net: Allow xfrm_user_net_exit to batch efficiently.
Eric W. Biederman [Thu, 3 Dec 2009 02:29:05 +0000 (02:29 +0000)]
xfrm.nlsk is provided by the xfrm_user module and is access via rcu from
other parts of the xfrm code.  Add xfrm.nlsk_stash a copy of xfrm.nlsk that
will never be set to NULL.  This allows the synchronize_net and
netlink_kernel_release to be deferred until a whole batch of xfrm.nlsk sockets
have been set to NULL.

Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

include/net/netns/xfrm.h
net/xfrm/xfrm_user.c

index 1ba9127..56f8e55 100644 (file)
@@ -43,6 +43,7 @@ struct netns_xfrm {
        struct work_struct      policy_hash_work;
 
        struct sock             *nlsk;
+       struct sock             *nlsk_stash;
 
        u32                     sysctl_aevent_etime;
        u32                     sysctl_aevent_rseqth;
index fb42d77..1ada618 100644 (file)
@@ -2721,22 +2721,24 @@ static int __net_init xfrm_user_net_init(struct net *net)
                                     xfrm_netlink_rcv, NULL, THIS_MODULE);
        if (nlsk == NULL)
                return -ENOMEM;
+       net->xfrm.nlsk_stash = nlsk; /* Don't set to NULL */
        rcu_assign_pointer(net->xfrm.nlsk, nlsk);
        return 0;
 }
 
-static void __net_exit xfrm_user_net_exit(struct net *net)
+static void __net_exit xfrm_user_net_exit(struct list_head *net_exit_list)
 {
-       struct sock *nlsk = net->xfrm.nlsk;
-
-       rcu_assign_pointer(net->xfrm.nlsk, NULL);
-       synchronize_rcu();
-       netlink_kernel_release(nlsk);
+       struct net *net;
+       list_for_each_entry(net, net_exit_list, exit_list)
+               rcu_assign_pointer(net->xfrm.nlsk, NULL);
+       synchronize_net();
+       list_for_each_entry(net, net_exit_list, exit_list)
+               netlink_kernel_release(net->xfrm.nlsk_stash);
 }
 
 static struct pernet_operations xfrm_user_net_ops = {
-       .init = xfrm_user_net_init,
-       .exit = xfrm_user_net_exit,
+       .init       = xfrm_user_net_init,
+       .exit_batch = xfrm_user_net_exit,
 };
 
 static int __init xfrm_user_init(void)