VirtualBox

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

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

VSCSI,DrvSCSI: Switch to PDMIMEDIAEX instead of using PDMIMEDIAASYNC

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.3 KB
Line 
1/* $Id: DrvSCSI.cpp 63992 2016-09-25 17:58:56Z 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 PDMIMEDIAEXPORT
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 extended media interface. */
60 PPDMIMEDIAEX pDrvMediaEx;
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 extended media port interface. */
72 PDMIMEDIAEXPORT IPortEx;
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 /** Indicates whether PDMDrvHlpAsyncNotificationCompleted should be called by
89 * any of the dummy functions. */
90 bool volatile fDummySignal;
91 /** Release statistics: number of bytes written. */
92 STAMCOUNTER StatBytesWritten;
93 /** Release statistics: number of bytes read. */
94 STAMCOUNTER StatBytesRead;
95 /** Release statistics: Current I/O depth. */
96 volatile uint32_t StatIoDepth;
97 /** Errors printed in the release log. */
98 unsigned cErrors;
99 /** Mark the drive as having a non-rotational medium (i.e. as a SSD). */
100 bool fNonRotational;
101 /** Medium is readonly */
102 bool fReadonly;
103} DRVSCSI, *PDRVSCSI;
104
105/** Convert a VSCSI I/O request handle to the associated PDMIMEDIAEX I/O request. */
106#define DRVSCSI_VSCSIIOREQ_2_PDMMEDIAEXIOREQ(a_hVScsiIoReq) (*(PPDMMEDIAEXIOREQ)((uint8_t *)(a_hVScsiIoReq) - sizeof(PDMMEDIAEXIOREQ)))
107/** Convert a PDMIMEDIAEX I/O additional request memory to a VSCSI I/O request. */
108#define DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(a_pvIoReqAlloc) ((VSCSIIOREQ)((uint8_t *)(a_pvIoReqAlloc) + sizeof(PDMMEDIAEXIOREQ)))
109
110/**
111 * Returns whether the given status code indicates a non fatal error.
112 *
113 * @returns True if the error can be fixed by the user after the VM was suspended.
114 * False if not and the error should be reported to the guest.
115 * @param rc The status code to check.
116 */
117DECLINLINE(bool) drvscsiIsRedoPossible(int rc)
118{
119 if ( rc == VERR_DISK_FULL
120 || rc == VERR_FILE_TOO_BIG
121 || rc == VERR_BROKEN_PIPE
122 || rc == VERR_NET_CONNECTION_REFUSED
123 || rc == VERR_VD_DEK_MISSING)
124 return true;
125
126 return false;
127}
128
129/* -=-=-=-=- VScsiIoCallbacks -=-=-=-=- */
130
131/**
132 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqAllocSizeSet}
133 */
134static DECLCALLBACK(int) drvscsiReqAllocSizeSet(VSCSILUN hVScsiLun, void *pvScsiLunUser, size_t cbVScsiIoReqAlloc)
135{
136 RT_NOREF(hVScsiLun);
137 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
138
139 /* We need to store the I/O request handle so we can get it when VSCSI queues an I/O request. */
140 return pThis->pDrvMediaEx->pfnIoReqAllocSizeSet(pThis->pDrvMediaEx, cbVScsiIoReqAlloc + sizeof(PDMMEDIAEXIOREQ));
141}
142
143/**
144 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqAlloc}
145 */
146static DECLCALLBACK(int) drvscsiReqAlloc(VSCSILUN hVScsiLun, void *pvScsiLunUser,
147 uint64_t u64Tag, PVSCSIIOREQ phVScsiIoReq)
148{
149 RT_NOREF(hVScsiLun);
150 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
151 PDMMEDIAEXIOREQ hIoReq;
152 void *pvIoReqAlloc;
153 int rc = pThis->pDrvMediaEx->pfnIoReqAlloc(pThis->pDrvMediaEx, &hIoReq, &pvIoReqAlloc, u64Tag,
154 PDMIMEDIAEX_F_DEFAULT);
155 if (RT_SUCCESS(rc))
156 {
157 PPDMMEDIAEXIOREQ phIoReq = (PPDMMEDIAEXIOREQ)pvIoReqAlloc;
158
159 *phIoReq = hIoReq;
160 *phVScsiIoReq = (VSCSIIOREQ)(phIoReq + 1);
161 }
162
163 return rc;
164}
165
166/**
167 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqFree}
168 */
169static DECLCALLBACK(int) drvscsiReqFree(VSCSILUN hVScsiLun, void *pvScsiLunUser, VSCSIIOREQ hVScsiIoReq)
170{
171 RT_NOREF(hVScsiLun);
172 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
173 PDMMEDIAEXIOREQ hIoReq = DRVSCSI_VSCSIIOREQ_2_PDMMEDIAEXIOREQ(hVScsiIoReq);
174
175 return pThis->pDrvMediaEx->pfnIoReqFree(pThis->pDrvMediaEx, hIoReq);
176}
177
178/**
179 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunMediumGetSize}
180 */
181static DECLCALLBACK(int) drvscsiGetSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint64_t *pcbSize)
182{
183 RT_NOREF(hVScsiLun);
184 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
185
186 *pcbSize = pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
187
188 return VINF_SUCCESS;
189}
190
191/**
192 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunMediumGetSectorSize}
193 */
194static DECLCALLBACK(int) drvscsiGetSectorSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint32_t *pcbSectorSize)
195{
196 RT_NOREF(hVScsiLun);
197 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
198
199 *pcbSectorSize = pThis->pDrvMedia->pfnGetSectorSize(pThis->pDrvMedia);
200
201 return VINF_SUCCESS;
202}
203
204/**
205 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunMediumSetLock}
206 */
207static DECLCALLBACK(int) drvscsiSetLock(VSCSILUN hVScsiLun, void *pvScsiLunUser, bool fLocked)
208{
209 RT_NOREF(hVScsiLun);
210 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
211
212 if (fLocked)
213 pThis->pDrvMount->pfnLock(pThis->pDrvMount);
214 else
215 pThis->pDrvMount->pfnUnlock(pThis->pDrvMount);
216
217 return VINF_SUCCESS;
218}
219
220/**
221 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunReqTransferEnqueue}
222 */
223static DECLCALLBACK(int) drvscsiReqTransferEnqueue(VSCSILUN hVScsiLun, void *pvScsiLunUser, VSCSIIOREQ hVScsiIoReq)
224{
225 RT_NOREF(hVScsiLun);
226 int rc = VINF_SUCCESS;
227 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
228 PDMMEDIAEXIOREQ hIoReq = DRVSCSI_VSCSIIOREQ_2_PDMMEDIAEXIOREQ(hVScsiIoReq);
229
230 LogFlowFunc(("Enqueuing hVScsiIoReq=%#p\n", hVScsiIoReq));
231
232 VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
233 switch (enmTxDir)
234 {
235 case VSCSIIOREQTXDIR_FLUSH:
236 {
237 rc = pThis->pDrvMediaEx->pfnIoReqFlush(pThis->pDrvMediaEx, hIoReq);
238 if ( RT_FAILURE(rc)
239 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
240 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
241 pThis->pDrvIns->iInstance, rc));
242 break;
243 }
244 case VSCSIIOREQTXDIR_UNMAP:
245 {
246 PCRTRANGE paRanges;
247 unsigned cRanges;
248
249 rc = VSCSIIoReqUnmapParamsGet(hVScsiIoReq, &paRanges, &cRanges);
250 AssertRC(rc);
251
252 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
253 rc = pThis->pDrvMediaEx->pfnIoReqDiscard(pThis->pDrvMediaEx, hIoReq, paRanges, cRanges);
254 if ( RT_FAILURE(rc)
255 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
256 LogRel(("SCSI#%u: Discard returned rc=%Rrc\n",
257 pThis->pDrvIns->iInstance, rc));
258 break;
259 }
260 case VSCSIIOREQTXDIR_READ:
261 case VSCSIIOREQTXDIR_WRITE:
262 {
263 uint64_t uOffset = 0;
264 size_t cbTransfer = 0;
265 size_t cbSeg = 0;
266 PCRTSGSEG paSeg = NULL;
267 unsigned cSeg = 0;
268
269 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
270 &cSeg, &cbSeg, &paSeg);
271 AssertRC(rc);
272
273 if (enmTxDir == VSCSIIOREQTXDIR_READ)
274 {
275 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
276 rc = pThis->pDrvMediaEx->pfnIoReqRead(pThis->pDrvMediaEx, hIoReq, uOffset, cbTransfer);
277 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbTransfer);
278 }
279 else
280 {
281 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
282 rc = pThis->pDrvMediaEx->pfnIoReqWrite(pThis->pDrvMediaEx, hIoReq, uOffset, cbTransfer);
283 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbTransfer);
284 }
285
286 if ( RT_FAILURE(rc)
287 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
288 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
289 pThis->pDrvIns->iInstance,
290 enmTxDir == VSCSIIOREQTXDIR_READ
291 ? "Read"
292 : "Write",
293 uOffset,
294 cbTransfer, rc));
295 break;
296 }
297 default:
298 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
299 }
300
301 if (rc == VINF_SUCCESS)
302 {
303 if (enmTxDir == VSCSIIOREQTXDIR_READ)
304 pThis->pLed->Actual.s.fReading = 0;
305 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
306 pThis->pLed->Actual.s.fWriting = 0;
307 else
308 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
309
310 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS, false);
311 rc = VINF_SUCCESS;
312 }
313 else if (rc == VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
314 rc = VINF_SUCCESS;
315 else if (RT_FAILURE(rc))
316 {
317 if (enmTxDir == VSCSIIOREQTXDIR_READ)
318 pThis->pLed->Actual.s.fReading = 0;
319 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
320 pThis->pLed->Actual.s.fWriting = 0;
321 else
322 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
323
324 VSCSIIoReqCompleted(hVScsiIoReq, rc, drvscsiIsRedoPossible(rc));
325 rc = VINF_SUCCESS;
326 }
327 else
328 AssertMsgFailed(("Invalid return code rc=%Rrc\n", rc));
329
330 return rc;
331}
332
333/**
334 * @interface_method_impl{VSCSILUNIOCALLBACKS,pfnVScsiLunGetFeatureFlags}
335 */
336static DECLCALLBACK(int) drvscsiGetFeatureFlags(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint64_t *pfFeatures)
337{
338 RT_NOREF(hVScsiLun);
339 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
340
341 *pfFeatures = 0;
342
343 uint32_t fFeatures = 0;
344 int rc = pThis->pDrvMediaEx->pfnQueryFeatures(pThis->pDrvMediaEx, &fFeatures);
345 if (RT_SUCCESS(rc) && (fFeatures & PDMIMEDIAEX_FEATURE_F_DISCARD))
346 *pfFeatures |= VSCSI_LUN_FEATURE_UNMAP;
347
348 if (pThis->fNonRotational)
349 *pfFeatures |= VSCSI_LUN_FEATURE_NON_ROTATIONAL;
350
351 if (pThis->fReadonly)
352 *pfFeatures |= VSCSI_LUN_FEATURE_READONLY;
353
354 return VINF_SUCCESS;
355}
356
357
358/* -=-=-=-=- IPortEx -=-=-=-=- */
359
360/**
361 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
362 */
363static DECLCALLBACK(int) drvscsiIoReqCompleteNotify(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
364 void *pvIoReqAlloc, int rcReq)
365{
366 RT_NOREF1(hIoReq);
367
368 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IPortEx);
369 VSCSIIOREQ hVScsiIoReq = (VSCSIIOREQ)((uint8_t *)pvIoReqAlloc + sizeof(PDMMEDIAEXIOREQ));
370 VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
371
372 LogFlowFunc(("Request hVScsiIoReq=%#p completed\n", hVScsiIoReq));
373
374 if (enmTxDir == VSCSIIOREQTXDIR_READ)
375 pThis->pLed->Actual.s.fReading = 0;
376 else if ( enmTxDir == VSCSIIOREQTXDIR_WRITE
377 || enmTxDir == VSCSIIOREQTXDIR_UNMAP)
378 pThis->pLed->Actual.s.fWriting = 0;
379 else
380 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
381
382 if (RT_SUCCESS(rcReq))
383 VSCSIIoReqCompleted(hVScsiIoReq, rcReq, false /* fRedoPossible */);
384 else
385 {
386 pThis->cErrors++;
387 if (pThis->cErrors < MAX_LOG_REL_ERRORS)
388 {
389 if (enmTxDir == VSCSIIOREQTXDIR_FLUSH)
390 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
391 pThis->pDrvIns->iInstance, rcReq));
392 else if (enmTxDir == VSCSIIOREQTXDIR_UNMAP)
393 LogRel(("SCSI#%u: Unmap returned rc=%Rrc\n",
394 pThis->pDrvIns->iInstance, rcReq));
395 else
396 {
397 uint64_t uOffset = 0;
398 size_t cbTransfer = 0;
399 size_t cbSeg = 0;
400 PCRTSGSEG paSeg = NULL;
401 unsigned cSeg = 0;
402
403 VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
404 &cSeg, &cbSeg, &paSeg);
405
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, rcReq));
413 }
414 }
415
416 VSCSIIoReqCompleted(hVScsiIoReq, rcReq, drvscsiIsRedoPossible(rcReq));
417 }
418
419 return VINF_SUCCESS;
420}
421
422/**
423 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyFromBuf}
424 */
425static DECLCALLBACK(int) drvscsiIoReqCopyFromBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
426 void *pvIoReqAlloc, uint32_t offDst, PRTSGBUF pSgBuf,
427 size_t cbCopy)
428{
429 RT_NOREF2(pInterface, hIoReq);
430
431 VSCSIIOREQ hVScsiIoReq = DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(pvIoReqAlloc);
432 uint64_t uOffset = 0;
433 size_t cbTransfer = 0;
434 size_t cbSeg = 0;
435 PCRTSGSEG paSeg = NULL;
436 unsigned cSeg = 0;
437 size_t cbCopied = 0;
438
439 int rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg, &paSeg);
440 if (RT_SUCCESS(rc))
441 {
442 RTSGBUF SgBuf;
443 RTSgBufInit(&SgBuf, paSeg, cSeg);
444
445 RTSgBufAdvance(&SgBuf, offDst);
446 cbCopied = RTSgBufCopy(&SgBuf, pSgBuf, cbCopy);
447 }
448
449 return cbCopied == cbCopy ? VINF_SUCCESS : VERR_PDM_MEDIAEX_IOBUF_OVERFLOW;
450}
451
452/**
453 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyToBuf}
454 */
455static DECLCALLBACK(int) drvscsiIoReqCopyToBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
456 void *pvIoReqAlloc, uint32_t offSrc, PRTSGBUF pSgBuf,
457 size_t cbCopy)
458{
459 RT_NOREF2(pInterface, hIoReq);
460
461 VSCSIIOREQ hVScsiIoReq = DRVSCSI_PDMMEDIAEXIOREQ_2_VSCSIIOREQ(pvIoReqAlloc);
462 uint64_t uOffset = 0;
463 size_t cbTransfer = 0;
464 size_t cbSeg = 0;
465 PCRTSGSEG paSeg = NULL;
466 unsigned cSeg = 0;
467 size_t cbCopied = 0;
468
469 int rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg, &paSeg);
470 if (RT_SUCCESS(rc))
471 {
472 RTSGBUF SgBuf;
473 RTSgBufInit(&SgBuf, paSeg, cSeg);
474
475 RTSgBufAdvance(&SgBuf, offSrc);
476 cbCopied = RTSgBufCopy(pSgBuf, &SgBuf, cbCopy);
477 }
478
479 return cbCopied == cbCopy ? VINF_SUCCESS : VERR_PDM_MEDIAEX_IOBUF_UNDERRUN;
480}
481
482/**
483 * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
484 */
485static DECLCALLBACK(void) drvscsiIoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
486 void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
487{
488 RT_NOREF4(pInterface, hIoReq, pvIoReqAlloc, enmState);
489 AssertLogRelMsgFailed(("This should not be hit because I/O requests should not be suspended\n"));
490}
491
492static DECLCALLBACK(void) drvscsiVScsiReqCompleted(VSCSIDEVICE hVScsiDevice, void *pVScsiDeviceUser,
493 void *pVScsiReqUser, int rcScsiCode, bool fRedoPossible, int rcReq)
494{
495 RT_NOREF(hVScsiDevice);
496 PDRVSCSI pThis = (PDRVSCSI)pVScsiDeviceUser;
497
498 ASMAtomicDecU32(&pThis->StatIoDepth);
499
500 pThis->pDevScsiPort->pfnSCSIRequestCompleted(pThis->pDevScsiPort, (PPDMSCSIREQUEST)pVScsiReqUser,
501 rcScsiCode, fRedoPossible, rcReq);
502
503 if (RT_UNLIKELY(pThis->fDummySignal) && !pThis->StatIoDepth)
504 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
505}
506
507/* -=-=-=-=- ISCSIConnector -=-=-=-=- */
508
509#ifdef DEBUG
510/**
511 * Dumps a SCSI request structure for debugging purposes.
512 *
513 * @returns nothing.
514 * @param pRequest Pointer to the request to dump.
515 */
516static void drvscsiDumpScsiRequest(PPDMSCSIREQUEST pRequest)
517{
518 Log(("Dump for pRequest=%#p Command: %s\n", pRequest, SCSICmdText(pRequest->pbCDB[0])));
519 Log(("cbCDB=%u\n", pRequest->cbCDB));
520 for (uint32_t i = 0; i < pRequest->cbCDB; i++)
521 Log(("pbCDB[%u]=%#x\n", i, pRequest->pbCDB[i]));
522 Log(("cbScatterGather=%u\n", pRequest->cbScatterGather));
523 Log(("cScatterGatherEntries=%u\n", pRequest->cScatterGatherEntries));
524 /* Print all scatter gather entries. */
525 for (uint32_t i = 0; i < pRequest->cScatterGatherEntries; i++)
526 {
527 Log(("ScatterGatherEntry[%u].cbSeg=%u\n", i, pRequest->paScatterGatherHead[i].cbSeg));
528 Log(("ScatterGatherEntry[%u].pvSeg=%#p\n", i, pRequest->paScatterGatherHead[i].pvSeg));
529 }
530 Log(("pvUser=%#p\n", pRequest->pvUser));
531}
532#endif
533
534/** @interface_method_impl{PDMISCSICONNECTOR,pfnSCSIRequestSend} */
535static DECLCALLBACK(int) drvscsiRequestSend(PPDMISCSICONNECTOR pInterface, PPDMSCSIREQUEST pSCSIRequest)
536{
537 int rc;
538 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, ISCSIConnector);
539 VSCSIREQ hVScsiReq;
540
541#ifdef DEBUG
542 drvscsiDumpScsiRequest(pSCSIRequest);
543#endif
544
545 rc = VSCSIDeviceReqCreate(pThis->hVScsiDevice, &hVScsiReq,
546 pSCSIRequest->uLogicalUnit,
547 pSCSIRequest->pbCDB,
548 pSCSIRequest->cbCDB,
549 pSCSIRequest->cbScatterGather,
550 pSCSIRequest->cScatterGatherEntries,
551 pSCSIRequest->paScatterGatherHead,
552 pSCSIRequest->pbSenseBuffer,
553 pSCSIRequest->cbSenseBuffer,
554 pSCSIRequest);
555 if (RT_FAILURE(rc))
556 return rc;
557
558 ASMAtomicIncU32(&pThis->StatIoDepth);
559 rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, hVScsiReq);
560
561 return rc;
562}
563
564/** @interface_method_impl{PDMISCSICONNECTOR,pfnQueryLUNType} */
565static DECLCALLBACK(int) drvscsiQueryLUNType(PPDMISCSICONNECTOR pInterface, uint32_t iLun, PPDMSCSILUNTYPE pLunType)
566{
567 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, ISCSIConnector);
568 VSCSILUNTYPE enmLunType;
569
570 int rc = VSCSIDeviceLunQueryType(pThis->hVScsiDevice, iLun, &enmLunType);
571 if (RT_FAILURE(rc))
572 return rc;
573
574 switch (enmLunType)
575 {
576 case VSCSILUNTYPE_SBC: *pLunType = PDMSCSILUNTYPE_SBC; break;
577 case VSCSILUNTYPE_MMC: *pLunType = PDMSCSILUNTYPE_MMC; break;
578 case VSCSILUNTYPE_SSC: *pLunType = PDMSCSILUNTYPE_SSC; break;
579 default: *pLunType = PDMSCSILUNTYPE_INVALID;
580 }
581
582 return rc;
583}
584
585/* -=-=-=-=- IBase -=-=-=-=- */
586
587/**
588 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
589 */
590static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
591{
592 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
593 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
594
595 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, pThis->pDrvMount);
596 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
597 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector);
598 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pThis->IPort);
599 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pThis->IMountNotify);
600 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pThis->IPortEx);
601 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, pThis->pDrvMedia);
602 return NULL;
603}
604
605static DECLCALLBACK(int) drvscsiQueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
606 uint32_t *piInstance, uint32_t *piLUN)
607{
608 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IPort);
609
610 return pThis->pDevScsiPort->pfnQueryDeviceLocation(pThis->pDevScsiPort, ppcszController,
611 piInstance, piLUN);
612}
613
614/**
615 * Called when media is mounted.
616 *
617 * @param pInterface Pointer to the interface structure containing the called function pointer.
618 */
619static DECLCALLBACK(void) drvscsiMountNotify(PPDMIMOUNTNOTIFY pInterface)
620{
621 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMountNotify);
622 LogFlowFunc(("mounting LUN#%p\n", pThis->hVScsiLun));
623
624 /* Ignore the call if we're called while being attached. */
625 if (!pThis->pDrvMedia)
626 return;
627
628 /* Let the LUN know that a medium was mounted. */
629 VSCSILunMountNotify(pThis->hVScsiLun);
630}
631
632/**
633 * Called when media is unmounted
634 *
635 * @param pInterface Pointer to the interface structure containing the called function pointer.
636 */
637static DECLCALLBACK(void) drvscsiUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
638{
639 PDRVSCSI pThis = RT_FROM_MEMBER(pInterface, DRVSCSI, IMountNotify);
640 LogFlowFunc(("unmounting LUN#%p\n", pThis->hVScsiLun));
641
642 /* Let the LUN know that the medium was unmounted. */
643 VSCSILunUnmountNotify(pThis->hVScsiLun);
644}
645
646/**
647 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
648 *
649 * @param pDrvIns The driver instance.
650 * @param pfnAsyncNotify The async callback.
651 */
652static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
653{
654 RT_NOREF1(pfnAsyncNotify);
655
656 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
657
658 if (pThis->StatIoDepth > 0)
659 ASMAtomicWriteBool(&pThis->fDummySignal, true);
660}
661
662/**
663 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
664 *
665 * @returns true if we've quiesced, false if we're still working.
666 * @param pDrvIns The driver instance.
667 */
668static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
669{
670 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
671
672 if (pThis->StatIoDepth > 0)
673 return false;
674 else
675 return true;
676}
677
678/**
679 * @copydoc FNPDMDRVPOWEROFF
680 */
681static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
682{
683 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
684}
685
686/**
687 * @copydoc FNPDMDRVSUSPEND
688 */
689static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
690{
691 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
692}
693
694/**
695 * Callback employed by drvscsiReset.
696 *
697 * @returns true if we've quiesced, false if we're still working.
698 * @param pDrvIns The driver instance.
699 */
700static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
701{
702 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
703
704 if (pThis->StatIoDepth > 0)
705 return false;
706 else
707 return true;
708}
709
710/**
711 * @copydoc FNPDMDRVRESET
712 */
713static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
714{
715 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
716}
717
718/**
719 * Destruct a driver instance.
720 *
721 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
722 * resources can be freed correctly.
723 *
724 * @param pDrvIns The driver instance data.
725 */
726static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
727{
728 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
729 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
730
731 /* Free the VSCSI device and LUN handle. */
732 if (pThis->hVScsiDevice)
733 {
734 VSCSILUN hVScsiLun;
735 int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
736 AssertRC(rc);
737
738 Assert(hVScsiLun == pThis->hVScsiLun);
739 rc = VSCSILunDestroy(hVScsiLun);
740 AssertRC(rc);
741 rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
742 AssertRC(rc);
743
744 pThis->hVScsiDevice = NULL;
745 pThis->hVScsiLun = NULL;
746 }
747
748 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatBytesRead);
749 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatBytesWritten);
750 PDMDrvHlpSTAMDeregister(pDrvIns, (void *)&pThis->StatIoDepth);
751}
752
753/**
754 * Construct a block driver instance.
755 *
756 * @copydoc FNPDMDRVCONSTRUCT
757 */
758static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
759{
760 int rc = VINF_SUCCESS;
761 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
762 LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
763 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
764
765 /*
766 * Initialize the instance data.
767 */
768 pThis->pDrvIns = pDrvIns;
769 pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsiRequestSend;
770 pThis->ISCSIConnector.pfnQueryLUNType = drvscsiQueryLUNType;
771
772 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
773
774 pThis->IMountNotify.pfnMountNotify = drvscsiMountNotify;
775 pThis->IMountNotify.pfnUnmountNotify = drvscsiUnmountNotify;
776 pThis->IPort.pfnQueryDeviceLocation = drvscsiQueryDeviceLocation;
777 pThis->IPortEx.pfnIoReqCompleteNotify = drvscsiIoReqCompleteNotify;
778 pThis->IPortEx.pfnIoReqCopyFromBuf = drvscsiIoReqCopyFromBuf;
779 pThis->IPortEx.pfnIoReqCopyToBuf = drvscsiIoReqCopyToBuf;
780 pThis->IPortEx.pfnIoReqStateChanged = drvscsiIoReqStateChanged;
781
782 /* Query the SCSI port interface above. */
783 pThis->pDevScsiPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISCSIPORT);
784 AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
785
786 /* Query the optional LED interface above. */
787 pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
788 if (pThis->pLedPort != NULL)
789 {
790 /* Get The Led. */
791 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
792 if (RT_FAILURE(rc))
793 pThis->pLed = &pThis->Led;
794 }
795 else
796 pThis->pLed = &pThis->Led;
797
798 /*
799 * Validate and read configuration.
800 */
801 if (!CFGMR3AreValuesValid(pCfg, "NonRotationalMedium\0Readonly\0"))
802 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
803 N_("SCSI configuration error: unknown option specified"));
804
805 rc = CFGMR3QueryBoolDef(pCfg, "NonRotationalMedium", &pThis->fNonRotational, false);
806 if (RT_FAILURE(rc))
807 return PDMDRV_SET_ERROR(pDrvIns, rc,
808 N_("SCSI configuration error: failed to read \"NonRotationalMedium\" as boolean"));
809
810 rc = CFGMR3QueryBoolDef(pCfg, "Readonly", &pThis->fReadonly, false);
811 if (RT_FAILURE(rc))
812 return PDMDRV_SET_ERROR(pDrvIns, rc,
813 N_("SCSI configuration error: failed to read \"Readonly\" as boolean"));
814
815 /*
816 * Try attach driver below and query it's block interface.
817 */
818 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
819 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
820
821 /*
822 * Query the block and blockbios interfaces.
823 */
824 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIA);
825 AssertMsgReturn(VALID_PTR(pThis->pDrvMedia), ("VSCSI configuration error: No media interface!\n"),
826 VERR_PDM_MISSING_INTERFACE);
827
828 /* Query the extended media interface. */
829 pThis->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMEDIAEX);
830 AssertMsgReturn(VALID_PTR(pThis->pDrvMediaEx), ("VSCSI configuration error: No extended media interface!\n"),
831 VERR_PDM_MISSING_INTERFACE);
832
833 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
834
835 PDMMEDIATYPE enmType = pThis->pDrvMedia->pfnGetType(pThis->pDrvMedia);
836 VSCSILUNTYPE enmLunType;
837 switch (enmType)
838 {
839 case PDMMEDIATYPE_HARD_DISK:
840 enmLunType = VSCSILUNTYPE_SBC;
841 break;
842 case PDMMEDIATYPE_CDROM:
843 case PDMMEDIATYPE_DVD:
844 enmLunType = VSCSILUNTYPE_MMC;
845 break;
846 default:
847 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
848 N_("Only hard disks and CD/DVD-ROMs are currently supported as SCSI devices (enmType=%d)"),
849 enmType);
850 }
851 if ( ( enmType == PDMMEDIATYPE_DVD
852 || enmType == PDMMEDIATYPE_CDROM)
853 && !pThis->pDrvMount)
854 {
855 AssertMsgFailed(("Internal error: cdrom without a mountable interface\n"));
856 return VERR_INTERNAL_ERROR;
857 }
858
859 /* Create VSCSI device and LUN. */
860 pThis->VScsiIoCallbacks.pfnVScsiLunReqAllocSizeSet = drvscsiReqAllocSizeSet;
861 pThis->VScsiIoCallbacks.pfnVScsiLunReqAlloc = drvscsiReqAlloc;
862 pThis->VScsiIoCallbacks.pfnVScsiLunReqFree = drvscsiReqFree;
863 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
864 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSectorSize = drvscsiGetSectorSize;
865 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
866 pThis->VScsiIoCallbacks.pfnVScsiLunGetFeatureFlags = drvscsiGetFeatureFlags;
867 pThis->VScsiIoCallbacks.pfnVScsiLunMediumSetLock = drvscsiSetLock;
868
869 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis);
870 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n", rc), rc);
871 rc = VSCSILunCreate(&pThis->hVScsiLun, enmLunType, &pThis->VScsiIoCallbacks,
872 pThis);
873 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n", rc), rc);
874 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
875 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
876
877 /// @todo This is a very hacky way of telling the LUN whether a medium was mounted.
878 // The mount/unmount interface doesn't work in a very sensible manner!
879 if (pThis->pDrvMount)
880 {
881 if (pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia))
882 {
883 rc = VINF_SUCCESS; VSCSILunMountNotify(pThis->hVScsiLun);
884 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being mounted\n"), rc);
885 }
886 else
887 {
888 rc = VINF_SUCCESS; VSCSILunUnmountNotify(pThis->hVScsiLun);
889 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to notify the LUN of media being unmounted\n"), rc);
890 }
891 }
892
893 const char *pszCtrl = NULL;
894 uint32_t iCtrlInstance = 0;
895 uint32_t iCtrlLun = 0;
896
897 rc = pThis->pDevScsiPort->pfnQueryDeviceLocation(pThis->pDevScsiPort, &pszCtrl, &iCtrlInstance, &iCtrlLun);
898 if (RT_SUCCESS(rc))
899 {
900 const char *pszCtrlId = strcmp(pszCtrl, "Msd") == 0 ? "USB"
901 : strcmp(pszCtrl, "lsilogicsas") == 0 ? "SAS"
902 : "SCSI";
903 /* Register statistics counter. */
904 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
905 "Amount of data read.", "/Devices/%s%u/%u/ReadBytes", pszCtrlId, iCtrlInstance, iCtrlLun);
906 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
907 "Amount of data written.", "/Devices/%s%u/%u/WrittenBytes", pszCtrlId, iCtrlInstance, iCtrlLun);
908 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
909 "Number of active tasks.", "/Devices/%s%u/%u/IoDepth", pszCtrlId, iCtrlInstance, iCtrlLun);
910 }
911
912 pThis->StatIoDepth = 0;
913
914 uint32_t fFeatures = 0;
915 rc = pThis->pDrvMediaEx->pfnQueryFeatures(pThis->pDrvMediaEx, &fFeatures);
916 if (RT_FAILURE(rc))
917 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
918 N_("VSCSI configuration error: Failed to query features of device"));
919 if (fFeatures & PDMIMEDIAEX_FEATURE_F_DISCARD)
920 LogRel(("SCSI#%d: Enabled UNMAP support\n", pDrvIns->iInstance));
921
922 return VINF_SUCCESS;
923}
924
925/**
926 * SCSI driver registration record.
927 */
928const PDMDRVREG g_DrvSCSI =
929{
930 /* u32Version */
931 PDM_DRVREG_VERSION,
932 /* szName */
933 "SCSI",
934 /* szRCMod */
935 "",
936 /* szR0Mod */
937 "",
938 /* pszDescription */
939 "Generic SCSI driver.",
940 /* fFlags */
941 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
942 /* fClass. */
943 PDM_DRVREG_CLASS_SCSI,
944 /* cMaxInstances */
945 ~0U,
946 /* cbInstance */
947 sizeof(DRVSCSI),
948 /* pfnConstruct */
949 drvscsiConstruct,
950 /* pfnDestruct */
951 drvscsiDestruct,
952 /* pfnRelocate */
953 NULL,
954 /* pfnIOCtl */
955 NULL,
956 /* pfnPowerOn */
957 NULL,
958 /* pfnReset */
959 drvscsiReset,
960 /* pfnSuspend */
961 drvscsiSuspend,
962 /* pfnResume */
963 NULL,
964 /* pfnAttach */
965 NULL,
966 /* pfnDetach */
967 NULL,
968 /* pfnPowerOff */
969 drvscsiPowerOff,
970 /* pfnSoftReset */
971 NULL,
972 /* u32EndVersion */
973 PDM_DRVREG_VERSION
974};
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette