diff options
Diffstat (limited to 'kernel/trace/ftrace.c')
| -rw-r--r-- | kernel/trace/ftrace.c | 836 | 
1 files changed, 492 insertions, 344 deletions
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 03cf44ac54d..ac9d1dad630 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -62,7 +62,7 @@  #define FTRACE_HASH_DEFAULT_BITS 10  #define FTRACE_HASH_MAX_BITS 12 -#define FL_GLOBAL_CONTROL_MASK (FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_CONTROL) +#define FL_GLOBAL_CONTROL_MASK (FTRACE_OPS_FL_CONTROL)  #ifdef CONFIG_DYNAMIC_FTRACE  #define INIT_REGEX_LOCK(opsname)	\ @@ -85,6 +85,8 @@ int function_trace_stop __read_mostly;  /* Current function tracing op */  struct ftrace_ops *function_trace_op __read_mostly = &ftrace_list_end; +/* What to set function_trace_op to */ +static struct ftrace_ops *set_function_trace_op;  /* List for set_ftrace_pid's pids. */  LIST_HEAD(ftrace_pids); @@ -101,7 +103,6 @@ static int ftrace_disabled __read_mostly;  static DEFINE_MUTEX(ftrace_lock); -static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end;  static struct ftrace_ops *ftrace_control_list __read_mostly = &ftrace_list_end;  static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end;  ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; @@ -169,23 +170,6 @@ int ftrace_nr_registered_ops(void)  	return cnt;  } -static void -ftrace_global_list_func(unsigned long ip, unsigned long parent_ip, -			struct ftrace_ops *op, struct pt_regs *regs) -{ -	int bit; - -	bit = trace_test_and_set_recursion(TRACE_GLOBAL_START, TRACE_GLOBAL_MAX); -	if (bit < 0) -		return; - -	do_for_each_ftrace_op(op, ftrace_global_list) { -		op->func(ip, parent_ip, op, regs); -	} while_for_each_ftrace_op(op); - -	trace_clear_recursion(bit); -} -  static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip,  			    struct ftrace_ops *op, struct pt_regs *regs)  { @@ -235,55 +219,33 @@ static int control_ops_alloc(struct ftrace_ops *ops)  	return 0;  } -static void control_ops_free(struct ftrace_ops *ops) -{ -	free_percpu(ops->disabled); -} - -static void update_global_ops(void) +static void ftrace_sync(struct work_struct *work)  { -	ftrace_func_t func; -  	/* -	 * If there's only one function registered, then call that -	 * function directly. Otherwise, we need to iterate over the -	 * registered callers. +	 * This function is just a stub to implement a hard force +	 * of synchronize_sched(). This requires synchronizing +	 * tasks even in userspace and idle. +	 * +	 * Yes, function tracing is rude.  	 */ -	if (ftrace_global_list == &ftrace_list_end || -	    ftrace_global_list->next == &ftrace_list_end) { -		func = ftrace_global_list->func; -		/* -		 * As we are calling the function directly. -		 * If it does not have recursion protection, -		 * the function_trace_op needs to be updated -		 * accordingly. -		 */ -		if (ftrace_global_list->flags & FTRACE_OPS_FL_RECURSION_SAFE) -			global_ops.flags |= FTRACE_OPS_FL_RECURSION_SAFE; -		else -			global_ops.flags &= ~FTRACE_OPS_FL_RECURSION_SAFE; -	} else { -		func = ftrace_global_list_func; -		/* The list has its own recursion protection. */ -		global_ops.flags |= FTRACE_OPS_FL_RECURSION_SAFE; -	} - - -	/* If we filter on pids, update to use the pid function */ -	if (!list_empty(&ftrace_pids)) { -		set_ftrace_pid_function(func); -		func = ftrace_pid_func; -	} +} -	global_ops.func = func; +static void ftrace_sync_ipi(void *data) +{ +	/* Probably not needed, but do it anyway */ +	smp_rmb();  } +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +static void update_function_graph_func(void); +#else +static inline void update_function_graph_func(void) { } +#endif +  static void update_ftrace_function(void)  {  	ftrace_func_t func; -	update_global_ops(); -  	/*  	 * If we are at the end of the list and this ops is  	 * recursion safe and not dynamic and the arch supports passing ops, @@ -295,20 +257,67 @@ static void update_ftrace_function(void)  	     (ftrace_ops_list->flags & FTRACE_OPS_FL_RECURSION_SAFE) &&  	     !FTRACE_FORCE_LIST_FUNC)) {  		/* Set the ftrace_ops that the arch callback uses */ -		if (ftrace_ops_list == &global_ops) -			function_trace_op = ftrace_global_list; -		else -			function_trace_op = ftrace_ops_list; +		set_function_trace_op = ftrace_ops_list;  		func = ftrace_ops_list->func;  	} else {  		/* Just use the default ftrace_ops */ -		function_trace_op = &ftrace_list_end; +		set_function_trace_op = &ftrace_list_end;  		func = ftrace_ops_list_func;  	} +	update_function_graph_func(); + +	/* If there's no change, then do nothing more here */ +	if (ftrace_trace_function == func) +		return; + +	/* +	 * If we are using the list function, it doesn't care +	 * about the function_trace_ops. +	 */ +	if (func == ftrace_ops_list_func) { +		ftrace_trace_function = func; +		/* +		 * Don't even bother setting function_trace_ops, +		 * it would be racy to do so anyway. +		 */ +		return; +	} + +#ifndef CONFIG_DYNAMIC_FTRACE +	/* +	 * For static tracing, we need to be a bit more careful. +	 * The function change takes affect immediately. Thus, +	 * we need to coorditate the setting of the function_trace_ops +	 * with the setting of the ftrace_trace_function. +	 * +	 * Set the function to the list ops, which will call the +	 * function we want, albeit indirectly, but it handles the +	 * ftrace_ops and doesn't depend on function_trace_op. +	 */ +	ftrace_trace_function = ftrace_ops_list_func; +	/* +	 * Make sure all CPUs see this. Yes this is slow, but static +	 * tracing is slow and nasty to have enabled. +	 */ +	schedule_on_each_cpu(ftrace_sync); +	/* Now all cpus are using the list ops. */ +	function_trace_op = set_function_trace_op; +	/* Make sure the function_trace_op is visible on all CPUs */ +	smp_wmb(); +	/* Nasty way to force a rmb on all cpus */ +	smp_call_function(ftrace_sync_ipi, NULL, 1); +	/* OK, we are all set to update the ftrace_trace_function now! */ +#endif /* !CONFIG_DYNAMIC_FTRACE */ +  	ftrace_trace_function = func;  } +int using_ftrace_ops_list_func(void) +{ +	return ftrace_trace_function == ftrace_ops_list_func; +} +  static void add_ftrace_ops(struct ftrace_ops **list, struct ftrace_ops *ops)  {  	ops->next = *list; @@ -367,19 +376,12 @@ static int remove_ftrace_list_ops(struct ftrace_ops **list,  static int __register_ftrace_function(struct ftrace_ops *ops)  { -	if (unlikely(ftrace_disabled)) -		return -ENODEV; - -	if (FTRACE_WARN_ON(ops == &global_ops)) +	if (ops->flags & FTRACE_OPS_FL_DELETED)  		return -EINVAL;  	if (WARN_ON(ops->flags & FTRACE_OPS_FL_ENABLED))  		return -EBUSY; -	/* We don't support both control and global flags set. */ -	if ((ops->flags & FL_GLOBAL_CONTROL_MASK) == FL_GLOBAL_CONTROL_MASK) -		return -EINVAL; -  #ifndef CONFIG_DYNAMIC_FTRACE_WITH_REGS  	/*  	 * If the ftrace_ops specifies SAVE_REGS, then it only can be used @@ -397,10 +399,7 @@ static int __register_ftrace_function(struct ftrace_ops *ops)  	if (!core_kernel_data((unsigned long)ops))  		ops->flags |= FTRACE_OPS_FL_DYNAMIC; -	if (ops->flags & FTRACE_OPS_FL_GLOBAL) { -		add_ftrace_list_ops(&ftrace_global_list, &global_ops, ops); -		ops->flags |= FTRACE_OPS_FL_ENABLED; -	} else if (ops->flags & FTRACE_OPS_FL_CONTROL) { +	if (ops->flags & FTRACE_OPS_FL_CONTROL) {  		if (control_ops_alloc(ops))  			return -ENOMEM;  		add_ftrace_list_ops(&ftrace_control_list, &control_ops, ops); @@ -413,52 +412,16 @@ static int __register_ftrace_function(struct ftrace_ops *ops)  	return 0;  } -static void ftrace_sync(struct work_struct *work) -{ -	/* -	 * This function is just a stub to implement a hard force -	 * of synchronize_sched(). This requires synchronizing -	 * tasks even in userspace and idle. -	 * -	 * Yes, function tracing is rude. -	 */ -} -  static int __unregister_ftrace_function(struct ftrace_ops *ops)  {  	int ret; -	if (ftrace_disabled) -		return -ENODEV; -  	if (WARN_ON(!(ops->flags & FTRACE_OPS_FL_ENABLED)))  		return -EBUSY; -	if (FTRACE_WARN_ON(ops == &global_ops)) -		return -EINVAL; - -	if (ops->flags & FTRACE_OPS_FL_GLOBAL) { -		ret = remove_ftrace_list_ops(&ftrace_global_list, -					     &global_ops, ops); -		if (!ret) -			ops->flags &= ~FTRACE_OPS_FL_ENABLED; -	} else if (ops->flags & FTRACE_OPS_FL_CONTROL) { +	if (ops->flags & FTRACE_OPS_FL_CONTROL) {  		ret = remove_ftrace_list_ops(&ftrace_control_list,  					     &control_ops, ops); -		if (!ret) { -			/* -			 * The ftrace_ops is now removed from the list, -			 * so there'll be no new users. We must ensure -			 * all current users are done before we free -			 * the control data. -			 * Note synchronize_sched() is not enough, as we -			 * use preempt_disable() to do RCU, but the function -			 * tracer can be called where RCU is not active -			 * (before user_exit()). -			 */ -			schedule_on_each_cpu(ftrace_sync); -			control_ops_free(ops); -		}  	} else  		ret = remove_ftrace_ops(&ftrace_ops_list, ops); @@ -468,17 +431,6 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)  	if (ftrace_enabled)  		update_ftrace_function(); -	/* -	 * Dynamic ops may be freed, we must make sure that all -	 * callers are done before leaving this function. -	 * -	 * Again, normal synchronize_sched() is not good enough. -	 * We need to do a hard force of sched synchronization. -	 */ -	if (ops->flags & FTRACE_OPS_FL_DYNAMIC) -		schedule_on_each_cpu(ftrace_sync); - -  	return 0;  } @@ -781,7 +733,7 @@ static int ftrace_profile_init(void)  	int cpu;  	int ret = 0; -	for_each_online_cpu(cpu) { +	for_each_possible_cpu(cpu) {  		ret = ftrace_profile_init_cpu(cpu);  		if (ret)  			break; @@ -870,7 +822,7 @@ function_profile_call(unsigned long ip, unsigned long parent_ip,  	local_irq_save(flags); -	stat = &__get_cpu_var(ftrace_profile_stats); +	stat = this_cpu_ptr(&ftrace_profile_stats);  	if (!stat->hash || !ftrace_profile_enabled)  		goto out; @@ -901,7 +853,7 @@ static void profile_graph_return(struct ftrace_graph_ret *trace)  	unsigned long flags;  	local_irq_save(flags); -	stat = &__get_cpu_var(ftrace_profile_stats); +	stat = this_cpu_ptr(&ftrace_profile_stats);  	if (!stat->hash || !ftrace_profile_enabled)  		goto out; @@ -1088,19 +1040,6 @@ static __init void ftrace_profile_debugfs(struct dentry *d_tracer)  static struct pid * const ftrace_swapper_pid = &init_struct_pid; -loff_t -ftrace_filter_lseek(struct file *file, loff_t offset, int whence) -{ -	loff_t ret; - -	if (file->f_mode & FMODE_READ) -		ret = seq_lseek(file, offset, whence); -	else -		file->f_pos = ret = 1; - -	return ret; -} -  #ifdef CONFIG_DYNAMIC_FTRACE  #ifndef CONFIG_FTRACE_MCOUNT_RECORD @@ -1157,8 +1096,6 @@ struct ftrace_page {  	int			size;  }; -static struct ftrace_page *ftrace_new_pgs; -  #define ENTRY_SIZE sizeof(struct dyn_ftrace)  #define ENTRIES_PER_PAGE (PAGE_SIZE / ENTRY_SIZE) @@ -1168,7 +1105,7 @@ static struct ftrace_page *ftrace_new_pgs;  static struct ftrace_page	*ftrace_pages_start;  static struct ftrace_page	*ftrace_pages; -static bool ftrace_hash_empty(struct ftrace_hash *hash) +static bool __always_inline ftrace_hash_empty(struct ftrace_hash *hash)  {  	return !hash || !hash->count;  } @@ -1545,7 +1482,7 @@ unsigned long ftrace_location(unsigned long ip)   * the function tracer. It checks the ftrace internal tables to   * determine if the address belongs or not.   */ -int ftrace_text_reserved(void *start, void *end) +int ftrace_text_reserved(const void *start, const void *end)  {  	unsigned long ret; @@ -1615,7 +1552,14 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,  			in_other_hash = !!ftrace_lookup_ip(other_hash, rec->ip);  			/* +			 * If filter_hash is set, we want to match all functions +			 * that are in the hash but not in the other hash.  			 * +			 * If filter_hash is not set, then we are decrementing. +			 * That means we match anything that is in the hash +			 * and also in the other_hash. That is, we need to turn +			 * off functions in the other hash because they are disabled +			 * by this hash.  			 */  			if (filter_hash && in_hash && !in_other_hash)  				match = 1; @@ -1757,19 +1701,15 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update)  		/*  		 * If this record is being updated from a nop, then  		 *   return UPDATE_MAKE_CALL. -		 * Otherwise, if the EN flag is set, then return -		 *   UPDATE_MODIFY_CALL_REGS to tell the caller to convert -		 *   from the non-save regs, to a save regs function.  		 * Otherwise,  		 *   return UPDATE_MODIFY_CALL to tell the caller to convert -		 *   from the save regs, to a non-save regs function. +		 *   from the save regs, to a non-save regs function or +		 *   vice versa.  		 */  		if (flag & FTRACE_FL_ENABLED)  			return FTRACE_UPDATE_MAKE_CALL; -		else if (rec->flags & FTRACE_FL_REGS_EN) -			return FTRACE_UPDATE_MODIFY_CALL_REGS; -		else -			return FTRACE_UPDATE_MODIFY_CALL; + +		return FTRACE_UPDATE_MODIFY_CALL;  	}  	if (update) { @@ -1811,6 +1751,42 @@ int ftrace_test_record(struct dyn_ftrace *rec, int enable)  	return ftrace_check_record(rec, enable, 0);  } +/** + * ftrace_get_addr_new - Get the call address to set to + * @rec:  The ftrace record descriptor + * + * If the record has the FTRACE_FL_REGS set, that means that it + * wants to convert to a callback that saves all regs. If FTRACE_FL_REGS + * is not not set, then it wants to convert to the normal callback. + * + * Returns the address of the trampoline to set to + */ +unsigned long ftrace_get_addr_new(struct dyn_ftrace *rec) +{ +	if (rec->flags & FTRACE_FL_REGS) +		return (unsigned long)FTRACE_REGS_ADDR; +	else +		return (unsigned long)FTRACE_ADDR; +} + +/** + * ftrace_get_addr_curr - Get the call address that is already there + * @rec:  The ftrace record descriptor + * + * The FTRACE_FL_REGS_EN is set when the record already points to + * a function that saves all the regs. Basically the '_EN' version + * represents the current state of the function. + * + * Returns the address of the trampoline that is currently being called + */ +unsigned long ftrace_get_addr_curr(struct dyn_ftrace *rec) +{ +	if (rec->flags & FTRACE_FL_REGS_EN) +		return (unsigned long)FTRACE_REGS_ADDR; +	else +		return (unsigned long)FTRACE_ADDR; +} +  static int  __ftrace_replace_code(struct dyn_ftrace *rec, int enable)  { @@ -1818,12 +1794,12 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable)  	unsigned long ftrace_addr;  	int ret; -	ret = ftrace_update_record(rec, enable); +	ftrace_addr = ftrace_get_addr_new(rec); -	if (rec->flags & FTRACE_FL_REGS) -		ftrace_addr = (unsigned long)FTRACE_REGS_ADDR; -	else -		ftrace_addr = (unsigned long)FTRACE_ADDR; +	/* This needs to be done before we call ftrace_update_record */ +	ftrace_old_addr = ftrace_get_addr_curr(rec); + +	ret = ftrace_update_record(rec, enable);  	switch (ret) {  	case FTRACE_UPDATE_IGNORE: @@ -1835,13 +1811,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable)  	case FTRACE_UPDATE_MAKE_NOP:  		return ftrace_make_nop(NULL, rec, ftrace_addr); -	case FTRACE_UPDATE_MODIFY_CALL_REGS:  	case FTRACE_UPDATE_MODIFY_CALL: -		if (rec->flags & FTRACE_FL_REGS) -			ftrace_old_addr = (unsigned long)FTRACE_ADDR; -		else -			ftrace_old_addr = (unsigned long)FTRACE_REGS_ADDR; -  		return ftrace_modify_call(rec, ftrace_old_addr, ftrace_addr);  	} @@ -1979,6 +1949,7 @@ int __weak ftrace_arch_code_modify_post_process(void)  void ftrace_modify_all_code(int command)  {  	int update = command & FTRACE_UPDATE_TRACE_FUNC; +	int err = 0;  	/*  	 * If the ftrace_caller calls a ftrace_ops func directly, @@ -1990,21 +1961,33 @@ void ftrace_modify_all_code(int command)  	 * to make sure the ops are having the right functions  	 * traced.  	 */ -	if (update) -		ftrace_update_ftrace_func(ftrace_ops_list_func); +	if (update) { +		err = ftrace_update_ftrace_func(ftrace_ops_list_func); +		if (FTRACE_WARN_ON(err)) +			return; +	}  	if (command & FTRACE_UPDATE_CALLS)  		ftrace_replace_code(1);  	else if (command & FTRACE_DISABLE_CALLS)  		ftrace_replace_code(0); -	if (update && ftrace_trace_function != ftrace_ops_list_func) -		ftrace_update_ftrace_func(ftrace_trace_function); +	if (update && ftrace_trace_function != ftrace_ops_list_func) { +		function_trace_op = set_function_trace_op; +		smp_wmb(); +		/* If irqs are disabled, we are in stop machine */ +		if (!irqs_disabled()) +			smp_call_function(ftrace_sync_ipi, NULL, 1); +		err = ftrace_update_ftrace_func(ftrace_trace_function); +		if (FTRACE_WARN_ON(err)) +			return; +	}  	if (command & FTRACE_START_FUNC_RET) -		ftrace_enable_ftrace_graph_caller(); +		err = ftrace_enable_ftrace_graph_caller();  	else if (command & FTRACE_STOP_FUNC_RET) -		ftrace_disable_ftrace_graph_caller(); +		err = ftrace_disable_ftrace_graph_caller(); +	FTRACE_WARN_ON(err);  }  static int __ftrace_modify_code(void *data) @@ -2072,6 +2055,11 @@ static ftrace_func_t saved_ftrace_func;  static int ftrace_start_up;  static int global_start_up; +static void control_ops_free(struct ftrace_ops *ops) +{ +	free_percpu(ops->disabled); +} +  static void ftrace_startup_enable(int command)  {  	if (saved_ftrace_func != ftrace_trace_function) { @@ -2087,38 +2075,37 @@ static void ftrace_startup_enable(int command)  static int ftrace_startup(struct ftrace_ops *ops, int command)  { -	bool hash_enable = true; +	int ret;  	if (unlikely(ftrace_disabled))  		return -ENODEV; +	ret = __register_ftrace_function(ops); +	if (ret) +		return ret; +  	ftrace_start_up++;  	command |= FTRACE_UPDATE_CALLS; -	/* ops marked global share the filter hashes */ -	if (ops->flags & FTRACE_OPS_FL_GLOBAL) { -		ops = &global_ops; -		/* Don't update hash if global is already set */ -		if (global_start_up) -			hash_enable = false; -		global_start_up++; -	} -  	ops->flags |= FTRACE_OPS_FL_ENABLED; -	if (hash_enable) -		ftrace_hash_rec_enable(ops, 1); + +	ftrace_hash_rec_enable(ops, 1);  	ftrace_startup_enable(command);  	return 0;  } -static void ftrace_shutdown(struct ftrace_ops *ops, int command) +static int ftrace_shutdown(struct ftrace_ops *ops, int command)  { -	bool hash_disable = true; +	int ret;  	if (unlikely(ftrace_disabled)) -		return; +		return -ENODEV; + +	ret = __unregister_ftrace_function(ops); +	if (ret) +		return ret;  	ftrace_start_up--;  	/* @@ -2128,21 +2115,9 @@ static void ftrace_shutdown(struct ftrace_ops *ops, int command)  	 */  	WARN_ON_ONCE(ftrace_start_up < 0); -	if (ops->flags & FTRACE_OPS_FL_GLOBAL) { -		ops = &global_ops; -		global_start_up--; -		WARN_ON_ONCE(global_start_up < 0); -		/* Don't update hash if global still has users */ -		if (global_start_up) { -			WARN_ON_ONCE(!ftrace_start_up); -			hash_disable = false; -		} -	} - -	if (hash_disable) -		ftrace_hash_rec_disable(ops, 1); +	ftrace_hash_rec_disable(ops, 1); -	if (ops != &global_ops || !global_start_up) +	if (!global_start_up)  		ops->flags &= ~FTRACE_OPS_FL_ENABLED;  	command |= FTRACE_UPDATE_CALLS; @@ -2152,10 +2127,42 @@ static void ftrace_shutdown(struct ftrace_ops *ops, int command)  		command |= FTRACE_UPDATE_TRACE_FUNC;  	} -	if (!command || !ftrace_enabled) -		return; +	if (!command || !ftrace_enabled) { +		/* +		 * If these are control ops, they still need their +		 * per_cpu field freed. Since, function tracing is +		 * not currently active, we can just free them +		 * without synchronizing all CPUs. +		 */ +		if (ops->flags & FTRACE_OPS_FL_CONTROL) +			control_ops_free(ops); +		return 0; +	}  	ftrace_run_update_code(command); + +	/* +	 * Dynamic ops may be freed, we must make sure that all +	 * callers are done before leaving this function. +	 * The same goes for freeing the per_cpu data of the control +	 * ops. +	 * +	 * Again, normal synchronize_sched() is not good enough. +	 * We need to do a hard force of sched synchronization. +	 * This is because we use preempt_disable() to do RCU, but +	 * the function tracers can be called where RCU is not watching +	 * (like before user_exit()). We can not rely on the RCU +	 * infrastructure to do the synchronization, thus we must do it +	 * ourselves. +	 */ +	if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_CONTROL)) { +		schedule_on_each_cpu(ftrace_sync); + +		if (ops->flags & FTRACE_OPS_FL_CONTROL) +			control_ops_free(ops); +	} + +	return 0;  }  static void ftrace_startup_sysctl(void) @@ -2181,7 +2188,6 @@ static void ftrace_shutdown_sysctl(void)  }  static cycle_t		ftrace_update_time; -static unsigned long	ftrace_update_cnt;  unsigned long		ftrace_update_tot_cnt;  static inline int ops_traces_mod(struct ftrace_ops *ops) @@ -2237,11 +2243,12 @@ static int referenced_filters(struct dyn_ftrace *rec)  	return cnt;  } -static int ftrace_update_code(struct module *mod) +static int ftrace_update_code(struct module *mod, struct ftrace_page *new_pgs)  {  	struct ftrace_page *pg;  	struct dyn_ftrace *p;  	cycle_t start, stop; +	unsigned long update_cnt = 0;  	unsigned long ref = 0;  	bool test = false;  	int i; @@ -2267,9 +2274,8 @@ static int ftrace_update_code(struct module *mod)  	}  	start = ftrace_now(raw_smp_processor_id()); -	ftrace_update_cnt = 0; -	for (pg = ftrace_new_pgs; pg; pg = pg->next) { +	for (pg = new_pgs; pg; pg = pg->next) {  		for (i = 0; i < pg->index; i++) {  			int cnt = ref; @@ -2290,7 +2296,7 @@ static int ftrace_update_code(struct module *mod)  			if (!ftrace_code_disable(mod, p))  				break; -			ftrace_update_cnt++; +			update_cnt++;  			/*  			 * If the tracing is enabled, go ahead and enable the record. @@ -2309,11 +2315,9 @@ static int ftrace_update_code(struct module *mod)  		}  	} -	ftrace_new_pgs = NULL; -  	stop = ftrace_now(raw_smp_processor_id());  	ftrace_update_time = stop - start; -	ftrace_update_tot_cnt += ftrace_update_cnt; +	ftrace_update_tot_cnt += update_cnt;  	return 0;  } @@ -2405,22 +2409,6 @@ ftrace_allocate_pages(unsigned long num_to_init)  	return NULL;  } -static int __init ftrace_dyn_table_alloc(unsigned long num_to_init) -{ -	int cnt; - -	if (!num_to_init) { -		pr_info("ftrace: No functions to be traced?\n"); -		return -1; -	} - -	cnt = num_to_init / ENTRIES_PER_PAGE; -	pr_info("ftrace: allocating %ld entries in %d pages\n", -		num_to_init, cnt + 1); - -	return 0; -} -  #define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */  struct ftrace_iterator { @@ -2734,7 +2722,7 @@ static void ftrace_filter_reset(struct ftrace_hash *hash)   * routine, you can use ftrace_filter_write() for the write   * routine if @flag has FTRACE_ITER_FILTER set, or   * ftrace_notrace_write() if @flag has FTRACE_ITER_NOTRACE set. - * ftrace_filter_lseek() should be used as the lseek routine, and + * tracing_lseek() should be used as the lseek routine, and   * release must call ftrace_regex_release().   */  int @@ -2808,7 +2796,9 @@ ftrace_regex_open(struct ftrace_ops *ops, int flag,  static int  ftrace_filter_open(struct inode *inode, struct file *file)  { -	return ftrace_regex_open(&global_ops, +	struct ftrace_ops *ops = inode->i_private; + +	return ftrace_regex_open(ops,  			FTRACE_ITER_FILTER | FTRACE_ITER_DO_HASH,  			inode, file);  } @@ -2816,7 +2806,9 @@ ftrace_filter_open(struct inode *inode, struct file *file)  static int  ftrace_notrace_open(struct inode *inode, struct file *file)  { -	return ftrace_regex_open(&global_ops, FTRACE_ITER_NOTRACE, +	struct ftrace_ops *ops = inode->i_private; + +	return ftrace_regex_open(ops, FTRACE_ITER_NOTRACE,  				 inode, file);  } @@ -3060,16 +3052,13 @@ static void __enable_ftrace_function_probe(void)  	if (i == FTRACE_FUNC_HASHSIZE)  		return; -	ret = __register_ftrace_function(&trace_probe_ops); -	if (!ret) -		ret = ftrace_startup(&trace_probe_ops, 0); +	ret = ftrace_startup(&trace_probe_ops, 0);  	ftrace_probe_registered = 1;  }  static void __disable_ftrace_function_probe(void)  { -	int ret;  	int i;  	if (!ftrace_probe_registered) @@ -3082,9 +3071,7 @@ static void __disable_ftrace_function_probe(void)  	}  	/* no more funcs left */ -	ret = __unregister_ftrace_function(&trace_probe_ops); -	if (!ret) -		ftrace_shutdown(&trace_probe_ops, 0); +	ftrace_shutdown(&trace_probe_ops, 0);  	ftrace_probe_registered = 0;  } @@ -3307,7 +3294,11 @@ void unregister_ftrace_function_probe_all(char *glob)  static LIST_HEAD(ftrace_commands);  static DEFINE_MUTEX(ftrace_cmd_mutex); -int register_ftrace_command(struct ftrace_func_command *cmd) +/* + * Currently we only register ftrace commands from __init, so mark this + * __init too. + */ +__init int register_ftrace_command(struct ftrace_func_command *cmd)  {  	struct ftrace_func_command *p;  	int ret = 0; @@ -3326,7 +3317,11 @@ int register_ftrace_command(struct ftrace_func_command *cmd)  	return ret;  } -int unregister_ftrace_command(struct ftrace_func_command *cmd) +/* + * Currently we only unregister ftrace commands from __init, so mark + * this __init too. + */ +__init int unregister_ftrace_command(struct ftrace_func_command *cmd)  {  	struct ftrace_func_command *p, *n;  	int ret = -ENODEV; @@ -3466,10 +3461,6 @@ ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len,  	struct ftrace_hash *hash;  	int ret; -	/* All global ops uses the global ops filters */ -	if (ops->flags & FTRACE_OPS_FL_GLOBAL) -		ops = &global_ops; -  	if (unlikely(ftrace_disabled))  		return -ENODEV; @@ -3581,8 +3572,7 @@ int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf,  }  EXPORT_SYMBOL_GPL(ftrace_set_notrace);  /** - * ftrace_set_filter - set a function to filter on in ftrace - * @ops - the ops to set the filter with + * ftrace_set_global_filter - set a function to filter on with global tracers   * @buf - the string that holds the function filter text.   * @len - the length of the string.   * @reset - non zero to reset all filters before applying this filter. @@ -3597,8 +3587,7 @@ void ftrace_set_global_filter(unsigned char *buf, int len, int reset)  EXPORT_SYMBOL_GPL(ftrace_set_global_filter);  /** - * ftrace_set_notrace - set a function to not trace in ftrace - * @ops - the ops to set the notrace filter with + * ftrace_set_global_notrace - set a function to not trace with global tracers   * @buf - the string that holds the function notrace text.   * @len - the length of the string.   * @reset - non zero to reset all filters before applying this filter. @@ -3641,7 +3630,7 @@ __setup("ftrace_filter=", set_ftrace_filter);  #ifdef CONFIG_FUNCTION_GRAPH_TRACER  static char ftrace_graph_buf[FTRACE_FILTER_SIZE] __initdata; -static int ftrace_set_func(unsigned long *array, int *idx, char *buffer); +static int ftrace_set_func(unsigned long *array, int *idx, int size, char *buffer);  static int __init set_graph_function(char *str)  { @@ -3659,7 +3648,7 @@ static void __init set_ftrace_early_graph(char *buf)  		func = strsep(&buf, ",");  		/* we allow only one expression at a time */  		ret = ftrace_set_func(ftrace_graph_funcs, &ftrace_graph_count, -				      func); +				      FTRACE_GRAPH_MAX_FUNCS, func);  		if (ret)  			printk(KERN_DEBUG "ftrace: function %s not "  					  "traceable\n", func); @@ -3759,7 +3748,7 @@ static const struct file_operations ftrace_filter_fops = {  	.open = ftrace_filter_open,  	.read = seq_read,  	.write = ftrace_filter_write, -	.llseek = ftrace_filter_lseek, +	.llseek = tracing_lseek,  	.release = ftrace_regex_release,  }; @@ -3767,7 +3756,7 @@ static const struct file_operations ftrace_notrace_fops = {  	.open = ftrace_notrace_open,  	.read = seq_read,  	.write = ftrace_notrace_write, -	.llseek = ftrace_filter_lseek, +	.llseek = tracing_lseek,  	.release = ftrace_regex_release,  }; @@ -3776,15 +3765,25 @@ static const struct file_operations ftrace_notrace_fops = {  static DEFINE_MUTEX(graph_lock);  int ftrace_graph_count; -int ftrace_graph_filter_enabled; +int ftrace_graph_notrace_count;  unsigned long ftrace_graph_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly; +unsigned long ftrace_graph_notrace_funcs[FTRACE_GRAPH_MAX_FUNCS] __read_mostly; + +struct ftrace_graph_data { +	unsigned long *table; +	size_t size; +	int *count; +	const struct seq_operations *seq_ops; +};  static void *  __g_next(struct seq_file *m, loff_t *pos)  { -	if (*pos >= ftrace_graph_count) +	struct ftrace_graph_data *fgd = m->private; + +	if (*pos >= *fgd->count)  		return NULL; -	return &ftrace_graph_funcs[*pos]; +	return &fgd->table[*pos];  }  static void * @@ -3796,10 +3795,12 @@ g_next(struct seq_file *m, void *v, loff_t *pos)  static void *g_start(struct seq_file *m, loff_t *pos)  { +	struct ftrace_graph_data *fgd = m->private; +  	mutex_lock(&graph_lock);  	/* Nothing, tell g_show to print all functions are enabled */ -	if (!ftrace_graph_filter_enabled && !*pos) +	if (!*fgd->count && !*pos)  		return (void *)1;  	return __g_next(m, pos); @@ -3835,38 +3836,88 @@ static const struct seq_operations ftrace_graph_seq_ops = {  };  static int -ftrace_graph_open(struct inode *inode, struct file *file) +__ftrace_graph_open(struct inode *inode, struct file *file, +		    struct ftrace_graph_data *fgd)  {  	int ret = 0; -	if (unlikely(ftrace_disabled)) -		return -ENODEV; -  	mutex_lock(&graph_lock);  	if ((file->f_mode & FMODE_WRITE) &&  	    (file->f_flags & O_TRUNC)) { -		ftrace_graph_filter_enabled = 0; -		ftrace_graph_count = 0; -		memset(ftrace_graph_funcs, 0, sizeof(ftrace_graph_funcs)); +		*fgd->count = 0; +		memset(fgd->table, 0, fgd->size * sizeof(*fgd->table));  	}  	mutex_unlock(&graph_lock); -	if (file->f_mode & FMODE_READ) -		ret = seq_open(file, &ftrace_graph_seq_ops); +	if (file->f_mode & FMODE_READ) { +		ret = seq_open(file, fgd->seq_ops); +		if (!ret) { +			struct seq_file *m = file->private_data; +			m->private = fgd; +		} +	} else +		file->private_data = fgd;  	return ret;  }  static int +ftrace_graph_open(struct inode *inode, struct file *file) +{ +	struct ftrace_graph_data *fgd; + +	if (unlikely(ftrace_disabled)) +		return -ENODEV; + +	fgd = kmalloc(sizeof(*fgd), GFP_KERNEL); +	if (fgd == NULL) +		return -ENOMEM; + +	fgd->table = ftrace_graph_funcs; +	fgd->size = FTRACE_GRAPH_MAX_FUNCS; +	fgd->count = &ftrace_graph_count; +	fgd->seq_ops = &ftrace_graph_seq_ops; + +	return __ftrace_graph_open(inode, file, fgd); +} + +static int +ftrace_graph_notrace_open(struct inode *inode, struct file *file) +{ +	struct ftrace_graph_data *fgd; + +	if (unlikely(ftrace_disabled)) +		return -ENODEV; + +	fgd = kmalloc(sizeof(*fgd), GFP_KERNEL); +	if (fgd == NULL) +		return -ENOMEM; + +	fgd->table = ftrace_graph_notrace_funcs; +	fgd->size = FTRACE_GRAPH_MAX_FUNCS; +	fgd->count = &ftrace_graph_notrace_count; +	fgd->seq_ops = &ftrace_graph_seq_ops; + +	return __ftrace_graph_open(inode, file, fgd); +} + +static int  ftrace_graph_release(struct inode *inode, struct file *file)  { -	if (file->f_mode & FMODE_READ) +	if (file->f_mode & FMODE_READ) { +		struct seq_file *m = file->private_data; + +		kfree(m->private);  		seq_release(inode, file); +	} else { +		kfree(file->private_data); +	} +  	return 0;  }  static int -ftrace_set_func(unsigned long *array, int *idx, char *buffer) +ftrace_set_func(unsigned long *array, int *idx, int size, char *buffer)  {  	struct dyn_ftrace *rec;  	struct ftrace_page *pg; @@ -3879,7 +3930,7 @@ ftrace_set_func(unsigned long *array, int *idx, char *buffer)  	/* decode regex */  	type = filter_parse_regex(buffer, strlen(buffer), &search, ¬); -	if (!not && *idx >= FTRACE_GRAPH_MAX_FUNCS) +	if (!not && *idx >= size)  		return -EBUSY;  	search_len = strlen(search); @@ -3907,7 +3958,7 @@ ftrace_set_func(unsigned long *array, int *idx, char *buffer)  				fail = 0;  				if (!exists) {  					array[(*idx)++] = rec->ip; -					if (*idx >= FTRACE_GRAPH_MAX_FUNCS) +					if (*idx >= size)  						goto out;  				}  			} else { @@ -3925,8 +3976,6 @@ out:  	if (fail)  		return -EINVAL; -	ftrace_graph_filter_enabled = !!(*idx); -  	return 0;  } @@ -3935,36 +3984,33 @@ ftrace_graph_write(struct file *file, const char __user *ubuf,  		   size_t cnt, loff_t *ppos)  {  	struct trace_parser parser; -	ssize_t read, ret; +	ssize_t read, ret = 0; +	struct ftrace_graph_data *fgd = file->private_data;  	if (!cnt)  		return 0; -	mutex_lock(&graph_lock); - -	if (trace_parser_get_init(&parser, FTRACE_BUFF_MAX)) { -		ret = -ENOMEM; -		goto out_unlock; -	} +	if (trace_parser_get_init(&parser, FTRACE_BUFF_MAX)) +		return -ENOMEM;  	read = trace_get_user(&parser, ubuf, cnt, ppos);  	if (read >= 0 && trace_parser_loaded((&parser))) {  		parser.buffer[parser.idx] = 0; +		mutex_lock(&graph_lock); +  		/* we allow only one expression at a time */ -		ret = ftrace_set_func(ftrace_graph_funcs, &ftrace_graph_count, -					parser.buffer); -		if (ret) -			goto out_free; +		ret = ftrace_set_func(fgd->table, fgd->count, fgd->size, +				      parser.buffer); + +		mutex_unlock(&graph_lock);  	} -	ret = read; +	if (!ret) +		ret = read; -out_free:  	trace_parser_put(&parser); -out_unlock: -	mutex_unlock(&graph_lock);  	return ret;  } @@ -3973,11 +4019,49 @@ static const struct file_operations ftrace_graph_fops = {  	.open		= ftrace_graph_open,  	.read		= seq_read,  	.write		= ftrace_graph_write, -	.llseek		= ftrace_filter_lseek, +	.llseek		= tracing_lseek, +	.release	= ftrace_graph_release, +}; + +static const struct file_operations ftrace_graph_notrace_fops = { +	.open		= ftrace_graph_notrace_open, +	.read		= seq_read, +	.write		= ftrace_graph_write, +	.llseek		= tracing_lseek,  	.release	= ftrace_graph_release,  };  #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +void ftrace_create_filter_files(struct ftrace_ops *ops, +				struct dentry *parent) +{ + +	trace_create_file("set_ftrace_filter", 0644, parent, +			  ops, &ftrace_filter_fops); + +	trace_create_file("set_ftrace_notrace", 0644, parent, +			  ops, &ftrace_notrace_fops); +} + +/* + * The name "destroy_filter_files" is really a misnomer. Although + * in the future, it may actualy delete the files, but this is + * really intended to make sure the ops passed in are disabled + * and that when this function returns, the caller is free to + * free the ops. + * + * The "destroy" name is only to match the "create" name that this + * should be paired with. + */ +void ftrace_destroy_filter_files(struct ftrace_ops *ops) +{ +	mutex_lock(&ftrace_lock); +	if (ops->flags & FTRACE_OPS_FL_ENABLED) +		ftrace_shutdown(ops, 0); +	ops->flags |= FTRACE_OPS_FL_DELETED; +	mutex_unlock(&ftrace_lock); +} +  static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)  { @@ -3987,16 +4071,15 @@ static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)  	trace_create_file("enabled_functions", 0444,  			d_tracer, NULL, &ftrace_enabled_fops); -	trace_create_file("set_ftrace_filter", 0644, d_tracer, -			NULL, &ftrace_filter_fops); - -	trace_create_file("set_ftrace_notrace", 0644, d_tracer, -				    NULL, &ftrace_notrace_fops); +	ftrace_create_filter_files(&global_ops, d_tracer);  #ifdef CONFIG_FUNCTION_GRAPH_TRACER  	trace_create_file("set_graph_function", 0444, d_tracer,  				    NULL,  				    &ftrace_graph_fops); +	trace_create_file("set_graph_notrace", 0444, d_tracer, +				    NULL, +				    &ftrace_graph_notrace_fops);  #endif /* CONFIG_FUNCTION_GRAPH_TRACER */  	return 0; @@ -4104,9 +4187,6 @@ static int ftrace_process_locs(struct module *mod,  	/* Assign the last page to ftrace_pages */  	ftrace_pages = pg; -	/* These new locations need to be initialized */ -	ftrace_new_pgs = start_pg; -  	/*  	 * We only need to disable interrupts on start up  	 * because we are modifying code that an interrupt @@ -4117,7 +4197,7 @@ static int ftrace_process_locs(struct module *mod,  	 */  	if (!mod)  		local_irq_save(flags); -	ftrace_update_code(mod); +	ftrace_update_code(mod, start_pg);  	if (!mod)  		local_irq_restore(flags);  	ret = 0; @@ -4181,16 +4261,11 @@ static void ftrace_init_module(struct module *mod,  	ftrace_process_locs(mod, start, end);  } -static int ftrace_module_notify_enter(struct notifier_block *self, -				      unsigned long val, void *data) +void ftrace_module_init(struct module *mod)  { -	struct module *mod = data; - -	if (val == MODULE_STATE_COMING) -		ftrace_init_module(mod, mod->ftrace_callsites, -				   mod->ftrace_callsites + -				   mod->num_ftrace_callsites); -	return 0; +	ftrace_init_module(mod, mod->ftrace_callsites, +			   mod->ftrace_callsites + +			   mod->num_ftrace_callsites);  }  static int ftrace_module_notify_exit(struct notifier_block *self, @@ -4204,11 +4279,6 @@ static int ftrace_module_notify_exit(struct notifier_block *self,  	return 0;  }  #else -static int ftrace_module_notify_enter(struct notifier_block *self, -				      unsigned long val, void *data) -{ -	return 0; -}  static int ftrace_module_notify_exit(struct notifier_block *self,  				     unsigned long val, void *data)  { @@ -4216,40 +4286,32 @@ static int ftrace_module_notify_exit(struct notifier_block *self,  }  #endif /* CONFIG_MODULES */ -struct notifier_block ftrace_module_enter_nb = { -	.notifier_call = ftrace_module_notify_enter, -	.priority = INT_MAX,	/* Run before anything that can use kprobes */ -}; -  struct notifier_block ftrace_module_exit_nb = {  	.notifier_call = ftrace_module_notify_exit,  	.priority = INT_MIN,	/* Run after anything that can remove kprobes */  }; -extern unsigned long __start_mcount_loc[]; -extern unsigned long __stop_mcount_loc[]; -  void __init ftrace_init(void)  { -	unsigned long count, addr, flags; +	extern unsigned long __start_mcount_loc[]; +	extern unsigned long __stop_mcount_loc[]; +	unsigned long count, flags;  	int ret; -	/* Keep the ftrace pointer to the stub */ -	addr = (unsigned long)ftrace_stub; -  	local_irq_save(flags); -	ftrace_dyn_arch_init(&addr); +	ret = ftrace_dyn_arch_init();  	local_irq_restore(flags); - -	/* ftrace_dyn_arch_init places the return code in addr */ -	if (addr) +	if (ret)  		goto failed;  	count = __stop_mcount_loc - __start_mcount_loc; - -	ret = ftrace_dyn_table_alloc(count); -	if (ret) +	if (!count) { +		pr_info("ftrace: No functions to be traced?\n");  		goto failed; +	} + +	pr_info("ftrace: allocating %ld entries in %ld pages\n", +		count, count / ENTRIES_PER_PAGE + 1);  	last_ftrace_enabled = ftrace_enabled = 1; @@ -4257,10 +4319,6 @@ void __init ftrace_init(void)  				  __start_mcount_loc,  				  __stop_mcount_loc); -	ret = register_module_notifier(&ftrace_module_enter_nb); -	if (ret) -		pr_warning("Failed to register trace ftrace module enter notifier\n"); -  	ret = register_module_notifier(&ftrace_module_exit_nb);  	if (ret)  		pr_warning("Failed to register trace ftrace module exit notifier\n"); @@ -4290,12 +4348,21 @@ core_initcall(ftrace_nodyn_init);  static inline int ftrace_init_dyn_debugfs(struct dentry *d_tracer) { return 0; }  static inline void ftrace_startup_enable(int command) { }  /* Keep as macros so we do not need to define the commands */ -# define ftrace_startup(ops, command)			\ -	({						\ -		(ops)->flags |= FTRACE_OPS_FL_ENABLED;	\ -		0;					\ +# define ftrace_startup(ops, command)					\ +	({								\ +		int ___ret = __register_ftrace_function(ops);		\ +		if (!___ret)						\ +			(ops)->flags |= FTRACE_OPS_FL_ENABLED;		\ +		___ret;							\  	}) -# define ftrace_shutdown(ops, command)	do { } while (0) +# define ftrace_shutdown(ops, command)					\ +	({								\ +		int ___ret = __unregister_ftrace_function(ops);		\ +		if (!___ret)						\ +			(ops)->flags &= ~FTRACE_OPS_FL_ENABLED;		\ +		___ret;							\ +	}) +  # define ftrace_startup_sysctl()	do { } while (0)  # define ftrace_shutdown_sysctl()	do { } while (0) @@ -4307,6 +4374,34 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs)  #endif /* CONFIG_DYNAMIC_FTRACE */ +__init void ftrace_init_global_array_ops(struct trace_array *tr) +{ +	tr->ops = &global_ops; +	tr->ops->private = tr; +} + +void ftrace_init_array_ops(struct trace_array *tr, ftrace_func_t func) +{ +	/* If we filter on pids, update to use the pid function */ +	if (tr->flags & TRACE_ARRAY_FL_GLOBAL) { +		if (WARN_ON(tr->ops->func != ftrace_stub)) +			printk("ftrace ops had %pS for function\n", +			       tr->ops->func); +		/* Only the top level instance does pid tracing */ +		if (!list_empty(&ftrace_pids)) { +			set_ftrace_pid_function(func); +			func = ftrace_pid_func; +		} +	} +	tr->ops->func = func; +	tr->ops->private = tr; +} + +void ftrace_reset_array_ops(struct trace_array *tr) +{ +	tr->ops->func = ftrace_stub; +} +  static void  ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip,  			struct ftrace_ops *op, struct pt_regs *regs) @@ -4320,12 +4415,21 @@ ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip,  	 */  	preempt_disable_notrace();  	trace_recursion_set(TRACE_CONTROL_BIT); + +	/* +	 * Control funcs (perf) uses RCU. Only trace if +	 * RCU is currently active. +	 */ +	if (!rcu_is_watching()) +		goto out; +  	do_for_each_ftrace_op(op, ftrace_control_list) {  		if (!(op->flags & FTRACE_OPS_FL_STUB) &&  		    !ftrace_function_local_disabled(op) &&  		    ftrace_ops_test(op, ip, regs))  			op->func(ip, parent_ip, op, regs);  	} while_for_each_ftrace_op(op); + out:  	trace_recursion_clear(TRACE_CONTROL_BIT);  	preempt_enable_notrace();  } @@ -4356,9 +4460,16 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,  	 */  	preempt_disable_notrace();  	do_for_each_ftrace_op(op, ftrace_ops_list) { -		if (ftrace_ops_test(op, ip, regs)) +		if (ftrace_ops_test(op, ip, regs)) { +			if (WARN_ON(!op->func)) { +				function_trace_stop = 1; +				printk("op=%p %pS\n", op, op); +				goto out; +			}  			op->func(ip, parent_ip, op, regs); +		}  	} while_for_each_ftrace_op(op); +out:  	preempt_enable_notrace();  	trace_clear_recursion(bit);  } @@ -4631,7 +4742,7 @@ static const struct file_operations ftrace_pid_fops = {  	.open		= ftrace_pid_open,  	.write		= ftrace_pid_write,  	.read		= seq_read, -	.llseek		= ftrace_filter_lseek, +	.llseek		= tracing_lseek,  	.release	= ftrace_pid_release,  }; @@ -4695,9 +4806,7 @@ int register_ftrace_function(struct ftrace_ops *ops)  	mutex_lock(&ftrace_lock); -	ret = __register_ftrace_function(ops); -	if (!ret) -		ret = ftrace_startup(ops, 0); +	ret = ftrace_startup(ops, 0);  	mutex_unlock(&ftrace_lock); @@ -4716,9 +4825,7 @@ int unregister_ftrace_function(struct ftrace_ops *ops)  	int ret;  	mutex_lock(&ftrace_lock); -	ret = __unregister_ftrace_function(ops); -	if (!ret) -		ftrace_shutdown(ops, 0); +	ret = ftrace_shutdown(ops, 0);  	mutex_unlock(&ftrace_lock);  	return ret; @@ -4767,7 +4874,6 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,  #ifdef CONFIG_FUNCTION_GRAPH_TRACER  static int ftrace_graph_active; -static struct notifier_block ftrace_suspend_notifier;  int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace)  { @@ -4778,6 +4884,7 @@ int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace)  trace_func_graph_ret_t ftrace_graph_return =  			(trace_func_graph_ret_t)ftrace_stub;  trace_func_graph_ent_t ftrace_graph_entry = ftrace_graph_entry_stub; +static trace_func_graph_ent_t __ftrace_graph_entry = ftrace_graph_entry_stub;  /* Try to assign a return stack array on FTRACE_RETSTACK_ALLOC_SIZE tasks. */  static int alloc_retstack_tasklist(struct ftrace_ret_stack **ret_stack_list) @@ -4912,6 +5019,34 @@ ftrace_suspend_notifier_call(struct notifier_block *bl, unsigned long state,  	return NOTIFY_DONE;  } +static int ftrace_graph_entry_test(struct ftrace_graph_ent *trace) +{ +	if (!ftrace_ops_test(&global_ops, trace->func, NULL)) +		return 0; +	return __ftrace_graph_entry(trace); +} + +/* + * The function graph tracer should only trace the functions defined + * by set_ftrace_filter and set_ftrace_notrace. If another function + * tracer ops is registered, the graph tracer requires testing the + * function against the global ops, and not just trace any function + * that any ftrace_ops registered. + */ +static void update_function_graph_func(void) +{ +	if (ftrace_ops_list == &ftrace_list_end || +	    (ftrace_ops_list == &global_ops && +	     global_ops.next == &ftrace_list_end)) +		ftrace_graph_entry = __ftrace_graph_entry; +	else +		ftrace_graph_entry = ftrace_graph_entry_test; +} + +static struct notifier_block ftrace_suspend_notifier = { +	.notifier_call = ftrace_suspend_notifier_call, +}; +  int register_ftrace_graph(trace_func_graph_ret_t retfunc,  			trace_func_graph_ent_t entryfunc)  { @@ -4925,7 +5060,6 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc,  		goto out;  	} -	ftrace_suspend_notifier.notifier_call = ftrace_suspend_notifier_call;  	register_pm_notifier(&ftrace_suspend_notifier);  	ftrace_graph_active++; @@ -4936,7 +5070,19 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc,  	}  	ftrace_graph_return = retfunc; -	ftrace_graph_entry = entryfunc; + +	/* +	 * Update the indirect function to the entryfunc, and the +	 * function that gets called to the entry_test first. Then +	 * call the update fgraph entry function to determine if +	 * the entryfunc should be called directly or not. +	 */ +	__ftrace_graph_entry = entryfunc; +	ftrace_graph_entry = ftrace_graph_entry_test; +	update_function_graph_func(); + +	/* Function graph doesn't use the .func field of global_ops */ +	global_ops.flags |= FTRACE_OPS_FL_STUB;  	ret = ftrace_startup(&global_ops, FTRACE_START_FUNC_RET); @@ -4955,7 +5101,9 @@ void unregister_ftrace_graph(void)  	ftrace_graph_active--;  	ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub;  	ftrace_graph_entry = ftrace_graph_entry_stub; +	__ftrace_graph_entry = ftrace_graph_entry_stub;  	ftrace_shutdown(&global_ops, FTRACE_STOP_FUNC_RET); +	global_ops.flags &= ~FTRACE_OPS_FL_STUB;  	unregister_pm_notifier(&ftrace_suspend_notifier);  	unregister_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL);  | 
