DrvSCSI.cpp revision f687f34bd232be13744edbc0cc5155fa5d4540ed
cbaf00194b28ee57e4aeee473f66f91f1be4e022vboxsync * VBox storage drivers: Generic SCSI command parser and execution driver
8137be2315957032783c582a2e5c2523ea96f9bcvboxsync * Copyright (C) 2006-2010 Sun Microsystems, Inc.
8137be2315957032783c582a2e5c2523ea96f9bcvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * available from http://www.virtualbox.org. This file is free software;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * you can redistribute it and/or modify it under the terms of the GNU
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * General Public License (GPL) as published by the Free Software
e64031e20c39650a7bc902a3e1aba613b9415deevboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * additional information or have any questions.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/*******************************************************************************
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync* Header Files *
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync*******************************************************************************/
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync//#define DEBUG
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * SCSI driver instance data.
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync * @implements PDMISCSICONNECTOR
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * @implements PDMIBLOCKASYNCPORT
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * @implements PDMIMOUNTNOTIFY
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsynctypedef struct DRVSCSI
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Pointer driver instance. */
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync /** Pointer to the attached driver's base interface. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Pointer to the attached driver's block interface. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Pointer to the attached driver's async block interface. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Pointer to the attached driver's block bios interface. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Pointer to the attached driver's mount interface. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Pointer to the SCSI port interface of the device above. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** pointer to the Led port interface of the dveice above. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** The scsi connector interface .*/
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /** The block port interface. */
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync /** The optional block async port interface. */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync#if 0 /* these interfaces aren't implemented */
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync /** The mount notify interface. */
8137be2315957032783c582a2e5c2523ea96f9bcvboxsync /** Fallback status LED state for this drive.
fe96bc0e43d9c137304462ef8c2d79cbff22446fvboxsync * This is used in case the device doesn't has a LED interface. */
fe96bc0e43d9c137304462ef8c2d79cbff22446fvboxsync /** Pointer to the status LED for this drive. */
fa033b734cf3b131680f290326ccbbd23c42946bvboxsync /** VSCSI device handle. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** VSCSI LUN handle. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** I/O callbacks. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** The dedicated I/O thread for the non async approach. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Queue for passing the requests to the thread. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Request that we've left pending on wakeup or reset. */
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync /** Indicates whether PDMDrvHlpAsyncNotificationCompleted should be called by
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync * any of the dummy functions. */
a39ea3668b7019c23a68936259545f9b71bce1aavboxsync bool volatile fDummySignal;
a39ea3668b7019c23a68936259545f9b71bce1aavboxsync /** Release statistics: number of bytes written. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Release statistics: number of bytes read. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /** Release statistics: Current I/O depth. */
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync/** Converts a pointer to DRVSCSI::ISCSIConnector to a PDRVSCSI. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync#define PDMISCSICONNECTOR_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, ISCSIConnector)) )
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync/** Converts a pointer to DRVSCSI::IPortAsync to a PDRVSCSI. */
f379f813372b948dc6603b556f0ade7f838a5a65vboxsync#define PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IPortAsync)) )
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic int drvscsiProcessRequestOne(PDRVSCSI pThis, VSCSIIOREQ hVScsiIoReq)
aa32d4906f2f685992091893d5abdf27a2352a85vboxsync unsigned cSeg = 0;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg,
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync size_t cbProcess = (cbTransfer < paSeg->cbSeg) ? cbTransfer : paSeg->cbSeg;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync Log(("%s: uOffset=%llu cbProcess=%u\n", __FUNCTION__, uOffset, cbProcess));
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync rc = pThis->pDrvBlock->pfnRead(pThis->pDrvBlock, uOffset,
8137be2315957032783c582a2e5c2523ea96f9bcvboxsync AssertMsgFailed(("%s: Failed to read data %Rrc\n", __FUNCTION__, rc));
8137be2315957032783c582a2e5c2523ea96f9bcvboxsync STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbProcess);
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync rc = pThis->pDrvBlock->pfnWrite(pThis->pDrvBlock, uOffset,
dd97657cc7e8460edff31ebcff4c9d19bf8ad694vboxsync AssertMsgFailed(("%s: Failed to write data %Rrc\n", __FUNCTION__, rc));
dd97657cc7e8460edff31ebcff4c9d19bf8ad694vboxsync STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbProcess);
8137be2315957032783c582a2e5c2523ea96f9bcvboxsync /* Go to the next entry. */
8137be2315957032783c582a2e5c2523ea96f9bcvboxsync AssertMsgFailed(("Invalid transfer direction %d\n", enmTxDir));
8137be2315957032783c582a2e5c2523ea96f9bcvboxsyncstatic int drvscsiGetSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint64_t *pcbSize)
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync *pcbSize = pThis->pDrvBlock->pfnGetSize(pThis->pDrvBlock);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic int drvscsiTransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterface, void *pvUser, int rc)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PDRVSCSI pThis = PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync LogFlowFunc(("Request hVScsiIoReq=%#p completed\n", hVScsiIoReq));
f379f813372b948dc6603b556f0ade7f838a5a65vboxsync AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic int drvscsiReqTransferEnqueue(VSCSILUN hVScsiLun,
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync /* async I/O path. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync LogFlowFunc(("Enqueuing hVScsiIoReq=%#p\n", hVScsiIoReq));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync rc = pThis->pDrvBlockAsync->pfnStartFlush(pThis->pDrvBlockAsync, hVScsiIoReq);
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync AssertMsgFailed(("%s: Failed to flush data %Rrc\n", __FUNCTION__, rc));
b1cc88518a7578ee20491f3d97b9792c24c6428dvboxsync unsigned cSeg = 0;
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
362838d79d234a41380be42aae9118850cc3c929vboxsync pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
362838d79d234a41380be42aae9118850cc3c929vboxsync rc = pThis->pDrvBlockAsync->pfnStartRead(pThis->pDrvBlockAsync, uOffset,
362838d79d234a41380be42aae9118850cc3c929vboxsync if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
bc36547e8dd3d35e5f756643a267bbe01e2c1d4cvboxsync AssertMsgFailed(("%s: Failed to read data %Rrc\n", __FUNCTION__, rc));
bc36547e8dd3d35e5f756643a267bbe01e2c1d4cvboxsync STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbTransfer);
362838d79d234a41380be42aae9118850cc3c929vboxsync pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
362838d79d234a41380be42aae9118850cc3c929vboxsync rc = pThis->pDrvBlockAsync->pfnStartWrite(pThis->pDrvBlockAsync, uOffset,
22bdb1ce26b2d5a41d1b071c16f1078e5348bb0dvboxsync if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
22bdb1ce26b2d5a41d1b071c16f1078e5348bb0dvboxsync AssertMsgFailed(("%s: Failed to write data %Rrc\n", __FUNCTION__, rc));
22bdb1ce26b2d5a41d1b071c16f1078e5348bb0dvboxsync STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbTransfer);
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
dd97657cc7e8460edff31ebcff4c9d19bf8ad694vboxsync AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync AssertMsgFailed(("Invalid return code rc=%Rrc\n", rc));
dd97657cc7e8460edff31ebcff4c9d19bf8ad694vboxsync /* I/O thread. */
dd97657cc7e8460edff31ebcff4c9d19bf8ad694vboxsync rc = RTReqCallEx(pThis->pQueueRequests, NULL, 0, RTREQFLAGS_NO_WAIT,
dd97657cc7e8460edff31ebcff4c9d19bf8ad694vboxsync (PFNRT)drvscsiProcessRequestOne, 2, pThis, hVScsiIoReq);
dd97657cc7e8460edff31ebcff4c9d19bf8ad694vboxsyncstatic void drvscsiVScsiReqCompleted(VSCSIDEVICE hVScsiDevice, void *pVScsiDeviceUser,
dd97657cc7e8460edff31ebcff4c9d19bf8ad694vboxsync pThis->pDevScsiPort->pfnSCSIRequestCompleted(pThis->pDevScsiPort, (PPDMSCSIREQUEST)pVScsiReqUser,
ddab1f70f4034896cebf5e6af5a492ac46218346vboxsync * Dummy request function used by drvscsiReset to wait for all pending requests
ddab1f70f4034896cebf5e6af5a492ac46218346vboxsync * to complete prior to the device reset.
ddab1f70f4034896cebf5e6af5a492ac46218346vboxsync * @param pThis Pointer to the instace data.
ddab1f70f4034896cebf5e6af5a492ac46218346vboxsync * @returns VINF_SUCCESS.
e74eef731a813e4e06680c587a6759b9974b29c9vboxsyncstatic int drvscsiAsyncIOLoopSyncCallback(PDRVSCSI pThis)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
c28fa006ba669ad8f26ae31d00a338379c04ea1bvboxsync * Request function to wakeup the thread.
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync * @param pThis Pointer to the instace data.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * @returns VWRN_STATE_CHANGED.
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic int drvscsiAsyncIOLoopWakeupFunc(PDRVSCSI pThis)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync * The thread function which processes the requests asynchronously.
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync * @returns VBox status code.
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync * @param pDrvIns Pointer to the driver instance data.
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsync * @param pThread Pointer to the thread instance data.
0f1e77149ab5ab40fa2bd74a5330e087416b3c7bvboxsyncstatic int drvscsiAsyncIOLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync while (pThread->enmState == PDMTHREADSTATE_RUNNING)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync rc = RTReqProcess(pThis->pQueueRequests, RT_INDEFINITE_WAIT);
cba6719bd64ec749967bbe931230452664109857vboxsync AssertMsg(rc == VWRN_STATE_CHANGED, ("Left RTReqProcess and error code is not VWRN_STATE_CHANGED rc=%Rrc\n", rc));
ad77e3ec3cde24263bc7537575f5cae442bee3b1vboxsync * Deals with any pending dummy request
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * @returns true if no pending dummy request, false if still pending.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * @param pThis The instance data.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsync * @param cMillies The number of milliseconds to wait for any
dd97657cc7e8460edff31ebcff4c9d19bf8ad694vboxsync * pending request to finish.
801238b286a80a5dd67ba56a1f26c0bc98a2a1eavboxsyncstatic bool drvscsiAsyncIOLoopNoPendingDummy(PDRVSCSI pThis, uint32_t cMillies)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync return true;
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync int rc = RTReqWait(pThis->pPendingDummyReq, cMillies);
914d33aebb63d8c288dfd1b7e74f8e2acf3eaa66vboxsync return false;
b7a5b3f9f9ecce32ddacf8404c625ce0451bbdc1vboxsync return true;
fe96bc0e43d9c137304462ef8c2d79cbff22446fvboxsyncstatic int drvscsiAsyncIOLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync AssertMsgReturn(pThis->pQueueRequests, ("pQueueRequests is NULL\n"), VERR_INVALID_STATE);
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 10000 /* 10 sec */))
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync LogRel(("drvscsiAsyncIOLoopWakeup#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync rc = RTReqCall(pThis->pQueueRequests, &pReq, 10000 /* 10 sec. */, (PFNRT)drvscsiAsyncIOLoopWakeupFunc, 1, pThis);
ad77e3ec3cde24263bc7537575f5cae442bee3b1vboxsync LogRel(("drvscsiAsyncIOLoopWakeup#%u: %Rrc pReq=%p\n", pDrvIns->iInstance, rc, pReq));
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync/* -=-=-=-=- ISCSIConnector -=-=-=-=- */
ad48e47654d22f79b025dc4b21cb162cb123801avboxsync/** @copydoc PDMISCSICONNECTOR::pfnSCSIRequestSend. */
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsyncstatic DECLCALLBACK(int) drvscsiRequestSend(PPDMISCSICONNECTOR pInterface, PPDMSCSIREQUEST pSCSIRequest)
5b1d6bab9f4cf5dacf1883e7c4a40c84349f597fvboxsync PDRVSCSI pThis = PDMISCSICONNECTOR_2_DRVSCSI(pInterface);
ad77e3ec3cde24263bc7537575f5cae442bee3b1vboxsync rc = VSCSIDeviceReqCreate(pThis->hVScsiDevice, &hVScsiReq,
cba6719bd64ec749967bbe931230452664109857vboxsync rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, hVScsiReq);
cba6719bd64ec749967bbe931230452664109857vboxsync/* -=-=-=-=- IBase -=-=-=-=- */
cba6719bd64ec749967bbe931230452664109857vboxsync * @interface_method_impl{PDMIBASE,pfnQueryInterface}
return NULL;
static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
int rc = RTReqCall(pThis->pQueueRequests, &pReq, 0 /*ms*/, (PFNRT)drvscsiAsyncIOLoopSyncCallback, 1, pThis);
return VERR_PDM_MISSING_INTERFACE;
return VERR_PDM_MISSING_INTERFACE;
AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
enmType);
pThis);
PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
return VINF_SUCCESS;
sizeof(DRVSCSI),
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,