diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-02-04 19:50:26 -0500 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-02-04 22:07:35 -0500 |
commit | c14b78e7decd0d1d5add6a4604feb8609fe920a9 (patch) | |
tree | 820b4a0a2f3702581452e05d53d44097cb240d81 | |
parent | 5474f57f7d686ac918355419cb71496f835aaf5d (diff) |
netfilter: nfnetlink: add mutex per subsystem
This patch replaces the global lock to one lock per subsystem.
The per-subsystem lock avoids that processes operating
with different subsystems are synchronized.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r-- | include/linux/netfilter/nfnetlink.h | 4 | ||||
-rw-r--r-- | net/netfilter/ipset/ip_set_core.c | 26 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_netlink.c | 12 | ||||
-rw-r--r-- | net/netfilter/nfnetlink.c | 52 |
4 files changed, 53 insertions, 41 deletions
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index 4966ddec039b..ecbb8e495912 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h | |||
@@ -34,8 +34,8 @@ extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigne | |||
34 | extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error); | 34 | extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error); |
35 | extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags); | 35 | extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags); |
36 | 36 | ||
37 | extern void nfnl_lock(void); | 37 | extern void nfnl_lock(__u8 subsys_id); |
38 | extern void nfnl_unlock(void); | 38 | extern void nfnl_unlock(__u8 subsys_id); |
39 | 39 | ||
40 | #define MODULE_ALIAS_NFNL_SUBSYS(subsys) \ | 40 | #define MODULE_ALIAS_NFNL_SUBSYS(subsys) \ |
41 | MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys)) | 41 | MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys)) |
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 6d6d8f2b033e..f82b2e606cfd 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c | |||
@@ -88,14 +88,14 @@ find_set_type(const char *name, u8 family, u8 revision) | |||
88 | static bool | 88 | static bool |
89 | load_settype(const char *name) | 89 | load_settype(const char *name) |
90 | { | 90 | { |
91 | nfnl_unlock(); | 91 | nfnl_unlock(NFNL_SUBSYS_IPSET); |
92 | pr_debug("try to load ip_set_%s\n", name); | 92 | pr_debug("try to load ip_set_%s\n", name); |
93 | if (request_module("ip_set_%s", name) < 0) { | 93 | if (request_module("ip_set_%s", name) < 0) { |
94 | pr_warning("Can't find ip_set type %s\n", name); | 94 | pr_warning("Can't find ip_set type %s\n", name); |
95 | nfnl_lock(); | 95 | nfnl_lock(NFNL_SUBSYS_IPSET); |
96 | return false; | 96 | return false; |
97 | } | 97 | } |
98 | nfnl_lock(); | 98 | nfnl_lock(NFNL_SUBSYS_IPSET); |
99 | return true; | 99 | return true; |
100 | } | 100 | } |
101 | 101 | ||
@@ -532,7 +532,7 @@ ip_set_nfnl_get(const char *name) | |||
532 | ip_set_id_t i, index = IPSET_INVALID_ID; | 532 | ip_set_id_t i, index = IPSET_INVALID_ID; |
533 | struct ip_set *s; | 533 | struct ip_set *s; |
534 | 534 | ||
535 | nfnl_lock(); | 535 | nfnl_lock(NFNL_SUBSYS_IPSET); |
536 | for (i = 0; i < ip_set_max; i++) { | 536 | for (i = 0; i < ip_set_max; i++) { |
537 | s = nfnl_set(i); | 537 | s = nfnl_set(i); |
538 | if (s != NULL && STREQ(s->name, name)) { | 538 | if (s != NULL && STREQ(s->name, name)) { |
@@ -541,7 +541,7 @@ ip_set_nfnl_get(const char *name) | |||
541 | break; | 541 | break; |
542 | } | 542 | } |
543 | } | 543 | } |
544 | nfnl_unlock(); | 544 | nfnl_unlock(NFNL_SUBSYS_IPSET); |
545 | 545 | ||
546 | return index; | 546 | return index; |
547 | } | 547 | } |
@@ -561,13 +561,13 @@ ip_set_nfnl_get_byindex(ip_set_id_t index) | |||
561 | if (index > ip_set_max) | 561 | if (index > ip_set_max) |
562 | return IPSET_INVALID_ID; | 562 | return IPSET_INVALID_ID; |
563 | 563 | ||
564 | nfnl_lock(); | 564 | nfnl_lock(NFNL_SUBSYS_IPSET); |
565 | set = nfnl_set(index); | 565 | set = nfnl_set(index); |
566 | if (set) | 566 | if (set) |
567 | __ip_set_get(set); | 567 | __ip_set_get(set); |
568 | else | 568 | else |
569 | index = IPSET_INVALID_ID; | 569 | index = IPSET_INVALID_ID; |
570 | nfnl_unlock(); | 570 | nfnl_unlock(NFNL_SUBSYS_IPSET); |
571 | 571 | ||
572 | return index; | 572 | return index; |
573 | } | 573 | } |
@@ -584,11 +584,11 @@ void | |||
584 | ip_set_nfnl_put(ip_set_id_t index) | 584 | ip_set_nfnl_put(ip_set_id_t index) |
585 | { | 585 | { |
586 | struct ip_set *set; | 586 | struct ip_set *set; |
587 | nfnl_lock(); | 587 | nfnl_lock(NFNL_SUBSYS_IPSET); |
588 | set = nfnl_set(index); | 588 | set = nfnl_set(index); |
589 | if (set != NULL) | 589 | if (set != NULL) |
590 | __ip_set_put(set); | 590 | __ip_set_put(set); |
591 | nfnl_unlock(); | 591 | nfnl_unlock(NFNL_SUBSYS_IPSET); |
592 | } | 592 | } |
593 | EXPORT_SYMBOL_GPL(ip_set_nfnl_put); | 593 | EXPORT_SYMBOL_GPL(ip_set_nfnl_put); |
594 | 594 | ||
@@ -1763,10 +1763,10 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) | |||
1763 | goto done; | 1763 | goto done; |
1764 | } | 1764 | } |
1765 | req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; | 1765 | req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; |
1766 | nfnl_lock(); | 1766 | nfnl_lock(NFNL_SUBSYS_IPSET); |
1767 | find_set_and_id(req_get->set.name, &id); | 1767 | find_set_and_id(req_get->set.name, &id); |
1768 | req_get->set.index = id; | 1768 | req_get->set.index = id; |
1769 | nfnl_unlock(); | 1769 | nfnl_unlock(NFNL_SUBSYS_IPSET); |
1770 | goto copy; | 1770 | goto copy; |
1771 | } | 1771 | } |
1772 | case IP_SET_OP_GET_BYINDEX: { | 1772 | case IP_SET_OP_GET_BYINDEX: { |
@@ -1778,11 +1778,11 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) | |||
1778 | ret = -EINVAL; | 1778 | ret = -EINVAL; |
1779 | goto done; | 1779 | goto done; |
1780 | } | 1780 | } |
1781 | nfnl_lock(); | 1781 | nfnl_lock(NFNL_SUBSYS_IPSET); |
1782 | set = nfnl_set(req_get->set.index); | 1782 | set = nfnl_set(req_get->set.index); |
1783 | strncpy(req_get->set.name, set ? set->name : "", | 1783 | strncpy(req_get->set.name, set ? set->name : "", |
1784 | IPSET_MAXNAMELEN); | 1784 | IPSET_MAXNAMELEN); |
1785 | nfnl_unlock(); | 1785 | nfnl_unlock(NFNL_SUBSYS_IPSET); |
1786 | goto copy; | 1786 | goto copy; |
1787 | } | 1787 | } |
1788 | default: | 1788 | default: |
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 2334cc5d2b16..d490a300ce2b 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c | |||
@@ -1256,13 +1256,13 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct, | |||
1256 | if (!parse_nat_setup) { | 1256 | if (!parse_nat_setup) { |
1257 | #ifdef CONFIG_MODULES | 1257 | #ifdef CONFIG_MODULES |
1258 | rcu_read_unlock(); | 1258 | rcu_read_unlock(); |
1259 | nfnl_unlock(); | 1259 | nfnl_unlock(NFNL_SUBSYS_CTNETLINK); |
1260 | if (request_module("nf-nat") < 0) { | 1260 | if (request_module("nf-nat") < 0) { |
1261 | nfnl_lock(); | 1261 | nfnl_lock(NFNL_SUBSYS_CTNETLINK); |
1262 | rcu_read_lock(); | 1262 | rcu_read_lock(); |
1263 | return -EOPNOTSUPP; | 1263 | return -EOPNOTSUPP; |
1264 | } | 1264 | } |
1265 | nfnl_lock(); | 1265 | nfnl_lock(NFNL_SUBSYS_CTNETLINK); |
1266 | rcu_read_lock(); | 1266 | rcu_read_lock(); |
1267 | if (nfnetlink_parse_nat_setup_hook) | 1267 | if (nfnetlink_parse_nat_setup_hook) |
1268 | return -EAGAIN; | 1268 | return -EAGAIN; |
@@ -1274,13 +1274,13 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct, | |||
1274 | if (err == -EAGAIN) { | 1274 | if (err == -EAGAIN) { |
1275 | #ifdef CONFIG_MODULES | 1275 | #ifdef CONFIG_MODULES |
1276 | rcu_read_unlock(); | 1276 | rcu_read_unlock(); |
1277 | nfnl_unlock(); | 1277 | nfnl_unlock(NFNL_SUBSYS_CTNETLINK); |
1278 | if (request_module("nf-nat-%u", nf_ct_l3num(ct)) < 0) { | 1278 | if (request_module("nf-nat-%u", nf_ct_l3num(ct)) < 0) { |
1279 | nfnl_lock(); | 1279 | nfnl_lock(NFNL_SUBSYS_CTNETLINK); |
1280 | rcu_read_lock(); | 1280 | rcu_read_lock(); |
1281 | return -EOPNOTSUPP; | 1281 | return -EOPNOTSUPP; |
1282 | } | 1282 | } |
1283 | nfnl_lock(); | 1283 | nfnl_lock(NFNL_SUBSYS_CTNETLINK); |
1284 | rcu_read_lock(); | 1284 | rcu_read_lock(); |
1285 | #else | 1285 | #else |
1286 | err = -EOPNOTSUPP; | 1286 | err = -EOPNOTSUPP; |
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 58a09b7c3f6d..d578ec251712 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c | |||
@@ -36,8 +36,10 @@ MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER); | |||
36 | 36 | ||
37 | static char __initdata nfversion[] = "0.30"; | 37 | static char __initdata nfversion[] = "0.30"; |
38 | 38 | ||
39 | static const struct nfnetlink_subsystem __rcu *subsys_table[NFNL_SUBSYS_COUNT]; | 39 | static struct { |
40 | static DEFINE_MUTEX(nfnl_mutex); | 40 | struct mutex mutex; |
41 | const struct nfnetlink_subsystem __rcu *subsys; | ||
42 | } table[NFNL_SUBSYS_COUNT]; | ||
41 | 43 | ||
42 | static const int nfnl_group2type[NFNLGRP_MAX+1] = { | 44 | static const int nfnl_group2type[NFNLGRP_MAX+1] = { |
43 | [NFNLGRP_CONNTRACK_NEW] = NFNL_SUBSYS_CTNETLINK, | 45 | [NFNLGRP_CONNTRACK_NEW] = NFNL_SUBSYS_CTNETLINK, |
@@ -48,27 +50,32 @@ static const int nfnl_group2type[NFNLGRP_MAX+1] = { | |||
48 | [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP, | 50 | [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP, |
49 | }; | 51 | }; |
50 | 52 | ||
51 | void nfnl_lock(void) | 53 | void nfnl_lock(__u8 subsys_id) |
52 | { | 54 | { |
53 | mutex_lock(&nfnl_mutex); | 55 | mutex_lock(&table[subsys_id].mutex); |
54 | } | 56 | } |
55 | EXPORT_SYMBOL_GPL(nfnl_lock); | 57 | EXPORT_SYMBOL_GPL(nfnl_lock); |
56 | 58 | ||
57 | void nfnl_unlock(void) | 59 | void nfnl_unlock(__u8 subsys_id) |
58 | { | 60 | { |
59 | mutex_unlock(&nfnl_mutex); | 61 | mutex_unlock(&table[subsys_id].mutex); |
60 | } | 62 | } |
61 | EXPORT_SYMBOL_GPL(nfnl_unlock); | 63 | EXPORT_SYMBOL_GPL(nfnl_unlock); |
62 | 64 | ||
65 | static struct mutex *nfnl_get_lock(__u8 subsys_id) | ||
66 | { | ||
67 | return &table[subsys_id].mutex; | ||
68 | } | ||
69 | |||
63 | int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n) | 70 | int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n) |
64 | { | 71 | { |
65 | nfnl_lock(); | 72 | nfnl_lock(n->subsys_id); |
66 | if (subsys_table[n->subsys_id]) { | 73 | if (table[n->subsys_id].subsys) { |
67 | nfnl_unlock(); | 74 | nfnl_unlock(n->subsys_id); |
68 | return -EBUSY; | 75 | return -EBUSY; |
69 | } | 76 | } |
70 | rcu_assign_pointer(subsys_table[n->subsys_id], n); | 77 | rcu_assign_pointer(table[n->subsys_id].subsys, n); |
71 | nfnl_unlock(); | 78 | nfnl_unlock(n->subsys_id); |
72 | 79 | ||
73 | return 0; | 80 | return 0; |
74 | } | 81 | } |
@@ -76,9 +83,9 @@ EXPORT_SYMBOL_GPL(nfnetlink_subsys_register); | |||
76 | 83 | ||
77 | int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n) | 84 | int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n) |
78 | { | 85 | { |
79 | nfnl_lock(); | 86 | nfnl_lock(n->subsys_id); |
80 | subsys_table[n->subsys_id] = NULL; | 87 | table[n->subsys_id].subsys = NULL; |
81 | nfnl_unlock(); | 88 | nfnl_unlock(n->subsys_id); |
82 | synchronize_rcu(); | 89 | synchronize_rcu(); |
83 | return 0; | 90 | return 0; |
84 | } | 91 | } |
@@ -91,7 +98,7 @@ static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t t | |||
91 | if (subsys_id >= NFNL_SUBSYS_COUNT) | 98 | if (subsys_id >= NFNL_SUBSYS_COUNT) |
92 | return NULL; | 99 | return NULL; |
93 | 100 | ||
94 | return rcu_dereference(subsys_table[subsys_id]); | 101 | return rcu_dereference(table[subsys_id].subsys); |
95 | } | 102 | } |
96 | 103 | ||
97 | static inline const struct nfnl_callback * | 104 | static inline const struct nfnl_callback * |
@@ -175,6 +182,7 @@ replay: | |||
175 | struct nlattr *cda[ss->cb[cb_id].attr_count + 1]; | 182 | struct nlattr *cda[ss->cb[cb_id].attr_count + 1]; |
176 | struct nlattr *attr = (void *)nlh + min_len; | 183 | struct nlattr *attr = (void *)nlh + min_len; |
177 | int attrlen = nlh->nlmsg_len - min_len; | 184 | int attrlen = nlh->nlmsg_len - min_len; |
185 | __u8 subsys_id = NFNL_SUBSYS_ID(type); | ||
178 | 186 | ||
179 | err = nla_parse(cda, ss->cb[cb_id].attr_count, | 187 | err = nla_parse(cda, ss->cb[cb_id].attr_count, |
180 | attr, attrlen, ss->cb[cb_id].policy); | 188 | attr, attrlen, ss->cb[cb_id].policy); |
@@ -189,10 +197,9 @@ replay: | |||
189 | rcu_read_unlock(); | 197 | rcu_read_unlock(); |
190 | } else { | 198 | } else { |
191 | rcu_read_unlock(); | 199 | rcu_read_unlock(); |
192 | nfnl_lock(); | 200 | nfnl_lock(subsys_id); |
193 | if (rcu_dereference_protected( | 201 | if (rcu_dereference_protected(table[subsys_id].subsys, |
194 | subsys_table[NFNL_SUBSYS_ID(type)], | 202 | lockdep_is_held(nfnl_get_lock(subsys_id))) != ss || |
195 | lockdep_is_held(&nfnl_mutex)) != ss || | ||
196 | nfnetlink_find_client(type, ss) != nc) | 203 | nfnetlink_find_client(type, ss) != nc) |
197 | err = -EAGAIN; | 204 | err = -EAGAIN; |
198 | else if (nc->call) | 205 | else if (nc->call) |
@@ -200,7 +207,7 @@ replay: | |||
200 | (const struct nlattr **)cda); | 207 | (const struct nlattr **)cda); |
201 | else | 208 | else |
202 | err = -EINVAL; | 209 | err = -EINVAL; |
203 | nfnl_unlock(); | 210 | nfnl_unlock(subsys_id); |
204 | } | 211 | } |
205 | if (err == -EAGAIN) | 212 | if (err == -EAGAIN) |
206 | goto replay; | 213 | goto replay; |
@@ -267,6 +274,11 @@ static struct pernet_operations nfnetlink_net_ops = { | |||
267 | 274 | ||
268 | static int __init nfnetlink_init(void) | 275 | static int __init nfnetlink_init(void) |
269 | { | 276 | { |
277 | int i; | ||
278 | |||
279 | for (i=0; i<NFNL_SUBSYS_COUNT; i++) | ||
280 | mutex_init(&table[i].mutex); | ||
281 | |||
270 | pr_info("Netfilter messages via NETLINK v%s.\n", nfversion); | 282 | pr_info("Netfilter messages via NETLINK v%s.\n", nfversion); |
271 | return register_pernet_subsys(&nfnetlink_net_ops); | 283 | return register_pernet_subsys(&nfnetlink_net_ops); |
272 | } | 284 | } |