[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>
This commit is contained in:
Stanislaw Gruszka 2005-11-29 16:16:21 -08:00 committed by David S. Miller
parent 4969328026
commit aaaaaadbe7
3 changed files with 20 additions and 23 deletions

View file

@ -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); dev = try_then_request_module(atm_dev_lookup(itf), "atm-device-%d", itf);
} else { } else {
dev = NULL; dev = NULL;
spin_lock(&atm_dev_lock); down(&atm_dev_mutex);
if (!list_empty(&atm_devs)) { if (!list_empty(&atm_devs)) {
dev = list_entry(atm_devs.next, struct atm_dev, dev_list); dev = list_entry(atm_devs.next, struct atm_dev, dev_list);
atm_dev_hold(dev); atm_dev_hold(dev);
} }
spin_unlock(&atm_dev_lock); up(&atm_dev_mutex);
} }
if (!dev) if (!dev)
return -ENODEV; return -ENODEV;

View file

@ -25,7 +25,7 @@
LIST_HEAD(atm_devs); LIST_HEAD(atm_devs);
DEFINE_SPINLOCK(atm_dev_lock); DECLARE_MUTEX(atm_dev_mutex);
static struct atm_dev *__alloc_atm_dev(const char *type) 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) { list_for_each(p, &atm_devs) {
dev = list_entry(p, struct atm_dev, dev_list); dev = list_entry(p, struct atm_dev, dev_list);
if ((dev->ops) && (dev->number == number)) { if (dev->number == number) {
atm_dev_hold(dev); atm_dev_hold(dev);
return dev; return dev;
} }
@ -64,9 +64,9 @@ struct atm_dev *atm_dev_lookup(int number)
{ {
struct atm_dev *dev; struct atm_dev *dev;
spin_lock(&atm_dev_lock); down(&atm_dev_mutex);
dev = __atm_dev_lookup(number); dev = __atm_dev_lookup(number);
spin_unlock(&atm_dev_lock); up(&atm_dev_mutex);
return dev; return dev;
} }
@ -81,11 +81,11 @@ struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
type); type);
return NULL; return NULL;
} }
spin_lock(&atm_dev_lock); down(&atm_dev_mutex);
if (number != -1) { if (number != -1) {
if ((inuse = __atm_dev_lookup(number))) { if ((inuse = __atm_dev_lookup(number))) {
atm_dev_put(inuse); atm_dev_put(inuse);
spin_unlock(&atm_dev_lock); up(&atm_dev_mutex);
kfree(dev); kfree(dev);
return NULL; 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->flags, 0, sizeof(dev->flags));
memset(&dev->stats, 0, sizeof(dev->stats)); memset(&dev->stats, 0, sizeof(dev->stats));
atomic_set(&dev->refcnt, 1); 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) { if (atm_proc_dev_register(dev) < 0) {
printk(KERN_ERR "atm_dev_register: " printk(KERN_ERR "atm_dev_register: "
"atm_proc_dev_register failed for dev %s\n", "atm_proc_dev_register failed for dev %s\n",
type); type);
spin_lock(&atm_dev_lock); up(&atm_dev_mutex);
list_del(&dev->dev_list);
spin_unlock(&atm_dev_lock);
kfree(dev); kfree(dev);
return NULL; return NULL;
} }
list_add_tail(&dev->dev_list, &atm_devs);
up(&atm_dev_mutex);
return dev; return dev;
} }
@ -129,9 +127,9 @@ void atm_dev_deregister(struct atm_dev *dev)
atm_proc_dev_deregister(dev); atm_proc_dev_deregister(dev);
spin_lock(&atm_dev_lock); down(&atm_dev_mutex);
list_del(&dev->dev_list); list_del(&dev->dev_list);
spin_unlock(&atm_dev_lock); up(&atm_dev_mutex);
warning_time = jiffies; warning_time = jiffies;
while (atomic_read(&dev->refcnt) != 1) { while (atomic_read(&dev->refcnt) != 1) {
@ -211,16 +209,16 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
return -EFAULT; return -EFAULT;
if (get_user(len, &iobuf->length)) if (get_user(len, &iobuf->length))
return -EFAULT; return -EFAULT;
spin_lock(&atm_dev_lock); down(&atm_dev_mutex);
list_for_each(p, &atm_devs) list_for_each(p, &atm_devs)
size += sizeof(int); size += sizeof(int);
if (size > len) { if (size > len) {
spin_unlock(&atm_dev_lock); up(&atm_dev_mutex);
return -E2BIG; return -E2BIG;
} }
tmp_buf = kmalloc(size, GFP_ATOMIC); tmp_buf = kmalloc(size, GFP_ATOMIC);
if (!tmp_buf) { if (!tmp_buf) {
spin_unlock(&atm_dev_lock); up(&atm_dev_mutex);
return -ENOMEM; return -ENOMEM;
} }
tmp_p = tmp_buf; 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); dev = list_entry(p, struct atm_dev, dev_list);
*tmp_p++ = dev->number; *tmp_p++ = dev->number;
} }
spin_unlock(&atm_dev_lock); up(&atm_dev_mutex);
error = ((copy_to_user(buf, tmp_buf, size)) || error = ((copy_to_user(buf, tmp_buf, size)) ||
put_user(size, &iobuf->length)) put_user(size, &iobuf->length))
? -EFAULT : 0; ? -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) 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; return *pos ? dev_get_idx(*pos) : (void *) 1;
} }
void atm_dev_seq_stop(struct seq_file *seq, void *v) 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) void *atm_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)

View file

@ -11,8 +11,7 @@
extern struct list_head atm_devs; 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); int atm_dev_ioctl(unsigned int cmd, void __user *arg);