Index: sys/net/if_llatbl.h =================================================================== --- sys/net/if_llatbl.h +++ sys/net/if_llatbl.h @@ -105,6 +105,9 @@ #define LLE_REQ_LOCK(lle) mtx_lock(&(lle)->req_mtx) #define LLE_REQ_UNLOCK(lle) mtx_unlock(&(lle)->req_mtx) +#define LLE_TIMER_INIT(lle) callout_init_rw(&(lle)->lle_timer, \ + &(lle)->lle_lock, CALLOUT_RETURNUNLOCKED) + #define LLE_IS_VALID(lle) (((lle) != NULL) && ((lle) != (void *)-1)) #define LLE_ADDREF(lle) do { \ @@ -216,6 +219,9 @@ #endif int lltable_sysctl_dumparp(int, struct sysctl_req *); +void llentry_timer_start(struct llentry *, int, void (*)(void *)); +void llentry_timer_stop(struct llentry *); +void llentry_timer_uninit(struct llentry *); size_t llentry_free(struct llentry *); struct llentry *llentry_alloc(struct ifnet *, struct lltable *, struct sockaddr_storage *); Index: sys/net/if_llatbl.c =================================================================== --- sys/net/if_llatbl.c +++ sys/net/if_llatbl.c @@ -415,6 +415,43 @@ return (0); } +void +llentry_timer_start(struct llentry *lle, int timeout, void (*func)(void *)) +{ + + if (callout_reset(&lle->lle_timer, timeout, func, lle) <= 0) + LLE_ADDREF(lle); +} + +void +llentry_timer_stop(struct llentry *lle) +{ + + if (callout_stop(&lle->lle_timer) > 0) + LLE_REMREF(lle); +} + +static void +llentry_timer_drain_callback(void *arg) +{ + struct llentry *lle = arg; + + LLE_FREE(lle); +} + +void +llentry_timer_uninit(struct llentry *lle) +{ + int cancelled; + + cancelled = callout_async_drain(&lle->lle_timer, + &llentry_timer_drain_callback); + if (cancelled > 0) + LLE_REMREF(lle); /* was stopped */ + else if (cancelled == 0) + LLE_ADDREF(lle); /* needs drain */ +} + /* * Free all entries from given table and free itself. */ @@ -436,8 +473,7 @@ IF_AFDATA_WUNLOCK(llt->llt_ifp); LIST_FOREACH_SAFE(lle, &dchain, lle_chain, next) { - if (callout_stop(&lle->lle_timer) > 0) - LLE_REMREF(lle); + llentry_timer_uninit(lle); llentry_free(lle); } Index: sys/netinet/if_ether.c =================================================================== --- sys/netinet/if_ether.c +++ sys/netinet/if_ether.c @@ -175,28 +175,10 @@ int r_skip_req; if (lle->la_flags & LLE_STATIC) { + LLE_WUNLOCK(lle); return; } - LLE_WLOCK(lle); - if (callout_pending(&lle->lle_timer)) { - /* - * Here we are a bit odd here in the treatment of - * active/pending. If the pending bit is set, it got - * rescheduled before I ran. The active - * bit we ignore, since if it was stopped - * in ll_tablefree() and was currently running - * it would have return 0 so the code would - * not have deleted it since the callout could - * not be stopped so we want to go through - * with the delete here now. If the callout - * was restarted, the pending bit will be back on and - * we just want to bail since the callout_reset would - * return 1 and our reference would have been removed - * by arpresolve() below. - */ - LLE_WUNLOCK(lle); - return; - } + ifp = lle->lle_tbl->llt_ifp; CURVNET_SET(ifp->if_vnet); @@ -259,7 +241,7 @@ EVENTHANDLER_INVOKE(lle_event, lle, evt); } - callout_stop(&lle->lle_timer); + llentry_timer_stop(lle); /* XXX: LOR avoidance. We still have ref on lle. */ LLE_WUNLOCK(lle); @@ -268,6 +250,7 @@ /* Guard against race with other llentry_free(). */ if (lle->la_flags & LLE_LINKED) { + /* Remove our reference */ LLE_REMREF(lle); lltable_unlink_entry(lle->lle_tbl, lle); } @@ -465,14 +448,8 @@ error = is_gw != 0 ? EHOSTUNREACH : EHOSTDOWN; if (renew) { - int canceled; - - LLE_ADDREF(la); la->la_expire = time_uptime; - canceled = callout_reset(&la->lle_timer, hz * V_arpt_down, - arptimer, la); - if (canceled) - LLE_REMREF(la); + llentry_timer_start(la, hz * V_arpt_down, arptimer); la->la_asked++; LLE_WUNLOCK(la); arprequest(ifp, NULL, &SIN(dst)->sin_addr, NULL); @@ -1109,7 +1086,7 @@ static void arp_mark_lle_reachable(struct llentry *la) { - int canceled, wtime; + int wtime; LLE_WLOCK_ASSERT(la); @@ -1117,15 +1094,11 @@ EVENTHANDLER_INVOKE(lle_event, la, LLENTRY_RESOLVED); if (!(la->la_flags & LLE_STATIC)) { - LLE_ADDREF(la); la->la_expire = time_uptime + V_arpt_keep; wtime = V_arpt_keep - V_arp_maxtries * V_arpt_rexmit; if (wtime < 0) wtime = V_arpt_keep; - canceled = callout_reset(&la->lle_timer, - hz * wtime, arptimer, la); - if (canceled) - LLE_REMREF(la); + llentry_timer_start(la, hz * wtime, arptimer); } la->la_asked = 0; la->la_preempt = V_arp_maxtries; Index: sys/netinet/in.c =================================================================== --- sys/netinet/in.c +++ sys/netinet/in.c @@ -1036,7 +1036,7 @@ lle->base.lle_free = in_lltable_destroy_lle; LLE_LOCK_INIT(&lle->base); LLE_REQ_INIT(&lle->base); - callout_init(&lle->base.lle_timer, 1); + LLE_TIMER_INIT(&lle->base); return (&lle->base); } @@ -1094,9 +1094,8 @@ lltable_unlink_entry(llt, lle); } - /* cancel timer */ - if (callout_stop(&lle->lle_timer) > 0) - LLE_REMREF(lle); + /* Cancel timer */ + llentry_timer_uninit(lle); /* Drop hold queue */ pkts_dropped = llentry_free(lle); @@ -1231,6 +1230,8 @@ #ifdef DIAGNOSTIC log(LOG_INFO, "ifaddr cache = %p is deleted\n", lle); #endif + /* Cancel timer */ + llentry_timer_uninit(lle); llentry_free(lle); } Index: sys/netinet6/in6.c =================================================================== --- sys/netinet6/in6.c +++ sys/netinet6/in6.c @@ -2082,7 +2082,7 @@ lle->base.lle_free = in6_lltable_destroy_lle; LLE_LOCK_INIT(&lle->base); LLE_REQ_INIT(&lle->base); - callout_init(&lle->base.lle_timer, 1); + LLE_TIMER_INIT(&lle->base); return (&lle->base); } @@ -2136,9 +2136,8 @@ lltable_unlink_entry(llt, lle); } - if (callout_stop(&lle->lle_timer) > 0) - LLE_REMREF(lle); - + /* Cancel timer */ + llentry_timer_uninit(lle); llentry_free(lle); } @@ -2234,6 +2233,8 @@ #ifdef DIAGNOSTIC log(LOG_INFO, "ifaddr cache = %p is deleted\n", lle); #endif + /* Cancel timer */ + llentry_timer_uninit(lle); llentry_free(lle); } Index: sys/netinet6/nd6.c =================================================================== --- sys/netinet6/nd6.c +++ sys/netinet6/nd6.c @@ -489,29 +489,22 @@ static void nd6_llinfo_settimer_locked(struct llentry *ln, long tick) { - int canceled; - LLE_WLOCK_ASSERT(ln); if (tick < 0) { ln->la_expire = 0; ln->ln_ntick = 0; - canceled = callout_stop(&ln->lle_timer); + llentry_timer_stop(ln); } else { ln->la_expire = time_uptime + tick / hz; - LLE_ADDREF(ln); if (tick > INT_MAX) { ln->ln_ntick = tick - INT_MAX; - canceled = callout_reset(&ln->lle_timer, INT_MAX, - nd6_llinfo_timer, ln); + llentry_timer_start(ln, INT_MAX, nd6_llinfo_timer); } else { ln->ln_ntick = 0; - canceled = callout_reset(&ln->lle_timer, tick, - nd6_llinfo_timer, ln); + llentry_timer_start(ln, tick, nd6_llinfo_timer); } } - if (canceled > 0) - LLE_REMREF(ln); } /* @@ -715,28 +708,9 @@ KASSERT(arg != NULL, ("%s: arg NULL", __func__)); ln = (struct llentry *)arg; - LLE_WLOCK(ln); - if (callout_pending(&ln->lle_timer)) { - /* - * Here we are a bit odd here in the treatment of - * active/pending. If the pending bit is set, it got - * rescheduled before I ran. The active - * bit we ignore, since if it was stopped - * in ll_tablefree() and was currently running - * it would have return 0 so the code would - * not have deleted it since the callout could - * not be stopped so we want to go through - * with the delete here now. If the callout - * was restarted, the pending bit will be back on and - * we just want to bail since the callout_reset would - * return 1 and our reference would have been removed - * by nd6_llinfo_settimer_locked above since canceled - * would have been 1. - */ - LLE_WUNLOCK(ln); - return; - } + LLE_WLOCK_ASSERT(ln); ifp = ln->lle_tbl->llt_ifp; + CURVNET_SET(ifp->if_vnet); ndi = ND_IFINFO(ifp); send_ns = 0; @@ -1364,6 +1338,7 @@ nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz); + /* Drop reference so that timeout frees "ln" */ LLE_REMREF(ln); LLE_WUNLOCK(ln); return; @@ -1439,12 +1414,14 @@ LLE_WLOCK(ln); /* Guard against race with other llentry_free(). */ if (ln->la_flags & LLE_LINKED) { - /* Remove callout reference */ + /* Remove our reference */ LLE_REMREF(ln); lltable_unlink_entry(ln->lle_tbl, ln); } IF_AFDATA_UNLOCK(ifp); + /* Cancel timer */ + llentry_timer_uninit(ln); llentry_free(ln); }