VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp@ 29443

Last change on this file since 29443 was 29443, checked in by vboxsync, 15 years ago

DrvDiskIntegrity: Check for double completion of requests

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.2 KB
Line 
1/* $Id: DrvDiskIntegrity.cpp 29443 2010-05-13 10:10:40Z vboxsync $ */
2/** @file
3 * VBox storage devices: Disk integrity check.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_DISK_INTEGRITY
23#include <VBox/pdmdrv.h>
24#include <iprt/assert.h>
25#include <iprt/string.h>
26#include <iprt/uuid.h>
27#include <iprt/avl.h>
28#include <iprt/mem.h>
29#include <iprt/message.h>
30#include <iprt/sg.h>
31#include <iprt/time.h>
32#include <iprt/semaphore.h>
33#include <iprt/asm.h>
34
35#include "Builtins.h"
36
37
38/*******************************************************************************
39* Structures and Typedefs *
40*******************************************************************************/
41
42/**
43 * Transfer direction.
44 */
45typedef enum DRVDISKAIOTXDIR
46{
47 /** Read */
48 DRVDISKAIOTXDIR_READ = 0,
49 /** Write */
50 DRVDISKAIOTXDIR_WRITE,
51 /** Flush */
52 DRVDISKAIOTXDIR_FLUSH
53} DRVDISKAIOTXDIR;
54
55/**
56 * async I/O request.
57 */
58typedef struct DRVDISKAIOREQ
59{
60 /** Transfer direction. */
61 DRVDISKAIOTXDIR enmTxDir;
62 /** Start offset. */
63 uint64_t off;
64 /** Transfer size. */
65 size_t cbTransfer;
66 /** Segment array. */
67 PCRTSGSEG paSeg;
68 /** Number of array entries. */
69 unsigned cSeg;
70 /** User argument */
71 void *pvUser;
72 /** Slot in the array. */
73 unsigned iSlot;
74 /** Start timestamp */
75 uint64_t tsStart;
76 /** Completion timestamp. */
77 uint64_t tsComplete;
78} DRVDISKAIOREQ, *PDRVDISKAIOREQ;
79
80/**
81 * I/O log entry.
82 */
83typedef struct IOLOGENT
84{
85 /** Start offset */
86 uint64_t off;
87 /** Write size */
88 size_t cbWrite;
89 /** Number of references to this entry. */
90 unsigned cRefs;
91} IOLOGENT, *PIOLOGENT;
92
93/**
94 * Disk segment.
95 */
96typedef struct DRVDISKSEGMENT
97{
98 /** AVL core. */
99 AVLRFOFFNODECORE Core;
100 /** Size of the segment */
101 size_t cbSeg;
102 /** Data for this segment */
103 uint8_t *pbSeg;
104 /** Numbner of entries in the I/O array. */
105 unsigned cIoLogEntries;
106 /** Array of I/O log references. */
107 PIOLOGENT apIoLog[1];
108} DRVDISKSEGMENT, *PDRVDISKSEGMENT;
109
110/**
111 * Active requests list entry.
112 */
113typedef struct DRVDISKAIOREQACTIVE
114{
115 /** Pointer to the request. */
116 volatile PDRVDISKAIOREQ pIoReq;
117 /** Start timestamp. */
118 uint64_t tsStart;
119} DRVDISKAIOREQACTIVE, *PDRVDISKAIOREQACTIVE;
120
121/**
122 * Disk integrity driver instance data.
123 *
124 * @implements PDMIMEDIA
125 */
126typedef struct DRVDISKINTEGRITY
127{
128 /** Pointer driver instance. */
129 PPDMDRVINS pDrvIns;
130 /** Pointer to the media driver below us.
131 * This is NULL if the media is not mounted. */
132 PPDMIMEDIA pDrvMedia;
133 /** Our media interface */
134 PDMIMEDIA IMedia;
135
136 /** Pointer to the media async driver below us.
137 * This is NULL if the media is not mounted. */
138 PPDMIMEDIAASYNC pDrvMediaAsync;
139 /** Our media async interface */
140 PDMIMEDIAASYNC IMediaAsync;
141
142 /** The async media port interface above. */
143 PPDMIMEDIAASYNCPORT pDrvMediaAsyncPort;
144 /** Our media async port interface */
145 PDMIMEDIAASYNCPORT IMediaAsyncPort;
146
147 /** Flag whether consistency checks are enabled. */
148 bool fCheckConsistency;
149 /** AVL tree containing the disk blocks to check. */
150 PAVLRFOFFTREE pTreeSegments;
151
152 /** Flag whether async request tracing is enabled. */
153 bool fTraceRequests;
154 /** Interval the thread should check for expired requests (milliseconds). */
155 uint32_t uCheckIntervalMs;
156 /** Expire timeout for a request (milliseconds). */
157 uint32_t uExpireIntervalMs;
158 /** Thread which checks for lost requests. */
159 RTTHREAD hThread;
160 /** Event semaphore */
161 RTSEMEVENT SemEvent;
162 /** Flag whether the thread should run. */
163 bool fRunning;
164 /** Array containing active requests. */
165 DRVDISKAIOREQACTIVE apReqActive[128];
166 /** Next free slot in the array */
167 volatile unsigned iNextFreeSlot;
168
169 /** Flag whether we check for requests completing twice. */
170 bool fCheckDoubleCompletion;
171 /** Number of requests we go back. */
172 unsigned cEntries;
173 /** Array of completed but still observed requests. */
174 PDRVDISKAIOREQ *papIoReq;
175 /** Current entry in the array. */
176 unsigned iEntry;
177} DRVDISKINTEGRITY, *PDRVDISKINTEGRITY;
178
179
180/**
181 * Allocate a new I/O request.
182 *
183 * @returns New I/O request.
184 * @param enmTxDir Transfer direction.
185 * @param off Start offset.
186 * @param paSeg Segment array.
187 * @param cSeg Number of segments.
188 * @param cbTransfer Number of bytes to transfer.
189 * @param pvUser User argument.
190 */
191static PDRVDISKAIOREQ drvdiskintIoReqAlloc(DRVDISKAIOTXDIR enmTxDir, uint64_t off, PCRTSGSEG paSeg,
192 unsigned cSeg, size_t cbTransfer, void *pvUser)
193{
194 PDRVDISKAIOREQ pIoReq = (PDRVDISKAIOREQ)RTMemAlloc(sizeof(DRVDISKAIOREQ));
195
196 if (RT_LIKELY(pIoReq))
197 {
198 pIoReq->enmTxDir = enmTxDir;
199 pIoReq->off = off;
200 pIoReq->cbTransfer = cbTransfer;
201 pIoReq->paSeg = paSeg;
202 pIoReq->cSeg = cSeg;
203 pIoReq->pvUser = pvUser;
204 pIoReq->iSlot = 0;
205 pIoReq->tsStart = RTTimeSystemMilliTS();
206 pIoReq->tsComplete = 0;
207 }
208
209 return pIoReq;
210}
211
212/**
213 * Free a async I/O request.
214 *
215 * @returns nothing.
216 * @param pThis Disk driver.
217 * @param pIoReq The I/O request to free.
218 */
219static void drvdiskintIoReqFree(PDRVDISKINTEGRITY pThis, PDRVDISKAIOREQ pIoReq)
220{
221 if (pThis->fCheckDoubleCompletion)
222 {
223 /* Search if the I/O request completed already. */
224 for (unsigned i = 0; i < pThis->cEntries; i++)
225 {
226 if (RT_UNLIKELY(pThis->papIoReq[i] == pIoReq))
227 {
228 RTMsgError("Request %#p completed already!\n", pIoReq);
229 RTMsgError("Start timestamp %llu Completion timestamp %llu (completed after %llu ms)\n",
230 pIoReq->tsStart, pIoReq->tsComplete, pIoReq->tsComplete - pIoReq->tsStart);
231 RTAssertDebugBreak();
232 }
233 }
234
235 pIoReq->tsComplete = RTTimeSystemMilliTS();
236 Assert(!pThis->papIoReq[pThis->iEntry]);
237 pThis->papIoReq[pThis->iEntry] = pIoReq;
238
239 pThis->iEntry = (pThis->iEntry+1) % pThis->cEntries;
240 if (pThis->papIoReq[pThis->iEntry])
241 {
242 RTMemFree(pThis->papIoReq[pThis->iEntry]);
243 pThis->papIoReq[pThis->iEntry] = NULL;
244 }
245 }
246 else
247 RTMemFree(pIoReq);
248}
249
250/**
251 * Record a successful write to the virtual disk.
252 *
253 * @returns VBox status code.
254 * @param pThis Disk integrity driver instance data.
255 * @param paSeg Segment array of the write to record.
256 * @param cSeg Number of segments.
257 * @param off Start offset.
258 * @param cbWrite Number of bytes to record.
259 */
260static int drvdiskintWriteRecord(PDRVDISKINTEGRITY pThis, PCRTSGSEG paSeg, unsigned cSeg,
261 uint64_t off, size_t cbWrite)
262{
263 int rc = VINF_SUCCESS;
264
265 LogFlowFunc(("pThis=%#p paSeg=%#p cSeg=%u off=%llx cbWrite=%u\n",
266 pThis, paSeg, cSeg, off, cbWrite));
267
268 /* Update the segments */
269 size_t cbLeft = cbWrite;
270 RTFOFF offCurr = (RTFOFF)off;
271 RTSGBUF SgBuf;
272 PIOLOGENT pIoLogEnt = (PIOLOGENT)RTMemAllocZ(sizeof(IOLOGENT));
273 if (!pIoLogEnt)
274 return VERR_NO_MEMORY;
275
276 pIoLogEnt->off = off;
277 pIoLogEnt->cbWrite = cbWrite;
278 pIoLogEnt->cRefs = 0;
279
280 RTSgBufInit(&SgBuf, paSeg, cSeg);
281
282 while (cbLeft)
283 {
284 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr);
285 size_t cbRange = 0;
286 bool fSet = false;
287 unsigned offSeg = 0;
288
289 if (!pSeg)
290 {
291 /* Get next segment */
292 pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true);
293 if ( !pSeg
294 || offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key)
295 cbRange = cbLeft;
296 else
297 cbRange = pSeg->Core.Key - offCurr;
298
299 Assert(cbRange % 512 == 0);
300
301 /* Create new segment */
302 pSeg = (PDRVDISKSEGMENT)RTMemAllocZ(RT_OFFSETOF(DRVDISKSEGMENT, apIoLog[cbRange / 512]));
303 if (pSeg)
304 {
305 pSeg->Core.Key = offCurr;
306 pSeg->Core.KeyLast = offCurr + (RTFOFF)cbRange - 1;
307 pSeg->cbSeg = cbRange;
308 pSeg->pbSeg = (uint8_t *)RTMemAllocZ(cbRange);
309 pSeg->cIoLogEntries = cbRange / 512;
310 if (!pSeg->pbSeg)
311 RTMemFree(pSeg);
312 else
313 {
314 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
315 AssertMsg(fInserted, ("Bug!\n"));
316 fSet = true;
317 }
318 }
319 }
320 else
321 {
322 fSet = true;
323 offSeg = offCurr - pSeg->Core.Key;
324 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
325 }
326
327 if (fSet)
328 {
329 AssertPtr(pSeg);
330 size_t cbCopied = RTSgBufCopyToBuf(&SgBuf, pSeg->pbSeg + offSeg, cbRange);
331 Assert(cbCopied == cbRange);
332
333 /* Update the I/O log pointers */
334 Assert(offSeg % 512 == 0);
335 Assert(cbRange % 512 == 0);
336 while (offSeg < cbRange)
337 {
338 uint32_t uSector = offSeg / 512;
339 PIOLOGENT pIoLogOld = NULL;
340
341 AssertMsg(uSector < pSeg->cIoLogEntries, ("Internal bug!\n"));
342
343 pIoLogOld = pSeg->apIoLog[uSector];
344 if (pIoLogOld)
345 {
346 pIoLogOld->cRefs--;
347 if (!pIoLogOld->cRefs)
348 RTMemFree(pIoLogOld);
349 }
350
351 pSeg->apIoLog[uSector] = pIoLogEnt;
352 pIoLogEnt->cRefs++;
353
354 offSeg += 512;
355 }
356 }
357 else
358 RTSgBufAdvance(&SgBuf, cbRange);
359
360 offCurr += cbRange;
361 cbLeft -= cbRange;
362 }
363
364 return rc;
365}
366
367/**
368 * Verifies a read request.
369 *
370 * @returns VBox status code.
371 * @param pThis Disk integrity driver instance data.
372 * @param paSeg Segment array of the containing the data buffers to verify.
373 * @param cSeg Number of segments.
374 * @param off Start offset.
375 * @param cbWrite Number of bytes to verify.
376 */
377static int drvdiskintReadVerify(PDRVDISKINTEGRITY pThis, PCRTSGSEG paSeg, unsigned cSeg,
378 uint64_t off, size_t cbRead)
379{
380 int rc = VINF_SUCCESS;
381
382 LogFlowFunc(("pThis=%#p paSeg=%#p cSeg=%u off=%llx cbRead=%u\n",
383 pThis, paSeg, cSeg, off, cbRead));
384
385 Assert(off % 512 == 0);
386 Assert(cbRead % 512 == 0);
387
388 /* Compare read data */
389 size_t cbLeft = cbRead;
390 RTFOFF offCurr = (RTFOFF)off;
391 RTSGBUF SgBuf;
392
393 RTSgBufInit(&SgBuf, paSeg, cSeg);
394
395 while (cbLeft)
396 {
397 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr);
398 size_t cbRange = 0;
399 bool fCmp = false;
400 unsigned offSeg = 0;
401
402 if (!pSeg)
403 {
404 /* Get next segment */
405 pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true);
406 if (!pSeg)
407 {
408 /* No data in the tree for this read. Assume everything is ok. */
409 cbRange = cbLeft;
410 }
411 else if (offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key)
412 cbRange = cbLeft;
413 else
414 cbRange = pSeg->Core.Key - offCurr;
415 }
416 else
417 {
418 fCmp = true;
419 offSeg = offCurr - pSeg->Core.Key;
420 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
421 }
422
423 if (fCmp)
424 {
425 RTSGSEG Seg;
426 RTSGBUF SgBufCmp;
427 size_t cbOff = 0;
428
429 Seg.cbSeg = cbRange;
430 Seg.pvSeg = pSeg->pbSeg + offSeg;
431
432 RTSgBufInit(&SgBufCmp, &Seg, 1);
433 if (RTSgBufCmpEx(&SgBuf, &SgBufCmp, cbRange, &cbOff, true))
434 {
435 /* Corrupted disk, print I/O log entry of the last write which accessed this range. */
436 uint32_t cSector = (offSeg + cbOff) / 512;
437 AssertMsg(cSector < pSeg->cIoLogEntries, ("Internal bug!\n"));
438
439 RTMsgError("Corrupted disk at offset %llu (%u bytes in the current read buffer)!\n",
440 offCurr + cbOff, cbOff);
441 RTMsgError("Last write to this sector started at offset %llu with %u bytes (%u references to this log entry)\n",
442 pSeg->apIoLog[cSector]->off,
443 pSeg->apIoLog[cSector]->cbWrite,
444 pSeg->apIoLog[cSector]->cRefs);
445 RTAssertDebugBreak();
446 }
447 }
448 else
449 RTSgBufAdvance(&SgBuf, cbRange);
450
451 offCurr += cbRange;
452 cbLeft -= cbRange;
453 }
454
455 return rc;
456}
457
458/**
459 * Adds a request to the active list.
460 *
461 * @returns nothing.
462 * @param pThis The driver instance data.
463 * @param pIoReq The request to add.
464 */
465static void drvdiskintIoReqAdd(PDRVDISKINTEGRITY pThis, PDRVDISKAIOREQ pIoReq)
466{
467 PDRVDISKAIOREQACTIVE pReqActive = &pThis->apReqActive[pThis->iNextFreeSlot];
468
469 Assert(!pReqActive->pIoReq);
470 pReqActive->tsStart = pIoReq->tsStart;
471 pReqActive->pIoReq = pIoReq;
472 pIoReq->iSlot = pThis->iNextFreeSlot;
473
474 /* Search for the next one. */
475 while (pThis->apReqActive[pThis->iNextFreeSlot].pIoReq)
476 pThis->iNextFreeSlot = (pThis->iNextFreeSlot+1) % RT_ELEMENTS(pThis->apReqActive);
477}
478
479/**
480 * Removes a request from the active list.
481 *
482 * @returns nothing.
483 * @param pThis The driver instance data.
484 * @param pIoReq The request to remove.
485 */
486static void drvdiskintIoReqRemove(PDRVDISKINTEGRITY pThis, PDRVDISKAIOREQ pIoReq)
487{
488 PDRVDISKAIOREQACTIVE pReqActive = &pThis->apReqActive[pIoReq->iSlot];
489
490 Assert(pReqActive->pIoReq == pIoReq);
491
492 ASMAtomicXchgPtr((void * volatile *)&pReqActive->pIoReq, NULL);
493}
494
495/**
496 * Thread checking for expired requests.
497 *
498 * @returns IPRT status code.
499 * @param pThread Thread handle.
500 * @param pvUser Opaque user data.
501 */
502static int drvdiskIntIoReqExpiredCheck(RTTHREAD pThread, void *pvUser)
503{
504 PDRVDISKINTEGRITY pThis = (PDRVDISKINTEGRITY)pvUser;
505
506 while (pThis->fRunning)
507 {
508 int rc = RTSemEventWait(pThis->SemEvent, pThis->uCheckIntervalMs);
509
510 if (!pThis->fRunning)
511 break;
512
513 Assert(rc == VERR_TIMEOUT);
514
515 /* Get current timestamp for comparison. */
516 uint64_t tsCurr = RTTimeSystemMilliTS();
517
518 /* Go through the array and check for expired requests. */
519 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apReqActive); i++)
520 {
521 PDRVDISKAIOREQACTIVE pReqActive = &pThis->apReqActive[i];
522 PDRVDISKAIOREQ pIoReq = (PDRVDISKAIOREQ)ASMAtomicReadPtr((void * volatile *)&pReqActive->pIoReq);
523
524 if ( pIoReq
525 && (tsCurr - pReqActive->tsStart) >= pThis->uExpireIntervalMs)
526 {
527 RTMsgError("Request %#p expired (active for %llu ms already)\n",
528 pIoReq, tsCurr - pReqActive->tsStart);
529 RTAssertDebugBreak();
530 }
531 }
532 }
533
534 return VINF_SUCCESS;
535}
536
537/* -=-=-=-=- IMedia -=-=-=-=- */
538
539/** Makes a PDRVDISKINTEGRITY out of a PPDMIMEDIA. */
540#define PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY)((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMedia)) )
541/** Makes a PDRVDISKINTEGRITY out of a PPDMIMEDIAASYNC. */
542#define PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY)((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMediaAsync)) )
543
544/*******************************************************************************
545* Media interface methods *
546*******************************************************************************/
547
548/** @copydoc PDMIMEDIA::pfnRead */
549static DECLCALLBACK(int) drvdiskintRead(PPDMIMEDIA pInterface,
550 uint64_t off, void *pvBuf, size_t cbRead)
551{
552 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
553 int rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, off, pvBuf, cbRead);
554 if (RT_FAILURE(rc))
555 return rc;
556
557 if (pThis->fCheckConsistency)
558 {
559 /* Verify the read. */
560 RTSGSEG Seg;
561 Seg.cbSeg = cbRead;
562 Seg.pvSeg = pvBuf;
563 rc = drvdiskintReadVerify(pThis, &Seg, 1, off, cbRead);
564 }
565
566 return rc;
567}
568
569/** @copydoc PDMIMEDIA::pfnWrite */
570static DECLCALLBACK(int) drvdiskintWrite(PPDMIMEDIA pInterface,
571 uint64_t off, const void *pvBuf,
572 size_t cbWrite)
573{
574 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
575 int rc = pThis->pDrvMedia->pfnWrite(pThis->pDrvMedia, off, pvBuf, cbWrite);
576 if (RT_FAILURE(rc))
577 return rc;
578
579 if (pThis->fCheckConsistency)
580 {
581 /* Record the write. */
582 RTSGSEG Seg;
583 Seg.cbSeg = cbWrite;
584 Seg.pvSeg = (void *)pvBuf;
585 rc = drvdiskintWriteRecord(pThis, &Seg, 1, off, cbWrite);
586 }
587
588 return rc;
589}
590
591static DECLCALLBACK(int) drvdiskintStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
592 PCRTSGSEG paSeg, unsigned cSeg,
593 size_t cbRead, void *pvUser)
594{
595 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d pvUser=%#p\n", __FUNCTION__,
596 uOffset, paSeg, cSeg, cbRead, pvUser));
597 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface);
598 PDRVDISKAIOREQ pIoReq = drvdiskintIoReqAlloc(DRVDISKAIOTXDIR_READ, uOffset, paSeg, cSeg, cbRead, pvUser);
599 AssertPtr(pIoReq);
600
601 if (pThis->fTraceRequests)
602 drvdiskintIoReqAdd(pThis, pIoReq);
603
604 int rc = pThis->pDrvMediaAsync->pfnStartRead(pThis->pDrvMediaAsync, uOffset, paSeg, cSeg,
605 cbRead, pIoReq);
606 if (rc == VINF_VD_ASYNC_IO_FINISHED)
607 {
608 /* Verify the read now. */
609 if (pThis->fCheckConsistency)
610 {
611 int rc2 = drvdiskintReadVerify(pThis, paSeg, cSeg, uOffset, cbRead);
612 AssertRC(rc2);
613 }
614
615 if (pThis->fTraceRequests)
616 drvdiskintIoReqRemove(pThis, pIoReq);
617 RTMemFree(pIoReq);
618 }
619 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
620 RTMemFree(pIoReq);
621
622 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
623 return rc;
624}
625
626static DECLCALLBACK(int) drvdiskintStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
627 PCRTSGSEG paSeg, unsigned cSeg,
628 size_t cbWrite, void *pvUser)
629{
630 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d pvUser=%#p\n", __FUNCTION__,
631 uOffset, paSeg, cSeg, cbWrite, pvUser));
632 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface);
633 PDRVDISKAIOREQ pIoReq = drvdiskintIoReqAlloc(DRVDISKAIOTXDIR_WRITE, uOffset, paSeg, cSeg, cbWrite, pvUser);
634 AssertPtr(pIoReq);
635
636 if (pThis->fTraceRequests)
637 drvdiskintIoReqAdd(pThis, pIoReq);
638
639 int rc = pThis->pDrvMediaAsync->pfnStartWrite(pThis->pDrvMediaAsync, uOffset, paSeg, cSeg,
640 cbWrite, pIoReq);
641 if (rc == VINF_VD_ASYNC_IO_FINISHED)
642 {
643 /* Verify the read now. */
644 if (pThis->fCheckConsistency)
645 {
646 int rc2 = drvdiskintWriteRecord(pThis, paSeg, cSeg, uOffset, cbWrite);
647 AssertRC(rc2);
648 }
649
650 if (pThis->fTraceRequests)
651 drvdiskintIoReqRemove(pThis, pIoReq);
652
653 RTMemFree(pIoReq);
654 }
655 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
656 RTMemFree(pIoReq);
657
658 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
659 return rc;
660}
661
662/** @copydoc PDMIMEDIAASYNC::pfnStartFlush */
663static DECLCALLBACK(int) drvdiskintStartFlush(PPDMIMEDIAASYNC pInterface, void *pvUser)
664{
665 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface);
666 PDRVDISKAIOREQ pIoReq = drvdiskintIoReqAlloc(DRVDISKAIOTXDIR_FLUSH, 0, NULL, 0, 0, pvUser);
667 AssertPtr(pIoReq);
668
669 return pThis->pDrvMediaAsync->pfnStartFlush(pThis->pDrvMediaAsync, pIoReq);
670}
671
672/** @copydoc PDMIMEDIA::pfnFlush */
673static DECLCALLBACK(int) drvdiskintFlush(PPDMIMEDIA pInterface)
674{
675 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
676 return pThis->pDrvMedia->pfnFlush(pThis->pDrvMedia);
677}
678
679/** @copydoc PDMIMEDIA::pfnGetSize */
680static DECLCALLBACK(uint64_t) drvdiskintGetSize(PPDMIMEDIA pInterface)
681{
682 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
683 return pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
684}
685
686/** @copydoc PDMIMEDIA::pfnIsReadOnly */
687static DECLCALLBACK(bool) drvdiskintIsReadOnly(PPDMIMEDIA pInterface)
688{
689 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
690 return pThis->pDrvMedia->pfnIsReadOnly(pThis->pDrvMedia);
691}
692
693/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
694static DECLCALLBACK(int) drvdiskintBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
695 PPDMMEDIAGEOMETRY pPCHSGeometry)
696{
697 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
698 return pThis->pDrvMedia->pfnBiosGetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
699}
700
701/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
702static DECLCALLBACK(int) drvdiskintBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
703 PCPDMMEDIAGEOMETRY pPCHSGeometry)
704{
705 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
706 return pThis->pDrvMedia->pfnBiosSetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
707}
708
709/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
710static DECLCALLBACK(int) drvdiskintBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
711 PPDMMEDIAGEOMETRY pLCHSGeometry)
712{
713 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
714 return pThis->pDrvMedia->pfnBiosGetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
715}
716
717/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
718static DECLCALLBACK(int) drvdiskintBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
719 PCPDMMEDIAGEOMETRY pLCHSGeometry)
720{
721 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
722 return pThis->pDrvMedia->pfnBiosSetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
723}
724
725/** @copydoc PDMIMEDIA::pfnGetUuid */
726static DECLCALLBACK(int) drvdiskintGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
727{
728 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
729 return pThis->pDrvMedia->pfnGetUuid(pThis->pDrvMedia, pUuid);
730}
731
732/* -=-=-=-=- IMediaAsyncPort -=-=-=-=- */
733
734/** Makes a PDRVBLOCKASYNC out of a PPDMIMEDIAASYNCPORT. */
735#define PDMIMEDIAASYNCPORT_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMediaAsyncPort))) )
736
737static DECLCALLBACK(int) drvdiskintAsyncTransferCompleteNotify(PPDMIMEDIAASYNCPORT pInterface, void *pvUser, int rcReq)
738{
739 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNCPORT_2_DRVDISKINTEGRITY(pInterface);
740 PDRVDISKAIOREQ pIoReq = (PDRVDISKAIOREQ)pvUser;
741 int rc = VINF_SUCCESS;
742
743 LogFlowFunc(("pIoReq=%#p\n", pIoReq));
744
745 /* Remove from the active list. */
746 if (pThis->fTraceRequests)
747 drvdiskintIoReqRemove(pThis, pIoReq);
748
749 if (RT_SUCCESS(rcReq) && pThis->fCheckConsistency)
750 {
751 if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_READ)
752 rc = drvdiskintReadVerify(pThis, pIoReq->paSeg, pIoReq->cSeg, pIoReq->off, pIoReq->cbTransfer);
753 else if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_WRITE)
754 rc = drvdiskintWriteRecord(pThis, pIoReq->paSeg, pIoReq->cSeg, pIoReq->off, pIoReq->cbTransfer);
755 else
756 AssertMsg(pIoReq->enmTxDir == DRVDISKAIOTXDIR_FLUSH, ("Huh?\n"));
757
758 AssertRC(rc);
759 }
760
761 void *pvUserComplete = pIoReq->pvUser;
762
763 drvdiskintIoReqFree(pThis, pIoReq);
764
765 rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pvUserComplete, rcReq);
766
767 return rc;
768}
769
770/* -=-=-=-=- IBase -=-=-=-=- */
771
772/**
773 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
774 */
775static DECLCALLBACK(void *) drvdiskintQueryInterface(PPDMIBASE pInterface, const char *pszIID)
776{
777 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
778 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
779
780 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
781 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
782 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNC, pThis->pDrvMediaAsync ? &pThis->IMediaAsync : NULL);
783 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNCPORT, &pThis->IMediaAsyncPort);
784 return NULL;
785}
786
787
788/* -=-=-=-=- driver interface -=-=-=-=- */
789
790static int drvdiskintTreeDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
791{
792 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)pNode;
793
794 RTMemFree(pSeg->pbSeg);
795 RTMemFree(pSeg);
796 return VINF_SUCCESS;
797}
798
799/**
800 * @copydoc FNPDMDRVDESTRUCT
801 */
802static DECLCALLBACK(void) drvdiskintDestruct(PPDMDRVINS pDrvIns)
803{
804 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
805
806 if (pThis->pTreeSegments)
807 {
808 RTAvlrFileOffsetDestroy(pThis->pTreeSegments, drvdiskintTreeDestroy, NULL);
809 RTMemFree(pThis->pTreeSegments);
810 }
811
812 if (pThis->fTraceRequests)
813 {
814 pThis->fRunning = false;
815 RTSemEventSignal(pThis->SemEvent);
816 RTSemEventDestroy(pThis->SemEvent);
817 }
818
819 if (pThis->fCheckDoubleCompletion)
820 {
821 /* Free all requests */
822 while (pThis->papIoReq[pThis->iEntry])
823 {
824 RTMemFree(pThis->papIoReq[pThis->iEntry]);
825 pThis->papIoReq[pThis->iEntry] = NULL;
826 pThis->iEntry = (pThis->iEntry+1) % pThis->cEntries;
827 }
828 }
829}
830
831/**
832 * Construct a disk integrity driver instance.
833 *
834 * @copydoc FNPDMDRVCONSTRUCT
835 */
836static DECLCALLBACK(int) drvdiskintConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
837{
838 int rc = VINF_SUCCESS;
839 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
840 LogFlow(("drvdiskintConstruct: iInstance=%d\n", pDrvIns->iInstance));
841 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
842
843 /*
844 * Validate configuration.
845 */
846 if (!CFGMR3AreValuesValid(pCfg, "CheckConsistency\0"
847 "TraceRequests\0"
848 "CheckIntervalMs\0"
849 "ExpireIntervalMs\0"
850 "CheckDoubleCompletions\0"
851 "HistorySize\0"))
852 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
853
854 rc = CFGMR3QueryBoolDef(pCfg, "CheckConsistency", &pThis->fCheckConsistency, false);
855 AssertRC(rc);
856 rc = CFGMR3QueryBoolDef(pCfg, "TraceRequests", &pThis->fTraceRequests, false);
857 AssertRC(rc);
858 rc = CFGMR3QueryU32Def(pCfg, "CheckIntervalMs", &pThis->uCheckIntervalMs, 5000); /* 5 seconds */
859 AssertRC(rc);
860 rc = CFGMR3QueryU32Def(pCfg, "ExpireIntervalMs", &pThis->uExpireIntervalMs, 20000); /* 20 seconds */
861 AssertRC(rc);
862 rc = CFGMR3QueryBoolDef(pCfg, "CheckDoubleCompletions", &pThis->fCheckDoubleCompletion, false);
863 AssertRC(rc);
864 rc = CFGMR3QueryU32Def(pCfg, "HistorySize", &pThis->cEntries, 512);
865 AssertRC(rc);
866
867 /*
868 * Initialize most of the data members.
869 */
870 pThis->pDrvIns = pDrvIns;
871
872 /* IBase. */
873 pDrvIns->IBase.pfnQueryInterface = drvdiskintQueryInterface;
874
875 /* IMedia */
876 pThis->IMedia.pfnRead = drvdiskintRead;
877 pThis->IMedia.pfnWrite = drvdiskintWrite;
878 pThis->IMedia.pfnFlush = drvdiskintFlush;
879 pThis->IMedia.pfnGetSize = drvdiskintGetSize;
880 pThis->IMedia.pfnIsReadOnly = drvdiskintIsReadOnly;
881 pThis->IMedia.pfnBiosGetPCHSGeometry = drvdiskintBiosGetPCHSGeometry;
882 pThis->IMedia.pfnBiosSetPCHSGeometry = drvdiskintBiosSetPCHSGeometry;
883 pThis->IMedia.pfnBiosGetLCHSGeometry = drvdiskintBiosGetLCHSGeometry;
884 pThis->IMedia.pfnBiosSetLCHSGeometry = drvdiskintBiosSetLCHSGeometry;
885 pThis->IMedia.pfnGetUuid = drvdiskintGetUuid;
886
887 /* IMediaAsync */
888 pThis->IMediaAsync.pfnStartRead = drvdiskintStartRead;
889 pThis->IMediaAsync.pfnStartWrite = drvdiskintStartWrite;
890 pThis->IMediaAsync.pfnStartFlush = drvdiskintStartFlush;
891
892 /* IMediaAsyncPort. */
893 pThis->IMediaAsyncPort.pfnTransferCompleteNotify = drvdiskintAsyncTransferCompleteNotify;
894
895 /*
896 * Try attach driver below and query it's media interface.
897 */
898 PPDMIBASE pBase;
899 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
900 if (RT_FAILURE(rc))
901 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
902 N_("Failed to attach driver below us! %Rrc"), rc);
903
904 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
905 if (!pThis->pDrvMedia)
906 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
907 N_("No media or async media interface below"));
908
909 pThis->pDrvMediaAsync = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIAASYNC);
910
911 /* Try to attach async media port interface above.*/
912 pThis->pDrvMediaAsyncPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAASYNCPORT);
913
914 if (pThis->fCheckConsistency)
915 {
916 /* Create the AVL tree. */
917 pThis->pTreeSegments = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
918 if (!pThis->pTreeSegments)
919 rc = VERR_NO_MEMORY;
920 }
921
922 if (pThis->fTraceRequests)
923 {
924 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apReqActive); i++)
925 {
926 pThis->apReqActive[i].pIoReq = NULL;
927 pThis->apReqActive[i].tsStart = 0;
928 }
929
930 pThis->iNextFreeSlot = 0;
931
932 /* Init event semaphore. */
933 rc = RTSemEventCreate(&pThis->SemEvent);
934 AssertRC(rc);
935 pThis->fRunning = true;
936 rc = RTThreadCreate(&pThis->hThread, drvdiskIntIoReqExpiredCheck, pThis,
937 0, RTTHREADTYPE_INFREQUENT_POLLER, 0, "DiskIntegrity");
938 AssertRC(rc);
939 }
940
941 if (pThis->fCheckDoubleCompletion)
942 {
943 pThis->iEntry = 0;
944 pThis->papIoReq = (PDRVDISKAIOREQ *)RTMemAllocZ(pThis->cEntries * sizeof(PDRVDISKAIOREQ));
945 AssertPtr(pThis->papIoReq);
946 }
947
948 return rc;
949}
950
951
952/**
953 * Block driver registration record.
954 */
955const PDMDRVREG g_DrvDiskIntegrity =
956{
957 /* u32Version */
958 PDM_DRVREG_VERSION,
959 /* szName */
960 "DiskIntegrity",
961 /* szRCMod */
962 "",
963 /* szR0Mod */
964 "",
965 /* pszDescription */
966 "Disk integrity driver.",
967 /* fFlags */
968 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
969 /* fClass. */
970 PDM_DRVREG_CLASS_BLOCK,
971 /* cMaxInstances */
972 ~0,
973 /* cbInstance */
974 sizeof(DRVDISKINTEGRITY),
975 /* pfnConstruct */
976 drvdiskintConstruct,
977 /* pfnDestruct */
978 drvdiskintDestruct,
979 /* pfnRelocate */
980 NULL,
981 /* pfnIOCtl */
982 NULL,
983 /* pfnPowerOn */
984 NULL,
985 /* pfnReset */
986 NULL,
987 /* pfnSuspend */
988 NULL,
989 /* pfnResume */
990 NULL,
991 /* pfnAttach */
992 NULL,
993 /* pfnDetach */
994 NULL,
995 /* pfnPowerOff */
996 NULL,
997 /* pfnSoftReset */
998 NULL,
999 /* u32EndVersion */
1000 PDM_DRVREG_VERSION
1001};
1002
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette