VirtualBox

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

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

pdmifs.h: Move the storage related interfaces (PDMIMEDIA, PDMIMOUNT, PDMISCSICONNECTOR, etc.) into a separate header to reduce the overall size of the header a bit

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