Handle rdtscp
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 11 Mar 2013 22:10:49 +0000 (15:10 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 11 Mar 2013 22:10:49 +0000 (15:10 -0700)
rdtscp has a bunch of benefits for measurements (stay tuned for more
commits), but it only works on more recent x86 hardware, and doesn't
work on my old core2 development machine running qemu.

x86 will now catch the rdtscp invalid opcode trap and fake the command.
This will not give accurate numbers for measurement.  During boot, look
for whether or not RDTSCP is supported if you are curious.

In the future, we could have a flag that replaces rdtscp calls with
rdtsc at compile time, if someone wanted more accurate measurements on
older hardware.  For now, I'm not too concerned with those older
machines when it comes to measuring performance.

kern/arch/i686/arch.h
kern/arch/i686/cpuinfo.c
kern/arch/i686/trap.c
kern/arch/riscv/arch.h
kern/arch/sparc/arch.h
user/parlib/include/i686/arch.h
user/parlib/include/riscv/arch.h
user/parlib/include/sparc/arch.h

index f5cdc70..0dd4383 100644 (file)
@@ -78,6 +78,15 @@ read_tsc(void)
        return tsc;
 }
 
+/* non-core-id reporting style (it is in ecx) */
+static __inline uint64_t
+read_tscp(void)
+{
+       uint64_t tsc;
+       __asm __volatile("rdtscp" : "=A" (tsc) : : "ecx");
+       return tsc;
+}
+
 static __inline uint64_t 
 read_tsc_serialized(void)
 {
index d54bd19..6404541 100644 (file)
@@ -128,6 +128,11 @@ void print_cpuinfo(void)
                printk("FS/GS Base RD/W supported\n");
        else
                printk("FS/GS Base RD/W not supported\n");
+       cpuid(0x80000001, 0x0, &eax, &ebx, &ecx, &edx);
+       if (edx & (1 << 27))
+               printk("RDTSCP supported\n");
+       else
+               printk("RDTSCP not supported: don't trust detailed measurements\n");
 
 }
 
index 41ce704..1258592 100644 (file)
@@ -244,6 +244,15 @@ print_trapframe(trapframe_t *tf)
        pcpui->__lock_depth_disabled--;
 }
 
+static void fake_rdtscp(struct trapframe *tf)
+{
+       uint64_t tsc_time = read_tsc();
+       tf->tf_eip += 3;
+       tf->tf_regs.reg_eax = tsc_time & 0xffffffff;
+       tf->tf_regs.reg_edx = tsc_time >> 32;
+       tf->tf_regs.reg_ecx = core_id();
+}
+
 /* Certain traps want IRQs enabled, such as the syscall.  Others can't handle
  * it, like the page fault handler.  Turn them on on a case-by-case basis. */
 static void trap_dispatch(struct trapframe *tf)
@@ -267,6 +276,32 @@ static void trap_dispatch(struct trapframe *tf)
                        enable_irq();
                        monitor(tf);
                        break;
+               case T_ILLOP:
+                       pcpui = &per_cpu_info[core_id()];
+                       pcpui->__lock_depth_disabled++;         /* for print debugging */
+                       /* We will muck with the actual TF.  If we're dealing with
+                        * userspace, we need to make sure we edit the actual TF that will
+                        * get restarted (pcpui), and not the TF on the kstack (which aren't
+                        * the same).  See set_current_tf() for more info. */
+                       if (!in_kernel(tf))
+                               tf = pcpui->cur_tf;
+                       printd("bad opcode, eip: %08p, next 3 bytes: %x %x %x\n",
+                              tf->tf_eip, 
+                              *(uint8_t*)(tf->tf_eip + 0), 
+                              *(uint8_t*)(tf->tf_eip + 1), 
+                              *(uint8_t*)(tf->tf_eip + 2)); 
+                       /* rdtscp: 0f 01 f9 */
+                       if (*(uint8_t*)(tf->tf_eip + 0) == 0x0f, 
+                           *(uint8_t*)(tf->tf_eip + 1) == 0x01, 
+                           *(uint8_t*)(tf->tf_eip + 2) == 0xf9) {
+                               fake_rdtscp(tf);
+                               pcpui->__lock_depth_disabled--; /* for print debugging */
+                               return;
+                       }
+                       enable_irq();
+                       monitor(tf);
+                       pcpui->__lock_depth_disabled--;         /* for print debugging */
+                       break;
                case T_PGFLT:
                        page_fault_handler(tf);
                        break;
@@ -306,7 +341,17 @@ env_pop_ancillary_state(env_t* e)
 }
 
 /* Helper.  For now, this copies out the TF to pcpui.  Eventually, we should
- * consider doing this in trapentry.S */
+ * consider doing this in trapentry.S
+ *
+ * TODO: consider having this return pcpui->cur_tf, so we can set tf in trap and
+ * irq handlers to edit the TF that will get restarted.  Right now, the kernel
+ * uses and restarts tf, but userspace restarts the old pcpui tf.  It is
+ * tempting to do this, but note that tf stays on the stack of the kthread,
+ * while pcpui->cur_tf is for the core we trapped in on.  Meaning if we ever
+ * block, suddenly cur_tf is pointing to some old clobbered state that was
+ * already returned to and can't be trusted.  Meanwhile tf can always be trusted
+ * (like with an in_kernel() check).  The only types of traps from the user that
+ * can be expected to have editable trapframes are ones that don't block. */
 static void set_current_tf(struct per_cpu_info *pcpui, struct trapframe *tf)
 {
        assert(!irq_is_enabled());
index 1479d4f..d1b7d08 100644 (file)
@@ -52,6 +52,13 @@ read_tsc(void)
        return t;
 }
 
+/* Continuing the poor tradition of x86 opcode functions... */
+static __inline uint64_t
+read_tscp(void)
+{
+       return read_tsc();
+}
+
 static __inline uint64_t 
 read_tsc_serialized(void)
 {
index 10ca149..e9c12ba 100644 (file)
@@ -72,6 +72,12 @@ read_tsc(void)
        return read_perfctr(0,0);
 }
 
+static __inline uint64_t
+read_tscp(void)
+{
+       return read_tsc();
+}
+
 static __inline uint64_t 
 read_tsc_serialized(void)
 {
index e8d1c53..36716a6 100644 (file)
@@ -33,6 +33,15 @@ read_tsc(void)
        return tsc;
 }
 
+/* non-core-id reporting style (it is in ecx) */
+static __inline uint64_t
+read_tscp(void)
+{
+       uint64_t tsc;
+       __asm __volatile("rdtscp" : "=A" (tsc) : : "ecx");
+       return tsc;
+}
+
 static __inline void
 cpuid(uint32_t info1, uint32_t info2, uint32_t *eaxp, uint32_t *ebxp,
       uint32_t *ecxp, uint32_t *edxp)
index fae709c..c40f6a6 100644 (file)
@@ -28,6 +28,12 @@ read_tsc(void)
 }
 
 static __inline uint64_t
+read_tscp(void)
+{
+       return read_tsc();
+}
+
+static __inline uint64_t
 read_tsc_serialized(void)
 {
        return read_tsc();
index ad0f471..2f8df22 100644 (file)
@@ -44,6 +44,12 @@ read_tsc(void)
 }
 
 static __inline uint64_t
+read_tscp(void)
+{
+       return read_tsc();
+}
+
+static __inline uint64_t
 read_tsc_serialized(void)
 {
        return read_tsc();