fs: cache optimise dentry and inode for rcu-walk

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>
This commit is contained in:
Nick Piggin 2011-01-07 17:49:56 +11:00
parent fb045adb99
commit 44a7d7a878
3 changed files with 42 additions and 38 deletions

View File

@ -83,8 +83,6 @@ EXPORT_SYMBOL(dcache_inode_lock);
static struct kmem_cache *dentry_cache __read_mostly; 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 * This is the single most critical data structure when it comes
* to the dcache: the hashtable for lookups. Somebody should try * to the dcache: the hashtable for lookups. Somebody should try

View File

@ -82,25 +82,33 @@ full_name_hash(const unsigned char *name, unsigned int len)
* large memory footprint increase). * large memory footprint increase).
*/ */
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
#define DNAME_INLINE_LEN_MIN 32 /* 192 bytes */ # define DNAME_INLINE_LEN 32 /* 192 bytes */
#else #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 #endif
struct dentry { struct dentry {
unsigned int d_count; /* protected by d_lock */ /* RCU lookup touched fields */
unsigned int d_flags; /* protected by d_lock */ unsigned int d_flags; /* protected by d_lock */
spinlock_t d_lock; /* per dentry lock */
seqcount_t d_seq; /* per dentry seqlock */ 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 hlist_node d_hash; /* lookup hash list */
struct dentry *d_parent; /* parent directory */ struct dentry *d_parent; /* parent directory */
struct qstr d_name; 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 */ struct list_head d_lru; /* LRU list */
/* /*
@ -112,12 +120,6 @@ struct dentry {
} d_u; } d_u;
struct list_head d_subdirs; /* our children */ struct list_head d_subdirs; /* our children */
struct list_head d_alias; /* inode alias list */ 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_release)(struct dentry *);
void (*d_iput)(struct dentry *, struct inode *); void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int); char *(*d_dname)(struct dentry *, char *, int);
}; } ____cacheline_aligned;
/* /*
* Locking rules for dentry_operations callbacks are to be found in * Locking rules for dentry_operations callbacks are to be found in

View File

@ -733,6 +733,20 @@ struct posix_acl;
#define ACL_NOT_CACHED ((void *)(-1)) #define ACL_NOT_CACHED ((void *)(-1))
struct inode { 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 hlist_node i_hash;
struct list_head i_wb_list; /* backing dev IO list */ struct list_head i_wb_list; /* backing dev IO list */
struct list_head i_lru; /* inode LRU list */ struct list_head i_lru; /* inode LRU list */
@ -744,8 +758,6 @@ struct inode {
unsigned long i_ino; unsigned long i_ino;
atomic_t i_count; atomic_t i_count;
unsigned int i_nlink; unsigned int i_nlink;
uid_t i_uid;
gid_t i_gid;
dev_t i_rdev; dev_t i_rdev;
unsigned int i_blkbits; unsigned int i_blkbits;
u64 i_version; u64 i_version;
@ -758,13 +770,8 @@ struct inode {
struct timespec i_ctime; struct timespec i_ctime;
blkcnt_t i_blocks; blkcnt_t i_blocks;
unsigned short i_bytes; 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; struct rw_semaphore i_alloc_sem;
const struct inode_operations *i_op;
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */ const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct super_block *i_sb;
struct file_lock *i_flock; struct file_lock *i_flock;
struct address_space *i_mapping; struct address_space *i_mapping;
struct address_space i_data; struct address_space i_data;
@ -785,11 +792,6 @@ struct inode {
struct hlist_head i_fsnotify_marks; struct hlist_head i_fsnotify_marks;
#endif #endif
unsigned long i_state;
unsigned long dirtied_when; /* jiffies of first dirtying */
unsigned int i_flags;
#ifdef CONFIG_IMA #ifdef CONFIG_IMA
/* protected by i_lock */ /* protected by i_lock */
unsigned int i_readcount; /* struct files open RO */ unsigned int i_readcount; /* struct files open RO */
@ -1549,8 +1551,15 @@ struct file_operations {
}; };
struct inode_operations { struct inode_operations {
int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
struct dentry * (*lookup) (struct inode *,struct dentry *, 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 (*link) (struct dentry *,struct inode *,struct dentry *);
int (*unlink) (struct inode *,struct dentry *); int (*unlink) (struct inode *,struct dentry *);
int (*symlink) (struct inode *,struct dentry *,const char *); 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 (*mknod) (struct inode *,struct dentry *,int,dev_t);
int (*rename) (struct inode *, struct dentry *, int (*rename) (struct inode *, struct dentry *,
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 *); void (*truncate) (struct inode *);
int (*permission) (struct inode *, int);
int (*check_acl)(struct inode *, int);
int (*setattr) (struct dentry *, struct iattr *); int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *); int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int); int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
@ -1576,7 +1580,7 @@ struct inode_operations {
loff_t len); loff_t len);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
u64 len); u64 len);
}; } ____cacheline_aligned;
struct seq_file; struct seq_file;