Commit ca8a6673 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'trace-v6.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull tracing fixes from Steven Rostedt:

 - Fix broken direct trampolines being called when another callback is
   attached the same function.

   ARM 64 does not support FTRACE_WITH_REGS, and when it added direct
   trampoline calls from ftrace, it removed the "WITH_REGS" flag from
   the ftrace_ops for direct trampolines. This broke x86 as x86 requires
   direct trampolines to have WITH_REGS.

   This wasn't noticed because direct trampolines work as long as the
   function it is attached to is not shared with other callbacks (like
   the function tracer). When there are other callbacks, a helper
   trampoline is called, to call all the non direct callbacks and when
   it returns, the direct trampoline is called.

   For x86, the direct trampoline sets a flag in the regs field to tell
   the x86 specific code to call the direct trampoline. But this only
   works if the ftrace_ops had WITH_REGS set. ARM does things
   differently that does not require this. For now, set WITH_REGS if the
   arch supports WITH_REGS (which ARM does not), and this makes it work
   for both ARM64 and x86.

 - Fix wasted memory in the saved_cmdlines logic.

   The saved_cmdlines is a cache that maps PIDs to COMMs that tracing
   can use. Most trace events only save the PID in the event. The
   saved_cmdlines file lists PIDs to COMMs so that the tracing tools can
   show an actual name and not just a PID for each event. There's an
   array of PIDs that map to a small set of saved COMM strings. The
   array is set to PID_MAX_DEFAULT which is usually set to 32768. When a
   PID comes in, it will add itself to this array along with the index
   into the COMM array (note if the system allows more than
   PID_MAX_DEFAULT, this cache is similar to cache lines as an update of
   a PID that has the same PID_MAX_DEFAULT bits set will flush out
   another task with the same matching bits set).

   A while ago, the size of this cache was changed to be dynamic and the
   array was moved into a structure and created with kmalloc(). But this
   new structure had the size of 131104 bytes, or 0x20020 in hex. As
   kmalloc allocates in powers of two, it was actually allocating
   0x40000 bytes (262144) leaving 131040 bytes of wasted memory. The
   last element of this structure was a pointer to the COMM string array
   which defaulted to just saving 128 COMMs.

   By changing the last field of this structure to a variable length
   string, and just having it round up to fill the allocated memory, the
   default size of the saved COMM cache is now 8190. This not only uses
   the wasted space, but actually saves space by removing the extra
   allocation for the COMM names.

* tag 'trace-v6.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  tracing: Fix wasted memory in saved_cmdlines logic
  ftrace: Fix DIRECT_CALLS to use SAVE_REGS by default
