x86: even faster core_id()s with segmentation
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 12 Jun 2014 04:32:46 +0000 (21:32 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 12 Jun 2014 04:32:46 +0000 (21:32 -0700)
The old slow core_id reads, using the LAPIC and the os_coreid() lookup
took about 50 TSC ticks per core_id() call (avg time in a for loop of
100000 calls).

The rdtscp option (the old FAST_COREID) was about 30 ticks.

The segmentation lookup is 1 tick.

rdtscp still has the pcoreid in ecx, so that userspace can use it.

kern/arch/x86/Kconfig
kern/arch/x86/coreid.h
kern/arch/x86/cpuinfo.c
kern/arch/x86/smp_boot.c
kern/include/smp.h

index a5ea74d..bfc769e 100644 (file)
@@ -24,15 +24,6 @@ config NOFASTCALL_FSBASE
                setting FS base from userspace, you can say y to disable the fastcall
                for a slight improvement for all syscalls.  If unsure, say n.
 
-config FAST_COREID
-       bool "Fast core_id() via rdtscp"
-       default n
-       help
-               Uses rdtscp for a faster core_id() call.  Requires a relatively recent
-               microarchitecture (Nehalem).  Also, in my experience, qemu's support
-               for rdtscp on some machines is a little lacking, so don't be surprised
-               if it doesn't work when virtualized.  Say n if you are unsure.
-
 endmenu
 
 menu "x86 Hacks"
index 98522cc..2f383e3 100644 (file)
@@ -37,14 +37,19 @@ static inline int node_id(void)
        return 0;
 }
 
-#ifdef CONFIG_FAST_COREID
+#ifdef CONFIG_X86_64
 static inline int core_id(void)
 {
-       int ret;
-       asm volatile ("rdtscp" : "=c"(ret) : : "eax", "edx");
-       return ret;
+       int coreid;
+       /* assuming we're right after stacktop.  gs base is the pcpui struct, but we
+        * don't have access to the pcpui struct or to the extern per_cpu_info here,
+        * due to include loops. */
+       asm volatile ("movl %%gs:8, %0" : "=r"(coreid));
+       return coreid;
 }
 #else
+/* 32 bit code just uses the old crap.  could use rdtscp, but not worth the
+ * hassle. */
 /* core_id() returns the OS core number, not to be confused with the
  * hardware-specific core identifier (such as the lapic id) returned by
  * hw_core_id() */
@@ -52,7 +57,7 @@ static inline int core_id(void)
 {
        return get_os_coreid(hw_core_id());
 }
-#endif /* CONFIG_FAST_COREID */
+#endif /* CONFIG_X86_64 */
 
 static inline int core_id_early(void)
 {
index 36b8239..33dd980 100644 (file)
@@ -154,11 +154,6 @@ void print_cpuinfo(void)
                write_msr(MSR_TSC_AUX, 0);
        } else {
                printk("RDTSCP not supported, but emulated for userspace\n");
-               #ifdef CONFIG_FAST_COREID
-               printk("\nCONFIG_FAST_COREID selected, but RDTSCP not available!\n");
-               printk("\nRebuild your kernel without CONFIG_FAST_COREID\n\n");
-               panic("Cannot boot\n");
-               #endif
        }
        /* Regardless, make sure userspace can access rdtsc (and rdtscp) */
        lcr4(rcr4() & ~CR4_TSD);
index 2ef956b..64d8714 100644 (file)
@@ -60,6 +60,22 @@ static void init_smp_call_function(void)
 
 /******************************************************************************/
 
+static void setup_rdtscp(int coreid)
+{
+       uint32_t edx;
+       int rdtscp_ecx;
+       /* TODO: have some sort of 'cpu info structure' with flags */
+       cpuid(0x80000001, 0x0, 0, 0, 0, &edx);
+       if (edx & (1 << 27)) {
+               write_msr(MSR_TSC_AUX, coreid);
+               /* Busted versions of qemu bug out here (32 bit) */
+               asm volatile ("rdtscp" : "=c"(rdtscp_ecx) : : "eax", "edx");
+               if (!coreid && (read_msr(MSR_TSC_AUX) != rdtscp_ecx))
+                       printk("\nBroken rdtscp detected, don't trust it for pcoreid!\n\n");
+       }
+}
+
+/* TODO: consider merging __arch_pcpu with parts of this (sync with RISCV) */
 void smp_final_core_init(void)
 {
        /* It is possible that the non-0 cores will wake up before the broadcast
@@ -70,23 +86,15 @@ void smp_final_core_init(void)
                wait = FALSE;
        while (wait)
                cpu_relax();
-#ifdef CONFIG_FAST_COREID
-       /* Need to bootstrap the rdtscp MSR with our OS coreid */
+#ifdef CONFIG_X86_64
        int coreid = get_os_coreid(hw_core_id());
-       write_msr(MSR_TSC_AUX, coreid);
-
-       /* Busted versions of qemu bug out here (32 bit) */
-       int rdtscp_ecx;
-       asm volatile ("rdtscp" : "=c"(rdtscp_ecx) : : "eax", "edx");
-       if (read_msr(MSR_TSC_AUX) != rdtscp_ecx) {
-               printk("Broken rdtscp detected!  Rebuild without CONFIG_FAST_COREID\n");
-               if (coreid)
-                       while(1);
-               /* note this panic may think it is not core 0, and core 0 might not have
-                * an issue (seems random) */
-               panic("");
-       }
+       struct per_cpu_info *pcpui = &per_cpu_info[coreid];
+       pcpui->coreid = coreid;
+       write_msr(MSR_GS_BASE, (uint64_t)pcpui);
+       write_msr(MSR_KERN_GS_BASE, (uint64_t)pcpui);
 #endif
+       /* don't need this for the kernel anymore, but userspace can still use it */
+       setup_rdtscp(coreid);
        setup_default_mtrrs(&generic_barrier);
        smp_percpu_init();
        waiton_barrier(&generic_barrier);
@@ -329,14 +337,8 @@ void __arch_pcpu_init(uint32_t coreid)
                                          sizeof(taskstate_t) + sizeof(pseudodesc_t));
        }
 #ifdef CONFIG_X86_64
-       /* Core 0 set up the base MSRs in entry64 */
-       if (!coreid) {
-               assert(read_msr(MSR_GS_BASE) == (uint64_t)pcpui);
-               assert(read_msr(MSR_KERN_GS_BASE) == (uint64_t)pcpui);
-       } else {
-               write_msr(MSR_GS_BASE, (uint64_t)pcpui);
-               write_msr(MSR_KERN_GS_BASE, (uint64_t)pcpui);
-       }
+       assert(read_msr(MSR_GS_BASE) == (uint64_t)pcpui);
+       assert(read_msr(MSR_KERN_GS_BASE) == (uint64_t)pcpui);
 #endif
        /* Don't try setting up til after setting GS */
        x86_sysenter_init(x86_get_stacktop_tss(pcpui->tss));
index 4b17ba1..c5422be 100644 (file)
@@ -28,7 +28,8 @@ typedef sharC_env_t;
 
 struct per_cpu_info {
 #ifdef CONFIG_X86_64
-       uintptr_t stacktop;
+       uintptr_t stacktop;                     /* must be first */
+       int coreid;                                     /* must be second */
        /* virtual machines */
        /* this is all kind of gross, but so it goes. Kmalloc
         * the vmxarea. It varies in size depending on the architecture.