diff options
| -rw-r--r-- | include/linux/ftrace.h | 7 | ||||
| -rw-r--r-- | kernel/trace/ftrace.c | 193 | ||||
| -rw-r--r-- | kernel/trace/trace_functions.c | 2 | ||||
| -rw-r--r-- | kernel/trace/trace_irqsoff.c | 1 | ||||
| -rw-r--r-- | kernel/trace/trace_sched_wakeup.c | 1 | ||||
| -rw-r--r-- | kernel/trace/trace_stack.c | 1 |
6 files changed, 166 insertions, 39 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index ab1c46e70bb6..4609c0ece79a 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
| @@ -31,13 +31,18 @@ typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip); | |||
| 31 | 31 | ||
| 32 | struct ftrace_hash; | 32 | struct ftrace_hash; |
| 33 | 33 | ||
| 34 | enum { | ||
| 35 | FTRACE_OPS_FL_ENABLED = 1 << 0, | ||
| 36 | FTRACE_OPS_FL_GLOBAL = 1 << 1, | ||
| 37 | }; | ||
| 38 | |||
| 34 | struct ftrace_ops { | 39 | struct ftrace_ops { |
| 35 | ftrace_func_t func; | 40 | ftrace_func_t func; |
| 36 | struct ftrace_ops *next; | 41 | struct ftrace_ops *next; |
| 42 | unsigned long flags; | ||
| 37 | #ifdef CONFIG_DYNAMIC_FTRACE | 43 | #ifdef CONFIG_DYNAMIC_FTRACE |
| 38 | struct ftrace_hash *notrace_hash; | 44 | struct ftrace_hash *notrace_hash; |
| 39 | struct ftrace_hash *filter_hash; | 45 | struct ftrace_hash *filter_hash; |
| 40 | unsigned long flags; | ||
| 41 | #endif | 46 | #endif |
| 42 | }; | 47 | }; |
| 43 | 48 | ||
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 92b6fdf49ae5..6c7e1df39b57 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
| @@ -87,24 +87,29 @@ static struct ftrace_ops ftrace_list_end __read_mostly = | |||
| 87 | .func = ftrace_stub, | 87 | .func = ftrace_stub, |
| 88 | }; | 88 | }; |
| 89 | 89 | ||
| 90 | static struct ftrace_ops *ftrace_list __read_mostly = &ftrace_list_end; | 90 | static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end; |
| 91 | static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end; | ||
| 91 | ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; | 92 | ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; |
| 92 | ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub; | 93 | ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub; |
| 93 | ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub; | 94 | ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub; |
| 94 | static struct ftrace_ops global_ops; | 95 | static struct ftrace_ops global_ops; |
| 95 | 96 | ||
| 97 | static void | ||
| 98 | ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip); | ||
| 99 | |||
| 96 | /* | 100 | /* |
| 97 | * Traverse the ftrace_list, invoking all entries. The reason that we | 101 | * Traverse the ftrace_global_list, invoking all entries. The reason that we |
| 98 | * can use rcu_dereference_raw() is that elements removed from this list | 102 | * can use rcu_dereference_raw() is that elements removed from this list |
| 99 | * are simply leaked, so there is no need to interact with a grace-period | 103 | * are simply leaked, so there is no need to interact with a grace-period |
| 100 | * mechanism. The rcu_dereference_raw() calls are needed to handle | 104 | * mechanism. The rcu_dereference_raw() calls are needed to handle |
| 101 | * concurrent insertions into the ftrace_list. | 105 | * concurrent insertions into the ftrace_global_list. |
| 102 | * | 106 | * |
| 103 | * Silly Alpha and silly pointer-speculation compiler optimizations! | 107 | * Silly Alpha and silly pointer-speculation compiler optimizations! |
| 104 | */ | 108 | */ |
| 105 | static void ftrace_list_func(unsigned long ip, unsigned long parent_ip) | 109 | static void ftrace_global_list_func(unsigned long ip, |
| 110 | unsigned long parent_ip) | ||
| 106 | { | 111 | { |
| 107 | struct ftrace_ops *op = rcu_dereference_raw(ftrace_list); /*see above*/ | 112 | struct ftrace_ops *op = rcu_dereference_raw(ftrace_global_list); /*see above*/ |
| 108 | 113 | ||
| 109 | while (op != &ftrace_list_end) { | 114 | while (op != &ftrace_list_end) { |
| 110 | op->func(ip, parent_ip); | 115 | op->func(ip, parent_ip); |
| @@ -163,11 +168,11 @@ static void update_global_ops(void) | |||
| 163 | * function directly. Otherwise, we need to iterate over the | 168 | * function directly. Otherwise, we need to iterate over the |
| 164 | * registered callers. | 169 | * registered callers. |
| 165 | */ | 170 | */ |
| 166 | if (ftrace_list == &ftrace_list_end || | 171 | if (ftrace_global_list == &ftrace_list_end || |
| 167 | ftrace_list->next == &ftrace_list_end) | 172 | ftrace_global_list->next == &ftrace_list_end) |
| 168 | func = ftrace_list->func; | 173 | func = ftrace_global_list->func; |
| 169 | else | 174 | else |
| 170 | func = ftrace_list_func; | 175 | func = ftrace_global_list_func; |
| 171 | 176 | ||
| 172 | /* If we filter on pids, update to use the pid function */ | 177 | /* If we filter on pids, update to use the pid function */ |
| 173 | if (!list_empty(&ftrace_pids)) { | 178 | if (!list_empty(&ftrace_pids)) { |
| @@ -184,7 +189,11 @@ static void update_ftrace_function(void) | |||
| 184 | 189 | ||
| 185 | update_global_ops(); | 190 | update_global_ops(); |
| 186 | 191 | ||
| 187 | func = global_ops.func; | 192 | if (ftrace_ops_list == &ftrace_list_end || |
| 193 | ftrace_ops_list->next == &ftrace_list_end) | ||
| 194 | func = ftrace_ops_list->func; | ||
| 195 | else | ||
| 196 | func = ftrace_ops_list_func; | ||
| 188 | 197 | ||
| 189 | #ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST | 198 | #ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST |
| 190 | ftrace_trace_function = func; | 199 | ftrace_trace_function = func; |
| @@ -198,10 +207,10 @@ static void add_ftrace_ops(struct ftrace_ops **list, struct ftrace_ops *ops) | |||
| 198 | { | 207 | { |
| 199 | ops->next = *list; | 208 | ops->next = *list; |
| 200 | /* | 209 | /* |
| 201 | * We are entering ops into the ftrace_list but another | 210 | * We are entering ops into the list but another |
| 202 | * CPU might be walking that list. We need to make sure | 211 | * CPU might be walking that list. We need to make sure |
| 203 | * the ops->next pointer is valid before another CPU sees | 212 | * the ops->next pointer is valid before another CPU sees |
| 204 | * the ops pointer included into the ftrace_list. | 213 | * the ops pointer included into the list. |
| 205 | */ | 214 | */ |
| 206 | rcu_assign_pointer(*list, ops); | 215 | rcu_assign_pointer(*list, ops); |
| 207 | } | 216 | } |
| @@ -238,7 +247,18 @@ static int __register_ftrace_function(struct ftrace_ops *ops) | |||
| 238 | if (FTRACE_WARN_ON(ops == &global_ops)) | 247 | if (FTRACE_WARN_ON(ops == &global_ops)) |
| 239 | return -EINVAL; | 248 | return -EINVAL; |
| 240 | 249 | ||
| 241 | add_ftrace_ops(&ftrace_list, ops); | 250 | if (WARN_ON(ops->flags & FTRACE_OPS_FL_ENABLED)) |
| 251 | return -EBUSY; | ||
| 252 | |||
| 253 | if (ops->flags & FTRACE_OPS_FL_GLOBAL) { | ||
| 254 | int first = ftrace_global_list == &ftrace_list_end; | ||
| 255 | add_ftrace_ops(&ftrace_global_list, ops); | ||
| 256 | ops->flags |= FTRACE_OPS_FL_ENABLED; | ||
| 257 | if (first) | ||
| 258 | add_ftrace_ops(&ftrace_ops_list, &global_ops); | ||
| 259 | } else | ||
| 260 | add_ftrace_ops(&ftrace_ops_list, ops); | ||
| 261 | |||
| 242 | if (ftrace_enabled) | 262 | if (ftrace_enabled) |
| 243 | update_ftrace_function(); | 263 | update_ftrace_function(); |
| 244 | 264 | ||
| @@ -252,12 +272,24 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) | |||
| 252 | if (ftrace_disabled) | 272 | if (ftrace_disabled) |
| 253 | return -ENODEV; | 273 | return -ENODEV; |
| 254 | 274 | ||
| 275 | if (WARN_ON(!(ops->flags & FTRACE_OPS_FL_ENABLED))) | ||
| 276 | return -EBUSY; | ||
| 277 | |||
| 255 | if (FTRACE_WARN_ON(ops == &global_ops)) | 278 | if (FTRACE_WARN_ON(ops == &global_ops)) |
| 256 | return -EINVAL; | 279 | return -EINVAL; |
| 257 | 280 | ||
| 258 | ret = remove_ftrace_ops(&ftrace_list, ops); | 281 | if (ops->flags & FTRACE_OPS_FL_GLOBAL) { |
| 282 | ret = remove_ftrace_ops(&ftrace_global_list, ops); | ||
| 283 | if (!ret && ftrace_global_list == &ftrace_list_end) | ||
| 284 | ret = remove_ftrace_ops(&ftrace_ops_list, &global_ops); | ||
| 285 | if (!ret) | ||
| 286 | ops->flags &= ~FTRACE_OPS_FL_ENABLED; | ||
| 287 | } else | ||
| 288 | ret = remove_ftrace_ops(&ftrace_ops_list, ops); | ||
| 289 | |||
| 259 | if (ret < 0) | 290 | if (ret < 0) |
| 260 | return ret; | 291 | return ret; |
| 292 | |||
| 261 | if (ftrace_enabled) | 293 | if (ftrace_enabled) |
| 262 | update_ftrace_function(); | 294 | update_ftrace_function(); |
| 263 | 295 | ||
| @@ -928,10 +960,6 @@ static const struct ftrace_hash empty_hash = { | |||
| 928 | }; | 960 | }; |
| 929 | #define EMPTY_HASH ((struct ftrace_hash *)&empty_hash) | 961 | #define EMPTY_HASH ((struct ftrace_hash *)&empty_hash) |
| 930 | 962 | ||
| 931 | enum { | ||
| 932 | FTRACE_OPS_FL_ENABLED = 1, | ||
| 933 | }; | ||
| 934 | |||
| 935 | static struct ftrace_ops global_ops = { | 963 | static struct ftrace_ops global_ops = { |
| 936 | .func = ftrace_stub, | 964 | .func = ftrace_stub, |
| 937 | .notrace_hash = EMPTY_HASH, | 965 | .notrace_hash = EMPTY_HASH, |
| @@ -1190,6 +1218,40 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) | |||
| 1190 | } | 1218 | } |
| 1191 | 1219 | ||
| 1192 | /* | 1220 | /* |
| 1221 | * Test the hashes for this ops to see if we want to call | ||
| 1222 | * the ops->func or not. | ||
| 1223 | * | ||
| 1224 | * It's a match if the ip is in the ops->filter_hash or | ||
| 1225 | * the filter_hash does not exist or is empty, | ||
| 1226 | * AND | ||
| 1227 | * the ip is not in the ops->notrace_hash. | ||
| 1228 | */ | ||
| 1229 | static int | ||
| 1230 | ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) | ||
| 1231 | { | ||
| 1232 | struct ftrace_hash *filter_hash; | ||
| 1233 | struct ftrace_hash *notrace_hash; | ||
| 1234 | int ret; | ||
| 1235 | |||
| 1236 | /* The hashes are freed with call_rcu_sched() */ | ||
| 1237 | preempt_disable_notrace(); | ||
| 1238 | |||
| 1239 | filter_hash = rcu_dereference_raw(ops->filter_hash); | ||
| 1240 | notrace_hash = rcu_dereference_raw(ops->notrace_hash); | ||
| 1241 | |||
| 1242 | if ((!filter_hash || !filter_hash->count || | ||
| 1243 | ftrace_lookup_ip(filter_hash, ip)) && | ||
| 1244 | (!notrace_hash || !notrace_hash->count || | ||
| 1245 | !ftrace_lookup_ip(notrace_hash, ip))) | ||
| 1246 | ret = 1; | ||
| 1247 | else | ||
| 1248 | ret = 0; | ||
| 1249 | preempt_enable_notrace(); | ||
| 1250 | |||
| 1251 | return ret; | ||
| 1252 | } | ||
| 1253 | |||
| 1254 | /* | ||
| 1193 | * This is a double for. Do not use 'break' to break out of the loop, | 1255 | * This is a double for. Do not use 'break' to break out of the loop, |
| 1194 | * you must use a goto. | 1256 | * you must use a goto. |
| 1195 | */ | 1257 | */ |
| @@ -1232,7 +1294,7 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, | |||
| 1232 | if (filter_hash) { | 1294 | if (filter_hash) { |
| 1233 | hash = ops->filter_hash; | 1295 | hash = ops->filter_hash; |
| 1234 | other_hash = ops->notrace_hash; | 1296 | other_hash = ops->notrace_hash; |
| 1235 | if (!hash->count) | 1297 | if (!hash || !hash->count) |
| 1236 | all = 1; | 1298 | all = 1; |
| 1237 | } else { | 1299 | } else { |
| 1238 | inc = !inc; | 1300 | inc = !inc; |
| @@ -1242,7 +1304,7 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, | |||
| 1242 | * If the notrace hash has no items, | 1304 | * If the notrace hash has no items, |
| 1243 | * then there's nothing to do. | 1305 | * then there's nothing to do. |
| 1244 | */ | 1306 | */ |
| 1245 | if (!hash->count) | 1307 | if (hash && !hash->count) |
| 1246 | return; | 1308 | return; |
| 1247 | } | 1309 | } |
| 1248 | 1310 | ||
| @@ -1256,11 +1318,11 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, | |||
| 1256 | * Only the filter_hash affects all records. | 1318 | * Only the filter_hash affects all records. |
| 1257 | * Update if the record is not in the notrace hash. | 1319 | * Update if the record is not in the notrace hash. |
| 1258 | */ | 1320 | */ |
| 1259 | if (!ftrace_lookup_ip(other_hash, rec->ip)) | 1321 | if (!other_hash || !ftrace_lookup_ip(other_hash, rec->ip)) |
| 1260 | match = 1; | 1322 | match = 1; |
| 1261 | } else { | 1323 | } else { |
| 1262 | in_hash = !!ftrace_lookup_ip(hash, rec->ip); | 1324 | in_hash = hash && !!ftrace_lookup_ip(hash, rec->ip); |
| 1263 | in_other_hash = !!ftrace_lookup_ip(other_hash, rec->ip); | 1325 | in_other_hash = other_hash && !!ftrace_lookup_ip(other_hash, rec->ip); |
| 1264 | 1326 | ||
| 1265 | /* | 1327 | /* |
| 1266 | * | 1328 | * |
| @@ -1546,6 +1608,7 @@ static void ftrace_run_update_code(int command) | |||
| 1546 | 1608 | ||
| 1547 | static ftrace_func_t saved_ftrace_func; | 1609 | static ftrace_func_t saved_ftrace_func; |
| 1548 | static int ftrace_start_up; | 1610 | static int ftrace_start_up; |
| 1611 | static int global_start_up; | ||
| 1549 | 1612 | ||
| 1550 | static void ftrace_startup_enable(int command) | 1613 | static void ftrace_startup_enable(int command) |
| 1551 | { | 1614 | { |
| @@ -1562,14 +1625,25 @@ static void ftrace_startup_enable(int command) | |||
| 1562 | 1625 | ||
| 1563 | static void ftrace_startup(struct ftrace_ops *ops, int command) | 1626 | static void ftrace_startup(struct ftrace_ops *ops, int command) |
| 1564 | { | 1627 | { |
| 1628 | bool hash_enable = true; | ||
| 1629 | |||
| 1565 | if (unlikely(ftrace_disabled)) | 1630 | if (unlikely(ftrace_disabled)) |
| 1566 | return; | 1631 | return; |
| 1567 | 1632 | ||
| 1568 | ftrace_start_up++; | 1633 | ftrace_start_up++; |
| 1569 | command |= FTRACE_ENABLE_CALLS; | 1634 | command |= FTRACE_ENABLE_CALLS; |
| 1570 | 1635 | ||
| 1636 | /* ops marked global share the filter hashes */ | ||
| 1637 | if (ops->flags & FTRACE_OPS_FL_GLOBAL) { | ||
| 1638 | ops = &global_ops; | ||
| 1639 | /* Don't update hash if global is already set */ | ||
| 1640 | if (global_start_up) | ||
| 1641 | hash_enable = false; | ||
| 1642 | global_start_up++; | ||
| 1643 | } | ||
| 1644 | |||
| 1571 | ops->flags |= FTRACE_OPS_FL_ENABLED; | 1645 | ops->flags |= FTRACE_OPS_FL_ENABLED; |
| 1572 | if (ftrace_start_up == 1) | 1646 | if (hash_enable) |
| 1573 | ftrace_hash_rec_enable(ops, 1); | 1647 | ftrace_hash_rec_enable(ops, 1); |
| 1574 | 1648 | ||
| 1575 | ftrace_startup_enable(command); | 1649 | ftrace_startup_enable(command); |
| @@ -1577,6 +1651,8 @@ static void ftrace_startup(struct ftrace_ops *ops, int command) | |||
| 1577 | 1651 | ||
| 1578 | static void ftrace_shutdown(struct ftrace_ops *ops, int command) | 1652 | static void ftrace_shutdown(struct ftrace_ops *ops, int command) |
| 1579 | { | 1653 | { |
| 1654 | bool hash_disable = true; | ||
| 1655 | |||
| 1580 | if (unlikely(ftrace_disabled)) | 1656 | if (unlikely(ftrace_disabled)) |
| 1581 | return; | 1657 | return; |
| 1582 | 1658 | ||
| @@ -1588,13 +1664,25 @@ static void ftrace_shutdown(struct ftrace_ops *ops, int command) | |||
| 1588 | */ | 1664 | */ |
| 1589 | WARN_ON_ONCE(ftrace_start_up < 0); | 1665 | WARN_ON_ONCE(ftrace_start_up < 0); |
| 1590 | 1666 | ||
| 1591 | if (!ftrace_start_up) | 1667 | if (ops->flags & FTRACE_OPS_FL_GLOBAL) { |
| 1668 | ops = &global_ops; | ||
| 1669 | global_start_up--; | ||
| 1670 | WARN_ON_ONCE(global_start_up < 0); | ||
| 1671 | /* Don't update hash if global still has users */ | ||
| 1672 | if (global_start_up) { | ||
| 1673 | WARN_ON_ONCE(!ftrace_start_up); | ||
| 1674 | hash_disable = false; | ||
| 1675 | } | ||
| 1676 | } | ||
| 1677 | |||
| 1678 | if (hash_disable) | ||
| 1592 | ftrace_hash_rec_disable(ops, 1); | 1679 | ftrace_hash_rec_disable(ops, 1); |
| 1593 | 1680 | ||
| 1594 | if (!ftrace_start_up) { | 1681 | if (ops != &global_ops || !global_start_up) |
| 1595 | command |= FTRACE_DISABLE_CALLS; | ||
| 1596 | ops->flags &= ~FTRACE_OPS_FL_ENABLED; | 1682 | ops->flags &= ~FTRACE_OPS_FL_ENABLED; |
| 1597 | } | 1683 | |
| 1684 | if (!ftrace_start_up) | ||
| 1685 | command |= FTRACE_DISABLE_CALLS; | ||
| 1598 | 1686 | ||
| 1599 | if (saved_ftrace_func != ftrace_trace_function) { | 1687 | if (saved_ftrace_func != ftrace_trace_function) { |
| 1600 | saved_ftrace_func = ftrace_trace_function; | 1688 | saved_ftrace_func = ftrace_trace_function; |
| @@ -2381,6 +2469,7 @@ static int ftrace_probe_registered; | |||
| 2381 | 2469 | ||
| 2382 | static void __enable_ftrace_function_probe(void) | 2470 | static void __enable_ftrace_function_probe(void) |
| 2383 | { | 2471 | { |
| 2472 | int ret; | ||
| 2384 | int i; | 2473 | int i; |
| 2385 | 2474 | ||
| 2386 | if (ftrace_probe_registered) | 2475 | if (ftrace_probe_registered) |
| @@ -2395,13 +2484,16 @@ static void __enable_ftrace_function_probe(void) | |||
| 2395 | if (i == FTRACE_FUNC_HASHSIZE) | 2484 | if (i == FTRACE_FUNC_HASHSIZE) |
| 2396 | return; | 2485 | return; |
| 2397 | 2486 | ||
| 2398 | __register_ftrace_function(&trace_probe_ops); | 2487 | ret = __register_ftrace_function(&trace_probe_ops); |
| 2399 | ftrace_startup(&global_ops, 0); | 2488 | if (!ret) |
| 2489 | ftrace_startup(&trace_probe_ops, 0); | ||
| 2490 | |||
| 2400 | ftrace_probe_registered = 1; | 2491 | ftrace_probe_registered = 1; |
| 2401 | } | 2492 | } |
| 2402 | 2493 | ||
| 2403 | static void __disable_ftrace_function_probe(void) | 2494 | static void __disable_ftrace_function_probe(void) |
| 2404 | { | 2495 | { |
| 2496 | int ret; | ||
| 2405 | int i; | 2497 | int i; |
| 2406 | 2498 | ||
| 2407 | if (!ftrace_probe_registered) | 2499 | if (!ftrace_probe_registered) |
| @@ -2414,8 +2506,10 @@ static void __disable_ftrace_function_probe(void) | |||
| 2414 | } | 2506 | } |
| 2415 | 2507 | ||
| 2416 | /* no more funcs left */ | 2508 | /* no more funcs left */ |
| 2417 | __unregister_ftrace_function(&trace_probe_ops); | 2509 | ret = __unregister_ftrace_function(&trace_probe_ops); |
| 2418 | ftrace_shutdown(&global_ops, 0); | 2510 | if (!ret) |
| 2511 | ftrace_shutdown(&trace_probe_ops, 0); | ||
| 2512 | |||
| 2419 | ftrace_probe_registered = 0; | 2513 | ftrace_probe_registered = 0; |
| 2420 | } | 2514 | } |
| 2421 | 2515 | ||
| @@ -3319,8 +3413,28 @@ static inline void ftrace_startup_enable(int command) { } | |||
| 3319 | # define ftrace_shutdown(ops, command) do { } while (0) | 3413 | # define ftrace_shutdown(ops, command) do { } while (0) |
| 3320 | # define ftrace_startup_sysctl() do { } while (0) | 3414 | # define ftrace_startup_sysctl() do { } while (0) |
| 3321 | # define ftrace_shutdown_sysctl() do { } while (0) | 3415 | # define ftrace_shutdown_sysctl() do { } while (0) |
| 3416 | |||
| 3417 | static inline int | ||
| 3418 | ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) | ||
| 3419 | { | ||
| 3420 | return 1; | ||
| 3421 | } | ||
| 3422 | |||
| 3322 | #endif /* CONFIG_DYNAMIC_FTRACE */ | 3423 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
| 3323 | 3424 | ||
| 3425 | static void | ||
| 3426 | ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip) | ||
| 3427 | { | ||
| 3428 | /* see comment above ftrace_global_list_func */ | ||
| 3429 | struct ftrace_ops *op = rcu_dereference_raw(ftrace_ops_list); | ||
| 3430 | |||
| 3431 | while (op != &ftrace_list_end) { | ||
| 3432 | if (ftrace_ops_test(op, ip)) | ||
| 3433 | op->func(ip, parent_ip); | ||
| 3434 | op = rcu_dereference_raw(op->next); | ||
| 3435 | }; | ||
| 3436 | } | ||
| 3437 | |||
| 3324 | static void clear_ftrace_swapper(void) | 3438 | static void clear_ftrace_swapper(void) |
| 3325 | { | 3439 | { |
| 3326 | struct task_struct *p; | 3440 | struct task_struct *p; |
| @@ -3621,7 +3735,9 @@ int register_ftrace_function(struct ftrace_ops *ops) | |||
| 3621 | goto out_unlock; | 3735 | goto out_unlock; |
| 3622 | 3736 | ||
| 3623 | ret = __register_ftrace_function(ops); | 3737 | ret = __register_ftrace_function(ops); |
| 3624 | ftrace_startup(&global_ops, 0); | 3738 | if (!ret) |
| 3739 | ftrace_startup(ops, 0); | ||
| 3740 | |||
| 3625 | 3741 | ||
| 3626 | out_unlock: | 3742 | out_unlock: |
| 3627 | mutex_unlock(&ftrace_lock); | 3743 | mutex_unlock(&ftrace_lock); |
| @@ -3640,7 +3756,8 @@ int unregister_ftrace_function(struct ftrace_ops *ops) | |||
| 3640 | 3756 | ||
| 3641 | mutex_lock(&ftrace_lock); | 3757 | mutex_lock(&ftrace_lock); |
| 3642 | ret = __unregister_ftrace_function(ops); | 3758 | ret = __unregister_ftrace_function(ops); |
| 3643 | ftrace_shutdown(&global_ops, 0); | 3759 | if (!ret) |
| 3760 | ftrace_shutdown(ops, 0); | ||
| 3644 | mutex_unlock(&ftrace_lock); | 3761 | mutex_unlock(&ftrace_lock); |
| 3645 | 3762 | ||
| 3646 | return ret; | 3763 | return ret; |
| @@ -3670,11 +3787,11 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, | |||
| 3670 | ftrace_startup_sysctl(); | 3787 | ftrace_startup_sysctl(); |
| 3671 | 3788 | ||
| 3672 | /* we are starting ftrace again */ | 3789 | /* we are starting ftrace again */ |
| 3673 | if (ftrace_list != &ftrace_list_end) { | 3790 | if (ftrace_ops_list != &ftrace_list_end) { |
| 3674 | if (ftrace_list->next == &ftrace_list_end) | 3791 | if (ftrace_ops_list->next == &ftrace_list_end) |
| 3675 | ftrace_trace_function = ftrace_list->func; | 3792 | ftrace_trace_function = ftrace_ops_list->func; |
| 3676 | else | 3793 | else |
| 3677 | ftrace_trace_function = ftrace_list_func; | 3794 | ftrace_trace_function = ftrace_ops_list_func; |
| 3678 | } | 3795 | } |
| 3679 | 3796 | ||
| 3680 | } else { | 3797 | } else { |
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index 16aee4d44e8f..8d0e1cc4e974 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c | |||
| @@ -149,11 +149,13 @@ function_stack_trace_call(unsigned long ip, unsigned long parent_ip) | |||
| 149 | static struct ftrace_ops trace_ops __read_mostly = | 149 | static struct ftrace_ops trace_ops __read_mostly = |
| 150 | { | 150 | { |
| 151 | .func = function_trace_call, | 151 | .func = function_trace_call, |
| 152 | .flags = FTRACE_OPS_FL_GLOBAL, | ||
| 152 | }; | 153 | }; |
| 153 | 154 | ||
| 154 | static struct ftrace_ops trace_stack_ops __read_mostly = | 155 | static struct ftrace_ops trace_stack_ops __read_mostly = |
| 155 | { | 156 | { |
| 156 | .func = function_stack_trace_call, | 157 | .func = function_stack_trace_call, |
| 158 | .flags = FTRACE_OPS_FL_GLOBAL, | ||
| 157 | }; | 159 | }; |
| 158 | 160 | ||
| 159 | /* Our two options */ | 161 | /* Our two options */ |
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index a4969b47afc1..c77424be284d 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c | |||
| @@ -153,6 +153,7 @@ irqsoff_tracer_call(unsigned long ip, unsigned long parent_ip) | |||
| 153 | static struct ftrace_ops trace_ops __read_mostly = | 153 | static struct ftrace_ops trace_ops __read_mostly = |
| 154 | { | 154 | { |
| 155 | .func = irqsoff_tracer_call, | 155 | .func = irqsoff_tracer_call, |
| 156 | .flags = FTRACE_OPS_FL_GLOBAL, | ||
| 156 | }; | 157 | }; |
| 157 | #endif /* CONFIG_FUNCTION_TRACER */ | 158 | #endif /* CONFIG_FUNCTION_TRACER */ |
| 158 | 159 | ||
diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c index 7319559ed59f..f029dd4fd2ca 100644 --- a/kernel/trace/trace_sched_wakeup.c +++ b/kernel/trace/trace_sched_wakeup.c | |||
| @@ -129,6 +129,7 @@ wakeup_tracer_call(unsigned long ip, unsigned long parent_ip) | |||
| 129 | static struct ftrace_ops trace_ops __read_mostly = | 129 | static struct ftrace_ops trace_ops __read_mostly = |
| 130 | { | 130 | { |
| 131 | .func = wakeup_tracer_call, | 131 | .func = wakeup_tracer_call, |
| 132 | .flags = FTRACE_OPS_FL_GLOBAL, | ||
| 132 | }; | 133 | }; |
| 133 | #endif /* CONFIG_FUNCTION_TRACER */ | 134 | #endif /* CONFIG_FUNCTION_TRACER */ |
| 134 | 135 | ||
diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c index 4c5dead0c239..b0b53b8e4c25 100644 --- a/kernel/trace/trace_stack.c +++ b/kernel/trace/trace_stack.c | |||
| @@ -133,6 +133,7 @@ stack_trace_call(unsigned long ip, unsigned long parent_ip) | |||
| 133 | static struct ftrace_ops trace_ops __read_mostly = | 133 | static struct ftrace_ops trace_ops __read_mostly = |
| 134 | { | 134 | { |
| 135 | .func = stack_trace_call, | 135 | .func = stack_trace_call, |
| 136 | .flags = FTRACE_OPS_FL_GLOBAL, | ||
| 136 | }; | 137 | }; |
| 137 | 138 | ||
| 138 | static ssize_t | 139 | static ssize_t |
