VirtualBox

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

Last change on this file since 59248 was 59248, checked in by vboxsync, 9 years ago

Storage: Get rid of the block driver and merge the the little extra functionality it had into the VD driver. Enables us to get rid of PDMIBLOCK which is basically a subset of PDMIMEDIA and makes changes to the latter interface tedious because it had to be replicated in the former. (bugref:4114)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.3 KB
Line 
1/* $Id: DrvSCSI.cpp 59248 2016-01-04 14:13:22Z vboxsync $ */
2/** @file
3 * VBox storage drivers: Generic SCSI command parser and execution driver
4 */
5
6/*
7 * Copyright (C) 2006-2015 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/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22//#define DEBUG
23#define LOG_GROUP LOG_GROUP_DRV_SCSI
24#include <VBox/vmm/pdmdrv.h>
25#include <VBox/vmm/pdmifs.h>
26#include <VBox/vmm/pdmthread.h>
27#include <VBox/vscsi.h>
28#include <VBox/scsi.h>
29#include <iprt/asm.h>
30#include <iprt/assert.h>
31#include <iprt/mem.h>
32#include <iprt/req.h>
33#include <iprt/semaphore.h>
34#include <iprt/string.h>
35#include <iprt/uuid.h>
36
37#include "VBoxDD.h"
38
39/** The maximum number of release log entries per device. */
40#define MAX_LOG_REL_ERRORS 1024
41
42/**
43 * SCSI driver instance data.
44 *
45 * @implements PDMISCSICONNECTOR
46 * @implements PDMIMEDIAASYNCPORT
47 * @implements PDMIMOUNTNOTIFY
48 */
49typedef struct DRVSCSI
50{
51 /** Pointer driver instance. */
52 PPDMDRVINS pDrvIns;
53
54 /** Pointer to the attached driver's base interface. */
55 PPDMIBASE pDrvBase;
56 /** Pointer to the attached driver's block interface. */
57 PPDMIMEDIA pDrvMedia;
58 /** Pointer to the attached driver's async block interface. */
59 PPDMIMEDIAASYNC pDrvMediaAsync;
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 media port interface. */
69 PDMIMEDIAPORT IPort;
70 /** The optional media async port interface. */
71 PDMIMEDIAASYNCPORT IPortAsync;
72 /** The mount notify interface. */
73 PDMIMOUNTNOTIFY IMountNotify;
74 /** Fallback status LED state for this drive.
75 * This is used in case the device doesn't has a LED interface. */
76 PDMLED Led;
77 /** Pointer to the status LED for this drive. */
78 PPDMLED pLed;
79
80 /** VSCSI device handle. */
81 VSCSIDEVICE hVScsiDevice;
82 /** VSCSI LUN handle. */
83 VSCSILUN hVScsiLun;
84 /** I/O callbacks. */
85 VSCSILUNIOCALLBACKS VScsiIoCallbacks;
86
87 /** The dedicated I/O thread for the non async approach. */
88 PPDMTHREAD pAsyncIOThread;
89 /** Queue for passing the requests to the thread. */
90 RTREQQUEUE hQueueRequests;
91 /** Request that we've left pending on wakeup or reset. */
92 PRTREQ pPendingDummyReq;
93 /** Indicates whether PDMDrvHlpAsyncNotificationCompleted should be called by
94 * any of the dummy functions. */
95 bool volatile fDummySignal;
96 /** Release statistics: number of bytes written. */
97 STAMCOUNTER StatBytesWritten;
98 /** Release statistics: number of bytes read. */
99 STAMCOUNTER StatBytesRead;
100 /** Release statistics: Current I/O depth. */
101 volatile uint32_t StatIoDepth;
102 /** Errors printed in the release log. */
103 unsigned cErrors;
104 /** Mark the drive as having a non-rotational medium (i.e. as a SSD). */
105 bool fNonRotational;
106 /** Medium is readonly */
107 bool fReadonly;
108} DRVSCSI, *PDRVSCSI;
109
110/** Converts a pointer to DRVSCSI::ISCSIConnector to a PDRVSCSI. */
111#define PDMISCSICONNECTOR_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, ISCSIConnector)) )
112/** Converts a pointer to DRVSCSI::IPortAsync to a PDRVSCSI. */
113#define PDMIMEDIAASYNCPORT_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IPortAsync)) )
114/** Converts a pointer to DRVSCSI::IMountNotify to PDRVSCSI. */
115#define PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IMountNotify)) )
116/** Converts a pointer to DRVSCSI::IPort to a PDRVSCSI. */
117#define PDMIMEDIAPORT_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IPort)) )
118
119static bool drvscsiIsRedoPossible(int rc)
120{
121 if ( rc == VERR_DISK_FULL
122 || rc == VERR_FILE_TOO_BIG
123 || rc == VERR_BROKEN_PIPE
124 || rc == VERR_NET_CONNECTION_REFUSED
125 || rc == VERR_VD_DEK_MISSING)
126 return true;
127
128 return false;
129}
130
131static int drvscsiProcessRequestOne(PDRVSCSI pThis, VSCSIIOREQ hVScsiIoReq)
132{
133 int rc = VINF_SUCCESS;
134 VSCSIIOREQTXDIR enmTxDir;
135
136 enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
137
138 switch (enmTxDir)
139 {
140 case VSCSIIOREQTXDIR_FLUSH:
141 {
142 rc = pThis->pDrvMedia->pfnFlush(pThis->pDrvMedia);
143 if ( RT_FAILURE(rc)
144 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
145 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
146 pThis->pDrvIns->iInstance, rc));
147 break;
148 }
149 case VSCSIIOREQTXDIR_READ:
150 case VSCSIIOREQTXDIR_WRITE:
151 {
152 uint64_t uOffset = 0;
153 size_t cbTransfer = 0;
154 size_t cbSeg = 0;
155 PCRTSGSEG paSeg = NULL;
156 unsigned cSeg = 0;
157
158 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg,
159 &paSeg);
160 AssertRC(rc);
161
162 while (cbTransfer && cSeg)
163 {
164 size_t cbProcess = (cbTransfer < paSeg->cbSeg) ? cbTransfer : paSeg->cbSeg;
165
166 Log(("%s: uOffset=%llu cbProcess=%u\n", __FUNCTION__, uOffset, cbProcess));
167
168 if (enmTxDir == VSCSIIOREQTXDIR_READ)
169 {
170 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
171 rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, uOffset,
172 paSeg->pvSeg, cbProcess);
173 pThis->pLed->Actual.s.fReading = 0;
174 if (RT_FAILURE(rc))
175 break;
176 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbProcess);
177 }
178 else
179 {
180 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
181 rc = pThis->pDrvMedia->pfnWrite(pThis->pDrvMedia, uOffset,
182 paSeg->pvSeg, cbProcess);
183 pThis->pLed->Actual.s.fWriting = 0;
184 if (RT_FAILURE(rc))
185 break;
186 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbProcess);
187 }
188
189 /* Go to the next entry. */
190 uOffset += cbProcess;
191 cbTransfer -= cbProcess;
192 paSeg++;
193 cSeg--;
194 }
195
196 if ( RT_FAILURE(rc)
197 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
198 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
199 pThis->pDrvIns->iInstance,
200 enmTxDir == VSCSIIOREQTXDIR_READ
201 ? "Read"
202 : "Write",
203 uOffset,
204 cbTransfer, rc));
205
206 break;
207 }
208 case VSCSIIOREQTXDIR_UNMAP:
209 {
210 PCRTRANGE paRanges;
211 unsigned cRanges;
212
213 rc = VSCSIIoReqUnmapParamsGet(hVScsiIoReq, &paRanges, &cRanges);
214 AssertRC(rc);
215
216 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
217 rc = pThis->pDrvMedia->pfnDiscard(pThis->pDrvMedia, paRanges, cRanges);
218 pThis->pLed->Actual.s.fWriting = 0;
219
220 if ( RT_FAILURE(rc)
221 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
222 LogRel(("SCSI#%u: Unmap returned rc=%Rrc\n",
223 pThis->pDrvIns->iInstance, rc));
224
225 break;
226 }
227 default:
228 AssertMsgFailed(("Invalid transfer direction %d\n", enmTxDir));
229 }
230
231 if (RT_SUCCESS(rc))
232 VSCSIIoReqCompleted(hVScsiIoReq, rc, false /* fRedoPossible */);
233 else
234 VSCSIIoReqCompleted(hVScsiIoReq, rc, drvscsiIsRedoPossible(rc));
235
236 return VINF_SUCCESS;
237}
238
239static DECLCALLBACK(int) drvscsiGetSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint64_t *pcbSize)
240{
241 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
242
243 *pcbSize = pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
244
245 return VINF_SUCCESS;
246}
247
248
249static DECLCALLBACK(int) drvscsiGetSectorSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint32_t *pcbSectorSize)
250{
251 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
252
253 *pcbSectorSize = pThis->pDrvMedia->pfnGetSectorSize(pThis->pDrvMedia);
254
255 return VINF_SUCCESS;
256}
257static DECLCALLBACK(int) drvscsiSetLock(VSCSILUN hVScsiLun, void *pvScsiLunUser, bool fLocked)
258{
259 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
260
261 if (fLocked)
262 pThis->pDrvMount->pfnLock(pThis->pDrvMount);
263 else
264 pThis->pDrvMount->pfnUnlock(pThis->pDrvMount);
265
266 return VINF_SUCCESS;
267}
268
269static DECLCALLBACK(int) drvscsiTransferCompleteNotify(PPDMIMEDIAASYNCPORT pInterface, void *pvUser, int rc)
270{
271 PDRVSCSI pThis = PDMIMEDIAASYNCPORT_2_DRVSCSI(pInterface);
272 VSCSIIOREQ hVScsiIoReq = (VSCSIIOREQ)pvUser;
273 VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
274
275 LogFlowFunc(("Request hVScsiIoReq=%#p completed\n", hVScsiIoReq));
276
277 if (enmTxDir == VSCSIIOREQTXDIR_READ)
278 pThis->pLed->Actual.s.fReading = 0;
279 else if ( enmTxDir == VSCSIIOREQTXDIR_WRITE
280 || enmTxDir == VSCSIIOREQTXDIR_UNMAP)
281 pThis->pLed->Actual.s.fWriting = 0;
282 else
283 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
284
285 if (RT_SUCCESS(rc))
286 VSCSIIoReqCompleted(hVScsiIoReq, rc, false /* fRedoPossible */);
287 else
288 {
289 pThis->cErrors++;
290 if (pThis->cErrors < MAX_LOG_REL_ERRORS)
291 {
292 if (enmTxDir == VSCSIIOREQTXDIR_FLUSH)
293 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
294 pThis->pDrvIns->iInstance, rc));
295 else if (enmTxDir == VSCSIIOREQTXDIR_UNMAP)
296 LogRel(("SCSI#%u: Unmap returned rc=%Rrc\n",
297 pThis->pDrvIns->iInstance, rc));
298 else
299 {
300 uint64_t uOffset = 0;
301 size_t cbTransfer = 0;
302 size_t cbSeg = 0;
303 PCRTSGSEG paSeg = NULL;
304 unsigned cSeg = 0;
305
306 VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
307 &cSeg, &cbSeg, &paSeg);
308
309 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
310 pThis->pDrvIns->iInstance,
311 enmTxDir == VSCSIIOREQTXDIR_READ
312 ? "Read"
313 : "Write",
314 uOffset,
315 cbTransfer, rc));
316 }
317 }
318
319 VSCSIIoReqCompleted(hVScsiIoReq, rc, drvscsiIsRedoPossible(rc));
320 }
321
322 return VINF_SUCCESS;
323}
324
325static DECLCALLBACK(int) drvscsiReqTransferEnqueue(VSCSILUN hVScsiLun,
326 void *pvScsiLunUser,
327 VSCSIIOREQ hVScsiIoReq)
328{
329 int rc = VINF_SUCCESS;
330 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
331
332 if (pThis->pDrvMediaAsync)
333 {
334 /* async I/O path. */
335 VSCSIIOREQTXDIR enmTxDir;
336
337 LogFlowFunc(("Enqueuing hVScsiIoReq=%#p\n", hVScsiIoReq));
338
339 enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
340
341 switch (enmTxDir)
342 {
343 case VSCSIIOREQTXDIR_FLUSH:
344 {
345 rc = pThis->pDrvMediaAsync->pfnStartFlush(pThis->pDrvMediaAsync, hVScsiIoReq);
346 if ( RT_FAILURE(rc)
347 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS
348 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
349 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
350 pThis->pDrvIns->iInstance, rc));
351 break;
352 }
353 case VSCSIIOREQTXDIR_UNMAP:
354 {
355 PCRTRANGE paRanges;
356 unsigned cRanges;
357
358 rc = VSCSIIoReqUnmapParamsGet(hVScsiIoReq, &paRanges, &cRanges);
359 AssertRC(rc);
360
361 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
362 rc = pThis->pDrvMediaAsync->pfnStartDiscard(pThis->pDrvMediaAsync, paRanges, cRanges, hVScsiIoReq);
363 if ( RT_FAILURE(rc)
364 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS
365 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
366 LogRel(("SCSI#%u: Discard returned rc=%Rrc\n",
367 pThis->pDrvIns->iInstance, rc));
368 break;
369 }
370 case VSCSIIOREQTXDIR_READ:
371 case VSCSIIOREQTXDIR_WRITE:
372 {
373 uint64_t uOffset = 0;
374 size_t cbTransfer = 0;
375 size_t cbSeg = 0;
376 PCRTSGSEG paSeg = NULL;
377 unsigned cSeg = 0;
378
379 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
380 &cSeg, &cbSeg, &paSeg);
381 AssertRC(rc);
382
383 if (enmTxDir == VSCSIIOREQTXDIR_READ)
384 {
385 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
386 rc = pThis->pDrvMediaAsync->pfnStartRead(pThis->pDrvMediaAsync, uOffset,
387 paSeg, cSeg, cbTransfer,
388 hVScsiIoReq);
389 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbTransfer);
390 }
391 else
392 {
393 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
394 rc = pThis->pDrvMediaAsync->pfnStartWrite(pThis->pDrvMediaAsync, uOffset,
395 paSeg, cSeg, cbTransfer,
396 hVScsiIoReq);
397 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbTransfer);
398 }
399
400 if ( RT_FAILURE(rc)
401 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS
402 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
403 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
404 pThis->pDrvIns->iInstance,
405 enmTxDir == VSCSIIOREQTXDIR_READ
406 ? "Read"
407 : "Write",
408 uOffset,
409 cbTransfer, rc));
410 break;
411 }
412 default:
413 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
414 }
415
416 if (rc == VINF_VD_ASYNC_IO_FINISHED)
417 {
418 if (enmTxDir == VSCSIIOREQTXDIR_READ)
419 pThis->pLed->Actual.s.fReading = 0;
420 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
421 pThis->pLed->Actual.s.fWriting = 0;
422 else
423 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
424
425 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS, false);
426 rc = VINF_SUCCESS;
427 }
428 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
429 rc = VINF_SUCCESS;
430 else if (RT_FAILURE(rc))
431 {
432 if (enmTxDir == VSCSIIOREQTXDIR_READ)
433 pThis->pLed->Actual.s.fReading = 0;
434 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
435 pThis->pLed->Actual.s.fWriting = 0;
436 else
437 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
438
439 VSCSIIoReqCompleted(hVScsiIoReq, rc, drvscsiIsRedoPossible(rc));
440 rc = VINF_SUCCESS;
441 }
442 else
443 AssertMsgFailed(("Invalid return code rc=%Rrc\n", rc));
444 }
445 else
446 {
447 /* I/O thread. */
448 rc = RTReqQueueCallEx(pThis->hQueueRequests, NULL, 0, RTREQFLAGS_NO_WAIT,
449 (PFNRT)drvscsiProcessRequestOne, 2, pThis, hVScsiIoReq);
450 }
451
452 return rc;
453}
454
455static DECLCALLBACK(int) drvscsiGetFeatureFlags(VSCSILUN hVScsiLun,
456 void *pvScsiLunUser,
457 uint64_t *pfFeatures)
458{
459 int rc = VINF_SUCCESS;
460 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
461
462 *pfFeatures = 0;
463
464 if ( pThis->pDrvMedia->pfnDiscard
465 || ( pThis->pDrvMediaAsync
466 && pThis->pDrvMediaAsync->pfnStartDiscard))
467 *pfFeatures |= VSCSI_LUN_FEATURE_UNMAP;
468
469 if (pThis->fNonRotational)
470 *pfFeatures |= VSCSI_LUN_FEATURE_NON_ROTATIONAL;
471
472 if (pThis->fReadonly)
473 *pfFeatures |= VSCSI_LUN_FEATURE_READONLY;
474
475 return VINF_SUCCESS;
476}
477
478static DECLCALLBACK(void) drvscsiVScsiReqCompleted(VSCSIDEVICE hVScsiDevice, void *pVScsiDeviceUser,
479 void *pVScsiReqUser, int rcScsiCode, bool fRedoPossible,
480 int rcReq)
481{
482 PDRVSCSI pThis = (PDRVSCSI)pVScsiDeviceUser;
483
484 ASMAtomicDecU32(&pThis->StatIoDepth);
485
486 pThis->pDevScsiPort->pfnSCSIRequestCompleted(pThis->pDevScsiPort, (PPDMSCSIREQUEST)pVScsiReqUser,
487 rcScsiCode, fRedoPossible, rcReq);
488
489 if (RT_UNLIKELY(pThis->fDummySignal) && !pThis->StatIoDepth)
490 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
491}
492
493/**
494 * Dummy request function used by drvscsiReset to wait for all pending requests
495 * to complete prior to the device reset.
496 *
497 * @param pThis Pointer to the instance data.
498 * @returns VINF_SUCCESS.
499 */
500static int drvscsiAsyncIOLoopSyncCallback(PDRVSCSI pThis)
501{
502 if (pThis->fDummySignal)
503 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
504 return VINF_SUCCESS;
505}
506
507/**
508 * Request function to wakeup the thread.
509 *
510 * @param pThis Pointer to the instance data.
511 * @returns VWRN_STATE_CHANGED.
512 */
513static int drvscsiAsyncIOLoopWakeupFunc(PDRVSCSI pThis)
514{
515 if (pThis->fDummySignal)
516 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
517 return VWRN_STATE_CHANGED;
518}
519
520/**
521 * The thread function which processes the requests asynchronously.
522 *
523 * @returns VBox status code.
524 * @param pDrvIns Pointer to the driver instance data.
525 * @param pThread Pointer to the thread instance data.
526 */
527static DECLCALLBACK(int) drvscsiAsyncIOLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
528{
529 int rc = VINF_SUCCESS;
530 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
531
532 LogFlowFunc(("Entering async IO loop.\n"));
533
534 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
535 return VINF_SUCCESS;
536
537 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
538 {
539 rc = RTReqQueueProcess(pThis->hQueueRequests, RT_INDEFINITE_WAIT);
540 AssertMsg(rc == VWRN_STATE_CHANGED, ("Left RTReqProcess and error code is not VWRN_STATE_CHANGED rc=%Rrc\n", rc));
541 }
542
543 return VINF_SUCCESS;
544}
545
546/**
547 * Deals with any pending dummy request
548 *
549 * @returns true if no pending dummy request, false if still pending.
550 * @param pThis The instance data.
551 * @param cMillies The number of milliseconds to wait for any
552 * pending request to finish.
553 */
554static bool drvscsiAsyncIOLoopNoPendingDummy(PDRVSCSI pThis, uint32_t cMillies)
555{
556 if (!pThis->pPendingDummyReq)
557 return true;
558 int rc = RTReqWait(pThis->pPendingDummyReq, cMillies);
559 if (RT_FAILURE(rc))
560 return false;
561 RTReqRelease(pThis->pPendingDummyReq);
562 pThis->pPendingDummyReq = NULL;
563 return true;
564}
565
566static DECLCALLBACK(int) drvscsiAsyncIOLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
567{
568 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
569 PRTREQ pReq;
570 int rc;
571
572 AssertMsgReturn(pThis->hQueueRequests != NIL_RTREQQUEUE, ("hQueueRequests is NULL\n"), VERR_INVALID_STATE);
573
574 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 10000 /* 10 sec */))
575 {
576 LogRel(("drvscsiAsyncIOLoopWakeup#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
577 return VERR_TIMEOUT;
578 }
579
580 rc = RTReqQueueCall(pThis->hQueueRequests, &pReq, 10000 /* 10 sec. */, (PFNRT)drvscsiAsyncIOLoopWakeupFunc, 1, pThis);
581 if (RT_SUCCESS(rc))
582 RTReqRelease(pReq);
583 else
584 {
585 pThis->pPendingDummyReq = pReq;
586 LogRel(("drvscsiAsyncIOLoopWakeup#%u: %Rrc pReq=%p\n", pDrvIns->iInstance, rc, pReq));
587 }
588
589 return rc;
590}
591
592/* -=-=-=-=- ISCSIConnector -=-=-=-=- */
593
594#ifdef DEBUG
595/**
596 * Dumps a SCSI request structure for debugging purposes.
597 *
598 * @returns nothing.
599 * @param pRequest Pointer to the request to dump.
600 */
601static void drvscsiDumpScsiRequest(PPDMSCSIREQUEST pRequest)
602{
603 Log(("Dump for pRequest=%#p Command: %s\n", pRequest, SCSICmdText(pRequest->pbCDB[0])));
604 Log(("cbCDB=%u\n", pRequest->cbCDB));
605 for (uint32_t i = 0; i < pRequest->cbCDB; i++)
606 Log(("pbCDB[%u]=%#x\n", i, pRequest->pbCDB[i]));
607 Log(("cbScatterGather=%u\n", pRequest->cbScatterGather));
608 Log(("cScatterGatherEntries=%u\n", pRequest->cScatterGatherEntries));
609 /* Print all scatter gather entries. */
610 for (uint32_t i = 0; i < pRequest->cScatterGatherEntries; i++)
611 {
612 Log(("ScatterGatherEntry[%u].cbSeg=%u\n", i, pRequest->paScatterGatherHead[i].cbSeg));
613 Log(("ScatterGatherEntry[%u].pvSeg=%#p\n", i, pRequest->paScatterGatherHead[i].pvSeg));
614 }
615 Log(("pvUser=%#p\n", pRequest->pvUser));
616}
617#endif
618
619/** @interface_method_impl{PDMISCSICONNECTOR,pfnSCSIRequestSend} */
620static DECLCALLBACK(int) drvscsiRequestSend(PPDMISCSICONNECTOR pInterface, PPDMSCSIREQUEST pSCSIRequest)
621{
622 int rc;
623 PDRVSCSI pThis = PDMISCSICONNECTOR_2_DRVSCSI(pInterface);
624 VSCSIREQ hVScsiReq;
625
626#ifdef DEBUG
627 drvscsiDumpScsiRequest(pSCSIRequest);
628#endif
629
630 rc = VSCSIDeviceReqCreate(pThis->hVScsiDevice, &hVScsiReq,
631 pSCSIRequest->uLogicalUnit,
632 pSCSIRequest->pbCDB,
633 pSCSIRequest->cbCDB,
634 pSCSIRequest->cbScatterGather,
635 pSCSIRequest->cScatterGatherEntries,
636 pSCSIRequest->paScatterGatherHead,
637 pSCSIRequest->pbSenseBuffer,
638 pSCSIRequest->cbSenseBuffer,
639 pSCSIRequest);
640 if (RT_FAILURE(rc))
641 return rc;
642
643 ASMAtomicIncU32(&pThis->StatIoDepth);
644 rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, hVScsiReq);
645
646 return rc;
647}
648
649/** @interface_method_impl{PDMISCSICONNECTOR,pfnQueryLUNType} */
650static DECLCALLBACK(int) drvscsiQueryLUNType(PPDMISCSICONNECTOR pInterface, uint32_t iLun, PPDMSCSILUNTYPE pLunType)
651{
652 int rc;
653 PDRVSCSI pThis = PDMISCSICONNECTOR_2_DRVSCSI(pInterface);
654 VSCSILUNTYPE enmLunType;
655
656 rc = VSCSIDeviceLunQueryType(pThis->hVScsiDevice, iLun, &enmLunType);
657 if (RT_FAILURE(rc))
658 return rc;
659
660 switch (enmLunType)
661 {
662 case VSCSILUNTYPE_SBC: *pLunType = PDMSCSILUNTYPE_SBC; break;
663 case VSCSILUNTYPE_MMC: *pLunType = PDMSCSILUNTYPE_MMC; break;
664 case VSCSILUNTYPE_SSC: *pLunType = PDMSCSILUNTYPE_SSC; break;
665 default: *pLunType = PDMSCSILUNTYPE_INVALID;
666 }
667
668 return rc;
669}
670
671/* -=-=-=-=- IBase -=-=-=-=- */
672
673/**
674 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
675 */
676static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
677{
678 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
679 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
680
681 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, pThis->pDrvMount);
682 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
683 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector);
684 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pThis->IPort);
685 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pThis->IMountNotify);
686 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNCPORT, &pThis->IPortAsync);
687 return NULL;
688}
689
690static DECLCALLBACK(int) drvscsiQueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
691 uint32_t *piInstance, uint32_t *piLUN)
692{
693 PDRVSCSI pThis = PDMIMEDIAPORT_2_DRVSCSI(pInterface);
694
695 return pThis->pDevScsiPort->pfnQueryDeviceLocation(pThis->pDevScsiPort, ppcszController,
696 piInstance, piLUN);
697}
698
699/**
700 * Called when media is mounted.
701 *
702 * @param pInterface Pointer to the interface structure containing the called function pointer.
703 */
704static DECLCALLBACK(void) drvscsiMountNotify(PPDMIMOUNTNOTIFY pInterface)
705{
706 PDRVSCSI pThis = PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface);
707 LogFlowFunc(("mounting LUN#%p\n", pThis->hVScsiLun));
708
709 /* Ignore the call if we're called while being attached. */
710 if (!pThis->pDrvMedia)
711 return;
712
713 /* Let the LUN know that a medium was mounted. */
714 VSCSILunMountNotify(pThis->hVScsiLun);
715}
716
717/**
718 * Called when media is unmounted
719 *
720 * @param pInterface Pointer to the interface structure containing the called function pointer.
721 */
722static DECLCALLBACK(void) drvscsiUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
723{
724 PDRVSCSI pThis = PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface);
725 LogFlowFunc(("unmounting LUN#%p\n", pThis->hVScsiLun));
726
727 /* Let the LUN know that the medium was unmounted. */
728 VSCSILunUnmountNotify(pThis->hVScsiLun);
729}
730
731/**
732 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
733 *
734 * @param pDrvIns The driver instance.
735 * @param pfnAsyncNotify The async callback.
736 */
737static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
738{
739 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
740
741 if (!pThis->pDrvMediaAsync)
742 {
743 if (pThis->hQueueRequests != NIL_RTREQQUEUE)
744 return;
745
746 ASMAtomicWriteBool(&pThis->fDummySignal, true);
747 if (drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
748 {
749 if (!RTReqQueueIsBusy(pThis->hQueueRequests))
750 {
751 ASMAtomicWriteBool(&pThis->fDummySignal, false);
752 return;
753 }
754
755 PRTREQ pReq;
756 int rc = RTReqQueueCall(pThis->hQueueRequests, &pReq, 0 /*ms*/, (PFNRT)drvscsiAsyncIOLoopSyncCallback, 1, pThis);
757 if (RT_SUCCESS(rc))
758 {
759 ASMAtomicWriteBool(&pThis->fDummySignal, false);
760 RTReqRelease(pReq);
761 return;
762 }
763
764 pThis->pPendingDummyReq = pReq;
765 }
766 }
767 else
768 {
769 if (pThis->StatIoDepth > 0)
770 {
771 ASMAtomicWriteBool(&pThis->fDummySignal, true);
772 }
773 return;
774 }
775
776 PDMDrvHlpSetAsyncNotification(pDrvIns, pfnAsyncNotify);
777}
778
779/**
780 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
781 *
782 * @returns true if we've quiesced, false if we're still working.
783 * @param pDrvIns The driver instance.
784 */
785static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
786{
787 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
788
789 if (pThis->pDrvMediaAsync)
790 {
791 if (pThis->StatIoDepth > 0)
792 return false;
793 else
794 return true;
795 }
796 else
797 {
798 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
799 return false;
800 ASMAtomicWriteBool(&pThis->fDummySignal, false);
801 PDMR3ThreadSuspend(pThis->pAsyncIOThread);
802 return true;
803 }
804}
805
806/**
807 * @copydoc FNPDMDRVPOWEROFF
808 */
809static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
810{
811 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
812}
813
814/**
815 * @copydoc FNPDMDRVSUSPEND
816 */
817static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
818{
819 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
820}
821
822/**
823 * Callback employed by drvscsiReset.
824 *
825 * @returns true if we've quiesced, false if we're still working.
826 * @param pDrvIns The driver instance.
827 */
828static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
829{
830 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
831
832 if (pThis->pDrvMediaAsync)
833 {
834 if (pThis->StatIoDepth > 0)
835 return false;
836 else
837 return true;
838 }
839 else
840 {
841 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
842 return false;
843 ASMAtomicWriteBool(&pThis->fDummySignal, false);
844 return true;
845 }
846}
847
848/**
849 * @copydoc FNPDMDRVRESET
850 */
851static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
852{
853 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
854}
855
856/**
857 * Destruct a driver instance.
858 *
859 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
860 * resources can be freed correctly.
861 *
862 * @param pDrvIns The driver instance data.
863 */
864static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
865{
866 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
867 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
868
869 if (pThis->hQueueRequests != NIL_RTREQQUEUE)
870 {
871 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 100 /*ms*/))
872 LogRel(("drvscsiDestruct#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
873
874 int rc = RTReqQueueDestroy(pThis->hQueueRequests);
875 AssertMsgRC(rc, ("Failed to destroy queue rc=%Rrc\n", rc));
876 pThis->hQueueRequests = NIL_RTREQQUEUE;
877 }
878
879 /* Free the VSCSI device and LUN handle. */
880 if (pThis->hVScsiDevice)
881 {
882 VSCSILUN hVScsiLun;
883 int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
884 AssertRC(rc);
885
886 Assert(hVScsiLun == pThis->hVScsiLun);
887 rc = VSCSILunDestroy(hVScsiLun);
888 AssertRC(rc);
889 rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
890 AssertRC(rc);
891
892 pThis->hVScsiDevice = NULL;
893 pThis->hVScsiLun = NULL;
894 }
895}
896
897/**
898 * Construct a block driver instance.
899 *
900 * @copydoc FNPDMDRVCONSTRUCT
901 */
902static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
903{
904 int rc = VINF_SUCCESS;
905 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
906 LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
907 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
908
909 /*
910 * Initialize the instance data.
911 */
912 pThis->pDrvIns = pDrvIns;
913 pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsiRequestSend;
914 pThis->ISCSIConnector.pfnQueryLUNType = drvscsiQueryLUNType;
915
916 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
917
918 pThis->IMountNotify.pfnMountNotify = drvscsiMountNotify;
919 pThis->IMountNotify.pfnUnmountNotify = drvscsiUnmountNotify;
920 pThis->IPort.pfnQueryDeviceLocation = drvscsiQueryDeviceLocation;
921 pThis->IPortAsync.pfnTransferCompleteNotify = drvscsiTransferCompleteNotify;
922 pThis->hQueueRequests = NIL_RTREQQUEUE;
923
924 /* Query the SCSI port interface above. */
925 pThis->pDevScsiPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISCSIPORT);
926 AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
927
928 /* Query the optional LED interface above. */
929 pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
930 if (pThis->pLedPort != NULL)
931 {
932 /* Get The Led. */
933 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
934 if (RT_FAILURE(rc))
935 pThis->pLed = &pThis->Led;
936 }
937 else
938 pThis->pLed = &pThis->Led;
939
940 /*
941 * Validate and read configuration.
942 */
943 if (!CFGMR3AreValuesValid(pCfg, "NonRotationalMedium\0Readonly\0"))
944 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
945 N_("SCSI configuration error: unknown option specified"));
946
947 rc = CFGMR3QueryBoolDef(pCfg, "NonRotationalMedium", &pThis->fNonRotational, false);
948 if (RT_FAILURE(rc))
949 return PDMDRV_SET_ERROR(pDrvIns, rc,
950 N_("SCSI configuration error: failed to read \"NonRotationalMedium\" as boolean"));
951
952 rc = CFGMR3QueryBoolDef(pCfg, "Readonly", &pThis->fReadonly, false);
953 if (RT_FAILURE(rc))
954 return PDMDRV_SET_ERROR(pDrvIns, rc,
955 N_("SCSI configuration error: failed to read \"Readonly\" as boolean"));
956
957 /*
958 * Try attach driver below and query it's block interface.
959 */
960 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
961 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
962
963 /*
964 * Query the block and blockbios interfaces.
965 */
966 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIA);
967 if (!pThis->pDrvMedia)
968 {
969 AssertMsgFailed(("Configuration error: No block interface!\n"));
970 return VERR_PDM_MISSING_INTERFACE;
971 }
972
973 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
974
975 /* Try to get the optional async block interface. */
976 pThis->pDrvMediaAsync = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIAASYNC);
977
978 PDMMEDIATYPE enmType = pThis->pDrvMedia->pfnGetType(pThis->pDrvMedia);
979 VSCSILUNTYPE enmLunType;
980 switch (enmType)
981 {
982 case PDMMEDIATYPE_HARD_DISK:
983 enmLunType = VSCSILUNTYPE_SBC;
984 break;
985 case PDMMEDIATYPE_CDROM:
986 case PDMMEDIATYPE_DVD:
987 enmLunType = VSCSILUNTYPE_MMC;
988 break;
989 default:
990 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
991 N_("Only hard disks and CD/DVD-ROMs are currently supported as SCSI devices (enmType=%d)"),
992 enmType);
993 }
994 if ( ( enmType == PDMMEDIATYPE_DVD
995 || enmType == PDMMEDIATYPE_CDROM)
996 && !pThis->pDrvMount)
997 {
998 AssertMsgFailed(("Internal error: cdrom without a mountable interface\n"));
999 return VERR_INTERNAL_ERROR;
1000 }
1001
1002 /* Create VSCSI device and LUN. */
1003 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
1004 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSectorSize = drvscsiGetSectorSize;
1005 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
1006 pThis->VScsiIoCallbacks.pfnVScsiLunGetFeatureFlags = drvscsiGetFeatureFlags;
1007 pThis->VScsiIoCallbacks.pfnVScsiLunMediumSetLock = drvscsiSetLock;
1008
1009 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis);
1010 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n", rc), rc);
1011 rc = VSCSILunCreate(&pThis->hVScsiLun, enmLunType, &pThis->VScsiIoCallbacks,
1012 pThis);
1013 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n", rc), rc);
1014 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
1015 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
1016
1017 //@todo: This is a very hacky way of telling the LUN whether a medium was mounted.
1018 // The mount/unmount interface doesn't work in a very sensible manner!
1019 if (pThis->pDrvMount)
1020 {
1021 if (pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia))
1022 {
1023 rc = VINF_SUCCESS; VSCSILunMountNotify(pThis->hVScsiLun);
1024 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being mounted\n"), rc);
1025 }
1026 else
1027 {
1028 rc = VINF_SUCCESS; VSCSILunUnmountNotify(pThis->hVScsiLun);
1029 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being unmounted\n"), rc);
1030 }
1031 }
1032
1033 const char *pszCtrl = NULL;
1034 uint32_t iCtrlInstance = 0;
1035 uint32_t iCtrlLun = 0;
1036
1037 rc = pThis->pDevScsiPort->pfnQueryDeviceLocation(pThis->pDevScsiPort, &pszCtrl, &iCtrlInstance, &iCtrlLun);
1038 if (RT_SUCCESS(rc))
1039 {
1040 const char *pszCtrlId = strcmp(pszCtrl, "Msd") == 0 ? "USB"
1041 : strcmp(pszCtrl, "lsilogicsas") == 0 ? "SAS"
1042 : "SCSI";
1043 /* Register statistics counter. */
1044 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1045 "Amount of data read.", "/Devices/%s%u/%u/ReadBytes", pszCtrlId, iCtrlInstance, iCtrlLun);
1046 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1047 "Amount of data written.", "/Devices/%s%u/%u/WrittenBytes", pszCtrlId, iCtrlInstance, iCtrlLun);
1048 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
1049 "Number of active tasks.", "/Devices/%s%u/%u/IoDepth", pszCtrlId, iCtrlInstance, iCtrlLun);
1050 }
1051
1052 pThis->StatIoDepth = 0;
1053
1054 if (!pThis->pDrvMediaAsync)
1055 {
1056 /* Create request queue. */
1057 rc = RTReqQueueCreate(&pThis->hQueueRequests);
1058 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create request queue rc=%Rrc\n", rc), rc);
1059 /* Create I/O thread. */
1060 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pAsyncIOThread, pThis, drvscsiAsyncIOLoop,
1061 drvscsiAsyncIOLoopWakeup, 0, RTTHREADTYPE_IO, "SCSI async IO");
1062 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create async I/O thread rc=%Rrc\n", rc), rc);
1063
1064 LogRel(("SCSI#%d: using normal I/O\n", pDrvIns->iInstance));
1065 }
1066 else
1067 LogRel(("SCSI#%d: using async I/O\n", pDrvIns->iInstance));
1068
1069 if ( pThis->pDrvMedia->pfnDiscard
1070 || ( pThis->pDrvMediaAsync
1071 && pThis->pDrvMediaAsync->pfnStartDiscard))
1072 LogRel(("SCSI#%d: Enabled UNMAP support\n", pDrvIns->iInstance));
1073
1074 return VINF_SUCCESS;
1075}
1076
1077/**
1078 * SCSI driver registration record.
1079 */
1080const PDMDRVREG g_DrvSCSI =
1081{
1082 /* u32Version */
1083 PDM_DRVREG_VERSION,
1084 /* szName */
1085 "SCSI",
1086 /* szRCMod */
1087 "",
1088 /* szR0Mod */
1089 "",
1090 /* pszDescription */
1091 "Generic SCSI driver.",
1092 /* fFlags */
1093 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1094 /* fClass. */
1095 PDM_DRVREG_CLASS_SCSI,
1096 /* cMaxInstances */
1097 ~0U,
1098 /* cbInstance */
1099 sizeof(DRVSCSI),
1100 /* pfnConstruct */
1101 drvscsiConstruct,
1102 /* pfnDestruct */
1103 drvscsiDestruct,
1104 /* pfnRelocate */
1105 NULL,
1106 /* pfnIOCtl */
1107 NULL,
1108 /* pfnPowerOn */
1109 NULL,
1110 /* pfnReset */
1111 drvscsiReset,
1112 /* pfnSuspend */
1113 drvscsiSuspend,
1114 /* pfnResume */
1115 NULL,
1116 /* pfnAttach */
1117 NULL,
1118 /* pfnDetach */
1119 NULL,
1120 /* pfnPowerOff */
1121 drvscsiPowerOff,
1122 /* pfnSoftReset */
1123 NULL,
1124 /* u32EndVersion */
1125 PDM_DRVREG_VERSION
1126};
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