NFSv4: Clean up handling of privileged operations

Privileged rpc calls are those that are run by the state recovery thread,
in cases where we're trying to recover the system after a server reboot
or a network partition. In those cases, we want to fence off all other
rpc calls (see nfs4_begin_drain_session()) so that they don't end up
using stateids or clientids that are in the process of being recovered.

Prior to this patch, we had to set up special callback functions in
order to declare an rpc call as being privileged.
By adding a new field to the sequence arguments, this patch simplifies
things considerably, and allows us to declare the rpc call as privileged
before it is run.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
Trond Myklebust 2012-10-29 19:02:20 -04:00
parent 275e7e20aa
commit 8fe72bac8d
2 changed files with 44 additions and 73 deletions

View File

@ -490,11 +490,17 @@ static void nfs41_init_sequence(struct nfs4_sequence_args *args,
{
args->sa_slot = NULL;
args->sa_cache_this = 0;
args->sa_privileged = 0;
if (cache_reply)
args->sa_cache_this = 1;
res->sr_slot = NULL;
}
static void nfs4_set_sequence_privileged(struct nfs4_sequence_args *args)
{
args->sa_privileged = 1;
}
int nfs41_setup_sequence(struct nfs4_session *session,
struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res,
@ -514,7 +520,7 @@ int nfs41_setup_sequence(struct nfs4_session *session,
spin_lock(&tbl->slot_tbl_lock);
if (test_bit(NFS4_SESSION_DRAINING, &session->session_state) &&
!rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) {
!args->sa_privileged) {
/* The state manager will wait until the slot table is empty */
dprintk("%s session is draining\n", __func__);
goto out_sleep;
@ -548,6 +554,9 @@ out_success:
rpc_call_start(task);
return 0;
out_sleep:
/* Privileged tasks are queued with top priority */
if (args->sa_privileged)
rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
spin_unlock(&tbl->slot_tbl_lock);
return -EAGAIN;
@ -593,12 +602,6 @@ static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata)
nfs41_setup_sequence(session, data->seq_args, data->seq_res, task);
}
static void nfs41_call_priv_sync_prepare(struct rpc_task *task, void *calldata)
{
rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
nfs41_call_sync_prepare(task, calldata);
}
static void nfs41_call_sync_done(struct rpc_task *task, void *calldata)
{
struct nfs41_call_sync_data *data = calldata;
@ -611,17 +614,11 @@ static const struct rpc_call_ops nfs41_call_sync_ops = {
.rpc_call_done = nfs41_call_sync_done,
};
static const struct rpc_call_ops nfs41_call_priv_sync_ops = {
.rpc_call_prepare = nfs41_call_priv_sync_prepare,
.rpc_call_done = nfs41_call_sync_done,
};
static int nfs4_call_sync_sequence(struct rpc_clnt *clnt,
struct nfs_server *server,
struct rpc_message *msg,
struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res,
int privileged)
struct nfs4_sequence_res *res)
{
int ret;
struct rpc_task *task;
@ -637,8 +634,6 @@ static int nfs4_call_sync_sequence(struct rpc_clnt *clnt,
.callback_data = &data
};
if (privileged)
task_setup.callback_ops = &nfs41_call_priv_sync_ops;
task = rpc_run_task(&task_setup);
if (IS_ERR(task))
ret = PTR_ERR(task);
@ -656,16 +651,21 @@ int _nfs4_call_sync_session(struct rpc_clnt *clnt,
struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res)
{
return nfs4_call_sync_sequence(clnt, server, msg, args, res, 0);
return nfs4_call_sync_sequence(clnt, server, msg, args, res);
}
#else
static inline
static
void nfs41_init_sequence(struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res, int cache_reply)
{
}
static void nfs4_set_sequence_privileged(struct nfs4_sequence_args *args)
{
}
static int nfs4_sequence_done(struct rpc_task *task,
struct nfs4_sequence_res *res)
{
@ -1475,13 +1475,6 @@ unlock_no_action:
rcu_read_unlock();
out_no_action:
task->tk_action = NULL;
}
static void nfs4_recover_open_prepare(struct rpc_task *task, void *calldata)
{
rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
nfs4_open_prepare(task, calldata);
}
static void nfs4_open_done(struct rpc_task *task, void *calldata)
@ -1542,12 +1535,6 @@ static const struct rpc_call_ops nfs4_open_ops = {
.rpc_release = nfs4_open_release,
};
static const struct rpc_call_ops nfs4_recover_open_ops = {
.rpc_call_prepare = nfs4_recover_open_prepare,
.rpc_call_done = nfs4_open_done,
.rpc_release = nfs4_open_release,
};
static int nfs4_run_open_task(struct nfs4_opendata *data, int isrecover)
{
struct inode *dir = data->dir->d_inode;
@ -1577,7 +1564,7 @@ static int nfs4_run_open_task(struct nfs4_opendata *data, int isrecover)
data->rpc_status = 0;
data->cancelled = 0;
if (isrecover)
task_setup_data.callback_ops = &nfs4_recover_open_ops;
nfs4_set_sequence_privileged(&o_arg->seq_args);
task = rpc_run_task(&task_setup_data);
if (IS_ERR(task))
return PTR_ERR(task);
@ -4558,8 +4545,9 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
return;
/* Do we need to do an open_to_lock_owner? */
if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) {
if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0)
if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0) {
goto out_release_lock_seqid;
}
data->arg.open_stateid = &state->stateid;
data->arg.new_lock_owner = 1;
data->res.open_seqid = data->arg.open_seqid;
@ -4574,13 +4562,7 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
nfs_release_seqid(data->arg.open_seqid);
out_release_lock_seqid:
nfs_release_seqid(data->arg.lock_seqid);
dprintk("%s: done!, ret = %d\n", __func__, task->tk_status);
}
static void nfs4_recover_lock_prepare(struct rpc_task *task, void *calldata)
{
rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
nfs4_lock_prepare(task, calldata);
dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status);
}
static void nfs4_lock_done(struct rpc_task *task, void *calldata)
@ -4635,12 +4617,6 @@ static const struct rpc_call_ops nfs4_lock_ops = {
.rpc_release = nfs4_lock_release,
};
static const struct rpc_call_ops nfs4_recover_lock_ops = {
.rpc_call_prepare = nfs4_recover_lock_prepare,
.rpc_call_done = nfs4_lock_done,
.rpc_release = nfs4_lock_release,
};
static void nfs4_handle_setlk_error(struct nfs_server *server, struct nfs4_lock_state *lsp, int new_lock_owner, int error)
{
switch (error) {
@ -4683,15 +4659,15 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
return -ENOMEM;
if (IS_SETLKW(cmd))
data->arg.block = 1;
if (recovery_type > NFS_LOCK_NEW) {
if (recovery_type == NFS_LOCK_RECLAIM)
data->arg.reclaim = NFS_LOCK_RECLAIM;
task_setup_data.callback_ops = &nfs4_recover_lock_ops;
}
nfs41_init_sequence(&data->arg.seq_args, &data->res.seq_res, 1);
msg.rpc_argp = &data->arg;
msg.rpc_resp = &data->res;
task_setup_data.callback_data = data;
if (recovery_type > NFS_LOCK_NEW) {
if (recovery_type == NFS_LOCK_RECLAIM)
data->arg.reclaim = NFS_LOCK_RECLAIM;
nfs4_set_sequence_privileged(&data->arg.seq_args);
}
task = rpc_run_task(&task_setup_data);
if (IS_ERR(task))
return PTR_ERR(task);
@ -5432,7 +5408,6 @@ static void nfs4_get_lease_time_prepare(struct rpc_task *task,
(struct nfs4_get_lease_time_data *)calldata;
dprintk("--> %s\n", __func__);
rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
/* just setup sequence, do not trigger session recovery
since we're invoked within one */
nfs41_setup_sequence(data->clp->cl_session,
@ -5500,6 +5475,7 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
int status;
nfs41_init_sequence(&args.la_seq_args, &res.lr_seq_res, 0);
nfs4_set_sequence_privileged(&args.la_seq_args);
dprintk("--> %s\n", __func__);
task = rpc_run_task(&task_setup);
@ -5775,26 +5751,15 @@ static void nfs41_sequence_prepare(struct rpc_task *task, void *data)
nfs41_setup_sequence(clp->cl_session, args, res, task);
}
static void nfs41_sequence_prepare_privileged(struct rpc_task *task, void *data)
{
rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
nfs41_sequence_prepare(task, data);
}
static const struct rpc_call_ops nfs41_sequence_ops = {
.rpc_call_done = nfs41_sequence_call_done,
.rpc_call_prepare = nfs41_sequence_prepare,
.rpc_release = nfs41_sequence_release,
};
static const struct rpc_call_ops nfs41_sequence_privileged_ops = {
.rpc_call_done = nfs41_sequence_call_done,
.rpc_call_prepare = nfs41_sequence_prepare_privileged,
.rpc_release = nfs41_sequence_release,
};
static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred,
const struct rpc_call_ops *seq_ops)
static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
struct rpc_cred *cred,
bool is_privileged)
{
struct nfs4_sequence_data *calldata;
struct rpc_message msg = {
@ -5804,7 +5769,7 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_
struct rpc_task_setup task_setup_data = {
.rpc_client = clp->cl_rpcclient,
.rpc_message = &msg,
.callback_ops = seq_ops,
.callback_ops = &nfs41_sequence_ops,
.flags = RPC_TASK_ASYNC | RPC_TASK_SOFT,
};
@ -5816,6 +5781,8 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_
return ERR_PTR(-ENOMEM);
}
nfs41_init_sequence(&calldata->args, &calldata->res, 0);
if (is_privileged)
nfs4_set_sequence_privileged(&calldata->args);
msg.rpc_argp = &calldata->args;
msg.rpc_resp = &calldata->res;
calldata->clp = clp;
@ -5831,7 +5798,7 @@ static int nfs41_proc_async_sequence(struct nfs_client *clp, struct rpc_cred *cr
if ((renew_flags & NFS4_RENEW_TIMEOUT) == 0)
return 0;
task = _nfs41_proc_sequence(clp, cred, &nfs41_sequence_ops);
task = _nfs41_proc_sequence(clp, cred, false);
if (IS_ERR(task))
ret = PTR_ERR(task);
else
@ -5845,7 +5812,7 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
struct rpc_task *task;
int ret;
task = _nfs41_proc_sequence(clp, cred, &nfs41_sequence_privileged_ops);
task = _nfs41_proc_sequence(clp, cred, true);
if (IS_ERR(task)) {
ret = PTR_ERR(task);
goto out;
@ -5874,7 +5841,6 @@ static void nfs4_reclaim_complete_prepare(struct rpc_task *task, void *data)
{
struct nfs4_reclaim_complete_data *calldata = data;
rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
nfs41_setup_sequence(calldata->clp->cl_session,
&calldata->arg.seq_args,
&calldata->res.seq_res,
@ -5955,6 +5921,7 @@ static int nfs41_proc_reclaim_complete(struct nfs_client *clp)
calldata->arg.one_fs = 0;
nfs41_init_sequence(&calldata->arg.seq_args, &calldata->res.seq_res, 0);
nfs4_set_sequence_privileged(&calldata->arg.seq_args);
msg.rpc_argp = &calldata->arg;
msg.rpc_resp = &calldata->res;
task_setup_data.callback_data = calldata;
@ -6521,7 +6488,9 @@ static int _nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
dprintk("NFS call test_stateid %p\n", stateid);
nfs41_init_sequence(&args.seq_args, &res.seq_res, 0);
status = nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 1);
nfs4_set_sequence_privileged(&args.seq_args);
status = nfs4_call_sync_sequence(server->client, server, &msg,
&args.seq_args, &res.seq_res);
if (status != NFS_OK) {
dprintk("NFS reply test_stateid: failed, %d\n", status);
return status;
@ -6568,8 +6537,9 @@ static int _nfs4_free_stateid(struct nfs_server *server, nfs4_stateid *stateid)
dprintk("NFS call free_stateid %p\n", stateid);
nfs41_init_sequence(&args.seq_args, &res.seq_res, 0);
nfs4_set_sequence_privileged(&args.seq_args);
status = nfs4_call_sync_sequence(server->client, server, &msg,
&args.seq_args, &res.seq_res, 1);
&args.seq_args, &res.seq_res);
dprintk("NFS reply free_stateid: %d\n", status);
return status;
}

View File

@ -188,7 +188,8 @@ struct nfs4_channel_attrs {
struct nfs4_slot;
struct nfs4_sequence_args {
struct nfs4_slot *sa_slot;
u8 sa_cache_this;
u8 sa_cache_this : 1,
sa_privileged : 1;
};
struct nfs4_sequence_res {