VirtualBox

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

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

Better fix for not working VMDK's with SATA, a missing define in the Main Makefile prevented async I/O. More checks in drvvdConstruct.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.9 KB
Line 
1/** $Id: DrvVD.cpp 12474 2008-09-15 19:02:54Z 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 ( PDMINS_2_DATA(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, image information, private part.
80 */
81
82typedef struct VBOXIMAGE
83{
84 /** Pointer to next image. */
85 struct VBOXIMAGE *pNext;
86 /** Pointer to list of VD interfaces. Per-image. */
87 PVDINTERFACE pVDIfsImage;
88 /** Common structure for the configuration information interface. */
89 VDINTERFACE VDIConfig;
90} VBOXIMAGE, *PVBOXIMAGE;
91
92/**
93 * VBox disk container media main structure, private part.
94 */
95typedef struct VBOXDISK
96{
97 /** The VBox disk container. */
98 PVBOXHDD pDisk;
99 /** The media interface. */
100 PDMIMEDIA IMedia;
101 /** Pointer to the driver instance. */
102 PPDMDRVINS pDrvIns;
103 /** Flag whether suspend has changed image open mode to read only. */
104 bool fTempReadOnly;
105 /** Pointer to list of VD interfaces. Per-disk. */
106 PVDINTERFACE pVDIfsDisk;
107 /** Common structure for the supported error interface. */
108 VDINTERFACE VDIError;
109 /** Callback table for error interface. */
110 VDINTERFACEERROR VDIErrorCallbacks;
111 /** Common structure for the supported async I/O interface. */
112 VDINTERFACE VDIAsyncIO;
113 /** Callback table for async I/O interface. */
114 VDINTERFACEASYNCIO VDIAsyncIOCallbacks;
115 /** Callback table for the configuration information interface. */
116 VDINTERFACECONFIG VDIConfigCallbacks;
117 /** Flag whether opened disk suppports async I/O operations. */
118 bool fAsyncIOSupported;
119 /** The async media interface. */
120 PDMIMEDIAASYNC IMediaAsync;
121 /** The async media port interface above. */
122 PPDMIMEDIAASYNCPORT pDrvMediaAsyncPort;
123 /** Pointer to the asynchronous media driver below. */
124 PPDMITRANSPORTASYNC pDrvTransportAsync;
125 /** Async transport port interface. */
126 PDMITRANSPORTASYNCPORT ITransportAsyncPort;
127 /** Our cache to reduce allocation overhead. */
128 PRTOBJCACHE pCache;
129 /** Pointer to the list of data we need to keep per image. */
130 PVBOXIMAGE pImages;
131} VBOXDISK, *PVBOXDISK;
132
133/*******************************************************************************
134* Error reporting callback *
135*******************************************************************************/
136
137static void drvvdErrorCallback(void *pvUser, int rc, RT_SRC_POS_DECL,
138 const char *pszFormat, va_list va)
139{
140 PPDMDRVINS pDrvIns = (PPDMDRVINS)pvUser;
141 pDrvIns->pDrvHlp->pfnVMSetErrorV(pDrvIns, rc, RT_SRC_POS_ARGS, pszFormat, va);
142}
143
144
145/**
146 * Internal: allocate new image descriptor and put it in the list
147 */
148static PVBOXIMAGE drvvdNewImage(PVBOXDISK pThis)
149{
150 AssertPtr(pThis);
151 PVBOXIMAGE pImage = (PVBOXIMAGE)RTMemAllocZ(sizeof(VBOXIMAGE));
152 if (pImage)
153 {
154 pImage->pVDIfsImage = NULL;
155 PVBOXIMAGE *pp = &pThis->pImages;
156 while (*pp != NULL)
157 pp = &(*pp)->pNext;
158 *pp = pImage;
159 pImage->pNext = NULL;
160 }
161
162 return pImage;
163}
164
165/**
166 * Internal: free the list of images descriptors.
167 */
168static void drvvdFreeImages(PVBOXDISK pThis)
169{
170 while (pThis->pImages != NULL)
171 {
172 PVBOXIMAGE p = pThis->pImages;
173 pThis->pImages = pThis->pImages->pNext;
174 RTMemFree(p);
175 }
176}
177
178/*******************************************************************************
179* VD Async I/O interface implementation *
180*******************************************************************************/
181
182static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation, bool fReadonly, void **ppStorage)
183{
184 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
185
186 return pDrvVD->pDrvTransportAsync->pfnOpen(pDrvVD->pDrvTransportAsync, pszLocation, fReadonly, ppStorage);
187}
188
189static DECLCALLBACK(int) drvvdAsyncIOClose(void *pvUser, void *pStorage)
190{
191 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
192
193 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
194
195 return pDrvVD->pDrvTransportAsync->pfnClose(pDrvVD->pDrvTransportAsync, pStorage);
196}
197
198static DECLCALLBACK(int) drvvdAsyncIORead(void *pvUser, void *pStorage, uint64_t uOffset,
199 size_t cbRead, void *pvBuf, size_t *pcbRead)
200{
201 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
202
203 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
204
205 return pDrvVD->pDrvTransportAsync->pfnReadSynchronous(pDrvVD->pDrvTransportAsync,
206 pStorage,
207 uOffset, pvBuf, cbRead, pcbRead);
208}
209
210static DECLCALLBACK(int) drvvdAsyncIOWrite(void *pvUser, void *pStorage, uint64_t uOffset,
211 size_t cbWrite, const void *pvBuf, size_t *pcbWritten)
212{
213 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
214
215 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
216
217 return pDrvVD->pDrvTransportAsync->pfnWriteSynchronous(pDrvVD->pDrvTransportAsync,
218 pStorage,
219 uOffset, pvBuf, cbWrite, pcbWritten);
220}
221
222static DECLCALLBACK(int) drvvdAsyncIOFlush(void *pvUser, void *pStorage)
223{
224 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
225
226 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
227
228 return pDrvVD->pDrvTransportAsync->pfnFlushSynchronous(pDrvVD->pDrvTransportAsync, pStorage);
229}
230
231static DECLCALLBACK(int) drvvdAsyncIOPrepareRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuf, size_t cbRead, void **ppTask)
232{
233 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
234
235 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
236
237 return pDrvVD->pDrvTransportAsync->pfnPrepareRead(pDrvVD->pDrvTransportAsync, pStorage, uOffset, pvBuf, cbRead, ppTask);
238}
239
240static DECLCALLBACK(int) drvvdAsyncIOPrepareWrite(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuf, size_t cbWrite, void **ppTask)
241{
242 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
243
244 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
245
246 return pDrvVD->pDrvTransportAsync->pfnPrepareWrite(pDrvVD->pDrvTransportAsync, pStorage, uOffset, pvBuf, cbWrite, ppTask);
247}
248
249static DECLCALLBACK(int) drvvdAsyncIOTasksSubmit(void *pvUser, void *apTasks[], unsigned cTasks, void *pvUser2,
250 void *pvUserCaller, PFNVDCOMPLETED pfnTasksCompleted)
251{
252 PVBOXDISK pDrvVD = (PVBOXDISK)pvUser;
253 PDRVVDASYNCTASK pDrvVDAsyncTask;
254 int rc;
255
256 AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
257
258 rc = RTCacheRequest(pDrvVD->pCache, (void **)&pDrvVDAsyncTask);
259
260 if (RT_FAILURE(rc))
261 return rc;
262
263 pDrvVDAsyncTask->pfnCompleted = pfnTasksCompleted;
264 pDrvVDAsyncTask->pvUser = pvUser2;
265 pDrvVDAsyncTask->pvUserCaller = pvUserCaller;
266
267 return pDrvVD->pDrvTransportAsync->pfnTasksSubmit(pDrvVD->pDrvTransportAsync, apTasks, cTasks, pDrvVDAsyncTask);
268}
269
270/*******************************************************************************
271* VD Configuration interface implementation *
272*******************************************************************************/
273
274static bool drvvdCfgAreValuesValid(PVDCFGNODE pNode, const char *pszzValid)
275{
276 return CFGMR3AreValuesValid((PCFGMNODE)pNode, pszzValid);
277}
278
279static int drvvdCfgQueryType(PVDCFGNODE pNode, const char *pszName, PVDCFGVALUETYPE penmType)
280{
281 Assert(VDCFGVALUETYPE_INTEGER == (VDCFGVALUETYPE)CFGMVALUETYPE_INTEGER);
282 Assert(VDCFGVALUETYPE_STRING == (VDCFGVALUETYPE)CFGMVALUETYPE_STRING);
283 Assert(VDCFGVALUETYPE_BYTES == (VDCFGVALUETYPE)CFGMVALUETYPE_BYTES);
284 return CFGMR3QueryType((PCFGMNODE)pNode, pszName, (PCFGMVALUETYPE)penmType);
285}
286
287static int drvvdCfgQuerySize(PVDCFGNODE pNode, const char *pszName, size_t *pcb)
288{
289 return CFGMR3QuerySize((PCFGMNODE)pNode, pszName, pcb);
290}
291
292static int drvvdCfgQueryInteger(PVDCFGNODE pNode, const char *pszName, uint64_t *pu64)
293{
294 return CFGMR3QueryInteger((PCFGMNODE)pNode, pszName, pu64);
295}
296
297static int drvvdCfgQueryIntegerDef(PVDCFGNODE pNode, const char *pszName, uint64_t *pu64, uint64_t u64Def)
298{
299 return CFGMR3QueryInteger((PCFGMNODE)pNode, pszName, pu64);
300}
301
302static int drvvdCfgQueryString(PVDCFGNODE pNode, const char *pszName, char *pszString, size_t cchString)
303{
304 return CFGMR3QueryString((PCFGMNODE)pNode, pszName, pszString, cchString);
305}
306
307static int drvvdCfgQueryStringDef(PVDCFGNODE pNode, const char *pszName, char *pszString, size_t cchString, const char *pszDef)
308{
309 return CFGMR3QueryStringDef((PCFGMNODE)pNode, pszName, pszString, cchString, pszDef);
310}
311
312static int drvvdCfgQueryBytes(PVDCFGNODE pNode, const char *pszName, void *pvData, size_t cbData)
313{
314 return CFGMR3QueryBytes((PCFGMNODE)pNode, pszName, pvData, cbData);
315}
316
317
318/*******************************************************************************
319* Media interface methods *
320*******************************************************************************/
321
322/** @copydoc PDMIMEDIA::pfnRead */
323static DECLCALLBACK(int) drvvdRead(PPDMIMEDIA pInterface,
324 uint64_t off, void *pvBuf, size_t cbRead)
325{
326 LogFlow(("%s: off=%#llx pvBuf=%p cbRead=%d\n", __FUNCTION__,
327 off, pvBuf, cbRead));
328 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
329 int rc = VDRead(pThis->pDisk, off, pvBuf, cbRead);
330 if (RT_SUCCESS(rc))
331 Log2(("%s: off=%#llx pvBuf=%p cbRead=%d %.*Vhxd\n", __FUNCTION__,
332 off, pvBuf, cbRead, cbRead, pvBuf));
333 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
334 return rc;
335}
336
337/** @copydoc PDMIMEDIA::pfnWrite */
338static DECLCALLBACK(int) drvvdWrite(PPDMIMEDIA pInterface,
339 uint64_t off, const void *pvBuf,
340 size_t cbWrite)
341{
342 LogFlow(("%s: off=%#llx pvBuf=%p cbWrite=%d\n", __FUNCTION__,
343 off, pvBuf, cbWrite));
344 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
345 Log2(("%s: off=%#llx pvBuf=%p cbWrite=%d %.*Vhxd\n", __FUNCTION__,
346 off, pvBuf, cbWrite, cbWrite, pvBuf));
347 int rc = VDWrite(pThis->pDisk, off, pvBuf, cbWrite);
348 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
349 return rc;
350}
351
352/** @copydoc PDMIMEDIA::pfnFlush */
353static DECLCALLBACK(int) drvvdFlush(PPDMIMEDIA pInterface)
354{
355 LogFlow(("%s:\n", __FUNCTION__));
356 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
357 int rc = VDFlush(pThis->pDisk);
358 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
359 return rc;
360}
361
362/** @copydoc PDMIMEDIA::pfnGetSize */
363static DECLCALLBACK(uint64_t) drvvdGetSize(PPDMIMEDIA pInterface)
364{
365 LogFlow(("%s:\n", __FUNCTION__));
366 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
367 uint64_t cb = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
368 LogFlow(("%s: returns %#llx (%llu)\n", __FUNCTION__, cb, cb));
369 return cb;
370}
371
372/** @copydoc PDMIMEDIA::pfnIsReadOnly */
373static DECLCALLBACK(bool) drvvdIsReadOnly(PPDMIMEDIA pInterface)
374{
375 LogFlow(("%s:\n", __FUNCTION__));
376 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
377 bool f = VDIsReadOnly(pThis->pDisk);
378 LogFlow(("%s: returns %d\n", __FUNCTION__, f));
379 return f;
380}
381
382/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
383static DECLCALLBACK(int) drvvdBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
384 PPDMMEDIAGEOMETRY pPCHSGeometry)
385{
386 LogFlow(("%s:\n", __FUNCTION__));
387 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
388 int rc = VDGetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
389 if (RT_FAILURE(rc))
390 {
391 Log(("%s: geometry not available.\n", __FUNCTION__));
392 rc = VERR_PDM_GEOMETRY_NOT_SET;
393 }
394 LogFlow(("%s: returns %Rrc (CHS=%d/%d/%d)\n", __FUNCTION__,
395 rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
396 return rc;
397}
398
399/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
400static DECLCALLBACK(int) drvvdBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
401 PCPDMMEDIAGEOMETRY pPCHSGeometry)
402{
403 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
404 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
405 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
406 int rc = VDSetPCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pPCHSGeometry);
407 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
408 return rc;
409}
410
411/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
412static DECLCALLBACK(int) drvvdBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
413 PPDMMEDIAGEOMETRY pLCHSGeometry)
414{
415 LogFlow(("%s:\n", __FUNCTION__));
416 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
417 int rc = VDGetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
418 if (RT_FAILURE(rc))
419 {
420 Log(("%s: geometry not available.\n", __FUNCTION__));
421 rc = VERR_PDM_GEOMETRY_NOT_SET;
422 }
423 LogFlow(("%s: returns %Rrc (CHS=%d/%d/%d)\n", __FUNCTION__,
424 rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
425 return rc;
426}
427
428/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
429static DECLCALLBACK(int) drvvdBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
430 PCPDMMEDIAGEOMETRY pLCHSGeometry)
431{
432 LogFlow(("%s: CHS=%d/%d/%d\n", __FUNCTION__,
433 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
434 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
435 int rc = VDSetLCHSGeometry(pThis->pDisk, VD_LAST_IMAGE, pLCHSGeometry);
436 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
437 return rc;
438}
439
440/** @copydoc PDMIMEDIA::pfnGetUuid */
441static DECLCALLBACK(int) drvvdGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
442{
443 LogFlow(("%s:\n", __FUNCTION__));
444 PVBOXDISK pThis = PDMIMEDIA_2_VBOXDISK(pInterface);
445 int rc = VDGetUuid(pThis->pDisk, 0, pUuid);
446 LogFlow(("%s: returns %Rrc ({%RTuuid})\n", __FUNCTION__, rc, pUuid));
447 return rc;
448}
449
450/*******************************************************************************
451* Async Media interface methods *
452*******************************************************************************/
453
454static DECLCALLBACK(int) drvvdStartRead(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
455 PPDMDATASEG paSeg, unsigned cSeg,
456 size_t cbRead, void *pvUser)
457{
458 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbRead=%d\n pvUser=%#p", __FUNCTION__,
459 uOffset, paSeg, cSeg, cbRead, pvUser));
460 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
461 int rc = VDAsyncRead(pThis->pDisk, uOffset, cbRead, paSeg, cSeg, pvUser);
462 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
463 return rc;
464}
465
466static DECLCALLBACK(int) drvvdStartWrite(PPDMIMEDIAASYNC pInterface, uint64_t uOffset,
467 PPDMDATASEG paSeg, unsigned cSeg,
468 size_t cbWrite, void *pvUser)
469{
470 LogFlow(("%s: uOffset=%#llx paSeg=%#p cSeg=%u cbWrite=%d\n pvUser=%#p", __FUNCTION__,
471 uOffset, paSeg, cSeg, cbWrite, pvUser));
472 PVBOXDISK pThis = PDMIMEDIAASYNC_2_VBOXDISK(pInterface);
473 int rc = VDAsyncWrite(pThis->pDisk, uOffset, cbWrite, paSeg, cSeg, pvUser);
474 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
475 return rc;
476}
477
478/*******************************************************************************
479* Async transport port interface methods *
480*******************************************************************************/
481
482static DECLCALLBACK(int) drvvdTasksCompleteNotify(PPDMITRANSPORTASYNCPORT pInterface, void *pvUser)
483{
484 PVBOXDISK pThis = PDMITRANSPORTASYNCPORT_2_VBOXDISK(pInterface);
485 PDRVVDASYNCTASK pDrvVDAsyncTask = (PDRVVDASYNCTASK)pvUser;
486 int rc = VINF_VDI_ASYNC_IO_FINISHED;
487
488 /* Having a completion callback for a task is not mandatory. */
489 if (pDrvVDAsyncTask->pfnCompleted)
490 rc = pDrvVDAsyncTask->pfnCompleted(pDrvVDAsyncTask->pvUser);
491
492 /* Check if the request is finished. */
493 if (rc == VINF_VDI_ASYNC_IO_FINISHED)
494 {
495 rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pDrvVDAsyncTask->pvUserCaller);
496 }
497 else if (rc == VERR_VDI_ASYNC_IO_IN_PROGRESS)
498 rc = VINF_SUCCESS;
499
500 rc = RTCacheInsert(pThis->pCache, pDrvVDAsyncTask);
501 AssertRC(rc);
502
503 return rc;
504}
505
506
507/*******************************************************************************
508* Base interface methods *
509*******************************************************************************/
510
511/** @copydoc PDMIBASE::pfnQueryInterface */
512static DECLCALLBACK(void *) drvvdQueryInterface(PPDMIBASE pInterface,
513 PDMINTERFACE enmInterface)
514{
515 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
516 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
517 switch (enmInterface)
518 {
519 case PDMINTERFACE_BASE:
520 return &pDrvIns->IBase;
521 case PDMINTERFACE_MEDIA:
522 return &pThis->IMedia;
523 case PDMINTERFACE_MEDIA_ASYNC:
524 return pThis->fAsyncIOSupported ? &pThis->IMediaAsync : NULL;
525 case PDMINTERFACE_TRANSPORT_ASYNC_PORT:
526 return &pThis->ITransportAsyncPort;
527 default:
528 return NULL;
529 }
530}
531
532
533/*******************************************************************************
534* Driver methods *
535*******************************************************************************/
536
537
538/**
539 * Construct a VBox disk media driver instance.
540 *
541 * @returns VBox status.
542 * @param pDrvIns The driver instance data.
543 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
544 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
545 * of the driver instance. It's also found in pDrvIns->pCfgHandle as it's expected
546 * to be used frequently in this function.
547 */
548static DECLCALLBACK(int) drvvdConstruct(PPDMDRVINS pDrvIns,
549 PCFGMNODE pCfgHandle)
550{
551 LogFlow(("%s:\n", __FUNCTION__));
552 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
553 int rc = VINF_SUCCESS;
554 char *pszName = NULL; /**< The path of the disk image file. */
555 char *pszFormat = NULL; /**< The format backed to use for this image. */
556 bool fReadOnly; /**< True if the media is readonly. */
557 bool fHonorZeroWrites; /**< True if zero blocks should be written. */
558
559 /*
560 * Init the static parts.
561 */
562 pDrvIns->IBase.pfnQueryInterface = drvvdQueryInterface;
563 pThis->pDrvIns = pDrvIns;
564 pThis->fTempReadOnly = false;
565 pThis->pDisk = NULL;
566 pThis->fAsyncIOSupported = false;
567
568 /* IMedia */
569 pThis->IMedia.pfnRead = drvvdRead;
570 pThis->IMedia.pfnWrite = drvvdWrite;
571 pThis->IMedia.pfnFlush = drvvdFlush;
572 pThis->IMedia.pfnGetSize = drvvdGetSize;
573 pThis->IMedia.pfnIsReadOnly = drvvdIsReadOnly;
574 pThis->IMedia.pfnBiosGetPCHSGeometry = drvvdBiosGetPCHSGeometry;
575 pThis->IMedia.pfnBiosSetPCHSGeometry = drvvdBiosSetPCHSGeometry;
576 pThis->IMedia.pfnBiosGetLCHSGeometry = drvvdBiosGetLCHSGeometry;
577 pThis->IMedia.pfnBiosSetLCHSGeometry = drvvdBiosSetLCHSGeometry;
578 pThis->IMedia.pfnGetUuid = drvvdGetUuid;
579
580 /* IMediaAsync */
581 pThis->IMediaAsync.pfnStartRead = drvvdStartRead;
582 pThis->IMediaAsync.pfnStartWrite = drvvdStartWrite;
583
584 /* ITransportAsyncPort */
585 pThis->ITransportAsyncPort.pfnTaskCompleteNotify = drvvdTasksCompleteNotify;
586
587 /* Initialize supported VD interfaces. */
588 pThis->pVDIfsDisk = NULL;
589
590 pThis->VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
591 pThis->VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
592 pThis->VDIErrorCallbacks.pfnError = drvvdErrorCallback;
593
594 rc = VDInterfaceAdd(&pThis->VDIError, "DrvVD_VDIError", VDINTERFACETYPE_ERROR,
595 &pThis->VDIErrorCallbacks, pDrvIns, &pThis->pVDIfsDisk);
596 AssertRC(rc);
597
598 pThis->VDIAsyncIOCallbacks.cbSize = sizeof(VDINTERFACEASYNCIO);
599 pThis->VDIAsyncIOCallbacks.enmInterface = VDINTERFACETYPE_ASYNCIO;
600 pThis->VDIAsyncIOCallbacks.pfnOpen = drvvdAsyncIOOpen;
601 pThis->VDIAsyncIOCallbacks.pfnClose = drvvdAsyncIOClose;
602 pThis->VDIAsyncIOCallbacks.pfnRead = drvvdAsyncIORead;
603 pThis->VDIAsyncIOCallbacks.pfnWrite = drvvdAsyncIOWrite;
604 pThis->VDIAsyncIOCallbacks.pfnFlush = drvvdAsyncIOFlush;
605 pThis->VDIAsyncIOCallbacks.pfnPrepareRead = drvvdAsyncIOPrepareRead;
606 pThis->VDIAsyncIOCallbacks.pfnPrepareWrite = drvvdAsyncIOPrepareWrite;
607 pThis->VDIAsyncIOCallbacks.pfnTasksSubmit = drvvdAsyncIOTasksSubmit;
608
609 rc = VDInterfaceAdd(&pThis->VDIAsyncIO, "DrvVD_AsyncIO", VDINTERFACETYPE_ASYNCIO,
610 &pThis->VDIAsyncIOCallbacks, pThis, &pThis->pVDIfsDisk);
611 AssertRC(rc);
612
613 /* This is just prepared here, the actual interface is per-image, so it's
614 * added later. No need to have separate callback tables. */
615 pThis->VDIConfigCallbacks.cbSize = sizeof(VDINTERFACECONFIG);
616 pThis->VDIConfigCallbacks.enmInterface = VDINTERFACETYPE_CONFIG;
617 pThis->VDIConfigCallbacks.pfnAreValuesValid = drvvdCfgAreValuesValid;
618 pThis->VDIConfigCallbacks.pfnQueryType = drvvdCfgQueryType;
619 pThis->VDIConfigCallbacks.pfnQuerySize = drvvdCfgQuerySize;
620 pThis->VDIConfigCallbacks.pfnQueryInteger = drvvdCfgQueryInteger;
621 pThis->VDIConfigCallbacks.pfnQueryIntegerDef = drvvdCfgQueryIntegerDef;
622 pThis->VDIConfigCallbacks.pfnQueryString = drvvdCfgQueryString;
623 pThis->VDIConfigCallbacks.pfnQueryStringDef = drvvdCfgQueryStringDef;
624 pThis->VDIConfigCallbacks.pfnQueryBytes = drvvdCfgQueryBytes;
625
626 /* List of images is empty now. */
627 pThis->pImages = NULL;
628
629 /* Try to attach async media port interface above.*/
630 pThis->pDrvMediaAsyncPort = (PPDMIMEDIAASYNCPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MEDIA_ASYNC_PORT);
631
632 /*
633 * Attach the async transport driver below of the device above us implements the
634 * async interface.
635 */
636 if (pThis->pDrvMediaAsyncPort)
637 {
638 /* Try to attach the driver. */
639 PPDMIBASE pBase;
640
641 rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
642 if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
643 {
644 /*
645 * Though the device supports async I/O there is no transport driver
646 * which processes async requests.
647 * Revert to non async I/O.
648 */
649 rc = VINF_SUCCESS;
650 pThis->pDrvMediaAsyncPort = NULL;
651 pThis->fAsyncIOSupported = false;
652 }
653 else if (RT_FAILURE(rc))
654 {
655 AssertMsgFailed(("Failed to attach async transport driver below rc=%Rrc\n", rc));
656 }
657 else
658 {
659 /*
660 * The device supports async I/O and we successfully attached the transport driver.
661 * Indicate that async I/O is supported for now as we check if the image backend supports
662 * it later.
663 */
664 pThis->fAsyncIOSupported = true;
665
666 /* Success query the async transport interface. */
667 pThis->pDrvTransportAsync = (PPDMITRANSPORTASYNC)pBase->pfnQueryInterface(pBase, PDMINTERFACE_TRANSPORT_ASYNC);
668 if (!pThis->pDrvTransportAsync)
669 {
670 /* An attached driver without an async transport interface - impossible. */
671 AssertMsgFailed(("Configuration error: No async transport interface below!\n"));
672 return VERR_PDM_MISSING_INTERFACE_ABOVE;
673 }
674 }
675 }
676
677 /*
678 * Validate configuration and find all parent images.
679 * It's sort of up side down from the image dependency tree.
680 */
681 unsigned iLevel = 0;
682 PCFGMNODE pCurNode = pCfgHandle;
683
684 for (;;)
685 {
686 bool fValid;
687
688 if (pCurNode == pCfgHandle)
689 {
690 /* Toplevel configuration additionally contains the global image
691 * open flags. Some might be converted to per-image flags later. */
692 fValid = CFGMR3AreValuesValid(pCurNode,
693 "Format\0Path\0"
694 "ReadOnly\0HonorZeroWrites\0");
695 }
696 else
697 {
698 /* All other image configurations only contain image name and
699 * the format information. */
700 fValid = CFGMR3AreValuesValid(pCurNode, "Format\0Path\0");
701 }
702 if (!fValid)
703 {
704 rc = PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
705 RT_SRC_POS, N_("DrvVD: Configuration error: keys incorrect at level %d"), iLevel);
706 break;
707 }
708
709 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
710 if (!pParent)
711 break;
712 pCurNode = pParent;
713 iLevel++;
714 }
715
716 /*
717 * Open the images.
718 */
719 if (RT_SUCCESS(rc))
720 {
721 rc = VDCreate(pThis->pVDIfsDisk, &pThis->pDisk);
722 /* Error message is already set correctly. */
723 }
724
725 while (pCurNode && RT_SUCCESS(rc))
726 {
727 /* Allocate per-image data. */
728 PVBOXIMAGE pImage = drvvdNewImage(pThis);
729 if (!pImage)
730 {
731 rc = VERR_NO_MEMORY;
732 break;
733 }
734
735 /*
736 * Read the image configuration.
737 */
738 rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
739 if (RT_FAILURE(rc))
740 {
741 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
742 N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
743 break;
744 }
745
746 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Format", &pszFormat);
747 if (RT_FAILURE(rc))
748 {
749 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
750 N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
751 break;
752 }
753
754 if (iLevel == 0)
755 {
756 rc = CFGMR3QueryBool(pCurNode, "ReadOnly", &fReadOnly);
757 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
758 fReadOnly = false;
759 else if (RT_FAILURE(rc))
760 {
761 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
762 N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
763 break;
764 }
765
766 rc = CFGMR3QueryBool(pCfgHandle, "HonorZeroWrites", &fHonorZeroWrites);
767 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
768 fHonorZeroWrites = false;
769 else if (RT_FAILURE(rc))
770 {
771 rc = PDMDRV_SET_ERROR(pDrvIns, rc,
772 N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
773 break;
774 }
775 }
776 else
777 {
778 fReadOnly = true;
779 fHonorZeroWrites = false;
780 }
781
782 PCFGMNODE pCfg = CFGMR3GetChild(pCurNode, "VDConfig");
783 rc = VDInterfaceAdd(&pImage->VDIConfig, "DrvVD_Config", VDINTERFACETYPE_CONFIG,
784 &pThis->VDIConfigCallbacks, pCfg, &pImage->pVDIfsImage);
785 AssertRC(rc);
786
787 /*
788 * Open the image.
789 */
790 unsigned uOpenFlags;
791 if (fReadOnly)
792 uOpenFlags = VD_OPEN_FLAGS_READONLY;
793 else
794 uOpenFlags = VD_OPEN_FLAGS_NORMAL;
795 if (fHonorZeroWrites)
796 uOpenFlags |= VD_OPEN_FLAGS_HONOR_ZEROES;
797 if (pThis->pDrvMediaAsyncPort)
798 uOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
799
800 /** Try to open backend in asyc I/O mode first. */
801 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
802 if (rc == VERR_NOT_SUPPORTED)
803 {
804 /* Seems async I/O is not supported by the backend, open in normal mode. */
805 uOpenFlags &= ~VD_OPEN_FLAGS_ASYNC_IO;
806 rc = VDOpen(pThis->pDisk, pszFormat, pszName, uOpenFlags, pImage->pVDIfsImage);
807 }
808
809 if (RT_SUCCESS(rc))
810 Log(("%s: %d - Opened '%s' in %s mode\n", __FUNCTION__,
811 iLevel, pszName,
812 VDIsReadOnly(pThis->pDisk) ? "read-only" : "read-write"));
813 else
814 {
815 AssertMsgFailed(("Failed to open image '%s' rc=%Rrc\n", pszName, rc));
816 break;
817 }
818
819
820 MMR3HeapFree(pszName);
821 pszName = NULL;
822 MMR3HeapFree(pszFormat);
823 pszFormat = NULL;
824
825 /* next */
826 iLevel--;
827 pCurNode = CFGMR3GetParent(pCurNode);
828 }
829
830 if (RT_FAILURE(rc))
831 {
832 if (VALID_PTR(pThis->pDisk))
833 {
834 VDDestroy(pThis->pDisk);
835 pThis->pDisk = NULL;
836 }
837 drvvdFreeImages(pThis);
838 if (VALID_PTR(pszName))
839 MMR3HeapFree(pszName);
840 if (VALID_PTR(pszFormat))
841 MMR3HeapFree(pszFormat);
842
843 return rc;
844 }
845 else
846 {
847 /*
848 * Check if every opened image supports async I/O.
849 * If not we revert to non async I/O.
850 */
851 if (pThis->fAsyncIOSupported)
852 {
853 for (unsigned i = 0; i < VDGetCount(pThis->pDisk); i++)
854 {
855 VDBACKENDINFO vdBackendInfo;
856
857 rc = VDBackendInfoSingle(pThis->pDisk, i, &vdBackendInfo);
858 AssertRC(rc);
859
860 if (vdBackendInfo.uBackendCaps & VD_CAP_ASYNC)
861 {
862 /*
863 * Backend indicates support for at least some files.
864 * Check if current file is supported with async I/O)
865 */
866 rc = VDImageIsAsyncIOSupported(pThis->pDisk, i, &pThis->fAsyncIOSupported);
867 AssertRC(rc);
868
869 /*
870 * Check if current image is supported.
871 * If not we can stop checking because
872 * at least one does not support it.
873 */
874 if (!pThis->fAsyncIOSupported)
875 break;
876 }
877 else
878 {
879 pThis->fAsyncIOSupported = false;
880 break;
881 }
882 }
883 }
884
885 /*
886 * We know definitly if async I/O is supported now.
887 * Create cache if it is supported.
888 */
889 if (pThis->fAsyncIOSupported)
890 {
891 rc = RTCacheCreate(&pThis->pCache, 0, sizeof(DRVVDASYNCTASK), RTOBJCACHE_PROTECT_INSERT);
892 AssertMsg(RT_SUCCESS(rc), ("Failed to create cache rc=%Rrc\n", rc));
893 }
894 }
895
896 LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
897 return rc;
898}
899
900/**
901 * Destruct a driver instance.
902 *
903 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
904 * resources can be freed correctly.
905 *
906 * @param pDrvIns The driver instance data.
907 */
908static DECLCALLBACK(void) drvvdDestruct(PPDMDRVINS pDrvIns)
909{
910 int rc;
911 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
912 LogFlow(("%s:\n", __FUNCTION__));
913
914 drvvdFreeImages(pThis);
915 if (pThis->pCache)
916 {
917 rc = RTCacheDestroy(pThis->pCache);
918 AssertRC(rc);
919 }
920}
921
922
923/**
924 * When the VM has been suspended we'll change the image mode to read-only
925 * so that main and others can read the VDIs. This is important when
926 * saving state and so forth.
927 *
928 * @param pDrvIns The driver instance data.
929 */
930static DECLCALLBACK(void) drvvdSuspend(PPDMDRVINS pDrvIns)
931{
932 LogFlow(("%s:\n", __FUNCTION__));
933 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
934 if (!VDIsReadOnly(pThis->pDisk))
935 {
936 unsigned uOpenFlags;
937 int rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
938 AssertRC(rc);
939 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
940 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
941 AssertRC(rc);
942 pThis->fTempReadOnly = true;
943 }
944}
945
946/**
947 * Before the VM resumes we'll have to undo the read-only mode change
948 * done in drvvdSuspend.
949 *
950 * @param pDrvIns The driver instance data.
951 */
952static DECLCALLBACK(void) drvvdResume(PPDMDRVINS pDrvIns)
953{
954 LogFlow(("%s:\n", __FUNCTION__));
955 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
956 if (pThis->fTempReadOnly)
957 {
958 unsigned uOpenFlags;
959 int rc = VDGetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, &uOpenFlags);
960 AssertRC(rc);
961 uOpenFlags &= ~VD_OPEN_FLAGS_READONLY;
962 rc = VDSetOpenFlags(pThis->pDisk, VD_LAST_IMAGE, uOpenFlags);
963 AssertRC(rc);
964 pThis->fTempReadOnly = false;
965 }
966}
967
968static DECLCALLBACK(void) drvvdPowerOff(PPDMDRVINS pDrvIns)
969{
970 LogFlow(("%s:\n", __FUNCTION__));
971 PVBOXDISK pThis = PDMINS_2_DATA(pDrvIns, PVBOXDISK);
972
973 /*
974 * We must close the disk here to ensure that
975 * the backend closes all files before the
976 * async transport driver is destructed.
977 */
978 int rc = VDCloseAll(pThis->pDisk);
979 AssertRC(rc);
980}
981
982/**
983 * VBox disk container media driver registration record.
984 */
985const PDMDRVREG g_DrvVD =
986{
987 /* u32Version */
988 PDM_DRVREG_VERSION,
989 /* szDriverName */
990 "VD",
991 /* pszDescription */
992 "Generic VBox disk media driver.",
993 /* fFlags */
994 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
995 /* fClass. */
996 PDM_DRVREG_CLASS_MEDIA,
997 /* cMaxInstances */
998 ~0,
999 /* cbInstance */
1000 sizeof(VBOXDISK),
1001 /* pfnConstruct */
1002 drvvdConstruct,
1003 /* pfnDestruct */
1004 drvvdDestruct,
1005 /* pfnIOCtl */
1006 NULL,
1007 /* pfnPowerOn */
1008 NULL,
1009 /* pfnReset */
1010 NULL,
1011 /* pfnSuspend */
1012 drvvdSuspend,
1013 /* pfnResume */
1014 drvvdResume,
1015 /* pfnDetach */
1016 NULL,
1017 /* pfnPowerOff */
1018 drvvdPowerOff
1019};
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