VirtualBox

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

Last change on this file since 40257 was 39764, checked in by vboxsync, 13 years ago

DrvSCSI: Log errors to the release log for the synchronous I/O path too

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