To enforce user application throttling or mitigations, extend the
existing split lock detect kernel parameter:
        split_lock_detect=ratelimit:N

It limits bus lock rate to N per second for non root users.

Signed-off-by: Fenghua Yu <fenghua...@intel.com>
Reviewed-by: Tony Luck <tony.l...@intel.com>
---
 arch/x86/kernel/cpu/intel.c | 37 ++++++++++++++++++++++++++++++++-----
 include/linux/sched/user.h  |  4 +++-
 kernel/user.c               |  7 +++++++
 3 files changed, 42 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index 3aa57199484b..6dcc7e404f8b 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -10,6 +10,9 @@
 #include <linux/thread_info.h>
 #include <linux/init.h>
 #include <linux/uaccess.h>
+#include <linux/cred.h>
+#include <linux/delay.h>
+#include <linux/sched/user.h>
 
 #include <asm/cpufeature.h>
 #include <asm/msr.h>
@@ -40,6 +43,7 @@ enum split_lock_detect_state {
        sld_off = 0,
        sld_warn,
        sld_fatal,
+       sld_ratelimit,
 };
 
 /*
@@ -998,13 +1002,25 @@ static const struct {
        { "off",        sld_off   },
        { "warn",       sld_warn  },
        { "fatal",      sld_fatal },
+       { "ratelimit:", sld_ratelimit },
 };
 
 static inline bool match_option(const char *arg, int arglen, const char *opt)
 {
-       int len = strlen(opt);
 
-       return len == arglen && !strncmp(arg, opt, len);
+       int len = strlen(opt), ratelimit;
+
+       if (strncmp(arg, opt, len))
+               return false;
+
+       if (sscanf(arg, "ratelimit:%d", &ratelimit) == 1 && ratelimit > 0 &&
+           ratelimit_bl <= HZ / 2) {
+               ratelimit_bl = ratelimit;
+
+               return true;
+       }
+
+       return len == arglen;
 }
 
 static bool split_lock_verify_msr(bool on)
@@ -1084,8 +1100,8 @@ static void sld_update_msr(bool on)
 
 static void split_lock_init(void)
 {
-       /* If supported, #DB for bus lock will handle warn. */
-       if (bld && sld_state == sld_warn)
+       /* If supported, #DB for bus lock will handle warn and ratelimit. */
+       if (bld && (sld_state == sld_warn || sld_state == sld_ratelimit))
                return;
 
        if (cpu_model_supports_sld)
@@ -1142,7 +1158,8 @@ static void bus_lock_init(void)
 
 bool handle_user_split_lock(struct pt_regs *regs, long error_code)
 {
-       if ((regs->flags & X86_EFLAGS_AC) || !sld || sld_state == sld_fatal)
+       if ((regs->flags & X86_EFLAGS_AC) || !sld || sld_state == sld_fatal ||
+           sld_state == sld_ratelimit)
                return false;
        split_lock_warn(regs->ip);
        return true;
@@ -1156,6 +1173,11 @@ bool handle_bus_lock(struct pt_regs *regs)
        pr_warn_ratelimited("#DB: %s/%d took a bus_lock trap at address: 
0x%lx\n",
                            current->comm, current->pid, regs->ip);
 
+       if (sld_state == sld_ratelimit) {
+               while (!__ratelimit(&get_current_user()->ratelimit_bl))
+                       msleep(1000 / ratelimit_bl);
+       }
+
        return true;
 }
 
@@ -1251,6 +1273,11 @@ static void sld_state_show(void)
                else
                        pr_info("#DB: sending SIGBUS on user-space 
bus_locks\n");
                break;
+
+       case sld_ratelimit:
+               if (bld)
+                       pr_info("#DB: setting rate limit to %d/sec per user on 
non root user-space bus_locks\n", ratelimit_bl);
+               break;
        }
 }
 
diff --git a/include/linux/sched/user.h b/include/linux/sched/user.h
index a8ec3b6093fc..79f95002a123 100644
--- a/include/linux/sched/user.h
+++ b/include/linux/sched/user.h
@@ -40,8 +40,9 @@ struct user_struct {
        atomic_t nr_watches;    /* The number of watches this user currently 
has */
 #endif
 
-       /* Miscellaneous per-user rate limit */
+       /* Miscellaneous per-user rate limits */
        struct ratelimit_state ratelimit;
+       struct ratelimit_state ratelimit_bl;
 };
 
 extern int uids_sysfs_init(void);
@@ -51,6 +52,7 @@ extern struct user_struct *find_user(kuid_t);
 extern struct user_struct root_user;
 #define INIT_USER (&root_user)
 
+extern int ratelimit_bl;
 
 /* per-UID process charging. */
 extern struct user_struct * alloc_uid(kuid_t);
diff --git a/kernel/user.c b/kernel/user.c
index b1635d94a1f2..023dad617625 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -103,6 +103,7 @@ struct user_struct root_user = {
        .locked_shm     = 0,
        .uid            = GLOBAL_ROOT_UID,
        .ratelimit      = RATELIMIT_STATE_INIT(root_user.ratelimit, 0, 0),
+       .ratelimit_bl   = RATELIMIT_STATE_INIT(root_user.ratelimit_bl, 0, 0),
 };
 
 /*
@@ -172,6 +173,9 @@ void free_uid(struct user_struct *up)
                free_user(up, flags);
 }
 
+/* Architectures (e.g. X86) may set this for rate limited bus locks. */
+int ratelimit_bl;
+
 struct user_struct *alloc_uid(kuid_t uid)
 {
        struct hlist_head *hashent = uidhashentry(uid);
@@ -190,6 +194,9 @@ struct user_struct *alloc_uid(kuid_t uid)
                refcount_set(&new->__count, 1);
                ratelimit_state_init(&new->ratelimit, HZ, 100);
                ratelimit_set_flags(&new->ratelimit, RATELIMIT_MSG_ON_RELEASE);
+               ratelimit_state_init(&new->ratelimit_bl, HZ, ratelimit_bl);
+               ratelimit_set_flags(&new->ratelimit_bl,
+                                   RATELIMIT_MSG_ON_RELEASE);
 
                /*
                 * Before adding this, check whether we raced
-- 
2.29.1

Reply via email to