Commit e94965ed5beb23c6fabf7ed31f625e66d7ff28de

Authored by Dmitry Torokhov
Committed by Rusty Russell
1 parent 1bae4ce27c

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
... ... @@ -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;