VirtualBox

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

Last change on this file since 46155 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

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