mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Steven Rostedt <rostedt@goodmis.org>
To: Yaxiong Tian <tianyaxiong@kylinos.cn>
Cc: mhiramat@kernel.org, mark.rutland@arm.com,
	mathieu.desnoyers@efficios.com, linux-kernel@vger.kernel.org,
	linux-trace-kernel@vger.kernel.org
Subject: Re: [RFC PATCH] ftrace: speed up module enable by binary searching module symbols
Date: Wed, 4 Nov 2026 13:43:10 -0500	[thread overview]
Message-ID: <20261104134310.5cec1ebe@gandalf.local.home> (raw)
In-Reply-To: <20260916082715.320853-1-tianyaxiong@kylinos.cn>

On Wed, 16 Sep 2026 16:27:15 +0800
Yaxiong Tian <tianyaxiong@kylinos.cn> wrote:
> 
> Signed-off-by: Yaxiong Tian <tianyaxiong@kylinos.cn>
> ---
>  kernel/trace/ftrace.c | 131 +++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 130 insertions(+), 1 deletion(-)
> 
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 673a54fdf392..92ba9a4dfa69 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -26,6 +26,7 @@
>  #include <linux/uaccess.h>
>  #include <linux/bsearch.h>
>  #include <linux/module.h>
> +#include <linux/module_symbol.h>
>  #include <linux/ftrace.h>
>  #include <linux/sysctl.h>
>  #include <linux/slab.h>
> @@ -8010,11 +8011,137 @@ void ftrace_release_mod(struct module *mod)
>  	}
>  }
>  
> +#ifdef FTRACE_MCOUNT_MAX_OFFSET
> +/*
> + * ftrace_module_enable() validates every record with test_for_valid_rec(),
> + * which walks the whole symbol table of a module for each address in it, so a
> + * large module loads in quadratic time.  Search a sorted copy of the symbol
> + * addresses instead, the way get_symbol_pos() searches the kallsyms table.
> + * The array is an accelerator only; records it cannot resolve are tested the
> + * old way, so the verdict does not change.
> + */
> +struct ftrace_mod_syms {
> +	unsigned int	nr;
> +	unsigned long	addr[];	/* sorted, @nr entries */
> +};
> +
> +static int ftrace_mod_syms_cmp(const void *a, const void *b)
> +{
> +	unsigned long va = *(const unsigned long *)a;
> +	unsigned long vb = *(const unsigned long *)b;
> +
> +	return (va > vb) - (va < vb);
> +}

Interesting compare technique. I usually do:

static int ftrace_mod_syms_cmp(const void *a, const void *b)
{
	unsigned long va = *(const unsigned long *)a;
	unsigned long vb = *(const unsigned long *)b;

	if (va < vb)
		return -1;

	return va > vb;
}

But I wanted to compare them, so I wrote this:

int cmp1(const void *a, const void *b)
{
        int A = *(const int *)a;
        int B = *(const int *)b;

        return (A > B) - (A < B);
}

int cmp2(const void *a, const void *b)
{
        int A = *(const int *)a;
        int B = *(const int *)b;

        if (A < B)
                return -1;
        return A > B;
}

And complied with gcc with -O2 and the result was this:

0000000000000000 <cmp1>:
   0:   8b 06                   mov    (%rsi),%eax
   2:   39 07                   cmp    %eax,(%rdi)
   4:   0f 9f c0                setg   %al
   7:   0f 9c c2                setl   %dl
   a:   0f b6 d2                movzbl %dl,%edx
   d:   0f b6 c0                movzbl %al,%eax
  10:   29 d0                   sub    %edx,%eax
  12:   c3                      ret
  13:   66 90                   xchg   %ax,%ax
  15:   66 66 2e 0f 1f 84 00    data16 cs nopw 0x0(%rax,%rax,1)
  1c:   00 00 00 00 

0000000000000020 <cmp2>:
  20:   8b 06                   mov    (%rsi),%eax
  22:   39 07                   cmp    %eax,(%rdi)
  24:   ba ff ff ff ff          mov    $0xffffffff,%edx
  29:   0f 9f c0                setg   %al
  2c:   0f b6 c0                movzbl %al,%eax
  2f:   0f 4c c2                cmovl  %edx,%eax
  32:   c3                      ret

It looks like my version is a bit better with gcc.

I complied with clang and it did this:

0000000000000000 <cmp1>:
   0:   8b 07                   mov    (%rdi),%eax
   2:   3b 06                   cmp    (%rsi),%eax
   4:   0f 9c c0                setl   %al
   7:   0f 9f c1                setg   %cl
   a:   28 c1                   sub    %al,%cl
   c:   0f be c1                movsbl %cl,%eax
   f:   c3                      ret

0000000000000010 <cmp2>:
  10:   8b 07                   mov    (%rdi),%eax
  12:   3b 06                   cmp    (%rsi),%eax
  14:   0f 9c c0                setl   %al
  17:   0f 9f c1                setg   %cl
  1a:   28 c1                   sub    %al,%cl
  1c:   0f be c1                movsbl %cl,%eax
  1f:   c3                      ret

Which made the two identical!

Probably just a nit, but food for thought if you want to keep your method
or switch to mine ;-) I won't be picky about it.


