Commit 2996d8de authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Make ppp_async callable from hard interrupt

From: Paul Mackerras <paulus@samba.org>

Since there are serial drivers (particularly the USB serial driver) that
call the line discipline receive_buf and write_wakeup routines at hard
interrupt level, I have changed the ppp_async code to cope with that.  It
now uses a tasklet so that it calls the generic PPP code at soft interrupt
level even if its receive_buf and write_wakeup entries are called at hard
interrupt level.

This patch has been lightly tested here with a keyspan USB serial adaptor
and also with the built-in modem on my tibook, which uses the pmac_zilog
driver (which hooks into the drivers/serial infrastructure).
parent 42c4d9c7
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
* Part of the code in this driver was inspired by the old async-only * Part of the code in this driver was inspired by the old async-only
* PPP driver, written by Michael Callahan and Al Longyear, and * PPP driver, written by Michael Callahan and Al Longyear, and
* subsequently hacked by Paul Mackerras. * subsequently hacked by Paul Mackerras.
*
* ==FILEVERSION 20020125==
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -61,6 +59,9 @@ struct asyncppp { ...@@ -61,6 +59,9 @@ struct asyncppp {
struct sk_buff *rpkt; struct sk_buff *rpkt;
int lcp_fcs; int lcp_fcs;
struct sk_buff_head rqueue;
struct tasklet_struct tsk;
atomic_t refcnt; atomic_t refcnt;
struct semaphore dead_sem; struct semaphore dead_sem;
...@@ -74,8 +75,9 @@ struct asyncppp { ...@@ -74,8 +75,9 @@ struct asyncppp {
#define XMIT_BUSY 2 #define XMIT_BUSY 2
/* State bits */ /* State bits */
#define SC_TOSS 0x20000000 #define SC_TOSS 1
#define SC_ESCAPE 0x40000000 #define SC_ESCAPE 2
#define SC_PREV_ERROR 4
/* Bits in rbits */ /* Bits in rbits */
#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) #define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
...@@ -97,6 +99,8 @@ static void ppp_async_input(struct asyncppp *ap, const unsigned char *buf, ...@@ -97,6 +99,8 @@ static void ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
char *flags, int count); char *flags, int count);
static int ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, static int ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd,
unsigned long arg); unsigned long arg);
static void ppp_async_process(unsigned long arg);
static void async_lcp_peek(struct asyncppp *ap, unsigned char *data, static void async_lcp_peek(struct asyncppp *ap, unsigned char *data,
int len, int inbound); int len, int inbound);
...@@ -165,6 +169,9 @@ ppp_asynctty_open(struct tty_struct *tty) ...@@ -165,6 +169,9 @@ ppp_asynctty_open(struct tty_struct *tty)
ap->olim = ap->obuf; ap->olim = ap->obuf;
ap->lcp_fcs = -1; ap->lcp_fcs = -1;
skb_queue_head_init(&ap->rqueue);
tasklet_init(&ap->tsk, ppp_async_process, (unsigned long) ap);
atomic_set(&ap->refcnt, 1); atomic_set(&ap->refcnt, 1);
init_MUTEX_LOCKED(&ap->dead_sem); init_MUTEX_LOCKED(&ap->dead_sem);
...@@ -214,10 +221,12 @@ ppp_asynctty_close(struct tty_struct *tty) ...@@ -214,10 +221,12 @@ ppp_asynctty_close(struct tty_struct *tty)
*/ */
if (!atomic_dec_and_test(&ap->refcnt)) if (!atomic_dec_and_test(&ap->refcnt))
down(&ap->dead_sem); down(&ap->dead_sem);
tasklet_kill(&ap->tsk);
ppp_unregister_channel(&ap->chan); ppp_unregister_channel(&ap->chan);
if (ap->rpkt != 0) if (ap->rpkt != 0)
kfree_skb(ap->rpkt); kfree_skb(ap->rpkt);
skb_queue_purge(&ap->rqueue);
if (ap->tpkt != 0) if (ap->tpkt != 0)
kfree_skb(ap->tpkt); kfree_skb(ap->tpkt);
kfree(ap); kfree(ap);
...@@ -316,17 +325,24 @@ ppp_asynctty_room(struct tty_struct *tty) ...@@ -316,17 +325,24 @@ ppp_asynctty_room(struct tty_struct *tty)
return 65535; return 65535;
} }
/*
* This can now be called from hard interrupt level as well
* as soft interrupt level or mainline.
*/
static void static void
ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf,
char *flags, int count) char *cflags, int count)
{ {
struct asyncppp *ap = ap_get(tty); struct asyncppp *ap = ap_get(tty);
unsigned long flags;
if (ap == 0) if (ap == 0)
return; return;
spin_lock_bh(&ap->recv_lock); spin_lock_irqsave(&ap->recv_lock, flags);
ppp_async_input(ap, buf, flags, count); ppp_async_input(ap, buf, cflags, count);
spin_unlock_bh(&ap->recv_lock); spin_unlock_irqrestore(&ap->recv_lock, flags);
if (skb_queue_len(&ap->rqueue))
tasklet_schedule(&ap->tsk);
ap_put(ap); ap_put(ap);
if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) if (test_and_clear_bit(TTY_THROTTLED, &tty->flags)
&& tty->driver->unthrottle) && tty->driver->unthrottle)
...@@ -341,8 +357,8 @@ ppp_asynctty_wakeup(struct tty_struct *tty) ...@@ -341,8 +357,8 @@ ppp_asynctty_wakeup(struct tty_struct *tty)
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
if (ap == 0) if (ap == 0)
return; return;
if (ppp_async_push(ap)) set_bit(XMIT_WAKEUP, &ap->xmit_flags);
ppp_output_wakeup(&ap->chan); tasklet_schedule(&ap->tsk);
ap_put(ap); ap_put(ap);
} }
...@@ -396,9 +412,9 @@ ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg) ...@@ -396,9 +412,9 @@ ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg)
if (get_user(val, (int *) arg)) if (get_user(val, (int *) arg))
break; break;
ap->flags = val & ~SC_RCV_BITS; ap->flags = val & ~SC_RCV_BITS;
spin_lock_bh(&ap->recv_lock); spin_lock_irq(&ap->recv_lock);
ap->rbits = val & SC_RCV_BITS; ap->rbits = val & SC_RCV_BITS;
spin_unlock_bh(&ap->recv_lock); spin_unlock_irq(&ap->recv_lock);
err = 0; err = 0;
break; break;
...@@ -459,6 +475,28 @@ ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg) ...@@ -459,6 +475,28 @@ ppp_async_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg)
return err; return err;
} }
/*
* This is called at softirq level to deliver received packets
* to the ppp_generic code, and to tell the ppp_generic code
* if we can accept more output now.
*/
static void ppp_async_process(unsigned long arg)
{
struct asyncppp *ap = (struct asyncppp *) arg;
struct sk_buff *skb;
/* process received packets */
while ((skb = skb_dequeue(&ap->rqueue)) != NULL) {
if (skb->cb[0])
ppp_input_error(&ap->chan, 0);
ppp_input(&ap->chan, skb);
}
/* try to push more stuff out */
if (test_bit(XMIT_WAKEUP, &ap->xmit_flags) && ppp_async_push(ap))
ppp_output_wakeup(&ap->chan);
}
/* /*
* Procedures for encapsulation and framing. * Procedures for encapsulation and framing.
*/ */
...@@ -641,7 +679,6 @@ ppp_async_push(struct asyncppp *ap) ...@@ -641,7 +679,6 @@ ppp_async_push(struct asyncppp *ap)
struct tty_struct *tty = ap->tty; struct tty_struct *tty = ap->tty;
int tty_stuffed = 0; int tty_stuffed = 0;
set_bit(XMIT_WAKEUP, &ap->xmit_flags);
/* /*
* We can get called recursively here if the tty write * We can get called recursively here if the tty write
* function calls our wakeup function. This can happen * function calls our wakeup function. This can happen
...@@ -752,22 +789,19 @@ scan_ordinary(struct asyncppp *ap, const unsigned char *buf, int count) ...@@ -752,22 +789,19 @@ scan_ordinary(struct asyncppp *ap, const unsigned char *buf, int count)
} }
/* called when a flag is seen - do end-of-packet processing */ /* called when a flag is seen - do end-of-packet processing */
static inline void static void
process_input_packet(struct asyncppp *ap) process_input_packet(struct asyncppp *ap)
{ {
struct sk_buff *skb; struct sk_buff *skb;
unsigned char *p; unsigned char *p;
unsigned int len, fcs, proto; unsigned int len, fcs, proto;
int code = 0;
skb = ap->rpkt; skb = ap->rpkt;
ap->rpkt = 0; if (ap->state & (SC_TOSS | SC_ESCAPE))
if ((ap->state & (SC_TOSS | SC_ESCAPE)) || skb == 0) { goto err;
ap->state &= ~(SC_TOSS | SC_ESCAPE);
if (skb != 0) if (skb == NULL)
kfree_skb(skb); return; /* 0-length packet */
return;
}
/* check the FCS */ /* check the FCS */
p = skb->data; p = skb->data;
...@@ -801,20 +835,18 @@ process_input_packet(struct asyncppp *ap) ...@@ -801,20 +835,18 @@ process_input_packet(struct asyncppp *ap)
async_lcp_peek(ap, p, skb->len, 1); async_lcp_peek(ap, p, skb->len, 1);
} }
/* all OK, give it to the generic layer */ /* queue the frame to be processed */
ppp_input(&ap->chan, skb); skb->cb[0] = ap->state;
skb_queue_tail(&ap->rqueue, skb);
ap->rpkt = 0;
ap->state = 0;
return; return;
err: err:
kfree_skb(skb); /* frame had an error, remember that, reset SC_TOSS & SC_ESCAPE */
ppp_input_error(&ap->chan, code); ap->state = SC_PREV_ERROR;
} if (skb)
skb_trim(skb, 0);
static inline void
input_error(struct asyncppp *ap, int code)
{
ap->state |= SC_TOSS;
ppp_input_error(&ap->chan, code);
} }
/* called when the tty driver has data for us. */ /* called when the tty driver has data for us. */
...@@ -856,7 +888,7 @@ ppp_async_input(struct asyncppp *ap, const unsigned char *buf, ...@@ -856,7 +888,7 @@ ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
} }
if (f != 0) { if (f != 0) {
/* start tossing */ /* start tossing */
input_error(ap, f); ap->state |= SC_TOSS;
} else if (n > 0 && (ap->state & SC_TOSS) == 0) { } else if (n > 0 && (ap->state & SC_TOSS) == 0) {
/* stuff the chars in the skb */ /* stuff the chars in the skb */
...@@ -872,7 +904,7 @@ ppp_async_input(struct asyncppp *ap, const unsigned char *buf, ...@@ -872,7 +904,7 @@ ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
} }
if (n > skb_tailroom(skb)) { if (n > skb_tailroom(skb)) {
/* packet overflowed MRU */ /* packet overflowed MRU */
input_error(ap, 1); ap->state |= SC_TOSS;
} else { } else {
sp = skb_put(skb, n); sp = skb_put(skb, n);
memcpy(sp, buf, n); memcpy(sp, buf, n);
...@@ -909,7 +941,7 @@ ppp_async_input(struct asyncppp *ap, const unsigned char *buf, ...@@ -909,7 +941,7 @@ ppp_async_input(struct asyncppp *ap, const unsigned char *buf,
nomem: nomem:
printk(KERN_ERR "PPPasync: no memory (input pkt)\n"); printk(KERN_ERR "PPPasync: no memory (input pkt)\n");
input_error(ap, 0); ap->state |= SC_TOSS;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment