Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[PATCH] MIPS: Add a high precision sched_clock() via cnt32_to_63().

1 view
Skip to first unread message

Wu Zhangjin

unread,
Nov 20, 2009, 2:54:12 AM11/20/09
to Ralf Baechle, Thomas Gleixner, ros...@goodmis.org, Frederic Weisbecker, Nicholas Mc Guire, David Daney, linux...@linux-mips.org, Michal Simek, Ingo Molnar, linux-...@vger.kernel.org, Wu Zhangjin
From: Wu Zhangjin <wuzha...@gmail.com>

MIPS uses the jiffies based sched_clock(), the precision is very
low(about 10ms with 1000 HZ), which is not enough for some places. a
very obvious example is Ftracer.

Ftracer is originally designed for real time tracing, but without a high
precision sched_clock(), the timestamp information will be totally
garbage. So, we need to find a high precision sched_clock().

In the past series of patchset of "ftrace for MIPS", I have implemented
such a sched_clock(), which is based on the 32-bit long MIPS c0 count
and the timecounter/cyclecounter stuff in include/linux/clocksource.h.

That sched_clock() did work well for Ftracer, but touched several places
of the core of Ftracer. and also, that one is only enabled for Ftracer.

In the v8 revision of "ftrace for MIPS", Thomas Gleixner recommended to
enable sched_clock() for the whole system and re-implement it via the
cnt32_to_63() stuff in include/linux/cnt32_to_63.h.

Just had a look at the cnt32_to_64() and some implementations in
arch/arm/, and then cloned one for MIPS from arch/arm/plat-orion/time.c.

Of course, this is a "rude" version, perhaps we need to consider more
for different MIPSs(something like what have been done in
arch/arm/plat-orion/time.c). and also, the overhead is needed to
be measured. Herein just list the difference between the old
jiffes based one and this new one:

1. jiffies-based:

unsigned long long __attribute__((weak)) sched_clock(void)
{
return (unsigned long long)(jiffies - INITIAL_JIFFIES)
* (NSEC_PER_SEC / HZ);
}

2. cnt32_to_63() and read_c0_count() based one:

unsigned long long notrace sched_clock(void)
{
unsigned long long v = cnt32_to_63(read_c0_count());
return (v * tclk2ns_scale) >> tclk2ns_scale_factor;
}

#define cnt32_to_63(cnt_lo) \
({ \
static u32 __m_cnt_hi; \
union cnt32_to_63 __x; \
__x.hi = __m_cnt_hi; \
smp_rmb(); \
__x.lo = (cnt_lo); \
if (unlikely((s32)(__x.hi ^ __x.lo) < 0)) \
__m_cnt_hi = __x.hi = (__x.hi ^ 0x80000000) + (__x.hi >> 31); \
__x.val; \
})

#define read_c0_count() __read_32bit_c0_register($9, 0)
#define __read_32bit_c0_register(source, sel) \
({ int __res; \
if (sel == 0) \
__asm__ __volatile__( \
"mfc0\t%0, " #source "\n\t" \
: "=r" (__res)); \
else \
__asm__ __volatile__( \
".set\tmips32\n\t" \
"mfc0\t%0, " #source ", " #sel "\n\t" \
".set\tmips0\n\t" \
: "=r" (__res)); \
__res; \
})

NOTE: An exisiting problem is with this new sched_clock(), we can not
always get tracing result(no result of "cat /debug/tracing/trace") of
function graph tracer. Not sure whether it is relative to this
sched_clock()!

(I will split this patch out of the patchset "ftrace for MIPS" and
resend the patchset as v9 later for this one is really different from
the other patches)

Signed-off-by: Wu Zhangjin <wuzha...@gmail.com>
---
arch/mips/kernel/csrc-r4k.c | 37 +++++++++++++++++++++++++++++++++++++
1 files changed, 37 insertions(+), 0 deletions(-)

diff --git a/arch/mips/kernel/csrc-r4k.c b/arch/mips/kernel/csrc-r4k.c
index e95a3cd..865035d 100644
--- a/arch/mips/kernel/csrc-r4k.c
+++ b/arch/mips/kernel/csrc-r4k.c
@@ -6,10 +6,45 @@
* Copyright (C) 2007 by Ralf Baechle
*/
#include <linux/clocksource.h>
+#include <linux/cnt32_to_63.h>
+#include <linux/timer.h>
#include <linux/init.h>

