Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86, prctl: Hook L1D flushing in via prctl

Use the existing PR_GET/SET_SPECULATION_CTRL API to expose the L1D flush
capability. For L1D flushing PR_SPEC_FORCE_DISABLE and
PR_SPEC_DISABLE_NOEXEC are not supported.

Enabling L1D flush does not check if the task is running on an SMT enabled
core, rather a check is done at runtime (at the time of flush), if the task
runs on a SMT sibling then the task is sent a SIGBUS which is executed
before the task returns to user space or to a guest.

This is better than the other alternatives of:

a. Ensuring strict affinity of the task (hard to enforce without further
changes in the scheduler)

b. Silently skipping flush for tasks that move to SMT enabled cores.

Hook up the core prctl and implement the x86 specific parts which in turn
makes it functional.

Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Balbir Singh <sblbir@amazon.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20210108121056.21940-5-sblbir@amazon.com

authored by

Balbir Singh and committed by
Thomas Gleixner
e893bb1b b5f06f64

+34
+33
arch/x86/kernel/cpu/bugs.c
··· 1252 1252 speculation_ctrl_update_current(); 1253 1253 } 1254 1254 1255 + static int l1d_flush_prctl_set(struct task_struct *task, unsigned long ctrl) 1256 + { 1257 + 1258 + if (!static_branch_unlikely(&switch_mm_cond_l1d_flush)) 1259 + return -EPERM; 1260 + 1261 + switch (ctrl) { 1262 + case PR_SPEC_ENABLE: 1263 + set_ti_thread_flag(&task->thread_info, TIF_SPEC_L1D_FLUSH); 1264 + return 0; 1265 + case PR_SPEC_DISABLE: 1266 + clear_ti_thread_flag(&task->thread_info, TIF_SPEC_L1D_FLUSH); 1267 + return 0; 1268 + default: 1269 + return -ERANGE; 1270 + } 1271 + } 1272 + 1255 1273 static int ssb_prctl_set(struct task_struct *task, unsigned long ctrl) 1256 1274 { 1257 1275 if (ssb_mode != SPEC_STORE_BYPASS_PRCTL && ··· 1379 1361 return ssb_prctl_set(task, ctrl); 1380 1362 case PR_SPEC_INDIRECT_BRANCH: 1381 1363 return ib_prctl_set(task, ctrl); 1364 + case PR_SPEC_L1D_FLUSH: 1365 + return l1d_flush_prctl_set(task, ctrl); 1382 1366 default: 1383 1367 return -ENODEV; 1384 1368 } ··· 1396 1376 ib_prctl_set(task, PR_SPEC_FORCE_DISABLE); 1397 1377 } 1398 1378 #endif 1379 + 1380 + static int l1d_flush_prctl_get(struct task_struct *task) 1381 + { 1382 + if (!static_branch_unlikely(&switch_mm_cond_l1d_flush)) 1383 + return PR_SPEC_FORCE_DISABLE; 1384 + 1385 + if (test_ti_thread_flag(&task->thread_info, TIF_SPEC_L1D_FLUSH)) 1386 + return PR_SPEC_PRCTL | PR_SPEC_ENABLE; 1387 + else 1388 + return PR_SPEC_PRCTL | PR_SPEC_DISABLE; 1389 + } 1399 1390 1400 1391 static int ssb_prctl_get(struct task_struct *task) 1401 1392 { ··· 1458 1427 return ssb_prctl_get(task); 1459 1428 case PR_SPEC_INDIRECT_BRANCH: 1460 1429 return ib_prctl_get(task); 1430 + case PR_SPEC_L1D_FLUSH: 1431 + return l1d_flush_prctl_get(task); 1461 1432 default: 1462 1433 return -ENODEV; 1463 1434 }
+1
include/uapi/linux/prctl.h
··· 213 213 /* Speculation control variants */ 214 214 # define PR_SPEC_STORE_BYPASS 0 215 215 # define PR_SPEC_INDIRECT_BRANCH 1 216 + # define PR_SPEC_L1D_FLUSH 2 216 217 /* Return and control values for PR_SET/GET_SPECULATION_CTRL */ 217 218 # define PR_SPEC_NOT_AFFECTED 0 218 219 # define PR_SPEC_PRCTL (1UL << 0)