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

Damien Le Moal Damien.LeMoal at wdc.com
Tue Sep 15 04:39:31 EDT 2020


On 2020/09/15 17:25, Damien Le Moal wrote:
> 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

Is it needed to fix the device tree ? That was working before.

> [    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.


Note: just to be sure I was not doing something wrong, I use the same build
environment to check with a known good kernel (5.8.0) and I got a clean boot up
to user space. Here is the boot log:


[    0.000000] Linux version 5.8.0 (damien at localhost.localdomain)
(riscv64-linux-gcc.br_real (Buildroot 2020.08-368-g9148de6052-dirty) 9.3.0, GNU
ld (GNU Binutils) 2.32) #97 SMP Tue Sep 15 17:30:04 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 s18336 r0 d30816 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: 6312K/8192K available (939K kernel code, 127K rwdata,
169K rodata, 297K init, 97K bss, 1880K 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] riscv_timer_init_dt: Registering clocksource cpuid [0] hartid [0]
[    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff
max_cycles: 0x3990be68b, max_idle_ns: 881590404272 ns
[    0.000014] sched_clock: 64 bits at 7MHz, resolution 128ns, wraps every
4398046511054ns
[    0.008239] Console: colour dummy device 80x25
[    0.012481] Calibrating delay loop (skipped), value calculated using timer
frequency.. 15.60 BogoMIPS (lpj=31200)
[    0.022682] pid_max: default: 4096 minimum: 301
[    0.027299] Mount-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
[    0.034417] Mountpoint-cache hash table entries: 512 (order: 0, 4096 bytes,
linear)
[    0.044873] rcu: Hierarchical SRCU implementation.
[    0.049771] smp: Bringing up secondary CPUs ...
[    0.055055] smp: Brought up 1 node, 2 CPUs
[    0.059445] devtmpfs: initialized
[    0.065609] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff,
max_idle_ns: 7645041785100000 ns
[    0.074633] futex hash table entries: 16 (order: -2, 1024 bytes, linear)
[    0.084141] Kendryte K210 SoC sysctl
[    0.097636] clocksource: Switched to clocksource riscv_clocksource
[    0.235346] workingset: timestamp_bits=62 max_order=11 bucket_order=0
[    0.242991] 38000000.serial: ttySIF0 at MMIO 0x38000000 (irq = 1, base_baud =
0) is a SiFive UART v0
[    0.251551] printk: console [ttySIF0] enabled
[    0.251551] printk: console [ttySIF0] enabled
[    0.260079] printk: bootconsole [sifive0] disabled
[    0.260079] printk: bootconsole [sifive0] disabled
[    0.271932] random: get_random_bytes called from 0x000000008005075c with
crng_init=0
[    0.280470] Freeing unused kernel memory: 296K
[    0.284186] This architecture does not have kernel memory protection.
[    0.290631] Run /init as init process

+---------------------------+
| Kendryte K210 NOMMU Linux |
+---------------------------+


BusyBox v1.32.0 (2020-09-12 16:01:20 JST) hush - the humble shell
Enter 'help' for a list of built-in commands.

/ #




-- 
Damien Le Moal
Western Digital Research



More information about the linux-riscv mailing list