Commit ef087040 authored by Trond Myklebust's avatar Trond Myklebust Committed by Linus Torvalds

[PATCH] Support for NFSv4 READ + WRITE attribute cache consistency

Retrieve the post-operation attribute changes for NFSv4 READ and
WRITE operations. Unlike for NFSv2 and NFSv3, we do not retrieve the
full set of file attributes. The main reason for this is that
interpreting attributes is a much heavier task on NFSv4 (requiring, for
instance, translation of file owner names into uids ...). Hence

  For a READ request, we retrieve only the 'change attribute' (for cache
  consistency checking) and the atime.

  For a WRITE request, we retrieve the 'change attribute' and the file size.
  In addition, we retrieve the value of the change attribute prior to the
  write operation, in order to be able to do weak cache consistency checking.
parent c5d74703
......@@ -67,6 +67,20 @@ nfs3_async_handle_jukebox(struct rpc_task *task)
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);
}
}
/*
* Bare-bones access to getattr: this is for nfs_read_super.
*/
......@@ -239,6 +253,8 @@ nfs3_proc_read(struct inode *inode, struct rpc_cred *cred,
dprintk("NFS call read %d @ %Ld\n", count, (long long)offset);
fattr->valid = 0;
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0)
nfs_refresh_inode(inode, fattr);
dprintk("NFS reply read: %d\n", status);
*eofp = res.eof;
return status;
......@@ -279,6 +295,9 @@ nfs3_proc_write(struct inode *inode, struct rpc_cred *cred,
status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags);
if (status >= 0)
nfs3_write_refresh_inode(inode, fattr);
dprintk("NFS reply read: %d\n", status);
return status < 0? status : res.count;
}
......@@ -685,9 +704,13 @@ extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int);
static void
nfs3_read_done(struct rpc_task *task)
{
struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
if (nfs3_async_handle_jukebox(task))
return;
/* Call back common NFS readpage processing */
if (task->tk_status >= 0)
nfs_refresh_inode(data->inode, &data->fattr);
nfs_readpage_result(task);
}
......@@ -730,8 +753,12 @@ nfs3_proc_read_setup(struct nfs_read_data *data, unsigned int count)
static void
nfs3_write_done(struct rpc_task *task)
{
struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
if (nfs3_async_handle_jukebox(task))
return;
if (task->tk_status >= 0)
nfs3_write_refresh_inode(data->inode, data->res.fattr);
nfs_writeback_done(task);
}
......@@ -784,8 +811,12 @@ nfs3_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how)
static void
nfs3_commit_done(struct rpc_task *task)
{
struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
if (nfs3_async_handle_jukebox(task))
return;
if (task->tk_status >= 0)
nfs3_write_refresh_inode(data->inode, data->res.fattr);
nfs_commit_done(task);
}
......
......@@ -980,8 +980,12 @@ nfs4_proc_read(struct inode *inode, struct rpc_cred *cred,
dprintk("NFS call read %d @ %Ld\n", count, (long long)offset);
fattr->valid = 0;
status = rpc_call_sync(server->client, &msg, flags);
if (!status)
if (!status) {
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);
*eofp = res.eof;
return status;
......@@ -1338,9 +1342,16 @@ static void
nfs4_read_done(struct rpc_task *task)
{
struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata;
struct inode *inode = data->inode;
struct nfs_fattr *fattr = data->res.fattr;
if (task->tk_status > 0)
renew_lease(NFS_SERVER(data->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 */
nfs_readpage_result(task);
}
......@@ -1381,13 +1392,30 @@ nfs4_proc_read_setup(struct nfs_read_data *data, unsigned int count)
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
nfs4_write_done(struct rpc_task *task)
{
struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
struct inode *inode = data->inode;
if (task->tk_status > 0)
renew_lease(NFS_SERVER(data->inode), data->timestamp);
if (task->tk_status >= 0)
renew_lease(NFS_SERVER(inode), data->timestamp);
nfs4_write_refresh_inode(inode, data->res.fattr);
/* Call back common NFS writeback processing */
nfs_writeback_done(task);
}
......@@ -1438,6 +1466,16 @@ nfs4_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how)
rpc_call_setup(task, &msg, 0);
}
static void
nfs4_commit_done(struct rpc_task *task)
{
struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
nfs4_write_refresh_inode(data->inode, data->res.fattr);
/* Call back common NFS writeback processing */
nfs_commit_done(task);
}
static void
nfs4_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how)
{
......@@ -1462,7 +1500,7 @@ nfs4_proc_commit_setup(struct nfs_write_data *data, u64 start, u32 len, int how)
flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
/* Finalize the task. */
rpc_init_task(task, NFS_CLIENT(inode), nfs_commit_done, flags);
rpc_init_task(task, NFS_CLIENT(inode), nfs4_commit_done, flags);
task->tk_calldata = data;
/* Release requests */
task->tk_release = nfs_commit_release;
......
This diff is collapsed.
......@@ -48,6 +48,18 @@
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);
}
/*
* Bare-bones access to getattr: this is for nfs_read_super.
*/
......@@ -166,6 +178,8 @@ nfs_proc_read(struct inode *inode, struct rpc_cred *cred,
fattr->valid = 0;
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0)
nfs_refresh_inode(inode, fattr);
dprintk("NFS reply read: %d\n", status);
*eofp = res.eof;
return status;
......@@ -205,6 +219,9 @@ nfs_proc_write(struct inode *inode, struct rpc_cred *cred,
flags |= NFS_RPC_SWAPFLAGS;
status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
if (status >= 0)
nfs_write_refresh_inode(inode, fattr);
dprintk("NFS reply write: %d\n", status);
verf->committed = NFS_FILE_SYNC; /* NFSv2 always syncs data */
return status < 0? status : count;
......@@ -520,6 +537,16 @@ nfs_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
extern u32 * nfs_decode_dirent(u32 *, struct nfs_entry *, int);
static void
nfs_read_done(struct rpc_task *task)
{
struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata;
if (task->tk_status >= 0)
nfs_refresh_inode(data->inode, data->res.fattr);
nfs_readpage_result(task);
}
static void
nfs_proc_read_setup(struct nfs_read_data *data, unsigned int count)
{
......@@ -548,7 +575,7 @@ nfs_proc_read_setup(struct nfs_read_data *data, unsigned int count)
flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
/* Finalize the task. */
rpc_init_task(task, NFS_CLIENT(inode), nfs_readpage_result, flags);
rpc_init_task(task, NFS_CLIENT(inode), nfs_read_done, flags);
task->tk_calldata = data;
/* Release requests */
task->tk_release = nfs_readdata_release;
......@@ -556,6 +583,16 @@ nfs_proc_read_setup(struct nfs_read_data *data, unsigned int count)
rpc_call_setup(&data->task, &msg, 0);
}
static void
nfs_write_done(struct rpc_task *task)
{
struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
if (task->tk_status >= 0)
nfs_write_refresh_inode(data->inode, data->res.fattr);
nfs_writeback_done(task);
}
static void
nfs_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how)
{
......@@ -587,7 +624,7 @@ nfs_proc_write_setup(struct nfs_write_data *data, unsigned int count, int how)
flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
/* Finalize the task. */
rpc_init_task(task, NFS_CLIENT(inode), nfs_writeback_done, flags);
rpc_init_task(task, NFS_CLIENT(inode), nfs_write_done, flags);
task->tk_calldata = data;
/* Release requests */
task->tk_release = nfs_writedata_release;
......
......@@ -100,7 +100,6 @@ nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page)
lock_kernel();
result = NFS_PROTO(inode)->read(inode, cred, &fattr, flags,
offset, rsize, page, &eof);
nfs_refresh_inode(inode, &fattr);
unlock_kernel();
/*
......@@ -258,14 +257,12 @@ void
nfs_readpage_result(struct rpc_task *task)
{
struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata;
struct inode *inode = data->inode;
struct nfs_fattr *fattr = &data->fattr;
unsigned int count = data->res.count;
dprintk("NFS: %4d nfs_readpage_result, (status %d)\n",
task->tk_pid, task->tk_status);
nfs_refresh_inode(inode, fattr);
while (!list_empty(&data->pages)) {
struct nfs_page *req = nfs_list_entry(data->pages.next);
struct page *page = req->wb_page;
......
......@@ -124,23 +124,6 @@ void nfs_commit_release(struct rpc_task *task)
nfs_commit_free(wdata);
}
/*
* This function will be used to simulate weak cache consistency
* under NFSv2 when the NFSv3 attribute patch is included.
* For the moment, we just call nfs_refresh_inode().
*/
static __inline__ int
nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr)
{
if ((fattr->valid & NFS_ATTR_FATTR) && !(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;
}
return nfs_refresh_inode(inode, fattr);
}
/*
* Write a page synchronously.
* Offset is the data offset within the page.
......@@ -178,7 +161,6 @@ nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page,
result = NFS_PROTO(inode)->write(inode, cred, &fattr, flags,
offset, wsize, page, &verf);
nfs_write_attributes(inode, &fattr);
if (result < 0) {
/* Must mark the page invalid after I/O error */
......@@ -893,7 +875,6 @@ nfs_writeback_done(struct rpc_task *task)
* writebacks since the page->count is kept > 1 for as long
* as the page has a write request pending.
*/
nfs_write_attributes(inode, &data->fattr);
while (!list_empty(&data->pages)) {
req = nfs_list_entry(data->pages.next);
nfs_list_remove_request(req);
......@@ -1018,12 +999,10 @@ nfs_commit_done(struct rpc_task *task)
{
struct nfs_write_data *data = (struct nfs_write_data *)task->tk_calldata;
struct nfs_page *req;
struct inode *inode = data->inode;
dprintk("NFS: %4d nfs_commit_done (status %d)\n",
task->tk_pid, task->tk_status);
nfs_write_attributes(inode, &data->fattr);
while (!list_empty(&data->pages)) {
req = nfs_list_entry(data->pages.next);
nfs_list_remove_request(req);
......
......@@ -35,8 +35,11 @@ struct nfs_fattr {
struct timespec atime;
struct timespec mtime;
struct timespec ctime;
#ifdef CONFIG_NFS_V4
__u32 bitmap[2]; /* NFSv4 returned attribute bitmap */
__u64 change_attr; /* NFSv4 change attribute */
__u64 pre_change_attr;/* pre-op NFSv4 change attribute */
#endif
unsigned long timestamp;
};
......
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