VirtualBox

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

Last change on this file since 10715 was 10715, checked in by vboxsync, 16 years ago

Merge async I/O for VMDK backend from private branch

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.2 KB
Line 
1/** $Id: DrvVD.cpp 10715 2008-07-16 22:38:23Z vboxsync $ */
2/** @file
3 *
4 * VBox storage devices:
5 * Media implementation for VBox disk container
6 */
7
8/*
9 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24
25/*******************************************************************************
26* Header files *
27*******************************************************************************/
28#define LOG_GROUP LOG_GROUP_DRV_VD
29#include <VBox/VBoxHDD-new.h>
30#include <VBox/pdmdrv.h>
31#include <iprt/alloc.h>
32#include <iprt/assert.h>
33#include <iprt/uuid.h>
34#include <iprt/file.h>
35#include <iprt/string.h>
36#include <iprt/cache.h>
37
38#include "Builtins.h"
39
40
41/*******************************************************************************
42* Defined types, constants and macros *
43*******************************************************************************/
44
45/** Converts a pointer to VDIDISK::IMedia to a PVBOXDISK. */
46#define PDMIMEDIA_2_VBOXDISK(pInterface) \
47 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMedia)) )
48
49/** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
50#define PDMIBASE_2_DRVINS(pInterface) \
51 ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
52
53/** Converts a pointer to PDMDRVINS::IBase to a PVBOXDISK. */
54#define PDMIBASE_2_VBOXDISK(pInterface) \
55 ( PDMINS2DATA(PDMIBASE_2_DRVINS(pInterface), PVBOXDISK) )
56
57/** Converts a pointer to VBOXDISK::IMediaAsync to a PVBOXDISK. */
58#define PDMIMEDIAASYNC_2_VBOXDISK(pInterface) \
59 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, IMediaAsync)) )
60
61/** Converts a pointer to VBOXDISK::ITransportAsyncPort to a PVBOXDISK. */
62#define PDMITRANSPORTASYNCPORT_2_VBOXDISK(pInterface) \
63 ( (PVBOXDISK)((uintptr_t)pInterface - RT_OFFSETOF(VBOXDISK, ITransportAsyncPort)) )
64
65/**
66 * Structure for an async I/O task.
67 */
68typedef struct DRVVDASYNCTASK
69{
70 /** Callback which is called on completion. */
71 PFNVDCOMPLETED pfnCompleted;
72 /** Opqaue user data which is passed on completion. */
73 void *pvUser;
74 /** Opaque user data the caller passed on transfer initiation. */
75 void *pvUserCaller;
76} DRVVDASYNCTASK, *PDRVVDASYNCTASK;
77
78/**
79 * VBox disk container media main structure, private part.
80 */
81typedef struct VBOXDISK
82{
83 /** The VBox disk container. */
84 PVBOXHDD pDisk;
85 /** The media interface. */
86 PDMIMEDIA IMedia;
87 /** Pointer to the driver instance. */
88 PPDMDRVINS pDrvIns;
89 /** Flag whether suspend has changed image open mode to read only. */
90 bool fTempReadOnly;
91 /** Common structure for the supported error interface. */
92 VDINTERFACE VDIError;
93 /** Callback table for error interface. */
94 VDINTERFACEERROR VDIErrorCallbacks;
95 /** Common structure for the supported async I/O interface. */
96 VDINTERFACE VDIAsyncIO;
97 /** Callback table for async I/O interface. */
98 VDINTERFACEASYNCIO VDIAsyncIOCallbacks;
99 /** Flag whether opened disk suppports async I/O operations. */
100 bool fAsyncIOSupported;
101 /** The async media interface. */
102 PDMIMEDIAASYNC IMediaAsync;
103 /** The async media port interface above. */
104 PPDMIMEDIAASYNCPORT pDrvMediaAsyncPort;
105 /** Pointer to the asynchronous media driver below. */
106 PPDMITRANSPORTASYNC pDrvTransportAsync;
107 /** Async transport port interface. */
108 PDMITRANSPORTASYNCPORT ITransportAsyncPort;
109 /** Our cache to reduce allocation overhead. */
110 PRTOBJCACHE pCache;
111} VBOXDISK, *PVBOXDISK;
112
113/*******************************************************************************
114* Error reporting callback *
115*******************************************************************************/
116
117static void drvvdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL,
118 const char *pszFormat, va_list va)
119{
120 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
121 pDrvIns->pDrvHlp->pfnVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
122}
123
124/*******************************************************************************
125* VD Async I/O interface implementation *
126*******************************************************************************/
127
128static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation, bool fReadonly, void **ppStorage)
129{
130 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
131
132 return pDrvVD->pDrvTransportAsync->pfnOpen(pDrvVD->pDrvTransportAsync, pszLocation, fReadonly, ppStorage);
133}
134
135static DECLCALLBACK(int) drvvdAsyncIOClose(void *pvUser, void *pStorage)
136{
137 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
138
139 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
140
141 return pDrvVD->pDrvTransportAsync->pfnClose(pDrvVD->pDrvTransportAsync, pStorage);
142}
143
144static DECLCALLBACK(int) drvvdAsyncIORead(void *pvUser, void *pStorage, uint64_t uOffset,
145 size_t cbRead, void *pvBuf, size_t *pcbRead)
146{
147 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
148
149 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
150
151 return pDrvVD->pDrvTransportAsync->pfnReadSynchronous(pDrvVD->pDrvTransportAsync,
152 pStorage,
153 uOffset, pvBuf, cbRead, pcbRead);
154}
155
156static DECLCALLBACK(int) drvvdAsyncIOWrite(void *pvUser, void *pStorage, uint64_t uOffset,
157 size_t cbWrite, const void *pvBuf, size_t *pcbWritten)
158{
159 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
160
161 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
162
163 return pDrvVD->pDrvTransportAsync->pfnWriteSynchronous(pDrvVD->pDrvTransportAsync,
164 pStorage,
165 uOffset, pvBuf, cbWrite, pcbWritten);
166}
167
168static DECLCALLBACK(int) drvvdAsyncIOFlush(void *pvUser, void *pStorage)
169{
170 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
171
172 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
173
174 return pDrvVD->pDrvTransportAsync->pfnFlushSynchronous(pDrvVD->pDrvTransportAsync, pStorage);
175}
176
177static DECLCALLBACK(int) drvvdAsyncIOPrepareRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuf, size_t cbRead, void **ppTask)
178{
179 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
180
181 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
182
183 return pDrvVD->pDrvTransportAsync->pfnPrepareRead(pDrvVD->pDrvTransportAsync, pStorage, uOffset, pvBuf, cbRead, ppTask);
184}
185
186static DECLCALLBACK(int) drvvdAsyncIOPrepareWrite(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuf, size_t cbWrite, void **ppTask)
187{
188 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
189
190 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
191
192 return pDrvVD->pDrvTransportAsync->pfnPrepareWrite(pDrvVD->pDrvTransportAsync, pStorage, uOffset, pvBuf, cbWrite, ppTask);
193}
194
195static DECLCALLBACK(int) drvvdAsyncIOTasksSubmit(void *pvUser, void *apTasks[], unsigned cTasks, void *pvUser2,
196 void *pvUserCaller, PFNVDCOMPLETED pfnTasksCompleted)
197{
198 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
199 PDRVVDASYNCTASK pDrvVDAsyncTask;
200 int rc;
201
202 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
203
204 rc = RTCacheRequest(pDrvVD->pCache, (void **)&pDrvVDAsyncTask);
205
206 if (RT_FAILURE(rc))
207 return rc;
208
209 pDrvVDAsyncTask->pfnCompleted = pfnTasksCompleted;
210 pDrvVDAsyncTask->pvUser = pvUser2;
211 pDrvVDAsyncTask->pvUserCaller = pvUserCaller;
212
213 return pDrvVD->pDrvTransportAsync->pfnTasksSubmit(pDrvVD->pDrvTransportAsync, apTasks, cTasks, pDrvVDAsyncTask);
214}
215
216/*******************************************************************************
217* Media interface methods *
218*******************************************************************************/
219
220/** @copydoc PDMIMEDIA::pfnRead */
221static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
222 uint64_t off, void *pvBuf, size_t cbRead)
223{
224 LogFlow(("%s: off=%#llx pvBuf=%p cbRead=%d\n", __FUNCTION__,
225 off, pvBuf, cbRead));
226 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
227 int rc = VDRead(pData->pDisk, off, pvBuf, cbRead);
228 if (VBOX_SUCCESS(rc))
229 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Vhxd\n", __FUNCTION__,
230 off, pvBuf, cbRead, cbRead, pvBuf));
231 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
232 return rc;
233}
234
235/** @copydoc PDMIMEDIA::pfnWrite */
236static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
237 uint64_t off, const void *pvBuf,
238 size_t cbWrite)
239{
240 LogFlow(("%s: off=%#llx pvBuf=%p cbWrite=%d\n", __FUNCTION__,
241 off, pvBuf, cbWrite));
242 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
243 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Vhxd\n", __FUNCTION__,
244 off, pvBuf, cbWrite, cbWrite, pvBuf));
245 int rc = VDWrite(pData->pDisk, off, pvBuf, cbWrite);
246 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
247 return rc;
248}
249
250/** @copydoc PDMIMEDIA::pfnFlush */
251static DECLCALLBACK(int) drvvdFlush(PPDMIMEDIA pInterface)
252{
253 LogFlow(("%s:\n", __FUNCTION__));
254 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
255 int rc = VDFlush(pData->pDisk);
256 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
257 return rc;
258}
259
260/** @copydoc PDMIMEDIA::pfnGetSize */
261static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
262{
263 LogFlow(("%s:\n", __FUNCTION__));
264 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
265 uint64_t cb = VDGetSize(pData->pDisk, VD_LAST_IMAGE);
266 LogFlow(("%s: returns %#llx (%llu)\n", __FUNCTION__, cb, cb));
267 return cb;
268}
269
270/** @copydoc PDMIMEDIA::pfnIsReadOnly */
271static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
272{
273 LogFlow(("%s:\n", __FUNCTION__));
274 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
275 bool f = VDIsReadOnly(pData->pDisk);
276 LogFlow(("%s: returns %d\n", __FUNCTION__, f));
277 return f;
278}
279
280/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
281static DECLCALLBACK(int) drvvdBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
282 PPDMMEDIAGEOMETRY pPCHSGeometry)
283{
284 LogFlow(("%s:\n", __FUNCTION__));
285 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
286 int rc = VDGetPCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
287 if (VBOX_FAILURE(rc))
288 {
289 Log(("%s: geometry not available.\n", __FUNCTION__));
290 rc = VERR_PDM_GEOMETRY_NOT_SET;
291 }
292 LogFlow(("%s: returns %Vrc (CHS=%d/%d/%d)\n", __FUNCTION__,
293 rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
294 return rc;
295}
296
297/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
298static DECLCALLBACK(int) drvvdBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
299 PCPDMMEDIAGEOMETRY pPCHSGeometry)
300{
301 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
302 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
303 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
304 int rc = VDSetPCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
305 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
306 return rc;
307}
308
309/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
310static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
311 PPDMMEDIAGEOMETRY pLCHSGeometry)
312{
313 LogFlow(("%s:\n", __FUNCTION__));
314 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
315 int rc = VDGetLCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
316 if (VBOX_FAILURE(rc))
317 {
318 Log(("%s: geometry not available.\n", __FUNCTION__));
319 rc = VERR_PDM_GEOMETRY_NOT_SET;
320 }
321 LogFlow(("%s: returns %Vrc (CHS=%d/%d/%d)\n", __FUNCTION__,
322 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
323 return rc;
324}
325
326/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
327static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
328 PCPDMMEDIAGEOMETRY pLCHSGeometry)
329{
330 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
331 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
332 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
333 int rc = VDSetLCHSGeometry(pData->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
334 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
335 return rc;
336}
337
338/** @copydoc PDMIMEDIA::pfnGetUuid */
339static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
340{
341 LogFlow(("%s:\n", __FUNCTION__));
342 PVBOXDISK pData = PDMIMEDIA_2_VBOXDISK(pInterface);
343 int rc = VDGetUuid(pData->pDisk, 0, pUuid);
344 LogFlow(("%s: returns %Vrc ({%Vuuid})\n", __FUNCTION__, rc, pUuid));
345 return rc;
346}
347
348/*******************************************************************************
349* Async Media interface methods *
350*******************************************************************************/
351
352static DECLCALLBACK(int) drvvdStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
353 PPDMDATASEG paSeg, unsigned cSeg,
354 size_t cbRead, void *pvUser)
355{
356 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d\n pvUser=%#p", __FUNCTION__,
357 uOffset, paSeg, cSeg, cbRead, pvUser));
358 PVBOXDISK pData = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
359 int rc = VDAsyncRead(pData->pDisk, uOffset, cbRead, paSeg, cSeg, pvUser);
360 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
361 return rc;
362}
363
364static DECLCALLBACK(int) drvvdStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
365 PPDMDATASEG paSeg, unsigned cSeg,
366 size_t cbWrite, void *pvUser)
367{
368 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d\n pvUser=%#p", __FUNCTION__,
369 uOffset, paSeg, cSeg, cbWrite, pvUser));
370 PVBOXDISK pData = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
371 int rc = VDAsyncWrite(pData->pDisk, uOffset, cbWrite, paSeg, cSeg, pvUser);
372 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
373 return rc;
374}
375
376/*******************************************************************************
377* Async transport port interface methods *
378*******************************************************************************/
379
380static DECLCALLBACK(int) drvvdTasksCompleteNotify(PPDMITRANSPORTASYNCPORT pInterface, void *pvUser)
381{
382 PVBOXDISK pData = PDMITRANSPORTASYNCPORT_2_VBOXDISK(pInterface);
383 PDRVVDASYNCTASK pDrvVDAsyncTask = (PDRVVDASYNCTASK)pvUser;
384 int rc = VINF_VDI_ASYNC_IO_FINISHED;
385
386 /* Having a completion callback for a task is not mandatory. */
387 if (pDrvVDAsyncTask->pfnCompleted)
388 rc = pDrvVDAsyncTask->pfnCompleted(pDrvVDAsyncTask->pvUser);
389
390 /* Check if the request is finished. */
391 if (rc == VINF_VDI_ASYNC_IO_FINISHED)
392 {
393 rc = pData->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pData->pDrvMediaAsyncPort, pDrvVDAsyncTask->pvUserCaller);
394 }
395 else if (rc == VERR_VDI_ASYNC_IO_IN_PROGRESS)
396 rc = VINF_SUCCESS;
397
398 rc = RTCacheInsert(pData->pCache, pDrvVDAsyncTask);
399 AssertRC(rc);
400
401 return rc;
402}
403
404
405/*******************************************************************************
406* Base interface methods *
407*******************************************************************************/
408
409/** @copydoc PDMIBASE::pfnQueryInterface */
410static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface,
411 PDMINTERFACE enmInterface)
412{
413 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
414 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
415 switch (enmInterface)
416 {
417 case PDMINTERFACE_BASE:
418 return &pDrvIns->IBase;
419 case PDMINTERFACE_MEDIA:
420 return &pData->IMedia;
421 case PDMINTERFACE_MEDIA_ASYNC:
422 return pData->fAsyncIOSupported ? &pData->IMediaAsync : NULL;
423 case PDMINTERFACE_TRANSPORT_ASYNC_PORT:
424 return &pData->ITransportAsyncPort;
425 default:
426 return NULL;
427 }
428}
429
430
431/*******************************************************************************
432* Driver methods *
433*******************************************************************************/
434
435
436/**
437 * Construct a VBox disk media driver instance.
438 *
439 * @returns VBox status.
440 * @param pDrvIns The driver instance data.
441 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
442 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
443 * of the driver instance. It's also found in pDrvIns->pCfgHandle as it's expected
444 * to be used frequently in this function.
445 */
446static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
447 PCFGMNODE pCfgHandle)
448{
449 LogFlow(("%s:\n", __FUNCTION__));
450 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
451 int rc = VINF_SUCCESS;
452 char *pszName = NULL; /**< The path of the disk image file. */
453 char *pszFormat = NULL; /**< The format backed to use for this image. */
454 bool fReadOnly; /**< True if the media is readonly. */
455 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
456
457 /*
458 * Init the static parts.
459 */
460 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
461 pData->pDrvIns = pDrvIns;
462 pData->fTempReadOnly = false;
463 pData->pDisk = NULL;
464
465 /* IMedia */
466 pData->IMedia.pfnRead = drvvdRead;
467 pData->IMedia.pfnWrite = drvvdWrite;
468 pData->IMedia.pfnFlush = drvvdFlush;
469 pData->IMedia.pfnGetSize = drvvdGetSize;
470 pData->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
471 pData->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
472 pData->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
473 pData->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
474 pData->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
475 pData->IMedia.pfnGetUuid = drvvdGetUuid;
476
477 /* IMediaAsync */
478 pData->IMediaAsync.pfnStartRead = drvvdStartRead;
479 pData->IMediaAsync.pfnStartWrite = drvvdStartWrite;
480
481 /* ITransportAsyncPort */
482 pData->ITransportAsyncPort.pfnTaskCompleteNotify = drvvdTasksCompleteNotify;
483
484 /* Initialize supported VD interfaces. */
485 pData->VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
486 pData->VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
487 pData->VDIErrorCallbacks.pfnError = drvvdErrorCallback;
488
489 rc = VDInterfaceCreate(&pData->VDIError, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
490 &pData->VDIErrorCallbacks, pData, NULL);
491 AssertRC(rc);
492
493 pData->VDIAsyncIOCallbacks.cbSize = sizeof(VDINTERFACEASYNCIO);
494 pData->VDIAsyncIOCallbacks.enmInterface = VDINTERFACETYPE_ASYNCIO;
495 pData->VDIAsyncIOCallbacks.pfnOpen = drvvdAsyncIOOpen;
496 pData->VDIAsyncIOCallbacks.pfnClose = drvvdAsyncIOClose;
497 pData->VDIAsyncIOCallbacks.pfnRead = drvvdAsyncIORead;
498 pData->VDIAsyncIOCallbacks.pfnWrite = drvvdAsyncIOWrite;
499 pData->VDIAsyncIOCallbacks.pfnFlush = drvvdAsyncIOFlush;
500 pData->VDIAsyncIOCallbacks.pfnPrepareRead = drvvdAsyncIOPrepareRead;
501 pData->VDIAsyncIOCallbacks.pfnPrepareWrite = drvvdAsyncIOPrepareWrite;
502 pData->VDIAsyncIOCallbacks.pfnTasksSubmit = drvvdAsyncIOTasksSubmit;
503
504 rc = VDInterfaceCreate(&pData->VDIAsyncIO, "DrvVD_AsyncIO", VDINTERFACETYPE_ASYNCIO,
505 &pData->VDIAsyncIOCallbacks, pData, &pData->VDIError);
506 AssertRC(rc);
507
508 /* Try to attach async media port interface above.*/
509 pData->pDrvMediaAsyncPort = (PPDMIMEDIAASYNCPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MEDIA_ASYNC_PORT);
510
511 /*
512 * Attach the async transport driver below of the device above us implements the
513 * async interface.
514 */
515 if (pData->pDrvMediaAsyncPort)
516 {
517 /* Try to attach the driver. */
518 PPDMIBASE pBase;
519
520 rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
521 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
522 {
523 /*
524 * Though the device supports async I/O the backend seems to not support it.
525 * Revert to non async I/O.
526 */
527 pData->pDrvMediaAsyncPort = NULL;
528 }
529 else if (VBOX_FAILURE(rc))
530 {
531 AssertMsgFailed(("Failed to attach async transport driver below rc=%Vrc\n", rc));
532 }
533 else
534 {
535 /* Success query the async transport interface. */
536 pData->pDrvTransportAsync = (PPDMITRANSPORTASYNC)pBase->pfnQueryInterface(pBase, PDMINTERFACE_TRANSPORT_ASYNC);
537 if (!pData->pDrvTransportAsync)
538 {
539 /* Whoops. */
540 AssertMsgFailed(("Configuration error: No async transport interface below!\n"));
541 return VERR_PDM_MISSING_INTERFACE_ABOVE;
542
543 }
544 }
545 }
546
547 /*
548 * Validate configuration and find all parent images.
549 * It's sort of up side down from the image dependency tree.
550 */
551 unsigned iLevel = 0;
552 PCFGMNODE pCurNode = pCfgHandle;
553 for (;;)
554 {
555 bool fValid;
556
557 if (pCurNode == pCfgHandle)
558 {
559 /* Toplevel configuration additionally contains the global image
560 * open flags. Some might be converted to per-image flags later. */
561 fValid = CFGMR3AreValuesValid(pCurNode,
562 "Format\0Path\0"
563 "ReadOnly\0HonorZeroWrites\0");
564 }
565 else
566 {
567 /* All other image configurations only contain image name and
568 * the format information. */
569 fValid = CFGMR3AreValuesValid(pCurNode, "Format\0Path\0");
570 }
571 if (!fValid)
572 {
573 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
574 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
575 break;
576 }
577
578 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
579 if (!pParent)
580 break;
581 pCurNode = pParent;
582 iLevel++;
583 }
584
585 /*
586 * Open the images.
587 */
588 if (VBOX_SUCCESS(rc))
589 {
590 rc = VDCreate(&pData->VDIAsyncIO, &pData->pDisk);
591 /* Error message is already set correctly. */
592 }
593
594 while (pCurNode && VBOX_SUCCESS(rc))
595 {
596 /*
597 * Read the image configuration.
598 */
599 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
600 if (VBOX_FAILURE(rc))
601 {
602 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
603 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
604 break;
605 }
606
607 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Format", &pszFormat);
608 if (VBOX_FAILURE(rc))
609 {
610 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
611 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
612 break;
613 }
614
615 if (iLevel == 0)
616 {
617 rc = CFGMR3QueryBool(pCurNode, "ReadOnly", &fReadOnly);
618 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
619 fReadOnly = false;
620 else if (VBOX_FAILURE(rc))
621 {
622 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
623 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
624 break;
625 }
626
627 rc = CFGMR3QueryBool(pCfgHandle, "HonorZeroWrites", &fHonorZeroWrites);
628 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
629 fHonorZeroWrites = false;
630 else if (VBOX_FAILURE(rc))
631 {
632 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
633 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
634 break;
635 }
636 }
637 else
638 {
639 fReadOnly = true;
640 fHonorZeroWrites = false;
641 }
642
643 /*
644 * Open the image.
645 */
646 unsigned uOpenFlags;
647 if (fReadOnly)
648 uOpenFlags = VD_OPEN_FLAGS_READONLY;
649 else
650 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
651 if (fHonorZeroWrites)
652 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
653 if (pData->pDrvMediaAsyncPort)
654 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
655
656 /** Try to open backend in asyc I/O mode first. */
657 rc = VDOpen(pData->pDisk, pszFormat, pszName, uOpenFlags);
658 if (rc == VERR_NOT_SUPPORTED)
659 {
660 /* Seems asxnc I/O is not supported by the backend, open in normal mode. */
661 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
662 rc = VDOpen(pData->pDisk, pszFormat, pszName, uOpenFlags);
663 }
664
665 if (VBOX_SUCCESS(rc))
666 Log(("%s: %d - Opened '%s' in %s mode\n", __FUNCTION__,
667 iLevel, pszName,
668 VDIsReadOnly(pData->pDisk) ? "read-only" : "read-write"));
669 else
670 {
671 AssertMsgFailed(("Failed to open image '%s' rc=%Vrc\n", pszName, rc));
672 break;
673 }
674
675
676 MMR3HeapFree(pszName);
677 pszName = NULL;
678 MMR3HeapFree(pszFormat);
679 pszFormat = NULL;
680
681 /* next */
682 iLevel--;
683 pCurNode = CFGMR3GetParent(pCurNode);
684 }
685
686 if (VBOX_FAILURE(rc))
687 {
688 if (VALID_PTR(pData->pDisk))
689 {
690 VDDestroy(pData->pDisk);
691 pData->pDisk = NULL;
692 }
693 if (VALID_PTR(pszName))
694 MMR3HeapFree(pszName);
695 if (VALID_PTR(pszFormat))
696 MMR3HeapFree(pszFormat);
697 }
698
699 /*
700 * Check for async I/O support. Every opened image has to support
701 * it.
702 */
703 pData->fAsyncIOSupported = true;
704 for (unsigned i = 0; i < VDGetCount(pData->pDisk); i++)
705 {
706 VDBACKENDINFO vdBackendInfo;
707
708 rc = VDBackendInfoSingle(pData->pDisk, i, &vdBackendInfo);
709 AssertRC(rc);
710
711 if (vdBackendInfo.uBackendCaps & VD_CAP_ASYNC)
712 {
713 /*
714 * Backend indicates support for at least some files.
715 * Check if current file is supported with async I/O)
716 */
717 rc = VDImageIsAsyncIOSupported(pData->pDisk, i, &pData->fAsyncIOSupported);
718 AssertRC(rc);
719
720 /*
721 * Check if current image is supported.
722 * If not we can stop checking because
723 * at least one does not support it.
724 */
725 if (!pData->fAsyncIOSupported)
726 break;
727 }
728 else
729 {
730 pData->fAsyncIOSupported = false;
731 break;
732 }
733 }
734
735 /* Create cache if async I/O is supported. */
736 if (pData->fAsyncIOSupported)
737 {
738 rc = RTCacheCreate(&pData->pCache, 0, sizeof(DRVVDASYNCTASK), RTOBJCACHE_PROTECT_INSERT);
739 AssertMsg(RT_SUCCESS(rc), ("Failed to create cache rc=%Vrc\n", rc));
740 }
741
742 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
743 return rc;
744}
745
746/**
747 * Destruct a driver instance.
748 *
749 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
750 * resources can be freed correctly.
751 *
752 * @param pDrvIns The driver instance data.
753 */
754static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
755{
756 int rc;
757 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
758 LogFlow(("%s:\n", __FUNCTION__));
759 rc = RTCacheDestroy(pData->pCache);
760 AssertRC(rc);
761}
762
763
764/**
765 * When the VM has been suspended we'll change the image mode to read-only
766 * so that main and others can read the VDIs. This is important when
767 * saving state and so forth.
768 *
769 * @param pDrvIns The driver instance data.
770 */
771static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
772{
773 LogFlow(("%s:\n", __FUNCTION__));
774 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
775 if (!VDIsReadOnly(pData->pDisk))
776 {
777 unsigned uOpenFlags;
778 int rc = VDGetOpenFlags(pData->pDisk, VD_LAST_IMAGE, &uOpenFlags);
779 AssertRC(rc);
780 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
781 rc = VDSetOpenFlags(pData->pDisk, VD_LAST_IMAGE, uOpenFlags);
782 AssertRC(rc);
783 pData->fTempReadOnly = true;
784 }
785}
786
787/**
788 * Before the VM resumes we'll have to undo the read-only mode change
789 * done in drvvdSuspend.
790 *
791 * @param pDrvIns The driver instance data.
792 */
793static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
794{
795 LogFlow(("%s:\n", __FUNCTION__));
796 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
797 if (pData->fTempReadOnly)
798 {
799 unsigned uOpenFlags;
800 int rc = VDGetOpenFlags(pData->pDisk, VD_LAST_IMAGE, &uOpenFlags);
801 AssertRC(rc);
802 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
803 rc = VDSetOpenFlags(pData->pDisk, VD_LAST_IMAGE, uOpenFlags);
804 AssertRC(rc);
805 pData->fTempReadOnly = false;
806 }
807}
808
809static DECLCALLBACK(void) drvvdPowerOff(PPDMDRVINS pDrvIns)
810{
811 LogFlow(("%s:\n", __FUNCTION__));
812 PVBOXDISK pData = PDMINS2DATA(pDrvIns, PVBOXDISK);
813
814 /*
815 * We must close the disk here to ensure that
816 * the backend closes all files before the
817 * async transport driver is destructed.
818 */
819 int rc = VDCloseAll(pData->pDisk);
820 AssertRC(rc);
821}
822
823/**
824 * VBox disk container media driver registration record.
825 */
826const PDMDRVREG g_DrvVD =
827{
828 /* u32Version */
829 PDM_DRVREG_VERSION,
830 /* szDriverName */
831 "VD",
832 /* pszDescription */
833 "Generic VBox disk media driver.",
834 /* fFlags */
835 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
836 /* fClass. */
837 PDM_DRVREG_CLASS_MEDIA,
838 /* cMaxInstances */
839 ~0,
840 /* cbInstance */
841 sizeof(VBOXDISK),
842 /* pfnConstruct */
843 drvvdConstruct,
844 /* pfnDestruct */
845 drvvdDestruct,
846 /* pfnIOCtl */
847 NULL,
848 /* pfnPowerOn */
849 NULL,
850 /* pfnReset */
851 NULL,
852 /* pfnSuspend */
853 drvvdSuspend,
854 /* pfnResume */
855 drvvdResume,
856 /* pfnDetach */
857 NULL,
858 /* pfnPowerOff */
859 drvvdPowerOff
860};
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