diff options
author | Jan Kiszka <jan.kiszka@web.de> | 2010-02-08 05:12:36 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-16 19:01:32 -0500 |
commit | a11ef7be8e982426e9fbbfc84fa0c01d23ce05c3 (patch) | |
tree | ba8e293db3a4004100bc5f0608e5cfbf1e0bcfc5 /drivers/isdn | |
parent | b75b2eedcbf6458c68713c772c2ade83ab7a55f0 (diff) |
CAPI: Rework capiminor RX handler
Avoid re-queuing skbs unless the error detected in handle_recv_skb is
expected to be recoverable such as lacking memory, a full CAPI queue, a
full TTY input buffer, or a not yet existing TTY.
Signed-off-by: Jan Kiszka <jan.kiszka@web.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/isdn')
-rw-r--r-- | drivers/isdn/capi/capi.c | 60 |
1 files changed, 38 insertions, 22 deletions
diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 554fa1b36d13..c5c54fab50eb 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c | |||
@@ -436,15 +436,13 @@ gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb) | |||
436 | 436 | ||
437 | static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) | 437 | static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) |
438 | { | 438 | { |
439 | unsigned int datalen = skb->len - CAPIMSG_LEN(skb->data); | ||
439 | struct tty_struct *tty; | 440 | struct tty_struct *tty; |
440 | struct sk_buff *nskb; | 441 | struct sk_buff *nskb; |
441 | int datalen; | ||
442 | u16 errcode, datahandle; | 442 | u16 errcode, datahandle; |
443 | struct tty_ldisc *ld; | 443 | struct tty_ldisc *ld; |
444 | int ret = -1; | 444 | int ret = -1; |
445 | 445 | ||
446 | datalen = skb->len - CAPIMSG_LEN(skb->data); | ||
447 | |||
448 | tty = tty_port_tty_get(&mp->port); | 446 | tty = tty_port_tty_get(&mp->port); |
449 | if (!tty) { | 447 | if (!tty) { |
450 | #ifdef _DEBUG_DATAFLOW | 448 | #ifdef _DEBUG_DATAFLOW |
@@ -454,50 +452,68 @@ static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) | |||
454 | } | 452 | } |
455 | 453 | ||
456 | ld = tty_ldisc_ref(tty); | 454 | ld = tty_ldisc_ref(tty); |
457 | if (!ld) | 455 | if (!ld) { |
458 | goto out1; | 456 | /* fatal error, do not requeue */ |
457 | ret = 0; | ||
458 | kfree_skb(skb); | ||
459 | goto deref_tty; | ||
460 | } | ||
459 | 461 | ||
460 | if (ld->ops->receive_buf == NULL) { | 462 | if (ld->ops->receive_buf == NULL) { |
461 | #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) | 463 | #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) |
462 | printk(KERN_DEBUG "capi: ldisc has no receive_buf function\n"); | 464 | printk(KERN_DEBUG "capi: ldisc has no receive_buf function\n"); |
463 | #endif | 465 | #endif |
464 | goto out2; | 466 | /* fatal error, do not requeue */ |
467 | goto free_skb; | ||
465 | } | 468 | } |
466 | if (mp->ttyinstop) { | 469 | if (mp->ttyinstop) { |
467 | #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) | 470 | #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) |
468 | printk(KERN_DEBUG "capi: recv tty throttled\n"); | 471 | printk(KERN_DEBUG "capi: recv tty throttled\n"); |
469 | #endif | 472 | #endif |
470 | goto out2; | 473 | goto deref_ldisc; |
471 | } | 474 | } |
475 | |||
472 | if (tty->receive_room < datalen) { | 476 | if (tty->receive_room < datalen) { |
473 | #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) | 477 | #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) |
474 | printk(KERN_DEBUG "capi: no room in tty\n"); | 478 | printk(KERN_DEBUG "capi: no room in tty\n"); |
475 | #endif | 479 | #endif |
476 | goto out2; | 480 | goto deref_ldisc; |
477 | } | 481 | } |
478 | if ((nskb = gen_data_b3_resp_for(mp, skb)) == NULL) { | 482 | |
483 | nskb = gen_data_b3_resp_for(mp, skb); | ||
484 | if (!nskb) { | ||
479 | printk(KERN_ERR "capi: gen_data_b3_resp failed\n"); | 485 | printk(KERN_ERR "capi: gen_data_b3_resp failed\n"); |
480 | goto out2; | 486 | goto deref_ldisc; |
481 | } | 487 | } |
482 | datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4); | 488 | |
489 | datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4); | ||
490 | |||
483 | errcode = capi20_put_message(mp->ap, nskb); | 491 | errcode = capi20_put_message(mp->ap, nskb); |
484 | if (errcode != CAPI_NOERROR) { | 492 | |
493 | if (errcode == CAPI_NOERROR) { | ||
494 | skb_pull(skb, CAPIMSG_LEN(skb->data)); | ||
495 | #ifdef _DEBUG_DATAFLOW | ||
496 | printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n", | ||
497 | datahandle, skb->len); | ||
498 | #endif | ||
499 | ld->ops->receive_buf(tty, skb->data, NULL, skb->len); | ||
500 | } else { | ||
485 | printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n", | 501 | printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n", |
486 | errcode); | 502 | errcode); |
487 | kfree_skb(nskb); | 503 | kfree_skb(nskb); |
488 | goto out2; | 504 | |
505 | if (errcode == CAPI_SENDQUEUEFULL) | ||
506 | goto deref_ldisc; | ||
489 | } | 507 | } |
490 | (void)skb_pull(skb, CAPIMSG_LEN(skb->data)); | 508 | |
491 | #ifdef _DEBUG_DATAFLOW | 509 | free_skb: |
492 | printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n", | ||
493 | datahandle, skb->len); | ||
494 | #endif | ||
495 | ld->ops->receive_buf(tty, skb->data, NULL, skb->len); | ||
496 | kfree_skb(skb); | ||
497 | ret = 0; | 510 | ret = 0; |
498 | out2: | 511 | kfree_skb(skb); |
512 | |||
513 | deref_ldisc: | ||
499 | tty_ldisc_deref(ld); | 514 | tty_ldisc_deref(ld); |
500 | out1: | 515 | |
516 | deref_tty: | ||
501 | tty_kref_put(tty); | 517 | tty_kref_put(tty); |
502 | return ret; | 518 | return ret; |
503 | } | 519 | } |