diff -Nurp a/fs/nfs/Makefile b/fs/nfs/Makefile --- a/fs/nfs/Makefile 2010-01-05 16:42:09.000000000 +0100 +++ b/fs/nfs/Makefile 2010-06-16 16:51:35.000000000 +0200 @@ -15,5 +15,5 @@ nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4x delegation.o idmap.o \ callback.o callback_xdr.o callback_proc.o \ nfs4namespace.o -nfs-$(CONFIG_SYSCTL) += sysctl.o +nfs-$(CONFIG_SYSCTL) += sysctl.o sysctl_net_nfs.o nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o diff -Nurp a/fs/nfs/dir.c b/fs/nfs/dir.c --- a/fs/nfs/dir.c 2010-06-15 11:33:28.000000000 +0200 +++ b/fs/nfs/dir.c 2010-06-16 16:51:35.000000000 +0200 @@ -672,6 +672,8 @@ static int nfs_check_verifier(struct ino { if (IS_ROOT(dentry)) return 1; + if (NFS_I(dir)->noac_timeout) + return 0; if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONE) return 0; if (!nfs_verify_change_attribute(dir, dentry->d_time)) diff -Nurp a/fs/nfs/inode.c b/fs/nfs/inode.c --- a/fs/nfs/inode.c 2010-06-15 11:33:28.000000000 +0200 +++ b/fs/nfs/inode.c 2010-06-16 16:51:35.000000000 +0200 @@ -134,7 +134,8 @@ static void nfs_zap_caches_locked(struct nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE); - nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); + if (!nfsi->noac_timeout) + nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); nfsi->attrtimeo_timestamp = jiffies; memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); @@ -367,6 +368,7 @@ nfs_fhget(struct super_block *sb, struct } nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); nfsi->attrtimeo_timestamp = now; + nfsi->noac_timeout = 0; nfsi->access_cache = RB_ROOT; nfs_fscache_init_inode_cookie(inode); @@ -1246,6 +1248,16 @@ static int nfs_update_inode(struct inode if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED) inode->i_blocks = fattr->du.nfs2.blocks; + /* check for attribute cache reactivation */ + if ( nfsi->noac_timeout + && nfsi->attrtimeo == 0 + && time_after(jiffies, nfsi->noac_timeout) + ) + { + nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); + nfsi->noac_timeout = 0; + } + /* Update attrtimeo value if we're out of the unstable period */ if (invalid & NFS_INO_INVALID_ATTR) { nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE); @@ -1453,10 +1465,16 @@ static int __init init_nfs_fs(void) #ifdef CONFIG_PROC_FS rpc_proc_register(&nfs_rpcstat); #endif +#ifdef CONFIG_SYSCTL + nfs_sysctl_register(); +#endif if ((err = register_nfs_fs()) != 0) goto out; return 0; out: +#ifdef CONFIG_SYSCTL + nfs_sysctl_unregister(); +#endif #ifdef CONFIG_PROC_FS rpc_proc_unregister("nfs"); #endif @@ -1490,6 +1508,9 @@ static void __exit exit_nfs_fs(void) nfs_destroy_nfspagecache(); nfs_fscache_unregister(); nfs_dns_resolver_destroy(); +#ifdef CONFIG_SYSCTL + nfs_sysctl_unregister(); +#endif #ifdef CONFIG_PROC_FS rpc_proc_unregister("nfs"); #endif diff -Nurp a/fs/nfs/sysctl_net_nfs.c b/fs/nfs/sysctl_net_nfs.c --- a/fs/nfs/sysctl_net_nfs.c 1970-01-01 01:00:00.000000000 +0100 +++ b/fs/nfs/sysctl_net_nfs.c 2010-06-16 16:51:35.000000000 +0200 @@ -0,0 +1,111 @@ +/* + * Sysctl interface to NFS. + * + * Authors: Frank van Maarseveen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * + * /proc/sys/net/nfs/noac-timeout + * When a nonzero value is written, suspend atribute caching for the current + * working directory and one level of files inside for the specified number + * of seconds. Attribute caching will automatically be enabled when the time + * elapses. Writing a zero re-enables attribute caching as well. Reading + * yields the number of remaining seconds attribute caching will be disabled. + * + * Notes: + * - sys_sysctl() not supported, only /proc/sys + */ + +#include +#include +#include +#include +#include + +static int sysctl_noac_timeout; /* not used */ +static struct ctl_table_header * nfs_sysctl_header; + +static int proc_noac_timeout(ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int noac_timeout, error; + struct inode *inode; + struct nfs_inode *nfsi; + struct ctl_table ctl; + + inode = current->fs->pwd.dentry->d_inode; + if (inode->i_sb->s_magic != NFS_SUPER_MAGIC) + return -ENOSYS; + nfsi = NFS_I(inode); + + ctl = *table; + ctl.data = &noac_timeout; + + /* check for attribute cache reactivation */ + if ( nfsi->noac_timeout + && nfsi->attrtimeo == 0 + && time_after(jiffies, nfsi->noac_timeout) + ) + { + nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); + nfsi->noac_timeout = 0; + } + + noac_timeout = nfsi->noac_timeout; + if (noac_timeout) + noac_timeout -= jiffies; + error = proc_dointvec_jiffies(&ctl, write, buffer, lenp, ppos); + if (write && !error) { + if (noac_timeout) { + nfsi->noac_timeout = jiffies + noac_timeout; + nfsi->attrtimeo = 0; + } else { + nfsi->noac_timeout = 0; + nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); + } + } + return error; +} + +static ctl_table nfs_table[] = { + { + .procname = "noac-timeout", + .data = &sysctl_noac_timeout, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &proc_noac_timeout, + }, + { } +}; + +static ctl_table nfs_net_table[] = { + { + .procname = "nfs", + .mode = 0555, + .child = nfs_table, + }, + { } +}; + +static ctl_table nfs_root_table[] = { + { + .procname = "net", + .mode = 0555, + .child = nfs_net_table, + }, + { } +}; + +void nfs_sysctl_register(void) +{ + nfs_sysctl_header = register_sysctl_table(nfs_root_table); +} + +void nfs_sysctl_unregister(void) +{ + unregister_sysctl_table(nfs_sysctl_header); +} diff -Nurp a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h --- a/include/linux/nfs_fs.h 2010-06-15 11:33:33.000000000 +0200 +++ b/include/linux/nfs_fs.h 2010-06-16 16:51:35.000000000 +0200 @@ -143,6 +143,13 @@ struct nfs_inode { */ unsigned long cache_change_attribute; + /* + * attribute caching can selectively be suspended by clearing + * attrtimeo. When jiffies > noac_timeout then attribute + * caching will be reactivated. + */ + unsigned long noac_timeout; + struct rb_root access_cache; struct list_head access_cache_entry_lru; struct list_head access_cache_inode_lru; @@ -356,6 +363,12 @@ extern struct nfs_open_context *nfs_find extern u64 nfs_compat_user_ino64(u64 fileid); extern void nfs_fattr_init(struct nfs_fattr *fattr); +/* + * linux/fs/nfs/sysctl_net_nfs.c + */ +extern void nfs_sysctl_register(void); +extern void nfs_sysctl_unregister(void); + /* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */ extern __be32 root_nfs_parse_addr(char *name); /*__init*/ extern unsigned long nfs_inc_attr_generation_counter(void);