VirtualBox

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

Last change on this file since 35753 was 35353, checked in by vboxsync, 14 years ago

Move the misc files the in src/VBox/Devices/ directory into a build/ subdirectory, changing their names to match the target module.

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

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