VirtualBox

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

Last change on this file since 93732 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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