[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>
diff --git a/net/atm/common.c b/net/atm/common.c
index db9318f..9e016f4 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -427,12 +427,12 @@
 		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 35f3ceb..ad533b0 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 @@
 
 	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 *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 @@
 		    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 @@
 		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 @@
 
 	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 @@
 				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 @@
 				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 @@
 
 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 1291061..b7fb82a 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);