#include <asm/time.h>

+/*
+ * MIPS' sched_clock implementation.
+ *
+ * NOTE: because cnt32_to_63() needs to be called at least once per half period
+ * to work properly, and some of the MIPS' frequency is very low, perhaps a
+ * kernel timer is needed to be set up to ensure this requirement is always
+ * met. please refer to arch/arm/plat-orion/time.c and
+ * include/linux/cnt32_to_63.h
+ */
+static unsigned long tclk2ns_scale, tclk2ns_scale_factor;
+
+unsigned long long notrace sched_clock(void)
+{
+ unsigned long long v = cnt32_to_63(read_c0_count());
+ return (v * tclk2ns_scale) >> tclk2ns_scale_factor;
+}
+
+static void __init setup_sched_clock(struct clocksource *cs, unsigned long tclk)
+{
+ unsigned long long v;
+
+ v = cs->mult;
+ /*
+ * We want an even value to automatically clear the top bit
+ * returned by cnt32_to_63() without an additional run time
+ * instruction. So if the LSB is 1 then round it up.
+ */
+ if (v & 1)
+ v++;
+ tclk2ns_scale = v;
+ tclk2ns_scale_factor = cs->shift;
+}
+
static cycle_t c0_hpt_read(struct clocksource *cs)
{
return read_c0_count();
@@ -32,6 +67,8 @@ int __init init_r4k_clocksource(void)

clocksource_set_clock(&clocksource_mips, mips_hpt_frequency);

+ setup_sched_clock(&clocksource_mips, mips_hpt_frequency);
+
clocksource_register(&clocksource_mips);

return 0;
--
1.6.2.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majo...@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Wu Zhangjin

unread,
Nov 20, 2009, 8:21:17 AM11/20/09
to Ralf Baechle, Thomas Gleixner, ros...@goodmis.org, Frederic Weisbecker, Nicholas Mc Guire, David Daney, linux...@linux-mips.org, Michal Simek, Ingo Molnar, linux-...@vger.kernel.org
[...]

> ---
> arch/mips/kernel/csrc-r4k.c | 37 +++++++++++++++++++++++++++++++++++++
> 1 files changed, 37 insertions(+), 0 deletions(-)
>
> diff --git a/arch/mips/kernel/csrc-r4k.c b/arch/mips/kernel/csrc-r4k.c
> index e95a3cd..865035d 100644
> --- a/arch/mips/kernel/csrc-r4k.c
> +++ b/arch/mips/kernel/csrc-r4k.c
> @@ -6,10 +6,45 @@
> * Copyright (C) 2007 by Ralf Baechle
> */
> #include <linux/clocksource.h>
> +#include <linux/cnt32_to_63.h>
> +#include <linux/timer.h>

Sorry, <linux/timer.h> is not needed here, I have used it in the old
version with setup_timer()/mod_timer().

> #include <linux/init.h>
>
> #include <asm/time.h>
>
> +/*
> + * MIPS' sched_clock implementation.
> + *
> + * NOTE: because cnt32_to_63() needs to be called at least once per half period
> + * to work properly, and some of the MIPS' frequency is very low, perhaps a
> + * kernel timer is needed to be set up to ensure this requirement is always
> + * met. please refer to arch/arm/plat-orion/time.c and
> + * include/linux/cnt32_to_63.h
> + */
> +static unsigned long tclk2ns_scale, tclk2ns_scale_factor;
> +
> +unsigned long long notrace sched_clock(void)
> +{
> + unsigned long long v = cnt32_to_63(read_c0_count());
> + return (v * tclk2ns_scale) >> tclk2ns_scale_factor;
> +}
> +
> +static void __init setup_sched_clock(struct clocksource *cs, unsigned long tclk)

the tclk is not used, I have also used it in the old version with
setup_timer()/mod_timer(). so, we can remove it for this version.

> +{
> + unsigned long long v;
> +
> + v = cs->mult;
> + /*
> + * We want an even value to automatically clear the top bit
> + * returned by cnt32_to_63() without an additional run time
> + * instruction. So if the LSB is 1 then round it up.
> + */
> + if (v & 1)
> + v++;
> + tclk2ns_scale = v;
> + tclk2ns_scale_factor = cs->shift;
> +}
> +
> static cycle_t c0_hpt_read(struct clocksource *cs)
> {
> return read_c0_count();
> @@ -32,6 +67,8 @@ int __init init_r4k_clocksource(void)
>
> clocksource_set_clock(&clocksource_mips, mips_hpt_frequency);
>
> + setup_sched_clock(&clocksource_mips, mips_hpt_frequency);
> +

And here should be setup_sched_clock(&clocksource_mips);

Regards,
Wu Zhangjin

Wu Zhangjin

unread,
Nov 21, 2009, 9:49:46 AM11/21/09
to Ralf Baechle, Thomas Gleixner, ros...@goodmis.org, Frederic Weisbecker, Nicholas Mc Guire, David Daney, linux...@linux-mips.org, Michal Simek, Ingo Molnar, linux-...@vger.kernel.org, Wu Zhangjin
From: Wu Zhangjin <wuzha...@gmail.com>

This patch adds a cnt32_to_63() and MIPS c0 count based sched_clock(),
which can provide high resolution. and also, two new kernel options are
added. the HR_SCHED_CLOCK is used to enable/disable this sched_clock(),
and the HT_SCHED_CLOCK_UPDATE is used to allow whether update the
sched_clock() automatically or not.

Without it, the Ftrace for MIPS will give useless timestamp information.

(Because cnt32_to_63() needs to be called at least once per half period
to work properly, Differ from the old version, this v1 revision set up a
kernel timer to ensure the requirement of some MIPSs which have short c0
count period.)

Signed-off-by: Wu Zhangjin <wuzha...@gmail.com>
---

arch/mips/Kconfig | 30 ++++++++++++++++++++++
arch/mips/kernel/csrc-r4k.c | 58 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 88 insertions(+), 0 deletions(-)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index b342197..6264f97 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -1952,6 +1952,36 @@ config NR_CPUS
source "kernel/time/Kconfig"

#
+# High Resolution sched_clock() Configuration
+#
+
+config HR_SCHED_CLOCK
+ bool "High Resolution sched_clock()"
+ depends on CSRC_R4K
+ default n
+ help
+ This option enables the MIPS c0 count based high resolution
+ sched_clock().
+
+ If you need a ns precision timestamp, You are recommended to enable
+ this option. For example, If you are using the Ftrace subsystem to do
+ real time tracing, this option is needed.
+
+ If unsure, disable it.
+
+config HR_SCHED_CLOCK_UPDATE
+ bool "Update sched_clock() automatically"
+ depends on HR_SCHED_CLOCK
+ default y
+ help
+ Because Some of the MIPS c0 count period is quite short and because
+ cnt32_to_63() needs to be called at least once per half period to
+ work properly, a kernel timer is needed to set up to ensure this
+ requirement is always met.
+
+ If unusre, enable it.
+
+#
# Timer Interrupt Frequency Configuration
#

diff --git a/arch/mips/kernel/csrc-r4k.c b/arch/mips/kernel/csrc-r4k.c
index e95a3cd..5048989 100644
--- a/arch/mips/kernel/csrc-r4k.c
+++ b/arch/mips/kernel/csrc-r4k.c
@@ -6,10 +6,62 @@


* Copyright (C) 2007 by Ralf Baechle
*/
#include <linux/clocksource.h>
+#include <linux/cnt32_to_63.h>
+#include <linux/timer.h>

#include <linux/init.h>

#include <asm/time.h>

+/*
+ * MIPS' sched_clock implementation.
+ *

+ * because cnt32_to_63() needs to be called at least once per half period to
+ * work properly, and some of the MIPS' frequency is very low, perhaps a kernel
+ * timer is needed to be set up to ensure this requirement is always met.
+ * please refer to arch/arm/plat-orion/time.c and include/linux/cnt32_to_63.h
+ */
+static unsigned long __maybe_unused tclk2ns_scale;
+static unsigned long __maybe_unused tclk2ns_scale_factor;
+
+unsigned long long notrace __maybe_unused sched_clock(void)


+{
+ unsigned long long v = cnt32_to_63(read_c0_count());
+ return (v * tclk2ns_scale) >> tclk2ns_scale_factor;
+}
+

+static void __init __maybe_unused setup_sched_clock(struct clocksource *cs)


+{
+ unsigned long long v;
+
+ v = cs->mult;
+ /*
+ * We want an even value to automatically clear the top bit
+ * returned by cnt32_to_63() without an additional run time
+ * instruction. So if the LSB is 1 then round it up.
+ */
+ if (v & 1)
+ v++;
+ tclk2ns_scale = v;
+ tclk2ns_scale_factor = cs->shift;
+}
+

+static struct timer_list __maybe_unused cnt32_to_63_keepwarm_timer;
+
+static void __maybe_unused cnt32_to_63_keepwarm(unsigned long data)
+{
+ mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + data));
+ (void) sched_clock();
+}
+
+static void __maybe_unused setup_sched_clock_update(unsigned long tclk)
+{
+ unsigned long data;
+
+ data = (0xffffffffUL / tclk / 2 - 2) * HZ;
+ setup_timer(&cnt32_to_63_keepwarm_timer, cnt32_to_63_keepwarm, data);
+ mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + data));


+}
+
static cycle_t c0_hpt_read(struct clocksource *cs)
{
return read_c0_count();

@@ -32,7 +84,13 @@ int __init init_r4k_clocksource(void)

clocksource_set_clock(&clocksource_mips, mips_hpt_frequency);

+#ifdef CONFIG_HR_SCHED_CLOCK
+ setup_sched_clock(&clocksource_mips);
+#endif
clocksource_register(&clocksource_mips);

+#ifdef CONFIG_HR_SCHED_CLOCK_UPDATE
+ setup_sched_clock_update(mips_hpt_frequency);
+#endif
return 0;
}
--
1.6.2.1

Wu Zhangjin

unread,
Nov 22, 2009, 12:31:02 AM11/22/09
to Ralf Baechle, Thomas Gleixner, ros...@goodmis.org, Frederic Weisbecker, Nicholas Mc Guire, David Daney, linux...@linux-mips.org, Michal Simek, Ingo Molnar, linux-...@vger.kernel.org
On Sat, 2009-11-21 at 22:49 +0800, Wu Zhangjin wrote:
> From: Wu Zhangjin <wuzha...@gmail.com>
>
> This patch adds a cnt32_to_63() and MIPS c0 count based sched_clock(),
> which can provide high resolution. and also, two new kernel options are
> added. the HR_SCHED_CLOCK is used to enable/disable this sched_clock(),
> and the HT_SCHED_CLOCK_UPDATE is used to allow whether update the
> sched_clock() automatically or not.
>
> Without it, the Ftrace for MIPS will give useless timestamp information.
>
> (Because cnt32_to_63() needs to be called at least once per half period
> to work properly, Differ from the old version, this v1 revision set up a
> kernel timer to ensure the requirement of some MIPSs which have short c0
> count period.)
[...]

>
> diff --git a/arch/mips/kernel/csrc-r4k.c b/arch/mips/kernel/csrc-r4k.c
> index e95a3cd..5048989 100644
> --- a/arch/mips/kernel/csrc-r4k.c
> +++ b/arch/mips/kernel/csrc-r4k.c
> @@ -6,10 +6,62 @@
> * Copyright (C) 2007 by Ralf Baechle
> */
> #include <linux/clocksource.h>
> +#include <linux/cnt32_to_63.h>
> +#include <linux/timer.h>
> #include <linux/init.h>
>
> #include <asm/time.h>
>
> +/*
> + * MIPS' sched_clock implementation.
> + *
> + * because cnt32_to_63() needs to be called at least once per half period to
> + * work properly, and some of the MIPS' frequency is very low, perhaps a kernel
> + * timer is needed to be set up to ensure this requirement is always met.
> + * please refer to arch/arm/plat-orion/time.c and include/linux/cnt32_to_63.h
> + */
> +static unsigned long __maybe_unused tclk2ns_scale;
> +static unsigned long __maybe_unused tclk2ns_scale_factor;
> +

need to be:

#ifdef CONFIG_HR_SCHED_CLOCK
> +unsigned long long notrace sched_clock(void)


> +{
> + unsigned long long v = cnt32_to_63(read_c0_count());
> + return (v * tclk2ns_scale) >> tclk2ns_scale_factor;
> +}
> +

#endif

Regards,
Wu Zhangjin

0 new messages