VirtualBox

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

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

SCSI: Fix IoDepth statistic

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.1 KB
Line 
1/* $Id: DrvSCSI.cpp 29586 2010-05-17 21:25:30Z vboxsync $ */
2/** @file
3 * VBox storage drivers: Generic SCSI command parser and execution driver
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21//#define DEBUG
22#define LOG_GROUP LOG_GROUP_DRV_SCSI
23#include <VBox/pdmdrv.h>
24#include <VBox/pdmifs.h>
25#include <VBox/pdmthread.h>
26#include <VBox/vscsi.h>
27#include <iprt/asm.h>
28#include <iprt/assert.h>
29#include <iprt/mem.h>
30#include <iprt/req.h>
31#include <iprt/semaphore.h>
32#include <iprt/string.h>
33#include <iprt/uuid.h>
34
35#include "Builtins.h"
36
37/** The maximum number of release log entries per device. */
38#define MAX_LOG_REL_ERRORS 1024
39
40/**
41 * SCSI driver instance data.
42 *
43 * @implements PDMISCSICONNECTOR
44 * @implements PDMIBLOCKASYNCPORT
45 * @implements PDMIMOUNTNOTIFY
46 */
47typedef struct DRVSCSI
48{
49 /** Pointer driver instance. */
50 PPDMDRVINS pDrvIns;
51
52 /** Pointer to the attached driver's base interface. */
53 PPDMIBASE pDrvBase;
54 /** Pointer to the attached driver's block interface. */
55 PPDMIBLOCK pDrvBlock;
56 /** Pointer to the attached driver's async block interface. */
57 PPDMIBLOCKASYNC pDrvBlockAsync;
58 /** Pointer to the attached driver's block bios interface. */
59 PPDMIBLOCKBIOS pDrvBlockBios;
60 /** Pointer to the attached driver's mount interface. */
61 PPDMIMOUNT pDrvMount;
62 /** Pointer to the SCSI port interface of the device above. */
63 PPDMISCSIPORT pDevScsiPort;
64 /** pointer to the Led port interface of the dveice above. */
65 PPDMILEDPORTS pLedPort;
66 /** The scsi connector interface .*/
67 PDMISCSICONNECTOR ISCSIConnector;
68 /** The block port interface. */
69 PDMIBLOCKPORT IPort;
70 /** The optional block async port interface. */
71 PDMIBLOCKASYNCPORT IPortAsync;
72#if 0 /* these interfaces aren't implemented */
73 /** The mount notify interface. */
74 PDMIMOUNTNOTIFY IMountNotify;
75#endif
76 /** Fallback status LED state for this drive.
77 * This is used in case the device doesn't has a LED interface. */
78 PDMLED Led;
79 /** Pointer to the status LED for this drive. */
80 PPDMLED pLed;
81
82 /** VSCSI device handle. */
83 VSCSIDEVICE hVScsiDevice;
84 /** VSCSI LUN handle. */
85 VSCSILUN hVScsiLun;
86 /** I/O callbacks. */
87 VSCSILUNIOCALLBACKS VScsiIoCallbacks;
88
89 /** The dedicated I/O thread for the non async approach. */
90 PPDMTHREAD pAsyncIOThread;
91 /** Queue for passing the requests to the thread. */
92 PRTREQQUEUE pQueueRequests;
93 /** Request that we've left pending on wakeup or reset. */
94 PRTREQ pPendingDummyReq;
95 /** Indicates whether PDMDrvHlpAsyncNotificationCompleted should be called by
96 * any of the dummy functions. */
97 bool volatile fDummySignal;
98 /** Release statistics: number of bytes written. */
99 STAMCOUNTER StatBytesWritten;
100 /** Release statistics: number of bytes read. */
101 STAMCOUNTER StatBytesRead;
102 /** Release statistics: Current I/O depth. */
103 volatile uint32_t StatIoDepth;
104 /** Errors printed in the release log. */
105 unsigned cErrors;
106} DRVSCSI, *PDRVSCSI;
107
108/** Converts a pointer to DRVSCSI::ISCSIConnector to a PDRVSCSI. */
109#define PDMISCSICONNECTOR_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, ISCSIConnector)) )
110/** Converts a pointer to DRVSCSI::IPortAsync to a PDRVSCSI. */
111#define PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IPortAsync)) )
112
113static int drvscsiProcessRequestOne(PDRVSCSI pThis, VSCSIIOREQ hVScsiIoReq)
114{
115 int rc = VINF_SUCCESS;
116 VSCSIIOREQTXDIR enmTxDir;
117
118 enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
119
120 switch (enmTxDir)
121 {
122 case VSCSIIOREQTXDIR_FLUSH:
123 {
124 rc = pThis->pDrvBlock->pfnFlush(pThis->pDrvBlock);
125 break;
126 }
127 case VSCSIIOREQTXDIR_READ:
128 case VSCSIIOREQTXDIR_WRITE:
129 {
130 uint64_t uOffset = 0;
131 size_t cbTransfer = 0;
132 size_t cbSeg = 0;
133 PCRTSGSEG paSeg = NULL;
134 unsigned cSeg = 0;
135
136 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg,
137 &paSeg);
138 AssertRC(rc);
139
140 while (cbTransfer && cSeg)
141 {
142 size_t cbProcess = (cbTransfer < paSeg->cbSeg) ? cbTransfer : paSeg->cbSeg;
143
144 Log(("%s: uOffset=%llu cbProcess=%u\n", __FUNCTION__, uOffset, cbProcess));
145
146 if (enmTxDir == VSCSIIOREQTXDIR_READ)
147 {
148 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
149 rc = pThis->pDrvBlock->pfnRead(pThis->pDrvBlock, uOffset,
150 paSeg->pvSeg, cbProcess);
151 pThis->pLed->Actual.s.fReading = 0;
152 if (RT_FAILURE(rc))
153 AssertMsgFailed(("%s: Failed to read data %Rrc\n", __FUNCTION__, rc));
154 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbProcess);
155 }
156 else
157 {
158 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
159 rc = pThis->pDrvBlock->pfnWrite(pThis->pDrvBlock, uOffset,
160 paSeg->pvSeg, cbProcess);
161 pThis->pLed->Actual.s.fWriting = 0;
162 if (RT_FAILURE(rc))
163 AssertMsgFailed(("%s: Failed to write data %Rrc\n", __FUNCTION__, rc));
164 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbProcess);
165 }
166
167 /* Go to the next entry. */
168 uOffset += cbProcess;
169 cbTransfer -= cbProcess;
170 paSeg++;
171 cSeg--;
172 }
173
174 break;
175 }
176 default:
177 AssertMsgFailed(("Invalid transfer direction %d\n", enmTxDir));
178 }
179
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 VSCSIIoReqCompleted(hVScsiIoReq, rc);
210
211 return VINF_SUCCESS;
212}
213
214static int drvscsiReqTransferEnqueue(VSCSILUN hVScsiLun,
215 void *pvScsiLunUser,
216 VSCSIIOREQ hVScsiIoReq)
217{
218 int rc = VINF_SUCCESS;
219 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
220
221 if (pThis->pDrvBlockAsync)
222 {
223 /* async I/O path. */
224 VSCSIIOREQTXDIR enmTxDir;
225
226 LogFlowFunc(("Enqueuing hVScsiIoReq=%#p\n", hVScsiIoReq));
227
228 enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
229
230 switch (enmTxDir)
231 {
232 case VSCSIIOREQTXDIR_FLUSH:
233 {
234 rc = pThis->pDrvBlockAsync->pfnStartFlush(pThis->pDrvBlockAsync, hVScsiIoReq);
235 if ( RT_FAILURE(rc)
236 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS
237 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
238 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
239 pThis->pDrvIns->iInstance, rc));
240 break;
241 }
242 case VSCSIIOREQTXDIR_READ:
243 case VSCSIIOREQTXDIR_WRITE:
244 {
245 uint64_t uOffset = 0;
246 size_t cbTransfer = 0;
247 size_t cbSeg = 0;
248 PCRTSGSEG paSeg = NULL;
249 unsigned cSeg = 0;
250
251 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
252 &cSeg, &cbSeg, &paSeg);
253 AssertRC(rc);
254
255 if (enmTxDir == VSCSIIOREQTXDIR_READ)
256 {
257 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
258 rc = pThis->pDrvBlockAsync->pfnStartRead(pThis->pDrvBlockAsync, uOffset,
259 paSeg, cSeg, cbTransfer,
260 hVScsiIoReq);
261 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbTransfer);
262 }
263 else
264 {
265 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
266 rc = pThis->pDrvBlockAsync->pfnStartWrite(pThis->pDrvBlockAsync, uOffset,
267 paSeg, cSeg, cbTransfer,
268 hVScsiIoReq);
269 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbTransfer);
270 }
271
272 if ( RT_FAILURE(rc)
273 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS
274 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
275 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
276 pThis->pDrvIns->iInstance,
277 enmTxDir == VSCSIIOREQTXDIR_READ
278 ? "Read"
279 : "Write",
280 uOffset,
281 cbTransfer, rc));
282 break;
283 }
284 default:
285 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
286 }
287
288 if (rc == VINF_VD_ASYNC_IO_FINISHED)
289 {
290 if (enmTxDir == VSCSIIOREQTXDIR_READ)
291 pThis->pLed->Actual.s.fReading = 0;
292 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
293 pThis->pLed->Actual.s.fWriting = 0;
294 else
295 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
296
297 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS);
298 }
299 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
300 rc = VINF_SUCCESS;
301 else if (RT_FAILURE(rc))
302 {
303 if (enmTxDir == VSCSIIOREQTXDIR_READ)
304 pThis->pLed->Actual.s.fReading = 0;
305 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
306 pThis->pLed->Actual.s.fWriting = 0;
307 else
308 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
309
310 VSCSIIoReqCompleted(hVScsiIoReq, rc);
311 rc = VINF_SUCCESS;
312 }
313 else
314 AssertMsgFailed(("Invalid return code rc=%Rrc\n", rc));
315 }
316 else
317 {
318 /* I/O thread. */
319 rc = RTReqCallEx(pThis->pQueueRequests, NULL, 0, RTREQFLAGS_NO_WAIT,
320 (PFNRT)drvscsiProcessRequestOne, 2, pThis, hVScsiIoReq);
321 }
322
323 return rc;
324}
325
326static void drvscsiVScsiReqCompleted(VSCSIDEVICE hVScsiDevice, void *pVScsiDeviceUser,
327 void *pVScsiReqUser, int rcReq)
328{
329 PDRVSCSI pThis = (PDRVSCSI)pVScsiDeviceUser;
330
331 ASMAtomicDecU32(&pThis->StatIoDepth);
332
333 pThis->pDevScsiPort->pfnSCSIRequestCompleted(pThis->pDevScsiPort, (PPDMSCSIREQUEST)pVScsiReqUser,
334 rcReq);
335
336 if (RT_UNLIKELY(pThis->fDummySignal) && !pThis->StatIoDepth)
337 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
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 ASMAtomicIncU32(&pThis->StatIoDepth);
462 rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, hVScsiReq);
463
464 return rc;
465}
466
467/* -=-=-=-=- IBase -=-=-=-=- */
468
469/**
470 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
471 */
472static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
473{
474 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
475 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
476
477 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
478 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector);
479 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pThis->IPort);
480 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKASYNCPORT, &pThis->IPortAsync);
481 return NULL;
482}
483
484/**
485 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
486 *
487 * @param pDrvIns The driver instance.
488 * @param pfnAsyncNotify The async callback.
489 */
490static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
491{
492 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
493
494 if (!pThis->pDrvBlockAsync)
495 {
496 if (!pThis->pQueueRequests)
497 return;
498
499 ASMAtomicWriteBool(&pThis->fDummySignal, true);
500 if (drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
501 {
502 if (!RTReqIsBusy(pThis->pQueueRequests))
503 {
504 ASMAtomicWriteBool(&pThis->fDummySignal, false);
505 return;
506 }
507
508 PRTREQ pReq;
509 int rc = RTReqCall(pThis->pQueueRequests, &pReq, 0 /*ms*/, (PFNRT)drvscsiAsyncIOLoopSyncCallback, 1, pThis);
510 if (RT_SUCCESS(rc))
511 {
512 ASMAtomicWriteBool(&pThis->fDummySignal, false);
513 RTReqFree(pReq);
514 return;
515 }
516
517 pThis->pPendingDummyReq = pReq;
518 }
519 }
520 else
521 {
522 if (pThis->StatIoDepth > 0)
523 {
524 ASMAtomicWriteBool(&pThis->fDummySignal, true);
525 }
526 return;
527 }
528
529 PDMDrvHlpSetAsyncNotification(pDrvIns, pfnAsyncNotify);
530}
531
532/**
533 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
534 *
535 * @returns true if we've quiesced, false if we're still working.
536 * @param pDrvIns The driver instance.
537 */
538static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
539{
540 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
541
542 if (pThis->pDrvBlockAsync)
543 {
544 if (pThis->StatIoDepth > 0)
545 return false;
546 else
547 return true;
548 }
549 else
550 {
551 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
552 return false;
553 ASMAtomicWriteBool(&pThis->fDummySignal, false);
554 PDMR3ThreadSuspend(pThis->pAsyncIOThread);
555 return true;
556 }
557}
558
559/**
560 * @copydoc FNPDMDRVPOWEROFF
561 */
562static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
563{
564 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
565}
566
567/**
568 * @copydoc FNPDMDRVSUSPEND
569 */
570static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
571{
572 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
573}
574
575/**
576 * Callback employed by drvscsiReset.
577 *
578 * @returns true if we've quiesced, false if we're still working.
579 * @param pDrvIns The driver instance.
580 */
581static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
582{
583 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
584
585 if (pThis->pDrvBlockAsync)
586 {
587 if (pThis->StatIoDepth > 0)
588 return false;
589 else
590 return true;
591 }
592 else
593 {
594 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
595 return false;
596 ASMAtomicWriteBool(&pThis->fDummySignal, false);
597 return true;
598 }
599}
600
601/**
602 * @copydoc FNPDMDRVRESET
603 */
604static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
605{
606 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
607}
608
609/**
610 * Destruct a driver instance.
611 *
612 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
613 * resources can be freed correctly.
614 *
615 * @param pDrvIns The driver instance data.
616 */
617static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
618{
619 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
620 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
621
622 if (pThis->pQueueRequests)
623 {
624 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 100 /*ms*/))
625 LogRel(("drvscsiDestruct#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
626
627 int rc = RTReqDestroyQueue(pThis->pQueueRequests);
628 AssertMsgRC(rc, ("Failed to destroy queue rc=%Rrc\n", rc));
629 }
630
631 /* Free the VSCSI device and LUN handle. */
632 VSCSILUN hVScsiLun;
633 int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
634 AssertRC(rc);
635
636 Assert(hVScsiLun == pThis->hVScsiLun);
637 rc = VSCSILunDestroy(hVScsiLun);
638 AssertRC(rc);
639 rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
640 AssertRC(rc);
641}
642
643/**
644 * Construct a block driver instance.
645 *
646 * @copydoc FNPDMDRVCONSTRUCT
647 */
648static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
649{
650 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
651 LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
652 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
653
654 /*
655 * Initialize the instance data.
656 */
657 pThis->pDrvIns = pDrvIns;
658 pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsiRequestSend;
659
660 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
661
662 pThis->IPortAsync.pfnTransferCompleteNotify = drvscsiTransferCompleteNotify;
663
664 /*
665 * Try attach driver below and query it's block interface.
666 */
667 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
668 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
669
670 /*
671 * Query the block and blockbios interfaces.
672 */
673 pThis->pDrvBlock = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCK);
674 if (!pThis->pDrvBlock)
675 {
676 AssertMsgFailed(("Configuration error: No block interface!\n"));
677 return VERR_PDM_MISSING_INTERFACE;
678 }
679 pThis->pDrvBlockBios = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKBIOS);
680 if (!pThis->pDrvBlockBios)
681 {
682 AssertMsgFailed(("Configuration error: No block BIOS interface!\n"));
683 return VERR_PDM_MISSING_INTERFACE;
684 }
685
686 /* Query the SCSI port interface above. */
687 pThis->pDevScsiPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISCSIPORT);
688 AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
689
690 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
691
692 /* Query the optional LED interface above. */
693 pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
694 if (pThis->pLedPort != NULL)
695 {
696 /* Get The Led. */
697 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
698 if (RT_FAILURE(rc))
699 pThis->pLed = &pThis->Led;
700 }
701 else
702 pThis->pLed = &pThis->Led;
703
704 /* Try to get the optional async block interface. */
705 pThis->pDrvBlockAsync = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKASYNC);
706
707 PDMBLOCKTYPE enmType = pThis->pDrvBlock->pfnGetType(pThis->pDrvBlock);
708 if (enmType != PDMBLOCKTYPE_HARD_DISK)
709 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
710 N_("Only hard disks are currently supported as SCSI devices (enmType=%d)"),
711 enmType);
712
713 /* Create VSCSI device and LUN. */
714 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
715 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
716
717 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis);
718 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n"), rc);
719 rc = VSCSILunCreate(&pThis->hVScsiLun, VSCSILUNTYPE_SBC, &pThis->VScsiIoCallbacks,
720 pThis);
721 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n"), rc);
722 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
723 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
724
725 /* Register statistics counter. */
726 /** @todo aeichner: Find a way to put the instance number of the attached
727 * controller device when we support more than one controller of the same type.
728 * At the moment we have the 0 hardcoded. */
729 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
730 "Amount of data read.", "/Devices/SCSI0/%d/ReadBytes", pDrvIns->iInstance);
731 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
732 "Amount of data written.", "/Devices/SCSI0/%d/WrittenBytes", pDrvIns->iInstance);
733
734 pThis->StatIoDepth = 0;
735
736 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
737 "Number of active tasks.", "/Devices/SCSI0/%d/IoDepth", pDrvIns->iInstance);
738
739 if (!pThis->pDrvBlockAsync)
740 {
741 /* Create request queue. */
742 rc = RTReqCreateQueue(&pThis->pQueueRequests);
743 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create request queue rc=%Rrc\n"), rc);
744 /* Create I/O thread. */
745 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pAsyncIOThread, pThis, drvscsiAsyncIOLoop,
746 drvscsiAsyncIOLoopWakeup, 0, RTTHREADTYPE_IO, "SCSI async IO");
747 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create async I/O thread rc=%Rrc\n"), rc);
748 }
749
750 return VINF_SUCCESS;
751}
752
753/**
754 * SCSI driver registration record.
755 */
756const PDMDRVREG g_DrvSCSI =
757{
758 /* u32Version */
759 PDM_DRVREG_VERSION,
760 /* szName */
761 "SCSI",
762 /* szRCMod */
763 "",
764 /* szR0Mod */
765 "",
766 /* pszDescription */
767 "Generic SCSI driver.",
768 /* fFlags */
769 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
770 /* fClass. */
771 PDM_DRVREG_CLASS_SCSI,
772 /* cMaxInstances */
773 ~0,
774 /* cbInstance */
775 sizeof(DRVSCSI),
776 /* pfnConstruct */
777 drvscsiConstruct,
778 /* pfnDestruct */
779 drvscsiDestruct,
780 /* pfnRelocate */
781 NULL,
782 /* pfnIOCtl */
783 NULL,
784 /* pfnPowerOn */
785 NULL,
786 /* pfnReset */
787 drvscsiReset,
788 /* pfnSuspend */
789 drvscsiSuspend,
790 /* pfnResume */
791 NULL,
792 /* pfnAttach */
793 NULL,
794 /* pfnDetach */
795 NULL,
796 /* pfnPowerOff */
797 drvscsiPowerOff,
798 /* pfnSoftReset */
799 NULL,
800 /* u32EndVersion */
801 PDM_DRVREG_VERSION
802};
803
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