Commit | Line | Data |
---|---|---|
426cc91a | 1 | /* |
bd238fb4 | 2 | * net/9p/mux.c |
426cc91a EVH |
3 | * |
4 | * Protocol Multiplexer | |
5 | * | |
6 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> | |
3cf6429a | 7 | * Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net> |
426cc91a EVH |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify | |
42e8c509 EVH |
10 | * it under the terms of the GNU General Public License version 2 |
11 | * as published by the Free Software Foundation. | |
426cc91a EVH |
12 | * |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to: | |
20 | * Free Software Foundation | |
21 | * 51 Franklin Street, Fifth Floor | |
22 | * Boston, MA 02111-1301 USA | |
23 | * | |
24 | */ | |
25 | ||
426cc91a EVH |
26 | #include <linux/module.h> |
27 | #include <linux/errno.h> | |
28 | #include <linux/fs.h> | |
3cf6429a | 29 | #include <linux/poll.h> |
426cc91a EVH |
30 | #include <linux/kthread.h> |
31 | #include <linux/idr.h> | |
4f7a07b8 | 32 | #include <linux/mutex.h> |
bd238fb4 LI |
33 | #include <net/9p/9p.h> |
34 | #include <net/9p/transport.h> | |
35 | #include <net/9p/conn.h> | |
426cc91a | 36 | |
3cf6429a LI |
37 | #define ERREQFLUSH 1 |
38 | #define SCHED_TIMEOUT 10 | |
39 | #define MAXPOLLWADDR 2 | |
40 | ||
41 | enum { | |
42 | Rworksched = 1, /* read work scheduled or running */ | |
43 | Rpending = 2, /* can read */ | |
44 | Wworksched = 4, /* write work scheduled or running */ | |
45 | Wpending = 8, /* can write */ | |
46 | }; | |
47 | ||
41e5a6ac LI |
48 | enum { |
49 | None, | |
50 | Flushing, | |
51 | Flushed, | |
52 | }; | |
53 | ||
bd238fb4 | 54 | struct p9_mux_poll_task; |
3cf6429a | 55 | |
bd238fb4 LI |
56 | struct p9_req { |
57 | spinlock_t lock; /* protect request structure */ | |
3cf6429a | 58 | int tag; |
bd238fb4 LI |
59 | struct p9_fcall *tcall; |
60 | struct p9_fcall *rcall; | |
3cf6429a | 61 | int err; |
bd238fb4 | 62 | p9_conn_req_callback cb; |
3cf6429a | 63 | void *cba; |
41e5a6ac | 64 | int flush; |
3cf6429a LI |
65 | struct list_head req_list; |
66 | }; | |
67 | ||
bd238fb4 LI |
68 | struct p9_conn { |
69 | spinlock_t lock; /* protect lock structure */ | |
3cf6429a | 70 | struct list_head mux_list; |
bd238fb4 | 71 | struct p9_mux_poll_task *poll_task; |
3cf6429a LI |
72 | int msize; |
73 | unsigned char *extended; | |
bd238fb4 LI |
74 | struct p9_transport *trans; |
75 | struct p9_idpool *tagpool; | |
3cf6429a LI |
76 | int err; |
77 | wait_queue_head_t equeue; | |
78 | struct list_head req_list; | |
79 | struct list_head unsent_req_list; | |
bd238fb4 | 80 | struct p9_fcall *rcall; |
3cf6429a LI |
81 | int rpos; |
82 | char *rbuf; | |
83 | int wpos; | |
84 | int wsize; | |
85 | char *wbuf; | |
86 | wait_queue_t poll_wait[MAXPOLLWADDR]; | |
87 | wait_queue_head_t *poll_waddr[MAXPOLLWADDR]; | |
88 | poll_table pt; | |
89 | struct work_struct rq; | |
90 | struct work_struct wq; | |
91 | unsigned long wsched; | |
92 | }; | |
93 | ||
bd238fb4 | 94 | struct p9_mux_poll_task { |
3cf6429a LI |
95 | struct task_struct *task; |
96 | struct list_head mux_list; | |
97 | int muxnum; | |
98 | }; | |
99 | ||
bd238fb4 LI |
100 | struct p9_mux_rpc { |
101 | struct p9_conn *m; | |
3cf6429a | 102 | int err; |
bd238fb4 LI |
103 | struct p9_fcall *tcall; |
104 | struct p9_fcall *rcall; | |
3cf6429a LI |
105 | wait_queue_head_t wqueue; |
106 | }; | |
107 | ||
bd238fb4 LI |
108 | static int p9_poll_proc(void *); |
109 | static void p9_read_work(struct work_struct *work); | |
110 | static void p9_write_work(struct work_struct *work); | |
111 | static void p9_pollwait(struct file *filp, wait_queue_head_t *wait_address, | |
3cf6429a | 112 | poll_table * p); |
bd238fb4 LI |
113 | static u16 p9_mux_get_tag(struct p9_conn *); |
114 | static void p9_mux_put_tag(struct p9_conn *, u16); | |
3cf6429a | 115 | |
bd238fb4 LI |
116 | static DEFINE_MUTEX(p9_mux_task_lock); |
117 | static struct workqueue_struct *p9_mux_wq; | |
3cf6429a | 118 | |
bd238fb4 LI |
119 | static int p9_mux_num; |
120 | static int p9_mux_poll_task_num; | |
121 | static struct p9_mux_poll_task p9_mux_poll_tasks[100]; | |
3cf6429a | 122 | |
bd238fb4 | 123 | int p9_mux_global_init(void) |
3cf6429a LI |
124 | { |
125 | int i; | |
126 | ||
bd238fb4 LI |
127 | for (i = 0; i < ARRAY_SIZE(p9_mux_poll_tasks); i++) |
128 | p9_mux_poll_tasks[i].task = NULL; | |
3cf6429a | 129 | |
bd238fb4 LI |
130 | p9_mux_wq = create_workqueue("v9fs"); |
131 | if (!p9_mux_wq) { | |
f94b3470 | 132 | printk(KERN_WARNING "v9fs: mux: creating workqueue failed\n"); |
1dac06b2 | 133 | return -ENOMEM; |
f94b3470 | 134 | } |
1dac06b2 LI |
135 | |
136 | return 0; | |
3cf6429a | 137 | } |
426cc91a | 138 | |
bd238fb4 | 139 | void p9_mux_global_exit(void) |
426cc91a | 140 | { |
bd238fb4 | 141 | destroy_workqueue(p9_mux_wq); |
426cc91a EVH |
142 | } |
143 | ||
144 | /** | |
bd238fb4 | 145 | * p9_mux_calc_poll_procs - calculates the number of polling procs |
3cf6429a | 146 | * based on the number of mounted v9fs filesystems. |
426cc91a | 147 | * |
3cf6429a | 148 | * The current implementation returns sqrt of the number of mounts. |
426cc91a | 149 | */ |
bd238fb4 | 150 | static int p9_mux_calc_poll_procs(int muxnum) |
3cf6429a LI |
151 | { |
152 | int n; | |
153 | ||
bd238fb4 LI |
154 | if (p9_mux_poll_task_num) |
155 | n = muxnum / p9_mux_poll_task_num + | |
156 | (muxnum % p9_mux_poll_task_num ? 1 : 0); | |
3cf6429a LI |
157 | else |
158 | n = 1; | |
159 | ||
bd238fb4 LI |
160 | if (n > ARRAY_SIZE(p9_mux_poll_tasks)) |
161 | n = ARRAY_SIZE(p9_mux_poll_tasks); | |
426cc91a | 162 | |
3cf6429a LI |
163 | return n; |
164 | } | |
165 | ||
bd238fb4 | 166 | static int p9_mux_poll_start(struct p9_conn *m) |
426cc91a | 167 | { |
3cf6429a | 168 | int i, n; |
bd238fb4 | 169 | struct p9_mux_poll_task *vpt, *vptlast; |
1dac06b2 | 170 | struct task_struct *pproc; |
3cf6429a | 171 | |
bd238fb4 LI |
172 | P9_DPRINTK(P9_DEBUG_MUX, "mux %p muxnum %d procnum %d\n", m, p9_mux_num, |
173 | p9_mux_poll_task_num); | |
174 | mutex_lock(&p9_mux_task_lock); | |
3cf6429a | 175 | |
bd238fb4 LI |
176 | n = p9_mux_calc_poll_procs(p9_mux_num + 1); |
177 | if (n > p9_mux_poll_task_num) { | |
178 | for (i = 0; i < ARRAY_SIZE(p9_mux_poll_tasks); i++) { | |
179 | if (p9_mux_poll_tasks[i].task == NULL) { | |
180 | vpt = &p9_mux_poll_tasks[i]; | |
181 | P9_DPRINTK(P9_DEBUG_MUX, "create proc %p\n", | |
182 | vpt); | |
183 | pproc = kthread_create(p9_poll_proc, vpt, | |
184 | "v9fs-poll"); | |
1dac06b2 LI |
185 | |
186 | if (!IS_ERR(pproc)) { | |
187 | vpt->task = pproc; | |
188 | INIT_LIST_HEAD(&vpt->mux_list); | |
189 | vpt->muxnum = 0; | |
bd238fb4 | 190 | p9_mux_poll_task_num++; |
1dac06b2 LI |
191 | wake_up_process(vpt->task); |
192 | } | |
3cf6429a LI |
193 | break; |
194 | } | |
426cc91a | 195 | } |
426cc91a | 196 | |
bd238fb4 LI |
197 | if (i >= ARRAY_SIZE(p9_mux_poll_tasks)) |
198 | P9_DPRINTK(P9_DEBUG_ERROR, | |
199 | "warning: no free poll slots\n"); | |
3cf6429a | 200 | } |
426cc91a | 201 | |
bd238fb4 LI |
202 | n = (p9_mux_num + 1) / p9_mux_poll_task_num + |
203 | ((p9_mux_num + 1) % p9_mux_poll_task_num ? 1 : 0); | |
3cf6429a LI |
204 | |
205 | vptlast = NULL; | |
bd238fb4 LI |
206 | for (i = 0; i < ARRAY_SIZE(p9_mux_poll_tasks); i++) { |
207 | vpt = &p9_mux_poll_tasks[i]; | |
3cf6429a LI |
208 | if (vpt->task != NULL) { |
209 | vptlast = vpt; | |
210 | if (vpt->muxnum < n) { | |
bd238fb4 | 211 | P9_DPRINTK(P9_DEBUG_MUX, "put in proc %d\n", i); |
3cf6429a LI |
212 | list_add(&m->mux_list, &vpt->mux_list); |
213 | vpt->muxnum++; | |
214 | m->poll_task = vpt; | |
bd238fb4 LI |
215 | memset(&m->poll_waddr, 0, |
216 | sizeof(m->poll_waddr)); | |
217 | init_poll_funcptr(&m->pt, p9_pollwait); | |
3cf6429a LI |
218 | break; |
219 | } | |
220 | } | |
426cc91a EVH |
221 | } |
222 | ||
bd238fb4 | 223 | if (i >= ARRAY_SIZE(p9_mux_poll_tasks)) { |
1dac06b2 LI |
224 | if (vptlast == NULL) |
225 | return -ENOMEM; | |
226 | ||
bd238fb4 | 227 | P9_DPRINTK(P9_DEBUG_MUX, "put in proc %d\n", i); |
3cf6429a LI |
228 | list_add(&m->mux_list, &vptlast->mux_list); |
229 | vptlast->muxnum++; | |
1dac06b2 | 230 | m->poll_task = vptlast; |
3cf6429a | 231 | memset(&m->poll_waddr, 0, sizeof(m->poll_waddr)); |
bd238fb4 | 232 | init_poll_funcptr(&m->pt, p9_pollwait); |
426cc91a EVH |
233 | } |
234 | ||
bd238fb4 LI |
235 | p9_mux_num++; |
236 | mutex_unlock(&p9_mux_task_lock); | |
1dac06b2 LI |
237 | |
238 | return 0; | |
3cf6429a | 239 | } |
426cc91a | 240 | |
bd238fb4 | 241 | static void p9_mux_poll_stop(struct p9_conn *m) |
3cf6429a LI |
242 | { |
243 | int i; | |
bd238fb4 | 244 | struct p9_mux_poll_task *vpt; |
3cf6429a | 245 | |
bd238fb4 | 246 | mutex_lock(&p9_mux_task_lock); |
3cf6429a LI |
247 | vpt = m->poll_task; |
248 | list_del(&m->mux_list); | |
bd238fb4 | 249 | for (i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) { |
3cf6429a LI |
250 | if (m->poll_waddr[i] != NULL) { |
251 | remove_wait_queue(m->poll_waddr[i], &m->poll_wait[i]); | |
252 | m->poll_waddr[i] = NULL; | |
253 | } | |
254 | } | |
255 | vpt->muxnum--; | |
256 | if (!vpt->muxnum) { | |
bd238fb4 | 257 | P9_DPRINTK(P9_DEBUG_MUX, "destroy proc %p\n", vpt); |
2c0463a9 | 258 | kthread_stop(vpt->task); |
3cf6429a | 259 | vpt->task = NULL; |
bd238fb4 | 260 | p9_mux_poll_task_num--; |
3cf6429a | 261 | } |
bd238fb4 LI |
262 | p9_mux_num--; |
263 | mutex_unlock(&p9_mux_task_lock); | |
3cf6429a | 264 | } |
426cc91a | 265 | |
3cf6429a | 266 | /** |
bd238fb4 | 267 | * p9_conn_create - allocate and initialize the per-session mux data |
3cf6429a LI |
268 | * Creates the polling task if this is the first session. |
269 | * | |
270 | * @trans - transport structure | |
271 | * @msize - maximum message size | |
272 | * @extended - pointer to the extended flag | |
273 | */ | |
bd238fb4 | 274 | struct p9_conn *p9_conn_create(struct p9_transport *trans, int msize, |
3cf6429a LI |
275 | unsigned char *extended) |
276 | { | |
277 | int i, n; | |
bd238fb4 | 278 | struct p9_conn *m, *mtmp; |
3cf6429a | 279 | |
bd238fb4 LI |
280 | P9_DPRINTK(P9_DEBUG_MUX, "transport %p msize %d\n", trans, msize); |
281 | m = kmalloc(sizeof(struct p9_conn), GFP_KERNEL); | |
3cf6429a LI |
282 | if (!m) |
283 | return ERR_PTR(-ENOMEM); | |
284 | ||
285 | spin_lock_init(&m->lock); | |
286 | INIT_LIST_HEAD(&m->mux_list); | |
287 | m->msize = msize; | |
288 | m->extended = extended; | |
289 | m->trans = trans; | |
bd238fb4 | 290 | m->tagpool = p9_idpool_create(); |
1a3cac6c EVH |
291 | if (IS_ERR(m->tagpool)) { |
292 | mtmp = ERR_PTR(-ENOMEM); | |
bd238fb4 | 293 | kfree(m); |
1a3cac6c | 294 | return mtmp; |
bd238fb4 LI |
295 | } |
296 | ||
3cf6429a LI |
297 | m->err = 0; |
298 | init_waitqueue_head(&m->equeue); | |
299 | INIT_LIST_HEAD(&m->req_list); | |
300 | INIT_LIST_HEAD(&m->unsent_req_list); | |
531b1094 | 301 | m->rcall = NULL; |
3cf6429a | 302 | m->rpos = 0; |
531b1094 | 303 | m->rbuf = NULL; |
3cf6429a | 304 | m->wpos = m->wsize = 0; |
531b1094 | 305 | m->wbuf = NULL; |
bd238fb4 LI |
306 | INIT_WORK(&m->rq, p9_read_work); |
307 | INIT_WORK(&m->wq, p9_write_work); | |
3cf6429a LI |
308 | m->wsched = 0; |
309 | memset(&m->poll_waddr, 0, sizeof(m->poll_waddr)); | |
1dac06b2 | 310 | m->poll_task = NULL; |
bd238fb4 | 311 | n = p9_mux_poll_start(m); |
1a3cac6c EVH |
312 | if (n) { |
313 | kfree(m); | |
1dac06b2 | 314 | return ERR_PTR(n); |
1a3cac6c | 315 | } |
3cf6429a LI |
316 | |
317 | n = trans->poll(trans, &m->pt); | |
318 | if (n & POLLIN) { | |
bd238fb4 | 319 | P9_DPRINTK(P9_DEBUG_MUX, "mux %p can read\n", m); |
3cf6429a | 320 | set_bit(Rpending, &m->wsched); |
426cc91a EVH |
321 | } |
322 | ||
3cf6429a | 323 | if (n & POLLOUT) { |
bd238fb4 | 324 | P9_DPRINTK(P9_DEBUG_MUX, "mux %p can write\n", m); |
3cf6429a | 325 | set_bit(Wpending, &m->wsched); |
426cc91a EVH |
326 | } |
327 | ||
bd238fb4 | 328 | for (i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) { |
3cf6429a | 329 | if (IS_ERR(m->poll_waddr[i])) { |
bd238fb4 | 330 | p9_mux_poll_stop(m); |
3cf6429a LI |
331 | mtmp = (void *)m->poll_waddr; /* the error code */ |
332 | kfree(m); | |
333 | m = mtmp; | |
334 | break; | |
335 | } | |
426cc91a EVH |
336 | } |
337 | ||
3cf6429a LI |
338 | return m; |
339 | } | |
bd238fb4 | 340 | EXPORT_SYMBOL(p9_conn_create); |
426cc91a | 341 | |
3cf6429a | 342 | /** |
bd238fb4 | 343 | * p9_mux_destroy - cancels all pending requests and frees mux resources |
3cf6429a | 344 | */ |
bd238fb4 | 345 | void p9_conn_destroy(struct p9_conn *m) |
3cf6429a | 346 | { |
bd238fb4 | 347 | P9_DPRINTK(P9_DEBUG_MUX, "mux %p prev %p next %p\n", m, |
3cf6429a | 348 | m->mux_list.prev, m->mux_list.next); |
bd238fb4 | 349 | p9_conn_cancel(m, -ECONNRESET); |
3cf6429a LI |
350 | |
351 | if (!list_empty(&m->req_list)) { | |
352 | /* wait until all processes waiting on this session exit */ | |
bd238fb4 LI |
353 | P9_DPRINTK(P9_DEBUG_MUX, |
354 | "mux %p waiting for empty request queue\n", m); | |
3cf6429a | 355 | wait_event_timeout(m->equeue, (list_empty(&m->req_list)), 5000); |
bd238fb4 | 356 | P9_DPRINTK(P9_DEBUG_MUX, "mux %p request queue empty: %d\n", m, |
3cf6429a LI |
357 | list_empty(&m->req_list)); |
358 | } | |
426cc91a | 359 | |
bd238fb4 | 360 | p9_mux_poll_stop(m); |
3cf6429a | 361 | m->trans = NULL; |
bd238fb4 | 362 | p9_idpool_destroy(m->tagpool); |
3cf6429a | 363 | kfree(m); |
426cc91a | 364 | } |
bd238fb4 | 365 | EXPORT_SYMBOL(p9_conn_destroy); |
426cc91a EVH |
366 | |
367 | /** | |
bd238fb4 | 368 | * p9_pollwait - called by files poll operation to add v9fs-poll task |
3cf6429a | 369 | * to files wait queue |
426cc91a | 370 | */ |
3cf6429a | 371 | static void |
bd238fb4 | 372 | p9_pollwait(struct file *filp, wait_queue_head_t *wait_address, |
3cf6429a | 373 | poll_table * p) |
426cc91a | 374 | { |
3cf6429a | 375 | int i; |
bd238fb4 | 376 | struct p9_conn *m; |
426cc91a | 377 | |
bd238fb4 LI |
378 | m = container_of(p, struct p9_conn, pt); |
379 | for (i = 0; i < ARRAY_SIZE(m->poll_waddr); i++) | |
3cf6429a LI |
380 | if (m->poll_waddr[i] == NULL) |
381 | break; | |
cb2e87a6 | 382 | |
3cf6429a | 383 | if (i >= ARRAY_SIZE(m->poll_waddr)) { |
bd238fb4 | 384 | P9_DPRINTK(P9_DEBUG_ERROR, "not enough wait_address slots\n"); |
3cf6429a LI |
385 | return; |
386 | } | |
cb2e87a6 | 387 | |
3cf6429a | 388 | m->poll_waddr[i] = wait_address; |
cb2e87a6 | 389 | |
3cf6429a | 390 | if (!wait_address) { |
bd238fb4 | 391 | P9_DPRINTK(P9_DEBUG_ERROR, "no wait_address\n"); |
3cf6429a LI |
392 | m->poll_waddr[i] = ERR_PTR(-EIO); |
393 | return; | |
394 | } | |
426cc91a | 395 | |
3cf6429a LI |
396 | init_waitqueue_entry(&m->poll_wait[i], m->poll_task->task); |
397 | add_wait_queue(wait_address, &m->poll_wait[i]); | |
426cc91a EVH |
398 | } |
399 | ||
400 | /** | |
bd238fb4 | 401 | * p9_poll_mux - polls a mux and schedules read or write works if necessary |
426cc91a | 402 | */ |
bd238fb4 | 403 | static void p9_poll_mux(struct p9_conn *m) |
426cc91a | 404 | { |
3cf6429a | 405 | int n; |
426cc91a | 406 | |
3cf6429a LI |
407 | if (m->err < 0) |
408 | return; | |
409 | ||
410 | n = m->trans->poll(m->trans, NULL); | |
411 | if (n < 0 || n & (POLLERR | POLLHUP | POLLNVAL)) { | |
bd238fb4 | 412 | P9_DPRINTK(P9_DEBUG_MUX, "error mux %p err %d\n", m, n); |
3cf6429a LI |
413 | if (n >= 0) |
414 | n = -ECONNRESET; | |
bd238fb4 | 415 | p9_conn_cancel(m, n); |
3cf6429a LI |
416 | } |
417 | ||
418 | if (n & POLLIN) { | |
419 | set_bit(Rpending, &m->wsched); | |
bd238fb4 | 420 | P9_DPRINTK(P9_DEBUG_MUX, "mux %p can read\n", m); |
3cf6429a | 421 | if (!test_and_set_bit(Rworksched, &m->wsched)) { |
bd238fb4 LI |
422 | P9_DPRINTK(P9_DEBUG_MUX, "schedule read work %p\n", m); |
423 | queue_work(p9_mux_wq, &m->rq); | |
3cf6429a LI |
424 | } |
425 | } | |
426cc91a | 426 | |
3cf6429a LI |
427 | if (n & POLLOUT) { |
428 | set_bit(Wpending, &m->wsched); | |
bd238fb4 | 429 | P9_DPRINTK(P9_DEBUG_MUX, "mux %p can write\n", m); |
3cf6429a LI |
430 | if ((m->wsize || !list_empty(&m->unsent_req_list)) |
431 | && !test_and_set_bit(Wworksched, &m->wsched)) { | |
bd238fb4 LI |
432 | P9_DPRINTK(P9_DEBUG_MUX, "schedule write work %p\n", m); |
433 | queue_work(p9_mux_wq, &m->wq); | |
3cf6429a LI |
434 | } |
435 | } | |
426cc91a EVH |
436 | } |
437 | ||
438 | /** | |
bd238fb4 | 439 | * p9_poll_proc - polls all v9fs transports for new events and queues |
3cf6429a | 440 | * the appropriate work to the work queue |
426cc91a | 441 | */ |
bd238fb4 | 442 | static int p9_poll_proc(void *a) |
426cc91a | 443 | { |
bd238fb4 LI |
444 | struct p9_conn *m, *mtmp; |
445 | struct p9_mux_poll_task *vpt; | |
426cc91a | 446 | |
3cf6429a | 447 | vpt = a; |
bd238fb4 | 448 | P9_DPRINTK(P9_DEBUG_MUX, "start %p %p\n", current, vpt); |
3cf6429a LI |
449 | while (!kthread_should_stop()) { |
450 | set_current_state(TASK_INTERRUPTIBLE); | |
426cc91a | 451 | |
3cf6429a | 452 | list_for_each_entry_safe(m, mtmp, &vpt->mux_list, mux_list) { |
bd238fb4 | 453 | p9_poll_mux(m); |
3cf6429a LI |
454 | } |
455 | ||
bd238fb4 | 456 | P9_DPRINTK(P9_DEBUG_MUX, "sleeping...\n"); |
3cf6429a LI |
457 | schedule_timeout(SCHED_TIMEOUT * HZ); |
458 | } | |
cb2e87a6 | 459 | |
3cf6429a | 460 | __set_current_state(TASK_RUNNING); |
bd238fb4 | 461 | P9_DPRINTK(P9_DEBUG_MUX, "finish\n"); |
3cf6429a LI |
462 | return 0; |
463 | } | |
426cc91a | 464 | |
3cf6429a | 465 | /** |
bd238fb4 | 466 | * p9_write_work - called when a transport can send some data |
3cf6429a | 467 | */ |
bd238fb4 | 468 | static void p9_write_work(struct work_struct *work) |
3cf6429a LI |
469 | { |
470 | int n, err; | |
bd238fb4 LI |
471 | struct p9_conn *m; |
472 | struct p9_req *req; | |
426cc91a | 473 | |
bd238fb4 | 474 | m = container_of(work, struct p9_conn, wq); |
426cc91a | 475 | |
3cf6429a LI |
476 | if (m->err < 0) { |
477 | clear_bit(Wworksched, &m->wsched); | |
478 | return; | |
426cc91a EVH |
479 | } |
480 | ||
3cf6429a LI |
481 | if (!m->wsize) { |
482 | if (list_empty(&m->unsent_req_list)) { | |
483 | clear_bit(Wworksched, &m->wsched); | |
484 | return; | |
426cc91a EVH |
485 | } |
486 | ||
3cf6429a | 487 | spin_lock(&m->lock); |
034b91a3 | 488 | again: |
bd238fb4 | 489 | req = list_entry(m->unsent_req_list.next, struct p9_req, |
531b1094 LI |
490 | req_list); |
491 | list_move_tail(&req->req_list, &m->req_list); | |
034b91a3 LI |
492 | if (req->err == ERREQFLUSH) |
493 | goto again; | |
494 | ||
531b1094 LI |
495 | m->wbuf = req->tcall->sdata; |
496 | m->wsize = req->tcall->size; | |
3cf6429a LI |
497 | m->wpos = 0; |
498 | spin_unlock(&m->lock); | |
426cc91a EVH |
499 | } |
500 | ||
bd238fb4 LI |
501 | P9_DPRINTK(P9_DEBUG_MUX, "mux %p pos %d size %d\n", m, m->wpos, |
502 | m->wsize); | |
3cf6429a LI |
503 | clear_bit(Wpending, &m->wsched); |
504 | err = m->trans->write(m->trans, m->wbuf + m->wpos, m->wsize - m->wpos); | |
bd238fb4 | 505 | P9_DPRINTK(P9_DEBUG_MUX, "mux %p sent %d bytes\n", m, err); |
3cf6429a LI |
506 | if (err == -EAGAIN) { |
507 | clear_bit(Wworksched, &m->wsched); | |
508 | return; | |
509 | } | |
510 | ||
1d6b5602 | 511 | if (err < 0) |
3cf6429a | 512 | goto error; |
1d6b5602 LI |
513 | else if (err == 0) { |
514 | err = -EREMOTEIO; | |
515 | goto error; | |
516 | } | |
3cf6429a LI |
517 | |
518 | m->wpos += err; | |
519 | if (m->wpos == m->wsize) | |
520 | m->wpos = m->wsize = 0; | |
521 | ||
522 | if (m->wsize == 0 && !list_empty(&m->unsent_req_list)) { | |
523 | if (test_and_clear_bit(Wpending, &m->wsched)) | |
524 | n = POLLOUT; | |
525 | else | |
526 | n = m->trans->poll(m->trans, NULL); | |
527 | ||
528 | if (n & POLLOUT) { | |
bd238fb4 LI |
529 | P9_DPRINTK(P9_DEBUG_MUX, "schedule write work %p\n", m); |
530 | queue_work(p9_mux_wq, &m->wq); | |
3cf6429a LI |
531 | } else |
532 | clear_bit(Wworksched, &m->wsched); | |
533 | } else | |
534 | clear_bit(Wworksched, &m->wsched); | |
426cc91a | 535 | |
3cf6429a LI |
536 | return; |
537 | ||
bd238fb4 LI |
538 | error: |
539 | p9_conn_cancel(m, err); | |
3cf6429a | 540 | clear_bit(Wworksched, &m->wsched); |
426cc91a EVH |
541 | } |
542 | ||
bd238fb4 | 543 | static void process_request(struct p9_conn *m, struct p9_req *req) |
322b329a | 544 | { |
41e5a6ac | 545 | int ecode; |
bd238fb4 | 546 | struct p9_str *ename; |
3cf6429a | 547 | |
bd238fb4 | 548 | if (!req->err && req->rcall->id == P9_RERROR) { |
3cf6429a | 549 | ecode = req->rcall->params.rerror.errno; |
531b1094 | 550 | ename = &req->rcall->params.rerror.error; |
322b329a | 551 | |
bd238fb4 LI |
552 | P9_DPRINTK(P9_DEBUG_MUX, "Rerror %.*s\n", ename->len, |
553 | ename->str); | |
3cf6429a LI |
554 | |
555 | if (*m->extended) | |
556 | req->err = -ecode; | |
557 | ||
558 | if (!req->err) { | |
bd238fb4 | 559 | req->err = p9_errstr2errno(ename->str, ename->len); |
3cf6429a LI |
560 | |
561 | if (!req->err) { /* string match failed */ | |
531b1094 | 562 | PRINT_FCALL_ERROR("unknown error", req->rcall); |
3cf6429a LI |
563 | } |
564 | ||
565 | if (!req->err) | |
566 | req->err = -ESERVERFAULT; | |
567 | } | |
568 | } else if (req->tcall && req->rcall->id != req->tcall->id + 1) { | |
bd238fb4 LI |
569 | P9_DPRINTK(P9_DEBUG_ERROR, |
570 | "fcall mismatch: expected %d, got %d\n", | |
571 | req->tcall->id + 1, req->rcall->id); | |
3cf6429a LI |
572 | if (!req->err) |
573 | req->err = -EIO; | |
322b329a | 574 | } |
322b329a EVH |
575 | } |
576 | ||
426cc91a | 577 | /** |
bd238fb4 | 578 | * p9_read_work - called when there is some data to be read from a transport |
426cc91a | 579 | */ |
bd238fb4 | 580 | static void p9_read_work(struct work_struct *work) |
426cc91a | 581 | { |
531b1094 | 582 | int n, err; |
bd238fb4 LI |
583 | struct p9_conn *m; |
584 | struct p9_req *req, *rptr, *rreq; | |
585 | struct p9_fcall *rcall; | |
531b1094 | 586 | char *rbuf; |
3cf6429a | 587 | |
bd238fb4 | 588 | m = container_of(work, struct p9_conn, rq); |
3cf6429a LI |
589 | |
590 | if (m->err < 0) | |
591 | return; | |
592 | ||
593 | rcall = NULL; | |
bd238fb4 | 594 | P9_DPRINTK(P9_DEBUG_MUX, "start mux %p pos %d\n", m, m->rpos); |
531b1094 LI |
595 | |
596 | if (!m->rcall) { | |
597 | m->rcall = | |
bd238fb4 | 598 | kmalloc(sizeof(struct p9_fcall) + m->msize, GFP_KERNEL); |
531b1094 LI |
599 | if (!m->rcall) { |
600 | err = -ENOMEM; | |
601 | goto error; | |
602 | } | |
603 | ||
bd238fb4 | 604 | m->rbuf = (char *)m->rcall + sizeof(struct p9_fcall); |
531b1094 LI |
605 | m->rpos = 0; |
606 | } | |
607 | ||
3cf6429a LI |
608 | clear_bit(Rpending, &m->wsched); |
609 | err = m->trans->read(m->trans, m->rbuf + m->rpos, m->msize - m->rpos); | |
bd238fb4 | 610 | P9_DPRINTK(P9_DEBUG_MUX, "mux %p got %d bytes\n", m, err); |
3cf6429a LI |
611 | if (err == -EAGAIN) { |
612 | clear_bit(Rworksched, &m->wsched); | |
613 | return; | |
614 | } | |
426cc91a | 615 | |
3cf6429a LI |
616 | if (err <= 0) |
617 | goto error; | |
426cc91a | 618 | |
3cf6429a LI |
619 | m->rpos += err; |
620 | while (m->rpos > 4) { | |
621 | n = le32_to_cpu(*(__le32 *) m->rbuf); | |
622 | if (n >= m->msize) { | |
bd238fb4 | 623 | P9_DPRINTK(P9_DEBUG_ERROR, |
3cf6429a LI |
624 | "requested packet size too big: %d\n", n); |
625 | err = -EIO; | |
626 | goto error; | |
627 | } | |
628 | ||
629 | if (m->rpos < n) | |
426cc91a | 630 | break; |
3cf6429a | 631 | |
531b1094 | 632 | err = |
bd238fb4 | 633 | p9_deserialize_fcall(m->rbuf, n, m->rcall, *m->extended); |
cb2e87a6 | 634 | if (err < 0) { |
3cf6429a | 635 | goto error; |
426cc91a EVH |
636 | } |
637 | ||
bd238fb4 LI |
638 | #ifdef CONFIG_NET_9P_DEBUG |
639 | if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) { | |
5174fdab LI |
640 | char buf[150]; |
641 | ||
bd238fb4 | 642 | p9_printfcall(buf, sizeof(buf), m->rcall, |
5174fdab LI |
643 | *m->extended); |
644 | printk(KERN_NOTICE ">>> %p %s\n", m, buf); | |
645 | } | |
bd238fb4 | 646 | #endif |
5174fdab | 647 | |
531b1094 LI |
648 | rcall = m->rcall; |
649 | rbuf = m->rbuf; | |
650 | if (m->rpos > n) { | |
bd238fb4 | 651 | m->rcall = kmalloc(sizeof(struct p9_fcall) + m->msize, |
531b1094 LI |
652 | GFP_KERNEL); |
653 | if (!m->rcall) { | |
654 | err = -ENOMEM; | |
655 | goto error; | |
656 | } | |
657 | ||
bd238fb4 | 658 | m->rbuf = (char *)m->rcall + sizeof(struct p9_fcall); |
531b1094 LI |
659 | memmove(m->rbuf, rbuf + n, m->rpos - n); |
660 | m->rpos -= n; | |
661 | } else { | |
662 | m->rcall = NULL; | |
663 | m->rbuf = NULL; | |
664 | m->rpos = 0; | |
665 | } | |
666 | ||
bd238fb4 LI |
667 | P9_DPRINTK(P9_DEBUG_MUX, "mux %p fcall id %d tag %d\n", m, |
668 | rcall->id, rcall->tag); | |
3cf6429a LI |
669 | |
670 | req = NULL; | |
671 | spin_lock(&m->lock); | |
672 | list_for_each_entry_safe(rreq, rptr, &m->req_list, req_list) { | |
673 | if (rreq->tag == rcall->tag) { | |
674 | req = rreq; | |
41e5a6ac LI |
675 | if (req->flush != Flushing) |
676 | list_del(&req->req_list); | |
3cf6429a | 677 | break; |
426cc91a EVH |
678 | } |
679 | } | |
41e5a6ac | 680 | spin_unlock(&m->lock); |
426cc91a | 681 | |
41e5a6ac LI |
682 | if (req) { |
683 | req->rcall = rcall; | |
684 | process_request(m, req); | |
685 | ||
686 | if (req->flush != Flushing) { | |
687 | if (req->cb) | |
688 | (*req->cb) (req, req->cba); | |
689 | else | |
690 | kfree(req->rcall); | |
691 | ||
692 | wake_up(&m->equeue); | |
693 | } | |
694 | } else { | |
bd238fb4 LI |
695 | if (err >= 0 && rcall->id != P9_RFLUSH) |
696 | P9_DPRINTK(P9_DEBUG_ERROR, | |
697 | "unexpected response mux %p id %d tag %d\n", | |
698 | m, rcall->id, rcall->tag); | |
426cc91a EVH |
699 | kfree(rcall); |
700 | } | |
426cc91a EVH |
701 | } |
702 | ||
3cf6429a LI |
703 | if (!list_empty(&m->req_list)) { |
704 | if (test_and_clear_bit(Rpending, &m->wsched)) | |
705 | n = POLLIN; | |
706 | else | |
707 | n = m->trans->poll(m->trans, NULL); | |
708 | ||
709 | if (n & POLLIN) { | |
bd238fb4 LI |
710 | P9_DPRINTK(P9_DEBUG_MUX, "schedule read work %p\n", m); |
711 | queue_work(p9_mux_wq, &m->rq); | |
3cf6429a LI |
712 | } else |
713 | clear_bit(Rworksched, &m->wsched); | |
714 | } else | |
715 | clear_bit(Rworksched, &m->wsched); | |
426cc91a | 716 | |
3cf6429a | 717 | return; |
426cc91a | 718 | |
bd238fb4 LI |
719 | error: |
720 | p9_conn_cancel(m, err); | |
3cf6429a | 721 | clear_bit(Rworksched, &m->wsched); |
426cc91a EVH |
722 | } |
723 | ||
724 | /** | |
bd238fb4 | 725 | * p9_send_request - send 9P request |
3cf6429a LI |
726 | * The function can sleep until the request is scheduled for sending. |
727 | * The function can be interrupted. Return from the function is not | |
d6e05edc | 728 | * a guarantee that the request is sent successfully. Can return errors |
3cf6429a | 729 | * that can be retrieved by PTR_ERR macros. |
426cc91a | 730 | * |
3cf6429a LI |
731 | * @m: mux data |
732 | * @tc: request to be sent | |
733 | * @cb: callback function to call when response is received | |
734 | * @cba: parameter to pass to the callback function | |
426cc91a | 735 | */ |
bd238fb4 LI |
736 | static struct p9_req *p9_send_request(struct p9_conn *m, |
737 | struct p9_fcall *tc, | |
738 | p9_conn_req_callback cb, void *cba) | |
3cf6429a LI |
739 | { |
740 | int n; | |
bd238fb4 | 741 | struct p9_req *req; |
3cf6429a | 742 | |
bd238fb4 | 743 | P9_DPRINTK(P9_DEBUG_MUX, "mux %p task %p tcall %p id %d\n", m, current, |
3cf6429a LI |
744 | tc, tc->id); |
745 | if (m->err < 0) | |
746 | return ERR_PTR(m->err); | |
747 | ||
bd238fb4 | 748 | req = kmalloc(sizeof(struct p9_req), GFP_KERNEL); |
3cf6429a LI |
749 | if (!req) |
750 | return ERR_PTR(-ENOMEM); | |
426cc91a | 751 | |
bd238fb4 LI |
752 | if (tc->id == P9_TVERSION) |
753 | n = P9_NOTAG; | |
3cf6429a | 754 | else |
bd238fb4 | 755 | n = p9_mux_get_tag(m); |
3cf6429a LI |
756 | |
757 | if (n < 0) | |
758 | return ERR_PTR(-ENOMEM); | |
759 | ||
bd238fb4 LI |
760 | p9_set_tag(tc, n); |
761 | ||
762 | #ifdef CONFIG_NET_9P_DEBUG | |
763 | if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) { | |
5174fdab LI |
764 | char buf[150]; |
765 | ||
bd238fb4 | 766 | p9_printfcall(buf, sizeof(buf), tc, *m->extended); |
5174fdab LI |
767 | printk(KERN_NOTICE "<<< %p %s\n", m, buf); |
768 | } | |
bd238fb4 | 769 | #endif |
5174fdab | 770 | |
41e5a6ac | 771 | spin_lock_init(&req->lock); |
3cf6429a LI |
772 | req->tag = n; |
773 | req->tcall = tc; | |
774 | req->rcall = NULL; | |
775 | req->err = 0; | |
776 | req->cb = cb; | |
777 | req->cba = cba; | |
41e5a6ac | 778 | req->flush = None; |
3cf6429a LI |
779 | |
780 | spin_lock(&m->lock); | |
781 | list_add_tail(&req->req_list, &m->unsent_req_list); | |
782 | spin_unlock(&m->lock); | |
783 | ||
784 | if (test_and_clear_bit(Wpending, &m->wsched)) | |
785 | n = POLLOUT; | |
786 | else | |
787 | n = m->trans->poll(m->trans, NULL); | |
788 | ||
789 | if (n & POLLOUT && !test_and_set_bit(Wworksched, &m->wsched)) | |
bd238fb4 | 790 | queue_work(p9_mux_wq, &m->wq); |
3cf6429a LI |
791 | |
792 | return req; | |
793 | } | |
794 | ||
bd238fb4 | 795 | static void p9_mux_free_request(struct p9_conn *m, struct p9_req *req) |
41e5a6ac | 796 | { |
bd238fb4 | 797 | p9_mux_put_tag(m, req->tag); |
41e5a6ac LI |
798 | kfree(req); |
799 | } | |
800 | ||
bd238fb4 | 801 | static void p9_mux_flush_cb(struct p9_req *freq, void *a) |
426cc91a | 802 | { |
bd238fb4 | 803 | p9_conn_req_callback cb; |
3cf6429a | 804 | int tag; |
bd238fb4 LI |
805 | struct p9_conn *m; |
806 | struct p9_req *req, *rreq, *rptr; | |
3cf6429a LI |
807 | |
808 | m = a; | |
bd238fb4 | 809 | P9_DPRINTK(P9_DEBUG_MUX, "mux %p tc %p rc %p err %d oldtag %d\n", m, |
41e5a6ac LI |
810 | freq->tcall, freq->rcall, freq->err, |
811 | freq->tcall->params.tflush.oldtag); | |
3cf6429a LI |
812 | |
813 | spin_lock(&m->lock); | |
814 | cb = NULL; | |
41e5a6ac LI |
815 | tag = freq->tcall->params.tflush.oldtag; |
816 | req = NULL; | |
817 | list_for_each_entry_safe(rreq, rptr, &m->req_list, req_list) { | |
818 | if (rreq->tag == tag) { | |
819 | req = rreq; | |
3cf6429a | 820 | list_del(&req->req_list); |
3cf6429a LI |
821 | break; |
822 | } | |
823 | } | |
41e5a6ac | 824 | spin_unlock(&m->lock); |
3cf6429a | 825 | |
41e5a6ac LI |
826 | if (req) { |
827 | spin_lock(&req->lock); | |
828 | req->flush = Flushed; | |
829 | spin_unlock(&req->lock); | |
830 | ||
831 | if (req->cb) | |
832 | (*req->cb) (req, req->cba); | |
833 | else | |
834 | kfree(req->rcall); | |
835 | ||
836 | wake_up(&m->equeue); | |
837 | } | |
3cf6429a | 838 | |
41e5a6ac LI |
839 | kfree(freq->tcall); |
840 | kfree(freq->rcall); | |
bd238fb4 | 841 | p9_mux_free_request(m, freq); |
3cf6429a LI |
842 | } |
843 | ||
41e5a6ac | 844 | static int |
bd238fb4 | 845 | p9_mux_flush_request(struct p9_conn *m, struct p9_req *req) |
3cf6429a | 846 | { |
bd238fb4 LI |
847 | struct p9_fcall *fc; |
848 | struct p9_req *rreq, *rptr; | |
3cf6429a | 849 | |
bd238fb4 | 850 | P9_DPRINTK(P9_DEBUG_MUX, "mux %p req %p tag %d\n", m, req, req->tag); |
3cf6429a | 851 | |
41e5a6ac LI |
852 | /* if a response was received for a request, do nothing */ |
853 | spin_lock(&req->lock); | |
854 | if (req->rcall || req->err) { | |
855 | spin_unlock(&req->lock); | |
bd238fb4 LI |
856 | P9_DPRINTK(P9_DEBUG_MUX, |
857 | "mux %p req %p response already received\n", m, req); | |
41e5a6ac LI |
858 | return 0; |
859 | } | |
860 | ||
861 | req->flush = Flushing; | |
862 | spin_unlock(&req->lock); | |
863 | ||
864 | spin_lock(&m->lock); | |
865 | /* if the request is not sent yet, just remove it from the list */ | |
866 | list_for_each_entry_safe(rreq, rptr, &m->unsent_req_list, req_list) { | |
867 | if (rreq->tag == req->tag) { | |
bd238fb4 LI |
868 | P9_DPRINTK(P9_DEBUG_MUX, |
869 | "mux %p req %p request is not sent yet\n", m, req); | |
41e5a6ac LI |
870 | list_del(&rreq->req_list); |
871 | req->flush = Flushed; | |
872 | spin_unlock(&m->lock); | |
873 | if (req->cb) | |
874 | (*req->cb) (req, req->cba); | |
875 | return 0; | |
876 | } | |
877 | } | |
878 | spin_unlock(&m->lock); | |
879 | ||
880 | clear_thread_flag(TIF_SIGPENDING); | |
bd238fb4 LI |
881 | fc = p9_create_tflush(req->tag); |
882 | p9_send_request(m, fc, p9_mux_flush_cb, m); | |
41e5a6ac | 883 | return 1; |
3cf6429a LI |
884 | } |
885 | ||
886 | static void | |
bd238fb4 | 887 | p9_conn_rpc_cb(struct p9_req *req, void *a) |
3cf6429a | 888 | { |
bd238fb4 | 889 | struct p9_mux_rpc *r; |
3cf6429a | 890 | |
bd238fb4 | 891 | P9_DPRINTK(P9_DEBUG_MUX, "req %p r %p\n", req, a); |
3cf6429a | 892 | r = a; |
41e5a6ac LI |
893 | r->rcall = req->rcall; |
894 | r->err = req->err; | |
895 | ||
bd238fb4 | 896 | if (req->flush != None && !req->err) |
41e5a6ac LI |
897 | r->err = -ERESTARTSYS; |
898 | ||
3cf6429a LI |
899 | wake_up(&r->wqueue); |
900 | } | |
901 | ||
902 | /** | |
bd238fb4 | 903 | * p9_mux_rpc - sends 9P request and waits until a response is available. |
3cf6429a LI |
904 | * The function can be interrupted. |
905 | * @m: mux data | |
906 | * @tc: request to be sent | |
907 | * @rc: pointer where a pointer to the response is stored | |
908 | */ | |
909 | int | |
bd238fb4 LI |
910 | p9_conn_rpc(struct p9_conn *m, struct p9_fcall *tc, |
911 | struct p9_fcall **rc) | |
3cf6429a | 912 | { |
41e5a6ac | 913 | int err, sigpending; |
3cf6429a | 914 | unsigned long flags; |
bd238fb4 LI |
915 | struct p9_req *req; |
916 | struct p9_mux_rpc r; | |
3cf6429a LI |
917 | |
918 | r.err = 0; | |
41e5a6ac | 919 | r.tcall = tc; |
3cf6429a LI |
920 | r.rcall = NULL; |
921 | r.m = m; | |
922 | init_waitqueue_head(&r.wqueue); | |
923 | ||
924 | if (rc) | |
925 | *rc = NULL; | |
926 | ||
41e5a6ac LI |
927 | sigpending = 0; |
928 | if (signal_pending(current)) { | |
929 | sigpending = 1; | |
930 | clear_thread_flag(TIF_SIGPENDING); | |
931 | } | |
932 | ||
bd238fb4 | 933 | req = p9_send_request(m, tc, p9_conn_rpc_cb, &r); |
3cf6429a LI |
934 | if (IS_ERR(req)) { |
935 | err = PTR_ERR(req); | |
bd238fb4 | 936 | P9_DPRINTK(P9_DEBUG_MUX, "error %d\n", err); |
41e5a6ac | 937 | return err; |
3cf6429a LI |
938 | } |
939 | ||
3cf6429a LI |
940 | err = wait_event_interruptible(r.wqueue, r.rcall != NULL || r.err < 0); |
941 | if (r.err < 0) | |
942 | err = r.err; | |
943 | ||
bd238fb4 LI |
944 | if (err == -ERESTARTSYS && m->trans->status == Connected |
945 | && m->err == 0) { | |
946 | if (p9_mux_flush_request(m, req)) { | |
41e5a6ac LI |
947 | /* wait until we get response of the flush message */ |
948 | do { | |
949 | clear_thread_flag(TIF_SIGPENDING); | |
950 | err = wait_event_interruptible(r.wqueue, | |
951 | r.rcall || r.err); | |
bd238fb4 LI |
952 | } while (!r.rcall && !r.err && err == -ERESTARTSYS && |
953 | m->trans->status == Connected && !m->err); | |
94374e7c LI |
954 | |
955 | err = -ERESTARTSYS; | |
41e5a6ac LI |
956 | } |
957 | sigpending = 1; | |
958 | } | |
3cf6429a | 959 | |
41e5a6ac | 960 | if (sigpending) { |
3cf6429a LI |
961 | spin_lock_irqsave(¤t->sighand->siglock, flags); |
962 | recalc_sigpending(); | |
963 | spin_unlock_irqrestore(¤t->sighand->siglock, flags); | |
426cc91a EVH |
964 | } |
965 | ||
41e5a6ac LI |
966 | if (rc) |
967 | *rc = r.rcall; | |
968 | else | |
3cf6429a | 969 | kfree(r.rcall); |
41e5a6ac | 970 | |
bd238fb4 | 971 | p9_mux_free_request(m, req); |
41e5a6ac LI |
972 | if (err > 0) |
973 | err = -EIO; | |
3cf6429a LI |
974 | |
975 | return err; | |
976 | } | |
bd238fb4 | 977 | EXPORT_SYMBOL(p9_conn_rpc); |
3cf6429a | 978 | |
bd238fb4 | 979 | #ifdef P9_NONBLOCK |
3cf6429a | 980 | /** |
bd238fb4 | 981 | * p9_conn_rpcnb - sends 9P request without waiting for response. |
3cf6429a LI |
982 | * @m: mux data |
983 | * @tc: request to be sent | |
984 | * @cb: callback function to be called when response arrives | |
985 | * @cba: value to pass to the callback function | |
986 | */ | |
bd238fb4 LI |
987 | int p9_conn_rpcnb(struct p9_conn *m, struct p9_fcall *tc, |
988 | p9_conn_req_callback cb, void *a) | |
3cf6429a LI |
989 | { |
990 | int err; | |
bd238fb4 | 991 | struct p9_req *req; |
3cf6429a | 992 | |
bd238fb4 | 993 | req = p9_send_request(m, tc, cb, a); |
3cf6429a LI |
994 | if (IS_ERR(req)) { |
995 | err = PTR_ERR(req); | |
bd238fb4 | 996 | P9_DPRINTK(P9_DEBUG_MUX, "error %d\n", err); |
3cf6429a LI |
997 | return PTR_ERR(req); |
998 | } | |
426cc91a | 999 | |
bd238fb4 | 1000 | P9_DPRINTK(P9_DEBUG_MUX, "mux %p tc %p tag %d\n", m, tc, req->tag); |
426cc91a EVH |
1001 | return 0; |
1002 | } | |
bd238fb4 LI |
1003 | EXPORT_SYMBOL(p9_conn_rpcnb); |
1004 | #endif /* P9_NONBLOCK */ | |
3cf6429a LI |
1005 | |
1006 | /** | |
bd238fb4 | 1007 | * p9_conn_cancel - cancel all pending requests with error |
3cf6429a LI |
1008 | * @m: mux data |
1009 | * @err: error code | |
1010 | */ | |
bd238fb4 | 1011 | void p9_conn_cancel(struct p9_conn *m, int err) |
3cf6429a | 1012 | { |
bd238fb4 | 1013 | struct p9_req *req, *rtmp; |
3cf6429a LI |
1014 | LIST_HEAD(cancel_list); |
1015 | ||
bd238fb4 | 1016 | P9_DPRINTK(P9_DEBUG_ERROR, "mux %p err %d\n", m, err); |
3cf6429a LI |
1017 | m->err = err; |
1018 | spin_lock(&m->lock); | |
1019 | list_for_each_entry_safe(req, rtmp, &m->req_list, req_list) { | |
1020 | list_move(&req->req_list, &cancel_list); | |
1021 | } | |
41e5a6ac LI |
1022 | list_for_each_entry_safe(req, rtmp, &m->unsent_req_list, req_list) { |
1023 | list_move(&req->req_list, &cancel_list); | |
1024 | } | |
3cf6429a LI |
1025 | spin_unlock(&m->lock); |
1026 | ||
1027 | list_for_each_entry_safe(req, rtmp, &cancel_list, req_list) { | |
1028 | list_del(&req->req_list); | |
1029 | if (!req->err) | |
1030 | req->err = err; | |
1031 | ||
1032 | if (req->cb) | |
41e5a6ac | 1033 | (*req->cb) (req, req->cba); |
3cf6429a LI |
1034 | else |
1035 | kfree(req->rcall); | |
3cf6429a LI |
1036 | } |
1037 | ||
1038 | wake_up(&m->equeue); | |
1039 | } | |
bd238fb4 | 1040 | EXPORT_SYMBOL(p9_conn_cancel); |
531b1094 | 1041 | |
bd238fb4 | 1042 | static u16 p9_mux_get_tag(struct p9_conn *m) |
531b1094 LI |
1043 | { |
1044 | int tag; | |
1045 | ||
bd238fb4 | 1046 | tag = p9_idpool_get(m->tagpool); |
531b1094 | 1047 | if (tag < 0) |
bd238fb4 | 1048 | return P9_NOTAG; |
531b1094 LI |
1049 | else |
1050 | return (u16) tag; | |
1051 | } | |
1052 | ||
bd238fb4 | 1053 | static void p9_mux_put_tag(struct p9_conn *m, u16 tag) |
531b1094 | 1054 | { |
bd238fb4 LI |
1055 | if (tag != P9_NOTAG && p9_idpool_check(tag, m->tagpool)) |
1056 | p9_idpool_put(tag, m->tagpool); | |
531b1094 | 1057 | } |