Skip to content

Commit 11e969b

Browse files
committed
xtensa: support coprocessors on SMP
Current coprocessor support on xtensa only works correctly on uniprocessor configurations. Make it work on SMP too and keep it lazy. Make coprocessor_owner array per-CPU and move it to struct exc_table for easy access from the fast_coprocessor exception handler. Allow task to have live coprocessors only on single CPU, record this CPU number in the struct thread_info::cp_owner_cpu. Change struct thread_info::cpenable meaning to be 'coprocessors live on cp_owner_cpu'. Introduce C-level coprocessor exception handler that flushes and releases live coprocessors of the task taking 'coprocessor disabled' exception and call it from the fast_coprocessor handler when the task has live coprocessors on other CPU. Make coprocessor_flush_all and coprocessor_release_all work correctly when called from any CPU by sending IPI to the cp_owner_cpu. Add function coprocessor_flush_release_all to do flush followed by release atomically. Add function local_coprocessors_flush_release_all to flush and release all coprocessors on the local CPU and use it to flush coprocessor contexts from the CPU that goes offline. Signed-off-by: Max Filippov <[email protected]>
1 parent f29cab2 commit 11e969b

File tree

11 files changed

+230
-67
lines changed

11 files changed

+230
-67
lines changed

arch/xtensa/include/asm/coprocessor.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,12 @@ typedef struct { XCHAL_CP6_SA_LIST(2) } xtregs_cp6_t
142142
typedef struct { XCHAL_CP7_SA_LIST(2) } xtregs_cp7_t
143143
__attribute__ ((aligned (XCHAL_CP7_SA_ALIGN)));
144144

145-
extern struct thread_info* coprocessor_owner[XCHAL_CP_MAX];
145+
struct thread_info;
146146
void coprocessor_flush(struct thread_info *ti, int cp_index);
147147
void coprocessor_release_all(struct thread_info *ti);
148148
void coprocessor_flush_all(struct thread_info *ti);
149+
void coprocessor_flush_release_all(struct thread_info *ti);
150+
void local_coprocessors_flush_release_all(void);
149151

150152
#endif /* XTENSA_HAVE_COPROCESSORS */
151153

