diff options
Diffstat (limited to 'kernel/params.c')
| -rw-r--r-- | kernel/params.c | 233 | 
1 files changed, 161 insertions, 72 deletions
diff --git a/kernel/params.c b/kernel/params.c index 0b30ecd53a52..08107d181758 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -31,6 +31,42 @@  #define DEBUGP(fmt, a...)  #endif +/* Protects all parameters, and incidentally kmalloced_param list. */ +static DEFINE_MUTEX(param_lock); + +/* 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 void *kmalloc_parameter(unsigned int size) +{ +	struct kmalloced_param *p; + +	p = kmalloc(sizeof(*p) + size, GFP_KERNEL); +	if (!p) +		return NULL; + +	list_add(&p->list, &kmalloced_params); +	return p->val; +} + +/* Does nothing if parameter wasn't kmalloced above. */ +static void maybe_kfree_parameter(void *param) +{ +	struct kmalloced_param *p; + +	list_for_each_entry(p, &kmalloced_params, list) { +		if (p->val == param) { +			list_del(&p->list); +			kfree(p); +			break; +		} +	} +} +  static inline char dash2underscore(char c)  {  	if (c == '-') @@ -49,18 +85,25 @@ static inline int parameq(const char *input, const char *paramname)  static int parse_one(char *param,  		     char *val, -		     struct kernel_param *params,  +		     const struct kernel_param *params,  		     unsigned num_params,  		     int (*handle_unknown)(char *param, char *val))  {  	unsigned int i; +	int err;  	/* Find parameter */  	for (i = 0; i < num_params; i++) {  		if (parameq(param, params[i].name)) { +			/* Noone handled NULL, so do it here. */ +			if (!val && params[i].ops->set != param_set_bool) +				return -EINVAL;  			DEBUGP("They are equal!  Calling %p\n", -			       params[i].set); -			return params[i].set(val, ¶ms[i]); +			       params[i].ops->set); +			mutex_lock(¶m_lock); +			err = params[i].ops->set(val, ¶ms[i]); +			mutex_unlock(¶m_lock); +			return err;  		}  	} @@ -128,7 +171,7 @@ static char *next_arg(char *args, char **param, char **val)  /* Args looks like "foo=bar,bar2 baz=fuz wiz". */  int parse_args(const char *name,  	       char *args, -	       struct kernel_param *params, +	       const struct kernel_param *params,  	       unsigned num,  	       int (*unknown)(char *param, char *val))  { @@ -176,22 +219,29 @@ int parse_args(const char *name,  /* Lazy bastard, eh? */  #define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn)      	\ -	int param_set_##name(const char *val, struct kernel_param *kp)	\ +	int param_set_##name(const char *val, const struct kernel_param *kp) \  	{								\  		tmptype l;						\  		int ret;						\  									\ -		if (!val) return -EINVAL;				\  		ret = strtolfn(val, 0, &l);				\  		if (ret == -EINVAL || ((type)l != l))			\  			return -EINVAL;					\  		*((type *)kp->arg) = l;					\  		return 0;						\  	}								\ -	int param_get_##name(char *buffer, struct kernel_param *kp)	\ +	int param_get_##name(char *buffer, const struct kernel_param *kp) \  	{								\  		return sprintf(buffer, format, *((type *)kp->arg));	\ -	} +	}								\ +	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, "%c", unsigned long, strict_strtoul);  STANDARD_PARAM_DEF(short, short, "%hi", long, strict_strtol); @@ -201,39 +251,50 @@ STANDARD_PARAM_DEF(uint, unsigned int, "%u", unsigned long, strict_strtoul);  STANDARD_PARAM_DEF(long, long, "%li", long, strict_strtol);  STANDARD_PARAM_DEF(ulong, unsigned long, "%lu", unsigned long, strict_strtoul); -int param_set_charp(const char *val, struct kernel_param *kp) +int param_set_charp(const char *val, const struct kernel_param *kp)  { -	if (!val) { -		printk(KERN_ERR "%s: string parameter expected\n", -		       kp->name); -		return -EINVAL; -	} -  	if (strlen(val) > 1024) {  		printk(KERN_ERR "%s: string parameter too long\n",  		       kp->name);  		return -ENOSPC;  	} -	/* This is a hack.  We can't need to strdup in early boot, and we +	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 = kstrdup(val, GFP_KERNEL); +		*(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;  } +EXPORT_SYMBOL(param_set_charp); -int param_get_charp(char *buffer, struct kernel_param *kp) +int param_get_charp(char *buffer, const struct kernel_param *kp)  {  	return sprintf(buffer, "%s", *((char **)kp->arg));  } +EXPORT_SYMBOL(param_get_charp); + +static void param_free_charp(void *arg) +{ +	maybe_kfree_parameter(*((char **)arg)); +} + +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, struct kernel_param *kp) +int param_set_bool(const char *val, const struct kernel_param *kp)  {  	bool v; @@ -258,8 +319,9 @@ int param_set_bool(const char *val, struct kernel_param *kp)  		*(int *)kp->arg = v;  	return 0;  } +EXPORT_SYMBOL(param_set_bool); -int param_get_bool(char *buffer, struct kernel_param *kp) +int param_get_bool(char *buffer, const struct kernel_param *kp)  {  	bool val;  	if (kp->flags & KPARAM_ISBOOL) @@ -270,9 +332,16 @@ int param_get_bool(char *buffer, struct kernel_param *kp)  	/* Y and N chosen as being relatively non-coder friendly */  	return sprintf(buffer, "%c", val ? 'Y' : 'N');  } +EXPORT_SYMBOL(param_get_bool); + +struct kernel_param_ops param_ops_bool = { +	.set = param_set_bool, +	.get = param_get_bool, +}; +EXPORT_SYMBOL(param_ops_bool);  /* This one must be bool. */ -int param_set_invbool(const char *val, struct kernel_param *kp) +int param_set_invbool(const char *val, const struct kernel_param *kp)  {  	int ret;  	bool boolval; @@ -285,18 +354,26 @@ int param_set_invbool(const char *val, struct kernel_param *kp)  		*(bool *)kp->arg = !boolval;  	return ret;  } +EXPORT_SYMBOL(param_set_invbool); -int param_get_invbool(char *buffer, struct kernel_param *kp) +int param_get_invbool(char *buffer, const struct kernel_param *kp)  {  	return sprintf(buffer, "%c", (*(bool *)kp->arg) ? 'N' : 'Y');  } +EXPORT_SYMBOL(param_get_invbool); + +struct kernel_param_ops param_ops_invbool = { +	.set = param_set_invbool, +	.get = param_get_invbool, +}; +EXPORT_SYMBOL(param_ops_invbool);  /* We break the rule and mangle the string. */  static int param_array(const char *name,  		       const char *val,  		       unsigned int min, unsigned int max,  		       void *elem, int elemsize, -		       int (*set)(const char *, struct kernel_param *kp), +		       int (*set)(const char *, const struct kernel_param *kp),  		       u16 flags,  		       unsigned int *num)  { @@ -309,12 +386,6 @@ static int param_array(const char *name,  	kp.arg = elem;  	kp.flags = flags; -	/* No equals sign? */ -	if (!val) { -		printk(KERN_ERR "%s: expects arguments\n", name); -		return -EINVAL; -	} -  	*num = 0;  	/* We expect a comma-separated list of values. */  	do { @@ -330,6 +401,7 @@ static int param_array(const char *name,  		/* nul-terminate and parse */  		save = val[len];  		((char *)val)[len] = '\0'; +		BUG_ON(!mutex_is_locked(¶m_lock));  		ret = set(val, &kp);  		if (ret != 0) @@ -347,17 +419,17 @@ static int param_array(const char *name,  	return 0;  } -int param_array_set(const char *val, struct kernel_param *kp) +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, -			   arr->elemsize, arr->set, kp->flags, +			   arr->elemsize, arr->ops->set, kp->flags,  			   arr->num ?: &temp_num);  } -int param_array_get(char *buffer, struct kernel_param *kp) +static int param_array_get(char *buffer, const struct kernel_param *kp)  {  	int i, off, ret;  	const struct kparam_array *arr = kp->arr; @@ -368,7 +440,8 @@ int param_array_get(char *buffer, struct kernel_param *kp)  		if (i)  			buffer[off++] = ',';  		p.arg = arr->elem + arr->elemsize * i; -		ret = arr->get(buffer + off, &p); +		BUG_ON(!mutex_is_locked(¶m_lock)); +		ret = arr->ops->get(buffer + off, &p);  		if (ret < 0)  			return ret;  		off += ret; @@ -377,14 +450,27 @@ int param_array_get(char *buffer, struct kernel_param *kp)  	return off;  } -int param_set_copystring(const char *val, struct kernel_param *kp) +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); +} + +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 (!val) { -		printk(KERN_ERR "%s: missing param set value\n", kp->name); -		return -EINVAL; -	}  	if (strlen(val)+1 > kps->maxlen) {  		printk(KERN_ERR "%s: string doesn't fit in %u chars.\n",  		       kp->name, kps->maxlen-1); @@ -393,12 +479,20 @@ int param_set_copystring(const char *val, struct kernel_param *kp)  	strcpy(kps->string, val);  	return 0;  } +EXPORT_SYMBOL(param_set_copystring); -int param_get_string(char *buffer, struct kernel_param *kp) +int param_get_string(char *buffer, const struct kernel_param *kp)  {  	const struct kparam_string *kps = kp->str;  	return strlcpy(buffer, kps->string, kps->maxlen);  } +EXPORT_SYMBOL(param_get_string); + +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) @@ -409,7 +503,7 @@ extern struct kernel_param __start___param[], __stop___param[];  struct param_attribute  {  	struct module_attribute mattr; -	struct kernel_param *param; +	const struct kernel_param *param;  };  struct module_param_attrs @@ -428,10 +522,12 @@ static ssize_t param_attr_show(struct module_attribute *mattr,  	int count;  	struct param_attribute *attribute = to_param_attr(mattr); -	if (!attribute->param->get) +	if (!attribute->param->ops->get)  		return -EPERM; -	count = attribute->param->get(buf, attribute->param); +	mutex_lock(¶m_lock); +	count = attribute->param->ops->get(buf, attribute->param); +	mutex_unlock(¶m_lock);  	if (count > 0) {  		strcat(buf, "\n");  		++count; @@ -447,10 +543,12 @@ static ssize_t param_attr_store(struct module_attribute *mattr,   	int err;  	struct param_attribute *attribute = to_param_attr(mattr); -	if (!attribute->param->set) +	if (!attribute->param->ops->set)  		return -EPERM; -	err = attribute->param->set(buf, attribute->param); +	mutex_lock(¶m_lock); +	err = attribute->param->ops->set(buf, attribute->param); +	mutex_unlock(¶m_lock);  	if (!err)  		return len;  	return err; @@ -464,6 +562,18 @@ static ssize_t param_attr_store(struct module_attribute *mattr,  #endif  #ifdef CONFIG_SYSFS +void __kernel_param_lock(void) +{ +	mutex_lock(¶m_lock); +} +EXPORT_SYMBOL(__kernel_param_lock); + +void __kernel_param_unlock(void) +{ +	mutex_unlock(¶m_lock); +} +EXPORT_SYMBOL(__kernel_param_unlock); +  /*   * add_sysfs_param - add a parameter to sysfs   * @mk: struct module_kobject @@ -475,7 +585,7 @@ static ssize_t param_attr_store(struct module_attribute *mattr,   * if there's an error.   */  static __modinit int add_sysfs_param(struct module_kobject *mk, -				     struct kernel_param *kp, +				     const struct kernel_param *kp,  				     const char *name)  {  	struct module_param_attrs *new; @@ -557,7 +667,7 @@ static void free_module_param_attrs(struct module_kobject *mk)   * /sys/module/[mod->name]/parameters/   */  int module_param_sysfs_setup(struct module *mod, -			     struct kernel_param *kparam, +			     const struct kernel_param *kparam,  			     unsigned int num_params)  {  	int i, err; @@ -602,7 +712,11 @@ void module_param_sysfs_remove(struct module *mod)  void destroy_params(const struct kernel_param *params, unsigned num)  { -	/* FIXME: This should free kmalloced charp parameters.  It doesn't. */ +	unsigned int i; + +	for (i = 0; i < num; i++) +		if (params[i].ops->free) +			params[i].ops->free(params[i].arg);  }  static void __init kernel_add_sysfs_param(const char *name, @@ -768,28 +882,3 @@ static int __init param_sysfs_init(void)  subsys_initcall(param_sysfs_init);  #endif /* CONFIG_SYSFS */ - -EXPORT_SYMBOL(param_set_byte); -EXPORT_SYMBOL(param_get_byte); -EXPORT_SYMBOL(param_set_short); -EXPORT_SYMBOL(param_get_short); -EXPORT_SYMBOL(param_set_ushort); -EXPORT_SYMBOL(param_get_ushort); -EXPORT_SYMBOL(param_set_int); -EXPORT_SYMBOL(param_get_int); -EXPORT_SYMBOL(param_set_uint); -EXPORT_SYMBOL(param_get_uint); -EXPORT_SYMBOL(param_set_long); -EXPORT_SYMBOL(param_get_long); -EXPORT_SYMBOL(param_set_ulong); -EXPORT_SYMBOL(param_get_ulong); -EXPORT_SYMBOL(param_set_charp); -EXPORT_SYMBOL(param_get_charp); -EXPORT_SYMBOL(param_set_bool); -EXPORT_SYMBOL(param_get_bool); -EXPORT_SYMBOL(param_set_invbool); -EXPORT_SYMBOL(param_get_invbool); -EXPORT_SYMBOL(param_array_set); -EXPORT_SYMBOL(param_array_get); -EXPORT_SYMBOL(param_set_copystring); -EXPORT_SYMBOL(param_get_string);  | 
