Skip to content

Commit

Permalink
Merge tag 'trace-v5.16-rc4' of git://git.kernel.org/pub/scm/linux/ker…
Browse files Browse the repository at this point in the history
…nel/git/rostedt/linux-trace

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
  • Loading branch information
torvalds committed Dec 10, 2021
2 parents 0d21e66 + c24be24 commit 257dcf2
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 6 deletions.
76 changes: 76 additions & 0 deletions fs/tracefs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,77 @@ struct tracefs_fs_info {
struct tracefs_mount_opts mount_opts;
};

static void change_gid(struct dentry *dentry, kgid_t gid)
{
if (!dentry->d_inode)
return;
dentry->d_inode->i_gid = gid;
}

/*
* Taken from d_walk, but without he need for handling renames.
* Nothing can be renamed while walking the list, as tracefs
* does not support renames. This is only called when mounting
* or remounting the file system, to set all the files to
* the given gid.
*/
static void set_gid(struct dentry *parent, kgid_t gid)
{
struct dentry *this_parent;
struct list_head *next;

this_parent = parent;
spin_lock(&this_parent->d_lock);

change_gid(this_parent, gid);
repeat:
next = this_parent->d_subdirs.next;
resume:
while (next != &this_parent->d_subdirs) {
struct list_head *tmp = next;
struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
next = tmp->next;

spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);

change_gid(dentry, gid);

if (!list_empty(&dentry->d_subdirs)) {
spin_unlock(&this_parent->d_lock);
spin_release(&dentry->d_lock.dep_map, _RET_IP_);
this_parent = dentry;
spin_acquire(&this_parent->d_lock.dep_map, 0, 1, _RET_IP_);
goto repeat;
}
spin_unlock(&dentry->d_lock);
}
/*
* All done at this level ... ascend and resume the search.
*/
rcu_read_lock();
ascend:
if (this_parent != parent) {
struct dentry *child = this_parent;
this_parent = child->d_parent;

spin_unlock(&child->d_lock);
spin_lock(&this_parent->d_lock);

/* go into the first sibling still alive */
do {
next = child->d_child.next;
if (next == &this_parent->d_subdirs)
goto ascend;
child = list_entry(next, struct dentry, d_child);
} while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED));
rcu_read_unlock();
goto resume;
}
rcu_read_unlock();
spin_unlock(&this_parent->d_lock);
return;
}

static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts)
{
substring_t args[MAX_OPT_ARGS];
Expand Down Expand Up @@ -193,6 +264,7 @@ static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts)
if (!gid_valid(gid))
return -EINVAL;
opts->gid = gid;
set_gid(tracefs_mount->mnt_root, gid);
break;
case Opt_mode:
if (match_octal(&args[0], &option))
Expand Down Expand Up @@ -414,6 +486,8 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode,
inode->i_mode = mode;
inode->i_fop = fops ? fops : &tracefs_file_operations;
inode->i_private = data;
inode->i_uid = d_inode(dentry->d_parent)->i_uid;
inode->i_gid = d_inode(dentry->d_parent)->i_gid;
d_instantiate(dentry, inode);
fsnotify_create(dentry->d_parent->d_inode, dentry);
return end_creating(dentry);
Expand All @@ -436,6 +510,8 @@ static struct dentry *__create_dir(const char *name, struct dentry *parent,
inode->i_mode = S_IFDIR | S_IRWXU | S_IRUSR| S_IRGRP | S_IXUSR | S_IXGRP;
inode->i_op = ops;
inode->i_fop = &simple_dir_operations;
inode->i_uid = d_inode(dentry->d_parent)->i_uid;
inode->i_gid = d_inode(dentry->d_parent)->i_gid;

/* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink(inode);
Expand Down
8 changes: 7 additions & 1 deletion kernel/trace/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -5217,6 +5217,7 @@ int unregister_ftrace_direct(unsigned long ip, unsigned long addr)
{
struct ftrace_direct_func *direct;
struct ftrace_func_entry *entry;
struct ftrace_hash *hash;
int ret = -ENODEV;

mutex_lock(&direct_mutex);
Expand All @@ -5225,7 +5226,8 @@ int unregister_ftrace_direct(unsigned long ip, unsigned long addr)
if (!entry)
goto out_unlock;

if (direct_functions->count == 1)
hash = direct_ops.func_hash->filter_hash;
if (hash->count == 1)
unregister_ftrace_function(&direct_ops);

ret = ftrace_set_filter_ip(&direct_ops, ip, 1, 0);
Expand Down Expand Up @@ -5540,6 +5542,10 @@ int unregister_ftrace_direct_multi(struct ftrace_ops *ops, unsigned long addr)
err = unregister_ftrace_function(ops);
remove_direct_functions_hash(hash, addr);
mutex_unlock(&direct_mutex);

/* cleanup for possible another register call */
ops->func = NULL;
ops->trampoline = 0;
return err;
}
EXPORT_SYMBOL_GPL(unregister_ftrace_direct_multi);
Expand Down
11 changes: 6 additions & 5 deletions kernel/trace/trace_events_synth.c
Original file line number Diff line number Diff line change
Expand Up @@ -1237,9 +1237,8 @@ static int __create_synth_event(const char *name, const char *raw_fields)
argv + consumed, &consumed,
&field_version);
if (IS_ERR(field)) {
argv_free(argv);
ret = PTR_ERR(field);
goto err;
goto err_free_arg;
}

