[PATCH] RISC-V: Resurrect the MMIO timer implementation for M-mode systems

Damien Le Moal Damien.LeMoal at wdc.com
Tue Sep 15 04:25:43 EDT 2020


On Mon, 2020-09-14 at 09:56 -0700, Palmer Dabbelt wrote:
> The K210 doesn't implement rdtime in M-mode, and since that's where Linux runs
> in the NOMMU systems that means we can't use rdtime.  The K210 is the only
> system that anyone is currently running NOMMU or M-mode on, so here we're just
> inlining the timer read directly.
> 
> Signed-off-by: Palmer Dabbelt <palmerdabbelt at google.com>
> ---
> I don't actually have a K210 so I haven't tested this.  If nobody else has the
> time to I'll put together a QEMU that doesn't support rdtime in M-mode, but
> I've yet to mess around with the !MMU stuff so that might take a little while.
> This certainly doesn't seem worse than what's there right now, though, as
> rdtime isn't valid in M-mode on the K210 (our only M-mode platform).
> ---
>  arch/riscv/include/asm/clint.h    | 26 ++++++++++++++++++++++++++
>  arch/riscv/include/asm/timex.h    | 27 +++++++++++++++++++++++++++
>  drivers/clocksource/timer-clint.c | 17 +++++++++++++++++
>  3 files changed, 70 insertions(+)
>  create mode 100644 arch/riscv/include/asm/clint.h
> 
> diff --git a/arch/riscv/include/asm/clint.h b/arch/riscv/include/asm/clint.h
> new file mode 100644
> index 000000000000..0789fd37b40a
> --- /dev/null
> +++ b/arch/riscv/include/asm/clint.h
> @@ -0,0 +1,26 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2020 Google, Inc
> + */
> +
> +#ifndef _ASM_RISCV_CLINT_H
> +#define _ASM_RISCV_CLINT_H
> +
> +#include <linux/types.h>
> +#include <asm/mmio.h>
> +
> +#ifdef CONFIG_RISCV_M_MODE
> +/*
> + * This lives in the CLINT driver, but is accessed directly by timex.h to avoid
> + * any overhead when accessing the MMIO timer.
> + *
> + * The ISA defines mtime as a 64-bit memory-mapped register that increments at
> + * a constant frequency, but it doesn't define some other constraints we depend
> + * on (most notably ordering constraints, but also some simpler stuff like the
> + * memory layout).  Thus, this is called "clint_time_val" instead of something
> + * like "riscv_mtime", to signify that these non-ISA assumptions must hold.
> + */
> +extern u64 __iomem *clint_time_val;
> +#endif
> +
> +#endif
> diff --git a/arch/riscv/include/asm/timex.h b/arch/riscv/include/asm/timex.h
> index a3fb85d505d4..7f659dda0032 100644
> --- a/arch/riscv/include/asm/timex.h
> +++ b/arch/riscv/include/asm/timex.h
> @@ -10,6 +10,31 @@
>  
>  typedef unsigned long cycles_t;
>  
> +#ifdef CONFIG_RISCV_M_MODE
> +
> +#include <asm/clint.h>
> +
> +#ifdef CONFIG_64BIT
> +static inline cycles_t get_cycles(void)
> +{
> +	return readq_relaxed(clint_time_val);
> +}
> +#else /* !CONFIG_64BIT */
> +static inline u32 get_cycles(void)
> +{
> +	return readl_relaxed(((u32 *)clint_time_val));
> +}
> +#define get_cycles get_cycles
> +
> +static inline u32 get_cycles_hi(void)
> +{
> +	return readl_relaxed(((u32 *)clint_time_val) + 1);
> +}
> +#define get_cycles_hi get_cycles_hi
> +#endif /* CONFIG_64BIT */
> +
> +#else /* CONFIG_RISCV_M_MODE */
> +
>  static inline cycles_t get_cycles(void)
>  {
>  	return csr_read(CSR_TIME);
> @@ -41,6 +66,8 @@ static inline u64 get_cycles64(void)
>  }
>  #endif /* CONFIG_64BIT */
>  
> +#endif /* !CONFIG_RISCV_M_MODE */
> +
>  #define ARCH_HAS_READ_CURRENT_TIMER
>  static inline int read_current_timer(unsigned long *timer_val)
>  {
> diff --git a/drivers/clocksource/timer-clint.c b/drivers/clocksource/timer-clint.c
> index 8eeafa82c03d..d17367dee02c 100644
> --- a/drivers/clocksource/timer-clint.c
> +++ b/drivers/clocksource/timer-clint.c
> @@ -19,6 +19,11 @@
>  #include <linux/interrupt.h>
>  #include <linux/of_irq.h>
>  #include <linux/smp.h>
> +#include <linux/timex.h>
> +
> +#ifndef CONFIG_RISCV_M_MODE
> +#include <asm/clint.h>
> +#endif
>  
>  #define CLINT_IPI_OFF		0
>  #define CLINT_TIMER_CMP_OFF	0x4000
> @@ -31,6 +36,10 @@ static u64 __iomem *clint_timer_val;
>  static unsigned long clint_timer_freq;
>  static unsigned int clint_timer_irq;
>  
> +#ifdef CONFIG_RISCV_M_MODE
> +u64 __iomem *clint_time_val;
> +#endif
> +
>  static void clint_send_ipi(const struct cpumask *target)
>  {
>  	unsigned int cpu;
> @@ -184,6 +193,14 @@ static int __init clint_timer_init_dt(struct device_node *np)
>  	clint_timer_val = base + CLINT_TIMER_VAL_OFF;
>  	clint_timer_freq = riscv_timebase;
>  
> +#ifdef CONFIG_RISCV_M_MODE
> +	/*
> +	 * Yes, that's an odd naming scheme.  time_val is public, but hopefully
> +	 * will die in favor of something cleaner.
> +	 */
> +	clint_time_val = clint_timer_val;
> +#endif
> +
>  	pr_info("%pOFP: timer running at %ld Hz\n", np, clint_timer_freq);
>  
>  	rc = clocksource_register_hz(&clint_clocksource, clint_timer_freq);

I tried this patch on the Kendryte, on top of for-next and on top of
fixes (not sure which one is your target). First of all, with for-next, 
I get this compilation warning:

arch/riscv/mm/init.c:44:28: warning: ‘pt_ops’ defined but not used [-
Wunused-variable]
   44 | static struct pt_alloc_ops pt_ops;

Now for the tests:

(1) With for-next, no patch applied I get a crash. Expected I guess...

[    0.000000] Linux version 5.9.0-rc2-00022-g3a822d8ea3b3 (
damien at localhost.localdomain) (riscv64-linux-gcc.br_real (Buildroot
2020.08-368-g9148de6052-dirty) 9.3.0, GNU ld (GNU Binutils) 2.32) #93
SMP Tue Sep 15 17:13:25 JST 2020
[    0.000000] earlycon: sifive0 at MMIO 0x0000000038000000 (options
'')
[    0.000000] printk: bootconsole [sifive0] enabled
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000080000000-0x00000000807fffff]
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000080000000-0x00000000807fffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000080000000-
0x00000000807fffff]
[    0.000000] riscv: ISA extensions acdfim
[    0.000000] riscv: ELF capabilities acdfim
[    0.000000] percpu: max_distance=0x18000 too large for vmalloc space
0x0
[    0.000000] percpu: Embedded 12 pages/cpu s18400 r0 d30752 u49152
[    0.000000] Built 1 zonelists, mobility grouping off.  Total pages:
2020
[    0.000000] Kernel command line: earlycon console=ttySIF0
[    0.000000] Dentry cache hash table entries: 1024 (order: 1, 8192
bytes, linear)
[    0.000000] Inode-cache hash table entries: 512 (order: 0, 4096
bytes, linear)
[    0.000000] Sorting __ex_table...
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 6192K/8192K available (1287K kernel code, 117K
rwdata, 178K rodata, 301K init, 97K bss, 2000K reserved, 0K cma-
reserved)
[    0.000000] rcu: Hierarchical RCU implementation.
[    0.000000] rcu: RCU calculated value of scheduler-enlistment delay
is 25 jiffies.
[    0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[    0.000000] riscv-intc: 64 local interrupts mapped
[    0.000000] plic: interrupt-controller at c000000: mapped 65 interrupts
with 2 handlers for 4 contexts.
[    0.000000] Oops - illegal instruction [#1]
[    0.000000] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.9.0-rc2-
00022-g3a822d8ea3b3 #93
[    0.000000] epc: 000000008000d5c8 ra : 000000008000d4fa sp :
0000000080183f40
[    0.000000]  gp : 000000008019d350 tp : 0000000080187a00 t0 :
0000000000000019
[    0.000000]  t1 : 0000000000000018 t2 : 0000000000000001 s0 :
0000000080183f80
[    0.000000]  s1 : 0000000000000200 a0 : 0000000080196070 a1 :
0000000000000000
[    0.000000]  a2 : ffffffff80183f50 a3 : 0000000000000000 a4 :
0000000000000000
[    0.000000]  a5 : 0000000080168f70 a6 : 0000000000000000 a7 :
00000000000001e4
[    0.000000]  s2 : 000000008019d0a8 s3 : 000000008019d068 s4 :
00000000807e0b00
[    0.000000]  s5 : 000000008019d038 s6 : 000000008019d030 s7 :
00000000800123a8
[    0.000000]  s8 : 0000000000000000 s9 : 0000000000000000 s10:
0000000000000000
[    0.000000]  s11: 0000000000000000 t3 : 00000000801b42a8 t4 :
0000000000000068
[    0.000000]  t5 : 000000000000004c t6 : 0000000000000033
[    0.000000] status: 0000000000001800 badaddr: 00000000001b727c
cause: 0000000000000002
[    0.000000] random: get_random_bytes called from 0x0000000080051df8
with crng_init=0
[    0.000000] ---[ end trace 0000000000000000 ]---
[    0.000000] Kernel panic - not syncing: Fatal exception
[    0.000000] ---[ end Kernel panic - not syncing: Fatal exception ]
---


(2) With for-next, patch applied, I get a hang:

[    0.000000] Linux version 5.9.0-rc2-00023-gacd1158869ad (
damien at localhost.localdomain) (riscv64-linux-gcc.br_real (Buildroot
2020.08-368-g9148de6052-dirty) 9.3.0, GNU ld (GNU Binutils) 2.32) #94
SMP Tue Sep 15 17:15:59 JST 2020
[    0.000000] earlycon: sifive0 at MMIO 0x0000000038000000 (options
'')
[    0.000000] printk: bootconsole [sifive0] enabled
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000080000000-0x00000000807fffff]
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000080000000-0x00000000807fffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000080000000-
0x00000000807fffff]
[    0.000000] riscv: ISA extensions acdfim
[    0.000000] riscv: ELF capabilities acdfim
[    0.000000] percpu: max_distance=0x18000 too large for vmalloc space
0x0
[    0.000000] percpu: Embedded 12 pages/cpu s18400 r0 d30752 u49152
[    0.000000] Built 1 zonelists, mobility grouping off.  Total pages:
2020
[    0.000000] Kernel command line: earlycon console=ttySIF0
[    0.000000] Dentry cache hash table entries: 1024 (order: 1, 8192
bytes, linear)
[    0.000000] Inode-cache hash table entries: 512 (order: 0, 4096
bytes, linear)
[    0.000000] Sorting __ex_table...
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 6176K/8192K available (1291K kernel code, 128K
rwdata, 178K rodata, 305K init, 97K bss, 2016K reserved, 0K cma-
reserved)
[    0.000000] rcu: Hierarchical RCU implementation.
[    0.000000] rcu: RCU calculated value of scheduler-enlistment delay
is 25 jiffies.
[    0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[    0.000000] riscv-intc: 64 local interrupts mapped
[    0.000000] plic: interrupt-controller at c000000: mapped 65 interrupts
with 2 handlers for 4 contexts.
[    0.000000] random: get_random_bytes called from 0x00000000800019b4
with crng_init=0
[    0.000000] clint: interrupt-controller at 2000000: timer irq not found
[    0.000000] Failed to initialize '/soc/interrupt-controller at 2000000'
: -19
[    0.000000] timer_probe: no matching timers found
[    0.000000] Console: colour dummy device 80x25
[    0.000000] sched_clock: 64 bits at 250 Hz, resolution 4000000ns,
wraps every 9007199254000000ns
[    0.000000] Calibrating delay loop (skipped), value calculated using
timer frequency.. 15.60 BogoMIPS (lpj=31200)
[    0.000000] pid_max: default: 4096 minimum: 301
[    0.000000] Mount-cache hash table entries: 512 (order: 0, 4096
bytes, linear)
[    0.000000] Mountpoint-cache hash table entries: 512 (order: 0, 4096
bytes, linear)
[    0.000000] rcu: Hierarchical SRCU implementation.
[    0.000000] smp: Bringing up secondary CPUs ...
[    0.000000] SMP: IPI inject method not available


In both cases, the timer does not seem to work at all (see the message
timestamps).

Exact same results with your fixes branch too: hang with the patch and
illegal instruction without it.

Something is broken.

-- 
Damien Le Moal
Western Digital Research


More information about the linux-riscv mailing list