Merge branch 'bkl/fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/frederic...
[safe/jmp/linux-2.6] / drivers / base / core.c
index 2fd9e61..9630fbd 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/notifier.h>
 #include <linux/genhd.h>
 #include <linux/kallsyms.h>
-#include <linux/semaphore.h>
 #include <linux/mutex.h>
 #include <linux/async.h>
 
@@ -100,7 +99,7 @@ static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,
        return ret;
 }
 
-static struct sysfs_ops dev_sysfs_ops = {
+static const struct sysfs_ops dev_sysfs_ops = {
        .show   = dev_attr_show,
        .store  = dev_attr_store,
 };
@@ -132,9 +131,21 @@ static void device_release(struct kobject *kobj)
        kfree(p);
 }
 
+static const void *device_namespace(struct kobject *kobj)
+{
+       struct device *dev = to_dev(kobj);
+       const void *ns = NULL;
+
+       if (dev->class && dev->class->ns_type)
+               ns = dev->class->namespace(dev);
+
+       return ns;
+}
+
 static struct kobj_type device_ktype = {
        .release        = device_release,
        .sysfs_ops      = &dev_sysfs_ops,
+       .namespace      = device_namespace,
 };
 
 
@@ -252,7 +263,7 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj,
        return retval;
 }
 
-static struct kset_uevent_ops device_uevent_ops = {
+static const struct kset_uevent_ops device_uevent_ops = {
        .filter =       dev_uevent_filter,
        .name =         dev_uevent_name,
        .uevent =       dev_uevent,
@@ -306,15 +317,10 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
 {
        enum kobject_action action;
 
-       if (kobject_action_type(buf, count, &action) == 0) {
+       if (kobject_action_type(buf, count, &action) == 0)
                kobject_uevent(&dev->kobj, action);
-               goto out;
-       }
-
-       dev_err(dev, "uevent: unsupported action-string; this will "
-                    "be ignored in a future kernel version\n");
-       kobject_uevent(&dev->kobj, KOBJ_ADD);
-out:
+       else
+               dev_err(dev, "uevent: unknown action-string\n");
        return count;
 }
 
@@ -472,7 +478,8 @@ void device_remove_file(struct device *dev,
  * @dev: device.
  * @attr: device binary attribute descriptor.
  */
-int device_create_bin_file(struct device *dev, struct bin_attribute *attr)
+int device_create_bin_file(struct device *dev,
+                          const struct bin_attribute *attr)
 {
        int error = -EINVAL;
        if (dev)
@@ -486,7 +493,8 @@ EXPORT_SYMBOL_GPL(device_create_bin_file);
  * @dev: device.
  * @attr: device binary attribute descriptor.
  */
-void device_remove_bin_file(struct device *dev, struct bin_attribute *attr)
+void device_remove_bin_file(struct device *dev,
+                           const struct bin_attribute *attr)
 {
        if (dev)
                sysfs_remove_bin_file(&dev->kobj, attr);
@@ -562,10 +570,10 @@ void device_initialize(struct device *dev)
        dev->kobj.kset = devices_kset;
        kobject_init(&dev->kobj, &device_ktype);
        INIT_LIST_HEAD(&dev->dma_pools);
-       init_MUTEX(&dev->sem);
+       mutex_init(&dev->mutex);
+       lockdep_set_novalidate_class(&dev->mutex);
        spin_lock_init(&dev->devres_lock);
        INIT_LIST_HEAD(&dev->devres_head);
-       device_init_wakeup(dev, 0);
        device_pm_init(dev);
        set_dev_node(dev, -1);
 }
@@ -599,12 +607,61 @@ static struct kobject *virtual_device_parent(struct device *dev)
        return virtual_dir;
 }
 
-static struct kobject *get_device_parent(struct device *dev,
-                                        struct device *parent)
+struct class_dir {
+       struct kobject kobj;
+       struct class *class;
+};
+
+#define to_class_dir(obj) container_of(obj, struct class_dir, kobj)
+
+static void class_dir_release(struct kobject *kobj)
+{
+       struct class_dir *dir = to_class_dir(kobj);
+       kfree(dir);
+}
+
+static const
+struct kobj_ns_type_operations *class_dir_child_ns_type(struct kobject *kobj)
+{
+       struct class_dir *dir = to_class_dir(kobj);
+       return dir->class->ns_type;
+}
+
+static struct kobj_type class_dir_ktype = {
+       .release        = class_dir_release,
+       .sysfs_ops      = &kobj_sysfs_ops,
+       .child_ns_type  = class_dir_child_ns_type
+};
+
+static struct kobject *
+class_dir_create_and_add(struct class *class, struct kobject *parent_kobj)
 {
+       struct class_dir *dir;
        int retval;
 
+       dir = kzalloc(sizeof(*dir), GFP_KERNEL);
+       if (!dir)
+               return NULL;
+
+       dir->class = class;
+       kobject_init(&dir->kobj, &class_dir_ktype);
+
+       dir->kobj.kset = &class->p->class_dirs;
+
+       retval = kobject_add(&dir->kobj, parent_kobj, "%s", class->name);
+       if (retval < 0) {
+               kobject_put(&dir->kobj);
+               return NULL;
+       }
+       return &dir->kobj;
+}
+
+
+static struct kobject *get_device_parent(struct device *dev,
+                                        struct device *parent)
+{
        if (dev->class) {
+               static DEFINE_MUTEX(gdp_mutex);
                struct kobject *kobj = NULL;
                struct kobject *parent_kobj;
                struct kobject *k;
@@ -621,6 +678,8 @@ static struct kobject *get_device_parent(struct device *dev,
                else
                        parent_kobj = &parent->kobj;
 
+               mutex_lock(&gdp_mutex);
+
                /* find our class-directory at the parent and reference it */
                spin_lock(&dev->class->p->class_dirs.list_lock);
                list_for_each_entry(k, &dev->class->p->class_dirs.list, entry)
@@ -629,20 +688,15 @@ static struct kobject *get_device_parent(struct device *dev,
                                break;
                        }
                spin_unlock(&dev->class->p->class_dirs.list_lock);
-               if (kobj)
+               if (kobj) {
+                       mutex_unlock(&gdp_mutex);
                        return kobj;
+               }
 
                /* or create a new class-directory at the parent device */
-               k = kobject_create();
-               if (!k)
-                       return NULL;
-               k->kset = &dev->class->p->class_dirs;
-               retval = kobject_add(k, parent_kobj, "%s", dev->class->name);
-               if (retval < 0) {
-                       kobject_put(k);
-                       return NULL;
-               }
+               k = class_dir_create_and_add(dev->class, parent_kobj);
                /* do not emit an uevent for this simple "glue" directory */
+               mutex_unlock(&gdp_mutex);
                return k;
        }
 
@@ -732,7 +786,7 @@ out_device:
 out_busid:
        if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
            device_is_not_partition(dev))
-               sysfs_remove_link(&dev->class->p->class_subsys.kobj,
+               sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj,
                                  dev_name(dev));
 #else
        /* link in the class directory pointing to the device */
@@ -750,7 +804,7 @@ out_busid:
        return 0;
 
 out_busid:
-       sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev));
+       sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev_name(dev));
 #endif
 
 out_subsys:
@@ -778,13 +832,13 @@ static void device_remove_class_symlinks(struct device *dev)
 
        if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
            device_is_not_partition(dev))
