VirtualBox

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

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

SCSI: Log used I/O method

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