VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvVD.cpp

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 207.3 KB
Line 
1/* $Id: DrvVD.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * DrvVD - Generic VBox disk media driver.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DRV_VD
33#include <VBox/vd.h>
34#include <VBox/vmm/pdmdrv.h>
35#include <VBox/vmm/pdmstorageifs.h>
36#include <VBox/vmm/pdmasynccompletion.h>
37#include <VBox/vmm/pdmblkcache.h>
38#include <VBox/vmm/ssm.h>
39#include <iprt/asm.h>
40#include <iprt/alloc.h>
41#include <iprt/assert.h>
42#include <iprt/uuid.h>
43#include <iprt/file.h>
44#include <iprt/string.h>
45#include <iprt/semaphore.h>
46#include <iprt/sg.h>
47#include <iprt/system.h>
48#include <iprt/memsafer.h>
49#include <iprt/memcache.h>
50#include <iprt/list.h>
51
52#ifdef VBOX_WITH_INIP
53/* All lwip header files are not C++ safe. So hack around this. */
54RT_C_DECLS_BEGIN
55#include <lwip/opt.h>
56#include <lwip/inet.h>
57#include <lwip/tcp.h>
58#include <lwip/sockets.h>
59# if LWIP_IPV6
60# include <lwip/inet6.h>
61# endif
62RT_C_DECLS_END
63#endif /* VBOX_WITH_INIP */
64
65#include "HBDMgmt.h"
66#include "IOBufMgmt.h"
67
68#include "VBoxDD.h"
69
70#ifdef VBOX_WITH_INIP
71/* Small hack to get at lwIP initialized status */
72extern bool DevINIPConfigured(void);
73#endif /* VBOX_WITH_INIP */
74
75
76/** @def VBOX_PERIODIC_FLUSH
77 * Enable support for periodically flushing the VDI to disk. This may prove
78 * useful for those nasty problems with the ultra-slow host filesystems.
79 * If this is enabled, it can be configured via the CFGM key
80 * "VBoxInternal/Devices/piix3ide/0/LUN#<x>/Config/FlushInterval". @verbatim<x>@endverbatim
81 * must be replaced with the correct LUN number of the disk that should
82 * do the periodic flushes. The value of the key is the number of bytes
83 * written between flushes. A value of 0 (the default) denotes no flushes. */
84#define VBOX_PERIODIC_FLUSH
85
86/** @def VBOX_IGNORE_FLUSH
87 * Enable support for ignoring VDI flush requests. This can be useful for
88 * filesystems that show bad guest IDE write performance (especially with
89 * Windows guests). NOTE that this does not disable the flushes caused by
90 * the periodic flush cache feature above.
91 * If this feature is enabled, it can be configured via the CFGM key
92 * "VBoxInternal/Devices/piix3ide/0/LUN#<x>/Config/IgnoreFlush". @verbatim<x>@endverbatim
93 * must be replaced with the correct LUN number of the disk that should
94 * ignore flush requests. The value of the key is a boolean. The default
95 * is to ignore flushes, i.e. true. */
96#define VBOX_IGNORE_FLUSH
97
98
99/*********************************************************************************************************************************
100* Defined types, constants and macros *
101*********************************************************************************************************************************/
102
103/** Converts a pointer to VBOXDISK::IMedia to a PVBOXDISK. */
104#define PDMIMEDIA_2_VBOXDISK(pInterface) \
105 ( (PVBOXDISK)((uintptr_t)pInterface - RT_UOFFSETOF(VBOXDISK, IMedia)) )
106
107/** Saved state version of an I/O request .*/
108#define DRVVD_IOREQ_SAVED_STATE_VERSION UINT32_C(1)
109/** Maximum number of request errors in the release log before muting. */
110#define DRVVD_MAX_LOG_REL_ERRORS 100
111
112/** Forward declaration for the dis kcontainer. */
113typedef struct VBOXDISK *PVBOXDISK;
114
115/**
116 * VBox disk container, image information, private part.
117 */
118
119typedef struct VBOXIMAGE
120{
121 /** Pointer to next image. */
122 struct VBOXIMAGE *pNext;
123 /** Pointer to list of VD interfaces. Per-image. */
124 PVDINTERFACE pVDIfsImage;
125 /** Configuration information interface. */
126 VDINTERFACECONFIG VDIfConfig;
127 /** TCP network stack instance for host mode. */
128 VDIFINST hVdIfTcpNet;
129 /** TCP network stack interface (for INIP). */
130 VDINTERFACETCPNET VDIfTcpNet;
131 /** I/O interface. */
132 VDINTERFACEIO VDIfIo;
133} VBOXIMAGE, *PVBOXIMAGE;
134
135/**
136 * Storage backend data.
137 */
138typedef struct DRVVDSTORAGEBACKEND
139{
140 /** The virtual disk driver instance. */
141 PVBOXDISK pVD;
142 /** PDM async completion end point. */
143 PPDMASYNCCOMPLETIONENDPOINT pEndpoint;
144 /** The template. */
145 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
146 /** Event semaphore for synchronous operations. */
147 RTSEMEVENT EventSem;
148 /** Flag whether a synchronous operation is currently pending. */
149 volatile bool fSyncIoPending;
150 /** Return code of the last completed request. */
151 int rcReqLast;
152 /** Callback routine */
153 PFNVDCOMPLETED pfnCompleted;
154} DRVVDSTORAGEBACKEND, *PDRVVDSTORAGEBACKEND;
155
156/**
157 * VD I/O request state.
158 */
159typedef enum VDIOREQSTATE
160{
161 /** Invalid. */
162 VDIOREQSTATE_INVALID = 0,
163 /** The request is not in use and resides on the free list. */
164 VDIOREQSTATE_FREE,
165 /** The request was just allocated and is not active. */
166 VDIOREQSTATE_ALLOCATED,
167 /** The request was allocated and is in use. */
168 VDIOREQSTATE_ACTIVE,
169 /** The request was suspended and is not actively processed. */
170 VDIOREQSTATE_SUSPENDED,
171 /** The request is in the last step of completion and syncs memory. */
172 VDIOREQSTATE_COMPLETING,
173 /** The request completed. */
174 VDIOREQSTATE_COMPLETED,
175 /** The request was aborted but wasn't returned as complete from the storage
176 * layer below us. */
177 VDIOREQSTATE_CANCELED,
178 /** 32bit hack. */
179 VDIOREQSTATE_32BIT_HACK = 0x7fffffff
180} VDIOREQSTATE;
181
182/**
183 * VD I/O Request.
184 */
185typedef struct PDMMEDIAEXIOREQINT
186{
187 /** List node for the list of allocated requests. */
188 RTLISTNODE NdAllocatedList;
189 /** List for requests waiting for I/O memory or on the redo list. */
190 RTLISTNODE NdLstWait;
191 /** I/O request type. */
192 PDMMEDIAEXIOREQTYPE enmType;
193 /** Request state. */
194 volatile VDIOREQSTATE enmState;
195 /** I/O request ID. */
196 PDMMEDIAEXIOREQID uIoReqId;
197 /** Pointer to the disk container. */
198 PVBOXDISK pDisk;
199 /** Flags. */
200 uint32_t fFlags;
201 /** Timestamp when the request was submitted. */
202 uint64_t tsSubmit;
203 /** Type dependent data. */
204 union
205 {
206 /** Read/Write request sepcific data. */
207 struct
208 {
209 /** Start offset of the request. */
210 uint64_t offStart;
211 /** Size of the request. */
212 size_t cbReq;
213 /** Size left for this request. */
214 size_t cbReqLeft;
215 /** Size of the allocated I/O buffer. */
216 size_t cbIoBuf;
217 /** Pointer to the S/G buffer. */
218 PRTSGBUF pSgBuf;
219 /** Flag whether the pointer is a direct buffer or
220 * was allocated by us. */
221 bool fDirectBuf;
222 /** Buffer management data based on the fDirectBuf flag. */
223 union
224 {
225 /** Direct buffer. */
226 struct
227 {
228 /** Segment for the data buffer. */
229 RTSGSEG Seg;
230 /** S/G buffer structure. */
231 RTSGBUF SgBuf;
232 } Direct;
233 /** I/O buffer descriptor. */
234 IOBUFDESC IoBuf;
235 };
236 } ReadWrite;
237 /** Discard specific data. */
238 struct
239 {
240 /** Pointer to array of ranges to discard. */
241 PRTRANGE paRanges;
242 /** Number of ranges to discard. */
243 unsigned cRanges;
244 } Discard;
245 };
246 /** Allocator specific memory - variable size. */
247 uint8_t abAlloc[1];
248} PDMMEDIAEXIOREQINT;
249/** Pointer to a VD I/O request. */
250typedef PDMMEDIAEXIOREQINT *PPDMMEDIAEXIOREQINT;
251
252/**
253 * Structure for holding a list of allocated requests.
254 */
255typedef struct VDLSTIOREQALLOC
256{
257 /** Mutex protecting the table of allocated requests. */
258 RTSEMFASTMUTEX hMtxLstIoReqAlloc;
259 /** List anchor. */
260 RTLISTANCHOR LstIoReqAlloc;
261} VDLSTIOREQALLOC;
262typedef VDLSTIOREQALLOC *PVDLSTIOREQALLOC;
263
264/** Number of bins for allocated requests. */
265#define DRVVD_VDIOREQ_ALLOC_BINS 8
266
267/**
268 * VD config node.
269 */
270typedef struct VDCFGNODE
271{
272 /** List node for the list of config nodes. */
273 RTLISTNODE NdLst;
274 /** Pointer to the driver helper callbacks. */
275 PCPDMDRVHLPR3 pHlp;
276 /** The config node. */
277 PCFGMNODE pCfgNode;
278} VDCFGNODE;
279/** Pointer to a VD config node. */
280typedef VDCFGNODE *PVDCFGNODE;
281
282/**
283 * VBox disk container media main structure, private part.
284 *
285 * @implements PDMIMEDIA
286 * @implements PDMIMEDIAEX
287 * @implements PDMIMOUNT
288 * @implements VDINTERFACEERROR
289 * @implements VDINTERFACETCPNET
290 * @implements VDINTERFACEASYNCIO
291 * @implements VDINTERFACECONFIG
292 */
293typedef struct VBOXDISK
294{
295 /** The VBox disk container. */
296 PVDISK pDisk;
297 /** The media interface. */
298 PDMIMEDIA IMedia;
299 /** Media port. */
300 PPDMIMEDIAPORT pDrvMediaPort;
301 /** Pointer to the driver instance. */
302 PPDMDRVINS pDrvIns;
303 /** Flag whether suspend has changed image open mode to read only. */
304 bool fTempReadOnly;
305 /** Flag whether to use the runtime (true) or startup error facility. */
306 bool fErrorUseRuntime;
307 /** Pointer to list of VD interfaces. Per-disk. */
308 PVDINTERFACE pVDIfsDisk;
309 /** Error interface. */
310 VDINTERFACEERROR VDIfError;
311 /** Thread synchronization interface. */
312 VDINTERFACETHREADSYNC VDIfThreadSync;
313
314 /** Flag whether opened disk supports async I/O operations. */
315 bool fAsyncIOSupported;
316 /** Pointer to the list of data we need to keep per image. */
317 PVBOXIMAGE pImages;
318 /** Flag whether the media should allow concurrent open for writing. */
319 bool fShareable;
320 /** Flag whether a merge operation has been set up. */
321 bool fMergePending;
322 /** Synchronization to prevent destruction before merge finishes. */
323 RTSEMFASTMUTEX MergeCompleteMutex;
324 /** Synchronization between merge and other image accesses. */
325 RTSEMRW MergeLock;
326 /** Source image index for merging. */
327 unsigned uMergeSource;
328 /** Target image index for merging. */
329 unsigned uMergeTarget;
330
331 /** Flag whether boot acceleration is enabled. */
332 bool fBootAccelEnabled;
333 /** Flag whether boot acceleration is currently active. */
334 bool fBootAccelActive;
335 /** Size of the disk, used for read truncation. */
336 uint64_t cbDisk;
337 /** Size of the configured buffer. */
338 size_t cbBootAccelBuffer;
339 /** Start offset for which the buffer holds data. */
340 uint64_t offDisk;
341 /** Number of valid bytes in the buffer. */
342 size_t cbDataValid;
343 /** The disk buffer. */
344 uint8_t *pbData;
345 /** Bandwidth group the disk is assigned to. */
346 char *pszBwGroup;
347 /** Flag whether async I/O using the host cache is enabled. */
348 bool fAsyncIoWithHostCache;
349
350 /** I/O interface for a cache image. */
351 VDINTERFACEIO VDIfIoCache;
352 /** Interface list for the cache image. */
353 PVDINTERFACE pVDIfsCache;
354
355 /** The block cache handle if configured. */
356 PPDMBLKCACHE pBlkCache;
357 /** Host block device manager. */
358 HBDMGR hHbdMgr;
359
360 /** Drive type. */
361 PDMMEDIATYPE enmType;
362 /** Locked indicator. */
363 bool fLocked;
364 /** Mountable indicator. */
365 bool fMountable;
366 /** Visible to the BIOS. */
367 bool fBiosVisible;
368 /** Flag whether this medium should be presented as non rotational. */
369 bool fNonRotational;
370 /** Flag whether a suspend is in progress right now. */
371 volatile bool fSuspending;
372#ifdef VBOX_PERIODIC_FLUSH
373 /** HACK: Configuration value for number of bytes written after which to flush. */
374 uint32_t cbFlushInterval;
375 /** HACK: Current count for the number of bytes written since the last flush. */
376 uint32_t cbDataWritten;
377#endif /* VBOX_PERIODIC_FLUSH */
378#ifdef VBOX_IGNORE_FLUSH
379 /** HACK: Disable flushes for this drive. */
380 bool fIgnoreFlush;
381 /** Disable async flushes for this drive. */
382 bool fIgnoreFlushAsync;
383#endif /* VBOX_IGNORE_FLUSH */
384 /** Our mountable interface. */
385 PDMIMOUNT IMount;
386 /** Pointer to the mount notify interface above us. */
387 PPDMIMOUNTNOTIFY pDrvMountNotify;
388 /** Uuid of the drive. */
389 RTUUID Uuid;
390 /** BIOS PCHS Geometry. */
391 PDMMEDIAGEOMETRY PCHSGeometry;
392 /** BIOS LCHS Geometry. */
393 PDMMEDIAGEOMETRY LCHSGeometry;
394 /** Region list. */
395 PVDREGIONLIST pRegionList;
396
397 /** VD config support.
398 * @{ */
399 /** List head of config nodes. */
400 RTLISTANCHOR LstCfgNodes;
401 /** @} */
402
403 /** Cryptographic support
404 * @{ */
405 /** Pointer to the CFGM node containing the config of the crypto filter
406 * if enable. */
407 VDCFGNODE CfgCrypto;
408 /** Config interface for the encryption filter. */
409 VDINTERFACECONFIG VDIfCfg;
410 /** Crypto interface for the encryption filter. */
411 VDINTERFACECRYPTO VDIfCrypto;
412 /** The secret key interface used to retrieve keys. */
413 PPDMISECKEY pIfSecKey;
414 /** The secret key helper interface used to notify about missing keys. */
415 PPDMISECKEYHLP pIfSecKeyHlp;
416 /** @} */
417
418 /** @name IMEDIAEX interface support specific members.
419 * @{ */
420 /** Pointer to the IMEDIAEXPORT interface above us. */
421 PPDMIMEDIAEXPORT pDrvMediaExPort;
422 /** Our extended media interface. */
423 PDMIMEDIAEX IMediaEx;
424 /** Memory cache for the I/O requests. */
425 RTMEMCACHE hIoReqCache;
426 /** I/O buffer manager. */
427 IOBUFMGR hIoBufMgr;
428 /** Active request counter. */
429 volatile uint32_t cIoReqsActive;
430 /** Bins for allocated requests. */
431 VDLSTIOREQALLOC aIoReqAllocBins[DRVVD_VDIOREQ_ALLOC_BINS];
432 /** List of requests for I/O memory to be available - VDIOREQ::NdLstWait. */
433 RTLISTANCHOR LstIoReqIoBufWait;
434 /** Critical section protecting the list of requests waiting for I/O memory. */
435 RTCRITSECT CritSectIoReqsIoBufWait;
436 /** Number of requests waiting for a I/O buffer. */
437 volatile uint32_t cIoReqsWaiting;
438 /** Flag whether we have to resubmit requests on resume because the
439 * VM was suspended due to a recoverable I/O error.
440 */
441 volatile bool fRedo;
442 /** List of requests we have to redo. */
443 RTLISTANCHOR LstIoReqRedo;
444 /** Criticial section protecting the list of waiting requests. */
445 RTCRITSECT CritSectIoReqRedo;
446 /** Number of errors logged so far. */
447 unsigned cErrors;
448 /** @} */
449
450 /** @name Statistics.
451 * @{ */
452 /** How many attempts were made to query a direct buffer pointer from the
453 * device/driver above. */
454 STAMCOUNTER StatQueryBufAttempts;
455 /** How many attempts to query a direct buffer pointer succeeded. */
456 STAMCOUNTER StatQueryBufSuccess;
457 /** Release statistics: number of bytes written. */
458 STAMCOUNTER StatBytesWritten;
459 /** Release statistics: number of bytes read. */
460 STAMCOUNTER StatBytesRead;
461 /** Release statistics: Number of requests submitted. */
462 STAMCOUNTER StatReqsSubmitted;
463 /** Release statistics: Number of requests failed. */
464 STAMCOUNTER StatReqsFailed;
465 /** Release statistics: Number of requests succeeded. */
466 STAMCOUNTER StatReqsSucceeded;
467 /** Release statistics: Number of flush requests. */
468 STAMCOUNTER StatReqsFlush;
469 /** Release statistics: Number of write requests. */
470 STAMCOUNTER StatReqsWrite;
471 /** Release statistics: Number of read requests. */
472 STAMCOUNTER StatReqsRead;
473 /** Release statistics: Number of discard requests. */
474 STAMCOUNTER StatReqsDiscard;
475 /** Release statistics: Number of I/O requests processed per second. */
476 STAMCOUNTER StatReqsPerSec;
477 /** @} */
478} VBOXDISK;
479
480
481/*********************************************************************************************************************************
482* Internal Functions *
483*********************************************************************************************************************************/
484
485static DECLCALLBACK(void) drvvdMediaExIoReqComplete(void *pvUser1, void *pvUser2, int rcReq);
486static void drvvdPowerOffOrDestructOrUnmount(PPDMDRVINS pDrvIns);
487DECLINLINE(void) drvvdMediaExIoReqBufFree(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq);
488static int drvvdMediaExIoReqCompleteWorker(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, int rcReq, bool fUpNotify);
489static int drvvdMediaExIoReqReadWriteProcess(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, bool fUpNotify);
490
491/**
492 * Internal: allocate new image descriptor and put it in the list
493 */
494static PVBOXIMAGE drvvdNewImage(PVBOXDISK pThis)
495{
496 AssertPtr(pThis);
497 PVBOXIMAGE pImage = (PVBOXIMAGE)RTMemAllocZ(sizeof(VBOXIMAGE));
498 if (pImage)
499 {
500 pImage->pVDIfsImage = NULL;
501 PVBOXIMAGE *pp = &pThis->pImages;
502 while (*pp != NULL)
503 pp = &(*pp)->pNext;
504 *pp = pImage;
505 pImage->pNext = NULL;
506 }
507
508 return pImage;
509}
510
511/**
512 * Internal: free the list of images descriptors.
513 */
514static void drvvdFreeImages(PVBOXDISK pThis)
515{
516 while (pThis->pImages != NULL)
517 {
518 PVBOXIMAGE p = pThis->pImages;
519 pThis->pImages = pThis->pImages->pNext;
520 if (p->hVdIfTcpNet != NULL)
521 VDIfTcpNetInstDefaultDestroy(p->hVdIfTcpNet);
522 RTMemFree(p);
523 }
524}
525
526
527/**
528 * Make the image temporarily read-only.
529 *
530 * @returns VBox status code.
531 * @param pThis The driver instance data.
532 */
533static int drvvdSetReadonly(PVBOXDISK pThis)
534{
535 int rc = VINF_SUCCESS;
536 if ( pThis->pDisk
537 && !VDIsReadOnly(pThis->pDisk))
538 {
539 unsigned uOpenFlags;
540 rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
541 AssertRC(rc);
542 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
543 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
544 AssertRC(rc);
545 pThis->fTempReadOnly = true;
546 }
547 return rc;
548}
549
550
551/**
552 * Undo the temporary read-only status of the image.
553 *
554 * @returns VBox status code.
555 * @param pThis The driver instance data.
556 */
557static int drvvdSetWritable(PVBOXDISK pThis)
558{
559 int rc = VINF_SUCCESS;
560 if (pThis->fTempReadOnly)
561 {
562 unsigned uOpenFlags;
563 rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
564 AssertRC(rc);
565 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
566 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
567 if (RT_SUCCESS(rc))
568 pThis->fTempReadOnly = false;
569 else
570 AssertRC(rc);
571 }
572 return rc;
573}
574
575
576/*********************************************************************************************************************************
577* Error reporting callback *
578*********************************************************************************************************************************/
579
580static DECLCALLBACK(void) drvvdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL,
581 const char *pszFormat, va_list va)
582{
583 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
584 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
585 if (pThis->fErrorUseRuntime)
586 /* We must not pass VMSETRTERR_FLAGS_FATAL as it could lead to a
587 * deadlock: We are probably executed in a thread context != EMT
588 * and the EM thread would wait until every thread is suspended
589 * but we would wait for the EM thread ... */
590
591 PDMDrvHlpVMSetRuntimeErrorV(pDrvIns, /* fFlags=*/ 0, "DrvVD", pszFormat, va);
592 else
593 PDMDrvHlpVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
594}
595
596
597/*********************************************************************************************************************************
598* VD Async I/O interface implementation *
599*********************************************************************************************************************************/
600
601#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
602
603static DECLCALLBACK(void) drvvdAsyncTaskCompleted(PPDMDRVINS pDrvIns, void *pvTemplateUser, void *pvUser, int rcReq)
604{
605 RT_NOREF(pDrvIns);
606 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pvTemplateUser;
607
608 LogFlowFunc(("pDrvIns=%#p pvTemplateUser=%#p pvUser=%#p rcReq=%d\n",
609 pDrvIns, pvTemplateUser, pvUser, rcReq));
610
611 if (pStorageBackend->fSyncIoPending)
612 {
613 Assert(!pvUser);
614 pStorageBackend->rcReqLast = rcReq;
615 ASMAtomicWriteBool(&pStorageBackend->fSyncIoPending, false);
616 RTSemEventSignal(pStorageBackend->EventSem);
617 }
618 else
619 {
620 int rc;
621
622 AssertPtr(pvUser);
623
624 AssertPtr(pStorageBackend->pfnCompleted);
625 rc = pStorageBackend->pfnCompleted(pvUser, rcReq);
626 AssertRC(rc);
627 }
628}
629
630static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation,
631 uint32_t fOpen,
632 PFNVDCOMPLETED pfnCompleted,
633 void **ppStorage)
634{
635 PVBOXDISK pThis = (PVBOXDISK)pvUser;
636 PDRVVDSTORAGEBACKEND pStorageBackend = NULL;
637 int rc = VINF_SUCCESS;
638
639 /*
640 * Check whether the backend wants to open a block device and try to prepare it
641 * if we didn't claim it yet.
642 *
643 * We only create a block device manager on demand to not waste any resources.
644 */
645 if (HBDMgrIsBlockDevice(pszLocation))
646 {
647 if (pThis->hHbdMgr == NIL_HBDMGR)
648 rc = HBDMgrCreate(&pThis->hHbdMgr);
649
650 if ( RT_SUCCESS(rc)
651 && !HBDMgrIsBlockDeviceClaimed(pThis->hHbdMgr, pszLocation))
652 rc = HBDMgrClaimBlockDevice(pThis->hHbdMgr, pszLocation);
653
654 if (RT_FAILURE(rc))
655 return rc;
656 }
657
658 pStorageBackend = (PDRVVDSTORAGEBACKEND)RTMemAllocZ(sizeof(DRVVDSTORAGEBACKEND));
659 if (pStorageBackend)
660 {
661 pStorageBackend->pVD = pThis;
662 pStorageBackend->fSyncIoPending = false;
663 pStorageBackend->rcReqLast = VINF_SUCCESS;
664 pStorageBackend->pfnCompleted = pfnCompleted;
665
666 rc = RTSemEventCreate(&pStorageBackend->EventSem);
667 if (RT_SUCCESS(rc))
668 {
669 rc = PDMDrvHlpAsyncCompletionTemplateCreate(pThis->pDrvIns, &pStorageBackend->pTemplate,
670 drvvdAsyncTaskCompleted, pStorageBackend, "AsyncTaskCompleted");
671 if (RT_SUCCESS(rc))
672 {
673 uint32_t fFlags = (fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ
674 ? PDMACEP_FILE_FLAGS_READ_ONLY
675 : 0;
676 if (pThis->fShareable)
677 {
678 Assert((fOpen & RTFILE_O_DENY_MASK) == RTFILE_O_DENY_NONE);
679
680 fFlags |= PDMACEP_FILE_FLAGS_DONT_LOCK;
681 }
682 if (pThis->fAsyncIoWithHostCache)
683 fFlags |= PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED;
684
685 rc = PDMDrvHlpAsyncCompletionEpCreateForFile(pThis->pDrvIns,
686 &pStorageBackend->pEndpoint,
687 pszLocation, fFlags,
688 pStorageBackend->pTemplate);
689
690 if (RT_SUCCESS(rc))
691 {
692 if (pThis->pszBwGroup)
693 rc = PDMDrvHlpAsyncCompletionEpSetBwMgr(pThis->pDrvIns, pStorageBackend->pEndpoint, pThis->pszBwGroup);
694
695 if (RT_SUCCESS(rc))
696 {
697 LogFlow(("drvvdAsyncIOOpen: Successfully opened '%s'; fOpen=%#x pStorage=%p\n",
698 pszLocation, fOpen, pStorageBackend));
699 *ppStorage = pStorageBackend;
700 return VINF_SUCCESS;
701 }
702
703 PDMDrvHlpAsyncCompletionEpClose(pThis->pDrvIns, pStorageBackend->pEndpoint);
704 }
705
706 PDMDrvHlpAsyncCompletionTemplateDestroy(pThis->pDrvIns, pStorageBackend->pTemplate);
707 }
708 RTSemEventDestroy(pStorageBackend->EventSem);
709 }
710 RTMemFree(pStorageBackend);
711 }
712 else
713 rc = VERR_NO_MEMORY;
714
715 return rc;
716}
717
718static DECLCALLBACK(int) drvvdAsyncIOClose(void *pvUser, void *pStorage)
719{
720 RT_NOREF(pvUser);
721 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
722 PVBOXDISK pThis = pStorageBackend->pVD;
723
724 /*
725 * We don't unclaim any block devices on purpose here because they
726 * might get reopened shortly (switching to readonly during suspend)
727 *
728 * Block devices will get unclaimed during destruction of the driver.
729 */
730
731 PDMDrvHlpAsyncCompletionEpClose(pThis->pDrvIns, pStorageBackend->pEndpoint);
732 PDMDrvHlpAsyncCompletionTemplateDestroy(pThis->pDrvIns, pStorageBackend->pTemplate);
733 RTSemEventDestroy(pStorageBackend->EventSem);
734 RTMemFree(pStorageBackend);
735 return VINF_SUCCESS;;
736}
737
738static DECLCALLBACK(int) drvvdAsyncIOReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
739 void *pvBuf, size_t cbRead, size_t *pcbRead)
740{
741 RT_NOREF(pvUser);
742 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
743 PVBOXDISK pThis = pStorageBackend->pVD;
744 RTSGSEG DataSeg;
745 PPDMASYNCCOMPLETIONTASK pTask;
746
747 bool fOld = ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
748 Assert(!fOld); NOREF(fOld);
749 DataSeg.cbSeg = cbRead;
750 DataSeg.pvSeg = pvBuf;
751
752 int rc = PDMDrvHlpAsyncCompletionEpRead(pThis->pDrvIns, pStorageBackend->pEndpoint, uOffset, &DataSeg, 1, cbRead, NULL, &pTask);
753 if (RT_FAILURE(rc))
754 return rc;
755
756 if (rc == VINF_AIO_TASK_PENDING)
757 {
758 /* Wait */
759 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
760 AssertRC(rc);
761 }
762 else
763 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
764
765 if (pcbRead)
766 *pcbRead = cbRead;
767
768 return pStorageBackend->rcReqLast;
769}
770
771static DECLCALLBACK(int) drvvdAsyncIOWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
772 const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
773{
774 RT_NOREF(pvUser);
775 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
776 PVBOXDISK pThis = pStorageBackend->pVD;
777 RTSGSEG DataSeg;
778 PPDMASYNCCOMPLETIONTASK pTask;
779
780 bool fOld = ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
781 Assert(!fOld); NOREF(fOld);
782 DataSeg.cbSeg = cbWrite;
783 DataSeg.pvSeg = (void *)pvBuf;
784
785 int rc = PDMDrvHlpAsyncCompletionEpWrite(pThis->pDrvIns, pStorageBackend->pEndpoint, uOffset, &DataSeg, 1, cbWrite, NULL, &pTask);
786 if (RT_FAILURE(rc))
787 return rc;
788
789 if (rc == VINF_AIO_TASK_PENDING)
790 {
791 /* Wait */
792 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
793 AssertRC(rc);
794 }
795 else
796 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
797
798 if (pcbWritten)
799 *pcbWritten = cbWrite;
800
801 return pStorageBackend->rcReqLast;
802}
803
804static DECLCALLBACK(int) drvvdAsyncIOFlushSync(void *pvUser, void *pStorage)
805{
806 RT_NOREF(pvUser);
807 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
808 PVBOXDISK pThis = pStorageBackend->pVD;
809 PPDMASYNCCOMPLETIONTASK pTask;
810
811 LogFlowFunc(("pvUser=%#p pStorage=%#p\n", pvUser, pStorage));
812
813 bool fOld = ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
814 Assert(!fOld); NOREF(fOld);
815
816 int rc = PDMDrvHlpAsyncCompletionEpFlush(pThis->pDrvIns, pStorageBackend->pEndpoint, NULL, &pTask);
817 if (RT_FAILURE(rc))
818 return rc;
819
820 if (rc == VINF_AIO_TASK_PENDING)
821 {
822 /* Wait */
823 LogFlowFunc(("Waiting for flush to complete\n"));
824 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
825 AssertRC(rc);
826 }
827 else
828 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
829
830 return pStorageBackend->rcReqLast;
831}
832
833static DECLCALLBACK(int) drvvdAsyncIOReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
834 PCRTSGSEG paSegments, size_t cSegments,
835 size_t cbRead, void *pvCompletion,
836 void **ppTask)
837{
838 RT_NOREF(pvUser);
839 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
840 PVBOXDISK pThis = pStorageBackend->pVD;
841
842 int rc = PDMDrvHlpAsyncCompletionEpRead(pThis->pDrvIns, pStorageBackend->pEndpoint,
843 uOffset, paSegments, (unsigned)cSegments, cbRead,
844 pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
845 if (rc == VINF_AIO_TASK_PENDING)
846 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
847
848 return rc;
849}
850
851static DECLCALLBACK(int) drvvdAsyncIOWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
852 PCRTSGSEG paSegments, size_t cSegments,
853 size_t cbWrite, void *pvCompletion,
854 void **ppTask)
855{
856 RT_NOREF(pvUser);
857 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
858 PVBOXDISK pThis = pStorageBackend->pVD;
859
860 int rc = PDMDrvHlpAsyncCompletionEpWrite(pThis->pDrvIns, pStorageBackend->pEndpoint,
861 uOffset, paSegments, (unsigned)cSegments, cbWrite,
862 pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
863 if (rc == VINF_AIO_TASK_PENDING)
864 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
865
866 return rc;
867}
868
869static DECLCALLBACK(int) drvvdAsyncIOFlushAsync(void *pvUser, void *pStorage,
870 void *pvCompletion, void **ppTask)
871{
872 RT_NOREF(pvUser);
873 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
874 PVBOXDISK pThis = pStorageBackend->pVD;
875
876 int rc = PDMDrvHlpAsyncCompletionEpFlush(pThis->pDrvIns, pStorageBackend->pEndpoint, pvCompletion,
877 (PPPDMASYNCCOMPLETIONTASK)ppTask);
878 if (rc == VINF_AIO_TASK_PENDING)
879 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
880
881 return rc;
882}
883
884static DECLCALLBACK(int) drvvdAsyncIOGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
885{
886 RT_NOREF(pvUser);
887 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
888 PVBOXDISK pThis = pStorageBackend->pVD;
889
890 return PDMDrvHlpAsyncCompletionEpGetSize(pThis->pDrvIns, pStorageBackend->pEndpoint, pcbSize);
891}
892
893static DECLCALLBACK(int) drvvdAsyncIOSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
894{
895 RT_NOREF(pvUser);
896 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
897 PVBOXDISK pThis = pStorageBackend->pVD;
898
899 return PDMDrvHlpAsyncCompletionEpSetSize(pThis->pDrvIns, pStorageBackend->pEndpoint, cbSize);
900}
901
902static DECLCALLBACK(int) drvvdAsyncIOSetAllocationSize(void *pvUser, void *pvStorage, uint64_t cbSize, uint32_t fFlags)
903{
904 RT_NOREF(pvUser, pvStorage, cbSize, fFlags);
905 return VERR_NOT_SUPPORTED;
906}
907
908#endif /* VBOX_WITH_PDM_ASYNC_COMPLETION */
909
910
911/*********************************************************************************************************************************
912* VD Thread Synchronization interface implementation *
913*********************************************************************************************************************************/
914
915static DECLCALLBACK(int) drvvdThreadStartRead(void *pvUser)
916{
917 PVBOXDISK pThis = (PVBOXDISK)pvUser;
918
919 return RTSemRWRequestRead(pThis->MergeLock, RT_INDEFINITE_WAIT);
920}
921
922static DECLCALLBACK(int) drvvdThreadFinishRead(void *pvUser)
923{
924 PVBOXDISK pThis = (PVBOXDISK)pvUser;
925
926 return RTSemRWReleaseRead(pThis->MergeLock);
927}
928
929static DECLCALLBACK(int) drvvdThreadStartWrite(void *pvUser)
930{
931 PVBOXDISK pThis = (PVBOXDISK)pvUser;
932
933 return RTSemRWRequestWrite(pThis->MergeLock, RT_INDEFINITE_WAIT);
934}
935
936static DECLCALLBACK(int) drvvdThreadFinishWrite(void *pvUser)
937{
938 PVBOXDISK pThis = (PVBOXDISK)pvUser;
939
940 return RTSemRWReleaseWrite(pThis->MergeLock);
941}
942
943
944/*********************************************************************************************************************************
945* VD Configuration interface implementation *
946*********************************************************************************************************************************/
947
948static DECLCALLBACK(bool) drvvdCfgAreKeysValid(void *pvUser, const char *pszzValid)
949{
950 PVDCFGNODE pVdCfgNode = (PVDCFGNODE)pvUser;
951 PCPDMDRVHLPR3 pHlp = pVdCfgNode->pHlp;
952 return pHlp->pfnCFGMAreValuesValid(pVdCfgNode->pCfgNode, pszzValid);
953}
954
955static DECLCALLBACK(int) drvvdCfgQuerySize(void *pvUser, const char *pszName, size_t *pcb)
956{
957 PVDCFGNODE pVdCfgNode = (PVDCFGNODE)pvUser;
958 PCPDMDRVHLPR3 pHlp = pVdCfgNode->pHlp;
959 return pHlp->pfnCFGMQuerySize(pVdCfgNode->pCfgNode, pszName, pcb);
960}
961
962static DECLCALLBACK(int) drvvdCfgQuery(void *pvUser, const char *pszName, char *pszString, size_t cchString)
963{
964 PVDCFGNODE pVdCfgNode = (PVDCFGNODE)pvUser;
965 PCPDMDRVHLPR3 pHlp = pVdCfgNode->pHlp;
966 return pHlp->pfnCFGMQueryString(pVdCfgNode->pCfgNode, pszName, pszString, cchString);
967}
968
969static DECLCALLBACK(int) drvvdCfgQueryBytes(void *pvUser, const char *pszName, void *ppvData, size_t cbData)
970{
971 PVDCFGNODE pVdCfgNode = (PVDCFGNODE)pvUser;
972 PCPDMDRVHLPR3 pHlp = pVdCfgNode->pHlp;
973 return pHlp->pfnCFGMQueryBytes(pVdCfgNode->pCfgNode, pszName, ppvData, cbData);
974}
975
976
977/*******************************************************************************
978* VD Crypto interface implementation for the encryption support *
979*******************************************************************************/
980
981static DECLCALLBACK(int) drvvdCryptoKeyRetain(void *pvUser, const char *pszId, const uint8_t **ppbKey, size_t *pcbKey)
982{
983 PVBOXDISK pThis = (PVBOXDISK)pvUser;
984 int rc = VINF_SUCCESS;
985
986 AssertPtr(pThis->pIfSecKey);
987 if (pThis->pIfSecKey)
988 rc = pThis->pIfSecKey->pfnKeyRetain(pThis->pIfSecKey, pszId, ppbKey, pcbKey);
989 else
990 rc = VERR_NOT_SUPPORTED;
991
992 return rc;
993}
994
995static DECLCALLBACK(int) drvvdCryptoKeyRelease(void *pvUser, const char *pszId)
996{
997 PVBOXDISK pThis = (PVBOXDISK)pvUser;
998 int rc = VINF_SUCCESS;
999
1000 AssertPtr(pThis->pIfSecKey);
1001 if (pThis->pIfSecKey)
1002 rc = pThis->pIfSecKey->pfnKeyRelease(pThis->pIfSecKey, pszId);
1003 else
1004 rc = VERR_NOT_SUPPORTED;
1005
1006 return rc;
1007}
1008
1009static DECLCALLBACK(int) drvvdCryptoKeyStorePasswordRetain(void *pvUser, const char *pszId, const char **ppszPassword)
1010{
1011 PVBOXDISK pThis = (PVBOXDISK)pvUser;
1012 int rc = VINF_SUCCESS;
1013
1014 AssertPtr(pThis->pIfSecKey);
1015 if (pThis->pIfSecKey)
1016 rc = pThis->pIfSecKey->pfnPasswordRetain(pThis->pIfSecKey, pszId, ppszPassword);
1017 else
1018 rc = VERR_NOT_SUPPORTED;
1019
1020 return rc;
1021}
1022
1023static DECLCALLBACK(int) drvvdCryptoKeyStorePasswordRelease(void *pvUser, const char *pszId)
1024{
1025 PVBOXDISK pThis = (PVBOXDISK)pvUser;
1026 int rc = VINF_SUCCESS;
1027
1028 AssertPtr(pThis->pIfSecKey);
1029 if (pThis->pIfSecKey)
1030 rc = pThis->pIfSecKey->pfnPasswordRelease(pThis->pIfSecKey, pszId);
1031 else
1032 rc = VERR_NOT_SUPPORTED;
1033
1034 return rc;
1035}
1036
1037#ifdef VBOX_WITH_INIP
1038
1039
1040/*********************************************************************************************************************************
1041* VD TCP network stack interface implementation - INIP case *
1042*********************************************************************************************************************************/
1043
1044/**
1045 * vvl: this structure duplicate meaning of sockaddr,
1046 * perhaps it'd be better to get rid of it.
1047 */
1048typedef union INIPSOCKADDRUNION
1049{
1050 struct sockaddr Addr;
1051 struct sockaddr_in Ipv4;
1052#if LWIP_IPV6
1053 struct sockaddr_in6 Ipv6;
1054#endif
1055} INIPSOCKADDRUNION;
1056
1057typedef struct INIPSOCKET
1058{
1059 int hSock;
1060} INIPSOCKET, *PINIPSOCKET;
1061
1062static DECLCALLBACK(int) drvvdINIPFlush(VDSOCKET Sock);
1063
1064/** @interface_method_impl{VDINTERFACETCPNET,pfnSocketCreate} */
1065static DECLCALLBACK(int) drvvdINIPSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
1066{
1067 PINIPSOCKET pSocketInt = NULL;
1068
1069 /*
1070 * The extended select method is not supported because it is impossible to wakeup
1071 * the thread.
1072 */
1073 if (fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT)
1074 return VERR_NOT_SUPPORTED;
1075
1076 pSocketInt = (PINIPSOCKET)RTMemAllocZ(sizeof(INIPSOCKET));
1077 if (pSocketInt)
1078 {
1079 pSocketInt->hSock = INT32_MAX;
1080 *pSock = (VDSOCKET)pSocketInt;
1081 return VINF_SUCCESS;
1082 }
1083
1084 return VERR_NO_MEMORY;
1085}
1086
1087/** @interface_method_impl{VDINTERFACETCPNET,pfnSocketCreate} */
1088static DECLCALLBACK(int) drvvdINIPSocketDestroy(VDSOCKET Sock)
1089{
1090 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1091
1092 RTMemFree(pSocketInt);
1093 return VINF_SUCCESS;
1094}
1095
1096/** @interface_method_impl{VDINTERFACETCPNET,pfnClientConnect} */
1097static DECLCALLBACK(int) drvvdINIPClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort,
1098 RTMSINTERVAL cMillies)
1099{
1100 int rc = VINF_SUCCESS;
1101 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1102 int iInetFamily = PF_INET;
1103 struct in_addr ip;
1104#if LWIP_IPV6
1105 ip6_addr_t ip6;
1106 RT_ZERO(ip6);
1107#endif
1108
1109 NOREF(cMillies); /* LwIP doesn't support connect timeout. */
1110 RT_ZERO(ip); /* Shut up MSC. */
1111
1112 /* Check whether lwIP is set up in this VM instance. */
1113 if (!DevINIPConfigured())
1114 {
1115 LogRelFunc(("no IP stack\n"));
1116 return VERR_NET_HOST_UNREACHABLE;
1117 }
1118 /* Resolve hostname. As there is no standard resolver for lwIP yet,
1119 * just accept numeric IP addresses for now. */
1120#if LWIP_IPV6
1121 if (inet6_aton(pszAddress, &ip6))
1122 iInetFamily = PF_INET6;
1123 else /* concatination with if */
1124#endif
1125 if (!lwip_inet_aton(pszAddress, &ip))
1126 {
1127 LogRelFunc(("cannot resolve IP %s\n", pszAddress));
1128 return VERR_NET_HOST_UNREACHABLE;
1129 }
1130 /* Create socket and connect. */
1131 int iSock = lwip_socket(iInetFamily, SOCK_STREAM, 0);
1132 if (iSock != -1)
1133 {
1134 struct sockaddr *pSockAddr = NULL;
1135 struct sockaddr_in InAddr = {0};
1136#if LWIP_IPV6
1137 struct sockaddr_in6 In6Addr = {0};
1138#endif
1139 if (iInetFamily == PF_INET)
1140 {
1141 InAddr.sin_family = AF_INET;
1142 InAddr.sin_port = htons(uPort);
1143 InAddr.sin_addr = ip;
1144 InAddr.sin_len = sizeof(InAddr);
1145 pSockAddr = (struct sockaddr *)&InAddr;
1146 }
1147#if LWIP_IPV6
1148 else
1149 {
1150 In6Addr.sin6_family = AF_INET6;
1151 In6Addr.sin6_port = htons(uPort);
1152 memcpy(&In6Addr.sin6_addr, &ip6, sizeof(ip6));
1153 In6Addr.sin6_len = sizeof(In6Addr);
1154 pSockAddr = (struct sockaddr *)&In6Addr;
1155 }
1156#endif
1157 if ( pSockAddr
1158 && !lwip_connect(iSock, pSockAddr, pSockAddr->sa_len))
1159 {
1160 pSocketInt->hSock = iSock;
1161 return VINF_SUCCESS;
1162 }
1163 rc = VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
1164 lwip_close(iSock);
1165 }
1166 else
1167 rc = VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
1168 return rc;
1169}
1170
1171/** @interface_method_impl{VDINTERFACETCPNET,pfnClientClose} */
1172static DECLCALLBACK(int) drvvdINIPClientClose(VDSOCKET Sock)
1173{
1174 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1175
1176 lwip_close(pSocketInt->hSock);
1177 pSocketInt->hSock = INT32_MAX;
1178 return VINF_SUCCESS; /** @todo real solution needed */
1179}
1180
1181/** @interface_method_impl{VDINTERFACETCPNET,pfnIsClientConnected} */
1182static DECLCALLBACK(bool) drvvdINIPIsClientConnected(VDSOCKET Sock)
1183{
1184 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1185
1186 return pSocketInt->hSock != INT32_MAX;
1187}
1188
1189/** @interface_method_impl{VDINTERFACETCPNET,pfnSelectOne} */
1190static DECLCALLBACK(int) drvvdINIPSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
1191{
1192 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1193 fd_set fdsetR;
1194 FD_ZERO(&fdsetR);
1195 FD_SET((uintptr_t)pSocketInt->hSock, &fdsetR);
1196 fd_set fdsetE = fdsetR;
1197
1198 int rc;
1199 if (cMillies == RT_INDEFINITE_WAIT)
1200 rc = lwip_select(pSocketInt->hSock + 1, &fdsetR, NULL, &fdsetE, NULL);
1201 else
1202 {
1203 struct timeval timeout;
1204 timeout.tv_sec = cMillies / 1000;
1205 timeout.tv_usec = (cMillies % 1000) * 1000;
1206 rc = lwip_select(pSocketInt->hSock + 1, &fdsetR, NULL, &fdsetE, &timeout);
1207 }
1208 if (rc > 0)
1209 return VINF_SUCCESS;
1210 if (rc == 0)
1211 return VERR_TIMEOUT;
1212 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
1213}
1214
1215/** @interface_method_impl{VDINTERFACETCPNET,pfnRead} */
1216static DECLCALLBACK(int) drvvdINIPRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
1217{
1218 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1219
1220 /* Do params checking */
1221 if (!pvBuffer || !cbBuffer)
1222 {
1223 AssertMsgFailed(("Invalid params\n"));
1224 return VERR_INVALID_PARAMETER;
1225 }
1226
1227 /*
1228 * Read loop.
1229 * If pcbRead is NULL we have to fill the entire buffer!
1230 */
1231 size_t cbRead = 0;
1232 size_t cbToRead = cbBuffer;
1233 for (;;)
1234 {
1235 /** @todo this clipping here is just in case (the send function
1236 * needed it, so I added it here, too). Didn't investigate if this
1237 * really has issues. Better be safe than sorry. */
1238 ssize_t cbBytesRead = lwip_recv(pSocketInt->hSock, (char *)pvBuffer + cbRead,
1239 RT_MIN(cbToRead, 32768), 0);
1240 if (cbBytesRead < 0)
1241 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
1242 if (cbBytesRead == 0 && errno) /** @todo r=bird: lwip_recv will not touch errno on Windows. This may apply to other hosts as well */
1243 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
1244 if (pcbRead)
1245 {
1246 /* return partial data */
1247 *pcbRead = cbBytesRead;
1248 break;
1249 }
1250
1251 /* read more? */
1252 cbRead += cbBytesRead;
1253 if (cbRead == cbBuffer)
1254 break;
1255
1256 /* next */
1257 cbToRead = cbBuffer - cbRead;
1258 }
1259
1260 return VINF_SUCCESS;
1261}
1262
1263/** @interface_method_impl{VDINTERFACETCPNET,pfnWrite} */
1264static DECLCALLBACK(int) drvvdINIPWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
1265{
1266 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1267
1268 do
1269 {
1270 /** @todo lwip send only supports up to 65535 bytes in a single
1271 * send (stupid limitation buried in the code), so make sure we
1272 * don't get any wraparounds. This should be moved to DevINIP
1273 * stack interface once that's implemented. */
1274 ssize_t cbWritten = lwip_send(pSocketInt->hSock, (void *)pvBuffer,
1275 RT_MIN(cbBuffer, 32768), 0);
1276 if (cbWritten < 0)
1277 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
1278 AssertMsg(cbBuffer >= (size_t)cbWritten, ("Wrote more than we requested!!! cbWritten=%d cbBuffer=%d\n",
1279 cbWritten, cbBuffer));
1280 cbBuffer -= cbWritten;
1281 pvBuffer = (const char *)pvBuffer + cbWritten;
1282 } while (cbBuffer);
1283
1284 return VINF_SUCCESS;
1285}
1286
1287/** @interface_method_impl{VDINTERFACETCPNET,pfnSgWrite} */
1288static DECLCALLBACK(int) drvvdINIPSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
1289{
1290 int rc = VINF_SUCCESS;
1291
1292 /* This is an extremely crude emulation, however it's good enough
1293 * for our iSCSI code. INIP has no sendmsg(). */
1294 for (unsigned i = 0; i < pSgBuf->cSegs; i++)
1295 {
1296 rc = drvvdINIPWrite(Sock, pSgBuf->paSegs[i].pvSeg,
1297 pSgBuf->paSegs[i].cbSeg);
1298 if (RT_FAILURE(rc))
1299 break;
1300 }
1301 if (RT_SUCCESS(rc))
1302 drvvdINIPFlush(Sock);
1303
1304 return rc;
1305}
1306
1307/** @interface_method_impl{VDINTERFACETCPNET,pfnFlush} */
1308static DECLCALLBACK(int) drvvdINIPFlush(VDSOCKET Sock)
1309{
1310 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1311
1312 int fFlag = 1;
1313 lwip_setsockopt(pSocketInt->hSock, IPPROTO_TCP, TCP_NODELAY,
1314 (const char *)&fFlag, sizeof(fFlag));
1315 fFlag = 0;
1316 lwip_setsockopt(pSocketInt->hSock, IPPROTO_TCP, TCP_NODELAY,
1317 (const char *)&fFlag, sizeof(fFlag));
1318 return VINF_SUCCESS;
1319}
1320
1321/** @interface_method_impl{VDINTERFACETCPNET,pfnSetSendCoalescing} */
1322static DECLCALLBACK(int) drvvdINIPSetSendCoalescing(VDSOCKET Sock, bool fEnable)
1323{
1324 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1325
1326 int fFlag = fEnable ? 0 : 1;
1327 lwip_setsockopt(pSocketInt->hSock, IPPROTO_TCP, TCP_NODELAY,
1328 (const char *)&fFlag, sizeof(fFlag));
1329 return VINF_SUCCESS;
1330}
1331
1332/** @interface_method_impl{VDINTERFACETCPNET,pfnGetLocalAddress} */
1333static DECLCALLBACK(int) drvvdINIPGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
1334{
1335 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1336 INIPSOCKADDRUNION u;
1337 socklen_t cbAddr = sizeof(u);
1338 RT_ZERO(u);
1339 if (!lwip_getsockname(pSocketInt->hSock, &u.Addr, &cbAddr))
1340 {
1341 /*
1342 * Convert the address.
1343 */
1344 if ( cbAddr == sizeof(struct sockaddr_in)
1345 && u.Addr.sa_family == AF_INET)
1346 {
1347 RT_ZERO(*pAddr);
1348 pAddr->enmType = RTNETADDRTYPE_IPV4;
1349 pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port);
1350 pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr;
1351 }
1352#if LWIP_IPV6
1353 else if ( cbAddr == sizeof(struct sockaddr_in6)
1354 && u.Addr.sa_family == AF_INET6)
1355 {
1356 RT_ZERO(*pAddr);
1357 pAddr->enmType = RTNETADDRTYPE_IPV6;
1358 pAddr->uPort = RT_N2H_U16(u.Ipv6.sin6_port);
1359 memcpy(&pAddr->uAddr.IPv6, &u.Ipv6.sin6_addr, sizeof(RTNETADDRIPV6));
1360 }
1361#endif
1362 else
1363 return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
1364 return VINF_SUCCESS;
1365 }
1366 return VERR_NET_OPERATION_NOT_SUPPORTED;
1367}
1368
1369/** @interface_method_impl{VDINTERFACETCPNET,pfnGetPeerAddress} */
1370static DECLCALLBACK(int) drvvdINIPGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
1371{
1372 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
1373 INIPSOCKADDRUNION u;
1374 socklen_t cbAddr = sizeof(u);
1375 RT_ZERO(u);
1376 if (!lwip_getpeername(pSocketInt->hSock, &u.Addr, &cbAddr))
1377 {
1378 /*
1379 * Convert the address.
1380 */
1381 if ( cbAddr == sizeof(struct sockaddr_in)
1382 && u.Addr.sa_family == AF_INET)
1383 {
1384 RT_ZERO(*pAddr);
1385 pAddr->enmType = RTNETADDRTYPE_IPV4;
1386 pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port);
1387 pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr;
1388 }
1389#if LWIP_IPV6
1390 else if ( cbAddr == sizeof(struct sockaddr_in6)
1391 && u.Addr.sa_family == AF_INET6)
1392 {
1393 RT_ZERO(*pAddr);
1394 pAddr->enmType = RTNETADDRTYPE_IPV6;
1395 pAddr->uPort = RT_N2H_U16(u.Ipv6.sin6_port);
1396 memcpy(&pAddr->uAddr.IPv6, &u.Ipv6.sin6_addr, sizeof(RTNETADDRIPV6));
1397 }
1398#endif
1399 else
1400 return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
1401 return VINF_SUCCESS;
1402 }
1403 return VERR_NET_OPERATION_NOT_SUPPORTED;
1404}
1405
1406/** @interface_method_impl{VDINTERFACETCPNET,pfnSelectOneEx} */
1407static DECLCALLBACK(int) drvvdINIPSelectOneEx(VDSOCKET Sock, uint32_t fEvents, uint32_t *pfEvents, RTMSINTERVAL cMillies)
1408{
1409 RT_NOREF(Sock, fEvents, pfEvents, cMillies);
1410 AssertMsgFailed(("Not supported!\n"));
1411 return VERR_NOT_SUPPORTED;
1412}
1413
1414/** @interface_method_impl{VDINTERFACETCPNET,pfnPoke} */
1415static DECLCALLBACK(int) drvvdINIPPoke(VDSOCKET Sock)
1416{
1417 RT_NOREF(Sock);
1418 AssertMsgFailed(("Not supported!\n"));
1419 return VERR_NOT_SUPPORTED;
1420}
1421
1422#endif /* VBOX_WITH_INIP */
1423
1424
1425/**
1426 * Checks the prerequisites for encrypted I/O.
1427 *
1428 * @returns VBox status code.
1429 * @param pThis The VD driver instance data.
1430 * @param fSetError Flag whether to set a runtime error.
1431 */
1432static int drvvdKeyCheckPrereqs(PVBOXDISK pThis, bool fSetError)
1433{
1434 if ( pThis->CfgCrypto.pCfgNode
1435 && !pThis->pIfSecKey)
1436 {
1437 AssertPtr(pThis->pIfSecKeyHlp);
1438 pThis->pIfSecKeyHlp->pfnKeyMissingNotify(pThis->pIfSecKeyHlp);
1439
1440 if (fSetError)
1441 {
1442 int rc = PDMDrvHlpVMSetRuntimeError(pThis->pDrvIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DrvVD_DEKMISSING",
1443 N_("VD: The DEK for this disk is missing"));
1444 AssertRC(rc);
1445 }
1446 return VERR_VD_DEK_MISSING;
1447 }
1448
1449 return VINF_SUCCESS;
1450}
1451
1452
1453/*********************************************************************************************************************************
1454* Media interface methods *
1455*********************************************************************************************************************************/
1456
1457/** @interface_method_impl{PDMIMEDIA,pfnRead} */
1458static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
1459 uint64_t off, void *pvBuf, size_t cbRead)
1460{
1461 int rc = VINF_SUCCESS;
1462
1463 LogFlowFunc(("off=%#llx pvBuf=%p cbRead=%d\n", off, pvBuf, cbRead));
1464 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1465
1466 /*
1467 * Check the state.
1468 */
1469 if (!pThis->pDisk)
1470 {
1471 AssertMsgFailed(("Invalid state! Not mounted!\n"));
1472 return VERR_PDM_MEDIA_NOT_MOUNTED;
1473 }
1474
1475 rc = drvvdKeyCheckPrereqs(pThis, true /* fSetError */);
1476 if (RT_FAILURE(rc))
1477 return rc;
1478
1479 STAM_REL_COUNTER_INC(&pThis->StatReqsSubmitted);
1480 STAM_REL_COUNTER_INC(&pThis->StatReqsRead);
1481
1482 if (!pThis->fBootAccelActive)
1483 rc = VDRead(pThis->pDisk, off, pvBuf, cbRead);
1484 else
1485 {
1486 /* Can we serve the request from the buffer? */
1487 if ( off >= pThis->offDisk
1488 && off - pThis->offDisk < pThis->cbDataValid)
1489 {
1490 size_t cbToCopy = RT_MIN(cbRead, pThis->offDisk + pThis->cbDataValid - off);
1491
1492 memcpy(pvBuf, pThis->pbData + (off - pThis->offDisk), cbToCopy);
1493 cbRead -= cbToCopy;
1494 off += cbToCopy;
1495 pvBuf = (char *)pvBuf + cbToCopy;
1496 }
1497
1498 if ( cbRead > 0
1499 && cbRead < pThis->cbBootAccelBuffer)
1500 {
1501 /* Increase request to the buffer size and read. */
1502 pThis->cbDataValid = RT_MIN(pThis->cbDisk - off, pThis->cbBootAccelBuffer);
1503 pThis->offDisk = off;
1504 rc = VDRead(pThis->pDisk, off, pThis->pbData, pThis->cbDataValid);
1505 if (RT_FAILURE(rc))
1506 pThis->cbDataValid = 0;
1507 else
1508 memcpy(pvBuf, pThis->pbData, cbRead);
1509 }
1510 else if (cbRead >= pThis->cbBootAccelBuffer)
1511 {
1512 pThis->fBootAccelActive = false; /* Deactiviate */
1513 }
1514 }
1515
1516 if (RT_SUCCESS(rc))
1517 {
1518 STAM_REL_COUNTER_INC(&pThis->StatReqsSucceeded);
1519 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbRead);
1520 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d\n%.*Rhxd\n", __FUNCTION__,
1521 off, pvBuf, cbRead, cbRead, pvBuf));
1522 }
1523 else
1524 STAM_REL_COUNTER_INC(&pThis->StatReqsFailed);
1525
1526 LogFlowFunc(("returns %Rrc\n", rc));
1527 return rc;
1528}
1529
1530/** @interface_method_impl{PDMIMEDIA,pfnRead} */
1531static DECLCALLBACK(int) drvvdReadPcBios(PPDMIMEDIA pInterface,
1532 uint64_t off, void *pvBuf, size_t cbRead)
1533{
1534 int rc = VINF_SUCCESS;
1535
1536 LogFlowFunc(("off=%#llx pvBuf=%p cbRead=%d\n", off, pvBuf, cbRead));
1537 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1538
1539 /*
1540 * Check the state.
1541 */
1542 if (!pThis->pDisk)
1543 {
1544 AssertMsgFailed(("Invalid state! Not mounted!\n"));
1545 return VERR_PDM_MEDIA_NOT_MOUNTED;
1546 }
1547
1548 if ( pThis->CfgCrypto.pCfgNode
1549 && !pThis->pIfSecKey)
1550 return VERR_VD_DEK_MISSING;
1551
1552 if (!pThis->fBootAccelActive)
1553 rc = VDRead(pThis->pDisk, off, pvBuf, cbRead);
1554 else
1555 {
1556 /* Can we serve the request from the buffer? */
1557 if ( off >= pThis->offDisk
1558 && off - pThis->offDisk < pThis->cbDataValid)
1559 {
1560 size_t cbToCopy = RT_MIN(cbRead, pThis->offDisk + pThis->cbDataValid - off);
1561
1562 memcpy(pvBuf, pThis->pbData + (off - pThis->offDisk), cbToCopy);
1563 cbRead -= cbToCopy;
1564 off += cbToCopy;
1565 pvBuf = (char *)pvBuf + cbToCopy;
1566 }
1567
1568 if ( cbRead > 0
1569 && cbRead < pThis->cbBootAccelBuffer)
1570 {
1571 /* Increase request to the buffer size and read. */
1572 pThis->cbDataValid = RT_MIN(pThis->cbDisk - off, pThis->cbBootAccelBuffer);
1573 pThis->offDisk = off;
1574 rc = VDRead(pThis->pDisk, off, pThis->pbData, pThis->cbDataValid);
1575 if (RT_FAILURE(rc))
1576 pThis->cbDataValid = 0;
1577 else
1578 memcpy(pvBuf, pThis->pbData, cbRead);
1579 }
1580 else if (cbRead >= pThis->cbBootAccelBuffer)
1581 {
1582 pThis->fBootAccelActive = false; /* Deactiviate */
1583 }
1584 }
1585
1586 if (RT_SUCCESS(rc))
1587 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d\n%.*Rhxd\n", __FUNCTION__,
1588 off, pvBuf, cbRead, cbRead, pvBuf));
1589 LogFlowFunc(("returns %Rrc\n", rc));
1590 return rc;
1591}
1592
1593
1594/** @interface_method_impl{PDMIMEDIA,pfnWrite} */
1595static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
1596 uint64_t off, const void *pvBuf,
1597 size_t cbWrite)
1598{
1599 LogFlowFunc(("off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite));
1600 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1601 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d\n%.*Rhxd\n", __FUNCTION__,
1602 off, pvBuf, cbWrite, cbWrite, pvBuf));
1603
1604 /*
1605 * Check the state.
1606 */
1607 if (!pThis->pDisk)
1608 {
1609 AssertMsgFailed(("Invalid state! Not mounted!\n"));
1610 return VERR_PDM_MEDIA_NOT_MOUNTED;
1611 }
1612
1613 int rc = drvvdKeyCheckPrereqs(pThis, true /* fSetError */);
1614 if (RT_FAILURE(rc))
1615 return rc;
1616
1617 /* Invalidate any buffer if boot acceleration is enabled. */
1618 if (pThis->fBootAccelActive)
1619 {
1620 pThis->cbDataValid = 0;
1621 pThis->offDisk = 0;
1622 }
1623
1624 STAM_REL_COUNTER_INC(&pThis->StatReqsSubmitted);
1625 STAM_REL_COUNTER_INC(&pThis->StatReqsWrite);
1626
1627 rc = VDWrite(pThis->pDisk, off, pvBuf, cbWrite);
1628#ifdef VBOX_PERIODIC_FLUSH
1629 if (pThis->cbFlushInterval)
1630 {
1631 pThis->cbDataWritten += (uint32_t)cbWrite;
1632 if (pThis->cbDataWritten > pThis->cbFlushInterval)
1633 {
1634 pThis->cbDataWritten = 0;
1635 VDFlush(pThis->pDisk);
1636 }
1637 }
1638#endif /* VBOX_PERIODIC_FLUSH */
1639
1640 if (RT_SUCCESS(rc))
1641 {
1642 STAM_REL_COUNTER_INC(&pThis->StatReqsSucceeded);
1643 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbWrite);
1644 }
1645 else
1646 STAM_REL_COUNTER_INC(&pThis->StatReqsFailed);
1647
1648 LogFlowFunc(("returns %Rrc\n", rc));
1649 return rc;
1650}
1651
1652/** @interface_method_impl{PDMIMEDIA,pfnFlush} */
1653static DECLCALLBACK(int) drvvdFlush(PPDMIMEDIA pInterface)
1654{
1655 LogFlowFunc(("\n"));
1656 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1657
1658 /*
1659 * Check the state.
1660 */
1661 if (!pThis->pDisk)
1662 {
1663 AssertMsgFailed(("Invalid state! Not mounted!\n"));
1664 return VERR_PDM_MEDIA_NOT_MOUNTED;
1665 }
1666
1667#ifdef VBOX_IGNORE_FLUSH
1668 if (pThis->fIgnoreFlush)
1669 return VINF_SUCCESS;
1670#endif /* VBOX_IGNORE_FLUSH */
1671
1672 STAM_REL_COUNTER_INC(&pThis->StatReqsSubmitted);
1673 STAM_REL_COUNTER_INC(&pThis->StatReqsFlush);
1674
1675 int rc = VDFlush(pThis->pDisk);
1676 if (RT_SUCCESS(rc))
1677 STAM_REL_COUNTER_INC(&pThis->StatReqsSucceeded);
1678 else
1679 STAM_REL_COUNTER_INC(&pThis->StatReqsFailed);
1680
1681 LogFlowFunc(("returns %Rrc\n", rc));
1682 return rc;
1683}
1684
1685/** @interface_method_impl{PDMIMEDIA,pfnMerge} */
1686static DECLCALLBACK(int) drvvdMerge(PPDMIMEDIA pInterface,
1687 PFNSIMPLEPROGRESS pfnProgress,
1688 void *pvUser)
1689{
1690 LogFlowFunc(("\n"));
1691 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1692 int rc = VINF_SUCCESS;
1693
1694 /*
1695 * Check the state.
1696 */
1697 if (!pThis->pDisk)
1698 {
1699 AssertMsgFailed(("Invalid state! Not mounted!\n"));
1700 return VERR_PDM_MEDIA_NOT_MOUNTED;
1701 }
1702
1703 /* Note: There is an unavoidable race between destruction and another
1704 * thread invoking this function. This is handled safely and gracefully by
1705 * atomically invalidating the lock handle in drvvdDestruct. */
1706 int rc2 = RTSemFastMutexRequest(pThis->MergeCompleteMutex);
1707 AssertRC(rc2);
1708 if (RT_SUCCESS(rc2) && pThis->fMergePending)
1709 {
1710 /* Take shortcut: PFNSIMPLEPROGRESS is exactly the same type as
1711 * PFNVDPROGRESS, so there's no need for a conversion function. */
1712 /** @todo maybe introduce a conversion which limits update frequency. */
1713 PVDINTERFACE pVDIfsOperation = NULL;
1714 VDINTERFACEPROGRESS VDIfProgress;
1715 VDIfProgress.pfnProgress = pfnProgress;
1716 rc2 = VDInterfaceAdd(&VDIfProgress.Core, "DrvVD_VDIProgress", VDINTERFACETYPE_PROGRESS,
1717 pvUser, sizeof(VDINTERFACEPROGRESS), &pVDIfsOperation);
1718 AssertRC(rc2);
1719 pThis->fMergePending = false;
1720 rc = VDMerge(pThis->pDisk, pThis->uMergeSource,
1721 pThis->uMergeTarget, pVDIfsOperation);
1722 }
1723 rc2 = RTSemFastMutexRelease(pThis->MergeCompleteMutex);
1724 AssertRC(rc2);
1725 LogFlowFunc(("returns %Rrc\n", rc));
1726 return rc;
1727}
1728
1729/** @interface_method_impl{PDMIMEDIA,pfnSetSecKeyIf} */
1730static DECLCALLBACK(int) drvvdSetSecKeyIf(PPDMIMEDIA pInterface, PPDMISECKEY pIfSecKey, PPDMISECKEYHLP pIfSecKeyHlp)
1731{
1732 LogFlowFunc(("\n"));
1733 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1734 int rc = VINF_SUCCESS;
1735
1736 if (pThis->CfgCrypto.pCfgNode)
1737 {
1738 PVDINTERFACE pVDIfFilter = NULL;
1739
1740 pThis->pIfSecKeyHlp = pIfSecKeyHlp;
1741
1742 if ( pThis->pIfSecKey
1743 && !pIfSecKey)
1744 {
1745 /* Unload the crypto filter first to make sure it doesn't access the keys anymore. */
1746 rc = VDFilterRemove(pThis->pDisk, VD_FILTER_FLAGS_DEFAULT);
1747 AssertRC(rc);
1748
1749 pThis->pIfSecKey = NULL;
1750 }
1751
1752 if ( pIfSecKey
1753 && RT_SUCCESS(rc))
1754 {
1755 pThis->pIfSecKey = pIfSecKey;
1756
1757 rc = VDInterfaceAdd(&pThis->VDIfCfg.Core, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
1758 &pThis->CfgCrypto, sizeof(VDINTERFACECONFIG), &pVDIfFilter);
1759 AssertRC(rc);
1760
1761 rc = VDInterfaceAdd(&pThis->VDIfCrypto.Core, "DrvVD_Crypto", VDINTERFACETYPE_CRYPTO,
1762 pThis, sizeof(VDINTERFACECRYPTO), &pVDIfFilter);
1763 AssertRC(rc);
1764
1765 /* Load the crypt filter plugin. */
1766 rc = VDFilterAdd(pThis->pDisk, "CRYPT", VD_FILTER_FLAGS_DEFAULT, pVDIfFilter);
1767 if (RT_FAILURE(rc))
1768 pThis->pIfSecKey = NULL;
1769 }
1770 }
1771 else
1772 rc = VERR_NOT_SUPPORTED;
1773
1774 LogFlowFunc(("returns %Rrc\n", rc));
1775 return rc;
1776}
1777
1778/** @interface_method_impl{PDMIMEDIA,pfnGetSize} */
1779static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
1780{
1781 LogFlowFunc(("\n"));
1782 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1783
1784 /*
1785 * Check the state.
1786 */
1787 if (!pThis->pDisk)
1788 return 0;
1789
1790 uint64_t cb = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
1791 LogFlowFunc(("returns %#llx (%llu)\n", cb, cb));
1792 return cb;
1793}
1794
1795/** @interface_method_impl{PDMIMEDIA,pfnGetSectorSize} */
1796static DECLCALLBACK(uint32_t) drvvdGetSectorSize(PPDMIMEDIA pInterface)
1797{
1798 LogFlowFunc(("\n"));
1799 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1800
1801 /*
1802 * Check the state.
1803 */
1804 if (!pThis->pDisk)
1805 return 0;
1806
1807 uint32_t cb = VDGetSectorSize(pThis->pDisk, VD_LAST_IMAGE);
1808 LogFlowFunc(("returns %u\n", cb));
1809 return cb;
1810}
1811
1812/** @interface_method_impl{PDMIMEDIA,pfnIsReadOnly} */
1813static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
1814{
1815 LogFlowFunc(("\n"));
1816 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1817
1818 /*
1819 * Check the state.
1820 */
1821 if (!pThis->pDisk)
1822 return false;
1823
1824 bool f = VDIsReadOnly(pThis->pDisk);
1825 LogFlowFunc(("returns %d\n", f));
1826 return f;
1827}
1828
1829/** @interface_method_impl{PDMIMEDIA,pfnIsNonRotational} */
1830static DECLCALLBACK(bool) drvvdIsNonRotational(PPDMIMEDIA pInterface)
1831{
1832 LogFlowFunc(("\n"));
1833 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1834
1835 return pThis->fNonRotational;
1836}
1837
1838/** @interface_method_impl{PDMIMEDIA,pfnBiosGetPCHSGeometry} */
1839static DECLCALLBACK(int) drvvdBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
1840 PPDMMEDIAGEOMETRY pPCHSGeometry)
1841{
1842 LogFlowFunc(("\n"));
1843 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1844 VDGEOMETRY geo;
1845
1846 /*
1847 * Check the state.
1848 */
1849 if (!pThis->pDisk)
1850 return VERR_PDM_MEDIA_NOT_MOUNTED;
1851
1852 /*
1853 * Use configured/cached values if present.
1854 */
1855 if ( pThis->PCHSGeometry.cCylinders > 0
1856 && pThis->PCHSGeometry.cHeads > 0
1857 && pThis->PCHSGeometry.cSectors > 0)
1858 {
1859 *pPCHSGeometry = pThis->PCHSGeometry;
1860 LogFlow(("%s: returns VINF_SUCCESS {%d,%d,%d}\n", __FUNCTION__, pThis->PCHSGeometry.cCylinders, pThis->PCHSGeometry.cHeads, pThis->PCHSGeometry.cSectors));
1861 return VINF_SUCCESS;
1862 }
1863
1864 int rc = VDGetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1865 if (RT_SUCCESS(rc))
1866 {
1867 pPCHSGeometry->cCylinders = geo.cCylinders;
1868 pPCHSGeometry->cHeads = geo.cHeads;
1869 pPCHSGeometry->cSectors = geo.cSectors;
1870 pThis->PCHSGeometry = *pPCHSGeometry;
1871 }
1872 else
1873 {
1874 LogFunc(("geometry not available.\n"));
1875 rc = VERR_PDM_GEOMETRY_NOT_SET;
1876 }
1877 LogFlowFunc(("returns %Rrc (CHS=%d/%d/%d)\n",
1878 rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1879 return rc;
1880}
1881
1882/** @interface_method_impl{PDMIMEDIA,pfnBiosSetPCHSGeometry} */
1883static DECLCALLBACK(int) drvvdBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
1884 PCPDMMEDIAGEOMETRY pPCHSGeometry)
1885{
1886 LogFlowFunc(("CHS=%d/%d/%d\n",
1887 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1888 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1889 VDGEOMETRY geo;
1890
1891 /*
1892 * Check the state.
1893 */
1894 if (!pThis->pDisk)
1895 {
1896 AssertMsgFailed(("Invalid state! Not mounted!\n"));
1897 return VERR_PDM_MEDIA_NOT_MOUNTED;
1898 }
1899
1900 geo.cCylinders = pPCHSGeometry->cCylinders;
1901 geo.cHeads = pPCHSGeometry->cHeads;
1902 geo.cSectors = pPCHSGeometry->cSectors;
1903 int rc = VDSetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1904 if (rc == VERR_VD_GEOMETRY_NOT_SET)
1905 rc = VERR_PDM_GEOMETRY_NOT_SET;
1906 if (RT_SUCCESS(rc))
1907 pThis->PCHSGeometry = *pPCHSGeometry;
1908 LogFlowFunc(("returns %Rrc\n", rc));
1909 return rc;
1910}
1911
1912/** @interface_method_impl{PDMIMEDIA,pfnBiosGetLCHSGeometry} */
1913static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
1914 PPDMMEDIAGEOMETRY pLCHSGeometry)
1915{
1916 LogFlowFunc(("\n"));
1917 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1918 VDGEOMETRY geo;
1919
1920 /*
1921 * Check the state.
1922 */
1923 if (!pThis->pDisk)
1924 return VERR_PDM_MEDIA_NOT_MOUNTED;
1925
1926 /*
1927 * Use configured/cached values if present.
1928 */
1929 if ( pThis->LCHSGeometry.cCylinders > 0
1930 && pThis->LCHSGeometry.cHeads > 0
1931 && pThis->LCHSGeometry.cSectors > 0)
1932 {
1933 *pLCHSGeometry = pThis->LCHSGeometry;
1934 LogFlow(("%s: returns VINF_SUCCESS {%d,%d,%d}\n", __FUNCTION__, pThis->LCHSGeometry.cCylinders, pThis->LCHSGeometry.cHeads, pThis->LCHSGeometry.cSectors));
1935 return VINF_SUCCESS;
1936 }
1937
1938 int rc = VDGetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1939 if (RT_SUCCESS(rc))
1940 {
1941 pLCHSGeometry->cCylinders = geo.cCylinders;
1942 pLCHSGeometry->cHeads = geo.cHeads;
1943 pLCHSGeometry->cSectors = geo.cSectors;
1944 pThis->LCHSGeometry = *pLCHSGeometry;
1945 }
1946 else
1947 {
1948 LogFunc(("geometry not available.\n"));
1949 rc = VERR_PDM_GEOMETRY_NOT_SET;
1950 }
1951 LogFlowFunc(("returns %Rrc (CHS=%d/%d/%d)\n",
1952 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1953 return rc;
1954}
1955
1956/** @interface_method_impl{PDMIMEDIA,pfnBiosSetLCHSGeometry} */
1957static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
1958 PCPDMMEDIAGEOMETRY pLCHSGeometry)
1959{
1960 LogFlowFunc(("CHS=%d/%d/%d\n",
1961 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1962 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1963 VDGEOMETRY geo;
1964
1965 /*
1966 * Check the state.
1967 */
1968 if (!pThis->pDisk)
1969 {
1970 AssertMsgFailed(("Invalid state! Not mounted!\n"));
1971 return VERR_PDM_MEDIA_NOT_MOUNTED;
1972 }
1973
1974 geo.cCylinders = pLCHSGeometry->cCylinders;
1975 geo.cHeads = pLCHSGeometry->cHeads;
1976 geo.cSectors = pLCHSGeometry->cSectors;
1977 int rc = VDSetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1978 if (rc == VERR_VD_GEOMETRY_NOT_SET)
1979 rc = VERR_PDM_GEOMETRY_NOT_SET;
1980 if (RT_SUCCESS(rc))
1981 pThis->LCHSGeometry = *pLCHSGeometry;
1982 LogFlowFunc(("returns %Rrc\n", rc));
1983 return rc;
1984}
1985
1986/** @interface_method_impl{PDMIMEDIA,pfnBiosIsVisible} */
1987static DECLCALLBACK(bool) drvvdBiosIsVisible(PPDMIMEDIA pInterface)
1988{
1989 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1990 LogFlow(("drvvdBiosIsVisible: returns %d\n", pThis->fBiosVisible));
1991 return pThis->fBiosVisible;
1992}
1993
1994/** @interface_method_impl{PDMIMEDIA,pfnGetType} */
1995static DECLCALLBACK(PDMMEDIATYPE) drvvdGetType(PPDMIMEDIA pInterface)
1996{
1997 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1998 LogFlow(("drvvdBiosIsVisible: returns %d\n", pThis->fBiosVisible));
1999 return pThis->enmType;
2000}
2001
2002/** @interface_method_impl{PDMIMEDIA,pfnGetUuid} */
2003static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
2004{
2005 LogFlowFunc(("\n"));
2006 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2007
2008 /*
2009 * Copy the uuid.
2010 */
2011 *pUuid = pThis->Uuid;
2012 LogFlowFunc(("returns {%RTuuid}\n", pUuid));
2013 return VINF_SUCCESS;
2014}
2015
2016/** @interface_method_impl{PDMIMEDIA,pfnDiscard} */
2017static DECLCALLBACK(int) drvvdDiscard(PPDMIMEDIA pInterface, PCRTRANGE paRanges, unsigned cRanges)
2018{
2019 LogFlowFunc(("\n"));
2020 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2021
2022 STAM_REL_COUNTER_INC(&pThis->StatReqsSubmitted);
2023 STAM_REL_COUNTER_INC(&pThis->StatReqsDiscard);
2024
2025 int rc = VDDiscardRanges(pThis->pDisk, paRanges, cRanges);
2026 if (RT_SUCCESS(rc))
2027 STAM_REL_COUNTER_INC(&pThis->StatReqsSucceeded);
2028 else
2029 STAM_REL_COUNTER_INC(&pThis->StatReqsFailed);
2030
2031 LogFlowFunc(("returns %Rrc\n", rc));
2032 return rc;
2033}
2034
2035/** @interface_method_impl{PDMIMEDIA,pfnGetRegionCount} */
2036static DECLCALLBACK(uint32_t) drvvdGetRegionCount(PPDMIMEDIA pInterface)
2037{
2038 LogFlowFunc(("\n"));
2039 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2040 uint32_t cRegions = 0;
2041
2042 if (pThis->pDisk)
2043 {
2044 if (!pThis->pRegionList)
2045 {
2046 int rc = VDQueryRegions(pThis->pDisk, VD_LAST_IMAGE, VD_REGION_LIST_F_LOC_SIZE_BLOCKS,
2047 &pThis->pRegionList);
2048 if (RT_SUCCESS(rc))
2049 cRegions = pThis->pRegionList->cRegions;
2050 }
2051 else
2052 cRegions = pThis->pRegionList->cRegions;
2053 }
2054
2055 LogFlowFunc(("returns %u\n", cRegions));
2056 return cRegions;
2057}
2058
2059/** @interface_method_impl{PDMIMEDIA,pfnQueryRegionProperties} */
2060static DECLCALLBACK(int) drvvdQueryRegionProperties(PPDMIMEDIA pInterface, uint32_t uRegion, uint64_t *pu64LbaStart,
2061 uint64_t *pcBlocks, uint64_t *pcbBlock,
2062 PVDREGIONDATAFORM penmDataForm)
2063{
2064 LogFlowFunc(("\n"));
2065 int rc = VINF_SUCCESS;
2066 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2067
2068 if ( pThis->pRegionList
2069 && uRegion < pThis->pRegionList->cRegions)
2070 {
2071 PCVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[uRegion];
2072
2073 if (pu64LbaStart)
2074 *pu64LbaStart = pRegion->offRegion;
2075 if (pcBlocks)
2076 *pcBlocks = pRegion->cRegionBlocksOrBytes;
2077 if (pcbBlock)
2078 *pcbBlock = pRegion->cbBlock;
2079 if (penmDataForm)
2080 *penmDataForm = pRegion->enmDataForm;
2081 }
2082 else
2083 rc = VERR_NOT_FOUND;
2084
2085 LogFlowFunc(("returns %Rrc\n", rc));
2086 return rc;
2087}
2088
2089/** @interface_method_impl{PDMIMEDIA,pfnQueryRegionPropertiesForLba} */
2090static DECLCALLBACK(int) drvvdQueryRegionPropertiesForLba(PPDMIMEDIA pInterface, uint64_t u64LbaStart,
2091 uint32_t *puRegion, uint64_t *pcBlocks,
2092 uint64_t *pcbBlock, PVDREGIONDATAFORM penmDataForm)
2093{
2094 LogFlowFunc(("\n"));
2095 int rc = VINF_SUCCESS;
2096 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
2097
2098 if (!pThis->pRegionList)
2099 rc = VDQueryRegions(pThis->pDisk, VD_LAST_IMAGE, VD_REGION_LIST_F_LOC_SIZE_BLOCKS,
2100 &pThis->pRegionList);
2101
2102 if (RT_SUCCESS(rc))
2103 {
2104 rc = VERR_NOT_FOUND;
2105
2106 for (uint32_t i = 0; i < pThis->pRegionList->cRegions; i++)
2107 {
2108 PCVDREGIONDESC pRegion = &pThis->pRegionList->aRegions[i];
2109 if ( pRegion->offRegion <= u64LbaStart
2110 && pRegion->offRegion + pRegion->cRegionBlocksOrBytes > u64LbaStart)
2111 {
2112 uint64_t offRegion = u64LbaStart - pRegion->offRegion;
2113
2114 if (puRegion)
2115 *puRegion = i;
2116 if (pcBlocks)
2117 *pcBlocks = pRegion->cRegionBlocksOrBytes - offRegion;
2118 if (pcbBlock)
2119 *pcbBlock = pRegion->cbBlock;
2120 if (penmDataForm)
2121 *penmDataForm = pRegion->enmDataForm;
2122
2123 rc = VINF_SUCCESS;
2124 }
2125 }
2126 }
2127 else
2128 rc = VERR_NOT_FOUND;
2129
2130 LogFlowFunc(("returns %Rrc\n", rc));
2131 return rc;
2132}
2133
2134/* -=-=-=-=- IMount -=-=-=-=- */
2135
2136/** @interface_method_impl{PDMIMOUNT,pfnUnmount} */
2137static DECLCALLBACK(int) drvvdUnmount(PPDMIMOUNT pInterface, bool fForce, bool fEject)
2138{
2139 RT_NOREF(fEject);
2140 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMount);
2141
2142 /*
2143 * Validate state.
2144 */
2145 if (!pThis->pDisk)
2146 {
2147 Log(("drvvdUnmount: Not mounted\n"));
2148 return VERR_PDM_MEDIA_NOT_MOUNTED;
2149 }
2150 if (pThis->fLocked && !fForce)
2151 {
2152 Log(("drvvdUnmount: Locked\n"));
2153 return VERR_PDM_MEDIA_LOCKED;
2154 }
2155
2156 /* Media is no longer locked even if it was previously. */
2157 pThis->fLocked = false;
2158 drvvdPowerOffOrDestructOrUnmount(pThis->pDrvIns);
2159
2160 /*
2161 * Notify driver/device above us.
2162 */
2163 if (pThis->pDrvMountNotify)
2164 pThis->pDrvMountNotify->pfnUnmountNotify(pThis->pDrvMountNotify);
2165 Log(("drvblockUnmount: success\n"));
2166 return VINF_SUCCESS;
2167}
2168
2169
2170/** @interface_method_impl{PDMIMOUNT,pfnIsMounted} */
2171static DECLCALLBACK(bool) drvvdIsMounted(PPDMIMOUNT pInterface)
2172{
2173 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMount);
2174 return pThis->pDisk != NULL;
2175}
2176
2177/** @interface_method_impl{PDMIMOUNT,pfnLock} */
2178static DECLCALLBACK(int) drvvdLock(PPDMIMOUNT pInterface)
2179{
2180 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMount);
2181 Log(("drvblockLock: %d -> %d\n", pThis->fLocked, true));
2182 pThis->fLocked = true;
2183 return VINF_SUCCESS;
2184}
2185
2186/** @interface_method_impl{PDMIMOUNT,pfnUnlock} */
2187static DECLCALLBACK(int) drvvdUnlock(PPDMIMOUNT pInterface)
2188{
2189 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMount);
2190 Log(("drvblockUnlock: %d -> %d\n", pThis->fLocked, false));
2191 pThis->fLocked = false;
2192 return VINF_SUCCESS;
2193}
2194
2195/** @interface_method_impl{PDMIMOUNT,pfnIsLocked} */
2196static DECLCALLBACK(bool) drvvdIsLocked(PPDMIMOUNT pInterface)
2197{
2198 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMount);
2199 return pThis->fLocked;
2200}
2201
2202
2203static DECLCALLBACK(void) drvvdBlkCacheReqComplete(void *pvUser1, void *pvUser2, int rcReq)
2204{
2205 PVBOXDISK pThis = (PVBOXDISK)pvUser1;
2206
2207 AssertPtr(pThis->pBlkCache);
2208 PDMDrvHlpBlkCacheIoXferComplete(pThis->pDrvIns, pThis->pBlkCache, (PPDMBLKCACHEIOXFER)pvUser2, rcReq);
2209}
2210
2211
2212/** @copydoc FNPDMBLKCACHEXFERCOMPLETEDRV */
2213static DECLCALLBACK(void) drvvdBlkCacheXferCompleteIoReq(PPDMDRVINS pDrvIns, void *pvUser, int rc)
2214{
2215 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
2216
2217 drvvdMediaExIoReqCompleteWorker(pThis, (PPDMMEDIAEXIOREQINT)pvUser, rc, true /* fUpNotify */);
2218}
2219
2220/** @copydoc FNPDMBLKCACHEXFERENQUEUEDRV */
2221static DECLCALLBACK(int) drvvdBlkCacheXferEnqueue(PPDMDRVINS pDrvIns,
2222 PDMBLKCACHEXFERDIR enmXferDir,
2223 uint64_t off, size_t cbXfer,
2224 PCRTSGBUF pSgBuf, PPDMBLKCACHEIOXFER hIoXfer)
2225{
2226 int rc = VINF_SUCCESS;
2227 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
2228
2229 Assert (!pThis->CfgCrypto.pCfgNode);
2230
2231 switch (enmXferDir)
2232 {
2233 case PDMBLKCACHEXFERDIR_READ:
2234 rc = VDAsyncRead(pThis->pDisk, off, cbXfer, pSgBuf, drvvdBlkCacheReqComplete,
2235 pThis, hIoXfer);
2236 break;
2237 case PDMBLKCACHEXFERDIR_WRITE:
2238 rc = VDAsyncWrite(pThis->pDisk, off, cbXfer, pSgBuf, drvvdBlkCacheReqComplete,
2239 pThis, hIoXfer);
2240 break;
2241 case PDMBLKCACHEXFERDIR_FLUSH:
2242 rc = VDAsyncFlush(pThis->pDisk, drvvdBlkCacheReqComplete, pThis, hIoXfer);
2243 break;
2244 default:
2245 AssertMsgFailed(("Invalid transfer type %d\n", enmXferDir));
2246 rc = VERR_INVALID_PARAMETER;
2247 }
2248
2249 if (rc == VINF_VD_ASYNC_IO_FINISHED)
2250 PDMDrvHlpBlkCacheIoXferComplete(pThis->pDrvIns, pThis->pBlkCache, hIoXfer, VINF_SUCCESS);
2251 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
2252 PDMDrvHlpBlkCacheIoXferComplete(pThis->pDrvIns, pThis->pBlkCache, hIoXfer, rc);
2253
2254 return VINF_SUCCESS;
2255}
2256
2257/** @copydoc FNPDMBLKCACHEXFERENQUEUEDISCARDDRV */
2258static DECLCALLBACK(int) drvvdBlkCacheXferEnqueueDiscard(PPDMDRVINS pDrvIns, PCRTRANGE paRanges,
2259 unsigned cRanges, PPDMBLKCACHEIOXFER hIoXfer)
2260{
2261 int rc = VINF_SUCCESS;
2262 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
2263
2264 rc = VDAsyncDiscardRanges(pThis->pDisk, paRanges, cRanges,
2265 drvvdBlkCacheReqComplete, pThis, hIoXfer);
2266
2267 if (rc == VINF_VD_ASYNC_IO_FINISHED)
2268 PDMDrvHlpBlkCacheIoXferComplete(pThis->pDrvIns, pThis->pBlkCache, hIoXfer, VINF_SUCCESS);
2269 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
2270 PDMDrvHlpBlkCacheIoXferComplete(pThis->pDrvIns, pThis->pBlkCache, hIoXfer, rc);
2271
2272 return VINF_SUCCESS;
2273}
2274
2275
2276/*********************************************************************************************************************************
2277* Extended media interface methods *
2278*********************************************************************************************************************************/
2279
2280static void drvvdMediaExIoReqWarningDiskFull(PPDMDRVINS pDrvIns)
2281{
2282 int rc;
2283 LogRel(("VD#%u: Host disk full\n", pDrvIns->iInstance));
2284 rc = PDMDrvHlpVMSetRuntimeError(pDrvIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DrvVD_DISKFULL",
2285 N_("Host system reported disk full. VM execution is suspended. You can resume after freeing some space"));
2286 AssertRC(rc);
2287}
2288
2289static void drvvdMediaExIoReqWarningFileTooBig(PPDMDRVINS pDrvIns)
2290{
2291 int rc;
2292 LogRel(("VD#%u: File too big\n", pDrvIns->iInstance));
2293 rc = PDMDrvHlpVMSetRuntimeError(pDrvIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DrvVD_FILETOOBIG",
2294 N_("Host system reported that the file size limit of the host file system has been exceeded. VM execution is suspended. You need to move your virtual hard disk to a filesystem which allows bigger files"));
2295 AssertRC(rc);
2296}
2297
2298static void drvvdMediaExIoReqWarningISCSI(PPDMDRVINS pDrvIns)
2299{
2300 int rc;
2301 LogRel(("VD#%u: iSCSI target unavailable\n", pDrvIns->iInstance));
2302 rc = PDMDrvHlpVMSetRuntimeError(pDrvIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DrvVD_ISCSIDOWN",
2303 N_("The iSCSI target has stopped responding. VM execution is suspended. You can resume when it is available again"));
2304 AssertRC(rc);
2305}
2306
2307static void drvvdMediaExIoReqWarningFileStale(PPDMDRVINS pDrvIns)
2308{
2309 int rc;
2310 LogRel(("VD#%u: File handle became stale\n", pDrvIns->iInstance));
2311 rc = PDMDrvHlpVMSetRuntimeError(pDrvIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DrvVD_ISCSIDOWN",
2312 N_("The file became stale (often due to a restarted NFS server). VM execution is suspended. You can resume when it is available again"));
2313 AssertRC(rc);
2314}
2315
2316static void drvvdMediaExIoReqWarningDekMissing(PPDMDRVINS pDrvIns)
2317{
2318 LogRel(("VD#%u: DEK is missing\n", pDrvIns->iInstance));
2319 int rc = PDMDrvHlpVMSetRuntimeError(pDrvIns, VMSETRTERR_FLAGS_SUSPEND | VMSETRTERR_FLAGS_NO_WAIT, "DrvVD_DEKMISSING",
2320 N_("VD: The DEK for this disk is missing"));
2321 AssertRC(rc);
2322}
2323
2324/**
2325 * Checks whether a given status code indicates a recoverable error
2326 * suspending the VM if it is.
2327 *
2328 * @returns Flag indicating whether the status code is a recoverable error
2329 * (full disk, broken network connection).
2330 * @param pThis VBox disk container instance data.
2331 * @param rc Status code to check.
2332 */
2333static bool drvvdMediaExIoReqIsRedoSetWarning(PVBOXDISK pThis, int rc)
2334{
2335 if (rc == VERR_DISK_FULL)
2336 {
2337 if (ASMAtomicCmpXchgBool(&pThis->fRedo, true, false))
2338 drvvdMediaExIoReqWarningDiskFull(pThis->pDrvIns);
2339 return true;
2340 }
2341 if (rc == VERR_FILE_TOO_BIG)
2342 {
2343 if (ASMAtomicCmpXchgBool(&pThis->fRedo, true, false))
2344 drvvdMediaExIoReqWarningFileTooBig(pThis->pDrvIns);
2345 return true;
2346 }
2347 if (rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_REFUSED)
2348 {
2349 /* iSCSI connection abort (first error) or failure to reestablish
2350 * connection (second error). Pause VM. On resume we'll retry. */
2351 if (ASMAtomicCmpXchgBool(&pThis->fRedo, true, false))
2352 drvvdMediaExIoReqWarningISCSI(pThis->pDrvIns);
2353 return true;
2354 }
2355 if (rc == VERR_STALE_FILE_HANDLE)
2356 {
2357 if (ASMAtomicCmpXchgBool(&pThis->fRedo, true, false))
2358 drvvdMediaExIoReqWarningFileStale(pThis->pDrvIns);
2359 return true;
2360 }
2361 if (rc == VERR_VD_DEK_MISSING)
2362 {
2363 /* Error message already set. */
2364 if (ASMAtomicCmpXchgBool(&pThis->fRedo, true, false))
2365 drvvdMediaExIoReqWarningDekMissing(pThis->pDrvIns);
2366 return true;
2367 }
2368
2369 return false;
2370}
2371
2372/**
2373 * Syncs the memory buffers between the I/O request allocator and the internal buffer.
2374 *
2375 * @returns VBox status code.
2376 * @param pThis VBox disk container instance data.
2377 * @param pIoReq I/O request to sync.
2378 * @param fToIoBuf Flag indicating the sync direction.
2379 * true to copy data from the allocators buffer to our internal buffer.
2380 * false for the other direction.
2381 */
2382DECLINLINE(int) drvvdMediaExIoReqBufSync(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, bool fToIoBuf)
2383{
2384 int rc = VINF_SUCCESS;
2385
2386 Assert(pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE);
2387 Assert(pIoReq->ReadWrite.cbIoBuf > 0);
2388
2389 if (!pIoReq->ReadWrite.fDirectBuf)
2390 {
2391 /* Make sure the buffer is reset. */
2392 RTSgBufReset(&pIoReq->ReadWrite.IoBuf.SgBuf);
2393
2394 size_t const offSrc = pIoReq->ReadWrite.cbReq - pIoReq->ReadWrite.cbReqLeft;
2395 Assert((uint32_t)offSrc == offSrc);
2396 if (fToIoBuf)
2397 rc = pThis->pDrvMediaExPort->pfnIoReqCopyToBuf(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0], (uint32_t)offSrc,
2398 &pIoReq->ReadWrite.IoBuf.SgBuf,
2399 RT_MIN(pIoReq->ReadWrite.cbIoBuf, pIoReq->ReadWrite.cbReqLeft));
2400 else
2401 rc = pThis->pDrvMediaExPort->pfnIoReqCopyFromBuf(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0], (uint32_t)offSrc,
2402 &pIoReq->ReadWrite.IoBuf.SgBuf,
2403 (uint32_t)RT_MIN(pIoReq->ReadWrite.cbIoBuf, pIoReq->ReadWrite.cbReqLeft));
2404
2405 RTSgBufReset(&pIoReq->ReadWrite.IoBuf.SgBuf);
2406 }
2407 return rc;
2408}
2409
2410/**
2411 * Hashes the I/O request ID to an index for the allocated I/O request bin.
2412 */
2413DECLINLINE(unsigned) drvvdMediaExIoReqIdHash(PDMMEDIAEXIOREQID uIoReqId)
2414{
2415 return uIoReqId % DRVVD_VDIOREQ_ALLOC_BINS; /** @todo Find something better? */
2416}
2417
2418/**
2419 * Inserts the given I/O request in to the list of allocated I/O requests.
2420 *
2421 * @returns VBox status code.
2422 * @param pThis VBox disk container instance data.
2423 * @param pIoReq I/O request to insert.
2424 */
2425static int drvvdMediaExIoReqInsert(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
2426{
2427 int rc = VINF_SUCCESS;
2428 unsigned idxBin = drvvdMediaExIoReqIdHash(pIoReq->uIoReqId);
2429
2430 rc = RTSemFastMutexRequest(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
2431 if (RT_SUCCESS(rc))
2432 {
2433 /* Search for conflicting I/O request ID. */
2434 PPDMMEDIAEXIOREQINT pIt;
2435 RTListForEach(&pThis->aIoReqAllocBins[idxBin].LstIoReqAlloc, pIt, PDMMEDIAEXIOREQINT, NdAllocatedList)
2436 {
2437 if (RT_UNLIKELY( pIt->uIoReqId == pIoReq->uIoReqId
2438 && pIt->enmState != VDIOREQSTATE_CANCELED))
2439 {
2440 rc = VERR_PDM_MEDIAEX_IOREQID_CONFLICT;
2441 break;
2442 }
2443 }
2444 if (RT_SUCCESS(rc))
2445 RTListAppend(&pThis->aIoReqAllocBins[idxBin].LstIoReqAlloc, &pIoReq->NdAllocatedList);
2446 RTSemFastMutexRelease(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
2447 }
2448
2449 return rc;
2450}
2451
2452/**
2453 * Removes the given I/O request from the list of allocated I/O requests.
2454 *
2455 * @returns VBox status code.
2456 * @param pThis VBox disk container instance data.
2457 * @param pIoReq I/O request to insert.
2458 */
2459static int drvvdMediaExIoReqRemove(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
2460{
2461 int rc = VINF_SUCCESS;
2462 unsigned idxBin = drvvdMediaExIoReqIdHash(pIoReq->uIoReqId);
2463
2464 rc = RTSemFastMutexRequest(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
2465 if (RT_SUCCESS(rc))
2466 {
2467 RTListNodeRemove(&pIoReq->NdAllocatedList);
2468 RTSemFastMutexRelease(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
2469 }
2470
2471 return rc;
2472}
2473
2474/**
2475 * Retires a given I/O request marking it as complete and notiyfing the
2476 * device/driver above about the completion if requested.
2477 *
2478 * @param pThis VBox disk container instance data.
2479 * @param pIoReq I/O request to complete.
2480 * @param rcReq The status code the request completed with.
2481 * @param fUpNotify Flag whether to notify the driver/device above us about the completion.
2482 */
2483static void drvvdMediaExIoReqRetire(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, int rcReq, bool fUpNotify)
2484{
2485 LogFlowFunc(("pThis=%#p pIoReq=%#p rcReq=%Rrc fUpNotify=%RTbool\n",
2486 pThis, pIoReq, rcReq, fUpNotify));
2487
2488 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_COMPLETING, VDIOREQSTATE_ACTIVE);
2489 if (fXchg)
2490 {
2491 uint32_t cNew = ASMAtomicDecU32(&pThis->cIoReqsActive);
2492 AssertMsg(cNew != UINT32_MAX, ("Number of active requests underflowed!\n")); RT_NOREF(cNew);
2493 }
2494 else
2495 {
2496 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
2497 rcReq = VERR_PDM_MEDIAEX_IOREQ_CANCELED;
2498 }
2499
2500 ASMAtomicXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_COMPLETED);
2501 drvvdMediaExIoReqBufFree(pThis, pIoReq);
2502
2503 /*
2504 * Leave a release log entry if the request was active for more than 25 seconds
2505 * (30 seconds is the timeout of the guest).
2506 */
2507 uint64_t tsNow = RTTimeMilliTS();
2508 if (tsNow - pIoReq->tsSubmit >= 25 * 1000)
2509 {
2510 const char *pcszReq = NULL;
2511
2512 switch (pIoReq->enmType)
2513 {
2514 case PDMMEDIAEXIOREQTYPE_READ:
2515 pcszReq = "Read";
2516 break;
2517 case PDMMEDIAEXIOREQTYPE_WRITE:
2518 pcszReq = "Write";
2519 break;
2520 case PDMMEDIAEXIOREQTYPE_FLUSH:
2521 pcszReq = "Flush";
2522 break;
2523 case PDMMEDIAEXIOREQTYPE_DISCARD:
2524 pcszReq = "Discard";
2525 break;
2526 default:
2527 pcszReq = "<Invalid>";
2528 }
2529
2530 LogRel(("VD#%u: %s request was active for %llu seconds\n",
2531 pThis->pDrvIns->iInstance, pcszReq, (tsNow - pIoReq->tsSubmit) / 1000));
2532 }
2533
2534 if (RT_FAILURE(rcReq))
2535 {
2536 /* Log the error. */
2537 if (pThis->cErrors++ < DRVVD_MAX_LOG_REL_ERRORS)
2538 {
2539 if (rcReq == VERR_PDM_MEDIAEX_IOREQ_CANCELED)
2540 {
2541 if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_FLUSH)
2542 LogRel(("VD#%u: Aborted flush returned rc=%Rrc\n",
2543 pThis->pDrvIns->iInstance, rcReq));
2544 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD)
2545 LogRel(("VD#%u: Aborted discard returned rc=%Rrc\n",
2546 pThis->pDrvIns->iInstance, rcReq));
2547 else
2548 LogRel(("VD#%u: Aborted %s (%u bytes left) returned rc=%Rrc\n",
2549 pThis->pDrvIns->iInstance,
2550 pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
2551 ? "read"
2552 : "write",
2553 pIoReq->ReadWrite.cbReqLeft, rcReq));
2554 }
2555 else
2556 {
2557 if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_FLUSH)
2558 LogRel(("VD#%u: Flush returned rc=%Rrc\n",
2559 pThis->pDrvIns->iInstance, rcReq));
2560 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD)
2561 LogRel(("VD#%u: Discard returned rc=%Rrc\n",
2562 pThis->pDrvIns->iInstance, rcReq));
2563 else
2564 LogRel(("VD#%u: %s (%u bytes left) returned rc=%Rrc\n",
2565 pThis->pDrvIns->iInstance,
2566 pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
2567 ? "Read"
2568 : "Write",
2569 pIoReq->ReadWrite.cbReqLeft, rcReq));
2570 }
2571 }
2572
2573 STAM_REL_COUNTER_INC(&pThis->StatReqsFailed);
2574 }
2575 else
2576 {
2577 STAM_REL_COUNTER_INC(&pThis->StatReqsSucceeded);
2578
2579 switch (pIoReq->enmType)
2580 {
2581 case PDMMEDIAEXIOREQTYPE_READ:
2582 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, pIoReq->ReadWrite.cbReq);
2583 break;
2584 case PDMMEDIAEXIOREQTYPE_WRITE:
2585 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, pIoReq->ReadWrite.cbReq);
2586 break;
2587 default:
2588 break;
2589 }
2590 }
2591
2592 if (fUpNotify)
2593 {
2594 int rc = pThis->pDrvMediaExPort->pfnIoReqCompleteNotify(pThis->pDrvMediaExPort,
2595 pIoReq, &pIoReq->abAlloc[0], rcReq);
2596 AssertRC(rc);
2597 }
2598
2599 LogFlowFunc(("returns\n"));
2600}
2601
2602/**
2603 * I/O request completion worker.
2604 *
2605 * @returns VBox status code.
2606 * @param pThis VBox disk container instance data.
2607 * @param pIoReq I/O request to complete.
2608 * @param rcReq The status code the request completed with.
2609 * @param fUpNotify Flag whether to notify the driver/device above us about the completion.
2610 */
2611static int drvvdMediaExIoReqCompleteWorker(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, int rcReq, bool fUpNotify)
2612{
2613 LogFlowFunc(("pThis=%#p pIoReq=%#p rcReq=%Rrc fUpNotify=%RTbool\n",
2614 pThis, pIoReq, rcReq, fUpNotify));
2615
2616 /*
2617 * For a read we need to sync the memory before continuing to process
2618 * the request further.
2619 */
2620 if ( RT_SUCCESS(rcReq)
2621 && pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ)
2622 rcReq = drvvdMediaExIoReqBufSync(pThis, pIoReq, false /* fToIoBuf */);
2623
2624 /*
2625 * When the request owner instructs us to handle recoverable errors like full disks
2626 * do it. Mark the request as suspended, notify the owner and put the request on the
2627 * redo list.
2628 */
2629 if ( RT_FAILURE(rcReq)
2630 && (pIoReq->fFlags & PDMIMEDIAEX_F_SUSPEND_ON_RECOVERABLE_ERR)
2631 && drvvdMediaExIoReqIsRedoSetWarning(pThis, rcReq))
2632 {
2633 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_SUSPENDED, VDIOREQSTATE_ACTIVE);
2634 if (fXchg)
2635 {
2636 /* Put on redo list and adjust active request counter. */
2637 RTCritSectEnter(&pThis->CritSectIoReqRedo);
2638 RTListAppend(&pThis->LstIoReqRedo, &pIoReq->NdLstWait);
2639 RTCritSectLeave(&pThis->CritSectIoReqRedo);
2640 uint32_t cNew = ASMAtomicDecU32(&pThis->cIoReqsActive);
2641 AssertMsg(cNew != UINT32_MAX, ("Number of active requests underflowed!\n")); RT_NOREF(cNew);
2642 pThis->pDrvMediaExPort->pfnIoReqStateChanged(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
2643 PDMMEDIAEXIOREQSTATE_SUSPENDED);
2644 LogFlowFunc(("Suspended I/O request %#p\n", pIoReq));
2645 rcReq = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
2646 }
2647 else
2648 {
2649 /* Request was canceled inbetween, so don't care and notify the owner about the completed request. */
2650 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
2651 drvvdMediaExIoReqRetire(pThis, pIoReq, rcReq, fUpNotify);
2652 }
2653 }
2654 else
2655 {
2656 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
2657 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
2658 {
2659 /* Adjust the remaining amount to transfer. */
2660 Assert(pIoReq->ReadWrite.cbIoBuf > 0 || rcReq == VERR_PDM_MEDIAEX_IOREQ_CANCELED);
2661
2662 size_t cbReqIo = RT_MIN(pIoReq->ReadWrite.cbReqLeft, pIoReq->ReadWrite.cbIoBuf);
2663 pIoReq->ReadWrite.offStart += cbReqIo;
2664 pIoReq->ReadWrite.cbReqLeft -= cbReqIo;
2665 }
2666
2667 if ( RT_FAILURE(rcReq)
2668 || !pIoReq->ReadWrite.cbReqLeft
2669 || ( pIoReq->enmType != PDMMEDIAEXIOREQTYPE_READ
2670 && pIoReq->enmType != PDMMEDIAEXIOREQTYPE_WRITE))
2671 drvvdMediaExIoReqRetire(pThis, pIoReq, rcReq, fUpNotify);
2672 else
2673 drvvdMediaExIoReqReadWriteProcess(pThis, pIoReq, fUpNotify);
2674 }
2675
2676 LogFlowFunc(("returns %Rrc\n", rcReq));
2677 return rcReq;
2678}
2679
2680
2681/**
2682 * Allocates a memory buffer suitable for I/O for the given request.
2683 *
2684 * @returns VBox status code.
2685 * @retval VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS if there is no I/O memory available to allocate and
2686 * the request was placed on a waiting list.
2687 * @param pThis VBox disk container instance data.
2688 * @param pIoReq I/O request to allocate memory for.
2689 * @param cb Size of the buffer.
2690 */
2691DECLINLINE(int) drvvdMediaExIoReqBufAlloc(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, size_t cb)
2692{
2693 int rc = VERR_NOT_SUPPORTED;
2694 LogFlowFunc(("pThis=%#p pIoReq=%#p cb=%zu\n", pThis, pIoReq, cb));
2695
2696/** @todo This does not work at all with encryption enabled because the encryption plugin
2697 * encrypts the data in place trashing guest memory and causing data corruption later on!
2698 *
2699 * DO NOT ENABLE UNLESS YOU WANT YOUR DATA SHREDDED!!!
2700 */
2701#if 0
2702 if ( cb == _4K
2703 && pThis->pDrvMediaExPort->pfnIoReqQueryBuf)
2704 {
2705 /* Try to get a direct pointer to the buffer first. */
2706 void *pvBuf = NULL;
2707 size_t cbBuf = 0;
2708
2709 STAM_COUNTER_INC(&pThis->StatQueryBufAttempts);
2710 rc = pThis->pDrvMediaExPort->pfnIoReqQueryBuf(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
2711 &pvBuf, &cbBuf);
2712 if (RT_SUCCESS(rc))
2713 {
2714 STAM_COUNTER_INC(&pThis->StatQueryBufSuccess);
2715 pIoReq->ReadWrite.cbIoBuf = cbBuf;
2716 pIoReq->ReadWrite.fDirectBuf = true;
2717 pIoReq->ReadWrite.Direct.Seg.pvSeg = pvBuf;
2718 pIoReq->ReadWrite.Direct.Seg.cbSeg = cbBuf;
2719 RTSgBufInit(&pIoReq->ReadWrite.Direct.SgBuf, &pIoReq->ReadWrite.Direct.Seg, 1);
2720 pIoReq->ReadWrite.pSgBuf = &pIoReq->ReadWrite.Direct.SgBuf;
2721 }
2722 }
2723#endif
2724
2725 if (RT_FAILURE(rc))
2726 {
2727 rc = IOBUFMgrAllocBuf(pThis->hIoBufMgr, &pIoReq->ReadWrite.IoBuf, cb, &pIoReq->ReadWrite.cbIoBuf);
2728 if (rc == VERR_NO_MEMORY)
2729 {
2730 LogFlowFunc(("Could not allocate memory for request, deferring\n"));
2731 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
2732 RTListAppend(&pThis->LstIoReqIoBufWait, &pIoReq->NdLstWait);
2733 ASMAtomicIncU32(&pThis->cIoReqsWaiting);
2734 if (ASMAtomicReadBool(&pThis->fSuspending))
2735 pThis->pDrvMediaExPort->pfnIoReqStateChanged(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
2736 PDMMEDIAEXIOREQSTATE_SUSPENDED);
2737 LogFlowFunc(("Suspended I/O request %#p\n", pIoReq));
2738 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
2739 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
2740 }
2741 else
2742 {
2743 LogFlowFunc(("Allocated %zu bytes of memory\n", pIoReq->ReadWrite.cbIoBuf));
2744 Assert(pIoReq->ReadWrite.cbIoBuf > 0);
2745 pIoReq->ReadWrite.fDirectBuf = false;
2746 pIoReq->ReadWrite.pSgBuf = &pIoReq->ReadWrite.IoBuf.SgBuf;
2747 }
2748 }
2749
2750 LogFlowFunc(("returns %Rrc\n", rc));
2751 return rc;
2752}
2753
2754/**
2755 * Wrapper around the various ways to read from the underlying medium (cache, async vs. sync).
2756 *
2757 * @returns VBox status code.
2758 * @param pThis VBox disk container instance data.
2759 * @param pIoReq I/O request to process.
2760 * @param cbReqIo Transfer size.
2761 * @param pcbReqIo Where to store the amount of transferred data.
2762 */
2763static int drvvdMediaExIoReqReadWrapper(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, size_t cbReqIo, size_t *pcbReqIo)
2764{
2765 int rc = VINF_SUCCESS;
2766
2767 LogFlowFunc(("pThis=%#p pIoReq=%#p cbReqIo=%zu pcbReqIo=%#p\n", pThis, pIoReq, cbReqIo, pcbReqIo));
2768
2769 Assert(cbReqIo > 0);
2770
2771 if ( pThis->fAsyncIOSupported
2772 && !(pIoReq->fFlags & PDMIMEDIAEX_F_SYNC))
2773 {
2774 if (pThis->pBlkCache)
2775 {
2776 rc = PDMDrvHlpBlkCacheRead(pThis->pDrvIns, pThis->pBlkCache, pIoReq->ReadWrite.offStart,
2777 pIoReq->ReadWrite.pSgBuf, cbReqIo, pIoReq);
2778 if (rc == VINF_SUCCESS)
2779 rc = VINF_VD_ASYNC_IO_FINISHED;
2780 else if (rc == VINF_AIO_TASK_PENDING)
2781 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2782 }
2783 else
2784 rc = VDAsyncRead(pThis->pDisk, pIoReq->ReadWrite.offStart, cbReqIo, pIoReq->ReadWrite.pSgBuf,
2785 drvvdMediaExIoReqComplete, pThis, pIoReq);
2786 }
2787 else
2788 {
2789 void *pvBuf = RTSgBufGetNextSegment(pIoReq->ReadWrite.pSgBuf, &cbReqIo);
2790
2791 Assert(cbReqIo > 0 && RT_VALID_PTR(pvBuf));
2792 rc = VDRead(pThis->pDisk, pIoReq->ReadWrite.offStart, pvBuf, cbReqIo);
2793 if (RT_SUCCESS(rc))
2794 rc = VINF_VD_ASYNC_IO_FINISHED;
2795 }
2796
2797 *pcbReqIo = cbReqIo;
2798
2799 LogFlowFunc(("returns %Rrc *pcbReqIo=%zu\n", rc, *pcbReqIo));
2800 return rc;
2801}
2802
2803/**
2804 * Wrapper around the various ways to write to the underlying medium (cache, async vs. sync).
2805 *
2806 * @returns VBox status code.
2807 * @param pThis VBox disk container instance data.
2808 * @param pIoReq I/O request to process.
2809 * @param cbReqIo Transfer size.
2810 * @param pcbReqIo Where to store the amount of transferred data.
2811 */
2812static int drvvdMediaExIoReqWriteWrapper(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, size_t cbReqIo, size_t *pcbReqIo)
2813{
2814 int rc = VINF_SUCCESS;
2815
2816 Assert(cbReqIo > 0);
2817
2818 LogFlowFunc(("pThis=%#p pIoReq=%#p cbReqIo=%zu pcbReqIo=%#p\n", pThis, pIoReq, cbReqIo, pcbReqIo));
2819
2820 if ( pThis->fAsyncIOSupported
2821 && !(pIoReq->fFlags & PDMIMEDIAEX_F_SYNC))
2822 {
2823 if (pThis->pBlkCache)
2824 {
2825 rc = PDMDrvHlpBlkCacheWrite(pThis->pDrvIns, pThis->pBlkCache, pIoReq->ReadWrite.offStart,
2826 pIoReq->ReadWrite.pSgBuf, cbReqIo, pIoReq);
2827 if (rc == VINF_SUCCESS)
2828 rc = VINF_VD_ASYNC_IO_FINISHED;
2829 else if (rc == VINF_AIO_TASK_PENDING)
2830 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2831 }
2832 else
2833 rc = VDAsyncWrite(pThis->pDisk, pIoReq->ReadWrite.offStart, cbReqIo, pIoReq->ReadWrite.pSgBuf,
2834 drvvdMediaExIoReqComplete, pThis, pIoReq);
2835 }
2836 else
2837 {
2838 void *pvBuf = RTSgBufGetNextSegment(pIoReq->ReadWrite.pSgBuf, &cbReqIo);
2839
2840 Assert(cbReqIo > 0 && RT_VALID_PTR(pvBuf));
2841 rc = VDWrite(pThis->pDisk, pIoReq->ReadWrite.offStart, pvBuf, cbReqIo);
2842 if (RT_SUCCESS(rc))
2843 rc = VINF_VD_ASYNC_IO_FINISHED;
2844
2845#ifdef VBOX_PERIODIC_FLUSH
2846 if (pThis->cbFlushInterval)
2847 {
2848 pThis->cbDataWritten += (uint32_t)cbReqIo;
2849 if (pThis->cbDataWritten > pThis->cbFlushInterval)
2850 {
2851 pThis->cbDataWritten = 0;
2852 VDFlush(pThis->pDisk);
2853 }
2854 }
2855#endif /* VBOX_PERIODIC_FLUSH */
2856 }
2857
2858 *pcbReqIo = cbReqIo;
2859
2860 LogFlowFunc(("returns %Rrc *pcbReqIo=%zu\n", rc, *pcbReqIo));
2861 return rc;
2862}
2863
2864/**
2865 * Wrapper around the various ways to flush all data to the underlying medium (cache, async vs. sync).
2866 *
2867 * @returns VBox status code.
2868 * @param pThis VBox disk container instance data.
2869 * @param pIoReq I/O request to process.
2870 */
2871static int drvvdMediaExIoReqFlushWrapper(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
2872{
2873 int rc = VINF_SUCCESS;
2874
2875 LogFlowFunc(("pThis=%#p pIoReq=%#p\n", pThis, pIoReq));
2876
2877 if ( pThis->fAsyncIOSupported
2878 && !(pIoReq->fFlags & PDMIMEDIAEX_F_SYNC))
2879 {
2880#ifdef VBOX_IGNORE_FLUSH
2881 if (pThis->fIgnoreFlushAsync)
2882 rc = VINF_VD_ASYNC_IO_FINISHED;
2883 else
2884#endif /* VBOX_IGNORE_FLUSH */
2885 {
2886 if (pThis->pBlkCache)
2887 {
2888 rc = PDMDrvHlpBlkCacheFlush(pThis->pDrvIns, pThis->pBlkCache, pIoReq);
2889 if (rc == VINF_SUCCESS)
2890 rc = VINF_VD_ASYNC_IO_FINISHED;
2891 else if (rc == VINF_AIO_TASK_PENDING)
2892 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2893 }
2894 else
2895 rc = VDAsyncFlush(pThis->pDisk, drvvdMediaExIoReqComplete, pThis, pIoReq);
2896 }
2897 }
2898 else
2899 {
2900#ifdef VBOX_IGNORE_FLUSH
2901 if (pThis->fIgnoreFlush)
2902 rc = VINF_VD_ASYNC_IO_FINISHED;
2903 else
2904#endif /* VBOX_IGNORE_FLUSH */
2905 {
2906 rc = VDFlush(pThis->pDisk);
2907 if (RT_SUCCESS(rc))
2908 rc = VINF_VD_ASYNC_IO_FINISHED;
2909 }
2910 }
2911
2912 LogFlowFunc(("returns %Rrc\n", rc));
2913 return rc;
2914}
2915
2916/**
2917 * Wrapper around the various ways to discard data blocks on the underlying medium (cache, async vs. sync).
2918 *
2919 * @returns VBox status code.
2920 * @param pThis VBox disk container instance data.
2921 * @param pIoReq I/O request to process.
2922 */
2923static int drvvdMediaExIoReqDiscardWrapper(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
2924{
2925 int rc = VINF_SUCCESS;
2926
2927 LogFlowFunc(("pThis=%#p pIoReq=%#p\n", pThis, pIoReq));
2928
2929 if ( pThis->fAsyncIOSupported
2930 && !(pIoReq->fFlags & PDMIMEDIAEX_F_SYNC))
2931 {
2932 if (pThis->pBlkCache)
2933 {
2934 rc = PDMDrvHlpBlkCacheDiscard(pThis->pDrvIns, pThis->pBlkCache,
2935 pIoReq->Discard.paRanges, pIoReq->Discard.cRanges,
2936 pIoReq);
2937 if (rc == VINF_SUCCESS)
2938 rc = VINF_VD_ASYNC_IO_FINISHED;
2939 else if (rc == VINF_AIO_TASK_PENDING)
2940 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2941 }
2942 else
2943 rc = VDAsyncDiscardRanges(pThis->pDisk, pIoReq->Discard.paRanges, pIoReq->Discard.cRanges,
2944 drvvdMediaExIoReqComplete, pThis, pIoReq);
2945 }
2946 else
2947 {
2948 rc = VDDiscardRanges(pThis->pDisk, pIoReq->Discard.paRanges, pIoReq->Discard.cRanges);
2949 if (RT_SUCCESS(rc))
2950 rc = VINF_VD_ASYNC_IO_FINISHED;
2951 }
2952
2953 LogFlowFunc(("returns %Rrc\n", rc));
2954 return rc;
2955}
2956
2957/**
2958 * Processes a read/write request.
2959 *
2960 * @returns VBox status code.
2961 * @param pThis VBox disk container instance data.
2962 * @param pIoReq I/O request to process.
2963 * @param fUpNotify Flag whether to notify the driver/device above us about the completion.
2964 */
2965static int drvvdMediaExIoReqReadWriteProcess(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, bool fUpNotify)
2966{
2967 int rc = VINF_SUCCESS;
2968
2969 LogFlowFunc(("pThis=%#p pIoReq=%#p fUpNotify=%RTbool\n", pThis, pIoReq, fUpNotify));
2970
2971 Assert(pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE);
2972
2973 rc = drvvdKeyCheckPrereqs(pThis, false /* fSetError */);
2974
2975 while ( pIoReq->ReadWrite.cbReqLeft
2976 && rc == VINF_SUCCESS)
2977 {
2978 Assert(pIoReq->ReadWrite.cbIoBuf > 0);
2979
2980 size_t cbReqIo = RT_MIN(pIoReq->ReadWrite.cbReqLeft, pIoReq->ReadWrite.cbIoBuf);
2981
2982 if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ)
2983 rc = drvvdMediaExIoReqReadWrapper(pThis, pIoReq, cbReqIo, &cbReqIo);
2984 else
2985 {
2986 /* Sync memory buffer from the request initiator. */
2987 rc = drvvdMediaExIoReqBufSync(pThis, pIoReq, true /* fToIoBuf */);
2988 if (RT_SUCCESS(rc))
2989 rc = drvvdMediaExIoReqWriteWrapper(pThis, pIoReq, cbReqIo, &cbReqIo);
2990 }
2991
2992 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2993 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
2994 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
2995 {
2996 /*
2997 * Don't sync the buffer or update the I/O state for the last chunk as it is done
2998 * already in the completion worker called below.
2999 */
3000 if (cbReqIo < pIoReq->ReadWrite.cbReqLeft)
3001 {
3002 if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ)
3003 rc = drvvdMediaExIoReqBufSync(pThis, pIoReq, false /* fToIoBuf */);
3004 else
3005 rc = VINF_SUCCESS;
3006 pIoReq->ReadWrite.offStart += cbReqIo;
3007 pIoReq->ReadWrite.cbReqLeft -= cbReqIo;
3008 }
3009 else
3010 {
3011 rc = VINF_SUCCESS;
3012 break;
3013 }
3014 }
3015 }
3016
3017 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
3018 rc = drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rc, fUpNotify);
3019
3020 LogFlowFunc(("returns %Rrc\n", rc));
3021 return rc;
3022}
3023
3024
3025/**
3026 * Tries to process any requests waiting for available I/O memory.
3027 *
3028 * @param pThis VBox disk container instance data.
3029 */
3030static void drvvdMediaExIoReqProcessWaiting(PVBOXDISK pThis)
3031{
3032 uint32_t cIoReqsWaiting = ASMAtomicXchgU32(&pThis->cIoReqsWaiting, 0);
3033 if (cIoReqsWaiting > 0)
3034 {
3035 RTLISTANCHOR LstIoReqProcess;
3036 RTLISTANCHOR LstIoReqCanceled;
3037 RTListInit(&LstIoReqProcess);
3038 RTListInit(&LstIoReqCanceled);
3039
3040 /* Try to process as many requests as possible. */
3041 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
3042 PPDMMEDIAEXIOREQINT pIoReqCur, pIoReqNext;
3043
3044 RTListForEachSafe(&pThis->LstIoReqIoBufWait, pIoReqCur, pIoReqNext, PDMMEDIAEXIOREQINT, NdLstWait)
3045 {
3046 LogFlowFunc(("Found I/O request %#p on waiting list, trying to allocate buffer of size %zu bytes\n",
3047 pIoReqCur, pIoReqCur->ReadWrite.cbReq));
3048
3049 /* Allocate a suitable I/O buffer for this request. */
3050 int rc = IOBUFMgrAllocBuf(pThis->hIoBufMgr, &pIoReqCur->ReadWrite.IoBuf, pIoReqCur->ReadWrite.cbReq,
3051 &pIoReqCur->ReadWrite.cbIoBuf);
3052 if (rc == VINF_SUCCESS)
3053 {
3054 Assert(pIoReqCur->ReadWrite.cbIoBuf > 0);
3055
3056 cIoReqsWaiting--;
3057 RTListNodeRemove(&pIoReqCur->NdLstWait);
3058
3059 pIoReqCur->ReadWrite.fDirectBuf = false;
3060 pIoReqCur->ReadWrite.pSgBuf = &pIoReqCur->ReadWrite.IoBuf.SgBuf;
3061
3062 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReqCur->enmState,
3063 VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
3064 if (RT_UNLIKELY(!fXchg))
3065 {
3066 /* Must have been canceled inbetween. */
3067 Assert(pIoReqCur->enmState == VDIOREQSTATE_CANCELED);
3068
3069 /* Free the buffer here already again to let other requests get a chance to allocate the memory. */
3070 IOBUFMgrFreeBuf(&pIoReqCur->ReadWrite.IoBuf);
3071 pIoReqCur->ReadWrite.cbIoBuf = 0;
3072 RTListAppend(&LstIoReqCanceled, &pIoReqCur->NdLstWait);
3073 }
3074 else
3075 {
3076 ASMAtomicIncU32(&pThis->cIoReqsActive);
3077 RTListAppend(&LstIoReqProcess, &pIoReqCur->NdLstWait);
3078 }
3079 }
3080 else
3081 {
3082 Assert(rc == VERR_NO_MEMORY);
3083 break;
3084 }
3085 }
3086 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
3087
3088 ASMAtomicAddU32(&pThis->cIoReqsWaiting, cIoReqsWaiting);
3089
3090 /* Process the requests we could allocate memory for and the ones which got canceled outside the lock now. */
3091 RTListForEachSafe(&LstIoReqCanceled, pIoReqCur, pIoReqNext, PDMMEDIAEXIOREQINT, NdLstWait)
3092 {
3093 RTListNodeRemove(&pIoReqCur->NdLstWait);
3094 drvvdMediaExIoReqCompleteWorker(pThis, pIoReqCur, VERR_PDM_MEDIAEX_IOREQ_CANCELED, true /* fUpNotify */);
3095 }
3096
3097 RTListForEachSafe(&LstIoReqProcess, pIoReqCur, pIoReqNext, PDMMEDIAEXIOREQINT, NdLstWait)
3098 {
3099 RTListNodeRemove(&pIoReqCur->NdLstWait);
3100 drvvdMediaExIoReqReadWriteProcess(pThis, pIoReqCur, true /* fUpNotify */);
3101 }
3102 }
3103}
3104
3105/**
3106 * Frees a I/O memory buffer allocated previously.
3107 *
3108 * @param pThis VBox disk container instance data.
3109 * @param pIoReq I/O request for which to free memory.
3110 */
3111DECLINLINE(void) drvvdMediaExIoReqBufFree(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
3112{
3113 LogFlowFunc(("pThis=%#p pIoReq=%#p{.cbIoBuf=%zu}\n", pThis, pIoReq, pIoReq->ReadWrite.cbIoBuf));
3114
3115 if ( ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
3116 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
3117 && !pIoReq->ReadWrite.fDirectBuf
3118 && pIoReq->ReadWrite.cbIoBuf > 0)
3119 {
3120 IOBUFMgrFreeBuf(&pIoReq->ReadWrite.IoBuf);
3121
3122 if (!ASMAtomicReadBool(&pThis->fSuspending))
3123 drvvdMediaExIoReqProcessWaiting(pThis);
3124 }
3125
3126 LogFlowFunc(("returns\n"));
3127}
3128
3129
3130/**
3131 * Returns a string description of the given request state.
3132 *
3133 * @returns Pointer to the stringified state.
3134 * @param enmState The state.
3135 */
3136DECLINLINE(const char *) drvvdMediaExIoReqStateStringify(VDIOREQSTATE enmState)
3137{
3138#define STATE2STR(a_State) case VDIOREQSTATE_##a_State: return #a_State
3139 switch (enmState)
3140 {
3141 STATE2STR(INVALID);
3142 STATE2STR(FREE);
3143 STATE2STR(ALLOCATED);
3144 STATE2STR(ACTIVE);
3145 STATE2STR(SUSPENDED);
3146 STATE2STR(COMPLETING);
3147 STATE2STR(COMPLETED);
3148 STATE2STR(CANCELED);
3149 default:
3150 AssertMsgFailed(("Unknown state %u\n", enmState));
3151 return "UNKNOWN";
3152 }
3153#undef STATE2STR
3154}
3155
3156
3157/**
3158 * Returns a string description of the given request type.
3159 *
3160 * @returns Pointer to the stringified type.
3161 * @param enmType The request type.
3162 */
3163DECLINLINE(const char *) drvvdMediaExIoReqTypeStringify(PDMMEDIAEXIOREQTYPE enmType)
3164{
3165#define TYPE2STR(a_Type) case PDMMEDIAEXIOREQTYPE_##a_Type: return #a_Type
3166 switch (enmType)
3167 {
3168 TYPE2STR(INVALID);
3169 TYPE2STR(FLUSH);
3170 TYPE2STR(WRITE);
3171 TYPE2STR(READ);
3172 TYPE2STR(DISCARD);
3173 TYPE2STR(SCSI);
3174 default:
3175 AssertMsgFailed(("Unknown type %u\n", enmType));
3176 return "UNKNOWN";
3177 }
3178#undef TYPE2STR
3179}
3180
3181
3182/**
3183 * Dumps the interesting bits about the given I/O request to the release log.
3184 *
3185 * @param pThis VBox disk container instance data.
3186 * @param pIoReq The I/O request to dump.
3187 */
3188static void drvvdMediaExIoReqLogRel(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
3189{
3190 uint64_t offStart = 0;
3191 size_t cbReq = 0;
3192 size_t cbLeft = 0;
3193 size_t cbBufSize = 0;
3194 uint64_t tsActive = RTTimeMilliTS() - pIoReq->tsSubmit;
3195
3196 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
3197 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
3198 {
3199 offStart = pIoReq->ReadWrite.offStart;
3200 cbReq = pIoReq->ReadWrite.cbReq;
3201 cbLeft = pIoReq->ReadWrite.cbReqLeft;
3202 cbBufSize = pIoReq->ReadWrite.cbIoBuf;
3203 }
3204
3205 LogRel(("VD#%u: Request{%#p}:\n"
3206 " Type=%s State=%s Id=%#llx SubmitTs=%llu {%llu} Flags=%#x\n"
3207 " Offset=%llu Size=%zu Left=%zu BufSize=%zu\n",
3208 pThis->pDrvIns->iInstance, pIoReq,
3209 drvvdMediaExIoReqTypeStringify(pIoReq->enmType),
3210 drvvdMediaExIoReqStateStringify(pIoReq->enmState),
3211 pIoReq->uIoReqId, pIoReq->tsSubmit, tsActive, pIoReq->fFlags,
3212 offStart, cbReq, cbLeft, cbBufSize));
3213}
3214
3215
3216/**
3217 * Returns whether the VM is in a running state.
3218 *
3219 * @returns Flag indicating whether the VM is currently in a running state.
3220 * @param pThis VBox disk container instance data.
3221 */
3222DECLINLINE(bool) drvvdMediaExIoReqIsVmRunning(PVBOXDISK pThis)
3223{
3224 VMSTATE enmVmState = PDMDrvHlpVMState(pThis->pDrvIns);
3225 if ( enmVmState == VMSTATE_RESUMING
3226 || enmVmState == VMSTATE_RUNNING
3227 || enmVmState == VMSTATE_RUNNING_LS
3228 || enmVmState == VMSTATE_RESETTING
3229 || enmVmState == VMSTATE_RESETTING_LS
3230 || enmVmState == VMSTATE_SOFT_RESETTING
3231 || enmVmState == VMSTATE_SOFT_RESETTING_LS
3232 || enmVmState == VMSTATE_SUSPENDING
3233 || enmVmState == VMSTATE_SUSPENDING_LS
3234 || enmVmState == VMSTATE_SUSPENDING_EXT_LS)
3235 return true;
3236
3237 return false;
3238}
3239
3240/**
3241 * @copydoc FNVDASYNCTRANSFERCOMPLETE
3242 */
3243static DECLCALLBACK(void) drvvdMediaExIoReqComplete(void *pvUser1, void *pvUser2, int rcReq)
3244{
3245 PVBOXDISK pThis = (PVBOXDISK)pvUser1;
3246 PPDMMEDIAEXIOREQINT pIoReq = (PPDMMEDIAEXIOREQINT)pvUser2;
3247
3248 drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rcReq, true /* fUpNotify */);
3249}
3250
3251/**
3252 * Tries to cancel the given I/O request returning the result.
3253 *
3254 * @returns Flag whether the request was successfully canceled or whether it
3255 * already complete inbetween.
3256 * @param pThis VBox disk container instance data.
3257 * @param pIoReq The I/O request to cancel.
3258 */
3259static bool drvvdMediaExIoReqCancel(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
3260{
3261 bool fXchg = false;
3262 VDIOREQSTATE enmStateOld = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3263
3264 drvvdMediaExIoReqLogRel(pThis, pIoReq);
3265
3266 /*
3267 * We might have to try canceling the request multiple times if it transitioned from
3268 * ALLOCATED to ACTIVE or to SUSPENDED between reading the state and trying to change it.
3269 */
3270 while ( ( enmStateOld == VDIOREQSTATE_ALLOCATED
3271 || enmStateOld == VDIOREQSTATE_ACTIVE
3272 || enmStateOld == VDIOREQSTATE_SUSPENDED)
3273 && !fXchg)
3274 {
3275 fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_CANCELED, enmStateOld);
3276 if (fXchg)
3277 break;
3278
3279 enmStateOld = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3280 }
3281
3282 if (fXchg && enmStateOld == VDIOREQSTATE_ACTIVE)
3283 {
3284 uint32_t cNew = ASMAtomicDecU32(&pThis->cIoReqsActive);
3285 AssertMsg(cNew != UINT32_MAX, ("Number of active requests underflowed!\n")); RT_NOREF(cNew);
3286 }
3287
3288 return fXchg;
3289}
3290
3291/**
3292 * @interface_method_impl{PDMIMEDIAEX,pfnQueryFeatures}
3293 */
3294static DECLCALLBACK(int) drvvdQueryFeatures(PPDMIMEDIAEX pInterface, uint32_t *pfFeatures)
3295{
3296 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3297
3298 AssertPtrReturn(pfFeatures, VERR_INVALID_POINTER);
3299
3300 uint32_t fFeatures = 0;
3301 if (pThis->fAsyncIOSupported)
3302 fFeatures |= PDMIMEDIAEX_FEATURE_F_ASYNC;
3303 if (pThis->IMedia.pfnDiscard)
3304 fFeatures |= PDMIMEDIAEX_FEATURE_F_DISCARD;
3305
3306 *pfFeatures = fFeatures;
3307
3308 return VINF_SUCCESS;
3309}
3310
3311
3312/**
3313 * @interface_method_impl{PDMIMEDIAEX,pfnNotifySuspend}
3314 */
3315static DECLCALLBACK(void) drvvdNotifySuspend(PPDMIMEDIAEX pInterface)
3316{
3317 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3318
3319 ASMAtomicXchgBool(&pThis->fSuspending, true);
3320
3321 /* Mark all waiting requests as suspended so they don't get accounted for. */
3322 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
3323 PPDMMEDIAEXIOREQINT pIoReqCur, pIoReqNext;
3324 RTListForEachSafe(&pThis->LstIoReqIoBufWait, pIoReqCur, pIoReqNext, PDMMEDIAEXIOREQINT, NdLstWait)
3325 {
3326 pThis->pDrvMediaExPort->pfnIoReqStateChanged(pThis->pDrvMediaExPort, pIoReqCur, &pIoReqCur->abAlloc[0],
3327 PDMMEDIAEXIOREQSTATE_SUSPENDED);
3328 LogFlowFunc(("Suspended I/O request %#p\n", pIoReqCur));
3329 }
3330 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
3331}
3332
3333
3334/**
3335 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqAllocSizeSet}
3336 */
3337static DECLCALLBACK(int) drvvdIoReqAllocSizeSet(PPDMIMEDIAEX pInterface, size_t cbIoReqAlloc)
3338{
3339 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3340 if (RT_UNLIKELY(pThis->hIoReqCache != NIL_RTMEMCACHE))
3341 return VERR_INVALID_STATE;
3342
3343 return RTMemCacheCreate(&pThis->hIoReqCache, sizeof(PDMMEDIAEXIOREQINT) + cbIoReqAlloc, 0, UINT32_MAX,
3344 NULL, NULL, NULL, 0);
3345}
3346
3347/**
3348 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqAlloc}
3349 */
3350static DECLCALLBACK(int) drvvdIoReqAlloc(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq, void **ppvIoReqAlloc,
3351 PDMMEDIAEXIOREQID uIoReqId, uint32_t fFlags)
3352{
3353 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3354
3355 AssertReturn(!(fFlags & ~PDMIMEDIAEX_F_VALID), VERR_INVALID_PARAMETER);
3356
3357 PPDMMEDIAEXIOREQINT pIoReq = (PPDMMEDIAEXIOREQINT)RTMemCacheAlloc(pThis->hIoReqCache);
3358
3359 if (RT_UNLIKELY(!pIoReq))
3360 return VERR_NO_MEMORY;
3361
3362 pIoReq->uIoReqId = uIoReqId;
3363 pIoReq->fFlags = fFlags;
3364 pIoReq->pDisk = pThis;
3365 pIoReq->enmState = VDIOREQSTATE_ALLOCATED;
3366 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_INVALID;
3367
3368 int rc = drvvdMediaExIoReqInsert(pThis, pIoReq);
3369 if (RT_SUCCESS(rc))
3370 {
3371 *phIoReq = pIoReq;
3372 *ppvIoReqAlloc = &pIoReq->abAlloc[0];
3373 }
3374 else
3375 RTMemCacheFree(pThis->hIoReqCache, pIoReq);
3376
3377 return rc;
3378}
3379
3380/**
3381 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqFree}
3382 */
3383static DECLCALLBACK(int) drvvdIoReqFree(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
3384{
3385 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3386 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3387
3388 if ( pIoReq->enmState != VDIOREQSTATE_COMPLETED
3389 && pIoReq->enmState != VDIOREQSTATE_ALLOCATED)
3390 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3391
3392 /* Remove from allocated list. */
3393 int rc = drvvdMediaExIoReqRemove(pThis, pIoReq);
3394 if (RT_FAILURE(rc))
3395 return rc;
3396
3397 /* Free any associated I/O memory. */
3398 drvvdMediaExIoReqBufFree(pThis, pIoReq);
3399
3400 /* For discard request discard the range array. */
3401 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD
3402 && pIoReq->Discard.paRanges)
3403 {
3404 RTMemFree(pIoReq->Discard.paRanges);
3405 pIoReq->Discard.paRanges = NULL;
3406 }
3407
3408 pIoReq->enmState = VDIOREQSTATE_FREE;
3409 RTMemCacheFree(pThis->hIoReqCache, pIoReq);
3410 return VINF_SUCCESS;
3411}
3412
3413/**
3414 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQueryResidual}
3415 */
3416static DECLCALLBACK(int) drvvdIoReqQueryResidual(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, size_t *pcbResidual)
3417{
3418 RT_NOREF1(pInterface);
3419
3420 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3421
3422 if (pIoReq->enmState != VDIOREQSTATE_COMPLETED)
3423 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3424
3425 if ( pIoReq->enmType != PDMMEDIAEXIOREQTYPE_READ
3426 && pIoReq->enmType != PDMMEDIAEXIOREQTYPE_WRITE
3427 && pIoReq->enmType != PDMMEDIAEXIOREQTYPE_FLUSH)
3428 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3429
3430 *pcbResidual = 0; /* No data left to transfer always. */
3431 return VINF_SUCCESS;
3432}
3433
3434/**
3435 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQueryXferSize}
3436 */
3437static DECLCALLBACK(int) drvvdIoReqQueryXferSize(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, size_t *pcbXfer)
3438{
3439 int rc = VINF_SUCCESS;
3440 RT_NOREF1(pInterface);
3441
3442 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3443
3444 if (pIoReq->enmState != VDIOREQSTATE_COMPLETED)
3445 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3446
3447 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
3448 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
3449 *pcbXfer = pIoReq->ReadWrite.cbReq;
3450 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_FLUSH)
3451 *pcbXfer = 0;
3452 else
3453 rc = VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3454
3455 return rc;
3456}
3457
3458/**
3459 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancelAll}
3460 */
3461static DECLCALLBACK(int) drvvdIoReqCancelAll(PPDMIMEDIAEX pInterface)
3462{
3463 int rc = VINF_SUCCESS;
3464 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3465
3466 LogRel(("VD#%u: Cancelling all active requests\n", pThis->pDrvIns->iInstance));
3467
3468 for (unsigned idxBin = 0; idxBin < RT_ELEMENTS(pThis->aIoReqAllocBins); idxBin++)
3469 {
3470 rc = RTSemFastMutexRequest(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
3471 if (RT_SUCCESS(rc))
3472 {
3473 /* Search for I/O request with ID. */
3474 PPDMMEDIAEXIOREQINT pIt;
3475
3476 RTListForEach(&pThis->aIoReqAllocBins[idxBin].LstIoReqAlloc, pIt, PDMMEDIAEXIOREQINT, NdAllocatedList)
3477 {
3478 drvvdMediaExIoReqCancel(pThis, pIt);
3479 }
3480 RTSemFastMutexRelease(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
3481 }
3482 }
3483
3484 return rc;
3485}
3486
3487/**
3488 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancel}
3489 */
3490static DECLCALLBACK(int) drvvdIoReqCancel(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQID uIoReqId)
3491{
3492 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3493 unsigned idxBin = drvvdMediaExIoReqIdHash(uIoReqId);
3494
3495 LogRel(("VD#%u: Trying to cancel request %#llx\n", pThis->pDrvIns->iInstance, uIoReqId));
3496
3497 int rc = RTSemFastMutexRequest(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
3498 if (RT_SUCCESS(rc))
3499 {
3500 /* Search for I/O request with ID. */
3501 PPDMMEDIAEXIOREQINT pIt;
3502 rc = VERR_PDM_MEDIAEX_IOREQID_NOT_FOUND;
3503
3504 RTListForEach(&pThis->aIoReqAllocBins[idxBin].LstIoReqAlloc, pIt, PDMMEDIAEXIOREQINT, NdAllocatedList)
3505 {
3506 if (pIt->uIoReqId == uIoReqId)
3507 {
3508 if (drvvdMediaExIoReqCancel(pThis, pIt))
3509 rc = VINF_SUCCESS;
3510
3511 break;
3512 }
3513 }
3514 RTSemFastMutexRelease(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
3515 }
3516
3517 return rc;
3518}
3519
3520/**
3521 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqRead}
3522 */
3523static DECLCALLBACK(int) drvvdIoReqRead(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbRead)
3524{
3525 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3526 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3527 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3528
3529 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
3530 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3531
3532 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
3533 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3534
3535 STAM_REL_COUNTER_INC(&pThis->StatReqsSubmitted);
3536 STAM_REL_COUNTER_INC(&pThis->StatReqsRead);
3537
3538 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_READ;
3539 pIoReq->tsSubmit = RTTimeMilliTS();
3540 pIoReq->ReadWrite.offStart = off;
3541 pIoReq->ReadWrite.cbReq = cbRead;
3542 pIoReq->ReadWrite.cbReqLeft = cbRead;
3543 /* Allocate a suitable I/O buffer for this request. */
3544 int rc = drvvdMediaExIoReqBufAlloc(pThis, pIoReq, cbRead);
3545 if (rc == VINF_SUCCESS)
3546 {
3547 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
3548 if (RT_UNLIKELY(!fXchg))
3549 {
3550 /* Must have been canceled inbetween. */
3551 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
3552 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3553 }
3554 ASMAtomicIncU32(&pThis->cIoReqsActive);
3555
3556 rc = drvvdMediaExIoReqReadWriteProcess(pThis, pIoReq, false /* fUpNotify */);
3557 }
3558
3559 return rc;
3560}
3561
3562/**
3563 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqWrite}
3564 */
3565static DECLCALLBACK(int) drvvdIoReqWrite(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbWrite)
3566{
3567 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3568 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3569 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3570
3571 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
3572 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3573
3574 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
3575 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3576
3577 STAM_REL_COUNTER_INC(&pThis->StatReqsSubmitted);
3578 STAM_REL_COUNTER_INC(&pThis->StatReqsWrite);
3579
3580 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_WRITE;
3581 pIoReq->tsSubmit = RTTimeMilliTS();
3582 pIoReq->ReadWrite.offStart = off;
3583 pIoReq->ReadWrite.cbReq = cbWrite;
3584 pIoReq->ReadWrite.cbReqLeft = cbWrite;
3585 /* Allocate a suitable I/O buffer for this request. */
3586 int rc = drvvdMediaExIoReqBufAlloc(pThis, pIoReq, cbWrite);
3587 if (rc == VINF_SUCCESS)
3588 {
3589 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
3590 if (RT_UNLIKELY(!fXchg))
3591 {
3592 /* Must have been canceled inbetween. */
3593 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
3594 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3595 }
3596 ASMAtomicIncU32(&pThis->cIoReqsActive);
3597
3598 rc = drvvdMediaExIoReqReadWriteProcess(pThis, pIoReq, false /* fUpNotify */);
3599 }
3600
3601 return rc;
3602}
3603
3604/**
3605 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqFlush}
3606 */
3607static DECLCALLBACK(int) drvvdIoReqFlush(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
3608{
3609 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3610 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3611 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3612
3613 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
3614 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3615
3616 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
3617 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3618
3619 STAM_REL_COUNTER_INC(&pThis->StatReqsSubmitted);
3620 STAM_REL_COUNTER_INC(&pThis->StatReqsFlush);
3621
3622 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_FLUSH;
3623 pIoReq->tsSubmit = RTTimeMilliTS();
3624 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
3625 if (RT_UNLIKELY(!fXchg))
3626 {
3627 /* Must have been canceled inbetween. */
3628 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
3629 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3630 }
3631
3632 ASMAtomicIncU32(&pThis->cIoReqsActive);
3633 int rc = drvvdMediaExIoReqFlushWrapper(pThis, pIoReq);
3634 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3635 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
3636 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
3637 rc = VINF_SUCCESS;
3638
3639 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
3640 rc = drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rc, false /* fUpNotify */);
3641
3642 return rc;
3643}
3644
3645/**
3646 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqDiscard}
3647 */
3648static DECLCALLBACK(int) drvvdIoReqDiscard(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, unsigned cRangesMax)
3649{
3650 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3651 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3652 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3653
3654 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
3655 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3656
3657 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
3658 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3659
3660 STAM_REL_COUNTER_INC(&pThis->StatReqsSubmitted);
3661 STAM_REL_COUNTER_INC(&pThis->StatReqsDiscard);
3662
3663 /* Copy the ranges over now, this can be optimized in the future. */
3664 pIoReq->Discard.paRanges = (PRTRANGE)RTMemAllocZ(cRangesMax * sizeof(RTRANGE));
3665 if (RT_UNLIKELY(!pIoReq->Discard.paRanges))
3666 return VERR_NO_MEMORY;
3667
3668 int rc = pThis->pDrvMediaExPort->pfnIoReqQueryDiscardRanges(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
3669 0, cRangesMax, pIoReq->Discard.paRanges,
3670 &pIoReq->Discard.cRanges);
3671 if (RT_SUCCESS(rc))
3672 {
3673 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_DISCARD;
3674 pIoReq->tsSubmit = RTTimeMilliTS();
3675 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
3676 if (RT_UNLIKELY(!fXchg))
3677 {
3678 /* Must have been canceled inbetween. */
3679 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
3680 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3681 }
3682
3683 ASMAtomicIncU32(&pThis->cIoReqsActive);
3684 rc = drvvdMediaExIoReqDiscardWrapper(pThis, pIoReq);
3685 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3686 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
3687 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
3688 rc = VINF_SUCCESS;
3689
3690 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
3691 rc = drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rc, false /* fUpNotify */);
3692 }
3693
3694 return rc;
3695}
3696
3697/**
3698 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqSendScsiCmd}
3699 */
3700static DECLCALLBACK(int) drvvdIoReqSendScsiCmd(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq,
3701 uint32_t uLun, const uint8_t *pbCdb, size_t cbCdb,
3702 PDMMEDIAEXIOREQSCSITXDIR enmTxDir, PDMMEDIAEXIOREQSCSITXDIR *penmTxDirRet,
3703 size_t cbBuf, uint8_t *pabSense, size_t cbSense, size_t *pcbSenseRet,
3704 uint8_t *pu8ScsiSts, uint32_t cTimeoutMillies)
3705{
3706 RT_NOREF12(pInterface, uLun, pbCdb, cbCdb, enmTxDir, penmTxDirRet, cbBuf, pabSense, cbSense, pcbSenseRet, pu8ScsiSts, cTimeoutMillies);
3707 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3708 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3709
3710 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
3711 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3712
3713 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
3714 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3715
3716 return VERR_NOT_SUPPORTED;
3717}
3718
3719/**
3720 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetActiveCount}
3721 */
3722static DECLCALLBACK(uint32_t) drvvdIoReqGetActiveCount(PPDMIMEDIAEX pInterface)
3723{
3724 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3725 return ASMAtomicReadU32(&pThis->cIoReqsActive);
3726}
3727
3728/**
3729 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetSuspendedCount}
3730 */
3731static DECLCALLBACK(uint32_t) drvvdIoReqGetSuspendedCount(PPDMIMEDIAEX pInterface)
3732{
3733 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3734
3735 AssertReturn(!drvvdMediaExIoReqIsVmRunning(pThis), 0);
3736
3737 uint32_t cIoReqSuspended = 0;
3738 PPDMMEDIAEXIOREQINT pIoReq;
3739 RTCritSectEnter(&pThis->CritSectIoReqRedo);
3740 RTListForEach(&pThis->LstIoReqRedo, pIoReq, PDMMEDIAEXIOREQINT, NdLstWait)
3741 {
3742 cIoReqSuspended++;
3743 }
3744 RTCritSectLeave(&pThis->CritSectIoReqRedo);
3745
3746 return cIoReqSuspended + pThis->cIoReqsWaiting;
3747}
3748
3749/**
3750 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedStart}
3751 */
3752static DECLCALLBACK(int) drvvdIoReqQuerySuspendedStart(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq,
3753 void **ppvIoReqAlloc)
3754{
3755 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3756
3757 AssertReturn(!drvvdMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
3758 AssertReturn(!( RTListIsEmpty(&pThis->LstIoReqRedo)
3759 && RTListIsEmpty(&pThis->LstIoReqIoBufWait)), VERR_NOT_FOUND);
3760
3761 PRTLISTANCHOR pLst;
3762 PRTCRITSECT pCritSect;
3763 if (!RTListIsEmpty(&pThis->LstIoReqRedo))
3764 {
3765 pLst = &pThis->LstIoReqRedo;
3766 pCritSect = &pThis->CritSectIoReqRedo;
3767 }
3768 else
3769 {
3770 pLst = &pThis->LstIoReqIoBufWait;
3771 pCritSect = &pThis->CritSectIoReqsIoBufWait;
3772 }
3773
3774 RTCritSectEnter(pCritSect);
3775 PPDMMEDIAEXIOREQINT pIoReq = RTListGetFirst(pLst, PDMMEDIAEXIOREQINT, NdLstWait);
3776 *phIoReq = pIoReq;
3777 *ppvIoReqAlloc = &pIoReq->abAlloc[0];
3778 RTCritSectLeave(pCritSect);
3779
3780 return VINF_SUCCESS;
3781}
3782
3783/**
3784 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedNext}
3785 */
3786static DECLCALLBACK(int) drvvdIoReqQuerySuspendedNext(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq,
3787 PPDMMEDIAEXIOREQ phIoReqNext, void **ppvIoReqAllocNext)
3788{
3789 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3790 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3791
3792 AssertReturn(!drvvdMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
3793 AssertPtrReturn(pIoReq, VERR_INVALID_HANDLE);
3794 AssertReturn( ( pIoReq->enmState == VDIOREQSTATE_SUSPENDED
3795 && ( !RTListNodeIsLast(&pThis->LstIoReqRedo, &pIoReq->NdLstWait)
3796 || !RTListIsEmpty(&pThis->LstIoReqIoBufWait)))
3797 || ( pIoReq->enmState == VDIOREQSTATE_ALLOCATED
3798 && !RTListNodeIsLast(&pThis->LstIoReqIoBufWait, &pIoReq->NdLstWait)), VERR_NOT_FOUND);
3799
3800 PPDMMEDIAEXIOREQINT pIoReqNext;
3801 if (pIoReq->enmState == VDIOREQSTATE_SUSPENDED)
3802 {
3803 if (!RTListNodeIsLast(&pThis->LstIoReqRedo, &pIoReq->NdLstWait))
3804 {
3805 RTCritSectEnter(&pThis->CritSectIoReqRedo);
3806 pIoReqNext = RTListNodeGetNext(&pIoReq->NdLstWait, PDMMEDIAEXIOREQINT, NdLstWait);
3807 RTCritSectLeave(&pThis->CritSectIoReqRedo);
3808 }
3809 else
3810 {
3811 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
3812 pIoReqNext = RTListGetFirst(&pThis->LstIoReqIoBufWait, PDMMEDIAEXIOREQINT, NdLstWait);
3813 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
3814 }
3815 }
3816 else
3817 {
3818 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
3819 pIoReqNext = RTListNodeGetNext(&pIoReq->NdLstWait, PDMMEDIAEXIOREQINT, NdLstWait);
3820 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
3821 }
3822
3823 *phIoReqNext = pIoReqNext;
3824 *ppvIoReqAllocNext = &pIoReqNext->abAlloc[0];
3825
3826 return VINF_SUCCESS;
3827}
3828
3829/**
3830 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedSave}
3831 */
3832static DECLCALLBACK(int) drvvdIoReqSuspendedSave(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
3833{
3834 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3835 PCPDMDRVHLPR3 pHlp = pThis->pDrvIns->pHlpR3;
3836 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3837
3838 AssertReturn(!drvvdMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
3839 AssertPtrReturn(pIoReq, VERR_INVALID_HANDLE);
3840 AssertReturn( pIoReq->enmState == VDIOREQSTATE_SUSPENDED
3841 || pIoReq->enmState == VDIOREQSTATE_ALLOCATED, VERR_INVALID_STATE);
3842
3843 pHlp->pfnSSMPutU32(pSSM, DRVVD_IOREQ_SAVED_STATE_VERSION);
3844 pHlp->pfnSSMPutU32(pSSM, (uint32_t)pIoReq->enmType);
3845 pHlp->pfnSSMPutU32(pSSM, pIoReq->uIoReqId);
3846 pHlp->pfnSSMPutU32(pSSM, pIoReq->fFlags);
3847 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
3848 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
3849 {
3850 pHlp->pfnSSMPutU64(pSSM, pIoReq->ReadWrite.offStart);
3851 pHlp->pfnSSMPutU64(pSSM, pIoReq->ReadWrite.cbReq);
3852 pHlp->pfnSSMPutU64(pSSM, pIoReq->ReadWrite.cbReqLeft);
3853 }
3854 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD)
3855 {
3856 pHlp->pfnSSMPutU32(pSSM, pIoReq->Discard.cRanges);
3857 for (unsigned i = 0; i < pIoReq->Discard.cRanges; i++)
3858 {
3859 pHlp->pfnSSMPutU64(pSSM, pIoReq->Discard.paRanges[i].offStart);
3860 pHlp->pfnSSMPutU64(pSSM, pIoReq->Discard.paRanges[i].cbRange);
3861 }
3862 }
3863
3864 return pHlp->pfnSSMPutU32(pSSM, UINT32_MAX); /* sanity/terminator */
3865}
3866
3867/**
3868 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedLoad}
3869 */
3870static DECLCALLBACK(int) drvvdIoReqSuspendedLoad(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
3871{
3872 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3873 PCPDMDRVHLPR3 pHlp = pThis->pDrvIns->pHlpR3;
3874 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3875
3876 AssertReturn(!drvvdMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
3877 AssertPtrReturn(pIoReq, VERR_INVALID_HANDLE);
3878 AssertReturn(pIoReq->enmState == VDIOREQSTATE_ALLOCATED, VERR_INVALID_STATE);
3879
3880 uint32_t u32;
3881 uint64_t u64;
3882 int rc = VINF_SUCCESS;
3883 bool fPlaceOnRedoList = true;
3884
3885 pHlp->pfnSSMGetU32(pSSM, &u32);
3886 if (u32 <= DRVVD_IOREQ_SAVED_STATE_VERSION)
3887 {
3888 pHlp->pfnSSMGetU32(pSSM, &u32);
3889 AssertReturn( u32 == PDMMEDIAEXIOREQTYPE_WRITE
3890 || u32 == PDMMEDIAEXIOREQTYPE_READ
3891 || u32 == PDMMEDIAEXIOREQTYPE_DISCARD
3892 || u32 == PDMMEDIAEXIOREQTYPE_FLUSH,
3893 VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
3894 pIoReq->enmType = (PDMMEDIAEXIOREQTYPE)u32;
3895
3896 pHlp->pfnSSMGetU32(pSSM, &u32);
3897 AssertReturn(u32 == pIoReq->uIoReqId, VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
3898
3899 pHlp->pfnSSMGetU32(pSSM, &u32);
3900 AssertReturn(u32 == pIoReq->fFlags, VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
3901
3902 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
3903 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
3904 {
3905 pHlp->pfnSSMGetU64(pSSM, &pIoReq->ReadWrite.offStart);
3906 pHlp->pfnSSMGetU64(pSSM, &u64);
3907 pIoReq->ReadWrite.cbReq = (size_t)u64;
3908 pHlp->pfnSSMGetU64(pSSM, &u64);
3909 pIoReq->ReadWrite.cbReqLeft = (size_t)u64;
3910
3911 /*
3912 * Try to allocate enough I/O buffer, if this fails for some reason put it onto the
3913 * waiting list instead of the redo list.
3914 */
3915 pIoReq->ReadWrite.cbIoBuf = 0;
3916 rc = IOBUFMgrAllocBuf(pThis->hIoBufMgr, &pIoReq->ReadWrite.IoBuf, pIoReq->ReadWrite.cbReqLeft,
3917 &pIoReq->ReadWrite.cbIoBuf);
3918 if (rc == VERR_NO_MEMORY)
3919 {
3920 pIoReq->enmState = VDIOREQSTATE_ALLOCATED;
3921 ASMAtomicIncU32(&pThis->cIoReqsWaiting);
3922 RTListAppend(&pThis->LstIoReqIoBufWait, &pIoReq->NdLstWait);
3923 fPlaceOnRedoList = false;
3924 rc = VINF_SUCCESS;
3925 }
3926 else
3927 {
3928 pIoReq->ReadWrite.fDirectBuf = false;
3929 pIoReq->ReadWrite.pSgBuf = &pIoReq->ReadWrite.IoBuf.SgBuf;
3930 }
3931 }
3932 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD)
3933 {
3934 rc = pHlp->pfnSSMGetU32(pSSM, &pIoReq->Discard.cRanges);
3935 if (RT_SUCCESS(rc))
3936 {
3937 pIoReq->Discard.paRanges = (PRTRANGE)RTMemAllocZ(pIoReq->Discard.cRanges * sizeof(RTRANGE));
3938 if (RT_LIKELY(pIoReq->Discard.paRanges))
3939 {
3940 for (unsigned i = 0; i < pIoReq->Discard.cRanges; i++)
3941 {
3942 pHlp->pfnSSMGetU64(pSSM, &pIoReq->Discard.paRanges[i].offStart);
3943 pHlp->pfnSSMGetU64(pSSM, &u64);
3944 pIoReq->Discard.paRanges[i].cbRange = (size_t)u64;
3945 }
3946 }
3947 else
3948 rc = VERR_NO_MEMORY;
3949 }
3950 }
3951
3952 if (RT_SUCCESS(rc))
3953 rc = pHlp->pfnSSMGetU32(pSSM, &u32); /* sanity/terminator */
3954 if (RT_SUCCESS(rc))
3955 AssertReturn(u32 == UINT32_MAX, VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
3956 if ( RT_SUCCESS(rc)
3957 && fPlaceOnRedoList)
3958 {
3959 /* Mark as suspended */
3960 pIoReq->enmState = VDIOREQSTATE_SUSPENDED;
3961
3962 /* Link into suspended list so it gets kicked off again when we resume. */
3963 RTCritSectEnter(&pThis->CritSectIoReqRedo);
3964 RTListAppend(&pThis->LstIoReqRedo, &pIoReq->NdLstWait);
3965 RTCritSectLeave(&pThis->CritSectIoReqRedo);
3966 }
3967 }
3968
3969 return rc;
3970}
3971
3972/**
3973 * Loads all configured plugins.
3974 *
3975 * @returns VBox status code.
3976 * @param pDrvIns Driver instance data.
3977 * @param pCfg CFGM node holding plugin list.
3978 */
3979static int drvvdLoadPlugins(PPDMDRVINS pDrvIns, PCFGMNODE pCfg)
3980{
3981 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
3982
3983 PCFGMNODE pCfgPlugins = pHlp->pfnCFGMGetChild(pCfg, "Plugins");
3984
3985 if (pCfgPlugins)
3986 {
3987 PCFGMNODE pPluginCur = pHlp->pfnCFGMGetFirstChild(pCfgPlugins);
3988 while (pPluginCur)
3989 {
3990 int rc = VINF_SUCCESS;
3991 char *pszPluginFilename = NULL;
3992 rc = pHlp->pfnCFGMQueryStringAlloc(pPluginCur, "Path", &pszPluginFilename);
3993 if (RT_SUCCESS(rc))
3994 rc = VDPluginLoadFromFilename(pszPluginFilename);
3995
3996 if (RT_FAILURE(rc))
3997 LogRel(("VD: Failed to load plugin '%s' with %Rrc, continuing\n", pszPluginFilename, rc));
3998
3999 pPluginCur = pHlp->pfnCFGMGetNextChild(pPluginCur);
4000 }
4001 }
4002
4003 return VINF_SUCCESS;
4004}
4005
4006
4007/**
4008 * Sets up the disk filter chain.
4009 *
4010 * @returns VBox status code.
4011 * @param pThis The disk instance.
4012 * @param pCfg CFGM node holding the filter parameters.
4013 */
4014static int drvvdSetupFilters(PVBOXDISK pThis, PCFGMNODE pCfg)
4015{
4016 PCPDMDRVHLPR3 pHlp = pThis->pDrvIns->pHlpR3;
4017 int rc = VINF_SUCCESS;
4018
4019 PCFGMNODE pCfgFilter = pHlp->pfnCFGMGetChild(pCfg, "Filters");
4020 if (pCfgFilter)
4021 {
4022 PCFGMNODE pCfgFilterConfig = pHlp->pfnCFGMGetChild(pCfgFilter, "VDConfig");
4023 char *pszFilterName = NULL;
4024 VDINTERFACECONFIG VDIfConfig;
4025 PVDINTERFACE pVDIfsFilter = NULL;
4026
4027 rc = pHlp->pfnCFGMQueryStringAlloc(pCfgFilter, "FilterName", &pszFilterName);
4028 if (RT_SUCCESS(rc))
4029 {
4030 VDCFGNODE CfgNode;
4031
4032 VDIfConfig.pfnAreKeysValid = drvvdCfgAreKeysValid;
4033 VDIfConfig.pfnQuerySize = drvvdCfgQuerySize;
4034 VDIfConfig.pfnQuery = drvvdCfgQuery;
4035 VDIfConfig.pfnQueryBytes = drvvdCfgQueryBytes;
4036
4037 CfgNode.pHlp = pThis->pDrvIns->pHlpR3;
4038 CfgNode.pCfgNode = pCfgFilterConfig;
4039 rc = VDInterfaceAdd(&VDIfConfig.Core, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
4040 &CfgNode, sizeof(VDINTERFACECONFIG), &pVDIfsFilter);
4041 AssertRC(rc);
4042
4043 rc = VDFilterAdd(pThis->pDisk, pszFilterName, VD_FILTER_FLAGS_DEFAULT, pVDIfsFilter);
4044
4045 PDMDrvHlpMMHeapFree(pThis->pDrvIns, pszFilterName);
4046 }
4047 }
4048
4049 return rc;
4050}
4051
4052
4053/**
4054 * Translates a PDMMEDIATYPE value into a string.
4055 *
4056 * @returns Read only string.
4057 * @param enmType The type value.
4058 */
4059static const char *drvvdGetTypeName(PDMMEDIATYPE enmType)
4060{
4061 switch (enmType)
4062 {
4063 case PDMMEDIATYPE_ERROR: return "ERROR";
4064 case PDMMEDIATYPE_FLOPPY_360: return "FLOPPY_360";
4065 case PDMMEDIATYPE_FLOPPY_720: return "FLOPPY_720";
4066 case PDMMEDIATYPE_FLOPPY_1_20: return "FLOPPY_1_20";
4067 case PDMMEDIATYPE_FLOPPY_1_44: return "FLOPPY_1_44";
4068 case PDMMEDIATYPE_FLOPPY_2_88: return "FLOPPY_2_88";
4069 case PDMMEDIATYPE_FLOPPY_FAKE_15_6: return "FLOPPY_FAKE_15_6";
4070 case PDMMEDIATYPE_FLOPPY_FAKE_63_5: return "FLOPPY_FAKE_63_5";
4071 case PDMMEDIATYPE_CDROM: return "CDROM";
4072 case PDMMEDIATYPE_DVD: return "DVD";
4073 case PDMMEDIATYPE_HARD_DISK: return "HARD_DISK";
4074 default: return "Unknown";
4075 }
4076}
4077
4078/**
4079 * Returns the appropriate PDMMEDIATYPE for t he given string.
4080 *
4081 * @returns PDMMEDIATYPE
4082 * @param pszType The string representation of the media type.
4083 */
4084static PDMMEDIATYPE drvvdGetMediaTypeFromString(const char *pszType)
4085{
4086 PDMMEDIATYPE enmType = PDMMEDIATYPE_ERROR;
4087
4088 if (!strcmp(pszType, "HardDisk"))
4089 enmType = PDMMEDIATYPE_HARD_DISK;
4090 else if (!strcmp(pszType, "DVD"))
4091 enmType = PDMMEDIATYPE_DVD;
4092 else if (!strcmp(pszType, "CDROM"))
4093 enmType = PDMMEDIATYPE_CDROM;
4094 else if (!strcmp(pszType, "Floppy 2.88"))
4095 enmType = PDMMEDIATYPE_FLOPPY_2_88;
4096 else if (!strcmp(pszType, "Floppy 1.44"))
4097 enmType = PDMMEDIATYPE_FLOPPY_1_44;
4098 else if (!strcmp(pszType, "Floppy 1.20"))
4099 enmType = PDMMEDIATYPE_FLOPPY_1_20;
4100 else if (!strcmp(pszType, "Floppy 720"))
4101 enmType = PDMMEDIATYPE_FLOPPY_720;
4102 else if (!strcmp(pszType, "Floppy 360"))
4103 enmType = PDMMEDIATYPE_FLOPPY_360;
4104 else if (!strcmp(pszType, "Floppy 15.6"))
4105 enmType = PDMMEDIATYPE_FLOPPY_FAKE_15_6;
4106 else if (!strcmp(pszType, "Floppy 63.5"))
4107 enmType = PDMMEDIATYPE_FLOPPY_FAKE_63_5;
4108
4109 return enmType;
4110}
4111
4112/**
4113 * Converts PDMMEDIATYPE to the appropriate VDTYPE.
4114 *
4115 * @returns The VDTYPE.
4116 * @param enmType The PDMMEDIATYPE to convert from.
4117 */
4118static VDTYPE drvvdGetVDFromMediaType(PDMMEDIATYPE enmType)
4119{
4120 if (PDMMEDIATYPE_IS_FLOPPY(enmType))
4121 return VDTYPE_FLOPPY;
4122 else if (enmType == PDMMEDIATYPE_DVD || enmType == PDMMEDIATYPE_CDROM)
4123 return VDTYPE_OPTICAL_DISC;
4124 else if (enmType == PDMMEDIATYPE_HARD_DISK)
4125 return VDTYPE_HDD;
4126
4127 AssertMsgFailed(("Invalid media type %d{%s} given!\n", enmType, drvvdGetTypeName(enmType)));
4128 return VDTYPE_HDD;
4129}
4130
4131/**
4132 * Registers statistics associated with the given media driver.
4133 *
4134 * @returns VBox status code.
4135 * @param pThis The media driver instance.
4136 */
4137static int drvvdStatsRegister(PVBOXDISK pThis)
4138{
4139 PPDMDRVINS pDrvIns = pThis->pDrvIns;
4140
4141 /*
4142 * Figure out where to place the stats.
4143 */
4144 uint32_t iInstance = 0;
4145 uint32_t iLUN = 0;
4146 const char *pcszController = NULL;
4147 int rc = pThis->pDrvMediaPort->pfnQueryDeviceLocation(pThis->pDrvMediaPort, &pcszController, &iInstance, &iLUN);
4148 AssertRCReturn(rc, rc);
4149
4150 /*
4151 * Compose the prefix for the statistics to reduce the amount of repetition below.
4152 * The /Public/ bits are official and used by session info in the GUI.
4153 */
4154 char szCtrlUpper[32];
4155 rc = RTStrCopy(szCtrlUpper, sizeof(szCtrlUpper), pcszController);
4156 AssertRCReturn(rc, rc);
4157
4158 RTStrToUpper(szCtrlUpper);
4159 char szPrefix[128];
4160 RTStrPrintf(szPrefix, sizeof(szPrefix), "/Public/Storage/%s%u/Port%u", szCtrlUpper, iInstance, iLUN);
4161
4162 /*
4163 * Do the registrations.
4164 */
4165 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatQueryBufAttempts, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
4166 "Number of attempts to query a direct buffer.", "%s/QueryBufAttempts", szPrefix);
4167 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatQueryBufSuccess, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
4168 "Number of succeeded attempts to query a direct buffer.", "%s/QueryBufSuccess", szPrefix);
4169
4170 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4171 "Amount of data read.", "%s/BytesRead", szPrefix);
4172 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4173 "Amount of data written.", "%s/BytesWritten", szPrefix);
4174
4175 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT,
4176 "Number of I/O requests submitted.", "%s/ReqsSubmitted", szPrefix);
4177 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT,
4178 "Number of I/O requests failed.", "%s/ReqsFailed", szPrefix);
4179 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsSucceeded, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT,
4180 "Number of I/O requests succeeded.", "%s/ReqsSucceeded", szPrefix);
4181 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsFlush, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT,
4182 "Number of flush I/O requests submitted.", "%s/ReqsFlush", szPrefix);
4183 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsWrite, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT,
4184 "Number of write I/O requests submitted.", "%s/ReqsWrite", szPrefix);
4185 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT,
4186 "Number of read I/O requests submitted.", "%s/ReqsRead", szPrefix);
4187 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsDiscard, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT,
4188 "Number of discard I/O requests submitted.", "%s/ReqsDiscard", szPrefix);
4189
4190 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsPerSec, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4191 "Number of processed I/O requests per second.", "%s/ReqsPerSec", szPrefix);
4192
4193 return VINF_SUCCESS;
4194}
4195
4196/**
4197 * Deregisters statistics associated with the given media driver.
4198 *
4199 * @param pThis The media driver instance.
4200 */
4201static void drvvdStatsDeregister(PVBOXDISK pThis)
4202{
4203 PPDMDRVINS pDrvIns = pThis->pDrvIns;
4204
4205 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatQueryBufAttempts);
4206 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatQueryBufSuccess);
4207
4208 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatBytesRead);
4209 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatBytesWritten);
4210 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsSubmitted);
4211 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsFailed);
4212 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsSucceeded);
4213 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsFlush);
4214 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsWrite);
4215 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsRead);
4216 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsDiscard);
4217 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsPerSec);
4218}
4219
4220
4221/*********************************************************************************************************************************
4222* Base interface methods *
4223*********************************************************************************************************************************/
4224
4225/**
4226 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4227 */
4228static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4229{
4230 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
4231 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4232
4233 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
4234 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
4235 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, pThis->fMountable ? &pThis->IMount : NULL);
4236 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEX, pThis->pDrvMediaExPort ? &pThis->IMediaEx : NULL);
4237 return NULL;
4238}
4239
4240
4241/*********************************************************************************************************************************
4242* Saved state notification methods *
4243*********************************************************************************************************************************/
4244
4245/**
4246 * Load done callback for re-opening the image writable during teleportation.
4247 *
4248 * This is called both for successful and failed load runs, we only care about
4249 * successful ones.
4250 *
4251 * @returns VBox status code.
4252 * @param pDrvIns The driver instance.
4253 * @param pSSM The saved state handle.
4254 */
4255static DECLCALLBACK(int) drvvdLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
4256{
4257 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4258 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
4259 Assert(!pThis->fErrorUseRuntime);
4260
4261 /* Drop out if we don't have any work to do or if it's a failed load. */
4262 if ( !pThis->fTempReadOnly
4263 || RT_FAILURE(pHlp->pfnSSMHandleGetStatus(pSSM)))
4264 return VINF_SUCCESS;
4265
4266 int rc = drvvdSetWritable(pThis);
4267 if (RT_FAILURE(rc)) /** @todo does the bugger set any errors? */
4268 return pHlp->pfnSSMSetLoadError(pSSM, rc, RT_SRC_POS,
4269 N_("Failed to write lock the images"));
4270 return VINF_SUCCESS;
4271}
4272
4273
4274/*********************************************************************************************************************************
4275* Driver methods *
4276*********************************************************************************************************************************/
4277
4278/**
4279 * Worker for the power off or destruct callback.
4280 *
4281 * @param pDrvIns The driver instance.
4282 */
4283static void drvvdPowerOffOrDestructOrUnmount(PPDMDRVINS pDrvIns)
4284{
4285 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4286 LogFlowFunc(("\n"));
4287
4288 RTSEMFASTMUTEX mutex;
4289 ASMAtomicXchgHandle(&pThis->MergeCompleteMutex, NIL_RTSEMFASTMUTEX, &mutex);
4290 if (mutex != NIL_RTSEMFASTMUTEX)
4291 {
4292 /* Request the semaphore to wait until a potentially running merge
4293 * operation has been finished. */
4294 int rc = RTSemFastMutexRequest(mutex);
4295 AssertRC(rc);
4296 pThis->fMergePending = false;
4297 rc = RTSemFastMutexRelease(mutex);
4298 AssertRC(rc);
4299 rc = RTSemFastMutexDestroy(mutex);
4300 AssertRC(rc);
4301 }
4302
4303 if (RT_VALID_PTR(pThis->pBlkCache))
4304 {
4305 PDMDrvHlpBlkCacheRelease(pThis->pDrvIns, pThis->pBlkCache);
4306 pThis->pBlkCache = NULL;
4307 }
4308
4309 if (RT_VALID_PTR(pThis->pRegionList))
4310 {
4311 VDRegionListFree(pThis->pRegionList);
4312 pThis->pRegionList = NULL;
4313 }
4314
4315 if (RT_VALID_PTR(pThis->pDisk))
4316 {
4317 VDDestroy(pThis->pDisk);
4318 pThis->pDisk = NULL;
4319 }
4320 drvvdFreeImages(pThis);
4321}
4322
4323/**
4324 * @copydoc FNPDMDRVPOWEROFF
4325 */
4326static DECLCALLBACK(void) drvvdPowerOff(PPDMDRVINS pDrvIns)
4327{
4328 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
4329 drvvdPowerOffOrDestructOrUnmount(pDrvIns);
4330}
4331
4332/**
4333 * @callback_method_impl{FNPDMDRVRESUME}
4334 *
4335 * VM resume notification that we use to undo what the temporary read-only image
4336 * mode set by drvvdSuspend.
4337 *
4338 * Also switch to runtime error mode if we're resuming after a state load
4339 * without having been powered on first.
4340 *
4341 * @todo The VMSetError vs VMSetRuntimeError mess must be fixed elsewhere,
4342 * we're making assumptions about Main behavior here!
4343 */
4344static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
4345{
4346 LogFlowFunc(("\n"));
4347 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4348
4349 drvvdSetWritable(pThis);
4350 pThis->fSuspending = false;
4351 pThis->fRedo = false;
4352
4353 if (pThis->pBlkCache)
4354 {
4355 int rc = PDMDrvHlpBlkCacheResume(pThis->pDrvIns, pThis->pBlkCache);
4356 AssertRC(rc);
4357 }
4358
4359 if (pThis->pDrvMediaExPort)
4360 {
4361 /* Mark all requests waiting for I/O memory as active again so they get accounted for. */
4362 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
4363 PPDMMEDIAEXIOREQINT pIoReq, pIoReqNext;
4364 RTListForEachSafe(&pThis->LstIoReqIoBufWait, pIoReq, pIoReqNext, PDMMEDIAEXIOREQINT, NdLstWait)
4365 {
4366 pThis->pDrvMediaExPort->pfnIoReqStateChanged(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
4367 PDMMEDIAEXIOREQSTATE_ACTIVE);
4368 ASMAtomicIncU32(&pThis->cIoReqsActive);
4369 LogFlowFunc(("Resumed I/O request %#p\n", pIoReq));
4370 }
4371 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
4372
4373 /* Kick of any request we have to redo. */
4374 RTCritSectEnter(&pThis->CritSectIoReqRedo);
4375 RTListForEachSafe(&pThis->LstIoReqRedo, pIoReq, pIoReqNext, PDMMEDIAEXIOREQINT, NdLstWait)
4376 {
4377 int rc = VINF_SUCCESS;
4378 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_SUSPENDED);
4379
4380 RTListNodeRemove(&pIoReq->NdLstWait);
4381 ASMAtomicIncU32(&pThis->cIoReqsActive);
4382
4383 LogFlowFunc(("Resuming I/O request %#p fXchg=%RTbool\n", pIoReq, fXchg));
4384 if (fXchg)
4385 {
4386 pThis->pDrvMediaExPort->pfnIoReqStateChanged(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
4387 PDMMEDIAEXIOREQSTATE_ACTIVE);
4388 LogFlowFunc(("Resumed I/O request %#p\n", pIoReq));
4389 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
4390 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
4391 rc = drvvdMediaExIoReqReadWriteProcess(pThis, pIoReq, true /* fUpNotify */);
4392 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_FLUSH)
4393 {
4394 rc = drvvdMediaExIoReqFlushWrapper(pThis, pIoReq);
4395 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4396 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
4397 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
4398 rc = VINF_SUCCESS;
4399 }
4400 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD)
4401 {
4402 rc = drvvdMediaExIoReqDiscardWrapper(pThis, pIoReq);
4403 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4404 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
4405 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
4406 rc = VINF_SUCCESS;
4407 }
4408 else
4409 AssertMsgFailed(("Invalid request type %u\n", pIoReq->enmType));
4410
4411 /* The read write process will call the completion callback on its own. */
4412 if ( rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS
4413 && ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD
4414 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_FLUSH))
4415 {
4416 Assert( ( pIoReq->enmType != PDMMEDIAEXIOREQTYPE_WRITE
4417 && pIoReq->enmType != PDMMEDIAEXIOREQTYPE_READ)
4418 || !pIoReq->ReadWrite.cbReqLeft
4419 || RT_FAILURE(rc));
4420 drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rc, true /* fUpNotify */);
4421 }
4422
4423 }
4424 else
4425 {
4426 /* Request was canceled inbetween, so don't care and notify the owner about the completed request. */
4427 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
4428 drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, VERR_PDM_MEDIAEX_IOREQ_CANCELED, true /* fUpNotify */);
4429 }
4430 }
4431 Assert(RTListIsEmpty(&pThis->LstIoReqRedo));
4432 RTCritSectLeave(&pThis->CritSectIoReqRedo);
4433 }
4434
4435 /* Try to process any requests waiting for I/O memory now. */
4436 drvvdMediaExIoReqProcessWaiting(pThis);
4437 pThis->fErrorUseRuntime = true;
4438}
4439
4440/**
4441 * @callback_method_impl{FNPDMDRVSUSPEND}
4442 *
4443 * When the VM is being suspended, temporarily change to read-only image mode.
4444 *
4445 * This is important for several reasons:
4446 * -# It makes sure that there are no pending writes to the image. Most
4447 * backends implements this by closing and reopening the image in read-only
4448 * mode.
4449 * -# It allows Main to read the images during snapshotting without having
4450 * to account for concurrent writes.
4451 * -# This is essential for making teleportation targets sharing images work
4452 * right. Both with regards to caching and with regards to file sharing
4453 * locks (RTFILE_O_DENY_*). (See also drvvdLoadDone.)
4454 */
4455static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
4456{
4457 LogFlowFunc(("\n"));
4458 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4459
4460 if (pThis->pBlkCache)
4461 {
4462 int rc = PDMDrvHlpBlkCacheSuspend(pThis->pDrvIns, pThis->pBlkCache);
4463 AssertRC(rc);
4464 }
4465
4466 drvvdSetReadonly(pThis);
4467}
4468
4469/**
4470 * @callback_method_impl{FNPDMDRVPOWERON}
4471 */
4472static DECLCALLBACK(void) drvvdPowerOn(PPDMDRVINS pDrvIns)
4473{
4474 LogFlowFunc(("\n"));
4475 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4476 drvvdSetWritable(pThis);
4477 pThis->fErrorUseRuntime = true;
4478}
4479
4480/**
4481 * @callback_method_impl{FNPDMDRVRESET}
4482 */
4483static DECLCALLBACK(void) drvvdReset(PPDMDRVINS pDrvIns)
4484{
4485 LogFlowFunc(("\n"));
4486 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4487
4488 if (pThis->pBlkCache)
4489 {
4490 int rc = PDMDrvHlpBlkCacheClear(pThis->pDrvIns, pThis->pBlkCache);
4491 AssertRC(rc);
4492 }
4493
4494 if (pThis->fBootAccelEnabled)
4495 {
4496 pThis->fBootAccelActive = true;
4497 pThis->cbDataValid = 0;
4498 pThis->offDisk = 0;
4499 }
4500 pThis->fLocked = false;
4501}
4502
4503/**
4504 * @callback_method_impl{FNPDMDRVDESTRUCT}
4505 */
4506static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
4507{
4508 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
4509 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4510 LogFlowFunc(("\n"));
4511
4512 /*
4513 * Make sure the block cache and disks are closed when this driver is
4514 * destroyed. This method will get called without calling the power off
4515 * callback first when we reconfigure the driver chain after a snapshot.
4516 */
4517 drvvdPowerOffOrDestructOrUnmount(pDrvIns);
4518 if (pThis->MergeLock != NIL_RTSEMRW)
4519 {
4520 int rc = RTSemRWDestroy(pThis->MergeLock);
4521 AssertRC(rc);
4522 pThis->MergeLock = NIL_RTSEMRW;
4523 }
4524 if (pThis->pbData)
4525 {
4526 RTMemFree(pThis->pbData);
4527 pThis->pbData = NULL;
4528 }
4529 if (pThis->pszBwGroup)
4530 {
4531 PDMDrvHlpMMHeapFree(pDrvIns, pThis->pszBwGroup);
4532 pThis->pszBwGroup = NULL;
4533 }
4534 if (pThis->hHbdMgr != NIL_HBDMGR)
4535 HBDMgrDestroy(pThis->hHbdMgr);
4536 if (pThis->hIoReqCache != NIL_RTMEMCACHE)
4537 RTMemCacheDestroy(pThis->hIoReqCache);
4538 if (pThis->hIoBufMgr != NIL_IOBUFMGR)
4539 IOBUFMgrDestroy(pThis->hIoBufMgr);
4540 if (RTCritSectIsInitialized(&pThis->CritSectIoReqsIoBufWait))
4541 RTCritSectDelete(&pThis->CritSectIoReqsIoBufWait);
4542 if (RTCritSectIsInitialized(&pThis->CritSectIoReqRedo))
4543 RTCritSectDelete(&pThis->CritSectIoReqRedo);
4544 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aIoReqAllocBins); i++)
4545 if (pThis->aIoReqAllocBins[i].hMtxLstIoReqAlloc != NIL_RTSEMFASTMUTEX)
4546 RTSemFastMutexDestroy(pThis->aIoReqAllocBins[i].hMtxLstIoReqAlloc);
4547
4548 drvvdStatsDeregister(pThis);
4549
4550 PVDCFGNODE pIt;
4551 PVDCFGNODE pItNext;
4552 RTListForEachSafe(&pThis->LstCfgNodes, pIt, pItNext, VDCFGNODE, NdLst)
4553 {
4554 RTListNodeRemove(&pIt->NdLst);
4555 RTMemFreeZ(pIt, sizeof(*pIt));
4556 }
4557}
4558
4559/**
4560 * @callback_method_impl{FNPDMDRVCONSTRUCT,
4561 * Construct a VBox disk media driver instance.}
4562 */
4563static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
4564{
4565 RT_NOREF(fFlags);
4566 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
4567 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4568 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
4569
4570 LogFlowFunc(("\n"));
4571
4572 char *pszName = NULL; /* The path of the disk image file. */
4573 char *pszFormat = NULL; /* The format backed to use for this image. */
4574 char *pszCachePath = NULL; /* The path to the cache image. */
4575 char *pszCacheFormat = NULL; /* The format backend to use for the cache image. */
4576 bool fReadOnly = false; /* True if the media is read-only. */
4577 bool fMaybeReadOnly = false; /* True if the media may or may not be read-only. */
4578 bool fHonorZeroWrites = false; /* True if zero blocks should be written. */
4579
4580 /*
4581 * Init the static parts.
4582 */
4583 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
4584 pThis->pDrvIns = pDrvIns;
4585 pThis->fTempReadOnly = false;
4586 pThis->pDisk = NULL;
4587 pThis->fAsyncIOSupported = false;
4588 pThis->fShareable = false;
4589 pThis->fMergePending = false;
4590 pThis->MergeCompleteMutex = NIL_RTSEMFASTMUTEX;
4591 pThis->MergeLock = NIL_RTSEMRW;
4592 pThis->uMergeSource = VD_LAST_IMAGE;
4593 pThis->uMergeTarget = VD_LAST_IMAGE;
4594 pThis->CfgCrypto.pCfgNode = NULL;
4595 pThis->CfgCrypto.pHlp = pDrvIns->pHlpR3;
4596 pThis->pIfSecKey = NULL;
4597 pThis->hIoReqCache = NIL_RTMEMCACHE;
4598 pThis->hIoBufMgr = NIL_IOBUFMGR;
4599 pThis->pRegionList = NULL;
4600 pThis->fSuspending = false;
4601 pThis->fRedo = false;
4602
4603 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aIoReqAllocBins); i++)
4604 pThis->aIoReqAllocBins[i].hMtxLstIoReqAlloc = NIL_RTSEMFASTMUTEX;
4605
4606 /* IMedia */
4607 pThis->IMedia.pfnRead = drvvdRead;
4608 pThis->IMedia.pfnReadPcBios = drvvdReadPcBios;
4609 pThis->IMedia.pfnWrite = drvvdWrite;
4610 pThis->IMedia.pfnFlush = drvvdFlush;
4611 pThis->IMedia.pfnMerge = drvvdMerge;
4612 pThis->IMedia.pfnSetSecKeyIf = drvvdSetSecKeyIf;
4613 pThis->IMedia.pfnGetSize = drvvdGetSize;
4614 pThis->IMedia.pfnGetSectorSize = drvvdGetSectorSize;
4615 pThis->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
4616 pThis->IMedia.pfnIsNonRotational = drvvdIsNonRotational;
4617 pThis->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
4618 pThis->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
4619 pThis->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
4620 pThis->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
4621 pThis->IMedia.pfnBiosIsVisible = drvvdBiosIsVisible;
4622 pThis->IMedia.pfnGetType = drvvdGetType;
4623 pThis->IMedia.pfnGetUuid = drvvdGetUuid;
4624 pThis->IMedia.pfnDiscard = drvvdDiscard;
4625 pThis->IMedia.pfnSendCmd = NULL;
4626 pThis->IMedia.pfnGetRegionCount = drvvdGetRegionCount;
4627 pThis->IMedia.pfnQueryRegionProperties = drvvdQueryRegionProperties;
4628 pThis->IMedia.pfnQueryRegionPropertiesForLba = drvvdQueryRegionPropertiesForLba;
4629
4630 /* IMount */
4631 pThis->IMount.pfnUnmount = drvvdUnmount;
4632 pThis->IMount.pfnIsMounted = drvvdIsMounted;
4633 pThis->IMount.pfnLock = drvvdLock;
4634 pThis->IMount.pfnUnlock = drvvdUnlock;
4635 pThis->IMount.pfnIsLocked = drvvdIsLocked;
4636
4637 /* IMediaEx */
4638 pThis->IMediaEx.pfnQueryFeatures = drvvdQueryFeatures;
4639 pThis->IMediaEx.pfnNotifySuspend = drvvdNotifySuspend;
4640 pThis->IMediaEx.pfnIoReqAllocSizeSet = drvvdIoReqAllocSizeSet;
4641 pThis->IMediaEx.pfnIoReqAlloc = drvvdIoReqAlloc;
4642 pThis->IMediaEx.pfnIoReqFree = drvvdIoReqFree;
4643 pThis->IMediaEx.pfnIoReqQueryResidual = drvvdIoReqQueryResidual;
4644 pThis->IMediaEx.pfnIoReqQueryXferSize = drvvdIoReqQueryXferSize;
4645 pThis->IMediaEx.pfnIoReqCancelAll = drvvdIoReqCancelAll;
4646 pThis->IMediaEx.pfnIoReqCancel = drvvdIoReqCancel;
4647 pThis->IMediaEx.pfnIoReqRead = drvvdIoReqRead;
4648 pThis->IMediaEx.pfnIoReqWrite = drvvdIoReqWrite;
4649 pThis->IMediaEx.pfnIoReqFlush = drvvdIoReqFlush;
4650 pThis->IMediaEx.pfnIoReqDiscard = drvvdIoReqDiscard;
4651 pThis->IMediaEx.pfnIoReqSendScsiCmd = drvvdIoReqSendScsiCmd;
4652 pThis->IMediaEx.pfnIoReqGetActiveCount = drvvdIoReqGetActiveCount;
4653 pThis->IMediaEx.pfnIoReqGetSuspendedCount = drvvdIoReqGetSuspendedCount;
4654 pThis->IMediaEx.pfnIoReqQuerySuspendedStart = drvvdIoReqQuerySuspendedStart;
4655 pThis->IMediaEx.pfnIoReqQuerySuspendedNext = drvvdIoReqQuerySuspendedNext;
4656 pThis->IMediaEx.pfnIoReqSuspendedSave = drvvdIoReqSuspendedSave;
4657 pThis->IMediaEx.pfnIoReqSuspendedLoad = drvvdIoReqSuspendedLoad;
4658
4659 RTListInit(&pThis->LstCfgNodes);
4660
4661 /* Initialize supported VD interfaces. */
4662 pThis->pVDIfsDisk = NULL;
4663
4664 pThis->VDIfError.pfnError = drvvdErrorCallback;
4665 pThis->VDIfError.pfnMessage = NULL;
4666 int rc = VDInterfaceAdd(&pThis->VDIfError.Core, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
4667 pDrvIns, sizeof(VDINTERFACEERROR), &pThis->pVDIfsDisk);
4668 AssertRC(rc);
4669
4670 /* List of images is empty now. */
4671 pThis->pImages = NULL;
4672
4673 pThis->pDrvMediaPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAPORT);
4674 if (!pThis->pDrvMediaPort)
4675 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
4676 N_("No media port interface above"));
4677
4678 pThis->pDrvMountNotify = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMOUNTNOTIFY);
4679
4680 /*
4681 * Try to attach the optional extended media interface port above and initialize associated
4682 * structures if available.
4683 */
4684 pThis->pDrvMediaExPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAEXPORT);
4685 if (pThis->pDrvMediaExPort)
4686 {
4687 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aIoReqAllocBins); i++)
4688 {
4689 rc = RTSemFastMutexCreate(&pThis->aIoReqAllocBins[i].hMtxLstIoReqAlloc);
4690 if (RT_FAILURE(rc))
4691 break;
4692 RTListInit(&pThis->aIoReqAllocBins[i].LstIoReqAlloc);
4693 }
4694
4695 if (RT_SUCCESS(rc))
4696 rc = RTCritSectInit(&pThis->CritSectIoReqsIoBufWait);
4697
4698 if (RT_SUCCESS(rc))
4699 rc = RTCritSectInit(&pThis->CritSectIoReqRedo);
4700
4701 if (RT_FAILURE(rc))
4702 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Creating Mutex failed"));
4703
4704 RTListInit(&pThis->LstIoReqIoBufWait);
4705 RTListInit(&pThis->LstIoReqRedo);
4706 }
4707
4708 /* Before we access any VD API load all given plugins. */
4709 rc = drvvdLoadPlugins(pDrvIns, pCfg);
4710 if (RT_FAILURE(rc))
4711 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Loading VD plugins failed"));
4712
4713 /*
4714 * Validate configuration and find all parent images.
4715 * It's sort of up side down from the image dependency tree.
4716 */
4717 bool fHostIP = false;
4718 bool fUseNewIo = false;
4719 bool fUseBlockCache = false;
4720 bool fDiscard = false;
4721 bool fInformAboutZeroBlocks = false;
4722 bool fSkipConsistencyChecks = false;
4723 bool fEmptyDrive = false;
4724 unsigned iLevel = 0;
4725 PCFGMNODE pCurNode = pCfg;
4726 uint32_t cbIoBufMax = 0;
4727
4728 for (;;)
4729 {
4730 bool fValid;
4731
4732 if (pCurNode == pCfg)
4733 {
4734 /* Toplevel configuration additionally contains the global image
4735 * open flags. Some might be converted to per-image flags later. */
4736 fValid = pHlp->pfnCFGMAreValuesValid(pCurNode,
4737 "Format\0Path\0"
4738 "ReadOnly\0MaybeReadOnly\0TempReadOnly\0Shareable\0HonorZeroWrites\0"
4739 "HostIPStack\0UseNewIo\0BootAcceleration\0BootAccelerationBuffer\0"
4740 "SetupMerge\0MergeSource\0MergeTarget\0BwGroup\0Type\0BlockCache\0"
4741 "CachePath\0CacheFormat\0Discard\0InformAboutZeroBlocks\0"
4742 "SkipConsistencyChecks\0"
4743 "Locked\0BIOSVisible\0Cylinders\0Heads\0Sectors\0Mountable\0"
4744 "EmptyDrive\0IoBufMax\0NonRotationalMedium\0"
4745#if defined(VBOX_PERIODIC_FLUSH) || defined(VBOX_IGNORE_FLUSH)
4746 "FlushInterval\0IgnoreFlush\0IgnoreFlushAsync\0"
4747#endif /* !(VBOX_PERIODIC_FLUSH || VBOX_IGNORE_FLUSH) */
4748 );
4749 }
4750 else
4751 {
4752 /* All other image configurations only contain image name and
4753 * the format information. */
4754 fValid = pHlp->pfnCFGMAreValuesValid(pCurNode, "Format\0Path\0"
4755 "MergeSource\0MergeTarget\0");
4756 }
4757 if (!fValid)
4758 {
4759 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
4760 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
4761 break;
4762 }
4763
4764 if (pCurNode == pCfg)
4765 {
4766 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "HostIPStack", &fHostIP, true);
4767 if (RT_FAILURE(rc))
4768 {
4769 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4770 N_("DrvVD: Configuration error: Querying \"HostIPStack\" as boolean failed"));
4771 break;
4772 }
4773
4774 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "HonorZeroWrites", &fHonorZeroWrites, false);
4775 if (RT_FAILURE(rc))
4776 {
4777 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4778 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
4779 break;
4780 }
4781
4782 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "ReadOnly", &fReadOnly, false);
4783 if (RT_FAILURE(rc))
4784 {
4785 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4786 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
4787 break;
4788 }
4789
4790 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "MaybeReadOnly", &fMaybeReadOnly, false);
4791 if (RT_FAILURE(rc))
4792 {
4793 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4794 N_("DrvVD: Configuration error: Querying \"MaybeReadOnly\" as boolean failed"));
4795 break;
4796 }
4797
4798 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "TempReadOnly", &pThis->fTempReadOnly, false);
4799 if (RT_FAILURE(rc))
4800 {
4801 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4802 N_("DrvVD: Configuration error: Querying \"TempReadOnly\" as boolean failed"));
4803 break;
4804 }
4805 if (fReadOnly && pThis->fTempReadOnly)
4806 {
4807 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
4808 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"TempReadOnly\" are set"));
4809 break;
4810 }
4811
4812 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "Shareable", &pThis->fShareable, false);
4813 if (RT_FAILURE(rc))
4814 {
4815 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4816 N_("DrvVD: Configuration error: Querying \"Shareable\" as boolean failed"));
4817 break;
4818 }
4819
4820 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "UseNewIo", &fUseNewIo, false);
4821 if (RT_FAILURE(rc))
4822 {
4823 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4824 N_("DrvVD: Configuration error: Querying \"UseNewIo\" as boolean failed"));
4825 break;
4826 }
4827 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "SetupMerge", &pThis->fMergePending, false);
4828 if (RT_FAILURE(rc))
4829 {
4830 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4831 N_("DrvVD: Configuration error: Querying \"SetupMerge\" as boolean failed"));
4832 break;
4833 }
4834 if (fReadOnly && pThis->fMergePending)
4835 {
4836 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
4837 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"MergePending\" are set"));
4838 break;
4839 }
4840 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "BootAcceleration", &pThis->fBootAccelEnabled, false);
4841 if (RT_FAILURE(rc))
4842 {
4843 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4844 N_("DrvVD: Configuration error: Querying \"BootAcceleration\" as boolean failed"));
4845 break;
4846 }
4847 rc = pHlp->pfnCFGMQueryU32Def(pCurNode, "BootAccelerationBuffer", (uint32_t *)&pThis->cbBootAccelBuffer, 16 * _1K);
4848 if (RT_FAILURE(rc))
4849 {
4850 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4851 N_("DrvVD: Configuration error: Querying \"BootAccelerationBuffer\" as integer failed"));
4852 break;
4853 }
4854 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "BlockCache", &fUseBlockCache, false);
4855 if (RT_FAILURE(rc))
4856 {
4857 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4858 N_("DrvVD: Configuration error: Querying \"BlockCache\" as boolean failed"));
4859 break;
4860 }
4861 rc = pHlp->pfnCFGMQueryStringAlloc(pCurNode, "BwGroup", &pThis->pszBwGroup);
4862 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
4863 {
4864 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4865 N_("DrvVD: Configuration error: Querying \"BwGroup\" as string failed"));
4866 break;
4867 }
4868 else
4869 rc = VINF_SUCCESS;
4870 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "Discard", &fDiscard, false);
4871 if (RT_FAILURE(rc))
4872 {
4873 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4874 N_("DrvVD: Configuration error: Querying \"Discard\" as boolean failed"));
4875 break;
4876 }
4877 if (fReadOnly && fDiscard)
4878 {
4879 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
4880 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"Discard\" are set"));
4881 break;
4882 }
4883 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "InformAboutZeroBlocks", &fInformAboutZeroBlocks, false);
4884 if (RT_FAILURE(rc))
4885 {
4886 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4887 N_("DrvVD: Configuration error: Querying \"InformAboutZeroBlocks\" as boolean failed"));
4888 break;
4889 }
4890 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "SkipConsistencyChecks", &fSkipConsistencyChecks, true);
4891 if (RT_FAILURE(rc))
4892 {
4893 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4894 N_("DrvVD: Configuration error: Querying \"SKipConsistencyChecks\" as boolean failed"));
4895 break;
4896 }
4897
4898 char *psz = NULL;
4899 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "Type", &psz);
4900 if (RT_FAILURE(rc))
4901 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_BLOCK_NO_TYPE, N_("Failed to obtain the sub type"));
4902 pThis->enmType = drvvdGetMediaTypeFromString(psz);
4903 if (pThis->enmType == PDMMEDIATYPE_ERROR)
4904 {
4905 PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_BLOCK_UNKNOWN_TYPE, RT_SRC_POS,
4906 N_("Unknown type \"%s\""), psz);
4907 PDMDrvHlpMMHeapFree(pDrvIns, psz);
4908 return VERR_PDM_BLOCK_UNKNOWN_TYPE;
4909 }
4910 PDMDrvHlpMMHeapFree(pDrvIns, psz); psz = NULL;
4911
4912 rc = pHlp->pfnCFGMQueryStringAlloc(pCurNode, "CachePath", &pszCachePath);
4913 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
4914 {
4915 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4916 N_("DrvVD: Configuration error: Querying \"CachePath\" as string failed"));
4917 break;
4918 }
4919 rc = VINF_SUCCESS;
4920
4921 if (pszCachePath)
4922 {
4923 rc = pHlp->pfnCFGMQueryStringAlloc(pCurNode, "CacheFormat", &pszCacheFormat);
4924 if (RT_FAILURE(rc))
4925 {
4926 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4927 N_("DrvVD: Configuration error: Querying \"CacheFormat\" as string failed"));
4928 break;
4929 }
4930 }
4931
4932 /* Mountable */
4933 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "Mountable", &pThis->fMountable, false);
4934 if (RT_FAILURE(rc))
4935 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Mountable\" from the config"));
4936
4937 /* Locked */
4938 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "Locked", &pThis->fLocked, false);
4939 if (RT_FAILURE(rc))
4940 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Locked\" from the config"));
4941
4942 /* BIOS visible */
4943 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "BIOSVisible", &pThis->fBiosVisible, true);
4944 if (RT_FAILURE(rc))
4945 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"BIOSVisible\" from the config"));
4946
4947 /* Cylinders */
4948 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "Cylinders", &pThis->LCHSGeometry.cCylinders, 0);
4949 if (RT_FAILURE(rc))
4950 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Cylinders\" from the config"));
4951
4952 /* Heads */
4953 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "Heads", &pThis->LCHSGeometry.cHeads, 0);
4954 if (RT_FAILURE(rc))
4955 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Heads\" from the config"));
4956
4957 /* Sectors */
4958 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "Sectors", &pThis->LCHSGeometry.cSectors, 0);
4959 if (RT_FAILURE(rc))
4960 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Sectors\" from the config"));
4961
4962 /* Uuid */
4963 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "Uuid", &psz);
4964 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
4965 RTUuidClear(&pThis->Uuid);
4966 else if (RT_SUCCESS(rc))
4967 {
4968 rc = RTUuidFromStr(&pThis->Uuid, psz);
4969 if (RT_FAILURE(rc))
4970 {
4971 PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Uuid from string failed on \"%s\""), psz);
4972 PDMDrvHlpMMHeapFree(pDrvIns, psz);
4973 return rc;
4974 }
4975 PDMDrvHlpMMHeapFree(pDrvIns, psz); psz = NULL;
4976 }
4977 else
4978 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Uuid\" from the config"));
4979
4980#ifdef VBOX_PERIODIC_FLUSH
4981 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "FlushInterval", &pThis->cbFlushInterval, 0);
4982 if (RT_FAILURE(rc))
4983 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"FlushInterval\" from the config"));
4984#endif /* VBOX_PERIODIC_FLUSH */
4985
4986#ifdef VBOX_IGNORE_FLUSH
4987 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "IgnoreFlush", &pThis->fIgnoreFlush, true);
4988 if (RT_FAILURE(rc))
4989 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"IgnoreFlush\" from the config"));
4990
4991 if (pThis->fIgnoreFlush)
4992 LogRel(("DrvVD: Flushes will be ignored\n"));
4993 else
4994 LogRel(("DrvVD: Flushes will be passed to the disk\n"));
4995
4996 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "IgnoreFlushAsync", &pThis->fIgnoreFlushAsync, false);
4997 if (RT_FAILURE(rc))
4998 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"IgnoreFlushAsync\" from the config"));
4999
5000 if (pThis->fIgnoreFlushAsync)
5001 LogRel(("DrvVD: Async flushes will be ignored\n"));
5002 else
5003 LogRel(("DrvVD: Async flushes will be passed to the disk\n"));
5004#endif /* VBOX_IGNORE_FLUSH */
5005
5006 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "EmptyDrive", &fEmptyDrive, false);
5007 if (RT_FAILURE(rc))
5008 {
5009 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5010 N_("DrvVD: Configuration error: Querying \"EmptyDrive\" as boolean failed"));
5011 break;
5012 }
5013
5014 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "IoBufMax", &cbIoBufMax, 5 * _1M);
5015 if (RT_FAILURE(rc))
5016 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"IoBufMax\" from the config"));
5017
5018 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "NonRotationalMedium", &pThis->fNonRotational, false);
5019 if (RT_FAILURE(rc))
5020 return PDMDRV_SET_ERROR(pDrvIns, rc,
5021 N_("DrvVD configuration error: Querying \"NonRotationalMedium\" as boolean failed"));
5022 }
5023
5024 PCFGMNODE pParent = pHlp->pfnCFGMGetChild(pCurNode, "Parent");
5025 if (!pParent)
5026 break;
5027 pCurNode = pParent;
5028 iLevel++;
5029 }
5030
5031 if (pThis->pDrvMediaExPort)
5032 rc = IOBUFMgrCreate(&pThis->hIoBufMgr, cbIoBufMax, pThis->CfgCrypto.pCfgNode ? IOBUFMGR_F_REQUIRE_NOT_PAGABLE : IOBUFMGR_F_DEFAULT);
5033
5034 if ( !fEmptyDrive
5035 && RT_SUCCESS(rc))
5036 {
5037 /*
5038 * Create the image container and the necessary interfaces.
5039 */
5040 if (RT_SUCCESS(rc))
5041 {
5042 /*
5043 * The image has a bandwidth group but the host cache is enabled.
5044 * Use the async I/O framework but tell it to enable the host cache.
5045 */
5046 if (!fUseNewIo && pThis->pszBwGroup)
5047 {
5048 pThis->fAsyncIoWithHostCache = true;
5049 fUseNewIo = true;
5050 }
5051
5052 /** @todo quick hack to work around problems in the async I/O
5053 * implementation (rw semaphore thread ownership problem)
5054 * while a merge is running. Remove once this is fixed. */
5055 if (pThis->fMergePending)
5056 fUseNewIo = false;
5057
5058 if (RT_SUCCESS(rc) && pThis->fMergePending)
5059 {
5060 rc = RTSemFastMutexCreate(&pThis->MergeCompleteMutex);
5061 if (RT_SUCCESS(rc))
5062 rc = RTSemRWCreate(&pThis->MergeLock);
5063 if (RT_SUCCESS(rc))
5064 {
5065 pThis->VDIfThreadSync.pfnStartRead = drvvdThreadStartRead;
5066 pThis->VDIfThreadSync.pfnFinishRead = drvvdThreadFinishRead;
5067 pThis->VDIfThreadSync.pfnStartWrite = drvvdThreadStartWrite;
5068 pThis->VDIfThreadSync.pfnFinishWrite = drvvdThreadFinishWrite;
5069
5070 rc = VDInterfaceAdd(&pThis->VDIfThreadSync.Core, "DrvVD_ThreadSync", VDINTERFACETYPE_THREADSYNC,
5071 pThis, sizeof(VDINTERFACETHREADSYNC), &pThis->pVDIfsDisk);
5072 }
5073 else
5074 {
5075 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5076 N_("DrvVD: Failed to create semaphores for \"MergePending\""));
5077 }
5078 }
5079
5080 if (RT_SUCCESS(rc))
5081 {
5082 rc = VDCreate(pThis->pVDIfsDisk, drvvdGetVDFromMediaType(pThis->enmType), &pThis->pDisk);
5083 /* Error message is already set correctly. */
5084 }
5085 }
5086
5087 if (pThis->pDrvMediaExPort && fUseNewIo)
5088 pThis->fAsyncIOSupported = true;
5089
5090 uint64_t tsStart = RTTimeNanoTS();
5091
5092 unsigned iImageIdx = 0;
5093 while (pCurNode && RT_SUCCESS(rc))
5094 {
5095 /* Allocate per-image data. */
5096 PVBOXIMAGE pImage = drvvdNewImage(pThis);
5097 if (!pImage)
5098 {
5099 rc = VERR_NO_MEMORY;
5100 break;
5101 }
5102
5103 /*
5104 * Read the image configuration.
5105 */
5106 rc = pHlp->pfnCFGMQueryStringAlloc(pCurNode, "Path", &pszName);
5107 if (RT_FAILURE(rc))
5108 {
5109 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5110 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
5111 break;
5112 }
5113
5114 rc = pHlp->pfnCFGMQueryStringAlloc(pCurNode, "Format", &pszFormat);
5115 if (RT_FAILURE(rc))
5116 {
5117 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5118 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
5119 break;
5120 }
5121
5122 bool fMergeSource;
5123 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "MergeSource", &fMergeSource, false);
5124 if (RT_FAILURE(rc))
5125 {
5126 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5127 N_("DrvVD: Configuration error: Querying \"MergeSource\" as boolean failed"));
5128 break;
5129 }
5130 if (fMergeSource)
5131 {
5132 if (pThis->uMergeSource == VD_LAST_IMAGE)
5133 pThis->uMergeSource = iImageIdx;
5134 else
5135 {
5136 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
5137 N_("DrvVD: Configuration error: Multiple \"MergeSource\" occurrences"));
5138 break;
5139 }
5140 }
5141
5142 bool fMergeTarget;
5143 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "MergeTarget", &fMergeTarget, false);
5144 if (RT_FAILURE(rc))
5145 {
5146 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5147 N_("DrvVD: Configuration error: Querying \"MergeTarget\" as boolean failed"));
5148 break;
5149 }
5150 if (fMergeTarget)
5151 {
5152 if (pThis->uMergeTarget == VD_LAST_IMAGE)
5153 pThis->uMergeTarget = iImageIdx;
5154 else
5155 {
5156 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
5157 N_("DrvVD: Configuration error: Multiple \"MergeTarget\" occurrences"));
5158 break;
5159 }
5160 }
5161
5162 PCFGMNODE pCfgVDConfig = pHlp->pfnCFGMGetChild(pCurNode, "VDConfig");
5163 pImage->VDIfConfig.pfnAreKeysValid = drvvdCfgAreKeysValid;
5164 pImage->VDIfConfig.pfnQuerySize = drvvdCfgQuerySize;
5165 pImage->VDIfConfig.pfnQuery = drvvdCfgQuery;
5166 pImage->VDIfConfig.pfnQueryBytes = NULL;
5167
5168 PVDCFGNODE pCfgNode = (PVDCFGNODE)RTMemAllocZ(sizeof(*pCfgNode));
5169 if (RT_UNLIKELY(!pCfgNode))
5170 {
5171 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_NO_MEMORY,
5172 N_("DrvVD: Failed to allocate memory for config node"));
5173 break;
5174 }
5175
5176 pCfgNode->pHlp = pDrvIns->pHlpR3;
5177 pCfgNode->pCfgNode = pCfgVDConfig;
5178 RTListAppend(&pThis->LstCfgNodes, &pCfgNode->NdLst);
5179
5180 rc = VDInterfaceAdd(&pImage->VDIfConfig.Core, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
5181 pCfgNode, sizeof(VDINTERFACECONFIG), &pImage->pVDIfsImage);
5182 AssertRC(rc);
5183
5184 /* Check VDConfig for encryption config. */
5185 /** @todo This makes sure that the crypto config is not cleared accidentally
5186 * when it was set because there are multiple VDConfig entries for a snapshot chain
5187 * but only one contains the crypto config.
5188 *
5189 * This needs to be properly fixed by specifying which part of the image should contain the
5190 * crypto stuff.
5191 */
5192 if (!pThis->CfgCrypto.pCfgNode)
5193 {
5194 if (pCfgVDConfig)
5195 pThis->CfgCrypto.pCfgNode = pHlp->pfnCFGMGetChild(pCfgVDConfig, "CRYPT");
5196
5197 if (pThis->CfgCrypto.pCfgNode)
5198 {
5199 /* Setup VDConfig interface for disk encryption support. */
5200 pThis->VDIfCfg.pfnAreKeysValid = drvvdCfgAreKeysValid;
5201 pThis->VDIfCfg.pfnQuerySize = drvvdCfgQuerySize;
5202 pThis->VDIfCfg.pfnQuery = drvvdCfgQuery;
5203 pThis->VDIfCfg.pfnQueryBytes = NULL;
5204
5205 pThis->VDIfCrypto.pfnKeyRetain = drvvdCryptoKeyRetain;
5206 pThis->VDIfCrypto.pfnKeyRelease = drvvdCryptoKeyRelease;
5207 pThis->VDIfCrypto.pfnKeyStorePasswordRetain = drvvdCryptoKeyStorePasswordRetain;
5208 pThis->VDIfCrypto.pfnKeyStorePasswordRelease = drvvdCryptoKeyStorePasswordRelease;
5209 }
5210 }
5211
5212 /* Unconditionally insert the TCPNET interface, don't bother to check
5213 * if an image really needs it. Will be ignored. Since the TCPNET
5214 * interface is per image we could make this more flexible in the
5215 * future if we want to. */
5216 /* Construct TCPNET callback table depending on the config. This is
5217 * done unconditionally, as uninterested backends will ignore it. */
5218 if (fHostIP)
5219 rc = VDIfTcpNetInstDefaultCreate(&pImage->hVdIfTcpNet, &pImage->pVDIfsImage