static int drvscsiAsyncIOLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) { PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI); PRTREQ pReq; int rc; AssertMsgReturn(pThis->hQueueRequests != NIL_RTREQQUEUE, ("hQueueRequests is NULL\n"), VERR_INVALID_STATE); if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 10000 /* 10 sec */)) { LogRel(("drvscsiAsyncIOLoopWakeup#%u: previous dummy request is still pending\n", pDrvIns->iInstance)); return VERR_TIMEOUT; } rc = RTReqQueueCall(pThis->hQueueRequests, &pReq, 10000 /* 10 sec. */, (PFNRT)drvscsiAsyncIOLoopWakeupFunc, 1, pThis); if (RT_SUCCESS(rc)) RTReqRelease(pReq); else { pThis->pPendingDummyReq = pReq; LogRel(("drvscsiAsyncIOLoopWakeup#%u: %Rrc pReq=%p\n", pDrvIns->iInstance, rc, pReq)); } return rc; }
RTDECL(int) RTReqQueueCallV(RTREQQUEUE hQueue, PRTREQ *ppReq, RTMSINTERVAL cMillies, unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, va_list Args) { LogFlow(("RTReqQueueCallV: cMillies=%d fFlags=%#x pfnFunction=%p cArgs=%d\n", cMillies, fFlags, pfnFunction, cArgs)); /* * Check input. */ PRTREQQUEUEINT pQueue = hQueue; AssertPtrReturn(pQueue, VERR_INVALID_HANDLE); AssertReturn(pQueue->u32Magic == RTREQQUEUE_MAGIC, VERR_INVALID_HANDLE); AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER); AssertReturn(!(fFlags & ~(RTREQFLAGS_RETURN_MASK | RTREQFLAGS_NO_WAIT)), VERR_INVALID_PARAMETER); if (!(fFlags & RTREQFLAGS_NO_WAIT) || ppReq) { AssertPtrReturn(ppReq, VERR_INVALID_POINTER); *ppReq = NULL; } PRTREQ pReq = NULL; AssertMsgReturn(cArgs * sizeof(uintptr_t) <= sizeof(pReq->u.Internal.aArgs), ("cArgs=%u\n", cArgs), VERR_TOO_MUCH_DATA); /* * Allocate request */ int rc = RTReqQueueAlloc(pQueue, RTREQTYPE_INTERNAL, &pReq); if (rc != VINF_SUCCESS) return rc; /* * Initialize the request data. */ pReq->fFlags = fFlags; pReq->u.Internal.pfn = pfnFunction; pReq->u.Internal.cArgs = cArgs; for (unsigned iArg = 0; iArg < cArgs; iArg++) pReq->u.Internal.aArgs[iArg] = va_arg(Args, uintptr_t); /* * Queue the request and return. */ rc = RTReqSubmit(pReq, cMillies); if ( rc != VINF_SUCCESS && rc != VERR_TIMEOUT) { RTReqRelease(pReq); pReq = NULL; } if (!(fFlags & RTREQFLAGS_NO_WAIT)) { *ppReq = pReq; LogFlow(("RTReqQueueCallV: returns %Rrc *ppReq=%p\n", rc, pReq)); } else LogFlow(("RTReqQueueCallV: returns %Rrc\n", rc)); Assert(rc != VERR_INTERRUPTED); return rc; }
/** * Deals with any pending dummy request * * @returns true if no pending dummy request, false if still pending. * @param pThis The instance data. * @param cMillies The number of milliseconds to wait for any * pending request to finish. */ static bool drvscsiAsyncIOLoopNoPendingDummy(PDRVSCSI pThis, uint32_t cMillies) { if (!pThis->pPendingDummyReq) return true; int rc = RTReqWait(pThis->pPendingDummyReq, cMillies); if (RT_FAILURE(rc)) return false; RTReqRelease(pThis->pPendingDummyReq); pThis->pPendingDummyReq = NULL; return true; }
/** * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff. * * @param pDrvIns The driver instance. * @param pfnAsyncNotify The async callback. */ static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify) { PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI); if (!pThis->pDrvBlockAsync) { if (pThis->hQueueRequests != NIL_RTREQQUEUE) return; ASMAtomicWriteBool(&pThis->fDummySignal, true); if (drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/)) { if (!RTReqQueueIsBusy(pThis->hQueueRequests)) { ASMAtomicWriteBool(&pThis->fDummySignal, false); return; } PRTREQ pReq; int rc = RTReqQueueCall(pThis->hQueueRequests, &pReq, 0 /*ms*/, (PFNRT)drvscsiAsyncIOLoopSyncCallback, 1, pThis); if (RT_SUCCESS(rc)) { ASMAtomicWriteBool(&pThis->fDummySignal, false); RTReqRelease(pReq); return; } pThis->pPendingDummyReq = pReq; } } else { if (pThis->StatIoDepth > 0) { ASMAtomicWriteBool(&pThis->fDummySignal, true); } return; } PDMDrvHlpSetAsyncNotification(pDrvIns, pfnAsyncNotify); }
/** * Process one request. * * @returns IPRT status code. * * @param pReq Request packet to process. */ DECLHIDDEN(int) rtReqProcessOne(PRTREQINT pReq) { LogFlow(("rtReqProcessOne: pReq=%p type=%d fFlags=%#x\n", pReq, pReq->enmType, pReq->fFlags)); /* * Process the request. */ Assert(pReq->enmState == RTREQSTATE_QUEUED); pReq->enmState = RTREQSTATE_PROCESSING; int rcRet = VINF_SUCCESS; /* the return code of this function. */ int rcReq = VERR_NOT_IMPLEMENTED; /* the request status. */ switch (pReq->enmType) { /* * A packed down call frame. */ case RTREQTYPE_INTERNAL: { uintptr_t *pauArgs = &pReq->u.Internal.aArgs[0]; union { PFNRT pfn; DECLCALLBACKMEMBER(int, pfn00)(void); DECLCALLBACKMEMBER(int, pfn01)(uintptr_t); DECLCALLBACKMEMBER(int, pfn02)(uintptr_t, uintptr_t); DECLCALLBACKMEMBER(int, pfn03)(uintptr_t, uintptr_t, uintptr_t); DECLCALLBACKMEMBER(int, pfn04)(uintptr_t, uintptr_t, uintptr_t, uintptr_t); DECLCALLBACKMEMBER(int, pfn05)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); DECLCALLBACKMEMBER(int, pfn06)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); DECLCALLBACKMEMBER(int, pfn07)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); DECLCALLBACKMEMBER(int, pfn08)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); DECLCALLBACKMEMBER(int, pfn09)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); DECLCALLBACKMEMBER(int, pfn10)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); DECLCALLBACKMEMBER(int, pfn11)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); DECLCALLBACKMEMBER(int, pfn12)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); } u; u.pfn = pReq->u.Internal.pfn; #ifndef RT_ARCH_X86 switch (pReq->u.Internal.cArgs) { case 0: rcRet = u.pfn00(); break; case 1: rcRet = u.pfn01(pauArgs[0]); break; case 2: rcRet = u.pfn02(pauArgs[0], pauArgs[1]); break; case 3: rcRet = u.pfn03(pauArgs[0], pauArgs[1], pauArgs[2]); break; case 4: rcRet = u.pfn04(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3]); break; case 5: rcRet = u.pfn05(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4]); break; case 6: rcRet = u.pfn06(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5]); break; case 7: rcRet = u.pfn07(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6]); break; case 8: rcRet = u.pfn08(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7]); break; case 9: rcRet = u.pfn09(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8]); break; case 10: rcRet = u.pfn10(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8], pauArgs[9]); break; case 11: rcRet = u.pfn11(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8], pauArgs[9], pauArgs[10]); break; case 12: rcRet = u.pfn12(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8], pauArgs[9], pauArgs[10], pauArgs[11]); break; default: AssertReleaseMsgFailed(("cArgs=%d\n", pReq->u.Internal.cArgs)); rcRet = rcReq = VERR_INTERNAL_ERROR; break; } #else /* RT_ARCH_X86 */ size_t cbArgs = pReq->u.Internal.cArgs * sizeof(uintptr_t); # ifdef __GNUC__ __asm__ __volatile__("movl %%esp, %%edx\n\t" "subl %2, %%esp\n\t" "andl $0xfffffff0, %%esp\n\t" "shrl $2, %2\n\t" "movl %%esp, %%edi\n\t" "rep movsl\n\t" "movl %%edx, %%edi\n\t" "call *%%eax\n\t" "mov %%edi, %%esp\n\t" : "=a" (rcRet), "=S" (pauArgs), "=c" (cbArgs) : "0" (u.pfn), "1" (pauArgs), "2" (cbArgs) : "edi", "edx"); # else __asm { xor edx, edx /* just mess it up. */ mov eax, u.pfn mov ecx, cbArgs shr ecx, 2 mov esi, pauArgs mov ebx, esp sub esp, cbArgs and esp, 0xfffffff0 mov edi, esp rep movsd call eax mov esp, ebx mov rcRet, eax } # endif #endif /* RT_ARCH_X86 */ if ((pReq->fFlags & (RTREQFLAGS_RETURN_MASK)) == RTREQFLAGS_VOID) rcRet = VINF_SUCCESS; rcReq = rcRet; break; } default: AssertMsgFailed(("pReq->enmType=%d\n", pReq->enmType)); rcReq = VERR_NOT_IMPLEMENTED; break; } /* * Complete the request and then release our request handle reference. */ pReq->iStatusX = rcReq; pReq->enmState = RTREQSTATE_COMPLETED; if (pReq->fFlags & RTREQFLAGS_NO_WAIT) LogFlow(("rtReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc (no wait)\n", pReq, rcReq, rcRet)); else { /* Notify the waiting thread. */ LogFlow(("rtReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc - notifying waiting thread\n", pReq, rcReq, rcRet)); ASMAtomicXchgSize(&pReq->fEventSemClear, false); int rc2 = RTSemEventSignal(pReq->EventSem); if (rc2 != VINF_SUCCESS) { AssertRC(rc2); rcRet = rc2; } } RTReqRelease(pReq); return rcRet; }