]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - kernel/params.c
module: add per-module param_lock
[karo-tx-linux.git] / kernel / params.c
index a22d6a759b1a9391d5713364097bcfca35f197e7..8890d0b8dffceda61f667ce08195ef479fd04b36 100644 (file)
 #include <linux/slab.h>
 #include <linux/ctype.h>
 
-/* Protects all parameters, and incidentally kmalloced_param list. */
+/* Protects all built-in parameters, modules use their own param_lock */
 static DEFINE_MUTEX(param_lock);
 
+/* Use the module's mutex, or if built-in use the built-in mutex */
+#define KPARAM_MUTEX(mod)      ((mod) ? &(mod)->param_lock : &param_lock)
+#define KPARAM_IS_LOCKED(mod)  mutex_is_locked(KPARAM_MUTEX(mod))
+
 /* This just allows us to keep track of which parameters are kmalloced. */
 struct kmalloced_param {
        struct list_head list;
        char val[];
 };
 static LIST_HEAD(kmalloced_params);
+static DEFINE_SPINLOCK(kmalloced_params_lock);
 
 static void *kmalloc_parameter(unsigned int size)
 {
@@ -43,7 +48,10 @@ static void *kmalloc_parameter(unsigned int size)
        if (!p)
                return NULL;
 
+       spin_lock(&kmalloced_params_lock);
        list_add(&p->list, &kmalloced_params);
+       spin_unlock(&kmalloced_params_lock);
+
        return p->val;
 }
 
@@ -52,6 +60,7 @@ static void maybe_kfree_parameter(void *param)
 {
        struct kmalloced_param *p;
 
+       spin_lock(&kmalloced_params_lock);
        list_for_each_entry(p, &kmalloced_params, list) {
                if (p->val == param) {
                        list_del(&p->list);
@@ -59,6 +68,7 @@ static void maybe_kfree_parameter(void *param)
                        break;
                }
        }
+       spin_unlock(&kmalloced_params_lock);
 }
 
 static char dash2underscore(char c)
@@ -118,10 +128,10 @@ static int parse_one(char *param,
                                return -EINVAL;
                        pr_debug("handling %s with %p\n", param,
                                params[i].ops->set);
-                       mutex_lock(&param_lock);
+                       kernel_param_lock(params[i].mod);
                        param_check_unsafe(&params[i]);
                        err = params[i].ops->set(val, &params[i]);
-                       mutex_unlock(&param_lock);
+                       kernel_param_unlock(params[i].mod);
                        return err;
                }
        }
@@ -251,7 +261,7 @@ char *parse_args(const char *doing,
                return scnprintf(buffer, PAGE_SIZE, format,             \
                                *((type *)kp->arg));                    \
        }                                                               \
-       struct kernel_param_ops param_ops_##name = {                    \
+       const struct kernel_param_ops param_ops_##name = {                      \
                .set = param_set_##name,                                \
                .get = param_get_##name,                                \
        };                                                              \
@@ -303,7 +313,7 @@ static void param_free_charp(void *arg)
        maybe_kfree_parameter(*((char **)arg));
 }
 
