VirtualBox

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

Last change on this file since 51619 was 51619, checked in by vboxsync, 11 years ago

Devices/Storage: Add new read after write verification mode and fix driver crash after the interface changed

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