> +
> +static struct ftrace_mod_syms *ftrace_mod_syms_alloc(struct module *mod)
> +{
> +	struct mod_kallsyms *kallsyms;
> +	struct ftrace_mod_syms *syms;
> +	unsigned int i, nr = 0;
> +
> +	/* mod->kallsyms is not replaced until do_init_module() returns. */
> +	rcu_read_lock();
> +	kallsyms = rcu_dereference(mod->kallsyms);
> +	rcu_read_unlock();

The above was already discussed.

> +
> +	if (!kallsyms || kallsyms->num_symtab < 2)
> +		return NULL;
> +
> +	syms = kvmalloc(struct_size(syms, addr, kallsyms->num_symtab),
> +			GFP_KERNEL);
> +	if (!syms)
> +		return NULL;
> +
> +	/* The symbols find_kallsyms_symbol() would consider. */
> +	for (i = 1; i < kallsyms->num_symtab; i++) {
> +		const Elf_Sym *sym = &kallsyms->symtab[i];
> +		const char *name = kallsyms->strtab + sym->st_name;
> +
> +		if (sym->st_shndx == SHN_UNDEF || !*name ||
> +		    is_mapping_symbol(name))
> +			continue;
> +
> +		syms->addr[nr++] = kallsyms_symbol_value(sym);
> +	}
> +	syms->nr = nr;
> +
> +	if (nr > 1)
> +		sort(syms->addr, nr, sizeof(syms->addr[0]), ftrace_mod_syms_cmp,
> +		     NULL);
> +
> +	return syms;
> +}
> +
> +/* The verdict test_for_valid_rec() would give, without walking the table. */
> +static bool ftrace_mod_rec_is_valid(struct module *mod,
> +				    const struct ftrace_mod_syms *syms,
> +				    struct dyn_ftrace *rec)
> +{
> +	unsigned long base, best;
> +	unsigned int lo, hi;
> +
> +	if (!syms)
> +		return test_for_valid_rec(rec) != 0;
> +
> +	/* A record is in executable memory; anything else kallsyms answers. */
> +	if (within_module_mem_type(rec->ip, mod, MOD_TEXT))
> +		base = (unsigned long)mod->mem[MOD_TEXT].base;
> +	else if (within_module_mem_type(rec->ip, mod, MOD_INIT_TEXT))
> +		base = (unsigned long)mod->mem[MOD_INIT_TEXT].base;
> +	else
> +		return test_for_valid_rec(rec) != 0;
> +
> +	/* The closest address at or below @rec->ip, if there is one. */
> +	best = 0;
> +	for (lo = 0, hi = syms->nr; lo < hi; ) {
> +		unsigned int mid = lo + (hi - lo) / 2;
> +
> +		if (syms->addr[mid] <= rec->ip)
> +			lo = mid + 1;
> +		else
> +			hi = mid;
> +	}
> +	if (lo)
> +		best = syms->addr[lo - 1];
> +
> +	/*
> +	 * No symbol, or only ones below @base: let kallsyms_lookup() answer,
> +	 * as it also looks in the BPF and ftrace trampoline tables.
> +	 */
> +	if (best < base)
> +		return test_for_valid_rec(rec) != 0;
> +
> +	return rec->ip - best <= FTRACE_MCOUNT_MAX_OFFSET;
> +}
> +#else
> +struct ftrace_mod_syms;
> +
> +static inline struct ftrace_mod_syms *ftrace_mod_syms_alloc(struct module *mod)
> +{
> +	return NULL;
> +}
> +
> +static inline bool ftrace_mod_rec_is_valid(struct module *mod,
> +					   const struct ftrace_mod_syms *syms,
> +					   struct dyn_ftrace *rec)
> +{
> +	return test_for_valid_rec(rec) != 0;
> +}
> +#endif /* FTRACE_MCOUNT_MAX_OFFSET */
> +
>  void ftrace_module_enable(struct module *mod)
>  {
> +	struct ftrace_mod_syms *syms;

Hmm, remove this...


>  	struct dyn_ftrace *rec;
>  	struct ftrace_page *pg;
>  
> +	/*
> +	 * Allocates, so build it before taking ftrace_lock.  It is skipped when
> +	 * ftrace never came up, where the loop below would not run anyway; that
> +	 * also makes reading ftrace_disabled without the lock safe.
> +	 */
> +	syms = ftrace_disabled ? NULL : ftrace_mod_syms_alloc(mod);

and make the above:

	struct ftrace_mod_syms *syms __free(kvfree) =
		ftrace_disabled ? NULL : ftrace_mod_syms_alloc(mod);


> +
>  	mutex_lock(&ftrace_lock);
>  
>  	if (ftrace_disabled)
> @@ -8050,7 +8177,7 @@ void ftrace_module_enable(struct module *mod)
>  		cond_resched();
>  
>  		/* Weak functions should still be ignored */
> -		if (!test_for_valid_rec(rec)) {
> +		if (!ftrace_mod_rec_is_valid(mod, syms, rec)) {
>  			/* Clear all other flags. Should not be enabled anyway */
>  			rec->flags = FTRACE_FL_DISABLED;
>  			continue;
> @@ -8087,6 +8214,8 @@ void ftrace_module_enable(struct module *mod)
>   out_unlock:
>  	mutex_unlock(&ftrace_lock);
>  
> +	kvfree(syms);

Then we can also remove that.

Thanks,

-- Steve

> +
>  	process_cached_mods(mod->name);
>  }
>  


      parent reply	other threads:[~2026-11-04 18:43 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-16  8:27 Yaxiong Tian
2026-10-16 18:07 ` Steven Rostedt
2026-10-19  1:06   ` Yaxiong Tian
2026-10-19 15:25 ` Masami Hiramatsu
2026-10-20  2:08   ` Yaxiong Tian
2026-11-04 18:43 ` Steven Rostedt [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261104134310.5cec1ebe@gandalf.local.home \
    --to=rostedt@goodmis.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=mathieu.desnoyers@efficios.com \
    --cc=mhiramat@kernel.org \
    --cc=tianyaxiong@kylinos.cn \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®