You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3300 lines
78 KiB
3300 lines
78 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
atp.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the Appletalk Transaction Protocol code.
|
|
|
|
Author:
|
|
|
|
Jameel Hyder ([email protected])
|
|
Nikhil Kamkolkar ([email protected])
|
|
|
|
Revision History:
|
|
19 Jun 1992 Initial Version
|
|
|
|
Notes: Tab stop: 4
|
|
|
|
25 Mar 1994 JH - Changed the request response paradigm. It now works as follows:
|
|
When a request comes in, a response structure is allocated, initialized
|
|
and linked into the address object either in the hash table if it is a
|
|
XO request or the ALO linear list if it an ALO.
|
|
The GetReq handler is passed a pointer to the response structure. This
|
|
is referenced for the GetReq handler. The GetReq handler must Dereference
|
|
it explicity either in its release handler if a response was posted or
|
|
after a CancelResp is called.
|
|
|
|
The respDeref notifies the release handler when the reference goes to 1
|
|
and frees it up when it goes to zero.
|
|
|
|
The GetReq structure is now re-used if the handler so specifies. This
|
|
avoids the free-ing and re-allocing of these structures as well as
|
|
the need to call AtalkAtpGetReq() from within the handler.
|
|
|
|
Retry and release timers are per-atp-address now instead of one per
|
|
request and one per response. The release handler is not 'started'
|
|
till a response is posted.
|
|
--*/
|
|
|
|
#include <atalk.h>
|
|
#pragma hdrstop
|
|
#define FILENUM ATP
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE_PAP, AtalkAtpOpenAddress) // Since PAP is the only one which calls
|
|
// at DISPATCH_LEVEL
|
|
#pragma alloc_text(PAGE_ATP, AtalkAtpCleanupAddress)
|
|
#pragma alloc_text(PAGE_ATP, AtalkAtpCloseAddress)
|
|
#pragma alloc_text(PAGE_ATP, AtalkAtpPostReq)
|
|
#pragma alloc_text(PAGE_ATP, AtalkAtpSetReqHandler)
|
|
#pragma alloc_text(PAGE_ATP, AtalkAtpPostResp)
|
|
#pragma alloc_text(PAGE_ATP, AtalkAtpCancelReq)
|
|
#pragma alloc_text(PAGE_ATP, AtalkAtpIsReqComplete)
|
|
#pragma alloc_text(PAGE_ATP, AtalkAtpCancelResp)
|
|
#pragma alloc_text(PAGE_ATP, AtalkAtpCancelRespByTid)
|
|
#pragma alloc_text(PAGE_ATP, AtalkAtpPacketIn)
|
|
#pragma alloc_text(PAGE_ATP, atalkAtpTransmitReq)
|
|
#pragma alloc_text(PAGE_ATP, atalkAtpSendReqComplete)
|
|
#pragma alloc_text(PAGE_ATP, atalkAtpTransmitResp)
|
|
#pragma alloc_text(PAGE_ATP, atalkAtpSendRespComplete)
|
|
#pragma alloc_text(PAGE_ATP, atalkAtpTransmitRel)
|
|
#pragma alloc_text(PAGE_ATP, atalkAtpSendRelComplete)
|
|
#pragma alloc_text(PAGE_ATP, atalkAtpRespComplete)
|
|
#pragma alloc_text(PAGE_ATP, atalkAtpReqComplete)
|
|
#pragma alloc_text(PAGE_ATP, atalkAtpGetNextTidForAddr)
|
|
#pragma alloc_text(PAGE_ATP, atalkAtpReqRefNextNc)
|
|
#pragma alloc_text(PAGE_ATP, atalkAtpReqDeref)
|
|
#pragma alloc_text(PAGE_ATP, atalkAtpRespRefNextNc)
|
|
#pragma alloc_text(PAGE_ATP, AtalkAtpRespDeref)
|
|
#pragma alloc_text(PAGE_ATP, atalkAtpReqTimer)
|
|
#pragma alloc_text(PAGE_ATP, atalkAtpRelTimer)
|
|
#pragma alloc_text(PAGE_ATP, AtalkAtpGenericRespComplete)
|
|
|
|
#endif
|
|
|
|
ATALK_ERROR
|
|
AtalkAtpOpenAddress(
|
|
IN PPORT_DESCRIPTOR pPort,
|
|
IN BYTE Socket,
|
|
IN OUT PATALK_NODEADDR pDesiredNode OPTIONAL,
|
|
IN USHORT MaxSinglePktSize,
|
|
IN BOOLEAN SendUserBytesAll,
|
|
IN PATALK_DEV_CTX pDevCtx OPTIONAL,
|
|
IN BOOLEAN CacheSocket,
|
|
OUT PATP_ADDROBJ * ppAtpAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PATP_ADDROBJ pAtpAddr;
|
|
ATALK_ERROR error;
|
|
|
|
do
|
|
{
|
|
if ((pAtpAddr = AtalkAllocZeroedMemory(sizeof(ATP_ADDROBJ))) == NULL)
|
|
{
|
|
error = ATALK_RESR_MEM;
|
|
break;
|
|
}
|
|
|
|
// Initialize this structure. Note that packet handler could
|
|
// entered with this context even before secondary initialization
|
|
// completes. So we make sure, that it will not touch anything
|
|
// until then by using the OPEN flag.
|
|
|
|
#if DBG
|
|
pAtpAddr->atpao_Signature = ATPAO_SIGNATURE;
|
|
#endif
|
|
|
|
// Set creation reference count, include one each for release and retry timers
|
|
pAtpAddr->atpao_RefCount = (CacheSocket ? 4 : 3);
|
|
pAtpAddr->atpao_NextTid = 1;
|
|
pAtpAddr->atpao_MaxSinglePktSize = MaxSinglePktSize;
|
|
pAtpAddr->atpao_DevCtx = pDevCtx;
|
|
|
|
if (SendUserBytesAll)
|
|
{
|
|
pAtpAddr->atpao_Flags |= ATPAO_SENDUSERBYTESALL;
|
|
}
|
|
|
|
InitializeListHead(&pAtpAddr->atpao_ReqList);
|
|
AtalkTimerInitialize(&pAtpAddr->atpao_RelTimer,
|
|
atalkAtpRelTimer,
|
|
ATP_RELEASE_TIMER_INTERVAL);
|
|
|
|
InitializeListHead(&pAtpAddr->atpao_RespList);
|
|
AtalkTimerInitialize(&pAtpAddr->atpao_RetryTimer,
|
|
atalkAtpReqTimer,
|
|
ATP_RETRY_TIMER_INTERVAL);
|
|
|
|
// Open the ddp socket
|
|
error = AtalkDdpOpenAddress(pPort,
|
|
Socket,
|
|
pDesiredNode,
|
|
AtalkAtpPacketIn,
|
|
pAtpAddr,
|
|
DDPPROTO_ANY,
|
|
pDevCtx,
|
|
&pAtpAddr->atpao_DdpAddr);
|
|
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
// Socket open error will be logged at the ddp level.
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
|
|
("AtalkAtpOpenAddress: AtalkDdpOpenAddress failed %ld\n", error));
|
|
|
|
AtalkFreeMemory(pAtpAddr);
|
|
break;
|
|
}
|
|
|
|
// Activate the atp socket. Cache the socket if desired.
|
|
// This takes port lock on default port.
|
|
if (CacheSocket)
|
|
{
|
|
if (!ATALK_SUCCESS(AtalkIndAtpCacheSocket(pAtpAddr, pPort)))
|
|
{
|
|
pAtpAddr->atpao_RefCount--;
|
|
CacheSocket = FALSE;
|
|
}
|
|
}
|
|
pAtpAddr->atpao_Flags |= (ATPAO_OPEN | ATPAO_TIMERS | (CacheSocket ? ATPAO_CACHED : 0));
|
|
|
|
AtalkLockAtpIfNecessary();
|
|
|
|
// Start the release timer for responses on this address
|
|
AtalkTimerScheduleEvent(&pAtpAddr->atpao_RelTimer);
|
|
|
|
// Start the retry timer for requests on this address
|
|
AtalkTimerScheduleEvent(&pAtpAddr->atpao_RetryTimer);
|
|
|
|
*ppAtpAddr = pAtpAddr;
|
|
} while (FALSE);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAtpCleanupAddress(
|
|
IN PATP_ADDROBJ pAtpAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PATP_REQ pAtpReq, pAtpReqNext;
|
|
PATP_RESP pAtpResp, pAtpRespNext;
|
|
ATP_REQ_HANDLER ReqHandler;
|
|
ATALK_ERROR error = ATALK_PENDING;
|
|
KIRQL OldIrql;
|
|
USHORT i;
|
|
BOOLEAN cached, CancelTimers, done, ReEnqueue;
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAtpAddr->atpao_Lock, &OldIrql);
|
|
|
|
CancelTimers = FALSE;
|
|
done = FALSE;
|
|
if (pAtpAddr->atpao_Flags & ATPAO_TIMERS)
|
|
{
|
|
pAtpAddr->atpao_Flags &= ~ATPAO_TIMERS;
|
|
CancelTimers = TRUE;
|
|
}
|
|
|
|
if (pAtpAddr->atpao_Flags & ATPAO_CLEANUP)
|
|
{
|
|
done = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// put a Cleanup refcount for this routine, since we are going to cleanup
|
|
pAtpAddr->atpao_RefCount++;
|
|
}
|
|
pAtpAddr->atpao_Flags |= ATPAO_CLEANUP;
|
|
|
|
RELEASE_SPIN_LOCK(&pAtpAddr->atpao_Lock, OldIrql);
|
|
|
|
if (done)
|
|
{
|
|
return error;
|
|
}
|
|
|
|
if (CancelTimers)
|
|
{
|
|
// Cancel the release timer
|
|
if (AtalkTimerCancelEvent(&pAtpAddr->atpao_RelTimer, NULL))
|
|
{
|
|
AtalkAtpAddrDereference(pAtpAddr);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
|
|
("AtalkAtpCleanupAddress: couldn't cancel release timer\n"));
|
|
}
|
|
|
|
// And also the retry timer
|
|
if (AtalkTimerCancelEvent(&pAtpAddr->atpao_RetryTimer, NULL))
|
|
{
|
|
AtalkAtpAddrDereference(pAtpAddr);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
|
|
("AtalkAtpCleanupAddress: couldn't cancel retry timer\n"));
|
|
}
|
|
}
|
|
|
|
ASSERT (pAtpAddr->atpao_RefCount >= 1); // creation ref
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAtpAddr->atpao_Lock, &OldIrql);
|
|
|
|
// Call requests handler if set
|
|
if ((ReqHandler = pAtpAddr->atpao_ReqHandler) != NULL)
|
|
{
|
|
pAtpAddr->atpao_ReqHandler = NULL;
|
|
|
|
RELEASE_SPIN_LOCK(&pAtpAddr->atpao_Lock, OldIrql);
|
|
|
|
(*ReqHandler)(ATALK_ATP_CLOSING,
|
|
pAtpAddr->atpao_ReqCtx,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
|
|
// Dereference address object.
|
|
AtalkAtpAddrDereference(pAtpAddr);
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAtpAddr->atpao_Lock, &OldIrql);
|
|
}
|
|
|
|
// Cancel all the requests.
|
|
for (i = 0; i < ATP_REQ_HASH_SIZE; i++)
|
|
{
|
|
if ((pAtpReq = pAtpAddr->atpao_ReqHash[i]) == NULL)
|
|
{
|
|
// If empty, go on to the next index in hash table.
|
|
continue;
|
|
}
|
|
|
|
// Includes the one we are starting with.
|
|
atalkAtpReqRefNextNc(pAtpReq, &pAtpReqNext, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
// No requests left on this index. Go to the next one.
|
|
continue;
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
if ((pAtpReq = pAtpReqNext) == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ((pAtpReqNext = pAtpReq->req_Next) != NULL)
|
|
{
|
|
atalkAtpReqRefNextNc(pAtpReq->req_Next, &pAtpReqNext, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
// No requests left on this index. Go to the next one.
|
|
pAtpReqNext = NULL;
|
|
}
|
|
}
|
|
|
|
// Cancel this request.
|
|
RELEASE_SPIN_LOCK(&pAtpAddr->atpao_Lock, OldIrql);
|
|
|
|
AtalkAtpCancelReq(pAtpAddr,
|
|
pAtpReq->req_Tid,
|
|
&pAtpReq->req_Dest);
|
|
|
|
ASSERTMSG("RefCount incorrect\n", (pAtpReq->req_RefCount >= 1));
|
|
|
|
// remove the refcount added in the beginning of the loop
|
|
AtalkAtpReqDereference(pAtpReq);
|
|
ACQUIRE_SPIN_LOCK(&pAtpAddr->atpao_Lock, &OldIrql);
|
|
}
|
|
}
|
|
|
|
// Cancel all pending responses.
|
|
for (i = 0; i < ATP_RESP_HASH_SIZE; i++)
|
|
{
|
|
if ((pAtpResp = pAtpAddr->atpao_RespHash[i]) == NULL)
|
|
{
|
|
// If empty, go on to the next index in hash table.
|
|
continue;
|
|
}
|
|
|
|
// Includes the one we are starting with.
|
|
atalkAtpRespRefNextNc(pAtpResp, &pAtpRespNext, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
// No requests left on this index. Go to the next one.
|
|
continue;
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
if ((pAtpResp = pAtpRespNext) == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ((pAtpRespNext = pAtpResp->resp_Next) != NULL)
|
|
{
|
|
atalkAtpRespRefNextNc(pAtpResp->resp_Next, &pAtpRespNext, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
// No requests left on this index. Go to the next one.
|
|
pAtpRespNext = NULL;
|
|
}
|
|
}
|
|
|
|
// Cancel this response
|
|
RELEASE_SPIN_LOCK(&pAtpAddr->atpao_Lock, OldIrql);
|
|
|
|
AtalkAtpCancelResp(pAtpResp);
|
|
|
|
// remove the refcount added in the beginning of the loop
|
|
AtalkAtpRespDereference(pAtpResp);
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAtpAddr->atpao_Lock, &OldIrql);
|
|
}
|
|
}
|
|
|
|
// if the socket was cached, uncache it, remove reference.
|
|
cached = FALSE;
|
|
if (pAtpAddr->atpao_Flags & ATPAO_CACHED)
|
|
{
|
|
cached = TRUE;
|
|
pAtpAddr->atpao_Flags &= ~ATPAO_CACHED;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&pAtpAddr->atpao_Lock, OldIrql);
|
|
|
|
if (cached)
|
|
{
|
|
AtalkIndAtpUnCacheSocket(pAtpAddr);
|
|
AtalkAtpAddrDereference(pAtpAddr);
|
|
}
|
|
|
|
// remove the Cleanup refcount we put at the beginning of this routine
|
|
AtalkAtpAddrDereference(pAtpAddr);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAtpCloseAddress(
|
|
IN PATP_ADDROBJ pAtpAddr,
|
|
IN ATPAO_CLOSECOMPLETION pCloseCmp OPTIONAL,
|
|
IN PVOID pCloseCtx OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
BOOLEAN cleanup;
|
|
|
|
// Cancel all the pending get requests.
|
|
ACQUIRE_SPIN_LOCK(&pAtpAddr->atpao_Lock, &OldIrql);
|
|
if ((pAtpAddr->atpao_Flags & ATPAO_CLOSING) == 0)
|
|
{
|
|
cleanup = TRUE;
|
|
if (pAtpAddr->atpao_Flags & ATPAO_CLEANUP)
|
|
cleanup = FALSE;
|
|
|
|
pAtpAddr->atpao_Flags |= ATPAO_CLOSING;
|
|
pAtpAddr->atpao_CloseComp = pCloseCmp;
|
|
pAtpAddr->atpao_CloseCtx = pCloseCtx;
|
|
RELEASE_SPIN_LOCK(&pAtpAddr->atpao_Lock, OldIrql);
|
|
|
|
if (cleanup)
|
|
AtalkAtpCleanupAddress(pAtpAddr);
|
|
|
|
// Remove the creation reference
|
|
AtalkAtpAddrDereference(pAtpAddr);
|
|
}
|
|
else
|
|
{
|
|
// We are already closing! this should never happen!
|
|
ASSERT ((pAtpAddr->atpao_Flags & ATPAO_CLOSING) != 0);
|
|
KeBugCheck(0);
|
|
}
|
|
|
|
return ATALK_PENDING;
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAtpPostReq(
|
|
IN PATP_ADDROBJ pAtpAddr,
|
|
IN PATALK_ADDR pDest,
|
|
OUT PUSHORT pTid,
|
|
IN USHORT Flags,
|
|
IN PAMDL pReq,
|
|
IN USHORT ReqLen,
|
|
IN PBYTE pUserBytes OPTIONAL,
|
|
IN OUT PAMDL pResp OPTIONAL,
|
|
IN USHORT RespLen OPTIONAL,
|
|
IN SHORT RetryCnt,
|
|
IN LONG RetryInterval OPTIONAL, // In timer ticks
|
|
IN RELEASE_TIMERVALUE RelTimerVal,
|
|
IN ATP_RESP_HANDLER pCmpRoutine OPTIONAL,
|
|
IN PVOID pCtx OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PATP_REQ pAtpReq;
|
|
KIRQL OldIrql;
|
|
ULONG index;
|
|
ATALK_ERROR error = ATALK_NO_ERROR;
|
|
|
|
// Verify relevant parameters.
|
|
do
|
|
{
|
|
#ifdef ATP_STRICT
|
|
// NOTE: These checks need to be added to the TDI interface if/when ATP is
|
|
// opened upto user mode.
|
|
if ((ReqLen < 0) ||
|
|
(ReqLen > pAtpAddr->atpao_MaxSinglePktSize) ||
|
|
(RespLen < 0) ||
|
|
(RespLen > (pAtpAddr->atpao_MaxSinglePktSize * ATP_MAX_RESP_PKTS)))
|
|
{
|
|
error = ATALK_BUFFER_TOO_BIG;
|
|
break;
|
|
}
|
|
|
|
if ((RetryCnt < 0) && (RetryCnt != ATP_INFINITE_RETRIES))
|
|
{
|
|
error = ATALK_ATP_INVALID_RETRYCNT;
|
|
break;
|
|
}
|
|
|
|
if ((RelTimerVal < FIRSTVALID_TIMER) || (RelTimerVal > LAST_VALID_TIMER))
|
|
{
|
|
error = ATALK_ATP_INVALID_TIMERVAL;
|
|
break;
|
|
}
|
|
|
|
if (RetryInterval < 0)
|
|
{
|
|
error = ATALK_ATP_INVALID_RELINT;
|
|
break;
|
|
}
|
|
#endif
|
|
// The only valid values for Flags are ATP_REQ_EXACTLY_ONCE and ATP_REQ_REMOTE
|
|
ASSERT ((Flags & ~(ATP_REQ_EXACTLY_ONCE | ATP_REQ_REMOTE)) == 0);
|
|
|
|
if (RetryInterval == 0)
|
|
{
|
|
RetryInterval = ATP_DEF_RETRY_INTERVAL;
|
|
}
|
|
|
|
// Reference the address object.
|
|
AtalkAtpAddrReference(pAtpAddr, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ((pAtpReq = (PATP_REQ)AtalkBPAllocBlock(BLKID_ATPREQ)) == NULL)
|
|
{
|
|
AtalkAtpAddrDereference(pAtpAddr);
|
|
error = ATALK_RESR_MEM;
|
|
break;
|
|
}
|
|
} while (FALSE);
|
|
|
|
if (!ATALK_SUCCESS(error))
|
|
return error;
|
|
|
|
// We have memory allocated and the address object referenced at this
|
|
// point. Initialize the request structure.
|
|
#if DBG
|
|
RtlZeroMemory(pAtpReq, sizeof(ATP_REQ));
|
|
pAtpReq->req_Signature = ATP_REQ_SIGNATURE;
|
|
#endif
|
|
|
|
// Initial reference count - for creation.
|
|
// Also another ref for this routine itself. Ran into a situation
|
|
// where a thread posting the request was preempted and a close called.
|
|
// So at the point where the first thread is doing the transmit call,
|
|
// the request structure is already freed.
|
|
pAtpReq->req_RefCount = 2;
|
|
pAtpReq->req_pAtpAddr = pAtpAddr;
|
|
|
|
pAtpReq->req_RetryInterval = RetryInterval;
|
|
pAtpReq->req_RetryCnt = RetryCnt;
|
|
pAtpReq->req_RelTimerValue = RelTimerVal;
|
|
|
|
pAtpReq->req_Dest = *pDest;
|
|
pAtpReq->req_Buf = pReq;
|
|
pAtpReq->req_BufLen = ReqLen;
|
|
|
|
if (RetryCnt != 0)
|
|
Flags |= ATP_REQ_RETRY_TIMER;
|
|
pAtpReq->req_Flags = Flags;
|
|
|
|
if (pUserBytes != NULL)
|
|
{
|
|
RtlCopyMemory(pAtpReq->req_UserBytes,
|
|
pUserBytes,
|
|
ATP_USERBYTES_SIZE);
|
|
}
|
|
else
|
|
{
|
|
pAtpReq->req_dwUserBytes = 0;
|
|
}
|
|
|
|
pAtpReq->req_RespBuf = pResp;
|
|
pAtpReq->req_RespBufLen = RespLen;
|
|
atalkAtpBufferSizeToBitmap( pAtpReq->req_Bitmap,
|
|
RespLen,
|
|
pAtpAddr->atpao_MaxSinglePktSize);
|
|
pAtpReq->req_RespRecdLen = 0;
|
|
pAtpReq->req_RecdBitmap = 0;
|
|
|
|
// Setup the ndis buffer descriptors for the response buffer
|
|
AtalkIndAtpSetupNdisBuffer(pAtpReq, pAtpAddr->atpao_MaxSinglePktSize);
|
|
|
|
pAtpReq->req_Comp = pCmpRoutine;
|
|
pAtpReq->req_Ctx = pCtx;
|
|
|
|
INITIALIZE_SPIN_LOCK(&pAtpReq->req_Lock);
|
|
|
|
ACQUIRE_SPIN_LOCK(&pAtpAddr->atpao_Lock, &OldIrql);
|
|
atalkAtpGetNextTidForAddr(pAtpAddr,
|
|
pDest,
|
|
&pAtpReq->req_Tid,
|
|
&index);
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpPostReq: Tid %lx for %lx.%lx.%lx\n",
|
|
pAtpReq->req_Tid, pDest->ata_Network,
|
|
pDest->ata_Node, pDest->ata_Socket));
|
|
|
|
// Get the index where this request is supposed to go.
|
|
// Need to know the tid.
|
|
index = ATP_HASH_TID_DESTADDR(pAtpReq->req_Tid, pDest, ATP_REQ_HASH_SIZE);
|
|
|
|
// Put this in the request queue
|
|
AtalkLinkDoubleAtHead(pAtpAddr->atpao_ReqHash[index],
|
|
pAtpReq,
|
|
req_Next,
|
|
req_Prev);
|
|
|
|
if (RetryCnt != 0)
|
|
{
|
|
// Set the time stamp when this should be retried
|
|
pAtpReq->req_RetryTimeStamp = AtalkGetCurrentTick() + RetryInterval;
|
|
|
|
InsertTailList(&pAtpAddr->atpao_ReqList, &pAtpReq->req_List);
|
|
}
|
|
|
|
#ifdef PROFILING
|
|
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_AtpNumRequests,
|
|
&AtalkStatsLock.SpinLock);
|
|
#endif
|
|
RELEASE_SPIN_LOCK(&pAtpAddr->atpao_Lock, OldIrql);
|
|
|
|
// Return the tid
|
|
*pTid = pAtpReq->req_Tid;
|
|
|
|
// Now send the request
|
|
atalkAtpTransmitReq(pAtpReq);
|
|
|
|
// Remove the ref added at the beginning of this routine.
|
|
AtalkAtpReqDereference(pAtpReq);
|
|
|
|
return ATALK_NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
AtalkAtpSetReqHandler(
|
|
IN PATP_ADDROBJ pAtpAddr,
|
|
IN ATP_REQ_HANDLER ReqHandler,
|
|
IN PVOID ReqCtx OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
ATALK_ERROR error;
|
|
|
|
ASSERT (ReqHandler != NULL);
|
|
|
|
// Set the request handler in the address object
|
|
ACQUIRE_SPIN_LOCK(&pAtpAddr->atpao_Lock, &OldIrql);
|
|
|
|
ASSERT((pAtpAddr->atpao_Flags & ATPAO_CLOSING) == 0);
|
|
pAtpAddr->atpao_RefCount++;
|
|
pAtpAddr->atpao_ReqHandler = ReqHandler;
|
|
pAtpAddr->atpao_ReqCtx = ReqCtx;
|
|
|
|
RELEASE_SPIN_LOCK(&pAtpAddr->atpao_Lock, OldIrql);
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAtpPostResp(
|
|
IN PATP_RESP pAtpResp,
|
|
IN PATALK_ADDR pDest,
|
|
IN OUT PAMDL pResp,
|
|
IN USHORT RespLen,
|
|
IN PBYTE pUserBytes OPTIONAL,
|
|
IN ATP_REL_HANDLER pCmpRoutine,
|
|
IN PVOID pCtx OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PATP_ADDROBJ pAtpAddr;
|
|
BOOLEAN addrlocked = FALSE, resplocked = FALSE;
|
|
BOOLEAN DerefAddr = FALSE, DerefResp = FALSE;
|
|
SHORT ResponseLen;
|
|
KIRQL OldIrql;
|
|
ATALK_ERROR error;
|
|
|
|
ASSERT(VALID_ATPRS(pAtpResp));
|
|
ASSERT ((pAtpResp->resp_Flags & (ATP_RESP_VALID_RESP |
|
|
ATP_RESP_REL_TIMER |
|
|
ATP_RESP_HANDLER_NOTIFIED)) == 0);
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpPostResp: Posting response for Resp %lx, Tid %x %s\n",
|
|
pAtpResp, pAtpResp->resp_Tid,
|
|
(pAtpResp->resp_Flags & ATP_RESP_EXACTLY_ONCE) ? "XO" : "ALO"));
|
|
|
|
pAtpAddr = pAtpResp->resp_pAtpAddr;
|
|
ASSERT(VALID_ATPAO(pAtpAddr));
|
|
|
|
do
|
|
{
|
|
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
|
|
|
|
if ((RespLen < 0) ||
|
|
(RespLen > (pAtpAddr->atpao_MaxSinglePktSize * ATP_MAX_RESP_PKTS)))
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
|
|
("AtalkAtpPostResp: Invalid buffer size %ld", RespLen));
|
|
error = ATALK_BUFFER_INVALID_SIZE;
|
|
break;
|
|
}
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
addrlocked = TRUE;
|
|
|
|
atalkAtpAddrRefNonInterlock(pAtpAddr, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
break;
|
|
}
|
|
DerefAddr = TRUE;
|
|
|
|
atalkAtpBitmapToBufferSize( ResponseLen,
|
|
pAtpResp->resp_Bitmap,
|
|
pAtpAddr->atpao_MaxSinglePktSize);
|
|
if (ResponseLen < RespLen)
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
|
|
("AtalkAtpPostResp: bitmap resplen (%d) < specified (%d)\n",
|
|
ResponseLen, RespLen));
|
|
error = ATALK_BUFFER_TOO_BIG;
|
|
break;
|
|
}
|
|
|
|
|
|
AtalkAtpRespReferenceByPtrDpc(pAtpResp, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
break;
|
|
}
|
|
DerefResp = TRUE;
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpResp->resp_Lock);
|
|
resplocked = TRUE;
|
|
|
|
if (pAtpResp->resp_Flags & (ATP_RESP_CLOSING | ATP_RESP_CANCELLED))
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
|
|
("AtalkAtpPostResp: Closing/Cancelled %x", pAtpResp->resp_Flags));
|
|
error = ATALK_ATP_RESP_CLOSING;
|
|
break;
|
|
}
|
|
|
|
if (pAtpResp->resp_Flags & ATP_RESP_VALID_RESP)
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
|
|
("AtalkAtpPostResp: Already posted !\n"));
|
|
error = ATALK_ATP_RESP_TOOMANY;
|
|
break;
|
|
}
|
|
|
|
// No response was previously posted. OK to proceed.
|
|
pAtpResp->resp_Flags |= ATP_RESP_VALID_RESP;
|
|
|
|
pAtpResp->resp_Buf = pResp;
|
|
pAtpResp->resp_BufLen = RespLen;
|
|
pAtpResp->resp_Comp = pCmpRoutine;
|
|
|
|
ASSERT(pCmpRoutine != NULL);
|
|
|
|
pAtpResp->resp_Ctx = pCtx;
|
|
pAtpResp->resp_Dest = *pDest;
|
|
pAtpResp->resp_UserBytesOnly = (pAtpResp->resp_Bitmap == 0) ? TRUE : FALSE;
|
|
|
|
if (ARGUMENT_PRESENT(pUserBytes))
|
|
{
|
|
pAtpResp->resp_dwUserBytes = *(UNALIGNED ULONG *)pUserBytes;
|
|
}
|
|
else
|
|
{
|
|
pAtpResp->resp_dwUserBytes = 0;
|
|
}
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpPostResp: Posting response for %s request id %x\n",
|
|
(pAtpResp->resp_Flags & ATP_RESP_EXACTLY_ONCE) ? "XO" : "ALO",
|
|
pAtpResp->resp_Tid));
|
|
|
|
// Now setup to start the release timer, but only for XO
|
|
if (pAtpResp->resp_Flags & ATP_RESP_EXACTLY_ONCE)
|
|
{
|
|
pAtpResp->resp_Flags |= ATP_RESP_REL_TIMER;
|
|
InsertTailList(&pAtpAddr->atpao_RespList, &pAtpResp->resp_List);
|
|
}
|
|
|
|
// For ALO set the comp status right here.
|
|
pAtpResp->resp_CompStatus = error = ATALK_NO_ERROR;
|
|
} while (FALSE);
|
|
|
|
if (addrlocked)
|
|
{
|
|
if (resplocked)
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpResp->resp_Lock);
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
}
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
// Send the response.
|
|
ASSERT(pAtpResp->resp_Flags & ATP_RESP_VALID_RESP);
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_WARN,
|
|
("AtalkAtpPostResp: Transmitting response %lx\n", pAtpResp));
|
|
atalkAtpTransmitResp(pAtpResp);
|
|
}
|
|
|
|
// Dereference the address object.
|
|
if (DerefAddr)
|
|
AtalkAtpAddrDereferenceDpc(pAtpAddr);
|
|
|
|
if (DerefResp)
|
|
AtalkAtpRespDereferenceDpc(pAtpResp);
|
|
|
|
// for ALO transactions, we are done so take away the creation reference
|
|
if ((pAtpResp->resp_Flags & ATP_RESP_EXACTLY_ONCE) == 0)
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpPostResp: Removing creation reference for ALO request %lx Tid %x\n",
|
|
pAtpResp, pAtpResp->resp_Tid));
|
|
AtalkAtpRespDereferenceDpc(pAtpResp);
|
|
}
|
|
|
|
if (OldIrql != DISPATCH_LEVEL)
|
|
KeLowerIrql(OldIrql);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAtpCancelReq(
|
|
IN PATP_ADDROBJ pAtpAddr,
|
|
IN USHORT Tid,
|
|
IN PATALK_ADDR pDest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
KIRQL OldIrql;
|
|
PATP_REQ pAtpReq;
|
|
|
|
// Find the request.
|
|
ACQUIRE_SPIN_LOCK(&pAtpAddr->atpao_Lock, &OldIrql);
|
|
atalkAtpReqReferenceByAddrTidDpc(pAtpAddr,
|
|
pDest,
|
|
Tid,
|
|
&pAtpReq,
|
|
&error);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_WARN,
|
|
("AtalkAtpCancelReq: Cancelling req tid %x\n", Tid));
|
|
|
|
// Request is referenced for us. Remove the creation reference.
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpReq->req_Lock);
|
|
|
|
// Do not cancel a request that has just about been satisfied anyway !!!
|
|
if (pAtpReq->req_Flags & ATP_REQ_RESPONSE_COMPLETE)
|
|
{
|
|
error = ATALK_ATP_REQ_CLOSING;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpReq->req_Lock);
|
|
|
|
RELEASE_SPIN_LOCK(&pAtpAddr->atpao_Lock, OldIrql);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
// Try to remove the creation reference
|
|
atalkAtpReqComplete(pAtpReq, ATALK_ATP_REQ_CANCELLED);
|
|
}
|
|
|
|
// Remove the reference added at the beginning.
|
|
AtalkAtpReqDereference(pAtpReq);
|
|
}
|
|
else RELEASE_SPIN_LOCK(&pAtpAddr->atpao_Lock, OldIrql);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOLEAN
|
|
AtalkAtpIsReqComplete(
|
|
IN PATP_ADDROBJ pAtpAddr,
|
|
IN USHORT Tid,
|
|
IN PATALK_ADDR pDest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is always called at DISPATCH_LEVEL - only by PAP.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PATP_REQ pAtpReq;
|
|
ATALK_ERROR error;
|
|
BOOLEAN rc = FALSE;
|
|
|
|
ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
|
|
// Find the request.
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
atalkAtpReqReferenceByAddrTidDpc(pAtpAddr,
|
|
pDest,
|
|
Tid,
|
|
&pAtpReq,
|
|
&error);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpReq->req_Lock);
|
|
|
|
// Do not cancel a request that has just about been satisfied anyway !!!
|
|
if (pAtpReq->req_Flags & ATP_REQ_RESPONSE_COMPLETE)
|
|
{
|
|
rc = TRUE;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpReq->req_Lock);
|
|
AtalkAtpReqDereferenceDpc(pAtpReq);
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAtpCancelResp(
|
|
IN PATP_RESP pAtpResp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
NOTE: A Response can be cancelled in two states:
|
|
- *before* a response is posted
|
|
In this case no release handler is there so an extra dereference needs to be done
|
|
- *after* a response is posted
|
|
In this case a release handler is associated which will do the final dereference.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN extraDeref = FALSE, CompleteResp = FALSE;
|
|
KIRQL OldIrql;
|
|
ATALK_ERROR error;
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpCancelResp: Cancelling response for tid %x %s\n",
|
|
pAtpResp->resp_Tid,
|
|
(pAtpResp->resp_Flags & ATP_RESP_EXACTLY_ONCE) ? "XO" : "ALO"));
|
|
|
|
AtalkAtpRespReferenceByPtr(pAtpResp, &error);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
// Remove the creation reference for it.
|
|
// Only XO responses can be cancelled, if a repsonse has been posted.
|
|
ACQUIRE_SPIN_LOCK(&pAtpResp->resp_Lock, &OldIrql);
|
|
|
|
if ((pAtpResp->resp_Flags & ATP_RESP_VALID_RESP) == 0)
|
|
extraDeref = TRUE;
|
|
|
|
pAtpResp->resp_Flags |= ATP_RESP_CANCELLED;
|
|
|
|
if (pAtpResp->resp_Flags & ATP_RESP_EXACTLY_ONCE)
|
|
{
|
|
if (pAtpResp->resp_Flags & ATP_RESP_REL_TIMER)
|
|
{
|
|
ASSERT (pAtpResp->resp_Flags & ATP_RESP_VALID_RESP);
|
|
}
|
|
CompleteResp = TRUE;
|
|
}
|
|
else if ((pAtpResp->resp_Flags & ATP_RESP_VALID_RESP) == 0)
|
|
CompleteResp = TRUE;
|
|
|
|
RELEASE_SPIN_LOCK(&pAtpResp->resp_Lock, OldIrql);
|
|
|
|
if (extraDeref)
|
|
AtalkAtpRespDereference(pAtpResp);
|
|
|
|
if (CompleteResp)
|
|
{
|
|
// Try to remove the creation reference
|
|
atalkAtpRespComplete(pAtpResp, ATALK_ATP_RESP_CANCELLED);
|
|
}
|
|
|
|
// Remove the reference added at the beginning.
|
|
AtalkAtpRespDereference(pAtpResp);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
|
|
("AtalkAtpCancelResp: Failed to reference resp %lx, flags %x, tid %x\n",
|
|
pAtpResp, pAtpResp->resp_Flags, pAtpResp->resp_Tid));
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
|
|
ATALK_ERROR
|
|
AtalkAtpCancelRespByTid(
|
|
IN PATP_ADDROBJ pAtpAddr,
|
|
IN PATALK_ADDR pSrcAddr,
|
|
IN USHORT Tid
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
)
|
|
{
|
|
ATALK_ERROR error;
|
|
PATP_RESP pAtpResp;
|
|
|
|
ASSERT (VALID_ATPAO(pAtpAddr));
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
|
|
atalkAtpRespReferenceByAddrTidDpc(pAtpAddr, pSrcAddr, Tid, &pAtpResp, &error);
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
error = AtalkAtpCancelResp(pAtpResp);
|
|
AtalkAtpRespDereferenceDpc(pAtpResp);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
VOID
|
|
AtalkAtpPacketIn(
|
|
IN PPORT_DESCRIPTOR pPortDesc,
|
|
IN PDDP_ADDROBJ pDdpAddr,
|
|
IN PBYTE pPkt,
|
|
IN USHORT PktLen,
|
|
IN PATALK_ADDR pSrcAddr,
|
|
IN PATALK_ADDR pDstAddr,
|
|
IN ATALK_ERROR ErrorCode,
|
|
IN BYTE DdpType,
|
|
IN PATP_ADDROBJ pAtpAddr,
|
|
IN BOOLEAN OptimizedPath,
|
|
IN PVOID OptimizeCtx
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
NTSTATUS ntStatus;
|
|
USHORT atpDataSize;
|
|
ULONG index;
|
|
BYTE controlInfo, function, relTimer, bitmap;
|
|
USHORT seqNum, tid, startOffset;
|
|
SHORT expectedRespSize;
|
|
ULONG bytesCopied;
|
|
BOOLEAN sendSts, eomFlag, xoFlag;
|
|
BOOLEAN RetransmitResp = FALSE;
|
|
|
|
PATP_REQ pAtpReq;
|
|
ATP_REQ_HANDLER ReqHandler;
|
|
PATP_RESP pAtpResp;
|
|
|
|
BOOLEAN UnlockAddr = FALSE, DerefAddr = FALSE;
|
|
PBYTE pDgram = pPkt;
|
|
TIME TimeS, TimeE, TimeD;
|
|
|
|
TimeS = KeQueryPerformanceCounter(NULL);
|
|
|
|
ASSERT(VALID_ATPAO(pAtpAddr));
|
|
|
|
do
|
|
{
|
|
// Check for incoming errors
|
|
if ((!ATALK_SUCCESS(ErrorCode) &&
|
|
(ErrorCode != ATALK_SOCKET_CLOSED)) ||
|
|
(DdpType != DDPPROTO_ATP))
|
|
{
|
|
// Drop the packet. Invalid packet error log.
|
|
TMPLOGERR();
|
|
error = ATALK_ATP_INVALID_PKT;
|
|
break;
|
|
}
|
|
|
|
if (ErrorCode == ATALK_SOCKET_CLOSED)
|
|
{
|
|
// Our ddp address pointer is no longer valid. It will be potentially
|
|
// be freed after return from this call! Only valid request on this
|
|
// ATP request will now be AtpCloseAddress(). Also, we should never
|
|
// be called with this address object by DDP.
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
pAtpAddr->atpao_DdpAddr = NULL;
|
|
pAtpAddr->atpao_Flags &= ~ATPAO_OPEN;
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
|
|
// If we are coming in via the optimized path and socket closed
|
|
// deref the address object since it was referenced within the
|
|
// indication code.
|
|
if (OptimizedPath)
|
|
{
|
|
AtalkAtpAddrDereferenceDpc(pAtpAddr);
|
|
}
|
|
error = ErrorCode;
|
|
break;
|
|
}
|
|
|
|
// Make sure that we are not called after the ddp socket is closed.
|
|
ASSERT(pAtpAddr->atpao_Flags & ATPAO_OPEN);
|
|
|
|
if (PktLen < ATP_HEADER_SIZE)
|
|
{
|
|
error = ATALK_ATP_INVALID_PKT;
|
|
break;
|
|
}
|
|
|
|
// This must fail if OPEN is not set/initialization.
|
|
error = ATALK_NO_ERROR;
|
|
if (!OptimizedPath)
|
|
{
|
|
AtalkAtpAddrReferenceDpc(pAtpAddr, &error);
|
|
}
|
|
} while (FALSE);
|
|
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Dereference address at the end,unless we want to keep it for some reason.
|
|
DerefAddr = TRUE;
|
|
|
|
// Get the static fields from the ATP header.
|
|
controlInfo = *pDgram++;
|
|
|
|
|
|
function = (controlInfo & ATP_FUNC_MASK);
|
|
relTimer = (controlInfo & ATP_REL_TIMER_MASK);
|
|
xoFlag = ((controlInfo & ATP_XO_MASK) != 0);
|
|
eomFlag = ((controlInfo & ATP_EOM_MASK) != 0);
|
|
sendSts = ((controlInfo & ATP_STS_MASK) != 0);
|
|
|
|
// Get the bitmap/sequence number
|
|
bitmap = *pDgram++;
|
|
seqNum = (USHORT)bitmap;
|
|
|
|
// Get the transaction id
|
|
GETSHORT2SHORT(&tid, pDgram);
|
|
pDgram += sizeof(USHORT);
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpPacketIn: Packet tid %lx fu %lx ci %lx\n",
|
|
tid, function, controlInfo));
|
|
|
|
// pDgram now points to the user bytes.
|
|
do
|
|
{
|
|
// Check all the values
|
|
if (relTimer > LAST_VALID_TIMER)
|
|
{
|
|
// Use a thirty second timer value.
|
|
relTimer = THIRTY_SEC_TIMER;
|
|
}
|
|
|
|
atpDataSize = PktLen - ATP_HEADER_SIZE;
|
|
if (atpDataSize > pAtpAddr->atpao_MaxSinglePktSize)
|
|
{
|
|
error = ATALK_ATP_INVALID_PKT;
|
|
break;
|
|
}
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
UnlockAddr = TRUE;
|
|
|
|
switch (function)
|
|
{
|
|
case ATP_REQUEST:
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpPacketIn: Received REQUEST from %lx.%lx.%lx (%d.%d.%d)\n",
|
|
pSrcAddr->ata_Network, pSrcAddr->ata_Node, pSrcAddr->ata_Socket,
|
|
pSrcAddr->ata_Network, pSrcAddr->ata_Node, pSrcAddr->ata_Socket));
|
|
|
|
if (xoFlag)
|
|
{
|
|
// ExactlyOnce Transaction
|
|
// Check for a queued response. If available use it.
|
|
atalkAtpRespReferenceByAddrTidDpc(pAtpAddr,
|
|
pSrcAddr,
|
|
tid,
|
|
&pAtpResp,
|
|
&error);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
ASSERT (pAtpResp->resp_Flags & ATP_RESP_EXACTLY_ONCE);
|
|
|
|
// Found a response corresponding to this request. It
|
|
// is referenced for us. Retransmit it, if there is a
|
|
// response posted on it.
|
|
|
|
// Check to see if this response has a valid response
|
|
// posted by the atp client yet. If so reset the release timer.
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpResp->resp_Lock);
|
|
|
|
if (pAtpResp->resp_Flags & ATP_RESP_VALID_RESP)
|
|
{
|
|
if ((pAtpResp->resp_Flags & (ATP_RESP_TRANSMITTING | ATP_RESP_SENT)) == ATP_RESP_SENT)
|
|
{
|
|
|
|
RetransmitResp = TRUE;
|
|
if (pAtpResp->resp_Flags & ATP_RESP_REL_TIMER)
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpPacketIn: Retransmitting request %lx, tid %x (%x)\n",
|
|
pAtpResp, pAtpResp->resp_Tid, pAtpResp->resp_Flags));
|
|
|
|
pAtpResp->resp_RelTimeStamp = AtalkGetCurrentTick() +
|
|
pAtpResp->resp_RelTimerTicks;
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_WARN,
|
|
("AtalkAtpPacketIn: Restarted reltimer %lx\n", pAtpResp->resp_Tid));
|
|
|
|
// Set the latest bitmap for the request! We
|
|
// shouldn't touch this if no valid response is yet
|
|
// posted, so that we use the one in the first request
|
|
// packet received.
|
|
pAtpResp->resp_Bitmap = bitmap;
|
|
}
|
|
else
|
|
{
|
|
error = ATALK_ATP_RESP_CLOSING;
|
|
|
|
// Timer already fired. Drop the request.
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
|
|
("AtalkAtpPacketIn: Req recv after Reltimer fired ? Flags %lx\n",
|
|
pAtpResp->resp_Flags));
|
|
|
|
ASSERT (pAtpResp->resp_Flags & ATP_RESP_CLOSING);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = ATALK_ATP_NO_VALID_RESP;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpResp->resp_Lock);
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
UnlockAddr = FALSE;
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
ASSERT(pAtpResp->resp_Flags & ATP_RESP_VALID_RESP);
|
|
if (RetransmitResp)
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_WARN,
|
|
("AtalkAtpPacketIn: Retransmitting response %lx\n", pAtpResp));
|
|
|
|
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_AtpNumRemoteRetries,
|
|
&AtalkStatsLock.SpinLock);
|
|
atalkAtpTransmitResp(pAtpResp);
|
|
}
|
|
}
|
|
|
|
// Remove the refererence on this response structure.
|
|
AtalkAtpRespDereferenceDpc(pAtpResp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// make sure the 4 bytes (pAtpResp->resp_dwUserBytes) exist
|
|
if (PktLen < (ATP_USERBYTES_SIZE + sizeof(ULONG)))
|
|
{
|
|
error = ATALK_ATP_INVALID_PKT;
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
// Its either an ALO request or an XO request which we have not seen it before
|
|
// Decode the response bitmap. We're still holding the address spinlock
|
|
atalkAtpBitmapToBufferSize( expectedRespSize,
|
|
bitmap,
|
|
pAtpAddr->atpao_MaxSinglePktSize);
|
|
if (expectedRespSize < 0)
|
|
{
|
|
error = ATALK_ATP_INVALID_PKT;
|
|
break;
|
|
}
|
|
|
|
if (xoFlag)
|
|
{
|
|
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_AtpNumXoResponse,
|
|
&AtalkStatsLock.SpinLock);
|
|
}
|
|
else
|
|
{
|
|
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_AtpNumAloResponse,
|
|
&AtalkStatsLock.SpinLock);
|
|
}
|
|
|
|
// New request. Check for request handler set
|
|
if ((ReqHandler = pAtpAddr->atpao_ReqHandler) == NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
|
|
("AtalkAtpPacketIn: No GetRequests for request\n"));
|
|
|
|
error = ATALK_ATP_NO_GET_REQ;
|
|
break;
|
|
}
|
|
|
|
// Allocate memory for a send response structure.
|
|
if ((pAtpResp =(PATP_RESP)AtalkBPAllocBlock(BLKID_ATPRESP)) == NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
|
|
("AtalkAtpPacketIn: Could not alloc mem for resp\n"));
|
|
|
|
error = ATALK_RESR_MEM;
|
|
break;
|
|
}
|
|
|
|
#if DBG
|
|
RtlZeroMemory(pAtpResp, sizeof(ATP_RESP));
|
|
pAtpResp->resp_Signature = ATP_RESP_SIGNATURE;
|
|
#endif
|
|
// Initialize the send response structure. Note that we do
|
|
// not have a posted response yet for XO or this is an ALO
|
|
|
|
// Initialize spinlock/list
|
|
INITIALIZE_SPIN_LOCK(&pAtpResp->resp_Lock);
|
|
|
|
// Reference for Creation and indication
|
|
pAtpResp->resp_RefCount = 2;
|
|
|
|
// Remember the destination of this response.
|
|
pAtpResp->resp_Dest = *pSrcAddr;
|
|
pAtpResp->resp_Tid = tid;
|
|
pAtpResp->resp_Bitmap = bitmap;
|
|
|
|
// Backpointer to the address object
|
|
pAtpResp->resp_pAtpAddr = pAtpAddr;
|
|
|
|
// Remember a response needs to be posted by the atp client.
|
|
pAtpResp->resp_Flags = (OptimizedPath ? ATP_RESP_REMOTE : 0);
|
|
pAtpResp->resp_UserBytesOnly = (bitmap == 0) ? TRUE : FALSE;
|
|
pAtpResp->resp_Comp = NULL;
|
|
pAtpResp->resp_Ctx = NULL;
|
|
pAtpResp->resp_dwUserBytes = *(UNALIGNED ULONG *)(pDgram + ATP_USERBYTES_SIZE);
|
|
|
|
if (xoFlag)
|
|
{
|
|
// Get the index into the hash response array where this
|
|
// response would be.
|
|
index = ATP_HASH_TID_DESTADDR(tid, pSrcAddr, ATP_RESP_HASH_SIZE);
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpPacketIn: XO Req Index %lx resp for %lx-%lx.%lx.%lx %d\n",
|
|
index, tid, pSrcAddr->ata_Network, pSrcAddr->ata_Node,
|
|
pSrcAddr->ata_Socket, AtalkAtpRelTimerTicks[relTimer]));
|
|
|
|
// Put this in the XO response queue - LOCK Should be acquired!
|
|
AtalkLinkDoubleAtHead(pAtpAddr->atpao_RespHash[index],
|
|
pAtpResp,
|
|
resp_Next,
|
|
resp_Prev);
|
|
|
|
pAtpResp->resp_Flags |= ATP_RESP_EXACTLY_ONCE;
|
|
pAtpResp->resp_RelTimerTicks = (LONG)AtalkAtpRelTimerTicks[relTimer];
|
|
pAtpResp->resp_RelTimeStamp = AtalkGetCurrentTick() + pAtpResp->resp_RelTimerTicks;
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpPacketIn: ALO Req resp for %lx-%lx.%lx %d\n",
|
|
tid, pSrcAddr->ata_Network, pSrcAddr->ata_Node,
|
|
pSrcAddr->ata_Socket));
|
|
|
|
// Put this in the ALO response queue - LOCK Should be acquired!
|
|
AtalkLinkDoubleAtHead(pAtpAddr->atpao_AloRespLinkage,
|
|
pAtpResp,
|
|
resp_Next,
|
|
resp_Prev);
|
|
}
|
|
|
|
// We dont want to have the initial ref go away, as we have
|
|
// inserted a resp into the addr resp list.
|
|
DerefAddr = FALSE;
|
|
|
|
error = ATALK_NO_ERROR;
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
UnlockAddr = FALSE;
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpPacketIn: Indicating request %lx, tid %x %s\n",
|
|
pAtpResp, tid,
|
|
(pAtpResp->resp_Flags & ATP_RESP_EXACTLY_ONCE) ? "XO" : "ALO"));
|
|
|
|
#ifdef PROFILING
|
|
TimeD = KeQueryPerformanceCounter(NULL);
|
|
#endif
|
|
(*ReqHandler)(ATALK_NO_ERROR,
|
|
pAtpAddr->atpao_ReqCtx,
|
|
pAtpResp,
|
|
pSrcAddr,
|
|
atpDataSize,
|
|
pDgram + ATP_USERBYTES_SIZE,
|
|
pDgram);
|
|
|
|
#ifdef PROFILING
|
|
TimeE = KeQueryPerformanceCounter(NULL);
|
|
TimeE.QuadPart -= TimeD.QuadPart;
|
|
|
|
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_AtpNumReqHndlr,
|
|
&AtalkStatsLock.SpinLock);
|
|
INTERLOCKED_ADD_LARGE_INTGR(&AtalkStatistics.stat_AtpReqHndlrProcessTime,
|
|
TimeE,
|
|
&AtalkStatsLock.SpinLock);
|
|
#endif
|
|
break;
|
|
|
|
case ATP_RESPONSE:
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpPacketIn: Received RESPONSE from %lx.%lx.%lx, SeqNum %d tid %lx ss %lx\n",
|
|
pSrcAddr->ata_Network, pSrcAddr->ata_Node, pSrcAddr->ata_Socket,
|
|
seqNum, tid, sendSts));
|
|
|
|
if (seqNum > (ATP_MAX_RESP_PKTS-1))
|
|
{
|
|
// Drop the packet. Invalid packet error log.
|
|
TMPLOGERR();
|
|
break;
|
|
}
|
|
|
|
// See if we have a request for this tid and remote address.
|
|
if (OptimizedPath)
|
|
{
|
|
pAtpReq = (PATP_REQ)OptimizeCtx;
|
|
ASSERT (VALID_ATPRQ(pAtpReq));
|
|
ASSERT (pAtpReq->req_Bitmap == 0);
|
|
}
|
|
else
|
|
{
|
|
atalkAtpReqReferenceByAddrTidDpc(pAtpAddr,
|
|
pSrcAddr,
|
|
tid,
|
|
&pAtpReq,
|
|
&error);
|
|
}
|
|
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
// We dont have a corresponding pending request. Ignore.
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_WARN,
|
|
("AtalkAtpPacketIn: No pending request for tid %lx\n", tid));
|
|
break;
|
|
}
|
|
|
|
do
|
|
{
|
|
if (!OptimizedPath)
|
|
{
|
|
// Check the request bitmap, which could be zero if the user only
|
|
// wanted the user bytes and passed in a null response buffer.
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpReq->req_Lock);
|
|
|
|
// If we are the first packet, copy the response user bytes.
|
|
if (seqNum == 0)
|
|
{
|
|
RtlCopyMemory(pAtpReq->req_RespUserBytes,
|
|
pDgram,
|
|
ATP_USERBYTES_SIZE);
|
|
}
|
|
|
|
// Now skip over the user bytes
|
|
pDgram += ATP_USERBYTES_SIZE;
|
|
|
|
// Do we want to keep this response? Check the corresponding
|
|
// bit in our current bitmap set.
|
|
if (((pAtpReq->req_RecdBitmap & AtpBitmapForSeqNum[seqNum]) != 0) ||
|
|
((pAtpReq->req_Bitmap & AtpBitmapForSeqNum[seqNum]) == 0))
|
|
{
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpReq->req_Lock);
|
|
// We dont care about this packet. We already received it or weren't
|
|
// expecting it.
|
|
break;
|
|
}
|
|
|
|
// We want this response. Set bit in the recd bitmap. And
|
|
// Clear it in the expected packets req_Bitmap.
|
|
|
|
// !!!NOTE!!! We can release the spinlock even though the copy
|
|
// is not done. We have a ref to the req, and it wont
|
|
// get completed before that is done.
|
|
pAtpReq->req_Bitmap &= ~AtpBitmapForSeqNum[seqNum];
|
|
pAtpReq->req_RecdBitmap |= AtpBitmapForSeqNum[seqNum];
|
|
pAtpReq->req_RespRecdLen += atpDataSize;
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpPacketIn: req_Bitmap %x, req_RecdBitmap %x\n",
|
|
pAtpReq->req_Bitmap, pAtpReq->req_RecdBitmap));
|
|
|
|
|
|
// Now if eom is set, we need to reset all high order bits
|
|
// of the req_Bitmap. req_RecdBitmap should now indicate all
|
|
// the buffers we received. The two should be mutually exclusive
|
|
// at this point.
|
|
if (eomFlag)
|
|
{
|
|
pAtpReq->req_Bitmap &= AtpEomBitmapForSeqNum[seqNum];
|
|
ASSERT((pAtpReq->req_Bitmap & pAtpReq->req_RecdBitmap) == 0);
|
|
}
|
|
|
|
if (sendSts)
|
|
{
|
|
// Reset timer since we are going to retransmit the request
|
|
pAtpReq->req_RetryTimeStamp = AtalkGetCurrentTick() +
|
|
pAtpReq->req_RetryInterval;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpReq->req_Lock);
|
|
|
|
// Copy the data into the users buffer. Check if there's room.
|
|
startOffset = (USHORT)seqNum * pAtpAddr->atpao_MaxSinglePktSize;
|
|
if (pAtpReq->req_RespBufLen < (startOffset + atpDataSize))
|
|
{
|
|
// This should be a rare case; packet was in bitmap limits,
|
|
// but still wouldn't fit into user space.The other way this
|
|
// could occure is if the responder is sending less than full
|
|
// responses -- we don't "synch" up the user buffer until all
|
|
// packets have been received.
|
|
|
|
// We want to give up now, call the comp rotuine signaling
|
|
// the error -- unthread and free the request control block
|
|
// cancel the retry timer.
|
|
|
|
ASSERT(0);
|
|
error = ATALK_RESR_MEM;
|
|
atalkAtpReqComplete(pAtpReq, error);
|
|
break;
|
|
}
|
|
|
|
if ((atpDataSize > 0) && (pAtpReq->req_RespBuf != NULL))
|
|
{
|
|
// We have room to copy the data into the users buffer.
|
|
ntStatus = TdiCopyBufferToMdl(pDgram,
|
|
0,
|
|
atpDataSize,
|
|
pAtpReq->req_RespBuf,
|
|
startOffset,
|
|
&bytesCopied);
|
|
|
|
ASSERT(bytesCopied == atpDataSize);
|
|
ASSERT(NT_SUCCESS(ntStatus));
|
|
}
|
|
|
|
if (sendSts)
|
|
{
|
|
// We have reset the retry timer above
|
|
atalkAtpTransmitReq(pAtpReq);
|
|
}
|
|
|
|
// If the bitmap is non-zero, we are still awaiting more responses.
|
|
if (pAtpReq->req_Bitmap != 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT (pAtpReq->req_Bitmap == 0);
|
|
}
|
|
|
|
// Ok, we have the entire response !
|
|
// If an XO request send a release, synch up the user buffer,
|
|
// Deref the request to have it complete.
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
UnlockAddr = FALSE;
|
|
|
|
if (pAtpReq->req_Flags & ATP_REQ_EXACTLY_ONCE)
|
|
{
|
|
atalkAtpTransmitRel(pAtpReq);
|
|
}
|
|
|
|
// Do the synch up! USE RECD_BITMAP!!
|
|
|
|
// Set the response length, the user bytes in the request buffer.
|
|
|
|
// See if we can grab ownership of this request to remove
|
|
// the creation reference and complete it.
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpPacketIn: Completing req %lx tid %x\n",
|
|
pAtpReq, pAtpReq->req_Tid));
|
|
|
|
atalkAtpReqComplete(pAtpReq, error);
|
|
} while (FALSE);
|
|
|
|
// Remove reference on the request added at the beginning.
|
|
AtalkAtpReqDereferenceDpc(pAtpReq);
|
|
break;
|
|
|
|
case ATP_RELEASE:
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpPacketIn: Received release for tid %lx!\n", tid));
|
|
|
|
atalkAtpRespReferenceByAddrTidDpc(pAtpAddr,
|
|
pSrcAddr,
|
|
tid,
|
|
&pAtpResp,
|
|
&error);
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpPacketIn: Found resp for release for tid %lx!\n",
|
|
pAtpResp->resp_Tid));
|
|
|
|
// We received a release for this response. Cleanup and
|
|
// complete.
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpResp->resp_Lock);
|
|
|
|
pAtpResp->resp_Flags |= ATP_RESP_RELEASE_RECD;
|
|
if (pAtpResp->resp_Flags & ATP_RESP_REL_TIMER)
|
|
{
|
|
ASSERT (pAtpResp->resp_Flags & ATP_RESP_EXACTLY_ONCE);
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpResp->resp_Lock);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
|
|
("AtalkAtpPacketIn: resp not found - release for tid %lx!\n", tid));
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
UnlockAddr = FALSE;
|
|
|
|
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_AtpNumRecdRelease,
|
|
&AtalkStatsLock.SpinLock);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
ATALK_ERROR ErrorCode = ATALK_NO_ERROR;
|
|
|
|
// if client (mac) cancelled the request (possibly because session
|
|
// went away), make sure our completion routine gets called
|
|
if ((pAtpResp->resp_Flags & ATP_RESP_VALID_RESP) == 0)
|
|
{
|
|
ErrorCode = ATALK_ATP_RESP_CANCELLED;
|
|
pAtpResp->resp_Flags |= ATP_RESP_VALID_RESP;
|
|
}
|
|
|
|
// Try to have the creation reference removed
|
|
atalkAtpRespComplete(pAtpResp, ErrorCode);
|
|
|
|
// Remove the reference we added at the beginning.
|
|
AtalkAtpRespDereferenceDpc(pAtpResp);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
if (UnlockAddr)
|
|
{
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
}
|
|
} while (FALSE);
|
|
|
|
// Deref addr added at the beginning of this routine.
|
|
if (DerefAddr)
|
|
{
|
|
AtalkAtpAddrDereferenceDpc(pAtpAddr);
|
|
}
|
|
|
|
TimeE = KeQueryPerformanceCounter(NULL);
|
|
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
|
|
|
|
INTERLOCKED_ADD_LARGE_INTGR_DPC(&AtalkStatistics.stat_AtpPacketInProcessTime,
|
|
TimeD,
|
|
&AtalkStatsLock.SpinLock);
|
|
|
|
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_AtpNumPackets,
|
|
&AtalkStatsLock.SpinLock);
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID FASTCALL
|
|
atalkAtpTransmitReq(
|
|
IN PATP_REQ pAtpReq
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
ATP_HEADER atpHeader;
|
|
BOOLEAN remote;
|
|
BOOLEAN DerefReq = FALSE;
|
|
PBUFFER_DESC pBufDesc = NULL;
|
|
SEND_COMPL_INFO SendInfo;
|
|
|
|
// Reference the request. This goes away in request send completion.
|
|
AtalkAtpReqReferenceByPtr(pAtpReq, &error);
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("atalkAtpTransmitReq: Transmitting req %lx tid %x\n",
|
|
pAtpReq, pAtpReq->req_Tid));
|
|
|
|
// Build the atp header.
|
|
atpHeader.atph_CmdCtrl = ATP_REQUEST | (UCHAR)(ATP_REL_TIMER_MASK & pAtpReq->req_RelTimerValue);
|
|
if (pAtpReq->req_Flags & ATP_REQ_EXACTLY_ONCE)
|
|
atpHeader.atph_CmdCtrl |= ATP_XO_MASK;
|
|
|
|
// Put in the expected packets bitmap.
|
|
atpHeader.atph_Bitmap = pAtpReq->req_Bitmap;
|
|
|
|
// Put in the tid.
|
|
PUTSHORT2SHORT(&atpHeader.atph_Tid, pAtpReq->req_Tid);
|
|
|
|
// Copy the user bytes.
|
|
atpHeader.atph_dwUserBytes = pAtpReq->req_dwUserBytes;
|
|
|
|
// Build a buffer descriptor, this should hold the above mdl.
|
|
if (pAtpReq->req_BufLen > 0)
|
|
{
|
|
if ((pBufDesc = AtalkAllocBuffDesc(pAtpReq->req_Buf,
|
|
pAtpReq->req_BufLen,
|
|
0)) == NULL)
|
|
{
|
|
DerefReq = TRUE;
|
|
error = ATALK_RESR_MEM;
|
|
}
|
|
}
|
|
|
|
remote = (pAtpReq->req_Flags & ATP_REQ_REMOTE) ? TRUE : FALSE;
|
|
// Call ddp to send the packet. Dont touch request after this call,
|
|
// as the send completion could potentially lead to it being freed.
|
|
SendInfo.sc_TransmitCompletion = atalkAtpSendReqComplete;
|
|
SendInfo.sc_Ctx1 = pAtpReq;
|
|
SendInfo.sc_Ctx2 = pBufDesc;
|
|
// SendInfo.sc_Ctx3 = NULL;
|
|
if (ATALK_SUCCESS(error) &&
|
|
!ATALK_SUCCESS(error = AtalkDdpSend(pAtpReq->req_pAtpAddr->atpao_DdpAddr,
|
|
&pAtpReq->req_Dest,
|
|
(BYTE)DDPPROTO_ATP,
|
|
remote,
|
|
pBufDesc,
|
|
(PBYTE)&atpHeader,
|
|
ATP_HEADER_SIZE,
|
|
NULL,
|
|
&SendInfo)))
|
|
{
|
|
DerefReq = TRUE;
|
|
if (pBufDesc != NULL)
|
|
{
|
|
// The flags will indicate that the data buffer is not to be
|
|
// freed.
|
|
AtalkFreeBuffDesc(pBufDesc);
|
|
}
|
|
}
|
|
|
|
if (DerefReq)
|
|
{
|
|
pAtpReq->req_CompStatus = error;
|
|
AtalkAtpReqDereference(pAtpReq);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID FASTCALL
|
|
atalkAtpSendReqComplete(
|
|
IN NDIS_STATUS Status,
|
|
IN PSEND_COMPL_INFO pSendInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
if (pSendInfo->sc_Ctx2 != NULL)
|
|
{
|
|
AtalkFreeBuffDesc((PBUFFER_DESC)(pSendInfo->sc_Ctx2));
|
|
}
|
|
|
|
AtalkAtpReqDereference((PATP_REQ)(pSendInfo->sc_Ctx1));
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID FASTCALL
|
|
atalkAtpTransmitResp(
|
|
IN PATP_RESP pAtpResp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
KIRQL OldIrql;
|
|
BYTE i, bitmap, currentBit, seqNum, pktstosend;
|
|
BOOLEAN RemoteAddr;
|
|
USHORT bytesSent, bytesToSend, maxSinglePktSize;
|
|
SHORT remainingBytes;
|
|
PATP_ADDROBJ pAtpAddr;
|
|
PAMDL pAmdl[ATP_MAX_RESP_PKTS];
|
|
PBUFFER_DESC pBufDesc[ATP_MAX_RESP_PKTS];
|
|
ATP_HEADER atpHeader;
|
|
SEND_COMPL_INFO SendInfo;
|
|
|
|
// Verify we have a response posted
|
|
ASSERT(pAtpResp->resp_Flags & ATP_RESP_VALID_RESP);
|
|
|
|
pAtpAddr = pAtpResp->resp_pAtpAddr;
|
|
ASSERT(VALID_ATPAO(pAtpAddr));
|
|
|
|
RemoteAddr = ((pAtpResp->resp_Flags & ATP_RESP_REMOTE) == 0) ? FALSE : TRUE;
|
|
|
|
// send each response packet that is needed.
|
|
seqNum = 0;
|
|
pktstosend = 0;
|
|
currentBit = 1;
|
|
|
|
// Get the max packet size for this atp object
|
|
maxSinglePktSize = pAtpAddr->atpao_MaxSinglePktSize;
|
|
|
|
bitmap = pAtpResp->resp_Bitmap;
|
|
remainingBytes = pAtpResp->resp_BufLen;
|
|
bytesSent = 0;
|
|
|
|
// Indicate response type.
|
|
atpHeader.atph_CmdCtrl = ATP_RESPONSE;
|
|
|
|
// Put in the tid.
|
|
PUTSHORT2SHORT(&atpHeader.atph_Tid, pAtpResp->resp_Tid);
|
|
|
|
ASSERTMSG("atalkAtpTransmitResp: resp len is negative\n", (remainingBytes >= 0));
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
|
|
|
|
do
|
|
{
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpResp->resp_Lock);
|
|
pAtpResp->resp_Flags |= ATP_RESP_TRANSMITTING;
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpResp->resp_Lock);
|
|
|
|
do
|
|
{
|
|
pAmdl[seqNum] = NULL;
|
|
pBufDesc[seqNum] = NULL;
|
|
|
|
if (((bitmap & currentBit) != 0) ||
|
|
((seqNum == 0) && pAtpResp->resp_UserBytesOnly))
|
|
{
|
|
ASSERT(pAtpResp->resp_Flags & ATP_RESP_VALID_RESP);
|
|
|
|
bytesToSend = MIN(remainingBytes, maxSinglePktSize);
|
|
|
|
if (bytesToSend != 0)
|
|
{
|
|
ASSERT (pAtpResp->resp_Buf != NULL);
|
|
// Make an mdl for the proper subsection of the response mdl.
|
|
// Make a buffer descriptor for the mdl.
|
|
if (((pAmdl[seqNum] = AtalkSubsetAmdl(pAtpResp->resp_Buf,
|
|
bytesSent,
|
|
bytesToSend)) == NULL) ||
|
|
((pBufDesc[seqNum] = AtalkAllocBuffDesc(pAmdl[seqNum],
|
|
bytesToSend,
|
|
0)) == NULL))
|
|
{
|
|
ASSERTMSG("atalkAtpTransmitResp: Create mdl or BD failed\n", 0);
|
|
if (pAmdl[seqNum] != NULL)
|
|
{
|
|
AtalkFreeAMdl(pAmdl[seqNum]);
|
|
pAmdl[seqNum] = NULL;
|
|
}
|
|
if (seqNum > 0)
|
|
seqNum --; // Adjust this.
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
pktstosend ++;
|
|
|
|
}
|
|
else
|
|
{
|
|
// We are omitting this. Let us mark it appropriately
|
|
pBufDesc[seqNum] = (PBUFFER_DESC)-1;
|
|
}
|
|
|
|
seqNum ++;
|
|
currentBit <<= 1;
|
|
remainingBytes -= maxSinglePktSize;
|
|
bytesSent += maxSinglePktSize;
|
|
} while (remainingBytes > 0);
|
|
|
|
ASSERT (seqNum <= ATP_MAX_RESP_PKTS);
|
|
|
|
// Attempt to reference the response structure. If we fail, we abort.
|
|
// This will go away in the completion routine.
|
|
atalkAtpRespReferenceNDpc(pAtpResp, pktstosend, &error);
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
|
|
("atalkAtpTransmitResp: response %lx ref (%d) failed\n",
|
|
pAtpResp, seqNum, error));
|
|
|
|
// Need to free up the Mdls/Buffdescs
|
|
for (i = 0; i < seqNum; i++)
|
|
{
|
|
if (pAmdl[i] != NULL)
|
|
AtalkFreeAMdl(pAmdl[i]);
|
|
|
|
if ((pBufDesc[i] != NULL) && (pBufDesc[i] != (PBUFFER_DESC)-1))
|
|
AtalkFreeBuffDesc(pBufDesc[i]);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Now blast off all the packets
|
|
SendInfo.sc_TransmitCompletion = atalkAtpSendRespComplete;
|
|
SendInfo.sc_Ctx1 = pAtpResp;
|
|
// SendInfo.sc_Ctx3 = pAmdl[i];
|
|
for (i = 0; i < seqNum; i++)
|
|
{
|
|
if (pBufDesc[i] == (PBUFFER_DESC)-1)
|
|
continue;
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("atalkAtpTransmitResp: Sending seq #%d for tid %lx\n",
|
|
i, pAtpResp->resp_Tid));
|
|
|
|
// Indicate if this is the last packet of the response.
|
|
if (i == (seqNum-1))
|
|
{
|
|
atpHeader.atph_CmdCtrl |= ATP_EOM_MASK;
|
|
}
|
|
|
|
// Put in the sequence number
|
|
atpHeader.atph_SeqNum = i;
|
|
|
|
// User bytes only go in the first packet of the response
|
|
// unless otherwise indicated for this atp object.
|
|
if ((i == 0) ||
|
|
(pAtpAddr->atpao_Flags & ATPAO_SENDUSERBYTESALL))
|
|
{
|
|
atpHeader.atph_dwUserBytes = pAtpResp->resp_dwUserBytes;
|
|
}
|
|
else
|
|
{
|
|
// Zero the user bytes
|
|
atpHeader.atph_dwUserBytes = 0;
|
|
}
|
|
|
|
ASSERT(pAtpResp->resp_Flags & ATP_RESP_VALID_RESP);
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("atalkAtpTransmitResp: Sending seq #%d, BufDesc %lx, Resp %lx\n",
|
|
i, pBufDesc[i], pAtpResp));
|
|
|
|
ASSERT ((pBufDesc[i] == NULL) ||
|
|
VALID_BUFFDESC(pBufDesc[i]));
|
|
SendInfo.sc_Ctx2 = pBufDesc[i];
|
|
error = AtalkDdpSend(pAtpAddr->atpao_DdpAddr,
|
|
&pAtpResp->resp_Dest,
|
|
(BYTE)DDPPROTO_ATP,
|
|
RemoteAddr,
|
|
pBufDesc[i],
|
|
(PBYTE)&atpHeader,
|
|
ATP_HEADER_SIZE,
|
|
NULL,
|
|
&SendInfo);
|
|
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
|
|
("atalkAtpTransmitResp: AtalkDdpSend Failed %ld\n", error));
|
|
// Call completion so the buffer/mdl can get freed up,
|
|
// and the reference is removed.
|
|
atalkAtpSendRespComplete(error,
|
|
&SendInfo);
|
|
}
|
|
}
|
|
} while (FALSE);
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpResp->resp_Lock);
|
|
pAtpResp->resp_Flags |= ATP_RESP_SENT;
|
|
pAtpResp->resp_Flags &= ~ATP_RESP_TRANSMITTING;
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpResp->resp_Lock);
|
|
|
|
if (OldIrql != DISPATCH_LEVEL)
|
|
KeLowerIrql(OldIrql);
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID FASTCALL
|
|
atalkAtpSendRespComplete(
|
|
IN NDIS_STATUS Status,
|
|
IN PSEND_COMPL_INFO pSendInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
if (pSendInfo->sc_Ctx2 != NULL)
|
|
{
|
|
PAMDL pMdl;
|
|
|
|
if ((pMdl = ((PBUFFER_DESC)(pSendInfo->sc_Ctx2))->bd_OpaqueBuffer) != NULL)
|
|
AtalkFreeAMdl(pMdl);
|
|
AtalkFreeBuffDesc((PBUFFER_DESC)(pSendInfo->sc_Ctx2));
|
|
|
|
}
|
|
|
|
AtalkAtpRespDereference((PATP_RESP)(pSendInfo->sc_Ctx1));
|
|
}
|
|
|
|
|
|
|
|
// This is used to perform a retry when a release send fails in completion.
|
|
#define ATP_TID_RETRY_MASK 0xF0000000
|
|
#define ATP_TID_MASK 0xFFFF
|
|
|
|
VOID FASTCALL
|
|
atalkAtpTransmitRel(
|
|
IN PATP_REQ pAtpReq
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
ATP_HEADER atpHeader;
|
|
BOOLEAN remote;
|
|
SEND_COMPL_INFO SendInfo;
|
|
|
|
AtalkAtpAddrReferenceDpc(pAtpReq->req_pAtpAddr, &error);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("atalkAtpTransmitRel: Sending release for %lx\n", pAtpReq->req_Tid));
|
|
|
|
// Build header for this packet.
|
|
atpHeader.atph_dwUserBytes = 0;
|
|
|
|
// Indicate response type.
|
|
atpHeader.atph_CmdCtrl = ATP_RELEASE;
|
|
|
|
// Put in the bitmap
|
|
atpHeader.atph_Bitmap = pAtpReq->req_RecdBitmap;
|
|
|
|
// Put in the tid.
|
|
PUTSHORT2SHORT(&atpHeader.atph_Tid, pAtpReq->req_Tid);
|
|
|
|
remote = (pAtpReq->req_Flags & ATP_REQ_REMOTE) ? TRUE : FALSE;
|
|
SendInfo.sc_TransmitCompletion = atalkAtpSendRelComplete;
|
|
SendInfo.sc_Ctx1 = pAtpReq->req_pAtpAddr;
|
|
SendInfo.sc_Ctx2 = (PVOID)((ULONG_PTR)(ATP_TID_RETRY_MASK | pAtpReq->req_Tid));
|
|
SendInfo.sc_Ctx3 = (PVOID)((ULONG_PTR)(pAtpReq->req_Dest.ata_Address));
|
|
error = AtalkDdpSend(pAtpReq->req_pAtpAddr->atpao_DdpAddr,
|
|
&pAtpReq->req_Dest,
|
|
(BYTE)DDPPROTO_ATP,
|
|
remote,
|
|
NULL,
|
|
(PBYTE)&atpHeader,
|
|
ATP_HEADER_SIZE,
|
|
NULL,
|
|
&SendInfo);
|
|
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("atalkAtpTransmitRel: Send release failed %lx\n", error));
|
|
|
|
AtalkAtpAddrDereferenceDpc(pAtpReq->req_pAtpAddr);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID FASTCALL
|
|
atalkAtpSendRelComplete(
|
|
IN NDIS_STATUS Status,
|
|
IN PSEND_COMPL_INFO pSendInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
ATALK_ERROR error;
|
|
ATP_HEADER atpHeader;
|
|
#define pAtpAddr ((PATP_ADDROBJ)(pSendInfo->sc_Ctx1))
|
|
#define TidAndRetry (ULONG_PTR)(pSendInfo->sc_Ctx2)
|
|
#define DestAddr (ULONG_PTR)(pSendInfo->sc_Ctx3)
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("atalkAtpSendRelComplete: Send status %lx\n", Status));
|
|
|
|
if ((Status == NDIS_STATUS_SUCCESS) ||
|
|
((TidAndRetry & ATP_TID_RETRY_MASK) == 0))
|
|
{
|
|
// Either successful, or we have already retried.
|
|
AtalkAtpAddrDereference(pAtpAddr);
|
|
return;
|
|
}
|
|
|
|
// Go ahead and retry!
|
|
// Build header for this packet.
|
|
atpHeader.atph_dwUserBytes = 0;
|
|
|
|
// Indicate response type.
|
|
atpHeader.atph_CmdCtrl = ATP_RELEASE;
|
|
|
|
// Put in the tid.
|
|
PUTSHORT2SHORT(&atpHeader.atph_Tid, (TidAndRetry & ATP_TID_MASK));
|
|
|
|
pSendInfo->sc_Ctx2 = NULL;
|
|
pSendInfo->sc_Ctx3 = NULL;
|
|
error = AtalkDdpSend(pAtpAddr->atpao_DdpAddr,
|
|
(PATALK_ADDR)&DestAddr,
|
|
(BYTE)DDPPROTO_ATP,
|
|
FALSE,
|
|
NULL,
|
|
(PBYTE)&atpHeader,
|
|
ATP_HEADER_SIZE,
|
|
NULL,
|
|
pSendInfo);
|
|
|
|
if (!ATALK_SUCCESS(error))
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("atalkAtpSendRelComplete: Send release failed %lx\n", error));
|
|
|
|
AtalkAtpAddrDereference(pAtpAddr);
|
|
}
|
|
#undef pAtpAddr
|
|
#undef TidAndRetry
|
|
#undef DestAddr
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID FASTCALL
|
|
atalkAtpRespComplete(
|
|
IN OUT PATP_RESP pAtpResp,
|
|
IN ATALK_ERROR CompletionStatus
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
BOOLEAN ownResp = TRUE;
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("atalkAtpRespComplete: Completing %lx.%lx\n",
|
|
pAtpResp->resp_Tid, CompletionStatus));
|
|
|
|
// See if we can grab ownership of this response to remove
|
|
// the creation reference and complete it.
|
|
ACQUIRE_SPIN_LOCK(&pAtpResp->resp_Lock, &OldIrql);
|
|
if (pAtpResp->resp_Flags & ATP_RESP_CLOSING)
|
|
{
|
|
ownResp = FALSE;
|
|
}
|
|
pAtpResp->resp_Flags |= ATP_RESP_CLOSING;
|
|
pAtpResp->resp_CompStatus = CompletionStatus;
|
|
RELEASE_SPIN_LOCK(&pAtpResp->resp_Lock, OldIrql);
|
|
|
|
// If we managed to get ownership of the request, call the
|
|
// Deref for creation.
|
|
if (ownResp)
|
|
{
|
|
AtalkAtpRespDereference(pAtpResp);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID FASTCALL
|
|
atalkAtpReqComplete(
|
|
IN OUT PATP_REQ pAtpReq,
|
|
IN ATALK_ERROR CompletionStatus
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
BOOLEAN ownReq = TRUE;
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("atalkAtpReqComplete: Completing %lx\n", pAtpReq->req_Tid));
|
|
|
|
// See if we can grab ownership of this resquest to remove
|
|
// the creation reference and complete it.
|
|
ACQUIRE_SPIN_LOCK(&pAtpReq->req_Lock, &OldIrql);
|
|
if (pAtpReq->req_Flags & ATP_REQ_CLOSING)
|
|
{
|
|
ownReq = FALSE;
|
|
}
|
|
pAtpReq->req_CompStatus = CompletionStatus;
|
|
pAtpReq->req_Flags |= ATP_REQ_CLOSING;
|
|
RELEASE_SPIN_LOCK(&pAtpReq->req_Lock, OldIrql);
|
|
|
|
// If we managed to get ownership of the request, call the deref for creation.
|
|
if (ownReq)
|
|
{
|
|
AtalkAtpReqDereference(pAtpReq);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
atalkAtpGetNextTidForAddr(
|
|
IN PATP_ADDROBJ pAtpAddr,
|
|
IN PATALK_ADDR pRemoteAddr,
|
|
OUT PUSHORT pTid,
|
|
OUT PULONG pIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
USHORT TentativeTid;
|
|
ULONG index;
|
|
PATP_REQ pAtpReq;
|
|
|
|
do
|
|
{
|
|
TentativeTid = pAtpAddr->atpao_NextTid++;
|
|
if (pAtpAddr->atpao_NextTid == 0)
|
|
pAtpAddr->atpao_NextTid = 1;
|
|
|
|
// Check to see if this tid is in use for this address.
|
|
|
|
// !!!NOTE!!!
|
|
// This will be true even if the tid is in use for a closing
|
|
// request or a response.
|
|
|
|
// Calculate the hash value of the destination address of this request
|
|
// and the tid.
|
|
index = ATP_HASH_TID_DESTADDR(TentativeTid, pRemoteAddr, ATP_REQ_HASH_SIZE);
|
|
|
|
for (pAtpReq = pAtpAddr->atpao_ReqHash[index];
|
|
pAtpReq != NULL;
|
|
pAtpReq = pAtpReq->req_Next)
|
|
{
|
|
if ((ATALK_ADDRS_EQUAL(&pAtpReq->req_Dest, pRemoteAddr)) &&
|
|
(pAtpReq->req_Tid == TentativeTid))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
} while (pAtpReq != NULL);
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("atalkAtpGetNextTidForAddr: Tid %lx for %lx.%lx.%lx\n",
|
|
TentativeTid, pRemoteAddr->ata_Network, pRemoteAddr->ata_Node,
|
|
pRemoteAddr->ata_Socket));
|
|
|
|
*pTid = TentativeTid;
|
|
*pIndex = index;
|
|
}
|
|
|
|
|
|
VOID
|
|
atalkAtpReqRefNextNc(
|
|
IN PATP_REQ pAtpReq,
|
|
OUT PATP_REQ * ppNextNcReq,
|
|
OUT PATALK_ERROR pError
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MUST BE CALLED WITH THE ADDRESS LOCK HELD!
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
for (NOTHING; pAtpReq != NULL; pAtpReq = pAtpReq->req_Next)
|
|
{
|
|
AtalkAtpReqReferenceByPtrDpc(pAtpReq, pError);
|
|
if (ATALK_SUCCESS(*pError))
|
|
{
|
|
// Ok, this request is referenced!
|
|
*ppNextNcReq = pAtpReq;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID FASTCALL
|
|
atalkAtpReqDeref(
|
|
IN PATP_REQ pAtpReq,
|
|
IN BOOLEAN AtDpc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
BOOLEAN done = FALSE;
|
|
|
|
// This will call the completion routine and remove it from the
|
|
// list when ref count goes to 0.
|
|
ASSERT(VALID_ATPRQ(pAtpReq));
|
|
|
|
if (AtDpc)
|
|
{
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpReq->req_Lock);
|
|
}
|
|
else
|
|
{
|
|
ACQUIRE_SPIN_LOCK(&pAtpReq->req_Lock, &OldIrql);
|
|
}
|
|
|
|
if ((--pAtpReq->req_RefCount) == 0)
|
|
{
|
|
ASSERT(pAtpReq->req_Flags & ATP_REQ_CLOSING);
|
|
done = TRUE;
|
|
}
|
|
|
|
if (AtDpc)
|
|
{
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpReq->req_Lock);
|
|
}
|
|
else
|
|
{
|
|
RELEASE_SPIN_LOCK(&pAtpReq->req_Lock, OldIrql);
|
|
}
|
|
|
|
if (done)
|
|
{
|
|
if (AtDpc)
|
|
{
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpReq->req_pAtpAddr->atpao_Lock);
|
|
}
|
|
else
|
|
{
|
|
ACQUIRE_SPIN_LOCK(&pAtpReq->req_pAtpAddr->atpao_Lock, &OldIrql);
|
|
}
|
|
|
|
// Remove it from the list.
|
|
AtalkUnlinkDouble(pAtpReq, req_Next, req_Prev);
|
|
|
|
if (pAtpReq->req_Flags & ATP_REQ_RETRY_TIMER)
|
|
{
|
|
pAtpReq->req_Flags &= ~ATP_REQ_RETRY_TIMER;
|
|
RemoveEntryList(&pAtpReq->req_List);
|
|
}
|
|
|
|
if (AtDpc)
|
|
{
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpReq->req_pAtpAddr->atpao_Lock);
|
|
}
|
|
else
|
|
{
|
|
RELEASE_SPIN_LOCK(&pAtpReq->req_pAtpAddr->atpao_Lock, OldIrql);
|
|
}
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("atalkAtpReqDeref: Completing req for tid %lx.%d\n",
|
|
pAtpReq->req_Tid, pAtpReq->req_Tid));
|
|
|
|
// Call the completion routine for the request.
|
|
if (pAtpReq->req_Comp != NULL)
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
// Resp handlers expect to be called at DISPATCH. If the
|
|
// request was cancelled, make it so.
|
|
if (pAtpReq->req_CompStatus == ATALK_ATP_REQ_CANCELLED)
|
|
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
|
|
|
|
(*pAtpReq->req_Comp)(pAtpReq->req_CompStatus,
|
|
pAtpReq->req_Ctx,
|
|
pAtpReq->req_Buf,
|
|
pAtpReq->req_RespBuf,
|
|
pAtpReq->req_RespRecdLen,
|
|
pAtpReq->req_RespUserBytes);
|
|
|
|
if (pAtpReq->req_CompStatus == ATALK_ATP_REQ_CANCELLED)
|
|
KeLowerIrql(OldIrql);
|
|
}
|
|
|
|
// Deref the address object
|
|
if (AtDpc)
|
|
{
|
|
AtalkAtpAddrDereferenceDpc(pAtpReq->req_pAtpAddr);
|
|
}
|
|
else
|
|
{
|
|
AtalkAtpAddrDereference(pAtpReq->req_pAtpAddr);
|
|
}
|
|
|
|
// Release the ndis buffer descriptors, if any
|
|
AtalkIndAtpReleaseNdisBuffer(pAtpReq);
|
|
|
|
AtalkBPFreeBlock(pAtpReq);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
atalkAtpRespRefNextNc(
|
|
IN PATP_RESP pAtpResp,
|
|
OUT PATP_RESP * ppNextNcResp,
|
|
OUT PATALK_ERROR pError
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MUST BE CALLED WITH THE ADDRESS LOCK HELD!
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PATP_RESP pNextResp = NULL;
|
|
ATALK_ERROR error = ATALK_FAILURE;
|
|
|
|
for (; pAtpResp != NULL; pAtpResp = pAtpResp->resp_Next)
|
|
{
|
|
AtalkAtpRespReferenceByPtrDpc(pAtpResp, pError);
|
|
if (ATALK_SUCCESS(*pError))
|
|
{
|
|
// Ok, this request is referenced!
|
|
*ppNextNcResp = pAtpResp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID FASTCALL
|
|
AtalkAtpRespDeref(
|
|
IN PATP_RESP pAtpResp,
|
|
IN BOOLEAN AtDpc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PATP_ADDROBJ pAtpAddr;
|
|
KIRQL OldIrql;
|
|
BOOLEAN done = FALSE;
|
|
BOOLEAN NotifyRelHandler = FALSE;
|
|
|
|
// This will call the completion routine when the ref count goes to 1
|
|
// and remove it from the list when ref count goes to 0. The assumption
|
|
// here is that the release handler will be the last to Dereference.
|
|
|
|
if (AtDpc)
|
|
{
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpResp->resp_Lock);
|
|
}
|
|
else
|
|
{
|
|
ACQUIRE_SPIN_LOCK(&pAtpResp->resp_Lock, &OldIrql);
|
|
}
|
|
|
|
pAtpResp->resp_RefCount--;
|
|
if (pAtpResp->resp_RefCount == 0)
|
|
{
|
|
ASSERT(pAtpResp->resp_Flags & (ATP_RESP_HANDLER_NOTIFIED | ATP_RESP_CANCELLED));
|
|
|
|
done = TRUE;
|
|
}
|
|
else if ((pAtpResp->resp_RefCount == 1) &&
|
|
(pAtpResp->resp_Flags & ATP_RESP_VALID_RESP) &&
|
|
((pAtpResp->resp_Flags & ATP_RESP_HANDLER_NOTIFIED) == 0))
|
|
{
|
|
NotifyRelHandler = TRUE;
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpRespDereference: Notifying release handler for Resp %lx, tid %x %s\n",
|
|
pAtpResp, pAtpResp->resp_Tid,
|
|
(pAtpResp->resp_Flags & ATP_RESP_EXACTLY_ONCE) ? "XO" : "ALO"));
|
|
pAtpResp->resp_Flags |= ATP_RESP_HANDLER_NOTIFIED;
|
|
}
|
|
|
|
if (AtDpc)
|
|
{
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpResp->resp_Lock);
|
|
}
|
|
else
|
|
{
|
|
RELEASE_SPIN_LOCK(&pAtpResp->resp_Lock, OldIrql);
|
|
}
|
|
|
|
if (NotifyRelHandler)
|
|
{
|
|
ASSERT (!done);
|
|
|
|
// Call the completion routine.
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpRespDereference: Calling resp handler for tid %lx %s\n",
|
|
pAtpResp->resp_Tid,
|
|
(pAtpResp->resp_Flags & ATP_RESP_EXACTLY_ONCE) ? "XO" : "ALO"));
|
|
|
|
//
|
|
// if Mac cancels its request before a response is posted by the client,
|
|
// the compl. routine won't be set yet.
|
|
//
|
|
if (pAtpResp->resp_Comp != NULL)
|
|
{
|
|
(*pAtpResp->resp_Comp)(pAtpResp->resp_CompStatus, pAtpResp->resp_Ctx);
|
|
}
|
|
}
|
|
|
|
else if (done)
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("AtalkAtpRespDereference: Freeing resp for tid %lx - %lx %s\n",
|
|
pAtpResp->resp_Tid, pAtpResp->resp_CompStatus,
|
|
(pAtpResp->resp_Flags & ATP_RESP_EXACTLY_ONCE) ? "XO" : "ALO"));
|
|
|
|
pAtpAddr = pAtpResp->resp_pAtpAddr;
|
|
if (AtDpc)
|
|
{
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
}
|
|
else
|
|
{
|
|
ACQUIRE_SPIN_LOCK(&pAtpAddr->atpao_Lock, &OldIrql);
|
|
}
|
|
|
|
|
|
// Remove it from the list.
|
|
AtalkUnlinkDouble(pAtpResp, resp_Next, resp_Prev);
|
|
|
|
if (pAtpResp->resp_Flags & ATP_RESP_REL_TIMER)
|
|
{
|
|
ASSERT (pAtpResp->resp_Flags & ATP_RESP_EXACTLY_ONCE);
|
|
pAtpResp->resp_Flags &= ~ATP_RESP_REL_TIMER;
|
|
RemoveEntryList(&pAtpResp->resp_List);
|
|
}
|
|
|
|
if (AtDpc)
|
|
{
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
}
|
|
else
|
|
{
|
|
RELEASE_SPIN_LOCK(&pAtpAddr->atpao_Lock, OldIrql);
|
|
}
|
|
|
|
// Deref the address object
|
|
if (AtDpc)
|
|
{
|
|
AtalkAtpAddrDereferenceDpc(pAtpResp->resp_pAtpAddr);
|
|
}
|
|
else
|
|
{
|
|
AtalkAtpAddrDereference(pAtpResp->resp_pAtpAddr);
|
|
}
|
|
AtalkBPFreeBlock(pAtpResp);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID FASTCALL
|
|
AtalkAtpAddrDeref(
|
|
IN OUT PATP_ADDROBJ pAtpAddr,
|
|
IN BOOLEAN AtDpc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrql;
|
|
BOOLEAN done = FALSE;
|
|
|
|
if (AtDpc)
|
|
{
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
}
|
|
else
|
|
{
|
|
ACQUIRE_SPIN_LOCK(&pAtpAddr->atpao_Lock, &OldIrql);
|
|
}
|
|
|
|
ASSERT(pAtpAddr->atpao_RefCount > 0);
|
|
if (--(pAtpAddr->atpao_RefCount) == 0)
|
|
{
|
|
done = TRUE;
|
|
ASSERT(pAtpAddr->atpao_Flags & ATPAO_CLOSING);
|
|
}
|
|
|
|
if (AtDpc)
|
|
{
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
}
|
|
else
|
|
{
|
|
RELEASE_SPIN_LOCK(&pAtpAddr->atpao_Lock, OldIrql);
|
|
}
|
|
|
|
if (done)
|
|
{
|
|
// Call the close completion routine.
|
|
if (pAtpAddr->atpao_CloseComp != NULL)
|
|
{
|
|
(*pAtpAddr->atpao_CloseComp)(ATALK_NO_ERROR, pAtpAddr->atpao_CloseCtx);
|
|
}
|
|
|
|
// This address is done for. Close the ddp socket.
|
|
AtalkDdpCloseAddress(pAtpAddr->atpao_DdpAddr, NULL, NULL);
|
|
|
|
// Free up the memory
|
|
AtalkFreeMemory(pAtpAddr);
|
|
|
|
AtalkUnlockAtpIfNecessary();
|
|
}
|
|
}
|
|
|
|
|
|
VOID FASTCALL
|
|
AtalkIndAtpSetupNdisBuffer(
|
|
IN OUT PATP_REQ pAtpReq,
|
|
IN ULONG MaxSinglePktSize
|
|
)
|
|
{
|
|
NDIS_STATUS ndisStatus;
|
|
PNDIS_BUFFER ndisBuffer;
|
|
PNDIS_BUFFER ndisFirstBuffer;
|
|
PNDIS_BUFFER ndisPrevBuffer;
|
|
UINT ndisBufLen;
|
|
USHORT seqNum = 0;
|
|
USHORT startOffset = 0;
|
|
USHORT Offset;
|
|
USHORT BytesRemaining;
|
|
USHORT PartialBytesNeeded=0;
|
|
USHORT PacketRoom;
|
|
PMDL pCurrentMdl;
|
|
BOOLEAN fPartialMdl;
|
|
SHORT BufLen = (SHORT)pAtpReq->req_RespBufLen;
|
|
|
|
|
|
RtlZeroMemory(pAtpReq->req_NdisBuf,
|
|
sizeof(PVOID) * ATP_MAX_RESP_PKTS);
|
|
|
|
if (BufLen == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// BytesRemaining: bytes remaining in the current Mdl
|
|
// PacketRoom: bytes required to complete setting up the
|
|
// Atp request corresponding to seqNum
|
|
// ndisBufLen: bytes that will describe the (partial) mdl,
|
|
// obtained via NdisCopyBuffer
|
|
//
|
|
|
|
pCurrentMdl = pAtpReq->req_RespBuf;
|
|
|
|
ASSERT(pCurrentMdl != NULL);
|
|
|
|
BytesRemaining = (USHORT)MmGetMdlByteCount(pCurrentMdl);
|
|
Offset = 0;
|
|
|
|
ndisFirstBuffer = NULL;
|
|
|
|
while (BufLen > 0 && seqNum < ATP_MAX_RESP_PKTS)
|
|
{
|
|
PacketRoom = MIN(BufLen, (USHORT)MaxSinglePktSize);
|
|
|
|
while (PacketRoom > 0)
|
|
{
|
|
// are all the bytes there or are we at an Mdl boundary?
|
|
if (BytesRemaining >= PacketRoom)
|
|
{
|
|
ndisBufLen = (UINT)PacketRoom;
|
|
fPartialMdl = FALSE;
|
|
}
|
|
|
|
// looks like we are at boundary: need to get a partial mdl
|
|
else
|
|
{
|
|
ndisBufLen = (UINT)BytesRemaining;
|
|
fPartialMdl = TRUE;
|
|
}
|
|
|
|
ASSERT(ndisBufLen > 0);
|
|
|
|
NdisCopyBuffer(&ndisStatus,
|
|
&ndisBuffer,
|
|
AtalkNdisBufferPoolHandle,
|
|
(PVOID)pCurrentMdl,
|
|
Offset,
|
|
ndisBufLen);
|
|
|
|
if (ndisStatus != NDIS_STATUS_SUCCESS)
|
|
{
|
|
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
|
|
("AtalkIndAtpSetupNdisBuffer: NdisCopyBuffer failed!\n"));
|
|
break;
|
|
}
|
|
|
|
ASSERT(ndisBufLen == MmGetMdlByteCount(ndisBuffer));
|
|
|
|
ATALK_DBG_INC_COUNT(AtalkDbgMdlsAlloced);
|
|
|
|
// first buffer for this packet?
|
|
if (!ndisFirstBuffer)
|
|
{
|
|
ndisFirstBuffer = ndisBuffer;
|
|
ndisPrevBuffer = ndisBuffer;
|
|
}
|
|
|
|
// no, it's not the first. Chain it in!
|
|
else
|
|
{
|
|
ndisPrevBuffer->Next = ndisBuffer;
|
|
ndisPrevBuffer = ndisBuffer;
|
|
}
|
|
|
|
BufLen -= (SHORT)ndisBufLen;
|
|
Offset += (USHORT)ndisBufLen;
|
|
BytesRemaining -= (USHORT)ndisBufLen;
|
|
PacketRoom -= (USHORT)ndisBufLen;
|
|
|
|
// did we exhaust the current Mdl? move to the next mdl then!
|
|
if (fPartialMdl)
|
|
{
|
|
ASSERT(PacketRoom > 0);
|
|
|
|
pCurrentMdl = pCurrentMdl->Next;
|
|
ASSERT(pCurrentMdl != NULL);
|
|
|
|
BytesRemaining = (USHORT)MmGetMdlByteCount(pCurrentMdl);
|
|
Offset = 0;
|
|
}
|
|
}
|
|
|
|
if (PacketRoom > 0)
|
|
{
|
|
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
|
|
("AtalkIndAtpSetupNdisBuffer: couldn't get Mdl!\n"));
|
|
|
|
// if an mdl was allocated (describing part of buffer), free it
|
|
if (ndisFirstBuffer)
|
|
{
|
|
AtalkNdisFreeBuffer(ndisFirstBuffer);
|
|
}
|
|
break;
|
|
}
|
|
|
|
ASSERT(ndisFirstBuffer != NULL);
|
|
|
|
pAtpReq->req_NdisBuf[seqNum++] = ndisFirstBuffer;
|
|
ndisFirstBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
VOID FASTCALL
|
|
AtalkIndAtpReleaseNdisBuffer(
|
|
IN OUT PATP_REQ pAtpReq
|
|
)
|
|
{
|
|
LONG i;
|
|
PNDIS_BUFFER ndisBuffer;
|
|
PNDIS_BUFFER ndisNextBuffer;
|
|
|
|
for (i = 0; i < ATP_MAX_RESP_PKTS; i++)
|
|
{
|
|
if ((ndisBuffer = pAtpReq->req_NdisBuf[i]) != NULL)
|
|
{
|
|
AtalkNdisFreeBuffer(ndisBuffer);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL LONG FASTCALL
|
|
atalkAtpReqTimer(
|
|
IN PTIMERLIST pTimer,
|
|
IN BOOLEAN TimerShuttingDown
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PATP_REQ pAtpReq;
|
|
PATP_ADDROBJ pAtpAddr;
|
|
PLIST_ENTRY pList, pListNext;
|
|
ATALK_ERROR error;
|
|
LONG now;
|
|
BOOLEAN retry;
|
|
#ifdef PROFILING
|
|
LARGE_INTEGER TimeS, TimeE, TimeD;
|
|
|
|
TimeS = KeQueryPerformanceCounter(NULL);
|
|
#endif
|
|
|
|
pAtpAddr = CONTAINING_RECORD(pTimer, ATP_ADDROBJ, atpao_RetryTimer);
|
|
ASSERT(VALID_ATPAO(pAtpAddr));
|
|
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("atalkAtpReqTimer: Entered for address %lx\n", pAtpAddr));
|
|
|
|
if (TimerShuttingDown ||
|
|
(pAtpAddr->atpao_Flags & (ATPAO_CLOSING|ATPAO_CLEANUP)))
|
|
{
|
|
AtalkAtpAddrDereferenceDpc(pAtpAddr);
|
|
return ATALK_TIMER_NO_REQUEUE;
|
|
}
|
|
|
|
now = AtalkGetCurrentTick();
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
|
|
for (pList = pAtpAddr->atpao_ReqList.Flink;
|
|
pList != &pAtpAddr->atpao_ReqList;
|
|
pList = pListNext)
|
|
{
|
|
pAtpReq = CONTAINING_RECORD(pList, ATP_REQ, req_List);
|
|
ASSERT (VALID_ATPRQ(pAtpReq));
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpReq->req_Lock);
|
|
|
|
pListNext = pAtpReq->req_List.Flink;
|
|
|
|
// If either we are closing this request or have not timed out yet, skip.
|
|
if (((pAtpReq->req_Flags & (ATP_REQ_CLOSING |
|
|
ATP_REQ_RETRY_TIMER |
|
|
ATP_REQ_RESPONSE_COMPLETE)) != ATP_REQ_RETRY_TIMER) ||
|
|
(now < pAtpReq->req_RetryTimeStamp))
|
|
|
|
{
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpReq->req_Lock);
|
|
continue;
|
|
}
|
|
|
|
// If retry count == 0, we have reached the end of the road.
|
|
if ((pAtpReq->req_RetryCnt == ATP_INFINITE_RETRIES) ||
|
|
(--(pAtpReq->req_RetryCnt) > 0))
|
|
{
|
|
// Transmit the request again!
|
|
retry = TRUE;
|
|
pAtpReq->req_RetryTimeStamp = (now + pAtpReq->req_RetryInterval);
|
|
}
|
|
else
|
|
{
|
|
// We should now be Dereferenced for creation.
|
|
retry = FALSE;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpReq->req_Lock);
|
|
|
|
if (retry)
|
|
{
|
|
// We do not want to update statistics for requests are that are never going to
|
|
// be responded to (like tickle packets). Detect these and skip updating the
|
|
// stats for these
|
|
if (pAtpReq->req_RespBufLen > 0) // i.e. response expected
|
|
{
|
|
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_AtpNumLocalRetries,
|
|
&AtalkStatsLock.SpinLock);
|
|
}
|
|
|
|
AtalkAtpReqReferenceByPtrDpc(pAtpReq, &error);
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
atalkAtpTransmitReq(pAtpReq);
|
|
AtalkAtpReqDereferenceDpc(pAtpReq);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We have run out of retries - complete with an error
|
|
ASSERT (pAtpReq->req_RetryCnt == 0);
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_ERR,
|
|
("atalkAtpReqTimer: Request %lx, tid %x timed out !!!\n",
|
|
pAtpReq, pAtpReq->req_Tid));
|
|
|
|
AtalkAtpReqReferenceByPtrDpc(pAtpReq, &error);
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
|
|
if (ATALK_SUCCESS(error))
|
|
{
|
|
atalkAtpReqComplete(pAtpReq, ATALK_ATP_REQ_TIMEOUT);
|
|
AtalkAtpReqDereferenceDpc(pAtpReq);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(DBG_COMP_ASP, DBG_LEVEL_ERR,
|
|
("atalkAtpReqTimer: couldn't reference pAtpReq %lx :nothing done!\n",pAtpReq));
|
|
}
|
|
}
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
|
|
// Start over
|
|
pListNext = pAtpAddr->atpao_ReqList.Flink;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
|
|
#ifdef PROFILING
|
|
TimeE = KeQueryPerformanceCounter(NULL);
|
|
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
|
|
|
|
INTERLOCKED_ADD_LARGE_INTGR_DPC(&AtalkStatistics.stat_AtpReqTimerProcessTime,
|
|
TimeD,
|
|
&AtalkStatsLock.SpinLock);
|
|
|
|
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_AtpNumReqTimer,
|
|
&AtalkStatsLock.SpinLock);
|
|
#endif
|
|
|
|
return ATALK_TIMER_REQUEUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
LOCAL LONG FASTCALL
|
|
atalkAtpRelTimer(
|
|
IN PTIMERLIST pTimer,
|
|
IN BOOLEAN TimerShuttingDown
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PATP_ADDROBJ pAtpAddr;
|
|
PATP_RESP pAtpResp;
|
|
PLIST_ENTRY pList, pListNext;
|
|
LONG now;
|
|
#ifdef PROFILING
|
|
LARGE_INTEGER TimeS, TimeE, TimeD;
|
|
|
|
TimeS = KeQueryPerformanceCounter(NULL);
|
|
#endif
|
|
|
|
pAtpAddr = CONTAINING_RECORD(pTimer, ATP_ADDROBJ, atpao_RelTimer);
|
|
ASSERT(VALID_ATPAO(pAtpAddr));
|
|
|
|
if (TimerShuttingDown ||
|
|
(pAtpAddr->atpao_Flags & (ATPAO_CLOSING|ATPAO_CLEANUP)))
|
|
{
|
|
AtalkAtpAddrDereferenceDpc(pAtpAddr);
|
|
return ATALK_TIMER_NO_REQUEUE;
|
|
}
|
|
|
|
now = AtalkGetCurrentTick();
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
|
|
for (pList = pAtpAddr->atpao_RespList.Flink;
|
|
pList != &pAtpAddr->atpao_RespList;
|
|
pList = pListNext)
|
|
{
|
|
BOOLEAN derefResp;
|
|
|
|
pAtpResp = CONTAINING_RECORD(pList, ATP_RESP, resp_List);
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpResp->resp_Lock);
|
|
derefResp = TRUE;
|
|
|
|
ASSERT (VALID_ATPRS(pAtpResp));
|
|
ASSERT (pAtpResp->resp_Flags & (ATP_RESP_EXACTLY_ONCE|ATP_RESP_VALID_RESP|ATP_RESP_REL_TIMER));
|
|
|
|
pListNext = pAtpResp->resp_List.Flink;
|
|
|
|
if ((pAtpResp->resp_Flags &
|
|
(ATP_RESP_CLOSING |
|
|
ATP_RESP_REL_TIMER |
|
|
ATP_RESP_TRANSMITTING |
|
|
ATP_RESP_SENT |
|
|
ATP_RESP_HANDLER_NOTIFIED |
|
|
ATP_RESP_RELEASE_RECD)) == (ATP_RESP_REL_TIMER | ATP_RESP_SENT))
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_INFO,
|
|
("atalkAtpRelTimer: Checking req tid %lx (%x)\n",
|
|
pAtpResp->resp_Tid, pAtpResp->resp_Flags));
|
|
|
|
if (now >= pAtpResp->resp_RelTimeStamp)
|
|
{
|
|
DBGPRINT(DBG_COMP_ATP, DBG_LEVEL_WARN,
|
|
("atalkAtpRelTimer: Releasing req %lx tid %lx (%x)\n",
|
|
pAtpResp, pAtpResp->resp_Tid, pAtpResp->resp_Flags));
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpResp->resp_Lock);
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
derefResp = FALSE;
|
|
|
|
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_AtpNumRespTimeout,
|
|
&AtalkStatsLock.SpinLock);
|
|
|
|
// Try to have the creation reference removed
|
|
atalkAtpRespComplete(pAtpResp, ATALK_ATP_RESP_TIMEOUT);
|
|
|
|
ACQUIRE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
|
|
// Start over
|
|
pListNext = pAtpAddr->atpao_RespList.Flink;
|
|
}
|
|
}
|
|
|
|
if (derefResp)
|
|
{
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpResp->resp_Lock);
|
|
}
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&pAtpAddr->atpao_Lock);
|
|
|
|
#ifdef PROFILING
|
|
TimeE = KeQueryPerformanceCounter(NULL);
|
|
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
|
|
|
|
INTERLOCKED_ADD_LARGE_INTGR_DPC(&AtalkStatistics.stat_AtpRelTimerProcessTime,
|
|
TimeD,
|
|
&AtalkStatsLock.SpinLock);
|
|
|
|
INTERLOCKED_INCREMENT_LONG_DPC(&AtalkStatistics.stat_AtpNumRelTimer,
|
|
&AtalkStatsLock.SpinLock);
|
|
#endif
|
|
|
|
return ATALK_TIMER_REQUEUE;
|
|
}
|
|
|
|
|
|
VOID FASTCALL
|
|
AtalkAtpGenericRespComplete(
|
|
IN ATALK_ERROR ErrorCode,
|
|
IN PATP_RESP pAtpResp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
AtalkAtpRespDereference(pAtpResp);
|
|
}
|
|
|