[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:
parent
4969328026
commit
aaaaaadbe7
@ -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;
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user