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 | struct tracefs_mount_opts mount_opts; | 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 | static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts) | 235 | static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts) |
| 165 | { | 236 | { |
| 166 | substring_t args[MAX_OPT_ARGS]; | 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 | if (!gid_valid(gid)) | 264 | if (!gid_valid(gid)) |
| 194 | return -EINVAL; | 265 | return -EINVAL; |
| 195 | opts->gid = gid; | 266 | opts->gid = gid; |
| 267 | set_gid(tracefs_mount->mnt_root, gid); | ||
| 196 | break; | 268 | break; |
| 197 | case Opt_mode: | 269 | case Opt_mode: |
| 198 | if (match_octal(&args[0], &option)) | 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 | inode->i_mode = mode; | 486 | inode->i_mode = mode; |
| 415 | inode->i_fop = fops ? fops : &tracefs_file_operations; | 487 | inode->i_fop = fops ? fops : &tracefs_file_operations; |
| 416 | inode->i_private = data; | 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 | d_instantiate(dentry, inode); | 491 | d_instantiate(dentry, inode); |
| 418 | fsnotify_create(dentry->d_parent->d_inode, dentry); | 492 | fsnotify_create(dentry->d_parent->d_inode, dentry); |
| 419 | return end_creating(dentry); | 493 | return end_creating(dentry); |
| @@ -436,6 +510,8 @@ static struct dentry *__create_dir(const char *name, struct dentry *parent, · 16 unmodified lines | |||
| 436 | inode->i_mode = S_IFDIR | S_IRWXU | S_IRUSR| S_IRGRP | S_IXUSR | S_IXGRP; | 510 | inode->i_mode = S_IFDIR | S_IRWXU | S_IRUSR| S_IRGRP | S_IXUSR | S_IXGRP; |
| 437 | inode->i_op = ops; | 511 | inode->i_op = ops; |
| 438 | inode->i_fop = &simple_dir_operations; | 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 | /* directory inodes start off with i_nlink == 2 (for "." entry) */ | 516 | /* directory inodes start off with i_nlink == 2 (for "." entry) */ |
| 441 | inc_nlink(inode); | 517 | inc_nlink(inode); |
| @@ -5217,6 +5217,7 @@ int unregister_ftrace_direct(unsigned long ip, unsigned long addr) · 5216 unmodified lines | |||
| 5217 | { | 5217 | { |
| 5218 | struct ftrace_direct_func *direct; | 5218 | struct ftrace_direct_func *direct; |
| 5219 | struct ftrace_func_entry *entry; | 5219 | struct ftrace_func_entry *entry; |
| 5220 | struct ftrace_hash *hash; | ||
| 5220 | int ret = -ENODEV; | 5221 | int ret = -ENODEV; |
| 5221 | 5222 | ||
| 5222 | mutex_lock(&direct_mutex); | 5223 | mutex_lock(&direct_mutex); |
| @@ -5225,7 +5226,8 @@ int unregister_ftrace_direct(unsigned long ip, unsigned long addr) · 2 unmodified lines | |||
| 5225 | if (!entry) | 5226 | if (!entry) |
| 5226 | goto out_unlock; | 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 | unregister_ftrace_function(&direct_ops); | 5231 | unregister_ftrace_function(&direct_ops); |
| 5230 | 5232 | ||
| 5231 | ret = ftrace_set_filter_ip(&direct_ops, ip, 1, 0); | 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 | err = unregister_ftrace_function(ops); | 5542 | err = unregister_ftrace_function(ops); |
| 5541 | remove_direct_functions_hash(hash, addr); | 5543 | remove_direct_functions_hash(hash, addr); |
| 5542 | mutex_unlock(&direct_mutex); | 5544 | mutex_unlock(&direct_mutex); |
| 5545 | |||
| 5546 | /* cleanup for possible another register call */ | ||
| 5547 | ops->func = NULL; | ||
| 5548 | ops->trampoline = 0; | ||
| 5543 | return err; | 5549 | return err; |
| 5544 | } | 5550 | } |
| 5545 | EXPORT_SYMBOL_GPL(unregister_ftrace_direct_multi); | 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 | argv + consumed, &consumed, | 1237 | argv + consumed, &consumed, |
| 1238 | &field_version); | 1238 | &field_version); |
| 1239 | if (IS_ERR(field)) { | 1239 | if (IS_ERR(field)) { |
| 1240 | argv_free(argv); | ||
| 1241 | ret = PTR_ERR(field); | 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 | if (cmd_version > 1 && n_fields_this_loop >= 1) { | 1261 | if (cmd_version > 1 && n_fields_this_loop >= 1) { |
| 1263 | synth_err(SYNTH_ERR_INVALID_CMD, errpos(field_str)); | 1262 | synth_err(SYNTH_ERR_INVALID_CMD, errpos(field_str)); |
| 1264 | ret = -EINVAL; | 1263 | ret = -EINVAL; |
| 1265 | goto err; | 1264 | goto err_free_arg; |
| 1266 | } | 1265 | } |
| 1267 | 1266 | ||
| 1268 | fields[n_fields++] = field; | 1267 | fields[n_fields++] = field; |
| 1269 | if (n_fields == SYNTH_FIELDS_MAX) { | 1268 | if (n_fields == SYNTH_FIELDS_MAX) { |
| 1270 | synth_err(SYNTH_ERR_TOO_MANY_FIELDS, 0); | 1269 | synth_err(SYNTH_ERR_TOO_MANY_FIELDS, 0); |
| 1271 | ret = -EINVAL; | 1270 | ret = -EINVAL; |
| 1272 | goto err; | 1271 | goto err_free_arg; |
| 1273 | } | 1272 | } |
| 1274 | 1273 | ||
| 1275 | n_fields_this_loop++; | 1274 | n_fields_this_loop++; |
| 1276 | } | 1275 | } |
| 1276 | argv_free(argv); | ||
| 1277 | 1277 | ||
| 1278 | if (consumed < argc) { | 1278 | if (consumed < argc) { |
| 1279 | synth_err(SYNTH_ERR_INVALID_CMD, 0); | 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 | goto err; | 1281 | goto err; |
| 1282 | } | 1282 | } |
| 1283 | 1283 | ||
| 1284 | argv_free(argv); | ||
| 1285 | } | 1284 | } |
| 1286 | 1285 | ||
| 1287 | if (n_fields == 0) { | 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 | kfree(saved_fields); | 1306 | kfree(saved_fields); |
| 1308 | 1307 | ||
| 1309 | return ret; | 1308 | return ret; |
| 1309 | err_free_arg: | ||
| 1310 | argv_free(argv); | ||
| 1310 | err: | 1311 | err: |
| 1311 | for (i = 0; i < n_fields; i++) | 1312 | for (i = 0; i < n_fields; i++) |
| 1312 | free_synth_field(fields[i]); | 1313 | free_synth_field(fields[i]); |
| @@ -4,6 +4,7 @@ obj-$(CONFIG_SAMPLE_FTRACE_DIRECT) += ftrace-direct.o · 3 unmodified lines | |||
| 4 | obj-$(CONFIG_SAMPLE_FTRACE_DIRECT) += ftrace-direct-too.o | 4 | obj-$(CONFIG_SAMPLE_FTRACE_DIRECT) += ftrace-direct-too.o |
| 5 | obj-$(CONFIG_SAMPLE_FTRACE_DIRECT) += ftrace-direct-modify.o | 5 | obj-$(CONFIG_SAMPLE_FTRACE_DIRECT) += ftrace-direct-modify.o |
| 6 | obj-$(CONFIG_SAMPLE_FTRACE_DIRECT_MULTI) += ftrace-direct-multi.o | 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 | CFLAGS_sample-trace-array.o := -I$(src) | 9 | CFLAGS_sample-trace-array.o := -I$(src) |
| 9 | obj-$(CONFIG_SAMPLE_TRACE_ARRAY) += sample-trace-array.o | 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"); | ||