BOOLEAN
VirtIoMSInterruptRoutine (
    IN PVOID  DeviceExtension,
    IN ULONG  MessageID
    )
{
    pblk_req            vbr;
    unsigned int        len;
    PADAPTER_EXTENSION  adaptExt;
    PSCSI_REQUEST_BLOCK Srb;
    BOOLEAN             isInterruptServiced = FALSE;

    adaptExt = (PADAPTER_EXTENSION)DeviceExtension;

    RhelDbgPrint(TRACE_LEVEL_VERBOSE,
                 ("<--->%s : MessageID 0x%x\n", __FUNCTION__, MessageID));

    if (MessageID == 0) {
       adaptExt->rescan_geometry = TRUE;
       StorPortNotification( BusChangeDetected, DeviceExtension, 0);
       return TRUE;
    }

    while((vbr = (pblk_req)adaptExt->vq->vq_ops->get_buf(adaptExt->vq, &len)) != NULL) {
        Srb = (PSCSI_REQUEST_BLOCK)vbr->req;
        if (Srb) {
           switch (vbr->status) {
           case VIRTIO_BLK_S_OK:
              Srb->SrbStatus = SRB_STATUS_SUCCESS;
              break;
           case VIRTIO_BLK_S_UNSUPP:
              Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
              break;
           default:
              Srb->SrbStatus = SRB_STATUS_ERROR;
              RhelDbgPrint(TRACE_LEVEL_ERROR, ("SRB_STATUS_ERROR\n"));
              break;
           }
        }
        if (vbr->out_hdr.type == VIRTIO_BLK_T_FLUSH) {
            --adaptExt->in_fly;
            CompleteSRB(DeviceExtension, Srb);
        } else if (vbr->out_hdr.type == VIRTIO_BLK_T_GET_ID) {
            adaptExt->sn_ok = TRUE;
        } else if (Srb) {
            --adaptExt->in_fly;
            CompleteDPC(DeviceExtension, vbr, MessageID);
        }
        isInterruptServiced = TRUE;
    }
    if (adaptExt->in_fly > 0) {
        adaptExt->vq->vq_ops->kick(adaptExt->vq);
    }
    return isInterruptServiced;
}
VOID 
TransportReset(
    IN PVOID DeviceExtension,
    IN PVirtIOSCSIEvent evt
    )
{
    UCHAR TargetId = evt->lun[1];
    UCHAR Lun = (evt->lun[2] << 8) | evt->lun[3];

    switch (evt->reason)
    {
        case VIRTIO_SCSI_EVT_RESET_RESCAN:
           StorPortNotification( BusChangeDetected, DeviceExtension, 0);
           break;
        case VIRTIO_SCSI_EVT_RESET_REMOVED:
           StorPortNotification( BusChangeDetected, DeviceExtension, 0);
           break;
        default:
           RhelDbgPrint(TRACE_LEVEL_VERBOSE, ("<-->Unsupport virtio scsi event reason 0x%x\n", evt->reason));
    }
}
VOID
CompleteRequest(
    IN PVOID DeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
ENTER_FN();
    PostProcessRequest(DeviceExtension, Srb);
    StorPortNotification(RequestComplete,
                         DeviceExtension,
                         Srb);
EXIT_FN();
}
VOID
ParamChange(
    IN PVOID DeviceExtension,
    IN PVirtIOSCSIEvent evt
    )
{
    UCHAR TargetId = evt->lun[1];
    UCHAR Lun = (evt->lun[2] << 8) | evt->lun[3];
    UCHAR AdditionalSenseCode = (UCHAR)(evt->reason & 255);
    UCHAR AdditionalSenseCodeQualifier = (UCHAR)(evt->reason >> 8);

    if (AdditionalSenseCode == SCSI_ADSENSE_PARAMETERS_CHANGED && 
       (AdditionalSenseCodeQualifier == SPC3_SCSI_SENSEQ_PARAMETERS_CHANGED || 
        AdditionalSenseCodeQualifier == SPC3_SCSI_SENSEQ_MODE_PARAMETERS_CHANGED || 
        AdditionalSenseCodeQualifier == SPC3_SCSI_SENSEQ_CAPACITY_DATA_HAS_CHANGED))
    {
        StorPortNotification( BusChangeDetected, DeviceExtension, 0);
    }
}
BOOLEAN
MpHwStartIo(
            __in       pHW_HBA_EXT          pHBAExt,  // Adapter device-object extension from port driver.
            __in __out PSCSI_REQUEST_BLOCK  pSrb
           )
{
    UCHAR                     Result = ResultDone;
#ifdef USE_SCSIPORT
    UCHAR                     PathId = pSrb->PathId;
    UCHAR                     TargetId = pSrb->TargetId;
    UCHAR                     Lun = pSrb->Lun;
#endif

    KdPrint2(("PhDskMnt::MpHwStartIo:  pHBAExt = 0x%p, pSrb = 0x%p, Path=%i, Target=%i, Lun=%i, IRQL=%i\n",
        pHBAExt,
        pSrb,
        (int) pSrb->PathId,
        (int) pSrb->TargetId,
        (int) pSrb->Lun,
        KeGetCurrentIrql()));

    pSrb->SrbStatus = SRB_STATUS_PENDING;
    pSrb->ScsiStatus = SCSISTAT_GOOD;

    ImScsiCompletePendingSrbs(pHBAExt);

    _InterlockedExchangeAdd((volatile LONG *)&pHBAExt->SRBsSeen, 1);   // Bump count of SRBs encountered.

    // Next, if true, will cause port driver to remove the associated LUNs if, for example, devmgmt.msc is asked "scan for hardware changes."
    //if (pHBAExt->bDontReport)
    //{                       // Act as though the HBA/path is gone?
    //    pSrb->SrbStatus = SRB_STATUS_INVALID_LUN;
    //    goto done;
    //}

    switch (pSrb->Function)
    {
    case SRB_FUNCTION_IO_CONTROL:
        ScsiIoControl(pHBAExt, pSrb, &Result);
        break;

    case SRB_FUNCTION_EXECUTE_SCSI:
        ScsiExecute(pHBAExt, pSrb, &Result);
        break;

    case SRB_FUNCTION_RESET_LOGICAL_UNIT:
        DbgPrint("PhDskMnt::MpHwStartIo: SRB_FUNCTION_RESET_LOGICAL_UNIT.\n");
        pSrb->SrbStatus = ScsiResetLun(pHBAExt, pSrb);
        break;

    case SRB_FUNCTION_RESET_DEVICE:
        DbgPrint("PhDskMnt::MpHwStartIo: SRB_FUNCTION_RESET_DEVICE.\n");
        pSrb->SrbStatus = ScsiResetDevice(pHBAExt, pSrb);
        break;
            
    case SRB_FUNCTION_RESET_BUS:
        DbgPrint("PhDskMnt::MpHwStartIo: SRB_FUNCTION_RESET_BUS.\n");
        pSrb->SrbStatus = MpHwResetBus(pHBAExt, pSrb->PathId);
        break;
            
    case SRB_FUNCTION_PNP:                        
        MpHwHandlePnP(pHBAExt, (PSCSI_PNP_REQUEST_BLOCK)pSrb);
        break;

    case SRB_FUNCTION_POWER:                      
        KdPrint(("PhDskMnt::MpHwStartIo: SRB_FUNCTION_POWER.\n"));
        // Do nothing.
        pSrb->SrbStatus = SRB_STATUS_SUCCESS;

        break;

    case SRB_FUNCTION_SHUTDOWN:                   
        KdPrint(("PhDskMnt::MpHwStartIo: SRB_FUNCTION_SHUTDOWN.\n"));
        // Do nothing.
        pSrb->SrbStatus = SRB_STATUS_SUCCESS;

        break;

    default:
        KdPrint(("PhDskMnt::MpHwStartIo: Unknown pSrb Function = 0x%x\n", pSrb->Function));
        
        ScsiSetCheckCondition(pSrb, SRB_STATUS_ERROR, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_ILLEGAL_COMMAND, 0);

        break;

    } // switch (pSrb->Function)

    if (Result == ResultDone)
    {                         // Complete now?
        // Note:  A miniport with real hardware would not always be calling RequestComplete from HwScsiStartIo.  Rather,
        //        the miniport would typically be doing real I/O and would call RequestComplete only at the end of that
        //        real I/O, in its HwScsiInterrupt or in a DPC routine.

#ifdef USE_SCSIPORT
        KdPrint2(("PhDskMnt::MpHwStartIo sending 'RequestComplete', 'NextRequest' and 'NextLuRequest' to ScsiPort.\n"));
        ScsiPortNotification(RequestComplete, pHBAExt, pSrb);
        ScsiPortNotification(NextRequest, pHBAExt);
        ScsiPortNotification(NextLuRequest, pHBAExt, 0, 0, 0);
#endif
#ifdef USE_STORPORT
        KdPrint2(("PhDskMnt::MpHwStartIo sending 'RequestComplete' to port StorPort.\n"));
        StorPortNotification(RequestComplete, pHBAExt, pSrb);
#endif
    }
    else
    {
#ifdef USE_SCSIPORT
        _InterlockedExchangeAdd((volatile LONG*)&pHBAExt->WorkItems, 1);

        KdPrint2(("PhDskMnt::MpHwStartIo sending 'RequestTimerCall' and 'NextLuRequest' to ScsiPort.\n"));
        ScsiPortNotification(RequestTimerCall, pHBAExt, MpHwTimer, (ULONG) 1);
        ScsiPortNotification(NextLuRequest, pHBAExt, PathId, TargetId, Lun);
        ScsiPortNotification(NextLuRequest, pHBAExt, 0, 0, 0);
#endif
    }

    KdPrint2(("PhDskMnt::MpHwStartIo End.\n"));

    return TRUE;
}                                                     // End MpHwStartIo().
BOOLEAN
MpHwStartIo(
__in                PVOID                DeviceExtension,  // Adapter device-object extension from port driver.
__inout __deref     PSCSI_REQUEST_BLOCK  pSrb
)
{
    KIRQL                   lowest_assumed_irql = PASSIVE_LEVEL;
    ResultType              Result = ResultDone;
    pHW_HBA_EXT             pHBAExt = (pHW_HBA_EXT)DeviceExtension;
#ifdef USE_SCSIPORT
    UCHAR                   PathId = pSrb->PathId;
    UCHAR                   TargetId = pSrb->TargetId;
    UCHAR                   Lun = pSrb->Lun;
#endif

    KdPrint2(("PhDskMnt::MpHwStartIo:  pHBAExt = 0x%p, pSrb = 0x%p, Path=%i, Target=%i, Lun=%i, IRQL=%i\n",
        pHBAExt,
        pSrb,
        (int)pSrb->PathId,
        (int)pSrb->TargetId,
        (int)pSrb->Lun,
        KeGetCurrentIrql()));

    pSrb->SrbStatus = SRB_STATUS_PENDING;
    pSrb->ScsiStatus = SCSISTAT_GOOD;

    ImScsiCompletePendingSrbs(pHBAExt, &lowest_assumed_irql);

    _InterlockedExchangeAdd((volatile LONG *)&pHBAExt->SRBsSeen, 1);   // Bump count of SRBs encountered.

    // Next, if true, will cause port driver to remove the associated LUNs if, for example, devmgmt.msc is asked "scan for hardware changes."
    //if (pHBAExt->bDontReport)
    //{                       // Act as though the HBA/path is gone?
    //    pSrb->SrbStatus = SRB_STATUS_NO_DEVICE;
    //    goto done;
    //}

    switch (pSrb->Function)
    {
    case SRB_FUNCTION_IO_CONTROL:
        ScsiIoControl(pHBAExt, pSrb, &Result, &lowest_assumed_irql);
        break;

    case SRB_FUNCTION_EXECUTE_SCSI:
        if (pSrb->Cdb[0] == SCSIOP_REPORT_LUNS)
        {
            ScsiOpReportLuns(pHBAExt, pSrb, &lowest_assumed_irql);
        }
        else
        {
            ScsiExecute(pHBAExt, pSrb, &Result, &lowest_assumed_irql);
        }
        break;

    case SRB_FUNCTION_RESET_LOGICAL_UNIT:
        DbgPrint("PhDskMnt::MpHwStartIo: SRB_FUNCTION_RESET_LOGICAL_UNIT.\n");
        pSrb->SrbStatus = ScsiResetLun(pHBAExt, pSrb);
        break;

    case SRB_FUNCTION_RESET_DEVICE:
        DbgPrint("PhDskMnt::MpHwStartIo: SRB_FUNCTION_RESET_DEVICE.\n");
        pSrb->SrbStatus = ScsiResetDevice(pHBAExt, pSrb);
        break;

    case SRB_FUNCTION_RESET_BUS:
        DbgPrint("PhDskMnt::MpHwStartIo: SRB_FUNCTION_RESET_BUS.\n");
        pSrb->SrbStatus = MpHwResetBus(pHBAExt, pSrb->PathId);
        break;

    case SRB_FUNCTION_PNP:
        ScsiPnP(pHBAExt, (PSCSI_PNP_REQUEST_BLOCK)pSrb, &lowest_assumed_irql);
        break;

    case SRB_FUNCTION_POWER:
        KdPrint(("PhDskMnt::MpHwStartIo: SRB_FUNCTION_POWER.\n"));
        // Do nothing.
        pSrb->SrbStatus = SRB_STATUS_SUCCESS;

        break;

    case SRB_FUNCTION_SHUTDOWN:
        KdPrint(("PhDskMnt::MpHwStartIo: SRB_FUNCTION_SHUTDOWN.\n"));
        // Do nothing.
        pSrb->SrbStatus = SRB_STATUS_SUCCESS;

        break;

    default:
        KdPrint(("PhDskMnt::MpHwStartIo: Unknown pSrb Function = 0x%X\n", pSrb->Function));

        //StorPortLogError(pHBAExt, pSrb, pSrb->PathId, pSrb->TargetId, pSrb->Lun, SP_PROTOCOL_ERROR, 0x0200 | pSrb->Cdb[0]);

        ScsiSetCheckCondition(pSrb, SRB_STATUS_ERROR, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_ILLEGAL_COMMAND, 0);

        break;

    } // switch (pSrb->Function)

    if (Result == ResultDone)
    {                         // Complete now?
#ifdef USE_SCSIPORT
        KdPrint2(("PhDskMnt::MpHwStartIo sending 'RequestComplete', 'NextRequest' and 'NextLuRequest' to ScsiPort.\n"));
        ScsiPortNotification(RequestComplete, pHBAExt, pSrb);
        ScsiPortNotification(NextRequest, pHBAExt);
        ScsiPortNotification(NextLuRequest, pHBAExt, 0, 0, 0);
#endif
#ifdef USE_STORPORT
        KdPrint2(("PhDskMnt::MpHwStartIo sending 'RequestComplete' to port StorPort.\n"));
        StorPortNotification(RequestComplete, pHBAExt, pSrb);
#endif
    }
    else
    {
#ifdef USE_SCSIPORT
        _InterlockedExchangeAdd((volatile LONG*)&pHBAExt->WorkItems, 1);

        KdPrint2(("PhDskMnt::MpHwStartIo sending 'RequestTimerCall' and 'NextLuRequest' to ScsiPort.\n"));
        ScsiPortNotification(RequestTimerCall, pHBAExt, MpHwTimer, (ULONG)1);
        ScsiPortNotification(NextLuRequest, pHBAExt, PathId, TargetId, Lun);
        ScsiPortNotification(NextLuRequest, pHBAExt, 0, 0, 0);
#endif
    }

    KdPrint2(("PhDskMnt::MpHwStartIo End.\n"));

    return TRUE;
}                                                     // End MpHwStartIo().
Beispiel #7
0
VOID
AhciCompleteRequest(
    _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension,
    _In_ PSTORAGE_REQUEST_BLOCK  Srb,
    _In_ BOOLEAN AtDIRQL
    )
