Commit 1857599a authored by Rusty Russell's avatar Rusty Russell Committed by Linus Torvalds

[PATCH] Builtin Module Parameters in sysfs too

Currently, only module parameters in loaded modules are exported in
/sys/modules/, while those of "modules" built into the kernel can be set by
the kernel command line, but not read or set via sysfs.

- move module parameters from /sys/modules/$(module_name)/$(parameter_name) to
  /sys/modules/$(module_name)/parameters/$(parameter_name)

- remove dummy kernel_param for exporting refcnt, add "struct module *"-based
  attribute instead

- also export module paramters for "modules" which are built into the kernel,
  so parameters are always accessible at
  /sys/modules/$(KBUILD_MODNAME)/$(parameter_name)

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> (modified)
Signed-off-by: default avatarDominik Brodowski <linux@brodo.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 48ed3c3b
...@@ -44,6 +44,20 @@ struct modversion_info ...@@ -44,6 +44,20 @@ struct modversion_info
char name[MODULE_NAME_LEN]; char name[MODULE_NAME_LEN];
}; };
struct module;
struct module_attribute {
struct attribute attr;
ssize_t (*show)(struct module *, char *);
ssize_t (*store)(struct module *, const char *, size_t count);
};
struct module_kobject
{
struct kobject kobj;
struct module *mod;
};
/* These are either module local, or the kernel's dummy ones. */ /* These are either module local, or the kernel's dummy ones. */
extern int init_module(void); extern int init_module(void);
extern void cleanup_module(void); extern void cleanup_module(void);
...@@ -59,6 +73,8 @@ void sort_extable(struct exception_table_entry *start, ...@@ -59,6 +73,8 @@ void sort_extable(struct exception_table_entry *start,
struct exception_table_entry *finish); struct exception_table_entry *finish);
void sort_main_extable(void); void sort_main_extable(void);
extern struct subsystem module_subsys;
#ifdef MODULE #ifdef MODULE
#define ___module_cat(a,b) __mod_ ## a ## b #define ___module_cat(a,b) __mod_ ## a ## b
#define __module_cat(a,b) ___module_cat(a,b) #define __module_cat(a,b) ___module_cat(a,b)
...@@ -206,23 +222,6 @@ enum module_state ...@@ -206,23 +222,6 @@ 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];
};
/* Similar stuff for section attributes. */ /* Similar stuff for section attributes. */
#define MODULE_SECT_NAME_LEN 32 #define MODULE_SECT_NAME_LEN 32
struct module_sect_attr struct module_sect_attr
...@@ -238,6 +237,7 @@ struct module_sections ...@@ -238,6 +237,7 @@ struct module_sections
struct module_sect_attr attrs[0]; struct module_sect_attr attrs[0];
}; };
struct param_kobject;
struct module struct module
{ {
...@@ -251,6 +251,7 @@ struct module ...@@ -251,6 +251,7 @@ struct module
/* Sysfs stuff. */ /* Sysfs stuff. */
struct module_kobject *mkobj; struct module_kobject *mkobj;
struct param_kobject *params_kobject;
/* Exported symbols */ /* Exported symbols */
const struct kernel_symbol *syms; const struct kernel_symbol *syms;
...@@ -302,9 +303,6 @@ struct module ...@@ -302,9 +303,6 @@ 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
......
...@@ -152,4 +152,15 @@ int param_array(const char *name, ...@@ -152,4 +152,15 @@ int param_array(const char *name,
void *elem, int elemsize, void *elem, int elemsize,
int (*set)(const char *, struct kernel_param *kp), int (*set)(const char *, struct kernel_param *kp),
int *num); int *num);
/* for exporting parameters in /sys/parameters */
struct module;
extern int module_param_sysfs_setup(struct module *mod,
struct kernel_param *kparam,
unsigned int num_params);
extern void module_param_sysfs_remove(struct module *mod);
#endif /* _LINUX_MODULE_PARAMS_H */ #endif /* _LINUX_MODULE_PARAMS_H */
...@@ -369,22 +369,6 @@ static inline void percpu_modcopy(void *pcpudst, const void *src, ...@@ -369,22 +369,6 @@ static inline void percpu_modcopy(void *pcpudst, const void *src,
} }
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
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;
}
#ifdef CONFIG_MODULE_UNLOAD #ifdef CONFIG_MODULE_UNLOAD
/* Init the unload section of the module. */ /* Init the unload section of the module. */
static void module_unload_init(struct module *mod) static void module_unload_init(struct module *mod)
...@@ -665,22 +649,16 @@ void symbol_put_addr(void *addr) ...@@ -665,22 +649,16 @@ 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) static int show_refcnt(struct module *mod, char *buffer)
{ {
struct module *mod = container_of(kp, struct module, refcnt_param); /* sysfs holds a reference */
return sprintf(buffer, "%u\n", module_refcount(mod)-1);
/* sysfs holds one reference. */
return sprintf(buffer, "%u", module_refcount(mod)-1);
} }
static inline int sysfs_unload_setup(struct module *mod) static struct module_attribute refcnt = {
{ .attr = { .name = "refcnt", .mode = 0444, .owner = THIS_MODULE },
mod->refcnt_param.name = "refcnt"; .show = show_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)
...@@ -708,10 +686,6 @@ sys_delete_module(const char __user *name_user, unsigned int flags) ...@@ -708,10 +686,6 @@ sys_delete_module(const char __user *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
...@@ -1050,72 +1024,33 @@ static inline void remove_sect_attrs(struct module *mod) ...@@ -1050,72 +1024,33 @@ static inline void remove_sect_attrs(struct module *mod)
#endif /* CONFIG_KALLSYMS */ #endif /* CONFIG_KALLSYMS */
#ifdef CONFIG_MODULE_UNLOAD
static inline int module_add_refcnt_attr(struct module *mod)
#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; return sysfs_create_file(&mod->mkobj->kobj, &refcnt.attr);
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;
} }
static void module_remove_refcnt_attr(struct module *mod)
/* 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; return sysfs_remove_file(&mod->mkobj->kobj, &refcnt.attr);
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;
} }
#else
static struct sysfs_ops module_sysfs_ops = { static inline int module_add_refcnt_attr(struct module *mod)
.show = module_attr_show, {
.store = module_attr_store, return 0;
}; }
static void module_remove_refcnt_attr(struct module *mod)
static void module_kobj_release(struct kobject *kobj)
{ {
kfree(container_of(kobj, struct module_kobject, kobj));
} }
#endif
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, static int mod_sysfs_setup(struct module *mod,
struct kernel_param *kparam, struct kernel_param *kparam,
unsigned int num_params) unsigned int num_params)
{ {
unsigned int i;
int err; int err;
/* We overallocate: not every param is in sysfs, and maybe no refcnt */ mod->mkobj = kmalloc(sizeof(struct module_kobject), GFP_KERNEL);
mod->mkobj = kmalloc(sizeof(*mod->mkobj)
+ sizeof(mod->mkobj->attr[0]) * (num_params+1),
GFP_KERNEL);
if (!mod->mkobj) if (!mod->mkobj)
return -ENOMEM; return -ENOMEM;
...@@ -1124,27 +1059,22 @@ static int mod_sysfs_setup(struct module *mod, ...@@ -1124,27 +1059,22 @@ static int mod_sysfs_setup(struct module *mod,
if (err) if (err)
goto out; goto out;
kobj_set_kset_s(mod->mkobj, module_subsys); kobj_set_kset_s(mod->mkobj, module_subsys);
mod->mkobj->mod = mod;
err = kobject_register(&mod->mkobj->kobj); err = kobject_register(&mod->mkobj->kobj);
if (err) if (err)
goto out; goto out;
mod->mkobj->num_attributes = 0; err = module_add_refcnt_attr(mod);
if (err)
goto out_unreg;
for (i = 0; i < num_params; i++) { err = module_param_sysfs_setup(mod, kparam, num_params);
if (kparam[i].perm) {
err = add_attribute(mod, &kparam[i]);
if (err)
goto out_unreg;
}
}
err = sysfs_unload_setup(mod);
if (err) if (err)
goto out_unreg; goto out_unreg;
return 0; return 0;
out_unreg: 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 */ /* Calls module_kobj_release */
kobject_unregister(&mod->mkobj->kobj); kobject_unregister(&mod->mkobj->kobj);
return err; return err;
...@@ -1155,9 +1085,9 @@ static int mod_sysfs_setup(struct module *mod, ...@@ -1155,9 +1085,9 @@ static int mod_sysfs_setup(struct module *mod,
static void mod_kobject_remove(struct module *mod) static void mod_kobject_remove(struct module *mod)
{ {
unsigned int i; module_remove_refcnt_attr(mod);
for (i = 0; i < mod->mkobj->num_attributes; i++) module_param_sysfs_remove(mod);
sysfs_remove_file(&mod->mkobj->kobj,&mod->mkobj->attr[i].attr);
/* Calls module_kobj_release */ /* Calls module_kobj_release */
kobject_unregister(&mod->mkobj->kobj); kobject_unregister(&mod->mkobj->kobj);
} }
...@@ -2166,9 +2096,3 @@ EXPORT_SYMBOL(module_remove_driver); ...@@ -2166,9 +2096,3 @@ EXPORT_SYMBOL(module_remove_driver);
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);
...@@ -15,11 +15,14 @@ ...@@ -15,11 +15,14 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include <linux/config.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/device.h>
#include <linux/err.h>
#if 0 #if 0
#define DEBUGP printk #define DEBUGP printk
...@@ -346,6 +349,394 @@ int param_get_string(char *buffer, struct kernel_param *kp) ...@@ -346,6 +349,394 @@ int param_get_string(char *buffer, struct kernel_param *kp)
return strlcpy(buffer, kps->string, kps->maxlen); return strlcpy(buffer, kps->string, kps->maxlen);
} }
/* sysfs output in /sys/modules/XYZ/parameters/ */
extern struct kernel_param __start___param[], __stop___param[];
#define MAX_KBUILD_MODNAME KOBJ_NAME_LEN
struct param_attribute
{
struct attribute attr;
struct kernel_param *param;
};
struct param_kobject
{
struct kobject kobj;
unsigned int num_attributes;
struct param_attribute attr[0];
};
#define to_param_attr(n) container_of(n, struct param_attribute, attr);
static ssize_t param_attr_show(struct kobject *kobj,
struct attribute *attr,
char *buf)
{
int count;
struct param_attribute *attribute = to_param_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 param_attr_store(struct kobject *kobj,
struct attribute *attr,
const char *buf, size_t len)
{
int err;
struct param_attribute *attribute = to_param_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 param_sysfs_ops = {
.show = param_attr_show,
.store = param_attr_store,
};
static void param_kobj_release(struct kobject *kobj)
{
kfree(container_of(kobj, struct param_kobject, kobj));
}
static struct kobj_type param_ktype = {
.sysfs_ops = &param_sysfs_ops,
.release = &param_kobj_release,
};
static struct kset param_kset = {
.subsys = &module_subsys,
.ktype = &param_ktype,
};
#ifdef CONFIG_MODULES
#define __modinit
#else
#define __modinit __init
#endif
/*
* param_add_attribute - actually adds an parameter to sysfs
* @mod: owner of parameter
* @pk: param_kobject the attribute shall be assigned to.
* One per module, one per KBUILD_MODNAME.
* @kp: kernel_param to be added
* @skip: offset where the parameter name start in kp->name.
* Needed for built-in modules
*
* Fill in data into appropriate &pk->attr[], and create sysfs file.
*/
static __modinit int param_add_attribute(struct module *mod,
struct param_kobject *pk,
struct kernel_param *kp,
unsigned int skip)
{
struct param_attribute *a;
int err;
a = &pk->attr[pk->num_attributes];
a->attr.name = (char *) &kp->name[skip];
a->attr.owner = mod;
a->attr.mode = kp->perm;
a->param = kp;
err = sysfs_create_file(&pk->kobj, &a->attr);
if (!err)
pk->num_attributes++;
return err;
}
/*
* param_sysfs_remove - remove sysfs support for one module or KBUILD_MODNAME
* @pk: struct param_kobject which is to be removed
*
* Called when an error in registration occurs or a module is removed
* from the system.
*/
static __modinit void param_sysfs_remove(struct param_kobject *pk)
{
unsigned int i;
for (i = 0; i < pk->num_attributes; i++)
sysfs_remove_file(&pk->kobj,&pk->attr[i].attr);
/* Calls param_kobj_release */
kobject_unregister(&pk->kobj);
}
/*
* param_sysfs_setup - setup sysfs support for one module or KBUILD_MODNAME
* @mk: struct module_kobject (contains parent kobject)
* @kparam: array of struct kernel_param, the actual parameter definitions
* @num_params: number of entries in array
* @name_skip: offset where the parameter name start in kparam[].name. Needed for built-in "modules"
*
* Create a kobject for a (per-module) group of parameters, and create files
* in sysfs. A pointer to the param_kobject is returned on success,
* NULL if there's no parameter to export, or other ERR_PTR(err).
*/
static __modinit struct param_kobject *
param_sysfs_setup(struct module_kobject *mk,
struct kernel_param *kparam,
unsigned int num_params,
unsigned int name_skip)
{
struct param_kobject *pk;
unsigned int valid_attrs = 0;
unsigned int i;
int err;
for (i=0; i<num_params; i++) {
if (kparam[i].perm)
valid_attrs++;
}
if (!valid_attrs)
return NULL;
pk = kmalloc(sizeof(struct param_kobject)
+ sizeof(struct param_attribute) * valid_attrs,
GFP_KERNEL);
if (!pk)
return ERR_PTR(-ENOMEM);
memset(pk, 0, sizeof(struct param_kobject)
+ sizeof(struct param_attribute) * valid_attrs);
err = kobject_set_name(&pk->kobj, "parameters");
if (err)
goto out;
pk->kobj.kset = &param_kset;
pk->kobj.parent = &mk->kobj;
err = kobject_register(&pk->kobj);
if (err)
goto out;
for (i = 0; i < num_params; i++) {
if (kparam[i].perm) {
err = param_add_attribute(mk->mod, pk,
&kparam[i], name_skip);
if (err)
goto out_unreg;
}
}
return pk;
out_unreg:
param_sysfs_remove(pk);
return ERR_PTR(err);
out:
kfree(pk);
return ERR_PTR(err);
}
#ifdef CONFIG_MODULES
/*
* module_param_sysfs_setup - setup sysfs support for one module
* @mod: module
* @kparam: module parameters (array)
* @num_params: number of module parameters
*
* Adds sysfs entries for module parameters, and creates a link from
* /sys/module/[mod->name]/parameters to /sys/parameters/[mod->name]/
*/
int module_param_sysfs_setup(struct module *mod,
struct kernel_param *kparam,
unsigned int num_params)
{
struct param_kobject *pk;
pk = param_sysfs_setup(mod->mkobj, kparam, num_params, 0);
if (IS_ERR(pk))
return PTR_ERR(pk);
mod->params_kobject = pk;
return 0;
}
/*
* module_param_sysfs_remove - remove sysfs support for one module
* @mod: module
*
* Remove sysfs entries for module parameters and the corresponding
* kobject.
*/
void module_param_sysfs_remove(struct module *mod)
{
if (mod->params_kobject) {
param_sysfs_remove(mod->params_kobject);
mod->params_kobject = NULL;
}
}
#endif
/*
* kernel_param_sysfs_setup - wrapper for built-in params support
*/
static void __init kernel_param_sysfs_setup(const char *name,
struct kernel_param *kparam,
unsigned int num_params,
unsigned int name_skip)
{
struct module_kobject *mk;
mk = kmalloc(sizeof(struct module_kobject), GFP_KERNEL);
memset(mk, 0, sizeof(struct module_kobject));
mk->mod = THIS_MODULE;
kobj_set_kset_s(mk, module_subsys);
kobject_set_name(&mk->kobj, name);
kobject_register(&mk->kobj);
/* no need to keep the kobject if no parameter is exported */
if (!param_sysfs_setup(mk, kparam, num_params, name_skip))
kobject_unregister(&mk->kobj);
}
/*
* param_sysfs_builtin - add contents in /sys/parameters for built-in modules
*
* Add module_parameters to sysfs for "modules" built into the kernel.
*
* The "module" name (KBUILD_MODNAME) is stored before a dot, the
* "parameter" name is stored behind a dot in kernel_param->name. So,
* extract the "module" name for all built-in kernel_param-eters,
* and for all who have the same, call kernel_param_sysfs_setup.
*/
static void __init param_sysfs_builtin(void)
{
struct kernel_param *kp, *kp_begin = NULL;
unsigned int i, name_len, count = 0;
char modname[MAX_KBUILD_MODNAME + 1] = "";
for (i=0; i < __stop___param - __start___param; i++) {
char *dot;
kp = &__start___param[i];
/* We do not handle args without periods. */
dot = memchr(kp->name, '.', MAX_KBUILD_MODNAME);
if (!dot) {
DEBUGP("couldn't find period in %s\n", kp->name);
continue;
}
name_len = dot - kp->name;
/* new kbuild_modname? */
if (strlen(modname) != name_len
|| strncmp(modname, kp->name, name_len) != 0) {
/* add a new kobject for previous kernel_params. */
if (count)
kernel_param_sysfs_setup(modname,
kp_begin,
count,
strlen(modname)+1);
strncpy(modname, kp->name, name_len);
modname[name_len] = '\0';
count = 0;
kp_begin = kp;
}
count++;
}
/* last kernel_params need to be registered as well */
if (count)
kernel_param_sysfs_setup(modname, kp_begin, count,
strlen(modname)+1);
}
/* module-related sysfs stuff */
#ifdef CONFIG_MODULES
#define to_module_attr(n) container_of(n, struct module_attribute, attr);
#define to_module_kobject(n) container_of(n, struct module_kobject, kobj);
static ssize_t module_attr_show(struct kobject *kobj,
struct attribute *attr,
char *buf)
{
struct module_attribute *attribute;
struct module_kobject *mk;
int ret;
attribute = to_module_attr(attr);
mk = to_module_kobject(kobj);
if (!attribute->show)
return -EPERM;
if (!try_module_get(mk->mod))
return -ENODEV;
ret = attribute->show(mk->mod, buf);
module_put(mk->mod);
return ret;
}
static struct sysfs_ops module_sysfs_ops = {
.show = module_attr_show,
.store = NULL,
};
#else
static struct sysfs_ops module_sysfs_ops = {
.show = NULL,
.store = NULL,
};
#endif
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,
};
decl_subsys(module, &module_ktype, NULL);
/*
* param_sysfs_init - wrapper for built-in params support
*/
static int __init param_sysfs_init(void)
{
subsystem_register(&module_subsys);
kobject_set_name(&param_kset.kobj, "parameters");
kset_init(&param_kset);
param_sysfs_builtin();
return 0;
}
__initcall(param_sysfs_init);
EXPORT_SYMBOL(param_set_byte); EXPORT_SYMBOL(param_set_byte);
EXPORT_SYMBOL(param_get_byte); EXPORT_SYMBOL(param_get_byte);
EXPORT_SYMBOL(param_set_short); EXPORT_SYMBOL(param_set_short);
......
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