Skip to content

Commit dd7c983

Browse files
committed
csky/ftrace: Fixup ftrace_modify_code deadlock without CPU_HAS_ICACHE_INS
If ICACHE_INS is not supported, we use IPI to sync icache on each core. But ftrace_modify_code is called from stop_machine from default implementation of arch_ftrace_update_code and stop_machine callback is irq_disabled. When you call ipi with irq_disabled, a deadlock will happen. We couldn't use icache_flush with irq_disabled, but startup make_nop is specific case and it needn't ipi other cores. Signed-off-by: Guo Ren <[email protected]>
1 parent 89a3927 commit dd7c983

File tree

2 files changed

+70
-6
lines changed

2 files changed

+70
-6
lines changed

arch/csky/kernel/ftrace.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <linux/ftrace.h>
55
#include <linux/uaccess.h>
6+
#include <linux/stop_machine.h>
67
#include <asm/cacheflush.h>
78

89
#ifdef CONFIG_DYNAMIC_FTRACE
@@ -201,5 +202,35 @@ int ftrace_disable_ftrace_graph_caller(void)
201202
#endif /* CONFIG_DYNAMIC_FTRACE */
202203
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
203204

205+
#ifndef CONFIG_CPU_HAS_ICACHE_INS
206+
struct ftrace_modify_param {
207+
int command;
208+
atomic_t cpu_count;
209+
};
210+
211+
static int __ftrace_modify_code(void *data)
212+
{
213+
struct ftrace_modify_param *param = data;
214+
215+
if (atomic_inc_return(&param->cpu_count) == 1) {
216+
ftrace_modify_all_code(param->command);
217+
atomic_inc(&param->cpu_count);
218+
} else {
219+
while (atomic_read(&param->cpu_count) <= num_online_cpus())
220+
cpu_relax();
221+
local_icache_inv_all(NULL);
222+
}
223+
224+
return 0;
225+
}
226+
227+
void arch_ftrace_update_code(int command)
228+
{
229+
struct ftrace_modify_param param = { command, ATOMIC_INIT(0) };
230+
231+
stop_machine(__ftrace_modify_code, &param, cpu_online_mask);
232+
}
233+
#endif
234+
204235
/* _mcount is defined in abi's mcount.S */
205236
EXPORT_SYMBOL(_mcount);

arch/csky/mm/cachev2.c

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,19 @@
77
#include <asm/cache.h>
88
#include <asm/barrier.h>
99

10+
/* for L1-cache */
1011
#define INS_CACHE (1 << 0)
12+
#define DATA_CACHE (1 << 1)
1113
#define CACHE_INV (1 << 4)
14+
#define CACHE_CLR (1 << 5)
15+
#define CACHE_OMS (1 << 6)
1216

1317
void local_icache_inv_all(void *priv)
1418
{
1519
mtcr("cr17", INS_CACHE|CACHE_INV);
1620
sync_is();
1721
}
1822

19-
void icache_inv_all(void)
20-
{
21-
on_each_cpu(local_icache_inv_all, NULL, 1);
22-
}
23-
2423
#ifdef CONFIG_CPU_HAS_ICACHE_INS
2524
void icache_inv_range(unsigned long start, unsigned long end)
2625
{
@@ -31,9 +30,43 @@ void icache_inv_range(unsigned long start, unsigned long end)
3130
sync_is();
3231
}
3332
#else
33+
struct cache_range {
34+
unsigned long start;
35+
unsigned long end;
36+
};
37+
38+
static DEFINE_SPINLOCK(cache_lock);
39+
40+
static inline void cache_op_line(unsigned long i, unsigned int val)
41+
{
42+
mtcr("cr22", i);
43+
mtcr("cr17", val);
44+
}
45+
46+
void local_icache_inv_range(void *priv)
47+
{
48+
struct cache_range *param = priv;
49+
unsigned long i = param->start & ~(L1_CACHE_BYTES - 1);
50+
unsigned long flags;
51+
52+
spin_lock_irqsave(&cache_lock, flags);
53+
54+
for (; i < param->end; i += L1_CACHE_BYTES)
55+
cache_op_line(i, INS_CACHE | CACHE_INV | CACHE_OMS);
56+
57+
spin_unlock_irqrestore(&cache_lock, flags);
58+
59+
sync_is();
60+
}
61+
3462
void icache_inv_range(unsigned long start, unsigned long end)
3563
{
36-
icache_inv_all();
64+
struct cache_range param = { start, end };
65+
66+
if (irqs_disabled())
67+
local_icache_inv_range(&param);
68+
else
69+
on_each_cpu(local_icache_inv_range, &param, 1);
3770
}
3871
#endif
3972

0 commit comments

Comments
 (0)