VirtualBox

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

Last change on this file since 38791 was 38680, checked in by vboxsync, 13 years ago

VSCSI+DrvSCSI: Add support for the UNMAP command if discarding is enabled

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