VirtualBox

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

Last change on this file since 99775 was 99775, checked in by vboxsync, 18 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

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