diff mbox series

[v2,1/4] xen/riscv: introduce preinit_xen_time()

Message ID 1a04711f46a1c0a7cdf709abc37f143747215495.1742918184.git.oleksii.kurochko@gmail.com (mailing list archive)
State Superseded
Headers show
Series [v2,1/4] xen/riscv: introduce preinit_xen_time() | expand

Commit Message

Oleksii Kurochko March 25, 2025, 5:36 p.m. UTC
preinit_xen_time() does two things:
1. Parse timebase-frequency properpy of /cpus node to initialize
   cpu_khz variable.
2. Initialize xen_start_clock_cycles with the current time counter
   value.

Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>
---
Changes in v2:
 - Update SPDX tag for time.c
 - s/read_mostly/__ro_after_init for boot_count variable.
 - Add declaration of boot_count to asm/time.h.
 - Rename boot_count to xen_start_clock_cycles.
 - Stray double blank in the defintion of timer_ids.
 - s/u32/uint32_t.
 - Drop full stop.
 - Update the commit message.
 - s/__initconst/__initconstrel for timer_ids[].
---
 xen/arch/riscv/Makefile           |  1 +
 xen/arch/riscv/include/asm/time.h |  5 ++++
 xen/arch/riscv/setup.c            |  2 ++
 xen/arch/riscv/stubs.c            |  2 --
 xen/arch/riscv/time.c             | 39 +++++++++++++++++++++++++++++++
 5 files changed, 47 insertions(+), 2 deletions(-)
 create mode 100644 xen/arch/riscv/time.c

Comments

Jan Beulich March 26, 2025, 3:13 p.m. UTC | #1
On 25.03.2025 18:36, Oleksii Kurochko wrote:
> preinit_xen_time() does two things:
> 1. Parse timebase-frequency properpy of /cpus node to initialize
>    cpu_khz variable.
> 2. Initialize xen_start_clock_cycles with the current time counter
>    value.
> 
> Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>
> ---
> Changes in v2:
>  - Update SPDX tag for time.c
>  - s/read_mostly/__ro_after_init for boot_count variable.
>  - Add declaration of boot_count to asm/time.h.
>  - Rename boot_count to xen_start_clock_cycles.

I'm not going to insist on another name change, but I'm having a hard time
seeing why almost any global variable in Xen would need to have a xen_
prefix. "start" also can be ambiguous, so imo boot_clock_cycles would have
been best.

> --- /dev/null
> +++ b/xen/arch/riscv/time.c
> @@ -0,0 +1,39 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +#include <xen/device_tree.h>
> +#include <xen/init.h>
> +#include <xen/lib.h>
> +#include <xen/sections.h>
> +
> +unsigned long __ro_after_init cpu_khz; /* CPU clock frequency in kHz. */
> +unsigned long __ro_after_init xen_start_clock_cycles;

For the theoretical at this point case of support RV32, will unsigned long
be wide enough? I.e. is the counter only 32 bits wide when XLEN=32?

> +static __initdata struct dt_device_node *timer;

Please can __initdata (and alike) live at its canonical place, between
type and identifier?

It's also unclear at this point why this needs to be a file scope variable.

> +/* Set up the timer on the boot CPU (early init function) */
> +static void __init preinit_dt_xen_time(void)
> +{
> +    static const struct dt_device_match __initconstrel timer_ids[] =
> +    {
> +        DT_MATCH_PATH("/cpus"),
> +        { /* sentinel */ },
> +    };
> +    uint32_t rate;
> +
> +    timer = dt_find_matching_node(NULL, timer_ids);
> +    if ( !timer )
> +        panic("Unable to find a compatible timer in the device tree\n");
> +
> +    dt_device_set_used_by(timer, DOMID_XEN);
> +
> +    if ( !dt_property_read_u32(timer, "timebase-frequency", &rate) )
> +        panic("Unable to find clock frequency\n");
> +
> +    cpu_khz = rate / 1000;

"rate" being only 32 bits wide, what about clock rates above 4GHz? Or is
this some external clock running at a much lower rate than the CPU would?

> +}
> +
> +void __init preinit_xen_time(void)
> +{
> +    preinit_dt_xen_time();
> +
> +    xen_start_clock_cycles = get_cycles();
> +}

I take it that more stuff is going to be added to this function? Else I
wouldn't see why preinit_dt_xen_time() needed to be a separate function.

