VirtualBox

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

Last change on this file since 75111 was 74839, checked in by vboxsync, 6 years ago

DrvVD: Fixed failing suspend/resume cycle if an recoverable error occurs a second time, need to clear the redo flag on resume. Fixed active I/O request accounting [build fix]

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