arch/xtensa/include/asm/thread_info.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,17 @@ struct thread_info {
5252
__u32 cpu; /* current CPU */
5353
__s32 preempt_count; /* 0 => preemptable,< 0 => BUG*/
5454

55-
unsigned long cpenable;
5655
#if XCHAL_HAVE_EXCLUSIVE
5756
/* result of the most recent exclusive store */
5857
unsigned long atomctl8;
5958
#endif
6059

60+
/*
61+
* If i-th bit is set then coprocessor state is loaded into the
62+
* coprocessor i on CPU cp_owner_cpu.
63+
*/
64+
unsigned long cpenable;
65+
u32 cp_owner_cpu;
6166
/* Allocate storage for extra user states and coprocessor states. */
6267
#if XTENSA_HAVE_COPROCESSORS
6368
xtregs_coprocessor_t xtregs_cp;

arch/xtensa/include/asm/traps.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ struct exc_table {
2727
void *fixup;
2828
/* For passing a parameter to fixup */
2929
void *fixup_param;
30+
#if XTENSA_HAVE_COPROCESSORS
31+
/* Pointers to owner struct thread_info */
32+
struct thread_info *coprocessor_owner[XCHAL_CP_MAX];
33+
#endif
3034
/* Fast user exception handlers */
3135
void *fast_user_handler[EXCCAUSE_N];
3236
/* Fast kernel exception handlers */
@@ -35,6 +39,8 @@ struct exc_table {
3539
xtensa_exception_handler *default_handler[EXCCAUSE_N];
3640
};
3741

42+
DECLARE_PER_CPU(struct exc_table, exc_table);
43+
3844
xtensa_exception_handler *
3945
__init trap_set_handler(int cause, xtensa_exception_handler *handler);
4046

arch/xtensa/kernel/asm-offsets.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,12 @@ int main(void)
9191
/* struct thread_info (offset from start_struct) */
9292
DEFINE(THREAD_RA, offsetof (struct task_struct, thread.ra));
9393
DEFINE(THREAD_SP, offsetof (struct task_struct, thread.sp));
94-
DEFINE(THREAD_CPENABLE, offsetof (struct thread_info, cpenable));
9594
#if XCHAL_HAVE_EXCLUSIVE
9695
DEFINE(THREAD_ATOMCTL8, offsetof (struct thread_info, atomctl8));
9796
#endif
97+
DEFINE(THREAD_CPENABLE, offsetof(struct thread_info, cpenable));
98+
DEFINE(THREAD_CPU, offsetof(struct thread_info, cpu));
99+
DEFINE(THREAD_CP_OWNER_CPU, offsetof(struct thread_info, cp_owner_cpu));
98100
#if XTENSA_HAVE_COPROCESSORS
99101
DEFINE(THREAD_XTREGS_CP0, offsetof(struct thread_info, xtregs_cp.cp0));
100102
DEFINE(THREAD_XTREGS_CP1, offsetof(struct thread_info, xtregs_cp.cp1));
@@ -137,6 +139,10 @@ int main(void)
137139
DEFINE(EXC_TABLE_DOUBLE_SAVE, offsetof(struct exc_table, double_save));
138140
DEFINE(EXC_TABLE_FIXUP, offsetof(struct exc_table, fixup));
139141
DEFINE(EXC_TABLE_PARAM, offsetof(struct exc_table, fixup_param));
142+
#if XTENSA_HAVE_COPROCESSORS
143+
DEFINE(EXC_TABLE_COPROCESSOR_OWNER,
144+
offsetof(struct exc_table, coprocessor_owner));
145+
#endif
140146
DEFINE(EXC_TABLE_FAST_USER,
141147
offsetof(struct exc_table, fast_user_handler));
142148
DEFINE(EXC_TABLE_FAST_KERNEL,

arch/xtensa/kernel/coprocessor.S

Lines changed: 90 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,26 @@
1919
#include <asm/current.h>
2020
#include <asm/regs.h>
2121

22+
/*
23+
* Rules for coprocessor state manipulation on SMP:
24+
*
25+
* - a task may have live coprocessors only on one CPU.
26+
*
27+
* - whether coprocessor context of task T is live on some CPU is
28+
* denoted by T's thread_info->cpenable.
29+
*
30+
* - non-zero thread_info->cpenable means that thread_info->cp_owner_cpu
31+
* is valid in the T's thread_info. Zero thread_info->cpenable means that
32+
* coprocessor context is valid in the T's thread_info.
33+
*
34+
* - if a coprocessor context of task T is live on CPU X, only CPU X changes
35+
* T's thread_info->cpenable, cp_owner_cpu and coprocessor save area.
36+
* This is done by making sure that for the task T with live coprocessor
37+
* on CPU X cpenable SR is 0 when T runs on any other CPU Y.
38+
* When fast_coprocessor exception is taken on CPU Y it goes to the
39+
* C-level do_coprocessor that uses IPI to make CPU X flush T's coprocessors.
40+
*/
41+
2242
#if XTENSA_HAVE_COPROCESSORS
2343

2444
/*
@@ -101,9 +121,37 @@
101121

102122
ENTRY(fast_coprocessor)
103123

124+
s32i a3, a2, PT_AREG3
125+
126+
#ifdef CONFIG_SMP
127+
/*
128+
* Check if any coprocessor context is live on another CPU
129+
* and if so go through the C-level coprocessor exception handler
130+
* to flush it to memory.
131+
*/
132+
GET_THREAD_INFO (a0, a2)
133+
l32i a3, a0, THREAD_CPENABLE
134+
beqz a3, .Lload_local
135+
136+
/*
137+
* Pairs with smp_wmb in local_coprocessor_release_all
138+
* and with both memws below.
139+
*/
140+
memw
141+
l32i a3, a0, THREAD_CPU
142+
l32i a0, a0, THREAD_CP_OWNER_CPU
143+
beq a0, a3, .Lload_local
144+
145+
rsr a0, ps
146+
l32i a3, a2, PT_AREG3
147+
bbci.l a0, PS_UM_BIT, 1f
148+
call0 user_exception
149+
1: call0 kernel_exception
150+
#endif
151+
104152
/* Save remaining registers a1-a3 and SAR */
105153

106-
s32i a3, a2, PT_AREG3
154+
.Lload_local:
107155
rsr a3, sar
108156
s32i a1, a2, PT_AREG1
109157
s32i a3, a2, PT_SAR
@@ -117,6 +165,9 @@ ENTRY(fast_coprocessor)
117165
s32i a5, a1, PT_AREG5
118166
s32i a6, a1, PT_AREG6
119167
s32i a7, a1, PT_AREG7
168+
s32i a8, a1, PT_AREG8
169+
s32i a9, a1, PT_AREG9
170+
s32i a10, a1, PT_AREG10
120171

121172
/* Find coprocessor number. Subtract first CP EXCCAUSE from EXCCAUSE */
122173

@@ -139,51 +190,66 @@ ENTRY(fast_coprocessor)
139190
addx8 a7, a3, a7
140191
addx4 a7, a3, a7
141192

142-
/* Retrieve previous owner. (a3 still holds CP number) */
193+
/* Retrieve previous owner (a8). */
143194

144-
movi a0, coprocessor_owner # list of owners
195+
rsr a0, excsave1 # exc_table
145196
addx4 a0, a3, a0 # entry for CP
146-
l32i a4, a0, 0
197+
l32i a8, a0, EXC_TABLE_COPROCESSOR_OWNER
147198

148-
beqz a4, 1f # skip 'save' if no previous owner
199+
/* Set new owner (a9). */
149200

150-
/* Disable coprocessor for previous owner. (a2 = 1 << CP number) */
201+
GET_THREAD_INFO (a9, a1)
202+
l32i a4, a9, THREAD_CPU
203+
s32i a9, a0, EXC_TABLE_COPROCESSOR_OWNER
204+
s32i a4, a9, THREAD_CP_OWNER_CPU
151205

152-
l32i a5, a4, THREAD_CPENABLE
153-
xor a5, a5, a2 # (1 << cp-id) still in a2
154-
s32i a5, a4, THREAD_CPENABLE
206+
/*
207+
* Enable coprocessor for the new owner. (a2 = 1 << CP number)
208+
* This can be done before loading context into the coprocessor.
209+
*/
210+
l32i a4, a9, THREAD_CPENABLE
211+
or a4, a4, a2
155212

156213
/*
157-
* Get context save area and call save routine.
158-
* (a4 still holds previous owner (thread_info), a3 CP number)
214+
* Make sure THREAD_CP_OWNER_CPU is in memory before updating
215+
* THREAD_CPENABLE
159216
*/
217+
memw # (2)
218+
s32i a4, a9, THREAD_CPENABLE
160219

161-
l32i a2, a7, CP_REGS_TAB_OFFSET
162-
l32i a3, a7, CP_REGS_TAB_SAVE
163-
add a2, a2, a4
164-
callx0 a3
220+
beqz a8, 1f # skip 'save' if no previous owner
165221

166-
/* Note that only a0 and a1 were preserved. */
222+
/* Disable coprocessor for previous owner. (a2 = 1 << CP number) */
167223

168-
rsr a3, exccause
169-
addi a3, a3, -EXCCAUSE_COPROCESSOR0_DISABLED
170-
movi a0, coprocessor_owner
171-
addx4 a0, a3, a0
224+
l32i a10, a8, THREAD_CPENABLE
225+
xor a10, a10, a2
172226

173-
/* Set new 'owner' (a0 points to the CP owner, a3 contains the CP nr) */
227+
/* Get context save area and call save routine. */
174228

175-
1: GET_THREAD_INFO (a4, a1)
176-
s32i a4, a0, 0
229+
l32i a2, a7, CP_REGS_TAB_OFFSET
230+
l32i a3, a7, CP_REGS_TAB_SAVE
231+
add a2, a2, a8
232+
callx0 a3
177233

234+
/*
235+
* Make sure coprocessor context and THREAD_CP_OWNER_CPU are in memory
236+
* before updating THREAD_CPENABLE
237+
*/
238+
memw # (3)
239+
s32i a10, a8, THREAD_CPENABLE
240+
1:
178241
/* Get context save area and call load routine. */
179242

180243
l32i a2, a7, CP_REGS_TAB_OFFSET
181244
l32i a3, a7, CP_REGS_TAB_LOAD
182-
add a2, a2, a4
245+
add a2, a2, a9
183246
callx0 a3
184247

185248
/* Restore all registers and return from exception handler. */
186249

250+
l32i a10, a1, PT_AREG10
251+
l32i a9, a1, PT_AREG9
252+
l32i a8, a1, PT_AREG8
187253
l32i a7, a1, PT_AREG7
188254
l32i a6, a1, PT_AREG6
189255
l32i a5, a1, PT_AREG5
@@ -233,12 +299,4 @@ ENTRY(coprocessor_flush)
233299

234300
ENDPROC(coprocessor_flush)
235301

236-
.data
237-
238-
ENTRY(coprocessor_owner)
239-
240-
.fill XCHAL_CP_MAX, 4, 0
241-
242-
END(coprocessor_owner)
243-
244302
#endif /* XTENSA_HAVE_COPROCESSORS */

arch/xtensa/kernel/entry.S

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,8 +2071,16 @@ ENTRY(_switch_to)
20712071

20722072
#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
20732073
l32i a3, a5, THREAD_CPENABLE
2074-
xsr a3, cpenable
2075-
s32i a3, a4, THREAD_CPENABLE
2074+
#ifdef CONFIG_SMP
2075+
beqz a3, 1f
2076+
memw # pairs with memw (2) in fast_coprocessor
2077+
l32i a6, a5, THREAD_CP_OWNER_CPU
2078+
l32i a7, a5, THREAD_CPU
2079+
beq a6, a7, 1f # load 0 into CPENABLE if current CPU is not the owner
2080+
movi a3, 0
2081+
1:
2082+
#endif
2083+
wsr a3, cpenable
20762084
#endif
20772085

20782086
#if XCHAL_HAVE_EXCLUSIVE

0 commit comments

Comments
 (0)