diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
-rw-r--r-- | kernel/trace/ftrace.c | 54 |
1 files changed, 37 insertions, 17 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index fb186b9ddf51..31c90fec4158 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
@@ -1925,8 +1925,16 @@ ftrace_find_tramp_ops_curr(struct dyn_ftrace *rec) | |||
1925 | * when we are adding another op to the rec or removing the | 1925 | * when we are adding another op to the rec or removing the |
1926 | * current one. Thus, if the op is being added, we can | 1926 | * current one. Thus, if the op is being added, we can |
1927 | * ignore it because it hasn't attached itself to the rec | 1927 | * ignore it because it hasn't attached itself to the rec |
1928 | * yet. That means we just need to find the op that has a | 1928 | * yet. |
1929 | * trampoline and is not beeing added. | 1929 | * |
1930 | * If an ops is being modified (hooking to different functions) | ||
1931 | * then we don't care about the new functions that are being | ||
1932 | * added, just the old ones (that are probably being removed). | ||
1933 | * | ||
1934 | * If we are adding an ops to a function that already is using | ||
1935 | * a trampoline, it needs to be removed (trampolines are only | ||
1936 | * for single ops connected), then an ops that is not being | ||
1937 | * modified also needs to be checked. | ||
1930 | */ | 1938 | */ |
1931 | do_for_each_ftrace_op(op, ftrace_ops_list) { | 1939 | do_for_each_ftrace_op(op, ftrace_ops_list) { |
1932 | 1940 | ||
@@ -1940,17 +1948,23 @@ ftrace_find_tramp_ops_curr(struct dyn_ftrace *rec) | |||
1940 | if (op->flags & FTRACE_OPS_FL_ADDING) | 1948 | if (op->flags & FTRACE_OPS_FL_ADDING) |
1941 | continue; | 1949 | continue; |
1942 | 1950 | ||
1951 | |||
1943 | /* | 1952 | /* |
1944 | * If the ops is not being added and has a trampoline, | 1953 | * If the ops is being modified and is in the old |
1945 | * then it must be the one that we want! | 1954 | * hash, then it is probably being removed from this |
1955 | * function. | ||
1946 | */ | 1956 | */ |
1947 | if (hash_contains_ip(ip, op->func_hash)) | ||
1948 | return op; | ||
1949 | |||
1950 | /* If the ops is being modified, it may be in the old hash. */ | ||
1951 | if ((op->flags & FTRACE_OPS_FL_MODIFYING) && | 1957 | if ((op->flags & FTRACE_OPS_FL_MODIFYING) && |
1952 | hash_contains_ip(ip, &op->old_hash)) | 1958 | hash_contains_ip(ip, &op->old_hash)) |
1953 | return op; | 1959 | return op; |
1960 | /* | ||
1961 | * If the ops is not being added or modified, and it's | ||
1962 | * in its normal filter hash, then this must be the one | ||
1963 | * we want! | ||
1964 | */ | ||
1965 | if (!(op->flags & FTRACE_OPS_FL_MODIFYING) && | ||
1966 | hash_contains_ip(ip, op->func_hash)) | ||
1967 | return op; | ||
1954 | 1968 | ||
1955 | } while_for_each_ftrace_op(op); | 1969 | } while_for_each_ftrace_op(op); |
1956 | 1970 | ||
@@ -2293,10 +2307,13 @@ static void ftrace_run_update_code(int command) | |||
2293 | FTRACE_WARN_ON(ret); | 2307 | FTRACE_WARN_ON(ret); |
2294 | } | 2308 | } |
2295 | 2309 | ||
2296 | static void ftrace_run_modify_code(struct ftrace_ops *ops, int command) | 2310 | static void ftrace_run_modify_code(struct ftrace_ops *ops, int command, |
2311 | struct ftrace_hash *old_hash) | ||
2297 | { | 2312 | { |
2298 | ops->flags |= FTRACE_OPS_FL_MODIFYING; | 2313 | ops->flags |= FTRACE_OPS_FL_MODIFYING; |
2314 | ops->old_hash.filter_hash = old_hash; | ||
2299 | ftrace_run_update_code(command); | 2315 | ftrace_run_update_code(command); |
2316 | ops->old_hash.filter_hash = NULL; | ||
2300 | ops->flags &= ~FTRACE_OPS_FL_MODIFYING; | 2317 | ops->flags &= ~FTRACE_OPS_FL_MODIFYING; |
2301 | } | 2318 | } |
2302 | 2319 | ||
@@ -3340,7 +3357,7 @@ static struct ftrace_ops trace_probe_ops __read_mostly = | |||
3340 | 3357 | ||
3341 | static int ftrace_probe_registered; | 3358 | static int ftrace_probe_registered; |
3342 | 3359 | ||
3343 | static void __enable_ftrace_function_probe(void) | 3360 | static void __enable_ftrace_function_probe(struct ftrace_hash *old_hash) |
3344 | { | 3361 | { |
3345 | int ret; | 3362 | int ret; |
3346 | int i; | 3363 | int i; |
@@ -3348,7 +3365,8 @@ static void __enable_ftrace_function_probe(void) | |||
3348 | if (ftrace_probe_registered) { | 3365 | if (ftrace_probe_registered) { |
3349 | /* still need to update the function call sites */ | 3366 | /* still need to update the function call sites */ |
3350 | if (ftrace_enabled) | 3367 | if (ftrace_enabled) |
3351 | ftrace_run_modify_code(&trace_probe_ops, FTRACE_UPDATE_CALLS); | 3368 | ftrace_run_modify_code(&trace_probe_ops, FTRACE_UPDATE_CALLS, |
3369 | old_hash); | ||
3352 | return; | 3370 | return; |
3353 | } | 3371 | } |
3354 | 3372 | ||
@@ -3477,13 +3495,14 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, | |||
3477 | } while_for_each_ftrace_rec(); | 3495 | } while_for_each_ftrace_rec(); |
3478 | 3496 | ||
3479 | ret = ftrace_hash_move(&trace_probe_ops, 1, orig_hash, hash); | 3497 | ret = ftrace_hash_move(&trace_probe_ops, 1, orig_hash, hash); |
3498 | |||
3499 | __enable_ftrace_function_probe(old_hash); | ||
3500 | |||
3480 | if (!ret) | 3501 | if (!ret) |
3481 | free_ftrace_hash_rcu(old_hash); | 3502 | free_ftrace_hash_rcu(old_hash); |
3482 | else | 3503 | else |
3483 | count = ret; | 3504 | count = ret; |
3484 | 3505 | ||
3485 | __enable_ftrace_function_probe(); | ||
3486 | |||
3487 | out_unlock: | 3506 | out_unlock: |
3488 | mutex_unlock(&ftrace_lock); | 3507 | mutex_unlock(&ftrace_lock); |
3489 | out: | 3508 | out: |
@@ -3764,10 +3783,11 @@ ftrace_match_addr(struct ftrace_hash *hash, unsigned long ip, int remove) | |||
3764 | return add_hash_entry(hash, ip); | 3783 | return add_hash_entry(hash, ip); |
3765 | } | 3784 | } |
3766 | 3785 | ||
3767 | static void ftrace_ops_update_code(struct ftrace_ops *ops) | 3786 | static void ftrace_ops_update_code(struct ftrace_ops *ops, |
3787 | struct ftrace_hash *old_hash) | ||
3768 | { | 3788 | { |
3769 | if (ops->flags & FTRACE_OPS_FL_ENABLED && ftrace_enabled) | 3789 | if (ops->flags & FTRACE_OPS_FL_ENABLED && ftrace_enabled) |
3770 | ftrace_run_modify_code(ops, FTRACE_UPDATE_CALLS); | 3790 | ftrace_run_modify_code(ops, FTRACE_UPDATE_CALLS, old_hash); |
3771 | } | 3791 | } |
3772 | 3792 | ||
3773 | static int | 3793 | static int |
@@ -3813,7 +3833,7 @@ ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len, | |||
3813 | old_hash = *orig_hash; | 3833 | old_hash = *orig_hash; |
3814 | ret = ftrace_hash_move(ops, enable, orig_hash, hash); | 3834 | ret = ftrace_hash_move(ops, enable, orig_hash, hash); |
3815 | if (!ret) { | 3835 | if (!ret) { |
3816 | ftrace_ops_update_code(ops); | 3836 | ftrace_ops_update_code(ops, old_hash); |
3817 | free_ftrace_hash_rcu(old_hash); | 3837 | free_ftrace_hash_rcu(old_hash); |
3818 | } | 3838 | } |
3819 | mutex_unlock(&ftrace_lock); | 3839 | mutex_unlock(&ftrace_lock); |
@@ -4058,7 +4078,7 @@ int ftrace_regex_release(struct inode *inode, struct file *file) | |||
4058 | ret = ftrace_hash_move(iter->ops, filter_hash, | 4078 | ret = ftrace_hash_move(iter->ops, filter_hash, |
4059 | orig_hash, iter->hash); | 4079 | orig_hash, iter->hash); |
4060 | if (!ret) { | 4080 | if (!ret) { |
4061 | ftrace_ops_update_code(iter->ops); | 4081 | ftrace_ops_update_code(iter->ops, old_hash); |
4062 | free_ftrace_hash_rcu(old_hash); | 4082 | free_ftrace_hash_rcu(old_hash); |
4063 | } | 4083 | } |
4064 | mutex_unlock(&ftrace_lock); | 4084 | mutex_unlock(&ftrace_lock); |