Skip to content

Commit 24d4f52

Browse files
Youling Tangchenhuacai
authored andcommitted
LoongArch: ftrace: Implement ftrace_find_callable_addr() to simplify code
In the module processing functions, the same logic can be reused by implementing ftrace_find_callable_addr(). Signed-off-by: Youling Tang <tangyouling@loongson.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
1 parent 819cf65 commit 24d4f52

1 file changed

Lines changed: 57 additions & 59 deletions

File tree

arch/loongarch/kernel/ftrace_dyn.c

Lines changed: 57 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,11 @@ static int ftrace_modify_code(unsigned long pc, u32 old, u32 new, bool validate)
3131
}
3232

3333
#ifdef CONFIG_MODULES
34-
static inline int __get_mod(struct module **mod, unsigned long addr)
34+
static bool reachable_by_bl(unsigned long addr, unsigned long pc)
3535
{
36-
preempt_disable();
37-
*mod = __module_text_address(addr);
38-
preempt_enable();
36+
long offset = (long)addr - (long)pc;
3937

40-
if (WARN_ON(!(*mod)))
41-
return -EINVAL;
42-
43-
return 0;
38+
return offset >= -SZ_128M && offset < SZ_128M;
4439
}
4540

4641
static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr)
@@ -56,17 +51,58 @@ static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr)
5651
return NULL;
5752
}
5853

59-
static unsigned long get_plt_addr(struct module *mod, unsigned long addr)
54+
/*
55+
* Find the address the callsite must branch to in order to reach '*addr'.
56+
*
57+
* Due to the limited range of 'bl' instruction, modules may be placed too far
58+
* away to branch directly and we must use a PLT.
59+
*
60+
* Returns true when '*addr' contains a reachable target address, or has been
61+
* modified to contain a PLT address. Returns false otherwise.
62+
*/
63+
static bool ftrace_find_callable_addr(struct dyn_ftrace *rec, struct module *mod, unsigned long *addr)
6064
{
65+
unsigned long pc = rec->ip + LOONGARCH_INSN_SIZE;
6166
struct plt_entry *plt;
6267

63-
plt = get_ftrace_plt(mod, addr);
68+
/*
69+
* When the target is within range of the 'bl' instruction, use 'addr'
70+
* as-is and branch to that directly.
71+
*/
72+
if (reachable_by_bl(*addr, pc))
73+
return true;
74+
75+
/*
76+
* 'mod' is only set at module load time, but if we end up
77+
* dealing with an out-of-range condition, we can assume it
78+
* is due to a module being loaded far away from the kernel.
79+
*
80+
* NOTE: __module_text_address() must be called with preemption
81+
* disabled, but we can rely on ftrace_lock to ensure that 'mod'
82+
* retains its validity throughout the remainder of this code.
83+
*/
84+
if (!mod) {
85+
preempt_disable();
86+
mod = __module_text_address(pc);
87+
preempt_enable();
88+
}
89+
90+
if (WARN_ON(!mod))
91+
return false;
92+
93+
plt = get_ftrace_plt(mod, *addr);
6494
if (!plt) {
65-
pr_err("ftrace: no module PLT for %ps\n", (void *)addr);
66-
return -EINVAL;
95+
pr_err("ftrace: no module PLT for %ps\n", (void *)*addr);
96+
return false;
6797
}
6898

69-
return (unsigned long)plt;
99+
*addr = (unsigned long)plt;
100+
return true;
101+
}
102+
#else /* !CONFIG_MODULES */
103+
static bool ftrace_find_callable_addr(struct dyn_ftrace *rec, struct module *mod, unsigned long *addr)
104+
{
105+
return true;
70106
}
71107
#endif
72108

@@ -75,26 +111,14 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, unsigned
75111
{
76112
u32 old, new;
77113
unsigned long pc;
78-
long offset __maybe_unused;
79114

80115
pc = rec->ip + LOONGARCH_INSN_SIZE;
81116

82-
#ifdef CONFIG_MODULES
83-
offset = (long)pc - (long)addr;
84-
85-
if (offset < -SZ_128M || offset >= SZ_128M) {
86-
int ret;
87-
struct module *mod;
88-
89-
ret = __get_mod(&mod, pc);
90-
if (ret)
91-
return ret;
92-
93-
addr = get_plt_addr(mod, addr);
117+
if (!ftrace_find_callable_addr(rec, NULL, &addr))
118+
return -EINVAL;
94119

95-
old_addr = get_plt_addr(mod, old_addr);
96-
}
97-
#endif
120+
if (!ftrace_find_callable_addr(rec, NULL, &old_addr))
121+
return -EINVAL;
98122

99123
new = larch_insn_gen_bl(pc, addr);
100124
old = larch_insn_gen_bl(pc, old_addr);
@@ -151,24 +175,11 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
151175
{
152176
u32 old, new;
153177
unsigned long pc;
154-
long offset __maybe_unused;
155178

156179
pc = rec->ip + LOONGARCH_INSN_SIZE;
157180

158-
#ifdef CONFIG_MODULES
159-
offset = (long)pc - (long)addr;
160-
161-
if (offset < -SZ_128M || offset >= SZ_128M) {
162-
int ret;
163-
struct module *mod;
164-
165-
ret = __get_mod(&mod, pc);
166-
if (ret)
167-
return ret;
168-
169-
addr = get_plt_addr(mod, addr);
170-
}
171-
#endif
181+
if (!ftrace_find_callable_addr(rec, NULL, &addr))
182+
return -EINVAL;
172183

173184
old = larch_insn_gen_nop();
174185
new = larch_insn_gen_bl(pc, addr);
@@ -180,24 +191,11 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long ad
180191
{
181192
u32 old, new;
182193
unsigned long pc;
183-
long offset __maybe_unused;
184194

185195
pc = rec->ip + LOONGARCH_INSN_SIZE;
186196

187-
#ifdef CONFIG_MODULES
188-
offset = (long)pc - (long)addr;
189-
190-
if (offset < -SZ_128M || offset >= SZ_128M) {
191-
int ret;
192-
struct module *mod;
193-
194-
ret = __get_mod(&mod, pc);
195-
if (ret)
196-
return ret;
197-
198-
addr = get_plt_addr(mod, addr);
199-
}
200-
#endif
197+
if (!ftrace_find_callable_addr(rec, NULL, &addr))
198+
return -EINVAL;
201199

202200
new = larch_insn_gen_nop();
203201
old = larch_insn_gen_bl(pc, addr);

0 commit comments

Comments
 (0)