diff -dpruN Linux-2.4.3/fs/exec.c linux/fs/exec.c --- Linux-2.4.3/fs/exec.c Thu Apr 5 00:20:29 2001 +++ linux/fs/exec.c Sat Apr 7 12:45:01 2001 @@ -104,7 +104,9 @@ asmlinkage long sys_uselib(const char * struct file * file; struct nameidata nd; int error; + int retries = 1; +retry_lookup: error = user_path_walk(library, &nd); if (error) goto out; @@ -119,8 +121,16 @@ asmlinkage long sys_uselib(const char * file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); error = PTR_ERR(file); - if (IS_ERR(file)) + if (IS_ERR(file)) { + if (error == -ESTALE) { + if (retries) { + retries--; + goto retry_lookup; + } + error = -ENOENT; + } goto out; + } error = -ENOEXEC; if(file->f_op && file->f_op->read) { @@ -340,7 +350,9 @@ struct file *open_exec(const char *name) struct inode *inode; struct file *file; int err = 0; + int retries = 1; +retry_lookup: if (path_init(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd)) err = path_walk(name, &nd); file = ERR_PTR(err); @@ -358,9 +370,17 @@ struct file *open_exec(const char *name) fput(file); file = ERR_PTR(err); } - } out: - return file; + return file; + } + if (PTR_ERR(file) == -ESTALE) { + if (retries) { + retries--; + goto retry_lookup; + } + file = ERR_PTR(-ENOENT); + } + goto out; } } path_release(&nd); diff -dpruN Linux-2.4.3/fs/nfs/dir.c linux/fs/nfs/dir.c --- Linux-2.4.3/fs/nfs/dir.c Thu Apr 5 00:20:29 2001 +++ linux/fs/nfs/dir.c Fri Apr 6 19:17:06 2001 @@ -420,145 +420,6 @@ static int nfs_readdir(struct file *filp } /* - * Whenever an NFS operation succeeds, we know that the dentry - * is valid, so we update the revalidation timestamp. - */ -static inline void nfs_renew_times(struct dentry * dentry) -{ - dentry->d_time = jiffies; -} - -static inline int nfs_dentry_force_reval(struct dentry *dentry, int flags) -{ - struct inode *inode = dentry->d_inode; - unsigned long timeout = NFS_ATTRTIMEO(inode); - - /* - * If it's the last lookup in a series, we use a stricter - * cache consistency check by looking at the parent mtime. - * - * If it's been modified in the last hour, be really strict. - * (This still means that we can avoid doing unnecessary - * work on directories like /usr/share/bin etc which basically - * never change). - */ - if (!(flags & LOOKUP_CONTINUE)) { - long diff = CURRENT_TIME - dentry->d_parent->d_inode->i_mtime; - - if (diff < 15*60) - timeout = 0; - } - - return time_after(jiffies,dentry->d_time + timeout); -} - -/* - * We judge how long we want to trust negative - * dentries by looking at the parent inode mtime. - * - * If mtime is close to present time, we revalidate - * more often. - */ -#define NFS_REVALIDATE_NEGATIVE (1 * HZ) -static inline int nfs_neg_need_reval(struct dentry *dentry) -{ - struct inode *dir = dentry->d_parent->d_inode; - unsigned long timeout = NFS_ATTRTIMEO(dir); - long diff = CURRENT_TIME - dir->i_mtime; - - if (diff < 5*60 && timeout > NFS_REVALIDATE_NEGATIVE) - timeout = NFS_REVALIDATE_NEGATIVE; - - return time_after(jiffies, dentry->d_time + timeout); -} - -/* - * This is called every time the dcache has a lookup hit, - * and we should check whether we can really trust that - * lookup. - * - * NOTE! The hit can be a negative hit too, don't assume - * we have an inode! - * - * If the dentry is older than the revalidation interval, - * we do a new lookup and verify that the dentry is still - * correct. - */ -static int nfs_lookup_revalidate(struct dentry * dentry, int flags) -{ - struct inode *dir; - struct inode *inode; - int error; - struct nfs_fh fhandle; - struct nfs_fattr fattr; - - lock_kernel(); - dir = dentry->d_parent->d_inode; - inode = dentry->d_inode; - /* - * If we don't have an inode, let's look at the parent - * directory mtime to get a hint about how often we - * should validate things.. - */ - if (!inode) { - if (nfs_neg_need_reval(dentry)) - goto out_bad; - goto out_valid; - } - - if (is_bad_inode(inode)) { - dfprintk(VFS, "nfs_lookup_validate: %s/%s has dud inode\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - goto out_bad; - } - - if (!nfs_dentry_force_reval(dentry, flags)) - goto out_valid; - - if (IS_ROOT(dentry)) { - __nfs_revalidate_inode(NFS_SERVER(inode), inode); - goto out_valid_renew; - } - - /* - * Do a new lookup and check the dentry attributes. - */ - error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); - if (error) - goto out_bad; - - /* Inode number matches? */ - if (!(fattr.valid & NFS_ATTR_FATTR) || - NFS_FSID(inode) != fattr.fsid || - NFS_FILEID(inode) != fattr.fileid) - goto out_bad; - - /* Ok, remember that we successfully checked it.. */ - nfs_refresh_inode(inode, &fattr); - - if (nfs_inode_is_stale(inode, &fhandle, &fattr)) - goto out_bad; - - out_valid_renew: - nfs_renew_times(dentry); -out_valid: - unlock_kernel(); - return 1; -out_bad: - shrink_dcache_parent(dentry); - /* If we have submounts, don't unhash ! */ - if (have_submounts(dentry)) - goto out_valid; - d_drop(dentry); - /* Purge readdir caches. */ - nfs_zap_caches(dir); - if (inode && S_ISDIR(inode->i_mode)) - nfs_zap_caches(inode); - unlock_kernel(); - return 0; -} - -/* * This is called from dput() when d_count is going to 0. */ static int nfs_dentry_delete(struct dentry *dentry) @@ -590,7 +451,6 @@ static void nfs_dentry_iput(struct dentr } struct dentry_operations nfs_dentry_operations = { - d_revalidate: nfs_lookup_revalidate, d_delete: nfs_dentry_delete, d_iput: nfs_dentry_iput, }; @@ -622,7 +482,6 @@ static struct dentry *nfs_lookup(struct if (inode) { no_entry: d_add(dentry, inode); - nfs_renew_times(dentry); error = 0; } } @@ -642,7 +501,6 @@ static int nfs_instantiate(struct dentry inode = nfs_fhget(dentry, fhandle, fattr); if (inode) { d_instantiate(dentry, inode); - nfs_renew_times(dentry); error = 0; } return error; @@ -816,7 +674,6 @@ dentry->d_parent->d_name.name, dentry->d qsilly.len = strlen(silly); error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, dir, &qsilly); if (!error) { - nfs_renew_times(dentry); d_move(dentry, sdentry); error = nfs_async_unlink(dentry); /* If we return 0 we don't unlink */ @@ -897,9 +754,6 @@ static int nfs_unlink(struct inode *dir, error = nfs_sillyrename(dir, dentry); if (error && error != -EBUSY) { error = nfs_safe_remove(dentry); - if (!error) { - nfs_renew_times(dentry); - } } return error; } diff -dpruN Linux-2.4.3/fs/nfs/file.c linux/fs/nfs/file.c --- Linux-2.4.3/fs/nfs/file.c Thu Apr 5 00:19:51 2001 +++ linux/fs/nfs/file.c Fri Apr 6 19:17:19 2001 @@ -91,14 +91,13 @@ static ssize_t nfs_file_read(struct file * file, char * buf, size_t count, loff_t *ppos) { struct dentry * dentry = file->f_dentry; - struct inode * inode = dentry->d_inode; ssize_t result; dfprintk(VFS, "nfs: read(%s/%s, %lu@%lu)\n", dentry->d_parent->d_name.name, dentry->d_name.name, (unsigned long) count, (unsigned long) *ppos); - result = nfs_revalidate_inode(NFS_SERVER(inode), inode); + result = nfs_revalidate(dentry); if (!result) result = generic_file_read(file, buf, count, ppos); return result; @@ -108,13 +107,12 @@ static int nfs_file_mmap(struct file * file, struct vm_area_struct * vma) { struct dentry *dentry = file->f_dentry; - struct inode *inode = dentry->d_inode; - int status; + int status; dfprintk(VFS, "nfs: mmap(%s/%s)\n", dentry->d_parent->d_name.name, dentry->d_name.name); - status = nfs_revalidate_inode(NFS_SERVER(inode), inode); + status = nfs_revalidate(dentry); if (!status) status = generic_file_mmap(file, vma); return status; @@ -224,7 +222,7 @@ nfs_file_write(struct file *file, const result = -EBUSY; if (IS_SWAPFILE(inode)) goto out_swapfile; - result = nfs_revalidate_inode(NFS_SERVER(inode), inode); + result = nfs_revalidate(dentry); if (result) goto out; diff -dpruN Linux-2.4.3/fs/nfs/inode.c linux/fs/nfs/inode.c --- Linux-2.4.3/fs/nfs/inode.c Thu Apr 5 00:20:29 2001 +++ linux/fs/nfs/inode.c Sat Apr 7 15:37:55 2001 @@ -627,27 +627,6 @@ nfs_find_actor(struct inode *inode, unsi return 1; } -int -nfs_inode_is_stale(struct inode *inode, struct nfs_fh *fh, struct nfs_fattr *fattr) -{ - /* Empty inodes are not stale */ - if (!inode->i_mode) - return 0; - - if ((fattr->mode & S_IFMT) != (inode->i_mode & S_IFMT)) - return 1; - - if (is_bad_inode(inode)) - return 1; - - /* Has the filehandle changed? If so is the old one stale? */ - if (memcmp(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)) != 0 && - __nfs_revalidate_inode(NFS_SERVER(inode),inode) == -ESTALE) - return 1; - - return 0; -} - /* * This is our own version of iget that looks up inodes by file handle * instead of inode number. We use this technique instead of using @@ -775,35 +754,46 @@ nfs_wait_on_inode(struct inode *inode, i } /* - * Externally visible revalidation function + * Soft revalidation -- if the attributes were checked recently, + * pretend they are up to date. Everyone except nfs_open uses this. */ int nfs_revalidate(struct dentry *dentry) { struct inode *inode = dentry->d_inode; - return nfs_revalidate_inode(NFS_SERVER(inode), inode); + + if (time_before(jiffies, (NFS_READTIME(inode) + NFS_ATTRTIMEO(inode)))) + return NFS_STALE(inode) ? -ESTALE : 0; + + return __nfs_revalidate_inode(dentry); } -/* - * These are probably going to contain hooks for - * allocating and releasing RPC credentials for - * the file. I'll have to think about Tronds patch - * a bit more.. - */ -int nfs_open(struct inode *inode, struct file *filp) +int +nfs_open(struct inode *inode, struct file *filp) { + int error; struct rpc_auth *auth; struct rpc_cred *cred; - lock_kernel(); - auth = NFS_CLIENT(inode)->cl_auth; - cred = rpcauth_lookupcred(auth, 0); - filp->private_data = cred; - unlock_kernel(); - return 0; + /* + * Close-to-open cache consistency requires that open() + * check the server version of this object's attributes. + * See Callaghan's "NFS Illustrated" ss. 7.3.2 and 8.14.2 + */ + error = __nfs_revalidate_inode(filp->f_dentry); + if (!error) { + lock_kernel(); + auth = NFS_CLIENT(inode)->cl_auth; + cred = rpcauth_lookupcred(auth, 0); + filp->private_data = cred; + unlock_kernel(); + } + + return error; } -int nfs_release(struct inode *inode, struct file *filp) +int +nfs_release(struct inode *inode, struct file *filp) { struct rpc_auth *auth; struct rpc_cred *cred; @@ -817,31 +807,54 @@ int nfs_release(struct inode *inode, str return 0; } +static void +nfs_invalidate_dentry(struct dentry *dentry) +{ + struct inode * inode = dentry->d_inode; + + /* Safety first */ + if (!inode) + return; + + dfprintk(PAGECACHE, "NFS: invalidating (%x/%Ld)\n", + inode->i_dev, (long long)NFS_FILEID(inode)); + + shrink_dcache_parent(dentry); + + /* If we have submounts, don't unhash ! */ + if (have_submounts(dentry)) + return; + + d_drop(dentry); + + /* Purge readdir caches. */ + nfs_zap_caches(dentry->d_parent->d_inode); + if (inode && S_ISDIR(inode->i_mode)) + nfs_zap_caches(inode); +} + /* * This function is called whenever some part of NFS notices that * the cached attributes have to be refreshed. */ int -__nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) +__nfs_revalidate_inode(struct dentry * dentry) { - int status = 0; + int status = -ESTALE; + struct inode *inode = dentry->d_inode; struct nfs_fattr fattr; dfprintk(PAGECACHE, "NFS: revalidating (%x/%Ld)\n", inode->i_dev, (long long)NFS_FILEID(inode)); lock_kernel(); - if (!inode || is_bad_inode(inode) || NFS_STALE(inode)) { - unlock_kernel(); - return -ESTALE; - } + if (is_bad_inode(inode) || NFS_STALE(inode)) + goto out_nowait; while (NFS_REVALIDATING(inode)) { status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING); - if (status < 0) { - unlock_kernel(); - return status; - } + if (status < 0) + goto out_nowait; if (time_before(jiffies,NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) { status = NFS_STALE(inode) ? -ESTALE : 0; goto out_nowait; @@ -851,7 +864,8 @@ __nfs_revalidate_inode(struct nfs_server status = NFS_PROTO(inode)->getattr(inode, &fattr); if (status) { - dfprintk(PAGECACHE, "nfs_revalidate_inode: (%x/%Ld) getattr failed, error=%d\n", + dfprintk(PAGECACHE, __FUNCTION__ + ": (%x/%Ld) getattr failed, error=%d\n", inode->i_dev, (long long)NFS_FILEID(inode), status); if (status == -ESTALE) { NFS_FLAGS(inode) |= NFS_INO_STALE; @@ -860,18 +874,33 @@ __nfs_revalidate_inode(struct nfs_server goto out; } + /* + * When we detect a directory change on the server, + * we throw away cached lookup results. + */ + if (S_ISDIR(inode->i_mode)) + if ((NFS_CACHE_ISIZE(inode) != fattr.size) || + (NFS_CACHE_MTIME(inode) != fattr.mtime)) + shrink_dcache_parent(dentry); + status = nfs_refresh_inode(inode, &fattr); if (status) { - dfprintk(PAGECACHE, "nfs_revalidate_inode: (%x/%Ld) refresh failed, error=%d\n", + dfprintk(PAGECACHE, __FUNCTION__ + ": (%x/%Ld) refresh failed, error=%d\n", inode->i_dev, (long long)NFS_FILEID(inode), status); goto out; } + dfprintk(PAGECACHE, "NFS: (%x/%Ld) revalidation complete\n", inode->i_dev, (long long)NFS_FILEID(inode)); out: NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING; wake_up(&inode->i_wait); - out_nowait: + +out_nowait: + if (status == -ESTALE) + nfs_invalidate_dentry(dentry); + unlock_kernel(); return status; } diff -dpruN Linux-2.4.3/fs/open.c linux/fs/open.c --- Linux-2.4.3/fs/open.c Thu Apr 5 00:19:52 2001 +++ linux/fs/open.c Sat Apr 7 12:38:00 2001 @@ -614,8 +614,10 @@ asmlinkage long sys_fchown(unsigned int */ struct file *filp_open(const char * filename, int flags, int mode) { + unsigned retries = 1; int namei_flags, error; struct nameidata nd; + struct file * file; namei_flags = flags; if ((namei_flags+1) & O_ACCMODE) @@ -623,11 +625,21 @@ struct file *filp_open(const char * file if (namei_flags & O_TRUNC) namei_flags |= 2; +retry_lookup: error = open_namei(filename, namei_flags, mode, &nd); - if (!error) - return dentry_open(nd.dentry, nd.mnt, flags); + if (error) + return ERR_PTR(error); - return ERR_PTR(error); + file = dentry_open(nd.dentry, nd.mnt, flags); + if (PTR_ERR(file) != -ESTALE) + return file; + + if (retries) { + retries--; + goto retry_lookup; + } + + return ERR_PTR(-ENOENT); } struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags) diff -dpruN Linux-2.4.3/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h --- Linux-2.4.3/include/linux/nfs_fs.h Thu Jan 4 17:50:54 2001 +++ linux/include/linux/nfs_fs.h Fri Apr 6 16:38:56 2001 @@ -148,7 +148,7 @@ extern int nfs_revalidate(struct dentry extern int nfs_permission(struct inode *, int); extern int nfs_open(struct inode *, struct file *); extern int nfs_release(struct inode *, struct file *); -extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *); +extern int __nfs_revalidate_inode(struct dentry *); extern int nfs_notify_change(struct dentry *, struct iattr *); /* @@ -266,14 +266,6 @@ extern int nfs3_mount(struct sockaddr_i /* * inline functions */ -static inline int -nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) -{ - if (time_before(jiffies, NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) - return NFS_STALE(inode) ? -ESTALE : 0; - return __nfs_revalidate_inode(server, inode); -} - static inline loff_t nfs_size_to_loff_t(__u64 size) { diff -dpruN Linux-2.4.3/include/linux/sunrpc/debug.h linux/include/linux/sunrpc/debug.h --- Linux-2.4.3/include/linux/sunrpc/debug.h Thu Jan 4 17:50:47 2001 +++ linux/include/linux/sunrpc/debug.h Fri Apr 6 11:37:47 2001 @@ -49,7 +49,7 @@ extern unsigned int nfsd_debug; extern unsigned int nlm_debug; #endif -#define dprintk(args...) dfprintk(FACILITY, ## args) +#define dprintk(args...) /* */ #undef ifdebug #ifdef RPC_DEBUG