Currently, the only way for userspace to determine which symbol namespaces a module imports is to locate the .ko file on disk (which may not match the loaded module), then either parsing the binary manually and handling any potential compression, or shelling-out to modinfo.
This is painful in cases where userspace wants to distinguish between module variants that share the same name but import different namespaces. For example, the nvidia-uvm module exists in both open and closed source variants; the open source version imports the DMA_BUF namespace while the closed source version does not, and networking middleware may want to initialize itself differently depending on that. Add /sys/module/*/import_ns to expose imported namespaces for loaded modules. The file contains one namespace per line and only exists for modules that import at least one namespace. Signed-off-by: Nicholas Sielicki <[email protected]> --- Documentation/ABI/testing/sysfs-module | 9 ++++ include/linux/module.h | 1 + kernel/module/main.c | 62 +++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-module b/Documentation/ABI/testing/sysfs-module index 6bc9af6229f0..d8e2a33fd514 100644 --- a/Documentation/ABI/testing/sysfs-module +++ b/Documentation/ABI/testing/sysfs-module @@ -48,6 +48,15 @@ Contact: Kay Sievers <[email protected]> Description: Show the initialization state(live, coming, going) of the module. +What: /sys/module/*/import_ns +Date: January 2026 +KernelVersion: 6.20 +Contact: [email protected] +Description: List of symbol namespaces imported by this module via + MODULE_IMPORT_NS(). Each namespace appears on a separate line. + This file only exists for modules that import at least one + namespace. + What: /sys/module/*/taint Date: Jan 2012 KernelVersion: 3.3 diff --git a/include/linux/module.h b/include/linux/module.h index d80c3ea57472..f1bcca03f90b 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -419,6 +419,7 @@ struct module { struct module_attribute *modinfo_attrs; const char *version; const char *srcversion; + const char *imported_namespaces; struct kobject *holders_dir; /* Exported symbols */ diff --git a/kernel/module/main.c b/kernel/module/main.c index 710ee30b3bea..6c41934f1135 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -607,6 +607,23 @@ static const struct module_attribute modinfo_##field = { \ MODINFO_ATTR(version); MODINFO_ATTR(srcversion); +static ssize_t show_modinfo_import_ns(const struct module_attribute *mattr, + struct module_kobject *mk, char *buffer) +{ + return sysfs_emit(buffer, "%s\n", mk->mod->imported_namespaces); +} + +static int modinfo_import_ns_exists(struct module *mod) +{ + return mod->imported_namespaces != NULL; +} + +static const struct module_attribute modinfo_import_ns = { + .attr = { .name = "import_ns", .mode = 0444 }, + .show = show_modinfo_import_ns, + .test = modinfo_import_ns_exists, +}; + static struct { char name[MODULE_NAME_LEN]; char taints[MODULE_FLAGS_BUF_SIZE]; @@ -1058,6 +1075,7 @@ const struct module_attribute *const modinfo_attrs[] = { &module_uevent, &modinfo_version, &modinfo_srcversion, + &modinfo_import_ns, &modinfo_initstate, &modinfo_coresize, #ifdef CONFIG_ARCH_WANTS_MODULES_DATA_IN_VMALLOC @@ -1753,11 +1771,48 @@ static void module_license_taint_check(struct module *mod, const char *license) } } +static int copy_modinfo_import_ns(struct module *mod, struct load_info *info) +{ + char *ns; + size_t len, total_len = 0; + char *buf, *p; + + for_each_modinfo_entry(ns, info, "import_ns") + total_len += strlen(ns) + 1; + + if (!total_len) { + mod->imported_namespaces = NULL; + return 0; + } + + buf = kmalloc(total_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + p = buf; + for_each_modinfo_entry(ns, info, "import_ns") { + len = strlen(ns); + memcpy(p, ns, len); + p += len; + *p++ = '\n'; + } + /* Replace trailing newline with null terminator. */ + *(p - 1) = '\0'; + + mod->imported_namespaces = buf; + return 0; +} + +static void free_modinfo_import_ns(struct module *mod) +{ + kfree(mod->imported_namespaces); +} + static int setup_modinfo(struct module *mod, struct load_info *info) { const struct module_attribute *attr; char *imported_namespace; - int i; + int i, err; for (i = 0; (attr = modinfo_attrs[i]); i++) { if (attr->setup) @@ -1776,6 +1831,10 @@ static int setup_modinfo(struct module *mod, struct load_info *info) } } + err = copy_modinfo_import_ns(mod, info); + if (err) + return err; + return 0; } @@ -1788,6 +1847,7 @@ static void free_modinfo(struct module *mod) if (attr->free) attr->free(mod); } + free_modinfo_import_ns(mod); } bool __weak module_init_section(const char *name) -- 2.52.0

