/** Call back function when the timer event is signaled. @param[in] Event The Event this notify function registered to. @param[in] Context Pointer to the context data registered to the Event. **/ VOID EFIAPI ProcessAsyncTaskList ( IN EFI_EVENT Event, IN VOID* Context ) { NVME_CONTROLLER_PRIVATE_DATA *Private; EFI_PCI_IO_PROTOCOL *PciIo; NVME_CQ *Cq; UINT16 QueueId; UINT32 Data; LIST_ENTRY *Link; LIST_ENTRY *NextLink; NVME_PASS_THRU_ASYNC_REQ *AsyncRequest; NVME_BLKIO2_SUBTASK *Subtask; NVME_BLKIO2_REQUEST *BlkIo2Request; EFI_BLOCK_IO2_TOKEN *Token; BOOLEAN HasNewItem; EFI_STATUS Status; Private = (NVME_CONTROLLER_PRIVATE_DATA*)Context; QueueId = 2; Cq = Private->CqBuffer[QueueId] + Private->CqHdbl[QueueId].Cqh; HasNewItem = FALSE; PciIo = Private->PciIo; // // Submit asynchronous subtasks to the NVMe Submission Queue // for (Link = GetFirstNode (&Private->UnsubmittedSubtasks); !IsNull (&Private->UnsubmittedSubtasks, Link); Link = NextLink) { NextLink = GetNextNode (&Private->UnsubmittedSubtasks, Link); Subtask = NVME_BLKIO2_SUBTASK_FROM_LINK (Link); BlkIo2Request = Subtask->BlockIo2Request; Token = BlkIo2Request->Token; RemoveEntryList (Link); BlkIo2Request->UnsubmittedSubtaskNum--; // // If any previous subtask fails, do not process subsequent ones. // if (Token->TransactionStatus != EFI_SUCCESS) { if (IsListEmpty (&BlkIo2Request->SubtasksQueue) && BlkIo2Request->LastSubtaskSubmitted && (BlkIo2Request->UnsubmittedSubtaskNum == 0)) { // // Remove the BlockIo2 request from the device asynchronous queue. // RemoveEntryList (&BlkIo2Request->Link); FreePool (BlkIo2Request); gBS->SignalEvent (Token->Event); } FreePool (Subtask->CommandPacket->NvmeCmd); FreePool (Subtask->CommandPacket->NvmeCompletion); FreePool (Subtask->CommandPacket); FreePool (Subtask); continue; } Status = Private->Passthru.PassThru ( &Private->Passthru, Subtask->NamespaceId, Subtask->CommandPacket, Subtask->Event ); if (Status == EFI_NOT_READY) { InsertHeadList (&Private->UnsubmittedSubtasks, Link); BlkIo2Request->UnsubmittedSubtaskNum++; break; } else if (EFI_ERROR (Status)) { Token->TransactionStatus = EFI_DEVICE_ERROR; if (IsListEmpty (&BlkIo2Request->SubtasksQueue) && Subtask->IsLast) { // // Remove the BlockIo2 request from the device asynchronous queue. // RemoveEntryList (&BlkIo2Request->Link); FreePool (BlkIo2Request); gBS->SignalEvent (Token->Event); } FreePool (Subtask->CommandPacket->NvmeCmd); FreePool (Subtask->CommandPacket->NvmeCompletion); FreePool (Subtask->CommandPacket); FreePool (Subtask); } else { InsertTailList (&BlkIo2Request->SubtasksQueue, Link); if (Subtask->IsLast) { BlkIo2Request->LastSubtaskSubmitted = TRUE; } } } while (Cq->Pt != Private->Pt[QueueId]) { ASSERT (Cq->Sqid == QueueId); HasNewItem = TRUE; // // Find the command with given Command Id. // for (Link = GetFirstNode (&Private->AsyncPassThruQueue); !IsNull (&Private->AsyncPassThruQueue, Link); Link = NextLink) { NextLink = GetNextNode (&Private->AsyncPassThruQueue, Link); AsyncRequest = NVME_PASS_THRU_ASYNC_REQ_FROM_THIS (Link); if (AsyncRequest->CommandId == Cq->Cid) { // // Copy the Respose Queue entry for this command to the callers // response buffer. // CopyMem ( AsyncRequest->Packet->NvmeCompletion, Cq, sizeof(EFI_NVM_EXPRESS_COMPLETION) ); // // Free the resources allocated before cmd submission // if (AsyncRequest->MapData != NULL) { PciIo->Unmap (PciIo, AsyncRequest->MapData); } if (AsyncRequest->MapMeta != NULL) { PciIo->Unmap (PciIo, AsyncRequest->MapMeta); } if (AsyncRequest->MapPrpList != NULL) { PciIo->Unmap (PciIo, AsyncRequest->MapPrpList); } if (AsyncRequest->PrpListHost != NULL) { PciIo->FreeBuffer ( PciIo, AsyncRequest->PrpListNo, AsyncRequest->PrpListHost ); } RemoveEntryList (Link); gBS->SignalEvent (AsyncRequest->CallerEvent); FreePool (AsyncRequest); // // Update submission queue head. // Private->AsyncSqHead = Cq->Sqhd; break; } } Private->CqHdbl[QueueId].Cqh++; if (Private->CqHdbl[QueueId].Cqh > NVME_ASYNC_CCQ_SIZE) { Private->CqHdbl[QueueId].Cqh = 0; Private->Pt[QueueId] ^= 1; } Cq = Private->CqBuffer[QueueId] + Private->CqHdbl[QueueId].Cqh; } if (HasNewItem) { Data = ReadUnaligned32 ((UINT32*)&Private->CqHdbl[QueueId]); PciIo->Mem.Write ( PciIo, EfiPciIoWidthUint32, NVME_BAR, NVME_CQHDBL_OFFSET(QueueId, Private->Cap.Dstrd), 1, &Data ); } }
/** Aborts the asynchronous PassThru requests. @param[in] Private The pointer to the NVME_CONTROLLER_PRIVATE_DATA data structure. @retval EFI_SUCCESS The asynchronous PassThru requests have been aborted. @return EFI_DEVICE_ERROR Fail to abort all the asynchronous PassThru requests. **/ EFI_STATUS AbortAsyncPassThruTasks ( IN NVME_CONTROLLER_PRIVATE_DATA *Private ) { EFI_PCI_IO_PROTOCOL *PciIo; LIST_ENTRY *Link; LIST_ENTRY *NextLink; NVME_BLKIO2_SUBTASK *Subtask; NVME_BLKIO2_REQUEST *BlkIo2Request; NVME_PASS_THRU_ASYNC_REQ *AsyncRequest; EFI_BLOCK_IO2_TOKEN *Token; EFI_TPL OldTpl; EFI_STATUS Status; PciIo = Private->PciIo; OldTpl = gBS->RaiseTPL (TPL_NOTIFY); // // Cancel the unsubmitted subtasks. // for (Link = GetFirstNode (&Private->UnsubmittedSubtasks); !IsNull (&Private->UnsubmittedSubtasks, Link); Link = NextLink) { NextLink = GetNextNode (&Private->UnsubmittedSubtasks, Link); Subtask = NVME_BLKIO2_SUBTASK_FROM_LINK (Link); BlkIo2Request = Subtask->BlockIo2Request; Token = BlkIo2Request->Token; BlkIo2Request->UnsubmittedSubtaskNum--; if (Subtask->IsLast) { BlkIo2Request->LastSubtaskSubmitted = TRUE; } Token->TransactionStatus = EFI_ABORTED; RemoveEntryList (Link); InsertTailList (&BlkIo2Request->SubtasksQueue, Link); gBS->SignalEvent (Subtask->Event); } // // Cleanup the resources for the asynchronous PassThru requests. // for (Link = GetFirstNode (&Private->AsyncPassThruQueue); !IsNull (&Private->AsyncPassThruQueue, Link); Link = NextLink) { NextLink = GetNextNode (&Private->AsyncPassThruQueue, Link); AsyncRequest = NVME_PASS_THRU_ASYNC_REQ_FROM_THIS (Link); if (AsyncRequest->MapData != NULL) { PciIo->Unmap (PciIo, AsyncRequest->MapData); } if (AsyncRequest->MapMeta != NULL) { PciIo->Unmap (PciIo, AsyncRequest->MapMeta); } if (AsyncRequest->MapPrpList != NULL) { PciIo->Unmap (PciIo, AsyncRequest->MapPrpList); } if (AsyncRequest->PrpListHost != NULL) { PciIo->FreeBuffer ( PciIo, AsyncRequest->PrpListNo, AsyncRequest->PrpListHost ); } RemoveEntryList (Link); gBS->SignalEvent (AsyncRequest->CallerEvent); FreePool (AsyncRequest); } if (IsListEmpty (&Private->AsyncPassThruQueue) && IsListEmpty (&Private->UnsubmittedSubtasks)) { Status = EFI_SUCCESS; } else { Status = EFI_DEVICE_ERROR; } gBS->RestoreTPL (OldTpl); return Status; }