summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorglebius <glebius@FreeBSD.org>2013-11-22 19:22:26 +0000
committerglebius <glebius@FreeBSD.org>2013-11-22 19:22:26 +0000
commitf889028338d27bce7743ea9cf0a00609d17bc8d5 (patch)
tree71778636967ed49483d7b20f86d84e9530a89f8d
parentc884926273902ab83911a8b89c520ff5a8c58b20 (diff)
downloadFreeBSD-src-f889028338d27bce7743ea9cf0a00609d17bc8d5.zip
FreeBSD-src-f889028338d27bce7743ea9cf0a00609d17bc8d5.tar.gz
The DIOCKILLSRCNODES operation was implemented with O(m*n) complexity,
where "m" is number of source nodes and "n" is number of states. Thus, on heavy loaded router its processing consumed a lot of CPU time. Reimplement it with O(m+n) complexity. We first scan through source nodes and disconnect matching ones, putting them on the freelist and marking with a cookie value in their expire field. Then we scan through the states, detecting references to source nodes with a cookie, and disconnect them as well. Then the freelist is passed to pf_free_src_nodes(). In collaboration with: Kajetan Staszkiewicz <kajetan.staszkiewicz innogames.de> PR: kern/176763 Sponsored by: InnoGames GmbH Sponsored by: Nginx, Inc.
-rw-r--r--sys/netpfil/pf/pf_ioctl.c94
1 files changed, 56 insertions, 38 deletions
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index dcf0318..a6ff85b 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -155,6 +155,7 @@ struct cdev *pf_dev;
static void pf_clear_states(void);
static int pf_clear_tables(void);
static void pf_clear_srcnodes(struct pf_src_node *);
+static void pf_kill_srcnodes(struct pfioc_src_node_kill *);
static void pf_tbladdr_copyout(struct pf_addr_wrap *);
/*
@@ -3143,45 +3144,9 @@ DIOCCHANGEADDR_error:
break;
}
- case DIOCKILLSRCNODES: {
- struct pfioc_src_node_kill *psnk =
- (struct pfioc_src_node_kill *)addr;
- struct pf_srchash *sh;
- struct pf_src_node *sn;
- u_int i, killed = 0;
-
- for (i = 0, sh = V_pf_srchash; i < V_pf_srchashmask;
- i++, sh++) {
- /*
- * XXXGL: we don't ever acquire sources hash lock
- * but if we ever do, the below call to pf_clear_srcnodes()
- * would lead to a LOR.
- */
- PF_HASHROW_LOCK(sh);
- LIST_FOREACH(sn, &sh->nodes, entry)
- if (PF_MATCHA(psnk->psnk_src.neg,
- &psnk->psnk_src.addr.v.a.addr,
- &psnk->psnk_src.addr.v.a.mask,
- &sn->addr, sn->af) &&
- PF_MATCHA(psnk->psnk_dst.neg,
- &psnk->psnk_dst.addr.v.a.addr,
- &psnk->psnk_dst.addr.v.a.mask,
- &sn->raddr, sn->af)) {
- /* Handle state to src_node linkage */
- if (sn->states != 0)
- pf_clear_srcnodes(sn);
- sn->expire = 1;
- killed++;
- }
- PF_HASHROW_UNLOCK(sh);
- }
-
- if (killed > 0)
- pf_purge_expired_src_nodes();
-
- psnk->psnk_killed = killed;
+ case DIOCKILLSRCNODES:
+ pf_kill_srcnodes((struct pfioc_src_node_kill *)addr);
break;
- }
case DIOCSETHOSTID: {
u_int32_t *hostid = (u_int32_t *)addr;
@@ -3400,6 +3365,59 @@ pf_clear_srcnodes(struct pf_src_node *n)
n->states = 0;
}
}
+
+static void
+pf_kill_srcnodes(struct pfioc_src_node_kill *psnk)
+{
+ struct pf_src_node_list kill;
+
+ LIST_INIT(&kill);
+ for (int i = 0; i <= V_pf_srchashmask; i++) {
+ struct pf_srchash *sh = &V_pf_srchash[i];
+ struct pf_src_node *sn, *tmp;
+
+ PF_HASHROW_LOCK(sh);
+ LIST_FOREACH_SAFE(sn, &sh->nodes, entry, tmp)
+ if (PF_MATCHA(psnk->psnk_src.neg,
+ &psnk->psnk_src.addr.v.a.addr,
+ &psnk->psnk_src.addr.v.a.mask,
+ &sn->addr, sn->af) &&
+ PF_MATCHA(psnk->psnk_dst.neg,
+ &psnk->psnk_dst.addr.v.a.addr,
+ &psnk->psnk_dst.addr.v.a.mask,
+ &sn->raddr, sn->af)) {
+ pf_unlink_src_node_locked(sn);
+ LIST_INSERT_HEAD(&kill, sn, entry);
+ sn->expire = 1;
+ }
+ PF_HASHROW_UNLOCK(sh);
+ }
+
+ for (int i = 0; i <= V_pf_hashmask; i++) {
+ struct pf_idhash *ih = &V_pf_idhash[i];
+ struct pf_state *s;
+
+ PF_HASHROW_LOCK(ih);
+ LIST_FOREACH(s, &ih->states, entry) {
+ if (s->src_node && s->src_node->expire == 1) {
+#ifdef INVARIANTS
+ s->src_node->states--;
+#endif
+ s->src_node = NULL;
+ }
+ if (s->nat_src_node && s->nat_src_node->expire == 1) {
+#ifdef INVARIANTS
+ s->nat_src_node->states--;
+#endif
+ s->nat_src_node = NULL;
+ }
+ }
+ PF_HASHROW_UNLOCK(ih);
+ }
+
+ psnk->psnk_killed = pf_free_src_nodes(&kill);
+}
+
/*
* XXX - Check for version missmatch!!!
*/
OpenPOWER on IntegriCloud