Commit e94965ed5beb23c6fabf7ed31f625e66d7ff28de
Committed by
Rusty Russell
1 parent
1bae4ce27c
Exists in
master
and in
39 other branches
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>
Showing 3 changed files with 88 additions and 11 deletions Side-by-side Diff
include/asm-generic/vmlinux.lds.h
... | ... | @@ -364,6 +364,13 @@ |
364 | 364 | VMLINUX_SYMBOL(__start___param) = .; \ |
365 | 365 | *(__param) \ |
366 | 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 | 374 | . = ALIGN((align)); \ |
368 | 375 | VMLINUX_SYMBOL(__end_rodata) = .; \ |
369 | 376 | } \ |
include/linux/module.h
... | ... | @@ -58,6 +58,12 @@ |
58 | 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 | 67 | struct module_kobject |
62 | 68 | { |
63 | 69 | struct kobject kobj; |
64 | 70 | |
... | ... | @@ -161,7 +167,28 @@ |
161 | 167 | Using this automatically adds a checksum of the .c files and the |
162 | 168 | local headers in "srcversion". |
163 | 169 | */ |
170 | + | |
171 | +#ifdef MODULE | |
164 | 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 | 193 | /* Optional firmware file (or files) needed by the module |
167 | 194 | * format is simply firmware file name. Multiple firmware |
kernel/params.c
... | ... | @@ -719,9 +719,7 @@ |
719 | 719 | params[i].ops->free(params[i].arg); |
720 | 720 | } |
721 | 721 | |
722 | -static void __init kernel_add_sysfs_param(const char *name, | |
723 | - struct kernel_param *kparam, | |
724 | - unsigned int name_skip) | |
722 | +static struct module_kobject * __init locate_module_kobject(const char *name) | |
725 | 723 | { |
726 | 724 | struct module_kobject *mk; |
727 | 725 | struct kobject *kobj; |
728 | 726 | |
... | ... | @@ -729,10 +727,7 @@ |
729 | 727 | |
730 | 728 | kobj = kset_find_obj(module_kset, name); |
731 | 729 | if (kobj) { |
732 | - /* We already have one. Remove params so we can add more. */ | |
733 | 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 | 731 | } else { |
737 | 732 | mk = kzalloc(sizeof(struct module_kobject), GFP_KERNEL); |
738 | 733 | BUG_ON(!mk); |
739 | 734 | |
740 | 735 | |
... | ... | @@ -743,15 +738,36 @@ |
743 | 738 | "%s", name); |
744 | 739 | if (err) { |
745 | 740 | kobject_put(&mk->kobj); |
746 | - printk(KERN_ERR "Module '%s' failed add to sysfs, " | |
747 | - "error number %d\n", name, err); | |
748 | - printk(KERN_ERR "The system will be unstable now.\n"); | |
749 | - return; | |
741 | + printk(KERN_ERR | |
742 | + "Module '%s' failed add to sysfs, error number %d\n", | |
743 | + name, err); | |
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 | 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 | 771 | /* These should not fail at boot. */ |
756 | 772 | err = add_sysfs_param(mk, kparam, kparam->name + name_skip); |
757 | 773 | BUG_ON(err); |
758 | 774 | |
... | ... | @@ -796,7 +812,33 @@ |
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); | |
799 | 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 | +} | |
841 | + | |
800 | 842 | /* module-related sysfs stuff */ |
801 | 843 | |
802 | 844 | static ssize_t module_attr_show(struct kobject *kobj, |
... | ... | @@ -875,6 +917,7 @@ |
875 | 917 | } |
876 | 918 | module_sysfs_initialized = 1; |
877 | 919 | |
920 | + version_sysfs_builtin(); | |
878 | 921 | param_sysfs_builtin(); |
879 | 922 | |
880 | 923 | return 0; |