diff options
Diffstat (limited to 'kernel/params.c')
| -rw-r--r-- | kernel/params.c | 295 | 
1 files changed, 178 insertions, 117 deletions
diff --git a/kernel/params.c b/kernel/params.c index 08107d18175..1e52ca233fd 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -15,7 +15,6 @@      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/moduleparam.h>  #include <linux/kernel.h>  #include <linux/string.h>  #include <linux/errno.h> @@ -25,12 +24,6 @@  #include <linux/slab.h>  #include <linux/ctype.h> -#if 0 -#define DEBUGP printk -#else -#define DEBUGP(fmt, a...) -#endif -  /* Protects all parameters, and incidentally kmalloced_param list. */  static DEFINE_MUTEX(param_lock); @@ -67,27 +60,38 @@ static void maybe_kfree_parameter(void *param)  	}  } -static inline char dash2underscore(char c) +static char dash2underscore(char c)  {  	if (c == '-')  		return '_';  	return c;  } -static inline int parameq(const char *input, const char *paramname) +bool parameqn(const char *a, const char *b, size_t n)  { -	unsigned int i; -	for (i = 0; dash2underscore(input[i]) == paramname[i]; i++) -		if (input[i] == '\0') -			return 1; -	return 0; +	size_t i; + +	for (i = 0; i < n; i++) { +		if (dash2underscore(a[i]) != dash2underscore(b[i])) +			return false; +	} +	return true; +} + +bool parameq(const char *a, const char *b) +{ +	return parameqn(a, b, strlen(a)+1);  }  static int parse_one(char *param,  		     char *val, +		     const char *doing,  		     const struct kernel_param *params,  		     unsigned num_params, -		     int (*handle_unknown)(char *param, char *val)) +		     s16 min_level, +		     s16 max_level, +		     int (*handle_unknown)(char *param, char *val, +				     const char *doing))  {  	unsigned int i;  	int err; @@ -95,11 +99,15 @@ static int parse_one(char *param,  	/* 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) +			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_FL_NOARG))  				return -EINVAL; -			DEBUGP("They are equal!  Calling %p\n", -			       params[i].ops->set); +			pr_debug("handling %s with %p\n", param, +				params[i].ops->set);  			mutex_lock(¶m_lock);  			err = params[i].ops->set(val, ¶ms[i]);  			mutex_unlock(¶m_lock); @@ -108,11 +116,11 @@ static int parse_one(char *param,  	}  	if (handle_unknown) { -		DEBUGP("Unknown argument: calling %p\n", handle_unknown); -		return handle_unknown(param, val); +		pr_debug("doing %s: %s='%s'\n", doing, param, val); +		return handle_unknown(param, val, doing);  	} -	DEBUGP("Unknown argument `%s'\n", param); +	pr_debug("Unknown argument '%s'\n", param);  	return -ENOENT;  } @@ -169,70 +177,68 @@ 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, -	       const struct kernel_param *params, -	       unsigned num, -	       int (*unknown)(char *param, char *val)) +char *parse_args(const char *doing, +		 char *args, +		 const struct kernel_param *params, +		 unsigned num, +		 s16 min_level, +		 s16 max_level, +		 int (*unknown)(char *param, char *val, const char *doing))  {  	char *param, *val; -	DEBUGP("Parsing ARGS: %s\n", args); -  	/* 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, ¶m, &val); +		/* Stop at -- */ +		if (!val && strcmp(param, "--") == 0) +			return args;  		irq_was_disabled = irqs_disabled(); -		ret = parse_one(param, val, params, num, unknown); -		if (irq_was_disabled && !irqs_disabled()) { -			printk(KERN_WARNING "parse_args(): option '%s' enabled " -					"irq's!\n", param); -		} +		ret = parse_one(param, val, doing, params, num, +				min_level, max_level, unknown); +		if (irq_was_disabled && !irqs_disabled()) +			pr_warn("%s: option '%s' enabled irq's!\n", +				doing, param); +  		switch (ret) {  		case -ENOENT: -			printk(KERN_ERR "%s: Unknown parameter `%s'\n", -			       name, param); -			return ret; +			pr_err("%s: Unknown parameter `%s'\n", doing, param); +			return ERR_PTR(ret);  		case -ENOSPC: -			printk(KERN_ERR -			       "%s: `%s' too large for parameter `%s'\n", -			       name, val ?: "", param); -			return ret; +			pr_err("%s: `%s' too large for parameter `%s'\n", +			       doing, val ?: "", param); +			return ERR_PTR(ret);  		case 0:  			break;  		default: -			printk(KERN_ERR -			       "%s: `%s' invalid for parameter `%s'\n", -			       name, val ?: "", param); -			return ret; +			pr_err("%s: `%s' invalid for parameter `%s'\n", +			       doing, val ?: "", param); +			return ERR_PTR(ret);  		}  	}  	/* All parsed OK. */ -	return 0; +	return NULL;  }  /* Lazy bastard, eh? */ -#define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn)      	\ +#define STANDARD_PARAM_DEF(name, type, format, strtolfn)      		\  	int param_set_##name(const char *val, const struct kernel_param *kp) \  	{								\ -		tmptype l;						\ -		int ret;						\ -									\ -		ret = strtolfn(val, 0, &l);				\ -		if (ret == -EINVAL || ((type)l != l))			\ -			return -EINVAL;					\ -		*((type *)kp->arg) = l;					\ -		return 0;						\ +		return strtolfn(val, 0, (type *)kp->arg);		\  	}								\  	int param_get_##name(char *buffer, const struct kernel_param *kp) \  	{								\ -		return sprintf(buffer, format, *((type *)kp->arg));	\ +		return scnprintf(buffer, PAGE_SIZE, format,		\ +				*((type *)kp->arg));			\  	}								\  	struct kernel_param_ops param_ops_##name = {			\  		.set = param_set_##name,				\ @@ -243,19 +249,18 @@ int parse_args(const char *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); -STANDARD_PARAM_DEF(ushort, unsigned short, "%hu", unsigned long, strict_strtoul); -STANDARD_PARAM_DEF(int, int, "%i", long, strict_strtol); -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); +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);  int param_set_charp(const char *val, const struct kernel_param *kp)  {  	if (strlen(val) > 1024) { -		printk(KERN_ERR "%s: string parameter too long\n", -		       kp->name); +		pr_err("%s: string parameter too long\n", kp->name);  		return -ENOSPC;  	} @@ -277,7 +282,7 @@ EXPORT_SYMBOL(param_set_charp);  int param_get_charp(char *buffer, const struct kernel_param *kp)  { -	return sprintf(buffer, "%s", *((char **)kp->arg)); +	return scnprintf(buffer, PAGE_SIZE, "%s", *((char **)kp->arg));  }  EXPORT_SYMBOL(param_get_charp); @@ -296,45 +301,23 @@ 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)  { -	bool v; -  	/* No equals means "set"... */  	if (!val) val = "1";  	/* One of =[yYnN01] */ -	switch (val[0]) { -	case 'y': case 'Y': case '1': -		v = true; -		break; -	case 'n': case 'N': case '0': -		v = false; -		break; -	default: -		return -EINVAL; -	} - -	if (kp->flags & KPARAM_ISBOOL) -		*(bool *)kp->arg = v; -	else -		*(int *)kp->arg = v; -	return 0; +	return strtobool(val, kp->arg);  }  EXPORT_SYMBOL(param_set_bool);  int param_get_bool(char *buffer, const struct kernel_param *kp)  { -	bool val; -	if (kp->flags & KPARAM_ISBOOL) -		val = *(bool *)kp->arg; -	else -		val = *(int *)kp->arg; -  	/* Y and N chosen as being relatively non-coder friendly */ -	return sprintf(buffer, "%c", val ? 'Y' : 'N'); +	return sprintf(buffer, "%c", *(bool *)kp->arg ? 'Y' : 'N');  }  EXPORT_SYMBOL(param_get_bool);  struct kernel_param_ops param_ops_bool = { +	.flags = KERNEL_PARAM_FL_NOARG,  	.set = param_set_bool,  	.get = param_get_bool,  }; @@ -348,7 +331,6 @@ int param_set_invbool(const char *val, const struct kernel_param *kp)  	struct kernel_param dummy;  	dummy.arg = &boolval; -	dummy.flags = KPARAM_ISBOOL;  	ret = param_set_bool(val, &dummy);  	if (ret == 0)  		*(bool *)kp->arg = !boolval; @@ -368,13 +350,37 @@ struct kernel_param_ops param_ops_invbool = {  };  EXPORT_SYMBOL(param_ops_invbool); +int param_set_bint(const char *val, const struct kernel_param *kp) +{ +	struct kernel_param boolkp; +	bool v; +	int ret; + +	/* Match bool exactly, by re-using it. */ +	boolkp = *kp; +	boolkp.arg = &v; + +	ret = param_set_bool(val, &boolkp); +	if (ret == 0) +		*(int *)kp->arg = v; +	return ret; +} +EXPORT_SYMBOL(param_set_bint); + +struct kernel_param_ops param_ops_bint = { +	.flags = KERNEL_PARAM_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(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), -		       u16 flags, +		       s16 level,  		       unsigned int *num)  {  	int ret; @@ -384,7 +390,7 @@ static int param_array(const char *name,  	/* Get the name right for errors. */  	kp.name = name;  	kp.arg = elem; -	kp.flags = flags; +	kp.level = level;  	*num = 0;  	/* We expect a comma-separated list of values. */ @@ -392,8 +398,7 @@ static int param_array(const char *name,  		int len;  		if (*num == max) { -			printk(KERN_ERR "%s: can only take %i arguments\n", -			       name, max); +			pr_err("%s: can only take %i arguments\n", name, max);  			return -EINVAL;  		}  		len = strcspn(val, ","); @@ -412,8 +417,7 @@ static int param_array(const char *name,  	} while (save == ',');  	if (*num < min) { -		printk(KERN_ERR "%s: needs at least %i arguments\n", -		       name, min); +		pr_err("%s: needs at least %i arguments\n", name, min);  		return -EINVAL;  	}  	return 0; @@ -425,7 +429,7 @@ static int param_array_set(const char *val, const struct kernel_param *kp)  	unsigned int temp_num;  	return param_array(kp->name, val, 1, arr->max, arr->elem, -			   arr->elemsize, arr->ops->set, kp->flags, +			   arr->elemsize, arr->ops->set, kp->level,  			   arr->num ?: &temp_num);  } @@ -472,7 +476,7 @@ 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) { -		printk(KERN_ERR "%s: string doesn't fit in %u chars.\n", +		pr_err("%s: string doesn't fit in %u chars.\n",  		       kp->name, kps->maxlen-1);  		return -ENOSPC;  	} @@ -517,7 +521,7 @@ struct module_param_attrs  #define to_param_attr(n) container_of(n, struct param_attribute, mattr)  static ssize_t param_attr_show(struct module_attribute *mattr, -			       struct module *mod, char *buf) +			       struct module_kobject *mk, char *buf)  {  	int count;  	struct param_attribute *attribute = to_param_attr(mattr); @@ -537,7 +541,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 *owner, +				struct module_kobject *km,  				const char *buf, size_t len)  {   	int err; @@ -608,10 +612,13 @@ static __modinit int add_sysfs_param(struct module_kobject *mk,  		       sizeof(*mk->mp) + sizeof(mk->mp->attrs[0]) * (num+1),  		       GFP_KERNEL);  	if (!new) { -		kfree(mk->mp); +		kfree(attrs);  		err = -ENOMEM;  		goto fail;  	} +	/* Despite looking like the typical realloc() bug, this is safe. +	 * We *want* the old 'attrs' to be freed either way, and we'll store +	 * the new one in the success case. */  	attrs = krealloc(attrs, sizeof(new->grp.attrs[0])*(num+2), GFP_KERNEL);  	if (!attrs) {  		err = -ENOMEM; @@ -719,9 +726,7 @@ void destroy_params(const struct kernel_param *params, unsigned num)  			params[i].ops->free(params[i].arg);  } -static void __init kernel_add_sysfs_param(const char *name, -					  struct kernel_param *kparam, -					  unsigned int name_skip) +static struct module_kobject * __init locate_module_kobject(const char *name)  {  	struct module_kobject *mk;  	struct kobject *kobj; @@ -729,10 +734,7 @@ static void __init kernel_add_sysfs_param(const char *name,  	kobj = kset_find_obj(module_kset, name);  	if (kobj) { -		/* We already have one.  Remove params so we can add more. */  		mk = to_module_kobject(kobj); -		/* We need to remove it before adding parameters. */ -		sysfs_remove_group(&mk->kobj, &mk->mp->grp);  	} else {  		mk = kzalloc(sizeof(struct module_kobject), GFP_KERNEL);  		BUG_ON(!mk); @@ -741,17 +743,39 @@ static void __init kernel_add_sysfs_param(const char *name,  		mk->kobj.kset = module_kset;  		err = kobject_init_and_add(&mk->kobj, &module_ktype, NULL,  					   "%s", name); +#ifdef CONFIG_MODULES +		if (!err) +			err = sysfs_create_file(&mk->kobj, &module_uevent.attr); +#endif  		if (err) {  			kobject_put(&mk->kobj); -			printk(KERN_ERR "Module '%s' failed add to sysfs, " -			       "error number %d\n", name, err); -			printk(KERN_ERR	"The system will be unstable now.\n"); -			return; +			pr_crit("Adding module '%s' to sysfs failed (%d), the system may be unstable.\n", +				name, err); +			return NULL;  		} -		/* So that exit path is even. */ + +		/* So that we hold reference in both cases. */  		kobject_get(&mk->kobj);  	} +	return mk; +} + +static void __init kernel_add_sysfs_param(const char *name, +					  struct kernel_param *kparam, +					  unsigned int name_skip) +{ +	struct module_kobject *mk; +	int err; + +	mk = locate_module_kobject(name); +	if (!mk) +		return; + +	/* We need to remove old parameters before adding more. */ +	if (mk->mp) +		sysfs_remove_group(&mk->kobj, &mk->mp->grp); +  	/* These should not fail at boot. */  	err = add_sysfs_param(mk, kparam, kparam->name + name_skip);  	BUG_ON(err); @@ -762,7 +786,7 @@ static void __init kernel_add_sysfs_param(const char *name,  }  /* - * param_sysfs_builtin - add contents in /sys/parameters for built-in modules + * param_sysfs_builtin - add sysfs parameters for built-in modules   *   * Add module_parameters to sysfs for "modules" built into the kernel.   * @@ -796,6 +820,35 @@ static void __init param_sysfs_builtin(void)  	}  } +ssize_t __modver_version_show(struct module_attribute *mattr, +			      struct module_kobject *mk, char *buf) +{ +	struct module_version_attribute *vattr = +		container_of(mattr, struct module_version_attribute, mattr); + +	return scnprintf(buf, PAGE_SIZE, "%s\n", vattr->version); +} + +extern const struct module_version_attribute *__start___modver[]; +extern const struct module_version_attribute *__stop___modver[]; + +static void __init version_sysfs_builtin(void) +{ +	const struct module_version_attribute **p; +	struct module_kobject *mk; +	int err; + +	for (p = __start___modver; p < __stop___modver; p++) { +		const struct module_version_attribute *vattr = *p; + +		mk = locate_module_kobject(vattr->module_name); +		if (mk) { +			err = sysfs_create_file(&mk->kobj, &vattr->mattr.attr); +			kobject_uevent(&mk->kobj, KOBJ_ADD); +			kobject_put(&mk->kobj); +		} +	} +}  /* module-related sysfs stuff */ @@ -813,7 +866,7 @@ static ssize_t module_attr_show(struct kobject *kobj,  	if (!attribute->show)  		return -EIO; -	ret = attribute->show(attribute, mk->mod, buf); +	ret = attribute->show(attribute, mk, buf);  	return ret;  } @@ -832,7 +885,7 @@ static ssize_t module_attr_store(struct kobject *kobj,  	if (!attribute->store)  		return -EIO; -	ret = attribute->store(attribute, mk->mod, buf, len); +	ret = attribute->store(attribute, mk, buf, len);  	return ret;  } @@ -858,7 +911,14 @@ static const struct kset_uevent_ops module_uevent_ops = {  struct kset *module_kset;  int module_sysfs_initialized; +static void module_kobj_release(struct kobject *kobj) +{ +	struct module_kobject *mk = to_module_kobject(kobj); +	complete(mk->kobj_completion); +} +  struct kobj_type module_ktype = { +	.release   =	module_kobj_release,  	.sysfs_ops =	&module_sysfs_ops,  }; @@ -875,6 +935,7 @@ static int __init param_sysfs_init(void)  	}  	module_sysfs_initialized = 1; +	version_sysfs_builtin();  	param_sysfs_builtin();  	return 0;  | 
