diff options
author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2013-05-09 01:44:17 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2013-05-09 20:10:22 -0400 |
commit | f04f24fb7e48d446bd89a01c6056571f25972511 (patch) | |
tree | b1d1ecbe88df0eee0309970fd07dc37fb375a440 /kernel | |
parent | 7c088b5120ffef017e2ddc38f992277e96436ef6 (diff) |
ftrace, kprobes: Fix a deadlock on ftrace_regex_lock
Fix a deadlock on ftrace_regex_lock which happens when setting
an enable_event trigger on dynamic kprobe event as below.
----
sh-2.05b# echo p vfs_symlink > kprobe_events
sh-2.05b# echo vfs_symlink:enable_event:kprobes:p_vfs_symlink_0 > set_ftrace_filter
=============================================
[ INFO: possible recursive locking detected ]
3.9.0+ #35 Not tainted
---------------------------------------------
sh/72 is trying to acquire lock:
(ftrace_regex_lock){+.+.+.}, at: [<ffffffff810ba6c1>] ftrace_set_hash+0x81/0x1f0
but task is already holding lock:
(ftrace_regex_lock){+.+.+.}, at: [<ffffffff810b7cbd>] ftrace_regex_write.isra.29.part.30+0x3d/0x220
other info that might help us debug this:
Possible unsafe locking scenario:
CPU0
----
lock(ftrace_regex_lock);
lock(ftrace_regex_lock);
*** DEADLOCK ***
----
To fix that, this introduces a finer regex_lock for each ftrace_ops.
ftrace_regex_lock is too big of a lock which protects all
filter/notrace_hash operations, but it doesn't need to be a global
lock after supporting multiple ftrace_ops because each ftrace_ops
has its own filter/notrace_hash.
Link: http://lkml.kernel.org/r/20130509054417.30398.84254.stgit@mhiramat-M0-7522
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Tom Zanussi <tom.zanussi@intel.com>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
[ Added initialization flag and automate mutex initialization for
non ftrace.c ftrace_probes. ]
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/trace/ftrace.c | 73 |
1 files changed, 52 insertions, 21 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index d85a0ad81a67..827f2fe7bc3f 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -64,6 +64,13 @@ | |||
64 | 64 | ||
65 | #define FL_GLOBAL_CONTROL_MASK (FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_CONTROL) | 65 | #define FL_GLOBAL_CONTROL_MASK (FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_CONTROL) |
66 | 66 | ||
67 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
68 | #define INIT_REGEX_LOCK(opsname) \ | ||
69 | .regex_lock = __MUTEX_INITIALIZER(opsname.regex_lock), | ||
70 | #else | ||
71 | #define INIT_REGEX_LOCK(opsname) | ||
72 | #endif | ||
73 | |||
67 | static struct ftrace_ops ftrace_list_end __read_mostly = { | 74 | static struct ftrace_ops ftrace_list_end __read_mostly = { |
68 | .func = ftrace_stub, | 75 | .func = ftrace_stub, |
69 | .flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_STUB, | 76 | .flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_STUB, |
@@ -131,6 +138,16 @@ static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip); | |||
131 | while (likely(op = rcu_dereference_raw((op)->next)) && \ | 138 | while (likely(op = rcu_dereference_raw((op)->next)) && \ |
132 | unlikely((op) != &ftrace_list_end)) | 139 | unlikely((op) != &ftrace_list_end)) |
133 | 140 | ||
141 | static inline void ftrace_ops_init(struct ftrace_ops *ops) | ||
142 | { | ||
143 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
144 | if (!(ops->flags & FTRACE_OPS_FL_INITIALIZED)) { | ||
145 | mutex_init(&ops->regex_lock); | ||
146 | ops->flags |= FTRACE_OPS_FL_INITIALIZED; | ||
147 | } | ||
148 | #endif | ||
149 | } | ||
150 | |||
134 | /** | 151 | /** |
135 | * ftrace_nr_registered_ops - return number of ops registered | 152 | * ftrace_nr_registered_ops - return number of ops registered |
136 | * | 153 | * |
@@ -907,7 +924,8 @@ static void unregister_ftrace_profiler(void) | |||
907 | #else | 924 | #else |
908 | static struct ftrace_ops ftrace_profile_ops __read_mostly = { | 925 | static struct ftrace_ops ftrace_profile_ops __read_mostly = { |
909 | .func = function_profile_call, | 926 | .func = function_profile_call, |
910 | .flags = FTRACE_OPS_FL_RECURSION_SAFE, | 927 | .flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_INITIALIZED, |
928 | INIT_REGEX_LOCK(ftrace_profile_ops) | ||
911 | }; | 929 | }; |
912 | 930 | ||
913 | static int register_ftrace_profiler(void) | 931 | static int register_ftrace_profiler(void) |
@@ -1103,11 +1121,10 @@ static struct ftrace_ops global_ops = { | |||
1103 | .func = ftrace_stub, | 1121 | .func = ftrace_stub, |
1104 | .notrace_hash = EMPTY_HASH, | 1122 | .notrace_hash = EMPTY_HASH, |
1105 | .filter_hash = EMPTY_HASH, | 1123 | .filter_hash = EMPTY_HASH, |
1106 | .flags = FTRACE_OPS_FL_RECURSION_SAFE, | 1124 | .flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_INITIALIZED, |
1125 | INIT_REGEX_LOCK(global_ops) | ||
1107 | }; | 1126 | }; |
1108 | 1127 | ||
1109 | static DEFINE_MUTEX(ftrace_regex_lock); | ||
1110 | |||
1111 | struct ftrace_page { | 1128 | struct ftrace_page { |
1112 | struct ftrace_page *next; | 1129 | struct ftrace_page *next; |
1113 | struct dyn_ftrace *records; | 1130 | struct dyn_ftrace *records; |
@@ -1247,6 +1264,7 @@ static void free_ftrace_hash_rcu(struct ftrace_hash *hash) | |||
1247 | 1264 | ||
1248 | void ftrace_free_filter(struct ftrace_ops *ops) | 1265 | void ftrace_free_filter(struct ftrace_ops *ops) |
1249 | { | 1266 | { |
1267 | ftrace_ops_init(ops); | ||
1250 | free_ftrace_hash(ops->filter_hash); | 1268 | free_ftrace_hash(ops->filter_hash); |
1251 | free_ftrace_hash(ops->notrace_hash); | 1269 | free_ftrace_hash(ops->notrace_hash); |
1252 | } | 1270 | } |
@@ -2624,6 +2642,8 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag, | |||
2624 | struct ftrace_hash *hash; | 2642 | struct ftrace_hash *hash; |
2625 | int ret = 0; | 2643 | int ret = 0; |
2626 | 2644 | ||
2645 | ftrace_ops_init(ops); | ||
2646 | |||
2627 | if (unlikely(ftrace_disabled)) | 2647 | if (unlikely(ftrace_disabled)) |
2628 | return -ENODEV; | 2648 | return -ENODEV; |
2629 | 2649 | ||
@@ -2656,7 +2676,7 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag, | |||
2656 | } | 2676 | } |
2657 | } | 2677 | } |
2658 | 2678 | ||
2659 | mutex_lock(&ftrace_regex_lock); | 2679 | mutex_lock(&ops->regex_lock); |
2660 | 2680 | ||
2661 | if ((file->f_mode & FMODE_WRITE) && | 2681 | if ((file->f_mode & FMODE_WRITE) && |
2662 | (file->f_flags & O_TRUNC)) | 2682 | (file->f_flags & O_TRUNC)) |
@@ -2677,7 +2697,7 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag, | |||
2677 | } | 2697 | } |
2678 | } else | 2698 | } else |
2679 | file->private_data = iter; | 2699 | file->private_data = iter; |
2680 | mutex_unlock(&ftrace_regex_lock); | 2700 | mutex_unlock(&ops->regex_lock); |
2681 | 2701 | ||
2682 | return ret; | 2702 | return ret; |
2683 | } | 2703 | } |
@@ -2910,6 +2930,8 @@ static void function_trace_probe_call(unsigned long ip, unsigned long parent_ip, | |||
2910 | static struct ftrace_ops trace_probe_ops __read_mostly = | 2930 | static struct ftrace_ops trace_probe_ops __read_mostly = |
2911 | { | 2931 | { |
2912 | .func = function_trace_probe_call, | 2932 | .func = function_trace_probe_call, |
2933 | .flags = FTRACE_OPS_FL_INITIALIZED, | ||
2934 | INIT_REGEX_LOCK(trace_probe_ops) | ||
2913 | }; | 2935 | }; |
2914 | 2936 | ||
2915 | static int ftrace_probe_registered; | 2937 | static int ftrace_probe_registered; |
@@ -3256,18 +3278,18 @@ ftrace_regex_write(struct file *file, const char __user *ubuf, | |||
3256 | if (!cnt) | 3278 | if (!cnt) |
3257 | return 0; | 3279 | return 0; |
3258 | 3280 | ||
3259 | mutex_lock(&ftrace_regex_lock); | ||
3260 | |||
3261 | ret = -ENODEV; | ||
3262 | if (unlikely(ftrace_disabled)) | ||
3263 | goto out_unlock; | ||
3264 | |||
3265 | if (file->f_mode & FMODE_READ) { | 3281 | if (file->f_mode & FMODE_READ) { |
3266 | struct seq_file *m = file->private_data; | 3282 | struct seq_file *m = file->private_data; |
3267 | iter = m->private; | 3283 | iter = m->private; |
3268 | } else | 3284 | } else |
3269 | iter = file->private_data; | 3285 | iter = file->private_data; |
3270 | 3286 | ||
3287 | mutex_lock(&iter->ops->regex_lock); | ||
3288 | |||
3289 | ret = -ENODEV; | ||
3290 | if (unlikely(ftrace_disabled)) | ||
3291 | goto out_unlock; | ||
3292 | |||
3271 | parser = &iter->parser; | 3293 | parser = &iter->parser; |
3272 | read = trace_get_user(parser, ubuf, cnt, ppos); | 3294 | read = trace_get_user(parser, ubuf, cnt, ppos); |
3273 | 3295 | ||
@@ -3282,7 +3304,7 @@ ftrace_regex_write(struct file *file, const char __user *ubuf, | |||
3282 | 3304 | ||
3283 | ret = read; | 3305 | ret = read; |
3284 | out_unlock: | 3306 | out_unlock: |
3285 | mutex_unlock(&ftrace_regex_lock); | 3307 | mutex_unlock(&iter->ops->regex_lock); |
3286 | 3308 | ||
3287 | return ret; | 3309 | return ret; |
3288 | } | 3310 | } |
@@ -3344,7 +3366,7 @@ ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len, | |||
3344 | if (!hash) | 3366 | if (!hash) |
3345 | return -ENOMEM; | 3367 | return -ENOMEM; |
3346 | 3368 | ||
3347 | mutex_lock(&ftrace_regex_lock); | 3369 | mutex_lock(&ops->regex_lock); |
3348 | if (reset) | 3370 | if (reset) |
3349 | ftrace_filter_reset(hash); | 3371 | ftrace_filter_reset(hash); |
3350 | if (buf && !ftrace_match_records(hash, buf, len)) { | 3372 | if (buf && !ftrace_match_records(hash, buf, len)) { |
@@ -3366,7 +3388,7 @@ ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len, | |||
3366 | mutex_unlock(&ftrace_lock); | 3388 | mutex_unlock(&ftrace_lock); |
3367 | 3389 | ||
3368 | out_regex_unlock: | 3390 | out_regex_unlock: |
3369 | mutex_unlock(&ftrace_regex_lock); | 3391 | mutex_unlock(&ops->regex_lock); |
3370 | 3392 | ||
3371 | free_ftrace_hash(hash); | 3393 | free_ftrace_hash(hash); |
3372 | return ret; | 3394 | return ret; |
@@ -3392,6 +3414,7 @@ ftrace_set_addr(struct ftrace_ops *ops, unsigned long ip, int remove, | |||
3392 | int ftrace_set_filter_ip(struct ftrace_ops *ops, unsigned long ip, | 3414 | int ftrace_set_filter_ip(struct ftrace_ops *ops, unsigned long ip, |
3393 | int remove, int reset) | 3415 | int remove, int reset) |
3394 | { | 3416 | { |
3417 | ftrace_ops_init(ops); | ||
3395 | return ftrace_set_addr(ops, ip, remove, reset, 1); | 3418 | return ftrace_set_addr(ops, ip, remove, reset, 1); |
3396 | } | 3419 | } |
3397 | EXPORT_SYMBOL_GPL(ftrace_set_filter_ip); | 3420 | EXPORT_SYMBOL_GPL(ftrace_set_filter_ip); |
@@ -3416,6 +3439,7 @@ ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len, | |||
3416 | int ftrace_set_filter(struct ftrace_ops *ops, unsigned char *buf, | 3439 | int ftrace_set_filter(struct ftrace_ops *ops, unsigned char *buf, |
3417 | int len, int reset) | 3440 | int len, int reset) |
3418 | { | 3441 | { |
3442 | ftrace_ops_init(ops); | ||
3419 | return ftrace_set_regex(ops, buf, len, reset, 1); | 3443 | return ftrace_set_regex(ops, buf, len, reset, 1); |
3420 | } | 3444 | } |
3421 | EXPORT_SYMBOL_GPL(ftrace_set_filter); | 3445 | EXPORT_SYMBOL_GPL(ftrace_set_filter); |
@@ -3434,6 +3458,7 @@ EXPORT_SYMBOL_GPL(ftrace_set_filter); | |||
3434 | int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf, | 3458 | int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf, |
3435 | int len, int reset) | 3459 | int len, int reset) |
3436 | { | 3460 | { |
3461 | ftrace_ops_init(ops); | ||
3437 | return ftrace_set_regex(ops, buf, len, reset, 0); | 3462 | return ftrace_set_regex(ops, buf, len, reset, 0); |
3438 | } | 3463 | } |
3439 | EXPORT_SYMBOL_GPL(ftrace_set_notrace); | 3464 | EXPORT_SYMBOL_GPL(ftrace_set_notrace); |
@@ -3524,6 +3549,8 @@ ftrace_set_early_filter(struct ftrace_ops *ops, char *buf, int enable) | |||
3524 | { | 3549 | { |
3525 | char *func; | 3550 | char *func; |
3526 | 3551 | ||
3552 | ftrace_ops_init(ops); | ||
3553 | |||
3527 | while (buf) { | 3554 | while (buf) { |
3528 | func = strsep(&buf, ","); | 3555 | func = strsep(&buf, ","); |
3529 | ftrace_set_regex(ops, func, strlen(func), 0, enable); | 3556 | ftrace_set_regex(ops, func, strlen(func), 0, enable); |
@@ -3551,14 +3578,14 @@ int ftrace_regex_release(struct inode *inode, struct file *file) | |||
3551 | int filter_hash; | 3578 | int filter_hash; |
3552 | int ret; | 3579 | int ret; |
3553 | 3580 | ||
3554 | mutex_lock(&ftrace_regex_lock); | ||
3555 | if (file->f_mode & FMODE_READ) { | 3581 | if (file->f_mode & FMODE_READ) { |
3556 | iter = m->private; | 3582 | iter = m->private; |
3557 | |||
3558 | seq_release(inode, file); | 3583 | seq_release(inode, file); |
3559 | } else | 3584 | } else |
3560 | iter = file->private_data; | 3585 | iter = file->private_data; |
3561 | 3586 | ||
3587 | mutex_lock(&iter->ops->regex_lock); | ||
3588 | |||
3562 | parser = &iter->parser; | 3589 | parser = &iter->parser; |
3563 | if (trace_parser_loaded(parser)) { | 3590 | if (trace_parser_loaded(parser)) { |
3564 | parser->buffer[parser->idx] = 0; | 3591 | parser->buffer[parser->idx] = 0; |
@@ -3587,7 +3614,7 @@ int ftrace_regex_release(struct inode *inode, struct file *file) | |||
3587 | free_ftrace_hash(iter->hash); | 3614 | free_ftrace_hash(iter->hash); |
3588 | kfree(iter); | 3615 | kfree(iter); |
3589 | 3616 | ||
3590 | mutex_unlock(&ftrace_regex_lock); | 3617 | mutex_unlock(&iter->ops->regex_lock); |
3591 | return 0; | 3618 | return 0; |
3592 | } | 3619 | } |
3593 | 3620 | ||
@@ -4126,7 +4153,8 @@ void __init ftrace_init(void) | |||
4126 | 4153 | ||
4127 | static struct ftrace_ops global_ops = { | 4154 | static struct ftrace_ops global_ops = { |
4128 | .func = ftrace_stub, | 4155 | .func = ftrace_stub, |
4129 | .flags = FTRACE_OPS_FL_RECURSION_SAFE, | 4156 | .flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_INITIALIZED, |
4157 | INIT_REGEX_LOCK(global_ops) | ||
4130 | }; | 4158 | }; |
4131 | 4159 | ||
4132 | static int __init ftrace_nodyn_init(void) | 4160 | static int __init ftrace_nodyn_init(void) |
@@ -4180,8 +4208,9 @@ ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip, | |||
4180 | } | 4208 | } |
4181 | 4209 | ||
4182 | static struct ftrace_ops control_ops = { | 4210 | static struct ftrace_ops control_ops = { |
4183 | .func = ftrace_ops_control_func, | 4211 | .func = ftrace_ops_control_func, |
4184 | .flags = FTRACE_OPS_FL_RECURSION_SAFE, | 4212 | .flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_INITIALIZED, |
4213 | INIT_REGEX_LOCK(control_ops) | ||
4185 | }; | 4214 | }; |
4186 | 4215 | ||
4187 | static inline void | 4216 | static inline void |
@@ -4539,6 +4568,8 @@ int register_ftrace_function(struct ftrace_ops *ops) | |||
4539 | { | 4568 | { |
4540 | int ret = -1; | 4569 | int ret = -1; |
4541 | 4570 | ||
4571 | ftrace_ops_init(ops); | ||
4572 | |||
4542 | mutex_lock(&ftrace_lock); | 4573 | mutex_lock(&ftrace_lock); |
4543 | 4574 | ||
4544 | ret = __register_ftrace_function(ops); | 4575 | ret = __register_ftrace_function(ops); |