diff options
| author | Dmitry Torokhov <dtor@vmware.com> | 2010-12-15 17:00:19 -0500 |
|---|---|---|
| committer | Rusty Russell <rusty@rustcorp.com.au> | 2011-01-23 23:02:51 -0500 |
| commit | e94965ed5beb23c6fabf7ed31f625e66d7ff28de (patch) | |
| tree | 842e4cab961b568bcb98d8ab80d7d399110598a4 | |
| parent | 1bae4ce27c9c90344f23c65ea6966c50ffeae2f5 (diff) | |
module: show version information for built-in modules in sysfs
Currently only drivers that are built as modules have their versions
shown in /sys/module/<module_name>/version, but this information might
also be useful for built-in drivers as well. This especially important
for drivers that do not define any parameters - such drivers, if
built-in, are completely invisible from userspace.
This patch changes MODULE_VERSION() macro so that in case when we are
compiling built-in module, version information is stored in a separate
section. Kernel then uses this data to create 'version' sysfs attribute
in the same fashion it creates attributes for module parameters.
Signed-off-by: Dmitry Torokhov <dtor@vmware.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
| -rw-r--r-- | include/asm-generic/vmlinux.lds.h | 7 | ||||
| -rw-r--r-- | include/linux/module.h | 27 | ||||
| -rw-r--r-- | kernel/params.c | 65 |
3 files changed, 88 insertions, 11 deletions
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 68649336c4ad..6ebb81030d2d 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h | |||
| @@ -364,6 +364,13 @@ | |||
| 364 | VMLINUX_SYMBOL(__start___param) = .; \ | 364 | VMLINUX_SYMBOL(__start___param) = .; \ |
| 365 | *(__param) \ | 365 | *(__param) \ |
| 366 | VMLINUX_SYMBOL(__stop___param) = .; \ | 366 | VMLINUX_SYMBOL(__stop___param) = .; \ |
| 367 | } \ | ||
| 368 | \ | ||
| 369 | /* Built-in module versions. */ \ | ||
| 370 | __modver : AT(ADDR(__modver) - LOAD_OFFSET) { \ | ||
| 371 | VMLINUX_SYMBOL(__start___modver) = .; \ | ||
| 372 | *(__modver) \ | ||
| 373 | VMLINUX_SYMBOL(__stop___modver) = .; \ | ||
| 367 | . = ALIGN((align)); \ | 374 | . = ALIGN((align)); \ |
| 368 | VMLINUX_SYMBOL(__end_rodata) = .; \ | 375 | VMLINUX_SYMBOL(__end_rodata) = .; \ |
| 369 | } \ | 376 | } \ |
diff --git a/include/linux/module.h b/include/linux/module.h index 8b17fd8c790d..50efcd3ae850 100644 --- a/include/linux/module.h +++ b/include/linux/module.h | |||
| @@ -58,6 +58,12 @@ struct module_attribute { | |||
| 58 | void (*free)(struct module *); | 58 | void (*free)(struct module *); |
| 59 | }; | 59 | }; |
| 60 | 60 | ||
| 61 | struct module_version_attribute { | ||
| 62 | struct module_attribute mattr; | ||
| 63 | const char *module_name; | ||
| 64 | const char *version; | ||
| 65 | }; | ||
| 66 | |||
| 61 | struct module_kobject | 67 | struct module_kobject |
| 62 | { | 68 | { |
| 63 | struct kobject kobj; | 69 | struct kobject kobj; |
| @@ -161,7 +167,28 @@ extern struct module __this_module; | |||
| 161 | Using this automatically adds a checksum of the .c files and the | 167 | Using this automatically adds a checksum of the .c files and the |
| 162 | local headers in "srcversion". | 168 | local headers in "srcversion". |
| 163 | */ | 169 | */ |
| 170 | |||
| 171 | #ifdef MODULE | ||
| 164 | #define MODULE_VERSION(_version) MODULE_INFO(version, _version) | 172 | #define MODULE_VERSION(_version) MODULE_INFO(version, _version) |
| 173 | #else | ||
| 174 | #define MODULE_VERSION(_version) \ | ||
| 175 | extern ssize_t __modver_version_show(struct module_attribute *, \ | ||
| 176 | struct module *, char *); \ | ||
| 177 | static struct module_version_attribute __modver_version_attr \ | ||
| 178 | __used \ | ||
| 179 | __attribute__ ((__section__ ("__modver"),aligned(sizeof(void *)))) \ | ||
| 180 | = { \ | ||
| 181 | .mattr = { \ | ||
| 182 | .attr = { \ | ||
| 183 | .name = "version", \ | ||
| 184 | .mode = S_IRUGO, \ | ||
| 185 | }, \ | ||
| 186 | .show = __modver_version_show, \ | ||
| 187 | }, \ | ||
| 188 | .module_name = KBUILD_MODNAME, \ | ||
| 189 | .version = _version, \ | ||
| 190 | } | ||
| 191 | #endif | ||
| 165 | 192 | ||
| 166 | /* Optional firmware file (or files) needed by the module | 193 | /* Optional firmware file (or files) needed by the module |
| 167 | * format is simply firmware file name. Multiple firmware | 194 | * format is simply firmware file name. Multiple firmware |
diff --git a/kernel/params.c b/kernel/params.c index 08107d181758..0da1411222b9 100644 --- a/kernel/params.c +++ b/kernel/params.c | |||
| @@ -719,9 +719,7 @@ void destroy_params(const struct kernel_param *params, unsigned num) | |||
| 719 | params[i].ops->free(params[i].arg); | 719 | params[i].ops->free(params[i].arg); |
| 720 | } | 720 | } |
| 721 | 721 | ||
| 722 | static void __init kernel_add_sysfs_param(const char *name, | 722 | static struct module_kobject * __init locate_module_kobject(const char *name) |
| 723 | struct kernel_param *kparam, | ||
| 724 | unsigned int name_skip) | ||
| 725 | { | 723 | { |
| 726 | struct module_kobject *mk; | 724 | struct module_kobject *mk; |
| 727 | struct kobject *kobj; | 725 | struct kobject *kobj; |
| @@ -729,10 +727,7 @@ static void __init kernel_add_sysfs_param(const char *name, | |||
| 729 | 727 | ||
| 730 | kobj = kset_find_obj(module_kset, name); | 728 | kobj = kset_find_obj(module_kset, name); |
| 731 | if (kobj) { | 729 | if (kobj) { |
| 732 | /* We already have one. Remove params so we can add more. */ | ||
| 733 | mk = to_module_kobject(kobj); | 730 | mk = to_module_kobject(kobj); |
| 734 | /* We need to remove it before adding parameters. */ | ||
| 735 | sysfs_remove_group(&mk->kobj, &mk->mp->grp); | ||
| 736 | } else { | 731 | } else { |
| 737 | mk = kzalloc(sizeof(struct module_kobject), GFP_KERNEL); | 732 | mk = kzalloc(sizeof(struct module_kobject), GFP_KERNEL); |
| 738 | BUG_ON(!mk); | 733 | BUG_ON(!mk); |
| @@ -743,15 +738,36 @@ static void __init kernel_add_sysfs_param(const char *name, | |||
| 743 | "%s", name); | 738 | "%s", name); |
| 744 | if (err) { | 739 | if (err) { |
| 745 | kobject_put(&mk->kobj); | 740 | kobject_put(&mk->kobj); |
| 746 | printk(KERN_ERR "Module '%s' failed add to sysfs, " | 741 | printk(KERN_ERR |
| 747 | "error number %d\n", name, err); | 742 | "Module '%s' failed add to sysfs, error number %d\n", |
| 748 | printk(KERN_ERR "The system will be unstable now.\n"); | 743 | name, err); |
| 749 | return; | 744 | printk(KERN_ERR |
| 745 | "The system will be unstable now.\n"); | ||
| 746 | return NULL; | ||
| 750 | } | 747 | } |
| 751 | /* So that exit path is even. */ | 748 | |
| 749 | /* So that we hold reference in both cases. */ | ||
| 752 | kobject_get(&mk->kobj); | 750 | kobject_get(&mk->kobj); |
| 753 | } | 751 | } |
| 754 | 752 | ||
| 753 | return mk; | ||
| 754 | } | ||
| 755 | |||
| 756 | static void __init kernel_add_sysfs_param(const char *name, | ||
| 757 | struct kernel_param *kparam, | ||
| 758 | unsigned int name_skip) | ||
| 759 | { | ||
| 760 | struct module_kobject *mk; | ||
| 761 | int err; | ||
| 762 | |||
| 763 | mk = locate_module_kobject(name); | ||
| 764 | if (!mk) | ||
| 765 | return; | ||
| 766 | |||
| 767 | /* We need to remove old parameters before adding more. */ | ||
| 768 | if (mk->mp) | ||
| 769 | sysfs_remove_group(&mk->kobj, &mk->mp->grp); | ||
| 770 | |||
| 755 | /* These should not fail at boot. */ | 771 | /* These should not fail at boot. */ |
| 756 | err = add_sysfs_param(mk, kparam, kparam->name + name_skip); | 772 | err = add_sysfs_param(mk, kparam, kparam->name + name_skip); |
| 757 | BUG_ON(err); | 773 | BUG_ON(err); |
| @@ -796,6 +812,32 @@ static void __init param_sysfs_builtin(void) | |||
| 796 | } | 812 | } |
| 797 | } | 813 | } |
| 798 | 814 | ||
| 815 | ssize_t __modver_version_show(struct module_attribute *mattr, | ||
| 816 | struct module *mod, char *buf) | ||
| 817 | { | ||
| 818 | struct module_version_attribute *vattr = | ||
| 819 | container_of(mattr, struct module_version_attribute, mattr); | ||
| 820 | |||
| 821 | return sprintf(buf, "%s\n", vattr->version); | ||
| 822 | } | ||
| 823 | |||
| 824 | extern struct module_version_attribute __start___modver[], __stop___modver[]; | ||
| 825 | |||
| 826 | static void __init version_sysfs_builtin(void) | ||
| 827 | { | ||
| 828 | const struct module_version_attribute *vattr; | ||
| 829 | struct module_kobject *mk; | ||
| 830 | int err; | ||
| 831 | |||
| 832 | for (vattr = __start___modver; vattr < __stop___modver; vattr++) { | ||
| 833 | mk = locate_module_kobject(vattr->module_name); | ||
| 834 | if (mk) { | ||
| 835 | err = sysfs_create_file(&mk->kobj, &vattr->mattr.attr); | ||
| 836 | kobject_uevent(&mk->kobj, KOBJ_ADD); | ||
| 837 | kobject_put(&mk->kobj); | ||
| 838 | } | ||
| 839 | } | ||
| 840 | } | ||
| 799 | 841 | ||
| 800 | /* module-related sysfs stuff */ | 842 | /* module-related sysfs stuff */ |
| 801 | 843 | ||
| @@ -875,6 +917,7 @@ static int __init param_sysfs_init(void) | |||
| 875 | } | 917 | } |
| 876 | module_sysfs_initialized = 1; | 918 | module_sysfs_initialized = 1; |
| 877 | 919 | ||
| 920 | version_sysfs_builtin(); | ||
| 878 | param_sysfs_builtin(); | 921 | param_sysfs_builtin(); |
| 879 | 922 | ||
| 880 | return 0; | 923 | return 0; |
