patch-2.2.15 linux/drivers/net/ppp.c

Next file: linux/drivers/net/rcpci45.c
Previous file: linux/drivers/net/plip.c
Back to the patch index
Back to the overall index

diff -u --new-file --recursive --exclude-from ../../exclude v2.2.14/drivers/net/ppp.c linux/drivers/net/ppp.c
@@ -4,7 +4,7 @@
  *  Al Longyear <longyear@netcom.com>
  *  Extensively rewritten by Paul Mackerras <paulus@cs.anu.edu.au>
  *
- *  ==FILEVERSION 990510==
+ *  ==FILEVERSION 20000223==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the number above to the
@@ -175,6 +175,27 @@
 EXPORT_SYMBOL(ppp_register_compressor);
 EXPORT_SYMBOL(ppp_unregister_compressor);
 
+/* Bits in ppp->state */
+#define PUSHING		0	/* currently executing in ppp_tty_push */
+#define WAKEUP		1	/* someone else wants to also */
+#define XMITFULL	2	/* someone owns ppp->tpkt */
+#define FLUSHING	3	/* discard output */
+
+/* Non-blocking locking. */
+static inline int xmit_trylock(struct ppp *ppp)
+{
+	wmb();
+	if (test_and_set_bit(PUSHING, &ppp->state))
+		return 0;
+	return 1;
+}
+
+static inline void xmit_unlock(struct ppp *ppp)
+{
+	wmb();
+	clear_bit(PUSHING, &ppp->state);
+}
+
 /*************************************************************
  * LINE DISCIPLINE SUPPORT
  *    The following code implements the PPP line discipline
@@ -323,7 +344,7 @@
 {
 	ppp->escape = 0;
 	ppp->toss   = 0xE0;
-	ppp->tty_pushing = 0;
+	ppp->state = 0;
 
 	memset (ppp->xmit_async_map, 0, sizeof (ppp->xmit_async_map));
 	ppp->xmit_async_map[0] = 0xffffffff;
@@ -838,7 +859,7 @@
 	
 	CHECK_PPP(0);
 
-	if (ppp->tpkt != NULL)
+	if (test_and_set_bit(XMITFULL, &ppp->state))
 		return -1;
 	ppp->tpkt = skb;
 
@@ -886,67 +907,52 @@
 static int
 ppp_tty_sync_push(struct ppp *ppp)
 {
-	int sent;
-	struct tty_struct *tty = ppp2tty(ppp);
-	unsigned long flags;
+	int sent, done = 0;
+	struct tty_struct *tty;
 		
 	CHECK_PPP(0);
 
-	if (ppp->tpkt == NULL)
-		return 0;
-		
-	/* prevent reentrancy with tty_pushing flag */		
-	save_flags(flags);
-	cli();
-	if (ppp->tty_pushing) {
-		/* record wakeup attempt so we don't lose */
-		/* a wakeup call while doing push processing */
-		ppp->woke_up=1;
-		restore_flags(flags);
+	set_bit(WAKEUP, &ppp->state);
+	if (!xmit_trylock(ppp))
 		return 0;
-	}
-	ppp->tty_pushing = 1;
-	restore_flags(flags);
-	
-	if (tty == NULL || tty->disc_data != (void *) ppp)
-		goto flush;
-		
-	for(;;){
-		ppp->woke_up=0;
-		
+
+ again:
+	clear_bit(WAKEUP, &ppp->state);
+
+	if (ppp->tpkt != 0) {
 		/* Note: Sync driver accepts complete frame or nothing */
-		tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
-		sent = tty->driver.write(tty, 0, ppp->tpkt->data, ppp->tpkt->len);
-		if (sent < 0) {
+		tty = ppp2tty(ppp);
+		sent = -1;
+		if (test_bit(FLUSHING, &ppp->state))
+			sent = ppp->tpkt->len;
+		else if (tty != NULL && tty->disc_data == (void *) ppp) {
+			tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+			sent = tty->driver.write(tty, 0, ppp->tpkt->data, ppp->tpkt->len);
+		}
+		if (sent < 0)
 			/* write error (possible loss of CD) */
 			/* record error and discard current packet */
 			ppp->stats.ppp_oerrors++;
-			break;
-		}
-		ppp->stats.ppp_obytes += sent;
-		if (sent < ppp->tpkt->len) {
-			/* driver unable to accept frame just yet */
-			save_flags(flags);
-			cli();
-			if (ppp->woke_up) {
-				/* wake up called while processing */
-				/* try to send the frame again */
-				restore_flags(flags);
-				continue;
-			}
-			/* wait for wakeup callback to try send again */
-			ppp->tty_pushing = 0;
-			restore_flags(flags);
-			return 0;
-		}
-		break;
-	}
-flush:	
-	/* done with current packet (sent or discarded) */
-	kfree_skb(ppp->tpkt);
-	ppp->tpkt = 0;
-	ppp->tty_pushing = 0;
-	return 1;
+		else
+			ppp->stats.ppp_obytes += sent;
+		if (sent < 0 || sent >= ppp->tpkt->len) {
+			/* driver accepted the frame or we got an error */
+			kfree_skb(ppp->tpkt);
+			ppp->tpkt = 0;
+			wmb();
+			clear_bit(XMITFULL, &ppp->state);
+			done = 1;
+		}
+	}
+	if (ppp->tpkt == 0)
+		clear_bit(FLUSHING, &ppp->state);
+
+	xmit_unlock(ppp);
+	if (test_and_clear_bit(WAKEUP, &ppp->state))
+		if (xmit_trylock(ppp))
+			goto again;
+
+	return done;
 }
 
 /*
@@ -964,10 +970,11 @@
 
 	ppp_tty_push(ppp);
 
-	if (ppp->tpkt != NULL)
+	if (test_and_set_bit(XMITFULL, &ppp->state))
 		return -1;
-	ppp->tpkt = skb;
 	ppp->tpkt_pos = 0;
+	wmb();
+	ppp->tpkt = skb;
 
 	return ppp_tty_push(ppp);
 }
@@ -980,58 +987,62 @@
 ppp_tty_push(struct ppp *ppp)
 {
 	int avail, sent, done = 0;
-	struct tty_struct *tty = ppp2tty(ppp);
-	
+	struct tty_struct *tty;
+
 	if (ppp->flags & SC_SYNC) 
 		return ppp_tty_sync_push(ppp);
 
 	CHECK_PPP(0);
-	if (ppp->tty_pushing) {
-		ppp->woke_up = 1;
+
+	set_bit(WAKEUP, &ppp->state);
+	if (!xmit_trylock(ppp))
 		return 0;
-	}
-	if (tty == NULL || tty->disc_data != (void *) ppp)
-		goto flush;
-	while (ppp->optr < ppp->olim || ppp->tpkt != 0) {
-		ppp->tty_pushing = 1;
-		mb();
-		ppp->woke_up = 0;
-		avail = ppp->olim - ppp->optr;
-		if (avail > 0) {
+
+ again:
+	clear_bit(WAKEUP, &ppp->state);
+
+	avail = ppp->olim - ppp->optr;
+	if (avail > 0) {
+		tty = ppp2tty(ppp);
+		sent = -1;
+		if (test_bit(FLUSHING, &ppp->state)) {
+			sent = avail;
+		} else if (tty != NULL && tty->disc_data == (void *) ppp) {
 			tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
 			sent = tty->driver.write(tty, 0, ppp->optr, avail);
-			if (sent < 0)
-				goto flush;	/* error, e.g. loss of CD */
-			ppp->stats.ppp_obytes += sent;
-			ppp->optr += sent;
-			if (sent < avail) {
+		}
+		if (sent < 0) {
+			/* error, e.g. loss of CD */
+			ppp->stats.ppp_oerrors++;
+			ppp->optr = ppp->olim;
+			if (ppp->tpkt != 0) {
+				kfree_skb(ppp->tpkt);
+				ppp->tpkt = 0;
 				wmb();
-				ppp->tty_pushing = 0;
-				mb();
-				if (ppp->woke_up)
-					continue;
-				return done;
+				clear_bit(XMITFULL, &ppp->state);
+				done = 1;
 			}
+		} else {
+			ppp->stats.ppp_obytes += sent;
+			ppp->optr += sent;
 		}
-		if (ppp->tpkt != 0)
-			done = ppp_async_encode(ppp);
-		wmb();
-		ppp->tty_pushing = 0;
 	}
-	return done;
 
-flush:
-	ppp->tty_pushing = 1;
-	mb();
-	ppp->stats.ppp_oerrors++;
-	if (ppp->tpkt != 0) {
-		kfree_skb(ppp->tpkt);
-		ppp->tpkt = 0;
-		done = 1;
+	if (ppp->optr == ppp->olim) {
+		if (ppp->tpkt != 0) {
+			done |= ppp_async_encode(ppp);
+			goto again;
+		} else {
+			/* buffers are empty */
+			clear_bit(FLUSHING, &ppp->state);
+		}
 	}
-	ppp->optr = ppp->olim;
-	wmb();
-	ppp->tty_pushing = 0;
+
+	xmit_unlock(ppp);
+	if (test_and_clear_bit(WAKEUP, &ppp->state))
+		if (xmit_trylock(ppp))
+			goto again;
+
 	return done;
 }
 
@@ -1132,6 +1143,8 @@
 
 		kfree_skb(ppp->tpkt);
 		ppp->tpkt = 0;
+		wmb();
+		clear_bit(XMITFULL, &ppp->state);
 		return 1;
 	}
 
@@ -1152,22 +1165,10 @@
 ppp_tty_flush_output(struct ppp *ppp)
 {
 	struct sk_buff *skb;
-	int done = 0;
 
+	set_bit(FLUSHING, &ppp->state);
 	while ((skb = skb_dequeue(&ppp->xmt_q)) != NULL)
 		kfree_skb(skb);
-	ppp->tty_pushing = 1;
-	mb();
-	ppp->optr = ppp->olim;
-	if (ppp->tpkt != NULL) {
-		kfree_skb(ppp->tpkt);
-		ppp->tpkt = 0;
-		done = 1;
-	}
-	wmb();
-	ppp->tty_pushing = 0;
-	if (done)
-		ppp_output_wakeup(ppp);
 }
 
 /*
@@ -2711,7 +2712,7 @@
 	}
 
 	/*
-	 * The dev->tbusy field acts as a lock to allow only
+	 * The ppp->xmit_busy field acts as a lock to allow only
 	 * one packet to be processed at a time.  If we can't
 	 * get the lock, try again later.
 	 * We deliberately queue as little as possible inside

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)