Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
CONFIG_KUNIT=y
CONFIG_TIME_KUNIT_TEST=y
+220
View File
@@ -0,0 +1,220 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Timer subsystem related configuration options
#
# Options selectable by arch Kconfig
# Watchdog function for clocksources to detect instabilities
config CLOCKSOURCE_WATCHDOG
bool
# Architecture has extra clocksource init called from registration
config ARCH_CLOCKSOURCE_INIT
bool
config ARCH_WANTS_CLOCKSOURCE_READ_INLINE
bool
# The generic clock events infrastructure
config GENERIC_CLOCKEVENTS
def_bool !LEGACY_TIMER_TICK
# Architecture can handle broadcast in a driver-agnostic way
config ARCH_HAS_TICK_BROADCAST
bool
# Clockevents broadcasting infrastructure
config GENERIC_CLOCKEVENTS_BROADCAST
bool
depends on GENERIC_CLOCKEVENTS
# Handle broadcast in default_idle_call()
config GENERIC_CLOCKEVENTS_BROADCAST_IDLE
bool
depends on GENERIC_CLOCKEVENTS_BROADCAST
# Automatically adjust the min. reprogramming time for
# clock event device
config GENERIC_CLOCKEVENTS_MIN_ADJUST
bool
config GENERIC_CLOCKEVENTS_COUPLED
bool
config GENERIC_CLOCKEVENTS_COUPLED_INLINE
select GENERIC_CLOCKEVENTS_COUPLED
bool
# Generic update of CMOS clock
config GENERIC_CMOS_UPDATE
bool
# Deferred rearming of the hrtimer interrupt
config HRTIMER_REARM_DEFERRED
def_bool y
depends on GENERIC_ENTRY && HAVE_GENERIC_TIF_BITS
depends on HIGH_RES_TIMERS && SCHED_HRTICK
# Select to handle posix CPU timers from task_work
# and not from the timer interrupt context
config HAVE_POSIX_CPU_TIMERS_TASK_WORK
bool
config POSIX_CPU_TIMERS_TASK_WORK
bool
default y if POSIX_TIMERS && HAVE_POSIX_CPU_TIMERS_TASK_WORK
config LEGACY_TIMER_TICK
bool
help
The legacy timer tick helper is used by platforms that
lack support for the generic clockevent framework.
New platforms should use generic clockevents instead.
config TIME_KUNIT_TEST
tristate "KUnit test for kernel/time functions" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
help
Enable this option to test RTC library functions.
If unsure, say N.
config CONTEXT_TRACKING
bool
config CONTEXT_TRACKING_IDLE
bool
select CONTEXT_TRACKING
help
Tracks idle state on behalf of RCU.
menu "Timers subsystem"
if GENERIC_CLOCKEVENTS
# Core internal switch. Selected by NO_HZ_COMMON / HIGH_RES_TIMERS. This is
# only related to the tick functionality. Oneshot clockevent devices
# are supported independent of this.
config TICK_ONESHOT
bool
config NO_HZ_COMMON
bool
select TICK_ONESHOT
choice
prompt "Timer tick handling"
default NO_HZ_IDLE if NO_HZ
config HZ_PERIODIC
bool "Periodic timer ticks (constant rate, no dynticks)"
help
This option keeps the tick running periodically at a constant
rate, even when the CPU doesn't need it.
config NO_HZ_IDLE
bool "Idle dynticks system (tickless idle)"
select NO_HZ_COMMON
help
This option enables a tickless idle system: timer interrupts
will only trigger on an as-needed basis when the system is idle.
This is usually interesting for energy saving.
Most of the time you want to say Y here.
config NO_HZ_FULL
bool "Full dynticks system (tickless)"
# NO_HZ_COMMON dependency
# We need at least one periodic CPU for timekeeping
depends on SMP
depends on HAVE_CONTEXT_TRACKING_USER
# VIRT_CPU_ACCOUNTING_GEN dependency
depends on HAVE_VIRT_CPU_ACCOUNTING_GEN
select NO_HZ_COMMON
select RCU_NOCB_CPU
select VIRT_CPU_ACCOUNTING_GEN
select IRQ_WORK
select CPU_ISOLATION
help
Adaptively try to shutdown the tick whenever possible, even when
the CPU is running tasks. Typically this requires running a single
task on the CPU. Chances for running tickless are maximized when
the task mostly runs in userspace and has few kernel activity.
You need to fill up the nohz_full boot parameter with the
desired range of dynticks CPUs to use it. This is implemented at
the expense of some overhead in user <-> kernel transitions:
syscalls, exceptions and interrupts.
By default, without passing the nohz_full parameter, this behaves just
like NO_HZ_IDLE.
If you're a distro say Y.
endchoice
config CONTEXT_TRACKING_USER
bool
depends on HAVE_CONTEXT_TRACKING_USER
select CONTEXT_TRACKING
help
Track transitions between kernel and user on behalf of RCU and
tickless cputime accounting. The former case relies on context
tracking to enter/exit RCU extended quiescent states.
config CONTEXT_TRACKING_USER_FORCE
bool "Force user context tracking"
depends on CONTEXT_TRACKING_USER
default y if !NO_HZ_FULL
help
The major pre-requirement for full dynticks to work is to
support the user context tracking subsystem. But there are also
other dependencies to provide in order to make the full
dynticks working.
This option stands for testing when an arch implements the
user context tracking backend but doesn't yet fulfill all the
requirements to make the full dynticks feature working.
Without the full dynticks, there is no way to test the support
for user context tracking and the subsystems that rely on it: RCU
userspace extended quiescent state and tickless cputime
accounting. This option copes with the absence of the full
dynticks subsystem by forcing the user context tracking on all
CPUs in the system.
Say Y only if you're working on the development of an
architecture backend for the user context tracking.
Say N otherwise, this option brings an overhead that you
don't want in production.
config NO_HZ
bool "Old Idle dynticks config"
help
This is the old config entry that enables dynticks idle.
We keep it around for a little while to enforce backward
compatibility with older config files.
config HIGH_RES_TIMERS
bool "High Resolution Timer Support"
select TICK_ONESHOT
help
This option enables high resolution timer support. If your
hardware is not capable then this option only increases
the size of the kernel image.
endif
config POSIX_AUX_CLOCKS
bool "Enable auxiliary POSIX clocks"
depends on POSIX_TIMERS
help
Auxiliary POSIX clocks are clocks which can be steered
independently of the core timekeeper, which controls the
MONOTONIC, REALTIME, BOOTTIME and TAI clocks. They are useful to
provide e.g. lockless time accessors to independent PTP clocks
and other clock domains, which are not correlated to the TAI/NTP
notion of time.
endmenu
+35
View File
@@ -0,0 +1,35 @@
# SPDX-License-Identifier: GPL-2.0
# Branch profiling isn't noinstr-safe
ifdef CONFIG_TRACE_BRANCH_PROFILING
CFLAGS_sched_clock.o += -DDISABLE_BRANCH_PROFILING
endif
obj-y += time.o timer.o hrtimer.o sleep_timeout.o
obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o
obj-y += timeconv.o timecounter.o alarmtimer.o
ifeq ($(CONFIG_POSIX_TIMERS),y)
obj-y += posix-timers.o posix-cpu-timers.o posix-clock.o itimer.o
else
obj-y += posix-stubs.o
endif
obj-$(CONFIG_GENERIC_CLOCKEVENTS) += clockevents.o tick-common.o
ifeq ($(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST),y)
obj-y += tick-broadcast.o
obj-$(CONFIG_TICK_ONESHOT) += tick-broadcast-hrtimer.o
endif
obj-$(CONFIG_GENERIC_SCHED_CLOCK) += sched_clock.o
obj-$(CONFIG_TICK_ONESHOT) += tick-oneshot.o tick-sched.o
obj-$(CONFIG_LEGACY_TIMER_TICK) += tick-legacy.o
ifeq ($(CONFIG_SMP),y)
obj-$(CONFIG_NO_HZ_COMMON) += timer_migration.o
endif
obj-$(CONFIG_GENERIC_GETTIMEOFDAY) += vsyscall.o
obj-$(CONFIG_DEBUG_FS) += timekeeping_debug.o
obj-$(CONFIG_TEST_UDELAY) += test_udelay.o
obj-$(CONFIG_TIME_NS) += namespace.o
obj-$(CONFIG_TIME_NS_VDSO) += namespace_vdso.o
obj-$(CONFIG_TEST_CLOCKSOURCE_WATCHDOG) += clocksource-wdtest.o
obj-$(CONFIG_TIME_KUNIT_TEST) += time_test.o
+885
View File
@@ -0,0 +1,885 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Alarmtimer interface
*
* This interface provides a timer which is similar to hrtimers,
* but triggers a RTC alarm if the box is suspend.
*
* This interface is influenced by the Android RTC Alarm timer
* interface.
*
* Copyright (C) 2010 IBM Corporation
*
* Author: John Stultz <john.stultz@linaro.org>
*/
#include <linux/time.h>
#include <linux/hrtimer.h>
#include <linux/timerqueue.h>
#include <linux/rtc.h>
#include <linux/sched/signal.h>
#include <linux/sched/debug.h>
#include <linux/alarmtimer.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/posix-timers.h>
#include <linux/workqueue.h>
#include <linux/freezer.h>
#include <linux/compat.h>
#include <linux/module.h>
#include <linux/time_namespace.h>
#include "posix-timers.h"
#define CREATE_TRACE_POINTS
#include <trace/events/alarmtimer.h>
/**
* struct alarm_base - Alarm timer bases
* @lock: Lock for synchronized access to the base
* @timerqueue: Timerqueue head managing the list of events
* @get_ktime: Function to read the time correlating to the base
* @get_timespec: Function to read the namespace time correlating to the base
* @base_clockid: clockid for the base
*/
static struct alarm_base {
spinlock_t lock;
struct timerqueue_head timerqueue;
ktime_t (*get_ktime)(void);
void (*get_timespec)(struct timespec64 *tp);
clockid_t base_clockid;
} alarm_bases[ALARM_NUMTYPE];
#if defined(CONFIG_POSIX_TIMERS) || defined(CONFIG_RTC_CLASS)
/* freezer information to handle clock_nanosleep triggered wakeups */
static enum alarmtimer_type freezer_alarmtype;
static ktime_t freezer_expires;
static ktime_t freezer_delta;
static DEFINE_SPINLOCK(freezer_delta_lock);
#endif
#ifdef CONFIG_RTC_CLASS
/* rtc timer and device for setting alarm wakeups at suspend */
static struct rtc_timer rtctimer;
static struct rtc_device *rtcdev;
static DEFINE_SPINLOCK(rtcdev_lock);
/**
* alarmtimer_get_rtcdev - Return selected rtcdevice
*
* This function returns the rtc device to use for wakealarms.
*/
struct rtc_device *alarmtimer_get_rtcdev(void)
{
struct rtc_device *ret;
guard(spinlock_irqsave)(&rtcdev_lock);
ret = rtcdev;
return ret;
}
EXPORT_SYMBOL_GPL(alarmtimer_get_rtcdev);
static int alarmtimer_rtc_add_device(struct device *dev)
{
struct rtc_device *rtc = to_rtc_device(dev);
struct platform_device *pdev;
int ret = 0;
if (rtcdev)
return -EBUSY;
if (!test_bit(RTC_FEATURE_ALARM, rtc->features))
return -1;
if (!device_may_wakeup(rtc->dev.parent))
return -1;
pdev = platform_device_register_data(dev, "alarmtimer",
PLATFORM_DEVID_AUTO, NULL, 0);
if (!IS_ERR(pdev))
device_init_wakeup(&pdev->dev, true);
scoped_guard(spinlock_irqsave, &rtcdev_lock) {
if (!IS_ERR(pdev) && !rtcdev && try_module_get(rtc->owner)) {
rtcdev = rtc;
/* hold a reference so it doesn't go away */
get_device(dev);
pdev = NULL;
} else {
ret = -1;
}
}
platform_device_unregister(pdev);
return ret;
}
static inline void alarmtimer_rtc_timer_init(void)
{
rtc_timer_init(&rtctimer, NULL, NULL);
}
static struct class_interface alarmtimer_rtc_interface = {
.add_dev = &alarmtimer_rtc_add_device,
};
static int alarmtimer_rtc_interface_setup(void)
{
alarmtimer_rtc_interface.class = &rtc_class;
return class_interface_register(&alarmtimer_rtc_interface);
}
static void alarmtimer_rtc_interface_remove(void)
{
class_interface_unregister(&alarmtimer_rtc_interface);
}
#else
static inline int alarmtimer_rtc_interface_setup(void) { return 0; }
static inline void alarmtimer_rtc_interface_remove(void) { }
static inline void alarmtimer_rtc_timer_init(void) { }
#endif
/**
* alarmtimer_enqueue - Adds an alarm timer to an alarm_base timerqueue
* @base: pointer to the base where the timer is being run
* @alarm: pointer to alarm being enqueued.
*
* Adds alarm to a alarm_base timerqueue
*
* Must hold base->lock when calling.
*/
static void alarmtimer_enqueue(struct alarm_base *base, struct alarm *alarm)
{
if (alarm->state & ALARMTIMER_STATE_ENQUEUED)
timerqueue_del(&base->timerqueue, &alarm->node);
timerqueue_add(&base->timerqueue, &alarm->node);
alarm->state |= ALARMTIMER_STATE_ENQUEUED;
}
/**
* alarmtimer_dequeue - Removes an alarm timer from an alarm_base timerqueue
* @base: pointer to the base where the timer is running
* @alarm: pointer to alarm being removed
*
* Removes alarm to a alarm_base timerqueue
*
* Must hold base->lock when calling.
*/
static void alarmtimer_dequeue(struct alarm_base *base, struct alarm *alarm)
{
if (!(alarm->state & ALARMTIMER_STATE_ENQUEUED))
return;
timerqueue_del(&base->timerqueue, &alarm->node);
alarm->state &= ~ALARMTIMER_STATE_ENQUEUED;
}
/**
* alarmtimer_fired - Handles alarm hrtimer being fired.
* @timer: pointer to hrtimer being run
*
* When a alarm timer fires, this runs through the timerqueue to
* see which alarms expired, and runs those. If there are more alarm
* timers queued for the future, we set the hrtimer to fire when
* the next future alarm timer expires.
*/
static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer)
{
struct alarm *alarm = container_of(timer, struct alarm, timer);
struct alarm_base *base = &alarm_bases[alarm->type];
scoped_guard(spinlock_irqsave, &base->lock)
alarmtimer_dequeue(base, alarm);
if (alarm->function)
alarm->function(alarm, base->get_ktime());
trace_alarmtimer_fired(alarm, base->get_ktime());
return HRTIMER_NORESTART;
}
ktime_t alarm_expires_remaining(const struct alarm *alarm)
{
struct alarm_base *base = &alarm_bases[alarm->type];
return ktime_sub(alarm->node.expires, base->get_ktime());
}
EXPORT_SYMBOL_GPL(alarm_expires_remaining);
#ifdef CONFIG_RTC_CLASS
/**
* alarmtimer_suspend - Suspend time callback
* @dev: unused
*
* When we are going into suspend, we look through the bases
* to see which is the soonest timer to expire. We then
* set an rtc timer to fire that far into the future, which
* will wake us from suspend.
*/
static int alarmtimer_suspend(struct device *dev)
{
ktime_t min, now, expires;
struct rtc_device *rtc;
struct rtc_time tm;
int i, ret, type;
scoped_guard(spinlock_irqsave, &freezer_delta_lock) {
min = freezer_delta;
expires = freezer_expires;
type = freezer_alarmtype;
freezer_delta = 0;
}
rtc = alarmtimer_get_rtcdev();
/* If we have no rtcdev, just return */
if (!rtc)
return 0;
/* Find the soonest timer to expire */
for (i = 0; i < ALARM_NUMTYPE; i++) {
struct alarm_base *base = &alarm_bases[i];
struct timerqueue_node *next;
ktime_t next_expires;
ktime_t delta;
scoped_guard(spinlock_irqsave, &base->lock) {
next = timerqueue_getnext(&base->timerqueue);
if (next)
next_expires = next->expires;
}
if (!next)
continue;
delta = ktime_sub(next_expires, base->get_ktime());
if (!min || (delta < min)) {
expires = next_expires;
min = delta;
type = i;
}
}
if (min == 0)
return 0;
if (ktime_to_ns(min) < 2 * NSEC_PER_SEC) {
pm_wakeup_event(dev, 2 * MSEC_PER_SEC);
return -EBUSY;
}
trace_alarmtimer_suspend(expires, type);
/* Setup an rtc timer to fire that far in the future */
rtc_timer_cancel(rtc, &rtctimer);
rtc_read_time(rtc, &tm);
now = rtc_tm_to_ktime(tm);
/*
* If the RTC alarm timer only supports a limited time offset, set the
* alarm time to the maximum supported value.
* The system may wake up earlier (possibly much earlier) than expected
* when the alarmtimer runs. This is the best the kernel can do if
* the alarmtimer exceeds the time that the rtc device can be programmed
* for.
*/
min = rtc_bound_alarmtime(rtc, min);
now = ktime_add(now, min);
/* Set alarm, if in the past reject suspend briefly to handle */
ret = rtc_timer_start(rtc, &rtctimer, now, 0);
if (ret < 0)
pm_wakeup_event(dev, MSEC_PER_SEC);
return ret;
}
static int alarmtimer_resume(struct device *dev)
{
struct rtc_device *rtc;
rtc = alarmtimer_get_rtcdev();
if (rtc)
rtc_timer_cancel(rtc, &rtctimer);
return 0;
}
#else
static int alarmtimer_suspend(struct device *dev)
{
return 0;
}
static int alarmtimer_resume(struct device *dev)
{
return 0;
}
#endif
static void
__alarm_init(struct alarm *alarm, enum alarmtimer_type type,
void (*function)(struct alarm *, ktime_t))
{
timerqueue_init(&alarm->node);
alarm->function = function;
alarm->type = type;
alarm->state = ALARMTIMER_STATE_INACTIVE;
}
/**
* alarm_init - Initialize an alarm structure
* @alarm: ptr to alarm to be initialized
* @type: the type of the alarm
* @function: callback that is run when the alarm fires
*/
void alarm_init(struct alarm *alarm, enum alarmtimer_type type,
void (*function)(struct alarm *, ktime_t))
{
hrtimer_setup(&alarm->timer, alarmtimer_fired, alarm_bases[type].base_clockid,
HRTIMER_MODE_ABS);
__alarm_init(alarm, type, function);
}
EXPORT_SYMBOL_GPL(alarm_init);
/**
* alarm_start_timer - Sets an alarm to fire
* @alarm: Pointer to alarm to set
* @expires: Expiry time
* @relative: True if @expires is relative
*
* Returns: True if the alarm was queued. False if it already expired
*/
bool alarm_start_timer(struct alarm *alarm, ktime_t expires, bool relative)
{
struct alarm_base *base = &alarm_bases[alarm->type];
if (relative)
expires = ktime_add_safe(expires, base->get_ktime());
trace_alarmtimer_start(alarm, base->get_ktime());
guard(spinlock_irqsave)(&base->lock);
alarm->node.expires = expires;
alarmtimer_enqueue(base, alarm);
if (!hrtimer_start_range_ns_user(&alarm->timer, expires, 0, HRTIMER_MODE_ABS)) {
alarmtimer_dequeue(base, alarm);
return false;
}
return true;
}
EXPORT_SYMBOL_GPL(alarm_start_timer);
/**
* alarm_try_to_cancel - Tries to cancel an alarm timer
* @alarm: ptr to alarm to be canceled
*
* Returns 1 if the timer was canceled, 0 if it was not running,
* and -1 if the callback was running
*/
int alarm_try_to_cancel(struct alarm *alarm)
{
struct alarm_base *base = &alarm_bases[alarm->type];
int ret;
scoped_guard(spinlock_irqsave, &base->lock) {
ret = hrtimer_try_to_cancel(&alarm->timer);
if (ret >= 0)
alarmtimer_dequeue(base, alarm);
}
trace_alarmtimer_cancel(alarm, base->get_ktime());
return ret;
}
EXPORT_SYMBOL_GPL(alarm_try_to_cancel);
/**
* alarm_cancel - Spins trying to cancel an alarm timer until it is done
* @alarm: ptr to alarm to be canceled
*
* Returns 1 if the timer was canceled, 0 if it was not active.
*/
int alarm_cancel(struct alarm *alarm)
{
for (;;) {
int ret = alarm_try_to_cancel(alarm);
if (ret >= 0)
return ret;
hrtimer_cancel_wait_running(&alarm->timer);
}
}
EXPORT_SYMBOL_GPL(alarm_cancel);
u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval)
{
u64 overrun = 1;
ktime_t delta;
delta = ktime_sub(now, alarm->node.expires);
if (delta < 0)
return 0;
if (unlikely(delta >= interval)) {
s64 incr = ktime_to_ns(interval);
overrun = ktime_divns(delta, incr);
alarm->node.expires = ktime_add_ns(alarm->node.expires,
incr*overrun);
if (alarm->node.expires > now)
return overrun;
/*
* This (and the ktime_add() below) is the
* correction for exact:
*/
overrun++;
}
alarm->node.expires = ktime_add_safe(alarm->node.expires, interval);
return overrun;
}
EXPORT_SYMBOL_GPL(alarm_forward);
u64 alarm_forward_now(struct alarm *alarm, ktime_t interval)
{
struct alarm_base *base = &alarm_bases[alarm->type];
return alarm_forward(alarm, base->get_ktime(), interval);
}
EXPORT_SYMBOL_GPL(alarm_forward_now);
#ifdef CONFIG_POSIX_TIMERS
static void alarmtimer_freezerset(ktime_t absexp, enum alarmtimer_type type)
{
struct alarm_base *base;
ktime_t delta;
switch(type) {
case ALARM_REALTIME:
base = &alarm_bases[ALARM_REALTIME];
type = ALARM_REALTIME_FREEZER;
break;
case ALARM_BOOTTIME:
base = &alarm_bases[ALARM_BOOTTIME];
type = ALARM_BOOTTIME_FREEZER;
break;
default:
WARN_ONCE(1, "Invalid alarm type: %d\n", type);
return;
}
delta = ktime_sub(absexp, base->get_ktime());
guard(spinlock_irqsave)(&freezer_delta_lock);
if (!freezer_delta || (delta < freezer_delta)) {
freezer_delta = delta;
freezer_expires = absexp;
freezer_alarmtype = type;
}
}
/**
* clock2alarm - helper that converts from clockid to alarmtypes
* @clockid: clockid.
*/
static enum alarmtimer_type clock2alarm(clockid_t clockid)
{
if (clockid == CLOCK_REALTIME_ALARM)
return ALARM_REALTIME;
WARN_ON_ONCE(clockid != CLOCK_BOOTTIME_ALARM);
return ALARM_BOOTTIME;
}
/**
* alarm_handle_timer - Callback for posix timers
* @alarm: alarm that fired
* @now: time at the timer expiration
*
* Posix timer callback for expired alarm timers.
*/
static void alarm_handle_timer(struct alarm *alarm, ktime_t now)
{
struct k_itimer *ptr = container_of(alarm, struct k_itimer, it.alarm.alarmtimer);
guard(spinlock_irqsave)(&ptr->it_lock);
posix_timer_queue_signal(ptr);
}
/**
* alarm_timer_rearm - Posix timer callback for rearming timer
* @timr: Pointer to the posixtimer data struct
*/
static bool alarm_timer_rearm(struct k_itimer *timr)
{
struct alarm *alarm = &timr->it.alarm.alarmtimer;
timr->it_overrun += alarm_forward_now(alarm, timr->it_interval);
return alarm_start_timer(alarm, alarm->node.expires, false);
}
/**
* alarm_timer_forward - Posix timer callback for forwarding timer
* @timr: Pointer to the posixtimer data struct
* @now: Current time to forward the timer against
*/
static s64 alarm_timer_forward(struct k_itimer *timr, ktime_t now)
{
struct alarm *alarm = &timr->it.alarm.alarmtimer;
return alarm_forward(alarm, now, timr->it_interval);
}
/**
* alarm_timer_remaining - Posix timer callback to retrieve remaining time
* @timr: Pointer to the posixtimer data struct
* @now: Current time to calculate against
*/
static ktime_t alarm_timer_remaining(struct k_itimer *timr, ktime_t now)
{
struct alarm *alarm = &timr->it.alarm.alarmtimer;
return ktime_sub(alarm->node.expires, now);
}
/**
* alarm_timer_try_to_cancel - Posix timer callback to cancel a timer
* @timr: Pointer to the posixtimer data struct
*/
static int alarm_timer_try_to_cancel(struct k_itimer *timr)
{
return alarm_try_to_cancel(&timr->it.alarm.alarmtimer);
}
/**
* alarm_timer_wait_running - Posix timer callback to wait for a timer
* @timr: Pointer to the posixtimer data struct
*
* Called from the core code when timer cancel detected that the callback
* is running. @timr is unlocked and rcu read lock is held to prevent it
* from being freed.
*/
static void alarm_timer_wait_running(struct k_itimer *timr)
{
hrtimer_cancel_wait_running(&timr->it.alarm.alarmtimer.timer);
}
/**
* alarm_timer_arm - Posix timer callback to arm a timer
* @timr: Pointer to the posixtimer data struct
* @expires: The new expiry time
* @absolute: Expiry value is absolute time
* @sigev_none: Posix timer does not deliver signals
*/
static bool alarm_timer_arm(struct k_itimer *timr, ktime_t expires,
bool absolute, bool sigev_none)
{
struct alarm *alarm = &timr->it.alarm.alarmtimer;
struct alarm_base *base = &alarm_bases[alarm->type];
if (!absolute)
expires = ktime_add_safe(expires, base->get_ktime());
/*
* sigev_none needs to update the expires value and pretend
* that the timer is queued
*/
if (sigev_none) {
alarm->node.expires = expires;
return true;
}
return alarm_start_timer(&timr->it.alarm.alarmtimer, expires, false);
}
/**
* alarm_clock_getres - posix getres interface
* @which_clock: clockid
* @tp: timespec to fill
*
* Returns the granularity of underlying alarm base clock
*/
static int alarm_clock_getres(const clockid_t which_clock, struct timespec64 *tp)
{
if (!alarmtimer_get_rtcdev())
return -EINVAL;
tp->tv_sec = 0;
tp->tv_nsec = hrtimer_resolution;
return 0;
}
/**
* alarm_clock_get_timespec - posix clock_get_timespec interface
* @which_clock: clockid
* @tp: timespec to fill.
*
* Provides the underlying alarm base time in a tasks time namespace.
*/
static int alarm_clock_get_timespec(clockid_t which_clock, struct timespec64 *tp)
{
struct alarm_base *base = &alarm_bases[clock2alarm(which_clock)];
if (!alarmtimer_get_rtcdev())
return -EINVAL;
base->get_timespec(tp);
return 0;
}
/**
* alarm_clock_get_ktime - posix clock_get_ktime interface
* @which_clock: clockid
*
* Provides the underlying alarm base time in the root namespace.
*/
static ktime_t alarm_clock_get_ktime(clockid_t which_clock)
{
struct alarm_base *base = &alarm_bases[clock2alarm(which_clock)];
if (!alarmtimer_get_rtcdev())
return -EINVAL;
return base->get_ktime();
}
/**
* alarm_timer_create - posix timer_create interface
* @new_timer: k_itimer pointer to manage
*
* Initializes the k_itimer structure.
*/
static int alarm_timer_create(struct k_itimer *new_timer)
{
enum alarmtimer_type type;
if (!alarmtimer_get_rtcdev())
return -EOPNOTSUPP;
if (!capable(CAP_WAKE_ALARM))
return -EPERM;
type = clock2alarm(new_timer->it_clock);
alarm_init(&new_timer->it.alarm.alarmtimer, type, alarm_handle_timer);
return 0;
}
/**
* alarmtimer_nsleep_wakeup - Wakeup function for alarm_timer_nsleep
* @alarm: ptr to alarm that fired
* @now: time at the timer expiration
*
* Wakes up the task that set the alarmtimer
*/
static void alarmtimer_nsleep_wakeup(struct alarm *alarm, ktime_t now)
{
struct task_struct *task = alarm->data;
alarm->data = NULL;
if (task)
wake_up_process(task);
}
/**
* alarmtimer_do_nsleep - Internal alarmtimer nsleep implementation
* @alarm: ptr to alarmtimer
* @absexp: absolute expiration time
* @type: alarm type (BOOTTIME/REALTIME).
*
* Sets the alarm timer and sleeps until it is fired or interrupted.
*/
static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp,
enum alarmtimer_type type)
{
struct restart_block *restart;
alarm->data = (void *)current;
do {
set_current_state(TASK_INTERRUPTIBLE);
if (!alarm_start_timer(alarm, absexp, false))
alarm->data = NULL;
if (likely(alarm->data))
schedule();
alarm_cancel(alarm);
} while (alarm->data && !signal_pending(current));
__set_current_state(TASK_RUNNING);
destroy_hrtimer_on_stack(&alarm->timer);
if (!alarm->data)
return 0;
if (freezing(current))
alarmtimer_freezerset(absexp, type);
restart = &current->restart_block;
if (restart->nanosleep.type != TT_NONE) {
struct timespec64 rmt;
ktime_t rem;
rem = ktime_sub(absexp, alarm_bases[type].get_ktime());
if (rem <= 0)
return 0;
rmt = ktime_to_timespec64(rem);
return nanosleep_copyout(restart, &rmt);
}
return -ERESTART_RESTARTBLOCK;
}
static void
alarm_init_on_stack(struct alarm *alarm, enum alarmtimer_type type,
void (*function)(struct alarm *, ktime_t))
{
hrtimer_setup_on_stack(&alarm->timer, alarmtimer_fired, alarm_bases[type].base_clockid,
HRTIMER_MODE_ABS);
__alarm_init(alarm, type, function);
}
/**
* alarm_timer_nsleep_restart - restartblock alarmtimer nsleep
* @restart: ptr to restart block
*
* Handles restarted clock_nanosleep calls
*/
static long __sched alarm_timer_nsleep_restart(struct restart_block *restart)
{
enum alarmtimer_type type = restart->nanosleep.clockid;
ktime_t exp = restart->nanosleep.expires;
struct alarm alarm;
alarm_init_on_stack(&alarm, type, alarmtimer_nsleep_wakeup);
return alarmtimer_do_nsleep(&alarm, exp, type);
}
/**
* alarm_timer_nsleep - alarmtimer nanosleep
* @which_clock: clockid
* @flags: determines abstime or relative
* @tsreq: requested sleep time (abs or rel)
*
* Handles clock_nanosleep calls against _ALARM clockids
*/
static int alarm_timer_nsleep(const clockid_t which_clock, int flags,
const struct timespec64 *tsreq)
{
enum alarmtimer_type type = clock2alarm(which_clock);
struct restart_block *restart = &current->restart_block;
struct alarm alarm;
ktime_t exp;
int ret;
if (!alarmtimer_get_rtcdev())
return -EOPNOTSUPP;
if (flags & ~TIMER_ABSTIME)
return -EINVAL;
if (!capable(CAP_WAKE_ALARM))
return -EPERM;
alarm_init_on_stack(&alarm, type, alarmtimer_nsleep_wakeup);
exp = timespec64_to_ktime(*tsreq);
/* Convert (if necessary) to absolute time */
if (flags != TIMER_ABSTIME) {
ktime_t now = alarm_bases[type].get_ktime();
exp = ktime_add_safe(now, exp);
} else {
exp = timens_ktime_to_host(which_clock, exp);
}
ret = alarmtimer_do_nsleep(&alarm, exp, type);
if (ret != -ERESTART_RESTARTBLOCK)
return ret;
/* abs timers don't set remaining time or restart */
if (flags == TIMER_ABSTIME)
return -ERESTARTNOHAND;
restart->nanosleep.clockid = type;
restart->nanosleep.expires = exp;
set_restart_fn(restart, alarm_timer_nsleep_restart);
return ret;
}
const struct k_clock alarm_clock = {
.clock_getres = alarm_clock_getres,
.clock_get_ktime = alarm_clock_get_ktime,
.clock_get_timespec = alarm_clock_get_timespec,
.timer_create = alarm_timer_create,
.timer_set = common_timer_set,
.timer_del = common_timer_del,
.timer_get = common_timer_get,
.timer_arm = alarm_timer_arm,
.timer_rearm = alarm_timer_rearm,
.timer_forward = alarm_timer_forward,
.timer_remaining = alarm_timer_remaining,
.timer_try_to_cancel = alarm_timer_try_to_cancel,
.timer_wait_running = alarm_timer_wait_running,
.nsleep = alarm_timer_nsleep,
};
#endif /* CONFIG_POSIX_TIMERS */
/* Suspend hook structures */
static const struct dev_pm_ops alarmtimer_pm_ops = {
.suspend = alarmtimer_suspend,
.resume = alarmtimer_resume,
};
static struct platform_driver alarmtimer_driver = {
.driver = {
.name = "alarmtimer",
.pm = &alarmtimer_pm_ops,
}
};
static void get_boottime_timespec(struct timespec64 *tp)
{
ktime_get_boottime_ts64(tp);
timens_add_boottime(tp);
}
/**
* alarmtimer_init - Initialize alarm timer code
*
* This function initializes the alarm bases and registers
* the posix clock ids.
*/
static int __init alarmtimer_init(void)
{
int error;
int i;
alarmtimer_rtc_timer_init();
/* Initialize alarm bases */
alarm_bases[ALARM_REALTIME].base_clockid = CLOCK_REALTIME;
alarm_bases[ALARM_REALTIME].get_ktime = &ktime_get_real;
alarm_bases[ALARM_REALTIME].get_timespec = ktime_get_real_ts64;
alarm_bases[ALARM_BOOTTIME].base_clockid = CLOCK_BOOTTIME;
alarm_bases[ALARM_BOOTTIME].get_ktime = &ktime_get_boottime;
alarm_bases[ALARM_BOOTTIME].get_timespec = get_boottime_timespec;
for (i = 0; i < ALARM_NUMTYPE; i++) {
timerqueue_init_head(&alarm_bases[i].timerqueue);
spin_lock_init(&alarm_bases[i].lock);
}
error = alarmtimer_rtc_interface_setup();
if (error)
return error;
error = platform_driver_register(&alarmtimer_driver);
if (error)
goto out_if;
return 0;
out_if:
alarmtimer_rtc_interface_remove();
return error;
}
device_initcall(alarmtimer_init);
+828
View File
@@ -0,0 +1,828 @@
// SPDX-License-Identifier: GPL-2.0
/*
* This file contains functions which manage clock event devices.
*
* Copyright(C) 2005-2006, Linutronix GmbH, Thomas Gleixner <tglx@kernel.org>
* Copyright(C) 2005-2007, Red Hat, Inc., Ingo Molnar
* Copyright(C) 2006-2007, Timesys Corp., Thomas Gleixner
*/
#include <linux/clockchips.h>
#include <linux/hrtimer.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/smp.h>
#include <linux/device.h>
#include "tick-internal.h"
/* The registered clock event devices */
static LIST_HEAD(clockevent_devices);
static LIST_HEAD(clockevents_released);
/* Protection for the above */
static DEFINE_RAW_SPINLOCK(clockevents_lock);
/* Protection for unbind operations */
static DEFINE_MUTEX(clockevents_mutex);
struct ce_unbind {
struct clock_event_device *ce;
int res;
};
static u64 cev_delta2ns(unsigned long latch, struct clock_event_device *evt,
bool ismax)
{
u64 clc = (u64) latch << evt->shift;
u64 rnd;
if (WARN_ON(!evt->mult))
evt->mult = 1;
rnd = (u64) evt->mult - 1;
/*
* Upper bound sanity check. If the backwards conversion is
* not equal latch, we know that the above shift overflowed.
*/
if ((clc >> evt->shift) != (u64)latch)
clc = ~0ULL;
/*
* Scaled math oddities:
*
* For mult <= (1 << shift) we can safely add mult - 1 to
* prevent integer rounding loss. So the backwards conversion
* from nsec to device ticks will be correct.
*
* For mult > (1 << shift), i.e. device frequency is > 1GHz we
* need to be careful. Adding mult - 1 will result in a value
* which when converted back to device ticks can be larger
* than latch by up to (mult - 1) >> shift. For the min_delta
* calculation we still want to apply this in order to stay
* above the minimum device ticks limit. For the upper limit
* we would end up with a latch value larger than the upper
* limit of the device, so we omit the add to stay below the
* device upper boundary.
*
* Also omit the add if it would overflow the u64 boundary.
*/
if ((~0ULL - clc > rnd) &&
(!ismax || evt->mult <= (1ULL << evt->shift)))
clc += rnd;
do_div(clc, evt->mult);
/* Deltas less than 1usec are pointless noise */
return clc > 1000 ? clc : 1000;
}
/**
* clockevent_delta2ns - Convert a latch value (device ticks) to nanoseconds
* @latch: value to convert
* @evt: pointer to clock event device descriptor
*
* Math helper, returns latch value converted to nanoseconds (bound checked)
*/
u64 clockevent_delta2ns(unsigned long latch, struct clock_event_device *evt)
{
return cev_delta2ns(latch, evt, false);
}
EXPORT_SYMBOL_GPL(clockevent_delta2ns);
static int __clockevents_switch_state(struct clock_event_device *dev,
enum clock_event_state state)
{
if (dev->features & CLOCK_EVT_FEAT_DUMMY)
return 0;
/* On state transitions clear the forced flag unconditionally */
dev->next_event_forced = 0;
/* Transition with new state-specific callbacks */
switch (state) {
case CLOCK_EVT_STATE_DETACHED:
/* The clockevent device is getting replaced. Shut it down. */
case CLOCK_EVT_STATE_SHUTDOWN:
if (dev->set_state_shutdown)
return dev->set_state_shutdown(dev);
return 0;
case CLOCK_EVT_STATE_PERIODIC:
/* Core internal bug */
if (!(dev->features & CLOCK_EVT_FEAT_PERIODIC))
return -ENOSYS;
if (dev->set_state_periodic)
return dev->set_state_periodic(dev);
return 0;
case CLOCK_EVT_STATE_ONESHOT:
/* Core internal bug */
if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT))
return -ENOSYS;
if (dev->set_state_oneshot)
return dev->set_state_oneshot(dev);
return 0;
case CLOCK_EVT_STATE_ONESHOT_STOPPED:
/* Core internal bug */
if (WARN_ONCE(!clockevent_state_oneshot(dev),
"Current state: %d\n",
clockevent_get_state(dev)))
return -EINVAL;
if (dev->set_state_oneshot_stopped)
return dev->set_state_oneshot_stopped(dev);
else
return -ENOSYS;
default:
return -ENOSYS;
}
}
/**
* clockevents_switch_state - set the operating state of a clock event device
* @dev: device to modify
* @state: new state
*
* Must be called with interrupts disabled !
*/
void clockevents_switch_state(struct clock_event_device *dev,
enum clock_event_state state)
{
if (clockevent_get_state(dev) != state) {
if (__clockevents_switch_state(dev, state))
return;
clockevent_set_state(dev, state);
/*
* A nsec2cyc multiplicator of 0 is invalid and we'd crash
* on it, so fix it up and emit a warning:
*/
if (clockevent_state_oneshot(dev)) {
if (WARN_ON(!dev->mult))
dev->mult = 1;
}
}
}
/**
* clockevents_shutdown - shutdown the device and clear next_event
* @dev: device to shutdown
*/
void clockevents_shutdown(struct clock_event_device *dev)
{
clockevents_switch_state(dev, CLOCK_EVT_STATE_SHUTDOWN);
dev->next_event = KTIME_MAX;
dev->next_event_forced = 0;
}
/**
* clockevents_tick_resume - Resume the tick device before using it again
* @dev: device to resume
*/
int clockevents_tick_resume(struct clock_event_device *dev)
{
int ret = 0;
if (dev->tick_resume)
ret = dev->tick_resume(dev);
return ret;
}
#ifdef CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST
/* Limit min_delta to a jiffy */
#define MIN_DELTA_LIMIT (NSEC_PER_SEC / HZ)
/**
* clockevents_increase_min_delta - raise minimum delta of a clock event device
* @dev: device to increase the minimum delta
*
* Returns 0 on success, -ETIME when the minimum delta reached the limit.
*/
static int clockevents_increase_min_delta(struct clock_event_device *dev)
{
/* Nothing to do if we already reached the limit */
if (dev->min_delta_ns >= MIN_DELTA_LIMIT) {
printk_deferred(KERN_WARNING
"CE: Reprogramming failure. Giving up\n");
dev->next_event = KTIME_MAX;
return -ETIME;
}
if (dev->min_delta_ns < 5000)
dev->min_delta_ns = 5000;
else
dev->min_delta_ns += dev->min_delta_ns >> 1;
if (dev->min_delta_ns > MIN_DELTA_LIMIT)
dev->min_delta_ns = MIN_DELTA_LIMIT;
printk_deferred(KERN_WARNING
"CE: %s increased min_delta_ns to %llu nsec\n",
dev->name ? dev->name : "?",
(unsigned long long) dev->min_delta_ns);
return 0;
}
/**
* clockevents_program_min_delta - Set clock event device to the minimum delay.
* @dev: device to program
*
* Returns 0 on success, -ETIME when the retry loop failed.
*/
static int clockevents_program_min_delta(struct clock_event_device *dev)
{
unsigned long long clc;
int64_t delta;
int i;
for (i = 0;;) {
delta = dev->min_delta_ns;
dev->next_event = ktime_add_ns(ktime_get(), delta);
if (clockevent_state_shutdown(dev))
return 0;
dev->retries++;
clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
if (dev->set_next_event((unsigned long) clc, dev) == 0)
return 0;
if (++i > 2) {
/*
* We tried 3 times to program the device with the
* given min_delta_ns. Try to increase the minimum
* delta, if that fails as well get out of here.
*/
if (clockevents_increase_min_delta(dev))
return -ETIME;
i = 0;
}
}
}
#else /* CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST */
/**
* clockevents_program_min_delta - Set clock event device to the minimum delay.
* @dev: device to program
*
* Returns 0 on success, -ETIME when the retry loop failed.
*/
static int clockevents_program_min_delta(struct clock_event_device *dev)
{
unsigned long long clc;
int64_t delta = 0;
int i;
for (i = 0; i < 10; i++) {
delta += dev->min_delta_ns;
dev->next_event = ktime_add_ns(ktime_get(), delta);
if (clockevent_state_shutdown(dev))
return 0;
dev->retries++;
clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
if (dev->set_next_event((unsigned long) clc, dev) == 0)
return 0;
}
return -ETIME;
}
#endif /* CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST */
#ifdef CONFIG_GENERIC_CLOCKEVENTS_COUPLED
#ifdef CONFIG_GENERIC_CLOCKEVENTS_COUPLED_INLINE
#include <asm/clock_inlined.h>
#else
static __always_inline void
arch_inlined_clockevent_set_next_coupled(u64 cycles, struct clock_event_device *dev) { }
#endif
static inline bool clockevent_set_next_coupled(struct clock_event_device *dev, ktime_t expires)
{
u64 cycles;
if (unlikely(!(dev->features & CLOCK_EVT_FEAT_CLOCKSOURCE_COUPLED)))
return false;
if (unlikely(!ktime_expiry_to_cycles(dev->cs_id, expires, &cycles)))
return false;
if (IS_ENABLED(CONFIG_GENERIC_CLOCKEVENTS_COUPLED_INLINE))
arch_inlined_clockevent_set_next_coupled(cycles, dev);
else
dev->set_next_coupled(cycles, dev);
return true;
}
#else
static inline bool clockevent_set_next_coupled(struct clock_event_device *dev, ktime_t expires)
{
return false;
}
#endif
/**
* clockevents_program_event - Reprogram the clock event device.
* @dev: device to program
* @expires: absolute expiry time (monotonic clock)
* @force: program minimum delay if expires can not be set
*
* Returns 0 on success, -ETIME when the event is in the past.
*/
int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, bool force)
{
int64_t delta;
u64 cycles;
if (WARN_ON_ONCE(expires < 0))
return -ETIME;
dev->next_event = expires;
if (clockevent_state_shutdown(dev))
return 0;
/* We must be in ONESHOT state here */
WARN_ONCE(!clockevent_state_oneshot(dev), "Current state: %d\n",
clockevent_get_state(dev));
/* ktime_t based reprogramming for the broadcast hrtimer device */
if (unlikely(dev->features & CLOCK_EVT_FEAT_HRTIMER))
return dev->set_next_ktime(expires, dev);
if (likely(clockevent_set_next_coupled(dev, expires)))
return 0;
delta = ktime_to_ns(ktime_sub(expires, ktime_get()));
/* Required for tick_periodic() during early boot */
if (delta <= 0 && !force)
return -ETIME;
if (delta > (int64_t)dev->min_delta_ns) {
delta = min(delta, (int64_t) dev->max_delta_ns);
cycles = ((u64)delta * dev->mult) >> dev->shift;
if (!dev->set_next_event((unsigned long) cycles, dev)) {
dev->next_event_forced = 0;
return 0;
}
}
if (dev->next_event_forced)
return 0;
if (dev->set_next_event(dev->min_delta_ticks, dev)) {
if (!force || clockevents_program_min_delta(dev))
return -ETIME;
}
dev->next_event_forced = 1;
return 0;
}
/*
* Called after a clockevent has been added which might
* have replaced a current regular or broadcast device. A
* released normal device might be a suitable replacement
* for the current broadcast device. Similarly a released
* broadcast device might be a suitable replacement for a
* normal device.
*/
static void clockevents_notify_released(void)
{
struct clock_event_device *dev;
/*
* Keep iterating as long as tick_check_new_device()
* replaces a device.
*/
while (!list_empty(&clockevents_released)) {
dev = list_entry(clockevents_released.next,
struct clock_event_device, list);
list_move(&dev->list, &clockevent_devices);
tick_check_new_device(dev);
}
}
/*
* Try to install a replacement clock event device
*/
static int clockevents_replace(struct clock_event_device *ced)
{
struct clock_event_device *dev, *newdev = NULL;
list_for_each_entry(dev, &clockevent_devices, list) {
if (dev == ced || !clockevent_state_detached(dev))
continue;
if (!tick_check_replacement(newdev, dev))
continue;
if (!try_module_get(dev->owner))
continue;
if (newdev)
module_put(newdev->owner);
newdev = dev;
}
if (newdev) {
tick_install_replacement(newdev);
list_del_init(&ced->list);
}
return newdev ? 0 : -EBUSY;
}
/*
* Called with clockevents_mutex and clockevents_lock held
*/
static int __clockevents_try_unbind(struct clock_event_device *ced, int cpu)
{
/* Fast track. Device is unused */
if (clockevent_state_detached(ced)) {
list_del_init(&ced->list);
return 0;
}
return ced == per_cpu(tick_cpu_device, cpu).evtdev ? -EAGAIN : -EBUSY;
}
/*
* SMP function call to unbind a device
*/
static void __clockevents_unbind(void *arg)
{
struct ce_unbind *cu = arg;
int res;
raw_spin_lock(&clockevents_lock);
res = __clockevents_try_unbind(cu->ce, smp_processor_id());
if (res == -EAGAIN)
res = clockevents_replace(cu->ce);
cu->res = res;
raw_spin_unlock(&clockevents_lock);
}
/*
* Issues smp function call to unbind a per cpu device. Called with
* clockevents_mutex held.
*/
static int clockevents_unbind(struct clock_event_device *ced, int cpu)
{
struct ce_unbind cu = { .ce = ced, .res = -ENODEV };
smp_call_function_single(cpu, __clockevents_unbind, &cu, 1);
return cu.res;
}
/*
* Unbind a clockevents device.
*/
int clockevents_unbind_device(struct clock_event_device *ced, int cpu)
{
int ret;
mutex_lock(&clockevents_mutex);
ret = clockevents_unbind(ced, cpu);
mutex_unlock(&clockevents_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(clockevents_unbind_device);
/**
* clockevents_register_device - register a clock event device
* @dev: device to register
*/
void clockevents_register_device(struct clock_event_device *dev)
{
unsigned long flags;
/* Initialize state to DETACHED */
clockevent_set_state(dev, CLOCK_EVT_STATE_DETACHED);
if (!dev->cpumask) {
WARN_ON(num_possible_cpus() > 1);
dev->cpumask = cpumask_of(smp_processor_id());
}
if (dev->cpumask == cpu_all_mask) {
WARN(1, "%s cpumask == cpu_all_mask, using cpu_possible_mask instead\n",
dev->name);
dev->cpumask = cpu_possible_mask;
}
raw_spin_lock_irqsave(&clockevents_lock, flags);
list_add(&dev->list, &clockevent_devices);
tick_check_new_device(dev);
clockevents_notify_released();
raw_spin_unlock_irqrestore(&clockevents_lock, flags);
}
EXPORT_SYMBOL_GPL(clockevents_register_device);
static void clockevents_config(struct clock_event_device *dev, u32 freq)
{
u64 sec;
if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT))
return;
/*
* Calculate the maximum number of seconds we can sleep. Limit
* to 10 minutes for hardware which can program more than
* 32bit ticks so we still get reasonable conversion values.
*/
sec = dev->max_delta_ticks;
do_div(sec, freq);
if (!sec)
sec = 1;
else if (sec > 600 && dev->max_delta_ticks > UINT_MAX)
sec = 600;
clockevents_calc_mult_shift(dev, freq, sec);
dev->min_delta_ns = cev_delta2ns(dev->min_delta_ticks, dev, false);
dev->max_delta_ns = cev_delta2ns(dev->max_delta_ticks, dev, true);
}
/**
* clockevents_config_and_register - Configure and register a clock event device
* @dev: device to register
* @freq: The clock frequency
* @min_delta: The minimum clock ticks to program in oneshot mode
* @max_delta: The maximum clock ticks to program in oneshot mode
*
* min/max_delta can be 0 for devices which do not support oneshot mode.
*/
void clockevents_config_and_register(struct clock_event_device *dev,
u32 freq, unsigned long min_delta,
unsigned long max_delta)
{
dev->min_delta_ticks = min_delta;
dev->max_delta_ticks = max_delta;
clockevents_config(dev, freq);
clockevents_register_device(dev);
}
EXPORT_SYMBOL_GPL(clockevents_config_and_register);
int __clockevents_update_freq(struct clock_event_device *dev, u32 freq)
{
clockevents_config(dev, freq);
if (clockevent_state_oneshot(dev))
return clockevents_program_event(dev, dev->next_event, false);
if (clockevent_state_periodic(dev))
return __clockevents_switch_state(dev, CLOCK_EVT_STATE_PERIODIC);
return 0;
}
/**
* clockevents_update_freq - Update frequency and reprogram a clock event device.
* @dev: device to modify
* @freq: new device frequency
*
* Reconfigure and reprogram a clock event device in oneshot
* mode. Must be called on the cpu for which the device delivers per
* cpu timer events. If called for the broadcast device the core takes
* care of serialization.
*
* Returns 0 on success, -ETIME when the event is in the past.
*/
int clockevents_update_freq(struct clock_event_device *dev, u32 freq)
{
unsigned long flags;
int ret;
local_irq_save(flags);
ret = tick_broadcast_update_freq(dev, freq);
if (ret == -ENODEV)
ret = __clockevents_update_freq(dev, freq);
local_irq_restore(flags);
return ret;
}
/*
* Noop handler when we shut down an event device
*/
void clockevents_handle_noop(struct clock_event_device *dev)
{
}
/**
* clockevents_exchange_device - release and request clock devices
* @old: device to release (can be NULL)
* @new: device to request (can be NULL)
*
* Called from various tick functions with clockevents_lock held and
* interrupts disabled.
*/
void clockevents_exchange_device(struct clock_event_device *old,
struct clock_event_device *new)
{
/*
* Caller releases a clock event device. We queue it into the
* released list and do a notify add later.
*/
if (old) {
module_put(old->owner);
clockevents_switch_state(old, CLOCK_EVT_STATE_DETACHED);
list_move(&old->list, &clockevents_released);
}
if (new) {
BUG_ON(!clockevent_state_detached(new));
clockevents_shutdown(new);
}
}
/**
* clockevents_suspend - suspend clock devices
*/
void clockevents_suspend(void)
{
struct clock_event_device *dev;
list_for_each_entry_reverse(dev, &clockevent_devices, list)
if (dev->suspend && !clockevent_state_detached(dev))
dev->suspend(dev);
}
/**
* clockevents_resume - resume clock devices
*/
void clockevents_resume(void)
{
struct clock_event_device *dev;
list_for_each_entry(dev, &clockevent_devices, list)
if (dev->resume && !clockevent_state_detached(dev))
dev->resume(dev);
}
#ifdef CONFIG_HOTPLUG_CPU
/**
* tick_offline_cpu - Shutdown all clock events related
* to this CPU and take it out of the
* broadcast mechanism.
* @cpu: The outgoing CPU
*
* Called by the dying CPU during teardown.
*/
void tick_offline_cpu(unsigned int cpu)
{
struct clock_event_device *dev, *tmp;
raw_spin_lock(&clockevents_lock);
tick_broadcast_offline(cpu);
tick_shutdown();
/*
* Unregister the clock event devices which were
* released above.
*/
list_for_each_entry_safe(dev, tmp, &clockevents_released, list)
list_del(&dev->list);
/*
* Now check whether the CPU has left unused per cpu devices
*/
list_for_each_entry_safe(dev, tmp, &clockevent_devices, list) {
if (cpumask_test_cpu(cpu, dev->cpumask) &&
cpumask_weight(dev->cpumask) == 1 &&
!tick_is_broadcast_device(dev)) {
BUG_ON(!clockevent_state_detached(dev));
list_del(&dev->list);
}
}
raw_spin_unlock(&clockevents_lock);
}
#endif
#ifdef CONFIG_SYSFS
static const struct bus_type clockevents_subsys = {
.name = "clockevents",
.dev_name = "clockevent",
};
static DEFINE_PER_CPU(struct device, tick_percpu_dev);
static struct tick_device *tick_get_tick_dev(struct device *dev);
static ssize_t current_device_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct tick_device *td;
ssize_t count = 0;
raw_spin_lock_irq(&clockevents_lock);
td = tick_get_tick_dev(dev);
if (td && td->evtdev)
count = sysfs_emit(buf, "%s\n", td->evtdev->name);
raw_spin_unlock_irq(&clockevents_lock);
return count;
}
static DEVICE_ATTR_RO(current_device);
/* We don't support the abomination of removable broadcast devices */
static ssize_t unbind_device_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
char name[CS_NAME_LEN];
ssize_t ret = sysfs_get_uname(buf, name, count);
struct clock_event_device *ce = NULL, *iter;
if (ret < 0)
return ret;
ret = -ENODEV;
mutex_lock(&clockevents_mutex);
raw_spin_lock_irq(&clockevents_lock);
list_for_each_entry(iter, &clockevent_devices, list) {
if (!strcmp(iter->name, name)) {
ret = __clockevents_try_unbind(iter, dev->id);
ce = iter;
break;
}
}
raw_spin_unlock_irq(&clockevents_lock);
/*
* We hold clockevents_mutex, so ce can't go away
*/
if (ret == -EAGAIN)
ret = clockevents_unbind(ce, dev->id);
mutex_unlock(&clockevents_mutex);
return ret ? ret : count;
}
static DEVICE_ATTR_WO(unbind_device);
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
static struct device tick_bc_dev = {
.init_name = "broadcast",
.id = 0,
.bus = &clockevents_subsys,
};
static struct tick_device *tick_get_tick_dev(struct device *dev)
{
return dev == &tick_bc_dev ? tick_get_broadcast_device() :
&per_cpu(tick_cpu_device, dev->id);
}
static __init int tick_broadcast_init_sysfs(void)
{
int err = device_register(&tick_bc_dev);
if (!err)
err = device_create_file(&tick_bc_dev, &dev_attr_current_device);
return err;
}
#else
static struct tick_device *tick_get_tick_dev(struct device *dev)
{
return &per_cpu(tick_cpu_device, dev->id);
}
static inline int tick_broadcast_init_sysfs(void) { return 0; }
#endif
static int __init tick_init_sysfs(void)
{
int cpu;
for_each_possible_cpu(cpu) {
struct device *dev = &per_cpu(tick_percpu_dev, cpu);
int err;
dev->id = cpu;
dev->bus = &clockevents_subsys;
err = device_register(dev);
if (!err)
err = device_create_file(dev, &dev_attr_current_device);
if (!err)
err = device_create_file(dev, &dev_attr_unbind_device);
if (err)
return err;
}
return tick_broadcast_init_sysfs();
}
static int __init clockevents_init_sysfs(void)
{
int err = subsys_system_register(&clockevents_subsys, NULL);
if (!err)
err = tick_init_sysfs();
return err;
}
device_initcall(clockevents_init_sysfs);
#endif /* SYSFS */
@@ -0,0 +1,198 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Unit test for the clocksource watchdog.
*
* Copyright (C) 2021 Facebook, Inc.
* Copyright (C) 2026 Intel Corp.
*
* Author: Paul E. McKenney <paulmck@kernel.org>
* Author: Thomas Gleixner <tglx@kernel.org>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/clocksource.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include "tick-internal.h"
#include "timekeeping_internal.h"
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Clocksource watchdog unit test");
MODULE_AUTHOR("Paul E. McKenney <paulmck@kernel.org>");
MODULE_AUTHOR("Thomas Gleixner <tglx@kernel.org>");
enum wdtest_states {
WDTEST_INJECT_NONE,
WDTEST_INJECT_DELAY,
WDTEST_INJECT_POSITIVE,
WDTEST_INJECT_NEGATIVE,
WDTEST_INJECT_PERCPU = 0x100,
};
static enum wdtest_states wdtest_state;
static unsigned long wdtest_test_count;
static ktime_t wdtest_last_ts, wdtest_offset;
#define SHIFT_4000PPM 8
static ktime_t wdtest_get_offset(struct clocksource *cs)
{
if (wdtest_state < WDTEST_INJECT_PERCPU)
return wdtest_test_count & 0x1 ? 0 : wdtest_offset >> SHIFT_4000PPM;
/* Only affect the readout of the "remote" CPU */
return cs->wd_cpu == smp_processor_id() ? 0 : NSEC_PER_MSEC;
}
static u64 wdtest_ktime_read(struct clocksource *cs)
{
ktime_t now = ktime_get_raw_fast_ns();
ktime_t intv = now - wdtest_last_ts;
/*
* Only increment the test counter once per watchdog interval and
* store the interval for the offset calculation of this step. This
* guarantees a consistent behaviour even if the other side needs
* to repeat due to a watchdog read timeout.
*/
if (intv > (NSEC_PER_SEC / 4)) {
WRITE_ONCE(wdtest_test_count, wdtest_test_count + 1);
wdtest_last_ts = now;
wdtest_offset = intv;
}
switch (wdtest_state & ~WDTEST_INJECT_PERCPU) {
case WDTEST_INJECT_POSITIVE:
return now + wdtest_get_offset(cs);
case WDTEST_INJECT_NEGATIVE:
return now - wdtest_get_offset(cs);
case WDTEST_INJECT_DELAY:
udelay(500);
return now;
default:
return now;
}
}
#define KTIME_FLAGS (CLOCK_SOURCE_IS_CONTINUOUS | \
CLOCK_SOURCE_CALIBRATED | \
CLOCK_SOURCE_MUST_VERIFY | \
CLOCK_SOURCE_WDTEST)
static struct clocksource clocksource_wdtest_ktime = {
.name = "wdtest-ktime",
.rating = 10,
.read = wdtest_ktime_read,
.mask = CLOCKSOURCE_MASK(64),
.flags = KTIME_FLAGS,
.list = LIST_HEAD_INIT(clocksource_wdtest_ktime.list),
};
static void wdtest_clocksource_reset(enum wdtest_states which, bool percpu)
{
clocksource_unregister(&clocksource_wdtest_ktime);
pr_info("Test: State %d percpu %d\n", which, percpu);
wdtest_state = which;
if (percpu)
wdtest_state |= WDTEST_INJECT_PERCPU;
wdtest_test_count = 0;
wdtest_last_ts = 0;
clocksource_wdtest_ktime.rating = 10;
clocksource_wdtest_ktime.flags = KTIME_FLAGS;
if (percpu)
clocksource_wdtest_ktime.flags |= CLOCK_SOURCE_WDTEST_PERCPU;
clocksource_register_khz(&clocksource_wdtest_ktime, 1000 * 1000);
}
static bool wdtest_execute(enum wdtest_states which, bool percpu, unsigned int expect,
unsigned long calls)
{
wdtest_clocksource_reset(which, percpu);
for (; READ_ONCE(wdtest_test_count) < calls; msleep(100)) {
unsigned int flags = READ_ONCE(clocksource_wdtest_ktime.flags);
if (kthread_should_stop())
return false;
if (flags & CLOCK_SOURCE_UNSTABLE) {
if (expect & CLOCK_SOURCE_UNSTABLE)
return true;
pr_warn("Fail: Unexpected unstable\n");
return false;
}
if (flags & CLOCK_SOURCE_VALID_FOR_HRES) {
if (expect & CLOCK_SOURCE_VALID_FOR_HRES)
return true;
pr_warn("Fail: Unexpected valid for highres\n");
return false;
}
}
if (!expect)
return true;
pr_warn("Fail: Timed out\n");
return false;
}
static bool wdtest_run(bool percpu)
{
if (!wdtest_execute(WDTEST_INJECT_NONE, percpu, CLOCK_SOURCE_VALID_FOR_HRES, 8))
return false;
if (!wdtest_execute(WDTEST_INJECT_DELAY, percpu, 0, 4))
return false;
if (!wdtest_execute(WDTEST_INJECT_POSITIVE, percpu, CLOCK_SOURCE_UNSTABLE, 8))
return false;
if (!wdtest_execute(WDTEST_INJECT_NEGATIVE, percpu, CLOCK_SOURCE_UNSTABLE, 8))
return false;
return true;
}
static int wdtest_func(void *arg)
{
clocksource_register_khz(&clocksource_wdtest_ktime, 1000 * 1000);
if (wdtest_run(false)) {
if (wdtest_run(true))
pr_info("Success: All tests passed\n");
}
clocksource_unregister(&clocksource_wdtest_ktime);
if (!IS_MODULE(CONFIG_TEST_CLOCKSOURCE_WATCHDOG))
return 0;
while (!kthread_should_stop())
schedule_timeout_interruptible(3600 * HZ);
return 0;
}
static struct task_struct *wdtest_thread;
static int __init clocksource_wdtest_init(void)
{
struct task_struct *t = kthread_run(wdtest_func, NULL, "wdtest");
if (IS_ERR(t)) {
pr_warn("Failed to create wdtest kthread.\n");
return PTR_ERR(t);
}
wdtest_thread = t;
return 0;
}
module_init(clocksource_wdtest_init);
static void clocksource_wdtest_cleanup(void)
{
if (wdtest_thread)
kthread_stop(wdtest_thread);
}
module_exit(clocksource_wdtest_cleanup);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+422
View File
@@ -0,0 +1,422 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 1992 Darren Senn
*/
/* These are all the functions necessary to implement itimers */
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/syscalls.h>
#include <linux/time.h>
#include <linux/sched/signal.h>
#include <linux/sched/cputime.h>
#include <linux/posix-timers.h>
#include <linux/hrtimer.h>
#include <trace/events/timer.h>
#include <linux/compat.h>
#include <linux/uaccess.h>
/**
* itimer_get_remtime - get remaining time for the timer
*
* @timer: the timer to read
*
* Returns the delta between the expiry time and now, which can be
* less than zero or 1usec for an pending expired timer
*/
static struct timespec64 itimer_get_remtime(struct hrtimer *timer)
{
ktime_t rem = __hrtimer_get_remaining(timer, true);
/*
* Racy but safe: if the itimer expires after the above
* hrtimer_get_remtime() call but before this condition
* then we return 0 - which is correct.
*/
if (hrtimer_active(timer)) {
if (rem <= 0)
rem = NSEC_PER_USEC;
} else
rem = 0;
return ktime_to_timespec64(rem);
}
static void get_cpu_itimer(struct task_struct *tsk, unsigned int clock_id,
struct itimerspec64 *const value)
{
u64 val, interval;
struct cpu_itimer *it = &tsk->signal->it[clock_id];
spin_lock_irq(&tsk->sighand->siglock);
val = it->expires;
interval = it->incr;
if (val) {
u64 t, samples[CPUCLOCK_MAX];
thread_group_sample_cputime(tsk, samples);
t = samples[clock_id];
if (val < t)
/* about to fire */
val = TICK_NSEC;
else
val -= t;
}
spin_unlock_irq(&tsk->sighand->siglock);
value->it_value = ns_to_timespec64(val);
value->it_interval = ns_to_timespec64(interval);
}
static int do_getitimer(int which, struct itimerspec64 *value)
{
struct task_struct *tsk = current;
switch (which) {
case ITIMER_REAL:
spin_lock_irq(&tsk->sighand->siglock);
value->it_value = itimer_get_remtime(&tsk->signal->real_timer);
value->it_interval =
ktime_to_timespec64(tsk->signal->it_real_incr);
spin_unlock_irq(&tsk->sighand->siglock);
break;
case ITIMER_VIRTUAL:
get_cpu_itimer(tsk, CPUCLOCK_VIRT, value);
break;
case ITIMER_PROF:
get_cpu_itimer(tsk, CPUCLOCK_PROF, value);
break;
default:
return(-EINVAL);
}
return 0;
}
static int put_itimerval(struct __kernel_old_itimerval __user *o,
const struct itimerspec64 *i)
{
struct __kernel_old_itimerval v;
v.it_interval.tv_sec = i->it_interval.tv_sec;
v.it_interval.tv_usec = i->it_interval.tv_nsec / NSEC_PER_USEC;
v.it_value.tv_sec = i->it_value.tv_sec;
v.it_value.tv_usec = i->it_value.tv_nsec / NSEC_PER_USEC;
return copy_to_user(o, &v, sizeof(struct __kernel_old_itimerval)) ? -EFAULT : 0;
}
SYSCALL_DEFINE2(getitimer, int, which, struct __kernel_old_itimerval __user *, value)
{
struct itimerspec64 get_buffer;
int error = do_getitimer(which, &get_buffer);
if (!error && put_itimerval(value, &get_buffer))
error = -EFAULT;
return error;
}
#if defined(CONFIG_COMPAT) || defined(CONFIG_ALPHA)
struct old_itimerval32 {
struct old_timeval32 it_interval;
struct old_timeval32 it_value;
};
static int put_old_itimerval32(struct old_itimerval32 __user *o,
const struct itimerspec64 *i)
{
struct old_itimerval32 v32;
v32.it_interval.tv_sec = i->it_interval.tv_sec;
v32.it_interval.tv_usec = i->it_interval.tv_nsec / NSEC_PER_USEC;
v32.it_value.tv_sec = i->it_value.tv_sec;
v32.it_value.tv_usec = i->it_value.tv_nsec / NSEC_PER_USEC;
return copy_to_user(o, &v32, sizeof(struct old_itimerval32)) ? -EFAULT : 0;
}
COMPAT_SYSCALL_DEFINE2(getitimer, int, which,
struct old_itimerval32 __user *, value)
{
struct itimerspec64 get_buffer;
int error = do_getitimer(which, &get_buffer);
if (!error && put_old_itimerval32(value, &get_buffer))
error = -EFAULT;
return error;
}
#endif
/*
* Invoked from dequeue_signal() when SIG_ALRM is delivered.
*
* Restart the ITIMER_REAL timer if it is armed as periodic timer. Doing
* this in the signal delivery path instead of self rearming prevents a DoS
* with small increments in the high reolution timer case and reduces timer
* noise in general.
*/
void posixtimer_rearm_itimer(struct task_struct *tsk)
{
struct hrtimer *tmr = &tsk->signal->real_timer;
if (!hrtimer_is_queued(tmr) && tsk->signal->it_real_incr != 0) {
hrtimer_forward_now(tmr, tsk->signal->it_real_incr);
hrtimer_restart(tmr);
}
}
/*
* Interval timers are restarted in the signal delivery path. See
* posixtimer_rearm_itimer().
*/
enum hrtimer_restart it_real_fn(struct hrtimer *timer)
{
struct signal_struct *sig =
container_of(timer, struct signal_struct, real_timer);
struct pid *leader_pid = sig->pids[PIDTYPE_TGID];
trace_itimer_expire(ITIMER_REAL, leader_pid, 0);
kill_pid_info(SIGALRM, SEND_SIG_PRIV, leader_pid);
return HRTIMER_NORESTART;
}
static void set_cpu_itimer(struct task_struct *tsk, unsigned int clock_id,
const struct itimerspec64 *const value,
struct itimerspec64 *const ovalue)
{
u64 oval, nval, ointerval, ninterval;
struct cpu_itimer *it = &tsk->signal->it[clock_id];
nval = timespec64_to_ns(&value->it_value);
ninterval = timespec64_to_ns(&value->it_interval);
spin_lock_irq(&tsk->sighand->siglock);
oval = it->expires;
ointerval = it->incr;
if (oval || nval) {
if (nval > 0)
nval += TICK_NSEC;
set_process_cpu_timer(tsk, clock_id, &nval, &oval);
}
it->expires = nval;
it->incr = ninterval;
trace_itimer_state(clock_id == CPUCLOCK_VIRT ?
ITIMER_VIRTUAL : ITIMER_PROF, value, nval);
spin_unlock_irq(&tsk->sighand->siglock);
if (ovalue) {
ovalue->it_value = ns_to_timespec64(oval);
ovalue->it_interval = ns_to_timespec64(ointerval);
}
}
/*
* Returns true if the timeval is in canonical form
*/
#define timeval_valid(t) \
(((t)->tv_sec >= 0) && (((unsigned long) (t)->tv_usec) < USEC_PER_SEC))
static int do_setitimer(int which, struct itimerspec64 *value,
struct itimerspec64 *ovalue)
{
struct task_struct *tsk = current;
struct hrtimer *timer;
ktime_t expires;
switch (which) {
case ITIMER_REAL:
again:
spin_lock_irq(&tsk->sighand->siglock);
timer = &tsk->signal->real_timer;
if (ovalue) {
ovalue->it_value = itimer_get_remtime(timer);
ovalue->it_interval
= ktime_to_timespec64(tsk->signal->it_real_incr);
}
/* We are sharing ->siglock with it_real_fn() */
if (hrtimer_try_to_cancel(timer) < 0) {
spin_unlock_irq(&tsk->sighand->siglock);
hrtimer_cancel_wait_running(timer);
goto again;
}
expires = timespec64_to_ktime(value->it_value);
if (expires != 0) {
tsk->signal->it_real_incr =
timespec64_to_ktime(value->it_interval);
hrtimer_start(timer, expires, HRTIMER_MODE_REL);
} else
tsk->signal->it_real_incr = 0;
trace_itimer_state(ITIMER_REAL, value, 0);
spin_unlock_irq(&tsk->sighand->siglock);
break;
case ITIMER_VIRTUAL:
set_cpu_itimer(tsk, CPUCLOCK_VIRT, value, ovalue);
break;
case ITIMER_PROF:
set_cpu_itimer(tsk, CPUCLOCK_PROF, value, ovalue);
break;
default:
return -EINVAL;
}
return 0;
}
#ifdef CONFIG_SECURITY_SELINUX
void clear_itimer(void)
{
struct itimerspec64 v = {};
int i;
for (i = 0; i < 3; i++)
do_setitimer(i, &v, NULL);
}
#endif
#ifdef __ARCH_WANT_SYS_ALARM
/**
* alarm_setitimer - set alarm in seconds
*
* @seconds: number of seconds until alarm
* 0 disables the alarm
*
* Returns the remaining time in seconds of a pending timer or 0 when
* the timer is not active.
*
* On 32 bit machines the seconds value is limited to (INT_MAX/2) to avoid
* negative timeval settings which would cause immediate expiry.
*/
static unsigned int alarm_setitimer(unsigned int seconds)
{
struct itimerspec64 it_new, it_old;
#if BITS_PER_LONG < 64
if (seconds > INT_MAX)
seconds = INT_MAX;
#endif
it_new.it_value.tv_sec = seconds;
it_new.it_value.tv_nsec = 0;
it_new.it_interval.tv_sec = it_new.it_interval.tv_nsec = 0;
do_setitimer(ITIMER_REAL, &it_new, &it_old);
/*
* We can't return 0 if we have an alarm pending ... And we'd
* better return too much than too little anyway
*/
if ((!it_old.it_value.tv_sec && it_old.it_value.tv_nsec) ||
it_old.it_value.tv_nsec >= (NSEC_PER_SEC / 2))
it_old.it_value.tv_sec++;
return it_old.it_value.tv_sec;
}
/*
* For backwards compatibility? This can be done in libc so Alpha
* and all newer ports shouldn't need it.
*/
SYSCALL_DEFINE1(alarm, unsigned int, seconds)
{
return alarm_setitimer(seconds);
}
#endif
static int get_itimerval(struct itimerspec64 *o, const struct __kernel_old_itimerval __user *i)
{
struct __kernel_old_itimerval v;
if (copy_from_user(&v, i, sizeof(struct __kernel_old_itimerval)))
return -EFAULT;
/* Validate the timevals in value. */
if (!timeval_valid(&v.it_value) ||
!timeval_valid(&v.it_interval))
return -EINVAL;
o->it_interval.tv_sec = v.it_interval.tv_sec;
o->it_interval.tv_nsec = v.it_interval.tv_usec * NSEC_PER_USEC;
o->it_value.tv_sec = v.it_value.tv_sec;
o->it_value.tv_nsec = v.it_value.tv_usec * NSEC_PER_USEC;
return 0;
}
SYSCALL_DEFINE3(setitimer, int, which, struct __kernel_old_itimerval __user *, value,
struct __kernel_old_itimerval __user *, ovalue)
{
struct itimerspec64 set_buffer, get_buffer;
int error;
if (value) {
error = get_itimerval(&set_buffer, value);
if (error)
return error;
} else {
memset(&set_buffer, 0, sizeof(set_buffer));
printk_once(KERN_WARNING "%s calls setitimer() with new_value NULL pointer."
" Misfeature support will be removed\n",
current->comm);
}
error = do_setitimer(which, &set_buffer, ovalue ? &get_buffer : NULL);
if (error || !ovalue)
return error;
if (put_itimerval(ovalue, &get_buffer))
return -EFAULT;
return 0;
}
#if defined(CONFIG_COMPAT) || defined(CONFIG_ALPHA)
static int get_old_itimerval32(struct itimerspec64 *o, const struct old_itimerval32 __user *i)
{
struct old_itimerval32 v32;
if (copy_from_user(&v32, i, sizeof(struct old_itimerval32)))
return -EFAULT;
/* Validate the timevals in value. */
if (!timeval_valid(&v32.it_value) ||
!timeval_valid(&v32.it_interval))
return -EINVAL;
o->it_interval.tv_sec = v32.it_interval.tv_sec;
o->it_interval.tv_nsec = v32.it_interval.tv_usec * NSEC_PER_USEC;
o->it_value.tv_sec = v32.it_value.tv_sec;
o->it_value.tv_nsec = v32.it_value.tv_usec * NSEC_PER_USEC;
return 0;
}
COMPAT_SYSCALL_DEFINE3(setitimer, int, which,
struct old_itimerval32 __user *, value,
struct old_itimerval32 __user *, ovalue)
{
struct itimerspec64 set_buffer, get_buffer;
int error;
if (value) {
error = get_old_itimerval32(&set_buffer, value);
if (error)
return error;
} else {
memset(&set_buffer, 0, sizeof(set_buffer));
printk_once(KERN_WARNING "%s calls setitimer() with new_value NULL pointer."
" Misfeature support will be removed\n",
current->comm);
}
error = do_setitimer(which, &set_buffer, ovalue ? &get_buffer : NULL);
if (error || !ovalue)
return error;
if (put_old_itimerval32(ovalue, &get_buffer))
return -EFAULT;
return 0;
}
#endif
+316
View File
@@ -0,0 +1,316 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* This file contains the jiffies based clocksource.
*
* Copyright (C) 2004, 2005 IBM, John Stultz (johnstul@us.ibm.com)
*/
#include <linux/clocksource.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/init.h>
#include "timekeeping.h"
#include "tick-internal.h"
static u64 jiffies_read(struct clocksource *cs)
{
return (u64) jiffies;
}
/*
* The Jiffies based clocksource is the lowest common
* denominator clock source which should function on
* all systems. It has the same coarse resolution as
* the timer interrupt frequency HZ and it suffers
* inaccuracies caused by missed or lost timer
* interrupts and the inability for the timer
* interrupt hardware to accurately tick at the
* requested HZ value. It is also not recommended
* for "tick-less" systems.
*/
static struct clocksource clocksource_jiffies = {
.name = "jiffies",
.rating = 1, /* lowest valid rating*/
.read = jiffies_read,
.mask = CLOCKSOURCE_MASK(32),
.mult = TICK_NSEC << JIFFIES_SHIFT, /* details above */
.shift = JIFFIES_SHIFT,
.max_cycles = 10,
};
__cacheline_aligned_in_smp DEFINE_RAW_SPINLOCK(jiffies_lock);
__cacheline_aligned_in_smp seqcount_raw_spinlock_t jiffies_seq =
SEQCNT_RAW_SPINLOCK_ZERO(jiffies_seq, &jiffies_lock);
#if (BITS_PER_LONG < 64)
u64 get_jiffies_64(void)
{
unsigned int seq;
u64 ret;
do {
seq = read_seqcount_begin(&jiffies_seq);
ret = jiffies_64;
} while (read_seqcount_retry(&jiffies_seq, seq));
return ret;
}
EXPORT_SYMBOL(get_jiffies_64);
#endif
EXPORT_SYMBOL(jiffies);
static bool cs_jiffies_registered __initdata;
struct clocksource * __init __weak clocksource_default_clock(void)
{
if (!cs_jiffies_registered) {
__clocksource_register(&clocksource_jiffies);
cs_jiffies_registered = true;
}
return &clocksource_jiffies;
}
static struct clocksource refined_jiffies;
void __init register_refined_jiffies(long cycles_per_second)
{
u64 nsec_per_tick, shift_hz;
long cycles_per_tick;
refined_jiffies = clocksource_jiffies;
refined_jiffies.name = "refined-jiffies";
refined_jiffies.rating++;
/* Calc cycles per tick */
cycles_per_tick = (cycles_per_second + HZ/2)/HZ;
/* shift_hz stores hz<<8 for extra accuracy */
shift_hz = (u64)cycles_per_second << 8;
shift_hz += cycles_per_tick/2;
do_div(shift_hz, cycles_per_tick);
/* Calculate nsec_per_tick using shift_hz */
nsec_per_tick = (u64)NSEC_PER_SEC << 8;
nsec_per_tick += (u32)shift_hz/2;
do_div(nsec_per_tick, (u32)shift_hz);
refined_jiffies.mult = ((u32)nsec_per_tick) << JIFFIES_SHIFT;
__clocksource_register(&refined_jiffies);
}
#ifdef CONFIG_PROC_SYSCTL
static ulong mult_hz(const ulong val)
{
return val * HZ;
}
static ulong div_hz(const ulong val)
{
return val / HZ;
}
static int sysctl_u2k_int_conv_hz(const bool *negp, const ulong *u_ptr, int *k_ptr)
{
return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, mult_hz);
}
static int sysctl_k2u_int_conv_hz(bool *negp, ulong *u_ptr, const int *k_ptr)
{
return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, div_hz);
}
static int sysctl_u2k_int_conv_userhz(const bool *negp, const ulong *u_ptr, int *k_ptr)
{
return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, clock_t_to_jiffies);
}
static ulong sysctl_jiffies_to_clock_t(const ulong val)
{
return jiffies_to_clock_t(val);
}
static int sysctl_k2u_int_conv_userhz(bool *negp, ulong *u_ptr, const int *k_ptr)
{
return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, sysctl_jiffies_to_clock_t);
}
static ulong sysctl_msecs_to_jiffies(const ulong val)
{
return msecs_to_jiffies(val);
}
static int sysctl_u2k_int_conv_ms(const bool *negp, const ulong *u_ptr, int *k_ptr)
{
return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, sysctl_msecs_to_jiffies);
}
static ulong sysctl_jiffies_to_msecs(const ulong val)
{
return jiffies_to_msecs(val);
}
static int sysctl_k2u_int_conv_ms(bool *negp, ulong *u_ptr, const int *k_ptr)
{
return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, sysctl_jiffies_to_msecs);
}
static int do_proc_int_conv_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
int dir, const struct ctl_table *tbl)
{
return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
sysctl_u2k_int_conv_hz, sysctl_k2u_int_conv_hz);
}
static int do_proc_int_conv_userhz_jiffies(bool *negp, ulong *u_ptr,
int *k_ptr, int dir,
const struct ctl_table *tbl)
{
return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
sysctl_u2k_int_conv_userhz,
sysctl_k2u_int_conv_userhz);
}
static int do_proc_int_conv_ms_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
int dir, const struct ctl_table *tbl)
{
return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
sysctl_u2k_int_conv_ms, sysctl_k2u_int_conv_ms);
}
static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr,
int *k_ptr, int dir,
const struct ctl_table *tbl)
{
return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
sysctl_u2k_int_conv_ms, sysctl_k2u_int_conv_ms);
}
#else // CONFIG_PROC_SYSCTL
static int do_proc_int_conv_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
int dir, const struct ctl_table *tbl)
{
return -ENOSYS;
}
static int do_proc_int_conv_userhz_jiffies(bool *negp, ulong *u_ptr,
int *k_ptr, int dir,
const struct ctl_table *tbl)
{
return -ENOSYS;
}
static int do_proc_int_conv_ms_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
int dir, const struct ctl_table *tbl)
{
return -ENOSYS;
}
static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr,
int *k_ptr, int dir,
const struct ctl_table *tbl)
{
return -ENOSYS;
}
#endif
/**
* proc_dointvec_jiffies - read a vector of integers as seconds
* @table: the sysctl table
* @dir: %TRUE if this is a write to the sysctl file
* @buffer: the user buffer
* @lenp: the size of the user buffer
* @ppos: file position
*
* Reads/writes up to table->maxlen/sizeof(unsigned int) integer
* values from/to the user buffer, treated as an ASCII string.
* The values read are assumed to be in seconds, and are converted into
* jiffies.
*
* Returns 0 on success.
*/
int proc_dointvec_jiffies(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return proc_dointvec_conv(table, dir, buffer, lenp, ppos,
do_proc_int_conv_jiffies);
}
EXPORT_SYMBOL(proc_dointvec_jiffies);
/**
* proc_dointvec_userhz_jiffies - read a vector of integers as 1/USER_HZ seconds
* @table: the sysctl table
* @dir: %TRUE if this is a write to the sysctl file
* @buffer: the user buffer
* @lenp: the size of the user buffer
* @ppos: pointer to the file position
*
* Reads/writes up to table->maxlen/sizeof(unsigned int) integer
* values from/to the user buffer, treated as an ASCII string.
* The values read are assumed to be in 1/USER_HZ seconds, and
* are converted into jiffies.
*
* Returns 0 on success.
*/
int proc_dointvec_userhz_jiffies(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return proc_dointvec_conv(table, dir, buffer, lenp, ppos,
do_proc_int_conv_userhz_jiffies);
}
EXPORT_SYMBOL(proc_dointvec_userhz_jiffies);
/**
* proc_dointvec_ms_jiffies - read a vector of integers as 1 milliseconds
* @table: the sysctl table
* @dir: %TRUE if this is a write to the sysctl file
* @buffer: the user buffer
* @lenp: the size of the user buffer
* @ppos: the current position in the file
*
* Reads/writes up to table->maxlen/sizeof(unsigned int) integer
* values from/to the user buffer, treated as an ASCII string.
* The values read are assumed to be in 1/1000 seconds, and
* are converted into jiffies.
*
* Returns 0 on success.
*/
int proc_dointvec_ms_jiffies(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos)
{
return proc_dointvec_conv(table, dir, buffer, lenp, ppos,
do_proc_int_conv_ms_jiffies);
}
EXPORT_SYMBOL(proc_dointvec_ms_jiffies);
int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return proc_dointvec_conv(table, dir, buffer, lenp, ppos,
do_proc_int_conv_ms_jiffies_minmax);
}
/**
* proc_doulongvec_ms_jiffies_minmax - read a vector of millisecond values with min/max values
* @table: the sysctl table
* @dir: %TRUE if this is a write to the sysctl file
* @buffer: the user buffer
* @lenp: the size of the user buffer
* @ppos: file position
*
* Reads/writes up to table->maxlen/sizeof(unsigned long) unsigned long
* values from/to the user buffer, treated as an ASCII string. The values
* are treated as milliseconds, and converted to jiffies when they are stored.
*
* This routine will ensure the values are within the range specified by
* table->extra1 (min) and table->extra2 (max).
*
* Returns 0 on success.
*/
int proc_doulongvec_ms_jiffies_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
return proc_doulongvec_minmax_conv(table, dir, buffer, lenp, ppos,
HZ, 1000l);
}
EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax);
+360
View File
@@ -0,0 +1,360 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Author: Andrei Vagin <avagin@openvz.org>
* Author: Dmitry Safonov <dima@arista.com>
*/
#include <linux/time_namespace.h>
#include <linux/user_namespace.h>
#include <linux/sched/signal.h>
#include <linux/sched/task.h>
#include <linux/clocksource.h>
#include <linux/seq_file.h>
#include <linux/proc_ns.h>
#include <linux/export.h>
#include <linux/nstree.h>
#include <linux/time.h>
#include <linux/slab.h>
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/mm.h>
#include <linux/cleanup.h>
#include "namespace_internal.h"
ktime_t do_timens_ktime_to_host(clockid_t clockid, ktime_t tim,
struct timens_offsets *ns_offsets)
{
ktime_t offset;
switch (clockid) {
case CLOCK_MONOTONIC:
offset = timespec64_to_ktime(ns_offsets->monotonic);
break;
case CLOCK_BOOTTIME:
case CLOCK_BOOTTIME_ALARM:
offset = timespec64_to_ktime(ns_offsets->boottime);
break;
default:
return tim;
}
/*
* Check that @tim value is in [offset, KTIME_MAX + offset]
* and subtract offset.
*/
if (tim < offset) {
/*
* User can specify @tim *absolute* value - if it's lesser than
* the time namespace's offset - it's already expired.
*/
tim = 0;
} else {
tim = ktime_sub(tim, offset);
if (unlikely(tim > KTIME_MAX))
tim = KTIME_MAX;
}
return tim;
}
EXPORT_SYMBOL_GPL(do_timens_ktime_to_host);
static struct ucounts *inc_time_namespaces(struct user_namespace *ns)
{
return inc_ucount(ns, current_euid(), UCOUNT_TIME_NAMESPACES);
}
static void dec_time_namespaces(struct ucounts *ucounts)
{
dec_ucount(ucounts, UCOUNT_TIME_NAMESPACES);
}
/**
* clone_time_ns - Clone a time namespace
* @user_ns: User namespace which owns a new namespace.
* @old_ns: Namespace to clone
*
* Clone @old_ns and set the clone refcount to 1
*
* Return: The new namespace or ERR_PTR.
*/
static struct time_namespace *clone_time_ns(struct user_namespace *user_ns,
struct time_namespace *old_ns)
{
struct time_namespace *ns;
struct ucounts *ucounts;
int err;
err = -ENOSPC;
ucounts = inc_time_namespaces(user_ns);
if (!ucounts)
goto fail;
err = -ENOMEM;
ns = kzalloc_obj(*ns, GFP_KERNEL_ACCOUNT);
if (!ns)
goto fail_dec;
err = timens_vdso_alloc_vvar_page(ns);
if (err)
goto fail_free;
err = ns_common_init(ns);
if (err)
goto fail_free_page;
ns->ucounts = ucounts;
ns->user_ns = get_user_ns(user_ns);
ns->offsets = old_ns->offsets;
ns->frozen_offsets = false;
ns_tree_add(ns);
return ns;
fail_free_page:
timens_vdso_free_vvar_page(ns);
fail_free:
kfree(ns);
fail_dec:
dec_time_namespaces(ucounts);
fail:
return ERR_PTR(err);
}
/**
* copy_time_ns - Create timens_for_children from @old_ns
* @flags: Cloning flags
* @user_ns: User namespace which owns a new namespace.
* @old_ns: Namespace to clone
*
* If CLONE_NEWTIME specified in @flags, creates a new timens_for_children;
* adds a refcounter to @old_ns otherwise.
*
* Return: timens_for_children namespace or ERR_PTR.
*/
struct time_namespace *copy_time_ns(u64 flags,
struct user_namespace *user_ns, struct time_namespace *old_ns)
{
if (!(flags & CLONE_NEWTIME))
return get_time_ns(old_ns);
return clone_time_ns(user_ns, old_ns);
}
DEFINE_MUTEX(timens_offset_lock);
void free_time_ns(struct time_namespace *ns)
{
ns_tree_remove(ns);
dec_time_namespaces(ns->ucounts);
put_user_ns(ns->user_ns);
ns_common_free(ns);
timens_vdso_free_vvar_page(ns);
/* Concurrent nstree traversal depends on a grace period. */
kfree_rcu(ns, ns.ns_rcu);
}
static struct ns_common *timens_get(struct task_struct *task)
{
struct time_namespace *ns;
struct nsproxy *nsproxy;
guard(task_lock)(task);
nsproxy = task->nsproxy;
if (!nsproxy)
return NULL;
ns = nsproxy->time_ns;
get_time_ns(ns);
return &ns->ns;
}
static struct ns_common *timens_for_children_get(struct task_struct *task)
{
struct time_namespace *ns;
struct nsproxy *nsproxy;
guard(task_lock)(task);
nsproxy = task->nsproxy;
if (!nsproxy)
return NULL;
ns = nsproxy->time_ns_for_children;
get_time_ns(ns);
return &ns->ns;
}
static void timens_put(struct ns_common *ns)
{
put_time_ns(to_time_ns(ns));
}
static int timens_install(struct nsset *nsset, struct ns_common *new)
{
struct nsproxy *nsproxy = nsset->nsproxy;
struct time_namespace *ns = to_time_ns(new);
if (!current_is_single_threaded())
return -EUSERS;
if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
!ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN))
return -EPERM;
get_time_ns(ns);
put_time_ns(nsproxy->time_ns);
nsproxy->time_ns = ns;
get_time_ns(ns);
put_time_ns(nsproxy->time_ns_for_children);
nsproxy->time_ns_for_children = ns;
return 0;
}
void timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk)
{
struct ns_common *nsc = &nsproxy->time_ns_for_children->ns;
struct time_namespace *ns = to_time_ns(nsc);
/* create_new_namespaces() already incremented the ref counter */
if (nsproxy->time_ns == nsproxy->time_ns_for_children)
return;
get_time_ns(ns);
put_time_ns(nsproxy->time_ns);
nsproxy->time_ns = ns;
timens_commit(tsk, ns);
}
static struct user_namespace *timens_owner(struct ns_common *ns)
{
return to_time_ns(ns)->user_ns;
}
static void show_offset(struct seq_file *m, int clockid, struct timespec64 *ts)
{
char *clock;
switch (clockid) {
case CLOCK_BOOTTIME:
clock = "boottime";
break;
case CLOCK_MONOTONIC:
clock = "monotonic";
break;
default:
clock = "unknown";
break;
}
seq_printf(m, "%-10s %10lld %9ld\n", clock, ts->tv_sec, ts->tv_nsec);
}
void proc_timens_show_offsets(struct task_struct *p, struct seq_file *m)
{
struct time_namespace *time_ns __free(time_ns) = NULL;
struct ns_common *ns = timens_for_children_get(p);
if (!ns)
return;
time_ns = to_time_ns(ns);
show_offset(m, CLOCK_MONOTONIC, &time_ns->offsets.monotonic);
show_offset(m, CLOCK_BOOTTIME, &time_ns->offsets.boottime);
}
int proc_timens_set_offset(struct file *file, struct task_struct *p,
struct proc_timens_offset *offsets, int noffsets)
{
struct time_namespace *time_ns __free(time_ns) = NULL;
struct ns_common *ns = timens_for_children_get(p);
struct timespec64 tp;
int i;
if (!ns)
return -ESRCH;
time_ns = to_time_ns(ns);
if (!file_ns_capable(file, time_ns->user_ns, CAP_SYS_TIME))
return -EPERM;
for (i = 0; i < noffsets; i++) {
struct proc_timens_offset *off = &offsets[i];
switch (off->clockid) {
case CLOCK_MONOTONIC:
ktime_get_ts64(&tp);
break;
case CLOCK_BOOTTIME:
ktime_get_boottime_ts64(&tp);
break;
default:
return -EINVAL;
}
if (off->val.tv_sec > KTIME_SEC_MAX ||
off->val.tv_sec < -KTIME_SEC_MAX)
return -ERANGE;
tp = timespec64_add(tp, off->val);
/*
* KTIME_SEC_MAX is divided by 2 to be sure that KTIME_MAX is
* still unreachable.
*/
if (tp.tv_sec < 0 || tp.tv_sec > KTIME_SEC_MAX / 2)
return -ERANGE;
}
guard(mutex)(&timens_offset_lock);
if (time_ns->frozen_offsets)
return -EACCES;
/* Don't report errors after this line */
for (i = 0; i < noffsets; i++) {
struct proc_timens_offset *off = &offsets[i];
struct timespec64 *offset = NULL;
switch (off->clockid) {
case CLOCK_MONOTONIC:
offset = &time_ns->offsets.monotonic;
break;
case CLOCK_BOOTTIME:
offset = &time_ns->offsets.boottime;
break;
}
*offset = off->val;
}
return 0;
}
const struct proc_ns_operations timens_operations = {
.name = "time",
.get = timens_get,
.put = timens_put,
.install = timens_install,
.owner = timens_owner,
};
const struct proc_ns_operations timens_for_children_operations = {
.name = "time_for_children",
.real_ns_name = "time",
.get = timens_for_children_get,
.put = timens_put,
.install = timens_install,
.owner = timens_owner,
};
struct time_namespace init_time_ns = {
.ns = NS_COMMON_INIT(init_time_ns),
.user_ns = &init_user_ns,
.frozen_offsets = true,
};
EXPORT_SYMBOL_GPL(init_time_ns);
void __init time_ns_init(void)
{
ns_tree_add(&init_time_ns);
}
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _TIME_NAMESPACE_INTERNAL_H
#define _TIME_NAMESPACE_INTERNAL_H
#include <linux/mutex.h>
struct time_namespace;
/*
* Protects possibly multiple offsets writers racing each other
* and tasks entering the namespace.
*/
extern struct mutex timens_offset_lock;
#ifdef CONFIG_TIME_NS_VDSO
int timens_vdso_alloc_vvar_page(struct time_namespace *ns);
void timens_vdso_free_vvar_page(struct time_namespace *ns);
#else /* !CONFIG_TIME_NS_VDSO */
static inline int timens_vdso_alloc_vvar_page(struct time_namespace *ns)
{
return 0;
}
static inline void timens_vdso_free_vvar_page(struct time_namespace *ns)
{
}
#endif /* CONFIG_TIME_NS_VDSO */
#endif /* _TIME_NAMESPACE_INTERNAL_H */
+160
View File
@@ -0,0 +1,160 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Author: Andrei Vagin <avagin@openvz.org>
* Author: Dmitry Safonov <dima@arista.com>
*/
#include <linux/cleanup.h>
#include <linux/mm.h>
#include <linux/time_namespace.h>
#include <linux/time.h>
#include <linux/vdso_datastore.h>
#include <vdso/clocksource.h>
#include <vdso/datapage.h>
#include "namespace_internal.h"
static struct timens_offset offset_from_ts(struct timespec64 off)
{
struct timens_offset ret;
ret.sec = off.tv_sec;
ret.nsec = off.tv_nsec;
return ret;
}
/*
* A time namespace VVAR page has the same layout as the VVAR page which
* contains the system wide VDSO data.
*
* For a normal task the VVAR pages are installed in the normal ordering:
* VVAR
* PVCLOCK
* HVCLOCK
* TIMENS <- Not really required
*
* Now for a timens task the pages are installed in the following order:
* TIMENS
* PVCLOCK
* HVCLOCK
* VVAR
*
* The check for vdso_clock->clock_mode is in the unlikely path of
* the seq begin magic. So for the non-timens case most of the time
* 'seq' is even, so the branch is not taken.
*
* If 'seq' is odd, i.e. a concurrent update is in progress, the extra check
* for vdso_clock->clock_mode is a non-issue. The task is spin waiting for the
* update to finish and for 'seq' to become even anyway.
*
* Timens page has vdso_clock->clock_mode set to VDSO_CLOCKMODE_TIMENS which
* enforces the time namespace handling path.
*/
static void timens_setup_vdso_clock_data(struct vdso_clock *vc,
struct time_namespace *ns)
{
struct timens_offset *offset = vc->offset;
struct timens_offset monotonic = offset_from_ts(ns->offsets.monotonic);
struct timens_offset boottime = offset_from_ts(ns->offsets.boottime);
vc->seq = 1;
vc->clock_mode = VDSO_CLOCKMODE_TIMENS;
offset[CLOCK_MONOTONIC] = monotonic;
offset[CLOCK_MONOTONIC_RAW] = monotonic;
offset[CLOCK_MONOTONIC_COARSE] = monotonic;
offset[CLOCK_BOOTTIME] = boottime;
offset[CLOCK_BOOTTIME_ALARM] = boottime;
}
struct page *find_timens_vvar_page(struct vm_area_struct *vma)
{
if (likely(vma->vm_mm == current->mm))
return current->nsproxy->time_ns->vvar_page;
/*
* VM_PFNMAP | VM_IO protect .fault() handler from being called
* through interfaces like /proc/$pid/mem or
* process_vm_{readv,writev}() as long as there's no .access()
* in special_mapping_vmops().
* For more details check_vma_flags() and __access_remote_vm()
*/
WARN(1, "vvar_page accessed remotely");
return NULL;
}
static void timens_set_vvar_page(struct task_struct *task,
struct time_namespace *ns)
{
struct vdso_time_data *vdata;
struct vdso_clock *vc;
unsigned int i;
if (ns == &init_time_ns)
return;
/* Fast-path, taken by every task in namespace except the first. */
if (likely(ns->frozen_offsets))
return;
guard(mutex)(&timens_offset_lock);
/* Nothing to-do: vvar_page has been already initialized. */
if (ns->frozen_offsets)
return;
ns->frozen_offsets = true;
vdata = page_address(ns->vvar_page);
vc = vdata->clock_data;
for (i = 0; i < CS_BASES; i++)
timens_setup_vdso_clock_data(&vc[i], ns);
if (IS_ENABLED(CONFIG_POSIX_AUX_CLOCKS)) {
for (i = 0; i < ARRAY_SIZE(vdata->aux_clock_data); i++)
timens_setup_vdso_clock_data(&vdata->aux_clock_data[i], ns);
}
}
/*
* The vvar page layout depends on whether a task belongs to the root or
* non-root time namespace. Whenever a task changes its namespace, the VVAR
* page tables are cleared and then they will be re-faulted with a
* corresponding layout.
* See also the comment near timens_setup_vdso_clock_data() for details.
*/
static int vdso_join_timens(struct task_struct *task, struct time_namespace *ns)
{
struct mm_struct *mm = task->mm;
struct vm_area_struct *vma;
VMA_ITERATOR(vmi, mm, 0);
guard(mmap_read_lock)(mm);
for_each_vma(vmi, vma) {
if (vma_is_special_mapping(vma, &vdso_vvar_mapping))
zap_vma(vma);
}
return 0;
}
void timens_commit(struct task_struct *tsk, struct time_namespace *ns)
{
timens_set_vvar_page(tsk, ns);
vdso_join_timens(tsk, ns);
}
int timens_vdso_alloc_vvar_page(struct time_namespace *ns)
{
ns->vvar_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
if (!ns->vvar_page)
return -ENOMEM;
return 0;
}
void timens_vdso_free_vvar_page(struct time_namespace *ns)
{
__free_page(ns->vvar_page);
}
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_NTP_INTERNAL_H
#define _LINUX_NTP_INTERNAL_H
extern void ntp_init(void);
extern void ntp_clear(unsigned int tkid);
/* Returns how long ticks are at present, in ns / 2^NTP_SCALE_SHIFT. */
extern u64 ntp_tick_length(unsigned int tkid);
extern ktime_t ntp_get_next_leap(unsigned int tkid);
extern int second_overflow(unsigned int tkid, time64_t secs);
extern int ntp_adjtimex(unsigned int tkid, struct __kernel_timex *txc, const struct timespec64 *ts,
s32 *time_tai, struct audit_ntp_data *ad);
extern void __hardpps(const struct timespec64 *phase_ts, const struct timespec64 *raw_ts);
#if defined(CONFIG_GENERIC_CMOS_UPDATE) || defined(CONFIG_RTC_SYSTOHC)
extern void ntp_notify_cmos_timer(bool offset_set);
#else
static inline void ntp_notify_cmos_timer(bool offset_set) { }
#endif
#endif /* _LINUX_NTP_INTERNAL_H */
+318
View File
@@ -0,0 +1,318 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Support for dynamic clock devices
*
* Copyright (C) 2010 OMICRON electronics GmbH
*/
#include <linux/device.h>
#include <linux/export.h>
#include <linux/file.h>
#include <linux/posix-clock.h>
#include <linux/slab.h>
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include "posix-timers.h"
/*
* Returns NULL if the posix_clock instance attached to 'fp' is old and stale.
*/
static struct posix_clock *get_posix_clock(struct file *fp)
{
struct posix_clock_context *pccontext = fp->private_data;
struct posix_clock *clk = pccontext->clk;
down_read(&clk->rwsem);
if (!clk->zombie)
return clk;
up_read(&clk->rwsem);
return NULL;
}
static void put_posix_clock(struct posix_clock *clk)
{
up_read(&clk->rwsem);
}
static ssize_t posix_clock_read(struct file *fp, char __user *buf,
size_t count, loff_t *ppos)
{
struct posix_clock_context *pccontext = fp->private_data;
struct posix_clock *clk = get_posix_clock(fp);
int err = -EINVAL;
if (!clk)
return -ENODEV;
if (clk->ops.read)
err = clk->ops.read(pccontext, fp->f_flags, buf, count);
put_posix_clock(clk);
return err;
}
static __poll_t posix_clock_poll(struct file *fp, poll_table *wait)
{
struct posix_clock_context *pccontext = fp->private_data;
struct posix_clock *clk = get_posix_clock(fp);
__poll_t result = 0;
if (!clk)
return EPOLLERR;
if (clk->ops.poll)
result = clk->ops.poll(pccontext, fp, wait);
put_posix_clock(clk);
return result;
}
static long posix_clock_ioctl(struct file *fp,
unsigned int cmd, unsigned long arg)
{
struct posix_clock_context *pccontext = fp->private_data;
struct posix_clock *clk = get_posix_clock(fp);
int err = -ENOTTY;
if (!clk)
return -ENODEV;
if (clk->ops.ioctl)
err = clk->ops.ioctl(pccontext, cmd, arg);
put_posix_clock(clk);
return err;
}
static int posix_clock_open(struct inode *inode, struct file *fp)
{
int err;
struct posix_clock *clk =
container_of(inode->i_cdev, struct posix_clock, cdev);
struct posix_clock_context *pccontext;
down_read(&clk->rwsem);
if (clk->zombie) {
err = -ENODEV;
goto out;
}
pccontext = kzalloc_obj(*pccontext);
if (!pccontext) {
err = -ENOMEM;
goto out;
}
pccontext->clk = clk;
pccontext->fp = fp;
if (clk->ops.open) {
err = clk->ops.open(pccontext, fp->f_mode);
if (err) {
kfree(pccontext);
goto out;
}
}
fp->private_data = pccontext;
get_device(clk->dev);
err = 0;
out:
up_read(&clk->rwsem);
return err;
}
static int posix_clock_release(struct inode *inode, struct file *fp)
{
struct posix_clock_context *pccontext = fp->private_data;
struct posix_clock *clk;
int err = 0;
if (!pccontext)
return -ENODEV;
clk = pccontext->clk;
if (clk->ops.release)
err = clk->ops.release(pccontext);
put_device(clk->dev);
kfree(pccontext);
fp->private_data = NULL;
return err;
}
static const struct file_operations posix_clock_file_operations = {
.owner = THIS_MODULE,
.read = posix_clock_read,
.poll = posix_clock_poll,
.unlocked_ioctl = posix_clock_ioctl,
.compat_ioctl = posix_clock_ioctl,
.open = posix_clock_open,
.release = posix_clock_release,
};
int posix_clock_register(struct posix_clock *clk, struct device *dev)
{
int err;
init_rwsem(&clk->rwsem);
cdev_init(&clk->cdev, &posix_clock_file_operations);
err = cdev_device_add(&clk->cdev, dev);
if (err) {
pr_err("%s unable to add device %d:%d\n",
dev_name(dev), MAJOR(dev->devt), MINOR(dev->devt));
return err;
}
clk->cdev.owner = clk->ops.owner;
clk->dev = dev;
return 0;
}
EXPORT_SYMBOL_GPL(posix_clock_register);
void posix_clock_unregister(struct posix_clock *clk)
{
cdev_device_del(&clk->cdev, clk->dev);
down_write(&clk->rwsem);
clk->zombie = true;
up_write(&clk->rwsem);
put_device(clk->dev);
}
EXPORT_SYMBOL_GPL(posix_clock_unregister);
struct posix_clock_desc {
struct file *fp;
struct posix_clock *clk;
};
static int get_clock_desc(const clockid_t id, struct posix_clock_desc *cd)
{
struct file *fp = fget(clockid_to_fd(id));
int err = -EINVAL;
if (!fp)
return err;
if (fp->f_op->open != posix_clock_open || !fp->private_data)
goto out;
cd->fp = fp;
cd->clk = get_posix_clock(fp);
err = cd->clk ? 0 : -ENODEV;
out:
if (err)
fput(fp);
return err;
}
static void put_clock_desc(struct posix_clock_desc *cd)
{
put_posix_clock(cd->clk);
fput(cd->fp);
}
static int pc_clock_adjtime(clockid_t id, struct __kernel_timex *tx)
{
struct posix_clock_desc cd;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
if (tx->modes && (cd.fp->f_mode & FMODE_WRITE) == 0) {
err = -EACCES;
goto out;
}
if (cd.clk->ops.clock_adjtime)
err = cd.clk->ops.clock_adjtime(cd.clk, tx);
else
err = -EOPNOTSUPP;
out:
put_clock_desc(&cd);
return err;
}
static int pc_clock_gettime(clockid_t id, struct timespec64 *ts)
{
struct posix_clock_desc cd;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
if (cd.clk->ops.clock_gettime)
err = cd.clk->ops.clock_gettime(cd.clk, ts);
else
err = -EOPNOTSUPP;
put_clock_desc(&cd);
return err;
}
static int pc_clock_getres(clockid_t id, struct timespec64 *ts)
{
struct posix_clock_desc cd;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
if (cd.clk->ops.clock_getres)
err = cd.clk->ops.clock_getres(cd.clk, ts);
else
err = -EOPNOTSUPP;
put_clock_desc(&cd);
return err;
}
static int pc_clock_settime(clockid_t id, const struct timespec64 *ts)
{
struct posix_clock_desc cd;
int err;
if (!timespec64_valid_strict(ts))
return -EINVAL;
err = get_clock_desc(id, &cd);
if (err)
return err;
if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
err = -EACCES;
goto out;
}
if (cd.clk->ops.clock_settime)
err = cd.clk->ops.clock_settime(cd.clk, ts);
else
err = -EOPNOTSUPP;
out:
put_clock_desc(&cd);
return err;
}
const struct k_clock clock_posix_dynamic = {
.clock_getres = pc_clock_getres,
.clock_set = pc_clock_settime,
.clock_get_timespec = pc_clock_gettime,
.clock_adj = pc_clock_adjtime,
};
File diff suppressed because it is too large Load Diff
+209
View File
@@ -0,0 +1,209 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Dummy stubs used when CONFIG_POSIX_TIMERS=n
*
* Created by: Nicolas Pitre, July 2016
* Copyright: (C) 2016 Linaro Limited
*/
#include <linux/linkage.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/syscalls.h>
#include <linux/ktime.h>
#include <linux/timekeeping.h>
#include <linux/posix-timers.h>
#include <linux/time_namespace.h>
#include <linux/compat.h>
/*
* We preserve minimal support for CLOCK_REALTIME and CLOCK_MONOTONIC
* as it is easy to remain compatible with little code. CLOCK_BOOTTIME
* is also included for convenience as at least systemd uses it.
*/
SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock,
const struct __kernel_timespec __user *, tp)
{
struct timespec64 new_tp;
if (which_clock != CLOCK_REALTIME)
return -EINVAL;
if (get_timespec64(&new_tp, tp))
return -EFAULT;
return do_sys_settimeofday64(&new_tp, NULL);
}
static int do_clock_gettime(clockid_t which_clock, struct timespec64 *tp)
{
switch (which_clock) {
case CLOCK_REALTIME:
ktime_get_real_ts64(tp);
break;
case CLOCK_MONOTONIC:
ktime_get_ts64(tp);
timens_add_monotonic(tp);
break;
case CLOCK_BOOTTIME:
ktime_get_boottime_ts64(tp);
timens_add_boottime(tp);
break;
default:
return -EINVAL;
}
return 0;
}
SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
struct __kernel_timespec __user *, tp)
{
int ret;
struct timespec64 kernel_tp;
ret = do_clock_gettime(which_clock, &kernel_tp);
if (ret)
return ret;
if (put_timespec64(&kernel_tp, tp))
return -EFAULT;
return 0;
}
SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock, struct __kernel_timespec __user *, tp)
{
struct timespec64 rtn_tp = {
.tv_sec = 0,
.tv_nsec = hrtimer_resolution,
};
switch (which_clock) {
case CLOCK_REALTIME:
case CLOCK_MONOTONIC:
case CLOCK_BOOTTIME:
if (put_timespec64(&rtn_tp, tp))
return -EFAULT;
return 0;
default:
return -EINVAL;
}
}
SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
const struct __kernel_timespec __user *, rqtp,
struct __kernel_timespec __user *, rmtp)
{
struct timespec64 t;
ktime_t texp;
switch (which_clock) {
case CLOCK_REALTIME:
case CLOCK_MONOTONIC:
case CLOCK_BOOTTIME:
break;
default:
return -EINVAL;
}
if (get_timespec64(&t, rqtp))
return -EFAULT;
if (!timespec64_valid(&t))
return -EINVAL;
if (flags & TIMER_ABSTIME)
rmtp = NULL;
current->restart_block.fn = do_no_restart_syscall;
current->restart_block.nanosleep.type = rmtp ? TT_NATIVE : TT_NONE;
current->restart_block.nanosleep.rmtp = rmtp;
texp = timespec64_to_ktime(t);
if (flags & TIMER_ABSTIME)
texp = timens_ktime_to_host(which_clock, texp);
return hrtimer_nanosleep(texp, flags & TIMER_ABSTIME ?
HRTIMER_MODE_ABS : HRTIMER_MODE_REL,
which_clock);
}
#ifdef CONFIG_COMPAT_32BIT_TIME
SYSCALL_DEFINE2(clock_settime32, const clockid_t, which_clock,
struct old_timespec32 __user *, tp)
{
struct timespec64 new_tp;
if (which_clock != CLOCK_REALTIME)
return -EINVAL;
if (get_old_timespec32(&new_tp, tp))
return -EFAULT;
return do_sys_settimeofday64(&new_tp, NULL);
}
SYSCALL_DEFINE2(clock_gettime32, clockid_t, which_clock,
struct old_timespec32 __user *, tp)
{
int ret;
struct timespec64 kernel_tp;
ret = do_clock_gettime(which_clock, &kernel_tp);
if (ret)
return ret;
if (put_old_timespec32(&kernel_tp, tp))
return -EFAULT;
return 0;
}
SYSCALL_DEFINE2(clock_getres_time32, clockid_t, which_clock,
struct old_timespec32 __user *, tp)
{
struct timespec64 rtn_tp = {
.tv_sec = 0,
.tv_nsec = hrtimer_resolution,
};
switch (which_clock) {
case CLOCK_REALTIME:
case CLOCK_MONOTONIC:
case CLOCK_BOOTTIME:
if (put_old_timespec32(&rtn_tp, tp))
return -EFAULT;
return 0;
default:
return -EINVAL;
}
}
SYSCALL_DEFINE4(clock_nanosleep_time32, clockid_t, which_clock, int, flags,
struct old_timespec32 __user *, rqtp,
struct old_timespec32 __user *, rmtp)
{
struct timespec64 t;
ktime_t texp;
switch (which_clock) {
case CLOCK_REALTIME:
case CLOCK_MONOTONIC:
case CLOCK_BOOTTIME:
break;
default:
return -EINVAL;
}
if (get_old_timespec32(&t, rqtp))
return -EFAULT;
if (!timespec64_valid(&t))
return -EINVAL;
if (flags & TIMER_ABSTIME)
rmtp = NULL;
current->restart_block.fn = do_no_restart_syscall;
current->restart_block.nanosleep.type = rmtp ? TT_COMPAT : TT_NONE;
current->restart_block.nanosleep.compat_rmtp = rmtp;
texp = timespec64_to_ktime(t);
if (flags & TIMER_ABSTIME)
texp = timens_ktime_to_host(which_clock, texp);
return hrtimer_nanosleep(texp, flags & TIMER_ABSTIME ?
HRTIMER_MODE_ABS : HRTIMER_MODE_REL,
which_clock);
}
#endif
File diff suppressed because it is too large Load Diff
+53
View File
@@ -0,0 +1,53 @@
/* SPDX-License-Identifier: GPL-2.0 */
#define TIMER_RETRY 1
enum posix_timer_state {
POSIX_TIMER_DISARMED,
POSIX_TIMER_ARMED,
POSIX_TIMER_REQUEUE_PENDING,
};
struct k_clock {
int (*clock_getres)(const clockid_t which_clock,
struct timespec64 *tp);
int (*clock_set)(const clockid_t which_clock,
const struct timespec64 *tp);
/* Returns the clock value in the current time namespace. */
int (*clock_get_timespec)(const clockid_t which_clock,
struct timespec64 *tp);
/* Returns the clock value in the root time namespace. */
ktime_t (*clock_get_ktime)(const clockid_t which_clock);
int (*clock_adj)(const clockid_t which_clock, struct __kernel_timex *tx);
int (*timer_create)(struct k_itimer *timer);
int (*nsleep)(const clockid_t which_clock, int flags,
const struct timespec64 *);
int (*timer_set)(struct k_itimer *timr, int flags,
struct itimerspec64 *new_setting,
struct itimerspec64 *old_setting);
int (*timer_del)(struct k_itimer *timr);
void (*timer_get)(struct k_itimer *timr,
struct itimerspec64 *cur_setting);
bool (*timer_rearm)(struct k_itimer *timr);
s64 (*timer_forward)(struct k_itimer *timr, ktime_t now);
ktime_t (*timer_remaining)(struct k_itimer *timr, ktime_t now);
int (*timer_try_to_cancel)(struct k_itimer *timr);
bool (*timer_arm)(struct k_itimer *timr, ktime_t expires,
bool absolute, bool sigev_none);
void (*timer_wait_running)(struct k_itimer *timr);
};
extern const struct k_clock clock_posix_cpu;
extern const struct k_clock clock_posix_dynamic;
extern const struct k_clock clock_process;
extern const struct k_clock clock_thread;
extern const struct k_clock alarm_clock;
extern const struct k_clock clock_aux;
void posix_timer_queue_signal(struct k_itimer *timr);
void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting);
int common_timer_set(struct k_itimer *timr, int flags,
struct itimerspec64 *new_setting,
struct itimerspec64 *old_setting);
void posix_timer_set_common(struct k_itimer *timer, struct itimerspec64 *new_setting);
int common_timer_del(struct k_itimer *timer);
+333
View File
@@ -0,0 +1,333 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Generic sched_clock() support, to extend low level hardware time
* counters to full 64-bit ns values.
*/
#include <linux/clocksource.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/ktime.h>
#include <linux/kernel.h>
#include <linux/math.h>
#include <linux/moduleparam.h>
#include <linux/sched.h>
#include <linux/sched/clock.h>
#include <linux/syscore_ops.h>
#include <linux/hrtimer.h>
#include <linux/sched_clock.h>
#include <linux/seqlock.h>
#include <linux/bitops.h>
#include "timekeeping.h"
/**
* struct clock_data - all data needed for sched_clock() (including
* registration of a new clock source)
*
* @seq: Sequence counter for protecting updates. The lowest
* bit is the index for @read_data.
* @read_data: Data required to read from sched_clock.
* @wrap_kt: Duration for which clock can run before wrapping.
* @rate: Tick rate of the registered clock.
* @actual_read_sched_clock: Registered hardware level clock read function.
*
* The ordering of this structure has been chosen to optimize cache
* performance. In particular 'seq' and 'read_data[0]' (combined) should fit
* into a single 64-byte cache line.
*/
struct clock_data {
seqcount_latch_t seq;
struct clock_read_data read_data[2];
ktime_t wrap_kt;
unsigned long rate;
u64 (*actual_read_sched_clock)(void);
};
static struct hrtimer sched_clock_timer;
static int irqtime = -1;
core_param(irqtime, irqtime, int, 0400);
static u64 notrace jiffy_sched_clock_read(void)
{
/*
* We don't need to use get_jiffies_64 on 32-bit arches here
* because we register with BITS_PER_LONG
*/
return (u64)(jiffies - INITIAL_JIFFIES);
}
static struct clock_data cd ____cacheline_aligned = {
.read_data[0] = { .mult = NSEC_PER_SEC / HZ,
.read_sched_clock = jiffy_sched_clock_read, },
.actual_read_sched_clock = jiffy_sched_clock_read,
};
static __always_inline u64 cyc_to_ns(u64 cyc, u32 mult, u32 shift)
{
return (cyc * mult) >> shift;
}
notrace struct clock_read_data *sched_clock_read_begin(unsigned int *seq)
{
*seq = read_seqcount_latch(&cd.seq);
return cd.read_data + (*seq & 1);
}
notrace int sched_clock_read_retry(unsigned int seq)
{
return read_seqcount_latch_retry(&cd.seq, seq);
}
static __always_inline unsigned long long __sched_clock(void)
{
struct clock_read_data *rd;
unsigned int seq;
u64 cyc, res;
do {
seq = raw_read_seqcount_latch(&cd.seq);
rd = cd.read_data + (seq & 1);
cyc = (rd->read_sched_clock() - rd->epoch_cyc) &
rd->sched_clock_mask;
res = rd->epoch_ns + cyc_to_ns(cyc, rd->mult, rd->shift);
} while (raw_read_seqcount_latch_retry(&cd.seq, seq));
return res;
}
unsigned long long noinstr sched_clock_noinstr(void)
{
return __sched_clock();
}
unsigned long long notrace sched_clock(void)
{
unsigned long long ns;
preempt_disable_notrace();
/*
* All of __sched_clock() is a seqcount_latch reader critical section,
* but relies on the raw helpers which are uninstrumented. For KCSAN,
* mark all accesses in __sched_clock() as atomic.
*/
kcsan_nestable_atomic_begin();
ns = __sched_clock();
kcsan_nestable_atomic_end();
preempt_enable_notrace();
return ns;
}
/*
* Updating the data required to read the clock.
*
* sched_clock() will never observe mis-matched data even if called from
* an NMI. We do this by maintaining an odd/even copy of the data and
* steering sched_clock() to one or the other using a sequence counter.
* In order to preserve the data cache profile of sched_clock() as much
* as possible the system reverts back to the even copy when the update
* completes; the odd copy is used *only* during an update.
*/
static void update_clock_read_data(struct clock_read_data *rd)
{
/* steer readers towards the odd copy */
write_seqcount_latch_begin(&cd.seq);
/* now its safe for us to update the normal (even) copy */
cd.read_data[0] = *rd;
/* switch readers back to the even copy */
write_seqcount_latch(&cd.seq);
/* update the backup (odd) copy with the new data */
cd.read_data[1] = *rd;
write_seqcount_latch_end(&cd.seq);
}
/*
* Atomically update the sched_clock() epoch.
*/
static void update_sched_clock(void)
{
u64 cyc;
u64 ns;
struct clock_read_data rd;
rd = cd.read_data[0];
cyc = cd.actual_read_sched_clock();
ns = rd.epoch_ns + cyc_to_ns((cyc - rd.epoch_cyc) & rd.sched_clock_mask, rd.mult, rd.shift);
rd.epoch_ns = ns;
rd.epoch_cyc = cyc;
update_clock_read_data(&rd);
}
static enum hrtimer_restart sched_clock_poll(struct hrtimer *hrt)
{
update_sched_clock();
hrtimer_forward_now(hrt, cd.wrap_kt);
return HRTIMER_RESTART;
}
void sched_clock_register(u64 (*read)(void), int bits, unsigned long rate)
{
u64 res, wrap, new_mask, new_epoch, cyc, ns;
u32 new_mult, new_shift;
unsigned long r, flags;
char r_unit;
struct clock_read_data rd;
if (cd.rate > rate)
return;
/* Cannot register a sched_clock with interrupts on */
local_irq_save(flags);
/* Calculate the mult/shift to convert counter ticks to ns. */
clocks_calc_mult_shift(&new_mult, &new_shift, rate, NSEC_PER_SEC, 3600);
new_mask = CLOCKSOURCE_MASK(bits);
cd.rate = rate;
/* Calculate how many nanosecs until we risk wrapping */
wrap = clocks_calc_max_nsecs(new_mult, new_shift, 0, new_mask, NULL);
cd.wrap_kt = ns_to_ktime(wrap);
rd = cd.read_data[0];
/* Update epoch for new counter and update 'epoch_ns' from old counter*/
new_epoch = read();
cyc = cd.actual_read_sched_clock();
ns = rd.epoch_ns + cyc_to_ns((cyc - rd.epoch_cyc) & rd.sched_clock_mask, rd.mult, rd.shift);
cd.actual_read_sched_clock = read;
rd.read_sched_clock = read;
rd.sched_clock_mask = new_mask;
rd.mult = new_mult;
rd.shift = new_shift;
rd.epoch_cyc = new_epoch;
rd.epoch_ns = ns;
update_clock_read_data(&rd);
if (ACCESS_PRIVATE(&sched_clock_timer, function) != NULL) {
/* update timeout for clock wrap */
hrtimer_start(&sched_clock_timer, cd.wrap_kt,
HRTIMER_MODE_REL_HARD);
}
r = rate;
if (r >= 4000000) {
r = DIV_ROUND_CLOSEST(r, 1000000);
r_unit = 'M';
} else if (r >= 4000) {
r = DIV_ROUND_CLOSEST(r, 1000);
r_unit = 'k';
} else {
r_unit = ' ';
}
/* Calculate the ns resolution of this counter */
res = cyc_to_ns(1ULL, new_mult, new_shift);
pr_info("sched_clock: %u bits at %lu%cHz, resolution %lluns, wraps every %lluns\n",
bits, r, r_unit, res, wrap);
/* Enable IRQ time accounting if we have a fast enough sched_clock() */
if (irqtime > 0 || (irqtime == -1 && rate >= 1000000))
enable_sched_clock_irqtime();
local_irq_restore(flags);
pr_debug("Registered %pS as sched_clock source\n", read);
}
EXPORT_SYMBOL_GPL(sched_clock_register);
void __init generic_sched_clock_init(void)
{
/*
* If no sched_clock() function has been provided at that point,
* make it the final one.
*/
if (cd.actual_read_sched_clock == jiffy_sched_clock_read)
sched_clock_register(jiffy_sched_clock_read, BITS_PER_LONG, HZ);
update_sched_clock();
/*
* Start the timer to keep sched_clock() properly updated and
* sets the initial epoch.
*/
hrtimer_setup(&sched_clock_timer, sched_clock_poll, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
hrtimer_start(&sched_clock_timer, cd.wrap_kt, HRTIMER_MODE_REL_HARD);
}
/*
* Clock read function for use when the clock is suspended.
*
* This function makes it appear to sched_clock() as if the clock
* stopped counting at its last update.
*
* This function must only be called from the critical
* section in sched_clock(). It relies on the read_seqcount_retry()
* at the end of the critical section to be sure we observe the
* correct copy of 'epoch_cyc'.
*/
static u64 notrace suspended_sched_clock_read(void)
{
unsigned int seq = read_seqcount_latch(&cd.seq);
return cd.read_data[seq & 1].epoch_cyc;
}
int sched_clock_suspend(void)
{
struct clock_read_data *rd = &cd.read_data[0];
update_sched_clock();
hrtimer_cancel(&sched_clock_timer);
rd->read_sched_clock = suspended_sched_clock_read;
return 0;
}
static int sched_clock_syscore_suspend(void *data)
{
return sched_clock_suspend();
}
void sched_clock_resume(void)
{
struct clock_read_data *rd = &cd.read_data[0];
rd->epoch_cyc = cd.actual_read_sched_clock();
hrtimer_start(&sched_clock_timer, cd.wrap_kt, HRTIMER_MODE_REL_HARD);
rd->read_sched_clock = cd.actual_read_sched_clock;
}
static void sched_clock_syscore_resume(void *data)
{
sched_clock_resume();
}
static const struct syscore_ops sched_clock_syscore_ops = {
.suspend = sched_clock_syscore_suspend,
.resume = sched_clock_syscore_resume,
};
static struct syscore sched_clock_syscore = {
.ops = &sched_clock_syscore_ops,
};
static int __init sched_clock_syscore_init(void)
{
register_syscore(&sched_clock_syscore);
return 0;
}
device_initcall(sched_clock_syscore_init);
+377
View File
@@ -0,0 +1,377 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Kernel internal schedule timeout and sleeping functions
*/
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <linux/sched/signal.h>
#include <linux/sched/debug.h>
#include "tick-internal.h"
/*
* Since schedule_timeout()'s timer is defined on the stack, it must store
* the target task on the stack as well.
*/
struct process_timer {
struct timer_list timer;
struct task_struct *task;
};
static void process_timeout(struct timer_list *t)
{
struct process_timer *timeout = timer_container_of(timeout, t, timer);
wake_up_process(timeout->task);
}
/**
* schedule_timeout - sleep until timeout
* @timeout: timeout value in jiffies
*
* Make the current task sleep until @timeout jiffies have elapsed.
* The function behavior depends on the current task state
* (see also set_current_state() description):
*
* %TASK_RUNNING - the scheduler is called, but the task does not sleep
* at all. That happens because sched_submit_work() does nothing for
* tasks in %TASK_RUNNING state.
*
* %TASK_UNINTERRUPTIBLE - at least @timeout jiffies are guaranteed to
* pass before the routine returns unless the current task is explicitly
* woken up, (e.g. by wake_up_process()).
*
* %TASK_INTERRUPTIBLE - the routine may return early if a signal is
* delivered to the current task or the current task is explicitly woken
* up.
*
* The current task state is guaranteed to be %TASK_RUNNING when this
* routine returns.
*
* Specifying a @timeout value of %MAX_SCHEDULE_TIMEOUT will schedule
* the CPU away without a bound on the timeout. In this case the return
* value will be %MAX_SCHEDULE_TIMEOUT.
*
* Returns: 0 when the timer has expired otherwise the remaining time in
* jiffies will be returned. In all cases the return value is guaranteed
* to be non-negative.
*/
signed long __sched schedule_timeout(signed long timeout)
{
struct process_timer timer;
unsigned long expire;
switch (timeout) {
case MAX_SCHEDULE_TIMEOUT:
/*
* These two special cases are useful to be comfortable
* in the caller. Nothing more. We could take
* MAX_SCHEDULE_TIMEOUT from one of the negative value
* but I' d like to return a valid offset (>=0) to allow
* the caller to do everything it want with the retval.
*/
schedule();
goto out;
default:
/*
* Another bit of PARANOID. Note that the retval will be
* 0 since no piece of kernel is supposed to do a check
* for a negative retval of schedule_timeout() (since it
* should never happens anyway). You just have the printk()
* that will tell you if something is gone wrong and where.
*/
if (timeout < 0) {
pr_err("%s: wrong timeout value %lx\n", __func__, timeout);
dump_stack();
__set_current_state(TASK_RUNNING);
goto out;
}
}
expire = timeout + jiffies;
timer.task = current;
timer_setup_on_stack(&timer.timer, process_timeout, 0);
timer.timer.expires = expire;
add_timer(&timer.timer);
schedule();
timer_delete_sync(&timer.timer);
/* Remove the timer from the object tracker */
timer_destroy_on_stack(&timer.timer);
timeout = expire - jiffies;
out:
return timeout < 0 ? 0 : timeout;
}
EXPORT_SYMBOL(schedule_timeout);
/*
* __set_current_state() can be used in schedule_timeout_*() functions, because
* schedule_timeout() calls schedule() unconditionally.
*/
/**
* schedule_timeout_interruptible - sleep until timeout (interruptible)
* @timeout: timeout value in jiffies
*
* See schedule_timeout() for details.
*
* Task state is set to TASK_INTERRUPTIBLE before starting the timeout.
*/
signed long __sched schedule_timeout_interruptible(signed long timeout)
{
__set_current_state(TASK_INTERRUPTIBLE);
return schedule_timeout(timeout);
}
EXPORT_SYMBOL(schedule_timeout_interruptible);
/**
* schedule_timeout_killable - sleep until timeout (killable)
* @timeout: timeout value in jiffies
*
* See schedule_timeout() for details.
*
* Task state is set to TASK_KILLABLE before starting the timeout.
*/
signed long __sched schedule_timeout_killable(signed long timeout)
{
__set_current_state(TASK_KILLABLE);
return schedule_timeout(timeout);
}
EXPORT_SYMBOL(schedule_timeout_killable);
/**
* schedule_timeout_uninterruptible - sleep until timeout (uninterruptible)
* @timeout: timeout value in jiffies
*
* See schedule_timeout() for details.
*
* Task state is set to TASK_UNINTERRUPTIBLE before starting the timeout.
*/
signed long __sched schedule_timeout_uninterruptible(signed long timeout)
{
__set_current_state(TASK_UNINTERRUPTIBLE);
return schedule_timeout(timeout);
}
EXPORT_SYMBOL(schedule_timeout_uninterruptible);
/**
* schedule_timeout_idle - sleep until timeout (idle)
* @timeout: timeout value in jiffies
*
* See schedule_timeout() for details.
*
* Task state is set to TASK_IDLE before starting the timeout. It is similar to
* schedule_timeout_uninterruptible(), except this task will not contribute to
* load average.
*/
signed long __sched schedule_timeout_idle(signed long timeout)
{
__set_current_state(TASK_IDLE);
return schedule_timeout(timeout);
}
EXPORT_SYMBOL(schedule_timeout_idle);
/**
* schedule_hrtimeout_range_clock - sleep until timeout
* @expires: timeout value (ktime_t)
* @delta: slack in expires timeout (ktime_t)
* @mode: timer mode
* @clock_id: timer clock to be used
*
* Details are explained in schedule_hrtimeout_range() function description as
* this function is commonly used.
*/
int __sched schedule_hrtimeout_range_clock(ktime_t *expires, u64 delta,
const enum hrtimer_mode mode, clockid_t clock_id)
{
struct hrtimer_sleeper t;
/*
* Optimize when a zero timeout value is given. It does not
* matter whether this is an absolute or a relative time.
*/
if (expires && *expires == 0) {
__set_current_state(TASK_RUNNING);
return 0;
}
/*
* A NULL parameter means "infinite"
*/
if (!expires) {
schedule();
return -EINTR;
}
hrtimer_setup_sleeper_on_stack(&t, clock_id, mode);
hrtimer_set_expires_range_ns(&t.timer, *expires, delta);
hrtimer_sleeper_start_expires(&t, mode);
if (likely(t.task))
schedule();
hrtimer_cancel(&t.timer);
destroy_hrtimer_on_stack(&t.timer);
__set_current_state(TASK_RUNNING);
return !t.task ? 0 : -EINTR;
}
EXPORT_SYMBOL_GPL(schedule_hrtimeout_range_clock);
/**
* schedule_hrtimeout_range - sleep until timeout
* @expires: timeout value (ktime_t)
* @delta: slack in expires timeout (ktime_t)
* @mode: timer mode
*
* Make the current task sleep until the given expiry time has
* elapsed. The routine will return immediately unless
* the current task state has been set (see set_current_state()).
*
* The @delta argument gives the kernel the freedom to schedule the
* actual wakeup to a time that is both power and performance friendly
* for regular (non RT/DL) tasks.
* The kernel give the normal best effort behavior for "@expires+@delta",
* but may decide to fire the timer earlier, but no earlier than @expires.
*
* You can set the task state as follows -
*
* %TASK_UNINTERRUPTIBLE - at least @timeout time is guaranteed to
* pass before the routine returns unless the current task is explicitly
* woken up, (e.g. by wake_up_process()).
*
* %TASK_INTERRUPTIBLE - the routine may return early if a signal is
* delivered to the current task or the current task is explicitly woken
* up.
*
* The current task state is guaranteed to be TASK_RUNNING when this
* routine returns.
*
* Returns: 0 when the timer has expired. If the task was woken before the
* timer expired by a signal (only possible in state TASK_INTERRUPTIBLE) or
* by an explicit wakeup, it returns -EINTR.
*/
int __sched schedule_hrtimeout_range(ktime_t *expires, u64 delta,
const enum hrtimer_mode mode)
{
return schedule_hrtimeout_range_clock(expires, delta, mode,
CLOCK_MONOTONIC);
}
EXPORT_SYMBOL_GPL(schedule_hrtimeout_range);
/**
* schedule_hrtimeout - sleep until timeout
* @expires: timeout value (ktime_t)
* @mode: timer mode
*
* See schedule_hrtimeout_range() for details. @delta argument of
* schedule_hrtimeout_range() is set to 0 and has therefore no impact.
*/
int __sched schedule_hrtimeout(ktime_t *expires, const enum hrtimer_mode mode)
{
return schedule_hrtimeout_range(expires, 0, mode);
}
EXPORT_SYMBOL_GPL(schedule_hrtimeout);
/**
* msleep - sleep safely even with waitqueue interruptions
* @msecs: Requested sleep duration in milliseconds
*
* msleep() uses jiffy based timeouts for the sleep duration. Because of the
* design of the timer wheel, the maximum additional percentage delay (slack) is
* 12.5%. This is only valid for timers which will end up in level 1 or a higher
* level of the timer wheel. For explanation of those 12.5% please check the
* detailed description about the basics of the timer wheel.
*
* The slack of timers which will end up in level 0 depends on sleep duration
* (msecs) and HZ configuration and can be calculated in the following way (with
* the timer wheel design restriction that the slack is not less than 12.5%):
*
* ``slack = MSECS_PER_TICK / msecs``
*
* When the allowed slack of the callsite is known, the calculation could be
* turned around to find the minimal allowed sleep duration to meet the
* constraints. For example:
*
* * ``HZ=1000`` with ``slack=25%``: ``MSECS_PER_TICK / slack = 1 / (1/4) = 4``:
* all sleep durations greater or equal 4ms will meet the constraints.
* * ``HZ=1000`` with ``slack=12.5%``: ``MSECS_PER_TICK / slack = 1 / (1/8) = 8``:
* all sleep durations greater or equal 8ms will meet the constraints.
* * ``HZ=250`` with ``slack=25%``: ``MSECS_PER_TICK / slack = 4 / (1/4) = 16``:
* all sleep durations greater or equal 16ms will meet the constraints.
* * ``HZ=250`` with ``slack=12.5%``: ``MSECS_PER_TICK / slack = 4 / (1/8) = 32``:
* all sleep durations greater or equal 32ms will meet the constraints.
*
* See also the signal aware variant msleep_interruptible().
*/
void msleep(unsigned int msecs)
{
unsigned long timeout = msecs_to_jiffies(msecs);
while (timeout)
timeout = schedule_timeout_uninterruptible(timeout);
}
EXPORT_SYMBOL(msleep);
/**
* msleep_interruptible - sleep waiting for signals
* @msecs: Requested sleep duration in milliseconds
*
* See msleep() for some basic information.
*
* The difference between msleep() and msleep_interruptible() is that the sleep
* could be interrupted by a signal delivery and then returns early.
*
* Returns: The remaining time of the sleep duration transformed to msecs (see
* schedule_timeout() for details).
*/
unsigned long msleep_interruptible(unsigned int msecs)
{
unsigned long timeout = msecs_to_jiffies(msecs);
while (timeout && !signal_pending(current))
timeout = schedule_timeout_interruptible(timeout);
return jiffies_to_msecs(timeout);
}
EXPORT_SYMBOL(msleep_interruptible);
/**
* usleep_range_state - Sleep for an approximate time in a given state
* @min: Minimum time in usecs to sleep
* @max: Maximum time in usecs to sleep
* @state: State of the current task that will be while sleeping
*
* usleep_range_state() sleeps at least for the minimum specified time but not
* longer than the maximum specified amount of time. The range might reduce
* power usage by allowing hrtimers to coalesce an already scheduled interrupt
* with this hrtimer. In the worst case, an interrupt is scheduled for the upper
* bound.
*
* The sleeping task is set to the specified state before starting the sleep.
*
* In non-atomic context where the exact wakeup time is flexible, use
* usleep_range() or its variants instead of udelay(). The sleep improves
* responsiveness by avoiding the CPU-hogging busy-wait of udelay().
*/
void __sched usleep_range_state(unsigned long min, unsigned long max, unsigned int state)
{
ktime_t exp = ktime_add_us(ktime_get(), min);
u64 delta = (u64)(max - min) * NSEC_PER_USEC;
if (WARN_ON_ONCE(max < min))
delta = 0;
for (;;) {
__set_current_state(state);
/* Do not return before the requested sleep time has elapsed */
if (!schedule_hrtimeout_range(&exp, delta, HRTIMER_MODE_ABS))
break;
}
}
EXPORT_SYMBOL(usleep_range_state);
+160
View File
@@ -0,0 +1,160 @@
// SPDX-License-Identifier: GPL-2.0
/*
* udelay() test kernel module
*
* Test is executed by writing and reading to /sys/kernel/debug/udelay_test
* Tests are configured by writing: USECS ITERATIONS
* Tests are executed by reading from the same file.
* Specifying usecs of 0 or negative values will run multiples tests.
*
* Copyright (C) 2014 Google, Inc.
*/
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#define DEFAULT_ITERATIONS 100
#define DEBUGFS_FILENAME "udelay_test"
static DEFINE_MUTEX(udelay_test_lock);
static int udelay_test_usecs;
static int udelay_test_iterations = DEFAULT_ITERATIONS;
static int udelay_test_single(struct seq_file *s, int usecs, uint32_t iters)
{
int min = 0, max = 0, fail_count = 0;
uint64_t sum = 0;
uint64_t avg;
int i;
/* Allow udelay to be up to 0.5% fast */
int allowed_error_ns = usecs * 5;
for (i = 0; i < iters; ++i) {
s64 kt1, kt2;
int time_passed;
kt1 = ktime_get_ns();
udelay(usecs);
kt2 = ktime_get_ns();
time_passed = kt2 - kt1;
if (i == 0 || time_passed < min)
min = time_passed;
if (i == 0 || time_passed > max)
max = time_passed;
if ((time_passed + allowed_error_ns) / 1000 < usecs)
++fail_count;
WARN_ON(time_passed < 0);
sum += time_passed;
}
avg = sum;
do_div(avg, iters);
seq_printf(s, "%d usecs x %d: exp=%d allowed=%d min=%d avg=%lld max=%d",
usecs, iters, usecs * 1000,
(usecs * 1000) - allowed_error_ns, min, avg, max);
if (fail_count)
seq_printf(s, " FAIL=%d", fail_count);
seq_puts(s, "\n");
return 0;
}
static int udelay_test_show(struct seq_file *s, void *v)
{
int usecs;
int iters;
int ret = 0;
mutex_lock(&udelay_test_lock);
usecs = udelay_test_usecs;
iters = udelay_test_iterations;
mutex_unlock(&udelay_test_lock);
if (usecs > 0 && iters > 0) {
return udelay_test_single(s, usecs, iters);
} else if (usecs == 0) {
struct timespec64 ts;
ktime_get_ts64(&ts);
seq_printf(s, "udelay() test (lpj=%ld kt=%lld.%09ld)\n",
loops_per_jiffy, (s64)ts.tv_sec, ts.tv_nsec);
seq_puts(s, "usage:\n");
seq_puts(s, "echo USECS [ITERS] > " DEBUGFS_FILENAME "\n");
seq_puts(s, "cat " DEBUGFS_FILENAME "\n");
}
return ret;
}
static int udelay_test_open(struct inode *inode, struct file *file)
{
return single_open(file, udelay_test_show, inode->i_private);
}
static ssize_t udelay_test_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
char lbuf[32];
int ret;
int usecs;
int iters;
if (count >= sizeof(lbuf))
return -EINVAL;
if (copy_from_user(lbuf, buf, count))
return -EFAULT;
lbuf[count] = '\0';
ret = sscanf(lbuf, "%d %d", &usecs, &iters);
if (ret < 1)
return -EINVAL;
else if (ret < 2)
iters = DEFAULT_ITERATIONS;
mutex_lock(&udelay_test_lock);
udelay_test_usecs = usecs;
udelay_test_iterations = iters;
mutex_unlock(&udelay_test_lock);
return count;
}
static const struct file_operations udelay_test_debugfs_ops = {
.owner = THIS_MODULE,
.open = udelay_test_open,
.read = seq_read,
.write = udelay_test_write,
.llseek = seq_lseek,
.release = single_release,
};
static int __init udelay_test_init(void)
{
mutex_lock(&udelay_test_lock);
debugfs_create_file(DEBUGFS_FILENAME, S_IRUSR, NULL, NULL,
&udelay_test_debugfs_ops);
mutex_unlock(&udelay_test_lock);
return 0;
}
module_init(udelay_test_init);
static void __exit udelay_test_exit(void)
{
mutex_lock(&udelay_test_lock);
debugfs_lookup_and_remove(DEBUGFS_FILENAME, NULL);
mutex_unlock(&udelay_test_lock);
}
module_exit(udelay_test_exit);
MODULE_DESCRIPTION("udelay test module");
MODULE_AUTHOR("David Riley <davidriley@chromium.org>");
MODULE_LICENSE("GPL");
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Emulate a local clock event device via a pseudo clock device.
*/
#include <linux/cpu.h>
#include <linux/err.h>
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/percpu.h>
#include <linux/profile.h>
#include <linux/clockchips.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/module.h>
#include "tick-internal.h"
static struct hrtimer bctimer;
static int bc_shutdown(struct clock_event_device *evt)
{
/*
* Note, we cannot cancel the timer here as we might
* run into the following live lock scenario:
*
* cpu 0 cpu1
* lock(broadcast_lock);
* hrtimer_interrupt()
* bc_handler()
* tick_handle_oneshot_broadcast();
* lock(broadcast_lock);
* hrtimer_cancel()
* wait_for_callback()
*/
hrtimer_try_to_cancel(&bctimer);
return 0;
}
/*
* This is called from the guts of the broadcast code when the cpu
* which is about to enter idle has the earliest broadcast timer event.
*/
static int bc_set_next(ktime_t expires, struct clock_event_device *bc)
{
/*
* This is called either from enter/exit idle code or from the
* broadcast handler. In all cases tick_broadcast_lock is held.
*
* hrtimer_cancel() cannot be called here neither from the
* broadcast handler nor from the enter/exit idle code. The idle
* code can run into the problem described in bc_shutdown() and the
* broadcast handler cannot wait for itself to complete for obvious
* reasons.
*
* Each caller tries to arm the hrtimer on its own CPU, but if the
* hrtimer callback function is currently running, then
* hrtimer_start() cannot move it and the timer stays on the CPU on
* which it is assigned at the moment.
*/
hrtimer_start(&bctimer, expires, HRTIMER_MODE_ABS_PINNED_HARD);
/*
* The core tick broadcast mode expects bc->bound_on to be set
* correctly to prevent a CPU which has the broadcast hrtimer
* armed from going deep idle.
*
* As tick_broadcast_lock is held, nothing can change the cpu
* base which was just established in hrtimer_start() above. So
* the below access is safe even without holding the hrtimer
* base lock.
*/
bc->bound_on = bctimer.base->cpu_base->cpu;
return 0;
}
static struct clock_event_device ce_broadcast_hrtimer = {
.name = "bc_hrtimer",
.set_state_shutdown = bc_shutdown,
.set_next_ktime = bc_set_next,
.features = CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_HRTIMER,
.rating = 0,
.bound_on = -1,
.min_delta_ns = 1,
.max_delta_ns = KTIME_MAX,
.min_delta_ticks = 1,
.max_delta_ticks = ULONG_MAX,
.mult = 1,
.shift = 0,
.cpumask = cpu_possible_mask,
};
static enum hrtimer_restart bc_handler(struct hrtimer *t)
{
ce_broadcast_hrtimer.event_handler(&ce_broadcast_hrtimer);
return HRTIMER_NORESTART;
}
void tick_setup_hrtimer_broadcast(void)
{
hrtimer_setup(&bctimer, bc_handler, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
clockevents_register_device(&ce_broadcast_hrtimer);
}
File diff suppressed because it is too large Load Diff
+595
View File
@@ -0,0 +1,595 @@
// SPDX-License-Identifier: GPL-2.0
/*
* This file contains the base functions to manage periodic tick
* related events.
*
* Copyright(C) 2005-2006, Linutronix GmbH, Thomas Gleixner <tglx@kernel.org>
* Copyright(C) 2005-2007, Red Hat, Inc., Ingo Molnar
* Copyright(C) 2006-2007, Timesys Corp., Thomas Gleixner
*/
#include <linux/compiler.h>
#include <linux/cpu.h>
#include <linux/err.h>
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/nmi.h>
#include <linux/percpu.h>
#include <linux/profile.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <trace/events/power.h>
#include <asm/irq_regs.h>
#include "tick-internal.h"
/*
* Tick devices
*/
DEFINE_PER_CPU(struct tick_device, tick_cpu_device);
/*
* Tick next event: keeps track of the tick time. It's updated by the
* CPU which handles the tick and protected by jiffies_lock. There is
* no requirement to write hold the jiffies seqcount for it.
*/
ktime_t tick_next_period;
/*
* tick_do_timer_cpu is a timer core internal variable which holds the CPU NR
* which is responsible for calling do_timer(), i.e. the timekeeping stuff. This
* variable has two functions:
*
* 1) Prevent a thundering herd issue of a gazillion of CPUs trying to grab the
* timekeeping lock all at once. Only the CPU which is assigned to do the
* update is handling it.
*
* 2) Hand off the duty in the NOHZ idle case by setting the value to
* TICK_DO_TIMER_NONE, i.e. a non existing CPU. So the next cpu which looks
* at it will take over and keep the time keeping alive. The handover
* procedure also covers cpu hotplug.
*/
int tick_do_timer_cpu __read_mostly = TICK_DO_TIMER_BOOT;
#ifdef CONFIG_NO_HZ_FULL
/*
* tick_do_timer_boot_cpu indicates the boot CPU temporarily owns
* tick_do_timer_cpu and it should be taken over by an eligible secondary
* when one comes online.
*/
static int tick_do_timer_boot_cpu __read_mostly = -1;
#endif
/*
* Debugging: see timer_list.c
*/
struct tick_device *tick_get_device(int cpu)
{
return &per_cpu(tick_cpu_device, cpu);
}
/**
* tick_is_oneshot_available - check for a oneshot capable event device
*/
int tick_is_oneshot_available(void)
{
struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT))
return 0;
if (!(dev->features & CLOCK_EVT_FEAT_C3STOP))
return 1;
return tick_broadcast_oneshot_available();
}
/*
* Periodic tick
*/
static void tick_periodic(int cpu)
{
if (READ_ONCE(tick_do_timer_cpu) == cpu) {
raw_spin_lock(&jiffies_lock);
write_seqcount_begin(&jiffies_seq);
/* Keep track of the next tick event */
tick_next_period = ktime_add_ns(tick_next_period, TICK_NSEC);
do_timer(1);
write_seqcount_end(&jiffies_seq);
raw_spin_unlock(&jiffies_lock);
update_wall_time();
}
update_process_times(user_mode(get_irq_regs()));
profile_tick(CPU_PROFILING);
}
/*
* Event handler for periodic ticks
*/
void tick_handle_periodic(struct clock_event_device *dev)
{
int cpu = smp_processor_id();
ktime_t next = dev->next_event;
dev->next_event_forced = 0;
tick_periodic(cpu);
/*
* The cpu might have transitioned to HIGHRES or NOHZ mode via
* update_process_times() -> run_local_timers() ->
* hrtimer_run_queues().
*/
if (IS_ENABLED(CONFIG_TICK_ONESHOT) && dev->event_handler != tick_handle_periodic)
return;
if (!clockevent_state_oneshot(dev))
return;
for (;;) {
/*
* Setup the next period for devices, which do not have
* periodic mode:
*/
next = ktime_add_ns(next, TICK_NSEC);
if (!clockevents_program_event(dev, next, false))
return;
/*
* Have to be careful here. If we're in oneshot mode,
* before we call tick_periodic() in a loop, we need
* to be sure we're using a real hardware clocksource.
* Otherwise we could get trapped in an infinite
* loop, as the tick_periodic() increments jiffies,
* which then will increment time, possibly causing
* the loop to trigger again and again.
*/
if (timekeeping_valid_for_hres())
tick_periodic(cpu);
}
}
/*
* Setup the device for a periodic tick
*/
void tick_setup_periodic(struct clock_event_device *dev, int broadcast)
{
tick_set_periodic_handler(dev, broadcast);
/* Broadcast setup ? */
if (!tick_device_is_functional(dev))
return;
if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) &&
!tick_broadcast_oneshot_active()) {
clockevents_switch_state(dev, CLOCK_EVT_STATE_PERIODIC);
} else {
unsigned int seq;
ktime_t next;
do {
seq = read_seqcount_begin(&jiffies_seq);
next = tick_next_period;
} while (read_seqcount_retry(&jiffies_seq, seq));
clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT);
for (;;) {
if (!clockevents_program_event(dev, next, false))
return;
next = ktime_add_ns(next, TICK_NSEC);
}
}
}
/*
* Setup the tick device
*/
static void tick_setup_device(struct tick_device *td,
struct clock_event_device *newdev, int cpu,
const struct cpumask *cpumask)
{
void (*handler)(struct clock_event_device *) = NULL;
ktime_t next_event = 0;
/*
* First device setup ?
*/
if (!td->evtdev) {
/*
* If no cpu took the do_timer update, assign it to
* this cpu:
*/
if (READ_ONCE(tick_do_timer_cpu) == TICK_DO_TIMER_BOOT) {
WRITE_ONCE(tick_do_timer_cpu, cpu);
tick_next_period = ktime_get();
#ifdef CONFIG_NO_HZ_FULL
/*
* The boot CPU may be nohz_full, in which case the
* first housekeeping secondary will take do_timer()
* from it.
*/
if (tick_nohz_full_cpu(cpu))
tick_do_timer_boot_cpu = cpu;
} else if (tick_do_timer_boot_cpu != -1 && !tick_nohz_full_cpu(cpu)) {
tick_do_timer_boot_cpu = -1;
/*
* The boot CPU will stay in periodic (NOHZ disabled)
* mode until clocksource_done_booting() called after
* smp_init() selects a high resolution clocksource and
* timekeeping_notify() kicks the NOHZ stuff alive.
*
* So this WRITE_ONCE can only race with the READ_ONCE
* check in tick_periodic() but this race is harmless.
*/
WRITE_ONCE(tick_do_timer_cpu, cpu);
#endif
}
/*
* Startup in periodic mode first.
*/
td->mode = TICKDEV_MODE_PERIODIC;
} else {
handler = td->evtdev->event_handler;
next_event = td->evtdev->next_event;
td->evtdev->event_handler = clockevents_handle_noop;
}
td->evtdev = newdev;
/*
* When the device is not per cpu, pin the interrupt to the
* current cpu:
*/
if (!cpumask_equal(newdev->cpumask, cpumask))
irq_set_affinity(newdev->irq, cpumask);
/*
* When global broadcasting is active, check if the current
* device is registered as a placeholder for broadcast mode.
* This allows us to handle this x86 misfeature in a generic
* way. This function also returns !=0 when we keep the
* current active broadcast state for this CPU.
*/
if (tick_device_uses_broadcast(newdev, cpu))
return;
if (td->mode == TICKDEV_MODE_PERIODIC)
tick_setup_periodic(newdev, 0);
else
tick_setup_oneshot(newdev, handler, next_event);
}
void tick_install_replacement(struct clock_event_device *newdev)
{
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
int cpu = smp_processor_id();
clockevents_exchange_device(td->evtdev, newdev);
tick_setup_device(td, newdev, cpu, cpumask_of(cpu));
if (newdev->features & CLOCK_EVT_FEAT_ONESHOT)
tick_oneshot_notify();
}
static bool tick_check_percpu(struct clock_event_device *curdev,
struct clock_event_device *newdev, int cpu)
{
if (!cpumask_test_cpu(cpu, newdev->cpumask))
return false;
if (cpumask_equal(newdev->cpumask, cpumask_of(cpu)))
return true;
/* Check if irq affinity can be set */
if (newdev->irq >= 0 && !irq_can_set_affinity(newdev->irq))
return false;
/* Prefer an existing cpu local device */
if (curdev && cpumask_equal(curdev->cpumask, cpumask_of(cpu)))
return false;
return true;
}
static bool tick_check_preferred(struct clock_event_device *curdev,
struct clock_event_device *newdev)
{
/* Prefer oneshot capable device */
if (!(newdev->features & CLOCK_EVT_FEAT_ONESHOT)) {
if (curdev && (curdev->features & CLOCK_EVT_FEAT_ONESHOT))
return false;
if (tick_oneshot_mode_active())
return false;
}
/*
* Use the higher rated one, but prefer a CPU local device with a lower
* rating than a non-CPU local device
*/
return !curdev ||
newdev->rating > curdev->rating ||
!cpumask_equal(curdev->cpumask, newdev->cpumask);
}
/*
* Check whether the new device is a better fit than curdev. curdev
* can be NULL !
*/
bool tick_check_replacement(struct clock_event_device *curdev,
struct clock_event_device *newdev)
{
if (!tick_check_percpu(curdev, newdev, smp_processor_id()))
return false;
return tick_check_preferred(curdev, newdev);
}
/*
* Check, if the new registered device should be used. Called with
* clockevents_lock held and interrupts disabled.
*/
void tick_check_new_device(struct clock_event_device *newdev)
{
struct clock_event_device *curdev;
struct tick_device *td;
int cpu;
cpu = smp_processor_id();
td = &per_cpu(tick_cpu_device, cpu);
curdev = td->evtdev;
if (!tick_check_replacement(curdev, newdev))
goto out_bc;
if (!try_module_get(newdev->owner))
return;
/*
* Replace the eventually existing device by the new
* device. If the current device is the broadcast device, do
* not give it back to the clockevents layer !
*/
if (tick_is_broadcast_device(curdev)) {
clockevents_shutdown(curdev);
curdev = NULL;
}
clockevents_exchange_device(curdev, newdev);
tick_setup_device(td, newdev, cpu, cpumask_of(cpu));
if (newdev->features & CLOCK_EVT_FEAT_ONESHOT)
tick_oneshot_notify();
return;
out_bc:
/*
* Can the new device be used as a broadcast device ?
*/
tick_install_broadcast_device(newdev, cpu);
}
/**
* tick_broadcast_oneshot_control - Enter/exit broadcast oneshot mode
* @state: The target state (enter/exit)
*
* The system enters/leaves a state, where affected devices might stop
* Returns 0 on success, -EBUSY if the cpu is used to broadcast wakeups.
*
* Called with interrupts disabled, so clockevents_lock is not
* required here because the local clock event device cannot go away
* under us.
*/
int tick_broadcast_oneshot_control(enum tick_broadcast_state state)
{
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
if (!(td->evtdev->features & CLOCK_EVT_FEAT_C3STOP))
return 0;
return __tick_broadcast_oneshot_control(state);
}
EXPORT_SYMBOL_GPL(tick_broadcast_oneshot_control);
#ifdef CONFIG_HOTPLUG_CPU
void tick_assert_timekeeping_handover(void)
{
WARN_ON_ONCE(tick_do_timer_cpu == smp_processor_id());
}
/*
* Stop the tick and transfer the timekeeping job away from a dying cpu.
*/
int tick_cpu_dying(unsigned int dying_cpu)
{
/*
* If the current CPU is the timekeeper, it's the only one that can
* safely hand over its duty. Also all online CPUs are in stop
* machine, guaranteed not to be idle, therefore there is no
* concurrency and it's safe to pick any online successor.
*/
if (tick_do_timer_cpu == dying_cpu)
tick_do_timer_cpu = cpumask_first(cpu_online_mask);
/* Make sure the CPU won't try to retake the timekeeping duty */
tick_sched_timer_dying(dying_cpu);
/* Remove CPU from timer broadcasting */
tick_offline_cpu(dying_cpu);
return 0;
}
/*
* Shutdown an event device on the outgoing CPU:
*
* Called by the dying CPU during teardown, with clockevents_lock held
* and interrupts disabled.
*/
void tick_shutdown(void)
{
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
struct clock_event_device *dev = td->evtdev;
td->mode = TICKDEV_MODE_PERIODIC;
if (dev) {
clockevents_exchange_device(dev, NULL);
dev->event_handler = clockevents_handle_noop;
td->evtdev = NULL;
}
}
#endif
/**
* tick_suspend_local - Suspend the local tick device
*
* Called from the local cpu for freeze with interrupts disabled.
*
* No locks required. Nothing can change the per cpu device.
*/
void tick_suspend_local(void)
{
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
clockevents_shutdown(td->evtdev);
}
/**
* tick_resume_local - Resume the local tick device
*
* Called from the local CPU for unfreeze or XEN resume magic.
*
* No locks required. Nothing can change the per cpu device.
*/
void tick_resume_local(void)
{
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
bool broadcast = tick_resume_check_broadcast();
clockevents_tick_resume(td->evtdev);
if (!broadcast) {
if (td->mode == TICKDEV_MODE_PERIODIC)
tick_setup_periodic(td->evtdev, 0);
else
tick_resume_oneshot();
}
/*
* Ensure that hrtimers are up to date and the clockevents device
* is reprogrammed correctly when high resolution timers are
* enabled.
*/
hrtimers_resume_local();
}
/**
* tick_suspend - Suspend the tick and the broadcast device
*
* Called from syscore_suspend() via timekeeping_suspend with only one
* CPU online and interrupts disabled or from tick_unfreeze() under
* tick_freeze_lock.
*
* No locks required. Nothing can change the per cpu device.
*/
void tick_suspend(void)
{
tick_suspend_local();
tick_suspend_broadcast();
}
/**
* tick_resume - Resume the tick and the broadcast device
*
* Called from syscore_resume() via timekeeping_resume with only one
* CPU online and interrupts disabled.
*
* No locks required. Nothing can change the per cpu device.
*/
void tick_resume(void)
{
tick_resume_broadcast();
tick_resume_local();
}
#ifdef CONFIG_SUSPEND
static DEFINE_RAW_SPINLOCK(tick_freeze_lock);
static DEFINE_WAIT_OVERRIDE_MAP(tick_freeze_map, LD_WAIT_SLEEP);
static unsigned int tick_freeze_depth;
/**
* tick_freeze - Suspend the local tick and (possibly) timekeeping.
*
* Check if this is the last online CPU executing the function and if so,
* suspend timekeeping. Otherwise suspend the local tick.
*
* Call with interrupts disabled. Must be balanced with %tick_unfreeze().
* Interrupts must not be enabled before the subsequent %tick_unfreeze().
*/
void tick_freeze(void)
{
raw_spin_lock(&tick_freeze_lock);
tick_freeze_depth++;
if (tick_freeze_depth == num_online_cpus()) {
trace_suspend_resume(TPS("timekeeping_freeze"),
smp_processor_id(), true);
/*
* All other CPUs have their interrupts disabled and are
* suspended to idle. Other tasks have been frozen so there
* is no scheduling happening. This means that there is no
* concurrency in the system at this point. Therefore it is
* okay to acquire a sleeping lock on PREEMPT_RT, such as a
* spinlock, because the lock cannot be held by other CPUs
* or threads and acquiring it cannot block.
*
* Inform lockdep about the situation.
*/
lock_map_acquire_try(&tick_freeze_map);
system_state = SYSTEM_SUSPEND;
sched_clock_suspend();
timekeeping_suspend();
lock_map_release(&tick_freeze_map);
} else {
tick_suspend_local();
}
raw_spin_unlock(&tick_freeze_lock);
}
/**
* tick_unfreeze - Resume the local tick and (possibly) timekeeping.
*
* Check if this is the first CPU executing the function and if so, resume
* timekeeping. Otherwise resume the local tick.
*
* Call with interrupts disabled. Must be balanced with %tick_freeze().
* Interrupts must not be enabled after the preceding %tick_freeze().
*/
void tick_unfreeze(void)
{
raw_spin_lock(&tick_freeze_lock);
if (tick_freeze_depth == num_online_cpus()) {
/*
* Similar to tick_freeze(). On resumption the first CPU may
* acquire uncontended sleeping locks while other CPUs block on
* tick_freeze_lock.
*/
lock_map_acquire_try(&tick_freeze_map);
timekeeping_resume();
sched_clock_resume();
lock_map_release(&tick_freeze_map);
system_state = SYSTEM_RUNNING;
trace_suspend_resume(TPS("timekeeping_freeze"),
smp_processor_id(), false);
} else {
touch_softlockup_watchdog();
tick_resume_local();
}
tick_freeze_depth--;
raw_spin_unlock(&tick_freeze_lock);
}
#endif /* CONFIG_SUSPEND */
/**
* tick_init - initialize the tick control
*/
void __init tick_init(void)
{
tick_broadcast_init();
tick_nohz_init();
}
+213
View File
@@ -0,0 +1,213 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* tick internal variable and functions used by low/high res code
*/
#include <linux/hrtimer.h>
#include <linux/tick.h>
#include "timekeeping.h"
#include "tick-sched.h"
struct timer_events {
u64 local;
u64 global;
};
#ifdef CONFIG_GENERIC_CLOCKEVENTS
# define TICK_DO_TIMER_NONE -1
# define TICK_DO_TIMER_BOOT -2
DECLARE_PER_CPU(struct tick_device, tick_cpu_device);
extern ktime_t tick_next_period;
extern int tick_do_timer_cpu __read_mostly;
extern void tick_setup_periodic(struct clock_event_device *dev, int broadcast);
extern void tick_handle_periodic(struct clock_event_device *dev);
extern void tick_check_new_device(struct clock_event_device *dev);
extern void tick_offline_cpu(unsigned int cpu);
extern void tick_shutdown(void);
extern void tick_suspend(void);
extern void tick_resume(void);
extern bool tick_check_replacement(struct clock_event_device *curdev,
struct clock_event_device *newdev);
extern void tick_install_replacement(struct clock_event_device *dev);
extern int tick_is_oneshot_available(void);
extern struct tick_device *tick_get_device(int cpu);
extern int clockevents_tick_resume(struct clock_event_device *dev);
/* Check, if the device is functional or a dummy for broadcast */
static inline int tick_device_is_functional(struct clock_event_device *dev)
{
return !(dev->features & CLOCK_EVT_FEAT_DUMMY);
}
static inline enum clock_event_state clockevent_get_state(struct clock_event_device *dev)
{
return dev->state_use_accessors;
}
static inline void clockevent_set_state(struct clock_event_device *dev,
enum clock_event_state state)
{
dev->state_use_accessors = state;
}
extern void clockevents_shutdown(struct clock_event_device *dev);
extern void clockevents_exchange_device(struct clock_event_device *old,
struct clock_event_device *new);
extern void clockevents_switch_state(struct clock_event_device *dev,
enum clock_event_state state);
extern int clockevents_program_event(struct clock_event_device *dev,
ktime_t expires, bool force);
extern void clockevents_handle_noop(struct clock_event_device *dev);
extern int __clockevents_update_freq(struct clock_event_device *dev, u32 freq);
/* Broadcasting support */
# ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
extern int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu);
extern void tick_install_broadcast_device(struct clock_event_device *dev, int cpu);
extern int tick_is_broadcast_device(struct clock_event_device *dev);
extern void tick_suspend_broadcast(void);
extern void tick_resume_broadcast(void);
extern bool tick_resume_check_broadcast(void);
extern void tick_broadcast_init(void);
extern void tick_set_periodic_handler(struct clock_event_device *dev, int broadcast);
extern int tick_broadcast_update_freq(struct clock_event_device *dev, u32 freq);
extern struct tick_device *tick_get_broadcast_device(void);
extern struct cpumask *tick_get_broadcast_mask(void);
extern const struct clock_event_device *tick_get_wakeup_device(int cpu);
# else /* !CONFIG_GENERIC_CLOCKEVENTS_BROADCAST: */
static inline void tick_install_broadcast_device(struct clock_event_device *dev, int cpu) { }
static inline int tick_is_broadcast_device(struct clock_event_device *dev) { return 0; }
static inline int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu) { return 0; }
static inline void tick_do_periodic_broadcast(struct clock_event_device *d) { }
static inline void tick_suspend_broadcast(void) { }
static inline void tick_resume_broadcast(void) { }
static inline bool tick_resume_check_broadcast(void) { return false; }
static inline void tick_broadcast_init(void) { }
static inline int tick_broadcast_update_freq(struct clock_event_device *dev, u32 freq) { return -ENODEV; }
/* Set the periodic handler in non broadcast mode */
static inline void tick_set_periodic_handler(struct clock_event_device *dev, int broadcast)
{
dev->event_handler = tick_handle_periodic;
}
# endif /* !CONFIG_GENERIC_CLOCKEVENTS_BROADCAST */
#else /* !GENERIC_CLOCKEVENTS: */
static inline void tick_suspend(void) { }
static inline void tick_resume(void) { }
#endif /* !GENERIC_CLOCKEVENTS */
/* Oneshot related functions */
#ifdef CONFIG_TICK_ONESHOT
extern void tick_setup_oneshot(struct clock_event_device *newdev,
void (*handler)(struct clock_event_device *),
ktime_t nextevt);
extern int tick_program_event(ktime_t expires, int force);
extern void tick_oneshot_notify(void);
extern int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *));
extern void tick_resume_oneshot(void);
static inline bool tick_oneshot_possible(void) { return true; }
extern int tick_oneshot_mode_active(void);
extern void tick_clock_notify(void);
extern int tick_check_oneshot_change(int allow_nohz);
extern int tick_init_highres(void);
#else /* !CONFIG_TICK_ONESHOT: */
static inline
void tick_setup_oneshot(struct clock_event_device *newdev,
void (*handler)(struct clock_event_device *),
ktime_t nextevt) { BUG(); }
static inline void tick_resume_oneshot(void) { BUG(); }
static inline int tick_program_event(ktime_t expires, int force) { return 0; }
static inline void tick_oneshot_notify(void) { }
static inline bool tick_oneshot_possible(void) { return false; }
static inline int tick_oneshot_mode_active(void) { return 0; }
static inline void tick_clock_notify(void) { }
static inline int tick_check_oneshot_change(int allow_nohz) { return 0; }
#endif /* !CONFIG_TICK_ONESHOT */
/* Functions related to oneshot broadcasting */
#if defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) && defined(CONFIG_TICK_ONESHOT)
extern void tick_broadcast_switch_to_oneshot(void);
extern int tick_broadcast_oneshot_active(void);
extern void tick_check_oneshot_broadcast_this_cpu(void);
bool tick_broadcast_oneshot_available(void);
extern struct cpumask *tick_get_broadcast_oneshot_mask(void);
#else /* !(BROADCAST && ONESHOT): */
static inline void tick_broadcast_switch_to_oneshot(void) { }
static inline int tick_broadcast_oneshot_active(void) { return 0; }
static inline void tick_check_oneshot_broadcast_this_cpu(void) { }
static inline bool tick_broadcast_oneshot_available(void) { return tick_oneshot_possible(); }
#endif /* !(BROADCAST && ONESHOT) */
#if defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) && defined(CONFIG_HOTPLUG_CPU)
extern void tick_broadcast_offline(unsigned int cpu);
#else
static inline void tick_broadcast_offline(unsigned int cpu) { }
#endif
/* NO_HZ_FULL internal */
#ifdef CONFIG_NO_HZ_FULL
extern void tick_nohz_init(void);
# else
static inline void tick_nohz_init(void) { }
#endif
#ifdef CONFIG_NO_HZ_COMMON
extern void timers_update_nohz(void);
extern u64 get_jiffies_update(unsigned long *basej);
# ifdef CONFIG_SMP
extern struct static_key_false timers_migration_enabled;
extern void fetch_next_timer_interrupt_remote(unsigned long basej, u64 basem,
struct timer_events *tevt,
unsigned int cpu);
extern void timer_lock_remote_bases(unsigned int cpu);
extern void timer_unlock_remote_bases(unsigned int cpu);
extern bool timer_base_is_idle(void);
extern void timer_expire_remote(unsigned int cpu);
# endif
#else /* CONFIG_NO_HZ_COMMON */
static inline void timers_update_nohz(void) { }
#endif
DECLARE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases);
extern u64 get_next_timer_interrupt(unsigned long basej, u64 basem);
u64 timer_base_try_to_set_idle(unsigned long basej, u64 basem, bool *idle);
void timer_clear_idle(void);
#define CLOCK_SET_WALL \
(BIT(HRTIMER_BASE_REALTIME) | BIT(HRTIMER_BASE_REALTIME_SOFT) | \
BIT(HRTIMER_BASE_TAI) | BIT(HRTIMER_BASE_TAI_SOFT))
#define CLOCK_SET_BOOT \
(BIT(HRTIMER_BASE_BOOTTIME) | BIT(HRTIMER_BASE_BOOTTIME_SOFT))
void clock_was_set(unsigned int bases);
void clock_was_set_delayed(void);
void hrtimers_resume_local(void);
/* Since jiffies uses a simple TICK_NSEC multiplier
* conversion, the .shift value could be zero. However
* this would make NTP adjustments impossible as they are
* in units of 1/2^.shift. Thus we use JIFFIES_SHIFT to
* shift both the nominator and denominator the same
* amount, and give ntp adjustments in units of 1/2^8
*
* The value 8 is somewhat carefully chosen, as anything
* larger can result in overflows. TICK_NSEC grows as HZ
* shrinks, so values greater than 8 overflow 32bits when
* HZ=100.
*/
#if HZ < 34
#define JIFFIES_SHIFT 6
#elif HZ < 67
#define JIFFIES_SHIFT 7
#else
#define JIFFIES_SHIFT 8
#endif
extern ssize_t sysfs_get_uname(const char *buf, char *dst, size_t cnt);
+37
View File
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Timer tick function for architectures that lack generic clockevents,
* consolidated here from m68k/ia64/parisc/arm.
*/
#include <linux/irq.h>
#include <linux/profile.h>
#include <linux/timekeeper_internal.h>
#include "tick-internal.h"
/**
* legacy_timer_tick() - advances the timekeeping infrastructure
* @ticks: number of ticks, that have elapsed since the last call.
*
* This is used by platforms that have not been converted to
* generic clockevents.
*
* If 'ticks' is zero, the CPU is not handling timekeeping, so
* only perform process accounting and profiling.
*
* Must be called with interrupts disabled.
*/
void legacy_timer_tick(unsigned long ticks)
{
if (ticks) {
raw_spin_lock(&jiffies_lock);
write_seqcount_begin(&jiffies_seq);
do_timer(ticks);
write_seqcount_end(&jiffies_seq);
raw_spin_unlock(&jiffies_lock);
update_wall_time();
}
update_process_times(user_mode(get_irq_regs()));
profile_tick(CPU_PROFILING);
}
+146
View File
@@ -0,0 +1,146 @@
// SPDX-License-Identifier: GPL-2.0
/*
* This file contains functions which manage high resolution tick
* related events.
*
* Copyright(C) 2005-2006, Linutronix GmbH, Thomas Gleixner <tglx@kernel.org>
* Copyright(C) 2005-2007, Red Hat, Inc., Ingo Molnar
* Copyright(C) 2006-2007, Timesys Corp., Thomas Gleixner
*/
#include <linux/cpu.h>
#include <linux/err.h>
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/percpu.h>
#include <linux/profile.h>
#include <linux/sched.h>
#include "tick-internal.h"
/**
* tick_program_event - program the CPU local timer device for the next event
* @expires: the time at which the next timer event should occur
* @force: flag to force reprograming even if the event time hasn't changed
*
* Return: 0 on success, negative error code on failure
*/
int tick_program_event(ktime_t expires, int force)
{
struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
if (unlikely(expires == KTIME_MAX)) {
/*
* We don't need the clock event device any more, stop it.
*/
clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT_STOPPED);
dev->next_event = KTIME_MAX;
return 0;
}
if (unlikely(clockevent_state_oneshot_stopped(dev))) {
/*
* We need the clock event again, configure it in ONESHOT mode
* before using it.
*/
clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT);
}
return clockevents_program_event(dev, expires, force);
}
/**
* tick_resume_oneshot - resume oneshot mode
*/
void tick_resume_oneshot(void)
{
struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT);
clockevents_program_event(dev, ktime_get(), true);
}
/**
* tick_setup_oneshot - setup the event device for oneshot mode (hres or nohz)
* @newdev: Pointer to the clock event device to configure
* @handler: Function to be called when the event device triggers an interrupt
* @next_event: Initial expiry time for the next event (in ktime)
*
* Configures the specified clock event device for onshot mode,
* assigns the given handler as its event callback, and programs
* the device to trigger at the specified next event time.
*/
void tick_setup_oneshot(struct clock_event_device *newdev,
void (*handler)(struct clock_event_device *),
ktime_t next_event)
{
newdev->event_handler = handler;
clockevents_switch_state(newdev, CLOCK_EVT_STATE_ONESHOT);
clockevents_program_event(newdev, next_event, true);
}
/**
* tick_switch_to_oneshot - switch to oneshot mode
* @handler: function to call when an event occurs on the tick device
*
* Return: 0 on success, -EINVAL if the tick device is not present,
* not functional, or does not support oneshot mode.
*/
int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *))
{
struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
struct clock_event_device *dev = td->evtdev;
if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT) ||
!tick_device_is_functional(dev)) {
pr_info("Clockevents: could not switch to one-shot mode:");
if (!dev) {
pr_cont(" no tick device\n");
} else {
if (!tick_device_is_functional(dev))
pr_cont(" %s is not functional.\n", dev->name);
else
pr_cont(" %s does not support one-shot mode.\n",
dev->name);
}
return -EINVAL;
}
td->mode = TICKDEV_MODE_ONESHOT;
dev->event_handler = handler;
clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT);
tick_broadcast_switch_to_oneshot();
return 0;
}
/**
* tick_oneshot_mode_active - check whether the system is in oneshot mode
*
* Return: 1 when either nohz or highres are enabled, otherwise 0.
*/
int tick_oneshot_mode_active(void)
{
unsigned long flags;
int ret;
local_irq_save(flags);
ret = __this_cpu_read(tick_cpu_device.mode) == TICKDEV_MODE_ONESHOT;
local_irq_restore(flags);
return ret;
}
#ifdef CONFIG_HIGH_RES_TIMERS
/**
* tick_init_highres - switch to high resolution mode
*
* Called with interrupts disabled.
*
* Return: 0 on success, -EINVAL if the tick device cannot switch
* to oneshot/high-resolution mode.
*/
int tick_init_highres(void)
{
return tick_switch_to_oneshot(hrtimer_interrupt);
}
#endif
File diff suppressed because it is too large Load Diff
+112
View File
@@ -0,0 +1,112 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _TICK_SCHED_H
#define _TICK_SCHED_H
#include <linux/hrtimer.h>
enum tick_device_mode {
TICKDEV_MODE_PERIODIC,
TICKDEV_MODE_ONESHOT,
};
struct tick_device {
struct clock_event_device *evtdev;
enum tick_device_mode mode;
};
/* The CPU is in the tick idle mode */
#define TS_FLAG_INIDLE BIT(0)
/* The idle tick has been stopped */
#define TS_FLAG_STOPPED BIT(1)
/*
* Indicator that the CPU is actively in the tick idle mode;
* it is reset during irq handling phases.
*/
#define TS_FLAG_IDLE_ACTIVE BIT(2)
/* CPU was the last one doing do_timer before going idle */
#define TS_FLAG_DO_TIMER_LAST BIT(3)
/* NO_HZ is enabled */
#define TS_FLAG_NOHZ BIT(4)
/* High resolution tick mode */
#define TS_FLAG_HIGHRES BIT(5)
/**
* struct tick_sched - sched tick emulation and no idle tick control/stats
*
* @flags: State flags gathering the TS_FLAG_* features
* @got_idle_tick: Tick timer function has run with @inidle set
* @stalled_jiffies: Number of stalled jiffies detected across ticks
* @last_tick_jiffies: Value of jiffies seen on last tick
* @sched_timer: hrtimer to schedule the periodic tick in high
* resolution mode
* @last_tick: Store the last tick expiry time when the tick
* timer is modified for nohz sleeps. This is necessary
* to resume the tick timer operation in the timeline
* when the CPU returns from nohz sleep.
* @next_tick: Next tick to be fired when in dynticks mode.
* @idle_waketime: Time when the idle was interrupted
* @idle_entrytime: Time when the idle call was entered
* @last_jiffies: Base jiffies snapshot when next event was last computed
* @timer_expires_base: Base time clock monotonic for @timer_expires
* @timer_expires: Anticipated timer expiration time (in case sched tick is stopped)
* @next_timer: Expiry time of next expiring timer for debugging purpose only
* @idle_expires: Next tick in idle, for debugging purpose only
* @idle_calls: Total number of idle calls
* @idle_sleeps: Number of idle calls, where the sched tick was stopped
* @tick_dep_mask: Tick dependency mask - is set, if someone needs the tick
* @check_clocks: Notification mechanism about clocksource changes
*/
struct tick_sched {
/* Common flags */
unsigned long flags;
/* Tick handling: jiffies stall check */
unsigned int stalled_jiffies;
unsigned long last_tick_jiffies;
/* Tick handling */
struct hrtimer sched_timer;
ktime_t last_tick;
ktime_t next_tick;
ktime_t idle_waketime;
unsigned int got_idle_tick;
/* Idle entry */
ktime_t idle_entrytime;
/* Tick stop */
unsigned long last_jiffies;
u64 timer_expires_base;
u64 timer_expires;
u64 next_timer;
ktime_t idle_expires;
unsigned long idle_calls;
unsigned long idle_sleeps;
/* Full dynticks handling */
atomic_t tick_dep_mask;
/* Clocksource changes */
unsigned long check_clocks;
};
extern struct tick_sched *tick_get_tick_sched(int cpu);
extern void tick_setup_sched_timer(bool hrtimer);
#if defined CONFIG_TICK_ONESHOT
extern void tick_sched_timer_dying(int cpu);
#else
static inline void tick_sched_timer_dying(int cpu) { }
#endif
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
extern int __tick_broadcast_oneshot_control(enum tick_broadcast_state state);
#else
static inline int
__tick_broadcast_oneshot_control(enum tick_broadcast_state state)
{
return -EBUSY;
}
#endif
#endif
File diff suppressed because it is too large Load Diff
+102
View File
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: LGPL-2.1+
#include <kunit/test.h>
#include <linux/time.h>
/*
* Traditional implementation of leap year evaluation, but note that long
* is a signed type and the tests do cover negative year values. So this
* can't use the is_leap_year() helper from rtc.h.
*/
static bool is_leap(long year)
{
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
/*
* Gets the last day of a month.
*/
static int last_day_of_month(long year, int month)
{
if (month == 2)
return 28 + is_leap(year);
if (month == 4 || month == 6 || month == 9 || month == 11)
return 30;
return 31;
}
/*
* Advances a date by one day.
*/
static void advance_date(long *year, int *month, int *mday, int *yday)
{
if (*mday != last_day_of_month(*year, *month)) {
++*mday;
++*yday;
return;
}
*mday = 1;
if (*month != 12) {
++*month;
++*yday;
return;
}
*month = 1;
*yday = 0;
++*year;
}
/*
* Checks every day in a 160000 years interval centered at 1970-01-01
* against the expected result.
*/
static void time64_to_tm_test_date_range(struct kunit *test)
{
/*
* 80000 years = (80000 / 400) * 400 years
* = (80000 / 400) * 146097 days
* = (80000 / 400) * 146097 * 86400 seconds
*/
time64_t total_secs = ((time64_t) 80000) / 400 * 146097 * 86400;
long year = 1970 - 80000;
int month = 1;
int mdday = 1;
int yday = 0;
struct tm result;
time64_t secs;
s64 days;
for (secs = -total_secs; secs <= total_secs; secs += 86400) {
time64_to_tm(secs, 0, &result);
days = div_s64(secs, 86400);
#define FAIL_MSG "%05ld/%02d/%02d (%2d) : %lld", \
year, month, mdday, yday, days
KUNIT_ASSERT_EQ_MSG(test, year - 1900, result.tm_year, FAIL_MSG);
KUNIT_ASSERT_EQ_MSG(test, month - 1, result.tm_mon, FAIL_MSG);
KUNIT_ASSERT_EQ_MSG(test, mdday, result.tm_mday, FAIL_MSG);
KUNIT_ASSERT_EQ_MSG(test, yday, result.tm_yday, FAIL_MSG);
advance_date(&year, &month, &mdday, &yday);
}
}
static struct kunit_case time_test_cases[] = {
KUNIT_CASE_SLOW(time64_to_tm_test_date_range),
{}
};
static struct kunit_suite time_test_suite = {
.name = "time_test_cases",
.test_cases = time_test_cases,
};
kunit_test_suite(time_test_suite);
MODULE_DESCRIPTION("time unit test suite");
MODULE_LICENSE("GPL");
+117
View File
@@ -0,0 +1,117 @@
/* SPDX-License-Identifier: GPL-2.0 */
scale=0
define gcd(a,b) {
auto t;
while (b) {
t = b;
b = a % b;
a = t;
}
return a;
}
/* Division by reciprocal multiplication. */
define fmul(b,n,d) {
return (2^b*n+d-1)/d;
}
/* Adjustment factor when a ceiling value is used. Use as:
(imul * n) + (fmulxx * n + fadjxx) >> xx) */
define fadj(b,n,d) {
auto v;
d = d/gcd(n,d);
v = 2^b*(d-1)/d;
return v;
}
/* Compute the appropriate mul/adj values as well as a shift count,
which brings the mul value into the range 2^b-1 <= x < 2^b. Such
a shift value will be correct in the signed integer range and off
by at most one in the upper half of the unsigned range. */
define fmuls(b,n,d) {
auto s, m;
for (s = 0; 1; s++) {
m = fmul(s,n,d);
if (m >= 2^(b-1))
return s;
}
return 0;
}
define timeconst(hz) {
print "/* Automatically generated by kernel/time/timeconst.bc */\n"
print "/* Time conversion constants for HZ == ", hz, " */\n"
print "\n"
print "#ifndef KERNEL_TIMECONST_H\n"
print "#define KERNEL_TIMECONST_H\n\n"
print "#include <linux/param.h>\n"
print "#include <linux/types.h>\n\n"
print "#if HZ != ", hz, "\n"
print "#error \qinclude/generated/timeconst.h has the wrong HZ value!\q\n"
print "#endif\n\n"
if (hz < 2) {
print "#error Totally bogus HZ value!\n"
} else {
s=fmuls(32,1000,hz)
obase=16
print "#define HZ_TO_MSEC_MUL32\tU64_C(0x", fmul(s,1000,hz), ")\n"
print "#define HZ_TO_MSEC_ADJ32\tU64_C(0x", fadj(s,1000,hz), ")\n"
obase=10
print "#define HZ_TO_MSEC_SHR32\t", s, "\n"
s=fmuls(32,hz,1000)
obase=16
print "#define MSEC_TO_HZ_MUL32\tU64_C(0x", fmul(s,hz,1000), ")\n"
print "#define MSEC_TO_HZ_ADJ32\tU64_C(0x", fadj(s,hz,1000), ")\n"
obase=10
print "#define MSEC_TO_HZ_SHR32\t", s, "\n"
obase=10
cd=gcd(hz,1000)
print "#define HZ_TO_MSEC_NUM\t\t", 1000/cd, "\n"
print "#define HZ_TO_MSEC_DEN\t\t", hz/cd, "\n"
print "#define MSEC_TO_HZ_NUM\t\t", hz/cd, "\n"
print "#define MSEC_TO_HZ_DEN\t\t", 1000/cd, "\n"
print "\n"
s=fmuls(32,1000000,hz)
obase=16
print "#define HZ_TO_USEC_MUL32\tU64_C(0x", fmul(s,1000000,hz), ")\n"
print "#define HZ_TO_USEC_ADJ32\tU64_C(0x", fadj(s,1000000,hz), ")\n"
obase=10
print "#define HZ_TO_USEC_SHR32\t", s, "\n"
s=fmuls(32,hz,1000000)
obase=16
print "#define USEC_TO_HZ_MUL32\tU64_C(0x", fmul(s,hz,1000000), ")\n"
print "#define USEC_TO_HZ_ADJ32\tU64_C(0x", fadj(s,hz,1000000), ")\n"
obase=10
print "#define USEC_TO_HZ_SHR32\t", s, "\n"
obase=10
cd=gcd(hz,1000000)
print "#define HZ_TO_USEC_NUM\t\t", 1000000/cd, "\n"
print "#define HZ_TO_USEC_DEN\t\t", hz/cd, "\n"
print "#define USEC_TO_HZ_NUM\t\t", hz/cd, "\n"
print "#define USEC_TO_HZ_DEN\t\t", 1000000/cd, "\n"
cd=gcd(hz,1000000000)
print "#define HZ_TO_NSEC_NUM\t\t", 1000000000/cd, "\n"
print "#define HZ_TO_NSEC_DEN\t\t", hz/cd, "\n"
print "#define NSEC_TO_HZ_NUM\t\t", hz/cd, "\n"
print "#define NSEC_TO_HZ_DEN\t\t", 1000000000/cd, "\n"
print "\n"
print "#endif /* KERNEL_TIMECONST_H */\n"
}
halt
}
hz = read();
timeconst(hz)
+141
View File
@@ -0,0 +1,141 @@
// SPDX-License-Identifier: LGPL-2.0+
/*
* Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
* This file is part of the GNU C Library.
* Contributed by Paul Eggert (eggert@twinsun.com).
*
* The GNU C Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* The GNU C Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with the GNU C Library; see the file COPYING.LIB. If not,
* write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Converts the calendar time to broken-down time representation
*
* 2009-7-14:
* Moved from glibc-2.6 to kernel by Zhaolei<zhaolei@cn.fujitsu.com>
* 2021-06-02:
* Reimplemented by Cassio Neri <cassio.neri@gmail.com>
*/
#include <linux/time.h>
#include <linux/module.h>
#include <linux/kernel.h>
#define SECS_PER_HOUR (60 * 60)
#define SECS_PER_DAY (SECS_PER_HOUR * 24)
/**
* time64_to_tm - converts the calendar time to local broken-down time
*
* @totalsecs: the number of seconds elapsed since 00:00:00 on January 1, 1970,
* Coordinated Universal Time (UTC).
* @offset: offset seconds adding to totalsecs.
* @result: pointer to struct tm variable to receive broken-down time
*/
void time64_to_tm(time64_t totalsecs, int offset, struct tm *result)
{
u32 u32tmp, day_of_century, year_of_century, day_of_year, month, day;
u64 u64tmp, udays, century, year;
bool is_Jan_or_Feb, is_leap_year;
long days, rem;
int remainder;
days = div_s64_rem(totalsecs, SECS_PER_DAY, &remainder);
rem = remainder;
rem += offset;
while (rem < 0) {
rem += SECS_PER_DAY;
--days;
}
while (rem >= SECS_PER_DAY) {
rem -= SECS_PER_DAY;
++days;
}
result->tm_hour = rem / SECS_PER_HOUR;
rem %= SECS_PER_HOUR;
result->tm_min = rem / 60;
result->tm_sec = rem % 60;
/* January 1, 1970 was a Thursday. */
result->tm_wday = (4 + days) % 7;
if (result->tm_wday < 0)
result->tm_wday += 7;
/*
* The following algorithm is, basically, Proposition 6.3 of Neri
* and Schneider [1]. In a few words: it works on the computational
* (fictitious) calendar where the year starts in March, month = 2
* (*), and finishes in February, month = 13. This calendar is
* mathematically convenient because the day of the year does not
* depend on whether the year is leap or not. For instance:
*
* March 1st 0-th day of the year;
* ...
* April 1st 31-st day of the year;
* ...
* January 1st 306-th day of the year; (Important!)
* ...
* February 28th 364-th day of the year;
* February 29th 365-th day of the year (if it exists).
*
* After having worked out the date in the computational calendar
* (using just arithmetics) it's easy to convert it to the
* corresponding date in the Gregorian calendar.
*
* [1] "Euclidean Affine Functions and Applications to Calendar
* Algorithms". https://arxiv.org/abs/2102.06959
*
* (*) The numbering of months follows tm more closely and thus,
* is slightly different from [1].
*/
udays = ((u64) days) + 2305843009213814918ULL;
u64tmp = 4 * udays + 3;
century = div64_u64_rem(u64tmp, 146097, &u64tmp);
day_of_century = (u32) (u64tmp / 4);
u32tmp = 4 * day_of_century + 3;
u64tmp = 2939745ULL * u32tmp;
year_of_century = upper_32_bits(u64tmp);
day_of_year = lower_32_bits(u64tmp) / 2939745 / 4;
year = 100 * century + year_of_century;
is_leap_year = year_of_century ? !(year_of_century % 4) : !(century % 4);
u32tmp = 2141 * day_of_year + 132377;
month = u32tmp >> 16;
day = ((u16) u32tmp) / 2141;
/*
* Recall that January 1st is the 306-th day of the year in the
* computational (not Gregorian) calendar.
*/
is_Jan_or_Feb = day_of_year >= 306;
/* Convert to the Gregorian calendar and adjust to Unix time. */
year = year + is_Jan_or_Feb - 6313183731940000ULL;
month = is_Jan_or_Feb ? month - 12 : month;
day = day + 1;
day_of_year += is_Jan_or_Feb ? -306 : 31 + 28 + is_leap_year;
/* Convert to tm's format. */
result->tm_year = (long) (year - 1900);
result->tm_mon = (int) month;
result->tm_mday = (int) day;
result->tm_yday = (int) day_of_year;
}
EXPORT_SYMBOL(time64_to_tm);
+64
View File
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Based on clocksource code. See commit 74d23cc704d1
*/
#include <linux/export.h>
#include <linux/timecounter.h>
void timecounter_init(struct timecounter *tc,
struct cyclecounter *cc,
u64 start_tstamp)
{
tc->cc = cc;
tc->cycle_last = cc->read(cc);
tc->nsec = start_tstamp;
tc->mask = (1ULL << cc->shift) - 1;
tc->frac = 0;
}
EXPORT_SYMBOL_GPL(timecounter_init);
/**
* timecounter_read_delta - get nanoseconds since last call of this function
* @tc: Pointer to time counter
*
* When the underlying cycle counter runs over, this will be handled
* correctly as long as it does not run over more than once between
* calls.
*
* The first call to this function for a new time counter initializes
* the time tracking and returns an undefined result.
*/
static u64 timecounter_read_delta(struct timecounter *tc)
{
u64 cycle_now, cycle_delta;
u64 ns_offset;
/* read cycle counter: */
cycle_now = tc->cc->read(tc->cc);
/* calculate the delta since the last timecounter_read_delta(): */
cycle_delta = (cycle_now - tc->cycle_last) & tc->cc->mask;
/* convert to nanoseconds: */
ns_offset = cyclecounter_cyc2ns(tc->cc, cycle_delta,
tc->mask, &tc->frac);
/* update time stamp of timecounter_read_delta() call: */
tc->cycle_last = cycle_now;
return ns_offset;
}
u64 timecounter_read(struct timecounter *tc)
{
u64 nsec;
/* increment time by nanoseconds since last call */
nsec = timecounter_read_delta(tc);
nsec += tc->nsec;
tc->nsec = nsec;
return nsec;
}
EXPORT_SYMBOL_GPL(timecounter_read);
File diff suppressed because it is too large Load Diff
+36
View File
@@ -0,0 +1,36 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _KERNEL_TIME_TIMEKEEPING_H
#define _KERNEL_TIME_TIMEKEEPING_H
/*
* Internal interfaces for kernel/time/
*/
extern ktime_t ktime_get_update_offsets_now(unsigned int *cwsseq,
ktime_t *offs_real,
ktime_t *offs_boot,
ktime_t *offs_tai);
bool ktime_expiry_to_cycles(enum clocksource_ids id, ktime_t expires_ns, u64 *cycles);
extern int timekeeping_valid_for_hres(void);
extern u64 timekeeping_max_deferment(void);
extern void timekeeping_warp_clock(void);
extern int timekeeping_suspend(void);
extern void timekeeping_resume(void);
#ifdef CONFIG_GENERIC_SCHED_CLOCK
extern int sched_clock_suspend(void);
extern void sched_clock_resume(void);
#else
static inline int sched_clock_suspend(void) { return 0; }
static inline void sched_clock_resume(void) { }
#endif
extern void update_process_times(int user);
extern void do_timer(unsigned long ticks);
extern void update_wall_time(void);
extern raw_spinlock_t jiffies_lock;
extern seqcount_raw_spinlock_t jiffies_seq;
#define CS_NAME_LEN 32
#endif
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* debugfs file to track time spent in suspend
*
* Copyright (c) 2011, Google, Inc.
*/
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/seq_file.h>
#include <linux/suspend.h>
#include <linux/time.h>
#include "timekeeping_internal.h"
#define NUM_BINS 32
/* Incremented every time mg_floor is updated */
DEFINE_PER_CPU(unsigned long, timekeeping_mg_floor_swaps);
static unsigned int sleep_time_bin[NUM_BINS] = {0};
static int tk_debug_sleep_time_show(struct seq_file *s, void *data)
{
unsigned int bin;
seq_puts(s, " time (secs) count\n");
seq_puts(s, "------------------------------\n");
for (bin = 0; bin < 32; bin++) {
if (sleep_time_bin[bin] == 0)
continue;
seq_printf(s, "%10u - %-10u %4u\n",
bin ? 1 << (bin - 1) : 0, 1 << bin,
sleep_time_bin[bin]);
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(tk_debug_sleep_time);
static int __init tk_debug_sleep_time_init(void)
{
debugfs_create_file("sleep_time", 0444, NULL, NULL,
&tk_debug_sleep_time_fops);
return 0;
}
late_initcall(tk_debug_sleep_time_init);
void tk_debug_account_sleep_time(const struct timespec64 *t)
{
/* Cap bin index so we don't overflow the array */
int bin = min(fls(t->tv_sec), NUM_BINS-1);
sleep_time_bin[bin]++;
pm_deferred_pr_dbg("Timekeeping suspended for %lld.%03lu seconds\n",
(s64)t->tv_sec, t->tv_nsec / NSEC_PER_MSEC);
}
unsigned long timekeeping_get_mg_floor_swaps(void)
{
unsigned long sum = 0;
int cpu;
for_each_possible_cpu(cpu)
sum += data_race(per_cpu(timekeeping_mg_floor_swaps, cpu));
return sum;
}
@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _TIMEKEEPING_INTERNAL_H
#define _TIMEKEEPING_INTERNAL_H
#include <linux/clocksource.h>
#include <linux/spinlock.h>
#include <linux/time.h>
/*
* timekeeping debug functions
*/
#ifdef CONFIG_DEBUG_FS
DECLARE_PER_CPU(unsigned long, timekeeping_mg_floor_swaps);
static inline void timekeeping_inc_mg_floor_swaps(void)
{
this_cpu_inc(timekeeping_mg_floor_swaps);
}
extern void tk_debug_account_sleep_time(const struct timespec64 *t);
#else
#define tk_debug_account_sleep_time(x)
static inline void timekeeping_inc_mg_floor_swaps(void)
{
}
#endif
static inline u64 clocksource_delta(u64 now, u64 last, u64 mask, u64 max_delta)
{
u64 ret = (now - last) & mask;
/*
* Prevent time going backwards by checking the result against
* @max_delta. If greater, return 0.
*/
return ret > max_delta ? 0 : ret;
}
/* Semi public for serialization of non timekeeper VDSO updates. */
unsigned long timekeeper_lock_irqsave(void);
void timekeeper_unlock_irqrestore(unsigned long flags);
/* NTP specific interface to access the current seconds value */
long ktime_get_ntp_seconds(unsigned int id);
#endif /* _TIMEKEEPING_INTERNAL_H */
File diff suppressed because it is too large Load Diff
+356
View File
@@ -0,0 +1,356 @@
// SPDX-License-Identifier: GPL-2.0
/*
* List pending timers
*
* Copyright(C) 2006, Red Hat, Inc., Ingo Molnar
*/
#include <linux/proc_fs.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/kallsyms.h>
#include <linux/nmi.h>
#include <linux/uaccess.h>
#include "tick-internal.h"
struct timer_list_iter {
int cpu;
bool second_pass;
u64 now;
};
/*
* This allows printing both to /proc/timer_list and
* to the console (on SysRq-Q):
*/
__printf(2, 3)
static void SEQ_printf(struct seq_file *m, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (m)
seq_vprintf(m, fmt, args);
else
vprintk(fmt, args);
va_end(args);
}
static void
print_timer(struct seq_file *m, struct hrtimer *taddr, struct hrtimer *timer,
int idx, u64 now)
{
SEQ_printf(m, " #%d: <%p>, %ps", idx, taddr, ACCESS_PRIVATE(timer, function));
SEQ_printf(m, ", S:%02x", timer->is_queued);
SEQ_printf(m, "\n");
SEQ_printf(m, " # expires at %Lu-%Lu nsecs [in %Ld to %Ld nsecs]\n",
(unsigned long long)ktime_to_ns(hrtimer_get_softexpires(timer)),
(unsigned long long)ktime_to_ns(hrtimer_get_expires(timer)),
(long long)(ktime_to_ns(hrtimer_get_softexpires(timer)) - now),
(long long)(ktime_to_ns(hrtimer_get_expires(timer)) - now));
}
static void print_active_timers(struct seq_file *m, struct hrtimer_clock_base *base, u64 now)
{
struct timerqueue_linked_node *curr;
struct hrtimer *timer, tmp;
unsigned long next = 0, i;
unsigned long flags;
next_one:
i = 0;
touch_nmi_watchdog();
raw_spin_lock_irqsave(&base->cpu_base->lock, flags);
curr = timerqueue_linked_first(&base->active);
/*
* Crude but we have to do this O(N*N) thing, because
* we have to unlock the base when printing:
*/
while (curr && i < next) {
curr = timerqueue_linked_next(curr);
i++;
}
if (curr) {
timer = container_of(curr, struct hrtimer, node);
tmp = *timer;
raw_spin_unlock_irqrestore(&base->cpu_base->lock, flags);
print_timer(m, timer, &tmp, i, now);
next++;
goto next_one;
}
raw_spin_unlock_irqrestore(&base->cpu_base->lock, flags);
}
static void
print_base(struct seq_file *m, struct hrtimer_clock_base *base, u64 now)
{
SEQ_printf(m, " .base: %p\n", base);
SEQ_printf(m, " .index: %d\n", base->index);
SEQ_printf(m, " .resolution: %u nsecs\n", hrtimer_resolution);
#ifdef CONFIG_HIGH_RES_TIMERS
SEQ_printf(m, " .offset: %Ld nsecs\n",
(long long) base->offset);
#endif
SEQ_printf(m, "active timers:\n");
print_active_timers(m, base, now + ktime_to_ns(base->offset));
}
static void print_cpu(struct seq_file *m, int cpu, u64 now)
{
struct hrtimer_cpu_base *cpu_base = &per_cpu(hrtimer_bases, cpu);
int i;
SEQ_printf(m, "cpu: %d\n", cpu);
for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) {
SEQ_printf(m, " clock %d:\n", i);
print_base(m, cpu_base->clock_base + i, now);
}
#define P(x) \
SEQ_printf(m, " .%-15s: %Lu\n", #x, \
(unsigned long long)(cpu_base->x))
#define P_ns(x) \
SEQ_printf(m, " .%-15s: %Lu nsecs\n", #x, \
(unsigned long long)(ktime_to_ns(cpu_base->x)))
#ifdef CONFIG_HIGH_RES_TIMERS
P_ns(expires_next);
P(hres_active);
P(nr_events);
P(nr_retries);
P(nr_hangs);
P(max_hang_time);
#endif
#undef P
#undef P_ns
#ifdef CONFIG_TICK_ONESHOT
# define P(x) \
SEQ_printf(m, " .%-15s: %Lu\n", #x, \
(unsigned long long)(ts->x))
# define P_ns(x) \
SEQ_printf(m, " .%-15s: %Lu nsecs\n", #x, \
(unsigned long long)(ktime_to_ns(ts->x)))
# define P_flag(x, f) \
SEQ_printf(m, " .%-15s: %d\n", #x, !!(ts->flags & (f)))
{
struct tick_sched *ts = tick_get_tick_sched(cpu);
P_flag(nohz, TS_FLAG_NOHZ);
P_flag(highres, TS_FLAG_HIGHRES);
P_ns(last_tick);
P_flag(tick_stopped, TS_FLAG_STOPPED);
P(idle_calls);
P(idle_sleeps);
P_ns(idle_entrytime);
P_ns(idle_waketime);
P(last_jiffies);
P(next_timer);
P_ns(idle_expires);
SEQ_printf(m, "jiffies: %Lu\n",
(unsigned long long)jiffies);
}
#endif
#undef P
#undef P_ns
SEQ_printf(m, "\n");
}
#ifdef CONFIG_GENERIC_CLOCKEVENTS
static void
print_tickdevice(struct seq_file *m, struct tick_device *td, int cpu)
{
struct clock_event_device *dev = td->evtdev;
touch_nmi_watchdog();
SEQ_printf(m, "Tick Device: mode: %d\n", td->mode);
if (cpu < 0)
SEQ_printf(m, "Broadcast device\n");
else
SEQ_printf(m, "Per CPU device: %d\n", cpu);
SEQ_printf(m, "Clock Event Device: ");
if (!dev) {
SEQ_printf(m, "<NULL>\n");
return;
}
SEQ_printf(m, "%s\n", dev->name);
SEQ_printf(m, " max_delta_ns: %llu\n",
(unsigned long long) dev->max_delta_ns);
SEQ_printf(m, " min_delta_ns: %llu\n",
(unsigned long long) dev->min_delta_ns);
SEQ_printf(m, " mult: %u\n", dev->mult);
SEQ_printf(m, " shift: %u\n", dev->shift);
SEQ_printf(m, " mode: %d\n", clockevent_get_state(dev));
SEQ_printf(m, " next_event: %Ld nsecs\n",
(unsigned long long) ktime_to_ns(dev->next_event));
SEQ_printf(m, " set_next_event: %ps\n", dev->set_next_event);
if (dev->set_state_shutdown)
SEQ_printf(m, " shutdown: %ps\n",
dev->set_state_shutdown);
if (dev->set_state_periodic)
SEQ_printf(m, " periodic: %ps\n",
dev->set_state_periodic);
if (dev->set_state_oneshot)
SEQ_printf(m, " oneshot: %ps\n",
dev->set_state_oneshot);
if (dev->set_state_oneshot_stopped)
SEQ_printf(m, " oneshot stopped: %ps\n",
dev->set_state_oneshot_stopped);
if (dev->tick_resume)
SEQ_printf(m, " resume: %ps\n",
dev->tick_resume);
SEQ_printf(m, " event_handler: %ps\n", dev->event_handler);
SEQ_printf(m, "\n");
SEQ_printf(m, " retries: %lu\n", dev->retries);
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
if (cpu >= 0) {
const struct clock_event_device *wd = tick_get_wakeup_device(cpu);
SEQ_printf(m, "Wakeup Device: %s\n", wd ? wd->name : "<NULL>");
}
#endif
SEQ_printf(m, "\n");
}
static void timer_list_show_tickdevices_header(struct seq_file *m)
{
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
print_tickdevice(m, tick_get_broadcast_device(), -1);
SEQ_printf(m, "tick_broadcast_mask: %*pb\n",
cpumask_pr_args(tick_get_broadcast_mask()));
#ifdef CONFIG_TICK_ONESHOT
SEQ_printf(m, "tick_broadcast_oneshot_mask: %*pb\n",
cpumask_pr_args(tick_get_broadcast_oneshot_mask()));
#endif
SEQ_printf(m, "\n");
#endif
}
#endif
static inline void timer_list_header(struct seq_file *m, u64 now)
{
SEQ_printf(m, "Timer List Version: v0.11\n");
SEQ_printf(m, "HRTIMER_MAX_CLOCK_BASES: %d\n", HRTIMER_MAX_CLOCK_BASES);
SEQ_printf(m, "now at %Ld nsecs\n", (unsigned long long)now);
SEQ_printf(m, "\n");
}
void sysrq_timer_list_show(void)
{
u64 now = ktime_to_ns(ktime_get());
int cpu;
timer_list_header(NULL, now);
for_each_online_cpu(cpu)
print_cpu(NULL, cpu, now);
#ifdef CONFIG_GENERIC_CLOCKEVENTS
timer_list_show_tickdevices_header(NULL);
for_each_online_cpu(cpu)
print_tickdevice(NULL, tick_get_device(cpu), cpu);
#endif
return;
}
#ifdef CONFIG_PROC_FS
static int timer_list_show(struct seq_file *m, void *v)
{
struct timer_list_iter *iter = v;
if (iter->cpu == -1 && !iter->second_pass)
timer_list_header(m, iter->now);
else if (!iter->second_pass)
print_cpu(m, iter->cpu, iter->now);
#ifdef CONFIG_GENERIC_CLOCKEVENTS
else if (iter->cpu == -1 && iter->second_pass)
timer_list_show_tickdevices_header(m);
else
print_tickdevice(m, tick_get_device(iter->cpu), iter->cpu);
#endif
return 0;
}
static void *move_iter(struct timer_list_iter *iter, loff_t offset)
{
for (; offset; offset--) {
iter->cpu = cpumask_next(iter->cpu, cpu_online_mask);
if (iter->cpu >= nr_cpu_ids) {
#ifdef CONFIG_GENERIC_CLOCKEVENTS
if (!iter->second_pass) {
iter->cpu = -1;
iter->second_pass = true;
} else
return NULL;
#else
return NULL;
#endif
}
}
return iter;
}
static void *timer_list_start(struct seq_file *file, loff_t *offset)
{
struct timer_list_iter *iter = file->private;
if (!*offset)
iter->now = ktime_to_ns(ktime_get());
iter->cpu = -1;
iter->second_pass = false;
return move_iter(iter, *offset);
}
static void *timer_list_next(struct seq_file *file, void *v, loff_t *offset)
{
struct timer_list_iter *iter = file->private;
++*offset;
return move_iter(iter, 1);
}
static void timer_list_stop(struct seq_file *seq, void *v)
{
}
static const struct seq_operations timer_list_sops = {
.start = timer_list_start,
.next = timer_list_next,
.stop = timer_list_stop,
.show = timer_list_show,
};
static int __init init_timer_list_procfs(void)
{
struct proc_dir_entry *pe;
pe = proc_create_seq_private("timer_list", 0400, NULL, &timer_list_sops,
sizeof(struct timer_list_iter), NULL);
if (!pe)
return -ENOMEM;
return 0;
}
__initcall(init_timer_list_procfs);
#endif
File diff suppressed because it is too large Load Diff
+166
View File
@@ -0,0 +1,166 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _KERNEL_TIME_MIGRATION_H
#define _KERNEL_TIME_MIGRATION_H
/* Per group capacity. Must be a power of 2! */
#define TMIGR_CHILDREN_PER_GROUP 8
/**
* struct tmigr_hierarchy - a hierarchy associated to a given CPU capacity.
* Homogeneous systems have only one hierarchy.
* Heterogenous have one hierarchy per CPU capacity.
* @cpumask: CPUs belonging to this hierarchy
* @root: The current root of the hierarchy
* @capacity: CPU capacity associated to this hierarchy
* @node: Node in the global hierarchy list
* @level_list: Per level lists of tmigr groups
*/
struct tmigr_hierarchy {
struct cpumask *cpumask;
struct tmigr_group *root;
unsigned long capacity;
struct list_head node;
struct list_head level_list[];
};
/**
* struct tmigr_event - a timer event associated to a CPU
* @nextevt: The node to enqueue an event in the parent group queue
* @cpu: The CPU to which this event belongs
* @ignore: Hint whether the event could be ignored; it is set when
* CPU or group is active;
*/
struct tmigr_event {
struct timerqueue_node nextevt;
unsigned int cpu;
bool ignore;
};
/**
* struct tmigr_group - timer migration hierarchy group
* @lock: Lock protecting the event information and group hierarchy
* information during setup
* @parent: Pointer to the parent group. Pointer is updated when a
* new hierarchy level is added because of a CPU coming
* online the first time. Once it is set, the pointer will
* not be removed or updated. When accessing parent pointer
* lock less to decide whether to abort a propagation or
* not, it is not a problem. The worst outcome is an
* unnecessary/early CPU wake up. But do not access parent
* pointer several times in the same 'action' (like
* activation, deactivation, check for remote expiry,...)
* without holding the lock as it is not ensured that value
* will not change.
* @groupevt: Next event of the group which is only used when the
* group is !active. The group event is then queued into
* the parent timer queue.
* Ignore bit of @groupevt is set when the group is active.
* @next_expiry: Base monotonic expiry time of the next event of the
* group; It is used for the racy lockless check whether a
* remote expiry is required; it is always reliable
* @events: Timer queue for child events queued in the group
* @migr_state: State of the group (see union tmigr_state)
* @level: Hierarchy level of the group; Required during setup
* @numa_node: Required for setup only to make sure CPU and low level
* group information is NUMA local. It is set to NUMA node
* as long as the group level is per NUMA node (level <
* tmigr_crossnode_level); otherwise it is set to
* NUMA_NO_NODE
* @num_children: Counter of group children to make sure the group is only
* filled with TMIGR_CHILDREN_PER_GROUP; Required for setup
* only
* @groupmask: mask of the group in the parent group; is set during
* setup and will never change; can be read lockless
* @list: List head that is added to the per level
* tmigr_level_list; is required during setup when a
* new group needs to be connected to the existing
* hierarchy groups
*/
struct tmigr_group {
raw_spinlock_t lock;
struct tmigr_group *parent;
struct tmigr_event groupevt;
u64 next_expiry;
struct timerqueue_head events;
atomic_t migr_state;
unsigned int level;
int numa_node;
unsigned int num_children;
u8 groupmask;
struct list_head list;
};
/**
* struct tmigr_cpu - timer migration per CPU group
* @lock: Lock protecting the tmigr_cpu group information
* @available: Indicates whether the CPU is available for handling
* global timers. In the deactivate path it is required to
* know whether the migrator in the top level group is to
* be set offline, while a timer is pending. Then another
* available CPU needs to be notified to take over the
* migrator role. Furthermore the information is required
* in the CPU hotplug path as the CPU is able to go idle
* before the timer migration hierarchy hotplug callback is
* reached. During this phase, the CPU has to handle the
* global timers on its own and must not act as a migrator.
* @idle: Indicates whether the CPU is idle in the timer migration
* hierarchy
* @remote: Is set when timers of the CPU are expired remotely
* @tmgroup: Pointer to the parent group
* @groupmask: mask of tmigr_cpu in the parent group
* @wakeup: Stores the first timer when the timer migration
* hierarchy is completely idle and remote expiry was done;
* is returned to timer code in the idle path and is only
* used in idle path.
* @cpuevt: CPU event which could be enqueued into the parent group
*/
struct tmigr_cpu {
raw_spinlock_t lock;
bool available;
bool idle;
bool remote;
struct tmigr_group *tmgroup;
u8 groupmask;
u64 wakeup;
struct tmigr_event cpuevt;
};
/**
* union tmigr_state - state of tmigr_group
* @state: Combined version of the state - only used for atomic
* read/cmpxchg function
* &anon struct: Split version of the state - only use the struct members to
* update information to stay independent of endianness
* @active: Contains each mask bit of the active children
* @migrator: Contains mask of the child which is migrator
* @seq: Sequence counter needs to be increased when an update
* to the tmigr_state is done. It prevents a race when
* updates in the child groups are propagated in changed
* order. Detailed information about the scenario is
* given in the documentation at the begin of
* timer_migration.c.
*/
union tmigr_state {
u32 state;
struct {
u8 active;
u8 migrator;
u16 seq;
} __packed;
};
#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
extern void tmigr_handle_remote(void);
extern bool tmigr_requires_handle_remote(void);
extern void tmigr_cpu_activate(void);
extern u64 tmigr_cpu_deactivate(u64 nextevt);
extern u64 tmigr_cpu_new_timer(u64 nextevt);
extern u64 tmigr_quick_check(u64 nextevt);
#else
static inline void tmigr_handle_remote(void) { }
static inline bool tmigr_requires_handle_remote(void) { return false; }
static inline void tmigr_cpu_activate(void) { }
#endif
#endif
+215
View File
@@ -0,0 +1,215 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 ARM Ltd.
*
* Generic implementation of update_vsyscall and update_vsyscall_tz.
*
* Based on the x86 specific implementation.
*/
#include <linux/hrtimer.h>
#include <linux/timekeeper_internal.h>
#include <vdso/datapage.h>
#include <vdso/helpers.h>
#include <vdso/vsyscall.h>
#include "timekeeping_internal.h"
static inline void fill_clock_configuration(struct vdso_clock *vc, const struct tk_read_base *base)
{
vc->cycle_last = base->cycle_last;
#ifdef CONFIG_GENERIC_VDSO_OVERFLOW_PROTECT
vc->max_cycles = base->clock->max_cycles;
#endif
vc->mask = base->mask;
vc->mult = base->mult;
vc->shift = base->shift;
}
static inline void update_vdso_time_data(struct vdso_time_data *vdata, struct timekeeper *tk)
{
struct vdso_clock *vc = vdata->clock_data;
struct vdso_timestamp *vdso_ts;
u64 nsec, sec;
fill_clock_configuration(&vc[CS_HRES_COARSE], &tk->tkr_mono);
fill_clock_configuration(&vc[CS_RAW], &tk->tkr_raw);
/* CLOCK_MONOTONIC */
vdso_ts = &vc[CS_HRES_COARSE].basetime[CLOCK_MONOTONIC];
vdso_ts->sec = tk->xtime_sec + tk->wall_to_monotonic.tv_sec;
nsec = tk->tkr_mono.xtime_nsec;
nsec += ((u64)tk->wall_to_monotonic.tv_nsec << tk->tkr_mono.shift);
while (nsec >= (((u64)NSEC_PER_SEC) << tk->tkr_mono.shift)) {
nsec -= (((u64)NSEC_PER_SEC) << tk->tkr_mono.shift);
vdso_ts->sec++;
}
vdso_ts->nsec = nsec;
/* Copy MONOTONIC time for BOOTTIME */
sec = vdso_ts->sec;
/* Add the boot offset */
sec += tk->monotonic_to_boot.tv_sec;
nsec += (u64)tk->monotonic_to_boot.tv_nsec << tk->tkr_mono.shift;
/* CLOCK_BOOTTIME */
vdso_ts = &vc[CS_HRES_COARSE].basetime[CLOCK_BOOTTIME];
vdso_ts->sec = sec;
while (nsec >= (((u64)NSEC_PER_SEC) << tk->tkr_mono.shift)) {
nsec -= (((u64)NSEC_PER_SEC) << tk->tkr_mono.shift);
vdso_ts->sec++;
}
vdso_ts->nsec = nsec;
/* CLOCK_MONOTONIC_RAW */
vdso_ts = &vc[CS_RAW].basetime[CLOCK_MONOTONIC_RAW];
vdso_ts->sec = tk->raw_sec;
vdso_ts->nsec = tk->tkr_raw.xtime_nsec;
/* CLOCK_TAI */
vdso_ts = &vc[CS_HRES_COARSE].basetime[CLOCK_TAI];
vdso_ts->sec = tk->xtime_sec + (s64)tk->tai_offset;
vdso_ts->nsec = tk->tkr_mono.xtime_nsec;
}
void update_vsyscall(struct timekeeper *tk)
{
struct vdso_time_data *vdata = vdso_k_time_data;
struct vdso_clock *vc = vdata->clock_data;
struct vdso_timestamp *vdso_ts;
s32 clock_mode;
u64 nsec;
/* copy vsyscall data */
vdso_write_begin(vdata);
clock_mode = tk->tkr_mono.clock->vdso_clock_mode;
vc[CS_HRES_COARSE].clock_mode = clock_mode;
vc[CS_RAW].clock_mode = clock_mode;
/* CLOCK_REALTIME also required for time() */
vdso_ts = &vc[CS_HRES_COARSE].basetime[CLOCK_REALTIME];
vdso_ts->sec = tk->xtime_sec;
vdso_ts->nsec = tk->tkr_mono.xtime_nsec;
/* CLOCK_REALTIME_COARSE */
vdso_ts = &vc[CS_HRES_COARSE].basetime[CLOCK_REALTIME_COARSE];
vdso_ts->sec = tk->xtime_sec;
vdso_ts->nsec = tk->coarse_nsec;
/* CLOCK_MONOTONIC_COARSE */
vdso_ts = &vc[CS_HRES_COARSE].basetime[CLOCK_MONOTONIC_COARSE];
vdso_ts->sec = tk->xtime_sec + tk->wall_to_monotonic.tv_sec;
nsec = tk->coarse_nsec;
nsec = nsec + tk->wall_to_monotonic.tv_nsec;
vdso_ts->sec += __iter_div_u64_rem(nsec, NSEC_PER_SEC, &vdso_ts->nsec);
/*
* Read without the seqlock held by clock_getres().
*/
WRITE_ONCE(vdata->hrtimer_res, hrtimer_resolution);
/*
* If the current clocksource is not VDSO capable, then spare the
* update of the high resolution parts.
*/
if (clock_mode != VDSO_CLOCKMODE_NONE)
update_vdso_time_data(vdata, tk);
__arch_update_vdso_clock(&vc[CS_HRES_COARSE]);
__arch_update_vdso_clock(&vc[CS_RAW]);
vdso_write_end(vdata);
__arch_sync_vdso_time_data(vdata);
}
void update_vsyscall_tz(void)
{
struct vdso_time_data *vdata = vdso_k_time_data;
vdata->tz_minuteswest = sys_tz.tz_minuteswest;
vdata->tz_dsttime = sys_tz.tz_dsttime;
__arch_sync_vdso_time_data(vdata);
}
#ifdef CONFIG_POSIX_AUX_CLOCKS
void vdso_time_update_aux(struct timekeeper *tk)
{
struct vdso_time_data *vdata = vdso_k_time_data;
struct vdso_timestamp *vdso_ts;
struct vdso_clock *vc;
s32 clock_mode;
u64 nsec;
vc = &vdata->aux_clock_data[tk->id - TIMEKEEPER_AUX_FIRST];
vdso_ts = &vc->basetime[VDSO_BASE_AUX];
clock_mode = tk->tkr_mono.clock->vdso_clock_mode;
if (!tk->clock_valid)
clock_mode = VDSO_CLOCKMODE_NONE;
/* copy vsyscall data */
vdso_write_begin_clock(vc);
vc->clock_mode = clock_mode;
if (clock_mode != VDSO_CLOCKMODE_NONE) {
fill_clock_configuration(vc, &tk->tkr_mono);
vdso_ts->sec = tk->xtime_sec + tk->monotonic_to_aux.tv_sec;
nsec = tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift;
nsec += tk->monotonic_to_aux.tv_nsec;
vdso_ts->sec += __iter_div_u64_rem(nsec, NSEC_PER_SEC, &nsec);
nsec = nsec << tk->tkr_mono.shift;
vdso_ts->nsec = nsec;
}
__arch_update_vdso_clock(vc);
vdso_write_end_clock(vc);
__arch_sync_vdso_time_data(vdata);
}
#endif
/**
* vdso_update_begin - Start of a VDSO update section
*
* Allows architecture code to safely update the architecture specific VDSO
* data. Disables interrupts, acquires timekeeper lock to serialize against
* concurrent updates from timekeeping and invalidates the VDSO data
* sequence counter to prevent concurrent readers from accessing
* inconsistent data.
*
* Returns: Saved interrupt flags which need to be handed in to
* vdso_update_end().
*/
unsigned long vdso_update_begin(void)
{
struct vdso_time_data *vdata = vdso_k_time_data;
unsigned long flags = timekeeper_lock_irqsave();
vdso_write_begin(vdata);
return flags;
}
/**
* vdso_update_end - End of a VDSO update section
* @flags: Interrupt flags as returned from vdso_update_begin()
*
* Pairs with vdso_update_begin(). Marks vdso data consistent, invokes data
* synchronization if the architecture requires it, drops timekeeper lock
* and restores interrupt flags.
*/
void vdso_update_end(unsigned long flags)
{
struct vdso_time_data *vdata = vdso_k_time_data;
vdso_write_end(vdata);
__arch_sync_vdso_time_data(vdata);
timekeeper_unlock_irqrestore(flags);
}