xref: /openbmc/linux/kernel/trace/fprobe.c (revision 76d0de5729c0569c4071e7f21fcab394e502f03a)
1cad9931fSMasami Hiramatsu // SPDX-License-Identifier: GPL-2.0
2cad9931fSMasami Hiramatsu /*
3cad9931fSMasami Hiramatsu  * fprobe - Simple ftrace probe wrapper for function entry.
4cad9931fSMasami Hiramatsu  */
5cad9931fSMasami Hiramatsu #define pr_fmt(fmt) "fprobe: " fmt
6cad9931fSMasami Hiramatsu 
7cad9931fSMasami Hiramatsu #include <linux/err.h>
8cad9931fSMasami Hiramatsu #include <linux/fprobe.h>
9cad9931fSMasami Hiramatsu #include <linux/kallsyms.h>
10cad9931fSMasami Hiramatsu #include <linux/kprobes.h>
115b0ab789SMasami Hiramatsu #include <linux/rethook.h>
12cad9931fSMasami Hiramatsu #include <linux/slab.h>
13cad9931fSMasami Hiramatsu #include <linux/sort.h>
14cad9931fSMasami Hiramatsu 
155b0ab789SMasami Hiramatsu #include "trace.h"
165b0ab789SMasami Hiramatsu 
175b0ab789SMasami Hiramatsu struct fprobe_rethook_node {
185b0ab789SMasami Hiramatsu 	struct rethook_node node;
195b0ab789SMasami Hiramatsu 	unsigned long entry_ip;
20*76d0de57SMasami Hiramatsu (Google) 	char data[];
215b0ab789SMasami Hiramatsu };
225b0ab789SMasami Hiramatsu 
23cad9931fSMasami Hiramatsu static void fprobe_handler(unsigned long ip, unsigned long parent_ip,
24cad9931fSMasami Hiramatsu 			   struct ftrace_ops *ops, struct ftrace_regs *fregs)
25cad9931fSMasami Hiramatsu {
265b0ab789SMasami Hiramatsu 	struct fprobe_rethook_node *fpr;
27*76d0de57SMasami Hiramatsu (Google) 	struct rethook_node *rh = NULL;
28cad9931fSMasami Hiramatsu 	struct fprobe *fp;
29*76d0de57SMasami Hiramatsu (Google) 	void *entry_data = NULL;
30cad9931fSMasami Hiramatsu 	int bit;
31cad9931fSMasami Hiramatsu 
32cad9931fSMasami Hiramatsu 	fp = container_of(ops, struct fprobe, ops);
33cad9931fSMasami Hiramatsu 	if (fprobe_disabled(fp))
34cad9931fSMasami Hiramatsu 		return;
35cad9931fSMasami Hiramatsu 
36cad9931fSMasami Hiramatsu 	bit = ftrace_test_recursion_trylock(ip, parent_ip);
37cad9931fSMasami Hiramatsu 	if (bit < 0) {
38cad9931fSMasami Hiramatsu 		fp->nmissed++;
39cad9931fSMasami Hiramatsu 		return;
40cad9931fSMasami Hiramatsu 	}
41cad9931fSMasami Hiramatsu 
425b0ab789SMasami Hiramatsu 	if (fp->exit_handler) {
435b0ab789SMasami Hiramatsu 		rh = rethook_try_get(fp->rethook);
445b0ab789SMasami Hiramatsu 		if (!rh) {
455b0ab789SMasami Hiramatsu 			fp->nmissed++;
465b0ab789SMasami Hiramatsu 			goto out;
475b0ab789SMasami Hiramatsu 		}
485b0ab789SMasami Hiramatsu 		fpr = container_of(rh, struct fprobe_rethook_node, node);
495b0ab789SMasami Hiramatsu 		fpr->entry_ip = ip;
50*76d0de57SMasami Hiramatsu (Google) 		if (fp->entry_data_size)
51*76d0de57SMasami Hiramatsu (Google) 			entry_data = fpr->data;
525b0ab789SMasami Hiramatsu 	}
535b0ab789SMasami Hiramatsu 
54*76d0de57SMasami Hiramatsu (Google) 	if (fp->entry_handler)
55*76d0de57SMasami Hiramatsu (Google) 		fp->entry_handler(fp, ip, ftrace_get_regs(fregs), entry_data);
56*76d0de57SMasami Hiramatsu (Google) 
57*76d0de57SMasami Hiramatsu (Google) 	if (rh)
58*76d0de57SMasami Hiramatsu (Google) 		rethook_hook(rh, ftrace_get_regs(fregs), true);
59*76d0de57SMasami Hiramatsu (Google) 
605b0ab789SMasami Hiramatsu out:
61cad9931fSMasami Hiramatsu 	ftrace_test_recursion_unlock(bit);
62cad9931fSMasami Hiramatsu }
63cad9931fSMasami Hiramatsu NOKPROBE_SYMBOL(fprobe_handler);
64cad9931fSMasami Hiramatsu 
65ab51e15dSMasami Hiramatsu static void fprobe_kprobe_handler(unsigned long ip, unsigned long parent_ip,
66ab51e15dSMasami Hiramatsu 				  struct ftrace_ops *ops, struct ftrace_regs *fregs)
67ab51e15dSMasami Hiramatsu {
68ab51e15dSMasami Hiramatsu 	struct fprobe *fp = container_of(ops, struct fprobe, ops);
69ab51e15dSMasami Hiramatsu 
70ab51e15dSMasami Hiramatsu 	if (unlikely(kprobe_running())) {
71ab51e15dSMasami Hiramatsu 		fp->nmissed++;
72ab51e15dSMasami Hiramatsu 		return;
73ab51e15dSMasami Hiramatsu 	}
74ab51e15dSMasami Hiramatsu 	kprobe_busy_begin();
75ab51e15dSMasami Hiramatsu 	fprobe_handler(ip, parent_ip, ops, fregs);
76ab51e15dSMasami Hiramatsu 	kprobe_busy_end();
77ab51e15dSMasami Hiramatsu }
78ab51e15dSMasami Hiramatsu 
795b0ab789SMasami Hiramatsu static void fprobe_exit_handler(struct rethook_node *rh, void *data,
805b0ab789SMasami Hiramatsu 				struct pt_regs *regs)
815b0ab789SMasami Hiramatsu {
825b0ab789SMasami Hiramatsu 	struct fprobe *fp = (struct fprobe *)data;
835b0ab789SMasami Hiramatsu 	struct fprobe_rethook_node *fpr;
845b0ab789SMasami Hiramatsu 
855b0ab789SMasami Hiramatsu 	if (!fp || fprobe_disabled(fp))
865b0ab789SMasami Hiramatsu 		return;
875b0ab789SMasami Hiramatsu 
885b0ab789SMasami Hiramatsu 	fpr = container_of(rh, struct fprobe_rethook_node, node);
895b0ab789SMasami Hiramatsu 
90*76d0de57SMasami Hiramatsu (Google) 	fp->exit_handler(fp, fpr->entry_ip, regs,
91*76d0de57SMasami Hiramatsu (Google) 			 fp->entry_data_size ? (void *)fpr->data : NULL);
925b0ab789SMasami Hiramatsu }
935b0ab789SMasami Hiramatsu NOKPROBE_SYMBOL(fprobe_exit_handler);
945b0ab789SMasami Hiramatsu 
958be92533SJiri Olsa static int symbols_cmp(const void *a, const void *b)
968be92533SJiri Olsa {
978be92533SJiri Olsa 	const char **str_a = (const char **) a;
988be92533SJiri Olsa 	const char **str_b = (const char **) b;
998be92533SJiri Olsa 
1008be92533SJiri Olsa 	return strcmp(*str_a, *str_b);
1018be92533SJiri Olsa }
1028be92533SJiri Olsa 
103cad9931fSMasami Hiramatsu /* Convert ftrace location address from symbols */
104cad9931fSMasami Hiramatsu static unsigned long *get_ftrace_locations(const char **syms, int num)
105cad9931fSMasami Hiramatsu {
106cad9931fSMasami Hiramatsu 	unsigned long *addrs;
107cad9931fSMasami Hiramatsu 
108cad9931fSMasami Hiramatsu 	/* Convert symbols to symbol address */
109cad9931fSMasami Hiramatsu 	addrs = kcalloc(num, sizeof(*addrs), GFP_KERNEL);
110cad9931fSMasami Hiramatsu 	if (!addrs)
111cad9931fSMasami Hiramatsu 		return ERR_PTR(-ENOMEM);
112cad9931fSMasami Hiramatsu 
1138be92533SJiri Olsa 	/* ftrace_lookup_symbols expects sorted symbols */
1148be92533SJiri Olsa 	sort(syms, num, sizeof(*syms), symbols_cmp, NULL);
115cad9931fSMasami Hiramatsu 
1168be92533SJiri Olsa 	if (!ftrace_lookup_symbols(syms, num, addrs))
117cad9931fSMasami Hiramatsu 		return addrs;
118cad9931fSMasami Hiramatsu 
119cad9931fSMasami Hiramatsu 	kfree(addrs);
120cad9931fSMasami Hiramatsu 	return ERR_PTR(-ENOENT);
121cad9931fSMasami Hiramatsu }
122cad9931fSMasami Hiramatsu 
123cad9931fSMasami Hiramatsu static void fprobe_init(struct fprobe *fp)
124cad9931fSMasami Hiramatsu {
125cad9931fSMasami Hiramatsu 	fp->nmissed = 0;
126ab51e15dSMasami Hiramatsu 	if (fprobe_shared_with_kprobes(fp))
127ab51e15dSMasami Hiramatsu 		fp->ops.func = fprobe_kprobe_handler;
128ab51e15dSMasami Hiramatsu 	else
129cad9931fSMasami Hiramatsu 		fp->ops.func = fprobe_handler;
130cad9931fSMasami Hiramatsu 	fp->ops.flags |= FTRACE_OPS_FL_SAVE_REGS;
131cad9931fSMasami Hiramatsu }
132cad9931fSMasami Hiramatsu 
1335b0ab789SMasami Hiramatsu static int fprobe_init_rethook(struct fprobe *fp, int num)
1345b0ab789SMasami Hiramatsu {
1355b0ab789SMasami Hiramatsu 	int i, size;
1365b0ab789SMasami Hiramatsu 
1375b0ab789SMasami Hiramatsu 	if (num < 0)
1385b0ab789SMasami Hiramatsu 		return -EINVAL;
1395b0ab789SMasami Hiramatsu 
1405b0ab789SMasami Hiramatsu 	if (!fp->exit_handler) {
1415b0ab789SMasami Hiramatsu 		fp->rethook = NULL;
1425b0ab789SMasami Hiramatsu 		return 0;
1435b0ab789SMasami Hiramatsu 	}
1445b0ab789SMasami Hiramatsu 
1455b0ab789SMasami Hiramatsu 	/* Initialize rethook if needed */
1465b0ab789SMasami Hiramatsu 	size = num * num_possible_cpus() * 2;
1475b0ab789SMasami Hiramatsu 	if (size < 0)
1485b0ab789SMasami Hiramatsu 		return -E2BIG;
1495b0ab789SMasami Hiramatsu 
1505b0ab789SMasami Hiramatsu 	fp->rethook = rethook_alloc((void *)fp, fprobe_exit_handler);
151d05ea35eSRafael Mendonca 	if (!fp->rethook)
152d05ea35eSRafael Mendonca 		return -ENOMEM;
1535b0ab789SMasami Hiramatsu 	for (i = 0; i < size; i++) {
1549052e4e8SMasami Hiramatsu 		struct fprobe_rethook_node *node;
1555b0ab789SMasami Hiramatsu 
156*76d0de57SMasami Hiramatsu (Google) 		node = kzalloc(sizeof(*node) + fp->entry_data_size, GFP_KERNEL);
1575b0ab789SMasami Hiramatsu 		if (!node) {
1585b0ab789SMasami Hiramatsu 			rethook_free(fp->rethook);
1595b0ab789SMasami Hiramatsu 			fp->rethook = NULL;
1605b0ab789SMasami Hiramatsu 			return -ENOMEM;
1615b0ab789SMasami Hiramatsu 		}
1629052e4e8SMasami Hiramatsu 		rethook_add_node(fp->rethook, &node->node);
1635b0ab789SMasami Hiramatsu 	}
1645b0ab789SMasami Hiramatsu 	return 0;
1655b0ab789SMasami Hiramatsu }
1665b0ab789SMasami Hiramatsu 
1675b0ab789SMasami Hiramatsu static void fprobe_fail_cleanup(struct fprobe *fp)
1685b0ab789SMasami Hiramatsu {
1695b0ab789SMasami Hiramatsu 	if (fp->rethook) {
1705b0ab789SMasami Hiramatsu 		/* Don't need to cleanup rethook->handler because this is not used. */
1715b0ab789SMasami Hiramatsu 		rethook_free(fp->rethook);
1725b0ab789SMasami Hiramatsu 		fp->rethook = NULL;
1735b0ab789SMasami Hiramatsu 	}
1745b0ab789SMasami Hiramatsu 	ftrace_free_filter(&fp->ops);
1755b0ab789SMasami Hiramatsu }
1765b0ab789SMasami Hiramatsu 
177cad9931fSMasami Hiramatsu /**
178cad9931fSMasami Hiramatsu  * register_fprobe() - Register fprobe to ftrace by pattern.
179cad9931fSMasami Hiramatsu  * @fp: A fprobe data structure to be registered.
180cad9931fSMasami Hiramatsu  * @filter: A wildcard pattern of probed symbols.
181cad9931fSMasami Hiramatsu  * @notfilter: A wildcard pattern of NOT probed symbols.
182cad9931fSMasami Hiramatsu  *
183cad9931fSMasami Hiramatsu  * Register @fp to ftrace for enabling the probe on the symbols matched to @filter.
184cad9931fSMasami Hiramatsu  * If @notfilter is not NULL, the symbols matched the @notfilter are not probed.
185cad9931fSMasami Hiramatsu  *
186cad9931fSMasami Hiramatsu  * Return 0 if @fp is registered successfully, -errno if not.
187cad9931fSMasami Hiramatsu  */
188cad9931fSMasami Hiramatsu int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter)
189cad9931fSMasami Hiramatsu {
1905b0ab789SMasami Hiramatsu 	struct ftrace_hash *hash;
191cad9931fSMasami Hiramatsu 	unsigned char *str;
192cad9931fSMasami Hiramatsu 	int ret, len;
193cad9931fSMasami Hiramatsu 
194cad9931fSMasami Hiramatsu 	if (!fp || !filter)
195cad9931fSMasami Hiramatsu 		return -EINVAL;
196cad9931fSMasami Hiramatsu 
197cad9931fSMasami Hiramatsu 	fprobe_init(fp);
198cad9931fSMasami Hiramatsu 
199cad9931fSMasami Hiramatsu 	len = strlen(filter);
200cad9931fSMasami Hiramatsu 	str = kstrdup(filter, GFP_KERNEL);
201cad9931fSMasami Hiramatsu 	ret = ftrace_set_filter(&fp->ops, str, len, 0);
202cad9931fSMasami Hiramatsu 	kfree(str);
203cad9931fSMasami Hiramatsu 	if (ret)
204cad9931fSMasami Hiramatsu 		return ret;
205cad9931fSMasami Hiramatsu 
206cad9931fSMasami Hiramatsu 	if (notfilter) {
207cad9931fSMasami Hiramatsu 		len = strlen(notfilter);
208cad9931fSMasami Hiramatsu 		str = kstrdup(notfilter, GFP_KERNEL);
209cad9931fSMasami Hiramatsu 		ret = ftrace_set_notrace(&fp->ops, str, len, 0);
210cad9931fSMasami Hiramatsu 		kfree(str);
211cad9931fSMasami Hiramatsu 		if (ret)
212cad9931fSMasami Hiramatsu 			goto out;
213cad9931fSMasami Hiramatsu 	}
214cad9931fSMasami Hiramatsu 
2155b0ab789SMasami Hiramatsu 	/* TODO:
2165b0ab789SMasami Hiramatsu 	 * correctly calculate the total number of filtered symbols
2175b0ab789SMasami Hiramatsu 	 * from both filter and notfilter.
2185b0ab789SMasami Hiramatsu 	 */
219261608f3SMasami Hiramatsu 	hash = rcu_access_pointer(fp->ops.local_hash.filter_hash);
2205b0ab789SMasami Hiramatsu 	if (WARN_ON_ONCE(!hash))
2215b0ab789SMasami Hiramatsu 		goto out;
2225b0ab789SMasami Hiramatsu 
2235b0ab789SMasami Hiramatsu 	ret = fprobe_init_rethook(fp, (int)hash->count);
2245b0ab789SMasami Hiramatsu 	if (!ret)
225cad9931fSMasami Hiramatsu 		ret = register_ftrace_function(&fp->ops);
2265b0ab789SMasami Hiramatsu 
227cad9931fSMasami Hiramatsu out:
228cad9931fSMasami Hiramatsu 	if (ret)
2295b0ab789SMasami Hiramatsu 		fprobe_fail_cleanup(fp);
230cad9931fSMasami Hiramatsu 	return ret;
231cad9931fSMasami Hiramatsu }
232cad9931fSMasami Hiramatsu EXPORT_SYMBOL_GPL(register_fprobe);
233cad9931fSMasami Hiramatsu 
234cad9931fSMasami Hiramatsu /**
235cad9931fSMasami Hiramatsu  * register_fprobe_ips() - Register fprobe to ftrace by address.
236cad9931fSMasami Hiramatsu  * @fp: A fprobe data structure to be registered.
237cad9931fSMasami Hiramatsu  * @addrs: An array of target ftrace location addresses.
238cad9931fSMasami Hiramatsu  * @num: The number of entries of @addrs.
239cad9931fSMasami Hiramatsu  *
240cad9931fSMasami Hiramatsu  * Register @fp to ftrace for enabling the probe on the address given by @addrs.
241cad9931fSMasami Hiramatsu  * The @addrs must be the addresses of ftrace location address, which may be
242cad9931fSMasami Hiramatsu  * the symbol address + arch-dependent offset.
243cad9931fSMasami Hiramatsu  * If you unsure what this mean, please use other registration functions.
244cad9931fSMasami Hiramatsu  *
245cad9931fSMasami Hiramatsu  * Return 0 if @fp is registered successfully, -errno if not.
246cad9931fSMasami Hiramatsu  */
247cad9931fSMasami Hiramatsu int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num)
248cad9931fSMasami Hiramatsu {
249cad9931fSMasami Hiramatsu 	int ret;
250cad9931fSMasami Hiramatsu 
251cad9931fSMasami Hiramatsu 	if (!fp || !addrs || num <= 0)
252cad9931fSMasami Hiramatsu 		return -EINVAL;
253cad9931fSMasami Hiramatsu 
254cad9931fSMasami Hiramatsu 	fprobe_init(fp);
255cad9931fSMasami Hiramatsu 
256cad9931fSMasami Hiramatsu 	ret = ftrace_set_filter_ips(&fp->ops, addrs, num, 0, 0);
2575b0ab789SMasami Hiramatsu 	if (ret)
2585b0ab789SMasami Hiramatsu 		return ret;
2595b0ab789SMasami Hiramatsu 
2605b0ab789SMasami Hiramatsu 	ret = fprobe_init_rethook(fp, num);
261cad9931fSMasami Hiramatsu 	if (!ret)
262cad9931fSMasami Hiramatsu 		ret = register_ftrace_function(&fp->ops);
263cad9931fSMasami Hiramatsu 
264cad9931fSMasami Hiramatsu 	if (ret)
2655b0ab789SMasami Hiramatsu 		fprobe_fail_cleanup(fp);
266cad9931fSMasami Hiramatsu 	return ret;
267cad9931fSMasami Hiramatsu }
268cad9931fSMasami Hiramatsu EXPORT_SYMBOL_GPL(register_fprobe_ips);
269cad9931fSMasami Hiramatsu 
270cad9931fSMasami Hiramatsu /**
271cad9931fSMasami Hiramatsu  * register_fprobe_syms() - Register fprobe to ftrace by symbols.
272cad9931fSMasami Hiramatsu  * @fp: A fprobe data structure to be registered.
273cad9931fSMasami Hiramatsu  * @syms: An array of target symbols.
274cad9931fSMasami Hiramatsu  * @num: The number of entries of @syms.
275cad9931fSMasami Hiramatsu  *
276cad9931fSMasami Hiramatsu  * Register @fp to the symbols given by @syms array. This will be useful if
277cad9931fSMasami Hiramatsu  * you are sure the symbols exist in the kernel.
278cad9931fSMasami Hiramatsu  *
279cad9931fSMasami Hiramatsu  * Return 0 if @fp is registered successfully, -errno if not.
280cad9931fSMasami Hiramatsu  */
281cad9931fSMasami Hiramatsu int register_fprobe_syms(struct fprobe *fp, const char **syms, int num)
282cad9931fSMasami Hiramatsu {
283cad9931fSMasami Hiramatsu 	unsigned long *addrs;
284cad9931fSMasami Hiramatsu 	int ret;
285cad9931fSMasami Hiramatsu 
286cad9931fSMasami Hiramatsu 	if (!fp || !syms || num <= 0)
287cad9931fSMasami Hiramatsu 		return -EINVAL;
288cad9931fSMasami Hiramatsu 
289cad9931fSMasami Hiramatsu 	addrs = get_ftrace_locations(syms, num);
290cad9931fSMasami Hiramatsu 	if (IS_ERR(addrs))
291cad9931fSMasami Hiramatsu 		return PTR_ERR(addrs);
292cad9931fSMasami Hiramatsu 
293cad9931fSMasami Hiramatsu 	ret = register_fprobe_ips(fp, addrs, num);
294cad9931fSMasami Hiramatsu 
295cad9931fSMasami Hiramatsu 	kfree(addrs);
296cad9931fSMasami Hiramatsu 
297cad9931fSMasami Hiramatsu 	return ret;
298cad9931fSMasami Hiramatsu }
299cad9931fSMasami Hiramatsu EXPORT_SYMBOL_GPL(register_fprobe_syms);
300cad9931fSMasami Hiramatsu 
301cad9931fSMasami Hiramatsu /**
302cad9931fSMasami Hiramatsu  * unregister_fprobe() - Unregister fprobe from ftrace
303cad9931fSMasami Hiramatsu  * @fp: A fprobe data structure to be unregistered.
304cad9931fSMasami Hiramatsu  *
305cad9931fSMasami Hiramatsu  * Unregister fprobe (and remove ftrace hooks from the function entries).
306cad9931fSMasami Hiramatsu  *
307cad9931fSMasami Hiramatsu  * Return 0 if @fp is unregistered successfully, -errno if not.
308cad9931fSMasami Hiramatsu  */
309cad9931fSMasami Hiramatsu int unregister_fprobe(struct fprobe *fp)
310cad9931fSMasami Hiramatsu {
311cad9931fSMasami Hiramatsu 	int ret;
312cad9931fSMasami Hiramatsu 
31361b304b7SMasami Hiramatsu (Google) 	if (!fp || (fp->ops.saved_func != fprobe_handler &&
31461b304b7SMasami Hiramatsu (Google) 		    fp->ops.saved_func != fprobe_kprobe_handler))
315cad9931fSMasami Hiramatsu 		return -EINVAL;
316cad9931fSMasami Hiramatsu 
3175b0ab789SMasami Hiramatsu 	/*
3185b0ab789SMasami Hiramatsu 	 * rethook_free() starts disabling the rethook, but the rethook handlers
3195b0ab789SMasami Hiramatsu 	 * may be running on other processors at this point. To make sure that all
3205b0ab789SMasami Hiramatsu 	 * current running handlers are finished, call unregister_ftrace_function()
3215b0ab789SMasami Hiramatsu 	 * after this.
3225b0ab789SMasami Hiramatsu 	 */
3235b0ab789SMasami Hiramatsu 	if (fp->rethook)
3245b0ab789SMasami Hiramatsu 		rethook_free(fp->rethook);
325cad9931fSMasami Hiramatsu 
3265b0ab789SMasami Hiramatsu 	ret = unregister_ftrace_function(&fp->ops);
3275b0ab789SMasami Hiramatsu 	if (ret < 0)
3285b0ab789SMasami Hiramatsu 		return ret;
3295b0ab789SMasami Hiramatsu 
330cad9931fSMasami Hiramatsu 	ftrace_free_filter(&fp->ops);
331cad9931fSMasami Hiramatsu 
332cad9931fSMasami Hiramatsu 	return ret;
333cad9931fSMasami Hiramatsu }
334cad9931fSMasami Hiramatsu EXPORT_SYMBOL_GPL(unregister_fprobe);
335