VirtualBox

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

Last change on this file since 68080 was 65713, checked in by vboxsync, 8 years ago

Devices/DrvRamDisk: small memory leak during termination

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