Date: Thu, 29 Mar 2012 17:59:38 -0400 From: Ryan Stone <rysto32@gmail.com> To: Gleb Smirnoff <glebius@freebsd.org> Cc: freebsd-net@freebsd.org Subject: Re: kern/165863 Message-ID: <CAFMmRNwWZFzbqG7ejdEqsMTKzxxZv-ZbGGJGr98mYcE_ku7xMQ@mail.gmail.com> In-Reply-To: <201203090930.q299UCPX057950@freefall.freebsd.org> References: <201203090930.q299UCPX057950@freefall.freebsd.org>
next in thread | previous in thread | raw e-mail | index | archive | help
Ok, I think that I have an approach that will work. This is heavily based off of glebius' proposal. The big difference is that instead of initializing the arptimer callout with the ll_entry's lock, I initialize it with the if_afdata_lock. This eliminates the LOR problem in arptimer. It also nicely prevents any races between arptimer and in_lltable_prefix_free. Either arptimer will run and ensure that prefix_free can never see an entry that arptimer is in the process of destroying, or prefix_free will stop the callout before arptimer gets a chance to start. I'll admit that it feels a bit dirty to be doing anything if the ifp at this level, but I'd argue that is an artifact of using a lock in the ifp to protect the lltable. The patch below is not yet complete; it doesn't fix the IPv6 case. IPv6 is looking much trickier as when an NDP entry is timed out nd6_free() will drop the LLE_WLOCK, acquire the IF_AFDATA_LOCK and then re-acquire the LLE_WLOCK. It's not immediately clear to me what the best way to handle the race between in6_lltable_prefix_free and nd6_llinfo_timer is. Holding the afdata_lock across all of nd6_llinfo_timer feels like overkill. Any comments on this approach? Am I going in the wrong direction? diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c index bdb4efc..02d9af4 100644 --- a/sys/netinet/if_ether.c +++ b/sys/netinet/if_ether.c @@ -165,36 +165,26 @@ arptimer(void *arg) { struct ifnet *ifp; struct llentry *lle; - int pkts_dropped; + size_t pkts_dropped; KASSERT(arg != NULL, ("%s: arg NULL", __func__)); lle = (struct llentry *)arg; + + if (lle->la_flags & LLE_STATIC) + return; + ifp = lle->lle_tbl->llt_ifp; CURVNET_SET(ifp->if_vnet); - IF_AFDATA_LOCK(ifp); + IF_AFDATA_LOCK_ASSERT(ifp); LLE_WLOCK(lle); - if (lle->la_flags & LLE_STATIC) - LLE_WUNLOCK(lle); - else { - if (!callout_pending(&lle->la_timer) && - callout_active(&lle->la_timer)) { - callout_stop(&lle->la_timer); - LLE_REMREF(lle); - pkts_dropped = llentry_free(lle); - ARPSTAT_ADD(dropped, pkts_dropped); - ARPSTAT_INC(timeouts); - } else { -#ifdef DIAGNOSTIC - struct sockaddr *l3addr = L3_ADDR(lle); - log(LOG_INFO, - "arptimer issue: %p, IPv4 address: \"%s\"\n", lle, - inet_ntoa( - ((const struct sockaddr_in *)l3addr)->sin_addr)); -#endif - LLE_WUNLOCK(lle); - } - } - IF_AFDATA_UNLOCK(ifp); + LLE_REMREF(lle); + + /* llentry_free drops the LLE_WLOCK */ + pkts_dropped = llentry_free(lle); + + ARPSTAT_ADD(dropped, pkts_dropped); + ARPSTAT_INC(timeouts); + CURVNET_RESTORE(); } diff --git a/sys/netinet/in.c b/sys/netinet/in.c index ac0aada..0d5d80e 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -1275,15 +1275,17 @@ in_lltable_free(struct lltable *llt, struct llentry *lle) } static struct llentry * -in_lltable_new(const struct sockaddr *l3addr, u_int flags) +in_lltable_new(struct lltable *llt, const struct sockaddr *l3addr, u_int flags) { struct in_llentry *lle; + struct ifnet *ifp; lle = malloc(sizeof(struct in_llentry), M_LLTABLE, M_DONTWAIT | M_ZERO); if (lle == NULL) /* NB: caller generates msg */ return NULL; - callout_init(&lle->base.la_timer, CALLOUT_MPSAFE); + ifp = llt->llt_ifp; + callout_init_rw(&lle->base.la_timer, &ifp->if_afdata_lock, 0); /* * For IPv4 this will trigger "arpresolve" to generate * an ARP request. @@ -1292,6 +1294,7 @@ in_lltable_new(const struct sockaddr *l3addr, u_int flags) lle->l3_addr4 = *(const struct sockaddr_in *)l3addr; lle->base.lle_refcnt = 1; lle->base.lle_free = in_lltable_free; + lle->base.lle_tbl = llt; LLE_LOCK_INIT(&lle->base); return &lle->base; } @@ -1311,6 +1314,7 @@ in_lltable_prefix_free(struct lltable *llt, register int i; size_t pkts_dropped; + IF_AFDATA_WLOCK(llt->llt_ifp); for (i=0; i < LLTBL_HASHTBL_SIZE; i++) { LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) { @@ -1318,20 +1322,19 @@ in_lltable_prefix_free(struct lltable *llt, * (flags & LLE_STATIC) means deleting all entries * including static ARP entries */ - if (IN_ARE_MASKED_ADDR_EQUAL((struct sockaddr_in *)L3_ADDR(lle), - pfx, msk) && - ((flags & LLE_STATIC) || !(lle->la_flags & LLE_STATIC))) { - int canceled; + if (IN_ARE_MASKED_ADDR_EQUAL(satosin(L3_ADDR(lle)), + pfx, msk) && ((flags & LLE_STATIC) || + !(lle->la_flags & LLE_STATIC))) { - canceled = callout_drain(&lle->la_timer); LLE_WLOCK(lle); - if (canceled) + if (callout_stop(&lle->la_timer)) LLE_REMREF(lle); pkts_dropped = llentry_free(lle); ARPSTAT_ADD(dropped, pkts_dropped); } } } + IF_AFDATA_WUNLOCK(llt->llt_ifp); } @@ -1451,7 +1454,7 @@ in_lltable_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3add in_lltable_rtcheck(ifp, flags, l3addr) != 0) goto done; - lle = in_lltable_new(l3addr, flags); + lle = in_lltable_new(llt, l3addr, flags); if (lle == NULL) { log(LOG_INFO, "lla_lookup: new lle malloc failed\n"); goto done; @@ -1462,7 +1465,6 @@ in_lltable_lookup(struct lltable *llt, u_int flags, const struct sockaddr *l3add lle->la_flags |= (LLE_VALID | LLE_STATIC); } - lle->lle_tbl = llt; lle->lle_head = lleh; LIST_INSERT_HEAD(lleh, lle, lle_next); } else if (flags & LLE_DELETE) {
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAFMmRNwWZFzbqG7ejdEqsMTKzxxZv-ZbGGJGr98mYcE_ku7xMQ>