VirtualBox

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

Last change on this file since 33524 was 33101, checked in by vboxsync, 14 years ago

BusLogic,SCSI: Don't propagate VINF_VD_ASYNC_IO_FINISHED to the caller

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

© 2023 Oracle
ContactPrivacy policyTerms of Use