1278 lines · 33.8 KB · C
RawDownload
/* *  linux/kernel/time/timekeeping.c * *  Kernel timekeeping code and accessor functions * *  This code was moved from linux/kernel/timer.c. *  Please see that file for copyright and history logs. * */ #include <linux/module.h>#include <linux/interrupt.h>#include <linux/percpu.h>#include <linux/init.h>#include <linux/mm.h>#include <linux/sched.h>#include <linux/syscore_ops.h>#include <linux/clocksource.h>#include <linux/jiffies.h>#include <linux/time.h>#include <linux/tick.h>#include <linux/stop_machine.h> /* Structure holding internal timekeeping values. */struct timekeeper {	/* Current clocksource used for timekeeping. */	struct clocksource *clock;	/* NTP adjusted clock multiplier */	u32	mult;	/* The shift value of the current clocksource. */	int	shift; 	/* Number of clock cycles in one NTP interval. */	cycle_t cycle_interval;	/* Number of clock shifted nano seconds in one NTP interval. */	u64	xtime_interval;	/* shifted nano seconds left over when rounding cycle_interval */	s64	xtime_remainder;	/* Raw nano seconds accumulated per NTP interval. */	u32	raw_interval; 	/* Clock shifted nano seconds remainder not stored in xtime.tv_nsec. */	u64	xtime_nsec;	/* Difference between accumulated time and NTP time in ntp	 * shifted nano seconds. */	s64	ntp_error;	/* Shift conversion between clock shifted nano seconds and	 * ntp shifted nano seconds. */	int	ntp_error_shift; 	/* The current time */	struct timespec xtime;	/*	 * wall_to_monotonic is what we need to add to xtime (or xtime corrected	 * for sub jiffie times) to get to monotonic time.  Monotonic is pegged	 * at zero at system boot time, so wall_to_monotonic will be negative,	 * however, we will ALWAYS keep the tv_nsec part positive so we can use	 * the usual normalization.	 *	 * wall_to_monotonic is moved after resume from suspend for the	 * monotonic time not to jump. We need to add total_sleep_time to	 * wall_to_monotonic to get the real boot based time offset.	 *	 * - wall_to_monotonic is no longer the boot time, getboottime must be	 * used instead.	 */	struct timespec wall_to_monotonic;	/* time spent in suspend */	struct timespec total_sleep_time;	/* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */	struct timespec raw_time; 	/* Seqlock for all timekeeper values */	seqlock_t lock;}; static struct timekeeper timekeeper; /* * This read-write spinlock protects us from races in SMP while * playing with xtime. */__cacheline_aligned_in_smp DEFINE_SEQLOCK(xtime_lock);  /* flag for if timekeeping is suspended */int __read_mostly timekeeping_suspended;   /** * timekeeper_setup_internals - Set up internals to use clocksource clock. * * @clock:		Pointer to clocksource. * * Calculates a fixed cycle/nsec interval for a given clocksource/adjustment * pair and interval request. * * Unless you're the timekeeping code, you should not be using this! */static void timekeeper_setup_internals(struct clocksource *clock){	cycle_t interval;	u64 tmp, ntpinterval; 	timekeeper.clock = clock;	clock->cycle_last = clock->read(clock); 	/* Do the ns -> cycle conversion first, using original mult */	tmp = NTP_INTERVAL_LENGTH;	tmp <<= clock->shift;	ntpinterval = tmp;	tmp += clock->mult/2;	do_div(tmp, clock->mult);	if (tmp == 0)		tmp = 1; 	interval = (cycle_t) tmp;	timekeeper.cycle_interval = interval; 	/* Go back from cycles -> shifted ns */	timekeeper.xtime_interval = (u64) interval * clock->mult;	timekeeper.xtime_remainder = ntpinterval - timekeeper.xtime_interval;	timekeeper.raw_interval =		((u64) interval * clock->mult) >> clock->shift; 	timekeeper.xtime_nsec = 0;	timekeeper.shift = clock->shift; 	timekeeper.ntp_error = 0;	timekeeper.ntp_error_shift = NTP_SCALE_SHIFT - clock->shift; 	/*	 * The timekeeper keeps its own mult values for the currently	 * active clocksource. These value will be adjusted via NTP	 * to counteract clock drifting.	 */	timekeeper.mult = clock->mult;} /* Timekeeper helper functions. */static inline s64 timekeeping_get_ns(void){	cycle_t cycle_now, cycle_delta;	struct clocksource *clock; 	/* read clocksource: */	clock = timekeeper.clock;	cycle_now = clock->read(clock); 	/* calculate the delta since the last update_wall_time: */	cycle_delta = (cycle_now - clock->cycle_last) & clock->mask; 	/* return delta convert to nanoseconds using ntp adjusted mult. */	return clocksource_cyc2ns(cycle_delta, timekeeper.mult,				  timekeeper.shift);} static inline s64 timekeeping_get_ns_raw(void){	cycle_t cycle_now, cycle_delta;	struct clocksource *clock; 	/* read clocksource: */	clock = timekeeper.clock;	cycle_now = clock->read(clock); 	/* calculate the delta since the last update_wall_time: */	cycle_delta = (cycle_now - clock->cycle_last) & clock->mask; 	/* return delta convert to nanoseconds. */	return clocksource_cyc2ns(cycle_delta, clock->mult, clock->shift);} /* must hold write on timekeeper.lock */static void timekeeping_update(bool clearntp){	if (clearntp) {		timekeeper.ntp_error = 0;		ntp_clear();	}	update_vsyscall(&timekeeper.xtime, &timekeeper.wall_to_monotonic,			 timekeeper.clock, timekeeper.mult);}  /** * timekeeping_forward_now - update clock to the current time * * Forward the current clock to update its state since the last call to * update_wall_time(). This is useful before significant clock changes, * as it avoids having to deal with this time offset explicitly. */static void timekeeping_forward_now(void){	cycle_t cycle_now, cycle_delta;	struct clocksource *clock;	s64 nsec; 	clock = timekeeper.clock;	cycle_now = clock->read(clock);	cycle_delta = (cycle_now - clock->cycle_last) & clock->mask;	clock->cycle_last = cycle_now; 	nsec = clocksource_cyc2ns(cycle_delta, timekeeper.mult,				  timekeeper.shift); 	/* If arch requires, add in gettimeoffset() */	nsec += arch_gettimeoffset(); 	timespec_add_ns(&timekeeper.xtime, nsec); 	nsec = clocksource_cyc2ns(cycle_delta, clock->mult, clock->shift);	timespec_add_ns(&timekeeper.raw_time, nsec);} /** * getnstimeofday - Returns the time of day in a timespec * @ts:		pointer to the timespec to be set * * Returns the time of day in a timespec. */void getnstimeofday(struct timespec *ts){	unsigned long seq;	s64 nsecs; 	WARN_ON(timekeeping_suspended); 	do {		seq = read_seqbegin(&timekeeper.lock); 		*ts = timekeeper.xtime;		nsecs = timekeeping_get_ns(); 		/* If arch requires, add in gettimeoffset() */		nsecs += arch_gettimeoffset(); 	} while (read_seqretry(&timekeeper.lock, seq)); 	timespec_add_ns(ts, nsecs);} EXPORT_SYMBOL(getnstimeofday); ktime_t ktime_get(void){	unsigned int seq;	s64 secs, nsecs; 	WARN_ON(timekeeping_suspended); 	do {		seq = read_seqbegin(&timekeeper.lock);		secs = timekeeper.xtime.tv_sec +				timekeeper.wall_to_monotonic.tv_sec;		nsecs = timekeeper.xtime.tv_nsec +				timekeeper.wall_to_monotonic.tv_nsec;		nsecs += timekeeping_get_ns();		/* If arch requires, add in gettimeoffset() */		nsecs += arch_gettimeoffset(); 	} while (read_seqretry(&timekeeper.lock, seq));	/*	 * Use ktime_set/ktime_add_ns to create a proper ktime on	 * 32-bit architectures without CONFIG_KTIME_SCALAR.	 */	return ktime_add_ns(ktime_set(secs, 0), nsecs);}EXPORT_SYMBOL_GPL(ktime_get); /** * ktime_get_ts - get the monotonic clock in timespec format * @ts:		pointer to timespec variable * * The function calculates the monotonic clock from the realtime * clock and the wall_to_monotonic offset and stores the result * in normalized timespec format in the variable pointed to by @ts. */void ktime_get_ts(struct timespec *ts){	struct timespec tomono;	unsigned int seq;	s64 nsecs; 	WARN_ON(timekeeping_suspended); 	do {		seq = read_seqbegin(&timekeeper.lock);		*ts = timekeeper.xtime;		tomono = timekeeper.wall_to_monotonic;		nsecs = timekeeping_get_ns();		/* If arch requires, add in gettimeoffset() */		nsecs += arch_gettimeoffset(); 	} while (read_seqretry(&timekeeper.lock, seq)); 	set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec,				ts->tv_nsec + tomono.tv_nsec + nsecs);}EXPORT_SYMBOL_GPL(ktime_get_ts); #ifdef CONFIG_NTP_PPS /** * getnstime_raw_and_real - get day and raw monotonic time in timespec format * @ts_raw:	pointer to the timespec to be set to raw monotonic time * @ts_real:	pointer to the timespec to be set to the time of day * * This function reads both the time of day and raw monotonic time at the * same time atomically and stores the resulting timestamps in timespec * format. */void getnstime_raw_and_real(struct timespec *ts_raw, struct timespec *ts_real){	unsigned long seq;	s64 nsecs_raw, nsecs_real; 	WARN_ON_ONCE(timekeeping_suspended); 	do {		u32 arch_offset; 		seq = read_seqbegin(&timekeeper.lock); 		*ts_raw = timekeeper.raw_time;		*ts_real = timekeeper.xtime; 		nsecs_raw = timekeeping_get_ns_raw();		nsecs_real = timekeeping_get_ns(); 		/* If arch requires, add in gettimeoffset() */		arch_offset = arch_gettimeoffset();		nsecs_raw += arch_offset;		nsecs_real += arch_offset; 	} while (read_seqretry(&timekeeper.lock, seq)); 	timespec_add_ns(ts_raw, nsecs_raw);	timespec_add_ns(ts_real, nsecs_real);}EXPORT_SYMBOL(getnstime_raw_and_real); #endif /* CONFIG_NTP_PPS */ /** * do_gettimeofday - Returns the time of day in a timeval * @tv:		pointer to the timeval to be set * * NOTE: Users should be converted to using getnstimeofday() */void do_gettimeofday(struct timeval *tv){	struct timespec now; 	getnstimeofday(&now);	tv->tv_sec = now.tv_sec;	tv->tv_usec = now.tv_nsec/1000;} EXPORT_SYMBOL(do_gettimeofday);/** * do_settimeofday - Sets the time of day * @tv:		pointer to the timespec variable containing the new time * * Sets the time of day to the new time and update NTP and notify hrtimers */int do_settimeofday(const struct timespec *tv){	struct timespec ts_delta;	unsigned long flags; 	if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)		return -EINVAL; 	write_seqlock_irqsave(&timekeeper.lock, flags); 	timekeeping_forward_now(); 	ts_delta.tv_sec = tv->tv_sec - timekeeper.xtime.tv_sec;	ts_delta.tv_nsec = tv->tv_nsec - timekeeper.xtime.tv_nsec;	timekeeper.wall_to_monotonic =			timespec_sub(timekeeper.wall_to_monotonic, ts_delta); 	timekeeper.xtime = *tv;	timekeeping_update(true); 	write_sequnlock_irqrestore(&timekeeper.lock, flags); 	/* signal hrtimers about time change */	clock_was_set(); 	return 0;} EXPORT_SYMBOL(do_settimeofday);  /** * timekeeping_inject_offset - Adds or subtracts from the current time. * @tv:		pointer to the timespec variable containing the offset * * Adds or subtracts an offset value from the current time. */int timekeeping_inject_offset(struct timespec *ts){	unsigned long flags; 	if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC)		return -EINVAL; 	write_seqlock_irqsave(&timekeeper.lock, flags); 	timekeeping_forward_now(); 	timekeeper.xtime = timespec_add(timekeeper.xtime, *ts);	timekeeper.wall_to_monotonic =				timespec_sub(timekeeper.wall_to_monotonic, *ts); 	timekeeping_update(true); 	write_sequnlock_irqrestore(&timekeeper.lock, flags); 	/* signal hrtimers about time change */	clock_was_set(); 	return 0;}EXPORT_SYMBOL(timekeeping_inject_offset); /** * change_clocksource - Swaps clocksources if a new one is available * * Accumulates current time interval and initializes new clocksource */static int change_clocksource(void *data){	struct clocksource *new, *old;	unsigned long flags; 	new = (struct clocksource *) data; 	write_seqlock_irqsave(&timekeeper.lock, flags); 	timekeeping_forward_now();	if (!new->enable || new->enable(new) == 0) {		old = timekeeper.clock;		timekeeper_setup_internals(new);		if (old->disable)			old->disable(old);	}	timekeeping_update(true); 	write_sequnlock_irqrestore(&timekeeper.lock, flags); 	return 0;} /** * timekeeping_notify - Install a new clock source * @clock:		pointer to the clock source * * This function is called from clocksource.c after a new, better clock * source has been registered. The caller holds the clocksource_mutex. */void timekeeping_notify(struct clocksource *clock){	if (timekeeper.clock == clock)		return;	stop_machine(change_clocksource, clock, NULL);	tick_clock_notify();} /** * ktime_get_real - get the real (wall-) time in ktime_t format * * returns the time in ktime_t format */ktime_t ktime_get_real(void){	struct timespec now; 	getnstimeofday(&now); 	return timespec_to_ktime(now);}EXPORT_SYMBOL_GPL(ktime_get_real); /** * getrawmonotonic - Returns the raw monotonic time in a timespec * @ts:		pointer to the timespec to be set * * Returns the raw monotonic time (completely un-modified by ntp) */void getrawmonotonic(struct timespec *ts){	unsigned long seq;	s64 nsecs; 	do {

Lines 1–500 of 1278.