VirtualBox

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

Last change on this file since 37324 was 35353, checked in by vboxsync, 14 years ago

Move the misc files the in src/VBox/Devices/ directory into a build/ subdirectory, changing their names to match the target module.

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