/** * I/O thread for the memory backend. * * @returns IPRT status code. * * @param hThread The thread handle. * @param pvUser Opaque user data. */ static int vdIoBackendMemThread(RTTHREAD hThread, void *pvUser) { PVDIOBACKENDMEM pIoBackend = (PVDIOBACKENDMEM)pvUser; while (pIoBackend->fRunning) { int rc = RTSemEventWait(pIoBackend->EventSem, RT_INDEFINITE_WAIT); if (RT_FAILURE(rc) || !pIoBackend->fRunning) break; PVDIOBACKENDREQ pReq; PPVDIOBACKENDREQ ppReq; size_t cbData; uint32_t cReqsWaiting = ASMAtomicXchgU32(&pIoBackend->cReqsWaiting, 0); while (cReqsWaiting) { int rcReq = VINF_SUCCESS; /* Do we have another request? */ RTCircBufAcquireReadBlock(pIoBackend->pRequestRing, sizeof(PVDIOBACKENDREQ), (void **)&ppReq, &cbData); Assert(!ppReq || cbData == sizeof(PVDIOBACKENDREQ)); RTCircBufReleaseReadBlock(pIoBackend->pRequestRing, cbData); pReq = *ppReq; cReqsWaiting--; LogFlowFunc(("Processing request\n")); switch (pReq->enmTxDir) { case VDIOTXDIR_READ: { RTSGBUF SgBuf; RTSgBufInit(&SgBuf, pReq->aSegs, pReq->cSegs); rcReq = VDMemDiskRead(pReq->pMemDisk, pReq->off, pReq->cbTransfer, &SgBuf); break; } case VDIOTXDIR_WRITE: { RTSGBUF SgBuf; RTSgBufInit(&SgBuf, pReq->aSegs, pReq->cSegs); rcReq = VDMemDiskWrite(pReq->pMemDisk, pReq->off, pReq->cbTransfer, &SgBuf); break; } case VDIOTXDIR_FLUSH: break; default: AssertMsgFailed(("Invalid TX direction!\n")); } /* Notify completion. */ pReq->pfnComplete(pReq->pvUser, rcReq); RTMemFree(pReq); } } return VINF_SUCCESS; }
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; }
/** * 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 shaReadSyncCallback(void *pvUser, void *pvStorage, uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbRead) { /* 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); // DEBUG_PRINT_FLOW(); PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvStorage; int rc = VINF_SUCCESS; // pInt->calls++; // RTPrintf("Read uOffset: %7lu cbRead: %7lu = %7lu\n", uOffset, cbRead, uOffset + cbRead); /* Check if we jump forward in the file. If so we have to read the * remaining stuff in the gap anyway (SHA1/SHA256; streaming). */ if (pInt->cbCurAll < uOffset) { rc = shaReadSyncCallback(pvUser, pvStorage, pInt->cbCurAll, 0, (size_t)(uOffset - pInt->cbCurAll), 0); if (RT_FAILURE(rc)) return rc; // RTPrintf("Gap Read uOffset: %7lu cbRead: %7lu = %7lu\n", uOffset, cbRead, uOffset + cbRead); } size_t cbAllRead = 0; for(;;) { /* Finished? */ if (cbAllRead == cbRead) break; size_t cbAvail = RTCircBufUsed(pInt->pCircBuf); if ( cbAvail == 0 && pInt->fEOF && !RTCircBufIsWriting(pInt->pCircBuf)) { break; } /* If there isn't enough data make sure the worker thread is fetching * more. */ if ((cbRead - cbAllRead) > cbAvail) { rc = shaSignalManifestThread(pInt, STATUS_READ); if(RT_FAILURE(rc)) break; /* If there is _no_ data available, we have to wait until it is. */ if (cbAvail == 0) { rc = shaWaitForManifestThreadFinished(pInt); if (RT_FAILURE(rc)) break; cbAvail = RTCircBufUsed(pInt->pCircBuf); // RTPrintf("############## wait %lu %lu %lu \n", cbRead, cbAllRead, cbAvail); // pInt->waits++; } } size_t cbToRead = RT_MIN(cbRead - cbAllRead, cbAvail); char *pcBuf; size_t cbMemRead = 0; /* Acquire a block for reading from our circular buffer. */ RTCircBufAcquireReadBlock(pInt->pCircBuf, cbToRead, (void**)&pcBuf, &cbMemRead); if (pvBuf) /* Make it possible to blind read data (for skipping) */ memcpy(&((char*)pvBuf)[cbAllRead], pcBuf, cbMemRead); /* Mark the block as empty again. */ RTCircBufReleaseReadBlock(pInt->pCircBuf, cbMemRead); cbAllRead += cbMemRead; pInt->cbCurAll += cbMemRead; } if (pcbRead) *pcbRead = cbAllRead; if (rc == VERR_EOF) rc = VINF_SUCCESS; /* Signal the thread to read more data in the mean time. */ if ( RT_SUCCESS(rc) && RTCircBufFree(pInt->pCircBuf) >= (RTCircBufSize(pInt->pCircBuf) / 2)) rc = shaSignalManifestThread(pInt, STATUS_READ); return rc; }