windows-nt/Source/XPSP1/NT/drivers/storage/iscsiprt/client/enum.c
2020-09-26 16:20:57 +08:00

754 lines
21 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (C) Microsoft Corporation, 2000
Module Name:
enum.c
Abstract:
This file contains device enumeration routines
Environment:
kernel mode only
Revision History:
--*/
#include "port.h"
VOID
iSpEnumerateDevicesAsynchronous(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
)
{
PISCSI_FDO_EXTENSION fdoExtension;
PCOMMON_EXTENSION commonExtension;
PISCSI_CONNECTION iScsiConnection;
PIRP Irp;
LARGE_INTEGER Timeout;
NTSTATUS status = STATUS_SUCCESS;
UCHAR oldIrql;
fdoExtension = (PISCSI_FDO_EXTENSION) Context;
commonExtension = DeviceObject->DeviceExtension;
IoFreeWorkItem(fdoExtension->EnumerationWorkItem);
fdoExtension->EnumerationWorkItem = NULL;
//
// Local network nodes should be setup at this point.
// If not, fail the enumeration irp
//
if ((fdoExtension->LocalNodesInitialized) == TRUE) {
PDEVICE_OBJECT pdo;
PISCSI_PDO_EXTENSION pdoExtension;
ULONG inx;
DebugPrint((3, "Number of targets : %d\n",
(fdoExtension->NumberOfTargets)));
fdoExtension->TargetsYetToRespond = fdoExtension->NumberOfTargets;
for (inx = 0; inx < (fdoExtension->NumberOfTargets); inx++) {
pdo = fdoExtension->PDOList[inx];
pdoExtension = (PISCSI_PDO_EXTENSION)(pdo->DeviceExtension);
iScsiConnection = pdoExtension->ClientNodeInfo;
DebugPrint((3, "Will connect to the server\n"));
pdoExtension->LogonTickCount = 0;
//
// Connection timeout is 60 seconds. Is this enough???
//
Timeout.QuadPart = -600000000;
status = iSpTdiConnect(iScsiConnection->ConnectionDeviceObject,
iScsiConnection->ConnectionFileObject,
pdoExtension->TargetIPAddress,
htons(pdoExtension->TargetPortNumber),
Timeout);
if (NT_SUCCESS(status)) {
DebugPrint((3, "Connected to the server\n"));
iScsiConnection->ConnectionState = ConnectionStateConnected;
pdoExtension->CurrentProtocolState = PSLogonInProgress;
DebugPrint((3, "Will send logon packet\n"));
status = iSpSendLoginCommand(pdoExtension);
if (NT_SUCCESS(status)) {
pdoExtension->LogonTickCount = 0;
DebugPrint((3, "Login command sent successfully\n"));
} else {
LARGE_INTEGER disconnectTimeout;
DebugPrint((1,
"Send failed for logon. Status : %x\n",
status));
InterlockedDecrement(&(fdoExtension->TargetsYetToRespond));
pdoExtension->CurrentProtocolState = PSLogonFailed;
disconnectTimeout.QuadPart = -100000000L;
iScsiConnection->ConnectionState = ConnectionStateStopping;
status = iSpTdiDisconnect(iScsiConnection->ConnectionDeviceObject,
iScsiConnection->ConnectionFileObject,
TDI_DISCONNECT_RELEASE,
iSpTdiCompletionRoutine,
iScsiConnection,
disconnectTimeout);
DebugPrint((3, "iSpTdiDisconnect returned : %x\n",
status));
}
} else {
DebugPrint((1, "Could not connect to server. Status : %x\n",
status));
pdoExtension->CurrentProtocolState = PSConnectToServerFailed;
InterlockedDecrement(&(fdoExtension->TargetsYetToRespond));
}
}
KeAcquireSpinLock(&(fdoExtension->EnumerationSpinLock),
&oldIrql);
//
// Launch the enum completion thread if all targets have
// responded or none could be contacted.
//
if (((fdoExtension->TargetsYetToRespond) == 0) &&
((fdoExtension->EnumerationThreadLaunched) == FALSE)) {
DebugPrint((0,
"All or no targets responded. Will complete QDR\n"));
iSpLaunchEnumerationCompletion(fdoExtension);
}
KeReleaseSpinLock(&(fdoExtension->EnumerationSpinLock),
oldIrql);
} else {
DebugPrint((1, "iSpEnumerateDevices : Client node not setup yet\n"));
status = STATUS_UNSUCCESSFUL;
Irp = fdoExtension->EnumerationIrp;
fdoExtension->EnumerationIrp = NULL;
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0L;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
return;
}
/*
NTSTATUS
iSpPerformDeviceEnumeration(
IN PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
{
PISCSI_FDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension;
PISCSI_CONNECTION iScsiConnection = fdoExtension->ClientNodeInfo;
PISCSI_LOGIN_COMMAND iscsiLoginCommand;
LARGE_INTEGER disconnectTimeout;
NTSTATUS status;
USHORT connectionID;
ASSERT((iScsiConnection != NULL));
ASSERT((iScsiConnection->Type) == ISCSI_CONNECTION_TYPE);
ASSERT((iScsiConnection->ConnectionState) == ConnectionStateConnected);
if ((fdoExtension->CurrentProtocolState) != PSConnectedToServer) {
DebugPrint((1, "Probably already logged on. CurrentState : %d\n",
(fdoExtension->CurrentProtocolState)));
return STATUS_UNSUCCESSFUL;
}
//
// First send logon packet
//
fdoExtension->CurrentIrp = Irp;
fdoExtension->CurrentProtocolState = PSLogonInProgress;
status = iSpSendLoginCommand(fdoExtension);
if (NT_SUCCESS(status)) {
DebugPrint((3, "Login command sent successfully\n"));
} else {
DebugPrint((1, "Send failed for logon. Status : %x\n", status));
fdoExtension->CurrentIrp = NULL;
fdoExtension->CurrentProtocolState = PSLogonFailed;
disconnectTimeout.QuadPart = -100000000L;
iScsiConnection->ConnectionState = ConnectionStateStopping;
status = iSpTdiDisconnect(iScsiConnection->ConnectionDeviceObject,
iScsiConnection->ConnectionFileObject,
TDI_DISCONNECT_RELEASE,
iSpTdiCompletionRoutine,
iScsiConnection,
disconnectTimeout);
DebugPrint((3, "iSpTdiDisconnect returned : %x\n",
status));
return STATUS_UNSUCCESSFUL;
}
//
// QDR Irp will be completed upon receipt of login response
//
// ISSUE : nramas : 12/24/2000
// Should have a timer here to take care of the case where
// the server fails to send logon response.
//
return STATUS_SUCCESS;
}
*/
NTSTATUS
iSpQueryDeviceRelationsCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
)
{
PISCSI_FDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
PISCSI_PDO_EXTENSION pdoExtension;
PCOMMON_EXTENSION commonExtension;
PISCSI_CONNECTION iScsiConnection;
PISCSI_LOGIN_RESPONSE loginResponse;
PIRP Irp;
PDEVICE_OBJECT pdo;
PDEVICE_RELATIONS deviceRelations;
PACTIVE_REQUESTS activeClientRequests = NULL;
ULONG relationSize;
ULONG maxCmdRN = 0;
ULONG inx;
ULONG targetIndex;
NTSTATUS status = STATUS_SUCCESS;
IoFreeWorkItem((PIO_WORKITEM) Context);
Irp = fdoExtension->EnumerationIrp;
fdoExtension->EnumerationIrp = NULL;
relationSize = sizeof(DEVICE_RELATIONS) +
((fdoExtension->NumberOfTargets) * sizeof(PDEVICE_OBJECT));
deviceRelations = iSpAllocatePool(PagedPool,
relationSize,
ISCSI_TAG_DEVICE_RELATIONS);
if (deviceRelations == NULL) {
DebugPrint((1, "Failed to allocate memory for device relations\n"));
for (inx = 0; inx < (fdoExtension->NumberOfTargets); inx++) {
pdo = fdoExtension->PDOList[inx];
iSpStopNetwork(pdo);
IoDeleteDevice(pdo);
fdoExtension->PDOList[inx] = NULL;
}
fdoExtension->NumberOfTargets = 0;
fdoExtension->LocalNodesInitialized = FALSE;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Information = 0L;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INSUFFICIENT_RESOURCES;
}
deviceRelations->Count = 0;
targetIndex = 0;
for (inx = 0; inx < (fdoExtension->NumberOfTargets); inx++) {
pdo = fdoExtension->PDOList[inx];
pdoExtension = (PISCSI_PDO_EXTENSION)(pdo->DeviceExtension);
iScsiConnection = pdoExtension->ClientNodeInfo;
if ((pdoExtension->CurrentProtocolState) == PSLogonSucceeded) {
loginResponse = (PISCSI_LOGIN_RESPONSE) (iScsiConnection->IScsiPacket);
//
// Allocate memory to keep active requests. Size of this
// array is the value returned in MaxCmdRN field
//
GetUlongFromArray((loginResponse->InitStatRN),
(iScsiConnection->CurrentStatusRefNum));
GetUlongFromArray((loginResponse->ExpCmdRN),
(iScsiConnection->CommandRefNum));
GetUlongFromArray((loginResponse->MaxCmdRN), maxCmdRN);
iScsiConnection->MaxCommandRefNum = maxCmdRN;
DebugPrint((1, "InitStatRN : %d, ExpCmdRN : %d, MaxCmdRN : %d\n",
(iScsiConnection->CurrentStatusRefNum),
(iScsiConnection->CommandRefNum),
maxCmdRN));
ASSERT((maxCmdRN != 0));
ASSERT(((iScsiConnection->CommandRefNum) <= maxCmdRN));
iScsiConnection->NumberOfReqsInProgress = 0;
iScsiConnection->ReceiveState = ReceiveHeader;
iScsiConnection->MaxPendingRequests = maxCmdRN;
activeClientRequests = iSpAllocatePool(
NonPagedPool,
(sizeof(ACTIVE_REQUESTS) * (maxCmdRN + 1)),
ISCSI_TAG_ACTIVE_REQ);
if (activeClientRequests == NULL) {
DebugPrint((1, "Failed to allocate ActiveClientRequests array\n"));
iSpStopNetwork(pdo);
IoDeleteDevice(pdo);
fdoExtension->PDOList[inx] = NULL;
} else {
RtlZeroMemory(activeClientRequests,
(sizeof(ACTIVE_REQUESTS) * (maxCmdRN + 1)));
commonExtension = pdo->DeviceExtension;
pdoExtension = pdo->DeviceExtension;
pdo->StackSize = 1;
pdo->Flags |= (DO_BUS_ENUMERATED_DEVICE | DO_DIRECT_IO);
pdo->AlignmentRequirement = DeviceObject->AlignmentRequirement;
commonExtension->DeviceObject = pdo;
commonExtension->LowerDeviceObject = DeviceObject;
commonExtension->IsPdo = TRUE;
commonExtension->MajorFunction = PdoMajorFunctionTable;
commonExtension->RemoveLock = 0;
commonExtension->CurrentPnpState = 0xff;
commonExtension->PreviousPnpState = 0xff;
iScsiConnection->ActiveClientRequests = activeClientRequests;
//
// Inquiry data will be filled when we get the
// first query id irp
//
pdoExtension->InquiryDataInitialized = FALSE;
pdoExtension->CurrentProtocolState = PSFullFeaturePhase;
pdoExtension->IsEnumerated = TRUE;
//
// Initialize the remove lock event.
//
KeInitializeEvent(
&(commonExtension->RemoveEvent),
SynchronizationEvent,
FALSE);
//
// Initialize the request list for this PDO
//
InitializeListHead(&(iScsiConnection->RequestList));
pdo->Flags &= ~DO_DEVICE_INITIALIZING;
fdoExtension->PDOList[targetIndex] = pdo;
targetIndex++;
ObReferenceObject(pdo);
DebugPrint((1, "PDO %d : 0x%x\n",
(deviceRelations->Count), pdo));
deviceRelations->Objects[deviceRelations->Count] = pdo;
(deviceRelations->Count)++;
}
} else {
iSpStopNetwork(pdo);
IoDeleteDevice(pdo);
fdoExtension->PDOList[inx] = NULL;
}
}
//
// Group all the targets to the beginning of the PDOList
//
for (inx = targetIndex; inx < MAX_TARGETS_SUPPORTED; inx++) {
fdoExtension->PDOList[inx] = NULL;
}
DebugPrint((1, "Number of PDOs reported : %d\n",
(deviceRelations->Count)));
if ((deviceRelations->Count) > 0) {
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
fdoExtension->EnumerationComplete = TRUE;
fdoExtension->NumberOfTargets = deviceRelations->Count;
IoCopyCurrentIrpStackLocationToNext(Irp);
return IoCallDriver((fdoExtension->CommonExtension.LowerDeviceObject),
Irp);
} else {
DebugPrint((1, "No PDOs to report in QDR\n"));
ExFreePool(deviceRelations);
fdoExtension->NumberOfTargets = 0;
fdoExtension->LocalNodesInitialized = FALSE;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = 0L;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_UNSUCCESSFUL;
}
}
NTSTATUS
IssueInquiry(
IN PDEVICE_OBJECT LogicalUnit
)
{
PISCSI_PDO_EXTENSION pdoExtension = LogicalUnit->DeviceExtension;
PIRP irp;
SCSI_REQUEST_BLOCK srb;
PCDB cdb;
PVOID dataBuffer;
PSENSE_DATA senseInfoBuffer;
UCHAR allocationLength;
ULONG bytesReturned;
NTSTATUS status;
PAGED_CODE();
dataBuffer = &(pdoExtension->InquiryData);
senseInfoBuffer = &(pdoExtension->InquirySenseBuffer);
irp = IoAllocateIrp((LogicalUnit->StackSize) + 1, FALSE);
if (irp == NULL) {
DebugPrint((1, "IssueInquiry : Failed to allocate IRP.\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
IoInitializeIrp(irp,
IoSizeOfIrp((LogicalUnit->StackSize) + 1),
((LogicalUnit->StackSize) + 1));
//
// Fill in SRB fields.
//
RtlZeroMemory(dataBuffer, sizeof(INQUIRYDATA));
RtlZeroMemory(senseInfoBuffer, SENSE_BUFFER_SIZE);
RtlZeroMemory(&srb, SCSI_REQUEST_BLOCK_SIZE);
srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
srb.Length = SCSI_REQUEST_BLOCK_SIZE;
//
// Set flags to disable synchronous negociation.
//
srb.SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
//
// Set timeout to 4 seconds.
//
srb.TimeOutValue = 4;
srb.CdbLength = 6;
cdb = (PCDB)(srb.Cdb);
//
// Set CDB operation code.
//
cdb->CDB6INQUIRY3.OperationCode = SCSIOP_INQUIRY;
//
// Set allocation length to inquiry data buffer size.
//
allocationLength = sizeof(INQUIRYDATA);
cdb->CDB6INQUIRY3.AllocationLength = allocationLength;
cdb->CDB6INQUIRY3.EnableVitalProductData = FALSE;
cdb->CDB6INQUIRY3.PageCode = 0;
status = iSpSendSrbSynchronous(LogicalUnit,
&srb,
irp,
dataBuffer,
allocationLength,
senseInfoBuffer,
SENSE_BUFFER_SIZE,
&bytesReturned
);
ASSERT(bytesReturned <= allocationLength);
//
// Return the inquiry data for the device if the call was successful.
// Otherwise cleanup.
//
if(NT_SUCCESS(status)) {
pdoExtension->InquiryDataInitialized = TRUE;
DebugPrint((3, "Inquiry data obtained successfully\n"));
} else {
DebugPrint((1, "Failed to obtain inquiry data. Status : %x\n",
status));
}
IoFreeIrp(irp);
return status;
}
NTSTATUS
iSpSendSrbSynchronous(
IN PDEVICE_OBJECT LogicalUnit,
IN PSCSI_REQUEST_BLOCK Srb,
IN PIRP Irp,
IN PVOID DataBuffer,
IN ULONG TransferLength,
IN OPTIONAL PVOID SenseInfoBuffer,
IN OPTIONAL UCHAR SenseInfoBufferLength,
OUT PULONG BytesReturned
)
{
KEVENT event;
PIO_STACK_LOCATION irpStack;
PMDL Mdl = NULL;
PSENSE_DATA senseInfo = SenseInfoBuffer;
NTSTATUS status;
PAGED_CODE();
KeInitializeEvent(&event, NotificationEvent, FALSE);
if(ARGUMENT_PRESENT(DataBuffer)) {
ASSERT(TransferLength != 0);
Mdl = IoAllocateMdl(DataBuffer,
TransferLength,
FALSE,
FALSE,
NULL);
if(Mdl == NULL) {
IoFreeIrp(Irp);
return STATUS_INSUFFICIENT_RESOURCES;
}
MmBuildMdlForNonPagedPool(Mdl);
Irp->MdlAddress = Mdl;
} else {
ASSERT(TransferLength == 0);
}
irpStack = IoGetNextIrpStackLocation(Irp);
//
// Mark the minor function to indicate that this is an internal scsiport
// request and that the start state of the device can be ignored.
//
irpStack->MajorFunction = IRP_MJ_SCSI;
irpStack->MinorFunction = 1;
irpStack->Parameters.Scsi.Srb = Srb;
Srb->SrbStatus = Srb->ScsiStatus = 0;
Srb->OriginalRequest = Irp;
//
// Enable auto request sense.
//
if(ARGUMENT_PRESENT(SenseInfoBuffer)) {
Srb->SenseInfoBuffer = SenseInfoBuffer;
Srb->SenseInfoBufferLength = SenseInfoBufferLength;
} else {
Srb->SenseInfoBuffer = NULL;
Srb->SenseInfoBufferLength = 0;
SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_AUTOSENSE);
}
if(ARGUMENT_PRESENT(Mdl)) {
Srb->DataBuffer = MmGetMdlVirtualAddress(Mdl);
Srb->DataTransferLength = TransferLength;
} else {
Srb->DataBuffer = NULL;
Srb->DataTransferLength = 0;
}
IoSetCompletionRoutine(Irp,
iSpSetEvent,
&event,
TRUE,
TRUE,
TRUE);
KeEnterCriticalRegion();
status = IoCallDriver(LogicalUnit, Irp);
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = Irp->IoStatus.Status;
*BytesReturned = (ULONG) Irp->IoStatus.Information;
IoFreeMdl(Mdl);
KeLeaveCriticalRegion();
return status;
}
VOID
iSpTickHandler(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
)
{
PISCSI_FDO_EXTENSION fdoExtension;
PISCSI_PDO_EXTENSION pdoExtension;
PDEVICE_OBJECT pdo;
ULONG inx;
UCHAR oldIrql;
fdoExtension = (PISCSI_FDO_EXTENSION) DeviceObject->DeviceExtension;
KeAcquireSpinLock(&(fdoExtension->EnumerationSpinLock),
&oldIrql);
if ((fdoExtension->TargetsYetToRespond) > 0) {
for (inx = 0; inx < (fdoExtension->NumberOfTargets); inx++) {
pdo = fdoExtension->PDOList[inx];
if (pdo != NULL) {
pdoExtension = (PISCSI_PDO_EXTENSION) (pdo->DeviceExtension);
if ((pdoExtension->CurrentProtocolState) == PSLogonInProgress) {
(pdoExtension->LogonTickCount)++;
}
if ((pdoExtension->LogonTickCount) == MAX_LOGON_WAIT_TIME) {
DebugPrint((0, "Timeout waiting for logon response\n"));
pdoExtension->CurrentProtocolState = PSLogonTimedOut;
(fdoExtension->TargetsYetToRespond)--;
if ((fdoExtension->TargetsYetToRespond) == 0) {
DebugPrint((0,
"TickHandler : All targets responded.\n"));
iSpLaunchEnumerationCompletion(fdoExtension);
}
}
}
}
}
KeReleaseSpinLock(&(fdoExtension->EnumerationSpinLock),
oldIrql);
}
VOID
iSpLaunchEnumerationCompletion(
IN PISCSI_FDO_EXTENSION FdoExtension
)
{
PIO_WORKITEM workItem;
if ((FdoExtension->EnumerationThreadLaunched) == FALSE) {
workItem = IoAllocateWorkItem(FdoExtension->DeviceObject);
if (workItem != NULL) {
IoQueueWorkItem(workItem,
iSpQueryDeviceRelationsCompletion,
DelayedWorkQueue,
workItem);
FdoExtension->EnumerationThreadLaunched = TRUE;
}
}
}