VirtualBox

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

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

Storage: Introduce interface to query the location of a medium (device + instance + LUN) in a VM to generate IDs which are constant for saved states

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.6 KB
Line 
1/* $Id: DrvDiskIntegrity.cpp 34433 2010-11-27 11:09:38Z 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 /** 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 return pThis->pDrvMediaAsync->pfnStartFlush(pThis->pDrvMediaAsync, pIoReq);
676}
677
678/** @copydoc PDMIMEDIA::pfnFlush */
679static DECLCALLBACK(int) drvdiskintFlush(PPDMIMEDIA pInterface)
680{
681 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
682 return pThis->pDrvMedia->pfnFlush(pThis->pDrvMedia);
683}
684
685/** @copydoc PDMIMEDIA::pfnGetSize */
686static DECLCALLBACK(uint64_t) drvdiskintGetSize(PPDMIMEDIA pInterface)
687{
688 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
689 return pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
690}
691
692/** @copydoc PDMIMEDIA::pfnIsReadOnly */
693static DECLCALLBACK(bool) drvdiskintIsReadOnly(PPDMIMEDIA pInterface)
694{
695 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
696 return pThis->pDrvMedia->pfnIsReadOnly(pThis->pDrvMedia);
697}
698
699/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
700static DECLCALLBACK(int) drvdiskintBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
701 PPDMMEDIAGEOMETRY pPCHSGeometry)
702{
703 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
704 return pThis->pDrvMedia->pfnBiosGetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
705}
706
707/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
708static DECLCALLBACK(int) drvdiskintBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
709 PCPDMMEDIAGEOMETRY pPCHSGeometry)
710{
711 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
712 return pThis->pDrvMedia->pfnBiosSetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
713}
714
715/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
716static DECLCALLBACK(int) drvdiskintBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
717 PPDMMEDIAGEOMETRY pLCHSGeometry)
718{
719 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
720 return pThis->pDrvMedia->pfnBiosGetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
721}
722
723/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
724static DECLCALLBACK(int) drvdiskintBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
725 PCPDMMEDIAGEOMETRY pLCHSGeometry)
726{
727 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
728 return pThis->pDrvMedia->pfnBiosSetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
729}
730
731/** @copydoc PDMIMEDIA::pfnGetUuid */
732static DECLCALLBACK(int) drvdiskintGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
733{
734 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
735 return pThis->pDrvMedia->pfnGetUuid(pThis->pDrvMedia, pUuid);
736}
737
738/* -=-=-=-=- IMediaAsyncPort -=-=-=-=- */
739
740/** Makes a PDRVBLOCKASYNC out of a PPDMIMEDIAASYNCPORT. */
741#define PDMIMEDIAASYNCPORT_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMediaAsyncPort))) )
742
743static DECLCALLBACK(int) drvdiskintAsyncTransferCompleteNotify(PPDMIMEDIAASYNCPORT pInterface, void *pvUser, int rcReq)
744{
745 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNCPORT_2_DRVDISKINTEGRITY(pInterface);
746 PDRVDISKAIOREQ pIoReq = (PDRVDISKAIOREQ)pvUser;
747 int rc = VINF_SUCCESS;
748
749 LogFlowFunc(("pIoReq=%#p\n", pIoReq));
750
751 /* Remove from the active list. */
752 if (pThis->fTraceRequests)
753 drvdiskintIoReqRemove(pThis, pIoReq);
754
755 if (RT_SUCCESS(rcReq) && pThis->fCheckConsistency)
756 {
757 if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_READ)
758 rc = drvdiskintReadVerify(pThis, pIoReq->paSeg, pIoReq->cSeg, pIoReq->off, pIoReq->cbTransfer);
759 else if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_WRITE)
760 rc = drvdiskintWriteRecord(pThis, pIoReq->paSeg, pIoReq->cSeg, pIoReq->off, pIoReq->cbTransfer);
761 else
762 AssertMsg(pIoReq->enmTxDir == DRVDISKAIOTXDIR_FLUSH, ("Huh?\n"));
763
764 AssertRC(rc);
765 }
766
767 void *pvUserComplete = pIoReq->pvUser;
768
769 drvdiskintIoReqFree(pThis, pIoReq);
770
771 rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pvUserComplete, rcReq);
772
773 return rc;
774}
775
776/* -=-=-=-=- IMediaPort -=-=-=-=- */
777
778/** Makes a PDRVBLOCK out of a PPDMIMEDIAPORT. */
779#define PDMIMEDIAPORT_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMediaPort))) )
780
781/**
782 * @interface_method_impl{PDMIMEDIAPORT,pfnQueryDeviceLocation}
783 */
784static DECLCALLBACK(int) drvdiskintQueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
785 uint32_t *piInstance, uint32_t *piLUN)
786{
787 PDRVDISKINTEGRITY pThis = PDMIMEDIAPORT_2_DRVDISKINTEGRITY(pInterface);
788
789 return pThis->pDrvMediaPort->pfnQueryDeviceLocation(pThis->pDrvMediaPort, ppcszController,
790 piInstance, piLUN);
791}
792
793/* -=-=-=-=- IBase -=-=-=-=- */
794
795/**
796 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
797 */
798static DECLCALLBACK(void *) drvdiskintQueryInterface(PPDMIBASE pInterface, const char *pszIID)
799{
800 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
801 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
802
803 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
804 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
805 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNC, pThis->pDrvMediaAsync ? &pThis->IMediaAsync : NULL);
806 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNCPORT, &pThis->IMediaAsyncPort);
807 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pThis->IMediaPort);
808 return NULL;
809}
810
811
812/* -=-=-=-=- driver interface -=-=-=-=- */
813
814static int drvdiskintTreeDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
815{
816 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)pNode;
817
818 RTMemFree(pSeg->pbSeg);
819 RTMemFree(pSeg);
820 return VINF_SUCCESS;
821}
822
823/**
824 * @copydoc FNPDMDRVDESTRUCT
825 */
826static DECLCALLBACK(void) drvdiskintDestruct(PPDMDRVINS pDrvIns)
827{
828 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
829
830 if (pThis->pTreeSegments)
831 {
832 RTAvlrFileOffsetDestroy(pThis->pTreeSegments, drvdiskintTreeDestroy, NULL);
833 RTMemFree(pThis->pTreeSegments);
834 }
835
836 if (pThis->fTraceRequests)
837 {
838 pThis->fRunning = false;
839 RTSemEventSignal(pThis->SemEvent);
840 RTSemEventDestroy(pThis->SemEvent);
841 }
842
843 if (pThis->fCheckDoubleCompletion)
844 {
845 /* Free all requests */
846 while (pThis->papIoReq[pThis->iEntry])
847 {
848 RTMemFree(pThis->papIoReq[pThis->iEntry]);
849 pThis->papIoReq[pThis->iEntry] = NULL;
850 pThis->iEntry = (pThis->iEntry+1) % pThis->cEntries;
851 }
852 }
853}
854
855/**
856 * Construct a disk integrity driver instance.
857 *
858 * @copydoc FNPDMDRVCONSTRUCT
859 */
860static DECLCALLBACK(int) drvdiskintConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
861{
862 int rc = VINF_SUCCESS;
863 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
864 LogFlow(("drvdiskintConstruct: iInstance=%d\n", pDrvIns->iInstance));
865 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
866
867 /*
868 * Validate configuration.
869 */
870 if (!CFGMR3AreValuesValid(pCfg, "CheckConsistency\0"
871 "TraceRequests\0"
872 "CheckIntervalMs\0"
873 "ExpireIntervalMs\0"
874 "CheckDoubleCompletions\0"
875 "HistorySize\0"))
876 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
877
878 rc = CFGMR3QueryBoolDef(pCfg, "CheckConsistency", &pThis->fCheckConsistency, false);
879 AssertRC(rc);
880 rc = CFGMR3QueryBoolDef(pCfg, "TraceRequests", &pThis->fTraceRequests, false);
881 AssertRC(rc);
882 rc = CFGMR3QueryU32Def(pCfg, "CheckIntervalMs", &pThis->uCheckIntervalMs, 5000); /* 5 seconds */
883 AssertRC(rc);
884 rc = CFGMR3QueryU32Def(pCfg, "ExpireIntervalMs", &pThis->uExpireIntervalMs, 20000); /* 20 seconds */
885 AssertRC(rc);
886 rc = CFGMR3QueryBoolDef(pCfg, "CheckDoubleCompletions", &pThis->fCheckDoubleCompletion, false);
887 AssertRC(rc);
888 rc = CFGMR3QueryU32Def(pCfg, "HistorySize", &pThis->cEntries, 512);
889 AssertRC(rc);
890
891 /*
892 * Initialize most of the data members.
893 */
894 pThis->pDrvIns = pDrvIns;
895
896 /* IBase. */
897 pDrvIns->IBase.pfnQueryInterface = drvdiskintQueryInterface;
898
899 /* IMedia */
900 pThis->IMedia.pfnRead = drvdiskintRead;
901 pThis->IMedia.pfnWrite = drvdiskintWrite;
902 pThis->IMedia.pfnFlush = drvdiskintFlush;
903 pThis->IMedia.pfnGetSize = drvdiskintGetSize;
904 pThis->IMedia.pfnIsReadOnly = drvdiskintIsReadOnly;
905 pThis->IMedia.pfnBiosGetPCHSGeometry = drvdiskintBiosGetPCHSGeometry;
906 pThis->IMedia.pfnBiosSetPCHSGeometry = drvdiskintBiosSetPCHSGeometry;
907 pThis->IMedia.pfnBiosGetLCHSGeometry = drvdiskintBiosGetLCHSGeometry;
908 pThis->IMedia.pfnBiosSetLCHSGeometry = drvdiskintBiosSetLCHSGeometry;
909 pThis->IMedia.pfnGetUuid = drvdiskintGetUuid;
910
911 /* IMediaAsync */
912 pThis->IMediaAsync.pfnStartRead = drvdiskintStartRead;
913 pThis->IMediaAsync.pfnStartWrite = drvdiskintStartWrite;
914 pThis->IMediaAsync.pfnStartFlush = drvdiskintStartFlush;
915
916 /* IMediaAsyncPort. */
917 pThis->IMediaAsyncPort.pfnTransferCompleteNotify = drvdiskintAsyncTransferCompleteNotify;
918
919 /* IMediaPort. */
920 pThis->IMediaPort.pfnQueryDeviceLocation = drvdiskintQueryDeviceLocation;
921
922 /* Query the media port interface above us. */
923 pThis->pDrvMediaPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAPORT);
924 if (!pThis->pDrvMediaPort)
925 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
926 N_("No media port inrerface above"));
927
928 /* Try to attach async media port interface above.*/
929 pThis->pDrvMediaAsyncPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAASYNCPORT);
930
931 /*
932 * Try attach driver below and query it's media interface.
933 */
934 PPDMIBASE pBase;
935 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
936 if (RT_FAILURE(rc))
937 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
938 N_("Failed to attach driver below us! %Rrc"), rc);
939
940 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
941 if (!pThis->pDrvMedia)
942 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
943 N_("No media or async media interface below"));
944
945 pThis->pDrvMediaAsync = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIAASYNC);
946
947 if (pThis->fCheckConsistency)
948 {
949 /* Create the AVL tree. */
950 pThis->pTreeSegments = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
951 if (!pThis->pTreeSegments)
952 rc = VERR_NO_MEMORY;
953 }
954
955 if (pThis->fTraceRequests)
956 {
957 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apReqActive); i++)
958 {
959 pThis->apReqActive[i].pIoReq = NULL;
960 pThis->apReqActive[i].tsStart = 0;
961 }
962
963 pThis->iNextFreeSlot = 0;
964
965 /* Init event semaphore. */
966 rc = RTSemEventCreate(&pThis->SemEvent);
967 AssertRC(rc);
968 pThis->fRunning = true;
969 rc = RTThreadCreate(&pThis->hThread, drvdiskIntIoReqExpiredCheck, pThis,
970 0, RTTHREADTYPE_INFREQUENT_POLLER, 0, "DiskIntegrity");
971 AssertRC(rc);
972 }
973
974 if (pThis->fCheckDoubleCompletion)
975 {
976 pThis->iEntry = 0;
977 pThis->papIoReq = (PDRVDISKAIOREQ *)RTMemAllocZ(pThis->cEntries * sizeof(PDRVDISKAIOREQ));
978 AssertPtr(pThis->papIoReq);
979 }
980
981 return rc;
982}
983
984
985/**
986 * Block driver registration record.
987 */
988const PDMDRVREG g_DrvDiskIntegrity =
989{
990 /* u32Version */
991 PDM_DRVREG_VERSION,
992 /* szName */
993 "DiskIntegrity",
994 /* szRCMod */
995 "",
996 /* szR0Mod */
997 "",
998 /* pszDescription */
999 "Disk integrity driver.",
1000 /* fFlags */
1001 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1002 /* fClass. */
1003 PDM_DRVREG_CLASS_BLOCK,
1004 /* cMaxInstances */
1005 ~0,
1006 /* cbInstance */
1007 sizeof(DRVDISKINTEGRITY),
1008 /* pfnConstruct */
1009 drvdiskintConstruct,
1010 /* pfnDestruct */
1011 drvdiskintDestruct,
1012 /* pfnRelocate */
1013 NULL,
1014 /* pfnIOCtl */
1015 NULL,
1016 /* pfnPowerOn */
1017 NULL,
1018 /* pfnReset */
1019 NULL,
1020 /* pfnSuspend */
1021 NULL,
1022 /* pfnResume */
1023 NULL,
1024 /* pfnAttach */
1025 NULL,
1026 /* pfnDetach */
1027 NULL,
1028 /* pfnPowerOff */
1029 NULL,
1030 /* pfnSoftReset */
1031 NULL,
1032 /* u32EndVersion */
1033 PDM_DRVREG_VERSION
1034};
1035
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