VirtualBox

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

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

DrvVD: IPv6 and new LWIP introduction.

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