mirror of https://github.com/acidanthera/audk.git
MdeModulePkg/NvmExpressDxe: Handle timeout for blocking PassThru req
https://bugzilla.tianocore.org/show_bug.cgi?id=433 When a blocking NVMe PassThru request experiences timeout, the current codes in function NvmExpressPassThru() do not abort the timeout request while advancing synchronous Submission Queue tail. Therefore, it is possible to submit a new blocking PassThru request when the synchronous Submission Queue is full. The commit adds logic to abort the timeout request by resetting the NVMe controller when a timeout occurs for a blocking PassThru request. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Star Zeng <star.zeng@intel.com>
This commit is contained in:
parent
d04b72c670
commit
5f5bba14b6
|
@ -3,7 +3,7 @@
|
||||||
NVM Express specification.
|
NVM Express specification.
|
||||||
|
|
||||||
(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
|
(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
|
||||||
Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>
|
Copyright (c) 2013 - 2017, Intel Corporation. All rights reserved.<BR>
|
||||||
This program and the accompanying materials
|
This program and the accompanying materials
|
||||||
are licensed and made available under the terms and conditions of the BSD License
|
are licensed and made available under the terms and conditions of the BSD License
|
||||||
which accompanies this distribution. The full text of the license may be found at
|
which accompanies this distribution. The full text of the license may be found at
|
||||||
|
@ -316,6 +316,100 @@ EXIT:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function supports
|
Sends an NVM Express Command Packet to an NVM Express controller or namespace. This function supports
|
||||||
both blocking I/O and non-blocking I/O. The blocking I/O functionality is required, and the non-blocking
|
both blocking I/O and non-blocking I/O. The blocking I/O functionality is required, and the non-blocking
|
||||||
|
@ -692,6 +786,44 @@ NvmExpressPassThru (
|
||||||
NvmeDumpStatus(Cq);
|
NvmeDumpStatus(Cq);
|
||||||
DEBUG_CODE_END();
|
DEBUG_CODE_END();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
//
|
||||||
|
// Timeout occurs for an NVMe command. Reset the controller to abort the
|
||||||
|
// outstanding commands.
|
||||||
|
//
|
||||||
|
DEBUG ((DEBUG_ERROR, "NvmExpressPassThru: Timeout occurs for an NVMe command.\n"));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Disable the timer to trigger the process of async transfers temporarily.
|
||||||
|
//
|
||||||
|
Status = gBS->SetTimer (Private->TimerEvent, TimerCancel, 0);
|
||||||
|
if (EFI_ERROR (Status)) {
|
||||||
|
goto EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Reset the NVMe controller.
|
||||||
|
//
|
||||||
|
Status = NvmeControllerInit (Private);
|
||||||
|
if (!EFI_ERROR (Status)) {
|
||||||
|
Status = AbortAsyncPassThruTasks (Private);
|
||||||
|
if (!EFI_ERROR (Status)) {
|
||||||
|
//
|
||||||
|
// Re-enable the timer to trigger the process of async transfers.
|
||||||
|
//
|
||||||
|
Status = gBS->SetTimer (Private->TimerEvent, TimerPeriodic, NVME_HC_ASYNC_TIMER);
|
||||||
|
if (!EFI_ERROR (Status)) {
|
||||||
|
//
|
||||||
|
// Return EFI_TIMEOUT to indicate a timeout occurs for NVMe PassThru command.
|
||||||
|
//
|
||||||
|
Status = EFI_TIMEOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Status = EFI_DEVICE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto EXIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((Private->CqHdbl[QueueId].Cqh ^= 1) == 0) {
|
if ((Private->CqHdbl[QueueId].Cqh ^= 1) == 0) {
|
||||||
|
|
Loading…
Reference in New Issue