VirtualBox

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

Last change on this file since 38791 was 38670, checked in by vboxsync, 13 years ago

DrvDiskIntegrity: Discard records if consistency check is enabled

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.2 KB
Line 
1/* $Id: DrvDiskIntegrity.cpp 38670 2011-09-07 09:40:44Z 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 <VBox/vddbg.h>
25#include <iprt/assert.h>
26#include <iprt/string.h>
27#include <iprt/uuid.h>
28#include <iprt/avl.h>
29#include <iprt/mem.h>
30#include <iprt/message.h>
31#include <iprt/sg.h>
32#include <iprt/time.h>
33#include <iprt/semaphore.h>
34#include <iprt/asm.h>
35
36#include "VBoxDD.h"
37
38
39/*******************************************************************************
40* Structures and Typedefs *
41*******************************************************************************/
42
43/**
44 * Transfer direction.
45 */
46typedef enum DRVDISKAIOTXDIR
47{
48 /** Read */
49 DRVDISKAIOTXDIR_READ = 0,
50 /** Write */
51 DRVDISKAIOTXDIR_WRITE,
52 /** Flush */
53 DRVDISKAIOTXDIR_FLUSH
54} DRVDISKAIOTXDIR;
55
56/**
57 * async I/O request.
58 */
59typedef struct DRVDISKAIOREQ
60{
61 /** Transfer direction. */
62 DRVDISKAIOTXDIR enmTxDir;
63 /** Start offset. */
64 uint64_t off;
65 /** Transfer size. */
66 size_t cbTransfer;
67 /** Segment array. */
68 PCRTSGSEG paSeg;
69 /** Number of array entries. */
70 unsigned cSeg;
71 /** User argument */
72 void *pvUser;
73 /** Slot in the array. */
74 unsigned iSlot;
75 /** Start timestamp */
76 uint64_t tsStart;
77 /** Completion timestamp. */
78 uint64_t tsComplete;
79 /** I/O log entry if configured. */
80 VDIOLOGENT hIoLogEntry;
81} DRVDISKAIOREQ, *PDRVDISKAIOREQ;
82
83/**
84 * I/O log entry.
85 */
86typedef struct IOLOGENT
87{
88 /** Start offset */
89 uint64_t off;
90 /** Write size */
91 size_t cbWrite;
92 /** Number of references to this entry. */
93 unsigned cRefs;
94} IOLOGENT, *PIOLOGENT;
95
96/**
97 * Disk segment.
98 */
99typedef struct DRVDISKSEGMENT
100{
101 /** AVL core. */
102 AVLRFOFFNODECORE Core;
103 /** Size of the segment */
104 size_t cbSeg;
105 /** Data for this segment */
106 uint8_t *pbSeg;
107 /** Number of entries in the I/O array. */
108 unsigned cIoLogEntries;
109 /** Array of I/O log references. */
110 PIOLOGENT apIoLog[1];
111} DRVDISKSEGMENT, *PDRVDISKSEGMENT;
112
113/**
114 * Active requests list entry.
115 */
116typedef struct DRVDISKAIOREQACTIVE
117{
118 /** Pointer to the request. */
119 volatile PDRVDISKAIOREQ pIoReq;
120 /** Start timestamp. */
121 uint64_t tsStart;
122} DRVDISKAIOREQACTIVE, *PDRVDISKAIOREQACTIVE;
123
124/**
125 * Disk integrity driver instance data.
126 *
127 * @implements PDMIMEDIA
128 */
129typedef struct DRVDISKINTEGRITY
130{
131 /** Pointer driver instance. */
132 PPDMDRVINS pDrvIns;
133 /** Pointer to the media driver below us.
134 * This is NULL if the media is not mounted. */
135 PPDMIMEDIA pDrvMedia;
136 /** Our media interface */
137 PDMIMEDIA IMedia;
138
139 /** The media port interface above. */
140 PPDMIMEDIAPORT pDrvMediaPort;
141 /** Media port interface */
142 PDMIMEDIAPORT IMediaPort;
143
144 /** Pointer to the media async driver below us.
145 * This is NULL if the media is not mounted. */
146 PPDMIMEDIAASYNC pDrvMediaAsync;
147 /** Our media async interface */
148 PDMIMEDIAASYNC IMediaAsync;
149
150 /** The async media port interface above. */
151 PPDMIMEDIAASYNCPORT pDrvMediaAsyncPort;
152 /** Our media async port interface */
153 PDMIMEDIAASYNCPORT IMediaAsyncPort;
154
155 /** Flag whether consistency checks are enabled. */
156 bool fCheckConsistency;
157 /** AVL tree containing the disk blocks to check. */
158 PAVLRFOFFTREE pTreeSegments;
159
160 /** Flag whether async request tracing is enabled. */
161 bool fTraceRequests;
162 /** Interval the thread should check for expired requests (milliseconds). */
163 uint32_t uCheckIntervalMs;
164 /** Expire timeout for a request (milliseconds). */
165 uint32_t uExpireIntervalMs;
166 /** Thread which checks for lost requests. */
167 RTTHREAD hThread;
168 /** Event semaphore */
169 RTSEMEVENT SemEvent;
170 /** Flag whether the thread should run. */
171 bool fRunning;
172 /** Array containing active requests. */
173 DRVDISKAIOREQACTIVE apReqActive[128];
174 /** Next free slot in the array */
175 volatile unsigned iNextFreeSlot;
176
177 /** Flag whether we check for requests completing twice. */
178 bool fCheckDoubleCompletion;
179 /** Number of requests we go back. */
180 unsigned cEntries;
181 /** Array of completed but still observed requests. */
182 PDRVDISKAIOREQ *papIoReq;
183 /** Current entry in the array. */
184 unsigned iEntry;
185
186 /** I/O logger to use if enabled. */
187 VDIOLOGGER hIoLogger;
188} DRVDISKINTEGRITY, *PDRVDISKINTEGRITY;
189
190
191/**
192 * Allocate a new I/O request.
193 *
194 * @returns New I/O request.
195 * @param enmTxDir Transfer direction.
196 * @param off Start offset.
197 * @param paSeg Segment array.
198 * @param cSeg Number of segments.
199 * @param cbTransfer Number of bytes to transfer.
200 * @param pvUser User argument.
201 */
202static PDRVDISKAIOREQ drvdiskintIoReqAlloc(DRVDISKAIOTXDIR enmTxDir, uint64_t off, PCRTSGSEG paSeg,
203 unsigned cSeg, size_t cbTransfer, void *pvUser)
204{
205 PDRVDISKAIOREQ pIoReq = (PDRVDISKAIOREQ)RTMemAlloc(sizeof(DRVDISKAIOREQ));
206
207 if (RT_LIKELY(pIoReq))
208 {
209 pIoReq->enmTxDir = enmTxDir;
210 pIoReq->off = off;
211 pIoReq->cbTransfer = cbTransfer;
212 pIoReq->paSeg = paSeg;
213 pIoReq->cSeg = cSeg;
214 pIoReq->pvUser = pvUser;
215 pIoReq->iSlot = 0;
216 pIoReq->tsStart = RTTimeSystemMilliTS();
217 pIoReq->tsComplete = 0;
218 pIoReq->hIoLogEntry = NULL;
219 }
220
221 return pIoReq;
222}
223
224/**
225 * Free a async I/O request.
226 *
227 * @returns nothing.
228 * @param pThis Disk driver.
229 * @param pIoReq The I/O request to free.
230 */
231static void drvdiskintIoReqFree(PDRVDISKINTEGRITY pThis, PDRVDISKAIOREQ pIoReq)
232{
233 if (pThis->fCheckDoubleCompletion)
234 {
235 /* Search if the I/O request completed already. */
236 for (unsigned i = 0; i < pThis->cEntries; i++)
237 {
238 if (RT_UNLIKELY(pThis->papIoReq[i] == pIoReq))
239 {
240 RTMsgError("Request %#p completed already!\n", pIoReq);
241 RTMsgError("Start timestamp %llu Completion timestamp %llu (completed after %llu ms)\n",
242 pIoReq->tsStart, pIoReq->tsComplete, pIoReq->tsComplete - pIoReq->tsStart);
243 RTAssertDebugBreak();
244 }
245 }
246
247 pIoReq->tsComplete = RTTimeSystemMilliTS();
248 Assert(!pThis->papIoReq[pThis->iEntry]);
249 pThis->papIoReq[pThis->iEntry] = pIoReq;
250
251 pThis->iEntry = (pThis->iEntry+1) % pThis->cEntries;
252 if (pThis->papIoReq[pThis->iEntry])
253 {
254 RTMemFree(pThis->papIoReq[pThis->iEntry]);
255 pThis->papIoReq[pThis->iEntry] = NULL;
256 }
257 }
258 else
259 RTMemFree(pIoReq);
260}
261
262static void drvdiskintIoLogEntryRelease(PIOLOGENT pIoLogEnt)
263{
264 pIoLogEnt->cRefs--;
265 if (!pIoLogEnt->cRefs)
266 RTMemFree(pIoLogEnt);
267}
268
269/**
270 * Record a successful write to the virtual disk.
271 *
272 * @returns VBox status code.
273 * @param pThis Disk integrity driver instance data.
274 * @param paSeg Segment array of the write to record.
275 * @param cSeg Number of segments.
276 * @param off Start offset.
277 * @param cbWrite Number of bytes to record.
278 */
279static int drvdiskintWriteRecord(PDRVDISKINTEGRITY pThis, PCRTSGSEG paSeg, unsigned cSeg,
280 uint64_t off, size_t cbWrite)
281{
282 int rc = VINF_SUCCESS;
283
284 LogFlowFunc(("pThis=%#p paSeg=%#p cSeg=%u off=%llx cbWrite=%u\n",
285 pThis, paSeg, cSeg, off, cbWrite));
286
287 /* Update the segments */
288 size_t cbLeft = cbWrite;
289 RTFOFF offCurr = (RTFOFF)off;
290 RTSGBUF SgBuf;
291 PIOLOGENT pIoLogEnt = (PIOLOGENT)RTMemAllocZ(sizeof(IOLOGENT));
292 if (!pIoLogEnt)
293 return VERR_NO_MEMORY;
294
295 pIoLogEnt->off = off;
296 pIoLogEnt->cbWrite = cbWrite;
297 pIoLogEnt->cRefs = 0;
298
299 RTSgBufInit(&SgBuf, paSeg, cSeg);
300
301 while (cbLeft)
302 {
303 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr);
304 size_t cbRange = 0;
305 bool fSet = false;
306 unsigned offSeg = 0;
307
308 if (!pSeg)
309 {
310 /* Get next segment */
311 pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true);
312 if ( !pSeg
313 || offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key)
314 cbRange = cbLeft;
315 else
316 cbRange = pSeg->Core.Key - offCurr;
317
318 Assert(cbRange % 512 == 0);
319
320 /* Create new segment */
321 pSeg = (PDRVDISKSEGMENT)RTMemAllocZ(RT_OFFSETOF(DRVDISKSEGMENT, apIoLog[cbRange / 512]));
322 if (pSeg)
323 {
324 pSeg->Core.Key = offCurr;
325 pSeg->Core.KeyLast = offCurr + (RTFOFF)cbRange - 1;
326 pSeg->cbSeg = cbRange;
327 pSeg->pbSeg = (uint8_t *)RTMemAllocZ(cbRange);
328 pSeg->cIoLogEntries = cbRange / 512;
329 if (!pSeg->pbSeg)
330 RTMemFree(pSeg);
331 else
332 {
333 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
334 AssertMsg(fInserted, ("Bug!\n"));
335 fSet = true;
336 }
337 }
338 }
339 else
340 {
341 fSet = true;
342 offSeg = offCurr - pSeg->Core.Key;
343 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
344 }
345
346 if (fSet)
347 {
348 AssertPtr(pSeg);
349 size_t cbCopied = RTSgBufCopyToBuf(&SgBuf, pSeg->pbSeg + offSeg, cbRange);
350 Assert(cbCopied == cbRange);
351
352 /* Update the I/O log pointers */
353 Assert(offSeg % 512 == 0);
354 Assert(cbRange % 512 == 0);
355 while (offSeg < cbRange)
356 {
357 uint32_t uSector = offSeg / 512;
358 PIOLOGENT pIoLogOld = NULL;
359
360 AssertMsg(uSector < pSeg->cIoLogEntries, ("Internal bug!\n"));
361
362 pIoLogOld = pSeg->apIoLog[uSector];
363 if (pIoLogOld)
364 {
365 pIoLogOld->cRefs--;
366 if (!pIoLogOld->cRefs)
367 RTMemFree(pIoLogOld);
368 }
369
370 pSeg->apIoLog[uSector] = pIoLogEnt;
371 pIoLogEnt->cRefs++;
372
373 offSeg += 512;
374 }
375 }
376 else
377 RTSgBufAdvance(&SgBuf, cbRange);
378
379 offCurr += cbRange;
380 cbLeft -= cbRange;
381 }
382
383 return rc;
384}
385
386/**
387 * Verifies a read request.
388 *
389 * @returns VBox status code.
390 * @param pThis Disk integrity driver instance data.
391 * @param paSeg Segment array of the containing the data buffers to verify.
392 * @param cSeg Number of segments.
393 * @param off Start offset.
394 * @param cbWrite Number of bytes to verify.
395 */
396static int drvdiskintReadVerify(PDRVDISKINTEGRITY pThis, PCRTSGSEG paSeg, unsigned cSeg,
397 uint64_t off, size_t cbRead)
398{
399 int rc = VINF_SUCCESS;
400
401 LogFlowFunc(("pThis=%#p paSeg=%#p cSeg=%u off=%llx cbRead=%u\n",
402 pThis, paSeg, cSeg, off, cbRead));
403
404 Assert(off % 512 == 0);
405 Assert(cbRead % 512 == 0);
406
407 /* Compare read data */
408 size_t cbLeft = cbRead;
409 RTFOFF offCurr = (RTFOFF)off;
410 RTSGBUF SgBuf;
411
412 RTSgBufInit(&SgBuf, paSeg, cSeg);
413
414 while (cbLeft)
415 {
416 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr);
417 size_t cbRange = 0;
418 bool fCmp = false;
419 unsigned offSeg = 0;
420
421 if (!pSeg)
422 {
423 /* Get next segment */
424 pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true);
425 if (!pSeg)
426 {
427 /* No data in the tree for this read. Assume everything is ok. */
428 cbRange = cbLeft;
429 }
430 else if (offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key)
431 cbRange = cbLeft;
432 else
433 cbRange = pSeg->Core.Key - offCurr;
434 }
435 else
436 {
437 fCmp = true;
438 offSeg = offCurr - pSeg->Core.Key;
439 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
440 }
441
442 if (fCmp)
443 {
444 RTSGSEG Seg;
445 RTSGBUF SgBufCmp;
446 size_t cbOff = 0;
447
448 Seg.cbSeg = cbRange;
449 Seg.pvSeg = pSeg->pbSeg + offSeg;
450
451 RTSgBufInit(&SgBufCmp, &Seg, 1);
452 if (RTSgBufCmpEx(&SgBuf, &SgBufCmp, cbRange, &cbOff, true))
453 {
454 /* Corrupted disk, print I/O log entry of the last write which accessed this range. */
455 uint32_t cSector = (offSeg + cbOff) / 512;
456 AssertMsg(cSector < pSeg->cIoLogEntries, ("Internal bug!\n"));
457
458 RTMsgError("Corrupted disk at offset %llu (%u bytes in the current read buffer)!\n",
459 offCurr + cbOff, cbOff);
460 RTMsgError("Last write to this sector started at offset %llu with %u bytes (%u references to this log entry)\n",
461 pSeg->apIoLog[cSector]->off,
462 pSeg->apIoLog[cSector]->cbWrite,
463 pSeg->apIoLog[cSector]->cRefs);
464 RTAssertDebugBreak();
465 }
466 }
467 else
468 RTSgBufAdvance(&SgBuf, cbRange);
469
470 offCurr += cbRange;
471 cbLeft -= cbRange;
472 }
473
474 return rc;
475}
476
477/**
478 * Discards the given ranges from the disk.
479 *
480 * @returns VBox status code.
481 * @param pThis Disk integrity driver instance data.
482 * @param paRanges Array of ranges to discard.
483 * @param cRanges Number of ranges in the array.
484 */
485static int drvdiskintDiscardRecords(PDRVDISKINTEGRITY pThis, PPDMRANGE paRanges, unsigned cRanges)
486{
487 int rc = VINF_SUCCESS;
488
489 LogFlowFunc(("pThis=%#p paRanges=%#p cRanges=%u\n", pThis, paRanges, cRanges));
490
491 for (unsigned i = 0; i < cRanges; i++)
492 {
493 uint64_t offStart = paRanges[i].offStart;
494 size_t cbLeft = paRanges[i].cbRange;
495
496 LogFlowFunc(("Discarding off=%llu cbRange=%zu\n", offStart, cbLeft));
497
498 while (cbLeft)
499 {
500 size_t cbRange;
501 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offStart);
502
503 if (!pSeg)
504 {
505 /* Get next segment */
506 pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offStart, true);
507 if ( !pSeg
508 || (RTFOFF)offStart + (RTFOFF)cbLeft <= pSeg->Core.Key)
509 cbRange = cbLeft;
510 else
511 cbRange = pSeg->Core.Key - offStart;
512
513 Assert(!(cbRange % 512));
514 }
515 else
516 {
517 size_t cbPreLeft, cbPostLeft;
518
519 cbRange = RT_MIN(cbRange, pSeg->Core.KeyLast - offStart + 1);
520 cbPreLeft = offStart - pSeg->Core.Key;
521 cbPostLeft = pSeg->cbSeg - cbRange - cbPreLeft;
522
523 Assert(!(cbRange % 512));
524 Assert(!(cbPreLeft % 512));
525 Assert(!(cbPostLeft % 512));
526
527 LogFlowFunc(("cbRange=%zu cbPreLeft=%zu cbPostLeft=%zu\n",
528 cbRange, cbPreLeft, cbPostLeft));
529
530 RTAvlrFileOffsetRemove(pThis->pTreeSegments, pSeg->Core.Key);
531
532 if (!cbPreLeft && !cbPostLeft)
533 {
534 /* Just free the whole segment. */
535 LogFlowFunc(("Freeing whole segment pSeg=%#p\n", pSeg));
536 RTMemFree(pSeg->pbSeg);
537 for (unsigned idx = 0; idx < pSeg->cIoLogEntries; idx++)
538 drvdiskintIoLogEntryRelease(pSeg->apIoLog[idx]);
539 RTMemFree(pSeg);
540 }
541 else if (cbPreLeft && !cbPostLeft)
542 {
543 /* Realloc to new size and insert. */
544 LogFlowFunc(("Realloc segment pSeg=%#p\n", pSeg));
545 pSeg->pbSeg = (uint8_t *)RTMemRealloc(pSeg->pbSeg, cbPreLeft);
546 for (unsigned idx = cbPreLeft / 512; idx < pSeg->cIoLogEntries; idx++)
547 drvdiskintIoLogEntryRelease(pSeg->apIoLog[idx]);
548 pSeg = (PDRVDISKSEGMENT)RTMemRealloc(pSeg, RT_OFFSETOF(DRVDISKSEGMENT, apIoLog[cbPreLeft / 512]));
549 pSeg->Core.KeyLast = pSeg->Core.Key + cbPreLeft - 1;
550 pSeg->cbSeg = cbPreLeft;
551 pSeg->cIoLogEntries = cbPreLeft / 512;
552 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
553 Assert(fInserted);
554 }
555 else if (!cbPreLeft && cbPostLeft)
556 {
557 /* Move data to the front and realloc. */
558 LogFlowFunc(("Move data and realloc segment pSeg=%#p\n", pSeg));
559 memmove(pSeg->pbSeg, pSeg->pbSeg + cbRange, cbPostLeft);
560 for (unsigned idx = 0; idx < cbRange / 512; idx++)
561 drvdiskintIoLogEntryRelease(pSeg->apIoLog[idx]);
562 for (unsigned idx = 0; idx < cbPostLeft /512; idx++)
563 pSeg->apIoLog[idx] = pSeg->apIoLog[(cbRange / 512) + idx];
564 pSeg = (PDRVDISKSEGMENT)RTMemRealloc(pSeg, RT_OFFSETOF(DRVDISKSEGMENT, apIoLog[cbPostLeft / 512]));
565 pSeg->pbSeg = (uint8_t *)RTMemRealloc(pSeg->pbSeg, cbPostLeft);
566 pSeg->Core.Key += cbRange;
567 pSeg->cbSeg = cbPostLeft;
568 pSeg->cIoLogEntries = cbPostLeft / 512;
569 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
570 Assert(fInserted);
571 }
572 else
573 {
574 /* Split the segment into 2 new segments. */
575 LogFlowFunc(("Split segment pSeg=%#p\n", pSeg));
576 PDRVDISKSEGMENT pSegPost = (PDRVDISKSEGMENT)RTMemAllocZ(RT_OFFSETOF(DRVDISKSEGMENT, apIoLog[cbPostLeft / 512]));
577 if (pSegPost)
578 {
579 pSegPost->Core.Key = pSeg->Core.Key + cbPreLeft + cbRange;
580 pSegPost->Core.KeyLast = pSeg->Core.KeyLast;
581 pSegPost->cbSeg = cbPostLeft;
582 pSegPost->pbSeg = (uint8_t *)RTMemAllocZ(cbPostLeft);
583 pSegPost->cIoLogEntries = cbPostLeft / 512;
584 if (!pSegPost->pbSeg)
585 RTMemFree(pSegPost);
586 else
587 {
588 memcpy(pSegPost->pbSeg, pSeg->pbSeg + cbPreLeft + cbRange, cbPostLeft);
589 for (unsigned idx = 0; idx < cbPostLeft / 512; idx++)
590 pSegPost->apIoLog[idx] = pSeg->apIoLog[((cbPreLeft + cbRange) / 512) + idx];
591
592 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSegPost->Core);
593 Assert(fInserted);
594 }
595 }
596
597 /* Shrink the current segment. */
598 pSeg->pbSeg = (uint8_t *)RTMemRealloc(pSeg->pbSeg, cbPreLeft);
599 for (unsigned idx = cbPreLeft / 512; idx < (cbPreLeft + cbRange) / 512; idx++)
600 drvdiskintIoLogEntryRelease(pSeg->apIoLog[idx]);
601 pSeg = (PDRVDISKSEGMENT)RTMemRealloc(pSeg, RT_OFFSETOF(DRVDISKSEGMENT, apIoLog[cbPreLeft / 512]));
602 pSeg->Core.KeyLast = pSeg->Core.Key + cbPreLeft - 1;
603 pSeg->cbSeg = cbPreLeft;
604 pSeg->cIoLogEntries = cbPreLeft / 512;
605 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
606 Assert(fInserted);
607 } /* if (cbPreLeft && cbPostLeft) */
608 }
609
610 offStart += cbRange;
611 cbLeft -= cbRange;
612 }
613 }
614
615 LogFlowFunc(("returns rc=%Rrc\n", rc));
616 return rc;
617}
618
619/**
620 * Adds a request to the active list.
621 *
622 * @returns nothing.
623 * @param pThis The driver instance data.
624 * @param pIoReq The request to add.
625 */
626static void drvdiskintIoReqAdd(PDRVDISKINTEGRITY pThis, PDRVDISKAIOREQ pIoReq)
627{
628 PDRVDISKAIOREQACTIVE pReqActive = &pThis->apReqActive[pThis->iNextFreeSlot];
629
630 Assert(!pReqActive->pIoReq);
631 pReqActive->tsStart = pIoReq->tsStart;
632 pReqActive->pIoReq = pIoReq;
633 pIoReq->iSlot = pThis->iNextFreeSlot;
634
635 /* Search for the next one. */
636 while (pThis->apReqActive[pThis->iNextFreeSlot].pIoReq)
637 pThis->iNextFreeSlot = (pThis->iNextFreeSlot+1) % RT_ELEMENTS(pThis->apReqActive);
638}
639
640/**
641 * Removes a request from the active list.
642 *
643 * @returns nothing.
644 * @param pThis The driver instance data.
645 * @param pIoReq The request to remove.
646 */
647static void drvdiskintIoReqRemove(PDRVDISKINTEGRITY pThis, PDRVDISKAIOREQ pIoReq)
648{
649 PDRVDISKAIOREQACTIVE pReqActive = &pThis->apReqActive[pIoReq->iSlot];
650
651 Assert(pReqActive->pIoReq == pIoReq);
652
653 ASMAtomicWriteNullPtr(&pReqActive->pIoReq);
654}
655
656/**
657 * Thread checking for expired requests.
658 *
659 * @returns IPRT status code.
660 * @param pThread Thread handle.
661 * @param pvUser Opaque user data.
662 */
663static int drvdiskIntIoReqExpiredCheck(RTTHREAD pThread, void *pvUser)
664{
665 PDRVDISKINTEGRITY pThis = (PDRVDISKINTEGRITY)pvUser;
666
667 while (pThis->fRunning)
668 {
669 int rc = RTSemEventWait(pThis->SemEvent, pThis->uCheckIntervalMs);
670
671 if (!pThis->fRunning)
672 break;
673
674 Assert(rc == VERR_TIMEOUT);
675
676 /* Get current timestamp for comparison. */
677 uint64_t tsCurr = RTTimeSystemMilliTS();
678
679 /* Go through the array and check for expired requests. */
680 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apReqActive); i++)
681 {
682 PDRVDISKAIOREQACTIVE pReqActive = &pThis->apReqActive[i];
683 PDRVDISKAIOREQ pIoReq = ASMAtomicReadPtrT(&pReqActive->pIoReq, PDRVDISKAIOREQ);
684
685 if ( pIoReq
686 && (tsCurr > pReqActive->tsStart)
687 && (tsCurr - pReqActive->tsStart) >= pThis->uExpireIntervalMs)
688 {
689 RTMsgError("Request %#p expired (active for %llu ms already)\n",
690 pIoReq, tsCurr - pReqActive->tsStart);
691 RTAssertDebugBreak();
692 }
693 }
694 }
695
696 return VINF_SUCCESS;
697}
698
699/* -=-=-=-=- IMedia -=-=-=-=- */
700
701/** Makes a PDRVDISKINTEGRITY out of a PPDMIMEDIA. */
702#define PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY)((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMedia)) )
703/** Makes a PDRVDISKINTEGRITY out of a PPDMIMEDIAASYNC. */
704#define PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY)((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMediaAsync)) )
705
706/*******************************************************************************
707* Media interface methods *
708*******************************************************************************/
709
710/** @copydoc PDMIMEDIA::pfnRead */
711static DECLCALLBACK(int) drvdiskintRead(PPDMIMEDIA pInterface,
712 uint64_t off, void *pvBuf, size_t cbRead)
713{
714 int rc = VINF_SUCCESS;
715 VDIOLOGENT hIoLogEntry;
716 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
717
718 if (pThis->hIoLogger)
719 {
720 rc = VDDbgIoLogStart(pThis->hIoLogger, false, VDDBGIOLOGREQ_READ, off,
721 cbRead, NULL, &hIoLogEntry);
722 AssertRC(rc);
723 }
724
725 rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, off, pvBuf, cbRead);
726
727 if (pThis->hIoLogger)
728 {
729 RTSGSEG Seg;
730 RTSGBUF SgBuf;
731
732 Seg.pvSeg = pvBuf;
733 Seg.cbSeg = cbRead;
734 RTSgBufInit(&SgBuf, &Seg, 1);
735
736 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, hIoLogEntry, rc, &SgBuf);
737 AssertRC(rc2);
738 }
739
740 if (RT_FAILURE(rc))
741 return rc;
742
743 if (pThis->fCheckConsistency)
744 {
745 /* Verify the read. */
746 RTSGSEG Seg;
747 Seg.cbSeg = cbRead;
748 Seg.pvSeg = pvBuf;
749 rc = drvdiskintReadVerify(pThis, &Seg, 1, off, cbRead);
750 }
751
752 return rc;
753}
754
755/** @copydoc PDMIMEDIA::pfnWrite */
756static DECLCALLBACK(int) drvdiskintWrite(PPDMIMEDIA pInterface,
757 uint64_t off, const void *pvBuf,
758 size_t cbWrite)
759{
760 int rc = VINF_SUCCESS;
761 VDIOLOGENT hIoLogEntry;
762 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
763
764 if (pThis->hIoLogger)
765 {
766 RTSGSEG Seg;
767 RTSGBUF SgBuf;
768
769 Seg.pvSeg = (void *)pvBuf;
770 Seg.cbSeg = cbWrite;
771 RTSgBufInit(&SgBuf, &Seg, 1);
772
773 rc = VDDbgIoLogStart(pThis->hIoLogger, false, VDDBGIOLOGREQ_WRITE, off,
774 cbWrite, &SgBuf, &hIoLogEntry);
775 AssertRC(rc);
776 }
777
778 rc = pThis->pDrvMedia->pfnWrite(pThis->pDrvMedia, off, pvBuf, cbWrite);
779
780 if (pThis->hIoLogger)
781 {
782 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, hIoLogEntry, rc, NULL);
783 AssertRC(rc2);
784 }
785
786 if (RT_FAILURE(rc))
787 return rc;
788
789 if (pThis->fCheckConsistency)
790 {
791 /* Record the write. */
792 RTSGSEG Seg;
793 Seg.cbSeg = cbWrite;
794 Seg.pvSeg = (void *)pvBuf;
795 rc = drvdiskintWriteRecord(pThis, &Seg, 1, off, cbWrite);
796 }
797
798 return rc;
799}
800
801static DECLCALLBACK(int) drvdiskintStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
802 PCRTSGSEG paSeg, unsigned cSeg,
803 size_t cbRead, void *pvUser)
804{
805 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d pvUser=%#p\n", __FUNCTION__,
806 uOffset, paSeg, cSeg, cbRead, pvUser));
807 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface);
808 PDRVDISKAIOREQ pIoReq = drvdiskintIoReqAlloc(DRVDISKAIOTXDIR_READ, uOffset, paSeg, cSeg, cbRead, pvUser);
809 AssertPtr(pIoReq);
810
811 if (pThis->fTraceRequests)
812 drvdiskintIoReqAdd(pThis, pIoReq);
813
814 if (pThis->hIoLogger)
815 {
816 int rc2 = VDDbgIoLogStart(pThis->hIoLogger, true, VDDBGIOLOGREQ_READ, uOffset,
817 cbRead, NULL, &pIoReq->hIoLogEntry);
818 AssertRC(rc2);
819 }
820
821 int rc = pThis->pDrvMediaAsync->pfnStartRead(pThis->pDrvMediaAsync, uOffset, paSeg, cSeg,
822 cbRead, pIoReq);
823 if (rc == VINF_VD_ASYNC_IO_FINISHED)
824 {
825 /* Verify the read now. */
826 if (pThis->fCheckConsistency)
827 {
828 int rc2 = drvdiskintReadVerify(pThis, paSeg, cSeg, uOffset, cbRead);
829 AssertRC(rc2);
830 }
831
832 if (pThis->hIoLogger)
833 {
834 RTSGBUF SgBuf;
835
836 RTSgBufInit(&SgBuf, paSeg, cSeg);
837
838 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, pIoReq->hIoLogEntry, VINF_SUCCESS, &SgBuf);
839 AssertRC(rc2);
840 }
841
842 if (pThis->fTraceRequests)
843 drvdiskintIoReqRemove(pThis, pIoReq);
844 RTMemFree(pIoReq);
845 }
846 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
847 RTMemFree(pIoReq);
848
849 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
850 return rc;
851}
852
853static DECLCALLBACK(int) drvdiskintStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
854 PCRTSGSEG paSeg, unsigned cSeg,
855 size_t cbWrite, void *pvUser)
856{
857 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d pvUser=%#p\n", __FUNCTION__,
858 uOffset, paSeg, cSeg, cbWrite, pvUser));
859 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface);
860 PDRVDISKAIOREQ pIoReq = drvdiskintIoReqAlloc(DRVDISKAIOTXDIR_WRITE, uOffset, paSeg, cSeg, cbWrite, pvUser);
861 AssertPtr(pIoReq);
862
863 if (pThis->fTraceRequests)
864 drvdiskintIoReqAdd(pThis, pIoReq);
865
866 if (pThis->hIoLogger)
867 {
868 RTSGBUF SgBuf;
869
870 RTSgBufInit(&SgBuf, paSeg, cSeg);
871
872 int rc2 = VDDbgIoLogStart(pThis->hIoLogger, true, VDDBGIOLOGREQ_WRITE, uOffset,
873 cbWrite, &SgBuf, &pIoReq->hIoLogEntry);
874 AssertRC(rc2);
875 }
876
877 int rc = pThis->pDrvMediaAsync->pfnStartWrite(pThis->pDrvMediaAsync, uOffset, paSeg, cSeg,
878 cbWrite, pIoReq);
879 if (rc == VINF_VD_ASYNC_IO_FINISHED)
880 {
881 /* Record the write. */
882 if (pThis->fCheckConsistency)
883 {
884 int rc2 = drvdiskintWriteRecord(pThis, paSeg, cSeg, uOffset, cbWrite);
885 AssertRC(rc2);
886 }
887
888 if (pThis->hIoLogger)
889 {
890 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, pIoReq->hIoLogEntry, VINF_SUCCESS, NULL);
891 AssertRC(rc2);
892 }
893
894 if (pThis->fTraceRequests)
895 drvdiskintIoReqRemove(pThis, pIoReq);
896
897 RTMemFree(pIoReq);
898 }
899 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
900 RTMemFree(pIoReq);
901
902 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
903 return rc;
904}
905
906/** @copydoc PDMIMEDIAASYNC::pfnStartFlush */
907static DECLCALLBACK(int) drvdiskintStartFlush(PPDMIMEDIAASYNC pInterface, void *pvUser)
908{
909 int rc = VINF_SUCCESS;
910 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNC_2_DRVDISKINTEGRITY(pInterface);
911 PDRVDISKAIOREQ pIoReq = drvdiskintIoReqAlloc(DRVDISKAIOTXDIR_FLUSH, 0, NULL, 0, 0, pvUser);
912 AssertPtr(pIoReq);
913
914 if (pThis->fTraceRequests)
915 drvdiskintIoReqAdd(pThis, pIoReq);
916
917 if (pThis->hIoLogger)
918 {
919 rc = VDDbgIoLogStart(pThis->hIoLogger, true, VDDBGIOLOGREQ_FLUSH, 0,
920 0, NULL, &pIoReq->hIoLogEntry);
921 AssertRC(rc);
922 }
923
924 rc = pThis->pDrvMediaAsync->pfnStartFlush(pThis->pDrvMediaAsync, pIoReq);
925
926 if (rc == VINF_VD_ASYNC_IO_FINISHED)
927 {
928 if (pThis->hIoLogger)
929 {
930 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, pIoReq->hIoLogEntry, VINF_SUCCESS, NULL);
931 AssertRC(rc2);
932 }
933
934 RTMemFree(pIoReq);
935 }
936 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
937 RTMemFree(pIoReq);
938
939 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
940 return rc;
941}
942
943/** @copydoc PDMIMEDIA::pfnFlush */
944static DECLCALLBACK(int) drvdiskintFlush(PPDMIMEDIA pInterface)
945{
946 int rc = VINF_SUCCESS;
947 VDIOLOGENT hIoLogEntry;
948 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
949
950 if (pThis->hIoLogger)
951 {
952 rc = VDDbgIoLogStart(pThis->hIoLogger, false, VDDBGIOLOGREQ_FLUSH, 0,
953 0, NULL, &hIoLogEntry);
954 AssertRC(rc);
955 }
956
957 rc = pThis->pDrvMedia->pfnFlush(pThis->pDrvMedia);
958
959 if (pThis->hIoLogger)
960 {
961 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, hIoLogEntry, rc, NULL);
962 AssertRC(rc2);
963 }
964
965 return rc;
966}
967
968/** @copydoc PDMIMEDIA::pfnGetSize */
969static DECLCALLBACK(uint64_t) drvdiskintGetSize(PPDMIMEDIA pInterface)
970{
971 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
972 return pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
973}
974
975/** @copydoc PDMIMEDIA::pfnIsReadOnly */
976static DECLCALLBACK(bool) drvdiskintIsReadOnly(PPDMIMEDIA pInterface)
977{
978 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
979 return pThis->pDrvMedia->pfnIsReadOnly(pThis->pDrvMedia);
980}
981
982/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
983static DECLCALLBACK(int) drvdiskintBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
984 PPDMMEDIAGEOMETRY pPCHSGeometry)
985{
986 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
987 return pThis->pDrvMedia->pfnBiosGetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
988}
989
990/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
991static DECLCALLBACK(int) drvdiskintBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
992 PCPDMMEDIAGEOMETRY pPCHSGeometry)
993{
994 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
995 return pThis->pDrvMedia->pfnBiosSetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
996}
997
998/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
999static DECLCALLBACK(int) drvdiskintBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
1000 PPDMMEDIAGEOMETRY pLCHSGeometry)
1001{
1002 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
1003 return pThis->pDrvMedia->pfnBiosGetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
1004}
1005
1006/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
1007static DECLCALLBACK(int) drvdiskintBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
1008 PCPDMMEDIAGEOMETRY pLCHSGeometry)
1009{
1010 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
1011 return pThis->pDrvMedia->pfnBiosSetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
1012}
1013
1014/** @copydoc PDMIMEDIA::pfnGetUuid */
1015static DECLCALLBACK(int) drvdiskintGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
1016{
1017 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
1018 return pThis->pDrvMedia->pfnGetUuid(pThis->pDrvMedia, pUuid);
1019}
1020
1021/** @copydoc PDMIMEDIA::pfnDiscard */
1022static DECLCALLBACK(int) drvdiskintDiscard(PPDMIMEDIA pInterface, PPDMRANGE paRanges, unsigned cRanges)
1023{
1024 int rc = VINF_SUCCESS;
1025 VDIOLOGENT hIoLogEntry;
1026 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
1027
1028 if (pThis->hIoLogger)
1029 {
1030 rc = VDDbgIoLogStartDiscard(pThis->hIoLogger, false, (PVDRANGE)paRanges, cRanges, &hIoLogEntry);
1031 AssertRC(rc);
1032 }
1033
1034 rc = pThis->pDrvMedia->pfnDiscard(pThis->pDrvMedia, paRanges, cRanges);
1035
1036 if (pThis->hIoLogger)
1037 {
1038 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, hIoLogEntry, rc, NULL);
1039 AssertRC(rc2);
1040 }
1041
1042 if (pThis->fCheckConsistency)
1043 rc = drvdiskintDiscardRecords(pThis, paRanges, cRanges);
1044
1045 return rc;
1046}
1047
1048/* -=-=-=-=- IMediaAsyncPort -=-=-=-=- */
1049
1050/** Makes a PDRVBLOCKASYNC out of a PPDMIMEDIAASYNCPORT. */
1051#define PDMIMEDIAASYNCPORT_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMediaAsyncPort))) )
1052
1053static DECLCALLBACK(int) drvdiskintAsyncTransferCompleteNotify(PPDMIMEDIAASYNCPORT pInterface, void *pvUser, int rcReq)
1054{
1055 PDRVDISKINTEGRITY pThis = PDMIMEDIAASYNCPORT_2_DRVDISKINTEGRITY(pInterface);
1056 PDRVDISKAIOREQ pIoReq = (PDRVDISKAIOREQ)pvUser;
1057 int rc = VINF_SUCCESS;
1058
1059 LogFlowFunc(("pIoReq=%#p\n", pIoReq));
1060
1061 /* Remove from the active list. */
1062 if (pThis->fTraceRequests)
1063 drvdiskintIoReqRemove(pThis, pIoReq);
1064
1065 if (RT_SUCCESS(rcReq) && pThis->fCheckConsistency)
1066 {
1067 if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_READ)
1068 rc = drvdiskintReadVerify(pThis, pIoReq->paSeg, pIoReq->cSeg, pIoReq->off, pIoReq->cbTransfer);
1069 else if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_WRITE)
1070 rc = drvdiskintWriteRecord(pThis, pIoReq->paSeg, pIoReq->cSeg, pIoReq->off, pIoReq->cbTransfer);
1071 else
1072 AssertMsg(pIoReq->enmTxDir == DRVDISKAIOTXDIR_FLUSH, ("Huh?\n"));
1073
1074 AssertRC(rc);
1075 }
1076
1077 if (pThis->hIoLogger)
1078 {
1079 RTSGBUF SgBuf;
1080
1081 if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_READ)
1082 RTSgBufInit(&SgBuf, pIoReq->paSeg, pIoReq->cSeg);
1083
1084 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, pIoReq->hIoLogEntry, rc, &SgBuf);
1085 AssertRC(rc2);
1086 }
1087
1088 void *pvUserComplete = pIoReq->pvUser;
1089
1090 drvdiskintIoReqFree(pThis, pIoReq);
1091
1092 rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pvUserComplete, rcReq);
1093
1094 return rc;
1095}
1096
1097/* -=-=-=-=- IMediaPort -=-=-=-=- */
1098
1099/** Makes a PDRVBLOCK out of a PPDMIMEDIAPORT. */
1100#define PDMIMEDIAPORT_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMediaPort))) )
1101
1102/**
1103 * @interface_method_impl{PDMIMEDIAPORT,pfnQueryDeviceLocation}
1104 */
1105static DECLCALLBACK(int) drvdiskintQueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
1106 uint32_t *piInstance, uint32_t *piLUN)
1107{
1108 PDRVDISKINTEGRITY pThis = PDMIMEDIAPORT_2_DRVDISKINTEGRITY(pInterface);
1109
1110 return pThis->pDrvMediaPort->pfnQueryDeviceLocation(pThis->pDrvMediaPort, ppcszController,
1111 piInstance, piLUN);
1112}
1113
1114/* -=-=-=-=- IBase -=-=-=-=- */
1115
1116/**
1117 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1118 */
1119static DECLCALLBACK(void *) drvdiskintQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1120{
1121 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1122 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
1123
1124 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1125 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
1126 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNC, pThis->pDrvMediaAsync ? &pThis->IMediaAsync : NULL);
1127 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNCPORT, &pThis->IMediaAsyncPort);
1128 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pThis->IMediaPort);
1129 return NULL;
1130}
1131
1132
1133/* -=-=-=-=- driver interface -=-=-=-=- */
1134
1135static int drvdiskintTreeDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
1136{
1137 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)pNode;
1138
1139 RTMemFree(pSeg->pbSeg);
1140 RTMemFree(pSeg);
1141 return VINF_SUCCESS;
1142}
1143
1144/**
1145 * @copydoc FNPDMDRVDESTRUCT
1146 */
1147static DECLCALLBACK(void) drvdiskintDestruct(PPDMDRVINS pDrvIns)
1148{
1149 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
1150
1151 if (pThis->pTreeSegments)
1152 {
1153 RTAvlrFileOffsetDestroy(pThis->pTreeSegments, drvdiskintTreeDestroy, NULL);
1154 RTMemFree(pThis->pTreeSegments);
1155 }
1156
1157 if (pThis->fTraceRequests)
1158 {
1159 pThis->fRunning = false;
1160 RTSemEventSignal(pThis->SemEvent);
1161 RTSemEventDestroy(pThis->SemEvent);
1162 }
1163
1164 if (pThis->fCheckDoubleCompletion)
1165 {
1166 /* Free all requests */
1167 while (pThis->papIoReq[pThis->iEntry])
1168 {
1169 RTMemFree(pThis->papIoReq[pThis->iEntry]);
1170 pThis->papIoReq[pThis->iEntry] = NULL;
1171 pThis->iEntry = (pThis->iEntry+1) % pThis->cEntries;
1172 }
1173 }
1174
1175 if (pThis->hIoLogger)
1176 VDDbgIoLogDestroy(pThis->hIoLogger);
1177}
1178
1179/**
1180 * Construct a disk integrity driver instance.
1181 *
1182 * @copydoc FNPDMDRVCONSTRUCT
1183 */
1184static DECLCALLBACK(int) drvdiskintConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1185{
1186 int rc = VINF_SUCCESS;
1187 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
1188 LogFlow(("drvdiskintConstruct: iInstance=%d\n", pDrvIns->iInstance));
1189 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1190
1191 /*
1192 * Validate configuration.
1193 */
1194 if (!CFGMR3AreValuesValid(pCfg, "CheckConsistency\0"
1195 "TraceRequests\0"
1196 "CheckIntervalMs\0"
1197 "ExpireIntervalMs\0"
1198 "CheckDoubleCompletions\0"
1199 "HistorySize\0"
1200 "IoLog\0"))
1201 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
1202
1203 rc = CFGMR3QueryBoolDef(pCfg, "CheckConsistency", &pThis->fCheckConsistency, false);
1204 AssertRC(rc);
1205 rc = CFGMR3QueryBoolDef(pCfg, "TraceRequests", &pThis->fTraceRequests, false);
1206 AssertRC(rc);
1207 rc = CFGMR3QueryU32Def(pCfg, "CheckIntervalMs", &pThis->uCheckIntervalMs, 5000); /* 5 seconds */
1208 AssertRC(rc);
1209 rc = CFGMR3QueryU32Def(pCfg, "ExpireIntervalMs", &pThis->uExpireIntervalMs, 20000); /* 20 seconds */
1210 AssertRC(rc);
1211 rc = CFGMR3QueryBoolDef(pCfg, "CheckDoubleCompletions", &pThis->fCheckDoubleCompletion, false);
1212 AssertRC(rc);
1213 rc = CFGMR3QueryU32Def(pCfg, "HistorySize", &pThis->cEntries, 512);
1214 AssertRC(rc);
1215
1216 char *pszIoLogFilename = NULL;
1217 rc = CFGMR3QueryStringAlloc(pCfg, "IoLog", &pszIoLogFilename);
1218 Assert(RT_SUCCESS(rc) || rc == VERR_CFGM_VALUE_NOT_FOUND);
1219
1220 /*
1221 * Initialize most of the data members.
1222 */
1223 pThis->pDrvIns = pDrvIns;
1224
1225 /* IBase. */
1226 pDrvIns->IBase.pfnQueryInterface = drvdiskintQueryInterface;
1227
1228 /* IMedia */
1229 pThis->IMedia.pfnRead = drvdiskintRead;
1230 pThis->IMedia.pfnWrite = drvdiskintWrite;
1231 pThis->IMedia.pfnFlush = drvdiskintFlush;
1232 pThis->IMedia.pfnGetSize = drvdiskintGetSize;
1233 pThis->IMedia.pfnIsReadOnly = drvdiskintIsReadOnly;
1234 pThis->IMedia.pfnBiosGetPCHSGeometry = drvdiskintBiosGetPCHSGeometry;
1235 pThis->IMedia.pfnBiosSetPCHSGeometry = drvdiskintBiosSetPCHSGeometry;
1236 pThis->IMedia.pfnBiosGetLCHSGeometry = drvdiskintBiosGetLCHSGeometry;
1237 pThis->IMedia.pfnBiosSetLCHSGeometry = drvdiskintBiosSetLCHSGeometry;
1238 pThis->IMedia.pfnGetUuid = drvdiskintGetUuid;
1239
1240 /* IMediaAsync */
1241 pThis->IMediaAsync.pfnStartRead = drvdiskintStartRead;
1242 pThis->IMediaAsync.pfnStartWrite = drvdiskintStartWrite;
1243 pThis->IMediaAsync.pfnStartFlush = drvdiskintStartFlush;
1244
1245 /* IMediaAsyncPort. */
1246 pThis->IMediaAsyncPort.pfnTransferCompleteNotify = drvdiskintAsyncTransferCompleteNotify;
1247
1248 /* IMediaPort. */
1249 pThis->IMediaPort.pfnQueryDeviceLocation = drvdiskintQueryDeviceLocation;
1250
1251 /* Query the media port interface above us. */
1252 pThis->pDrvMediaPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAPORT);
1253 if (!pThis->pDrvMediaPort)
1254 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
1255 N_("No media port inrerface above"));
1256
1257 /* Try to attach async media port interface above.*/
1258 pThis->pDrvMediaAsyncPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAASYNCPORT);
1259
1260 /*
1261 * Try attach driver below and query it's media interface.
1262 */
1263 PPDMIBASE pBase;
1264 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
1265 if (RT_FAILURE(rc))
1266 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1267 N_("Failed to attach driver below us! %Rrc"), rc);
1268
1269 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
1270 if (!pThis->pDrvMedia)
1271 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
1272 N_("No media or async media interface below"));
1273
1274 pThis->pDrvMediaAsync = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIAASYNC);
1275
1276 if (pThis->pDrvMedia->pfnDiscard)
1277 pThis->IMedia.pfnDiscard = drvdiskintDiscard;
1278
1279 if (pThis->fCheckConsistency)
1280 {
1281 /* Create the AVL tree. */
1282 pThis->pTreeSegments = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
1283 if (!pThis->pTreeSegments)
1284 rc = VERR_NO_MEMORY;
1285 }
1286
1287 if (pThis->fTraceRequests)
1288 {
1289 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apReqActive); i++)
1290 {
1291 pThis->apReqActive[i].pIoReq = NULL;
1292 pThis->apReqActive[i].tsStart = 0;
1293 }
1294
1295 pThis->iNextFreeSlot = 0;
1296
1297 /* Init event semaphore. */
1298 rc = RTSemEventCreate(&pThis->SemEvent);
1299 AssertRC(rc);
1300 pThis->fRunning = true;
1301 rc = RTThreadCreate(&pThis->hThread, drvdiskIntIoReqExpiredCheck, pThis,
1302 0, RTTHREADTYPE_INFREQUENT_POLLER, 0, "DiskIntegrity");
1303 AssertRC(rc);
1304 }
1305
1306 if (pThis->fCheckDoubleCompletion)
1307 {
1308 pThis->iEntry = 0;
1309 pThis->papIoReq = (PDRVDISKAIOREQ *)RTMemAllocZ(pThis->cEntries * sizeof(PDRVDISKAIOREQ));
1310 AssertPtr(pThis->papIoReq);
1311 }
1312
1313 if (pszIoLogFilename)
1314 {
1315 rc = VDDbgIoLogCreate(&pThis->hIoLogger, pszIoLogFilename, VDDBG_IOLOG_LOG_DATA);
1316 MMR3HeapFree(pszIoLogFilename);
1317 }
1318
1319 return rc;
1320}
1321
1322
1323/**
1324 * Block driver registration record.
1325 */
1326const PDMDRVREG g_DrvDiskIntegrity =
1327{
1328 /* u32Version */
1329 PDM_DRVREG_VERSION,
1330 /* szName */
1331 "DiskIntegrity",
1332 /* szRCMod */
1333 "",
1334 /* szR0Mod */
1335 "",
1336 /* pszDescription */
1337 "Disk integrity driver.",
1338 /* fFlags */
1339 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1340 /* fClass. */
1341 PDM_DRVREG_CLASS_BLOCK,
1342 /* cMaxInstances */
1343 ~0,
1344 /* cbInstance */
1345 sizeof(DRVDISKINTEGRITY),
1346 /* pfnConstruct */
1347 drvdiskintConstruct,
1348 /* pfnDestruct */
1349 drvdiskintDestruct,
1350 /* pfnRelocate */
1351 NULL,
1352 /* pfnIOCtl */
1353 NULL,
1354 /* pfnPowerOn */
1355 NULL,
1356 /* pfnReset */
1357 NULL,
1358 /* pfnSuspend */
1359 NULL,
1360 /* pfnResume */
1361 NULL,
1362 /* pfnAttach */
1363 NULL,
1364 /* pfnDetach */
1365 NULL,
1366 /* pfnPowerOff */
1367 NULL,
1368 /* pfnSoftReset */
1369 NULL,
1370 /* u32EndVersion */
1371 PDM_DRVREG_VERSION
1372};
1373
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