diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2010-08-12 01:04:19 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2010-08-11 09:34:20 -0400 |
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 *)) | |||
130 | #define module_param(name, type, perm) \ | 130 | #define module_param(name, type, perm) \ |
131 | module_param_named(name, name, type, perm) | 131 | module_param_named(name, name, type, perm) |
132 | 132 | ||
133 | /** | ||
134 | * kparam_block_sysfs_write - make sure a parameter isn't written via sysfs. | ||
135 | * @name: the name of the parameter | ||
136 | * | ||
137 | * There's no point blocking write on a paramter that isn't writable via sysfs! | ||
138 | */ | ||
139 | #define kparam_block_sysfs_write(name) \ | ||
140 | do { \ | ||
141 | BUG_ON(!(__param_##name.perm & 0222)); \ | ||
142 | __kernel_param_lock(); \ | ||
143 | } while (0) | ||
144 | |||
145 | /** | ||
146 | * kparam_unblock_sysfs_write - allows sysfs to write to a parameter again. | ||
147 | * @name: the name of the parameter | ||
148 | */ | ||
149 | #define kparam_unblock_sysfs_write(name) \ | ||
150 | do { \ | ||
151 | BUG_ON(!(__param_##name.perm & 0222)); \ | ||
152 | __kernel_param_unlock(); \ | ||
153 | } while (0) | ||
154 | |||
155 | /** | ||
156 | * kparam_block_sysfs_read - make sure a parameter isn't read via sysfs. | ||
157 | * @name: the name of the parameter | ||
158 | * | ||
159 | * This also blocks sysfs writes. | ||
160 | */ | ||
161 | #define kparam_block_sysfs_read(name) \ | ||
162 | do { \ | ||
163 | BUG_ON(!(__param_##name.perm & 0444)); \ | ||
164 | __kernel_param_lock(); \ | ||
165 | } while (0) | ||
166 | |||
167 | /** | ||
168 | * kparam_unblock_sysfs_read - allows sysfs to read a parameter again. | ||
169 | * @name: the name of the parameter | ||
170 | */ | ||
171 | #define kparam_unblock_sysfs_read(name) \ | ||
172 | do { \ | ||
173 | BUG_ON(!(__param_##name.perm & 0444)); \ | ||
174 | __kernel_param_unlock(); \ | ||
175 | } while (0) | ||
176 | |||
177 | #ifdef CONFIG_SYSFS | ||
178 | extern void __kernel_param_lock(void); | ||
179 | extern void __kernel_param_unlock(void); | ||
180 | #else | ||
181 | static inline void __kernel_param_lock(void) | ||
182 | { | ||
183 | } | ||
184 | static inline void __kernel_param_unlock(void) | ||
185 | { | ||
186 | } | ||
187 | #endif | ||
188 | |||
133 | #ifndef MODULE | 189 | #ifndef MODULE |
134 | /** | 190 | /** |
135 | * core_param - define a historical core kernel parameter. | 191 | * 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 @@ | |||
31 | #define DEBUGP(fmt, a...) | 31 | #define DEBUGP(fmt, a...) |
32 | #endif | 32 | #endif |
33 | 33 | ||
34 | /* Protects all parameters, and incidentally kmalloced_param list. */ | ||
35 | static DEFINE_MUTEX(param_lock); | ||
36 | |||
34 | /* This just allows us to keep track of which parameters are kmalloced. */ | 37 | /* This just allows us to keep track of which parameters are kmalloced. */ |
35 | struct kmalloced_param { | 38 | struct kmalloced_param { |
36 | struct list_head list; | 39 | struct list_head list; |
37 | char val[]; | 40 | char val[]; |
38 | }; | 41 | }; |
39 | static DEFINE_MUTEX(param_lock); | ||
40 | static LIST_HEAD(kmalloced_params); | 42 | static LIST_HEAD(kmalloced_params); |
41 | 43 | ||
42 | static void *kmalloc_parameter(unsigned int size) | 44 | static void *kmalloc_parameter(unsigned int size) |
@@ -47,10 +49,7 @@ static void *kmalloc_parameter(unsigned int size) | |||
47 | if (!p) | 49 | if (!p) |
48 | return NULL; | 50 | return NULL; |
49 | 51 | ||
50 | mutex_lock(¶m_lock); | ||
51 | list_add(&p->list, &kmalloced_params); | 52 | list_add(&p->list, &kmalloced_params); |
52 | mutex_unlock(¶m_lock); | ||
53 | |||
54 | return p->val; | 53 | return p->val; |
55 | } | 54 | } |
56 | 55 | ||
@@ -59,7 +58,6 @@ static void maybe_kfree_parameter(void *param) | |||
59 | { | 58 | { |
60 | struct kmalloced_param *p; | 59 | struct kmalloced_param *p; |
61 | 60 | ||
62 | mutex_lock(¶m_lock); | ||
63 | list_for_each_entry(p, &kmalloced_params, list) { | 61 | list_for_each_entry(p, &kmalloced_params, list) { |
64 | if (p->val == param) { | 62 | if (p->val == param) { |
65 | list_del(&p->list); | 63 | list_del(&p->list); |
@@ -67,7 +65,6 @@ static void maybe_kfree_parameter(void *param) | |||
67 | break; | 65 | break; |
68 | } | 66 | } |
69 | } | 67 | } |
70 | mutex_unlock(¶m_lock); | ||
71 | } | 68 | } |
72 | 69 | ||
73 | static inline char dash2underscore(char c) | 70 | static inline char dash2underscore(char c) |
@@ -93,6 +90,7 @@ static int parse_one(char *param, | |||
93 | int (*handle_unknown)(char *param, char *val)) | 90 | int (*handle_unknown)(char *param, char *val)) |
94 | { | 91 | { |
95 | unsigned int i; | 92 | unsigned int i; |
93 | int err; | ||
96 | 94 | ||
97 | /* Find parameter */ | 95 | /* Find parameter */ |
98 | for (i = 0; i < num_params; i++) { | 96 | for (i = 0; i < num_params; i++) { |
@@ -102,7 +100,10 @@ static int parse_one(char *param, | |||
102 | return -EINVAL; | 100 | return -EINVAL; |
103 | DEBUGP("They are equal! Calling %p\n", | 101 | DEBUGP("They are equal! Calling %p\n", |
104 | params[i].ops->set); | 102 | params[i].ops->set); |
105 | return params[i].ops->set(val, ¶ms[i]); | 103 | mutex_lock(¶m_lock); |
104 | err = params[i].ops->set(val, ¶ms[i]); | ||
105 | mutex_unlock(¶m_lock); | ||
106 | return err; | ||
106 | } | 107 | } |
107 | } | 108 | } |
108 | 109 | ||
@@ -400,6 +401,7 @@ static int param_array(const char *name, | |||
400 | /* nul-terminate and parse */ | 401 | /* nul-terminate and parse */ |
401 | save = val[len]; | 402 | save = val[len]; |
402 | ((char *)val)[len] = '\0'; | 403 | ((char *)val)[len] = '\0'; |
404 | BUG_ON(!mutex_is_locked(¶m_lock)); | ||
403 | ret = set(val, &kp); | 405 | ret = set(val, &kp); |
404 | 406 | ||
405 | if (ret != 0) | 407 | if (ret != 0) |
@@ -438,6 +440,7 @@ static int param_array_get(char *buffer, const struct kernel_param *kp) | |||
438 | if (i) | 440 | if (i) |
439 | buffer[off++] = ','; | 441 | buffer[off++] = ','; |
440 | p.arg = arr->elem + arr->elemsize * i; | 442 | p.arg = arr->elem + arr->elemsize * i; |
443 | BUG_ON(!mutex_is_locked(¶m_lock)); | ||
441 | ret = arr->ops->get(buffer + off, &p); | 444 | ret = arr->ops->get(buffer + off, &p); |
442 | if (ret < 0) | 445 | if (ret < 0) |
443 | return ret; | 446 | return ret; |
@@ -522,7 +525,9 @@ static ssize_t param_attr_show(struct module_attribute *mattr, | |||
522 | if (!attribute->param->ops->get) | 525 | if (!attribute->param->ops->get) |
523 | return -EPERM; | 526 | return -EPERM; |
524 | 527 | ||
528 | mutex_lock(¶m_lock); | ||
525 | count = attribute->param->ops->get(buf, attribute->param); | 529 | count = attribute->param->ops->get(buf, attribute->param); |
530 | mutex_unlock(¶m_lock); | ||
526 | if (count > 0) { | 531 | if (count > 0) { |
527 | strcat(buf, "\n"); | 532 | strcat(buf, "\n"); |
528 | ++count; | 533 | ++count; |
@@ -541,7 +546,9 @@ static ssize_t param_attr_store(struct module_attribute *mattr, | |||
541 | if (!attribute->param->ops->set) | 546 | if (!attribute->param->ops->set) |
542 | return -EPERM; | 547 | return -EPERM; |
543 | 548 | ||
549 | mutex_lock(¶m_lock); | ||
544 | err = attribute->param->ops->set(buf, attribute->param); | 550 | err = attribute->param->ops->set(buf, attribute->param); |
551 | mutex_unlock(¶m_lock); | ||
545 | if (!err) | 552 | if (!err) |
546 | return len; | 553 | return len; |
547 | return err; | 554 | return err; |
@@ -555,6 +562,18 @@ static ssize_t param_attr_store(struct module_attribute *mattr, | |||
555 | #endif | 562 | #endif |
556 | 563 | ||
557 | #ifdef CONFIG_SYSFS | 564 | #ifdef CONFIG_SYSFS |
565 | void __kernel_param_lock(void) | ||
566 | { | ||
567 | mutex_lock(¶m_lock); | ||
568 | } | ||
569 | EXPORT_SYMBOL(__kernel_param_lock); | ||
570 | |||
571 | void __kernel_param_unlock(void) | ||
572 | { | ||
573 | mutex_unlock(¶m_lock); | ||
574 | } | ||
575 | EXPORT_SYMBOL(__kernel_param_unlock); | ||
576 | |||
558 | /* | 577 | /* |
559 | * add_sysfs_param - add a parameter to sysfs | 578 | * add_sysfs_param - add a parameter to sysfs |
560 | * @mk: struct module_kobject | 579 | * @mk: struct module_kobject |