diff options
Diffstat (limited to 'kernel/trace')
| -rw-r--r-- | kernel/trace/fgraph.c | 1 | ||||
| -rw-r--r-- | kernel/trace/fprobe.c | 21 | ||||
| -rw-r--r-- | kernel/trace/ftrace.c | 45 | ||||
| -rw-r--r-- | kernel/trace/ftrace_internal.h | 5 | ||||
| -rw-r--r-- | kernel/trace/rethook.c | 13 | ||||
| -rw-r--r-- | kernel/trace/ring_buffer.c | 24 | ||||
| -rw-r--r-- | kernel/trace/trace.c | 22 | ||||
| -rw-r--r-- | kernel/trace/trace.h | 2 | ||||
| -rw-r--r-- | kernel/trace/trace_eprobe.c | 18 | ||||
| -rw-r--r-- | kernel/trace/trace_events_hist.c | 8 | ||||
| -rw-r--r-- | kernel/trace/trace_events_user.c | 3 | ||||
| -rw-r--r-- | kernel/trace/trace_kprobe_selftest.c | 3 | ||||
| -rw-r--r-- | kernel/trace/trace_probe.c | 2 | ||||
| -rw-r--r-- | kernel/trace/trace_probe_kernel.h | 30 | ||||
| -rw-r--r-- | kernel/trace/trace_probe_tmpl.h | 10 | ||||
| -rw-r--r-- | kernel/trace/trace_uprobe.c | 3 | 
16 files changed, 141 insertions, 69 deletions
| diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c index cd2c35b1dd8f..c83c005e654e 100644 --- a/kernel/trace/fgraph.c +++ b/kernel/trace/fgraph.c @@ -15,6 +15,7 @@  #include <trace/events/sched.h>  #include "ftrace_internal.h" +#include "trace.h"  #ifdef CONFIG_DYNAMIC_FTRACE  #define ASSIGN_OPS_HASH(opsname, val) \ diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index e4704ec26df7..3b21f4063258 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -100,14 +100,22 @@ static void fprobe_kprobe_handler(unsigned long ip, unsigned long parent_ip,  		return;  	} +	/* +	 * This user handler is shared with other kprobes and is not expected to be +	 * called recursively. So if any other kprobe handler is running, this will +	 * exit as kprobe does. See the section 'Share the callbacks with kprobes' +	 * in Documentation/trace/fprobe.rst for more information. +	 */  	if (unlikely(kprobe_running())) {  		fp->nmissed++; -		return; +		goto recursion_unlock;  	}  	kprobe_busy_begin();  	__fprobe_handler(ip, parent_ip, ops, fregs);  	kprobe_busy_end(); + +recursion_unlock:  	ftrace_test_recursion_unlock(bit);  } @@ -371,19 +379,16 @@ int unregister_fprobe(struct fprobe *fp)  	if (!fprobe_is_registered(fp))  		return -EINVAL; -	/* -	 * rethook_free() starts disabling the rethook, but the rethook handlers -	 * may be running on other processors at this point. To make sure that all -	 * current running handlers are finished, call unregister_ftrace_function() -	 * after this. -	 */  	if (fp->rethook) -		rethook_free(fp->rethook); +		rethook_stop(fp->rethook);  	ret = unregister_ftrace_function(&fp->ops);  	if (ret < 0)  		return ret; +	if (fp->rethook) +		rethook_free(fp->rethook); +  	ftrace_free_filter(&fp->ops);  	return ret; diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 3740aca79fe7..05c0024815bf 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -3305,6 +3305,22 @@ static int ftrace_allocate_records(struct ftrace_page *pg, int count)  	return cnt;  } +static void ftrace_free_pages(struct ftrace_page *pages) +{ +	struct ftrace_page *pg = pages; + +	while (pg) { +		if (pg->records) { +			free_pages((unsigned long)pg->records, pg->order); +			ftrace_number_of_pages -= 1 << pg->order; +		} +		pages = pg->next; +		kfree(pg); +		pg = pages; +		ftrace_number_of_groups--; +	} +} +  static struct ftrace_page *  ftrace_allocate_pages(unsigned long num_to_init)  { @@ -3343,17 +3359,7 @@ ftrace_allocate_pages(unsigned long num_to_init)  	return start_pg;   free_pages: -	pg = start_pg; -	while (pg) { -		if (pg->records) { -			free_pages((unsigned long)pg->records, pg->order); -			ftrace_number_of_pages -= 1 << pg->order; -		} -		start_pg = pg->next; -		kfree(pg); -		pg = start_pg; -		ftrace_number_of_groups--; -	} +	ftrace_free_pages(start_pg);  	pr_info("ftrace: FAILED to allocate memory for functions\n");  	return NULL;  } @@ -6471,9 +6477,11 @@ static int ftrace_process_locs(struct module *mod,  			       unsigned long *start,  			       unsigned long *end)  { +	struct ftrace_page *pg_unuse = NULL;  	struct ftrace_page *start_pg;  	struct ftrace_page *pg;  	struct dyn_ftrace *rec; +	unsigned long skipped = 0;  	unsigned long count;  	unsigned long *p;  	unsigned long addr; @@ -6536,8 +6544,10 @@ static int ftrace_process_locs(struct module *mod,  		 * object files to satisfy alignments.  		 * Skip any NULL pointers.  		 */ -		if (!addr) +		if (!addr) { +			skipped++;  			continue; +		}  		end_offset = (pg->index+1) * sizeof(pg->records[0]);  		if (end_offset > PAGE_SIZE << pg->order) { @@ -6551,8 +6561,10 @@ static int ftrace_process_locs(struct module *mod,  		rec->ip = addr;  	} -	/* We should have used all pages */ -	WARN_ON(pg->next); +	if (pg->next) { +		pg_unuse = pg->next; +		pg->next = NULL; +	}  	/* Assign the last page to ftrace_pages */  	ftrace_pages = pg; @@ -6574,6 +6586,11 @@ static int ftrace_process_locs(struct module *mod,   out:  	mutex_unlock(&ftrace_lock); +	/* We should have used all pages unless we skipped some */ +	if (pg_unuse) { +		WARN_ON(!skipped); +		ftrace_free_pages(pg_unuse); +	}  	return ret;  } diff --git a/kernel/trace/ftrace_internal.h b/kernel/trace/ftrace_internal.h index 382775edf690..5012c04f92c0 100644 --- a/kernel/trace/ftrace_internal.h +++ b/kernel/trace/ftrace_internal.h @@ -2,6 +2,9 @@  #ifndef _LINUX_KERNEL_FTRACE_INTERNAL_H  #define  _LINUX_KERNEL_FTRACE_INTERNAL_H +int __register_ftrace_function(struct ftrace_ops *ops); +int __unregister_ftrace_function(struct ftrace_ops *ops); +  #ifdef CONFIG_FUNCTION_TRACER  extern struct mutex ftrace_lock; @@ -15,8 +18,6 @@ int ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs);  #else /* !CONFIG_DYNAMIC_FTRACE */ -int __register_ftrace_function(struct ftrace_ops *ops); -int __unregister_ftrace_function(struct ftrace_ops *ops);  /* Keep as macros so we do not need to define the commands */  # define ftrace_startup(ops, command)					\  	({								\ diff --git a/kernel/trace/rethook.c b/kernel/trace/rethook.c index f32ee484391a..5eb9b598f4e9 100644 --- a/kernel/trace/rethook.c +++ b/kernel/trace/rethook.c @@ -54,6 +54,19 @@ static void rethook_free_rcu(struct rcu_head *head)  }  /** + * rethook_stop() - Stop using a rethook. + * @rh: the struct rethook to stop. + * + * Stop using a rethook to prepare for freeing it. If you want to wait for + * all running rethook handler before calling rethook_free(), you need to + * call this first and wait RCU, and call rethook_free(). + */ +void rethook_stop(struct rethook *rh) +{ +	WRITE_ONCE(rh->handler, NULL); +} + +/**   * rethook_free() - Free struct rethook.   * @rh: the struct rethook to be freed.   * diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 834b361a4a66..14d8001140c8 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -5242,28 +5242,34 @@ unsigned long ring_buffer_size(struct trace_buffer *buffer, int cpu)  }  EXPORT_SYMBOL_GPL(ring_buffer_size); +static void rb_clear_buffer_page(struct buffer_page *page) +{ +	local_set(&page->write, 0); +	local_set(&page->entries, 0); +	rb_init_page(page->page); +	page->read = 0; +} +  static void  rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer)  { +	struct buffer_page *page; +  	rb_head_page_deactivate(cpu_buffer);  	cpu_buffer->head_page  		= list_entry(cpu_buffer->pages, struct buffer_page, list); -	local_set(&cpu_buffer->head_page->write, 0); -	local_set(&cpu_buffer->head_page->entries, 0); -	local_set(&cpu_buffer->head_page->page->commit, 0); - -	cpu_buffer->head_page->read = 0; +	rb_clear_buffer_page(cpu_buffer->head_page); +	list_for_each_entry(page, cpu_buffer->pages, list) { +		rb_clear_buffer_page(page); +	}  	cpu_buffer->tail_page = cpu_buffer->head_page;  	cpu_buffer->commit_page = cpu_buffer->head_page;  	INIT_LIST_HEAD(&cpu_buffer->reader_page->list);  	INIT_LIST_HEAD(&cpu_buffer->new_pages); -	local_set(&cpu_buffer->reader_page->write, 0); -	local_set(&cpu_buffer->reader_page->entries, 0); -	local_set(&cpu_buffer->reader_page->page->commit, 0); -	cpu_buffer->reader_page->read = 0; +	rb_clear_buffer_page(cpu_buffer->reader_page);  	local_set(&cpu_buffer->entries_bytes, 0);  	local_set(&cpu_buffer->overrun, 0); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 4529e264cb86..be847d45d81c 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -3118,6 +3118,7 @@ static void __ftrace_trace_stack(struct trace_buffer *buffer,  	struct ftrace_stack *fstack;  	struct stack_entry *entry;  	int stackidx; +	void *ptr;  	/*  	 * Add one, for this function and the call to save_stack_trace() @@ -3161,9 +3162,25 @@ static void __ftrace_trace_stack(struct trace_buffer *buffer,  				    trace_ctx);  	if (!event)  		goto out; -	entry = ring_buffer_event_data(event); +	ptr = ring_buffer_event_data(event); +	entry = ptr; + +	/* +	 * For backward compatibility reasons, the entry->caller is an +	 * array of 8 slots to store the stack. This is also exported +	 * to user space. The amount allocated on the ring buffer actually +	 * holds enough for the stack specified by nr_entries. This will +	 * go into the location of entry->caller. Due to string fortifiers +	 * checking the size of the destination of memcpy() it triggers +	 * when it detects that size is greater than 8. To hide this from +	 * the fortifiers, we use "ptr" and pointer arithmetic to assign caller. +	 * +	 * The below is really just: +	 *   memcpy(&entry->caller, fstack->calls, size); +	 */ +	ptr += offsetof(typeof(*entry), caller); +	memcpy(ptr, fstack->calls, size); -	memcpy(&entry->caller, fstack->calls, size);  	entry->size = nr_entries;  	if (!call_filter_check_discard(call, entry, buffer, event)) @@ -6764,6 +6781,7 @@ static int tracing_release_pipe(struct inode *inode, struct file *file)  	free_cpumask_var(iter->started);  	kfree(iter->fmt); +	kfree(iter->temp);  	mutex_destroy(&iter->mutex);  	kfree(iter); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index ed7906b13f09..e1edc2197fc8 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -113,6 +113,8 @@ enum trace_type {  #define MEM_FAIL(condition, fmt, ...)					\  	DO_ONCE_LITE_IF(condition, pr_err, "ERROR: " fmt, ##__VA_ARGS__) +#define FAULT_STRING "(fault)" +  #define HIST_STACKTRACE_DEPTH	16  #define HIST_STACKTRACE_SIZE	(HIST_STACKTRACE_DEPTH * sizeof(unsigned long))  #define HIST_STACKTRACE_SKIP	5 diff --git a/kernel/trace/trace_eprobe.c b/kernel/trace/trace_eprobe.c index cb0077ba2b49..a0a704ba27db 100644 --- a/kernel/trace/trace_eprobe.c +++ b/kernel/trace/trace_eprobe.c @@ -644,6 +644,7 @@ static int enable_trace_eprobe(struct trace_event_call *call,  	struct trace_eprobe *ep;  	bool enabled;  	int ret = 0; +	int cnt = 0;  	tp = trace_probe_primary_from_call(call);  	if (WARN_ON_ONCE(!tp)) @@ -667,12 +668,25 @@ static int enable_trace_eprobe(struct trace_event_call *call,  		if (ret)  			break;  		enabled = true; +		cnt++;  	}  	if (ret) {  		/* Failed to enable one of them. Roll back all */ -		if (enabled) -			disable_eprobe(ep, file->tr); +		if (enabled) { +			/* +			 * It's a bug if one failed for something other than memory +			 * not being available but another eprobe succeeded. +			 */ +			WARN_ON_ONCE(ret != -ENOMEM); + +			list_for_each_entry(pos, trace_probe_probe_list(tp), list) { +				ep = container_of(pos, struct trace_eprobe, tp); +				disable_eprobe(ep, file->tr); +				if (!--cnt) +					break; +			} +		}  		if (file)  			trace_probe_remove_file(tp, file);  		else diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index b97d3ad832f1..c8c61381eba4 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -6663,13 +6663,15 @@ static int event_hist_trigger_parse(struct event_command *cmd_ops,  	if (get_named_trigger_data(trigger_data))  		goto enable; -	if (has_hist_vars(hist_data)) -		save_hist_vars(hist_data); -  	ret = create_actions(hist_data);  	if (ret)  		goto out_unreg; +	if (has_hist_vars(hist_data) || hist_data->n_var_refs) { +		if (save_hist_vars(hist_data)) +			goto out_unreg; +	} +  	ret = tracing_map_init(hist_data->map);  	if (ret)  		goto out_unreg; diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c index 4f5e74bbdab2..33cb6af31f39 100644 --- a/kernel/trace/trace_events_user.c +++ b/kernel/trace/trace_events_user.c @@ -1317,6 +1317,9 @@ static int user_field_set_string(struct ftrace_event_field *field,  	pos += snprintf(buf + pos, LEN_OR_ZERO, " ");  	pos += snprintf(buf + pos, LEN_OR_ZERO, "%s", field->name); +	if (str_has_prefix(field->type, "struct ")) +		pos += snprintf(buf + pos, LEN_OR_ZERO, " %d", field->size); +  	if (colon)  		pos += snprintf(buf + pos, LEN_OR_ZERO, ";"); diff --git a/kernel/trace/trace_kprobe_selftest.c b/kernel/trace/trace_kprobe_selftest.c index 16548ee4c8c6..3851cd1e6a62 100644 --- a/kernel/trace/trace_kprobe_selftest.c +++ b/kernel/trace/trace_kprobe_selftest.c @@ -1,4 +1,7 @@  // SPDX-License-Identifier: GPL-2.0 + +#include "trace_kprobe_selftest.h" +  /*   * Function used during the kprobe self test. This function is in a separate   * compile unit so it can be compile with CC_FLAGS_FTRACE to ensure that it diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index 7ba371da0926..b2b726bea1f9 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -67,7 +67,7 @@ int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s, void *data, void *ent)  	int len = *(u32 *)data >> 16;  	if (!len) -		trace_seq_puts(s, "(fault)"); +		trace_seq_puts(s, FAULT_STRING);  	else  		trace_seq_printf(s, "\"%s\"",  				 (const char *)get_loc_data(data, ent)); diff --git a/kernel/trace/trace_probe_kernel.h b/kernel/trace/trace_probe_kernel.h index c4e1d4c03a85..bb723eefd7b7 100644 --- a/kernel/trace/trace_probe_kernel.h +++ b/kernel/trace/trace_probe_kernel.h @@ -2,8 +2,6 @@  #ifndef __TRACE_PROBE_KERNEL_H_  #define __TRACE_PROBE_KERNEL_H_ -#define FAULT_STRING "(fault)" -  /*   * This depends on trace_probe.h, but can not include it due to   * the way trace_probe_tmpl.h is used by trace_kprobe.c and trace_eprobe.c. @@ -15,16 +13,8 @@ static nokprobe_inline int  fetch_store_strlen_user(unsigned long addr)  {  	const void __user *uaddr =  (__force const void __user *)addr; -	int ret; -	ret = strnlen_user_nofault(uaddr, MAX_STRING_SIZE); -	/* -	 * strnlen_user_nofault returns zero on fault, insert the -	 * FAULT_STRING when that occurs. -	 */ -	if (ret <= 0) -		return strlen(FAULT_STRING) + 1; -	return ret; +	return strnlen_user_nofault(uaddr, MAX_STRING_SIZE);  }  /* Return the length of string -- including null terminal byte */ @@ -44,18 +34,14 @@ fetch_store_strlen(unsigned long addr)  		len++;  	} while (c && ret == 0 && len < MAX_STRING_SIZE); -	/* For faults, return enough to hold the FAULT_STRING */ -	return (ret < 0) ? strlen(FAULT_STRING) + 1 : len; +	return (ret < 0) ? ret : len;  } -static nokprobe_inline void set_data_loc(int ret, void *dest, void *__dest, void *base, int len) +static nokprobe_inline void set_data_loc(int ret, void *dest, void *__dest, void *base)  { -	if (ret >= 0) { -		*(u32 *)dest = make_data_loc(ret, __dest - base); -	} else { -		strscpy(__dest, FAULT_STRING, len); -		ret = strlen(__dest) + 1; -	} +	if (ret < 0) +		ret = 0; +	*(u32 *)dest = make_data_loc(ret, __dest - base);  }  /* @@ -76,7 +62,7 @@ fetch_store_string_user(unsigned long addr, void *dest, void *base)  	__dest = get_loc_data(dest, base);  	ret = strncpy_from_user_nofault(__dest, uaddr, maxlen); -	set_data_loc(ret, dest, __dest, base, maxlen); +	set_data_loc(ret, dest, __dest, base);  	return ret;  } @@ -107,7 +93,7 @@ fetch_store_string(unsigned long addr, void *dest, void *base)  	 * probing.  	 */  	ret = strncpy_from_kernel_nofault(__dest, (void *)addr, maxlen); -	set_data_loc(ret, dest, __dest, base, maxlen); +	set_data_loc(ret, dest, __dest, base);  	return ret;  } diff --git a/kernel/trace/trace_probe_tmpl.h b/kernel/trace/trace_probe_tmpl.h index 00707630788d..3935b347f874 100644 --- a/kernel/trace/trace_probe_tmpl.h +++ b/kernel/trace/trace_probe_tmpl.h @@ -156,11 +156,11 @@ stage3:  			code++;  			goto array;  		case FETCH_OP_ST_USTRING: -			ret += fetch_store_strlen_user(val + code->offset); +			ret = fetch_store_strlen_user(val + code->offset);  			code++;  			goto array;  		case FETCH_OP_ST_SYMSTR: -			ret += fetch_store_symstrlen(val + code->offset); +			ret = fetch_store_symstrlen(val + code->offset);  			code++;  			goto array;  		default: @@ -204,6 +204,8 @@ stage3:  array:  	/* the last stage: Loop on array */  	if (code->op == FETCH_OP_LP_ARRAY) { +		if (ret < 0) +			ret = 0;  		total += ret;  		if (++i < code->param) {  			code = s3; @@ -265,9 +267,7 @@ store_trace_args(void *data, struct trace_probe *tp, void *rec,  		if (unlikely(arg->dynamic))  			*dl = make_data_loc(maxlen, dyndata - base);  		ret = process_fetch_insn(arg->code, rec, dl, base); -		if (unlikely(ret < 0 && arg->dynamic)) { -			*dl = make_data_loc(0, dyndata - base); -		} else { +		if (arg->dynamic && likely(ret > 0)) {  			dyndata += ret;  			maxlen -= ret;  		} diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index fa09b33ee731..688bf579f2f1 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -170,7 +170,8 @@ fetch_store_string(unsigned long addr, void *dest, void *base)  			 */  			ret++;  		*(u32 *)dest = make_data_loc(ret, (void *)dst - base); -	} +	} else +		*(u32 *)dest = make_data_loc(0, (void *)dst - base);  	return ret;  } | 
