DrvVD.cpp revision 3bf9ff5b4c2da22ac8dce07123e55c2cec55d483
/* $Id$ */
/** @file
* DrvVD - Generic VBox disk media driver.
*/
/*
* Copyright (C) 2006-2008 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DRV_VD
#include <VBox/VBoxHDD-new.h>
#include "Builtins.h"
/*******************************************************************************
* Defined types, constants and macros *
*******************************************************************************/
/** Converts a pointer to VDIDISK::IMedia to a PVBOXDISK. */
#define PDMIMEDIA_2_VBOXDISK(pInterface) \
/** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
#define PDMIBASE_2_DRVINS(pInterface) \
/** Converts a pointer to PDMDRVINS::IBase to a PVBOXDISK. */
#define PDMIBASE_2_VBOXDISK(pInterface) \
/** Converts a pointer to VBOXDISK::IMediaAsync to a PVBOXDISK. */
#define PDMIMEDIAASYNC_2_VBOXDISK(pInterface) \
/** Converts a pointer to VBOXDISK::ITransportAsyncPort to a PVBOXDISK. */
#define PDMITRANSPORTASYNCPORT_2_VBOXDISK(pInterface) \
/**
* Structure for an async I/O task.
*/
typedef struct DRVVDASYNCTASK
{
/** Callback which is called on completion. */
/** Opqaue user data which is passed on completion. */
void *pvUser;
/** Opaque user data the caller passed on transfer initiation. */
void *pvUserCaller;
/**
* VBox disk container, image information, private part.
*/
typedef struct VBOXIMAGE
{
/** Pointer to next image. */
/** Pointer to list of VD interfaces. Per-image. */
/** Common structure for the configuration information interface. */
} VBOXIMAGE, *PVBOXIMAGE;
/**
* VBox disk container media main structure, private part.
*/
typedef struct VBOXDISK
{
/** The VBox disk container. */
/** The media interface. */
/** Pointer to the driver instance. */
/** Flag whether suspend has changed image open mode to read only. */
bool fTempReadOnly;
/** Pointer to list of VD interfaces. Per-disk. */
/** Common structure for the supported error interface. */
/** Callback table for error interface. */
/** Common structure for the supported async I/O interface. */
/** Callback table for async I/O interface. */
/** Callback table for the configuration information interface. */
/** Flag whether opened disk suppports async I/O operations. */
bool fAsyncIOSupported;
/** The async media interface. */
/** The async media port interface above. */
/** Pointer to the asynchronous media driver below. */
/** Async transport port interface. */
/** Our cache to reduce allocation overhead. */
/** Pointer to the list of data we need to keep per image. */
/*******************************************************************************
* Error reporting callback *
*******************************************************************************/
{
}
/**
* Internal: allocate new image descriptor and put it in the list
*/
{
if (pImage)
{
}
return pImage;
}
/**
* Internal: free the list of images descriptors.
*/
{
{
RTMemFree(p);
}
}
/*******************************************************************************
* VD Async I/O interface implementation *
*******************************************************************************/
static DECLCALLBACK(int) drvvdAsyncIOOpen(void *pvUser, const char *pszLocation, bool fReadonly, void **ppStorage)
{
return pDrvVD->pDrvTransportAsync->pfnOpen(pDrvVD->pDrvTransportAsync, pszLocation, fReadonly, ppStorage);
}
{
AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
}
{
AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
}
{
AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
}
{
AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
}
static DECLCALLBACK(int) drvvdAsyncIOPrepareRead(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuf, size_t cbRead, void **ppTask)
{
AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
return pDrvVD->pDrvTransportAsync->pfnPrepareRead(pDrvVD->pDrvTransportAsync, pStorage, uOffset, pvBuf, cbRead, ppTask);
}
static DECLCALLBACK(int) drvvdAsyncIOPrepareWrite(void *pvUser, void *pStorage, uint64_t uOffset, void *pvBuf, size_t cbWrite, void **ppTask)
{
AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
return pDrvVD->pDrvTransportAsync->pfnPrepareWrite(pDrvVD->pDrvTransportAsync, pStorage, uOffset, pvBuf, cbWrite, ppTask);
}
static DECLCALLBACK(int) drvvdAsyncIOTasksSubmit(void *pvUser, void *apTasks[], unsigned cTasks, void *pvUser2,
{
int rc;
AssertMsg(pDrvVD->pDrvTransportAsync, ("Asynchronous function called but no async transport interface below\n"));
if (RT_FAILURE(rc))
return rc;
return pDrvVD->pDrvTransportAsync->pfnTasksSubmit(pDrvVD->pDrvTransportAsync, apTasks, cTasks, pDrvVDAsyncTask);
}
/*******************************************************************************
* VD Configuration interface implementation *
*******************************************************************************/
{
}
{
}
{
}
{
}
static int drvvdCfgQueryIntegerDef(PVDCFGNODE pNode, const char *pszName, uint64_t *pu64, uint64_t u64Def)
{
}
static int drvvdCfgQueryString(PVDCFGNODE pNode, const char *pszName, char *pszString, size_t cchString)
{
}
static int drvvdCfgQueryStringDef(PVDCFGNODE pNode, const char *pszName, char *pszString, size_t cchString, const char *pszDef)
{
}
{
}
/*******************************************************************************
* Media interface methods *
*******************************************************************************/
/** @copydoc PDMIMEDIA::pfnRead */
{
if (RT_SUCCESS(rc))
return rc;
}
/** @copydoc PDMIMEDIA::pfnWrite */
{
return rc;
}
/** @copydoc PDMIMEDIA::pfnFlush */
{
return rc;
}
/** @copydoc PDMIMEDIA::pfnGetSize */
{
return cb;
}
/** @copydoc PDMIMEDIA::pfnIsReadOnly */
{
return f;
}
/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
{
if (RT_FAILURE(rc))
{
}
return rc;
}
/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
{
return rc;
}
/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
{
if (RT_FAILURE(rc))
{
}
return rc;
}
/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
{
return rc;
}
/** @copydoc PDMIMEDIA::pfnGetUuid */
{
return rc;
}
/*******************************************************************************
* Async Media interface methods *
*******************************************************************************/
{
return rc;
}
{
return rc;
}
/*******************************************************************************
* Async transport port interface methods *
*******************************************************************************/
{
int rc = VINF_VDI_ASYNC_IO_FINISHED;
/* Having a completion callback for a task is not mandatory. */
if (pDrvVDAsyncTask->pfnCompleted)
/* Check if the request is finished. */
if (rc == VINF_VDI_ASYNC_IO_FINISHED)
{
rc = pThis->pDrvMediaAsyncPort->pfnTransferCompleteNotify(pThis->pDrvMediaAsyncPort, pDrvVDAsyncTask->pvUserCaller);
}
else if (rc == VERR_VDI_ASYNC_IO_IN_PROGRESS)
rc = VINF_SUCCESS;
return rc;
}
/*******************************************************************************
* Base interface methods *
*******************************************************************************/
/** @copydoc PDMIBASE::pfnQueryInterface */
{
switch (enmInterface)
{
case PDMINTERFACE_BASE:
case PDMINTERFACE_MEDIA:
case PDMINTERFACE_MEDIA_ASYNC:
return &pThis->ITransportAsyncPort;
default:
return NULL;
}
}
/*******************************************************************************
* Driver methods *
*******************************************************************************/
/**
* Construct a VBox disk media driver instance.
*
* @returns VBox status.
* @param pDrvIns The driver instance data.
* If the registration structure is needed, pDrvIns->pDrvReg points to it.
* @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
* of the driver instance. It's also found in pDrvIns->pCfgHandle as it's expected
* to be used frequently in this function.
*/
{
int rc = VINF_SUCCESS;
bool fReadOnly; /**< True if the media is readonly. */
bool fHonorZeroWrites; /**< True if zero blocks should be written. */
/*
* Init the static parts.
*/
pThis->fTempReadOnly = false;
pThis->fAsyncIOSupported = false;
/* IMedia */
/* IMediaAsync */
/* ITransportAsyncPort */
/* Initialize supported VD interfaces. */
/* This is just prepared here, the actual interface is per-image, so it's
* added later. No need to have separate callback tables. */
/* List of images is empty now. */
/* Try to attach async media port interface above.*/
pThis->pDrvMediaAsyncPort = (PPDMIMEDIAASYNCPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_MEDIA_ASYNC_PORT);
/*
* Attach the async transport driver below of the device above us implements the
* async interface.
*/
if (pThis->pDrvMediaAsyncPort)
{
/* Try to attach the driver. */
if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
{
/*
* Though the device supports async I/O there is no transport driver
* which processes async requests.
* Revert to non async I/O.
*/
rc = VINF_SUCCESS;
pThis->fAsyncIOSupported = false;
}
else if (RT_FAILURE(rc))
{
}
else
{
/*
* The device supports async I/O and we successfully attached the transport driver.
* Indicate that async I/O is supported for now as we check if the image backend supports
* it later.
*/
pThis->fAsyncIOSupported = true;
/* Success query the async transport interface. */
pThis->pDrvTransportAsync = (PPDMITRANSPORTASYNC)pBase->pfnQueryInterface(pBase, PDMINTERFACE_TRANSPORT_ASYNC);
if (!pThis->pDrvTransportAsync)
{
/* An attached driver without an async transport interface - impossible. */
AssertMsgFailed(("Configuration error: No async transport interface below!\n"));
return VERR_PDM_MISSING_INTERFACE_ABOVE;
}
}
}
/*
* Validate configuration and find all parent images.
* It's sort of up side down from the image dependency tree.
*/
unsigned iLevel = 0;
for (;;)
{
bool fValid;
if (pCurNode == pCfgHandle)
{
/* Toplevel configuration additionally contains the global image
* open flags. Some might be converted to per-image flags later. */
"Format\0Path\0"
"ReadOnly\0HonorZeroWrites\0");
}
else
{
/* All other image configurations only contain image name and
* the format information. */
}
if (!fValid)
{
break;
}
if (!pParent)
break;
iLevel++;
}
/*
* Open the images.
*/
if (RT_SUCCESS(rc))
{
/* Error message is already set correctly. */
}
{
/* Allocate per-image data. */
if (!pImage)
{
rc = VERR_NO_MEMORY;
break;
}
/*
* Read the image configuration.
*/
if (RT_FAILURE(rc))
{
N_("DrvVD: Configuration error: Querying \"Path\" as string failed"));
break;
}
if (RT_FAILURE(rc))
{
N_("DrvVD: Configuration error: Querying \"Format\" as string failed"));
break;
}
if (iLevel == 0)
{
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
fReadOnly = false;
else if (RT_FAILURE(rc))
{
N_("DrvVD: Configuration error: Querying \"ReadOnly\" as boolean failed"));
break;
}
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
fHonorZeroWrites = false;
else if (RT_FAILURE(rc))
{
N_("DrvVD: Configuration error: Querying \"HonorZeroWrites\" as boolean failed"));
break;
}
}
else
{
fReadOnly = true;
fHonorZeroWrites = false;
}
/*
* Open the image.
*/
unsigned uOpenFlags;
if (fReadOnly)
else
if (fHonorZeroWrites)
if (pThis->pDrvMediaAsyncPort)
/** Try to open backend in asyc I/O mode first. */
if (rc == VERR_NOT_SUPPORTED)
{
/* Seems async I/O is not supported by the backend, open in normal mode. */
}
if (RT_SUCCESS(rc))
else
{
break;
}
/* next */
iLevel--;
}
if (RT_FAILURE(rc))
{
{
}
return rc;
}
else
{
/*
* Check if every opened image supports async I/O.
* If not we revert to non async I/O.
*/
if (pThis->fAsyncIOSupported)
{
{
{
/*
* Backend indicates support for at least some files.
* Check if current file is supported with async I/O)
*/
/*
* Check if current image is supported.
* If not we can stop checking because
* at least one does not support it.
*/
if (!pThis->fAsyncIOSupported)
break;
}
else
{
pThis->fAsyncIOSupported = false;
break;
}
}
}
/*
* We know definitly if async I/O is supported now.
* Create cache if it is supported.
*/
if (pThis->fAsyncIOSupported)
{
}
}
return rc;
}
/**
* Destruct a driver instance.
*
* Most VM resources are freed by the VM. This callback is provided so that any non-VM
* resources can be freed correctly.
*
* @param pDrvIns The driver instance data.
*/
{
int rc;
{
}
}
/**
* When the VM has been suspended we'll change the image mode to read-only
* so that main and others can read the VDIs. This is important when
* saving state and so forth.
*
* @param pDrvIns The driver instance data.
*/
{
{
unsigned uOpenFlags;
pThis->fTempReadOnly = true;
}
}
/**
* Before the VM resumes we'll have to undo the read-only mode change
* done in drvvdSuspend.
*
* @param pDrvIns The driver instance data.
*/
{
if (pThis->fTempReadOnly)
{
unsigned uOpenFlags;
pThis->fTempReadOnly = false;
}
}
{
/*
* We must close the disk here to ensure that
* the backend closes all files before the
* async transport driver is destructed.
*/
}
/**
* VBox disk container media driver registration record.
*/
{
/* u32Version */
/* szDriverName */
"VD",
/* pszDescription */
"Generic VBox disk media driver.",
/* fFlags */
/* fClass. */
/* cMaxInstances */
~0,
/* cbInstance */
sizeof(VBOXDISK),
/* pfnConstruct */
/* pfnDestruct */
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
/* pfnResume */
/* pfnDetach */
NULL,
/* pfnPowerOff */
};