timekeeping: Consolidate timekeeping_inject_offset code
The code to check the adjtimex() or clock_adjtime() arguments is spread out across multiple files for presumably only historic reasons. As a preparatation for a rework to get rid of the use of 'struct timeval' and 'struct timespec' in there, this moves all the portions into kernel/time/timekeeping.c and marks them as 'static'. The warp_clock() function here is not as closely related as the others, but I feel it still makes sense to move it here in order to consolidate all callers of timekeeping_inject_offset(). Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Ingo Molnar <mingo@kernel.org> Cc: Miroslav Lichvar <mlichvar@redhat.com> Cc: Richard Cochran <richardcochran@gmail.com> Cc: Prarit Bhargava <prarit@redhat.com> Cc: Stephen Boyd <stephen.boyd@linaro.org> Signed-off-by: Arnd Bergmann <arnd@arndb.de> [jstultz: Whitespace fixup] Signed-off-by: John Stultz <john.stultz@linaro.org>
This commit is contained in:
parent
0f295b0650
commit
e0956dcc4b
@ -134,32 +134,6 @@ static inline bool timeval_valid(const struct timeval *tv)
|
||||
|
||||
extern struct timespec timespec_trunc(struct timespec t, unsigned gran);
|
||||
|
||||
/*
|
||||
* Validates if a timespec/timeval used to inject a time offset is valid.
|
||||
* Offsets can be postive or negative. The value of the timeval/timespec
|
||||
* is the sum of its fields, but *NOTE*: the field tv_usec/tv_nsec must
|
||||
* always be non-negative.
|
||||
*/
|
||||
static inline bool timeval_inject_offset_valid(const struct timeval *tv)
|
||||
{
|
||||
/* We don't check the tv_sec as it can be positive or negative */
|
||||
|
||||
/* Can't have more microseconds then a second */
|
||||
if (tv->tv_usec < 0 || tv->tv_usec >= USEC_PER_SEC)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool timespec_inject_offset_valid(const struct timespec *ts)
|
||||
{
|
||||
/* We don't check the tv_sec as it can be positive or negative */
|
||||
|
||||
/* Can't have more nanoseconds then a second */
|
||||
if (ts->tv_nsec < 0 || ts->tv_nsec >= NSEC_PER_SEC)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Some architectures do not supply their own clocksource.
|
||||
* This is mainly the case in architectures that get their
|
||||
* inter-tick times by reading the counter on their interval
|
||||
|
@ -713,67 +713,6 @@ static inline void process_adjtimex_modes(struct timex *txc,
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* ntp_validate_timex - Ensures the timex is ok for use in do_adjtimex
|
||||
*/
|
||||
int ntp_validate_timex(struct timex *txc)
|
||||
{
|
||||
if (txc->modes & ADJ_ADJTIME) {
|
||||
/* singleshot must not be used with any other mode bits */
|
||||
if (!(txc->modes & ADJ_OFFSET_SINGLESHOT))
|
||||
return -EINVAL;
|
||||
if (!(txc->modes & ADJ_OFFSET_READONLY) &&
|
||||
!capable(CAP_SYS_TIME))
|
||||
return -EPERM;
|
||||
} else {
|
||||
/* In order to modify anything, you gotta be super-user! */
|
||||
if (txc->modes && !capable(CAP_SYS_TIME))
|
||||
return -EPERM;
|
||||
/*
|
||||
* if the quartz is off by more than 10% then
|
||||
* something is VERY wrong!
|
||||
*/
|
||||
if (txc->modes & ADJ_TICK &&
|
||||
(txc->tick < 900000/USER_HZ ||
|
||||
txc->tick > 1100000/USER_HZ))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (txc->modes & ADJ_SETOFFSET) {
|
||||
/* In order to inject time, you gotta be super-user! */
|
||||
if (!capable(CAP_SYS_TIME))
|
||||
return -EPERM;
|
||||
|
||||
if (txc->modes & ADJ_NANO) {
|
||||
struct timespec ts;
|
||||
|
||||
ts.tv_sec = txc->time.tv_sec;
|
||||
ts.tv_nsec = txc->time.tv_usec;
|
||||
if (!timespec_inject_offset_valid(&ts))
|
||||
return -EINVAL;
|
||||
|
||||
} else {
|
||||
if (!timeval_inject_offset_valid(&txc->time))
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for potential multiplication overflows that can
|
||||
* only happen on 64-bit systems:
|
||||
*/
|
||||
if ((txc->modes & ADJ_FREQUENCY) && (BITS_PER_LONG == 64)) {
|
||||
if (LLONG_MIN / PPM_SCALE > txc->freq)
|
||||
return -EINVAL;
|
||||
if (LLONG_MAX / PPM_SCALE < txc->freq)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* adjtimex mainly allows reading (and writing, if superuser) of
|
||||
* kernel time-keeping variables. used by xntpd.
|
||||
|
@ -7,7 +7,6 @@ extern void ntp_clear(void);
|
||||
extern u64 ntp_tick_length(void);
|
||||
extern ktime_t ntp_get_next_leap(void);
|
||||
extern int second_overflow(time64_t secs);
|
||||
extern int ntp_validate_timex(struct timex *);
|
||||
extern int __do_adjtimex(struct timex *, struct timespec64 *, s32 *);
|
||||
extern void __hardpps(const struct timespec64 *, const struct timespec64 *);
|
||||
#endif /* _LINUX_NTP_INTERNAL_H */
|
||||
|
@ -157,40 +157,6 @@ SYSCALL_DEFINE2(gettimeofday, struct timeval __user *, tv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Indicates if there is an offset between the system clock and the hardware
|
||||
* clock/persistent clock/rtc.
|
||||
*/
|
||||
int persistent_clock_is_local;
|
||||
|
||||
/*
|
||||
* Adjust the time obtained from the CMOS to be UTC time instead of
|
||||
* local time.
|
||||
*
|
||||
* This is ugly, but preferable to the alternatives. Otherwise we
|
||||
* would either need to write a program to do it in /etc/rc (and risk
|
||||
* confusion if the program gets run more than once; it would also be
|
||||
* hard to make the program warp the clock precisely n hours) or
|
||||
* compile in the timezone information into the kernel. Bad, bad....
|
||||
*
|
||||
* - TYT, 1992-01-01
|
||||
*
|
||||
* The best thing to do is to keep the CMOS clock in universal time (UTC)
|
||||
* as real UNIX machines always do it. This avoids all headaches about
|
||||
* daylight saving times and warping kernel clocks.
|
||||
*/
|
||||
static inline void warp_clock(void)
|
||||
{
|
||||
if (sys_tz.tz_minuteswest != 0) {
|
||||
struct timespec adjust;
|
||||
|
||||
persistent_clock_is_local = 1;
|
||||
adjust.tv_sec = sys_tz.tz_minuteswest * 60;
|
||||
adjust.tv_nsec = 0;
|
||||
timekeeping_inject_offset(&adjust);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* In case for some reason the CMOS clock has not already been running
|
||||
* in UTC, but in some local time: The first time we set the timezone,
|
||||
@ -224,7 +190,7 @@ int do_sys_settimeofday64(const struct timespec64 *tv, const struct timezone *tz
|
||||
if (firsttime) {
|
||||
firsttime = 0;
|
||||
if (!tv)
|
||||
warp_clock();
|
||||
timekeeping_warp_clock();
|
||||
}
|
||||
}
|
||||
if (tv)
|
||||
|
@ -1258,13 +1258,39 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL(do_settimeofday64);
|
||||
|
||||
/*
|
||||
* Validates if a timespec/timeval used to inject a time offset is valid.
|
||||
* Offsets can be postive or negative. The value of the timeval/timespec
|
||||
* is the sum of its fields, but *NOTE*: the field tv_usec/tv_nsec must
|
||||
* always be non-negative.
|
||||
*/
|
||||
static inline bool timeval_inject_offset_valid(const struct timeval *tv)
|
||||
{
|
||||
/* We don't check the tv_sec as it can be positive or negative */
|
||||
|
||||
/* Can't have more microseconds then a second */
|
||||
if (tv->tv_usec < 0 || tv->tv_usec >= USEC_PER_SEC)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool timespec_inject_offset_valid(const struct timespec *ts)
|
||||
{
|
||||
/* We don't check the tv_sec as it can be positive or negative */
|
||||
|
||||
/* Can't have more nanoseconds then a second */
|
||||
if (ts->tv_nsec < 0 || ts->tv_nsec >= NSEC_PER_SEC)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
static int timekeeping_inject_offset(struct timespec *ts)
|
||||
{
|
||||
struct timekeeper *tk = &tk_core.timekeeper;
|
||||
unsigned long flags;
|
||||
@ -1303,7 +1329,40 @@ error: /* even if we error out, we forwarded the time, so call update */
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(timekeeping_inject_offset);
|
||||
|
||||
/*
|
||||
* Indicates if there is an offset between the system clock and the hardware
|
||||
* clock/persistent clock/rtc.
|
||||
*/
|
||||
int persistent_clock_is_local;
|
||||
|
||||
/*
|
||||
* Adjust the time obtained from the CMOS to be UTC time instead of
|
||||
* local time.
|
||||
*
|
||||
* This is ugly, but preferable to the alternatives. Otherwise we
|
||||
* would either need to write a program to do it in /etc/rc (and risk
|
||||
* confusion if the program gets run more than once; it would also be
|
||||
* hard to make the program warp the clock precisely n hours) or
|
||||
* compile in the timezone information into the kernel. Bad, bad....
|
||||
*
|
||||
* - TYT, 1992-01-01
|
||||
*
|
||||
* The best thing to do is to keep the CMOS clock in universal time (UTC)
|
||||
* as real UNIX machines always do it. This avoids all headaches about
|
||||
* daylight saving times and warping kernel clocks.
|
||||
*/
|
||||
void timekeeping_warp_clock(void)
|
||||
{
|
||||
if (sys_tz.tz_minuteswest != 0) {
|
||||
struct timespec adjust;
|
||||
|
||||
persistent_clock_is_local = 1;
|
||||
adjust.tv_sec = sys_tz.tz_minuteswest * 60;
|
||||
adjust.tv_nsec = 0;
|
||||
timekeeping_inject_offset(&adjust);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* __timekeeping_set_tai_offset - Sets the TAI offset from UTC and monotonic
|
||||
@ -2247,6 +2306,66 @@ ktime_t ktime_get_update_offsets_now(unsigned int *cwsseq, ktime_t *offs_real,
|
||||
return base;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntp_validate_timex - Ensures the timex is ok for use in do_adjtimex
|
||||
*/
|
||||
static int ntp_validate_timex(struct timex *txc)
|
||||
{
|
||||
if (txc->modes & ADJ_ADJTIME) {
|
||||
/* singleshot must not be used with any other mode bits */
|
||||
if (!(txc->modes & ADJ_OFFSET_SINGLESHOT))
|
||||
return -EINVAL;
|
||||
if (!(txc->modes & ADJ_OFFSET_READONLY) &&
|
||||
!capable(CAP_SYS_TIME))
|
||||
return -EPERM;
|
||||
} else {
|
||||
/* In order to modify anything, you gotta be super-user! */
|
||||
if (txc->modes && !capable(CAP_SYS_TIME))
|
||||
return -EPERM;
|
||||
/*
|
||||
* if the quartz is off by more than 10% then
|
||||
* something is VERY wrong!
|
||||
*/
|
||||
if (txc->modes & ADJ_TICK &&
|
||||
(txc->tick < 900000/USER_HZ ||
|
||||
txc->tick > 1100000/USER_HZ))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (txc->modes & ADJ_SETOFFSET) {
|
||||
/* In order to inject time, you gotta be super-user! */
|
||||
if (!capable(CAP_SYS_TIME))
|
||||
return -EPERM;
|
||||
|
||||
if (txc->modes & ADJ_NANO) {
|
||||
struct timespec ts;
|
||||
|
||||
ts.tv_sec = txc->time.tv_sec;
|
||||
ts.tv_nsec = txc->time.tv_usec;
|
||||
if (!timespec_inject_offset_valid(&ts))
|
||||
return -EINVAL;
|
||||
|
||||
} else {
|
||||
if (!timeval_inject_offset_valid(&txc->time))
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for potential multiplication overflows that can
|
||||
* only happen on 64-bit systems:
|
||||
*/
|
||||
if ((txc->modes & ADJ_FREQUENCY) && (BITS_PER_LONG == 64)) {
|
||||
if (LLONG_MIN / PPM_SCALE > txc->freq)
|
||||
return -EINVAL;
|
||||
if (LLONG_MAX / PPM_SCALE < txc->freq)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* do_adjtimex() - Accessor function to NTP __do_adjtimex function
|
||||
*/
|
||||
|
@ -10,7 +10,7 @@ extern ktime_t ktime_get_update_offsets_now(unsigned int *cwsseq,
|
||||
|
||||
extern int timekeeping_valid_for_hres(void);
|
||||
extern u64 timekeeping_max_deferment(void);
|
||||
extern int timekeeping_inject_offset(struct timespec *ts);
|
||||
extern void timekeeping_warp_clock(void);
|
||||
extern int timekeeping_suspend(void);
|
||||
extern void timekeeping_resume(void);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user