aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2018-03-23 17:58:18 -0400
committerThomas Gleixner <tglx@linutronix.de>2018-03-27 06:01:48 -0400
commit67bbd7a8d6bcdc44cc27105ae8c374e9176ceaf1 (patch)
treeaa876adde482382ffae7c3447a3179f9afde385d
parent07cde313b2d21f728cec2836db7cdb55476f7a26 (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.c34
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 @@
47static struct class *cpuid_class; 48static struct class *cpuid_class;
48static enum cpuhp_state cpuhp_cpuid_state; 49static enum cpuhp_state cpuhp_cpuid_state;
49 50
51struct cpuid_regs_done {
52 struct cpuid_regs regs;
53 struct completion done;
54};
55
50static void cpuid_smp_cpuid(void *cmd_block) 56static 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
58static ssize_t cpuid_read(struct file *file, char __user *buf, 67static 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;