VirtualBox

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

Last change on this file since 99122 was 98271, checked in by vboxsync, 2 years ago

Devices/Storage: Suspend the VM when the file handle became stale (often due to a restarted MFS server), bugref:9811

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 207.4 KB
Line 
1/* $Id: DrvVD.cpp 98271 2023-01-24 10:08:42Z 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 */
2333bool 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 * @returns VBox status code.
2479 * @param pThis VBox disk container instance data.
2480 * @param pIoReq I/O request to complete.
2481 * @param rcReq The status code the request completed with.
2482 * @param fUpNotify Flag whether to notify the driver/device above us about the completion.
2483 */
2484static void drvvdMediaExIoReqRetire(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, int rcReq, bool fUpNotify)
2485{
2486 LogFlowFunc(("pThis=%#p pIoReq=%#p rcReq=%Rrc fUpNotify=%RTbool\n",
2487 pThis, pIoReq, rcReq, fUpNotify));
2488
2489 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_COMPLETING, VDIOREQSTATE_ACTIVE);
2490 if (fXchg)
2491 {
2492 uint32_t cNew = ASMAtomicDecU32(&pThis->cIoReqsActive);
2493 AssertMsg(cNew != UINT32_MAX, ("Number of active requests underflowed!\n")); RT_NOREF(cNew);
2494 }
2495 else
2496 {
2497 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
2498 rcReq = VERR_PDM_MEDIAEX_IOREQ_CANCELED;
2499 }
2500
2501 ASMAtomicXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_COMPLETED);
2502 drvvdMediaExIoReqBufFree(pThis, pIoReq);
2503
2504 /*
2505 * Leave a release log entry if the request was active for more than 25 seconds
2506 * (30 seconds is the timeout of the guest).
2507 */
2508 uint64_t tsNow = RTTimeMilliTS();
2509 if (tsNow - pIoReq->tsSubmit >= 25 * 1000)
2510 {
2511 const char *pcszReq = NULL;
2512
2513 switch (pIoReq->enmType)
2514 {
2515 case PDMMEDIAEXIOREQTYPE_READ:
2516 pcszReq = "Read";
2517 break;
2518 case PDMMEDIAEXIOREQTYPE_WRITE:
2519 pcszReq = "Write";
2520 break;
2521 case PDMMEDIAEXIOREQTYPE_FLUSH:
2522 pcszReq = "Flush";
2523 break;
2524 case PDMMEDIAEXIOREQTYPE_DISCARD:
2525 pcszReq = "Discard";
2526 break;
2527 default:
2528 pcszReq = "<Invalid>";
2529 }
2530
2531 LogRel(("VD#%u: %s request was active for %llu seconds\n",
2532 pThis->pDrvIns->iInstance, pcszReq, (tsNow - pIoReq->tsSubmit) / 1000));
2533 }
2534
2535 if (RT_FAILURE(rcReq))
2536 {
2537 /* Log the error. */
2538 if (pThis->cErrors++ < DRVVD_MAX_LOG_REL_ERRORS)
2539 {
2540 if (rcReq == VERR_PDM_MEDIAEX_IOREQ_CANCELED)
2541 {
2542 if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_FLUSH)
2543 LogRel(("VD#%u: Aborted flush returned rc=%Rrc\n",
2544 pThis->pDrvIns->iInstance, rcReq));
2545 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD)
2546 LogRel(("VD#%u: Aborted discard returned rc=%Rrc\n",
2547 pThis->pDrvIns->iInstance, rcReq));
2548 else
2549 LogRel(("VD#%u: Aborted %s (%u bytes left) returned rc=%Rrc\n",
2550 pThis->pDrvIns->iInstance,
2551 pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
2552 ? "read"
2553 : "write",
2554 pIoReq->ReadWrite.cbReqLeft, rcReq));
2555 }
2556 else
2557 {
2558 if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_FLUSH)
2559 LogRel(("VD#%u: Flush returned rc=%Rrc\n",
2560 pThis->pDrvIns->iInstance, rcReq));
2561 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD)
2562 LogRel(("VD#%u: Discard returned rc=%Rrc\n",
2563 pThis->pDrvIns->iInstance, rcReq));
2564 else
2565 LogRel(("VD#%u: %s (%u bytes left) returned rc=%Rrc\n",
2566 pThis->pDrvIns->iInstance,
2567 pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
2568 ? "Read"
2569 : "Write",
2570 pIoReq->ReadWrite.cbReqLeft, rcReq));
2571 }
2572 }
2573
2574 STAM_REL_COUNTER_INC(&pThis->StatReqsFailed);
2575 }
2576 else
2577 {
2578 STAM_REL_COUNTER_INC(&pThis->StatReqsSucceeded);
2579
2580 switch (pIoReq->enmType)
2581 {
2582 case PDMMEDIAEXIOREQTYPE_READ:
2583 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, pIoReq->ReadWrite.cbReq);
2584 break;
2585 case PDMMEDIAEXIOREQTYPE_WRITE:
2586 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, pIoReq->ReadWrite.cbReq);
2587 break;
2588 default:
2589 break;
2590 }
2591 }
2592
2593 if (fUpNotify)
2594 {
2595 int rc = pThis->pDrvMediaExPort->pfnIoReqCompleteNotify(pThis->pDrvMediaExPort,
2596 pIoReq, &pIoReq->abAlloc[0], rcReq);
2597 AssertRC(rc);
2598 }
2599
2600 LogFlowFunc(("returns\n"));
2601}
2602
2603/**
2604 * I/O request completion worker.
2605 *
2606 * @returns VBox status code.
2607 * @param pThis VBox disk container instance data.
2608 * @param pIoReq I/O request to complete.
2609 * @param rcReq The status code the request completed with.
2610 * @param fUpNotify Flag whether to notify the driver/device above us about the completion.
2611 */
2612static int drvvdMediaExIoReqCompleteWorker(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, int rcReq, bool fUpNotify)
2613{
2614 LogFlowFunc(("pThis=%#p pIoReq=%#p rcReq=%Rrc fUpNotify=%RTbool\n",
2615 pThis, pIoReq, rcReq, fUpNotify));
2616
2617 /*
2618 * For a read we need to sync the memory before continuing to process
2619 * the request further.
2620 */
2621 if ( RT_SUCCESS(rcReq)
2622 && pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ)
2623 rcReq = drvvdMediaExIoReqBufSync(pThis, pIoReq, false /* fToIoBuf */);
2624
2625 /*
2626 * When the request owner instructs us to handle recoverable errors like full disks
2627 * do it. Mark the request as suspended, notify the owner and put the request on the
2628 * redo list.
2629 */
2630 if ( RT_FAILURE(rcReq)
2631 && (pIoReq->fFlags & PDMIMEDIAEX_F_SUSPEND_ON_RECOVERABLE_ERR)
2632 && drvvdMediaExIoReqIsRedoSetWarning(pThis, rcReq))
2633 {
2634 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_SUSPENDED, VDIOREQSTATE_ACTIVE);
2635 if (fXchg)
2636 {
2637 /* Put on redo list and adjust active request counter. */
2638 RTCritSectEnter(&pThis->CritSectIoReqRedo);
2639 RTListAppend(&pThis->LstIoReqRedo, &pIoReq->NdLstWait);
2640 RTCritSectLeave(&pThis->CritSectIoReqRedo);
2641 uint32_t cNew = ASMAtomicDecU32(&pThis->cIoReqsActive);
2642 AssertMsg(cNew != UINT32_MAX, ("Number of active requests underflowed!\n")); RT_NOREF(cNew);
2643 pThis->pDrvMediaExPort->pfnIoReqStateChanged(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
2644 PDMMEDIAEXIOREQSTATE_SUSPENDED);
2645 LogFlowFunc(("Suspended I/O request %#p\n", pIoReq));
2646 rcReq = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
2647 }
2648 else
2649 {
2650 /* Request was canceled inbetween, so don't care and notify the owner about the completed request. */
2651 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
2652 drvvdMediaExIoReqRetire(pThis, pIoReq, rcReq, fUpNotify);
2653 }
2654 }
2655 else
2656 {
2657 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
2658 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
2659 {
2660 /* Adjust the remaining amount to transfer. */
2661 Assert(pIoReq->ReadWrite.cbIoBuf > 0 || rcReq == VERR_PDM_MEDIAEX_IOREQ_CANCELED);
2662
2663 size_t cbReqIo = RT_MIN(pIoReq->ReadWrite.cbReqLeft, pIoReq->ReadWrite.cbIoBuf);
2664 pIoReq->ReadWrite.offStart += cbReqIo;
2665 pIoReq->ReadWrite.cbReqLeft -= cbReqIo;
2666 }
2667
2668 if ( RT_FAILURE(rcReq)
2669 || !pIoReq->ReadWrite.cbReqLeft
2670 || ( pIoReq->enmType != PDMMEDIAEXIOREQTYPE_READ
2671 && pIoReq->enmType != PDMMEDIAEXIOREQTYPE_WRITE))
2672 drvvdMediaExIoReqRetire(pThis, pIoReq, rcReq, fUpNotify);
2673 else
2674 drvvdMediaExIoReqReadWriteProcess(pThis, pIoReq, fUpNotify);
2675 }
2676
2677 LogFlowFunc(("returns %Rrc\n", rcReq));
2678 return rcReq;
2679}
2680
2681
2682/**
2683 * Allocates a memory buffer suitable for I/O for the given request.
2684 *
2685 * @returns VBox status code.
2686 * @retval VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS if there is no I/O memory available to allocate and
2687 * the request was placed on a waiting list.
2688 * @param pThis VBox disk container instance data.
2689 * @param pIoReq I/O request to allocate memory for.
2690 * @param cb Size of the buffer.
2691 */
2692DECLINLINE(int) drvvdMediaExIoReqBufAlloc(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, size_t cb)
2693{
2694 int rc = VERR_NOT_SUPPORTED;
2695 LogFlowFunc(("pThis=%#p pIoReq=%#p cb=%zu\n", pThis, pIoReq, cb));
2696
2697/** @todo This does not work at all with encryption enabled because the encryption plugin
2698 * encrypts the data in place trashing guest memory and causing data corruption later on!
2699 *
2700 * DO NOT ENABLE UNLESS YOU WANT YOUR DATA SHREDDED!!!
2701 */
2702#if 0
2703 if ( cb == _4K
2704 && pThis->pDrvMediaExPort->pfnIoReqQueryBuf)
2705 {
2706 /* Try to get a direct pointer to the buffer first. */
2707 void *pvBuf = NULL;
2708 size_t cbBuf = 0;
2709
2710 STAM_COUNTER_INC(&pThis->StatQueryBufAttempts);
2711 rc = pThis->pDrvMediaExPort->pfnIoReqQueryBuf(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
2712 &pvBuf, &cbBuf);
2713 if (RT_SUCCESS(rc))
2714 {
2715 STAM_COUNTER_INC(&pThis->StatQueryBufSuccess);
2716 pIoReq->ReadWrite.cbIoBuf = cbBuf;
2717 pIoReq->ReadWrite.fDirectBuf = true;
2718 pIoReq->ReadWrite.Direct.Seg.pvSeg = pvBuf;
2719 pIoReq->ReadWrite.Direct.Seg.cbSeg = cbBuf;
2720 RTSgBufInit(&pIoReq->ReadWrite.Direct.SgBuf, &pIoReq->ReadWrite.Direct.Seg, 1);
2721 pIoReq->ReadWrite.pSgBuf = &pIoReq->ReadWrite.Direct.SgBuf;
2722 }
2723 }
2724#endif
2725
2726 if (RT_FAILURE(rc))
2727 {
2728 rc = IOBUFMgrAllocBuf(pThis->hIoBufMgr, &pIoReq->ReadWrite.IoBuf, cb, &pIoReq->ReadWrite.cbIoBuf);
2729 if (rc == VERR_NO_MEMORY)
2730 {
2731 LogFlowFunc(("Could not allocate memory for request, deferring\n"));
2732 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
2733 RTListAppend(&pThis->LstIoReqIoBufWait, &pIoReq->NdLstWait);
2734 ASMAtomicIncU32(&pThis->cIoReqsWaiting);
2735 if (ASMAtomicReadBool(&pThis->fSuspending))
2736 pThis->pDrvMediaExPort->pfnIoReqStateChanged(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
2737 PDMMEDIAEXIOREQSTATE_SUSPENDED);
2738 LogFlowFunc(("Suspended I/O request %#p\n", pIoReq));
2739 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
2740 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
2741 }
2742 else
2743 {
2744 LogFlowFunc(("Allocated %zu bytes of memory\n", pIoReq->ReadWrite.cbIoBuf));
2745 Assert(pIoReq->ReadWrite.cbIoBuf > 0);
2746 pIoReq->ReadWrite.fDirectBuf = false;
2747 pIoReq->ReadWrite.pSgBuf = &pIoReq->ReadWrite.IoBuf.SgBuf;
2748 }
2749 }
2750
2751 LogFlowFunc(("returns %Rrc\n", rc));
2752 return rc;
2753}
2754
2755/**
2756 * Wrapper around the various ways to read from the underlying medium (cache, async vs. sync).
2757 *
2758 * @returns VBox status code.
2759 * @param pThis VBox disk container instance data.
2760 * @param pIoReq I/O request to process.
2761 * @param cbReqIo Transfer size.
2762 * @param pcbReqIo Where to store the amount of transferred data.
2763 */
2764static int drvvdMediaExIoReqReadWrapper(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, size_t cbReqIo, size_t *pcbReqIo)
2765{
2766 int rc = VINF_SUCCESS;
2767
2768 LogFlowFunc(("pThis=%#p pIoReq=%#p cbReqIo=%zu pcbReqIo=%#p\n", pThis, pIoReq, cbReqIo, pcbReqIo));
2769
2770 Assert(cbReqIo > 0);
2771
2772 if ( pThis->fAsyncIOSupported
2773 && !(pIoReq->fFlags & PDMIMEDIAEX_F_SYNC))
2774 {
2775 if (pThis->pBlkCache)
2776 {
2777 rc = PDMDrvHlpBlkCacheRead(pThis->pDrvIns, pThis->pBlkCache, pIoReq->ReadWrite.offStart,
2778 pIoReq->ReadWrite.pSgBuf, cbReqIo, pIoReq);
2779 if (rc == VINF_SUCCESS)
2780 rc = VINF_VD_ASYNC_IO_FINISHED;
2781 else if (rc == VINF_AIO_TASK_PENDING)
2782 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2783 }
2784 else
2785 rc = VDAsyncRead(pThis->pDisk, pIoReq->ReadWrite.offStart, cbReqIo, pIoReq->ReadWrite.pSgBuf,
2786 drvvdMediaExIoReqComplete, pThis, pIoReq);
2787 }
2788 else
2789 {
2790 void *pvBuf = RTSgBufGetNextSegment(pIoReq->ReadWrite.pSgBuf, &cbReqIo);
2791
2792 Assert(cbReqIo > 0 && RT_VALID_PTR(pvBuf));
2793 rc = VDRead(pThis->pDisk, pIoReq->ReadWrite.offStart, pvBuf, cbReqIo);
2794 if (RT_SUCCESS(rc))
2795 rc = VINF_VD_ASYNC_IO_FINISHED;
2796 }
2797
2798 *pcbReqIo = cbReqIo;
2799
2800 LogFlowFunc(("returns %Rrc *pcbReqIo=%zu\n", rc, *pcbReqIo));
2801 return rc;
2802}
2803
2804/**
2805 * Wrapper around the various ways to write to the underlying medium (cache, async vs. sync).
2806 *
2807 * @returns VBox status code.
2808 * @param pThis VBox disk container instance data.
2809 * @param pIoReq I/O request to process.
2810 * @param cbReqIo Transfer size.
2811 * @param pcbReqIo Where to store the amount of transferred data.
2812 */
2813static int drvvdMediaExIoReqWriteWrapper(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, size_t cbReqIo, size_t *pcbReqIo)
2814{
2815 int rc = VINF_SUCCESS;
2816
2817 Assert(cbReqIo > 0);
2818
2819 LogFlowFunc(("pThis=%#p pIoReq=%#p cbReqIo=%zu pcbReqIo=%#p\n", pThis, pIoReq, cbReqIo, pcbReqIo));
2820
2821 if ( pThis->fAsyncIOSupported
2822 && !(pIoReq->fFlags & PDMIMEDIAEX_F_SYNC))
2823 {
2824 if (pThis->pBlkCache)
2825 {
2826 rc = PDMDrvHlpBlkCacheWrite(pThis->pDrvIns, pThis->pBlkCache, pIoReq->ReadWrite.offStart,
2827 pIoReq->ReadWrite.pSgBuf, cbReqIo, pIoReq);
2828 if (rc == VINF_SUCCESS)
2829 rc = VINF_VD_ASYNC_IO_FINISHED;
2830 else if (rc == VINF_AIO_TASK_PENDING)
2831 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2832 }
2833 else
2834 rc = VDAsyncWrite(pThis->pDisk, pIoReq->ReadWrite.offStart, cbReqIo, pIoReq->ReadWrite.pSgBuf,
2835 drvvdMediaExIoReqComplete, pThis, pIoReq);
2836 }
2837 else
2838 {
2839 void *pvBuf = RTSgBufGetNextSegment(pIoReq->ReadWrite.pSgBuf, &cbReqIo);
2840
2841 Assert(cbReqIo > 0 && RT_VALID_PTR(pvBuf));
2842 rc = VDWrite(pThis->pDisk, pIoReq->ReadWrite.offStart, pvBuf, cbReqIo);
2843 if (RT_SUCCESS(rc))
2844 rc = VINF_VD_ASYNC_IO_FINISHED;
2845
2846#ifdef VBOX_PERIODIC_FLUSH
2847 if (pThis->cbFlushInterval)
2848 {
2849 pThis->cbDataWritten += (uint32_t)cbReqIo;
2850 if (pThis->cbDataWritten > pThis->cbFlushInterval)
2851 {
2852 pThis->cbDataWritten = 0;
2853 VDFlush(pThis->pDisk);
2854 }
2855 }
2856#endif /* VBOX_PERIODIC_FLUSH */
2857 }
2858
2859 *pcbReqIo = cbReqIo;
2860
2861 LogFlowFunc(("returns %Rrc *pcbReqIo=%zu\n", rc, *pcbReqIo));
2862 return rc;
2863}
2864
2865/**
2866 * Wrapper around the various ways to flush all data to the underlying medium (cache, async vs. sync).
2867 *
2868 * @returns VBox status code.
2869 * @param pThis VBox disk container instance data.
2870 * @param pIoReq I/O request to process.
2871 */
2872static int drvvdMediaExIoReqFlushWrapper(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
2873{
2874 int rc = VINF_SUCCESS;
2875
2876 LogFlowFunc(("pThis=%#p pIoReq=%#p\n", pThis, pIoReq));
2877
2878 if ( pThis->fAsyncIOSupported
2879 && !(pIoReq->fFlags & PDMIMEDIAEX_F_SYNC))
2880 {
2881#ifdef VBOX_IGNORE_FLUSH
2882 if (pThis->fIgnoreFlushAsync)
2883 rc = VINF_VD_ASYNC_IO_FINISHED;
2884 else
2885#endif /* VBOX_IGNORE_FLUSH */
2886 {
2887 if (pThis->pBlkCache)
2888 {
2889 rc = PDMDrvHlpBlkCacheFlush(pThis->pDrvIns, pThis->pBlkCache, pIoReq);
2890 if (rc == VINF_SUCCESS)
2891 rc = VINF_VD_ASYNC_IO_FINISHED;
2892 else if (rc == VINF_AIO_TASK_PENDING)
2893 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2894 }
2895 else
2896 rc = VDAsyncFlush(pThis->pDisk, drvvdMediaExIoReqComplete, pThis, pIoReq);
2897 }
2898 }
2899 else
2900 {
2901#ifdef VBOX_IGNORE_FLUSH
2902 if (pThis->fIgnoreFlush)
2903 rc = VINF_VD_ASYNC_IO_FINISHED;
2904 else
2905#endif /* VBOX_IGNORE_FLUSH */
2906 {
2907 rc = VDFlush(pThis->pDisk);
2908 if (RT_SUCCESS(rc))
2909 rc = VINF_VD_ASYNC_IO_FINISHED;
2910 }
2911 }
2912
2913 LogFlowFunc(("returns %Rrc\n", rc));
2914 return rc;
2915}
2916
2917/**
2918 * Wrapper around the various ways to discard data blocks on the underlying medium (cache, async vs. sync).
2919 *
2920 * @returns VBox status code.
2921 * @param pThis VBox disk container instance data.
2922 * @param pIoReq I/O request to process.
2923 */
2924static int drvvdMediaExIoReqDiscardWrapper(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
2925{
2926 int rc = VINF_SUCCESS;
2927
2928 LogFlowFunc(("pThis=%#p pIoReq=%#p\n", pThis, pIoReq));
2929
2930 if ( pThis->fAsyncIOSupported
2931 && !(pIoReq->fFlags & PDMIMEDIAEX_F_SYNC))
2932 {
2933 if (pThis->pBlkCache)
2934 {
2935 rc = PDMDrvHlpBlkCacheDiscard(pThis->pDrvIns, pThis->pBlkCache,
2936 pIoReq->Discard.paRanges, pIoReq->Discard.cRanges,
2937 pIoReq);
2938 if (rc == VINF_SUCCESS)
2939 rc = VINF_VD_ASYNC_IO_FINISHED;
2940 else if (rc == VINF_AIO_TASK_PENDING)
2941 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2942 }
2943 else
2944 rc = VDAsyncDiscardRanges(pThis->pDisk, pIoReq->Discard.paRanges, pIoReq->Discard.cRanges,
2945 drvvdMediaExIoReqComplete, pThis, pIoReq);
2946 }
2947 else
2948 {
2949 rc = VDDiscardRanges(pThis->pDisk, pIoReq->Discard.paRanges, pIoReq->Discard.cRanges);
2950 if (RT_SUCCESS(rc))
2951 rc = VINF_VD_ASYNC_IO_FINISHED;
2952 }
2953
2954 LogFlowFunc(("returns %Rrc\n", rc));
2955 return rc;
2956}
2957
2958/**
2959 * Processes a read/write request.
2960 *
2961 * @returns VBox status code.
2962 * @param pThis VBox disk container instance data.
2963 * @param pIoReq I/O request to process.
2964 * @param fUpNotify Flag whether to notify the driver/device above us about the completion.
2965 */
2966static int drvvdMediaExIoReqReadWriteProcess(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq, bool fUpNotify)
2967{
2968 int rc = VINF_SUCCESS;
2969
2970 LogFlowFunc(("pThis=%#p pIoReq=%#p fUpNotify=%RTbool\n", pThis, pIoReq, fUpNotify));
2971
2972 Assert(pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE);
2973
2974 rc = drvvdKeyCheckPrereqs(pThis, false /* fSetError */);
2975
2976 while ( pIoReq->ReadWrite.cbReqLeft
2977 && rc == VINF_SUCCESS)
2978 {
2979 Assert(pIoReq->ReadWrite.cbIoBuf > 0);
2980
2981 size_t cbReqIo = RT_MIN(pIoReq->ReadWrite.cbReqLeft, pIoReq->ReadWrite.cbIoBuf);
2982
2983 if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ)
2984 rc = drvvdMediaExIoReqReadWrapper(pThis, pIoReq, cbReqIo, &cbReqIo);
2985 else
2986 {
2987 /* Sync memory buffer from the request initiator. */
2988 rc = drvvdMediaExIoReqBufSync(pThis, pIoReq, true /* fToIoBuf */);
2989 if (RT_SUCCESS(rc))
2990 rc = drvvdMediaExIoReqWriteWrapper(pThis, pIoReq, cbReqIo, &cbReqIo);
2991 }
2992
2993 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
2994 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
2995 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
2996 {
2997 /*
2998 * Don't sync the buffer or update the I/O state for the last chunk as it is done
2999 * already in the completion worker called below.
3000 */
3001 if (cbReqIo < pIoReq->ReadWrite.cbReqLeft)
3002 {
3003 if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ)
3004 rc = drvvdMediaExIoReqBufSync(pThis, pIoReq, false /* fToIoBuf */);
3005 else
3006 rc = VINF_SUCCESS;
3007 pIoReq->ReadWrite.offStart += cbReqIo;
3008 pIoReq->ReadWrite.cbReqLeft -= cbReqIo;
3009 }
3010 else
3011 {
3012 rc = VINF_SUCCESS;
3013 break;
3014 }
3015 }
3016 }
3017
3018 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
3019 rc = drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rc, fUpNotify);
3020
3021 LogFlowFunc(("returns %Rrc\n", rc));
3022 return rc;
3023}
3024
3025
3026/**
3027 * Tries to process any requests waiting for available I/O memory.
3028 *
3029 * @returns nothing.
3030 * @param pThis VBox disk container instance data.
3031 */
3032static void drvvdMediaExIoReqProcessWaiting(PVBOXDISK pThis)
3033{
3034 uint32_t cIoReqsWaiting = ASMAtomicXchgU32(&pThis->cIoReqsWaiting, 0);
3035 if (cIoReqsWaiting > 0)
3036 {
3037 RTLISTANCHOR LstIoReqProcess;
3038 RTLISTANCHOR LstIoReqCanceled;
3039 RTListInit(&LstIoReqProcess);
3040 RTListInit(&LstIoReqCanceled);
3041
3042 /* Try to process as many requests as possible. */
3043 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
3044 PPDMMEDIAEXIOREQINT pIoReqCur, pIoReqNext;
3045
3046 RTListForEachSafe(&pThis->LstIoReqIoBufWait, pIoReqCur, pIoReqNext, PDMMEDIAEXIOREQINT, NdLstWait)
3047 {
3048 LogFlowFunc(("Found I/O request %#p on waiting list, trying to allocate buffer of size %zu bytes\n",
3049 pIoReqCur, pIoReqCur->ReadWrite.cbReq));
3050
3051 /* Allocate a suitable I/O buffer for this request. */
3052 int rc = IOBUFMgrAllocBuf(pThis->hIoBufMgr, &pIoReqCur->ReadWrite.IoBuf, pIoReqCur->ReadWrite.cbReq,
3053 &pIoReqCur->ReadWrite.cbIoBuf);
3054 if (rc == VINF_SUCCESS)
3055 {
3056 Assert(pIoReqCur->ReadWrite.cbIoBuf > 0);
3057
3058 cIoReqsWaiting--;
3059 RTListNodeRemove(&pIoReqCur->NdLstWait);
3060
3061 pIoReqCur->ReadWrite.fDirectBuf = false;
3062 pIoReqCur->ReadWrite.pSgBuf = &pIoReqCur->ReadWrite.IoBuf.SgBuf;
3063
3064 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReqCur->enmState,
3065 VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
3066 if (RT_UNLIKELY(!fXchg))
3067 {
3068 /* Must have been canceled inbetween. */
3069 Assert(pIoReqCur->enmState == VDIOREQSTATE_CANCELED);
3070
3071 /* Free the buffer here already again to let other requests get a chance to allocate the memory. */
3072 IOBUFMgrFreeBuf(&pIoReqCur->ReadWrite.IoBuf);
3073 pIoReqCur->ReadWrite.cbIoBuf = 0;
3074 RTListAppend(&LstIoReqCanceled, &pIoReqCur->NdLstWait);
3075 }
3076 else
3077 {
3078 ASMAtomicIncU32(&pThis->cIoReqsActive);
3079 RTListAppend(&LstIoReqProcess, &pIoReqCur->NdLstWait);
3080 }
3081 }
3082 else
3083 {
3084 Assert(rc == VERR_NO_MEMORY);
3085 break;
3086 }
3087 }
3088 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
3089
3090 ASMAtomicAddU32(&pThis->cIoReqsWaiting, cIoReqsWaiting);
3091
3092 /* Process the requests we could allocate memory for and the ones which got canceled outside the lock now. */
3093 RTListForEachSafe(&LstIoReqCanceled, pIoReqCur, pIoReqNext, PDMMEDIAEXIOREQINT, NdLstWait)
3094 {
3095 RTListNodeRemove(&pIoReqCur->NdLstWait);
3096 drvvdMediaExIoReqCompleteWorker(pThis, pIoReqCur, VERR_PDM_MEDIAEX_IOREQ_CANCELED, true /* fUpNotify */);
3097 }
3098
3099 RTListForEachSafe(&LstIoReqProcess, pIoReqCur, pIoReqNext, PDMMEDIAEXIOREQINT, NdLstWait)
3100 {
3101 RTListNodeRemove(&pIoReqCur->NdLstWait);
3102 drvvdMediaExIoReqReadWriteProcess(pThis, pIoReqCur, true /* fUpNotify */);
3103 }
3104 }
3105}
3106
3107/**
3108 * Frees a I/O memory buffer allocated previously.
3109 *
3110 * @returns nothing.
3111 * @param pThis VBox disk container instance data.
3112 * @param pIoReq I/O request for which to free memory.
3113 */
3114DECLINLINE(void) drvvdMediaExIoReqBufFree(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
3115{
3116 LogFlowFunc(("pThis=%#p pIoReq=%#p{.cbIoBuf=%zu}\n", pThis, pIoReq, pIoReq->ReadWrite.cbIoBuf));
3117
3118 if ( ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
3119 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
3120 && !pIoReq->ReadWrite.fDirectBuf
3121 && pIoReq->ReadWrite.cbIoBuf > 0)
3122 {
3123 IOBUFMgrFreeBuf(&pIoReq->ReadWrite.IoBuf);
3124
3125 if (!ASMAtomicReadBool(&pThis->fSuspending))
3126 drvvdMediaExIoReqProcessWaiting(pThis);
3127 }
3128
3129 LogFlowFunc(("returns\n"));
3130}
3131
3132
3133/**
3134 * Returns a string description of the given request state.
3135 *
3136 * @returns Pointer to the stringified state.
3137 * @param enmState The state.
3138 */
3139DECLINLINE(const char *) drvvdMediaExIoReqStateStringify(VDIOREQSTATE enmState)
3140{
3141#define STATE2STR(a_State) case VDIOREQSTATE_##a_State: return #a_State
3142 switch (enmState)
3143 {
3144 STATE2STR(INVALID);
3145 STATE2STR(FREE);
3146 STATE2STR(ALLOCATED);
3147 STATE2STR(ACTIVE);
3148 STATE2STR(SUSPENDED);
3149 STATE2STR(COMPLETING);
3150 STATE2STR(COMPLETED);
3151 STATE2STR(CANCELED);
3152 default:
3153 AssertMsgFailed(("Unknown state %u\n", enmState));
3154 return "UNKNOWN";
3155 }
3156#undef STATE2STR
3157}
3158
3159
3160/**
3161 * Returns a string description of the given request type.
3162 *
3163 * @returns Pointer to the stringified type.
3164 * @param enmType The request type.
3165 */
3166DECLINLINE(const char *) drvvdMediaExIoReqTypeStringify(PDMMEDIAEXIOREQTYPE enmType)
3167{
3168#define TYPE2STR(a_Type) case PDMMEDIAEXIOREQTYPE_##a_Type: return #a_Type
3169 switch (enmType)
3170 {
3171 TYPE2STR(INVALID);
3172 TYPE2STR(FLUSH);
3173 TYPE2STR(WRITE);
3174 TYPE2STR(READ);
3175 TYPE2STR(DISCARD);
3176 TYPE2STR(SCSI);
3177 default:
3178 AssertMsgFailed(("Unknown type %u\n", enmType));
3179 return "UNKNOWN";
3180 }
3181#undef TYPE2STR
3182}
3183
3184
3185/**
3186 * Dumps the interesting bits about the given I/O request to the release log.
3187 *
3188 * @returns nothing.
3189 * @param pThis VBox disk container instance data.
3190 * @param pIoReq The I/O request to dump.
3191 */
3192static void drvvdMediaExIoReqLogRel(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
3193{
3194 uint64_t offStart = 0;
3195 size_t cbReq = 0;
3196 size_t cbLeft = 0;
3197 size_t cbBufSize = 0;
3198 uint64_t tsActive = RTTimeMilliTS() - pIoReq->tsSubmit;
3199
3200 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
3201 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
3202 {
3203 offStart = pIoReq->ReadWrite.offStart;
3204 cbReq = pIoReq->ReadWrite.cbReq;
3205 cbLeft = pIoReq->ReadWrite.cbReqLeft;
3206 cbBufSize = pIoReq->ReadWrite.cbIoBuf;
3207 }
3208
3209 LogRel(("VD#%u: Request{%#p}:\n"
3210 " Type=%s State=%s Id=%#llx SubmitTs=%llu {%llu} Flags=%#x\n"
3211 " Offset=%llu Size=%zu Left=%zu BufSize=%zu\n",
3212 pThis->pDrvIns->iInstance, pIoReq,
3213 drvvdMediaExIoReqTypeStringify(pIoReq->enmType),
3214 drvvdMediaExIoReqStateStringify(pIoReq->enmState),
3215 pIoReq->uIoReqId, pIoReq->tsSubmit, tsActive, pIoReq->fFlags,
3216 offStart, cbReq, cbLeft, cbBufSize));
3217}
3218
3219
3220/**
3221 * Returns whether the VM is in a running state.
3222 *
3223 * @returns Flag indicating whether the VM is currently in a running state.
3224 * @param pThis VBox disk container instance data.
3225 */
3226DECLINLINE(bool) drvvdMediaExIoReqIsVmRunning(PVBOXDISK pThis)
3227{
3228 VMSTATE enmVmState = PDMDrvHlpVMState(pThis->pDrvIns);
3229 if ( enmVmState == VMSTATE_RESUMING
3230 || enmVmState == VMSTATE_RUNNING
3231 || enmVmState == VMSTATE_RUNNING_LS
3232 || enmVmState == VMSTATE_RESETTING
3233 || enmVmState == VMSTATE_RESETTING_LS
3234 || enmVmState == VMSTATE_SOFT_RESETTING
3235 || enmVmState == VMSTATE_SOFT_RESETTING_LS
3236 || enmVmState == VMSTATE_SUSPENDING
3237 || enmVmState == VMSTATE_SUSPENDING_LS
3238 || enmVmState == VMSTATE_SUSPENDING_EXT_LS)
3239 return true;
3240
3241 return false;
3242}
3243
3244/**
3245 * @copydoc FNVDASYNCTRANSFERCOMPLETE
3246 */
3247static DECLCALLBACK(void) drvvdMediaExIoReqComplete(void *pvUser1, void *pvUser2, int rcReq)
3248{
3249 PVBOXDISK pThis = (PVBOXDISK)pvUser1;
3250 PPDMMEDIAEXIOREQINT pIoReq = (PPDMMEDIAEXIOREQINT)pvUser2;
3251
3252 drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rcReq, true /* fUpNotify */);
3253}
3254
3255/**
3256 * Tries to cancel the given I/O request returning the result.
3257 *
3258 * @returns Flag whether the request was successfully canceled or whether it
3259 * already complete inbetween.
3260 * @param pThis VBox disk container instance data.
3261 * @param pIoReq The I/O request to cancel.
3262 */
3263static bool drvvdMediaExIoReqCancel(PVBOXDISK pThis, PPDMMEDIAEXIOREQINT pIoReq)
3264{
3265 bool fXchg = false;
3266 VDIOREQSTATE enmStateOld = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3267
3268 drvvdMediaExIoReqLogRel(pThis, pIoReq);
3269
3270 /*
3271 * We might have to try canceling the request multiple times if it transitioned from
3272 * ALLOCATED to ACTIVE or to SUSPENDED between reading the state and trying to change it.
3273 */
3274 while ( ( enmStateOld == VDIOREQSTATE_ALLOCATED
3275 || enmStateOld == VDIOREQSTATE_ACTIVE
3276 || enmStateOld == VDIOREQSTATE_SUSPENDED)
3277 && !fXchg)
3278 {
3279 fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_CANCELED, enmStateOld);
3280 if (fXchg)
3281 break;
3282
3283 enmStateOld = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3284 }
3285
3286 if (fXchg && enmStateOld == VDIOREQSTATE_ACTIVE)
3287 {
3288 uint32_t cNew = ASMAtomicDecU32(&pThis->cIoReqsActive);
3289 AssertMsg(cNew != UINT32_MAX, ("Number of active requests underflowed!\n")); RT_NOREF(cNew);
3290 }
3291
3292 return fXchg;
3293}
3294
3295/**
3296 * @interface_method_impl{PDMIMEDIAEX,pfnQueryFeatures}
3297 */
3298static DECLCALLBACK(int) drvvdQueryFeatures(PPDMIMEDIAEX pInterface, uint32_t *pfFeatures)
3299{
3300 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3301
3302 AssertPtrReturn(pfFeatures, VERR_INVALID_POINTER);
3303
3304 uint32_t fFeatures = 0;
3305 if (pThis->fAsyncIOSupported)
3306 fFeatures |= PDMIMEDIAEX_FEATURE_F_ASYNC;
3307 if (pThis->IMedia.pfnDiscard)
3308 fFeatures |= PDMIMEDIAEX_FEATURE_F_DISCARD;
3309
3310 *pfFeatures = fFeatures;
3311
3312 return VINF_SUCCESS;
3313}
3314
3315
3316/**
3317 * @interface_method_impl{PDMIMEDIAEX,pfnNotifySuspend}
3318 */
3319static DECLCALLBACK(void) drvvdNotifySuspend(PPDMIMEDIAEX pInterface)
3320{
3321 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3322
3323 ASMAtomicXchgBool(&pThis->fSuspending, true);
3324
3325 /* Mark all waiting requests as suspended so they don't get accounted for. */
3326 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
3327 PPDMMEDIAEXIOREQINT pIoReqCur, pIoReqNext;
3328 RTListForEachSafe(&pThis->LstIoReqIoBufWait, pIoReqCur, pIoReqNext, PDMMEDIAEXIOREQINT, NdLstWait)
3329 {
3330 pThis->pDrvMediaExPort->pfnIoReqStateChanged(pThis->pDrvMediaExPort, pIoReqCur, &pIoReqCur->abAlloc[0],
3331 PDMMEDIAEXIOREQSTATE_SUSPENDED);
3332 LogFlowFunc(("Suspended I/O request %#p\n", pIoReqCur));
3333 }
3334 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
3335}
3336
3337
3338/**
3339 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqAllocSizeSet}
3340 */
3341static DECLCALLBACK(int) drvvdIoReqAllocSizeSet(PPDMIMEDIAEX pInterface, size_t cbIoReqAlloc)
3342{
3343 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3344 if (RT_UNLIKELY(pThis->hIoReqCache != NIL_RTMEMCACHE))
3345 return VERR_INVALID_STATE;
3346
3347 return RTMemCacheCreate(&pThis->hIoReqCache, sizeof(PDMMEDIAEXIOREQINT) + cbIoReqAlloc, 0, UINT32_MAX,
3348 NULL, NULL, NULL, 0);
3349}
3350
3351/**
3352 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqAlloc}
3353 */
3354static DECLCALLBACK(int) drvvdIoReqAlloc(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq, void **ppvIoReqAlloc,
3355 PDMMEDIAEXIOREQID uIoReqId, uint32_t fFlags)
3356{
3357 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3358
3359 AssertReturn(!(fFlags & ~PDMIMEDIAEX_F_VALID), VERR_INVALID_PARAMETER);
3360
3361 PPDMMEDIAEXIOREQINT pIoReq = (PPDMMEDIAEXIOREQINT)RTMemCacheAlloc(pThis->hIoReqCache);
3362
3363 if (RT_UNLIKELY(!pIoReq))
3364 return VERR_NO_MEMORY;
3365
3366 pIoReq->uIoReqId = uIoReqId;
3367 pIoReq->fFlags = fFlags;
3368 pIoReq->pDisk = pThis;
3369 pIoReq->enmState = VDIOREQSTATE_ALLOCATED;
3370 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_INVALID;
3371
3372 int rc = drvvdMediaExIoReqInsert(pThis, pIoReq);
3373 if (RT_SUCCESS(rc))
3374 {
3375 *phIoReq = pIoReq;
3376 *ppvIoReqAlloc = &pIoReq->abAlloc[0];
3377 }
3378 else
3379 RTMemCacheFree(pThis->hIoReqCache, pIoReq);
3380
3381 return rc;
3382}
3383
3384/**
3385 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqFree}
3386 */
3387static DECLCALLBACK(int) drvvdIoReqFree(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
3388{
3389 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3390 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3391
3392 if ( pIoReq->enmState != VDIOREQSTATE_COMPLETED
3393 && pIoReq->enmState != VDIOREQSTATE_ALLOCATED)
3394 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3395
3396 /* Remove from allocated list. */
3397 int rc = drvvdMediaExIoReqRemove(pThis, pIoReq);
3398 if (RT_FAILURE(rc))
3399 return rc;
3400
3401 /* Free any associated I/O memory. */
3402 drvvdMediaExIoReqBufFree(pThis, pIoReq);
3403
3404 /* For discard request discard the range array. */
3405 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD
3406 && pIoReq->Discard.paRanges)
3407 {
3408 RTMemFree(pIoReq->Discard.paRanges);
3409 pIoReq->Discard.paRanges = NULL;
3410 }
3411
3412 pIoReq->enmState = VDIOREQSTATE_FREE;
3413 RTMemCacheFree(pThis->hIoReqCache, pIoReq);
3414 return VINF_SUCCESS;
3415}
3416
3417/**
3418 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQueryResidual}
3419 */
3420static DECLCALLBACK(int) drvvdIoReqQueryResidual(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, size_t *pcbResidual)
3421{
3422 RT_NOREF1(pInterface);
3423
3424 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3425
3426 if (pIoReq->enmState != VDIOREQSTATE_COMPLETED)
3427 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3428
3429 if ( pIoReq->enmType != PDMMEDIAEXIOREQTYPE_READ
3430 && pIoReq->enmType != PDMMEDIAEXIOREQTYPE_WRITE
3431 && pIoReq->enmType != PDMMEDIAEXIOREQTYPE_FLUSH)
3432 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3433
3434 *pcbResidual = 0; /* No data left to transfer always. */
3435 return VINF_SUCCESS;
3436}
3437
3438/**
3439 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQueryXferSize}
3440 */
3441static DECLCALLBACK(int) drvvdIoReqQueryXferSize(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, size_t *pcbXfer)
3442{
3443 int rc = VINF_SUCCESS;
3444 RT_NOREF1(pInterface);
3445
3446 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3447
3448 if (pIoReq->enmState != VDIOREQSTATE_COMPLETED)
3449 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3450
3451 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
3452 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
3453 *pcbXfer = pIoReq->ReadWrite.cbReq;
3454 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_FLUSH)
3455 *pcbXfer = 0;
3456 else
3457 rc = VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3458
3459 return rc;
3460}
3461
3462/**
3463 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancelAll}
3464 */
3465static DECLCALLBACK(int) drvvdIoReqCancelAll(PPDMIMEDIAEX pInterface)
3466{
3467 int rc = VINF_SUCCESS;
3468 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3469
3470 LogRel(("VD#%u: Cancelling all active requests\n", pThis->pDrvIns->iInstance));
3471
3472 for (unsigned idxBin = 0; idxBin < RT_ELEMENTS(pThis->aIoReqAllocBins); idxBin++)
3473 {
3474 rc = RTSemFastMutexRequest(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
3475 if (RT_SUCCESS(rc))
3476 {
3477 /* Search for I/O request with ID. */
3478 PPDMMEDIAEXIOREQINT pIt;
3479
3480 RTListForEach(&pThis->aIoReqAllocBins[idxBin].LstIoReqAlloc, pIt, PDMMEDIAEXIOREQINT, NdAllocatedList)
3481 {
3482 drvvdMediaExIoReqCancel(pThis, pIt);
3483 }
3484 RTSemFastMutexRelease(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
3485 }
3486 }
3487
3488 return rc;
3489}
3490
3491/**
3492 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancel}
3493 */
3494static DECLCALLBACK(int) drvvdIoReqCancel(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQID uIoReqId)
3495{
3496 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3497 unsigned idxBin = drvvdMediaExIoReqIdHash(uIoReqId);
3498
3499 LogRel(("VD#%u: Trying to cancel request %#llx\n", pThis->pDrvIns->iInstance, uIoReqId));
3500
3501 int rc = RTSemFastMutexRequest(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
3502 if (RT_SUCCESS(rc))
3503 {
3504 /* Search for I/O request with ID. */
3505 PPDMMEDIAEXIOREQINT pIt;
3506 rc = VERR_PDM_MEDIAEX_IOREQID_NOT_FOUND;
3507
3508 RTListForEach(&pThis->aIoReqAllocBins[idxBin].LstIoReqAlloc, pIt, PDMMEDIAEXIOREQINT, NdAllocatedList)
3509 {
3510 if (pIt->uIoReqId == uIoReqId)
3511 {
3512 if (drvvdMediaExIoReqCancel(pThis, pIt))
3513 rc = VINF_SUCCESS;
3514
3515 break;
3516 }
3517 }
3518 RTSemFastMutexRelease(pThis->aIoReqAllocBins[idxBin].hMtxLstIoReqAlloc);
3519 }
3520
3521 return rc;
3522}
3523
3524/**
3525 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqRead}
3526 */
3527static DECLCALLBACK(int) drvvdIoReqRead(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbRead)
3528{
3529 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3530 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3531 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3532
3533 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
3534 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3535
3536 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
3537 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3538
3539 STAM_REL_COUNTER_INC(&pThis->StatReqsSubmitted);
3540 STAM_REL_COUNTER_INC(&pThis->StatReqsRead);
3541
3542 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_READ;
3543 pIoReq->tsSubmit = RTTimeMilliTS();
3544 pIoReq->ReadWrite.offStart = off;
3545 pIoReq->ReadWrite.cbReq = cbRead;
3546 pIoReq->ReadWrite.cbReqLeft = cbRead;
3547 /* Allocate a suitable I/O buffer for this request. */
3548 int rc = drvvdMediaExIoReqBufAlloc(pThis, pIoReq, cbRead);
3549 if (rc == VINF_SUCCESS)
3550 {
3551 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
3552 if (RT_UNLIKELY(!fXchg))
3553 {
3554 /* Must have been canceled inbetween. */
3555 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
3556 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3557 }
3558 ASMAtomicIncU32(&pThis->cIoReqsActive);
3559
3560 rc = drvvdMediaExIoReqReadWriteProcess(pThis, pIoReq, false /* fUpNotify */);
3561 }
3562
3563 return rc;
3564}
3565
3566/**
3567 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqWrite}
3568 */
3569static DECLCALLBACK(int) drvvdIoReqWrite(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbWrite)
3570{
3571 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3572 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3573 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3574
3575 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
3576 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3577
3578 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
3579 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3580
3581 STAM_REL_COUNTER_INC(&pThis->StatReqsSubmitted);
3582 STAM_REL_COUNTER_INC(&pThis->StatReqsWrite);
3583
3584 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_WRITE;
3585 pIoReq->tsSubmit = RTTimeMilliTS();
3586 pIoReq->ReadWrite.offStart = off;
3587 pIoReq->ReadWrite.cbReq = cbWrite;
3588 pIoReq->ReadWrite.cbReqLeft = cbWrite;
3589 /* Allocate a suitable I/O buffer for this request. */
3590 int rc = drvvdMediaExIoReqBufAlloc(pThis, pIoReq, cbWrite);
3591 if (rc == VINF_SUCCESS)
3592 {
3593 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
3594 if (RT_UNLIKELY(!fXchg))
3595 {
3596 /* Must have been canceled inbetween. */
3597 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
3598 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3599 }
3600 ASMAtomicIncU32(&pThis->cIoReqsActive);
3601
3602 rc = drvvdMediaExIoReqReadWriteProcess(pThis, pIoReq, false /* fUpNotify */);
3603 }
3604
3605 return rc;
3606}
3607
3608/**
3609 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqFlush}
3610 */
3611static DECLCALLBACK(int) drvvdIoReqFlush(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
3612{
3613 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3614 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3615 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3616
3617 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
3618 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3619
3620 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
3621 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3622
3623 STAM_REL_COUNTER_INC(&pThis->StatReqsSubmitted);
3624 STAM_REL_COUNTER_INC(&pThis->StatReqsFlush);
3625
3626 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_FLUSH;
3627 pIoReq->tsSubmit = RTTimeMilliTS();
3628 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
3629 if (RT_UNLIKELY(!fXchg))
3630 {
3631 /* Must have been canceled inbetween. */
3632 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
3633 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3634 }
3635
3636 ASMAtomicIncU32(&pThis->cIoReqsActive);
3637 int rc = drvvdMediaExIoReqFlushWrapper(pThis, pIoReq);
3638 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3639 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
3640 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
3641 rc = VINF_SUCCESS;
3642
3643 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
3644 rc = drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rc, false /* fUpNotify */);
3645
3646 return rc;
3647}
3648
3649/**
3650 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqDiscard}
3651 */
3652static DECLCALLBACK(int) drvvdIoReqDiscard(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, unsigned cRangesMax)
3653{
3654 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3655 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3656 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3657
3658 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
3659 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3660
3661 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
3662 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3663
3664 STAM_REL_COUNTER_INC(&pThis->StatReqsSubmitted);
3665 STAM_REL_COUNTER_INC(&pThis->StatReqsDiscard);
3666
3667 /* Copy the ranges over now, this can be optimized in the future. */
3668 pIoReq->Discard.paRanges = (PRTRANGE)RTMemAllocZ(cRangesMax * sizeof(RTRANGE));
3669 if (RT_UNLIKELY(!pIoReq->Discard.paRanges))
3670 return VERR_NO_MEMORY;
3671
3672 int rc = pThis->pDrvMediaExPort->pfnIoReqQueryDiscardRanges(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
3673 0, cRangesMax, pIoReq->Discard.paRanges,
3674 &pIoReq->Discard.cRanges);
3675 if (RT_SUCCESS(rc))
3676 {
3677 pIoReq->enmType = PDMMEDIAEXIOREQTYPE_DISCARD;
3678 pIoReq->tsSubmit = RTTimeMilliTS();
3679 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_ALLOCATED);
3680 if (RT_UNLIKELY(!fXchg))
3681 {
3682 /* Must have been canceled inbetween. */
3683 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
3684 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3685 }
3686
3687 ASMAtomicIncU32(&pThis->cIoReqsActive);
3688 rc = drvvdMediaExIoReqDiscardWrapper(pThis, pIoReq);
3689 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
3690 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
3691 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
3692 rc = VINF_SUCCESS;
3693
3694 if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
3695 rc = drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rc, false /* fUpNotify */);
3696 }
3697
3698 return rc;
3699}
3700
3701/**
3702 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqSendScsiCmd}
3703 */
3704static DECLCALLBACK(int) drvvdIoReqSendScsiCmd(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq,
3705 uint32_t uLun, const uint8_t *pbCdb, size_t cbCdb,
3706 PDMMEDIAEXIOREQSCSITXDIR enmTxDir, PDMMEDIAEXIOREQSCSITXDIR *penmTxDirRet,
3707 size_t cbBuf, uint8_t *pabSense, size_t cbSense, size_t *pcbSenseRet,
3708 uint8_t *pu8ScsiSts, uint32_t cTimeoutMillies)
3709{
3710 RT_NOREF12(pInterface, uLun, pbCdb, cbCdb, enmTxDir, penmTxDirRet, cbBuf, pabSense, cbSense, pcbSenseRet, pu8ScsiSts, cTimeoutMillies);
3711 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3712 VDIOREQSTATE enmState = (VDIOREQSTATE)ASMAtomicReadU32((volatile uint32_t *)&pIoReq->enmState);
3713
3714 if (RT_UNLIKELY(enmState == VDIOREQSTATE_CANCELED))
3715 return VERR_PDM_MEDIAEX_IOREQ_CANCELED;
3716
3717 if (RT_UNLIKELY(enmState != VDIOREQSTATE_ALLOCATED))
3718 return VERR_PDM_MEDIAEX_IOREQ_INVALID_STATE;
3719
3720 return VERR_NOT_SUPPORTED;
3721}
3722
3723/**
3724 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetActiveCount}
3725 */
3726static DECLCALLBACK(uint32_t) drvvdIoReqGetActiveCount(PPDMIMEDIAEX pInterface)
3727{
3728 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3729 return ASMAtomicReadU32(&pThis->cIoReqsActive);
3730}
3731
3732/**
3733 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetSuspendedCount}
3734 */
3735static DECLCALLBACK(uint32_t) drvvdIoReqGetSuspendedCount(PPDMIMEDIAEX pInterface)
3736{
3737 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3738
3739 AssertReturn(!drvvdMediaExIoReqIsVmRunning(pThis), 0);
3740
3741 uint32_t cIoReqSuspended = 0;
3742 PPDMMEDIAEXIOREQINT pIoReq;
3743 RTCritSectEnter(&pThis->CritSectIoReqRedo);
3744 RTListForEach(&pThis->LstIoReqRedo, pIoReq, PDMMEDIAEXIOREQINT, NdLstWait)
3745 {
3746 cIoReqSuspended++;
3747 }
3748 RTCritSectLeave(&pThis->CritSectIoReqRedo);
3749
3750 return cIoReqSuspended + pThis->cIoReqsWaiting;
3751}
3752
3753/**
3754 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedStart}
3755 */
3756static DECLCALLBACK(int) drvvdIoReqQuerySuspendedStart(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq,
3757 void **ppvIoReqAlloc)
3758{
3759 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3760
3761 AssertReturn(!drvvdMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
3762 AssertReturn(!( RTListIsEmpty(&pThis->LstIoReqRedo)
3763 && RTListIsEmpty(&pThis->LstIoReqIoBufWait)), VERR_NOT_FOUND);
3764
3765 PRTLISTANCHOR pLst;
3766 PRTCRITSECT pCritSect;
3767 if (!RTListIsEmpty(&pThis->LstIoReqRedo))
3768 {
3769 pLst = &pThis->LstIoReqRedo;
3770 pCritSect = &pThis->CritSectIoReqRedo;
3771 }
3772 else
3773 {
3774 pLst = &pThis->LstIoReqIoBufWait;
3775 pCritSect = &pThis->CritSectIoReqsIoBufWait;
3776 }
3777
3778 RTCritSectEnter(pCritSect);
3779 PPDMMEDIAEXIOREQINT pIoReq = RTListGetFirst(pLst, PDMMEDIAEXIOREQINT, NdLstWait);
3780 *phIoReq = pIoReq;
3781 *ppvIoReqAlloc = &pIoReq->abAlloc[0];
3782 RTCritSectLeave(pCritSect);
3783
3784 return VINF_SUCCESS;
3785}
3786
3787/**
3788 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedNext}
3789 */
3790static DECLCALLBACK(int) drvvdIoReqQuerySuspendedNext(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq,
3791 PPDMMEDIAEXIOREQ phIoReqNext, void **ppvIoReqAllocNext)
3792{
3793 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3794 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3795
3796 AssertReturn(!drvvdMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
3797 AssertPtrReturn(pIoReq, VERR_INVALID_HANDLE);
3798 AssertReturn( ( pIoReq->enmState == VDIOREQSTATE_SUSPENDED
3799 && ( !RTListNodeIsLast(&pThis->LstIoReqRedo, &pIoReq->NdLstWait)
3800 || !RTListIsEmpty(&pThis->LstIoReqIoBufWait)))
3801 || ( pIoReq->enmState == VDIOREQSTATE_ALLOCATED
3802 && !RTListNodeIsLast(&pThis->LstIoReqIoBufWait, &pIoReq->NdLstWait)), VERR_NOT_FOUND);
3803
3804 PPDMMEDIAEXIOREQINT pIoReqNext;
3805 if (pIoReq->enmState == VDIOREQSTATE_SUSPENDED)
3806 {
3807 if (!RTListNodeIsLast(&pThis->LstIoReqRedo, &pIoReq->NdLstWait))
3808 {
3809 RTCritSectEnter(&pThis->CritSectIoReqRedo);
3810 pIoReqNext = RTListNodeGetNext(&pIoReq->NdLstWait, PDMMEDIAEXIOREQINT, NdLstWait);
3811 RTCritSectLeave(&pThis->CritSectIoReqRedo);
3812 }
3813 else
3814 {
3815 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
3816 pIoReqNext = RTListGetFirst(&pThis->LstIoReqIoBufWait, PDMMEDIAEXIOREQINT, NdLstWait);
3817 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
3818 }
3819 }
3820 else
3821 {
3822 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
3823 pIoReqNext = RTListNodeGetNext(&pIoReq->NdLstWait, PDMMEDIAEXIOREQINT, NdLstWait);
3824 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
3825 }
3826
3827 *phIoReqNext = pIoReqNext;
3828 *ppvIoReqAllocNext = &pIoReqNext->abAlloc[0];
3829
3830 return VINF_SUCCESS;
3831}
3832
3833/**
3834 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedSave}
3835 */
3836static DECLCALLBACK(int) drvvdIoReqSuspendedSave(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
3837{
3838 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3839 PCPDMDRVHLPR3 pHlp = pThis->pDrvIns->pHlpR3;
3840 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3841
3842 AssertReturn(!drvvdMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
3843 AssertPtrReturn(pIoReq, VERR_INVALID_HANDLE);
3844 AssertReturn( pIoReq->enmState == VDIOREQSTATE_SUSPENDED
3845 || pIoReq->enmState == VDIOREQSTATE_ALLOCATED, VERR_INVALID_STATE);
3846
3847 pHlp->pfnSSMPutU32(pSSM, DRVVD_IOREQ_SAVED_STATE_VERSION);
3848 pHlp->pfnSSMPutU32(pSSM, (uint32_t)pIoReq->enmType);
3849 pHlp->pfnSSMPutU32(pSSM, pIoReq->uIoReqId);
3850 pHlp->pfnSSMPutU32(pSSM, pIoReq->fFlags);
3851 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
3852 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
3853 {
3854 pHlp->pfnSSMPutU64(pSSM, pIoReq->ReadWrite.offStart);
3855 pHlp->pfnSSMPutU64(pSSM, pIoReq->ReadWrite.cbReq);
3856 pHlp->pfnSSMPutU64(pSSM, pIoReq->ReadWrite.cbReqLeft);
3857 }
3858 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD)
3859 {
3860 pHlp->pfnSSMPutU32(pSSM, pIoReq->Discard.cRanges);
3861 for (unsigned i = 0; i < pIoReq->Discard.cRanges; i++)
3862 {
3863 pHlp->pfnSSMPutU64(pSSM, pIoReq->Discard.paRanges[i].offStart);
3864 pHlp->pfnSSMPutU64(pSSM, pIoReq->Discard.paRanges[i].cbRange);
3865 }
3866 }
3867
3868 return pHlp->pfnSSMPutU32(pSSM, UINT32_MAX); /* sanity/terminator */
3869}
3870
3871/**
3872 * @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedLoad}
3873 */
3874static DECLCALLBACK(int) drvvdIoReqSuspendedLoad(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
3875{
3876 PVBOXDISK pThis = RT_FROM_MEMBER(pInterface, VBOXDISK, IMediaEx);
3877 PCPDMDRVHLPR3 pHlp = pThis->pDrvIns->pHlpR3;
3878 PPDMMEDIAEXIOREQINT pIoReq = hIoReq;
3879
3880 AssertReturn(!drvvdMediaExIoReqIsVmRunning(pThis), VERR_INVALID_STATE);
3881 AssertPtrReturn(pIoReq, VERR_INVALID_HANDLE);
3882 AssertReturn(pIoReq->enmState == VDIOREQSTATE_ALLOCATED, VERR_INVALID_STATE);
3883
3884 uint32_t u32;
3885 uint64_t u64;
3886 int rc = VINF_SUCCESS;
3887 bool fPlaceOnRedoList = true;
3888
3889 pHlp->pfnSSMGetU32(pSSM, &u32);
3890 if (u32 <= DRVVD_IOREQ_SAVED_STATE_VERSION)
3891 {
3892 pHlp->pfnSSMGetU32(pSSM, &u32);
3893 AssertReturn( u32 == PDMMEDIAEXIOREQTYPE_WRITE
3894 || u32 == PDMMEDIAEXIOREQTYPE_READ
3895 || u32 == PDMMEDIAEXIOREQTYPE_DISCARD
3896 || u32 == PDMMEDIAEXIOREQTYPE_FLUSH,
3897 VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
3898 pIoReq->enmType = (PDMMEDIAEXIOREQTYPE)u32;
3899
3900 pHlp->pfnSSMGetU32(pSSM, &u32);
3901 AssertReturn(u32 == pIoReq->uIoReqId, VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
3902
3903 pHlp->pfnSSMGetU32(pSSM, &u32);
3904 AssertReturn(u32 == pIoReq->fFlags, VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
3905
3906 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
3907 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
3908 {
3909 pHlp->pfnSSMGetU64(pSSM, &pIoReq->ReadWrite.offStart);
3910 pHlp->pfnSSMGetU64(pSSM, &u64);
3911 pIoReq->ReadWrite.cbReq = (size_t)u64;
3912 pHlp->pfnSSMGetU64(pSSM, &u64);
3913 pIoReq->ReadWrite.cbReqLeft = (size_t)u64;
3914
3915 /*
3916 * Try to allocate enough I/O buffer, if this fails for some reason put it onto the
3917 * waiting list instead of the redo list.
3918 */
3919 pIoReq->ReadWrite.cbIoBuf = 0;
3920 rc = IOBUFMgrAllocBuf(pThis->hIoBufMgr, &pIoReq->ReadWrite.IoBuf, pIoReq->ReadWrite.cbReqLeft,
3921 &pIoReq->ReadWrite.cbIoBuf);
3922 if (rc == VERR_NO_MEMORY)
3923 {
3924 pIoReq->enmState = VDIOREQSTATE_ALLOCATED;
3925 ASMAtomicIncU32(&pThis->cIoReqsWaiting);
3926 RTListAppend(&pThis->LstIoReqIoBufWait, &pIoReq->NdLstWait);
3927 fPlaceOnRedoList = false;
3928 rc = VINF_SUCCESS;
3929 }
3930 else
3931 {
3932 pIoReq->ReadWrite.fDirectBuf = false;
3933 pIoReq->ReadWrite.pSgBuf = &pIoReq->ReadWrite.IoBuf.SgBuf;
3934 }
3935 }
3936 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD)
3937 {
3938 rc = pHlp->pfnSSMGetU32(pSSM, &pIoReq->Discard.cRanges);
3939 if (RT_SUCCESS(rc))
3940 {
3941 pIoReq->Discard.paRanges = (PRTRANGE)RTMemAllocZ(pIoReq->Discard.cRanges * sizeof(RTRANGE));
3942 if (RT_LIKELY(pIoReq->Discard.paRanges))
3943 {
3944 for (unsigned i = 0; i < pIoReq->Discard.cRanges; i++)
3945 {
3946 pHlp->pfnSSMGetU64(pSSM, &pIoReq->Discard.paRanges[i].offStart);
3947 pHlp->pfnSSMGetU64(pSSM, &u64);
3948 pIoReq->Discard.paRanges[i].cbRange = (size_t)u64;
3949 }
3950 }
3951 else
3952 rc = VERR_NO_MEMORY;
3953 }
3954 }
3955
3956 if (RT_SUCCESS(rc))
3957 rc = pHlp->pfnSSMGetU32(pSSM, &u32); /* sanity/terminator */
3958 if (RT_SUCCESS(rc))
3959 AssertReturn(u32 == UINT32_MAX, VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
3960 if ( RT_SUCCESS(rc)
3961 && fPlaceOnRedoList)
3962 {
3963 /* Mark as suspended */
3964 pIoReq->enmState = VDIOREQSTATE_SUSPENDED;
3965
3966 /* Link into suspended list so it gets kicked off again when we resume. */
3967 RTCritSectEnter(&pThis->CritSectIoReqRedo);
3968 RTListAppend(&pThis->LstIoReqRedo, &pIoReq->NdLstWait);
3969 RTCritSectLeave(&pThis->CritSectIoReqRedo);
3970 }
3971 }
3972
3973 return rc;
3974}
3975
3976/**
3977 * Loads all configured plugins.
3978 *
3979 * @returns VBox status code.
3980 * @param pDrvIns Driver instance data.
3981 * @param pCfg CFGM node holding plugin list.
3982 */
3983static int drvvdLoadPlugins(PPDMDRVINS pDrvIns, PCFGMNODE pCfg)
3984{
3985 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
3986
3987 PCFGMNODE pCfgPlugins = pHlp->pfnCFGMGetChild(pCfg, "Plugins");
3988
3989 if (pCfgPlugins)
3990 {
3991 PCFGMNODE pPluginCur = pHlp->pfnCFGMGetFirstChild(pCfgPlugins);
3992 while (pPluginCur)
3993 {
3994 int rc = VINF_SUCCESS;
3995 char *pszPluginFilename = NULL;
3996 rc = pHlp->pfnCFGMQueryStringAlloc(pPluginCur, "Path", &pszPluginFilename);
3997 if (RT_SUCCESS(rc))
3998 rc = VDPluginLoadFromFilename(pszPluginFilename);
3999
4000 if (RT_FAILURE(rc))
4001 LogRel(("VD: Failed to load plugin '%s' with %Rrc, continuing\n", pszPluginFilename, rc));
4002
4003 pPluginCur = pHlp->pfnCFGMGetNextChild(pPluginCur);
4004 }
4005 }
4006
4007 return VINF_SUCCESS;
4008}
4009
4010
4011/**
4012 * Sets up the disk filter chain.
4013 *
4014 * @returns VBox status code.
4015 * @param pThis The disk instance.
4016 * @param pCfg CFGM node holding the filter parameters.
4017 */
4018static int drvvdSetupFilters(PVBOXDISK pThis, PCFGMNODE pCfg)
4019{
4020 PCPDMDRVHLPR3 pHlp = pThis->pDrvIns->pHlpR3;
4021 int rc = VINF_SUCCESS;
4022
4023 PCFGMNODE pCfgFilter = pHlp->pfnCFGMGetChild(pCfg, "Filters");
4024 if (pCfgFilter)
4025 {
4026 PCFGMNODE pCfgFilterConfig = pHlp->pfnCFGMGetChild(pCfgFilter, "VDConfig");
4027 char *pszFilterName = NULL;
4028 VDINTERFACECONFIG VDIfConfig;
4029 PVDINTERFACE pVDIfsFilter = NULL;
4030
4031 rc = pHlp->pfnCFGMQueryStringAlloc(pCfgFilter, "FilterName", &pszFilterName);
4032 if (RT_SUCCESS(rc))
4033 {
4034 VDCFGNODE CfgNode;
4035
4036 VDIfConfig.pfnAreKeysValid = drvvdCfgAreKeysValid;
4037 VDIfConfig.pfnQuerySize = drvvdCfgQuerySize;
4038 VDIfConfig.pfnQuery = drvvdCfgQuery;
4039 VDIfConfig.pfnQueryBytes = drvvdCfgQueryBytes;
4040
4041 CfgNode.pHlp = pThis->pDrvIns->pHlpR3;
4042 CfgNode.pCfgNode = pCfgFilterConfig;
4043 rc = VDInterfaceAdd(&VDIfConfig.Core, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
4044 &CfgNode, sizeof(VDINTERFACECONFIG), &pVDIfsFilter);
4045 AssertRC(rc);
4046
4047 rc = VDFilterAdd(pThis->pDisk, pszFilterName, VD_FILTER_FLAGS_DEFAULT, pVDIfsFilter);
4048
4049 PDMDrvHlpMMHeapFree(pThis->pDrvIns, pszFilterName);
4050 }
4051 }
4052
4053 return rc;
4054}
4055
4056
4057/**
4058 * Translates a PDMMEDIATYPE value into a string.
4059 *
4060 * @returns Read only string.
4061 * @param enmType The type value.
4062 */
4063static const char *drvvdGetTypeName(PDMMEDIATYPE enmType)
4064{
4065 switch (enmType)
4066 {
4067 case PDMMEDIATYPE_ERROR: return "ERROR";
4068 case PDMMEDIATYPE_FLOPPY_360: return "FLOPPY_360";
4069 case PDMMEDIATYPE_FLOPPY_720: return "FLOPPY_720";
4070 case PDMMEDIATYPE_FLOPPY_1_20: return "FLOPPY_1_20";
4071 case PDMMEDIATYPE_FLOPPY_1_44: return "FLOPPY_1_44";
4072 case PDMMEDIATYPE_FLOPPY_2_88: return "FLOPPY_2_88";
4073 case PDMMEDIATYPE_FLOPPY_FAKE_15_6: return "FLOPPY_FAKE_15_6";
4074 case PDMMEDIATYPE_FLOPPY_FAKE_63_5: return "FLOPPY_FAKE_63_5";
4075 case PDMMEDIATYPE_CDROM: return "CDROM";
4076 case PDMMEDIATYPE_DVD: return "DVD";
4077 case PDMMEDIATYPE_HARD_DISK: return "HARD_DISK";
4078 default: return "Unknown";
4079 }
4080}
4081
4082/**
4083 * Returns the appropriate PDMMEDIATYPE for t he given string.
4084 *
4085 * @returns PDMMEDIATYPE
4086 * @param pszType The string representation of the media type.
4087 */
4088static PDMMEDIATYPE drvvdGetMediaTypeFromString(const char *pszType)
4089{
4090 PDMMEDIATYPE enmType = PDMMEDIATYPE_ERROR;
4091
4092 if (!strcmp(pszType, "HardDisk"))
4093 enmType = PDMMEDIATYPE_HARD_DISK;
4094 else if (!strcmp(pszType, "DVD"))
4095 enmType = PDMMEDIATYPE_DVD;
4096 else if (!strcmp(pszType, "CDROM"))
4097 enmType = PDMMEDIATYPE_CDROM;
4098 else if (!strcmp(pszType, "Floppy 2.88"))
4099 enmType = PDMMEDIATYPE_FLOPPY_2_88;
4100 else if (!strcmp(pszType, "Floppy 1.44"))
4101 enmType = PDMMEDIATYPE_FLOPPY_1_44;
4102 else if (!strcmp(pszType, "Floppy 1.20"))
4103 enmType = PDMMEDIATYPE_FLOPPY_1_20;
4104 else if (!strcmp(pszType, "Floppy 720"))
4105 enmType = PDMMEDIATYPE_FLOPPY_720;
4106 else if (!strcmp(pszType, "Floppy 360"))
4107 enmType = PDMMEDIATYPE_FLOPPY_360;
4108 else if (!strcmp(pszType, "Floppy 15.6"))
4109 enmType = PDMMEDIATYPE_FLOPPY_FAKE_15_6;
4110 else if (!strcmp(pszType, "Floppy 63.5"))
4111 enmType = PDMMEDIATYPE_FLOPPY_FAKE_63_5;
4112
4113 return enmType;
4114}
4115
4116/**
4117 * Converts PDMMEDIATYPE to the appropriate VDTYPE.
4118 *
4119 * @returns The VDTYPE.
4120 * @param enmType The PDMMEDIATYPE to convert from.
4121 */
4122static VDTYPE drvvdGetVDFromMediaType(PDMMEDIATYPE enmType)
4123{
4124 if (PDMMEDIATYPE_IS_FLOPPY(enmType))
4125 return VDTYPE_FLOPPY;
4126 else if (enmType == PDMMEDIATYPE_DVD || enmType == PDMMEDIATYPE_CDROM)
4127 return VDTYPE_OPTICAL_DISC;
4128 else if (enmType == PDMMEDIATYPE_HARD_DISK)
4129 return VDTYPE_HDD;
4130
4131 AssertMsgFailed(("Invalid media type %d{%s} given!\n", enmType, drvvdGetTypeName(enmType)));
4132 return VDTYPE_HDD;
4133}
4134
4135/**
4136 * Registers statistics associated with the given media driver.
4137 *
4138 * @returns VBox status code.
4139 * @param pThis The media driver instance.
4140 */
4141static int drvvdStatsRegister(PVBOXDISK pThis)
4142{
4143 PPDMDRVINS pDrvIns = pThis->pDrvIns;
4144
4145 /*
4146 * Figure out where to place the stats.
4147 */
4148 uint32_t iInstance = 0;
4149 uint32_t iLUN = 0;
4150 const char *pcszController = NULL;
4151 int rc = pThis->pDrvMediaPort->pfnQueryDeviceLocation(pThis->pDrvMediaPort, &pcszController, &iInstance, &iLUN);
4152 AssertRCReturn(rc, rc);
4153
4154 /*
4155 * Compose the prefix for the statistics to reduce the amount of repetition below.
4156 * The /Public/ bits are official and used by session info in the GUI.
4157 */
4158 char szCtrlUpper[32];
4159 rc = RTStrCopy(szCtrlUpper, sizeof(szCtrlUpper), pcszController);
4160 AssertRCReturn(rc, rc);
4161
4162 RTStrToUpper(szCtrlUpper);
4163 char szPrefix[128];
4164 RTStrPrintf(szPrefix, sizeof(szPrefix), "/Public/Storage/%s%u/Port%u", szCtrlUpper, iInstance, iLUN);
4165
4166 /*
4167 * Do the registrations.
4168 */
4169 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatQueryBufAttempts, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
4170 "Number of attempts to query a direct buffer.", "%s/QueryBufAttempts", szPrefix);
4171 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatQueryBufSuccess, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
4172 "Number of succeeded attempts to query a direct buffer.", "%s/QueryBufSuccess", szPrefix);
4173
4174 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4175 "Amount of data read.", "%s/BytesRead", szPrefix);
4176 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
4177 "Amount of data written.", "%s/BytesWritten", szPrefix);
4178
4179 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsSubmitted, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT,
4180 "Number of I/O requests submitted.", "%s/ReqsSubmitted", szPrefix);
4181 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsFailed, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT,
4182 "Number of I/O requests failed.", "%s/ReqsFailed", szPrefix);
4183 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsSucceeded, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT,
4184 "Number of I/O requests succeeded.", "%s/ReqsSucceeded", szPrefix);
4185 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsFlush, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT,
4186 "Number of flush I/O requests submitted.", "%s/ReqsFlush", szPrefix);
4187 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsWrite, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT,
4188 "Number of write I/O requests submitted.", "%s/ReqsWrite", szPrefix);
4189 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT,
4190 "Number of read I/O requests submitted.", "%s/ReqsRead", szPrefix);
4191 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsDiscard, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_COUNT,
4192 "Number of discard I/O requests submitted.", "%s/ReqsDiscard", szPrefix);
4193
4194 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReqsPerSec, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
4195 "Number of processed I/O requests per second.", "%s/ReqsPerSec", szPrefix);
4196
4197 return VINF_SUCCESS;
4198}
4199
4200/**
4201 * Deregisters statistics associated with the given media driver.
4202 *
4203 * @returns nothing.
4204 * @param pThis The media driver instance.
4205 */
4206static void drvvdStatsDeregister(PVBOXDISK pThis)
4207{
4208 PPDMDRVINS pDrvIns = pThis->pDrvIns;
4209
4210 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatQueryBufAttempts);
4211 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatQueryBufSuccess);
4212
4213 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatBytesRead);
4214 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatBytesWritten);
4215 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsSubmitted);
4216 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsFailed);
4217 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsSucceeded);
4218 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsFlush);
4219 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsWrite);
4220 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsRead);
4221 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsDiscard);
4222 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReqsPerSec);
4223}
4224
4225
4226/*********************************************************************************************************************************
4227* Base interface methods *
4228*********************************************************************************************************************************/
4229
4230/**
4231 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4232 */
4233static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4234{
4235 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
4236 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4237
4238 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
4239 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
4240 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, pThis->fMountable ? &pThis->IMount : NULL);
4241 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEX, pThis->pDrvMediaExPort ? &pThis->IMediaEx : NULL);
4242 return NULL;
4243}
4244
4245
4246/*********************************************************************************************************************************
4247* Saved state notification methods *
4248*********************************************************************************************************************************/
4249
4250/**
4251 * Load done callback for re-opening the image writable during teleportation.
4252 *
4253 * This is called both for successful and failed load runs, we only care about
4254 * successful ones.
4255 *
4256 * @returns VBox status code.
4257 * @param pDrvIns The driver instance.
4258 * @param pSSM The saved state handle.
4259 */
4260static DECLCALLBACK(int) drvvdLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
4261{
4262 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4263 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
4264 Assert(!pThis->fErrorUseRuntime);
4265
4266 /* Drop out if we don't have any work to do or if it's a failed load. */
4267 if ( !pThis->fTempReadOnly
4268 || RT_FAILURE(pHlp->pfnSSMHandleGetStatus(pSSM)))
4269 return VINF_SUCCESS;
4270
4271 int rc = drvvdSetWritable(pThis);
4272 if (RT_FAILURE(rc)) /** @todo does the bugger set any errors? */
4273 return pHlp->pfnSSMSetLoadError(pSSM, rc, RT_SRC_POS,
4274 N_("Failed to write lock the images"));
4275 return VINF_SUCCESS;
4276}
4277
4278
4279/*********************************************************************************************************************************
4280* Driver methods *
4281*********************************************************************************************************************************/
4282
4283/**
4284 * Worker for the power off or destruct callback.
4285 *
4286 * @returns nothing.
4287 * @param pDrvIns The driver instance.
4288 */
4289static void drvvdPowerOffOrDestructOrUnmount(PPDMDRVINS pDrvIns)
4290{
4291 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4292 LogFlowFunc(("\n"));
4293
4294 RTSEMFASTMUTEX mutex;
4295 ASMAtomicXchgHandle(&pThis->MergeCompleteMutex, NIL_RTSEMFASTMUTEX, &mutex);
4296 if (mutex != NIL_RTSEMFASTMUTEX)
4297 {
4298 /* Request the semaphore to wait until a potentially running merge
4299 * operation has been finished. */
4300 int rc = RTSemFastMutexRequest(mutex);
4301 AssertRC(rc);
4302 pThis->fMergePending = false;
4303 rc = RTSemFastMutexRelease(mutex);
4304 AssertRC(rc);
4305 rc = RTSemFastMutexDestroy(mutex);
4306 AssertRC(rc);
4307 }
4308
4309 if (RT_VALID_PTR(pThis->pBlkCache))
4310 {
4311 PDMDrvHlpBlkCacheRelease(pThis->pDrvIns, pThis->pBlkCache);
4312 pThis->pBlkCache = NULL;
4313 }
4314
4315 if (RT_VALID_PTR(pThis->pRegionList))
4316 {
4317 VDRegionListFree(pThis->pRegionList);
4318 pThis->pRegionList = NULL;
4319 }
4320
4321 if (RT_VALID_PTR(pThis->pDisk))
4322 {
4323 VDDestroy(pThis->pDisk);
4324 pThis->pDisk = NULL;
4325 }
4326 drvvdFreeImages(pThis);
4327}
4328
4329/**
4330 * @copydoc FNPDMDRVPOWEROFF
4331 */
4332static DECLCALLBACK(void) drvvdPowerOff(PPDMDRVINS pDrvIns)
4333{
4334 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
4335 drvvdPowerOffOrDestructOrUnmount(pDrvIns);
4336}
4337
4338/**
4339 * @callback_method_impl{FNPDMDRVRESUME}
4340 *
4341 * VM resume notification that we use to undo what the temporary read-only image
4342 * mode set by drvvdSuspend.
4343 *
4344 * Also switch to runtime error mode if we're resuming after a state load
4345 * without having been powered on first.
4346 *
4347 * @todo The VMSetError vs VMSetRuntimeError mess must be fixed elsewhere,
4348 * we're making assumptions about Main behavior here!
4349 */
4350static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
4351{
4352 LogFlowFunc(("\n"));
4353 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4354
4355 drvvdSetWritable(pThis);
4356 pThis->fSuspending = false;
4357 pThis->fRedo = false;
4358
4359 if (pThis->pBlkCache)
4360 {
4361 int rc = PDMDrvHlpBlkCacheResume(pThis->pDrvIns, pThis->pBlkCache);
4362 AssertRC(rc);
4363 }
4364
4365 if (pThis->pDrvMediaExPort)
4366 {
4367 /* Mark all requests waiting for I/O memory as active again so they get accounted for. */
4368 RTCritSectEnter(&pThis->CritSectIoReqsIoBufWait);
4369 PPDMMEDIAEXIOREQINT pIoReq, pIoReqNext;
4370 RTListForEachSafe(&pThis->LstIoReqIoBufWait, pIoReq, pIoReqNext, PDMMEDIAEXIOREQINT, NdLstWait)
4371 {
4372 pThis->pDrvMediaExPort->pfnIoReqStateChanged(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
4373 PDMMEDIAEXIOREQSTATE_ACTIVE);
4374 ASMAtomicIncU32(&pThis->cIoReqsActive);
4375 LogFlowFunc(("Resumed I/O request %#p\n", pIoReq));
4376 }
4377 RTCritSectLeave(&pThis->CritSectIoReqsIoBufWait);
4378
4379 /* Kick of any request we have to redo. */
4380 RTCritSectEnter(&pThis->CritSectIoReqRedo);
4381 RTListForEachSafe(&pThis->LstIoReqRedo, pIoReq, pIoReqNext, PDMMEDIAEXIOREQINT, NdLstWait)
4382 {
4383 int rc = VINF_SUCCESS;
4384 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pIoReq->enmState, VDIOREQSTATE_ACTIVE, VDIOREQSTATE_SUSPENDED);
4385
4386 RTListNodeRemove(&pIoReq->NdLstWait);
4387 ASMAtomicIncU32(&pThis->cIoReqsActive);
4388
4389 LogFlowFunc(("Resuming I/O request %#p fXchg=%RTbool\n", pIoReq, fXchg));
4390 if (fXchg)
4391 {
4392 pThis->pDrvMediaExPort->pfnIoReqStateChanged(pThis->pDrvMediaExPort, pIoReq, &pIoReq->abAlloc[0],
4393 PDMMEDIAEXIOREQSTATE_ACTIVE);
4394 LogFlowFunc(("Resumed I/O request %#p\n", pIoReq));
4395 if ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_READ
4396 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_WRITE)
4397 rc = drvvdMediaExIoReqReadWriteProcess(pThis, pIoReq, true /* fUpNotify */);
4398 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_FLUSH)
4399 {
4400 rc = drvvdMediaExIoReqFlushWrapper(pThis, pIoReq);
4401 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4402 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
4403 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
4404 rc = VINF_SUCCESS;
4405 }
4406 else if (pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD)
4407 {
4408 rc = drvvdMediaExIoReqDiscardWrapper(pThis, pIoReq);
4409 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
4410 rc = VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS;
4411 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
4412 rc = VINF_SUCCESS;
4413 }
4414 else
4415 AssertMsgFailed(("Invalid request type %u\n", pIoReq->enmType));
4416
4417 /* The read write process will call the completion callback on its own. */
4418 if ( rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS
4419 && ( pIoReq->enmType == PDMMEDIAEXIOREQTYPE_DISCARD
4420 || pIoReq->enmType == PDMMEDIAEXIOREQTYPE_FLUSH))
4421 {
4422 Assert( ( pIoReq->enmType != PDMMEDIAEXIOREQTYPE_WRITE
4423 && pIoReq->enmType != PDMMEDIAEXIOREQTYPE_READ)
4424 || !pIoReq->ReadWrite.cbReqLeft
4425 || RT_FAILURE(rc));
4426 drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, rc, true /* fUpNotify */);
4427 }
4428
4429 }
4430 else
4431 {
4432 /* Request was canceled inbetween, so don't care and notify the owner about the completed request. */
4433 Assert(pIoReq->enmState == VDIOREQSTATE_CANCELED);
4434 drvvdMediaExIoReqCompleteWorker(pThis, pIoReq, VERR_PDM_MEDIAEX_IOREQ_CANCELED, true /* fUpNotify */);
4435 }
4436 }
4437 Assert(RTListIsEmpty(&pThis->LstIoReqRedo));
4438 RTCritSectLeave(&pThis->CritSectIoReqRedo);
4439 }
4440
4441 /* Try to process any requests waiting for I/O memory now. */
4442 drvvdMediaExIoReqProcessWaiting(pThis);
4443 pThis->fErrorUseRuntime = true;
4444}
4445
4446/**
4447 * @callback_method_impl{FNPDMDRVSUSPEND}
4448 *
4449 * When the VM is being suspended, temporarily change to read-only image mode.
4450 *
4451 * This is important for several reasons:
4452 * -# It makes sure that there are no pending writes to the image. Most
4453 * backends implements this by closing and reopening the image in read-only
4454 * mode.
4455 * -# It allows Main to read the images during snapshotting without having
4456 * to account for concurrent writes.
4457 * -# This is essential for making teleportation targets sharing images work
4458 * right. Both with regards to caching and with regards to file sharing
4459 * locks (RTFILE_O_DENY_*). (See also drvvdLoadDone.)
4460 */
4461static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
4462{
4463 LogFlowFunc(("\n"));
4464 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4465
4466 if (pThis->pBlkCache)
4467 {
4468 int rc = PDMDrvHlpBlkCacheSuspend(pThis->pDrvIns, pThis->pBlkCache);
4469 AssertRC(rc);
4470 }
4471
4472 drvvdSetReadonly(pThis);
4473}
4474
4475/**
4476 * @callback_method_impl{FNPDMDRVPOWERON}
4477 */
4478static DECLCALLBACK(void) drvvdPowerOn(PPDMDRVINS pDrvIns)
4479{
4480 LogFlowFunc(("\n"));
4481 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4482 drvvdSetWritable(pThis);
4483 pThis->fErrorUseRuntime = true;
4484}
4485
4486/**
4487 * @callback_method_impl{FNPDMDRVRESET}
4488 */
4489static DECLCALLBACK(void) drvvdReset(PPDMDRVINS pDrvIns)
4490{
4491 LogFlowFunc(("\n"));
4492 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4493
4494 if (pThis->pBlkCache)
4495 {
4496 int rc = PDMDrvHlpBlkCacheClear(pThis->pDrvIns, pThis->pBlkCache);
4497 AssertRC(rc);
4498 }
4499
4500 if (pThis->fBootAccelEnabled)
4501 {
4502 pThis->fBootAccelActive = true;
4503 pThis->cbDataValid = 0;
4504 pThis->offDisk = 0;
4505 }
4506 pThis->fLocked = false;
4507}
4508
4509/**
4510 * @callback_method_impl{FNPDMDRVDESTRUCT}
4511 */
4512static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
4513{
4514 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
4515 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4516 LogFlowFunc(("\n"));
4517
4518 /*
4519 * Make sure the block cache and disks are closed when this driver is
4520 * destroyed. This method will get called without calling the power off
4521 * callback first when we reconfigure the driver chain after a snapshot.
4522 */
4523 drvvdPowerOffOrDestructOrUnmount(pDrvIns);
4524 if (pThis->MergeLock != NIL_RTSEMRW)
4525 {
4526 int rc = RTSemRWDestroy(pThis->MergeLock);
4527 AssertRC(rc);
4528 pThis->MergeLock = NIL_RTSEMRW;
4529 }
4530 if (pThis->pbData)
4531 {
4532 RTMemFree(pThis->pbData);
4533 pThis->pbData = NULL;
4534 }
4535 if (pThis->pszBwGroup)
4536 {
4537 PDMDrvHlpMMHeapFree(pDrvIns, pThis->pszBwGroup);
4538 pThis->pszBwGroup = NULL;
4539 }
4540 if (pThis->hHbdMgr != NIL_HBDMGR)
4541 HBDMgrDestroy(pThis->hHbdMgr);
4542 if (pThis->hIoReqCache != NIL_RTMEMCACHE)
4543 RTMemCacheDestroy(pThis->hIoReqCache);
4544 if (pThis->hIoBufMgr != NIL_IOBUFMGR)
4545 IOBUFMgrDestroy(pThis->hIoBufMgr);
4546 if (RTCritSectIsInitialized(&pThis->CritSectIoReqsIoBufWait))
4547 RTCritSectDelete(&pThis->CritSectIoReqsIoBufWait);
4548 if (RTCritSectIsInitialized(&pThis->CritSectIoReqRedo))
4549 RTCritSectDelete(&pThis->CritSectIoReqRedo);
4550 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aIoReqAllocBins); i++)
4551 if (pThis->aIoReqAllocBins[i].hMtxLstIoReqAlloc != NIL_RTSEMFASTMUTEX)
4552 RTSemFastMutexDestroy(pThis->aIoReqAllocBins[i].hMtxLstIoReqAlloc);
4553
4554 drvvdStatsDeregister(pThis);
4555
4556 PVDCFGNODE pIt;
4557 PVDCFGNODE pItNext;
4558 RTListForEachSafe(&pThis->LstCfgNodes, pIt, pItNext, VDCFGNODE, NdLst)
4559 {
4560 RTListNodeRemove(&pIt->NdLst);
4561 RTMemFreeZ(pIt, sizeof(*pIt));
4562 }
4563}
4564
4565/**
4566 * @callback_method_impl{FNPDMDRVCONSTRUCT,
4567 * Construct a VBox disk media driver instance.}
4568 */
4569static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
4570{
4571 RT_NOREF(fFlags);
4572 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
4573 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
4574 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
4575
4576 LogFlowFunc(("\n"));
4577
4578 char *pszName = NULL; /* The path of the disk image file. */
4579 char *pszFormat = NULL; /* The format backed to use for this image. */
4580 char *pszCachePath = NULL; /* The path to the cache image. */
4581 char *pszCacheFormat = NULL; /* The format backend to use for the cache image. */
4582 bool fReadOnly = false; /* True if the media is read-only. */
4583 bool fMaybeReadOnly = false; /* True if the media may or may not be read-only. */
4584 bool fHonorZeroWrites = false; /* True if zero blocks should be written. */
4585
4586 /*
4587 * Init the static parts.
4588 */
4589 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
4590 pThis->pDrvIns = pDrvIns;
4591 pThis->fTempReadOnly = false;
4592 pThis->pDisk = NULL;
4593 pThis->fAsyncIOSupported = false;
4594 pThis->fShareable = false;
4595 pThis->fMergePending = false;
4596 pThis->MergeCompleteMutex = NIL_RTSEMFASTMUTEX;
4597 pThis->MergeLock = NIL_RTSEMRW;
4598 pThis->uMergeSource = VD_LAST_IMAGE;
4599 pThis->uMergeTarget = VD_LAST_IMAGE;
4600 pThis->CfgCrypto.pCfgNode = NULL;
4601 pThis->CfgCrypto.pHlp = pDrvIns->pHlpR3;
4602 pThis->pIfSecKey = NULL;
4603 pThis->hIoReqCache = NIL_RTMEMCACHE;
4604 pThis->hIoBufMgr = NIL_IOBUFMGR;
4605 pThis->pRegionList = NULL;
4606 pThis->fSuspending = false;
4607 pThis->fRedo = false;
4608
4609 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aIoReqAllocBins); i++)
4610 pThis->aIoReqAllocBins[i].hMtxLstIoReqAlloc = NIL_RTSEMFASTMUTEX;
4611
4612 /* IMedia */
4613 pThis->IMedia.pfnRead = drvvdRead;
4614 pThis->IMedia.pfnReadPcBios = drvvdReadPcBios;
4615 pThis->IMedia.pfnWrite = drvvdWrite;
4616 pThis->IMedia.pfnFlush = drvvdFlush;
4617 pThis->IMedia.pfnMerge = drvvdMerge;
4618 pThis->IMedia.pfnSetSecKeyIf = drvvdSetSecKeyIf;
4619 pThis->IMedia.pfnGetSize = drvvdGetSize;
4620 pThis->IMedia.pfnGetSectorSize = drvvdGetSectorSize;
4621 pThis->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
4622 pThis->IMedia.pfnIsNonRotational = drvvdIsNonRotational;
4623 pThis->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
4624 pThis->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
4625 pThis->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
4626 pThis->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
4627 pThis->IMedia.pfnBiosIsVisible = drvvdBiosIsVisible;
4628 pThis->IMedia.pfnGetType = drvvdGetType;
4629 pThis->IMedia.pfnGetUuid = drvvdGetUuid;
4630 pThis->IMedia.pfnDiscard = drvvdDiscard;
4631 pThis->IMedia.pfnSendCmd = NULL;
4632 pThis->IMedia.pfnGetRegionCount = drvvdGetRegionCount;
4633 pThis->IMedia.pfnQueryRegionProperties = drvvdQueryRegionProperties;
4634 pThis->IMedia.pfnQueryRegionPropertiesForLba = drvvdQueryRegionPropertiesForLba;
4635
4636 /* IMount */
4637 pThis->IMount.pfnUnmount = drvvdUnmount;
4638 pThis->IMount.pfnIsMounted = drvvdIsMounted;
4639 pThis->IMount.pfnLock = drvvdLock;
4640 pThis->IMount.pfnUnlock = drvvdUnlock;
4641 pThis->IMount.pfnIsLocked = drvvdIsLocked;
4642
4643 /* IMediaEx */
4644 pThis->IMediaEx.pfnQueryFeatures = drvvdQueryFeatures;
4645 pThis->IMediaEx.pfnNotifySuspend = drvvdNotifySuspend;
4646 pThis->IMediaEx.pfnIoReqAllocSizeSet = drvvdIoReqAllocSizeSet;
4647 pThis->IMediaEx.pfnIoReqAlloc = drvvdIoReqAlloc;
4648 pThis->IMediaEx.pfnIoReqFree = drvvdIoReqFree;
4649 pThis->IMediaEx.pfnIoReqQueryResidual = drvvdIoReqQueryResidual;
4650 pThis->IMediaEx.pfnIoReqQueryXferSize = drvvdIoReqQueryXferSize;
4651 pThis->IMediaEx.pfnIoReqCancelAll = drvvdIoReqCancelAll;
4652 pThis->IMediaEx.pfnIoReqCancel = drvvdIoReqCancel;
4653 pThis->IMediaEx.pfnIoReqRead = drvvdIoReqRead;
4654 pThis->IMediaEx.pfnIoReqWrite = drvvdIoReqWrite;
4655 pThis->IMediaEx.pfnIoReqFlush = drvvdIoReqFlush;
4656 pThis->IMediaEx.pfnIoReqDiscard = drvvdIoReqDiscard;
4657 pThis->IMediaEx.pfnIoReqSendScsiCmd = drvvdIoReqSendScsiCmd;
4658 pThis->IMediaEx.pfnIoReqGetActiveCount = drvvdIoReqGetActiveCount;
4659 pThis->IMediaEx.pfnIoReqGetSuspendedCount = drvvdIoReqGetSuspendedCount;
4660 pThis->IMediaEx.pfnIoReqQuerySuspendedStart = drvvdIoReqQuerySuspendedStart;
4661 pThis->IMediaEx.pfnIoReqQuerySuspendedNext = drvvdIoReqQuerySuspendedNext;
4662 pThis->IMediaEx.pfnIoReqSuspendedSave = drvvdIoReqSuspendedSave;
4663 pThis->IMediaEx.pfnIoReqSuspendedLoad = drvvdIoReqSuspendedLoad;
4664
4665 RTListInit(&pThis->LstCfgNodes);
4666
4667 /* Initialize supported VD interfaces. */
4668 pThis->pVDIfsDisk = NULL;
4669
4670 pThis->VDIfError.pfnError = drvvdErrorCallback;
4671 pThis->VDIfError.pfnMessage = NULL;
4672 int rc = VDInterfaceAdd(&pThis->VDIfError.Core, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
4673 pDrvIns, sizeof(VDINTERFACEERROR), &pThis->pVDIfsDisk);
4674 AssertRC(rc);
4675
4676 /* List of images is empty now. */
4677 pThis->pImages = NULL;
4678
4679 pThis->pDrvMediaPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAPORT);
4680 if (!pThis->pDrvMediaPort)
4681 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
4682 N_("No media port interface above"));
4683
4684 pThis->pDrvMountNotify = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMOUNTNOTIFY);
4685
4686 /*
4687 * Try to attach the optional extended media interface port above and initialize associated
4688 * structures if available.
4689 */
4690 pThis->pDrvMediaExPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAEXPORT);
4691 if (pThis->pDrvMediaExPort)
4692 {
4693 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aIoReqAllocBins); i++)
4694 {
4695 rc = RTSemFastMutexCreate(&pThis->aIoReqAllocBins[i].hMtxLstIoReqAlloc);
4696 if (RT_FAILURE(rc))
4697 break;
4698 RTListInit(&pThis->aIoReqAllocBins[i].LstIoReqAlloc);
4699 }
4700
4701 if (RT_SUCCESS(rc))
4702 rc = RTCritSectInit(&pThis->CritSectIoReqsIoBufWait);
4703
4704 if (RT_SUCCESS(rc))
4705 rc = RTCritSectInit(&pThis->CritSectIoReqRedo);
4706
4707 if (RT_FAILURE(rc))
4708 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Creating Mutex failed"));
4709
4710 RTListInit(&pThis->LstIoReqIoBufWait);
4711 RTListInit(&pThis->LstIoReqRedo);
4712 }
4713
4714 /* Before we access any VD API load all given plugins. */
4715 rc = drvvdLoadPlugins(pDrvIns, pCfg);
4716 if (RT_FAILURE(rc))
4717 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Loading VD plugins failed"));
4718
4719 /*
4720 * Validate configuration and find all parent images.
4721 * It's sort of up side down from the image dependency tree.
4722 */
4723 bool fHostIP = false;
4724 bool fUseNewIo = false;
4725 bool fUseBlockCache = false;
4726 bool fDiscard = false;
4727 bool fInformAboutZeroBlocks = false;
4728 bool fSkipConsistencyChecks = false;
4729 bool fEmptyDrive = false;
4730 unsigned iLevel = 0;
4731 PCFGMNODE pCurNode = pCfg;
4732 uint32_t cbIoBufMax = 0;
4733
4734 for (;;)
4735 {
4736 bool fValid;
4737
4738 if (pCurNode == pCfg)
4739 {
4740 /* Toplevel configuration additionally contains the global image
4741 * open flags. Some might be converted to per-image flags later. */
4742 fValid = pHlp->pfnCFGMAreValuesValid(pCurNode,
4743 "Format\0Path\0"
4744 "ReadOnly\0MaybeReadOnly\0TempReadOnly\0Shareable\0HonorZeroWrites\0"
4745 "HostIPStack\0UseNewIo\0BootAcceleration\0BootAccelerationBuffer\0"
4746 "SetupMerge\0MergeSource\0MergeTarget\0BwGroup\0Type\0BlockCache\0"
4747 "CachePath\0CacheFormat\0Discard\0InformAboutZeroBlocks\0"
4748 "SkipConsistencyChecks\0"
4749 "Locked\0BIOSVisible\0Cylinders\0Heads\0Sectors\0Mountable\0"
4750 "EmptyDrive\0IoBufMax\0NonRotationalMedium\0"
4751#if defined(VBOX_PERIODIC_FLUSH) || defined(VBOX_IGNORE_FLUSH)
4752 "FlushInterval\0IgnoreFlush\0IgnoreFlushAsync\0"
4753#endif /* !(VBOX_PERIODIC_FLUSH || VBOX_IGNORE_FLUSH) */
4754 );
4755 }
4756 else
4757 {
4758 /* All other image configurations only contain image name and
4759 * the format information. */
4760 fValid = pHlp->pfnCFGMAreValuesValid(pCurNode, "Format\0Path\0"
4761 "MergeSource\0MergeTarget\0");
4762 }
4763 if (!fValid)
4764 {
4765 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
4766 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
4767 break;
4768 }
4769
4770 if (pCurNode == pCfg)
4771 {
4772 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "HostIPStack", &fHostIP, true);
4773 if (RT_FAILURE(rc))
4774 {
4775 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4776 N_("DrvVD: Configuration error: Querying \"HostIPStack\" as boolean failed"));
4777 break;
4778 }
4779
4780 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "HonorZeroWrites", &fHonorZeroWrites, false);
4781 if (RT_FAILURE(rc))
4782 {
4783 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4784 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
4785 break;
4786 }
4787
4788 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "ReadOnly", &fReadOnly, false);
4789 if (RT_FAILURE(rc))
4790 {
4791 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4792 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
4793 break;
4794 }
4795
4796 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "MaybeReadOnly", &fMaybeReadOnly, false);
4797 if (RT_FAILURE(rc))
4798 {
4799 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4800 N_("DrvVD: Configuration error: Querying \"MaybeReadOnly\" as boolean failed"));
4801 break;
4802 }
4803
4804 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "TempReadOnly", &pThis->fTempReadOnly, false);
4805 if (RT_FAILURE(rc))
4806 {
4807 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4808 N_("DrvVD: Configuration error: Querying \"TempReadOnly\" as boolean failed"));
4809 break;
4810 }
4811 if (fReadOnly && pThis->fTempReadOnly)
4812 {
4813 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
4814 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"TempReadOnly\" are set"));
4815 break;
4816 }
4817
4818 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "Shareable", &pThis->fShareable, false);
4819 if (RT_FAILURE(rc))
4820 {
4821 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4822 N_("DrvVD: Configuration error: Querying \"Shareable\" as boolean failed"));
4823 break;
4824 }
4825
4826 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "UseNewIo", &fUseNewIo, false);
4827 if (RT_FAILURE(rc))
4828 {
4829 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4830 N_("DrvVD: Configuration error: Querying \"UseNewIo\" as boolean failed"));
4831 break;
4832 }
4833 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "SetupMerge", &pThis->fMergePending, false);
4834 if (RT_FAILURE(rc))
4835 {
4836 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4837 N_("DrvVD: Configuration error: Querying \"SetupMerge\" as boolean failed"));
4838 break;
4839 }
4840 if (fReadOnly && pThis->fMergePending)
4841 {
4842 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
4843 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"MergePending\" are set"));
4844 break;
4845 }
4846 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "BootAcceleration", &pThis->fBootAccelEnabled, false);
4847 if (RT_FAILURE(rc))
4848 {
4849 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4850 N_("DrvVD: Configuration error: Querying \"BootAcceleration\" as boolean failed"));
4851 break;
4852 }
4853 rc = pHlp->pfnCFGMQueryU32Def(pCurNode, "BootAccelerationBuffer", (uint32_t *)&pThis->cbBootAccelBuffer, 16 * _1K);
4854 if (RT_FAILURE(rc))
4855 {
4856 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4857 N_("DrvVD: Configuration error: Querying \"BootAccelerationBuffer\" as integer failed"));
4858 break;
4859 }
4860 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "BlockCache", &fUseBlockCache, false);
4861 if (RT_FAILURE(rc))
4862 {
4863 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4864 N_("DrvVD: Configuration error: Querying \"BlockCache\" as boolean failed"));
4865 break;
4866 }
4867 rc = pHlp->pfnCFGMQueryStringAlloc(pCurNode, "BwGroup", &pThis->pszBwGroup);
4868 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
4869 {
4870 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4871 N_("DrvVD: Configuration error: Querying \"BwGroup\" as string failed"));
4872 break;
4873 }
4874 else
4875 rc = VINF_SUCCESS;
4876 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "Discard", &fDiscard, false);
4877 if (RT_FAILURE(rc))
4878 {
4879 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4880 N_("DrvVD: Configuration error: Querying \"Discard\" as boolean failed"));
4881 break;
4882 }
4883 if (fReadOnly && fDiscard)
4884 {
4885 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
4886 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"Discard\" are set"));
4887 break;
4888 }
4889 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "InformAboutZeroBlocks", &fInformAboutZeroBlocks, false);
4890 if (RT_FAILURE(rc))
4891 {
4892 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4893 N_("DrvVD: Configuration error: Querying \"InformAboutZeroBlocks\" as boolean failed"));
4894 break;
4895 }
4896 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "SkipConsistencyChecks", &fSkipConsistencyChecks, true);
4897 if (RT_FAILURE(rc))
4898 {
4899 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4900 N_("DrvVD: Configuration error: Querying \"SKipConsistencyChecks\" as boolean failed"));
4901 break;
4902 }
4903
4904 char *psz = NULL;
4905 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "Type", &psz);
4906 if (RT_FAILURE(rc))
4907 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_BLOCK_NO_TYPE, N_("Failed to obtain the sub type"));
4908 pThis->enmType = drvvdGetMediaTypeFromString(psz);
4909 if (pThis->enmType == PDMMEDIATYPE_ERROR)
4910 {
4911 PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_BLOCK_UNKNOWN_TYPE, RT_SRC_POS,
4912 N_("Unknown type \"%s\""), psz);
4913 PDMDrvHlpMMHeapFree(pDrvIns, psz);
4914 return VERR_PDM_BLOCK_UNKNOWN_TYPE;
4915 }
4916 PDMDrvHlpMMHeapFree(pDrvIns, psz); psz = NULL;
4917
4918 rc = pHlp->pfnCFGMQueryStringAlloc(pCurNode, "CachePath", &pszCachePath);
4919 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
4920 {
4921 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4922 N_("DrvVD: Configuration error: Querying \"CachePath\" as string failed"));
4923 break;
4924 }
4925 else
4926 rc = VINF_SUCCESS;
4927
4928 if (pszCachePath)
4929 {
4930 rc = pHlp->pfnCFGMQueryStringAlloc(pCurNode, "CacheFormat", &pszCacheFormat);
4931 if (RT_FAILURE(rc))
4932 {
4933 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
4934 N_("DrvVD: Configuration error: Querying \"CacheFormat\" as string failed"));
4935 break;
4936 }
4937 }
4938
4939 /* Mountable */
4940 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "Mountable", &pThis->fMountable, false);
4941 if (RT_FAILURE(rc))
4942 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Mountable\" from the config"));
4943
4944 /* Locked */
4945 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "Locked", &pThis->fLocked, false);
4946 if (RT_FAILURE(rc))
4947 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Locked\" from the config"));
4948
4949 /* BIOS visible */
4950 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "BIOSVisible", &pThis->fBiosVisible, true);
4951 if (RT_FAILURE(rc))
4952 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"BIOSVisible\" from the config"));
4953
4954 /* Cylinders */
4955 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "Cylinders", &pThis->LCHSGeometry.cCylinders, 0);
4956 if (RT_FAILURE(rc))
4957 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Cylinders\" from the config"));
4958
4959 /* Heads */
4960 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "Heads", &pThis->LCHSGeometry.cHeads, 0);
4961 if (RT_FAILURE(rc))
4962 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Heads\" from the config"));
4963
4964 /* Sectors */
4965 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "Sectors", &pThis->LCHSGeometry.cSectors, 0);
4966 if (RT_FAILURE(rc))
4967 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Sectors\" from the config"));
4968
4969 /* Uuid */
4970 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "Uuid", &psz);
4971 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
4972 RTUuidClear(&pThis->Uuid);
4973 else if (RT_SUCCESS(rc))
4974 {
4975 rc = RTUuidFromStr(&pThis->Uuid, psz);
4976 if (RT_FAILURE(rc))
4977 {
4978 PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Uuid from string failed on \"%s\""), psz);
4979 PDMDrvHlpMMHeapFree(pDrvIns, psz);
4980 return rc;
4981 }
4982 PDMDrvHlpMMHeapFree(pDrvIns, psz); psz = NULL;
4983 }
4984 else
4985 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"Uuid\" from the config"));
4986
4987#ifdef VBOX_PERIODIC_FLUSH
4988 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "FlushInterval", &pThis->cbFlushInterval, 0);
4989 if (RT_FAILURE(rc))
4990 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"FlushInterval\" from the config"));
4991#endif /* VBOX_PERIODIC_FLUSH */
4992
4993#ifdef VBOX_IGNORE_FLUSH
4994 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "IgnoreFlush", &pThis->fIgnoreFlush, true);
4995 if (RT_FAILURE(rc))
4996 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"IgnoreFlush\" from the config"));
4997
4998 if (pThis->fIgnoreFlush)
4999 LogRel(("DrvVD: Flushes will be ignored\n"));
5000 else
5001 LogRel(("DrvVD: Flushes will be passed to the disk\n"));
5002
5003 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "IgnoreFlushAsync", &pThis->fIgnoreFlushAsync, false);
5004 if (RT_FAILURE(rc))
5005 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"IgnoreFlushAsync\" from the config"));
5006
5007 if (pThis->fIgnoreFlushAsync)
5008 LogRel(("DrvVD: Async flushes will be ignored\n"));
5009 else
5010 LogRel(("DrvVD: Async flushes will be passed to the disk\n"));
5011#endif /* VBOX_IGNORE_FLUSH */
5012
5013 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "EmptyDrive", &fEmptyDrive, false);
5014 if (RT_FAILURE(rc))
5015 {
5016 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5017 N_("DrvVD: Configuration error: Querying \"EmptyDrive\" as boolean failed"));
5018 break;
5019 }
5020
5021 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "IoBufMax", &cbIoBufMax, 5 * _1M);
5022 if (RT_FAILURE(rc))
5023 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Failed to query \"IoBufMax\" from the config"));
5024
5025 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "NonRotationalMedium", &pThis->fNonRotational, false);
5026 if (RT_FAILURE(rc))
5027 return PDMDRV_SET_ERROR(pDrvIns, rc,
5028 N_("DrvVD configuration error: Querying \"NonRotationalMedium\" as boolean failed"));
5029 }
5030
5031 PCFGMNODE pParent = pHlp->pfnCFGMGetChild(pCurNode, "Parent");
5032 if (!pParent)
5033 break;
5034 pCurNode = pParent;
5035 iLevel++;
5036 }
5037
5038 if (pThis->pDrvMediaExPort)
5039 rc = IOBUFMgrCreate(&pThis->hIoBufMgr, cbIoBufMax, pThis->CfgCrypto.pCfgNode ? IOBUFMGR_F_REQUIRE_NOT_PAGABLE : IOBUFMGR_F_DEFAULT);
5040
5041 if ( !fEmptyDrive
5042 && RT_SUCCESS(rc))
5043 {
5044 /*
5045 * Create the image container and the necessary interfaces.
5046 */
5047 if (RT_SUCCESS(rc))
5048 {
5049 /*
5050 * The image has a bandwidth group but the host cache is enabled.
5051 * Use the async I/O framework but tell it to enable the host cache.
5052 */
5053 if (!fUseNewIo && pThis->pszBwGroup)
5054 {
5055 pThis->fAsyncIoWithHostCache = true;
5056 fUseNewIo = true;
5057 }
5058
5059 /** @todo quick hack to work around problems in the async I/O
5060 * implementation (rw semaphore thread ownership problem)
5061 * while a merge is running. Remove once this is fixed. */
5062 if (pThis->fMergePending)
5063 fUseNewIo = false;
5064
5065 if (RT_SUCCESS(rc) && pThis->fMergePending)
5066 {
5067 rc = RTSemFastMutexCreate(&pThis->MergeCompleteMutex);
5068 if (RT_SUCCESS(rc))
5069 rc = RTSemRWCreate(&pThis->MergeLock);
5070 if (RT_SUCCESS(rc))
5071 {
5072 pThis->VDIfThreadSync.pfnStartRead = drvvdThreadStartRead;
5073 pThis->VDIfThreadSync.pfnFinishRead = drvvdThreadFinishRead;
5074 pThis->VDIfThreadSync.pfnStartWrite = drvvdThreadStartWrite;
5075 pThis->VDIfThreadSync.pfnFinishWrite = drvvdThreadFinishWrite;
5076
5077 rc = VDInterfaceAdd(&pThis->VDIfThreadSync.Core, "DrvVD_ThreadSync", VDINTERFACETYPE_THREADSYNC,
5078 pThis, sizeof(VDINTERFACETHREADSYNC), &pThis->pVDIfsDisk);
5079 }
5080 else
5081 {
5082 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5083 N_("DrvVD: Failed to create semaphores for \"MergePending\""));
5084 }
5085 }
5086
5087 if (RT_SUCCESS(rc))
5088 {
5089 rc = VDCreate(pThis->pVDIfsDisk, drvvdGetVDFromMediaType(pThis->enmType), &pThis->pDisk);
5090 /* Error message is already set correctly. */
5091 }
5092 }
5093
5094 if (pThis->pDrvMediaExPort && fUseNewIo)
5095 pThis->fAsyncIOSupported = true;
5096
5097 uint64_t tsStart = RTTimeNanoTS();
5098
5099 unsigned iImageIdx = 0;
5100 while (pCurNode && RT_SUCCESS(rc))
5101 {
5102 /* Allocate per-image data. */
5103 PVBOXIMAGE pImage = drvvdNewImage(pThis);
5104 if (!pImage)
5105 {
5106 rc = VERR_NO_MEMORY;
5107 break;
5108 }
5109
5110 /*
5111 * Read the image configuration.
5112 */
5113 rc = pHlp->pfnCFGMQueryStringAlloc(pCurNode, "Path", &pszName);
5114 if (RT_FAILURE(rc))
5115 {
5116 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5117 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
5118 break;
5119 }
5120
5121 rc = pHlp->pfnCFGMQueryStringAlloc(pCurNode, "Format", &pszFormat);
5122 if (RT_FAILURE(rc))
5123 {
5124 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5125 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
5126 break;
5127 }
5128
5129 bool fMergeSource;
5130 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "MergeSource", &fMergeSource, false);
5131 if (RT_FAILURE(rc))
5132 {
5133 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5134 N_("DrvVD: Configuration error: Querying \"MergeSource\" as boolean failed"));
5135 break;
5136 }
5137 if (fMergeSource)
5138 {
5139 if (pThis->uMergeSource == VD_LAST_IMAGE)
5140 pThis->uMergeSource = iImageIdx;
5141 else
5142 {
5143 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
5144 N_("DrvVD: Configuration error: Multiple \"MergeSource\" occurrences"));
5145 break;
5146 }
5147 }
5148
5149 bool fMergeTarget;
5150 rc = pHlp->pfnCFGMQueryBoolDef(pCurNode, "MergeTarget", &fMergeTarget, false);
5151 if (RT_FAILURE(rc))
5152 {
5153 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
5154 N_("DrvVD: Configuration error: Querying \"MergeTarget\" as boolean failed"));
5155 break;
5156 }
5157 if (fMergeTarget)
5158 {
5159 if (pThis->uMergeTarget == VD_LAST_IMAGE)
5160 pThis->uMergeTarget = iImageIdx;
5161 else
5162 {
5163 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
5164 N_("DrvVD: Configuration error: Multiple \"MergeTarget\" occurrences"));
5165 break;
5166 }
5167 }
5168
5169 PCFGMNODE pCfgVDConfig = pHlp->pfnCFGMGetChild(pCurNode, "VDConfig");
5170 pImage->VDIfConfig.pfnAreKeysValid = drvvdCfgAreKeysValid;
5171 pImage->VDIfConfig.pfnQuerySize = drvvdCfgQuerySize;
5172 pImage->VDIfConfig.pfnQuery = drvvdCfgQuery;
5173 pImage->VDIfConfig.pfnQueryBytes = NULL;
5174
5175 PVDCFGNODE pCfgNode = (PVDCFGNODE)RTMemAllocZ(sizeof(*pCfgNode));
5176 if (RT_UNLIKELY(!pCfgNode))
5177 {
5178 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_NO_MEMORY,
5179 N_("DrvVD: Failed to allocate memory for config node"));
5180 break;
5181 }
5182
5183 pCfgNode->pHlp = pDrvIns->pHlpR3;
5184 pCfgNode->pCfgNode = pCfgVDConfig;
5185 RTListAppend(&pThis->LstCfgNodes, &pCfgNode->NdLst);
5186
5187 rc = VDInterfaceAdd(&pImage->VDIfConfig.Core, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
5188 pCfgNode, sizeof(VDINTERFACECONFIG), &pImage->pVDIfsImage);
5189 AssertRC(rc);
5190
5191 /* Check VDConfig for encryption config. */
5192 /** @todo This makes sure that the crypto config is not cleared accidentally
5193 * when it was set because there are multiple VDConfig entries for a snapshot chain
5194 * but only one contains the crypto config.
5195 *
5196 * This needs to be properly fixed by specifying which part of the image should contain the
5197 * crypto stuff.
5198 */
5199 if (!pThis->CfgCrypto.pCfgNode)
5200 {
5201 if (pCfgVDConfig)
5202 pThis->CfgCrypto.pCfgNode = pHlp->pfnCFGMGetChild(pCfgVDConfig, "CRYPT");
5203
5204 if (pThis->CfgCrypto.pCfgNode)
5205 {
5206 /* Setup VDConfig interface for disk encryption support. */
5207 pThis->VDIfCfg.pfnAreKeysValid = drvvdCfgAreKeysValid;
5208 pThis->VDIfCfg.pfnQuerySize = drvvdCfgQuerySize;
5209 pThis->VDIfCfg.pfnQuery = drvvdCfgQuery;
5210 pThis->VDIfCfg.pfnQueryBytes = NULL;
5211
5212 pThis->VDIfCrypto.pfnKeyRetain = drvvdCryptoKeyRetain;
5213 pThis->VDIfCrypto.pfnKeyRelease = drvvdCryptoKeyRelease;
5214 pThis->VDIfCrypto.pfnKeyStorePasswordRetain = drvvdCryptoKeyStorePasswordRetain;
5215 pThis->VDIfCrypto.pfnKeyStorePasswordRelease = drvvdCryptoKeyStorePasswordRelease;
5216 }
5217 }
5218
5219 /* Unconditionally insert the TCPNET interface, don't bother to check
5220 * if an image really needs it. Will be ignored. Since the TCPNET
5221 * interface is per image we could make this more flexible in the
5222 * future if we want to. */
5223 /* Construct TCPNET callback table depending on the config. This is
5224 * done unconditionally, as uninterested backends will ignore it. */
5225 if (fHostIP)
5226 rc = VDIfTcpNetInstDefaultCreate(&pImage->hVdIfTcpNet, &pImage->pVDIfsImage);
5227 else
5228 {
5229#ifndef VBOX_WITH_INIP
5230 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
5231 RT_SRC_POS, N_("DrvVD: Configuration error: TCP over Internal Networking not compiled in"));
5232#else /* VBOX_WITH_INIP */
5233 pImage->VDIfTcpNet.pfnSocketCreate = drvvdINIPSocketCreate;
5234 pImage->VDIfTcpNet.pfnSocketDestroy = drvvdINIPSocketDestroy;
5235 pImage->VDIfTcpNet.pfnClientConnect = drvvdINIPClientConnect;
5236 pImage->VDIfTcpNet.pfnClientClose = drvvdINIPClientClose;
5237 pImage->VDIfTcpNet.pfnIsClientConnected = drvvdINIPIsClientConnected;
5238 pImage->VDIfTcpNet.pfnSelectOne = drvvdINIPSelectOne;
5239 pImage->VDIfTcpNet.pfnRead = drvvdINIPRead;
5240 pImage->VDIfTcpNet.pfnWrite = drvvdINIPWrite;
5241 pImage->VDIfTcpNet.pfnSgWrite = drvvdINIPSgWrite;
5242 pImage->VDIfTcpNet.pfnFlush = drvvdINIPFlush;
5243 pImage->VDIfTcpNet.pfnSetSendCoalescing = drvvdINIPSetSendCoalescing;
5244 pImage->VDIfTcpNet.pfnGetLocalAddress = drvvdINIPGetLocalAddress;
5245 pImage->VDIfTcpNet.pfnGetPeerAddress = drvvdINIPGetPeerAddress;
5246 pImage->VDIfTcpNet.pfnSelectOneEx = drvvdINIPSelectOneEx;
5247 pImage->VDIfTcpNet.pfnPoke = drvvdINIPPoke;
5248
5249 rc = VDInterfaceAdd(&pImage->VDIfTcpNet.Core, "DrvVD_TCPNET",
5250 VDINTERFACETYPE_TCPNET, NULL,
5251 sizeof(VDINTERFACETCPNET), &pImage->pVDIfsImage);
5252 AssertRC(rc);
5253#endif /* VBOX_WITH_INIP */
5254 }
5255
5256 /* Insert the custom I/O interface only if we're told to use new IO.
5257 * Since the I/O interface is per image we could make this more
5258 * flexible in the future if we want to. */
5259 if (fUseNewIo)
5260 {
5261#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
5262 pImage->VDIfIo.pfnOpen = drvvdAsyncIOOpen;
5263 pImage->VDIfIo.pfnClose = drvvdAsyncIOClose;
5264 pImage->VDIfIo.pfnGetSize = drvvdAsyncIOGetSize;
5265 pImage->VDIfIo.pfnSetSize = drvvdAsyncIOSetSize;
5266 pImage->VDIfIo.pfnSetAllocationSize = drvvdAsyncIOSetAllocationSize;
5267 pImage->VDIfIo.pfnReadSync = drvvdAsyncIOReadSync;
5268 pImage->VDIfIo.pfnWriteSync = drvvdAsyncIOWriteSync;
5269 pImage->VDIfIo.pfnFlushSync = drvvdAsyncIOFlushSync;
5270 pImage->VDIfIo.pfnReadAsync = drvvdAsyncIOReadAsync;
5271 pImage->VDIfIo.pfnWriteAsync = drvvdAsyncIOWriteAsync;
5272 pImage->VDIfIo.pfnFlushAsync = drvvdAsyncIOFlushAsync;
5273#else /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
5274 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
5275 RT_SRC_POS, N_("DrvVD: Configuration error: Async Completion Framework not compiled in"));
5276#endif /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
5277 if (RT_SUCCESS(rc))
5278 rc = VDInterfaceAdd(&pImage->VDIfIo.Core, "DrvVD_IO", VDINTERFACETYPE_IO,
5279 pThis, sizeof(VDINTERFACEIO), &pImage->pVDIfsImage);
5280 AssertRC(rc);
5281 }
5282
5283 /*
5284 * Open the image.
5285 */
5286 unsigned uOpenFlags;
5287 if (fReadOnly || pThis->fTempReadOnly || iLevel != 0)
5288 uOpenFlags = VD_OPEN_FLAGS_READONLY;
5289 else
5290 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
5291 if (fHonorZeroWrites)
5292 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
5293 if (pThis->fAsyncIOSupported)
5294 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
5295 if (pThis->fShareable)
5296 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
5297 if (fDiscard && iLevel == 0)
5298 uOpenFlags |= VD_OPEN_FLAGS_DISCARD;
5299 if (fInformAboutZeroBlocks)
5300 uOpenFlags |= VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS;
5301 if ( (uOpenFlags & VD_OPEN_FLAGS_READONLY)
5302 && fSkipConsistencyChecks)
5303 uOpenFlags |= VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS;
5304
5305 /* Try to open backend in async I/O mode first. */
5306 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
5307 if (rc == VERR_NOT_SUPPORTED)
5308 {
5309 pThis->fAsyncIOSupported = false;
5310 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
5311 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
5312 }
5313
5314 if (rc == VERR_VD_DISCARD_NOT_SUPPORTED)
5315 {
5316 fDiscard = false;
5317 uOpenFlags &= ~VD_OPEN_FLAGS_DISCARD;
5318 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
5319 }
5320
5321 if (!fDiscard)
5322 {
5323 pThis->IMedia.pfnDiscard = NULL;
5324 pThis->IMediaEx.pfnIoReqDiscard = NULL;
5325 }
5326
5327 if (RT_SUCCESS(rc))
5328 {
5329 LogFunc(("%d - Opened '%s' in %s mode\n",
5330 iLevel, pszName,
5331 VDIsReadOnly(pThis->pDisk) ? "read-only" : "read-write"));
5332 if ( VDIsReadOnly(pThis->pDisk)
5333 && !fReadOnly
5334 && !fMaybeReadOnly
5335 && !pThis->fTempReadOnly
5336 && iLevel == 0)
5337 {
5338 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_VD_IMAGE_READ_ONLY, RT_SRC_POS,
5339 N_("Failed to open image '%s' for writing due to wrong permissions"),
5340 pszName);
5341 break;
5342 }
5343 }
5344 else
5345 {
5346 rc = PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
5347 N_("Failed to open image '%s' in %s mode"), pszName,
5348 (uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "read-only" : "read-write");
5349 break;
5350 }
5351
5352 PDMDrvHlpMMHeapFree(pDrvIns, pszName);
5353 pszName = NULL;
5354 PDMDrvHlpMMHeapFree(pDrvIns, pszFormat);
5355 pszFormat = NULL;
5356
5357 /* next */
5358 iLevel--;
5359 iImageIdx++;
5360 pCurNode = pHlp->pfnCFGMGetParent(pCurNode);
5361 }
5362
5363 LogRel(("VD: Opening the disk took %lld ns\n", RTTimeNanoTS() - tsStart));
5364
5365 /* Open the cache image if set. */
5366 if ( RT_SUCCESS(rc)
5367 && RT_VALID_PTR(pszCachePath))
5368 {
5369 /* Insert the custom I/O interface only if we're told to use new IO.
5370 * Since the I/O interface is per image we could make this more
5371 * flexible in the future if we want to. */
5372 if (fUseNewIo)
5373 {
5374#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
5375 pThis->VDIfIoCache.pfnOpen = drvvdAsyncIOOpen;
5376 pThis->VDIfIoCache.pfnClose = drvvdAsyncIOClose;
5377 pThis->VDIfIoCache.pfnGetSize = drvvdAsyncIOGetSize;
5378 pThis->VDIfIoCache.pfnSetSize = drvvdAsyncIOSetSize;
5379 pThis->VDIfIoCache.pfnReadSync = drvvdAsyncIOReadSync;
5380 pThis->VDIfIoCache.pfnWriteSync = drvvdAsyncIOWriteSync;
5381 pThis->VDIfIoCache.pfnFlushSync = drvvdAsyncIOFlushSync;
5382 pThis->VDIfIoCache.pfnReadAsync = drvvdAsyncIOReadAsync;
5383 pThis->VDIfIoCache.pfnWriteAsync = drvvdAsyncIOWriteAsync;
5384 pThis->VDIfIoCache.pfnFlushAsync = drvvdAsyncIOFlushAsync;
5385#else /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
5386 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
5387 RT_SRC_POS, N_("DrvVD: Configuration error: Async Completion Framework not compiled in"));
5388#endif /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
5389 if (RT_SUCCESS(rc))
5390 rc = VDInterfaceAdd(&pThis->VDIfIoCache.Core, "DrvVD_IO", VDINTERFACETYPE_IO,
5391 pThis, sizeof(VDINTERFACEIO), &pThis->pVDIfsCache);
5392 AssertRC(rc);
5393 }
5394
5395 rc = VDCacheOpen(pThis->pDisk, pszCacheFormat, pszCachePath, VD_OPEN_FLAGS_NORMAL, pThis->pVDIfsCache);
5396 if (RT_FAILURE(rc))
5397 rc = PDMDRV_SET_ERROR(pDrvIns, rc, N_("DrvVD: Could not open cache image"));
5398 }
5399
5400 if (RT_VALID_PTR(pszCachePath))
5401 PDMDrvHlpMMHeapFree(pDrvIns, pszCachePath);
5402 if (RT_VALID_PTR(pszCacheFormat))
5403 PDMDrvHlpMMHeapFree(pDrvIns, pszCacheFormat);
5404
5405 if ( RT_SUCCESS(rc)
5406 && pThis->fMergePending
5407 && ( pThis->uMergeSource == VD_LAST_IMAGE
5408 || pThis->uMergeTarget == VD_LAST_IMAGE))
5409 {
5410 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
5411 N_("DrvVD: Configuration error: Inconsistent image merge data"));
5412 }
5413
5414 /* Create the block cache if enabled. */
5415 if ( fUseBlockCache
5416 && !pThis->fShareable
5417 && !fDiscard
5418 && !pThis->CfgCrypto.pCfgNode /* Disk encryption disables the block cache for security reasons */
5419 && RT_SUCCESS(rc))
5420 {
5421 /*
5422 * We need a unique ID for the block cache (to identify the owner of data
5423 * blocks in a saved state). UUIDs are not really suitable because
5424 * there are image formats which don't support them. Furthermore it is
5425 * possible that a new diff image was attached after a saved state
5426 * which changes the UUID.
5427 * However the device "name + device instance + LUN" triple the disk is
5428 * attached to is always constant for saved states.
5429 */
5430 char *pszId = NULL;
5431 uint32_t iInstance, iLUN;
5432 const char *pcszController;
5433
5434 rc = pThis->pDrvMediaPort->pfnQueryDeviceLocation(pThis->pDrvMediaPort, &pcszController,
5435 &iInstance, &iLUN);
5436 if (RT_FAILURE(rc))
5437 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
5438 N_("DrvVD: Configuration error: Could not query device data"));
5439 else
5440 {
5441 int cbStr = RTStrAPrintf(&pszId, "%s-%d-%d", pcszController, iInstance, iLUN);
5442
5443 if (cbStr > 0)
5444 {
5445 rc = PDMDrvHlpBlkCacheRetain(pDrvIns, &pThis->pBlkCache,
5446 drvvdBlkCacheXferCompleteIoReq,
5447 drvvdBlkCacheXferEnqueue,
5448 drvvdBlkCacheXferEnqueueDiscard,
5449 pszId);
5450 if (rc == VERR_NOT_SUPPORTED)
5451 {
5452 LogRel(("VD: Block cache is not supported\n"));
5453 rc = VINF_SUCCESS;
5454 }
5455 else
5456 AssertRC(rc);
5457
5458 RTStrFree(pszId);
5459 }
5460 else
5461 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
5462 N_("DrvVD: Out of memory when creating block cache"));
5463 }
5464 }
5465
5466 if (RT_SUCCESS(rc))
5467 rc = drvvdSetupFilters(pThis, pCfg);
5468
5469 /*
5470 * Register a load-done callback so we can undo TempReadOnly config before
5471 * we get to drvvdResume. Automatically deregistered upon destruction.
5472 */
5473 if (RT_SUCCESS(rc))
5474 rc = PDMDrvHlpSSMRegisterEx(pDrvIns, 0 /* version */, 0 /* cbGuess */,
5475 NULL /*pfnLivePrep*/, NULL /*pfnLiveExec*/, NULL /*pfnLiveVote*/,
5476 NULL /*pfnSavePrep*/, NULL /*pfnSaveExec*/, NULL /*pfnSaveDone*/,
5477 NULL /*pfnDonePrep*/, NULL /*pfnLoadExec*/, drvvdLoadDone);
5478
5479 /* Setup the boot acceleration stuff if enabled. */
5480 if (RT_SUCCESS(rc) && pThis->fBootAccelEnabled)
5481 {
5482 pThis->cbDisk = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
5483 Assert(pThis->cbDisk > 0);
5484 pThis->pbData = (uint8_t *)RTMemAllocZ(pThis->cbBootAccelBuffer);
5485 if (pThis->pbData)
5486 {
5487 pThis->fBootAccelActive = true;
5488 pThis->offDisk = 0;
5489 pThis->cbDataValid = 0;
5490 LogRel(("VD: Boot acceleration enabled\n"));
5491 }
5492 else
5493 LogRel(("VD: Boot acceleration, out of memory, disabled\n"));
5494 }
5495
5496 if ( RTUuidIsNull(&pThis->Uuid)
5497 && pThis->enmType == PDMMEDIATYPE_HARD_DISK)
5498 VDGetUuid(pThis->pDisk, 0, &pThis->Uuid);
5499
5500 /*
5501 * Automatically upgrade the floppy drive if the specified one is too
5502 * small to represent the whole boot time image. (We cannot do this later
5503 * since the BIOS (and others) gets the info via CMOS.)
5504 *
5505 * This trick should make 2.88 images as well as the fake 15.6 and 63.5 MB
5506 * images despite the hardcoded default 1.44 drive.
5507 */
5508 if ( PDMMEDIATYPE_IS_FLOPPY(pThis->enmType)
5509 && pThis->pDisk)
5510 {
5511 uint64_t const cbFloppyImg = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
5512 PDMMEDIATYPE const enmCfgType = pThis->enmType;
5513 switch (enmCfgType)
5514 {
5515 default:
5516 AssertFailed();
5517 RT_FALL_THRU();
5518 case PDMMEDIATYPE_FLOPPY_360:
5519 if (cbFloppyImg > 40 * 2 * 9 * 512)
5520 pThis->enmType = PDMMEDIATYPE_FLOPPY_720;
5521 RT_FALL_THRU();
5522 case PDMMEDIATYPE_FLOPPY_720:
5523 if (cbFloppyImg > 80 * 2 * 14 * 512)
5524 pThis->enmType = PDMMEDIATYPE_FLOPPY_1_20;
5525 RT_FALL_THRU();
5526 case PDMMEDIATYPE_FLOPPY_1_20:
5527 if (cbFloppyImg > 80 * 2 * 20 * 512)
5528 pThis->enmType = PDMMEDIATYPE_FLOPPY_1_44;
5529 RT_FALL_THRU();
5530 case PDMMEDIATYPE_FLOPPY_1_44:
5531 if (cbFloppyImg > 80 * 2 * 24 * 512)
5532 pThis->enmType = PDMMEDIATYPE_FLOPPY_2_88;
5533 RT_FALL_THRU();
5534 case PDMMEDIATYPE_FLOPPY_2_88:
5535 if (cbFloppyImg > 80 * 2 * 48 * 512)
5536 pThis->enmType = PDMMEDIATYPE_FLOPPY_FAKE_15_6;
5537 RT_FALL_THRU();
5538 case PDMMEDIATYPE_FLOPPY_FAKE_15_6:
5539 if (cbFloppyImg > 255 * 2 * 63 * 512)
5540 pThis->enmType = PDMMEDIATYPE_FLOPPY_FAKE_63_5;
5541 RT_FALL_THRU();
5542 case PDMMEDIATYPE_FLOPPY_FAKE_63_5:
5543 if (cbFloppyImg > 255 * 2 * 255 * 512)
5544 LogRel(("Warning: Floppy image is larger that 63.5 MB! (%llu bytes)\n", cbFloppyImg));
5545 break;
5546 }
5547 if (pThis->enmType != enmCfgType)
5548 LogRel(("DrvVD: Automatically upgraded floppy drive from %s to %s to better support the %u byte image\n",
5549 drvvdGetTypeName(enmCfgType), drvvdGetTypeName(pThis->enmType), cbFloppyImg));
5550 }
5551 } /* !fEmptyDrive */
5552
5553 if (RT_SUCCESS(rc))
5554 drvvdStatsRegister(pThis);
5555
5556 if (RT_FAILURE(rc))
5557 {
5558 if (RT_VALID_PTR(pszName))
5559 PDMDrvHlpMMHeapFree(pDrvIns, pszName);
5560 if (RT_VALID_PTR(pszFormat))
5561 PDMDrvHlpMMHeapFree(pDrvIns, pszFormat);
5562 /* drvvdDestruct does the rest. */
5563 }
5564
5565 LogFlowFunc(("returns %Rrc\n", rc));
5566 return rc;
5567}
5568
5569/**
5570 * VBox disk container media driver registration record.
5571 */
5572const PDMDRVREG g_DrvVD =
5573{
5574 /* u32Version */
5575 PDM_DRVREG_VERSION,
5576 /* szName */
5577 "VD",
5578 /* szRCMod */
5579 "",
5580 /* szR0Mod */
5581 "",
5582 /* pszDescription */
5583 "Generic VBox disk media driver.",
5584 /* fFlags */
5585 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
5586 /* fClass. */
5587 PDM_DRVREG_CLASS_MEDIA,
5588 /* cMaxInstances */
5589 ~0U,
5590 /* cbInstance */
5591 sizeof(VBOXDISK),
5592 /* pfnConstruct */
5593 drvvdConstruct,
5594 /* pfnDestruct */
5595 drvvdDestruct,
5596 /* pfnRelocate */
5597 NULL,
5598 /* pfnIOCtl */
5599 NULL,
5600 /* pfnPowerOn */
5601 drvvdPowerOn,
5602 /* pfnReset */
5603 drvvdReset,
5604 /* pfnSuspend */
5605 drvvdSuspend,
5606 /* pfnResume */
5607 drvvdResume,
5608 /* pfnAttach */
5609 NULL,
5610 /* pfnDetach */
5611 NULL,
5612 /* pfnPowerOff */
5613 drvvdPowerOff,
5614 /* pfnSoftReset */
5615 NULL,
5616 /* u32EndVersion */
5617 PDM_DRVREG_VERSION
5618};
5619
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