VirtualBox

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

Last change on this file since 102468 was 99887, checked in by vboxsync, 19 months ago

Devices/DrvRamDisk: Return VERR_PDM_GEOMETRY_NOT_SET to indicate that the caller has to resort to some other method for deducing the CHS geometry

  • 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 99887 2023-05-22 10:33:51Z 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_PDM_GEOMETRY_NOT_SET;
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_PDM_GEOMETRY_NOT_SET;
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