VirtualBox

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

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

DrvSCSI: Fix crash when an error occurs and print errors in the release log

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

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