Skip to contentSkip to conversation

Fix missing entropy on Zynq arch due to get_cycles() not supported

PATCH Superseded Andrea Merello 1 participant 3 messages Feb 17, 2021

Subsystems (5)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover Fix missing entropy on Zynq arch due to get_cycles() not supported

A real-world problem has been seen with a Zynq-based board running Debian 10, where ssh daemon takes a really long time to come up at boot. This is due to lack of random numbers.

Since commit 50ee7529ec450 ("random: try to actively add entropy rather than passively wait for it") we try to generate entropy whenever we are in short of random numbers and someone needs them.

This trick works only when CPU cycle counter is available. On ARM this means that get_cycles() works and in turn read_current_timer() works.

Zynq HW includes two "cadence TTC" timers and the "ARM global timer". All these pieces of HW are fed by the CPU clock, which dynamically changes whenever CPU frequency scaling is enabled.

In timer-cadence-ttc driver this scenario is handled by looking at parent clock changes and adjusting things when required. This is the only usable clocksource when CPU frequency scaling is in use. arm_global_timer driver is disabled in Kconfig when CPU_FREQ is enabled for Zynq arch.

Unfortunately timer-cadence-ttc driver does register itself via register_current_timer_delay() and that ultimately ends up in get_cycles() to always return zero, causing the aforementioned lack of entropy problem. I believe that the reason for this is because Cadence TTC counter on Zynq silicon is only 16-bit wide.

This patchset works around this by implementing in ARM global timer driver a mechanism to compensate for parent clock variations, similarly to what it's done in Cadence TTC timer driver, so that it can be used together with CPU frequency scaling on Zynq arch.

This proved to finally fix the problem on my Zynq-based Z-turn board.

Andrea Merello (2):
clocksource: arm_global_timer: implement rate compensation whenever
source clock changes
arm: zynq: don't disable CONFIG_ARM_GLOBAL_TIMER due to
CONFIG_CPU_FREQ anymore

 arch/arm/mach-zynq/Kconfig             |   2 +-
 drivers/clocksource/Kconfig            |  14 +++
 drivers/clocksource/arm_global_timer.c | 122 +++++++++++++++++++++++--
 3 files changed, 127 insertions(+), 11 deletions(-)

--
2.17.1

_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

#1 lore raw

PATCH 1/2 clocksource: arm_global_timer: implement rate compensation whenever source clock changes

This patch adds rate change notification support for the parent clock; should that clock change, then we try to adjust the our prescaler in order to compensate (i.e. we adjust to still get the same timer frequency).

This is loosely based on what it's done in timer-cadence-ttc. timer-sun51, mips-gic-timer and smp_twd.c also seem to look at their parent clock rate and to perform some kind of adjustment whenever needed.

In this particular case we have only one single counter and prescaler for all clocksource, clockevent and timer_delay, and we just update it for all (i.e. we don't let it go and call clockevents_update_freq() to notify to the kernel that our rate has changed).

Note that, there is apparently no other way to fixup things, because once we call register_current_timer_delay(), specifying the timer rate, it seems that that rate is not supposed to change ever.

In order for this mechanism to work, we have to make assumptions about how much the initial clock is supposed to eventually decrease from the initial one, and set our initial prescaler to a value that we can eventually decrease enough to compensate. We provide an option in KConfig for this.

In case we end up in a situation in which we are not able to compensate the parent clock change, we fail returning NOTIFY_BAD.

This fixes a real-world problem with Zynq arch not being able to use this driver and CPU_FREQ at the same time (because ARM global timer is fed by the CPU clock, which may keep changing when CPU_FREQ is enabled).

---
 drivers/clocksource/Kconfig            |  13 +++
 drivers/clocksource/arm_global_timer.c | 122 +++++++++++++++++++++++--
 2 files changed, 125 insertions(+), 10 deletions(-)
drivers/clocksource/Kconfig +13 -0
index 14c7c4712478..0f1c625275a0 100644
@@ -385,6 +385,19 @@ config ARM_GLOBAL_TIMER
 	help
 	  This option enables support for the ARM global timer unit.
 
+config ARM_GT_INITIAL_PRESCALER_VAL
+	int "ARM global timer initial prescaler value"
+	default 1
+	depends on ARM_GLOBAL_TIMER
+	help
+	  When the ARM global timer initializes, its current rate is declared
+	  to the kernel and maintained forever. Should it's parent clock
+	  change, the driver tries to fix the timer's internal prescaler.
+	  On some machs (i.e. Zynq) the initial prescaler value thus poses
+	  bounds about how much the parent clock is allowed to decrease or
+	  increase wrt the initial clock value.
+	  This affects CPU_FREQ max delta from the initial frequency.
+
 config ARM_TIMER_SP804
 	bool "Support for Dual Timer SP804 module" if COMPILE_TEST
 	depends on GENERIC_SCHED_CLOCK && CLKDEV_LOOKUP
drivers/clocksource/arm_global_timer.c +112 -10

196 lines not on this page · raw message

Signature · 7 lines
2.17.1


_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
#2 lore raw

PATCH 2/2 arm: zynq: don't disable CONFIG_ARM_GLOBAL_TIMER due to CONFIG_CPU_FREQ anymore

In reply to Andrea Merello

Now ARM global timer driver could work even if it's source clock rate changes, so we don't need to disable that driver when cpu frequency scaling is in use.

This cause Zynq arch to get support for timer delay and get_cycles().

---
 arch/arm/mach-zynq/Kconfig  | 2 +-
 drivers/clocksource/Kconfig | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)
arch/arm/mach-zynq/Kconfig +1 -1
index 43fb941dcd07..a56748d671c4 100644
@@ -6,7 +6,7 @@ config ARCH_ZYNQ
 	select ARCH_SUPPORTS_BIG_ENDIAN
 	select ARM_AMBA
 	select ARM_GIC
-	select ARM_GLOBAL_TIMER if !CPU_FREQ
+	select ARM_GLOBAL_TIMER
 	select CADENCE_TTC_TIMER
 	select HAVE_ARM_SCU if SMP
 	select HAVE_ARM_TWD if SMP
drivers/clocksource/Kconfig +1 -0
index 0f1c625275a0..421cd763bdb2 100644
@@ -387,6 +387,7 @@ config ARM_GLOBAL_TIMER
 
 config ARM_GT_INITIAL_PRESCALER_VAL
 	int "ARM global timer initial prescaler value"
+	default 2 if ARCH_ZYNQ
 	default 1
 	depends on ARM_GLOBAL_TIMER
 	help
Signature · 7 lines
2.17.1


_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel