diff mbox series

[v1,2/2] modules: add scmversion field

Message ID 20201121011652.2006613-3-willmcvicker@google.com (mailing list archive)
State New, archived
Headers show
Series Add support to capture external module's SCM version | expand

Commit Message

William McVicker Nov. 21, 2020, 1:16 a.m. UTC
Add the modinfo field `scmversion` to include the SCM version of kernel
modules, e.g. git sha1. This allows one to identify the exact source
code version of a given kernel module.

You can retrieve it in two ways,

1) By using modinfo
    > modinfo -F scmversion <module_name>
2) By module sysfs node
    > cat /sys/module/<module_name>/scmversion

Signed-off-by: Will McVicker <willmcvicker@google.com>
---
 include/linux/module.h   |  1 +
 kernel/module.c          | 20 ++++++++++++++++++++
 scripts/Makefile.modpost | 19 +++++++++++++++++--
 scripts/mod/modpost.c    | 28 +++++++++++++++++++++++++++-
 4 files changed, 65 insertions(+), 3 deletions(-)

Comments

Greg KH Nov. 23, 2020, 9:30 a.m. UTC | #1
On Sat, Nov 21, 2020 at 01:16:51AM +0000, Will McVicker wrote:
> Add the modinfo field `scmversion` to include the SCM version of kernel
> modules, e.g. git sha1. This allows one to identify the exact source
> code version of a given kernel module.
> 
> You can retrieve it in two ways,
> 
> 1) By using modinfo
>     > modinfo -F scmversion <module_name>
> 2) By module sysfs node
>     > cat /sys/module/<module_name>/scmversion

I agree with Christoph that this doesn't help any in-kernel stuff, so I
don't think it can be merged.  Why not just build these modules as part
of the normal kernel build process, the Android build system should
allow that, right?

But even if it was ok, you are adding new sysfs attributes without a
Documentation/ABI/ update, which is not ok either, so always remember
that for future patches.

thanks,

