VirtualBox

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

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

VBoxHDD: Async I/O for flat images are back

  • 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 27977 2010-04-04 19:21:59Z 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 PCPDMDATASEG 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 PCPDMDATASEG 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 (PPDMDATASEG)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 (PPDMDATASEG)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 = PDMDrvHlpPDMThreadCreate(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.

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