diff options
Diffstat (limited to 'net/sunrpc/sched.c')
-rw-r--r-- | net/sunrpc/sched.c | 106 |
1 files changed, 78 insertions, 28 deletions
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 6e14bb287..765dc05fc 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -16,6 +16,7 @@ #include <linux/unistd.h> #include <linux/smp.h> #include <linux/smp_lock.h> + #include <linux/sunrpc/clnt.h> #ifdef RPC_DEBUG @@ -45,6 +46,11 @@ static struct rpc_wait_queue schedq = RPC_INIT_WAITQ("schedq"); static struct rpc_wait_queue childq = RPC_INIT_WAITQ("childq"); /* + * RPC tasks sit here while waiting for conditions to improve. + */ +static struct rpc_wait_queue delay_queue = RPC_INIT_WAITQ("delayq"); + +/* * All RPC tasks are linked into this list */ static struct rpc_task * all_tasks = NULL; @@ -92,7 +98,8 @@ rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task) } /* - * Remove request from queue + * Remove request from queue. + * Note: must be called with interrupts disabled. */ void rpc_remove_wait_queue(struct rpc_task *task) @@ -149,6 +156,9 @@ rpc_del_timer(struct rpc_task *task) /* * Make an RPC task runnable. + * + * Note: If the task is ASYNC, this must be called with + * interrupts disabled to protect the wait queue operation. */ static inline void rpc_make_runnable(struct rpc_task *task) @@ -313,8 +323,6 @@ static void __rpc_atrun(struct rpc_task *); void rpc_delay(struct rpc_task *task, unsigned long delay) { - static struct rpc_wait_queue delay_queue; - task->tk_timeout = delay; rpc_sleep_on(&delay_queue, task, NULL, __rpc_atrun); } @@ -388,12 +396,14 @@ __rpc_execute(struct rpc_task *task) /* sync task: sleep here */ dprintk("RPC: %4d sync task going to sleep\n", task->tk_pid); + if (current->pid == rpciod_pid) + printk("RPC: rpciod waiting on sync task!\n"); current->timeout = 0; sleep_on(&task->tk_wait); /* When the task received a signal, remove from * any queues etc, and make runnable again. */ - if (signalled()) + if (0 && signalled()) __rpc_wake_up(task); dprintk("RPC: %4d sync task resuming\n", @@ -433,10 +443,15 @@ rpc_execute(struct rpc_task *task) static int executing = 0; int incr = RPC_IS_ASYNC(task)? 1 : 0; - if (incr && (executing || rpc_inhibit)) { - printk("RPC: rpc_execute called recursively!\n"); - return; + if (incr) { + if (rpc_inhibit) { + printk("RPC: execution inhibited!\n"); + return; + } + if (executing) + printk("RPC: %d tasks executed\n", executing); } + executing += incr; __rpc_execute(task); executing -= incr; @@ -519,6 +534,7 @@ rpc_allocate(unsigned int flags, unsigned int size) if (flags & RPC_TASK_ASYNC) return NULL; current->timeout = jiffies + (HZ >> 4); + current->state = TASK_INTERRUPTIBLE; schedule(); } while (!signalled()); @@ -684,20 +700,27 @@ rpc_new_child(struct rpc_clnt *clnt, struct rpc_task *parent) { struct rpc_task *task; - if (!(task = rpc_new_task(clnt, NULL, RPC_TASK_ASYNC|RPC_TASK_CHILD))) { - parent->tk_status = -ENOMEM; - return NULL; - } + task = rpc_new_task(clnt, NULL, RPC_TASK_ASYNC | RPC_TASK_CHILD); + if (!task) + goto fail; task->tk_exit = rpc_child_exit; task->tk_calldata = parent; - return task; + +fail: + parent->tk_status = -ENOMEM; + return NULL; } void rpc_run_child(struct rpc_task *task, struct rpc_task *child, rpc_action func) { + unsigned long oldflags; + + save_flags(oldflags); cli(); rpc_make_runnable(child); + restore_flags(oldflags); + /* N.B. Is it possible for the child to have already finished? */ rpc_sleep_on(&childq, task, func, NULL); } @@ -711,6 +734,7 @@ rpc_killall_tasks(struct rpc_clnt *clnt) struct rpc_task **q, *rovr; dprintk("RPC: killing all tasks for client %p\n", clnt); + /* N.B. Why bother to inhibit? Nothing blocks here ... */ rpc_inhibit++; for (q = &all_tasks; (rovr = *q); q = &rovr->tk_next_task) { if (!clnt || rovr->tk_client == clnt) { @@ -792,29 +816,21 @@ static void rpciod_killall(void) { unsigned long flags; - sigset_t old_set; - - /* FIXME: What had been going on before was saving and restoring - current->signal. This as opposed to blocking signals? Do we - still need them to wake up out of schedule? In any case it - isn't playing nice and a better way should be found. */ - - spin_lock_irqsave(¤t->sigmask_lock, flags); - old_set = current->blocked; - sigfillset(¤t->blocked); - recalc_sigpending(current); - spin_unlock_irqrestore(¤t->sigmask_lock, flags); while (all_tasks) { + current->sigpending = 0; rpc_killall_tasks(NULL); __rpc_schedule(); - current->timeout = jiffies + HZ / 100; - need_resched = 1; - schedule(); + if (all_tasks) { +printk("rpciod_killall: waiting for tasks to exit\n"); + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + current->timeout = 0; + } } spin_lock_irqsave(¤t->sigmask_lock, flags); - current->blocked = old_set; recalc_sigpending(current); spin_unlock_irqrestore(¤t->sigmask_lock, flags); } @@ -901,3 +917,37 @@ out: up(&rpciod_sema); MOD_DEC_USE_COUNT; } + +#ifdef RPC_DEBUG +#include <linux/nfs_fs.h> +void rpc_show_tasks(void) +{ + struct rpc_task *t = all_tasks, *next; + struct nfs_wreq *wreq; + + if (!t) + return; + printk("-pid- proc flgs status -client- -prog- --rqstp- -timeout " + "-rpcwait -action- --exit--\n"); + for (; t; t = next) { + next = t->tk_next_task; + printk("%05d %04d %04x %06d %8p %6d %8p %08ld %8s %8p %8p\n", + t->tk_pid, t->tk_proc, t->tk_flags, t->tk_status, + t->tk_client, t->tk_client->cl_prog, + t->tk_rqstp, t->tk_timeout, + t->tk_rpcwait ? rpc_qname(t->tk_rpcwait) : " <NULL> ", + t->tk_action, t->tk_exit); + + if (!(t->tk_flags & RPC_TASK_NFSWRITE)) + continue; + /* NFS write requests */ + wreq = (struct nfs_wreq *) t->tk_calldata; + printk(" NFS: flgs=%08x, pid=%d, pg=%p, off=(%d, %d)\n", + wreq->wb_flags, wreq->wb_pid, wreq->wb_page, + wreq->wb_offset, wreq->wb_bytes); + printk(" name=%s/%s\n", + wreq->wb_dentry->d_parent->d_name.name, + wreq->wb_dentry->d_name.name); + } +} +#endif |