Btrfs: improve inode hash function/inode lookup
authorFilipe David Borba Manana <fdmanana@gmail.com>
Sun, 6 Oct 2013 21:22:33 +0000 (22:22 +0100)
committerChris Mason <chris.mason@fusionio.com>
Tue, 12 Nov 2013 02:55:19 +0000 (21:55 -0500)
Currently the hash value used for adding an inode to the VFS's inode
hash table consists of the plain inode number, which is a 64 bits
integer. This results in hash table buckets (hlist_head lists) with
too many elements for at least 2 important scenarios:

1) When we have many subvolumes. Each subvolume has its own btree
   where its files and directories are added to, and each has its
   own objectid (inode number) namespace. This means that if we have
   N subvolumes, and all have inode number X associated to a file or
   directory, the corresponding inodes all map to the same hash table
   entry, resulting in a bucket (hlist_head list) with N elements;

2) On 32 bits machines. Th VFS hash values are unsigned longs, which
   are 32 bits wide on 32 bits machines, and the inode (objectid)
   numbers are 64 bits unsigned integers. We simply cast the inode
   numbers to hash values, which means that for all inodes with the
   same 32 bits lower half, the same hash bucket is used for all of
   them. For example, all inodes with a number (objectid) between
   0x0000_0000_ffff_ffff and 0xffff_ffff_ffff_ffff will end up in
   the same hash table bucket.

This change ensures the inode's hash value depends both on the
objectid (inode number) and its subvolume's (btree root) objectid.
For 32 bits machines, this change gives better entropy by making
the hash value depend on both the upper and lower 32 bits of the
64 bits hash previously computed.

Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
fs/btrfs/btrfs_inode.h
fs/btrfs/disk-io.c
fs/btrfs/inode.c

index 71f074e1870b2e9fe183cf338ad2515314155e6e..ac0b39db27d175af15a718a41ac2ebf11c32150f 100644 (file)
@@ -19,6 +19,7 @@
 #ifndef __BTRFS_I__
 #define __BTRFS_I__
 
+#include <linux/hash.h>
 #include "extent_map.h"
 #include "extent_io.h"
 #include "ordered-data.h"
@@ -179,6 +180,25 @@ static inline struct btrfs_inode *BTRFS_I(struct inode *inode)
        return container_of(inode, struct btrfs_inode, vfs_inode);
 }
 
+static inline unsigned long btrfs_inode_hash(u64 objectid,
+                                            const struct btrfs_root *root)
+{
+       u64 h = objectid ^ (root->objectid * GOLDEN_RATIO_PRIME);
+
+#if BITS_PER_LONG == 32
+       h = (h >> 32) ^ (h & 0xffffffff);
+#endif
+
+       return (unsigned long)h;
+}
+
+static inline void btrfs_insert_inode_hash(struct inode *inode)
+{
+       unsigned long h = btrfs_inode_hash(inode->i_ino, BTRFS_I(inode)->root);
+
+       __insert_inode_hash(inode, h);
+}
+
 static inline u64 btrfs_ino(struct inode *inode)
 {
        u64 ino = BTRFS_I(inode)->location.objectid;
index ade6c0e796164331be64d64a8f6d44b8e5609f93..d205bddc7776f5bc48622b628096eab6d7a72c10 100644 (file)
@@ -2294,7 +2294,7 @@ int open_ctree(struct super_block *sb,
               sizeof(struct btrfs_key));
        set_bit(BTRFS_INODE_DUMMY,
                &BTRFS_I(fs_info->btree_inode)->runtime_flags);
-       insert_inode_hash(fs_info->btree_inode);
+       btrfs_insert_inode_hash(fs_info->btree_inode);
 
        spin_lock_init(&fs_info->block_group_cache_lock);
        fs_info->block_group_cache_tree = RB_ROOT;
index 1ca49eaba3bb4aee7e18aba92e38bbc500e71e69..bb242f2fb51e60dca08a8963a6f71602f68a74e9 100644 (file)
@@ -4837,10 +4837,12 @@ static struct inode *btrfs_iget_locked(struct super_block *s,
 {
        struct inode *inode;
        struct btrfs_iget_args args;
+       unsigned long hashval = btrfs_inode_hash(objectid, root);
+
        args.ino = objectid;
        args.root = root;
 
-       inode = iget5_locked(s, objectid, btrfs_find_actor,
+       inode = iget5_locked(s, hashval, btrfs_find_actor,
                             btrfs_init_locked_inode,
                             (void *)&args);
        return inode;
@@ -5460,7 +5462,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
                                BTRFS_INODE_NODATASUM;
        }
 
-       insert_inode_hash(inode);
+       btrfs_insert_inode_hash(inode);
        inode_tree_add(inode);
 
        trace_btrfs_inode_new(inode);