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); |
