int (*call)(struct sock *nl, struct sk_buff *skb, 
                    const struct nlmsghdr *nlh,
                    const struct nlattr * const cda[]);
+       int (*call_rcu)(struct sock *nl, struct sk_buff *skb, 
+                   const struct nlmsghdr *nlh,
+                   const struct nlattr * const cda[]);
        const struct nla_policy *policy;        /* netlink attribute policy */
        const u_int16_t attr_count;             /* number of nlattr's */
 };
 
 
 static char __initdata nfversion[] = "0.30";
 
-static const struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT];
+static const struct nfnetlink_subsystem __rcu *subsys_table[NFNL_SUBSYS_COUNT];
 static DEFINE_MUTEX(nfnl_mutex);
 
 void nfnl_lock(void)
                nfnl_unlock();
                return -EBUSY;
        }
-       subsys_table[n->subsys_id] = n;
+       rcu_assign_pointer(subsys_table[n->subsys_id], n);
        nfnl_unlock();
 
        return 0;
        nfnl_lock();
        subsys_table[n->subsys_id] = NULL;
        nfnl_unlock();
-
+       synchronize_rcu();
        return 0;
 }
 EXPORT_SYMBOL_GPL(nfnetlink_subsys_unregister);
        if (subsys_id >= NFNL_SUBSYS_COUNT)
                return NULL;
 
-       return subsys_table[subsys_id];
+       return rcu_dereference(subsys_table[subsys_id]);
 }
 
 static inline const struct nfnl_callback *
 
        type = nlh->nlmsg_type;
 replay:
+       rcu_read_lock();
        ss = nfnetlink_get_subsys(type);
        if (!ss) {
 #ifdef CONFIG_MODULES
-               nfnl_unlock();
+               rcu_read_unlock();
                request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type));
-               nfnl_lock();
+               rcu_read_lock();
                ss = nfnetlink_get_subsys(type);
                if (!ss)
 #endif
+               {
+                       rcu_read_unlock();
                        return -EINVAL;
+               }
        }
 
        nc = nfnetlink_find_client(type, ss);
-       if (!nc)
+       if (!nc) {
+               rcu_read_unlock();
                return -EINVAL;
+       }
 
        {
                int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
                if (err < 0)
                        return err;
 
-               err = nc->call(net->nfnl, skb, nlh, (const struct nlattr **)cda);
+               if (nc->call_rcu) {
+                       err = nc->call_rcu(net->nfnl, skb, nlh,
+                                          (const struct nlattr **)cda);
+                       rcu_read_unlock();
+               } else {
+                       rcu_read_unlock();
+                       nfnl_lock();
+                       if (rcu_dereference_protected(
+                                       subsys_table[NFNL_SUBSYS_ID(type)],
+                                       lockdep_is_held(&nfnl_mutex)) != ss ||
+                           nfnetlink_find_client(type, ss) != nc)
+                               err = -EAGAIN;
+                       else
+                               err = nc->call(net->nfnl, skb, nlh,
+                                                  (const struct nlattr **)cda);
+                       nfnl_unlock();
+               }
                if (err == -EAGAIN)
                        goto replay;
                return err;
 
 static void nfnetlink_rcv(struct sk_buff *skb)
 {
-       nfnl_lock();
        netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
-       nfnl_unlock();
 }
 
 static int __net_init nfnetlink_net_init(struct net *net)