From 4b99dcc37d1a622a42e560991e71ba0c59a6c01d Mon Sep 17 00:00:00 2001
From: Kai Germaschewski <kai@tp1.ruhr-uni-bochum.de>
Date: Tue, 1 Oct 2002 03:07:24 -0500
Subject: [PATCH] ISDN: Use list.h list for list of online channels

Cleaner and less error-prone than the open coded doubly linked
list.
---
 drivers/isdn/i4l/isdn_net.c | 39 +++++++++++++-------------
 drivers/isdn/i4l/isdn_net.h | 56 +++++++++++++------------------------
 drivers/isdn/i4l/isdn_ppp.c |  8 ++----
 include/linux/isdn.h        | 10 +++----
 4 files changed, 47 insertions(+), 66 deletions(-)

diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
index 7d3b3d8962b4..af241fadf8ed 100644
--- a/drivers/isdn/i4l/isdn_net.c
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -126,9 +126,9 @@ isdn_net_dev_stop_queue(isdn_net_dev *idev)
 static inline int
 isdn_net_device_busy(isdn_net_dev *idev)
 {
-	isdn_net_dev *ndev;
 	isdn_net_local *mlp;
 	unsigned long flags;
+	int retval = 1;
 
 	if (!isdn_net_dev_busy(idev))
 		return 0;
@@ -138,17 +138,15 @@ isdn_net_device_busy(isdn_net_dev *idev)
 	else
 		mlp = &idev->local;
 	
-	spin_lock_irqsave(&mlp->queue_lock, flags);
-	ndev = idev->next;
-	while (ndev != idev) {
-		if (!isdn_net_dev_busy(ndev)) {
-			spin_unlock_irqrestore(&mlp->queue_lock, flags);
-			return 0;
+	spin_lock_irqsave(&mlp->online_lock, flags);
+	list_for_each_entry(idev, &mlp->online, online) {
+		if (!isdn_net_dev_busy(idev)) {
+			retval = 0;
+			break;
 		}
-		ndev = ndev->next;
 	}
-	spin_unlock_irqrestore(&mlp->queue_lock, flags);
-	return 1;
+	spin_unlock_irqrestore(&mlp->online_lock, flags);
+	return retval;
 }
 
 static inline
@@ -394,10 +392,14 @@ static void isdn_net_connected(isdn_net_dev *idev)
 	add_timer(&idev->hup_timer);
 
 	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) {
-		if (idev->master) { /* is lp a slave? */
-			isdn_net_local *mlp = idev->master;
-			isdn_net_add_to_bundle(mlp, idev);
-		}
+		isdn_net_local *mlp;
+		
+		if (idev->master)
+			mlp = idev->master;
+		else
+			mlp = &idev->local;
+
+		isdn_net_add_to_bundle(mlp, idev);
 	}
 	printk(KERN_INFO "isdn_net: %s connected\n", idev->name);
 	/* If first Chargeinfo comes before B-Channel connect,
@@ -994,7 +996,8 @@ isdn_net_xmit(struct net_device *ndev, struct sk_buff *skb)
 			idev->sqfull = 0;
 		}
 		/* this is a hack to allow auto-hangup for slaves on moderate loads */
-		mlp->queue = mlp->netdev;
+		list_del(&mlp->online);
+		list_add_tail(&mlp->online, &lp->netdev->online);
 	}
 
 	return retv;
@@ -1581,11 +1584,9 @@ isdn_net_new(char *name, struct net_device *master)
 	}
 	netdev->local.magic = ISDN_NET_MAGIC;
 
-	netdev->local.queue = netdev;
-	spin_lock_init(&netdev->local.queue_lock);
+	INIT_LIST_HEAD(&netdev->local.online);
+	spin_lock_init(&netdev->local.online_lock);
 
-	netdev->last = netdev;
-	netdev->next = netdev;
 	netdev->local.netdev = netdev;
 
 	tasklet_init(&netdev->tlet, isdn_net_tasklet, (unsigned long) netdev);
diff --git a/drivers/isdn/i4l/isdn_net.h b/drivers/isdn/i4l/isdn_net.h
index f14ac3dc0cb0..0876c6570a1b 100644
--- a/drivers/isdn/i4l/isdn_net.h
+++ b/drivers/isdn/i4l/isdn_net.h
@@ -87,26 +87,23 @@ isdn_net_get_locked_dev(isdn_net_local *mlp)
 {
 	unsigned long flags;
 	isdn_net_dev *idev;
-	isdn_net_dev *head;
 
-	spin_lock_irqsave(&mlp->queue_lock, flags);
-	head = mlp->queue;
-	idev = head; 
-	spin_lock_bh(&idev->xmit_lock);
-	while (isdn_net_dev_busy(idev)) {
-		spin_unlock_bh(&idev->xmit_lock);
-		mlp->queue = mlp->queue->next;
-		idev = mlp->queue;
-		if (idev == head) { /* not found -- should never happen */
-			idev = NULL;
-			goto errout;
-		}
+	spin_lock_irqsave(&mlp->online_lock, flags);
+
+	list_for_each_entry(idev, &mlp->online, online) {
 		spin_lock_bh(&idev->xmit_lock);
+		if (!isdn_net_dev_busy(idev)) {
+			/* point the head to next online channel */
+			list_del(&mlp->online);
+			list_add(&mlp->online, &idev->online);
+			goto found;
+		}
+		spin_unlock_bh(&idev->xmit_lock);
 	}
-	idev = mlp->queue;
-	mlp->queue = mlp->queue->next;
-errout:
-	spin_unlock_irqrestore(&mlp->queue_lock, flags);
+	idev = NULL;
+
+ found:
+	spin_unlock_irqrestore(&mlp->online_lock, flags);
 	return idev;
 }
 
@@ -116,19 +113,11 @@ isdn_net_get_locked_dev(isdn_net_local *mlp)
 static inline void
 isdn_net_add_to_bundle(isdn_net_local *mlp, isdn_net_dev *idev)
 {
-	isdn_net_dev *qdev;
 	unsigned long flags;
 
-	spin_lock_irqsave(&mlp->queue_lock, flags);
-
-	qdev = mlp->queue;
-	idev->last = qdev->last;
-	qdev->last->next = idev;
-	qdev->last = idev;
-	idev->next = qdev;
-	mlp->queue = idev;
-
-	spin_unlock_irqrestore(&mlp->queue_lock, flags);
+	spin_lock_irqsave(&mlp->online_lock, flags);
+	list_add(&idev->online, &mlp->online);
+	spin_unlock_irqrestore(&mlp->online_lock, flags);
 }
 /*
  * remove a channel from the bundle it belongs to
@@ -144,14 +133,9 @@ isdn_net_rm_from_bundle(isdn_net_dev *idev)
 	else
 		mlp = &idev->local;
 
-	spin_lock_irqsave(&mlp->queue_lock, flags);
-	idev->last->next = idev->next;
-	idev->next->last = idev->last;
-	if (mlp->queue == idev) {
-		mlp->queue = idev->next;
-	}
-	idev->next = idev->last = idev;	/* (re)set own pointers */
-	spin_unlock_irqrestore(&mlp->queue_lock, flags);
+	spin_lock_irqsave(&mlp->online_lock, flags);
+	list_del(&idev->online);
+	spin_unlock_irqrestore(&mlp->online_lock, flags);
 }
 
 /*
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
index edf82f6bff1b..94f95dbe9e16 100644
--- a/drivers/isdn/i4l/isdn_ppp.c
+++ b/drivers/isdn/i4l/isdn_ppp.c
@@ -1396,7 +1396,7 @@ static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to )
 		is->mp_seqno = 0;
 		if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL)
 			return -ENOMEM;
-		idev->next = idev->last = idev;	/* nobody else in a queue */
+
 		lp->netdev->pb->frags = NULL;
 		lp->netdev->pb->frames = 0;
 		lp->netdev->pb->seq = LONG_MAX;
@@ -1468,7 +1468,7 @@ static void isdn_ppp_mp_receive(isdn_net_local *lp, isdn_net_dev *dev,
 	
 	/* find the minimum received sequence number over all links */
 	is->last_link_seqno = minseq = newseq;
-	for (qdev = lp->queue;;) {
+	list_for_each_entry(qdev, &lp->online, online) {
 		slot = qdev->ppp_slot;
 		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
 			printk(KERN_ERR "%s: lpq->ppp_slot(%d)\n",
@@ -1478,8 +1478,6 @@ static void isdn_ppp_mp_receive(isdn_net_local *lp, isdn_net_dev *dev,
 			if (MP_LT(lls, minseq))
 				minseq = lls;
 		}
-		if ((qdev = qdev->next) == lp->queue)
-			break;
 	}
 	if (MP_LT(minseq, mp->seq))
 		minseq = mp->seq;	/* can't go beyond already processed
@@ -1781,7 +1779,7 @@ isdn_ppp_bundle(struct ippp_struct *is, int unit)
     	spin_lock_irqsave(&p->pb->lock, flags);
 
 	nidev = is->idev;
-	idev = p->local.queue;
+	idev = list_entry(p->local.online.next, isdn_net_dev, online);
 	if( nidev->ppp_slot < 0 || nidev->ppp_slot >= ISDN_MAX_CHANNELS ||
 	    idev ->ppp_slot < 0 || idev ->ppp_slot >= ISDN_MAX_CHANNELS ) {
 		printk(KERN_ERR "ippp_bundle: binding to invalid slot %d\n",
diff --git a/include/linux/isdn.h b/include/linux/isdn.h
index 89ab9c70e193..3bd1f510b71b 100644
--- a/include/linux/isdn.h
+++ b/include/linux/isdn.h
@@ -336,10 +336,10 @@ typedef struct isdn_net_local_s {
 				       /* phone[1] = Outgoing Numbers      */
   struct isdn_net_dev_s  *netdev;      /* Ptr to netdev                    */
 
-  struct isdn_net_dev_s  *queue;      /* circular list of all bundled
+  struct list_head       online;       /* circular list of all bundled
 					  channels, which are currently
 					  online                           */
-  spinlock_t queue_lock;               /* lock to protect queue            */
+  spinlock_t             online_lock;  /* lock to protect queue            */
 
 #ifdef CONFIG_ISDN_X25
   struct concap_device_ops *dops;      /* callbacks used by encapsulator   */
@@ -404,12 +404,10 @@ typedef struct isdn_net_dev_s {
   isdn_net_local        *master;       /* Ptr to Master device for slaves  */
   struct isdn_net_dev_s *slave;        /* Ptr to Slave device for masters  */
 
-  struct isdn_net_dev_s *next;         /* Ptr to next link in bundle       */
-  struct isdn_net_dev_s *last;         /* Ptr to last link in bundle       */
-
+  struct list_head       online;       /* Members of local->online         */
 
   char                   name[10];     /* Name of device                   */
-  struct list_head global_list;        /* global list of all isdn_net_devs */
+  struct list_head       global_list;  /* global list of all isdn_net_devs */
 #ifdef CONFIG_ISDN_PPP
   ippp_bundle * pb;		/* pointer to the common bundle structure
    			         * with the per-bundle data */
-- 
2.30.9