mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 08:38:45 +02:00 
			
		
		
		
	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); | ||||
| } | ||||
| 
 | ||||
| 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 = { | ||||
| 	.show	= class_attr_show, | ||||
| 	.store	= class_attr_store, | ||||
|  | @ -71,6 +79,7 @@ static const struct sysfs_ops class_sysfs_ops = { | |||
| static struct kobj_type class_ktype = { | ||||
| 	.sysfs_ops	= &class_sysfs_ops, | ||||
| 	.release	= class_release, | ||||
| 	.child_ns_type	= class_child_ns_type, | ||||
| }; | ||||
| 
 | ||||
| /* Hotplug events for classes go to the class class_subsys */ | ||||
|  |  | |||
|  | @ -131,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, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -595,11 +607,59 @@ static struct kobject *virtual_device_parent(struct device *dev) | |||
| 	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, | ||||
| 					 struct device *parent) | ||||
| { | ||||
| 	int retval; | ||||
| 
 | ||||
| 	if (dev->class) { | ||||
| 		static DEFINE_MUTEX(gdp_mutex); | ||||
| 		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 */ | ||||
| 		k = kobject_create(); | ||||
| 		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; | ||||
| 		} | ||||
| 		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; | ||||
|  |  | |||
|  | @ -202,6 +202,9 @@ struct class { | |||
| 	int (*suspend)(struct device *dev, pm_message_t state); | ||||
| 	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; | ||||
| 
 | ||||
| 	struct class_private *p; | ||||
|  |  | |||
|  | @ -108,6 +108,8 @@ struct kobj_type { | |||
| 	void (*release)(struct kobject *kobj); | ||||
| 	const struct sysfs_ops *sysfs_ops; | ||||
| 	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 { | ||||
|  | @ -134,6 +136,30 @@ struct kobj_attribute { | |||
| 
 | ||||
| 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. | ||||
|  * | ||||
|  |  | |||
							
								
								
									
										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); | ||||
| 
 | ||||
| 
 | ||||
| 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_put); | ||||
| EXPORT_SYMBOL(kobject_del); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Eric W. Biederman
						Eric W. Biederman