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_slot = NULL;
args->sa_cache_this = 0; args->sa_cache_this = 0;
args->sa_privileged = 0;
if (cache_reply) if (cache_reply)
args->sa_cache_this = 1; args->sa_cache_this = 1;
res->sr_slot = NULL; 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, int nfs41_setup_sequence(struct nfs4_session *session,
struct nfs4_sequence_args *args, struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res, struct nfs4_sequence_res *res,
@ -514,7 +520,7 @@ int nfs41_setup_sequence(struct nfs4_session *session,
spin_lock(&tbl->slot_tbl_lock); spin_lock(&tbl->slot_tbl_lock);
if (test_bit(NFS4_SESSION_DRAINING, &session->session_state) && 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 */ /* The state manager will wait until the slot table is empty */
dprintk("%s session is draining\n", __func__); dprintk("%s session is draining\n", __func__);
goto out_sleep; goto out_sleep;
@ -548,6 +554,9 @@ out_success:
rpc_call_start(task); rpc_call_start(task);
return 0; return 0;
out_sleep: 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); rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
spin_unlock(&tbl->slot_tbl_lock); spin_unlock(&tbl->slot_tbl_lock);
return -EAGAIN; 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); 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) static void nfs41_call_sync_done(struct rpc_task *task, void *calldata)
{ {
struct nfs41_call_sync_data *data = 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, .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, static int nfs4_call_sync_sequence(struct rpc_clnt *clnt,
struct nfs_server *server, struct nfs_server *server,
struct rpc_message *msg, struct rpc_message *msg,
struct nfs4_sequence_args *args, struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res, struct nfs4_sequence_res *res)
int privileged)
{ {
int ret; int ret;
struct rpc_task *task; struct rpc_task *task;
@ -637,8 +634,6 @@ static int nfs4_call_sync_sequence(struct rpc_clnt *clnt,
.callback_data = &data .callback_data = &data
}; };
if (privileged)
task_setup.callback_ops = &nfs41_call_priv_sync_ops;
task = rpc_run_task(&task_setup); task = rpc_run_task(&task_setup);
if (IS_ERR(task)) if (IS_ERR(task))
ret = PTR_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_args *args,
struct nfs4_sequence_res *res) 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 #else
static inline static
void nfs41_init_sequence(struct nfs4_sequence_args *args, void nfs41_init_sequence(struct nfs4_sequence_args *args,
struct nfs4_sequence_res *res, int cache_reply) 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, static int nfs4_sequence_done(struct rpc_task *task,
struct nfs4_sequence_res *res) struct nfs4_sequence_res *res)
{ {
@ -1475,13 +1475,6 @@ unlock_no_action:
rcu_read_unlock(); rcu_read_unlock();
out_no_action: out_no_action:
task->tk_action = NULL; 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) 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, .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) static int nfs4_run_open_task(struct nfs4_opendata *data, int isrecover)
{ {
struct inode *dir = data->dir->d_inode; 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->rpc_status = 0;
data->cancelled = 0; data->cancelled = 0;
if (isrecover) 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); task = rpc_run_task(&task_setup_data);
if (IS_ERR(task)) if (IS_ERR(task))
return PTR_ERR(task); return PTR_ERR(task);
@ -4558,8 +4545,9 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
return; return;
/* Do we need to do an open_to_lock_owner? */ /* Do we need to do an open_to_lock_owner? */
if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) { 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; goto out_release_lock_seqid;
}
data->arg.open_stateid = &state->stateid; data->arg.open_stateid = &state->stateid;
data->arg.new_lock_owner = 1; data->arg.new_lock_owner = 1;
data->res.open_seqid = data->arg.open_seqid; 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); nfs_release_seqid(data->arg.open_seqid);
out_release_lock_seqid: out_release_lock_seqid:
nfs_release_seqid(data->arg.lock_seqid); nfs_release_seqid(data->arg.lock_seqid);
dprintk("%s: done!, ret = %d\n", __func__, task->tk_status); dprintk("%s: done!, ret = %d\n", __func__, data->rpc_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);
} }
static void nfs4_lock_done(struct rpc_task *task, void *calldata) 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, .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) static void nfs4_handle_setlk_error(struct nfs_server *server, struct nfs4_lock_state *lsp, int new_lock_owner, int error)
{ {
switch (error) { switch (error) {
@ -4683,15 +4659,15 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
return -ENOMEM; return -ENOMEM;
if (IS_SETLKW(cmd)) if (IS_SETLKW(cmd))
data->arg.block = 1; 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); nfs41_init_sequence(&data->arg.seq_args, &data->res.seq_res, 1);
msg.rpc_argp = &data->arg; msg.rpc_argp = &data->arg;
msg.rpc_resp = &data->res; msg.rpc_resp = &data->res;
task_setup_data.callback_data = data; 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); task = rpc_run_task(&task_setup_data);
if (IS_ERR(task)) if (IS_ERR(task))
return PTR_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; (struct nfs4_get_lease_time_data *)calldata;
dprintk("--> %s\n", __func__); dprintk("--> %s\n", __func__);
rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
/* just setup sequence, do not trigger session recovery /* just setup sequence, do not trigger session recovery
since we're invoked within one */ since we're invoked within one */
nfs41_setup_sequence(data->clp->cl_session, 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; int status;
nfs41_init_sequence(&args.la_seq_args, &res.lr_seq_res, 0); nfs41_init_sequence(&args.la_seq_args, &res.lr_seq_res, 0);
nfs4_set_sequence_privileged(&args.la_seq_args);
dprintk("--> %s\n", __func__); dprintk("--> %s\n", __func__);
task = rpc_run_task(&task_setup); 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); 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 = { static const struct rpc_call_ops nfs41_sequence_ops = {
.rpc_call_done = nfs41_sequence_call_done, .rpc_call_done = nfs41_sequence_call_done,
.rpc_call_prepare = nfs41_sequence_prepare, .rpc_call_prepare = nfs41_sequence_prepare,
.rpc_release = nfs41_sequence_release, .rpc_release = nfs41_sequence_release,
}; };
static const struct rpc_call_ops nfs41_sequence_privileged_ops = { static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
.rpc_call_done = nfs41_sequence_call_done, struct rpc_cred *cred,
.rpc_call_prepare = nfs41_sequence_prepare_privileged, bool is_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)
{ {
struct nfs4_sequence_data *calldata; struct nfs4_sequence_data *calldata;
struct rpc_message msg = { 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 = { struct rpc_task_setup task_setup_data = {
.rpc_client = clp->cl_rpcclient, .rpc_client = clp->cl_rpcclient,
.rpc_message = &msg, .rpc_message = &msg,
.callback_ops = seq_ops, .callback_ops = &nfs41_sequence_ops,
.flags = RPC_TASK_ASYNC | RPC_TASK_SOFT, .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); return ERR_PTR(-ENOMEM);
} }
nfs41_init_sequence(&calldata->args, &calldata->res, 0); nfs41_init_sequence(&calldata->args, &calldata->res, 0);
if (is_privileged)
nfs4_set_sequence_privileged(&calldata->args);
msg.rpc_argp = &calldata->args; msg.rpc_argp = &calldata->args;
msg.rpc_resp = &calldata->res; msg.rpc_resp = &calldata->res;
calldata->clp = clp; 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) if ((renew_flags & NFS4_RENEW_TIMEOUT) == 0)
return 0; return 0;
task = _nfs41_proc_sequence(clp, cred, &nfs41_sequence_ops); task = _nfs41_proc_sequence(clp, cred, false);
if (IS_ERR(task)) if (IS_ERR(task))
ret = PTR_ERR(task); ret = PTR_ERR(task);
else else
@ -5845,7 +5812,7 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
struct rpc_task *task; struct rpc_task *task;
int ret; int ret;
task = _nfs41_proc_sequence(clp, cred, &nfs41_sequence_privileged_ops); task = _nfs41_proc_sequence(clp, cred, true);
if (IS_ERR(task)) { if (IS_ERR(task)) {
ret = PTR_ERR(task); ret = PTR_ERR(task);
goto out; 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; struct nfs4_reclaim_complete_data *calldata = data;
rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
nfs41_setup_sequence(calldata->clp->cl_session, nfs41_setup_sequence(calldata->clp->cl_session,
&calldata->arg.seq_args, &calldata->arg.seq_args,
&calldata->res.seq_res, &calldata->res.seq_res,
@ -5955,6 +5921,7 @@ static int nfs41_proc_reclaim_complete(struct nfs_client *clp)
calldata->arg.one_fs = 0; calldata->arg.one_fs = 0;
nfs41_init_sequence(&calldata->arg.seq_args, &calldata->res.seq_res, 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_argp = &calldata->arg;
msg.rpc_resp = &calldata->res; msg.rpc_resp = &calldata->res;
task_setup_data.callback_data = calldata; 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); dprintk("NFS call test_stateid %p\n", stateid);
nfs41_init_sequence(&args.seq_args, &res.seq_res, 0); 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) { if (status != NFS_OK) {
dprintk("NFS reply test_stateid: failed, %d\n", status); dprintk("NFS reply test_stateid: failed, %d\n", status);
return 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); dprintk("NFS call free_stateid %p\n", stateid);
nfs41_init_sequence(&args.seq_args, &res.seq_res, 0); 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, 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); dprintk("NFS reply free_stateid: %d\n", status);
return status; return status;
} }

View File

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