Commit ca9268fe authored by Trond Myklebust's avatar Trond Myklebust

NFSv2/v3/v4: New attribute revalidation code that no

     longer relies on ctime for correctness in avoiding
     update races.

VFS: allow filesystems to disable inode_update_time() on
     a per-inode basis.
parent 9bf35f8c
...@@ -1178,6 +1178,8 @@ void inode_update_time(struct inode *inode, int ctime_too) ...@@ -1178,6 +1178,8 @@ void inode_update_time(struct inode *inode, int ctime_too)
struct timespec now; struct timespec now;
int sync_it = 0; int sync_it = 0;
if (IS_NOCMTIME(inode))
return;
if (IS_RDONLY(inode)) if (IS_RDONLY(inode))
return; return;
......
...@@ -139,11 +139,13 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page) ...@@ -139,11 +139,13 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
struct file *file = desc->file; struct file *file = desc->file;
struct inode *inode = file->f_dentry->d_inode; struct inode *inode = file->f_dentry->d_inode;
struct rpc_cred *cred = nfs_file_cred(file); struct rpc_cred *cred = nfs_file_cred(file);
unsigned long timestamp;
int error; int error;
dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->index); dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->index);
again: again:
timestamp = jiffies;
error = NFS_PROTO(inode)->readdir(file->f_dentry, cred, desc->entry->cookie, page, error = NFS_PROTO(inode)->readdir(file->f_dentry, cred, desc->entry->cookie, page,
NFS_SERVER(inode)->dtsize, desc->plus); NFS_SERVER(inode)->dtsize, desc->plus);
if (error < 0) { if (error < 0) {
...@@ -157,18 +159,21 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page) ...@@ -157,18 +159,21 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
goto error; goto error;
} }
SetPageUptodate(page); SetPageUptodate(page);
NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME;
/* Ensure consistent page alignment of the data. /* Ensure consistent page alignment of the data.
* Note: assumes we have exclusive access to this mapping either * Note: assumes we have exclusive access to this mapping either
* throught inode->i_sem or some other mechanism. * throught inode->i_sem or some other mechanism.
*/ */
if (page->index == 0) if (page->index == 0) {
invalidate_inode_pages(inode->i_mapping); invalidate_inode_pages(inode->i_mapping);
NFS_I(inode)->readdir_timestamp = timestamp;
}
unlock_page(page); unlock_page(page);
return 0; return 0;
error: error:
SetPageError(page); SetPageError(page);
unlock_page(page); unlock_page(page);
invalidate_inode_pages(inode->i_mapping); nfs_zap_caches(inode);
desc->error = error; desc->error = error;
return -EIO; return -EIO;
} }
...@@ -381,6 +386,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, ...@@ -381,6 +386,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
page, page,
NFS_SERVER(inode)->dtsize, NFS_SERVER(inode)->dtsize,
desc->plus); desc->plus);
NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME;
desc->page = page; desc->page = page;
desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */
if (desc->error >= 0) { if (desc->error >= 0) {
...@@ -459,7 +465,15 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -459,7 +465,15 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
} }
res = 0; res = 0;
break; break;
} else if (res < 0) }
if (res == -ETOOSMALL && desc->plus) {
NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS;
nfs_zap_caches(inode);
desc->plus = 0;
desc->entry->eof = 0;
continue;
}
if (res < 0)
break; break;
res = nfs_do_filldir(desc, dirent, filldir); res = nfs_do_filldir(desc, dirent, filldir);
...@@ -481,14 +495,19 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -481,14 +495,19 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
* In the case it has, we assume that the dentries are untrustworthy * In the case it has, we assume that the dentries are untrustworthy
* and may need to be looked up again. * and may need to be looked up again.
*/ */
static inline static inline int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
{ {
if (IS_ROOT(dentry)) if (IS_ROOT(dentry))
return 1; return 1;
if (nfs_revalidate_inode(NFS_SERVER(dir), dir)) if ((NFS_FLAGS(dir) & NFS_INO_INVALID_ATTR) != 0
|| nfs_attribute_timeout(dir))
return 0; return 0;
return time_after(dentry->d_time, NFS_MTIME_UPDATE(dir)); return nfs_verify_change_attribute(dir, (unsigned long)dentry->d_fsdata);
}
static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf)
{
dentry->d_fsdata = (void *)verf;
} }
/* /*
...@@ -528,9 +547,7 @@ int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry, ...@@ -528,9 +547,7 @@ int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry,
/* Don't revalidate a negative dentry if we're creating a new file */ /* Don't revalidate a negative dentry if we're creating a new file */
if ((ndflags & LOOKUP_CREATE) && !(ndflags & LOOKUP_CONTINUE)) if ((ndflags & LOOKUP_CREATE) && !(ndflags & LOOKUP_CONTINUE))
return 0; return 0;
if (!nfs_check_verifier(dir, dentry)) return !nfs_check_verifier(dir, dentry);
return 1;
return time_after(jiffies, dentry->d_time + NFS_ATTRTIMEO(dir));
} }
/* /*
...@@ -552,6 +569,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -552,6 +569,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
int error; int error;
struct nfs_fh fhandle; struct nfs_fh fhandle;
struct nfs_fattr fattr; struct nfs_fattr fattr;
unsigned long verifier;
int isopen = 0; int isopen = 0;
parent = dget_parent(dentry); parent = dget_parent(dentry);
...@@ -574,6 +592,9 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -574,6 +592,9 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
goto out_bad; goto out_bad;
} }
/* Revalidate parent directory attribute cache */
nfs_revalidate_inode(NFS_SERVER(dir), dir);
/* Force a full look up iff the parent directory has changed */ /* Force a full look up iff the parent directory has changed */
if (nfs_check_verifier(dir, dentry)) { if (nfs_check_verifier(dir, dentry)) {
if (nfs_lookup_verify_inode(inode, isopen)) if (nfs_lookup_verify_inode(inode, isopen))
...@@ -581,6 +602,12 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -581,6 +602,12 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
goto out_valid; goto out_valid;
} }
/*
* Note: we're not holding inode->i_sem and so may be racing with
* operations that change the directory. We therefore save the
* change attribute *before* we do the RPC call.
*/
verifier = nfs_save_change_attribute(dir);
error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr); error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
if (!error) { if (!error) {
if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0) if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0)
...@@ -603,6 +630,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) ...@@ -603,6 +630,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
out_valid_renew: out_valid_renew:
nfs_renew_times(dentry); nfs_renew_times(dentry);
nfs_set_verifier(dentry, verifier);
out_valid: out_valid:
unlock_kernel(); unlock_kernel();
dput(parent); dput(parent);
...@@ -693,6 +721,8 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru ...@@ -693,6 +721,8 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
dentry->d_op = NFS_PROTO(dir)->dentry_ops; dentry->d_op = NFS_PROTO(dir)->dentry_ops;
lock_kernel(); lock_kernel();
/* Revalidate parent directory attribute cache */
nfs_revalidate_inode(NFS_SERVER(dir), dir);
/* If we're doing an exclusive create, optimize away the lookup */ /* If we're doing an exclusive create, optimize away the lookup */
if (nfs_is_exclusive_create(dir, nd)) if (nfs_is_exclusive_create(dir, nd))
...@@ -715,6 +745,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru ...@@ -715,6 +745,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
error = 0; error = 0;
d_add(dentry, inode); d_add(dentry, inode);
nfs_renew_times(dentry); nfs_renew_times(dentry);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out_unlock: out_unlock:
unlock_kernel(); unlock_kernel();
out: out:
...@@ -768,7 +799,15 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry ...@@ -768,7 +799,15 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
/* Open the file on the server */ /* Open the file on the server */
lock_kernel(); lock_kernel();
inode = nfs4_atomic_open(dir, dentry, nd); /* Revalidate parent directory attribute cache */
nfs_revalidate_inode(NFS_SERVER(dir), dir);
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);
unlock_kernel(); unlock_kernel();
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
error = PTR_ERR(inode); error = PTR_ERR(inode);
...@@ -790,6 +829,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry ...@@ -790,6 +829,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
no_entry: no_entry:
d_add(dentry, inode); d_add(dentry, inode);
nfs_renew_times(dentry); nfs_renew_times(dentry);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out: out:
BUG_ON(error > 0); BUG_ON(error > 0);
return ERR_PTR(error); return ERR_PTR(error);
...@@ -801,13 +841,16 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -801,13 +841,16 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
{ {
struct dentry *parent = NULL; struct dentry *parent = NULL;
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
struct inode *dir;
unsigned long verifier;
int openflags, ret = 0; int openflags, ret = 0;
/* NFS only supports OPEN for regular files */ /* NFS only supports OPEN for regular files */
if (inode && !S_ISREG(inode->i_mode)) if (inode && !S_ISREG(inode->i_mode))
goto no_open; goto no_open;
parent = dget_parent(dentry); parent = dget_parent(dentry);
if (!is_atomic_open(parent->d_inode, nd)) dir = parent->d_inode;
if (!is_atomic_open(dir, nd))
goto no_open; goto no_open;
openflags = nd->intent.open.flags; openflags = nd->intent.open.flags;
if (openflags & O_CREAT) { if (openflags & O_CREAT) {
...@@ -821,8 +864,16 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -821,8 +864,16 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
/* We can't create new files, or truncate existing ones here */ /* We can't create new files, or truncate existing ones here */
openflags &= ~(O_CREAT|O_TRUNC); openflags &= ~(O_CREAT|O_TRUNC);
/*
* Note: we're not holding inode->i_sem and so may be racing with
* operations that change the directory. We therefore save the
* change attribute *before* we do the RPC call.
*/
lock_kernel(); lock_kernel();
ret = nfs4_open_revalidate(parent->d_inode, dentry, openflags); verifier = nfs_save_change_attribute(dir);
ret = nfs4_open_revalidate(dir, dentry, openflags);
if (!ret)
nfs_set_verifier(dentry, verifier);
unlock_kernel(); unlock_kernel();
out: out:
dput(parent); dput(parent);
...@@ -869,15 +920,20 @@ int nfs_cached_lookup(struct inode *dir, struct dentry *dentry, ...@@ -869,15 +920,20 @@ int nfs_cached_lookup(struct inode *dir, struct dentry *dentry,
struct nfs_server *server; struct nfs_server *server;
struct nfs_entry entry; struct nfs_entry entry;
struct page *page; struct page *page;
unsigned long timestamp = NFS_MTIME_UPDATE(dir); unsigned long timestamp;
int res; int res;
if (!NFS_USE_READDIRPLUS(dir)) if (!NFS_USE_READDIRPLUS(dir))
return -ENOENT; return -ENOENT;
server = NFS_SERVER(dir); server = NFS_SERVER(dir);
if (server->flags & NFS_MOUNT_NOAC) /* Don't use readdirplus unless the cache is stable */
if ((server->flags & NFS_MOUNT_NOAC) != 0
|| nfs_caches_unstable(dir)
|| nfs_attribute_timeout(dir))
return -ENOENT;
if ((NFS_FLAGS(dir) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA)) != 0)
return -ENOENT; return -ENOENT;
nfs_revalidate_inode(server, dir); timestamp = NFS_I(dir)->readdir_timestamp;
entry.fh = fh; entry.fh = fh;
entry.fattr = fattr; entry.fattr = fattr;
...@@ -931,6 +987,7 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, ...@@ -931,6 +987,7 @@ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
if (inode) { if (inode) {
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
nfs_renew_times(dentry); nfs_renew_times(dentry);
nfs_set_verifier(dentry, nfs_save_change_attribute(dentry->d_parent->d_inode));
error = 0; error = 0;
} }
return error; return error;
...@@ -969,11 +1026,13 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode, ...@@ -969,11 +1026,13 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
* does not pass the create flags. * does not pass the create flags.
*/ */
lock_kernel(); lock_kernel();
nfs_zap_caches(dir); nfs_begin_data_update(dir);
inode = NFS_PROTO(dir)->create(dir, &dentry->d_name, &attr, open_flags); inode = NFS_PROTO(dir)->create(dir, &dentry->d_name, &attr, open_flags);
nfs_end_data_update(dir);
if (!IS_ERR(inode)) { if (!IS_ERR(inode)) {
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
nfs_renew_times(dentry); nfs_renew_times(dentry);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
error = 0; error = 0;
} else { } else {
error = PTR_ERR(inode); error = PTR_ERR(inode);
...@@ -1004,9 +1063,10 @@ nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) ...@@ -1004,9 +1063,10 @@ nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
attr.ia_valid = ATTR_MODE; attr.ia_valid = ATTR_MODE;
lock_kernel(); lock_kernel();
nfs_zap_caches(dir); nfs_begin_data_update(dir);
error = NFS_PROTO(dir)->mknod(dir, &dentry->d_name, &attr, rdev, error = NFS_PROTO(dir)->mknod(dir, &dentry->d_name, &attr, rdev,
&fhandle, &fattr); &fhandle, &fattr);
nfs_end_data_update(dir);
if (!error) if (!error)
error = nfs_instantiate(dentry, &fhandle, &fattr); error = nfs_instantiate(dentry, &fhandle, &fattr);
else else
...@@ -1041,9 +1101,10 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ...@@ -1041,9 +1101,10 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
*/ */
d_drop(dentry); d_drop(dentry);
#endif #endif
nfs_zap_caches(dir); nfs_begin_data_update(dir);
error = NFS_PROTO(dir)->mkdir(dir, &dentry->d_name, &attr, &fhandle, error = NFS_PROTO(dir)->mkdir(dir, &dentry->d_name, &attr, &fhandle,
&fattr); &fattr);
nfs_end_data_update(dir);
if (!error) if (!error)
error = nfs_instantiate(dentry, &fhandle, &fattr); error = nfs_instantiate(dentry, &fhandle, &fattr);
else else
...@@ -1060,10 +1121,12 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -1060,10 +1121,12 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
dir->i_ino, dentry->d_name.name); dir->i_ino, dentry->d_name.name);
lock_kernel(); lock_kernel();
nfs_zap_caches(dir); nfs_begin_data_update(dir);
error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name); error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
if (!error) /* Ensure the VFS deletes this inode */
if (error == 0 && dentry->d_inode != NULL)
dentry->d_inode->i_nlink = 0; dentry->d_inode->i_nlink = 0;
nfs_end_data_update(dir);
unlock_kernel(); unlock_kernel();
return error; return error;
...@@ -1119,12 +1182,21 @@ dentry->d_parent->d_name.name, dentry->d_name.name); ...@@ -1119,12 +1182,21 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
goto out; goto out;
} while(sdentry->d_inode != NULL); /* need negative lookup */ } while(sdentry->d_inode != NULL); /* need negative lookup */
nfs_zap_caches(dir);
qsilly.name = silly; qsilly.name = silly;
qsilly.len = strlen(silly); qsilly.len = strlen(silly);
error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, dir, &qsilly); nfs_begin_data_update(dir);
if (dentry->d_inode) {
nfs_begin_data_update(dentry->d_inode);
error = NFS_PROTO(dir)->rename(dir, &dentry->d_name,
dir, &qsilly);
nfs_end_data_update(dentry->d_inode);
} else
error = NFS_PROTO(dir)->rename(dir, &dentry->d_name,
dir, &qsilly);
nfs_end_data_update(dir);
if (!error) { if (!error) {
nfs_renew_times(dentry); nfs_renew_times(dentry);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
d_move(dentry, sdentry); d_move(dentry, sdentry);
error = nfs_async_unlink(dentry); error = nfs_async_unlink(dentry);
/* If we return 0 we don't unlink */ /* If we return 0 we don't unlink */
...@@ -1156,14 +1228,17 @@ static int nfs_safe_remove(struct dentry *dentry) ...@@ -1156,14 +1228,17 @@ static int nfs_safe_remove(struct dentry *dentry)
goto out; goto out;
} }
nfs_zap_caches(dir); nfs_begin_data_update(dir);
if (inode) if (inode != NULL) {
NFS_CACHEINV(inode); nfs_begin_data_update(inode);
error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
if (error < 0) /* The VFS may want to delete this inode */
goto out; if (error == 0)
if (inode) inode->i_nlink--;
inode->i_nlink--; nfs_end_data_update(inode);
} else
error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
nfs_end_data_update(dir);
out: out:
return error; return error;
} }
...@@ -1198,9 +1273,10 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1198,9 +1273,10 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
error = nfs_safe_remove(dentry); error = nfs_safe_remove(dentry);
if (!error) if (!error) {
nfs_renew_times(dentry); nfs_renew_times(dentry);
else if (need_rehash) nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
} else if (need_rehash)
d_rehash(dentry); d_rehash(dentry);
unlock_kernel(); unlock_kernel();
return error; return error;
...@@ -1247,9 +1323,10 @@ dentry->d_parent->d_name.name, dentry->d_name.name); ...@@ -1247,9 +1323,10 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
qsymname.len = strlen(symname); qsymname.len = strlen(symname);
lock_kernel(); lock_kernel();
nfs_zap_caches(dir); nfs_begin_data_update(dir);
error = NFS_PROTO(dir)->symlink(dir, &dentry->d_name, &qsymname, error = NFS_PROTO(dir)->symlink(dir, &dentry->d_name, &qsymname,
&attr, &sym_fh, &sym_attr); &attr, &sym_fh, &sym_attr);
nfs_end_data_update(dir);
if (!error) { if (!error) {
error = nfs_instantiate(dentry, &sym_fh, &sym_attr); error = nfs_instantiate(dentry, &sym_fh, &sym_attr);
} else { } else {
...@@ -1281,9 +1358,12 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) ...@@ -1281,9 +1358,12 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
*/ */
lock_kernel(); lock_kernel();
d_drop(dentry); d_drop(dentry);
nfs_zap_caches(dir);
NFS_CACHEINV(inode); nfs_begin_data_update(dir);
nfs_begin_data_update(inode);
error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name); error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
nfs_end_data_update(inode);
nfs_end_data_update(dir);
unlock_kernel(); unlock_kernel();
return error; return error;
} }
...@@ -1388,16 +1468,23 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1388,16 +1468,23 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (new_inode) if (new_inode)
d_delete(new_dentry); d_delete(new_dentry);
nfs_zap_caches(new_dir); nfs_begin_data_update(old_dir);
nfs_zap_caches(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, error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name,
new_dir, &new_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: out:
if (rehash) if (rehash)
d_rehash(rehash); d_rehash(rehash);
if (!error && !S_ISDIR(old_inode->i_mode)) if (!error) {
d_move(old_dentry, new_dentry); if (!S_ISDIR(old_inode->i_mode))
nfs_renew_times(new_dentry); d_move(old_dentry, new_dentry);
nfs_renew_times(new_dentry);
nfs_set_verifier(new_dentry, nfs_save_change_attribute(new_dir));
}
/* new dentry created? */ /* new dentry created? */
if (dentry) if (dentry)
...@@ -1451,7 +1538,8 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd) ...@@ -1451,7 +1538,8 @@ nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
if (cache->cred == cred if (cache->cred == cred
&& time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))) { && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))
&& !(NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR)) {
if (!(res = cache->err)) { if (!(res = cache->err)) {
/* Is the mask a subset of an accepted mask? */ /* Is the mask a subset of an accepted mask? */
if ((cache->mask & mask) == mask) if ((cache->mask & mask) == mask)
......
...@@ -269,6 +269,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file, ...@@ -269,6 +269,7 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
if (IS_SYNC(inode) || NFS_PROTO(inode)->version == 2 || count <= wsize) if (IS_SYNC(inode) || NFS_PROTO(inode)->version == 2 || count <= wsize)
wdata.args.stable = NFS_FILE_SYNC; wdata.args.stable = NFS_FILE_SYNC;
nfs_begin_data_update(inode);
retry: retry:
need_commit = 0; need_commit = 0;
tot_bytes = 0; tot_bytes = 0;
...@@ -334,6 +335,8 @@ nfs_direct_write_seg(struct inode *inode, struct file *file, ...@@ -334,6 +335,8 @@ nfs_direct_write_seg(struct inode *inode, struct file *file,
VERF_SIZE) != 0) VERF_SIZE) != 0)
goto sync_retry; goto sync_retry;
} }
nfs_end_data_update(inode);
NFS_FLAGS(inode) |= NFS_INO_INVALID_DATA;
return tot_bytes; return tot_bytes;
......
...@@ -104,11 +104,16 @@ nfs_file_flush(struct file *file) ...@@ -104,11 +104,16 @@ nfs_file_flush(struct file *file)
dfprintk(VFS, "nfs: flush(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); dfprintk(VFS, "nfs: flush(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
if ((file->f_mode & FMODE_WRITE) == 0)
return 0;
lock_kernel(); lock_kernel();
status = nfs_wb_file(inode, file); /* Ensure that data+attribute caches are up to date after close() */
status = nfs_wb_all(inode);
if (!status) { if (!status) {
status = file->f_error; status = file->f_error;
file->f_error = 0; file->f_error = 0;
if (!status)
__nfs_revalidate_inode(NFS_SERVER(inode), inode);
} }
unlock_kernel(); unlock_kernel();
return status; return status;
......
...@@ -53,8 +53,8 @@ ...@@ -53,8 +53,8 @@
*/ */
#define NFS_MAX_READAHEAD RPC_MAXREQS #define NFS_MAX_READAHEAD RPC_MAXREQS
void nfs_zap_caches(struct inode *);
static void nfs_invalidate_inode(struct inode *); static void nfs_invalidate_inode(struct inode *);
static int nfs_update_inode(struct inode *, struct nfs_fattr *, unsigned long);
static struct inode *nfs_alloc_inode(struct super_block *sb); static struct inode *nfs_alloc_inode(struct super_block *sb);
static void nfs_destroy_inode(struct inode *); static void nfs_destroy_inode(struct inode *);
...@@ -151,6 +151,7 @@ nfs_clear_inode(struct inode *inode) ...@@ -151,6 +151,7 @@ nfs_clear_inode(struct inode *inode)
cred = nfsi->cache_access.cred; cred = nfsi->cache_access.cred;
if (cred) if (cred)
put_rpccred(cred); put_rpccred(cred);
BUG_ON(atomic_read(&nfsi->data_updates) != 0);
} }
void void
...@@ -627,13 +628,17 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) ...@@ -627,13 +628,17 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
void void
nfs_zap_caches(struct inode *inode) nfs_zap_caches(struct inode *inode)
{ {
struct nfs_inode *nfsi = NFS_I(inode);
int mode = inode->i_mode;
NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
NFS_ATTRTIMEO_UPDATE(inode) = jiffies; NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
invalidate_remote_inode(inode);
memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
NFS_CACHEINV(inode); if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))
nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
else
nfsi->flags |= NFS_INO_INVALID_ATTR;
} }
/* /*
...@@ -673,9 +678,6 @@ nfs_find_actor(struct inode *inode, void *opaque) ...@@ -673,9 +678,6 @@ nfs_find_actor(struct inode *inode, void *opaque)
return 0; return 0;
if (is_bad_inode(inode)) if (is_bad_inode(inode))
return 0; return 0;
/* Force an attribute cache update if inode->i_count == 0 */
if (!atomic_read(&inode->i_count))
NFS_CACHEINV(inode);
return 1; return 1;
} }
...@@ -729,7 +731,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) ...@@ -729,7 +731,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
inode->i_ino = hash; inode->i_ino = hash;
/* We can't support update_atime(), since the server will reset it */ /* We can't support update_atime(), since the server will reset it */
inode->i_flags |= S_NOATIME; inode->i_flags |= S_NOATIME|S_NOCMTIME;
inode->i_mode = fattr->mode; inode->i_mode = fattr->mode;
/* Why so? Because we want revalidate for devices/FIFOs, and /* Why so? Because we want revalidate for devices/FIFOs, and
* that's precisely what we have in nfs_file_inode_operations. * that's precisely what we have in nfs_file_inode_operations.
...@@ -754,10 +756,6 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) ...@@ -754,10 +756,6 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
inode->i_atime = fattr->atime; inode->i_atime = fattr->atime;
inode->i_mtime = fattr->mtime; inode->i_mtime = fattr->mtime;
inode->i_ctime = fattr->ctime; inode->i_ctime = fattr->ctime;
nfsi->read_cache_ctime = fattr->ctime;
nfsi->read_cache_mtime = fattr->mtime;
nfsi->cache_mtime_jiffies = fattr->timestamp;
nfsi->read_cache_isize = fattr->size;
if (fattr->valid & NFS_ATTR_FATTR_V4) if (fattr->valid & NFS_ATTR_FATTR_V4)
nfsi->change_attr = fattr->change_attr; nfsi->change_attr = fattr->change_attr;
inode->i_size = nfs_size_to_loff_t(fattr->size); inode->i_size = nfs_size_to_loff_t(fattr->size);
...@@ -804,70 +802,50 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -804,70 +802,50 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
struct nfs_fattr fattr; struct nfs_fattr fattr;
int error; int error;
if (attr->ia_valid & ATTR_SIZE) {
if (!S_ISREG(inode->i_mode) || attr->ia_size == i_size_read(inode))
attr->ia_valid &= ~ATTR_SIZE;
}
/* Optimization: if the end result is no change, don't RPC */ /* Optimization: if the end result is no change, don't RPC */
attr->ia_valid &= NFS_VALID_ATTRS; attr->ia_valid &= NFS_VALID_ATTRS;
if (attr->ia_valid == 0) if (attr->ia_valid == 0)
return 0; return 0;
lock_kernel(); lock_kernel();
nfs_begin_data_update(inode);
/* /* Write all dirty data if we're changing file permissions or size */
* Make sure the inode is up-to-date. if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE)) != 0) {
*/ if (filemap_fdatawrite(inode->i_mapping) == 0)
error = nfs_revalidate_inode(NFS_SERVER(inode),inode); filemap_fdatawait(inode->i_mapping);
if (error) { nfs_wb_all(inode);
#ifdef NFS_PARANOIA
printk("nfs_setattr: revalidate failed, error=%d\n", error);
#endif
goto out;
} }
if (!S_ISREG(inode->i_mode)) {
attr->ia_valid &= ~ATTR_SIZE;
if (attr->ia_valid == 0)
goto out;
} else {
filemap_fdatawrite(inode->i_mapping);
error = nfs_wb_all(inode);
filemap_fdatawait(inode->i_mapping);
if (error)
goto out;
/* Optimize away unnecessary truncates */
if ((attr->ia_valid & ATTR_SIZE) && i_size_read(inode) == attr->ia_size)
attr->ia_valid &= ~ATTR_SIZE;
}
if (!attr->ia_valid)
goto out;
error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr); error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr);
if (error) if (error == 0) {
goto out; nfs_refresh_inode(inode, &fattr);
/* if ((attr->ia_valid & ATTR_MODE) != 0) {
* If we changed the size or mtime, update the inode int mode;
* now to avoid invalidating the page cache. mode = inode->i_mode & ~S_IALLUGO;
*/ mode |= attr->ia_mode & S_IALLUGO;
if (attr->ia_valid & ATTR_SIZE) { inode->i_mode = mode;
if (attr->ia_size != fattr.size) }
printk("nfs_setattr: attr=%Ld, fattr=%Ld??\n", if ((attr->ia_valid & ATTR_UID) != 0)
(long long) attr->ia_size, (long long)fattr.size); inode->i_uid = attr->ia_uid;
vmtruncate(inode, attr->ia_size); if ((attr->ia_valid & ATTR_GID) != 0)
inode->i_gid = attr->ia_gid;
if ((attr->ia_valid & ATTR_SIZE) != 0) {
i_size_write(inode, attr->ia_size);
vmtruncate(inode, attr->ia_size);
}
} }
if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) {
/* struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred;
* If we changed the size or mtime, update the inode if (*cred) {
* now to avoid invalidating the page cache. put_rpccred(*cred);
*/ *cred = NULL;
if (!(fattr.valid & NFS_ATTR_WCC)) { }
struct nfs_inode *nfsi = NFS_I(inode);
fattr.pre_size = nfsi->read_cache_isize;
fattr.pre_mtime = nfsi->read_cache_mtime;
fattr.pre_ctime = nfsi->read_cache_ctime;
fattr.valid |= NFS_ATTR_WCC;
} }
/* Force an attribute cache update */ nfs_end_data_update(inode);
NFS_CACHEINV(inode);
error = nfs_refresh_inode(inode, &fattr);
out:
unlock_kernel(); unlock_kernel();
return error; return error;
} }
...@@ -895,7 +873,19 @@ nfs_wait_on_inode(struct inode *inode, int flag) ...@@ -895,7 +873,19 @@ nfs_wait_on_inode(struct inode *inode, int flag)
int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
int err = nfs_revalidate_inode(NFS_SERVER(inode), inode); struct nfs_inode *nfsi = NFS_I(inode);
int need_atime = nfsi->flags & NFS_INO_INVALID_ATIME;
int err;
if (__IS_FLG(inode, MS_NOATIME))
need_atime = 0;
else if (__IS_FLG(inode, MS_NODIRATIME) && S_ISDIR(inode->i_mode))
need_atime = 0;
/* We may force a getattr if the user cares about atime */
if (need_atime)
err = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
else
err = nfs_revalidate_inode(NFS_SERVER(inode), inode);
if (!err) if (!err)
generic_fillattr(inode, stat); generic_fillattr(inode, stat);
return err; return err;
...@@ -930,8 +920,10 @@ int nfs_open(struct inode *inode, struct file *filp) ...@@ -930,8 +920,10 @@ int nfs_open(struct inode *inode, struct file *filp)
auth = NFS_CLIENT(inode)->cl_auth; auth = NFS_CLIENT(inode)->cl_auth;
cred = rpcauth_lookupcred(auth, 0); cred = rpcauth_lookupcred(auth, 0);
filp->private_data = cred; filp->private_data = cred;
if (filp->f_mode & FMODE_WRITE) if ((filp->f_mode & FMODE_WRITE) != 0) {
nfs_set_mmcred(inode, cred); nfs_set_mmcred(inode, cred);
nfs_begin_data_update(inode);
}
return 0; return 0;
} }
...@@ -940,6 +932,8 @@ int nfs_release(struct inode *inode, struct file *filp) ...@@ -940,6 +932,8 @@ int nfs_release(struct inode *inode, struct file *filp)
struct rpc_cred *cred; struct rpc_cred *cred;
lock_kernel(); lock_kernel();
if ((filp->f_mode & FMODE_WRITE) != 0)
nfs_end_data_update(inode);
cred = nfs_file_cred(filp); cred = nfs_file_cred(filp);
if (cred) if (cred)
put_rpccred(cred); put_rpccred(cred);
...@@ -956,6 +950,9 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) ...@@ -956,6 +950,9 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
{ {
int status = -ESTALE; int status = -ESTALE;
struct nfs_fattr fattr; struct nfs_fattr fattr;
struct nfs_inode *nfsi = NFS_I(inode);
unsigned long verifier;
unsigned int flags;
dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n", dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n",
inode->i_sb->s_id, (long long)NFS_FILEID(inode)); inode->i_sb->s_id, (long long)NFS_FILEID(inode));
...@@ -965,23 +962,22 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) ...@@ -965,23 +962,22 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
goto out_nowait; goto out_nowait;
if (NFS_STALE(inode) && inode != inode->i_sb->s_root->d_inode) if (NFS_STALE(inode) && inode != inode->i_sb->s_root->d_inode)
goto out_nowait; goto out_nowait;
if (NFS_FAKE_ROOT(inode)) {
dfprintk(VFS, "NFS: not revalidating fake root\n");
status = 0;
goto out_nowait;
}
while (NFS_REVALIDATING(inode)) { while (NFS_REVALIDATING(inode)) {
status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING); status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING);
if (status < 0) if (status < 0)
goto out_nowait; goto out_nowait;
if (time_before(jiffies,NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) { if (NFS_SERVER(inode)->flags & NFS_MOUNT_NOAC)
status = NFS_STALE(inode) ? -ESTALE : 0; continue;
goto out_nowait; if (NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ATIME))
} continue;
status = NFS_STALE(inode) ? -ESTALE : 0;
goto out_nowait;
} }
NFS_FLAGS(inode) |= NFS_INO_REVALIDATING; NFS_FLAGS(inode) |= NFS_INO_REVALIDATING;
/* Protect against RPC races by saving the change attribute */
verifier = nfs_save_change_attribute(inode);
status = NFS_PROTO(inode)->getattr(inode, &fattr); status = NFS_PROTO(inode)->getattr(inode, &fattr);
if (status) { if (status) {
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n", dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n",
...@@ -995,13 +991,36 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) ...@@ -995,13 +991,36 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
goto out; goto out;
} }
status = nfs_refresh_inode(inode, &fattr); status = nfs_update_inode(inode, &fattr, verifier);
if (status) { if (status) {
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n", dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n",
inode->i_sb->s_id, inode->i_sb->s_id,
(long long)NFS_FILEID(inode), status); (long long)NFS_FILEID(inode), status);
goto out; goto out;
} }
flags = nfsi->flags;
/*
* We may need to keep the attributes marked as invalid if
* we raced with nfs_end_attr_update().
*/
if (verifier == nfsi->cache_change_attribute)
nfsi->flags &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME);
/* Do the page cache invalidation */
if (flags & NFS_INO_INVALID_DATA) {
if (S_ISREG(inode->i_mode)) {
if (filemap_fdatawrite(inode->i_mapping) == 0)
filemap_fdatawait(inode->i_mapping);
nfs_wb_all(inode);
}
nfsi->flags &= ~NFS_INO_INVALID_DATA;
invalidate_inode_pages2(inode->i_mapping);
memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n",
inode->i_sb->s_id,
(long long)NFS_FILEID(inode));
/* This ensures we revalidate dentries */
nfsi->cache_change_attribute++;
}
dfprintk(PAGECACHE, "NFS: (%s/%Ld) revalidation complete\n", dfprintk(PAGECACHE, "NFS: (%s/%Ld) revalidation complete\n",
inode->i_sb->s_id, inode->i_sb->s_id,
(long long)NFS_FILEID(inode)); (long long)NFS_FILEID(inode));
...@@ -1009,41 +1028,107 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) ...@@ -1009,41 +1028,107 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
NFS_FLAGS(inode) &= ~NFS_INO_STALE; NFS_FLAGS(inode) &= ~NFS_INO_STALE;
out: out:
NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING; NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING;
wake_up(&NFS_I(inode)->nfs_i_wait); wake_up(&nfsi->nfs_i_wait);
out_nowait: out_nowait:
unlock_kernel(); unlock_kernel();
return status; return status;
} }
/* /**
* nfs_fattr_obsolete - Test if attribute data is newer than cached data * nfs_begin_data_update
* @inode: inode * @inode - pointer to inode
* @fattr: attributes to test * 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
*/
void nfs_end_data_update(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
/* Mark the attribute cache for revalidation */
nfsi->flags |= NFS_INO_INVALID_ATTR;
/* Directories and symlinks: invalidate page cache too */
if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
nfsi->flags |= NFS_INO_INVALID_DATA;
nfsi->cache_change_attribute ++;
atomic_dec(&nfsi->data_updates);
}
/**
* nfs_refresh_inode - verify consistency of the inode attribute cache
* @inode - pointer to inode
* @fattr - updated attributes
* *
* Avoid stuffing the attribute cache with obsolete information. * Verifies the attribute cache. If we have just changed the attributes,
* We always accept updates if the attribute cache timed out, or if * so that fattr carries weak cache consistency data, then it may
* fattr->ctime is newer than our cached value. * also update the ctime/mtime/change_attribute.
* If fattr->ctime matches the cached value, we still accept the update
* if it increases the file size.
*/ */
static inline int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
long cdif; loff_t cur_size, new_isize;
int data_unstable;
if (time_after(jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo))
goto out_valid; /* Are we in the process of updating data on the server? */
cdif = fattr->ctime.tv_sec - nfsi->read_cache_ctime.tv_sec; data_unstable = nfs_caches_unstable(inode);
if (cdif == 0)
cdif = fattr->ctime.tv_nsec - nfsi->read_cache_ctime.tv_nsec; if (fattr->valid & NFS_ATTR_FATTR_V4) {
if (cdif > 0) if ((fattr->valid & NFS_ATTR_PRE_CHANGE) != 0
goto out_valid; && nfsi->change_attr == fattr->pre_change_attr)
/* Ugh... */ nfsi->change_attr = fattr->change_attr;
if (cdif == 0 && fattr->size > nfsi->read_cache_isize) if (!data_unstable && nfsi->change_attr != fattr->change_attr)
goto out_valid; nfsi->flags |= NFS_INO_INVALID_ATTR;
return -1; }
out_valid:
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))
return -EIO;
cur_size = i_size_read(inode);
new_isize = nfs_size_to_loff_t(fattr->size);
/* 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))
memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
if (timespec_equal(&inode->i_mtime, &fattr->pre_mtime))
memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
}
/* Verify a few of the more important attributes */
if (!data_unstable) {
if (!timespec_equal(&inode->i_mtime, &fattr->mtime)
|| cur_size != new_isize)
nfsi->flags |= NFS_INO_INVALID_ATTR;
} else if (S_ISREG(inode->i_mode) && new_isize > cur_size)
nfsi->flags |= NFS_INO_INVALID_ATTR;
/* 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->flags |= NFS_INO_INVALID_ATTR;
/* Has the link count changed? */
if (inode->i_nlink != fattr->nlink)
nfsi->flags |= NFS_INO_INVALID_ATTR;
if (!timespec_equal(&inode->i_atime, &fattr->atime))
nfsi->flags |= NFS_INO_INVALID_ATIME;
nfsi->read_cache_jiffies = fattr->timestamp;
return 0; return 0;
} }
...@@ -1059,20 +1144,22 @@ int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1059,20 +1144,22 @@ int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr)
* *
* A very similar scenario holds for the dir cache. * A very similar scenario holds for the dir cache.
*/ */
int static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsigned long verifier)
__nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
__u64 new_size; __u64 new_size;
loff_t new_isize; loff_t new_isize;
int invalid = 0; unsigned int invalid = 0;
int mtime_update = 0;
loff_t cur_isize; loff_t cur_isize;
int data_unstable;
dfprintk(VFS, "NFS: refresh_inode(%s/%ld ct=%d info=0x%x)\n", dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n",
inode->i_sb->s_id, inode->i_ino, __FUNCTION__, inode->i_sb->s_id, inode->i_ino,
atomic_read(&inode->i_count), fattr->valid); atomic_read(&inode->i_count), fattr->valid);
if ((fattr->valid & NFS_ATTR_FATTR) == 0)
return 0;
/* First successful call after mount, fill real data. */ /* First successful call after mount, fill real data. */
if (NFS_FAKE_ROOT(inode)) { if (NFS_FAKE_ROOT(inode)) {
dfprintk(VFS, "NFS: updating fake root\n"); dfprintk(VFS, "NFS: updating fake root\n");
...@@ -1081,43 +1168,49 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1081,43 +1168,49 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
} }
if (nfsi->fileid != fattr->fileid) { if (nfsi->fileid != fattr->fileid) {
printk(KERN_ERR "nfs_refresh_inode: inode number mismatch\n" printk(KERN_ERR "%s: inode number mismatch\n"
"expected (%s/0x%Lx), got (%s/0x%Lx)\n", "expected (%s/0x%Lx), got (%s/0x%Lx)\n",
__FUNCTION__,
inode->i_sb->s_id, (long long)nfsi->fileid, inode->i_sb->s_id, (long long)nfsi->fileid,
inode->i_sb->s_id, (long long)fattr->fileid); inode->i_sb->s_id, (long long)fattr->fileid);
goto out_err; goto out_err;
} }
/* Throw out obsolete READDIRPLUS attributes */
if (time_before(fattr->timestamp, NFS_READTIME(inode)))
return 0;
/* /*
* Make sure the inode's type hasn't changed. * Make sure the inode's type hasn't changed.
*/ */
if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
goto out_changed; goto out_changed;
new_size = fattr->size;
new_isize = nfs_size_to_loff_t(fattr->size);
/* Avoid races */
if (nfs_fattr_obsolete(inode, fattr))
goto out_nochange;
/* /*
* Update the read time so we don't revalidate too often. * Update the read time so we don't revalidate too often.
*/ */
nfsi->read_cache_jiffies = fattr->timestamp; nfsi->read_cache_jiffies = fattr->timestamp;
/* /* Are we racing with known updates of the metadata on the server? */
* Note: NFS_CACHE_ISIZE(inode) reflects the state of the cache. data_unstable = ! nfs_verify_change_attribute(inode, verifier);
* NOT inode->i_size!!!
*/ /* Check if the file size agrees */
if (nfsi->read_cache_isize != new_size) { new_size = fattr->size;
new_isize = nfs_size_to_loff_t(fattr->size);
cur_isize = i_size_read(inode);
if (cur_isize != new_size) {
#ifdef NFS_DEBUG_VERBOSE #ifdef NFS_DEBUG_VERBOSE
printk(KERN_DEBUG "NFS: isize change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino); printk(KERN_DEBUG "NFS: isize change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino);
#endif #endif
invalid = 1; /*
* If we have pending writebacks, things can get
* messy.
*/
if (S_ISREG(inode->i_mode) && data_unstable) {
if (new_isize > cur_isize) {
i_size_write(inode, new_isize);
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
}
} else {
i_size_write(inode, new_isize);
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
}
} }
/* /*
...@@ -1125,12 +1218,13 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1125,12 +1218,13 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
* can change this value in VFS without requiring a * can change this value in VFS without requiring a
* cache revalidation. * cache revalidation.
*/ */
if (!timespec_equal(&nfsi->read_cache_mtime, &fattr->mtime)) { if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) {
memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
#ifdef NFS_DEBUG_VERBOSE #ifdef NFS_DEBUG_VERBOSE
printk(KERN_DEBUG "NFS: mtime change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino); printk(KERN_DEBUG "NFS: mtime change on %s/%ld\n", inode->i_sb->s_id, inode->i_ino);
#endif #endif
invalid = 1; if (!data_unstable)
mtime_update = 1; invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
} }
if ((fattr->valid & NFS_ATTR_FATTR_V4) if ((fattr->valid & NFS_ATTR_FATTR_V4)
...@@ -1139,47 +1233,15 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1139,47 +1233,15 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
printk(KERN_DEBUG "NFS: change_attr change on %s/%ld\n", printk(KERN_DEBUG "NFS: change_attr change on %s/%ld\n",
inode->i_sb->s_id, inode->i_ino); inode->i_sb->s_id, inode->i_ino);
#endif #endif
invalid = 1; nfsi->change_attr = fattr->change_attr;
} if (!data_unstable)
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
/* Check Weak Cache Consistency data.
* If size and mtime match the pre-operation values, we can
* assume that any attribute changes were caused by our NFS
* operation, so there's no need to invalidate the caches.
*/
if ((fattr->valid & NFS_ATTR_PRE_CHANGE)
&& nfsi->change_attr == fattr->pre_change_attr) {
invalid = 0;
}
else if ((fattr->valid & NFS_ATTR_WCC)
&& nfsi->read_cache_isize == fattr->pre_size
&& timespec_equal(&nfsi->read_cache_mtime, &fattr->pre_mtime)) {
invalid = 0;
}
/*
* If we have pending writebacks, things can get
* messy.
*/
cur_isize = i_size_read(inode);
if (nfs_have_writebacks(inode) && new_isize < cur_isize)
new_isize = cur_isize;
nfsi->read_cache_ctime = fattr->ctime;
inode->i_ctime = fattr->ctime;
inode->i_atime = fattr->atime;
if (mtime_update) {
if (invalid)
nfsi->cache_mtime_jiffies = fattr->timestamp;
nfsi->read_cache_mtime = fattr->mtime;
inode->i_mtime = fattr->mtime;
} }
nfsi->read_cache_isize = new_size; memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
i_size_write(inode, new_isize); memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime));
if (inode->i_mode != fattr->mode || if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO) ||
inode->i_uid != fattr->uid || inode->i_uid != fattr->uid ||
inode->i_gid != fattr->gid) { inode->i_gid != fattr->gid) {
struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred; struct rpc_cred **cred = &NFS_I(inode)->cache_access.cred;
...@@ -1187,11 +1249,9 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1187,11 +1249,9 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
put_rpccred(*cred); put_rpccred(*cred);
*cred = NULL; *cred = NULL;
} }
invalid |= NFS_INO_INVALID_ATTR;
} }
if (fattr->valid & NFS_ATTR_FATTR_V4)
nfsi->change_attr = fattr->change_attr;
inode->i_mode = fattr->mode; inode->i_mode = fattr->mode;
inode->i_nlink = fattr->nlink; inode->i_nlink = fattr->nlink;
inode->i_uid = fattr->uid; inode->i_uid = fattr->uid;
...@@ -1207,31 +1267,30 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) ...@@ -1207,31 +1267,30 @@ __nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
inode->i_blocks = fattr->du.nfs2.blocks; inode->i_blocks = fattr->du.nfs2.blocks;
inode->i_blksize = fattr->du.nfs2.blocksize; inode->i_blksize = fattr->du.nfs2.blocksize;
} }
/* Update attrtimeo value */ /* Update attrtimeo value if we're out of the unstable period */
if (invalid) { if (invalid & NFS_INO_INVALID_ATTR) {
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = jiffies; nfsi->attrtimeo_timestamp = jiffies;
invalidate_remote_inode(inode);
memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
} else if (time_after(jiffies, nfsi->attrtimeo_timestamp+nfsi->attrtimeo)) { } else if (time_after(jiffies, nfsi->attrtimeo_timestamp+nfsi->attrtimeo)) {
if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode)) if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode))
nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode); nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = jiffies; nfsi->attrtimeo_timestamp = jiffies;
} }
/* 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;
nfsi->flags |= invalid;
return 0; return 0;
out_nochange:
if (!timespec_equal(&fattr->atime, &inode->i_atime))
inode->i_atime = fattr->atime;
return 0;
out_changed: out_changed:
/* /*
* Big trouble! The inode has become a different object. * Big trouble! The inode has become a different object.
*/ */
#ifdef NFS_PARANOIA #ifdef NFS_PARANOIA
printk(KERN_DEBUG "nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n", printk(KERN_DEBUG "%s: inode %ld mode changed, %07o to %07o\n",
inode->i_ino, inode->i_mode, fattr->mode); __FUNCTION__, inode->i_ino, inode->i_mode, fattr->mode);
#endif #endif
/* /*
* No need to worry about unhashing the dentry, as the * No need to worry about unhashing the dentry, as the
...@@ -1718,6 +1777,7 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) ...@@ -1718,6 +1777,7 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
INIT_LIST_HEAD(&nfsi->dirty); INIT_LIST_HEAD(&nfsi->dirty);
INIT_LIST_HEAD(&nfsi->commit); INIT_LIST_HEAD(&nfsi->commit);
INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC); INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC);
atomic_set(&nfsi->data_updates, 0);
nfsi->ndirty = 0; nfsi->ndirty = 0;
nfsi->ncommit = 0; nfsi->ncommit = 0;
nfsi->npages = 0; nfsi->npages = 0;
......
...@@ -68,20 +68,6 @@ nfs3_async_handle_jukebox(struct rpc_task *task) ...@@ -68,20 +68,6 @@ nfs3_async_handle_jukebox(struct rpc_task *task)
return 1; return 1;
} }
static void
nfs3_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
{
if (fattr->valid & NFS_ATTR_FATTR) {
if (!(fattr->valid & NFS_ATTR_WCC)) {
fattr->pre_size = NFS_CACHE_ISIZE(inode);
fattr->pre_mtime = NFS_CACHE_MTIME(inode);
fattr->pre_ctime = NFS_CACHE_CTIME(inode);
fattr->valid |= NFS_ATTR_WCC;
}
nfs_refresh_inode(inode, fattr);
}
}
static struct rpc_cred * static struct rpc_cred *
nfs_cred(struct inode *inode, struct file *filp) nfs_cred(struct inode *inode, struct file *filp)
{ {
...@@ -280,7 +266,7 @@ nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp) ...@@ -280,7 +266,7 @@ nfs3_proc_write(struct nfs_write_data *wdata, struct file *filp)
msg.rpc_cred = nfs_cred(inode, filp); msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags); status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags);
if (status >= 0) if (status >= 0)
nfs3_write_refresh_inode(inode, fattr); nfs_refresh_inode(inode, fattr);
dprintk("NFS reply write: %d\n", status); dprintk("NFS reply write: %d\n", status);
return status < 0? status : wdata->res.count; return status < 0? status : wdata->res.count;
} }
...@@ -303,7 +289,7 @@ nfs3_proc_commit(struct nfs_write_data *cdata, struct file *filp) ...@@ -303,7 +289,7 @@ nfs3_proc_commit(struct nfs_write_data *cdata, struct file *filp)
msg.rpc_cred = nfs_cred(inode, filp); msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (status >= 0) if (status >= 0)
nfs3_write_refresh_inode(inode, fattr); nfs_refresh_inode(inode, fattr);
dprintk("NFS reply commit: %d\n", status); dprintk("NFS reply commit: %d\n", status);
return status; return status;
} }
...@@ -777,12 +763,13 @@ nfs3_proc_read_setup(struct nfs_read_data *data, unsigned int count) ...@@ -777,12 +763,13 @@ nfs3_proc_read_setup(struct nfs_read_data *data, unsigned int count)
static void static void
nfs3_write_done(struct rpc_task *task) nfs3_write_done(struct rpc_task *task)
{ {
struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; struct nfs_write_data *data;
if (nfs3_async_handle_jukebox(task)) if (nfs3_async_handle_jukebox(task))
return; return;
data = (struct nfs_write_data *)task->tk_calldata;
if (task->tk_status >= 0) if (task->tk_status >= 0)
nfs3_write_refresh_inode(data->inode, data->res.fattr); nfs_refresh_inode(data->inode, data->res.fattr);
nfs_writeback_done(task); nfs_writeback_done(task);
} }
...@@ -835,12 +822,13 @@ nfs3_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how) ...@@ -835,12 +822,13 @@ nfs3_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how)
static void static void
nfs3_commit_done(struct rpc_task *task) nfs3_commit_done(struct rpc_task *task)
{ {
struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; struct nfs_write_data *data;
if (nfs3_async_handle_jukebox(task)) if (nfs3_async_handle_jukebox(task))
return; return;
data = (struct nfs_write_data *)task->tk_calldata;
if (task->tk_status >= 0) if (task->tk_status >= 0)
nfs3_write_refresh_inode(data->inode, data->res.fattr); nfs_refresh_inode(data->inode, data->res.fattr);
nfs_commit_done(task); nfs_commit_done(task);
} }
......
...@@ -1088,12 +1088,8 @@ nfs4_proc_read(struct nfs_read_data *rdata, struct file *filp) ...@@ -1088,12 +1088,8 @@ nfs4_proc_read(struct nfs_read_data *rdata, struct file *filp)
fattr->valid = 0; fattr->valid = 0;
status = rpc_call_sync(server->client, &msg, flags); status = rpc_call_sync(server->client, &msg, flags);
if (!status) { if (!status)
renew_lease(server, timestamp); renew_lease(server, timestamp);
/* Check cache consistency */
if (fattr->change_attr != NFS_CHANGE_ATTR(inode))
nfs_zap_caches(inode);
}
dprintk("NFS reply read: %d\n", status); dprintk("NFS reply read: %d\n", status);
return status; return status;
} }
...@@ -1130,7 +1126,6 @@ nfs4_proc_write(struct nfs_write_data *wdata, struct file *filp) ...@@ -1130,7 +1126,6 @@ nfs4_proc_write(struct nfs_write_data *wdata, struct file *filp)
fattr->valid = 0; fattr->valid = 0;
status = rpc_call_sync(server->client, &msg, rpcflags); status = rpc_call_sync(server->client, &msg, rpcflags);
NFS_CACHEINV(inode);
dprintk("NFS reply write: %d\n", status); dprintk("NFS reply write: %d\n", status);
return status; return status;
} }
...@@ -1517,7 +1512,6 @@ nfs4_read_done(struct rpc_task *task) ...@@ -1517,7 +1512,6 @@ nfs4_read_done(struct rpc_task *task)
{ {
struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata; struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata;
struct inode *inode = data->inode; struct inode *inode = data->inode;
struct nfs_fattr *fattr = data->res.fattr;
if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) {
task->tk_action = nfs4_restart_read; task->tk_action = nfs4_restart_read;
...@@ -1525,11 +1519,6 @@ nfs4_read_done(struct rpc_task *task) ...@@ -1525,11 +1519,6 @@ nfs4_read_done(struct rpc_task *task)
} }
if (task->tk_status > 0) if (task->tk_status > 0)
renew_lease(NFS_SERVER(inode), data->timestamp); renew_lease(NFS_SERVER(inode), data->timestamp);
/* Check cache consistency */
if (fattr->change_attr != NFS_CHANGE_ATTR(inode))
nfs_zap_caches(inode);
if (fattr->bitmap[1] & FATTR4_WORD1_TIME_ACCESS)
inode->i_atime = fattr->atime;
/* Call back common NFS readpage processing */ /* Call back common NFS readpage processing */
nfs_readpage_result(task); nfs_readpage_result(task);
} }
...@@ -1576,21 +1565,6 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count) ...@@ -1576,21 +1565,6 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count)
rpc_call_setup(task, &msg, 0); rpc_call_setup(task, &msg, 0);
} }
static void
nfs4_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
{
/* Check cache consistency */
if (fattr->pre_change_attr != NFS_CHANGE_ATTR(inode))
nfs_zap_caches(inode);
NFS_CHANGE_ATTR(inode) = fattr->change_attr;
if (fattr->bitmap[1] & FATTR4_WORD1_SPACE_USED)
inode->i_blocks = (fattr->du.nfs3.used + 511) >> 9;
if (fattr->bitmap[1] & FATTR4_WORD1_TIME_METADATA)
inode->i_ctime = fattr->ctime;
if (fattr->bitmap[1] & FATTR4_WORD1_TIME_MODIFY)
inode->i_mtime = fattr->mtime;
}
static void static void
nfs4_restart_write(struct rpc_task *task) nfs4_restart_write(struct rpc_task *task)
{ {
...@@ -1617,7 +1591,6 @@ nfs4_write_done(struct rpc_task *task) ...@@ -1617,7 +1591,6 @@ nfs4_write_done(struct rpc_task *task)
} }
if (task->tk_status >= 0) if (task->tk_status >= 0)
renew_lease(NFS_SERVER(inode), data->timestamp); renew_lease(NFS_SERVER(inode), data->timestamp);
nfs4_write_refresh_inode(inode, data->res.fattr);
/* Call back common NFS writeback processing */ /* Call back common NFS writeback processing */
nfs_writeback_done(task); nfs_writeback_done(task);
} }
...@@ -1684,7 +1657,6 @@ nfs4_commit_done(struct rpc_task *task) ...@@ -1684,7 +1657,6 @@ nfs4_commit_done(struct rpc_task *task)
task->tk_action = nfs4_restart_write; task->tk_action = nfs4_restart_write;
return; return;
} }
nfs4_write_refresh_inode(inode, data->res.fattr);
/* Call back common NFS writeback processing */ /* Call back common NFS writeback processing */
nfs_commit_done(task); nfs_commit_done(task);
} }
...@@ -1807,6 +1779,7 @@ nfs4_proc_file_open(struct inode *inode, struct file *filp) ...@@ -1807,6 +1779,7 @@ nfs4_proc_file_open(struct inode *inode, struct file *filp)
if (filp->f_mode & FMODE_WRITE) { if (filp->f_mode & FMODE_WRITE) {
lock_kernel(); lock_kernel();
nfs_set_mmcred(inode, state->owner->so_cred); nfs_set_mmcred(inode, state->owner->so_cred);
nfs_begin_data_update(inode);
unlock_kernel(); unlock_kernel();
} }
filp->private_data = state; filp->private_data = state;
...@@ -1823,6 +1796,11 @@ nfs4_proc_file_release(struct inode *inode, struct file *filp) ...@@ -1823,6 +1796,11 @@ nfs4_proc_file_release(struct inode *inode, struct file *filp)
if (state) if (state)
nfs4_close_state(state, filp->f_mode); nfs4_close_state(state, filp->f_mode);
if (filp->f_mode & FMODE_WRITE) {
lock_kernel();
nfs_end_data_update(inode);
unlock_kernel();
}
return 0; return 0;
} }
......
...@@ -49,18 +49,6 @@ ...@@ -49,18 +49,6 @@
extern struct rpc_procinfo nfs_procedures[]; extern struct rpc_procinfo nfs_procedures[];
static void
nfs_write_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
{
if (!(fattr->valid & NFS_ATTR_WCC)) {
fattr->pre_size = NFS_CACHE_ISIZE(inode);
fattr->pre_mtime = NFS_CACHE_MTIME(inode);
fattr->pre_ctime = NFS_CACHE_CTIME(inode);
fattr->valid |= NFS_ATTR_WCC;
}
nfs_refresh_inode(inode, fattr);
}
static struct rpc_cred * static struct rpc_cred *
nfs_cred(struct inode *inode, struct file *filp) nfs_cred(struct inode *inode, struct file *filp)
{ {
...@@ -205,7 +193,7 @@ nfs_proc_write(struct nfs_write_data *wdata, struct file *filp) ...@@ -205,7 +193,7 @@ nfs_proc_write(struct nfs_write_data *wdata, struct file *filp)
msg.rpc_cred = nfs_cred(inode, filp); msg.rpc_cred = nfs_cred(inode, filp);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags); status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0) { if (status >= 0) {
nfs_write_refresh_inode(inode, fattr); nfs_refresh_inode(inode, fattr);
wdata->res.count = wdata->args.count; wdata->res.count = wdata->args.count;
wdata->verf.committed = NFS_FILE_SYNC; wdata->verf.committed = NFS_FILE_SYNC;
} }
...@@ -331,10 +319,8 @@ nfs_proc_unlink_done(struct dentry *dir, struct rpc_task *task) ...@@ -331,10 +319,8 @@ nfs_proc_unlink_done(struct dentry *dir, struct rpc_task *task)
{ {
struct rpc_message *msg = &task->tk_msg; struct rpc_message *msg = &task->tk_msg;
if (msg->rpc_argp) { if (msg->rpc_argp)
NFS_CACHEINV(dir->d_inode);
kfree(msg->rpc_argp); kfree(msg->rpc_argp);
}
return 0; return 0;
} }
...@@ -584,7 +570,7 @@ nfs_write_done(struct rpc_task *task) ...@@ -584,7 +570,7 @@ nfs_write_done(struct rpc_task *task)
struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
if (task->tk_status >= 0) if (task->tk_status >= 0)
nfs_write_refresh_inode(data->inode, data->res.fattr); nfs_refresh_inode(data->inode, data->res.fattr);
nfs_writeback_done(task); nfs_writeback_done(task);
} }
......
...@@ -124,6 +124,7 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page) ...@@ -124,6 +124,7 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page)
if (result < rdata.args.count) /* NFSv2ism */ if (result < rdata.args.count) /* NFSv2ism */
break; break;
} while (count); } while (count);
NFS_FLAGS(inode) |= NFS_INO_INVALID_ATIME;
if (count) if (count)
memclear_highpage_flush(page, rdata.args.pgbase, count); memclear_highpage_flush(page, rdata.args.pgbase, count);
...@@ -266,6 +267,7 @@ nfs_readpage_result(struct rpc_task *task) ...@@ -266,6 +267,7 @@ nfs_readpage_result(struct rpc_task *task)
dprintk("NFS: %4d nfs_readpage_result, (status %d)\n", dprintk("NFS: %4d nfs_readpage_result, (status %d)\n",
task->tk_pid, task->tk_status); task->tk_pid, task->tk_status);
NFS_FLAGS(data->inode) |= NFS_INO_INVALID_ATIME;
while (!list_empty(&data->pages)) { while (!list_empty(&data->pages)) {
struct nfs_page *req = nfs_list_entry(data->pages.next); struct nfs_page *req = nfs_list_entry(data->pages.next);
struct page *page = req->wb_page; struct page *page = req->wb_page;
......
...@@ -104,6 +104,7 @@ nfs_async_unlink_init(struct rpc_task *task) ...@@ -104,6 +104,7 @@ nfs_async_unlink_init(struct rpc_task *task)
status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name); status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name);
if (status < 0) if (status < 0)
goto out_err; goto out_err;
nfs_begin_data_update(dir->d_inode);
rpc_call_setup(task, &msg, 0); rpc_call_setup(task, &msg, 0);
return; return;
out_err: out_err:
...@@ -126,7 +127,7 @@ nfs_async_unlink_done(struct rpc_task *task) ...@@ -126,7 +127,7 @@ nfs_async_unlink_done(struct rpc_task *task)
if (!dir) if (!dir)
return; return;
dir_i = dir->d_inode; dir_i = dir->d_inode;
nfs_zap_caches(dir_i); nfs_end_data_update(dir_i);
if (NFS_PROTO(dir_i)->unlink_done(dir, task)) if (NFS_PROTO(dir_i)->unlink_done(dir, task))
return; return;
put_rpccred(data->cred); put_rpccred(data->cred);
......
...@@ -157,6 +157,7 @@ nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page, ...@@ -157,6 +157,7 @@ nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page,
(long long)NFS_FILEID(inode), (long long)NFS_FILEID(inode),
count, (long long)(page_offset(page) + offset)); count, (long long)(page_offset(page) + offset));
nfs_begin_data_update(inode);
do { do {
if (count < wsize && !swapfile) if (count < wsize && !swapfile)
wdata.args.count = count; wdata.args.count = count;
...@@ -190,15 +191,15 @@ nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page, ...@@ -190,15 +191,15 @@ nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page,
ClearPageError(page); ClearPageError(page);
io_error: io_error:
nfs_end_data_update(inode);
if (wdata.cred) if (wdata.cred)
put_rpccred(wdata.cred); put_rpccred(wdata.cred);
return written ? written : result; return written ? written : result;
} }
static int static int nfs_writepage_async(struct file *file, struct inode *inode,
nfs_writepage_async(struct file *file, struct inode *inode, struct page *page, struct page *page, unsigned int offset, unsigned int count)
unsigned int offset, unsigned int count)
{ {
struct nfs_page *req; struct nfs_page *req;
loff_t end; loff_t end;
...@@ -213,7 +214,6 @@ nfs_writepage_async(struct file *file, struct inode *inode, struct page *page, ...@@ -213,7 +214,6 @@ nfs_writepage_async(struct file *file, struct inode *inode, struct page *page,
end = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count); end = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count);
if (i_size_read(inode) < end) if (i_size_read(inode) < end)
i_size_write(inode, end); i_size_write(inode, end);
out: out:
return status; return status;
} }
...@@ -312,8 +312,10 @@ nfs_inode_add_request(struct inode *inode, struct nfs_page *req) ...@@ -312,8 +312,10 @@ nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
BUG_ON(error == -EEXIST); BUG_ON(error == -EEXIST);
if (error) if (error)
return error; return error;
if (!nfsi->npages) if (!nfsi->npages) {
igrab(inode); igrab(inode);
nfs_begin_data_update(inode);
}
nfsi->npages++; nfsi->npages++;
req->wb_count++; req->wb_count++;
return 0; return 0;
...@@ -336,6 +338,7 @@ nfs_inode_remove_request(struct nfs_page *req) ...@@ -336,6 +338,7 @@ nfs_inode_remove_request(struct nfs_page *req)
nfsi->npages--; nfsi->npages--;
if (!nfsi->npages) { if (!nfsi->npages) {
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfs_wreq_lock);
nfs_end_data_update(inode);
iput(inode); iput(inode);
} else } else
spin_unlock(&nfs_wreq_lock); spin_unlock(&nfs_wreq_lock);
...@@ -891,10 +894,7 @@ nfs_writeback_done(struct rpc_task *task) ...@@ -891,10 +894,7 @@ nfs_writeback_done(struct rpc_task *task)
#endif #endif
/* /*
* Update attributes as result of writeback. * Process the nfs_page list
* FIXME: There is an inherent race with invalidate_inode_pages and
* writebacks since the page->count is kept > 1 for as long
* as the page has a write request pending.
*/ */
while (!list_empty(&data->pages)) { while (!list_empty(&data->pages)) {
req = nfs_list_entry(data->pages.next); req = nfs_list_entry(data->pages.next);
......
...@@ -138,6 +138,7 @@ extern int leases_enable, dir_notify_enable, lease_break_time; ...@@ -138,6 +138,7 @@ extern int leases_enable, dir_notify_enable, lease_break_time;
#define S_DEAD 32 /* removed, but still open directory */ #define S_DEAD 32 /* removed, but still open directory */
#define S_NOQUOTA 64 /* Inode is not counted to quota */ #define S_NOQUOTA 64 /* Inode is not counted to quota */
#define S_DIRSYNC 128 /* Directory modifications are synchronous */ #define S_DIRSYNC 128 /* Directory modifications are synchronous */
#define S_NOCMTIME 256 /* Do not update file c/mtime */
/* /*
* Note that nosuid etc flags are inode-specific: setting some file-system * Note that nosuid etc flags are inode-specific: setting some file-system
...@@ -171,6 +172,7 @@ extern int leases_enable, dir_notify_enable, lease_break_time; ...@@ -171,6 +172,7 @@ extern int leases_enable, dir_notify_enable, lease_break_time;
#define IS_ONE_SECOND(inode) __IS_FLG(inode, MS_ONE_SECOND) #define IS_ONE_SECOND(inode) __IS_FLG(inode, MS_ONE_SECOND)
#define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD) #define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD)
#define IS_NOCMTIME(inode) ((inode)->i_flags & S_NOCMTIME)
/* the read-only stuff doesn't really belong here, but any other place is /* the read-only stuff doesn't really belong here, but any other place is
probably as bad and I don't want to create yet another include file. */ probably as bad and I don't want to create yet another include file. */
......
...@@ -99,7 +99,7 @@ struct nfs_inode { ...@@ -99,7 +99,7 @@ struct nfs_inode {
/* /*
* Various flags * Various flags
*/ */
unsigned short flags; unsigned int flags;
/* /*
* read_cache_jiffies is when we started read-caching this inode, * read_cache_jiffies is when we started read-caching this inode,
...@@ -118,19 +118,22 @@ struct nfs_inode { ...@@ -118,19 +118,22 @@ struct nfs_inode {
* *
* mtime != read_cache_mtime * mtime != read_cache_mtime
*/ */
unsigned long readdir_timestamp;
unsigned long read_cache_jiffies; unsigned long read_cache_jiffies;
struct timespec read_cache_ctime;
struct timespec read_cache_mtime;
__u64 read_cache_isize;
unsigned long attrtimeo; unsigned long attrtimeo;
unsigned long attrtimeo_timestamp; unsigned long attrtimeo_timestamp;
__u64 change_attr; /* v4 only */ __u64 change_attr; /* v4 only */
/* "Generation counter" for the attribute cache. This is
* bumped whenever we update the metadata on the
* server.
*/
unsigned long cache_change_attribute;
/* /*
* Timestamp that dates the change made to read_cache_mtime. * Counter indicating the number of outstanding requests that
* This is of use for dentry revalidation * will cause a file data update.
*/ */
unsigned long cache_mtime_jiffies; atomic_t data_updates;
struct nfs_access_cache cache_access; struct nfs_access_cache cache_access;
...@@ -170,7 +173,9 @@ struct nfs_inode { ...@@ -170,7 +173,9 @@ struct nfs_inode {
#define NFS_INO_STALE 0x0001 /* possible stale inode */ #define NFS_INO_STALE 0x0001 /* possible stale inode */
#define NFS_INO_ADVISE_RDPLUS 0x0002 /* advise readdirplus */ #define NFS_INO_ADVISE_RDPLUS 0x0002 /* advise readdirplus */
#define NFS_INO_REVALIDATING 0x0004 /* revalidating attrs */ #define NFS_INO_REVALIDATING 0x0004 /* revalidating attrs */
#define NFS_INO_FLUSH 0x0008 /* inode is due for flushing */ #define NFS_INO_INVALID_ATTR 0x0008 /* cached attrs are invalid */
#define NFS_INO_INVALID_DATA 0x0010 /* cached data is invalid */
#define NFS_INO_INVALID_ATIME 0x0020 /* cached atime is invalid */
#define NFS_INO_FAKE_ROOT 0x0080 /* root inode placeholder */ #define NFS_INO_FAKE_ROOT 0x0080 /* root inode placeholder */
static inline struct nfs_inode *NFS_I(struct inode *inode) static inline struct nfs_inode *NFS_I(struct inode *inode)
...@@ -186,15 +191,7 @@ static inline struct nfs_inode *NFS_I(struct inode *inode) ...@@ -186,15 +191,7 @@ static inline struct nfs_inode *NFS_I(struct inode *inode)
#define NFS_ADDR(inode) (RPC_PEERADDR(NFS_CLIENT(inode))) #define NFS_ADDR(inode) (RPC_PEERADDR(NFS_CLIENT(inode)))
#define NFS_COOKIEVERF(inode) (NFS_I(inode)->cookieverf) #define NFS_COOKIEVERF(inode) (NFS_I(inode)->cookieverf)
#define NFS_READTIME(inode) (NFS_I(inode)->read_cache_jiffies) #define NFS_READTIME(inode) (NFS_I(inode)->read_cache_jiffies)
#define NFS_MTIME_UPDATE(inode) (NFS_I(inode)->cache_mtime_jiffies)
#define NFS_CACHE_CTIME(inode) (NFS_I(inode)->read_cache_ctime)
#define NFS_CACHE_MTIME(inode) (NFS_I(inode)->read_cache_mtime)
#define NFS_CACHE_ISIZE(inode) (NFS_I(inode)->read_cache_isize)
#define NFS_CHANGE_ATTR(inode) (NFS_I(inode)->change_attr) #define NFS_CHANGE_ATTR(inode) (NFS_I(inode)->change_attr)
#define NFS_CACHEINV(inode) \
do { \
NFS_READTIME(inode) = jiffies - NFS_MAXATTRTIMEO(inode) - 1; \
} while (0)
#define NFS_ATTRTIMEO(inode) (NFS_I(inode)->attrtimeo) #define NFS_ATTRTIMEO(inode) (NFS_I(inode)->attrtimeo)
#define NFS_MINATTRTIMEO(inode) \ #define NFS_MINATTRTIMEO(inode) \
(S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmin \ (S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmin \
...@@ -211,6 +208,17 @@ do { \ ...@@ -211,6 +208,17 @@ do { \
#define NFS_FILEID(inode) (NFS_I(inode)->fileid) #define NFS_FILEID(inode) (NFS_I(inode)->fileid)
static inline int nfs_caches_unstable(struct inode *inode)
{
return atomic_read(&NFS_I(inode)->data_updates) != 0;
}
static inline void NFS_CACHEINV(struct inode *inode)
{
if (!nfs_caches_unstable(inode))
NFS_FLAGS(inode) |= NFS_INO_INVALID_ATTR;
}
static inline int nfs_server_capable(struct inode *inode, int cap) static inline int nfs_server_capable(struct inode *inode, int cap)
{ {
return NFS_SERVER(inode)->caps & cap; return NFS_SERVER(inode)->caps & cap;
...@@ -227,13 +235,37 @@ loff_t page_offset(struct page *page) ...@@ -227,13 +235,37 @@ loff_t page_offset(struct page *page)
return ((loff_t)page->index) << PAGE_CACHE_SHIFT; return ((loff_t)page->index) << PAGE_CACHE_SHIFT;
} }
/**
* nfs_save_change_attribute - Returns the inode attribute change cookie
* @inode - pointer to inode
* The "change attribute" is updated every time we finish an operation
* that will result in a metadata change on the server.
*/
static inline long nfs_save_change_attribute(struct inode *inode)
{
return NFS_I(inode)->cache_change_attribute;
}
/**
* nfs_verify_change_attribute - Detects NFS inode cache updates
* @inode - pointer to 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.
*/
static inline int nfs_verify_change_attribute(struct inode *inode, unsigned long chattr)
{
return !nfs_caches_unstable(inode)
&& chattr == NFS_I(inode)->cache_change_attribute;
}
/* /*
* linux/fs/nfs/inode.c * linux/fs/nfs/inode.c
*/ */
extern void nfs_zap_caches(struct inode *); extern void nfs_zap_caches(struct inode *);
extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *, extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *,
struct nfs_fattr *); struct nfs_fattr *);
extern int __nfs_refresh_inode(struct inode *, struct nfs_fattr *); extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
extern int nfs_permission(struct inode *, int, struct nameidata *); extern int nfs_permission(struct inode *, int, struct nameidata *);
extern void nfs_set_mmcred(struct inode *, struct rpc_cred *); extern void nfs_set_mmcred(struct inode *, struct rpc_cred *);
...@@ -241,6 +273,10 @@ extern int nfs_open(struct inode *, struct file *); ...@@ -241,6 +273,10 @@ extern int nfs_open(struct inode *, struct file *);
extern int nfs_release(struct inode *, struct file *); extern int nfs_release(struct inode *, struct file *);
extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *); extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
extern int nfs_setattr(struct dentry *, struct iattr *); extern int nfs_setattr(struct dentry *, struct iattr *);
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 *);
/* /*
* linux/fs/nfs/file.c * linux/fs/nfs/file.c
...@@ -383,20 +419,27 @@ extern int nfsroot_mount(struct sockaddr_in *, char *, struct nfs_fh *, ...@@ -383,20 +419,27 @@ extern int nfsroot_mount(struct sockaddr_in *, char *, struct nfs_fh *,
/* /*
* inline functions * inline functions
*/ */
static inline int
nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) static inline int nfs_attribute_timeout(struct inode *inode)
{ {
if (time_before(jiffies, NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) struct nfs_inode *nfsi = NFS_I(inode);
return NFS_STALE(inode) ? -ESTALE : 0;
return __nfs_revalidate_inode(server, inode); return time_after(jiffies, nfsi->read_cache_jiffies+nfsi->attrtimeo);
} }
static inline int /**
nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) * nfs_revalidate_inode - Revalidate the inode attributes
* @server - pointer to nfs_server struct
* @inode - pointer to inode struct
*
* Updates inode attribute information by retrieving the data from the server.
*/
static inline int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
{ {
if ((fattr->valid & NFS_ATTR_FATTR) == 0) if (!(NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))
return 0; && !nfs_attribute_timeout(inode))
return __nfs_refresh_inode(inode,fattr); return NFS_STALE(inode) ? -ESTALE : 0;
return __nfs_revalidate_inode(server, inode);
} }
static inline loff_t static inline loff_t
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment