diff options
Diffstat (limited to 'lib/kobject.c')
| -rw-r--r-- | lib/kobject.c | 89 | 
1 files changed, 71 insertions, 18 deletions
diff --git a/lib/kobject.c b/lib/kobject.c index 96217513470..58751bb80a7 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -17,6 +17,25 @@  #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)); + +		sysfs_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_debug("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 @@ -725,6 +779,7 @@ const struct sysfs_ops kobj_sysfs_ops = {  	.show	= kobj_attr_show,  	.store	= kobj_attr_store,  }; +EXPORT_SYMBOL_GPL(kobj_sysfs_ops);  /**   * kset_register - initialize and add a kset. @@ -753,6 +808,7 @@ void kset_unregister(struct kset *k)  {  	if (!k)  		return; +	kobject_del(&k->kobj);  	kobject_put(&k->kobj);  } @@ -933,10 +989,7 @@ const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj)  bool kobj_ns_current_may_mount(enum kobj_ns_type type)  { -	bool may_mount = false; - -	if (type == KOBJ_NS_TYPE_NONE) -		return true; +	bool may_mount = true;  	spin_lock(&kobj_ns_type_lock);  	if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) &&  | 
