patch-2.4.19 linux-2.4.19/drivers/net/ppp_synctty.c
Next file: linux-2.4.19/drivers/net/pppoe.c
Previous file: linux-2.4.19/drivers/net/ppp_generic.c
Back to the patch index
Back to the overall index
- Lines: 176
- Date:
Fri Aug 2 17:39:44 2002
- Orig file:
linux-2.4.18/drivers/net/ppp_synctty.c
- Orig date:
Thu Oct 11 11:17:22 2001
diff -urN linux-2.4.18/drivers/net/ppp_synctty.c linux-2.4.19/drivers/net/ppp_synctty.c
@@ -25,11 +25,11 @@
* the generic PPP layer to give it frames to send and to process
* 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
* subsequently hacked by Paul Mackerras.
*
- * ==FILEVERSION 20000322==
+ * ==FILEVERSION 20020125==
*/
#include <linux/module.h>
@@ -41,10 +41,12 @@
#include <linux/ppp_defs.h>
#include <linux/if_ppp.h>
#include <linux/ppp_channel.h>
+#include <linux/spinlock.h>
#include <linux/init.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. */
struct syncppp {
@@ -65,6 +67,8 @@
struct sk_buff *rpkt;
+ atomic_t refcnt;
+ struct semaphore dead_sem;
struct ppp_channel chan; /* interface to generic ppp layer */
};
@@ -161,7 +165,36 @@
*/
/*
- * 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
ppp_sync_open(struct tty_struct *tty)
@@ -185,6 +218,9 @@
ap->xaccm[3] = 0x60000000U;
ap->raccm = ~0U;
+ atomic_set(&ap->refcnt, 1);
+ init_MUTEX_LOCKED(&ap->dead_sem);
+
ap->chan.private = ap;
ap->chan.ops = &sync_ops;
ap->chan.mtu = PPP_MRU;
@@ -206,16 +242,34 @@
/*
* 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
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)
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);
if (ap->rpkt != 0)
kfree_skb(ap->rpkt);
@@ -251,9 +305,11 @@
ppp_synctty_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
- struct syncppp *ap = tty->disc_data;
+ struct syncppp *ap = sp_get(tty);
int err, val;
+ if (ap == 0)
+ return -ENXIO;
err = -EFAULT;
switch (cmd) {
case PPPIOCGCHAN:
@@ -299,6 +355,7 @@
err = -ENOIOCTLCMD;
}
+ sp_put(ap);
return err;
}
@@ -319,13 +376,14 @@
ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf,
char *flags, int count)
{
- struct syncppp *ap = tty->disc_data;
+ struct syncppp *ap = sp_get(tty);
if (ap == 0)
return;
spin_lock_bh(&ap->recv_lock);
ppp_sync_input(ap, buf, flags, count);
spin_unlock_bh(&ap->recv_lock);
+ sp_put(ap);
if (test_and_clear_bit(TTY_THROTTLED, &tty->flags)
&& tty->driver.unthrottle)
tty->driver.unthrottle(tty);
@@ -334,13 +392,14 @@
static void
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);
if (ap == 0)
return;
if (ppp_sync_push(ap))
ppp_output_wakeup(&ap->chan);
+ sp_put(ap);
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)