SUNRPC: Add a timer function to wait queues.
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 18 Jul 2007 20:18:52 +0000 (16:18 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 29 Feb 2008 07:21:59 +0000 (23:21 -0800)
This is designed to replace the timeout timer in the individual rpc_tasks.
By putting the timer function in the wait queue, we will eventually be able
to reduce the total number of timers in use by the RPC subsystem.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
include/linux/sunrpc/sched.h
net/sunrpc/sched.c

index d39729e2b893d71c5a71267899c7cbd15617e351..7751d3a0549705eed214a7e272a74c30cdd439fd 100644 (file)
@@ -33,6 +33,8 @@ struct rpc_wait_queue;
 struct rpc_wait {
        struct list_head        list;           /* wait queue links */
        struct list_head        links;          /* Links to related tasks */
+       struct list_head        timer_list;     /* Timer list */
+       unsigned long           expires;
 };
 
 /*
@@ -191,6 +193,12 @@ struct rpc_task_setup {
 #define RPC_PRIORITY_HIGH      (1)
 #define RPC_NR_PRIORITY                (1 + RPC_PRIORITY_HIGH - RPC_PRIORITY_LOW)
 
+struct rpc_timer {
+       struct timer_list timer;
+       struct list_head list;
+       unsigned long expires;
+};
+
 /*
  * RPC synchronization objects
  */
@@ -203,6 +211,7 @@ struct rpc_wait_queue {
        unsigned char           count;                  /* # task groups remaining serviced so far */
        unsigned char           nr;                     /* # tasks remaining for cookie */
        unsigned short          qlen;                   /* total # tasks waiting in queue */
+       struct rpc_timer        timer_list;
 #ifdef RPC_DEBUG
        const char *            name;
 #endif
index 86aa897e7b08788dcb1a2c1ed22fa3610f645b8e..29b1c1441f4de0b653d9a8eded7337d3a0bd9dd4 100644 (file)
@@ -40,6 +40,7 @@ static mempool_t      *rpc_buffer_mempool __read_mostly;
 
 static void                    rpc_async_schedule(struct work_struct *);
 static void                     rpc_release_task(struct rpc_task *task);
+static void __rpc_queue_timer_fn(unsigned long ptr);
 
 /*
  * RPC tasks sit here while waiting for conditions to improve.
@@ -59,8 +60,18 @@ struct workqueue_struct *rpciod_workqueue;
 static void
 __rpc_disable_timer(struct rpc_task *task)
 {
+       if (task->tk_timeout == 0)
+               return;
        dprintk("RPC: %5u disabling timer\n", task->tk_pid);
        task->tk_timeout = 0;
+       list_del(&task->u.tk_wait.timer_list);
+}
+
+static void
+rpc_set_queue_timer(struct rpc_wait_queue *queue, unsigned long expires)
+{
+       queue->timer_list.expires = expires;
+       mod_timer(&queue->timer_list.timer, expires);
 }
 
 /*
@@ -153,7 +164,6 @@ static void __rpc_remove_wait_queue_priority(struct rpc_task *task)
                list_move(&t->u.tk_wait.list, &task->u.tk_wait.list);
                list_splice_init(&task->u.tk_wait.links, &t->u.tk_wait.links);
        }
-       list_del(&task->u.tk_wait.list);
 }
 
 /*
@@ -162,10 +172,10 @@ static void __rpc_remove_wait_queue_priority(struct rpc_task *task)
  */
 static void __rpc_remove_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task)
 {
+       __rpc_disable_timer(task);
        if (RPC_IS_PRIORITY(queue))
                __rpc_remove_wait_queue_priority(task);
-       else
-               list_del(&task->u.tk_wait.list);
+       list_del(&task->u.tk_wait.list);
        queue->qlen--;
        dprintk("RPC: %5u removed from queue %p \"%s\"\n",
                        task->tk_pid, queue, rpc_qname(queue));
@@ -198,6 +208,9 @@ static void __rpc_init_priority_wait_queue(struct rpc_wait_queue *queue, const c
                INIT_LIST_HEAD(&queue->tasks[i]);
        queue->maxpriority = nr_queues - 1;
        rpc_reset_waitqueue_priority(queue);
+       queue->qlen = 0;
+       setup_timer(&queue->timer_list.timer, __rpc_queue_timer_fn, (unsigned long)queue);
+       INIT_LIST_HEAD(&queue->timer_list.list);
 #ifdef RPC_DEBUG
        queue->name = qname;
 #endif
@@ -216,6 +229,7 @@ EXPORT_SYMBOL_GPL(rpc_init_wait_queue);
 
 void rpc_destroy_wait_queue(struct rpc_wait_queue *queue)
 {
+       del_timer_sync(&queue->timer_list.timer);
 }
 EXPORT_SYMBOL_GPL(rpc_destroy_wait_queue);
 
@@ -369,7 +383,6 @@ static void __rpc_do_wake_up_task(struct rpc_wait_queue *queue, struct rpc_task
                return;
        }
 
-       __rpc_disable_timer(task);
        __rpc_remove_wait_queue(queue, task);
 
        rpc_make_runnable(task);
@@ -562,6 +575,31 @@ static void rpc_run_timer(unsigned long ptr)
        smp_mb__after_clear_bit();
 }
 
+static void __rpc_queue_timer_fn(unsigned long ptr)
+{
+       struct rpc_wait_queue *queue = (struct rpc_wait_queue *)ptr;
+       struct rpc_task *task, *n;
+       unsigned long expires, now, timeo;
+
+       spin_lock(&queue->lock);
+       expires = now = jiffies;
+       list_for_each_entry_safe(task, n, &queue->timer_list.list, u.tk_wait.timer_list) {
+               timeo = task->u.tk_wait.expires;
+               if (time_after_eq(now, timeo)) {
+                       list_del_init(&task->u.tk_wait.timer_list);
+                       dprintk("RPC: %5u timeout\n", task->tk_pid);
+                       task->tk_status = -ETIMEDOUT;
+                       rpc_wake_up_task_queue_locked(queue, task);
+                       continue;
+               }
+               if (expires == now || time_after(expires, timeo))
+                       expires = timeo;
+       }
+       if (!list_empty(&queue->timer_list.list))
+               rpc_set_queue_timer(queue, expires);
+       spin_unlock(&queue->lock);
+}
+
 static void __rpc_atrun(struct rpc_task *task)
 {
        task->tk_status = 0;