VirtualBox

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

Last change on this file since 71930 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 68.4 KB
Line 
1/* $Id: DrvDiskIntegrity.cpp 69500 2017-10-28 15:14:05Z vboxsync $ */
2/** @file
3 * VBox storage devices: Disk integrity check.
4 */
5
6/*
7 * Copyright (C) 2006-2017 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/vmm/pdmstorageifs.h>
25#include <VBox/vddbg.h>
26#include <iprt/assert.h>
27#include <iprt/string.h>
28#include <iprt/uuid.h>
29#include <iprt/avl.h>
30#include <iprt/mem.h>
31#include <iprt/memcache.h>
32#include <iprt/message.h>
33#include <iprt/sg.h>
34#include <iprt/time.h>
35#include <iprt/semaphore.h>
36#include <iprt/asm.h>
37
38#include "VBoxDD.h"
39
40
41/*********************************************************************************************************************************
42* Structures and Typedefs *
43*********************************************************************************************************************************/
44
45/**
46 * Transfer direction.
47 */
48typedef enum DRVDISKAIOTXDIR
49{
50 /** Invalid. */
51 DRVDISKAIOTXDIR_INVALID = 0,
52 /** Read */
53 DRVDISKAIOTXDIR_READ,
54 /** Write */
55 DRVDISKAIOTXDIR_WRITE,
56 /** Flush */
57 DRVDISKAIOTXDIR_FLUSH,
58 /** Discard */
59 DRVDISKAIOTXDIR_DISCARD,
60 /** Read after write for immediate verification. */
61 DRVDISKAIOTXDIR_READ_AFTER_WRITE
62} DRVDISKAIOTXDIR;
63
64/**
65 * async I/O request.
66 */
67typedef struct DRVDISKAIOREQ
68{
69 /** Transfer direction. */
70 DRVDISKAIOTXDIR enmTxDir;
71 /** Start offset. */
72 uint64_t off;
73 /** Transfer size. */
74 size_t cbTransfer;
75 /** Segment array. */
76 PCRTSGSEG paSeg;
77 /** Number of array entries. */
78 unsigned cSeg;
79 /** User argument */
80 void *pvUser;
81 /** Slot in the array. */
82 unsigned iSlot;
83 /** Start timestamp */
84 uint64_t tsStart;
85 /** Completion timestamp. */
86 uint64_t tsComplete;
87 /** I/O log entry if configured. */
88 VDIOLOGENT hIoLogEntry;
89 /** Ranges to discard. */
90 PCRTRANGE paRanges;
91 /** Number of ranges. */
92 unsigned cRanges;
93 /** I/O segment for the extended media interface
94 * to hold the data. */
95 RTSGSEG IoSeg;
96} DRVDISKAIOREQ, *PDRVDISKAIOREQ;
97
98/**
99 * I/O log entry.
100 */
101typedef struct IOLOGENT
102{
103 /** Start offset */
104 uint64_t off;
105 /** Write size */
106 size_t cbWrite;
107 /** Number of references to this entry. */
108 unsigned cRefs;
109} IOLOGENT, *PIOLOGENT;
110
111/**
112 * Disk segment.
113 */
114typedef struct DRVDISKSEGMENT
115{
116 /** AVL core. */
117 AVLRFOFFNODECORE Core;
118 /** Size of the segment */
119 size_t cbSeg;
120 /** Data for this segment */
121 uint8_t *pbSeg;
122 /** Number of entries in the I/O array. */
123 unsigned cIoLogEntries;
124 /** Array of I/O log references. */
125 PIOLOGENT apIoLog[1];
126} DRVDISKSEGMENT, *PDRVDISKSEGMENT;
127
128/**
129 * Active requests list entry.
130 */
131typedef struct DRVDISKAIOREQACTIVE
132{
133 /** Pointer to the request. */
134 volatile PDRVDISKAIOREQ pIoReq;
135 /** Start timestamp. */
136 uint64_t tsStart;
137} DRVDISKAIOREQACTIVE, *PDRVDISKAIOREQACTIVE;
138
139/**
140 * Disk integrity driver instance data.
141 *
142 * @implements PDMIMEDIA
143 */
144typedef struct DRVDISKINTEGRITY
145{
146 /** Pointer driver instance. */
147 PPDMDRVINS pDrvIns;
148 /** Pointer to the media driver below us.
149 * This is NULL if the media is not mounted. */
150 PPDMIMEDIA pDrvMedia;
151 /** Our media interface */
152 PDMIMEDIA IMedia;
153
154 /** The media port interface above. */
155 PPDMIMEDIAPORT pDrvMediaPort;
156 /** Media port interface */
157 PDMIMEDIAPORT IMediaPort;
158
159 /** The extended media port interface above. */
160 PPDMIMEDIAEXPORT pDrvMediaExPort;
161 /** Our extended media port interface */
162 PDMIMEDIAEXPORT IMediaExPort;
163
164 /** The extended media interface below. */
165 PPDMIMEDIAEX pDrvMediaEx;
166 /** Our extended media interface */
167 PDMIMEDIAEX IMediaEx;
168
169 /** Flag whether consistency checks are enabled. */
170 bool fCheckConsistency;
171 /** Flag whether the RAM disk was prepopulated. */
172 bool fPrepopulateRamDisk;
173 /** AVL tree containing the disk blocks to check. */
174 PAVLRFOFFTREE pTreeSegments;
175
176 /** Flag whether async request tracing is enabled. */
177 bool fTraceRequests;
178 /** Interval the thread should check for expired requests (milliseconds). */
179 uint32_t uCheckIntervalMs;
180 /** Expire timeout for a request (milliseconds). */
181 uint32_t uExpireIntervalMs;
182 /** Thread which checks for lost requests. */
183 RTTHREAD hThread;
184 /** Event semaphore */
185 RTSEMEVENT SemEvent;
186 /** Flag whether the thread should run. */
187 bool fRunning;
188 /** Array containing active requests. */
189 DRVDISKAIOREQACTIVE apReqActive[128];
190 /** Next free slot in the array */
191 volatile unsigned iNextFreeSlot;
192 /** Request cache. */
193 RTMEMCACHE hReqCache;
194
195 /** Flag whether we check for requests completing twice. */
196 bool fCheckDoubleCompletion;
197 /** Number of requests we go back. */
198 unsigned cEntries;
199 /** Array of completed but still observed requests. */
200 PDRVDISKAIOREQ *papIoReq;
201 /** Current entry in the array. */
202 unsigned iEntry;
203
204 /** Flag whether to do a immediate read after write for verification. */
205 bool fReadAfterWrite;
206 /** Flag whether to record the data to write before the write completed successfully.
207 * Useful in case the data is modified in place later on (encryption for instance). */
208 bool fRecordWriteBeforeCompletion;
209 /** Flag whether to validate memory buffers when the extended media interface is used. */
210 bool fValidateMemBufs;
211
212 /** I/O logger to use if enabled. */
213 VDIOLOGGER hIoLogger;
214 /** Size of the opaque handle until our tracking structure starts in bytes. */
215 size_t cbIoReqOpaque;
216} DRVDISKINTEGRITY, *PDRVDISKINTEGRITY;
217
218
219#define DISKINTEGRITY_IOREQ_HANDLE_2_DRVDISKAIOREQ(a_pThis, a_hIoReq) ((*(PDRVDISKAIOREQ *)((uintptr_t)(a_hIoReq) + (a_pThis)->cbIoReqOpaque)))
220#define DISKINTEGRITY_IOREQ_HANDLE_2_UPPER_OPAQUE(a_pThis, a_hIoReq) ((void *)((uintptr_t)(a_hIoReq) + (a_pThis)->cbIoReqOpaque + sizeof(PDRVDISKAIOREQ)))
221#define DISKINTEGRITY_IOREQ_ALLOC_2_DRVDISKAIOREQ(a_pvIoReqAlloc) (*(PDRVDISKAIOREQ *)(a_pvIoReqAlloc))
222#define DISKINTEGRITY_IOREQ_ALLOC_2_UPPER(a_pvIoReqAlloc) ((void *)((uintptr_t)(a_pvIoReqAlloc) + sizeof(PDRVDISKAIOREQ)))
223
224static void drvdiskintIoReqCheckForDoubleCompletion(PDRVDISKINTEGRITY pThis, PDRVDISKAIOREQ pIoReq,
225 bool fMediaEx)
226{
227 /* Search if the I/O request completed already. */
228 for (unsigned i = 0; i < pThis->cEntries; i++)
229 {
230 if (RT_UNLIKELY(pThis->papIoReq[i] == pIoReq))
231 {
232 RTMsgError("Request %#p completed already!\n", pIoReq);
233 if (!fMediaEx)
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 if (!fMediaEx)
248 RTMemFree(pThis->papIoReq[pThis->iEntry]);
249 pThis->papIoReq[pThis->iEntry] = NULL;
250 }
251}
252
253static void drvdiskintIoLogEntryRelease(PIOLOGENT pIoLogEnt)
254{
255 pIoLogEnt->cRefs--;
256 if (!pIoLogEnt->cRefs)
257 RTMemFree(pIoLogEnt);
258}
259
260/**
261 * Record a successful write to the virtual disk.
262 *
263 * @returns VBox status code.
264 * @param pThis Disk integrity driver instance data.
265 * @param paSeg Segment array of the write to record.
266 * @param cSeg Number of segments.
267 * @param off Start offset.
268 * @param cbWrite Number of bytes to record.
269 */
270static int drvdiskintWriteRecord(PDRVDISKINTEGRITY pThis, PCRTSGSEG paSeg, unsigned cSeg,
271 uint64_t off, size_t cbWrite)
272{
273 int rc = VINF_SUCCESS;
274
275 LogFlowFunc(("pThis=%#p paSeg=%#p cSeg=%u off=%llx cbWrite=%u\n",
276 pThis, paSeg, cSeg, off, cbWrite));
277
278 /* Update the segments */
279 size_t cbLeft = cbWrite;
280 RTFOFF offCurr = (RTFOFF)off;
281 RTSGBUF SgBuf;
282 PIOLOGENT pIoLogEnt = (PIOLOGENT)RTMemAllocZ(sizeof(IOLOGENT));
283 if (!pIoLogEnt)
284 return VERR_NO_MEMORY;
285
286 pIoLogEnt->off = off;
287 pIoLogEnt->cbWrite = cbWrite;
288 pIoLogEnt->cRefs = 0;
289
290 RTSgBufInit(&SgBuf, paSeg, cSeg);
291
292 while (cbLeft)
293 {
294 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr);
295 size_t cbRange = 0;
296 bool fSet = false;
297 unsigned offSeg = 0;
298
299 if (!pSeg)
300 {
301 /* Get next segment */
302 pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true);
303 if ( !pSeg
304 || offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key)
305 cbRange = cbLeft;
306 else
307 cbRange = pSeg->Core.Key - offCurr;
308
309 Assert(cbRange % 512 == 0);
310
311 /* Create new segment */
312 pSeg = (PDRVDISKSEGMENT)RTMemAllocZ(RT_OFFSETOF(DRVDISKSEGMENT, apIoLog[cbRange / 512]));
313 if (pSeg)
314 {
315 pSeg->Core.Key = offCurr;
316 pSeg->Core.KeyLast = offCurr + (RTFOFF)cbRange - 1;
317 pSeg->cbSeg = cbRange;
318 pSeg->pbSeg = (uint8_t *)RTMemAllocZ(cbRange);
319 pSeg->cIoLogEntries = (uint32_t)cbRange / 512;
320 if (!pSeg->pbSeg)
321 RTMemFree(pSeg);
322 else
323 {
324 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
325 AssertMsg(fInserted, ("Bug!\n")); RT_NOREF(fInserted);
326 fSet = true;
327 }
328 }
329 }
330 else
331 {
332 fSet = true;
333 offSeg = offCurr - pSeg->Core.Key;
334 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
335 }
336
337 if (fSet)
338 {
339 AssertPtr(pSeg);
340 size_t cbCopied = RTSgBufCopyToBuf(&SgBuf, pSeg->pbSeg + offSeg, cbRange);
341 Assert(cbCopied == cbRange); RT_NOREF(cbCopied);
342
343 /* Update the I/O log pointers */
344 Assert(offSeg % 512 == 0);
345 Assert(cbRange % 512 == 0);
346 while (offSeg < cbRange)
347 {
348 uint32_t uSector = offSeg / 512;
349 PIOLOGENT pIoLogOld = NULL;
350
351 AssertMsg(uSector < pSeg->cIoLogEntries, ("Internal bug!\n"));
352
353 pIoLogOld = pSeg->apIoLog[uSector];
354 if (pIoLogOld)
355 {
356 pIoLogOld->cRefs--;
357 if (!pIoLogOld->cRefs)
358 RTMemFree(pIoLogOld);
359 }
360
361 pSeg->apIoLog[uSector] = pIoLogEnt;
362 pIoLogEnt->cRefs++;
363
364 offSeg += 512;
365 }
366 }
367 else
368 RTSgBufAdvance(&SgBuf, cbRange);
369
370 offCurr += cbRange;
371 cbLeft -= cbRange;
372 }
373
374 return rc;
375}
376
377/**
378 * Verifies a read request.
379 *
380 * @returns VBox status code.
381 * @param pThis Disk integrity driver instance data.
382 * @param paSeg Segment array of the containing the data buffers to verify.
383 * @param cSeg Number of segments.
384 * @param off Start offset.
385 * @param cbRead Number of bytes to verify.
386 */
387static int drvdiskintReadVerify(PDRVDISKINTEGRITY pThis, PCRTSGSEG paSeg, unsigned cSeg,
388 uint64_t off, size_t cbRead)
389{
390 int rc = VINF_SUCCESS;
391
392 LogFlowFunc(("pThis=%#p paSeg=%#p cSeg=%u off=%llx cbRead=%u\n",
393 pThis, paSeg, cSeg, off, cbRead));
394
395 Assert(off % 512 == 0);
396 Assert(cbRead % 512 == 0);
397
398 /* Compare read data */
399 size_t cbLeft = cbRead;
400 RTFOFF offCurr = (RTFOFF)off;
401 RTSGBUF SgBuf;
402
403 RTSgBufInit(&SgBuf, paSeg, cSeg);
404
405 while (cbLeft)
406 {
407 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr);
408 size_t cbRange = 0;
409 bool fCmp = false;
410 unsigned offSeg = 0;
411
412 if (!pSeg)
413 {
414 /* Get next segment */
415 pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true);
416 if (!pSeg)
417 {
418 /* No data in the tree for this read. Assume everything is ok. */
419 cbRange = cbLeft;
420 }
421 else if (offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key)
422 cbRange = cbLeft;
423 else
424 cbRange = pSeg->Core.Key - offCurr;
425
426 if (pThis->fPrepopulateRamDisk)
427 {
428 /* No segment means everything should be 0 for this part. */
429 if (!RTSgBufIsZero(&SgBuf, cbRange))
430 {
431 RTMsgError("Corrupted disk at offset %llu (expected everything to be 0)!\n",
432 offCurr);
433 RTAssertDebugBreak();
434 }
435 }
436 }
437 else
438 {
439 fCmp = true;
440 offSeg = offCurr - pSeg->Core.Key;
441 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
442 }
443
444 if (fCmp)
445 {
446 RTSGSEG Seg;
447 RTSGBUF SgBufCmp;
448 size_t cbOff = 0;
449
450 Seg.cbSeg = cbRange;
451 Seg.pvSeg = pSeg->pbSeg + offSeg;
452
453 RTSgBufInit(&SgBufCmp, &Seg, 1);
454 if (RTSgBufCmpEx(&SgBuf, &SgBufCmp, cbRange, &cbOff, true))
455 {
456 /* Corrupted disk, print I/O log entry of the last write which accessed this range. */
457 uint32_t cSector = (offSeg + (uint32_t)cbOff) / 512;
458 AssertMsg(cSector < pSeg->cIoLogEntries, ("Internal bug!\n"));
459
460 RTMsgError("Corrupted disk at offset %llu (%u bytes in the current read buffer)!\n",
461 offCurr + cbOff, cbOff);
462 RTMsgError("Last write to this sector started at offset %llu with %u bytes (%u references to this log entry)\n",
463 pSeg->apIoLog[cSector]->off,
464 pSeg->apIoLog[cSector]->cbWrite,
465 pSeg->apIoLog[cSector]->cRefs);
466 RTAssertDebugBreak();
467 }
468 }
469 else
470 RTSgBufAdvance(&SgBuf, cbRange);
471
472 offCurr += cbRange;
473 cbLeft -= cbRange;
474 }
475
476 return rc;
477}
478
479/**
480 * Discards the given ranges from the disk.
481 *
482 * @returns VBox status code.
483 * @param pThis Disk integrity driver instance data.
484 * @param paRanges Array of ranges to discard.
485 * @param cRanges Number of ranges in the array.
486 */
487static int drvdiskintDiscardRecords(PDRVDISKINTEGRITY pThis, PCRTRANGE paRanges, unsigned cRanges)
488{
489 int rc = VINF_SUCCESS;
490
491 LogFlowFunc(("pThis=%#p paRanges=%#p cRanges=%u\n", pThis, paRanges, cRanges));
492
493 for (unsigned i = 0; i < cRanges; i++)
494 {
495 uint64_t offStart = paRanges[i].offStart;
496 size_t cbLeft = paRanges[i].cbRange;
497
498 LogFlowFunc(("Discarding off=%llu cbRange=%zu\n", offStart, cbLeft));
499
500 while (cbLeft)
501 {
502 size_t cbRange;
503 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offStart);
504
505 if (!pSeg)
506 {
507 /* Get next segment */
508 pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offStart, true);
509 if ( !pSeg
510 || (RTFOFF)offStart + (RTFOFF)cbLeft <= pSeg->Core.Key)
511 cbRange = cbLeft;
512 else
513 cbRange = pSeg->Core.Key - offStart;
514
515 Assert(!(cbRange % 512));
516 }
517 else
518 {
519 size_t cbPreLeft, cbPostLeft;
520
521 cbRange = RT_MIN(cbLeft, pSeg->Core.KeyLast - offStart + 1);
522 cbPreLeft = offStart - pSeg->Core.Key;
523 cbPostLeft = pSeg->cbSeg - cbRange - cbPreLeft;
524
525 Assert(!(cbRange % 512));
526 Assert(!(cbPreLeft % 512));
527 Assert(!(cbPostLeft % 512));
528
529 LogFlowFunc(("cbRange=%zu cbPreLeft=%zu cbPostLeft=%zu\n",
530 cbRange, cbPreLeft, cbPostLeft));
531
532 RTAvlrFileOffsetRemove(pThis->pTreeSegments, pSeg->Core.Key);
533
534 if (!cbPreLeft && !cbPostLeft)
535 {
536 /* Just free the whole segment. */
537 LogFlowFunc(("Freeing whole segment pSeg=%#p\n", pSeg));
538 RTMemFree(pSeg->pbSeg);
539 for (unsigned idx = 0; idx < pSeg->cIoLogEntries; idx++)
540 drvdiskintIoLogEntryRelease(pSeg->apIoLog[idx]);
541 RTMemFree(pSeg);
542 }
543 else if (cbPreLeft && !cbPostLeft)
544 {
545 /* Realloc to new size and insert. */
546 LogFlowFunc(("Realloc segment pSeg=%#p\n", pSeg));
547 pSeg->pbSeg = (uint8_t *)RTMemRealloc(pSeg->pbSeg, cbPreLeft);
548 for (unsigned idx = (uint32_t)(cbPreLeft / 512); idx < pSeg->cIoLogEntries; idx++)
549 drvdiskintIoLogEntryRelease(pSeg->apIoLog[idx]);
550 pSeg = (PDRVDISKSEGMENT)RTMemRealloc(pSeg, RT_OFFSETOF(DRVDISKSEGMENT, apIoLog[cbPreLeft / 512]));
551 pSeg->Core.KeyLast = pSeg->Core.Key + cbPreLeft - 1;
552 pSeg->cbSeg = cbPreLeft;
553 pSeg->cIoLogEntries = (uint32_t)(cbPreLeft / 512);
554 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
555 Assert(fInserted); RT_NOREF(fInserted);
556 }
557 else if (!cbPreLeft && cbPostLeft)
558 {
559 /* Move data to the front and realloc. */
560 LogFlowFunc(("Move data and realloc segment pSeg=%#p\n", pSeg));
561 memmove(pSeg->pbSeg, pSeg->pbSeg + cbRange, cbPostLeft);
562 for (unsigned idx = 0; idx < cbRange / 512; idx++)
563 drvdiskintIoLogEntryRelease(pSeg->apIoLog[idx]);
564 for (unsigned idx = 0; idx < cbPostLeft /512; idx++)
565 pSeg->apIoLog[idx] = pSeg->apIoLog[(cbRange / 512) + idx];
566 pSeg = (PDRVDISKSEGMENT)RTMemRealloc(pSeg, RT_OFFSETOF(DRVDISKSEGMENT, apIoLog[cbPostLeft / 512]));
567 pSeg->pbSeg = (uint8_t *)RTMemRealloc(pSeg->pbSeg, cbPostLeft);
568 pSeg->Core.Key += cbRange;
569 pSeg->cbSeg = cbPostLeft;
570 pSeg->cIoLogEntries = (uint32_t)(cbPostLeft / 512);
571 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
572 Assert(fInserted); RT_NOREF(fInserted);
573 }
574 else
575 {
576 /* Split the segment into 2 new segments. */
577 LogFlowFunc(("Split segment pSeg=%#p\n", pSeg));
578 PDRVDISKSEGMENT pSegPost = (PDRVDISKSEGMENT)RTMemAllocZ(RT_OFFSETOF(DRVDISKSEGMENT, apIoLog[cbPostLeft / 512]));
579 if (pSegPost)
580 {
581 pSegPost->Core.Key = pSeg->Core.Key + cbPreLeft + cbRange;
582 pSegPost->Core.KeyLast = pSeg->Core.KeyLast;
583 pSegPost->cbSeg = cbPostLeft;
584 pSegPost->pbSeg = (uint8_t *)RTMemAllocZ(cbPostLeft);
585 pSegPost->cIoLogEntries = (uint32_t)(cbPostLeft / 512);
586 if (!pSegPost->pbSeg)
587 RTMemFree(pSegPost);
588 else
589 {
590 memcpy(pSegPost->pbSeg, pSeg->pbSeg + cbPreLeft + cbRange, cbPostLeft);
591 for (unsigned idx = 0; idx < (uint32_t)(cbPostLeft / 512); idx++)
592 pSegPost->apIoLog[idx] = pSeg->apIoLog[((cbPreLeft + cbRange) / 512) + idx];
593
594 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSegPost->Core);
595 Assert(fInserted); RT_NOREF(fInserted);
596 }
597 }
598
599 /* Shrink the current segment. */
600 pSeg->pbSeg = (uint8_t *)RTMemRealloc(pSeg->pbSeg, cbPreLeft);
601 for (unsigned idx = (uint32_t)(cbPreLeft / 512); idx < (uint32_t)((cbPreLeft + cbRange) / 512); idx++)
602 drvdiskintIoLogEntryRelease(pSeg->apIoLog[idx]);
603 pSeg = (PDRVDISKSEGMENT)RTMemRealloc(pSeg, RT_OFFSETOF(DRVDISKSEGMENT, apIoLog[cbPreLeft / 512]));
604 pSeg->Core.KeyLast = pSeg->Core.Key + cbPreLeft - 1;
605 pSeg->cbSeg = cbPreLeft;
606 pSeg->cIoLogEntries = (uint32_t)(cbPreLeft / 512);
607 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
608 Assert(fInserted); RT_NOREF(fInserted);
609 } /* if (cbPreLeft && cbPostLeft) */
610 }
611
612 offStart += cbRange;
613 cbLeft -= cbRange;
614 }
615 }
616
617 LogFlowFunc(("returns rc=%Rrc\n", rc));
618 return rc;
619}
620
621/**
622 * Adds a request to the active list.
623 *
624 * @returns nothing.
625 * @param pThis The driver instance data.
626 * @param pIoReq The request to add.
627 */
628static void drvdiskintIoReqAdd(PDRVDISKINTEGRITY pThis, PDRVDISKAIOREQ pIoReq)
629{
630 PDRVDISKAIOREQACTIVE pReqActive = &pThis->apReqActive[pThis->iNextFreeSlot];
631
632 Assert(!pReqActive->pIoReq);
633 pReqActive->tsStart = pIoReq->tsStart;
634 pReqActive->pIoReq = pIoReq;
635 pIoReq->iSlot = pThis->iNextFreeSlot;
636
637 /* Search for the next one. */
638 while (pThis->apReqActive[pThis->iNextFreeSlot].pIoReq)
639 pThis->iNextFreeSlot = (pThis->iNextFreeSlot+1) % RT_ELEMENTS(pThis->apReqActive);
640}
641
642/**
643 * Removes a request from the active list.
644 *
645 * @returns nothing.
646 * @param pThis The driver instance data.
647 * @param pIoReq The request to remove.
648 */
649static void drvdiskintIoReqRemove(PDRVDISKINTEGRITY pThis, PDRVDISKAIOREQ pIoReq)
650{
651 PDRVDISKAIOREQACTIVE pReqActive = &pThis->apReqActive[pIoReq->iSlot];
652
653 Assert(pReqActive->pIoReq == pIoReq);
654
655 ASMAtomicWriteNullPtr(&pReqActive->pIoReq);
656}
657
658/**
659 * Thread checking for expired requests.
660 *
661 * @returns IPRT status code.
662 * @param pThread Thread handle.
663 * @param pvUser Opaque user data.
664 */
665static DECLCALLBACK(int) drvdiskIntIoReqExpiredCheck(RTTHREAD pThread, void *pvUser)
666{
667 PDRVDISKINTEGRITY pThis = (PDRVDISKINTEGRITY)pvUser;
668
669 RT_NOREF(pThread);
670
671 while (pThis->fRunning)
672 {
673 int rc = RTSemEventWait(pThis->SemEvent, pThis->uCheckIntervalMs);
674
675 if (!pThis->fRunning)
676 break;
677
678 Assert(rc == VERR_TIMEOUT); RT_NOREF(rc);
679
680 /* Get current timestamp for comparison. */
681 uint64_t tsCurr = RTTimeSystemMilliTS();
682
683 /* Go through the array and check for expired requests. */
684 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apReqActive); i++)
685 {
686 PDRVDISKAIOREQACTIVE pReqActive = &pThis->apReqActive[i];
687 PDRVDISKAIOREQ pIoReq = ASMAtomicReadPtrT(&pReqActive->pIoReq, PDRVDISKAIOREQ);
688
689 if ( pIoReq
690 && (tsCurr > pReqActive->tsStart)
691 && (tsCurr - pReqActive->tsStart) >= pThis->uExpireIntervalMs)
692 {
693 RTMsgError("Request %#p expired (active for %llu ms already)\n",
694 pIoReq, tsCurr - pReqActive->tsStart);
695 RTAssertDebugBreak();
696 }
697 }
698 }
699
700 return VINF_SUCCESS;
701}
702
703/**
704 * Verify a completed read after write request.
705 *
706 * @returns VBox status code.
707 * @param pThis The driver instance data.
708 * @param pIoReq The request to be verified.
709 */
710static int drvdiskintReadAfterWriteVerify(PDRVDISKINTEGRITY pThis, PDRVDISKAIOREQ pIoReq)
711{
712 int rc = VINF_SUCCESS;
713
714 if (pThis->fCheckConsistency)
715 rc = drvdiskintReadVerify(pThis, pIoReq->paSeg, pIoReq->cSeg, pIoReq->off, pIoReq->cbTransfer);
716 else /** @todo Implement read after write verification without a memory based image of the disk. */
717 AssertMsgFailed(("TODO\n"));
718
719 return rc;
720}
721
722/* -=-=-=-=- IMedia -=-=-=-=- */
723
724/** Makes a PDRVDISKINTEGRITY out of a PPDMIMEDIA. */
725#define PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY)((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMedia)) )
726
727
728/*********************************************************************************************************************************
729* Media interface methods *
730*********************************************************************************************************************************/
731
732/** @interface_method_impl{PDMIMEDIA,pfnRead} */
733static DECLCALLBACK(int) drvdiskintRead(PPDMIMEDIA pInterface,
734 uint64_t off, void *pvBuf, size_t cbRead)
735{
736 int rc = VINF_SUCCESS;
737 VDIOLOGENT hIoLogEntry = NULL;
738 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
739
740 if (pThis->hIoLogger)
741 {
742 rc = VDDbgIoLogStart(pThis->hIoLogger, false, VDDBGIOLOGREQ_READ, off,
743 cbRead, NULL, &hIoLogEntry);
744 AssertRC(rc);
745 }
746
747 rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, off, pvBuf, cbRead);
748
749 if (pThis->hIoLogger)
750 {
751 RTSGSEG Seg;
752 RTSGBUF SgBuf;
753
754 Seg.pvSeg = pvBuf;
755 Seg.cbSeg = cbRead;
756 RTSgBufInit(&SgBuf, &Seg, 1);
757
758 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, hIoLogEntry, rc, &SgBuf);
759 AssertRC(rc2);
760 }
761
762 if (RT_FAILURE(rc))
763 return rc;
764
765 if (pThis->fCheckConsistency)
766 {
767 /* Verify the read. */
768 RTSGSEG Seg;
769 Seg.cbSeg = cbRead;
770 Seg.pvSeg = pvBuf;
771 rc = drvdiskintReadVerify(pThis, &Seg, 1, off, cbRead);
772 }
773
774 return rc;
775}
776
777/** @interface_method_impl{PDMIMEDIA,pfnWrite} */
778static DECLCALLBACK(int) drvdiskintWrite(PPDMIMEDIA pInterface,
779 uint64_t off, const void *pvBuf,
780 size_t cbWrite)
781{
782 int rc = VINF_SUCCESS;
783 VDIOLOGENT hIoLogEntry = NULL;
784 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
785
786 if (pThis->hIoLogger)
787 {
788 RTSGSEG Seg;
789 RTSGBUF SgBuf;
790
791 Seg.pvSeg = (void *)pvBuf;
792 Seg.cbSeg = cbWrite;
793 RTSgBufInit(&SgBuf, &Seg, 1);
794
795 rc = VDDbgIoLogStart(pThis->hIoLogger, false, VDDBGIOLOGREQ_WRITE, off,
796 cbWrite, &SgBuf, &hIoLogEntry);
797 AssertRC(rc);
798 }
799
800 if (pThis->fRecordWriteBeforeCompletion)
801 {
802 RTSGSEG Seg;
803 Seg.cbSeg = cbWrite;
804 Seg.pvSeg = (void *)pvBuf;
805
806 rc = drvdiskintWriteRecord(pThis, &Seg, 1, off, cbWrite);
807 if (RT_FAILURE(rc))
808 return rc;
809 }
810
811 rc = pThis->pDrvMedia->pfnWrite(pThis->pDrvMedia, off, pvBuf, cbWrite);
812
813 if (pThis->hIoLogger)
814 {
815 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, hIoLogEntry, rc, NULL);
816 AssertRC(rc2);
817 }
818
819 if (RT_FAILURE(rc))
820 return rc;
821
822 if ( pThis->fCheckConsistency
823 && !pThis->fRecordWriteBeforeCompletion)
824 {
825 /* Record the write. */
826 RTSGSEG Seg;
827 Seg.cbSeg = cbWrite;
828 Seg.pvSeg = (void *)pvBuf;
829 rc = drvdiskintWriteRecord(pThis, &Seg, 1, off, cbWrite);
830 }
831
832 return rc;
833}
834
835/** @interface_method_impl{PDMIMEDIA,pfnFlush} */
836static DECLCALLBACK(int) drvdiskintFlush(PPDMIMEDIA pInterface)
837{
838 int rc = VINF_SUCCESS;
839 VDIOLOGENT hIoLogEntry = NULL;
840 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
841
842 if (pThis->hIoLogger)
843 {
844 rc = VDDbgIoLogStart(pThis->hIoLogger, false, VDDBGIOLOGREQ_FLUSH, 0,
845 0, NULL, &hIoLogEntry);
846 AssertRC(rc);
847 }
848
849 rc = pThis->pDrvMedia->pfnFlush(pThis->pDrvMedia);
850
851 if (pThis->hIoLogger)
852 {
853 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, hIoLogEntry, rc, NULL);
854 AssertRC(rc2);
855 }
856
857 return rc;
858}
859
860/** @interface_method_impl{PDMIMEDIA,pfnGetSize} */
861static DECLCALLBACK(uint64_t) drvdiskintGetSize(PPDMIMEDIA pInterface)
862{
863 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
864 return pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
865}
866
867/** @interface_method_impl{PDMIMEDIA,pfnIsReadOnly} */
868static DECLCALLBACK(bool) drvdiskintIsReadOnly(PPDMIMEDIA pInterface)
869{
870 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
871 return pThis->pDrvMedia->pfnIsReadOnly(pThis->pDrvMedia);
872}
873
874/** @interface_method_impl{PDMIMEDIA,pfnBiosIsVisible} */
875static DECLCALLBACK(bool) drvdiskintBiosIsVisible(PPDMIMEDIA pInterface)
876{
877 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
878 return pThis->pDrvMedia->pfnBiosIsVisible(pThis->pDrvMedia);
879}
880
881/** @interface_method_impl{PDMIMEDIA,pfnGetType} */
882static DECLCALLBACK(PDMMEDIATYPE) drvdiskintGetType(PPDMIMEDIA pInterface)
883{
884 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
885 return pThis->pDrvMedia->pfnGetType(pThis->pDrvMedia);
886}
887
888/** @interface_method_impl{PDMIMEDIA,pfnBiosGetPCHSGeometry} */
889static DECLCALLBACK(int) drvdiskintBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
890 PPDMMEDIAGEOMETRY pPCHSGeometry)
891{
892 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
893 return pThis->pDrvMedia->pfnBiosGetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
894}
895
896/** @interface_method_impl{PDMIMEDIA,pfnBiosSetPCHSGeometry} */
897static DECLCALLBACK(int) drvdiskintBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
898 PCPDMMEDIAGEOMETRY pPCHSGeometry)
899{
900 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
901 return pThis->pDrvMedia->pfnBiosSetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
902}
903
904/** @interface_method_impl{PDMIMEDIA,pfnBiosGetLCHSGeometry} */
905static DECLCALLBACK(int) drvdiskintBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
906 PPDMMEDIAGEOMETRY pLCHSGeometry)
907{
908 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
909 return pThis->pDrvMedia->pfnBiosGetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
910}
911
912/** @interface_method_impl{PDMIMEDIA,pfnBiosSetLCHSGeometry} */
913static DECLCALLBACK(int) drvdiskintBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
914 PCPDMMEDIAGEOMETRY pLCHSGeometry)
915{
916 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
917 return pThis->pDrvMedia->pfnBiosSetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
918}
919
920/** @interface_method_impl{PDMIMEDIA,pfnGetUuid} */
921static DECLCALLBACK(int) drvdiskintGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
922{
923 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
924 return pThis->pDrvMedia->pfnGetUuid(pThis->pDrvMedia, pUuid);
925}
926
927/** @interface_method_impl{PDMIMEDIA,pfnGetSectorSize} */
928static DECLCALLBACK(uint32_t) drvdiskintGetSectorSize(PPDMIMEDIA pInterface)
929{
930 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
931 return pThis->pDrvMedia->pfnGetSectorSize(pThis->pDrvMedia);
932}
933
934/** @interface_method_impl{PDMIMEDIA,pfnDiscard} */
935static DECLCALLBACK(int) drvdiskintDiscard(PPDMIMEDIA pInterface, PCRTRANGE paRanges, unsigned cRanges)
936{
937 int rc = VINF_SUCCESS;
938 VDIOLOGENT hIoLogEntry = NULL;
939 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
940
941 if (pThis->hIoLogger)
942 {
943 rc = VDDbgIoLogStartDiscard(pThis->hIoLogger, false, paRanges, cRanges, &hIoLogEntry);
944 AssertRC(rc);
945 }
946
947 rc = pThis->pDrvMedia->pfnDiscard(pThis->pDrvMedia, paRanges, cRanges);
948
949 if (pThis->hIoLogger)
950 {
951 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, hIoLogEntry, rc, NULL);
952 AssertRC(rc2);
953 }
954
955 if (pThis->fCheckConsistency)
956 rc = drvdiskintDiscardRecords(pThis, paRanges, cRanges);
957
958 return rc;
959}
960
961/** @interface_method_impl{PDMIMEDIA,pfnReadPcBios} */
962static DECLCALLBACK(int) drvdiskintReadPcBios(PPDMIMEDIA pInterface,
963 uint64_t off, void *pvBuf, size_t cbRead)
964{
965 LogFlowFunc(("\n"));
966 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
967
968 return pThis->pDrvMedia->pfnReadPcBios(pThis->pDrvMedia, off, pvBuf, cbRead);
969}
970
971/** @interface_method_impl{PDMIMEDIA,pfnIsNonRotational} */
972static DECLCALLBACK(bool) drvdiskintIsNonRotational(PPDMIMEDIA pInterface)
973{
974 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
975 return pThis->pDrvMedia->pfnIsNonRotational(pThis->pDrvMedia);
976}
977
978/* -=-=-=-=- IMediaPort -=-=-=-=- */
979
980/** Makes a PDRVBLOCK out of a PPDMIMEDIAPORT. */
981#define PDMIMEDIAPORT_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMediaPort))) )
982
983/**
984 * @interface_method_impl{PDMIMEDIAPORT,pfnQueryDeviceLocation}
985 */
986static DECLCALLBACK(int) drvdiskintQueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
987 uint32_t *piInstance, uint32_t *piLUN)
988{
989 PDRVDISKINTEGRITY pThis = PDMIMEDIAPORT_2_DRVDISKINTEGRITY(pInterface);
990
991 return pThis->pDrvMediaPort->pfnQueryDeviceLocation(pThis->pDrvMediaPort, ppcszController,
992 piInstance, piLUN);
993}
994
995/* -=-=-=-=- IMediaExPort -=-=-=-=- */
996
997/**
998 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
999 */
1000static DECLCALLBACK(int) drvdiskintIoReqCompleteNotify(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1001 void *pvIoReqAlloc, int rcReq)
1002{
1003 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaExPort);
1004 PDRVDISKAIOREQ pIoReq = DISKINTEGRITY_IOREQ_ALLOC_2_DRVDISKAIOREQ(pvIoReqAlloc);
1005 int rc = VINF_SUCCESS;
1006
1007 LogFlowFunc(("pIoReq=%#p\n", pIoReq));
1008
1009 /* Remove from the active list. */
1010 if (pThis->fTraceRequests)
1011 drvdiskintIoReqRemove(pThis, pIoReq);
1012
1013 if (RT_SUCCESS(rcReq) && pThis->fCheckConsistency)
1014 {
1015 if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_READ)
1016 rc = drvdiskintReadVerify(pThis, &pIoReq->IoSeg, 1, pIoReq->off, pIoReq->cbTransfer);
1017 else if ( pIoReq->enmTxDir == DRVDISKAIOTXDIR_WRITE
1018 && !pThis->fRecordWriteBeforeCompletion)
1019 rc = drvdiskintWriteRecord(pThis, &pIoReq->IoSeg, 1, pIoReq->off, pIoReq->cbTransfer);
1020 else if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_DISCARD)
1021 rc = drvdiskintDiscardRecords(pThis, pIoReq->paRanges, pIoReq->cRanges);
1022 else if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_READ_AFTER_WRITE)
1023 rc = drvdiskintReadAfterWriteVerify(pThis, pIoReq);
1024 else
1025 AssertMsg( pIoReq->enmTxDir == DRVDISKAIOTXDIR_FLUSH
1026 || ( pIoReq->enmTxDir == DRVDISKAIOTXDIR_WRITE
1027 && pThis->fRecordWriteBeforeCompletion), ("Huh?\n"));
1028
1029 AssertRC(rc);
1030 }
1031
1032 if ( RT_SUCCESS(rcReq)
1033 && pThis->fValidateMemBufs
1034 && pIoReq->enmTxDir == DRVDISKAIOTXDIR_READ)
1035 {
1036 /* Check that the guest memory buffer matches what was written. */
1037 RTSGSEG SegCmp;
1038 SegCmp.pvSeg = RTMemAlloc(pIoReq->cbTransfer);
1039 SegCmp.cbSeg = pIoReq->cbTransfer;
1040
1041 RTSGBUF SgBufCmp;
1042 RTSgBufInit(&SgBufCmp, &SegCmp, 1);
1043 rc = pThis->pDrvMediaExPort->pfnIoReqCopyToBuf(pThis->pDrvMediaExPort, hIoReq,
1044 DISKINTEGRITY_IOREQ_ALLOC_2_UPPER(pvIoReqAlloc),
1045 0, &SgBufCmp, pIoReq->cbTransfer);
1046 AssertRC(rc);
1047
1048 RTSGBUF SgBuf;
1049 RTSgBufInit(&SgBuf, &pIoReq->IoSeg, 1);
1050 if (RTSgBufCmp(&SgBuf, &SgBufCmp, pIoReq->cbTransfer))
1051 {
1052 RTMsgError("Corrupted memory buffer at offset %llu!\n", 0);
1053 RTAssertDebugBreak();
1054 }
1055
1056 RTMemFree(SegCmp.pvSeg);
1057 }
1058
1059 if (pThis->hIoLogger)
1060 {
1061 RTSGBUF SgBuf;
1062
1063 if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_READ)
1064 RTSgBufInit(&SgBuf, &pIoReq->IoSeg, 1);
1065
1066 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, pIoReq->hIoLogEntry, rc, &SgBuf);
1067 AssertRC(rc2);
1068 }
1069
1070 if ( pThis->fReadAfterWrite
1071 && pIoReq->enmTxDir == DRVDISKAIOTXDIR_WRITE)
1072 {
1073#if 0 /** @todo */
1074 pIoReq->enmTxDir = DRVDISKAIOTXDIR_READ_AFTER_WRITE;
1075
1076 /* Add again because it was removed above. */
1077 if (pThis->fTraceRequests)
1078 drvdiskintIoReqAdd(pThis, pIoReq);
1079
1080 rc = pThis->pDrvMediaAsync->pfnStartRead(pThis->pDrvMediaAsync, pIoReq->off, pIoReq->paSeg, pIoReq->cSeg,
1081 pIoReq->cbTransfer, pIoReq);
1082 if (rc == VINF_VD_ASYNC_IO_FINISHED)
1083 {
1084 rc = drvdiskintReadAfterWriteVerify(pThis, pIoReq);
1085
1086 if (pThis->fTraceRequests)
1087 drvdiskintIoReqRemove(pThis, pIoReq);
1088 RTMemFree(pIoReq);
1089 }
1090 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1091 rc = VINF_SUCCESS;
1092 else if (RT_FAILURE(rc))
1093 RTMemFree(pIoReq);
1094#endif
1095 }
1096 else
1097 {
1098 rc = pThis->pDrvMediaExPort->pfnIoReqCompleteNotify(pThis->pDrvMediaExPort, hIoReq,
1099 DISKINTEGRITY_IOREQ_ALLOC_2_UPPER(pvIoReqAlloc),
1100 rcReq);
1101 /* Put on the watch list. */
1102 if (pThis->fCheckDoubleCompletion)
1103 drvdiskintIoReqCheckForDoubleCompletion(pThis, pIoReq, true /* fMediaEx */);
1104 }
1105
1106 return rc;
1107}
1108
1109/**
1110 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyFromBuf}
1111 */
1112static DECLCALLBACK(int) drvdiskintIoReqCopyFromBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1113 void *pvIoReqAlloc, uint32_t offDst, PRTSGBUF pSgBuf,
1114 size_t cbCopy)
1115{
1116 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaExPort);
1117 PDRVDISKAIOREQ pIoReq = DISKINTEGRITY_IOREQ_ALLOC_2_DRVDISKAIOREQ(pvIoReqAlloc);
1118 RTSGBUF SgBuf;
1119
1120 RTSgBufClone(&SgBuf, pSgBuf);
1121
1122 int rc = pThis->pDrvMediaExPort->pfnIoReqCopyFromBuf(pThis->pDrvMediaExPort, hIoReq,
1123 DISKINTEGRITY_IOREQ_ALLOC_2_UPPER(pvIoReqAlloc),
1124 offDst, pSgBuf, cbCopy);
1125 if ( RT_SUCCESS(rc)
1126 && pIoReq->IoSeg.pvSeg)
1127 {
1128 /* Update our copy. */
1129 RTSgBufCopyToBuf(&SgBuf, (uint8_t *)pIoReq->IoSeg.pvSeg + offDst, cbCopy);
1130
1131 /* Validate the just read data against our copy if possible. */
1132 if ( pThis->fValidateMemBufs
1133 && pThis->fCheckConsistency
1134 && pIoReq->enmTxDir == DRVDISKAIOTXDIR_READ)
1135 {
1136 RTSGSEG Seg;
1137
1138 Seg.pvSeg = (uint8_t *)pIoReq->IoSeg.pvSeg + offDst;
1139 Seg.cbSeg = cbCopy;
1140
1141 rc = drvdiskintReadVerify(pThis, &Seg, 1, pIoReq->off + offDst,
1142 cbCopy);
1143 }
1144 }
1145
1146 return rc;
1147}
1148
1149/**
1150 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyToBuf}
1151 */
1152static DECLCALLBACK(int) drvdiskintIoReqCopyToBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1153 void *pvIoReqAlloc, uint32_t offSrc, PRTSGBUF pSgBuf,
1154 size_t cbCopy)
1155{
1156 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaExPort);
1157 PDRVDISKAIOREQ pIoReq = DISKINTEGRITY_IOREQ_ALLOC_2_DRVDISKAIOREQ(pvIoReqAlloc);
1158 RTSGBUF SgBuf;
1159
1160 RTSgBufClone(&SgBuf, pSgBuf);
1161
1162 int rc = pThis->pDrvMediaExPort->pfnIoReqCopyToBuf(pThis->pDrvMediaExPort, hIoReq,
1163 DISKINTEGRITY_IOREQ_ALLOC_2_UPPER(pvIoReqAlloc),
1164 offSrc, pSgBuf, cbCopy);
1165 if ( RT_SUCCESS(rc)
1166 && pIoReq->IoSeg.pvSeg)
1167 {
1168 if (pThis->fValidateMemBufs)
1169 {
1170 /* Make sure what the caller requested matches what we got earlier. */
1171 RTSGBUF SgBufCmp;
1172 RTSgBufInit(&SgBufCmp, &pIoReq->IoSeg, 1);
1173 RTSgBufAdvance(&SgBufCmp, offSrc);
1174
1175 if (RTSgBufCmp(&SgBuf, &SgBufCmp, cbCopy))
1176 {
1177 RTMsgError("Corrupted memory buffer at offset %llu!\n", offSrc);
1178 RTAssertDebugBreak();
1179 }
1180 }
1181 else
1182 {
1183 /* Update our copy. */
1184 RTSgBufCopyToBuf(&SgBuf, (uint8_t *)pIoReq->IoSeg.pvSeg + offSrc, cbCopy);
1185 }
1186 }
1187
1188 return rc;
1189}
1190
1191/**
1192 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqQueryDiscardRanges}
1193 */
1194static DECLCALLBACK(int) drvdiskintIoReqQueryDiscardRanges(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1195 void *pvIoReqAlloc, uint32_t idxRangeStart,
1196 uint32_t cRanges, PRTRANGE paRanges,
1197 uint32_t *pcRanges)
1198{
1199 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaExPort);
1200 return pThis->pDrvMediaExPort->pfnIoReqQueryDiscardRanges(pThis->pDrvMediaExPort, hIoReq,
1201 DISKINTEGRITY_IOREQ_ALLOC_2_UPPER(pvIoReqAlloc),
1202 idxRangeStart, cRanges, paRanges, pcRanges);
1203}
1204
1205/**
1206 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
1207 */
1208static DECLCALLBACK(void) drvdiskintIoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
1209 void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
1210{
1211 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaExPort);
1212 pThis->pDrvMediaExPort->pfnIoReqStateChanged(pThis->pDrvMediaExPort, hIoReq,
1213 DISKINTEGRITY_IOREQ_ALLOC_2_UPPER(pvIoReqAlloc),
1214 enmState);
1215}
1216
1217/* -=-=-=-=- IMediaEx -=-=-=-=- */
1218
1219/**
1220 * @interface_method_impl{PDMIMEDIAEX,pfnQueryFeatures}
1221 */
1222static DECLCALLBACK(int) drvdiskintQueryFeatures(PPDMIMEDIAEX pInterface, uint32_t *pfFeatures)
1223{
1224 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1225 return pThis->pDrvMediaEx->pfnQueryFeatures(pThis->pDrvMediaEx, pfFeatures);
1226}
1227
1228/**
1229 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqAllocSizeSet}
1230 */
1231static DECLCALLBACK(int) drvdiskintIoReqAllocSizeSet(PPDMIMEDIAEX pInterface, size_t cbIoReqAlloc)
1232{
1233 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1234
1235 /* Increase the amount by the size of a pointer to our private tracking structure. */
1236 cbIoReqAlloc += sizeof(PDRVDISKAIOREQ);
1237
1238 pThis->fCheckDoubleCompletion = false;
1239
1240 return pThis->pDrvMediaEx->pfnIoReqAllocSizeSet(pThis->pDrvMediaEx, cbIoReqAlloc);
1241}
1242
1243/**
1244 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqAlloc}
1245 */
1246static DECLCALLBACK(int) drvdiskintIoReqAlloc(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq, void **ppvIoReqAlloc,
1247 PDMMEDIAEXIOREQID uIoReqId, uint32_t fFlags)
1248{
1249 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1250 int rc = VINF_SUCCESS;
1251 PDRVDISKAIOREQ pIoReq = (PDRVDISKAIOREQ)RTMemCacheAlloc(pThis->hReqCache);
1252 if (RT_LIKELY(pIoReq))
1253 {
1254 pIoReq->enmTxDir = DRVDISKAIOTXDIR_INVALID;
1255 pIoReq->off = 0;
1256 pIoReq->cbTransfer = 0;
1257 pIoReq->paSeg = NULL;
1258 pIoReq->cSeg = 0;
1259 pIoReq->pvUser = NULL;
1260 pIoReq->iSlot = 0;
1261 pIoReq->tsStart = 0;
1262 pIoReq->tsComplete = 0;
1263 pIoReq->hIoLogEntry = NULL;
1264 pIoReq->IoSeg.pvSeg = NULL;
1265 pIoReq->IoSeg.cbSeg = 0;
1266
1267 PDRVDISKAIOREQ *ppIoReq = NULL;
1268 rc = pThis->pDrvMediaEx->pfnIoReqAlloc(pThis->pDrvMediaEx, phIoReq, (void **)&ppIoReq, uIoReqId, fFlags);
1269 if RT_SUCCESS(rc)
1270 {
1271 /*
1272 * Store the size off the start of our tracking structure because it is
1273 * required to access it for the read/write callbacks.
1274 *
1275 * ASSUMPTION that the offset is constant.
1276 */
1277 if (!pThis->cbIoReqOpaque)
1278 pThis->cbIoReqOpaque = (uintptr_t)ppIoReq - (uintptr_t)*phIoReq;
1279 else
1280 Assert(pThis->cbIoReqOpaque == (uintptr_t)ppIoReq - (uintptr_t)*phIoReq);
1281
1282 *ppIoReq = pIoReq;
1283 *ppvIoReqAlloc = ((uint8_t *)ppIoReq) + sizeof(PDRVDISKAIOREQ);
1284 }
1285 else
1286 RTMemCacheFree(pThis->hReqCache, pIoReq);
1287 }
1288 else
1289 rc = VERR_NO_MEMORY;
1290
1291 return rc;
1292}
1293
1294/**
1295 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqFree}
1296 */
1297static DECLCALLBACK(int) drvdiskintIoReqFree(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
1298{
1299 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1300 PDRVDISKAIOREQ pIoReq = DISKINTEGRITY_IOREQ_HANDLE_2_DRVDISKAIOREQ(pThis, hIoReq);
1301
1302 if (pIoReq->IoSeg.pvSeg)
1303 RTMemFree(pIoReq->IoSeg.pvSeg);
1304
1305 return pThis->pDrvMediaEx->pfnIoReqFree(pThis->pDrvMediaEx, hIoReq);
1306}
1307
1308/**
1309 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQueryResidual}
1310 */
1311static DECLCALLBACK(int) drvdiskintIoReqQueryResidual(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, size_t *pcbResidual)
1312{
1313 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1314 return pThis->pDrvMediaEx->pfnIoReqQueryResidual(pThis->pDrvMediaEx, hIoReq, pcbResidual);
1315}
1316
1317/**
1318 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQueryXferSize}
1319 */
1320static DECLCALLBACK(int) drvdiskintIoReqQueryXferSize(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, size_t *pcbXfer)
1321{
1322 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1323 return pThis->pDrvMediaEx->pfnIoReqQueryXferSize(pThis->pDrvMediaEx, hIoReq, pcbXfer);
1324}
1325
1326/**
1327 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancelAll}
1328 */
1329static DECLCALLBACK(int) drvdiskintIoReqCancelAll(PPDMIMEDIAEX pInterface)
1330{
1331 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1332 return pThis->pDrvMediaEx->pfnIoReqCancelAll(pThis->pDrvMediaEx);
1333}
1334
1335/**
1336 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancel}
1337 */
1338static DECLCALLBACK(int) drvdiskintIoReqCancel(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQID uIoReqId)
1339{
1340 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1341 return pThis->pDrvMediaEx->pfnIoReqCancel(pThis->pDrvMediaEx, uIoReqId);
1342}
1343
1344/**
1345 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqRead}
1346 */
1347static DECLCALLBACK(int) drvdiskintIoReqRead(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbRead)
1348{
1349 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1350 PDRVDISKAIOREQ pIoReq = DISKINTEGRITY_IOREQ_HANDLE_2_DRVDISKAIOREQ(pThis, hIoReq);
1351
1352 pIoReq->enmTxDir = DRVDISKAIOTXDIR_READ;
1353 pIoReq->off = off;
1354 pIoReq->cbTransfer = cbRead;
1355
1356 /* Allocate a I/O buffer if the I/O is verified.*/
1357 if (pThis->fCheckConsistency)
1358 {
1359 pIoReq->IoSeg.pvSeg = RTMemAlloc(cbRead);
1360 pIoReq->IoSeg.cbSeg = cbRead;
1361 }
1362
1363 if (pThis->fTraceRequests)
1364 drvdiskintIoReqAdd(pThis, pIoReq);
1365
1366 if (pThis->hIoLogger)
1367 {
1368 int rc2 = VDDbgIoLogStart(pThis->hIoLogger, true, VDDBGIOLOGREQ_READ, off,
1369 cbRead, NULL, &pIoReq->hIoLogEntry);
1370 AssertRC(rc2);
1371 }
1372
1373 int rc = pThis->pDrvMediaEx->pfnIoReqRead(pThis->pDrvMediaEx, hIoReq, off, cbRead);
1374 if (rc == VINF_SUCCESS)
1375 {
1376 /* Verify the read now. */
1377 if (pThis->fCheckConsistency)
1378 {
1379 int rc2 = drvdiskintReadVerify(pThis, &pIoReq->IoSeg, 1, off, cbRead);
1380 AssertRC(rc2);
1381 }
1382
1383 if (pThis->hIoLogger)
1384 {
1385 RTSGBUF SgBuf;
1386
1387 RTSgBufInit(&SgBuf, &pIoReq->IoSeg, 1);
1388
1389 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, pIoReq->hIoLogEntry, VINF_SUCCESS, &SgBuf);
1390 AssertRC(rc2);
1391 }
1392
1393 if (pThis->fTraceRequests)
1394 drvdiskintIoReqRemove(pThis, pIoReq);
1395 }
1396
1397 LogFlowFunc(("returns %Rrc\n", rc));
1398 return rc;
1399}
1400
1401/**
1402 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqWrite}
1403 */
1404static DECLCALLBACK(int) drvdiskintIoReqWrite(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbWrite)
1405{
1406 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1407 PDRVDISKAIOREQ pIoReq = DISKINTEGRITY_IOREQ_HANDLE_2_DRVDISKAIOREQ(pThis, hIoReq);
1408
1409 pIoReq->enmTxDir = DRVDISKAIOTXDIR_WRITE;
1410 pIoReq->off = off;
1411 pIoReq->cbTransfer = cbWrite;
1412
1413 /* Allocate a I/O buffer if the I/O is verified.*/
1414 if ( pThis->fCheckConsistency
1415 || pThis->fValidateMemBufs
1416 || pThis->hIoLogger
1417 || pThis->fRecordWriteBeforeCompletion)
1418 {
1419 pIoReq->IoSeg.pvSeg = RTMemAlloc(cbWrite);
1420 pIoReq->IoSeg.cbSeg = cbWrite;
1421
1422 /* Sync the memory buffer over if we should validate it. */
1423 if ( pThis->fValidateMemBufs
1424 || pThis->hIoLogger
1425 || pThis->fRecordWriteBeforeCompletion)
1426 {
1427 RTSGBUF SgBuf;
1428
1429 RTSgBufInit(&SgBuf, &pIoReq->IoSeg, 1);
1430 int rc2 = pThis->pDrvMediaExPort->pfnIoReqCopyToBuf(pThis->pDrvMediaExPort, hIoReq,
1431 DISKINTEGRITY_IOREQ_HANDLE_2_UPPER_OPAQUE(pThis, hIoReq),
1432 0, &SgBuf, cbWrite);
1433 AssertRC(rc2);
1434 }
1435 }
1436
1437 if (pThis->fTraceRequests)
1438 drvdiskintIoReqAdd(pThis, pIoReq);
1439
1440 if (pThis->hIoLogger)
1441 {
1442 RTSGBUF SgBuf;
1443
1444 RTSgBufInit(&SgBuf, &pIoReq->IoSeg, 1);
1445 int rc2 = VDDbgIoLogStart(pThis->hIoLogger, true, VDDBGIOLOGREQ_WRITE, off,
1446 cbWrite, &SgBuf, &pIoReq->hIoLogEntry);
1447 AssertRC(rc2);
1448 }
1449
1450 if (pThis->fRecordWriteBeforeCompletion)
1451 {
1452
1453 int rc2 = drvdiskintWriteRecord(pThis, &pIoReq->IoSeg, 1, off, cbWrite);
1454 AssertRC(rc2);
1455 }
1456
1457 int rc = pThis->pDrvMediaEx->pfnIoReqWrite(pThis->pDrvMediaEx, hIoReq, off, cbWrite);
1458 if (rc == VINF_SUCCESS)
1459 {
1460 /* Record the write. */
1461 if ( pThis->fCheckConsistency
1462 && !pThis->fRecordWriteBeforeCompletion)
1463 {
1464 int rc2 = drvdiskintWriteRecord(pThis, &pIoReq->IoSeg, 1, off, cbWrite);
1465 AssertRC(rc2);
1466 }
1467
1468 if (pThis->hIoLogger)
1469 {
1470 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, pIoReq->hIoLogEntry, VINF_SUCCESS, NULL);
1471 AssertRC(rc2);
1472 }
1473
1474 if (pThis->fTraceRequests)
1475 drvdiskintIoReqRemove(pThis, pIoReq);
1476 }
1477
1478 LogFlowFunc(("returns %Rrc\n", rc));
1479 return rc;
1480}
1481
1482/**
1483 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqFlush}
1484 */
1485static DECLCALLBACK(int) drvdiskintIoReqFlush(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
1486{
1487 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1488 PDRVDISKAIOREQ pIoReq = DISKINTEGRITY_IOREQ_HANDLE_2_DRVDISKAIOREQ(pThis, hIoReq);
1489
1490 pIoReq->enmTxDir = DRVDISKAIOTXDIR_FLUSH;
1491 pIoReq->off = 0;
1492 pIoReq->cbTransfer = 0;
1493
1494 if (pThis->fTraceRequests)
1495 drvdiskintIoReqAdd(pThis, pIoReq);
1496
1497 if (pThis->hIoLogger)
1498 {
1499 int rc2 = VDDbgIoLogStart(pThis->hIoLogger, true, VDDBGIOLOGREQ_FLUSH, 0,
1500 0, NULL, &pIoReq->hIoLogEntry);
1501 AssertRC(rc2);
1502 }
1503
1504 int rc = pThis->pDrvMediaEx->pfnIoReqFlush(pThis->pDrvMediaEx, hIoReq);
1505 if (rc == VINF_SUCCESS)
1506 {
1507 if (pThis->hIoLogger)
1508 {
1509 int rc2 = VDDbgIoLogComplete(pThis->hIoLogger, pIoReq->hIoLogEntry, VINF_SUCCESS, NULL);
1510 AssertRC(rc2);
1511 }
1512 }
1513
1514 LogFlowFunc(("returns %Rrc\n", rc));
1515 return rc;
1516}
1517
1518/**
1519 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqDiscard}
1520 */
1521static DECLCALLBACK(int) drvdiskintIoReqDiscard(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, unsigned cRangesMax)
1522{
1523 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1524 return pThis->pDrvMediaEx->pfnIoReqDiscard(pThis->pDrvMediaEx, hIoReq, cRangesMax);
1525}
1526
1527/**
1528 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetActiveCount}
1529 */
1530static DECLCALLBACK(uint32_t) drvdiskintIoReqGetActiveCount(PPDMIMEDIAEX pInterface)
1531{
1532 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1533 return pThis->pDrvMediaEx->pfnIoReqGetActiveCount(pThis->pDrvMediaEx);
1534}
1535
1536/**
1537 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetSuspendedCount}
1538 */
1539static DECLCALLBACK(uint32_t) drvdiskintIoReqGetSuspendedCount(PPDMIMEDIAEX pInterface)
1540{
1541 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1542 return pThis->pDrvMediaEx->pfnIoReqGetSuspendedCount(pThis->pDrvMediaEx);
1543}
1544
1545/**
1546 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedStart}
1547 */
1548static DECLCALLBACK(int) drvdiskintIoReqQuerySuspendedStart(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq, void **ppvIoReqAlloc)
1549{
1550 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1551 return pThis->pDrvMediaEx->pfnIoReqQuerySuspendedStart(pThis->pDrvMediaEx, phIoReq, ppvIoReqAlloc);
1552}
1553
1554/**
1555 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedNext}
1556 */
1557static DECLCALLBACK(int) drvdiskintIoReqQuerySuspendedNext(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq,
1558 PPDMMEDIAEXIOREQ phIoReqNext, void **ppvIoReqAllocNext)
1559{
1560 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1561 return pThis->pDrvMediaEx->pfnIoReqQuerySuspendedNext(pThis->pDrvMediaEx, hIoReq, phIoReqNext, ppvIoReqAllocNext);
1562}
1563
1564/**
1565 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedSave}
1566 */
1567static DECLCALLBACK(int) drvdiskintIoReqSuspendedSave(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
1568{
1569 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1570 return pThis->pDrvMediaEx->pfnIoReqSuspendedSave(pThis->pDrvMediaEx, pSSM, hIoReq);
1571}
1572
1573/**
1574 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedLoad}
1575 */
1576static DECLCALLBACK(int) drvdiskintIoReqSuspendedLoad(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
1577{
1578 PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
1579 return pThis->pDrvMediaEx->pfnIoReqSuspendedLoad(pThis->pDrvMediaEx, pSSM, hIoReq);
1580}
1581
1582/* -=-=-=-=- IBase -=-=-=-=- */
1583
1584/**
1585 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1586 */
1587static DECLCALLBACK(void *) drvdiskintQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1588{
1589 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1590 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
1591
1592 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1593 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
1594 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pThis->IMediaPort);
1595 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pThis->IMediaExPort);
1596 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEX, pThis->pDrvMediaEx ? &pThis->IMediaEx : NULL);
1597 return NULL;
1598}
1599
1600
1601/* -=-=-=-=- driver interface -=-=-=-=- */
1602
1603static DECLCALLBACK(int) drvdiskintTreeDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
1604{
1605 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)pNode;
1606
1607 RT_NOREF(pvUser);
1608
1609 RTMemFree(pSeg->pbSeg);
1610 RTMemFree(pSeg);
1611 return VINF_SUCCESS;
1612}
1613
1614/**
1615 * @copydoc FNPDMDRVDESTRUCT
1616 */
1617static DECLCALLBACK(void) drvdiskintDestruct(PPDMDRVINS pDrvIns)
1618{
1619 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
1620
1621 if (pThis->pTreeSegments)
1622 {
1623 RTAvlrFileOffsetDestroy(pThis->pTreeSegments, drvdiskintTreeDestroy, NULL);
1624 RTMemFree(pThis->pTreeSegments);
1625 }
1626
1627 if (pThis->fTraceRequests)
1628 {
1629 pThis->fRunning = false;
1630 RTSemEventSignal(pThis->SemEvent);
1631 RTSemEventDestroy(pThis->SemEvent);
1632 }
1633
1634 if (pThis->fCheckDoubleCompletion)
1635 {
1636 /* Free all requests */
1637 while (pThis->papIoReq[pThis->iEntry])
1638 {
1639 RTMemFree(pThis->papIoReq[pThis->iEntry]);
1640 pThis->papIoReq[pThis->iEntry] = NULL;
1641 pThis->iEntry = (pThis->iEntry+1) % pThis->cEntries;
1642 }
1643 }
1644
1645 if (pThis->hIoLogger)
1646 VDDbgIoLogDestroy(pThis->hIoLogger);
1647
1648 if (pThis->hReqCache != NIL_RTMEMCACHE)
1649 {
1650 RTMemCacheDestroy(pThis->hReqCache);
1651 pThis->hReqCache = NIL_RTMEMCACHE;
1652 }
1653}
1654
1655/**
1656 * Construct a disk integrity driver instance.
1657 *
1658 * @copydoc FNPDMDRVCONSTRUCT
1659 */
1660static DECLCALLBACK(int) drvdiskintConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1661{
1662 int rc = VINF_SUCCESS;
1663 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
1664 LogFlow(("drvdiskintConstruct: iInstance=%d\n", pDrvIns->iInstance));
1665 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1666
1667 /*
1668 * Validate configuration.
1669 */
1670 if (!CFGMR3AreValuesValid(pCfg, "CheckConsistency\0"
1671 "TraceRequests\0"
1672 "CheckIntervalMs\0"
1673 "ExpireIntervalMs\0"
1674 "CheckDoubleCompletions\0"
1675 "HistorySize\0"
1676 "IoLog\0"
1677 "IoLogData\0"
1678 "PrepopulateRamDisk\0"
1679 "ReadAfterWrite\0"
1680 "RecordWriteBeforeCompletion\0"
1681 "ValidateMemoryBuffers\0"))
1682 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
1683
1684 rc = CFGMR3QueryBoolDef(pCfg, "CheckConsistency", &pThis->fCheckConsistency, false);
1685 AssertRC(rc);
1686 rc = CFGMR3QueryBoolDef(pCfg, "TraceRequests", &pThis->fTraceRequests, false);
1687 AssertRC(rc);
1688 rc = CFGMR3QueryU32Def(pCfg, "CheckIntervalMs", &pThis->uCheckIntervalMs, 5000); /* 5 seconds */
1689 AssertRC(rc);
1690 rc = CFGMR3QueryU32Def(pCfg, "ExpireIntervalMs", &pThis->uExpireIntervalMs, 20000); /* 20 seconds */
1691 AssertRC(rc);
1692 rc = CFGMR3QueryBoolDef(pCfg, "CheckDoubleCompletions", &pThis->fCheckDoubleCompletion, false);
1693 AssertRC(rc);
1694 rc = CFGMR3QueryU32Def(pCfg, "HistorySize", &pThis->cEntries, 512);
1695 AssertRC(rc);
1696 rc = CFGMR3QueryBoolDef(pCfg, "PrepopulateRamDisk", &pThis->fPrepopulateRamDisk, false);
1697 AssertRC(rc);
1698 rc = CFGMR3QueryBoolDef(pCfg, "ReadAfterWrite", &pThis->fReadAfterWrite, false);
1699 AssertRC(rc);
1700 rc = CFGMR3QueryBoolDef(pCfg, "RecordWriteBeforeCompletion", &pThis->fRecordWriteBeforeCompletion, false);
1701 AssertRC(rc);
1702 rc = CFGMR3QueryBoolDef(pCfg, "ValidateMemoryBuffers", &pThis->fValidateMemBufs, false);
1703 AssertRC(rc);
1704
1705 bool fIoLogData = false;
1706 rc = CFGMR3QueryBoolDef(pCfg, "IoLogData", &fIoLogData, false);
1707 AssertRC(rc);
1708 char *pszIoLogFilename = NULL;
1709 rc = CFGMR3QueryStringAlloc(pCfg, "IoLog", &pszIoLogFilename);
1710 Assert(RT_SUCCESS(rc) || rc == VERR_CFGM_VALUE_NOT_FOUND);
1711
1712 /*
1713 * Initialize most of the data members.
1714 */
1715 pThis->pDrvIns = pDrvIns;
1716 pThis->hReqCache = NIL_RTMEMCACHE;
1717
1718 /* IBase. */
1719 pDrvIns->IBase.pfnQueryInterface = drvdiskintQueryInterface;
1720
1721 /* IMedia */
1722 pThis->IMedia.pfnRead = drvdiskintRead;
1723 pThis->IMedia.pfnWrite = drvdiskintWrite;
1724 pThis->IMedia.pfnFlush = drvdiskintFlush;
1725 pThis->IMedia.pfnGetSize = drvdiskintGetSize;
1726 pThis->IMedia.pfnIsReadOnly = drvdiskintIsReadOnly;
1727 pThis->IMedia.pfnBiosIsVisible = drvdiskintBiosIsVisible;
1728 pThis->IMedia.pfnBiosGetPCHSGeometry = drvdiskintBiosGetPCHSGeometry;
1729 pThis->IMedia.pfnBiosSetPCHSGeometry = drvdiskintBiosSetPCHSGeometry;
1730 pThis->IMedia.pfnBiosGetLCHSGeometry = drvdiskintBiosGetLCHSGeometry;
1731 pThis->IMedia.pfnBiosSetLCHSGeometry = drvdiskintBiosSetLCHSGeometry;
1732 pThis->IMedia.pfnGetUuid = drvdiskintGetUuid;
1733 pThis->IMedia.pfnGetSectorSize = drvdiskintGetSectorSize;
1734 pThis->IMedia.pfnGetType = drvdiskintGetType;
1735 pThis->IMedia.pfnReadPcBios = drvdiskintReadPcBios;
1736 pThis->IMedia.pfnIsNonRotational = drvdiskintIsNonRotational;
1737
1738 /* IMediaEx. */
1739 pThis->IMediaEx.pfnQueryFeatures = drvdiskintQueryFeatures;
1740 pThis->IMediaEx.pfnIoReqAllocSizeSet = drvdiskintIoReqAllocSizeSet;
1741 pThis->IMediaEx.pfnIoReqAlloc = drvdiskintIoReqAlloc;
1742 pThis->IMediaEx.pfnIoReqFree = drvdiskintIoReqFree;
1743 pThis->IMediaEx.pfnIoReqQueryResidual = drvdiskintIoReqQueryResidual;
1744 pThis->IMediaEx.pfnIoReqQueryXferSize = drvdiskintIoReqQueryXferSize;
1745 pThis->IMediaEx.pfnIoReqCancelAll = drvdiskintIoReqCancelAll;
1746 pThis->IMediaEx.pfnIoReqCancel = drvdiskintIoReqCancel;
1747 pThis->IMediaEx.pfnIoReqRead = drvdiskintIoReqRead;
1748 pThis->IMediaEx.pfnIoReqWrite = drvdiskintIoReqWrite;
1749 pThis->IMediaEx.pfnIoReqFlush = drvdiskintIoReqFlush;
1750 pThis->IMediaEx.pfnIoReqDiscard = drvdiskintIoReqDiscard;
1751 pThis->IMediaEx.pfnIoReqGetActiveCount = drvdiskintIoReqGetActiveCount;
1752 pThis->IMediaEx.pfnIoReqGetSuspendedCount = drvdiskintIoReqGetSuspendedCount;
1753 pThis->IMediaEx.pfnIoReqQuerySuspendedStart = drvdiskintIoReqQuerySuspendedStart;
1754 pThis->IMediaEx.pfnIoReqQuerySuspendedNext = drvdiskintIoReqQuerySuspendedNext;
1755 pThis->IMediaEx.pfnIoReqSuspendedSave = drvdiskintIoReqSuspendedSave;
1756 pThis->IMediaEx.pfnIoReqSuspendedLoad = drvdiskintIoReqSuspendedLoad;
1757
1758 /* IMediaPort. */
1759 pThis->IMediaPort.pfnQueryDeviceLocation = drvdiskintQueryDeviceLocation;
1760
1761 /* IMediaExPort. */
1762 pThis->IMediaExPort.pfnIoReqCompleteNotify = drvdiskintIoReqCompleteNotify;
1763 pThis->IMediaExPort.pfnIoReqCopyFromBuf = drvdiskintIoReqCopyFromBuf;
1764 pThis->IMediaExPort.pfnIoReqCopyToBuf = drvdiskintIoReqCopyToBuf;
1765 pThis->IMediaExPort.pfnIoReqQueryDiscardRanges = drvdiskintIoReqQueryDiscardRanges;
1766 pThis->IMediaExPort.pfnIoReqStateChanged = drvdiskintIoReqStateChanged;
1767
1768 /* Query the media port interface above us. */
1769 pThis->pDrvMediaPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAPORT);
1770 if (!pThis->pDrvMediaPort)
1771 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
1772 N_("No media port inrerface above"));
1773
1774 /* Try to attach extended media port interface above.*/
1775 pThis->pDrvMediaExPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAEXPORT);
1776
1777 rc = RTMemCacheCreate(&pThis->hReqCache, sizeof(DRVDISKAIOREQ), 0, UINT32_MAX,
1778 NULL, NULL, NULL, 0);
1779 if (RT_FAILURE(rc))
1780 return PDMDRV_SET_ERROR(pDrvIns, rc,
1781 N_("Failed to create request tracking structure cache"));
1782
1783 /*
1784 * Try attach driver below and query it's media interface.
1785 */
1786 PPDMIBASE pBase;
1787 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
1788 if (RT_FAILURE(rc))
1789 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1790 N_("Failed to attach driver below us! %Rrc"), rc);
1791
1792 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
1793 if (!pThis->pDrvMedia)
1794 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
1795 N_("No media or async media interface below"));
1796
1797 pThis->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIAEX);
1798
1799 if (pThis->pDrvMedia->pfnDiscard)
1800 pThis->IMedia.pfnDiscard = drvdiskintDiscard;
1801
1802 if (pThis->fCheckConsistency)
1803 {
1804 /* Create the AVL tree. */
1805 pThis->pTreeSegments = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
1806 if (!pThis->pTreeSegments)
1807 rc = VERR_NO_MEMORY;
1808 }
1809
1810 if (pThis->fTraceRequests)
1811 {
1812 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apReqActive); i++)
1813 {
1814 pThis->apReqActive[i].pIoReq = NULL;
1815 pThis->apReqActive[i].tsStart = 0;
1816 }
1817
1818 pThis->iNextFreeSlot = 0;
1819
1820 /* Init event semaphore. */
1821 rc = RTSemEventCreate(&pThis->SemEvent);
1822 AssertRC(rc);
1823 pThis->fRunning = true;
1824 rc = RTThreadCreate(&pThis->hThread, drvdiskIntIoReqExpiredCheck, pThis,
1825 0, RTTHREADTYPE_INFREQUENT_POLLER, 0, "DiskIntegrity");
1826 AssertRC(rc);
1827 }
1828
1829 if (pThis->fCheckDoubleCompletion)
1830 {
1831 pThis->iEntry = 0;
1832 pThis->papIoReq = (PDRVDISKAIOREQ *)RTMemAllocZ(pThis->cEntries * sizeof(PDRVDISKAIOREQ));
1833 AssertPtr(pThis->papIoReq);
1834 }
1835
1836 if (pszIoLogFilename)
1837 {
1838 rc = VDDbgIoLogCreate(&pThis->hIoLogger, pszIoLogFilename, fIoLogData ? VDDBG_IOLOG_LOG_DATA : 0);
1839 MMR3HeapFree(pszIoLogFilename);
1840 }
1841
1842 /* Read in all data before the start if requested. */
1843 if (pThis->fPrepopulateRamDisk)
1844 {
1845 uint64_t cbDisk = 0;
1846
1847 LogRel(("DiskIntegrity: Prepopulating RAM disk, this will take some time...\n"));
1848
1849 cbDisk = pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
1850 if (cbDisk)
1851 {
1852 uint64_t off = 0;
1853 uint8_t abBuffer[_64K];
1854 RTSGSEG Seg;
1855
1856 Seg.pvSeg = abBuffer;
1857
1858 while (cbDisk)
1859 {
1860 size_t cbThisRead = RT_MIN(cbDisk, sizeof(abBuffer));
1861
1862 rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, off, abBuffer, cbThisRead);
1863 if (RT_FAILURE(rc))
1864 break;
1865
1866 if (ASMBitFirstSet(abBuffer, sizeof(abBuffer) * 8) != -1)
1867 {
1868 Seg.cbSeg = cbThisRead;
1869 rc = drvdiskintWriteRecord(pThis, &Seg, 1,
1870 off, cbThisRead);
1871 if (RT_FAILURE(rc))
1872 break;
1873 }
1874
1875 cbDisk -= cbThisRead;
1876 off += cbThisRead;
1877 }
1878
1879 LogRel(("DiskIntegrity: Prepopulating RAM disk finished with %Rrc\n", rc));
1880 }
1881 else
1882 return PDMDRV_SET_ERROR(pDrvIns, VERR_INTERNAL_ERROR,
1883 N_("DiskIntegrity: Error querying the media size below"));
1884 }
1885
1886 return rc;
1887}
1888
1889
1890/**
1891 * Block driver registration record.
1892 */
1893const PDMDRVREG g_DrvDiskIntegrity =
1894{
1895 /* u32Version */
1896 PDM_DRVREG_VERSION,
1897 /* szName */
1898 "DiskIntegrity",
1899 /* szRCMod */
1900 "",
1901 /* szR0Mod */
1902 "",
1903 /* pszDescription */
1904 "Disk integrity driver.",
1905 /* fFlags */
1906 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1907 /* fClass. */
1908 PDM_DRVREG_CLASS_BLOCK,
1909 /* cMaxInstances */
1910 ~0U,
1911 /* cbInstance */
1912 sizeof(DRVDISKINTEGRITY),
1913 /* pfnConstruct */
1914 drvdiskintConstruct,
1915 /* pfnDestruct */
1916 drvdiskintDestruct,
1917 /* pfnRelocate */
1918 NULL,
1919 /* pfnIOCtl */
1920 NULL,
1921 /* pfnPowerOn */
1922 NULL,
1923 /* pfnReset */
1924 NULL,
1925 /* pfnSuspend */
1926 NULL,
1927 /* pfnResume */
1928 NULL,
1929 /* pfnAttach */
1930 NULL,
1931 /* pfnDetach */
1932 NULL,
1933 /* pfnPowerOff */
1934 NULL,
1935 /* pfnSoftReset */
1936 NULL,
1937 /* u32EndVersion */
1938 PDM_DRVREG_VERSION
1939};
1940
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