VirtualBox

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

Last change on this file since 39498 was 39498, checked in by vboxsync, 13 years ago

RTReq refactoring.

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