DrvSCSI.cpp revision d5ea45cc92d7f1d3ade8189944531f665bfe8ed5
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync/* $Id$ */
ee29f83d0638dba8e0380d7f594fe2d726d9403cvboxsync/** @file
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync * VBox storage drivers: Generic SCSI command parser and execution driver
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync */
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync/*
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync * Copyright (C) 2006-2010 Sun Microsystems, Inc.
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync *
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync * available from http://www.virtualbox.org. This file is free software;
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync * you can redistribute it and/or modify it under the terms of the GNU
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync * General Public License (GPL) as published by the Free Software
9496b6f77d66eb89f088668752b8838d578d6e10vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync *
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync * additional information or have any questions.
613c0d015cbaef93be47fc03f0708744c5c24f79vboxsync */
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync/*******************************************************************************
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync* Header Files *
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync*******************************************************************************/
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync//#define DEBUG
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync#define LOG_GROUP LOG_GROUP_DRV_SCSI
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync#include <VBox/pdmdrv.h>
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync#include <VBox/pdmifs.h>
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync#include <VBox/pdmthread.h>
c11bd53b8a7019cd1079a8f82b27e3d4defb91cevboxsync#include <VBox/vscsi.h>
c11bd53b8a7019cd1079a8f82b27e3d4defb91cevboxsync#include <iprt/assert.h>
c11bd53b8a7019cd1079a8f82b27e3d4defb91cevboxsync#include <iprt/mem.h>
c11bd53b8a7019cd1079a8f82b27e3d4defb91cevboxsync#include <iprt/req.h>
c11bd53b8a7019cd1079a8f82b27e3d4defb91cevboxsync#include <iprt/semaphore.h>
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync#include <iprt/string.h>
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync#include <iprt/uuid.h>
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync
613c0d015cbaef93be47fc03f0708744c5c24f79vboxsync#include "Builtins.h"
9a1578b66f9e563cf99c75ffa881db476f477e3avboxsync
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync/**
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync * SCSI driver instance data.
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync *
18673eee0624b4581d3d56ab9ad9d10ebc7f5bddvboxsync * @implements PDMISCSICONNECTOR
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync * @implements PDMIBLOCKASYNCPORT
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync * @implements PDMIMOUNTNOTIFY
59816992ec770eb9c03fbc1233ae62114a026badvboxsync */
7bff28e0cedd8656acd24b420759649184d8cf00vboxsynctypedef struct DRVSCSI
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync{
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync /** Pointer driver instance. */
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync PPDMDRVINS pDrvIns;
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync /** Pointer to the attached driver's base interface. */
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync PPDMIBASE pDrvBase;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync /** Pointer to the attached driver's block interface. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync PPDMIBLOCK pDrvBlock;
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync /** Pointer to the attached driver's async block interface. */
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync PPDMIBLOCKASYNC pDrvBlockAsync;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync /** Pointer to the attached driver's block bios interface. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync PPDMIBLOCKBIOS pDrvBlockBios;
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync /** Pointer to the attached driver's mount interface. */
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync PPDMIMOUNT pDrvMount;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync /** Pointer to the SCSI port interface of the device above. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync PPDMISCSIPORT pDevScsiPort;
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync /** pointer to the Led port interface of the dveice above. */
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync PPDMILEDPORTS pLedPort;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync /** The scsi connector interface .*/
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync PDMISCSICONNECTOR ISCSIConnector;
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync /** The block port interface. */
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync PDMIBLOCKPORT IPort;
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync /** The optional block async port interface. */
97674677e4f2aeae576c39f966568dd664ba7979vboxsync PDMIBLOCKASYNCPORT IPortAsync;
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync#if 0 /* these interfaces aren't implemented */
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync /** The mount notify interface. */
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync PDMIMOUNTNOTIFY IMountNotify;
c4c106ad74e0ad745ac49a2c4182c4f42ced7248vboxsync#endif
97674677e4f2aeae576c39f966568dd664ba7979vboxsync /** Fallback status LED state for this drive.
97674677e4f2aeae576c39f966568dd664ba7979vboxsync * This is used in case the device doesn't has a LED interface. */
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync PDMLED Led;
eecfcc5748685e35d0d4d835fb30d5494ad62734vboxsync /** Pointer to the status LED for this drive. */
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync PPDMLED pLed;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync /** VSCSI device handle. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync VSCSIDEVICE hVScsiDevice;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync /** VSCSI LUN handle. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync VSCSILUN hVScsiLun;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync /** I/O callbacks. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync VSCSILUNIOCALLBACKS VScsiIoCallbacks;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync /** The dedicated I/O thread for the non async approach. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync PPDMTHREAD pAsyncIOThread;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync /** Queue for passing the requests to the thread. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync PRTREQQUEUE pQueueRequests;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync /** Request that we've left pending on wakeup or reset. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync PRTREQ pPendingDummyReq;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync /** Indicates whether PDMDrvHlpAsyncNotificationCompleted should be called by
907ba2c9b3d1821f95be17115ecad9fe8a2cae02vboxsync * any of the dummy functions. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync bool volatile fDummySignal;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync /** Release statistics: number of bytes written. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync STAMCOUNTER StatBytesWritten;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync /** Release statistics: number of bytes read. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync STAMCOUNTER StatBytesRead;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync /** Release statistics: Current I/O depth. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync volatile uint32_t StatIoDepth;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync} DRVSCSI, *PDRVSCSI;
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync/** Converts a pointer to DRVSCSI::ISCSIConnector to a PDRVSCSI. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync#define PDMISCSICONNECTOR_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, ISCSIConnector)) )
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync/** Converts a pointer to DRVSCSI::IPortAsync to a PDRVSCSI. */
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync#define PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IPortAsync)) )
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync
771761cda2c81e899526a0dce22c8cd2510fff82vboxsyncstatic int drvscsiProcessRequestOne(PDRVSCSI pThis, VSCSIIOREQ hVScsiIoReq)
f6cc81e94c29cc9b39b896cf32ecfe0501b4a1e5vboxsync{
97674677e4f2aeae576c39f966568dd664ba7979vboxsync int rc = VINF_SUCCESS;
84bebd094718692856715fb7ed9e592c9421e039vboxsync VSCSIIOREQTXDIR enmTxDir;
97674677e4f2aeae576c39f966568dd664ba7979vboxsync
97674677e4f2aeae576c39f966568dd664ba7979vboxsync enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
97674677e4f2aeae576c39f966568dd664ba7979vboxsync
97674677e4f2aeae576c39f966568dd664ba7979vboxsync switch (enmTxDir)
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync {
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync case VSCSIIOREQTXDIR_FLUSH:
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync {
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync rc = pThis->pDrvBlock->pfnFlush(pThis->pDrvBlock);
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync break;
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync }
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync case VSCSIIOREQTXDIR_READ:
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync case VSCSIIOREQTXDIR_WRITE:
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync {
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync uint64_t uOffset = 0;
6dc98f1ea961efeeaf2624e4022634fa5a650cb6vboxsync size_t cbTransfer = 0;
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync size_t cbSeg = 0;
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync PCPDMDATASEG paSeg = NULL;
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync unsigned cSeg = 0;
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg,
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync &paSeg);
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync AssertRC(rc);
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync while (cbTransfer && cSeg)
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync {
f6cc81e94c29cc9b39b896cf32ecfe0501b4a1e5vboxsync size_t cbProcess = (cbTransfer < paSeg->cbSeg) ? cbTransfer : paSeg->cbSeg;
9a1578b66f9e563cf99c75ffa881db476f477e3avboxsync
9a1578b66f9e563cf99c75ffa881db476f477e3avboxsync Log(("%s: uOffset=%llu cbProcess=%u\n", __FUNCTION__, uOffset, cbProcess));
9a1578b66f9e563cf99c75ffa881db476f477e3avboxsync
f6cc81e94c29cc9b39b896cf32ecfe0501b4a1e5vboxsync if (enmTxDir == VSCSIIOREQTXDIR_READ)
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync {
771761cda2c81e899526a0dce22c8cd2510fff82vboxsync pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync rc = pThis->pDrvBlock->pfnRead(pThis->pDrvBlock, uOffset,
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync paSeg->pvSeg, cbProcess);
9ced981a0263f6280ccbf5dc64c0e81fbe4a2fdavboxsync pThis->pLed->Actual.s.fReading = 0;
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync if (RT_FAILURE(rc))
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync AssertMsgFailed(("%s: Failed to read data %Rrc\n", __FUNCTION__, rc));
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbProcess);
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync }
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync else
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync {
59816992ec770eb9c03fbc1233ae62114a026badvboxsync pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
9a1578b66f9e563cf99c75ffa881db476f477e3avboxsync rc = pThis->pDrvBlock->pfnWrite(pThis->pDrvBlock, uOffset,
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync paSeg->pvSeg, cbProcess);
97674677e4f2aeae576c39f966568dd664ba7979vboxsync pThis->pLed->Actual.s.fWriting = 0;
02487e3b759646add535e475e35311265235a629vboxsync if (RT_FAILURE(rc))
72f66530e1bf87aa6894a5f55f1b4d36caa5761fvboxsync AssertMsgFailed(("%s: Failed to write data %Rrc\n", __FUNCTION__, rc));
97674677e4f2aeae576c39f966568dd664ba7979vboxsync STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbProcess);
97674677e4f2aeae576c39f966568dd664ba7979vboxsync }
7bff28e0cedd8656acd24b420759649184d8cf00vboxsync
/* Go to the next entry. */
uOffset += cbProcess;
cbTransfer -= cbProcess;
paSeg++;
cSeg--;
}
break;
}
default:
AssertMsgFailed(("Invalid transfer direction %d\n", enmTxDir));
}
ASMAtomicDecU32(&pThis->StatIoDepth);
VSCSIIoReqCompleted(hVScsiIoReq, rc);
return VINF_SUCCESS;
}
static int drvscsiGetSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint64_t *pcbSize)
{
PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
*pcbSize = pThis->pDrvBlock->pfnGetSize(pThis->pDrvBlock);
return VINF_SUCCESS;
}
static int drvscsiTransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterface, void *pvUser)
{
PDRVSCSI pThis = PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface);
VSCSIIOREQ hVScsiIoReq = (VSCSIIOREQ)pvUser;
VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
LogFlowFunc(("Request hVScsiIoReq=%#p completed\n", hVScsiIoReq));
if (enmTxDir == VSCSIIOREQTXDIR_READ)
pThis->pLed->Actual.s.fReading = 0;
else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
pThis->pLed->Actual.s.fWriting = 0;
else
AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
ASMAtomicDecU32(&pThis->StatIoDepth);
VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS);
return VINF_SUCCESS;
}
static int drvscsiReqTransferEnqueue(VSCSILUN hVScsiLun,
void *pvScsiLunUser,
VSCSIIOREQ hVScsiIoReq)
{
int rc = VINF_SUCCESS;
PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
ASMAtomicIncU32(&pThis->StatIoDepth);
if (pThis->pDrvBlockAsync)
{
/* asnyc I/O path. */
VSCSIIOREQTXDIR enmTxDir;
LogFlowFunc(("Enqueuing hVScsiIoReq=%#p\n", hVScsiIoReq));
enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
switch (enmTxDir)
{
case VSCSIIOREQTXDIR_FLUSH:
{
/** @todo Flush callback for the async I/O interface. */
ASMAtomicDecU32(&pThis->StatIoDepth);
VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS);
break;
}
case VSCSIIOREQTXDIR_READ:
case VSCSIIOREQTXDIR_WRITE:
{
uint64_t uOffset = 0;
size_t cbTransfer = 0;
size_t cbSeg = 0;
PCPDMDATASEG paSeg = NULL;
unsigned cSeg = 0;
rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
&cSeg, &cbSeg, &paSeg);
AssertRC(rc);
if (enmTxDir == VSCSIIOREQTXDIR_READ)
{
pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
rc = pThis->pDrvBlockAsync->pfnStartRead(pThis->pDrvBlockAsync, uOffset,
(PPDMDATASEG)paSeg, cSeg, cbTransfer,
hVScsiIoReq);
if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
AssertMsgFailed(("%s: Failed to read data %Rrc\n", __FUNCTION__, rc));
STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbTransfer);
}
else
{
pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
rc = pThis->pDrvBlockAsync->pfnStartWrite(pThis->pDrvBlockAsync, uOffset,
(PPDMDATASEG)paSeg, cSeg, cbTransfer,
hVScsiIoReq);
if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
AssertMsgFailed(("%s: Failed to write data %Rrc\n", __FUNCTION__, rc));
STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbTransfer);
}
if (rc == VINF_VD_ASYNC_IO_FINISHED)
{
if (enmTxDir == VSCSIIOREQTXDIR_READ)
pThis->pLed->Actual.s.fReading = 0;
else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
pThis->pLed->Actual.s.fWriting = 0;
else
AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
ASMAtomicDecU32(&pThis->StatIoDepth);
VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS);
}
else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
rc = VINF_SUCCESS;
else if (RT_FAILURE(rc))
{
if (enmTxDir == VSCSIIOREQTXDIR_READ)
pThis->pLed->Actual.s.fReading = 0;
else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
pThis->pLed->Actual.s.fWriting = 0;
else
AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
ASMAtomicDecU32(&pThis->StatIoDepth);
VSCSIIoReqCompleted(hVScsiIoReq, rc);
}
else
AssertMsgFailed(("Invalid return coe rc=%Rrc\n", rc));
break;
}
default:
AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
}
}
else
{
/* I/O thread. */
rc = RTReqCallEx(pThis->pQueueRequests, NULL, 0, RTREQFLAGS_NO_WAIT,
(PFNRT)drvscsiProcessRequestOne, 2, pThis, hVScsiIoReq);
}
return rc;
}
static void drvscsiVScsiReqCompleted(VSCSIDEVICE hVScsiDevice, void *pVScsiDeviceUser,
void *pVScsiReqUser, int rcReq)
{
PDRVSCSI pThis = (PDRVSCSI)pVScsiDeviceUser;
pThis->pDevScsiPort->pfnSCSIRequestCompleted(pThis->pDevScsiPort, (PPDMSCSIREQUEST)pVScsiReqUser,
rcReq);
}
/**
* Dummy request function used by drvscsiReset to wait for all pending requests
* to complete prior to the device reset.
*
* @param pThis Pointer to the instace data.
* @returns VINF_SUCCESS.
*/
static int drvscsiAsyncIOLoopSyncCallback(PDRVSCSI pThis)
{
if (pThis->fDummySignal)
PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
return VINF_SUCCESS;
}
/**
* Request function to wakeup the thread.
*
* @param pThis Pointer to the instace data.
* @returns VWRN_STATE_CHANGED.
*/
static int drvscsiAsyncIOLoopWakeupFunc(PDRVSCSI pThis)
{
if (pThis->fDummySignal)
PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
return VWRN_STATE_CHANGED;
}
/**
* The thread function which processes the requests asynchronously.
*
* @returns VBox status code.
* @param pDrvIns Pointer to the driver instance data.
* @param pThread Pointer to the thread instance data.
*/
static int drvscsiAsyncIOLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
{
int rc = VINF_SUCCESS;
PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
LogFlowFunc(("Entering async IO loop.\n"));
if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
return VINF_SUCCESS;
while (pThread->enmState == PDMTHREADSTATE_RUNNING)
{
rc = RTReqProcess(pThis->pQueueRequests, RT_INDEFINITE_WAIT);
AssertMsg(rc == VWRN_STATE_CHANGED, ("Left RTReqProcess and error code is not VWRN_STATE_CHANGED rc=%Rrc\n", rc));
}
return VINF_SUCCESS;
}
/**
* Deals with any pending dummy request
*
* @returns true if no pending dummy request, false if still pending.
* @param pThis The instance data.
* @param cMillies The number of milliseconds to wait for any
* pending request to finish.
*/
static bool drvscsiAsyncIOLoopNoPendingDummy(PDRVSCSI pThis, uint32_t cMillies)
{
if (!pThis->pPendingDummyReq)
return true;
int rc = RTReqWait(pThis->pPendingDummyReq, cMillies);
if (RT_FAILURE(rc))
return false;
RTReqFree(pThis->pPendingDummyReq);
pThis->pPendingDummyReq = NULL;
return true;
}
static int drvscsiAsyncIOLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
{
PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
PRTREQ pReq;
int rc;
AssertMsgReturn(pThis->pQueueRequests, ("pQueueRequests is NULL\n"), VERR_INVALID_STATE);
if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 10000 /* 10 sec */))
{
LogRel(("drvscsiAsyncIOLoopWakeup#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
return VERR_TIMEOUT;
}
rc = RTReqCall(pThis->pQueueRequests, &pReq, 10000 /* 10 sec. */, (PFNRT)drvscsiAsyncIOLoopWakeupFunc, 1, pThis);
if (RT_SUCCESS(rc))
RTReqFree(pReq);
else
{
pThis->pPendingDummyReq = pReq;
LogRel(("drvscsiAsyncIOLoopWakeup#%u: %Rrc pReq=%p\n", pDrvIns->iInstance, rc, pReq));
}
return rc;
}
/* -=-=-=-=- ISCSIConnector -=-=-=-=- */
/** @copydoc PDMISCSICONNECTOR::pfnSCSIRequestSend. */
static DECLCALLBACK(int) drvscsiRequestSend(PPDMISCSICONNECTOR pInterface, PPDMSCSIREQUEST pSCSIRequest)
{
int rc;
PDRVSCSI pThis = PDMISCSICONNECTOR_2_DRVSCSI(pInterface);
VSCSIREQ hVScsiReq;
rc = VSCSIDeviceReqCreate(pThis->hVScsiDevice, &hVScsiReq,
pSCSIRequest->uLogicalUnit,
pSCSIRequest->pbCDB,
pSCSIRequest->cbCDB,
pSCSIRequest->cbScatterGather,
pSCSIRequest->cScatterGatherEntries,
pSCSIRequest->paScatterGatherHead,
pSCSIRequest->pbSenseBuffer,
pSCSIRequest->cbSenseBuffer,
pSCSIRequest);
if (RT_FAILURE(rc))
return rc;
rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, hVScsiReq);
return rc;
}
/* -=-=-=-=- IBase -=-=-=-=- */
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
{
PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pThis->IPort);
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKASYNCPORT, &pThis->IPortAsync);
return NULL;
}
/**
* Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
*
* @param pDrvIns The driver instance.
* @param pfnAsyncNotify The async callback.
*/
static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
{
PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
if (!pThis->pQueueRequests)
return;
ASMAtomicWriteBool(&pThis->fDummySignal, true);
if (drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
{
if (!RTReqIsBusy(pThis->pQueueRequests))
{
ASMAtomicWriteBool(&pThis->fDummySignal, false);
return;
}
PRTREQ pReq;
int rc = RTReqCall(pThis->pQueueRequests, &pReq, 0 /*ms*/, (PFNRT)drvscsiAsyncIOLoopSyncCallback, 1, pThis);
if (RT_SUCCESS(rc))
{
ASMAtomicWriteBool(&pThis->fDummySignal, false);
RTReqFree(pReq);
return;
}
pThis->pPendingDummyReq = pReq;
}
PDMDrvHlpSetAsyncNotification(pDrvIns, pfnAsyncNotify);
}
/**
* Callback employed by drvscsiSuspend and drvscsiPowerOff.
*
* @returns true if we've quiesced, false if we're still working.
* @param pDrvIns The driver instance.
*/
static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
{
PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
return false;
ASMAtomicWriteBool(&pThis->fDummySignal, false);
PDMR3ThreadSuspend(pThis->pAsyncIOThread);
return true;
}
/**
* @copydoc FNPDMDRVPOWEROFF
*/
static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
{
drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
}
/**
* @copydoc FNPDMDRVSUSPEND
*/
static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
{
drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
}
/**
* Callback employed by drvscsiReset.
*
* @returns true if we've quiesced, false if we're still working.
* @param pDrvIns The driver instance.
*/
static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
{
PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
return false;
ASMAtomicWriteBool(&pThis->fDummySignal, false);
return true;
}
/**
* @copydoc FNPDMDRVRESET
*/
static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
{
drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
}
/**
* 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.
*/
static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
{
PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
if (pThis->pQueueRequests)
{
if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 100 /*ms*/))
LogRel(("drvscsiDestruct#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
int rc = RTReqDestroyQueue(pThis->pQueueRequests);
AssertMsgRC(rc, ("Failed to destroy queue rc=%Rrc\n", rc));
}
/* Free the VSCSI device and LUN handle. */
VSCSILUN hVScsiLun;
int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
AssertRC(rc);
Assert(hVScsiLun == pThis->hVScsiLun);
rc = VSCSILunDestroy(hVScsiLun);
AssertRC(rc);
rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
AssertRC(rc);
}
/**
* Construct a block driver instance.
*
* @copydoc FNPDMDRVCONSTRUCT
*/
static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
{
PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
/*
* Initialize the instance data.
*/
pThis->pDrvIns = pDrvIns;
pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsiRequestSend;
pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
pThis->IPortAsync.pfnTransferCompleteNotify = drvscsiTransferCompleteNotify;
/*
* Try attach driver below and query it's block interface.
*/
int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
/*
* Query the block and blockbios interfaces.
*/
pThis->pDrvBlock = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCK);
if (!pThis->pDrvBlock)
{
AssertMsgFailed(("Configuration error: No block interface!\n"));
return VERR_PDM_MISSING_INTERFACE;
}
pThis->pDrvBlockBios = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKBIOS);
if (!pThis->pDrvBlockBios)
{
AssertMsgFailed(("Configuration error: No block BIOS interface!\n"));
return VERR_PDM_MISSING_INTERFACE;
}
/* Query the SCSI port interface above. */
pThis->pDevScsiPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISCSIPORT);
AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
/* Query the optional LED interface above. */
pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
if (pThis->pLedPort != NULL)
{
/* Get The Led. */
rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
if (RT_FAILURE(rc))
pThis->pLed = &pThis->Led;
}
else
pThis->pLed = &pThis->Led;
/* Try to get the optional async block interface. */
pThis->pDrvBlockAsync = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKASYNC);
PDMBLOCKTYPE enmType = pThis->pDrvBlock->pfnGetType(pThis->pDrvBlock);
if (enmType != PDMBLOCKTYPE_HARD_DISK)
return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
N_("Only hard disks are currently supported as SCSI devices (enmType=%d)"),
enmType);
/* Create VSCSI device and LUN. */
pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis);
AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n"), rc);
rc = VSCSILunCreate(&pThis->hVScsiLun, VSCSILUNTYPE_SBC, &pThis->VScsiIoCallbacks,
pThis);
AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n"), rc);
rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
/* Create request queue. */
rc = RTReqCreateQueue(&pThis->pQueueRequests);
AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create request queue rc=%Rrc\n"), rc);
/* Register statistics counter. */
/** @todo aeichner: Find a way to put the instance number of the attached
* controller device when we support more than one controller of the same type.
* At the moment we have the 0 hardcoded. */
PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
"Amount of data read.", "/Devices/SCSI0/%d/ReadBytes", pDrvIns->iInstance);
PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
"Amount of data written.", "/Devices/SCSI0/%d/WrittenBytes", pDrvIns->iInstance);
pThis->StatIoDepth = 0;
PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
"Number of active tasks.", "/Devices/SCSI0/%d/IoDepth", pDrvIns->iInstance);
/* Create I/O thread. */
rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pAsyncIOThread, pThis, drvscsiAsyncIOLoop,
drvscsiAsyncIOLoopWakeup, 0, RTTHREADTYPE_IO, "SCSI async IO");
AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create async I/O thread rc=%Rrc\n"), rc);
return VINF_SUCCESS;
}
/**
* SCSI driver registration record.
*/
const PDMDRVREG g_DrvSCSI =
{
/* u32Version */
PDM_DRVREG_VERSION,
/* szName */
"SCSI",
/* szRCMod */
"",
/* szR0Mod */
"",
/* pszDescription */
"Generic SCSI driver.",
/* fFlags */
PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
/* fClass. */
PDM_DRVREG_CLASS_SCSI,
/* cMaxInstances */
~0,
/* cbInstance */
sizeof(DRVSCSI),
/* pfnConstruct */
drvscsiConstruct,
/* pfnDestruct */
drvscsiDestruct,
/* pfnRelocate */
NULL,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
drvscsiReset,
/* pfnSuspend */
drvscsiSuspend,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnPowerOff */
drvscsiPowerOff,
/* pfnSoftReset */
NULL,
/* u32EndVersion */
PDM_DRVREG_VERSION
};