lib/cpumask.c: use memblock apis for early memory allocations
[firefly-linux-kernel-4.4.55.git] / lib / kobject.c
index 084f7b18d0c0a722e215dce8d14dcda0e6ba78d8..b0b26665c61161d4f33d1dbdaf49fe74275f400e 100644 (file)
 #include <linux/export.h>
 #include <linux/stat.h>
 #include <linux/slab.h>
+#include <linux/random.h>
+
+/**
+ * kobject_namespace - return @kobj's namespace tag
+ * @kobj: kobject in question
+ *
+ * Returns namespace tag of @kobj if its parent has namespace ops enabled
+ * and thus @kobj should have a namespace tag associated with it.  Returns
+ * %NULL otherwise.
+ */
+const void *kobject_namespace(struct kobject *kobj)
+{
+       const struct kobj_ns_type_operations *ns_ops = kobj_ns_ops(kobj);
+
+       if (!ns_ops || ns_ops->type == KOBJ_NS_TYPE_NONE)
+               return NULL;
+
+       return kobj->ktype->namespace(kobj);
+}
 
 /*
  * populate_dir - populate directory with attributes.
@@ -46,14 +65,39 @@ static int populate_dir(struct kobject *kobj)
 
 static int create_dir(struct kobject *kobj)
 {
-       int error = 0;
-       error = sysfs_create_dir(kobj);
-       if (!error) {
-               error = populate_dir(kobj);
-               if (error)
-                       sysfs_remove_dir(kobj);
+       const struct kobj_ns_type_operations *ops;
+       int error;
+
+       error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj));
+       if (error)
+               return error;
+
+       error = populate_dir(kobj);
+       if (error) {
+               sysfs_remove_dir(kobj);
+               return error;
        }
-       return error;
+
+       /*
+        * @kobj->sd may be deleted by an ancestor going away.  Hold an
+        * extra reference so that it stays until @kobj is gone.
+        */
+       sysfs_get(kobj->sd);
+
+       /*
+        * If @kobj has ns_ops, its children need to be filtered based on
+        * their namespace tags.  Enable namespace support on @kobj->sd.
+        */
+       ops = kobj_child_ns_ops(kobj);
+       if (ops) {
+               BUG_ON(ops->type <= KOBJ_NS_TYPE_NONE);
+               BUG_ON(ops->type >= KOBJ_NS_TYPES);
+               BUG_ON(!kobj_ns_type_registered(ops->type));
+
+               kernfs_enable_ns(kobj->sd);
+       }
+
+       return 0;
 }
 
 static int get_kobj_path_length(struct kobject *kobj)
@@ -220,8 +264,10 @@ int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
                return 0;
 
        kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
-       if (!kobj->name)
+       if (!kobj->name) {
+               kobj->name = old_name;
                return -ENOMEM;
+       }
 
        /* ewww... some of these buggers have '/' in the name ... */
        while ((s = strchr(kobj->name, '/')))
@@ -319,7 +365,7 @@ static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
  *
  * If @parent is set, then the parent of the @kobj will be set to it.
  * If @parent is NULL, then the parent of the @kobj will be set to the
- * kobject associted with the kset assigned to this kobject.  If no kset
+ * kobject associated with the kset assigned to this kobject.  If no kset
  * is assigned to the kobject, then the kobject will be located in the
  * root of the sysfs tree.
  *
@@ -428,7 +474,7 @@ int kobject_rename(struct kobject *kobj, const char *new_name)
                goto out;
        }
 
-       error = sysfs_rename_dir(kobj, new_name);
+       error = sysfs_rename_dir_ns(kobj, new_name, kobject_namespace(kobj));
        if (error)
                goto out;
 
@@ -472,6 +518,7 @@ int kobject_move(struct kobject *kobj, struct kobject *new_parent)
                if (kobj->kset)
                        new_parent = kobject_get(&kobj->kset->kobj);
        }
+
        /* old object path */
        devpath = kobject_get_path(kobj, GFP_KERNEL);
        if (!devpath) {
@@ -486,7 +533,7 @@ int kobject_move(struct kobject *kobj, struct kobject *new_parent)
        sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
        envp[0] = devpath_string;
        envp[1] = NULL;
-       error = sysfs_move_dir(kobj, new_parent);
+       error = sysfs_move_dir_ns(kobj, new_parent, kobject_namespace(kobj));
        if (error)
                goto out;
        old_parent = kobj->parent;
@@ -508,10 +555,15 @@ out:
  */
 void kobject_del(struct kobject *kobj)
 {
+       struct kernfs_node *sd;
+
        if (!kobj)
                return;
 
+       sd = kobj->sd;
        sysfs_remove_dir(kobj);
+       sysfs_put(sd);
+
        kobj->state_in_sysfs = 0;
        kobj_kset_leave(kobj);
        kobject_put(kobj->parent);
@@ -592,10 +644,12 @@ static void kobject_release(struct kref *kref)
 {
        struct kobject *kobj = container_of(kref, struct kobject, kref);
 #ifdef CONFIG_DEBUG_KOBJECT_RELEASE
-       pr_info("kobject: '%s' (%p): %s, parent %p (delayed)\n",
-                kobject_name(kobj), kobj, __func__, kobj->parent);
+       unsigned long delay = HZ + HZ * (get_random_int() & 0x3);
+       pr_info("kobject: '%s' (%p): %s, parent %p (delayed %ld)\n",
+                kobject_name(kobj), kobj, __func__, kobj->parent, delay);
        INIT_DELAYED_WORK(&kobj->release, kobject_delayed_cleanup);
-       schedule_delayed_work(&kobj->release, HZ);
+
+       schedule_delayed_work(&kobj->release, delay);
 #else
        kobject_cleanup(kobj);
 #endif
@@ -753,6 +807,7 @@ void kset_unregister(struct kset *k)
 {
        if (!k)
                return;
+       kobject_del(&k->kobj);
        kobject_put(&k->kobj);
 }