-struct kernel_param_ops param_ops_charp = {
+const struct kernel_param_ops param_ops_charp = {
        .set = param_set_charp,
        .get = param_get_charp,
        .free = param_free_charp,
@@ -328,13 +338,44 @@ int param_get_bool(char *buffer, const struct kernel_param *kp)
 }
 EXPORT_SYMBOL(param_get_bool);
 
-struct kernel_param_ops param_ops_bool = {
+const struct kernel_param_ops param_ops_bool = {
        .flags = KERNEL_PARAM_OPS_FL_NOARG,
        .set = param_set_bool,
        .get = param_get_bool,
 };
 EXPORT_SYMBOL(param_ops_bool);
 
+int param_set_bool_enable_only(const char *val, const struct kernel_param *kp)
+{
+       int err = 0;
+       bool new_value;
+       bool orig_value = *(bool *)kp->arg;
+       struct kernel_param dummy_kp = *kp;
+
+       dummy_kp.arg = &new_value;
+
+       err = param_set_bool(val, &dummy_kp);
+       if (err)
+               return err;
+
+       /* Don't let them unset it once it's set! */
+       if (!new_value && orig_value)
+               return -EROFS;
+
+       if (new_value)
+               err = param_set_bool(val, kp);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(param_set_bool_enable_only);
+
+const struct kernel_param_ops param_ops_bool_enable_only = {
+       .flags = KERNEL_PARAM_OPS_FL_NOARG,
+       .set = param_set_bool_enable_only,
+       .get = param_get_bool,
+};
+EXPORT_SYMBOL_GPL(param_ops_bool_enable_only);
+
 /* This one must be bool. */
 int param_set_invbool(const char *val, const struct kernel_param *kp)
 {
@@ -356,7 +397,7 @@ int param_get_invbool(char *buffer, const struct kernel_param *kp)
 }
 EXPORT_SYMBOL(param_get_invbool);
 
-struct kernel_param_ops param_ops_invbool = {
+const struct kernel_param_ops param_ops_invbool = {
        .set = param_set_invbool,
        .get = param_get_invbool,
 };
@@ -364,12 +405,11 @@ EXPORT_SYMBOL(param_ops_invbool);
 
 int param_set_bint(const char *val, const struct kernel_param *kp)
 {
-       struct kernel_param boolkp;
+       /* Match bool exactly, by re-using it. */
+       struct kernel_param boolkp = *kp;
        bool v;
        int ret;
 
-       /* Match bool exactly, by re-using it. */
-       boolkp = *kp;
        boolkp.arg = &v;
 
        ret = param_set_bool(val, &boolkp);
@@ -379,7 +419,7 @@ int param_set_bint(const char *val, const struct kernel_param *kp)
 }
 EXPORT_SYMBOL(param_set_bint);
 
-struct kernel_param_ops param_ops_bint = {
+const struct kernel_param_ops param_ops_bint = {
        .flags = KERNEL_PARAM_OPS_FL_NOARG,
        .set = param_set_bint,
        .get = param_get_int,
@@ -387,7 +427,8 @@ struct kernel_param_ops param_ops_bint = {
 EXPORT_SYMBOL(param_ops_bint);
 
 /* We break the rule and mangle the string. */
-static int param_array(const char *name,
+static int param_array(struct module *mod,
+                      const char *name,
                       const char *val,
                       unsigned int min, unsigned int max,
                       void *elem, int elemsize,
@@ -418,7 +459,7 @@ static int param_array(const char *name,
                /* nul-terminate and parse */
                save = val[len];
                ((char *)val)[len] = '\0';
-               BUG_ON(!mutex_is_locked(&param_lock));
+               BUG_ON(!KPARAM_IS_LOCKED(mod));
                ret = set(val, &kp);
 
                if (ret != 0)
@@ -440,7 +481,7 @@ static int param_array_set(const char *val, const struct kernel_param *kp)
        const struct kparam_array *arr = kp->arr;
        unsigned int temp_num;
 
-       return param_array(kp->name, val, 1, arr->max, arr->elem,
+       return param_array(kp->mod, kp->name, val, 1, arr->max, arr->elem,
                           arr->elemsize, arr->ops->set, kp->level,
                           arr->num ?: &temp_num);
 }
@@ -449,14 +490,13 @@ static int param_array_get(char *buffer, const struct kernel_param *kp)
 {
        int i, off, ret;
        const struct kparam_array *arr = kp->arr;
-       struct kernel_param p;
+       struct kernel_param p = *kp;
 
-       p = *kp;
        for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) {
                if (i)
                        buffer[off++] = ',';
                p.arg = arr->elem + arr->elemsize * i;
-               BUG_ON(!mutex_is_locked(&param_lock));
+               BUG_ON(!KPARAM_IS_LOCKED(p.mod));
                ret = arr->ops->get(buffer + off, &p);
                if (ret < 0)
                        return ret;
@@ -476,7 +516,7 @@ static void param_array_free(void *arg)
                        arr->ops->free(arr->elem + arr->elemsize * i);
 }
 
-struct kernel_param_ops param_array_ops = {
+const struct kernel_param_ops param_array_ops = {
        .set = param_array_set,
        .get = param_array_get,
        .free = param_array_free,
@@ -504,7 +544,7 @@ int param_get_string(char *buffer, const struct kernel_param *kp)
 }
 EXPORT_SYMBOL(param_get_string);
 
-struct kernel_param_ops param_ops_string = {
+const struct kernel_param_ops param_ops_string = {
        .set = param_set_copystring,
        .get = param_get_string,
 };
@@ -539,9 +579,9 @@ static ssize_t param_attr_show(struct module_attribute *mattr,
        if (!attribute->param->ops->get)
                return -EPERM;
 
-       mutex_lock(&param_lock);
+       kernel_param_lock(mk->mod);
        count = attribute->param->ops->get(buf, attribute->param);
-       mutex_unlock(&param_lock);
+       kernel_param_unlock(mk->mod);
        if (count > 0) {
                strcat(buf, "\n");
                ++count;
@@ -551,7 +591,7 @@ static ssize_t param_attr_show(struct module_attribute *mattr,
 
 /* sysfs always hands a nul-terminated string in buf.  We rely on that. */
 static ssize_t param_attr_store(struct module_attribute *mattr,
-                               struct module_kobject *km,
+                               struct module_kobject *mk,
                                const char *buf, size_t len)
 {
        int err;
@@ -560,10 +600,10 @@ static ssize_t param_attr_store(struct module_attribute *mattr,
        if (!attribute->param->ops->set)
                return -EPERM;
 
-       mutex_lock(&param_lock);
+       kernel_param_lock(mk->mod);
        param_check_unsafe(attribute->param);
        err = attribute->param->ops->set(buf, attribute->param);
-       mutex_unlock(&param_lock);
+       kernel_param_unlock(mk->mod);
        if (!err)
                return len;
        return err;
@@ -576,18 +616,19 @@ static ssize_t param_attr_store(struct module_attribute *mattr,
 #define __modinit __init
 #endif
 
-#ifdef CONFIG_SYSFS
-void __kernel_param_lock(void)
+void kernel_param_lock(struct module *mod)
 {
-       mutex_lock(&param_lock);
+       mutex_lock(KPARAM_MUTEX(mod));
 }
-EXPORT_SYMBOL(__kernel_param_lock);
 
-void __kernel_param_unlock(void)
+void kernel_param_unlock(struct module *mod)
 {
-       mutex_unlock(&param_lock);
+       mutex_unlock(KPARAM_MUTEX(mod));
 }
-EXPORT_SYMBOL(__kernel_param_unlock);
+
+#ifdef CONFIG_SYSFS
+EXPORT_SYMBOL(kernel_param_lock);
+EXPORT_SYMBOL(kernel_param_unlock);
 
 /*
  * add_sysfs_param - add a parameter to sysfs
@@ -853,6 +894,7 @@ static void __init version_sysfs_builtin(void)
                mk = locate_module_kobject(vattr->module_name);
                if (mk) {
                        err = sysfs_create_file(&mk->kobj, &vattr->mattr.attr);
+                       WARN_ON_ONCE(err);
                        kobject_uevent(&mk->kobj, KOBJ_ADD);
                        kobject_put(&mk->kobj);
                }