From 3bfb0fc59192f7fffae0a1caada8dc68af53997a Mon Sep 17 00:00:00 2001 From: Alexandros Batsakis Date: Wed, 9 Dec 2009 01:50:11 -0800 Subject: [PATCH 01/13] nfs: minor cleanup of session draining Signed-off-by: Alexandros Batsakis Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 9f5f11ecfd93..d117a7befd46 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -341,6 +341,23 @@ nfs4_free_slot(struct nfs4_slot_table *tbl, u8 free_slotid) free_slotid, tbl->highest_used_slotid); } +/* + * Signal state manager thread if session is drained + */ +static void nfs41_check_drain_session_complete(struct nfs4_session *ses) +{ + if (!test_bit(NFS4CLNT_SESSION_DRAINING, &ses->clp->cl_state)) { + rpc_wake_up_next(&ses->fc_slot_table.slot_tbl_waitq); + return; + } + + if (ses->fc_slot_table.highest_used_slotid != -1) + return; + + dprintk("%s COMPLETE: Session Drained\n", __func__); + complete(&ses->complete); +} + static void nfs41_sequence_free_slot(const struct nfs_client *clp, struct nfs4_sequence_res *res) { @@ -356,15 +373,7 @@ static void nfs41_sequence_free_slot(const struct nfs_client *clp, spin_lock(&tbl->slot_tbl_lock); nfs4_free_slot(tbl, res->sr_slotid); - - /* Signal state manager thread if session is drained */ - if (test_bit(NFS4CLNT_SESSION_DRAINING, &clp->cl_state)) { - if (tbl->highest_used_slotid == -1) { - dprintk("%s COMPLETE: Session Drained\n", __func__); - complete(&clp->cl_session->complete); - } - } else - rpc_wake_up_next(&tbl->slot_tbl_waitq); + nfs41_check_drain_session_complete(clp->cl_session); spin_unlock(&tbl->slot_tbl_lock); res->sr_slotid = NFS4_MAX_SLOT_TABLE; } From 0f7e720694e88bacf808b525069fb72d1c237171 Mon Sep 17 00:00:00 2001 From: Alexandros Batsakis Date: Wed, 9 Dec 2009 01:50:13 -0800 Subject: [PATCH 02/13] nfs: do not do a LOOKUP after open Signed-off-by: Alexandros Batsakis Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d117a7befd46..297e23310efd 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1436,9 +1436,6 @@ static int _nfs4_proc_open(struct nfs4_opendata *data) if (status != 0 || !data->rpc_done) return status; - if (o_res->fh.size == 0) - _nfs4_proc_lookup(dir, o_arg->name, &o_res->fh, o_res->f_attr); - if (o_arg->open_flags & O_CREAT) { update_changeattr(dir, &o_res->cinfo); nfs_post_op_update_inode(dir, o_res->dir_attr); From afe6c27ccb8cc31ce8ed0bd3589ce549f523c8e7 Mon Sep 17 00:00:00 2001 From: Alexandros Batsakis Date: Wed, 9 Dec 2009 01:50:14 -0800 Subject: [PATCH 03/13] nfs: change nfs4_do_setlk params to identify recovery type Signed-off-by: Alexandros Batsakis Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 4 ++++ fs/nfs/nfs4proc.c | 14 +++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 7e57b04e4014..e8791d5a2f7a 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -108,6 +108,10 @@ enum { NFS_OWNER_RECLAIM_NOGRACE }; +#define NFS_LOCK_NEW 0 +#define NFS_LOCK_RECLAIM 1 +#define NFS_LOCK_EXPIRED 2 + /* * struct nfs4_state maintains the client-side state for a given * (state_owner,inode) tuple (OPEN) or state_owner (LOCK). diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 297e23310efd..d44f1071930f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4002,7 +4002,7 @@ static const struct rpc_call_ops nfs4_lock_ops = { .rpc_release = nfs4_lock_release, }; -static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int reclaim) +static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int recovery_type) { struct nfs4_lockdata *data; struct rpc_task *task; @@ -4026,8 +4026,8 @@ 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 (reclaim != 0) - data->arg.reclaim = 1; + if (recovery_type == NFS_LOCK_RECLAIM) + data->arg.reclaim = NFS_LOCK_RECLAIM; msg.rpc_argp = &data->arg, msg.rpc_resp = &data->res, task_setup_data.callback_data = data; @@ -4054,7 +4054,7 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request /* Cache the lock if possible... */ if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) return 0; - err = _nfs4_do_setlk(state, F_SETLK, request, 1); + err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_RECLAIM); if (err != -NFS4ERR_DELAY) break; nfs4_handle_exception(server, err, &exception); @@ -4074,7 +4074,7 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request do { if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) return 0; - err = _nfs4_do_setlk(state, F_SETLK, request, 0); + err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_EXPIRED); switch (err) { default: goto out; @@ -4110,7 +4110,7 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock status = do_vfs_lock(request->fl_file, request); goto out_unlock; } - status = _nfs4_do_setlk(state, cmd, request, 0); + status = _nfs4_do_setlk(state, cmd, request, NFS_LOCK_NEW); if (status != 0) goto out_unlock; /* Note: we always want to sleep here! */ @@ -4193,7 +4193,7 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl) if (err != 0) goto out; do { - err = _nfs4_do_setlk(state, F_SETLK, fl, 0); + err = _nfs4_do_setlk(state, F_SETLK, fl, NFS_LOCK_NEW); switch (err) { default: printk(KERN_ERR "%s: unhandled error %d.\n", From 48f186124220794fce85ed1439fc32f16f69d3e2 Mon Sep 17 00:00:00 2001 From: Alexandros Batsakis Date: Mon, 14 Dec 2009 21:27:53 -0800 Subject: [PATCH 04/13] rpc: add rpc_queue_empty function Signed-off-by: Alexandros Batsakis Signed-off-by: Trond Myklebust --- include/linux/sunrpc/sched.h | 1 + net/sunrpc/sched.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 1906782ec86b..9157405f9320 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -229,6 +229,7 @@ void rpc_wake_up_queued_task(struct rpc_wait_queue *, void rpc_wake_up(struct rpc_wait_queue *); struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *); void rpc_wake_up_status(struct rpc_wait_queue *, int); +int rpc_queue_empty(struct rpc_wait_queue *); void rpc_delay(struct rpc_task *, unsigned long); void * rpc_malloc(struct rpc_task *, size_t); void rpc_free(void *); diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index cef74ba0666c..89ea8e69ec78 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -384,6 +384,20 @@ static void rpc_wake_up_task_queue_locked(struct rpc_wait_queue *queue, struct r __rpc_do_wake_up_task(queue, task); } +/* + * Tests whether rpc queue is empty + */ +int rpc_queue_empty(struct rpc_wait_queue *queue) +{ + int res; + + spin_lock_bh(&queue->lock); + res = queue->qlen; + spin_unlock_bh(&queue->lock); + return (res == 0); +} +EXPORT_SYMBOL_GPL(rpc_queue_empty); + /* * Wake up a task on a specific queue */ From 40ead580ae70bba1f66f426aeb938051e4e83900 Mon Sep 17 00:00:00 2001 From: Alexandros Batsakis Date: Mon, 14 Dec 2009 21:27:54 -0800 Subject: [PATCH 05/13] nfs: remove rpc_task argument from nfs4_find_slot Signed-off-by: Alexandros Batsakis Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d44f1071930f..acd698baf8eb 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -430,7 +430,7 @@ static void nfs41_sequence_done(struct nfs_client *clp, * Note: must be called with under the slot_tbl_lock. */ static u8 -nfs4_find_slot(struct nfs4_slot_table *tbl, struct rpc_task *task) +nfs4_find_slot(struct nfs4_slot_table *tbl) { int slotid; u8 ret_id = NFS4_MAX_SLOT_TABLE; @@ -483,7 +483,7 @@ static int nfs41_setup_sequence(struct nfs4_session *session, return -EAGAIN; } - slotid = nfs4_find_slot(tbl, task); + slotid = nfs4_find_slot(tbl); if (slotid == NFS4_MAX_SLOT_TABLE) { rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); spin_unlock(&tbl->slot_tbl_lock); From cf3b01b54880debb01ea7d471123da5887a7c2cb Mon Sep 17 00:00:00 2001 From: Alexandros Batsakis Date: Mon, 14 Dec 2009 21:27:55 -0800 Subject: [PATCH 06/13] rpc: add a new priority in RPC task Signed-off-by: Alexandros Batsakis Signed-off-by: Trond Myklebust --- include/linux/sunrpc/sched.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 9157405f9320..7bc7fd5291ce 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -173,7 +173,8 @@ struct rpc_task_setup { #define RPC_PRIORITY_LOW (-1) #define RPC_PRIORITY_NORMAL (0) #define RPC_PRIORITY_HIGH (1) -#define RPC_NR_PRIORITY (1 + RPC_PRIORITY_HIGH - RPC_PRIORITY_LOW) +#define RPC_PRIORITY_PRIVILEGED (2) +#define RPC_NR_PRIORITY (1 + RPC_PRIORITY_PRIVILEGED - RPC_PRIORITY_LOW) struct rpc_timer { struct timer_list timer; @@ -255,6 +256,16 @@ static inline int rpc_wait_for_completion_task(struct rpc_task *task) return __rpc_wait_for_completion_task(task, NULL); } +static inline void rpc_task_set_priority(struct rpc_task *task, unsigned char prio) +{ + task->tk_priority = prio - RPC_PRIORITY_LOW; +} + +static inline int rpc_task_has_priority(struct rpc_task *task, unsigned char prio) +{ + return (task->tk_priority + RPC_PRIORITY_LOW == prio); +} + #ifdef RPC_DEBUG static inline const char * rpc_qname(struct rpc_wait_queue *q) { From 689cf5c15baf603a8041565ff0bd0d65d1634fd7 Mon Sep 17 00:00:00 2001 From: Alexandros Batsakis Date: Mon, 14 Dec 2009 21:27:56 -0800 Subject: [PATCH 07/13] nfs: enforce FIFO ordering of operations trying to acquire slot Signed-off-by: Alexandros Batsakis Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 22 ++++++++++++++++++---- fs/nfs/nfs4state.c | 18 ++++++++++++++++-- net/sunrpc/sched.c | 1 + 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index acd698baf8eb..02513da9a016 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -346,8 +346,12 @@ nfs4_free_slot(struct nfs4_slot_table *tbl, u8 free_slotid) */ static void nfs41_check_drain_session_complete(struct nfs4_session *ses) { + struct rpc_task *task; + if (!test_bit(NFS4CLNT_SESSION_DRAINING, &ses->clp->cl_state)) { - rpc_wake_up_next(&ses->fc_slot_table.slot_tbl_waitq); + task = rpc_wake_up_next(&ses->fc_slot_table.slot_tbl_waitq); + if (task) + rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); return; } @@ -483,6 +487,14 @@ static int nfs41_setup_sequence(struct nfs4_session *session, return -EAGAIN; } + if (!rpc_queue_empty(&tbl->slot_tbl_waitq) && + !rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) { + rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); + spin_unlock(&tbl->slot_tbl_lock); + dprintk("%s enforce FIFO order\n", __func__); + return -EAGAIN; + } + slotid = nfs4_find_slot(tbl); if (slotid == NFS4_MAX_SLOT_TABLE) { rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); @@ -492,6 +504,7 @@ static int nfs41_setup_sequence(struct nfs4_session *session, } spin_unlock(&tbl->slot_tbl_lock); + rpc_task_set_priority(task, RPC_PRIORITY_NORMAL); slot = tbl->slots + slotid; args->sa_session = session; args->sa_slotid = slotid; @@ -4401,11 +4414,12 @@ 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 */ ret = nfs41_setup_sequence(data->clp->cl_session, - &data->args->la_seq_args, - &data->res->lr_seq_res, 0, task); + &data->args->la_seq_args, + &data->res->lr_seq_res, 0, task); BUG_ON(ret == -EAGAIN); rpc_call_start(task); @@ -4625,7 +4639,7 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) tbl = &session->fc_slot_table; tbl->highest_used_slotid = -1; spin_lock_init(&tbl->slot_tbl_lock); - rpc_init_wait_queue(&tbl->slot_tbl_waitq, "ForeChannel Slot table"); + rpc_init_priority_wait_queue(&tbl->slot_tbl_waitq, "ForeChannel Slot table"); tbl = &session->bc_slot_table; tbl->highest_used_slotid = -1; diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index e76427e6346f..0e45075836b2 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -138,8 +138,22 @@ static int nfs41_setup_state_renewal(struct nfs_client *clp) static void nfs41_end_drain_session(struct nfs_client *clp, struct nfs4_session *ses) { - if (test_and_clear_bit(NFS4CLNT_SESSION_DRAINING, &clp->cl_state)) - rpc_wake_up(&ses->fc_slot_table.slot_tbl_waitq); + int max_slots; + + if (test_and_clear_bit(NFS4CLNT_SESSION_DRAINING, &clp->cl_state)) { + spin_lock(&ses->fc_slot_table.slot_tbl_lock); + max_slots = ses->fc_slot_table.max_slots; + while (max_slots--) { + struct rpc_task *task; + + task = rpc_wake_up_next(&ses->fc_slot_table. + slot_tbl_waitq); + if (!task) + break; + rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); + } + spin_unlock(&ses->fc_slot_table.slot_tbl_lock); + } } static int nfs41_begin_drain_session(struct nfs_client *clp, diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 89ea8e69ec78..aae6907fd546 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -210,6 +210,7 @@ void rpc_init_priority_wait_queue(struct rpc_wait_queue *queue, const char *qnam { __rpc_init_priority_wait_queue(queue, qname, RPC_NR_PRIORITY); } +EXPORT_SYMBOL_GPL(rpc_init_priority_wait_queue); void rpc_init_wait_queue(struct rpc_wait_queue *queue, const char *qname) { From b257957e502a2c467c3c75005215a3f45ecb7f25 Mon Sep 17 00:00:00 2001 From: Alexandros Batsakis Date: Mon, 14 Dec 2009 21:27:57 -0800 Subject: [PATCH 08/13] nfs: make recovery state manager operations privileged Signed-off-by: Alexandros Batsakis Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 115 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 97 insertions(+), 18 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 02513da9a016..18cce76867ce 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -64,6 +64,7 @@ struct nfs4_opendata; static int _nfs4_proc_open(struct nfs4_opendata *data); +static int _nfs4_recover_proc_open(struct nfs4_opendata *data); static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *); static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr); @@ -567,6 +568,12 @@ static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata) rpc_call_start(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; @@ -579,12 +586,18 @@ struct rpc_call_ops nfs41_call_sync_ops = { .rpc_call_done = nfs41_call_sync_done, }; +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 nfs_client *clp, struct rpc_clnt *clnt, struct rpc_message *msg, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, - int cache_reply) + int cache_reply, + int privileged) { int ret; struct rpc_task *task; @@ -602,6 +615,8 @@ static int nfs4_call_sync_sequence(struct nfs_client *clp, }; res->sr_slotid = NFS4_MAX_SLOT_TABLE; + 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); @@ -619,7 +634,7 @@ int _nfs4_call_sync_session(struct nfs_server *server, int cache_reply) { return nfs4_call_sync_sequence(server->nfs_client, server->client, - msg, args, res, cache_reply); + msg, args, res, cache_reply, 0); } #endif /* CONFIG_NFS_V4_1 */ @@ -1057,7 +1072,7 @@ static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmod memset(&opendata->o_res, 0, sizeof(opendata->o_res)); memset(&opendata->c_res, 0, sizeof(opendata->c_res)); nfs4_init_opendata_res(opendata); - ret = _nfs4_proc_open(opendata); + ret = _nfs4_recover_proc_open(opendata); if (ret != 0) return ret; newstate = nfs4_opendata_to_nfs4_state(opendata); @@ -1348,6 +1363,12 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) } +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) { struct nfs4_opendata *data = calldata; @@ -1406,10 +1427,13 @@ static const struct rpc_call_ops nfs4_open_ops = { .rpc_release = nfs4_open_release, }; -/* - * Note: On error, nfs4_proc_open will free the struct nfs4_opendata - */ -static int _nfs4_proc_open(struct nfs4_opendata *data) +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; struct nfs_server *server = NFS_SERVER(dir); @@ -1436,16 +1460,55 @@ static int _nfs4_proc_open(struct nfs4_opendata *data) data->rpc_done = 0; data->rpc_status = 0; data->cancelled = 0; + if (isrecover) + task_setup_data.callback_ops = &nfs4_recover_open_ops; task = rpc_run_task(&task_setup_data); - if (IS_ERR(task)) - return PTR_ERR(task); - status = nfs4_wait_for_completion_rpc_task(task); - if (status != 0) { - data->cancelled = 1; - smp_wmb(); - } else - status = data->rpc_status; - rpc_put_task(task); + if (IS_ERR(task)) + return PTR_ERR(task); + status = nfs4_wait_for_completion_rpc_task(task); + if (status != 0) { + data->cancelled = 1; + smp_wmb(); + } else + status = data->rpc_status; + rpc_put_task(task); + + return status; +} + +static int _nfs4_recover_proc_open(struct nfs4_opendata *data) +{ + struct inode *dir = data->dir->d_inode; + struct nfs_openres *o_res = &data->o_res; + int status; + + status = nfs4_run_open_task(data, 1); + if (status != 0 || !data->rpc_done) + return status; + + nfs_refresh_inode(dir, o_res->dir_attr); + + if (o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) { + status = _nfs4_proc_open_confirm(data); + if (status != 0) + return status; + } + + return status; +} + +/* + * Note: On error, nfs4_proc_open will free the struct nfs4_opendata + */ +static int _nfs4_proc_open(struct nfs4_opendata *data) +{ + struct inode *dir = data->dir->d_inode; + struct nfs_server *server = NFS_SERVER(dir); + struct nfs_openargs *o_arg = &data->o_arg; + struct nfs_openres *o_res = &data->o_res; + int status; + + status = nfs4_run_open_task(data, 0); if (status != 0 || !data->rpc_done) return status; @@ -3960,6 +4023,12 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata) 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) { struct nfs4_lockdata *data = calldata; @@ -4015,6 +4084,12 @@ 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 int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int recovery_type) { struct nfs4_lockdata *data; @@ -4039,8 +4114,11 @@ 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_RECLAIM) + 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; + } msg.rpc_argp = &data->arg, msg.rpc_resp = &data->res, task_setup_data.callback_data = data; @@ -4891,7 +4969,7 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred) args.sa_cache_this = 0; return nfs4_call_sync_sequence(clp, clp->cl_rpcclient, &msg, &args, - &res, 0); + &res, args.sa_cache_this, 1); } void nfs41_sequence_call_done(struct rpc_task *task, void *data) @@ -4973,6 +5051,7 @@ 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); if (nfs4_setup_sequence(calldata->clp, &calldata->arg.seq_args, &calldata->res.seq_res, 0, task)) return; From 5601a00d671fe89f9b087513244abcd08ad67e7d Mon Sep 17 00:00:00 2001 From: Alexandros Batsakis Date: Mon, 14 Dec 2009 21:27:58 -0800 Subject: [PATCH 09/13] nfs: run state manager in privileged mode Signed-off-by: Alexandros Batsakis Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 3 ++- fs/nfs/nfs4state.c | 34 ++++++++++++++-------------------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 18cce76867ce..4d6560a73949 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -477,7 +477,8 @@ static int nfs41_setup_sequence(struct nfs4_session *session, tbl = &session->fc_slot_table; spin_lock(&tbl->slot_tbl_lock); - if (test_bit(NFS4CLNT_SESSION_DRAINING, &session->clp->cl_state)) { + if (test_bit(NFS4CLNT_SESSION_DRAINING, &session->clp->cl_state) && + !rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) { /* * The state manager will wait until the slot table is empty. * Schedule the reset thread diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 0e45075836b2..6674b28ddb66 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -135,9 +135,9 @@ static int nfs41_setup_state_renewal(struct nfs_client *clp) return status; } -static void nfs41_end_drain_session(struct nfs_client *clp, - struct nfs4_session *ses) +static void nfs4_end_drain_session(struct nfs_client *clp) { + struct nfs4_session *ses = clp->cl_session; int max_slots; if (test_and_clear_bit(NFS4CLNT_SESSION_DRAINING, &clp->cl_state)) { @@ -156,9 +156,9 @@ static void nfs41_end_drain_session(struct nfs_client *clp, } } -static int nfs41_begin_drain_session(struct nfs_client *clp, - struct nfs4_session *ses) +static int nfs4_begin_drain_session(struct nfs_client *clp) { + struct nfs4_session *ses = clp->cl_session; struct nfs4_slot_table *tbl = &ses->fc_slot_table; spin_lock(&tbl->slot_tbl_lock); @@ -176,16 +176,12 @@ int nfs41_init_clientid(struct nfs_client *clp, struct rpc_cred *cred) { int status; - status = nfs41_begin_drain_session(clp, clp->cl_session); - if (status != 0) - goto out; status = nfs4_proc_exchange_id(clp, cred); if (status != 0) goto out; status = nfs4_proc_create_session(clp); if (status != 0) goto out; - nfs41_end_drain_session(clp, clp->cl_session); nfs41_setup_state_renewal(clp); nfs_mark_client_ready(clp, NFS_CS_READY); out: @@ -1271,13 +1267,8 @@ void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags) static int nfs4_reset_session(struct nfs_client *clp) { - struct nfs4_session *ses = clp->cl_session; int status; - status = nfs41_begin_drain_session(clp, ses); - if (status != 0) - return status; - status = nfs4_proc_destroy_session(clp->cl_session); if (status && status != -NFS4ERR_BADSESSION && status != -NFS4ERR_DEADSESSION) { @@ -1293,19 +1284,18 @@ static int nfs4_reset_session(struct nfs_client *clp) out: /* * Let the state manager reestablish state - * without waking other tasks yet. */ - if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) { - /* Wake up the next rpc task */ - nfs41_end_drain_session(clp, ses); - if (status == 0) - nfs41_setup_state_renewal(clp); - } + if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) && + status == 0) + nfs41_setup_state_renewal(clp); + return status; } #else /* CONFIG_NFS_V4_1 */ static int nfs4_reset_session(struct nfs_client *clp) { return 0; } +static int nfs4_begin_drain_session(struct nfs_client *clp) { return 0; } +static int nfs4_end_drain_session(struct nfs_client *clp) { return 0; } #endif /* CONFIG_NFS_V4_1 */ /* Set NFS4CLNT_LEASE_EXPIRED for all v4.0 errors and for recoverable errors @@ -1337,6 +1327,7 @@ static void nfs4_state_manager(struct nfs_client *clp) for(;;) { if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) { /* We're going to have to re-establish a clientid */ + nfs4_begin_drain_session(clp); status = nfs4_reclaim_lease(clp); if (status) { nfs4_set_lease_expired(clp, status); @@ -1363,6 +1354,7 @@ static void nfs4_state_manager(struct nfs_client *clp) /* Initialize or reset the session */ if (test_and_clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) && nfs4_has_session(clp)) { + nfs4_begin_drain_session(clp); status = nfs4_reset_session(clp); if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) continue; @@ -1396,6 +1388,7 @@ static void nfs4_state_manager(struct nfs_client *clp) goto out_error; } + nfs4_end_drain_session(clp); if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) { nfs_client_return_marked_delegations(clp); continue; @@ -1412,6 +1405,7 @@ static void nfs4_state_manager(struct nfs_client *clp) out_error: printk(KERN_WARNING "Error: state manager failed on NFSv4 server %s" " with error %d\n", clp->cl_hostname, -status); + nfs4_end_drain_session(clp); nfs4_clear_state_manager_bit(clp); } From a5523b84c40d34d2c30b45096fbb099b98e4b5a3 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Mon, 14 Dec 2009 15:46:16 -0500 Subject: [PATCH 10/13] nfs41: do not zero seqid portion of stateid on close Remove code left over from a previous minorversion draft. which specified zeroing seqid portions of stateid's. Signed-off-by: Andy Adamson Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 4d6560a73949..bd4555fbdf25 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1931,8 +1931,6 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait) calldata->state = state; calldata->arg.fh = NFS_FH(state->inode); calldata->arg.stateid = &state->open_stateid; - if (nfs4_has_session(server->nfs_client)) - memset(calldata->arg.stateid->data, 0, 4); /* clear seqid */ /* Serialization for the sequence id */ calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid); if (calldata->arg.seqid == NULL) From 68bf05efb7facbcf4a7b8d6b48a0800a90895511 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Tue, 15 Dec 2009 12:55:02 -0500 Subject: [PATCH 11/13] nfs41: fix session fore channel negotiation If the rsize or wsize is not set on the mount command, negotiate the highest supported rsize and wsize in session creation. Fixes a bug where the client negotiated nfs41_maxwrite_overhead as ca_maxrequestsize and nfs41_maxread_overhead as ca_maxresponsesize resulting in NFS4ERR_REQ_TOO_BIG errors on writes. Signed-off-by: Andy Adamson Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index bd4555fbdf25..019a009e73a0 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4935,14 +4935,22 @@ int nfs4_init_session(struct nfs_server *server) { struct nfs_client *clp = server->nfs_client; struct nfs4_session *session; + unsigned int rsize, wsize; int ret; if (!nfs4_has_session(clp)) return 0; + rsize = server->rsize; + if (rsize == 0) + rsize = NFS_MAX_FILE_IO_SIZE; + wsize = server->wsize; + if (wsize == 0) + wsize = NFS_MAX_FILE_IO_SIZE; + session = clp->cl_session; - session->fc_attrs.max_rqst_sz = server->wsize + nfs41_maxwrite_overhead; - session->fc_attrs.max_resp_sz = server->rsize + nfs41_maxread_overhead; + session->fc_attrs.max_rqst_sz = wsize + nfs41_maxwrite_overhead; + session->fc_attrs.max_resp_sz = rsize + nfs41_maxread_overhead; ret = nfs4_recover_expired_lease(server); if (!ret) From 72211dbe727f7c1451aa5adfcbd1197b090eb276 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 15 Dec 2009 14:47:36 -0500 Subject: [PATCH 12/13] NFSv4: Release the sequence id before restarting a CLOSE rpc call If the CLOSE or OPEN_DOWNGRADE call triggers a state recovery, and has to be resent, then we must release the seqid. Otherwise the open recovery will wait for the close to finish, which causes a deadlock. This is mainly a NFSv4.1 problem, although it can theoretically happen with NFSv4.0 too, in a OPEN_DOWNGRADE situation. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 1 + fs/nfs/nfs4proc.c | 7 +++---- fs/nfs/nfs4state.c | 9 +++++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index e8791d5a2f7a..865265bdca03 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -286,6 +286,7 @@ extern struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter); extern int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task); extern void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid); extern void nfs_increment_lock_seqid(int status, struct nfs_seqid *seqid); +extern void nfs_release_seqid(struct nfs_seqid *seqid); extern void nfs_free_seqid(struct nfs_seqid *seqid); extern const nfs4_stateid zero_stateid; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 019a009e73a0..198d51d17c13 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1835,11 +1835,10 @@ static void nfs4_close_done(struct rpc_task *task, void *data) if (calldata->arg.fmode == 0) break; default: - if (nfs4_async_handle_error(task, server, state) == -EAGAIN) { - nfs_restart_rpc(task, server->nfs_client); - return; - } + if (nfs4_async_handle_error(task, server, state) == -EAGAIN) + rpc_restart_call_prepare(task); } + nfs_release_seqid(calldata->arg.seqid); nfs_refresh_inode(calldata->inode, calldata->res.fattr); } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 6674b28ddb66..18e8b26878c5 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -765,16 +765,21 @@ struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter) return new; } -void nfs_free_seqid(struct nfs_seqid *seqid) +void nfs_release_seqid(struct nfs_seqid *seqid) { if (!list_empty(&seqid->list)) { struct rpc_sequence *sequence = seqid->sequence->sequence; spin_lock(&sequence->lock); - list_del(&seqid->list); + list_del_init(&seqid->list); spin_unlock(&sequence->lock); rpc_wake_up(&sequence->wait); } +} + +void nfs_free_seqid(struct nfs_seqid *seqid) +{ + nfs_release_seqid(seqid); kfree(seqid); } From 380454126f1357db9270f9d1ca05dfe1a6e4ad47 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 15 Dec 2009 17:36:57 -0500 Subject: [PATCH 13/13] NFSv4: Fix a regression in the NFSv4 state manager Commit 5601a00d671fe89f9b087513244abcd08ad67e7d (nfs: run state manager in privileged mode) introduces a regression in the NFSv4 code when compiled with CONFIG_NFS_V4_1. The calls to nfs4_end_drain_session() from the main loop in nfs4_state_manager() Oops due to the lack of an NFSv4.1 session when running NFSv4.0. The fix is to move those two calls back into nfs41_init_clientid() and nfs4_reset_session(). The calls to nfs4_end_drain_session() that remain inside nfs4_state_manager() are safe, since the NFSv4.0 code will never set the NFS4CLNT_SESSION_DRAINING bit. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4state.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 18e8b26878c5..6d263ed79e92 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -176,6 +176,7 @@ int nfs41_init_clientid(struct nfs_client *clp, struct rpc_cred *cred) { int status; + nfs4_begin_drain_session(clp); status = nfs4_proc_exchange_id(clp, cred); if (status != 0) goto out; @@ -1274,6 +1275,7 @@ static int nfs4_reset_session(struct nfs_client *clp) { int status; + nfs4_begin_drain_session(clp); status = nfs4_proc_destroy_session(clp->cl_session); if (status && status != -NFS4ERR_BADSESSION && status != -NFS4ERR_DEADSESSION) { @@ -1299,7 +1301,6 @@ static int nfs4_reset_session(struct nfs_client *clp) #else /* CONFIG_NFS_V4_1 */ static int nfs4_reset_session(struct nfs_client *clp) { return 0; } -static int nfs4_begin_drain_session(struct nfs_client *clp) { return 0; } static int nfs4_end_drain_session(struct nfs_client *clp) { return 0; } #endif /* CONFIG_NFS_V4_1 */ @@ -1332,7 +1333,6 @@ static void nfs4_state_manager(struct nfs_client *clp) for(;;) { if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) { /* We're going to have to re-establish a clientid */ - nfs4_begin_drain_session(clp); status = nfs4_reclaim_lease(clp); if (status) { nfs4_set_lease_expired(clp, status); @@ -1359,7 +1359,6 @@ static void nfs4_state_manager(struct nfs_client *clp) /* Initialize or reset the session */ if (test_and_clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) && nfs4_has_session(clp)) { - nfs4_begin_drain_session(clp); status = nfs4_reset_session(clp); if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) continue;