OvmfPkg: librarize reusable bits from VirtioBlkDxe's SynchronousRequest()

new VirtioLib functions:
- VirtioPrepare(): prepare for appending descriptors
- VirtioFlush(): submit descriptor chain and await host answer

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>

git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13844 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
jljusten 2012-10-12 18:54:35 +00:00
parent 7fcacd6c92
commit e371e7e545
4 changed files with 205 additions and 66 deletions

View File

@ -135,6 +135,36 @@ VirtioRingUninit (
);
//
// Internal use structure for tracking the submission of a multi-descriptor
// request.
//
typedef struct {
UINT16 HeadIdx;
UINT16 NextAvailIdx;
} DESC_INDICES;
/**
Turn off interrupt notifications from the host, and prepare for appending
multiple descriptors to the virtio ring.
The calling driver must be in VSTAT_DRIVER_OK state.
@param[in out] Ring The virtio ring we intend to append descriptors to.
@param[out] Indices The DESC_INDICES structure to initialize.
**/
VOID
EFIAPI
VirtioPrepare (
IN OUT VRING *Ring,
OUT DESC_INDICES *Indices
);
/**
Append a contiguous buffer for transmission / reception via the virtio ring.
@ -148,6 +178,9 @@ VirtioRingUninit (
request submission. It is the calling driver's responsibility to verify the
ring size in advance.
The caller is responsible for initializing *Indices with VirtioPrepare()
first.
@param[in out] Ring The virtio ring to append the buffer to, as a
descriptor.
@ -164,6 +197,8 @@ VirtioRingUninit (
host only interprets it dependent on
VRING_DESC_F_NEXT.
In *Indices:
@param [in] HeadIdx The index identifying the head buffer (first
buffer appended) belonging to this same
request.
@ -176,12 +211,41 @@ VirtioRingUninit (
VOID
EFIAPI
VirtioAppendDesc (
IN OUT VRING *Ring,
IN UINTN BufferPhysAddr,
IN UINT32 BufferSize,
IN UINT16 Flags,
IN UINT16 HeadIdx,
IN OUT UINT16 *NextAvailIdx
IN OUT VRING *Ring,
IN UINTN BufferPhysAddr,
IN UINT32 BufferSize,
IN UINT16 Flags,
IN OUT DESC_INDICES *Indices
);
/**
Notify the host about appended descriptors and wait until it processes the
last one (ie. all of them).
@param[in] PciIo The target virtio PCI device to notify.
@param[in] VirtQueueId Identifies the queue for the target device.
@param[in out] Ring The virtio ring with descriptors to submit.
@param[in] Indices The function waits until the host processes
descriptors up to Indices->NextAvailIdx.
@return Error code from VirtioWrite() if it fails.
@retval EFI_SUCCESS Otherwise, the host processed all descriptors.
**/
EFI_STATUS
EFIAPI
VirtioFlush (
IN EFI_PCI_IO_PROTOCOL *PciIo,
IN UINT16 VirtQueueId,
IN OUT VRING *Ring,
IN DESC_INDICES *Indices
);
#endif // _VIRTIO_LIB_H_

View File

