VirtualBox

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

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

SCSI: Another assertion fix

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