Commit f803ee7a authored by Paul Fulghum's avatar Paul Fulghum Committed by Linus Torvalds

[PATCH] ppp_synctty.c receive/write_wakeup fix

Allow receive and write_wakeup callbacks to be called at hard interrupt
context and/or with interrupts disabled (removes softirq warning).

This mirrors changes by Paul Mackerras to ppp_async.c for the same purpose.
Patch has been previously posted for comments and has been tested with
success by multiple persons.
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 5a86174c
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
* 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== * ==FILEVERSION 20040616==
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -65,7 +65,9 @@ struct syncppp { ...@@ -65,7 +65,9 @@ struct syncppp {
struct sk_buff *tpkt; struct sk_buff *tpkt;
unsigned long last_xmit; unsigned long last_xmit;
struct sk_buff *rpkt; struct sk_buff_head rqueue;
struct tasklet_struct tsk;
atomic_t refcnt; atomic_t refcnt;
struct semaphore dead_sem; struct semaphore dead_sem;
...@@ -88,6 +90,7 @@ static struct sk_buff* ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *); ...@@ -88,6 +90,7 @@ static struct sk_buff* ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *);
static int ppp_sync_send(struct ppp_channel *chan, struct sk_buff *skb); static int ppp_sync_send(struct ppp_channel *chan, struct sk_buff *skb);
static int ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd, static int ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd,
unsigned long arg); unsigned long arg);
static void ppp_sync_process(unsigned long arg);
static int ppp_sync_push(struct syncppp *ap); static int ppp_sync_push(struct syncppp *ap);
static void ppp_sync_flush_output(struct syncppp *ap); static void ppp_sync_flush_output(struct syncppp *ap);
static void ppp_sync_input(struct syncppp *ap, const unsigned char *buf, static void ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
...@@ -217,6 +220,9 @@ ppp_sync_open(struct tty_struct *tty) ...@@ -217,6 +220,9 @@ ppp_sync_open(struct tty_struct *tty)
ap->xaccm[3] = 0x60000000U; ap->xaccm[3] = 0x60000000U;
ap->raccm = ~0U; ap->raccm = ~0U;
skb_queue_head_init(&ap->rqueue);
tasklet_init(&ap->tsk, ppp_sync_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);
...@@ -267,10 +273,10 @@ ppp_sync_close(struct tty_struct *tty) ...@@ -267,10 +273,10 @@ ppp_sync_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) skb_queue_purge(&ap->rqueue);
kfree_skb(ap->rpkt);
if (ap->tpkt != 0) if (ap->tpkt != 0)
kfree_skb(ap->tpkt); kfree_skb(ap->tpkt);
kfree(ap); kfree(ap);
...@@ -369,17 +375,24 @@ ppp_sync_room(struct tty_struct *tty) ...@@ -369,17 +375,24 @@ ppp_sync_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_sync_receive(struct tty_struct *tty, const unsigned char *buf, ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf,
char *flags, int count) char *cflags, int count)
{ {
struct syncppp *ap = sp_get(tty); struct syncppp *ap = sp_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_sync_input(ap, buf, flags, count); ppp_sync_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);
sp_put(ap); sp_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)
...@@ -394,8 +407,8 @@ ppp_sync_wakeup(struct tty_struct *tty) ...@@ -394,8 +407,8 @@ ppp_sync_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_sync_push(ap)) set_bit(XMIT_WAKEUP, &ap->xmit_flags);
ppp_output_wakeup(&ap->chan); tasklet_schedule(&ap->tsk);
sp_put(ap); sp_put(ap);
} }
...@@ -449,9 +462,9 @@ ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg) ...@@ -449,9 +462,9 @@ ppp_sync_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;
...@@ -511,6 +524,32 @@ ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg) ...@@ -511,6 +524,32 @@ ppp_sync_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_sync_process(unsigned long arg)
{
struct syncppp *ap = (struct syncppp *) arg;
struct sk_buff *skb;
/* process received packets */
while ((skb = skb_dequeue(&ap->rqueue)) != NULL) {
if (skb->len == 0) {
/* zero length buffers indicate error */
ppp_input_error(&ap->chan, 0);
kfree_skb(skb);
}
else
ppp_input(&ap->chan, skb);
}
/* try to push more stuff out */
if (test_bit(XMIT_WAKEUP, &ap->xmit_flags) && ppp_sync_push(ap))
ppp_output_wakeup(&ap->chan);
}
/* /*
* Procedures for encapsulation and framing. * Procedures for encapsulation and framing.
*/ */
...@@ -600,7 +639,6 @@ ppp_sync_push(struct syncppp *ap) ...@@ -600,7 +639,6 @@ ppp_sync_push(struct syncppp *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);
if (!spin_trylock_bh(&ap->xmit_lock)) if (!spin_trylock_bh(&ap->xmit_lock))
return 0; return 0;
for (;;) { for (;;) {
...@@ -667,15 +705,44 @@ ppp_sync_flush_output(struct syncppp *ap) ...@@ -667,15 +705,44 @@ ppp_sync_flush_output(struct syncppp *ap)
* Receive-side routines. * Receive-side routines.
*/ */
static inline void /* called when the tty driver has data for us.
process_input_packet(struct syncppp *ap) *
* Data is frame oriented: each call to ppp_sync_input is considered
* a whole frame. If the 1st flag byte is non-zero then the whole
* frame is considered to be in error and is tossed.
*/
static void
ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
char *flags, int count)
{ {
struct sk_buff *skb; struct sk_buff *skb;
unsigned char *p; unsigned char *p;
int code = 0;
skb = ap->rpkt; if (count == 0)
ap->rpkt = 0; return;
if (ap->flags & SC_LOG_INPKT)
ppp_print_buffer ("receive buffer", buf, count);
/* stuff the chars in the skb */
if ((skb = dev_alloc_skb(ap->mru + PPP_HDRLEN + 2)) == 0) {
printk(KERN_ERR "PPPsync: no memory (input pkt)\n");
goto err;
}
/* Try to get the payload 4-byte aligned */
if (buf[0] != PPP_ALLSTATIONS)
skb_reserve(skb, 2 + (buf[0] & 1));
if (flags != 0 && *flags) {
/* error flag set, ignore frame */
goto err;
} else if (count > skb_tailroom(skb)) {
/* packet overflowed MRU */
goto err;
}
p = skb_put(skb, count);
memcpy(p, buf, count);
/* strip address/control field if present */ /* strip address/control field if present */
p = skb->data; p = skb->data;
...@@ -693,59 +760,15 @@ process_input_packet(struct syncppp *ap) ...@@ -693,59 +760,15 @@ process_input_packet(struct syncppp *ap)
} else if (skb->len < 2) } else if (skb->len < 2)
goto err; goto err;
/* pass to generic layer */ /* queue the frame to be processed */
ppp_input(&ap->chan, skb); skb_queue_tail(&ap->rqueue, skb);
return; return;
err: err:
kfree_skb(skb); /* queue zero length packet as error indication */
ppp_input_error(&ap->chan, code); if (skb || (skb = dev_alloc_skb(0))) {
} skb_trim(skb, 0);
skb_queue_tail(&ap->rqueue, skb);
/* called when the tty driver has data for us.
*
* Data is frame oriented: each call to ppp_sync_input is considered
* a whole frame. If the 1st flag byte is non-zero then the whole
* frame is considered to be in error and is tossed.
*/
static void
ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
char *flags, int count)
{
struct sk_buff *skb;
unsigned char *sp;
if (count == 0)
return;
/* if flag set, then error, ignore frame */
if (flags != 0 && *flags) {
ppp_input_error(&ap->chan, *flags);
return;
}
if (ap->flags & SC_LOG_INPKT)
ppp_print_buffer ("receive buffer", buf, count);
/* stuff the chars in the skb */
if ((skb = ap->rpkt) == 0) {
if ((skb = dev_alloc_skb(ap->mru + PPP_HDRLEN + 2)) == 0) {
printk(KERN_ERR "PPPsync: no memory (input pkt)\n");
ppp_input_error(&ap->chan, 0);
return;
}
/* Try to get the payload 4-byte aligned */
if (buf[0] != PPP_ALLSTATIONS)
skb_reserve(skb, 2 + (buf[0] & 1));
ap->rpkt = skb;
}
if (count > skb_tailroom(skb)) {
/* packet overflowed MRU */
ppp_input_error(&ap->chan, 1);
} else {
sp = skb_put(skb, count);
memcpy(sp, buf, count);
process_input_packet(ap);
} }
} }
......
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