greg k-h
Greg KH Nov. 23, 2020, 9:32 a.m. UTC | #2
On Sat, Nov 21, 2020 at 01:16:51AM +0000, Will McVicker wrote:
> +/**
> + * struct modinfo_attrs - Module attributes.
> + * @module_uevent: Used to notify udev of events.
> + * @modinfo_version: Module version.
> + * @modinfo_srcversion: Checksum of module source.
> + * @modinfo_scmversion: SCM version of module source.
> + * @modinfo_initstate: Module init state.
> + * @modinfo_coresize: Module core layout size.
> + * @modinfo_initsize: Module init layout size.
> + * @modinfo_taint: Indicates if the module is tainted.
> + * @modinfo_refcnt: Number of references in the kernel to the module.
> + *
> + * These are the module attributes accessible via the sysfs files
> + * /sys/module/<module_name>/<attribute>.
> + *
> + * The following subset of attributes can also be accessed via the modinfo tool
> + * as well: version, srcversion, and scmversion.
> + */
>  static struct module_attribute *modinfo_attrs[] = {
>  	&module_uevent,
>  	&modinfo_version,
>  	&modinfo_srcversion,
> +	&modinfo_scmversion,
>  	&modinfo_initstate,
>  	&modinfo_coresize,
>  	&modinfo_initsize,

This isn't the normal way to document an array, with kerneldoc, I don't
think I've seen that anywhere else in the kernel, have you?

Anyway, again, Documentation/ABI/ is the correct place for this.

thanks,

greg k-h
diff mbox series

Patch

diff --git a/include/linux/module.h b/include/linux/module.h
index 6264617bab4d..63137ca5147b 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -372,6 +372,7 @@  struct module {
 	struct module_attribute *modinfo_attrs;
 	const char *version;
 	const char *srcversion;
+	const char *scmversion;
 	struct kobject *holders_dir;
 
 	/* Exported symbols */
diff --git a/kernel/module.c b/kernel/module.c
index a4fa44a652a7..be155ec80083 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -807,6 +807,7 @@  static struct module_attribute modinfo_##field = {                    \
 
 MODINFO_ATTR(version);
 MODINFO_ATTR(srcversion);
+MODINFO_ATTR(scmversion);
 
 static char last_unloaded_module[MODULE_NAME_LEN+1];
 
@@ -1265,10 +1266,29 @@  static ssize_t show_taint(struct module_attribute *mattr,
 static struct module_attribute modinfo_taint =
 	__ATTR(taint, 0444, show_taint, NULL);
 
+/**
+ * struct modinfo_attrs - Module attributes.
+ * @module_uevent: Used to notify udev of events.
+ * @modinfo_version: Module version.
+ * @modinfo_srcversion: Checksum of module source.
+ * @modinfo_scmversion: SCM version of module source.
+ * @modinfo_initstate: Module init state.
+ * @modinfo_coresize: Module core layout size.
+ * @modinfo_initsize: Module init layout size.
+ * @modinfo_taint: Indicates if the module is tainted.
+ * @modinfo_refcnt: Number of references in the kernel to the module.
+ *
+ * These are the module attributes accessible via the sysfs files
+ * /sys/module/<module_name>/<attribute>.
+ *
+ * The following subset of attributes can also be accessed via the modinfo tool
+ * as well: version, srcversion, and scmversion.
+ */
 static struct module_attribute *modinfo_attrs[] = {
 	&module_uevent,
 	&modinfo_version,
 	&modinfo_srcversion,
+	&modinfo_scmversion,
 	&modinfo_initstate,
 	&modinfo_coresize,
 	&modinfo_initsize,
diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost
index f54b6ac37ac2..4486eb72240e 100644
--- a/scripts/Makefile.modpost
+++ b/scripts/Makefile.modpost
@@ -77,8 +77,23 @@  src := $(obj)
 include $(if $(wildcard $(KBUILD_EXTMOD)/Kbuild), \
              $(KBUILD_EXTMOD)/Kbuild, $(KBUILD_EXTMOD)/Makefile)
 
-# modpost option for external modules
-MODPOST += -e
+# Get the external module's source path. KBUILD_EXTMOD could either be an
+# absolute path or relative path from $(srctree). This makes sure that we
+# aren't using a relative path from a separate working directory (O= or
+# KBUILD_OUTPUT) since that may not be the actual module's SCM project path. So
+# check the path relative to $(srctree) first.
+ifneq ($(realpath $(srctree)/$(KBUILD_EXTMOD) 2>/dev/null),)
+	module_srcpath := $(srctree)/$(KBUILD_EXTMOD)
+else
+	module_srcpath := $(KBUILD_EXTMOD)
+endif
+
+# Get the SCM version of the external module. Sed verifies setlocalversion
+# returns a proper revision based on the SCM type, e.g. git, mercurial, or svn.
+module_scmversion := $(shell $(srctree)/scripts/setlocalversion $(module_srcpath) | \
+	sed -n 's/.*-\(\(\(g\|hg\)[a-fA-F0-9]\+\|svn[0-9]\+\)\(-dirty\)\?\).*\?/\1/p')
+# modpost option for external modules.
+MODPOST += -e$(module_scmversion)
 
 input-symdump := Module.symvers $(KBUILD_EXTRA_SYMBOLS)
 output-symdump := $(KBUILD_EXTMOD)/Module.symvers
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index f882ce0d9327..db59eb2a880d 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -30,6 +30,8 @@  static int have_vmlinux = 0;
 static int all_versions = 0;
 /* If we are modposting external module set to 1 */
 static int external_module = 0;
+#define MODULE_SCMVERSION_SIZE 64
+static char module_scmversion[MODULE_SCMVERSION_SIZE];
 /* Only warn about unresolved symbols */
 static int warn_unresolved = 0;
 /* How a symbol is exported */
@@ -2272,6 +2274,27 @@  static void add_intree_flag(struct buffer *b, int is_intree)
 		buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n");
 }
 
+/**
+ * add_scmversion() - Adds the MODULE_INFO macro for the scmversion.
+ * @b: Buffer to append to.
+ * @is_intree: Indicates if the module is in-tree or is an external module.
+ *
+ * This function fills in the module attribute `scmversion` for the kernel
+ * module. This is useful for determining a given module's SCM version on
+ * device via /sys/modules/<module>/scmversion and/or using the modinfo tool.
+ *
+ * If it's an in-tree module, then the UTS_RELEASE version is used. Otherwise,
+ * the provided SCM version is used. If there was no SCM version provided to
+ * the script for an external module, then `scmversion` is omitted.
+ */
+static void add_scmversion(struct buffer *b, int is_intree)
+{
+	if (is_intree)
+		buf_printf(b, "\nMODULE_INFO(scmversion, UTS_RELEASE);\n");
+	else if (module_scmversion[0] != '\0')
+		buf_printf(b, "\nMODULE_INFO(scmversion, \"%s\");\n", module_scmversion);
+}
+
 /* Cannot check for assembler */
 static void add_retpoline(struct buffer *b)
 {
@@ -2559,10 +2582,12 @@  int main(int argc, char **argv)
 	struct dump_list *dump_read_start = NULL;
 	struct dump_list **dump_read_iter = &dump_read_start;
 
-	while ((opt = getopt(argc, argv, "ei:mnT:o:awENd:")) != -1) {
+	while ((opt = getopt(argc, argv, "e::i:mnT:o:awENd:")) != -1) {
 		switch (opt) {
 		case 'e':
 			external_module = 1;
+			if (optarg)
+				strncpy(module_scmversion, optarg, sizeof(module_scmversion) - 1);
 			break;
 		case 'i':
 			*dump_read_iter =
@@ -2645,6 +2670,7 @@  int main(int argc, char **argv)
 		add_depends(&buf, mod);
 		add_moddevtable(&buf, mod);
 		add_srcversion(&buf, mod);
+		add_scmversion(&buf, !external_module);
 
 		sprintf(fname, "%s.mod.c", mod->name);
 		write_if_changed(&buf, fname);