parents 6dc512a0 44dc5c41
...@@ -5325,7 +5325,17 @@ static LIST_HEAD(ftrace_direct_funcs); ...@@ -5325,7 +5325,17 @@ static LIST_HEAD(ftrace_direct_funcs);
static int register_ftrace_function_nolock(struct ftrace_ops *ops); static int register_ftrace_function_nolock(struct ftrace_ops *ops);
/*
* If there are multiple ftrace_ops, use SAVE_REGS by default, so that direct
* call will be jumped from ftrace_regs_caller. Only if the architecture does
* not support ftrace_regs_caller but direct_call, use SAVE_ARGS so that it
* jumps from ftrace_caller for multiple ftrace_ops.
*/
#ifndef HAVE_DYNAMIC_FTRACE_WITH_REGS
#define MULTI_FLAGS (FTRACE_OPS_FL_DIRECT | FTRACE_OPS_FL_SAVE_ARGS) #define MULTI_FLAGS (FTRACE_OPS_FL_DIRECT | FTRACE_OPS_FL_SAVE_ARGS)
#else
#define MULTI_FLAGS (FTRACE_OPS_FL_DIRECT | FTRACE_OPS_FL_SAVE_REGS)
#endif
static int check_direct_multi(struct ftrace_ops *ops) static int check_direct_multi(struct ftrace_ops *ops)
{ {
......
...@@ -2320,7 +2320,7 @@ struct saved_cmdlines_buffer { ...@@ -2320,7 +2320,7 @@ struct saved_cmdlines_buffer {
unsigned *map_cmdline_to_pid; unsigned *map_cmdline_to_pid;
unsigned cmdline_num; unsigned cmdline_num;
int cmdline_idx; int cmdline_idx;
char *saved_cmdlines; char saved_cmdlines[];
}; };
static struct saved_cmdlines_buffer *savedcmd; static struct saved_cmdlines_buffer *savedcmd;
...@@ -2334,47 +2334,58 @@ static inline void set_cmdline(int idx, const char *cmdline) ...@@ -2334,47 +2334,58 @@ static inline void set_cmdline(int idx, const char *cmdline)
strncpy(get_saved_cmdlines(idx), cmdline, TASK_COMM_LEN); strncpy(get_saved_cmdlines(idx), cmdline, TASK_COMM_LEN);
} }
static int allocate_cmdlines_buffer(unsigned int val, static void free_saved_cmdlines_buffer(struct saved_cmdlines_buffer *s)
struct saved_cmdlines_buffer *s) {
int order = get_order(sizeof(*s) + s->cmdline_num * TASK_COMM_LEN);
kfree(s->map_cmdline_to_pid);
free_pages((unsigned long)s, order);
}
static struct saved_cmdlines_buffer *allocate_cmdlines_buffer(unsigned int val)
{ {
struct saved_cmdlines_buffer *s;
struct page *page;
int orig_size, size;
int order;
/* Figure out how much is needed to hold the given number of cmdlines */
orig_size = sizeof(*s) + val * TASK_COMM_LEN;
order = get_order(orig_size);
size = 1 << (order + PAGE_SHIFT);
page = alloc_pages(GFP_KERNEL, order);
if (!page)
return NULL;
s = page_address(page);
memset(s, 0, sizeof(*s));
/* Round up to actual allocation */
val = (size - sizeof(*s)) / TASK_COMM_LEN;
s->cmdline_num = val;
s->map_cmdline_to_pid = kmalloc_array(val, s->map_cmdline_to_pid = kmalloc_array(val,
sizeof(*s->map_cmdline_to_pid), sizeof(*s->map_cmdline_to_pid),
GFP_KERNEL); GFP_KERNEL);
if (!s->map_cmdline_to_pid) if (!s->map_cmdline_to_pid) {
return -ENOMEM; free_saved_cmdlines_buffer(s);
return NULL;
s->saved_cmdlines = kmalloc_array(TASK_COMM_LEN, val, GFP_KERNEL);
if (!s->saved_cmdlines) {
kfree(s->map_cmdline_to_pid);
return -ENOMEM;
} }
s->cmdline_idx = 0; s->cmdline_idx = 0;
s->cmdline_num = val;
memset(&s->map_pid_to_cmdline, NO_CMDLINE_MAP, memset(&s->map_pid_to_cmdline, NO_CMDLINE_MAP,
sizeof(s->map_pid_to_cmdline)); sizeof(s->map_pid_to_cmdline));
memset(s->map_cmdline_to_pid, NO_CMDLINE_MAP, memset(s->map_cmdline_to_pid, NO_CMDLINE_MAP,
val * sizeof(*s->map_cmdline_to_pid)); val * sizeof(*s->map_cmdline_to_pid));
return 0; return s;
} }
static int trace_create_savedcmd(void) static int trace_create_savedcmd(void)
{ {
int ret; savedcmd = allocate_cmdlines_buffer(SAVED_CMDLINES_DEFAULT);
savedcmd = kmalloc(sizeof(*savedcmd), GFP_KERNEL);
if (!savedcmd)
return -ENOMEM;
ret = allocate_cmdlines_buffer(SAVED_CMDLINES_DEFAULT, savedcmd);
if (ret < 0) {
kfree(savedcmd);
savedcmd = NULL;
return -ENOMEM;
}
return 0; return savedcmd ? 0 : -ENOMEM;
} }
int is_tracing_stopped(void) int is_tracing_stopped(void)
...@@ -6056,26 +6067,14 @@ tracing_saved_cmdlines_size_read(struct file *filp, char __user *ubuf, ...@@ -6056,26 +6067,14 @@ tracing_saved_cmdlines_size_read(struct file *filp, char __user *ubuf,
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
} }
static void free_saved_cmdlines_buffer(struct saved_cmdlines_buffer *s)
{
kfree(s->saved_cmdlines);
kfree(s->map_cmdline_to_pid);
kfree(s);
}
static int tracing_resize_saved_cmdlines(unsigned int val) static int tracing_resize_saved_cmdlines(unsigned int val)
{ {
struct saved_cmdlines_buffer *s, *savedcmd_temp; struct saved_cmdlines_buffer *s, *savedcmd_temp;
s = kmalloc(sizeof(*s), GFP_KERNEL); s = allocate_cmdlines_buffer(val);
if (!s) if (!s)
return -ENOMEM; return -ENOMEM;
if (allocate_cmdlines_buffer(val, s) < 0) {
kfree(s);
return -ENOMEM;
}
preempt_disable(); preempt_disable();
arch_spin_lock(&trace_cmdline_lock); arch_spin_lock(&trace_cmdline_lock);
savedcmd_temp = savedcmd; savedcmd_temp = savedcmd;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment