diff options
Diffstat (limited to 'arch/arm64/kernel/fpsimd.c')
| -rw-r--r-- | arch/arm64/kernel/fpsimd.c | 139 |
1 files changed, 96 insertions, 43 deletions
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c index 0cfcf5c237c5..eec4776ae5f0 100644 --- a/arch/arm64/kernel/fpsimd.c +++ b/arch/arm64/kernel/fpsimd.c | |||
| @@ -82,7 +82,8 @@ | |||
| 82 | * To prevent this from racing with the manipulation of the task's FPSIMD state | 82 | * To prevent this from racing with the manipulation of the task's FPSIMD state |
| 83 | * from task context and thereby corrupting the state, it is necessary to | 83 | * from task context and thereby corrupting the state, it is necessary to |
| 84 | * protect any manipulation of a task's fpsimd_state or TIF_FOREIGN_FPSTATE | 84 | * protect any manipulation of a task's fpsimd_state or TIF_FOREIGN_FPSTATE |
| 85 | * flag with local_bh_disable() unless softirqs are already masked. | 85 | * flag with {, __}get_cpu_fpsimd_context(). This will still allow softirqs to |
| 86 | * run but prevent them to use FPSIMD. | ||
| 86 | * | 87 | * |
| 87 | * For a certain task, the sequence may look something like this: | 88 | * For a certain task, the sequence may look something like this: |
| 88 | * - the task gets scheduled in; if both the task's fpsimd_cpu field | 89 | * - the task gets scheduled in; if both the task's fpsimd_cpu field |
| @@ -145,6 +146,56 @@ extern void __percpu *efi_sve_state; | |||
| 145 | 146 | ||
| 146 | #endif /* ! CONFIG_ARM64_SVE */ | 147 | #endif /* ! CONFIG_ARM64_SVE */ |
| 147 | 148 | ||
| 149 | DEFINE_PER_CPU(bool, fpsimd_context_busy); | ||
| 150 | EXPORT_PER_CPU_SYMBOL(fpsimd_context_busy); | ||
| 151 | |||
| 152 | static void __get_cpu_fpsimd_context(void) | ||
| 153 | { | ||
| 154 | bool busy = __this_cpu_xchg(fpsimd_context_busy, true); | ||
| 155 | |||
| 156 | WARN_ON(busy); | ||
| 157 | } | ||
| 158 | |||
| 159 | /* | ||
| 160 | * Claim ownership of the CPU FPSIMD context for use by the calling context. | ||
| 161 | * | ||
| 162 | * The caller may freely manipulate the FPSIMD context metadata until | ||
| 163 | * put_cpu_fpsimd_context() is called. | ||
| 164 | * | ||
| 165 | * The double-underscore version must only be called if you know the task | ||
| 166 | * can't be preempted. | ||
| 167 | */ | ||
| 168 | static void get_cpu_fpsimd_context(void) | ||
| 169 | { | ||
| 170 | preempt_disable(); | ||
| 171 | __get_cpu_fpsimd_context(); | ||
| 172 | } | ||
| 173 | |||
| 174 | static void __put_cpu_fpsimd_context(void) | ||
| 175 | { | ||
| 176 | bool busy = __this_cpu_xchg(fpsimd_context_busy, false); | ||
| 177 | |||
| 178 | WARN_ON(!busy); /* No matching get_cpu_fpsimd_context()? */ | ||
| 179 | } | ||
| 180 | |||
| 181 | /* | ||
| 182 | * Release the CPU FPSIMD context. | ||
| 183 | * | ||
| 184 | * Must be called from a context in which get_cpu_fpsimd_context() was | ||
| 185 | * previously called, with no call to put_cpu_fpsimd_context() in the | ||
| 186 | * meantime. | ||
| 187 | */ | ||
| 188 | static void put_cpu_fpsimd_context(void) | ||
| 189 | { | ||
| 190 | __put_cpu_fpsimd_context(); | ||
| 191 | preempt_enable(); | ||
| 192 | } | ||
| 193 | |||
| 194 | static bool have_cpu_fpsimd_context(void) | ||
| 195 | { | ||
| 196 | return !preemptible() && __this_cpu_read(fpsimd_context_busy); | ||
| 197 | } | ||
| 198 | |||
| 148 | /* | 199 | /* |
| 149 | * Call __sve_free() directly only if you know task can't be scheduled | 200 | * Call __sve_free() directly only if you know task can't be scheduled |
| 150 | * or preempted. | 201 | * or preempted. |
| @@ -215,12 +266,10 @@ static void sve_free(struct task_struct *task) | |||
| 215 | * This function should be called only when the FPSIMD/SVE state in | 266 | * This function should be called only when the FPSIMD/SVE state in |
| 216 | * thread_struct is known to be up to date, when preparing to enter | 267 | * thread_struct is known to be up to date, when preparing to enter |
| 217 | * userspace. | 268 | * userspace. |
| 218 | * | ||
| 219 | * Softirqs (and preemption) must be disabled. | ||
| 220 | */ | 269 | */ |
| 221 | static void task_fpsimd_load(void) | 270 | static void task_fpsimd_load(void) |
| 222 | { | 271 | { |
| 223 | WARN_ON(!in_softirq() && !irqs_disabled()); | 272 | WARN_ON(!have_cpu_fpsimd_context()); |
| 224 | 273 | ||
| 225 | if (system_supports_sve() && test_thread_flag(TIF_SVE)) | 274 | if (system_supports_sve() && test_thread_flag(TIF_SVE)) |
| 226 | sve_load_state(sve_pffr(¤t->thread), | 275 | sve_load_state(sve_pffr(¤t->thread), |
| @@ -233,16 +282,14 @@ static void task_fpsimd_load(void) | |||
| 233 | /* | 282 | /* |
| 234 | * Ensure FPSIMD/SVE storage in memory for the loaded context is up to | 283 | * Ensure FPSIMD/SVE storage in memory for the loaded context is up to |
| 235 | * date with respect to the CPU registers. | 284 | * date with respect to the CPU registers. |
| 236 | * | ||
| 237 | * Softirqs (and preemption) must be disabled. | ||
| 238 | */ | 285 | */ |
| 239 | void fpsimd_save(void) | 286 | static void fpsimd_save(void) |
| 240 | { | 287 | { |
| 241 | struct fpsimd_last_state_struct const *last = | 288 | struct fpsimd_last_state_struct const *last = |
| 242 | this_cpu_ptr(&fpsimd_last_state); | 289 | this_cpu_ptr(&fpsimd_last_state); |
| 243 | /* set by fpsimd_bind_task_to_cpu() or fpsimd_bind_state_to_cpu() */ | 290 | /* set by fpsimd_bind_task_to_cpu() or fpsimd_bind_state_to_cpu() */ |
| 244 | 291 | ||
| 245 | WARN_ON(!in_softirq() && !irqs_disabled()); | 292 | WARN_ON(!have_cpu_fpsimd_context()); |
| 246 | 293 | ||
| 247 | if (!test_thread_flag(TIF_FOREIGN_FPSTATE)) { | 294 | if (!test_thread_flag(TIF_FOREIGN_FPSTATE)) { |
| 248 | if (system_supports_sve() && test_thread_flag(TIF_SVE)) { | 295 | if (system_supports_sve() && test_thread_flag(TIF_SVE)) { |
| @@ -364,7 +411,8 @@ static __uint128_t arm64_cpu_to_le128(__uint128_t x) | |||
| 364 | * task->thread.sve_state. | 411 | * task->thread.sve_state. |
| 365 | * | 412 | * |
| 366 | * Task can be a non-runnable task, or current. In the latter case, | 413 | * Task can be a non-runnable task, or current. In the latter case, |
| 367 | * softirqs (and preemption) must be disabled. | 414 | * the caller must have ownership of the cpu FPSIMD context before calling |
| 415 | * this function. | ||
| 368 | * task->thread.sve_state must point to at least sve_state_size(task) | 416 | * task->thread.sve_state must point to at least sve_state_size(task) |
| 369 | * bytes of allocated kernel memory. | 417 | * bytes of allocated kernel memory. |
| 370 | * task->thread.uw.fpsimd_state must be up to date before calling this | 418 | * task->thread.uw.fpsimd_state must be up to date before calling this |
| @@ -393,7 +441,8 @@ static void fpsimd_to_sve(struct task_struct *task) | |||
| 393 | * task->thread.uw.fpsimd_state. | 441 | * task->thread.uw.fpsimd_state. |
| 394 | * | 442 | * |
| 395 | * Task can be a non-runnable task, or current. In the latter case, | 443 | * Task can be a non-runnable task, or current. In the latter case, |
| 396 | * softirqs (and preemption) must be disabled. | 444 | * the caller must have ownership of the cpu FPSIMD context before calling |
| 445 | * this function. | ||
| 397 | * task->thread.sve_state must point to at least sve_state_size(task) | 446 | * task->thread.sve_state must point to at least sve_state_size(task) |
| 398 | * bytes of allocated kernel memory. | 447 | * bytes of allocated kernel memory. |
| 399 | * task->thread.sve_state must be up to date before calling this function. | 448 | * task->thread.sve_state must be up to date before calling this function. |
| @@ -557,7 +606,7 @@ int sve_set_vector_length(struct task_struct *task, | |||
| 557 | * non-SVE thread. | 606 | * non-SVE thread. |
| 558 | */ | 607 | */ |
| 559 | if (task == current) { | 608 | if (task == current) { |
| 560 | local_bh_disable(); | 609 | get_cpu_fpsimd_context(); |
| 561 | 610 | ||
| 562 | fpsimd_save(); | 611 | fpsimd_save(); |
| 563 | } | 612 | } |
| @@ -567,7 +616,7 @@ int sve_set_vector_length(struct task_struct *task, | |||
| 567 | sve_to_fpsimd(task); | 616 | sve_to_fpsimd(task); |
| 568 | 617 | ||
| 569 | if (task == current) | 618 | if (task == current) |
| 570 | local_bh_enable(); | 619 | put_cpu_fpsimd_context(); |
| 571 | 620 | ||
| 572 | /* | 621 | /* |
| 573 | * Force reallocation of task SVE state to the correct size | 622 | * Force reallocation of task SVE state to the correct size |
| @@ -880,7 +929,7 @@ asmlinkage void do_sve_acc(unsigned int esr, struct pt_regs *regs) | |||
| 880 | 929 | ||
| 881 | sve_alloc(current); | 930 | sve_alloc(current); |
| 882 | 931 | ||
| 883 | local_bh_disable(); | 932 | get_cpu_fpsimd_context(); |
| 884 | 933 | ||
| 885 | fpsimd_save(); | 934 | fpsimd_save(); |
| 886 | 935 | ||
| @@ -891,7 +940,7 @@ asmlinkage void do_sve_acc(unsigned int esr, struct pt_regs *regs) | |||
| 891 | if (test_and_set_thread_flag(TIF_SVE)) | 940 | if (test_and_set_thread_flag(TIF_SVE)) |
| 892 | WARN_ON(1); /* SVE access shouldn't have trapped */ | 941 | WARN_ON(1); /* SVE access shouldn't have trapped */ |
| 893 | 942 | ||
| 894 | local_bh_enable(); | 943 | put_cpu_fpsimd_context(); |
| 895 | } | 944 | } |
| 896 | 945 | ||
| 897 | /* | 946 | /* |
| @@ -935,6 +984,8 @@ void fpsimd_thread_switch(struct task_struct *next) | |||
| 935 | if (!system_supports_fpsimd()) | 984 | if (!system_supports_fpsimd()) |
| 936 | return; | 985 | return; |
| 937 | 986 | ||
| 987 | __get_cpu_fpsimd_context(); | ||
| 988 | |||
| 938 | /* Save unsaved fpsimd state, if any: */ | 989 | /* Save unsaved fpsimd state, if any: */ |
| 939 | fpsimd_save(); | 990 | fpsimd_save(); |
| 940 | 991 | ||
| @@ -949,6 +1000,8 @@ void fpsimd_thread_switch(struct task_struct *next) | |||
| 949 | 1000 | ||
| 950 | update_tsk_thread_flag(next, TIF_FOREIGN_FPSTATE, | 1001 | update_tsk_thread_flag(next, TIF_FOREIGN_FPSTATE, |
| 951 | wrong_task || wrong_cpu); | 1002 | wrong_task || wrong_cpu); |
| 1003 | |||
| 1004 | __put_cpu_fpsimd_context(); | ||
| 952 | } | 1005 | } |
| 953 | 1006 | ||
| 954 | void fpsimd_flush_thread(void) | 1007 | void fpsimd_flush_thread(void) |
| @@ -958,7 +1011,7 @@ void fpsimd_flush_thread(void) | |||
| 958 | if (!system_supports_fpsimd()) | 1011 | if (!system_supports_fpsimd()) |
| 959 | return; | 1012 | return; |
| 960 | 1013 | ||
| 961 | local_bh_disable(); | 1014 | get_cpu_fpsimd_context(); |
| 962 | 1015 | ||
| 963 | fpsimd_flush_task_state(current); | 1016 | fpsimd_flush_task_state(current); |
| 964 | memset(¤t->thread.uw.fpsimd_state, 0, | 1017 | memset(¤t->thread.uw.fpsimd_state, 0, |
| @@ -999,7 +1052,7 @@ void fpsimd_flush_thread(void) | |||
| 999 | current->thread.sve_vl_onexec = 0; | 1052 | current->thread.sve_vl_onexec = 0; |
| 1000 | } | 1053 | } |
| 1001 | 1054 | ||
| 1002 | local_bh_enable(); | 1055 | put_cpu_fpsimd_context(); |
| 1003 | } | 1056 | } |
| 1004 | 1057 | ||
| 1005 | /* | 1058 | /* |
| @@ -1011,9 +1064,9 @@ void fpsimd_preserve_current_state(void) | |||
| 1011 | if (!system_supports_fpsimd()) | 1064 | if (!system_supports_fpsimd()) |
| 1012 | return; | 1065 | return; |
| 1013 | 1066 | ||
| 1014 | local_bh_disable(); | 1067 | get_cpu_fpsimd_context(); |
| 1015 | fpsimd_save(); | 1068 | fpsimd_save(); |
| 1016 | local_bh_enable(); | 1069 | put_cpu_fpsimd_context(); |
| 1017 | } | 1070 | } |
| 1018 | 1071 | ||
| 1019 | /* | 1072 | /* |
| @@ -1030,7 +1083,8 @@ void fpsimd_signal_preserve_current_state(void) | |||
| 1030 | 1083 | ||
| 1031 | /* | 1084 | /* |
| 1032 | * Associate current's FPSIMD context with this cpu | 1085 | * Associate current's FPSIMD context with this cpu |
| 1033 | * Preemption must be disabled when calling this function. | 1086 | * The caller must have ownership of the cpu FPSIMD context before calling |
| 1087 | * this function. | ||
| 1034 | */ | 1088 | */ |
| 1035 | void fpsimd_bind_task_to_cpu(void) | 1089 | void fpsimd_bind_task_to_cpu(void) |
| 1036 | { | 1090 | { |
| @@ -1076,14 +1130,14 @@ void fpsimd_restore_current_state(void) | |||
| 1076 | if (!system_supports_fpsimd()) | 1130 | if (!system_supports_fpsimd()) |
| 1077 | return; | 1131 | return; |
| 1078 | 1132 | ||
| 1079 | local_bh_disable(); | 1133 | get_cpu_fpsimd_context(); |
| 1080 | 1134 | ||
| 1081 | if (test_and_clear_thread_flag(TIF_FOREIGN_FPSTATE)) { | 1135 | if (test_and_clear_thread_flag(TIF_FOREIGN_FPSTATE)) { |
| 1082 | task_fpsimd_load(); | 1136 | task_fpsimd_load(); |
| 1083 | fpsimd_bind_task_to_cpu(); | 1137 | fpsimd_bind_task_to_cpu(); |
| 1084 | } | 1138 | } |
| 1085 | 1139 | ||
| 1086 | local_bh_enable(); | 1140 | put_cpu_fpsimd_context(); |
| 1087 | } | 1141 | } |
| 1088 | 1142 | ||
| 1089 | /* | 1143 | /* |
| @@ -1096,7 +1150,7 @@ void fpsimd_update_current_state(struct user_fpsimd_state const *state) | |||
| 1096 | if (!system_supports_fpsimd()) | 1150 | if (!system_supports_fpsimd()) |
| 1097 | return; | 1151 | return; |
| 1098 | 1152 | ||
| 1099 | local_bh_disable(); | 1153 | get_cpu_fpsimd_context(); |
| 1100 | 1154 | ||
| 1101 | current->thread.uw.fpsimd_state = *state; | 1155 | current->thread.uw.fpsimd_state = *state; |
| 1102 | if (system_supports_sve() && test_thread_flag(TIF_SVE)) | 1156 | if (system_supports_sve() && test_thread_flag(TIF_SVE)) |
| @@ -1107,7 +1161,7 @@ void fpsimd_update_current_state(struct user_fpsimd_state const *state) | |||
| 1107 | 1161 | ||
| 1108 | clear_thread_flag(TIF_FOREIGN_FPSTATE); | 1162 | clear_thread_flag(TIF_FOREIGN_FPSTATE); |
| 1109 | 1163 | ||
| 1110 | local_bh_enable(); | 1164 | put_cpu_fpsimd_context(); |
| 1111 | } | 1165 | } |
| 1112 | 1166 | ||
| 1113 | /* | 1167 | /* |
| @@ -1133,18 +1187,29 @@ void fpsimd_flush_task_state(struct task_struct *t) | |||
| 1133 | 1187 | ||
| 1134 | /* | 1188 | /* |
| 1135 | * Invalidate any task's FPSIMD state that is present on this cpu. | 1189 | * Invalidate any task's FPSIMD state that is present on this cpu. |
| 1136 | * This function must be called with softirqs disabled. | 1190 | * The FPSIMD context should be acquired with get_cpu_fpsimd_context() |
| 1191 | * before calling this function. | ||
| 1137 | */ | 1192 | */ |
| 1138 | void fpsimd_flush_cpu_state(void) | 1193 | static void fpsimd_flush_cpu_state(void) |
| 1139 | { | 1194 | { |
| 1140 | __this_cpu_write(fpsimd_last_state.st, NULL); | 1195 | __this_cpu_write(fpsimd_last_state.st, NULL); |
| 1141 | set_thread_flag(TIF_FOREIGN_FPSTATE); | 1196 | set_thread_flag(TIF_FOREIGN_FPSTATE); |
| 1142 | } | 1197 | } |
| 1143 | 1198 | ||
| 1144 | #ifdef CONFIG_KERNEL_MODE_NEON | 1199 | /* |
| 1200 | * Save the FPSIMD state to memory and invalidate cpu view. | ||
| 1201 | * This function must be called with preemption disabled. | ||
| 1202 | */ | ||
| 1203 | void fpsimd_save_and_flush_cpu_state(void) | ||
| 1204 | { | ||
| 1205 | WARN_ON(preemptible()); | ||
| 1206 | __get_cpu_fpsimd_context(); | ||
| 1207 | fpsimd_save(); | ||
| 1208 | fpsimd_flush_cpu_state(); | ||
| 1209 | __put_cpu_fpsimd_context(); | ||
| 1210 | } | ||
| 1145 | 1211 | ||
| 1146 | DEFINE_PER_CPU(bool, kernel_neon_busy); | 1212 | #ifdef CONFIG_KERNEL_MODE_NEON |
| 1147 | EXPORT_PER_CPU_SYMBOL(kernel_neon_busy); | ||
| 1148 | 1213 | ||
| 1149 | /* | 1214 | /* |
| 1150 | * Kernel-side NEON support functions | 1215 | * Kernel-side NEON support functions |
| @@ -1170,19 +1235,13 @@ void kernel_neon_begin(void) | |||
| 1170 | 1235 | ||
| 1171 | BUG_ON(!may_use_simd()); | 1236 | BUG_ON(!may_use_simd()); |
| 1172 | 1237 | ||
| 1173 | local_bh_disable(); | 1238 | get_cpu_fpsimd_context(); |
| 1174 | |||
| 1175 | __this_cpu_write(kernel_neon_busy, true); | ||
| 1176 | 1239 | ||
| 1177 | /* Save unsaved fpsimd state, if any: */ | 1240 | /* Save unsaved fpsimd state, if any: */ |
| 1178 | fpsimd_save(); | 1241 | fpsimd_save(); |
| 1179 | 1242 | ||
| 1180 | /* Invalidate any task state remaining in the fpsimd regs: */ | 1243 | /* Invalidate any task state remaining in the fpsimd regs: */ |
| 1181 | fpsimd_flush_cpu_state(); | 1244 | fpsimd_flush_cpu_state(); |
| 1182 | |||
| 1183 | preempt_disable(); | ||
| 1184 | |||
| 1185 | local_bh_enable(); | ||
| 1186 | } | 1245 | } |
| 1187 | EXPORT_SYMBOL(kernel_neon_begin); | 1246 | EXPORT_SYMBOL(kernel_neon_begin); |
| 1188 | 1247 | ||
| @@ -1197,15 +1256,10 @@ EXPORT_SYMBOL(kernel_neon_begin); | |||
| 1197 | */ | 1256 | */ |
| 1198 | void kernel_neon_end(void) | 1257 | void kernel_neon_end(void) |
| 1199 | { | 1258 | { |
| 1200 | bool busy; | ||
| 1201 | |||
| 1202 | if (!system_supports_fpsimd()) | 1259 | if (!system_supports_fpsimd()) |
| 1203 | return; | 1260 | return; |
| 1204 | 1261 | ||
| 1205 | busy = __this_cpu_xchg(kernel_neon_busy, false); | 1262 | put_cpu_fpsimd_context(); |
| 1206 | WARN_ON(!busy); /* No matching kernel_neon_begin()? */ | ||
| 1207 | |||
| 1208 | preempt_enable(); | ||
| 1209 | } | 1263 | } |
| 1210 | EXPORT_SYMBOL(kernel_neon_end); | 1264 | EXPORT_SYMBOL(kernel_neon_end); |
| 1211 | 1265 | ||
| @@ -1297,8 +1351,7 @@ static int fpsimd_cpu_pm_notifier(struct notifier_block *self, | |||
| 1297 | { | 1351 | { |
| 1298 | switch (cmd) { | 1352 | switch (cmd) { |
| 1299 | case CPU_PM_ENTER: | 1353 | case CPU_PM_ENTER: |
| 1300 | fpsimd_save(); | 1354 | fpsimd_save_and_flush_cpu_state(); |
| 1301 | fpsimd_flush_cpu_state(); | ||
| 1302 | break; | 1355 | break; |
| 1303 | case CPU_PM_EXIT: | 1356 | case CPU_PM_EXIT: |
| 1304 | break; | 1357 | break; |
