|
|
/*++
Copyright (C) Microsoft Corporation, 2000
Module Name:
protocol.c
Abstract:
This file contains iSCSI protocol related routines.
Environment:
kernel mode only
Revision History:
--*/
#include "port.h"
LONG GlobalSessionID;
BOOLEAN PrintDataBuffer = FALSE;
UCHAR GetCdbLength( IN UCHAR OpCode );
NTSTATUS iSpSendLoginResponse( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) { PISCSI_FDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension; PISCSI_CONNECTION iScsiConnection = fdoExtension->ServerNodeInfo; PISCSI_LOGIN_RESPONSE iscsiLoginResponse; PISCSI_LOGIN_COMMAND loginCommand; NTSTATUS status; ULONG bytesSent; ULONG tempULong;
DelayThreadExecution(1);
IoFreeWorkItem((PIO_WORKITEM) Context); ASSERT((iScsiConnection != NULL)); ASSERT((iScsiConnection->Type) == ISCSI_CONNECTION_TYPE);
loginCommand = (PISCSI_LOGIN_COMMAND)(iScsiConnection->IScsiHeader); iscsiLoginResponse = iSpAllocatePool(NonPagedPool, sizeof(ISCSI_LOGIN_RESPONSE), ISCSI_TAG_LOGIN_RES); if (iscsiLoginResponse == NULL) { DebugPrint((0, "Failed to allocate logon response packet\n")); return STATUS_INSUFFICIENT_RESOURCES; }
RtlZeroMemory(iscsiLoginResponse, sizeof(ISCSI_LOGIN_RESPONSE));
iscsiLoginResponse->OpCode = ISCSIOP_LOGIN_RESPONSE;
//
// Copy client's Session ID
//
iscsiLoginResponse->ISID[0] = loginCommand->ISID[0]; iscsiLoginResponse->ISID[1] = loginCommand->ISID[1];
tempULong = InterlockedIncrement(&GlobalSessionID); iscsiLoginResponse->TSID[0] = (UCHAR) ((tempULong & 0xFF00) >> 8); iscsiLoginResponse->TSID[1] = (UCHAR) (tempULong & 0xFF);
iscsiLoginResponse->ExpCmdRN[0] = loginCommand->InitCmdRN[0]; iscsiLoginResponse->ExpCmdRN[1] = loginCommand->InitCmdRN[1]; iscsiLoginResponse->ExpCmdRN[2] = loginCommand->InitCmdRN[2]; iscsiLoginResponse->ExpCmdRN[3] = loginCommand->InitCmdRN[3];
iscsiLoginResponse->MaxCmdRN[3] = MAX_PENDING_REQUESTS;
iscsiLoginResponse->InitStatRN[3] = 1;
iscsiLoginResponse->Status = ISCSI_LOGINSTATUS_ACCEPT;
GetUlongFromArray((loginCommand->InitCmdRN), (iScsiConnection->ExpCommandRefNum));
iScsiConnection->MaxCommandRefNum = MAX_PENDING_REQUESTS;
iScsiConnection->StatusRefNum = 1;
//
// Send logon response
//
fdoExtension->CurrentProtocolState = PSFullFeaturePhase; status = iSpSendData(iScsiConnection->ConnectionDeviceObject, iScsiConnection->ConnectionFileObject, iscsiLoginResponse, sizeof(ISCSI_LOGIN_RESPONSE), &bytesSent); if (NT_SUCCESS(status)) {
DebugPrint((3, "Send succeeded for logon response. Bytes sent : %d\n", bytesSent)); } else { DebugPrint((0, "Could not send logon response. Status %x\n", status));
fdoExtension->CurrentProtocolState = PSLogonFailed; }
return status; }
VOID iSpProcessScsiCommand( IN PVOID Context ) { PISCSI_FDO_EXTENSION fdoExtension = (PISCSI_FDO_EXTENSION) Context; PISCSI_CONNECTION iScsiConnection = fdoExtension->ServerNodeInfo; PISCSI_SCSI_COMMAND iScsiCommand; PISCSI_SCSI_RESPONSE iScsiResponse = NULL; PIRP irp = NULL; PMDL mdl = NULL; PACTIVE_REQUESTS currentRequest; PIO_STACK_LOCATION irpStack; PCDB cdb; SCSI_REQUEST_BLOCK srb; KEVENT event; IO_STATUS_BLOCK ioStatus; ULONG length =0; ULONG sizeRequired; ULONG inx; ULONG bytesSent; NTSTATUS status;
while (TRUE) {
KeWaitForSingleObject( (PVOID) &(iScsiConnection->RequestSemaphore), Executive, KernelMode, FALSE, NULL );
if ((iScsiConnection->TerminateThread) == TRUE) {
//
// This is an indication to terminate this thread
//
PsTerminateSystemThread(STATUS_SUCCESS); }
inx = (iScsiConnection->ExpCommandRefNum) % MAX_PENDING_REQUESTS; if (inx == 0) { inx = MAX_PENDING_REQUESTS; }
DebugPrint((3, "Will process request at index %d\n", inx));
currentRequest = &(iScsiConnection->ActiveRequests[inx]);
iScsiCommand = (PISCSI_SCSI_COMMAND) (currentRequest->IScsiHeader);
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
//
// Set the size of the SCSI Request Block
//
srb.Length = SCSI_REQUEST_BLOCK_SIZE;
srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
if ((iScsiCommand->TurnOffAutoSense) == FALSE) { srb.SenseInfoBuffer = currentRequest->SenseData;
srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE; }
//
// Get the CDB Length based on the CDB OpCode
//
srb.CdbLength = GetCdbLength(iScsiCommand->Cdb[0]);
cdb = (PCDB)(srb.Cdb); RtlCopyMemory(cdb, iScsiCommand->Cdb, srb.CdbLength);
//
// Set DataBuffer pointer to the command buffer in
// the current request.
//
srb.DataBuffer = currentRequest->CommandBuffer;
GetUlongFromArray((iScsiCommand->Length), length); if (length == 0) { DebugPrint((3, "Length 0. Probably READ command\n")); GetUlongFromArray((iScsiCommand->ExpDataXferLength), length); }
//
// If length is non-zero at this point, then it's
// either a Read (ExpDataXferLength is non-zero), or
// Write (Immediate data length is non-zero) command.
//
if (length != 0) { DebugPrint((3, "Read or Write data command\n"));
//
// Set the transfer length.
//
srb.DataTransferLength = length;
}
if (iScsiCommand->Read) {
srb.SrbFlags = SRB_FLAGS_DATA_IN;
} else if (length != 0) {
//
// If length is Non-Zero and Read bit is
// NOT set, then it should be a Write command
//
srb.SrbFlags = SRB_FLAGS_DATA_OUT;
}
switch (iScsiCommand->ATTR) { case ISCSI_TASKATTR_UNTAGGED: case ISCSI_TASKATTR_SIMPLE : { srb.QueueAction = SRB_SIMPLE_TAG_REQUEST; break; }
case ISCSI_TASKATTR_ORDERED: { srb.QueueAction = SRB_ORDERED_QUEUE_TAG_REQUEST; break; }
case ISCSI_TASKATTR_HEADOFQUEUE: { srb.QueueAction = SRB_HEAD_OF_QUEUE_TAG_REQUEST; break; }
default: { srb.QueueAction = SRB_SIMPLE_TAG_REQUEST; break; } }
srb.QueueTag = SP_UNTAGGED;
SET_FLAG(srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); SET_FLAG(srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
//
// Set the event object to the unsignaled state.
// It will be used to signal request completion.
//
KeInitializeEvent(&event, NotificationEvent, FALSE);
//
// Build device I/O control request with METHOD_NEITHER data transfer.
// We'll queue a completion routine to cleanup the MDL's and such ourself.
//
irp = IoAllocateIrp( (CCHAR) (fdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1), FALSE);
if(irp == NULL) {
DebugPrint((0, "Failed to allocate Irp\n"));
//
// ISSUE : Should handle this failure better
//
continue; }
//
// Get next stack location.
//
irpStack = IoGetNextIrpStackLocation(irp);
//
// Set up SRB for execute scsi request. Save SRB address in next stack
// for the port driver.
//
irpStack->MajorFunction = IRP_MJ_SCSI; irpStack->Parameters.Scsi.Srb = &srb;
IoSetCompletionRoutine(irp, iSpSendSrbSynchronousCompletion, &srb, TRUE, TRUE, TRUE);
irp->UserIosb = &ioStatus; irp->UserEvent = &event;
if (srb.DataTransferLength) { //
// Build an MDL for the data buffer and stick it into the irp. The
// completion routine will unlock the pages and free the MDL.
//
irp->MdlAddress = IoAllocateMdl(srb.DataBuffer, length, FALSE, FALSE, irp ); if (irp->MdlAddress == NULL) { IoFreeIrp( irp );
DebugPrint((0, "Failed to allocate MDL\n"));
//
// ISSUE : Should handle this failure better
//
continue; }
try {
MmProbeAndLockPages( irp->MdlAddress, KernelMode, (iScsiCommand->Read ? IoWriteAccess : IoWriteAccess));
} except(EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode();
IoFreeMdl(irp->MdlAddress);
IoFreeIrp( irp );
DebugPrint((0, "Could not lock pages. Status : %x\n", status));
//
// ISSUE : Should handle this failure better
//
continue; } }
//
// Set timeout value for this request.
//
// N.B. The value should be chosen depending on
// the type of the device, and type of command.
// For now, just set some reasonable value
//
srb.TimeOutValue = 180;
//
// Zero out status.
//
srb.ScsiStatus = srb.SrbStatus = 0;
srb.NextSrb = 0;
//
// Set up IRP Address.
//
srb.OriginalRequest = irp;
//
// Call the port driver with the request and wait for it to complete.
//
status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = ioStatus.Status; }
//
// Send the response to the client
//
sizeRequired = sizeof(ISCSI_SCSI_RESPONSE); if (!NT_SUCCESS(status)) {
if (status == STATUS_BUFFER_OVERFLOW) {
if (length >= srb.DataTransferLength) { DebugPrint((1, "DataUnderrun. Xp Len %d, XFer Len %d\n", length, srb.DataTransferLength));
status = STATUS_SUCCESS;
sizeRequired += srb.DataTransferLength; length = srb.DataTransferLength; } else { DebugPrint((0, "Buffer overflow error. XLen %d, XFer Len %d\n", length, srb.DataTransferLength));
sizeRequired += srb.SenseInfoBufferLength; length = srb.SenseInfoBufferLength; }
} else { DebugPrint((0, "Command failed. OpCode : 0x%x, Status : 0x%x\n", srb.Cdb[0], status));
DebugPrint((1, "Expected length : %d, Transfered length : %d\n", length, srb.DataTransferLength));
sizeRequired += srb.SenseInfoBufferLength; length = srb.SenseInfoBufferLength; }
} else if (iScsiCommand->Read) {
ASSERT((length >= srb.DataTransferLength));
sizeRequired += srb.DataTransferLength; length = srb.DataTransferLength;
} else {
length = 0;
}
DebugPrint((3, "Size of the response - %d. \n", sizeRequired));
if ((PrintDataBuffer == TRUE) && (length != 0)) {
for (inx = 0; inx < length; inx++) { DebugPrint((0, "%02x ", (((PUCHAR)(srb.DataBuffer))[inx]))); if ((inx != 0) && ((inx % 16) == 0)) { DebugPrint((0, "\n")); } }
DebugPrint((0, "\n")); }
iScsiResponse = iSpAllocatePool(NonPagedPool, sizeRequired, ISCSI_TAG_SCSIRES); if (iScsiResponse != NULL) {
RtlZeroMemory(iScsiResponse, sizeRequired);
iScsiResponse->OpCode = ISCSIOP_SCSI_RESPONSE;
CopyFourBytes((iScsiResponse->TaskTag), (iScsiCommand->TaskTag));
if (NT_SUCCESS(status)) {
iScsiResponse->CmdStatus = SCSISTAT_GOOD; iScsiResponse->iSCSIStatus = ISCSISTAT_GOOD;
} else {
DebugPrint((1, "Error. Response data size : %d\n", sizeRequired));
iScsiResponse->CmdStatus = SCSISTAT_CHECK_CONDITION; iScsiResponse->iSCSIStatus = ISCSISTAT_CHECK_CONDITION; }
SetUlongInArray((iScsiResponse->StatusRN), (iScsiConnection->StatusRefNum)); (iScsiConnection->StatusRefNum)++;
(iScsiConnection->ExpCommandRefNum)++; SetUlongInArray((iScsiResponse->ExpCmdRN), (iScsiConnection->ExpCommandRefNum));
SetUlongInArray((iScsiResponse->MaxCmdRN), (iScsiConnection->ExpCommandRefNum) + (MAX_PENDING_REQUESTS) - 1);
if (!NT_SUCCESS(status)) {
if (srb.SrbStatus & SRB_STATUS_AUTOSENSE_VALID) { DebugPrint((1, "Sense Data is valid\n"));
if ((iScsiCommand->TurnOffAutoSense) == FALSE) { ULONG inx; RtlCopyMemory((iScsiResponse + 1), srb.SenseInfoBuffer, srb.SenseInfoBufferLength);
DebugPrint((0, "OpCode : %x, Sense Data : ", srb.Cdb[0])); for (inx = 0; inx < (srb.SenseInfoBufferLength); inx++) { DebugPrint((0, "%02x ", ((PUCHAR)(srb.SenseInfoBuffer))[inx])); } DebugPrint((0, "\n"));
SetUlongInArray((iScsiResponse->Length), srb.SenseInfoBufferLength);
iScsiResponse->SenseDataLength[0] = (UCHAR) ((srb.SenseInfoBufferLength) & 0x0000FF00); iScsiResponse->SenseDataLength[1] = (UCHAR) ((srb.SenseInfoBufferLength) & 0x000000FF); }
} else { ULONG inx0, inx1; PUCHAR responseBuffer = (PUCHAR) iScsiResponse;
DebugPrint((0, "Sense Data is NOT valid\n"));
length = 0; sizeRequired = sizeof(ISCSI_SCSI_RESPONSE);
DebugPrint((1, "\n Beginning Of Data\n"));
inx0 = 0;
while (inx0 < sizeRequired) {
inx1 = 0;
DebugPrint((1, "\t"));
while ((inx1 < 4) && ((inx0+inx1) < sizeRequired)) {
DebugPrint((1, "%02x ", responseBuffer[inx0+inx1]));
inx1++;
}
DebugPrint((1, "\n")); inx0 += 4; }
DebugPrint((1, " End Of Data\n")); }
} else if (length != 0) {
RtlCopyMemory((iScsiResponse + 1), srb.DataBuffer, length);
SetUlongInArray((iScsiResponse->Length), length);
iScsiResponse->ResponseLength[0] = (UCHAR) (length & 0x0000FF00); iScsiResponse->ResponseLength[1] = (UCHAR) (length & 0x000000FF);
}
status = iSpSendData(iScsiConnection->ConnectionDeviceObject, iScsiConnection->ConnectionFileObject, iScsiResponse, sizeRequired, &bytesSent); if (NT_SUCCESS(status)) { DebugPrint((3, "Successfully sent SCSI Response. Bytes sent : %d\n", bytesSent)); } else { DebugPrint((0, "Failed to send SCSI response. Status : 0x%x\n", status)); } }
if (irp->MdlAddress) { MmUnlockPages(irp->MdlAddress);
IoFreeMdl(irp->MdlAddress); }
IoFreeIrp( irp ); }
return; }
NTSTATUS iSpSendSrbSynchronousCompletion( PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context ) { *(Irp->UserIosb) = Irp->IoStatus;
//
// Signal the caller's event.
//
KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED; }
UCHAR GetCdbLength( IN UCHAR OpCode ) { UCHAR commandGroup;
commandGroup = (OpCode >> 5) & 0x07; DebugPrint((3, "Command Group - %d\n", commandGroup));
switch (commandGroup) { case COMMAND_GROUP_0: { return CDB6GENERIC_LENGTH; }
case COMMAND_GROUP_1: case COMMAND_GROUP_2: { return CDB10GENERIC_LENGTH; }
case COMMAND_GROUP_5: { return CDB12GENERIC_LENGTH; } default: { ASSERTMSG("Unknown CDB Opcode type\n", FALSE); } } // switch (commandGroup)
return 0; }
ULONG iSpGetActiveClientRequestIndex( IN PISCSI_CONNECTION IScsiConnection, IN PISCSI_SCSI_COMMAND IScsiCommand ) { ULONG cmdRefNum; ULONG expCmdRefNum; ULONG inx;
GetUlongFromArray((IScsiCommand->CmdRN), cmdRefNum);
expCmdRefNum = IScsiConnection->ExpCommandRefNum;
if ((cmdRefNum < expCmdRefNum) || (cmdRefNum >= (expCmdRefNum + MAX_PENDING_REQUESTS))) {
DebugPrint((0, "Unexpected Command Ref Num : %d", cmdRefNum)); ASSERT(FALSE); }
inx = cmdRefNum % MAX_PENDING_REQUESTS; if (inx == 0) { inx = MAX_PENDING_REQUESTS; }
DebugPrint((3, "Will copy request to slot %d\n", inx));
return inx; }
|