Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm_arch_timer: Expose event stream status

The arch timer configuration for a CPU might get reset after suspending
said CPU.

In order to reliably use the event stream in the kernel (e.g. for delays),
we keep track of the state where we can safely consider the event stream as
properly configured. After writing to cntkctl, we issue an ISB to ensure
that subsequent delay loops can rely on the event stream being enabled.

Signed-off-by: Julien Thierry <julien.thierry@arm.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>

authored by

Julien Thierry and committed by
Will Deacon
ec5c8e42 611a7bc7

+30 -3
+1
arch/arm/include/asm/arch_timer.h
··· 106 106 static inline void arch_timer_set_cntkctl(u32 cntkctl) 107 107 { 108 108 asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl)); 109 + isb(); 109 110 } 110 111 111 112 #endif
+1
arch/arm64/include/asm/arch_timer.h
··· 144 144 static inline void arch_timer_set_cntkctl(u32 cntkctl) 145 145 { 146 146 write_sysreg(cntkctl, cntkctl_el1); 147 + isb(); 147 148 } 148 149 149 150 static inline u64 arch_counter_get_cntpct(void)
+22 -3
drivers/clocksource/arm_arch_timer.c
··· 77 77 static bool arch_counter_suspend_stop; 78 78 static bool vdso_default = true; 79 79 80 + static cpumask_t evtstrm_available = CPU_MASK_NONE; 80 81 static bool evtstrm_enable = IS_ENABLED(CONFIG_ARM_ARCH_TIMER_EVTSTREAM); 81 82 82 83 static int __init early_evtstrm_cfg(char *buf) ··· 741 740 #ifdef CONFIG_COMPAT 742 741 compat_elf_hwcap |= COMPAT_HWCAP_EVTSTRM; 743 742 #endif 743 + cpumask_set_cpu(smp_processor_id(), &evtstrm_available); 744 744 } 745 745 746 746 static void arch_timer_configure_evtstream(void) ··· 866 864 return arch_timer_rate; 867 865 } 868 866 867 + bool arch_timer_evtstrm_available(void) 868 + { 869 + /* 870 + * We might get called from a preemptible context. This is fine 871 + * because availability of the event stream should be always the same 872 + * for a preemptible context and context where we might resume a task. 873 + */ 874 + return cpumask_test_cpu(raw_smp_processor_id(), &evtstrm_available); 875 + } 876 + 869 877 static u64 arch_counter_get_cntvct_mem(void) 870 878 { 871 879 u32 vct_lo, vct_hi, tmp_hi; ··· 941 929 { 942 930 struct clock_event_device *clk = this_cpu_ptr(arch_timer_evt); 943 931 932 + cpumask_clear_cpu(smp_processor_id(), &evtstrm_available); 933 + 944 934 arch_timer_stop(clk); 945 935 return 0; 946 936 } ··· 952 938 static int arch_timer_cpu_pm_notify(struct notifier_block *self, 953 939 unsigned long action, void *hcpu) 954 940 { 955 - if (action == CPU_PM_ENTER) 941 + if (action == CPU_PM_ENTER) { 956 942 __this_cpu_write(saved_cntkctl, arch_timer_get_cntkctl()); 957 - else if (action == CPU_PM_ENTER_FAILED || action == CPU_PM_EXIT) 943 + 944 + cpumask_clear_cpu(smp_processor_id(), &evtstrm_available); 945 + } else if (action == CPU_PM_ENTER_FAILED || action == CPU_PM_EXIT) { 958 946 arch_timer_set_cntkctl(__this_cpu_read(saved_cntkctl)); 947 + 948 + if (elf_hwcap & HWCAP_EVTSTRM) 949 + cpumask_set_cpu(smp_processor_id(), &evtstrm_available); 950 + } 959 951 return NOTIFY_OK; 960 952 } 961 953 ··· 1036 1016 err = arch_timer_cpu_pm_init(); 1037 1017 if (err) 1038 1018 goto out_unreg_notify; 1039 - 1040 1019 1041 1020 /* Register and immediately configure the timer on the boot CPU */ 1042 1021 err = cpuhp_setup_state(CPUHP_AP_ARM_ARCH_TIMER_STARTING,
+6
include/clocksource/arm_arch_timer.h
··· 93 93 extern u32 arch_timer_get_rate(void); 94 94 extern u64 (*arch_timer_read_counter)(void); 95 95 extern struct arch_timer_kvm_info *arch_timer_get_kvm_info(void); 96 + extern bool arch_timer_evtstrm_available(void); 96 97 97 98 #else 98 99 ··· 105 104 static inline u64 arch_timer_read_counter(void) 106 105 { 107 106 return 0; 107 + } 108 + 109 + static inline bool arch_timer_evtstrm_available(void) 110 + { 111 + return false; 108 112 } 109 113 110 114 #endif