# HG changeset patch
# User Jimi Xenidis <jimix@xxxxxxxxxxxxxx>
# Node ID e20a469dabb42c8731171556f99e076a7981d2bf
# Parent 464acece0dad70663f8658ec555db5564ba126c2
[POWERPC][XEN] Synchronize Timebase on all CPUs
This patch add the necessary support to use
arch/powerpc/kernel/smp-tbsync.c
from Linux.
Original copyright and author are:
Copyright (C) 2003 Samuel Rydh (samuel@xxxxxxxxx)
Signed-off-by: Jimi Xenidis <jimix@xxxxxxxxxxxxxx>
Signed-off-by: Hollis Blanchard <hollisb@xxxxxxxxxx>
---
xen/arch/powerpc/Makefile | 1
xen/arch/powerpc/exceptions.h | 1
xen/arch/powerpc/setup.c | 10 +
xen/arch/powerpc/smp-tbsync.c | 186 ++++++++++++++++++++++++++
xen/include/asm-powerpc/powerpc64/processor.h | 11 +
xen/include/asm-powerpc/reg_defs.h | 4
xen/include/asm-powerpc/smp.h | 4
xen/include/asm-powerpc/time.h | 9 +
8 files changed, 223 insertions(+), 3 deletions(-)
diff -r 464acece0dad -r e20a469dabb4 xen/arch/powerpc/Makefile
--- a/xen/arch/powerpc/Makefile Thu Sep 07 02:21:17 2006 -0400
+++ b/xen/arch/powerpc/Makefile Thu Sep 07 02:50:06 2006 -0400
@@ -36,6 +36,7 @@ obj-y += setup.o
obj-y += setup.o
obj-y += shadow.o
obj-y += smp.o
+obj-y += smp-tbsync.o
obj-y += time.o
obj-y += usercopy.o
diff -r 464acece0dad -r e20a469dabb4 xen/arch/powerpc/exceptions.h
--- a/xen/arch/powerpc/exceptions.h Thu Sep 07 02:21:17 2006 -0400
+++ b/xen/arch/powerpc/exceptions.h Thu Sep 07 02:50:06 2006 -0400
@@ -51,4 +51,5 @@ extern char exception_vectors[];
extern char exception_vectors[];
extern char exception_vectors_end[];
extern int spin_start[];
+extern int secondary_cpu_init(int cpuid, unsigned long r4);
#endif
diff -r 464acece0dad -r e20a469dabb4 xen/arch/powerpc/setup.c
--- a/xen/arch/powerpc/setup.c Thu Sep 07 02:21:17 2006 -0400
+++ b/xen/arch/powerpc/setup.c Thu Sep 07 02:50:06 2006 -0400
@@ -16,6 +16,8 @@
* Copyright (C) IBM Corp. 2005, 2006
*
* Authors: Jimi Xenidis <jimix@xxxxxxxxxxxxxx>
+ * Amos Waterland <apw@xxxxxxxxxx>
+ * Hollis Blanchard <hollisb@xxxxxxxxxx>
*/
#include <xen/config.h>
@@ -242,16 +244,22 @@ static int kick_secondary_cpus(int maxcp
break;
init_parea(cpuid);
cpu_set(cpuid, cpu_online_map);
+ smp_generic_give_timebase();
+
+ /* wait for it */
+ while (!cpu_online(cpuid))
+ cpu_relax();
}
return 0;
}
/* This is the first C code that secondary processors invoke. */
-int secondary_cpu_init(int cpuid, unsigned long r4);
int secondary_cpu_init(int cpuid, unsigned long r4)
{
cpu_initialize(cpuid);
+ smp_generic_take_timebase();
+ cpu_set(cpuid, cpu_online_map);
while(1);
}
diff -r 464acece0dad -r e20a469dabb4
xen/include/asm-powerpc/powerpc64/processor.h
--- a/xen/include/asm-powerpc/powerpc64/processor.h Thu Sep 07 02:21:17
2006 -0400
+++ b/xen/include/asm-powerpc/powerpc64/processor.h Thu Sep 07 02:50:06
2006 -0400
@@ -91,10 +91,21 @@ static inline unsigned long mftb(void)
return tb;
}
+static inline void mttbl(unsigned low)
+{
+ __asm__ __volatile__ ("mtspr %0, %1" : : "i"(SPRN_TBWL), "r" (low));
+}
+
+static inline void mttbu(unsigned upper)
+{
+ __asm__ __volatile__ ("mtspr %0, %1" : : "i"(SPRN_TBWU), "r" (upper));
+}
+
static inline void mthdec(unsigned ticks)
{
__asm__ __volatile__ ("mtspr %0, %1" : : "i"(SPRN_HDEC), "r" (ticks));
}
+
static inline unsigned int mfhdec(void)
{
unsigned int val;
diff -r 464acece0dad -r e20a469dabb4 xen/include/asm-powerpc/reg_defs.h
--- a/xen/include/asm-powerpc/reg_defs.h Thu Sep 07 02:21:17 2006 -0400
+++ b/xen/include/asm-powerpc/reg_defs.h Thu Sep 07 02:50:06 2006 -0400
@@ -146,10 +146,14 @@
#define SPRN_DEC 22
#define SPRN_SRR0 26
#define SPRN_SRR1 27
+#define SPRN_TBRL 268
+#define SPRN_TBRU 269
#define SPRN_SPRG0 272
#define SPRN_SPRG1 273
#define SPRN_SPRG2 274
#define SPRN_SPRG3 275
+#define SPRN_TBWL 284
+#define SPRN_TBWU 285
#define SPRN_HSPRG0 304
#define SPRN_HSPRG1 305
diff -r 464acece0dad -r e20a469dabb4 xen/include/asm-powerpc/smp.h
--- a/xen/include/asm-powerpc/smp.h Thu Sep 07 02:21:17 2006 -0400
+++ b/xen/include/asm-powerpc/smp.h Thu Sep 07 02:50:06 2006 -0400
@@ -23,6 +23,7 @@
#include <xen/types.h>
#include <xen/cpumask.h>
+#include <xen/init.h>
#include <asm/current.h>
extern int smp_num_siblings;
@@ -32,5 +33,6 @@ extern int smp_num_siblings;
#define hard_smp_processor_id() raw_smp_processor_id()
extern cpumask_t cpu_sibling_map[];
extern cpumask_t cpu_core_map[];
-
+extern void __devinit smp_generic_take_timebase(void);
+extern void __devinit smp_generic_give_timebase(void);
#endif
diff -r 464acece0dad -r e20a469dabb4 xen/include/asm-powerpc/time.h
--- a/xen/include/asm-powerpc/time.h Thu Sep 07 02:21:17 2006 -0400
+++ b/xen/include/asm-powerpc/time.h Thu Sep 07 02:50:06 2006 -0400
@@ -16,7 +16,7 @@
* Copyright (C) IBM Corp. 2005, 2006
*
* Authors: Hollis Blanchard <hollisb@xxxxxxxxxx>
- * Hollis Blanchard <jimix@xxxxxxxxxxxxxx>
+ * Jimi Xenidis <jimix@xxxxxxxxxxxxxx>
*/
#ifndef _ASM_TIME_H_
@@ -54,6 +54,13 @@ static inline u64 get_timebase(void)
return s;
}
+static inline void set_timebase(unsigned upper, unsigned lower)
+{
+ mttbl(0);
+ mttbu(upper);
+ mttbl(lower);
+}
+
typedef u64 cycles_t;
static inline cycles_t get_cycles(void)
{
diff -r 464acece0dad -r e20a469dabb4 xen/arch/powerpc/smp-tbsync.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/arch/powerpc/smp-tbsync.c Thu Sep 07 02:50:06 2006 -0400
@@ -0,0 +1,186 @@
+/*
+ * Smp timebase synchronization for ppc.
+ *
+ * Copyright (C) 2003 Samuel Rydh (samuel@xxxxxxxxx)
+ *
+ */
+/* XXX Xen hacks ... */
+#define get_tb() get_timebase()
+#define set_tb(u,l) set_timebase(u,l)
+#define kmalloc(s,f) xmalloc_bytes(s);
+#define kfree(p) xfree(p)
+#define abs(x) ({ \
+ int __x = (x); \
+ (__x < 0) ? -__x : __x; \
+ })
+
+#include <xen/kernel.h>
+#include <xen/sched.h>
+#include <xen/smp.h>
+#ifndef __XEN__
+#include <linux/unistd.h>
+#endif
+#include <xen/init.h>
+#include <asm/atomic.h>
+#include <asm/smp.h>
+#include <asm/time.h>
+
+
+/* don't mess with IRQs */
+#define local_irq_enable()
+#define local_irq_disable()
+
+#define NUM_ITER 300
+
+enum {
+ kExit=0, kSetAndTest, kTest
+};
+
+static struct {
+ volatile u64 tb;
+ volatile u64 mark;
+ volatile int cmd;
+ volatile int handshake;
+ int filler[2];
+
+ volatile int ack;
+ int filler2[7];
+
+ volatile int race_result;
+} *tbsync;
+
+static volatile int running;
+
+static void __devinit enter_contest(u64 mark, long add)
+{
+ while (get_tb() < mark)
+ tbsync->race_result = add;
+}
+
+void __devinit smp_generic_take_timebase(void)
+{
+ int cmd;
+ u64 tb;
+
+ local_irq_disable();
+ while (!running)
+ barrier();
+ rmb();
+
+ for (;;) {
+ tbsync->ack = 1;
+ while (!tbsync->handshake)
+ barrier();
+ rmb();
+
+ cmd = tbsync->cmd;
+ tb = tbsync->tb;
+ mb();
+ tbsync->ack = 0;
+ if (cmd == kExit)
+ break;
+
+ while (tbsync->handshake)
+ barrier();
+ if (cmd == kSetAndTest)
+ set_tb(tb >> 32, tb & 0xfffffffful);
+ enter_contest(tbsync->mark, -1);
+ }
+ local_irq_enable();
+}
+
+static int __devinit start_contest(int cmd, long offset, int num)
+{
+ int i, score=0;
+ u64 tb;
+ long mark;
+
+ tbsync->cmd = cmd;
+
+ local_irq_disable();
+ for (i = -3; i < num; ) {
+ tb = get_tb() + 400;
+ tbsync->tb = tb + offset;
+ tbsync->mark = mark = tb + 400;
+
+ wmb();
+
+ tbsync->handshake = 1;
+ while (tbsync->ack)
+ barrier();
+
+ while (get_tb() <= tb)
+ barrier();
+ tbsync->handshake = 0;
+ enter_contest(mark, 1);
+
+ while (!tbsync->ack)
+ barrier();
+
+ if (i++ > 0)
+ score += tbsync->race_result;
+ }
+ local_irq_enable();
+ return score;
+}
+
+void __devinit smp_generic_give_timebase(void)
+{
+ int i, score, score2, old, min=0, max=5000, offset=1000;
+
+ printk("Synchronizing timebase\n");
+
+ /* if this fails then this kernel won't work anyway... */
+ tbsync = kmalloc( sizeof(*tbsync), GFP_KERNEL );
+ memset( tbsync, 0, sizeof(*tbsync) );
+ mb();
+ running = 1;
+
+ while (!tbsync->ack)
+ barrier();
+
+ printk("Got ack\n");
+
+ /* binary search */
+ for (old = -1; old != offset ; offset = (min+max) / 2) {
+ score = start_contest(kSetAndTest, offset, NUM_ITER);
+
+ printk("score %d, offset %d\n", score, offset );
+
+ if( score > 0 )
+ max = offset;
+ else
+ min = offset;
+ old = offset;
+ }
+ score = start_contest(kSetAndTest, min, NUM_ITER);
+ score2 = start_contest(kSetAndTest, max, NUM_ITER);
+
+ printk("Min %d (score %d), Max %d (score %d)\n",
+ min, score, max, score2);
+ score = abs(score);
+ score2 = abs(score2);
+ offset = (score < score2) ? min : max;
+
+ /* guard against inaccurate mttb */
+ for (i = 0; i < 10; i++) {
+ start_contest(kSetAndTest, offset, NUM_ITER/10);
+
+ if ((score2 = start_contest(kTest, offset, NUM_ITER)) < 0)
+ score2 = -score2;
+ if (score2 <= score || score2 < 20)
+ break;
+ }
+ printk("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER );
+
+ /* exiting */
+ tbsync->cmd = kExit;
+ wmb();
+ tbsync->handshake = 1;
+ while (tbsync->ack)
+ barrier();
+ tbsync->handshake = 0;
+ kfree(tbsync);
+ tbsync = NULL;
+ running = 0;
+}
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|