diff options
author | Jan Kiszka <jan.kiszka@web.de> | 2010-02-08 10:12:36 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-16 16:01:32 -0800 |
commit | a11ef7be8e982426e9fbbfc84fa0c01d23ce05c3 (patch) | |
tree | ba8e293db3a4004100bc5f0608e5cfbf1e0bcfc5 /drivers | |
parent | b75b2eedcbf6458c68713c772c2ade83ab7a55f0 (diff) | |
download | linux-a11ef7be8e982426e9fbbfc84fa0c01d23ce05c3.tar.bz2 |
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')
-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) static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) { + unsigned int datalen = skb->len - CAPIMSG_LEN(skb->data); struct tty_struct *tty; struct sk_buff *nskb; - int datalen; u16 errcode, datahandle; struct tty_ldisc *ld; int ret = -1; - datalen = skb->len - CAPIMSG_LEN(skb->data); - tty = tty_port_tty_get(&mp->port); if (!tty) { #ifdef _DEBUG_DATAFLOW @@ -454,50 +452,68 @@ static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) } ld = tty_ldisc_ref(tty); - if (!ld) - goto out1; + if (!ld) { + /* fatal error, do not requeue */ + ret = 0; + kfree_skb(skb); + goto deref_tty; + } if (ld->ops->receive_buf == NULL) { #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: ldisc has no receive_buf function\n"); #endif - goto out2; + /* fatal error, do not requeue */ + goto free_skb; } if (mp->ttyinstop) { #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: recv tty throttled\n"); #endif - goto out2; + goto deref_ldisc; } + if (tty->receive_room < datalen) { #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: no room in tty\n"); #endif - goto out2; + goto deref_ldisc; } - if ((nskb = gen_data_b3_resp_for(mp, skb)) == NULL) { + + nskb = gen_data_b3_resp_for(mp, skb); + if (!nskb) { printk(KERN_ERR "capi: gen_data_b3_resp failed\n"); - goto out2; + goto deref_ldisc; } - datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4); + + datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4); + errcode = capi20_put_message(mp->ap, nskb); - if (errcode != CAPI_NOERROR) { + + if (errcode == CAPI_NOERROR) { + skb_pull(skb, CAPIMSG_LEN(skb->data)); +#ifdef _DEBUG_DATAFLOW + printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n", + datahandle, skb->len); +#endif + ld->ops->receive_buf(tty, skb->data, NULL, skb->len); + } else { printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n", errcode); kfree_skb(nskb); - goto out2; + + if (errcode == CAPI_SENDQUEUEFULL) + goto deref_ldisc; } - (void)skb_pull(skb, CAPIMSG_LEN(skb->data)); -#ifdef _DEBUG_DATAFLOW - printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n", - datahandle, skb->len); -#endif - ld->ops->receive_buf(tty, skb->data, NULL, skb->len); - kfree_skb(skb); + +free_skb: ret = 0; -out2: + kfree_skb(skb); + +deref_ldisc: tty_ldisc_deref(ld); -out1: + +deref_tty: tty_kref_put(tty); return ret; } |