Commit b974e3f3 authored by David S. Miller's avatar David S. Miller

Fix device list locking.

Based upon a patch from Maxim Krasnyansky.
parent be1a4f14
...@@ -31,95 +31,107 @@ static struct atm_dev *last_dev = NULL; ...@@ -31,95 +31,107 @@ static struct atm_dev *last_dev = NULL;
struct atm_vcc *nodev_vccs = NULL; struct atm_vcc *nodev_vccs = NULL;
extern spinlock_t atm_dev_lock; extern spinlock_t atm_dev_lock;
/* Caller must hold atm_dev_lock. */
static struct atm_dev *alloc_atm_dev(const char *type) static struct atm_dev *__alloc_atm_dev(const char *type)
{ {
struct atm_dev *dev; struct atm_dev *dev;
dev = kmalloc(sizeof(*dev),GFP_KERNEL); dev = kmalloc(sizeof(*dev), GFP_ATOMIC);
if (!dev) return NULL; if (!dev)
memset(dev,0,sizeof(*dev)); return NULL;
memset(dev, 0, sizeof(*dev));
dev->type = type; dev->type = type;
dev->signal = ATM_PHY_SIG_UNKNOWN; dev->signal = ATM_PHY_SIG_UNKNOWN;
dev->link_rate = ATM_OC3_PCR; dev->link_rate = ATM_OC3_PCR;
dev->next = NULL; dev->next = NULL;
spin_lock(&atm_dev_lock);
dev->prev = last_dev; dev->prev = last_dev;
if (atm_devs) last_dev->next = dev; if (atm_devs)
else atm_devs = dev; last_dev->next = dev;
else
atm_devs = dev;
last_dev = dev; last_dev = dev;
spin_unlock(&atm_dev_lock);
return dev; return dev;
} }
/* Caller must hold atm_dev_lock. */
static void free_atm_dev(struct atm_dev *dev) static void __free_atm_dev(struct atm_dev *dev)
{ {
spin_lock (&atm_dev_lock); if (dev->prev)
dev->prev->next = dev->next;
if (dev->prev) dev->prev->next = dev->next; else
else atm_devs = dev->next; atm_devs = dev->next;
if (dev->next) dev->next->prev = dev->prev; if (dev->next)
else last_dev = dev->prev; dev->next->prev = dev->prev;
else
last_dev = dev->prev;
kfree(dev); kfree(dev);
spin_unlock (&atm_dev_lock);
} }
/* Caller must hold atm_dev_lock. */
struct atm_dev *atm_find_dev(int number) struct atm_dev *atm_find_dev(int number)
{ {
struct atm_dev *dev; struct atm_dev *dev;
for (dev = atm_devs; dev; dev = dev->next) for (dev = atm_devs; dev; dev = dev->next)
if (dev->ops && dev->number == number) return dev; if (dev->ops && dev->number == number)
return dev;
return NULL; return NULL;
} }
struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops, struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
int number,atm_dev_flags_t *flags) int number, atm_dev_flags_t *flags)
{ {
struct atm_dev *dev; struct atm_dev *dev;
dev = alloc_atm_dev(type); spin_lock(&atm_dev_lock);
dev = __alloc_atm_dev(type);
if (!dev) { if (!dev) {
printk(KERN_ERR "atm_dev_register: no space for dev %s\n", printk(KERN_ERR "atm_dev_register: no space for dev %s\n",
type); type);
return NULL; goto done;
} }
if (number != -1) { if (number != -1) {
if (atm_find_dev(number)) { if (atm_find_dev(number)) {
free_atm_dev(dev); __free_atm_dev(dev);
return NULL; dev = NULL;
goto done;
} }
dev->number = number; dev->number = number;
} } else {
else {
dev->number = 0; dev->number = 0;
while (atm_find_dev(dev->number)) dev->number++; while (atm_find_dev(dev->number))
dev->number++;
} }
dev->vccs = dev->last = NULL; dev->vccs = dev->last = NULL;
dev->dev_data = NULL; dev->dev_data = NULL;
barrier(); barrier();
dev->ops = ops; dev->ops = ops;
if (flags) dev->flags = *flags; if (flags)
else memset(&dev->flags,0,sizeof(dev->flags)); dev->flags = *flags;
memset((void *) &dev->stats,0,sizeof(dev->stats)); else
memset(&dev->flags, 0, sizeof(dev->flags));
memset(&dev->stats, 0, sizeof(dev->stats));
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
if (ops->proc_read) if (ops->proc_read) {
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",type); "atm_proc_dev_register failed for dev %s\n",
spin_unlock (&atm_dev_lock); type);
free_atm_dev(dev); __free_atm_dev(dev);
return NULL; dev = NULL;
goto done;
} }
}
#endif #endif
spin_unlock (&atm_dev_lock);
done:
spin_unlock(&atm_dev_lock);
return dev; return dev;
} }
...@@ -127,19 +139,22 @@ struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops, ...@@ -127,19 +139,22 @@ struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops,
void atm_dev_deregister(struct atm_dev *dev) void atm_dev_deregister(struct atm_dev *dev)
{ {
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
if (dev->ops->proc_read) atm_proc_dev_deregister(dev); if (dev->ops->proc_read)
atm_proc_dev_deregister(dev);
#endif #endif
free_atm_dev(dev); spin_lock(&atm_dev_lock);
__free_atm_dev(dev);
spin_unlock(&atm_dev_lock);
} }
void shutdown_atm_dev(struct atm_dev *dev) void shutdown_atm_dev(struct atm_dev *dev)
{ {
if (dev->vccs) { if (dev->vccs) {
set_bit(ATM_DF_CLOSE,&dev->flags); set_bit(ATM_DF_CLOSE, &dev->flags);
return; return;
} }
if (dev->ops->dev_close) dev->ops->dev_close(dev); if (dev->ops->dev_close)
dev->ops->dev_close(dev);
atm_dev_deregister(dev); atm_dev_deregister(dev);
} }
...@@ -149,16 +164,18 @@ struct sock *alloc_atm_vcc_sk(int family) ...@@ -149,16 +164,18 @@ struct sock *alloc_atm_vcc_sk(int family)
struct atm_vcc *vcc; struct atm_vcc *vcc;
sk = sk_alloc(family, GFP_KERNEL, 1, NULL); sk = sk_alloc(family, GFP_KERNEL, 1, NULL);
if (!sk) return NULL; if (!sk)
return NULL;
vcc = atm_sk(sk) = kmalloc(sizeof(*vcc), GFP_KERNEL); vcc = atm_sk(sk) = kmalloc(sizeof(*vcc), GFP_KERNEL);
if (!vcc) { if (!vcc) {
sk_free(sk); sk_free(sk);
return NULL; return NULL;
} }
sock_init_data(NULL,sk); sock_init_data(NULL, sk);
memset(vcc,0,sizeof(*vcc)); memset(vcc, 0, sizeof(*vcc));
vcc->sk = sk; vcc->sk = sk;
if (nodev_vccs) nodev_vccs->prev = vcc; if (nodev_vccs)
nodev_vccs->prev = vcc;
vcc->prev = NULL; vcc->prev = NULL;
vcc->next = nodev_vccs; vcc->next = nodev_vccs;
nodev_vccs = vcc; nodev_vccs = vcc;
...@@ -168,11 +185,16 @@ struct sock *alloc_atm_vcc_sk(int family) ...@@ -168,11 +185,16 @@ struct sock *alloc_atm_vcc_sk(int family)
static void unlink_vcc(struct atm_vcc *vcc,struct atm_dev *hold_dev) static void unlink_vcc(struct atm_vcc *vcc,struct atm_dev *hold_dev)
{ {
if (vcc->prev) vcc->prev->next = vcc->next; if (vcc->prev)
else if (vcc->dev) vcc->dev->vccs = vcc->next; vcc->prev->next = vcc->next;
else nodev_vccs = vcc->next; else if (vcc->dev)
if (vcc->next) vcc->next->prev = vcc->prev; vcc->dev->vccs = vcc->next;
else if (vcc->dev) vcc->dev->last = vcc->prev; else
nodev_vccs = vcc->next;
if (vcc->next)
vcc->next->prev = vcc->prev;
else if (vcc->dev)
vcc->dev->last = vcc->prev;
if (vcc->dev && vcc->dev != hold_dev && !vcc->dev->vccs && if (vcc->dev && vcc->dev != hold_dev && !vcc->dev->vccs &&
test_bit(ATM_DF_CLOSE,&vcc->dev->flags)) test_bit(ATM_DF_CLOSE,&vcc->dev->flags))
shutdown_atm_dev(vcc->dev); shutdown_atm_dev(vcc->dev);
...@@ -185,7 +207,6 @@ void free_atm_vcc_sk(struct sock *sk) ...@@ -185,7 +207,6 @@ void free_atm_vcc_sk(struct sock *sk)
sk_free(sk); sk_free(sk);
} }
void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev) void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev)
{ {
unlink_vcc(vcc,dev); unlink_vcc(vcc,dev);
...@@ -193,19 +214,20 @@ void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev) ...@@ -193,19 +214,20 @@ void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev)
if (dev) { if (dev) {
vcc->next = NULL; vcc->next = NULL;
vcc->prev = dev->last; vcc->prev = dev->last;
if (dev->vccs) dev->last->next = vcc; if (dev->vccs)
else dev->vccs = vcc; dev->last->next = vcc;
else
dev->vccs = vcc;
dev->last = vcc; dev->last = vcc;
} } else {
else { if (nodev_vccs)
if (nodev_vccs) nodev_vccs->prev = vcc; nodev_vccs->prev = vcc;
vcc->next = nodev_vccs; vcc->next = nodev_vccs;
vcc->prev = NULL; vcc->prev = NULL;
nodev_vccs = vcc; nodev_vccs = vcc;
} }
} }
EXPORT_SYMBOL(atm_dev_register); EXPORT_SYMBOL(atm_dev_register);
EXPORT_SYMBOL(atm_dev_deregister); EXPORT_SYMBOL(atm_dev_deregister);
EXPORT_SYMBOL(atm_find_dev); EXPORT_SYMBOL(atm_find_dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment