Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e94965ed authored by Dmitry Torokhov's avatar Dmitry Torokhov Committed by Rusty Russell
Browse files

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: default avatarDmitry Torokhov <dtor@vmware.com>
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 1bae4ce2
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -364,6 +364,13 @@
		VMLINUX_SYMBOL(__start___param) = .;			\
		VMLINUX_SYMBOL(__start___param) = .;			\
		*(__param)						\
		*(__param)						\
		VMLINUX_SYMBOL(__stop___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));					\
		. = ALIGN((align));					\
		VMLINUX_SYMBOL(__end_rodata) = .;			\
		VMLINUX_SYMBOL(__end_rodata) = .;			\
	}								\
	}								\
+27 −0
Original line number Original line Diff line number Diff line
@@ -58,6 +58,12 @@ struct module_attribute {
	void (*free)(struct module *);
	void (*free)(struct module *);
};
};


struct module_version_attribute {
	struct module_attribute mattr;
	const char *module_name;
	const char *version;
};

struct module_kobject
struct module_kobject
{
{
	struct kobject kobj;
	struct kobject kobj;
@@ -161,7 +167,28 @@ extern struct module __this_module;
  Using this automatically adds a checksum of the .c files and the
  Using this automatically adds a checksum of the .c files and the
  local headers in "srcversion".
  local headers in "srcversion".
*/
*/

#ifdef MODULE
#define MODULE_VERSION(_version) MODULE_INFO(version, _version)
#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
/* Optional firmware file (or files) needed by the module
 * format is simply firmware file name.  Multiple firmware
 * format is simply firmware file name.  Multiple firmware
+54 −11
Original line number Original line Diff line number Diff line
@@ -719,9 +719,7 @@ void destroy_params(const struct kernel_param *params, unsigned num)
			params[i].ops->free(params[i].arg);
			params[i].ops->free(params[i].arg);
}
}


static void __init kernel_add_sysfs_param(const char *name,
static struct module_kobject * __init locate_module_kobject(const char *name)
					  struct kernel_param *kparam,
					  unsigned int name_skip)
{
{
	struct module_kobject *mk;
	struct module_kobject *mk;
	struct kobject *kobj;
	struct kobject *kobj;
@@ -729,10 +727,7 @@ static void __init kernel_add_sysfs_param(const char *name,


	kobj = kset_find_obj(module_kset, name);
	kobj = kset_find_obj(module_kset, name);
	if (kobj) {
	if (kobj) {
		/* We already have one.  Remove params so we can add more. */
		mk = to_module_kobject(kobj);
		mk = to_module_kobject(kobj);
		/* We need to remove it before adding parameters. */
		sysfs_remove_group(&mk->kobj, &mk->mp->grp);
	} else {
	} else {
		mk = kzalloc(sizeof(struct module_kobject), GFP_KERNEL);
		mk = kzalloc(sizeof(struct module_kobject), GFP_KERNEL);
		BUG_ON(!mk);
		BUG_ON(!mk);
@@ -743,15 +738,36 @@ static void __init kernel_add_sysfs_param(const char *name,
					   "%s", name);
					   "%s", name);
		if (err) {
		if (err) {
			kobject_put(&mk->kobj);
			kobject_put(&mk->kobj);
			printk(KERN_ERR "Module '%s' failed add to sysfs, "
			printk(KERN_ERR
			       "error number %d\n", name, err);
				"Module '%s' failed add to sysfs, error number %d\n",
			printk(KERN_ERR	"The system will be unstable now.\n");
				name, err);
			return;
			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);
		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. */
	/* These should not fail at boot. */
	err = add_sysfs_param(mk, kparam, kparam->name + name_skip);
	err = add_sysfs_param(mk, kparam, kparam->name + name_skip);
	BUG_ON(err);
	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 */
/* module-related sysfs stuff */


@@ -875,6 +917,7 @@ static int __init param_sysfs_init(void)
	}
	}
	module_sysfs_initialized = 1;
	module_sysfs_initialized = 1;


	version_sysfs_builtin();
	param_sysfs_builtin();
	param_sysfs_builtin();


	return 0;
	return 0;