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>
This commit is contained in:
Dmitry Torokhov 2010-12-15 14:00:19 -08:00 committed by Rusty Russell
parent 1bae4ce27c
commit e94965ed5b
3 changed files with 88 additions and 11 deletions

View File

@ -364,6 +364,13 @@
VMLINUX_SYMBOL(__start___param) = .; \
*(__param) \
VMLINUX_SYMBOL(__stop___param) = .; \
} \
\
/* Built-in module versions. */ \
__modver : AT(ADDR(__modver) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start___modver) = .; \
*(__modver) \
VMLINUX_SYMBOL(__stop___modver) = .; \
. = ALIGN((align)); \
VMLINUX_SYMBOL(__end_rodata) = .; \
} \

View File

@ -58,6 +58,12 @@ struct module_attribute {
void (*free)(struct module *);
};
struct module_version_attribute {
struct module_attribute mattr;
const char *module_name;
const char *version;
};
struct module_kobject
{
struct kobject kobj;
@ -161,7 +167,28 @@ extern struct module __this_module;
Using this automatically adds a checksum of the .c files and the
local headers in "srcversion".
*/
#ifdef MODULE
#define MODULE_VERSION(_version) MODULE_INFO(version, _version)
#else
#define MODULE_VERSION(_version) \
extern ssize_t __modver_version_show(struct module_attribute *, \
struct module *, char *); \
static struct module_version_attribute __modver_version_attr \
__used \
__attribute__ ((__section__ ("__modver"),aligned(sizeof(void *)))) \
= { \
.mattr = { \
.attr = { \
.name = "version", \
.mode = S_IRUGO, \
}, \
.show = __modver_version_show, \
}, \
.module_name = KBUILD_MODNAME, \
.version = _version, \
}
#endif
/* Optional firmware file (or files) needed by the module
* format is simply firmware file name. Multiple firmware

View File

@ -719,9 +719,7 @@ void destroy_params(const struct kernel_param *params, unsigned num)
params[i].ops->free(params[i].arg);
}
static void __init kernel_add_sysfs_param(const char *name,
struct kernel_param *kparam,
unsigned int name_skip)
static struct module_kobject * __init locate_module_kobject(const char *name)
{
struct module_kobject *mk;
struct kobject *kobj;
@ -729,10 +727,7 @@ static void __init kernel_add_sysfs_param(const char *name,
kobj = kset_find_obj(module_kset, name);
if (kobj) {
/* We already have one. Remove params so we can add more. */
mk = to_module_kobject(kobj);
/* We need to remove it before adding parameters. */
sysfs_remove_group(&mk->kobj, &mk->mp->grp);
} else {
mk = kzalloc(sizeof(struct module_kobject), GFP_KERNEL);
BUG_ON(!mk);
@ -743,15 +738,36 @@ static void __init kernel_add_sysfs_param(const char *name,
"%s", name);
if (err) {
kobject_put(&mk->kobj);
printk(KERN_ERR "Module '%s' failed add to sysfs, "
"error number %d\n", name, err);
printk(KERN_ERR "The system will be unstable now.\n");
return;
printk(KERN_ERR
"Module '%s' failed add to sysfs, error number %d\n",
name, err);
printk(KERN_ERR
"The system will be unstable now.\n");
return NULL;
}
/* So that exit path is even. */
/* So that we hold reference in both cases. */
kobject_get(&mk->kobj);
}
return mk;
}
static void __init kernel_add_sysfs_param(const char *name,
struct kernel_param *kparam,
unsigned int name_skip)
{
struct module_kobject *mk;
int err;
mk = locate_module_kobject(name);
if (!mk)
return;
/* We need to remove old parameters before adding more. */
if (mk->mp)
sysfs_remove_group(&mk->kobj, &mk->mp->grp);
/* These should not fail at boot. */
err = add_sysfs_param(mk, kparam, kparam->name + name_skip);
BUG_ON(err);
@ -796,6 +812,32 @@ static void __init param_sysfs_builtin(void)
}
}
ssize_t __modver_version_show(struct module_attribute *mattr,
struct module *mod, char *buf)
{
struct module_version_attribute *vattr =
container_of(mattr, struct module_version_attribute, mattr);
return sprintf(buf, "%s\n", vattr->version);
}
extern struct module_version_attribute __start___modver[], __stop___modver[];
static void __init version_sysfs_builtin(void)
{
const struct module_version_attribute *vattr;
struct module_kobject *mk;
int err;
for (vattr = __start___modver; vattr < __stop___modver; vattr++) {
mk = locate_module_kobject(vattr->module_name);
if (mk) {
err = sysfs_create_file(&mk->kobj, &vattr->mattr.attr);
kobject_uevent(&mk->kobj, KOBJ_ADD);
kobject_put(&mk->kobj);
}
}
}
/* module-related sysfs stuff */
@ -875,6 +917,7 @@ static int __init param_sysfs_init(void)
}
module_sysfs_initialized = 1;
version_sysfs_builtin();
param_sysfs_builtin();
return 0;