VirtualBox

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

Last change on this file since 33000 was 32983, checked in by vboxsync, 14 years ago

LsiLogic: Suspend the VM on a recoverable error without changing the saved state format

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

© 2023 Oracle
ContactPrivacy policyTerms of Use