-               sysfs_remove_link(&dev->class->p->class_subsys.kobj,
+               sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj,
                                  dev_name(dev));
 #else
        if (dev->parent && device_is_not_partition(dev))
                sysfs_remove_link(&dev->kobj, "device");
 
-       sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev));
+       sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev_name(dev));
 #endif
 
        sysfs_remove_link(&dev->kobj, "subsystem");
@@ -907,8 +961,10 @@ int device_add(struct device *dev)
                dev->init_name = NULL;
        }
 
-       if (!dev_name(dev))
+       if (!dev_name(dev)) {
+               error = -EINVAL;
                goto name_error;
+       }
 
        pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
 
@@ -1337,6 +1393,8 @@ static void root_device_release(struct device *dev)
  * 'module' symlink which points to the @owner directory
  * in sysfs.
  *
+ * Returns &struct device pointer on success, or ERR_PTR() on error.
+ *
  * Note: You probably want to use root_device_register().
  */
 struct device *__root_device_register(const char *name, struct module *owner)
@@ -1362,7 +1420,7 @@ struct device *__root_device_register(const char *name, struct module *owner)
                return ERR_PTR(err);
        }
 
-#ifdef CONFIG_MODULE   /* gotta find a "cleaner" way to do this */
+#ifdef CONFIG_MODULES  /* gotta find a "cleaner" way to do this */
        if (owner) {
                struct module_kobject *mk = &owner->mkobj;
 
@@ -1424,6 +1482,8 @@ static void device_create_release(struct device *dev)
  * Any further sysfs files that might be required can be created using this
  * pointer.
  *
+ * Returns &struct device pointer on success, or ERR_PTR() on error.
+ *
  * Note: the struct class passed to this function must have previously
  * been created with a call to class_create().
  */
@@ -1484,6 +1544,8 @@ EXPORT_SYMBOL_GPL(device_create_vargs);
  * Any further sysfs files that might be required can be created using this
  * pointer.
  *
+ * Returns &struct device pointer on success, or ERR_PTR() on error.
+ *
  * Note: the struct class passed to this function must have previously
  * been created with a call to class_create().
  */
@@ -1562,6 +1624,14 @@ int device_rename(struct device *dev, char *new_name)
                goto out;
        }
 
