patch-2.1.89 linux/drivers/net/slip.c
Next file: linux/drivers/net/slip.h
Previous file: linux/drivers/net/shaper.c
Back to the patch index
Back to the overall index
- Lines: 1290
- Date:
Sun Mar 1 14:40:39 1998
- Orig file:
v2.1.88/linux/drivers/net/slip.c
- Orig date:
Mon Feb 23 18:12:06 1998
diff -u --recursive --new-file v2.1.88/linux/drivers/net/slip.c linux/drivers/net/slip.c
@@ -50,6 +50,8 @@
* from multislip BSDI driver which was written
* by Igor Chechik, RELCOM Corp. Only algorithms
* have been ported to Linux SLIP driver.
+ * Vitaly E. Lavrov : Sane behaviour on tty hangup.
+ * Alexey Kuznetsov : Cleanup interfaces to tty&netdevice modules.
*/
#define SL_CHECK_TRANSMIT
@@ -110,133 +112,139 @@
static int sl_ioctl(struct device *dev,struct ifreq *rq,int cmd);
#endif
-/* Find a free SLIP channel, and link in this `tty' line. */
-static inline struct slip *
-sl_alloc(void)
-{
- slip_ctrl_t *slp = NULL;
- int i;
-
- if (slip_ctrls == NULL) return NULL; /* Master array missing ! */
-
- for (i = 0; i < slip_maxdev; i++) {
- slp = slip_ctrls[i];
- /* Not allocated ? */
- if (slp == NULL)
- break;
- /* Not in use ? */
- if (!test_and_set_bit(SLF_INUSE, &slp->ctrl.flags))
- break;
- }
- /* SLP is set.. */
-
- /* Sorry, too many, all slots in use */
- if (i >= slip_maxdev) return NULL;
-
- /* If no channels are available, allocate one */
- if (!slp &&
- (slip_ctrls[i] = (slip_ctrl_t *)kmalloc(sizeof(slip_ctrl_t),
- GFP_KERNEL)) != NULL) {
- slp = slip_ctrls[i];
- memset(slp, 0, sizeof(slip_ctrl_t));
-
- /* Initialize channel control data */
- set_bit(SLF_INUSE, &slp->ctrl.flags);
- slp->ctrl.tty = NULL;
- sprintf(slp->if_name, "sl%d", i);
- slp->dev.name = slp->if_name;
- slp->dev.base_addr = i;
- slp->dev.priv = (void*)&(slp->ctrl);
- slp->dev.next = NULL;
- slp->dev.init = slip_init;
-/* printk(KERN_INFO "slip: kmalloc()ed SLIP control node for line %s\n",
- slp->if_name); */
- }
- if (slp != NULL) {
+/********************************
+* Buffer administration routines:
+* sl_alloc_bufs()
+* sl_free_bufs()
+* sl_realloc_bufs()
+*
+* NOTE: sl_realloc_bufs != sl_free_bufs + sl_alloc_bufs, because
+* sl_realloc_bufs provides strong atomicity and reallocation
+* on actively running device.
+*********************************/
- /* register device so that it can be ifconfig'ed */
- /* slip_init() will be called as a side-effect */
- /* SIDE-EFFECT WARNING: slip_init() CLEARS slp->ctrl ! */
-
- if (register_netdev(&(slp->dev)) == 0) {
- /* (Re-)Set the INUSE bit. Very Important! */
- set_bit(SLF_INUSE, &slp->ctrl.flags);
- slp->ctrl.dev = &(slp->dev);
- slp->dev.priv = (void*)&(slp->ctrl);
+/*
+ Allocate channel buffers.
+ */
-/* printk(KERN_INFO "slip: linked in netdev %s for active use\n",
- slp->if_name); */
+static int
+sl_alloc_bufs(struct slip *sl, int mtu)
+{
+ int err = -ENOBUFS;
+ unsigned long len;
+ char * rbuff = NULL;
+ char * xbuff = NULL;
+#ifdef SL_INCLUDE_CSLIP
+ char * cbuff = NULL;
+ struct slcompress *slcomp = NULL;
+#endif
- return (&(slp->ctrl));
+ /*
+ * Allocate the SLIP frame buffers:
+ *
+ * rbuff Receive buffer.
+ * xbuff Transmit buffer.
+ * cbuff Temporary compression buffer.
+ */
+ len = mtu * 2;
- } else {
- clear_bit(SLF_INUSE,&(slp->ctrl.flags));
- printk("sl_alloc() - register_netdev() failure.\n");
- }
+ /*
+ * allow for arrival of larger UDP packets, even if we say not to
+ * also fixes a bug in which SunOS sends 512-byte packets even with
+ * an MSS of 128
+ */
+ if (len < 576 * 2)
+ len = 576 * 2;
+ rbuff = kmalloc(len + 4, GFP_KERNEL);
+ if (rbuff == NULL)
+ goto err_exit;
+ xbuff = kmalloc(len + 4, GFP_KERNEL);
+ if (xbuff == NULL)
+ goto err_exit;
+#ifdef SL_INCLUDE_CSLIP
+ cbuff = kmalloc(len + 4, GFP_KERNEL);
+ if (cbuff == NULL)
+ goto err_exit;
+ slcomp = slhc_init(16, 16);
+ if (slcomp == NULL)
+ goto err_exit;
+#endif
+ start_bh_atomic();
+ if (sl->tty == NULL) {
+ end_bh_atomic();
+ err = -ENODEV;
+ goto err_exit;
}
+ sl->mtu = mtu;
+ sl->buffsize = len;
+ sl->rcount = 0;
+ sl->xleft = 0;
+ rbuff = xchg(&sl->rbuff, rbuff);
+ xbuff = xchg(&sl->xbuff, xbuff);
+#ifdef CONFIG_SLIP_MODE_SLIP6
+ cbuff = xchg(&sl->cbuff, cbuff);
+ slcomp = xchg(&sl->slcomp, slcomp);
+ sl->xdata = 0;
+ sl->xbits = 0;
+#endif
+ end_bh_atomic();
+ err = 0;
- return NULL;
+ /* Cleanup */
+err_exit:
+#ifdef SL_INCLUDE_CSLIP
+ if (cbuff)
+ kfree(cbuff);
+ if (slcomp)
+ slhc_free(slcomp);
+#endif
+ if (xbuff)
+ kfree(xbuff);
+ if (rbuff)
+ kfree(rbuff);
+ return err;
}
-
-/* Free a SLIP channel. */
-static inline void
-sl_free(struct slip *sl)
+/* Free a SLIP channel buffers. */
+static void
+sl_free_bufs(struct slip *sl)
{
+ void * tmp;
+
/* Free all SLIP frame buffers. */
- if (sl->rbuff) {
- kfree(sl->rbuff);
- }
- sl->rbuff = NULL;
- if (sl->xbuff) {
- kfree(sl->xbuff);
- }
- sl->xbuff = NULL;
+ if ((tmp = xchg(&sl->rbuff, NULL)) != NULL)
+ kfree(tmp);
+ if ((tmp = xchg(&sl->xbuff, NULL)) != NULL)
+ kfree(tmp);
#ifdef SL_INCLUDE_CSLIP
- /* Save CSLIP statistics */
- if (sl->slcomp) {
- sl->rx_compressed += sl->slcomp->sls_i_compressed;
- sl->rx_dropped += sl->slcomp->sls_i_tossed;
- sl->tx_compressed += sl->slcomp->sls_o_compressed;
- sl->tx_misses += sl->slcomp->sls_o_misses;
- }
- if (sl->cbuff) {
- kfree(sl->cbuff);
- }
- sl->cbuff = NULL;
- if(sl->slcomp)
- slhc_free(sl->slcomp);
- sl->slcomp = NULL;
+ if ((tmp = xchg(&sl->cbuff, NULL)) != NULL)
+ kfree(tmp);
+ if ((tmp = xchg(&sl->slcomp, NULL)) != NULL)
+ slhc_free(tmp);
#endif
-
- if (!test_and_clear_bit(SLF_INUSE, &sl->flags)) {
- printk("%s: sl_free for already free unit.\n", sl->dev->name);
- }
}
-/* MTU has been changed by the IP layer. Unfortunately we are not told
- about this, but we spot it ourselves and fix things up. We could be
- in an upcall from the tty driver, or in an ip packet queue. */
+/*
+ Reallocate slip channel buffers.
+ */
-static void sl_changedmtu(struct slip *sl)
+static int sl_realloc_bufs(struct slip *sl, int mtu)
{
+ int err = 0;
struct device *dev = sl->dev;
- unsigned char *xbuff, *rbuff, *oxbuff, *orbuff;
+ unsigned char *xbuff, *rbuff;
#ifdef SL_INCLUDE_CSLIP
- unsigned char *cbuff, *ocbuff;
+ unsigned char *cbuff;
#endif
- int len;
- unsigned long flags;
+ int len = mtu * 2;
- len = dev->mtu * 2;
/*
* allow for arrival of larger UDP packets, even if we say not to
* also fixes a bug in which SunOS sends 512-byte packets even with
* an MSS of 128
*/
- if (len < 576 * 2) {
+ if (len < 576 * 2)
len = 576 * 2;
- }
xbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
rbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
@@ -244,37 +252,30 @@
cbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
#endif
+
#ifdef SL_INCLUDE_CSLIP
if (xbuff == NULL || rbuff == NULL || cbuff == NULL) {
#else
if (xbuff == NULL || rbuff == NULL) {
#endif
- printk("%s: unable to grow slip buffers, MTU change cancelled.\n",
- sl->dev->name);
- dev->mtu = sl->mtu;
- if (xbuff != NULL) {
- kfree(xbuff);
- }
- if (rbuff != NULL) {
- kfree(rbuff);
+ if (mtu >= sl->mtu) {
+ printk("%s: unable to grow slip buffers, MTU change cancelled.\n",
+ dev->name);
+ err = -ENOBUFS;
}
-#ifdef SL_INCLUDE_CSLIP
- if (cbuff != NULL) {
- kfree(cbuff);
- }
-#endif
- return;
+ goto done;
}
- save_flags(flags); cli();
+ start_bh_atomic();
+
+ err = -ENODEV;
+ if (sl->tty == NULL)
+ goto done_on_bh;
- oxbuff = sl->xbuff;
- sl->xbuff = xbuff;
- orbuff = sl->rbuff;
- sl->rbuff = rbuff;
+ xbuff = xchg(&sl->xbuff, xbuff);
+ rbuff = xchg(&sl->rbuff, rbuff);
#ifdef SL_INCLUDE_CSLIP
- ocbuff = sl->cbuff;
- sl->cbuff = cbuff;
+ cbuff = xchg(&sl->cbuff, cbuff);
#endif
if (sl->xleft) {
if (sl->xleft <= len) {
@@ -288,30 +289,31 @@
if (sl->rcount) {
if (sl->rcount <= len) {
- memcpy(sl->rbuff, orbuff, sl->rcount);
+ memcpy(sl->rbuff, rbuff, sl->rcount);
} else {
sl->rcount = 0;
sl->rx_over_errors++;
set_bit(SLF_ERROR, &sl->flags);
}
}
- sl->mtu = dev->mtu;
-
+ sl->mtu = mtu;
+ dev->mtu = mtu;
sl->buffsize = len;
+ err = 0;
- restore_flags(flags);
+done_on_bh:
+ end_bh_atomic();
- if (oxbuff != NULL) {
- kfree(oxbuff);
- }
- if (orbuff != NULL) {
- kfree(orbuff);
- }
+done:
+ if (xbuff)
+ kfree(xbuff);
+ if (rbuff)
+ kfree(rbuff);
#ifdef SL_INCLUDE_CSLIP
- if (ocbuff != NULL) {
- kfree(ocbuff);
- }
+ if (cbuff)
+ kfree(cbuff);
#endif
+ return err;
}
@@ -398,14 +400,7 @@
unsigned char *p;
int actual, count;
-
- if (sl->mtu != sl->dev->mtu) { /* Someone has been ifconfigging */
-
- sl_changedmtu(sl);
- }
-
if (len > sl->mtu) { /* Sigh, shouldn't occur BUT ... */
- len = sl->mtu;
printk ("%s: truncating oversized transmit packet!\n", sl->dev->name);
sl->tx_dropped++;
sl_unlock(sl);
@@ -440,8 +435,10 @@
#endif
sl->xleft = count - actual;
sl->xhead = sl->xbuff + actual;
+#ifdef CONFIG_SLIP_SMART
/* VSV */
clear_bit(SLF_OUTWAIT, &sl->flags); /* reset outfill flag */
+#endif
}
/*
@@ -480,8 +477,14 @@
if (!dev->start) {
printk("%s: xmit call when iface is down\n", dev->name);
- return 1;
+ dev_kfree_skb(skb);
+ return 0;
}
+ if (sl->tty == NULL) {
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
/*
* If we are busy already- too bad. We ought to be able
* to queue things at this point, to allow for a little
@@ -524,107 +527,137 @@
}
-/* Return the frame type ID. This is normally IP but maybe be AX.25. */
+/******************************************
+ * Routines looking at netdevice side.
+ ******************************************/
+
+/* Netdevice UP -> DOWN routine */
-/* Open the low-level part of the SLIP channel. Easy! */
static int
-sl_open(struct device *dev)
+sl_close(struct device *dev)
{
struct slip *sl = (struct slip*)(dev->priv);
- unsigned long len;
- if (sl->tty == NULL) {
- return -ENODEV;
- }
-
- /*
- * Allocate the SLIP frame buffers:
- *
- * rbuff Receive buffer.
- * xbuff Transmit buffer.
- * cbuff Temporary compression buffer.
- */
- len = dev->mtu * 2;
- /*
- * allow for arrival of larger UDP packets, even if we say not to
- * also fixes a bug in which SunOS sends 512-byte packets even with
- * an MSS of 128
- */
- if (len < 576 * 2) {
- len = 576 * 2;
- }
- sl->rbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
- if (sl->rbuff == NULL) {
- goto norbuff;
- }
- sl->xbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
- if (sl->xbuff == NULL) {
- goto noxbuff;
- }
-#ifdef SL_INCLUDE_CSLIP
- sl->cbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
- if (sl->cbuff == NULL) {
- goto nocbuff;
- }
- sl->slcomp = slhc_init(16, 16);
- if (sl->slcomp == NULL) {
- goto noslcomp;
+ start_bh_atomic();
+ if (sl->tty) {
+ /* TTY discipline is running. */
+ sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
}
-#endif
- sl->mtu = dev->mtu;
- sl->buffsize = len;
+ dev->tbusy = 1;
+ dev->start = 0;
sl->rcount = 0;
sl->xleft = 0;
-#ifdef CONFIG_SLIP_MODE_SLIP6
- sl->xdata = 0;
- sl->xbits = 0;
-#endif
- sl->flags &= (1 << SLF_INUSE); /* Clear ESCAPE & ERROR flags */
-#ifdef CONFIG_SLIP_SMART
- sl->keepalive=0; /* no keepalive by default = VSV */
- init_timer(&sl->keepalive_timer); /* initialize timer_list struct */
- sl->keepalive_timer.data=(unsigned long)sl;
- sl->keepalive_timer.function=sl_keepalive;
- sl->outfill=0; /* & outfill too */
- init_timer(&sl->outfill_timer);
- sl->outfill_timer.data=(unsigned long)sl;
- sl->outfill_timer.function=sl_outfill;
-#endif
- dev->tbusy = 0;
- dev->start = 1;
+ end_bh_atomic();
+ MOD_DEC_USE_COUNT;
return 0;
+}
- /* Cleanup */
+/* Netdevice DOWN -> UP routine */
+
+static int sl_open(struct device *dev)
+{
+ struct slip *sl = (struct slip*)(dev->priv);
+
+ if (sl->tty==NULL)
+ return -ENODEV;
+
+ sl->flags &= (1 << SLF_INUSE);
+ dev->start = 1;
+ dev->tbusy = 0;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* Netdevice change MTU request */
+
+static int sl_change_mtu(struct device *dev, int new_mtu)
+{
+ struct slip *sl = (struct slip*)(dev->priv);
+
+ if (new_mtu < 68 || new_mtu > 65534)
+ return -EINVAL;
+
+ if (new_mtu != dev->mtu)
+ return sl_realloc_bufs(sl, new_mtu);
+ return 0;
+}
+
+/* Netdevice get statistics request */
+
+static struct net_device_stats *
+sl_get_stats(struct device *dev)
+{
+ static struct net_device_stats stats;
+ struct slip *sl = (struct slip*)(dev->priv);
#ifdef SL_INCLUDE_CSLIP
-noslcomp:
- kfree(sl->cbuff);
-nocbuff:
+ struct slcompress *comp;
#endif
- kfree(sl->xbuff);
-noxbuff:
- kfree(sl->rbuff);
-norbuff:
- return -ENOMEM;
+
+ memset(&stats, 0, sizeof(struct net_device_stats));
+
+ stats.rx_packets = sl->rx_packets;
+ stats.tx_packets = sl->tx_packets;
+ stats.rx_bytes = sl->rx_bytes;
+ stats.tx_bytes = sl->tx_bytes;
+ stats.rx_dropped = sl->rx_dropped;
+ stats.tx_dropped = sl->tx_dropped;
+ stats.tx_errors = sl->tx_errors;
+ stats.rx_errors = sl->rx_errors;
+ stats.rx_over_errors = sl->rx_over_errors;
+#ifdef SL_INCLUDE_CSLIP
+ stats.rx_fifo_errors = sl->rx_compressed;
+ stats.tx_fifo_errors = sl->tx_compressed;
+ stats.collisions = sl->tx_misses;
+ comp = sl->slcomp;
+ if (comp) {
+ stats.rx_fifo_errors += comp->sls_i_compressed;
+ stats.rx_dropped += comp->sls_i_tossed;
+ stats.tx_fifo_errors += comp->sls_o_compressed;
+ stats.collisions += comp->sls_o_misses;
+ }
+#endif /* CONFIG_INET */
+ return (&stats);
}
+/* Netdevice register callback */
-/* Close the low-level part of the SLIP channel. Easy! */
-static int
-sl_close(struct device *dev)
+static int sl_init(struct device *dev)
{
struct slip *sl = (struct slip*)(dev->priv);
- if (sl->tty == NULL) {
- return -EBUSY;
- }
- sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
- dev->tbusy = 1;
- dev->start = 0;
+ /*
+ * Finish setting up the DEVICE info.
+ */
+
+ dev->mtu = sl->mtu;
+ dev->hard_start_xmit = sl_xmit;
+ dev->open = sl_open;
+ dev->stop = sl_close;
+ dev->get_stats = sl_get_stats;
+ dev->change_mtu = sl_change_mtu;
+#ifdef CONFIG_SLIP_SMART
+ dev->do_ioctl = sl_ioctl;
+#endif
+ dev->hard_header_len = 0;
+ dev->addr_len = 0;
+ dev->type = ARPHRD_SLIP + sl->mode;
+ dev->tx_queue_len = 10;
+
+ dev_init_buffers(dev);
+
+ /* New-style flags. */
+ dev->flags = IFF_NOARP|IFF_POINTOPOINT|IFF_MULTICAST;
return 0;
}
+
+/******************************************
+ Routines looking at TTY side.
+ ******************************************/
+
+
static int slip_receive_room(struct tty_struct *tty)
{
return 65536; /* We can handle an infinite amount of data. :-) */
@@ -644,15 +677,6 @@
if (!sl || sl->magic != SLIP_MAGIC || !sl->dev->start)
return;
- /*
- * Argh! mtu change time! - costs us the packet part received
- * at the change
- */
- if (sl->mtu != sl->dev->mtu) {
-
- sl_changedmtu(sl);
- }
-
/* Read the characters out of the buffer */
while (count--) {
if (fp && *fp++) {
@@ -671,6 +695,127 @@
}
}
+/************************************
+ * slip_open helper routines.
+ ************************************/
+
+/* Collect hanged up channels */
+
+static void sl_sync(void)
+{
+ int i;
+
+ for (i = 0; i < slip_maxdev; i++) {
+ slip_ctrl_t *slp = slip_ctrls[i];
+ if (slp == NULL)
+ break;
+ if (slp->ctrl.tty || slp->ctrl.leased)
+ continue;
+ if (slp->dev.flags&IFF_UP)
+ dev_close(&slp->dev);
+ }
+}
+
+/* Find a free SLIP channel, and link in this `tty' line. */
+static struct slip *
+sl_alloc(kdev_t line)
+{
+ struct slip *sl;
+ slip_ctrl_t *slp = NULL;
+ int i;
+ int sel = -1;
+ int score = -1;
+
+ if (slip_ctrls == NULL)
+ return NULL; /* Master array missing ! */
+
+ for (i = 0; i < slip_maxdev; i++) {
+ slp = slip_ctrls[i];
+ if (slp == NULL)
+ break;
+
+ if (slp->ctrl.leased) {
+ if (slp->ctrl.line != line)
+ continue;
+ if (slp->ctrl.tty)
+ return NULL;
+
+ /* Clear ESCAPE & ERROR flags */
+ slp->ctrl.flags &= (1 << SLF_INUSE);
+ return &slp->ctrl;
+ }
+
+ if (slp->ctrl.tty)
+ continue;
+
+ if (current->pid == slp->ctrl.pid) {
+ if (slp->ctrl.line == line && score < 3) {
+ sel = i;
+ score = 3;
+ continue;
+ }
+ if (score < 2) {
+ sel = i;
+ score = 2;
+ }
+ continue;
+ }
+ if (slp->ctrl.line == line && score < 1) {
+ sel = i;
+ score = 1;
+ continue;
+ }
+ if (score < 0) {
+ sel = i;
+ score = 0;
+ }
+ }
+
+ if (sel >= 0) {
+ i = sel;
+ slp = slip_ctrls[i];
+ if (score > 1) {
+ slp->ctrl.flags &= (1 << SLF_INUSE);
+ return &slp->ctrl;
+ }
+ }
+
+ /* Sorry, too many, all slots in use */
+ if (i >= slip_maxdev)
+ return NULL;
+
+ if (slp) {
+ if (test_bit(SLF_INUSE, &slp->ctrl.flags)) {
+ unregister_netdevice(&slp->dev);
+ sl_free_bufs(&slp->ctrl);
+ }
+ } else if ((slp = (slip_ctrl_t *)kmalloc(sizeof(slip_ctrl_t),GFP_KERNEL)) == NULL)
+ return NULL;
+
+ memset(slp, 0, sizeof(slip_ctrl_t));
+
+ sl = &slp->ctrl;
+ /* Initialize channel control data */
+ sl->magic = SLIP_MAGIC;
+ sl->dev = &slp->dev;
+ sl->mode = SL_MODE_DEFAULT;
+ sprintf(slp->if_name, "sl%d", i);
+ slp->dev.name = slp->if_name;
+ slp->dev.base_addr = i;
+ slp->dev.priv = (void*)sl;
+ slp->dev.init = sl_init;
+#ifdef CONFIG_SLIP_SMART
+ init_timer(&sl->keepalive_timer); /* initialize timer_list struct */
+ sl->keepalive_timer.data=(unsigned long)sl;
+ sl->keepalive_timer.function=sl_keepalive;
+ init_timer(&sl->outfill_timer);
+ sl->outfill_timer.data=(unsigned long)sl;
+ sl->outfill_timer.function=sl_outfill;
+#endif
+ slip_ctrls[i] = slp;
+ return &slp->ctrl;
+}
+
/*
* Open the high-level part of the SLIP channel.
* This function is called by the TTY module when the
@@ -681,42 +826,98 @@
static int
slip_open(struct tty_struct *tty)
{
- struct slip *sl = (struct slip *) tty->disc_data;
+ struct slip *sl;
int err;
+ MOD_INC_USE_COUNT;
+
+ /* RTnetlink lock is misused here to serialize concurrent
+ opens of slip channels. There are better ways, but it is
+ the simplest one.
+ */
+ rtnl_lock();
+
+ /* Collect hanged up channels. */
+ sl_sync();
+
+ sl = (struct slip *) tty->disc_data;
+
+ err = -EEXIST;
/* First make sure we're not already connected. */
- if (sl && sl->magic == SLIP_MAGIC) {
- return -EEXIST;
- }
+ if (sl && sl->magic == SLIP_MAGIC)
+ goto err_exit;
/* OK. Find a free SLIP channel to use. */
- if ((sl = sl_alloc()) == NULL) {
- return -ENFILE;
- }
+ err = -ENFILE;
+ if ((sl = sl_alloc(tty->device)) == NULL)
+ goto err_exit;
sl->tty = tty;
tty->disc_data = sl;
- if (tty->driver.flush_buffer) {
+ sl->line = tty->device;
+ sl->pid = current->pid;
+ if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
- }
- if (tty->ldisc.flush_buffer) {
+ if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer(tty);
- }
- /* Restore default settings */
- sl->mode = SL_MODE_DEFAULT;
- sl->dev->type = ARPHRD_SLIP + sl->mode;
- /* Perform the low-level SLIP initialization. */
- if ((err = sl_open(sl->dev))) {
- return err;
+ if (!test_bit(SLF_INUSE, &sl->flags)) {
+ /* Perform the low-level SLIP initialization. */
+ if ((err = sl_alloc_bufs(sl, SL_MTU)) != 0)
+ goto err_free_chan;
+
+ if (register_netdevice(sl->dev)) {
+ sl_free_bufs(sl);
+ goto err_free_chan;
+ }
+
+ set_bit(SLF_INUSE, &sl->flags);
}
- MOD_INC_USE_COUNT;
+#ifdef CONFIG_SLIP_SMART
+ if (sl->keepalive) {
+ sl->keepalive_timer.expires=jiffies+sl->keepalive*HZ;
+ add_timer (&sl->keepalive_timer);
+ }
+ if (sl->outfill) {
+ sl->outfill_timer.expires=jiffies+sl->outfill*HZ;
+ add_timer (&sl->outfill_timer);
+ }
+#endif
/* Done. We have linked the TTY line to a channel. */
+ rtnl_unlock();
return sl->dev->base_addr;
+
+err_free_chan:
+ sl->tty = NULL;
+ tty->disc_data = NULL;
+ clear_bit(SLF_INUSE, &sl->flags);
+
+err_exit:
+ rtnl_unlock();
+
+ /* Count references from TTY module */
+ MOD_DEC_USE_COUNT;
+ return err;
}
+/*
+ Let me to blame a bit.
+ 1. TTY module calls this funstion on soft interrupt.
+ 2. TTY module calls this function WITH MASKED INTERRUPTS!
+ 3. TTY module does not notify us about line discipline
+ shutdown,
+
+ Seems, now it is clean. The solution is to consider netdevice and
+ line discipline sides as two independant threads.
+
+ By-product (not desired): sl? does not feel hangups and remains open.
+ It is supposed, that user level program (dip, diald, slattach...)
+ will catch SIGHUP and make the rest of work.
+
+ I see no way to make more with current tty code. --ANK
+ */
/*
* Close down a SLIP channel.
@@ -730,69 +931,26 @@
struct slip *sl = (struct slip *) tty->disc_data;
/* First make sure we're connected. */
- if (!sl || sl->magic != SLIP_MAGIC) {
+ if (!sl || sl->magic != SLIP_MAGIC || sl->tty != tty)
return;
- }
-
- rtnl_lock();
- if (sl->dev->flags & IFF_UP)
- {
- /* STRONG layering violation! --ANK */
- (void) dev_close(sl->dev);
- }
tty->disc_data = 0;
sl->tty = NULL;
+ if (!sl->leased)
+ sl->line = 0;
+
/* VSV = very important to remove timers */
#ifdef CONFIG_SLIP_SMART
if (sl->keepalive)
- (void)del_timer (&sl->keepalive_timer);
+ del_timer (&sl->keepalive_timer);
if (sl->outfill)
- (void)del_timer (&sl->outfill_timer);
-#endif
- sl_free(sl);
- unregister_netdevice(sl->dev);
- rtnl_unlock();
- MOD_DEC_USE_COUNT;
-}
-
-
-static struct net_device_stats *
-sl_get_stats(struct device *dev)
-{
- static struct net_device_stats stats;
- struct slip *sl = (struct slip*)(dev->priv);
-#ifdef SL_INCLUDE_CSLIP
- struct slcompress *comp;
+ del_timer (&sl->outfill_timer);
#endif
- memset(&stats, 0, sizeof(struct net_device_stats));
-
- stats.rx_packets = sl->rx_packets;
- stats.tx_packets = sl->tx_packets;
- stats.rx_bytes = sl->rx_bytes;
- stats.tx_bytes = sl->tx_bytes;
- stats.rx_dropped = sl->rx_dropped;
- stats.tx_dropped = sl->tx_dropped;
- stats.tx_errors = sl->tx_errors;
- stats.rx_errors = sl->rx_errors;
- stats.rx_over_errors = sl->rx_over_errors;
-#ifdef SL_INCLUDE_CSLIP
- stats.rx_fifo_errors = sl->rx_compressed;
- stats.tx_fifo_errors = sl->tx_compressed;
- stats.collisions = sl->tx_misses;
- comp = sl->slcomp;
- if (comp) {
- stats.rx_fifo_errors += comp->sls_i_compressed;
- stats.rx_dropped += comp->sls_i_tossed;
- stats.tx_fifo_errors += comp->sls_o_compressed;
- stats.collisions += comp->sls_o_misses;
- }
-#endif /* CONFIG_INET */
- return (&stats);
+ /* Count references from TTY module */
+ MOD_DEC_USE_COUNT;
}
-
/************************************************************************
* STANDARD SLIP ENCAPSULATION *
************************************************************************/
@@ -840,9 +998,11 @@
switch(s) {
case END:
+#ifdef CONFIG_SLIP_SMART
/* drop keeptest bit = VSV */
if (test_bit(SLF_KEEPTEST, &sl->flags))
clear_bit(SLF_KEEPTEST, &sl->flags);
+#endif
if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && (sl->rcount > 2)) {
sl_bump(sl);
@@ -925,9 +1085,11 @@
unsigned char c;
if (s == 0x70) {
+#ifdef CONFIG_SLIP_SMART
/* drop keeptest bit = VSV */
if (test_bit(SLF_KEEPTEST, &sl->flags))
clear_bit(SLF_KEEPTEST, &sl->flags);
+#endif
if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && (sl->rcount > 2)) {
sl_bump(sl);
@@ -959,7 +1121,6 @@
slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
{
struct slip *sl = (struct slip *) tty->disc_data;
- int err;
unsigned int tmp;
/* First make sure we're connected. */
@@ -978,19 +1139,13 @@
return 0;
case SIOCGIFENCAP:
- err = verify_area(VERIFY_WRITE, arg, sizeof(int));
- if (err) {
- return err;
- }
- put_user(sl->mode, (int *)arg);
+ if (put_user(sl->mode, (int *)arg))
+ return -EFAULT;
return 0;
case SIOCSIFENCAP:
- err = verify_area(VERIFY_READ, arg, sizeof(int));
- if (err) {
- return err;
- }
- get_user(tmp,(int *)arg);
+ if (get_user(tmp,(int *)arg))
+ return -EFAULT;
#ifndef SL_INCLUDE_CSLIP
if (tmp & (SL_MODE_CSLIP|SL_MODE_ADAPTIVE)) {
return -EINVAL;
@@ -1017,13 +1172,16 @@
#ifdef CONFIG_SLIP_SMART
/* VSV changes start here */
case SIOCSKEEPALIVE:
- err = verify_area(VERIFY_READ, arg, sizeof(int));
- if (err) {
- return -err;
- }
- get_user(tmp,(int *)arg);
+ if (get_user(tmp,(int *)arg))
+ return -EFAULT;
if (tmp > 255) /* max for unchar */
return -EINVAL;
+
+ start_bh_atomic();
+ if (!sl->tty) {
+ end_bh_atomic();
+ return -ENODEV;
+ }
if (sl->keepalive)
(void)del_timer (&sl->keepalive_timer);
if ((sl->keepalive = (unchar) tmp) != 0) {
@@ -1031,24 +1189,25 @@
add_timer(&sl->keepalive_timer);
set_bit(SLF_KEEPTEST, &sl->flags);
}
+ end_bh_atomic();
+
return 0;
case SIOCGKEEPALIVE:
- err = verify_area(VERIFY_WRITE, arg, sizeof(int));
- if (err) {
- return -err;
- }
- put_user(sl->keepalive, (int *)arg);
+ if (put_user(sl->keepalive, (int *)arg))
+ return -EFAULT;
return 0;
case SIOCSOUTFILL:
- err = verify_area(VERIFY_READ, arg, sizeof(int));
- if (err) {
- return -err;
- }
- get_user(tmp,(int *)arg);
+ if (get_user(tmp,(int *)arg))
+ return -EFAULT;
if (tmp > 255) /* max for unchar */
return -EINVAL;
+ start_bh_atomic();
+ if (!sl->tty) {
+ end_bh_atomic();
+ return -ENODEV;
+ }
if (sl->outfill)
(void)del_timer (&sl->outfill_timer);
if ((sl->outfill = (unchar) tmp) != 0){
@@ -1056,14 +1215,12 @@
add_timer(&sl->outfill_timer);
set_bit(SLF_OUTWAIT, &sl->flags);
}
+ end_bh_atomic();
return 0;
case SIOCGOUTFILL:
- err = verify_area(VERIFY_WRITE, arg, sizeof(int));
- if (err) {
- return -err;
- }
- put_user(sl->outfill, (int *)arg);
+ if (put_user(sl->outfill, (int *)arg))
+ return -EFAULT;
return 0;
/* VSV changes end */
#endif
@@ -1091,6 +1248,13 @@
if (sl == NULL) /* Allocation failed ?? */
return -ENODEV;
+ start_bh_atomic(); /* Hangup would kill us */
+
+ if (!sl->tty) {
+ end_bh_atomic();
+ return -ENODEV;
+ }
+
switch(cmd){
case SIOCSKEEPALIVE:
/* max for unchar */
@@ -1124,20 +1288,30 @@
case SIOCGOUTFILL:
rq->ifr_data=(caddr_t)((unsigned long)sl->outfill);
+ break;
+
+ case SIOCSLEASE:
+ /* Resolve race condition, when ioctl'ing hanged up
+ and opened by another process device.
+ */
+ if (sl->tty != current->tty && sl->pid != current->pid) {
+ end_bh_atomic();
+ return -EPERM;
+ }
+ sl->leased = 0;
+ if ((unsigned long)rq->ifr_data)
+ sl->leased = 1;
+ break;
+
+ case SIOCGLEASE:
+ rq->ifr_data=(caddr_t)((unsigned long)sl->leased);
};
+ end_bh_atomic();
return 0;
}
#endif
/* VSV changes end */
-static int sl_open_dev(struct device *dev)
-{
- struct slip *sl = (struct slip*)(dev->priv);
- if(sl->tty==NULL)
- return -ENODEV;
- return 0;
-}
-
/* Initialize SLIP control device -- register SLIP line discipline */
#ifdef MODULE
static int slip_init_ctrl_dev(void)
@@ -1203,45 +1377,7 @@
}
-/* Initialise the SLIP driver. Called by the device init code */
-
-int slip_init(struct device *dev)
-{
- struct slip *sl = (struct slip*)(dev->priv);
-
- if (sl == NULL) /* Allocation failed ?? */
- return -ENODEV;
-
- /* Set up the "SLIP Control Block". (And clear statistics) */
- memset(sl, 0, sizeof (struct slip));
- sl->magic = SLIP_MAGIC;
- sl->dev = dev;
-
- /*
- * Finish setting up the DEVICE info.
- */
-
- dev->mtu = SL_MTU;
- dev->hard_start_xmit = sl_xmit;
- dev->open = sl_open_dev;
- dev->stop = sl_close;
- dev->get_stats = sl_get_stats;
-#ifdef CONFIG_SLIP_SMART
- dev->do_ioctl = sl_ioctl;
-#endif
- dev->hard_header_len = 0;
- dev->addr_len = 0;
- dev->type = ARPHRD_SLIP + SL_MODE_DEFAULT;
- dev->tx_queue_len = 10;
-
- dev_init_buffers(dev);
-
- /* New-style flags. */
- dev->flags = IFF_NOARP|IFF_POINTOPOINT|IFF_MULTICAST;
-
- return 0;
-}
#ifdef MODULE
int
@@ -1255,25 +1391,51 @@
{
int i;
- if (slip_ctrls != NULL)
- {
- for (i = 0; i < slip_maxdev; i++)
- {
- if (slip_ctrls[i])
- {
- /*
- * VSV = if dev->start==0, then device
- * unregistered while close proc.
- */
- if (slip_ctrls[i]->dev.start)
- unregister_netdev(&(slip_ctrls[i]->dev));
+ if (slip_ctrls != NULL) {
+ unsigned long start = jiffies;
+ int busy = 0;
+
+ /* First of all: check for active disciplines and hangup them.
+ */
+ do {
+ if (busy) {
+ current->counter = 0;
+ schedule();
+ }
+
+ busy = 0;
+ start_bh_atomic();
+ for (i = 0; i < slip_maxdev; i++) {
+ struct slip_ctrl *slc = slip_ctrls[i];
+ if (slc && slc->ctrl.tty) {
+ busy++;
+ tty_hangup(slc->ctrl.tty);
+ }
+ }
+ end_bh_atomic();
+ } while (busy && jiffies - start < 1*HZ);
- kfree(slip_ctrls[i]);
+ busy = 0;
+ for (i = 0; i < slip_maxdev; i++) {
+ struct slip_ctrl *slc = slip_ctrls[i];
+ if (slc) {
+ unregister_netdev(&slc->dev);
+ if (slc->ctrl.tty) {
+ printk("%s: tty discipline is still running\n", slc->dev.name);
+ /* Pin module forever */
+ MOD_INC_USE_COUNT;
+ busy++;
+ continue;
+ }
+ sl_free_bufs(&slc->ctrl);
+ kfree(slc);
slip_ctrls[i] = NULL;
}
}
- kfree(slip_ctrls);
- slip_ctrls = NULL;
+ if (!busy) {
+ kfree(slip_ctrls);
+ slip_ctrls = NULL;
+ }
}
if ((i = tty_register_ldisc(N_SLIP, NULL)))
{
@@ -1292,7 +1454,7 @@
{
struct slip *sl=(struct slip *)sls;
- if(sls==0L)
+ if (sl==NULL || sl->tty == NULL)
return;
if(sl->outfill)
@@ -1318,15 +1480,13 @@
sl->outfill_timer.expires=jiffies+sl->outfill*HZ;
add_timer(&sl->outfill_timer);
}
- else
- del_timer(&sl->outfill_timer);
}
static void sl_keepalive(unsigned long sls)
{
struct slip *sl=(struct slip *)sls;
- if(sl == NULL)
+ if (sl == NULL || sl->tty == NULL)
return;
if( sl->keepalive)
@@ -1334,7 +1494,6 @@
if(test_bit(SLF_KEEPTEST, &sl->flags))
{
/* keepalive still high :(, we must hangup */
- (void)del_timer(&sl->keepalive_timer);
if( sl->outfill ) /* outfill timer must be deleted too */
(void)del_timer(&sl->outfill_timer);
printk("%s: no packets received during keepalive timeout, hangup.\n", sl->dev->name);
@@ -1344,12 +1503,9 @@
}
else
set_bit(SLF_KEEPTEST, &sl->flags);
- (void)del_timer(&sl->keepalive_timer);
- sl->keepalive_timer.expires=jiffies+sl->keepalive*HZ;
+ sl->keepalive_timer.expires=jiffies+sl->keepalive*HZ;
add_timer(&sl->keepalive_timer);
}
- else
- (void)del_timer(&sl->keepalive_timer);
}
#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov