| /** @file | |
| Minimal block driver for Mini-OS. | |
| Copyright (c) 2007-2008 Samuel Thibault. | |
| Copyright (C) 2014, Citrix Ltd. | |
| Copyright (c) 2014, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Library/PrintLib.h> | |
| #include <Library/DebugLib.h> | |
| #include "BlockFront.h" | |
| #include <IndustryStandard/Xen/io/protocols.h> | |
| #include <IndustryStandard/Xen/io/xenbus.h> | |
| /** | |
| Helper to read an integer from XenStore. | |
| If the number overflows according to the range defined by UINT64, | |
| then ASSERT(). | |
| @param This A pointer to a XENBUS_PROTOCOL instance. | |
| @param Node The XenStore node to read from. | |
| @param FromBackend Read frontend or backend value. | |
| @param ValuePtr Where to put the value. | |
| @retval XENSTORE_STATUS_SUCCESS If successful, will update ValuePtr. | |
| @return Any other return value indicate the error, | |
| ValuePtr is not updated in this case. | |
| **/ | |
| STATIC | |
| XENSTORE_STATUS | |
| XenBusReadUint64 ( | |
| IN XENBUS_PROTOCOL *This, | |
| IN CONST CHAR8 *Node, | |
| IN BOOLEAN FromBackend, | |
| OUT UINT64 *ValuePtr | |
| ) | |
| { | |
| XENSTORE_STATUS Status; | |
| CHAR8 *Ptr; | |
| if (!FromBackend) { | |
| Status = This->XsRead (This, XST_NIL, Node, (VOID **)&Ptr); | |
| } else { | |
| Status = This->XsBackendRead (This, XST_NIL, Node, (VOID **)&Ptr); | |
| } | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| return Status; | |
| } | |
| // AsciiStrDecimalToUint64 will ASSERT if Ptr overflow UINT64. | |
| *ValuePtr = AsciiStrDecimalToUint64 (Ptr); | |
| FreePool (Ptr); | |
| return Status; | |
| } | |
| /** | |
| Free an instance of XEN_BLOCK_FRONT_DEVICE. | |
| @param Dev The instance to free. | |
| **/ | |
| STATIC | |
| VOID | |
| XenPvBlockFree ( | |
| IN XEN_BLOCK_FRONT_DEVICE *Dev | |
| ) | |
| { | |
| XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo; | |
| if (Dev->RingRef != 0) { | |
| XenBusIo->GrantEndAccess (XenBusIo, Dev->RingRef); | |
| } | |
| if (Dev->Ring.sring != NULL) { | |
| FreePages (Dev->Ring.sring, 1); | |
| } | |
| if (Dev->EventChannel != 0) { | |
| XenBusIo->EventChannelClose (XenBusIo, Dev->EventChannel); | |
| } | |
| FreePool (Dev); | |
| } | |
| /** | |
| Wait until the backend has reached the ExpectedState. | |
| @param Dev A XEN_BLOCK_FRONT_DEVICE instance. | |
| @param ExpectedState The backend state expected. | |
| @param LastStatePtr An optional pointer where to right the final state. | |
| @return Return XENSTORE_STATUS_SUCCESS if the new backend state is ExpectedState | |
| or return an error otherwise. | |
| **/ | |
| STATIC | |
| XENSTORE_STATUS | |
| XenPvBlkWaitForBackendState ( | |
| IN XEN_BLOCK_FRONT_DEVICE *Dev, | |
| IN XenbusState ExpectedState, | |
| OUT XenbusState *LastStatePtr OPTIONAL | |
| ) | |
| { | |
| XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo; | |
| XenbusState State; | |
| UINT64 Value; | |
| XENSTORE_STATUS Status = XENSTORE_STATUS_SUCCESS; | |
| while (TRUE) { | |
| Status = XenBusReadUint64 (XenBusIo, "state", TRUE, &Value); | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| return Status; | |
| } | |
| if (Value > XenbusStateReconfigured) { | |
| // | |
| // Value is not a State value. | |
| // | |
| return XENSTORE_STATUS_EIO; | |
| } | |
| State = Value; | |
| if (State == ExpectedState) { | |
| break; | |
| } else if (State > ExpectedState) { | |
| Status = XENSTORE_STATUS_FAIL; | |
| break; | |
| } | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "XenPvBlk: waiting backend state %d, current: %d\n", | |
| ExpectedState, | |
| State | |
| )); | |
| XenBusIo->WaitForWatch (XenBusIo, Dev->StateWatchToken); | |
| } | |
| if (LastStatePtr != NULL) { | |
| *LastStatePtr = State; | |
| } | |
| return Status; | |
| } | |
| EFI_STATUS | |
| XenPvBlockFrontInitialization ( | |
| IN XENBUS_PROTOCOL *XenBusIo, | |
| IN CONST CHAR8 *NodeName, | |
| OUT XEN_BLOCK_FRONT_DEVICE **DevPtr | |
| ) | |
| { | |
| XENSTORE_TRANSACTION Transaction; | |
| CHAR8 *DeviceType; | |
| blkif_sring_t *SharedRing; | |
| XENSTORE_STATUS Status; | |
| XEN_BLOCK_FRONT_DEVICE *Dev; | |
| XenbusState State; | |
| UINT64 Value; | |
| CHAR8 *Params; | |
| ASSERT (NodeName != NULL); | |
| Dev = AllocateZeroPool (sizeof (XEN_BLOCK_FRONT_DEVICE)); | |
| if (Dev == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Dev->Signature = XEN_BLOCK_FRONT_SIGNATURE; | |
| Dev->NodeName = NodeName; | |
| Dev->XenBusIo = XenBusIo; | |
| Dev->DeviceId = XenBusIo->DeviceId; | |
| XenBusIo->XsRead (XenBusIo, XST_NIL, "device-type", (VOID **)&DeviceType); | |
| if (AsciiStrCmp (DeviceType, "cdrom") == 0) { | |
| Dev->MediaInfo.CdRom = TRUE; | |
| } else { | |
| Dev->MediaInfo.CdRom = FALSE; | |
| } | |
| FreePool (DeviceType); | |
| if (Dev->MediaInfo.CdRom) { | |
| Status = XenBusIo->XsBackendRead (XenBusIo, XST_NIL, "params", (VOID **)&Params); | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| DEBUG ((DEBUG_ERROR, "%a: Failed to read params (%d)\n", __func__, Status)); | |
| goto Error; | |
| } | |
| if ((AsciiStrLen (Params) == 0) || (AsciiStrCmp (Params, "aio:") == 0)) { | |
| FreePool (Params); | |
| DEBUG ((DEBUG_INFO, "%a: Empty cdrom\n", __func__)); | |
| goto Error; | |
| } | |
| FreePool (Params); | |
| } | |
| Status = XenBusReadUint64 (XenBusIo, "backend-id", FALSE, &Value); | |
| if ((Status != XENSTORE_STATUS_SUCCESS) || (Value > MAX_UINT16)) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "XenPvBlk: Failed to get backend-id (%d)\n", | |
| Status | |
| )); | |
| goto Error; | |
| } | |
| Dev->DomainId = (domid_t)Value; | |
| XenBusIo->EventChannelAllocate (XenBusIo, Dev->DomainId, &Dev->EventChannel); | |
| SharedRing = (blkif_sring_t *)AllocatePages (1); | |
| SHARED_RING_INIT (SharedRing); | |
| FRONT_RING_INIT (&Dev->Ring, SharedRing, EFI_PAGE_SIZE); | |
| XenBusIo->GrantAccess ( | |
| XenBusIo, | |
| Dev->DomainId, | |
| (INTN)SharedRing >> EFI_PAGE_SHIFT, | |
| FALSE, | |
| &Dev->RingRef | |
| ); | |
| Again: | |
| Status = XenBusIo->XsTransactionStart (XenBusIo, &Transaction); | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| DEBUG ((DEBUG_WARN, "XenPvBlk: Failed to start transaction, %d\n", Status)); | |
| goto Error; | |
| } | |
| Status = XenBusIo->XsPrintf ( | |
| XenBusIo, | |
| &Transaction, | |
| NodeName, | |
| "ring-ref", | |
| "%d", | |
| Dev->RingRef | |
| ); | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| DEBUG ((DEBUG_ERROR, "XenPvBlk: Failed to write ring-ref.\n")); | |
| goto AbortTransaction; | |
| } | |
| Status = XenBusIo->XsPrintf ( | |
| XenBusIo, | |
| &Transaction, | |
| NodeName, | |
| "event-channel", | |
| "%d", | |
| Dev->EventChannel | |
| ); | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| DEBUG ((DEBUG_ERROR, "XenPvBlk: Failed to write event-channel.\n")); | |
| goto AbortTransaction; | |
| } | |
| Status = XenBusIo->XsPrintf ( | |
| XenBusIo, | |
| &Transaction, | |
| NodeName, | |
| "protocol", | |
| "%a", | |
| XEN_IO_PROTO_ABI_NATIVE | |
| ); | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| DEBUG ((DEBUG_ERROR, "XenPvBlk: Failed to write protocol.\n")); | |
| goto AbortTransaction; | |
| } | |
| Status = XenBusIo->SetState (XenBusIo, &Transaction, XenbusStateConnected); | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| DEBUG ((DEBUG_ERROR, "XenPvBlk: Failed to switch state.\n")); | |
| goto AbortTransaction; | |
| } | |
| Status = XenBusIo->XsTransactionEnd (XenBusIo, &Transaction, FALSE); | |
| if (Status == XENSTORE_STATUS_EAGAIN) { | |
| goto Again; | |
| } | |
| XenBusIo->RegisterWatchBackend (XenBusIo, "state", &Dev->StateWatchToken); | |
| // | |
| // Waiting for backend | |
| // | |
| Status = XenPvBlkWaitForBackendState (Dev, XenbusStateConnected, &State); | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "XenPvBlk: backend for %a/%d not available, rc=%d state=%d\n", | |
| XenBusIo->Type, | |
| XenBusIo->DeviceId, | |
| Status, | |
| State | |
| )); | |
| goto Error2; | |
| } | |
| Status = XenBusReadUint64 (XenBusIo, "info", TRUE, &Value); | |
| if ((Status != XENSTORE_STATUS_SUCCESS) || (Value > MAX_UINT32)) { | |
| goto Error2; | |
| } | |
| Dev->MediaInfo.VDiskInfo = (UINT32)Value; | |
| if (Dev->MediaInfo.VDiskInfo & VDISK_READONLY) { | |
| Dev->MediaInfo.ReadWrite = FALSE; | |
| } else { | |
| Dev->MediaInfo.ReadWrite = TRUE; | |
| } | |
| Status = XenBusReadUint64 (XenBusIo, "sectors", TRUE, &Dev->MediaInfo.Sectors); | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| goto Error2; | |
| } | |
| Status = XenBusReadUint64 (XenBusIo, "sector-size", TRUE, &Value); | |
| if ((Status != XENSTORE_STATUS_SUCCESS) || (Value > MAX_UINT32)) { | |
| goto Error2; | |
| } | |
| if ((UINT32)Value % 512 != 0) { | |
| // | |
| // This is not supported by the driver. | |
| // | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "XenPvBlk: Unsupported sector-size value %Lu, " | |
| "it must be a multiple of 512\n", | |
| Value | |
| )); | |
| goto Error2; | |
| } | |
| Dev->MediaInfo.SectorSize = (UINT32)Value; | |
| // Default value | |
| Value = 0; | |
| XenBusReadUint64 (XenBusIo, "feature-barrier", TRUE, &Value); | |
| if (Value == 1) { | |
| Dev->MediaInfo.FeatureBarrier = TRUE; | |
| } else { | |
| Dev->MediaInfo.FeatureBarrier = FALSE; | |
| } | |
| // Default value | |
| Value = 0; | |
| XenBusReadUint64 (XenBusIo, "feature-flush-cache", TRUE, &Value); | |
| if (Value == 1) { | |
| Dev->MediaInfo.FeatureFlushCache = TRUE; | |
| } else { | |
| Dev->MediaInfo.FeatureFlushCache = FALSE; | |
| } | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "XenPvBlk: New disk with %ld sectors of %d bytes\n", | |
| Dev->MediaInfo.Sectors, | |
| Dev->MediaInfo.SectorSize | |
| )); | |
| *DevPtr = Dev; | |
| return EFI_SUCCESS; | |
| Error2: | |
| XenBusIo->UnregisterWatch (XenBusIo, Dev->StateWatchToken); | |
| XenBusIo->XsRemove (XenBusIo, XST_NIL, "ring-ref"); | |
| XenBusIo->XsRemove (XenBusIo, XST_NIL, "event-channel"); | |
| XenBusIo->XsRemove (XenBusIo, XST_NIL, "protocol"); | |
| goto Error; | |
| AbortTransaction: | |
| XenBusIo->XsTransactionEnd (XenBusIo, &Transaction, TRUE); | |
| Error: | |
| XenPvBlockFree (Dev); | |
| return EFI_DEVICE_ERROR; | |
| } | |
| VOID | |
| XenPvBlockFrontShutdown ( | |
| IN XEN_BLOCK_FRONT_DEVICE *Dev | |
| ) | |
| { | |
| XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo; | |
| XENSTORE_STATUS Status; | |
| UINT64 Value; | |
| XenPvBlockSync (Dev); | |
| Status = XenBusIo->SetState (XenBusIo, XST_NIL, XenbusStateClosing); | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "XenPvBlk: error while changing state to Closing: %d\n", | |
| Status | |
| )); | |
| goto Close; | |
| } | |
| Status = XenPvBlkWaitForBackendState (Dev, XenbusStateClosing, NULL); | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "XenPvBlk: error while waiting for closing backend state: %d\n", | |
| Status | |
| )); | |
| goto Close; | |
| } | |
| Status = XenBusIo->SetState (XenBusIo, XST_NIL, XenbusStateClosed); | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "XenPvBlk: error while changing state to Closed: %d\n", | |
| Status | |
| )); | |
| goto Close; | |
| } | |
| Status = XenPvBlkWaitForBackendState (Dev, XenbusStateClosed, NULL); | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "XenPvBlk: error while waiting for closed backend state: %d\n", | |
| Status | |
| )); | |
| goto Close; | |
| } | |
| Status = XenBusIo->SetState (XenBusIo, XST_NIL, XenbusStateInitialising); | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "XenPvBlk: error while changing state to initialising: %d\n", | |
| Status | |
| )); | |
| goto Close; | |
| } | |
| while (TRUE) { | |
| Status = XenBusReadUint64 (XenBusIo, "state", TRUE, &Value); | |
| if (Status != XENSTORE_STATUS_SUCCESS) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "XenPvBlk: error while waiting for new backend state: %d\n", | |
| Status | |
| )); | |
| goto Close; | |
| } | |
| if ((Value <= XenbusStateInitWait) || (Value >= XenbusStateClosed)) { | |
| break; | |
| } | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "XenPvBlk: waiting backend state %d, current: %Lu\n", | |
| XenbusStateInitWait, | |
| Value | |
| )); | |
| XenBusIo->WaitForWatch (XenBusIo, Dev->StateWatchToken); | |
| } | |
| Close: | |
| XenBusIo->UnregisterWatch (XenBusIo, Dev->StateWatchToken); | |
| XenBusIo->XsRemove (XenBusIo, XST_NIL, "ring-ref"); | |
| XenBusIo->XsRemove (XenBusIo, XST_NIL, "event-channel"); | |
| XenBusIo->XsRemove (XenBusIo, XST_NIL, "protocol"); | |
| XenPvBlockFree (Dev); | |
| } | |
| STATIC | |
| VOID | |
| XenPvBlockWaitSlot ( | |
| IN XEN_BLOCK_FRONT_DEVICE *Dev | |
| ) | |
| { | |
| /* Wait for a slot */ | |
| if (RING_FULL (&Dev->Ring)) { | |
| while (TRUE) { | |
| XenPvBlockAsyncIoPoll (Dev); | |
| if (!RING_FULL (&Dev->Ring)) { | |
| break; | |
| } | |
| /* Really no slot, could wait for an event on Dev->EventChannel. */ | |
| } | |
| } | |
| } | |
| VOID | |
| XenPvBlockAsyncIo ( | |
| IN OUT XEN_BLOCK_FRONT_IO *IoData, | |
| IN BOOLEAN IsWrite | |
| ) | |
| { | |
| XEN_BLOCK_FRONT_DEVICE *Dev = IoData->Dev; | |
| XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo; | |
| blkif_request_t *Request; | |
| RING_IDX RingIndex; | |
| BOOLEAN Notify; | |
| INT32 NumSegments, Index; | |
| UINTN Start, End; | |
| // Can't io at non-sector-aligned location | |
| ASSERT (!(IoData->Sector & ((Dev->MediaInfo.SectorSize / 512) - 1))); | |
| // Can't io non-sector-sized amounts | |
| ASSERT (!(IoData->Size & (Dev->MediaInfo.SectorSize - 1))); | |
| // Can't io non-sector-aligned buffer | |
| ASSERT (!((UINTN)IoData->Buffer & (Dev->MediaInfo.SectorSize - 1))); | |
| Start = (UINTN)IoData->Buffer & ~EFI_PAGE_MASK; | |
| End = ((UINTN)IoData->Buffer + IoData->Size + EFI_PAGE_SIZE - 1) & ~EFI_PAGE_MASK; | |
| IoData->NumRef = NumSegments = (INT32)((End - Start) / EFI_PAGE_SIZE); | |
| ASSERT (NumSegments <= BLKIF_MAX_SEGMENTS_PER_REQUEST); | |
| XenPvBlockWaitSlot (Dev); | |
| RingIndex = Dev->Ring.req_prod_pvt; | |
| Request = RING_GET_REQUEST (&Dev->Ring, RingIndex); | |
| Request->operation = IsWrite ? BLKIF_OP_WRITE : BLKIF_OP_READ; | |
| Request->nr_segments = (UINT8)NumSegments; | |
| Request->handle = Dev->DeviceId; | |
| Request->id = (UINTN)IoData; | |
| Request->sector_number = IoData->Sector; | |
| for (Index = 0; Index < NumSegments; Index++) { | |
| Request->seg[Index].first_sect = 0; | |
| Request->seg[Index].last_sect = EFI_PAGE_SIZE / 512 - 1; | |
| } | |
| Request->seg[0].first_sect = (UINT8)(((UINTN)IoData->Buffer & EFI_PAGE_MASK) / 512); | |
| Request->seg[NumSegments - 1].last_sect = | |
| (UINT8)((((UINTN)IoData->Buffer + IoData->Size - 1) & EFI_PAGE_MASK) / 512); | |
| for (Index = 0; Index < NumSegments; Index++) { | |
| UINTN Data = Start + Index * EFI_PAGE_SIZE; | |
| XenBusIo->GrantAccess ( | |
| XenBusIo, | |
| Dev->DomainId, | |
| Data >> EFI_PAGE_SHIFT, | |
| IsWrite, | |
| &Request->seg[Index].gref | |
| ); | |
| IoData->GrantRef[Index] = Request->seg[Index].gref; | |
| } | |
| Dev->Ring.req_prod_pvt = RingIndex + 1; | |
| MemoryFence (); | |
| RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev->Ring, Notify); | |
| if (Notify) { | |
| UINT32 ReturnCode; | |
| ReturnCode = XenBusIo->EventChannelNotify (XenBusIo, Dev->EventChannel); | |
| if (ReturnCode != 0) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n", | |
| ReturnCode | |
| )); | |
| } | |
| } | |
| } | |
| EFI_STATUS | |
| XenPvBlockIo ( | |
| IN OUT XEN_BLOCK_FRONT_IO *IoData, | |
| IN BOOLEAN IsWrite | |
| ) | |
| { | |
| // | |
| // Status value that correspond to an IO in progress. | |
| // | |
| IoData->Status = EFI_ALREADY_STARTED; | |
| XenPvBlockAsyncIo (IoData, IsWrite); | |
| while (IoData->Status == EFI_ALREADY_STARTED) { | |
| XenPvBlockAsyncIoPoll (IoData->Dev); | |
| } | |
| return IoData->Status; | |
| } | |
| STATIC | |
| VOID | |
| XenPvBlockPushOperation ( | |
| IN XEN_BLOCK_FRONT_DEVICE *Dev, | |
| IN UINT8 Operation, | |
| IN UINT64 Id | |
| ) | |
| { | |
| INT32 Index; | |
| blkif_request_t *Request; | |
| BOOLEAN Notify; | |
| XenPvBlockWaitSlot (Dev); | |
| Index = Dev->Ring.req_prod_pvt; | |
| Request = RING_GET_REQUEST (&Dev->Ring, Index); | |
| Request->operation = Operation; | |
| Request->nr_segments = 0; | |
| Request->handle = Dev->DeviceId; | |
| Request->id = Id; | |
| /* Not needed anyway, but the backend will check it */ | |
| Request->sector_number = 0; | |
| Dev->Ring.req_prod_pvt = Index + 1; | |
| MemoryFence (); | |
| RING_PUSH_REQUESTS_AND_CHECK_NOTIFY (&Dev->Ring, Notify); | |
| if (Notify) { | |
| XENBUS_PROTOCOL *XenBusIo = Dev->XenBusIo; | |
| UINT32 ReturnCode; | |
| ReturnCode = XenBusIo->EventChannelNotify (XenBusIo, Dev->EventChannel); | |
| if (ReturnCode != 0) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "XenPvBlk: Unexpected return value from EventChannelNotify: %d\n", | |
| ReturnCode | |
| )); | |
| } | |
| } | |
| } | |
| VOID | |
| XenPvBlockSync ( | |
| IN XEN_BLOCK_FRONT_DEVICE *Dev | |
| ) | |
| { | |
| if (Dev->MediaInfo.ReadWrite) { | |
| if (Dev->MediaInfo.FeatureBarrier) { | |
| XenPvBlockPushOperation (Dev, BLKIF_OP_WRITE_BARRIER, 0); | |
| } | |
| if (Dev->MediaInfo.FeatureFlushCache) { | |
| XenPvBlockPushOperation (Dev, BLKIF_OP_FLUSH_DISKCACHE, 0); | |
| } | |
| } | |
| /* Note: This won't finish if another thread enqueues requests. */ | |
| while (TRUE) { | |
| XenPvBlockAsyncIoPoll (Dev); | |
| if (RING_FREE_REQUESTS (&Dev->Ring) == RING_SIZE (&Dev->Ring)) { | |
| break; | |
| } | |
| } | |
| } | |
| VOID | |
| XenPvBlockAsyncIoPoll ( | |
| IN XEN_BLOCK_FRONT_DEVICE *Dev | |
| ) | |
| { | |
| RING_IDX ProducerIndex, ConsumerIndex; | |
| blkif_response_t *Response; | |
| INT32 More; | |
| do { | |
| ProducerIndex = Dev->Ring.sring->rsp_prod; | |
| /* Ensure we see queued responses up to 'ProducerIndex'. */ | |
| MemoryFence (); | |
| ConsumerIndex = Dev->Ring.rsp_cons; | |
| while (ConsumerIndex != ProducerIndex) { | |
| XEN_BLOCK_FRONT_IO *IoData = NULL; | |
| INT16 Status; | |
| Response = RING_GET_RESPONSE (&Dev->Ring, ConsumerIndex); | |
| IoData = (VOID *)(UINTN)Response->id; | |
| Status = Response->status; | |
| switch (Response->operation) { | |
| case BLKIF_OP_READ: | |
| case BLKIF_OP_WRITE: | |
| { | |
| INT32 Index; | |
| if (Status != BLKIF_RSP_OKAY) { | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "XenPvBlk: " | |
| "%a error %d on %a at sector %Lx, num bytes %Lx\n", | |
| Response->operation == BLKIF_OP_READ ? "read" : "write", | |
| Status, | |
| IoData->Dev->NodeName, | |
| (UINT64)IoData->Sector, | |
| (UINT64)IoData->Size | |
| )); | |
| } | |
| for (Index = 0; Index < IoData->NumRef; Index++) { | |
| Dev->XenBusIo->GrantEndAccess (Dev->XenBusIo, IoData->GrantRef[Index]); | |
| } | |
| break; | |
| } | |
| case BLKIF_OP_WRITE_BARRIER: | |
| if (Status != BLKIF_RSP_OKAY) { | |
| DEBUG ((DEBUG_ERROR, "XenPvBlk: write barrier error %d\n", Status)); | |
| } | |
| break; | |
| case BLKIF_OP_FLUSH_DISKCACHE: | |
| if (Status != BLKIF_RSP_OKAY) { | |
| DEBUG ((DEBUG_ERROR, "XenPvBlk: flush error %d\n", Status)); | |
| } | |
| break; | |
| default: | |
| DEBUG (( | |
| DEBUG_ERROR, | |
| "XenPvBlk: unrecognized block operation %d response (status %d)\n", | |
| Response->operation, | |
| Status | |
| )); | |
| break; | |
| } | |
| Dev->Ring.rsp_cons = ++ConsumerIndex; | |
| if (IoData != NULL) { | |
| IoData->Status = Status ? EFI_DEVICE_ERROR : EFI_SUCCESS; | |
| } | |
| if (Dev->Ring.rsp_cons != ConsumerIndex) { | |
| /* We reentered, we must not continue here */ | |
| break; | |
| } | |
| } | |
| RING_FINAL_CHECK_FOR_RESPONSES (&Dev->Ring, More); | |
| } while (More != 0); | |
| } |