diff -Nurp a/fs/nfs/Makefile b/fs/nfs/Makefile --- a/fs/nfs/Makefile 2006-04-23 17:35:59.000000000 +0200 +++ b/fs/nfs/Makefile 2006-04-23 23:31:21.000000000 +0200 @@ -14,4 +14,5 @@ nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4x callback.o callback_xdr.o callback_proc.o nfs-$(CONFIG_NFS_DIRECTIO) += direct.o nfs-$(CONFIG_SYSCTL) += sysctl.o +nfs-$(CONFIG_SYSCTL) += sysctl_net_nfs.o nfs-objs := $(nfs-y) diff -Nurp a/fs/nfs/dir.c b/fs/nfs/dir.c --- a/fs/nfs/dir.c 2006-04-23 17:35:59.000000000 +0200 +++ b/fs/nfs/dir.c 2006-04-23 23:25:27.000000000 +0200 @@ -609,6 +609,8 @@ int nfs_fsync_dir(struct file *filp, str */ static inline int nfs_check_verifier(struct inode *dir, struct dentry *dentry) { + if (NFS_NOAC_TIMEOUT(dir)) + return 0; if (IS_ROOT(dentry)) return 1; if ((NFS_I(dir)->cache_validity & NFS_INO_INVALID_ATTR) != 0 diff -Nurp a/fs/nfs/inode.c b/fs/nfs/inode.c --- a/fs/nfs/inode.c 2006-04-23 17:35:59.000000000 +0200 +++ b/fs/nfs/inode.c 2006-04-23 23:25:27.000000000 +0200 @@ -660,7 +660,8 @@ static void nfs_zap_caches_locked(struct struct nfs_inode *nfsi = NFS_I(inode); int mode = inode->i_mode; - NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); + if (!NFS_NOAC_TIMEOUT(inode)) + NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); NFS_ATTRTIMEO_UPDATE(inode) = jiffies; memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); @@ -819,6 +820,7 @@ nfs_fhget(struct super_block *sb, struct } nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); nfsi->attrtimeo_timestamp = jiffies; + nfsi->noac_timeout = 0; memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); nfsi->cache_access.cred = NULL; @@ -1519,6 +1521,16 @@ static int nfs_update_inode(struct inode inode->i_blksize = fattr->du.nfs2.blocksize; } + /* check for attribute cache reactivation */ + if ( NFS_NOAC_TIMEOUT(inode) + && NFS_ATTRTIMEO(inode) == 0 + && time_after(jiffies, NFS_NOAC_TIMEOUT(inode)) + ) + { + NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); + NFS_NOAC_TIMEOUT(inode) = 0; + } + /* Update attrtimeo value if we're out of the unstable period */ if (invalid & NFS_INO_INVALID_ATTR) { nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); @@ -2209,6 +2221,9 @@ static int __init init_nfs_fs(void) #ifdef CONFIG_PROC_FS rpc_proc_register(&nfs_rpcstat); #endif +#ifdef CONFIG_SYSCTL + nfs_sysctl_register(); +#endif err = register_filesystem(&nfs_fs_type); if (err) goto out; @@ -2216,6 +2231,9 @@ static int __init init_nfs_fs(void) goto out; return 0; out: +#ifdef CONFIG_SYSCTL + nfs_sysctl_unregister(); +#endif #ifdef CONFIG_PROC_FS rpc_proc_unregister("nfs"); #endif @@ -2246,6 +2264,9 @@ static void __exit exit_nfs_fs(void) #ifdef CONFIG_PROC_FS rpc_proc_unregister("nfs"); #endif +#ifdef CONFIG_SYSCTL + nfs_sysctl_unregister(); +#endif unregister_filesystem(&nfs_fs_type); unregister_nfs4fs(); } 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 2006-04-23 23:25:27.000000000 +0200 @@ -0,0 +1,93 @@ +/* + * 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 + +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, struct file *filp, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int noac_timeout, error; + struct inode *inode; + struct ctl_table ctl; + + inode = current->fs->pwd->d_inode; + if (inode->i_sb->s_magic != NFS_SUPER_MAGIC) + return -ENOSYS; + + ctl = *table; + ctl.data = &noac_timeout; + + /* check for attribute cache reactivation */ + if ( NFS_NOAC_TIMEOUT(inode) + && NFS_ATTRTIMEO(inode) == 0 + && time_after(jiffies, NFS_NOAC_TIMEOUT(inode)) + ) + { + NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); + NFS_NOAC_TIMEOUT(inode) = 0; + } + + noac_timeout = NFS_NOAC_TIMEOUT(inode); + if (noac_timeout) + noac_timeout -= jiffies; + error = proc_dointvec_jiffies(&ctl, write, filp, buffer, lenp, ppos); + if (write && !error) { + if (noac_timeout) { + NFS_NOAC_TIMEOUT(inode) = jiffies + noac_timeout; + NFS_ATTRTIMEO(inode) = 0; + } else { + NFS_NOAC_TIMEOUT(inode) = 0; + NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); + } + } + return error; +} + +static ctl_table nfs_table[] = { + {NET_NFS_NOAC_TIMEOUT, "noac-timeout", &sysctl_noac_timeout, sizeof(int), 0666, NULL, &proc_noac_timeout}, + {0} +}; + +static ctl_table nfs_net_table[] = { + {NET_NFS, "nfs", NULL, 0, 0555, nfs_table}, + {0} +}; + +static ctl_table nfs_root_table[] = { + {CTL_NET, "net", NULL, 0, 0555, nfs_net_table}, + {0} +}; + +void nfs_sysctl_register(void) +{ + nfs_sysctl_header = register_sysctl_table(nfs_root_table, 0); +} + +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 2006-04-23 17:36:04.000000000 +0200 +++ b/include/linux/nfs_fs.h 2006-04-23 23:25:27.000000000 +0200 @@ -139,6 +139,13 @@ struct nfs_inode { unsigned long attrtimeo_timestamp; __u64 change_attr; /* v4 only */ + /* + * attribute caching can selectively be suspended by clearing + * attrtimeo. When jiffies > noac_timeout then attribute + * caching will be reactivated. + */ + unsigned long noac_timeout; + unsigned long last_updated; /* "Generation counter" for the attribute cache. This is * bumped whenever we update the metadata on the @@ -221,6 +228,7 @@ static inline struct nfs_inode *NFS_I(st #define NFS_READTIME(inode) (NFS_I(inode)->read_cache_jiffies) #define NFS_CHANGE_ATTR(inode) (NFS_I(inode)->change_attr) #define NFS_ATTRTIMEO(inode) (NFS_I(inode)->attrtimeo) +#define NFS_NOAC_TIMEOUT(inode) (NFS_I(inode)->noac_timeout) #define NFS_MINATTRTIMEO(inode) \ (S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmin \ : NFS_SERVER(inode)->acregmin) @@ -318,6 +326,12 @@ extern void nfs_file_set_open_context(st extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode); extern void nfs_file_clear_open_context(struct file *filp); +/* + * 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 u32 root_nfs_parse_addr(char *name); /*__init*/ diff -Nurp a/include/linux/sysctl.h b/include/linux/sysctl.h --- a/include/linux/sysctl.h 2006-04-23 17:36:04.000000000 +0200 +++ b/include/linux/sysctl.h 2006-04-23 23:25:27.000000000 +0200 @@ -211,6 +211,7 @@ enum NET_SCTP=17, NET_LLC=18, NET_NETFILTER=19, + NET_NFS=20, }; /* /proc/sys/kernel/random */ @@ -683,6 +684,11 @@ enum { NET_DECNET_DEBUG_LEVEL = 255 }; +/* /proc/sys/net/nfs/ */ +enum { + NET_NFS_NOAC_TIMEOUT = 1 +}; + /* /proc/sys/net/decnet/conf/ */ enum { NET_DECNET_CONF_LOOPBACK = -2,