/*
Expand All @@ -1262,26 +1261,26 @@ static int __create_synth_event(const char *name, const char *raw_fields)
if (cmd_version > 1 && n_fields_this_loop >= 1) {
synth_err(SYNTH_ERR_INVALID_CMD, errpos(field_str));
ret = -EINVAL;
goto err;
goto err_free_arg;
}

fields[n_fields++] = field;
if (n_fields == SYNTH_FIELDS_MAX) {
synth_err(SYNTH_ERR_TOO_MANY_FIELDS, 0);
ret = -EINVAL;
goto err;
goto err_free_arg;
}

n_fields_this_loop++;
}
argv_free(argv);

if (consumed < argc) {
synth_err(SYNTH_ERR_INVALID_CMD, 0);
ret = -EINVAL;
goto err;
}

argv_free(argv);
}

if (n_fields == 0) {
Expand All @@ -1307,6 +1306,8 @@ static int __create_synth_event(const char *name, const char *raw_fields)
kfree(saved_fields);

return ret;
err_free_arg:
argv_free(argv);
err:
for (i = 0; i < n_fields; i++)
free_synth_field(fields[i]);
Expand Down
1 change: 1 addition & 0 deletions samples/ftrace/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ obj-$(CONFIG_SAMPLE_FTRACE_DIRECT) += ftrace-direct.o
obj-$(CONFIG_SAMPLE_FTRACE_DIRECT) += ftrace-direct-too.o
obj-$(CONFIG_SAMPLE_FTRACE_DIRECT) += ftrace-direct-modify.o
obj-$(CONFIG_SAMPLE_FTRACE_DIRECT_MULTI) += ftrace-direct-multi.o
obj-$(CONFIG_SAMPLE_FTRACE_DIRECT_MULTI) += ftrace-direct-multi-modify.o

CFLAGS_sample-trace-array.o := -I$(src)
obj-$(CONFIG_SAMPLE_TRACE_ARRAY) += sample-trace-array.o
152 changes: 152 additions & 0 deletions samples/ftrace/ftrace-direct-multi-modify.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/ftrace.h>
#include <asm/asm-offsets.h>

void my_direct_func1(unsigned long ip)
{
trace_printk("my direct func1 ip %lx\n", ip);
}

void my_direct_func2(unsigned long ip)
{
trace_printk("my direct func2 ip %lx\n", ip);
}

extern void my_tramp1(void *);
extern void my_tramp2(void *);

#ifdef CONFIG_X86_64

asm (
" .pushsection .text, \"ax\", @progbits\n"
" .type my_tramp1, @function\n"
" .globl my_tramp1\n"
" my_tramp1:"
" pushq %rbp\n"
" movq %rsp, %rbp\n"
" pushq %rdi\n"
" movq 8(%rbp), %rdi\n"
" call my_direct_func1\n"
" popq %rdi\n"
" leave\n"
" ret\n"
" .size my_tramp1, .-my_tramp1\n"
" .type my_tramp2, @function\n"
"\n"
" .globl my_tramp2\n"
" my_tramp2:"
" pushq %rbp\n"
" movq %rsp, %rbp\n"
" pushq %rdi\n"
" movq 8(%rbp), %rdi\n"
" call my_direct_func2\n"
" popq %rdi\n"
" leave\n"
" ret\n"
" .size my_tramp2, .-my_tramp2\n"
" .popsection\n"
);

#endif /* CONFIG_X86_64 */

