|
7 | 7 | #include <linux/smp.h>
|
8 | 8 | #include <linux/sched.h>
|
9 | 9 | #include <linux/sched/clock.h>
|
| 10 | +#include <linux/semaphore.h> |
10 | 11 | #include <linux/thread_info.h>
|
11 | 12 | #include <linux/init.h>
|
12 | 13 | #include <linux/uaccess.h>
|
| 14 | +#include <linux/workqueue.h> |
13 | 15 | #include <linux/delay.h>
|
| 16 | +#include <linux/cpuhotplug.h> |
14 | 17 |
|
15 | 18 | #include <asm/cpufeature.h>
|
16 | 19 | #include <asm/msr.h>
|
@@ -999,6 +1002,8 @@ static const struct {
|
999 | 1002 |
|
1000 | 1003 | static struct ratelimit_state bld_ratelimit;
|
1001 | 1004 |
|
| 1005 | +static DEFINE_SEMAPHORE(buslock_sem); |
| 1006 | + |
1002 | 1007 | static inline bool match_option(const char *arg, int arglen, const char *opt)
|
1003 | 1008 | {
|
1004 | 1009 | int len = strlen(opt), ratelimit;
|
@@ -1109,18 +1114,52 @@ static void split_lock_init(void)
|
1109 | 1114 | split_lock_verify_msr(sld_state != sld_off);
|
1110 | 1115 | }
|
1111 | 1116 |
|
| 1117 | +static void __split_lock_reenable(struct work_struct *work) |
| 1118 | +{ |
| 1119 | + sld_update_msr(true); |
| 1120 | + up(&buslock_sem); |
| 1121 | +} |
| 1122 | + |
| 1123 | +/* |
| 1124 | + * If a CPU goes offline with pending delayed work to re-enable split lock |
| 1125 | + * detection then the delayed work will be executed on some other CPU. That |
| 1126 | + * handles releasing the buslock_sem, but because it executes on a |
| 1127 | + * different CPU probably won't re-enable split lock detection. This is a |
| 1128 | + * problem on HT systems since the sibling CPU on the same core may then be |
| 1129 | + * left running with split lock detection disabled. |
| 1130 | + * |
| 1131 | + * Unconditionally re-enable detection here. |
| 1132 | + */ |
| 1133 | +static int splitlock_cpu_offline(unsigned int cpu) |
| 1134 | +{ |
| 1135 | + sld_update_msr(true); |
| 1136 | + |
| 1137 | + return 0; |
| 1138 | +} |
| 1139 | + |
| 1140 | +static DECLARE_DELAYED_WORK(split_lock_reenable, __split_lock_reenable); |
| 1141 | + |
1112 | 1142 | static void split_lock_warn(unsigned long ip)
|
1113 | 1143 | {
|
1114 |
| - pr_warn_ratelimited("#AC: %s/%d took a split_lock trap at address: 0x%lx\n", |
1115 |
| - current->comm, current->pid, ip); |
| 1144 | + int cpu; |
1116 | 1145 |
|
1117 |
| - /* |
1118 |
| - * Disable the split lock detection for this task so it can make |
1119 |
| - * progress and set TIF_SLD so the detection is re-enabled via |
1120 |
| - * switch_to_sld() when the task is scheduled out. |
1121 |
| - */ |
| 1146 | + if (!current->reported_split_lock) |
| 1147 | + pr_warn_ratelimited("#AC: %s/%d took a split_lock trap at address: 0x%lx\n", |
| 1148 | + current->comm, current->pid, ip); |
| 1149 | + current->reported_split_lock = 1; |
| 1150 | + |
| 1151 | + /* misery factor #1, sleep 10ms before trying to execute split lock */ |
| 1152 | + if (msleep_interruptible(10) > 0) |
| 1153 | + return; |
| 1154 | + /* Misery factor #2, only allow one buslocked disabled core at a time */ |
| 1155 | + if (down_interruptible(&buslock_sem) == -EINTR) |
| 1156 | + return; |
| 1157 | + cpu = get_cpu(); |
| 1158 | + schedule_delayed_work_on(cpu, &split_lock_reenable, 2); |
| 1159 | + |
| 1160 | + /* Disable split lock detection on this CPU to make progress */ |
1122 | 1161 | sld_update_msr(false);
|
1123 |
| - set_tsk_thread_flag(current, TIF_SLD); |
| 1162 | + put_cpu(); |
1124 | 1163 | }
|
1125 | 1164 |
|
1126 | 1165 | bool handle_guest_split_lock(unsigned long ip)
|
@@ -1274,10 +1313,14 @@ static void sld_state_show(void)
|
1274 | 1313 | pr_info("disabled\n");
|
1275 | 1314 | break;
|
1276 | 1315 | case sld_warn:
|
1277 |
| - if (boot_cpu_has(X86_FEATURE_SPLIT_LOCK_DETECT)) |
| 1316 | + if (boot_cpu_has(X86_FEATURE_SPLIT_LOCK_DETECT)) { |
1278 | 1317 | pr_info("#AC: crashing the kernel on kernel split_locks and warning on user-space split_locks\n");
|
1279 |
| - else if (boot_cpu_has(X86_FEATURE_BUS_LOCK_DETECT)) |
| 1318 | + if (cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, |
| 1319 | + "x86/splitlock", NULL, splitlock_cpu_offline) < 0) |
| 1320 | + pr_warn("No splitlock CPU offline handler\n"); |
| 1321 | + } else if (boot_cpu_has(X86_FEATURE_BUS_LOCK_DETECT)) { |
1280 | 1322 | pr_info("#DB: warning on user-space bus_locks\n");
|
| 1323 | + } |
1281 | 1324 | break;
|
1282 | 1325 | case sld_fatal:
|
1283 | 1326 | if (boot_cpu_has(X86_FEATURE_SPLIT_LOCK_DETECT)) {
|
|
0 commit comments