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

Configure Feed

Select the types of activity you want to include in your feed.

timekeeping: Provide interface to control auxiliary clocks

Auxiliary clocks are disabled by default and attempts to access them
fail.

Provide an interface to enable/disable them at run-time.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: John Stultz <jstultz@google.com>
Link: https://lore.kernel.org/all/20250625183758.444626478@linutronix.de

+121
+5
Documentation/ABI/stable/sysfs-kernel-time-aux-clocks
··· 1 + What: /sys/kernel/time/aux_clocks/<ID>/enable 2 + Date: May 2025 3 + Contact: Thomas Gleixner <tglx@linutronix.de> 4 + Description: 5 + Controls the enablement of auxiliary clock timekeepers.
+116
kernel/time/timekeeping.c
··· 6 6 #include <linux/timekeeper_internal.h> 7 7 #include <linux/module.h> 8 8 #include <linux/interrupt.h> 9 + #include <linux/kobject.h> 9 10 #include <linux/percpu.h> 10 11 #include <linux/init.h> 11 12 #include <linux/mm.h> ··· 2916 2915 .clock_set = aux_clock_set, 2917 2916 .clock_adj = aux_clock_adj, 2918 2917 }; 2918 + 2919 + static void aux_clock_enable(clockid_t id) 2920 + { 2921 + struct tk_read_base *tkr_raw = &tk_core.timekeeper.tkr_raw; 2922 + struct tk_data *aux_tkd = aux_get_tk_data(id); 2923 + struct timekeeper *aux_tks = &aux_tkd->shadow_timekeeper; 2924 + 2925 + /* Prevent the core timekeeper from changing. */ 2926 + guard(raw_spinlock_irq)(&tk_core.lock); 2927 + 2928 + /* 2929 + * Setup the auxiliary clock assuming that the raw core timekeeper 2930 + * clock frequency conversion is close enough. Userspace has to 2931 + * adjust for the deviation via clock_adjtime(2). 2932 + */ 2933 + guard(raw_spinlock_nested)(&aux_tkd->lock); 2934 + 2935 + /* Remove leftovers of a previous registration */ 2936 + memset(aux_tks, 0, sizeof(*aux_tks)); 2937 + /* Restore the timekeeper id */ 2938 + aux_tks->id = aux_tkd->timekeeper.id; 2939 + /* Setup the timekeeper based on the current system clocksource */ 2940 + tk_setup_internals(aux_tks, tkr_raw->clock); 2941 + 2942 + /* Mark it valid and set it live */ 2943 + aux_tks->clock_valid = true; 2944 + timekeeping_update_from_shadow(aux_tkd, TK_UPDATE_ALL); 2945 + } 2946 + 2947 + static void aux_clock_disable(clockid_t id) 2948 + { 2949 + struct tk_data *aux_tkd = aux_get_tk_data(id); 2950 + 2951 + guard(raw_spinlock_irq)(&aux_tkd->lock); 2952 + aux_tkd->shadow_timekeeper.clock_valid = false; 2953 + timekeeping_update_from_shadow(aux_tkd, TK_UPDATE_ALL); 2954 + } 2955 + 2956 + static DEFINE_MUTEX(aux_clock_mutex); 2957 + 2958 + static ssize_t aux_clock_enable_store(struct kobject *kobj, struct kobj_attribute *attr, 2959 + const char *buf, size_t count) 2960 + { 2961 + /* Lazy atoi() as name is "0..7" */ 2962 + int id = kobj->name[0] & 0x7; 2963 + bool enable; 2964 + 2965 + if (!capable(CAP_SYS_TIME)) 2966 + return -EPERM; 2967 + 2968 + if (kstrtobool(buf, &enable) < 0) 2969 + return -EINVAL; 2970 + 2971 + guard(mutex)(&aux_clock_mutex); 2972 + if (enable == test_bit(id, &aux_timekeepers)) 2973 + return count; 2974 + 2975 + if (enable) { 2976 + aux_clock_enable(CLOCK_AUX + id); 2977 + set_bit(id, &aux_timekeepers); 2978 + } else { 2979 + aux_clock_disable(CLOCK_AUX + id); 2980 + clear_bit(id, &aux_timekeepers); 2981 + } 2982 + return count; 2983 + } 2984 + 2985 + static ssize_t aux_clock_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 2986 + { 2987 + unsigned long active = READ_ONCE(aux_timekeepers); 2988 + /* Lazy atoi() as name is "0..7" */ 2989 + int id = kobj->name[0] & 0x7; 2990 + 2991 + return sysfs_emit(buf, "%d\n", test_bit(id, &active)); 2992 + } 2993 + 2994 + static struct kobj_attribute aux_clock_enable_attr = __ATTR_RW(aux_clock_enable); 2995 + 2996 + static struct attribute *aux_clock_enable_attrs[] = { 2997 + &aux_clock_enable_attr.attr, 2998 + NULL 2999 + }; 3000 + 3001 + static const struct attribute_group aux_clock_enable_attr_group = { 3002 + .attrs = aux_clock_enable_attrs, 3003 + }; 3004 + 3005 + static int __init tk_aux_sysfs_init(void) 3006 + { 3007 + struct kobject *auxo, *tko = kobject_create_and_add("time", kernel_kobj); 3008 + 3009 + if (!tko) 3010 + return -ENOMEM; 3011 + 3012 + auxo = kobject_create_and_add("aux_clocks", tko); 3013 + if (!auxo) { 3014 + kobject_put(tko); 3015 + return -ENOMEM; 3016 + } 3017 + 3018 + for (int i = 0; i <= MAX_AUX_CLOCKS; i++) { 3019 + char id[2] = { [0] = '0' + i, }; 3020 + struct kobject *clk = kobject_create_and_add(id, auxo); 3021 + 3022 + if (!clk) 3023 + return -ENOMEM; 3024 + 3025 + int ret = sysfs_create_group(clk, &aux_clock_enable_attr_group); 3026 + 3027 + if (ret) 3028 + return ret; 3029 + } 3030 + return 0; 3031 + } 3032 + late_initcall(tk_aux_sysfs_init); 2919 3033 2920 3034 static __init void tk_aux_setup(void) 2921 3035 {