cputimer: Allow MP synchronized TSC to become cputimer on x86_64
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 21 Jun 2013 01:26:37 +0000 (09:26 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 21 Jun 2013 01:43:00 +0000 (09:43 +0800)
sysclock_t stays as 32bits, as suggested by dillon@.

The TSC cputimer frequency is scaled down TSC frequency (less or equal
to 128Mhz), which makes 32bits sysclock_t counter hold at least 33
seconds.

The TSC cputimer is obviously faster than other cputimers, e.g. HPET or
ACPI timer, so it has the highest priority as of this commit.  It is
enabled by default, if MP synchronized TSC is detected.  It could be
disabled by setting tunable hw.tsc_cputimer_enable to 0.

sys/platform/pc64/isa/clock.c
sys/sys/systimer.h

index 836591a..ba6558c 100644 (file)
@@ -150,6 +150,23 @@ static struct cputimer     i8254_cputimer = {
     0, 0, 0
 };
 
+static sysclock_t tsc_cputimer_count(void);
+static void tsc_cputimer_construct(struct cputimer *, sysclock_t);
+
+static struct cputimer tsc_cputimer = {
+    SLIST_ENTRY_INITIALIZER,
+    "TSC",
+    CPUTIMER_PRI_TSC,
+    CPUTIMER_TSC,
+    tsc_cputimer_count,
+    cputimer_default_fromhz,
+    cputimer_default_fromus,
+    tsc_cputimer_construct,
+    cputimer_default_destruct,
+    0,
+    0, 0, 0
+};
+
 static void i8254_intr_reload(struct cputimer_intr *, sysclock_t);
 static void i8254_intr_config(struct cputimer_intr *, const struct cputimer *);
 static void i8254_intr_initclock(struct cputimer_intr *, boolean_t);
@@ -1257,6 +1274,57 @@ tsc_mpsync_test(void)
 }
 SYSINIT(tsc_mpsync, SI_BOOT2_FINISH_SMP, SI_ORDER_ANY, tsc_mpsync_test, NULL);
 
+#define TSC_CPUTIMER_FREQMAX   128000000       /* 128Mhz */
+
+static int tsc_cputimer_shift;
+
+static void
+tsc_cputimer_construct(struct cputimer *timer, sysclock_t oldclock)
+{
+       timer->base = 0;
+       timer->base = oldclock - tsc_cputimer_count();
+}
+
+static sysclock_t
+tsc_cputimer_count(void)
+{
+       uint64_t tsc;
+
+       tsc = rdtsc();
+       tsc >>= tsc_cputimer_shift;
+
+       return (tsc + tsc_cputimer.base);
+}
+
+static void
+tsc_cputimer_register(void)
+{
+       uint64_t freq;
+       int enable = 1;
+
+       if (!tsc_mpsync)
+               return;
+
+       TUNABLE_INT_FETCH("hw.tsc_cputimer_enable", &enable);
+       if (!enable)
+               return;
+
+       freq = tsc_frequency;
+       while (freq > TSC_CPUTIMER_FREQMAX) {
+               freq >>= 1;
+               ++tsc_cputimer_shift;
+       }
+       kprintf("TSC: cputimer freq %ju, shift %d\n",
+           (uintmax_t)freq, tsc_cputimer_shift);
+
+       tsc_cputimer.freq = freq;
+
+       cputimer_register(&tsc_cputimer);
+       cputimer_select(&tsc_cputimer, 0);
+}
+SYSINIT(tsc_cputimer_reg, SI_BOOT2_MACHDEP, SI_ORDER_ANY,
+    tsc_cputimer_register, NULL);
+
 SYSCTL_NODE(_hw, OID_AUTO, i8254, CTLFLAG_RW, 0, "I8254");
 SYSCTL_UINT(_hw_i8254, OID_AUTO, freq, CTLFLAG_RD, &i8254_cputimer.freq, 0,
            "frequency");
index 33f5bc0..fc24067 100644 (file)
@@ -128,6 +128,7 @@ extern struct cputimer *sys_cputimer;
 #define CPUTIMER_HPET          5
 #define CPUTIMER_GEODE         6
 #define CPUTIMER_CS5536                7
+#define CPUTIMER_TSC           8
 
 #define CPUTIMER_PRI_DUMMY     -10
 #define CPUTIMER_PRI_8254      0
@@ -136,6 +137,7 @@ extern struct cputimer *sys_cputimer;
 #define CPUTIMER_PRI_CS5536    30
 #define CPUTIMER_PRI_GEODE     40
 #define CPUTIMER_PRI_VKERNEL   200
+#define CPUTIMER_PRI_TSC       250
 
 void cputimer_select(struct cputimer *, int);
 void cputimer_register(struct cputimer *);