@ -15,9 +15,11 @@
**/
#include <IndustryStandard/Pci22.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/VirtioLib.h>
@ -274,6 +276,39 @@ VirtioRingUninit (
}
/**
Turn off interrupt notifications from the host, and prepare for appending
multiple descriptors to the virtio ring.
The calling driver must be in VSTAT_DRIVER_OK state.
@param[in out] Ring The virtio ring we intend to append descriptors to.
@param[out] Indices The DESC_INDICES structure to initialize.
**/
VOID
EFIAPI
VirtioPrepare (
IN OUT VRING *Ring,
OUT DESC_INDICES *Indices
)
{
//
// Prepare for virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device.
// We're going to poll the answer, the host should not send an interrupt.
//
*Ring->Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;
//
// Prepare for virtio-0.9.5, 2.4.1 Supplying Buffers to the Device.
//
Indices->HeadIdx = *Ring->Avail.Idx;
Indices->NextAvailIdx = Indices->HeadIdx;
}
/**
Append a contiguous buffer for transmission / reception via the virtio ring.
@ -287,6 +322,9 @@ VirtioRingUninit (
request submission. It is the calling driver's responsibility to verify the
ring size in advance.
The caller is responsible for initializing *Indices with VirtioPrepare()
first.
@param[in out] Ring The virtio ring to append the buffer to, as a
descriptor.
@ -303,6 +341,8 @@ VirtioRingUninit (
host only interprets it dependent on
VRING_DESC_F_NEXT.
In *Indices:
@param [in] HeadIdx The index identifying the head buffer (first
buffer appended) belonging to this same
request.
@ -315,21 +355,96 @@ VirtioRingUninit (
VOID
EFIAPI
VirtioAppendDesc (
IN OUT VRING *Ring,
IN UINTN BufferPhysAddr,
IN UINT32 BufferSize,
IN UINT16 Flags,
IN UINT16 HeadIdx,
IN OUT UINT16 *NextAvailIdx
IN OUT VRING *Ring,
IN UINTN BufferPhysAddr,
IN UINT32 BufferSize,
IN UINT16 Flags,
IN OUT DESC_INDICES *Indices
)
{
volatile VRING_DESC *Desc;
Desc = &Ring->Desc[*NextAvailIdx % Ring->QueueSize];
Desc = &Ring->Desc[Indices->NextAvailIdx % Ring->QueueSize];
Desc->Addr = BufferPhysAddr;
Desc->Len = BufferSize;
Desc->Flags = Flags;
Ring->Avail.Ring[(*NextAvailIdx)++ % Ring->QueueSize] =
HeadIdx % Ring->QueueSize;
Desc->Next = *NextAvailIdx % Ring->QueueSize;
Ring->Avail.Ring[Indices->NextAvailIdx++ % Ring->QueueSize] =
Indices->HeadIdx % Ring->QueueSize;
Desc->Next = Indices->NextAvailIdx % Ring->QueueSize;
}
/**
Notify the host about appended descriptors and wait until it processes the
last one (ie. all of them).
@param[in] PciIo The target virtio PCI device to notify.
@param[in] VirtQueueId Identifies the queue for the target device.
@param[in out] Ring The virtio ring with descriptors to submit.
@param[in] Indices The function waits until the host processes
descriptors up to Indices->NextAvailIdx.
@return Error code from VirtioWrite() if it fails.
@retval EFI_SUCCESS Otherwise, the host processed all descriptors.
**/
EFI_STATUS
EFIAPI
VirtioFlush (
IN EFI_PCI_IO_PROTOCOL *PciIo,
IN UINT16 VirtQueueId,
IN OUT VRING *Ring,
IN DESC_INDICES *Indices
)
{
EFI_STATUS Status;
UINTN PollPeriodUsecs;
//
// virtio-0.9.5, 2.4.1.3 Updating the Index Field
//
MemoryFence();
*Ring->Avail.Idx = Indices->NextAvailIdx;
//
// virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are
// OK.
//
MemoryFence();
Status = VirtioWrite (
PciIo,
OFFSET_OF (VIRTIO_HDR, VhdrQueueNotify),
sizeof (UINT16),
VirtQueueId
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
// Wait until the host processes and acknowledges our descriptor chain. The
// condition we use for polling is greatly simplified and relies on the
// synchronous, lock-step progress.
//
// Keep slowing down until we reach a poll period of slightly above 1 ms.
//
PollPeriodUsecs = 1;
MemoryFence();
while (*Ring->Used.Idx != Indices->NextAvailIdx) {
gBS->Stall (PollPeriodUsecs); // calls AcpiTimerLib::MicroSecondDelay
if (PollPeriodUsecs < 1024) {
PollPeriodUsecs *= 2;
}
MemoryFence();
}
return EFI_SUCCESS;
}

View File

@ -29,6 +29,8 @@
OvmfPkg/OvmfPkg.dec
[LibraryClasses]
BaseLib
BaseMemoryLib
DebugLib
MemoryAllocationLib
UefiBootServicesTableLib

View File

@ -248,9 +248,7 @@ SynchronousRequest (
UINT32 BlockSize;
volatile VIRTIO_BLK_REQ Request;
volatile UINT8 HostStatus;
UINT16 FirstAvailIdx;
UINT16 NextAvailIdx;
UINTN PollPeriodUsecs;
DESC_INDICES Indices;
BlockSize = Dev->BlockIoMedia.BlockSize;
@ -275,11 +273,7 @@ SynchronousRequest (
Request.IoPrio = 0;
Request.Sector = Lba * (BlockSize / 512);
//
// Prepare for virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device.
// We're going to poll the answer, the host should not send an interrupt.
//
*Dev->Ring.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;
VirtioPrepare (&Dev->Ring, &Indices);
//
// preset a host status for ourselves that we do not accept as success
@ -292,17 +286,11 @@ SynchronousRequest (
//
ASSERT (Dev->Ring.QueueSize >= 3);
//
// Implement virtio-0.9.5, 2.4.1 Supplying Buffers to the Device.
//
FirstAvailIdx = *Dev->Ring.Avail.Idx;
NextAvailIdx = FirstAvailIdx;
//
// virtio-blk header in first desc
//
VirtioAppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request,
VRING_DESC_F_NEXT, FirstAvailIdx, &NextAvailIdx);
VRING_DESC_F_NEXT, &Indices);
//
// data buffer for read/write in second desc
@ -323,50 +311,20 @@ SynchronousRequest (
//
VirtioAppendDesc (&Dev->Ring, (UINTN) Buffer, (UINT32) BufferSize,
VRING_DESC_F_NEXT | (RequestIsWrite ? 0 : VRING_DESC_F_WRITE),
FirstAvailIdx, &NextAvailIdx);
&Indices);
}
//
// host status in last (second or third) desc
//
VirtioAppendDesc (&Dev->Ring, (UINTN) &HostStatus, sizeof HostStatus,
VRING_DESC_F_WRITE, FirstAvailIdx, &NextAvailIdx);
VRING_DESC_F_WRITE, &Indices);
//
// virtio-0.9.5, 2.4.1.3 Updating the Index Field
// virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D).
//
MemoryFence();
*Dev->Ring.Avail.Idx = NextAvailIdx;
//
// virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are
// OK. virtio-blk's only virtqueue is #0, called "requestq" (see Appendix D).
//
MemoryFence();
if (EFI_ERROR (VIRTIO_CFG_WRITE (Dev, Generic.VhdrQueueNotify, 0))) {
return EFI_DEVICE_ERROR;
}
//
// virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device
// Wait until the host processes and acknowledges our 3-part descriptor
// chain. The condition we use for polling is greatly simplified and relies
// on synchronous, the lock-step progress.
//
// Keep slowing down until we reach a poll period of slightly above 1 ms.
//
PollPeriodUsecs = 1;
MemoryFence();
while (*Dev->Ring.Used.Idx != NextAvailIdx) {
gBS->Stall (PollPeriodUsecs); // calls AcpiTimerLib::MicroSecondDelay
if (PollPeriodUsecs < 1024) {
PollPeriodUsecs *= 2;
}
MemoryFence();
}
if (HostStatus == VIRTIO_BLK_S_OK) {
if (VirtioFlush (Dev->PciIo, 0, &Dev->Ring, &Indices) == EFI_SUCCESS &&
HostStatus == VIRTIO_BLK_S_OK) {
return EFI_SUCCESS;
}