Skip to content

Commit

Permalink
From patchwork series 400226
Browse files Browse the repository at this point in the history
  • Loading branch information
Fox Snowpatch committed Mar 25, 2024
1 parent 4db4221 commit bc9d2f1
Show file tree
Hide file tree
Showing 12 changed files with 204 additions and 130 deletions.
26 changes: 11 additions & 15 deletions arch/powerpc/include/asm/vdso/gettimeofday.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@

#define VDSO_HAS_TIME 1

/*
* powerpc specific delta calculation.
*
* This variant removes the masking of the subtraction because the
* clocksource mask of all VDSO capable clocksources on powerpc is U64_MAX
* which would result in a pointless operation. The compiler cannot
* optimize it away as the mask comes from the vdso data and is not compile
* time constant.
*/
#define VDSO_DELTA_NOMASK 1

static __always_inline int do_syscall_2(const unsigned long _r0, const unsigned long _r3,
const unsigned long _r4)
{
Expand Down Expand Up @@ -105,21 +116,6 @@ static inline bool vdso_clocksource_ok(const struct vdso_data *vd)
}
#define vdso_clocksource_ok vdso_clocksource_ok

/*
* powerpc specific delta calculation.
*
* This variant removes the masking of the subtraction because the
* clocksource mask of all VDSO capable clocksources on powerpc is U64_MAX
* which would result in a pointless operation. The compiler cannot
* optimize it away as the mask comes from the vdso data and is not compile
* time constant.
*/
static __always_inline u64 vdso_calc_delta(u64 cycles, u64 last, u64 mask, u32 mult)
{
return (cycles - last) * mult;
}
#define vdso_calc_delta vdso_calc_delta