+#ifndef CONFIG_SYSFS_DEPRECATED
+       if (dev->class) {
+               error = sysfs_rename_link(&dev->class->p->class_subsys.kobj,
+                       &dev->kobj, old_device_name, new_name);
+               if (error)
+                       goto out;
+       }
+#endif
        error = kobject_rename(&dev->kobj, new_name);
        if (error)
                goto out;
@@ -1570,23 +1640,12 @@ int device_rename(struct device *dev, char *new_name)
        if (old_class_name) {
                new_class_name = make_class_name(dev->class->name, &dev->kobj);
                if (new_class_name) {
-                       error = sysfs_create_link_nowarn(&dev->parent->kobj,
-                                                        &dev->kobj,
-                                                        new_class_name);
-                       if (error)
-                               goto out;
-                       sysfs_remove_link(&dev->parent->kobj, old_class_name);
+                       error = sysfs_rename_link(&dev->parent->kobj,
+                                                 &dev->kobj,
+                                                 old_class_name,
+                                                 new_class_name);
                }
        }
-#else
-       if (dev->class) {
-               error = sysfs_create_link_nowarn(&dev->class->p->class_subsys.kobj,
-                                                &dev->kobj, dev_name(dev));
-               if (error)
-                       goto out;
-               sysfs_remove_link(&dev->class->p->class_subsys.kobj,
-                                 old_device_name);
-       }
 #endif
 
 out:
@@ -1727,10 +1786,25 @@ EXPORT_SYMBOL_GPL(device_move);
  */
 void device_shutdown(void)
 {
-       struct device *dev, *devn;
+       struct device *dev;
+
+       spin_lock(&devices_kset->list_lock);
+       /*
+        * Walk the devices list backward, shutting down each in turn.
+        * Beware that device unplug events may also start pulling
+        * devices offline, even as the system is shutting down.
+        */
+       while (!list_empty(&devices_kset->list)) {
+               dev = list_entry(devices_kset->list.prev, struct device,
+                               kobj.entry);
+               get_device(dev);
+               /*
+                * Make sure the device is off the kset list, in the
+                * event that dev->*->shutdown() doesn't remove it.
+                */
+               list_del_init(&dev->kobj.entry);
+               spin_unlock(&devices_kset->list_lock);
 
-       list_for_each_entry_safe_reverse(dev, devn, &devices_kset->list,
-                               kobj.entry) {
                if (dev->bus && dev->bus->shutdown) {
                        dev_dbg(dev, "shutdown\n");
                        dev->bus->shutdown(dev);
@@ -1738,6 +1812,10 @@ void device_shutdown(void)
                        dev_dbg(dev, "shutdown\n");
                        dev->driver->shutdown(dev);
                }
+               put_device(dev);
+
+               spin_lock(&devices_kset->list_lock);
        }
+       spin_unlock(&devices_kset->list_lock);
        async_synchronize_full();
 }