/*++ Copyright (c) 1992 Microsoft Corporation Module Name: atp.c Abstract: This module contains the Appletalk Transaction Protocol code. Author: Jameel Hyder (jameelh@microsoft.com) Nikhil Kamkolkar (nikhilk@microsoft.com) 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 #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); }