VirtualBox

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

Last change on this file since 107445 was 107445, checked in by vboxsync, 6 weeks ago

Devices/Storage/DrvRamDisk.cpp: Remove unused variable, remove unused assignment (not required as the called function will propagate the status code to the driver above because the fUpNotify parameter is true), bugref:3409

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