diff options
author | Eric Dumazet <edumazet@google.com> | 2018-03-23 17:58:18 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2018-03-27 06:01:48 -0400 |
commit | 67bbd7a8d6bcdc44cc27105ae8c374e9176ceaf1 (patch) | |
tree | aa876adde482382ffae7c3447a3179f9afde385d | |
parent | 07cde313b2d21f728cec2836db7cdb55476f7a26 (diff) |
x86/cpuid: Allow cpuid_read() to schedule
High latencies can be observed caused by a daemon periodically reading
CPUID on all cpus. On KASAN enabled kernels ~10ms latencies can be
observed. Even without KASAN, sending an IPI to a CPU, which is in a deep
sleep state or in a long hard IRQ disabled section, waiting for the answer
can consume hundreds of microseconds.
cpuid_read() is invoked in preemptible context, so it can be converted to
sleep instead of busy wait.
Switching to smp_call_function_single_async() and a completion allows to
reschedule and reduces CPU usage and latencies.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Ingo Molnar <mingo@kernel.org>
Cc: Hugh Dickins <hughd@google.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Link: https://lkml.kernel.org/r/20180323215818.127774-2-edumazet@google.com
-rw-r--r-- | arch/x86/kernel/cpuid.c | 34 |
1 files changed, 26 insertions, 8 deletions
diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c index 0931a105ffe1..1d300f96df4b 100644 --- a/arch/x86/kernel/cpuid.c +++ b/arch/x86/kernel/cpuid.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <linux/notifier.h> | 40 | #include <linux/notifier.h> |
41 | #include <linux/uaccess.h> | 41 | #include <linux/uaccess.h> |
42 | #include <linux/gfp.h> | 42 | #include <linux/gfp.h> |
43 | #include <linux/completion.h> | ||
43 | 44 | ||
44 | #include <asm/processor.h> | 45 | #include <asm/processor.h> |
45 | #include <asm/msr.h> | 46 | #include <asm/msr.h> |
@@ -47,19 +48,27 @@ | |||
47 | static struct class *cpuid_class; | 48 | static struct class *cpuid_class; |
48 | static enum cpuhp_state cpuhp_cpuid_state; | 49 | static enum cpuhp_state cpuhp_cpuid_state; |
49 | 50 | ||
51 | struct cpuid_regs_done { | ||
52 | struct cpuid_regs regs; | ||
53 | struct completion done; | ||
54 | }; | ||
55 | |||
50 | static void cpuid_smp_cpuid(void *cmd_block) | 56 | static void cpuid_smp_cpuid(void *cmd_block) |
51 | { | 57 | { |
52 | struct cpuid_regs *cmd = (struct cpuid_regs *)cmd_block; | 58 | struct cpuid_regs_done *cmd = cmd_block; |
59 | |||
60 | cpuid_count(cmd->regs.eax, cmd->regs.ecx, | ||
61 | &cmd->regs.eax, &cmd->regs.ebx, | ||
62 | &cmd->regs.ecx, &cmd->regs.edx); | ||
53 | 63 | ||
54 | cpuid_count(cmd->eax, cmd->ecx, | 64 | complete(&cmd->done); |
55 | &cmd->eax, &cmd->ebx, &cmd->ecx, &cmd->edx); | ||
56 | } | 65 | } |
57 | 66 | ||
58 | static ssize_t cpuid_read(struct file *file, char __user *buf, | 67 | static ssize_t cpuid_read(struct file *file, char __user *buf, |
59 | size_t count, loff_t *ppos) | 68 | size_t count, loff_t *ppos) |
60 | { | 69 | { |
61 | char __user *tmp = buf; | 70 | char __user *tmp = buf; |
62 | struct cpuid_regs cmd; | 71 | struct cpuid_regs_done cmd; |
63 | int cpu = iminor(file_inode(file)); | 72 | int cpu = iminor(file_inode(file)); |
64 | u64 pos = *ppos; | 73 | u64 pos = *ppos; |
65 | ssize_t bytes = 0; | 74 | ssize_t bytes = 0; |
@@ -68,19 +77,28 @@ static ssize_t cpuid_read(struct file *file, char __user *buf, | |||
68 | if (count % 16) | 77 | if (count % 16) |
69 | return -EINVAL; /* Invalid chunk size */ | 78 | return -EINVAL; /* Invalid chunk size */ |
70 | 79 | ||
80 | init_completion(&cmd.done); | ||
71 | for (; count; count -= 16) { | 81 | for (; count; count -= 16) { |
72 | cmd.eax = pos; | 82 | call_single_data_t csd = { |
73 | cmd.ecx = pos >> 32; | 83 | .func = cpuid_smp_cpuid, |
74 | err = smp_call_function_single(cpu, cpuid_smp_cpuid, &cmd, 1); | 84 | .info = &cmd, |
85 | }; | ||
86 | |||
87 | cmd.regs.eax = pos; | ||
88 | cmd.regs.ecx = pos >> 32; | ||
89 | |||
90 | err = smp_call_function_single_async(cpu, &csd); | ||
75 | if (err) | 91 | if (err) |
76 | break; | 92 | break; |
77 | if (copy_to_user(tmp, &cmd, 16)) { | 93 | wait_for_completion(&cmd.done); |
94 | if (copy_to_user(tmp, &cmd.regs, 16)) { | ||
78 | err = -EFAULT; | 95 | err = -EFAULT; |
79 | break; | 96 | break; |
80 | } | 97 | } |
81 | tmp += 16; | 98 | tmp += 16; |
82 | bytes += 16; | 99 | bytes += 16; |
83 | *ppos = ++pos; | 100 | *ppos = ++pos; |
101 | reinit_completion(&cmd.done); | ||
84 | } | 102 | } |
85 | 103 | ||
86 | return bytes ? bytes : err; | 104 | return bytes ? bytes : err; |