Commit b863a25d authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Add modules to sysfs

This patch adds basic kobject support to struct module, and it creates a 
/sys/module directory which contains all of the individual modules.  Each
module currently exports the refcount (if they are unloadable) and any
module paramaters that are marked exportable in sysfs.

Was written by me and Rusty over and over many times during the past 6 months.
parent 009196b4
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include <linux/kmod.h> #include <linux/kmod.h>
#include <linux/elf.h> #include <linux/elf.h>
#include <linux/stringify.h> #include <linux/stringify.h>
#include <linux/kobject.h>
#include <linux/moduleparam.h>
#include <asm/local.h> #include <asm/local.h>
#include <asm/module.h> #include <asm/module.h>
...@@ -207,6 +209,23 @@ enum module_state ...@@ -207,6 +209,23 @@ enum module_state
MODULE_STATE_GOING, MODULE_STATE_GOING,
}; };
/* sysfs stuff */
struct module_attribute
{
struct attribute attr;
struct kernel_param *param;
};
struct module_kobject
{
/* Everyone should have one of these. */
struct kobject kobj;
/* We always have refcnt, we may have others from module_param(). */
unsigned int num_attributes;
struct module_attribute attr[0];
};
struct module struct module
{ {
enum module_state state; enum module_state state;
...@@ -217,6 +236,9 @@ struct module ...@@ -217,6 +236,9 @@ struct module
/* Unique handle for this module */ /* Unique handle for this module */
char name[MODULE_NAME_LEN]; char name[MODULE_NAME_LEN];
/* Sysfs stuff. */
struct module_kobject *mkobj;
/* Exported symbols */ /* Exported symbols */
const struct kernel_symbol *syms; const struct kernel_symbol *syms;
unsigned int num_syms; unsigned int num_syms;
...@@ -267,6 +289,9 @@ struct module ...@@ -267,6 +289,9 @@ struct module
/* Destruction function. */ /* Destruction function. */
void (*exit)(void); void (*exit)(void);
/* Fake kernel param for refcnt. */
struct kernel_param refcnt_param;
#endif #endif
#ifdef CONFIG_KALLSYMS #ifdef CONFIG_KALLSYMS
......
...@@ -50,7 +50,7 @@ struct kparam_array ...@@ -50,7 +50,7 @@ struct kparam_array
not there, read bits mean it's readable, write bits mean it's not there, read bits mean it's readable, write bits mean it's
writable. */ writable. */
#define __module_param_call(prefix, name, set, get, arg, perm) \ #define __module_param_call(prefix, name, set, get, arg, perm) \
static char __param_str_##name[] __initdata = prefix #name; \ static char __param_str_##name[] = prefix #name; \
static struct kernel_param const __param_##name \ static struct kernel_param const __param_##name \
__attribute_used__ \ __attribute_used__ \
__attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \ __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \
...@@ -71,7 +71,7 @@ struct kparam_array ...@@ -71,7 +71,7 @@ struct kparam_array
/* Actually copy string: maxlen param is usually sizeof(string). */ /* Actually copy string: maxlen param is usually sizeof(string). */
#define module_param_string(name, string, len, perm) \ #define module_param_string(name, string, len, perm) \
static struct kparam_string __param_string_##name __initdata \ static struct kparam_string __param_string_##name \
= { len, string }; \ = { len, string }; \
module_param_call(name, param_set_copystring, param_get_charp, \ module_param_call(name, param_set_copystring, param_get_charp, \
&__param_string_##name, perm) &__param_string_##name, perm)
......
...@@ -504,6 +504,22 @@ static int try_stop_module(struct module *mod, int flags, int *forced) ...@@ -504,6 +504,22 @@ static int try_stop_module(struct module *mod, int flags, int *forced)
return stop_machine_run(__try_stop_module, &sref, NR_CPUS); return stop_machine_run(__try_stop_module, &sref, NR_CPUS);
} }
static int add_attribute(struct module *mod, struct kernel_param *kp)
{
struct module_attribute *a;
int retval;
a = &mod->mkobj->attr[mod->mkobj->num_attributes];
a->attr.name = (char *)kp->name;
a->attr.owner = mod;
a->attr.mode = kp->perm;
a->param = kp;
retval = sysfs_create_file(&mod->mkobj->kobj, &a->attr);
if (!retval)
mod->mkobj->num_attributes++;
return retval;
}
unsigned int module_refcount(struct module *mod) unsigned int module_refcount(struct module *mod)
{ {
unsigned int i, total = 0; unsigned int i, total = 0;
...@@ -663,6 +679,23 @@ void symbol_put_addr(void *addr) ...@@ -663,6 +679,23 @@ void symbol_put_addr(void *addr)
} }
EXPORT_SYMBOL_GPL(symbol_put_addr); EXPORT_SYMBOL_GPL(symbol_put_addr);
static int refcnt_get_fn(char *buffer, struct kernel_param *kp)
{
struct module *mod = container_of(kp, struct module, refcnt_param);
/* sysfs holds one reference. */
return sprintf(buffer, "%u", module_refcount(mod)-1);
}
static inline int sysfs_unload_setup(struct module *mod)
{
mod->refcnt_param.name = "refcnt";
mod->refcnt_param.perm = 0444;
mod->refcnt_param.get = refcnt_get_fn;
return add_attribute(mod, &mod->refcnt_param);
}
#else /* !CONFIG_MODULE_UNLOAD */ #else /* !CONFIG_MODULE_UNLOAD */
static void print_unload_info(struct seq_file *m, struct module *mod) static void print_unload_info(struct seq_file *m, struct module *mod)
{ {
...@@ -689,6 +722,10 @@ sys_delete_module(const char *name_user, unsigned int flags) ...@@ -689,6 +722,10 @@ sys_delete_module(const char *name_user, unsigned int flags)
return -ENOSYS; return -ENOSYS;
} }
static inline int sysfs_unload_setup(struct module *mod)
{
return 0;
}
#endif /* CONFIG_MODULE_UNLOAD */ #endif /* CONFIG_MODULE_UNLOAD */
#ifdef CONFIG_OBSOLETE_MODPARM #ifdef CONFIG_OBSOLETE_MODPARM
...@@ -944,6 +981,116 @@ static unsigned long resolve_symbol(Elf_Shdr *sechdrs, ...@@ -944,6 +981,116 @@ static unsigned long resolve_symbol(Elf_Shdr *sechdrs,
return ret; return ret;
} }
#define to_module_attr(n) container_of(n, struct module_attribute, attr);
static ssize_t module_attr_show(struct kobject *kobj,
struct attribute *attr,
char *buf)
{
int count;
struct module_attribute *attribute = to_module_attr(attr);
if (!attribute->param->get)
return -EPERM;
count = attribute->param->get(buf, attribute->param);
if (count > 0) {
strcat(buf, "\n");
++count;
}
return count;
}
/* sysfs always hands a nul-terminated string in buf. We rely on that. */
static ssize_t module_attr_store(struct kobject *kobj,
struct attribute *attr,
const char *buf, size_t len)
{
int err;
struct module_attribute *attribute = to_module_attr(attr);
if (!attribute->param->set)
return -EPERM;
err = attribute->param->set(buf, attribute->param);
if (!err)
return len;
return err;
}
static struct sysfs_ops module_sysfs_ops = {
.show = module_attr_show,
.store = module_attr_store,
};
static void module_kobj_release(struct kobject *kobj)
{
kfree(container_of(kobj, struct module_kobject, kobj));
}
static struct kobj_type module_ktype = {
.sysfs_ops = &module_sysfs_ops,
.release = &module_kobj_release,
};
static decl_subsys(module, &module_ktype, NULL);
static int mod_sysfs_setup(struct module *mod,
struct kernel_param *kparam,
unsigned int num_params)
{
unsigned int i;
int err;
/* We overallocate: not every param is in sysfs, and maybe no refcnt */
mod->mkobj = kmalloc(sizeof(*mod->mkobj)
+ sizeof(mod->mkobj->attr[0]) * (num_params+1),
GFP_KERNEL);
if (!mod->mkobj)
return -ENOMEM;
memset(&mod->mkobj->kobj, 0, sizeof(mod->mkobj->kobj));
err = kobject_set_name(&mod->mkobj->kobj, mod->name);
if (err)
goto out;
kobj_set_kset_s(mod->mkobj, module_subsys);
err = kobject_register(&mod->mkobj->kobj);
if (err)
goto out;
mod->mkobj->num_attributes = 0;
for (i = 0; i < num_params; i++) {
if (kparam[i].perm) {
err = add_attribute(mod, &kparam[i]);
if (err)
goto out_unreg;
}
}
err = sysfs_unload_setup(mod);
if (err)
goto out_unreg;
return 0;
out_unreg:
for (i = 0; i < mod->mkobj->num_attributes; i++)
sysfs_remove_file(&mod->mkobj->kobj,&mod->mkobj->attr[i].attr);
/* Calls module_kobj_release */
kobject_unregister(&mod->mkobj->kobj);
return err;
out:
kfree(mod->mkobj);
return err;
}
static void mod_kobject_remove(struct module *mod)
{
unsigned int i;
for (i = 0; i < mod->mkobj->num_attributes; i++)
sysfs_remove_file(&mod->mkobj->kobj,&mod->mkobj->attr[i].attr);
/* Calls module_kobj_release */
kobject_unregister(&mod->mkobj->kobj);
}
/* Free a module, remove from lists, etc (must hold module mutex). */ /* Free a module, remove from lists, etc (must hold module mutex). */
static void free_module(struct module *mod) static void free_module(struct module *mod)
{ {
...@@ -952,6 +1099,8 @@ static void free_module(struct module *mod) ...@@ -952,6 +1099,8 @@ static void free_module(struct module *mod)
list_del(&mod->list); list_del(&mod->list);
spin_unlock_irq(&modlist_lock); spin_unlock_irq(&modlist_lock);
mod_kobject_remove(mod);
/* Arch-specific cleanup. */ /* Arch-specific cleanup. */
module_arch_cleanup(mod); module_arch_cleanup(mod);
...@@ -1556,6 +1705,11 @@ static struct module *load_module(void __user *umod, ...@@ -1556,6 +1705,11 @@ static struct module *load_module(void __user *umod,
/ sizeof(struct kernel_param), / sizeof(struct kernel_param),
NULL); NULL);
} }
err = mod_sysfs_setup(mod,
(struct kernel_param *)
sechdrs[setupindex].sh_addr,
sechdrs[setupindex].sh_size
/ sizeof(struct kernel_param));
if (err < 0) if (err < 0)
goto arch_cleanup; goto arch_cleanup;
...@@ -1891,3 +2045,9 @@ struct module *module_text_address(unsigned long addr) ...@@ -1891,3 +2045,9 @@ struct module *module_text_address(unsigned long addr)
void struct_module(struct module *mod) { return; } void struct_module(struct module *mod) { return; }
EXPORT_SYMBOL(struct_module); EXPORT_SYMBOL(struct_module);
#endif #endif
static int __init modules_init(void)
{
return subsystem_register(&module_subsys);
}
__initcall(modules_init);
...@@ -161,7 +161,7 @@ int parse_args(const char *name, ...@@ -161,7 +161,7 @@ int parse_args(const char *name,
\ \
if (!val) return -EINVAL; \ if (!val) return -EINVAL; \
l = strtolfn(val, &endp, 0); \ l = strtolfn(val, &endp, 0); \
if (endp == val || *endp || ((type)l != l)) \ if (endp == val || ((type)l != l)) \
return -EINVAL; \ return -EINVAL; \
*((type *)kp->arg) = l; \ *((type *)kp->arg) = l; \
return 0; \ return 0; \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment