VirtualBox

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

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

SCSI: Return the PDMIMEDIA interface of the driver below so DevPcBios can get it to be able to retrieve the disk geometry

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.4 KB
Line 
1/* $Id: DrvSCSI.cpp 59274 2016-01-07 09:37:41Z 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 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, pThis->pDrvMedia);
689 return NULL;
690}
691
692static DECLCALLBACK(int) drvscsiQueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
693 uint32_t *piInstance, uint32_t *piLUN)
694{
695 PDRVSCSI pThis = PDMIMEDIAPORT_2_DRVSCSI(pInterface);
696
697 return pThis->pDevScsiPort->pfnQueryDeviceLocation(pThis->pDevScsiPort, ppcszController,
698 piInstance, piLUN);
699}
700
701/**
702 * Called when media is mounted.
703 *
704 * @param pInterface Pointer to the interface structure containing the called function pointer.
705 */
706static DECLCALLBACK(void) drvscsiMountNotify(PPDMIMOUNTNOTIFY pInterface)
707{
708 PDRVSCSI pThis = PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface);
709 LogFlowFunc(("mounting LUN#%p\n", pThis->hVScsiLun));
710
711 /* Ignore the call if we're called while being attached. */
712 if (!pThis->pDrvMedia)
713 return;
714
715 /* Let the LUN know that a medium was mounted. */
716 VSCSILunMountNotify(pThis->hVScsiLun);
717}
718
719/**
720 * Called when media is unmounted
721 *
722 * @param pInterface Pointer to the interface structure containing the called function pointer.
723 */
724static DECLCALLBACK(void) drvscsiUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
725{
726 PDRVSCSI pThis = PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface);
727 LogFlowFunc(("unmounting LUN#%p\n", pThis->hVScsiLun));
728
729 /* Let the LUN know that the medium was unmounted. */
730 VSCSILunUnmountNotify(pThis->hVScsiLun);
731}
732
733/**
734 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
735 *
736 * @param pDrvIns The driver instance.
737 * @param pfnAsyncNotify The async callback.
738 */
739static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
740{
741 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
742
743 if (!pThis->pDrvMediaAsync)
744 {
745 if (pThis->hQueueRequests != NIL_RTREQQUEUE)
746 return;
747
748 ASMAtomicWriteBool(&pThis->fDummySignal, true);
749 if (drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
750 {
751 if (!RTReqQueueIsBusy(pThis->hQueueRequests))
752 {
753 ASMAtomicWriteBool(&pThis->fDummySignal, false);
754 return;
755 }
756
757 PRTREQ pReq;
758 int rc = RTReqQueueCall(pThis->hQueueRequests, &pReq, 0 /*ms*/, (PFNRT)drvscsiAsyncIOLoopSyncCallback, 1, pThis);
759 if (RT_SUCCESS(rc))
760 {
761 ASMAtomicWriteBool(&pThis->fDummySignal, false);
762 RTReqRelease(pReq);
763 return;
764 }
765
766 pThis->pPendingDummyReq = pReq;
767 }
768 }
769 else
770 {
771 if (pThis->StatIoDepth > 0)
772 {
773 ASMAtomicWriteBool(&pThis->fDummySignal, true);
774 }
775 return;
776 }
777
778 PDMDrvHlpSetAsyncNotification(pDrvIns, pfnAsyncNotify);
779}
780
781/**
782 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
783 *
784 * @returns true if we've quiesced, false if we're still working.
785 * @param pDrvIns The driver instance.
786 */
787static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
788{
789 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
790
791 if (pThis->pDrvMediaAsync)
792 {
793 if (pThis->StatIoDepth > 0)
794 return false;
795 else
796 return true;
797 }
798 else
799 {
800 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
801 return false;
802 ASMAtomicWriteBool(&pThis->fDummySignal, false);
803 PDMR3ThreadSuspend(pThis->pAsyncIOThread);
804 return true;
805 }
806}
807
808/**
809 * @copydoc FNPDMDRVPOWEROFF
810 */
811static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
812{
813 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
814}
815
816/**
817 * @copydoc FNPDMDRVSUSPEND
818 */
819static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
820{
821 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
822}
823
824/**
825 * Callback employed by drvscsiReset.
826 *
827 * @returns true if we've quiesced, false if we're still working.
828 * @param pDrvIns The driver instance.
829 */
830static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
831{
832 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
833
834 if (pThis->pDrvMediaAsync)
835 {
836 if (pThis->StatIoDepth > 0)
837 return false;
838 else
839 return true;
840 }
841 else
842 {
843 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
844 return false;
845 ASMAtomicWriteBool(&pThis->fDummySignal, false);
846 return true;
847 }
848}
849
850/**
851 * @copydoc FNPDMDRVRESET
852 */
853static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
854{
855 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
856}
857
858/**
859 * Destruct a driver instance.
860 *
861 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
862 * resources can be freed correctly.
863 *
864 * @param pDrvIns The driver instance data.
865 */
866static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
867{
868 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
869 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
870
871 if (pThis->hQueueRequests != NIL_RTREQQUEUE)
872 {
873 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 100 /*ms*/))
874 LogRel(("drvscsiDestruct#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
875
876 int rc = RTReqQueueDestroy(pThis->hQueueRequests);
877 AssertMsgRC(rc, ("Failed to destroy queue rc=%Rrc\n", rc));
878 pThis->hQueueRequests = NIL_RTREQQUEUE;
879 }
880
881 /* Free the VSCSI device and LUN handle. */
882 if (pThis->hVScsiDevice)
883 {
884 VSCSILUN hVScsiLun;
885 int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
886 AssertRC(rc);
887
888 Assert(hVScsiLun == pThis->hVScsiLun);
889 rc = VSCSILunDestroy(hVScsiLun);
890 AssertRC(rc);
891 rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
892 AssertRC(rc);
893
894 pThis->hVScsiDevice = NULL;
895 pThis->hVScsiLun = NULL;
896 }
897}
898
899/**
900 * Construct a block driver instance.
901 *
902 * @copydoc FNPDMDRVCONSTRUCT
903 */
904static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
905{
906 int rc = VINF_SUCCESS;
907 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
908 LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
909 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
910
911 /*
912 * Initialize the instance data.
913 */
914 pThis->pDrvIns = pDrvIns;
915 pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsiRequestSend;
916 pThis->ISCSIConnector.pfnQueryLUNType = drvscsiQueryLUNType;
917
918 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
919
920 pThis->IMountNotify.pfnMountNotify = drvscsiMountNotify;
921 pThis->IMountNotify.pfnUnmountNotify = drvscsiUnmountNotify;
922 pThis->IPort.pfnQueryDeviceLocation = drvscsiQueryDeviceLocation;
923 pThis->IPortAsync.pfnTransferCompleteNotify = drvscsiTransferCompleteNotify;
924 pThis->hQueueRequests = NIL_RTREQQUEUE;
925
926 /* Query the SCSI port interface above. */
927 pThis->pDevScsiPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISCSIPORT);
928 AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
929
930 /* Query the optional LED interface above. */
931 pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
932 if (pThis->pLedPort != NULL)
933 {
934 /* Get The Led. */
935 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
936 if (RT_FAILURE(rc))
937 pThis->pLed = &pThis->Led;
938 }
939 else
940 pThis->pLed = &pThis->Led;
941
942 /*
943 * Validate and read configuration.
944 */
945 if (!CFGMR3AreValuesValid(pCfg, "NonRotationalMedium\0Readonly\0"))
946 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
947 N_("SCSI configuration error: unknown option specified"));
948
949 rc = CFGMR3QueryBoolDef(pCfg, "NonRotationalMedium", &pThis->fNonRotational, false);
950 if (RT_FAILURE(rc))
951 return PDMDRV_SET_ERROR(pDrvIns, rc,
952 N_("SCSI configuration error: failed to read \"NonRotationalMedium\" as boolean"));
953
954 rc = CFGMR3QueryBoolDef(pCfg, "Readonly", &pThis->fReadonly, false);
955 if (RT_FAILURE(rc))
956 return PDMDRV_SET_ERROR(pDrvIns, rc,
957 N_("SCSI configuration error: failed to read \"Readonly\" as boolean"));
958
959 /*
960 * Try attach driver below and query it's block interface.
961 */
962 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
963 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
964
965 /*
966 * Query the block and blockbios interfaces.
967 */
968 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIA);
969 if (!pThis->pDrvMedia)
970 {
971 AssertMsgFailed(("Configuration error: No block interface!\n"));
972 return VERR_PDM_MISSING_INTERFACE;
973 }
974
975 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
976
977 /* Try to get the optional async block interface. */
978 pThis->pDrvMediaAsync = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIAASYNC);
979
980 PDMMEDIATYPE enmType = pThis->pDrvMedia->pfnGetType(pThis->pDrvMedia);
981 VSCSILUNTYPE enmLunType;
982 switch (enmType)
983 {
984 case PDMMEDIATYPE_HARD_DISK:
985 enmLunType = VSCSILUNTYPE_SBC;
986 break;
987 case PDMMEDIATYPE_CDROM:
988 case PDMMEDIATYPE_DVD:
989 enmLunType = VSCSILUNTYPE_MMC;
990 break;
991 default:
992 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
993 N_("Only hard disks and CD/DVD-ROMs are currently supported as SCSI devices (enmType=%d)"),
994 enmType);
995 }
996 if ( ( enmType == PDMMEDIATYPE_DVD
997 || enmType == PDMMEDIATYPE_CDROM)
998 && !pThis->pDrvMount)
999 {
1000 AssertMsgFailed(("Internal error: cdrom without a mountable interface\n"));
1001 return VERR_INTERNAL_ERROR;
1002 }
1003
1004 /* Create VSCSI device and LUN. */
1005 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
1006 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSectorSize = drvscsiGetSectorSize;
1007 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
1008 pThis->VScsiIoCallbacks.pfnVScsiLunGetFeatureFlags = drvscsiGetFeatureFlags;
1009 pThis->VScsiIoCallbacks.pfnVScsiLunMediumSetLock = drvscsiSetLock;
1010
1011 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis);
1012 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n", rc), rc);
1013 rc = VSCSILunCreate(&pThis->hVScsiLun, enmLunType, &pThis->VScsiIoCallbacks,
1014 pThis);
1015 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n", rc), rc);
1016 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
1017 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
1018
1019 //@todo: This is a very hacky way of telling the LUN whether a medium was mounted.
1020 // The mount/unmount interface doesn't work in a very sensible manner!
1021 if (pThis->pDrvMount)
1022 {
1023 if (pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia))
1024 {
1025 rc = VINF_SUCCESS; VSCSILunMountNotify(pThis->hVScsiLun);
1026 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being mounted\n"), rc);
1027 }
1028 else
1029 {
1030 rc = VINF_SUCCESS; VSCSILunUnmountNotify(pThis->hVScsiLun);
1031 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being unmounted\n"), rc);
1032 }
1033 }
1034
1035 const char *pszCtrl = NULL;
1036 uint32_t iCtrlInstance = 0;
1037 uint32_t iCtrlLun = 0;
1038
1039 rc = pThis->pDevScsiPort->pfnQueryDeviceLocation(pThis->pDevScsiPort, &pszCtrl, &iCtrlInstance, &iCtrlLun);
1040 if (RT_SUCCESS(rc))
1041 {
1042 const char *pszCtrlId = strcmp(pszCtrl, "Msd") == 0 ? "USB"
1043 : strcmp(pszCtrl, "lsilogicsas") == 0 ? "SAS"
1044 : "SCSI";
1045 /* Register statistics counter. */
1046 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1047 "Amount of data read.", "/Devices/%s%u/%u/ReadBytes", pszCtrlId, iCtrlInstance, iCtrlLun);
1048 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1049 "Amount of data written.", "/Devices/%s%u/%u/WrittenBytes", pszCtrlId, iCtrlInstance, iCtrlLun);
1050 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
1051 "Number of active tasks.", "/Devices/%s%u/%u/IoDepth", pszCtrlId, iCtrlInstance, iCtrlLun);
1052 }
1053
1054 pThis->StatIoDepth = 0;
1055
1056 if (!pThis->pDrvMediaAsync)
1057 {
1058 /* Create request queue. */
1059 rc = RTReqQueueCreate(&pThis->hQueueRequests);
1060 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create request queue rc=%Rrc\n", rc), rc);
1061 /* Create I/O thread. */
1062 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pAsyncIOThread, pThis, drvscsiAsyncIOLoop,
1063 drvscsiAsyncIOLoopWakeup, 0, RTTHREADTYPE_IO, "SCSI async IO");
1064 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create async I/O thread rc=%Rrc\n", rc), rc);
1065
1066 LogRel(("SCSI#%d: using normal I/O\n", pDrvIns->iInstance));
1067 }
1068 else
1069 LogRel(("SCSI#%d: using async I/O\n", pDrvIns->iInstance));
1070
1071 if ( pThis->pDrvMedia->pfnDiscard
1072 || ( pThis->pDrvMediaAsync
1073 && pThis->pDrvMediaAsync->pfnStartDiscard))
1074 LogRel(("SCSI#%d: Enabled UNMAP support\n", pDrvIns->iInstance));
1075
1076 return VINF_SUCCESS;
1077}
1078
1079/**
1080 * SCSI driver registration record.
1081 */
1082const PDMDRVREG g_DrvSCSI =
1083{
1084 /* u32Version */
1085 PDM_DRVREG_VERSION,
1086 /* szName */
1087 "SCSI",
1088 /* szRCMod */
1089 "",
1090 /* szR0Mod */
1091 "",
1092 /* pszDescription */
1093 "Generic SCSI driver.",
1094 /* fFlags */
1095 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1096 /* fClass. */
1097 PDM_DRVREG_CLASS_SCSI,
1098 /* cMaxInstances */
1099 ~0U,
1100 /* cbInstance */
1101 sizeof(DRVSCSI),
1102 /* pfnConstruct */
1103 drvscsiConstruct,
1104 /* pfnDestruct */
1105 drvscsiDestruct,
1106 /* pfnRelocate */
1107 NULL,
1108 /* pfnIOCtl */
1109 NULL,
1110 /* pfnPowerOn */
1111 NULL,
1112 /* pfnReset */
1113 drvscsiReset,
1114 /* pfnSuspend */
1115 drvscsiSuspend,
1116 /* pfnResume */
1117 NULL,
1118 /* pfnAttach */
1119 NULL,
1120 /* pfnDetach */
1121 NULL,
1122 /* pfnPowerOff */
1123 drvscsiPowerOff,
1124 /* pfnSoftReset */
1125 NULL,
1126 /* u32EndVersion */
1127 PDM_DRVREG_VERSION
1128};
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