For ext3 and reiserfs (at least), __mark_inode_dirty() is really expensive. Most of the calls are for atime updates. We used to have an optimisation which only didthe work if the atime had changed by one second, but that was removed some time ago, to support higher resolution timestamping. And now that __mark_inode_dirty() has an mb() in it, ext2 has started to hurt a bit as well. This patch puts the optimisation back, for filesystems which do not want the higher resolution. We add a new inode_operations.update_atime, and provide a library function one_sec_update_atime() which filesystems can point their inode_operations field at. Across a `make -j3 bzImage' the total number of calls to __mark_inode_dirty() are reduced from 194000 to 99000. Secondly, update_atime() (which is renamed here to generic_update_atime()) is calling current_kernel_time() even for noatime mounts. current_kernel_time() does mb(). This code has been reorder so that the call to current_kernel_time is deferred until after we have checked for noatime. fs/autofs4/root.c | 2 +- fs/inode.c | 46 ++++++++++++++++++++++++++++++++++++---------- include/linux/fs.h | 14 +++++++++++--- kernel/ksyms.c | 3 ++- 4 files changed, 50 insertions(+), 15 deletions(-) diff -puN fs/inode.c~update_atime-speedup fs/inode.c --- 25/fs/inode.c~update_atime-speedup 2003-02-27 01:40:40.000000000 -0800 +++ 25-akpm/fs/inode.c 2003-02-27 01:40:40.000000000 -0800 @@ -1081,29 +1081,55 @@ sector_t bmap(struct inode * inode, sect } /** - * update_atime - update the access time + * generic_update_atime - update the access time * @inode: inode accessed * * Update the accessed time on an inode and mark it for writeback. * This function automatically handles read only file systems and media, * as well as the "noatime" flag and inode specific "noatime" markers. */ - -void update_atime(struct inode *inode) +int generic_update_atime(struct inode *inode) { - struct timespec now = CURRENT_TIME; + struct timespec now; - /* Can later do this more lazily with a per superblock interval */ - if (timespec_equal(&inode->i_atime, &now)) - return; if (IS_NOATIME(inode)) - return; + return 0; if (IS_NODIRATIME(inode) && S_ISDIR(inode->i_mode)) - return; + return 0; if (IS_RDONLY(inode)) - return; + return 0; + + now = CURRENT_TIME; + + if (timespec_equal(&inode->i_atime, &now)) + return 0; inode->i_atime = now; mark_inode_dirty_sync(inode); + return 0; +} + +/* + * For filesystems which only support one second atime resolution + */ +int one_sec_update_atime(struct inode *inode) +{ + long now; + + now = xtime.tv_sec; + if (inode->i_atime.tv_sec == now) + return 0; + + if (IS_NOATIME(inode)) + return 0; + if (IS_NODIRATIME(inode) && S_ISDIR(inode->i_mode)) + return 0; + if (IS_RDONLY(inode)) + return 0; + + inode->i_atime.tv_sec = now; + inode->i_atime.tv_nsec = 0; + mark_inode_dirty_sync(inode); + return 0; } /** diff -puN fs/autofs4/root.c~update_atime-speedup fs/autofs4/root.c --- 25/fs/autofs4/root.c~update_atime-speedup 2003-02-27 01:40:40.000000000 -0800 +++ 25-akpm/fs/autofs4/root.c 2003-02-27 01:40:40.000000000 -0800 @@ -61,7 +61,7 @@ static void autofs4_update_usage(struct struct autofs_info *ino = autofs4_dentry_ino(dentry); if (ino) { - update_atime(dentry->d_inode); + UPDATE_ATIME(dentry->d_inode); ino->last_used = jiffies; } } diff -puN include/linux/fs.h~update_atime-speedup include/linux/fs.h --- 25/include/linux/fs.h~update_atime-speedup 2003-02-27 01:40:40.000000000 -0800 +++ 25-akpm/include/linux/fs.h 2003-02-27 01:40:40.000000000 -0800 @@ -203,9 +203,6 @@ extern int leases_enable, dir_notify_ena #include #include -extern void update_atime (struct inode *); -#define UPDATE_ATIME(inode) update_atime (inode) - extern void inode_init(unsigned long); extern void mnt_init(unsigned long); extern void files_init(unsigned long); @@ -746,6 +743,7 @@ struct inode_operations { ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); ssize_t (*listxattr) (struct dentry *, char *, size_t); int (*removexattr) (struct dentry *, const char *); + int (*update_atime)(struct inode *inode); }; struct seq_file; @@ -807,6 +805,16 @@ static inline void mark_inode_dirty_sync __mark_inode_dirty(inode, I_DIRTY_SYNC); } +int generic_update_atime(struct inode *inode); +int one_sec_update_atime(struct inode *inode); + +/* FIXME: rename this to update_atime() */ +static inline int UPDATE_ATIME(struct inode *inode) +{ + if (inode->i_op->update_atime) + return (*inode->i_op->update_atime)(inode); + return generic_update_atime(inode); +} /** * &export_operations - for nfsd to communicate with file systems diff -puN kernel/ksyms.c~update_atime-speedup kernel/ksyms.c --- 25/kernel/ksyms.c~update_atime-speedup 2003-02-27 01:40:40.000000000 -0800 +++ 25-akpm/kernel/ksyms.c 2003-02-27 01:40:40.000000000 -0800 @@ -134,7 +134,8 @@ EXPORT_SYMBOL(get_user_pages); /* filesystem internal functions */ EXPORT_SYMBOL(def_blk_fops); -EXPORT_SYMBOL(update_atime); +EXPORT_SYMBOL(generic_update_atime); +EXPORT_SYMBOL(one_sec_update_atime); EXPORT_SYMBOL(get_fs_type); EXPORT_SYMBOL(user_get_super); EXPORT_SYMBOL(get_super); _