VirtualBox

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

Last change on this file since 27848 was 27671, checked in by vboxsync, 15 years ago

SCSI: Fixes for the async I/O path

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.3 KB
Line 
1/* $Id: DrvSCSI.cpp 27671 2010-03-24 15:45:38Z 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.virtualbox.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 LogFlowFunc(("Request hVScsiIoReq=%#p completed\n", hVScsiIoReq));
200
201 if (enmTxDir == VSCSIIOREQTXDIR_READ)
202 pThis->pLed->Actual.s.fReading = 0;
203 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
204 pThis->pLed->Actual.s.fWriting = 0;
205 else
206 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
207
208 ASMAtomicDecU32(&pThis->StatIoDepth);
209 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS);
210
211 return VINF_SUCCESS;
212}
213
214static int drvscsiReqTransferEnqueue(VSCSILUN hVScsiLun,
215 void *pvScsiLunUser,
216 VSCSIIOREQ hVScsiIoReq)
217{
218 int rc = VINF_SUCCESS;
219 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
220
221 ASMAtomicIncU32(&pThis->StatIoDepth);
222
223 if (pThis->pDrvBlockAsync)
224 {
225 /* asnyc I/O path. */
226 VSCSIIOREQTXDIR enmTxDir;
227
228 LogFlowFunc(("Enqueuing hVScsiIoReq=%#p\n", hVScsiIoReq));
229
230 enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
231
232 switch (enmTxDir)
233 {
234 case VSCSIIOREQTXDIR_FLUSH:
235 {
236 /** @todo Flush callback for the async I/O interface. */
237 ASMAtomicDecU32(&pThis->StatIoDepth);
238 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS);
239 break;
240 }
241 case VSCSIIOREQTXDIR_READ:
242 case VSCSIIOREQTXDIR_WRITE:
243 {
244 uint64_t uOffset = 0;
245 size_t cbTransfer = 0;
246 size_t cbSeg = 0;
247 PCPDMDATASEG paSeg = NULL;
248 unsigned cSeg = 0;
249
250 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
251 &cSeg, &cbSeg, &paSeg);
252 AssertRC(rc);
253
254 if (enmTxDir == VSCSIIOREQTXDIR_READ)
255 {
256 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
257 rc = pThis->pDrvBlockAsync->pfnStartRead(pThis->pDrvBlockAsync, uOffset,
258 (PPDMDATASEG)paSeg, cSeg, cbTransfer,
259 hVScsiIoReq);
260 if (RT_FAILURE(rc))
261 AssertMsgFailed(("%s: Failed to read data %Rrc\n", __FUNCTION__, rc));
262 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbTransfer);
263 }
264 else
265 {
266 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
267 rc = pThis->pDrvBlockAsync->pfnStartWrite(pThis->pDrvBlockAsync, uOffset,
268 (PPDMDATASEG)paSeg, cSeg, cbTransfer,
269 hVScsiIoReq);
270 if (RT_FAILURE(rc))
271 AssertMsgFailed(("%s: Failed to write data %Rrc\n", __FUNCTION__, rc));
272 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbTransfer);
273 }
274
275 if (rc == VINF_VD_ASYNC_IO_FINISHED)
276 {
277 if (enmTxDir == VSCSIIOREQTXDIR_READ)
278 pThis->pLed->Actual.s.fReading = 0;
279 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
280 pThis->pLed->Actual.s.fWriting = 0;
281 else
282 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
283 ASMAtomicDecU32(&pThis->StatIoDepth);
284 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS);
285 }
286
287 break;
288 }
289 default:
290 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
291 }
292 }
293 else
294 {
295 /* I/O thread. */
296 rc = RTReqCallEx(pThis->pQueueRequests, NULL, 0, RTREQFLAGS_NO_WAIT,
297 (PFNRT)drvscsiProcessRequestOne, 2, pThis, hVScsiIoReq);
298 }
299
300 return rc;
301}
302
303static void drvscsiVScsiReqCompleted(VSCSIDEVICE hVScsiDevice, void *pVScsiDeviceUser,
304 void *pVScsiReqUser, int rcReq)
305{
306 PDRVSCSI pThis = (PDRVSCSI)pVScsiDeviceUser;
307
308 pThis->pDevScsiPort->pfnSCSIRequestCompleted(pThis->pDevScsiPort, (PPDMSCSIREQUEST)pVScsiReqUser,
309 rcReq);
310}
311
312/**
313 * Dummy request function used by drvscsiReset to wait for all pending requests
314 * to complete prior to the device reset.
315 *
316 * @param pThis Pointer to the instace data.
317 * @returns VINF_SUCCESS.
318 */
319static int drvscsiAsyncIOLoopSyncCallback(PDRVSCSI pThis)
320{
321 if (pThis->fDummySignal)
322 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
323 return VINF_SUCCESS;
324}
325
326/**
327 * Request function to wakeup the thread.
328 *
329 * @param pThis Pointer to the instace data.
330 * @returns VWRN_STATE_CHANGED.
331 */
332static int drvscsiAsyncIOLoopWakeupFunc(PDRVSCSI pThis)
333{
334 if (pThis->fDummySignal)
335 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
336 return VWRN_STATE_CHANGED;
337}
338
339/**
340 * The thread function which processes the requests asynchronously.
341 *
342 * @returns VBox status code.
343 * @param pDrvIns Pointer to the driver instance data.
344 * @param pThread Pointer to the thread instance data.
345 */
346static int drvscsiAsyncIOLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
347{
348 int rc = VINF_SUCCESS;
349 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
350
351 LogFlowFunc(("Entering async IO loop.\n"));
352
353 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
354 return VINF_SUCCESS;
355
356 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
357 {
358 rc = RTReqProcess(pThis->pQueueRequests, RT_INDEFINITE_WAIT);
359 AssertMsg(rc == VWRN_STATE_CHANGED, ("Left RTReqProcess and error code is not VWRN_STATE_CHANGED rc=%Rrc\n", rc));
360 }
361
362 return VINF_SUCCESS;
363}
364
365/**
366 * Deals with any pending dummy request
367 *
368 * @returns true if no pending dummy request, false if still pending.
369 * @param pThis The instance data.
370 * @param cMillies The number of milliseconds to wait for any
371 * pending request to finish.
372 */
373static bool drvscsiAsyncIOLoopNoPendingDummy(PDRVSCSI pThis, uint32_t cMillies)
374{
375 if (!pThis->pPendingDummyReq)
376 return true;
377 int rc = RTReqWait(pThis->pPendingDummyReq, cMillies);
378 if (RT_FAILURE(rc))
379 return false;
380 RTReqFree(pThis->pPendingDummyReq);
381 pThis->pPendingDummyReq = NULL;
382 return true;
383}
384
385static int drvscsiAsyncIOLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
386{
387 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
388 PRTREQ pReq;
389 int rc;
390
391 AssertMsgReturn(pThis->pQueueRequests, ("pQueueRequests is NULL\n"), VERR_INVALID_STATE);
392
393 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 10000 /* 10 sec */))
394 {
395 LogRel(("drvscsiAsyncIOLoopWakeup#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
396 return VERR_TIMEOUT;
397 }
398
399 rc = RTReqCall(pThis->pQueueRequests, &pReq, 10000 /* 10 sec. */, (PFNRT)drvscsiAsyncIOLoopWakeupFunc, 1, pThis);
400 if (RT_SUCCESS(rc))
401 RTReqFree(pReq);
402 else
403 {
404 pThis->pPendingDummyReq = pReq;
405 LogRel(("drvscsiAsyncIOLoopWakeup#%u: %Rrc pReq=%p\n", pDrvIns->iInstance, rc, pReq));
406 }
407
408 return rc;
409}
410
411/* -=-=-=-=- ISCSIConnector -=-=-=-=- */
412
413/** @copydoc PDMISCSICONNECTOR::pfnSCSIRequestSend. */
414static DECLCALLBACK(int) drvscsiRequestSend(PPDMISCSICONNECTOR pInterface, PPDMSCSIREQUEST pSCSIRequest)
415{
416 int rc;
417 PDRVSCSI pThis = PDMISCSICONNECTOR_2_DRVSCSI(pInterface);
418 VSCSIREQ hVScsiReq;
419
420 rc = VSCSIDeviceReqCreate(pThis->hVScsiDevice, &hVScsiReq,
421 pSCSIRequest->uLogicalUnit,
422 pSCSIRequest->pbCDB,
423 pSCSIRequest->cbCDB,
424 pSCSIRequest->cbScatterGather,
425 pSCSIRequest->cScatterGatherEntries,
426 pSCSIRequest->paScatterGatherHead,
427 pSCSIRequest->pbSenseBuffer,
428 pSCSIRequest->cbSenseBuffer,
429 pSCSIRequest);
430 if (RT_FAILURE(rc))
431 return rc;
432
433 rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, hVScsiReq);
434
435 return rc;
436}
437
438/* -=-=-=-=- IBase -=-=-=-=- */
439
440/**
441 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
442 */
443static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
444{
445 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
446 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
447
448 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
449 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector);
450 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pThis->IPort);
451 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKASYNCPORT, &pThis->IPortAsync);
452 return NULL;
453}
454
455/**
456 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
457 *
458 * @param pDrvIns The driver instance.
459 * @param pfnAsyncNotify The async callback.
460 */
461static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
462{
463 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
464
465 if (!pThis->pQueueRequests)
466 return;
467
468 ASMAtomicWriteBool(&pThis->fDummySignal, true);
469 if (drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
470 {
471 if (!RTReqIsBusy(pThis->pQueueRequests))
472 {
473 ASMAtomicWriteBool(&pThis->fDummySignal, false);
474 return;
475 }
476
477 PRTREQ pReq;
478 int rc = RTReqCall(pThis->pQueueRequests, &pReq, 0 /*ms*/, (PFNRT)drvscsiAsyncIOLoopSyncCallback, 1, pThis);
479 if (RT_SUCCESS(rc))
480 {
481 ASMAtomicWriteBool(&pThis->fDummySignal, false);
482 RTReqFree(pReq);
483 return;
484 }
485
486 pThis->pPendingDummyReq = pReq;
487 }
488 PDMDrvHlpSetAsyncNotification(pDrvIns, pfnAsyncNotify);
489}
490
491/**
492 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
493 *
494 * @returns true if we've quiesced, false if we're still working.
495 * @param pDrvIns The driver instance.
496 */
497static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
498{
499 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
500
501 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
502 return false;
503 ASMAtomicWriteBool(&pThis->fDummySignal, false);
504 PDMR3ThreadSuspend(pThis->pAsyncIOThread);
505 return true;
506}
507
508/**
509 * @copydoc FNPDMDRVPOWEROFF
510 */
511static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
512{
513 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
514}
515
516/**
517 * @copydoc FNPDMDRVSUSPEND
518 */
519static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
520{
521 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
522}
523
524/**
525 * Callback employed by drvscsiReset.
526 *
527 * @returns true if we've quiesced, false if we're still working.
528 * @param pDrvIns The driver instance.
529 */
530static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
531{
532 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
533
534 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
535 return false;
536 ASMAtomicWriteBool(&pThis->fDummySignal, false);
537 return true;
538}
539
540/**
541 * @copydoc FNPDMDRVRESET
542 */
543static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
544{
545 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
546}
547
548/**
549 * Destruct a driver instance.
550 *
551 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
552 * resources can be freed correctly.
553 *
554 * @param pDrvIns The driver instance data.
555 */
556static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
557{
558 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
559 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
560
561 if (pThis->pQueueRequests)
562 {
563 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 100 /*ms*/))
564 LogRel(("drvscsiDestruct#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
565
566 int rc = RTReqDestroyQueue(pThis->pQueueRequests);
567 AssertMsgRC(rc, ("Failed to destroy queue rc=%Rrc\n", rc));
568 }
569
570 /* Free the VSCSI device and LUN handle. */
571 VSCSILUN hVScsiLun;
572 int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
573 AssertRC(rc);
574
575 Assert(hVScsiLun == pThis->hVScsiLun);
576 rc = VSCSILunDestroy(hVScsiLun);
577 AssertRC(rc);
578 rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
579 AssertRC(rc);
580}
581
582/**
583 * Construct a block driver instance.
584 *
585 * @copydoc FNPDMDRVCONSTRUCT
586 */
587static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
588{
589 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
590 LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
591 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
592
593 /*
594 * Initialize the instance data.
595 */
596 pThis->pDrvIns = pDrvIns;
597 pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsiRequestSend;
598
599 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
600
601 pThis->IPortAsync.pfnTransferCompleteNotify = drvscsiTransferCompleteNotify;
602
603 /*
604 * Try attach driver below and query it's block interface.
605 */
606 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
607 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
608
609 /*
610 * Query the block and blockbios interfaces.
611 */
612 pThis->pDrvBlock = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCK);
613 if (!pThis->pDrvBlock)
614 {
615 AssertMsgFailed(("Configuration error: No block interface!\n"));
616 return VERR_PDM_MISSING_INTERFACE;
617 }
618 pThis->pDrvBlockBios = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKBIOS);
619 if (!pThis->pDrvBlockBios)
620 {
621 AssertMsgFailed(("Configuration error: No block BIOS interface!\n"));
622 return VERR_PDM_MISSING_INTERFACE;
623 }
624
625 /* Query the SCSI port interface above. */
626 pThis->pDevScsiPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISCSIPORT);
627 AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
628
629 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
630
631 /* Query the optional LED interface above. */
632 pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
633 if (pThis->pLedPort != NULL)
634 {
635 /* Get The Led. */
636 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
637 if (RT_FAILURE(rc))
638 pThis->pLed = &pThis->Led;
639 }
640 else
641 pThis->pLed = &pThis->Led;
642
643 /* Try to get the optional async block interface. */
644 pThis->pDrvBlockAsync = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKASYNC);
645
646 PDMBLOCKTYPE enmType = pThis->pDrvBlock->pfnGetType(pThis->pDrvBlock);
647 if (enmType != PDMBLOCKTYPE_HARD_DISK)
648 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
649 N_("Only hard disks are currently supported as SCSI devices (enmType=%d)"),
650 enmType);
651
652 /* Create VSCSI device and LUN. */
653 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
654 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
655
656 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis);
657 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n"), rc);
658 rc = VSCSILunCreate(&pThis->hVScsiLun, VSCSILUNTYPE_SBC, &pThis->VScsiIoCallbacks,
659 pThis);
660 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n"), rc);
661 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
662 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
663
664 /* Create request queue. */
665 rc = RTReqCreateQueue(&pThis->pQueueRequests);
666 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create request queue rc=%Rrc\n"), rc);
667
668 /* Register statistics counter. */
669 /** @todo aeichner: Find a way to put the instance number of the attached
670 * controller device when we support more than one controller of the same type.
671 * At the moment we have the 0 hardcoded. */
672 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
673 "Amount of data read.", "/Devices/SCSI0/%d/ReadBytes", pDrvIns->iInstance);
674 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
675 "Amount of data written.", "/Devices/SCSI0/%d/WrittenBytes", pDrvIns->iInstance);
676
677 pThis->StatIoDepth = 0;
678
679 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
680 "Number of active tasks.", "/Devices/SCSI0/%d/IoDepth", pDrvIns->iInstance);
681
682
683 /* Create I/O thread. */
684 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pAsyncIOThread, pThis, drvscsiAsyncIOLoop,
685 drvscsiAsyncIOLoopWakeup, 0, RTTHREADTYPE_IO, "SCSI async IO");
686 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create async I/O thread rc=%Rrc\n"), rc);
687
688 return VINF_SUCCESS;
689}
690
691/**
692 * SCSI driver registration record.
693 */
694const PDMDRVREG g_DrvSCSI =
695{
696 /* u32Version */
697 PDM_DRVREG_VERSION,
698 /* szName */
699 "SCSI",
700 /* szRCMod */
701 "",
702 /* szR0Mod */
703 "",
704 /* pszDescription */
705 "Generic SCSI driver.",
706 /* fFlags */
707 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
708 /* fClass. */
709 PDM_DRVREG_CLASS_SCSI,
710 /* cMaxInstances */
711 ~0,
712 /* cbInstance */
713 sizeof(DRVSCSI),
714 /* pfnConstruct */
715 drvscsiConstruct,
716 /* pfnDestruct */
717 drvscsiDestruct,
718 /* pfnRelocate */
719 NULL,
720 /* pfnIOCtl */
721 NULL,
722 /* pfnPowerOn */
723 NULL,
724 /* pfnReset */
725 drvscsiReset,
726 /* pfnSuspend */
727 drvscsiSuspend,
728 /* pfnResume */
729 NULL,
730 /* pfnAttach */
731 NULL,
732 /* pfnDetach */
733 NULL,
734 /* pfnPowerOff */
735 drvscsiPowerOff,
736 /* pfnSoftReset */
737 NULL,
738 /* u32EndVersion */
739 PDM_DRVREG_VERSION
740};
741
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