Commit e6b3c4db authored by Trond Myklebust's avatar Trond Myklebust

Fix a second potential rpc_wakeup race...

Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent cc4dc59e
...@@ -636,7 +636,7 @@ static int _nfs4_proc_open_confirm(struct nfs4_opendata *data) ...@@ -636,7 +636,7 @@ static int _nfs4_proc_open_confirm(struct nfs4_opendata *data)
smp_wmb(); smp_wmb();
} else } else
status = data->rpc_status; status = data->rpc_status;
rpc_release_task(task); rpc_put_task(task);
return status; return status;
} }
...@@ -742,7 +742,7 @@ static int _nfs4_proc_open(struct nfs4_opendata *data) ...@@ -742,7 +742,7 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
smp_wmb(); smp_wmb();
} else } else
status = data->rpc_status; status = data->rpc_status;
rpc_release_task(task); rpc_put_task(task);
if (status != 0) if (status != 0)
return status; return status;
...@@ -3067,7 +3067,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co ...@@ -3067,7 +3067,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
if (status == 0) if (status == 0)
nfs_post_op_update_inode(inode, &data->fattr); nfs_post_op_update_inode(inode, &data->fattr);
} }
rpc_release_task(task); rpc_put_task(task);
return status; return status;
} }
...@@ -3314,7 +3314,7 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock * ...@@ -3314,7 +3314,7 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
if (IS_ERR(task)) if (IS_ERR(task))
goto out; goto out;
status = nfs4_wait_for_completion_rpc_task(task); status = nfs4_wait_for_completion_rpc_task(task);
rpc_release_task(task); rpc_put_task(task);
out: out:
return status; return status;
} }
...@@ -3430,7 +3430,7 @@ static void nfs4_lock_release(void *calldata) ...@@ -3430,7 +3430,7 @@ static void nfs4_lock_release(void *calldata)
task = nfs4_do_unlck(&data->fl, data->ctx, data->lsp, task = nfs4_do_unlck(&data->fl, data->ctx, data->lsp,
data->arg.lock_seqid); data->arg.lock_seqid);
if (!IS_ERR(task)) if (!IS_ERR(task))
rpc_release_task(task); rpc_put_task(task);
dprintk("%s: cancelling lock!\n", __FUNCTION__); dprintk("%s: cancelling lock!\n", __FUNCTION__);
} else } else
nfs_free_seqid(data->arg.lock_seqid); nfs_free_seqid(data->arg.lock_seqid);
...@@ -3472,7 +3472,7 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f ...@@ -3472,7 +3472,7 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
ret = -EAGAIN; ret = -EAGAIN;
} else } else
data->cancelled = 1; data->cancelled = 1;
rpc_release_task(task); rpc_put_task(task);
dprintk("%s: done, ret = %d!\n", __FUNCTION__, ret); dprintk("%s: done, ret = %d!\n", __FUNCTION__, ret);
return ret; return ret;
} }
......
...@@ -178,13 +178,6 @@ struct rpc_call_ops { ...@@ -178,13 +178,6 @@ struct rpc_call_ops {
} while (0) } while (0)
#define RPC_IS_ACTIVATED(t) (test_bit(RPC_TASK_ACTIVE, &(t)->tk_runstate)) #define RPC_IS_ACTIVATED(t) (test_bit(RPC_TASK_ACTIVE, &(t)->tk_runstate))
#define rpc_set_active(t) (set_bit(RPC_TASK_ACTIVE, &(t)->tk_runstate))
#define rpc_clear_active(t) \
do { \
smp_mb__before_clear_bit(); \
clear_bit(RPC_TASK_ACTIVE, &(t)->tk_runstate); \
smp_mb__after_clear_bit(); \
} while(0)
/* /*
* Task priorities. * Task priorities.
...@@ -254,6 +247,7 @@ struct rpc_task *rpc_run_task(struct rpc_clnt *clnt, int flags, ...@@ -254,6 +247,7 @@ struct rpc_task *rpc_run_task(struct rpc_clnt *clnt, int flags,
void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt,
int flags, const struct rpc_call_ops *ops, int flags, const struct rpc_call_ops *ops,
void *data); void *data);
void rpc_put_task(struct rpc_task *);
void rpc_release_task(struct rpc_task *); void rpc_release_task(struct rpc_task *);
void rpc_exit_task(struct rpc_task *); void rpc_exit_task(struct rpc_task *);
void rpc_killall_tasks(struct rpc_clnt *); void rpc_killall_tasks(struct rpc_clnt *);
......
...@@ -466,10 +466,9 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) ...@@ -466,10 +466,9 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
BUG_ON(flags & RPC_TASK_ASYNC); BUG_ON(flags & RPC_TASK_ASYNC);
status = -ENOMEM;
task = rpc_new_task(clnt, flags, &rpc_default_ops, NULL); task = rpc_new_task(clnt, flags, &rpc_default_ops, NULL);
if (task == NULL) if (task == NULL)
goto out; return -ENOMEM;
/* Mask signals on RPC calls _and_ GSS_AUTH upcalls */ /* Mask signals on RPC calls _and_ GSS_AUTH upcalls */
rpc_task_sigmask(task, &oldset); rpc_task_sigmask(task, &oldset);
...@@ -478,15 +477,17 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) ...@@ -478,15 +477,17 @@ int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
/* Set up the call info struct and execute the task */ /* Set up the call info struct and execute the task */
status = task->tk_status; status = task->tk_status;
if (status == 0) { if (status != 0) {
atomic_inc(&task->tk_count); rpc_release_task(task);
status = rpc_execute(task); goto out;
if (status == 0)
status = task->tk_status;
} }
rpc_restore_sigmask(&oldset); atomic_inc(&task->tk_count);
rpc_release_task(task); status = rpc_execute(task);
if (status == 0)
status = task->tk_status;
rpc_put_task(task);
out: out:
rpc_restore_sigmask(&oldset);
return status; return status;
} }
......
...@@ -134,7 +134,7 @@ void rpc_getport(struct rpc_task *task) ...@@ -134,7 +134,7 @@ void rpc_getport(struct rpc_task *task)
child = rpc_run_task(pmap_clnt, RPC_TASK_ASYNC, &pmap_getport_ops, map); child = rpc_run_task(pmap_clnt, RPC_TASK_ASYNC, &pmap_getport_ops, map);
if (IS_ERR(child)) if (IS_ERR(child))
goto bailout; goto bailout;
rpc_release_task(child); rpc_put_task(child);
task->tk_xprt->stat.bind_count++; task->tk_xprt->stat.bind_count++;
return; return;
......
...@@ -266,12 +266,28 @@ static int rpc_wait_bit_interruptible(void *word) ...@@ -266,12 +266,28 @@ static int rpc_wait_bit_interruptible(void *word)
return 0; return 0;
} }
static void rpc_set_active(struct rpc_task *task)
{
if (test_and_set_bit(RPC_TASK_ACTIVE, &task->tk_runstate) != 0)
return;
spin_lock(&rpc_sched_lock);
#ifdef RPC_DEBUG
task->tk_magic = RPC_TASK_MAGIC_ID;
task->tk_pid = rpc_task_id++;
#endif
/* Add to global list of all tasks */
list_add_tail(&task->tk_task, &all_tasks);
spin_unlock(&rpc_sched_lock);
}
/* /*
* Mark an RPC call as having completed by clearing the 'active' bit * Mark an RPC call as having completed by clearing the 'active' bit
*/ */
static inline void rpc_mark_complete_task(struct rpc_task *task) static void rpc_mark_complete_task(struct rpc_task *task)
{ {
rpc_clear_active(task); smp_mb__before_clear_bit();
clear_bit(RPC_TASK_ACTIVE, &task->tk_runstate);
smp_mb__after_clear_bit();
wake_up_bit(&task->tk_runstate, RPC_TASK_ACTIVE); wake_up_bit(&task->tk_runstate, RPC_TASK_ACTIVE);
} }
...@@ -335,9 +351,6 @@ static void __rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, ...@@ -335,9 +351,6 @@ static void __rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task,
return; return;
} }
/* Mark the task as being activated if so needed */
rpc_set_active(task);
__rpc_add_wait_queue(q, task); __rpc_add_wait_queue(q, task);
BUG_ON(task->tk_callback != NULL); BUG_ON(task->tk_callback != NULL);
...@@ -348,6 +361,9 @@ static void __rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, ...@@ -348,6 +361,9 @@ static void __rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task,
void rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, void rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task,
rpc_action action, rpc_action timer) rpc_action action, rpc_action timer)
{ {
/* Mark the task as being activated if so needed */
rpc_set_active(task);
/* /*
* Protect the queue operations. * Protect the queue operations.
*/ */
...@@ -673,8 +689,6 @@ static int __rpc_execute(struct rpc_task *task) ...@@ -673,8 +689,6 @@ static int __rpc_execute(struct rpc_task *task)
} }
dprintk("RPC: %4d, return %d, status %d\n", task->tk_pid, status, task->tk_status); dprintk("RPC: %4d, return %d, status %d\n", task->tk_pid, status, task->tk_status);
/* Wake up anyone who is waiting for task completion */
rpc_mark_complete_task(task);
/* Release all resources associated with the task */ /* Release all resources associated with the task */
rpc_release_task(task); rpc_release_task(task);
return status; return status;
...@@ -788,15 +802,6 @@ void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, int flags, cons ...@@ -788,15 +802,6 @@ void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, int flags, cons
task->tk_flags |= RPC_TASK_NOINTR; task->tk_flags |= RPC_TASK_NOINTR;
} }
#ifdef RPC_DEBUG
task->tk_magic = RPC_TASK_MAGIC_ID;
task->tk_pid = rpc_task_id++;
#endif
/* Add to global list of all tasks */
spin_lock(&rpc_sched_lock);
list_add_tail(&task->tk_task, &all_tasks);
spin_unlock(&rpc_sched_lock);
BUG_ON(task->tk_ops == NULL); BUG_ON(task->tk_ops == NULL);
/* starting timestamp */ /* starting timestamp */
...@@ -849,16 +854,35 @@ struct rpc_task *rpc_new_task(struct rpc_clnt *clnt, int flags, const struct rpc ...@@ -849,16 +854,35 @@ struct rpc_task *rpc_new_task(struct rpc_clnt *clnt, int flags, const struct rpc
goto out; goto out;
} }
void rpc_release_task(struct rpc_task *task)
void rpc_put_task(struct rpc_task *task)
{ {
const struct rpc_call_ops *tk_ops = task->tk_ops; const struct rpc_call_ops *tk_ops = task->tk_ops;
void *calldata = task->tk_calldata; void *calldata = task->tk_calldata;
if (!atomic_dec_and_test(&task->tk_count))
return;
/* Release resources */
if (task->tk_rqstp)
xprt_release(task);
if (task->tk_msg.rpc_cred)
rpcauth_unbindcred(task);
if (task->tk_client) {
rpc_release_client(task->tk_client);
task->tk_client = NULL;
}
if (task->tk_flags & RPC_TASK_DYNAMIC)
rpc_free_task(task);
if (tk_ops->rpc_release)
tk_ops->rpc_release(calldata);
}
EXPORT_SYMBOL(rpc_put_task);
void rpc_release_task(struct rpc_task *task)
{
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
BUG_ON(task->tk_magic != RPC_TASK_MAGIC_ID); BUG_ON(task->tk_magic != RPC_TASK_MAGIC_ID);
#endif #endif
if (!atomic_dec_and_test(&task->tk_count))
return;
dprintk("RPC: %4d release task\n", task->tk_pid); dprintk("RPC: %4d release task\n", task->tk_pid);
/* Remove from global task list */ /* Remove from global task list */
...@@ -871,23 +895,13 @@ void rpc_release_task(struct rpc_task *task) ...@@ -871,23 +895,13 @@ void rpc_release_task(struct rpc_task *task)
/* Synchronously delete any running timer */ /* Synchronously delete any running timer */
rpc_delete_timer(task); rpc_delete_timer(task);
/* Release resources */
if (task->tk_rqstp)
xprt_release(task);
if (task->tk_msg.rpc_cred)
rpcauth_unbindcred(task);
if (task->tk_client) {
rpc_release_client(task->tk_client);
task->tk_client = NULL;
}
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
task->tk_magic = 0; task->tk_magic = 0;
#endif #endif
if (task->tk_flags & RPC_TASK_DYNAMIC) /* Wake up anyone who is waiting for task completion */
rpc_free_task(task); rpc_mark_complete_task(task);
if (tk_ops->rpc_release)
tk_ops->rpc_release(calldata); rpc_put_task(task);
} }
/** /**
......
...@@ -33,7 +33,6 @@ EXPORT_SYMBOL(rpciod_down); ...@@ -33,7 +33,6 @@ EXPORT_SYMBOL(rpciod_down);
EXPORT_SYMBOL(rpciod_up); EXPORT_SYMBOL(rpciod_up);
EXPORT_SYMBOL(rpc_new_task); EXPORT_SYMBOL(rpc_new_task);
EXPORT_SYMBOL(rpc_wake_up_status); EXPORT_SYMBOL(rpc_wake_up_status);
EXPORT_SYMBOL(rpc_release_task);
/* RPC client functions */ /* RPC client functions */
EXPORT_SYMBOL(rpc_clone_client); EXPORT_SYMBOL(rpc_clone_client);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment