VirtualBox

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

Last change on this file since 90330 was 87560, checked in by vboxsync, 4 years ago

Devics/Storage/DrvRamDisk.cpp: Implement missing interface callback causing a NULL pointer access on VM suspend

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