From: Jeff Mahoney Subject: Export supported status via sysfs This patch adds a /sys/kernel/supported file indicating the supportability status of the entire kernel. It also adds a /sys/module//supported file indicating the supportability status of individual modules. This is useful because it can be used to obtain the supported status of a running system without current modules (ie: immediately after a kernel update but before a reboot) and without generating an oops. Signed-off-by: Jeff Mahoney --- include/linux/module.h | 1 kernel/ksysfs.c | 18 ++++++++++++ kernel/module.c | 70 ++++++++++++++++++++++++++++++++----------------- 3 files changed, 66 insertions(+), 23 deletions(-) --- a/include/linux/module.h +++ b/include/linux/module.h @@ -363,6 +363,7 @@ static inline int module_is_live(struct struct module *module_text_address(unsigned long addr); struct module *__module_text_address(unsigned long addr); int is_module_address(unsigned long addr); +const char *supported_printable(int taint); /* Returns 0 and fills in value, defined and namebuf, or -ERANGE if symnum out of range. */ --- a/kernel/ksysfs.c +++ b/kernel/ksysfs.c @@ -104,6 +104,23 @@ static struct bin_attribute notes_attr = struct kobject *kernel_kobj; EXPORT_SYMBOL_GPL(kernel_kobj); +const char *supported_printable(int taint) +{ + if (taint & TAINT_NO_SUPPORT) + return "No"; + else if (taint & TAINT_EXTERNAL_SUPPORT) + return "Yes, External"; + else + return "Yes"; +} + +static ssize_t supported_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", supported_printable(tainted)); +} +KERNEL_ATTR_RO(supported); + static struct attribute * kernel_attrs[] = { #if defined(CONFIG_HOTPLUG) && defined(CONFIG_NET) &uevent_seqnum_attr.attr, @@ -114,6 +131,7 @@ static struct attribute * kernel_attrs[] &kexec_crash_loaded_attr.attr, &vmcoreinfo_attr.attr, #endif + &supported_attr.attr, NULL }; --- a/kernel/module.c +++ b/kernel/module.c @@ -922,10 +922,36 @@ static struct module_attribute initstate .show = show_initstate, }; +static void setup_modinfo_supported(struct module *mod, const char *s) +{ + if (!s) { + mod->taints |= TAINT_NO_SUPPORT; + return; + } + + if (strcmp(s, "external") == 0) + mod->taints |= TAINT_EXTERNAL_SUPPORT; + else if (strcmp(s, "yes")) + mod->taints |= TAINT_NO_SUPPORT; +} + +static ssize_t show_modinfo_supported(struct module_attribute *mattr, + struct module *mod, char *buffer) +{ + return sprintf(buffer, "%s\n", supported_printable(mod->taints)); +} + +static struct module_attribute modinfo_supported = { + .attr = { .name = "supported", .mode = 0444 }, + .show = show_modinfo_supported, + .setup = setup_modinfo_supported, +}; + static struct module_attribute *modinfo_attrs[] = { &modinfo_version, &modinfo_srcversion, &initstate, + &modinfo_supported, #ifdef CONFIG_MODULE_UNLOAD &refcnt, #endif @@ -1820,7 +1846,6 @@ static noinline struct module *load_modu Elf_Ehdr *hdr; Elf_Shdr *sechdrs; char *secstrings, *args, *modmagic, *strtab = NULL; - char *supported; unsigned int i; unsigned int symindex = 0; unsigned int strindex = 0; @@ -1975,28 +2000,6 @@ static noinline struct module *load_modu goto free_hdr; } - supported = get_modinfo(sechdrs, infoindex, "supported"); - if (supported) { - if (!strcmp(supported, "external")) - add_taint_module(mod, TAINT_EXTERNAL_SUPPORT); - else if (strcmp(supported, "yes")) - supported = NULL; - } - if (!supported) { - if (unsupported == 0) { - printk(KERN_WARNING "%s: module not supported by " - "Novell, refusing to load. To override, echo " - "1 > /proc/sys/kernel/unsupported\n", mod->name); - err = -ENOEXEC; - goto free_hdr; - } - add_taint_module(mod, TAINT_NO_SUPPORT); - if (unsupported == 1) { - printk(KERN_WARNING "%s: module not supported by " - "Novell, setting U taint flag.\n", mod->name); - } - } - /* Now copy in args */ args = strndup_user(uargs, ~0UL >> 1); if (IS_ERR(args)) { @@ -2256,6 +2259,26 @@ static noinline struct module *load_modu add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs); add_notes_attrs(mod, hdr->e_shnum, secstrings, sechdrs); + /* We don't use add_taint() here because it also disables lockdep. */ + if (mod->taints & TAINT_EXTERNAL_SUPPORT) + tainted |= TAINT_EXTERNAL_SUPPORT; + else if (mod->taints == TAINT_NO_SUPPORT) { + if (unsupported == 0) { + printk(KERN_WARNING "%s: module not supported by " + "Novell, refusing to load. To override, echo " + "1 > /proc/sys/kernel/unsupported\n", mod->name); + err = -ENOEXEC; + goto free_hdr; + } + tainted |= TAINT_NO_SUPPORT; + if (unsupported == 1) { + printk(KERN_WARNING "%s: module is not supported by " + "Novell. Novell Technical Services may decline " + "your support request if it involves a kernel " + "fault.\n", mod->name); + } + } + /* Size of section 0 is 0, so this works well if no unwind info. */ mod->unwind_info = unwind_add_table(mod, (void *)sechdrs[unwindex].sh_addr, @@ -2735,6 +2758,7 @@ void print_modules(void) if (last_unloaded_module[0]) printk(" [last unloaded: %s]", last_unloaded_module); printk("\n"); + printk("Supported: %s\n", supported_printable(tainted)); } #ifdef CONFIG_MODVERSIONS