]> git.neil.brown.name Git - history.git/commitdiff
Fix device list locking.
authorDavid S. Miller <davem@nuts.ninka.net>
Tue, 26 Mar 2002 07:11:11 +0000 (23:11 -0800)
committerDavid S. Miller <davem@nuts.ninka.net>
Tue, 26 Mar 2002 07:11:11 +0000 (23:11 -0800)
Based upon a patch from Maxim Krasnyansky.

net/atm/resources.c

index c04f8b10ee2209449c041d177a88068261d0eac7..3857176cc8bc2750f5863e4b0940d5cfb2e749d2 100644 (file)
@@ -31,95 +31,107 @@ static struct atm_dev *last_dev = NULL;
 struct atm_vcc *nodev_vccs = NULL;
 extern spinlock_t atm_dev_lock;
 
-
-static struct atm_dev *alloc_atm_dev(const char *type)
+/* Caller must hold atm_dev_lock. */
+static struct atm_dev *__alloc_atm_dev(const char *type)
 {
        struct atm_dev *dev;
 
-       dev = kmalloc(sizeof(*dev),GFP_KERNEL);
-       if (!dev) return NULL;
-       memset(dev,0,sizeof(*dev));
+       dev = kmalloc(sizeof(*dev), GFP_ATOMIC);
+       if (!dev)
+               return NULL;
+       memset(dev, 0, sizeof(*dev));
        dev->type = type;
        dev->signal = ATM_PHY_SIG_UNKNOWN;
        dev->link_rate = ATM_OC3_PCR;
        dev->next = NULL;
 
-       spin_lock(&atm_dev_lock);
-
        dev->prev = last_dev;
 
-       if (atm_devs) last_dev->next = dev;
-       else atm_devs = dev;
+       if (atm_devs)
+               last_dev->next = dev;
+       else
+               atm_devs = dev;
        last_dev = dev;
-       spin_unlock(&atm_dev_lock);
+
        return dev;
 }
 
-
-static void free_atm_dev(struct atm_dev *dev)
+/* Caller must hold atm_dev_lock. */
+static void __free_atm_dev(struct atm_dev *dev)
 {
-       spin_lock (&atm_dev_lock);
-       
-       if (dev->prev) dev->prev->next = dev->next;
-       else atm_devs = dev->next;
-       if (dev->next) dev->next->prev = dev->prev;
-       else last_dev = dev->prev;
+       if (dev->prev)
+               dev->prev->next = dev->next;
+       else
+               atm_devs = dev->next;
+       if (dev->next)
+               dev->next->prev = dev->prev;
+       else
+               last_dev = dev->prev;
        kfree(dev);
-       
-       spin_unlock (&atm_dev_lock);
 }
 
-
+/* Caller must hold atm_dev_lock. */
 struct atm_dev *atm_find_dev(int number)
 {
        struct atm_dev *dev;
 
        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;
 }
 
 
-struct atm_dev *atm_dev_register(const char *type,const struct atmdev_ops *ops,
-    int number,atm_dev_flags_t *flags)
+struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
+                                int number, atm_dev_flags_t *flags)
 {
        struct atm_dev *dev;
 
-       dev = alloc_atm_dev(type);
+       spin_lock(&atm_dev_lock);
+
+       dev = __alloc_atm_dev(type);
        if (!dev) {
                printk(KERN_ERR "atm_dev_register: no space for dev %s\n",
                    type);
-               return NULL;
+               goto done;
        }
        if (number != -1) {
                if (atm_find_dev(number)) {
-                       free_atm_dev(dev);
-                       return NULL;
+                       __free_atm_dev(dev);
+                       dev = NULL;
+                       goto done;
                }
                dev->number = number;
-       }
-       else {
+       } else {
                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->dev_data = NULL;
        barrier();
        dev->ops = ops;
-       if (flags) dev->flags = *flags;
-       else memset(&dev->flags,0,sizeof(dev->flags));
-       memset((void *) &dev->stats,0,sizeof(dev->stats));
+       if (flags)
+               dev->flags = *flags;
+       else
+               memset(&dev->flags, 0, sizeof(dev->flags));
+       memset(&dev->stats, 0, sizeof(dev->stats));
+
 #ifdef CONFIG_PROC_FS
-       if (ops->proc_read)
+       if (ops->proc_read) {
                if (atm_proc_dev_register(dev) < 0) {
                        printk(KERN_ERR "atm_dev_register: "
-                           "atm_proc_dev_register failed for dev %s\n",type);
-                       spin_unlock (&atm_dev_lock);            
-                       free_atm_dev(dev);
-                       return NULL;
+                              "atm_proc_dev_register failed for dev %s\n",
+                              type);
+                       __free_atm_dev(dev);
+                       dev = NULL;
+                       goto done;
                }
+       }
 #endif
-       spin_unlock (&atm_dev_lock);            
+
+done:
+       spin_unlock(&atm_dev_lock);
        return dev;
 }
 
@@ -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)
 {
 #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
-       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)
 {
        if (dev->vccs) {
-               set_bit(ATM_DF_CLOSE,&dev->flags);
+               set_bit(ATM_DF_CLOSE, &dev->flags);
                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);
 }
 
@@ -149,16 +164,18 @@ struct sock *alloc_atm_vcc_sk(int family)
        struct atm_vcc *vcc;
 
        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);
        if (!vcc) {
                sk_free(sk);
                return NULL;
        }
-       sock_init_data(NULL,sk);
-       memset(vcc,0,sizeof(*vcc));
+       sock_init_data(NULL, sk);
+       memset(vcc, 0, sizeof(*vcc));
        vcc->sk = sk;
-       if (nodev_vccs) nodev_vccs->prev = vcc;
+       if (nodev_vccs)
+               nodev_vccs->prev = vcc;
        vcc->prev = NULL;
        vcc->next = nodev_vccs;
        nodev_vccs = vcc;
@@ -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)
 {
-       if (vcc->prev) vcc->prev->next = vcc->next;
-       else if (vcc->dev) vcc->dev->vccs = vcc->next;
-           else nodev_vccs = vcc->next;
-       if (vcc->next) vcc->next->prev = vcc->prev;
-       else if (vcc->dev) vcc->dev->last = vcc->prev;
+       if (vcc->prev)
+               vcc->prev->next = vcc->next;
+       else if (vcc->dev)
+               vcc->dev->vccs = vcc->next;
+       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 &&
            test_bit(ATM_DF_CLOSE,&vcc->dev->flags))
                shutdown_atm_dev(vcc->dev);
@@ -185,7 +207,6 @@ void free_atm_vcc_sk(struct sock *sk)
        sk_free(sk);
 }
 
-
 void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev)
 {
        unlink_vcc(vcc,dev);
@@ -193,19 +214,20 @@ void bind_vcc(struct atm_vcc *vcc,struct atm_dev *dev)
        if (dev) {
                vcc->next = NULL;
                vcc->prev = dev->last;
-               if (dev->vccs) dev->last->next = vcc;
-               else dev->vccs = vcc;
+               if (dev->vccs)
+                       dev->last->next = vcc;
+               else
+                       dev->vccs = vcc;
                dev->last = vcc;
-       }
-       else {
-               if (nodev_vccs) nodev_vccs->prev = vcc;
+       } else {
+               if (nodev_vccs)
+                       nodev_vccs->prev = vcc;
                vcc->next = nodev_vccs;
                vcc->prev = NULL;
                nodev_vccs = vcc;
        }
 }
 
-
 EXPORT_SYMBOL(atm_dev_register);
 EXPORT_SYMBOL(atm_dev_deregister);
 EXPORT_SYMBOL(atm_find_dev);