Index: sys/net/if_llatbl.h =================================================================== --- sys/net/if_llatbl.h +++ sys/net/if_llatbl.h @@ -104,6 +104,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 @@ -494,6 +494,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. */ @@ -515,8 +552,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 @@ -174,28 +174,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); @@ -258,7 +240,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); @@ -267,6 +249,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); } @@ -531,14 +514,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); @@ -1219,7 +1196,7 @@ static void arp_mark_lle_reachable(struct llentry *la) { - int canceled, wtime; + int wtime; LLE_WLOCK_ASSERT(la); @@ -1227,15 +1204,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 @@ -1078,7 +1078,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); } @@ -1136,9 +1136,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); @@ -1283,6 +1282,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 @@ -2039,7 +2039,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); } @@ -2093,9 +2093,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); } @@ -2191,6 +2190,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 @@ -520,29 +520,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); } /* @@ -746,29 +739,23 @@ KASSERT(arg != NULL, ("%s: arg NULL", __func__)); ln = (struct llentry *)arg; + LLE_WLOCK_ASSERT(ln); ifp = lltable_get_ifp(ln->lle_tbl); CURVNET_SET(ifp->if_vnet); + /* XXX swap locks */ + LLE_WUNLOCK(ln); ND6_RLOCK(); 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. + * Because this callout is protected by a lock + * callout_reset() won't return 1 when we get into + * this function and we need to drop our reference if + * there was callout reset in between. */ - LLE_WUNLOCK(ln); + LLE_FREE_LOCKED(ln); ND6_RUNLOCK(); CURVNET_RESTORE(); return; @@ -1414,6 +1401,7 @@ nd6_llinfo_settimer_locked(ln, (long)V_nd6_gctimer * hz); + /* Drop reference so that timeout frees "ln" */ LLE_REMREF(ln); LLE_WUNLOCK(ln); defrouter_rele(dr); @@ -1490,12 +1478,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); if (dr != NULL) defrouter_rele(dr);