[PATCH] module: add modinfo support for all built-in modules

From: Denis Cheng
Date: Fri Jan 18 2008 - 03:37:35 EST


the current modinfo support is for external modules only, it provided module
information under /sys/module/<XYZ>/, such as verion, ...;
now some application(such as iscsid of open-iscsi) has been designed to
use this module information; but built-in modules don't have modinfo support,
so these apps would break if modules they depend on are compiled built-in.

this patch add modinfo support for all built-in modules, so now no matter
whether modules they depends on are built-in or external, modules' information
could always be accessed from /sys/module/<XYZ>/version, apps won't break.

Signed-off-by: Denis Cheng <crquan@xxxxxxxxx>
---
include/asm-generic/vmlinux.lds.h | 7 ++
include/linux/moduleparam.h | 18 ++++-
kernel/module.c | 147 +++++++++++++++++++++++++++++++++++++
3 files changed, 170 insertions(+), 2 deletions(-)

diff --git a/include/asm-generic/vmlinux.lds.h
b/include/asm-generic/vmlinux.lds.h
index 9f584cc..896f0fe 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -137,6 +137,13 @@
VMLINUX_SYMBOL(__start___param) = .; \
*(__param) \
VMLINUX_SYMBOL(__stop___param) = .; \
+ } \
+ \
+ /* Built-in module information. */ \
+ .modinfo : AT(ADDR(.modinfo) - LOAD_OFFSET) { \
+ VMLINUX_SYMBOL(__start___modinfo) = .; \
+ *(.modinfo) \
+ VMLINUX_SYMBOL(__stop___modinfo) = .; \
VMLINUX_SYMBOL(__end_rodata) = .; \
} \
\
diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
index 13410b2..86ddbd4 100644
--- a/include/linux/moduleparam.h
+++ b/include/linux/moduleparam.h
@@ -13,16 +13,30 @@
#define MODULE_PARAM_PREFIX KBUILD_MODNAME "."
#endif