Jan
Oleksii Kurochko March 26, 2025, 7:49 p.m. UTC | #2
On 3/26/25 4:13 PM, Jan Beulich wrote:
> On 25.03.2025 18:36, Oleksii Kurochko wrote:
>> preinit_xen_time() does two things:
>> 1. Parse timebase-frequency properpy of /cpus node to initialize
>>     cpu_khz variable.
>> 2. Initialize xen_start_clock_cycles with the current time counter
>>     value.
>>
>> Signed-off-by: Oleksii Kurochko<oleksii.kurochko@gmail.com>
>> ---
>> Changes in v2:
>>   - Update SPDX tag for time.c
>>   - s/read_mostly/__ro_after_init for boot_count variable.
>>   - Add declaration of boot_count to asm/time.h.
>>   - Rename boot_count to xen_start_clock_cycles.
> I'm not going to insist on another name change, but I'm having a hard time
> seeing why almost any global variable in Xen would need to have a xen_
> prefix. "start" also can be ambiguous, so imo boot_clock_cycles would have
> been best.

I can change that during the work on the version of this patch.

>
>> --- /dev/null
>> +++ b/xen/arch/riscv/time.c
>> @@ -0,0 +1,39 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +#include <xen/device_tree.h>
>> +#include <xen/init.h>
>> +#include <xen/lib.h>
>> +#include <xen/sections.h>
>> +
>> +unsigned long __ro_after_init cpu_khz; /* CPU clock frequency in kHz. */
>> +unsigned long __ro_after_init xen_start_clock_cycles;
> For the theoretical at this point case of support RV32, will unsigned long
> be wide enough? I.e. is the counter only 32 bits wide when XLEN=32?

No, it will be really an issue and the type should be uint64_t for this variable
because on RV32I the timeh CSR is a read-only shadow of the upper 32 bits of the
memory-mapped mtime register, while time shadows only the lower 32 bits of mtime.
So on RV32 it is still 64-bit long and requires two reads to get cycle value.

>
>> +static __initdata struct dt_device_node *timer;
> Please can __initdata (and alike) live at its canonical place, between
> type and identifier?
>
> It's also unclear at this point why this needs to be a file scope variable.

Because this variable is used and will be used only in preinit_dt_xen_time().
But I think we could move the declaration of this variable to preinit_dt_xen_time()
as it is used only during preinit_dt_xen_time().

>
>> +/* Set up the timer on the boot CPU (early init function) */
>> +static void __init preinit_dt_xen_time(void)
>> +{
>> +    static const struct dt_device_match __initconstrel timer_ids[] =
>> +    {
>> +        DT_MATCH_PATH("/cpus"),
>> +        { /* sentinel */ },
>> +    };
>> +    uint32_t rate;
>> +
>> +    timer = dt_find_matching_node(NULL, timer_ids);
>> +    if ( !timer )
>> +        panic("Unable to find a compatible timer in the device tree\n");
>> +
>> +    dt_device_set_used_by(timer, DOMID_XEN);
>> +
>> +    if ( !dt_property_read_u32(timer, "timebase-frequency", &rate) )
>> +        panic("Unable to find clock frequency\n");
>> +
>> +    cpu_khz = rate / 1000;
> "rate" being only 32 bits wide, what about clock rates above 4GHz? Or is
> this some external clock running at a much lower rate than the CPU would?

It is the frequency of the hardware timer that drives the 
|mtime|register, it defines the frequency (in Hz) at which the timer 
increments.


>
>> +}
>> +
>> +void __init preinit_xen_time(void)
>> +{
>> +    preinit_dt_xen_time();
>> +
>> +    xen_start_clock_cycles = get_cycles();
>> +}
> I take it that more stuff is going to be added to this function? Else I
> wouldn't see why preinit_dt_xen_time() needed to be a separate function.

Actually, I checked my latest downstream branch and I haven't added nothing
extra to this function.
Only one thing that I want to add is ACPI case:
     if ( acpi_disabled )
         preinit_dt_xen_time();
     else
         panic("TBD\n"); /* preinit_acpi_xen_time(); */

