diff options
Diffstat (limited to 'net/9p/client.c')
-rw-r--r-- | net/9p/client.c | 265 |
1 files changed, 251 insertions, 14 deletions
diff --git a/net/9p/client.c b/net/9p/client.c index f2d07ef9e6a4..29934febecdb 100644 --- a/net/9p/client.c +++ b/net/9p/client.c | |||
@@ -55,6 +55,9 @@ static const match_table_t tokens = { | |||
55 | {Opt_err, NULL}, | 55 | {Opt_err, NULL}, |
56 | }; | 56 | }; |
57 | 57 | ||
58 | static int | ||
59 | p9_client_rpc(struct p9_client *c, struct p9_fcall *tc, struct p9_fcall **rc); | ||
60 | |||
58 | /** | 61 | /** |
59 | * v9fs_parse_options - parse mount options into session structure | 62 | * v9fs_parse_options - parse mount options into session structure |
60 | * @options: options string passed from mount | 63 | * @options: options string passed from mount |
@@ -269,6 +272,36 @@ static void p9_tag_cleanup(struct p9_client *c) | |||
269 | } | 272 | } |
270 | 273 | ||
271 | /** | 274 | /** |
275 | * p9_client_flush - flush (cancel) a request | ||
276 | * c: client state | ||
277 | * req: request to cancel | ||
278 | * | ||
279 | * This sents a flush for a particular requests and links | ||
280 | * the flush request to the original request. The current | ||
281 | * code only supports a single flush request although the protocol | ||
282 | * allows for multiple flush requests to be sent for a single request. | ||
283 | * | ||
284 | */ | ||
285 | |||
286 | static int p9_client_flush(struct p9_client *c, struct p9_req_t *req) | ||
287 | { | ||
288 | struct p9_fcall *tc, *rc = NULL; | ||
289 | int err; | ||
290 | |||
291 | P9_DPRINTK(P9_DEBUG_9P, "client %p tag %d\n", c, req->tc->tag); | ||
292 | |||
293 | tc = p9_create_tflush(req->tc->tag); | ||
294 | if (IS_ERR(tc)) | ||
295 | return PTR_ERR(tc); | ||
296 | |||
297 | err = p9_client_rpc(c, tc, &rc); | ||
298 | |||
299 | /* we don't free anything here because RPC isn't complete */ | ||
300 | |||
301 | return err; | ||
302 | } | ||
303 | |||
304 | /** | ||
272 | * p9_free_req - free a request and clean-up as necessary | 305 | * p9_free_req - free a request and clean-up as necessary |
273 | * c: client state | 306 | * c: client state |
274 | * r: request to release | 307 | * r: request to release |
@@ -289,6 +322,224 @@ void p9_free_req(struct p9_client *c, struct p9_req_t *r) | |||
289 | } | 322 | } |
290 | } | 323 | } |
291 | 324 | ||
325 | /** | ||
326 | * p9_client_cb - call back from transport to client | ||
327 | * c: client state | ||
328 | * req: request received | ||
329 | * | ||
330 | */ | ||
331 | void p9_client_cb(struct p9_client *c, struct p9_req_t *req) | ||
332 | { | ||
333 | struct p9_req_t *other_req; | ||
334 | unsigned long flags; | ||
335 | |||
336 | P9_DPRINTK(P9_DEBUG_MUX, ": %d\n", req->tc->tag); | ||
337 | |||
338 | if (req->status == REQ_STATUS_ERROR) | ||
339 | wake_up(req->wq); | ||
340 | |||
341 | if (req->tc->id == P9_TFLUSH) { /* flush receive path */ | ||
342 | P9_DPRINTK(P9_DEBUG_MUX, "flush: %d\n", req->tc->tag); | ||
343 | spin_lock_irqsave(&c->lock, flags); | ||
344 | other_req = p9_tag_lookup(c, req->tc->params.tflush.oldtag); | ||
345 | if (other_req->flush_tag != req->tc->tag) /* stale flush */ | ||
346 | spin_unlock_irqrestore(&c->lock, flags); | ||
347 | else { | ||
348 | BUG_ON(other_req->status != REQ_STATUS_FLSH); | ||
349 | other_req->status = REQ_STATUS_FLSHD; | ||
350 | spin_unlock_irqrestore(&c->lock, flags); | ||
351 | wake_up(other_req->wq); | ||
352 | } | ||
353 | p9_free_req(c, req); | ||
354 | } else { /* normal receive path */ | ||
355 | P9_DPRINTK(P9_DEBUG_MUX, "normal: %d\n", req->tc->tag); | ||
356 | spin_lock_irqsave(&c->lock, flags); | ||
357 | if (req->status != REQ_STATUS_FLSHD) | ||
358 | req->status = REQ_STATUS_RCVD; | ||
359 | req->flush_tag = P9_NOTAG; | ||
360 | spin_unlock_irqrestore(&c->lock, flags); | ||
361 | wake_up(req->wq); | ||
362 | P9_DPRINTK(P9_DEBUG_MUX, "wakeup: %d\n", req->tc->tag); | ||
363 | } | ||
364 | } | ||
365 | EXPORT_SYMBOL(p9_client_cb); | ||
366 | |||
367 | /** | ||
368 | * p9_client_rpc - issue a request and wait for a response | ||
369 | * @c: client session | ||
370 | * @tc: &p9_fcall request to transmit | ||
371 | * @rc: &p9_fcall to put reponse into | ||
372 | * | ||
373 | * Returns 0 on success, error code on failure | ||
374 | */ | ||
375 | |||
376 | static int | ||
377 | p9_client_rpc(struct p9_client *c, struct p9_fcall *tc, struct p9_fcall **rc) | ||
378 | { | ||
379 | int tag, err, size; | ||
380 | char *rdata; | ||
381 | struct p9_req_t *req; | ||
382 | unsigned long flags; | ||
383 | int sigpending; | ||
384 | int flushed = 0; | ||
385 | |||
386 | P9_DPRINTK(P9_DEBUG_9P, "client %p tc %p rc %p\n", c, tc, rc); | ||
387 | |||
388 | if (c->status != Connected) | ||
389 | return -EIO; | ||
390 | |||
391 | if (signal_pending(current)) { | ||
392 | sigpending = 1; | ||
393 | clear_thread_flag(TIF_SIGPENDING); | ||
394 | } else | ||
395 | sigpending = 0; | ||
396 | |||
397 | tag = P9_NOTAG; | ||
398 | if (tc->id != P9_TVERSION) { | ||
399 | tag = p9_idpool_get(c->tagpool); | ||
400 | if (tag < 0) | ||
401 | return -ENOMEM; | ||
402 | } | ||
403 | |||
404 | req = p9_tag_alloc(c, tag); | ||
405 | |||
406 | /* if this is a flush request, backlink flush request now to | ||
407 | * avoid race conditions later. */ | ||
408 | if (tc->id == P9_TFLUSH) { | ||
409 | struct p9_req_t *other_req = | ||
410 | p9_tag_lookup(c, tc->params.tflush.oldtag); | ||
411 | if (other_req->status == REQ_STATUS_FLSH) | ||
412 | other_req->flush_tag = tag; | ||
413 | } | ||
414 | |||
415 | p9_set_tag(tc, tag); | ||
416 | |||
417 | /* | ||
418 | * if client passed in a pre-allocated response fcall struct | ||
419 | * then we just use that, otherwise we allocate one. | ||
420 | */ | ||
421 | |||
422 | if (rc == NULL) | ||
423 | req->rc = NULL; | ||
424 | else | ||
425 | req->rc = *rc; | ||
426 | if (req->rc == NULL) { | ||
427 | req->rc = kmalloc(sizeof(struct p9_fcall) + c->msize, | ||
428 | GFP_KERNEL); | ||
429 | if (!req->rc) { | ||
430 | err = -ENOMEM; | ||
431 | p9_idpool_put(tag, c->tagpool); | ||
432 | p9_free_req(c, req); | ||
433 | goto reterr; | ||
434 | } | ||
435 | *rc = req->rc; | ||
436 | } | ||
437 | |||
438 | rdata = (char *)req->rc+sizeof(struct p9_fcall); | ||
439 | |||
440 | req->tc = tc; | ||
441 | P9_DPRINTK(P9_DEBUG_9P, "request: tc: %p rc: %p\n", req->tc, req->rc); | ||
442 | |||
443 | err = c->trans_mod->request(c, req); | ||
444 | if (err < 0) { | ||
445 | c->status = Disconnected; | ||
446 | goto reterr; | ||
447 | } | ||
448 | |||
449 | /* if it was a flush we just transmitted, return our tag */ | ||
450 | if (tc->id == P9_TFLUSH) | ||
451 | return 0; | ||
452 | again: | ||
453 | P9_DPRINTK(P9_DEBUG_9P, "wait %p tag: %d\n", req->wq, tag); | ||
454 | err = wait_event_interruptible(*req->wq, | ||
455 | req->status >= REQ_STATUS_RCVD); | ||
456 | P9_DPRINTK(P9_DEBUG_9P, "wait %p tag: %d returned %d (flushed=%d)\n", | ||
457 | req->wq, tag, err, flushed); | ||
458 | |||
459 | if (req->status == REQ_STATUS_ERROR) { | ||
460 | P9_DPRINTK(P9_DEBUG_9P, "req_status error %d\n", req->t_err); | ||
461 | err = req->t_err; | ||
462 | } else if (err == -ERESTARTSYS && flushed) { | ||
463 | P9_DPRINTK(P9_DEBUG_9P, "flushed - going again\n"); | ||
464 | goto again; | ||
465 | } else if (req->status == REQ_STATUS_FLSHD) { | ||
466 | P9_DPRINTK(P9_DEBUG_9P, "flushed - erestartsys\n"); | ||
467 | err = -ERESTARTSYS; | ||
468 | } | ||
469 | |||
470 | if ((err == -ERESTARTSYS) && (c->status == Connected) && (!flushed)) { | ||
471 | P9_DPRINTK(P9_DEBUG_9P, "flushing\n"); | ||
472 | spin_lock_irqsave(&c->lock, flags); | ||
473 | if (req->status == REQ_STATUS_SENT) | ||
474 | req->status = REQ_STATUS_FLSH; | ||
475 | spin_unlock_irqrestore(&c->lock, flags); | ||
476 | sigpending = 1; | ||
477 | flushed = 1; | ||
478 | clear_thread_flag(TIF_SIGPENDING); | ||
479 | |||
480 | if (c->trans_mod->cancel(c, req)) { | ||
481 | err = p9_client_flush(c, req); | ||
482 | if (err == 0) | ||
483 | goto again; | ||
484 | } | ||
485 | } | ||
486 | |||
487 | if (sigpending) { | ||
488 | spin_lock_irqsave(¤t->sighand->siglock, flags); | ||
489 | recalc_sigpending(); | ||
490 | spin_unlock_irqrestore(¤t->sighand->siglock, flags); | ||
491 | } | ||
492 | |||
493 | if (err < 0) | ||
494 | goto reterr; | ||
495 | |||
496 | size = le32_to_cpu(*(__le32 *) rdata); | ||
497 | |||
498 | err = p9_deserialize_fcall(rdata, size, req->rc, c->dotu); | ||
499 | if (err < 0) { | ||
500 | P9_DPRINTK(P9_DEBUG_9P, | ||
501 | "9p debug: client rpc deserialize returned %d\n", err); | ||
502 | goto reterr; | ||
503 | } | ||
504 | |||
505 | #ifdef CONFIG_NET_9P_DEBUG | ||
506 | if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) { | ||
507 | char buf[150]; | ||
508 | |||
509 | p9_printfcall(buf, sizeof(buf), req->rc, c->dotu); | ||
510 | printk(KERN_NOTICE ">>> %p %s\n", c, buf); | ||
511 | } | ||
512 | #endif | ||
513 | |||
514 | if (req->rc->id == P9_RERROR) { | ||
515 | int ecode = req->rc->params.rerror.errno; | ||
516 | struct p9_str *ename = &req->rc->params.rerror.error; | ||
517 | |||
518 | P9_DPRINTK(P9_DEBUG_MUX, "Rerror %.*s\n", ename->len, | ||
519 | ename->str); | ||
520 | |||
521 | if (c->dotu) | ||
522 | err = -ecode; | ||
523 | |||
524 | if (!err) { | ||
525 | err = p9_errstr2errno(ename->str, ename->len); | ||
526 | |||
527 | /* string match failed */ | ||
528 | if (!err) { | ||
529 | PRINT_FCALL_ERROR("unknown error", req->rc); | ||
530 | err = -ESERVERFAULT; | ||
531 | } | ||
532 | } | ||
533 | } else | ||
534 | err = 0; | ||
535 | |||
536 | reterr: | ||
537 | p9_free_req(c, req); | ||
538 | |||
539 | P9_DPRINTK(P9_DEBUG_9P, "returning %d\n", err); | ||
540 | return err; | ||
541 | } | ||
542 | |||
292 | static struct p9_fid *p9_fid_create(struct p9_client *clnt) | 543 | static struct p9_fid *p9_fid_create(struct p9_client *clnt) |
293 | { | 544 | { |
294 | int err; | 545 | int err; |
@@ -339,20 +590,6 @@ static void p9_fid_destroy(struct p9_fid *fid) | |||
339 | kfree(fid); | 590 | kfree(fid); |
340 | } | 591 | } |
341 | 592 | ||
342 | /** | ||
343 | * p9_client_rpc - sends 9P request and waits until a response is available. | ||
344 | * The function can be interrupted. | ||
345 | * @c: client data | ||
346 | * @tc: request to be sent | ||
347 | * @rc: pointer where a pointer to the response is stored | ||
348 | */ | ||
349 | int | ||
350 | p9_client_rpc(struct p9_client *c, struct p9_fcall *tc, | ||
351 | struct p9_fcall **rc) | ||
352 | { | ||
353 | return c->trans_mod->rpc(c, tc, rc); | ||
354 | } | ||
355 | |||
356 | struct p9_client *p9_client_create(const char *dev_name, char *options) | 593 | struct p9_client *p9_client_create(const char *dev_name, char *options) |
357 | { | 594 | { |
358 | int err, n; | 595 | int err, n; |