-#ifdef MODULE
#define ___module_cat(a,b) __mod_ ## a ## b
#define __module_cat(a,b) ___module_cat(a,b)
+
+#ifdef MODULE
#define __MODULE_INFO(tag, name, info) \
static const char __module_cat(name,__LINE__)[] \
__attribute_used__ \
__attribute__((section(".modinfo"),unused)) = __stringify(tag) "=" info
#else /* !MODULE */
-#define __MODULE_INFO(tag, name, info)
+struct kernel_modinfo {
+ char *modname;
+ char *tag;
+ char *info;
+};
+#define __MODULE_INFO(tag, name, info) \
+static const struct kernel_modinfo __module_cat(name, __LINE__) \
+ __attribute_used__ \
+ __attribute__((section(".modinfo"), unused)) = \
+ { KBUILD_MODNAME, __stringify(tag), info }
+
+extern const struct kernel_modinfo __start___modinfo[], __stop___modinfo[];
+
#endif
+
#define __MODULE_PARM_TYPE(name, _type) \
__MODULE_INFO(parmtype, name##type, #name ":" _type)

diff --git a/kernel/module.c b/kernel/module.c
index c2e3e2e..9828918 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2609,3 +2609,150 @@ void module_update_markers(struct module
*probe_module, int *refcount)
mutex_unlock(&module_mutex);
}
#endif
+
+#ifdef CONFIG_SYSFS
+struct modinfo_attribute {
+ struct module_attribute mattr;
+ const struct kernel_modinfo *modinfo;
+};
+
+struct module_modinfo_attrs {
+ struct attribute_group grp;
+ struct modinfo_attribute attrs[0];
+};
+
+#define to_modinfo_attr(n) container_of(n, struct modinfo_attribute, mattr)
+
+static ssize_t modinfo_attr_show(struct module_attribute *mattr,
+ struct module *mod, char *buf)
+{
+ const struct kernel_modinfo *km = to_modinfo_attr(mattr)->modinfo;
+ return snprintf(buf, PAGE_SIZE, "%s\n", km->info);
+}
+
+/* skip the parameters' description in .modinfo */
+static __init int modinfo_skip(char *name)
+{
+ return (strlen(name) == 4 && !strncmp(name, "parm", 4)) ||
+ (strlen(name) == 8 && !strncmp(name, "parmtype", 8));
+}
+
+static __init void modinfo_sysfs_setup(struct module_kobject *mk,
+ const struct kernel_modinfo km_begin[],
+ unsigned int num_params)
+{
+ struct module_modinfo_attrs *mp;
+ unsigned int valid_attrs = 0;
+ unsigned int i, size[2];
+ struct modinfo_attribute *pattr;
+ struct attribute **gattr;
+
+ for (i = 0; i < num_params; ++i) {
+ if (!modinfo_skip(km_begin[i].tag))
+ valid_attrs++;
+ }
+
+ if (!valid_attrs)
+ return;
+
+ size[0] = ALIGN(sizeof(*mp) +
+ valid_attrs * sizeof(mp->attrs[0]),
+ sizeof(mp->grp.attrs[0]));
+ size[1] = (valid_attrs + 1) * sizeof(mp->grp.attrs[0]);
+
+ mp = kzalloc(size[0] + size[1], GFP_KERNEL);
+ if (!mp)
+ return;
+
+ mp->grp.attrs = (void *)mp + size[0];
+
+ pattr = &mp->attrs[0];
+ gattr = &mp->grp.attrs[0];
+ for (i = 0; i < valid_attrs; i++) {
+ const struct kernel_modinfo *km = &km_begin[i];
+ if (!modinfo_skip(km_begin[i].tag)) {
+ pattr->modinfo = km;
+ pattr->mattr.show = modinfo_attr_show;
+ pattr->mattr.attr.name = km->tag;
+ pattr->mattr.attr.mode = S_IRUGO;
+ *(gattr++) = &(pattr++)->mattr.attr;
+ }
+ }
+
+ if (sysfs_create_group(&mk->kobj, &mp->grp))
+ kfree(mp);
+}
+
+static void __init kernel_modinfo_sysfs_setup(const char *modname,
+ const struct kernel_modinfo km_begin[],
+ unsigned int num_params)
+{
+ struct kobject *mkobj;
+ struct module_kobject *mk;
+ int ret;
+
+ mkobj = kset_find_obj(&module_subsys, modname);
+
+ if (mkobj)
+ mk = container_of(mkobj, struct module_kobject, kobj);
+ else {
+ mk = kzalloc(sizeof(struct module_kobject), GFP_KERNEL);
+
+ kobj_set_kset_s(mk, module_subsys);
+ kobject_set_name(&mk->kobj, modname);
+ kobject_init(&mk->kobj);
+ ret = kobject_add(&mk->kobj);
+ if (ret) {
+ printk(KERN_ERR "Module '%s' failed to be added to"
+ " sysfs, error number %d\n",
+ modname, ret);
+ printk(KERN_ERR "The system will be unstable now.\n");
+ return;
+ }
+ }
+
+ modinfo_sysfs_setup(mk, km_begin, num_params);
+
+ if (mkobj) {
+ /* kset_find_obj incremental a reference on mkobj */
+ kobject_put(mkobj);
+ } else
+ kobject_uevent(&mk->kobj, KOBJ_ADD);
+}
+
+/*
+ * module_info_sysfs_init is for built-in modules
+ *
+ * __initcall("6") is later than subsys_init
+ */
+static int __init builtin_modinfo_sysfs_init(void)
+{
+ const struct kernel_modinfo *km, *km_begin = NULL;
+ unsigned int count = 0;
+ char *modname = NULL;
+
+ for (km = __start___modinfo; km < __stop___modinfo; km++) {
+ /* new kbuild_modname? */
+ if (km->modname != modname) {
+ /* add a new kobject for previous kernel_modinfo . */
+ if (count)
+ kernel_modinfo_sysfs_setup(modname,
+ km_begin,
+ count);
+
+ modname = km->modname;
+ count = 0;
+ km_begin = km;
+ }
+ count++;
+ }
+
+ /* last kernel_modinfo need to be registered as well */
+ if (count)
+ kernel_modinfo_sysfs_setup(modname, km_begin, count);
+
+ return 0;
+}
+__initcall(builtin_modinfo_sysfs_init);
+
+#endif
--
1.5.3.5

--
Denis Cheng
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/