These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / kernel / params.c
index a22d6a7..a6d6149 100644 (file)
 #include <linux/slab.h>
 #include <linux/ctype.h>
 
-/* Protects all parameters, and incidentally kmalloced_param list. */
+#ifdef CONFIG_SYSFS
+/* 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 */
+#ifdef CONFIG_MODULES
+#define KPARAM_MUTEX(mod)      ((mod) ? &(mod)->param_lock : &param_lock)
+#else
+#define KPARAM_MUTEX(mod)      (&param_lock)
+#endif
+
+static inline void check_kparam_locked(struct module *mod)
+{
+       BUG_ON(!mutex_is_locked(KPARAM_MUTEX(mod)));
+}
+#else
+static inline void check_kparam_locked(struct module *mod)
+{
+}
+#endif /* !CONFIG_SYSFS */
+
 /* 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 +62,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 +74,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 +82,7 @@ static void maybe_kfree_parameter(void *param)
                        break;
                }
        }
+       spin_unlock(&kmalloced_params_lock);
 }
 
 static char dash2underscore(char c)
@@ -100,8 +124,9 @@ static int parse_one(char *param,
                     unsigned num_params,
                     s16 min_level,
                     s16 max_level,
+                    void *arg,
                     int (*handle_unknown)(char *param, char *val,
-                                    const char *doing))
+                                    const char *doing, void *arg))
 {
        unsigned int i;
        int err;
@@ -118,17 +143,17 @@ 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;
                }
        }
 
        if (handle_unknown) {
                pr_debug("doing %s: %s='%s'\n", doing, param, val);
-               return handle_unknown(param, val, doing);
+               return handle_unknown(param, val, doing, arg);
        }
 
        pr_debug("Unknown argument '%s'\n", param);
@@ -194,9 +219,11 @@ char *parse_args(const char *doing,
                 unsigned num,
                 s16 min_level,
                 s16 max_level,
-                int (*unknown)(char *param, char *val, const char *doing))
+                void *arg,
+                int (*unknown)(char *param, char *val,
+                               const char *doing, void *arg))
 {
-       char *param, *val;
+       char *param, *val, *err = NULL;
 
        /* Chew leading spaces */
        args = skip_spaces(args);
@@ -211,33 +238,34 @@ char *parse_args(const char *doing,
                args = next_arg(args, &param, &val);
                /* Stop at -- */
                if (!val && strcmp(param, "--") == 0)
-                       return args;
+                       return err ?: args;
                irq_was_disabled = irqs_disabled();
                ret = parse_one(param, val, doing, params, num,
-                               min_level, max_level, unknown);
+                               min_level, max_level, arg, unknown);
                if (irq_was_disabled && !irqs_disabled())
                        pr_warn("%s: option '%s' enabled irq's!\n",
                                doing, param);
 
                switch (ret) {
+               case 0:
+                       continue;
                case -ENOENT:
                        pr_err("%s: Unknown parameter `%s'\n", doing, param);
-                       return ERR_PTR(ret);
+                       break;
                case -ENOSPC:
                        pr_err("%s: `%s' too large for parameter `%s'\n",
                               doing, val ?: "", param);
-                       return ERR_PTR(ret);
-               case 0:
                        break;
                default:
                        pr_err("%s: `%s' invalid for parameter `%s'\n",
                               doing, val ?: "", param);
-                       return ERR_PTR(ret);
+                       break;
                }
+
+               err = ERR_PTR(ret);
        }
 
-       /* All parsed OK. */
-       return NULL;
+       return err;
 }
 
 /* Lazy bastard, eh? */
@@ -251,7 +279,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,                                \
        };                                                              \
@@ -298,12 +326,13 @@ int param_get_charp(char *buffer, const struct kernel_param *kp)
 }
 EXPORT_SYMBOL(param_get_charp);
 
-static void param_free_charp(void *arg)
+void param_free_charp(void *arg)
 {
        maybe_kfree_parameter(*((char **)arg));
 }
+EXPORT_SYMBOL(param_free_charp);
 
-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 +357,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 +416,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 +424,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 +438,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 +446,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 +478,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));
+               check_kparam_locked(mod);
                ret = set(val, &kp);
 
                if (ret != 0)
@@ -440,7 +500,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 +509,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));
+               check_kparam_locked(p.mod);
                ret = arr->ops->get(buffer + off, &p);
                if (ret < 0)
                        return ret;
@@ -476,7 +535,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 +563,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 +598,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 +610,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 +619,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;
@@ -577,17 +636,18 @@ static ssize_t param_attr_store(struct module_attribute *mattr,
 #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);
+
+EXPORT_SYMBOL(kernel_param_lock);
+EXPORT_SYMBOL(kernel_param_unlock);
 
 /*
  * add_sysfs_param - add a parameter to sysfs
@@ -853,6 +913,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);
                }