VirtualBox

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

Last change on this file since 91945 was 91937, checked in by vboxsync, 3 years ago

Devices/Storage/DrvVD: Eliminate the last direct calls to CFGMR3* and use the driver helper callbacks, bguref:10074

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