VirtualBox

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

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

PDM/VSCSI: Added PDMISCSICONNECTOR interface to report LUN type.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.1 KB
Line 
1/* $Id: DrvSCSI.cpp 56439 2015-06-15 17:14:02Z 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* Header Files *
20*******************************************************************************/
21//#define DEBUG
22#define LOG_GROUP LOG_GROUP_DRV_SCSI
23#include <VBox/vmm/pdmdrv.h>
24#include <VBox/vmm/pdmifs.h>
25#include <VBox/vmm/pdmthread.h>
26#include <VBox/vscsi.h>
27#include <VBox/scsi.h>
28#include <iprt/asm.h>
29#include <iprt/assert.h>
30#include <iprt/mem.h>
31#include <iprt/req.h>
32#include <iprt/semaphore.h>
33#include <iprt/string.h>
34#include <iprt/uuid.h>
35
36#include "VBoxDD.h"
37
38/** The maximum number of release log entries per device. */
39#define MAX_LOG_REL_ERRORS 1024
40
41/**
42 * SCSI driver instance data.
43 *
44 * @implements PDMISCSICONNECTOR
45 * @implements PDMIBLOCKASYNCPORT
46 * @implements PDMIMOUNTNOTIFY
47 */
48typedef struct DRVSCSI
49{
50 /** Pointer driver instance. */
51 PPDMDRVINS pDrvIns;
52
53 /** Pointer to the attached driver's base interface. */
54 PPDMIBASE pDrvBase;
55 /** Pointer to the attached driver's block interface. */
56 PPDMIBLOCK pDrvBlock;
57 /** Pointer to the attached driver's async block interface. */
58 PPDMIBLOCKASYNC pDrvBlockAsync;
59 /** Pointer to the attached driver's block bios interface. */
60 PPDMIBLOCKBIOS pDrvBlockBios;
61 /** Pointer to the attached driver's mount interface. */
62 PPDMIMOUNT pDrvMount;
63 /** Pointer to the SCSI port interface of the device above. */
64 PPDMISCSIPORT pDevScsiPort;
65 /** pointer to the Led port interface of the dveice above. */
66 PPDMILEDPORTS pLedPort;
67 /** The scsi connector interface .*/
68 PDMISCSICONNECTOR ISCSIConnector;
69 /** The block port interface. */
70 PDMIBLOCKPORT IPort;
71 /** The optional block async port interface. */
72 PDMIBLOCKASYNCPORT IPortAsync;
73 /** 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 PDMIBLOCKASYNCPORT_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 PDMIBLOCKPORT_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->pDrvBlock->pfnFlush(pThis->pDrvBlock);
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->pDrvBlock->pfnRead(pThis->pDrvBlock, 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->pDrvBlock->pfnWrite(pThis->pDrvBlock, 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->pDrvBlock->pfnDiscard(pThis->pDrvBlock, 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->pDrvBlock->pfnGetSize(pThis->pDrvBlock);
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->pDrvBlock->pfnGetSectorSize(pThis->pDrvBlock);
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 int drvscsiTransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterface, void *pvUser, int rc)
271{
272 PDRVSCSI pThis = PDMIBLOCKASYNCPORT_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->pDrvBlockAsync)
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->pDrvBlockAsync->pfnStartFlush(pThis->pDrvBlockAsync, 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->pDrvBlockAsync->pfnStartDiscard(pThis->pDrvBlockAsync, 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->pDrvBlockAsync->pfnStartRead(pThis->pDrvBlockAsync, 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->pDrvBlockAsync->pfnStartWrite(pThis->pDrvBlockAsync, 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->pDrvBlock->pfnDiscard
466 || ( pThis->pDrvBlockAsync
467 && pThis->pDrvBlockAsync->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 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 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 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/** @copydoc 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/** @copydoc 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, PDMIBLOCKBIOS, pThis->pDrvBlockBios);
684 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
685 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector);
686 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pThis->IPort);
687 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pThis->IMountNotify);
688 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKASYNCPORT, &pThis->IPortAsync);
689 return NULL;
690}
691
692static DECLCALLBACK(int) drvscsiQueryDeviceLocation(PPDMIBLOCKPORT pInterface, const char **ppcszController,
693 uint32_t *piInstance, uint32_t *piLUN)
694{
695 PDRVSCSI pThis = PDMIBLOCKPORT_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->pDrvBlock)
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->pDrvBlockAsync)
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->pDrvBlockAsync)
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->pDrvBlockAsync)
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->pDrvBlock = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCK);
969 if (!pThis->pDrvBlock)
970 {
971 AssertMsgFailed(("Configuration error: No block interface!\n"));
972 return VERR_PDM_MISSING_INTERFACE;
973 }
974 pThis->pDrvBlockBios = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKBIOS);
975 if (!pThis->pDrvBlockBios)
976 {
977 AssertMsgFailed(("Configuration error: No block BIOS interface!\n"));
978 return VERR_PDM_MISSING_INTERFACE;
979 }
980
981 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
982
983 /* Try to get the optional async block interface. */
984 pThis->pDrvBlockAsync = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKASYNC);
985
986 PDMBLOCKTYPE enmType = pThis->pDrvBlock->pfnGetType(pThis->pDrvBlock);
987 VSCSILUNTYPE enmLunType;
988 switch (enmType)
989 {
990 case PDMBLOCKTYPE_HARD_DISK:
991 enmLunType = VSCSILUNTYPE_SBC;
992 break;
993 case PDMBLOCKTYPE_CDROM:
994 case PDMBLOCKTYPE_DVD:
995 enmLunType = VSCSILUNTYPE_MMC;
996 break;
997 default:
998 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
999 N_("Only hard disks and CD/DVD-ROMs are currently supported as SCSI devices (enmType=%d)"),
1000 enmType);
1001 }
1002 if ( ( enmType == PDMBLOCKTYPE_DVD
1003 || enmType == PDMBLOCKTYPE_CDROM)
1004 && !pThis->pDrvMount)
1005 {
1006 AssertMsgFailed(("Internal error: cdrom without a mountable interface\n"));
1007 return VERR_INTERNAL_ERROR;
1008 }
1009
1010 /* Create VSCSI device and LUN. */
1011 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
1012 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSectorSize = drvscsiGetSectorSize;
1013 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
1014 pThis->VScsiIoCallbacks.pfnVScsiLunGetFeatureFlags = drvscsiGetFeatureFlags;
1015 pThis->VScsiIoCallbacks.pfnVScsiLunMediumSetLock = drvscsiSetLock;
1016
1017 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis);
1018 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n"), rc);
1019 rc = VSCSILunCreate(&pThis->hVScsiLun, enmLunType, &pThis->VScsiIoCallbacks,
1020 pThis);
1021 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n"), rc);
1022 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
1023 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
1024
1025 //@todo: This is a very hacky way of telling the LUN whether a medium was mounted.
1026 // The mount/unmount interface doesn't work in a very sensible manner!
1027 if (pThis->pDrvMount)
1028 {
1029 if (pThis->pDrvBlock->pfnGetSize(pThis->pDrvBlock))
1030 {
1031 rc = VINF_SUCCESS; VSCSILunMountNotify(pThis->hVScsiLun);
1032 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being mounted\n"), rc);
1033 }
1034 else
1035 {
1036 rc = VINF_SUCCESS; VSCSILunUnmountNotify(pThis->hVScsiLun);
1037 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being unmounted\n"), rc);
1038 }
1039 }
1040
1041 /* Register statistics counter. */
1042 /** @todo aeichner: Find a way to put the instance number of the attached
1043 * controller device when we support more than one controller of the same type.
1044 * At the moment we have the 0 hardcoded. */
1045 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1046 "Amount of data read.", "/Devices/SCSI0/%d/ReadBytes", pDrvIns->iInstance);
1047 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
1048 "Amount of data written.", "/Devices/SCSI0/%d/WrittenBytes", pDrvIns->iInstance);
1049
1050 pThis->StatIoDepth = 0;
1051
1052 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
1053 "Number of active tasks.", "/Devices/SCSI0/%d/IoDepth", pDrvIns->iInstance);
1054
1055 if (!pThis->pDrvBlockAsync)
1056 {
1057 /* Create request queue. */
1058 rc = RTReqQueueCreate(&pThis->hQueueRequests);
1059 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create request queue rc=%Rrc\n"), 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);
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->pDrvBlock->pfnDiscard
1071 || ( pThis->pDrvBlockAsync
1072 && pThis->pDrvBlockAsync->pfnStartDiscard))
1073 LogRel(("SCSI#%d: Enabled UNMAP support\n"));
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