diff options
| -rw-r--r-- | include/linux/timex.h | 9 | ||||
| -rw-r--r-- | kernel/time/ntp.c | 76 |
2 files changed, 48 insertions, 37 deletions
diff --git a/include/linux/timex.h b/include/linux/timex.h index fc6035d29d56..c00bcdd3ae42 100644 --- a/include/linux/timex.h +++ b/include/linux/timex.h | |||
| @@ -141,8 +141,15 @@ struct timex { | |||
| 141 | #define ADJ_MICRO 0x1000 /* select microsecond resolution */ | 141 | #define ADJ_MICRO 0x1000 /* select microsecond resolution */ |
| 142 | #define ADJ_NANO 0x2000 /* select nanosecond resolution */ | 142 | #define ADJ_NANO 0x2000 /* select nanosecond resolution */ |
| 143 | #define ADJ_TICK 0x4000 /* tick value */ | 143 | #define ADJ_TICK 0x4000 /* tick value */ |
| 144 | |||
| 145 | #ifdef __KERNEL__ | ||
| 146 | #define ADJ_ADJTIME 0x8000 /* switch between adjtime/adjtimex modes */ | ||
| 147 | #define ADJ_OFFSET_SINGLESHOT 0x0001 /* old-fashioned adjtime */ | ||
| 148 | #define ADJ_OFFSET_READONLY 0x2000 /* read-only adjtime */ | ||
| 149 | #else | ||
| 144 | #define ADJ_OFFSET_SINGLESHOT 0x8001 /* old-fashioned adjtime */ | 150 | #define ADJ_OFFSET_SINGLESHOT 0x8001 /* old-fashioned adjtime */ |
| 145 | #define ADJ_OFFSET_SS_READ 0xa001 /* read-only adjtime */ | 151 | #define ADJ_OFFSET_SS_READ 0xa001 /* read-only adjtime */ |
| 152 | #endif | ||
| 146 | 153 | ||
| 147 | /* xntp 3.4 compatibility names */ | 154 | /* xntp 3.4 compatibility names */ |
| 148 | #define MOD_OFFSET ADJ_OFFSET | 155 | #define MOD_OFFSET ADJ_OFFSET |
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index 5125ddd8196b..c6921aa1a42a 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c | |||
| @@ -277,38 +277,50 @@ static inline void notify_cmos_timer(void) { } | |||
| 277 | int do_adjtimex(struct timex *txc) | 277 | int do_adjtimex(struct timex *txc) |
| 278 | { | 278 | { |
| 279 | struct timespec ts; | 279 | struct timespec ts; |
| 280 | long save_adjust, sec; | ||
| 281 | int result; | 280 | int result; |
| 282 | 281 | ||
| 283 | /* In order to modify anything, you gotta be super-user! */ | 282 | /* Validate the data before disabling interrupts */ |
| 284 | if (txc->modes && !capable(CAP_SYS_TIME)) | 283 | if (txc->modes & ADJ_ADJTIME) { |
| 285 | return -EPERM; | ||
| 286 | |||
| 287 | /* Now we validate the data before disabling interrupts */ | ||
| 288 | |||
| 289 | if ((txc->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT) { | ||
| 290 | /* singleshot must not be used with any other mode bits */ | 284 | /* singleshot must not be used with any other mode bits */ |
| 291 | if (txc->modes & ~ADJ_OFFSET_SS_READ) | 285 | if (!(txc->modes & ADJ_OFFSET_SINGLESHOT)) |
| 292 | return -EINVAL; | 286 | return -EINVAL; |
| 287 | if (!(txc->modes & ADJ_OFFSET_READONLY) && | ||
| 288 | !capable(CAP_SYS_TIME)) | ||
| 289 | return -EPERM; | ||
| 290 | } else { | ||
| 291 | /* In order to modify anything, you gotta be super-user! */ | ||
| 292 | if (txc->modes && !capable(CAP_SYS_TIME)) | ||
| 293 | return -EPERM; | ||
| 294 | |||
| 295 | /* if the quartz is off by more than 10% something is VERY wrong! */ | ||
| 296 | if (txc->modes & ADJ_TICK && | ||
| 297 | (txc->tick < 900000/USER_HZ || | ||
| 298 | txc->tick > 1100000/USER_HZ)) | ||
| 299 | return -EINVAL; | ||
| 300 | |||
| 301 | if (txc->modes & ADJ_STATUS && time_state != TIME_OK) | ||
| 302 | hrtimer_cancel(&leap_timer); | ||
| 293 | } | 303 | } |
| 294 | 304 | ||
| 295 | /* if the quartz is off by more than 10% something is VERY wrong ! */ | ||
| 296 | if (txc->modes & ADJ_TICK) | ||
| 297 | if (txc->tick < 900000/USER_HZ || | ||
| 298 | txc->tick > 1100000/USER_HZ) | ||
| 299 | return -EINVAL; | ||
| 300 | |||
| 301 | if (time_state != TIME_OK && txc->modes & ADJ_STATUS) | ||
| 302 | hrtimer_cancel(&leap_timer); | ||
| 303 | getnstimeofday(&ts); | 305 | getnstimeofday(&ts); |
| 304 | 306 | ||
| 305 | write_seqlock_irq(&xtime_lock); | 307 | write_seqlock_irq(&xtime_lock); |
| 306 | 308 | ||
| 307 | /* Save for later - semantics of adjtime is to return old value */ | ||
| 308 | save_adjust = time_adjust; | ||
| 309 | |||
| 310 | /* If there are input parameters, then process them */ | 309 | /* If there are input parameters, then process them */ |
| 310 | if (txc->modes & ADJ_ADJTIME) { | ||
| 311 | long save_adjust = time_adjust; | ||
| 312 | |||
| 313 | if (!(txc->modes & ADJ_OFFSET_READONLY)) { | ||
| 314 | /* adjtime() is independent from ntp_adjtime() */ | ||
| 315 | time_adjust = txc->offset; | ||
| 316 | ntp_update_frequency(); | ||
| 317 | } | ||
| 318 | txc->offset = save_adjust; | ||
| 319 | goto adj_done; | ||
| 320 | } | ||
| 311 | if (txc->modes) { | 321 | if (txc->modes) { |
| 322 | long sec; | ||
| 323 | |||
| 312 | if (txc->modes & ADJ_STATUS) { | 324 | if (txc->modes & ADJ_STATUS) { |
| 313 | if ((time_status & STA_PLL) && | 325 | if ((time_status & STA_PLL) && |
| 314 | !(txc->status & STA_PLL)) { | 326 | !(txc->status & STA_PLL)) { |
| @@ -375,13 +387,8 @@ int do_adjtimex(struct timex *txc) | |||
| 375 | if (txc->modes & ADJ_TAI && txc->constant > 0) | 387 | if (txc->modes & ADJ_TAI && txc->constant > 0) |
| 376 | time_tai = txc->constant; | 388 | time_tai = txc->constant; |
| 377 | 389 | ||
| 378 | if (txc->modes & ADJ_OFFSET) { | 390 | if (txc->modes & ADJ_OFFSET) |
| 379 | if (txc->modes == ADJ_OFFSET_SINGLESHOT) | 391 | ntp_update_offset(txc->offset); |
| 380 | /* adjtime() is independent from ntp_adjtime() */ | ||
| 381 | time_adjust = txc->offset; | ||
| 382 | else | ||
| 383 | ntp_update_offset(txc->offset); | ||
| 384 | } | ||
| 385 | if (txc->modes & ADJ_TICK) | 392 | if (txc->modes & ADJ_TICK) |
| 386 | tick_usec = txc->tick; | 393 | tick_usec = txc->tick; |
| 387 | 394 | ||
| @@ -389,19 +396,16 @@ int do_adjtimex(struct timex *txc) | |||
| 389 | ntp_update_frequency(); | 396 | ntp_update_frequency(); |
| 390 | } | 397 | } |
| 391 | 398 | ||
| 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 | adj_done: | ||
| 392 | result = time_state; /* mostly `TIME_OK' */ | 405 | result = time_state; /* mostly `TIME_OK' */ |
| 393 | if (time_status & (STA_UNSYNC|STA_CLOCKERR)) | 406 | if (time_status & (STA_UNSYNC|STA_CLOCKERR)) |
| 394 | result = TIME_ERROR; | 407 | result = TIME_ERROR; |
| 395 | 408 | ||
| 396 | if ((txc->modes == ADJ_OFFSET_SINGLESHOT) || | ||
| 397 | (txc->modes == ADJ_OFFSET_SS_READ)) | ||
| 398 | txc->offset = save_adjust; | ||
| 399 | else { | ||
| 400 | txc->offset = shift_right(time_offset * NTP_INTERVAL_FREQ, | ||
| 401 | NTP_SCALE_SHIFT); | ||
| 402 | if (!(time_status & STA_NANO)) | ||
| 403 | txc->offset /= NSEC_PER_USEC; | ||
| 404 | } | ||
| 405 | txc->freq = shift_right((s32)(time_freq >> PPM_SCALE_INV_SHIFT) * | 409 | txc->freq = shift_right((s32)(time_freq >> PPM_SCALE_INV_SHIFT) * |
| 406 | (s64)PPM_SCALE_INV, | 410 | (s64)PPM_SCALE_INV, |
| 407 | NTP_SCALE_SHIFT); | 411 | NTP_SCALE_SHIFT); |
