diff options
author | David S. Miller <davem@davemloft.net> | 2014-09-13 16:38:53 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-09-13 16:38:53 -0400 |
commit | 4361eb664de09a9f60955c7f5e9355f7ed46a08d (patch) | |
tree | 1456a054df7e78b1e4f10fac2041b3673e2b35de | |
parent | 8801d48cbde6ddd275c1e6b866e434a72cafeabd (diff) | |
parent | 1691c63ea42d6f57ba769df401b9773664edb936 (diff) |
Merge branch 'ipv6-cleanups'
Cong Wang says:
====================
ipv6: clean up locking code in anycast and mcast
This patchset cleans up the locking code in anycast.c and mcast.c
and makes the refcount code more readable.
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
v1 -> v2:
* refactor some code and make it in a separated patch
* update comments
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/netdevice.h | 4 | ||||
-rw-r--r-- | include/net/addrconf.h | 2 | ||||
-rw-r--r-- | net/core/dev.c | 14 | ||||
-rw-r--r-- | net/ipv6/addrconf.c | 2 | ||||
-rw-r--r-- | net/ipv6/anycast.c | 108 | ||||
-rw-r--r-- | net/ipv6/mcast.c | 129 |
6 files changed, 123 insertions, 136 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ae721f53739e..ee38b948d9a0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -2083,8 +2083,8 @@ void __dev_remove_pack(struct packet_type *pt); | |||
2083 | void dev_add_offload(struct packet_offload *po); | 2083 | void dev_add_offload(struct packet_offload *po); |
2084 | void dev_remove_offload(struct packet_offload *po); | 2084 | void dev_remove_offload(struct packet_offload *po); |
2085 | 2085 | ||
2086 | struct net_device *dev_get_by_flags_rcu(struct net *net, unsigned short flags, | 2086 | struct net_device *__dev_get_by_flags(struct net *net, unsigned short flags, |
2087 | unsigned short mask); | 2087 | unsigned short mask); |
2088 | struct net_device *dev_get_by_name(struct net *net, const char *name); | 2088 | struct net_device *dev_get_by_name(struct net *net, const char *name); |
2089 | struct net_device *dev_get_by_name_rcu(struct net *net, const char *name); | 2089 | struct net_device *dev_get_by_name_rcu(struct net *net, const char *name); |
2090 | struct net_device *__dev_get_by_name(struct net *net, const char *name); | 2090 | struct net_device *__dev_get_by_name(struct net *net, const char *name); |
diff --git a/include/net/addrconf.h b/include/net/addrconf.h index f679877bb601..9b1d42e66cca 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h | |||
@@ -202,7 +202,7 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, | |||
202 | const struct in6_addr *addr); | 202 | const struct in6_addr *addr); |
203 | void ipv6_sock_ac_close(struct sock *sk); | 203 | void ipv6_sock_ac_close(struct sock *sk); |
204 | 204 | ||
205 | int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr); | 205 | int __ipv6_dev_ac_inc(struct inet6_dev *idev, const struct in6_addr *addr); |
206 | int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr); | 206 | int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr); |
207 | bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev, | 207 | bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev, |
208 | const struct in6_addr *addr); | 208 | const struct in6_addr *addr); |
diff --git a/net/core/dev.c b/net/core/dev.c index b3d6dbc0c696..e916ba8caccf 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -897,23 +897,25 @@ struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type) | |||
897 | EXPORT_SYMBOL(dev_getfirstbyhwtype); | 897 | EXPORT_SYMBOL(dev_getfirstbyhwtype); |
898 | 898 | ||
899 | /** | 899 | /** |
900 | * dev_get_by_flags_rcu - find any device with given flags | 900 | * __dev_get_by_flags - find any device with given flags |
901 | * @net: the applicable net namespace | 901 | * @net: the applicable net namespace |
902 | * @if_flags: IFF_* values | 902 | * @if_flags: IFF_* values |
903 | * @mask: bitmask of bits in if_flags to check | 903 | * @mask: bitmask of bits in if_flags to check |
904 | * | 904 | * |
905 | * Search for any interface with the given flags. Returns NULL if a device | 905 | * Search for any interface with the given flags. Returns NULL if a device |
906 | * is not found or a pointer to the device. Must be called inside | 906 | * is not found or a pointer to the device. Must be called inside |
907 | * rcu_read_lock(), and result refcount is unchanged. | 907 | * rtnl_lock(), and result refcount is unchanged. |
908 | */ | 908 | */ |
909 | 909 | ||
910 | struct net_device *dev_get_by_flags_rcu(struct net *net, unsigned short if_flags, | 910 | struct net_device *__dev_get_by_flags(struct net *net, unsigned short if_flags, |
911 | unsigned short mask) | 911 | unsigned short mask) |
912 | { | 912 | { |
913 | struct net_device *dev, *ret; | 913 | struct net_device *dev, *ret; |
914 | 914 | ||
915 | ASSERT_RTNL(); | ||
916 | |||
915 | ret = NULL; | 917 | ret = NULL; |
916 | for_each_netdev_rcu(net, dev) { | 918 | for_each_netdev(net, dev) { |
917 | if (((dev->flags ^ if_flags) & mask) == 0) { | 919 | if (((dev->flags ^ if_flags) & mask) == 0) { |
918 | ret = dev; | 920 | ret = dev; |
919 | break; | 921 | break; |
@@ -921,7 +923,7 @@ struct net_device *dev_get_by_flags_rcu(struct net *net, unsigned short if_flags | |||
921 | } | 923 | } |
922 | return ret; | 924 | return ret; |
923 | } | 925 | } |
924 | EXPORT_SYMBOL(dev_get_by_flags_rcu); | 926 | EXPORT_SYMBOL(__dev_get_by_flags); |
925 | 927 | ||
926 | /** | 928 | /** |
927 | * dev_valid_name - check if name is okay for network device | 929 | * dev_valid_name - check if name is okay for network device |
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index ad4598fcc416..6b6a373a30b2 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -1725,7 +1725,7 @@ static void addrconf_join_anycast(struct inet6_ifaddr *ifp) | |||
1725 | ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len); | 1725 | ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len); |
1726 | if (ipv6_addr_any(&addr)) | 1726 | if (ipv6_addr_any(&addr)) |
1727 | return; | 1727 | return; |
1728 | ipv6_dev_ac_inc(ifp->idev->dev, &addr); | 1728 | __ipv6_dev_ac_inc(ifp->idev, &addr); |
1729 | } | 1729 | } |
1730 | 1730 | ||
1731 | /* caller must hold RTNL */ | 1731 | /* caller must hold RTNL */ |
diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index ff2de7d9d8e6..952c1fd06150 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c | |||
@@ -46,10 +46,6 @@ | |||
46 | 46 | ||
47 | static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr); | 47 | static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr); |
48 | 48 | ||
49 | /* Big ac list lock for all the sockets */ | ||
50 | static DEFINE_SPINLOCK(ipv6_sk_ac_lock); | ||
51 | |||
52 | |||
53 | /* | 49 | /* |
54 | * socket join an anycast group | 50 | * socket join an anycast group |
55 | */ | 51 | */ |
@@ -78,7 +74,6 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
78 | pac->acl_addr = *addr; | 74 | pac->acl_addr = *addr; |
79 | 75 | ||
80 | rtnl_lock(); | 76 | rtnl_lock(); |
81 | rcu_read_lock(); | ||
82 | if (ifindex == 0) { | 77 | if (ifindex == 0) { |
83 | struct rt6_info *rt; | 78 | struct rt6_info *rt; |
84 | 79 | ||
@@ -91,11 +86,11 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
91 | goto error; | 86 | goto error; |
92 | } else { | 87 | } else { |
93 | /* router, no matching interface: just pick one */ | 88 | /* router, no matching interface: just pick one */ |
94 | dev = dev_get_by_flags_rcu(net, IFF_UP, | 89 | dev = __dev_get_by_flags(net, IFF_UP, |
95 | IFF_UP | IFF_LOOPBACK); | 90 | IFF_UP | IFF_LOOPBACK); |
96 | } | 91 | } |
97 | } else | 92 | } else |
98 | dev = dev_get_by_index_rcu(net, ifindex); | 93 | dev = __dev_get_by_index(net, ifindex); |
99 | 94 | ||
100 | if (dev == NULL) { | 95 | if (dev == NULL) { |
101 | err = -ENODEV; | 96 | err = -ENODEV; |
@@ -127,17 +122,14 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
127 | goto error; | 122 | goto error; |
128 | } | 123 | } |
129 | 124 | ||
130 | err = ipv6_dev_ac_inc(dev, addr); | 125 | err = __ipv6_dev_ac_inc(idev, addr); |
131 | if (!err) { | 126 | if (!err) { |
132 | spin_lock_bh(&ipv6_sk_ac_lock); | ||
133 | pac->acl_next = np->ipv6_ac_list; | 127 | pac->acl_next = np->ipv6_ac_list; |
134 | np->ipv6_ac_list = pac; | 128 | np->ipv6_ac_list = pac; |
135 | spin_unlock_bh(&ipv6_sk_ac_lock); | ||
136 | pac = NULL; | 129 | pac = NULL; |
137 | } | 130 | } |
138 | 131 | ||
139 | error: | 132 | error: |
140 | rcu_read_unlock(); | ||
141 | rtnl_unlock(); | 133 | rtnl_unlock(); |
142 | if (pac) | 134 | if (pac) |
143 | sock_kfree_s(sk, pac, sizeof(*pac)); | 135 | sock_kfree_s(sk, pac, sizeof(*pac)); |
@@ -154,7 +146,7 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
154 | struct ipv6_ac_socklist *pac, *prev_pac; | 146 | struct ipv6_ac_socklist *pac, *prev_pac; |
155 | struct net *net = sock_net(sk); | 147 | struct net *net = sock_net(sk); |
156 | 148 | ||
157 | spin_lock_bh(&ipv6_sk_ac_lock); | 149 | rtnl_lock(); |
158 | prev_pac = NULL; | 150 | prev_pac = NULL; |
159 | for (pac = np->ipv6_ac_list; pac; pac = pac->acl_next) { | 151 | for (pac = np->ipv6_ac_list; pac; pac = pac->acl_next) { |
160 | if ((ifindex == 0 || pac->acl_ifindex == ifindex) && | 152 | if ((ifindex == 0 || pac->acl_ifindex == ifindex) && |
@@ -163,7 +155,7 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
163 | prev_pac = pac; | 155 | prev_pac = pac; |
164 | } | 156 | } |
165 | if (!pac) { | 157 | if (!pac) { |
166 | spin_unlock_bh(&ipv6_sk_ac_lock); | 158 | rtnl_unlock(); |
167 | return -ENOENT; | 159 | return -ENOENT; |
168 | } | 160 | } |
169 | if (prev_pac) | 161 | if (prev_pac) |
@@ -171,14 +163,9 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
171 | else | 163 | else |
172 | np->ipv6_ac_list = pac->acl_next; | 164 | np->ipv6_ac_list = pac->acl_next; |
173 | 165 | ||
174 | spin_unlock_bh(&ipv6_sk_ac_lock); | 166 | dev = __dev_get_by_index(net, pac->acl_ifindex); |
175 | |||
176 | rtnl_lock(); | ||
177 | rcu_read_lock(); | ||
178 | dev = dev_get_by_index_rcu(net, pac->acl_ifindex); | ||
179 | if (dev) | 167 | if (dev) |
180 | ipv6_dev_ac_dec(dev, &pac->acl_addr); | 168 | ipv6_dev_ac_dec(dev, &pac->acl_addr); |
181 | rcu_read_unlock(); | ||
182 | rtnl_unlock(); | 169 | rtnl_unlock(); |
183 | 170 | ||
184 | sock_kfree_s(sk, pac, sizeof(*pac)); | 171 | sock_kfree_s(sk, pac, sizeof(*pac)); |
@@ -196,19 +183,16 @@ void ipv6_sock_ac_close(struct sock *sk) | |||
196 | if (!np->ipv6_ac_list) | 183 | if (!np->ipv6_ac_list) |
197 | return; | 184 | return; |
198 | 185 | ||
199 | spin_lock_bh(&ipv6_sk_ac_lock); | 186 | rtnl_lock(); |
200 | pac = np->ipv6_ac_list; | 187 | pac = np->ipv6_ac_list; |
201 | np->ipv6_ac_list = NULL; | 188 | np->ipv6_ac_list = NULL; |
202 | spin_unlock_bh(&ipv6_sk_ac_lock); | ||
203 | 189 | ||
204 | prev_index = 0; | 190 | prev_index = 0; |
205 | rtnl_lock(); | ||
206 | rcu_read_lock(); | ||
207 | while (pac) { | 191 | while (pac) { |
208 | struct ipv6_ac_socklist *next = pac->acl_next; | 192 | struct ipv6_ac_socklist *next = pac->acl_next; |
209 | 193 | ||
210 | if (pac->acl_ifindex != prev_index) { | 194 | if (pac->acl_ifindex != prev_index) { |
211 | dev = dev_get_by_index_rcu(net, pac->acl_ifindex); | 195 | dev = __dev_get_by_index(net, pac->acl_ifindex); |
212 | prev_index = pac->acl_ifindex; | 196 | prev_index = pac->acl_ifindex; |
213 | } | 197 | } |
214 | if (dev) | 198 | if (dev) |
@@ -216,10 +200,14 @@ void ipv6_sock_ac_close(struct sock *sk) | |||
216 | sock_kfree_s(sk, pac, sizeof(*pac)); | 200 | sock_kfree_s(sk, pac, sizeof(*pac)); |
217 | pac = next; | 201 | pac = next; |
218 | } | 202 | } |
219 | rcu_read_unlock(); | ||
220 | rtnl_unlock(); | 203 | rtnl_unlock(); |
221 | } | 204 | } |
222 | 205 | ||
206 | static void aca_get(struct ifacaddr6 *aca) | ||
207 | { | ||
208 | atomic_inc(&aca->aca_refcnt); | ||
209 | } | ||
210 | |||
223 | static void aca_put(struct ifacaddr6 *ac) | 211 | static void aca_put(struct ifacaddr6 *ac) |
224 | { | 212 | { |
225 | if (atomic_dec_and_test(&ac->aca_refcnt)) { | 213 | if (atomic_dec_and_test(&ac->aca_refcnt)) { |
@@ -229,23 +217,40 @@ static void aca_put(struct ifacaddr6 *ac) | |||
229 | } | 217 | } |
230 | } | 218 | } |
231 | 219 | ||
220 | static struct ifacaddr6 *aca_alloc(struct rt6_info *rt, | ||
221 | const struct in6_addr *addr) | ||
222 | { | ||
223 | struct inet6_dev *idev = rt->rt6i_idev; | ||
224 | struct ifacaddr6 *aca; | ||
225 | |||
226 | aca = kzalloc(sizeof(*aca), GFP_ATOMIC); | ||
227 | if (aca == NULL) | ||
228 | return NULL; | ||
229 | |||
230 | aca->aca_addr = *addr; | ||
231 | in6_dev_hold(idev); | ||
232 | aca->aca_idev = idev; | ||
233 | aca->aca_rt = rt; | ||
234 | aca->aca_users = 1; | ||
235 | /* aca_tstamp should be updated upon changes */ | ||
236 | aca->aca_cstamp = aca->aca_tstamp = jiffies; | ||
237 | atomic_set(&aca->aca_refcnt, 1); | ||
238 | spin_lock_init(&aca->aca_lock); | ||
239 | |||
240 | return aca; | ||
241 | } | ||
242 | |||
232 | /* | 243 | /* |
233 | * device anycast group inc (add if not found) | 244 | * device anycast group inc (add if not found) |
234 | */ | 245 | */ |
235 | int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr) | 246 | int __ipv6_dev_ac_inc(struct inet6_dev *idev, const struct in6_addr *addr) |
236 | { | 247 | { |
237 | struct ifacaddr6 *aca; | 248 | struct ifacaddr6 *aca; |
238 | struct inet6_dev *idev; | ||
239 | struct rt6_info *rt; | 249 | struct rt6_info *rt; |
240 | int err; | 250 | int err; |
241 | 251 | ||
242 | ASSERT_RTNL(); | 252 | ASSERT_RTNL(); |
243 | 253 | ||
244 | idev = in6_dev_get(dev); | ||
245 | |||
246 | if (idev == NULL) | ||
247 | return -EINVAL; | ||
248 | |||
249 | write_lock_bh(&idev->lock); | 254 | write_lock_bh(&idev->lock); |
250 | if (idev->dead) { | 255 | if (idev->dead) { |
251 | err = -ENODEV; | 256 | err = -ENODEV; |
@@ -260,46 +265,35 @@ int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr) | |||
260 | } | 265 | } |
261 | } | 266 | } |
262 | 267 | ||
263 | /* | ||
264 | * not found: create a new one. | ||
265 | */ | ||
266 | |||
267 | aca = kzalloc(sizeof(struct ifacaddr6), GFP_ATOMIC); | ||
268 | |||
269 | if (aca == NULL) { | ||
270 | err = -ENOMEM; | ||
271 | goto out; | ||
272 | } | ||
273 | |||
274 | rt = addrconf_dst_alloc(idev, addr, true); | 268 | rt = addrconf_dst_alloc(idev, addr, true); |
275 | if (IS_ERR(rt)) { | 269 | if (IS_ERR(rt)) { |
276 | kfree(aca); | ||
277 | err = PTR_ERR(rt); | 270 | err = PTR_ERR(rt); |
278 | goto out; | 271 | goto out; |
279 | } | 272 | } |
280 | 273 | aca = aca_alloc(rt, addr); | |
281 | aca->aca_addr = *addr; | 274 | if (aca == NULL) { |
282 | aca->aca_idev = idev; | 275 | ip6_rt_put(rt); |
283 | aca->aca_rt = rt; | 276 | err = -ENOMEM; |
284 | aca->aca_users = 1; | 277 | goto out; |
285 | /* aca_tstamp should be updated upon changes */ | 278 | } |
286 | aca->aca_cstamp = aca->aca_tstamp = jiffies; | ||
287 | atomic_set(&aca->aca_refcnt, 2); | ||
288 | spin_lock_init(&aca->aca_lock); | ||
289 | 279 | ||
290 | aca->aca_next = idev->ac_list; | 280 | aca->aca_next = idev->ac_list; |
291 | idev->ac_list = aca; | 281 | idev->ac_list = aca; |
282 | |||
283 | /* Hold this for addrconf_join_solict() below before we unlock, | ||
284 | * it is already exposed via idev->ac_list. | ||
285 | */ | ||
286 | aca_get(aca); | ||
292 | write_unlock_bh(&idev->lock); | 287 | write_unlock_bh(&idev->lock); |
293 | 288 | ||
294 | ip6_ins_rt(rt); | 289 | ip6_ins_rt(rt); |
295 | 290 | ||
296 | addrconf_join_solict(dev, &aca->aca_addr); | 291 | addrconf_join_solict(idev->dev, &aca->aca_addr); |
297 | 292 | ||
298 | aca_put(aca); | 293 | aca_put(aca); |
299 | return 0; | 294 | return 0; |
300 | out: | 295 | out: |
301 | write_unlock_bh(&idev->lock); | 296 | write_unlock_bh(&idev->lock); |
302 | in6_dev_put(idev); | ||
303 | return err; | 297 | return err; |
304 | } | 298 | } |
305 | 299 | ||
@@ -341,7 +335,7 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr) | |||
341 | return 0; | 335 | return 0; |
342 | } | 336 | } |
343 | 337 | ||
344 | /* called with rcu_read_lock() */ | 338 | /* called with rtnl_lock() */ |
345 | static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr) | 339 | static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr) |
346 | { | 340 | { |
347 | struct inet6_dev *idev = __in6_dev_get(dev); | 341 | struct inet6_dev *idev = __in6_dev_get(dev); |
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 484a94215d88..592eba61e78a 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c | |||
@@ -73,9 +73,6 @@ static void *__mld2_query_bugs[] __attribute__((__unused__)) = { | |||
73 | 73 | ||
74 | static struct in6_addr mld2_all_mcr = MLD2_ALL_MCR_INIT; | 74 | static struct in6_addr mld2_all_mcr = MLD2_ALL_MCR_INIT; |
75 | 75 | ||
76 | /* Big mc list lock for all the sockets */ | ||
77 | static DEFINE_SPINLOCK(ipv6_sk_mc_lock); | ||
78 | |||
79 | static void igmp6_join_group(struct ifmcaddr6 *ma); | 76 | static void igmp6_join_group(struct ifmcaddr6 *ma); |
80 | static void igmp6_leave_group(struct ifmcaddr6 *ma); | 77 | static void igmp6_leave_group(struct ifmcaddr6 *ma); |
81 | static void igmp6_timer_handler(unsigned long data); | 78 | static void igmp6_timer_handler(unsigned long data); |
@@ -165,7 +162,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
165 | mc_lst->addr = *addr; | 162 | mc_lst->addr = *addr; |
166 | 163 | ||
167 | rtnl_lock(); | 164 | rtnl_lock(); |
168 | rcu_read_lock(); | ||
169 | if (ifindex == 0) { | 165 | if (ifindex == 0) { |
170 | struct rt6_info *rt; | 166 | struct rt6_info *rt; |
171 | rt = rt6_lookup(net, addr, NULL, 0, 0); | 167 | rt = rt6_lookup(net, addr, NULL, 0, 0); |
@@ -174,10 +170,9 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
174 | ip6_rt_put(rt); | 170 | ip6_rt_put(rt); |
175 | } | 171 | } |
176 | } else | 172 | } else |
177 | dev = dev_get_by_index_rcu(net, ifindex); | 173 | dev = __dev_get_by_index(net, ifindex); |
178 | 174 | ||
179 | if (dev == NULL) { | 175 | if (dev == NULL) { |
180 | rcu_read_unlock(); | ||
181 | rtnl_unlock(); | 176 | rtnl_unlock(); |
182 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); | 177 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); |
183 | return -ENODEV; | 178 | return -ENODEV; |
@@ -195,18 +190,14 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
195 | err = ipv6_dev_mc_inc(dev, addr); | 190 | err = ipv6_dev_mc_inc(dev, addr); |
196 | 191 | ||
197 | if (err) { | 192 | if (err) { |
198 | rcu_read_unlock(); | ||
199 | rtnl_unlock(); | 193 | rtnl_unlock(); |
200 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); | 194 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); |
201 | return err; | 195 | return err; |
202 | } | 196 | } |
203 | 197 | ||
204 | spin_lock(&ipv6_sk_mc_lock); | ||
205 | mc_lst->next = np->ipv6_mc_list; | 198 | mc_lst->next = np->ipv6_mc_list; |
206 | rcu_assign_pointer(np->ipv6_mc_list, mc_lst); | 199 | rcu_assign_pointer(np->ipv6_mc_list, mc_lst); |
207 | spin_unlock(&ipv6_sk_mc_lock); | ||
208 | 200 | ||
209 | rcu_read_unlock(); | ||
210 | rtnl_unlock(); | 201 | rtnl_unlock(); |
211 | 202 | ||
212 | return 0; | 203 | return 0; |
@@ -226,20 +217,16 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
226 | return -EINVAL; | 217 | return -EINVAL; |
227 | 218 | ||
228 | rtnl_lock(); | 219 | rtnl_lock(); |
229 | spin_lock(&ipv6_sk_mc_lock); | ||
230 | for (lnk = &np->ipv6_mc_list; | 220 | for (lnk = &np->ipv6_mc_list; |
231 | (mc_lst = rcu_dereference_protected(*lnk, | 221 | (mc_lst = rtnl_dereference(*lnk)) != NULL; |
232 | lockdep_is_held(&ipv6_sk_mc_lock))) != NULL; | ||
233 | lnk = &mc_lst->next) { | 222 | lnk = &mc_lst->next) { |
234 | if ((ifindex == 0 || mc_lst->ifindex == ifindex) && | 223 | if ((ifindex == 0 || mc_lst->ifindex == ifindex) && |
235 | ipv6_addr_equal(&mc_lst->addr, addr)) { | 224 | ipv6_addr_equal(&mc_lst->addr, addr)) { |
236 | struct net_device *dev; | 225 | struct net_device *dev; |
237 | 226 | ||
238 | *lnk = mc_lst->next; | 227 | *lnk = mc_lst->next; |
239 | spin_unlock(&ipv6_sk_mc_lock); | ||
240 | 228 | ||
241 | rcu_read_lock(); | 229 | dev = __dev_get_by_index(net, mc_lst->ifindex); |
242 | dev = dev_get_by_index_rcu(net, mc_lst->ifindex); | ||
243 | if (dev != NULL) { | 230 | if (dev != NULL) { |
244 | struct inet6_dev *idev = __in6_dev_get(dev); | 231 | struct inet6_dev *idev = __in6_dev_get(dev); |
245 | 232 | ||
@@ -248,7 +235,6 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
248 | __ipv6_dev_mc_dec(idev, &mc_lst->addr); | 235 | __ipv6_dev_mc_dec(idev, &mc_lst->addr); |
249 | } else | 236 | } else |
250 | (void) ip6_mc_leave_src(sk, mc_lst, NULL); | 237 | (void) ip6_mc_leave_src(sk, mc_lst, NULL); |
251 | rcu_read_unlock(); | ||
252 | rtnl_unlock(); | 238 | rtnl_unlock(); |
253 | 239 | ||
254 | atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); | 240 | atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); |
@@ -256,7 +242,6 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
256 | return 0; | 242 | return 0; |
257 | } | 243 | } |
258 | } | 244 | } |
259 | spin_unlock(&ipv6_sk_mc_lock); | ||
260 | rtnl_unlock(); | 245 | rtnl_unlock(); |
261 | 246 | ||
262 | return -EADDRNOTAVAIL; | 247 | return -EADDRNOTAVAIL; |
@@ -303,16 +288,12 @@ void ipv6_sock_mc_close(struct sock *sk) | |||
303 | return; | 288 | return; |
304 | 289 | ||
305 | rtnl_lock(); | 290 | rtnl_lock(); |
306 | spin_lock(&ipv6_sk_mc_lock); | 291 | while ((mc_lst = rtnl_dereference(np->ipv6_mc_list)) != NULL) { |
307 | while ((mc_lst = rcu_dereference_protected(np->ipv6_mc_list, | ||
308 | lockdep_is_held(&ipv6_sk_mc_lock))) != NULL) { | ||
309 | struct net_device *dev; | 292 | struct net_device *dev; |
310 | 293 | ||
311 | np->ipv6_mc_list = mc_lst->next; | 294 | np->ipv6_mc_list = mc_lst->next; |
312 | spin_unlock(&ipv6_sk_mc_lock); | ||
313 | 295 | ||
314 | rcu_read_lock(); | 296 | dev = __dev_get_by_index(net, mc_lst->ifindex); |
315 | dev = dev_get_by_index_rcu(net, mc_lst->ifindex); | ||
316 | if (dev) { | 297 | if (dev) { |
317 | struct inet6_dev *idev = __in6_dev_get(dev); | 298 | struct inet6_dev *idev = __in6_dev_get(dev); |
318 | 299 | ||
@@ -321,14 +302,11 @@ void ipv6_sock_mc_close(struct sock *sk) | |||
321 | __ipv6_dev_mc_dec(idev, &mc_lst->addr); | 302 | __ipv6_dev_mc_dec(idev, &mc_lst->addr); |
322 | } else | 303 | } else |
323 | (void) ip6_mc_leave_src(sk, mc_lst, NULL); | 304 | (void) ip6_mc_leave_src(sk, mc_lst, NULL); |
324 | rcu_read_unlock(); | ||
325 | 305 | ||
326 | atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); | 306 | atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); |
327 | kfree_rcu(mc_lst, rcu); | 307 | kfree_rcu(mc_lst, rcu); |
328 | 308 | ||
329 | spin_lock(&ipv6_sk_mc_lock); | ||
330 | } | 309 | } |
331 | spin_unlock(&ipv6_sk_mc_lock); | ||
332 | rtnl_unlock(); | 310 | rtnl_unlock(); |
333 | } | 311 | } |
334 | 312 | ||
@@ -578,9 +556,8 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, | |||
578 | } | 556 | } |
579 | 557 | ||
580 | err = -EADDRNOTAVAIL; | 558 | err = -EADDRNOTAVAIL; |
581 | /* | 559 | /* changes to the ipv6_mc_list require the socket lock and |
582 | * changes to the ipv6_mc_list require the socket lock and | 560 | * rtnl lock. We have the socket lock and rcu read lock, |
583 | * a read lock on ip6_sk_mc_lock. We have the socket lock, | ||
584 | * so reading the list is safe. | 561 | * so reading the list is safe. |
585 | */ | 562 | */ |
586 | 563 | ||
@@ -604,9 +581,8 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, | |||
604 | copy_to_user(optval, gsf, GROUP_FILTER_SIZE(0))) { | 581 | copy_to_user(optval, gsf, GROUP_FILTER_SIZE(0))) { |
605 | return -EFAULT; | 582 | return -EFAULT; |
606 | } | 583 | } |
607 | /* changes to psl require the socket lock, a read lock on | 584 | /* changes to psl require the socket lock, and a write lock |
608 | * on ipv6_sk_mc_lock and a write lock on pmc->sflock. We | 585 | * on pmc->sflock. We have the socket lock so reading here is safe. |
609 | * have the socket lock, so reading here is safe. | ||
610 | */ | 586 | */ |
611 | for (i = 0; i < copycount; i++) { | 587 | for (i = 0; i < copycount; i++) { |
612 | struct sockaddr_in6 *psin6; | 588 | struct sockaddr_in6 *psin6; |
@@ -665,14 +641,6 @@ bool inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr, | |||
665 | return rv; | 641 | return rv; |
666 | } | 642 | } |
667 | 643 | ||
668 | static void ma_put(struct ifmcaddr6 *mc) | ||
669 | { | ||
670 | if (atomic_dec_and_test(&mc->mca_refcnt)) { | ||
671 | in6_dev_put(mc->idev); | ||
672 | kfree(mc); | ||
673 | } | ||
674 | } | ||
675 | |||
676 | static void igmp6_group_added(struct ifmcaddr6 *mc) | 644 | static void igmp6_group_added(struct ifmcaddr6 *mc) |
677 | { | 645 | { |
678 | struct net_device *dev = mc->idev->dev; | 646 | struct net_device *dev = mc->idev->dev; |
@@ -838,6 +806,48 @@ static void mld_clear_delrec(struct inet6_dev *idev) | |||
838 | read_unlock_bh(&idev->lock); | 806 | read_unlock_bh(&idev->lock); |
839 | } | 807 | } |
840 | 808 | ||
809 | static void mca_get(struct ifmcaddr6 *mc) | ||
810 | { | ||
811 | atomic_inc(&mc->mca_refcnt); | ||
812 | } | ||
813 | |||
814 | static void ma_put(struct ifmcaddr6 *mc) | ||
815 | { | ||
816 | if (atomic_dec_and_test(&mc->mca_refcnt)) { | ||
817 | in6_dev_put(mc->idev); | ||
818 | kfree(mc); | ||
819 | } | ||
820 | } | ||
821 | |||
822 | static struct ifmcaddr6 *mca_alloc(struct inet6_dev *idev, | ||
823 | const struct in6_addr *addr) | ||
824 | { | ||
825 | struct ifmcaddr6 *mc; | ||
826 | |||
827 | mc = kzalloc(sizeof(*mc), GFP_ATOMIC); | ||
828 | if (mc == NULL) | ||
829 | return NULL; | ||
830 | |||
831 | setup_timer(&mc->mca_timer, igmp6_timer_handler, (unsigned long)mc); | ||
832 | |||
833 | mc->mca_addr = *addr; | ||
834 | mc->idev = idev; /* reference taken by caller */ | ||
835 | mc->mca_users = 1; | ||
836 | /* mca_stamp should be updated upon changes */ | ||
837 | mc->mca_cstamp = mc->mca_tstamp = jiffies; | ||
838 | atomic_set(&mc->mca_refcnt, 1); | ||
839 | spin_lock_init(&mc->mca_lock); | ||
840 | |||
841 | /* initial mode is (EX, empty) */ | ||
842 | mc->mca_sfmode = MCAST_EXCLUDE; | ||
843 | mc->mca_sfcount[MCAST_EXCLUDE] = 1; | ||
844 | |||
845 | if (ipv6_addr_is_ll_all_nodes(&mc->mca_addr) || | ||
846 | IPV6_ADDR_MC_SCOPE(&mc->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) | ||
847 | mc->mca_flags |= MAF_NOREPORT; | ||
848 | |||
849 | return mc; | ||
850 | } | ||
841 | 851 | ||
842 | /* | 852 | /* |
843 | * device multicast group inc (add if not found) | 853 | * device multicast group inc (add if not found) |
@@ -873,38 +883,20 @@ int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr) | |||
873 | } | 883 | } |
874 | } | 884 | } |
875 | 885 | ||
876 | /* | 886 | mc = mca_alloc(idev, addr); |
877 | * not found: create a new one. | 887 | if (!mc) { |
878 | */ | ||
879 | |||
880 | mc = kzalloc(sizeof(struct ifmcaddr6), GFP_ATOMIC); | ||
881 | |||
882 | if (mc == NULL) { | ||
883 | write_unlock_bh(&idev->lock); | 888 | write_unlock_bh(&idev->lock); |
884 | in6_dev_put(idev); | 889 | in6_dev_put(idev); |
885 | return -ENOMEM; | 890 | return -ENOMEM; |
886 | } | 891 | } |
887 | 892 | ||
888 | setup_timer(&mc->mca_timer, igmp6_timer_handler, (unsigned long)mc); | ||
889 | |||
890 | mc->mca_addr = *addr; | ||
891 | mc->idev = idev; /* (reference taken) */ | ||
892 | mc->mca_users = 1; | ||
893 | /* mca_stamp should be updated upon changes */ | ||
894 | mc->mca_cstamp = mc->mca_tstamp = jiffies; | ||
895 | atomic_set(&mc->mca_refcnt, 2); | ||
896 | spin_lock_init(&mc->mca_lock); | ||
897 | |||
898 | /* initial mode is (EX, empty) */ | ||
899 | mc->mca_sfmode = MCAST_EXCLUDE; | ||
900 | mc->mca_sfcount[MCAST_EXCLUDE] = 1; | ||
901 | |||
902 | if (ipv6_addr_is_ll_all_nodes(&mc->mca_addr) || | ||
903 | IPV6_ADDR_MC_SCOPE(&mc->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) | ||
904 | mc->mca_flags |= MAF_NOREPORT; | ||
905 | |||
906 | mc->next = idev->mc_list; | 893 | mc->next = idev->mc_list; |
907 | idev->mc_list = mc; | 894 | idev->mc_list = mc; |
895 | |||
896 | /* Hold this for the code below before we unlock, | ||
897 | * it is already exposed via idev->mc_list. | ||
898 | */ | ||
899 | mca_get(mc); | ||
908 | write_unlock_bh(&idev->lock); | 900 | write_unlock_bh(&idev->lock); |
909 | 901 | ||
910 | mld_del_delrec(idev, &mc->mca_addr); | 902 | mld_del_delrec(idev, &mc->mca_addr); |
@@ -948,7 +940,7 @@ int ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr) | |||
948 | struct inet6_dev *idev; | 940 | struct inet6_dev *idev; |
949 | int err; | 941 | int err; |
950 | 942 | ||
951 | rcu_read_lock(); | 943 | ASSERT_RTNL(); |
952 | 944 | ||
953 | idev = __in6_dev_get(dev); | 945 | idev = __in6_dev_get(dev); |
954 | if (!idev) | 946 | if (!idev) |
@@ -956,7 +948,6 @@ int ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr) | |||
956 | else | 948 | else |
957 | err = __ipv6_dev_mc_dec(idev, addr); | 949 | err = __ipv6_dev_mc_dec(idev, addr); |
958 | 950 | ||
959 | rcu_read_unlock(); | ||
960 | return err; | 951 | return err; |
961 | } | 952 | } |
962 | 953 | ||
@@ -2373,7 +2364,7 @@ static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml, | |||
2373 | { | 2364 | { |
2374 | int err; | 2365 | int err; |
2375 | 2366 | ||
2376 | /* callers have the socket lock and a write lock on ipv6_sk_mc_lock, | 2367 | /* callers have the socket lock and rtnl lock |
2377 | * so no other readers or writers of iml or its sflist | 2368 | * so no other readers or writers of iml or its sflist |
2378 | */ | 2369 | */ |
2379 | if (!iml->sflist) { | 2370 | if (!iml->sflist) { |