VirtualBox

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

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

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

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 207.3 KB
Line 
1/* $Id: DrvVD.cpp 99775 2023-05-12 12:21:58Z vboxsync $ */
2/** @file
3 * DrvVD - Generic VBox disk media driver.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DRV_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 else
4920 rc = VINF_SUCCESS;
4921
4922 if (pszCachePath)
4923 {
4924 rc = pHlp->pfnCFGMQueryStringAlloc(pCurNode, "CacheFormat", &pszCacheFormat);
4925 if (RT_FAILURE(rc))
4926 {
4927 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4928 N_("DrvVD: Configuration error: Querying \"CacheFormat\" as string failed"));
4929 break;
4930 }
4931 }
4932
4933 /* Mountable */
4934 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "Mountable", &pThis->fMountable, false);
4935 if (RT_FAILURE(rc))
4936 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Mountable\" from the config"));
4937
4938 /* Locked */
4939 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "Locked", &pThis->fLocked, false);
4940 if (RT_FAILURE(rc))
4941 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Locked\" from the config"));
4942
4943 /* BIOS visible */
4944 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "BIOSVisible", &pThis->fBiosVisible, true);
4945 if (RT_FAILURE(rc))
4946 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"BIOSVisible\" from the config"));
4947
4948 /* Cylinders */
4949 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "Cylinders", &pThis->LCHSGeometry.cCylinders, 0);
4950 if (RT_FAILURE(rc))
4951 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Cylinders\" from the config"));
4952
4953 /* Heads */
4954 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "Heads", &pThis->LCHSGeometry.cHeads, 0);
4955 if (RT_FAILURE(rc))
4956 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Heads\" from the config"));
4957
4958 /* Sectors */
4959 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "Sectors", &pThis->LCHSGeometry.cSectors, 0);
4960 if (RT_FAILURE(rc))
4961 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Sectors\" from the config"));
4962
4963 /* Uuid */
4964 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "Uuid", &psz);
4965 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
4966 RTUuidClear(&pThis->Uuid);
4967 else if (RT_SUCCESS(rc))
4968 {
4969 rc = RTUuidFromStr(&pThis->Uuid, psz);
4970 if (RT_FAILURE(rc))
4971 {
4972 PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Uuid from string failed on \"%s\""), psz);
4973 PDMDrvHlpMMHeapFree(pDrvIns, psz);
4974 return rc;
4975 }
4976 PDMDrvHlpMMHeapFree(pDrvIns, psz); psz = NULL;
4977 }
4978 else
4979 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Uuid\" from the config"));
4980
4981#ifdef VBOX_PERIODIC_FLUSH
4982 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "FlushInterval", &pThis->cbFlushInterval, 0);
4983 if (RT_FAILURE(rc))
4984 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"FlushInterval\" from the config"));
4985#endif /* VBOX_PERIODIC_FLUSH */
4986
4987#ifdef VBOX_IGNORE_FLUSH
4988 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "IgnoreFlush", &pThis->fIgnoreFlush, true);
4989 if (RT_FAILURE(rc))
4990 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"IgnoreFlush\" from the config"));
4991
4992 if (pThis->fIgnoreFlush)
4993 LogRel(("DrvVD: Flushes will be ignored\n"));
4994 else
4995 LogRel(("DrvVD: Flushes will be passed to the disk\n"));
4996
4997 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "IgnoreFlushAsync", &pThis->fIgnoreFlushAsync, false);
4998 if (RT_FAILURE(rc))
4999 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"IgnoreFlushAsync\" from the config"));
5000
5001 if (pThis->fIgnoreFlushAsync)
5002 LogRel(("DrvVD: Async flushes will be ignored\n"));
5003 else
5004 LogRel(("DrvVD: Async flushes will be passed to the disk\n"));
5005#endif /* VBOX_IGNORE_FLUSH */
5006
5007 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "EmptyDrive", &fEmptyDrive, false);
5008 if (RT_FAILURE(rc))
5009 {
5010 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5011 N_("DrvVD: Configuration error: Querying \"EmptyDrive\" as boolean failed"));
5012 break;
5013 }
5014
5015 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "IoBufMax", &cbIoBufMax, 5 * _1M);
5016 if (RT_FAILURE(rc))
5017 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"IoBufMax\" from the config"));
5018
5019 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "NonRotationalMedium", &pThis->fNonRotational, false);
5020 if (RT_FAILURE(rc))
5021 return PDMDRV_SET_ERROR(pDrvIns, rc,
5022 N_("DrvVD configuration error: Querying \"NonRotationalMedium\" as boolean failed"));
5023 }
5024
5025 PCFGMNODE pParent = pHlp->pfnCFGMGetChild(pCurNode, "Parent");
5026 if (!pParent)
5027 break;
5028 pCurNode = pParent;
5029 iLevel++;
5030 }
5031
5032 if (pThis->pDrvMediaExPort)
5033 rc = IOBUFMgrCreate(&pThis->hIoBufMgr, cbIoBufMax, pThis->CfgCrypto.pCfgNode ? IOBUFMGR_F_REQUIRE_NOT_PAGABLE : IOBUFMGR_F_DEFAULT);
5034
5035 if ( !fEmptyDrive
5036 && RT_SUCCESS(rc))
5037 {
5038 /*
5039 * Create the image container and the necessary interfaces.
5040 */
5041 if (RT_SUCCESS(rc))
5042 {
5043 /*
5044 * The image has a bandwidth group but the host cache is enabled.
5045 * Use the async I/O framework but tell it to enable the host cache.
5046 */
5047 if (!fUseNewIo && pThis->pszBwGroup)
5048 {
5049 pThis->fAsyncIoWithHostCache = true;
5050 fUseNewIo = true;
5051 }
5052
5053 /** @todo quick hack to work around problems in the async I/O
5054 * implementation (rw semaphore thread ownership problem)
5055 * while a merge is running. Remove once this is fixed. */
5056 if (pThis->fMergePending)
5057 fUseNewIo = false;
5058
5059 if (RT_SUCCESS(rc) && pThis->fMergePending)
5060 {
5061 rc = RTSemFastMutexCreate(&pThis->MergeCompleteMutex);
5062 if (RT_SUCCESS(rc))
5063 rc = RTSemRWCreate(&pThis->MergeLock);
5064 if (RT_SUCCESS(rc))
5065 {
5066 pThis->VDIfThreadSync.pfnStartRead = drvvdThreadStartRead;
5067 pThis->VDIfThreadSync.pfnFinishRead = drvvdThreadFinishRead;
5068 pThis->VDIfThreadSync.pfnStartWrite = drvvdThreadStartWrite;
5069 pThis->VDIfThreadSync.pfnFinishWrite = drvvdThreadFinishWrite;
5070
5071 rc = VDInterfaceAdd(&pThis->VDIfThreadSync.Core, "DrvVD_ThreadSync", VDINTERFACETYPE_THREADSYNC,
5072 pThis, sizeof(VDINTERFACETHREADSYNC), &pThis->pVDIfsDisk);
5073 }
5074 else
5075 {
5076 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5077 N_("DrvVD: Failed to create semaphores for \"MergePending\""));
5078 }
5079 }
5080
5081 if (RT_SUCCESS(rc))
5082 {
5083 rc = VDCreate(pThis->pVDIfsDisk, drvvdGetVDFromMediaType(pThis->enmType), &pThis->pDisk);
5084 /* Error message is already set correctly. */
5085 }
5086 }
5087
5088 if (pThis->pDrvMediaExPort && fUseNewIo)
5089 pThis->fAsyncIOSupported = true;
5090
5091 uint64_t tsStart = RTTimeNanoTS();
5092
5093 unsigned iImageIdx = 0;
5094 while (pCurNode && RT_SUCCESS(rc))
5095 {
5096 /* Allocate per-image data. */
5097 PVBOXIMAGE pImage = drvvdNewImage(pThis);
5098 if (!pImage)
5099 {
5100 rc = VERR_NO_MEMORY;
5101 break;
5102 }
5103
5104 /*
5105 * Read the image configuration.
5106 */
5107 rc = pHlp->pfnCFGMQueryStringAlloc(pCurNode, "Path", &pszName);
5108 if (RT_FAILURE(rc))
5109 {
5110 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5111 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
5112 break;
5113 }
5114
5115 rc = pHlp->pfnCFGMQueryStringAlloc(pCurNode, "Format", &pszFormat);
5116 if (RT_FAILURE(rc))
5117 {
5118 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5119 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
5120 break;
5121 }
5122
5123 bool fMergeSource;
5124 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "MergeSource", &fMergeSource, false);
5125 if (RT_FAILURE(rc))
5126 {
5127 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5128 N_("DrvVD: Configuration error: Querying \"MergeSource\" as boolean failed"));
5129 break;
5130 }
5131 if (fMergeSource)
5132 {
5133 if (pThis->uMergeSource == VD_LAST_IMAGE)
5134 pThis->uMergeSource = iImageIdx;
5135 else
5136 {
5137 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
5138 N_("DrvVD: Configuration error: Multiple \"MergeSource\" occurrences"));
5139 break;
5140 }
5141 }
5142
5143 bool fMergeTarget;
5144 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "MergeTarget", &fMergeTarget, false);
5145 if (RT_FAILURE(rc))
5146 {
5147 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5148 N_("DrvVD: Configuration error: Querying \"MergeTarget\" as boolean failed"));
5149 break;
5150 }
5151 if (fMergeTarget)
5152 {
5153 if (pThis->uMergeTarget == VD_LAST_IMAGE)
5154 pThis->uMergeTarget = iImageIdx;
5155 else
5156 {
5157 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
5158 N_("DrvVD: Configuration error: Multiple \"MergeTarget\" occurrences"));
5159 break;
5160 }
5161 }
5162
5163 PCFGMNODE pCfgVDConfig = pHlp->pfnCFGMGetChild(pCurNode, "VDConfig");
5164 pImage->VDIfConfig.pfnAreKeysValid = drvvdCfgAreKeysValid;
5165 pImage->VDIfConfig.pfnQuerySize = drvvdCfgQuerySize;
5166 pImage->VDIfConfig.pfnQuery = drvvdCfgQuery;
5167 pImage->VDIfConfig.pfnQueryBytes = NULL;
5168
5169 PVDCFGNODE pCfgNode = (PVDCFGNODE)RTMemAllocZ(sizeof(*pCfgNode));
5170 if (RT_UNLIKELY(!pCfgNode))
5171 {
5172 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_NO_MEMORY,
5173 N_("DrvVD: Failed to allocate memory for config node"));
5174 break;
5175 }
5176
5177 pCfgNode->pHlp = pDrvIns->pHlpR3;
5178 pCfgNode->pCfgNode = pCfgVDConfig;
5179 RTListAppend(&pThis->LstCfgNodes, &pCfgNode->NdLst);
5180
5181 rc = VDInterfaceAdd(&pImage->VDIfConfig.Core, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
5182 pCfgNode, sizeof(VDINTERFACECONFIG), &pImage->pVDIfsImage);
5183 AssertRC(rc);
5184
5185 /* Check VDConfig for encryption config. */
5186 /** @todo This makes sure that the crypto config is not cleared accidentally
5187 * when it was set because there are multiple VDConfig entries for a snapshot chain
5188 * but only one contains the crypto config.
5189 *
5190 * This needs to be properly fixed by specifying which part of the image should contain the
5191 * crypto stuff.
5192 */
5193 if (!pThis->CfgCrypto.pCfgNode)
5194 {
5195 if (pCfgVDConfig)
5196 pThis->CfgCrypto.pCfgNode = pHlp->pfnCFGMGetChild(pCfgVDConfig, "CRYPT");
5197
5198 if (pThis->CfgCrypto.pCfgNode)
5199 {
5200 /* Setup VDConfig interface for disk encryption support. */
5201 pThis->VDIfCfg.pfnAreKeysValid = drvvdCfgAreKeysValid;
5202 pThis->VDIfCfg.pfnQuerySize = drvvdCfgQuerySize;
5203 pThis->VDIfCfg.pfnQuery = drvvdCfgQuery;
5204 pThis->VDIfCfg.pfnQueryBytes = NULL;
5205
5206 pThis->VDIfCrypto.pfnKeyRetain = drvvdCryptoKeyRetain;
5207 pThis->VDIfCrypto.pfnKeyRelease = drvvdCryptoKeyRelease;
5208 pThis->VDIfCrypto.pfnKeyStorePasswordRetain = drvvdCryptoKeyStorePasswordRetain;
5209 pThis->VDIfCrypto.pfnKeyStorePasswordRelease = drvvdCryptoKeyStorePasswordRelease;
5210 }
5211 }
5212
5213 /* Unconditionally insert the TCPNET interface, don't bother to check
5214 * if an image really needs it. Will be ignored. Since the TCPNET
5215 * interface is per image we could make this more flexible in the
5216 * future if we want to. */
5217 /* Construct TCPNET callback table depending on the config. This is
5218 * done unconditionally, as uninterested backends will ignore it. */
5219 if (fHostIP)
5220 rc = VDIfTcpNetInstDefaultCreate(&pImage->hVdIfTcpNet, &pImage->pVDIfsImage);
5221 else
5222 {
5223#ifndef VBOX_WITH_INIP
5224 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
5225 RT_SRC_POS, N_("DrvVD: Configuration error: TCP over Internal Networking not compiled in"));
5226#else /* VBOX_WITH_INIP */
5227 pImage->VDIfTcpNet.pfnSocketCreate = drvvdINIPSocketCreate;
5228 pImage->VDIfTcpNet.pfnSocketDestroy = drvvdINIPSocketDestroy;
5229 pImage->VDIfTcpNet.pfnClientConnect = drvvdINIPClientConnect;
5230 pImage->VDIfTcpNet.pfnClientClose = drvvdINIPClientClose;
5231 pImage->VDIfTcpNet.pfnIsClientConnected = drvvdINIPIsClientConnected;
5232 pImage->VDIfTcpNet.pfnSelectOne = drvvdINIPSelectOne;
5233 pImage->VDIfTcpNet.pfnRead = drvvdINIPRead;
5234 pImage->VDIfTcpNet.pfnWrite = drvvdINIPWrite;
5235 pImage->VDIfTcpNet.pfnSgWrite = drvvdINIPSgWrite;
5236 pImage->VDIfTcpNet.pfnFlush = drvvdINIPFlush;
5237 pImage->VDIfTcpNet.pfnSetSendCoalescing = drvvdINIPSetSendCoalescing;
5238 pImage->VDIfTcpNet.pfnGetLocalAddress = drvvdINIPGetLocalAddress;
5239 pImage->VDIfTcpNet.pfnGetPeerAddress = drvvdINIPGetPeerAddress;
5240 pImage->VDIfTcpNet.pfnSelectOneEx = drvvdINIPSelectOneEx;
5241 pImage->VDIfTcpNet.pfnPoke = drvvdINIPPoke;
5242
5243 rc = VDInterfaceAdd(&pImage->VDIfTcpNet.Core, "DrvVD_TCPNET",
5244 VDINTERFACETYPE_TCPNET, NULL,
5245 sizeof(VDINTERFACETCPNET), &pImage->pVDIfsImage);
5246 AssertRC(rc);
5247#endif /* VBOX_WITH_INIP */
5248 }
5249
5250 /* Insert the custom I/O interface only if we're told to use new IO.
5251 * Since the I/O interface is per image we could make this more
5252 * flexible in the future if we want to. */
5253 if (fUseNewIo)
5254 {
5255#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
5256 pImage->VDIfIo.pfnOpen = drvvdAsyncIOOpen;
5257 pImage->VDIfIo.pfnClose = drvvdAsyncIOClose;
5258 pImage->VDIfIo.pfnGetSize = drvvdAsyncIOGetSize;
5259 pImage->VDIfIo.pfnSetSize = drvvdAsyncIOSetSize;
5260 pImage->VDIfIo.pfnSetAllocationSize = drvvdAsyncIOSetAllocationSize;
5261 pImage->VDIfIo.pfnReadSync = drvvdAsyncIOReadSync;
5262 pImage->VDIfIo.pfnWriteSync = drvvdAsyncIOWriteSync;
5263 pImage->VDIfIo.pfnFlushSync = drvvdAsyncIOFlushSync;
5264 pImage->VDIfIo.pfnReadAsync = drvvdAsyncIOReadAsync;
5265 pImage->VDIfIo.pfnWriteAsync = drvvdAsyncIOWriteAsync;
5266 pImage->VDIfIo.pfnFlushAsync = drvvdAsyncIOFlushAsync;
5267#else /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
5268 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
5269 RT_SRC_POS, N_("DrvVD: Configuration error: Async Completion Framework not compiled in"));
5270#endif /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
5271 if (RT_SUCCESS(rc))
5272 rc = VDInterfaceAdd(&pImage->VDIfIo.Core, "DrvVD_IO", VDINTERFACETYPE_IO,
5273 pThis, sizeof(VDINTERFACEIO), &pImage->pVDIfsImage);
5274 AssertRC(rc);
5275 }
5276
5277 /*
5278 * Open the image.
5279 */
5280 unsigned uOpenFlags;
5281 if (fReadOnly || pThis->fTempReadOnly || iLevel != 0)
5282 uOpenFlags = VD_OPEN_FLAGS_READONLY;
5283 else
5284 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
5285 if (fHonorZeroWrites)
5286 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
5287 if (pThis->fAsyncIOSupported)
5288 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
5289 if (pThis->fShareable)
5290 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
5291 if (fDiscard && iLevel == 0)
5292 uOpenFlags |= VD_OPEN_FLAGS_DISCARD;
5293 if (fInformAboutZeroBlocks)
5294 uOpenFlags |= VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS;
5295 if ( (uOpenFlags & VD_OPEN_FLAGS_READONLY)
5296 && fSkipConsistencyChecks)
5297 uOpenFlags |= VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS;
5298
5299 /* Try to open backend in async I/O mode first. */
5300 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
5301 if (rc == VERR_NOT_SUPPORTED)
5302 {
5303 pThis->fAsyncIOSupported = false;
5304 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
5305 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
5306 }
5307
5308 if (rc == VERR_VD_DISCARD_NOT_SUPPORTED)
5309 {
5310 fDiscard = false;
5311 uOpenFlags &= ~VD_OPEN_FLAGS_DISCARD;
5312 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
5313 }
5314
5315 if (!fDiscard)
5316 {
5317 pThis->IMedia.pfnDiscard = NULL;
5318 pThis->IMediaEx.pfnIoReqDiscard = NULL;
5319 }
5320
5321 if (RT_SUCCESS(rc))
5322 {
5323 LogFunc(("%d - Opened '%s' in %s mode\n",
5324 iLevel, pszName,
5325 VDIsReadOnly(pThis->pDisk) ? "read-only" : "read-write"));
5326 if ( VDIsReadOnly(pThis->pDisk)
5327 && !fReadOnly
5328 && !fMaybeReadOnly
5329 && !pThis->fTempReadOnly
5330 && iLevel == 0)
5331 {
5332 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_VD_IMAGE_READ_ONLY, RT_SRC_POS,
5333 N_("Failed to open image '%s' for writing due to wrong permissions"),
5334 pszName);
5335 break;
5336 }
5337 }
5338 else
5339 {
5340 rc = PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
5341 N_("Failed to open image '%s' in %s mode"), pszName,
5342 (uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "read-only" : "read-write");
5343 break;
5344 }
5345
5346 PDMDrvHlpMMHeapFree(pDrvIns, pszName);
5347 pszName = NULL;
5348 PDMDrvHlpMMHeapFree(pDrvIns, pszFormat);
5349 pszFormat = NULL;
5350
5351 /* next */
5352 iLevel--;
5353 iImageIdx++;
5354 pCurNode = pHlp->pfnCFGMGetParent(pCurNode);
5355 }
5356
5357 LogRel(("VD: Opening the disk took %lld ns\n", RTTimeNanoTS() - tsStart));
5358
5359 /* Open the cache image if set. */
5360 if ( RT_SUCCESS(rc)
5361 && RT_VALID_PTR(pszCachePath))
5362 {
5363 /* Insert the custom I/O interface only if we're told to use new IO.
5364 * Since the I/O interface is per image we could make this more
5365 * flexible in the future if we want to. */
5366 if (fUseNewIo)
5367 {
5368#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
5369 pThis->VDIfIoCache.pfnOpen = drvvdAsyncIOOpen;
5370 pThis->VDIfIoCache.pfnClose = drvvdAsyncIOClose;
5371 pThis->VDIfIoCache.pfnGetSize = drvvdAsyncIOGetSize;
5372 pThis->VDIfIoCache.pfnSetSize = drvvdAsyncIOSetSize;
5373 pThis->VDIfIoCache.pfnReadSync = drvvdAsyncIOReadSync;
5374 pThis->VDIfIoCache.pfnWriteSync = drvvdAsyncIOWriteSync;
5375 pThis->VDIfIoCache.pfnFlushSync = drvvdAsyncIOFlushSync;
5376 pThis->VDIfIoCache.pfnReadAsync = drvvdAsyncIOReadAsync;
5377 pThis->VDIfIoCache.pfnWriteAsync = drvvdAsyncIOWriteAsync;
5378 pThis->VDIfIoCache.pfnFlushAsync = drvvdAsyncIOFlushAsync;
5379#else /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
5380 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
5381 RT_SRC_POS, N_("DrvVD: Configuration error: Async Completion Framework not compiled in"));
5382#endif /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
5383 if (RT_SUCCESS(rc))
5384 rc = VDInterfaceAdd(&pThis->VDIfIoCache.Core, "DrvVD_IO", VDINTERFACETYPE_IO,
5385 pThis, sizeof(VDINTERFACEIO), &pThis->pVDIfsCache);
5386 AssertRC(rc);
5387 }
5388
5389 rc = VDCacheOpen(pThis->pDisk, pszCacheFormat, pszCachePath, VD_OPEN_FLAGS_NORMAL, pThis->pVDIfsCache);
5390 if (RT_FAILURE(rc))
5391 rc = PDMDRV_SET_ERROR(pDrvIns, rc, N_("DrvVD: Could not open cache image"));
5392 }
5393
5394 if (RT_VALID_PTR(pszCachePath))
5395 PDMDrvHlpMMHeapFree(pDrvIns, pszCachePath);
5396 if (RT_VALID_PTR(pszCacheFormat))
5397 PDMDrvHlpMMHeapFree(pDrvIns, pszCacheFormat);
5398
5399 if ( RT_SUCCESS(rc)
5400 && pThis->fMergePending
5401 && ( pThis->uMergeSource == VD_LAST_IMAGE
5402 || pThis->uMergeTarget == VD_LAST_IMAGE))
5403 {
5404 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
5405 N_("DrvVD: Configuration error: Inconsistent image merge data"));
5406 }
5407
5408 /* Create the block cache if enabled. */
5409 if ( fUseBlockCache
5410 && !pThis->fShareable
5411 && !fDiscard
5412 && !pThis->CfgCrypto.pCfgNode /* Disk encryption disables the block cache for security reasons */
5413 && RT_SUCCESS(rc))
5414 {
5415 /*
5416 * We need a unique ID for the block cache (to identify the owner of data
5417 * blocks in a saved state). UUIDs are not really suitable because
5418 * there are image formats which don't support them. Furthermore it is
5419 * possible that a new diff image was attached after a saved state
5420 * which changes the UUID.
5421 * However the device "name + device instance + LUN" triple the disk is
5422 * attached to is always constant for saved states.
5423 */
5424 char *pszId = NULL;
5425 uint32_t iInstance, iLUN;
5426 const char *pcszController;
5427
5428 rc = pThis->pDrvMediaPort->pfnQueryDeviceLocation(pThis->pDrvMediaPort, &pcszController,
5429 &iInstance, &iLUN);
5430 if (RT_FAILURE(rc))
5431 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
5432 N_("DrvVD: Configuration error: Could not query device data"));
5433 else
5434 {
5435 int cbStr = RTStrAPrintf(&pszId, "%s-%d-%d", pcszController, iInstance, iLUN);
5436
5437 if (cbStr > 0)
5438 {
5439 rc = PDMDrvHlpBlkCacheRetain(pDrvIns, &pThis->pBlkCache,
5440 drvvdBlkCacheXferCompleteIoReq,
5441 drvvdBlkCacheXferEnqueue,
5442 drvvdBlkCacheXferEnqueueDiscard,
5443 pszId);
5444 if (rc == VERR_NOT_SUPPORTED)
5445 {
5446 LogRel(("VD: Block cache is not supported\n"));
5447 rc = VINF_SUCCESS;
5448 }
5449 else
5450 AssertRC(rc);
5451
5452 RTStrFree(pszId);
5453 }
5454 else
5455 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
5456 N_("DrvVD: Out of memory when creating block cache"));
5457 }
5458 }
5459
5460 if (RT_SUCCESS(rc))
5461 rc = drvvdSetupFilters(pThis, pCfg);
5462
5463 /*
5464 * Register a load-done callback so we can undo TempReadOnly config before
5465 * we get to drvvdResume. Automatically deregistered upon destruction.
5466 */
5467 if (RT_SUCCESS(rc))
5468 rc = PDMDrvHlpSSMRegisterEx(pDrvIns, 0 /* version */, 0 /* cbGuess */,
5469 NULL /*pfnLivePrep*/, NULL /*pfnLiveExec*/, NULL /*pfnLiveVote*/,
5470 NULL /*pfnSavePrep*/, NULL /*pfnSaveExec*/, NULL /*pfnSaveDone*/,
5471 NULL /*pfnDonePrep*/, NULL /*pfnLoadExec*/, drvvdLoadDone);
5472
5473 /* Setup the boot acceleration stuff if enabled. */
5474 if (RT_SUCCESS(rc) && pThis->fBootAccelEnabled)
5475 {
5476 pThis->cbDisk = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
5477 Assert(pThis->cbDisk > 0);
5478 pThis->pbData = (uint8_t *)RTMemAllocZ(pThis->cbBootAccelBuffer);
5479 if (pThis->pbData)
5480 {
5481 pThis->fBootAccelActive = true;
5482 pThis->offDisk = 0;
5483 pThis->cbDataValid = 0;
5484 LogRel(("VD: Boot acceleration enabled\n"));
5485 }
5486 else
5487 LogRel(("VD: Boot acceleration, out of memory, disabled\n"));
5488 }
5489
5490 if ( RTUuidIsNull(&pThis->Uuid)
5491 && pThis->enmType == PDMMEDIATYPE_HARD_DISK)
5492 VDGetUuid(pThis->pDisk, 0, &pThis->Uuid);
5493
5494 /*
5495 * Automatically upgrade the floppy drive if the specified one is too
5496 * small to represent the whole boot time image. (We cannot do this later
5497 * since the BIOS (and others) gets the info via CMOS.)
5498 *
5499 * This trick should make 2.88 images as well as the fake 15.6 and 63.5 MB
5500 * images despite the hardcoded default 1.44 drive.
5501 */
5502 if ( PDMMEDIATYPE_IS_FLOPPY(pThis->enmType)
5503 && pThis->pDisk)
5504 {
5505 uint64_t const cbFloppyImg = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
5506 PDMMEDIATYPE const enmCfgType = pThis->enmType;
5507 switch (enmCfgType)
5508 {
5509 default:
5510 AssertFailed();
5511 RT_FALL_THRU();
5512 case PDMMEDIATYPE_FLOPPY_360:
5513 if (cbFloppyImg > 40 * 2 * 9 * 512)
5514 pThis->enmType = PDMMEDIATYPE_FLOPPY_720;
5515 RT_FALL_THRU();
5516 case PDMMEDIATYPE_FLOPPY_720:
5517 if (cbFloppyImg > 80 * 2 * 14 * 512)
5518 pThis->enmType = PDMMEDIATYPE_FLOPPY_1_20;
5519 RT_FALL_THRU();
5520 case PDMMEDIATYPE_FLOPPY_1_20:
5521 if (cbFloppyImg > 80 * 2 * 20 * 512)
5522 pThis->enmType = PDMMEDIATYPE_FLOPPY_1_44;
5523 RT_FALL_THRU();
5524 case PDMMEDIATYPE_FLOPPY_1_44:
5525 if (cbFloppyImg > 80 * 2 * 24 * 512)
5526 pThis->enmType = PDMMEDIATYPE_FLOPPY_2_88;
5527 RT_FALL_THRU();
5528 case PDMMEDIATYPE_FLOPPY_2_88:
5529 if (cbFloppyImg > 80 * 2 * 48 * 512)
5530 pThis->enmType = PDMMEDIATYPE_FLOPPY_FAKE_15_6;
5531 RT_FALL_THRU();
5532 case PDMMEDIATYPE_FLOPPY_FAKE_15_6:
5533 if (cbFloppyImg > 255 * 2 * 63 * 512)
5534 pThis->enmType = PDMMEDIATYPE_FLOPPY_FAKE_63_5;
5535 RT_FALL_THRU();
5536 case PDMMEDIATYPE_FLOPPY_FAKE_63_5:
5537 if (cbFloppyImg > 255 * 2 * 255 * 512)
5538 LogRel(("Warning: Floppy image is larger that 63.5 MB! (%llu bytes)\n", cbFloppyImg));
5539 break;
5540 }
5541 if (pThis->enmType != enmCfgType)
5542 LogRel(("DrvVD: Automatically upgraded floppy drive from %s to %s to better support the %u byte image\n",
5543 drvvdGetTypeName(enmCfgType), drvvdGetTypeName(pThis->enmType), cbFloppyImg));
5544 }
5545 } /* !fEmptyDrive */
5546
5547 if (RT_SUCCESS(rc))
5548 drvvdStatsRegister(pThis);
5549
5550 if (RT_FAILURE(rc))
5551 {
5552 if (RT_VALID_PTR(pszName))
5553 PDMDrvHlpMMHeapFree(pDrvIns, pszName);
5554 if (RT_VALID_PTR(pszFormat))
5555 PDMDrvHlpMMHeapFree(pDrvIns, pszFormat);
5556 /* drvvdDestruct does the rest. */
5557 }
5558
5559 LogFlowFunc(("returns %Rrc\n", rc));
5560 return rc;
5561}
5562
5563/**
5564 * VBox disk container media driver registration record.
5565 */
5566const PDMDRVREG g_DrvVD =
5567{
5568 /* u32Version */
5569 PDM_DRVREG_VERSION,
5570 /* szName */
5571 "VD",
5572 /* szRCMod */
5573 "",
5574 /* szR0Mod */
5575 "",
5576 /* pszDescription */
5577 "Generic VBox disk media driver.",
5578 /* fFlags */
5579 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
5580 /* fClass. */
5581 PDM_DRVREG_CLASS_MEDIA,
5582 /* cMaxInstances */
5583 ~0U,
5584 /* cbInstance */
5585 sizeof(VBOXDISK),
5586 /* pfnConstruct */
5587 drvvdConstruct,
5588 /* pfnDestruct */
5589 drvvdDestruct,
5590 /* pfnRelocate */
5591 NULL,
5592 /* pfnIOCtl */
5593 NULL,
5594 /* pfnPowerOn */
5595 drvvdPowerOn,
5596 /* pfnReset */
5597 drvvdReset,
5598 /* pfnSuspend */
5599 drvvdSuspend,
5600 /* pfnResume */
5601 drvvdResume,
5602 /* pfnAttach */
5603 NULL,
5604 /* pfnDetach */
5605 NULL,
5606 /* pfnPowerOff */
5607 drvvdPowerOff,
5608 /* pfnSoftReset */
5609 NULL,
5610 /* u32EndVersion */
5611 PDM_DRVREG_VERSION
5612};
5613
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette