{
struct nfs_inode *nfsi = NFS_I(inode);
+ spin_lock(&inode->i_lock);
+ nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
if (cinfo->before == nfsi->change_attr && cinfo->atomic)
nfsi->change_attr = cinfo->after;
+ spin_unlock(&inode->i_lock);
}
/* Helper for asynchronous RPC calls */
-static int nfs4_call_async(struct rpc_clnt *clnt, rpc_action tk_begin,
- rpc_action tk_exit, void *calldata)
+static int nfs4_call_async(struct rpc_clnt *clnt,
+ const struct rpc_call_ops *tk_ops, void *calldata)
{
struct rpc_task *task;
- if (!(task = rpc_new_task(clnt, tk_exit, RPC_TASK_ASYNC)))
+ if (!(task = rpc_new_task(clnt, RPC_TASK_ASYNC, tk_ops, calldata)))
return -ENOMEM;
-
- task->tk_calldata = calldata;
- task->tk_action = tk_begin;
rpc_execute(task);
return 0;
}
struct inode *inode = state->inode;
open_flags &= (FMODE_READ|FMODE_WRITE);
- /* Protect against nfs4_find_state() */
+ /* Protect against nfs4_find_state_byowner() */
spin_lock(&state->owner->so_lock);
spin_lock(&inode->i_lock);
- state->state |= open_flags;
- /* NB! List reordering - see the reclaim code for why. */
- if ((open_flags & FMODE_WRITE) && 0 == state->nwriters++)
- list_move(&state->open_states, &state->owner->so_states);
+ memcpy(&state->stateid, stateid, sizeof(state->stateid));
+ if ((open_flags & FMODE_WRITE))
+ state->nwriters++;
if (open_flags & FMODE_READ)
state->nreaders++;
- memcpy(&state->stateid, stateid, sizeof(state->stateid));
+ nfs4_state_set_mode_locked(state, state->state | open_flags);
spin_unlock(&inode->i_lock);
spin_unlock(&state->owner->so_lock);
}
nfs_increment_open_seqid(status, o_arg->seqid);
if (status != 0)
goto out;
- update_changeattr(dir, &o_res->cinfo);
+ if (o_arg->open_flags & O_CREAT) {
+ update_changeattr(dir, &o_res->cinfo);
+ nfs_post_op_update_inode(dir, o_res->dir_attr);
+ } else
+ nfs_refresh_inode(dir, o_res->dir_attr);
if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
status = _nfs4_proc_open_confirm(server->client, &o_res->fh,
sp, &o_res->stateid, o_arg->seqid);
struct inode *inode = state->inode;
struct nfs_server *server = NFS_SERVER(dir);
struct nfs_delegation *delegation = NFS_I(inode)->delegation;
- struct nfs_fattr f_attr = {
- .valid = 0,
- };
+ struct nfs_fattr f_attr, dir_attr;
struct nfs_openargs o_arg = {
.fh = NFS_FH(dir),
.open_flags = state->state,
};
struct nfs_openres o_res = {
.f_attr = &f_attr,
+ .dir_attr = &dir_attr,
.server = server,
};
int status = 0;
status = -ENOMEM;
if (o_arg.seqid == NULL)
goto out;
+ nfs_fattr_init(&f_attr);
+ nfs_fattr_init(&dir_attr);
status = _nfs4_proc_open(dir, sp, &o_arg, &o_res);
if (status != 0)
goto out_nodeleg;
struct nfs4_client *clp = server->nfs4_state;
struct inode *inode = NULL;
int status;
- struct nfs_fattr f_attr = {
- .valid = 0,
- };
+ struct nfs_fattr f_attr, dir_attr;
struct nfs_openargs o_arg = {
.fh = NFS_FH(dir),
.open_flags = flags,
};
struct nfs_openres o_res = {
.f_attr = &f_attr,
+ .dir_attr = &dir_attr,
.server = server,
};
o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid);
if (o_arg.seqid == NULL)
return -ENOMEM;
+ nfs_fattr_init(&f_attr);
+ nfs_fattr_init(&dir_attr);
status = _nfs4_proc_open(dir, sp, &o_arg, &o_res);
if (status != 0)
goto out_err;
};
int status;
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
if (state != NULL) {
msg.rpc_cred = state->owner->so_cred;
struct nfs4_state *state;
struct nfs_closeargs arg;
struct nfs_closeres res;
+ struct nfs_fattr fattr;
};
-static void nfs4_free_closedata(struct nfs4_closedata *calldata)
+static void nfs4_free_closedata(void *data)
{
- struct nfs4_state *state = calldata->state;
- struct nfs4_state_owner *sp = state->owner;
+ struct nfs4_closedata *calldata = data;
+ struct nfs4_state_owner *sp = calldata->state->owner;
nfs4_put_open_state(calldata->state);
nfs_free_seqid(calldata->arg.seqid);
kfree(calldata);
}
-static void nfs4_close_done(struct rpc_task *task)
+static void nfs4_close_done(struct rpc_task *task, void *data)
{
- struct nfs4_closedata *calldata = (struct nfs4_closedata *)task->tk_calldata;
+ struct nfs4_closedata *calldata = data;
struct nfs4_state *state = calldata->state;
struct nfs_server *server = NFS_SERVER(calldata->inode);
+ if (RPC_ASSASSINATED(task))
+ return;
/* hmm. we are done with the inode, and in the process of freeing
* the state_owner. we keep this around to process errors
*/
break;
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
- state->state = calldata->arg.open_flags;
nfs4_schedule_state_recovery(server->nfs4_state);
break;
default:
return;
}
}
- state->state = calldata->arg.open_flags;
- nfs4_free_closedata(calldata);
+ nfs_refresh_inode(calldata->inode, calldata->res.fattr);
}
-static void nfs4_close_begin(struct rpc_task *task)
+static void nfs4_close_prepare(struct rpc_task *task, void *data)
{
- struct nfs4_closedata *calldata = (struct nfs4_closedata *)task->tk_calldata;
+ struct nfs4_closedata *calldata = data;
struct nfs4_state *state = calldata->state;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE],
.rpc_resp = &calldata->res,
.rpc_cred = state->owner->so_cred,
};
- int mode = 0;
- int status;
+ int mode = 0, old_mode;
- status = nfs_wait_on_sequence(calldata->arg.seqid, task);
- if (status != 0)
+ if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0)
return;
- /* Don't reorder reads */
- smp_rmb();
/* Recalculate the new open mode in case someone reopened the file
* while we were waiting in line to be scheduled.
*/
- if (state->nreaders != 0)
- mode |= FMODE_READ;
- if (state->nwriters != 0)
- mode |= FMODE_WRITE;
- if (test_bit(NFS_DELEGATED_STATE, &state->flags))
- state->state = mode;
- if (mode == state->state) {
- nfs4_free_closedata(calldata);
- task->tk_exit = NULL;
- rpc_exit(task, 0);
+ spin_lock(&state->owner->so_lock);
+ spin_lock(&calldata->inode->i_lock);
+ mode = old_mode = state->state;
+ if (state->nreaders == 0)
+ mode &= ~FMODE_READ;
+ if (state->nwriters == 0)
+ mode &= ~FMODE_WRITE;
+ nfs4_state_set_mode_locked(state, mode);
+ spin_unlock(&calldata->inode->i_lock);
+ spin_unlock(&state->owner->so_lock);
+ if (mode == old_mode || test_bit(NFS_DELEGATED_STATE, &state->flags)) {
+ /* Note: exit _without_ calling nfs4_close_done */
+ task->tk_action = NULL;
return;
}
+ nfs_fattr_init(calldata->res.fattr);
if (mode != 0)
msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
calldata->arg.open_flags = mode;
rpc_call_setup(task, &msg, 0);
}
+static const struct rpc_call_ops nfs4_close_ops = {
+ .rpc_call_prepare = nfs4_close_prepare,
+ .rpc_call_done = nfs4_close_done,
+ .rpc_release = nfs4_free_closedata,
+};
+
/*
* It is possible for data to be read/written from a mem-mapped file
* after the sys_close call (which hits the vfs layer as a flush).
*
* NOTE: Caller must be holding the sp->so_owner semaphore!
*/
-int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode)
+int nfs4_do_close(struct inode *inode, struct nfs4_state *state)
{
+ struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_closedata *calldata;
int status = -ENOMEM;
calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid);
if (calldata->arg.seqid == NULL)
goto out_free_calldata;
+ calldata->arg.bitmask = server->attr_bitmask;
+ calldata->res.fattr = &calldata->fattr;
+ calldata->res.server = server;
- status = nfs4_call_async(NFS_SERVER(inode)->client, nfs4_close_begin,
- nfs4_close_done, calldata);
+ status = nfs4_call_async(server->client, &nfs4_close_ops, calldata);
if (status == 0)
goto out;
static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *info)
{
- struct nfs_fattr * fattr = info->fattr;
struct nfs4_lookup_root_arg args = {
.bitmask = nfs4_fattr_bitmap,
};
struct nfs4_lookup_res res = {
.server = server,
- .fattr = fattr,
+ .fattr = info->fattr,
.fh = fhandle,
};
struct rpc_message msg = {
.rpc_argp = &args,
.rpc_resp = &res,
};
- fattr->valid = 0;
+ nfs_fattr_init(info->fattr);
return rpc_call_sync(server->client, &msg, 0);
}
q.len = p - q.name;
do {
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = nfs4_handle_exception(server,
rpc_call_sync(server->client, &msg, 0),
&exception);
.rpc_resp = &res,
};
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
return rpc_call_sync(server->client, &msg, 0);
}
{
struct rpc_cred *cred;
struct inode *inode = dentry->d_inode;
- struct nfs4_state *state;
+ struct nfs_open_context *ctx;
+ struct nfs4_state *state = NULL;
int status;
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
if (IS_ERR(cred))
return PTR_ERR(cred);
- /* Search for an existing WRITE delegation first */
- state = nfs4_open_delegated(inode, FMODE_WRITE, cred);
- if (!IS_ERR(state)) {
- /* NB: nfs4_open_delegated() bumps the inode->i_count */
- iput(inode);
- } else {
- /* Search for an existing open(O_WRITE) stateid */
- state = nfs4_find_state(inode, cred, FMODE_WRITE);
- }
+
+ /* Search for an existing open(O_WRITE) file */
+ ctx = nfs_find_open_context(inode, cred, FMODE_WRITE);
+ if (ctx != NULL)
+ state = ctx->state;
status = nfs4_do_setattr(NFS_SERVER(inode), fattr,
NFS_FH(inode), sattr, state);
if (status == 0)
nfs_setattr_update_inode(inode, sattr);
- if (state != NULL)
- nfs4_close_state(state, FMODE_WRITE);
+ if (ctx != NULL)
+ put_nfs_open_context(ctx);
put_rpccred(cred);
return status;
}
.rpc_resp = &res,
};
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
dprintk("NFS call lookup %s\n", name->name);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
dprintk("NFS call read %d @ %Ld\n", rdata->args.count,
(long long) rdata->args.offset);
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
status = rpc_call_sync(server->client, &msg, flags);
if (!status)
renew_lease(server, timestamp);
dprintk("NFS call write %d @ %Ld\n", wdata->args.count,
(long long) wdata->args.offset);
- fattr->valid = 0;
+ wdata->args.bitmask = server->attr_bitmask;
+ wdata->res.server = server;
+ nfs_fattr_init(fattr);
status = rpc_call_sync(server->client, &msg, rpcflags);
dprintk("NFS reply write: %d\n", status);
- return status;
+ if (status < 0)
+ return status;
+ nfs_post_op_update_inode(inode, fattr);
+ return wdata->res.count;
}
static int nfs4_proc_write(struct nfs_write_data *wdata)
dprintk("NFS call commit %d @ %Ld\n", cdata->args.count,
(long long) cdata->args.offset);
- fattr->valid = 0;
+ cdata->args.bitmask = server->attr_bitmask;
+ cdata->res.server = server;
+ nfs_fattr_init(fattr);
status = rpc_call_sync(server->client, &msg, 0);
dprintk("NFS reply commit: %d\n", status);
+ if (status >= 0)
+ nfs_post_op_update_inode(inode, fattr);
return status;
}
static int _nfs4_proc_remove(struct inode *dir, struct qstr *name)
{
+ struct nfs_server *server = NFS_SERVER(dir);
struct nfs4_remove_arg args = {
.fh = NFS_FH(dir),
.name = name,
+ .bitmask = server->attr_bitmask,
+ };
+ struct nfs_fattr dir_attr;
+ struct nfs4_remove_res res = {
+ .server = server,
+ .dir_attr = &dir_attr,
};
- struct nfs4_change_info res;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE],
.rpc_argp = &args,
};
int status;
- status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
- if (status == 0)
- update_changeattr(dir, &res);
+ nfs_fattr_init(res.dir_attr);
+ status = rpc_call_sync(server->client, &msg, 0);
+ if (status == 0) {
+ update_changeattr(dir, &res.cinfo);
+ nfs_post_op_update_inode(dir, res.dir_attr);
+ }
return status;
}
struct unlink_desc {
struct nfs4_remove_arg args;
- struct nfs4_change_info res;
+ struct nfs4_remove_res res;
+ struct nfs_fattr dir_attr;
};
static int nfs4_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir,
struct qstr *name)
{
+ struct nfs_server *server = NFS_SERVER(dir->d_inode);
struct unlink_desc *up;
up = (struct unlink_desc *) kmalloc(sizeof(*up), GFP_KERNEL);
up->args.fh = NFS_FH(dir->d_inode);
up->args.name = name;
+ up->args.bitmask = server->attr_bitmask;
+ up->res.server = server;
+ up->res.dir_attr = &up->dir_attr;
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
msg->rpc_argp = &up->args;
if (msg->rpc_resp != NULL) {
up = container_of(msg->rpc_resp, struct unlink_desc, res);
- update_changeattr(dir->d_inode, &up->res);
+ update_changeattr(dir->d_inode, &up->res.cinfo);
+ nfs_post_op_update_inode(dir->d_inode, up->res.dir_attr);
kfree(up);
msg->rpc_resp = NULL;
msg->rpc_argp = NULL;
static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
struct inode *new_dir, struct qstr *new_name)
{
+ struct nfs_server *server = NFS_SERVER(old_dir);
struct nfs4_rename_arg arg = {
.old_dir = NFS_FH(old_dir),
.new_dir = NFS_FH(new_dir),
.old_name = old_name,
.new_name = new_name,
+ .bitmask = server->attr_bitmask,
+ };
+ struct nfs_fattr old_fattr, new_fattr;
+ struct nfs4_rename_res res = {
+ .server = server,
+ .old_fattr = &old_fattr,
+ .new_fattr = &new_fattr,
};
- struct nfs4_rename_res res = { };
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME],
.rpc_argp = &arg,
};
int status;
- status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0);
+ nfs_fattr_init(res.old_fattr);
+ nfs_fattr_init(res.new_fattr);
+ status = rpc_call_sync(server->client, &msg, 0);
if (!status) {
update_changeattr(old_dir, &res.old_cinfo);
+ nfs_post_op_update_inode(old_dir, res.old_fattr);
update_changeattr(new_dir, &res.new_cinfo);
+ nfs_post_op_update_inode(new_dir, res.new_fattr);
}
return status;
}
static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
{
+ struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_link_arg arg = {
.fh = NFS_FH(inode),
.dir_fh = NFS_FH(dir),
.name = name,
+ .bitmask = server->attr_bitmask,
+ };
+ struct nfs_fattr fattr, dir_attr;
+ struct nfs4_link_res res = {
+ .server = server,
+ .fattr = &fattr,
+ .dir_attr = &dir_attr,
};
- struct nfs4_change_info cinfo = { };
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LINK],
.rpc_argp = &arg,
- .rpc_resp = &cinfo,
+ .rpc_resp = &res,
};
int status;
- status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
- if (!status)
- update_changeattr(dir, &cinfo);
+ nfs_fattr_init(res.fattr);
+ nfs_fattr_init(res.dir_attr);
+ status = rpc_call_sync(server->client, &msg, 0);
+ if (!status) {
+ update_changeattr(dir, &res.cinfo);
+ nfs_post_op_update_inode(dir, res.dir_attr);
+ nfs_refresh_inode(inode, res.fattr);
+ }
return status;
}
struct nfs_fattr *fattr)
{
struct nfs_server *server = NFS_SERVER(dir);
+ struct nfs_fattr dir_fattr;
struct nfs4_create_arg arg = {
.dir_fh = NFS_FH(dir),
.server = server,
.server = server,
.fh = fhandle,
.fattr = fattr,
+ .dir_fattr = &dir_fattr,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SYMLINK],
if (path->len > NFS4_MAXPATHLEN)
return -ENAMETOOLONG;
arg.u.symlink = path;
- fattr->valid = 0;
+ nfs_fattr_init(fattr);
+ nfs_fattr_init(&dir_fattr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (!status)
update_changeattr(dir, &res.dir_cinfo);
+ nfs_post_op_update_inode(dir, res.dir_fattr);
return status;
}
{
struct nfs_server *server = NFS_SERVER(dir);
struct nfs_fh fhandle;
- struct nfs_fattr fattr;
+ struct nfs_fattr fattr, dir_fattr;
struct nfs4_create_arg arg = {
.dir_fh = NFS_FH(dir),
.server = server,
.server = server,
.fh = &fhandle,
.fattr = &fattr,
+ .dir_fattr = &dir_fattr,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE],
};
int status;
- fattr.valid = 0;
+ nfs_fattr_init(&fattr);
+ nfs_fattr_init(&dir_fattr);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (!status) {
update_changeattr(dir, &res.dir_cinfo);
+ nfs_post_op_update_inode(dir, res.dir_fattr);
status = nfs_instantiate(dentry, &fhandle, &fattr);
}
return status;
{
struct nfs_server *server = NFS_SERVER(dir);
struct nfs_fh fh;
- struct nfs_fattr fattr;
+ struct nfs_fattr fattr, dir_fattr;
struct nfs4_create_arg arg = {
.dir_fh = NFS_FH(dir),
.server = server,
.server = server,
.fh = &fh,
.fattr = &fattr,
+ .dir_fattr = &dir_fattr,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE],
int status;
int mode = sattr->ia_mode;
- fattr.valid = 0;
+ nfs_fattr_init(&fattr);
+ nfs_fattr_init(&dir_fattr);
BUG_ON(!(sattr->ia_valid & ATTR_MODE));
BUG_ON(!S_ISFIFO(mode) && !S_ISBLK(mode) && !S_ISCHR(mode) && !S_ISSOCK(mode));
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
if (status == 0) {
update_changeattr(dir, &res.dir_cinfo);
+ nfs_post_op_update_inode(dir, res.dir_fattr);
status = nfs_instantiate(dentry, &fh, &fattr);
}
return status;
.rpc_resp = fsstat,
};
- fsstat->fattr->valid = 0;
+ nfs_fattr_init(fsstat->fattr);
return rpc_call_sync(server->client, &msg, 0);
}
static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
{
- fsinfo->fattr->valid = 0;
+ nfs_fattr_init(fsinfo->fattr);
return nfs4_do_fsinfo(server, fhandle, fsinfo);
}
return 0;
}
- pathconf->fattr->valid = 0;
+ nfs_fattr_init(pathconf->fattr);
return rpc_call_sync(server->client, &msg, 0);
}
return err;
}
-static void
-nfs4_read_done(struct rpc_task *task)
+static void nfs4_read_done(struct rpc_task *task, void *calldata)
{
- struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata;
+ struct nfs_read_data *data = calldata;
struct inode *inode = data->inode;
if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) {
if (task->tk_status > 0)
renew_lease(NFS_SERVER(inode), data->timestamp);
/* Call back common NFS readpage processing */
- nfs_readpage_result(task);
+ nfs_readpage_result(task, calldata);
}
+static const struct rpc_call_ops nfs4_read_ops = {
+ .rpc_call_done = nfs4_read_done,
+ .rpc_release = nfs_readdata_release,
+};
+
static void
nfs4_proc_read_setup(struct nfs_read_data *data)
{
flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
/* Finalize the task. */
- rpc_init_task(task, NFS_CLIENT(inode), nfs4_read_done, flags);
+ rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs4_read_ops, data);
rpc_call_setup(task, &msg, 0);
}
-static void
-nfs4_write_done(struct rpc_task *task)
+static void nfs4_write_done(struct rpc_task *task, void *calldata)
{
- struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
+ struct nfs_write_data *data = calldata;
struct inode *inode = data->inode;
if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) {
rpc_restart_call(task);
return;
}
- if (task->tk_status >= 0)
+ if (task->tk_status >= 0) {
renew_lease(NFS_SERVER(inode), data->timestamp);
+ nfs_post_op_update_inode(inode, data->res.fattr);
+ }
/* Call back common NFS writeback processing */
- nfs_writeback_done(task);
+ nfs_writeback_done(task, calldata);
}
+static const struct rpc_call_ops nfs4_write_ops = {
+ .rpc_call_done = nfs4_write_done,
+ .rpc_release = nfs_writedata_release,
+};
+
static void
nfs4_proc_write_setup(struct nfs_write_data *data, int how)
{
.rpc_cred = data->cred,
};
struct inode *inode = data->inode;
+ struct nfs_server *server = NFS_SERVER(inode);
int stable;
int flags;
} else
stable = NFS_UNSTABLE;
data->args.stable = stable;
+ data->args.bitmask = server->attr_bitmask;
+ data->res.server = server;
data->timestamp = jiffies;
flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
/* Finalize the task. */
- rpc_init_task(task, NFS_CLIENT(inode), nfs4_write_done, flags);
+ rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs4_write_ops, data);
rpc_call_setup(task, &msg, 0);
}
-static void
-nfs4_commit_done(struct rpc_task *task)
+static void nfs4_commit_done(struct rpc_task *task, void *calldata)
{
- struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
+ struct nfs_write_data *data = calldata;
struct inode *inode = data->inode;
if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) {
rpc_restart_call(task);
return;
}
+ if (task->tk_status >= 0)
+ nfs_post_op_update_inode(inode, data->res.fattr);
/* Call back common NFS writeback processing */
- nfs_commit_done(task);
+ nfs_commit_done(task, calldata);
}
+static const struct rpc_call_ops nfs4_commit_ops = {
+ .rpc_call_done = nfs4_commit_done,
+ .rpc_release = nfs_commit_release,
+};
+
static void
nfs4_proc_commit_setup(struct nfs_write_data *data, int how)
{
.rpc_cred = data->cred,
};
struct inode *inode = data->inode;
+ struct nfs_server *server = NFS_SERVER(inode);
int flags;
+ data->args.bitmask = server->attr_bitmask;
+ data->res.server = server;
+
/* Set the initial flags for the task. */
flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC;
/* Finalize the task. */
- rpc_init_task(task, NFS_CLIENT(inode), nfs4_commit_done, flags);
+ rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs4_commit_ops, data);
rpc_call_setup(task, &msg, 0);
}
* nfs4_proc_async_renew(): This is not one of the nfs_rpc_ops; it is a special
* standalone procedure for queueing an asynchronous RENEW.
*/
-static void
-renew_done(struct rpc_task *task)
+static void nfs4_renew_done(struct rpc_task *task, void *data)
{
struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp;
- unsigned long timestamp = (unsigned long)task->tk_calldata;
+ unsigned long timestamp = (unsigned long)data;
if (task->tk_status < 0) {
switch (task->tk_status) {
spin_unlock(&clp->cl_lock);
}
+static const struct rpc_call_ops nfs4_renew_ops = {
+ .rpc_call_done = nfs4_renew_done,
+};
+
int
nfs4_proc_async_renew(struct nfs4_client *clp)
{
};
return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT,
- renew_done, (void *)jiffies);
+ &nfs4_renew_ops, (void *)jiffies);
}
int
case -NFS4ERR_GRACE:
case -NFS4ERR_DELAY:
ret = nfs4_delay(server->client, &exception->timeout);
- if (ret == 0)
- exception->retry = 1;
- break;
+ if (ret != 0)
+ break;
case -NFS4ERR_OLD_STATEID:
- if (ret == 0)
- exception->retry = 1;
+ exception->retry = 1;
}
/* We failed to handle the error */
return nfs4_map_errors(ret);
}
}
-static void nfs4_locku_complete(struct nfs4_unlockdata *calldata)
+static void nfs4_locku_complete(void *data)
{
+ struct nfs4_unlockdata *calldata = data;
complete(&calldata->completion);
nfs4_locku_release_calldata(calldata);
}
-static void nfs4_locku_done(struct rpc_task *task)
+static void nfs4_locku_done(struct rpc_task *task, void *data)
{
- struct nfs4_unlockdata *calldata = (struct nfs4_unlockdata *)task->tk_calldata;
+ struct nfs4_unlockdata *calldata = data;
nfs_increment_lock_seqid(task->tk_status, calldata->luargs.seqid);
switch (task->tk_status) {
default:
if (nfs4_async_handle_error(task, calldata->res.server) == -EAGAIN) {
rpc_restart_call(task);
- return;
}
}
- nfs4_locku_complete(calldata);
}
-static void nfs4_locku_begin(struct rpc_task *task)
+static void nfs4_locku_prepare(struct rpc_task *task, void *data)
{
- struct nfs4_unlockdata *calldata = (struct nfs4_unlockdata *)task->tk_calldata;
+ struct nfs4_unlockdata *calldata = data;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKU],
.rpc_argp = &calldata->arg,
if (status != 0)
return;
if ((calldata->lsp->ls_flags & NFS_LOCK_INITIALIZED) == 0) {
- nfs4_locku_complete(calldata);
- task->tk_exit = NULL;
- rpc_exit(task, 0);
+ /* Note: exit _without_ running nfs4_locku_done */
+ task->tk_action = NULL;
return;
}
rpc_call_setup(task, &msg, 0);
}
+static const struct rpc_call_ops nfs4_locku_ops = {
+ .rpc_call_prepare = nfs4_locku_prepare,
+ .rpc_call_done = nfs4_locku_done,
+ .rpc_release = nfs4_locku_complete,
+};
+
static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
{
struct nfs4_unlockdata *calldata;
struct nfs4_lock_state *lsp;
int status;
+ /* Is this a delegated lock? */
+ if (test_bit(NFS_DELEGATED_STATE, &state->flags))
+ return do_vfs_lock(request->fl_file, request);
+
status = nfs4_set_lock_state(state, request);
if (status != 0)
return status;
atomic_set(&calldata->refcount, 2);
init_completion(&calldata->completion);
- status = nfs4_call_async(NFS_SERVER(inode)->client, nfs4_locku_begin,
- nfs4_locku_done, calldata);
+ status = nfs4_call_async(NFS_SERVER(inode)->client, &nfs4_locku_ops, calldata);
if (status == 0)
wait_for_completion_interruptible(&calldata->completion);
do_vfs_lock(request->fl_file, request);
struct nfs4_exception exception = { };
int err;
+ /* Cache the lock if possible... */
+ if (test_bit(NFS_DELEGATED_STATE, &state->flags))
+ return 0;
do {
err = _nfs4_do_setlk(state, F_SETLK, request, 1);
if (err != -NFS4ERR_DELAY)
struct nfs4_exception exception = { };
int err;
+ err = nfs4_set_lock_state(state, request);
+ if (err != 0)
+ return err;
do {
err = _nfs4_do_setlk(state, F_SETLK, request, 0);
if (err != -NFS4ERR_DELAY)
struct nfs4_client *clp = state->owner->so_client;
int status;
+ /* Is this a delegated open? */
+ if (NFS_I(state->inode)->delegation_state != 0) {
+ /* Yes: cache locks! */
+ status = do_vfs_lock(request->fl_file, request);
+ /* ...but avoid races with delegation recall... */
+ if (status < 0 || test_bit(NFS_DELEGATED_STATE, &state->flags))
+ return status;
+ }
down_read(&clp->cl_sem);
status = nfs4_set_lock_state(state, request);
- if (status == 0)
- status = _nfs4_do_setlk(state, cmd, request, 0);
- if (status == 0) {
- /* Note: we always want to sleep here! */
- request->fl_flags |= FL_SLEEP;
- if (do_vfs_lock(request->fl_file, request) < 0)
- printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
- }
+ if (status != 0)
+ goto out;
+ status = _nfs4_do_setlk(state, cmd, request, 0);
+ if (status != 0)
+ goto out;
+ /* Note: we always want to sleep here! */
+ request->fl_flags |= FL_SLEEP;
+ if (do_vfs_lock(request->fl_file, request) < 0)
+ printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
+out:
up_read(&clp->cl_sem);
return status;
}
return status;
}
+int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
+{
+ struct nfs_server *server = NFS_SERVER(state->inode);
+ struct nfs4_exception exception = { };
+ int err;
+
+ err = nfs4_set_lock_state(state, fl);
+ if (err != 0)
+ goto out;
+ do {
+ err = _nfs4_do_setlk(state, F_SETLK, fl, 0);
+ if (err != -NFS4ERR_DELAY)
+ break;
+ err = nfs4_handle_exception(server, err, &exception);
+ } while (exception.retry);
+out:
+ return err;
+}
#define XATTR_NAME_NFSV4_ACL "system.nfs4_acl"