Merge git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6
[pandora-kernel.git] / fs / smbfs / smbiod.c
1 /*
2  *  smbiod.c
3  *
4  *  Copyright (C) 2000, Charles Loep / Corel Corp.
5  *  Copyright (C) 2001, Urban Widmark
6  */
7
8
9 #include <linux/sched.h>
10 #include <linux/kernel.h>
11 #include <linux/mm.h>
12 #include <linux/string.h>
13 #include <linux/stat.h>
14 #include <linux/errno.h>
15 #include <linux/init.h>
16 #include <linux/file.h>
17 #include <linux/dcache.h>
18 #include <linux/module.h>
19 #include <linux/net.h>
20 #include <linux/kthread.h>
21 #include <net/ip.h>
22
23 #include <linux/smb_fs.h>
24 #include <linux/smbno.h>
25 #include <linux/smb_mount.h>
26
27 #include <asm/system.h>
28 #include <asm/uaccess.h>
29
30 #include "smb_debug.h"
31 #include "request.h"
32 #include "proto.h"
33
34 enum smbiod_state {
35         SMBIOD_DEAD,
36         SMBIOD_STARTING,
37         SMBIOD_RUNNING,
38 };
39
40 static enum smbiod_state smbiod_state = SMBIOD_DEAD;
41 static struct task_struct *smbiod_thread;
42 static DECLARE_WAIT_QUEUE_HEAD(smbiod_wait);
43 static LIST_HEAD(smb_servers);
44 static DEFINE_SPINLOCK(servers_lock);
45
46 #define SMBIOD_DATA_READY       (1<<0)
47 static unsigned long smbiod_flags;
48
49 static int smbiod(void *);
50 static int smbiod_start(void);
51
52 /*
53  * called when there's work for us to do
54  */
55 void smbiod_wake_up(void)
56 {
57         if (smbiod_state == SMBIOD_DEAD)
58                 return;
59         set_bit(SMBIOD_DATA_READY, &smbiod_flags);
60         wake_up_interruptible(&smbiod_wait);
61 }
62
63 /*
64  * start smbiod if none is running
65  */
66 static int smbiod_start(void)
67 {
68         struct task_struct *tsk;
69         int err = 0;
70
71         if (smbiod_state != SMBIOD_DEAD)
72                 return 0;
73         smbiod_state = SMBIOD_STARTING;
74         __module_get(THIS_MODULE);
75         spin_unlock(&servers_lock);
76         tsk = kthread_run(smbiod, NULL, "smbiod");
77         if (IS_ERR(tsk)) {
78                 err = PTR_ERR(tsk);
79                 module_put(THIS_MODULE);
80         }
81
82         spin_lock(&servers_lock);
83         if (err < 0) {
84                 smbiod_state = SMBIOD_DEAD;
85                 smbiod_thread = NULL;
86         } else {
87                 smbiod_state = SMBIOD_RUNNING;
88                 smbiod_thread = tsk;
89         }
90         return err;
91 }
92
93 /*
94  * register a server & start smbiod if necessary
95  */
96 int smbiod_register_server(struct smb_sb_info *server)
97 {
98         int ret;
99         spin_lock(&servers_lock);
100         list_add(&server->entry, &smb_servers);
101         VERBOSE("%p\n", server);
102         ret = smbiod_start();
103         spin_unlock(&servers_lock);
104         return ret;
105 }
106
107 /*
108  * Unregister a server
109  * Must be called with the server lock held.
110  */
111 void smbiod_unregister_server(struct smb_sb_info *server)
112 {
113         spin_lock(&servers_lock);
114         list_del_init(&server->entry);
115         VERBOSE("%p\n", server);
116         spin_unlock(&servers_lock);
117
118         smbiod_wake_up();
119         smbiod_flush(server);
120 }
121
122 void smbiod_flush(struct smb_sb_info *server)
123 {
124         struct list_head *tmp, *n;
125         struct smb_request *req;
126
127         list_for_each_safe(tmp, n, &server->xmitq) {
128                 req = list_entry(tmp, struct smb_request, rq_queue);
129                 req->rq_errno = -EIO;
130                 list_del_init(&req->rq_queue);
131                 smb_rput(req);
132                 wake_up_interruptible(&req->rq_wait);
133         }
134         list_for_each_safe(tmp, n, &server->recvq) {
135                 req = list_entry(tmp, struct smb_request, rq_queue);
136                 req->rq_errno = -EIO;
137                 list_del_init(&req->rq_queue);
138                 smb_rput(req);
139                 wake_up_interruptible(&req->rq_wait);
140         }
141 }
142
143 /*
144  * Wake up smbmount and make it reconnect to the server.
145  * This must be called with the server locked.
146  *
147  * FIXME: add smbconnect version to this
148  */
149 int smbiod_retry(struct smb_sb_info *server)
150 {
151         struct list_head *head;
152         struct smb_request *req;
153         struct pid *pid = get_pid(server->conn_pid);
154         int result = 0;
155
156         VERBOSE("state: %d\n", server->state);
157         if (server->state == CONN_VALID || server->state == CONN_RETRYING)
158                 goto out;
159
160         smb_invalidate_inodes(server);
161
162         /*
163          * Some requests are meaningless after a retry, so we abort them.
164          * One example are all requests using 'fileid' since the files are
165          * closed on retry.
166          */
167         head = server->xmitq.next;
168         while (head != &server->xmitq) {
169                 req = list_entry(head, struct smb_request, rq_queue);
170                 head = head->next;
171
172                 req->rq_bytes_sent = 0;
173                 if (req->rq_flags & SMB_REQ_NORETRY) {
174                         VERBOSE("aborting request %p on xmitq\n", req);
175                         req->rq_errno = -EIO;
176                         list_del_init(&req->rq_queue);
177                         smb_rput(req);
178                         wake_up_interruptible(&req->rq_wait);
179                 }
180         }
181
182         /*
183          * FIXME: test the code for retrying request we already sent
184          */
185         head = server->recvq.next;
186         while (head != &server->recvq) {
187                 req = list_entry(head, struct smb_request, rq_queue);
188                 head = head->next;
189 #if 0
190                 if (req->rq_flags & SMB_REQ_RETRY) {
191                         /* must move the request to the xmitq */
192                         VERBOSE("retrying request %p on recvq\n", req);
193                         list_move(&req->rq_queue, &server->xmitq);
194                         continue;
195                 }
196 #endif
197
198                 VERBOSE("aborting request %p on recvq\n", req);
199                 /* req->rq_rcls = ???; */ /* FIXME: set smb error code too? */
200                 req->rq_errno = -EIO;
201                 list_del_init(&req->rq_queue);
202                 smb_rput(req);
203                 wake_up_interruptible(&req->rq_wait);
204         }
205
206         smb_close_socket(server);
207
208         if (!pid) {
209                 /* FIXME: this is fatal, umount? */
210                 printk(KERN_ERR "smb_retry: no connection process\n");
211                 server->state = CONN_RETRIED;
212                 goto out;
213         }
214
215         /*
216          * Change state so that only one retry per server will be started.
217          */
218         server->state = CONN_RETRYING;
219
220         /*
221          * Note: use the "priv" flag, as a user process may need to reconnect.
222          */
223         result = kill_pid(pid, SIGUSR1, 1);
224         if (result) {
225                 /* FIXME: this is most likely fatal, umount? */
226                 printk(KERN_ERR "smb_retry: signal failed [%d]\n", result);
227                 goto out;
228         }
229         VERBOSE("signalled pid %d\n", pid_nr(pid));
230
231         /* FIXME: The retried requests should perhaps get a "time boost". */
232
233 out:
234         put_pid(pid);
235         return result;
236 }
237
238 /*
239  * Currently handles lockingX packets.
240  */
241 static void smbiod_handle_request(struct smb_sb_info *server)
242 {
243         PARANOIA("smbiod got a request ... and we don't implement oplocks!\n");
244         server->rstate = SMB_RECV_DROP;
245 }
246
247 /*
248  * Do some IO for one server.
249  */
250 static void smbiod_doio(struct smb_sb_info *server)
251 {
252         int result;
253         int maxwork = 7;
254
255         if (server->state != CONN_VALID)
256                 goto out;
257
258         do {
259                 result = smb_request_recv(server);
260                 if (result < 0) {
261                         server->state = CONN_INVALID;
262                         smbiod_retry(server);
263                         goto out;       /* reconnecting is slow */
264                 } else if (server->rstate == SMB_RECV_REQUEST)
265                         smbiod_handle_request(server);
266         } while (result > 0 && maxwork-- > 0);
267
268         /*
269          * If there is more to read then we want to be sure to wake up again.
270          */
271         if (server->state != CONN_VALID)
272                 goto out;
273         if (smb_recv_available(server) > 0)
274                 set_bit(SMBIOD_DATA_READY, &smbiod_flags);
275
276         do {
277                 result = smb_request_send_server(server);
278                 if (result < 0) {
279                         server->state = CONN_INVALID;
280                         smbiod_retry(server);
281                         goto out;       /* reconnecting is slow */
282                 }
283         } while (result > 0);
284
285         /*
286          * If the last request was not sent out we want to wake up again.
287          */
288         if (!list_empty(&server->xmitq))
289                 set_bit(SMBIOD_DATA_READY, &smbiod_flags);
290
291 out:
292         return;
293 }
294
295 /*
296  * smbiod kernel thread
297  */
298 static int smbiod(void *unused)
299 {
300         VERBOSE("SMB Kernel thread starting (%d) ...\n", current->pid);
301
302         for (;;) {
303                 struct smb_sb_info *server;
304                 struct list_head *pos, *n;
305
306                 /* FIXME: Use poll? */
307                 wait_event_interruptible(smbiod_wait,
308                          test_bit(SMBIOD_DATA_READY, &smbiod_flags));
309                 if (signal_pending(current)) {
310                         spin_lock(&servers_lock);
311                         smbiod_state = SMBIOD_DEAD;
312                         spin_unlock(&servers_lock);
313                         break;
314                 }
315
316                 clear_bit(SMBIOD_DATA_READY, &smbiod_flags);
317
318                 spin_lock(&servers_lock);
319                 if (list_empty(&smb_servers)) {
320                         smbiod_state = SMBIOD_DEAD;
321                         spin_unlock(&servers_lock);
322                         break;
323                 }
324
325                 list_for_each_safe(pos, n, &smb_servers) {
326                         server = list_entry(pos, struct smb_sb_info, entry);
327                         VERBOSE("checking server %p\n", server);
328
329                         if (server->state == CONN_VALID) {
330                                 spin_unlock(&servers_lock);
331
332                                 smb_lock_server(server);
333                                 smbiod_doio(server);
334                                 smb_unlock_server(server);
335
336                                 spin_lock(&servers_lock);
337                         }
338                 }
339                 spin_unlock(&servers_lock);
340         }
341
342         VERBOSE("SMB Kernel thread exiting (%d) ...\n", current->pid);
343         module_put_and_exit(0);
344 }