#ifdef CONFIG_S390

asm (
" .pushsection .text, \"ax\", @progbits\n"
" .type my_tramp1, @function\n"
" .globl my_tramp1\n"
" my_tramp1:"
" lgr %r1,%r15\n"
" stmg %r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n"
" stg %r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n"
" aghi %r15,"__stringify(-STACK_FRAME_OVERHEAD)"\n"
" stg %r1,"__stringify(__SF_BACKCHAIN)"(%r15)\n"
" lgr %r2,%r0\n"
" brasl %r14,my_direct_func1\n"
" aghi %r15,"__stringify(STACK_FRAME_OVERHEAD)"\n"
" lmg %r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n"
" lg %r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n"
" lgr %r1,%r0\n"
" br %r1\n"
" .size my_tramp1, .-my_tramp1\n"
"\n"
" .type my_tramp2, @function\n"
" .globl my_tramp2\n"
" my_tramp2:"
" lgr %r1,%r15\n"
" stmg %r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n"
" stg %r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n"
" aghi %r15,"__stringify(-STACK_FRAME_OVERHEAD)"\n"
" stg %r1,"__stringify(__SF_BACKCHAIN)"(%r15)\n"
" lgr %r2,%r0\n"
" brasl %r14,my_direct_func2\n"
" aghi %r15,"__stringify(STACK_FRAME_OVERHEAD)"\n"
" lmg %r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n"
" lg %r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n"
" lgr %r1,%r0\n"
" br %r1\n"
" .size my_tramp2, .-my_tramp2\n"
" .popsection\n"
);

#endif /* CONFIG_S390 */

static unsigned long my_tramp = (unsigned long)my_tramp1;
static unsigned long tramps[2] = {
(unsigned long)my_tramp1,
(unsigned long)my_tramp2,
};

static struct ftrace_ops direct;

static int simple_thread(void *arg)
{
static int t;
int ret = 0;

while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(2 * HZ);

if (ret)
continue;
t ^= 1;
ret = modify_ftrace_direct_multi(&direct, tramps[t]);
if (!ret)
my_tramp = tramps[t];
WARN_ON_ONCE(ret);
}

return 0;
}

static struct task_struct *simple_tsk;

static int __init ftrace_direct_multi_init(void)
{
int ret;

ftrace_set_filter_ip(&direct, (unsigned long) wake_up_process, 0, 0);
ftrace_set_filter_ip(&direct, (unsigned long) schedule, 0, 0);

ret = register_ftrace_direct_multi(&direct, my_tramp);

if (!ret)
simple_tsk = kthread_run(simple_thread, NULL, "event-sample-fn");
return ret;
}

static void __exit ftrace_direct_multi_exit(void)
{
kthread_stop(simple_tsk);
unregister_ftrace_direct_multi(&direct, my_tramp);
}

module_init(ftrace_direct_multi_init);
module_exit(ftrace_direct_multi_exit);

MODULE_AUTHOR("Jiri Olsa");
MODULE_DESCRIPTION("Example use case of using modify_ftrace_direct_multi()");
MODULE_LICENSE("GPL");

0 comments on commit 257dcf2

Please sign in to comment.