/*++ Copyright (c) 1997 - 98, Microsoft Corporation Module Name: rtmtimer.c Abstract: Contains timer callbacks for handling RTM functions like aging out routes etc. Author: Chaitanya Kodeboyina (chaitk) 14-Sep-1998 Revision History: --*/ #include "pchrtm.h" #pragma hdrstop VOID NTAPI RouteExpiryTimeoutCallback ( IN PVOID Context, IN BOOLEAN TimeOut ) /*++ Routine Description: This routine is invoked when the expiry timer associated with a route fires. At this time, the route needs to be aged out. Arguments: Context - Context for this timer callback TimeOut - TRUE if the timer fired, FALSE if wait satisfied. Return Value: None --*/ { PRTM_ENTITY_HANDLE EntityHandle; PRTM_ROUTE_HANDLE RouteHandle; PADDRFAM_INFO AddrFamInfo; PENTITY_INFO Entity; PDEST_INFO Dest; PROUTE_INFO Route; DWORD ChangeFlags; BOOL Success; DWORD Status; UNREFERENCED_PARAMETER(TimeOut); Route = (PROUTE_INFO) ((PROUTE_TIMER)Context)->Route; Dest = DEST_FROM_HANDLE(Route->RouteInfo.DestHandle); // // Has the timer has not been updated after it fired // ACQUIRE_DEST_WRITE_LOCK(Dest); if (Route->TimerContext != Context) { RELEASE_DEST_WRITE_LOCK(Dest); // // The timer has been updated after it fired, // This timer context is freed by the update // return; } // // The timer is still valid for this route, // Indicate to entity and free the context // Route->TimerContext = NULL; RELEASE_DEST_WRITE_LOCK(Dest); // // Inform the owner that the route has expired // EntityHandle = Route->RouteInfo.RouteOwner; Entity = ENTITY_FROM_HANDLE(EntityHandle); AddrFamInfo = Entity->OwningAddrFamily; RouteHandle = MAKE_HANDLE_FROM_POINTER(Route); REFERENCE_ROUTE(Route, HANDLE_REF); Status = ERROR_NOT_SUPPORTED; if (Entity->EventCallback) { // // This callback can turn back and post RTM calls, // so release locks before invoking this callback // Status = Entity->EventCallback(EntityHandle, RTM_ROUTE_EXPIRED, RouteHandle, &Route->RouteInfo); } if (Status == ERROR_NOT_SUPPORTED) { // // Delete the route as the owner does not care // Status = RtmDeleteRouteToDest(EntityHandle, RouteHandle, &ChangeFlags); // // The route could already have been deleted here // ASSERT((Status == NO_ERROR) || (Status == ERROR_NOT_FOUND) || (Status == ERROR_INVALID_HANDLE)); } // // Free the context as we do not need it now // Success = DeleteTimerQueueTimer(AddrFamInfo->RouteTimerQueue, ((PROUTE_TIMER)Context)->Timer, NULL); // ASSERT(Success); FreeMemory(Context); DEREFERENCE_ROUTE(Route, TIMER_REF); return; } VOID NTAPI RouteHolddownTimeoutCallback ( IN PVOID Context, IN BOOLEAN TimeOut ) /*++ Routine Description: This routine is invoked when holddown timer associated with a route fires. At this time, the route needs to be taken out of holddown. Arguments: Context - Context for this timer callback TimeOut - TRUE if the timer fired, FALSE if wait satisfied. Return Value: None --*/ { PADDRFAM_INFO AddrFamInfo; PENTITY_INFO Entity; PDEST_INFO Dest; PROUTE_INFO Route; PROUTE_INFO HoldRoute; PLOOKUP_LINKAGE DestData; ULONG NotifyToCNs; DWORD ViewsForCT[RTM_NUM_CHANGE_TYPES]; UINT i; BOOL Success; DWORD Status; UNREFERENCED_PARAMETER(TimeOut); Route = (PROUTE_INFO) ((PROUTE_TIMER)Context)->Route; Dest = DEST_FROM_HANDLE(Route->RouteInfo.DestHandle); Entity = ENTITY_FROM_HANDLE(Route->RouteInfo.RouteOwner); AddrFamInfo = Entity->OwningAddrFamily; // // The route must surely be in holddown by this time // ASSERT(Route->RouteInfo.State == RTM_ROUTE_STATE_DELETED); // // Has the timer has not been updated after it fired // ACQUIRE_DEST_WRITE_LOCK(Dest); if (Route->TimerContext != Context) { RELEASE_DEST_WRITE_LOCK(Dest); ASSERT(FALSE); // // The timer has been updated after it fired, // This timer context is freed by the update // return; } // // The timer is still valid for this route // // // Remove this holddown route from the dest // for (i = 0; i < AddrFamInfo->NumberOfViews; i++) { HoldRoute = Dest->ViewInfo[i].HoldRoute; if (HoldRoute == Route) { DEREFERENCE_ROUTE(HoldRoute, HOLD_REF); Dest->ViewInfo[i].HoldRoute = NULL; } } // // We need to generate notifications for any // holddown protocols interesed in this dest // // // Calculate the CNs that need to be notified // ACQUIRE_NOTIFICATIONS_READ_LOCK(AddrFamInfo); for (i = 0; i < RTM_NUM_CHANGE_TYPES; i++) { ViewsForCT[i] = AddrFamInfo->ViewsSupported; } NotifyToCNs = ComputeCNsToBeNotified(AddrFamInfo, Dest->DestMarkedBits, ViewsForCT); // // Add to the global change list if required // if (NotifyToCNs) { AddToChangedDestLists(AddrFamInfo, Dest, NotifyToCNs); } RELEASE_NOTIFICATIONS_READ_LOCK(AddrFamInfo); // // Reset the timer context and free it later // Route->TimerContext = NULL; // // Reduce hold ref so that dest can be deleted // ASSERT(Dest->HoldRefCount > 0); if (Dest->NumRoutes || (Dest->HoldRefCount > 1)) { Dest->HoldRefCount--; } else { // // Removal of hold might result in dest deletion // RELEASE_DEST_WRITE_LOCK(Dest); ACQUIRE_ROUTE_TABLE_WRITE_LOCK(AddrFamInfo); ACQUIRE_DEST_WRITE_LOCK(Dest); Dest->HoldRefCount--; if ((Dest->NumRoutes == 0) && (Dest->HoldRefCount == 0)) { Dest->State = DEST_STATE_DELETED; Status = DeleteFromTable(AddrFamInfo->RouteTable, Dest->DestAddress.NumBits, Dest->DestAddress.AddrBits, NULL, &DestData); ASSERT(SUCCESS(Status)); AddrFamInfo->NumDests--; } RELEASE_ROUTE_TABLE_WRITE_LOCK(AddrFamInfo); } RELEASE_DEST_WRITE_LOCK(Dest); // // Free the context as we do not need it now // Success = DeleteTimerQueueTimer(AddrFamInfo->RouteTimerQueue, ((PROUTE_TIMER)Context)->Timer, NULL); // ASSERT(Success); FreeMemory(Context); DEREFERENCE_ROUTE(Route, TIMER_REF); return; }