VirtualBox

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

Last change on this file since 63547 was 62965, checked in by vboxsync, 8 years ago

Devices: warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.5 KB
Line 
1/* $Id: DrvSCSI.cpp 62965 2016-08-04 09:57:20Z vboxsync $ */
2/** @file
3 * VBox storage drivers: Generic SCSI command parser and execution driver
4 */
5
6/*
7 * Copyright (C) 2006-2016 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 RT_NOREF(hVScsiLun);
243 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
244
245 *pcbSize = pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
246
247 return VINF_SUCCESS;
248}
249
250
251static DECLCALLBACK(int) drvscsiGetSectorSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint32_t *pcbSectorSize)
252{
253 RT_NOREF(hVScsiLun);
254 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
255
256 *pcbSectorSize = pThis->pDrvMedia->pfnGetSectorSize(pThis->pDrvMedia);
257
258 return VINF_SUCCESS;
259}
260static DECLCALLBACK(int) drvscsiSetLock(VSCSILUN hVScsiLun, void *pvScsiLunUser, bool fLocked)
261{
262 RT_NOREF(hVScsiLun);
263 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
264
265 if (fLocked)
266 pThis->pDrvMount->pfnLock(pThis->pDrvMount);
267 else
268 pThis->pDrvMount->pfnUnlock(pThis->pDrvMount);
269
270 return VINF_SUCCESS;
271}
272
273static DECLCALLBACK(int) drvscsiTransferCompleteNotify(PPDMIMEDIAASYNCPORT pInterface, void *pvUser, int rc)
274{
275 PDRVSCSI pThis = PDMIMEDIAASYNCPORT_2_DRVSCSI(pInterface);
276 VSCSIIOREQ hVScsiIoReq = (VSCSIIOREQ)pvUser;
277 VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
278
279 LogFlowFunc(("Request hVScsiIoReq=%#p completed\n", hVScsiIoReq));
280
281 if (enmTxDir == VSCSIIOREQTXDIR_READ)
282 pThis->pLed->Actual.s.fReading = 0;
283 else if ( enmTxDir == VSCSIIOREQTXDIR_WRITE
284 || enmTxDir == VSCSIIOREQTXDIR_UNMAP)
285 pThis->pLed->Actual.s.fWriting = 0;
286 else
287 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
288
289 if (RT_SUCCESS(rc))
290 VSCSIIoReqCompleted(hVScsiIoReq, rc, false /* fRedoPossible */);
291 else
292 {
293 pThis->cErrors++;
294 if (pThis->cErrors < MAX_LOG_REL_ERRORS)
295 {
296 if (enmTxDir == VSCSIIOREQTXDIR_FLUSH)
297 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
298 pThis->pDrvIns->iInstance, rc));
299 else if (enmTxDir == VSCSIIOREQTXDIR_UNMAP)
300 LogRel(("SCSI#%u: Unmap returned rc=%Rrc\n",
301 pThis->pDrvIns->iInstance, rc));
302 else
303 {
304 uint64_t uOffset = 0;
305 size_t cbTransfer = 0;
306 size_t cbSeg = 0;
307 PCRTSGSEG paSeg = NULL;
308 unsigned cSeg = 0;
309
310 VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
311 &cSeg, &cbSeg, &paSeg);
312
313 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
314 pThis->pDrvIns->iInstance,
315 enmTxDir == VSCSIIOREQTXDIR_READ
316 ? "Read"
317 : "Write",
318 uOffset,
319 cbTransfer, rc));
320 }
321 }
322
323 VSCSIIoReqCompleted(hVScsiIoReq, rc, drvscsiIsRedoPossible(rc));
324 }
325
326 return VINF_SUCCESS;
327}
328
329static DECLCALLBACK(int) drvscsiReqTransferEnqueue(VSCSILUN hVScsiLun, void *pvScsiLunUser, VSCSIIOREQ hVScsiIoReq)
330{
331 RT_NOREF(hVScsiLun);
332 int rc = VINF_SUCCESS;
333 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
334
335 if (pThis->pDrvMediaAsync)
336 {
337 /* async I/O path. */
338 VSCSIIOREQTXDIR enmTxDir;
339
340 LogFlowFunc(("Enqueuing hVScsiIoReq=%#p\n", hVScsiIoReq));
341
342 enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
343
344 switch (enmTxDir)
345 {
346 case VSCSIIOREQTXDIR_FLUSH:
347 {
348 rc = pThis->pDrvMediaAsync->pfnStartFlush(pThis->pDrvMediaAsync, hVScsiIoReq);
349 if ( RT_FAILURE(rc)
350 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS
351 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
352 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
353 pThis->pDrvIns->iInstance, rc));
354 break;
355 }
356 case VSCSIIOREQTXDIR_UNMAP:
357 {
358 PCRTRANGE paRanges;
359 unsigned cRanges;
360
361 rc = VSCSIIoReqUnmapParamsGet(hVScsiIoReq, &paRanges, &cRanges);
362 AssertRC(rc);
363
364 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
365 rc = pThis->pDrvMediaAsync->pfnStartDiscard(pThis->pDrvMediaAsync, paRanges, cRanges, hVScsiIoReq);
366 if ( RT_FAILURE(rc)
367 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS
368 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
369 LogRel(("SCSI#%u: Discard returned rc=%Rrc\n",
370 pThis->pDrvIns->iInstance, rc));
371 break;
372 }
373 case VSCSIIOREQTXDIR_READ:
374 case VSCSIIOREQTXDIR_WRITE:
375 {
376 uint64_t uOffset = 0;
377 size_t cbTransfer = 0;
378 size_t cbSeg = 0;
379 PCRTSGSEG paSeg = NULL;
380 unsigned cSeg = 0;
381
382 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
383 &cSeg, &cbSeg, &paSeg);
384 AssertRC(rc);
385
386 if (enmTxDir == VSCSIIOREQTXDIR_READ)
387 {
388 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
389 rc = pThis->pDrvMediaAsync->pfnStartRead(pThis->pDrvMediaAsync, uOffset,
390 paSeg, cSeg, cbTransfer,
391 hVScsiIoReq);
392 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbTransfer);
393 }
394 else
395 {
396 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
397 rc = pThis->pDrvMediaAsync->pfnStartWrite(pThis->pDrvMediaAsync, uOffset,
398 paSeg, cSeg, cbTransfer,
399 hVScsiIoReq);
400 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbTransfer);
401 }
402
403 if ( RT_FAILURE(rc)
404 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS
405 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
406 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
407 pThis->pDrvIns->iInstance,
408 enmTxDir == VSCSIIOREQTXDIR_READ
409 ? "Read"
410 : "Write",
411 uOffset,
412 cbTransfer, rc));
413 break;
414 }
415 default:
416 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
417 }
418
419 if (rc == VINF_VD_ASYNC_IO_FINISHED)
420 {
421 if (enmTxDir == VSCSIIOREQTXDIR_READ)
422 pThis->pLed->Actual.s.fReading = 0;
423 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
424 pThis->pLed->Actual.s.fWriting = 0;
425 else
426 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
427
428 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS, false);
429 rc = VINF_SUCCESS;
430 }
431 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
432 rc = VINF_SUCCESS;
433 else if (RT_FAILURE(rc))
434 {
435 if (enmTxDir == VSCSIIOREQTXDIR_READ)
436 pThis->pLed->Actual.s.fReading = 0;
437 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
438 pThis->pLed->Actual.s.fWriting = 0;
439 else
440 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
441
442 VSCSIIoReqCompleted(hVScsiIoReq, rc, drvscsiIsRedoPossible(rc));
443 rc = VINF_SUCCESS;
444 }
445 else
446 AssertMsgFailed(("Invalid return code rc=%Rrc\n", rc));
447 }
448 else
449 {
450 /* I/O thread. */
451 rc = RTReqQueueCallEx(pThis->hQueueRequests, NULL, 0, RTREQFLAGS_NO_WAIT,
452 (PFNRT)drvscsiProcessRequestOne, 2, pThis, hVScsiIoReq);
453 }
454
455 return rc;
456}
457
458static DECLCALLBACK(int) drvscsiGetFeatureFlags(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint64_t *pfFeatures)
459{
460 RT_NOREF(hVScsiLun);
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, int rcReq)
481{
482 RT_NOREF(hVScsiDevice);
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 RT_NOREF(pThread);
570 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
571 PRTREQ pReq;
572 int rc;
573
574 AssertMsgReturn(pThis->hQueueRequests != NIL_RTREQQUEUE, ("hQueueRequests is NULL\n"), VERR_INVALID_STATE);
575
576 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 10000 /* 10 sec */))
577 {
578 LogRel(("drvscsiAsyncIOLoopWakeup#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
579 return VERR_TIMEOUT;
580 }
581
582 rc = RTReqQueueCall(pThis->hQueueRequests, &pReq, 10000 /* 10 sec. */, (PFNRT)drvscsiAsyncIOLoopWakeupFunc, 1, pThis);
583 if (RT_SUCCESS(rc))
584 RTReqRelease(pReq);
585 else
586 {
587 pThis->pPendingDummyReq = pReq;
588 LogRel(("drvscsiAsyncIOLoopWakeup#%u: %Rrc pReq=%p\n", pDrvIns->iInstance, rc, pReq));
589 }
590
591 return rc;
592}
593
594/* -=-=-=-=- ISCSIConnector -=-=-=-=- */
595
596#ifdef DEBUG
597/**
598 * Dumps a SCSI request structure for debugging purposes.
599 *
600 * @returns nothing.
601 * @param pRequest Pointer to the request to dump.
602 */
603static void drvscsiDumpScsiRequest(PPDMSCSIREQUEST pRequest)
604{
605 Log(("Dump for pRequest=%#p Command: %s\n", pRequest, SCSICmdText(pRequest->pbCDB[0])));
606 Log(("cbCDB=%u\n", pRequest->cbCDB));
607 for (uint32_t i = 0; i < pRequest->cbCDB; i++)
608 Log(("pbCDB[%u]=%#x\n", i, pRequest->pbCDB[i]));
609 Log(("cbScatterGather=%u\n", pRequest->cbScatterGather));
610 Log(("cScatterGatherEntries=%u\n", pRequest->cScatterGatherEntries));
611 /* Print all scatter gather entries. */
612 for (uint32_t i = 0; i < pRequest->cScatterGatherEntries; i++)
613 {
614 Log(("ScatterGatherEntry[%u].cbSeg=%u\n", i, pRequest->paScatterGatherHead[i].cbSeg));
615 Log(("ScatterGatherEntry[%u].pvSeg=%#p\n", i, pRequest->paScatterGatherHead[i].pvSeg));
616 }
617 Log(("pvUser=%#p\n", pRequest->pvUser));
618}
619#endif
620
621/** @interface_method_impl{PDMISCSICONNECTOR,pfnSCSIRequestSend} */
622static DECLCALLBACK(int) drvscsiRequestSend(PPDMISCSICONNECTOR pInterface, PPDMSCSIREQUEST pSCSIRequest)
623{
624 int rc;
625 PDRVSCSI pThis = PDMISCSICONNECTOR_2_DRVSCSI(pInterface);
626 VSCSIREQ hVScsiReq;
627
628#ifdef DEBUG
629 drvscsiDumpScsiRequest(pSCSIRequest);
630#endif
631
632 rc = VSCSIDeviceReqCreate(pThis->hVScsiDevice, &hVScsiReq,
633 pSCSIRequest->uLogicalUnit,
634 pSCSIRequest->pbCDB,
635 pSCSIRequest->cbCDB,
636 pSCSIRequest->cbScatterGather,
637 pSCSIRequest->cScatterGatherEntries,
638 pSCSIRequest->paScatterGatherHead,
639 pSCSIRequest->pbSenseBuffer,
640 pSCSIRequest->cbSenseBuffer,
641 pSCSIRequest);
642 if (RT_FAILURE(rc))
643 return rc;
644
645 ASMAtomicIncU32(&pThis->StatIoDepth);
646 rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, hVScsiReq);
647
648 return rc;
649}
650
651/** @interface_method_impl{PDMISCSICONNECTOR,pfnQueryLUNType} */
652static DECLCALLBACK(int) drvscsiQueryLUNType(PPDMISCSICONNECTOR pInterface, uint32_t iLun, PPDMSCSILUNTYPE pLunType)
653{
654 int rc;
655 PDRVSCSI pThis = PDMISCSICONNECTOR_2_DRVSCSI(pInterface);
656 VSCSILUNTYPE enmLunType;
657
658 rc = VSCSIDeviceLunQueryType(pThis->hVScsiDevice, iLun, &enmLunType);
659 if (RT_FAILURE(rc))
660 return rc;
661
662 switch (enmLunType)
663 {
664 case VSCSILUNTYPE_SBC: *pLunType = PDMSCSILUNTYPE_SBC; break;
665 case VSCSILUNTYPE_MMC: *pLunType = PDMSCSILUNTYPE_MMC; break;
666 case VSCSILUNTYPE_SSC: *pLunType = PDMSCSILUNTYPE_SSC; break;
667 default: *pLunType = PDMSCSILUNTYPE_INVALID;
668 }
669
670 return rc;
671}
672
673/* -=-=-=-=- IBase -=-=-=-=- */
674
675/**
676 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
677 */
678static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
679{
680 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
681 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
682
683 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, pThis->pDrvMount);
684 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
685 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector);
686 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pThis->IPort);
687 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pThis->IMountNotify);
688 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAASYNCPORT, &pThis->IPortAsync);
689 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, pThis->pDrvMedia);
690 return NULL;
691}
692
693static DECLCALLBACK(int) drvscsiQueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
694 uint32_t *piInstance, uint32_t *piLUN)
695{
696 PDRVSCSI pThis = PDMIMEDIAPORT_2_DRVSCSI(pInterface);
697
698 return pThis->pDevScsiPort->pfnQueryDeviceLocation(pThis->pDevScsiPort, ppcszController,
699 piInstance, piLUN);
700}
701
702/**
703 * Called when media is mounted.
704 *
705 * @param pInterface Pointer to the interface structure containing the called function pointer.
706 */
707static DECLCALLBACK(void) drvscsiMountNotify(PPDMIMOUNTNOTIFY pInterface)
708{
709 PDRVSCSI pThis = PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface);
710 LogFlowFunc(("mounting LUN#%p\n", pThis->hVScsiLun));
711
712 /* Ignore the call if we're called while being attached. */
713 if (!pThis->pDrvMedia)
714 return;
715
716 /* Let the LUN know that a medium was mounted. */
717 VSCSILunMountNotify(pThis->hVScsiLun);
718}
719
720/**
721 * Called when media is unmounted
722 *
723 * @param pInterface Pointer to the interface structure containing the called function pointer.
724 */
725static DECLCALLBACK(void) drvscsiUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
726{
727 PDRVSCSI pThis = PDMIMOUNTNOTIFY_2_DRVSCSI(pInterface);
728 LogFlowFunc(("unmounting LUN#%p\n", pThis->hVScsiLun));
729
730 /* Let the LUN know that the medium was unmounted. */
731 VSCSILunUnmountNotify(pThis->hVScsiLun);
732}
733
734/**
735 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
736 *
737 * @param pDrvIns The driver instance.
738 * @param pfnAsyncNotify The async callback.
739 */
740static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
741{
742 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
743
744 if (!pThis->pDrvMediaAsync)
745 {
746 if (pThis->hQueueRequests != NIL_RTREQQUEUE)
747 return;
748
749 ASMAtomicWriteBool(&pThis->fDummySignal, true);
750 if (drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
751 {
752 if (!RTReqQueueIsBusy(pThis->hQueueRequests))
753 {
754 ASMAtomicWriteBool(&pThis->fDummySignal, false);
755 return;
756 }
757
758 PRTREQ pReq;
759 int rc = RTReqQueueCall(pThis->hQueueRequests, &pReq, 0 /*ms*/, (PFNRT)drvscsiAsyncIOLoopSyncCallback, 1, pThis);
760 if (RT_SUCCESS(rc))
761 {
762 ASMAtomicWriteBool(&pThis->fDummySignal, false);
763 RTReqRelease(pReq);
764 return;
765 }
766
767 pThis->pPendingDummyReq = pReq;
768 }
769 }
770 else
771 {
772 if (pThis->StatIoDepth > 0)
773 {
774 ASMAtomicWriteBool(&pThis->fDummySignal, true);
775 }
776 return;
777 }
778
779 PDMDrvHlpSetAsyncNotification(pDrvIns, pfnAsyncNotify);
780}
781
782/**
783 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
784 *
785 * @returns true if we've quiesced, false if we're still working.
786 * @param pDrvIns The driver instance.
787 */
788static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
789{
790 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
791
792 if (pThis->pDrvMediaAsync)
793 {
794 if (pThis->StatIoDepth > 0)
795 return false;
796 else
797 return true;
798 }
799 else
800 {
801 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
802 return false;
803 ASMAtomicWriteBool(&pThis->fDummySignal, false);
804 PDMR3ThreadSuspend(pThis->pAsyncIOThread);
805 return true;
806 }
807}
808
809/**
810 * @copydoc FNPDMDRVPOWEROFF
811 */
812static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
813{
814 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
815}
816
817/**
818 * @copydoc FNPDMDRVSUSPEND
819 */
820static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
821{
822 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
823}
824
825/**
826 * Callback employed by drvscsiReset.
827 *
828 * @returns true if we've quiesced, false if we're still working.
829 * @param pDrvIns The driver instance.
830 */
831static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
832{
833 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
834
835 if (pThis->pDrvMediaAsync)
836 {
837 if (pThis->StatIoDepth > 0)
838 return false;
839 else
840 return true;
841 }
842 else
843 {
844 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
845 return false;
846 ASMAtomicWriteBool(&pThis->fDummySignal, false);
847 return true;
848 }
849}
850
851/**
852 * @copydoc FNPDMDRVRESET
853 */
854static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
855{
856 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
857}
858
859/**
860 * Destruct a driver instance.
861 *
862 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
863 * resources can be freed correctly.
864 *
865 * @param pDrvIns The driver instance data.
866 */
867static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
868{
869 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
870 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
871
872 if (pThis->hQueueRequests != NIL_RTREQQUEUE)
873 {
874 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 100 /*ms*/))
875 LogRel(("drvscsiDestruct#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
876
877 int rc = RTReqQueueDestroy(pThis->hQueueRequests);
878 AssertMsgRC(rc, ("Failed to destroy queue rc=%Rrc\n", rc));
879 pThis->hQueueRequests = NIL_RTREQQUEUE;
880 }
881
882 /* Free the VSCSI device and LUN handle. */
883 if (pThis->hVScsiDevice)
884 {
885 VSCSILUN hVScsiLun;
886 int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
887 AssertRC(rc);
888
889 Assert(hVScsiLun == pThis->hVScsiLun);
890 rc = VSCSILunDestroy(hVScsiLun);
891 AssertRC(rc);
892 rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
893 AssertRC(rc);
894
895 pThis->hVScsiDevice = NULL;
896 pThis->hVScsiLun = NULL;
897 }
898
899 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatBytesRead);
900 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatBytesWritten);
901 PDMDrvHlpSTAMDeregister(pDrvIns, (void *)&pThis->StatIoDepth);
902}
903
904/**
905 * Construct a block driver instance.
906 *
907 * @copydoc FNPDMDRVCONSTRUCT
908 */
909static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
910{
911 int rc = VINF_SUCCESS;
912 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
913 LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
914 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
915
916 /*
917 * Initialize the instance data.
918 */
919 pThis->pDrvIns = pDrvIns;
920 pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsiRequestSend;
921 pThis->ISCSIConnector.pfnQueryLUNType = drvscsiQueryLUNType;
922
923 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
924
925 pThis->IMountNotify.pfnMountNotify = drvscsiMountNotify;
926 pThis->IMountNotify.pfnUnmountNotify = drvscsiUnmountNotify;
927 pThis->IPort.pfnQueryDeviceLocation = drvscsiQueryDeviceLocation;
928 pThis->IPortAsync.pfnTransferCompleteNotify = drvscsiTransferCompleteNotify;
929 pThis->hQueueRequests = NIL_RTREQQUEUE;
930
931 /* Query the SCSI port interface above. */
932 pThis->pDevScsiPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISCSIPORT);
933 AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
934
935 /* Query the optional LED interface above. */
936 pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
937 if (pThis->pLedPort != NULL)
938 {
939 /* Get The Led. */
940 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
941 if (RT_FAILURE(rc))
942 pThis->pLed = &pThis->Led;
943 }
944 else
945 pThis->pLed = &pThis->Led;
946
947 /*
948 * Validate and read configuration.
949 */
950 if (!CFGMR3AreValuesValid(pCfg, "NonRotationalMedium\0Readonly\0"))
951 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
952 N_("SCSI configuration error: unknown option specified"));
953
954 rc = CFGMR3QueryBoolDef(pCfg, "NonRotationalMedium", &pThis->fNonRotational, false);
955 if (RT_FAILURE(rc))
956 return PDMDRV_SET_ERROR(pDrvIns, rc,
957 N_("SCSI configuration error: failed to read \"NonRotationalMedium\" as boolean"));
958
959 rc = CFGMR3QueryBoolDef(pCfg, "Readonly", &pThis->fReadonly, false);
960 if (RT_FAILURE(rc))
961 return PDMDRV_SET_ERROR(pDrvIns, rc,
962 N_("SCSI configuration error: failed to read \"Readonly\" as boolean"));
963
964 /*
965 * Try attach driver below and query it's block interface.
966 */
967 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
968 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
969
970 /*
971 * Query the block and blockbios interfaces.
972 */
973 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIA);
974 if (!pThis->pDrvMedia)
975 {
976 AssertMsgFailed(("Configuration error: No block interface!\n"));
977 return VERR_PDM_MISSING_INTERFACE;
978 }
979
980 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
981
982 /* Try to get the optional async block interface. */
983 pThis->pDrvMediaAsync = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIAASYNC);
984
985 PDMMEDIATYPE enmType = pThis->pDrvMedia->pfnGetType(pThis->pDrvMedia);
986 VSCSILUNTYPE enmLunType;
987 switch (enmType)
988 {
989 case PDMMEDIATYPE_HARD_DISK:
990 enmLunType = VSCSILUNTYPE_SBC;
991 break;
992 case PDMMEDIATYPE_CDROM:
993 case PDMMEDIATYPE_DVD:
994 enmLunType = VSCSILUNTYPE_MMC;
995 break;
996 default:
997 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
998 N_("Only hard disks and CD/DVD-ROMs are currently supported as SCSI devices (enmType=%d)"),
999 enmType);
1000 }
1001 if ( ( enmType == PDMMEDIATYPE_DVD
1002 || enmType == PDMMEDIATYPE_CDROM)
1003 && !pThis->pDrvMount)
1004 {
1005 AssertMsgFailed(("Internal error: cdrom without a mountable interface\n"));
1006 return VERR_INTERNAL_ERROR;
1007 }
1008
1009 /* Create VSCSI device and LUN. */
1010 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
1011 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSectorSize = drvscsiGetSectorSize;
1012 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
1013 pThis->VScsiIoCallbacks.pfnVScsiLunGetFeatureFlags = drvscsiGetFeatureFlags;
1014 pThis->VScsiIoCallbacks.pfnVScsiLunMediumSetLock = drvscsiSetLock;
1015
1016 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis);
1017 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n", rc), rc);
1018 rc = VSCSILunCreate(&pThis->hVScsiLun, enmLunType, &pThis->VScsiIoCallbacks,
1019 pThis);
1020 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n", rc), rc);
1021 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
1022 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
1023
1024 //@todo: This is a very hacky way of telling the LUN whether a medium was mounted.
1025 // The mount/unmount interface doesn't work in a very sensible manner!
1026 if (pThis->pDrvMount)
1027 {
1028 if (pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia))
1029 {
1030 rc = VINF_SUCCESS; VSCSILunMountNotify(pThis->hVScsiLun);
1031 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being mounted\n"), rc);
1032 }
1033 else
1034 {
1035 rc = VINF_SUCCESS; VSCSILunUnmountNotify(pThis->hVScsiLun);
1036 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being unmounted\n"), rc);
1037 }
1038 }
1039
1040 const char *pszCtrl = NULL;
1041 uint32_t iCtrlInstance = 0;
1042 uint32_t iCtrlLun = 0;
1043
1044 rc = pThis->pDevScsiPort->pfnQueryDeviceLocation(pThis->pDevScsiPort, &pszCtrl, &iCtrlInstance, &iCtrlLun);
1045 if (RT_SUCCESS(rc))
1046 {
1047 const char *pszCtrlId = strcmp(pszCtrl, "Msd") == 0 ? "USB"
1048 : strcmp(pszCtrl, "lsilogicsas") == 0 ? "SAS"
1049 : "SCSI";
1050 /* Register statistics counter. */
1051 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1052 "Amount of data read.", "/Devices/%s%u/%u/ReadBytes", pszCtrlId, iCtrlInstance, iCtrlLun);
1053 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1054 "Amount of data written.", "/Devices/%s%u/%u/WrittenBytes", pszCtrlId, iCtrlInstance, iCtrlLun);
1055 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
1056 "Number of active tasks.", "/Devices/%s%u/%u/IoDepth", pszCtrlId, iCtrlInstance, iCtrlLun);
1057 }
1058
1059 pThis->StatIoDepth = 0;
1060
1061 if (!pThis->pDrvMediaAsync)
1062 {
1063 /* Create request queue. */
1064 rc = RTReqQueueCreate(&pThis->hQueueRequests);
1065 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create request queue rc=%Rrc\n", rc), rc);
1066 /* Create I/O thread. */
1067 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pAsyncIOThread, pThis, drvscsiAsyncIOLoop,
1068 drvscsiAsyncIOLoopWakeup, 0, RTTHREADTYPE_IO, "SCSI async IO");
1069 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create async I/O thread rc=%Rrc\n", rc), rc);
1070
1071 LogRel(("SCSI#%d: using normal I/O\n", pDrvIns->iInstance));
1072 }
1073 else
1074 LogRel(("SCSI#%d: using async I/O\n", pDrvIns->iInstance));
1075
1076 if ( pThis->pDrvMedia->pfnDiscard
1077 || ( pThis->pDrvMediaAsync
1078 && pThis->pDrvMediaAsync->pfnStartDiscard))
1079 LogRel(("SCSI#%d: Enabled UNMAP support\n", pDrvIns->iInstance));
1080
1081 return VINF_SUCCESS;
1082}
1083
1084/**
1085 * SCSI driver registration record.
1086 */
1087const PDMDRVREG g_DrvSCSI =
1088{
1089 /* u32Version */
1090 PDM_DRVREG_VERSION,
1091 /* szName */
1092 "SCSI",
1093 /* szRCMod */
1094 "",
1095 /* szR0Mod */
1096 "",
1097 /* pszDescription */
1098 "Generic SCSI driver.",
1099 /* fFlags */
1100 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1101 /* fClass. */
1102 PDM_DRVREG_CLASS_SCSI,
1103 /* cMaxInstances */
1104 ~0U,
1105 /* cbInstance */
1106 sizeof(DRVSCSI),
1107 /* pfnConstruct */
1108 drvscsiConstruct,
1109 /* pfnDestruct */
1110 drvscsiDestruct,
1111 /* pfnRelocate */
1112 NULL,
1113 /* pfnIOCtl */
1114 NULL,
1115 /* pfnPowerOn */
1116 NULL,
1117 /* pfnReset */
1118 drvscsiReset,
1119 /* pfnSuspend */
1120 drvscsiSuspend,
1121 /* pfnResume */
1122 NULL,
1123 /* pfnAttach */
1124 NULL,
1125 /* pfnDetach */
1126 NULL,
1127 /* pfnPowerOff */
1128 drvscsiPowerOff,
1129 /* pfnSoftReset */
1130 NULL,
1131 /* u32EndVersion */
1132 PDM_DRVREG_VERSION
1133};
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