neigh: optimize neigh_parms_release()
authorNicolas Dichtel <nicolas.dichtel@6wind.com>
Wed, 29 Oct 2014 18:29:31 +0000 (19:29 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 29 Oct 2014 20:11:50 +0000 (16:11 -0400)
In neigh_parms_release() we loop over all entries to find the entry given in
argument and being able to remove it from the list. By using a double linked
list, we can avoid this loop.

Here are some numbers with 30 000 dummy interfaces configured:

Before the patch:
$ time rmmod dummy
real 2m0.118s
user 0m0.000s
sys 1m50.048s

After the patch:
$ time rmmod dummy
real 1m9.970s
user 0m0.000s
sys 0m47.976s

Suggested-by: Thierry Herbelot <thierry.herbelot@6wind.com>
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Acked-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/neighbour.h
net/core/neighbour.c

index f60558d0254ca1a482ab4eb2924ecbaf8d6c169c..dedfb188b1a720bb7adcbda146a7b32b754f72b9 100644 (file)
@@ -69,7 +69,7 @@ struct neigh_parms {
        struct net *net;
 #endif
        struct net_device *dev;
-       struct neigh_parms *next;
+       struct list_head list;
        int     (*neigh_setup)(struct neighbour *);
        void    (*neigh_cleanup)(struct neighbour *);
        struct neigh_table *tbl;
@@ -203,6 +203,7 @@ struct neigh_table {
        void                    (*proxy_redo)(struct sk_buff *skb);
        char                    *id;
        struct neigh_parms      parms;
+       struct list_head        parms_list;
        int                     gc_interval;
        int                     gc_thresh1;
        int                     gc_thresh2;
index ef31fef25e5a872eb6565c1d57953d0c6558d2c7..edd04116ecb7d7d973edf5f802d465aed5b6fcac 100644 (file)
@@ -773,7 +773,7 @@ static void neigh_periodic_work(struct work_struct *work)
        if (time_after(jiffies, tbl->last_rand + 300 * HZ)) {
                struct neigh_parms *p;
                tbl->last_rand = jiffies;
-               for (p = &tbl->parms; p; p = p->next)
+               list_for_each_entry(p, &tbl->parms_list, list)
                        p->reachable_time =
                                neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME));
        }
@@ -1446,7 +1446,7 @@ static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,
 {
        struct neigh_parms *p;
 
-       for (p = &tbl->parms; p; p = p->next) {
+       list_for_each_entry(p, &tbl->parms_list, list) {
                if ((p->dev && p->dev->ifindex == ifindex && net_eq(neigh_parms_net(p), net)) ||
                    (!p->dev && !ifindex && net_eq(net, &init_net)))
                        return p;
@@ -1481,8 +1481,7 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
                }
 
                write_lock_bh(&tbl->lock);
-               p->next         = tbl->parms.next;
-               tbl->parms.next = p;
+               list_add(&p->list, &tbl->parms.list);
                write_unlock_bh(&tbl->lock);
 
                neigh_parms_data_state_cleanall(p);
@@ -1501,24 +1500,15 @@ static void neigh_rcu_free_parms(struct rcu_head *head)
 
 void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
 {
-       struct neigh_parms **p;
-
        if (!parms || parms == &tbl->parms)
                return;
        write_lock_bh(&tbl->lock);
-       for (p = &tbl->parms.next; *p; p = &(*p)->next) {
-               if (*p == parms) {
-                       *p = parms->next;
-                       parms->dead = 1;
-                       write_unlock_bh(&tbl->lock);
-                       if (parms->dev)
-                               dev_put(parms->dev);
-                       call_rcu(&parms->rcu_head, neigh_rcu_free_parms);
-                       return;
-               }
-       }
+       list_del(&parms->list);
+       parms->dead = 1;
        write_unlock_bh(&tbl->lock);
-       neigh_dbg(1, "%s: not found\n", __func__);
+       if (parms->dev)
+               dev_put(parms->dev);
+       call_rcu(&parms->rcu_head, neigh_rcu_free_parms);
 }
 EXPORT_SYMBOL(neigh_parms_release);
 
@@ -1535,6 +1525,8 @@ static void neigh_table_init_no_netlink(struct neigh_table *tbl)
        unsigned long now = jiffies;
        unsigned long phsize;
 
+       INIT_LIST_HEAD(&tbl->parms_list);
+       list_add(&tbl->parms.list, &tbl->parms_list);
        write_pnet(&tbl->parms.net, &init_net);
        atomic_set(&tbl->parms.refcnt, 1);
        tbl->parms.reachable_time =
@@ -2154,7 +2146,9 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
                                       NLM_F_MULTI) <= 0)
                        break;
 
-               for (nidx = 0, p = tbl->parms.next; p; p = p->next) {
+               nidx = 0;
+               p = list_next_entry(&tbl->parms, list);
+               list_for_each_entry_from(p, &tbl->parms_list, list) {
                        if (!net_eq(neigh_parms_net(p), net))
                                continue;