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
|