diff options
-rw-r--r-- | drivers/base/class.c | 9 | ||||
-rw-r--r-- | drivers/base/core.c | 77 | ||||
-rw-r--r-- | include/linux/device.h | 3 | ||||
-rw-r--r-- | include/linux/kobject.h | 26 | ||||
-rw-r--r-- | lib/kobject.c | 103 |
5 files changed, 204 insertions, 14 deletions
diff --git a/drivers/base/class.c b/drivers/base/class.c index 9c6a0d6408e7..8e231d05b400 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c | |||
@@ -63,6 +63,14 @@ static void class_release(struct kobject *kobj) | |||
63 | kfree(cp); | 63 | kfree(cp); |
64 | } | 64 | } |
65 | 65 | ||
66 | static const struct kobj_ns_type_operations *class_child_ns_type(struct kobject *kobj) | ||
67 | { | ||
68 | struct class_private *cp = to_class(kobj); | ||
69 | struct class *class = cp->class; | ||
70 | |||
71 | return class->ns_type; | ||
72 | } | ||
73 | |||
66 | static const struct sysfs_ops class_sysfs_ops = { | 74 | static const struct sysfs_ops class_sysfs_ops = { |
67 | .show = class_attr_show, | 75 | .show = class_attr_show, |
68 | .store = class_attr_store, | 76 | .store = class_attr_store, |
@@ -71,6 +79,7 @@ static const struct sysfs_ops class_sysfs_ops = { | |||
71 | static struct kobj_type class_ktype = { | 79 | static struct kobj_type class_ktype = { |
72 | .sysfs_ops = &class_sysfs_ops, | 80 | .sysfs_ops = &class_sysfs_ops, |
73 | .release = class_release, | 81 | .release = class_release, |
82 | .child_ns_type = class_child_ns_type, | ||
74 | }; | 83 | }; |
75 | 84 | ||
76 | /* Hotplug events for classes go to the class class_subsys */ | 85 | /* Hotplug events for classes go to the class class_subsys */ |
diff --git a/drivers/base/core.c b/drivers/base/core.c index 356dd011b8f9..f0699918e2f6 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -131,9 +131,21 @@ static void device_release(struct kobject *kobj) | |||
131 | kfree(p); | 131 | kfree(p); |
132 | } | 132 | } |
133 | 133 | ||
134 | static const void *device_namespace(struct kobject *kobj) | ||
135 | { | ||
136 | struct device *dev = to_dev(kobj); | ||
137 | const void *ns = NULL; | ||
138 | |||
139 | if (dev->class && dev->class->ns_type) | ||
140 | ns = dev->class->namespace(dev); | ||
141 | |||
142 | return ns; | ||
143 | } | ||
144 | |||
134 | static struct kobj_type device_ktype = { | 145 | static struct kobj_type device_ktype = { |
135 | .release = device_release, | 146 | .release = device_release, |
136 | .sysfs_ops = &dev_sysfs_ops, | 147 | .sysfs_ops = &dev_sysfs_ops, |
148 | .namespace = device_namespace, | ||
137 | }; | 149 | }; |
138 | 150 | ||
139 | 151 | ||
@@ -595,11 +607,59 @@ static struct kobject *virtual_device_parent(struct device *dev) | |||
595 | return virtual_dir; | 607 | return virtual_dir; |
596 | } | 608 | } |
597 | 609 | ||
598 | static struct kobject *get_device_parent(struct device *dev, | 610 | struct class_dir { |
599 | struct device *parent) | 611 | struct kobject kobj; |
612 | struct class *class; | ||
613 | }; | ||
614 | |||
615 | #define to_class_dir(obj) container_of(obj, struct class_dir, kobj) | ||
616 | |||
617 | static void class_dir_release(struct kobject *kobj) | ||
618 | { | ||
619 | struct class_dir *dir = to_class_dir(kobj); | ||
620 | kfree(dir); | ||
621 | } | ||
622 | |||
623 | static const | ||
624 | struct kobj_ns_type_operations *class_dir_child_ns_type(struct kobject *kobj) | ||
600 | { | 625 | { |
626 | struct class_dir *dir = to_class_dir(kobj); | ||
627 | return dir->class->ns_type; | ||
628 | } | ||
629 | |||
630 | static struct kobj_type class_dir_ktype = { | ||
631 | .release = class_dir_release, | ||
632 | .sysfs_ops = &kobj_sysfs_ops, | ||
633 | .child_ns_type = class_dir_child_ns_type | ||
634 | }; | ||
635 | |||
636 | static struct kobject * | ||
637 | class_dir_create_and_add(struct class *class, struct kobject *parent_kobj) | ||
638 | { | ||
639 | struct class_dir *dir; | ||
601 | int retval; | 640 | int retval; |
602 | 641 | ||
642 | dir = kzalloc(sizeof(*dir), GFP_KERNEL); | ||
643 | if (!dir) | ||
644 | return NULL; | ||
645 | |||
646 | dir->class = class; | ||
647 | kobject_init(&dir->kobj, &class_dir_ktype); | ||
648 | |||
649 | dir->kobj.kset = &class->p->class_dirs; | ||
650 | |||
651 | retval = kobject_add(&dir->kobj, parent_kobj, "%s", class->name); | ||
652 | if (retval < 0) { | ||
653 | kobject_put(&dir->kobj); | ||
654 | return NULL; | ||
655 | } | ||
656 | return &dir->kobj; | ||
657 | } | ||
658 | |||
659 | |||
660 | static struct kobject *get_device_parent(struct device *dev, | ||
661 | struct device *parent) | ||
662 | { | ||
603 | if (dev->class) { | 663 | if (dev->class) { |
604 | static DEFINE_MUTEX(gdp_mutex); | 664 | static DEFINE_MUTEX(gdp_mutex); |
605 | struct kobject *kobj = NULL; | 665 | struct kobject *kobj = NULL; |
@@ -634,18 +694,7 @@ static struct kobject *get_device_parent(struct device *dev, | |||
634 | } | 694 | } |
635 | 695 | ||
636 | /* or create a new class-directory at the parent device */ | 696 | /* or create a new class-directory at the parent device */ |
637 | k = kobject_create(); | 697 | k = class_dir_create_and_add(dev->class, parent_kobj); |
638 | if (!k) { | ||
639 | mutex_unlock(&gdp_mutex); | ||
640 | return NULL; | ||
641 | } | ||
642 | k->kset = &dev->class->p->class_dirs; | ||
643 | retval = kobject_add(k, parent_kobj, "%s", dev->class->name); | ||
644 | if (retval < 0) { | ||
645 | mutex_unlock(&gdp_mutex); | ||
646 | kobject_put(k); | ||
647 | return NULL; | ||
648 | } | ||
649 | /* do not emit an uevent for this simple "glue" directory */ | 698 | /* do not emit an uevent for this simple "glue" directory */ |
650 | mutex_unlock(&gdp_mutex); | 699 | mutex_unlock(&gdp_mutex); |
651 | return k; | 700 | return k; |
diff --git a/include/linux/device.h b/include/linux/device.h index 6f9619190aaf..7bb9f426f3e6 100644 --- a/include/linux/device.h +++ b/include/linux/device.h | |||
@@ -202,6 +202,9 @@ struct class { | |||
202 | int (*suspend)(struct device *dev, pm_message_t state); | 202 | int (*suspend)(struct device *dev, pm_message_t state); |
203 | int (*resume)(struct device *dev); | 203 | int (*resume)(struct device *dev); |
204 | 204 | ||
205 | const struct kobj_ns_type_operations *ns_type; | ||
206 | const void *(*namespace)(struct device *dev); | ||
207 | |||
205 | const struct dev_pm_ops *pm; | 208 | const struct dev_pm_ops *pm; |
206 | 209 | ||
207 | struct class_private *p; | 210 | struct class_private *p; |
diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 3950d3c2850d..d9456f69904f 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h | |||
@@ -108,6 +108,8 @@ struct kobj_type { | |||
108 | void (*release)(struct kobject *kobj); | 108 | void (*release)(struct kobject *kobj); |
109 | const struct sysfs_ops *sysfs_ops; | 109 | const struct sysfs_ops *sysfs_ops; |
110 | struct attribute **default_attrs; | 110 | struct attribute **default_attrs; |
111 | const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); | ||
112 | const void *(*namespace)(struct kobject *kobj); | ||
111 | }; | 113 | }; |
112 | 114 | ||
113 | struct kobj_uevent_env { | 115 | struct kobj_uevent_env { |
@@ -134,6 +136,30 @@ struct kobj_attribute { | |||
134 | 136 | ||
135 | extern const struct sysfs_ops kobj_sysfs_ops; | 137 | extern const struct sysfs_ops kobj_sysfs_ops; |
136 | 138 | ||
139 | enum kobj_ns_type { | ||
140 | KOBJ_NS_TYPE_NONE = 0, | ||
141 | KOBJ_NS_TYPES | ||
142 | }; | ||
143 | |||
144 | struct sock; | ||
145 | struct kobj_ns_type_operations { | ||
146 | enum kobj_ns_type type; | ||
147 | const void *(*current_ns)(void); | ||
148 | const void *(*netlink_ns)(struct sock *sk); | ||
149 | const void *(*initial_ns)(void); | ||
150 | }; | ||
151 | |||
152 | int kobj_ns_type_register(const struct kobj_ns_type_operations *ops); | ||
153 | int kobj_ns_type_registered(enum kobj_ns_type type); | ||
154 | const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent); | ||
155 | const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj); | ||
156 | |||
157 | const void *kobj_ns_current(enum kobj_ns_type type); | ||
158 | const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk); | ||
159 | const void *kobj_ns_initial(enum kobj_ns_type type); | ||
160 | void kobj_ns_exit(enum kobj_ns_type type, const void *ns); | ||
161 | |||
162 | |||
137 | /** | 163 | /** |
138 | * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem. | 164 | * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem. |
139 | * | 165 | * |
diff --git a/lib/kobject.c b/lib/kobject.c index 8115eb1bbf4d..bbb2bb40ee1f 100644 --- a/lib/kobject.c +++ b/lib/kobject.c | |||
@@ -850,6 +850,109 @@ struct kset *kset_create_and_add(const char *name, | |||
850 | } | 850 | } |
851 | EXPORT_SYMBOL_GPL(kset_create_and_add); | 851 | EXPORT_SYMBOL_GPL(kset_create_and_add); |
852 | 852 | ||
853 | |||
854 | static DEFINE_SPINLOCK(kobj_ns_type_lock); | ||
855 | static const struct kobj_ns_type_operations *kobj_ns_ops_tbl[KOBJ_NS_TYPES]; | ||
856 | |||
857 | int kobj_ns_type_register(const struct kobj_ns_type_operations *ops) | ||
858 | { | ||
859 | enum kobj_ns_type type = ops->type; | ||
860 | int error; | ||
861 | |||
862 | spin_lock(&kobj_ns_type_lock); | ||
863 | |||
864 | error = -EINVAL; | ||
865 | if (type >= KOBJ_NS_TYPES) | ||
866 | goto out; | ||
867 | |||
868 | error = -EINVAL; | ||
869 | if (type <= KOBJ_NS_TYPE_NONE) | ||
870 | goto out; | ||
871 | |||
872 | error = -EBUSY; | ||
873 | if (kobj_ns_ops_tbl[type]) | ||
874 | goto out; | ||
875 | |||
876 | error = 0; | ||
877 | kobj_ns_ops_tbl[type] = ops; | ||
878 | |||
879 | out: | ||
880 | spin_unlock(&kobj_ns_type_lock); | ||
881 | return error; | ||
882 | } | ||
883 | |||
884 | int kobj_ns_type_registered(enum kobj_ns_type type) | ||
885 | { | ||
886 | int registered = 0; | ||
887 | |||
888 | spin_lock(&kobj_ns_type_lock); | ||
889 | if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES)) | ||
890 | registered = kobj_ns_ops_tbl[type] != NULL; | ||
891 | spin_unlock(&kobj_ns_type_lock); | ||
892 | |||
893 | return registered; | ||
894 | } | ||
895 | |||
896 | const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent) | ||
897 | { | ||
898 | const struct kobj_ns_type_operations *ops = NULL; | ||
899 | |||
900 | if (parent && parent->ktype->child_ns_type) | ||
901 | ops = parent->ktype->child_ns_type(parent); | ||
902 | |||
903 | return ops; | ||
904 | } | ||
905 | |||
906 | const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj) | ||
907 | { | ||
908 | return kobj_child_ns_ops(kobj->parent); | ||
909 | } | ||
910 | |||
911 | |||
912 | const void *kobj_ns_current(enum kobj_ns_type type) | ||
913 | { | ||
914 | const void *ns = NULL; | ||
915 | |||
916 | spin_lock(&kobj_ns_type_lock); | ||
917 | if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && | ||
918 | kobj_ns_ops_tbl[type]) | ||
919 | ns = kobj_ns_ops_tbl[type]->current_ns(); | ||
920 | spin_unlock(&kobj_ns_type_lock); | ||
921 | |||
922 | return ns; | ||
923 | } | ||
924 | |||
925 | const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk) | ||
926 | { | ||
927 | const void *ns = NULL; | ||
928 | |||
929 | spin_lock(&kobj_ns_type_lock); | ||
930 | if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && | ||
931 | kobj_ns_ops_tbl[type]) | ||
932 | ns = kobj_ns_ops_tbl[type]->netlink_ns(sk); | ||
933 | spin_unlock(&kobj_ns_type_lock); | ||
934 | |||
935 | return ns; | ||
936 | } | ||
937 | |||
938 | const void *kobj_ns_initial(enum kobj_ns_type type) | ||
939 | { | ||
940 | const void *ns = NULL; | ||
941 | |||
942 | spin_lock(&kobj_ns_type_lock); | ||
943 | if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && | ||
944 | kobj_ns_ops_tbl[type]) | ||
945 | ns = kobj_ns_ops_tbl[type]->initial_ns(); | ||
946 | spin_unlock(&kobj_ns_type_lock); | ||
947 | |||
948 | return ns; | ||
949 | } | ||
950 | |||
951 | void kobj_ns_exit(enum kobj_ns_type type, const void *ns) | ||
952 | { | ||
953 | } | ||
954 | |||
955 | |||
853 | EXPORT_SYMBOL(kobject_get); | 956 | EXPORT_SYMBOL(kobject_get); |
854 | EXPORT_SYMBOL(kobject_put); | 957 | EXPORT_SYMBOL(kobject_put); |
855 | EXPORT_SYMBOL(kobject_del); | 958 | EXPORT_SYMBOL(kobject_del); |