aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/trace/ftrace.txt5
-rw-r--r--include/linux/ftrace.h16
-rw-r--r--kernel/trace/ftrace.c142
3 files changed, 159 insertions, 4 deletions
diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt
index 4da42616939f..f10f5f5d260d 100644
--- a/Documentation/trace/ftrace.txt
+++ b/Documentation/trace/ftrace.txt
@@ -234,6 +234,11 @@ of ftrace. Here is a list of some of the key files:
234 will be displayed on the same line as the function that 234 will be displayed on the same line as the function that
235 is returning registers. 235 is returning registers.
236 236
237 If the callback registered to be traced by a function with
238 the "ip modify" attribute (thus the regs->ip can be changed),
239 an 'I' will be displayed on the same line as the function that
240 can be overridden.
241
237 function_profile_enabled: 242 function_profile_enabled:
238 243
239 When set it will enable all functions with either the function 244 When set it will enable all functions with either the function
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 7b2616fa2472..ed501953f0b2 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -61,6 +61,11 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops);
61/* 61/*
62 * FTRACE_OPS_FL_* bits denote the state of ftrace_ops struct and are 62 * FTRACE_OPS_FL_* bits denote the state of ftrace_ops struct and are
63 * set in the flags member. 63 * set in the flags member.
64 * CONTROL, SAVE_REGS, SAVE_REGS_IF_SUPPORTED, RECURSION_SAFE, STUB and
65 * IPMODIFY are a kind of attribute flags which can be set only before
66 * registering the ftrace_ops, and can not be modified while registered.
67 * Changing those attribute flags after regsitering ftrace_ops will
68 * cause unexpected results.
64 * 69 *
65 * ENABLED - set/unset when ftrace_ops is registered/unregistered 70 * ENABLED - set/unset when ftrace_ops is registered/unregistered
66 * DYNAMIC - set when ftrace_ops is registered to denote dynamically 71 * DYNAMIC - set when ftrace_ops is registered to denote dynamically
@@ -101,6 +106,10 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops);
101 * The ftrace_ops trampoline can be set by the ftrace users, and 106 * The ftrace_ops trampoline can be set by the ftrace users, and
102 * in such cases the arch must not modify it. Only the arch ftrace 107 * in such cases the arch must not modify it. Only the arch ftrace
103 * core code should set this flag. 108 * core code should set this flag.
109 * IPMODIFY - The ops can modify the IP register. This can only be set with
110 * SAVE_REGS. If another ops with this flag set is already registered
111 * for any of the functions that this ops will be registered for, then
112 * this ops will fail to register or set_filter_ip.
104 */ 113 */
105enum { 114enum {
106 FTRACE_OPS_FL_ENABLED = 1 << 0, 115 FTRACE_OPS_FL_ENABLED = 1 << 0,
@@ -116,6 +125,7 @@ enum {
116 FTRACE_OPS_FL_REMOVING = 1 << 10, 125 FTRACE_OPS_FL_REMOVING = 1 << 10,
117 FTRACE_OPS_FL_MODIFYING = 1 << 11, 126 FTRACE_OPS_FL_MODIFYING = 1 << 11,
118 FTRACE_OPS_FL_ALLOC_TRAMP = 1 << 12, 127 FTRACE_OPS_FL_ALLOC_TRAMP = 1 << 12,
128 FTRACE_OPS_FL_IPMODIFY = 1 << 13,
119}; 129};
120 130
121#ifdef CONFIG_DYNAMIC_FTRACE 131#ifdef CONFIG_DYNAMIC_FTRACE
@@ -310,6 +320,7 @@ bool is_ftrace_trampoline(unsigned long addr);
310 * ENABLED - the function is being traced 320 * ENABLED - the function is being traced
311 * REGS - the record wants the function to save regs 321 * REGS - the record wants the function to save regs
312 * REGS_EN - the function is set up to save regs. 322 * REGS_EN - the function is set up to save regs.
323 * IPMODIFY - the record allows for the IP address to be changed.
313 * 324 *
314 * When a new ftrace_ops is registered and wants a function to save 325 * When a new ftrace_ops is registered and wants a function to save
315 * pt_regs, the rec->flag REGS is set. When the function has been 326 * pt_regs, the rec->flag REGS is set. When the function has been
@@ -323,10 +334,11 @@ enum {
323 FTRACE_FL_REGS_EN = (1UL << 29), 334 FTRACE_FL_REGS_EN = (1UL << 29),
324 FTRACE_FL_TRAMP = (1UL << 28), 335 FTRACE_FL_TRAMP = (1UL << 28),
325 FTRACE_FL_TRAMP_EN = (1UL << 27), 336 FTRACE_FL_TRAMP_EN = (1UL << 27),
337 FTRACE_FL_IPMODIFY = (1UL << 26),
326}; 338};
327 339
328#define FTRACE_REF_MAX_SHIFT 27 340#define FTRACE_REF_MAX_SHIFT 26
329#define FTRACE_FL_BITS 5 341#define FTRACE_FL_BITS 6
330#define FTRACE_FL_MASKED_BITS ((1UL << FTRACE_FL_BITS) - 1) 342#define FTRACE_FL_MASKED_BITS ((1UL << FTRACE_FL_BITS) - 1)
331#define FTRACE_FL_MASK (FTRACE_FL_MASKED_BITS << FTRACE_REF_MAX_SHIFT) 343#define FTRACE_FL_MASK (FTRACE_FL_MASKED_BITS << FTRACE_REF_MAX_SHIFT)
332#define FTRACE_REF_MAX ((1UL << FTRACE_REF_MAX_SHIFT) - 1) 344#define FTRACE_REF_MAX ((1UL << FTRACE_REF_MAX_SHIFT) - 1)
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 588af40d33db..929a733d302e 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -1358,6 +1358,9 @@ ftrace_hash_rec_disable_modify(struct ftrace_ops *ops, int filter_hash);
1358static void 1358static void
1359ftrace_hash_rec_enable_modify(struct ftrace_ops *ops, int filter_hash); 1359ftrace_hash_rec_enable_modify(struct ftrace_ops *ops, int filter_hash);
1360 1360
1361static int ftrace_hash_ipmodify_update(struct ftrace_ops *ops,
1362 struct ftrace_hash *new_hash);
1363
1361static int 1364static int
1362ftrace_hash_move(struct ftrace_ops *ops, int enable, 1365ftrace_hash_move(struct ftrace_ops *ops, int enable,
1363 struct ftrace_hash **dst, struct ftrace_hash *src) 1366 struct ftrace_hash **dst, struct ftrace_hash *src)
@@ -1368,8 +1371,13 @@ ftrace_hash_move(struct ftrace_ops *ops, int enable,
1368 struct ftrace_hash *new_hash; 1371 struct ftrace_hash *new_hash;
1369 int size = src->count; 1372 int size = src->count;
1370 int bits = 0; 1373 int bits = 0;
1374 int ret;
1371 int i; 1375 int i;
1372 1376
1377 /* Reject setting notrace hash on IPMODIFY ftrace_ops */
1378 if (ops->flags & FTRACE_OPS_FL_IPMODIFY && !enable)
1379 return -EINVAL;
1380
1373 /* 1381 /*
1374 * If the new source is empty, just free dst and assign it 1382 * If the new source is empty, just free dst and assign it
1375 * the empty_hash. 1383 * the empty_hash.
@@ -1403,6 +1411,16 @@ ftrace_hash_move(struct ftrace_ops *ops, int enable,
1403 } 1411 }
1404 1412
1405update: 1413update:
1414 /* Make sure this can be applied if it is IPMODIFY ftrace_ops */
1415 if (enable) {
1416 /* IPMODIFY should be updated only when filter_hash updating */
1417 ret = ftrace_hash_ipmodify_update(ops, new_hash);
1418 if (ret < 0) {
1419 free_ftrace_hash(new_hash);
1420 return ret;
1421 }
1422 }
1423
1406 /* 1424 /*
1407 * Remove the current set, update the hash and add 1425 * Remove the current set, update the hash and add
1408 * them back. 1426 * them back.
@@ -1767,6 +1785,114 @@ static void ftrace_hash_rec_enable_modify(struct ftrace_ops *ops,
1767 ftrace_hash_rec_update_modify(ops, filter_hash, 1); 1785 ftrace_hash_rec_update_modify(ops, filter_hash, 1);
1768} 1786}
1769 1787
1788/*
1789 * Try to update IPMODIFY flag on each ftrace_rec. Return 0 if it is OK
1790 * or no-needed to update, -EBUSY if it detects a conflict of the flag
1791 * on a ftrace_rec, and -EINVAL if the new_hash tries to trace all recs.
1792 * Note that old_hash and new_hash has below meanings
1793 * - If the hash is NULL, it hits all recs (if IPMODIFY is set, this is rejected)
1794 * - If the hash is EMPTY_HASH, it hits nothing
1795 * - Anything else hits the recs which match the hash entries.
1796 */
1797static int __ftrace_hash_update_ipmodify(struct ftrace_ops *ops,
1798 struct ftrace_hash *old_hash,
1799 struct ftrace_hash *new_hash)
1800{
1801 struct ftrace_page *pg;
1802 struct dyn_ftrace *rec, *end = NULL;
1803 int in_old, in_new;
1804
1805 /* Only update if the ops has been registered */
1806 if (!(ops->flags & FTRACE_OPS_FL_ENABLED))
1807 return 0;
1808
1809 if (!(ops->flags & FTRACE_OPS_FL_IPMODIFY))
1810 return 0;
1811
1812 /*
1813 * Since the IPMODIFY is a very address sensitive action, we do not
1814 * allow ftrace_ops to set all functions to new hash.
1815 */
1816 if (!new_hash || !old_hash)
1817 return -EINVAL;
1818
1819 /* Update rec->flags */
1820 do_for_each_ftrace_rec(pg, rec) {
1821 /* We need to update only differences of filter_hash */
1822 in_old = !!ftrace_lookup_ip(old_hash, rec->ip);
1823 in_new = !!ftrace_lookup_ip(new_hash, rec->ip);
1824 if (in_old == in_new)
1825 continue;
1826
1827 if (in_new) {
1828 /* New entries must ensure no others are using it */
1829 if (rec->flags & FTRACE_FL_IPMODIFY)
1830 goto rollback;
1831 rec->flags |= FTRACE_FL_IPMODIFY;
1832 } else /* Removed entry */
1833 rec->flags &= ~FTRACE_FL_IPMODIFY;
1834 } while_for_each_ftrace_rec();
1835
1836 return 0;
1837
1838rollback:
1839 end = rec;
1840
1841 /* Roll back what we did above */
1842 do_for_each_ftrace_rec(pg, rec) {
1843 if (rec == end)
1844 goto err_out;
1845
1846 in_old = !!ftrace_lookup_ip(old_hash, rec->ip);
1847 in_new = !!ftrace_lookup_ip(new_hash, rec->ip);
1848 if (in_old == in_new)
1849 continue;
1850
1851 if (in_new)
1852 rec->flags &= ~FTRACE_FL_IPMODIFY;
1853 else
1854 rec->flags |= FTRACE_FL_IPMODIFY;
1855 } while_for_each_ftrace_rec();
1856
1857err_out:
1858 return -EBUSY;
1859}
1860
1861static int ftrace_hash_ipmodify_enable(struct ftrace_ops *ops)
1862{
1863 struct ftrace_hash *hash = ops->func_hash->filter_hash;
1864
1865 if (ftrace_hash_empty(hash))
1866 hash = NULL;
1867
1868 return __ftrace_hash_update_ipmodify(ops, EMPTY_HASH, hash);
1869}
1870
1871/* Disabling always succeeds */
1872static void ftrace_hash_ipmodify_disable(struct ftrace_ops *ops)
1873{
1874 struct ftrace_hash *hash = ops->func_hash->filter_hash;
1875
1876 if (ftrace_hash_empty(hash))
1877 hash = NULL;
1878
1879 __ftrace_hash_update_ipmodify(ops, hash, EMPTY_HASH);
1880}
1881
1882static int ftrace_hash_ipmodify_update(struct ftrace_ops *ops,
1883 struct ftrace_hash *new_hash)
1884{
1885 struct ftrace_hash *old_hash = ops->func_hash->filter_hash;
1886
1887 if (ftrace_hash_empty(old_hash))
1888 old_hash = NULL;
1889
1890 if (ftrace_hash_empty(new_hash))
1891 new_hash = NULL;
1892
1893 return __ftrace_hash_update_ipmodify(ops, old_hash, new_hash);
1894}
1895
1770static void print_ip_ins(const char *fmt, unsigned char *p) 1896static void print_ip_ins(const char *fmt, unsigned char *p)
1771{ 1897{
1772 int i; 1898 int i;
@@ -2436,6 +2562,15 @@ static int ftrace_startup(struct ftrace_ops *ops, int command)
2436 */ 2562 */
2437 ops->flags |= FTRACE_OPS_FL_ENABLED | FTRACE_OPS_FL_ADDING; 2563 ops->flags |= FTRACE_OPS_FL_ENABLED | FTRACE_OPS_FL_ADDING;
2438 2564
2565 ret = ftrace_hash_ipmodify_enable(ops);
2566 if (ret < 0) {
2567 /* Rollback registration process */
2568 __unregister_ftrace_function(ops);
2569 ftrace_start_up--;
2570 ops->flags &= ~FTRACE_OPS_FL_ENABLED;
2571 return ret;
2572 }
2573
2439 ftrace_hash_rec_enable(ops, 1); 2574 ftrace_hash_rec_enable(ops, 1);
2440 2575
2441 ftrace_startup_enable(command); 2576 ftrace_startup_enable(command);
@@ -2464,6 +2599,8 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
2464 */ 2599 */
2465 WARN_ON_ONCE(ftrace_start_up < 0); 2600 WARN_ON_ONCE(ftrace_start_up < 0);
2466 2601
2602 /* Disabling ipmodify never fails */
2603 ftrace_hash_ipmodify_disable(ops);
2467 ftrace_hash_rec_disable(ops, 1); 2604 ftrace_hash_rec_disable(ops, 1);
2468 2605
2469 ops->flags &= ~FTRACE_OPS_FL_ENABLED; 2606 ops->flags &= ~FTRACE_OPS_FL_ENABLED;
@@ -3058,9 +3195,10 @@ static int t_show(struct seq_file *m, void *v)
3058 if (iter->flags & FTRACE_ITER_ENABLED) { 3195 if (iter->flags & FTRACE_ITER_ENABLED) {
3059 struct ftrace_ops *ops = NULL; 3196 struct ftrace_ops *ops = NULL;
3060 3197
3061 seq_printf(m, " (%ld)%s", 3198 seq_printf(m, " (%ld)%s%s",
3062 ftrace_rec_count(rec), 3199 ftrace_rec_count(rec),
3063 rec->flags & FTRACE_FL_REGS ? " R" : " "); 3200 rec->flags & FTRACE_FL_REGS ? " R" : " ",
3201 rec->flags & FTRACE_FL_IPMODIFY ? " I" : " ");
3064 if (rec->flags & FTRACE_FL_TRAMP_EN) { 3202 if (rec->flags & FTRACE_FL_TRAMP_EN) {
3065 ops = ftrace_find_tramp_ops_any(rec); 3203 ops = ftrace_find_tramp_ops_any(rec);
3066 if (ops) 3204 if (ops)