fs: cache optimise dentry and inode for rcu-walk
authorNick Piggin <npiggin@kernel.dk>
Fri, 7 Jan 2011 06:49:56 +0000 (17:49 +1100)
committerNick Piggin <npiggin@kernel.dk>
Fri, 7 Jan 2011 06:50:28 +0000 (17:50 +1100)
Put dentry and inode fields into top of data structure.  This allows RCU path
traversal to perform an RCU dentry lookup in a path walk by touching only the
first 56 bytes of the dentry.

We also fit in 8 bytes of inline name in the first 64 bytes, so for short
names, only 64 bytes needs to be touched to perform the lookup. We should
get rid of the hash->prev pointer from the first 64 bytes, and fit 16 bytes
of name in there, which will take care of 81% rather than 32% of the kernel
tree.

inode is also rearranged so that RCU lookup will only touch a single cacheline
in the inode, plus one in the i_ops structure.

This is important for directory component lookups in RCU path walking. In the
kernel source, directory names average is around 6 chars, so this works.

When we reach the last element of the lookup, we need to lock it and take its
refcount which requires another cacheline access.

Align dentry and inode operations structs, so members will be at predictable
offsets and we can group common operations into head of structure.

Signed-off-by: Nick Piggin <npiggin@kernel.dk>
fs/dcache.c
include/linux/dcache.h
include/linux/fs.h

index f9693da3efbd73c36224bb14b982642425ae4a17..07d1f6862dc73f6d6f3fd7111d88f19ed84a23ed 100644 (file)
@@ -83,8 +83,6 @@ EXPORT_SYMBOL(dcache_inode_lock);
 
 static struct kmem_cache *dentry_cache __read_mostly;
 
-#define DNAME_INLINE_LEN (sizeof(struct dentry)-offsetof(struct dentry,d_iname))
-
 /*
  * This is the single most critical data structure when it comes
  * to the dcache: the hashtable for lookups. Somebody should try
index f4b40a751f096d4264e5fcc8ff2c66c8485c1807..b1aeda0772588f04401a454820d2e35b3f974f93 100644 (file)
@@ -82,25 +82,33 @@ full_name_hash(const unsigned char *name, unsigned int len)
  * large memory footprint increase).
  */
 #ifdef CONFIG_64BIT
-#define DNAME_INLINE_LEN_MIN 32 /* 192 bytes */
+# define DNAME_INLINE_LEN 32 /* 192 bytes */
 #else
-#define DNAME_INLINE_LEN_MIN 40 /* 128 bytes */
+# ifdef CONFIG_SMP
+#  define DNAME_INLINE_LEN 36 /* 128 bytes */
+# else
+#  define DNAME_INLINE_LEN 40 /* 128 bytes */
+# endif
 #endif
 
 struct dentry {
-       unsigned int d_count;           /* protected by d_lock */
+       /* RCU lookup touched fields */
        unsigned int d_flags;           /* protected by d_lock */
-       spinlock_t d_lock;              /* per dentry lock */
        seqcount_t d_seq;               /* per dentry seqlock */
-       struct inode *d_inode;          /* Where the name belongs to - NULL is
-                                        * negative */
-       /*
-        * The next three fields are touched by __d_lookup.  Place them here
-        * so they all fit in a cache line.
-        */
        struct hlist_node d_hash;       /* lookup hash list */
        struct dentry *d_parent;        /* parent directory */
        struct qstr d_name;
+       struct inode *d_inode;          /* Where the name belongs to - NULL is
+                                        * negative */
+       unsigned char d_iname[DNAME_INLINE_LEN];        /* small names */
+
+       /* Ref lookup also touches following */
+       unsigned int d_count;           /* protected by d_lock */
+       spinlock_t d_lock;              /* per dentry lock */
+       const struct dentry_operations *d_op;
+       struct super_block *d_sb;       /* The root of the dentry tree */
+       unsigned long d_time;           /* used by d_revalidate */
+       void *d_fsdata;                 /* fs-specific data */
 
        struct list_head d_lru;         /* LRU list */
        /*
@@ -112,12 +120,6 @@ struct dentry {
        } d_u;
        struct list_head d_subdirs;     /* our children */
        struct list_head d_alias;       /* inode alias list */
-       unsigned long d_time;           /* used by d_revalidate */
-       const struct dentry_operations *d_op;
-       struct super_block *d_sb;       /* The root of the dentry tree */
-       void *d_fsdata;                 /* fs-specific data */
-
-       unsigned char d_iname[DNAME_INLINE_LEN_MIN];    /* small names */
 };
 
 /*
@@ -143,7 +145,7 @@ struct dentry_operations {
        void (*d_release)(struct dentry *);
        void (*d_iput)(struct dentry *, struct inode *);
        char *(*d_dname)(struct dentry *, char *, int);
-};
+} ____cacheline_aligned;
 
 /*
  * Locking rules for dentry_operations callbacks are to be found in
index ea202fff44f82a83a2ab93a33ca84e0fa74664b4..a04aa96acb71f173733346dbaf19f213d89f1b4d 100644 (file)
@@ -733,6 +733,20 @@ struct posix_acl;
 #define ACL_NOT_CACHED ((void *)(-1))
 
 struct inode {
+       /* RCU path lookup touches following: */
+       umode_t                 i_mode;
+       uid_t                   i_uid;
+       gid_t                   i_gid;
+       const struct inode_operations   *i_op;
+       struct super_block      *i_sb;
+
+       spinlock_t              i_lock; /* i_blocks, i_bytes, maybe i_size */
+       unsigned int            i_flags;
+       struct mutex            i_mutex;
+
+       unsigned long           i_state;
+       unsigned long           dirtied_when;   /* jiffies of first dirtying */
+
        struct hlist_node       i_hash;
        struct list_head        i_wb_list;      /* backing dev IO list */
        struct list_head        i_lru;          /* inode LRU list */
