Skip to content

Commit 79eb15d

Browse files
edumazetkuba-moo
authored andcommitted
ipv4: add net_hash_mix() dispersion to fib_info_laddrhash keys
net/ipv4/fib_semantics.c uses a hash table (fib_info_laddrhash) in which fib_sync_down_addr() can locate fib_info based on IPv4 local address. This hash table is resized based on total number of hashed fib_info, but the hash function is only using the local address. For hosts having many active network namespaces, all fib_info for loopback devices (IPv4 address 127.0.0.1) are hashed into a single bucket, making netns dismantles very slow. Signed-off-by: Eric Dumazet <[email protected]> Reviewed-by: David Ahern <[email protected]> Signed-off-by: Jakub Kicinski <[email protected]>
1 parent d07418a commit 79eb15d

File tree

1 file changed

+15
-14
lines changed

1 file changed

+15
-14
lines changed

net/ipv4/fib_semantics.c

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ static DEFINE_SPINLOCK(fib_info_lock);
5252
static struct hlist_head *fib_info_hash;
5353
static struct hlist_head *fib_info_laddrhash;
5454
static unsigned int fib_info_hash_size;
55+
static unsigned int fib_info_hash_bits;
5556
static unsigned int fib_info_cnt;
5657

5758
#define DEVINDEX_HASHBITS 8
@@ -1247,13 +1248,13 @@ int fib_check_nh(struct net *net, struct fib_nh *nh, u32 table, u8 scope,
12471248
return err;
12481249
}
12491250

1250-
static inline unsigned int fib_laddr_hashfn(__be32 val)
1251+
static struct hlist_head *
1252+
fib_info_laddrhash_bucket(const struct net *net, __be32 val)
12511253
{
1252-
unsigned int mask = (fib_info_hash_size - 1);
1254+
u32 slot = hash_32(net_hash_mix(net) ^ (__force u32)val,
1255+
fib_info_hash_bits);
12531256

1254-
return ((__force u32)val ^
1255-
((__force u32)val >> 7) ^
1256-
((__force u32)val >> 14)) & mask;
1257+
return &fib_info_laddrhash[slot];
12571258
}
12581259

12591260
static struct hlist_head *fib_info_hash_alloc(int bytes)
@@ -1289,6 +1290,7 @@ static void fib_info_hash_move(struct hlist_head *new_info_hash,
12891290
old_info_hash = fib_info_hash;
12901291
old_laddrhash = fib_info_laddrhash;
12911292
fib_info_hash_size = new_size;
1293+
fib_info_hash_bits = ilog2(new_size);
12921294

12931295
for (i = 0; i < old_size; i++) {
12941296
struct hlist_head *head = &fib_info_hash[i];
@@ -1306,21 +1308,20 @@ static void fib_info_hash_move(struct hlist_head *new_info_hash,
13061308
}
13071309
fib_info_hash = new_info_hash;
13081310

1311+
fib_info_laddrhash = new_laddrhash;
13091312
for (i = 0; i < old_size; i++) {
1310-
struct hlist_head *lhead = &fib_info_laddrhash[i];
1313+
struct hlist_head *lhead = &old_laddrhash[i];
13111314
struct hlist_node *n;
13121315
struct fib_info *fi;
13131316

13141317
hlist_for_each_entry_safe(fi, n, lhead, fib_lhash) {
13151318
struct hlist_head *ldest;
1316-
unsigned int new_hash;
13171319

1318-
new_hash = fib_laddr_hashfn(fi->fib_prefsrc);
1319-
ldest = &new_laddrhash[new_hash];
1320+
ldest = fib_info_laddrhash_bucket(fi->fib_net,
1321+
fi->fib_prefsrc);
13201322
hlist_add_head(&fi->fib_lhash, ldest);
13211323
}
13221324
}
1323-
fib_info_laddrhash = new_laddrhash;
13241325

13251326
spin_unlock_bh(&fib_info_lock);
13261327

@@ -1605,7 +1606,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg,
16051606
if (fi->fib_prefsrc) {
16061607
struct hlist_head *head;
16071608

1608-
head = &fib_info_laddrhash[fib_laddr_hashfn(fi->fib_prefsrc)];
1609+
head = fib_info_laddrhash_bucket(net, fi->fib_prefsrc);
16091610
hlist_add_head(&fi->fib_lhash, head);
16101611
}
16111612
if (fi->nh) {
@@ -1877,16 +1878,16 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event,
18771878
*/
18781879
int fib_sync_down_addr(struct net_device *dev, __be32 local)
18791880
{
1880-
int ret = 0;
1881-
unsigned int hash = fib_laddr_hashfn(local);
1882-
struct hlist_head *head = &fib_info_laddrhash[hash];
18831881
int tb_id = l3mdev_fib_table(dev) ? : RT_TABLE_MAIN;
18841882
struct net *net = dev_net(dev);
1883+
struct hlist_head *head;
18851884
struct fib_info *fi;
1885+
int ret = 0;
18861886

18871887
if (!fib_info_laddrhash || local == 0)
18881888
return 0;
18891889

1890+
head = fib_info_laddrhash_bucket(net, local);
18901891
hlist_for_each_entry(fi, head, fib_lhash) {
18911892
if (!net_eq(fi->fib_net, net) ||
18921893
fi->fib_tb_id != tb_id)

0 commit comments

Comments
 (0)