ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / net / rxrpc / krxiod.c
1 /* krxiod.c: Rx I/O 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/sched.h>
13 #include <linux/completion.h>
14 #include <linux/spinlock.h>
15 #include <linux/init.h>
16 #include <rxrpc/krxiod.h>
17 #include <rxrpc/transport.h>
18 #include <rxrpc/peer.h>
19 #include <rxrpc/call.h>
20 #include "internal.h"
21
22 static DECLARE_WAIT_QUEUE_HEAD(rxrpc_krxiod_sleepq);
23 static DECLARE_COMPLETION(rxrpc_krxiod_dead);
24
25 static atomic_t rxrpc_krxiod_qcount = ATOMIC_INIT(0);
26
27 static LIST_HEAD(rxrpc_krxiod_transportq);
28 static spinlock_t rxrpc_krxiod_transportq_lock = SPIN_LOCK_UNLOCKED;
29
30 static LIST_HEAD(rxrpc_krxiod_callq);
31 static spinlock_t rxrpc_krxiod_callq_lock = SPIN_LOCK_UNLOCKED;
32
33 static volatile int rxrpc_krxiod_die;
34
35 /*****************************************************************************/
36 /*
37  * Rx I/O daemon
38  */
39 static int rxrpc_krxiod(void *arg)
40 {
41         DECLARE_WAITQUEUE(krxiod,current);
42
43         printk("Started krxiod %d\n",current->pid);
44
45         daemonize("krxiod");
46
47         /* loop around waiting for work to do */
48         do {
49                 /* wait for work or to be told to exit */
50                 _debug("### Begin Wait");
51                 if (!atomic_read(&rxrpc_krxiod_qcount)) {
52                         set_current_state(TASK_INTERRUPTIBLE);
53
54                         add_wait_queue(&rxrpc_krxiod_sleepq, &krxiod);
55
56                         for (;;) {
57                                 set_current_state(TASK_INTERRUPTIBLE);
58                                 if (atomic_read(&rxrpc_krxiod_qcount) ||
59                                     rxrpc_krxiod_die ||
60                                     signal_pending(current))
61                                         break;
62
63                                 schedule();
64                         }
65
66                         remove_wait_queue(&rxrpc_krxiod_sleepq, &krxiod);
67                         set_current_state(TASK_RUNNING);
68                 }
69                 _debug("### End Wait");
70
71                 /* do work if been given some to do */
72                 _debug("### Begin Work");
73
74                 /* see if there's a transport in need of attention */
75                 if (!list_empty(&rxrpc_krxiod_transportq)) {
76                         struct rxrpc_transport *trans = NULL;
77
78                         spin_lock_irq(&rxrpc_krxiod_transportq_lock);
79
80                         if (!list_empty(&rxrpc_krxiod_transportq)) {
81                                 trans = list_entry(
82                                         rxrpc_krxiod_transportq.next,
83                                         struct rxrpc_transport,
84                                         krxiodq_link);
85
86                                 list_del_init(&trans->krxiodq_link);
87                                 atomic_dec(&rxrpc_krxiod_qcount);
88
89                                 /* make sure it hasn't gone away and doesn't go
90                                  * away */
91                                 if (atomic_read(&trans->usage)>0)
92                                         rxrpc_get_transport(trans);
93                                 else
94                                         trans = NULL;
95                         }
96
97                         spin_unlock_irq(&rxrpc_krxiod_transportq_lock);
98
99                         if (trans) {
100                                 rxrpc_trans_receive_packet(trans);
101                                 rxrpc_put_transport(trans);
102                         }
103                 }
104
105                 /* see if there's a call in need of attention */
106                 if (!list_empty(&rxrpc_krxiod_callq)) {
107                         struct rxrpc_call *call = NULL;
108
109                         spin_lock_irq(&rxrpc_krxiod_callq_lock);
110
111                         if (!list_empty(&rxrpc_krxiod_callq)) {
112                                 call = list_entry(rxrpc_krxiod_callq.next,
113                                                   struct rxrpc_call,
114                                                   rcv_krxiodq_lk);
115                                 list_del_init(&call->rcv_krxiodq_lk);
116                                 atomic_dec(&rxrpc_krxiod_qcount);
117
118                                 /* make sure it hasn't gone away and doesn't go
119                                  * away */
120                                 if (atomic_read(&call->usage) > 0) {
121                                         _debug("@@@ KRXIOD"
122                                                " Begin Attend Call %p", call);
123                                         rxrpc_get_call(call);
124                                 }
125                                 else {
126                                         call = NULL;
127                                 }
128                         }
129
130                         spin_unlock_irq(&rxrpc_krxiod_callq_lock);
131
132                         if (call) {
133                                 rxrpc_call_do_stuff(call);
134                                 rxrpc_put_call(call);
135                                 _debug("@@@ KRXIOD End Attend Call %p", call);
136                         }
137                 }
138
139                 _debug("### End Work");
140
141                 /* discard pending signals */
142                 rxrpc_discard_my_signals();
143
144         } while (!rxrpc_krxiod_die);
145
146         /* and that's all */
147         complete_and_exit(&rxrpc_krxiod_dead, 0);
148
149 } /* end rxrpc_krxiod() */
150
151 /*****************************************************************************/
152 /*
153  * start up a krxiod daemon
154  */
155 int __init rxrpc_krxiod_init(void)
156 {
157         return kernel_thread(rxrpc_krxiod, NULL, 0);
158
159 } /* end rxrpc_krxiod_init() */
160
161 /*****************************************************************************/
162 /*
163  * kill the krxiod daemon and wait for it to complete
164  */
165 void rxrpc_krxiod_kill(void)
166 {
167         rxrpc_krxiod_die = 1;
168         wake_up_all(&rxrpc_krxiod_sleepq);
169         wait_for_completion(&rxrpc_krxiod_dead);
170
171 } /* end rxrpc_krxiod_kill() */
172
173 /*****************************************************************************/
174 /*
175  * queue a transport for attention by krxiod
176  */
177 void rxrpc_krxiod_queue_transport(struct rxrpc_transport *trans)
178 {
179         unsigned long flags;
180
181         _enter("");
182
183         if (list_empty(&trans->krxiodq_link)) {
184                 spin_lock_irqsave(&rxrpc_krxiod_transportq_lock, flags);
185
186                 if (list_empty(&trans->krxiodq_link)) {
187                         if (atomic_read(&trans->usage) > 0) {
188                                 list_add_tail(&trans->krxiodq_link,
189                                               &rxrpc_krxiod_transportq);
190                                 atomic_inc(&rxrpc_krxiod_qcount);
191                         }
192                 }
193
194                 spin_unlock_irqrestore(&rxrpc_krxiod_transportq_lock, flags);
195                 wake_up_all(&rxrpc_krxiod_sleepq);
196         }
197
198         _leave("");
199
200 } /* end rxrpc_krxiod_queue_transport() */
201
202 /*****************************************************************************/
203 /*
204  * dequeue a transport from krxiod's attention queue
205  */
206 void rxrpc_krxiod_dequeue_transport(struct rxrpc_transport *trans)
207 {
208         unsigned long flags;
209
210         _enter("");
211
212         spin_lock_irqsave(&rxrpc_krxiod_transportq_lock, flags);
213         if (!list_empty(&trans->krxiodq_link)) {
214                 list_del_init(&trans->krxiodq_link);
215                 atomic_dec(&rxrpc_krxiod_qcount);
216         }
217         spin_unlock_irqrestore(&rxrpc_krxiod_transportq_lock, flags);
218
219         _leave("");
220
221 } /* end rxrpc_krxiod_dequeue_transport() */
222
223 /*****************************************************************************/
224 /*
225  * queue a call for attention by krxiod
226  */
227 void rxrpc_krxiod_queue_call(struct rxrpc_call *call)
228 {
229         unsigned long flags;
230
231         if (list_empty(&call->rcv_krxiodq_lk)) {
232                 spin_lock_irqsave(&rxrpc_krxiod_callq_lock, flags);
233                 if (atomic_read(&call->usage) > 0) {
234                         list_add_tail(&call->rcv_krxiodq_lk,
235                                       &rxrpc_krxiod_callq);
236                         atomic_inc(&rxrpc_krxiod_qcount);
237                 }
238                 spin_unlock_irqrestore(&rxrpc_krxiod_callq_lock, flags);
239         }
240         wake_up_all(&rxrpc_krxiod_sleepq);
241
242 } /* end rxrpc_krxiod_queue_call() */
243
244 /*****************************************************************************/
245 /*
246  * dequeue a call from krxiod's attention queue
247  */
248 void rxrpc_krxiod_dequeue_call(struct rxrpc_call *call)
249 {
250         unsigned long flags;
251
252         spin_lock_irqsave(&rxrpc_krxiod_callq_lock, flags);
253         if (!list_empty(&call->rcv_krxiodq_lk)) {
254                 list_del_init(&call->rcv_krxiodq_lk);
255                 atomic_dec(&rxrpc_krxiod_qcount);
256         }
257         spin_unlock_irqrestore(&rxrpc_krxiod_callq_lock, flags);
258
259 } /* end rxrpc_krxiod_dequeue_call() */