Commit 89f951a1 authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Peter Zijlstra
Browse files

clockevents: Provide support for clocksource coupled comparators



Some clockevent devices are coupled to the system clocksource by
implementing a less than or equal comparator which compares the programmed
absolute expiry time against the underlying time counter.

The timekeeping core provides a function to convert and absolute
CLOCK_MONOTONIC based expiry time to a absolute clock cycles time which can
be directly fed into the comparator. That spares two time reads in the next
event progamming path, one to convert the absolute nanoseconds time to a
delta value and the other to convert the delta value back to a absolute
time value suitable for the comparator.

Provide a new clocksource callback which takes the absolute cycle value and
wire it up in clockevents_program_event(). Similar to clocksources allow
architectures to inline the rearm operation.

Signed-off-by: default avatarThomas Gleixner <tglx@kernel.org>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Link: https://patch.msgid.link/20260224163430.010425428@kernel.org
parent cd38bdb8
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ enum clock_event_state {
 */
# define CLOCK_EVT_FEAT_PERIODIC		0x000001
# define CLOCK_EVT_FEAT_ONESHOT			0x000002
# define CLOCK_EVT_FEAT_CLOCKSOURCE_COUPLED	0x000004

/*
 * x86(64) specific (mis)features:
@@ -100,6 +101,7 @@ struct clock_event_device {
	void			(*event_handler)(struct clock_event_device *);
	int			(*set_next_event)(unsigned long evt, struct clock_event_device *);
	int			(*set_next_ktime)(ktime_t expires, struct clock_event_device *);
	void			(*set_next_coupled)(u64 cycles, struct clock_event_device *);
	ktime_t			next_event;
	u64			max_delta_ns;
	u64			min_delta_ns;
@@ -107,6 +109,7 @@ struct clock_event_device {
	u32			shift;
	enum clock_event_state	state_use_accessors;
	unsigned int		features;
	enum clocksource_ids	cs_id;
	unsigned long		retries;

	int			(*set_state_periodic)(struct clock_event_device *);
+4 −0
Original line number Diff line number Diff line
@@ -50,6 +50,10 @@ config GENERIC_CLOCKEVENTS_MIN_ADJUST
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
+39 −5
Original line number Diff line number Diff line
@@ -292,6 +292,38 @@ static int clockevents_program_min_delta(struct clock_event_device *dev)

#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 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
@@ -300,11 +332,10 @@ static int clockevents_program_min_delta(struct clock_event_device *dev)
 *
 * 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)
int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, bool force)
{
	unsigned long long clc;
	int64_t delta;
	u64 cycles;
	int rc;

	if (WARN_ON_ONCE(expires < 0))
@@ -323,6 +354,9 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires,
	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()));
	if (delta <= 0)
		return force ? clockevents_program_min_delta(dev) : -ETIME;
@@ -330,8 +364,8 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires,
	delta = min(delta, (int64_t) dev->max_delta_ns);
	delta = max(delta, (int64_t) dev->min_delta_ns);

	clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
	rc = dev->set_next_event((unsigned long) clc, dev);
	cycles = ((u64)delta * dev->mult) >> dev->shift;
	rc = dev->set_next_event((unsigned long) cycles, dev);

	return (rc && force) ? clockevents_program_min_delta(dev) : rc;
}