VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvSCSI.cpp@ 27665

最後變更 在這個檔案從27665是 27665,由 vboxsync 提交於 15 年 前

SCSI: Cleanup during destruction

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 24.6 KB
 
1/* $Id: DrvSCSI.cpp 27665 2010-03-24 12:33:33Z vboxsync $ */
2/** @file
3 * VBox storage drivers: Generic SCSI command parser and execution driver
4 */
5
6/*
7 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25//#define DEBUG
26#define LOG_GROUP LOG_GROUP_DRV_SCSI
27#include <VBox/pdmdrv.h>
28#include <VBox/pdmifs.h>
29#include <VBox/pdmthread.h>
30#include <VBox/vscsi.h>
31#include <iprt/assert.h>
32#include <iprt/mem.h>
33#include <iprt/req.h>
34#include <iprt/semaphore.h>
35#include <iprt/string.h>
36#include <iprt/uuid.h>
37
38#include "Builtins.h"
39
40/**
41 * SCSI driver instance data.
42 *
43 * @implements PDMISCSICONNECTOR
44 * @implements PDMIBLOCKASYNCPORT
45 * @implements PDMIMOUNTNOTIFY
46 */
47typedef struct DRVSCSI
48{
49 /** Pointer driver instance. */
50 PPDMDRVINS pDrvIns;
51
52 /** Pointer to the attached driver's base interface. */
53 PPDMIBASE pDrvBase;
54 /** Pointer to the attached driver's block interface. */
55 PPDMIBLOCK pDrvBlock;
56 /** Pointer to the attached driver's async block interface. */
57 PPDMIBLOCKASYNC pDrvBlockAsync;
58 /** Pointer to the attached driver's block bios interface. */
59 PPDMIBLOCKBIOS pDrvBlockBios;
60 /** Pointer to the attached driver's mount interface. */
61 PPDMIMOUNT pDrvMount;
62 /** Pointer to the SCSI port interface of the device above. */
63 PPDMISCSIPORT pDevScsiPort;
64 /** pointer to the Led port interface of the dveice above. */
65 PPDMILEDPORTS pLedPort;
66 /** The scsi connector interface .*/
67 PDMISCSICONNECTOR ISCSIConnector;
68 /** The block port interface. */
69 PDMIBLOCKPORT IPort;
70 /** The optional block async port interface. */
71 PDMIBLOCKASYNCPORT IPortAsync;
72#if 0 /* these interfaces aren't implemented */
73 /** The mount notify interface. */
74 PDMIMOUNTNOTIFY IMountNotify;
75#endif
76 /** Fallback status LED state for this drive.
77 * This is used in case the device doesn't has a LED interface. */
78 PDMLED Led;
79 /** Pointer to the status LED for this drive. */
80 PPDMLED pLed;
81
82 /** VSCSI device handle. */
83 VSCSIDEVICE hVScsiDevice;
84 /** VSCSI LUN handle. */
85 VSCSILUN hVScsiLun;
86 /** I/O callbacks. */
87 VSCSILUNIOCALLBACKS VScsiIoCallbacks;
88
89 /** The dedicated I/O thread for the non async approach. */
90 PPDMTHREAD pAsyncIOThread;
91 /** Queue for passing the requests to the thread. */
92 PRTREQQUEUE pQueueRequests;
93 /** Request that we've left pending on wakeup or reset. */
94 PRTREQ pPendingDummyReq;
95 /** Indicates whether PDMDrvHlpAsyncNotificationCompleted should be called by
96 * any of the dummy functions. */
97 bool volatile fDummySignal;
98 /** Release statistics: number of bytes written. */
99 STAMCOUNTER StatBytesWritten;
100 /** Release statistics: number of bytes read. */
101 STAMCOUNTER StatBytesRead;
102 /** Release statistics: Current I/O depth. */
103 volatile uint32_t StatIoDepth;
104} DRVSCSI, *PDRVSCSI;
105
106/** Converts a pointer to DRVSCSI::ISCSIConnector to a PDRVSCSI. */
107#define PDMISCSICONNECTOR_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, ISCSIConnector)) )
108/** Converts a pointer to DRVSCSI::IPortAsync to a PDRVSCSI. */
109#define PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IPortAsync)) )
110
111static int drvscsiProcessRequestOne(PDRVSCSI pThis, VSCSIIOREQ hVScsiIoReq)
112{
113 int rc = VINF_SUCCESS;
114 VSCSIIOREQTXDIR enmTxDir;
115
116 enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
117
118 switch (enmTxDir)
119 {
120 case VSCSIIOREQTXDIR_FLUSH:
121 {
122 rc = pThis->pDrvBlock->pfnFlush(pThis->pDrvBlock);
123 break;
124 }
125 case VSCSIIOREQTXDIR_READ:
126 case VSCSIIOREQTXDIR_WRITE:
127 {
128 uint64_t uOffset = 0;
129 size_t cbTransfer = 0;
130 size_t cbSeg = 0;
131 PCPDMDATASEG paSeg = NULL;
132 unsigned cSeg = 0;
133
134 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg,
135 &paSeg);
136 AssertRC(rc);
137
138 while (cbTransfer && cSeg)
139 {
140 size_t cbProcess = (cbTransfer < paSeg->cbSeg) ? cbTransfer : paSeg->cbSeg;
141
142 Log(("%s: uOffset=%llu cbProcess=%u\n", __FUNCTION__, uOffset, cbProcess));
143
144 if (enmTxDir == VSCSIIOREQTXDIR_READ)
145 {
146 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
147 rc = pThis->pDrvBlock->pfnRead(pThis->pDrvBlock, uOffset,
148 paSeg->pvSeg, cbProcess);
149 pThis->pLed->Actual.s.fReading = 0;
150 if (RT_FAILURE(rc))
151 AssertMsgFailed(("%s: Failed to read data %Rrc\n", __FUNCTION__, rc));
152 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbProcess);
153 }
154 else
155 {
156 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
157 rc = pThis->pDrvBlock->pfnWrite(pThis->pDrvBlock, uOffset,
158 paSeg->pvSeg, cbProcess);
159 pThis->pLed->Actual.s.fWriting = 0;
160 if (RT_FAILURE(rc))
161 AssertMsgFailed(("%s: Failed to write data %Rrc\n", __FUNCTION__, rc));
162 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbProcess);
163 }
164
165 /* Go to the next entry. */
166 uOffset += cbProcess;
167 cbTransfer -= cbProcess;
168 paSeg++;
169 cSeg--;
170 }
171
172 break;
173 }
174 default:
175 AssertMsgFailed(("Invalid transfer direction %d\n", enmTxDir));
176 }
177
178 ASMAtomicDecU32(&pThis->StatIoDepth);
179 VSCSIIoReqCompleted(hVScsiIoReq, rc);
180
181 return VINF_SUCCESS;
182}
183
184static int drvscsiGetSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint64_t *pcbSize)
185{
186 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
187
188 *pcbSize = pThis->pDrvBlock->pfnGetSize(pThis->pDrvBlock);
189
190 return VINF_SUCCESS;
191}
192
193static int drvscsiTransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterface, void *pvUser)
194{
195 PDRVSCSI pThis = PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface);
196 VSCSIIOREQ hVScsiIoReq = (VSCSIIOREQ)pvUser;
197 VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
198
199 if (enmTxDir == VSCSIIOREQTXDIR_READ)
200 pThis->pLed->Actual.s.fReading = 0;
201 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
202 pThis->pLed->Actual.s.fWriting = 0;
203 else
204 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
205
206 ASMAtomicDecU32(&pThis->StatIoDepth);
207 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS);
208
209 return VINF_SUCCESS;
210}
211
212static int drvscsiReqTransferEnqueue(VSCSILUN hVScsiLun,
213 void *pvScsiLunUser,
214 VSCSIIOREQ hVScsiIoReq)
215{
216 int rc = VINF_SUCCESS;
217 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
218
219 ASMAtomicIncU32(&pThis->StatIoDepth);
220
221 if (pThis->pDrvBlockAsync)
222 {
223 /* asnyc I/O path. */
224 VSCSIIOREQTXDIR enmTxDir;
225
226 enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
227
228 switch (enmTxDir)
229 {
230 case VSCSIIOREQTXDIR_FLUSH:
231 {
232 /** @todo Flush callback for the async I/O interface. */
233 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS);
234 break;
235 }
236 case VSCSIIOREQTXDIR_READ:
237 case VSCSIIOREQTXDIR_WRITE:
238 {
239 uint64_t uOffset = 0;
240 size_t cbTransfer = 0;
241 size_t cbSeg = 0;
242 PCPDMDATASEG paSeg = NULL;
243 unsigned cSeg = 0;
244
245 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
246 &cSeg, &cbSeg, &paSeg);
247 AssertRC(rc);
248
249 if (enmTxDir == VSCSIIOREQTXDIR_READ)
250 {
251 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
252 rc = pThis->pDrvBlockAsync->pfnStartRead(pThis->pDrvBlockAsync, uOffset,
253 (PPDMDATASEG)paSeg, cSeg, cbTransfer,
254 hVScsiIoReq);
255 if (RT_FAILURE(rc))
256 AssertMsgFailed(("%s: Failed to read data %Rrc\n", __FUNCTION__, rc));
257 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbSeg);
258 }
259 else
260 {
261 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
262 rc = pThis->pDrvBlockAsync->pfnStartWrite(pThis->pDrvBlockAsync, uOffset,
263 (PPDMDATASEG)paSeg, cSeg, cbTransfer,
264 hVScsiIoReq);
265 if (RT_FAILURE(rc))
266 AssertMsgFailed(("%s: Failed to write data %Rrc\n", __FUNCTION__, rc));
267 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbSeg);
268 }
269
270 break;
271 }
272 default:
273 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
274 }
275 }
276 else
277 {
278 /* I/O thread. */
279 rc = RTReqCallEx(pThis->pQueueRequests, NULL, 0, RTREQFLAGS_NO_WAIT,
280 (PFNRT)drvscsiProcessRequestOne, 2, pThis, hVScsiIoReq);
281 }
282
283 return rc;
284}
285
286static void drvscsiVScsiReqCompleted(VSCSIDEVICE hVScsiDevice, void *pVScsiDeviceUser,
287 void *pVScsiReqUser, int rcReq)
288{
289 PDRVSCSI pThis = (PDRVSCSI)pVScsiDeviceUser;
290
291 pThis->pDevScsiPort->pfnSCSIRequestCompleted(pThis->pDevScsiPort, (PPDMSCSIREQUEST)pVScsiReqUser,
292 rcReq);
293}
294
295/**
296 * Dummy request function used by drvscsiReset to wait for all pending requests
297 * to complete prior to the device reset.
298 *
299 * @param pThis Pointer to the instace data.
300 * @returns VINF_SUCCESS.
301 */
302static int drvscsiAsyncIOLoopSyncCallback(PDRVSCSI pThis)
303{
304 if (pThis->fDummySignal)
305 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
306 return VINF_SUCCESS;
307}
308
309/**
310 * Request function to wakeup the thread.
311 *
312 * @param pThis Pointer to the instace data.
313 * @returns VWRN_STATE_CHANGED.
314 */
315static int drvscsiAsyncIOLoopWakeupFunc(PDRVSCSI pThis)
316{
317 if (pThis->fDummySignal)
318 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
319 return VWRN_STATE_CHANGED;
320}
321
322/**
323 * The thread function which processes the requests asynchronously.
324 *
325 * @returns VBox status code.
326 * @param pDrvIns Pointer to the driver instance data.
327 * @param pThread Pointer to the thread instance data.
328 */
329static int drvscsiAsyncIOLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
330{
331 int rc = VINF_SUCCESS;
332 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
333
334 LogFlowFunc(("Entering async IO loop.\n"));
335
336 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
337 return VINF_SUCCESS;
338
339 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
340 {
341 rc = RTReqProcess(pThis->pQueueRequests, RT_INDEFINITE_WAIT);
342 AssertMsg(rc == VWRN_STATE_CHANGED, ("Left RTReqProcess and error code is not VWRN_STATE_CHANGED rc=%Rrc\n", rc));
343 }
344
345 return VINF_SUCCESS;
346}
347
348/**
349 * Deals with any pending dummy request
350 *
351 * @returns true if no pending dummy request, false if still pending.
352 * @param pThis The instance data.
353 * @param cMillies The number of milliseconds to wait for any
354 * pending request to finish.
355 */
356static bool drvscsiAsyncIOLoopNoPendingDummy(PDRVSCSI pThis, uint32_t cMillies)
357{
358 if (!pThis->pPendingDummyReq)
359 return true;
360 int rc = RTReqWait(pThis->pPendingDummyReq, cMillies);
361 if (RT_FAILURE(rc))
362 return false;
363 RTReqFree(pThis->pPendingDummyReq);
364 pThis->pPendingDummyReq = NULL;
365 return true;
366}
367
368static int drvscsiAsyncIOLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
369{
370 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
371 PRTREQ pReq;
372 int rc;
373
374 AssertMsgReturn(pThis->pQueueRequests, ("pQueueRequests is NULL\n"), VERR_INVALID_STATE);
375
376 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 10000 /* 10 sec */))
377 {
378 LogRel(("drvscsiAsyncIOLoopWakeup#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
379 return VERR_TIMEOUT;
380 }
381
382 rc = RTReqCall(pThis->pQueueRequests, &pReq, 10000 /* 10 sec. */, (PFNRT)drvscsiAsyncIOLoopWakeupFunc, 1, pThis);
383 if (RT_SUCCESS(rc))
384 RTReqFree(pReq);
385 else
386 {
387 pThis->pPendingDummyReq = pReq;
388 LogRel(("drvscsiAsyncIOLoopWakeup#%u: %Rrc pReq=%p\n", pDrvIns->iInstance, rc, pReq));
389 }
390
391 return rc;
392}
393
394/* -=-=-=-=- ISCSIConnector -=-=-=-=- */
395
396/** @copydoc PDMISCSICONNECTOR::pfnSCSIRequestSend. */
397static DECLCALLBACK(int) drvscsiRequestSend(PPDMISCSICONNECTOR pInterface, PPDMSCSIREQUEST pSCSIRequest)
398{
399 int rc;
400 PDRVSCSI pThis = PDMISCSICONNECTOR_2_DRVSCSI(pInterface);
401 VSCSIREQ hVScsiReq;
402
403 rc = VSCSIDeviceReqCreate(pThis->hVScsiDevice, &hVScsiReq,
404 pSCSIRequest->uLogicalUnit,
405 pSCSIRequest->pbCDB,
406 pSCSIRequest->cbCDB,
407 pSCSIRequest->cbScatterGather,
408 pSCSIRequest->cScatterGatherEntries,
409 pSCSIRequest->paScatterGatherHead,
410 pSCSIRequest->pbSenseBuffer,
411 pSCSIRequest->cbSenseBuffer,
412 pSCSIRequest);
413 if (RT_FAILURE(rc))
414 return rc;
415
416 rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, hVScsiReq);
417
418 return rc;
419}
420
421/* -=-=-=-=- IBase -=-=-=-=- */
422
423/**
424 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
425 */
426static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
427{
428 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
429 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
430
431 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
432 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector);
433 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pThis->IPort);
434 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKASYNCPORT, &pThis->IPortAsync);
435 return NULL;
436}
437
438/**
439 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
440 *
441 * @param pDrvIns The driver instance.
442 * @param pfnAsyncNotify The async callback.
443 */
444static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
445{
446 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
447
448 if (!pThis->pQueueRequests)
449 return;
450
451 ASMAtomicWriteBool(&pThis->fDummySignal, true);
452 if (drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
453 {
454 if (!RTReqIsBusy(pThis->pQueueRequests))
455 {
456 ASMAtomicWriteBool(&pThis->fDummySignal, false);
457 return;
458 }
459
460 PRTREQ pReq;
461 int rc = RTReqCall(pThis->pQueueRequests, &pReq, 0 /*ms*/, (PFNRT)drvscsiAsyncIOLoopSyncCallback, 1, pThis);
462 if (RT_SUCCESS(rc))
463 {
464 ASMAtomicWriteBool(&pThis->fDummySignal, false);
465 RTReqFree(pReq);
466 return;
467 }
468
469 pThis->pPendingDummyReq = pReq;
470 }
471 PDMDrvHlpSetAsyncNotification(pDrvIns, pfnAsyncNotify);
472}
473
474/**
475 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
476 *
477 * @returns true if we've quiesced, false if we're still working.
478 * @param pDrvIns The driver instance.
479 */
480static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
481{
482 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
483
484 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
485 return false;
486 ASMAtomicWriteBool(&pThis->fDummySignal, false);
487 PDMR3ThreadSuspend(pThis->pAsyncIOThread);
488 return true;
489}
490
491/**
492 * @copydoc FNPDMDRVPOWEROFF
493 */
494static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
495{
496 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
497}
498
499/**
500 * @copydoc FNPDMDRVSUSPEND
501 */
502static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
503{
504 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
505}
506
507/**
508 * Callback employed by drvscsiReset.
509 *
510 * @returns true if we've quiesced, false if we're still working.
511 * @param pDrvIns The driver instance.
512 */
513static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
514{
515 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
516
517 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
518 return false;
519 ASMAtomicWriteBool(&pThis->fDummySignal, false);
520 return true;
521}
522
523/**
524 * @copydoc FNPDMDRVRESET
525 */
526static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
527{
528 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
529}
530
531/**
532 * Destruct a driver instance.
533 *
534 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
535 * resources can be freed correctly.
536 *
537 * @param pDrvIns The driver instance data.
538 */
539static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
540{
541 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
542 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
543
544 if (pThis->pQueueRequests)
545 {
546 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 100 /*ms*/))
547 LogRel(("drvscsiDestruct#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
548
549 int rc = RTReqDestroyQueue(pThis->pQueueRequests);
550 AssertMsgRC(rc, ("Failed to destroy queue rc=%Rrc\n", rc));
551 }
552
553 /* Free the VSCSI device and LUN handle. */
554 VSCSILUN hVScsiLun;
555 int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
556 AssertRC(rc);
557
558 Assert(hVScsiLun == pThis->hVScsiLun);
559 rc = VSCSILunDestroy(hVScsiLun);
560 AssertRC(rc);
561 rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
562 AssertRC(rc);
563}
564
565/**
566 * Construct a block driver instance.
567 *
568 * @copydoc FNPDMDRVCONSTRUCT
569 */
570static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
571{
572 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
573 LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
574 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
575
576 /*
577 * Initialize the instance data.
578 */
579 pThis->pDrvIns = pDrvIns;
580 pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsiRequestSend;
581
582 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
583
584 pThis->IPortAsync.pfnTransferCompleteNotify = drvscsiTransferCompleteNotify;
585
586 /*
587 * Try attach driver below and query it's block interface.
588 */
589 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
590 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
591
592 /*
593 * Query the block and blockbios interfaces.
594 */
595 pThis->pDrvBlock = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCK);
596 if (!pThis->pDrvBlock)
597 {
598 AssertMsgFailed(("Configuration error: No block interface!\n"));
599 return VERR_PDM_MISSING_INTERFACE;
600 }
601 pThis->pDrvBlockBios = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKBIOS);
602 if (!pThis->pDrvBlockBios)
603 {
604 AssertMsgFailed(("Configuration error: No block BIOS interface!\n"));
605 return VERR_PDM_MISSING_INTERFACE;
606 }
607
608 /* Query the SCSI port interface above. */
609 pThis->pDevScsiPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISCSIPORT);
610 AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
611
612 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
613
614 /* Query the optional LED interface above. */
615 pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
616 if (pThis->pLedPort != NULL)
617 {
618 /* Get The Led. */
619 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
620 if (RT_FAILURE(rc))
621 pThis->pLed = &pThis->Led;
622 }
623 else
624 pThis->pLed = &pThis->Led;
625
626 /* Try to get the optional async block interface. */
627 pThis->pDrvBlockAsync = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKASYNC);
628
629 PDMBLOCKTYPE enmType = pThis->pDrvBlock->pfnGetType(pThis->pDrvBlock);
630 if (enmType != PDMBLOCKTYPE_HARD_DISK)
631 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
632 N_("Only hard disks are currently supported as SCSI devices (enmType=%d)"),
633 enmType);
634
635 /* Create VSCSI device and LUN. */
636 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
637 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
638
639 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis);
640 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n"), rc);
641 rc = VSCSILunCreate(&pThis->hVScsiLun, VSCSILUNTYPE_SBC, &pThis->VScsiIoCallbacks,
642 pThis);
643 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n"), rc);
644 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
645 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
646
647 /* Create request queue. */
648 rc = RTReqCreateQueue(&pThis->pQueueRequests);
649 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create request queue rc=%Rrc\n"), rc);
650
651 /* Register statistics counter. */
652 /** @todo aeichner: Find a way to put the instance number of the attached
653 * controller device when we support more than one controller of the same type.
654 * At the moment we have the 0 hardcoded. */
655 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
656 "Amount of data read.", "/Devices/SCSI0/%d/ReadBytes", pDrvIns->iInstance);
657 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
658 "Amount of data written.", "/Devices/SCSI0/%d/WrittenBytes", pDrvIns->iInstance);
659
660 pThis->StatIoDepth = 0;
661
662 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
663 "Number of active tasks.", "/Devices/SCSI0/%d/IoDepth", pDrvIns->iInstance);
664
665
666 /* Create I/O thread. */
667 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pAsyncIOThread, pThis, drvscsiAsyncIOLoop,
668 drvscsiAsyncIOLoopWakeup, 0, RTTHREADTYPE_IO, "SCSI async IO");
669 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create async I/O thread rc=%Rrc\n"), rc);
670
671 return VINF_SUCCESS;
672}
673
674/**
675 * SCSI driver registration record.
676 */
677const PDMDRVREG g_DrvSCSI =
678{
679 /* u32Version */
680 PDM_DRVREG_VERSION,
681 /* szName */
682 "SCSI",
683 /* szRCMod */
684 "",
685 /* szR0Mod */
686 "",
687 /* pszDescription */
688 "Generic SCSI driver.",
689 /* fFlags */
690 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
691 /* fClass. */
692 PDM_DRVREG_CLASS_SCSI,
693 /* cMaxInstances */
694 ~0,
695 /* cbInstance */
696 sizeof(DRVSCSI),
697 /* pfnConstruct */
698 drvscsiConstruct,
699 /* pfnDestruct */
700 drvscsiDestruct,
701 /* pfnRelocate */
702 NULL,
703 /* pfnIOCtl */
704 NULL,
705 /* pfnPowerOn */
706 NULL,
707 /* pfnReset */
708 drvscsiReset,
709 /* pfnSuspend */
710 drvscsiSuspend,
711 /* pfnResume */
712 NULL,
713 /* pfnAttach */
714 NULL,
715 /* pfnDetach */
716 NULL,
717 /* pfnPowerOff */
718 drvscsiPowerOff,
719 /* pfnSoftReset */
720 NULL,
721 /* u32EndVersion */
722 PDM_DRVREG_VERSION
723};
724
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette