From aaaaaadbe7a663d110814db50fcbe7d320eb4c32 Mon Sep 17 00:00:00 2001
From: Stanislaw Gruszka <stf_xl@wp.pl>
Date: Tue, 29 Nov 2005 16:16:21 -0800
Subject: [PATCH] [ATM]: avoid race conditions related to atm_devs list

Use semaphore to protect atm_devs list, as no one need access to it from
interrupt context.  Avoid race conditions between atm_dev_register(),
atm_dev_lookup() and atm_dev_deregister().  Fix double spin_unlock() bug.

Signed-off-by: Stanislaw Gruszka <stf_xl@wp.pl>
Signed-off-by: Chas Williams <chas@cmf.nrl.navy.mil>
Signed-off-by: David S. Miller <davem@davemloft.net>
---
 net/atm/common.c    |  4 ++--
 net/atm/resources.c | 36 +++++++++++++++++-------------------
 net/atm/resources.h |  3 +--
 3 files changed, 20 insertions(+), 23 deletions(-)

diff --git a/net/atm/common.c b/net/atm/common.c
index db9318fc6031..9e016f404e14 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -427,12 +427,12 @@ int vcc_connect(struct socket *sock, int itf, short vpi, int vci)
 		dev = try_then_request_module(atm_dev_lookup(itf), "atm-device-%d", itf);
 	} else {
 		dev = NULL;
-		spin_lock(&atm_dev_lock);
+		down(&atm_dev_mutex);
 		if (!list_empty(&atm_devs)) {
 			dev = list_entry(atm_devs.next, struct atm_dev, dev_list);
 			atm_dev_hold(dev);
 		}
-		spin_unlock(&atm_dev_lock);
+		up(&atm_dev_mutex);
 	}
 	if (!dev)
 		return -ENODEV;
diff --git a/net/atm/resources.c b/net/atm/resources.c
index 35f3ceb76868..ad533b02b84f 100644
--- a/net/atm/resources.c
+++ b/net/atm/resources.c
@@ -25,7 +25,7 @@
 
 
 LIST_HEAD(atm_devs);
-DEFINE_SPINLOCK(atm_dev_lock);
+DECLARE_MUTEX(atm_dev_mutex);
 
 static struct atm_dev *__alloc_atm_dev(const char *type)
 {
@@ -52,7 +52,7 @@ static struct atm_dev *__atm_dev_lookup(int number)
 
 	list_for_each(p, &atm_devs) {
 		dev = list_entry(p, struct atm_dev, dev_list);
-		if ((dev->ops) && (dev->number == number)) {
+		if (dev->number == number) {
 			atm_dev_hold(dev);
 			return dev;
 		}
@@ -64,9 +64,9 @@ struct atm_dev *atm_dev_lookup(int number)
 {
 	struct atm_dev *dev;
 
-	spin_lock(&atm_dev_lock);
+	down(&atm_dev_mutex);
 	dev = __atm_dev_lookup(number);
-	spin_unlock(&atm_dev_lock);
+	up(&atm_dev_mutex);
 	return dev;
 }
 
@@ -81,11 +81,11 @@ struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
 		    type);
 		return NULL;
 	}
-	spin_lock(&atm_dev_lock);
+	down(&atm_dev_mutex);
 	if (number != -1) {
 		if ((inuse = __atm_dev_lookup(number))) {
 			atm_dev_put(inuse);
-			spin_unlock(&atm_dev_lock);
+			up(&atm_dev_mutex);
 			kfree(dev);
 			return NULL;
 		}
@@ -105,19 +105,17 @@ struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
 		memset(&dev->flags, 0, sizeof(dev->flags));
 	memset(&dev->stats, 0, sizeof(dev->stats));
 	atomic_set(&dev->refcnt, 1);
-	list_add_tail(&dev->dev_list, &atm_devs);
-	spin_unlock(&atm_dev_lock);
 
 	if (atm_proc_dev_register(dev) < 0) {
 		printk(KERN_ERR "atm_dev_register: "
 		       "atm_proc_dev_register failed for dev %s\n",
 		       type);
-		spin_lock(&atm_dev_lock);
-		list_del(&dev->dev_list);
-		spin_unlock(&atm_dev_lock);
+		up(&atm_dev_mutex);
 		kfree(dev);
 		return NULL;
 	}
+	list_add_tail(&dev->dev_list, &atm_devs);
+	up(&atm_dev_mutex);
 
 	return dev;
 }
@@ -129,9 +127,9 @@ void atm_dev_deregister(struct atm_dev *dev)
 
 	atm_proc_dev_deregister(dev);
 
-	spin_lock(&atm_dev_lock);
+	down(&atm_dev_mutex);
 	list_del(&dev->dev_list);
-	spin_unlock(&atm_dev_lock);
+	up(&atm_dev_mutex);
 
         warning_time = jiffies;
         while (atomic_read(&dev->refcnt) != 1) {
@@ -211,16 +209,16 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
 				return -EFAULT;
 			if (get_user(len, &iobuf->length))
 				return -EFAULT;
-			spin_lock(&atm_dev_lock);
+			down(&atm_dev_mutex);
 			list_for_each(p, &atm_devs)
 				size += sizeof(int);
 			if (size > len) {
-				spin_unlock(&atm_dev_lock);
+				up(&atm_dev_mutex);
 				return -E2BIG;
 			}
 			tmp_buf = kmalloc(size, GFP_ATOMIC);
 			if (!tmp_buf) {
-				spin_unlock(&atm_dev_lock);
+				up(&atm_dev_mutex);
 				return -ENOMEM;
 			}
 			tmp_p = tmp_buf;
@@ -228,7 +226,7 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
 				dev = list_entry(p, struct atm_dev, dev_list);
 				*tmp_p++ = dev->number;
 			}
-			spin_unlock(&atm_dev_lock);
+			up(&atm_dev_mutex);
 		        error = ((copy_to_user(buf, tmp_buf, size)) ||
 					put_user(size, &iobuf->length))
 						? -EFAULT : 0;
@@ -415,13 +413,13 @@ static __inline__ void *dev_get_idx(loff_t left)
 
 void *atm_dev_seq_start(struct seq_file *seq, loff_t *pos)
 {
- 	spin_lock(&atm_dev_lock);
+ 	down(&atm_dev_mutex);
 	return *pos ? dev_get_idx(*pos) : (void *) 1;
 }
 
 void atm_dev_seq_stop(struct seq_file *seq, void *v)
 {
- 	spin_unlock(&atm_dev_lock);
+ 	up(&atm_dev_mutex);
 }
  
 void *atm_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
diff --git a/net/atm/resources.h b/net/atm/resources.h
index 12910619dbb6..b7fb82a93b42 100644
--- a/net/atm/resources.h
+++ b/net/atm/resources.h
@@ -11,8 +11,7 @@
 
 
 extern struct list_head atm_devs;
-extern spinlock_t atm_dev_lock;
-
+extern struct semaphore atm_dev_mutex;
 
 int atm_dev_ioctl(unsigned int cmd, void __user *arg);