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