Pull tracing fixes from Steven Rostedt: "Tracing, ftrace and tracefs fixes: - Have tracefs honor the gid mount option - Have new files in tracefs inherit the parent ownership - Have direct_ops unregister when it has no more functions - Properly clean up the ops when unregistering multi direct ops - Add a sample module to test the multiple direct ops - Fix memory leak in error path of __create_synth_event()" * tag 'trace-v5.16-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace: tracing: Fix possible memory leak in __create_synth_event() error path ftrace/samples: Add module to test multi direct modify interface ftrace: Add cleanup to unregister_ftrace_direct_multi ftrace: Use direct_ops hash in unregister_ftrace_direct tracefs: Set all files to the same group ownership as the mount option tracefs: Have new files inherit the ownership of their parent
Unified · Split
| @@ -161,6 +161,77 @@ struct tracefs_fs_info { · 160 unmodified lines | ||
| 161 | 161 | struct tracefs_mount_opts mount_opts; |
| 162 | 162 | }; |
| 163 | 163 | |
| 164 | +static void change_gid(struct dentry *dentry, kgid_t gid) | |
| 165 | +{ | |
| 166 | + if (!dentry->d_inode) | |
| 167 | + return; | |
| 168 | + dentry->d_inode->i_gid = gid; | |
| 169 | +} | |
| 170 | + | |
| 171 | +/* | |
| 172 | + * Taken from d_walk, but without he need for handling renames. | |
| 173 | + * Nothing can be renamed while walking the list, as tracefs | |
| 174 | + * does not support renames. This is only called when mounting | |
| 175 | + * or remounting the file system, to set all the files to | |
| 176 | + * the given gid. | |
| 177 | + */ | |
| 178 | +static void set_gid(struct dentry *parent, kgid_t gid) | |
| 179 | +{ | |
| 180 | + struct dentry *this_parent; | |
| 181 | + struct list_head *next; | |
| 182 | + | |
| 183 | + this_parent = parent; | |
| 184 | + spin_lock(&this_parent->d_lock); | |
| 185 | + | |
| 186 | + change_gid(this_parent, gid); | |
| 187 | +repeat: | |
| 188 | + next = this_parent->d_subdirs.next; | |
| 189 | +resume: | |
| 190 | + while (next != &this_parent->d_subdirs) { | |
| 191 | + struct list_head *tmp = next; | |
| 192 | + struct dentry *dentry = list_entry(tmp, struct dentry, d_child); | |
| 193 | + next = tmp->next; | |
| 194 | + | |
| 195 | + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); | |
| 196 | + | |
| 197 | + change_gid(dentry, gid); | |
| 198 | + | |
| 199 | + if (!list_empty(&dentry->d_subdirs)) { | |
| 200 | + spin_unlock(&this_parent->d_lock); | |
| 201 | + spin_release(&dentry->d_lock.dep_map, _RET_IP_); | |
| 202 | + this_parent = dentry; | |
| 203 | + spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_); | |
| 204 | + goto repeat; | |
| 205 | + } | |
| 206 | + spin_unlock(&dentry->d_lock); | |
| 207 | + } | |
| 208 | + /* | |
| 209 | + * All done at this level ... ascend and resume the search. | |
| 210 | + */ | |
| 211 | + rcu_read_lock(); | |
| 212 | +ascend: | |
| 213 | + if (this_parent != parent) { | |
| 214 | + struct dentry *child = this_parent; | |
| 215 | + this_parent = child->d_parent; | |
| 216 | + | |
| 217 | + spin_unlock(&child->d_lock); | |
| 218 | + spin_lock(&this_parent->d_lock); | |
| 219 | + | |
| 220 | + /* go into the first sibling still alive */ | |
| 221 | + do { | |
| 222 | + next = child->d_child.next; | |
| 223 | + if (next == &this_parent->d_subdirs) | |
| 224 | + goto ascend; | |
| 225 | + child = list_entry(next, struct dentry, d_child); | |
| 226 | + } while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED)); | |
| 227 | + rcu_read_unlock(); | |
| 228 | + goto resume; | |
| 229 | + } | |
| 230 | + rcu_read_unlock(); | |
| 231 | + spin_unlock(&this_parent->d_lock); | |
| 232 | + return; | |
| 233 | +} | |
| 234 | + | |
| 164 | 235 | static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts) |
| 165 | 236 | { |
| 166 | 237 | substring_t args[MAX_OPT_ARGS]; |
| @@ -193,6 +264,7 @@ static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts) · 26 unmodified lines | ||
| 193 | 264 | if (!gid_valid(gid)) |
| 194 | 265 | return -EINVAL; |
| 195 | 266 | opts->gid = gid; |
| 267 | + set_gid(tracefs_mount->mnt_root, gid); | |
| 196 | 268 | break; |
| 197 | 269 | case Opt_mode: |
| 198 | 270 | if (match_octal(&args[0], &option)) |
| @@ -414,6 +486,8 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode, · 215 unmodified lines | ||
| 414 | 486 | inode->i_mode = mode; |
| 415 | 487 | inode->i_fop = fops ? fops : &tracefs_file_operations; |
| 416 | 488 | inode->i_private = data; |
| 489 | + inode->i_uid = d_inode(dentry->d_parent)->i_uid; | |
| 490 | + inode->i_gid = d_inode(dentry->d_parent)->i_gid; | |
| 417 | 491 | d_instantiate(dentry, inode); |
| 418 | 492 | fsnotify_create(dentry->d_parent->d_inode, dentry); |
| 419 | 493 | return end_creating(dentry); |
| @@ -436,6 +510,8 @@ static struct dentry *__create_dir(const char *name, struct dentry *parent, · 16 unmodified lines | ||
| 436 | 510 | inode->i_mode = S_IFDIR | S_IRWXU | S_IRUSR| S_IRGRP | S_IXUSR | S_IXGRP; |
| 437 | 511 | inode->i_op = ops; |
| 438 | 512 | inode->i_fop = &simple_dir_operations; |
| 513 | + inode->i_uid = d_inode(dentry->d_parent)->i_uid; | |
| 514 | + inode->i_gid = d_inode(dentry->d_parent)->i_gid; | |
| 439 | 515 | |
| 440 | 516 | /* directory inodes start off with i_nlink == 2 (for "." entry) */ |
| 441 | 517 | inc_nlink(inode); |
| @@ -5217,6 +5217,7 @@ int unregister_ftrace_direct(unsigned long ip, unsigned long addr) · 5216 unmodified lines | ||
| 5217 | 5217 | { |
| 5218 | 5218 | struct ftrace_direct_func *direct; |
| 5219 | 5219 | struct ftrace_func_entry *entry; |
| 5220 | + struct ftrace_hash *hash; | |
| 5220 | 5221 | int ret = -ENODEV; |
| 5221 | 5222 | |
| 5222 | 5223 | mutex_lock(&direct_mutex); |
| @@ -5225,7 +5226,8 @@ int unregister_ftrace_direct(unsigned long ip, unsigned long addr) · 2 unmodified lines | ||
| 5225 | 5226 | if (!entry) |
| 5226 | 5227 | goto out_unlock; |
| 5227 | 5228 | |
| 5228 | − if (direct_functions->count == 1) | |
| 5229 | + hash = direct_ops.func_hash->filter_hash; | |
| 5230 | + if (hash->count == 1) | |
| 5229 | 5231 | unregister_ftrace_function(&direct_ops); |
| 5230 | 5232 | |
| 5231 | 5233 | ret = ftrace_set_filter_ip(&direct_ops, ip, 1, 0); |
| @@ -5540,6 +5542,10 @@ int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr) · 308 unmodified lines | ||
| 5540 | 5542 | err = unregister_ftrace_function(ops); |
| 5541 | 5543 | remove_direct_functions_hash(hash, addr); |
| 5542 | 5544 | mutex_unlock(&direct_mutex); |
| 5545 | + | |
| 5546 | + /* cleanup for possible another register call */ | |
| 5547 | + ops->func = NULL; | |
| 5548 | + ops->trampoline = 0; | |
| 5543 | 5549 | return err; |
| 5544 | 5550 | } |
| 5545 | 5551 | EXPORT_SYMBOL_GPL(unregister_ftrace_direct_multi); |
| @@ -1237,9 +1237,8 @@ static int __create_synth_event(const char *name, const char *raw_fields) · 1236 unmodified lines | ||
| 1237 | 1237 | argv + consumed, &consumed, |
| 1238 | 1238 | &field_version); |
| 1239 | 1239 | if (IS_ERR(field)) { |
| 1240 | − argv_free(argv); | |
| 1241 | 1240 | ret = PTR_ERR(field); |
| 1242 | − goto err; | |
| 1241 | + goto err_free_arg; | |
| 1243 | 1242 | } |
| 1244 | 1243 | |
| 1245 | 1244 | /* |
| @@ -1262,18 +1261,19 @@ static int __create_synth_event(const char *name, const char *raw_fields) · 16 unmodified lines | ||
| 1262 | 1261 | if (cmd_version > 1 && n_fields_this_loop >= 1) { |
| 1263 | 1262 | synth_err(SYNTH_ERR_INVALID_CMD, errpos(field_str)); |
| 1264 | 1263 | ret = -EINVAL; |
| 1265 | − goto err; | |
| 1264 | + goto err_free_arg; | |
| 1266 | 1265 | } |
| 1267 | 1266 | |
| 1268 | 1267 | fields[n_fields++] = field; |
| 1269 | 1268 | if (n_fields == SYNTH_FIELDS_MAX) { |
| 1270 | 1269 | synth_err(SYNTH_ERR_TOO_MANY_FIELDS, 0); |
| 1271 | 1270 | ret = -EINVAL; |
| 1272 | − goto err; | |
| 1271 | + goto err_free_arg; | |
| 1273 | 1272 | } |
| 1274 | 1273 | |
| 1275 | 1274 | n_fields_this_loop++; |
| 1276 | 1275 | } |
| 1276 | + argv_free(argv); | |
| 1277 | 1277 | |
| 1278 | 1278 | if (consumed < argc) { |
| 1279 | 1279 | synth_err(SYNTH_ERR_INVALID_CMD, 0); |
| @@ -1281,7 +1281,6 @@ static int __create_synth_event(const char *name, const char *raw_fields) · 1 unmodified line | ||
| 1281 | 1281 | goto err; |
| 1282 | 1282 | } |
| 1283 | 1283 | |
| 1284 | − argv_free(argv); | |
| 1285 | 1284 | } |
| 1286 | 1285 | |
| 1287 | 1286 | if (n_fields == 0) { |
| @@ -1307,6 +1306,8 @@ static int __create_synth_event(const char *name, const char *raw_fields) · 19 unmodified lines | ||
| 1307 | 1306 | kfree(saved_fields); |
| 1308 | 1307 | |
| 1309 | 1308 | return ret; |
| 1309 | + err_free_arg: | |
| 1310 | + argv_free(argv); | |
| 1310 | 1311 | err: |
| 1311 | 1312 | for (i = 0; i < n_fields; i++) |
| 1312 | 1313 | free_synth_field(fields[i]); |
| @@ -4,6 +4,7 @@ obj-$(CONFIG_SAMPLE_FTRACE_DIRECT) += ftrace-direct.o · 3 unmodified lines | ||
| 4 | 4 | obj-$(CONFIG_SAMPLE_FTRACE_DIRECT) += ftrace-direct-too.o |
| 5 | 5 | obj-$(CONFIG_SAMPLE_FTRACE_DIRECT) += ftrace-direct-modify.o |
| 6 | 6 | obj-$(CONFIG_SAMPLE_FTRACE_DIRECT_MULTI) += ftrace-direct-multi.o |
| 7 | +obj-$(CONFIG_SAMPLE_FTRACE_DIRECT_MULTI) += ftrace-direct-multi-modify.o | |
| 7 | 8 | |
| 8 | 9 | CFLAGS_sample-trace-array.o := -I$(src) |
| 9 | 10 | obj-$(CONFIG_SAMPLE_TRACE_ARRAY) += sample-trace-array.o |
| @@ -0,0 +1,152 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +#include <linux/module.h> | |
| 3 | +#include <linux/kthread.h> | |
| 4 | +#include <linux/ftrace.h> | |
| 5 | +#include <asm/asm-offsets.h> | |
| 6 | + | |
| 7 | +void my_direct_func1(unsigned long ip) | |
| 8 | +{ | |
| 9 | + trace_printk("my direct func1 ip %lx\n", ip); | |
| 10 | +} | |
| 11 | + | |
| 12 | +void my_direct_func2(unsigned long ip) | |
| 13 | +{ | |
| 14 | + trace_printk("my direct func2 ip %lx\n", ip); | |
| 15 | +} | |
| 16 | + | |
| 17 | +extern void my_tramp1(void *); | |
| 18 | +extern void my_tramp2(void *); | |
| 19 | + | |
| 20 | +#ifdef CONFIG_X86_64 | |
| 21 | + | |
| 22 | +asm ( | |
| 23 | +" .pushsection .text, \"ax\", @progbits\n" | |
| 24 | +" .type my_tramp1, @function\n" | |
| 25 | +" .globl my_tramp1\n" | |
| 26 | +" my_tramp1:" | |
| 27 | +" pushq %rbp\n" | |
| 28 | +" movq %rsp, %rbp\n" | |
| 29 | +" pushq %rdi\n" | |
| 30 | +" movq 8(%rbp), %rdi\n" | |
| 31 | +" call my_direct_func1\n" | |
| 32 | +" popq %rdi\n" | |
| 33 | +" leave\n" | |
| 34 | +" ret\n" | |
| 35 | +" .size my_tramp1, .-my_tramp1\n" | |
| 36 | +" .type my_tramp2, @function\n" | |
| 37 | +"\n" | |
| 38 | +" .globl my_tramp2\n" | |
| 39 | +" my_tramp2:" | |
| 40 | +" pushq %rbp\n" | |
| 41 | +" movq %rsp, %rbp\n" | |
| 42 | +" pushq %rdi\n" | |
| 43 | +" movq 8(%rbp), %rdi\n" | |
| 44 | +" call my_direct_func2\n" | |
| 45 | +" popq %rdi\n" | |
| 46 | +" leave\n" | |
| 47 | +" ret\n" | |
| 48 | +" .size my_tramp2, .-my_tramp2\n" | |
| 49 | +" .popsection\n" | |
| 50 | +); | |
| 51 | + | |
| 52 | +#endif /* CONFIG_X86_64 */ | |
| 53 | + | |
| 54 | +#ifdef CONFIG_S390 | |
| 55 | + | |
| 56 | +asm ( | |
| 57 | +" .pushsection .text, \"ax\", @progbits\n" | |
| 58 | +" .type my_tramp1, @function\n" | |
| 59 | +" .globl my_tramp1\n" | |
| 60 | +" my_tramp1:" | |
| 61 | +" lgr %r1,%r15\n" | |
| 62 | +" stmg %r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" | |
| 63 | +" stg %r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" | |
| 64 | +" aghi %r15,"__stringify(-STACK_FRAME_OVERHEAD)"\n" | |
| 65 | +" stg %r1,"__stringify(__SF_BACKCHAIN)"(%r15)\n" | |
| 66 | +" lgr %r2,%r0\n" | |
| 67 | +" brasl %r14,my_direct_func1\n" | |
| 68 | +" aghi %r15,"__stringify(STACK_FRAME_OVERHEAD)"\n" | |
| 69 | +" lmg %r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" | |
| 70 | +" lg %r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" | |
| 71 | +" lgr %r1,%r0\n" | |
| 72 | +" br %r1\n" | |
| 73 | +" .size my_tramp1, .-my_tramp1\n" | |
| 74 | +"\n" | |
| 75 | +" .type my_tramp2, @function\n" | |
| 76 | +" .globl my_tramp2\n" | |
| 77 | +" my_tramp2:" | |
| 78 | +" lgr %r1,%r15\n" | |
| 79 | +" stmg %r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" | |
| 80 | +" stg %r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" | |
| 81 | +" aghi %r15,"__stringify(-STACK_FRAME_OVERHEAD)"\n" | |
| 82 | +" stg %r1,"__stringify(__SF_BACKCHAIN)"(%r15)\n" | |
| 83 | +" lgr %r2,%r0\n" | |
| 84 | +" brasl %r14,my_direct_func2\n" | |
| 85 | +" aghi %r15,"__stringify(STACK_FRAME_OVERHEAD)"\n" | |
| 86 | +" lmg %r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" | |
| 87 | +" lg %r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" | |
| 88 | +" lgr %r1,%r0\n" | |
| 89 | +" br %r1\n" | |
| 90 | +" .size my_tramp2, .-my_tramp2\n" | |
| 91 | +" .popsection\n" | |
| 92 | +); | |
| 93 | + | |
| 94 | +#endif /* CONFIG_S390 */ | |
| 95 | + | |
| 96 | +static unsigned long my_tramp = (unsigned long)my_tramp1; | |
| 97 | +static unsigned long tramps[2] = { | |
| 98 | + (unsigned long)my_tramp1, | |
| 99 | + (unsigned long)my_tramp2, | |
| 100 | +}; | |
| 101 | + | |
| 102 | +static struct ftrace_ops direct; | |
| 103 | + | |
| 104 | +static int simple_thread(void *arg) | |
| 105 | +{ | |
| 106 | + static int t; | |
| 107 | + int ret = 0; | |
| 108 | + | |
| 109 | + while (!kthread_should_stop()) { | |
| 110 | + set_current_state(TASK_INTERRUPTIBLE); | |
| 111 | + schedule_timeout(2 * HZ); | |
| 112 | + | |
| 113 | + if (ret) | |
| 114 | + continue; | |
| 115 | + t ^= 1; | |
| 116 | + ret = modify_ftrace_direct_multi(&direct, tramps[t]); | |
| 117 | + if (!ret) | |
| 118 | + my_tramp = tramps[t]; | |
| 119 | + WARN_ON_ONCE(ret); | |
| 120 | + } | |
| 121 | + | |
| 122 | + return 0; | |
| 123 | +} | |
| 124 | + | |
| 125 | +static struct task_struct *simple_tsk; | |
| 126 | + | |
| 127 | +static int __init ftrace_direct_multi_init(void) | |
| 128 | +{ | |
| 129 | + int ret; | |
| 130 | + | |
| 131 | + ftrace_set_filter_ip(&direct, (unsigned long) wake_up_process, 0, 0); | |
| 132 | + ftrace_set_filter_ip(&direct, (unsigned long) schedule, 0, 0); | |
| 133 | + | |
| 134 | + ret = register_ftrace_direct_multi(&direct, my_tramp); | |
| 135 | + | |
| 136 | + if (!ret) | |
| 137 | + simple_tsk = kthread_run(simple_thread, NULL, "event-sample-fn"); | |
| 138 | + return ret; | |
| 139 | +} | |
| 140 | + | |
| 141 | +static void __exit ftrace_direct_multi_exit(void) | |
| 142 | +{ | |
| 143 | + kthread_stop(simple_tsk); | |
| 144 | + unregister_ftrace_direct_multi(&direct, my_tramp); | |
| 145 | +} | |
| 146 | + | |
| 147 | +module_init(ftrace_direct_multi_init); | |
| 148 | +module_exit(ftrace_direct_multi_exit); | |
| 149 | + | |
| 150 | +MODULE_AUTHOR("Jiri Olsa"); | |
| 151 | +MODULE_DESCRIPTION("Example use case of using modify_ftrace_direct_multi()"); | |
| 152 | +MODULE_LICENSE("GPL"); | |