Commit f477fdbe authored by Paul Mackerras's avatar Paul Mackerras

PPP updates and fixes. This fixes the various SMP races, deadlocks

and scheduling-in-interrupt problems we had, and also makes it
much faster when handling large numbers (100s or more) of PPP units.
parent 22e962f9
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,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 20000227== * ==FILEVERSION 20020125==
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#define PPP_VERSION "2.4.1" #define PPP_VERSION "2.4.2"
#define OBUFSIZE 256 #define OBUFSIZE 256
...@@ -62,6 +62,8 @@ struct asyncppp { ...@@ -62,6 +62,8 @@ struct asyncppp {
struct sk_buff *rpkt; struct sk_buff *rpkt;
int lcp_fcs; int lcp_fcs;
atomic_t refcnt;
struct semaphore dead_sem;
struct ppp_channel chan; /* interface to generic ppp layer */ struct ppp_channel chan; /* interface to generic ppp layer */
unsigned char obuf[OBUFSIZE]; unsigned char obuf[OBUFSIZE];
}; };
...@@ -107,6 +109,35 @@ static struct ppp_channel_ops async_ops = { ...@@ -107,6 +109,35 @@ static struct ppp_channel_ops async_ops = {
* Routines implementing the PPP line discipline. * Routines implementing the PPP line discipline.
*/ */
/*
* We have a potential race on dereferencing tty->disc_data,
* because the tty layer provides no locking at all - thus one
* cpu could be running ppp_asynctty_receive while another
* calls ppp_asynctty_close, which zeroes tty->disc_data and
* frees the memory that ppp_asynctty_receive is using. The best
* way to fix this is to use a rwlock in the tty struct, but for now
* we use a single global rwlock for all ttys in ppp line discipline.
*/
static rwlock_t disc_data_lock = RW_LOCK_UNLOCKED;
static struct asyncppp *ap_get(struct tty_struct *tty)
{
struct asyncppp *ap;
read_lock(&disc_data_lock);
ap = tty->disc_data;
if (ap != NULL)
atomic_inc(&ap->refcnt);
read_unlock(&disc_data_lock);
return ap;
}
static void ap_put(struct asyncppp *ap)
{
if (atomic_dec_and_test(&ap->refcnt))
up(&ap->dead_sem);
}
/* /*
* Called when a tty is put into PPP line discipline. * Called when a tty is put into PPP line discipline.
*/ */
...@@ -135,6 +166,9 @@ ppp_asynctty_open(struct tty_struct *tty) ...@@ -135,6 +166,9 @@ ppp_asynctty_open(struct tty_struct *tty)
ap->olim = ap->obuf; ap->olim = ap->obuf;
ap->lcp_fcs = -1; ap->lcp_fcs = -1;
atomic_set(&ap->refcnt, 1);
init_MUTEX_LOCKED(&ap->dead_sem);
ap->chan.private = ap; ap->chan.private = ap;
ap->chan.ops = &async_ops; ap->chan.ops = &async_ops;
ap->chan.mtu = PPP_MRU; ap->chan.mtu = PPP_MRU;
...@@ -155,19 +189,34 @@ ppp_asynctty_open(struct tty_struct *tty) ...@@ -155,19 +189,34 @@ ppp_asynctty_open(struct tty_struct *tty)
/* /*
* Called when the tty is put into another line discipline * Called when the tty is put into another line discipline
* or it hangs up. * or it hangs up. We have to wait for any cpu currently
* We assume that while we are in this routine, the tty layer * executing in any of the other ppp_asynctty_* routines to
* won't call any of the other line discipline entries for the * finish before we can call ppp_unregister_channel and free
* same tty. * the asyncppp struct. This routine must be called from
* process context, not interrupt or softirq context.
*/ */
static void static void
ppp_asynctty_close(struct tty_struct *tty) ppp_asynctty_close(struct tty_struct *tty)
{ {
struct asyncppp *ap = tty->disc_data; struct asyncppp *ap;
write_lock(&disc_data_lock);
ap = tty->disc_data;
tty->disc_data = 0;
write_unlock(&disc_data_lock);
if (ap == 0) if (ap == 0)
return; return;
tty->disc_data = 0;
/*
* We have now ensured that nobody can start using ap from now
* on, but we have to wait for all existing users to finish.
* Note that ppp_unregister_channel ensures that no calls to
* our channel ops (i.e. ppp_async_send/ioctl) are in progress
* by the time it returns.
*/
if (!atomic_dec_and_test(&ap->refcnt))
down(&ap->dead_sem);
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);
...@@ -203,9 +252,11 @@ static int ...@@ -203,9 +252,11 @@ static int
ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file, ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
struct asyncppp *ap = tty->disc_data; struct asyncppp *ap = ap_get(tty);
int err, val; int err, val;
if (ap == 0)
return -ENXIO;
err = -EFAULT; err = -EFAULT;
switch (cmd) { switch (cmd) {
case PPPIOCGCHAN: case PPPIOCGCHAN:
...@@ -251,6 +302,7 @@ ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file, ...@@ -251,6 +302,7 @@ ppp_asynctty_ioctl(struct tty_struct *tty, struct file *file,
err = -ENOIOCTLCMD; err = -ENOIOCTLCMD;
} }
ap_put(ap);
return err; return err;
} }
...@@ -271,13 +323,14 @@ static void ...@@ -271,13 +323,14 @@ 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 *flags, int count)
{ {
struct asyncppp *ap = tty->disc_data; struct asyncppp *ap = ap_get(tty);
if (ap == 0) if (ap == 0)
return; return;
spin_lock_bh(&ap->recv_lock); spin_lock_bh(&ap->recv_lock);
ppp_async_input(ap, buf, flags, count); ppp_async_input(ap, buf, flags, count);
spin_unlock_bh(&ap->recv_lock); spin_unlock_bh(&ap->recv_lock);
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)
tty->driver.unthrottle(tty); tty->driver.unthrottle(tty);
...@@ -286,13 +339,14 @@ ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, ...@@ -286,13 +339,14 @@ ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf,
static void static void
ppp_asynctty_wakeup(struct tty_struct *tty) ppp_asynctty_wakeup(struct tty_struct *tty)
{ {
struct asyncppp *ap = tty->disc_data; struct asyncppp *ap = ap_get(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)) if (ppp_async_push(ap))
ppp_output_wakeup(&ap->chan); ppp_output_wakeup(&ap->chan);
ap_put(ap);
} }
......
...@@ -35,16 +35,12 @@ ...@@ -35,16 +35,12 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/ppp_defs.h> #include <linux/ppp_defs.h>
#include <linux/ppp-comp.h> #include <linux/ppp-comp.h>
#include <linux/zlib.h> #include <linux/zlib.h>
static spinlock_t comp_free_list_lock = SPIN_LOCK_UNLOCKED;
static LIST_HEAD(comp_free_list);
/* /*
* State for a Deflate (de)compressor. * State for a Deflate (de)compressor.
*/ */
...@@ -56,7 +52,6 @@ struct ppp_deflate_state { ...@@ -56,7 +52,6 @@ struct ppp_deflate_state {
int debug; int debug;
z_stream strm; z_stream strm;
struct compstat stats; struct compstat stats;
struct list_head list;
}; };
#define DEFLATE_OVHD 2 /* Deflate overhead/packet */ #define DEFLATE_OVHD 2 /* Deflate overhead/packet */
...@@ -81,27 +76,6 @@ static void z_comp_reset __P((void *state)); ...@@ -81,27 +76,6 @@ static void z_comp_reset __P((void *state));
static void z_decomp_reset __P((void *state)); static void z_decomp_reset __P((void *state));
static void z_comp_stats __P((void *state, struct compstat *stats)); static void z_comp_stats __P((void *state, struct compstat *stats));
static void z_comp_delayedfree(void *arg)
{
struct ppp_deflate_state *state;
spin_lock_bh(&comp_free_list_lock);
while(!list_empty(&comp_free_list)) {
state = list_entry(comp_free_list.next, struct ppp_deflate_state, list);
list_del(&state->list);
spin_unlock_bh(&comp_free_list_lock);
if (state->strm.workspace)
vfree(state->strm.workspace);
kfree(state);
spin_lock_bh(&comp_free_list_lock);
}
spin_unlock_bh(&comp_free_list_lock);
}
static struct tq_struct z_comp_task = {
routine: z_comp_delayedfree
};
static void static void
z_comp_free(arg) z_comp_free(arg)
void *arg; void *arg;
...@@ -110,12 +84,9 @@ z_comp_free(arg) ...@@ -110,12 +84,9 @@ z_comp_free(arg)
if (state) { if (state) {
zlib_deflateEnd(&state->strm); zlib_deflateEnd(&state->strm);
if (state->strm.workspace)
spin_lock_bh(&comp_free_list_lock); vfree(state->strm.workspace);
list_add(&state->list, &comp_free_list); kfree(state);
spin_unlock_bh(&comp_free_list_lock);
schedule_task(&z_comp_task);
MOD_DEC_USE_COUNT; MOD_DEC_USE_COUNT;
} }
} }
...@@ -616,8 +587,6 @@ void __exit deflate_cleanup(void) ...@@ -616,8 +587,6 @@ void __exit deflate_cleanup(void)
{ {
ppp_unregister_compressor(&ppp_deflate); ppp_unregister_compressor(&ppp_deflate);
ppp_unregister_compressor(&ppp_deflate_draft); ppp_unregister_compressor(&ppp_deflate_draft);
/* Ensure that any deflate state pending free is actually freed */
flush_scheduled_tasks();
} }
module_init(deflate_init); module_init(deflate_init);
......
/* /*
* Generic PPP layer for Linux. * Generic PPP layer for Linux.
* *
* Copyright 1999-2000 Paul Mackerras. * Copyright 1999-2002 Paul Mackerras.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,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 20000902== * ==FILEVERSION 20020217==
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -43,10 +43,12 @@ ...@@ -43,10 +43,12 @@
#include <linux/tcp.h> #include <linux/tcp.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/rwsem.h>
#include <linux/stddef.h>
#include <net/slhc_vj.h> #include <net/slhc_vj.h>
#include <asm/atomic.h> #include <asm/atomic.h>
#define PPP_VERSION "2.4.1" #define PPP_VERSION "2.4.2"
/* /*
* Network protocols we support. * Network protocols we support.
...@@ -75,11 +77,11 @@ struct ppp_file { ...@@ -75,11 +77,11 @@ struct ppp_file {
wait_queue_head_t rwait; /* for poll on reading /dev/ppp */ wait_queue_head_t rwait; /* for poll on reading /dev/ppp */
atomic_t refcnt; /* # refs (incl /dev/ppp attached) */ atomic_t refcnt; /* # refs (incl /dev/ppp attached) */
int hdrlen; /* space to leave for headers */ int hdrlen; /* space to leave for headers */
struct list_head list; /* link in all_* list */
int index; /* interface unit / channel number */ int index; /* interface unit / channel number */
int dead; /* unit/channel has been shut down */
}; };
#define PF_TO_X(pf, X) ((X *)((char *)(pf)-(unsigned long)(&((X *)0)->file))) #define PF_TO_X(pf, X) ((X *)((char *)(pf) - offsetof(X, file)))
#define PF_TO_PPP(pf) PF_TO_X(pf, struct ppp) #define PF_TO_PPP(pf) PF_TO_X(pf, struct ppp)
#define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct channel) #define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct channel)
...@@ -93,26 +95,27 @@ struct ppp_file { ...@@ -93,26 +95,27 @@ struct ppp_file {
* It can have 0 or more ppp channels connected to it. * It can have 0 or more ppp channels connected to it.
*/ */
struct ppp { struct ppp {
struct ppp_file file; /* stuff for read/write/poll */ struct ppp_file file; /* stuff for read/write/poll 0 */
struct list_head channels; /* list of attached channels */ struct file *owner; /* file that owns this unit 48 */
int n_channels; /* how many channels are attached */ struct list_head channels; /* list of attached channels 4c */
spinlock_t rlock; /* lock for receive side */ int n_channels; /* how many channels are attached 54 */
spinlock_t wlock; /* lock for transmit side */ spinlock_t rlock; /* lock for receive side 58 */
int mru; /* max receive unit */ spinlock_t wlock; /* lock for transmit side 5c */
unsigned int flags; /* control bits */ int mru; /* max receive unit 60 */
unsigned int xstate; /* transmit state bits */ unsigned int flags; /* control bits 64 */
unsigned int rstate; /* receive state bits */ unsigned int xstate; /* transmit state bits 68 */
int debug; /* debug flags */ unsigned int rstate; /* receive state bits 6c */
int debug; /* debug flags 70 */
struct slcompress *vj; /* state for VJ header compression */ struct slcompress *vj; /* state for VJ header compression */
enum NPmode npmode[NUM_NP]; /* what to do with each net proto */ enum NPmode npmode[NUM_NP]; /* what to do with each net proto 78 */
struct sk_buff *xmit_pending; /* a packet ready to go out */ struct sk_buff *xmit_pending; /* a packet ready to go out 88 */
struct compressor *xcomp; /* transmit packet compressor */ struct compressor *xcomp; /* transmit packet compressor 8c */
void *xc_state; /* its internal state */ void *xc_state; /* its internal state 90 */
struct compressor *rcomp; /* receive decompressor */ struct compressor *rcomp; /* receive decompressor 94 */
void *rc_state; /* its internal state */ void *rc_state; /* its internal state 98 */
unsigned long last_xmit; /* jiffies when last pkt sent */ unsigned long last_xmit; /* jiffies when last pkt sent 9c */
unsigned long last_recv; /* jiffies when last pkt rcvd */ unsigned long last_recv; /* jiffies when last pkt rcvd a0 */
struct net_device *dev; /* network interface device */ struct net_device *dev; /* network interface device a4 */
#ifdef CONFIG_PPP_MULTILINK #ifdef CONFIG_PPP_MULTILINK
int nxchan; /* next channel to send something on */ int nxchan; /* next channel to send something on */
u32 nxseq; /* next sequence number to send */ u32 nxseq; /* next sequence number to send */
...@@ -144,11 +147,13 @@ struct ppp { ...@@ -144,11 +147,13 @@ struct ppp {
*/ */
struct channel { struct channel {
struct ppp_file file; /* stuff for read/write/poll */ struct ppp_file file; /* stuff for read/write/poll */
struct list_head list; /* link in all/new_channels list */
struct ppp_channel *chan; /* public channel data structure */ struct ppp_channel *chan; /* public channel data structure */
struct rw_semaphore chan_sem; /* protects `chan' during chan ioctl */
spinlock_t downl; /* protects `chan', file.xq dequeue */ spinlock_t downl; /* protects `chan', file.xq dequeue */
struct ppp *ppp; /* ppp unit we're connected to */ struct ppp *ppp; /* ppp unit we're connected to */
struct list_head clist; /* link in list of channels per unit */ struct list_head clist; /* link in list of channels per unit */
rwlock_t upl; /* protects `ppp' and `ulist' */ rwlock_t upl; /* protects `ppp' */
#ifdef CONFIG_PPP_MULTILINK #ifdef CONFIG_PPP_MULTILINK
u8 avail; /* flag used in multilink stuff */ u8 avail; /* flag used in multilink stuff */
u8 had_frag; /* >= 1 fragments have been sent */ u8 had_frag; /* >= 1 fragments have been sent */
...@@ -166,12 +171,35 @@ struct channel { ...@@ -166,12 +171,35 @@ struct channel {
*/ */
/* /*
* all_ppp_lock protects the all_ppp_units. * A cardmap represents a mapping from unsigned integers to pointers,
* It also ensures that finding a ppp unit in the all_ppp_units list * and provides a fast "find lowest unused number" operation.
* It uses a broad (32-way) tree with a bitmap at each level.
* It is designed to be space-efficient for small numbers of entries
* and time-efficient for large numbers of entries.
*/
#define CARDMAP_ORDER 5
#define CARDMAP_WIDTH (1U << CARDMAP_ORDER)
#define CARDMAP_MASK (CARDMAP_WIDTH - 1)
struct cardmap {
int shift;
unsigned long inuse;
struct cardmap *parent;
void *ptr[CARDMAP_WIDTH];
};
static void *cardmap_get(struct cardmap *map, unsigned int nr);
static void cardmap_set(struct cardmap **map, unsigned int nr, void *ptr);
static unsigned int cardmap_find_first_free(struct cardmap *map);
static void cardmap_destroy(struct cardmap **map);
/*
* all_ppp_sem protects the all_ppp_units mapping.
* It also ensures that finding a ppp unit in the all_ppp_units map
* and updating its file.refcnt field is atomic. * and updating its file.refcnt field is atomic.
*/ */
static spinlock_t all_ppp_lock = SPIN_LOCK_UNLOCKED; static DECLARE_MUTEX(all_ppp_sem);
static LIST_HEAD(all_ppp_units); static struct cardmap *all_ppp_units;
static atomic_t ppp_unit_count = ATOMIC_INIT(0);
/* /*
* all_channels_lock protects all_channels and last_channel_index, * all_channels_lock protects all_channels and last_channel_index,
...@@ -180,7 +208,9 @@ static LIST_HEAD(all_ppp_units); ...@@ -180,7 +208,9 @@ static LIST_HEAD(all_ppp_units);
*/ */
static spinlock_t all_channels_lock = SPIN_LOCK_UNLOCKED; static spinlock_t all_channels_lock = SPIN_LOCK_UNLOCKED;
static LIST_HEAD(all_channels); static LIST_HEAD(all_channels);
static LIST_HEAD(new_channels);
static int last_channel_index; static int last_channel_index;
static atomic_t channel_count = ATOMIC_INIT(0);
/* Get the PPP protocol number from a skb */ /* Get the PPP protocol number from a skb */
#define PPP_PROTO(skb) (((skb)->data[0] << 8) + (skb)->data[1]) #define PPP_PROTO(skb) (((skb)->data[0] << 8) + (skb)->data[1])
...@@ -205,10 +235,6 @@ static int last_channel_index; ...@@ -205,10 +235,6 @@ static int last_channel_index;
#define seq_after(a, b) ((s32)((a) - (b)) > 0) #define seq_after(a, b) ((s32)((a) - (b)) > 0)
/* Prototypes. */ /* Prototypes. */
static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file,
char *buf, size_t count);
static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf,
size_t count);
static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file, static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
unsigned int cmd, unsigned long arg); unsigned int cmd, unsigned long arg);
static void ppp_xmit_process(struct ppp *ppp); static void ppp_xmit_process(struct ppp *ppp);
...@@ -235,6 +261,7 @@ static struct compressor *find_compressor(int type); ...@@ -235,6 +261,7 @@ static struct compressor *find_compressor(int type);
static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st); static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st);
static struct ppp *ppp_create_interface(int unit, int *retp); static struct ppp *ppp_create_interface(int unit, int *retp);
static void init_ppp_file(struct ppp_file *pf, int kind); static void init_ppp_file(struct ppp_file *pf, int kind);
static void ppp_shutdown_interface(struct ppp *ppp);
static void ppp_destroy_interface(struct ppp *ppp); static void ppp_destroy_interface(struct ppp *ppp);
static struct ppp *ppp_find_unit(int unit); static struct ppp *ppp_find_unit(int unit);
static struct channel *ppp_find_channel(int unit); static struct channel *ppp_find_channel(int unit);
...@@ -303,7 +330,6 @@ static const int npindex_to_ethertype[NUM_NP] = { ...@@ -303,7 +330,6 @@ static const int npindex_to_ethertype[NUM_NP] = {
#define ppp_unlock(ppp) do { ppp_recv_unlock(ppp); \ #define ppp_unlock(ppp) do { ppp_recv_unlock(ppp); \
ppp_xmit_unlock(ppp); } while (0) ppp_xmit_unlock(ppp); } while (0)
/* /*
* /dev/ppp device routines. * /dev/ppp device routines.
* The /dev/ppp device is used by pppd to control the ppp unit. * The /dev/ppp device is used by pppd to control the ppp unit.
...@@ -323,10 +349,16 @@ static int ppp_open(struct inode *inode, struct file *file) ...@@ -323,10 +349,16 @@ static int ppp_open(struct inode *inode, struct file *file)
static int ppp_release(struct inode *inode, struct file *file) static int ppp_release(struct inode *inode, struct file *file)
{ {
struct ppp_file *pf = (struct ppp_file *) file->private_data; struct ppp_file *pf = file->private_data;
struct ppp *ppp;
if (pf != 0) { if (pf != 0) {
file->private_data = 0; file->private_data = 0;
if (pf->kind == INTERFACE) {
ppp = PF_TO_PPP(pf);
if (file == ppp->owner)
ppp_shutdown_interface(ppp);
}
if (atomic_dec_and_test(&pf->refcnt)) { if (atomic_dec_and_test(&pf->refcnt)) {
switch (pf->kind) { switch (pf->kind) {
case INTERFACE: case INTERFACE:
...@@ -344,30 +376,21 @@ static int ppp_release(struct inode *inode, struct file *file) ...@@ -344,30 +376,21 @@ static int ppp_release(struct inode *inode, struct file *file)
static ssize_t ppp_read(struct file *file, char *buf, static ssize_t ppp_read(struct file *file, char *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct ppp_file *pf = (struct ppp_file *) file->private_data; struct ppp_file *pf = file->private_data;
return ppp_file_read(pf, file, buf, count);
}
static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file,
char *buf, size_t count)
{
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
ssize_t ret; ssize_t ret;
struct sk_buff *skb = 0; struct sk_buff *skb = 0;
ret = -ENXIO;
if (pf == 0) if (pf == 0)
goto out; /* not currently attached */ return -ENXIO;
add_wait_queue(&pf->rwait, &wait); add_wait_queue(&pf->rwait, &wait);
current->state = TASK_INTERRUPTIBLE;
for (;;) { for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
skb = skb_dequeue(&pf->rq); skb = skb_dequeue(&pf->rq);
if (skb) if (skb)
break; break;
ret = 0; ret = 0;
if (pf->kind == CHANNEL && PF_TO_CHANNEL(pf)->chan == 0) if (pf->dead)
break; break;
ret = -EAGAIN; ret = -EAGAIN;
if (file->f_flags & O_NONBLOCK) if (file->f_flags & O_NONBLOCK)
...@@ -377,7 +400,7 @@ static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file, ...@@ -377,7 +400,7 @@ static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file,
break; break;
schedule(); schedule();
} }
current->state = TASK_RUNNING; set_current_state(TASK_RUNNING);
remove_wait_queue(&pf->rwait, &wait); remove_wait_queue(&pf->rwait, &wait);
if (skb == 0) if (skb == 0)
...@@ -400,21 +423,12 @@ static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file, ...@@ -400,21 +423,12 @@ static ssize_t ppp_file_read(struct ppp_file *pf, struct file *file,
static ssize_t ppp_write(struct file *file, const char *buf, static ssize_t ppp_write(struct file *file, const char *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct ppp_file *pf = (struct ppp_file *) file->private_data; struct ppp_file *pf = file->private_data;
return ppp_file_write(pf, buf, count);
}
static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf,
size_t count)
{
struct sk_buff *skb; struct sk_buff *skb;
ssize_t ret; ssize_t ret;
ret = -ENXIO;
if (pf == 0) if (pf == 0)
goto out; return -ENXIO;
ret = -ENOMEM; ret = -ENOMEM;
skb = alloc_skb(count + pf->hdrlen, GFP_KERNEL); skb = alloc_skb(count + pf->hdrlen, GFP_KERNEL);
if (skb == 0) if (skb == 0)
...@@ -446,7 +460,7 @@ static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf, ...@@ -446,7 +460,7 @@ static ssize_t ppp_file_write(struct ppp_file *pf, const char *buf,
/* No kernel lock - fine */ /* No kernel lock - fine */
static unsigned int ppp_poll(struct file *file, poll_table *wait) static unsigned int ppp_poll(struct file *file, poll_table *wait)
{ {
struct ppp_file *pf = (struct ppp_file *) file->private_data; struct ppp_file *pf = file->private_data;
unsigned int mask; unsigned int mask;
if (pf == 0) if (pf == 0)
...@@ -455,28 +469,52 @@ static unsigned int ppp_poll(struct file *file, poll_table *wait) ...@@ -455,28 +469,52 @@ static unsigned int ppp_poll(struct file *file, poll_table *wait)
mask = POLLOUT | POLLWRNORM; mask = POLLOUT | POLLWRNORM;
if (skb_peek(&pf->rq) != 0) if (skb_peek(&pf->rq) != 0)
mask |= POLLIN | POLLRDNORM; mask |= POLLIN | POLLRDNORM;
if (pf->kind == CHANNEL) { if (pf->dead)
struct channel *pch = PF_TO_CHANNEL(pf); mask |= POLLHUP;
if (pch->chan == 0)
mask |= POLLHUP;
}
return mask; return mask;
} }
static int ppp_ioctl(struct inode *inode, struct file *file, static int ppp_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
struct ppp_file *pf = (struct ppp_file *) file->private_data; struct ppp_file *pf = file->private_data;
struct ppp *ppp; struct ppp *ppp;
int err = -EFAULT, val, val2, i; int err = -EFAULT, val, val2, i;
struct ppp_idle idle; struct ppp_idle idle;
struct npioctl npi; struct npioctl npi;
int unit; int unit, cflags;
struct slcompress *vj; struct slcompress *vj;
if (pf == 0) if (pf == 0)
return ppp_unattached_ioctl(pf, file, cmd, arg); return ppp_unattached_ioctl(pf, file, cmd, arg);
if (cmd == PPPIOCDETACH) {
/*
* We have to be careful here... if the file descriptor
* has been dup'd, we could have another process in the
* middle of a poll using the same file *, so we had
* better not free the interface data structures -
* instead we fail the ioctl. Even in this case, we
* shut down the interface if we are the owner of it.
* Actually, we should get rid of PPPIOCDETACH, userland
* (i.e. pppd) could achieve the same effect by closing
* this fd and reopening /dev/ppp.
*/
err = -EINVAL;
if (pf->kind == INTERFACE) {
ppp = PF_TO_PPP(pf);
if (file == ppp->owner)
ppp_shutdown_interface(ppp);
}
if (atomic_read(&file->f_count) <= 2) {
ppp_release(inode, file);
err = 0;
} else
printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%d\n",
atomic_read(&file->f_count));
return err;
}
if (pf->kind == CHANNEL) { if (pf->kind == CHANNEL) {
struct channel *pch = PF_TO_CHANNEL(pf); struct channel *pch = PF_TO_CHANNEL(pf);
struct ppp_channel *chan; struct ppp_channel *chan;
...@@ -492,20 +530,13 @@ static int ppp_ioctl(struct inode *inode, struct file *file, ...@@ -492,20 +530,13 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
err = ppp_disconnect_channel(pch); err = ppp_disconnect_channel(pch);
break; break;
case PPPIOCDETACH:
file->private_data = 0;
if (atomic_dec_and_test(&pf->refcnt))
ppp_destroy_channel(pch);
err = 0;
break;
default: default:
spin_lock_bh(&pch->downl); down_read(&pch->chan_sem);
chan = pch->chan; chan = pch->chan;
err = -ENOTTY; err = -ENOTTY;
if (chan && chan->ops->ioctl) if (chan && chan->ops->ioctl)
err = chan->ops->ioctl(chan, cmd, arg); err = chan->ops->ioctl(chan, cmd, arg);
spin_unlock_bh(&pch->downl); up_read(&pch->chan_sem);
} }
return err; return err;
} }
...@@ -518,13 +549,6 @@ static int ppp_ioctl(struct inode *inode, struct file *file, ...@@ -518,13 +549,6 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
ppp = PF_TO_PPP(pf); ppp = PF_TO_PPP(pf);
switch (cmd) { switch (cmd) {
case PPPIOCDETACH:
file->private_data = 0;
if (atomic_dec_and_test(&pf->refcnt))
ppp_destroy_interface(ppp);
err = 0;
break;
case PPPIOCSMRU: case PPPIOCSMRU:
if (get_user(val, (int *) arg)) if (get_user(val, (int *) arg))
break; break;
...@@ -536,10 +560,11 @@ static int ppp_ioctl(struct inode *inode, struct file *file, ...@@ -536,10 +560,11 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
if (get_user(val, (int *) arg)) if (get_user(val, (int *) arg))
break; break;
ppp_lock(ppp); ppp_lock(ppp);
if (ppp->flags & ~val & SC_CCP_OPEN) cflags = ppp->flags & ~val;
ppp_ccp_closed(ppp);
ppp->flags = val & SC_FLAG_BITS; ppp->flags = val & SC_FLAG_BITS;
ppp_unlock(ppp); ppp_unlock(ppp);
if (cflags & SC_CCP_OPEN)
ppp_ccp_closed(ppp);
err = 0; err = 0;
break; break;
...@@ -675,6 +700,7 @@ static int ppp_ioctl(struct inode *inode, struct file *file, ...@@ -675,6 +700,7 @@ static int ppp_ioctl(struct inode *inode, struct file *file,
default: default:
err = -ENOTTY; err = -ENOTTY;
} }
return err; return err;
} }
...@@ -694,6 +720,7 @@ static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file, ...@@ -694,6 +720,7 @@ static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
if (ppp == 0) if (ppp == 0)
break; break;
file->private_data = &ppp->file; file->private_data = &ppp->file;
ppp->owner = file;
err = -EFAULT; err = -EFAULT;
if (put_user(ppp->file.index, (int *) arg)) if (put_user(ppp->file.index, (int *) arg))
break; break;
...@@ -704,31 +731,29 @@ static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file, ...@@ -704,31 +731,29 @@ static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
/* Attach to an existing ppp unit */ /* Attach to an existing ppp unit */
if (get_user(unit, (int *) arg)) if (get_user(unit, (int *) arg))
break; break;
spin_lock(&all_ppp_lock); down(&all_ppp_sem);
err = -ENXIO;
ppp = ppp_find_unit(unit); ppp = ppp_find_unit(unit);
if (ppp != 0) if (ppp != 0) {
atomic_inc(&ppp->file.refcnt); atomic_inc(&ppp->file.refcnt);
spin_unlock(&all_ppp_lock); file->private_data = &ppp->file;
err = -ENXIO; err = 0;
if (ppp == 0) }
break; up(&all_ppp_sem);
file->private_data = &ppp->file;
err = 0;
break; break;
case PPPIOCATTCHAN: case PPPIOCATTCHAN:
if (get_user(unit, (int *) arg)) if (get_user(unit, (int *) arg))
break; break;
spin_lock_bh(&all_channels_lock); spin_lock_bh(&all_channels_lock);
err = -ENXIO;
chan = ppp_find_channel(unit); chan = ppp_find_channel(unit);
if (chan != 0) if (chan != 0) {
atomic_inc(&chan->file.refcnt); atomic_inc(&chan->file.refcnt);
file->private_data = &chan->file;
err = 0;
}
spin_unlock_bh(&all_channels_lock); spin_unlock_bh(&all_channels_lock);
err = -ENXIO;
if (chan == 0)
break;
file->private_data = &chan->file;
err = 0;
break; break;
default: default:
...@@ -905,15 +930,16 @@ ppp_xmit_process(struct ppp *ppp) ...@@ -905,15 +930,16 @@ ppp_xmit_process(struct ppp *ppp)
struct sk_buff *skb; struct sk_buff *skb;
ppp_xmit_lock(ppp); ppp_xmit_lock(ppp);
ppp_push(ppp); if (ppp->dev != 0) {
while (ppp->xmit_pending == 0 ppp_push(ppp);
&& (skb = skb_dequeue(&ppp->file.xq)) != 0) while (ppp->xmit_pending == 0
ppp_send_frame(ppp, skb); && (skb = skb_dequeue(&ppp->file.xq)) != 0)
/* If there's no work left to do, tell the core net ppp_send_frame(ppp, skb);
code that we can accept some more. */ /* If there's no work left to do, tell the core net
if (ppp->xmit_pending == 0 && skb_peek(&ppp->file.xq) == 0 code that we can accept some more. */
&& ppp->dev != 0) if (ppp->xmit_pending == 0 && skb_peek(&ppp->file.xq) == 0)
netif_wake_queue(ppp->dev); netif_wake_queue(ppp->dev);
}
ppp_xmit_unlock(ppp); ppp_xmit_unlock(ppp);
} }
...@@ -1526,6 +1552,7 @@ ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb) ...@@ -1526,6 +1552,7 @@ ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb)
error indication. */ error indication. */
if (len == DECOMP_FATALERROR) if (len == DECOMP_FATALERROR)
ppp->rstate |= SC_DC_FERROR; ppp->rstate |= SC_DC_FERROR;
kfree_skb(ns);
goto err; goto err;
} }
...@@ -1797,7 +1824,7 @@ ppp_register_channel(struct ppp_channel *chan) ...@@ -1797,7 +1824,7 @@ ppp_register_channel(struct ppp_channel *chan)
{ {
struct channel *pch; struct channel *pch;
pch = kmalloc(sizeof(struct channel), GFP_ATOMIC); pch = kmalloc(sizeof(struct channel), GFP_KERNEL);
if (pch == 0) if (pch == 0)
return -ENOMEM; return -ENOMEM;
memset(pch, 0, sizeof(struct channel)); memset(pch, 0, sizeof(struct channel));
...@@ -1809,11 +1836,13 @@ ppp_register_channel(struct ppp_channel *chan) ...@@ -1809,11 +1836,13 @@ ppp_register_channel(struct ppp_channel *chan)
#ifdef CONFIG_PPP_MULTILINK #ifdef CONFIG_PPP_MULTILINK
pch->lastseq = -1; pch->lastseq = -1;
#endif /* CONFIG_PPP_MULTILINK */ #endif /* CONFIG_PPP_MULTILINK */
init_rwsem(&pch->chan_sem);
spin_lock_init(&pch->downl); spin_lock_init(&pch->downl);
pch->upl = RW_LOCK_UNLOCKED; pch->upl = RW_LOCK_UNLOCKED;
spin_lock_bh(&all_channels_lock); spin_lock_bh(&all_channels_lock);
pch->file.index = ++last_channel_index; pch->file.index = ++last_channel_index;
list_add(&pch->file.list, &all_channels); list_add(&pch->list, &new_channels);
atomic_inc(&channel_count);
spin_unlock_bh(&all_channels_lock); spin_unlock_bh(&all_channels_lock);
MOD_INC_USE_COUNT; MOD_INC_USE_COUNT;
return 0; return 0;
...@@ -1826,7 +1855,9 @@ int ppp_channel_index(struct ppp_channel *chan) ...@@ -1826,7 +1855,9 @@ int ppp_channel_index(struct ppp_channel *chan)
{ {
struct channel *pch = chan->ppp; struct channel *pch = chan->ppp;
return pch->file.index; if (pch != 0)
return pch->file.index;
return -1;
} }
/* /*
...@@ -1848,7 +1879,7 @@ int ppp_unit_number(struct ppp_channel *chan) ...@@ -1848,7 +1879,7 @@ int ppp_unit_number(struct ppp_channel *chan)
/* /*
* Disconnect a channel from the generic layer. * Disconnect a channel from the generic layer.
* This can be called from mainline or BH/softirq level. * This must be called in process context.
*/ */
void void
ppp_unregister_channel(struct ppp_channel *chan) ppp_unregister_channel(struct ppp_channel *chan)
...@@ -1863,14 +1894,17 @@ ppp_unregister_channel(struct ppp_channel *chan) ...@@ -1863,14 +1894,17 @@ ppp_unregister_channel(struct ppp_channel *chan)
* This ensures that we have returned from any calls into the * This ensures that we have returned from any calls into the
* the channel's start_xmit or ioctl routine before we proceed. * the channel's start_xmit or ioctl routine before we proceed.
*/ */
down_write(&pch->chan_sem);
spin_lock_bh(&pch->downl); spin_lock_bh(&pch->downl);
pch->chan = 0; pch->chan = 0;
spin_unlock_bh(&pch->downl); spin_unlock_bh(&pch->downl);
up_write(&pch->chan_sem);
ppp_disconnect_channel(pch); ppp_disconnect_channel(pch);
wake_up_interruptible(&pch->file.rwait);
spin_lock_bh(&all_channels_lock); spin_lock_bh(&all_channels_lock);
list_del(&pch->file.list); list_del(&pch->list);
spin_unlock_bh(&all_channels_lock); spin_unlock_bh(&all_channels_lock);
pch->file.dead = 1;
wake_up_interruptible(&pch->file.rwait);
if (atomic_dec_and_test(&pch->file.refcnt)) if (atomic_dec_and_test(&pch->file.refcnt))
ppp_destroy_channel(pch); ppp_destroy_channel(pch);
MOD_DEC_USE_COUNT; MOD_DEC_USE_COUNT;
...@@ -1899,9 +1933,9 @@ static int ...@@ -1899,9 +1933,9 @@ static int
ppp_set_compress(struct ppp *ppp, unsigned long arg) ppp_set_compress(struct ppp *ppp, unsigned long arg)
{ {
int err; int err;
struct compressor *cp; struct compressor *cp, *ocomp;
struct ppp_option_data data; struct ppp_option_data data;
void *state; void *state, *ostate;
unsigned char ccp_option[CCP_MAX_OPTION_LENGTH]; unsigned char ccp_option[CCP_MAX_OPTION_LENGTH];
#ifdef CONFIG_KMOD #ifdef CONFIG_KMOD
char modname[32]; char modname[32];
...@@ -1927,41 +1961,39 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg) ...@@ -1927,41 +1961,39 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg)
#endif /* CONFIG_KMOD */ #endif /* CONFIG_KMOD */
if (cp == 0) if (cp == 0)
goto out; goto out;
/*
* XXX race: the compressor module could get unloaded between
* here and when we do the comp_alloc or decomp_alloc call below.
*/
err = -ENOBUFS; err = -ENOBUFS;
if (data.transmit) { if (data.transmit) {
ppp_xmit_lock(ppp);
ppp->xstate &= ~SC_COMP_RUN;
if (ppp->xc_state != 0) {
ppp->xcomp->comp_free(ppp->xc_state);
ppp->xc_state = 0;
}
ppp_xmit_unlock(ppp);
state = cp->comp_alloc(ccp_option, data.length); state = cp->comp_alloc(ccp_option, data.length);
if (state != 0) { if (state != 0) {
ppp_xmit_lock(ppp); ppp_xmit_lock(ppp);
ppp->xstate &= ~SC_COMP_RUN;
ocomp = ppp->xcomp;
ostate = ppp->xc_state;
ppp->xcomp = cp; ppp->xcomp = cp;
ppp->xc_state = state; ppp->xc_state = state;
ppp_xmit_unlock(ppp); ppp_xmit_unlock(ppp);
if (ostate != 0)
ocomp->comp_free(ostate);
err = 0; err = 0;
} }
} else { } else {
ppp_recv_lock(ppp);
ppp->rstate &= ~SC_DECOMP_RUN;
if (ppp->rc_state != 0) {
ppp->rcomp->decomp_free(ppp->rc_state);
ppp->rc_state = 0;
}
ppp_recv_unlock(ppp);
state = cp->decomp_alloc(ccp_option, data.length); state = cp->decomp_alloc(ccp_option, data.length);
if (state != 0) { if (state != 0) {
ppp_recv_lock(ppp); ppp_recv_lock(ppp);
ppp->rstate &= ~SC_DECOMP_RUN;
ocomp = ppp->rcomp;
ostate = ppp->rc_state;
ppp->rcomp = cp; ppp->rcomp = cp;
ppp->rc_state = state; ppp->rc_state = state;
ppp_recv_unlock(ppp); ppp_recv_unlock(ppp);
if (ostate != 0)
ocomp->decomp_free(ostate);
err = 0; err = 0;
} }
} }
...@@ -2059,19 +2091,25 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound) ...@@ -2059,19 +2091,25 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound)
static void static void
ppp_ccp_closed(struct ppp *ppp) ppp_ccp_closed(struct ppp *ppp)
{ {
ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP); void *xstate, *rstate;
struct compressor *xcomp, *rcomp;
ppp->xstate &= ~SC_COMP_RUN; ppp_lock(ppp);
if (ppp->xc_state) { ppp->flags &= ~(SC_CCP_OPEN | SC_CCP_UP);
ppp->xcomp->comp_free(ppp->xc_state); ppp->xstate = 0;
ppp->xc_state = 0; xcomp = ppp->xcomp;
} xstate = ppp->xc_state;
ppp->xc_state = 0;
ppp->rstate = 0;
rcomp = ppp->rcomp;
rstate = ppp->rc_state;
ppp->rc_state = 0;
ppp_unlock(ppp);
ppp->xstate &= ~SC_DECOMP_RUN; if (xstate)
if (ppp->rc_state) { xcomp->comp_free(xstate);
ppp->rcomp->decomp_free(ppp->rc_state); if (rstate)
ppp->rc_state = 0; rcomp->decomp_free(rstate);
}
} }
/* List of compressors. */ /* List of compressors. */
...@@ -2191,39 +2229,27 @@ static struct ppp * ...@@ -2191,39 +2229,27 @@ static struct ppp *
ppp_create_interface(int unit, int *retp) ppp_create_interface(int unit, int *retp)
{ {
struct ppp *ppp; struct ppp *ppp;
struct net_device *dev; struct net_device *dev = NULL;
struct list_head *list; int ret = -ENOMEM;
int last_unit = -1;
int ret = -EEXIST;
int i; int i;
spin_lock(&all_ppp_lock); ppp = kmalloc(sizeof(struct ppp), GFP_KERNEL);
list = &all_ppp_units;
while ((list = list->next) != &all_ppp_units) {
ppp = list_entry(list, struct ppp, file.list);
if ((unit < 0 && ppp->file.index > last_unit + 1)
|| (unit >= 0 && unit < ppp->file.index))
break;
if (unit == ppp->file.index)
goto out; /* unit already exists */
last_unit = ppp->file.index;
}
if (unit < 0)
unit = last_unit + 1;
/* Create a new ppp structure and link it before `list'. */
ret = -ENOMEM;
ppp = kmalloc(sizeof(struct ppp), GFP_ATOMIC);
if (ppp == 0) if (ppp == 0)
goto out; goto err;
dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
if (dev == 0)
goto err;
memset(ppp, 0, sizeof(struct ppp)); memset(ppp, 0, sizeof(struct ppp));
dev = kmalloc(sizeof(struct net_device), GFP_ATOMIC);
if (dev == 0) {
kfree(ppp);
goto out;
}
memset(dev, 0, sizeof(struct net_device)); memset(dev, 0, sizeof(struct net_device));
ret = -EEXIST;
down(&all_ppp_sem);
if (unit < 0)
unit = cardmap_find_first_free(all_ppp_units);
else if (cardmap_get(all_ppp_units, unit) != NULL)
goto err_unlock; /* unit already exists */
/* Initialize the new ppp unit */
ppp->file.index = unit; ppp->file.index = unit;
ppp->mru = PPP_MRU; ppp->mru = PPP_MRU;
init_ppp_file(&ppp->file, INTERFACE); init_ppp_file(&ppp->file, INTERFACE);
...@@ -2248,19 +2274,26 @@ ppp_create_interface(int unit, int *retp) ...@@ -2248,19 +2274,26 @@ ppp_create_interface(int unit, int *retp)
ret = register_netdevice(dev); ret = register_netdevice(dev);
rtnl_unlock(); rtnl_unlock();
if (ret != 0) { if (ret != 0) {
printk(KERN_ERR "PPP: couldn't register device (%d)\n", ret); printk(KERN_ERR "PPP: couldn't register device %s (%d)\n",
kfree(dev); dev->name, ret);
kfree(ppp); goto err_unlock;
goto out;
} }
list_add(&ppp->file.list, list->prev); atomic_inc(&ppp_unit_count);
out: cardmap_set(&all_ppp_units, unit, ppp);
spin_unlock(&all_ppp_lock); up(&all_ppp_sem);
*retp = ret; *retp = 0;
if (ret != 0)
ppp = 0;
return ppp; return ppp;
err_unlock:
up(&all_ppp_sem);
err:
*retp = ret;
if (ppp)
kfree(ppp);
if (dev)
kfree(dev);
return NULL;
} }
/* /*
...@@ -2277,19 +2310,48 @@ init_ppp_file(struct ppp_file *pf, int kind) ...@@ -2277,19 +2310,48 @@ init_ppp_file(struct ppp_file *pf, int kind)
} }
/* /*
* Free up all the resources used by a ppp interface unit. * Take down a ppp interface unit - called when the owning file
* (the one that created the unit) is closed or detached.
*/ */
static void ppp_destroy_interface(struct ppp *ppp) static void ppp_shutdown_interface(struct ppp *ppp)
{ {
struct net_device *dev; struct net_device *dev;
int n_channels ;
spin_lock(&all_ppp_lock); down(&all_ppp_sem);
list_del(&ppp->file.list);
/* Last fd open to this ppp unit is being closed or detached:
mark the interface down, free the ppp unit */
ppp_lock(ppp); ppp_lock(ppp);
dev = ppp->dev;
ppp->dev = 0;
ppp_unlock(ppp);
if (dev) {
rtnl_lock();
dev_close(dev);
unregister_netdevice(dev);
rtnl_unlock();
}
cardmap_set(&all_ppp_units, ppp->file.index, NULL);
ppp->file.dead = 1;
ppp->owner = NULL;
wake_up_interruptible(&ppp->file.rwait);
up(&all_ppp_sem);
}
/*
* Free the memory used by a ppp unit. This is only called once
* there are no channels connected to the unit and no file structs
* that reference the unit.
*/
static void ppp_destroy_interface(struct ppp *ppp)
{
atomic_dec(&ppp_unit_count);
if (!ppp->file.dead || ppp->n_channels) {
/* "can't happen" */
printk(KERN_ERR "ppp: destroying ppp struct %p but dead=%d "
"n_channels=%d !\n", ppp, ppp->file.dead,
ppp->n_channels);
return;
}
ppp_ccp_closed(ppp); ppp_ccp_closed(ppp);
if (ppp->vj) { if (ppp->vj) {
slhc_free(ppp->vj); slhc_free(ppp->vj);
...@@ -2310,52 +2372,27 @@ static void ppp_destroy_interface(struct ppp *ppp) ...@@ -2310,52 +2372,27 @@ static void ppp_destroy_interface(struct ppp *ppp)
ppp->active_filter.filter = 0; ppp->active_filter.filter = 0;
} }
#endif /* CONFIG_PPP_FILTER */ #endif /* CONFIG_PPP_FILTER */
dev = ppp->dev;
ppp->dev = 0;
n_channels = ppp->n_channels ;
ppp_unlock(ppp);
if (dev) {
rtnl_lock();
dev_close(dev);
unregister_netdevice(dev);
rtnl_unlock();
}
/* kfree(ppp);
* We can't acquire any new channels (since we have the
* all_ppp_lock) so if n_channels is 0, we can free the
* ppp structure. Otherwise we leave it around until the
* last channel disconnects from it.
*/
if (n_channels == 0)
kfree(ppp);
spin_unlock(&all_ppp_lock);
} }
/* /*
* Locate an existing ppp unit. * Locate an existing ppp unit.
* The caller should have locked the all_ppp_lock. * The caller should have locked the all_ppp_sem.
*/ */
static struct ppp * static struct ppp *
ppp_find_unit(int unit) ppp_find_unit(int unit)
{ {
struct ppp *ppp; return cardmap_get(all_ppp_units, unit);
struct list_head *list;
list = &all_ppp_units;
while ((list = list->next) != &all_ppp_units) {
ppp = list_entry(list, struct ppp, file.list);
if (ppp->file.index == unit)
return ppp;
}
return 0;
} }
/* /*
* Locate an existing ppp channel. * Locate an existing ppp channel.
* The caller should have locked the all_channels_lock. * The caller should have locked the all_channels_lock.
* First we look in the new_channels list, then in the
* all_channels list. If found in the new_channels list,
* we move it to the all_channels list. This is for speed
* when we have a lot of channels in use.
*/ */
static struct channel * static struct channel *
ppp_find_channel(int unit) ppp_find_channel(int unit)
...@@ -2363,9 +2400,18 @@ ppp_find_channel(int unit) ...@@ -2363,9 +2400,18 @@ ppp_find_channel(int unit)
struct channel *pch; struct channel *pch;
struct list_head *list; struct list_head *list;
list = &new_channels;
while ((list = list->next) != &new_channels) {
pch = list_entry(list, struct channel, list);
if (pch->file.index == unit) {
list_del(&pch->list);
list_add(&pch->list, &all_channels);
return pch;
}
}
list = &all_channels; list = &all_channels;
while ((list = list->next) != &all_channels) { while ((list = list->next) != &all_channels) {
pch = list_entry(list, struct channel, file.list); pch = list_entry(list, struct channel, list);
if (pch->file.index == unit) if (pch->file.index == unit)
return pch; return pch;
} }
...@@ -2382,19 +2428,16 @@ ppp_connect_channel(struct channel *pch, int unit) ...@@ -2382,19 +2428,16 @@ ppp_connect_channel(struct channel *pch, int unit)
int ret = -ENXIO; int ret = -ENXIO;
int hdrlen; int hdrlen;
spin_lock(&all_ppp_lock); down(&all_ppp_sem);
ppp = ppp_find_unit(unit); ppp = ppp_find_unit(unit);
if (ppp == 0) if (ppp == 0)
goto out; goto out;
write_lock_bh(&pch->upl); write_lock_bh(&pch->upl);
ret = -EINVAL; ret = -EINVAL;
if (pch->ppp != 0) if (pch->ppp != 0)
goto outwl; goto outl;
ppp_lock(ppp);
spin_lock_bh(&pch->downl);
if (pch->chan == 0) /* need to check this?? */
goto outr;
ppp_lock(ppp);
if (pch->file.hdrlen > ppp->file.hdrlen) if (pch->file.hdrlen > ppp->file.hdrlen)
ppp->file.hdrlen = pch->file.hdrlen; ppp->file.hdrlen = pch->file.hdrlen;
hdrlen = pch->file.hdrlen + 2; /* for protocol bytes */ hdrlen = pch->file.hdrlen + 2; /* for protocol bytes */
...@@ -2403,15 +2446,14 @@ ppp_connect_channel(struct channel *pch, int unit) ...@@ -2403,15 +2446,14 @@ ppp_connect_channel(struct channel *pch, int unit)
list_add_tail(&pch->clist, &ppp->channels); list_add_tail(&pch->clist, &ppp->channels);
++ppp->n_channels; ++ppp->n_channels;
pch->ppp = ppp; pch->ppp = ppp;
atomic_inc(&ppp->file.refcnt);
ppp_unlock(ppp);
ret = 0; ret = 0;
outr: outl:
spin_unlock_bh(&pch->downl);
ppp_unlock(ppp);
outwl:
write_unlock_bh(&pch->upl); write_unlock_bh(&pch->upl);
out: out:
spin_unlock(&all_ppp_lock); up(&all_ppp_sem);
return ret; return ret;
} }
...@@ -2423,25 +2465,21 @@ ppp_disconnect_channel(struct channel *pch) ...@@ -2423,25 +2465,21 @@ ppp_disconnect_channel(struct channel *pch)
{ {
struct ppp *ppp; struct ppp *ppp;
int err = -EINVAL; int err = -EINVAL;
int dead;
write_lock_bh(&pch->upl); write_lock_bh(&pch->upl);
ppp = pch->ppp; ppp = pch->ppp;
pch->ppp = NULL;
write_unlock_bh(&pch->upl);
if (ppp != 0) { if (ppp != 0) {
/* remove it from the ppp unit's list */ /* remove it from the ppp unit's list */
pch->ppp = NULL;
ppp_lock(ppp); ppp_lock(ppp);
list_del(&pch->clist); list_del(&pch->clist);
--ppp->n_channels; --ppp->n_channels;
dead = ppp->dev == 0 && ppp->n_channels == 0;
ppp_unlock(ppp); ppp_unlock(ppp);
if (dead) if (atomic_dec_and_test(&ppp->file.refcnt))
/* Last disconnect from a ppp unit ppp_destroy_interface(ppp);
that is already dead: free it. */
kfree(ppp);
err = 0; err = 0;
} }
write_unlock_bh(&pch->upl);
return err; return err;
} }
...@@ -2450,6 +2488,14 @@ ppp_disconnect_channel(struct channel *pch) ...@@ -2450,6 +2488,14 @@ ppp_disconnect_channel(struct channel *pch)
*/ */
static void ppp_destroy_channel(struct channel *pch) static void ppp_destroy_channel(struct channel *pch)
{ {
atomic_dec(&channel_count);
if (!pch->file.dead) {
/* "can't happen" */
printk(KERN_ERR "ppp: destroying undead channel %p !\n",
pch);
return;
}
skb_queue_purge(&pch->file.xq); skb_queue_purge(&pch->file.xq);
skb_queue_purge(&pch->file.rq); skb_queue_purge(&pch->file.rq);
kfree(pch); kfree(pch);
...@@ -2458,13 +2504,124 @@ static void ppp_destroy_channel(struct channel *pch) ...@@ -2458,13 +2504,124 @@ static void ppp_destroy_channel(struct channel *pch)
static void __exit ppp_cleanup(void) static void __exit ppp_cleanup(void)
{ {
/* should never happen */ /* should never happen */
if (!list_empty(&all_ppp_units) || !list_empty(&all_channels)) if (atomic_read(&ppp_unit_count) || atomic_read(&channel_count))
printk(KERN_ERR "PPP: removing module but units remain!\n"); printk(KERN_ERR "PPP: removing module but units remain!\n");
cardmap_destroy(&all_ppp_units);
if (devfs_unregister_chrdev(PPP_MAJOR, "ppp") != 0) if (devfs_unregister_chrdev(PPP_MAJOR, "ppp") != 0)
printk(KERN_ERR "PPP: failed to unregister PPP device\n"); printk(KERN_ERR "PPP: failed to unregister PPP device\n");
devfs_unregister(devfs_handle); devfs_unregister(devfs_handle);
} }
/*
* Cardmap implementation.
*/
static void *cardmap_get(struct cardmap *map, unsigned int nr)
{
struct cardmap *p;
int i;
for (p = map; p != NULL; ) {
if ((i = nr >> p->shift) >= CARDMAP_WIDTH)
return NULL;
if (p->shift == 0)
return p->ptr[i];
nr &= ~(CARDMAP_MASK << p->shift);
p = p->ptr[i];
}
return NULL;
}
static void cardmap_set(struct cardmap **pmap, unsigned int nr, void *ptr)
{
struct cardmap *p;
int i;
p = *pmap;
if (p == NULL || (nr >> p->shift) >= CARDMAP_WIDTH) {
do {
/* need a new top level */
struct cardmap *np = kmalloc(sizeof(*np), GFP_KERNEL);
memset(np, 0, sizeof(*np));
np->ptr[0] = p;
if (p != NULL) {
np->shift = p->shift + CARDMAP_ORDER;
p->parent = np;
} else
np->shift = 0;
p = np;
} while ((nr >> p->shift) >= CARDMAP_WIDTH);
*pmap = p;
}
while (p->shift > 0) {
i = (nr >> p->shift) & CARDMAP_MASK;
if (p->ptr[i] == NULL) {
struct cardmap *np = kmalloc(sizeof(*np), GFP_KERNEL);
memset(np, 0, sizeof(*np));
np->shift = p->shift - CARDMAP_ORDER;
np->parent = p;
p->ptr[i] = np;
}
if (ptr == NULL)
clear_bit(i, &p->inuse);
p = p->ptr[i];
}
i = nr & CARDMAP_MASK;
p->ptr[i] = ptr;
if (ptr != NULL)
set_bit(i, &p->inuse);
else
clear_bit(i, &p->inuse);
}
static unsigned int cardmap_find_first_free(struct cardmap *map)
{
struct cardmap *p;
unsigned int nr = 0;
int i;
if ((p = map) == NULL)
return 0;
for (;;) {
i = find_first_zero_bit(&p->inuse, CARDMAP_WIDTH);
if (i >= CARDMAP_WIDTH) {
if (p->parent == NULL)
return CARDMAP_WIDTH << p->shift;
p = p->parent;
i = (nr >> p->shift) & CARDMAP_MASK;
set_bit(i, &p->inuse);
continue;
}
nr = (nr & (~CARDMAP_MASK << p->shift)) | (i << p->shift);
if (p->shift == 0 || p->ptr[i] == NULL)
return nr;
p = p->ptr[i];
}
}
static void cardmap_destroy(struct cardmap **pmap)
{
struct cardmap *p, *np;
int i;
for (p = *pmap; p != NULL; p = np) {
if (p->shift != 0) {
for (i = 0; i < CARDMAP_WIDTH; ++i)
if (p->ptr[i] != NULL)
break;
if (i < CARDMAP_WIDTH) {
np = p->ptr[i];
p->ptr[i] = NULL;
continue;
}
}
np = p->parent;
kfree(p);
}
*pmap = NULL;
}
/* Module/initialization stuff */
module_init(ppp_init); module_init(ppp_init);
module_exit(ppp_cleanup); module_exit(ppp_cleanup);
......
...@@ -25,11 +25,11 @@ ...@@ -25,11 +25,11 @@
* the generic PPP layer to give it frames to send and to process * the generic PPP layer to give it frames to send and to process
* received frames. It implements the PPP line discipline. * received frames. It implements the PPP line discipline.
* *
* Part of the code in this driver was inspired by the old sync-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 20000322== * ==FILEVERSION 20020125==
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -41,10 +41,12 @@ ...@@ -41,10 +41,12 @@
#include <linux/ppp_defs.h> #include <linux/ppp_defs.h>
#include <linux/if_ppp.h> #include <linux/if_ppp.h>
#include <linux/ppp_channel.h> #include <linux/ppp_channel.h>
#include <linux/spinlock.h>
#include <linux/init.h> #include <linux/init.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/semaphore.h>
#define PPP_VERSION "2.4.1" #define PPP_VERSION "2.4.2"
/* Structure for storing local state. */ /* Structure for storing local state. */
struct syncppp { struct syncppp {
...@@ -65,6 +67,8 @@ struct syncppp { ...@@ -65,6 +67,8 @@ struct syncppp {
struct sk_buff *rpkt; struct sk_buff *rpkt;
atomic_t refcnt;
struct semaphore dead_sem;
struct ppp_channel chan; /* interface to generic ppp layer */ struct ppp_channel chan; /* interface to generic ppp layer */
}; };
...@@ -161,7 +165,36 @@ ppp_print_buffer (const char *name, const __u8 *buf, int count) ...@@ -161,7 +165,36 @@ ppp_print_buffer (const char *name, const __u8 *buf, int count)
*/ */
/* /*
* Called when a tty is put into line discipline. * We have a potential race on dereferencing tty->disc_data,
* because the tty layer provides no locking at all - thus one
* cpu could be running ppp_synctty_receive while another
* calls ppp_synctty_close, which zeroes tty->disc_data and
* frees the memory that ppp_synctty_receive is using. The best
* way to fix this is to use a rwlock in the tty struct, but for now
* we use a single global rwlock for all ttys in ppp line discipline.
*/
static rwlock_t disc_data_lock = RW_LOCK_UNLOCKED;
static struct syncppp *sp_get(struct tty_struct *tty)
{
struct syncppp *ap;
read_lock(&disc_data_lock);
ap = tty->disc_data;
if (ap != NULL)
atomic_inc(&ap->refcnt);
read_unlock(&disc_data_lock);
return ap;
}
static void sp_put(struct syncppp *ap)
{
if (atomic_dec_and_test(&ap->refcnt))
up(&ap->dead_sem);
}
/*
* Called when a tty is put into sync-PPP line discipline.
*/ */
static int static int
ppp_sync_open(struct tty_struct *tty) ppp_sync_open(struct tty_struct *tty)
...@@ -185,6 +218,9 @@ ppp_sync_open(struct tty_struct *tty) ...@@ -185,6 +218,9 @@ ppp_sync_open(struct tty_struct *tty)
ap->xaccm[3] = 0x60000000U; ap->xaccm[3] = 0x60000000U;
ap->raccm = ~0U; ap->raccm = ~0U;
atomic_set(&ap->refcnt, 1);
init_MUTEX_LOCKED(&ap->dead_sem);
ap->chan.private = ap; ap->chan.private = ap;
ap->chan.ops = &sync_ops; ap->chan.ops = &sync_ops;
ap->chan.mtu = PPP_MRU; ap->chan.mtu = PPP_MRU;
...@@ -206,16 +242,34 @@ ppp_sync_open(struct tty_struct *tty) ...@@ -206,16 +242,34 @@ ppp_sync_open(struct tty_struct *tty)
/* /*
* Called when the tty is put into another line discipline * Called when the tty is put into another line discipline
* (or it hangs up). * or it hangs up. We have to wait for any cpu currently
* executing in any of the other ppp_synctty_* routines to
* finish before we can call ppp_unregister_channel and free
* the syncppp struct. This routine must be called from
* process context, not interrupt or softirq context.
*/ */
static void static void
ppp_sync_close(struct tty_struct *tty) ppp_sync_close(struct tty_struct *tty)
{ {
struct syncppp *ap = tty->disc_data; struct syncppp *ap;
write_lock(&disc_data_lock);
ap = tty->disc_data;
tty->disc_data = 0;
write_unlock(&disc_data_lock);
if (ap == 0) if (ap == 0)
return; return;
tty->disc_data = 0;
/*
* We have now ensured that nobody can start using ap from now
* on, but we have to wait for all existing users to finish.
* Note that ppp_unregister_channel ensures that no calls to
* our channel ops (i.e. ppp_sync_send/ioctl) are in progress
* by the time it returns.
*/
if (!atomic_dec_and_test(&ap->refcnt))
down(&ap->dead_sem);
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);
...@@ -251,9 +305,11 @@ static int ...@@ -251,9 +305,11 @@ static int
ppp_synctty_ioctl(struct tty_struct *tty, struct file *file, ppp_synctty_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
struct syncppp *ap = tty->disc_data; struct syncppp *ap = sp_get(tty);
int err, val; int err, val;
if (ap == 0)
return -ENXIO;
err = -EFAULT; err = -EFAULT;
switch (cmd) { switch (cmd) {
case PPPIOCGCHAN: case PPPIOCGCHAN:
...@@ -299,6 +355,7 @@ ppp_synctty_ioctl(struct tty_struct *tty, struct file *file, ...@@ -299,6 +355,7 @@ ppp_synctty_ioctl(struct tty_struct *tty, struct file *file,
err = -ENOIOCTLCMD; err = -ENOIOCTLCMD;
} }
sp_put(ap);
return err; return err;
} }
...@@ -319,13 +376,14 @@ static void ...@@ -319,13 +376,14 @@ 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 *flags, int count)
{ {
struct syncppp *ap = tty->disc_data; struct syncppp *ap = sp_get(tty);
if (ap == 0) if (ap == 0)
return; return;
spin_lock_bh(&ap->recv_lock); spin_lock_bh(&ap->recv_lock);
ppp_sync_input(ap, buf, flags, count); ppp_sync_input(ap, buf, flags, count);
spin_unlock_bh(&ap->recv_lock); spin_unlock_bh(&ap->recv_lock);
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)
tty->driver.unthrottle(tty); tty->driver.unthrottle(tty);
...@@ -334,13 +392,14 @@ ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, ...@@ -334,13 +392,14 @@ ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf,
static void static void
ppp_sync_wakeup(struct tty_struct *tty) ppp_sync_wakeup(struct tty_struct *tty)
{ {
struct syncppp *ap = tty->disc_data; struct syncppp *ap = sp_get(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)) if (ppp_sync_push(ap))
ppp_output_wakeup(&ap->chan); ppp_output_wakeup(&ap->chan);
sp_put(ap);
} }
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* PPPoE --- PPP over Ethernet (RFC 2516) * PPPoE --- PPP over Ethernet (RFC 2516)
* *
* *
* Version: 0.6.9 * Version: 0.6.10
* *
* 220102 : Fix module use count on failure in pppoe_create, pppox_sk -acme * 220102 : Fix module use count on failure in pppoe_create, pppox_sk -acme
* 030700 : Fixed connect logic to allow for disconnect. * 030700 : Fixed connect logic to allow for disconnect.
...@@ -32,6 +32,9 @@ ...@@ -32,6 +32,9 @@
* a memory leak. * a memory leak.
* 081001 : Misc. cleanup (licence string, non-blocking, prevent * 081001 : Misc. cleanup (licence string, non-blocking, prevent
* reference of device on close). * reference of device on close).
* 121301 : New ppp channels interface; cannot unregister a channel
* from interrupts. Thus, we mark the socket as a ZOMBIE
* and do the unregistration later.
* *
* Author: Michal Ostrowski <mostrows@speakeasy.net> * Author: Michal Ostrowski <mostrows@speakeasy.net>
* Contributors: * Contributors:
...@@ -87,11 +90,11 @@ struct proto_ops pppoe_ops; ...@@ -87,11 +90,11 @@ struct proto_ops pppoe_ops;
#if 0 #if 0
#define CHECKPTR(x,y) { if (!(x) && pppoe_debug &7 ){ printk(KERN_CRIT "PPPoE Invalid pointer : %s , %p\n",#x,(x)); error=-EINVAL; goto y; }} #define CHECKPTR(x,y) do { if (!(x) && pppoe_debug &7 ){ printk(KERN_CRIT "PPPoE Invalid pointer : %s , %p\n",#x,(x)); error=-EINVAL; goto y; }} while (0)
#define DEBUG(s,args...) if( pppoe_debug & (s) ) printk(KERN_CRIT args ); #define DEBUG(s,args...) do { if( pppoe_debug & (s) ) printk(KERN_CRIT args ); } while (0)
#else #else
#define CHECKPTR(x,y) do {} while (0); #define CHECKPTR(x,y) do { } while (0)
#define DEBUG(s,args...) do { } while (0); #define DEBUG(s,args...) do { } while (0)
#endif #endif
...@@ -275,10 +278,10 @@ static void pppoe_flush_dev(struct net_device *dev) ...@@ -275,10 +278,10 @@ static void pppoe_flush_dev(struct net_device *dev)
lock_sock(sk); lock_sock(sk);
if (sk->state & (PPPOX_CONNECTED | PPPOX_BOUND)) { if (sk->state & (PPPOX_CONNECTED|PPPOX_BOUND)){
pppox_unbind_sock(sk); pppox_unbind_sock(sk);
dev_put(dev); dev_put(dev);
sk->state = PPPOX_DEAD; sk->state = PPPOX_ZOMBIE;
sk->state_change(sk); sk->state_change(sk);
} }
...@@ -441,8 +444,10 @@ static int pppoe_disc_rcv(struct sk_buff *skb, ...@@ -441,8 +444,10 @@ static int pppoe_disc_rcv(struct sk_buff *skb,
* one socket family type, we cannot (easily) distinguish * one socket family type, we cannot (easily) distinguish
* what kind of SKB it is during backlog rcv. * what kind of SKB it is during backlog rcv.
*/ */
if (sk->lock.users == 0) if (sk->lock.users == 0) {
sk->state = PPPOX_ZOMBIE;
pppox_unbind_sock(sk); pppox_unbind_sock(sk);
}
bh_unlock_sock(sk); bh_unlock_sock(sk);
sock_put(sk); sock_put(sk);
...@@ -719,7 +724,7 @@ int pppoe_ioctl(struct socket *sock, unsigned int cmd, ...@@ -719,7 +724,7 @@ int pppoe_ioctl(struct socket *sock, unsigned int cmd,
struct pppox_opt *relay_po; struct pppox_opt *relay_po;
err = -EBUSY; err = -EBUSY;
if (sk->state & PPPOX_BOUND) if (sk->state & (PPPOX_BOUND|PPPOX_ZOMBIE|PPPOX_DEAD))
break; break;
err = -ENOTCONN; err = -ENOTCONN;
......
...@@ -147,13 +147,14 @@ extern void pppox_unbind_sock(struct sock *sk);/* delete ppp-channel binding */ ...@@ -147,13 +147,14 @@ extern void pppox_unbind_sock(struct sock *sk);/* delete ppp-channel binding */
extern int pppox_channel_ioctl(struct ppp_channel *pc, unsigned int cmd, extern int pppox_channel_ioctl(struct ppp_channel *pc, unsigned int cmd,
unsigned long arg); unsigned long arg);
/* PPPoE socket states */ /* PPPoX socket states */
enum { enum {
PPPOX_NONE = 0, /* initial state */ PPPOX_NONE = 0, /* initial state */
PPPOX_CONNECTED = 1, /* connection established ==TCP_ESTABLISHED */ PPPOX_CONNECTED = 1, /* connection established ==TCP_ESTABLISHED */
PPPOX_BOUND = 2, /* bound to ppp device */ PPPOX_BOUND = 2, /* bound to ppp device */
PPPOX_RELAY = 4, /* forwarding is enabled */ PPPOX_RELAY = 4, /* forwarding is enabled */
PPPOX_DEAD = 8 PPPOX_ZOMBIE = 8, /* dead, but still bound to ppp device */
PPPOX_DEAD = 16 /* dead, useless, please clean me up!*/
}; };
extern struct ppp_channel_ops pppoe_chan_ops; extern struct ppp_channel_ops pppoe_chan_ops;
......
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