aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/params.c
diff options
context:
space:
mode:
authorDan Streetman <ddstreet@ieee.org>2015-06-16 16:48:52 -0400
committerRusty Russell <rusty@rustcorp.com.au>2015-06-23 01:57:38 -0400
commitb51d23e4e9fea6f264d39535c2a62d1f51e7ccc3 (patch)
tree032ebaa1088f1c20985b0872ab31a9d403a35884 /kernel/params.c
parent5104b7d7678b0029417f6ac08243773a77259ac6 (diff)
module: add per-module param_lock
Add a "param_lock" mutex to each module, and update params.c to use the correct built-in or module mutex while locking kernel params. Remove the kparam_block_sysfs_r/w() macros, replace them with direct calls to kernel_param_[un]lock(module). The kernel param code currently uses a single mutex to protect modification of any and all kernel params. While this generally works, there is one specific problem with it; a module callback function cannot safely load another module, i.e. with request_module() or even with indirect calls such as crypto_has_alg(). If the module to be loaded has any of its params configured (e.g. with a /etc/modprobe.d/* config file), then the attempt will result in a deadlock between the first module param callback waiting for modprobe, and modprobe trying to lock the single kernel param mutex to set the new module's param. This fixes that by using per-module mutexes, so that each individual module is protected against concurrent changes in its own kernel params, but is not blocked by changes to other module params. All built-in modules continue to use the built-in mutex, since they will always be loaded at runtime and references (e.g. request_module(), crypto_has_alg()) to them will never cause load-time param changing. This also simplifies the interface used by modules to block sysfs access to their params; while there are currently functions to block and unblock sysfs param access which are split up by read and write and expect a single kernel param to be passed, their actual operation is identical and applies to all params, not just the one passed to them; they simply lock and unlock the global param mutex. They are replaced with direct calls to kernel_param_[un]lock(THIS_MODULE), which locks THIS_MODULE's param_lock, or if the module is built-in, it locks the built-in mutex. Suggested-by: Rusty Russell <rusty@rustcorp.com.au> Signed-off-by: Dan Streetman <ddstreet@ieee.org> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'kernel/params.c')
-rw-r--r--kernel/params.c50
1 files changed, 31 insertions, 19 deletions
diff --git a/kernel/params.c b/kernel/params.c
index a8b09f6c87dc..8890d0b8dffc 100644
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -25,15 +25,20 @@
25#include <linux/slab.h> 25#include <linux/slab.h>
26#include <linux/ctype.h> 26#include <linux/ctype.h>
27 27
28/* Protects all parameters, and incidentally kmalloced_param list. */ 28/* Protects all built-in parameters, modules use their own param_lock */
29static DEFINE_MUTEX(param_lock); 29static DEFINE_MUTEX(param_lock);
30 30
31/* Use the module's mutex, or if built-in use the built-in mutex */
32#define KPARAM_MUTEX(mod) ((mod) ? &(mod)->param_lock : &param_lock)
33#define KPARAM_IS_LOCKED(mod) mutex_is_locked(KPARAM_MUTEX(mod))
34
31/* This just allows us to keep track of which parameters are kmalloced. */ 35/* This just allows us to keep track of which parameters are kmalloced. */
32struct kmalloced_param { 36struct kmalloced_param {
33 struct list_head list; 37 struct list_head list;
34 char val[]; 38 char val[];
35}; 39};
36static LIST_HEAD(kmalloced_params); 40static LIST_HEAD(kmalloced_params);
41static DEFINE_SPINLOCK(kmalloced_params_lock);
37 42
38static void *kmalloc_parameter(unsigned int size) 43static void *kmalloc_parameter(unsigned int size)
39{ 44{
@@ -43,7 +48,10 @@ static void *kmalloc_parameter(unsigned int size)
43 if (!p) 48 if (!p)
44 return NULL; 49 return NULL;
45 50
51 spin_lock(&kmalloced_params_lock);
46 list_add(&p->list, &kmalloced_params); 52 list_add(&p->list, &kmalloced_params);
53 spin_unlock(&kmalloced_params_lock);
54
47 return p->val; 55 return p->val;
48} 56}
49 57
@@ -52,6 +60,7 @@ static void maybe_kfree_parameter(void *param)
52{ 60{
53 struct kmalloced_param *p; 61 struct kmalloced_param *p;
54 62
63 spin_lock(&kmalloced_params_lock);
55 list_for_each_entry(p, &kmalloced_params, list) { 64 list_for_each_entry(p, &kmalloced_params, list) {
56 if (p->val == param) { 65 if (p->val == param) {
57 list_del(&p->list); 66 list_del(&p->list);
@@ -59,6 +68,7 @@ static void maybe_kfree_parameter(void *param)
59 break; 68 break;
60 } 69 }
61 } 70 }
71 spin_unlock(&kmalloced_params_lock);
62} 72}
63 73
64static char dash2underscore(char c) 74static char dash2underscore(char c)
@@ -118,10 +128,10 @@ static int parse_one(char *param,
118 return -EINVAL; 128 return -EINVAL;
119 pr_debug("handling %s with %p\n", param, 129 pr_debug("handling %s with %p\n", param,
120 params[i].ops->set); 130 params[i].ops->set);
121 mutex_lock(&param_lock); 131 kernel_param_lock(params[i].mod);
122 param_check_unsafe(&params[i]); 132 param_check_unsafe(&params[i]);
123 err = params[i].ops->set(val, &params[i]); 133 err = params[i].ops->set(val, &params[i]);
124 mutex_unlock(&param_lock); 134 kernel_param_unlock(params[i].mod);
125 return err; 135 return err;
126 } 136 }
127 } 137 }
@@ -417,7 +427,8 @@ const struct kernel_param_ops param_ops_bint = {
417EXPORT_SYMBOL(param_ops_bint); 427EXPORT_SYMBOL(param_ops_bint);
418 428
419/* We break the rule and mangle the string. */ 429/* We break the rule and mangle the string. */
420static int param_array(const char *name, 430static int param_array(struct module *mod,
431 const char *name,
421 const char *val, 432 const char *val,
422 unsigned int min, unsigned int max, 433 unsigned int min, unsigned int max,
423 void *elem, int elemsize, 434 void *elem, int elemsize,
@@ -448,7 +459,7 @@ static int param_array(const char *name,
448 /* nul-terminate and parse */ 459 /* nul-terminate and parse */
449 save = val[len]; 460 save = val[len];
450 ((char *)val)[len] = '\0'; 461 ((char *)val)[len] = '\0';
451 BUG_ON(!mutex_is_locked(&param_lock)); 462 BUG_ON(!KPARAM_IS_LOCKED(mod));
452 ret = set(val, &kp); 463 ret = set(val, &kp);
453 464
454 if (ret != 0) 465 if (ret != 0)
@@ -470,7 +481,7 @@ static int param_array_set(const char *val, const struct kernel_param *kp)
470 const struct kparam_array *arr = kp->arr; 481 const struct kparam_array *arr = kp->arr;
471 unsigned int temp_num; 482 unsigned int temp_num;
472 483
473 return param_array(kp->name, val, 1, arr->max, arr->elem, 484 return param_array(kp->mod, kp->name, val, 1, arr->max, arr->elem,
474 arr->elemsize, arr->ops->set, kp->level, 485 arr->elemsize, arr->ops->set, kp->level,
475 arr->num ?: &temp_num); 486 arr->num ?: &temp_num);
476} 487}
@@ -485,7 +496,7 @@ static int param_array_get(char *buffer, const struct kernel_param *kp)
485 if (i) 496 if (i)
486 buffer[off++] = ','; 497 buffer[off++] = ',';
487 p.arg = arr->elem + arr->elemsize * i; 498 p.arg = arr->elem + arr->elemsize * i;
488 BUG_ON(!mutex_is_locked(&param_lock)); 499 BUG_ON(!KPARAM_IS_LOCKED(p.mod));
489 ret = arr->ops->get(buffer + off, &p); 500 ret = arr->ops->get(buffer + off, &p);
490 if (ret < 0) 501 if (ret < 0)
491 return ret; 502 return ret;
@@ -568,9 +579,9 @@ static ssize_t param_attr_show(struct module_attribute *mattr,
568 if (!attribute->param->ops->get) 579 if (!attribute->param->ops->get)
569 return -EPERM; 580 return -EPERM;
570 581
571 mutex_lock(&param_lock); 582 kernel_param_lock(mk->mod);
572 count = attribute->param->ops->get(buf, attribute->param); 583 count = attribute->param->ops->get(buf, attribute->param);
573 mutex_unlock(&param_lock); 584 kernel_param_unlock(mk->mod);
574 if (count > 0) { 585 if (count > 0) {
575 strcat(buf, "\n"); 586 strcat(buf, "\n");
576 ++count; 587 ++count;
@@ -580,7 +591,7 @@ static ssize_t param_attr_show(struct module_attribute *mattr,
580 591
581/* sysfs always hands a nul-terminated string in buf. We rely on that. */ 592/* sysfs always hands a nul-terminated string in buf. We rely on that. */
582static ssize_t param_attr_store(struct module_attribute *mattr, 593static ssize_t param_attr_store(struct module_attribute *mattr,
583 struct module_kobject *km, 594 struct module_kobject *mk,
584 const char *buf, size_t len) 595 const char *buf, size_t len)
585{ 596{
586 int err; 597 int err;
@@ -589,10 +600,10 @@ static ssize_t param_attr_store(struct module_attribute *mattr,
589 if (!attribute->param->ops->set) 600 if (!attribute->param->ops->set)
590 return -EPERM; 601 return -EPERM;
591 602
592 mutex_lock(&param_lock); 603 kernel_param_lock(mk->mod);
593 param_check_unsafe(attribute->param); 604 param_check_unsafe(attribute->param);
594 err = attribute->param->ops->set(buf, attribute->param); 605 err = attribute->param->ops->set(buf, attribute->param);
595 mutex_unlock(&param_lock); 606 kernel_param_unlock(mk->mod);
596 if (!err) 607 if (!err)
597 return len; 608 return len;
598 return err; 609 return err;
@@ -605,18 +616,19 @@ static ssize_t param_attr_store(struct module_attribute *mattr,
605#define __modinit __init 616#define __modinit __init
606#endif 617#endif
607 618
608#ifdef CONFIG_SYSFS 619void kernel_param_lock(struct module *mod)
609void __kernel_param_lock(void)
610{ 620{
611 mutex_lock(&param_lock); 621 mutex_lock(KPARAM_MUTEX(mod));
612} 622}
613EXPORT_SYMBOL(__kernel_param_lock);
614 623
615void __kernel_param_unlock(void) 624void kernel_param_unlock(struct module *mod)
616{ 625{
617 mutex_unlock(&param_lock); 626 mutex_unlock(KPARAM_MUTEX(mod));
618} 627}
619EXPORT_SYMBOL(__kernel_param_unlock); 628
629#ifdef CONFIG_SYSFS
630EXPORT_SYMBOL(kernel_param_lock);
631EXPORT_SYMBOL(kernel_param_unlock);
620 632
621/* 633/*
622 * add_sysfs_param - add a parameter to sysfs 634 * add_sysfs_param - add a parameter to sysfs