cregit-Linux how code gets into the kernel

Release 4.15 kernel/params.c

Directory: kernel
/* Helpers for initial module or kernel cmdline parsing
   Copyright (C) 2001 Rusty Russell.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/ctype.h>

#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))); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell2187.50%150.00%
Dan Streetman312.50%150.00%
Total24100.00%2100.00%

#else
static inline void check_kparam_locked(struct module *mod) { }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell11100.00%1100.00%
Total11100.00%1100.00%

#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) { struct kmalloced_param *p; p = kmalloc(sizeof(*p) + size, GFP_KERNEL); if (!p) return NULL; spin_lock(&kmalloced_params_lock); list_add(&p->list, &kmalloced_params); spin_unlock(&kmalloced_params_lock); return p->val; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell5582.09%150.00%
Dan Streetman1217.91%150.00%
Total67100.00%2100.00%

/* Does nothing if parameter wasn't kmalloced above. */
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); kfree(p); break; } } spin_unlock(&kmalloced_params_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell4980.33%150.00%
Dan Streetman1219.67%150.00%
Total61100.00%2100.00%


static char dash2underscore(char c) { if (c == '-') return '_'; return c; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton2095.24%150.00%
Eric Sesterhenn / Snakebyte14.76%150.00%
Total21100.00%2100.00%


bool parameqn(const char *a, const char *b, size_t n) { size_t i; for (i = 0; i < n; i++) { if (dash2underscore(a[i]) != dash2underscore(b[i])) return false; } return true; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton3253.33%150.00%
Michal Schmidt2846.67%150.00%
Total60100.00%2100.00%


bool parameq(const char *a, const char *b) { return parameqn(a, b, strlen(a)+1); }

Contributors

PersonTokensPropCommitsCommitProp
Michal Schmidt2893.33%150.00%
Andrew Morton26.67%150.00%
Total30100.00%2100.00%


static void param_check_unsafe(const struct kernel_param *kp) { if (kp->flags & KERNEL_PARAM_FL_UNSAFE) { pr_warn("Setting dangerous option %s - tainting kernel\n", kp->name); add_taint(TAINT_USER, LOCKDEP_STILL_OK); } }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell38100.00%1100.00%
Total38100.00%1100.00%


static int parse_one(char *param, char *val, const char *doing, const struct kernel_param *params, unsigned num_params, s16 min_level, s16 max_level, void *arg, int (*handle_unknown)(char *param, char *val, const char *doing, void *arg)) { unsigned int i; int err; /* Find parameter */ for (i = 0; i < num_params; i++) { if (parameq(param, params[i].name)) { if (params[i].level < min_level || params[i].level > max_level) return 0; /* No one handled NULL, so do it here. */ if (!val && !(params[i].ops->flags & KERNEL_PARAM_OPS_FL_NOARG)) return -EINVAL; pr_debug("handling %s with %p\n", param, params[i].ops->set); kernel_param_lock(params[i].mod); param_check_unsafe(&params[i]); err = params[i].ops->set(val, &params[i]); 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, arg); } pr_debug("Unknown argument '%s'\n", param); return -ENOENT; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell17066.41%640.00%
Pawel Moll2911.33%16.67%
Jim Cromie259.77%213.33%
Dan Streetman145.47%16.67%
Luis R. Rodriguez103.91%16.67%
Steven Rostedt51.95%16.67%
Andrew Morton10.39%16.67%
Lucas De Marchi10.39%16.67%
Jani Nikula10.39%16.67%
Total256100.00%15100.00%

/* Args looks like "foo=bar,bar2 baz=fuz wiz". */
char *parse_args(const char *doing, char *args, const struct kernel_param *params, unsigned num, s16 min_level, s16 max_level, void *arg, int (*unknown)(char *param, char *val, const char *doing, void *arg)) { char *param, *val, *err = NULL; /* Chew leading spaces */ args = skip_spaces(args); if (*args) pr_debug("doing %s, parsing ARGS: '%s'\n", doing, args); while (*args) { int ret; int irq_was_disabled; args = next_arg(args, &param, &val); /* Stop at -- */ if (!val && strcmp(param, "--") == 0) return err ?: args; irq_was_disabled = irqs_disabled(); ret = parse_one(param, val, doing, params, num, 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); break; case -ENOSPC: pr_err("%s: `%s' too large for parameter `%s'\n", doing, val ?: "", param); break; default: pr_err("%s: `%s' invalid for parameter `%s'\n", doing, val ?: "", param); break; } err = ERR_PTR(ret); } return err; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell15962.85%436.36%
Jim Cromie3212.65%218.18%
Ard van Breemen218.30%19.09%
Oleg Nesterov197.51%19.09%
Luis R. Rodriguez103.95%19.09%
Pawel Moll103.95%19.09%
Peter Oberparleiter20.79%19.09%
Total253100.00%11100.00%

/* Lazy bastard, eh? */ #define STANDARD_PARAM_DEF(name, type, format, strtolfn) \ int param_set_##name(const char *val, const struct kernel_param *kp) \ { \ return strtolfn(val, 0, (type *)kp->arg); \ } \ int param_get_##name(char *buffer, const struct kernel_param *kp) \ { \ return scnprintf(buffer, PAGE_SIZE, format "\n", \ *((type *)kp->arg)); \ } \ const struct kernel_param_ops param_ops_##name = { \ .set = param_set_##name, \ .get = param_get_##name, \ }; \ EXPORT_SYMBOL(param_set_##name); \ EXPORT_SYMBOL(param_get_##name); \ EXPORT_SYMBOL(param_ops_##name) STANDARD_PARAM_DEF(byte, unsigned char, "%hhu", kstrtou8); STANDARD_PARAM_DEF(short, short, "%hi", kstrtos16); STANDARD_PARAM_DEF(ushort, unsigned short, "%hu", kstrtou16); STANDARD_PARAM_DEF(int, int, "%i", kstrtoint); STANDARD_PARAM_DEF(uint, unsigned int, "%u", kstrtouint); STANDARD_PARAM_DEF(long, long, "%li", kstrtol); STANDARD_PARAM_DEF(ulong, unsigned long, "%lu", kstrtoul); STANDARD_PARAM_DEF(ullong, unsigned long long, "%llu", kstrtoull);
int param_set_charp(const char *val, const struct kernel_param *kp) { if (strlen(val) > 1024) { pr_err("%s: string parameter too long\n", kp->name); return -ENOSPC; } maybe_kfree_parameter(*(char **)kp->arg); /* This is a hack. We can't kmalloc in early boot, and we * don't need to; this mangled commandline is preserved. */ if (slab_is_available()) { *(char **)kp->arg = kmalloc_parameter(strlen(val)+1); if (!*(char **)kp->arg) return -ENOMEM; strcpy(*(char **)kp->arg, val); } else *(const char **)kp->arg = val; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell12999.23%583.33%
Jim Cromie10.77%116.67%
Total130100.00%6100.00%

EXPORT_SYMBOL(param_set_charp);
int param_get_charp(char *buffer, const struct kernel_param *kp) { return scnprintf(buffer, PAGE_SIZE, "%s\n", *((char **)kp->arg)); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell3389.19%250.00%
Chen Gang S38.11%125.00%
Jean Delvare12.70%125.00%
Total37100.00%4100.00%

EXPORT_SYMBOL(param_get_charp);
void param_free_charp(void *arg) { maybe_kfree_parameter(*((char **)arg)); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell22100.00%1100.00%
Total22100.00%1100.00%

EXPORT_SYMBOL(param_free_charp); const struct kernel_param_ops param_ops_charp = { .set = param_set_charp, .get = param_get_charp, .free = param_free_charp, }; EXPORT_SYMBOL(param_ops_charp); /* Actually could be a bool or an int, for historical reasons. */
int param_set_bool(const char *val, const struct kernel_param *kp) { /* No equals means "set"... */ if (!val) val = "1"; /* One of =[yYnN01] */ return strtobool(val, kp->arg); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell3389.19%375.00%
Jonathan Cameron410.81%125.00%
Total37100.00%4100.00%

EXPORT_SYMBOL(param_set_bool);
int param_get_bool(char *buffer, const struct kernel_param *kp) { /* Y and N chosen as being relatively non-coder friendly */ return sprintf(buffer, "%c\n", *(bool *)kp->arg ? 'Y' : 'N'); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell3697.30%480.00%
Jean Delvare12.70%120.00%
Total37100.00%5100.00%

EXPORT_SYMBOL(param_get_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; }

Contributors

PersonTokensPropCommitsCommitProp
Luis R. Rodriguez95100.00%1100.00%
Total95100.00%1100.00%

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) { int ret; bool boolval; struct kernel_param dummy; dummy.arg = &boolval; ret = param_set_bool(val, &dummy); if (ret == 0) *(bool *)kp->arg = !boolval; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell6195.31%480.00%
Jan Beulich34.69%120.00%
Total64100.00%5100.00%

EXPORT_SYMBOL(param_set_invbool);
int param_get_invbool(char *buffer, const struct kernel_param *kp) { return sprintf(buffer, "%c\n", (*(bool *)kp->arg) ? 'N' : 'Y'); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell2565.79%360.00%
Jan Beulich1231.58%120.00%
Jean Delvare12.63%120.00%
Total38100.00%5100.00%

EXPORT_SYMBOL(param_get_invbool); const struct kernel_param_ops param_ops_invbool = { .set = param_set_invbool, .get = param_get_invbool, }; EXPORT_SYMBOL(param_ops_invbool);
int param_set_bint(const char *val, const struct kernel_param *kp) { /* Match bool exactly, by re-using it. */ struct kernel_param boolkp = *kp; bool v; int ret; boolkp.arg = &v; ret = param_set_bool(val, &boolkp); if (ret == 0) *(int *)kp->arg = v; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell6394.03%150.00%
Dan Streetman45.97%150.00%
Total67100.00%2100.00%

EXPORT_SYMBOL(param_set_bint); const struct kernel_param_ops param_ops_bint = { .flags = KERNEL_PARAM_OPS_FL_NOARG, .set = param_set_bint, .get = param_get_int, }; EXPORT_SYMBOL(param_ops_bint); /* We break the rule and mangle the string. */
static int param_array(struct module *mod, const char *name, const char *val, unsigned int min, unsigned int max, void *elem, int elemsize, int (*set)(const char *, const struct kernel_param *kp), s16 level, unsigned int *num) { int ret; struct kernel_param kp; char save; /* Get the name right for errors. */ kp.name = name; kp.arg = elem; kp.level = level; *num = 0; /* We expect a comma-separated list of values. */ do { int len; if (*num == max) { pr_err("%s: can only take %i arguments\n", name, max); return -EINVAL; } len = strcspn(val, ","); /* nul-terminate and parse */ save = val[len]; ((char *)val)[len] = '\0'; check_kparam_locked(mod); ret = set(val, &kp); if (ret != 0) return ret; kp.arg += elemsize; val += len+1; (*num)++; } while (save == ','); if (*num < min) { pr_err("%s: needs at least %i arguments\n", name, min); return -EINVAL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell19685.96%650.00%
Andrew Morton187.89%18.33%
Dan Streetman62.63%18.33%
Pawel Moll41.75%18.33%
Jim Cromie20.88%18.33%
Richard Knutsson10.44%18.33%
Adrian Bunk10.44%18.33%
Total228100.00%12100.00%


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->mod, kp->name, val, 1, arr->max, arr->elem, arr->elemsize, arr->ops->set, kp->level, arr->num ?: &temp_num); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell4254.55%444.44%
Andrew Morton2329.87%111.11%
Bert Wesarg56.49%111.11%
Dan Streetman45.19%111.11%
Jan Beulich22.60%111.11%
Pawel Moll11.30%111.11%
Total77100.00%9100.00%


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 = *kp; for (i = off = 0; i < (arr->num ? *arr->num : arr->max); i++) { /* Replace \n with comma */ if (i) buffer[off - 1] = ','; p.arg = arr->elem + arr->elemsize * i; check_kparam_locked(p.mod); ret = arr->ops->get(buffer + off, &p); if (ret < 0) return ret; off += ret; } buffer[off] = '\0'; return off; }

Contributors

PersonTokensPropCommitsCommitProp
Andrew Morton7954.86%110.00%
Rusty Russell5538.19%550.00%
Dan Streetman53.47%220.00%
Jean Delvare32.08%110.00%
Jan Beulich21.39%110.00%
Total144100.00%10100.00%


static void param_array_free(void *arg) { unsigned int i; const struct kparam_array *arr = arg; if (arr->ops->free) for (i = 0; i < (arr->num ? *arr->num : arr->max); i++) arr->ops->free(arr->elem + arr->elemsize * i); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell73100.00%1100.00%
Total73100.00%1100.00%

const struct kernel_param_ops param_array_ops = { .set = param_array_set, .get = param_array_get, .free = param_array_free, }; EXPORT_SYMBOL(param_array_ops);
int param_set_copystring(const char *val, const struct kernel_param *kp) { const struct kparam_string *kps = kp->str; if (strlen(val)+1 > kps->maxlen) { pr_err("%s: string doesn't fit in %u chars.\n", kp->name, kps->maxlen-1); return -ENOSPC; } strcpy(kps->string, val); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell6995.83%250.00%
Jan Beulich22.78%125.00%
Jim Cromie11.39%125.00%
Total72100.00%4100.00%

EXPORT_SYMBOL(param_set_copystring);
int param_get_string(char *buffer, const struct kernel_param *kp) { const struct kparam_string *kps = kp->str; return scnprintf(buffer, PAGE_SIZE, "%s\n", kps->string); }

Contributors

PersonTokensPropCommitsCommitProp
Arnd Bergmann3179.49%125.00%
Jean Delvare512.82%125.00%
Jan Beulich25.13%125.00%
Rusty Russell12.56%125.00%
Total39100.00%4100.00%

EXPORT_SYMBOL(param_get_string); const struct kernel_param_ops param_ops_string = { .set = param_set_copystring, .get = param_get_string, }; EXPORT_SYMBOL(param_ops_string); /* sysfs output in /sys/modules/XYZ/parameters/ */ #define to_module_attr(n) container_of(n, struct module_attribute, attr) #define to_module_kobject(n) container_of(n, struct module_kobject, kobj) struct param_attribute { struct module_attribute mattr; const struct kernel_param *param; }; struct module_param_attrs { unsigned int num; struct attribute_group grp; struct param_attribute attrs[0]; }; #ifdef CONFIG_SYSFS #define to_param_attr(n) container_of(n, struct param_attribute, mattr)
static ssize_t param_attr_show(struct module_attribute *mattr, struct module_kobject *mk, char *buf) { int count; struct param_attribute *attribute = to_param_attr(mattr); if (!attribute->param->ops->get) return -EPERM; kernel_param_lock(mk->mod); count = attribute->param->ops->get(buf, attribute->param); kernel_param_unlock(mk->mod); return count; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell6984.15%350.00%
Dan Streetman89.76%116.67%
Tejun Heo33.66%116.67%
Kay Sievers22.44%116.67%
Total82100.00%6100.00%

/* 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 *mk, const char *buf, size_t len) { int err; struct param_attribute *attribute = to_param_attr(mattr); if (!attribute->param->ops->set) return -EPERM; kernel_param_lock(mk->mod); param_check_unsafe(attribute->param); err = attribute->param->ops->set(buf, attribute->param); kernel_param_unlock(mk->mod); if (!err) return len; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell8887.13%457.14%
Dan Streetman98.91%114.29%
Tejun Heo32.97%114.29%
Kay Sievers10.99%114.29%
Total101100.00%7100.00%

#endif #ifdef CONFIG_MODULES #define __modinit #else #define __modinit __init #endif #ifdef CONFIG_SYSFS
void kernel_param_lock(struct module *mod) { mutex_lock(KPARAM_MUTEX(mod)); }

Contributors

PersonTokensPropCommitsCommitProp
Dan Streetman950.00%150.00%
Rusty Russell950.00%150.00%
Total18100.00%2100.00%


void kernel_param_unlock(struct module *mod) { mutex_unlock(KPARAM_MUTEX(mod)); }

Contributors

PersonTokensPropCommitsCommitProp
Rusty Russell950.00%150.00%
Dan Streetman950.00%150.00%
Total18100.00%2100.00%

EXPORT_SYMBOL(kernel_param_lock); EXPORT_SYMBOL(kernel_param_unlock); /* * add_sysfs_param - add a parameter to sysfs * @mk: struct module_kobject * @kp: the actual parameter definition to add to sysfs * @name: name of parameter * * Create a kobject if for a (per-module) parameter if mp NULL, and * create file in sysfs. Returns an error on out of memory. Always cleans up * if there's an error. */
static __modinit