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. 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 request submission. It is the calling driver's responsibility to verify the
ring size in advance. 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 @param[in out] Ring The virtio ring to append the buffer to, as a
descriptor. descriptor.
@ -164,6 +197,8 @@ VirtioRingUninit (
host only interprets it dependent on host only interprets it dependent on
VRING_DESC_F_NEXT. VRING_DESC_F_NEXT.
In *Indices:
@param [in] HeadIdx The index identifying the head buffer (first @param [in] HeadIdx The index identifying the head buffer (first
buffer appended) belonging to this same buffer appended) belonging to this same
request. request.
@ -176,12 +211,41 @@ VirtioRingUninit (
VOID VOID
EFIAPI EFIAPI
VirtioAppendDesc ( VirtioAppendDesc (
IN OUT VRING *Ring, IN OUT VRING *Ring,
IN UINTN BufferPhysAddr, IN UINTN BufferPhysAddr,
IN UINT32 BufferSize, IN UINT32 BufferSize,
IN UINT16 Flags, IN UINT16 Flags,
IN UINT16 HeadIdx, IN OUT DESC_INDICES *Indices
IN OUT UINT16 *NextAvailIdx );
/**
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_ #endif // _VIRTIO_LIB_H_

View File

@ -15,9 +15,11 @@
**/ **/
#include <IndustryStandard/Pci22.h> #include <IndustryStandard/Pci22.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h> #include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h> #include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h> #include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/VirtioLib.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. 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 request submission. It is the calling driver's responsibility to verify the
ring size in advance. 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 @param[in out] Ring The virtio ring to append the buffer to, as a
descriptor. descriptor.
@ -303,6 +341,8 @@ VirtioRingUninit (
host only interprets it dependent on host only interprets it dependent on
VRING_DESC_F_NEXT. VRING_DESC_F_NEXT.
In *Indices:
@param [in] HeadIdx The index identifying the head buffer (first @param [in] HeadIdx The index identifying the head buffer (first
buffer appended) belonging to this same buffer appended) belonging to this same
request. request.
@ -315,21 +355,96 @@ VirtioRingUninit (
VOID VOID
EFIAPI EFIAPI
VirtioAppendDesc ( VirtioAppendDesc (
IN OUT VRING *Ring, IN OUT VRING *Ring,
IN UINTN BufferPhysAddr, IN UINTN BufferPhysAddr,
IN UINT32 BufferSize, IN UINT32 BufferSize,
IN UINT16 Flags, IN UINT16 Flags,
IN UINT16 HeadIdx, IN OUT DESC_INDICES *Indices
IN OUT UINT16 *NextAvailIdx
) )
{ {
volatile VRING_DESC *Desc; volatile VRING_DESC *Desc;
Desc = &Ring->Desc[*NextAvailIdx % Ring->QueueSize]; Desc = &Ring->Desc[Indices->NextAvailIdx % Ring->QueueSize];
Desc->Addr = BufferPhysAddr; Desc->Addr = BufferPhysAddr;
Desc->Len = BufferSize; Desc->Len = BufferSize;
Desc->Flags = Flags; Desc->Flags = Flags;
Ring->Avail.Ring[(*NextAvailIdx)++ % Ring->QueueSize] = Ring->Avail.Ring[Indices->NextAvailIdx++ % Ring->QueueSize] =
HeadIdx % Ring->QueueSize; Indices->HeadIdx % Ring->QueueSize;
Desc->Next = *NextAvailIdx % 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 OvmfPkg/OvmfPkg.dec
[LibraryClasses] [LibraryClasses]
BaseLib
BaseMemoryLib BaseMemoryLib
DebugLib DebugLib
MemoryAllocationLib MemoryAllocationLib
UefiBootServicesTableLib

View File

@ -248,9 +248,7 @@ SynchronousRequest (
UINT32 BlockSize; UINT32 BlockSize;
volatile VIRTIO_BLK_REQ Request; volatile VIRTIO_BLK_REQ Request;
volatile UINT8 HostStatus; volatile UINT8 HostStatus;
UINT16 FirstAvailIdx; DESC_INDICES Indices;
UINT16 NextAvailIdx;
UINTN PollPeriodUsecs;
BlockSize = Dev->BlockIoMedia.BlockSize; BlockSize = Dev->BlockIoMedia.BlockSize;
@ -275,11 +273,7 @@ SynchronousRequest (
Request.IoPrio = 0; Request.IoPrio = 0;
Request.Sector = Lba * (BlockSize / 512); Request.Sector = Lba * (BlockSize / 512);
// VirtioPrepare (&Dev->Ring, &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.
//
*Dev->Ring.Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;
// //
// preset a host status for ourselves that we do not accept as success // preset a host status for ourselves that we do not accept as success
@ -292,17 +286,11 @@ SynchronousRequest (
// //
ASSERT (Dev->Ring.QueueSize >= 3); 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 // virtio-blk header in first desc
// //
VirtioAppendDesc (&Dev->Ring, (UINTN) &Request, sizeof Request, 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 // data buffer for read/write in second desc
@ -323,50 +311,20 @@ SynchronousRequest (
// //
VirtioAppendDesc (&Dev->Ring, (UINTN) Buffer, (UINT32) BufferSize, VirtioAppendDesc (&Dev->Ring, (UINTN) Buffer, (UINT32) BufferSize,
VRING_DESC_F_NEXT | (RequestIsWrite ? 0 : VRING_DESC_F_WRITE), VRING_DESC_F_NEXT | (RequestIsWrite ? 0 : VRING_DESC_F_WRITE),
FirstAvailIdx, &NextAvailIdx); &Indices);
} }
// //
// host status in last (second or third) desc // host status in last (second or third) desc
// //
VirtioAppendDesc (&Dev->Ring, (UINTN) &HostStatus, sizeof HostStatus, 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(); if (VirtioFlush (Dev->PciIo, 0, &Dev->Ring, &Indices) == EFI_SUCCESS &&
*Dev->Ring.Avail.Idx = NextAvailIdx; HostStatus == VIRTIO_BLK_S_OK) {
//
// 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) {
return EFI_SUCCESS; return EFI_SUCCESS;
} }