| @@ -1358,6 +1358,9 @@ ftrace_hash_rec_disable_modify(struct ftrace_ops *ops, int filter_hash); · 1357 unmodified lines |
| 1358 | static void | 1358 | static void |
| 1359 | ftrace_hash_rec_enable_modify(struct ftrace_ops *ops, int filter_hash); | 1359 | ftrace_hash_rec_enable_modify(struct ftrace_ops *ops, int filter_hash); |
| 1360 | | 1360 | |
| | 1361 | static int ftrace_hash_ipmodify_update(struct ftrace_ops *ops, |
| | 1362 | struct ftrace_hash *new_hash); |
| | 1363 | |
| 1361 | static int | 1364 | static int |
| 1362 | ftrace_hash_move(struct ftrace_ops *ops, int enable, | 1365 | ftrace_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, · 4 unmodified lines |
| 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, · 27 unmodified lines |
| 1403 | } | 1411 | } |
| 1404 | | 1412 | |
| 1405 | update: | 1413 | update: |
| | 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, · 358 unmodified lines |
| 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 | */ |
| | 1797 | static 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 | |
| | 1838 | rollback: |
| | 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 | |
| | 1857 | err_out: |
| | 1858 | return -EBUSY; |
| | 1859 | } |
| | 1860 | |
| | 1861 | static 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 */ |
| | 1872 | static 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 | |
| | 1882 | static 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 | |
| 1770 | static void print_ip_ins(const char *fmt, unsigned char *p) | 1896 | static 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) · 663 unmodified lines |
| 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) · 22 unmodified lines |
| 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) · 588 unmodified lines |
| 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) |