diff options
Diffstat (limited to 'net/ipv4/igmp.c')
-rw-r--r-- | net/ipv4/igmp.c | 140 |
1 files changed, 90 insertions, 50 deletions
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index d41e5de79a82..15d3eeda92f5 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c | |||
@@ -71,6 +71,7 @@ | |||
71 | */ | 71 | */ |
72 | 72 | ||
73 | #include <linux/module.h> | 73 | #include <linux/module.h> |
74 | #include <linux/slab.h> | ||
74 | #include <asm/uaccess.h> | 75 | #include <asm/uaccess.h> |
75 | #include <asm/system.h> | 76 | #include <asm/system.h> |
76 | #include <linux/types.h> | 77 | #include <linux/types.h> |
@@ -946,7 +947,6 @@ int igmp_rcv(struct sk_buff *skb) | |||
946 | break; | 947 | break; |
947 | case IGMP_HOST_MEMBERSHIP_REPORT: | 948 | case IGMP_HOST_MEMBERSHIP_REPORT: |
948 | case IGMPV2_HOST_MEMBERSHIP_REPORT: | 949 | case IGMPV2_HOST_MEMBERSHIP_REPORT: |
949 | case IGMPV3_HOST_MEMBERSHIP_REPORT: | ||
950 | /* Is it our report looped back? */ | 950 | /* Is it our report looped back? */ |
951 | if (skb_rtable(skb)->fl.iif == 0) | 951 | if (skb_rtable(skb)->fl.iif == 0) |
952 | break; | 952 | break; |
@@ -960,6 +960,7 @@ int igmp_rcv(struct sk_buff *skb) | |||
960 | in_dev_put(in_dev); | 960 | in_dev_put(in_dev); |
961 | return pim_rcv_v1(skb); | 961 | return pim_rcv_v1(skb); |
962 | #endif | 962 | #endif |
963 | case IGMPV3_HOST_MEMBERSHIP_REPORT: | ||
963 | case IGMP_DVMRP: | 964 | case IGMP_DVMRP: |
964 | case IGMP_TRACE: | 965 | case IGMP_TRACE: |
965 | case IGMP_HOST_LEAVE_MESSAGE: | 966 | case IGMP_HOST_LEAVE_MESSAGE: |
@@ -1799,7 +1800,7 @@ int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) | |||
1799 | iml->next = inet->mc_list; | 1800 | iml->next = inet->mc_list; |
1800 | iml->sflist = NULL; | 1801 | iml->sflist = NULL; |
1801 | iml->sfmode = MCAST_EXCLUDE; | 1802 | iml->sfmode = MCAST_EXCLUDE; |
1802 | inet->mc_list = iml; | 1803 | rcu_assign_pointer(inet->mc_list, iml); |
1803 | ip_mc_inc_group(in_dev, addr); | 1804 | ip_mc_inc_group(in_dev, addr); |
1804 | err = 0; | 1805 | err = 0; |
1805 | done: | 1806 | done: |
@@ -1807,24 +1808,46 @@ done: | |||
1807 | return err; | 1808 | return err; |
1808 | } | 1809 | } |
1809 | 1810 | ||
1811 | static void ip_sf_socklist_reclaim(struct rcu_head *rp) | ||
1812 | { | ||
1813 | struct ip_sf_socklist *psf; | ||
1814 | |||
1815 | psf = container_of(rp, struct ip_sf_socklist, rcu); | ||
1816 | /* sk_omem_alloc should have been decreased by the caller*/ | ||
1817 | kfree(psf); | ||
1818 | } | ||
1819 | |||
1810 | static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, | 1820 | static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml, |
1811 | struct in_device *in_dev) | 1821 | struct in_device *in_dev) |
1812 | { | 1822 | { |
1823 | struct ip_sf_socklist *psf = iml->sflist; | ||
1813 | int err; | 1824 | int err; |
1814 | 1825 | ||
1815 | if (iml->sflist == NULL) { | 1826 | if (psf == NULL) { |
1816 | /* any-source empty exclude case */ | 1827 | /* any-source empty exclude case */ |
1817 | return ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, | 1828 | return ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, |
1818 | iml->sfmode, 0, NULL, 0); | 1829 | iml->sfmode, 0, NULL, 0); |
1819 | } | 1830 | } |
1820 | err = ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, | 1831 | err = ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr, |
1821 | iml->sfmode, iml->sflist->sl_count, | 1832 | iml->sfmode, psf->sl_count, psf->sl_addr, 0); |
1822 | iml->sflist->sl_addr, 0); | 1833 | rcu_assign_pointer(iml->sflist, NULL); |
1823 | sock_kfree_s(sk, iml->sflist, IP_SFLSIZE(iml->sflist->sl_max)); | 1834 | /* decrease mem now to avoid the memleak warning */ |
1824 | iml->sflist = NULL; | 1835 | atomic_sub(IP_SFLSIZE(psf->sl_max), &sk->sk_omem_alloc); |
1836 | call_rcu(&psf->rcu, ip_sf_socklist_reclaim); | ||
1825 | return err; | 1837 | return err; |
1826 | } | 1838 | } |
1827 | 1839 | ||
1840 | |||
1841 | static void ip_mc_socklist_reclaim(struct rcu_head *rp) | ||
1842 | { | ||
1843 | struct ip_mc_socklist *iml; | ||
1844 | |||
1845 | iml = container_of(rp, struct ip_mc_socklist, rcu); | ||
1846 | /* sk_omem_alloc should have been decreased by the caller*/ | ||
1847 | kfree(iml); | ||
1848 | } | ||
1849 | |||
1850 | |||
1828 | /* | 1851 | /* |
1829 | * Ask a socket to leave a group. | 1852 | * Ask a socket to leave a group. |
1830 | */ | 1853 | */ |
@@ -1854,12 +1877,14 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) | |||
1854 | 1877 | ||
1855 | (void) ip_mc_leave_src(sk, iml, in_dev); | 1878 | (void) ip_mc_leave_src(sk, iml, in_dev); |
1856 | 1879 | ||
1857 | *imlp = iml->next; | 1880 | rcu_assign_pointer(*imlp, iml->next); |
1858 | 1881 | ||
1859 | if (in_dev) | 1882 | if (in_dev) |
1860 | ip_mc_dec_group(in_dev, group); | 1883 | ip_mc_dec_group(in_dev, group); |
1861 | rtnl_unlock(); | 1884 | rtnl_unlock(); |
1862 | sock_kfree_s(sk, iml, sizeof(*iml)); | 1885 | /* decrease mem now to avoid the memleak warning */ |
1886 | atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); | ||
1887 | call_rcu(&iml->rcu, ip_mc_socklist_reclaim); | ||
1863 | return 0; | 1888 | return 0; |
1864 | } | 1889 | } |
1865 | if (!in_dev) | 1890 | if (!in_dev) |
@@ -1899,8 +1924,9 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct | |||
1899 | err = -EADDRNOTAVAIL; | 1924 | err = -EADDRNOTAVAIL; |
1900 | 1925 | ||
1901 | for (pmc=inet->mc_list; pmc; pmc=pmc->next) { | 1926 | for (pmc=inet->mc_list; pmc; pmc=pmc->next) { |
1902 | if (pmc->multi.imr_multiaddr.s_addr == imr.imr_multiaddr.s_addr | 1927 | if ((pmc->multi.imr_multiaddr.s_addr == |
1903 | && pmc->multi.imr_ifindex == imr.imr_ifindex) | 1928 | imr.imr_multiaddr.s_addr) && |
1929 | (pmc->multi.imr_ifindex == imr.imr_ifindex)) | ||
1904 | break; | 1930 | break; |
1905 | } | 1931 | } |
1906 | if (!pmc) { /* must have a prior join */ | 1932 | if (!pmc) { /* must have a prior join */ |
@@ -1973,9 +1999,12 @@ int ip_mc_source(int add, int omode, struct sock *sk, struct | |||
1973 | if (psl) { | 1999 | if (psl) { |
1974 | for (i=0; i<psl->sl_count; i++) | 2000 | for (i=0; i<psl->sl_count; i++) |
1975 | newpsl->sl_addr[i] = psl->sl_addr[i]; | 2001 | newpsl->sl_addr[i] = psl->sl_addr[i]; |
1976 | sock_kfree_s(sk, psl, IP_SFLSIZE(psl->sl_max)); | 2002 | /* decrease mem now to avoid the memleak warning */ |
2003 | atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); | ||
2004 | call_rcu(&psl->rcu, ip_sf_socklist_reclaim); | ||
1977 | } | 2005 | } |
1978 | pmc->sflist = psl = newpsl; | 2006 | rcu_assign_pointer(pmc->sflist, newpsl); |
2007 | psl = newpsl; | ||
1979 | } | 2008 | } |
1980 | rv = 1; /* > 0 for insert logic below if sl_count is 0 */ | 2009 | rv = 1; /* > 0 for insert logic below if sl_count is 0 */ |
1981 | for (i=0; i<psl->sl_count; i++) { | 2010 | for (i=0; i<psl->sl_count; i++) { |
@@ -2071,11 +2100,13 @@ int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf, int ifindex) | |||
2071 | if (psl) { | 2100 | if (psl) { |
2072 | (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, | 2101 | (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, |
2073 | psl->sl_count, psl->sl_addr, 0); | 2102 | psl->sl_count, psl->sl_addr, 0); |
2074 | sock_kfree_s(sk, psl, IP_SFLSIZE(psl->sl_max)); | 2103 | /* decrease mem now to avoid the memleak warning */ |
2104 | atomic_sub(IP_SFLSIZE(psl->sl_max), &sk->sk_omem_alloc); | ||
2105 | call_rcu(&psl->rcu, ip_sf_socklist_reclaim); | ||
2075 | } else | 2106 | } else |
2076 | (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, | 2107 | (void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode, |
2077 | 0, NULL, 0); | 2108 | 0, NULL, 0); |
2078 | pmc->sflist = newpsl; | 2109 | rcu_assign_pointer(pmc->sflist, newpsl); |
2079 | pmc->sfmode = msf->imsf_fmode; | 2110 | pmc->sfmode = msf->imsf_fmode; |
2080 | err = 0; | 2111 | err = 0; |
2081 | done: | 2112 | done: |
@@ -2208,30 +2239,40 @@ int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif) | |||
2208 | struct ip_mc_socklist *pmc; | 2239 | struct ip_mc_socklist *pmc; |
2209 | struct ip_sf_socklist *psl; | 2240 | struct ip_sf_socklist *psl; |
2210 | int i; | 2241 | int i; |
2242 | int ret; | ||
2211 | 2243 | ||
2244 | ret = 1; | ||
2212 | if (!ipv4_is_multicast(loc_addr)) | 2245 | if (!ipv4_is_multicast(loc_addr)) |
2213 | return 1; | 2246 | goto out; |
2214 | 2247 | ||
2215 | for (pmc=inet->mc_list; pmc; pmc=pmc->next) { | 2248 | rcu_read_lock(); |
2249 | for (pmc=rcu_dereference(inet->mc_list); pmc; pmc=rcu_dereference(pmc->next)) { | ||
2216 | if (pmc->multi.imr_multiaddr.s_addr == loc_addr && | 2250 | if (pmc->multi.imr_multiaddr.s_addr == loc_addr && |
2217 | pmc->multi.imr_ifindex == dif) | 2251 | pmc->multi.imr_ifindex == dif) |
2218 | break; | 2252 | break; |
2219 | } | 2253 | } |
2254 | ret = inet->mc_all; | ||
2220 | if (!pmc) | 2255 | if (!pmc) |
2221 | return inet->mc_all; | 2256 | goto unlock; |
2222 | psl = pmc->sflist; | 2257 | psl = pmc->sflist; |
2258 | ret = (pmc->sfmode == MCAST_EXCLUDE); | ||
2223 | if (!psl) | 2259 | if (!psl) |
2224 | return pmc->sfmode == MCAST_EXCLUDE; | 2260 | goto unlock; |
2225 | 2261 | ||
2226 | for (i=0; i<psl->sl_count; i++) { | 2262 | for (i=0; i<psl->sl_count; i++) { |
2227 | if (psl->sl_addr[i] == rmt_addr) | 2263 | if (psl->sl_addr[i] == rmt_addr) |
2228 | break; | 2264 | break; |
2229 | } | 2265 | } |
2266 | ret = 0; | ||
2230 | if (pmc->sfmode == MCAST_INCLUDE && i >= psl->sl_count) | 2267 | if (pmc->sfmode == MCAST_INCLUDE && i >= psl->sl_count) |
2231 | return 0; | 2268 | goto unlock; |
2232 | if (pmc->sfmode == MCAST_EXCLUDE && i < psl->sl_count) | 2269 | if (pmc->sfmode == MCAST_EXCLUDE && i < psl->sl_count) |
2233 | return 0; | 2270 | goto unlock; |
2234 | return 1; | 2271 | ret = 1; |
2272 | unlock: | ||
2273 | rcu_read_unlock(); | ||
2274 | out: | ||
2275 | return ret; | ||
2235 | } | 2276 | } |
2236 | 2277 | ||
2237 | /* | 2278 | /* |
@@ -2250,7 +2291,7 @@ void ip_mc_drop_socket(struct sock *sk) | |||
2250 | rtnl_lock(); | 2291 | rtnl_lock(); |
2251 | while ((iml = inet->mc_list) != NULL) { | 2292 | while ((iml = inet->mc_list) != NULL) { |
2252 | struct in_device *in_dev; | 2293 | struct in_device *in_dev; |
2253 | inet->mc_list = iml->next; | 2294 | rcu_assign_pointer(inet->mc_list, iml->next); |
2254 | 2295 | ||
2255 | in_dev = inetdev_by_index(net, iml->multi.imr_ifindex); | 2296 | in_dev = inetdev_by_index(net, iml->multi.imr_ifindex); |
2256 | (void) ip_mc_leave_src(sk, iml, in_dev); | 2297 | (void) ip_mc_leave_src(sk, iml, in_dev); |
@@ -2258,7 +2299,9 @@ void ip_mc_drop_socket(struct sock *sk) | |||
2258 | ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr); | 2299 | ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr); |
2259 | in_dev_put(in_dev); | 2300 | in_dev_put(in_dev); |
2260 | } | 2301 | } |
2261 | sock_kfree_s(sk, iml, sizeof(*iml)); | 2302 | /* decrease mem now to avoid the memleak warning */ |
2303 | atomic_sub(sizeof(*iml), &sk->sk_omem_alloc); | ||
2304 | call_rcu(&iml->rcu, ip_mc_socklist_reclaim); | ||
2262 | } | 2305 | } |
2263 | rtnl_unlock(); | 2306 | rtnl_unlock(); |
2264 | } | 2307 | } |
@@ -2311,9 +2354,10 @@ static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq) | |||
2311 | struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); | 2354 | struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); |
2312 | 2355 | ||
2313 | state->in_dev = NULL; | 2356 | state->in_dev = NULL; |
2314 | for_each_netdev(net, state->dev) { | 2357 | for_each_netdev_rcu(net, state->dev) { |
2315 | struct in_device *in_dev; | 2358 | struct in_device *in_dev; |
2316 | in_dev = in_dev_get(state->dev); | 2359 | |
2360 | in_dev = __in_dev_get_rcu(state->dev); | ||
2317 | if (!in_dev) | 2361 | if (!in_dev) |
2318 | continue; | 2362 | continue; |
2319 | read_lock(&in_dev->mc_list_lock); | 2363 | read_lock(&in_dev->mc_list_lock); |
@@ -2323,7 +2367,6 @@ static inline struct ip_mc_list *igmp_mc_get_first(struct seq_file *seq) | |||
2323 | break; | 2367 | break; |
2324 | } | 2368 | } |
2325 | read_unlock(&in_dev->mc_list_lock); | 2369 | read_unlock(&in_dev->mc_list_lock); |
2326 | in_dev_put(in_dev); | ||
2327 | } | 2370 | } |
2328 | return im; | 2371 | return im; |
2329 | } | 2372 | } |
@@ -2333,16 +2376,15 @@ static struct ip_mc_list *igmp_mc_get_next(struct seq_file *seq, struct ip_mc_li | |||
2333 | struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); | 2376 | struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); |
2334 | im = im->next; | 2377 | im = im->next; |
2335 | while (!im) { | 2378 | while (!im) { |
2336 | if (likely(state->in_dev != NULL)) { | 2379 | if (likely(state->in_dev != NULL)) |
2337 | read_unlock(&state->in_dev->mc_list_lock); | 2380 | read_unlock(&state->in_dev->mc_list_lock); |
2338 | in_dev_put(state->in_dev); | 2381 | |
2339 | } | 2382 | state->dev = next_net_device_rcu(state->dev); |
2340 | state->dev = next_net_device(state->dev); | ||
2341 | if (!state->dev) { | 2383 | if (!state->dev) { |
2342 | state->in_dev = NULL; | 2384 | state->in_dev = NULL; |
2343 | break; | 2385 | break; |
2344 | } | 2386 | } |
2345 | state->in_dev = in_dev_get(state->dev); | 2387 | state->in_dev = __in_dev_get_rcu(state->dev); |
2346 | if (!state->in_dev) | 2388 | if (!state->in_dev) |
2347 | continue; | 2389 | continue; |
2348 | read_lock(&state->in_dev->mc_list_lock); | 2390 | read_lock(&state->in_dev->mc_list_lock); |
@@ -2361,9 +2403,9 @@ static struct ip_mc_list *igmp_mc_get_idx(struct seq_file *seq, loff_t pos) | |||
2361 | } | 2403 | } |
2362 | 2404 | ||
2363 | static void *igmp_mc_seq_start(struct seq_file *seq, loff_t *pos) | 2405 | static void *igmp_mc_seq_start(struct seq_file *seq, loff_t *pos) |
2364 | __acquires(dev_base_lock) | 2406 | __acquires(rcu) |
2365 | { | 2407 | { |
2366 | read_lock(&dev_base_lock); | 2408 | rcu_read_lock(); |
2367 | return *pos ? igmp_mc_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; | 2409 | return *pos ? igmp_mc_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; |
2368 | } | 2410 | } |
2369 | 2411 | ||
@@ -2379,16 +2421,15 @@ static void *igmp_mc_seq_next(struct seq_file *seq, void *v, loff_t *pos) | |||
2379 | } | 2421 | } |
2380 | 2422 | ||
2381 | static void igmp_mc_seq_stop(struct seq_file *seq, void *v) | 2423 | static void igmp_mc_seq_stop(struct seq_file *seq, void *v) |
2382 | __releases(dev_base_lock) | 2424 | __releases(rcu) |
2383 | { | 2425 | { |
2384 | struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); | 2426 | struct igmp_mc_iter_state *state = igmp_mc_seq_private(seq); |
2385 | if (likely(state->in_dev != NULL)) { | 2427 | if (likely(state->in_dev != NULL)) { |
2386 | read_unlock(&state->in_dev->mc_list_lock); | 2428 | read_unlock(&state->in_dev->mc_list_lock); |
2387 | in_dev_put(state->in_dev); | ||
2388 | state->in_dev = NULL; | 2429 | state->in_dev = NULL; |
2389 | } | 2430 | } |
2390 | state->dev = NULL; | 2431 | state->dev = NULL; |
2391 | read_unlock(&dev_base_lock); | 2432 | rcu_read_unlock(); |
2392 | } | 2433 | } |
2393 | 2434 | ||
2394 | static int igmp_mc_seq_show(struct seq_file *seq, void *v) | 2435 | static int igmp_mc_seq_show(struct seq_file *seq, void *v) |
@@ -2462,9 +2503,9 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq) | |||
2462 | 2503 | ||
2463 | state->idev = NULL; | 2504 | state->idev = NULL; |
2464 | state->im = NULL; | 2505 | state->im = NULL; |
2465 | for_each_netdev(net, state->dev) { | 2506 | for_each_netdev_rcu(net, state->dev) { |
2466 | struct in_device *idev; | 2507 | struct in_device *idev; |
2467 | idev = in_dev_get(state->dev); | 2508 | idev = __in_dev_get_rcu(state->dev); |
2468 | if (unlikely(idev == NULL)) | 2509 | if (unlikely(idev == NULL)) |
2469 | continue; | 2510 | continue; |
2470 | read_lock(&idev->mc_list_lock); | 2511 | read_lock(&idev->mc_list_lock); |
@@ -2480,7 +2521,6 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq) | |||
2480 | spin_unlock_bh(&im->lock); | 2521 | spin_unlock_bh(&im->lock); |
2481 | } | 2522 | } |
2482 | read_unlock(&idev->mc_list_lock); | 2523 | read_unlock(&idev->mc_list_lock); |
2483 | in_dev_put(idev); | ||
2484 | } | 2524 | } |
2485 | return psf; | 2525 | return psf; |
2486 | } | 2526 | } |
@@ -2494,16 +2534,15 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l | |||
2494 | spin_unlock_bh(&state->im->lock); | 2534 | spin_unlock_bh(&state->im->lock); |
2495 | state->im = state->im->next; | 2535 | state->im = state->im->next; |
2496 | while (!state->im) { | 2536 | while (!state->im) { |
2497 | if (likely(state->idev != NULL)) { | 2537 | if (likely(state->idev != NULL)) |
2498 | read_unlock(&state->idev->mc_list_lock); | 2538 | read_unlock(&state->idev->mc_list_lock); |
2499 | in_dev_put(state->idev); | 2539 | |
2500 | } | 2540 | state->dev = next_net_device_rcu(state->dev); |
2501 | state->dev = next_net_device(state->dev); | ||
2502 | if (!state->dev) { | 2541 | if (!state->dev) { |
2503 | state->idev = NULL; | 2542 | state->idev = NULL; |
2504 | goto out; | 2543 | goto out; |
2505 | } | 2544 | } |
2506 | state->idev = in_dev_get(state->dev); | 2545 | state->idev = __in_dev_get_rcu(state->dev); |
2507 | if (!state->idev) | 2546 | if (!state->idev) |
2508 | continue; | 2547 | continue; |
2509 | read_lock(&state->idev->mc_list_lock); | 2548 | read_lock(&state->idev->mc_list_lock); |
@@ -2528,8 +2567,9 @@ static struct ip_sf_list *igmp_mcf_get_idx(struct seq_file *seq, loff_t pos) | |||
2528 | } | 2567 | } |
2529 | 2568 | ||
2530 | static void *igmp_mcf_seq_start(struct seq_file *seq, loff_t *pos) | 2569 | static void *igmp_mcf_seq_start(struct seq_file *seq, loff_t *pos) |
2570 | __acquires(rcu) | ||
2531 | { | 2571 | { |
2532 | read_lock(&dev_base_lock); | 2572 | rcu_read_lock(); |
2533 | return *pos ? igmp_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; | 2573 | return *pos ? igmp_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; |
2534 | } | 2574 | } |
2535 | 2575 | ||
@@ -2545,6 +2585,7 @@ static void *igmp_mcf_seq_next(struct seq_file *seq, void *v, loff_t *pos) | |||
2545 | } | 2585 | } |
2546 | 2586 | ||
2547 | static void igmp_mcf_seq_stop(struct seq_file *seq, void *v) | 2587 | static void igmp_mcf_seq_stop(struct seq_file *seq, void *v) |
2588 | __releases(rcu) | ||
2548 | { | 2589 | { |
2549 | struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); | 2590 | struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq); |
2550 | if (likely(state->im != NULL)) { | 2591 | if (likely(state->im != NULL)) { |
@@ -2553,11 +2594,10 @@ static void igmp_mcf_seq_stop(struct seq_file *seq, void *v) | |||
2553 | } | 2594 | } |
2554 | if (likely(state->idev != NULL)) { | 2595 | if (likely(state->idev != NULL)) { |
2555 | read_unlock(&state->idev->mc_list_lock); | 2596 | read_unlock(&state->idev->mc_list_lock); |
2556 | in_dev_put(state->idev); | ||
2557 | state->idev = NULL; | 2597 | state->idev = NULL; |
2558 | } | 2598 | } |
2559 | state->dev = NULL; | 2599 | state->dev = NULL; |
2560 | read_unlock(&dev_base_lock); | 2600 | rcu_read_unlock(); |
2561 | } | 2601 | } |
2562 | 2602 | ||
2563 | static int igmp_mcf_seq_show(struct seq_file *seq, void *v) | 2603 | static int igmp_mcf_seq_show(struct seq_file *seq, void *v) |
@@ -2605,7 +2645,7 @@ static const struct file_operations igmp_mcf_seq_fops = { | |||
2605 | .release = seq_release_net, | 2645 | .release = seq_release_net, |
2606 | }; | 2646 | }; |
2607 | 2647 | ||
2608 | static int igmp_net_init(struct net *net) | 2648 | static int __net_init igmp_net_init(struct net *net) |
2609 | { | 2649 | { |
2610 | struct proc_dir_entry *pde; | 2650 | struct proc_dir_entry *pde; |
2611 | 2651 | ||
@@ -2623,7 +2663,7 @@ out_igmp: | |||
2623 | return -ENOMEM; | 2663 | return -ENOMEM; |
2624 | } | 2664 | } |
2625 | 2665 | ||
2626 | static void igmp_net_exit(struct net *net) | 2666 | static void __net_exit igmp_net_exit(struct net *net) |
2627 | { | 2667 | { |
2628 | proc_net_remove(net, "mcfilter"); | 2668 | proc_net_remove(net, "mcfilter"); |
2629 | proc_net_remove(net, "igmp"); | 2669 | proc_net_remove(net, "igmp"); |