VirtualBox

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

Last change on this file since 33524 was 30112, checked in by vboxsync, 14 years ago

iprt/asm.h,*: Added ASMAtomicWriteNullPtr and ASMAtomicUoWriteNullPtr to better deal with NULL being 0 in C++.

  • 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 30112 2010-06-09 12:31:50Z 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.virtualbox.org. 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 ASMAtomicWriteNullPtr(&pReqActive->pIoReq);
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 = ASMAtomicReadPtrT(&pReqActive->pIoReq, PDRVDISKAIOREQ);
523
524 if ( pIoReq
525 && (tsCurr > pReqActive->tsStart)
526 && (tsCurr - pReqActive->tsStart) >= pThis->uExpireIntervalMs)
527 {
528 RTMsgError("Request %#p expired (active for %llu ms already)\n",
529 pIoReq, tsCurr - pReqActive->tsStart);
530 RTAssertDebugBreak();
531 }
532 }
533 }
534
535 return VINF_SUCCESS;
536}
537
538/* -=-=-=-=- IMedia -=-=-=-=- */
539
540/** Makes a PDRVDISKINTEGRITY out of a PPDMIMEDIA. */
541#define PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY)((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMedia)) )
542/** Makes a PDRVDISKINTEGRITY out of a PPDMIMEDIAASYNC. */
543#define PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY)((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMediaAsync)) )
544
545/*******************************************************************************
546* Media interface methods *
547*******************************************************************************/
548
549/** @copydoc PDMIMEDIA::pfnRead */
550static DECLCALLBACK(int) drvdiskintRead(PPDMIMEDIA pInterface,
551 uint64_t off, void *pvBuf, size_t cbRead)
552{
553 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
554 int rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, off, pvBuf, cbRead);
555 if (RT_FAILURE(rc))
556 return rc;
557
558 if (pThis->fCheckConsistency)
559 {
560 /* Verify the read. */
561 RTSGSEG Seg;
562 Seg.cbSeg = cbRead;
563 Seg.pvSeg = pvBuf;
564 rc = drvdiskintReadVerify(pThis, &Seg, 1, off, cbRead);
565 }
566
567 return rc;
568}
569
570/** @copydoc PDMIMEDIA::pfnWrite */
571static DECLCALLBACK(int) drvdiskintWrite(PPDMIMEDIA pInterface,
572 uint64_t off, const void *pvBuf,
573 size_t cbWrite)
574{
575 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
576 int rc = pThis->pDrvMedia->pfnWrite(pThis->pDrvMedia, off, pvBuf, cbWrite);
577 if (RT_FAILURE(rc))
578 return rc;
579
580 if (pThis->fCheckConsistency)
581 {
582 /* Record the write. */
583 RTSGSEG Seg;
584 Seg.cbSeg = cbWrite;
585 Seg.pvSeg = (void *)pvBuf;
586 rc = drvdiskintWriteRecord(pThis, &Seg, 1, off, cbWrite);
587 }
588
589 return rc;
590}
591
592static DECLCALLBACK(int) drvdiskintStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
593 PCRTSGSEG paSeg, unsigned cSeg,
594 size_t cbRead, void *pvUser)
595{
596 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d pvUser=%#p\n", __FUNCTION__,
597 uOffset, paSeg, cSeg, cbRead, pvUser));
598 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface);
599 PDRVDISKAIOREQ pIoReq = drvdiskintIoReqAlloc(DRVDISKAIOTXDIR_READ, uOffset, paSeg, cSeg, cbRead, pvUser);
600 AssertPtr(pIoReq);
601
602 if (pThis->fTraceRequests)
603 drvdiskintIoReqAdd(pThis, pIoReq);
604
605 int rc = pThis->pDrvMediaAsync->pfnStartRead(pThis->pDrvMediaAsync, uOffset, paSeg, cSeg,
606 cbRead, pIoReq);
607 if (rc == VINF_VD_ASYNC_IO_FINISHED)
608 {
609 /* Verify the read now. */
610 if (pThis->fCheckConsistency)
611 {
612 int rc2 = drvdiskintReadVerify(pThis, paSeg, cSeg, uOffset, cbRead);
613 AssertRC(rc2);
614 }
615
616 if (pThis->fTraceRequests)
617 drvdiskintIoReqRemove(pThis, pIoReq);
618 RTMemFree(pIoReq);
619 }
620 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
621 RTMemFree(pIoReq);
622
623 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
624 return rc;
625}
626
627static DECLCALLBACK(int) drvdiskintStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
628 PCRTSGSEG paSeg, unsigned cSeg,
629 size_t cbWrite, void *pvUser)
630{
631 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d pvUser=%#p\n", __FUNCTION__,
632 uOffset, paSeg, cSeg, cbWrite, pvUser));
633 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface);
634 PDRVDISKAIOREQ pIoReq = drvdiskintIoReqAlloc(DRVDISKAIOTXDIR_WRITE, uOffset, paSeg, cSeg, cbWrite, pvUser);
635 AssertPtr(pIoReq);
636
637 if (pThis->fTraceRequests)
638 drvdiskintIoReqAdd(pThis, pIoReq);
639
640 int rc = pThis->pDrvMediaAsync->pfnStartWrite(pThis->pDrvMediaAsync, uOffset, paSeg, cSeg,
641 cbWrite, pIoReq);
642 if (rc == VINF_VD_ASYNC_IO_FINISHED)
643 {
644 /* Verify the read now. */
645 if (pThis->fCheckConsistency)
646 {
647 int rc2 = drvdiskintWriteRecord(pThis, paSeg, cSeg, uOffset, cbWrite);
648 AssertRC(rc2);
649 }
650
651 if (pThis->fTraceRequests)
652 drvdiskintIoReqRemove(pThis, pIoReq);
653
654 RTMemFree(pIoReq);
655 }
656 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
657 RTMemFree(pIoReq);
658
659 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
660 return rc;
661}
662
663/** @copydoc PDMIMEDIAASYNC::pfnStartFlush */
664static DECLCALLBACK(int) drvdiskintStartFlush(PPDMIMEDIAASYNC pInterface, void *pvUser)
665{
666 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface);
667 PDRVDISKAIOREQ pIoReq = drvdiskintIoReqAlloc(DRVDISKAIOTXDIR_FLUSH, 0, NULL, 0, 0, pvUser);
668 AssertPtr(pIoReq);
669
670 return pThis->pDrvMediaAsync->pfnStartFlush(pThis->pDrvMediaAsync, pIoReq);
671}
672
673/** @copydoc PDMIMEDIA::pfnFlush */
674static DECLCALLBACK(int) drvdiskintFlush(PPDMIMEDIA pInterface)
675{
676 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
677 return pThis->pDrvMedia->pfnFlush(pThis->pDrvMedia);
678}
679
680/** @copydoc PDMIMEDIA::pfnGetSize */
681static DECLCALLBACK(uint64_t) drvdiskintGetSize(PPDMIMEDIA pInterface)
682{
683 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
684 return pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
685}
686
687/** @copydoc PDMIMEDIA::pfnIsReadOnly */
688static DECLCALLBACK(bool) drvdiskintIsReadOnly(PPDMIMEDIA pInterface)
689{
690 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
691 return pThis->pDrvMedia->pfnIsReadOnly(pThis->pDrvMedia);
692}
693
694/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
695static DECLCALLBACK(int) drvdiskintBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
696 PPDMMEDIAGEOMETRY pPCHSGeometry)
697{
698 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
699 return pThis->pDrvMedia->pfnBiosGetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
700}
701
702/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
703static DECLCALLBACK(int) drvdiskintBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
704 PCPDMMEDIAGEOMETRY pPCHSGeometry)
705{
706 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
707 return pThis->pDrvMedia->pfnBiosSetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
708}
709
710/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
711static DECLCALLBACK(int) drvdiskintBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
712 PPDMMEDIAGEOMETRY pLCHSGeometry)
713{
714 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
715 return pThis->pDrvMedia->pfnBiosGetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
716}
717
718/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
719static DECLCALLBACK(int) drvdiskintBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
720 PCPDMMEDIAGEOMETRY pLCHSGeometry)
721{
722 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
723 return pThis->pDrvMedia->pfnBiosSetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
724}
725
726/** @copydoc PDMIMEDIA::pfnGetUuid */
727static DECLCALLBACK(int) drvdiskintGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
728{
729 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
730 return pThis->pDrvMedia->pfnGetUuid(pThis->pDrvMedia, pUuid);
731}
732
733/* -=-=-=-=- IMediaAsyncPort -=-=-=-=- */
734
735/** Makes a PDRVBLOCKASYNC out of a PPDMIMEDIAASYNCPORT. */
736#define PDMIMEDIAASYNCPORT_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMediaAsyncPort))) )
737
738static DECLCALLBACK(int) drvdiskintAsyncTransferCompleteNotify(PPDMIMEDIAASYNCPORT pInterface, void *pvUser, int rcReq)
739{
740 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNCPORT_2_DRVDISKINTEGRITY(pInterface);
741 PDRVDISKAIOREQ pIoReq = (PDRVDISKAIOREQ)pvUser;
742 int rc = VINF_SUCCESS;
743
744 LogFlowFunc(("pIoReq=%#p\n", pIoReq));
745
746 /* Remove from the active list. */
747 if (pThis->fTraceRequests)
748 drvdiskintIoReqRemove(pThis, pIoReq);
749
750 if (RT_SUCCESS(rcReq) && pThis->fCheckConsistency)
751 {
752 if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_READ)
753 rc = drvdiskintReadVerify(pThis, pIoReq->paSeg, pIoReq->cSeg, pIoReq->off, pIoReq->cbTransfer);
754 else if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_WRITE)
755 rc = drvdiskintWriteRecord(pThis, pIoReq->paSeg, pIoReq->cSeg, pIoReq->off, pIoReq->cbTransfer);
756 else
757 AssertMsg(pIoReq->enmTxDir == DRVDISKAIOTXDIR_FLUSH, ("Huh?\n"));
758
759 AssertRC(rc);
760 }
761
762 void *pvUserComplete = pIoReq->pvUser;
763
764 drvdiskintIoReqFree(pThis, pIoReq);
765
766 rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pvUserComplete, rcReq);
767
768 return rc;
769}
770
771/* -=-=-=-=- IBase -=-=-=-=- */
772
773/**
774 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
775 */
776static DECLCALLBACK(void *) drvdiskintQueryInterface(PPDMIBASE pInterface, const char *pszIID)
777{
778 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
779 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
780
781 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
782 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
783 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNC, pThis->pDrvMediaAsync ? &pThis->IMediaAsync : NULL);
784 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNCPORT, &pThis->IMediaAsyncPort);
785 return NULL;
786}
787
788
789/* -=-=-=-=- driver interface -=-=-=-=- */
790
791static int drvdiskintTreeDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
792{
793 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)pNode;
794
795 RTMemFree(pSeg->pbSeg);
796 RTMemFree(pSeg);
797 return VINF_SUCCESS;
798}
799
800/**
801 * @copydoc FNPDMDRVDESTRUCT
802 */
803static DECLCALLBACK(void) drvdiskintDestruct(PPDMDRVINS pDrvIns)
804{
805 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
806
807 if (pThis->pTreeSegments)
808 {
809 RTAvlrFileOffsetDestroy(pThis->pTreeSegments, drvdiskintTreeDestroy, NULL);
810 RTMemFree(pThis->pTreeSegments);
811 }
812
813 if (pThis->fTraceRequests)
814 {
815 pThis->fRunning = false;
816 RTSemEventSignal(pThis->SemEvent);
817 RTSemEventDestroy(pThis->SemEvent);
818 }
819
820 if (pThis->fCheckDoubleCompletion)
821 {
822 /* Free all requests */
823 while (pThis->papIoReq[pThis->iEntry])
824 {
825 RTMemFree(pThis->papIoReq[pThis->iEntry]);
826 pThis->papIoReq[pThis->iEntry] = NULL;
827 pThis->iEntry = (pThis->iEntry+1) % pThis->cEntries;
828 }
829 }
830}
831
832/**
833 * Construct a disk integrity driver instance.
834 *
835 * @copydoc FNPDMDRVCONSTRUCT
836 */
837static DECLCALLBACK(int) drvdiskintConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
838{
839 int rc = VINF_SUCCESS;
840 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
841 LogFlow(("drvdiskintConstruct: iInstance=%d\n", pDrvIns->iInstance));
842 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
843
844 /*
845 * Validate configuration.
846 */
847 if (!CFGMR3AreValuesValid(pCfg, "CheckConsistency\0"
848 "TraceRequests\0"
849 "CheckIntervalMs\0"
850 "ExpireIntervalMs\0"
851 "CheckDoubleCompletions\0"
852 "HistorySize\0"))
853 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
854
855 rc = CFGMR3QueryBoolDef(pCfg, "CheckConsistency", &pThis->fCheckConsistency, false);
856 AssertRC(rc);
857 rc = CFGMR3QueryBoolDef(pCfg, "TraceRequests", &pThis->fTraceRequests, false);
858 AssertRC(rc);
859 rc = CFGMR3QueryU32Def(pCfg, "CheckIntervalMs", &pThis->uCheckIntervalMs, 5000); /* 5 seconds */
860 AssertRC(rc);
861 rc = CFGMR3QueryU32Def(pCfg, "ExpireIntervalMs", &pThis->uExpireIntervalMs, 20000); /* 20 seconds */
862 AssertRC(rc);
863 rc = CFGMR3QueryBoolDef(pCfg, "CheckDoubleCompletions", &pThis->fCheckDoubleCompletion, false);
864 AssertRC(rc);
865 rc = CFGMR3QueryU32Def(pCfg, "HistorySize", &pThis->cEntries, 512);
866 AssertRC(rc);
867
868 /*
869 * Initialize most of the data members.
870 */
871 pThis->pDrvIns = pDrvIns;
872
873 /* IBase. */
874 pDrvIns->IBase.pfnQueryInterface = drvdiskintQueryInterface;
875
876 /* IMedia */
877 pThis->IMedia.pfnRead = drvdiskintRead;
878 pThis->IMedia.pfnWrite = drvdiskintWrite;
879 pThis->IMedia.pfnFlush = drvdiskintFlush;
880 pThis->IMedia.pfnGetSize = drvdiskintGetSize;
881 pThis->IMedia.pfnIsReadOnly = drvdiskintIsReadOnly;
882 pThis->IMedia.pfnBiosGetPCHSGeometry = drvdiskintBiosGetPCHSGeometry;
883 pThis->IMedia.pfnBiosSetPCHSGeometry = drvdiskintBiosSetPCHSGeometry;
884 pThis->IMedia.pfnBiosGetLCHSGeometry = drvdiskintBiosGetLCHSGeometry;
885 pThis->IMedia.pfnBiosSetLCHSGeometry = drvdiskintBiosSetLCHSGeometry;
886 pThis->IMedia.pfnGetUuid = drvdiskintGetUuid;
887
888 /* IMediaAsync */
889 pThis->IMediaAsync.pfnStartRead = drvdiskintStartRead;
890 pThis->IMediaAsync.pfnStartWrite = drvdiskintStartWrite;
891 pThis->IMediaAsync.pfnStartFlush = drvdiskintStartFlush;
892
893 /* IMediaAsyncPort. */
894 pThis->IMediaAsyncPort.pfnTransferCompleteNotify = drvdiskintAsyncTransferCompleteNotify;
895
896 /*
897 * Try attach driver below and query it's media interface.
898 */
899 PPDMIBASE pBase;
900 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
901 if (RT_FAILURE(rc))
902 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
903 N_("Failed to attach driver below us! %Rrc"), rc);
904
905 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
906 if (!pThis->pDrvMedia)
907 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
908 N_("No media or async media interface below"));
909
910 pThis->pDrvMediaAsync = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIAASYNC);
911
912 /* Try to attach async media port interface above.*/
913 pThis->pDrvMediaAsyncPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAASYNCPORT);
914
915 if (pThis->fCheckConsistency)
916 {
917 /* Create the AVL tree. */
918 pThis->pTreeSegments = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
919 if (!pThis->pTreeSegments)
920 rc = VERR_NO_MEMORY;
921 }
922
923 if (pThis->fTraceRequests)
924 {
925 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apReqActive); i++)
926 {
927 pThis->apReqActive[i].pIoReq = NULL;
928 pThis->apReqActive[i].tsStart = 0;
929 }
930
931 pThis->iNextFreeSlot = 0;
932
933 /* Init event semaphore. */
934 rc = RTSemEventCreate(&pThis->SemEvent);
935 AssertRC(rc);
936 pThis->fRunning = true;
937 rc = RTThreadCreate(&pThis->hThread, drvdiskIntIoReqExpiredCheck, pThis,
938 0, RTTHREADTYPE_INFREQUENT_POLLER, 0, "DiskIntegrity");
939 AssertRC(rc);
940 }
941
942 if (pThis->fCheckDoubleCompletion)
943 {
944 pThis->iEntry = 0;
945 pThis->papIoReq = (PDRVDISKAIOREQ *)RTMemAllocZ(pThis->cEntries * sizeof(PDRVDISKAIOREQ));
946 AssertPtr(pThis->papIoReq);
947 }
948
949 return rc;
950}
951
952
953/**
954 * Block driver registration record.
955 */
956const PDMDRVREG g_DrvDiskIntegrity =
957{
958 /* u32Version */
959 PDM_DRVREG_VERSION,
960 /* szName */
961 "DiskIntegrity",
962 /* szRCMod */
963 "",
964 /* szR0Mod */
965 "",
966 /* pszDescription */
967 "Disk integrity driver.",
968 /* fFlags */
969 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
970 /* fClass. */
971 PDM_DRVREG_CLASS_BLOCK,
972 /* cMaxInstances */
973 ~0,
974 /* cbInstance */
975 sizeof(DRVDISKINTEGRITY),
976 /* pfnConstruct */
977 drvdiskintConstruct,
978 /* pfnDestruct */
979 drvdiskintDestruct,
980 /* pfnRelocate */
981 NULL,
982 /* pfnIOCtl */
983 NULL,
984 /* pfnPowerOn */
985 NULL,
986 /* pfnReset */
987 NULL,
988 /* pfnSuspend */
989 NULL,
990 /* pfnResume */
991 NULL,
992 /* pfnAttach */
993 NULL,
994 /* pfnDetach */
995 NULL,
996 /* pfnPowerOff */
997 NULL,
998 /* pfnSoftReset */
999 NULL,
1000 /* u32EndVersion */
1001 PDM_DRVREG_VERSION
1002};
1003
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use