Author: glebius
Date: Fri Nov 22 19:22:26 2013
New Revision: 258480
URL: http://svnweb.freebsd.org/changeset/base/258480

Log:
  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.

Modified:
  head/sys/netpfil/pf/pf_ioctl.c

Modified: head/sys/netpfil/pf/pf_ioctl.c
==============================================================================
--- head/sys/netpfil/pf/pf_ioctl.c      Fri Nov 22 19:16:34 2013        
(r258479)
+++ head/sys/netpfil/pf/pf_ioctl.c      Fri Nov 22 19:22:26 2013        
(r258480)
@@ -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!!!
  */
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to