summaryrefslogtreecommitdiffstats
path: root/src/VBox/VMM/VMMR3/PDMAsyncCompletionFileFailsafe.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/VMM/VMMR3/PDMAsyncCompletionFileFailsafe.cpp')
-rw-r--r--src/VBox/VMM/VMMR3/PDMAsyncCompletionFileFailsafe.cpp270
1 files changed, 270 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMR3/PDMAsyncCompletionFileFailsafe.cpp b/src/VBox/VMM/VMMR3/PDMAsyncCompletionFileFailsafe.cpp
new file mode 100644
index 00000000..c7ebcf6b
--- /dev/null
+++ b/src/VBox/VMM/VMMR3/PDMAsyncCompletionFileFailsafe.cpp
@@ -0,0 +1,270 @@
+/* $Id: PDMAsyncCompletionFileFailsafe.cpp $ */
+/** @file
+ * PDM Async I/O - Transport data asynchronous in R3 using EMT.
+ * Simple File I/O manager.
+ */
+
+/*
+ * Copyright (C) 2006-2020 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_PDM_ASYNC_COMPLETION
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <VBox/log.h>
+
+#include "PDMAsyncCompletionFileInternal.h"
+
+
+
+/**
+ * Put a list of tasks in the pending request list of an endpoint.
+ */
+DECLINLINE(void) pdmacFileAioMgrEpAddTaskList(PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint, PPDMACTASKFILE pTaskHead)
+{
+ /* Add the rest of the tasks to the pending list */
+ if (!pEndpoint->AioMgr.pReqsPendingHead)
+ {
+ Assert(!pEndpoint->AioMgr.pReqsPendingTail);
+ pEndpoint->AioMgr.pReqsPendingHead = pTaskHead;
+ }
+ else
+ {
+ Assert(pEndpoint->AioMgr.pReqsPendingTail);
+ pEndpoint->AioMgr.pReqsPendingTail->pNext = pTaskHead;
+ }
+
+ /* Update the tail. */
+ while (pTaskHead->pNext)
+ pTaskHead = pTaskHead->pNext;
+
+ pEndpoint->AioMgr.pReqsPendingTail = pTaskHead;
+ pTaskHead->pNext = NULL;
+}
+
+/**
+ * Processes a given task list for assigned to the given endpoint.
+ */
+static int pdmacFileAioMgrFailsafeProcessEndpointTaskList(PPDMACEPFILEMGR pAioMgr,
+ PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint,
+ PPDMACTASKFILE pTasks)
+{
+ int rc = VINF_SUCCESS;
+
+ while (pTasks)
+ {
+ RTMSINTERVAL msWhenNext;
+ PPDMACTASKFILE pCurr = pTasks;
+
+ if (!pdmacEpIsTransferAllowed(&pEndpoint->Core, (uint32_t)pCurr->DataSeg.cbSeg, &msWhenNext))
+ {
+ pAioMgr->msBwLimitExpired = RT_MIN(pAioMgr->msBwLimitExpired, msWhenNext);
+ break;
+ }
+
+ pTasks = pTasks->pNext;
+
+ switch (pCurr->enmTransferType)
+ {
+ case PDMACTASKFILETRANSFER_FLUSH:
+ {
+ rc = RTFileFlush(pEndpoint->hFile);
+ break;
+ }
+ case PDMACTASKFILETRANSFER_READ:
+ case PDMACTASKFILETRANSFER_WRITE:
+ {
+ if (pCurr->enmTransferType == PDMACTASKFILETRANSFER_READ)
+ {
+ rc = RTFileReadAt(pEndpoint->hFile, pCurr->Off,
+ pCurr->DataSeg.pvSeg,
+ pCurr->DataSeg.cbSeg,
+ NULL);
+ }
+ else
+ {
+ if (RT_UNLIKELY((uint64_t)pCurr->Off + pCurr->DataSeg.cbSeg > pEndpoint->cbFile))
+ {
+ ASMAtomicWriteU64(&pEndpoint->cbFile, pCurr->Off + pCurr->DataSeg.cbSeg);
+ RTFileSetSize(pEndpoint->hFile, pCurr->Off + pCurr->DataSeg.cbSeg);
+ }
+
+ rc = RTFileWriteAt(pEndpoint->hFile, pCurr->Off,
+ pCurr->DataSeg.pvSeg,
+ pCurr->DataSeg.cbSeg,
+ NULL);
+ }
+
+ break;
+ }
+ default:
+ AssertMsgFailed(("Invalid transfer type %d\n", pTasks->enmTransferType));
+ }
+
+ pCurr->pfnCompleted(pCurr, pCurr->pvUser, rc);
+ pdmacFileTaskFree(pEndpoint, pCurr);
+ }
+
+ if (pTasks)
+ {
+ /* Add the rest of the tasks to the pending list */
+ pdmacFileAioMgrEpAddTaskList(pEndpoint, pTasks);
+ }
+
+ return VINF_SUCCESS;
+}
+
+static int pdmacFileAioMgrFailsafeProcessEndpoint(PPDMACEPFILEMGR pAioMgr,
+ PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint)
+{
+ int rc = VINF_SUCCESS;
+ PPDMACTASKFILE pTasks = pEndpoint->AioMgr.pReqsPendingHead;
+
+ pEndpoint->AioMgr.pReqsPendingHead = NULL;
+ pEndpoint->AioMgr.pReqsPendingTail = NULL;
+
+ /* Process the request pending list first in case the endpoint was migrated due to an error. */
+ if (pTasks)
+ rc = pdmacFileAioMgrFailsafeProcessEndpointTaskList(pAioMgr, pEndpoint, pTasks);
+
+ if (RT_SUCCESS(rc))
+ {
+ pTasks = pdmacFileEpGetNewTasks(pEndpoint);
+
+ if (pTasks)
+ rc = pdmacFileAioMgrFailsafeProcessEndpointTaskList(pAioMgr, pEndpoint, pTasks);
+ }
+
+ return rc;
+}
+
+/**
+ * A fallback method in case something goes wrong with the normal
+ * I/O manager.
+ */
+DECLCALLBACK(int) pdmacFileAioMgrFailsafe(RTTHREAD hThreadSelf, void *pvUser)
+{
+ int rc = VINF_SUCCESS;
+ PPDMACEPFILEMGR pAioMgr = (PPDMACEPFILEMGR)pvUser;
+ NOREF(hThreadSelf);
+
+ while ( (pAioMgr->enmState == PDMACEPFILEMGRSTATE_RUNNING)
+ || (pAioMgr->enmState == PDMACEPFILEMGRSTATE_SUSPENDING))
+ {
+ ASMAtomicWriteBool(&pAioMgr->fWaitingEventSem, true);
+ if (!ASMAtomicReadBool(&pAioMgr->fWokenUp))
+ rc = RTSemEventWait(pAioMgr->EventSem, pAioMgr->msBwLimitExpired);
+ ASMAtomicWriteBool(&pAioMgr->fWaitingEventSem, false);
+ Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT);
+
+ LogFlow(("Got woken up\n"));
+ ASMAtomicWriteBool(&pAioMgr->fWokenUp, false);
+
+ /* Process endpoint events first. */
+ PPDMASYNCCOMPLETIONENDPOINTFILE pEndpoint = pAioMgr->pEndpointsHead;
+ while (pEndpoint)
+ {
+ pAioMgr->msBwLimitExpired = RT_INDEFINITE_WAIT;
+ rc = pdmacFileAioMgrFailsafeProcessEndpoint(pAioMgr, pEndpoint);
+ AssertRC(rc);
+ pEndpoint = pEndpoint->AioMgr.pEndpointNext;
+ }
+
+ /* Now check for an external blocking event. */
+ if (pAioMgr->fBlockingEventPending)
+ {
+ switch (pAioMgr->enmBlockingEvent)
+ {
+ case PDMACEPFILEAIOMGRBLOCKINGEVENT_ADD_ENDPOINT:
+ {
+ PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointNew = pAioMgr->BlockingEventData.AddEndpoint.pEndpoint;
+ AssertMsg(VALID_PTR(pEndpointNew), ("Adding endpoint event without a endpoint to add\n"));
+
+ pEndpointNew->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_ACTIVE;
+
+ pEndpointNew->AioMgr.pEndpointNext = pAioMgr->pEndpointsHead;
+ pEndpointNew->AioMgr.pEndpointPrev = NULL;
+ if (pAioMgr->pEndpointsHead)
+ pAioMgr->pEndpointsHead->AioMgr.pEndpointPrev = pEndpointNew;
+ pAioMgr->pEndpointsHead = pEndpointNew;
+
+ pAioMgr->cEndpoints++;
+
+ /*
+ * Process the task list the first time. There might be pending requests
+ * if the endpoint was migrated from another endpoint.
+ */
+ rc = pdmacFileAioMgrFailsafeProcessEndpoint(pAioMgr, pEndpointNew);
+ AssertRC(rc);
+ break;
+ }
+ case PDMACEPFILEAIOMGRBLOCKINGEVENT_REMOVE_ENDPOINT:
+ {
+ PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointRemove = pAioMgr->BlockingEventData.RemoveEndpoint.pEndpoint;
+ AssertMsg(VALID_PTR(pEndpointRemove), ("Removing endpoint event without a endpoint to remove\n"));
+
+ pEndpointRemove->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_REMOVING;
+
+ PPDMASYNCCOMPLETIONENDPOINTFILE pPrev = pEndpointRemove->AioMgr.pEndpointPrev;
+ PPDMASYNCCOMPLETIONENDPOINTFILE pNext = pEndpointRemove->AioMgr.pEndpointNext;
+
+ if (pPrev)
+ pPrev->AioMgr.pEndpointNext = pNext;
+ else
+ pAioMgr->pEndpointsHead = pNext;
+
+ if (pNext)
+ pNext->AioMgr.pEndpointPrev = pPrev;
+
+ pAioMgr->cEndpoints--;
+ break;
+ }
+ case PDMACEPFILEAIOMGRBLOCKINGEVENT_CLOSE_ENDPOINT:
+ {
+ PPDMASYNCCOMPLETIONENDPOINTFILE pEndpointClose = pAioMgr->BlockingEventData.CloseEndpoint.pEndpoint;
+ AssertMsg(VALID_PTR(pEndpointClose), ("Close endpoint event without a endpoint to Close\n"));
+
+ pEndpointClose->enmState = PDMASYNCCOMPLETIONENDPOINTFILESTATE_CLOSING;
+
+ /* Make sure all tasks finished. */
+ rc = pdmacFileAioMgrFailsafeProcessEndpoint(pAioMgr, pEndpointClose);
+ AssertRC(rc);
+ break;
+ }
+ case PDMACEPFILEAIOMGRBLOCKINGEVENT_SHUTDOWN:
+ pAioMgr->enmState = PDMACEPFILEMGRSTATE_SHUTDOWN;
+ break;
+ case PDMACEPFILEAIOMGRBLOCKINGEVENT_SUSPEND:
+ pAioMgr->enmState = PDMACEPFILEMGRSTATE_SUSPENDING;
+ break;
+ case PDMACEPFILEAIOMGRBLOCKINGEVENT_RESUME:
+ pAioMgr->enmState = PDMACEPFILEMGRSTATE_RUNNING;
+ break;
+ default:
+ AssertMsgFailed(("Invalid event type %d\n", pAioMgr->enmBlockingEvent));
+ }
+
+ ASMAtomicWriteBool(&pAioMgr->fBlockingEventPending, false);
+ pAioMgr->enmBlockingEvent = PDMACEPFILEAIOMGRBLOCKINGEVENT_INVALID;
+
+ /* Release the waiting thread. */
+ rc = RTSemEventSignal(pAioMgr->EventSemBlock);
+ AssertRC(rc);
+ }
+ }
+
+ return rc;
+}
+