VirtualBox

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

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

Storage/DrvRamDisk: warning fixes for release builds

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