commit 9d1e9232223a7f065be7f956a7b749a4cbbbe16d Author: Trond Myklebust Date: Thu May 25 01:40:46 2006 -0400 NFSv4: Some NFSv4 servers have broken behaviour for the change attribute The Linux NFSv4 server violates RFC3530 in that the change attribute is not guaranteed to be updated for every change to the inode. Our optimisation for checking whether or not the inode metadata has changed or not is broken too. Grr.... Signed-off-by: Trond Myklebust [ modified to apply on 2.6.9-89 by Chuck Lever ] [ The change to use "now" instead of "jiffies" is already applied to 2.6.9-89. ] diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 13:15:43.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 13:20:00.000000000 -0400 @@ -1370,9 +1370,8 @@ static int nfs_check_inode_attributes(st /* Do atomic weak cache consistency updates */ nfs_wcc_update_inode(inode, fattr); - if ((fattr->valid & NFS_ATTR_FATTR_V4) != 0) { - if (nfsi->change_attr == fattr->change_attr) - goto out; + if ((fattr->valid & NFS_ATTR_FATTR_V4) != 0 && + nfsi->change_attr != fattr->change_attr) { nfsi->cache_validity |= NFS_INO_INVALID_ATTR; if (!data_unstable) nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE; @@ -1400,7 +1399,6 @@ static int nfs_check_inode_attributes(st if (inode->i_nlink != fattr->nlink) nfsi->cache_validity |= NFS_INO_INVALID_ATTR; -out: if (!timespec_equal(&inode->i_atime, &fattr->atime)) nfsi->cache_validity |= NFS_INO_INVALID_ATIME; @@ -1573,15 +1571,13 @@ static int nfs_update_inode(struct inode inode->i_blksize = fattr->du.nfs2.blocksize; } - if ((fattr->valid & NFS_ATTR_FATTR_V4)) { - if (nfsi->change_attr != fattr->change_attr) { - dprintk("NFS: change_attr change on server for file %s/%ld\n", - inode->i_sb->s_id, inode->i_ino); - nfsi->change_attr = fattr->change_attr; - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; - nfsi->cache_change_attribute = now; - } else - invalid &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA); + if ((fattr->valid & NFS_ATTR_FATTR_V4) != 0 && + nfsi->change_attr != fattr->change_attr) { + dprintk("NFS: change_attr change on server for file %s/%ld\n", + inode->i_sb->s_id, inode->i_ino); + nfsi->change_attr = fattr->change_attr; + invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; + nfsi->cache_change_attribute = now; } /* Update attrtimeo value if we're out of the unstable period */ commit 73a3d07c1082145a3b78407bb5252df290470c4c Author: Trond Myklebust Date: Thu May 25 01:40:47 2006 -0400 NFS: Clean up inode metadata updates Signed-off-by: Trond Myklebust [ modified to apply on 2.6.9-89 by Chuck Lever ] [ The change from nfs_refresh_inode() to nfs_post_op_update_inode() in _nfs4_proc_link() is already applied in 2.6.9-89 ] diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-22 21:19:51.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-22 21:24:43.000000000 -0400 @@ -1328,12 +1328,6 @@ static void nfs_wcc_update_inode(struct { struct nfs_inode *nfsi = NFS_I(inode); - if ((fattr->valid & NFS_ATTR_PRE_CHANGE) != 0 - && nfsi->change_attr == fattr->pre_change_attr) { - nfsi->change_attr = fattr->change_attr; - nfsi->cache_change_attribute = jiffies; - } - /* If we have atomic WCC data, we may update some attributes */ if ((fattr->valid & NFS_ATTR_WCC) != 0) { if (timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) { @@ -1366,9 +1360,6 @@ static int nfs_check_inode_attributes(st loff_t cur_size, new_isize; int data_unstable; - if ((fattr->valid & NFS_ATTR_FATTR) == 0) - return 0; - /* Has the inode gone and changed behind our back? */ if (nfsi->fileid != fattr->fileid || (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) { @@ -1495,9 +1486,6 @@ static int nfs_update_inode(struct inode __FUNCTION__, inode->i_sb->s_id, inode->i_ino, atomic_read(&inode->i_count), fattr->valid); - if ((fattr->valid & NFS_ATTR_FATTR) == 0) - return 0; - if (nfsi->fileid != fattr->fileid) { printk(KERN_ERR "%s: inode number mismatch\n" "expected (%s/0x%Lx), got (%s/0x%Lx)\n", diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_xdr.h cel-2.6.9/include/linux/nfs_xdr.h --- linux-2.6.9/include/linux/nfs_xdr.h 2009-10-22 21:19:45.000000000 -0400 +++ cel-2.6.9/include/linux/nfs_xdr.h 2009-10-22 21:21:20.000000000 -0400 @@ -47,8 +47,7 @@ struct nfs_fattr { #define NFS_ATTR_WCC 0x0001 /* pre-op WCC data */ #define NFS_ATTR_FATTR 0x0002 /* post-op attributes */ #define NFS_ATTR_FATTR_V3 0x0004 /* NFSv3 attributes */ -#define NFS_ATTR_FATTR_V4 0x0008 -#define NFS_ATTR_PRE_CHANGE 0x0010 +#define NFS_ATTR_FATTR_V4 0x0008 /* NFSv4 change attribute */ /* * Info on the file system commit f1bb0b92ba2cdfffe6e437f7a7da53138cf08d52 Author: Trond Myklebust Date: Thu May 25 01:40:55 2006 -0400 NFS: Fix page cache revalidation Fix up a bug in the handling of NFS_INO_REVAL_PAGECACHE: make sure that nfs_update_inode() clears it when we're sure we're not racing with other updates. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 14:46:33.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 14:47:23.000000000 -0400 @@ -1373,18 +1373,12 @@ static int nfs_check_inode_attributes(st nfs_wcc_update_inode(inode, fattr); if ((fattr->valid & NFS_ATTR_FATTR_V4) != 0 && - nfsi->change_attr != fattr->change_attr) { - nfsi->cache_validity |= NFS_INO_INVALID_ATTR; - if (!data_unstable) - nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE; - } + nfsi->change_attr != fattr->change_attr) + nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; /* Verify a few of the more important attributes */ - if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) { - nfsi->cache_validity |= NFS_INO_INVALID_ATTR; - if (!data_unstable) - nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE; - } + if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) + nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; cur_size = i_size_read(inode); new_isize = nfs_size_to_loff_t(fattr->size); @@ -1426,7 +1420,6 @@ int nfs_refresh_inode(struct inode *inod if ((fattr->valid & NFS_ATTR_FATTR) == 0) return 0; spin_lock(&inode->i_lock); - nfsi->cache_validity &= ~NFS_INO_REVAL_PAGECACHE; if (time_after(fattr->time_start, nfsi->last_updated)) status = nfs_update_inode(inode, fattr); else @@ -1451,7 +1444,7 @@ int nfs_post_op_update_inode(struct inod spin_lock(&inode->i_lock); if (unlikely((fattr->valid & NFS_ATTR_FATTR) == 0)) { - nfsi->cache_validity |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS; + nfsi->cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; goto out; } status = nfs_update_inode(inode, fattr); @@ -1512,7 +1505,7 @@ static int nfs_update_inode(struct inode /* Are we racing with known updates of the metadata on the server? */ data_stable = nfs_verify_change_attribute(inode, fattr->time_start); if (data_stable) - nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME); + nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_ATIME); /* Do atomic weak cache consistency updates */ nfs_wcc_update_inode(inode, fattr); commit 38478b24e37587f1c4fedf8ac070ca54f052ed28 Author: Trond Myklebust Date: Thu May 25 01:40:57 2006 -0400 NFS: More page cache revalidation fixups Whenever the directory changes, we want to make sure that we always invalidate its page cache. Fix up update_changeattr() and nfs_mark_for_revalidate() so that they do so. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/nfs4proc.c cel-2.6.9/fs/nfs/nfs4proc.c --- linux-2.6.9/fs/nfs/nfs4proc.c 2009-10-22 21:52:20.000000000 -0400 +++ cel-2.6.9/fs/nfs/nfs4proc.c 2009-10-22 21:53:10.000000000 -0400 @@ -181,15 +181,15 @@ renew_lease(struct nfs_server *server, u spin_unlock(&clp->cl_lock); } -static void update_changeattr(struct inode *inode, struct nfs4_change_info *cinfo) +static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo) { - struct nfs_inode *nfsi = NFS_I(inode); + struct nfs_inode *nfsi = NFS_I(dir); - spin_lock(&inode->i_lock); - nfsi->cache_validity |= NFS_INO_INVALID_ATTR; + spin_lock(&dir->i_lock); + nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA; if (cinfo->before == nfsi->change_attr && cinfo->atomic) nfsi->change_attr = cinfo->after; - spin_unlock(&inode->i_lock); + spin_unlock(&dir->i_lock); } /* diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h --- linux-2.6.9/include/linux/nfs_fs.h 2009-10-22 21:52:19.000000000 -0400 +++ cel-2.6.9/include/linux/nfs_fs.h 2009-10-22 21:53:10.000000000 -0400 @@ -267,8 +267,12 @@ static inline int nfs_caches_unstable(st static inline void nfs_mark_for_revalidate(struct inode *inode) { + struct nfs_inode *nfsi = NFS_I(inode); + spin_lock(&inode->i_lock); - NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS; + nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS; + if (S_ISDIR(inode->i_mode)) + nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA; spin_unlock(&inode->i_lock); } commit 97db8f41792839a6912fd21be8b61dd6c50db58f Author: Trond Myklebust Date: Thu Sep 14 14:03:14 2006 -0400 NFS: Don't invalidate the symlink we just stuffed into the cache And slight optimisation of nfs_end_data_update(): directories never have delegations anyway. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 13:24:46.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 13:29:32.000000000 -0400 @@ -1312,13 +1312,11 @@ void nfs_end_data_update(struct inode *i { struct nfs_inode *nfsi = NFS_I(inode); - if (!nfs_have_delegation(inode, FMODE_READ)) { - /* Directories and symlinks: invalidate page cache */ - if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) { - spin_lock(&inode->i_lock); - nfsi->cache_validity |= NFS_INO_INVALID_DATA; - spin_unlock(&inode->i_lock); - } + /* Directories: invalidate page cache */ + if (S_ISDIR(inode->i_mode)) { + spin_lock(&inode->i_lock); + nfsi->cache_validity |= NFS_INO_INVALID_DATA; + spin_unlock(&inode->i_lock); } nfsi->cache_change_attribute = jiffies; atomic_dec(&nfsi->data_updates); commit b0c4fddca2bc3967381b728732a8850de35e1b20 Author: Trond Myklebust Date: Mon Feb 5 14:44:22 2007 -0800 NFS: Cleanup - avoid rereading 'jiffies' more than once in the same routine Micro-optimisations for nfs_fhget() and nfs_wcc_update_inode(). Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-22 20:29:06.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-22 20:55:28.000000000 -0400 @@ -845,6 +845,7 @@ nfs_fhget(struct super_block *sb, struct if (inode->i_state & I_NEW) { struct nfs_inode *nfsi = NFS_I(inode); + unsigned long now = jiffies; /* We set i_ino for the few things that still rely on it, * such as stat(2) */ @@ -884,7 +885,8 @@ nfs_fhget(struct super_block *sb, struct init_special_inode(inode, inode->i_mode, fattr->rdev); nfsi->read_cache_jiffies = fattr->time_start; - nfsi->last_updated = jiffies; + nfsi->last_updated = now; + nfsi->cache_change_attribute = now; inode->i_atime = fattr->atime; inode->i_mtime = fattr->mtime; inode->i_ctime = fattr->ctime; @@ -905,7 +907,7 @@ nfs_fhget(struct super_block *sb, struct inode->i_blksize = fattr->du.nfs2.blocksize; } nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); - nfsi->attrtimeo_timestamp = jiffies; + nfsi->attrtimeo_timestamp = now; memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); nfsi->access_cache = RB_ROOT; @@ -1326,6 +1328,7 @@ void nfs_end_data_update(struct inode *i static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr) { struct nfs_inode *nfsi = NFS_I(inode); + unsigned long now = jiffies; if ((fattr->valid & NFS_ATTR_PRE_CHANGE) != 0 && nfsi->change_attr == fattr->pre_change_attr) { @@ -1337,15 +1340,15 @@ static void nfs_wcc_update_inode(struct if ((fattr->valid & NFS_ATTR_WCC) != 0) { if (timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) { memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime)); - nfsi->cache_change_attribute = jiffies; + nfsi->cache_change_attribute = now; } if (timespec_equal(&inode->i_mtime, &fattr->pre_mtime)) { memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); - nfsi->cache_change_attribute = jiffies; + nfsi->cache_change_attribute = now; } if (inode->i_size == fattr->pre_size && nfsi->npages == 0) { inode->i_size = fattr->size; - nfsi->cache_change_attribute = jiffies; + nfsi->cache_change_attribute = now; } } } @@ -2161,7 +2164,6 @@ static struct inode *nfs_alloc_inode(str return NULL; nfsi->flags = 0UL; nfsi->cache_validity = 0UL; - nfsi->cache_change_attribute = jiffies; #ifdef CONFIG_NFS_V3_ACL nfsi->acl_access = ERR_PTR(-EAGAIN); nfsi->acl_default = ERR_PTR(-EAGAIN); commit 1f4eab7e7c1d90dcd8ca4d7c064ee78dfbb345eb Author: Neil Brown Date: Mon Apr 16 09:35:27 2007 +1000 NFS: Set meaningful value for fattr->time_start in readdirplus results. Don't use uninitialsed value for fattr->time_start in readdirplus results. The 'fattr' structure filled in by nfs3_decode_direct does not get a value for ->time_start set. Thus if an entry is for an inode that we already have in cache, when nfs_readdir_lookup calls nfs_fhget, it will call nfs_refresh_inode and may update the inode with out-of-date information. Directories are read a page at a time, so each page could have a different timestamp that "should" be used to set the time_start for the fattr for info in that page. However storing the timestamp per page is awkward. (We could stick in the first 4 bytes and only read 4092 bytes, but that is a bigger code change than I am interested it). This patch ignores the readdir_plus attributes if a readdir finds the information already in cache, and otherwise sets ->time_start to the time the readdir request was sent to the server. It might be nice to store - in the directory inode - the time stamp for the earliest readdir request that is still in the page cache, so that we don't ignore attribute data that we don't have to. This patch doesn't do that. Signed-off-by: Neil Brown Signed-off-by: Trond Myklebust [ modified to apply on 2.6.9-89 by Chuck Lever ] [ commit 25606656 already applied to 2.6.9-89 ] diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c --- linux-2.6.9/fs/nfs/dir.c 2009-10-27 10:36:09.000000000 -0400 +++ cel-2.6.9/fs/nfs/dir.c 2009-10-27 11:54:17.000000000 -0400 @@ -178,6 +178,8 @@ typedef struct { struct nfs_entry *entry; decode_dirent_t decode; int plus; + unsigned long timestamp; + int timestamp_valid; } nfs_readdir_descriptor_t; /* Now we cache directories properly, by stuffing the dirent @@ -217,6 +219,8 @@ int nfs_readdir_filler(nfs_readdir_descr } goto error; } + desc->timestamp = timestamp; + desc->timestamp_valid = 1; SetPageUptodate(page); spin_lock(&inode->i_lock); NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATIME; @@ -246,6 +250,10 @@ int dir_decode(nfs_readdir_descriptor_t if (IS_ERR(p)) return PTR_ERR(p); desc->ptr = p; + if (desc->timestamp_valid) + desc->entry->fattr->time_start = desc->timestamp; + else + desc->entry->fattr->valid &= ~NFS_ATTR_FATTR; return 0; } @@ -335,6 +343,10 @@ int find_dirent_page(nfs_readdir_descrip dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc->page_index); + /* If we find the page in the page_cache, we cannot be sure + * how fresh the data is, so we will ignore readdir_plus attributes. + */ + desc->timestamp_valid = 0; page = read_cache_page(inode->i_mapping, desc->page_index, (filler_t *)nfs_readdir_filler, desc); if (IS_ERR(page)) { @@ -535,6 +547,7 @@ int uncached_readdir(nfs_readdir_descrip struct rpc_cred *cred = nfs_file_cred(file); struct page *page = NULL; int status; + unsigned long timestamp; dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (unsigned long long)*desc->dir_cookie); @@ -543,6 +556,7 @@ int uncached_readdir(nfs_readdir_descrip status = -ENOMEM; goto out; } + timestamp = jiffies; status = NFS_PROTO(inode)->readdir(file->f_dentry, cred, *desc->dir_cookie, page, NFS_SERVER(inode)->dtsize, @@ -553,6 +567,8 @@ int uncached_readdir(nfs_readdir_descrip desc->page = page; desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ if (status >= 0) { + desc->timestamp = timestamp; + desc->timestamp_valid = 1; if ((status = dir_decode(desc)) == 0) desc->entry->prev_cookie = *desc->dir_cookie; } else commit e70c490810dc683fad39e57cf00e69d5f120c542 Author: Trond Myklebust Date: Wed May 9 09:00:18 2007 -0400 NFS: Remove redundant check in nfs_check_verifier() The check for nfs_attribute_timeout(dir) in nfs_check_verifier is redundant: nfs_lookup_revalidate() will already call nfs_revalidate_inode() on the parent dir when necessary. The only case where this is not done is the case of a negative dentry. Fix this case by moving up the revalidation code. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c --- linux-2.6.9/fs/nfs/dir.c 2009-10-22 20:00:55.000000000 -0400 +++ cel-2.6.9/fs/nfs/dir.c 2009-10-22 20:01:43.000000000 -0400 @@ -712,9 +712,7 @@ static inline int nfs_check_verifier(str if (IS_ROOT(dentry)) return 1; verf = (unsigned long)dentry->d_fsdata; - if ((NFS_I(dir)->cache_validity & NFS_INO_INVALID_ATTR) != 0 - || nfs_attribute_timeout(dir) - || nfs_caches_unstable(dir) + if (nfs_caches_unstable(dir) || verf != NFS_I(dir)->cache_change_attribute) return 0; return 1; @@ -805,6 +803,10 @@ static int nfs_lookup_revalidate(struct nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE); inode = dentry->d_inode; + /* Revalidate parent directory attribute cache */ + if (nfs_revalidate_inode(NFS_SERVER(dir), dir) < 0) + goto out_zap_parent; + if (!inode) { if (nfs_neg_need_reval(dir, dentry, nd)) goto out_bad; @@ -817,10 +819,6 @@ static int nfs_lookup_revalidate(struct goto out_bad; } - /* Revalidate parent directory attribute cache */ - if (nfs_revalidate_inode(NFS_SERVER(dir), dir) < 0) - goto out_zap_parent; - /* Force a full look up iff the parent directory has changed */ if (nfs_check_verifier(dir, dentry)) { if (nfs_lookup_verify_inode(inode, nd)) commit 412c77cee6d6e73fbe1dc3d67f52163efed33fc4 Author: Trond Myklebust Date: Tue Jul 3 16:10:55 2007 -0400 NFSv4: Defer inode revalidation when setting up a delegation Currently we force a synchronous call to __nfs_revalidate_inode() in nfs_inode_set_delegation(). This not only ensures that we cannot call nfs_inode_set_delegation from an asynchronous context, but it also slows down any call to open(). Signed-off-by: Trond Myklebust [ modified to apply on 2.6.9-89 by Chuck Lever ] [ white space and comments were not exactly the same ] diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/delegation.c cel-2.6.9/fs/nfs/delegation.c --- linux-2.6.9/fs/nfs/delegation.c 2009-10-23 15:11:41.000000000 -0400 +++ cel-2.6.9/fs/nfs/delegation.c 2009-10-23 15:18:03.000000000 -0400 @@ -84,10 +84,6 @@ int nfs_inode_set_delegation(struct inod struct nfs_delegation *delegation; int status = 0; - /* Ensure we first revalidate the attributes and page cache! */ - if ((nfsi->cache_validity & (NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_ATTR))) - __nfs_revalidate_inode(NFS_SERVER(inode), inode); - delegation = nfs_alloc_delegation(); if (delegation == NULL) return -ENOMEM; @@ -113,6 +109,12 @@ int nfs_inode_set_delegation(struct inod status = -EIO; } } + + /* Ensure we revalidate the attributes and page cache! */ + spin_lock(&inode->i_lock); + nfsi->cache_validity |= NFS_INO_REVAL_FORCED; + spin_unlock(&inode->i_lock); + spin_unlock(&clp->cl_lock); if (delegation != NULL) kfree(delegation); @@ -159,6 +161,12 @@ int __nfs_inode_return_delegation(struct nfsi->delegation = NULL; nfsi->delegation_state = 0; } + + /* Ensure we revalidate the attributes and page cache! */ + spin_lock(&inode->i_lock); + nfsi->cache_validity |= NFS_INO_REVAL_FORCED; + spin_unlock(&inode->i_lock); + spin_unlock(&clp->cl_lock); nfs_delegation_claim_opens(inode); up_write(&nfsi->rwsem); diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 15:12:14.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 15:12:42.000000000 -0400 @@ -1596,8 +1596,10 @@ static int nfs_update_inode(struct inode invalid &= ~NFS_INO_INVALID_DATA; if (data_stable) invalid &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME|NFS_INO_REVAL_PAGECACHE); - if (!nfs_have_delegation(inode, FMODE_READ)) + if (!nfs_have_delegation(inode, FMODE_READ) || + (nfsi->cache_validity & NFS_INO_REVAL_FORCED)) nfsi->cache_validity |= invalid; + nfsi->cache_validity &= ~NFS_INO_REVAL_FORCED; return 0; out_changed: diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h --- linux-2.6.9/include/linux/nfs_fs.h 2009-10-23 15:12:14.000000000 -0400 +++ cel-2.6.9/include/linux/nfs_fs.h 2009-10-23 15:16:49.000000000 -0400 @@ -222,6 +222,7 @@ struct nfs_inode { #define NFS_INO_INVALID_ACCESS 0x0008 /* cached access cred invalid */ #define NFS_INO_INVALID_ACL 0x0010 /* cached acls are invalid */ #define NFS_INO_REVAL_PAGECACHE 0x0020 /* must revalidate pagecache */ +#define NFS_INO_REVAL_FORCED 0x0040 /* force revalidation ignoring a delegation */ /* * Legal values of flags field commit 3062c532ad410fe0e8320566fe2879a396be6701 Author: Trond Myklebust Date: Sat Jul 14 17:36:45 2007 -0400 NFS: Use dentry->d_time to store the parent directory verifier. This will free up the d_fsdata field for other use. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c --- linux-2.6.9/fs/nfs/dir.c 2009-10-22 20:21:04.000000000 -0400 +++ cel-2.6.9/fs/nfs/dir.c 2009-10-22 20:22:10.000000000 -0400 @@ -711,7 +711,7 @@ static inline int nfs_check_verifier(str if (IS_ROOT(dentry)) return 1; - verf = (unsigned long)dentry->d_fsdata; + verf = dentry->d_time; if (nfs_caches_unstable(dir) || verf != NFS_I(dir)->cache_change_attribute) return 0; @@ -720,7 +720,7 @@ static inline int nfs_check_verifier(str static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf) { - dentry->d_fsdata = (void *)verf; + dentry->d_time = verf; } /* commit f2115dc9877d480392e48e3c83bc8cbb4b418fee Author: Trond Myklebust Date: Wed Aug 15 12:49:17 2007 -0400 NFS: Fix over-conservative attribute invalidation in nfs_update_inode() We should always be declaring the attribute cache as valid after having updated it. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 15:22:15.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 15:23:17.000000000 -0400 @@ -1505,8 +1505,8 @@ static int nfs_update_inode(struct inode /* Are we racing with known updates of the metadata on the server? */ data_stable = nfs_verify_change_attribute(inode, fattr->time_start); - if (data_stable) - nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_ATIME); + nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ATIME + | NFS_INO_REVAL_PAGECACHE); /* Do atomic weak cache consistency updates */ nfs_wcc_update_inode(inode, fattr); @@ -1590,12 +1590,11 @@ static int nfs_update_inode(struct inode nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode); nfsi->attrtimeo_timestamp = now; } + invalid &= ~NFS_INO_INVALID_ATTR; /* Don't invalidate the data if we were to blame */ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) invalid &= ~NFS_INO_INVALID_DATA; - if (data_stable) - invalid &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME|NFS_INO_REVAL_PAGECACHE); if (!nfs_have_delegation(inode, FMODE_READ) || (nfsi->cache_validity & NFS_INO_REVAL_FORCED)) nfsi->cache_validity |= invalid; commit 68e8a70d3cae23716f6b2b3872eba10eccea148c Author: Trond Myklebust Date: Wed Aug 15 12:59:12 2007 -0400 NFS: nfs_post_op_update_inode() should call nfs_refresh_inode() Ensure that we don't clobber the results from a more recent getattr call... Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 15:26:24.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 15:27:10.000000000 -0400 @@ -1443,14 +1443,14 @@ int nfs_post_op_update_inode(struct inod struct nfs_inode *nfsi = NFS_I(inode); int status = 0; - spin_lock(&inode->i_lock); if (unlikely((fattr->valid & NFS_ATTR_FATTR) == 0)) { + spin_lock(&inode->i_lock); nfsi->cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; + spin_unlock(&inode->i_lock); goto out; } - status = nfs_update_inode(inode, fattr); + status = nfs_refresh_inode(inode, fattr); out: - spin_unlock(&inode->i_lock); return status; } commit 7957c1418f4b6c66e28d4ac3c4d7a8c19d526c48 Author: Trond Myklebust Date: Fri Sep 28 14:20:12 2007 -0400 NFS: fix nfs_verify_change_attribute We always want to check that the verifier and directory cache_change_attribute match. This also allows us to remove the 'wraparound hack' for the cache_change_attribute. If we're only checking for equality, then we don't care about wraparound issues. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-22 18:59:01.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-22 19:00:16.000000000 -0400 @@ -1559,10 +1559,6 @@ static int nfs_update_inode(struct inode nfsi->read_cache_jiffies = fattr->time_start; nfsi->last_updated = now; - /* Fix a wraparound issue with nfsi->cache_change_attribute */ - if (time_before(now, nfsi->cache_change_attribute)) - nfsi->cache_change_attribute = now - 600*HZ; - /* Are we racing with known updates of the metadata on the server? */ data_stable = nfs_verify_change_attribute(inode, fattr->time_start); if (data_stable) diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h --- linux-2.6.9/include/linux/nfs_fs.h 2009-10-22 18:58:59.000000000 -0400 +++ cel-2.6.9/include/linux/nfs_fs.h 2009-10-22 19:00:31.000000000 -0400 @@ -315,7 +315,7 @@ static inline long nfs_save_change_attri static inline int nfs_verify_change_attribute(struct inode *inode, unsigned long chattr) { return !nfs_caches_unstable(inode) - && time_after_eq(chattr, NFS_I(inode)->cache_change_attribute); + && chattr == NFS_I(inode)->cache_change_attribute; } /* commit 6ecc5e8fcad7ad64d68c098249359831331bd299 Author: Trond Myklebust Date: Fri Sep 28 14:20:33 2007 -0400 NFS: Fix dcache revalidation bugs We don't need to force a dentry lookup just because we're making changes to the directory. Don't update nfsi->cache_change_attribute in nfs_end_data_update: that overrides the NFSv3/v4 weak consistency checking that tells us our update was the only one, and that tells us the dcache is still valid. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c --- linux-2.6.9/fs/nfs/dir.c 2009-10-23 21:14:29.000000000 -0400 +++ cel-2.6.9/fs/nfs/dir.c 2009-10-23 21:15:07.000000000 -0400 @@ -707,15 +707,11 @@ int nfs_fsync_dir(struct file *filp, str */ static inline int nfs_check_verifier(struct inode *dir, struct dentry *dentry) { - unsigned long verf; - if (IS_ROOT(dentry)) return 1; - verf = dentry->d_time; - if (nfs_caches_unstable(dir) - || verf != NFS_I(dir)->cache_change_attribute) - return 0; - return 1; + if (dentry->d_time == NFS_I(dir)->cache_change_attribute) + return 1; + return 0; } static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf) diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 21:14:29.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 21:15:07.000000000 -0400 @@ -1320,7 +1320,6 @@ void nfs_end_data_update(struct inode *i nfsi->cache_validity |= NFS_INO_INVALID_DATA; spin_unlock(&inode->i_lock); } - nfsi->cache_change_attribute = jiffies; atomic_dec(&nfsi->data_updates); } commit e323ea46d95d7f8c789effd1194dfc120284dbbd Author: Trond Myklebust Date: Sun Sep 30 17:03:25 2007 -0400 NFS: nfs_wcc_update_inode: directory caches are always invalidated We must ensure that the readdir data is always invalidated whether or not the weak cache consistency data update succeeds. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-22 20:59:02.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-22 21:01:02.000000000 -0400 @@ -1344,6 +1344,8 @@ static void nfs_wcc_update_inode(struct } if (timespec_equal(&inode->i_mtime, &fattr->pre_mtime)) { memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); + if (S_ISDIR(inode->i_mode)) + nfsi->cache_validity |= NFS_INO_INVALID_DATA; nfsi->cache_change_attribute = now; } if (inode->i_size == fattr->pre_size && nfsi->npages == 0) { commit 17cadc95372e28024be0874e67329c1862912c5d Author: Trond Myklebust Date: Thu Sep 27 10:07:31 2007 -0400 NFS: Don't force a dcache revalidation if nfs_wcc_update_inode succeeds The reason is that if the weak cache consistency update was successful, then we know that our client must be the only one that changed the directory, and we've already updated the dcache to reflect the change. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 15:31:00.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 15:31:35.000000000 -0400 @@ -1327,24 +1327,18 @@ void nfs_end_data_update(struct inode *i static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr) { struct nfs_inode *nfsi = NFS_I(inode); - unsigned long now = jiffies; /* If we have atomic WCC data, we may update some attributes */ if ((fattr->valid & NFS_ATTR_WCC) != 0) { - if (timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) { + if (timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime)); - nfsi->cache_change_attribute = now; - } if (timespec_equal(&inode->i_mtime, &fattr->pre_mtime)) { memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); if (S_ISDIR(inode->i_mode)) nfsi->cache_validity |= NFS_INO_INVALID_DATA; - nfsi->cache_change_attribute = now; } - if (inode->i_size == fattr->pre_size && nfsi->npages == 0) { + if (inode->i_size == fattr->pre_size && nfsi->npages == 0) inode->i_size = fattr->size; - nfsi->cache_change_attribute = now; - } } } @@ -1448,6 +1442,7 @@ int nfs_post_op_update_inode(struct inod if (unlikely((fattr->valid & NFS_ATTR_FATTR) == 0)) { spin_lock(&inode->i_lock); nfsi->cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; + nfsi->cache_change_attribute = jiffies; spin_unlock(&inode->i_lock); goto out; } diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/nfs4proc.c cel-2.6.9/fs/nfs/nfs4proc.c --- linux-2.6.9/fs/nfs/nfs4proc.c 2009-10-23 15:31:00.000000000 -0400 +++ cel-2.6.9/fs/nfs/nfs4proc.c 2009-10-23 15:31:35.000000000 -0400 @@ -186,9 +186,12 @@ static void update_changeattr(struct ino struct nfs_inode *nfsi = NFS_I(dir); spin_lock(&dir->i_lock); - nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA; - if (cinfo->before == nfsi->change_attr && cinfo->atomic) + if (cinfo->after != nfsi->change_attr) { + nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA; + if (!cinfo->atomic || cinfo->before != nfsi->change_attr) + nfsi->cache_change_attribute = jiffies; nfsi->change_attr = cinfo->after; + } spin_unlock(&dir->i_lock); } diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h --- linux-2.6.9/include/linux/nfs_fs.h 2009-10-23 15:31:00.000000000 -0400 +++ cel-2.6.9/include/linux/nfs_fs.h 2009-10-23 15:31:35.000000000 -0400 @@ -272,8 +272,10 @@ static inline void nfs_mark_for_revalida spin_lock(&inode->i_lock); nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS; - if (S_ISDIR(inode->i_mode)) + if (S_ISDIR(inode->i_mode)) { nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA; + nfsi->cache_change_attribute = jiffies; + } spin_unlock(&inode->i_lock); } commit 47aabaa7e45385fee4a535a6f6e523ff944e1684 Author: Trond Myklebust Date: Thu Sep 27 15:57:24 2007 -0400 NFSv4: Don't use ctime/mtime for determining when to invalidate the caches In NFSv4 we should only be looking at the change attribute. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 13:40:25.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 13:47:59.000000000 -0400 @@ -1510,6 +1510,27 @@ static int nfs_update_inode(struct inode /* Do atomic weak cache consistency updates */ nfs_wcc_update_inode(inode, fattr); + /* More cache consistency checks */ + if (!(fattr->valid & NFS_ATTR_FATTR_V4)) { + /* NFSv2/v3: Check if the mtime agrees */ + if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) { + dprintk("NFS: mtime change on server for file %s/%ld\n", + inode->i_sb->s_id, inode->i_ino); + invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; + nfsi->cache_change_attribute = now; + } + /* If ctime has changed we should definitely clear access+acl caches */ + if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) { + invalid |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; + nfsi->cache_change_attribute = now; + } + } else if (nfsi->change_attr != fattr->change_attr) { + dprintk("NFS: change_attr change on server for file %s/%ld\n", + inode->i_sb->s_id, inode->i_ino); + invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; + nfsi->cache_change_attribute = now; + } + /* Check if our cached file size is stale */ new_isize = nfs_size_to_loff_t(fattr->size); cur_isize = i_size_read(inode); @@ -1531,22 +1552,11 @@ static int nfs_update_inode(struct inode inode->i_sb->s_id, inode->i_ino); } - /* Check if the mtime agrees */ - if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) { - memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); - dprintk("NFS: mtime change on server for file %s/%ld\n", - inode->i_sb->s_id, inode->i_ino); - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; - nfsi->cache_change_attribute = now; - } - /* If ctime has changed we should definitely clear access+acl caches */ - if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) { - invalid |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; - memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime)); - nfsi->cache_change_attribute = now; - } + memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); + memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime)); memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime)); + nfsi->change_attr = fattr->change_attr; if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO) || inode->i_uid != fattr->uid || @@ -1569,15 +1579,6 @@ static int nfs_update_inode(struct inode inode->i_blksize = fattr->du.nfs2.blocksize; } - if ((fattr->valid & NFS_ATTR_FATTR_V4) != 0 && - nfsi->change_attr != fattr->change_attr) { - dprintk("NFS: change_attr change on server for file %s/%ld\n", - inode->i_sb->s_id, inode->i_ino); - nfsi->change_attr = fattr->change_attr; - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; - nfsi->cache_change_attribute = now; - } - /* Update attrtimeo value if we're out of the unstable period */ if (invalid & NFS_INO_INVALID_ATTR) { nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE); commit b64e8a5ef758888cb42b7c105dcfaaf51aab1baf Author: Trond Myklebust Date: Sun Sep 30 15:13:17 2007 -0400 NFS: Remove bogus check of cache_change_attribute in nfs_update_inode Remove the bogus 'data_stable' check in nfs_update_inode. The cache_change_attribute tells you if the directory changed on the server, and should have nothing to do with the file length. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 13:55:59.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 14:00:22.000000000 -0400 @@ -1475,7 +1475,6 @@ static int nfs_update_inode(struct inode loff_t cur_isize, new_isize; unsigned int invalid = 0; unsigned long now = jiffies; - int data_stable; dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n", __FUNCTION__, inode->i_sb->s_id, inode->i_ino, @@ -1502,8 +1501,6 @@ static int nfs_update_inode(struct inode nfsi->read_cache_jiffies = fattr->time_start; nfsi->last_updated = now; - /* Are we racing with known updates of the metadata on the server? */ - data_stable = nfs_verify_change_attribute(inode, fattr->time_start); nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ATIME | NFS_INO_REVAL_PAGECACHE); @@ -1535,15 +1532,9 @@ static int nfs_update_inode(struct inode new_isize = nfs_size_to_loff_t(fattr->size); cur_isize = i_size_read(inode); if (new_isize != cur_isize) { - /* Do we perhaps have any outstanding writes? */ - if (nfsi->npages == 0) { - /* No, but did we race with nfs_end_data_update()? */ - if (data_stable) { - inode->i_size = new_isize; - invalid |= NFS_INO_INVALID_DATA; - } - invalid |= NFS_INO_INVALID_ATTR; - } else if (new_isize > cur_isize) { + /* Do we perhaps have any outstanding writes, or has + * the file grown beyond our last write? */ + if (nfsi->npages == 0 || new_isize > cur_isize) { inode->i_size = new_isize; invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; } commit 4b841736bc16b320bcdb1e8ece585b3ced9a8811 Author: Trond Myklebust Date: Sat Sep 29 17:15:01 2007 -0400 NFS: Fix nfs_verify_change_attribute() We don't care about whether or not some other process on our client is changing the directory while we're in nfs_lookup_revalidate(), because the dcache will take care of ensuring local atomicity. We can therefore remove the test for nfs_caches_unstable(). Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c --- linux-2.6.9/fs/nfs/dir.c 2009-10-23 21:22:17.000000000 -0400 +++ cel-2.6.9/fs/nfs/dir.c 2009-10-23 21:22:51.000000000 -0400 @@ -709,7 +709,7 @@ static inline int nfs_check_verifier(str { if (IS_ROOT(dentry)) return 1; - if (dentry->d_time == NFS_I(dir)->cache_change_attribute) + if (nfs_verify_change_attribute(dir, dentry->d_time)) return 1; return 0; } diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h --- linux-2.6.9/include/linux/nfs_fs.h 2009-10-23 21:22:17.000000000 -0400 +++ cel-2.6.9/include/linux/nfs_fs.h 2009-10-23 21:23:22.000000000 -0400 @@ -313,16 +313,16 @@ static inline long nfs_save_change_attri } /** - * nfs_verify_change_attribute - Detects NFS inode cache updates - * @inode - pointer to inode + * nfs_verify_change_attribute - Detects NFS remote directory changes + * @dir - pointer to parent directory inode * @chattr - previously saved change attribute - * Return "false" if metadata has been updated (or is in the process of - * being updated) since the change attribute was saved. + * Return "false" if the verifiers doesn't match the change attribute. + * This would usually indicate that the directory contents have changed on + * the server, and that any dentries need revalidating. */ -static inline int nfs_verify_change_attribute(struct inode *inode, unsigned long chattr) +static inline int nfs_verify_change_attribute(struct inode *dir, unsigned long chattr) { - return !nfs_caches_unstable(inode) - && chattr == NFS_I(inode)->cache_change_attribute; + return chattr == NFS_I(dir)->cache_change_attribute; } /* commit 2f78e4313afd34a4ded591ec5687843113fbaa01 Author: Trond Myklebust Date: Sun Sep 30 15:31:19 2007 -0400 NFS: Don't set cache_change_attribute in nfs_revalidate_mapping The attribute revalidation code will already have taken care of resetting nfsi->cache_change_attribute. Signed-off-by: Trond Myklebust [ modified to apply on 2.6.9-89 by Chuck Lever ] [ 2.6.9-89 missing commit 717d44e8 and pre-requisites ] diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-11-17 16:32:06.000000000 -0500 +++ cel-2.6.9/fs/nfs/inode.c 2009-11-17 16:33:38.000000000 -0500 @@ -1280,11 +1280,8 @@ void nfs_revalidate_mapping(struct inode spin_lock(&inode->i_lock); nfsi->cache_validity &= ~NFS_INO_INVALID_DATA; - if (S_ISDIR(inode->i_mode)) { + if (S_ISDIR(inode->i_mode)) memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); - /* This ensures we revalidate child dentries */ - nfsi->cache_change_attribute = jiffies; - } spin_unlock(&inode->i_lock); dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n", commit 12b373ebf05485d4937dd63a00c16f8efeaa79ba Author: Trond Myklebust Date: Mon Oct 1 09:56:59 2007 -0400 NFS: Don't revalidate dentries on directory size or ctime changes We only need to look at the mtime changes... Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 14:02:50.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 14:11:08.000000000 -0400 @@ -1517,10 +1517,8 @@ static int nfs_update_inode(struct inode nfsi->cache_change_attribute = now; } /* If ctime has changed we should definitely clear access+acl caches */ - if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) { + if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) invalid |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; - nfsi->cache_change_attribute = now; - } } else if (nfsi->change_attr != fattr->change_attr) { dprintk("NFS: change_attr change on server for file %s/%ld\n", inode->i_sb->s_id, inode->i_ino); @@ -1538,7 +1536,6 @@ static int nfs_update_inode(struct inode inode->i_size = new_isize; invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; } - nfsi->cache_change_attribute = now; dprintk("NFS: isize change on server for file %s/%ld\n", inode->i_sb->s_id, inode->i_ino); } commit 7668fdbe9aaeab705d1169ac86d0d18a12906d06 Author: Trond Myklebust Date: Mon Oct 1 09:59:15 2007 -0400 NFS: nfs_post_op_update_inode don't update cache_change_attribute If nfs_post_op_update_inode fails because the server didn't return any attributes, then we let the subsequent inode revalidation update cache_change_attribute. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 15:34:21.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 16:16:13.000000000 -0400 @@ -1436,18 +1436,14 @@ int nfs_refresh_inode(struct inode *inod int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr) { struct nfs_inode *nfsi = NFS_I(inode); - int status = 0; - if (unlikely((fattr->valid & NFS_ATTR_FATTR) == 0)) { - spin_lock(&inode->i_lock); - nfsi->cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; - nfsi->cache_change_attribute = jiffies; - spin_unlock(&inode->i_lock); - goto out; - } - status = nfs_refresh_inode(inode, fattr); -out: - return status; + if (fattr->valid & NFS_ATTR_FATTR) + return nfs_refresh_inode(inode, fattr); + + spin_lock(&inode->i_lock); + nfsi->cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; + spin_unlock(&inode->i_lock); + return 0; } /* commit f38211100d4823be530577dc3452f838861222ec Author: Trond Myklebust Date: Mon Oct 1 10:00:23 2007 -0400 NFS: nfs_mark_for_revalidate don't update cache_change_attribute Just let the subsequent inode revalidation do the update... Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h --- linux-2.6.9/include/linux/nfs_fs.h 2009-11-17 16:40:45.000000000 -0500 +++ cel-2.6.9/include/linux/nfs_fs.h 2009-11-17 16:41:31.000000000 -0500 @@ -272,10 +272,8 @@ static inline void nfs_mark_for_revalida spin_lock(&inode->i_lock); nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS; - if (S_ISDIR(inode->i_mode)) { + if (S_ISDIR(inode->i_mode)) nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA; - nfsi->cache_change_attribute = jiffies; - } spin_unlock(&inode->i_lock); } commit a1643a92f6de92074116922a2d2906dd33499ff4 Author: Trond Myklebust Date: Sat Sep 29 17:25:43 2007 -0400 NFS: NFS_CACHEINV() should not test for nfs_caches_unstable() The fact that we're in the process of modifying the inode does not mean that we should not invalidate the attribute and data caches. The defensive thing is to always invalidate when we're confronted with inode mtime/ctime or change_attribute updates that we do not immediately recognise. Signed-off-by: Trond Myklebust [ modified to apply on 2.6.9-89 by Chuck Lever ] [ commit 864472e9 does not apply to 2.6.9-89 ] diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c --- linux-2.6.9/fs/nfs/dir.c 2009-10-27 15:03:46.000000000 -0400 +++ cel-2.6.9/fs/nfs/dir.c 2009-10-27 15:04:59.000000000 -0400 @@ -874,7 +874,7 @@ static int nfs_lookup_revalidate(struct out_zap_parent: nfs_zap_caches(dir); out_bad: - NFS_CACHEINV(dir); + nfs_mark_for_revalidate(dir); if (inode && S_ISDIR(inode->i_mode)) { /* Purge readdir caches. */ nfs_zap_caches(inode); diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/nfs4proc.c cel-2.6.9/fs/nfs/nfs4proc.c --- linux-2.6.9/fs/nfs/nfs4proc.c 2009-10-27 15:03:46.000000000 -0400 +++ cel-2.6.9/fs/nfs/nfs4proc.c 2009-10-27 15:05:12.000000000 -0400 @@ -246,8 +246,6 @@ static int _nfs4_open_reclaim(struct nfs } } clear_bit(NFS_DELEGATED_STATE, &state->flags); - /* Ensure we update the inode attributes */ - NFS_CACHEINV(inode); return status; } diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h --- linux-2.6.9/include/linux/nfs_fs.h 2009-10-27 15:03:46.000000000 -0400 +++ cel-2.6.9/include/linux/nfs_fs.h 2009-10-27 15:04:59.000000000 -0400 @@ -279,12 +279,6 @@ static inline void nfs_mark_for_revalida spin_unlock(&inode->i_lock); } -static inline void NFS_CACHEINV(struct inode *inode) -{ - if (!nfs_caches_unstable(inode)) - nfs_mark_for_revalidate(inode); -} - static inline int nfs_server_capable(struct inode *inode, int cap) { return NFS_SERVER(inode)->caps & cap; commit 80eb209def76d375677840800eb838abce1e6639 Author: Trond Myklebust Date: Sat Sep 29 17:34:46 2007 -0400 NFS: Remove NFS_I(inode)->data_updates We have no more users... Signed-off-by: Trond Myklebust [ modified to apply on 2.6.9-89 by Chuck Lever ] [ 2.6.9-89 is missing commits 2aefa104 and da6d503a; 2.6.9-89 still caches NFS LOOKUP results ] diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c --- linux-2.6.9/fs/nfs/dir.c 2009-10-27 15:42:19.000000000 -0400 +++ cel-2.6.9/fs/nfs/dir.c 2009-10-27 15:43:29.000000000 -0400 @@ -1256,9 +1256,7 @@ int nfs_cached_lookup(struct inode *dir, return -ENOENT; server = NFS_SERVER(dir); /* Don't use readdirplus unless the cache is stable */ - if ((server->flags & NFS_MOUNT_NOAC) != 0 - || nfs_caches_unstable(dir) - || nfs_attribute_timeout(dir)) + if (nfs_attribute_timeout(dir)) return -ENOENT; if ((NFS_I(dir)->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA)) != 0) return -ENOENT; diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-27 15:42:19.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-27 15:42:50.000000000 -0400 @@ -195,7 +195,6 @@ nfs_clear_inode(struct inode *inode) nfs_wb_all(inode); BUG_ON (!list_empty(&nfsi->open_files)); - BUG_ON(atomic_read(&nfsi->data_updates) != 0); nfs_zap_acl_cache(inode); nfs_access_zap_cache(inode); } @@ -1294,16 +1293,6 @@ void nfs_revalidate_mapping(struct inode } /** - * nfs_begin_data_update - * @inode - pointer to inode - * Declare that a set of operations will update file data on the server - */ -void nfs_begin_data_update(struct inode *inode) -{ - atomic_inc(&NFS_I(inode)->data_updates); -} - -/** * nfs_end_data_update * @inode - pointer to inode * Declare end of the operations that will update file data @@ -1312,15 +1301,12 @@ void nfs_begin_data_update(struct inode */ void nfs_end_data_update(struct inode *inode) { - struct nfs_inode *nfsi = NFS_I(inode); - /* Directories: invalidate page cache */ if (S_ISDIR(inode->i_mode)) { spin_lock(&inode->i_lock); - nfsi->cache_validity |= NFS_INO_INVALID_DATA; + NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA; spin_unlock(&inode->i_lock); } - atomic_dec(&nfsi->data_updates); } static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr) @@ -1354,7 +1340,6 @@ static int nfs_check_inode_attributes(st { struct nfs_inode *nfsi = NFS_I(inode); loff_t cur_size, new_isize; - int data_unstable; /* Has the inode gone and changed behind our back? */ if (nfsi->fileid != fattr->fileid @@ -1362,9 +1347,6 @@ static int nfs_check_inode_attributes(st return -EIO; } - /* Are we in the process of updating data on the server? */ - data_unstable = nfs_caches_unstable(inode); - /* Do atomic weak cache consistency updates */ nfs_wcc_update_inode(inode, fattr); @@ -2144,7 +2126,6 @@ static void init_once(void * foo, kmem_c INIT_LIST_HEAD(&nfsi->access_cache_entry_lru); INIT_LIST_HEAD(&nfsi->access_cache_inode_lru); INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC); - atomic_set(&nfsi->data_updates, 0); nfsi->ndirty = 0; nfsi->ncommit = 0; nfsi->npages = 0; diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h --- linux-2.6.9/include/linux/nfs_fs.h 2009-10-27 15:42:19.000000000 -0400 +++ cel-2.6.9/include/linux/nfs_fs.h 2009-10-27 15:42:50.000000000 -0400 @@ -165,11 +165,6 @@ struct nfs_inode { * server. */ unsigned long cache_change_attribute; - /* - * Counter indicating the number of outstanding requests that - * will cause a file data update. - */ - atomic_t data_updates; struct rb_root access_cache; struct list_head access_cache_entry_lru; @@ -261,9 +256,13 @@ static inline struct nfs_inode *NFS_I(st #define NFS_FILEID(inode) (NFS_I(inode)->fileid) -static inline int nfs_caches_unstable(struct inode *inode) +/** + * nfs_begin_data_update + * @inode - pointer to inode + * Declare that a set of operations will update file data on the server + */ +static inline void nfs_begin_data_update(struct inode *inode) { - return atomic_read(&NFS_I(inode)->data_updates) != 0; } static inline void nfs_mark_for_revalidate(struct inode *inode) @@ -343,7 +342,6 @@ extern int nfs_setattr(struct dentry *, extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr); extern void nfs_begin_attr_update(struct inode *); extern void nfs_end_attr_update(struct inode *); -extern void nfs_begin_data_update(struct inode *); extern void nfs_end_data_update(struct inode *); extern void nfs_end_data_update_defer(struct inode *); extern struct nfs_open_context *alloc_nfs_open_context(struct vfsmount *mnt, struct dentry *dentry, struct rpc_cred *cred); commit 60ccd4ec4170c9487e3792322626acd160197bce Author: Trond Myklebust Date: Sat Sep 29 17:48:19 2007 -0400 NFS: Remove nfs_begin_data_update/nfs_end_data_update The lower level routines in fs/nfs/proc.c, fs/nfs/nfs3proc.c and fs/nfs/nfs4proc.c should already be dealing with the revalidation issues. Signed-off-by: Trond Myklebust [ modified to apply on 2.6.9-89 by Chuck Lever ] [ 2.6.9-89 missing commit 02a913a7 (361 lines); 2.6.9-89 missing incompatible changes to symlink, direct write; 2.6.9-89 missing incompatible changes to async unlink (pre-git) ] diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c --- linux-2.6.9/fs/nfs/dir.c 2009-10-23 17:11:54.000000000 -0400 +++ cel-2.6.9/fs/nfs/dir.c 2009-10-23 17:24:48.000000000 -0400 @@ -1060,12 +1060,7 @@ static struct dentry *nfs_atomic_lookup( goto out; } - if (nd->intent.open.flags & O_CREAT) { - nfs_begin_data_update(dir); - inode = nfs4_atomic_open(dir, dentry, nd); - nfs_end_data_update(dir); - } else - inode = nfs4_atomic_open(dir, dentry, nd); + inode = nfs4_atomic_open(dir, dentry, nd); unlock_kernel(); if (IS_ERR(inode)) { error = PTR_ERR(inode); @@ -1344,9 +1339,7 @@ static int nfs_create(struct inode *dir, * does not pass the create flags. */ lock_kernel(); - nfs_begin_data_update(dir); error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags); - nfs_end_data_update(dir); if (error != 0) goto out_err; nfs_renew_times(dentry); @@ -1379,9 +1372,7 @@ nfs_mknod(struct inode *dir, struct dent attr.ia_valid = ATTR_MODE; lock_kernel(); - nfs_begin_data_update(dir); status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev); - nfs_end_data_update(dir); if (status != 0) goto out_err; nfs_renew_times(dentry); @@ -1409,9 +1400,7 @@ static int nfs_mkdir(struct inode *dir, attr.ia_mode = mode | S_IFDIR; lock_kernel(); - nfs_begin_data_update(dir); error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr); - nfs_end_data_update(dir); if (error != 0) goto out_err; nfs_renew_times(dentry); @@ -1433,12 +1422,10 @@ static int nfs_rmdir(struct inode *dir, dir->i_ino, dentry->d_name.name); lock_kernel(); - nfs_begin_data_update(dir); error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name); /* Ensure the VFS deletes this inode */ if (error == 0 && dentry->d_inode != NULL) dentry->d_inode->i_nlink = 0; - nfs_end_data_update(dir); unlock_kernel(); return error; @@ -1501,16 +1488,12 @@ dentry->d_parent->d_name.name, dentry->d qsilly.name = silly; qsilly.len = strlen(silly); - nfs_begin_data_update(dir); - if (dentry->d_inode) { - nfs_begin_data_update(dentry->d_inode); + if (dentry->d_inode) error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, dir, &qsilly); - nfs_end_data_update(dentry->d_inode); - } else + else error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, dir, &qsilly); - nfs_end_data_update(dir); if (!error) { nfs_renew_times(dentry); nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); @@ -1545,18 +1528,14 @@ static int nfs_safe_remove(struct dentry goto out; } - nfs_begin_data_update(dir); if (inode != NULL) { nfs_inode_return_delegation(inode); - nfs_begin_data_update(inode); error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); /* The VFS may want to delete this inode */ if (error == 0) inode->i_nlink--; - nfs_end_data_update(inode); } else error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); - nfs_end_data_update(dir); out: return error; } @@ -1628,10 +1607,8 @@ dentry->d_parent->d_name.name, dentry->d qsymname.len = strlen(symname); lock_kernel(); - nfs_begin_data_update(dir); error = NFS_PROTO(dir)->symlink(dir, &dentry->d_name, &qsymname, &attr, &sym_fh, &sym_attr); - nfs_end_data_update(dir); if (!error) { error = nfs_instantiate(dentry, &sym_fh, &sym_attr); } else { @@ -1663,11 +1640,7 @@ nfs_link(struct dentry *old_dentry, stru lock_kernel(); d_drop(dentry); - nfs_begin_data_update(dir); - nfs_begin_data_update(inode); error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name); - nfs_end_data_update(inode); - nfs_end_data_update(dir); unlock_kernel(); return error; } @@ -1776,14 +1749,8 @@ go_ahead: d_delete(new_dentry); } - nfs_begin_data_update(old_dir); - nfs_begin_data_update(new_dir); - nfs_begin_data_update(old_inode); error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); - nfs_end_data_update(old_inode); - nfs_end_data_update(new_dir); - nfs_end_data_update(old_dir); out: if (rehash) d_rehash(rehash); diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/direct.c cel-2.6.9/fs/nfs/direct.c --- linux-2.6.9/fs/nfs/direct.c 2009-10-23 17:11:30.000000000 -0400 +++ cel-2.6.9/fs/nfs/direct.c 2009-10-23 17:17:16.000000000 -0400 @@ -446,8 +446,6 @@ static void nfs_direct_write_result(stru } spin_unlock(&dreq->lock); - - nfs_end_data_update(data->inode); nfs_direct_complete(dreq); } @@ -541,8 +539,6 @@ static ssize_t nfs_direct_write(struct k if (!is_sync_kiocb(iocb)) dreq->iocb = iocb; - nfs_begin_data_update(inode); - rpc_clnt_sigmask(clnt, &oldset); nfs_direct_write_schedule(dreq, user_addr, count, pos); result = nfs_direct_wait(dreq, clnt->cl_intr); @@ -702,10 +698,6 @@ ssize_t nfs_file_direct_write(struct kio pos, pages, page_count); /* - * XXX: nfs_end_data_update() already ensures this file's - * cached data is subsequently invalidated. Do we really - * need to call invalidate_inode_pages2() again here? - * * For aio writes, this invalidation will almost certainly * occur before the writes complete. Kind of racey. */ diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 17:11:54.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 17:18:06.000000000 -0400 @@ -948,7 +948,6 @@ nfs_setattr(struct dentry *dentry, struc return 0; lock_kernel(); - nfs_begin_data_update(inode); /* Write all dirty data */ if (S_ISREG(inode->i_mode)) { filemap_write_and_wait(inode->i_mapping); @@ -957,7 +956,6 @@ nfs_setattr(struct dentry *dentry, struc error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr); if (error == 0) nfs_refresh_inode(inode, &fattr); - nfs_end_data_update(inode); unlock_kernel(); return error; } @@ -1292,23 +1290,6 @@ void nfs_revalidate_mapping(struct inode } } -/** - * nfs_end_data_update - * @inode - pointer to inode - * Declare end of the operations that will update file data - * This will mark the inode as immediately needing revalidation - * of its attribute cache. - */ -void nfs_end_data_update(struct inode *inode) -{ - /* Directories: invalidate page cache */ - if (S_ISDIR(inode->i_mode)) { - spin_lock(&inode->i_lock); - NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA; - spin_unlock(&inode->i_lock); - } -} - static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr) { struct nfs_inode *nfsi = NFS_I(inode); diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/nfs3acl.c cel-2.6.9/fs/nfs/nfs3acl.c --- linux-2.6.9/fs/nfs/nfs3acl.c 2009-10-23 17:11:50.000000000 -0400 +++ cel-2.6.9/fs/nfs/nfs3acl.c 2009-10-23 17:19:03.000000000 -0400 @@ -307,13 +307,11 @@ static int nfs3_proc_setacls(struct inod dprintk("NFS call setacl\n"); nfs_fattr_init(&fattr); - nfs_begin_data_update(inode); status = rpc_call(server->client_acl, ACLPROC3_SETACL, &args, &fattr, 0); spin_lock(&inode->i_lock); NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ACCESS; spin_unlock(&inode->i_lock); - nfs_end_data_update(inode); dprintk("NFS reply setacl: %d\n", status); /* pages may have been allocated at the xdr layer. */ diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/unlink.c cel-2.6.9/fs/nfs/unlink.c --- linux-2.6.9/fs/nfs/unlink.c 2009-10-23 17:11:30.000000000 -0400 +++ cel-2.6.9/fs/nfs/unlink.c 2009-10-23 17:20:05.000000000 -0400 @@ -104,11 +104,6 @@ nfs_async_unlink_init(struct rpc_task *t status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name); if (status < 0) goto out_err; - /* - * We should really have surrounded the RPC call with a call to - * nfs_begin_data_update()/nfs_end_data_update(), but that may - * cause problems at umount time. - */ rpc_call_setup(task, &msg, 0); return; out_err: diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/write.c cel-2.6.9/fs/nfs/write.c --- linux-2.6.9/fs/nfs/write.c 2009-10-23 17:11:35.000000000 -0400 +++ cel-2.6.9/fs/nfs/write.c 2009-10-23 17:21:03.000000000 -0400 @@ -175,7 +175,6 @@ static int nfs_writepage_sync(struct nfs (long long)NFS_FILEID(inode), count, (long long)(page_offset(page) + offset)); - nfs_begin_data_update(inode); do { if (count < wsize) wdata->args.count = count; @@ -207,8 +206,6 @@ static int nfs_writepage_sync(struct nfs ClearPageError(page); io_error: - nfs_end_data_update(inode); - kfree(wdata); return written ? written : result; } @@ -381,7 +378,6 @@ static int nfs_inode_add_request(struct return error; if (!nfsi->npages) { igrab(inode); - nfs_begin_data_update(inode); if (nfs_have_delegation(inode, FMODE_WRITE)) nfsi->change_attr++; } @@ -405,7 +401,6 @@ static void nfs_inode_remove_request(str nfsi->npages--; if (!nfsi->npages) { spin_unlock(&nfsi->req_lock); - nfs_end_data_update(inode); iput(inode); } else spin_unlock(&nfsi->req_lock); diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h --- linux-2.6.9/include/linux/nfs_fs.h 2009-10-23 17:11:54.000000000 -0400 +++ cel-2.6.9/include/linux/nfs_fs.h 2009-10-23 17:29:43.000000000 -0400 @@ -256,15 +256,6 @@ static inline struct nfs_inode *NFS_I(st #define NFS_FILEID(inode) (NFS_I(inode)->fileid) -/** - * nfs_begin_data_update - * @inode - pointer to inode - * Declare that a set of operations will update file data on the server - */ -static inline void nfs_begin_data_update(struct inode *inode) -{ -} - static inline void nfs_mark_for_revalidate(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); @@ -346,10 +337,6 @@ extern int __nfs_revalidate_inode(struct extern void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping); extern int nfs_setattr(struct dentry *, struct iattr *); extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr); -extern void nfs_begin_attr_update(struct inode *); -extern void nfs_end_attr_update(struct inode *); -extern void nfs_end_data_update(struct inode *); -extern void nfs_end_data_update_defer(struct inode *); extern struct nfs_open_context *alloc_nfs_open_context(struct vfsmount *mnt, struct dentry *dentry, struct rpc_cred *cred); extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx); extern void put_nfs_open_context(struct nfs_open_context *ctx); commit 6d2b2966869142660f46d1e06cf9d15c3debcf77 Author: Trond Myklebust Date: Mon Oct 1 18:57:50 2007 -0400 NFS: Reset nfsi->last_updated only if the attribute changed Otherwise set it to nfsi->read_cache_jiffies in order to prevent jiffy wraparound issues. Signed-off-by: Trond Myklebust [ modified to apply on 2.6.9-89 by Chuck Lever ] [ commit 64672d55 (12/23/08) already applied to 2.6.9-89 ] diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-26 19:52:19.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-26 19:54:20.000000000 -0400 @@ -1470,7 +1470,6 @@ static int nfs_update_inode(struct inode * Update the read time so we don't revalidate too often. */ nfsi->read_cache_jiffies = fattr->time_start; - nfsi->last_updated = now; nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ATIME | NFS_INO_REVAL_PAGECACHE); @@ -1543,11 +1542,18 @@ static int nfs_update_inode(struct inode nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE); nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); nfsi->attrtimeo_timestamp = now; - } else if (!time_in_range_open(now, nfsi->attrtimeo_timestamp, - nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) { - if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode)) - nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode); - nfsi->attrtimeo_timestamp = now; + nfsi->last_updated = now; + } else { + if (!time_in_range_open(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) { + if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode)) + nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode); + nfsi->attrtimeo_timestamp = now; + } + /* + * Avoid jiffy wraparound issues with nfsi->last_updated + */ + if (!time_in_range(nfsi->last_updated, nfsi->read_cache_jiffies, now)) + nfsi->last_updated = nfsi->read_cache_jiffies; } invalid &= ~NFS_INO_INVALID_ATTR; /* Don't invalidate the data if we were to blame */ commit 40d24704091c8a29a4c99d25670f1996749aea6f Author: Trond Myklebust Date: Mon Oct 8 09:24:22 2007 -0400 NFS: Fix a connectathon regression in NFSv3 and NFSv4 We're failing basic test6 against Linux servers because they lack a correct change attribute. The fix is to assume that we always want to invalidate the readdir caches when we call update_changeattr and/or nfs_post_op_update_inode on a directory. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 19:45:39.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 20:16:11.000000000 -0400 @@ -1400,11 +1400,19 @@ int nfs_post_op_update_inode(struct inod { struct nfs_inode *nfsi = NFS_I(inode); - if (fattr->valid & NFS_ATTR_FATTR) + if (fattr->valid & NFS_ATTR_FATTR) { + if (S_ISDIR(inode->i_mode)) { + spin_lock(&inode->i_lock); + nfsi->cache_validity |= NFS_INO_INVALID_DATA; + spin_unlock(&inode->i_lock); + } return nfs_refresh_inode(inode, fattr); + } spin_lock(&inode->i_lock); nfsi->cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; + if (S_ISDIR(inode->i_mode)) + nfsi->cache_validity |= NFS_INO_INVALID_DATA; spin_unlock(&inode->i_lock); return 0; } diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/nfs4proc.c cel-2.6.9/fs/nfs/nfs4proc.c --- linux-2.6.9/fs/nfs/nfs4proc.c 2009-10-23 19:45:39.000000000 -0400 +++ cel-2.6.9/fs/nfs/nfs4proc.c 2009-10-23 20:16:11.000000000 -0400 @@ -186,12 +186,10 @@ static void update_changeattr(struct ino struct nfs_inode *nfsi = NFS_I(dir); spin_lock(&dir->i_lock); - if (cinfo->after != nfsi->change_attr) { - nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA; - if (!cinfo->atomic || cinfo->before != nfsi->change_attr) - nfsi->cache_change_attribute = jiffies; - nfsi->change_attr = cinfo->after; - } + nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA; + if (!cinfo->atomic || cinfo->before != nfsi->change_attr) + nfsi->cache_change_attribute = jiffies; + nfsi->change_attr = cinfo->after; spin_unlock(&dir->i_lock); } commit 2a3f5fd45938bd86ce8faf4cb26be4f7e9ae2941 Author: Trond Myklebust Date: Mon Oct 8 14:26:13 2007 -0400 NFS: nfs_refresh_inode should clear cache_validity flags on success If the cached attributes match the ones supplied in the fattr, then assume we've revalidated the inode. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-26 19:37:13.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-26 19:38:29.000000000 -0400 @@ -1321,6 +1321,7 @@ static int nfs_check_inode_attributes(st { struct nfs_inode *nfsi = NFS_I(inode); loff_t cur_size, new_isize; + unsigned long invalid = 0; /* Has the inode gone and changed behind our back? */ if (nfsi->fileid != fattr->fileid @@ -1333,29 +1334,36 @@ static int nfs_check_inode_attributes(st if ((fattr->valid & NFS_ATTR_FATTR_V4) != 0 && nfsi->change_attr != fattr->change_attr) - nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; + invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; /* Verify a few of the more important attributes */ if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) - nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; + invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; cur_size = i_size_read(inode); new_isize = nfs_size_to_loff_t(fattr->size); if (cur_size != new_isize && nfsi->npages == 0) - nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; + invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; /* Have any file permissions changed? */ if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO) || inode->i_uid != fattr->uid || inode->i_gid != fattr->gid) - nfsi->cache_validity |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL; + invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL; /* Has the link count changed? */ if (inode->i_nlink != fattr->nlink) - nfsi->cache_validity |= NFS_INO_INVALID_ATTR; + invalid |= NFS_INO_INVALID_ATTR; if (!timespec_equal(&inode->i_atime, &fattr->atime)) - nfsi->cache_validity |= NFS_INO_INVALID_ATIME; + invalid |= NFS_INO_INVALID_ATIME; + + if (invalid != 0) + nfsi->cache_validity |= invalid; + else + nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR + | NFS_INO_INVALID_ATIME + | NFS_INO_REVAL_PAGECACHE); nfsi->read_cache_jiffies = fattr->time_start; return 0; @@ -1400,21 +1408,12 @@ int nfs_post_op_update_inode(struct inod { struct nfs_inode *nfsi = NFS_I(inode); - if (fattr->valid & NFS_ATTR_FATTR) { - if (S_ISDIR(inode->i_mode)) { - spin_lock(&inode->i_lock); - nfsi->cache_validity |= NFS_INO_INVALID_DATA; - spin_unlock(&inode->i_lock); - } - return nfs_refresh_inode(inode, fattr); - } - spin_lock(&inode->i_lock); - nfsi->cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; + nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; if (S_ISDIR(inode->i_mode)) nfsi->cache_validity |= NFS_INO_INVALID_DATA; spin_unlock(&inode->i_lock); - return 0; + return nfs_refresh_inode(inode, fattr); } /* @@ -1433,7 +1432,7 @@ static int nfs_update_inode(struct inode { struct nfs_inode *nfsi = NFS_I(inode); loff_t cur_isize, new_isize; - unsigned int invalid = 0; + unsigned long invalid = 0; unsigned long now = jiffies; dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n", commit bfc69a456642a51c89dfd8e5184468857cb44f32 Author: Trond Myklebust Date: Mon Oct 15 18:18:29 2007 -0400 NFS: define a function to update nfsi->cache_change_attribute Signed-off-by: Trond Myklebust [ modified to apply on 2.6.9-89 by Chuck Lever ] [ different context in include/linux/nfs_fs.h ] diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c --- linux-2.6.9/fs/nfs/dir.c 2009-10-23 20:19:10.000000000 -0400 +++ cel-2.6.9/fs/nfs/dir.c 2009-10-23 20:19:55.000000000 -0400 @@ -700,6 +700,21 @@ int nfs_fsync_dir(struct file *filp, str return 0; } +/** + * nfs_force_lookup_revalidate - Mark the directory as having changed + * @dir - pointer to directory inode + * + * This forces the revalidation code in nfs_lookup_revalidate() to do a + * full lookup on all child dentries of 'dir' whenever a change occurs + * on the server that might have invalidated our dcache. + * + * The caller should be holding dir->i_lock + */ +void nfs_force_lookup_revalidate(struct inode *dir) +{ + NFS_I(dir)->cache_change_attribute = jiffies; +} + /* * A check for whether or not the parent directory has changed. * In the case it has, we assume that the dentries are untrustworthy diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 20:19:10.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 20:19:55.000000000 -0400 @@ -1474,7 +1474,8 @@ static int nfs_update_inode(struct inode dprintk("NFS: mtime change on server for file %s/%ld\n", inode->i_sb->s_id, inode->i_ino); invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; - nfsi->cache_change_attribute = now; + if (S_ISDIR(inode->i_mode)) + nfs_force_lookup_revalidate(inode); } /* If ctime has changed we should definitely clear access+acl caches */ if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) @@ -1483,7 +1484,8 @@ static int nfs_update_inode(struct inode dprintk("NFS: change_attr change on server for file %s/%ld\n", inode->i_sb->s_id, inode->i_ino); invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; - nfsi->cache_change_attribute = now; + if (S_ISDIR(inode->i_mode)) + nfs_force_lookup_revalidate(inode); } /* Check if our cached file size is stale */ diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/nfs4proc.c cel-2.6.9/fs/nfs/nfs4proc.c --- linux-2.6.9/fs/nfs/nfs4proc.c 2009-10-23 20:19:10.000000000 -0400 +++ cel-2.6.9/fs/nfs/nfs4proc.c 2009-10-23 20:19:55.000000000 -0400 @@ -188,7 +188,7 @@ static void update_changeattr(struct ino spin_lock(&dir->i_lock); nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA; if (!cinfo->atomic || cinfo->before != nfsi->change_attr) - nfsi->cache_change_attribute = jiffies; + nfs_force_lookup_revalidate(dir); nfsi->change_attr = cinfo->after; spin_unlock(&dir->i_lock); } diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h --- linux-2.6.9/include/linux/nfs_fs.h 2009-10-23 20:19:10.000000000 -0400 +++ cel-2.6.9/include/linux/nfs_fs.h 2009-10-23 20:19:55.000000000 -0400 @@ -414,6 +414,7 @@ extern struct file_operations_ext nfs3_d #endif /* CONFIG_NFS_V3 */ extern struct dentry_operations nfs_dentry_operations; +extern void nfs_force_lookup_revalidate(struct inode *dir); extern int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr); extern int is_atomic_open(struct inode *dir, struct nameidata *nd); commit 870a5be8b92151332da65021b7b21104e9c1de07 Author: Trond Myklebust Date: Sun Oct 5 12:07:23 2008 -0400 NFS: Clean up nfs_refresh_inode() and nfs_post_op_update_inode() Try to avoid taking and dropping the inode->i_lock more than once. Do so by moving the code in nfs_refresh_inode() that needs to be done under the spinlock into a function nfs_refresh_inode_locked(), and then having both nfs_refresh_inode() and nfs_post_op_update_inode() call it directly. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-26 19:41:09.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-26 19:41:53.000000000 -0400 @@ -1369,6 +1369,15 @@ static int nfs_check_inode_attributes(st return 0; } +static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr) +{ + struct nfs_inode *nfsi = NFS_I(inode); + + if (time_after(fattr->time_start, nfsi->last_updated)) + return nfs_update_inode(inode, fattr); + return nfs_check_inode_attributes(inode, fattr); +} + /** * nfs_refresh_inode - try to update the inode attribute cache * @inode - pointer to inode @@ -1381,17 +1390,12 @@ static int nfs_check_inode_attributes(st */ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) { - struct nfs_inode *nfsi = NFS_I(inode); int status; if ((fattr->valid & NFS_ATTR_FATTR) == 0) return 0; spin_lock(&inode->i_lock); - if (time_after(fattr->time_start, nfsi->last_updated)) - status = nfs_update_inode(inode, fattr); - else - status = nfs_check_inode_attributes(inode, fattr); - + status = nfs_refresh_inode_locked(inode, fattr); spin_unlock(&inode->i_lock); return status; } @@ -1407,13 +1411,16 @@ int nfs_refresh_inode(struct inode *inod int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr) { struct nfs_inode *nfsi = NFS_I(inode); + int status = 0; spin_lock(&inode->i_lock); nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; if (S_ISDIR(inode->i_mode)) nfsi->cache_validity |= NFS_INO_INVALID_DATA; + if ((fattr->valid & NFS_ATTR_FATTR) != 0) + status = nfs_refresh_inode_locked(inode, fattr); spin_unlock(&inode->i_lock); - return nfs_refresh_inode(inode, fattr); + return status; } /* commit a10ad17630024bf7aae8e7f18352f816ee483091 Author: Trond Myklebust Date: Tue Sep 23 17:28:41 2008 -0400 NFS: Fix the NFS attribute update Currently nfs_refresh_inode() will only update the inode metadata if it sees that the RPC call that returned the nfs_fattr was started after the last update of the inode. This means that if we have parallel RPC calls to the same inode (when sending WRITE calls, for instance), we may often miss updates. This patch attempts to recover those missed updates by also accepting them if the ctime in the nfs_fattr is more recent than the inode's cached ctime. It also recovers the case where the file size has increased, but the ctime has not been updated due to limited ctime resolution. Signed-off-by: Trond Myklebust [ modified to apply on 2.6.9-89 by Chuck Lever ] [ 2.6.9-89 does not have timespec_compare(), introduced by commit 643a6545, and updated by 77adbfbf. ] diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-27 16:39:46.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-27 16:42:25.000000000 -0400 @@ -1369,11 +1369,49 @@ static int nfs_check_inode_attributes(st return 0; } -static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr) +static int nfs_ctime_need_update(const struct inode *inode, const struct nfs_fattr *fattr) { - struct nfs_inode *nfsi = NFS_I(inode); + return timespec_compare(&fattr->ctime, &inode->i_ctime) > 0; +} + +static int nfs_size_need_update(const struct inode *inode, const struct nfs_fattr *fattr) +{ + return nfs_size_to_loff_t(fattr->size) > i_size_read(inode); +} - if (time_after(fattr->time_start, nfsi->last_updated)) +/** + * nfs_inode_attrs_need_update - check if the inode attributes need updating + * @inode - pointer to inode + * @fattr - attributes + * + * Attempt to divine whether or not an RPC call reply carrying stale + * attributes got scheduled after another call carrying updated ones. + * + * To do so, the function first assumes that a more recent ctime means + * that the attributes in fattr are newer, however it also attempt to + * catch the case where ctime either didn't change, or went backwards + * (if someone reset the clock on the server) by looking at whether + * or not this RPC call was started after the inode was last updated. + * Note also the check for jiffy wraparound if the last_updated timestamp + * is later than 'jiffies'. + * + * The function returns 'true' if it thinks the attributes in 'fattr' are + * more recent than the ones cached in the inode. + * + */ +static int nfs_inode_attrs_need_update(const struct inode *inode, const struct nfs_fattr *fattr) +{ + const struct nfs_inode *nfsi = NFS_I(inode); + + return nfs_ctime_need_update(inode, fattr) || + nfs_size_need_update(inode, fattr) || + time_after(fattr->time_start, nfsi->last_updated) || + time_after(nfsi->last_updated, jiffies); +} + +static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr) +{ + if (nfs_inode_attrs_need_update(inode, fattr)) return nfs_update_inode(inode, fattr); return nfs_check_inode_attributes(inode, fattr); } diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/time.h cel-2.6.9/include/linux/time.h --- linux-2.6.9/include/linux/time.h 2009-10-27 16:39:22.000000000 -0400 +++ cel-2.6.9/include/linux/time.h 2009-10-27 16:43:42.000000000 -0400 @@ -301,6 +301,20 @@ static __inline__ int timespec_equal(str return (a->tv_sec == b->tv_sec) && (a->tv_nsec == b->tv_nsec); } +/* + * lhs < rhs: return <0 + * lhs == rhs: return 0 + * lhs > rhs: return >0 + */ +static inline int timespec_compare(const struct timespec *lhs, const struct timespec *rhs) +{ + if (lhs->tv_sec < rhs->tv_sec) + return -1; + if (lhs->tv_sec > rhs->tv_sec) + return 1; + return lhs->tv_nsec - rhs->tv_nsec; +} + /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. commit 03254e65a60d3113164672dbbadc023c4a56ecd1 Author: Trond Myklebust Date: Thu Oct 9 13:27:55 2008 -0400 NFS: Fix attribute updates This fixes a regression seen when running the Connectathon testsuite against an ext3 filesystem. The reason was that the inode was constantly being marked as 'just updated' by the jiffy wraparound test. This again meant that newer GETATTR calls were failing to pass the nfs_inode_attrs_need_update() test unless the changes caused a ctime update on the server, since they were perceived as having been started before the latest inode update. Given that nfs_inode_attrs_need_update() already checks for wraparound of nfsi->last_updated, we can drop the buggy "protection" in nfs_update_inode(). Also make a slight micro-optimisation of nfs_inode_attrs_need_update(): we are more often going to see time_after(fattr->time_start, nfsi->last_updated) be true, rather than seeing an update of ctime/size, so put that test first to ensure that we optimise away the ctime/size tests. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-26 19:57:05.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-26 19:57:49.000000000 -0400 @@ -1403,10 +1403,10 @@ static int nfs_inode_attrs_need_update(c { const struct nfs_inode *nfsi = NFS_I(inode); - return nfs_ctime_need_update(inode, fattr) || - nfs_size_need_update(inode, fattr) || - time_after(fattr->time_start, nfsi->last_updated) || - time_after(nfsi->last_updated, jiffies); + return time_after(fattr->time_start, nfsi->last_updated) || + nfs_ctime_need_update(inode, fattr) || + nfs_size_need_update(inode, fattr) || + time_after(nfsi->last_updated, jiffies); } static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr) @@ -1584,11 +1584,6 @@ static int nfs_update_inode(struct inode nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode); nfsi->attrtimeo_timestamp = now; } - /* - * Avoid jiffy wraparound issues with nfsi->last_updated - */ - if (!time_in_range(nfsi->last_updated, nfsi->read_cache_jiffies, now)) - nfsi->last_updated = nfsi->read_cache_jiffies; } invalid &= ~NFS_INO_INVALID_ATTR; /* Don't invalidate the data if we were to blame */ commit 4704f0e274829e3af00737d2d9adace2d71a9605 Author: Trond Myklebust Date: Tue Oct 14 19:16:07 2008 -0400 NFS: Fix the resolution problem with nfs_inode_attrs_need_update() It appears that 'jiffies' timestamps do not have high enough resolution for nfs_inode_attrs_need_update(). One problem is that a GETATTR can be launched within < 1 jiffy of the last operation that updated the attribute. Another problem is that RPC calls can take < 1 jiffy to execute. We can fix this by switching the variables to use a simple global counter that gets incremented every time we start another GETATTR call. Signed-off-by: Trond Myklebust [ modified to apply on 2.6.9-89 by Chuck Lever ] [ commit 25606656 already applied to 2.6.9-89; context in include/linux/nfs_fs.h is different ] diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c --- linux-2.6.9/fs/nfs/dir.c 2009-10-27 12:00:41.000000000 -0400 +++ cel-2.6.9/fs/nfs/dir.c 2009-10-27 12:02:51.000000000 -0400 @@ -179,6 +179,7 @@ typedef struct { decode_dirent_t decode; int plus; unsigned long timestamp; + unsigned long gencount; int timestamp_valid; } nfs_readdir_descriptor_t; @@ -200,13 +201,14 @@ int nfs_readdir_filler(nfs_readdir_descr struct file *file = desc->file; struct inode *inode = file->f_dentry->d_inode; struct rpc_cred *cred = nfs_file_cred(file); - unsigned long timestamp; + unsigned long timestamp, gencount; int error; dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->index); again: timestamp = jiffies; + gencount = nfs_inc_attr_generation_counter(); error = NFS_PROTO(inode)->readdir(file->f_dentry, cred, desc->entry->cookie, page, NFS_SERVER(inode)->dtsize, desc->plus); if (error < 0) { @@ -220,6 +222,7 @@ int nfs_readdir_filler(nfs_readdir_descr goto error; } desc->timestamp = timestamp; + desc->gencount = gencount; desc->timestamp_valid = 1; SetPageUptodate(page); spin_lock(&inode->i_lock); @@ -250,9 +253,10 @@ int dir_decode(nfs_readdir_descriptor_t if (IS_ERR(p)) return PTR_ERR(p); desc->ptr = p; - if (desc->timestamp_valid) + if (desc->timestamp_valid) { desc->entry->fattr->time_start = desc->timestamp; - else + desc->entry->fattr->gencount = desc->gencount; + } else desc->entry->fattr->valid &= ~NFS_ATTR_FATTR; return 0; } @@ -547,7 +551,7 @@ int uncached_readdir(nfs_readdir_descrip struct rpc_cred *cred = nfs_file_cred(file); struct page *page = NULL; int status; - unsigned long timestamp; + unsigned long timestamp, gencount; dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (unsigned long long)*desc->dir_cookie); @@ -557,6 +561,7 @@ int uncached_readdir(nfs_readdir_descrip goto out; } timestamp = jiffies; + gencount = nfs_inc_attr_generation_counter(); status = NFS_PROTO(inode)->readdir(file->f_dentry, cred, *desc->dir_cookie, page, NFS_SERVER(inode)->dtsize, @@ -568,6 +573,7 @@ int uncached_readdir(nfs_readdir_descrip desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ if (status >= 0) { desc->timestamp = timestamp; + desc->gencount = gencount; desc->timestamp_valid = 1; if ((status = dir_decode(desc)) == 0) desc->entry->prev_cookie = *desc->dir_cookie; diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-27 12:00:41.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-27 12:01:30.000000000 -0400 @@ -884,7 +884,7 @@ nfs_fhget(struct super_block *sb, struct init_special_inode(inode, inode->i_mode, fattr->rdev); nfsi->read_cache_jiffies = fattr->time_start; - nfsi->last_updated = now; + nfsi->attr_gencount = fattr->gencount; nfsi->cache_change_attribute = now; inode->i_atime = fattr->atime; inode->i_mtime = fattr->mtime; @@ -1379,6 +1379,30 @@ static int nfs_size_need_update(const st return nfs_size_to_loff_t(fattr->size) > i_size_read(inode); } +static unsigned long nfs_attr_generation_counter; + +static unsigned long nfs_read_attr_generation_counter(void) +{ + smp_rmb(); + return nfs_attr_generation_counter; +} + +unsigned long nfs_inc_attr_generation_counter(void) +{ + unsigned long ret; + smp_rmb(); + ret = ++nfs_attr_generation_counter; + smp_wmb(); + return ret; +} + +void nfs_fattr_init(struct nfs_fattr *fattr) +{ + fattr->valid = 0; + fattr->time_start = jiffies; + fattr->gencount = nfs_inc_attr_generation_counter(); +} + /** * nfs_inode_attrs_need_update - check if the inode attributes need updating * @inode - pointer to inode @@ -1392,8 +1416,7 @@ static int nfs_size_need_update(const st * catch the case where ctime either didn't change, or went backwards * (if someone reset the clock on the server) by looking at whether * or not this RPC call was started after the inode was last updated. - * Note also the check for jiffy wraparound if the last_updated timestamp - * is later than 'jiffies'. + * Note also the check for wraparound of 'attr_gencount' * * The function returns 'true' if it thinks the attributes in 'fattr' are * more recent than the ones cached in the inode. @@ -1403,10 +1426,10 @@ static int nfs_inode_attrs_need_update(c { const struct nfs_inode *nfsi = NFS_I(inode); - return time_after(fattr->time_start, nfsi->last_updated) || + return ((long)fattr->gencount - (long)nfsi->attr_gencount) > 0 || nfs_ctime_need_update(inode, fattr) || nfs_size_need_update(inode, fattr) || - time_after(nfsi->last_updated, jiffies); + ((long)nfsi->attr_gencount - (long)nfs_read_attr_generation_counter() > 0); } static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr) @@ -1522,7 +1545,7 @@ static int nfs_update_inode(struct inode } /* If ctime has changed we should definitely clear access+acl caches */ if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) - invalid |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; + invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; } else if (nfsi->change_attr != fattr->change_attr) { dprintk("NFS: change_attr change on server for file %s/%ld\n", inode->i_sb->s_id, inode->i_ino); @@ -1577,7 +1600,7 @@ static int nfs_update_inode(struct inode nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE); nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); nfsi->attrtimeo_timestamp = now; - nfsi->last_updated = now; + nfsi->attr_gencount = nfs_inc_attr_generation_counter(); } else { if (!time_in_range_open(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) { if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode)) diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h --- linux-2.6.9/include/linux/nfs_fs.h 2009-10-27 12:00:41.000000000 -0400 +++ cel-2.6.9/include/linux/nfs_fs.h 2009-10-27 12:06:27.000000000 -0400 @@ -159,7 +159,7 @@ struct nfs_inode { unsigned long attrtimeo_timestamp; __u64 change_attr; /* v4 only */ - unsigned long last_updated; + unsigned long attr_gencount; /* "Generation counter" for the attribute cache. This is * bumped whenever we update the metadata on the * server. @@ -344,15 +344,11 @@ extern void nfs_file_set_open_context(st extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, int mode); extern void nfs_file_clear_open_context(struct file *filp); extern u64 nfs_compat_user_ino64(u64 fileid); +extern void nfs_fattr_init(struct nfs_fattr *fattr); /* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */ extern u32 root_nfs_parse_addr(char *name); /*__init*/ - -static inline void nfs_fattr_init(struct nfs_fattr *fattr) -{ - fattr->valid = 0; - fattr->time_start = jiffies; -} +extern unsigned long nfs_inc_attr_generation_counter(void); /* * linux/fs/nfs/file.c diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_xdr.h cel-2.6.9/include/linux/nfs_xdr.h --- linux-2.6.9/include/linux/nfs_xdr.h 2009-10-27 12:00:41.000000000 -0400 +++ cel-2.6.9/include/linux/nfs_xdr.h 2009-10-27 12:01:30.000000000 -0400 @@ -42,6 +42,7 @@ struct nfs_fattr { __u64 change_attr; /* NFSv4 change attribute */ __u64 pre_change_attr;/* pre-op NFSv4 change attribute */ unsigned long time_start; + unsigned long gencount; }; #define NFS_ATTR_WCC 0x0001 /* pre-op WCC data */ commit 011935a0a710c20bb7ae63523b78856848db1926 Author: Trond Myklebust Date: Tue Oct 14 19:24:50 2008 -0400 NFS: Fix a resolution problem with nfs_inode->cache_change_attribute The cache_change_attribute is used to decide whether or not a directory has changed, in which case we may need to look it up again. Again, the use of 'jiffies' leads to an issue of resolution. Once again, the fix is to change nfs_inode->cache_change_attribute, and just make it a simple counter. Signed-off-by: Trond Myklebust diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c --- linux-2.6.9/fs/nfs/dir.c 2009-10-23 20:22:24.000000000 -0400 +++ cel-2.6.9/fs/nfs/dir.c 2009-10-23 20:23:15.000000000 -0400 @@ -712,7 +712,7 @@ int nfs_fsync_dir(struct file *filp, str */ void nfs_force_lookup_revalidate(struct inode *dir) { - NFS_I(dir)->cache_change_attribute = jiffies; + NFS_I(dir)->cache_change_attribute++; } /* diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c --- linux-2.6.9/fs/nfs/inode.c 2009-10-23 20:22:25.000000000 -0400 +++ cel-2.6.9/fs/nfs/inode.c 2009-10-23 20:23:15.000000000 -0400 @@ -885,7 +885,6 @@ nfs_fhget(struct super_block *sb, struct nfsi->read_cache_jiffies = fattr->time_start; nfsi->attr_gencount = fattr->gencount; - nfsi->cache_change_attribute = now; inode->i_atime = fattr->atime; inode->i_mtime = fattr->mtime; inode->i_ctime = fattr->ctime;