- Timestamp:
- Apr 8, 2010 8:35:17 PM (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp
r27561 r28114 32 32 #include <iprt/mem.h> 33 33 #include <iprt/message.h> 34 #include <iprt/sg.h> 34 35 35 36 #include "Builtins.h" … … 39 40 * Structures and Typedefs * 40 41 *******************************************************************************/ 42 43 /** 44 * async I/O request. 45 */ 46 typedef struct DRVDISKAIOREQ 47 { 48 /** Flag whether this is a read or write request. */ 49 bool fRead; 50 /** Start offset. */ 51 uint64_t off; 52 /** Transfer size. */ 53 size_t cbTransfer; 54 /** Segment array. */ 55 PCRTSGSEG paSeg; 56 /** Number of array entries. */ 57 unsigned cSeg; 58 /** User argument */ 59 void *pvUser; 60 } DRVDISKAIOREQ, *PDRVDISKAIOREQ; 41 61 42 62 /** … … 85 105 PDMIMEDIA IMedia; 86 106 107 /** Pointer to the media async driver below us. 108 * This is NULL if the media is not mounted. */ 109 PPDMIMEDIAASYNC pDrvMediaAsync; 110 /** Our media async interface */ 111 PDMIMEDIAASYNC IMediaAsync; 112 113 /** The async media port interface above. */ 114 PPDMIMEDIAASYNCPORT pDrvMediaAsyncPort; 115 /** Our media async port interface */ 116 PDMIMEDIAASYNCPORT IMediaAsyncPort; 117 87 118 /** AVL tree containing the disk blocks to check. */ 88 119 PAVLRFOFFTREE pTreeSegments; … … 90 121 91 122 92 /* -=-=-=-=- IMedia -=-=-=-=- */ 93 94 /** Makes a PDRVDISKINTEGRITY out of a PPDMIMEDIA. */ 95 #define PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY)((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMedia)) ) 96 97 /******************************************************************************* 98 * Media interface methods * 99 *******************************************************************************/ 100 101 /** @copydoc PDMIMEDIA::pfnRead */ 102 static DECLCALLBACK(int) drvdiskintRead(PPDMIMEDIA pInterface, 103 uint64_t off, void *pvBuf, size_t cbRead) 104 { 105 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface); 106 int rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, off, pvBuf, cbRead); 107 if (RT_FAILURE(rc)) 108 return rc; 109 110 Assert(off % 512 == 0); 111 Assert(cbRead % 512 == 0); 112 113 /* Compare read data */ 114 size_t cbLeft = cbRead; 115 RTFOFF offCurr = (RTFOFF)off; 116 uint8_t *pbBuf = (uint8_t *)pvBuf; 117 118 while (cbLeft) 123 /** 124 * Allocate a new I/O request. 125 * 126 * @returns New I/O request. 127 * @param fRead Flag whether this is a read or a write. 128 * @param off Start offset. 129 * @param paSeg Segment array. 130 * @param cSeg Number of segments. 131 * @param cbTransfer Number of bytes to transfer. 132 * @param pvUser User argument. 133 */ 134 static PDRVDISKAIOREQ drvdiskintIoReqAlloc(bool fRead, uint64_t off, PCRTSGSEG paSeg, 135 unsigned cSeg, size_t cbTransfer, void *pvUser) 136 { 137 PDRVDISKAIOREQ pIoReq = (PDRVDISKAIOREQ)RTMemAlloc(sizeof(DRVDISKAIOREQ)); 138 139 if (RT_LIKELY(pIoReq)) 119 140 { 120 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr); 121 size_t cbRange = 0; 122 bool fCmp = false; 123 unsigned offSeg = 0; 124 125 if (!pSeg) 126 { 127 /* Get next segment */ 128 pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true); 129 if (!pSeg) 130 { 131 /* No data in the tree for this read. Assume everything is ok. */ 132 cbRange = cbLeft; 133 } 134 else if (offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key) 135 cbRange = cbLeft; 136 else 137 cbRange = pSeg->Core.Key - offCurr; 138 } 139 else 140 { 141 fCmp = true; 142 offSeg = offCurr - pSeg->Core.Key; 143 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr)); 144 } 145 146 if ( fCmp 147 && memcmp(pbBuf, pSeg->pbSeg + offSeg, cbRange)) 148 { 149 unsigned offWrong = 0; 150 for (offWrong = 0; offWrong < cbRange; offWrong++) 151 if (pbBuf[offWrong] != pSeg->pbSeg[offSeg + offWrong]) 152 { 153 /* Corrupted disk, print I/O log entry of the last write which accessed this range. */ 154 uint32_t cSector = (offSeg + offWrong) / 512; 155 AssertMsg(cSector < pSeg->cIoLogEntries, ("Internal bug!\n")); 156 157 RTMsgError("Corrupted disk at offset %llu (%u bytes in the current read buffer)!\n", 158 offCurr + offWrong, offWrong); 159 RTMsgError("Last write to this sector started at offset %llu with %u bytes (%u references to this log entry)\n", 160 pSeg->apIoLog[cSector]->off, 161 pSeg->apIoLog[cSector]->cbWrite, 162 pSeg->apIoLog[cSector]->cRefs); 163 RTAssertDebugBreak(); 164 } 165 } 166 167 offCurr += cbRange; 168 cbLeft -= cbRange; 169 pbBuf += cbRange; 141 pIoReq->fRead = fRead; 142 pIoReq->off = off; 143 pIoReq->cbTransfer = cbTransfer; 144 pIoReq->paSeg = paSeg; 145 pIoReq->cSeg = cSeg; 146 pIoReq->pvUser = pvUser; 170 147 } 171 148 172 return rc; 173 } 174 175 /** @copydoc PDMIMEDIA::pfnWrite */ 176 static DECLCALLBACK(int) drvdiskintWrite(PPDMIMEDIA pInterface, 177 uint64_t off, const void *pvBuf, 178 size_t cbWrite) 179 { 180 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface); 181 int rc = pThis->pDrvMedia->pfnWrite(pThis->pDrvMedia, off, pvBuf, cbWrite); 182 if (RT_FAILURE(rc)) 183 return rc; 149 return pIoReq; 150 } 151 152 /** 153 * Record a successful write to the virtual disk. 154 * 155 * @returns VBox status code. 156 * @param pThis Disk integrity driver instance data. 157 * @param paSeg Segment array of the write to record. 158 * @param cSeg Number of segments. 159 * @param off Start offset. 160 * @param cbWrite Number of bytes to record. 161 */ 162 static int drvdiskintWriteRecord(PDRVDISKINTEGRITY pThis, PCRTSGSEG paSeg, unsigned cSeg, 163 uint64_t off, size_t cbWrite) 164 { 165 int rc = VINF_SUCCESS; 184 166 185 167 /* Update the segments */ 186 168 size_t cbLeft = cbWrite; 187 169 RTFOFF offCurr = (RTFOFF)off; 188 uint8_t *pbBuf = (uint8_t *)pvBuf;170 RTSGBUF SgBuf; 189 171 PIOLOGENT pIoLogEnt = (PIOLOGENT)RTMemAllocZ(sizeof(IOLOGENT)); 190 172 if (!pIoLogEnt) … … 194 176 pIoLogEnt->cbWrite = cbWrite; 195 177 pIoLogEnt->cRefs = 0; 178 179 RTSgBufInit(&SgBuf, paSeg, cSeg); 196 180 197 181 while (cbLeft) … … 243 227 { 244 228 AssertPtr(pSeg); 245 memcpy(pSeg->pbSeg + offSeg, pbBuf, cbRange);229 RTSgBufCopyToBuf(&SgBuf, pSeg->pbSeg + offSeg, cbRange); 246 230 247 231 /* Update the I/O log pointers */ … … 269 253 } 270 254 } 255 else 256 RTSgBufAdvance(&SgBuf, cbRange); 271 257 272 258 offCurr += cbRange; 273 259 cbLeft -= cbRange; 274 pbBuf += cbRange;275 260 } 276 261 262 return rc; 263 } 264 265 /** 266 * Verifies a read request. 267 * 268 * @returns VBox status code. 269 * @param pThis Disk integrity driver instance data. 270 * @param paSeg Segment array of the containing the data buffers to verify. 271 * @param cSeg Number of segments. 272 * @param off Start offset. 273 * @param cbWrite Number of bytes to verify. 274 */ 275 static int drvdiskintReadVerify(PDRVDISKINTEGRITY pThis, PCRTSGSEG paSeg, unsigned cSeg, 276 uint64_t off, size_t cbRead) 277 { 278 int rc = VINF_SUCCESS; 279 280 Assert(off % 512 == 0); 281 Assert(cbRead % 512 == 0); 282 283 /* Compare read data */ 284 size_t cbLeft = cbRead; 285 RTFOFF offCurr = (RTFOFF)off; 286 RTSGBUF SgBuf; 287 288 RTSgBufInit(&SgBuf, paSeg, cSeg); 289 290 while (cbLeft) 291 { 292 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr); 293 size_t cbRange = 0; 294 bool fCmp = false; 295 unsigned offSeg = 0; 296 297 if (!pSeg) 298 { 299 /* Get next segment */ 300 pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true); 301 if (!pSeg) 302 { 303 /* No data in the tree for this read. Assume everything is ok. */ 304 cbRange = cbLeft; 305 } 306 else if (offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key) 307 cbRange = cbLeft; 308 else 309 cbRange = pSeg->Core.Key - offCurr; 310 } 311 else 312 { 313 fCmp = true; 314 offSeg = offCurr - pSeg->Core.Key; 315 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr)); 316 } 317 318 if (fCmp) 319 { 320 RTSGSEG Seg; 321 RTSGBUF SgBufCmp; 322 323 Seg.cbSeg = cbRange; 324 Seg.pvSeg = pSeg->pbSeg + offSeg; 325 326 RTSgBufInit(&SgBufCmp, &Seg, 1); 327 if (RTSgBufCmp(&SgBuf, &SgBufCmp, cbRange)) 328 { 329 unsigned offWrong = 0; 330 #if 0 331 for (offWrong = 0; offWrong < cbRange; offWrong++) 332 if (pbBuf[offWrong] != pSeg->pbSeg[offSeg + offWrong]) 333 #endif 334 { 335 /* Corrupted disk, print I/O log entry of the last write which accessed this range. */ 336 uint32_t cSector = (offSeg + offWrong) / 512; 337 AssertMsg(cSector < pSeg->cIoLogEntries, ("Internal bug!\n")); 338 339 RTMsgError("Corrupted disk at offset %llu (%u bytes in the current read buffer)!\n", 340 offCurr + offWrong, offWrong); 341 RTMsgError("Last write to this sector started at offset %llu with %u bytes (%u references to this log entry)\n", 342 pSeg->apIoLog[cSector]->off, 343 pSeg->apIoLog[cSector]->cbWrite, 344 pSeg->apIoLog[cSector]->cRefs); 345 RTAssertDebugBreak(); 346 } 347 } 348 } 349 350 offCurr += cbRange; 351 cbLeft -= cbRange; 352 RTSgBufAdvance(&SgBuf, cbRange); 353 } 354 355 return rc; 356 } 357 358 /* -=-=-=-=- IMedia -=-=-=-=- */ 359 360 /** Makes a PDRVDISKINTEGRITY out of a PPDMIMEDIA. */ 361 #define PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY)((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMedia)) ) 362 /** Makes a PDRVDISKINTEGRITY out of a PPDMIMEDIAASYNC. */ 363 #define PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY)((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMediaAsync)) ) 364 365 /******************************************************************************* 366 * Media interface methods * 367 *******************************************************************************/ 368 369 /** @copydoc PDMIMEDIA::pfnRead */ 370 static DECLCALLBACK(int) drvdiskintRead(PPDMIMEDIA pInterface, 371 uint64_t off, void *pvBuf, size_t cbRead) 372 { 373 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface); 374 int rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, off, pvBuf, cbRead); 375 if (RT_FAILURE(rc)) 376 return rc; 377 378 /* Verify the read. */ 379 RTSGSEG Seg; 380 Seg.cbSeg = cbRead; 381 Seg.pvSeg = pvBuf; 382 return drvdiskintReadVerify(pThis, &Seg, 1, off, cbRead); 383 } 384 385 /** @copydoc PDMIMEDIA::pfnWrite */ 386 static DECLCALLBACK(int) drvdiskintWrite(PPDMIMEDIA pInterface, 387 uint64_t off, const void *pvBuf, 388 size_t cbWrite) 389 { 390 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface); 391 int rc = pThis->pDrvMedia->pfnWrite(pThis->pDrvMedia, off, pvBuf, cbWrite); 392 if (RT_FAILURE(rc)) 393 return rc; 394 395 /* Record the write. */ 396 RTSGSEG Seg; 397 Seg.cbSeg = cbWrite; 398 Seg.pvSeg = (void *)pvBuf; 399 return drvdiskintWriteRecord(pThis, &Seg, 1, off, cbWrite); 400 } 401 402 static DECLCALLBACK(int) drvdiskintStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset, 403 PCRTSGSEG paSeg, unsigned cSeg, 404 size_t cbRead, void *pvUser) 405 { 406 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d\n pvUser=%#p", __FUNCTION__, 407 uOffset, paSeg, cSeg, cbRead, pvUser)); 408 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface); 409 PDRVDISKAIOREQ pIoReq = drvdiskintIoReqAlloc(true, uOffset, paSeg, cSeg, cbRead, pvUser); 410 AssertPtr(pIoReq); 411 412 int rc = pThis->pDrvMediaAsync->pfnStartRead(pThis->pDrvMediaAsync, uOffset, paSeg, cSeg, 413 cbRead, pIoReq); 414 if (rc == VINF_VD_ASYNC_IO_FINISHED) 415 { 416 /* Verify the read now. */ 417 int rc2 = drvdiskintReadVerify(pThis, paSeg, cSeg, uOffset, cbRead); 418 AssertRC(rc2); 419 RTMemFree(pIoReq); 420 } 421 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS) 422 RTMemFree(pIoReq); 423 424 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc)); 425 return rc; 426 } 427 428 static DECLCALLBACK(int) drvdiskintStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset, 429 PCRTSGSEG paSeg, unsigned cSeg, 430 size_t cbWrite, void *pvUser) 431 { 432 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d\n pvUser=%#p", __FUNCTION__, 433 uOffset, paSeg, cSeg, cbWrite, pvUser)); 434 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface); 435 PDRVDISKAIOREQ pIoReq = drvdiskintIoReqAlloc(false, uOffset, paSeg, cSeg, cbWrite, pvUser); 436 AssertPtr(pIoReq); 437 438 int rc = pThis->pDrvMediaAsync->pfnStartWrite(pThis->pDrvMediaAsync, uOffset, paSeg, cSeg, 439 cbWrite, pIoReq); 440 if (rc == VINF_VD_ASYNC_IO_FINISHED) 441 { 442 /* Verify the read now. */ 443 int rc2 = drvdiskintWriteRecord(pThis, paSeg, cSeg, uOffset, cbWrite); 444 AssertRC(rc2); 445 RTMemFree(pIoReq); 446 } 447 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS) 448 RTMemFree(pIoReq); 449 450 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc)); 277 451 return rc; 278 452 } … … 338 512 } 339 513 514 /* -=-=-=-=- IMediaAsyncPort -=-=-=-=- */ 515 516 /** Makes a PDRVBLOCKASYNC out of a PPDMIMEDIAASYNCPORT. */ 517 #define PDMIMEDIAASYNCPORT_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMediaAsyncPort))) ) 518 519 static DECLCALLBACK(int) drvdiskintAsyncTransferCompleteNotify(PPDMIMEDIAASYNCPORT pInterface, void *pvUser) 520 { 521 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNCPORT_2_DRVDISKINTEGRITY(pInterface); 522 PDRVDISKAIOREQ pIoReq = (PDRVDISKAIOREQ)pvUser; 523 int rc = VINF_SUCCESS; 524 525 if (pIoReq->fRead) 526 rc = drvdiskintReadVerify(pThis, pIoReq->paSeg, pIoReq->cSeg, pIoReq->off, pIoReq->cbTransfer); 527 else 528 rc = drvdiskintWriteRecord(pThis, pIoReq->paSeg, pIoReq->cSeg, pIoReq->off, pIoReq->cbTransfer); 529 530 AssertRC(rc); 531 532 rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pIoReq->pvUser); 533 RTMemFree(pIoReq); 534 535 return rc; 536 } 537 340 538 /* -=-=-=-=- IBase -=-=-=-=- */ 341 539 … … 350 548 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase); 351 549 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia); 550 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNCPORT, pThis->pDrvMediaAsync ? &pThis->IMediaAsyncPort : NULL); 352 551 return NULL; 353 552 } … … 416 615 pThis->IMedia.pfnGetUuid = drvdiskintGetUuid; 417 616 617 /* IMediaAsync */ 618 pThis->IMediaAsync.pfnStartRead = drvdiskintStartRead; 619 pThis->IMediaAsync.pfnStartWrite = drvdiskintStartWrite; 620 621 /* IMediaAsyncPort. */ 622 pThis->IMediaAsyncPort.pfnTransferCompleteNotify = drvdiskintAsyncTransferCompleteNotify; 623 418 624 /* 419 625 * Try attach driver below and query it's media interface. … … 430 636 N_("No media or async media interface below")); 431 637 432 /** Create the AVL tree. */ 638 pThis->pDrvMediaAsync = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIAASYNC); 639 640 /* Try to attach async media port interface above.*/ 641 pThis->pDrvMediaAsyncPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAASYNCPORT); 642 643 /* Create the AVL tree. */ 433 644 pThis->pTreeSegments = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE)); 434 645 if (!pThis->pTreeSegments)
Note:
See TracChangeset
for help on using the changeset viewer.