VirtualBox

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

Last change on this file since 38694 was 38622, checked in by vboxsync, 14 years ago

AHCI+DrvBlock+DrvVD: Add support for the TRIM command and connect with the VD discard support.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette