diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2010-06-02 15:21:31 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-06-03 06:18:19 -0400 |
commit | 96d362202bfc0e5da78ee59b1645296fbca515f4 (patch) | |
tree | ce3cc34d9a75f8c5d91ea9eb711993657d32af77 | |
parent | b5f7e7554753e2cc3ef3bef0271fdb32027df2ba (diff) |
ipv4: RCU conversion of ip_route_input_slow/ip_route_input_mc
Avoid two atomic ops on struct in_device refcount per incoming packet,
if slow path taken, (or route cache disabled)
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv4/route.c | 35 |
1 files changed, 17 insertions, 18 deletions
diff --git a/net/ipv4/route.c b/net/ipv4/route.c index d377b45005fc..1cfe0d199e7c 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c | |||
@@ -1843,13 +1843,14 @@ static void rt_set_nexthop(struct rtable *rt, struct fib_result *res, u32 itag) | |||
1843 | rt->rt_type = res->type; | 1843 | rt->rt_type = res->type; |
1844 | } | 1844 | } |
1845 | 1845 | ||
1846 | /* called in rcu_read_lock() section */ | ||
1846 | static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, | 1847 | static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, |
1847 | u8 tos, struct net_device *dev, int our) | 1848 | u8 tos, struct net_device *dev, int our) |
1848 | { | 1849 | { |
1849 | unsigned hash; | 1850 | unsigned int hash; |
1850 | struct rtable *rth; | 1851 | struct rtable *rth; |
1851 | __be32 spec_dst; | 1852 | __be32 spec_dst; |
1852 | struct in_device *in_dev = in_dev_get(dev); | 1853 | struct in_device *in_dev = __in_dev_get_rcu(dev); |
1853 | u32 itag = 0; | 1854 | u32 itag = 0; |
1854 | int err; | 1855 | int err; |
1855 | 1856 | ||
@@ -1914,18 +1915,14 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, | |||
1914 | #endif | 1915 | #endif |
1915 | RT_CACHE_STAT_INC(in_slow_mc); | 1916 | RT_CACHE_STAT_INC(in_slow_mc); |
1916 | 1917 | ||
1917 | in_dev_put(in_dev); | ||
1918 | hash = rt_hash(daddr, saddr, dev->ifindex, rt_genid(dev_net(dev))); | 1918 | hash = rt_hash(daddr, saddr, dev->ifindex, rt_genid(dev_net(dev))); |
1919 | return rt_intern_hash(hash, rth, NULL, skb, dev->ifindex); | 1919 | return rt_intern_hash(hash, rth, NULL, skb, dev->ifindex); |
1920 | 1920 | ||
1921 | e_nobufs: | 1921 | e_nobufs: |
1922 | in_dev_put(in_dev); | ||
1923 | return -ENOBUFS; | 1922 | return -ENOBUFS; |
1924 | |||
1925 | e_inval: | 1923 | e_inval: |
1926 | err = -EINVAL; | 1924 | return -EINVAL; |
1927 | e_err: | 1925 | e_err: |
1928 | in_dev_put(in_dev); | ||
1929 | return err; | 1926 | return err; |
1930 | } | 1927 | } |
1931 | 1928 | ||
@@ -2101,7 +2098,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, | |||
2101 | u8 tos, struct net_device *dev) | 2098 | u8 tos, struct net_device *dev) |
2102 | { | 2099 | { |
2103 | struct fib_result res; | 2100 | struct fib_result res; |
2104 | struct in_device *in_dev = in_dev_get(dev); | 2101 | struct in_device *in_dev = __in_dev_get_rcu(dev); |
2105 | struct flowi fl = { .nl_u = { .ip4_u = | 2102 | struct flowi fl = { .nl_u = { .ip4_u = |
2106 | { .daddr = daddr, | 2103 | { .daddr = daddr, |
2107 | .saddr = saddr, | 2104 | .saddr = saddr, |
@@ -2179,7 +2176,6 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr, | |||
2179 | 2176 | ||
2180 | err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos); | 2177 | err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos); |
2181 | done: | 2178 | done: |
2182 | in_dev_put(in_dev); | ||
2183 | if (free_res) | 2179 | if (free_res) |
2184 | fib_res_put(&res); | 2180 | fib_res_put(&res); |
2185 | out: return err; | 2181 | out: return err; |
@@ -2288,16 +2284,18 @@ int ip_route_input_common(struct sk_buff *skb, __be32 daddr, __be32 saddr, | |||
2288 | unsigned hash; | 2284 | unsigned hash; |
2289 | int iif = dev->ifindex; | 2285 | int iif = dev->ifindex; |
2290 | struct net *net; | 2286 | struct net *net; |
2287 | int res; | ||
2291 | 2288 | ||
2292 | net = dev_net(dev); | 2289 | net = dev_net(dev); |
2293 | 2290 | ||
2291 | rcu_read_lock(); | ||
2292 | |||
2294 | if (!rt_caching(net)) | 2293 | if (!rt_caching(net)) |
2295 | goto skip_cache; | 2294 | goto skip_cache; |
2296 | 2295 | ||
2297 | tos &= IPTOS_RT_MASK; | 2296 | tos &= IPTOS_RT_MASK; |
2298 | hash = rt_hash(daddr, saddr, iif, rt_genid(net)); | 2297 | hash = rt_hash(daddr, saddr, iif, rt_genid(net)); |
2299 | 2298 | ||
2300 | rcu_read_lock(); | ||
2301 | for (rth = rcu_dereference(rt_hash_table[hash].chain); rth; | 2299 | for (rth = rcu_dereference(rt_hash_table[hash].chain); rth; |
2302 | rth = rcu_dereference(rth->u.dst.rt_next)) { | 2300 | rth = rcu_dereference(rth->u.dst.rt_next)) { |
2303 | if ((((__force u32)rth->fl.fl4_dst ^ (__force u32)daddr) | | 2301 | if ((((__force u32)rth->fl.fl4_dst ^ (__force u32)daddr) | |
@@ -2321,7 +2319,6 @@ int ip_route_input_common(struct sk_buff *skb, __be32 daddr, __be32 saddr, | |||
2321 | } | 2319 | } |
2322 | RT_CACHE_STAT_INC(in_hlist_search); | 2320 | RT_CACHE_STAT_INC(in_hlist_search); |
2323 | } | 2321 | } |
2324 | rcu_read_unlock(); | ||
2325 | 2322 | ||
2326 | skip_cache: | 2323 | skip_cache: |
2327 | /* Multicast recognition logic is moved from route cache to here. | 2324 | /* Multicast recognition logic is moved from route cache to here. |
@@ -2336,12 +2333,11 @@ skip_cache: | |||
2336 | route cache entry is created eventually. | 2333 | route cache entry is created eventually. |
2337 | */ | 2334 | */ |
2338 | if (ipv4_is_multicast(daddr)) { | 2335 | if (ipv4_is_multicast(daddr)) { |
2339 | struct in_device *in_dev; | 2336 | struct in_device *in_dev = __in_dev_get_rcu(dev); |
2340 | 2337 | ||
2341 | rcu_read_lock(); | 2338 | if (in_dev) { |
2342 | if ((in_dev = __in_dev_get_rcu(dev)) != NULL) { | ||
2343 | int our = ip_check_mc(in_dev, daddr, saddr, | 2339 | int our = ip_check_mc(in_dev, daddr, saddr, |
2344 | ip_hdr(skb)->protocol); | 2340 | ip_hdr(skb)->protocol); |
2345 | if (our | 2341 | if (our |
2346 | #ifdef CONFIG_IP_MROUTE | 2342 | #ifdef CONFIG_IP_MROUTE |
2347 | || | 2343 | || |
@@ -2349,15 +2345,18 @@ skip_cache: | |||
2349 | IN_DEV_MFORWARD(in_dev)) | 2345 | IN_DEV_MFORWARD(in_dev)) |
2350 | #endif | 2346 | #endif |
2351 | ) { | 2347 | ) { |
2348 | int res = ip_route_input_mc(skb, daddr, saddr, | ||
2349 | tos, dev, our); | ||
2352 | rcu_read_unlock(); | 2350 | rcu_read_unlock(); |
2353 | return ip_route_input_mc(skb, daddr, saddr, | 2351 | return res; |
2354 | tos, dev, our); | ||
2355 | } | 2352 | } |
2356 | } | 2353 | } |
2357 | rcu_read_unlock(); | 2354 | rcu_read_unlock(); |
2358 | return -EINVAL; | 2355 | return -EINVAL; |
2359 | } | 2356 | } |
2360 | return ip_route_input_slow(skb, daddr, saddr, tos, dev); | 2357 | res = ip_route_input_slow(skb, daddr, saddr, tos, dev); |
2358 | rcu_read_unlock(); | ||
2359 | return res; | ||
2361 | } | 2360 | } |
2362 | EXPORT_SYMBOL(ip_route_input_common); | 2361 | EXPORT_SYMBOL(ip_route_input_common); |
2363 | 2362 | ||