diff options
author | glebius <glebius@FreeBSD.org> | 2012-08-02 13:57:49 +0000 |
---|---|---|
committer | glebius <glebius@FreeBSD.org> | 2012-08-02 13:57:49 +0000 |
commit | abf245020a075c487a1ac4e60c7069e2d8c9c7c3 (patch) | |
tree | bc9d35350ff3e80778a0341908f6905a862f4004 /sys/net/if_llatbl.c | |
parent | 34fe3f296a23dcd2b2315ab9b7cbe217a7e36c17 (diff) | |
download | FreeBSD-src-abf245020a075c487a1ac4e60c7069e2d8c9c7c3.zip FreeBSD-src-abf245020a075c487a1ac4e60c7069e2d8c9c7c3.tar.gz |
Fix races between in_lltable_prefix_free(), lla_lookup(),
llentry_free() and arptimer():
o Use callout_init_rw() for lle timeout, this allows us safely
disestablish them.
- This allows us to simplify the arptimer() and make it
race safe.
o Consistently use ifp->if_afdata_lock to lock access to
linked lists in the lle hashes.
o Introduce new lle flag LLE_LINKED, which marks an entry that
is attached to the hash.
- Use LLE_LINKED to avoid double unlinking via consequent
calls to llentry_free().
- Mark lle with LLE_DELETED via |= operation istead of =,
so that other flags won't be lost.
o Make LLE_ADDREF(), LLE_REMREF() and LLE_FREE_LOCKED() more
consistent and provide more informative KASSERTs.
The patch is a collaborative work of all submitters and myself.
PR: kern/165863
Submitted by: Andrey Zonov <andrey zonov.org>
Submitted by: Ryan Stone <rysto32 gmail.com>
Submitted by: Eric van Gyzen <eric_van_gyzen dell.com>
Diffstat (limited to 'sys/net/if_llatbl.c')
-rw-r--r-- | sys/net/if_llatbl.c | 19 |
1 files changed, 13 insertions, 6 deletions
diff --git a/sys/net/if_llatbl.c b/sys/net/if_llatbl.c index 2fc75ef..bb49fdd 100644 --- a/sys/net/if_llatbl.c +++ b/sys/net/if_llatbl.c @@ -106,10 +106,19 @@ llentry_free(struct llentry *lle) size_t pkts_dropped; struct mbuf *next; - pkts_dropped = 0; + IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp); LLE_WLOCK_ASSERT(lle); + + /* XXX: guard against race with other llentry_free(). */ + if (!(lle->la_flags & LLE_LINKED)) { + LLE_FREE_LOCKED(lle); + return (0); + } + LIST_REMOVE(lle, lle_next); + lle->la_flags &= ~(LLE_VALID | LLE_LINKED); + pkts_dropped = 0; while ((lle->la_numheld > 0) && (lle->la_hold != NULL)) { next = lle->la_hold->m_nextpkt; m_freem(lle->la_hold); @@ -122,7 +131,6 @@ llentry_free(struct llentry *lle) ("%s: la_numheld %d > 0, pkts_droped %zd", __func__, lle->la_numheld, pkts_dropped)); - lle->la_flags &= ~LLE_VALID; LLE_FREE_LOCKED(lle); return (pkts_dropped); @@ -173,17 +181,16 @@ lltable_free(struct lltable *llt) SLIST_REMOVE(&V_lltables, llt, lltable, llt_link); LLTABLE_WUNLOCK(); + 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) { - int canceled; - - canceled = callout_drain(&lle->la_timer); LLE_WLOCK(lle); - if (canceled) + if (callout_stop(&lle->la_timer)) LLE_REMREF(lle); llentry_free(lle); } } + IF_AFDATA_WUNLOCK(llt->llt_ifp); free(llt, M_LLTABLE); } |