Commit 1b1cf8fe authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'x86-splitlock-2021-06-28' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 splitlock updates from Ingo Molnar:

 - Add the "ratelimit:N" parameter to the split_lock_detect= boot
   option, to rate-limit the generation of bus-lock exceptions.

   This is both easier on system resources and kinder to offending
   applications than the current policy of outright killing them.

 - Document the split-lock detection feature and its parameters.

* tag 'x86-splitlock-2021-06-28' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  Documentation/x86: Add ratelimit in buslock.rst
  Documentation/admin-guide: Add bus lock ratelimit
  x86/bus_lock: Set rate limit for bus lock
  Documentation/x86: Add buslock.rst
parents 5f498328 d28397ea
...@@ -5278,6 +5278,14 @@ ...@@ -5278,6 +5278,14 @@
exception. Default behavior is by #AC if exception. Default behavior is by #AC if
both features are enabled in hardware. both features are enabled in hardware.
ratelimit:N -
Set system wide rate limit to N bus locks
per second for bus lock detection.
0 < N <= 1000.
N/A for split lock detection.
If an #AC exception is hit in the kernel or in If an #AC exception is hit in the kernel or in
firmware (i.e. not while executing in user mode) firmware (i.e. not while executing in user mode)
the kernel will oops in either "warn" or "fatal" the kernel will oops in either "warn" or "fatal"
......
.. SPDX-License-Identifier: GPL-2.0
.. include:: <isonum.txt>
===============================
Bus lock detection and handling
===============================
:Copyright: |copy| 2021 Intel Corporation
:Authors: - Fenghua Yu <fenghua.yu@intel.com>
- Tony Luck <tony.luck@intel.com>
Problem
=======
A split lock is any atomic operation whose operand crosses two cache lines.
Since the operand spans two cache lines and the operation must be atomic,
the system locks the bus while the CPU accesses the two cache lines.
A bus lock is acquired through either split locked access to writeback (WB)
memory or any locked access to non-WB memory. This is typically thousands of
cycles slower than an atomic operation within a cache line. It also disrupts
performance on other cores and brings the whole system to its knees.
Detection
=========
Intel processors may support either or both of the following hardware
mechanisms to detect split locks and bus locks.
#AC exception for split lock detection
--------------------------------------
Beginning with the Tremont Atom CPU split lock operations may raise an
Alignment Check (#AC) exception when a split lock operation is attemped.
#DB exception for bus lock detection
------------------------------------
Some CPUs have the ability to notify the kernel by an #DB trap after a user
instruction acquires a bus lock and is executed. This allows the kernel to
terminate the application or to enforce throttling.
Software handling
=================
The kernel #AC and #DB handlers handle bus lock based on the kernel
parameter "split_lock_detect". Here is a summary of different options:
+------------------+----------------------------+-----------------------+
|split_lock_detect=|#AC for split lock |#DB for bus lock |
+------------------+----------------------------+-----------------------+
|off |Do nothing |Do nothing |
+------------------+----------------------------+-----------------------+
|warn |Kernel OOPs |Warn once per task and |
|(default) |Warn once per task and |and continues to run. |
| |disable future checking | |
| |When both features are | |
| |supported, warn in #AC | |
+------------------+----------------------------+-----------------------+
|fatal |Kernel OOPs |Send SIGBUS to user. |
| |Send SIGBUS to user | |
| |When both features are | |
| |supported, fatal in #AC | |
+------------------+----------------------------+-----------------------+
|ratelimit:N |Do nothing |Limit bus lock rate to |
|(0 < N <= 1000) | |N bus locks per second |
| | |system wide and warn on|
| | |bus locks. |
+------------------+----------------------------+-----------------------+
Usages
======
Detecting and handling bus lock may find usages in various areas:
It is critical for real time system designers who build consolidated real
time systems. These systems run hard real time code on some cores and run
"untrusted" user processes on other cores. The hard real time cannot afford
to have any bus lock from the untrusted processes to hurt real time
performance. To date the designers have been unable to deploy these
solutions as they have no way to prevent the "untrusted" user code from
generating split lock and bus lock to block the hard real time code to
access memory during bus locking.
It's also useful for general computing to prevent guests or user
applications from slowing down the overall system by executing instructions
with bus lock.
Guidance
========
off
---
Disable checking for split lock and bus lock. This option can be useful if
there are legacy applications that trigger these events at a low rate so
that mitigation is not needed.
warn
----
A warning is emitted when a bus lock is detected which allows to identify
the offending application. This is the default behavior.
fatal
-----
In this case, the bus lock is not tolerated and the process is killed.
ratelimit
---------
A system wide bus lock rate limit N is specified where 0 < N <= 1000. This
allows a bus lock rate up to N bus locks per second. When the bus lock rate
is exceeded then any task which is caught via the buslock #DB exception is
throttled by enforced sleeps until the rate goes under the limit again.
This is an effective mitigation in cases where a minimal impact can be
tolerated, but an eventual Denial of Service attack has to be prevented. It
allows to identify the offending processes and analyze whether they are
malicious or just badly written.
Selecting a rate limit of 1000 allows the bus to be locked for up to about
seven million cycles each second (assuming 7000 cycles for each bus
lock). On a 2 GHz processor that would be about 0.35% system slowdown.
...@@ -29,6 +29,7 @@ x86-specific Documentation ...@@ -29,6 +29,7 @@ x86-specific Documentation
microcode microcode
resctrl resctrl
tsx_async_abort tsx_async_abort
buslock
usb-legacy-support usb-legacy-support
i386/index i386/index
x86_64/index x86_64/index
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/thread_info.h> #include <linux/thread_info.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/delay.h>
#include <asm/cpufeature.h> #include <asm/cpufeature.h>
#include <asm/msr.h> #include <asm/msr.h>
...@@ -41,6 +42,7 @@ enum split_lock_detect_state { ...@@ -41,6 +42,7 @@ enum split_lock_detect_state {
sld_off = 0, sld_off = 0,
sld_warn, sld_warn,
sld_fatal, sld_fatal,
sld_ratelimit,
}; };
/* /*
...@@ -999,13 +1001,30 @@ static const struct { ...@@ -999,13 +1001,30 @@ static const struct {
{ "off", sld_off }, { "off", sld_off },
{ "warn", sld_warn }, { "warn", sld_warn },
{ "fatal", sld_fatal }, { "fatal", sld_fatal },
{ "ratelimit:", sld_ratelimit },
}; };
static struct ratelimit_state bld_ratelimit;
static inline bool match_option(const char *arg, int arglen, const char *opt) static inline bool match_option(const char *arg, int arglen, const char *opt)
{ {
int len = strlen(opt); int len = strlen(opt), ratelimit;
if (strncmp(arg, opt, len))
return false;
/*
* Min ratelimit is 1 bus lock/sec.
* Max ratelimit is 1000 bus locks/sec.
*/
if (sscanf(arg, "ratelimit:%d", &ratelimit) == 1 &&
ratelimit > 0 && ratelimit <= 1000) {
ratelimit_state_init(&bld_ratelimit, HZ, ratelimit);
ratelimit_set_flags(&bld_ratelimit, RATELIMIT_MSG_ON_RELEASE);
return true;
}
return len == arglen && !strncmp(arg, opt, len); return len == arglen;
} }
static bool split_lock_verify_msr(bool on) static bool split_lock_verify_msr(bool on)
...@@ -1084,6 +1103,15 @@ static void sld_update_msr(bool on) ...@@ -1084,6 +1103,15 @@ static void sld_update_msr(bool on)
static void split_lock_init(void) static void split_lock_init(void)
{ {
/*
* #DB for bus lock handles ratelimit and #AC for split lock is
* disabled.
*/
if (sld_state == sld_ratelimit) {
split_lock_verify_msr(false);
return;
}
if (cpu_model_supports_sld) if (cpu_model_supports_sld)
split_lock_verify_msr(sld_state != sld_off); split_lock_verify_msr(sld_state != sld_off);
} }
...@@ -1156,6 +1184,12 @@ void handle_bus_lock(struct pt_regs *regs) ...@@ -1156,6 +1184,12 @@ void handle_bus_lock(struct pt_regs *regs)
switch (sld_state) { switch (sld_state) {
case sld_off: case sld_off:
break; break;
case sld_ratelimit:
/* Enforce no more than bld_ratelimit bus locks/sec. */
while (!__ratelimit(&bld_ratelimit))
msleep(20);
/* Warn on the bus lock. */
fallthrough;
case sld_warn: case sld_warn:
pr_warn_ratelimited("#DB: %s/%d took a bus_lock trap at address: 0x%lx\n", pr_warn_ratelimited("#DB: %s/%d took a bus_lock trap at address: 0x%lx\n",
current->comm, current->pid, regs->ip); current->comm, current->pid, regs->ip);
...@@ -1261,6 +1295,10 @@ static void sld_state_show(void) ...@@ -1261,6 +1295,10 @@ static void sld_state_show(void)
" from non-WB" : ""); " from non-WB" : "");
} }
break; break;
case sld_ratelimit:
if (boot_cpu_has(X86_FEATURE_BUS_LOCK_DETECT))
pr_info("#DB: setting system wide bus lock rate limit to %u/sec\n", bld_ratelimit.burst);
break;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment