summaryrefslogtreecommitdiffstats
path: root/sys/net/if_llatbl.c
diff options
context:
space:
mode:
authormelifaro <melifaro@FreeBSD.org>2015-08-10 12:03:59 +0000
committermelifaro <melifaro@FreeBSD.org>2015-08-10 12:03:59 +0000
commit4f240a9c31022feb60343e2e1108338b44edb083 (patch)
tree112dd76676cd486bd689769878bb091803964520 /sys/net/if_llatbl.c
parentfa26a290636c09c75688724a15b3624e6c44c8e4 (diff)
downloadFreeBSD-src-4f240a9c31022feb60343e2e1108338b44edb083.zip
FreeBSD-src-4f240a9c31022feb60343e2e1108338b44edb083.tar.gz
Partially merge r274887,r275334,r275577,r275578,r275586 to minimize
differences between projects/routing and HEAD. This commit tries to keep code logic the same while changing underlying code to use unified callbacks. * Add llt_foreach_entry method to traverse all entries in given llt * Add llt_dump_entry method to export particular lle entry in sysctl/rtsock format (code is not indented properly to minimize diff). Will be fixed in the next commits. * Add llt_link_entry/llt_unlink_entry methods to link/unlink particular lle. * Add llt_fill_sa_entry method to export address in the lle to sockaddr format. * Add llt_hash method to use in generic hash table support code. * Add llt_free_entry method which is used in llt_prefix_free code. * Prepare for fine-grained locking by separating lle unlink and deletion in lltable_free() and lltable_prefix_free(). * Provide lltable_get<ifp|af>() functions to reduce direct 'struct lltable' access by external callers. * Remove @llt agrument from lle_free() lle callback since it was unused. * Temporarily add L3_CADDR() macro for 'const' sockaddr typecasting. * Switch to per-af hashing code. * Rename LLE_FREE_LOCKED() callback from in[6]_lltable_free() to in_[6]lltable_destroy() to avoid clashing with llt_free_entry() method. Update description from these functions. * Use unified lltable_free_entry() function instead of per-af one. Reviewed by: ae
Diffstat (limited to 'sys/net/if_llatbl.c')
-rw-r--r--sys/net/if_llatbl.c281
1 files changed, 260 insertions, 21 deletions
diff --git a/sys/net/if_llatbl.c b/sys/net/if_llatbl.c
index 3f4e737..18276f0 100644
--- a/sys/net/if_llatbl.c
+++ b/sys/net/if_llatbl.c
@@ -70,6 +70,35 @@ static void vnet_lltable_init(void);
struct rwlock lltable_rwlock;
RW_SYSINIT(lltable_rwlock, &lltable_rwlock, "lltable_rwlock");
+static void llentries_unlink(struct lltable *llt, struct llentries *head);
+
+static void htable_unlink_entry(struct llentry *lle);
+static void htable_link_entry(struct lltable *llt, struct llentry *lle);
+static int htable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f,
+ void *farg);
+
+/*
+ * Dump lle state for a specific address family.
+ */
+static int
+lltable_dump_af(struct lltable *llt, struct sysctl_req *wr)
+{
+ int error;
+
+ LLTABLE_LOCK_ASSERT();
+
+ if (llt->llt_ifp->if_flags & IFF_LOOPBACK)
+ return (0);
+ error = 0;
+
+ IF_AFDATA_RLOCK(llt->llt_ifp);
+ error = lltable_foreach_lle(llt,
+ (llt_foreach_cb_t *)llt->llt_dump_entry, wr);
+ IF_AFDATA_RUNLOCK(llt->llt_ifp);
+
+ return (error);
+}
+
/*
* Dump arp state for a specific address family.
*/
@@ -82,7 +111,7 @@ lltable_sysctl_dumparp(int af, struct sysctl_req *wr)
LLTABLE_RLOCK();
SLIST_FOREACH(llt, &V_lltables, llt_link) {
if (llt->llt_af == af) {
- error = llt->llt_dump(llt, wr);
+ error = lltable_dump_af(llt, wr);
if (error != 0)
goto done;
}
@@ -93,25 +122,136 @@ done:
}
/*
- * Deletes an address from the address table.
- * This function is called by the timer functions
- * such as arptimer() and nd6_llinfo_timer(), and
- * the caller does the locking.
+ * Common function helpers for chained hash table.
+ */
+
+/*
+ * Runs specified callback for each entry in @llt.
+ * Caller does the locking.
+ *
+ */
+static int
+htable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg)
+{
+ struct llentry *lle, *next;
+ int i, error;
+
+ error = 0;
+
+ for (i = 0; i < LLTBL_HASHTBL_SIZE; i++) {
+ LIST_FOREACH_SAFE(lle, &llt->lle_head[i], lle_next, next) {
+ error = f(llt, lle, farg);
+ if (error != 0)
+ break;
+ }
+ }
+
+ return (error);
+}
+
+static void
+htable_link_entry(struct lltable *llt, struct llentry *lle)
+{
+ struct llentries *lleh;
+ uint32_t hashidx;
+
+ if ((lle->la_flags & LLE_LINKED) != 0)
+ return;
+
+ IF_AFDATA_WLOCK_ASSERT(llt->llt_ifp);
+
+ hashidx = llt->llt_hash(lle, LLTBL_HASHTBL_SIZE);
+ lleh = &llt->lle_head[hashidx];
+
+ lle->lle_tbl = llt;
+ lle->lle_head = lleh;
+ lle->la_flags |= LLE_LINKED;
+ LIST_INSERT_HEAD(lleh, lle, lle_next);
+}
+
+static void
+htable_unlink_entry(struct llentry *lle)
+{
+
+ if ((lle->la_flags & LLE_LINKED) != 0) {
+ IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp);
+ LIST_REMOVE(lle, lle_next);
+ lle->la_flags &= ~(LLE_VALID | LLE_LINKED);
+#if 0
+ lle->lle_tbl = NULL;
+ lle->lle_head = NULL;
+#endif
+ }
+}
+
+struct prefix_match_data {
+ const struct sockaddr *prefix;
+ const struct sockaddr *mask;
+ struct llentries dchain;
+ u_int flags;
+};
+
+static int
+htable_prefix_free_cb(struct lltable *llt, struct llentry *lle, void *farg)
+{
+ struct prefix_match_data *pmd;
+
+ pmd = (struct prefix_match_data *)farg;
+
+ if (llt->llt_match_prefix(pmd->prefix, pmd->mask, pmd->flags, lle)) {
+ LLE_WLOCK(lle);
+ LIST_INSERT_HEAD(&pmd->dchain, lle, lle_chain);
+ }
+
+ return (0);
+}
+
+static void
+htable_prefix_free(struct lltable *llt, const struct sockaddr *prefix,
+ const struct sockaddr *mask, u_int flags)
+{
+ struct llentry *lle, *next;
+ struct prefix_match_data pmd;
+
+ bzero(&pmd, sizeof(pmd));
+ pmd.prefix = prefix;
+ pmd.mask = mask;
+ pmd.flags = flags;
+ LIST_INIT(&pmd.dchain);
+
+ IF_AFDATA_WLOCK(llt->llt_ifp);
+ /* Push matching lles to chain */
+ lltable_foreach_lle(llt, htable_prefix_free_cb, &pmd);
+
+ llentries_unlink(llt, &pmd.dchain);
+ IF_AFDATA_WUNLOCK(llt->llt_ifp);
+
+ LIST_FOREACH_SAFE(lle, &pmd.dchain, lle_chain, next)
+ llt->llt_free_entry(llt, lle);
+}
+
+static void
+llentries_unlink(struct lltable *llt, struct llentries *head)
+{
+ struct llentry *lle, *next;
+
+ LIST_FOREACH_SAFE(lle, head, lle_chain, next)
+ llt->llt_unlink_entry(lle);
+}
+
+/*
+ * Helper function used to drop all mbufs in hold queue.
*
* Returns the number of held packets, if any, that were dropped.
*/
size_t
-llentry_free(struct llentry *lle)
+lltable_drop_entry_queue(struct llentry *lle)
{
size_t pkts_dropped;
struct mbuf *next;
- IF_AFDATA_WLOCK_ASSERT(lle->lle_tbl->llt_ifp);
LLE_WLOCK_ASSERT(lle);
- 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;
@@ -125,6 +265,34 @@ llentry_free(struct llentry *lle)
("%s: la_numheld %d > 0, pkts_droped %zd", __func__,
lle->la_numheld, pkts_dropped));
+ return (pkts_dropped);
+}
+
+/*
+ * Deletes an address from the address table.
+ * This function is called by the timer functions
+ * such as arptimer() and nd6_llinfo_timer(), and
+ * the caller does the locking.
+ *
+ * Returns the number of held packets, if any, that were dropped.
+ */
+size_t
+llentry_free(struct llentry *lle)
+{
+ struct lltable *llt;
+ size_t pkts_dropped;
+
+ LLE_WLOCK_ASSERT(lle);
+
+ if ((lle->la_flags & LLE_LINKED) != 0) {
+ llt = lle->lle_tbl;
+
+ IF_AFDATA_WLOCK_ASSERT(llt->llt_ifp);
+ llt->llt_unlink_entry(lle);
+ }
+
+ pkts_dropped = lltable_drop_entry_queue(lle);
+
LLE_FREE_LOCKED(lle);
return (pkts_dropped);
@@ -162,11 +330,28 @@ llentry_alloc(struct ifnet *ifp, struct lltable *lt,
/*
* Free all entries from given table and free itself.
*/
+
+static int
+lltable_free_cb(struct lltable *llt, struct llentry *lle, void *farg)
+{
+ struct llentries *dchain;
+
+ dchain = (struct llentries *)farg;
+
+ LLE_WLOCK(lle);
+ LIST_INSERT_HEAD(dchain, lle, lle_chain);
+
+ return (0);
+}
+
+/*
+ * Free all entries from given table and free itself.
+ */
void
lltable_free(struct lltable *llt)
{
struct llentry *lle, *next;
- int i;
+ struct llentries dchain;
KASSERT(llt != NULL, ("%s: llt is NULL", __func__));
@@ -174,17 +359,19 @@ lltable_free(struct lltable *llt)
SLIST_REMOVE(&V_lltables, llt, lltable, llt_link);
LLTABLE_WUNLOCK();
+ LIST_INIT(&dchain);
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) {
- LLE_WLOCK(lle);
- if (callout_stop(&lle->la_timer))
- LLE_REMREF(lle);
- llentry_free(lle);
- }
- }
+ /* Push all lles to @dchain */
+ lltable_foreach_lle(llt, lltable_free_cb, &dchain);
+ llentries_unlink(llt, &dchain);
IF_AFDATA_WUNLOCK(llt->llt_ifp);
+ LIST_FOREACH_SAFE(lle, &dchain, lle_chain, next) {
+ if (callout_stop(&lle->la_timer))
+ LLE_REMREF(lle);
+ llentry_free(lle);
+ }
+
free(llt, M_LLTABLE);
}
@@ -232,8 +419,6 @@ lltable_prefix_free(int af, struct sockaddr *prefix, struct sockaddr *mask,
LLTABLE_RUNLOCK();
}
-
-
/*
* Create a new lltable.
*/
@@ -250,6 +435,12 @@ lltable_init(struct ifnet *ifp, int af)
for (i = 0; i < LLTBL_HASHTBL_SIZE; i++)
LIST_INIT(&llt->lle_head[i]);
+ /* Set some default callbacks */
+ llt->llt_link_entry = htable_link_entry;
+ llt->llt_unlink_entry = htable_unlink_entry;
+ llt->llt_prefix_free = htable_prefix_free;
+ llt->llt_foreach_entry = htable_foreach_lle;
+
LLTABLE_WLOCK();
SLIST_INSERT_HEAD(&V_lltables, llt, llt_link);
LLTABLE_WUNLOCK();
@@ -258,6 +449,54 @@ lltable_init(struct ifnet *ifp, int af)
}
/*
+ * External methods used by lltable consumers
+ */
+
+int
+lltable_foreach_lle(struct lltable *llt, llt_foreach_cb_t *f, void *farg)
+{
+
+ return (llt->llt_foreach_entry(llt, f, farg));
+}
+
+void
+lltable_link_entry(struct lltable *llt, struct llentry *lle)
+{
+
+ llt->llt_link_entry(llt, lle);
+}
+
+void
+lltable_unlink_entry(struct lltable *llt, struct llentry *lle)
+{
+
+ llt->llt_unlink_entry(lle);
+}
+
+void
+lltable_fill_sa_entry(const struct llentry *lle, struct sockaddr *sa)
+{
+ struct lltable *llt;
+
+ llt = lle->lle_tbl;
+ llt->llt_fill_sa_entry(lle, sa);
+}
+
+struct ifnet *
+lltable_get_ifp(const struct lltable *llt)
+{
+
+ return (llt->llt_ifp);
+}
+
+int
+lltable_get_af(const struct lltable *llt)
+{
+
+ return (llt->llt_af);
+}
+
+/*
* Called in route_output when rtm_flags contains RTF_LLDATA.
*/
int
OpenPOWER on IntegriCloud