diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2010-08-11 23:04:19 -0600 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2010-08-11 23:04:20 +0930 |
commit | 907b29eb41aa604477a655bff7345731da94514d (patch) | |
tree | 12a7142ffa81a65da204384dfa26365d30803735 | |
parent | 914dcaa84c53f2c3efa6016efcae13fd92a8a17c (diff) |
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>
-rw-r--r-- | include/linux/moduleparam.h | 56 | ||||
-rw-r--r-- | kernel/params.c | 33 |
2 files changed, 82 insertions, 7 deletions
diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index 6d48831fe7d2..ca74a3402d63 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -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. diff --git a/kernel/params.c b/kernel/params.c index a3eeeefc9472..08107d181758 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -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 |