diff -u --recursive --new-file linux-2.5.73-09-xprt/include/linux/sunrpc/clnt.h linux-2.5.73-10-mem/include/linux/sunrpc/clnt.h --- linux-2.5.73-09-xprt/include/linux/sunrpc/clnt.h 2003-06-28 15:10:15.000000000 +0200 +++ linux-2.5.73-10-mem/include/linux/sunrpc/clnt.h 2003-06-29 16:49:49.000000000 +0200 @@ -35,6 +35,7 @@ */ struct rpc_clnt { atomic_t cl_users; /* number of references */ + atomic_t cl_active; /* number of active calls */ struct rpc_xprt * cl_xprt; /* transport */ struct rpc_procinfo * cl_procinfo; /* procedure info */ u32 cl_maxproc; /* max procedure number */ @@ -60,6 +61,7 @@ struct rpc_portmap cl_pmap; /* port mapping */ struct rpc_wait_queue cl_bindwait; /* waiting on getport() */ + wait_queue_head_t cl_waitq; /* wait queue */ int cl_nodelen; /* nodename length */ char cl_nodename[UNX_MAXNODENAME]; @@ -130,6 +132,15 @@ void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset); void rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int); void rpc_set_timeout(struct rpc_clnt *, struct rpc_timeout *); +int rpc_congestion_wait(struct rpc_clnt *); + +static inline void rpc_mark_active(struct rpc_task *task) +{ + struct rpc_clnt *clnt = task->tk_client; + task->tk_active = 1; + if (clnt) + atomic_inc(&clnt->cl_active); +} static inline int rpc_need_rebind(struct rpc_clnt *clnt) { diff -u --recursive --new-file linux-2.5.73-09-xprt/kernel/ksyms.c linux-2.5.73-10-mem/kernel/ksyms.c --- linux-2.5.73-09-xprt/kernel/ksyms.c 2003-06-24 23:26:24.000000000 +0200 +++ linux-2.5.73-10-mem/kernel/ksyms.c 2003-06-28 15:10:17.000000000 +0200 @@ -457,6 +457,7 @@ EXPORT_SYMBOL(interruptible_sleep_on); EXPORT_SYMBOL(interruptible_sleep_on_timeout); EXPORT_SYMBOL(schedule); +EXPORT_SYMBOL(io_schedule); #ifdef CONFIG_PREEMPT EXPORT_SYMBOL(preempt_schedule); #endif diff -u --recursive --new-file linux-2.5.73-09-xprt/net/sunrpc/clnt.c linux-2.5.73-10-mem/net/sunrpc/clnt.c --- linux-2.5.73-09-xprt/net/sunrpc/clnt.c 2003-06-29 21:14:38.000000000 +0200 +++ linux-2.5.73-10-mem/net/sunrpc/clnt.c 2003-06-29 21:17:47.000000000 +0200 @@ -125,6 +125,7 @@ clnt->cl_pmap.pm_prot = proto; clnt->cl_stats = program->stats; INIT_RPC_WAITQ(&clnt->cl_bindwait, "bindwait"); + init_waitqueue_head(&clnt->cl_waitq); rwlock_init(&clnt->cl_rwlock); if (!clnt->cl_addr.sin_port) @@ -396,6 +397,37 @@ } /* + * Throttle the number of active RPC requests + */ +int +rpc_congestion_wait(struct rpc_clnt *clnt) +{ + int ret = 0; + DECLARE_WAITQUEUE(wait, current); + + if (atomic_read(&clnt->cl_active) < RPC_MAXCONG) + goto out; + add_wait_queue(&clnt->cl_waitq, &wait); + for (;;) { + if (clnt->cl_intr) + set_current_state(TASK_INTERRUPTIBLE); + else + set_current_state(TASK_UNINTERRUPTIBLE); + if (atomic_read(&clnt->cl_active) < RPC_MAXCONG) + break; + if (clnt->cl_intr && signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + io_schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&clnt->cl_waitq, &wait); +out: + return ret; +} + +/* * Restart an (async) RPC call. Usually called from within the * exit handler. */ diff -u --recursive --new-file linux-2.5.73-09-xprt/net/sunrpc/sched.c linux-2.5.73-10-mem/net/sunrpc/sched.c --- linux-2.5.73-09-xprt/net/sunrpc/sched.c 2003-06-20 22:16:26.000000000 +0200 +++ linux-2.5.73-10-mem/net/sunrpc/sched.c 2003-06-29 16:43:14.000000000 +0200 @@ -257,13 +257,11 @@ return; } rpc_clear_sleeping(task); - if (waitqueue_active(&rpciod_idle)) - wake_up(&rpciod_idle); + wake_up(&rpciod_idle); } } else { rpc_clear_sleeping(task); - if (waitqueue_active(&task->tk_wait)) - wake_up(&task->tk_wait); + wake_up(&task->tk_wait); } } @@ -276,7 +274,7 @@ /* Don't run a child twice! */ if (RPC_IS_ACTIVATED(task)) return; - task->tk_active = 1; + rpc_mark_active(task); rpc_set_sleeping(task); rpc_make_runnable(task); } @@ -289,8 +287,7 @@ { if(rpciod_pid==0) printk(KERN_ERR "rpciod: wot no daemon?\n"); - if (waitqueue_active(&rpciod_idle)) - wake_up(&rpciod_idle); + wake_up(&rpciod_idle); } /* @@ -315,7 +312,7 @@ /* Mark the task as being activated if so needed */ if (!RPC_IS_ACTIVATED(task)) { - task->tk_active = 1; + rpc_mark_active(task); rpc_set_sleeping(task); } @@ -488,7 +485,8 @@ static int __rpc_execute(struct rpc_task *task) { - int status = 0; + int interruptible = task->tk_client->cl_intr; + int status = 0; dprintk("RPC: %4d rpc_execute flgs %x\n", task->tk_pid, task->tk_flags); @@ -547,14 +545,24 @@ } spin_unlock_bh(&rpc_queue_lock); - while (RPC_IS_SLEEPING(task)) { + if (RPC_IS_SLEEPING(task)) { + DEFINE_WAIT(wait); + /* sync task: sleep here */ dprintk("RPC: %4d sync task going to sleep\n", task->tk_pid); if (current->pid == rpciod_pid) printk(KERN_ERR "RPC: rpciod waiting on sync task!\n"); - __wait_event(task->tk_wait, !RPC_IS_SLEEPING(task)); + prepare_to_wait(&task->tk_wait, &wait, + interruptible ? TASK_INTERRUPTIBLE : + TASK_UNINTERRUPTIBLE); + if (likely(RPC_IS_SLEEPING(task))) { + if (likely(!(signalled() && interruptible))) + io_schedule(); + } + finish_wait(&task->tk_wait, &wait); + dprintk("RPC: %4d sync task resuming\n", task->tk_pid); /* @@ -563,7 +571,7 @@ * clean up after sleeping on some queue, we don't * break the loop here, but go around once more. */ - if (task->tk_client->cl_intr && signalled()) { + if (unlikely(signalled() && interruptible)) { dprintk("RPC: %4d got signal\n", task->tk_pid); task->tk_flags |= RPC_TASK_KILLED; rpc_exit(task, -ERESTARTSYS); @@ -620,7 +628,12 @@ goto out_err; } - task->tk_active = 1; + if (task->tk_client) { + status = rpc_congestion_wait(task->tk_client); + if (status < 0) + goto out_release; + } + rpc_mark_active(task); rpc_set_running(task); return __rpc_execute(task); out_release: @@ -818,8 +831,6 @@ /* Remove from any wait queue we're still on */ __rpc_remove_wait_queue(task); - task->tk_active = 0; - spin_unlock_bh(&rpc_queue_lock); /* Synchronously delete any running timer */ @@ -832,6 +843,10 @@ rpcauth_unbindcred(task); rpc_free(task); if (task->tk_client) { + if (task->tk_active) { + atomic_dec(&task->tk_client->cl_active); + wake_up(&task->tk_client->cl_waitq); + } rpc_release_client(task->tk_client); task->tk_client = NULL; } @@ -979,8 +994,20 @@ } if (!rpciod_task_pending()) { + DEFINE_WAIT(wait); + dprintk("RPC: rpciod back to sleep\n"); - wait_event_interruptible(rpciod_idle, rpciod_task_pending()); + + prepare_to_wait(&rpciod_idle, &wait, TASK_INTERRUPTIBLE); + spin_lock_bh(&rpc_queue_lock); + if (likely(!rpciod_task_pending())) { + spin_unlock_bh(&rpc_queue_lock); + if (likely(!signalled())) + schedule(); + } else + spin_unlock_bh(&rpc_queue_lock); + finish_wait(&rpciod_idle, &wait); + dprintk("RPC: switch to rpciod\n"); rounds = 0; }