@@ -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
4641static 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