VirtualBox

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

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

PDMIMEDIAEX: Add parameter to pfnIoReqSendScsiCmd to indicate where to store the SCSI status code on success

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