~ Oleksii
Jan Beulich March 27, 2025, 7:42 a.m. UTC | #3
On 26.03.2025 20:49, Oleksii Kurochko wrote:
> On 3/26/25 4:13 PM, Jan Beulich wrote:
>> On 25.03.2025 18:36, Oleksii Kurochko wrote:
>>> +/* Set up the timer on the boot CPU (early init function) */
>>> +static void __init preinit_dt_xen_time(void)
>>> +{
>>> +    static const struct dt_device_match __initconstrel timer_ids[] =
>>> +    {
>>> +        DT_MATCH_PATH("/cpus"),
>>> +        { /* sentinel */ },
>>> +    };
>>> +    uint32_t rate;
>>> +
>>> +    timer = dt_find_matching_node(NULL, timer_ids);
>>> +    if ( !timer )
>>> +        panic("Unable to find a compatible timer in the device tree\n");
>>> +
>>> +    dt_device_set_used_by(timer, DOMID_XEN);
>>> +
>>> +    if ( !dt_property_read_u32(timer, "timebase-frequency", &rate) )
>>> +        panic("Unable to find clock frequency\n");
>>> +
>>> +    cpu_khz = rate / 1000;
>> "rate" being only 32 bits wide, what about clock rates above 4GHz? Or is
>> this some external clock running at a much lower rate than the CPU would?
> 
> It is the frequency of the hardware timer that drives the 
> |mtime|register, it defines the frequency (in Hz) at which the timer 
> increments.

And that timer can't plausibly run at more than 4 GHz?

>>> +void __init preinit_xen_time(void)
>>> +{
>>> +    preinit_dt_xen_time();
>>> +
>>> +    xen_start_clock_cycles = get_cycles();
>>> +}
>> I take it that more stuff is going to be added to this function? Else I
>> wouldn't see why preinit_dt_xen_time() needed to be a separate function.
> 
> Actually, I checked my latest downstream branch and I haven't added nothing
> extra to this function.
> Only one thing that I want to add is ACPI case:
>      if ( acpi_disabled )
>          preinit_dt_xen_time();
>      else
>          panic("TBD\n"); /* preinit_acpi_xen_time(); */

That's probably a good enough reason then to keep the function separate.

Jan
Oleksii Kurochko March 27, 2025, 11:48 a.m. UTC | #4
On 3/27/25 8:42 AM, Jan Beulich wrote:
> On 26.03.2025 20:49, Oleksii Kurochko wrote:
>> On 3/26/25 4:13 PM, Jan Beulich wrote:
>>> On 25.03.2025 18:36, Oleksii Kurochko wrote:
>>>> +/* Set up the timer on the boot CPU (early init function) */
>>>> +static void __init preinit_dt_xen_time(void)
>>>> +{
>>>> +    static const struct dt_device_match __initconstrel timer_ids[] =
>>>> +    {
>>>> +        DT_MATCH_PATH("/cpus"),
>>>> +        { /* sentinel */ },
>>>> +    };
>>>> +    uint32_t rate;
>>>> +
>>>> +    timer = dt_find_matching_node(NULL, timer_ids);
>>>> +    if ( !timer )
>>>> +        panic("Unable to find a compatible timer in the device tree\n");
>>>> +
>>>> +    dt_device_set_used_by(timer, DOMID_XEN);
>>>> +
>>>> +    if ( !dt_property_read_u32(timer, "timebase-frequency", &rate) )
>>>> +        panic("Unable to find clock frequency\n");
>>>> +
>>>> +    cpu_khz = rate / 1000;
>>> "rate" being only 32 bits wide, what about clock rates above 4GHz? Or is
>>> this some external clock running at a much lower rate than the CPU would?
>> It is the frequency of the hardware timer that drives the
>> |mtime|register, it defines the frequency (in Hz) at which the timer
>> increments.
> And that timer can't plausibly run at more than 4 GHz?

I haven't seen yet such big timer frequency.

But if to look at device tree spec:
timebase-frequency: Specifies the current frequency at which the
timebase and decrementer registers are updated (in Hertz).
The value is a <prop-encoded-array> in one of two forms:
• A 32-bit integer consisting of one <u32>
   specifying the frequency.
• A 64-bit integer represented as a <u64>.

Interesting that Linux kernel reads timebase-frequency as u32:
    u32 prop;
    ...
    if (!cpu || of_property_read_u32(cpu, "timebase-frequency", &prop))
and then saves it to:
    riscv_timebase = prop;
where riscv_timebase is declared as unsigned long.

I think it can be left as it is now as if timebase-frequency will be u64 then
it means that it will be needed to read two u32 values and in this case dt_property_read_u32()
will return 0 and the panic will occur.

~ Oleksii
Jan Beulich March 27, 2025, 12:23 p.m. UTC | #5
On 27.03.2025 12:48, Oleksii Kurochko wrote:
> I think it can be left as it is now as if timebase-frequency will be u64 then
> it means that it will be needed to read two u32 values and in this case dt_property_read_u32()
> will return 0 and the panic will occur.

Fair enough; please say a word on this aspect in the description, though.

Jan
diff mbox series

Patch

diff --git a/xen/arch/riscv/Makefile b/xen/arch/riscv/Makefile
index b0c8270a99..82016a957a 100644
--- a/xen/arch/riscv/Makefile
+++ b/xen/arch/riscv/Makefile
@@ -9,6 +9,7 @@  obj-y += setup.o
 obj-y += shutdown.o
 obj-y += smp.o
 obj-y += stubs.o
+obj-y += time.o
 obj-y += traps.o
 obj-y += vm_event.o
 
diff --git a/xen/arch/riscv/include/asm/time.h b/xen/arch/riscv/include/asm/time.h
index fc1572e9b4..c24745508b 100644
--- a/xen/arch/riscv/include/asm/time.h
+++ b/xen/arch/riscv/include/asm/time.h
@@ -5,6 +5,9 @@ 
 #include <xen/bug.h>
 #include <asm/csr.h>
 
+/* Clock cycles count at Xen startup */
+extern unsigned long xen_start_clock_cycles;
+
 struct vcpu;
 
 static inline void force_update_vcpu_system_time(struct vcpu *v)
@@ -19,6 +22,8 @@  static inline cycles_t get_cycles(void)
     return csr_read(CSR_TIME);
 }
 
+void preinit_xen_time(void);
+
 #endif /* ASM__RISCV__TIME_H */
 
 /*
diff --git a/xen/arch/riscv/setup.c b/xen/arch/riscv/setup.c
index b0e587678e..836ad16fed 100644
--- a/xen/arch/riscv/setup.c
+++ b/xen/arch/riscv/setup.c
@@ -126,6 +126,8 @@  void __init noreturn start_xen(unsigned long bootcpu_id,
 
     riscv_fill_hwcap();
 
+    preinit_xen_time();
+
     printk("All set up\n");
 
     machine_halt();
diff --git a/xen/arch/riscv/stubs.c b/xen/arch/riscv/stubs.c
index 5951b0ce91..caa133de84 100644
--- a/xen/arch/riscv/stubs.c
+++ b/xen/arch/riscv/stubs.c
@@ -27,8 +27,6 @@  nodemask_t __read_mostly node_online_map = { { [0] = 1UL } };
 
 /* time.c */
 
-unsigned long __ro_after_init cpu_khz;  /* CPU clock frequency in kHz. */
-
 s_time_t get_s_time(void)
 {
     BUG_ON("unimplemented");
diff --git a/xen/arch/riscv/time.c b/xen/arch/riscv/time.c
new file mode 100644
index 0000000000..ae8efa3c59
--- /dev/null
+++ b/xen/arch/riscv/time.c
@@ -0,0 +1,39 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+#include <xen/device_tree.h>
+#include <xen/init.h>
+#include <xen/lib.h>
+#include <xen/sections.h>
+
+unsigned long __ro_after_init cpu_khz; /* CPU clock frequency in kHz. */
+unsigned long __ro_after_init xen_start_clock_cycles;
+
+static __initdata struct dt_device_node *timer;
+
+/* Set up the timer on the boot CPU (early init function) */
+static void __init preinit_dt_xen_time(void)
+{
+    static const struct dt_device_match __initconstrel timer_ids[] =
+    {
+        DT_MATCH_PATH("/cpus"),
+        { /* sentinel */ },
+    };
+    uint32_t rate;
+
+    timer = dt_find_matching_node(NULL, timer_ids);
+    if ( !timer )
+        panic("Unable to find a compatible timer in the device tree\n");
+
+    dt_device_set_used_by(timer, DOMID_XEN);
+
+    if ( !dt_property_read_u32(timer, "timebase-frequency", &rate) )
+        panic("Unable to find clock frequency\n");
+
+    cpu_khz = rate / 1000;
+}
+
+void __init preinit_xen_time(void)
+{
+    preinit_dt_xen_time();
+
+    xen_start_clock_cycles = get_cycles();
+}