VirtualBox

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

Last change on this file since 98110 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

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