VirtualBox

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

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

async I/O: Add async flush method

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.0 KB
Line 
1/* $Id: DrvSCSI.cpp 28383 2010-04-15 18:07:21Z 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 /* async 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 rc = pThis->pDrvBlockAsync->pfnStartFlush(pThis->pDrvBlockAsync, hVScsiIoReq);
237 if (RT_FAILURE(rc) && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
238 AssertMsgFailed(("%s: Failed to flush data %Rrc\n", __FUNCTION__, rc));
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 break;
276 }
277 default:
278 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
279 }
280
281 if (rc == VINF_VD_ASYNC_IO_FINISHED)
282 {
283 if (enmTxDir == VSCSIIOREQTXDIR_READ)
284 pThis->pLed->Actual.s.fReading = 0;
285 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
286 pThis->pLed->Actual.s.fWriting = 0;
287 else
288 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
289
290 ASMAtomicDecU32(&pThis->StatIoDepth);
291 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS);
292 }
293 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
294 rc = VINF_SUCCESS;
295 else if (RT_FAILURE(rc))
296 {
297 if (enmTxDir == VSCSIIOREQTXDIR_READ)
298 pThis->pLed->Actual.s.fReading = 0;
299 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
300 pThis->pLed->Actual.s.fWriting = 0;
301 else
302 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
303
304 ASMAtomicDecU32(&pThis->StatIoDepth);
305 VSCSIIoReqCompleted(hVScsiIoReq, rc);
306 }
307 else
308 AssertMsgFailed(("Invalid return code rc=%Rrc\n", rc));
309 }
310 else
311 {
312 /* I/O thread. */
313 rc = RTReqCallEx(pThis->pQueueRequests, NULL, 0, RTREQFLAGS_NO_WAIT,
314 (PFNRT)drvscsiProcessRequestOne, 2, pThis, hVScsiIoReq);
315 }
316
317 return rc;
318}
319
320static void drvscsiVScsiReqCompleted(VSCSIDEVICE hVScsiDevice, void *pVScsiDeviceUser,
321 void *pVScsiReqUser, int rcReq)
322{
323 PDRVSCSI pThis = (PDRVSCSI)pVScsiDeviceUser;
324
325 pThis->pDevScsiPort->pfnSCSIRequestCompleted(pThis->pDevScsiPort, (PPDMSCSIREQUEST)pVScsiReqUser,
326 rcReq);
327}
328
329/**
330 * Dummy request function used by drvscsiReset to wait for all pending requests
331 * to complete prior to the device reset.
332 *
333 * @param pThis Pointer to the instace data.
334 * @returns VINF_SUCCESS.
335 */
336static int drvscsiAsyncIOLoopSyncCallback(PDRVSCSI pThis)
337{
338 if (pThis->fDummySignal)
339 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
340 return VINF_SUCCESS;
341}
342
343/**
344 * Request function to wakeup the thread.
345 *
346 * @param pThis Pointer to the instace data.
347 * @returns VWRN_STATE_CHANGED.
348 */
349static int drvscsiAsyncIOLoopWakeupFunc(PDRVSCSI pThis)
350{
351 if (pThis->fDummySignal)
352 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
353 return VWRN_STATE_CHANGED;
354}
355
356/**
357 * The thread function which processes the requests asynchronously.
358 *
359 * @returns VBox status code.
360 * @param pDrvIns Pointer to the driver instance data.
361 * @param pThread Pointer to the thread instance data.
362 */
363static int drvscsiAsyncIOLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
364{
365 int rc = VINF_SUCCESS;
366 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
367
368 LogFlowFunc(("Entering async IO loop.\n"));
369
370 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
371 return VINF_SUCCESS;
372
373 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
374 {
375 rc = RTReqProcess(pThis->pQueueRequests, RT_INDEFINITE_WAIT);
376 AssertMsg(rc == VWRN_STATE_CHANGED, ("Left RTReqProcess and error code is not VWRN_STATE_CHANGED rc=%Rrc\n", rc));
377 }
378
379 return VINF_SUCCESS;
380}
381
382/**
383 * Deals with any pending dummy request
384 *
385 * @returns true if no pending dummy request, false if still pending.
386 * @param pThis The instance data.
387 * @param cMillies The number of milliseconds to wait for any
388 * pending request to finish.
389 */
390static bool drvscsiAsyncIOLoopNoPendingDummy(PDRVSCSI pThis, uint32_t cMillies)
391{
392 if (!pThis->pPendingDummyReq)
393 return true;
394 int rc = RTReqWait(pThis->pPendingDummyReq, cMillies);
395 if (RT_FAILURE(rc))
396 return false;
397 RTReqFree(pThis->pPendingDummyReq);
398 pThis->pPendingDummyReq = NULL;
399 return true;
400}
401
402static int drvscsiAsyncIOLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
403{
404 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
405 PRTREQ pReq;
406 int rc;
407
408 AssertMsgReturn(pThis->pQueueRequests, ("pQueueRequests is NULL\n"), VERR_INVALID_STATE);
409
410 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 10000 /* 10 sec */))
411 {
412 LogRel(("drvscsiAsyncIOLoopWakeup#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
413 return VERR_TIMEOUT;
414 }
415
416 rc = RTReqCall(pThis->pQueueRequests, &pReq, 10000 /* 10 sec. */, (PFNRT)drvscsiAsyncIOLoopWakeupFunc, 1, pThis);
417 if (RT_SUCCESS(rc))
418 RTReqFree(pReq);
419 else
420 {
421 pThis->pPendingDummyReq = pReq;
422 LogRel(("drvscsiAsyncIOLoopWakeup#%u: %Rrc pReq=%p\n", pDrvIns->iInstance, rc, pReq));
423 }
424
425 return rc;
426}
427
428/* -=-=-=-=- ISCSIConnector -=-=-=-=- */
429
430/** @copydoc PDMISCSICONNECTOR::pfnSCSIRequestSend. */
431static DECLCALLBACK(int) drvscsiRequestSend(PPDMISCSICONNECTOR pInterface, PPDMSCSIREQUEST pSCSIRequest)
432{
433 int rc;
434 PDRVSCSI pThis = PDMISCSICONNECTOR_2_DRVSCSI(pInterface);
435 VSCSIREQ hVScsiReq;
436
437 rc = VSCSIDeviceReqCreate(pThis->hVScsiDevice, &hVScsiReq,
438 pSCSIRequest->uLogicalUnit,
439 pSCSIRequest->pbCDB,
440 pSCSIRequest->cbCDB,
441 pSCSIRequest->cbScatterGather,
442 pSCSIRequest->cScatterGatherEntries,
443 pSCSIRequest->paScatterGatherHead,
444 pSCSIRequest->pbSenseBuffer,
445 pSCSIRequest->cbSenseBuffer,
446 pSCSIRequest);
447 if (RT_FAILURE(rc))
448 return rc;
449
450 rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, hVScsiReq);
451
452 return rc;
453}
454
455/* -=-=-=-=- IBase -=-=-=-=- */
456
457/**
458 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
459 */
460static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
461{
462 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
463 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
464
465 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
466 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector);
467 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pThis->IPort);
468 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKASYNCPORT, &pThis->IPortAsync);
469 return NULL;
470}
471
472/**
473 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
474 *
475 * @param pDrvIns The driver instance.
476 * @param pfnAsyncNotify The async callback.
477 */
478static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
479{
480 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
481
482 if (!pThis->pQueueRequests)
483 return;
484
485 ASMAtomicWriteBool(&pThis->fDummySignal, true);
486 if (drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
487 {
488 if (!RTReqIsBusy(pThis->pQueueRequests))
489 {
490 ASMAtomicWriteBool(&pThis->fDummySignal, false);
491 return;
492 }
493
494 PRTREQ pReq;
495 int rc = RTReqCall(pThis->pQueueRequests, &pReq, 0 /*ms*/, (PFNRT)drvscsiAsyncIOLoopSyncCallback, 1, pThis);
496 if (RT_SUCCESS(rc))
497 {
498 ASMAtomicWriteBool(&pThis->fDummySignal, false);
499 RTReqFree(pReq);
500 return;
501 }
502
503 pThis->pPendingDummyReq = pReq;
504 }
505 PDMDrvHlpSetAsyncNotification(pDrvIns, pfnAsyncNotify);
506}
507
508/**
509 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
510 *
511 * @returns true if we've quiesced, false if we're still working.
512 * @param pDrvIns The driver instance.
513 */
514static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
515{
516 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
517
518 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
519 return false;
520 ASMAtomicWriteBool(&pThis->fDummySignal, false);
521 PDMR3ThreadSuspend(pThis->pAsyncIOThread);
522 return true;
523}
524
525/**
526 * @copydoc FNPDMDRVPOWEROFF
527 */
528static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
529{
530 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
531}
532
533/**
534 * @copydoc FNPDMDRVSUSPEND
535 */
536static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
537{
538 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
539}
540
541/**
542 * Callback employed by drvscsiReset.
543 *
544 * @returns true if we've quiesced, false if we're still working.
545 * @param pDrvIns The driver instance.
546 */
547static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
548{
549 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
550
551 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
552 return false;
553 ASMAtomicWriteBool(&pThis->fDummySignal, false);
554 return true;
555}
556
557/**
558 * @copydoc FNPDMDRVRESET
559 */
560static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
561{
562 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
563}
564
565/**
566 * Destruct a driver instance.
567 *
568 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
569 * resources can be freed correctly.
570 *
571 * @param pDrvIns The driver instance data.
572 */
573static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
574{
575 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
576 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
577
578 if (pThis->pQueueRequests)
579 {
580 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 100 /*ms*/))
581 LogRel(("drvscsiDestruct#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
582
583 int rc = RTReqDestroyQueue(pThis->pQueueRequests);
584 AssertMsgRC(rc, ("Failed to destroy queue rc=%Rrc\n", rc));
585 }
586
587 /* Free the VSCSI device and LUN handle. */
588 VSCSILUN hVScsiLun;
589 int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
590 AssertRC(rc);
591
592 Assert(hVScsiLun == pThis->hVScsiLun);
593 rc = VSCSILunDestroy(hVScsiLun);
594 AssertRC(rc);
595 rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
596 AssertRC(rc);
597}
598
599/**
600 * Construct a block driver instance.
601 *
602 * @copydoc FNPDMDRVCONSTRUCT
603 */
604static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
605{
606 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
607 LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
608 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
609
610 /*
611 * Initialize the instance data.
612 */
613 pThis->pDrvIns = pDrvIns;
614 pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsiRequestSend;
615
616 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
617
618 pThis->IPortAsync.pfnTransferCompleteNotify = drvscsiTransferCompleteNotify;
619
620 /*
621 * Try attach driver below and query it's block interface.
622 */
623 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
624 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
625
626 /*
627 * Query the block and blockbios interfaces.
628 */
629 pThis->pDrvBlock = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCK);
630 if (!pThis->pDrvBlock)
631 {
632 AssertMsgFailed(("Configuration error: No block interface!\n"));
633 return VERR_PDM_MISSING_INTERFACE;
634 }
635 pThis->pDrvBlockBios = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKBIOS);
636 if (!pThis->pDrvBlockBios)
637 {
638 AssertMsgFailed(("Configuration error: No block BIOS interface!\n"));
639 return VERR_PDM_MISSING_INTERFACE;
640 }
641
642 /* Query the SCSI port interface above. */
643 pThis->pDevScsiPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISCSIPORT);
644 AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
645
646 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
647
648 /* Query the optional LED interface above. */
649 pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
650 if (pThis->pLedPort != NULL)
651 {
652 /* Get The Led. */
653 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
654 if (RT_FAILURE(rc))
655 pThis->pLed = &pThis->Led;
656 }
657 else
658 pThis->pLed = &pThis->Led;
659
660 /* Try to get the optional async block interface. */
661 pThis->pDrvBlockAsync = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKASYNC);
662
663 PDMBLOCKTYPE enmType = pThis->pDrvBlock->pfnGetType(pThis->pDrvBlock);
664 if (enmType != PDMBLOCKTYPE_HARD_DISK)
665 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
666 N_("Only hard disks are currently supported as SCSI devices (enmType=%d)"),
667 enmType);
668
669 /* Create VSCSI device and LUN. */
670 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
671 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
672
673 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis);
674 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n"), rc);
675 rc = VSCSILunCreate(&pThis->hVScsiLun, VSCSILUNTYPE_SBC, &pThis->VScsiIoCallbacks,
676 pThis);
677 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n"), rc);
678 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
679 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
680
681 /* Create request queue. */
682 rc = RTReqCreateQueue(&pThis->pQueueRequests);
683 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create request queue rc=%Rrc\n"), rc);
684
685 /* Register statistics counter. */
686 /** @todo aeichner: Find a way to put the instance number of the attached
687 * controller device when we support more than one controller of the same type.
688 * At the moment we have the 0 hardcoded. */
689 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
690 "Amount of data read.", "/Devices/SCSI0/%d/ReadBytes", pDrvIns->iInstance);
691 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
692 "Amount of data written.", "/Devices/SCSI0/%d/WrittenBytes", pDrvIns->iInstance);
693
694 pThis->StatIoDepth = 0;
695
696 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
697 "Number of active tasks.", "/Devices/SCSI0/%d/IoDepth", pDrvIns->iInstance);
698
699
700 /* Create I/O thread. */
701 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pAsyncIOThread, pThis, drvscsiAsyncIOLoop,
702 drvscsiAsyncIOLoopWakeup, 0, RTTHREADTYPE_IO, "SCSI async IO");
703 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create async I/O thread rc=%Rrc\n"), rc);
704
705 return VINF_SUCCESS;
706}
707
708/**
709 * SCSI driver registration record.
710 */
711const PDMDRVREG g_DrvSCSI =
712{
713 /* u32Version */
714 PDM_DRVREG_VERSION,
715 /* szName */
716 "SCSI",
717 /* szRCMod */
718 "",
719 /* szR0Mod */
720 "",
721 /* pszDescription */
722 "Generic SCSI driver.",
723 /* fFlags */
724 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
725 /* fClass. */
726 PDM_DRVREG_CLASS_SCSI,
727 /* cMaxInstances */
728 ~0,
729 /* cbInstance */
730 sizeof(DRVSCSI),
731 /* pfnConstruct */
732 drvscsiConstruct,
733 /* pfnDestruct */
734 drvscsiDestruct,
735 /* pfnRelocate */
736 NULL,
737 /* pfnIOCtl */
738 NULL,
739 /* pfnPowerOn */
740 NULL,
741 /* pfnReset */
742 drvscsiReset,
743 /* pfnSuspend */
744 drvscsiSuspend,
745 /* pfnResume */
746 NULL,
747 /* pfnAttach */
748 NULL,
749 /* pfnDetach */
750 NULL,
751 /* pfnPowerOff */
752 drvscsiPowerOff,
753 /* pfnSoftReset */
754 NULL,
755 /* u32EndVersion */
756 PDM_DRVREG_VERSION
757};
758
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