param: locking for kernel parameters
There may be cases (most obviously, sysfs-writable charp parameters) where a module needs to prevent sysfs access to parameters. Rather than express this in terms of a big lock, the functions are expressed in terms of what they protect against. This is clearer, esp. if the implementation changes to a module-level or even param-level lock. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Reviewed-by: Takashi Iwai <tiwai@suse.de> Tested-by: Phil Carmody <ext-phil.2.carmody@nokia.com>
This commit is contained in:
		| @@ -130,6 +130,62 @@ __check_old_set_param(int (*oldset)(const char *, struct kernel_param *)) | ||||
| #define module_param(name, type, perm)				\ | ||||
| 	module_param_named(name, name, type, perm) | ||||
|  | ||||
| /** | ||||
|  * kparam_block_sysfs_write - make sure a parameter isn't written via sysfs. | ||||
|  * @name: the name of the parameter | ||||
|  * | ||||
|  * There's no point blocking write on a paramter that isn't writable via sysfs! | ||||
|  */ | ||||
| #define kparam_block_sysfs_write(name)			\ | ||||
| 	do {						\ | ||||
| 		BUG_ON(!(__param_##name.perm & 0222));	\ | ||||
| 		__kernel_param_lock();			\ | ||||
| 	} while (0) | ||||
|  | ||||
| /** | ||||
|  * kparam_unblock_sysfs_write - allows sysfs to write to a parameter again. | ||||
|  * @name: the name of the parameter | ||||
|  */ | ||||
| #define kparam_unblock_sysfs_write(name)		\ | ||||
| 	do {						\ | ||||
| 		BUG_ON(!(__param_##name.perm & 0222));	\ | ||||
| 		__kernel_param_unlock();		\ | ||||
| 	} while (0) | ||||
|  | ||||
| /** | ||||
|  * kparam_block_sysfs_read - make sure a parameter isn't read via sysfs. | ||||
|  * @name: the name of the parameter | ||||
|  * | ||||
|  * This also blocks sysfs writes. | ||||
|  */ | ||||
| #define kparam_block_sysfs_read(name)			\ | ||||
| 	do {						\ | ||||
| 		BUG_ON(!(__param_##name.perm & 0444));	\ | ||||
| 		__kernel_param_lock();			\ | ||||
| 	} while (0) | ||||
|  | ||||
| /** | ||||
|  * kparam_unblock_sysfs_read - allows sysfs to read a parameter again. | ||||
|  * @name: the name of the parameter | ||||
|  */ | ||||
| #define kparam_unblock_sysfs_read(name)			\ | ||||
| 	do {						\ | ||||
| 		BUG_ON(!(__param_##name.perm & 0444));	\ | ||||
| 		__kernel_param_unlock();		\ | ||||
| 	} while (0) | ||||
|  | ||||
| #ifdef CONFIG_SYSFS | ||||
| extern void __kernel_param_lock(void); | ||||
| extern void __kernel_param_unlock(void); | ||||
| #else | ||||
| static inline void __kernel_param_lock(void) | ||||
| { | ||||
| } | ||||
| static inline void __kernel_param_unlock(void) | ||||
| { | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifndef MODULE | ||||
| /** | ||||
|  * core_param - define a historical core kernel parameter. | ||||
|   | ||||
| @@ -31,12 +31,14 @@ | ||||
| #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 DEFINE_MUTEX(param_lock); | ||||
| static LIST_HEAD(kmalloced_params); | ||||
|  | ||||
| static void *kmalloc_parameter(unsigned int size) | ||||
| @@ -47,10 +49,7 @@ static void *kmalloc_parameter(unsigned int size) | ||||
| 	if (!p) | ||||
| 		return NULL; | ||||
|  | ||||
| 	mutex_lock(¶m_lock); | ||||
| 	list_add(&p->list, &kmalloced_params); | ||||
| 	mutex_unlock(¶m_lock); | ||||
|  | ||||
| 	return p->val; | ||||
| } | ||||
|  | ||||
| @@ -59,7 +58,6 @@ static void maybe_kfree_parameter(void *param) | ||||
| { | ||||
| 	struct kmalloced_param *p; | ||||
|  | ||||
| 	mutex_lock(¶m_lock); | ||||
| 	list_for_each_entry(p, &kmalloced_params, list) { | ||||
| 		if (p->val == param) { | ||||
| 			list_del(&p->list); | ||||
| @@ -67,7 +65,6 @@ static void maybe_kfree_parameter(void *param) | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	mutex_unlock(¶m_lock); | ||||
| } | ||||
|  | ||||
| static inline char dash2underscore(char c) | ||||
| @@ -93,6 +90,7 @@ static int parse_one(char *param, | ||||
| 		     int (*handle_unknown)(char *param, char *val)) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 	int err; | ||||
|  | ||||
| 	/* Find parameter */ | ||||
| 	for (i = 0; i < num_params; i++) { | ||||
| @@ -102,7 +100,10 @@ static int parse_one(char *param, | ||||
| 				return -EINVAL; | ||||
| 			DEBUGP("They are equal!  Calling %p\n", | ||||
| 			       params[i].ops->set); | ||||
| 			return params[i].ops->set(val, ¶ms[i]); | ||||
| 			mutex_lock(¶m_lock); | ||||
| 			err = params[i].ops->set(val, ¶ms[i]); | ||||
| 			mutex_unlock(¶m_lock); | ||||
| 			return err; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -400,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) | ||||
| @@ -438,6 +440,7 @@ static int param_array_get(char *buffer, const struct kernel_param *kp) | ||||
| 		if (i) | ||||
| 			buffer[off++] = ','; | ||||
| 		p.arg = arr->elem + arr->elemsize * i; | ||||
| 		BUG_ON(!mutex_is_locked(¶m_lock)); | ||||
| 		ret = arr->ops->get(buffer + off, &p); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| @@ -522,7 +525,9 @@ static ssize_t param_attr_show(struct module_attribute *mattr, | ||||
| 	if (!attribute->param->ops->get) | ||||
| 		return -EPERM; | ||||
|  | ||||
| 	mutex_lock(¶m_lock); | ||||
| 	count = attribute->param->ops->get(buf, attribute->param); | ||||
| 	mutex_unlock(¶m_lock); | ||||
| 	if (count > 0) { | ||||
| 		strcat(buf, "\n"); | ||||
| 		++count; | ||||
| @@ -541,7 +546,9 @@ static ssize_t param_attr_store(struct module_attribute *mattr, | ||||
| 	if (!attribute->param->ops->set) | ||||
| 		return -EPERM; | ||||
|  | ||||
| 	mutex_lock(¶m_lock); | ||||
| 	err = attribute->param->ops->set(buf, attribute->param); | ||||
| 	mutex_unlock(¶m_lock); | ||||
| 	if (!err) | ||||
| 		return len; | ||||
| 	return err; | ||||
| @@ -555,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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user