forked from mirrors/linux
		
	kobj: Add basic infrastructure for dealing with namespaces.
Move complete knowledge of namespaces into the kobject layer so we can use that information when reporting kobjects to userspace. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
		
							parent
							
								
									ba514a57f5
								
							
						
					
					
						commit
						bc451f2058
					
				
					 5 changed files with 204 additions and 14 deletions
				
			
		|  | @ -63,6 +63,14 @@ static void class_release(struct kobject *kobj) | ||||||
| 	kfree(cp); | 	kfree(cp); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static const struct kobj_ns_type_operations *class_child_ns_type(struct kobject *kobj) | ||||||
|  | { | ||||||
|  | 	struct class_private *cp = to_class(kobj); | ||||||
|  | 	struct class *class = cp->class; | ||||||
|  | 
 | ||||||
|  | 	return class->ns_type; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static const struct sysfs_ops class_sysfs_ops = { | static const struct sysfs_ops class_sysfs_ops = { | ||||||
| 	.show	= class_attr_show, | 	.show	= class_attr_show, | ||||||
| 	.store	= class_attr_store, | 	.store	= class_attr_store, | ||||||
|  | @ -71,6 +79,7 @@ static const struct sysfs_ops class_sysfs_ops = { | ||||||
| static struct kobj_type class_ktype = { | static struct kobj_type class_ktype = { | ||||||
| 	.sysfs_ops	= &class_sysfs_ops, | 	.sysfs_ops	= &class_sysfs_ops, | ||||||
| 	.release	= class_release, | 	.release	= class_release, | ||||||
|  | 	.child_ns_type	= class_child_ns_type, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /* Hotplug events for classes go to the class class_subsys */ | /* Hotplug events for classes go to the class class_subsys */ | ||||||
|  |  | ||||||
|  | @ -131,9 +131,21 @@ static void device_release(struct kobject *kobj) | ||||||
| 	kfree(p); | 	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 = { | static struct kobj_type device_ktype = { | ||||||
| 	.release	= device_release, | 	.release	= device_release, | ||||||
| 	.sysfs_ops	= &dev_sysfs_ops, | 	.sysfs_ops	= &dev_sysfs_ops, | ||||||
|  | 	.namespace	= device_namespace, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -595,11 +607,59 @@ static struct kobject *virtual_device_parent(struct device *dev) | ||||||
| 	return virtual_dir; | 	return virtual_dir; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 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, | static struct kobject *get_device_parent(struct device *dev, | ||||||
| 					 struct device *parent) | 					 struct device *parent) | ||||||
| { | { | ||||||
| 	int retval; |  | ||||||
| 
 |  | ||||||
| 	if (dev->class) { | 	if (dev->class) { | ||||||
| 		static DEFINE_MUTEX(gdp_mutex); | 		static DEFINE_MUTEX(gdp_mutex); | ||||||
| 		struct kobject *kobj = NULL; | 		struct kobject *kobj = NULL; | ||||||
|  | @ -634,18 +694,7 @@ static struct kobject *get_device_parent(struct device *dev, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		/* or create a new class-directory at the parent device */ | 		/* or create a new class-directory at the parent device */ | ||||||
| 		k = kobject_create(); | 		k = class_dir_create_and_add(dev->class, parent_kobj); | ||||||
| 		if (!k) { |  | ||||||
| 			mutex_unlock(&gdp_mutex); |  | ||||||
| 			return NULL; |  | ||||||
| 		} |  | ||||||
| 		k->kset = &dev->class->p->class_dirs; |  | ||||||
| 		retval = kobject_add(k, parent_kobj, "%s", dev->class->name); |  | ||||||
| 		if (retval < 0) { |  | ||||||
| 			mutex_unlock(&gdp_mutex); |  | ||||||
| 			kobject_put(k); |  | ||||||
| 			return NULL; |  | ||||||
| 		} |  | ||||||
| 		/* do not emit an uevent for this simple "glue" directory */ | 		/* do not emit an uevent for this simple "glue" directory */ | ||||||
| 		mutex_unlock(&gdp_mutex); | 		mutex_unlock(&gdp_mutex); | ||||||
| 		return k; | 		return k; | ||||||
|  |  | ||||||
|  | @ -202,6 +202,9 @@ struct class { | ||||||
| 	int (*suspend)(struct device *dev, pm_message_t state); | 	int (*suspend)(struct device *dev, pm_message_t state); | ||||||
| 	int (*resume)(struct device *dev); | 	int (*resume)(struct device *dev); | ||||||
| 
 | 
 | ||||||
|  | 	const struct kobj_ns_type_operations *ns_type; | ||||||
|  | 	const void *(*namespace)(struct device *dev); | ||||||
|  | 
 | ||||||
| 	const struct dev_pm_ops *pm; | 	const struct dev_pm_ops *pm; | ||||||
| 
 | 
 | ||||||
| 	struct class_private *p; | 	struct class_private *p; | ||||||
|  |  | ||||||
|  | @ -108,6 +108,8 @@ struct kobj_type { | ||||||
| 	void (*release)(struct kobject *kobj); | 	void (*release)(struct kobject *kobj); | ||||||
| 	const struct sysfs_ops *sysfs_ops; | 	const struct sysfs_ops *sysfs_ops; | ||||||
| 	struct attribute **default_attrs; | 	struct attribute **default_attrs; | ||||||
|  | 	const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); | ||||||
|  | 	const void *(*namespace)(struct kobject *kobj); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct kobj_uevent_env { | struct kobj_uevent_env { | ||||||
|  | @ -134,6 +136,30 @@ struct kobj_attribute { | ||||||
| 
 | 
 | ||||||
| extern const struct sysfs_ops kobj_sysfs_ops; | extern const struct sysfs_ops kobj_sysfs_ops; | ||||||
| 
 | 
 | ||||||
|  | enum kobj_ns_type { | ||||||
|  | 	KOBJ_NS_TYPE_NONE = 0, | ||||||
|  | 	KOBJ_NS_TYPES | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct sock; | ||||||
|  | struct kobj_ns_type_operations { | ||||||
|  | 	enum kobj_ns_type type; | ||||||
|  | 	const void *(*current_ns)(void); | ||||||
|  | 	const void *(*netlink_ns)(struct sock *sk); | ||||||
|  | 	const void *(*initial_ns)(void); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | int kobj_ns_type_register(const struct kobj_ns_type_operations *ops); | ||||||
|  | int kobj_ns_type_registered(enum kobj_ns_type type); | ||||||
|  | const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent); | ||||||
|  | const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj); | ||||||
|  | 
 | ||||||
|  | const void *kobj_ns_current(enum kobj_ns_type type); | ||||||
|  | const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk); | ||||||
|  | const void *kobj_ns_initial(enum kobj_ns_type type); | ||||||
|  | void kobj_ns_exit(enum kobj_ns_type type, const void *ns); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem. |  * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem. | ||||||
|  * |  * | ||||||
|  |  | ||||||
							
								
								
									
										103
									
								
								lib/kobject.c
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								lib/kobject.c
									
									
									
									
									
								
							|  | @ -850,6 +850,109 @@ struct kset *kset_create_and_add(const char *name, | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(kset_create_and_add); | EXPORT_SYMBOL_GPL(kset_create_and_add); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | static DEFINE_SPINLOCK(kobj_ns_type_lock); | ||||||
|  | static const struct kobj_ns_type_operations *kobj_ns_ops_tbl[KOBJ_NS_TYPES]; | ||||||
|  | 
 | ||||||
|  | int kobj_ns_type_register(const struct kobj_ns_type_operations *ops) | ||||||
|  | { | ||||||
|  | 	enum kobj_ns_type type = ops->type; | ||||||
|  | 	int error; | ||||||
|  | 
 | ||||||
|  | 	spin_lock(&kobj_ns_type_lock); | ||||||
|  | 
 | ||||||
|  | 	error = -EINVAL; | ||||||
|  | 	if (type >= KOBJ_NS_TYPES) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	error = -EINVAL; | ||||||
|  | 	if (type <= KOBJ_NS_TYPE_NONE) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	error = -EBUSY; | ||||||
|  | 	if (kobj_ns_ops_tbl[type]) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	error = 0; | ||||||
|  | 	kobj_ns_ops_tbl[type] = ops; | ||||||
|  | 
 | ||||||
|  | out: | ||||||
|  | 	spin_unlock(&kobj_ns_type_lock); | ||||||
|  | 	return error; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int kobj_ns_type_registered(enum kobj_ns_type type) | ||||||
|  | { | ||||||
|  | 	int registered = 0; | ||||||
|  | 
 | ||||||
|  | 	spin_lock(&kobj_ns_type_lock); | ||||||
|  | 	if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES)) | ||||||
|  | 		registered = kobj_ns_ops_tbl[type] != NULL; | ||||||
|  | 	spin_unlock(&kobj_ns_type_lock); | ||||||
|  | 
 | ||||||
|  | 	return registered; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent) | ||||||
|  | { | ||||||
|  | 	const struct kobj_ns_type_operations *ops = NULL; | ||||||
|  | 
 | ||||||
|  | 	if (parent && parent->ktype->child_ns_type) | ||||||
|  | 		ops = parent->ktype->child_ns_type(parent); | ||||||
|  | 
 | ||||||
|  | 	return ops; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj) | ||||||
|  | { | ||||||
|  | 	return kobj_child_ns_ops(kobj->parent); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | const void *kobj_ns_current(enum kobj_ns_type type) | ||||||
|  | { | ||||||
|  | 	const void *ns = NULL; | ||||||
|  | 
 | ||||||
|  | 	spin_lock(&kobj_ns_type_lock); | ||||||
|  | 	if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && | ||||||
|  | 	    kobj_ns_ops_tbl[type]) | ||||||
|  | 		ns = kobj_ns_ops_tbl[type]->current_ns(); | ||||||
|  | 	spin_unlock(&kobj_ns_type_lock); | ||||||
|  | 
 | ||||||
|  | 	return ns; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk) | ||||||
|  | { | ||||||
|  | 	const void *ns = NULL; | ||||||
|  | 
 | ||||||
|  | 	spin_lock(&kobj_ns_type_lock); | ||||||
|  | 	if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && | ||||||
|  | 	    kobj_ns_ops_tbl[type]) | ||||||
|  | 		ns = kobj_ns_ops_tbl[type]->netlink_ns(sk); | ||||||
|  | 	spin_unlock(&kobj_ns_type_lock); | ||||||
|  | 
 | ||||||
|  | 	return ns; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const void *kobj_ns_initial(enum kobj_ns_type type) | ||||||
|  | { | ||||||
|  | 	const void *ns = NULL; | ||||||
|  | 
 | ||||||
|  | 	spin_lock(&kobj_ns_type_lock); | ||||||
|  | 	if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && | ||||||
|  | 	    kobj_ns_ops_tbl[type]) | ||||||
|  | 		ns = kobj_ns_ops_tbl[type]->initial_ns(); | ||||||
|  | 	spin_unlock(&kobj_ns_type_lock); | ||||||
|  | 
 | ||||||
|  | 	return ns; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void kobj_ns_exit(enum kobj_ns_type type, const void *ns) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| EXPORT_SYMBOL(kobject_get); | EXPORT_SYMBOL(kobject_get); | ||||||
| EXPORT_SYMBOL(kobject_put); | EXPORT_SYMBOL(kobject_put); | ||||||
| EXPORT_SYMBOL(kobject_del); | EXPORT_SYMBOL(kobject_del); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Eric W. Biederman
						Eric W. Biederman