Pull bugzilla-7907 into release branch
[linux-2.6] / fs / afs / kafstimod.c
1 /* kafstimod.c: AFS timeout daemon
2  *
3  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells (dhowells@redhat.com)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/sched.h>
15 #include <linux/completion.h>
16 #include <linux/freezer.h>
17 #include "cell.h"
18 #include "volume.h"
19 #include "kafstimod.h"
20 #include <asm/errno.h>
21 #include "internal.h"
22
23 static DECLARE_COMPLETION(kafstimod_alive);
24 static DECLARE_COMPLETION(kafstimod_dead);
25 static DECLARE_WAIT_QUEUE_HEAD(kafstimod_sleepq);
26 static int kafstimod_die;
27
28 static LIST_HEAD(kafstimod_list);
29 static DEFINE_SPINLOCK(kafstimod_lock);
30
31 static int kafstimod(void *arg);
32
33 /*****************************************************************************/
34 /*
35  * start the timeout daemon
36  */
37 int afs_kafstimod_start(void)
38 {
39         int ret;
40
41         ret = kernel_thread(kafstimod, NULL, 0);
42         if (ret < 0)
43                 return ret;
44
45         wait_for_completion(&kafstimod_alive);
46
47         return ret;
48 } /* end afs_kafstimod_start() */
49
50 /*****************************************************************************/
51 /*
52  * stop the timeout daemon
53  */
54 void afs_kafstimod_stop(void)
55 {
56         /* get rid of my daemon */
57         kafstimod_die = 1;
58         wake_up(&kafstimod_sleepq);
59         wait_for_completion(&kafstimod_dead);
60
61 } /* end afs_kafstimod_stop() */
62
63 /*****************************************************************************/
64 /*
65  * timeout processing daemon
66  */
67 static int kafstimod(void *arg)
68 {
69         struct afs_timer *timer;
70
71         DECLARE_WAITQUEUE(myself, current);
72
73         printk("kAFS: Started kafstimod %d\n", current->pid);
74
75         daemonize("kafstimod");
76
77         complete(&kafstimod_alive);
78
79         /* loop around looking for things to attend to */
80  loop:
81         set_current_state(TASK_INTERRUPTIBLE);
82         add_wait_queue(&kafstimod_sleepq, &myself);
83
84         for (;;) {
85                 unsigned long jif;
86                 signed long timeout;
87
88                 /* deal with the server being asked to die */
89                 if (kafstimod_die) {
90                         remove_wait_queue(&kafstimod_sleepq, &myself);
91                         _leave("");
92                         complete_and_exit(&kafstimod_dead, 0);
93                 }
94
95                 try_to_freeze();
96
97                 /* discard pending signals */
98                 afs_discard_my_signals();
99
100                 /* work out the time to elapse before the next event */
101                 spin_lock(&kafstimod_lock);
102                 if (list_empty(&kafstimod_list)) {
103                         timeout = MAX_SCHEDULE_TIMEOUT;
104                 }
105                 else {
106                         timer = list_entry(kafstimod_list.next,
107                                            struct afs_timer, link);
108                         timeout = timer->timo_jif;
109                         jif = jiffies;
110
111                         if (time_before_eq((unsigned long) timeout, jif))
112                                 goto immediate;
113
114                         else {
115                                 timeout = (long) timeout - (long) jiffies;
116                         }
117                 }
118                 spin_unlock(&kafstimod_lock);
119
120                 schedule_timeout(timeout);
121
122                 set_current_state(TASK_INTERRUPTIBLE);
123         }
124
125         /* the thing on the front of the queue needs processing
126          * - we come here with the lock held and timer pointing to the expired
127          *   entry
128          */
129  immediate:
130         remove_wait_queue(&kafstimod_sleepq, &myself);
131         set_current_state(TASK_RUNNING);
132
133         _debug("@@@ Begin Timeout of %p", timer);
134
135         /* dequeue the timer */
136         list_del_init(&timer->link);
137         spin_unlock(&kafstimod_lock);
138
139         /* call the timeout function */
140         timer->ops->timed_out(timer);
141
142         _debug("@@@ End Timeout");
143         goto loop;
144
145 } /* end kafstimod() */
146
147 /*****************************************************************************/
148 /*
149  * (re-)queue a timer
150  */
151 void afs_kafstimod_add_timer(struct afs_timer *timer, unsigned long timeout)
152 {
153         struct afs_timer *ptimer;
154         struct list_head *_p;
155
156         _enter("%p,%lu", timer, timeout);
157
158         spin_lock(&kafstimod_lock);
159
160         list_del(&timer->link);
161
162         /* the timer was deferred or reset - put it back in the queue at the
163          * right place */
164         timer->timo_jif = jiffies + timeout;
165
166         list_for_each(_p, &kafstimod_list) {
167                 ptimer = list_entry(_p, struct afs_timer, link);
168                 if (time_before(timer->timo_jif, ptimer->timo_jif))
169                         break;
170         }
171
172         list_add_tail(&timer->link, _p); /* insert before stopping point */
173
174         spin_unlock(&kafstimod_lock);
175
176         wake_up(&kafstimod_sleepq);
177
178         _leave("");
179 } /* end afs_kafstimod_add_timer() */
180
181 /*****************************************************************************/
182 /*
183  * dequeue a timer
184  * - returns 0 if the timer was deleted or -ENOENT if it wasn't queued
185  */
186 int afs_kafstimod_del_timer(struct afs_timer *timer)
187 {
188         int ret = 0;
189
190         _enter("%p", timer);
191
192         spin_lock(&kafstimod_lock);
193
194         if (list_empty(&timer->link))
195                 ret = -ENOENT;
196         else
197                 list_del_init(&timer->link);
198
199         spin_unlock(&kafstimod_lock);
200
201         wake_up(&kafstimod_sleepq);
202
203         _leave(" = %d", ret);
204         return ret;
205 } /* end afs_kafstimod_del_timer() */