static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *);
static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry);
static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception);
+static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp);
extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus);
extern struct rpc_procinfo nfs4_procedures[];
kunmap_atomic(start, KM_USER0);
}
-static void
-renew_lease(struct nfs_server *server, unsigned long timestamp)
+static void renew_lease(const struct nfs_server *server, unsigned long timestamp)
{
struct nfs4_client *clp = server->nfs4_state;
spin_lock(&clp->cl_lock);
struct dentry *dir;
struct nfs4_state_owner *owner;
struct iattr attrs;
+ unsigned long timestamp;
int rpc_status;
int cancelled;
};
.rpc_resp = &data->c_res,
.rpc_cred = data->owner->so_cred,
};
+ data->timestamp = jiffies;
rpc_call_setup(task, &msg, 0);
}
data->rpc_status = task->tk_status;
if (RPC_ASSASSINATED(task))
return;
- if (data->rpc_status == 0)
+ if (data->rpc_status == 0) {
memcpy(data->o_res.stateid.data, data->c_res.stateid.data,
sizeof(data->o_res.stateid.data));
+ renew_lease(data->o_res.server, data->timestamp);
+ }
nfs_increment_open_seqid(data->rpc_status, data->c_arg.seqid);
nfs_confirm_seqid(&data->owner->so_seqid, data->rpc_status);
}
data->o_arg.clientid = sp->so_client->cl_clientid;
if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS)
msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR];
+ data->timestamp = jiffies;
rpc_call_setup(task, &msg, 0);
}
default:
data->rpc_status = -ENOTDIR;
}
+ renew_lease(data->o_res.server, data->timestamp);
}
nfs_increment_open_seqid(data->rpc_status, data->o_arg.seqid);
}
return -EACCES;
}
+int nfs4_recover_expired_lease(struct nfs_server *server)
+{
+ struct nfs4_client *clp = server->nfs4_state;
+
+ if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
+ nfs4_schedule_state_recovery(clp);
+ return nfs4_wait_clnt_recover(server->client, clp);
+}
+
/*
* OPEN_EXPIRED:
* reclaim state on the server after a network partition.
int open_flags = flags & (FMODE_READ|FMODE_WRITE);
int err;
+ err = -ENOMEM;
+ if (!(sp = nfs4_get_state_owner(server, cred))) {
+ dprintk("%s: nfs4_get_state_owner failed!\n", __FUNCTION__);
+ return err;
+ }
+ err = nfs4_recover_expired_lease(server);
+ if (err != 0)
+ goto out_put_state_owner;
/* Protect against reboot recovery - NOTE ORDER! */
down_read(&clp->cl_sem);
/* Protect against delegation recall */
if (delegation == NULL || (delegation->type & open_flags) != open_flags)
goto out_err;
err = -ENOMEM;
- if (!(sp = nfs4_get_state_owner(server, cred))) {
- dprintk("%s: nfs4_get_state_owner failed!\n", __FUNCTION__);
- goto out_err;
- }
state = nfs4_get_open_state(inode, sp);
if (state == NULL)
goto out_err;
spin_unlock(&inode->i_lock);
goto out_ok;
} else if (state->state != 0)
- goto out_err;
+ goto out_put_open_state;
lock_kernel();
err = _nfs4_do_access(inode, cred, open_flags);
unlock_kernel();
if (err != 0)
- goto out_err;
+ goto out_put_open_state;
set_bit(NFS_DELEGATED_STATE, &state->flags);
update_open_stateid(state, &delegation->stateid, open_flags);
out_ok:
up_read(&nfsi->rwsem);
up_read(&clp->cl_sem);
*res = state;
- return 0;
+ return 0;
+out_put_open_state:
+ nfs4_put_open_state(state);
out_err:
- if (sp != NULL) {
- if (state != NULL)
- nfs4_put_open_state(state);
- nfs4_put_state_owner(sp);
- }
up_read(&nfsi->rwsem);
up_read(&clp->cl_sem);
if (err != -EACCES)
nfs_inode_return_delegation(inode);
+out_put_state_owner:
+ nfs4_put_state_owner(sp);
return err;
}
int status;
/* Protect against reboot recovery conflicts */
- down_read(&clp->cl_sem);
status = -ENOMEM;
if (!(sp = nfs4_get_state_owner(server, cred))) {
dprintk("nfs4_do_open: nfs4_get_state_owner failed!\n");
goto out_err;
}
+ status = nfs4_recover_expired_lease(server);
+ if (status != 0)
+ goto err_put_state_owner;
+ down_read(&clp->cl_sem);
+ status = -ENOMEM;
opendata = nfs4_opendata_alloc(dentry, sp, flags, sattr);
if (opendata == NULL)
goto err_put_state_owner;
.rpc_argp = &arg,
.rpc_resp = &res,
};
+ unsigned long timestamp = jiffies;
int status;
nfs_fattr_init(fattr);
memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));
status = rpc_call_sync(server->client, &msg, 0);
+ if (status == 0 && state != NULL)
+ renew_lease(server, timestamp);
return status;
}
struct nfs_closeargs arg;
struct nfs_closeres res;
struct nfs_fattr fattr;
+ unsigned long timestamp;
};
static void nfs4_free_closedata(void *data)
case 0:
memcpy(&state->stateid, &calldata->res.stateid,
sizeof(state->stateid));
+ renew_lease(server, calldata->timestamp);
break;
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
if (mode != 0)
msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
calldata->arg.open_flags = mode;
+ calldata->timestamp = jiffies;
rpc_call_setup(task, &msg, 0);
}
if (status == 0)
status = nfs4_do_fsinfo(server, fhandle, info);
out:
- return status;
+ return nfs4_map_errors(status);
}
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
wdata->args.bitmask = server->attr_bitmask;
wdata->res.server = server;
+ wdata->timestamp = jiffies;
nfs_fattr_init(fattr);
status = rpc_call_sync(server->client, &msg, rpcflags);
dprintk("NFS reply write: %d\n", status);
if (status < 0)
return status;
+ renew_lease(server, wdata->timestamp);
nfs_post_op_update_inode(inode, fattr);
return wdata->res.count;
}
cdata->args.bitmask = server->attr_bitmask;
cdata->res.server = server;
+ cdata->timestamp = jiffies;
nfs_fattr_init(fattr);
status = rpc_call_sync(server->client, &msg, 0);
+ if (status >= 0)
+ renew_lease(server, cdata->timestamp);
dprintk("NFS reply commit: %d\n", status);
if (status >= 0)
nfs_post_op_update_inode(inode, fattr);
.rpc_call_done = nfs4_renew_done,
};
-int
-nfs4_proc_async_renew(struct nfs4_client *clp)
+int nfs4_proc_async_renew(struct nfs4_client *clp, struct rpc_cred *cred)
{
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW],
.rpc_argp = clp,
- .rpc_cred = clp->cl_cred,
+ .rpc_cred = cred,
};
return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT,
&nfs4_renew_ops, (void *)jiffies);
}
-int
-nfs4_proc_renew(struct nfs4_client *clp)
+int nfs4_proc_renew(struct nfs4_client *clp, struct rpc_cred *cred)
{
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW],
.rpc_argp = clp,
- .rpc_cred = clp->cl_cred,
+ .rpc_cred = cred,
};
unsigned long now = jiffies;
int status;
case -NFS4ERR_EXPIRED:
rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL, NULL);
nfs4_schedule_state_recovery(clp);
- if (test_bit(NFS4CLNT_OK, &clp->cl_state))
+ if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) == 0)
rpc_wake_up_task(task);
task->tk_status = 0;
return -EAGAIN;
return 0;
}
+static int nfs4_wait_bit_interruptible(void *word)
+{
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ schedule();
+ return 0;
+}
+
static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp)
{
- DEFINE_WAIT(wait);
sigset_t oldset;
- int interruptible, res = 0;
+ int res;
might_sleep();
rpc_clnt_sigmask(clnt, &oldset);
- interruptible = TASK_UNINTERRUPTIBLE;
- if (clnt->cl_intr)
- interruptible = TASK_INTERRUPTIBLE;
- prepare_to_wait(&clp->cl_waitq, &wait, interruptible);
- nfs4_schedule_state_recovery(clp);
- if (clnt->cl_intr && signalled())
- res = -ERESTARTSYS;
- else if (!test_bit(NFS4CLNT_OK, &clp->cl_state))
- schedule();
- finish_wait(&clp->cl_waitq, &wait);
+ res = wait_on_bit(&clp->cl_state, NFS4CLNT_STATE_RECOVER,
+ nfs4_wait_bit_interruptible,
+ TASK_INTERRUPTIBLE);
rpc_clnt_sigunmask(clnt, &oldset);
return res;
}
case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
+ nfs4_schedule_state_recovery(clp);
ret = nfs4_wait_clnt_recover(server->client, clp);
if (ret == 0)
exception->retry = 1;
return nfs4_map_errors(ret);
}
-int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short port)
+int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short port, struct rpc_cred *cred)
{
nfs4_verifier sc_verifier;
struct nfs4_setclientid setclientid = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID],
.rpc_argp = &setclientid,
.rpc_resp = clp,
- .rpc_cred = clp->cl_cred,
+ .rpc_cred = cred,
};
u32 *p;
int loop = 0;
setclientid.sc_name_len = scnprintf(setclientid.sc_name,
sizeof(setclientid.sc_name), "%s/%u.%u.%u.%u %s %u",
clp->cl_ipaddr, NIPQUAD(clp->cl_addr.s_addr),
- clp->cl_cred->cr_ops->cr_name,
+ cred->cr_ops->cr_name,
clp->cl_id_uniquifier);
setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
sizeof(setclientid.sc_netid), "tcp");
}
int
-nfs4_proc_setclientid_confirm(struct nfs4_client *clp)
+nfs4_proc_setclientid_confirm(struct nfs4_client *clp, struct rpc_cred *cred)
{
struct nfs_fsinfo fsinfo;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID_CONFIRM],
.rpc_argp = clp,
.rpc_resp = &fsinfo,
- .rpc_cred = clp->cl_cred,
+ .rpc_cred = cred,
};
unsigned long now;
int status;
spin_lock(&clp->cl_lock);
clp->cl_lease_time = fsinfo.lease_time * HZ;
clp->cl_last_renewal = now;
+ clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
spin_unlock(&clp->cl_lock);
}
return status;
}
-static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid)
+struct nfs4_delegreturndata {
+ struct nfs4_delegreturnargs args;
+ struct nfs4_delegreturnres res;
+ struct nfs_fh fh;
+ nfs4_stateid stateid;
+ struct rpc_cred *cred;
+ unsigned long timestamp;
+ struct nfs_fattr fattr;
+ int rpc_status;
+};
+
+static void nfs4_delegreturn_prepare(struct rpc_task *task, void *calldata)
{
- struct nfs4_delegreturnargs args = {
- .fhandle = NFS_FH(inode),
- .stateid = stateid,
- };
+ struct nfs4_delegreturndata *data = calldata;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DELEGRETURN],
- .rpc_argp = &args,
- .rpc_cred = cred,
+ .rpc_argp = &data->args,
+ .rpc_resp = &data->res,
+ .rpc_cred = data->cred,
};
+ nfs_fattr_init(data->res.fattr);
+ rpc_call_setup(task, &msg, 0);
+}
- return rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
+{
+ struct nfs4_delegreturndata *data = calldata;
+ data->rpc_status = task->tk_status;
+ if (data->rpc_status == 0)
+ renew_lease(data->res.server, data->timestamp);
+}
+
+static void nfs4_delegreturn_release(void *calldata)
+{
+ struct nfs4_delegreturndata *data = calldata;
+
+ put_rpccred(data->cred);
+ kfree(calldata);
+}
+
+static const struct rpc_call_ops nfs4_delegreturn_ops = {
+ .rpc_call_prepare = nfs4_delegreturn_prepare,
+ .rpc_call_done = nfs4_delegreturn_done,
+ .rpc_release = nfs4_delegreturn_release,
+};
+
+static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid)
+{
+ struct nfs4_delegreturndata *data;
+ struct nfs_server *server = NFS_SERVER(inode);
+ struct rpc_task *task;
+ int status;
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+ data->args.fhandle = &data->fh;
+ data->args.stateid = &data->stateid;
+ data->args.bitmask = server->attr_bitmask;
+ nfs_copy_fh(&data->fh, NFS_FH(inode));
+ memcpy(&data->stateid, stateid, sizeof(data->stateid));
+ data->res.fattr = &data->fattr;
+ data->res.server = server;
+ data->cred = get_rpccred(cred);
+ data->timestamp = jiffies;
+ data->rpc_status = 0;
+
+ task = rpc_run_task(NFS_CLIENT(inode), RPC_TASK_ASYNC, &nfs4_delegreturn_ops, data);
+ if (IS_ERR(task)) {
+ nfs4_delegreturn_release(data);
+ return PTR_ERR(task);
+ }
+ status = nfs4_wait_for_completion_rpc_task(task);
+ if (status == 0) {
+ status = data->rpc_status;
+ if (status == 0)
+ nfs_post_op_update_inode(inode, &data->fattr);
+ }
+ rpc_release_task(task);
+ return status;
}
int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid)
return timeout;
}
-static inline int
-nfs4_lck_type(int cmd, struct file_lock *request)
-{
- /* set lock type */
- switch (request->fl_type) {
- case F_RDLCK:
- return IS_SETLKW(cmd) ? NFS4_READW_LT : NFS4_READ_LT;
- case F_WRLCK:
- return IS_SETLKW(cmd) ? NFS4_WRITEW_LT : NFS4_WRITE_LT;
- case F_UNLCK:
- return NFS4_WRITE_LT;
- }
- BUG();
- return 0;
-}
-
-static inline uint64_t
-nfs4_lck_length(struct file_lock *request)
-{
- if (request->fl_end == OFFSET_MAX)
- return ~(uint64_t)0;
- return request->fl_end - request->fl_start + 1;
-}
-
static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
struct inode *inode = state->inode;
struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_client *clp = server->nfs4_state;
- struct nfs_lockargs arg = {
+ struct nfs_lockt_args arg = {
.fh = NFS_FH(inode),
- .type = nfs4_lck_type(cmd, request),
- .offset = request->fl_start,
- .length = nfs4_lck_length(request),
+ .fl = request,
};
- struct nfs_lockres res = {
- .server = server,
+ struct nfs_lockt_res res = {
+ .denied = request,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKT],
.rpc_resp = &res,
.rpc_cred = state->owner->so_cred,
};
- struct nfs_lowner nlo;
struct nfs4_lock_state *lsp;
int status;
down_read(&clp->cl_sem);
- nlo.clientid = clp->cl_clientid;
+ arg.lock_owner.clientid = clp->cl_clientid;
status = nfs4_set_lock_state(state, request);
if (status != 0)
goto out;
lsp = request->fl_u.nfs4_fl.owner;
- nlo.id = lsp->ls_id;
- arg.u.lockt = &nlo;
+ arg.lock_owner.id = lsp->ls_id;
status = rpc_call_sync(server->client, &msg, 0);
- if (!status) {
- request->fl_type = F_UNLCK;
- } else if (status == -NFS4ERR_DENIED) {
- int64_t len, start, end;
- start = res.u.denied.offset;
- len = res.u.denied.length;
- end = start + len - 1;
- if (end < 0 || len == 0)
- request->fl_end = OFFSET_MAX;
- else
- request->fl_end = (loff_t)end;
- request->fl_start = (loff_t)start;
- request->fl_type = F_WRLCK;
- if (res.u.denied.type & 1)
- request->fl_type = F_RDLCK;
- request->fl_pid = 0;
- status = 0;
+ switch (status) {
+ case 0:
+ request->fl_type = F_UNLCK;
+ break;
+ case -NFS4ERR_DENIED:
+ status = 0;
}
out:
up_read(&clp->cl_sem);
}
struct nfs4_unlockdata {
- struct nfs_lockargs arg;
- struct nfs_locku_opargs luargs;
- struct nfs_lockres res;
+ struct nfs_locku_args arg;
+ struct nfs_locku_res res;
struct nfs4_lock_state *lsp;
struct nfs_open_context *ctx;
+ struct file_lock fl;
+ const struct nfs_server *server;
+ unsigned long timestamp;
};
+static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl,
+ struct nfs_open_context *ctx,
+ struct nfs4_lock_state *lsp,
+ struct nfs_seqid *seqid)
+{
+ struct nfs4_unlockdata *p;
+ struct inode *inode = lsp->ls_state->inode;
+
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL)
+ return NULL;
+ p->arg.fh = NFS_FH(inode);
+ p->arg.fl = &p->fl;
+ p->arg.seqid = seqid;
+ p->arg.stateid = &lsp->ls_stateid;
+ p->lsp = lsp;
+ atomic_inc(&lsp->ls_count);
+ /* Ensure we don't close file until we're done freeing locks! */
+ p->ctx = get_nfs_open_context(ctx);
+ memcpy(&p->fl, fl, sizeof(p->fl));
+ p->server = NFS_SERVER(inode);
+ return p;
+}
+
static void nfs4_locku_release_calldata(void *data)
{
struct nfs4_unlockdata *calldata = data;
- nfs_free_seqid(calldata->luargs.seqid);
+ nfs_free_seqid(calldata->arg.seqid);
nfs4_put_lock_state(calldata->lsp);
put_nfs_open_context(calldata->ctx);
kfree(calldata);
if (RPC_ASSASSINATED(task))
return;
- nfs_increment_lock_seqid(task->tk_status, calldata->luargs.seqid);
+ nfs_increment_lock_seqid(task->tk_status, calldata->arg.seqid);
switch (task->tk_status) {
case 0:
memcpy(calldata->lsp->ls_stateid.data,
- calldata->res.u.stateid.data,
+ calldata->res.stateid.data,
sizeof(calldata->lsp->ls_stateid.data));
+ renew_lease(calldata->server, calldata->timestamp);
break;
case -NFS4ERR_STALE_STATEID:
case -NFS4ERR_EXPIRED:
- nfs4_schedule_state_recovery(calldata->res.server->nfs4_state);
+ nfs4_schedule_state_recovery(calldata->server->nfs4_state);
break;
default:
- if (nfs4_async_handle_error(task, calldata->res.server) == -EAGAIN) {
+ if (nfs4_async_handle_error(task, calldata->server) == -EAGAIN) {
rpc_restart_call(task);
}
}
.rpc_resp = &calldata->res,
.rpc_cred = calldata->lsp->ls_state->owner->so_cred,
};
- int status;
- status = nfs_wait_on_sequence(calldata->luargs.seqid, task);
- if (status != 0)
+ if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0)
return;
if ((calldata->lsp->ls_flags & NFS_LOCK_INITIALIZED) == 0) {
/* Note: exit _without_ running nfs4_locku_done */
task->tk_action = NULL;
return;
}
+ calldata->timestamp = jiffies;
rpc_call_setup(task, &msg, 0);
}
.rpc_release = nfs4_locku_release_calldata,
};
+static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
+ struct nfs_open_context *ctx,
+ struct nfs4_lock_state *lsp,
+ struct nfs_seqid *seqid)
+{
+ struct nfs4_unlockdata *data;
+ struct rpc_task *task;
+
+ data = nfs4_alloc_unlockdata(fl, ctx, lsp, seqid);
+ if (data == NULL) {
+ nfs_free_seqid(seqid);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* Unlock _before_ we do the RPC call */
+ do_vfs_lock(fl->fl_file, fl);
+ task = rpc_run_task(NFS_CLIENT(lsp->ls_state->inode), RPC_TASK_ASYNC, &nfs4_locku_ops, data);
+ if (IS_ERR(task))
+ nfs4_locku_release_calldata(data);
+ return task;
+}
+
static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
{
- struct nfs4_unlockdata *calldata;
- struct inode *inode = state->inode;
- struct nfs_server *server = NFS_SERVER(inode);
+ struct nfs_seqid *seqid;
struct nfs4_lock_state *lsp;
struct rpc_task *task;
int status = 0;
/* Is this a delegated lock? */
if (test_bit(NFS_DELEGATED_STATE, &state->flags))
- goto out;
+ goto out_unlock;
+ /* Is this open_owner holding any locks on the server? */
+ if (test_bit(LK_STATE_IN_USE, &state->flags) == 0)
+ goto out_unlock;
status = nfs4_set_lock_state(state, request);
if (status != 0)
- goto out;
+ goto out_unlock;
lsp = request->fl_u.nfs4_fl.owner;
- /* We might have lost the locks! */
- if ((lsp->ls_flags & NFS_LOCK_INITIALIZED) == 0)
- goto out;
status = -ENOMEM;
- calldata = kmalloc(sizeof(*calldata), GFP_KERNEL);
- if (calldata == NULL)
- goto out;
- calldata->luargs.seqid = nfs_alloc_seqid(&lsp->ls_seqid);
- if (calldata->luargs.seqid == NULL) {
- kfree(calldata);
- goto out;
- }
- calldata->luargs.stateid = &lsp->ls_stateid;
- calldata->arg.fh = NFS_FH(inode);
- calldata->arg.type = nfs4_lck_type(cmd, request);
- calldata->arg.offset = request->fl_start;
- calldata->arg.length = nfs4_lck_length(request);
- calldata->arg.u.locku = &calldata->luargs;
- calldata->res.server = server;
- calldata->lsp = lsp;
- atomic_inc(&lsp->ls_count);
-
- /* Ensure we don't close file until we're done freeing locks! */
- calldata->ctx = get_nfs_open_context((struct nfs_open_context*)request->fl_file->private_data);
-
- task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_locku_ops, calldata);
- if (!IS_ERR(task)) {
- status = nfs4_wait_for_completion_rpc_task(task);
- rpc_release_task(task);
- } else {
- status = PTR_ERR(task);
- nfs4_locku_release_calldata(calldata);
- }
-out:
+ seqid = nfs_alloc_seqid(&lsp->ls_seqid);
+ if (seqid == NULL)
+ goto out_unlock;
+ task = nfs4_do_unlck(request, request->fl_file->private_data, lsp, seqid);
+ status = PTR_ERR(task);
+ if (IS_ERR(task))
+ goto out_unlock;
+ status = nfs4_wait_for_completion_rpc_task(task);
+ rpc_release_task(task);
+ return status;
+out_unlock:
do_vfs_lock(request->fl_file, request);
return status;
}
-static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *request, int reclaim)
+struct nfs4_lockdata {
+ struct nfs_lock_args arg;
+ struct nfs_lock_res res;
+ struct nfs4_lock_state *lsp;
+ struct nfs_open_context *ctx;
+ struct file_lock fl;
+ unsigned long timestamp;
+ int rpc_status;
+ int cancelled;
+};
+
+static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
+ struct nfs_open_context *ctx, struct nfs4_lock_state *lsp)
{
- struct inode *inode = state->inode;
+ struct nfs4_lockdata *p;
+ struct inode *inode = lsp->ls_state->inode;
struct nfs_server *server = NFS_SERVER(inode);
- struct nfs4_lock_state *lsp = request->fl_u.nfs4_fl.owner;
- struct nfs_lock_opargs largs = {
- .lock_stateid = &lsp->ls_stateid,
- .open_stateid = &state->stateid,
- .lock_owner = {
- .clientid = server->nfs4_state->cl_clientid,
- .id = lsp->ls_id,
- },
- .reclaim = reclaim,
- };
- struct nfs_lockargs arg = {
- .fh = NFS_FH(inode),
- .type = nfs4_lck_type(cmd, request),
- .offset = request->fl_start,
- .length = nfs4_lck_length(request),
- .u = {
- .lock = &largs,
- },
- };
- struct nfs_lockres res = {
- .server = server,
- };
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL)
+ return NULL;
+
+ p->arg.fh = NFS_FH(inode);
+ p->arg.fl = &p->fl;
+ p->arg.lock_seqid = nfs_alloc_seqid(&lsp->ls_seqid);
+ if (p->arg.lock_seqid == NULL)
+ goto out_free;
+ p->arg.lock_stateid = &lsp->ls_stateid;
+ p->arg.lock_owner.clientid = server->nfs4_state->cl_clientid;
+ p->arg.lock_owner.id = lsp->ls_id;
+ p->lsp = lsp;
+ atomic_inc(&lsp->ls_count);
+ p->ctx = get_nfs_open_context(ctx);
+ memcpy(&p->fl, fl, sizeof(p->fl));
+ return p;
+out_free:
+ kfree(p);
+ return NULL;
+}
+
+static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
+{
+ struct nfs4_lockdata *data = calldata;
+ struct nfs4_state *state = data->lsp->ls_state;
+ struct nfs4_state_owner *sp = state->owner;
struct rpc_message msg = {
- .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCK],
- .rpc_argp = &arg,
- .rpc_resp = &res,
- .rpc_cred = state->owner->so_cred,
+ .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCK],
+ .rpc_argp = &data->arg,
+ .rpc_resp = &data->res,
+ .rpc_cred = sp->so_cred,
};
- int status = -ENOMEM;
- largs.lock_seqid = nfs_alloc_seqid(&lsp->ls_seqid);
- if (largs.lock_seqid == NULL)
- return -ENOMEM;
- if (!(lsp->ls_seqid.flags & NFS_SEQID_CONFIRMED)) {
- struct nfs4_state_owner *owner = state->owner;
-
- largs.open_seqid = nfs_alloc_seqid(&owner->so_seqid);
- if (largs.open_seqid == NULL)
+ if (nfs_wait_on_sequence(data->arg.lock_seqid, task) != 0)
+ return;
+ dprintk("%s: begin!\n", __FUNCTION__);
+ /* Do we need to do an open_to_lock_owner? */
+ if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) {
+ data->arg.open_seqid = nfs_alloc_seqid(&sp->so_seqid);
+ if (data->arg.open_seqid == NULL) {
+ data->rpc_status = -ENOMEM;
+ task->tk_action = NULL;
goto out;
- largs.new_lock_owner = 1;
- status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
- /* increment open seqid on success, and seqid mutating errors */
- if (largs.new_lock_owner != 0) {
- nfs_increment_open_seqid(status, largs.open_seqid);
- if (status == 0)
- nfs_confirm_seqid(&lsp->ls_seqid, 0);
}
- nfs_free_seqid(largs.open_seqid);
- } else
- status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR);
- /* increment lock seqid on success, and seqid mutating errors*/
- nfs_increment_lock_seqid(status, largs.lock_seqid);
- /* save the returned stateid. */
- if (status == 0) {
- memcpy(lsp->ls_stateid.data, res.u.stateid.data,
- sizeof(lsp->ls_stateid.data));
- lsp->ls_flags |= NFS_LOCK_INITIALIZED;
- } else if (status == -NFS4ERR_DENIED)
- status = -EAGAIN;
+ data->arg.open_stateid = &state->stateid;
+ data->arg.new_lock_owner = 1;
+ }
+ data->timestamp = jiffies;
+ rpc_call_setup(task, &msg, 0);
out:
- nfs_free_seqid(largs.lock_seqid);
- return status;
+ dprintk("%s: done!, ret = %d\n", __FUNCTION__, data->rpc_status);
+}
+
+static void nfs4_lock_done(struct rpc_task *task, void *calldata)
+{
+ struct nfs4_lockdata *data = calldata;
+
+ dprintk("%s: begin!\n", __FUNCTION__);
+
+ data->rpc_status = task->tk_status;
+ if (RPC_ASSASSINATED(task))
+ goto out;
+ if (data->arg.new_lock_owner != 0) {
+ nfs_increment_open_seqid(data->rpc_status, data->arg.open_seqid);
+ if (data->rpc_status == 0)
+ nfs_confirm_seqid(&data->lsp->ls_seqid, 0);
+ else
+ goto out;
+ }
+ if (data->rpc_status == 0) {
+ memcpy(data->lsp->ls_stateid.data, data->res.stateid.data,
+ sizeof(data->lsp->ls_stateid.data));
+ data->lsp->ls_flags |= NFS_LOCK_INITIALIZED;
+ renew_lease(NFS_SERVER(data->ctx->dentry->d_inode), data->timestamp);
+ }
+ nfs_increment_lock_seqid(data->rpc_status, data->arg.lock_seqid);
+out:
+ dprintk("%s: done, ret = %d!\n", __FUNCTION__, data->rpc_status);
+}
+
+static void nfs4_lock_release(void *calldata)
+{
+ struct nfs4_lockdata *data = calldata;
+
+ dprintk("%s: begin!\n", __FUNCTION__);
+ if (data->arg.open_seqid != NULL)
+ nfs_free_seqid(data->arg.open_seqid);
+ if (data->cancelled != 0) {
+ struct rpc_task *task;
+ task = nfs4_do_unlck(&data->fl, data->ctx, data->lsp,
+ data->arg.lock_seqid);
+ if (!IS_ERR(task))
+ rpc_release_task(task);
+ dprintk("%s: cancelling lock!\n", __FUNCTION__);
+ } else
+ nfs_free_seqid(data->arg.lock_seqid);
+ nfs4_put_lock_state(data->lsp);
+ put_nfs_open_context(data->ctx);
+ kfree(data);
+ dprintk("%s: done!\n", __FUNCTION__);
+}
+
+static const struct rpc_call_ops nfs4_lock_ops = {
+ .rpc_call_prepare = nfs4_lock_prepare,
+ .rpc_call_done = nfs4_lock_done,
+ .rpc_release = nfs4_lock_release,
+};
+
+static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int reclaim)
+{
+ struct nfs4_lockdata *data;
+ struct rpc_task *task;
+ int ret;
+
+ dprintk("%s: begin!\n", __FUNCTION__);
+ data = nfs4_alloc_lockdata(fl, fl->fl_file->private_data,
+ fl->fl_u.nfs4_fl.owner);
+ if (data == NULL)
+ return -ENOMEM;
+ if (IS_SETLKW(cmd))
+ data->arg.block = 1;
+ if (reclaim != 0)
+ data->arg.reclaim = 1;
+ task = rpc_run_task(NFS_CLIENT(state->inode), RPC_TASK_ASYNC,
+ &nfs4_lock_ops, data);
+ if (IS_ERR(task)) {
+ nfs4_lock_release(data);
+ return PTR_ERR(task);
+ }
+ ret = nfs4_wait_for_completion_rpc_task(task);
+ if (ret == 0) {
+ ret = data->rpc_status;
+ if (ret == -NFS4ERR_DENIED)
+ ret = -EAGAIN;
+ } else
+ data->cancelled = 1;
+ rpc_release_task(task);
+ dprintk("%s: done, ret = %d!\n", __FUNCTION__, ret);
+ return ret;
}
static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request)