diff options
Diffstat (limited to 'kernel/time/ntp.c')
-rw-r--r-- | kernel/time/ntp.c | 93 |
1 files changed, 48 insertions, 45 deletions
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index 9c114b726ab3..8ff15e5d486b 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c | |||
@@ -10,13 +10,13 @@ | |||
10 | 10 | ||
11 | #include <linux/mm.h> | 11 | #include <linux/mm.h> |
12 | #include <linux/time.h> | 12 | #include <linux/time.h> |
13 | #include <linux/timer.h> | ||
14 | #include <linux/timex.h> | 13 | #include <linux/timex.h> |
15 | #include <linux/jiffies.h> | 14 | #include <linux/jiffies.h> |
16 | #include <linux/hrtimer.h> | 15 | #include <linux/hrtimer.h> |
17 | #include <linux/capability.h> | 16 | #include <linux/capability.h> |
18 | #include <linux/math64.h> | 17 | #include <linux/math64.h> |
19 | #include <linux/clocksource.h> | 18 | #include <linux/clocksource.h> |
19 | #include <linux/workqueue.h> | ||
20 | #include <asm/timex.h> | 20 | #include <asm/timex.h> |
21 | 21 | ||
22 | /* | 22 | /* |
@@ -217,11 +217,11 @@ void second_overflow(void) | |||
217 | /* Disable the cmos update - used by virtualization and embedded */ | 217 | /* Disable the cmos update - used by virtualization and embedded */ |
218 | int no_sync_cmos_clock __read_mostly; | 218 | int no_sync_cmos_clock __read_mostly; |
219 | 219 | ||
220 | static void sync_cmos_clock(unsigned long dummy); | 220 | static void sync_cmos_clock(struct work_struct *work); |
221 | 221 | ||
222 | static DEFINE_TIMER(sync_cmos_timer, sync_cmos_clock, 0, 0); | 222 | static DECLARE_DELAYED_WORK(sync_cmos_work, sync_cmos_clock); |
223 | 223 | ||
224 | static void sync_cmos_clock(unsigned long dummy) | 224 | static void sync_cmos_clock(struct work_struct *work) |
225 | { | 225 | { |
226 | struct timespec now, next; | 226 | struct timespec now, next; |
227 | int fail = 1; | 227 | int fail = 1; |
@@ -257,13 +257,13 @@ static void sync_cmos_clock(unsigned long dummy) | |||
257 | next.tv_sec++; | 257 | next.tv_sec++; |
258 | next.tv_nsec -= NSEC_PER_SEC; | 258 | next.tv_nsec -= NSEC_PER_SEC; |
259 | } | 259 | } |
260 | mod_timer(&sync_cmos_timer, jiffies + timespec_to_jiffies(&next)); | 260 | schedule_delayed_work(&sync_cmos_work, timespec_to_jiffies(&next)); |
261 | } | 261 | } |
262 | 262 | ||
263 | static void notify_cmos_timer(void) | 263 | static void notify_cmos_timer(void) |
264 | { | 264 | { |
265 | if (!no_sync_cmos_clock) | 265 | if (!no_sync_cmos_clock) |
266 | mod_timer(&sync_cmos_timer, jiffies + 1); | 266 | schedule_delayed_work(&sync_cmos_work, 0); |
267 | } | 267 | } |
268 | 268 | ||
269 | #else | 269 | #else |
@@ -276,38 +276,50 @@ static inline void notify_cmos_timer(void) { } | |||
276 | int do_adjtimex(struct timex *txc) | 276 | int do_adjtimex(struct timex *txc) |
277 | { | 277 | { |
278 | struct timespec ts; | 278 | struct timespec ts; |
279 | long save_adjust, sec; | ||
280 | int result; | 279 | int result; |
281 | 280 | ||
282 | /* In order to modify anything, you gotta be super-user! */ | 281 | /* Validate the data before disabling interrupts */ |
283 | if (txc->modes && !capable(CAP_SYS_TIME)) | 282 | if (txc->modes & ADJ_ADJTIME) { |
284 | return -EPERM; | ||
285 | |||
286 | /* Now we validate the data before disabling interrupts */ | ||
287 | |||
288 | if ((txc->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT) { | ||
289 | /* singleshot must not be used with any other mode bits */ | 283 | /* singleshot must not be used with any other mode bits */ |
290 | if (txc->modes & ~ADJ_OFFSET_SS_READ) | 284 | if (!(txc->modes & ADJ_OFFSET_SINGLESHOT)) |
291 | return -EINVAL; | 285 | return -EINVAL; |
286 | if (!(txc->modes & ADJ_OFFSET_READONLY) && | ||
287 | !capable(CAP_SYS_TIME)) | ||
288 | return -EPERM; | ||
289 | } else { | ||
290 | /* In order to modify anything, you gotta be super-user! */ | ||
291 | if (txc->modes && !capable(CAP_SYS_TIME)) | ||
292 | return -EPERM; | ||
293 | |||
294 | /* if the quartz is off by more than 10% something is VERY wrong! */ | ||
295 | if (txc->modes & ADJ_TICK && | ||
296 | (txc->tick < 900000/USER_HZ || | ||
297 | txc->tick > 1100000/USER_HZ)) | ||
298 | return -EINVAL; | ||
299 | |||
300 | if (txc->modes & ADJ_STATUS && time_state != TIME_OK) | ||
301 | hrtimer_cancel(&leap_timer); | ||
292 | } | 302 | } |
293 | 303 | ||
294 | /* if the quartz is off by more than 10% something is VERY wrong ! */ | ||
295 | if (txc->modes & ADJ_TICK) | ||
296 | if (txc->tick < 900000/USER_HZ || | ||
297 | txc->tick > 1100000/USER_HZ) | ||
298 | return -EINVAL; | ||
299 | |||
300 | if (time_state != TIME_OK && txc->modes & ADJ_STATUS) | ||
301 | hrtimer_cancel(&leap_timer); | ||
302 | getnstimeofday(&ts); | 304 | getnstimeofday(&ts); |
303 | 305 | ||
304 | write_seqlock_irq(&xtime_lock); | 306 | write_seqlock_irq(&xtime_lock); |
305 | 307 | ||
306 | /* Save for later - semantics of adjtime is to return old value */ | ||
307 | save_adjust = time_adjust; | ||
308 | |||
309 | /* If there are input parameters, then process them */ | 308 | /* If there are input parameters, then process them */ |
309 | if (txc->modes & ADJ_ADJTIME) { | ||
310 | long save_adjust = time_adjust; | ||
311 | |||
312 | if (!(txc->modes & ADJ_OFFSET_READONLY)) { | ||
313 | /* adjtime() is independent from ntp_adjtime() */ | ||
314 | time_adjust = txc->offset; | ||
315 | ntp_update_frequency(); | ||
316 | } | ||
317 | txc->offset = save_adjust; | ||
318 | goto adj_done; | ||
319 | } | ||
310 | if (txc->modes) { | 320 | if (txc->modes) { |
321 | long sec; | ||
322 | |||
311 | if (txc->modes & ADJ_STATUS) { | 323 | if (txc->modes & ADJ_STATUS) { |
312 | if ((time_status & STA_PLL) && | 324 | if ((time_status & STA_PLL) && |
313 | !(txc->status & STA_PLL)) { | 325 | !(txc->status & STA_PLL)) { |
@@ -374,13 +386,8 @@ int do_adjtimex(struct timex *txc) | |||
374 | if (txc->modes & ADJ_TAI && txc->constant > 0) | 386 | if (txc->modes & ADJ_TAI && txc->constant > 0) |
375 | time_tai = txc->constant; | 387 | time_tai = txc->constant; |
376 | 388 | ||
377 | if (txc->modes & ADJ_OFFSET) { | 389 | if (txc->modes & ADJ_OFFSET) |
378 | if (txc->modes == ADJ_OFFSET_SINGLESHOT) | 390 | ntp_update_offset(txc->offset); |
379 | /* adjtime() is independent from ntp_adjtime() */ | ||
380 | time_adjust = txc->offset; | ||
381 | else | ||
382 | ntp_update_offset(txc->offset); | ||
383 | } | ||
384 | if (txc->modes & ADJ_TICK) | 391 | if (txc->modes & ADJ_TICK) |
385 | tick_usec = txc->tick; | 392 | tick_usec = txc->tick; |
386 | 393 | ||
@@ -388,22 +395,18 @@ int do_adjtimex(struct timex *txc) | |||
388 | ntp_update_frequency(); | 395 | ntp_update_frequency(); |
389 | } | 396 | } |
390 | 397 | ||
398 | txc->offset = shift_right(time_offset * NTP_INTERVAL_FREQ, | ||
399 | NTP_SCALE_SHIFT); | ||
400 | if (!(time_status & STA_NANO)) | ||
401 | txc->offset /= NSEC_PER_USEC; | ||
402 | |||
403 | adj_done: | ||
391 | result = time_state; /* mostly `TIME_OK' */ | 404 | result = time_state; /* mostly `TIME_OK' */ |
392 | if (time_status & (STA_UNSYNC|STA_CLOCKERR)) | 405 | if (time_status & (STA_UNSYNC|STA_CLOCKERR)) |
393 | result = TIME_ERROR; | 406 | result = TIME_ERROR; |
394 | 407 | ||
395 | if ((txc->modes == ADJ_OFFSET_SINGLESHOT) || | 408 | txc->freq = shift_right((time_freq >> PPM_SCALE_INV_SHIFT) * |
396 | (txc->modes == ADJ_OFFSET_SS_READ)) | 409 | (s64)PPM_SCALE_INV, NTP_SCALE_SHIFT); |
397 | txc->offset = save_adjust; | ||
398 | else { | ||
399 | txc->offset = shift_right(time_offset * NTP_INTERVAL_FREQ, | ||
400 | NTP_SCALE_SHIFT); | ||
401 | if (!(time_status & STA_NANO)) | ||
402 | txc->offset /= NSEC_PER_USEC; | ||
403 | } | ||
404 | txc->freq = shift_right((s32)(time_freq >> PPM_SCALE_INV_SHIFT) * | ||
405 | (s64)PPM_SCALE_INV, | ||
406 | NTP_SCALE_SHIFT); | ||
407 | txc->maxerror = time_maxerror; | 410 | txc->maxerror = time_maxerror; |
408 | txc->esterror = time_esterror; | 411 | txc->esterror = time_esterror; |
409 | txc->status = time_status; | 412 | txc->status = time_status; |