VirtualBox

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

Last change on this file since 43861 was 43861, checked in by vboxsync, 12 years ago

Storage: Introduce new flag to skip unnecessary consistency checks in the VMDK backend while opening an image in readonly mode to speed up opening snapshots

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 100.4 KB
Line 
1/* $Id: DrvVD.cpp 43861 2012-11-13 10:35:55Z vboxsync $ */
2/** @file
3 * DrvVD - Generic VBox disk media driver.
4 */
5
6/*
7 * Copyright (C) 2006-2010 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/pdmasynccompletion.h>
26#include <VBox/vmm/pdmblkcache.h>
27#include <iprt/asm.h>
28#include <iprt/alloc.h>
29#include <iprt/assert.h>
30#include <iprt/uuid.h>
31#include <iprt/file.h>
32#include <iprt/string.h>
33#include <iprt/tcp.h>
34#include <iprt/semaphore.h>
35#include <iprt/sg.h>
36#include <iprt/poll.h>
37#include <iprt/pipe.h>
38#include <iprt/system.h>
39
40#ifdef VBOX_WITH_INIP
41/* All lwip header files are not C++ safe. So hack around this. */
42RT_C_DECLS_BEGIN
43#include <lwip/inet.h>
44#include <lwip/tcp.h>
45#include <lwip/sockets.h>
46RT_C_DECLS_END
47#endif /* VBOX_WITH_INIP */
48
49#include "VBoxDD.h"
50
51#ifdef VBOX_WITH_INIP
52/* Small hack to get at lwIP initialized status */
53extern bool DevINIPConfigured(void);
54#endif /* VBOX_WITH_INIP */
55
56
57/*******************************************************************************
58* Defined types, constants and macros *
59*******************************************************************************/
60
61/** Converts a pointer to VBOXDISK::IMedia to a PVBOXDISK. */
62#define PDMIMEDIA_2_VBOXDISK(pInterface) \
63 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMedia)) )
64
65/** Converts a pointer to VBOXDISK::IMediaAsync to a PVBOXDISK. */
66#define PDMIMEDIAASYNC_2_VBOXDISK(pInterface) \
67 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMediaAsync)) )
68
69/**
70 * VBox disk container, image information, private part.
71 */
72
73typedef struct VBOXIMAGE
74{
75 /** Pointer to next image. */
76 struct VBOXIMAGE *pNext;
77 /** Pointer to list of VD interfaces. Per-image. */
78 PVDINTERFACE pVDIfsImage;
79 /** Configuration information interface. */
80 VDINTERFACECONFIG VDIfConfig;
81 /** TCP network stack interface. */
82 VDINTERFACETCPNET VDIfTcpNet;
83 /** I/O interface. */
84 VDINTERFACEIO VDIfIo;
85} VBOXIMAGE, *PVBOXIMAGE;
86
87/**
88 * Storage backend data.
89 */
90typedef struct DRVVDSTORAGEBACKEND
91{
92 /** PDM async completion end point. */
93 PPDMASYNCCOMPLETIONENDPOINT pEndpoint;
94 /** The template. */
95 PPDMASYNCCOMPLETIONTEMPLATE pTemplate;
96 /** Event semaphore for synchronous operations. */
97 RTSEMEVENT EventSem;
98 /** Flag whether a synchronous operation is currently pending. */
99 volatile bool fSyncIoPending;
100 /** Return code of the last completed request. */
101 int rcReqLast;
102 /** Callback routine */
103 PFNVDCOMPLETED pfnCompleted;
104} DRVVDSTORAGEBACKEND, *PDRVVDSTORAGEBACKEND;
105
106/**
107 * VBox disk container media main structure, private part.
108 *
109 * @implements PDMIMEDIA
110 * @implements PDMIMEDIAASYNC
111 * @implements VDINTERFACEERROR
112 * @implements VDINTERFACETCPNET
113 * @implements VDINTERFACEASYNCIO
114 * @implements VDINTERFACECONFIG
115 */
116typedef struct VBOXDISK
117{
118 /** The VBox disk container. */
119 PVBOXHDD pDisk;
120 /** The media interface. */
121 PDMIMEDIA IMedia;
122 /** Media port. */
123 PPDMIMEDIAPORT pDrvMediaPort;
124 /** Pointer to the driver instance. */
125 PPDMDRVINS pDrvIns;
126 /** Flag whether suspend has changed image open mode to read only. */
127 bool fTempReadOnly;
128 /** Flag whether to use the runtime (true) or startup error facility. */
129 bool fErrorUseRuntime;
130 /** Pointer to list of VD interfaces. Per-disk. */
131 PVDINTERFACE pVDIfsDisk;
132 /** Error interface. */
133 VDINTERFACEERROR VDIfError;
134 /** Thread synchronization interface. */
135 VDINTERFACETHREADSYNC VDIfThreadSync;
136
137 /** Flag whether opened disk supports async I/O operations. */
138 bool fAsyncIOSupported;
139 /** The async media interface. */
140 PDMIMEDIAASYNC IMediaAsync;
141 /** The async media port interface above. */
142 PPDMIMEDIAASYNCPORT pDrvMediaAsyncPort;
143 /** Pointer to the list of data we need to keep per image. */
144 PVBOXIMAGE pImages;
145 /** Flag whether the media should allow concurrent open for writing. */
146 bool fShareable;
147 /** Flag whether a merge operation has been set up. */
148 bool fMergePending;
149 /** Synchronization to prevent destruction before merge finishes. */
150 RTSEMFASTMUTEX MergeCompleteMutex;
151 /** Synchronization between merge and other image accesses. */
152 RTSEMRW MergeLock;
153 /** Source image index for merging. */
154 unsigned uMergeSource;
155 /** Target image index for merging. */
156 unsigned uMergeTarget;
157
158 /** Flag whether boot acceleration is enabled. */
159 bool fBootAccelEnabled;
160 /** Flag whether boot acceleration is currently active. */
161 bool fBootAccelActive;
162 /** Size of the disk, used for read truncation. */
163 size_t cbDisk;
164 /** Size of the configured buffer. */
165 size_t cbBootAccelBuffer;
166 /** Start offset for which the buffer holds data. */
167 uint64_t offDisk;
168 /** Number of valid bytes in the buffer. */
169 size_t cbDataValid;
170 /** The disk buffer. */
171 uint8_t *pbData;
172 /** Bandwidth group the disk is assigned to. */
173 char *pszBwGroup;
174 /** Flag whether async I/O using the host cache is enabled. */
175 bool fAsyncIoWithHostCache;
176
177 /** I/O interface for a cache image. */
178 VDINTERFACEIO VDIfIoCache;
179 /** Interface list for the cache image. */
180 PVDINTERFACE pVDIfsCache;
181
182 /** The block cache handle if configured. */
183 PPDMBLKCACHE pBlkCache;
184} VBOXDISK, *PVBOXDISK;
185
186
187/*******************************************************************************
188* Internal Functions *
189*******************************************************************************/
190
191/**
192 * Internal: allocate new image descriptor and put it in the list
193 */
194static PVBOXIMAGE drvvdNewImage(PVBOXDISK pThis)
195{
196 AssertPtr(pThis);
197 PVBOXIMAGE pImage = (PVBOXIMAGE)RTMemAllocZ(sizeof(VBOXIMAGE));
198 if (pImage)
199 {
200 pImage->pVDIfsImage = NULL;
201 PVBOXIMAGE *pp = &pThis->pImages;
202 while (*pp != NULL)
203 pp = &(*pp)->pNext;
204 *pp = pImage;
205 pImage->pNext = NULL;
206 }
207
208 return pImage;
209}
210
211/**
212 * Internal: free the list of images descriptors.
213 */
214static void drvvdFreeImages(PVBOXDISK pThis)
215{
216 while (pThis->pImages != NULL)
217 {
218 PVBOXIMAGE p = pThis->pImages;
219 pThis->pImages = pThis->pImages->pNext;
220 RTMemFree(p);
221 }
222}
223
224
225/**
226 * Make the image temporarily read-only.
227 *
228 * @returns VBox status code.
229 * @param pThis The driver instance data.
230 */
231static int drvvdSetReadonly(PVBOXDISK pThis)
232{
233 int rc = VINF_SUCCESS;
234 if (!VDIsReadOnly(pThis->pDisk))
235 {
236 unsigned uOpenFlags;
237 rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
238 AssertRC(rc);
239 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
240 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
241 AssertRC(rc);
242 pThis->fTempReadOnly = true;
243 }
244 return rc;
245}
246
247
248/**
249 * Undo the temporary read-only status of the image.
250 *
251 * @returns VBox status code.
252 * @param pThis The driver instance data.
253 */
254static int drvvdSetWritable(PVBOXDISK pThis)
255{
256 int rc = VINF_SUCCESS;
257 if (pThis->fTempReadOnly)
258 {
259 unsigned uOpenFlags;
260 rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
261 AssertRC(rc);
262 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
263 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
264 if (RT_SUCCESS(rc))
265 pThis->fTempReadOnly = false;
266 else
267 AssertRC(rc);
268 }
269 return rc;
270}
271
272
273/*******************************************************************************
274* Error reporting callback *
275*******************************************************************************/
276
277static void drvvdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL,
278 const char *pszFormat, va_list va)
279{
280 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
281 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
282 if (pThis->fErrorUseRuntime)
283 /* We must not pass VMSETRTERR_FLAGS_FATAL as it could lead to a
284 * deadlock: We are probably executed in a thread context != EMT
285 * and the EM thread would wait until every thread is suspended
286 * but we would wait for the EM thread ... */
287
288 PDMDrvHlpVMSetRuntimeErrorV(pDrvIns, /* fFlags=*/ 0, "DrvVD", pszFormat, va);
289 else
290 PDMDrvHlpVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
291}
292
293/*******************************************************************************
294* VD Async I/O interface implementation *
295*******************************************************************************/
296
297#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
298
299static DECLCALLBACK(void) drvvdAsyncTaskCompleted(PPDMDRVINS pDrvIns, void *pvTemplateUser, void *pvUser, int rcReq)
300{
301 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
302 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pvTemplateUser;
303
304 LogFlowFunc(("pDrvIns=%#p pvTemplateUser=%#p pvUser=%#p rcReq=%d\n",
305 pDrvIns, pvTemplateUser, pvUser, rcReq));
306
307 if (pStorageBackend->fSyncIoPending)
308 {
309 Assert(!pvUser);
310 pStorageBackend->rcReqLast = rcReq;
311 pStorageBackend->fSyncIoPending = false;
312 RTSemEventSignal(pStorageBackend->EventSem);
313 }
314 else
315 {
316 int rc;
317
318 AssertPtr(pvUser);
319
320 AssertPtr(pStorageBackend->pfnCompleted);
321 rc = pStorageBackend->pfnCompleted(pvUser, rcReq);
322 AssertRC(rc);
323 }
324}
325
326static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation,
327 uint32_t fOpen,
328 PFNVDCOMPLETED pfnCompleted,
329 void **ppStorage)
330{
331 PVBOXDISK pThis = (PVBOXDISK)pvUser;
332 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)RTMemAllocZ(sizeof(DRVVDSTORAGEBACKEND));
333 int rc = VINF_SUCCESS;
334
335 if (pStorageBackend)
336 {
337 pStorageBackend->fSyncIoPending = false;
338 pStorageBackend->rcReqLast = VINF_SUCCESS;
339 pStorageBackend->pfnCompleted = pfnCompleted;
340
341 rc = RTSemEventCreate(&pStorageBackend->EventSem);
342 if (RT_SUCCESS(rc))
343 {
344 rc = PDMDrvHlpAsyncCompletionTemplateCreate(pThis->pDrvIns, &pStorageBackend->pTemplate,
345 drvvdAsyncTaskCompleted, pStorageBackend, "AsyncTaskCompleted");
346 if (RT_SUCCESS(rc))
347 {
348 uint32_t fFlags = (fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ
349 ? PDMACEP_FILE_FLAGS_READ_ONLY
350 : 0;
351 if (pThis->fShareable)
352 {
353 Assert((fOpen & RTFILE_O_DENY_MASK) == RTFILE_O_DENY_NONE);
354
355 fFlags |= PDMACEP_FILE_FLAGS_DONT_LOCK;
356 }
357 if (pThis->fAsyncIoWithHostCache)
358 fFlags |= PDMACEP_FILE_FLAGS_HOST_CACHE_ENABLED;
359
360 rc = PDMR3AsyncCompletionEpCreateForFile(&pStorageBackend->pEndpoint,
361 pszLocation, fFlags,
362 pStorageBackend->pTemplate);
363
364 if (RT_SUCCESS(rc))
365 {
366 if (pThis->pszBwGroup)
367 rc = PDMR3AsyncCompletionEpSetBwMgr(pStorageBackend->pEndpoint, pThis->pszBwGroup);
368
369 if (RT_SUCCESS(rc))
370 {
371 *ppStorage = pStorageBackend;
372 return VINF_SUCCESS;
373 }
374
375 PDMR3AsyncCompletionEpClose(pStorageBackend->pEndpoint);
376 }
377
378 PDMR3AsyncCompletionTemplateDestroy(pStorageBackend->pTemplate);
379 }
380 RTSemEventDestroy(pStorageBackend->EventSem);
381 }
382 RTMemFree(pStorageBackend);
383 }
384 else
385 rc = VERR_NO_MEMORY;
386
387 return rc;
388}
389
390static DECLCALLBACK(int) drvvdAsyncIOClose(void *pvUser, void *pStorage)
391{
392 PVBOXDISK pThis = (PVBOXDISK)pvUser;
393 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
394
395 PDMR3AsyncCompletionEpClose(pStorageBackend->pEndpoint);
396 PDMR3AsyncCompletionTemplateDestroy(pStorageBackend->pTemplate);
397 RTSemEventDestroy(pStorageBackend->EventSem);
398 RTMemFree(pStorageBackend);
399
400 return VINF_SUCCESS;;
401}
402
403static DECLCALLBACK(int) drvvdAsyncIOReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
404 void *pvBuf, size_t cbRead, size_t *pcbRead)
405{
406 PVBOXDISK pThis = (PVBOXDISK)pvUser;
407 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
408 RTSGSEG DataSeg;
409 PPDMASYNCCOMPLETIONTASK pTask;
410
411 Assert(!pStorageBackend->fSyncIoPending);
412 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
413 DataSeg.cbSeg = cbRead;
414 DataSeg.pvSeg = pvBuf;
415
416 int rc = PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, &DataSeg, 1, cbRead, NULL, &pTask);
417 if (RT_FAILURE(rc))
418 return rc;
419
420 if (rc == VINF_AIO_TASK_PENDING)
421 {
422 /* Wait */
423 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
424 AssertRC(rc);
425 }
426 else
427 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
428
429 if (pcbRead)
430 *pcbRead = cbRead;
431
432 return pStorageBackend->rcReqLast;
433}
434
435static DECLCALLBACK(int) drvvdAsyncIOWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
436 const void *pvBuf, size_t cbWrite, size_t *pcbWritten)
437{
438 PVBOXDISK pThis = (PVBOXDISK)pvUser;
439 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
440 RTSGSEG DataSeg;
441 PPDMASYNCCOMPLETIONTASK pTask;
442
443 Assert(!pStorageBackend->fSyncIoPending);
444 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
445 DataSeg.cbSeg = cbWrite;
446 DataSeg.pvSeg = (void *)pvBuf;
447
448 int rc = PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, &DataSeg, 1, cbWrite, NULL, &pTask);
449 if (RT_FAILURE(rc))
450 return rc;
451
452 if (rc == VINF_AIO_TASK_PENDING)
453 {
454 /* Wait */
455 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
456 AssertRC(rc);
457 }
458 else
459 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
460
461 if (pcbWritten)
462 *pcbWritten = cbWrite;
463
464 return pStorageBackend->rcReqLast;
465}
466
467static DECLCALLBACK(int) drvvdAsyncIOFlushSync(void *pvUser, void *pStorage)
468{
469 PVBOXDISK pThis = (PVBOXDISK)pvUser;
470 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
471 PPDMASYNCCOMPLETIONTASK pTask;
472
473 LogFlowFunc(("pvUser=%#p pStorage=%#p\n", pvUser, pStorage));
474
475 Assert(!pStorageBackend->fSyncIoPending);
476 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, true);
477
478 int rc = PDMR3AsyncCompletionEpFlush(pStorageBackend->pEndpoint, NULL, &pTask);
479 if (RT_FAILURE(rc))
480 return rc;
481
482 if (rc == VINF_AIO_TASK_PENDING)
483 {
484 /* Wait */
485 LogFlowFunc(("Waiting for flush to complete\n"));
486 rc = RTSemEventWait(pStorageBackend->EventSem, RT_INDEFINITE_WAIT);
487 AssertRC(rc);
488 }
489 else
490 ASMAtomicXchgBool(&pStorageBackend->fSyncIoPending, false);
491
492 return pStorageBackend->rcReqLast;
493}
494
495static DECLCALLBACK(int) drvvdAsyncIOReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
496 PCRTSGSEG paSegments, size_t cSegments,
497 size_t cbRead, void *pvCompletion,
498 void **ppTask)
499{
500 PVBOXDISK pThis = (PVBOXDISK)pvUser;
501 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
502
503 int rc = PDMR3AsyncCompletionEpRead(pStorageBackend->pEndpoint, uOffset, paSegments, cSegments, cbRead,
504 pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
505 if (rc == VINF_AIO_TASK_PENDING)
506 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
507
508 return rc;
509}
510
511static DECLCALLBACK(int) drvvdAsyncIOWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
512 PCRTSGSEG paSegments, size_t cSegments,
513 size_t cbWrite, void *pvCompletion,
514 void **ppTask)
515{
516 PVBOXDISK pThis = (PVBOXDISK)pvUser;
517 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
518
519 int rc = PDMR3AsyncCompletionEpWrite(pStorageBackend->pEndpoint, uOffset, paSegments, cSegments, cbWrite,
520 pvCompletion, (PPPDMASYNCCOMPLETIONTASK)ppTask);
521 if (rc == VINF_AIO_TASK_PENDING)
522 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
523
524 return rc;
525}
526
527static DECLCALLBACK(int) drvvdAsyncIOFlushAsync(void *pvUser, void *pStorage,
528 void *pvCompletion, void **ppTask)
529{
530 PVBOXDISK pThis = (PVBOXDISK)pvUser;
531 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
532
533 int rc = PDMR3AsyncCompletionEpFlush(pStorageBackend->pEndpoint, pvCompletion,
534 (PPPDMASYNCCOMPLETIONTASK)ppTask);
535 if (rc == VINF_AIO_TASK_PENDING)
536 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
537
538 return rc;
539}
540
541static DECLCALLBACK(int) drvvdAsyncIOGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
542{
543 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
544 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
545
546 return PDMR3AsyncCompletionEpGetSize(pStorageBackend->pEndpoint, pcbSize);
547}
548
549static DECLCALLBACK(int) drvvdAsyncIOSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
550{
551 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
552 PDRVVDSTORAGEBACKEND pStorageBackend = (PDRVVDSTORAGEBACKEND)pStorage;
553
554 return PDMR3AsyncCompletionEpSetSize(pStorageBackend->pEndpoint, cbSize);
555}
556
557#endif /* VBOX_WITH_PDM_ASYNC_COMPLETION */
558
559
560/*******************************************************************************
561* VD Thread Synchronization interface implementation *
562*******************************************************************************/
563
564static DECLCALLBACK(int) drvvdThreadStartRead(void *pvUser)
565{
566 PVBOXDISK pThis = (PVBOXDISK)pvUser;
567
568 return RTSemRWRequestRead(pThis->MergeLock, RT_INDEFINITE_WAIT);
569}
570
571static DECLCALLBACK(int) drvvdThreadFinishRead(void *pvUser)
572{
573 PVBOXDISK pThis = (PVBOXDISK)pvUser;
574
575 return RTSemRWReleaseRead(pThis->MergeLock);
576}
577
578static DECLCALLBACK(int) drvvdThreadStartWrite(void *pvUser)
579{
580 PVBOXDISK pThis = (PVBOXDISK)pvUser;
581
582 return RTSemRWRequestWrite(pThis->MergeLock, RT_INDEFINITE_WAIT);
583}
584
585static DECLCALLBACK(int) drvvdThreadFinishWrite(void *pvUser)
586{
587 PVBOXDISK pThis = (PVBOXDISK)pvUser;
588
589 return RTSemRWReleaseWrite(pThis->MergeLock);
590}
591
592
593/*******************************************************************************
594* VD Configuration interface implementation *
595*******************************************************************************/
596
597static bool drvvdCfgAreKeysValid(void *pvUser, const char *pszzValid)
598{
599 return CFGMR3AreValuesValid((PCFGMNODE)pvUser, pszzValid);
600}
601
602static int drvvdCfgQuerySize(void *pvUser, const char *pszName, size_t *pcb)
603{
604 return CFGMR3QuerySize((PCFGMNODE)pvUser, pszName, pcb);
605}
606
607static int drvvdCfgQuery(void *pvUser, const char *pszName, char *pszString, size_t cchString)
608{
609 return CFGMR3QueryString((PCFGMNODE)pvUser, pszName, pszString, cchString);
610}
611
612
613#ifdef VBOX_WITH_INIP
614/*******************************************************************************
615* VD TCP network stack interface implementation - INIP case *
616*******************************************************************************/
617
618typedef union INIPSOCKADDRUNION
619{
620 struct sockaddr Addr;
621 struct sockaddr_in Ipv4;
622} INIPSOCKADDRUNION;
623
624typedef struct INIPSOCKET
625{
626 int hSock;
627} INIPSOCKET, *PINIPSOCKET;
628
629static DECLCALLBACK(int) drvvdINIPFlush(VDSOCKET Sock);
630
631/** @copydoc VDINTERFACETCPNET::pfnSocketCreate */
632static DECLCALLBACK(int) drvvdINIPSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
633{
634 PINIPSOCKET pSocketInt = NULL;
635
636 /*
637 * The extended select method is not supported because it is impossible to wakeup
638 * the thread.
639 */
640 if (fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT)
641 return VERR_NOT_SUPPORTED;
642
643 pSocketInt = (PINIPSOCKET)RTMemAllocZ(sizeof(INIPSOCKET));
644 if (pSocketInt)
645 {
646 pSocketInt->hSock = INT32_MAX;
647 *pSock = (VDSOCKET)pSocketInt;
648 return VINF_SUCCESS;
649 }
650
651 return VERR_NO_MEMORY;
652}
653
654/** @copydoc VDINTERFACETCPNET::pfnSocketCreate */
655static DECLCALLBACK(int) drvvdINIPSocketDestroy(VDSOCKET Sock)
656{
657 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
658
659 RTMemFree(pSocketInt);
660 return VINF_SUCCESS;
661}
662
663/** @copydoc VDINTERFACETCPNET::pfnClientConnect */
664static DECLCALLBACK(int) drvvdINIPClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
665{
666 int rc = VINF_SUCCESS;
667 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
668
669 /* Check whether lwIP is set up in this VM instance. */
670 if (!DevINIPConfigured())
671 {
672 LogRelFunc(("no IP stack\n"));
673 return VERR_NET_HOST_UNREACHABLE;
674 }
675 /* Resolve hostname. As there is no standard resolver for lwIP yet,
676 * just accept numeric IP addresses for now. */
677 struct in_addr ip;
678 if (!lwip_inet_aton(pszAddress, &ip))
679 {
680 LogRelFunc(("cannot resolve IP %s\n", pszAddress));
681 return VERR_NET_HOST_UNREACHABLE;
682 }
683 /* Create socket and connect. */
684 int iSock = lwip_socket(PF_INET, SOCK_STREAM, 0);
685 if (iSock != -1)
686 {
687 struct sockaddr_in InAddr = {0};
688 InAddr.sin_family = AF_INET;
689 InAddr.sin_port = htons(uPort);
690 InAddr.sin_addr = ip;
691 if (!lwip_connect(iSock, (struct sockaddr *)&InAddr, sizeof(InAddr)))
692 {
693 pSocketInt->hSock = iSock;
694 return VINF_SUCCESS;
695 }
696 rc = VERR_NET_CONNECTION_REFUSED; /* @todo real solution needed */
697 lwip_close(iSock);
698 }
699 else
700 rc = VERR_NET_CONNECTION_REFUSED; /* @todo real solution needed */
701 return rc;
702}
703
704/** @copydoc VDINTERFACETCPNET::pfnClientClose */
705static DECLCALLBACK(int) drvvdINIPClientClose(VDSOCKET Sock)
706{
707 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
708
709 lwip_close(pSocketInt->hSock);
710 pSocketInt->hSock = INT32_MAX;
711 return VINF_SUCCESS; /** @todo real solution needed */
712}
713
714/** @copydoc VDINTERFACETCPNET::pfnIsClientConnected */
715static DECLCALLBACK(bool) drvvdINIPIsClientConnected(VDSOCKET Sock)
716{
717 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
718
719 return pSocketInt->hSock != INT32_MAX;
720}
721
722/** @copydoc VDINTERFACETCPNET::pfnSelectOne */
723static DECLCALLBACK(int) drvvdINIPSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
724{
725 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
726 fd_set fdsetR;
727 FD_ZERO(&fdsetR);
728 FD_SET((uintptr_t)pSocketInt->hSock, &fdsetR);
729 fd_set fdsetE = fdsetR;
730
731 int rc;
732 if (cMillies == RT_INDEFINITE_WAIT)
733 rc = lwip_select(pSocketInt->hSock + 1, &fdsetR, NULL, &fdsetE, NULL);
734 else
735 {
736 struct timeval timeout;
737 timeout.tv_sec = cMillies / 1000;
738 timeout.tv_usec = (cMillies % 1000) * 1000;
739 rc = lwip_select(pSocketInt->hSock + 1, &fdsetR, NULL, &fdsetE, &timeout);
740 }
741 if (rc > 0)
742 return VINF_SUCCESS;
743 if (rc == 0)
744 return VERR_TIMEOUT;
745 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
746}
747
748/** @copydoc VDINTERFACETCPNET::pfnRead */
749static DECLCALLBACK(int) drvvdINIPRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
750{
751 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
752
753 /* Do params checking */
754 if (!pvBuffer || !cbBuffer)
755 {
756 AssertMsgFailed(("Invalid params\n"));
757 return VERR_INVALID_PARAMETER;
758 }
759
760 /*
761 * Read loop.
762 * If pcbRead is NULL we have to fill the entire buffer!
763 */
764 size_t cbRead = 0;
765 size_t cbToRead = cbBuffer;
766 for (;;)
767 {
768 /** @todo this clipping here is just in case (the send function
769 * needed it, so I added it here, too). Didn't investigate if this
770 * really has issues. Better be safe than sorry. */
771 ssize_t cbBytesRead = lwip_recv(pSocketInt->hSock, (char *)pvBuffer + cbRead,
772 RT_MIN(cbToRead, 32768), 0);
773 if (cbBytesRead < 0)
774 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
775 if (cbBytesRead == 0 && errno) /** @todo r=bird: lwip_recv will not touch errno on Windows. This may apply to other hosts as well */
776 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution */
777 if (pcbRead)
778 {
779 /* return partial data */
780 *pcbRead = cbBytesRead;
781 break;
782 }
783
784 /* read more? */
785 cbRead += cbBytesRead;
786 if (cbRead == cbBuffer)
787 break;
788
789 /* next */
790 cbToRead = cbBuffer - cbRead;
791 }
792
793 return VINF_SUCCESS;
794}
795
796/** @copydoc VDINTERFACETCPNET::pfnWrite */
797static DECLCALLBACK(int) drvvdINIPWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
798{
799 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
800
801 do
802 {
803 /** @todo lwip send only supports up to 65535 bytes in a single
804 * send (stupid limitation buried in the code), so make sure we
805 * don't get any wraparounds. This should be moved to DevINIP
806 * stack interface once that's implemented. */
807 ssize_t cbWritten = lwip_send(pSocketInt->hSock, (void *)pvBuffer,
808 RT_MIN(cbBuffer, 32768), 0);
809 if (cbWritten < 0)
810 return VERR_NET_CONNECTION_REFUSED; /** @todo real solution needed */
811 AssertMsg(cbBuffer >= (size_t)cbWritten, ("Wrote more than we requested!!! cbWritten=%d cbBuffer=%d\n",
812 cbWritten, cbBuffer));
813 cbBuffer -= cbWritten;
814 pvBuffer = (const char *)pvBuffer + cbWritten;
815 } while (cbBuffer);
816
817 return VINF_SUCCESS;
818}
819
820/** @copydoc VDINTERFACETCPNET::pfnSgWrite */
821static DECLCALLBACK(int) drvvdINIPSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
822{
823 int rc = VINF_SUCCESS;
824
825 /* This is an extremely crude emulation, however it's good enough
826 * for our iSCSI code. INIP has no sendmsg(). */
827 for (unsigned i = 0; i < pSgBuf->cSegs; i++)
828 {
829 rc = drvvdINIPWrite(Sock, pSgBuf->paSegs[i].pvSeg,
830 pSgBuf->paSegs[i].cbSeg);
831 if (RT_FAILURE(rc))
832 break;
833 }
834 if (RT_SUCCESS(rc))
835 drvvdINIPFlush(Sock);
836
837 return rc;
838}
839
840/** @copydoc VDINTERFACETCPNET::pfnFlush */
841static DECLCALLBACK(int) drvvdINIPFlush(VDSOCKET Sock)
842{
843 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
844
845 int fFlag = 1;
846 lwip_setsockopt(pSocketInt->hSock, IPPROTO_TCP, TCP_NODELAY,
847 (const char *)&fFlag, sizeof(fFlag));
848 fFlag = 0;
849 lwip_setsockopt(pSocketInt->hSock, IPPROTO_TCP, TCP_NODELAY,
850 (const char *)&fFlag, sizeof(fFlag));
851 return VINF_SUCCESS;
852}
853
854/** @copydoc VDINTERFACETCPNET::pfnSetSendCoalescing */
855static DECLCALLBACK(int) drvvdINIPSetSendCoalescing(VDSOCKET Sock, bool fEnable)
856{
857 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
858
859 int fFlag = fEnable ? 0 : 1;
860 lwip_setsockopt(pSocketInt->hSock, IPPROTO_TCP, TCP_NODELAY,
861 (const char *)&fFlag, sizeof(fFlag));
862 return VINF_SUCCESS;
863}
864
865/** @copydoc VDINTERFACETCPNET::pfnGetLocalAddress */
866static DECLCALLBACK(int) drvvdINIPGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
867{
868 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
869 INIPSOCKADDRUNION u;
870 socklen_t cbAddr = sizeof(u);
871 RT_ZERO(u);
872 if (!lwip_getsockname(pSocketInt->hSock, &u.Addr, &cbAddr))
873 {
874 /*
875 * Convert the address.
876 */
877 if ( cbAddr == sizeof(struct sockaddr_in)
878 && u.Addr.sa_family == AF_INET)
879 {
880 RT_ZERO(*pAddr);
881 pAddr->enmType = RTNETADDRTYPE_IPV4;
882 pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port);
883 pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr;
884 }
885 else
886 return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
887 return VINF_SUCCESS;
888 }
889 return VERR_NET_OPERATION_NOT_SUPPORTED;
890}
891
892/** @copydoc VDINTERFACETCPNET::pfnGetPeerAddress */
893static DECLCALLBACK(int) drvvdINIPGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
894{
895 PINIPSOCKET pSocketInt = (PINIPSOCKET)Sock;
896 INIPSOCKADDRUNION u;
897 socklen_t cbAddr = sizeof(u);
898 RT_ZERO(u);
899 if (!lwip_getpeername(pSocketInt->hSock, &u.Addr, &cbAddr))
900 {
901 /*
902 * Convert the address.
903 */
904 if ( cbAddr == sizeof(struct sockaddr_in)
905 && u.Addr.sa_family == AF_INET)
906 {
907 RT_ZERO(*pAddr);
908 pAddr->enmType = RTNETADDRTYPE_IPV4;
909 pAddr->uPort = RT_N2H_U16(u.Ipv4.sin_port);
910 pAddr->uAddr.IPv4.u = u.Ipv4.sin_addr.s_addr;
911 }
912 else
913 return VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED;
914 return VINF_SUCCESS;
915 }
916 return VERR_NET_OPERATION_NOT_SUPPORTED;
917}
918
919/** @copydoc VDINTERFACETCPNET::pfnSelectOneEx */
920static DECLCALLBACK(int) drvvdINIPSelectOneEx(VDSOCKET Sock, uint32_t fEvents, uint32_t *pfEvents, RTMSINTERVAL cMillies)
921{
922 AssertMsgFailed(("Not supported!\n"));
923 return VERR_NOT_SUPPORTED;
924}
925
926/** @copydoc VDINTERFACETCPNET::pfnPoke */
927static DECLCALLBACK(int) drvvdINIPPoke(VDSOCKET Sock)
928{
929 AssertMsgFailed(("Not supported!\n"));
930 return VERR_NOT_SUPPORTED;
931}
932
933#endif /* VBOX_WITH_INIP */
934
935
936/*******************************************************************************
937* VD TCP network stack interface implementation - Host TCP case *
938*******************************************************************************/
939
940/**
941 * Socket data.
942 */
943typedef struct VDSOCKETINT
944{
945 /** IPRT socket handle. */
946 RTSOCKET hSocket;
947 /** Pollset with the wakeup pipe and socket. */
948 RTPOLLSET hPollSet;
949 /** Pipe endpoint - read (in the pollset). */
950 RTPIPE hPipeR;
951 /** Pipe endpoint - write. */
952 RTPIPE hPipeW;
953 /** Flag whether the thread was woken up. */
954 volatile bool fWokenUp;
955 /** Flag whether the thread is waiting in the select call. */
956 volatile bool fWaiting;
957 /** Old event mask. */
958 uint32_t fEventsOld;
959} VDSOCKETINT, *PVDSOCKETINT;
960
961/** Pollset id of the socket. */
962#define VDSOCKET_POLL_ID_SOCKET 0
963/** Pollset id of the pipe. */
964#define VDSOCKET_POLL_ID_PIPE 1
965
966/** @copydoc VDINTERFACETCPNET::pfnSocketCreate */
967static DECLCALLBACK(int) drvvdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
968{
969 int rc = VINF_SUCCESS;
970 int rc2 = VINF_SUCCESS;
971 PVDSOCKETINT pSockInt = NULL;
972
973 pSockInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
974 if (!pSockInt)
975 return VERR_NO_MEMORY;
976
977 pSockInt->hSocket = NIL_RTSOCKET;
978 pSockInt->hPollSet = NIL_RTPOLLSET;
979 pSockInt->hPipeR = NIL_RTPIPE;
980 pSockInt->hPipeW = NIL_RTPIPE;
981 pSockInt->fWokenUp = false;
982 pSockInt->fWaiting = false;
983
984 if (fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT)
985 {
986 /* Init pipe and pollset. */
987 rc = RTPipeCreate(&pSockInt->hPipeR, &pSockInt->hPipeW, 0);
988 if (RT_SUCCESS(rc))
989 {
990 rc = RTPollSetCreate(&pSockInt->hPollSet);
991 if (RT_SUCCESS(rc))
992 {
993 rc = RTPollSetAddPipe(pSockInt->hPollSet, pSockInt->hPipeR,
994 RTPOLL_EVT_READ, VDSOCKET_POLL_ID_PIPE);
995 if (RT_SUCCESS(rc))
996 {
997 *pSock = pSockInt;
998 return VINF_SUCCESS;
999 }
1000
1001 RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_PIPE);
1002 rc2 = RTPollSetDestroy(pSockInt->hPollSet);
1003 AssertRC(rc2);
1004 }
1005
1006 rc2 = RTPipeClose(pSockInt->hPipeR);
1007 AssertRC(rc2);
1008 rc2 = RTPipeClose(pSockInt->hPipeW);
1009 AssertRC(rc2);
1010 }
1011 }
1012 else
1013 {
1014 *pSock = pSockInt;
1015 return VINF_SUCCESS;
1016 }
1017
1018 RTMemFree(pSockInt);
1019
1020 return rc;
1021}
1022
1023/** @copydoc VDINTERFACETCPNET::pfnSocketDestroy */
1024static DECLCALLBACK(int) drvvdTcpSocketDestroy(VDSOCKET Sock)
1025{
1026 int rc = VINF_SUCCESS;
1027 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1028
1029 /* Destroy the pipe and pollset if necessary. */
1030 if (pSockInt->hPollSet != NIL_RTPOLLSET)
1031 {
1032 if (pSockInt->hSocket != NIL_RTSOCKET)
1033 {
1034 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET);
1035 Assert(RT_SUCCESS(rc) || rc == VERR_POLL_HANDLE_ID_NOT_FOUND);
1036 }
1037 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_PIPE);
1038 AssertRC(rc);
1039 rc = RTPollSetDestroy(pSockInt->hPollSet);
1040 AssertRC(rc);
1041 rc = RTPipeClose(pSockInt->hPipeR);
1042 AssertRC(rc);
1043 rc = RTPipeClose(pSockInt->hPipeW);
1044 AssertRC(rc);
1045 }
1046
1047 if (pSockInt->hSocket != NIL_RTSOCKET)
1048 rc = RTTcpClientCloseEx(pSockInt->hSocket, false /*fGracefulShutdown*/);
1049
1050 RTMemFree(pSockInt);
1051
1052 return rc;
1053}
1054
1055/** @copydoc VDINTERFACETCPNET::pfnClientConnect */
1056static DECLCALLBACK(int) drvvdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
1057{
1058 int rc = VINF_SUCCESS;
1059 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1060
1061 rc = RTTcpClientConnect(pszAddress, uPort, &pSockInt->hSocket);
1062 if (RT_SUCCESS(rc))
1063 {
1064 /* Add to the pollset if required. */
1065 if (pSockInt->hPollSet != NIL_RTPOLLSET)
1066 {
1067 pSockInt->fEventsOld = RTPOLL_EVT_READ | RTPOLL_EVT_WRITE | RTPOLL_EVT_ERROR;
1068
1069 rc = RTPollSetAddSocket(pSockInt->hPollSet, pSockInt->hSocket,
1070 pSockInt->fEventsOld, VDSOCKET_POLL_ID_SOCKET);
1071 }
1072
1073 if (RT_SUCCESS(rc))
1074 return VINF_SUCCESS;
1075
1076 rc = RTTcpClientCloseEx(pSockInt->hSocket, false /*fGracefulShutdown*/);
1077 }
1078
1079 return rc;
1080}
1081
1082/** @copydoc VDINTERFACETCPNET::pfnClientClose */
1083static DECLCALLBACK(int) drvvdTcpClientClose(VDSOCKET Sock)
1084{
1085 int rc = VINF_SUCCESS;
1086 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1087
1088 if (pSockInt->hPollSet != NIL_RTPOLLSET)
1089 {
1090 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET);
1091 AssertRC(rc);
1092 }
1093
1094 rc = RTTcpClientCloseEx(pSockInt->hSocket, false /*fGracefulShutdown*/);
1095 pSockInt->hSocket = NIL_RTSOCKET;
1096
1097 return rc;
1098}
1099
1100/** @copydoc VDINTERFACETCPNET::pfnIsClientConnected */
1101static DECLCALLBACK(bool) drvvdTcpIsClientConnected(VDSOCKET Sock)
1102{
1103 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1104
1105 return pSockInt->hSocket != NIL_RTSOCKET;
1106}
1107
1108/** @copydoc VDINTERFACETCPNET::pfnSelectOne */
1109static DECLCALLBACK(int) drvvdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
1110{
1111 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1112
1113 return RTTcpSelectOne(pSockInt->hSocket, cMillies);
1114}
1115
1116/** @copydoc VDINTERFACETCPNET::pfnRead */
1117static DECLCALLBACK(int) drvvdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
1118{
1119 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1120
1121 return RTTcpRead(pSockInt->hSocket, pvBuffer, cbBuffer, pcbRead);
1122}
1123
1124/** @copydoc VDINTERFACETCPNET::pfnWrite */
1125static DECLCALLBACK(int) drvvdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
1126{
1127 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1128
1129 return RTTcpWrite(pSockInt->hSocket, pvBuffer, cbBuffer);
1130}
1131
1132/** @copydoc VDINTERFACETCPNET::pfnSgWrite */
1133static DECLCALLBACK(int) drvvdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
1134{
1135 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1136
1137 return RTTcpSgWrite(pSockInt->hSocket, pSgBuf);
1138}
1139
1140/** @copydoc VDINTERFACETCPNET::pfnReadNB */
1141static DECLCALLBACK(int) drvvdTcpReadNB(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
1142{
1143 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1144
1145 return RTTcpReadNB(pSockInt->hSocket, pvBuffer, cbBuffer, pcbRead);
1146}
1147
1148/** @copydoc VDINTERFACETCPNET::pfnWriteNB */
1149static DECLCALLBACK(int) drvvdTcpWriteNB(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten)
1150{
1151 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1152
1153 return RTTcpWriteNB(pSockInt->hSocket, pvBuffer, cbBuffer, pcbWritten);
1154}
1155
1156/** @copydoc VDINTERFACETCPNET::pfnSgWriteNB */
1157static DECLCALLBACK(int) drvvdTcpSgWriteNB(VDSOCKET Sock, PRTSGBUF pSgBuf, size_t *pcbWritten)
1158{
1159 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1160
1161 return RTTcpSgWriteNB(pSockInt->hSocket, pSgBuf, pcbWritten);
1162}
1163
1164/** @copydoc VDINTERFACETCPNET::pfnFlush */
1165static DECLCALLBACK(int) drvvdTcpFlush(VDSOCKET Sock)
1166{
1167 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1168
1169 return RTTcpFlush(pSockInt->hSocket);
1170}
1171
1172/** @copydoc VDINTERFACETCPNET::pfnSetSendCoalescing */
1173static DECLCALLBACK(int) drvvdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
1174{
1175 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1176
1177 return RTTcpSetSendCoalescing(pSockInt->hSocket, fEnable);
1178}
1179
1180/** @copydoc VDINTERFACETCPNET::pfnGetLocalAddress */
1181static DECLCALLBACK(int) drvvdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
1182{
1183 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1184
1185 return RTTcpGetLocalAddress(pSockInt->hSocket, pAddr);
1186}
1187
1188/** @copydoc VDINTERFACETCPNET::pfnGetPeerAddress */
1189static DECLCALLBACK(int) drvvdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
1190{
1191 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1192
1193 return RTTcpGetPeerAddress(pSockInt->hSocket, pAddr);
1194}
1195
1196static int drvvdTcpSelectOneExPoll(VDSOCKET Sock, uint32_t fEvents,
1197 uint32_t *pfEvents, RTMSINTERVAL cMillies)
1198{
1199 int rc = VINF_SUCCESS;
1200 uint32_t id = 0;
1201 uint32_t fEventsRecv = 0;
1202 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1203
1204 *pfEvents = 0;
1205
1206 if ( pSockInt->fEventsOld != fEvents
1207 && pSockInt->hSocket != NIL_RTSOCKET)
1208 {
1209 uint32_t fPollEvents = 0;
1210
1211 if (fEvents & VD_INTERFACETCPNET_EVT_READ)
1212 fPollEvents |= RTPOLL_EVT_READ;
1213 if (fEvents & VD_INTERFACETCPNET_EVT_WRITE)
1214 fPollEvents |= RTPOLL_EVT_WRITE;
1215 if (fEvents & VD_INTERFACETCPNET_EVT_ERROR)
1216 fPollEvents |= RTPOLL_EVT_ERROR;
1217
1218 rc = RTPollSetEventsChange(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET, fPollEvents);
1219 if (RT_FAILURE(rc))
1220 return rc;
1221
1222 pSockInt->fEventsOld = fEvents;
1223 }
1224
1225 ASMAtomicXchgBool(&pSockInt->fWaiting, true);
1226 if (ASMAtomicXchgBool(&pSockInt->fWokenUp, false))
1227 {
1228 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1229 return VERR_INTERRUPTED;
1230 }
1231
1232 rc = RTPoll(pSockInt->hPollSet, cMillies, &fEventsRecv, &id);
1233 Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT);
1234
1235 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1236
1237 if (RT_SUCCESS(rc))
1238 {
1239 if (id == VDSOCKET_POLL_ID_SOCKET)
1240 {
1241 fEventsRecv &= RTPOLL_EVT_VALID_MASK;
1242
1243 if (fEventsRecv & RTPOLL_EVT_READ)
1244 *pfEvents |= VD_INTERFACETCPNET_EVT_READ;
1245 if (fEventsRecv & RTPOLL_EVT_WRITE)
1246 *pfEvents |= VD_INTERFACETCPNET_EVT_WRITE;
1247 if (fEventsRecv & RTPOLL_EVT_ERROR)
1248 *pfEvents |= VD_INTERFACETCPNET_EVT_ERROR;
1249 }
1250 else
1251 {
1252 size_t cbRead = 0;
1253 uint8_t abBuf[10];
1254 Assert(id == VDSOCKET_POLL_ID_PIPE);
1255 Assert((fEventsRecv & RTPOLL_EVT_VALID_MASK) == RTPOLL_EVT_READ);
1256
1257 /* We got interrupted, drain the pipe. */
1258 rc = RTPipeRead(pSockInt->hPipeR, abBuf, sizeof(abBuf), &cbRead);
1259 AssertRC(rc);
1260
1261 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1262
1263 rc = VERR_INTERRUPTED;
1264 }
1265 }
1266
1267 return rc;
1268}
1269
1270/** @copydoc VDINTERFACETCPNET::pfnSelectOneEx */
1271static DECLCALLBACK(int) drvvdTcpSelectOneExNoPoll(VDSOCKET Sock, uint32_t fEvents,
1272 uint32_t *pfEvents, RTMSINTERVAL cMillies)
1273{
1274 int rc = VINF_SUCCESS;
1275 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1276
1277 *pfEvents = 0;
1278
1279 ASMAtomicXchgBool(&pSockInt->fWaiting, true);
1280 if (ASMAtomicXchgBool(&pSockInt->fWokenUp, false))
1281 {
1282 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1283 return VERR_INTERRUPTED;
1284 }
1285
1286 if ( pSockInt->hSocket == NIL_RTSOCKET
1287 || !fEvents)
1288 {
1289 /*
1290 * Only the pipe is configured or the caller doesn't wait for a socket event,
1291 * wait until there is something to read from the pipe.
1292 */
1293 size_t cbRead = 0;
1294 char ch = 0;
1295 rc = RTPipeReadBlocking(pSockInt->hPipeR, &ch, 1, &cbRead);
1296 if (RT_SUCCESS(rc))
1297 {
1298 Assert(cbRead == 1);
1299 rc = VERR_INTERRUPTED;
1300 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1301 }
1302 }
1303 else
1304 {
1305 uint32_t fSelectEvents = 0;
1306
1307 if (fEvents & VD_INTERFACETCPNET_EVT_READ)
1308 fSelectEvents |= RTSOCKET_EVT_READ;
1309 if (fEvents & VD_INTERFACETCPNET_EVT_WRITE)
1310 fSelectEvents |= RTSOCKET_EVT_WRITE;
1311 if (fEvents & VD_INTERFACETCPNET_EVT_ERROR)
1312 fSelectEvents |= RTSOCKET_EVT_ERROR;
1313
1314 if (fEvents & VD_INTERFACETCPNET_HINT_INTERRUPT)
1315 {
1316 uint32_t fEventsRecv = 0;
1317
1318 /* Make sure the socket is not in the pollset. */
1319 rc = RTPollSetRemove(pSockInt->hPollSet, VDSOCKET_POLL_ID_SOCKET);
1320 Assert(RT_SUCCESS(rc) || rc == VERR_POLL_HANDLE_ID_NOT_FOUND);
1321
1322 for (;;)
1323 {
1324 uint32_t id = 0;
1325 rc = RTPoll(pSockInt->hPollSet, 5, &fEvents, &id);
1326 if (rc == VERR_TIMEOUT)
1327 {
1328 /* Check the socket. */
1329 rc = RTTcpSelectOneEx(pSockInt->hSocket, fSelectEvents, &fEventsRecv, 0);
1330 if (RT_SUCCESS(rc))
1331 {
1332 if (fEventsRecv & RTSOCKET_EVT_READ)
1333 *pfEvents |= VD_INTERFACETCPNET_EVT_READ;
1334 if (fEventsRecv & RTSOCKET_EVT_WRITE)
1335 *pfEvents |= VD_INTERFACETCPNET_EVT_WRITE;
1336 if (fEventsRecv & RTSOCKET_EVT_ERROR)
1337 *pfEvents |= VD_INTERFACETCPNET_EVT_ERROR;
1338 break; /* Quit */
1339 }
1340 else if (rc != VERR_TIMEOUT)
1341 break;
1342 }
1343 else if (RT_SUCCESS(rc))
1344 {
1345 size_t cbRead = 0;
1346 uint8_t abBuf[10];
1347 Assert(id == VDSOCKET_POLL_ID_PIPE);
1348 Assert((fEventsRecv & RTPOLL_EVT_VALID_MASK) == RTPOLL_EVT_READ);
1349
1350 /* We got interrupted, drain the pipe. */
1351 rc = RTPipeRead(pSockInt->hPipeR, abBuf, sizeof(abBuf), &cbRead);
1352 AssertRC(rc);
1353
1354 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1355
1356 rc = VERR_INTERRUPTED;
1357 break;
1358 }
1359 else
1360 break;
1361 }
1362 }
1363 else /* The caller waits for a socket event. */
1364 {
1365 uint32_t fEventsRecv = 0;
1366
1367 /* Loop until we got woken up or a socket event occurred. */
1368 for (;;)
1369 {
1370 /** @todo find an adaptive wait algorithm based on the
1371 * number of wakeups in the past. */
1372 rc = RTTcpSelectOneEx(pSockInt->hSocket, fSelectEvents, &fEventsRecv, 5);
1373 if (rc == VERR_TIMEOUT)
1374 {
1375 /* Check if there is an event pending. */
1376 size_t cbRead = 0;
1377 char ch = 0;
1378 rc = RTPipeRead(pSockInt->hPipeR, &ch, 1, &cbRead);
1379 if (RT_SUCCESS(rc) && rc != VINF_TRY_AGAIN)
1380 {
1381 Assert(cbRead == 1);
1382 rc = VERR_INTERRUPTED;
1383 ASMAtomicXchgBool(&pSockInt->fWokenUp, false);
1384 break; /* Quit */
1385 }
1386 else
1387 Assert(rc == VINF_TRY_AGAIN);
1388 }
1389 else if (RT_SUCCESS(rc))
1390 {
1391 if (fEventsRecv & RTSOCKET_EVT_READ)
1392 *pfEvents |= VD_INTERFACETCPNET_EVT_READ;
1393 if (fEventsRecv & RTSOCKET_EVT_WRITE)
1394 *pfEvents |= VD_INTERFACETCPNET_EVT_WRITE;
1395 if (fEventsRecv & RTSOCKET_EVT_ERROR)
1396 *pfEvents |= VD_INTERFACETCPNET_EVT_ERROR;
1397 break; /* Quit */
1398 }
1399 else
1400 break;
1401 }
1402 }
1403 }
1404
1405 ASMAtomicXchgBool(&pSockInt->fWaiting, false);
1406
1407 return rc;
1408}
1409
1410/** @copydoc VDINTERFACETCPNET::pfnPoke */
1411static DECLCALLBACK(int) drvvdTcpPoke(VDSOCKET Sock)
1412{
1413 int rc = VINF_SUCCESS;
1414 size_t cbWritten = 0;
1415 PVDSOCKETINT pSockInt = (PVDSOCKETINT)Sock;
1416
1417 ASMAtomicXchgBool(&pSockInt->fWokenUp, true);
1418
1419 if (ASMAtomicReadBool(&pSockInt->fWaiting))
1420 {
1421 rc = RTPipeWrite(pSockInt->hPipeW, "", 1, &cbWritten);
1422 Assert(RT_SUCCESS(rc) || cbWritten == 0);
1423 }
1424
1425 return VINF_SUCCESS;
1426}
1427
1428
1429/*******************************************************************************
1430* Media interface methods *
1431*******************************************************************************/
1432
1433/** @copydoc PDMIMEDIA::pfnRead */
1434static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
1435 uint64_t off, void *pvBuf, size_t cbRead)
1436{
1437 int rc = VINF_SUCCESS;
1438
1439 LogFlowFunc(("off=%#llx pvBuf=%p cbRead=%d\n", off, pvBuf, cbRead));
1440 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1441
1442 if (!pThis->fBootAccelActive)
1443 rc = VDRead(pThis->pDisk, off, pvBuf, cbRead);
1444 else
1445 {
1446 /* Can we serve the request from the buffer? */
1447 if ( off >= pThis->offDisk
1448 && off - pThis->offDisk < pThis->cbDataValid)
1449 {
1450 size_t cbToCopy = RT_MIN(cbRead, pThis->offDisk + pThis->cbDataValid - off);
1451
1452 memcpy(pvBuf, pThis->pbData + (off - pThis->offDisk), cbToCopy);
1453 cbRead -= cbToCopy;
1454 off += cbToCopy;
1455 pvBuf = (char *)pvBuf + cbToCopy;
1456 }
1457
1458 if ( cbRead > 0
1459 && cbRead < pThis->cbBootAccelBuffer)
1460 {
1461 /* Increase request to the buffer size and read. */
1462 pThis->cbDataValid = RT_MIN(pThis->cbDisk - off, pThis->cbBootAccelBuffer);
1463 pThis->offDisk = off;
1464 rc = VDRead(pThis->pDisk, off, pThis->pbData, pThis->cbDataValid);
1465 if (RT_FAILURE(rc))
1466 pThis->cbDataValid = 0;
1467 else
1468 memcpy(pvBuf, pThis->pbData, cbRead);
1469 }
1470 else if (cbRead >= pThis->cbBootAccelBuffer)
1471 {
1472 pThis->fBootAccelActive = false; /* Deactiviate */
1473 }
1474 }
1475
1476 if (RT_SUCCESS(rc))
1477 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Rhxd\n", __FUNCTION__,
1478 off, pvBuf, cbRead, cbRead, pvBuf));
1479 LogFlowFunc(("returns %Rrc\n", rc));
1480 return rc;
1481}
1482
1483/** @copydoc PDMIMEDIA::pfnWrite */
1484static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
1485 uint64_t off, const void *pvBuf,
1486 size_t cbWrite)
1487{
1488 LogFlowFunc(("off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite));
1489 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1490 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Rhxd\n", __FUNCTION__,
1491 off, pvBuf, cbWrite, cbWrite, pvBuf));
1492
1493 /* Invalidate any buffer if boot acceleration is enabled. */
1494 if (pThis->fBootAccelActive)
1495 {
1496 pThis->cbDataValid = 0;
1497 pThis->offDisk = 0;
1498 }
1499
1500 int rc = VDWrite(pThis->pDisk, off, pvBuf, cbWrite);
1501 LogFlowFunc(("returns %Rrc\n", rc));
1502 return rc;
1503}
1504
1505/** @copydoc PDMIMEDIA::pfnFlush */
1506static DECLCALLBACK(int) drvvdFlush(PPDMIMEDIA pInterface)
1507{
1508 LogFlowFunc(("\n"));
1509 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1510 int rc = VDFlush(pThis->pDisk);
1511 LogFlowFunc(("returns %Rrc\n", rc));
1512 return rc;
1513}
1514
1515/** @copydoc PDMIMEDIA::pfnMerge */
1516static DECLCALLBACK(int) drvvdMerge(PPDMIMEDIA pInterface,
1517 PFNSIMPLEPROGRESS pfnProgress,
1518 void *pvUser)
1519{
1520 LogFlowFunc(("\n"));
1521 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1522 int rc = VINF_SUCCESS;
1523
1524 /* Note: There is an unavoidable race between destruction and another
1525 * thread invoking this function. This is handled safely and gracefully by
1526 * atomically invalidating the lock handle in drvvdDestruct. */
1527 int rc2 = RTSemFastMutexRequest(pThis->MergeCompleteMutex);
1528 AssertRC(rc2);
1529 if (RT_SUCCESS(rc2) && pThis->fMergePending)
1530 {
1531 /* Take shortcut: PFNSIMPLEPROGRESS is exactly the same type as
1532 * PFNVDPROGRESS, so there's no need for a conversion function. */
1533 /** @todo maybe introduce a conversion which limits update frequency. */
1534 PVDINTERFACE pVDIfsOperation = NULL;
1535 VDINTERFACEPROGRESS VDIfProgress;
1536 VDIfProgress.pfnProgress = pfnProgress;
1537 rc2 = VDInterfaceAdd(&VDIfProgress.Core, "DrvVD_VDIProgress", VDINTERFACETYPE_PROGRESS,
1538 pvUser, sizeof(VDINTERFACEPROGRESS), &pVDIfsOperation);
1539 AssertRC(rc2);
1540 pThis->fMergePending = false;
1541 rc = VDMerge(pThis->pDisk, pThis->uMergeSource,
1542 pThis->uMergeTarget, pVDIfsOperation);
1543 }
1544 rc2 = RTSemFastMutexRelease(pThis->MergeCompleteMutex);
1545 AssertRC(rc2);
1546 LogFlowFunc(("returns %Rrc\n", rc));
1547 return rc;
1548}
1549
1550/** @copydoc PDMIMEDIA::pfnGetSize */
1551static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
1552{
1553 LogFlowFunc(("\n"));
1554 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1555 uint64_t cb = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
1556 LogFlowFunc(("returns %#llx (%llu)\n", cb, cb));
1557 return cb;
1558}
1559
1560/** @copydoc PDMIMEDIA::pfnIsReadOnly */
1561static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
1562{
1563 LogFlowFunc(("\n"));
1564 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1565 bool f = VDIsReadOnly(pThis->pDisk);
1566 LogFlowFunc(("returns %d\n", f));
1567 return f;
1568}
1569
1570/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
1571static DECLCALLBACK(int) drvvdBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
1572 PPDMMEDIAGEOMETRY pPCHSGeometry)
1573{
1574 LogFlowFunc(("\n"));
1575 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1576 VDGEOMETRY geo;
1577 int rc = VDGetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1578 if (RT_SUCCESS(rc))
1579 {
1580 pPCHSGeometry->cCylinders = geo.cCylinders;
1581 pPCHSGeometry->cHeads = geo.cHeads;
1582 pPCHSGeometry->cSectors = geo.cSectors;
1583 }
1584 else
1585 {
1586 LogFunc(("geometry not available.\n"));
1587 rc = VERR_PDM_GEOMETRY_NOT_SET;
1588 }
1589 LogFlowFunc(("returns %Rrc (CHS=%d/%d/%d)\n",
1590 rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1591 return rc;
1592}
1593
1594/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
1595static DECLCALLBACK(int) drvvdBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
1596 PCPDMMEDIAGEOMETRY pPCHSGeometry)
1597{
1598 LogFlowFunc(("CHS=%d/%d/%d\n",
1599 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1600 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1601 VDGEOMETRY geo;
1602 geo.cCylinders = pPCHSGeometry->cCylinders;
1603 geo.cHeads = pPCHSGeometry->cHeads;
1604 geo.cSectors = pPCHSGeometry->cSectors;
1605 int rc = VDSetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1606 if (rc == VERR_VD_GEOMETRY_NOT_SET)
1607 rc = VERR_PDM_GEOMETRY_NOT_SET;
1608 LogFlowFunc(("returns %Rrc\n", rc));
1609 return rc;
1610}
1611
1612/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
1613static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
1614 PPDMMEDIAGEOMETRY pLCHSGeometry)
1615{
1616 LogFlowFunc(("\n"));
1617 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1618 VDGEOMETRY geo;
1619 int rc = VDGetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1620 if (RT_SUCCESS(rc))
1621 {
1622 pLCHSGeometry->cCylinders = geo.cCylinders;
1623 pLCHSGeometry->cHeads = geo.cHeads;
1624 pLCHSGeometry->cSectors = geo.cSectors;
1625 }
1626 else
1627 {
1628 LogFunc(("geometry not available.\n"));
1629 rc = VERR_PDM_GEOMETRY_NOT_SET;
1630 }
1631 LogFlowFunc(("returns %Rrc (CHS=%d/%d/%d)\n",
1632 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1633 return rc;
1634}
1635
1636/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
1637static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
1638 PCPDMMEDIAGEOMETRY pLCHSGeometry)
1639{
1640 LogFlowFunc(("CHS=%d/%d/%d\n",
1641 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1642 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1643 VDGEOMETRY geo;
1644 geo.cCylinders = pLCHSGeometry->cCylinders;
1645 geo.cHeads = pLCHSGeometry->cHeads;
1646 geo.cSectors = pLCHSGeometry->cSectors;
1647 int rc = VDSetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, &geo);
1648 if (rc == VERR_VD_GEOMETRY_NOT_SET)
1649 rc = VERR_PDM_GEOMETRY_NOT_SET;
1650 LogFlowFunc(("returns %Rrc\n", rc));
1651 return rc;
1652}
1653
1654/** @copydoc PDMIMEDIA::pfnGetUuid */
1655static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
1656{
1657 LogFlowFunc(("\n"));
1658 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1659 int rc = VDGetUuid(pThis->pDisk, 0, pUuid);
1660 LogFlowFunc(("returns %Rrc ({%RTuuid})\n", rc, pUuid));
1661 return rc;
1662}
1663
1664static DECLCALLBACK(int) drvvdDiscard(PPDMIMEDIA pInterface, PCRTRANGE paRanges, unsigned cRanges)
1665{
1666 LogFlowFunc(("\n"));
1667 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
1668
1669 int rc = VDDiscardRanges(pThis->pDisk, paRanges, cRanges);
1670 LogFlowFunc(("returns %Rrc\n", rc));
1671 return rc;
1672}
1673
1674/*******************************************************************************
1675* Async Media interface methods *
1676*******************************************************************************/
1677
1678static void drvvdAsyncReqComplete(void *pvUser1, void *pvUser2, int rcReq)
1679{
1680 PVBOXDISK pThis = (PVBOXDISK)pvUser1;
1681
1682 if (!pThis->pBlkCache)
1683 {
1684 int rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort,
1685 pvUser2, rcReq);
1686 AssertRC(rc);
1687 }
1688 else
1689 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, (PPDMBLKCACHEIOXFER)pvUser2, rcReq);
1690}
1691
1692static DECLCALLBACK(int) drvvdStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
1693 PCRTSGSEG paSeg, unsigned cSeg,
1694 size_t cbRead, void *pvUser)
1695{
1696 LogFlowFunc(("uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d pvUser=%#p\n",
1697 uOffset, paSeg, cSeg, cbRead, pvUser));
1698 int rc = VINF_SUCCESS;
1699 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
1700
1701 pThis->fBootAccelActive = false;
1702
1703 RTSGBUF SgBuf;
1704 RTSgBufInit(&SgBuf, paSeg, cSeg);
1705 if (!pThis->pBlkCache)
1706 rc = VDAsyncRead(pThis->pDisk, uOffset, cbRead, &SgBuf,
1707 drvvdAsyncReqComplete, pThis, pvUser);
1708 else
1709 {
1710 rc = PDMR3BlkCacheRead(pThis->pBlkCache, uOffset, &SgBuf, cbRead, pvUser);
1711 if (rc == VINF_AIO_TASK_PENDING)
1712 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1713 else if (rc == VINF_SUCCESS)
1714 rc = VINF_VD_ASYNC_IO_FINISHED;
1715 }
1716
1717 LogFlowFunc(("returns %Rrc\n", rc));
1718 return rc;
1719}
1720
1721static DECLCALLBACK(int) drvvdStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
1722 PCRTSGSEG paSeg, unsigned cSeg,
1723 size_t cbWrite, void *pvUser)
1724{
1725 LogFlowFunc(("uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d pvUser=%#p\n",
1726 uOffset, paSeg, cSeg, cbWrite, pvUser));
1727 int rc = VINF_SUCCESS;
1728 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
1729
1730 pThis->fBootAccelActive = false;
1731
1732 RTSGBUF SgBuf;
1733 RTSgBufInit(&SgBuf, paSeg, cSeg);
1734
1735 if (!pThis->pBlkCache)
1736 rc = VDAsyncWrite(pThis->pDisk, uOffset, cbWrite, &SgBuf,
1737 drvvdAsyncReqComplete, pThis, pvUser);
1738 else
1739 {
1740 rc = PDMR3BlkCacheWrite(pThis->pBlkCache, uOffset, &SgBuf, cbWrite, pvUser);
1741 if (rc == VINF_AIO_TASK_PENDING)
1742 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1743 else if (rc == VINF_SUCCESS)
1744 rc = VINF_VD_ASYNC_IO_FINISHED;
1745 }
1746
1747 LogFlowFunc(("returns %Rrc\n", rc));
1748 return rc;
1749}
1750
1751static DECLCALLBACK(int) drvvdStartFlush(PPDMIMEDIAASYNC pInterface, void *pvUser)
1752{
1753 LogFlowFunc(("pvUser=%#p\n", pvUser));
1754 int rc = VINF_SUCCESS;
1755 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
1756
1757 if (!pThis->pBlkCache)
1758 rc = VDAsyncFlush(pThis->pDisk, drvvdAsyncReqComplete, pThis, pvUser);
1759 else
1760 {
1761 rc = PDMR3BlkCacheFlush(pThis->pBlkCache, pvUser);
1762 if (rc == VINF_AIO_TASK_PENDING)
1763 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1764 else if (rc == VINF_SUCCESS)
1765 rc = VINF_VD_ASYNC_IO_FINISHED;
1766 }
1767 LogFlowFunc(("returns %Rrc\n", rc));
1768 return rc;
1769}
1770
1771static DECLCALLBACK(int) drvvdStartDiscard(PPDMIMEDIAASYNC pInterface, PCRTRANGE paRanges,
1772 unsigned cRanges, void *pvUser)
1773{
1774 int rc = VINF_SUCCESS;
1775 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
1776
1777 LogFlowFunc(("paRanges=%#p cRanges=%u pvUser=%#p\n",
1778 paRanges, cRanges, pvUser));
1779
1780 if (!pThis->pBlkCache)
1781 rc = VDAsyncDiscardRanges(pThis->pDisk, paRanges, cRanges, drvvdAsyncReqComplete,
1782 pThis, pvUser);
1783 else
1784 {
1785 rc = PDMR3BlkCacheDiscard(pThis->pBlkCache, paRanges, cRanges, pvUser);
1786 if (rc == VINF_AIO_TASK_PENDING)
1787 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1788 else if (rc == VINF_SUCCESS)
1789 rc = VINF_VD_ASYNC_IO_FINISHED;
1790 }
1791 LogFlowFunc(("returns %Rrc\n", rc));
1792 return rc;
1793}
1794
1795/** @copydoc FNPDMBLKCACHEXFERCOMPLETEDRV */
1796static void drvvdBlkCacheXferComplete(PPDMDRVINS pDrvIns, void *pvUser, int rcReq)
1797{
1798 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1799
1800 int rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort,
1801 pvUser, rcReq);
1802 AssertRC(rc);
1803}
1804
1805/** @copydoc FNPDMBLKCACHEXFERENQUEUEDRV */
1806static int drvvdBlkCacheXferEnqueue(PPDMDRVINS pDrvIns,
1807 PDMBLKCACHEXFERDIR enmXferDir,
1808 uint64_t off, size_t cbXfer,
1809 PCRTSGBUF pcSgBuf, PPDMBLKCACHEIOXFER hIoXfer)
1810{
1811 int rc = VINF_SUCCESS;
1812 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1813
1814 switch (enmXferDir)
1815 {
1816 case PDMBLKCACHEXFERDIR_READ:
1817 rc = VDAsyncRead(pThis->pDisk, off, cbXfer, pcSgBuf, drvvdAsyncReqComplete,
1818 pThis, hIoXfer);
1819 break;
1820 case PDMBLKCACHEXFERDIR_WRITE:
1821 rc = VDAsyncWrite(pThis->pDisk, off, cbXfer, pcSgBuf, drvvdAsyncReqComplete,
1822 pThis, hIoXfer);
1823 break;
1824 case PDMBLKCACHEXFERDIR_FLUSH:
1825 rc = VDAsyncFlush(pThis->pDisk, drvvdAsyncReqComplete, pThis, hIoXfer);
1826 break;
1827 default:
1828 AssertMsgFailed(("Invalid transfer type %d\n", enmXferDir));
1829 rc = VERR_INVALID_PARAMETER;
1830 }
1831
1832 if (rc == VINF_VD_ASYNC_IO_FINISHED)
1833 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, hIoXfer, VINF_SUCCESS);
1834 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
1835 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, hIoXfer, rc);
1836
1837 return VINF_SUCCESS;
1838}
1839
1840/** @copydoc FNPDMBLKCACHEXFERENQUEUEDISCARDDRV */
1841static int drvvdBlkCacheXferEnqueueDiscard(PPDMDRVINS pDrvIns, PCRTRANGE paRanges,
1842 unsigned cRanges, PPDMBLKCACHEIOXFER hIoXfer)
1843{
1844 int rc = VINF_SUCCESS;
1845 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1846
1847 rc = VDAsyncDiscardRanges(pThis->pDisk, paRanges, cRanges,
1848 drvvdAsyncReqComplete, pThis, hIoXfer);
1849
1850 if (rc == VINF_VD_ASYNC_IO_FINISHED)
1851 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, hIoXfer, VINF_SUCCESS);
1852 else if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
1853 PDMR3BlkCacheIoXferComplete(pThis->pBlkCache, hIoXfer, rc);
1854
1855 return VINF_SUCCESS;
1856}
1857
1858/*******************************************************************************
1859* Base interface methods *
1860*******************************************************************************/
1861
1862/**
1863 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1864 */
1865static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1866{
1867 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1868 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1869
1870 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1871 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
1872 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNC, pThis->fAsyncIOSupported ? &pThis->IMediaAsync : NULL);
1873 return NULL;
1874}
1875
1876
1877/*******************************************************************************
1878* Saved state notification methods *
1879*******************************************************************************/
1880
1881/**
1882 * Load done callback for re-opening the image writable during teleportation.
1883 *
1884 * This is called both for successful and failed load runs, we only care about
1885 * successful ones.
1886 *
1887 * @returns VBox status code.
1888 * @param pDrvIns The driver instance.
1889 * @param pSSM The saved state handle.
1890 */
1891static DECLCALLBACK(int) drvvdLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1892{
1893 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1894 Assert(!pThis->fErrorUseRuntime);
1895
1896 /* Drop out if we don't have any work to do or if it's a failed load. */
1897 if ( !pThis->fTempReadOnly
1898 || RT_FAILURE(SSMR3HandleGetStatus(pSSM)))
1899 return VINF_SUCCESS;
1900
1901 int rc = drvvdSetWritable(pThis);
1902 if (RT_FAILURE(rc)) /** @todo does the bugger set any errors? */
1903 return SSMR3SetLoadError(pSSM, rc, RT_SRC_POS,
1904 N_("Failed to write lock the images"));
1905 return VINF_SUCCESS;
1906}
1907
1908
1909/*******************************************************************************
1910* Driver methods *
1911*******************************************************************************/
1912
1913/**
1914 * VM resume notification that we use to undo what the temporary read-only image
1915 * mode set by drvvdSuspend.
1916 *
1917 * Also switch to runtime error mode if we're resuming after a state load
1918 * without having been powered on first.
1919 *
1920 * @param pDrvIns The driver instance data.
1921 *
1922 * @todo The VMSetError vs VMSetRuntimeError mess must be fixed elsewhere,
1923 * we're making assumptions about Main behavior here!
1924 */
1925static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
1926{
1927 LogFlowFunc(("\n"));
1928 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1929
1930 drvvdSetWritable(pThis);
1931 pThis->fErrorUseRuntime = true;
1932
1933 if (pThis->pBlkCache)
1934 {
1935 int rc = PDMR3BlkCacheResume(pThis->pBlkCache);
1936 AssertRC(rc);
1937 }
1938}
1939
1940/**
1941 * The VM is being suspended, temporarily change to read-only image mode.
1942 *
1943 * This is important for several reasons:
1944 * -# It makes sure that there are no pending writes to the image. Most
1945 * backends implements this by closing and reopening the image in read-only
1946 * mode.
1947 * -# It allows Main to read the images during snapshotting without having
1948 * to account for concurrent writes.
1949 * -# This is essential for making teleportation targets sharing images work
1950 * right. Both with regards to caching and with regards to file sharing
1951 * locks (RTFILE_O_DENY_*). (See also drvvdLoadDone.)
1952 *
1953 * @param pDrvIns The driver instance data.
1954 */
1955static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
1956{
1957 LogFlowFunc(("\n"));
1958 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1959
1960 if (pThis->pBlkCache)
1961 {
1962 int rc = PDMR3BlkCacheSuspend(pThis->pBlkCache);
1963 AssertRC(rc);
1964 }
1965
1966 drvvdSetReadonly(pThis);
1967}
1968
1969/**
1970 * VM PowerOn notification for undoing the TempReadOnly config option and
1971 * changing to runtime error mode.
1972 *
1973 * @param pDrvIns The driver instance data.
1974 *
1975 * @todo The VMSetError vs VMSetRuntimeError mess must be fixed elsewhere,
1976 * we're making assumptions about Main behavior here!
1977 */
1978static DECLCALLBACK(void) drvvdPowerOn(PPDMDRVINS pDrvIns)
1979{
1980 LogFlowFunc(("\n"));
1981 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1982 drvvdSetWritable(pThis);
1983 pThis->fErrorUseRuntime = true;
1984}
1985
1986/**
1987 * @copydoc FNPDMDRVRESET
1988 */
1989static DECLCALLBACK(void) drvvdReset(PPDMDRVINS pDrvIns)
1990{
1991 LogFlowFunc(("\n"));
1992 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
1993
1994 if (pThis->pBlkCache)
1995 {
1996 int rc = PDMR3BlkCacheClear(pThis->pBlkCache);
1997 AssertRC(rc);
1998 }
1999
2000 if (pThis->fBootAccelEnabled)
2001 {
2002 pThis->fBootAccelActive = true;
2003 pThis->cbDataValid = 0;
2004 pThis->offDisk = 0;
2005 }
2006}
2007
2008/**
2009 * @copydoc FNPDMDRVDESTRUCT
2010 */
2011static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
2012{
2013 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
2014 LogFlowFunc(("\n"));
2015 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2016
2017 RTSEMFASTMUTEX mutex;
2018 ASMAtomicXchgHandle(&pThis->MergeCompleteMutex, NIL_RTSEMFASTMUTEX, &mutex);
2019 if (mutex != NIL_RTSEMFASTMUTEX)
2020 {
2021 /* Request the semaphore to wait until a potentially running merge
2022 * operation has been finished. */
2023 int rc = RTSemFastMutexRequest(mutex);
2024 AssertRC(rc);
2025 pThis->fMergePending = false;
2026 rc = RTSemFastMutexRelease(mutex);
2027 AssertRC(rc);
2028 rc = RTSemFastMutexDestroy(mutex);
2029 AssertRC(rc);
2030 }
2031
2032 if (VALID_PTR(pThis->pBlkCache))
2033 {
2034 PDMR3BlkCacheRelease(pThis->pBlkCache);
2035 pThis->pBlkCache = NULL;
2036 }
2037
2038 if (VALID_PTR(pThis->pDisk))
2039 {
2040 VDDestroy(pThis->pDisk);
2041 pThis->pDisk = NULL;
2042 }
2043 drvvdFreeImages(pThis);
2044
2045 if (pThis->MergeLock != NIL_RTSEMRW)
2046 {
2047 int rc = RTSemRWDestroy(pThis->MergeLock);
2048 AssertRC(rc);
2049 pThis->MergeLock = NIL_RTSEMRW;
2050 }
2051 if (pThis->pbData)
2052 RTMemFree(pThis->pbData);
2053 if (pThis->pszBwGroup)
2054 {
2055 MMR3HeapFree(pThis->pszBwGroup);
2056 pThis->pszBwGroup = NULL;
2057 }
2058}
2059
2060/**
2061 * Construct a VBox disk media driver instance.
2062 *
2063 * @copydoc FNPDMDRVCONSTRUCT
2064 */
2065static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
2066 PCFGMNODE pCfg,
2067 uint32_t fFlags)
2068{
2069 LogFlowFunc(("\n"));
2070 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
2071 int rc = VINF_SUCCESS;
2072 char *pszName = NULL; /**< The path of the disk image file. */
2073 char *pszFormat = NULL; /**< The format backed to use for this image. */
2074 char *pszCachePath = NULL; /**< The path to the cache image. */
2075 char *pszCacheFormat = NULL; /**< The format backend to use for the cache image. */
2076 bool fReadOnly; /**< True if the media is read-only. */
2077 bool fMaybeReadOnly; /**< True if the media may or may not be read-only. */
2078 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
2079 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2080
2081 /*
2082 * Init the static parts.
2083 */
2084 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
2085 pThis->pDrvIns = pDrvIns;
2086 pThis->fTempReadOnly = false;
2087 pThis->pDisk = NULL;
2088 pThis->fAsyncIOSupported = false;
2089 pThis->fShareable = false;
2090 pThis->fMergePending = false;
2091 pThis->MergeCompleteMutex = NIL_RTSEMFASTMUTEX;
2092 pThis->uMergeSource = VD_LAST_IMAGE;
2093 pThis->uMergeTarget = VD_LAST_IMAGE;
2094
2095 /* IMedia */
2096 pThis->IMedia.pfnRead = drvvdRead;
2097 pThis->IMedia.pfnWrite = drvvdWrite;
2098 pThis->IMedia.pfnFlush = drvvdFlush;
2099 pThis->IMedia.pfnMerge = drvvdMerge;
2100 pThis->IMedia.pfnGetSize = drvvdGetSize;
2101 pThis->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
2102 pThis->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
2103 pThis->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
2104 pThis->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
2105 pThis->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
2106 pThis->IMedia.pfnGetUuid = drvvdGetUuid;
2107 pThis->IMedia.pfnDiscard = drvvdDiscard;
2108
2109 /* IMediaAsync */
2110 pThis->IMediaAsync.pfnStartRead = drvvdStartRead;
2111 pThis->IMediaAsync.pfnStartWrite = drvvdStartWrite;
2112 pThis->IMediaAsync.pfnStartFlush = drvvdStartFlush;
2113 pThis->IMediaAsync.pfnStartDiscard = drvvdStartDiscard;
2114
2115 /* Initialize supported VD interfaces. */
2116 pThis->pVDIfsDisk = NULL;
2117
2118 pThis->VDIfError.pfnError = drvvdErrorCallback;
2119 pThis->VDIfError.pfnMessage = NULL;
2120 rc = VDInterfaceAdd(&pThis->VDIfError.Core, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
2121 pDrvIns, sizeof(VDINTERFACEERROR), &pThis->pVDIfsDisk);
2122 AssertRC(rc);
2123
2124 /* List of images is empty now. */
2125 pThis->pImages = NULL;
2126
2127 pThis->pDrvMediaPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAPORT);
2128 if (!pThis->pDrvMediaPort)
2129 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
2130 N_("No media port interface above"));
2131
2132 /* Try to attach async media port interface above.*/
2133 pThis->pDrvMediaAsyncPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAASYNCPORT);
2134
2135 /*
2136 * Validate configuration and find all parent images.
2137 * It's sort of up side down from the image dependency tree.
2138 */
2139 bool fHostIP = false;
2140 bool fUseNewIo = false;
2141 bool fUseBlockCache = false;
2142 bool fDiscard = false;
2143 bool fInformAboutZeroBlocks = false;
2144 bool fSkipConsistencyChecks = false;
2145 unsigned iLevel = 0;
2146 PCFGMNODE pCurNode = pCfg;
2147 VDTYPE enmType = VDTYPE_HDD;
2148
2149 for (;;)
2150 {
2151 bool fValid;
2152
2153 if (pCurNode == pCfg)
2154 {
2155 /* Toplevel configuration additionally contains the global image
2156 * open flags. Some might be converted to per-image flags later. */
2157 fValid = CFGMR3AreValuesValid(pCurNode,
2158 "Format\0Path\0"
2159 "ReadOnly\0MaybeReadOnly\0TempReadOnly\0Shareable\0HonorZeroWrites\0"
2160 "HostIPStack\0UseNewIo\0BootAcceleration\0BootAccelerationBuffer\0"
2161 "SetupMerge\0MergeSource\0MergeTarget\0BwGroup\0Type\0BlockCache\0"
2162 "CachePath\0CacheFormat\0Discard\0InformAboutZeroBlocks\0"
2163 "SkipConsistencyChecks\0");
2164 }
2165 else
2166 {
2167 /* All other image configurations only contain image name and
2168 * the format information. */
2169 fValid = CFGMR3AreValuesValid(pCurNode, "Format\0Path\0"
2170 "MergeSource\0MergeTarget\0");
2171 }
2172 if (!fValid)
2173 {
2174 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
2175 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
2176 break;
2177 }
2178
2179 if (pCurNode == pCfg)
2180 {
2181 rc = CFGMR3QueryBoolDef(pCurNode, "HostIPStack", &fHostIP, true);
2182 if (RT_FAILURE(rc))
2183 {
2184 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2185 N_("DrvVD: Configuration error: Querying \"HostIPStack\" as boolean failed"));
2186 break;
2187 }
2188
2189 rc = CFGMR3QueryBoolDef(pCurNode, "HonorZeroWrites", &fHonorZeroWrites, false);
2190 if (RT_FAILURE(rc))
2191 {
2192 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2193 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
2194 break;
2195 }
2196
2197 rc = CFGMR3QueryBoolDef(pCurNode, "ReadOnly", &fReadOnly, false);
2198 if (RT_FAILURE(rc))
2199 {
2200 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2201 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
2202 break;
2203 }
2204
2205 rc = CFGMR3QueryBoolDef(pCurNode, "MaybeReadOnly", &fMaybeReadOnly, false);
2206 if (RT_FAILURE(rc))
2207 {
2208 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2209 N_("DrvVD: Configuration error: Querying \"MaybeReadOnly\" as boolean failed"));
2210 break;
2211 }
2212
2213 rc = CFGMR3QueryBoolDef(pCurNode, "TempReadOnly", &pThis->fTempReadOnly, false);
2214 if (RT_FAILURE(rc))
2215 {
2216 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2217 N_("DrvVD: Configuration error: Querying \"TempReadOnly\" as boolean failed"));
2218 break;
2219 }
2220 if (fReadOnly && pThis->fTempReadOnly)
2221 {
2222 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2223 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"TempReadOnly\" are set"));
2224 break;
2225 }
2226
2227 rc = CFGMR3QueryBoolDef(pCurNode, "Shareable", &pThis->fShareable, false);
2228 if (RT_FAILURE(rc))
2229 {
2230 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2231 N_("DrvVD: Configuration error: Querying \"Shareable\" as boolean failed"));
2232 break;
2233 }
2234
2235 rc = CFGMR3QueryBoolDef(pCurNode, "UseNewIo", &fUseNewIo, false);
2236 if (RT_FAILURE(rc))
2237 {
2238 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2239 N_("DrvVD: Configuration error: Querying \"UseNewIo\" as boolean failed"));
2240 break;
2241 }
2242 rc = CFGMR3QueryBoolDef(pCurNode, "SetupMerge", &pThis->fMergePending, false);
2243 if (RT_FAILURE(rc))
2244 {
2245 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2246 N_("DrvVD: Configuration error: Querying \"SetupMerge\" as boolean failed"));
2247 break;
2248 }
2249 if (fReadOnly && pThis->fMergePending)
2250 {
2251 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2252 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"MergePending\" are set"));
2253 break;
2254 }
2255 rc = CFGMR3QueryBoolDef(pCurNode, "BootAcceleration", &pThis->fBootAccelEnabled, false);
2256 if (RT_FAILURE(rc))
2257 {
2258 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2259 N_("DrvVD: Configuration error: Querying \"BootAcceleration\" as boolean failed"));
2260 break;
2261 }
2262 rc = CFGMR3QueryU32Def(pCurNode, "BootAccelerationBuffer", (uint32_t *)&pThis->cbBootAccelBuffer, 16 * _1K);
2263 if (RT_FAILURE(rc))
2264 {
2265 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2266 N_("DrvVD: Configuration error: Querying \"BootAccelerationBuffer\" as integer failed"));
2267 break;
2268 }
2269 rc = CFGMR3QueryBoolDef(pCurNode, "BlockCache", &fUseBlockCache, false);
2270 if (RT_FAILURE(rc))
2271 {
2272 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2273 N_("DrvVD: Configuration error: Querying \"BlockCache\" as boolean failed"));
2274 break;
2275 }
2276 rc = CFGMR3QueryStringAlloc(pCurNode, "BwGroup", &pThis->pszBwGroup);
2277 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
2278 {
2279 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2280 N_("DrvVD: Configuration error: Querying \"BwGroup\" as string failed"));
2281 break;
2282 }
2283 else
2284 rc = VINF_SUCCESS;
2285 rc = CFGMR3QueryBoolDef(pCurNode, "Discard", &fDiscard, false);
2286 if (RT_FAILURE(rc))
2287 {
2288 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2289 N_("DrvVD: Configuration error: Querying \"Discard\" as boolean failed"));
2290 break;
2291 }
2292 if (fReadOnly && fDiscard)
2293 {
2294 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2295 N_("DrvVD: Configuration error: Both \"ReadOnly\" and \"Discard\" are set"));
2296 break;
2297 }
2298 rc = CFGMR3QueryBoolDef(pCurNode, "InformAboutZeroBlocks", &fInformAboutZeroBlocks, false);
2299 if (RT_FAILURE(rc))
2300 {
2301 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2302 N_("DrvVD: Configuration error: Querying \"InformAboutZeroBlocks\" as boolean failed"));
2303 break;
2304 }
2305 rc = CFGMR3QueryBoolDef(pCurNode, "SkipConsistencyChecks", &fSkipConsistencyChecks, true);
2306 if (RT_FAILURE(rc))
2307 {
2308 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2309 N_("DrvVD: Configuration error: Querying \"SKipConsistencyChecks\" as boolean failed"));
2310 break;
2311 }
2312
2313 char *psz;
2314 rc = CFGMR3QueryStringAlloc(pCfg, "Type", &psz);
2315 if (RT_FAILURE(rc))
2316 {
2317 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_BLOCK_NO_TYPE, N_("Failed to obtain the type"));
2318 break;
2319 }
2320 else if (!strcmp(psz, "HardDisk"))
2321 enmType = VDTYPE_HDD;
2322 else if (!strcmp(psz, "DVD"))
2323 enmType = VDTYPE_DVD;
2324 else if (!strcmp(psz, "Floppy"))
2325 enmType = VDTYPE_FLOPPY;
2326 else
2327 {
2328 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_BLOCK_UNKNOWN_TYPE, RT_SRC_POS,
2329 N_("Unknown type \"%s\""), psz);
2330 MMR3HeapFree(psz);
2331 break;
2332 }
2333 MMR3HeapFree(psz); psz = NULL;
2334
2335 rc = CFGMR3QueryStringAlloc(pCurNode, "CachePath", &pszCachePath);
2336 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
2337 {
2338 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2339 N_("DrvVD: Configuration error: Querying \"CachePath\" as string failed"));
2340 break;
2341 }
2342 else
2343 rc = VINF_SUCCESS;
2344
2345 if (pszCachePath)
2346 {
2347 rc = CFGMR3QueryStringAlloc(pCurNode, "CacheFormat", &pszCacheFormat);
2348 if (RT_FAILURE(rc))
2349 {
2350 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2351 N_("DrvVD: Configuration error: Querying \"CacheFormat\" as string failed"));
2352 break;
2353 }
2354 }
2355 }
2356
2357 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
2358 if (!pParent)
2359 break;
2360 pCurNode = pParent;
2361 iLevel++;
2362 }
2363
2364 /*
2365 * Create the image container and the necessary interfaces.
2366 */
2367 if (RT_SUCCESS(rc))
2368 {
2369 /*
2370 * The image has a bandwidth group but the host cache is enabled.
2371 * Use the async I/O framework but tell it to enable the host cache.
2372 */
2373 if (!fUseNewIo && pThis->pszBwGroup)
2374 {
2375 pThis->fAsyncIoWithHostCache = true;
2376 fUseNewIo = true;
2377 }
2378
2379 /** @todo quick hack to work around problems in the async I/O
2380 * implementation (rw semaphore thread ownership problem)
2381 * while a merge is running. Remove once this is fixed. */
2382 if (pThis->fMergePending)
2383 fUseNewIo = false;
2384
2385 if (RT_SUCCESS(rc) && pThis->fMergePending)
2386 {
2387 rc = RTSemFastMutexCreate(&pThis->MergeCompleteMutex);
2388 if (RT_SUCCESS(rc))
2389 rc = RTSemRWCreate(&pThis->MergeLock);
2390 if (RT_SUCCESS(rc))
2391 {
2392 pThis->VDIfThreadSync.pfnStartRead = drvvdThreadStartRead;
2393 pThis->VDIfThreadSync.pfnFinishRead = drvvdThreadFinishRead;
2394 pThis->VDIfThreadSync.pfnStartWrite = drvvdThreadStartWrite;
2395 pThis->VDIfThreadSync.pfnFinishWrite = drvvdThreadFinishWrite;
2396
2397 rc = VDInterfaceAdd(&pThis->VDIfThreadSync.Core, "DrvVD_ThreadSync", VDINTERFACETYPE_THREADSYNC,
2398 pThis, sizeof(VDINTERFACETHREADSYNC), &pThis->pVDIfsDisk);
2399 }
2400 else
2401 {
2402 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2403 N_("DrvVD: Failed to create semaphores for \"MergePending\""));
2404 }
2405 }
2406
2407 if (RT_SUCCESS(rc))
2408 {
2409 rc = VDCreate(pThis->pVDIfsDisk, enmType, &pThis->pDisk);
2410 /* Error message is already set correctly. */
2411 }
2412 }
2413
2414 if (pThis->pDrvMediaAsyncPort && fUseNewIo)
2415 pThis->fAsyncIOSupported = true;
2416
2417 uint64_t tsStart = RTTimeNanoTS();
2418
2419 unsigned iImageIdx = 0;
2420 while (pCurNode && RT_SUCCESS(rc))
2421 {
2422 /* Allocate per-image data. */
2423 PVBOXIMAGE pImage = drvvdNewImage(pThis);
2424 if (!pImage)
2425 {
2426 rc = VERR_NO_MEMORY;
2427 break;
2428 }
2429
2430 /*
2431 * Read the image configuration.
2432 */
2433 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
2434 if (RT_FAILURE(rc))
2435 {
2436 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2437 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
2438 break;
2439 }
2440
2441 rc = CFGMR3QueryStringAlloc(pCurNode, "Format", &pszFormat);
2442 if (RT_FAILURE(rc))
2443 {
2444 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2445 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
2446 break;
2447 }
2448
2449 bool fMergeSource;
2450 rc = CFGMR3QueryBoolDef(pCurNode, "MergeSource", &fMergeSource, false);
2451 if (RT_FAILURE(rc))
2452 {
2453 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2454 N_("DrvVD: Configuration error: Querying \"MergeSource\" as boolean failed"));
2455 break;
2456 }
2457 if (fMergeSource)
2458 {
2459 if (pThis->uMergeSource == VD_LAST_IMAGE)
2460 pThis->uMergeSource = iImageIdx;
2461 else
2462 {
2463 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2464 N_("DrvVD: Configuration error: Multiple \"MergeSource\" occurrences"));
2465 break;
2466 }
2467 }
2468
2469 bool fMergeTarget;
2470 rc = CFGMR3QueryBoolDef(pCurNode, "MergeTarget", &fMergeTarget, false);
2471 if (RT_FAILURE(rc))
2472 {
2473 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
2474 N_("DrvVD: Configuration error: Querying \"MergeTarget\" as boolean failed"));
2475 break;
2476 }
2477 if (fMergeTarget)
2478 {
2479 if (pThis->uMergeTarget == VD_LAST_IMAGE)
2480 pThis->uMergeTarget = iImageIdx;
2481 else
2482 {
2483 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2484 N_("DrvVD: Configuration error: Multiple \"MergeTarget\" occurrences"));
2485 break;
2486 }
2487 }
2488
2489 PCFGMNODE pCfgVDConfig = CFGMR3GetChild(pCurNode, "VDConfig");
2490 pImage->VDIfConfig.pfnAreKeysValid = drvvdCfgAreKeysValid;
2491 pImage->VDIfConfig.pfnQuerySize = drvvdCfgQuerySize;
2492 pImage->VDIfConfig.pfnQuery = drvvdCfgQuery;
2493 rc = VDInterfaceAdd(&pImage->VDIfConfig.Core, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
2494 pCfgVDConfig, sizeof(VDINTERFACECONFIG), &pImage->pVDIfsImage);
2495 AssertRC(rc);
2496
2497 /* Unconditionally insert the TCPNET interface, don't bother to check
2498 * if an image really needs it. Will be ignored. Since the TCPNET
2499 * interface is per image we could make this more flexible in the
2500 * future if we want to. */
2501 /* Construct TCPNET callback table depending on the config. This is
2502 * done unconditionally, as uninterested backends will ignore it. */
2503 if (fHostIP)
2504 {
2505 pImage->VDIfTcpNet.pfnSocketCreate = drvvdTcpSocketCreate;
2506 pImage->VDIfTcpNet.pfnSocketDestroy = drvvdTcpSocketDestroy;
2507 pImage->VDIfTcpNet.pfnClientConnect = drvvdTcpClientConnect;
2508 pImage->VDIfTcpNet.pfnIsClientConnected = drvvdTcpIsClientConnected;
2509 pImage->VDIfTcpNet.pfnClientClose = drvvdTcpClientClose;
2510 pImage->VDIfTcpNet.pfnSelectOne = drvvdTcpSelectOne;
2511 pImage->VDIfTcpNet.pfnRead = drvvdTcpRead;
2512 pImage->VDIfTcpNet.pfnWrite = drvvdTcpWrite;
2513 pImage->VDIfTcpNet.pfnSgWrite = drvvdTcpSgWrite;
2514 pImage->VDIfTcpNet.pfnReadNB = drvvdTcpReadNB;
2515 pImage->VDIfTcpNet.pfnWriteNB = drvvdTcpWriteNB;
2516 pImage->VDIfTcpNet.pfnSgWriteNB = drvvdTcpSgWriteNB;
2517 pImage->VDIfTcpNet.pfnFlush = drvvdTcpFlush;
2518 pImage->VDIfTcpNet.pfnSetSendCoalescing = drvvdTcpSetSendCoalescing;
2519 pImage->VDIfTcpNet.pfnGetLocalAddress = drvvdTcpGetLocalAddress;
2520 pImage->VDIfTcpNet.pfnGetPeerAddress = drvvdTcpGetPeerAddress;
2521
2522 /*
2523 * There is a 15ms delay between receiving the data and marking the socket
2524 * as readable on Windows XP which hurts async I/O performance of
2525 * TCP backends badly. Provide a different select method without
2526 * using poll on XP.
2527 * This is only used on XP because it is not as efficient as the one using poll
2528 * and all other Windows versions are working fine.
2529 */
2530 char szOS[64];
2531 memset(szOS, 0, sizeof(szOS));
2532 rc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, &szOS[0], sizeof(szOS));
2533
2534 if (RT_SUCCESS(rc) && !strncmp(szOS, "Windows XP", 10))
2535 {
2536 LogRel(("VD: Detected Windows XP, disabled poll based waiting for TCP\n"));
2537 pImage->VDIfTcpNet.pfnSelectOneEx = drvvdTcpSelectOneExNoPoll;
2538 }
2539 else
2540 pImage->VDIfTcpNet.pfnSelectOneEx = drvvdTcpSelectOneExPoll;
2541
2542 pImage->VDIfTcpNet.pfnPoke = drvvdTcpPoke;
2543 }
2544 else
2545 {
2546#ifndef VBOX_WITH_INIP
2547 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
2548 RT_SRC_POS, N_("DrvVD: Configuration error: TCP over Internal Networking not compiled in"));
2549#else /* VBOX_WITH_INIP */
2550 pImage->VDIfTcpNet.pfnSocketCreate = drvvdINIPSocketCreate;
2551 pImage->VDIfTcpNet.pfnSocketDestroy = drvvdINIPSocketDestroy;
2552 pImage->VDIfTcpNet.pfnClientConnect = drvvdINIPClientConnect;
2553 pImage->VDIfTcpNet.pfnClientClose = drvvdINIPClientClose;
2554 pImage->VDIfTcpNet.pfnIsClientConnected = drvvdINIPIsClientConnected;
2555 pImage->VDIfTcpNet.pfnSelectOne = drvvdINIPSelectOne;
2556 pImage->VDIfTcpNet.pfnRead = drvvdINIPRead;
2557 pImage->VDIfTcpNet.pfnWrite = drvvdINIPWrite;
2558 pImage->VDIfTcpNet.pfnSgWrite = drvvdINIPSgWrite;
2559 pImage->VDIfTcpNet.pfnFlush = drvvdINIPFlush;
2560 pImage->VDIfTcpNet.pfnSetSendCoalescing = drvvdINIPSetSendCoalescing;
2561 pImage->VDIfTcpNet.pfnGetLocalAddress = drvvdINIPGetLocalAddress;
2562 pImage->VDIfTcpNet.pfnGetPeerAddress = drvvdINIPGetPeerAddress;
2563 pImage->VDIfTcpNet.pfnSelectOneEx = drvvdINIPSelectOneEx;
2564 pImage->VDIfTcpNet.pfnPoke = drvvdINIPPoke;
2565#endif /* VBOX_WITH_INIP */
2566 }
2567 rc = VDInterfaceAdd(&pImage->VDIfTcpNet.Core, "DrvVD_TCPNET",
2568 VDINTERFACETYPE_TCPNET, NULL,
2569 sizeof(VDINTERFACETCPNET), &pImage->pVDIfsImage);
2570 AssertRC(rc);
2571
2572 /* Insert the custom I/O interface only if we're told to use new IO.
2573 * Since the I/O interface is per image we could make this more
2574 * flexible in the future if we want to. */
2575 if (fUseNewIo)
2576 {
2577#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
2578 pImage->VDIfIo.pfnOpen = drvvdAsyncIOOpen;
2579 pImage->VDIfIo.pfnClose = drvvdAsyncIOClose;
2580 pImage->VDIfIo.pfnGetSize = drvvdAsyncIOGetSize;
2581 pImage->VDIfIo.pfnSetSize = drvvdAsyncIOSetSize;
2582 pImage->VDIfIo.pfnReadSync = drvvdAsyncIOReadSync;
2583 pImage->VDIfIo.pfnWriteSync = drvvdAsyncIOWriteSync;
2584 pImage->VDIfIo.pfnFlushSync = drvvdAsyncIOFlushSync;
2585 pImage->VDIfIo.pfnReadAsync = drvvdAsyncIOReadAsync;
2586 pImage->VDIfIo.pfnWriteAsync = drvvdAsyncIOWriteAsync;
2587 pImage->VDIfIo.pfnFlushAsync = drvvdAsyncIOFlushAsync;
2588#else /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
2589 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
2590 RT_SRC_POS, N_("DrvVD: Configuration error: Async Completion Framework not compiled in"));
2591#endif /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
2592 if (RT_SUCCESS(rc))
2593 rc = VDInterfaceAdd(&pImage->VDIfIo.Core, "DrvVD_IO", VDINTERFACETYPE_IO,
2594 pThis, sizeof(VDINTERFACEIO), &pImage->pVDIfsImage);
2595 AssertRC(rc);
2596 }
2597
2598 /*
2599 * Open the image.
2600 */
2601 unsigned uOpenFlags;
2602 if (fReadOnly || pThis->fTempReadOnly || iLevel != 0)
2603 uOpenFlags = VD_OPEN_FLAGS_READONLY;
2604 else
2605 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
2606 if (fHonorZeroWrites)
2607 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
2608 if (pThis->fAsyncIOSupported)
2609 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
2610 if (pThis->fShareable)
2611 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
2612 if (fDiscard && iLevel == 0)
2613 uOpenFlags |= VD_OPEN_FLAGS_DISCARD;
2614 if (fInformAboutZeroBlocks)
2615 uOpenFlags |= VD_OPEN_FLAGS_INFORM_ABOUT_ZERO_BLOCKS;
2616 if ( (uOpenFlags & VD_OPEN_FLAGS_READONLY)
2617 && fSkipConsistencyChecks)
2618 uOpenFlags |= VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS;
2619
2620 /* Try to open backend in async I/O mode first. */
2621 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
2622 if (rc == VERR_NOT_SUPPORTED)
2623 {
2624 pThis->fAsyncIOSupported = false;
2625 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
2626 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
2627 }
2628
2629 if (rc == VERR_VD_DISCARD_NOT_SUPPORTED)
2630 {
2631 fDiscard = false;
2632 uOpenFlags &= ~VD_OPEN_FLAGS_DISCARD;
2633 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
2634 }
2635
2636 if (!fDiscard)
2637 {
2638 pThis->IMedia.pfnDiscard = NULL;
2639 pThis->IMediaAsync.pfnStartDiscard = NULL;
2640 }
2641
2642 if (RT_SUCCESS(rc))
2643 {
2644 LogFunc(("%d - Opened '%s' in %s mode\n",
2645 iLevel, pszName,
2646 VDIsReadOnly(pThis->pDisk) ? "read-only" : "read-write"));
2647 if ( VDIsReadOnly(pThis->pDisk)
2648 && !fReadOnly
2649 && !fMaybeReadOnly
2650 && !pThis->fTempReadOnly
2651 && iLevel == 0)
2652 {
2653 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_VD_IMAGE_READ_ONLY, RT_SRC_POS,
2654 N_("Failed to open image '%s' for writing due to wrong permissions"),
2655 pszName);
2656 break;
2657 }
2658 }
2659 else
2660 {
2661 rc = PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
2662 N_("Failed to open image '%s' in %s mode rc=%Rrc"), pszName,
2663 (uOpenFlags & VD_OPEN_FLAGS_READONLY) ? "read-only" : "read-write", rc);
2664 break;
2665 }
2666
2667
2668 MMR3HeapFree(pszName);
2669 pszName = NULL;
2670 MMR3HeapFree(pszFormat);
2671 pszFormat = NULL;
2672
2673 /* next */
2674 iLevel--;
2675 iImageIdx++;
2676 pCurNode = CFGMR3GetParent(pCurNode);
2677 }
2678
2679 LogRel(("VD: Opening the disk took %lld ns\n", RTTimeNanoTS() - tsStart));
2680
2681 /* Open the cache image if set. */
2682 if ( RT_SUCCESS(rc)
2683 && VALID_PTR(pszCachePath))
2684 {
2685 /* Insert the custom I/O interface only if we're told to use new IO.
2686 * Since the I/O interface is per image we could make this more
2687 * flexible in the future if we want to. */
2688 if (fUseNewIo)
2689 {
2690#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
2691 pThis->VDIfIoCache.pfnOpen = drvvdAsyncIOOpen;
2692 pThis->VDIfIoCache.pfnClose = drvvdAsyncIOClose;
2693 pThis->VDIfIoCache.pfnGetSize = drvvdAsyncIOGetSize;
2694 pThis->VDIfIoCache.pfnSetSize = drvvdAsyncIOSetSize;
2695 pThis->VDIfIoCache.pfnReadSync = drvvdAsyncIOReadSync;
2696 pThis->VDIfIoCache.pfnWriteSync = drvvdAsyncIOWriteSync;
2697 pThis->VDIfIoCache.pfnFlushSync = drvvdAsyncIOFlushSync;
2698 pThis->VDIfIoCache.pfnReadAsync = drvvdAsyncIOReadAsync;
2699 pThis->VDIfIoCache.pfnWriteAsync = drvvdAsyncIOWriteAsync;
2700 pThis->VDIfIoCache.pfnFlushAsync = drvvdAsyncIOFlushAsync;
2701#else /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
2702 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
2703 RT_SRC_POS, N_("DrvVD: Configuration error: Async Completion Framework not compiled in"));
2704#endif /* !VBOX_WITH_PDM_ASYNC_COMPLETION */
2705 if (RT_SUCCESS(rc))
2706 rc = VDInterfaceAdd(&pThis->VDIfIoCache.Core, "DrvVD_IO", VDINTERFACETYPE_IO,
2707 pThis, sizeof(VDINTERFACEIO), &pThis->pVDIfsCache);
2708 AssertRC(rc);
2709 }
2710
2711 rc = VDCacheOpen(pThis->pDisk, pszCacheFormat, pszCachePath, VD_OPEN_FLAGS_NORMAL, pThis->pVDIfsCache);
2712 if (RT_FAILURE(rc))
2713 rc = PDMDRV_SET_ERROR(pDrvIns, rc, N_("DrvVD: Could not open cache image"));
2714 }
2715
2716 if (VALID_PTR(pszCachePath))
2717 MMR3HeapFree(pszCachePath);
2718 if (VALID_PTR(pszCacheFormat))
2719 MMR3HeapFree(pszCacheFormat);
2720
2721 if ( RT_SUCCESS(rc)
2722 && pThis->fMergePending
2723 && ( pThis->uMergeSource == VD_LAST_IMAGE
2724 || pThis->uMergeTarget == VD_LAST_IMAGE))
2725 {
2726 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2727 N_("DrvVD: Configuration error: Inconsistent image merge data"));
2728 }
2729
2730 /* Create the block cache if enabled. */
2731 if ( fUseBlockCache
2732 && !pThis->fShareable
2733 && !fDiscard
2734 && RT_SUCCESS(rc))
2735 {
2736 /*
2737 * We need a unique ID for the block cache (to identify the owner of data
2738 * blocks in a saved state). UUIDs are not really suitable because
2739 * there are image formats which don't support them. Furthermore it is
2740 * possible that a new diff image was attached after a saved state
2741 * which changes the UUID.
2742 * However the device "name + device instance + LUN" triple the disk is
2743 * attached to is always constant for saved states.
2744 */
2745 char *pszId = NULL;
2746 uint32_t iInstance, iLUN;
2747 const char *pcszController;
2748
2749 rc = pThis->pDrvMediaPort->pfnQueryDeviceLocation(pThis->pDrvMediaPort, &pcszController,
2750 &iInstance, &iLUN);
2751 if (RT_FAILURE(rc))
2752 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2753 N_("DrvVD: Configuration error: Could not query device data"));
2754 else
2755 {
2756 int cbStr = RTStrAPrintf(&pszId, "%s-%d-%d", pcszController, iInstance, iLUN);
2757
2758 if (cbStr > 0)
2759 {
2760 rc = PDMDrvHlpBlkCacheRetain(pDrvIns, &pThis->pBlkCache,
2761 drvvdBlkCacheXferComplete,
2762 drvvdBlkCacheXferEnqueue,
2763 drvvdBlkCacheXferEnqueueDiscard,
2764 pszId);
2765 if (rc == VERR_NOT_SUPPORTED)
2766 {
2767 LogRel(("VD: Block cache is not supported\n"));
2768 rc = VINF_SUCCESS;
2769 }
2770 else
2771 AssertRC(rc);
2772
2773 RTStrFree(pszId);
2774 }
2775 else
2776 rc = PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRIVER_INVALID_PROPERTIES,
2777 N_("DrvVD: Out of memory when creating block cache"));
2778 }
2779 }
2780
2781 /*
2782 * Register a load-done callback so we can undo TempReadOnly config before
2783 * we get to drvvdResume. Autoamtically deregistered upon destruction.
2784 */
2785 if (RT_SUCCESS(rc))
2786 rc = PDMDrvHlpSSMRegisterEx(pDrvIns, 0 /* version */, 0 /* cbGuess */,
2787 NULL /*pfnLivePrep*/, NULL /*pfnLiveExec*/, NULL /*pfnLiveVote*/,
2788 NULL /*pfnSavePrep*/, NULL /*pfnSaveExec*/, NULL /*pfnSaveDone*/,
2789 NULL /*pfnDonePrep*/, NULL /*pfnLoadExec*/, drvvdLoadDone);
2790
2791 /* Setup the boot acceleration stuff if enabled. */
2792 if (RT_SUCCESS(rc) && pThis->fBootAccelEnabled)
2793 {
2794 pThis->cbDisk = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
2795 Assert(pThis->cbDisk > 0);
2796 pThis->pbData = (uint8_t *)RTMemAllocZ(pThis->cbBootAccelBuffer);
2797 if (pThis->pbData)
2798 {
2799 pThis->fBootAccelActive = true;
2800 pThis->offDisk = 0;
2801 pThis->cbDataValid = 0;
2802 LogRel(("VD: Boot acceleration enabled\n"));
2803 }
2804 else
2805 LogRel(("VD: Boot acceleration, out of memory, disabled\n"));
2806 }
2807
2808 if (RT_FAILURE(rc))
2809 {
2810 if (VALID_PTR(pszName))
2811 MMR3HeapFree(pszName);
2812 if (VALID_PTR(pszFormat))
2813 MMR3HeapFree(pszFormat);
2814 /* drvvdDestruct does the rest. */
2815 }
2816
2817 LogFlowFunc(("returns %Rrc\n", rc));
2818 return rc;
2819}
2820
2821/**
2822 * VBox disk container media driver registration record.
2823 */
2824const PDMDRVREG g_DrvVD =
2825{
2826 /* u32Version */
2827 PDM_DRVREG_VERSION,
2828 /* szName */
2829 "VD",
2830 /* szRCMod */
2831 "",
2832 /* szR0Mod */
2833 "",
2834 /* pszDescription */
2835 "Generic VBox disk media driver.",
2836 /* fFlags */
2837 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2838 /* fClass. */
2839 PDM_DRVREG_CLASS_MEDIA,
2840 /* cMaxInstances */
2841 ~0U,
2842 /* cbInstance */
2843 sizeof(VBOXDISK),
2844 /* pfnConstruct */
2845 drvvdConstruct,
2846 /* pfnDestruct */
2847 drvvdDestruct,
2848 /* pfnRelocate */
2849 NULL,
2850 /* pfnIOCtl */
2851 NULL,
2852 /* pfnPowerOn */
2853 drvvdPowerOn,
2854 /* pfnReset */
2855 drvvdReset,
2856 /* pfnSuspend */
2857 drvvdSuspend,
2858 /* pfnResume */
2859 drvvdResume,
2860 /* pfnAttach */
2861 NULL,
2862 /* pfnDetach */
2863 NULL,
2864 /* pfnPowerOff */
2865 NULL,
2866 /* pfnSoftReset */
2867 NULL,
2868 /* u32EndVersion */
2869 PDM_DRVREG_VERSION
2870};
2871
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