VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvRamDisk.cpp@ 64141

Last change on this file since 64141 was 64019, checked in by vboxsync, 8 years ago

Storage: Get rid of PDMIMEDIA::pfnIoBufAllocate and PDMIMEDIA::pfnIoBufFree, was only used by AHCI anyway and is superseded by the I/O buffer management in PDMIMEDIAEX now

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 62.5 KB
Line 
1/* $Id: DrvRamDisk.cpp 64019 2016-09-26 16:32:53Z vboxsync $ */
2/** @file
3 * VBox storage devices: RAM disk driver.
4 */
5
6/*
7 * Copyright (C) 2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_DISK_INTEGRITY
23#include <VBox/vmm/pdmdrv.h>
24#include <VBox/vmm/pdmstorageifs.h>
25#include <iprt/assert.h>
26#include <iprt/string.h>
27#include <iprt/uuid.h>
28#include <iprt/avl.h>
29#include <iprt/list.h>
30#include <iprt/mem.h>
31#include <iprt/memcache.h>
32#include <iprt/message.h>
33#include <iprt/sg.h>
34#include <iprt/time.h>
35#include <iprt/semaphore.h>
36#include <iprt/asm.h>
37#include <iprt/req.h>
38#include <iprt/thread.h>
39
40#include "VBoxDD.h"
41#include "IOBufMgmt.h"
42
43/*********************************************************************************************************************************
44* Structures and Typedefs *
45*********************************************************************************************************************************/
46
47/** Pointer to a ramdisk driver instance. */
48typedef struct DRVRAMDISK *PDRVRAMDISK;
49
50/**
51 * Disk segment.
52 */
53typedef struct DRVDISKSEGMENT
54{
55 /** AVL core. */
56 AVLRFOFFNODECORE Core;
57 /** Size of the segment */
58 size_t cbSeg;
59 /** Data for this segment */
60 uint8_t *pbSeg;
61} DRVDISKSEGMENT, *PDRVDISKSEGMENT;
62
63/**
64 * VD I/O request state.
65 */
66typedef enum VDIOREQSTATE
67{
68 /** Invalid. */
69 VDIOREQSTATE_INVALID = 0,
70 /** The request is not in use and resides on the free list. */
71 VDIOREQSTATE_FREE,
72 /** The request was just allocated and is not active. */
73 VDIOREQSTATE_ALLOCATED,
74 /** The request was allocated and is in use. */
75 VDIOREQSTATE_ACTIVE,
76 /** The request was suspended and is not actively processed. */
77 VDIOREQSTATE_SUSPENDED,
78 /** The request is in the last step of completion and syncs memory. */
79 VDIOREQSTATE_COMPLETING,
80 /** The request completed. */
81 VDIOREQSTATE_COMPLETED,
82 /** The request was aborted but wasn't returned as complete from the storage
83 * layer below us. */
84 VDIOREQSTATE_CANCELED,
85 /** 32bit hack. */
86 VDIOREQSTATE_32BIT_HACK = 0x7fffffff
87} VDIOREQSTATE;
88
89/**
90 * VD I/O Request.
91 */
92typedef struct PDMMEDIAEXIOREQINT
93{
94 /** List node for the list of allocated requests. */
95 RTLISTNODE NdAllocatedList;
96 /** List for requests waiting for I/O memory or on the redo list. */
97 RTLISTNODE NdLstWait;
98 /** I/O request type. */
99 PDMMEDIAEXIOREQTYPE enmType;
100 /** Request state. */
101 volatile VDIOREQSTATE enmState;
102 /** I/O request ID. */
103 PDMMEDIAEXIOREQID uIoReqId;
104 /** Pointer to the disk container. */
105 PDRVRAMDISK pDisk;
106 /** Flags. */
107 uint32_t fFlags;
108 /** Timestamp when the request was submitted. */
109 uint64_t tsSubmit;
110 /** Type dependent data. */
111 union
112 {
113 /** Read/Write request sepcific data. */
114 struct
115 {
116 /** Start offset of the request. */
117 uint64_t offStart;
118 /** Size of the request. */
119 size_t cbReq;
120 /** Size left for this request. */
121 size_t cbReqLeft;
122 /** Size of the allocated I/O buffer. */
123 size_t cbIoBuf;
124 /** I/O buffer descriptor. */
125 IOBUFDESC IoBuf;
126 } ReadWrite;
127 /** Discard specific data. */
128 struct
129 {
130 /** Pointer to array of ranges to discard. */
131 PRTRANGE paRanges;
132 /** Number of ranges to discard. */
133 unsigned cRanges;
134 } Discard;
135 };
136 /** Allocator specific memory - variable size. */
137 uint8_t abAlloc[1];
138} PDMMEDIAEXIOREQINT;
139/** Pointer to a VD I/O request. */
140typedef PDMMEDIAEXIOREQINT *PPDMMEDIAEXIOREQINT;
141
142/**
143 * Structure for holding a list of allocated requests.
144 */
145typedef struct VDLSTIOREQALLOC
146{
147 /** Mutex protecting the table of allocated requests. */
148 RTSEMFASTMUTEX hMtxLstIoReqAlloc;
149 /** List anchor. */
150 RTLISTANCHOR LstIoReqAlloc;
151} VDLSTIOREQALLOC;
152typedef VDLSTIOREQALLOC *PVDLSTIOREQALLOC;
153
154/** Number of bins for allocated requests. */
155#define DRVVD_VDIOREQ_ALLOC_BINS 8
156
157/**
158 * Disk integrity driver instance data.
159 *
160 * @implements PDMIMEDIA
161 */
162typedef struct DRVRAMDISK
163{
164 /** Pointer driver instance. */
165 PPDMDRVINS pDrvIns;
166 /** Pointer to the media driver below us.
167 * This is NULL if the media is not mounted. */
168 PPDMIMEDIA pDrvMedia;
169 /** Our media interface */
170 PDMIMEDIA IMedia;
171
172 /** The media port interface above. */
173 PPDMIMEDIAPORT pDrvMediaPort;
174 /** Media port interface */
175 PDMIMEDIAPORT IMediaPort;
176
177 /** Flag whether the RAM disk was pre allocated. */
178 bool fPreallocRamDisk;
179 /** AVL tree containing the disk blocks to check. */
180 PAVLRFOFFTREE pTreeSegments;
181 /** Size of the disk. */
182 uint64_t cbDisk;
183 /** Size of one sector. */
184 uint32_t cbSector;
185
186 /** Worker request queue. */
187 RTREQQUEUE hReqQ;
188 /** Worker thread for async requests. */
189 RTTHREAD hThrdWrk;
190
191 /** @name IMEDIAEX interface support specific members.
192 * @{ */
193 /** Pointer to the IMEDIAEXPORT interface above us. */
194 PPDMIMEDIAEXPORT pDrvMediaExPort;
195 /** Our extended media interface. */
196 PDMIMEDIAEX IMediaEx;
197 /** Memory cache for the I/O requests. */
198 RTMEMCACHE hIoReqCache;
199 /** I/O buffer manager. */
200 IOBUFMGR hIoBufMgr;
201 /** Active request counter. */
202 volatile uint32_t cIoReqsActive;
203 /** Bins for allocated requests. */
204 VDLSTIOREQALLOC aIoReqAllocBins[DRVVD_VDIOREQ_ALLOC_BINS];
205 /** List of requests for I/O memory to be available - VDIOREQ::NdLstWait. */
206 RTLISTANCHOR LstIoReqIoBufWait;
207 /** Critical section protecting the list of requests waiting for I/O memory. */
208 RTCRITSECT CritSectIoReqsIoBufWait;
209 /** Number of requests waiting for a I/O buffer. */
210 volatile uint32_t cIoReqsWaiting;
211 /** Flag whether we have to resubmit requests on resume because the
212 * VM was suspended due to a recoverable I/O error.
213 */
214 volatile bool fRedo;
215 /** List of requests we have to redo. */
216 RTLISTANCHOR LstIoReqRedo;
217 /** Criticial section protecting the list of waiting requests. */
218 RTCRITSECT CritSectIoReqRedo;
219 /** Number of errors logged so far. */
220 unsigned cErrors;
221 /** @} */
222
223} DRVRAMDISK;
224
225
226static void drvramdiskMediaExIoReqComplete(PDRVRAMDISK pThis, PPDMMEDIAEXIOREQINT pIoReq,
227 int rcReq);
228
229/**
230 * Record a successful write to the virtual disk.
231 *
232 * @returns VBox status code.
233 * @param pThis Disk integrity driver instance data.
234 * @param pSgBuf The S/G buffer holding the data to write.
235 * @param off Start offset.
236 * @param cbWrite Number of bytes to record.
237 */
238static int drvramdiskWriteWorker(PDRVRAMDISK pThis, PRTSGBUF pSgBuf,
239 uint64_t off, size_t cbWrite)
240{
241 int rc = VINF_SUCCESS;
242
243 LogFlowFunc(("pThis=%#p pSgBuf=%#p cSeg=%u off=%llx cbWrite=%u\n",
244 pThis, pSgBuf, off, cbWrite));
245
246 /* Update the segments */
247 size_t cbLeft = cbWrite;
248 RTFOFF offCurr = (RTFOFF)off;
249
250 while (cbLeft)
251 {
252 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr);
253 size_t cbRange = 0;
254 bool fSet = false;
255 unsigned offSeg = 0;
256
257 if (!pSeg)
258 {
259 /* Get next segment */
260 pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true);
261 if ( !pSeg
262 || offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key)
263 cbRange = cbLeft;
264 else
265 cbRange = pSeg->Core.Key - offCurr;
266
267 Assert(cbRange % 512 == 0);
268
269 /* Create new segment */
270 pSeg = (PDRVDISKSEGMENT)RTMemAllocZ(sizeof(DRVDISKSEGMENT));
271 if (pSeg)
272 {
273 pSeg->Core.Key = offCurr;
274 pSeg->Core.KeyLast = offCurr + (RTFOFF)cbRange - 1;
275 pSeg->cbSeg = cbRange;
276 pSeg->pbSeg = (uint8_t *)RTMemAllocZ(cbRange);
277 if (!pSeg->pbSeg)
278 RTMemFree(pSeg);
279 else
280 {
281 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
282 AssertMsg(fInserted, ("Bug!\n")); RT_NOREF(fInserted);
283 fSet = true;
284 }
285 }
286 }
287 else
288 {
289 fSet = true;
290 offSeg = offCurr - pSeg->Core.Key;
291 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
292 }
293
294 if (fSet)
295 {
296 AssertPtr(pSeg);
297 size_t cbCopied = RTSgBufCopyToBuf(pSgBuf, pSeg->pbSeg + offSeg, cbRange);
298 Assert(cbCopied == cbRange); RT_NOREF(cbCopied);
299 }
300 else
301 RTSgBufAdvance(pSgBuf, cbRange);
302
303 offCurr += cbRange;
304 cbLeft -= cbRange;
305 }
306
307 return rc;
308}
309
310/**
311 * Verifies a read request.
312 *
313 * @returns VBox status code.
314 * @param pThis Disk integrity driver instance data.
315 * @param pSgBuf The S/G buffer to store the data.
316 * @param off Start offset.
317 * @param cbWrite Number of bytes to verify.
318 */
319static int drvramdiskReadWorker(PDRVRAMDISK pThis, PRTSGBUF pSgBuf,
320 uint64_t off, size_t cbRead)
321{
322 int rc = VINF_SUCCESS;
323
324 LogFlowFunc(("pThis=%#p pSgBuf=%#p off=%llx cbRead=%u\n",
325 pThis, pSgBuf, off, cbRead));
326
327 Assert(off % 512 == 0);
328 Assert(cbRead % 512 == 0);
329
330 /* Compare read data */
331 size_t cbLeft = cbRead;
332 RTFOFF offCurr = (RTFOFF)off;
333
334 while (cbLeft)
335 {
336 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr);
337 size_t cbRange = 0;
338 bool fCmp = false;
339 unsigned offSeg = 0;
340
341 if (!pSeg)
342 {
343 /* Get next segment */
344 pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true);
345 if ( !pSeg
346 || offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key)
347 cbRange = cbLeft;
348 else
349 cbRange = pSeg->Core.Key - offCurr;
350
351 /* No segment means everything should be 0 for this part. */
352 RTSgBufSet(pSgBuf, 0, cbRange);
353 }
354 else
355 {
356 fCmp = true;
357 offSeg = offCurr - pSeg->Core.Key;
358 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
359
360 RTSGSEG Seg;
361 RTSGBUF SgBufSrc;
362
363 Seg.cbSeg = cbRange;
364 Seg.pvSeg = pSeg->pbSeg + offSeg;
365
366 RTSgBufInit(&SgBufSrc, &Seg, 1);
367 RTSgBufCopy(pSgBuf, &SgBufSrc, cbRange);
368 }
369
370 offCurr += cbRange;
371 cbLeft -= cbRange;
372 }
373
374 return rc;
375}
376
377/**
378 * Discards the given ranges from the disk.
379 *
380 * @returns VBox status code.
381 * @param pThis Disk integrity driver instance data.
382 * @param paRanges Array of ranges to discard.
383 * @param cRanges Number of ranges in the array.
384 */
385static int drvramdiskDiscardRecords(PDRVRAMDISK pThis, PCRTRANGE paRanges, unsigned cRanges)
386{
387 int rc = VINF_SUCCESS;
388
389 LogFlowFunc(("pThis=%#p paRanges=%#p cRanges=%u\n", pThis, paRanges, cRanges));
390
391 for (unsigned i = 0; i < cRanges; i++)
392 {
393 uint64_t offStart = paRanges[i].offStart;
394 size_t cbLeft = paRanges[i].cbRange;
395
396 LogFlowFunc(("Discarding off=%llu cbRange=%zu\n", offStart, cbLeft));
397
398 while (cbLeft)
399 {
400 size_t cbRange;
401 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offStart);
402
403 if (!pSeg)
404 {
405 /* Get next segment */
406 pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offStart, true);
407 if ( !pSeg
408 || (RTFOFF)offStart + (RTFOFF)cbLeft <= pSeg->Core.Key)
409 cbRange = cbLeft;
410 else
411 cbRange = pSeg->Core.Key - offStart;
412
413 Assert(!(cbRange % 512));
414 }
415 else
416 {
417 size_t cbPreLeft, cbPostLeft;
418
419 cbRange = RT_MIN(cbLeft, pSeg->Core.KeyLast - offStart + 1);
420 cbPreLeft = offStart - pSeg->Core.Key;
421 cbPostLeft = pSeg->cbSeg - cbRange - cbPreLeft;
422
423 Assert(!(cbRange % 512));
424 Assert(!(cbPreLeft % 512));
425 Assert(!(cbPostLeft % 512));
426
427 LogFlowFunc(("cbRange=%zu cbPreLeft=%zu cbPostLeft=%zu\n",
428 cbRange, cbPreLeft, cbPostLeft));
429
430 RTAvlrFileOffsetRemove(pThis->pTreeSegments, pSeg->Core.Key);
431
432 if (!cbPreLeft && !cbPostLeft)
433 {
434 /* Just free the whole segment. */
435 LogFlowFunc(("Freeing whole segment pSeg=%#p\n", pSeg));
436 RTMemFree(pSeg->pbSeg);
437 RTMemFree(pSeg);
438 }
439 else if (cbPreLeft && !cbPostLeft)
440 {
441 /* Realloc to new size and insert. */
442 LogFlowFunc(("Realloc segment pSeg=%#p\n", pSeg));
443 pSeg->pbSeg = (uint8_t *)RTMemRealloc(pSeg->pbSeg, cbPreLeft);
444 pSeg = (PDRVDISKSEGMENT)RTMemRealloc(pSeg, sizeof(DRVDISKSEGMENT));
445 pSeg->Core.KeyLast = pSeg->Core.Key + cbPreLeft - 1;
446 pSeg->cbSeg = cbPreLeft;
447 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
448 Assert(fInserted); RT_NOREF(fInserted);
449 }
450 else if (!cbPreLeft && cbPostLeft)
451 {
452 /* Move data to the front and realloc. */
453 LogFlowFunc(("Move data and realloc segment pSeg=%#p\n", pSeg));
454 memmove(pSeg->pbSeg, pSeg->pbSeg + cbRange, cbPostLeft);
455 pSeg = (PDRVDISKSEGMENT)RTMemRealloc(pSeg, sizeof(DRVDISKSEGMENT));
456 pSeg->pbSeg = (uint8_t *)RTMemRealloc(pSeg->pbSeg, cbPostLeft);
457 pSeg->Core.Key += cbRange;
458 pSeg->cbSeg = cbPostLeft;
459 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
460 Assert(fInserted); RT_NOREF(fInserted);
461 }
462 else
463 {
464 /* Split the segment into 2 new segments. */
465 LogFlowFunc(("Split segment pSeg=%#p\n", pSeg));
466 PDRVDISKSEGMENT pSegPost = (PDRVDISKSEGMENT)RTMemAllocZ(sizeof(DRVDISKSEGMENT));
467 if (pSegPost)
468 {
469 pSegPost->Core.Key = pSeg->Core.Key + cbPreLeft + cbRange;
470 pSegPost->Core.KeyLast = pSeg->Core.KeyLast;
471 pSegPost->cbSeg = cbPostLeft;
472 pSegPost->pbSeg = (uint8_t *)RTMemAllocZ(cbPostLeft);
473 if (!pSegPost->pbSeg)
474 RTMemFree(pSegPost);
475 else
476 {
477 memcpy(pSegPost->pbSeg, pSeg->pbSeg + cbPreLeft + cbRange, cbPostLeft);
478 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSegPost->Core);
479 Assert(fInserted); RT_NOREF(fInserted);
480 }
481 }
482
483 /* Shrink the current segment. */
484 pSeg->pbSeg = (uint8_t *)RTMemRealloc(pSeg->pbSeg, cbPreLeft);
485 pSeg = (PDRVDISKSEGMENT)RTMemRealloc(pSeg, sizeof(DRVDISKSEGMENT));
486 pSeg->Core.KeyLast = pSeg->Core.Key + cbPreLeft - 1;
487 pSeg->cbSeg = cbPreLeft;
488 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
489 Assert(fInserted); RT_NOREF(fInserted);
490 } /* if (cbPreLeft && cbPostLeft) */
491 }
492
493 offStart += cbRange;
494 cbLeft -= cbRange;
495 }
496 }
497
498 LogFlowFunc(("returns rc=%Rrc\n", rc));
499 return rc;
500}
501
502/* -=-=-=-=- IMedia -=-=-=-=- */
503
504/*********************************************************************************************************************************
505* Media interface methods *
506*********************************************************************************************************************************/
507
508/** @copydoc PDMIMEDIA::pfnRead */
509static DECLCALLBACK(int) drvramdiskRead(PPDMIMEDIA pInterface,
510 uint64_t off, void *pvBuf, size_t cbRead)
511{
512 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMedia);
513 RTSGSEG Seg;
514 RTSGBUF SgBuf;
515
516 Seg.cbSeg = cbRead;
517 Seg.pvSeg = pvBuf;
518 RTSgBufInit(&SgBuf, &Seg, 1);
519 return drvramdiskReadWorker(pThis, &SgBuf, off, cbRead);
520}
521
522/** @copydoc PDMIMEDIA::pfnWrite */
523static DECLCALLBACK(int) drvramdiskWrite(PPDMIMEDIA pInterface,
524 uint64_t off, const void *pvBuf,
525 size_t cbWrite)
526{
527 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMedia);
528 RTSGSEG Seg;
529 RTSGBUF SgBuf;
530
531 Seg.cbSeg = cbWrite;
532 Seg.pvSeg = (void *)pvBuf;
533 RTSgBufInit(&SgBuf, &Seg, 1);
534 return drvramdiskWriteWorker(pThis, &SgBuf, off, cbWrite);
535}
536
537/** @copydoc PDMIMEDIA::pfnFlush */
538static DECLCALLBACK(int) drvramdiskFlush(PPDMIMEDIA pInterface)
539{
540 RT_NOREF1(pInterface);
541 /* Nothing to do here. */
542 return VINF_SUCCESS;
543}
544
545/** @copydoc PDMIMEDIA::pfnGetSize */
546static DECLCALLBACK(uint64_t) drvramdiskGetSize(PPDMIMEDIA pInterface)
547{
548 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMedia);
549 return pThis->cbDisk;
550}
551
552/** @interface_method_impl{PDMIMEDIA,pfnBiosIsVisible} */
553static DECLCALLBACK(bool) drvramdiskBiosIsVisible(PPDMIMEDIA pInterface)
554{
555 RT_NOREF1(pInterface);
556 return false;
557}
558
559/** @copydoc PDMIMEDIA::pfnGetType */
560static DECLCALLBACK(PDMMEDIATYPE) drvramdiskGetType(PPDMIMEDIA pInterface)
561{
562 RT_NOREF1(pInterface);
563 return PDMMEDIATYPE_HARD_DISK;
564}
565
566/** @copydoc PDMIMEDIA::pfnIsReadOnly */
567static DECLCALLBACK(bool) drvramdiskIsReadOnly(PPDMIMEDIA pInterface)
568{
569 RT_NOREF1(pInterface);
570 return false; /** @todo */
571}
572
573/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
574static DECLCALLBACK(int) drvramdiskBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
575 PPDMMEDIAGEOMETRY pPCHSGeometry)
576{
577 RT_NOREF2(pInterface, pPCHSGeometry);
578 return VERR_NOT_IMPLEMENTED;
579}
580
581/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
582static DECLCALLBACK(int) drvramdiskBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
583 PCPDMMEDIAGEOMETRY pPCHSGeometry)
584{
585 RT_NOREF2(pInterface, pPCHSGeometry);
586 return VERR_NOT_IMPLEMENTED;
587}
588
589/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
590static DECLCALLBACK(int) drvramdiskBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
591 PPDMMEDIAGEOMETRY pLCHSGeometry)
592{
593 RT_NOREF2(pInterface, pLCHSGeometry);
594 return VERR_NOT_IMPLEMENTED;
595}
596
597/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
598static DECLCALLBACK(int) drvramdiskBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
599 PCPDMMEDIAGEOMETRY pLCHSGeometry)
600{
601 RT_NOREF2(pInterface, pLCHSGeometry);
602 return VERR_NOT_IMPLEMENTED;
603}
604
605/** @copydoc PDMIMEDIA::pfnGetUuid */
606static DECLCALLBACK(int) drvramdiskGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
607{
608 RT_NOREF1(pInterface);
609 return RTUuidClear(pUuid);
610}
611
612/** @copydoc PDMIMEDIA::pfnGetSectorSize */
613static DECLCALLBACK(uint32_t) drvramdiskGetSectorSize(PPDMIMEDIA pInterface)
614{
615 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMedia);
616 return pThis->cbSector;
617}
618
619/** @copydoc PDMIMEDIA::pfnDiscard */
620static DECLCALLBACK(int) drvramdiskDiscard(PPDMIMEDIA pInterface, PCRTRANGE paRanges, unsigned cRanges)
621{
622 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMedia);
623 return drvramdiskDiscardRecords(pThis, paRanges, cRanges);
624}
625
626/** @copydoc PDMIMEDIA::pfnReadPcBios */
627static DECLCALLBACK(int) drvramdiskReadPcBios(PPDMIMEDIA pInterface,
628 uint64_t off, void *pvBuf, size_t cbRead)
629{
630 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMedia);
631 RTSGSEG Seg;
632 RTSGBUF SgBuf;
633
634 Seg.cbSeg = cbRead;
635 Seg.pvSeg = pvBuf;
636 RTSgBufInit(&SgBuf, &Seg, 1);
637 return drvramdiskReadWorker(pThis, &SgBuf, off, cbRead);
638}
639
640
641/*********************************************************************************************************************************
642* Extended media interface methods *
643*********************************************************************************************************************************/
644
645static void drvramdiskMediaExIoReqWarningOutOfMemory(PPDMDRVINS pDrvIns)
646{
647 int rc;
648 LogRel(("RamDisk#%u: Out of memory\n", pDrvIns->iInstance));
649 rc = PDMDrvHlpVMSetRuntimeError(pDrvIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DrvRamDisk_OOM",
650 N_("There is not enough free memory for the ramdisk"));
651 AssertRC(rc);
652}
653
654/**
655 * Checks whether a given status code indicates a recoverable error
656 * suspending the VM if it is.
657 *
658 * @returns Flag indicating whether the status code is a recoverable error
659 * (full disk, broken network connection).
660 * @param pThis VBox disk container instance data.
661 * @param rc Status code to check.
662 */
663bool drvramdiskMediaExIoReqIsRedoSetWarning(PDRVRAMDISK pThis, int rc)
664{
665 if (rc == VERR_NO_MEMORY)
666 {
667 if (ASMAtomicCmpXchgBool(&pThis->fRedo, true, false))
668 drvramdiskMediaExIoReqWarningOutOfMemory(pThis->pDrvIns);
669 return true;
670 }
671
672 return false;
673}
674
675/**
676 * Syncs the memory buffers between the I/O request allocator and the internal buffer.
677 *
678 * @returns VBox status code.
679 * @param pThis VBox disk container instance data.
680 * @param pIoReq I/O request to sync.
681 * @param fToIoBuf Flag indicating the sync direction.
682 * true to copy data from the allocators buffer to our internal buffer.
683 * false for the other direction.
684 */
685DECLINLINE(int) drvramdiskMediaExIoReqBufSync(PDRVRAMDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, bool fToIoBuf)
686{
687 int rc = VINF_SUCCESS;
688
689 Assert(pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE);
690
691 /* Make sure the buffer is reset. */
692 RTSgBufReset(&pIoReq->ReadWrite.IoBuf.SgBuf);
693
694 if (fToIoBuf)
695 rc = pThis->pDrvMediaExPort->pfnIoReqCopyToBuf(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
696 pIoReq->ReadWrite.cbReq - pIoReq->ReadWrite.cbReqLeft,
697 &pIoReq->ReadWrite.IoBuf.SgBuf,
698 RT_MIN(pIoReq->ReadWrite.cbIoBuf, pIoReq->ReadWrite.cbReqLeft));
699 else
700 rc = pThis->pDrvMediaExPort->pfnIoReqCopyFromBuf(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
701 pIoReq->ReadWrite.cbReq - pIoReq->ReadWrite.cbReqLeft,
702 &pIoReq->ReadWrite.IoBuf.SgBuf,
703 RT_MIN(pIoReq->ReadWrite.cbIoBuf, pIoReq->ReadWrite.cbReqLeft));
704
705 RTSgBufReset(&pIoReq->ReadWrite.IoBuf.SgBuf);
706 return rc;
707}
708
709/**
710 * Hashes the I/O request ID to an index for the allocated I/O request bin.
711 */
712DECLINLINE(unsigned) drvramdiskMediaExIoReqIdHash(PDMMEDIAEXIOREQID uIoReqId)
713{
714 return uIoReqId % DRVVD_VDIOREQ_ALLOC_BINS; /** @todo: Find something better? */
715}
716
717/**
718 * Inserts the given I/O request in to the list of allocated I/O requests.
719 *
720 * @returns VBox status code.
721 * @param pThis VBox disk container instance data.
722 * @param pIoReq I/O request to insert.
723 */
724static int drvramdiskMediaExIoReqInsert(PDRVRAMDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
725{
726 int rc = VINF_SUCCESS;
727 unsigned idxBin = drvramdiskMediaExIoReqIdHash(pIoReq->uIoReqId);
728
729 rc = RTSemFastMutexRequest(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
730 if (RT_SUCCESS(rc))
731 {
732 /* Search for conflicting I/O request ID. */
733 PPDMMEDIAEXIOREQINT pIt;
734 RTListForEach(&pThis->aIoReqAllocBins[idxBin].LstIoReqAlloc, pIt, PDMMEDIAEXIOREQINT, NdAllocatedList)
735 {
736 if (RT_UNLIKELY(pIt->uIoReqId == pIoReq->uIoReqId))
737 {
738 rc = VERR_PDM_MEDIAEX_IOREQID_CONFLICT;
739 break;
740 }
741 }
742 if (RT_SUCCESS(rc))
743 RTListAppend(&pThis->aIoReqAllocBins[idxBin].LstIoReqAlloc, &pIoReq->NdAllocatedList);
744 RTSemFastMutexRelease(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
745 }
746
747 return rc;
748}
749
750/**
751 * Removes the given I/O request from the list of allocated I/O requests.
752 *
753 * @returns VBox status code.
754 * @param pThis VBox disk container instance data.
755 * @param pIoReq I/O request to insert.
756 */
757static int drvramdiskMediaExIoReqRemove(PDRVRAMDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
758{
759 int rc = VINF_SUCCESS;
760 unsigned idxBin = drvramdiskMediaExIoReqIdHash(pIoReq->uIoReqId);
761
762 rc = RTSemFastMutexRequest(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
763 if (RT_SUCCESS(rc))
764 {
765 RTListNodeRemove(&pIoReq->NdAllocatedList);
766 RTSemFastMutexRelease(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
767 }
768
769 return rc;
770}
771
772/**
773 * I/O request completion worker.
774 *
775 * @returns VBox status code.
776 * @param pThis VBox disk container instance data.
777 * @param pIoReq I/O request to complete.
778 * @param rcReq The status code the request completed with.
779 * @param fUpNotify Flag whether to notify the driver/device above us about the completion.
780 */
781static int drvramdiskMediaExIoReqCompleteWorker(PDRVRAMDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, int rcReq, bool fUpNotify)
782{
783 int rc;
784 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_COMPLETING, VDIOREQSTATE_ACTIVE);
785 if (fXchg)
786 ASMAtomicDecU32(&pThis->cIoReqsActive);
787 else
788 {
789 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
790 rcReq = VERR_PDM_MEDIAEX_IOREQ_CANCELED;
791 }
792
793 ASMAtomicXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_COMPLETED);
794
795 /*
796 * Leave a release log entry if the request was active for more than 25 seconds
797 * (30 seconds is the timeout of the guest).
798 */
799 uint64_t tsNow = RTTimeMilliTS();
800 if (tsNow - pIoReq->tsSubmit >= 25 * 1000)
801 {
802 const char *pcszReq = NULL;
803
804 switch (pIoReq->enmType)
805 {
806 case PDMMEDIAEXIOREQTYPE_READ:
807 pcszReq = "Read";
808 break;
809 case PDMMEDIAEXIOREQTYPE_WRITE:
810 pcszReq = "Write";
811 break;
812 case PDMMEDIAEXIOREQTYPE_FLUSH:
813 pcszReq = "Flush";
814 break;
815 case PDMMEDIAEXIOREQTYPE_DISCARD:
816 pcszReq = "Discard";
817 break;
818 default:
819 pcszReq = "<Invalid>";
820 }
821
822 LogRel(("RamDisk#%u: %s request was active for %llu seconds\n",
823 pThis->pDrvIns->iInstance, pcszReq, (tsNow - pIoReq->tsSubmit) / 1000));
824 }
825
826 if (RT_FAILURE(rcReq))
827 {
828 /* Log the error. */
829 if (pThis->cErrors++ < 100)
830 {
831 if (rcReq == VERR_PDM_MEDIAEX_IOREQ_CANCELED)
832 {
833 if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_FLUSH)
834 LogRel(("RamDisk#%u: Aborted flush returned rc=%Rrc\n",
835 pThis->pDrvIns->iInstance, rcReq));
836 else
837 LogRel(("RamDisk#%u: Aborted %s (%u bytes left) returned rc=%Rrc\n",
838 pThis->pDrvIns->iInstance,
839 pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
840 ? "read"
841 : "write",
842 pIoReq->ReadWrite.cbReqLeft, rcReq));
843 }
844 else
845 {
846 if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_FLUSH)
847 LogRel(("RamDisk#%u: Flush returned rc=%Rrc\n",
848 pThis->pDrvIns->iInstance, rcReq));
849 else
850 LogRel(("RamDisk#%u: %s (%u bytes left) returned rc=%Rrc\n",
851 pThis->pDrvIns->iInstance,
852 pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
853 ? "Read"
854 : "Write",
855 pIoReq->ReadWrite.cbReqLeft, rcReq));
856 }
857 }
858 }
859
860 if (fUpNotify)
861 {
862 rc = pThis->pDrvMediaExPort->pfnIoReqCompleteNotify(pThis->pDrvMediaExPort,
863 pIoReq, &pIoReq->abAlloc[0], rcReq);
864 AssertRC(rc);
865 }
866
867 return rcReq;
868}
869
870/**
871 * Allocates a memory buffer suitable for I/O for the given request.
872 *
873 * @returns VBox status code.
874 * @param VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS if there is no I/O memory available to allocate and
875 * the request was placed on a waiting list.
876 * @param pThis VBox disk container instance data.
877 * @param pIoReq I/O request to allocate memory for.
878 * @param cb Size of the buffer.
879 */
880DECLINLINE(int) drvramdiskMediaExIoReqBufAlloc(PDRVRAMDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, size_t cb)
881{
882 int rc = IOBUFMgrAllocBuf(pThis->hIoBufMgr, &pIoReq->ReadWrite.IoBuf, cb, &pIoReq->ReadWrite.cbIoBuf);
883 if (rc == VERR_NO_MEMORY)
884 {
885 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
886 RTListAppend(&pThis->LstIoReqIoBufWait, &pIoReq->NdLstWait);
887 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
888 ASMAtomicIncU32(&pThis->cIoReqsWaiting);
889 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
890 }
891
892 return rc;
893}
894
895/**
896 * Worker for a read request.
897 *
898 * @returns VBox status code.
899 * @param pThis RAM disk container instance data.
900 * @param pIoReq The read request.
901 */
902static DECLCALLBACK(int) drvramdiskIoReqReadWorker(PDRVRAMDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
903{
904 size_t cbReqIo = RT_MIN(pIoReq->ReadWrite.cbReqLeft, pIoReq->ReadWrite.cbIoBuf);
905 int rc = drvramdiskReadWorker(pThis, &pIoReq->ReadWrite.IoBuf.SgBuf, pIoReq->ReadWrite.offStart,
906 cbReqIo);
907 drvramdiskMediaExIoReqComplete(pThis, pIoReq, rc);
908 return VINF_SUCCESS;
909}
910
911/**
912 * Worker for a read request.
913 *
914 * @returns VBox status code.
915 * @param pThis RAM disk container instance data.
916 * @param pIoReq The read request.
917 */
918static DECLCALLBACK(int) drvramdiskIoReqWriteWorker(PDRVRAMDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
919{
920 size_t cbReqIo = RT_MIN(pIoReq->ReadWrite.cbReqLeft, pIoReq->ReadWrite.cbIoBuf);
921 int rc = drvramdiskWriteWorker(pThis, &pIoReq->ReadWrite.IoBuf.SgBuf, pIoReq->ReadWrite.offStart,
922 cbReqIo);
923 drvramdiskMediaExIoReqComplete(pThis, pIoReq, rc);
924 return VINF_SUCCESS;
925}
926
927/**
928 * Processes a read/write request.
929 *
930 * @returns VBox status code.
931 * @param pThis VBox disk container instance data.
932 * @param pIoReq I/O request to process.
933 * @param fUpNotify Flag whether to notify the driver/device above us about the completion.
934 */
935static int drvramdiskMediaExIoReqReadWriteProcess(PDRVRAMDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, bool fUpNotify)
936{
937 int rc = VINF_SUCCESS;
938
939 Assert(pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE);
940
941 while ( pIoReq->ReadWrite.cbReqLeft
942 && rc == VINF_SUCCESS)
943 {
944 if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ)
945 rc = RTReqQueueCallEx(pThis->hReqQ, NULL, 0, RTREQFLAGS_NO_WAIT,
946 (PFNRT)drvramdiskIoReqReadWorker, 2, pThis, pIoReq);
947 else
948 {
949 /* Sync memory buffer from the request initiator. */
950 rc = drvramdiskMediaExIoReqBufSync(pThis, pIoReq, true /* fToIoBuf */);
951 if (RT_SUCCESS(rc))
952 rc = RTReqQueueCallEx(pThis->hReqQ, NULL, 0, RTREQFLAGS_NO_WAIT,
953 (PFNRT)drvramdiskIoReqWriteWorker, 2, pThis, pIoReq);
954 }
955
956 if (rc == VINF_SUCCESS)
957 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
958 }
959
960 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
961 {
962 Assert(!pIoReq->ReadWrite.cbReqLeft || RT_FAILURE(rc));
963 rc = drvramdiskMediaExIoReqCompleteWorker(pThis, pIoReq, rc, fUpNotify);
964 }
965
966 return rc;
967}
968
969
970/**
971 * Frees a I/O memory buffer allocated previously.
972 *
973 * @returns nothing.
974 * @param pThis VBox disk container instance data.
975 * @param pIoReq I/O request for which to free memory.
976 */
977DECLINLINE(void) drvramdiskMediaExIoReqBufFree(PDRVRAMDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
978{
979 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
980 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
981 {
982 IOBUFMgrFreeBuf(&pIoReq->ReadWrite.IoBuf);
983
984 if (ASMAtomicReadU32(&pThis->cIoReqsWaiting) > 0)
985 {
986 /* Try to process as many requests as possible. */
987 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
988 PPDMMEDIAEXIOREQINT pIoReqCur, pIoReqNext;
989
990 RTListForEachSafe(&pThis->LstIoReqIoBufWait, pIoReqCur, pIoReqNext, PDMMEDIAEXIOREQINT, NdLstWait)
991 {
992 /* Allocate a suitable I/O buffer for this request. */
993 int rc = IOBUFMgrAllocBuf(pThis->hIoBufMgr, &pIoReqCur->ReadWrite.IoBuf, pIoReqCur->ReadWrite.cbReq,
994 &pIoReqCur->ReadWrite.cbIoBuf);
995 if (rc == VINF_SUCCESS)
996 {
997 ASMAtomicDecU32(&pThis->cIoReqsWaiting);
998 RTListNodeRemove(&pIoReqCur->NdLstWait);
999
1000 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReqCur->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
1001 if (RT_UNLIKELY(!fXchg))
1002 {
1003 /* Must have been canceled inbetween. */
1004 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
1005 drvramdiskMediaExIoReqCompleteWorker(pThis, pIoReqCur, VERR_PDM_MEDIAEX_IOREQ_CANCELED, true /* fUpNotify */);
1006 }
1007 ASMAtomicIncU32(&pThis->cIoReqsActive);
1008 rc = drvramdiskMediaExIoReqReadWriteProcess(pThis, pIoReqCur, true /* fUpNotify */);
1009 }
1010 else
1011 {
1012 Assert(rc == VERR_NO_MEMORY);
1013 break;
1014 }
1015 }
1016 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
1017 }
1018 }
1019}
1020
1021
1022/**
1023 * Returns whether the VM is in a running state.
1024 *
1025 * @returns Flag indicating whether the VM is currently in a running state.
1026 * @param pThis VBox disk container instance data.
1027 */
1028DECLINLINE(bool) drvramdiskMediaExIoReqIsVmRunning(PDRVRAMDISK pThis)
1029{
1030 VMSTATE enmVmState = PDMDrvHlpVMState(pThis->pDrvIns);
1031 if ( enmVmState == VMSTATE_RESUMING
1032 || enmVmState == VMSTATE_RUNNING
1033 || enmVmState == VMSTATE_RUNNING_LS
1034 || enmVmState == VMSTATE_RUNNING_FT
1035 || enmVmState == VMSTATE_RESETTING
1036 || enmVmState == VMSTATE_RESETTING_LS
1037 || enmVmState == VMSTATE_SOFT_RESETTING
1038 || enmVmState == VMSTATE_SOFT_RESETTING_LS
1039 || enmVmState == VMSTATE_SUSPENDING
1040 || enmVmState == VMSTATE_SUSPENDING_LS
1041 || enmVmState == VMSTATE_SUSPENDING_EXT_LS)
1042 return true;
1043
1044 return false;
1045}
1046
1047/**
1048 * @copydoc FNVDASYNCTRANSFERCOMPLETE
1049 */
1050static void drvramdiskMediaExIoReqComplete(PDRVRAMDISK pThis, PPDMMEDIAEXIOREQINT pIoReq,
1051 int rcReq)
1052{
1053 /*
1054 * For a read we need to sync the memory before continuing to process
1055 * the request further.
1056 */
1057 if ( RT_SUCCESS(rcReq)
1058 && pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ)
1059 rcReq = drvramdiskMediaExIoReqBufSync(pThis, pIoReq, false /* fToIoBuf */);
1060
1061 /*
1062 * When the request owner instructs us to handle recoverable errors like full disks
1063 * do it. Mark the request as suspended, notify the owner and put the request on the
1064 * redo list.
1065 */
1066 if ( RT_FAILURE(rcReq)
1067 && (pIoReq->fFlags & PDMIMEDIAEX_F_SUSPEND_ON_RECOVERABLE_ERR)
1068 && drvramdiskMediaExIoReqIsRedoSetWarning(pThis, rcReq))
1069 {
1070 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_SUSPENDED, VDIOREQSTATE_ACTIVE);
1071 if (fXchg)
1072 {
1073 /* Put on redo list and adjust active request counter. */
1074 RTCritSectEnter(&pThis->CritSectIoReqRedo);
1075 RTListAppend(&pThis->LstIoReqRedo, &pIoReq->NdLstWait);
1076 RTCritSectLeave(&pThis->CritSectIoReqRedo);
1077 ASMAtomicDecU32(&pThis->cIoReqsActive);
1078 pThis->pDrvMediaExPort->pfnIoReqStateChanged(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
1079 PDMMEDIAEXIOREQSTATE_SUSPENDED);
1080 }
1081 else
1082 {
1083 /* Request was canceled inbetween, so don't care and notify the owner about the completed request. */
1084 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
1085 drvramdiskMediaExIoReqCompleteWorker(pThis, pIoReq, rcReq, true /* fUpNotify */);
1086 }
1087 }
1088 else
1089 {
1090 /* Adjust the remaining amount to transfer. */
1091 size_t cbReqIo = RT_MIN(pIoReq->ReadWrite.cbReqLeft, pIoReq->ReadWrite.cbIoBuf);
1092 pIoReq->ReadWrite.offStart += cbReqIo;
1093 pIoReq->ReadWrite.cbReqLeft -= cbReqIo;
1094
1095 if ( RT_FAILURE(rcReq)
1096 || !pIoReq->ReadWrite.cbReqLeft
1097 || ( pIoReq->enmType != PDMMEDIAEXIOREQTYPE_READ
1098 && pIoReq->enmType != PDMMEDIAEXIOREQTYPE_WRITE))
1099 drvramdiskMediaExIoReqCompleteWorker(pThis, pIoReq, rcReq, true /* fUpNotify */);
1100 else
1101 drvramdiskMediaExIoReqReadWriteProcess(pThis, pIoReq, true /* fUpNotify */);
1102 }
1103}
1104
1105/**
1106 * Worker for a flush request.
1107 *
1108 * @returns VBox status code.
1109 * @param pThis RAM disk container instance data.
1110 * @param pIoReq The flush request.
1111 */
1112static DECLCALLBACK(int) drvramdiskIoReqFlushWorker(PDRVRAMDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
1113{
1114 /* Nothing to do for a ram disk. */
1115 drvramdiskMediaExIoReqComplete(pThis, pIoReq, VINF_SUCCESS);
1116 return VINF_SUCCESS;
1117}
1118
1119/**
1120 * Worker for a discard request.
1121 *
1122 * @returns VBox status code.
1123 * @param pThis RAM disk container instance data.
1124 * @param pIoReq The discard request.
1125 */
1126static DECLCALLBACK(int) drvramdiskIoReqDiscardWorker(PDRVRAMDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
1127{
1128 int rc = drvramdiskDiscardRecords(pThis, pIoReq->Discard.paRanges, pIoReq->Discard.cRanges);
1129 drvramdiskMediaExIoReqComplete(pThis, pIoReq, rc);
1130 return VINF_SUCCESS;
1131}
1132
1133/**
1134 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqAllocSizeSet}
1135 */
1136static DECLCALLBACK(int) drvramdiskIoReqAllocSizeSet(PPDMIMEDIAEX pInterface, size_t cbIoReqAlloc)
1137{
1138 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMediaEx);
1139
1140 if (RT_UNLIKELY(pThis->hIoReqCache != NIL_RTMEMCACHE))
1141 return VERR_INVALID_STATE;
1142
1143 return RTMemCacheCreate(&pThis->hIoReqCache, sizeof(PDMMEDIAEXIOREQINT) + cbIoReqAlloc, 0, UINT32_MAX,
1144 NULL, NULL, NULL, 0);
1145}
1146
1147/**
1148 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqAlloc}
1149 */
1150static DECLCALLBACK(int) drvramdiskIoReqAlloc(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq, void **ppvIoReqAlloc,
1151 PDMMEDIAEXIOREQID uIoReqId, uint32_t fFlags)
1152{
1153 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMediaEx);
1154
1155 AssertReturn(!(fFlags & ~PDMIMEDIAEX_F_VALID), VERR_INVALID_PARAMETER);
1156
1157 PPDMMEDIAEXIOREQINT pIoReq = (PPDMMEDIAEXIOREQINT)RTMemCacheAlloc(pThis->hIoReqCache);
1158
1159 if (RT_UNLIKELY(!pIoReq))
1160 return VERR_NO_MEMORY;
1161
1162 pIoReq->uIoReqId = uIoReqId;
1163 pIoReq->fFlags = fFlags;
1164 pIoReq->pDisk = pThis;
1165 pIoReq->enmState = VDIOREQSTATE_ALLOCATED;
1166 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_INVALID;
1167
1168 int rc = drvramdiskMediaExIoReqInsert(pThis, pIoReq);
1169 if (RT_SUCCESS(rc))
1170 {
1171 *phIoReq = pIoReq;
1172 *ppvIoReqAlloc = &pIoReq->abAlloc[0];
1173 }
1174 else
1175 RTMemCacheFree(pThis->hIoReqCache, pIoReq);
1176
1177 return rc;
1178}
1179
1180/**
1181 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqFree}
1182 */
1183static DECLCALLBACK(int) drvramdiskIoReqFree(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
1184{
1185 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMediaEx);
1186 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
1187
1188 if ( pIoReq->enmState != VDIOREQSTATE_COMPLETED
1189 && pIoReq->enmState != VDIOREQSTATE_ALLOCATED)
1190 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
1191
1192 /* Remove from allocated list. */
1193 int rc = drvramdiskMediaExIoReqRemove(pThis, pIoReq);
1194 if (RT_FAILURE(rc))
1195 return rc;
1196
1197 /* Free any associated I/O memory. */
1198 drvramdiskMediaExIoReqBufFree(pThis, pIoReq);
1199
1200 /* For discard request discard the range array. */
1201 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD
1202 && pIoReq->Discard.paRanges)
1203 {
1204 RTMemFree(pIoReq->Discard.paRanges);
1205 pIoReq->Discard.paRanges = NULL;
1206 }
1207
1208 pIoReq->enmState = VDIOREQSTATE_FREE;
1209 RTMemCacheFree(pThis->hIoReqCache, pIoReq);
1210 return VINF_SUCCESS;
1211}
1212
1213/**
1214 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancel}
1215 */
1216static DECLCALLBACK(int) drvramdiskIoReqCancel(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQID uIoReqId)
1217{
1218 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMediaEx);
1219 unsigned idxBin = drvramdiskMediaExIoReqIdHash(uIoReqId);
1220
1221 int rc = RTSemFastMutexRequest(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
1222 if (RT_SUCCESS(rc))
1223 {
1224 /* Search for I/O request with ID. */
1225 PPDMMEDIAEXIOREQINT pIt;
1226 rc = VERR_PDM_MEDIAEX_IOREQID_NOT_FOUND;
1227
1228 RTListForEach(&pThis->aIoReqAllocBins[idxBin].LstIoReqAlloc, pIt, PDMMEDIAEXIOREQINT, NdAllocatedList)
1229 {
1230 if (pIt->uIoReqId == uIoReqId)
1231 {
1232 bool fXchg = true;
1233 VDIOREQSTATE enmStateOld = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIt->enmState);
1234
1235 /*
1236 * We might have to try canceling the request multiple times if it transitioned from
1237 * ALLOCATED to ACTIVE or to SUSPENDED between reading the state and trying to change it.
1238 */
1239 while ( ( enmStateOld == VDIOREQSTATE_ALLOCATED
1240 || enmStateOld == VDIOREQSTATE_ACTIVE
1241 || enmStateOld == VDIOREQSTATE_SUSPENDED)
1242 && !fXchg)
1243 {
1244 fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIt->enmState, VDIOREQSTATE_CANCELED, enmStateOld);
1245 if (!fXchg)
1246 enmStateOld = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIt->enmState);
1247 }
1248
1249 if (fXchg)
1250 {
1251 ASMAtomicDecU32(&pThis->cIoReqsActive);
1252 rc = VINF_SUCCESS;
1253 }
1254 break;
1255 }
1256 }
1257 RTSemFastMutexRelease(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
1258 }
1259
1260 return rc;
1261}
1262
1263/**
1264 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqRead}
1265 */
1266static DECLCALLBACK(int) drvramdiskIoReqRead(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbRead)
1267{
1268 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMediaEx);
1269 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
1270 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
1271
1272 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
1273 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
1274
1275 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
1276 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
1277
1278 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_READ;
1279 pIoReq->tsSubmit = RTTimeMilliTS();
1280 pIoReq->ReadWrite.offStart = off;
1281 pIoReq->ReadWrite.cbReq = cbRead;
1282 pIoReq->ReadWrite.cbReqLeft = cbRead;
1283 /* Allocate a suitable I/O buffer for this request. */
1284 int rc = drvramdiskMediaExIoReqBufAlloc(pThis, pIoReq, cbRead);
1285 if (rc == VINF_SUCCESS)
1286 {
1287 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
1288 if (RT_UNLIKELY(!fXchg))
1289 {
1290 /* Must have been canceled inbetween. */
1291 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
1292 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
1293 }
1294 ASMAtomicIncU32(&pThis->cIoReqsActive);
1295
1296 rc = drvramdiskMediaExIoReqReadWriteProcess(pThis, pIoReq, false /* fUpNotify */);
1297 }
1298
1299 return rc;
1300}
1301
1302/**
1303 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqWrite}
1304 */
1305static DECLCALLBACK(int) drvramdiskIoReqWrite(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbWrite)
1306{
1307 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMediaEx);
1308 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
1309 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
1310
1311 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
1312 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
1313
1314 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
1315 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
1316
1317 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_WRITE;
1318 pIoReq->tsSubmit = RTTimeMilliTS();
1319 pIoReq->ReadWrite.offStart = off;
1320 pIoReq->ReadWrite.cbReq = cbWrite;
1321 pIoReq->ReadWrite.cbReqLeft = cbWrite;
1322 /* Allocate a suitable I/O buffer for this request. */
1323 int rc = drvramdiskMediaExIoReqBufAlloc(pThis, pIoReq, cbWrite);
1324 if (rc == VINF_SUCCESS)
1325 {
1326 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
1327 if (RT_UNLIKELY(!fXchg))
1328 {
1329 /* Must have been canceled inbetween. */
1330 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
1331 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
1332 }
1333 ASMAtomicIncU32(&pThis->cIoReqsActive);
1334
1335 rc = drvramdiskMediaExIoReqReadWriteProcess(pThis, pIoReq, false /* fUpNotify */);
1336 }
1337
1338 return rc;
1339}
1340
1341/**
1342 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqFlush}
1343 */
1344static DECLCALLBACK(int) drvramdiskIoReqFlush(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
1345{
1346 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMediaEx);
1347 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
1348 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
1349
1350 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
1351 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
1352
1353 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
1354 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
1355
1356 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_FLUSH;
1357 pIoReq->tsSubmit = RTTimeMilliTS();
1358 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
1359 if (RT_UNLIKELY(!fXchg))
1360 {
1361 /* Must have been canceled inbetween. */
1362 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
1363 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
1364 }
1365
1366 ASMAtomicIncU32(&pThis->cIoReqsActive);
1367 return RTReqQueueCallEx(pThis->hReqQ, NULL, 0, RTREQFLAGS_NO_WAIT,
1368 (PFNRT)drvramdiskIoReqFlushWorker, 2, pThis, pIoReq);
1369}
1370
1371/**
1372 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqDiscard}
1373 */
1374static DECLCALLBACK(int) drvramdiskIoReqDiscard(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, unsigned cRangesMax)
1375{
1376 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMediaEx);
1377 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
1378 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
1379
1380 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
1381 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
1382
1383 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
1384 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
1385
1386 /* Copy the ranges over now, this can be optimized in the future. */
1387 pIoReq->Discard.paRanges = (PRTRANGE)RTMemAllocZ(cRangesMax * sizeof(RTRANGE));
1388 if (RT_UNLIKELY(!pIoReq->Discard.paRanges))
1389 return VERR_NO_MEMORY;
1390
1391 int rc = pThis->pDrvMediaExPort->pfnIoReqQueryDiscardRanges(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
1392 0, cRangesMax, pIoReq->Discard.paRanges,
1393 &pIoReq->Discard.cRanges);
1394 if (RT_SUCCESS(rc))
1395 {
1396 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_DISCARD;
1397 pIoReq->tsSubmit = RTTimeMilliTS();
1398
1399 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
1400 if (RT_UNLIKELY(!fXchg))
1401 {
1402 /* Must have been canceled inbetween. */
1403 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
1404 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
1405 }
1406
1407 ASMAtomicIncU32(&pThis->cIoReqsActive);
1408
1409 rc = RTReqQueueCallEx(pThis->hReqQ, NULL, 0, RTREQFLAGS_NO_WAIT,
1410 (PFNRT)drvramdiskIoReqDiscardWorker, 2, pThis, pIoReq);
1411 }
1412
1413 return rc;
1414}
1415
1416/**
1417 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetActiveCount}
1418 */
1419static DECLCALLBACK(uint32_t) drvramdiskIoReqGetActiveCount(PPDMIMEDIAEX pInterface)
1420{
1421 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMediaEx);
1422 return ASMAtomicReadU32(&pThis->cIoReqsActive);
1423}
1424
1425/**
1426 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetSuspendedCount}
1427 */
1428static DECLCALLBACK(uint32_t) drvramdiskIoReqGetSuspendedCount(PPDMIMEDIAEX pInterface)
1429{
1430 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMediaEx);
1431
1432 AssertReturn(!drvramdiskMediaExIoReqIsVmRunning(pThis), 0);
1433
1434 uint32_t cIoReqSuspended = 0;
1435 PPDMMEDIAEXIOREQINT pIoReq;
1436 RTCritSectEnter(&pThis->CritSectIoReqRedo);
1437 RTListForEach(&pThis->LstIoReqRedo, pIoReq, PDMMEDIAEXIOREQINT, NdLstWait)
1438 {
1439 cIoReqSuspended++;
1440 }
1441 RTCritSectLeave(&pThis->CritSectIoReqRedo);
1442
1443 return cIoReqSuspended;
1444}
1445
1446/**
1447 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedFirst}
1448 */
1449static DECLCALLBACK(int) drvramdiskIoReqQuerySuspendedStart(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq,
1450 void **ppvIoReqAlloc)
1451{
1452 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMediaEx);
1453
1454 AssertReturn(!drvramdiskMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
1455 AssertReturn(!RTListIsEmpty(&pThis->LstIoReqRedo), VERR_NOT_FOUND);
1456
1457 RTCritSectEnter(&pThis->CritSectIoReqRedo);
1458 PPDMMEDIAEXIOREQINT pIoReq = RTListGetFirst(&pThis->LstIoReqRedo, PDMMEDIAEXIOREQINT, NdLstWait);
1459 *phIoReq = pIoReq;
1460 *ppvIoReqAlloc = &pIoReq->abAlloc[0];
1461 RTCritSectLeave(&pThis->CritSectIoReqRedo);
1462
1463 return VINF_SUCCESS;
1464}
1465
1466/**
1467 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedNext}
1468 */
1469static DECLCALLBACK(int) drvramdiskIoReqQuerySuspendedNext(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq,
1470 PPDMMEDIAEXIOREQ phIoReqNext, void **ppvIoReqAllocNext)
1471{
1472 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMediaEx);
1473 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
1474
1475 AssertReturn(!drvramdiskMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
1476 AssertPtrReturn(pIoReq, VERR_INVALID_HANDLE);
1477 AssertReturn(!RTListNodeIsLast(&pThis->LstIoReqRedo, &pIoReq->NdLstWait), VERR_NOT_FOUND);
1478
1479 RTCritSectEnter(&pThis->CritSectIoReqRedo);
1480 PPDMMEDIAEXIOREQINT pIoReqNext = RTListNodeGetNext(&pIoReq->NdLstWait, PDMMEDIAEXIOREQINT, NdLstWait);
1481 *phIoReqNext = pIoReqNext;
1482 *ppvIoReqAllocNext = &pIoReqNext->abAlloc[0];
1483 RTCritSectLeave(&pThis->CritSectIoReqRedo);
1484
1485 return VINF_SUCCESS;
1486}
1487
1488/**
1489 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedSave}
1490 */
1491static DECLCALLBACK(int) drvramdiskIoReqSuspendedSave(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
1492{
1493 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMediaEx);
1494 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
1495
1496 RT_NOREF1(pSSM);
1497
1498 AssertReturn(!drvramdiskMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
1499 AssertPtrReturn(pIoReq, VERR_INVALID_HANDLE);
1500 AssertReturn(pIoReq->enmState == VDIOREQSTATE_SUSPENDED, VERR_INVALID_STATE);
1501
1502 return VERR_NOT_IMPLEMENTED;
1503}
1504
1505/**
1506 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedLoad}
1507 */
1508static DECLCALLBACK(int) drvramdiskIoReqSuspendedLoad(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
1509{
1510 PDRVRAMDISK pThis = RT_FROM_MEMBER(pInterface, DRVRAMDISK, IMediaEx);
1511 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
1512
1513 RT_NOREF1(pSSM);
1514
1515 AssertReturn(!drvramdiskMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
1516 AssertPtrReturn(pIoReq, VERR_INVALID_HANDLE);
1517 AssertReturn(pIoReq->enmState == VDIOREQSTATE_ALLOCATED, VERR_INVALID_STATE);
1518
1519 return VERR_NOT_IMPLEMENTED;
1520}
1521
1522static DECLCALLBACK(int) drvramdiskIoReqWorker(RTTHREAD hThrdSelf, void *pvUser)
1523{
1524 int rc = VINF_SUCCESS;
1525 PDRVRAMDISK pThis = (PDRVRAMDISK)pvUser;
1526
1527 RT_NOREF1(hThrdSelf);
1528
1529 do
1530 {
1531 rc = RTReqQueueProcess(pThis->hReqQ, RT_INDEFINITE_WAIT);
1532 } while (RT_SUCCESS(rc));
1533
1534 return VINF_SUCCESS;
1535}
1536
1537/* -=-=-=-=- IBase -=-=-=-=- */
1538
1539/**
1540 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1541 */
1542static DECLCALLBACK(void *) drvramdiskQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1543{
1544 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1545 PDRVRAMDISK pThis = PDMINS_2_DATA(pDrvIns, PDRVRAMDISK);
1546
1547 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1548 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
1549 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEX, &pThis->IMediaEx);
1550
1551 return NULL;
1552}
1553
1554
1555/* -=-=-=-=- driver interface -=-=-=-=- */
1556
1557static DECLCALLBACK(int) drvramdiskTreeDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
1558{
1559 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)pNode;
1560
1561 RT_NOREF1(pvUser);
1562
1563 RTMemFree(pSeg->pbSeg);
1564 RTMemFree(pSeg);
1565 return VINF_SUCCESS;
1566}
1567
1568/**
1569 * @copydoc FNPDMDRVDESTRUCT
1570 */
1571static DECLCALLBACK(void) drvramdiskDestruct(PPDMDRVINS pDrvIns)
1572{
1573 PDRVRAMDISK pThis = PDMINS_2_DATA(pDrvIns, PDRVRAMDISK);
1574
1575 if (pThis->pTreeSegments)
1576 {
1577 RTAvlrFileOffsetDestroy(pThis->pTreeSegments, drvramdiskTreeDestroy, NULL);
1578 RTMemFree(pThis->pTreeSegments);
1579 }
1580}
1581
1582/**
1583 * Construct a disk integrity driver instance.
1584 *
1585 * @copydoc FNPDMDRVCONSTRUCT
1586 */
1587static DECLCALLBACK(int) drvramdiskConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1588{
1589 RT_NOREF1(fFlags);
1590 int rc = VINF_SUCCESS;
1591 uint32_t cbIoBufMax;
1592 PDRVRAMDISK pThis = PDMINS_2_DATA(pDrvIns, PDRVRAMDISK);
1593 LogFlow(("drvdiskintConstruct: iInstance=%d\n", pDrvIns->iInstance));
1594 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1595
1596 /*
1597 * Validate configuration.
1598 */
1599 if (!CFGMR3AreValuesValid(pCfg, "Size\0"
1600 "PreAlloc\0"
1601 "IoBufMax\0"
1602 "SectorSize\0"))
1603 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
1604
1605 rc = CFGMR3QueryU64(pCfg, "Size", &pThis->cbDisk);
1606 if (RT_FAILURE(rc))
1607 return PDMDRV_SET_ERROR(pDrvIns, rc,
1608 N_("RamDisk: Error querying the media size"));
1609 rc = CFGMR3QueryBoolDef(pCfg, "PreAlloc", &pThis->fPreallocRamDisk, false);
1610 AssertRC(rc);
1611 rc = CFGMR3QueryU32Def(pCfg, "IoBufMax", &cbIoBufMax, 5 * _1M);
1612 if (RT_FAILURE(rc))
1613 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"IoBufMax\" from the config"));
1614 rc = CFGMR3QueryU32Def(pCfg, "SectorSize", &pThis->cbSector, 512);
1615 if (RT_FAILURE(rc))
1616 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"SectorSize\" from the config"));
1617
1618 /*
1619 * Initialize most of the data members.
1620 */
1621 pThis->pDrvIns = pDrvIns;
1622
1623 /* IBase. */
1624 pDrvIns->IBase.pfnQueryInterface = drvramdiskQueryInterface;
1625
1626 /* IMedia */
1627 pThis->IMedia.pfnRead = drvramdiskRead;
1628 pThis->IMedia.pfnWrite = drvramdiskWrite;
1629 pThis->IMedia.pfnFlush = drvramdiskFlush;
1630 pThis->IMedia.pfnGetSize = drvramdiskGetSize;
1631 pThis->IMedia.pfnBiosIsVisible = drvramdiskBiosIsVisible;
1632 pThis->IMedia.pfnGetType = drvramdiskGetType;
1633 pThis->IMedia.pfnIsReadOnly = drvramdiskIsReadOnly;
1634 pThis->IMedia.pfnBiosGetPCHSGeometry = drvramdiskBiosGetPCHSGeometry;
1635 pThis->IMedia.pfnBiosSetPCHSGeometry = drvramdiskBiosSetPCHSGeometry;
1636 pThis->IMedia.pfnBiosGetLCHSGeometry = drvramdiskBiosGetLCHSGeometry;
1637 pThis->IMedia.pfnBiosSetLCHSGeometry = drvramdiskBiosSetLCHSGeometry;
1638 pThis->IMedia.pfnGetUuid = drvramdiskGetUuid;
1639 pThis->IMedia.pfnGetSectorSize = drvramdiskGetSectorSize;
1640 pThis->IMedia.pfnReadPcBios = drvramdiskReadPcBios;
1641 pThis->IMedia.pfnDiscard = drvramdiskDiscard;
1642
1643 /* IMediaEx */
1644 pThis->IMediaEx.pfnIoReqAllocSizeSet = drvramdiskIoReqAllocSizeSet;
1645 pThis->IMediaEx.pfnIoReqAlloc = drvramdiskIoReqAlloc;
1646 pThis->IMediaEx.pfnIoReqFree = drvramdiskIoReqFree;
1647 pThis->IMediaEx.pfnIoReqCancel = drvramdiskIoReqCancel;
1648 pThis->IMediaEx.pfnIoReqRead = drvramdiskIoReqRead;
1649 pThis->IMediaEx.pfnIoReqWrite = drvramdiskIoReqWrite;
1650 pThis->IMediaEx.pfnIoReqFlush = drvramdiskIoReqFlush;
1651 pThis->IMediaEx.pfnIoReqDiscard = drvramdiskIoReqDiscard;
1652 pThis->IMediaEx.pfnIoReqGetActiveCount = drvramdiskIoReqGetActiveCount;
1653 pThis->IMediaEx.pfnIoReqGetSuspendedCount = drvramdiskIoReqGetSuspendedCount;
1654 pThis->IMediaEx.pfnIoReqQuerySuspendedStart = drvramdiskIoReqQuerySuspendedStart;
1655 pThis->IMediaEx.pfnIoReqQuerySuspendedNext = drvramdiskIoReqQuerySuspendedNext;
1656 pThis->IMediaEx.pfnIoReqSuspendedSave = drvramdiskIoReqSuspendedSave;
1657 pThis->IMediaEx.pfnIoReqSuspendedLoad = drvramdiskIoReqSuspendedLoad;
1658
1659 /* Query the media port interface above us. */
1660 pThis->pDrvMediaPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAPORT);
1661 if (!pThis->pDrvMediaPort)
1662 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
1663 N_("No media port interface above"));
1664
1665 /* Try to attach extended media port interface above.*/
1666 pThis->pDrvMediaExPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAEXPORT);
1667 if (pThis->pDrvMediaExPort)
1668 {
1669 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aIoReqAllocBins); i++)
1670 {
1671 rc = RTSemFastMutexCreate(&pThis->aIoReqAllocBins[i].hMtxLstIoReqAlloc);
1672 if (RT_FAILURE(rc))
1673 break;
1674 RTListInit(&pThis->aIoReqAllocBins[i].LstIoReqAlloc);
1675 }
1676
1677 if (RT_SUCCESS(rc))
1678 rc = RTCritSectInit(&pThis->CritSectIoReqsIoBufWait);
1679
1680 if (RT_SUCCESS(rc))
1681 rc = RTCritSectInit(&pThis->CritSectIoReqRedo);
1682
1683 if (RT_FAILURE(rc))
1684 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Creating Mutex failed"));
1685
1686 RTListInit(&pThis->LstIoReqIoBufWait);
1687 RTListInit(&pThis->LstIoReqRedo);
1688 }
1689
1690 /* Create the AVL tree. */
1691 pThis->pTreeSegments = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
1692 if (!pThis->pTreeSegments)
1693 rc = VERR_NO_MEMORY;
1694
1695 if (pThis->pDrvMediaExPort)
1696 {
1697 rc = RTReqQueueCreate(&pThis->hReqQ);
1698 if (RT_SUCCESS(rc))
1699 {
1700 /* Spin up the worker thread. */
1701 rc = RTThreadCreate(&pThis->hThrdWrk, drvramdiskIoReqWorker, pThis, 0,
1702 RTTHREADTYPE_IO, 0, "RAMDSK");
1703 }
1704 }
1705
1706 if (pThis->pDrvMediaExPort)
1707 rc = IOBUFMgrCreate(&pThis->hIoBufMgr, cbIoBufMax, IOBUFMGR_F_DEFAULT);
1708
1709 /* Read in all data before the start if requested. */
1710 if ( RT_SUCCESS(rc)
1711 && pThis->fPreallocRamDisk)
1712 {
1713 LogRel(("RamDisk: Preallocating RAM disk...\n"));
1714 return VERR_NOT_IMPLEMENTED;
1715 }
1716
1717 return rc;
1718}
1719
1720
1721/**
1722 * Block driver registration record.
1723 */
1724const PDMDRVREG g_DrvRamDisk =
1725{
1726 /* u32Version */
1727 PDM_DRVREG_VERSION,
1728 /* szName */
1729 "RamDisk",
1730 /* szRCMod */
1731 "",
1732 /* szR0Mod */
1733 "",
1734 /* pszDescription */
1735 "RAM disk driver.",
1736 /* fFlags */
1737 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1738 /* fClass. */
1739 PDM_DRVREG_CLASS_BLOCK,
1740 /* cMaxInstances */
1741 ~0U,
1742 /* cbInstance */
1743 sizeof(DRVRAMDISK),
1744 /* pfnConstruct */
1745 drvramdiskConstruct,
1746 /* pfnDestruct */
1747 drvramdiskDestruct,
1748 /* pfnRelocate */
1749 NULL,
1750 /* pfnIOCtl */
1751 NULL,
1752 /* pfnPowerOn */
1753 NULL,
1754 /* pfnReset */
1755 NULL,
1756 /* pfnSuspend */
1757 NULL,
1758 /* pfnResume */
1759 NULL,
1760 /* pfnAttach */
1761 NULL,
1762 /* pfnDetach */
1763 NULL,
1764 /* pfnPowerOff */
1765 NULL,
1766 /* pfnSoftReset */
1767 NULL,
1768 /* u32EndVersion */
1769 PDM_DRVREG_VERSION
1770};
1771
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