/*++
    Wrapper for ComleteRequest to protect against completing the local SRB back to port driver who doesn't know anything about local SRB
It assumes:
    SRB is completely ready to be completed back to the SRB generator
It performs:
    1. If Srb has completion routine, put it in completion queue.
    2. Complete the command back to the owner. Do not complete the Local Srb.
Called by:
    AhciHwStartIo
    RequiresControllerResources
    ReleaseSlottedCommand

NOTE:
    The caller of this routine should call AhciGetNextIos or ActiveQueue to program the command (from Srb completion routine) to adapter

--*/
{
    PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb);

  //1. If Srb acquired active reference or has completion routine, put it in completion queue. Otherwise, complete it.
    if ( ((srbExtension->Flags & ATA_FLAGS_ACTIVE_REFERENCE) != 0) ||
         (srbExtension->CompletionRoutine != NULL) ) {

        STOR_LOCK_HANDLE    lockhandle = {0};

        if (AtDIRQL == FALSE) {
            AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);
            AddQueue(ChannelExtension, &ChannelExtension->CompletionQueue, Srb, 0xDEADBEEF, 0x90);
            AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle);
        } else {
            AddQueue(ChannelExtension, &ChannelExtension->CompletionQueue, Srb, 0xDEADBEEF, 0x90);
        }

        if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) {
            RecordExecutionHistory(ChannelExtension, 0x30000045); //Exit AhciCompleteRequest, SRB is put in CompletionQueue
        }

        if (!IsDumpMode(ChannelExtension->AdapterExtension)) {
            StorPortIssueDpc(ChannelExtension->AdapterExtension, &ChannelExtension->CompletionDpc, ChannelExtension, Srb);
        } else {
            AhciPortSrbCompletionDpcRoutine(&ChannelExtension->CompletionDpc, ChannelExtension->AdapterExtension, ChannelExtension, Srb);
        }

    } else {
      //2. Complete the command back to the owner
        if (IsMiniportInternalSrb(ChannelExtension, Srb)) {
            NT_ASSERT(((PSCSI_REQUEST_BLOCK)Srb == &ChannelExtension->Sense.Srb) || (ChannelExtension->StateFlags.ReservedSlotInUse == 0));
            RecordExecutionHistory(ChannelExtension, 0x20000045); //Exit AhciCompleteRequest,  Local SRB
            return;
        } else {
            NT_ASSERT(Srb->SrbStatus != SRB_STATUS_PENDING);
            StorPortNotification(RequestComplete, ChannelExtension->AdapterExtension, Srb);

            if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) {
                RecordExecutionHistory(ChannelExtension, 0x10000045); //Exit AhciCompleteRequest, SRB from port driver
            }
        }
    }

    return;
}
VOID
ImScsiWorkerThread(__in PVOID Context)
{
    pHW_LU_EXTENSION            pLUExt = (pHW_LU_EXTENSION)Context;
    pMP_WorkRtnParms            pWkRtnParms = NULL;
    PLIST_ENTRY                 request_list = NULL;
    PKSPIN_LOCK                 request_list_lock = NULL;
    PKEVENT                     wait_objects[2] = { NULL };

    KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);

    if (pLUExt != NULL)
    {
        KdPrint(("PhDskMnt::ImScsiWorkerThread: Device worker thread start. pLUExt = 0x%p\n",
            pLUExt));

        request_list = &pLUExt->RequestList;
        request_list_lock = &pLUExt->RequestListLock;
        wait_objects[0] = &pLUExt->RequestEvent;

        // If this is a VM backed disk that should be pre-loaded with an image file
        // we have to load the contents of that file now before entering the service
        // loop.
        if (pLUExt->VMDisk && (pLUExt->ImageFile != NULL))
            if (!ImScsiFillMemoryDisk(pLUExt))
                KeSetEvent(&pLUExt->StopThread, (KPRIORITY)0, FALSE);
    }
    else
    {
        KdPrint(("PhDskMnt::ImScsiWorkerThread: Global worker thread start. pLUExt=%p\n",
            pLUExt));

        request_list = &pMPDrvInfoGlobal->RequestList;
        request_list_lock = &pMPDrvInfoGlobal->RequestListLock;
        wait_objects[0] = &pMPDrvInfoGlobal->RequestEvent;
    }
    wait_objects[1] = &pMPDrvInfoGlobal->StopWorker;

    for (;;)
    {
        PLIST_ENTRY                 request;
        KLOCK_QUEUE_HANDLE          lock_handle;
        KIRQL                       lowest_assumed_irql = PASSIVE_LEVEL;

#ifdef USE_SCSIPORT

        NTSTATUS                    status = STATUS_SUCCESS;
        PIRP                        irp = NULL;

        ImScsiGetControllerObject();

        if (pMPDrvInfoGlobal->ControllerObject != NULL)
        {
            KdPrint2(("PhDskMnt::ImScsiWorkerThread: Pre-building IRP for next SMB_IMSCSI_CHECK.\n"));

            irp = ImScsiBuildCompletionIrp();
        }
#endif

        for (;;)
        {
            ImScsiAcquireLock(request_list_lock, &lock_handle, lowest_assumed_irql);

            request = RemoveHeadList(request_list);

            ImScsiReleaseLock(&lock_handle, &lowest_assumed_irql);

            if (request != request_list)
            {
                break;
            }

            if (KeReadStateEvent(&pMPDrvInfoGlobal->StopWorker) ||
                ((pLUExt != NULL) && (KeReadStateEvent(&pLUExt->StopThread))))
            {
                KdPrint(("PhDskMnt::ImScsiWorkerThread shutting down.\n"));

                if (pLUExt != NULL)
                {
                    KIRQL lowest_assumed_irql = PASSIVE_LEVEL;
                    ImScsiCleanupLU(pLUExt, &lowest_assumed_irql);
                }

#ifdef USE_SCSIPORT
                // One last SMB_IMSCSI_CHECK call to flush response queue and free allocated IRP
                if (irp != NULL)
                {
                    IoCallDriver(pMPDrvInfoGlobal->ControllerObject, irp);
                }
#endif

                PsTerminateSystemThread(STATUS_SUCCESS);
                return;
            }

            KdPrint2(("PhDskMnt::ImScsiWorkerThread idle, waiting for request.\n"));

            KeWaitForMultipleObjects(2, (PVOID*)wait_objects, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);
        }

        pWkRtnParms = CONTAINING_RECORD(request, MP_WorkRtnParms, RequestListEntry);

        KdPrint2(("PhDskMnt::ImScsiWorkerThread got request. pWkRtnParms = 0x%p\n",
            pWkRtnParms));

        // Request to wait for LU worker thread to terminate
        if (pWkRtnParms->pSrb == NULL)
        {
            KdPrint(("PhDskMnt::ImScsiWorkerThread: Request to wait for LU worker thread to exit. pLUExt=%p\n",
                pWkRtnParms->pLUExt));

            if (pWkRtnParms->pLUExt->WorkerThread != NULL)
            {
                KeWaitForSingleObject(
                    pWkRtnParms->pLUExt->WorkerThread,
                    Executive,
                    KernelMode,
                    FALSE,
                    NULL);

                ObDereferenceObject(pWkRtnParms->pLUExt->WorkerThread);
                pWkRtnParms->pLUExt->WorkerThread = NULL;

                KdPrint(("PhDskMnt::ImScsiWorkerThread: Worker thread exit. Ready to free LUExt.\n"));
            }
            else
            {
                KdPrint(("PhDskMnt::ImScsiWorkerThread: Worker not started. Ready to free LUExt.\n"));
            }
            
            ExFreePoolWithTag(pWkRtnParms->pLUExt, MP_TAG_GENERAL);

            ExFreePoolWithTag(pWkRtnParms, MP_TAG_GENERAL);

            continue;
        }

        ImScsiDispatchWork(pWkRtnParms);

#ifdef USE_SCSIPORT

        KdPrint2(("PhDskMnt::ImScsiWorkerThread: Calling SMB_IMSCSI_CHECK for work: 0x%p.\n", pWkRtnParms));

        status = ImScsiCallForCompletion(irp, pWkRtnParms, &lowest_assumed_irql);

        if (!NT_SUCCESS(status))
            DbgPrint("PhDskMnt::ImScsiWorkerThread: IoCallDriver failed: 0x%X for work 0x%p\n", status, pWkRtnParms);
        else
            KdPrint2(("PhDskMnt::ImScsiWorkerThread: Finished SMB_IMSCSI_CHECK for work: 0x%p.\n", pWkRtnParms));

#endif

#ifdef USE_STORPORT

        KdPrint2(("PhDskMnt::ImScsiWorkerThread: Sending 'RequestComplete' to StorPort for work: 0x%p.\n", pWkRtnParms));

        StorPortNotification(RequestComplete, pWkRtnParms->pHBAExt, pWkRtnParms->pSrb);

        ExFreePoolWithTag(pWkRtnParms, MP_TAG_GENERAL);

        KdPrint2(("PhDskMnt::ImScsiWorkerThread: Finished work: 0x%p.\n", pWkRtnParms));

#endif

    }
}
BOOLEAN
VioScsiBuildIo(
    IN PVOID DeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
    PCDB                  cdb;
    ULONG                 i;
    ULONG                 fragLen;
    ULONG                 sgElement;
    ULONG                 sgMaxElements;
    PADAPTER_EXTENSION    adaptExt;
    PSRB_EXTENSION        srbExt;
    PSTOR_SCATTER_GATHER_LIST sgList;
    VirtIOSCSICmd         *cmd;

ENTER_FN();
    cdb      = (PCDB)&Srb->Cdb[0];
    srbExt   = (PSRB_EXTENSION)Srb->SrbExtension;
    adaptExt = (PADAPTER_EXTENSION)DeviceExtension;

    if( (Srb->PathId > 0) ||
        (Srb->TargetId >= adaptExt->scsi_config.max_target) ||
        (Srb->Lun >= adaptExt->scsi_config.max_lun) ) {
        Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
        StorPortNotification(RequestComplete,
                             DeviceExtension,
                             Srb);
        return FALSE;
    }

    RhelDbgPrint(TRACE_LEVEL_VERBOSE, ("<-->%s (%d::%d::%d)\n", DbgGetScsiOpStr(Srb), Srb->PathId, Srb->TargetId, Srb->Lun));
    
    memset(srbExt, 0, sizeof(*srbExt));

    cmd = &srbExt->cmd;
    cmd->sc = Srb;
    cmd->req.cmd.lun[0] = 1;
    cmd->req.cmd.lun[1] = Srb->TargetId;
    cmd->req.cmd.lun[2] = 0;
    cmd->req.cmd.lun[3] = Srb->Lun;
    cmd->req.cmd.tag = (unsigned long)Srb;
    cmd->req.cmd.task_attr = VIRTIO_SCSI_S_SIMPLE;
    cmd->req.cmd.prio = 0;
    cmd->req.cmd.crn = 0;
    memcpy(cmd->req.cmd.cdb, cdb, min(VIRTIO_SCSI_CDB_SIZE, Srb->CdbLength));

    sgElement = 0;
    srbExt->sg[sgElement].physAddr = StorPortGetPhysicalAddress(DeviceExtension, NULL, &cmd->req.cmd, &fragLen);
    srbExt->sg[sgElement].ulSize   = sizeof(cmd->req.cmd);
    sgElement++;

    sgList = StorPortGetScatterGatherList(DeviceExtension, Srb);
    if (sgList)
    {
        sgMaxElements = sgList->NumberOfElements;

        if((Srb->SrbFlags & SRB_FLAGS_DATA_OUT) == SRB_FLAGS_DATA_OUT) {
            for (i = 0; i < sgMaxElements; i++, sgElement++) {
                srbExt->sg[sgElement].physAddr = sgList->List[i].PhysicalAddress;
                srbExt->sg[sgElement].ulSize   = sgList->List[i].Length;
                srbExt->Xfer += sgList->List[i].Length;
            }
        }
    }
    srbExt->out = sgElement;
    srbExt->sg[sgElement].physAddr = StorPortGetPhysicalAddress(DeviceExtension, NULL, &cmd->resp.cmd, &fragLen);
    srbExt->sg[sgElement].ulSize   = sizeof(cmd->resp.cmd);
    sgElement++;
    if (sgList)
    {
        sgMaxElements = sgList->NumberOfElements;

        if((Srb->SrbFlags & SRB_FLAGS_DATA_OUT) != SRB_FLAGS_DATA_OUT) {
            for (i = 0; i < sgMaxElements; i++, sgElement++) {
                srbExt->sg[sgElement].physAddr = sgList->List[i].PhysicalAddress;
                srbExt->sg[sgElement].ulSize   = sgList->List[i].Length;
                srbExt->Xfer += sgList->List[i].Length;
            }
        }
    }
    srbExt->in = sgElement - srbExt->out;

EXIT_FN();
    return TRUE;
}
BOOLEAN
VioScsiMSInterrupt (
    IN PVOID  DeviceExtension,
    IN ULONG  MessageID
    )
{
    PVirtIOSCSICmd      cmd;
    PVirtIOSCSIEventNode evtNode;
    unsigned int        len;
    PADAPTER_EXTENSION  adaptExt;
    BOOLEAN             isInterruptServiced = FALSE;
    PSCSI_REQUEST_BLOCK Srb;
    PSRB_EXTENSION      srbExt;
    ULONG               intReason = 0;

    adaptExt = (PADAPTER_EXTENSION)DeviceExtension;

    RhelDbgPrint(TRACE_LEVEL_VERBOSE,
                 ("<--->%s : MessageID 0x%x\n", __FUNCTION__, MessageID));

    if (MessageID == 0)
    {
       return TRUE;
    }
    if (MessageID == 1)
    {
        if (adaptExt->tmf_infly)
        {
           while((cmd = (PVirtIOSCSICmd)adaptExt->vq[0]->vq_ops->get_buf(adaptExt->vq[0], &len)) != NULL)
           {
              VirtIOSCSICtrlTMFResp *resp;
              Srb = (PSCSI_REQUEST_BLOCK)cmd->sc;
              ASSERT(Srb == &adaptExt->tmf_cmd.Srb);
              resp = &cmd->resp.tmf;
              switch(resp->response) {
              case VIRTIO_SCSI_S_OK:
              case VIRTIO_SCSI_S_FUNCTION_SUCCEEDED:
                 break;
              default:
                 RhelDbgPrint(TRACE_LEVEL_ERROR, ("Unknown response %d\n", resp->response));
                 ASSERT(0);
                 break;
              }
              StorPortResume(DeviceExtension);
           }
           adaptExt->tmf_infly = FALSE;
        }
        return TRUE;
    }
    if (MessageID == 2) {
        while((evtNode = (PVirtIOSCSIEventNode)adaptExt->vq[1]->vq_ops->get_buf(adaptExt->vq[1], &len)) != NULL) {
           PVirtIOSCSIEvent evt = &evtNode->event;
           switch (evt->event) {
           case VIRTIO_SCSI_T_NO_EVENT:
              break;
           case VIRTIO_SCSI_T_TRANSPORT_RESET:
              TransportReset(DeviceExtension, evt);
              break;
           case VIRTIO_SCSI_T_PARAM_CHANGE:
              ParamChange(DeviceExtension, evt);
              break;
           default:
              RhelDbgPrint(TRACE_LEVEL_ERROR, ("Unsupport virtio scsi event %x\n", evt->event));
              break;
           }
           SynchronizedKickEventRoutine(DeviceExtension, evtNode);  
        }
        return TRUE;
    }
    if (MessageID == 3)
    {
        while((cmd = (PVirtIOSCSICmd)adaptExt->vq[2]->vq_ops->get_buf(adaptExt->vq[2], &len)) != NULL)
        {
           VirtIOSCSICmdResp   *resp;
           Srb     = (PSCSI_REQUEST_BLOCK)cmd->sc;
           resp    = &cmd->resp.cmd;
           srbExt  = (PSRB_EXTENSION)Srb->SrbExtension;

           switch (resp->response)
           {
           case VIRTIO_SCSI_S_OK:
              Srb->ScsiStatus = resp->status;
              Srb->SrbStatus = (Srb->ScsiStatus == SCSISTAT_GOOD) ? SRB_STATUS_SUCCESS : SRB_STATUS_ERROR;
              break;
           case VIRTIO_SCSI_S_UNDERRUN:
              RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("VIRTIO_SCSI_S_UNDERRUN\n"));
              Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
              break;
           case VIRTIO_SCSI_S_ABORTED:
              RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("VIRTIO_SCSI_S_ABORTED\n"));
              Srb->SrbStatus = SRB_STATUS_ABORTED;
              break;
           case VIRTIO_SCSI_S_BAD_TARGET:
              RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("VIRTIO_SCSI_S_BAD_TARGET\n"));
              Srb->SrbStatus = SRB_STATUS_INVALID_TARGET_ID;
              break;
           case VIRTIO_SCSI_S_RESET:
              RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("VIRTIO_SCSI_S_RESET\n"));
              Srb->SrbStatus = SRB_STATUS_BUS_RESET;
              break;
           case VIRTIO_SCSI_S_BUSY:
              RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("VIRTIO_SCSI_S_BUSY\n"));
              Srb->SrbStatus = SRB_STATUS_BUSY;
              break;
           case VIRTIO_SCSI_S_TRANSPORT_FAILURE:
              RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("VIRTIO_SCSI_S_TRANSPORT_FAILURE\n"));
              Srb->SrbStatus = SRB_STATUS_ERROR;
              break;
           case VIRTIO_SCSI_S_TARGET_FAILURE:
              RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("VIRTIO_SCSI_S_TARGET_FAILURE\n"));
              Srb->SrbStatus = SRB_STATUS_ERROR;
              break;
           case VIRTIO_SCSI_S_NEXUS_FAILURE:
              RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("VIRTIO_SCSI_S_NEXUS_FAILURE\n"));
              Srb->SrbStatus = SRB_STATUS_ERROR;
              break;
           case VIRTIO_SCSI_S_FAILURE:
              RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("VIRTIO_SCSI_S_FAILURE\n"));
              Srb->SrbStatus = SRB_STATUS_ERROR;
              break;
           default:
              Srb->SrbStatus = SRB_STATUS_ERROR;
              RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("Unknown response %d\n", resp->response));
              break;
           }
           if (Srb->SrbStatus != SRB_STATUS_SUCCESS)
           {
              PSENSE_DATA pSense = (PSENSE_DATA) Srb->SenseInfoBuffer;
              if (Srb->SenseInfoBufferLength >= FIELD_OFFSET(SENSE_DATA, CommandSpecificInformation)) {
                 memcpy(Srb->SenseInfoBuffer, resp->sense,
                 min(resp->sense_len, Srb->SenseInfoBufferLength));
                 if (Srb->SrbStatus == SRB_STATUS_ERROR) {
                     Srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
                 }
              }
              Srb->DataTransferLength = 0;
           } 
           else if (srbExt->Xfer && Srb->DataTransferLength > srbExt->Xfer) 
           {
              Srb->DataTransferLength = srbExt->Xfer;
              Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
           }
           if (srbExt && srbExt->Xfer && Srb->DataTransferLength > srbExt->Xfer) {
              Srb->DataTransferLength = srbExt->Xfer;
              Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
           }
           --adaptExt->in_fly; 
           StorPortNotification(RequestComplete,
                         DeviceExtension,
                         Srb);

           if (adaptExt->in_fly > 0)
           {
               adaptExt->vq[2]->vq_ops->kick(adaptExt->vq[2]);
           }
        }
        return TRUE;
    }
    return FALSE;
}