VirtualBox

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

Last change on this file since 78125 was 77856, checked in by vboxsync, 6 years ago

Devices/DrvVD: Get rid of the TCP/NET implementation and use the default one provided by VD, shaves off over 500 lines of code from this complex driver

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