[PATCH] v9fs: Support to force umount
[linux-2.6] / fs / 9p / mux.c
1 /*
2  * linux/fs/9p/mux.c
3  *
4  * Protocol Multiplexer
5  *
6  *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
7  *  Copyright (C) 2004 by Latchesar Ionkov <lucho@ionkov.net>
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to:
21  *  Free Software Foundation
22  *  51 Franklin Street, Fifth Floor
23  *  Boston, MA  02111-1301  USA
24  *
25  */
26
27 #include <linux/config.h>
28 #include <linux/module.h>
29 #include <linux/errno.h>
30 #include <linux/fs.h>
31 #include <linux/kthread.h>
32 #include <linux/idr.h>
33
34 #include "debug.h"
35 #include "v9fs.h"
36 #include "9p.h"
37 #include "transport.h"
38 #include "conv.h"
39 #include "mux.h"
40
41 /**
42  * dprintcond - print condition of session info
43  * @v9ses: session info structure
44  * @req: RPC request structure
45  *
46  */
47
48 static inline int
49 dprintcond(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req)
50 {
51         dprintk(DEBUG_MUX, "condition: %d, %p\n", v9ses->transport->status,
52                 req->rcall);
53         return 0;
54 }
55
56 /**
57  * xread - force read of a certain number of bytes
58  * @v9ses: session info structure
59  * @ptr: pointer to buffer
60  * @sz: number of bytes to read
61  *
62  * Chuck Cranor CS-533 project1
63  */
64
65 static int xread(struct v9fs_session_info *v9ses, void *ptr, unsigned long sz)
66 {
67         int rd = 0;
68         int ret = 0;
69         while (rd < sz) {
70                 ret = v9ses->transport->read(v9ses->transport, ptr, sz - rd);
71                 if (ret <= 0) {
72                         dprintk(DEBUG_ERROR, "xread errno %d\n", ret);
73                         return ret;
74                 }
75                 rd += ret;
76                 ptr += ret;
77         }
78         return (rd);
79 }
80
81 /**
82  * read_message - read a full 9P2000 fcall packet
83  * @v9ses: session info structure
84  * @rcall: fcall structure to read into
85  * @rcalllen: size of fcall buffer
86  *
87  */
88
89 static int
90 read_message(struct v9fs_session_info *v9ses,
91              struct v9fs_fcall *rcall, int rcalllen)
92 {
93         unsigned char buf[4];
94         void *data;
95         int size = 0;
96         int res = 0;
97
98         res = xread(v9ses, buf, sizeof(buf));
99         if (res < 0) {
100                 dprintk(DEBUG_ERROR,
101                         "Reading of count field failed returned: %d\n", res);
102                 return res;
103         }
104
105         if (res < 4) {
106                 dprintk(DEBUG_ERROR,
107                         "Reading of count field failed returned: %d\n", res);
108                 return -EIO;
109         }
110
111         size = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
112         dprintk(DEBUG_MUX, "got a packet count: %d\n", size);
113
114         /* adjust for the four bytes of size */
115         size -= 4;
116
117         if (size > v9ses->maxdata) {
118                 dprintk(DEBUG_ERROR, "packet too big: %d\n", size);
119                 return -E2BIG;
120         }
121
122         data = kmalloc(size, GFP_KERNEL);
123         if (!data) {
124                 eprintk(KERN_WARNING, "out of memory\n");
125                 return -ENOMEM;
126         }
127
128         res = xread(v9ses, data, size);
129         if (res < size) {
130                 dprintk(DEBUG_ERROR, "Reading of fcall failed returned: %d\n",
131                         res);
132                 kfree(data);
133                 return res;
134         }
135
136         /* we now have an in-memory string that is the reply.
137          * deserialize it. There is very little to go wrong at this point
138          * save for v9fs_alloc errors.
139          */
140         res = v9fs_deserialize_fcall(v9ses, size, data, v9ses->maxdata,
141                                      rcall, rcalllen);
142
143         kfree(data);
144
145         if (res < 0)
146                 return res;
147
148         return 0;
149 }
150
151 /**
152  * v9fs_recv - receive an RPC response for a particular tag
153  * @v9ses: session info structure
154  * @req: RPC request structure
155  *
156  */
157
158 static int v9fs_recv(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req)
159 {
160         int ret = 0;
161
162         dprintk(DEBUG_MUX, "waiting for response: %d\n", req->tcall->tag);
163         ret = wait_event_interruptible(v9ses->read_wait,
164                        ((v9ses->transport->status != Connected) ||
165                         (req->rcall != 0) || dprintcond(v9ses, req)));
166
167         dprintk(DEBUG_MUX, "got it: rcall %p\n", req->rcall);
168         if (v9ses->transport->status == Disconnected)
169                 return -ECONNRESET;
170
171         if (ret == 0) {
172                 spin_lock(&v9ses->muxlock);
173                 list_del(&req->next);
174                 spin_unlock(&v9ses->muxlock);
175         }
176
177         return ret;
178 }
179
180 /**
181  * v9fs_send - send a 9P request
182  * @v9ses: session info structure
183  * @req: RPC request to send
184  *
185  */
186
187 static int v9fs_send(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req)
188 {
189         int ret = -1;
190         void *data = NULL;
191         struct v9fs_fcall *tcall = req->tcall;
192
193         data = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL);
194         if (!data)
195                 return -ENOMEM;
196
197         tcall->size = 0;        /* enforce size recalculation */
198         ret =
199             v9fs_serialize_fcall(v9ses, tcall, data,
200                                  v9ses->maxdata + V9FS_IOHDRSZ);
201         if (ret < 0)
202                 goto free_data;
203
204         spin_lock(&v9ses->muxlock);
205         list_add(&req->next, &v9ses->mux_fcalls);
206         spin_unlock(&v9ses->muxlock);
207
208         dprintk(DEBUG_MUX, "sending message: tag %d size %d\n", tcall->tag,
209                 tcall->size);
210         ret = v9ses->transport->write(v9ses->transport, data, tcall->size);
211
212         if (ret != tcall->size) {
213                 spin_lock(&v9ses->muxlock);
214                 list_del(&req->next);
215                 kfree(req->rcall);
216
217                 spin_unlock(&v9ses->muxlock);
218                 if (ret >= 0)
219                         ret = -EREMOTEIO;
220         } else
221                 ret = 0;
222
223       free_data:
224         kfree(data);
225         return ret;
226 }
227
228 /**
229  * v9fs_mux_rpc - send a request, receive a response
230  * @v9ses: session info structure
231  * @tcall: fcall to send
232  * @rcall: buffer to place response into
233  *
234  */
235
236 long
237 v9fs_mux_rpc(struct v9fs_session_info *v9ses, struct v9fs_fcall *tcall,
238              struct v9fs_fcall **rcall)
239 {
240         int tid = -1;
241         struct v9fs_fcall *fcall = NULL;
242         struct v9fs_rpcreq req;
243         int ret = -1;
244
245         if (!v9ses)
246                 return -EINVAL;
247
248         if (rcall)
249                 *rcall = NULL;
250
251         if (tcall->id != TVERSION) {
252                 tid = v9fs_get_idpool(&v9ses->tidpool);
253                 if (tid < 0)
254                         return -ENOMEM;
255         }
256
257         tcall->tag = tid;
258
259         req.tcall = tcall;
260         req.rcall = NULL;
261
262         ret = v9fs_send(v9ses, &req);
263
264         if (ret < 0) {
265                 if (tcall->id != TVERSION)
266                         v9fs_put_idpool(tid, &v9ses->tidpool);
267                 dprintk(DEBUG_MUX, "error %d\n", ret);
268                 return ret;
269         }
270
271         ret = v9fs_recv(v9ses, &req);
272
273         fcall = req.rcall;
274
275         dprintk(DEBUG_MUX, "received: tag=%x, ret=%d\n", tcall->tag, ret);
276         if (ret == -ERESTARTSYS) {
277                 if (v9ses->transport->status != Disconnected
278                     && tcall->id != TFLUSH) {
279                         unsigned long flags;
280
281                         dprintk(DEBUG_MUX, "flushing the tag: %d\n",
282                                 tcall->tag);
283                         clear_thread_flag(TIF_SIGPENDING);
284                         v9fs_t_flush(v9ses, tcall->tag);
285                         spin_lock_irqsave(&current->sighand->siglock, flags);
286                         recalc_sigpending();
287                         spin_unlock_irqrestore(&current->sighand->siglock,
288                                                flags);
289                         dprintk(DEBUG_MUX, "flushing done\n");
290                 }
291
292                 goto release_req;
293         } else if (ret < 0)
294                 goto release_req;
295
296         if (!fcall)
297                 ret = -EIO;
298         else {
299                 if (fcall->id == RERROR) {
300                         ret = v9fs_errstr2errno(fcall->params.rerror.error);
301                         if (ret == 0) { /* string match failed */
302                                 if (fcall->params.rerror.errno)
303                                         ret = -(fcall->params.rerror.errno);
304                                 else
305                                         ret = -ESERVERFAULT;
306                         }
307                 } else if (fcall->id != tcall->id + 1) {
308                         dprintk(DEBUG_ERROR,
309                                 "fcall mismatch: expected %d, got %d\n",
310                                 tcall->id + 1, fcall->id);
311                         ret = -EIO;
312                 }
313         }
314
315       release_req:
316         if (tcall->id != TVERSION)
317                 v9fs_put_idpool(tid, &v9ses->tidpool);
318         if (rcall)
319                 *rcall = fcall;
320         else
321                 kfree(fcall);
322
323         return ret;
324 }
325
326 /**
327  * v9fs_mux_cancel_requests - cancels all pending requests
328  *
329  * @v9ses: session info structure
330  * @err: error code to return to the requests
331  */
332 void v9fs_mux_cancel_requests(struct v9fs_session_info *v9ses, int err)
333 {
334         struct v9fs_rpcreq *rptr;
335         struct v9fs_rpcreq *rreq;
336
337         dprintk(DEBUG_MUX, " %d\n", err);
338         spin_lock(&v9ses->muxlock);
339         list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) {
340                 rreq->err = err;
341         }
342         spin_unlock(&v9ses->muxlock);
343         wake_up_all(&v9ses->read_wait);
344 }
345
346 /**
347  * v9fs_recvproc - kproc to handle demultiplexing responses
348  * @data: session info structure
349  *
350  */
351
352 static int v9fs_recvproc(void *data)
353 {
354         struct v9fs_session_info *v9ses = (struct v9fs_session_info *)data;
355         struct v9fs_fcall *rcall = NULL;
356         struct v9fs_rpcreq *rptr;
357         struct v9fs_rpcreq *req;
358         struct v9fs_rpcreq *rreq;
359         int err = 0;
360
361         allow_signal(SIGKILL);
362         set_current_state(TASK_INTERRUPTIBLE);
363         complete(&v9ses->proccmpl);
364         while (!kthread_should_stop() && err >= 0) {
365                 req = rptr = rreq = NULL;
366
367                 rcall = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL);
368                 if (!rcall) {
369                         eprintk(KERN_ERR, "no memory for buffers\n");
370                         break;
371                 }
372
373                 err = read_message(v9ses, rcall, v9ses->maxdata + V9FS_IOHDRSZ);
374                 if (err < 0) {
375                         kfree(rcall);
376                         break;
377                 }
378                 spin_lock(&v9ses->muxlock);
379                 list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) {
380                         if (rreq->tcall->tag == rcall->tag) {
381                                 req = rreq;
382                                 req->rcall = rcall;
383                                 break;
384                         }
385                 }
386
387                 if (req && (req->tcall->id == TFLUSH)) {
388                         struct v9fs_rpcreq *treq = NULL;
389                         list_for_each_entry_safe(treq, rptr, &v9ses->mux_fcalls, next) {
390                                 if (treq->tcall->tag ==
391                                     req->tcall->params.tflush.oldtag) {
392                                         list_del(&rptr->next);
393                                         kfree(treq->rcall);
394                                         break;
395                                 }
396                         }
397                 }
398
399                 spin_unlock(&v9ses->muxlock);
400
401                 if (!req) {
402                         dprintk(DEBUG_ERROR,
403                                 "unexpected response: id %d tag %d\n",
404                                 rcall->id, rcall->tag);
405
406                         kfree(rcall);
407                 }
408
409                 wake_up_all(&v9ses->read_wait);
410                 set_current_state(TASK_INTERRUPTIBLE);
411         }
412
413         /* Inform all pending processes about the failure */
414         wake_up_all(&v9ses->read_wait);
415
416         if (signal_pending(current))
417                 complete(&v9ses->proccmpl);
418
419         dprintk(DEBUG_MUX, "recvproc: end\n");
420         v9ses->recvproc = NULL;
421
422         return err >= 0;
423 }
424
425 /**
426  * v9fs_mux_init - initialize multiplexer (spawn kproc)
427  * @v9ses: session info structure
428  * @dev_name: mount device information (to create unique kproc)
429  *
430  */
431
432 int v9fs_mux_init(struct v9fs_session_info *v9ses, const char *dev_name)
433 {
434         char procname[60];
435
436         strncpy(procname, dev_name, sizeof(procname));
437         procname[sizeof(procname) - 1] = 0;
438
439         init_waitqueue_head(&v9ses->read_wait);
440         init_completion(&v9ses->fcread);
441         init_completion(&v9ses->proccmpl);
442         spin_lock_init(&v9ses->muxlock);
443         INIT_LIST_HEAD(&v9ses->mux_fcalls);
444         v9ses->recvproc = NULL;
445         v9ses->curfcall = NULL;
446
447         v9ses->recvproc = kthread_create(v9fs_recvproc, v9ses,
448                                          "v9fs_recvproc %s", procname);
449
450         if (IS_ERR(v9ses->recvproc)) {
451                 eprintk(KERN_ERR, "cannot create receiving thread\n");
452                 v9fs_session_close(v9ses);
453                 return -ECONNABORTED;
454         }
455
456         wake_up_process(v9ses->recvproc);
457         wait_for_completion(&v9ses->proccmpl);
458
459         return 0;
460 }