VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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