478 lines
11 KiB
C
478 lines
11 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
dlccncl.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains functions which handle IRP cancellations for DLC
|
|||
|
commands
|
|||
|
|
|||
|
Contents:
|
|||
|
SetIrpCancelRoutine
|
|||
|
DlcCancelIrp
|
|||
|
CancelCommandIrp
|
|||
|
CancelTransmitIrp
|
|||
|
(MapIoctlCode)
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Richard L Firth (rfirth) 22-Mar-1993
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
kernel mode only
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
22-Mar-1993 rfirth
|
|||
|
Created
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "dlc.h"
|
|||
|
|
|||
|
VOID
|
|||
|
CancelCommandIrp(
|
|||
|
IN PIRP Irp,
|
|||
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|||
|
IN PLIST_ENTRY Queue
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
CancelTransmitIrp(
|
|||
|
IN PIRP pIrp,
|
|||
|
IN PDLC_FILE_CONTEXT pFileContext
|
|||
|
);
|
|||
|
|
|||
|
#if DBG
|
|||
|
PSTR MapIoctlCode(ULONG);
|
|||
|
|
|||
|
//BOOLEAN DebugCancel = TRUE;
|
|||
|
BOOLEAN DebugCancel = FALSE;
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SetIrpCancelRoutine(
|
|||
|
IN PIRP Irp,
|
|||
|
IN BOOLEAN Set
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Sets or resets the cancel routine in a cancellable IRP. We MUST NOT be
|
|||
|
holding the driver spinlock when we call this function - if another thread
|
|||
|
is cancelling an IRP we will deadlock - exactly the reason why we now only
|
|||
|
have a single spinlock for the DLC driver!
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - pointer to cancellable IRP
|
|||
|
Set - TRUE if the cancel routine in the IRP is to be set to DlcCancelIrp
|
|||
|
else the cancel routine is set to NULL (no longer cancellable)
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
KIRQL irql;
|
|||
|
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&irql);
|
|||
|
|
|||
|
if (!Irp->Cancel) {
|
|||
|
|
|||
|
IoSetCancelRoutine(Irp, Set ? DlcCancelIrp : NULL);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
IoReleaseCancelSpinLock(irql);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
DlcCancelIrp(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is set as the cancel function in all cancellable DLC IRPs -
|
|||
|
TRANSMIT, RECEIVE and READ
|
|||
|
|
|||
|
NB: !!! IopCancelSpinLock is held when this function is called !!!
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - pointer to DEVICE_OBJECT
|
|||
|
Irp - pointer to IRP being cancelled
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION irpStack;
|
|||
|
ULONG command;
|
|||
|
PDLC_FILE_CONTEXT pFileContext;
|
|||
|
PLIST_ENTRY queue;
|
|||
|
|
|||
|
IoSetCancelRoutine(Irp, NULL);
|
|||
|
|
|||
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
|
|||
|
#if DBG
|
|||
|
if (DebugCancel) {
|
|||
|
DbgPrint("DlcCancelIrp. IRP @ %08X Type = %08X [%s]\n",
|
|||
|
Irp,
|
|||
|
irpStack->Parameters.DeviceIoControl.IoControlCode,
|
|||
|
MapIoctlCode(irpStack->Parameters.DeviceIoControl.IoControlCode)
|
|||
|
);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
pFileContext = irpStack->FileObject->FsContext;
|
|||
|
command = irpStack->Parameters.DeviceIoControl.IoControlCode;
|
|||
|
|
|||
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|||
|
|
|||
|
ACQUIRE_DRIVER_LOCK();
|
|||
|
|
|||
|
ENTER_DLC(pFileContext);
|
|||
|
|
|||
|
switch (command) {
|
|||
|
case IOCTL_DLC_READ:
|
|||
|
case IOCTL_DLC_READ2:
|
|||
|
queue = &pFileContext->CommandQueue;
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_DLC_RECEIVE:
|
|||
|
case IOCTL_DLC_RECEIVE2:
|
|||
|
queue = &pFileContext->ReceiveQueue;
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_DLC_TRANSMIT:
|
|||
|
case IOCTL_DLC_TRANSMIT2:
|
|||
|
CancelTransmitIrp(Irp, pFileContext);
|
|||
|
queue = NULL;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
#if DBG
|
|||
|
DbgPrint("DlcCancelIrp: didn't expect to cancel %s: add handler!\n", MapIoctlCode(command));
|
|||
|
#endif
|
|||
|
|
|||
|
queue = NULL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (queue) {
|
|||
|
CancelCommandIrp(Irp, pFileContext, queue);
|
|||
|
}
|
|||
|
|
|||
|
LEAVE_DLC(pFileContext);
|
|||
|
|
|||
|
RELEASE_DRIVER_LOCK();
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
CancelCommandIrp(
|
|||
|
IN PIRP Irp,
|
|||
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|||
|
IN PLIST_ENTRY Queue
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Cancels a pending I/O request. Typically, this will be one of the DLC requests
|
|||
|
which stays pending for a long time e.g. READ or RECEIVE
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - IRP to cancel
|
|||
|
pFileContext - file context owning command to cancel
|
|||
|
Queue - pointer to command queue from which to delete
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDLC_COMMAND pCmdPacket;
|
|||
|
PVOID searchHandle;
|
|||
|
BOOLEAN IsReceive;
|
|||
|
USHORT StationId;
|
|||
|
PDLC_OBJECT pAbortedObject = NULL;
|
|||
|
|
|||
|
#if DBG
|
|||
|
if (DebugCancel) {
|
|||
|
DbgPrint("CancelCommandIrp\n");
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// the thing to search for is the address of the CCB
|
|||
|
//
|
|||
|
|
|||
|
searchHandle = ((PNT_DLC_PARMS)Irp->AssociatedIrp.SystemBuffer)->Async.Ccb.pCcbAddress;
|
|||
|
|
|||
|
if (((PNT_DLC_PARMS)Irp->AssociatedIrp.SystemBuffer)->Async.Ccb.uchDlcCommand == LLC_RECEIVE) {
|
|||
|
StationId = ((PNT_DLC_PARMS)Irp->AssociatedIrp.SystemBuffer)->Async.Parms.Receive.usStationId;
|
|||
|
GetStation(pFileContext, StationId, &pAbortedObject);
|
|||
|
IsReceive = TRUE;
|
|||
|
} else {
|
|||
|
IsReceive = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// remove the command info from this file context's command queue
|
|||
|
//
|
|||
|
|
|||
|
pCmdPacket = SearchAndRemoveSpecificCommand(Queue, searchHandle);
|
|||
|
if (pCmdPacket) {
|
|||
|
|
|||
|
//
|
|||
|
// if we are cancelling a RECEIVE which has a non-NULL data completion
|
|||
|
// flag then we also need to dissociate the receive parameters (the
|
|||
|
// address of the system buffer in the IRP being cancelled)
|
|||
|
//
|
|||
|
|
|||
|
if (IsReceive
|
|||
|
&& pAbortedObject
|
|||
|
&& pCmdPacket->pIrp->AssociatedIrp.SystemBuffer == pAbortedObject->pRcvParms) {
|
|||
|
pAbortedObject->pRcvParms = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// increment file context reference count; CompleteAsyncCommand will
|
|||
|
// dereference the file context
|
|||
|
//
|
|||
|
|
|||
|
ReferenceFileContext(pFileContext);
|
|||
|
CompleteAsyncCommand(pFileContext,
|
|||
|
DLC_STATUS_CANCELLED_BY_SYSTEM_ACTION,
|
|||
|
Irp,
|
|||
|
NULL, // pointer for pNext field
|
|||
|
TRUE // called on cancel path
|
|||
|
);
|
|||
|
|
|||
|
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pCmdPacket);
|
|||
|
|
|||
|
DereferenceFileContext(pFileContext);
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// race condition?: the command completed before we got chance to cancel it
|
|||
|
//
|
|||
|
|
|||
|
#if DBG
|
|||
|
DbgPrint("DLC.CancelCommandIrp: Command NOT located. CCB=%08X\n", searchHandle);
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
CancelTransmitIrp(
|
|||
|
IN PIRP Irp,
|
|||
|
IN PDLC_FILE_CONTEXT pFileContext
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Cancels a pending transmit command. We are only interested in I-Frame
|
|||
|
transmit requests since these only complete when a corresponding ACK is
|
|||
|
received from the remote station. U-Frame transmissions don't get retried
|
|||
|
so will normally complete virtually immediately
|
|||
|
|
|||
|
This routine currently does nothing in the retail version, and just
|
|||
|
complains about things in the debug version.
|
|||
|
|
|||
|
Cancel transmit is not defined in the IBM LAN Reference, nor is it
|
|||
|
defined for NT DLC. This is only called by the IO subsystem when
|
|||
|
somebody terminates a thread or process with outstanding IO requests
|
|||
|
that include a DLC transmit request.
|
|||
|
|
|||
|
For application termination, this is not really a problem since eventually
|
|||
|
the termination process will close the application's FileContext(s) and
|
|||
|
all SAPs, link stations, etc. belonging to the application will get closed
|
|||
|
down anyway.
|
|||
|
|
|||
|
For thread termination, it is a real problem if an application abandons
|
|||
|
a transmit (usually Transmit I-Frame) by closing the thread that
|
|||
|
requested the transmit. DLC has no defined course of action to toss
|
|||
|
the transmit, without changing the associated link station state. This
|
|||
|
happened with hpmon.dll when the remote station (printer) got jammed
|
|||
|
and sent Receiver Not Ready in response to attempts to give it the
|
|||
|
frame. When something like this happens, it is up to the application
|
|||
|
to reset or close the link station, or wait, and not rely on thread
|
|||
|
termination to do the right thing here (because it won't).
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - pointer to IRP to cancel
|
|||
|
pFileContext - pointer to owning file context
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION irpStack;
|
|||
|
PNT_DLC_CCB pCcb;
|
|||
|
|
|||
|
#if DBG
|
|||
|
|
|||
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
pCcb = &((PNT_DLC_PARMS)Irp->AssociatedIrp.SystemBuffer)->Async.Ccb;
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
#if DBG
|
|||
|
|
|||
|
DbgPrint("DLC.CancelTransmitIrp: Cancel %s not supported! CCB %08X\n",
|
|||
|
pCcb->uchDlcCommand == LLC_TRANSMIT_FRAMES ? "TRANSMIT_FRAMES"
|
|||
|
: pCcb->uchDlcCommand == LLC_TRANSMIT_DIR_FRAME ? "TRANSMIT_DIR_FRAME"
|
|||
|
: pCcb->uchDlcCommand == LLC_TRANSMIT_UI_FRAME ? "TRANSMIT_UI_FRAME"
|
|||
|
: pCcb->uchDlcCommand == LLC_TRANSMIT_XID_CMD ? "TRANSMIT_XID_CMD"
|
|||
|
: pCcb->uchDlcCommand == LLC_TRANSMIT_XID_RESP_FINAL ? "TRANSMIT_XID_RESP_FINAL"
|
|||
|
: pCcb->uchDlcCommand == LLC_TRANSMIT_XID_RESP_NOT_FINAL ? "TRANSMIT_XID_RESP_NOT_FINAL"
|
|||
|
: pCcb->uchDlcCommand == LLC_TRANSMIT_TEST_CMD ? "TRANSMIT_TEST_CMD"
|
|||
|
: pCcb->uchDlcCommand == LLC_TRANSMIT_I_FRAME ? "TRANSMIT_I_FRAME"
|
|||
|
: "UNKNOWN TRANSMIT COMMAND!",
|
|||
|
pCcb
|
|||
|
);
|
|||
|
|
|||
|
ASSERT ( pCcb->uchDlcCommand != LLC_TRANSMIT_I_FRAME );
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#if DBG
|
|||
|
PSTR MapIoctlCode(ULONG IoctlCode) {
|
|||
|
switch (IoctlCode) {
|
|||
|
case IOCTL_DLC_READ:
|
|||
|
return "READ";
|
|||
|
|
|||
|
case IOCTL_DLC_RECEIVE:
|
|||
|
return "RECEIVE";
|
|||
|
|
|||
|
case IOCTL_DLC_TRANSMIT:
|
|||
|
return "TRANSMIT";
|
|||
|
|
|||
|
case IOCTL_DLC_BUFFER_FREE:
|
|||
|
return "BUFFER_FREE";
|
|||
|
|
|||
|
case IOCTL_DLC_BUFFER_GET:
|
|||
|
return "BUFFER_GET";
|
|||
|
|
|||
|
case IOCTL_DLC_BUFFER_CREATE:
|
|||
|
return "BUFFER_CREATE";
|
|||
|
|
|||
|
case IOCTL_DLC_SET_EXCEPTION_FLAGS:
|
|||
|
return "SET_EXCEPTION_FLAGS";
|
|||
|
|
|||
|
case IOCTL_DLC_CLOSE_STATION:
|
|||
|
return "CLOSE_STATION";
|
|||
|
|
|||
|
case IOCTL_DLC_CONNECT_STATION:
|
|||
|
return "CONNECT_STATION";
|
|||
|
|
|||
|
case IOCTL_DLC_FLOW_CONTROL:
|
|||
|
return "FLOW_CONTROL";
|
|||
|
|
|||
|
case IOCTL_DLC_OPEN_STATION:
|
|||
|
return "OPEN_STATION";
|
|||
|
|
|||
|
case IOCTL_DLC_RESET:
|
|||
|
return "RESET";
|
|||
|
|
|||
|
case IOCTL_DLC_READ_CANCEL:
|
|||
|
return "READ_CANCEL";
|
|||
|
|
|||
|
case IOCTL_DLC_RECEIVE_CANCEL:
|
|||
|
return "RECEIVE_CANCEL";
|
|||
|
|
|||
|
case IOCTL_DLC_QUERY_INFORMATION:
|
|||
|
return "QUERY_INFORMATION";
|
|||
|
|
|||
|
case IOCTL_DLC_SET_INFORMATION:
|
|||
|
return "SET_INFORMATION";
|
|||
|
|
|||
|
case IOCTL_DLC_TIMER_CANCEL:
|
|||
|
return "TIMER_CANCEL";
|
|||
|
|
|||
|
case IOCTL_DLC_TIMER_CANCEL_GROUP:
|
|||
|
return "TIMER_CANCEL_GROUP";
|
|||
|
|
|||
|
case IOCTL_DLC_TIMER_SET:
|
|||
|
return "TIMER_SET";
|
|||
|
|
|||
|
case IOCTL_DLC_OPEN_SAP:
|
|||
|
return "OPEN_SAP";
|
|||
|
|
|||
|
case IOCTL_DLC_CLOSE_SAP:
|
|||
|
return "CLOSE_SAP";
|
|||
|
|
|||
|
case IOCTL_DLC_OPEN_DIRECT:
|
|||
|
return "OPEN_DIRECT";
|
|||
|
|
|||
|
case IOCTL_DLC_CLOSE_DIRECT:
|
|||
|
return "CLOSE_DIRECT";
|
|||
|
|
|||
|
case IOCTL_DLC_OPEN_ADAPTER:
|
|||
|
return "OPEN_ADAPTER";
|
|||
|
|
|||
|
case IOCTL_DLC_CLOSE_ADAPTER:
|
|||
|
return "CLOSE_ADAPTER";
|
|||
|
|
|||
|
case IOCTL_DLC_REALLOCTE_STATION:
|
|||
|
return "REALLOCTE_STATION";
|
|||
|
|
|||
|
case IOCTL_DLC_READ2:
|
|||
|
return "READ2";
|
|||
|
|
|||
|
case IOCTL_DLC_RECEIVE2:
|
|||
|
return "RECEIVE2";
|
|||
|
|
|||
|
case IOCTL_DLC_TRANSMIT2:
|
|||
|
return "TRANSMIT2";
|
|||
|
|
|||
|
case IOCTL_DLC_COMPLETE_COMMAND:
|
|||
|
return "COMPLETE_COMMAND";
|
|||
|
|
|||
|
case IOCTL_DLC_TRACE_INITIALIZE:
|
|||
|
return "TRACE_INITIALIZE";
|
|||
|
|
|||
|
}
|
|||
|
return "*** UNKNOWN IOCTL CODE ***";
|
|||
|
}
|
|||
|
#endif
|