diff options
author | Eric Dumazet <edumazet@google.com> | 2013-06-07 11:48:57 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-06-12 03:25:23 -0400 |
commit | e9897071350bd9d94a56b5b6f79c85b1a98fc7e7 (patch) | |
tree | d01026afb4450ef685722cd7a0fe1815336170ca /net/ipv4 | |
parent | 64153ce0a7b61b2a5cacb01805cbf670142339e9 (diff) |
igmp: hash a hash table to speedup ip_check_mc_rcu()
After IP route cache removal, multicast applications using
a lot of multicast addresses hit a O(N) behavior in ip_check_mc_rcu()
Add a per in_device hash table to get faster lookup.
This hash table is created only if the number of items in mc_list is
above 4.
Reported-by: Shawn Bohrer <sbohrer@rgmadvisors.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Tested-by: Shawn Bohrer <sbohrer@rgmadvisors.com>
Reviewed-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/devinet.c | 1 | ||||
-rw-r--r-- | net/ipv4/igmp.c | 73 |
2 files changed, 71 insertions, 3 deletions
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index b047e2d8a614..3469506c106d 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c | |||
@@ -215,6 +215,7 @@ void in_dev_finish_destroy(struct in_device *idev) | |||
215 | 215 | ||
216 | WARN_ON(idev->ifa_list); | 216 | WARN_ON(idev->ifa_list); |
217 | WARN_ON(idev->mc_list); | 217 | WARN_ON(idev->mc_list); |
218 | kfree(rcu_dereference_protected(idev->mc_hash, 1)); | ||
218 | #ifdef NET_REFCNT_DEBUG | 219 | #ifdef NET_REFCNT_DEBUG |
219 | pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL"); | 220 | pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL"); |
220 | #endif | 221 | #endif |
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 450f625361e4..f72011df9c59 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c | |||
@@ -1217,6 +1217,57 @@ static void igmp_group_added(struct ip_mc_list *im) | |||
1217 | * Multicast list managers | 1217 | * Multicast list managers |
1218 | */ | 1218 | */ |
1219 | 1219 | ||
1220 | static u32 ip_mc_hash(const struct ip_mc_list *im) | ||
1221 | { | ||
1222 | return hash_32((u32)im->multiaddr, MC_HASH_SZ_LOG); | ||
1223 | } | ||
1224 | |||
1225 | static void ip_mc_hash_add(struct in_device *in_dev, | ||
1226 | struct ip_mc_list *im) | ||
1227 | { | ||
1228 | struct ip_mc_list __rcu **mc_hash; | ||
1229 | u32 hash; | ||
1230 | |||
1231 | mc_hash = rtnl_dereference(in_dev->mc_hash); | ||
1232 | if (mc_hash) { | ||
1233 | hash = ip_mc_hash(im); | ||
1234 | im->next_hash = rtnl_dereference(mc_hash[hash]); | ||
1235 | rcu_assign_pointer(mc_hash[hash], im); | ||
1236 | return; | ||
1237 | } | ||
1238 | |||
1239 | /* do not use a hash table for small number of items */ | ||
1240 | if (in_dev->mc_count < 4) | ||
1241 | return; | ||
1242 | |||
1243 | mc_hash = kzalloc(sizeof(struct ip_mc_list *) << MC_HASH_SZ_LOG, | ||
1244 | GFP_KERNEL); | ||
1245 | if (!mc_hash) | ||
1246 | return; | ||
1247 | |||
1248 | for_each_pmc_rtnl(in_dev, im) { | ||
1249 | hash = ip_mc_hash(im); | ||
1250 | im->next_hash = rtnl_dereference(mc_hash[hash]); | ||
1251 | RCU_INIT_POINTER(mc_hash[hash], im); | ||
1252 | } | ||
1253 | |||
1254 | rcu_assign_pointer(in_dev->mc_hash, mc_hash); | ||
1255 | } | ||
1256 | |||
1257 | static void ip_mc_hash_remove(struct in_device *in_dev, | ||
1258 | struct ip_mc_list *im) | ||
1259 | { | ||
1260 | struct ip_mc_list __rcu **mc_hash = rtnl_dereference(in_dev->mc_hash); | ||
1261 | struct ip_mc_list *aux; | ||
1262 | |||
1263 | if (!mc_hash) | ||
1264 | return; | ||
1265 | mc_hash += ip_mc_hash(im); | ||
1266 | while ((aux = rtnl_dereference(*mc_hash)) != im) | ||
1267 | mc_hash = &aux->next_hash; | ||
1268 | *mc_hash = im->next_hash; | ||
1269 | } | ||
1270 | |||
1220 | 1271 | ||
1221 | /* | 1272 | /* |
1222 | * A socket has joined a multicast group on device dev. | 1273 | * A socket has joined a multicast group on device dev. |
@@ -1258,6 +1309,8 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) | |||
1258 | in_dev->mc_count++; | 1309 | in_dev->mc_count++; |
1259 | rcu_assign_pointer(in_dev->mc_list, im); | 1310 | rcu_assign_pointer(in_dev->mc_list, im); |
1260 | 1311 | ||
1312 | ip_mc_hash_add(in_dev, im); | ||
1313 | |||
1261 | #ifdef CONFIG_IP_MULTICAST | 1314 | #ifdef CONFIG_IP_MULTICAST |
1262 | igmpv3_del_delrec(in_dev, im->multiaddr); | 1315 | igmpv3_del_delrec(in_dev, im->multiaddr); |
1263 | #endif | 1316 | #endif |
@@ -1314,6 +1367,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) | |||
1314 | ip = &i->next_rcu) { | 1367 | ip = &i->next_rcu) { |
1315 | if (i->multiaddr == addr) { | 1368 | if (i->multiaddr == addr) { |
1316 | if (--i->users == 0) { | 1369 | if (--i->users == 0) { |
1370 | ip_mc_hash_remove(in_dev, i); | ||
1317 | *ip = i->next_rcu; | 1371 | *ip = i->next_rcu; |
1318 | in_dev->mc_count--; | 1372 | in_dev->mc_count--; |
1319 | igmp_group_dropped(i); | 1373 | igmp_group_dropped(i); |
@@ -2321,12 +2375,25 @@ void ip_mc_drop_socket(struct sock *sk) | |||
2321 | int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 proto) | 2375 | int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 proto) |
2322 | { | 2376 | { |
2323 | struct ip_mc_list *im; | 2377 | struct ip_mc_list *im; |
2378 | struct ip_mc_list __rcu **mc_hash; | ||
2324 | struct ip_sf_list *psf; | 2379 | struct ip_sf_list *psf; |
2325 | int rv = 0; | 2380 | int rv = 0; |
2326 | 2381 | ||
2327 | for_each_pmc_rcu(in_dev, im) { | 2382 | mc_hash = rcu_dereference(in_dev->mc_hash); |
2328 | if (im->multiaddr == mc_addr) | 2383 | if (mc_hash) { |
2329 | break; | 2384 | u32 hash = hash_32((u32)mc_addr, MC_HASH_SZ_LOG); |
2385 | |||
2386 | for (im = rcu_dereference(mc_hash[hash]); | ||
2387 | im != NULL; | ||
2388 | im = rcu_dereference(im->next_hash)) { | ||
2389 | if (im->multiaddr == mc_addr) | ||
2390 | break; | ||
2391 | } | ||
2392 | } else { | ||
2393 | for_each_pmc_rcu(in_dev, im) { | ||
2394 | if (im->multiaddr == mc_addr) | ||
2395 | break; | ||
2396 | } | ||
2330 | } | 2397 | } |
2331 | if (im && proto == IPPROTO_IGMP) { | 2398 | if (im && proto == IPPROTO_IGMP) { |
2332 | rv = 1; | 2399 | rv = 1; |