diff options
Diffstat (limited to 'kernel/module.c')
-rw-r--r-- | kernel/module.c | 99 |
1 files changed, 97 insertions, 2 deletions
diff --git a/kernel/module.c b/kernel/module.c index 83b3d376708c..068e271ab3a5 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/notifier.h> | 35 | #include <linux/notifier.h> |
36 | #include <linux/stop_machine.h> | 36 | #include <linux/stop_machine.h> |
37 | #include <linux/device.h> | 37 | #include <linux/device.h> |
38 | #include <linux/string.h> | ||
38 | #include <asm/uaccess.h> | 39 | #include <asm/uaccess.h> |
39 | #include <asm/semaphore.h> | 40 | #include <asm/semaphore.h> |
40 | #include <asm/cacheflush.h> | 41 | #include <asm/cacheflush.h> |
@@ -370,6 +371,43 @@ static inline void percpu_modcopy(void *pcpudst, const void *src, | |||
370 | #endif /* CONFIG_SMP */ | 371 | #endif /* CONFIG_SMP */ |
371 | 372 | ||
372 | #ifdef CONFIG_MODULE_UNLOAD | 373 | #ifdef CONFIG_MODULE_UNLOAD |
374 | #define MODINFO_ATTR(field) \ | ||
375 | static void setup_modinfo_##field(struct module *mod, const char *s) \ | ||
376 | { \ | ||
377 | mod->field = kstrdup(s, GFP_KERNEL); \ | ||
378 | } \ | ||
379 | static ssize_t show_modinfo_##field(struct module_attribute *mattr, \ | ||
380 | struct module *mod, char *buffer) \ | ||
381 | { \ | ||
382 | return sprintf(buffer, "%s\n", mod->field); \ | ||
383 | } \ | ||
384 | static int modinfo_##field##_exists(struct module *mod) \ | ||
385 | { \ | ||
386 | return mod->field != NULL; \ | ||
387 | } \ | ||
388 | static void free_modinfo_##field(struct module *mod) \ | ||
389 | { \ | ||
390 | kfree(mod->field); \ | ||
391 | mod->field = NULL; \ | ||
392 | } \ | ||
393 | static struct module_attribute modinfo_##field = { \ | ||
394 | .attr = { .name = __stringify(field), .mode = 0444, \ | ||
395 | .owner = THIS_MODULE }, \ | ||
396 | .show = show_modinfo_##field, \ | ||
397 | .setup = setup_modinfo_##field, \ | ||
398 | .test = modinfo_##field##_exists, \ | ||
399 | .free = free_modinfo_##field, \ | ||
400 | }; | ||
401 | |||
402 | MODINFO_ATTR(version); | ||
403 | MODINFO_ATTR(srcversion); | ||
404 | |||
405 | static struct module_attribute *modinfo_attrs[] = { | ||
406 | &modinfo_version, | ||
407 | &modinfo_srcversion, | ||
408 | NULL, | ||
409 | }; | ||
410 | |||
373 | /* Init the unload section of the module. */ | 411 | /* Init the unload section of the module. */ |
374 | static void module_unload_init(struct module *mod) | 412 | static void module_unload_init(struct module *mod) |
375 | { | 413 | { |
@@ -379,7 +417,7 @@ static void module_unload_init(struct module *mod) | |||
379 | for (i = 0; i < NR_CPUS; i++) | 417 | for (i = 0; i < NR_CPUS; i++) |
380 | local_set(&mod->ref[i].count, 0); | 418 | local_set(&mod->ref[i].count, 0); |
381 | /* Hold reference count during initialization. */ | 419 | /* Hold reference count during initialization. */ |
382 | local_set(&mod->ref[_smp_processor_id()].count, 1); | 420 | local_set(&mod->ref[raw_smp_processor_id()].count, 1); |
383 | /* Backwards compatibility macros put refcount during init. */ | 421 | /* Backwards compatibility macros put refcount during init. */ |
384 | mod->waiter = current; | 422 | mod->waiter = current; |
385 | } | 423 | } |
@@ -692,7 +730,7 @@ static int obsparm_copy_string(const char *val, struct kernel_param *kp) | |||
692 | return 0; | 730 | return 0; |
693 | } | 731 | } |
694 | 732 | ||
695 | int set_obsolete(const char *val, struct kernel_param *kp) | 733 | static int set_obsolete(const char *val, struct kernel_param *kp) |
696 | { | 734 | { |
697 | unsigned int min, max; | 735 | unsigned int min, max; |
698 | unsigned int size, maxsize; | 736 | unsigned int size, maxsize; |
@@ -1031,6 +1069,32 @@ static void module_remove_refcnt_attr(struct module *mod) | |||
1031 | } | 1069 | } |
1032 | #endif | 1070 | #endif |
1033 | 1071 | ||
1072 | #ifdef CONFIG_MODULE_UNLOAD | ||
1073 | static int module_add_modinfo_attrs(struct module *mod) | ||
1074 | { | ||
1075 | struct module_attribute *attr; | ||
1076 | int error = 0; | ||
1077 | int i; | ||
1078 | |||
1079 | for (i = 0; (attr = modinfo_attrs[i]) && !error; i++) { | ||
1080 | if (!attr->test || | ||
1081 | (attr->test && attr->test(mod))) | ||
1082 | error = sysfs_create_file(&mod->mkobj.kobj,&attr->attr); | ||
1083 | } | ||
1084 | return error; | ||
1085 | } | ||
1086 | |||
1087 | static void module_remove_modinfo_attrs(struct module *mod) | ||
1088 | { | ||
1089 | struct module_attribute *attr; | ||
1090 | int i; | ||
1091 | |||
1092 | for (i = 0; (attr = modinfo_attrs[i]); i++) { | ||
1093 | sysfs_remove_file(&mod->mkobj.kobj,&attr->attr); | ||
1094 | attr->free(mod); | ||
1095 | } | ||
1096 | } | ||
1097 | #endif | ||
1034 | 1098 | ||
1035 | static int mod_sysfs_setup(struct module *mod, | 1099 | static int mod_sysfs_setup(struct module *mod, |
1036 | struct kernel_param *kparam, | 1100 | struct kernel_param *kparam, |
@@ -1056,6 +1120,12 @@ static int mod_sysfs_setup(struct module *mod, | |||
1056 | if (err) | 1120 | if (err) |
1057 | goto out_unreg; | 1121 | goto out_unreg; |
1058 | 1122 | ||
1123 | #ifdef CONFIG_MODULE_UNLOAD | ||
1124 | err = module_add_modinfo_attrs(mod); | ||
1125 | if (err) | ||
1126 | goto out_unreg; | ||
1127 | #endif | ||
1128 | |||
1059 | return 0; | 1129 | return 0; |
1060 | 1130 | ||
1061 | out_unreg: | 1131 | out_unreg: |
@@ -1066,6 +1136,9 @@ out: | |||
1066 | 1136 | ||
1067 | static void mod_kobject_remove(struct module *mod) | 1137 | static void mod_kobject_remove(struct module *mod) |
1068 | { | 1138 | { |
1139 | #ifdef CONFIG_MODULE_UNLOAD | ||
1140 | module_remove_modinfo_attrs(mod); | ||
1141 | #endif | ||
1069 | module_remove_refcnt_attr(mod); | 1142 | module_remove_refcnt_attr(mod); |
1070 | module_param_sysfs_remove(mod); | 1143 | module_param_sysfs_remove(mod); |
1071 | 1144 | ||
@@ -1311,6 +1384,23 @@ static char *get_modinfo(Elf_Shdr *sechdrs, | |||
1311 | return NULL; | 1384 | return NULL; |
1312 | } | 1385 | } |
1313 | 1386 | ||
1387 | #ifdef CONFIG_MODULE_UNLOAD | ||
1388 | static void setup_modinfo(struct module *mod, Elf_Shdr *sechdrs, | ||
1389 | unsigned int infoindex) | ||
1390 | { | ||
1391 | struct module_attribute *attr; | ||
1392 | int i; | ||
1393 | |||
1394 | for (i = 0; (attr = modinfo_attrs[i]); i++) { | ||
1395 | if (attr->setup) | ||
1396 | attr->setup(mod, | ||
1397 | get_modinfo(sechdrs, | ||
1398 | infoindex, | ||
1399 | attr->attr.name)); | ||
1400 | } | ||
1401 | } | ||
1402 | #endif | ||
1403 | |||
1314 | #ifdef CONFIG_KALLSYMS | 1404 | #ifdef CONFIG_KALLSYMS |
1315 | int is_exported(const char *name, const struct module *mod) | 1405 | int is_exported(const char *name, const struct module *mod) |
1316 | { | 1406 | { |
@@ -1615,6 +1705,11 @@ static struct module *load_module(void __user *umod, | |||
1615 | /* Set up license info based on the info section */ | 1705 | /* Set up license info based on the info section */ |
1616 | set_license(mod, get_modinfo(sechdrs, infoindex, "license")); | 1706 | set_license(mod, get_modinfo(sechdrs, infoindex, "license")); |
1617 | 1707 | ||
1708 | #ifdef CONFIG_MODULE_UNLOAD | ||
1709 | /* Set up MODINFO_ATTR fields */ | ||
1710 | setup_modinfo(mod, sechdrs, infoindex); | ||
1711 | #endif | ||
1712 | |||
1618 | /* Fix up syms, so that st_value is a pointer to location. */ | 1713 | /* Fix up syms, so that st_value is a pointer to location. */ |
1619 | err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex, | 1714 | err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex, |
1620 | mod); | 1715 | mod); |