VirtualBox

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

Last change on this file since 39655 was 39566, checked in by vboxsync, 13 years ago

SCSI: Add support for readonly disks

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.8 KB
Line 
1/* $Id: DrvSCSI.cpp 39566 2011-12-09 13:27:31Z vboxsync $ */
2/** @file
3 * VBox storage drivers: Generic SCSI command parser and execution driver
4 */
5
6/*
7 * Copyright (C) 2006-2010 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#if 0 /* these interfaces aren't implemented */
74 /** The mount notify interface. */
75 PDMIMOUNTNOTIFY IMountNotify;
76#endif
77 /** Fallback status LED state for this drive.
78 * This is used in case the device doesn't has a LED interface. */
79 PDMLED Led;
80 /** Pointer to the status LED for this drive. */
81 PPDMLED pLed;
82
83 /** VSCSI device handle. */
84 VSCSIDEVICE hVScsiDevice;
85 /** VSCSI LUN handle. */
86 VSCSILUN hVScsiLun;
87 /** I/O callbacks. */
88 VSCSILUNIOCALLBACKS VScsiIoCallbacks;
89
90 /** The dedicated I/O thread for the non async approach. */
91 PPDMTHREAD pAsyncIOThread;
92 /** Queue for passing the requests to the thread. */
93 RTREQQUEUE hQueueRequests;
94 /** Request that we've left pending on wakeup or reset. */
95 PRTREQ pPendingDummyReq;
96 /** Indicates whether PDMDrvHlpAsyncNotificationCompleted should be called by
97 * any of the dummy functions. */
98 bool volatile fDummySignal;
99 /** Release statistics: number of bytes written. */
100 STAMCOUNTER StatBytesWritten;
101 /** Release statistics: number of bytes read. */
102 STAMCOUNTER StatBytesRead;
103 /** Release statistics: Current I/O depth. */
104 volatile uint32_t StatIoDepth;
105 /** Errors printed in the release log. */
106 unsigned cErrors;
107 /** Mark the drive as having a non-rotational medium (i.e. as a SSD). */
108 bool fNonRotational;
109 /** Medium is readonly */
110 bool fReadonly;
111} DRVSCSI, *PDRVSCSI;
112
113/** Converts a pointer to DRVSCSI::ISCSIConnector to a PDRVSCSI. */
114#define PDMISCSICONNECTOR_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, ISCSIConnector)) )
115/** Converts a pointer to DRVSCSI::IPortAsync to a PDRVSCSI. */
116#define PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface) ( (PDRVSCSI)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSI, IPortAsync)) )
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 return true;
127
128 return false;
129}
130
131static int drvscsiProcessRequestOne(PDRVSCSI pThis, VSCSIIOREQ hVScsiIoReq)
132{
133 int rc = VINF_SUCCESS;
134 VSCSIIOREQTXDIR enmTxDir;
135
136 enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
137
138 switch (enmTxDir)
139 {
140 case VSCSIIOREQTXDIR_FLUSH:
141 {
142 rc = pThis->pDrvBlock->pfnFlush(pThis->pDrvBlock);
143 break;
144 }
145 case VSCSIIOREQTXDIR_READ:
146 case VSCSIIOREQTXDIR_WRITE:
147 {
148 uint64_t uOffset = 0;
149 size_t cbTransfer = 0;
150 size_t cbSeg = 0;
151 PCRTSGSEG paSeg = NULL;
152 unsigned cSeg = 0;
153
154 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer, &cSeg, &cbSeg,
155 &paSeg);
156 AssertRC(rc);
157
158 while (cbTransfer && cSeg)
159 {
160 size_t cbProcess = (cbTransfer < paSeg->cbSeg) ? cbTransfer : paSeg->cbSeg;
161
162 Log(("%s: uOffset=%llu cbProcess=%u\n", __FUNCTION__, uOffset, cbProcess));
163
164 if (enmTxDir == VSCSIIOREQTXDIR_READ)
165 {
166 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
167 rc = pThis->pDrvBlock->pfnRead(pThis->pDrvBlock, uOffset,
168 paSeg->pvSeg, cbProcess);
169 pThis->pLed->Actual.s.fReading = 0;
170 if (RT_FAILURE(rc))
171 break;
172 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbProcess);
173 }
174 else
175 {
176 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
177 rc = pThis->pDrvBlock->pfnWrite(pThis->pDrvBlock, uOffset,
178 paSeg->pvSeg, cbProcess);
179 pThis->pLed->Actual.s.fWriting = 0;
180 if (RT_FAILURE(rc))
181 break;
182 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbProcess);
183 }
184
185 /* Go to the next entry. */
186 uOffset += cbProcess;
187 cbTransfer -= cbProcess;
188 paSeg++;
189 cSeg--;
190 }
191
192 break;
193 }
194 case VSCSIIOREQTXDIR_UNMAP:
195 {
196 PCRTRANGE paRanges;
197 unsigned cRanges;
198
199 rc = VSCSIIoReqUnmapParamsGet(hVScsiIoReq, &paRanges, &cRanges);
200 AssertRC(rc);
201
202 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
203 rc = pThis->pDrvBlock->pfnDiscard(pThis->pDrvBlock, paRanges, cRanges);
204 pThis->pLed->Actual.s.fWriting = 0;
205 break;
206 }
207 default:
208 AssertMsgFailed(("Invalid transfer direction %d\n", enmTxDir));
209 }
210
211 if (RT_SUCCESS(rc))
212 VSCSIIoReqCompleted(hVScsiIoReq, rc, false /* fRedoPossible */);
213 else
214 VSCSIIoReqCompleted(hVScsiIoReq, rc, drvscsiIsRedoPossible(rc));
215
216 return VINF_SUCCESS;
217}
218
219static DECLCALLBACK(int) drvscsiGetSize(VSCSILUN hVScsiLun, void *pvScsiLunUser, uint64_t *pcbSize)
220{
221 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
222
223 *pcbSize = pThis->pDrvBlock->pfnGetSize(pThis->pDrvBlock);
224
225 return VINF_SUCCESS;
226}
227
228static int drvscsiTransferCompleteNotify(PPDMIBLOCKASYNCPORT pInterface, void *pvUser, int rc)
229{
230 PDRVSCSI pThis = PDMIBLOCKASYNCPORT_2_DRVSCSI(pInterface);
231 VSCSIIOREQ hVScsiIoReq = (VSCSIIOREQ)pvUser;
232 VSCSIIOREQTXDIR enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
233
234 LogFlowFunc(("Request hVScsiIoReq=%#p completed\n", hVScsiIoReq));
235
236 if (enmTxDir == VSCSIIOREQTXDIR_READ)
237 pThis->pLed->Actual.s.fReading = 0;
238 else if ( enmTxDir == VSCSIIOREQTXDIR_WRITE
239 || enmTxDir == VSCSIIOREQTXDIR_UNMAP)
240 pThis->pLed->Actual.s.fWriting = 0;
241 else
242 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
243
244 if (RT_SUCCESS(rc))
245 VSCSIIoReqCompleted(hVScsiIoReq, rc, false /* fRedoPossible */);
246 else
247 {
248 pThis->cErrors++;
249 if ( pThis->cErrors < MAX_LOG_REL_ERRORS
250 && enmTxDir == VSCSIIOREQTXDIR_FLUSH)
251 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
252 pThis->pDrvIns->iInstance, rc));
253 else
254 {
255 uint64_t uOffset = 0;
256 size_t cbTransfer = 0;
257 size_t cbSeg = 0;
258 PCRTSGSEG paSeg = NULL;
259 unsigned cSeg = 0;
260
261 VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
262 &cSeg, &cbSeg, &paSeg);
263
264 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
265 pThis->pDrvIns->iInstance,
266 enmTxDir == VSCSIIOREQTXDIR_READ
267 ? "Read"
268 : "Write",
269 uOffset,
270 cbTransfer, rc));
271 }
272
273 VSCSIIoReqCompleted(hVScsiIoReq, rc, drvscsiIsRedoPossible(rc));
274 }
275
276 return VINF_SUCCESS;
277}
278
279static DECLCALLBACK(int) drvscsiReqTransferEnqueue(VSCSILUN hVScsiLun,
280 void *pvScsiLunUser,
281 VSCSIIOREQ hVScsiIoReq)
282{
283 int rc = VINF_SUCCESS;
284 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
285
286 if (pThis->pDrvBlockAsync)
287 {
288 /* async I/O path. */
289 VSCSIIOREQTXDIR enmTxDir;
290
291 LogFlowFunc(("Enqueuing hVScsiIoReq=%#p\n", hVScsiIoReq));
292
293 enmTxDir = VSCSIIoReqTxDirGet(hVScsiIoReq);
294
295 switch (enmTxDir)
296 {
297 case VSCSIIOREQTXDIR_FLUSH:
298 {
299 rc = pThis->pDrvBlockAsync->pfnStartFlush(pThis->pDrvBlockAsync, hVScsiIoReq);
300 if ( RT_FAILURE(rc)
301 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS
302 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
303 LogRel(("SCSI#%u: Flush returned rc=%Rrc\n",
304 pThis->pDrvIns->iInstance, rc));
305 break;
306 }
307 case VSCSIIOREQTXDIR_UNMAP:
308 {
309 PCRTRANGE paRanges;
310 unsigned cRanges;
311
312 rc = VSCSIIoReqUnmapParamsGet(hVScsiIoReq, &paRanges, &cRanges);
313 AssertRC(rc);
314
315 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
316 rc = pThis->pDrvBlockAsync->pfnStartDiscard(pThis->pDrvBlockAsync, paRanges, cRanges, hVScsiIoReq);
317 if ( RT_FAILURE(rc)
318 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS
319 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
320 LogRel(("SCSI#%u: Discard returned rc=%Rrc\n",
321 pThis->pDrvIns->iInstance, rc));
322 break;
323 }
324 case VSCSIIOREQTXDIR_READ:
325 case VSCSIIOREQTXDIR_WRITE:
326 {
327 uint64_t uOffset = 0;
328 size_t cbTransfer = 0;
329 size_t cbSeg = 0;
330 PCRTSGSEG paSeg = NULL;
331 unsigned cSeg = 0;
332
333 rc = VSCSIIoReqParamsGet(hVScsiIoReq, &uOffset, &cbTransfer,
334 &cSeg, &cbSeg, &paSeg);
335 AssertRC(rc);
336
337 if (enmTxDir == VSCSIIOREQTXDIR_READ)
338 {
339 pThis->pLed->Asserted.s.fReading = pThis->pLed->Actual.s.fReading = 1;
340 rc = pThis->pDrvBlockAsync->pfnStartRead(pThis->pDrvBlockAsync, uOffset,
341 paSeg, cSeg, cbTransfer,
342 hVScsiIoReq);
343 STAM_REL_COUNTER_ADD(&pThis->StatBytesRead, cbTransfer);
344 }
345 else
346 {
347 pThis->pLed->Asserted.s.fWriting = pThis->pLed->Actual.s.fWriting = 1;
348 rc = pThis->pDrvBlockAsync->pfnStartWrite(pThis->pDrvBlockAsync, uOffset,
349 paSeg, cSeg, cbTransfer,
350 hVScsiIoReq);
351 STAM_REL_COUNTER_ADD(&pThis->StatBytesWritten, cbTransfer);
352 }
353
354 if ( RT_FAILURE(rc)
355 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS
356 && pThis->cErrors++ < MAX_LOG_REL_ERRORS)
357 LogRel(("SCSI#%u: %s at offset %llu (%u bytes left) returned rc=%Rrc\n",
358 pThis->pDrvIns->iInstance,
359 enmTxDir == VSCSIIOREQTXDIR_READ
360 ? "Read"
361 : "Write",
362 uOffset,
363 cbTransfer, rc));
364 break;
365 }
366 default:
367 AssertMsgFailed(("Invalid transfer direction %u\n", enmTxDir));
368 }
369
370 if (rc == VINF_VD_ASYNC_IO_FINISHED)
371 {
372 if (enmTxDir == VSCSIIOREQTXDIR_READ)
373 pThis->pLed->Actual.s.fReading = 0;
374 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
375 pThis->pLed->Actual.s.fWriting = 0;
376 else
377 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
378
379 VSCSIIoReqCompleted(hVScsiIoReq, VINF_SUCCESS, false);
380 rc = VINF_SUCCESS;
381 }
382 else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
383 rc = VINF_SUCCESS;
384 else if (RT_FAILURE(rc))
385 {
386 if (enmTxDir == VSCSIIOREQTXDIR_READ)
387 pThis->pLed->Actual.s.fReading = 0;
388 else if (enmTxDir == VSCSIIOREQTXDIR_WRITE)
389 pThis->pLed->Actual.s.fWriting = 0;
390 else
391 AssertMsg(enmTxDir == VSCSIIOREQTXDIR_FLUSH, ("Invalid transfer direction %u\n", enmTxDir));
392
393 VSCSIIoReqCompleted(hVScsiIoReq, rc, drvscsiIsRedoPossible(rc));
394 rc = VINF_SUCCESS;
395 }
396 else
397 AssertMsgFailed(("Invalid return code rc=%Rrc\n", rc));
398 }
399 else
400 {
401 /* I/O thread. */
402 rc = RTReqQueueCallEx(pThis->hQueueRequests, NULL, 0, RTREQFLAGS_NO_WAIT,
403 (PFNRT)drvscsiProcessRequestOne, 2, pThis, hVScsiIoReq);
404 }
405
406 return rc;
407}
408
409static DECLCALLBACK(int) drvscsiGetFeatureFlags(VSCSILUN hVScsiLun,
410 void *pvScsiLunUser,
411 uint64_t *pfFeatures)
412{
413 int rc = VINF_SUCCESS;
414 PDRVSCSI pThis = (PDRVSCSI)pvScsiLunUser;
415
416 *pfFeatures = 0;
417
418 if ( pThis->pDrvBlock->pfnDiscard
419 || ( pThis->pDrvBlockAsync
420 && pThis->pDrvBlockAsync->pfnStartDiscard))
421 *pfFeatures |= VSCSI_LUN_FEATURE_UNMAP;
422
423 if (pThis->fNonRotational)
424 *pfFeatures |= VSCSI_LUN_FEATURE_NON_ROTATIONAL;
425
426 if (pThis->fReadonly)
427 *pfFeatures |= VSCSI_LUN_FEATURE_READONLY;
428
429 return VINF_SUCCESS;
430}
431
432static void drvscsiVScsiReqCompleted(VSCSIDEVICE hVScsiDevice, void *pVScsiDeviceUser,
433 void *pVScsiReqUser, int rcScsiCode, bool fRedoPossible,
434 int rcReq)
435{
436 PDRVSCSI pThis = (PDRVSCSI)pVScsiDeviceUser;
437
438 ASMAtomicDecU32(&pThis->StatIoDepth);
439
440 pThis->pDevScsiPort->pfnSCSIRequestCompleted(pThis->pDevScsiPort, (PPDMSCSIREQUEST)pVScsiReqUser,
441 rcScsiCode, fRedoPossible, rcReq);
442
443 if (RT_UNLIKELY(pThis->fDummySignal) && !pThis->StatIoDepth)
444 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
445}
446
447/**
448 * Dummy request function used by drvscsiReset to wait for all pending requests
449 * to complete prior to the device reset.
450 *
451 * @param pThis Pointer to the instance data.
452 * @returns VINF_SUCCESS.
453 */
454static int drvscsiAsyncIOLoopSyncCallback(PDRVSCSI pThis)
455{
456 if (pThis->fDummySignal)
457 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
458 return VINF_SUCCESS;
459}
460
461/**
462 * Request function to wakeup the thread.
463 *
464 * @param pThis Pointer to the instance data.
465 * @returns VWRN_STATE_CHANGED.
466 */
467static int drvscsiAsyncIOLoopWakeupFunc(PDRVSCSI pThis)
468{
469 if (pThis->fDummySignal)
470 PDMDrvHlpAsyncNotificationCompleted(pThis->pDrvIns);
471 return VWRN_STATE_CHANGED;
472}
473
474/**
475 * The thread function which processes the requests asynchronously.
476 *
477 * @returns VBox status code.
478 * @param pDrvIns Pointer to the driver instance data.
479 * @param pThread Pointer to the thread instance data.
480 */
481static int drvscsiAsyncIOLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
482{
483 int rc = VINF_SUCCESS;
484 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
485
486 LogFlowFunc(("Entering async IO loop.\n"));
487
488 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
489 return VINF_SUCCESS;
490
491 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
492 {
493 rc = RTReqQueueProcess(pThis->hQueueRequests, RT_INDEFINITE_WAIT);
494 AssertMsg(rc == VWRN_STATE_CHANGED, ("Left RTReqProcess and error code is not VWRN_STATE_CHANGED rc=%Rrc\n", rc));
495 }
496
497 return VINF_SUCCESS;
498}
499
500/**
501 * Deals with any pending dummy request
502 *
503 * @returns true if no pending dummy request, false if still pending.
504 * @param pThis The instance data.
505 * @param cMillies The number of milliseconds to wait for any
506 * pending request to finish.
507 */
508static bool drvscsiAsyncIOLoopNoPendingDummy(PDRVSCSI pThis, uint32_t cMillies)
509{
510 if (!pThis->pPendingDummyReq)
511 return true;
512 int rc = RTReqWait(pThis->pPendingDummyReq, cMillies);
513 if (RT_FAILURE(rc))
514 return false;
515 RTReqRelease(pThis->pPendingDummyReq);
516 pThis->pPendingDummyReq = NULL;
517 return true;
518}
519
520static int drvscsiAsyncIOLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
521{
522 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
523 PRTREQ pReq;
524 int rc;
525
526 AssertMsgReturn(pThis->hQueueRequests != NIL_RTREQQUEUE, ("hQueueRequests is NULL\n"), VERR_INVALID_STATE);
527
528 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 10000 /* 10 sec */))
529 {
530 LogRel(("drvscsiAsyncIOLoopWakeup#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
531 return VERR_TIMEOUT;
532 }
533
534 rc = RTReqQueueCall(pThis->hQueueRequests, &pReq, 10000 /* 10 sec. */, (PFNRT)drvscsiAsyncIOLoopWakeupFunc, 1, pThis);
535 if (RT_SUCCESS(rc))
536 RTReqRelease(pReq);
537 else
538 {
539 pThis->pPendingDummyReq = pReq;
540 LogRel(("drvscsiAsyncIOLoopWakeup#%u: %Rrc pReq=%p\n", pDrvIns->iInstance, rc, pReq));
541 }
542
543 return rc;
544}
545
546/* -=-=-=-=- ISCSIConnector -=-=-=-=- */
547
548#ifdef DEBUG
549/**
550 * Dumps a SCSI request structure for debugging purposes.
551 *
552 * @returns nothing.
553 * @param pRequest Pointer to the request to dump.
554 */
555static void drvscsiDumpScsiRequest(PPDMSCSIREQUEST pRequest)
556{
557 Log(("Dump for pRequest=%#p Command: %s\n", pRequest, SCSICmdText(pRequest->pbCDB[0])));
558 Log(("cbCDB=%u\n", pRequest->cbCDB));
559 for (uint32_t i = 0; i < pRequest->cbCDB; i++)
560 Log(("pbCDB[%u]=%#x\n", i, pRequest->pbCDB[i]));
561 Log(("cbScatterGather=%u\n", pRequest->cbScatterGather));
562 Log(("cScatterGatherEntries=%u\n", pRequest->cScatterGatherEntries));
563 /* Print all scatter gather entries. */
564 for (uint32_t i = 0; i < pRequest->cScatterGatherEntries; i++)
565 {
566 Log(("ScatterGatherEntry[%u].cbSeg=%u\n", i, pRequest->paScatterGatherHead[i].cbSeg));
567 Log(("ScatterGatherEntry[%u].pvSeg=%#p\n", i, pRequest->paScatterGatherHead[i].pvSeg));
568 }
569 Log(("pvUser=%#p\n", pRequest->pvUser));
570}
571#endif
572
573/** @copydoc PDMISCSICONNECTOR::pfnSCSIRequestSend. */
574static DECLCALLBACK(int) drvscsiRequestSend(PPDMISCSICONNECTOR pInterface, PPDMSCSIREQUEST pSCSIRequest)
575{
576 int rc;
577 PDRVSCSI pThis = PDMISCSICONNECTOR_2_DRVSCSI(pInterface);
578 VSCSIREQ hVScsiReq;
579
580#ifdef DEBUG
581 drvscsiDumpScsiRequest(pSCSIRequest);
582#endif
583
584 rc = VSCSIDeviceReqCreate(pThis->hVScsiDevice, &hVScsiReq,
585 pSCSIRequest->uLogicalUnit,
586 pSCSIRequest->pbCDB,
587 pSCSIRequest->cbCDB,
588 pSCSIRequest->cbScatterGather,
589 pSCSIRequest->cScatterGatherEntries,
590 pSCSIRequest->paScatterGatherHead,
591 pSCSIRequest->pbSenseBuffer,
592 pSCSIRequest->cbSenseBuffer,
593 pSCSIRequest);
594 if (RT_FAILURE(rc))
595 return rc;
596
597 ASMAtomicIncU32(&pThis->StatIoDepth);
598 rc = VSCSIDeviceReqEnqueue(pThis->hVScsiDevice, hVScsiReq);
599
600 return rc;
601}
602
603/* -=-=-=-=- IBase -=-=-=-=- */
604
605/**
606 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
607 */
608static DECLCALLBACK(void *) drvscsiQueryInterface(PPDMIBASE pInterface, const char *pszIID)
609{
610 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
611 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
612
613 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
614 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector);
615 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pThis->IPort);
616 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKASYNCPORT, &pThis->IPortAsync);
617 return NULL;
618}
619
620static DECLCALLBACK(int) drvscsiQueryDeviceLocation(PPDMIBLOCKPORT pInterface, const char **ppcszController,
621 uint32_t *piInstance, uint32_t *piLUN)
622{
623 PDRVSCSI pThis = PDMIBLOCKPORT_2_DRVSCSI(pInterface);
624
625 return pThis->pDevScsiPort->pfnQueryDeviceLocation(pThis->pDevScsiPort, ppcszController,
626 piInstance, piLUN);
627}
628
629/**
630 * Worker for drvscsiReset, drvscsiSuspend and drvscsiPowerOff.
631 *
632 * @param pDrvIns The driver instance.
633 * @param pfnAsyncNotify The async callback.
634 */
635static void drvscsiR3ResetOrSuspendOrPowerOff(PPDMDRVINS pDrvIns, PFNPDMDRVASYNCNOTIFY pfnAsyncNotify)
636{
637 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
638
639 if (!pThis->pDrvBlockAsync)
640 {
641 if (pThis->hQueueRequests != NIL_RTREQQUEUE)
642 return;
643
644 ASMAtomicWriteBool(&pThis->fDummySignal, true);
645 if (drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
646 {
647 if (!RTReqQueueIsBusy(pThis->hQueueRequests))
648 {
649 ASMAtomicWriteBool(&pThis->fDummySignal, false);
650 return;
651 }
652
653 PRTREQ pReq;
654 int rc = RTReqQueueCall(pThis->hQueueRequests, &pReq, 0 /*ms*/, (PFNRT)drvscsiAsyncIOLoopSyncCallback, 1, pThis);
655 if (RT_SUCCESS(rc))
656 {
657 ASMAtomicWriteBool(&pThis->fDummySignal, false);
658 RTReqRelease(pReq);
659 return;
660 }
661
662 pThis->pPendingDummyReq = pReq;
663 }
664 }
665 else
666 {
667 if (pThis->StatIoDepth > 0)
668 {
669 ASMAtomicWriteBool(&pThis->fDummySignal, true);
670 }
671 return;
672 }
673
674 PDMDrvHlpSetAsyncNotification(pDrvIns, pfnAsyncNotify);
675}
676
677/**
678 * Callback employed by drvscsiSuspend and drvscsiPowerOff.
679 *
680 * @returns true if we've quiesced, false if we're still working.
681 * @param pDrvIns The driver instance.
682 */
683static DECLCALLBACK(bool) drvscsiIsAsyncSuspendOrPowerOffDone(PPDMDRVINS pDrvIns)
684{
685 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
686
687 if (pThis->pDrvBlockAsync)
688 {
689 if (pThis->StatIoDepth > 0)
690 return false;
691 else
692 return true;
693 }
694 else
695 {
696 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
697 return false;
698 ASMAtomicWriteBool(&pThis->fDummySignal, false);
699 PDMR3ThreadSuspend(pThis->pAsyncIOThread);
700 return true;
701 }
702}
703
704/**
705 * @copydoc FNPDMDRVPOWEROFF
706 */
707static DECLCALLBACK(void) drvscsiPowerOff(PPDMDRVINS pDrvIns)
708{
709 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
710}
711
712/**
713 * @copydoc FNPDMDRVSUSPEND
714 */
715static DECLCALLBACK(void) drvscsiSuspend(PPDMDRVINS pDrvIns)
716{
717 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncSuspendOrPowerOffDone);
718}
719
720/**
721 * Callback employed by drvscsiReset.
722 *
723 * @returns true if we've quiesced, false if we're still working.
724 * @param pDrvIns The driver instance.
725 */
726static DECLCALLBACK(bool) drvscsiIsAsyncResetDone(PPDMDRVINS pDrvIns)
727{
728 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
729
730 if (pThis->pDrvBlockAsync)
731 {
732 if (pThis->StatIoDepth > 0)
733 return false;
734 else
735 return true;
736 }
737 else
738 {
739 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 0 /*ms*/))
740 return false;
741 ASMAtomicWriteBool(&pThis->fDummySignal, false);
742 return true;
743 }
744}
745
746/**
747 * @copydoc FNPDMDRVRESET
748 */
749static DECLCALLBACK(void) drvscsiReset(PPDMDRVINS pDrvIns)
750{
751 drvscsiR3ResetOrSuspendOrPowerOff(pDrvIns, drvscsiIsAsyncResetDone);
752}
753
754/**
755 * Destruct a driver instance.
756 *
757 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
758 * resources can be freed correctly.
759 *
760 * @param pDrvIns The driver instance data.
761 */
762static DECLCALLBACK(void) drvscsiDestruct(PPDMDRVINS pDrvIns)
763{
764 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
765 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
766
767 if (pThis->hQueueRequests != NIL_RTREQQUEUE)
768 {
769 if (!drvscsiAsyncIOLoopNoPendingDummy(pThis, 100 /*ms*/))
770 LogRel(("drvscsiDestruct#%u: previous dummy request is still pending\n", pDrvIns->iInstance));
771
772 int rc = RTReqQueueDestroy(pThis->hQueueRequests);
773 AssertMsgRC(rc, ("Failed to destroy queue rc=%Rrc\n", rc));
774 pThis->hQueueRequests = NIL_RTREQQUEUE;
775 }
776
777 /* Free the VSCSI device and LUN handle. */
778 VSCSILUN hVScsiLun;
779 int rc = VSCSIDeviceLunDetach(pThis->hVScsiDevice, 0, &hVScsiLun);
780 AssertRC(rc);
781
782 Assert(hVScsiLun == pThis->hVScsiLun);
783 rc = VSCSILunDestroy(hVScsiLun);
784 AssertRC(rc);
785 rc = VSCSIDeviceDestroy(pThis->hVScsiDevice);
786 AssertRC(rc);
787}
788
789/**
790 * Construct a block driver instance.
791 *
792 * @copydoc FNPDMDRVCONSTRUCT
793 */
794static DECLCALLBACK(int) drvscsiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
795{
796 int rc = VINF_SUCCESS;
797 PDRVSCSI pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSI);
798 LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
799 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
800
801 /*
802 * Initialize the instance data.
803 */
804 pThis->pDrvIns = pDrvIns;
805 pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsiRequestSend;
806
807 pDrvIns->IBase.pfnQueryInterface = drvscsiQueryInterface;
808
809 pThis->IPort.pfnQueryDeviceLocation = drvscsiQueryDeviceLocation;
810 pThis->IPortAsync.pfnTransferCompleteNotify = drvscsiTransferCompleteNotify;
811 pThis->hQueueRequests = NIL_RTREQQUEUE;
812
813 /* Query the SCSI port interface above. */
814 pThis->pDevScsiPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISCSIPORT);
815 AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
816
817 /* Query the optional LED interface above. */
818 pThis->pLedPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMILEDPORTS);
819 if (pThis->pLedPort != NULL)
820 {
821 /* Get The Led. */
822 rc = pThis->pLedPort->pfnQueryStatusLed(pThis->pLedPort, 0, &pThis->pLed);
823 if (RT_FAILURE(rc))
824 pThis->pLed = &pThis->Led;
825 }
826 else
827 pThis->pLed = &pThis->Led;
828
829 /*
830 * Validate and read configuration.
831 */
832 if (!CFGMR3AreValuesValid(pCfg, "NonRotationalMedium\0Readonly\0"))
833 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
834 N_("SCSI configuration error: unknown option specified"));
835
836 rc = CFGMR3QueryBoolDef(pCfg, "NonRotationalMedium", &pThis->fNonRotational, false);
837 if (RT_FAILURE(rc))
838 return PDMDRV_SET_ERROR(pDrvIns, rc,
839 N_("SCSI configuration error: failed to read \"NonRotationalMedium\" as boolean"));
840
841 rc = CFGMR3QueryBoolDef(pCfg, "Readonly", &pThis->fReadonly, false);
842 if (RT_FAILURE(rc))
843 return PDMDRV_SET_ERROR(pDrvIns, rc,
844 N_("SCSI configuration error: failed to read \"Readonly\" as boolean"));
845
846 /*
847 * Try attach driver below and query it's block interface.
848 */
849 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pThis->pDrvBase);
850 AssertMsgReturn(RT_SUCCESS(rc), ("Attaching driver below failed rc=%Rrc\n", rc), rc);
851
852 /*
853 * Query the block and blockbios interfaces.
854 */
855 pThis->pDrvBlock = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCK);
856 if (!pThis->pDrvBlock)
857 {
858 AssertMsgFailed(("Configuration error: No block interface!\n"));
859 return VERR_PDM_MISSING_INTERFACE;
860 }
861 pThis->pDrvBlockBios = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKBIOS);
862 if (!pThis->pDrvBlockBios)
863 {
864 AssertMsgFailed(("Configuration error: No block BIOS interface!\n"));
865 return VERR_PDM_MISSING_INTERFACE;
866 }
867
868 pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIMOUNT);
869
870 /* Try to get the optional async block interface. */
871 pThis->pDrvBlockAsync = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBLOCKASYNC);
872
873 PDMBLOCKTYPE enmType = pThis->pDrvBlock->pfnGetType(pThis->pDrvBlock);
874 if (enmType != PDMBLOCKTYPE_HARD_DISK)
875 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_UNSUPPORTED_BLOCK_TYPE, RT_SRC_POS,
876 N_("Only hard disks are currently supported as SCSI devices (enmType=%d)"),
877 enmType);
878
879 /* Create VSCSI device and LUN. */
880 pThis->VScsiIoCallbacks.pfnVScsiLunMediumGetSize = drvscsiGetSize;
881 pThis->VScsiIoCallbacks.pfnVScsiLunReqTransferEnqueue = drvscsiReqTransferEnqueue;
882 pThis->VScsiIoCallbacks.pfnVScsiLunGetFeatureFlags = drvscsiGetFeatureFlags;
883
884 rc = VSCSIDeviceCreate(&pThis->hVScsiDevice, drvscsiVScsiReqCompleted, pThis);
885 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI device rc=%Rrc\n"), rc);
886 rc = VSCSILunCreate(&pThis->hVScsiLun, VSCSILUNTYPE_SBC, &pThis->VScsiIoCallbacks,
887 pThis);
888 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create VSCSI LUN rc=%Rrc\n"), rc);
889 rc = VSCSIDeviceLunAttach(pThis->hVScsiDevice, pThis->hVScsiLun, 0);
890 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to attached the LUN to the SCSI device\n"), rc);
891
892 /* Register statistics counter. */
893 /** @todo aeichner: Find a way to put the instance number of the attached
894 * controller device when we support more than one controller of the same type.
895 * At the moment we have the 0 hardcoded. */
896 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
897 "Amount of data read.", "/Devices/SCSI0/%d/ReadBytes", pDrvIns->iInstance);
898 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
899 "Amount of data written.", "/Devices/SCSI0/%d/WrittenBytes", pDrvIns->iInstance);
900
901 pThis->StatIoDepth = 0;
902
903 PDMDrvHlpSTAMRegisterF(pDrvIns, (void *)&pThis->StatIoDepth, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT,
904 "Number of active tasks.", "/Devices/SCSI0/%d/IoDepth", pDrvIns->iInstance);
905
906 if (!pThis->pDrvBlockAsync)
907 {
908 /* Create request queue. */
909 rc = RTReqQueueCreate(&pThis->hQueueRequests);
910 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create request queue rc=%Rrc\n"), rc);
911 /* Create I/O thread. */
912 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pAsyncIOThread, pThis, drvscsiAsyncIOLoop,
913 drvscsiAsyncIOLoopWakeup, 0, RTTHREADTYPE_IO, "SCSI async IO");
914 AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create async I/O thread rc=%Rrc\n"), rc);
915
916 LogRel(("SCSI#%d: using normal I/O\n", pDrvIns->iInstance));
917 }
918 else
919 LogRel(("SCSI#%d: using async I/O\n", pDrvIns->iInstance));
920
921 if ( pThis->pDrvBlock->pfnDiscard
922 || ( pThis->pDrvBlockAsync
923 && pThis->pDrvBlockAsync->pfnStartDiscard))
924 LogRel(("SCSI#%d: Enabled UNMAP support\n"));
925
926 return VINF_SUCCESS;
927}
928
929/**
930 * SCSI driver registration record.
931 */
932const PDMDRVREG g_DrvSCSI =
933{
934 /* u32Version */
935 PDM_DRVREG_VERSION,
936 /* szName */
937 "SCSI",
938 /* szRCMod */
939 "",
940 /* szR0Mod */
941 "",
942 /* pszDescription */
943 "Generic SCSI driver.",
944 /* fFlags */
945 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
946 /* fClass. */
947 PDM_DRVREG_CLASS_SCSI,
948 /* cMaxInstances */
949 ~0,
950 /* cbInstance */
951 sizeof(DRVSCSI),
952 /* pfnConstruct */
953 drvscsiConstruct,
954 /* pfnDestruct */
955 drvscsiDestruct,
956 /* pfnRelocate */
957 NULL,
958 /* pfnIOCtl */
959 NULL,
960 /* pfnPowerOn */
961 NULL,
962 /* pfnReset */
963 drvscsiReset,
964 /* pfnSuspend */
965 drvscsiSuspend,
966 /* pfnResume */
967 NULL,
968 /* pfnAttach */
969 NULL,
970 /* pfnDetach */
971 NULL,
972 /* pfnPowerOff */
973 drvscsiPowerOff,
974 /* pfnSoftReset */
975 NULL,
976 /* u32EndVersion */
977 PDM_DRVREG_VERSION
978};
979
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