diff options
Diffstat (limited to 'kernel/module.c')
| -rw-r--r-- | kernel/module.c | 112 | 
1 files changed, 107 insertions, 5 deletions
| diff --git a/kernel/module.c b/kernel/module.c index a566745dde6..c32995fbd8f 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -35,6 +35,7 @@  #include <linux/notifier.h>  #include <linux/stop_machine.h>  #include <linux/device.h> +#include <linux/string.h>  #include <asm/uaccess.h>  #include <asm/semaphore.h>  #include <asm/cacheflush.h> @@ -249,13 +250,18 @@ static inline unsigned int block_size(int val)  /* Created by linker magic */  extern char __per_cpu_start[], __per_cpu_end[]; -static void *percpu_modalloc(unsigned long size, unsigned long align) +static void *percpu_modalloc(unsigned long size, unsigned long align, +			     const char *name)  {  	unsigned long extra;  	unsigned int i;  	void *ptr; -	BUG_ON(align > SMP_CACHE_BYTES); +	if (align > SMP_CACHE_BYTES) { +		printk(KERN_WARNING "%s: per-cpu alignment %li > %i\n", +		       name, align, SMP_CACHE_BYTES); +		align = SMP_CACHE_BYTES; +	}  	ptr = __per_cpu_start;  	for (i = 0; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) { @@ -347,7 +353,8 @@ static int percpu_modinit(void)  }	  __initcall(percpu_modinit);  #else /* ... !CONFIG_SMP */ -static inline void *percpu_modalloc(unsigned long size, unsigned long align) +static inline void *percpu_modalloc(unsigned long size, unsigned long align, +				    const char *name)  {  	return NULL;  } @@ -370,6 +377,43 @@ static inline void percpu_modcopy(void *pcpudst, const void *src,  #endif /* CONFIG_SMP */  #ifdef CONFIG_MODULE_UNLOAD +#define MODINFO_ATTR(field)	\ +static void setup_modinfo_##field(struct module *mod, const char *s)  \ +{                                                                     \ +	mod->field = kstrdup(s, GFP_KERNEL);                          \ +}                                                                     \ +static ssize_t show_modinfo_##field(struct module_attribute *mattr,   \ +	                struct module *mod, char *buffer)             \ +{                                                                     \ +	return sprintf(buffer, "%s\n", mod->field);                   \ +}                                                                     \ +static int modinfo_##field##_exists(struct module *mod)               \ +{                                                                     \ +	return mod->field != NULL;                                    \ +}                                                                     \ +static void free_modinfo_##field(struct module *mod)                  \ +{                                                                     \ +        kfree(mod->field);                                            \ +        mod->field = NULL;                                            \ +}                                                                     \ +static struct module_attribute modinfo_##field = {                    \ +	.attr = { .name = __stringify(field), .mode = 0444,           \ +		  .owner = THIS_MODULE },                             \ +	.show = show_modinfo_##field,                                 \ +	.setup = setup_modinfo_##field,                               \ +	.test = modinfo_##field##_exists,                             \ +	.free = free_modinfo_##field,                                 \ +}; + +MODINFO_ATTR(version); +MODINFO_ATTR(srcversion); + +static struct module_attribute *modinfo_attrs[] = { +	&modinfo_version, +	&modinfo_srcversion, +	NULL, +}; +  /* Init the unload section of the module. */  static void module_unload_init(struct module *mod)  { @@ -692,7 +736,7 @@ static int obsparm_copy_string(const char *val, struct kernel_param *kp)  	return 0;  } -int set_obsolete(const char *val, struct kernel_param *kp) +static int set_obsolete(const char *val, struct kernel_param *kp)  {  	unsigned int min, max;  	unsigned int size, maxsize; @@ -1031,6 +1075,32 @@ static void module_remove_refcnt_attr(struct module *mod)  }  #endif +#ifdef CONFIG_MODULE_UNLOAD +static int module_add_modinfo_attrs(struct module *mod) +{ +	struct module_attribute *attr; +	int error = 0; +	int i; + +	for (i = 0; (attr = modinfo_attrs[i]) && !error; i++) { +		if (!attr->test || +		    (attr->test && attr->test(mod))) +			error = sysfs_create_file(&mod->mkobj.kobj,&attr->attr); +	} +	return error; +} + +static void module_remove_modinfo_attrs(struct module *mod) +{ +	struct module_attribute *attr; +	int i; + +	for (i = 0; (attr = modinfo_attrs[i]); i++) { +		sysfs_remove_file(&mod->mkobj.kobj,&attr->attr); +		attr->free(mod); +	} +} +#endif  static int mod_sysfs_setup(struct module *mod,  			   struct kernel_param *kparam, @@ -1056,6 +1126,12 @@ static int mod_sysfs_setup(struct module *mod,  	if (err)  		goto out_unreg; +#ifdef CONFIG_MODULE_UNLOAD +	err = module_add_modinfo_attrs(mod); +	if (err) +		goto out_unreg; +#endif +  	return 0;  out_unreg: @@ -1066,6 +1142,9 @@ out:  static void mod_kobject_remove(struct module *mod)  { +#ifdef CONFIG_MODULE_UNLOAD +	module_remove_modinfo_attrs(mod); +#endif  	module_remove_refcnt_attr(mod);  	module_param_sysfs_remove(mod); @@ -1311,6 +1390,23 @@ static char *get_modinfo(Elf_Shdr *sechdrs,  	return NULL;  } +#ifdef CONFIG_MODULE_UNLOAD +static void setup_modinfo(struct module *mod, Elf_Shdr *sechdrs, +			  unsigned int infoindex) +{ +	struct module_attribute *attr; +	int i; + +	for (i = 0; (attr = modinfo_attrs[i]); i++) { +		if (attr->setup) +			attr->setup(mod, +				    get_modinfo(sechdrs, +						infoindex, +						attr->attr.name)); +	} +} +#endif +  #ifdef CONFIG_KALLSYMS  int is_exported(const char *name, const struct module *mod)  { @@ -1554,7 +1650,8 @@ static struct module *load_module(void __user *umod,  	if (pcpuindex) {  		/* We have a special allocation for this section. */  		percpu = percpu_modalloc(sechdrs[pcpuindex].sh_size, -					 sechdrs[pcpuindex].sh_addralign); +					 sechdrs[pcpuindex].sh_addralign, +					 mod->name);  		if (!percpu) {  			err = -ENOMEM;  			goto free_mod; @@ -1615,6 +1712,11 @@ static struct module *load_module(void __user *umod,  	/* Set up license info based on the info section */  	set_license(mod, get_modinfo(sechdrs, infoindex, "license")); +#ifdef CONFIG_MODULE_UNLOAD +	/* Set up MODINFO_ATTR fields */ +	setup_modinfo(mod, sechdrs, infoindex); +#endif +  	/* Fix up syms, so that st_value is a pointer to location. */  	err = simplify_symbols(sechdrs, symindex, strtab, versindex, pcpuindex,  			       mod); | 
