VirtualBox

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

Last change on this file since 28264 was 28258, checked in by vboxsync, 15 years ago

PDM critsects for drivers. Fixed critsect cleanup in failure path. Started on new transmit locking scheme (required for intnet buffer serialization).

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

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