85
85
* softirq kicks in. Upon vcpu_put(), KVM will save the vcpu FP state and
86
86
* flag the register state as invalid.
87
87
*
88
- * In order to allow softirq handlers to use FPSIMD, kernel_neon_begin() may
89
- * save the task's FPSIMD context back to task_struct from softirq context.
90
- * To prevent this from racing with the manipulation of the task's FPSIMD state
91
- * from task context and thereby corrupting the state, it is necessary to
92
- * protect any manipulation of a task's fpsimd_state or TIF_FOREIGN_FPSTATE
93
- * flag with {, __} get_cpu_fpsimd_context(). This will still allow softirqs to
94
- * run but prevent them to use FPSIMD .
88
+ * In order to allow softirq handlers to use FPSIMD, kernel_neon_begin() may be
89
+ * called from softirq context, which will save the task's FPSIMD context back
90
+ * to task_struct. To prevent this from racing with the manipulation of the
91
+ * task's FPSIMD state from task context and thereby corrupting the state, it
92
+ * is necessary to protect any manipulation of a task's fpsimd_state or
93
+ * TIF_FOREIGN_FPSTATE flag with get_cpu_fpsimd_context(), which will suspend
94
+ * softirq servicing entirely until put_cpu_fpsimd_context() is called .
95
95
*
96
96
* For a certain task, the sequence may look something like this:
97
97
* - the task gets scheduled in; if both the task's fpsimd_cpu field
@@ -209,27 +209,14 @@ static inline void sme_free(struct task_struct *t) { }
209
209
210
210
#endif
211
211
212
- DEFINE_PER_CPU (bool , fpsimd_context_busy );
213
- EXPORT_PER_CPU_SYMBOL (fpsimd_context_busy );
214
-
215
212
static void fpsimd_bind_task_to_cpu (void );
216
213
217
- static void __get_cpu_fpsimd_context (void )
218
- {
219
- bool busy = __this_cpu_xchg (fpsimd_context_busy , true);
220
-
221
- WARN_ON (busy );
222
- }
223
-
224
214
/*
225
215
* Claim ownership of the CPU FPSIMD context for use by the calling context.
226
216
*
227
217
* The caller may freely manipulate the FPSIMD context metadata until
228
218
* put_cpu_fpsimd_context() is called.
229
219
*
230
- * The double-underscore version must only be called if you know the task
231
- * can't be preempted.
232
- *
233
220
* On RT kernels local_bh_disable() is not sufficient because it only
234
221
* serializes soft interrupt related sections via a local lock, but stays
235
222
* preemptible. Disabling preemption is the right choice here as bottom
@@ -242,14 +229,6 @@ static void get_cpu_fpsimd_context(void)
242
229
local_bh_disable ();
243
230
else
244
231
preempt_disable ();
245
- __get_cpu_fpsimd_context ();
246
- }
247
-
248
- static void __put_cpu_fpsimd_context (void )
249
- {
250
- bool busy = __this_cpu_xchg (fpsimd_context_busy , false);
251
-
252
- WARN_ON (!busy ); /* No matching get_cpu_fpsimd_context()? */
253
232
}
254
233
255
234
/*
@@ -261,18 +240,12 @@ static void __put_cpu_fpsimd_context(void)
261
240
*/
262
241
static void put_cpu_fpsimd_context (void )
263
242
{
264
- __put_cpu_fpsimd_context ();
265
243
if (!IS_ENABLED (CONFIG_PREEMPT_RT ))
266
244
local_bh_enable ();
267
245
else
268
246
preempt_enable ();
269
247
}
270
248
271
- static bool have_cpu_fpsimd_context (void )
272
- {
273
- return !preemptible () && __this_cpu_read (fpsimd_context_busy );
274
- }
275
-
276
249
unsigned int task_get_vl (const struct task_struct * task , enum vec_type type )
277
250
{
278
251
return task -> thread .vl [type ];
@@ -383,7 +356,8 @@ static void task_fpsimd_load(void)
383
356
bool restore_ffr ;
384
357
385
358
WARN_ON (!system_supports_fpsimd ());
386
- WARN_ON (!have_cpu_fpsimd_context ());
359
+ WARN_ON (preemptible ());
360
+ WARN_ON (test_thread_flag (TIF_KERNEL_FPSTATE ));
387
361
388
362
if (system_supports_sve () || system_supports_sme ()) {
389
363
switch (current -> thread .fp_type ) {
@@ -406,7 +380,7 @@ static void task_fpsimd_load(void)
406
380
default :
407
381
/*
408
382
* This indicates either a bug in
409
- * fpsimd_save () or memory corruption, we
383
+ * fpsimd_save_user_state () or memory corruption, we
410
384
* should always record an explicit format
411
385
* when we save. We always at least have the
412
386
* memory allocated for FPSMID registers so
@@ -457,7 +431,7 @@ static void task_fpsimd_load(void)
457
431
* than via current, if we are saving KVM state then it will have
458
432
* ensured that the type of registers to save is set in last->to_save.
459
433
*/
460
- static void fpsimd_save (void )
434
+ static void fpsimd_save_user_state (void )
461
435
{
462
436
struct cpu_fp_state const * last =
463
437
this_cpu_ptr (& fpsimd_last_state );
@@ -467,7 +441,7 @@ static void fpsimd_save(void)
467
441
unsigned int vl ;
468
442
469
443
WARN_ON (!system_supports_fpsimd ());
470
- WARN_ON (! have_cpu_fpsimd_context ());
444
+ WARN_ON (preemptible ());
471
445
472
446
if (test_thread_flag (TIF_FOREIGN_FPSTATE ))
473
447
return ;
@@ -888,7 +862,7 @@ int vec_set_vector_length(struct task_struct *task, enum vec_type type,
888
862
if (task == current ) {
889
863
get_cpu_fpsimd_context ();
890
864
891
- fpsimd_save ();
865
+ fpsimd_save_user_state ();
892
866
}
893
867
894
868
fpsimd_flush_task_state (task );
@@ -1500,31 +1474,66 @@ void do_fpsimd_exc(unsigned long esr, struct pt_regs *regs)
1500
1474
current );
1501
1475
}
1502
1476
1477
+ static void fpsimd_load_kernel_state (struct task_struct * task )
1478
+ {
1479
+ struct cpu_fp_state * last = this_cpu_ptr (& fpsimd_last_state );
1480
+
1481
+ /*
1482
+ * Elide the load if this CPU holds the most recent kernel mode
1483
+ * FPSIMD context of the current task.
1484
+ */
1485
+ if (last -> st == & task -> thread .kernel_fpsimd_state &&
1486
+ task -> thread .kernel_fpsimd_cpu == smp_processor_id ())
1487
+ return ;
1488
+
1489
+ fpsimd_load_state (& task -> thread .kernel_fpsimd_state );
1490
+ }
1491
+
1492
+ static void fpsimd_save_kernel_state (struct task_struct * task )
1493
+ {
1494
+ struct cpu_fp_state cpu_fp_state = {
1495
+ .st = & task -> thread .kernel_fpsimd_state ,
1496
+ .to_save = FP_STATE_FPSIMD ,
1497
+ };
1498
+
1499
+ fpsimd_save_state (& task -> thread .kernel_fpsimd_state );
1500
+ fpsimd_bind_state_to_cpu (& cpu_fp_state );
1501
+
1502
+ task -> thread .kernel_fpsimd_cpu = smp_processor_id ();
1503
+ }
1504
+
1503
1505
void fpsimd_thread_switch (struct task_struct * next )
1504
1506
{
1505
1507
bool wrong_task , wrong_cpu ;
1506
1508
1507
1509
if (!system_supports_fpsimd ())
1508
1510
return ;
1509
1511
1510
- __get_cpu_fpsimd_context ( );
1512
+ WARN_ON_ONCE (! irqs_disabled () );
1511
1513
1512
1514
/* Save unsaved fpsimd state, if any: */
1513
- fpsimd_save ();
1514
-
1515
- /*
1516
- * Fix up TIF_FOREIGN_FPSTATE to correctly describe next's
1517
- * state. For kernel threads, FPSIMD registers are never loaded
1518
- * and wrong_task and wrong_cpu will always be true.
1519
- */
1520
- wrong_task = __this_cpu_read (fpsimd_last_state .st ) !=
1521
- & next -> thread .uw .fpsimd_state ;
1522
- wrong_cpu = next -> thread .fpsimd_cpu != smp_processor_id ();
1515
+ if (test_thread_flag (TIF_KERNEL_FPSTATE ))
1516
+ fpsimd_save_kernel_state (current );
1517
+ else
1518
+ fpsimd_save_user_state ();
1523
1519
1524
- update_tsk_thread_flag (next , TIF_FOREIGN_FPSTATE ,
1525
- wrong_task || wrong_cpu );
1520
+ if (test_tsk_thread_flag (next , TIF_KERNEL_FPSTATE )) {
1521
+ fpsimd_load_kernel_state (next );
1522
+ set_tsk_thread_flag (next , TIF_FOREIGN_FPSTATE );
1523
+ } else {
1524
+ /*
1525
+ * Fix up TIF_FOREIGN_FPSTATE to correctly describe next's
1526
+ * state. For kernel threads, FPSIMD registers are never
1527
+ * loaded with user mode FPSIMD state and so wrong_task and
1528
+ * wrong_cpu will always be true.
1529
+ */
1530
+ wrong_task = __this_cpu_read (fpsimd_last_state .st ) !=
1531
+ & next -> thread .uw .fpsimd_state ;
1532
+ wrong_cpu = next -> thread .fpsimd_cpu != smp_processor_id ();
1526
1533
1527
- __put_cpu_fpsimd_context ();
1534
+ update_tsk_thread_flag (next , TIF_FOREIGN_FPSTATE ,
1535
+ wrong_task || wrong_cpu );
1536
+ }
1528
1537
}
1529
1538
1530
1539
static void fpsimd_flush_thread_vl (enum vec_type type )
@@ -1614,7 +1623,7 @@ void fpsimd_preserve_current_state(void)
1614
1623
return ;
1615
1624
1616
1625
get_cpu_fpsimd_context ();
1617
- fpsimd_save ();
1626
+ fpsimd_save_user_state ();
1618
1627
put_cpu_fpsimd_context ();
1619
1628
}
1620
1629
@@ -1826,13 +1835,15 @@ static void fpsimd_flush_cpu_state(void)
1826
1835
*/
1827
1836
void fpsimd_save_and_flush_cpu_state (void )
1828
1837
{
1838
+ unsigned long flags ;
1839
+
1829
1840
if (!system_supports_fpsimd ())
1830
1841
return ;
1831
1842
WARN_ON (preemptible ());
1832
- __get_cpu_fpsimd_context ( );
1833
- fpsimd_save ();
1843
+ local_irq_save ( flags );
1844
+ fpsimd_save_user_state ();
1834
1845
fpsimd_flush_cpu_state ();
1835
- __put_cpu_fpsimd_context ( );
1846
+ local_irq_restore ( flags );
1836
1847
}
1837
1848
1838
1849
#ifdef CONFIG_KERNEL_MODE_NEON
@@ -1864,10 +1875,37 @@ void kernel_neon_begin(void)
1864
1875
get_cpu_fpsimd_context ();
1865
1876
1866
1877
/* Save unsaved fpsimd state, if any: */
1867
- fpsimd_save ();
1878
+ if (test_thread_flag (TIF_KERNEL_FPSTATE )) {
1879
+ BUG_ON (IS_ENABLED (CONFIG_PREEMPT_RT ) || !in_serving_softirq ());
1880
+ fpsimd_save_kernel_state (current );
1881
+ } else {
1882
+ fpsimd_save_user_state ();
1883
+
1884
+ /*
1885
+ * Set the thread flag so that the kernel mode FPSIMD state
1886
+ * will be context switched along with the rest of the task
1887
+ * state.
1888
+ *
1889
+ * On non-PREEMPT_RT, softirqs may interrupt task level kernel
1890
+ * mode FPSIMD, but the task will not be preemptible so setting
1891
+ * TIF_KERNEL_FPSTATE for those would be both wrong (as it
1892
+ * would mark the task context FPSIMD state as requiring a
1893
+ * context switch) and unnecessary.
1894
+ *
1895
+ * On PREEMPT_RT, softirqs are serviced from a separate thread,
1896
+ * which is scheduled as usual, and this guarantees that these
1897
+ * softirqs are not interrupting use of the FPSIMD in kernel
1898
+ * mode in task context. So in this case, setting the flag here
1899
+ * is always appropriate.
1900
+ */
1901
+ if (IS_ENABLED (CONFIG_PREEMPT_RT ) || !in_serving_softirq ())
1902
+ set_thread_flag (TIF_KERNEL_FPSTATE );
1903
+ }
1868
1904
1869
1905
/* Invalidate any task state remaining in the fpsimd regs: */
1870
1906
fpsimd_flush_cpu_state ();
1907
+
1908
+ put_cpu_fpsimd_context ();
1871
1909
}
1872
1910
EXPORT_SYMBOL_GPL (kernel_neon_begin );
1873
1911
@@ -1885,7 +1923,16 @@ void kernel_neon_end(void)
1885
1923
if (!system_supports_fpsimd ())
1886
1924
return ;
1887
1925
1888
- put_cpu_fpsimd_context ();
1926
+ /*
1927
+ * If we are returning from a nested use of kernel mode FPSIMD, restore
1928
+ * the task context kernel mode FPSIMD state. This can only happen when
1929
+ * running in softirq context on non-PREEMPT_RT.
1930
+ */
1931
+ if (!IS_ENABLED (CONFIG_PREEMPT_RT ) && in_serving_softirq () &&
1932
+ test_thread_flag (TIF_KERNEL_FPSTATE ))
1933
+ fpsimd_load_kernel_state (current );
1934
+ else
1935
+ clear_thread_flag (TIF_KERNEL_FPSTATE );
1889
1936
}
1890
1937
EXPORT_SYMBOL_GPL (kernel_neon_end );
1891
1938
0 commit comments