windows-nt/Source/XPSP1/NT/net/ndis/tunmp/sys/recv.c
2020-09-26 16:20:57 +08:00

589 lines
15 KiB
C

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
recv.c
Abstract:
NDIS send entry points and utility routines to handle receiving
data.
Environment:
Kernel mode only.
Revision History:
alid 10/22/2001 modified for TunMp driver
arvindm 4/6/2000 Created
--*/
#include "precomp.h"
#define __FILENUMBER 'VCER'
NTSTATUS
TunRead(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
/*++
Routine Description:
Dispatch routine to handle IRP_MJ_READ.
Arguments:
pDeviceObject - pointer to our device object
pIrp - Pointer to request packet
Return Value:
NT status code.
--*/
{
PIO_STACK_LOCATION pIrpSp;
ULONG FunctionCode;
NTSTATUS NtStatus;
PTUN_ADAPTER pAdapter;
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pAdapter = pIrpSp->FileObject->FsContext;
DEBUGP(DL_LOUD, ("==>TunRead, pAdapter %p\n",
pAdapter));
do
{
//
// Validate!
//
if (pAdapter == NULL)
{
DEBUGP(DL_FATAL, ("Read: NULL FsContext on FileObject %p\n",
pIrpSp->FileObject));
NtStatus = STATUS_INVALID_HANDLE;
break;
}
TUN_STRUCT_ASSERT(pAdapter, mc);
if (pIrp->MdlAddress == NULL)
{
DEBUGP(DL_FATAL, ("Read: NULL MDL address on IRP %p\n", pIrp));
NtStatus = STATUS_INVALID_PARAMETER;
break;
}
//
// Try to get a virtual address for the MDL.
//
if (MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority) == NULL)
{
DEBUGP(DL_FATAL, ("Read: MmGetSystemAddr failed for IRP %p, MDL %p\n",
pIrp, pIrp->MdlAddress));
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
}
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
if (TUN_TEST_FLAGS(pAdapter, TUN_ADAPTER_OFF))
{
DEBUGP(DL_WARN, ("TunRead, Adapter off. pAdapter %p\n",
pAdapter));
TUN_RELEASE_LOCK(&pAdapter->Lock);
NtStatus = STATUS_INVALID_DEVICE_STATE;
break;
}
//
// Add this IRP to the list of pended Read IRPs
//
TUN_INSERT_TAIL_LIST(&pAdapter->PendedReads, &pIrp->Tail.Overlay.ListEntry);
TUN_REF_ADAPTER(pAdapter); // pended read IRP
pAdapter->PendedReadCount++;
//
// Set up the IRP for possible cancellation.
//
pIrp->Tail.Overlay.DriverContext[0] = (PVOID)pAdapter;
IoMarkIrpPending(pIrp);
IoSetCancelRoutine(pIrp, TunCancelRead);
TUN_RELEASE_LOCK(&pAdapter->Lock);
NtStatus = STATUS_PENDING;
//
// Run the service routine for reads.
//
TunServiceReads(pAdapter);
break;
}
while (FALSE);
if (NtStatus != STATUS_PENDING)
{
TUN_ASSERT(NtStatus != STATUS_SUCCESS);
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = NtStatus;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
}
DEBUGP(DL_LOUD, ("<==TunRead, pAdapter %p\n",
pAdapter));
return (NtStatus);
}
VOID
TunCancelRead(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
/*++
Routine Description:
Cancel a pending read IRP. We unlink the IRP from the open context
queue and complete it.
Arguments:
pDeviceObject - pointer to our device object
pIrp - IRP to be cancelled
Return Value:
None
--*/
{
PTUN_ADAPTER pAdapter;
PLIST_ENTRY pEnt;
PLIST_ENTRY pIrpEntry;
BOOLEAN Found;
IoReleaseCancelSpinLock(pIrp->CancelIrql);
Found = FALSE;
pAdapter = (PTUN_ADAPTER) pIrp->Tail.Overlay.DriverContext[0];
DEBUGP(DL_LOUD, ("==>TunCancelRead, pAdapter %p\n",
pAdapter));
TUN_STRUCT_ASSERT(pAdapter, mc);
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
//
// Locate the IRP in the pended read queue and remove it if found.
//
for (pIrpEntry = pAdapter->PendedReads.Flink;
pIrpEntry != &pAdapter->PendedReads;
pIrpEntry = pIrpEntry->Flink)
{
if (pIrp == CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry))
{
TUN_REMOVE_ENTRY_LIST(&pIrp->Tail.Overlay.ListEntry);
pAdapter->PendedReadCount--;
Found = TRUE;
break;
}
}
if ((!TUN_TEST_FLAG(pAdapter, TUN_ADAPTER_ACTIVE)) &&
(pAdapter->PendedSendCount == 0) &&
(pAdapter->PendedReadCount == 0) &&
(TUN_TEST_FLAG(pAdapter, TUN_COMPLETE_REQUEST)))
{
TUN_CLEAR_FLAG(pAdapter, TUN_COMPLETE_REQUEST);
TUN_RELEASE_LOCK(&pAdapter->Lock);
NdisMSetInformationComplete(&pAdapter->MiniportHandle,
NDIS_STATUS_SUCCESS);
}
else
{
TUN_RELEASE_LOCK(&pAdapter->Lock);
}
if (Found)
{
DEBUGP(DL_LOUD, ("CancelRead: Open %p, IRP %p\n", pAdapter, pIrp));
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
TUN_DEREF_ADAPTER(pAdapter); // Cancel removed pended Read
}
DEBUGP(DL_LOUD, ("<==TunCancelRead, pAdapter %p\n",
pAdapter));
}
VOID
TunServiceReads(
IN PTUN_ADAPTER pAdapter
)
/*++
Routine Description:
Utility routine to copy received data into user buffers and
complete READ IRPs.
Arguments:
pAdapter - pointer to open context
Return Value:
None
--*/
{
PIRP pIrp;
PLIST_ENTRY pIrpEntry;
PNDIS_PACKET pRcvPacket;
PLIST_ENTRY pRcvPacketEntry;
PUCHAR pSrc, pDst;
ULONG BytesRemaining; // at pDst
PNDIS_BUFFER pNdisBuffer;
ULONG BytesAvailable, BytesCopied;
DEBUGP(DL_VERY_LOUD, ("==>ServiceReads: Adapter %p/%x\n",
pAdapter, pAdapter->Flags));
TUN_REF_ADAPTER(pAdapter);
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
while (!TUN_IS_LIST_EMPTY(&pAdapter->PendedReads) &&
!TUN_IS_LIST_EMPTY(&pAdapter->RecvPktQueue))
{
//
// Get the first pended Read IRP
//
pIrpEntry = pAdapter->PendedReads.Flink;
pIrp = CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry);
//
// Check to see if it is being cancelled.
//
if (IoSetCancelRoutine(pIrp, NULL))
{
//
// It isn't being cancelled, and can't be cancelled henceforth.
//
RemoveEntryList(pIrpEntry);
//
// NOTE: we decrement PendedReadCount way below in the
// while loop, to avoid letting through a thread trying
// to unbind.
//
}
else
{
//
// The IRP is being cancelled; let the cancel routine handle it.
//
DEBUGP(DL_LOUD, ("ServiceReads: Adapter %p, skipping cancelled IRP %p\n",
pAdapter, pIrp));
continue;
}
//
// Get the first queued receive packet
//
pRcvPacketEntry = pAdapter->RecvPktQueue.Flink;
RemoveEntryList(pRcvPacketEntry);
pAdapter->RecvPktCount--;
TUN_RELEASE_LOCK(&pAdapter->Lock);
TUN_DEREF_ADAPTER(pAdapter); // Service: dequeue rcv packet
pRcvPacket = TUN_LIST_ENTRY_TO_RCV_PKT(pRcvPacketEntry);
//
// Copy as much data as possible from the receive packet to
// the IRP MDL.
//
pDst = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);
TUN_ASSERT(pDst != NULL); // since it was already mapped
BytesRemaining = MmGetMdlByteCount(pIrp->MdlAddress);
pNdisBuffer = pRcvPacket->Private.Head;
BytesCopied = 0;
while (BytesRemaining > 0 && (pNdisBuffer != NULL))
{
NdisQueryBufferSafe(pNdisBuffer, &pSrc, &BytesAvailable, NormalPagePriority);
if (pSrc == NULL)
{
DEBUGP(DL_FATAL,
("ServiceReads: Adapter %p, QueryBuffer failed for buffer %p\n",
pAdapter, pNdisBuffer));
break;
}
if (BytesAvailable)
{
ULONG BytesToCopy = MIN(BytesAvailable, BytesRemaining);
TUN_COPY_MEM(pDst, pSrc, BytesToCopy);
BytesCopied += BytesToCopy;
BytesRemaining -= BytesToCopy;
pDst += BytesToCopy;
}
NdisGetNextBuffer(pNdisBuffer, &pNdisBuffer);
}
//
// Complete the IRP.
//
//1 shouldn't we fail the read IRP if we couldn't copy the entire data?
//1 check for pNdisBuffer != NULL
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = MmGetMdlByteCount(pIrp->MdlAddress) - BytesRemaining;
DEBUGP(DL_LOUD, ("ServiceReads: Adapter %p, IRP %p completed with %d bytes\n",
pAdapter, pIrp, pIrp->IoStatus.Information));
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
NdisMSendComplete(pAdapter->MiniportHandle,
pRcvPacket,
NDIS_STATUS_SUCCESS);
TUN_DEREF_ADAPTER(pAdapter); // took out pended Read
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
pAdapter->PendedReadCount--;
pAdapter->SendPackets++;
pAdapter->SendBytes += BytesCopied;
}
//1 convert to macro or in-line function
if ((!TUN_TEST_FLAG(pAdapter, TUN_ADAPTER_ACTIVE)) &&
(pAdapter->PendedSendCount == 0) &&
(pAdapter->PendedReadCount == 0) &&
(TUN_TEST_FLAG(pAdapter, TUN_COMPLETE_REQUEST)))
{
TUN_CLEAR_FLAG(pAdapter, TUN_COMPLETE_REQUEST);
TUN_RELEASE_LOCK(&pAdapter->Lock);
NdisMSetInformationComplete(&pAdapter->MiniportHandle,
NDIS_STATUS_SUCCESS);
}
else
{
TUN_RELEASE_LOCK(&pAdapter->Lock);
}
TUN_DEREF_ADAPTER(pAdapter); // temp ref - service reads
DEBUGP(DL_VERY_LOUD, ("<==ServiceReads: Adapter %p\n",
pAdapter));
}
VOID
TunCancelPendingReads(
IN PTUN_ADAPTER pAdapter
)
/*++
Routine Description:
Cancel any pending read IRPs queued on the given open.
Arguments:
pAdapter - pointer to open context
Return Value:
None
--*/
{
PIRP pIrp;
PLIST_ENTRY pIrpEntry;
DEBUGP(DL_LOUD, ("==>TunCancelPendingReads: Adapter %p/%x\n",
pAdapter, pAdapter->Flags));
TUN_REF_ADAPTER(pAdapter); // temp ref - cancel reads
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
while (!TUN_IS_LIST_EMPTY(&pAdapter->PendedReads))
{
//
// Get the first pended Read IRP
//
pIrpEntry = pAdapter->PendedReads.Flink;
pIrp = CONTAINING_RECORD(pIrpEntry, IRP, Tail.Overlay.ListEntry);
//
// Check to see if it is being cancelled.
//
if (IoSetCancelRoutine(pIrp, NULL))
{
//
// It isn't being cancelled, and can't be cancelled henceforth.
//
TUN_REMOVE_ENTRY_LIST(pIrpEntry);
TUN_RELEASE_LOCK(&pAdapter->Lock);
//
// Complete the IRP.
//
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
DEBUGP(DL_LOUD, ("CancelPendingReads: Open %p, IRP %p cancelled\n",
pAdapter, pIrp));
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
TUN_DEREF_ADAPTER(pAdapter); // took out pended Read for cancelling
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
pAdapter->PendedReadCount--;
}
else
{
//
// It is being cancelled, let the cancel routine handle it.
//
TUN_RELEASE_LOCK(&pAdapter->Lock);
//
// Give the cancel routine some breathing space, otherwise
// we might end up examining the same (cancelled) IRP over
// and over again.
//
TUN_SLEEP(1);
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
}
}
if ((!TUN_TEST_FLAG(pAdapter, TUN_ADAPTER_ACTIVE)) &&
(pAdapter->PendedSendCount == 0) &&
(pAdapter->PendedReadCount == 0) &&
(TUN_TEST_FLAG(pAdapter, TUN_COMPLETE_REQUEST)))
{
TUN_CLEAR_FLAG(pAdapter, TUN_COMPLETE_REQUEST);
TUN_RELEASE_LOCK(&pAdapter->Lock);
NdisMSetInformationComplete(&pAdapter->MiniportHandle,
NDIS_STATUS_SUCCESS);
}
else
{
TUN_RELEASE_LOCK(&pAdapter->Lock);
}
TUN_DEREF_ADAPTER(pAdapter); // temp ref - cancel reads
DEBUGP(DL_LOUD, ("<==TunCancelPendingReads: Adapter %p/%x\n",
pAdapter, pAdapter->Flags));
}
VOID
TunFlushReceiveQueue(
IN PTUN_ADAPTER pAdapter
)
/*++
Routine Description:
Free any receive packets queued up on the specified open
Arguments:
pAdapter - pointer to open context
Return Value:
None
--*/
{
PLIST_ENTRY pRcvPacketEntry;
PNDIS_PACKET pRcvPacket;
DEBUGP(DL_LOUD, ("==>TunFlushReceiveQueue: Adapter %p/%x\n",
pAdapter, pAdapter->Flags));
TUN_REF_ADAPTER(pAdapter); // temp ref - flushRcvQueue
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
while (!TUN_IS_LIST_EMPTY(&pAdapter->RecvPktQueue))
{
//
// Get the first queued receive packet
//
pRcvPacketEntry = pAdapter->RecvPktQueue.Flink;
TUN_REMOVE_ENTRY_LIST(pRcvPacketEntry);
pAdapter->RecvPktCount --;
pAdapter->XmitError++;
TUN_RELEASE_LOCK(&pAdapter->Lock);
pRcvPacket = TUN_LIST_ENTRY_TO_RCV_PKT(pRcvPacketEntry);
DEBUGP(DL_LOUD, ("FlushReceiveQueue: open %p, pkt %p\n",
pAdapter, pRcvPacket));
NdisMSendComplete(pAdapter->MiniportHandle,
pRcvPacket,
NDIS_STATUS_REQUEST_ABORTED);
TUN_DEREF_ADAPTER(pAdapter); // took out pended Read
TUN_ACQUIRE_LOCK(&pAdapter->Lock);
}
TUN_RELEASE_LOCK(&pAdapter->Lock);
TUN_DEREF_ADAPTER(pAdapter); // temp ref - flushRcvQueue
DEBUGP(DL_LOUD, ("<==TunFlushReceiveQueue: Adapter %p/%x\n",
pAdapter, pAdapter->Flags));
}