#ifndef __powerpc64__
static __always_inline u64 vdso_shift_ns(u64 ns, unsigned long shift)
{
Expand Down
7 changes: 2 additions & 5 deletions arch/s390/include/asm/vdso/gettimeofday.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,13 @@

#define VDSO_HAS_CLOCK_GETRES 1

#define VDSO_DELTA_NOMASK 1

#include <asm/syscall.h>
#include <asm/timex.h>
#include <asm/unistd.h>
#include <linux/compiler.h>

#define vdso_calc_delta __arch_vdso_calc_delta
static __always_inline u64 __arch_vdso_calc_delta(u64 cycles, u64 last, u64 mask, u32 mult)
{
return (cycles - last) * mult;
}

static __always_inline const struct vdso_data *__arch_get_vdso_data(void)
{
Expand Down
1 change: 1 addition & 0 deletions arch/x86/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ config X86
select GENERIC_TIME_VSYSCALL
select GENERIC_GETTIMEOFDAY
select GENERIC_VDSO_TIME_NS
select GENERIC_VDSO_OVERFLOW_PROTECT
select GUP_GET_PXX_LOW_HIGH if X86_PAE
select HARDIRQS_SW_RESEND
select HARDLOCKUP_CHECK_TIMESTAMP if X86_64
Expand Down
44 changes: 28 additions & 16 deletions arch/x86/include/asm/vdso/gettimeofday.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,42 +300,54 @@ static inline bool arch_vdso_cycles_ok(u64 cycles)
#define vdso_cycles_ok arch_vdso_cycles_ok

/*
* x86 specific delta calculation.
* x86 specific calculation of nanoseconds for the current cycle count
*
* The regular implementation assumes that clocksource reads are globally
* monotonic. The TSC can be slightly off across sockets which can cause
* the regular delta calculation (@cycles - @last) to return a huge time
* jump.
*
* Therefore it needs to be verified that @cycles are greater than
* @last. If not then use @last, which is the base time of the current
* conversion period.
* @vd->cycles_last. If not then use @vd->cycles_last, which is the base
* time of the current conversion period.
*
* This variant also uses a custom mask because while the clocksource mask of
* all the VDSO capable clocksources on x86 is U64_MAX, the above code uses
* U64_MASK as an exception value, additionally arch_vdso_cycles_ok() above
* declares everything with the MSB/Sign-bit set as invalid. Therefore the
* effective mask is S64_MAX.
*/
static __always_inline
u64 vdso_calc_delta(u64 cycles, u64 last, u64 mask, u32 mult)
static __always_inline u64 vdso_calc_ns(const struct vdso_data *vd, u64 cycles, u64 base)
{
/*
* Due to the MSB/Sign-bit being used as invalid marker (see
* arch_vdso_cycles_valid() above), the effective mask is S64_MAX.
*/
u64 delta = (cycles - last) & S64_MAX;
u64 delta = cycles - vd->cycle_last;

/*
* Due to the above mentioned TSC wobbles, filter out negative motion.
* Per the above masking, the effective sign bit is now bit 62.
* Negative motion and deltas which can cause multiplication
* overflow require special treatment. This check covers both as
* negative motion is guaranteed to be greater than @vd::max_cycles
* due to unsigned comparison.
*
* Due to the MSB/Sign-bit being used as invalid marker (see
* arch_vdso_cycles_valid() above), the effective mask is S64_MAX,
* but that case is also unlikely and will also take the unlikely path
* here.
*/
if (unlikely(delta & (1ULL << 62)))
return 0;
if (unlikely(delta > vd->max_cycles)) {
/*
* Due to the above mentioned TSC wobbles, filter out
* negative motion. Per the above masking, the effective
* sign bit is now bit 62.
*/
if (delta & (1ULL << 62))
return base >> vd->shift;

/* Handle multiplication overflow gracefully */
return mul_u64_u32_add_u64_shr(delta & S64_MAX, vd->mult, base, vd->shift);
}

return delta * mult;
return ((delta * vd->mult) + base) >> vd->shift;
}
#define vdso_calc_delta vdso_calc_delta
#define vdso_calc_ns vdso_calc_ns

#endif /* !__ASSEMBLY__ */

Expand Down
8 changes: 2 additions & 6 deletions include/linux/math64.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

#include <linux/types.h>
#include <linux/math.h>
#include <vdso/math64.h>
#include <asm/div64.h>
#include <vdso/math64.h>

#if BITS_PER_LONG == 64

Expand Down Expand Up @@ -179,16 +179,12 @@ static __always_inline u64 mul_u64_u64_shr(u64 a, u64 mul, unsigned int shift)
#ifndef mul_u64_u32_shr
static __always_inline u64 mul_u64_u32_shr(u64 a, u32 mul, unsigned int shift)
{
u32 ah, al;
u32 ah = a >> 32, al = a;
u64 ret;

al = a;
ah = a >> 32;

ret = mul_u32_u32(al, mul) >> shift;
if (ah)
ret += mul_u32_u32(ah, mul) << (32 - shift);

return ret;
}
#endif /* mul_u64_u32_shr */
Expand Down
4 changes: 4 additions & 0 deletions include/vdso/datapage.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ struct vdso_timestamp {
* @seq: timebase sequence counter
* @clock_mode: clock mode
* @cycle_last: timebase at clocksource init
* @max_cycles: maximum cycles which won't overflow 64bit multiplication
* @mask: clocksource mask
* @mult: clocksource multiplier
* @shift: clocksource shift
Expand Down Expand Up @@ -98,6 +99,9 @@ struct vdso_data {

s32 clock_mode;
u64 cycle_last;
#ifdef CONFIG_GENERIC_VDSO_OVERFLOW_PROTECT
u64 max_cycles;
#endif
u64 mask;
u32 mult;
u32 shift;
Expand Down
38 changes: 38 additions & 0 deletions include/vdso/math64.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,42 @@ __iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder)
return ret;
}

#if defined(CONFIG_ARCH_SUPPORTS_INT128) && defined(__SIZEOF_INT128__)

#ifndef mul_u64_u32_add_u64_shr
static __always_inline u64 mul_u64_u32_add_u64_shr(u64 a, u32 mul, u64 b, unsigned int shift)
{
return (u64)((((unsigned __int128)a * mul) + b) >> shift);
}
#endif /* mul_u64_u32_add_u64_shr */

#else

#ifndef mul_u64_u32_add_u64_shr
#ifndef mul_u32_u32
static inline u64 mul_u32_u32(u32 a, u32 b)
{
return (u64)a * b;
}
#define mul_u32_u32 mul_u32_u32
#endif
static __always_inline u64 mul_u64_u32_add_u64_shr(u64 a, u32 mul, u64 b, unsigned int shift)
{
u32 ah = a >> 32, al = a;
bool ovf;
u64 ret;

ovf = __builtin_add_overflow(mul_u32_u32(al, mul), b, &ret);
ret >>= shift;
if (ovf && shift)
ret += 1ULL << (64 - shift);
if (ah)
ret += mul_u32_u32(ah, mul) << (32 - shift);

return ret;
}
#endif /* mul_u64_u32_add_u64_shr */

#endif

#endif /* __VDSO_MATH64_H */
42 changes: 20 additions & 22 deletions kernel/time/clocksource.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@
#include "tick-internal.h"
#include "timekeeping_internal.h"

static noinline u64 cycles_to_nsec_safe(struct clocksource *cs, u64 start, u64 end)
{
u64 delta = clocksource_delta(end, start, cs->mask);

if (likely(delta < cs->max_cycles))
return clocksource_cyc2ns(delta, cs->mult, cs->shift);

return mul_u64_u32_shr(delta, cs->mult, cs->shift);
}

/**
* clocks_calc_mult_shift - calculate mult/shift factors for scaled math of clocks
* @mult: pointer to mult variable
Expand Down Expand Up @@ -222,8 +232,8 @@ enum wd_read_status {
static enum wd_read_status cs_watchdog_read(struct clocksource *cs, u64 *csnow, u64 *wdnow)
{
unsigned int nretries, max_retries;
u64 wd_end, wd_end2, wd_delta;
int64_t wd_delay, wd_seq_delay;
u64 wd_end, wd_end2;

max_retries = clocksource_get_max_watchdog_retry();
for (nretries = 0; nretries <= max_retries; nretries++) {
Expand All @@ -234,9 +244,7 @@ static enum wd_read_status cs_watchdog_read(struct clocksource *cs, u64 *csnow,
wd_end2 = watchdog->read(watchdog);
local_irq_enable();

wd_delta = clocksource_delta(wd_end, *wdnow, watchdog->mask);
wd_delay = clocksource_cyc2ns(wd_delta, watchdog->mult,
watchdog->shift);
wd_delay = cycles_to_nsec_safe(watchdog, *wdnow, wd_end);
if (wd_delay <= WATCHDOG_MAX_SKEW) {
if (nretries > 1 || nretries >= max_retries) {
pr_warn("timekeeping watchdog on CPU%d: %s retried %d times before success\n",
Expand All @@ -254,8 +262,7 @@ static enum wd_read_status cs_watchdog_read(struct clocksource *cs, u64 *csnow,
* report system busy, reinit the watchdog and skip the current
* watchdog test.
*/
wd_delta = clocksource_delta(wd_end2, wd_end, watchdog->mask);
wd_seq_delay = clocksource_cyc2ns(wd_delta, watchdog->mult, watchdog->shift);
wd_seq_delay = cycles_to_nsec_safe(watchdog, wd_end, wd_end2);
if (wd_seq_delay > WATCHDOG_MAX_SKEW/2)
goto skip_test;
}
Expand Down Expand Up @@ -366,8 +373,7 @@ void clocksource_verify_percpu(struct clocksource *cs)
delta = (csnow_end - csnow_mid) & cs->mask;
if (delta < 0)
cpumask_set_cpu(cpu, &cpus_ahead);
delta = clocksource_delta(csnow_end, csnow_begin, cs->mask);
cs_nsec = clocksource_cyc2ns(delta, cs->mult, cs->shift);
cs_nsec = cycles_to_nsec_safe(cs, csnow_begin, csnow_end);
if (cs_nsec > cs_nsec_max)
cs_nsec_max = cs_nsec;
if (cs_nsec < cs_nsec_min)
Expand Down Expand Up @@ -398,8 +404,8 @@ static inline void clocksource_reset_watchdog(void)

static void clocksource_watchdog(struct timer_list *unused)
{
u64 csnow, wdnow, cslast, wdlast, delta;
int64_t wd_nsec, cs_nsec, interval;
u64 csnow, wdnow, cslast, wdlast;
int next_cpu, reset_pending;
struct clocksource *cs;
enum wd_read_status read_ret;
Expand Down Expand Up @@ -456,12 +462,8 @@ static void clocksource_watchdog(struct timer_list *unused)
continue;
}

delta = clocksource_delta(wdnow, cs->wd_last, watchdog->mask);
wd_nsec = clocksource_cyc2ns(delta, watchdog->mult,
watchdog->shift);

delta = clocksource_delta(csnow, cs->cs_last, cs->mask);
cs_nsec = clocksource_cyc2ns(delta, cs->mult, cs->shift);
wd_nsec = cycles_to_nsec_safe(watchdog, cs->wd_last, wdnow);
cs_nsec = cycles_to_nsec_safe(cs, cs->cs_last, csnow);
wdlast = cs->wd_last; /* save these in case we print them */
cslast = cs->cs_last;
cs->cs_last = csnow;
Expand Down Expand Up @@ -832,7 +834,7 @@ void clocksource_start_suspend_timing(struct clocksource *cs, u64 start_cycles)
*/
u64 clocksource_stop_suspend_timing(struct clocksource *cs, u64 cycle_now)
{
u64 now, delta, nsec = 0;
u64 now, nsec = 0;

if (!suspend_clocksource)
return 0;
Expand All @@ -847,12 +849,8 @@ u64 clocksource_stop_suspend_timing(struct clocksource *cs, u64 cycle_now)
else
now = suspend_clocksource->read(suspend_clocksource);

if (now > suspend_start) {
delta = clocksource_delta(now, suspend_start,
suspend_clocksource->mask);
nsec = mul_u64_u32_shr(delta, suspend_clocksource->mult,
suspend_clocksource->shift);
}
if (now > suspend_start)
nsec = cycles_to_nsec_safe(suspend_clocksource, suspend_start, now);

/*
* Disable the suspend timer to save power if current clocksource is
Expand Down

0 comments on commit bc9d2f1

Please sign in to comment.