Commit b7f6d3a0 authored by Thomas Gleixner's avatar Thomas Gleixner
Browse files

Merge branch 'timers/vfs' into timers/core

Pick up the VFS specific interfaces so further timekeeping changes can be
based on them.
parents 8c111f1b 96f9a366
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -45,6 +45,11 @@ extern void ktime_get_real_ts64(struct timespec64 *tv);
extern void ktime_get_coarse_ts64(struct timespec64 *ts);
extern void ktime_get_coarse_real_ts64(struct timespec64 *ts);

/* Multigrain timestamp interfaces */
extern void ktime_get_coarse_real_ts64_mg(struct timespec64 *ts);
extern void ktime_get_real_ts64_mg(struct timespec64 *ts);
extern unsigned long timekeeping_get_mg_floor_swaps(void);

void getboottime64(struct timespec64 *ts);

/*
+105 −0
Original line number Diff line number Diff line
@@ -114,6 +114,23 @@ static struct tk_fast tk_fast_raw ____cacheline_aligned = {
	.base[1] = FAST_TK_INIT,
};

/*
 * Multigrain timestamps require tracking the latest fine-grained timestamp
 * that has been issued, and never returning a coarse-grained timestamp that is
 * earlier than that value.
 *
 * mg_floor represents the latest fine-grained time that has been handed out as
 * a file timestamp on the system. This is tracked as a monotonic ktime_t, and
 * converted to a realtime clock value on an as-needed basis.
 *
 * Maintaining mg_floor ensures the multigrain interfaces never issue a
 * timestamp earlier than one that has been previously issued.
 *
 * The exception to this rule is when there is a backward realtime clock jump. If
 * such an event occurs, a timestamp can appear to be earlier than a previous one.
 */
static __cacheline_aligned_in_smp atomic64_t mg_floor;

static inline void tk_normalize_xtime(struct timekeeper *tk)
{
	while (tk->tkr_mono.xtime_nsec >= ((u64)NSEC_PER_SEC << tk->tkr_mono.shift)) {
@@ -2408,6 +2425,94 @@ void ktime_get_coarse_real_ts64(struct timespec64 *ts)
}
EXPORT_SYMBOL(ktime_get_coarse_real_ts64);

/**
 * ktime_get_coarse_real_ts64_mg - return latter of coarse grained time or floor
 * @ts:		timespec64 to be filled
 *
 * Fetch the global mg_floor value, convert it to realtime and compare it
 * to the current coarse-grained time. Fill @ts with whichever is
 * latest. Note that this is a filesystem-specific interface and should be
 * avoided outside of that context.
 */
void ktime_get_coarse_real_ts64_mg(struct timespec64 *ts)
{
	struct timekeeper *tk = &tk_core.timekeeper;
	u64 floor = atomic64_read(&mg_floor);
	ktime_t f_real, offset, coarse;
	unsigned int seq;

	do {
		seq = read_seqcount_begin(&tk_core.seq);
		*ts = tk_xtime(tk);
		offset = tk_core.timekeeper.offs_real;
	} while (read_seqcount_retry(&tk_core.seq, seq));

	coarse = timespec64_to_ktime(*ts);
	f_real = ktime_add(floor, offset);
	if (ktime_after(f_real, coarse))
		*ts = ktime_to_timespec64(f_real);
}

/**
 * ktime_get_real_ts64_mg - attempt to update floor value and return result
 * @ts:		pointer to the timespec to be set
 *
 * Get a monotonic fine-grained time value and attempt to swap it into
 * mg_floor. If that succeeds then accept the new floor value. If it fails
 * then another task raced in during the interim time and updated the
 * floor.  Since any update to the floor must be later than the previous
 * floor, either outcome is acceptable.
 *
 * Typically this will be called after calling ktime_get_coarse_real_ts64_mg(),
 * and determining that the resulting coarse-grained timestamp did not effect
 * a change in ctime. Any more recent floor value would effect a change to
 * ctime, so there is no need to retry the atomic64_try_cmpxchg() on failure.
 *
 * @ts will be filled with the latest floor value, regardless of the outcome of
 * the cmpxchg. Note that this is a filesystem specific interface and should be
 * avoided outside of that context.
 */
void ktime_get_real_ts64_mg(struct timespec64 *ts)
{
	struct timekeeper *tk = &tk_core.timekeeper;
	ktime_t old = atomic64_read(&mg_floor);
	ktime_t offset, mono;
	unsigned int seq;
	u64 nsecs;

	do {
		seq = read_seqcount_begin(&tk_core.seq);

		ts->tv_sec = tk->xtime_sec;
		mono = tk->tkr_mono.base;
		nsecs = timekeeping_get_ns(&tk->tkr_mono);
		offset = tk_core.timekeeper.offs_real;
	} while (read_seqcount_retry(&tk_core.seq, seq));

	mono = ktime_add_ns(mono, nsecs);

	/*
	 * Attempt to update the floor with the new time value. As any
	 * update must be later then the existing floor, and would effect
	 * a change to ctime from the perspective of the current task,
	 * accept the resulting floor value regardless of the outcome of
	 * the swap.
	 */
	if (atomic64_try_cmpxchg(&mg_floor, &old, mono)) {
		ts->tv_nsec = 0;
		timespec64_add_ns(ts, nsecs);
		timekeeping_inc_mg_floor_swaps();
	} else {
		/*
		 * Another task changed mg_floor since "old" was fetched.
		 * "old" has been updated with the latest value of "mg_floor".
		 * That value is newer than the previous floor value, which
		 * is enough to effect a change to ctime. Accept it.
		 */
		*ts = ktime_to_timespec64(ktime_add(old, offset));
	}
}

void ktime_get_coarse_ts64(struct timespec64 *ts)
{
	struct timekeeper *tk = &tk_core.timekeeper;
+13 −0
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@

#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)
@@ -53,3 +56,13 @@ void tk_debug_account_sleep_time(const struct timespec64 *t)
			   (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;
}
+15 −0
Original line number Diff line number Diff line
@@ -10,9 +10,24 @@
 * 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

#ifdef CONFIG_CLOCKSOURCE_VALIDATE_LAST_CYCLE