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 | |
| 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>
| -rw-r--r-- | include/linux/ftrace.h | 4 | ||||
| -rw-r--r-- | kernel/trace/ftrace.c | 73 |
2 files changed, 56 insertions, 21 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index f83e17a40e8b..99d0fbcbaf79 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
| @@ -90,6 +90,8 @@ typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip, | |||
| 90 | * not set this, then the ftrace infrastructure will add recursion | 90 | * not set this, then the ftrace infrastructure will add recursion |
| 91 | * protection for the caller. | 91 | * protection for the caller. |
| 92 | * STUB - The ftrace_ops is just a place holder. | 92 | * STUB - The ftrace_ops is just a place holder. |
| 93 | * INITIALIZED - The ftrace_ops has already been initialized (first use time | ||
| 94 | * register_ftrace_function() is called, it will initialized the ops) | ||
| 93 | */ | 95 | */ |
| 94 | enum { | 96 | enum { |
| 95 | FTRACE_OPS_FL_ENABLED = 1 << 0, | 97 | FTRACE_OPS_FL_ENABLED = 1 << 0, |
| @@ -100,6 +102,7 @@ enum { | |||
| 100 | FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED = 1 << 5, | 102 | FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED = 1 << 5, |
| 101 | FTRACE_OPS_FL_RECURSION_SAFE = 1 << 6, | 103 | FTRACE_OPS_FL_RECURSION_SAFE = 1 << 6, |
| 102 | FTRACE_OPS_FL_STUB = 1 << 7, | 104 | FTRACE_OPS_FL_STUB = 1 << 7, |
| 105 | FTRACE_OPS_FL_INITIALIZED = 1 << 8, | ||
| 103 | }; | 106 | }; |
| 104 | 107 | ||
| 105 | struct ftrace_ops { | 108 | struct ftrace_ops { |
| @@ -110,6 +113,7 @@ struct ftrace_ops { | |||
| 110 | #ifdef CONFIG_DYNAMIC_FTRACE | 113 | #ifdef CONFIG_DYNAMIC_FTRACE |
| 111 | struct ftrace_hash *notrace_hash; | 114 | struct ftrace_hash *notrace_hash; |
| 112 | struct ftrace_hash *filter_hash; | 115 | struct ftrace_hash *filter_hash; |
| 116 | struct mutex regex_lock; | ||
| 113 | #endif | 117 | #endif |
| 114 | }; | 118 | }; |
| 115 | 119 | ||
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 | |||