@@ -744,8 +758,6 @@ struct inode {
        unsigned long           i_ino;
        atomic_t                i_count;
        unsigned int            i_nlink;
-       uid_t                   i_uid;
-       gid_t                   i_gid;
        dev_t                   i_rdev;
        unsigned int            i_blkbits;
        u64                     i_version;
@@ -758,13 +770,8 @@ struct inode {
        struct timespec         i_ctime;
        blkcnt_t                i_blocks;
        unsigned short          i_bytes;
-       umode_t                 i_mode;
-       spinlock_t              i_lock; /* i_blocks, i_bytes, maybe i_size */
-       struct mutex            i_mutex;
        struct rw_semaphore     i_alloc_sem;
-       const struct inode_operations   *i_op;
        const struct file_operations    *i_fop; /* former ->i_op->default_file_ops */
-       struct super_block      *i_sb;
        struct file_lock        *i_flock;
        struct address_space    *i_mapping;
        struct address_space    i_data;
@@ -785,11 +792,6 @@ struct inode {
        struct hlist_head       i_fsnotify_marks;
 #endif
 
-       unsigned long           i_state;
-       unsigned long           dirtied_when;   /* jiffies of first dirtying */
-
-       unsigned int            i_flags;
-
 #ifdef CONFIG_IMA
        /* protected by i_lock */
        unsigned int            i_readcount; /* struct files open RO */
@@ -1549,8 +1551,15 @@ struct file_operations {
 };
 
 struct inode_operations {
-       int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
        struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
+       void * (*follow_link) (struct dentry *, struct nameidata *);
+       int (*permission) (struct inode *, int);
+       int (*check_acl)(struct inode *, int);
+
+       int (*readlink) (struct dentry *, char __user *,int);
+       void (*put_link) (struct dentry *, struct nameidata *, void *);
+
+       int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
        int (*link) (struct dentry *,struct inode *,struct dentry *);
        int (*unlink) (struct inode *,struct dentry *);
        int (*symlink) (struct inode *,struct dentry *,const char *);
@@ -1559,12 +1568,7 @@ struct inode_operations {
        int (*mknod) (struct inode *,struct dentry *,int,dev_t);
        int (*rename) (struct inode *, struct dentry *,
                        struct inode *, struct dentry *);
-       int (*readlink) (struct dentry *, char __user *,int);
-       void * (*follow_link) (struct dentry *, struct nameidata *);
-       void (*put_link) (struct dentry *, struct nameidata *, void *);
        void (*truncate) (struct inode *);
-       int (*permission) (struct inode *, int);
-       int (*check_acl)(struct inode *, int);
        int (*setattr) (struct dentry *, struct iattr *);
        int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
        int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
@@ -1576,7 +1580,7 @@ struct inode_operations {
                          loff_t len);
        int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
                      u64 len);
-};
+} ____cacheline_aligned;
 
 struct seq_file;