VirtualBox

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

Last change on this file since 63951 was 63951, checked in by vboxsync, 8 years ago

DrvVD: More work on the extended media interface to bring it up to par with the older async interface so we can start replacing PDMIMEDIAASYNC with the new interface

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