int VDIoBackendMemTransfer(PVDIOBACKENDMEM pIoBackend, PVDMEMDISK pMemDisk, VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer, PRTSGBUF pSgBuf, PFNVDIOCOMPLETE pfnComplete, void *pvUser) { PVDIOBACKENDREQ pReq = NULL; PPVDIOBACKENDREQ ppReq = NULL; size_t cbData; unsigned cSegs = 0; LogFlowFunc(("Queuing request\n")); if (enmTxDir != VDIOTXDIR_FLUSH) RTSgBufSegArrayCreate(pSgBuf, NULL, &cSegs, cbTransfer); pReq = (PVDIOBACKENDREQ)RTMemAlloc(RT_OFFSETOF(VDIOBACKENDREQ, aSegs[cSegs])); if (!pReq) return VERR_NO_MEMORY; RTCircBufAcquireWriteBlock(pIoBackend->pRequestRing, sizeof(PVDIOBACKENDREQ), (void **)&ppReq, &cbData); if (!pReq) { RTMemFree(pReq); return VERR_NO_MEMORY; } Assert(cbData == sizeof(PVDIOBACKENDREQ)); pReq->enmTxDir = enmTxDir; pReq->cbTransfer = cbTransfer; pReq->off = off; pReq->pMemDisk = pMemDisk; pReq->pfnComplete = pfnComplete; pReq->pvUser = pvUser; if (enmTxDir != VDIOTXDIR_FLUSH) { RTSgBufSegArrayCreate(pSgBuf, &pReq->aSegs[0], &cSegs, cbTransfer); RTSgBufInit(&pReq->SgBuf, pReq->aSegs, cSegs); } *ppReq = pReq; RTCircBufReleaseWriteBlock(pIoBackend->pRequestRing, sizeof(PVDIOBACKENDREQ)); uint32_t cReqsWaiting = ASMAtomicIncU32(&pIoBackend->cReqsWaiting); if (cReqsWaiting == 1) vdIoBackendMemThreadPoke(pIoBackend); return VINF_SUCCESS; }
int VDIoBackendMemTransfer(PVDIOBACKENDMEM pIoBackend, PVDMEMDISK pMemDisk, VDIOTXDIR enmTxDir, uint64_t off, size_t cbTransfer, PCRTSGSEG paSegs, unsigned cSegs, PFNVDIOCOMPLETE pfnComplete, void *pvUser) { PVDIOBACKENDREQ pReq = NULL; PPVDIOBACKENDREQ ppReq = NULL; size_t cbData; LogFlowFunc(("Queuing request\n")); pReq = (PVDIOBACKENDREQ)RTMemAlloc(RT_OFFSETOF(VDIOBACKENDREQ, aSegs[cSegs])); if (!pReq) return VERR_NO_MEMORY; RTCircBufAcquireWriteBlock(pIoBackend->pRequestRing, sizeof(PVDIOBACKENDREQ), (void **)&ppReq, &cbData); if (!pReq) { RTMemFree(pReq); return VERR_NO_MEMORY; } Assert(cbData == sizeof(PVDIOBACKENDREQ)); pReq->enmTxDir = enmTxDir; pReq->cbTransfer = cbTransfer; pReq->off = off; pReq->pMemDisk = pMemDisk; pReq->cSegs = cSegs; pReq->pfnComplete = pfnComplete; pReq->pvUser = pvUser; for (unsigned i = 0; i < cSegs; i++) { pReq->aSegs[i].pvSeg = paSegs[i].pvSeg; pReq->aSegs[i].cbSeg = paSegs[i].cbSeg; } *ppReq = pReq; RTCircBufReleaseWriteBlock(pIoBackend->pRequestRing, sizeof(PVDIOBACKENDREQ)); uint32_t cReqsWaiting = ASMAtomicIncU32(&pIoBackend->cReqsWaiting); if (cReqsWaiting == 1) vdIoBackendMemThreadPoke(pIoBackend); return VINF_SUCCESS; }
/** * Basic API checks. */ static void tst1(void) { void *pvBuf; size_t cbSize; char pcTestPattern1[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9 }; char pcTestPattern2[] = { 0x8, 0x9, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9 }; char pcTestPattern3[] = { 0x5, 0x6, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }; /* Create */ RTTestISub("Creation"); PRTCIRCBUF pBuf; RTTESTI_CHECK_RC(RTCircBufCreate(&pBuf, 10), VINF_SUCCESS); RTTESTI_CHECK(RTCircBufFree(pBuf) == 10); RTTESTI_CHECK(RTCircBufUsed(pBuf) == 0); /* Full write */ RTTestISub("Full write"); RTCircBufAcquireWriteBlock(pBuf, 10, &pvBuf, &cbSize); RTTESTI_CHECK(cbSize == 10); memcpy(pvBuf, pcTestPattern1, 10); RTCircBufReleaseWriteBlock(pBuf, 10); RTTESTI_CHECK(RTCircBufFree(pBuf) == 0); RTTESTI_CHECK(RTCircBufUsed(pBuf) == 10); // RTTESTI_CHECK(memcmp(pBuf->pvBuf, pcTestPattern1, 10) == 0); /* Check the internal state */ /* Half read */ RTTestISub("Half read"); RTCircBufAcquireReadBlock(pBuf, 5, &pvBuf, &cbSize); RTTESTI_CHECK(cbSize == 5); RTTESTI_CHECK(memcmp(pvBuf, pcTestPattern1, 5) == 0); RTCircBufReleaseReadBlock(pBuf, 5); RTTESTI_CHECK(RTCircBufFree(pBuf) == 5); RTTESTI_CHECK(RTCircBufUsed(pBuf) == 5); /* Sub write */ RTTestISub("Sub write"); RTCircBufAcquireWriteBlock(pBuf, 2, &pvBuf, &cbSize); RTTESTI_CHECK(cbSize == 2); memcpy(pvBuf, &pcTestPattern1[8], 2); RTCircBufReleaseWriteBlock(pBuf, 2); RTTESTI_CHECK(RTCircBufFree(pBuf) == 3); RTTESTI_CHECK(RTCircBufUsed(pBuf) == 7); // RTTESTI_CHECK(memcmp(pBuf->pvBuf, pcTestPattern2, 10) == 0); /* Check the internal state */ /* Split tests */ /* Split read */ RTTestISub("Split read"); RTCircBufAcquireReadBlock(pBuf, 7, &pvBuf, &cbSize); RTTESTI_CHECK(cbSize == 5); RTTESTI_CHECK(memcmp(pvBuf, &pcTestPattern1[5], 5) == 0); RTCircBufReleaseReadBlock(pBuf, 5); RTTESTI_CHECK(RTCircBufFree(pBuf) == 8); RTTESTI_CHECK(RTCircBufUsed(pBuf) == 2); RTCircBufAcquireReadBlock(pBuf, 2, &pvBuf, &cbSize); RTTESTI_CHECK(cbSize == 2); RTTESTI_CHECK(memcmp(pvBuf, &pcTestPattern1[8], 2) == 0); RTCircBufReleaseReadBlock(pBuf, 2); RTTESTI_CHECK(RTCircBufFree(pBuf) == 10); RTTESTI_CHECK(RTCircBufUsed(pBuf) == 0); /* Split write */ RTTestISub("Split write"); RTCircBufAcquireWriteBlock(pBuf, 10, &pvBuf, &cbSize); RTTESTI_CHECK(cbSize == 8); memcpy(pvBuf, pcTestPattern1, 8); RTCircBufReleaseWriteBlock(pBuf, 8); RTTESTI_CHECK(RTCircBufFree(pBuf) == 2); RTTESTI_CHECK(RTCircBufUsed(pBuf) == 8); RTCircBufAcquireWriteBlock(pBuf, 2, &pvBuf, &cbSize); RTTESTI_CHECK(cbSize == 2); memcpy(pvBuf, &pcTestPattern1[5], 2); RTCircBufReleaseWriteBlock(pBuf, 2); RTTESTI_CHECK(RTCircBufFree(pBuf) == 0); RTTESTI_CHECK(RTCircBufUsed(pBuf) == 10); // RTTESTI_CHECK(memcmp(pBuf->pvBuf, pcTestPattern3, 10) == 0); /* Check the internal state */ /* Destroy */ RTCircBufDestroy(pBuf); }
static int shaWriteSyncCallback(void *pvUser, void *pvStorage, uint64_t uOffset, const void *pvBuf, size_t cbWrite, size_t *pcbWritten) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces); AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER); PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvStorage; DEBUG_PRINT_FLOW(); /* Check that the write is linear */ AssertMsgReturn(pInt->cbCurAll <= uOffset, ("Backward seeking is not allowed (uOffset: %7lu cbCurAll: %7lu)!", uOffset, pInt->cbCurAll), VERR_INVALID_PARAMETER); int rc = VINF_SUCCESS; /* Check if we have to add some free space at the end, before we start the * real write. */ if (pInt->cbCurAll < uOffset) { size_t cbSize = (size_t)(uOffset - pInt->cbCurAll); size_t cbAllWritten = 0; for(;;) { /* Finished? */ if (cbAllWritten == cbSize) break; size_t cbToWrite = RT_MIN(pInt->cbZeroBuf, cbSize - cbAllWritten); size_t cbWritten = 0; rc = shaWriteSyncCallback(pvUser, pvStorage, pInt->cbCurAll, pInt->pvZeroBuf, cbToWrite, &cbWritten); if (RT_FAILURE(rc)) break; cbAllWritten += cbWritten; } if (RT_FAILURE(rc)) return rc; } // RTPrintf("Write uOffset: %7lu cbWrite: %7lu = %7lu\n", uOffset, cbWrite, uOffset + cbWrite); size_t cbAllWritten = 0; for(;;) { /* Finished? */ if (cbAllWritten == cbWrite) break; size_t cbAvail = RTCircBufFree(pInt->pCircBuf); if ( cbAvail == 0 && pInt->fEOF) return VERR_EOF; /* If there isn't enough free space make sure the worker thread is * writing some data. */ if ((cbWrite - cbAllWritten) > cbAvail) { rc = shaSignalManifestThread(pInt, STATUS_WRITE); if(RT_FAILURE(rc)) break; /* If there is _no_ free space available, we have to wait until it is. */ if (cbAvail == 0) { rc = shaWaitForManifestThreadFinished(pInt); if (RT_FAILURE(rc)) break; cbAvail = RTCircBufFree(pInt->pCircBuf); // RTPrintf("############## wait %lu %lu %lu \n", cbRead, cbAllRead, cbAvail); // pInt->waits++; } } size_t cbToWrite = RT_MIN(cbWrite - cbAllWritten, cbAvail); char *pcBuf; size_t cbMemWritten = 0; /* Acquire a block for writing from our circular buffer. */ RTCircBufAcquireWriteBlock(pInt->pCircBuf, cbToWrite, (void**)&pcBuf, &cbMemWritten); memcpy(pcBuf, &((char*)pvBuf)[cbAllWritten], cbMemWritten); /* Mark the block full. */ RTCircBufReleaseWriteBlock(pInt->pCircBuf, cbMemWritten); cbAllWritten += cbMemWritten; pInt->cbCurAll += cbMemWritten; } if (pcbWritten) *pcbWritten = cbAllWritten; /* Signal the thread to write more data in the mean time. */ if ( RT_SUCCESS(rc) && RTCircBufUsed(pInt->pCircBuf) >= (RTCircBufSize(pInt->pCircBuf) / 2)) rc = shaSignalManifestThread(pInt, STATUS_WRITE); return rc; }
DECLCALLBACK(int) shaCalcWorkerThread(RTTHREAD /* aThread */, void *pvUser) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvUser; PVDINTERFACEIO pIfIo = VDIfIoGet(pInt->pShaStorage->pVDImageIfaces); AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER); int rc = VINF_SUCCESS; bool fLoop = true; while(fLoop) { /* What should we do next? */ uint32_t u32Status = ASMAtomicReadU32(&pInt->u32Status); // RTPrintf("status: %d\n", u32Status); switch (u32Status) { case STATUS_WAIT: { /* Wait for new work. */ rc = RTSemEventWait(pInt->newStatusEvent, 100); if ( RT_FAILURE(rc) && rc != VERR_TIMEOUT) fLoop = false; break; } case STATUS_WRITE: { ASMAtomicCmpXchgU32(&pInt->u32Status, STATUS_WRITING, STATUS_WRITE); size_t cbAvail = RTCircBufUsed(pInt->pCircBuf); size_t cbMemAllRead = 0; /* First loop over all the free memory in the circular * memory buffer (could be turn around at the end). */ for(;;) { if ( cbMemAllRead == cbAvail || fLoop == false) break; char *pcBuf; size_t cbMemToRead = cbAvail - cbMemAllRead; size_t cbMemRead = 0; /* Try to acquire all the used space of the circular buffer. */ RTCircBufAcquireReadBlock(pInt->pCircBuf, cbMemToRead, (void**)&pcBuf, &cbMemRead); size_t cbAllWritten = 0; /* Second, write as long as used memory is there. The write * method could also split the writes up into to smaller * parts. */ for(;;) { if (cbAllWritten == cbMemRead) break; size_t cbToWrite = cbMemRead - cbAllWritten; size_t cbWritten = 0; rc = vdIfIoFileWriteSync(pIfIo, pInt->pvStorage, pInt->cbCurFile, &pcBuf[cbAllWritten], cbToWrite, &cbWritten); // RTPrintf ("%lu %lu %lu %Rrc\n", pInt->cbCurFile, cbToRead, cbRead, rc); if (RT_FAILURE(rc)) { fLoop = false; break; } cbAllWritten += cbWritten; pInt->cbCurFile += cbWritten; } /* Update the SHA1/SHA256 context with the next data block. */ if ( RT_SUCCESS(rc) && pInt->pShaStorage->fCreateDigest) { if (pInt->pShaStorage->fSha256) RTSha256Update(&pInt->ctx.Sha256, pcBuf, cbAllWritten); else RTSha1Update(&pInt->ctx.Sha1, pcBuf, cbAllWritten); } /* Mark the block as empty. */ RTCircBufReleaseReadBlock(pInt->pCircBuf, cbAllWritten); cbMemAllRead += cbAllWritten; } /* Reset the thread status and signal the main thread that we * are finished. Use CmpXchg, so we not overwrite other states * which could be signaled in the meantime. */ if (ASMAtomicCmpXchgU32(&pInt->u32Status, STATUS_WAIT, STATUS_WRITING)) rc = RTSemEventSignal(pInt->workFinishedEvent); break; } case STATUS_READ: { ASMAtomicCmpXchgU32(&pInt->u32Status, STATUS_READING, STATUS_READ); size_t cbAvail = RTCircBufFree(pInt->pCircBuf); size_t cbMemAllWrite = 0; /* First loop over all the available memory in the circular * memory buffer (could be turn around at the end). */ for(;;) { if ( cbMemAllWrite == cbAvail || fLoop == false) break; char *pcBuf; size_t cbMemToWrite = cbAvail - cbMemAllWrite; size_t cbMemWrite = 0; /* Try to acquire all the free space of the circular buffer. */ RTCircBufAcquireWriteBlock(pInt->pCircBuf, cbMemToWrite, (void**)&pcBuf, &cbMemWrite); /* Second, read as long as we filled all the memory. The * read method could also split the reads up into to * smaller parts. */ size_t cbAllRead = 0; for(;;) { if (cbAllRead == cbMemWrite) break; size_t cbToRead = cbMemWrite - cbAllRead; size_t cbRead = 0; rc = vdIfIoFileReadSync(pIfIo, pInt->pvStorage, pInt->cbCurFile, &pcBuf[cbAllRead], cbToRead, &cbRead); // RTPrintf ("%lu %lu %lu %Rrc\n", pInt->cbCurFile, cbToRead, cbRead, rc); if (RT_FAILURE(rc)) { fLoop = false; break; } /* This indicates end of file. Stop reading. */ if (cbRead == 0) { fLoop = false; ASMAtomicWriteBool(&pInt->fEOF, true); break; } cbAllRead += cbRead; pInt->cbCurFile += cbRead; } /* Update the SHA1/SHA256 context with the next data block. */ if ( RT_SUCCESS(rc) && pInt->pShaStorage->fCreateDigest) { if (pInt->pShaStorage->fSha256) RTSha256Update(&pInt->ctx.Sha256, pcBuf, cbAllRead); else RTSha1Update(&pInt->ctx.Sha1, pcBuf, cbAllRead); } /* Mark the block as full. */ RTCircBufReleaseWriteBlock(pInt->pCircBuf, cbAllRead); cbMemAllWrite += cbAllRead; } /* Reset the thread status and signal the main thread that we * are finished. Use CmpXchg, so we not overwrite other states * which could be signaled in the meantime. */ if (ASMAtomicCmpXchgU32(&pInt->u32Status, STATUS_WAIT, STATUS_READING)) rc = RTSemEventSignal(pInt->workFinishedEvent); break; } case STATUS_END: { /* End signaled */ fLoop = false; break; } } } /* Cleanup any status changes to indicate we are finished. */ ASMAtomicWriteU32(&pInt->u32Status, STATUS_END); rc = RTSemEventSignal(pInt->workFinishedEvent); return rc; }