diff options
| author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2011-08-23 09:29:42 -0400 |
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2011-09-08 05:10:56 -0400 |
| commit | d1748302f70be7469809809283fe164156a34231 (patch) | |
| tree | c81137f4126f2fe1451c28441415c1cd0fa0f7cd /kernel | |
| parent | 29c158e81c733ac7d6a75c5ee929f34fb9f92983 (diff) | |
clockevents: Make minimum delay adjustments configurable
The automatic increase of the min_delta_ns of a clockevents device
should be done in the clockevents code as the minimum delay is an
attribute of the clockevents device.
In addition not all architectures want the automatic adjustment, on a
massively virtualized system it can happen that the programming of a
clock event fails several times in a row because the virtual cpu has
been rescheduled quickly enough. In that case the minimum delay will
erroneously be increased with no way back. The new config symbol
GENERIC_CLOCKEVENTS_MIN_ADJUST is used to enable the automatic
adjustment. The config option is selected only for x86.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: john stultz <johnstul@us.ibm.com>
Link: http://lkml.kernel.org/r/20110823133142.494157493@de.ibm.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/time/Kconfig | 2 | ||||
| -rw-r--r-- | kernel/time/clockevents.c | 125 | ||||
| -rw-r--r-- | kernel/time/tick-broadcast.c | 4 | ||||
| -rw-r--r-- | kernel/time/tick-common.c | 4 | ||||
| -rw-r--r-- | kernel/time/tick-internal.h | 2 | ||||
| -rw-r--r-- | kernel/time/tick-oneshot.c | 77 |
6 files changed, 121 insertions, 93 deletions
diff --git a/kernel/time/Kconfig b/kernel/time/Kconfig index f06a8a365648..b26c2228fe92 100644 --- a/kernel/time/Kconfig +++ b/kernel/time/Kconfig | |||
| @@ -27,3 +27,5 @@ config GENERIC_CLOCKEVENTS_BUILD | |||
| 27 | default y | 27 | default y |
| 28 | depends on GENERIC_CLOCKEVENTS || GENERIC_CLOCKEVENTS_MIGR | 28 | depends on GENERIC_CLOCKEVENTS || GENERIC_CLOCKEVENTS_MIGR |
| 29 | 29 | ||
| 30 | config GENERIC_CLOCKEVENTS_MIN_ADJUST | ||
| 31 | bool | ||
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index e4c699dfa4e8..713ef94eceef 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c | |||
| @@ -94,42 +94,139 @@ void clockevents_shutdown(struct clock_event_device *dev) | |||
| 94 | dev->next_event.tv64 = KTIME_MAX; | 94 | dev->next_event.tv64 = KTIME_MAX; |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | #ifdef CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST | ||
| 98 | |||
| 99 | /* Limit min_delta to a jiffie */ | ||
| 100 | #define MIN_DELTA_LIMIT (NSEC_PER_SEC / HZ) | ||
| 101 | |||
| 102 | /** | ||
| 103 | * clockevents_increase_min_delta - raise minimum delta of a clock event device | ||
| 104 | * @dev: device to increase the minimum delta | ||
| 105 | * | ||
| 106 | * Returns 0 on success, -ETIME when the minimum delta reached the limit. | ||
| 107 | */ | ||
| 108 | static int clockevents_increase_min_delta(struct clock_event_device *dev) | ||
| 109 | { | ||
| 110 | /* Nothing to do if we already reached the limit */ | ||
| 111 | if (dev->min_delta_ns >= MIN_DELTA_LIMIT) { | ||
| 112 | printk(KERN_WARNING "CE: Reprogramming failure. Giving up\n"); | ||
| 113 | dev->next_event.tv64 = KTIME_MAX; | ||
| 114 | return -ETIME; | ||
| 115 | } | ||
| 116 | |||
| 117 | if (dev->min_delta_ns < 5000) | ||
| 118 | dev->min_delta_ns = 5000; | ||
| 119 | else | ||
| 120 | dev->min_delta_ns += dev->min_delta_ns >> 1; | ||
| 121 | |||
| 122 | if (dev->min_delta_ns > MIN_DELTA_LIMIT) | ||
| 123 | dev->min_delta_ns = MIN_DELTA_LIMIT; | ||
| 124 | |||
| 125 | printk(KERN_WARNING "CE: %s increased min_delta_ns to %llu nsec\n", | ||
| 126 | dev->name ? dev->name : "?", | ||
| 127 | (unsigned long long) dev->min_delta_ns); | ||
| 128 | return 0; | ||
| 129 | } | ||
| 130 | |||
| 131 | /** | ||
| 132 | * clockevents_program_min_delta - Set clock event device to the minimum delay. | ||
| 133 | * @dev: device to program | ||
| 134 | * | ||
| 135 | * Returns 0 on success, -ETIME when the retry loop failed. | ||
| 136 | */ | ||
| 137 | static int clockevents_program_min_delta(struct clock_event_device *dev) | ||
| 138 | { | ||
| 139 | unsigned long long clc; | ||
| 140 | int64_t delta; | ||
| 141 | int i; | ||
| 142 | |||
| 143 | for (i = 0;;) { | ||
| 144 | delta = dev->min_delta_ns; | ||
| 145 | dev->next_event = ktime_add_ns(ktime_get(), delta); | ||
| 146 | |||
| 147 | if (dev->mode == CLOCK_EVT_MODE_SHUTDOWN) | ||
| 148 | return 0; | ||
| 149 | |||
| 150 | dev->retries++; | ||
| 151 | clc = ((unsigned long long) delta * dev->mult) >> dev->shift; | ||
| 152 | if (dev->set_next_event((unsigned long) clc, dev) == 0) | ||
| 153 | return 0; | ||
| 154 | |||
| 155 | if (++i > 2) { | ||
| 156 | /* | ||
| 157 | * We tried 3 times to program the device with the | ||
| 158 | * given min_delta_ns. Try to increase the minimum | ||
| 159 | * delta, if that fails as well get out of here. | ||
| 160 | */ | ||
| 161 | if (clockevents_increase_min_delta(dev)) | ||
| 162 | return -ETIME; | ||
| 163 | i = 0; | ||
| 164 | } | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | #else /* CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST */ | ||
| 169 | |||
| 170 | /** | ||
| 171 | * clockevents_program_min_delta - Set clock event device to the minimum delay. | ||
| 172 | * @dev: device to program | ||
| 173 | * | ||
| 174 | * Returns 0 on success, -ETIME when the retry loop failed. | ||
| 175 | */ | ||
| 176 | static int clockevents_program_min_delta(struct clock_event_device *dev) | ||
| 177 | { | ||
| 178 | unsigned long long clc; | ||
| 179 | int64_t delta; | ||
| 180 | |||
| 181 | delta = dev->min_delta_ns; | ||
| 182 | dev->next_event = ktime_add_ns(ktime_get(), delta); | ||
| 183 | |||
| 184 | if (dev->mode == CLOCK_EVT_MODE_SHUTDOWN) | ||
| 185 | return 0; | ||
| 186 | |||
| 187 | dev->retries++; | ||
| 188 | clc = ((unsigned long long) delta * dev->mult) >> dev->shift; | ||
| 189 | return dev->set_next_event((unsigned long) clc, dev); | ||
| 190 | } | ||
| 191 | |||
| 192 | #endif /* CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST */ | ||
| 193 | |||
| 97 | /** | 194 | /** |
| 98 | * clockevents_program_event - Reprogram the clock event device. | 195 | * clockevents_program_event - Reprogram the clock event device. |
| 196 | * @dev: device to program | ||
| 99 | * @expires: absolute expiry time (monotonic clock) | 197 | * @expires: absolute expiry time (monotonic clock) |
| 198 | * @force: program minimum delay if expires can not be set | ||
| 100 | * | 199 | * |
| 101 | * Returns 0 on success, -ETIME when the event is in the past. | 200 | * Returns 0 on success, -ETIME when the event is in the past. |
| 102 | */ | 201 | */ |
| 103 | int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, | 202 | int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, |
| 104 | ktime_t now) | 203 | bool force) |
| 105 | { | 204 | { |
| 106 | unsigned long long clc; | 205 | unsigned long long clc; |
| 107 | int64_t delta; | 206 | int64_t delta; |
| 207 | int rc; | ||
| 108 | 208 | ||
| 109 | if (unlikely(expires.tv64 < 0)) { | 209 | if (unlikely(expires.tv64 < 0)) { |
| 110 | WARN_ON_ONCE(1); | 210 | WARN_ON_ONCE(1); |
| 111 | return -ETIME; | 211 | return -ETIME; |
| 112 | } | 212 | } |
| 113 | 213 | ||
| 114 | delta = ktime_to_ns(ktime_sub(expires, now)); | ||
| 115 | |||
| 116 | if (delta <= 0) | ||
| 117 | return -ETIME; | ||
| 118 | |||
| 119 | dev->next_event = expires; | 214 | dev->next_event = expires; |
| 120 | 215 | ||
| 121 | if (dev->mode == CLOCK_EVT_MODE_SHUTDOWN) | 216 | if (dev->mode == CLOCK_EVT_MODE_SHUTDOWN) |
| 122 | return 0; | 217 | return 0; |
| 123 | 218 | ||
| 124 | if (delta > dev->max_delta_ns) | 219 | delta = ktime_to_ns(ktime_sub(expires, ktime_get())); |
| 125 | delta = dev->max_delta_ns; | 220 | if (delta <= 0) |
| 126 | if (delta < dev->min_delta_ns) | 221 | return force ? clockevents_program_min_delta(dev) : -ETIME; |
| 127 | delta = dev->min_delta_ns; | ||
| 128 | 222 | ||
| 129 | clc = delta * dev->mult; | 223 | delta = min(delta, (int64_t) dev->max_delta_ns); |
| 130 | clc >>= dev->shift; | 224 | delta = max(delta, (int64_t) dev->min_delta_ns); |
| 131 | 225 | ||
| 132 | return dev->set_next_event((unsigned long) clc, dev); | 226 | clc = ((unsigned long long) delta * dev->mult) >> dev->shift; |
| 227 | rc = dev->set_next_event((unsigned long) clc, dev); | ||
| 228 | |||
| 229 | return (rc && force) ? clockevents_program_min_delta(dev) : rc; | ||
| 133 | } | 230 | } |
| 134 | 231 | ||
| 135 | /** | 232 | /** |
| @@ -258,7 +355,7 @@ int clockevents_update_freq(struct clock_event_device *dev, u32 freq) | |||
| 258 | if (dev->mode != CLOCK_EVT_MODE_ONESHOT) | 355 | if (dev->mode != CLOCK_EVT_MODE_ONESHOT) |
| 259 | return 0; | 356 | return 0; |
| 260 | 357 | ||
| 261 | return clockevents_program_event(dev, dev->next_event, ktime_get()); | 358 | return clockevents_program_event(dev, dev->next_event, false); |
| 262 | } | 359 | } |
| 263 | 360 | ||
| 264 | /* | 361 | /* |
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index c7218d132738..f954282d9a82 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c | |||
| @@ -194,7 +194,7 @@ static void tick_handle_periodic_broadcast(struct clock_event_device *dev) | |||
| 194 | for (next = dev->next_event; ;) { | 194 | for (next = dev->next_event; ;) { |
| 195 | next = ktime_add(next, tick_period); | 195 | next = ktime_add(next, tick_period); |
| 196 | 196 | ||
| 197 | if (!clockevents_program_event(dev, next, ktime_get())) | 197 | if (!clockevents_program_event(dev, next, false)) |
| 198 | return; | 198 | return; |
| 199 | tick_do_periodic_broadcast(); | 199 | tick_do_periodic_broadcast(); |
| 200 | } | 200 | } |
| @@ -373,7 +373,7 @@ static int tick_broadcast_set_event(ktime_t expires, int force) | |||
| 373 | { | 373 | { |
| 374 | struct clock_event_device *bc = tick_broadcast_device.evtdev; | 374 | struct clock_event_device *bc = tick_broadcast_device.evtdev; |
| 375 | 375 | ||
| 376 | return tick_dev_program_event(bc, expires, force); | 376 | return clockevents_program_event(bc, expires, force); |
| 377 | } | 377 | } |
| 378 | 378 | ||
| 379 | int tick_resume_broadcast_oneshot(struct clock_event_device *bc) | 379 | int tick_resume_broadcast_oneshot(struct clock_event_device *bc) |
diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 119528de8235..da6c9ecad4e4 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c | |||
| @@ -94,7 +94,7 @@ void tick_handle_periodic(struct clock_event_device *dev) | |||
| 94 | */ | 94 | */ |
| 95 | next = ktime_add(dev->next_event, tick_period); | 95 | next = ktime_add(dev->next_event, tick_period); |
| 96 | for (;;) { | 96 | for (;;) { |
| 97 | if (!clockevents_program_event(dev, next, ktime_get())) | 97 | if (!clockevents_program_event(dev, next, false)) |
| 98 | return; | 98 | return; |
| 99 | /* | 99 | /* |
| 100 | * Have to be careful here. If we're in oneshot mode, | 100 | * Have to be careful here. If we're in oneshot mode, |
| @@ -137,7 +137,7 @@ void tick_setup_periodic(struct clock_event_device *dev, int broadcast) | |||
| 137 | clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); | 137 | clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); |
| 138 | 138 | ||
| 139 | for (;;) { | 139 | for (;;) { |
| 140 | if (!clockevents_program_event(dev, next, ktime_get())) | 140 | if (!clockevents_program_event(dev, next, false)) |
| 141 | return; | 141 | return; |
| 142 | next = ktime_add(next, tick_period); | 142 | next = ktime_add(next, tick_period); |
| 143 | } | 143 | } |
diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h index 1009b06d6f89..4e265b901fed 100644 --- a/kernel/time/tick-internal.h +++ b/kernel/time/tick-internal.h | |||
| @@ -26,8 +26,6 @@ extern void clockevents_shutdown(struct clock_event_device *dev); | |||
| 26 | extern void tick_setup_oneshot(struct clock_event_device *newdev, | 26 | extern void tick_setup_oneshot(struct clock_event_device *newdev, |
| 27 | void (*handler)(struct clock_event_device *), | 27 | void (*handler)(struct clock_event_device *), |
| 28 | ktime_t nextevt); | 28 | ktime_t nextevt); |
| 29 | extern int tick_dev_program_event(struct clock_event_device *dev, | ||
| 30 | ktime_t expires, int force); | ||
| 31 | extern int tick_program_event(ktime_t expires, int force); | 29 | extern int tick_program_event(ktime_t expires, int force); |
| 32 | extern void tick_oneshot_notify(void); | 30 | extern void tick_oneshot_notify(void); |
| 33 | extern int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *)); | 31 | extern int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *)); |
diff --git a/kernel/time/tick-oneshot.c b/kernel/time/tick-oneshot.c index 2d04411a5f05..824109060a33 100644 --- a/kernel/time/tick-oneshot.c +++ b/kernel/time/tick-oneshot.c | |||
| @@ -21,74 +21,6 @@ | |||
| 21 | 21 | ||
| 22 | #include "tick-internal.h" | 22 | #include "tick-internal.h" |
| 23 | 23 | ||
| 24 | /* Limit min_delta to a jiffie */ | ||
| 25 | #define MIN_DELTA_LIMIT (NSEC_PER_SEC / HZ) | ||
| 26 | |||
| 27 | static int tick_increase_min_delta(struct clock_event_device *dev) | ||
| 28 | { | ||
| 29 | /* Nothing to do if we already reached the limit */ | ||
| 30 | if (dev->min_delta_ns >= MIN_DELTA_LIMIT) | ||
| 31 | return -ETIME; | ||
| 32 | |||
| 33 | if (dev->min_delta_ns < 5000) | ||
| 34 | dev->min_delta_ns = 5000; | ||
| 35 | else | ||
| 36 | dev->min_delta_ns += dev->min_delta_ns >> 1; | ||
| 37 | |||
| 38 | if (dev->min_delta_ns > MIN_DELTA_LIMIT) | ||
| 39 | dev->min_delta_ns = MIN_DELTA_LIMIT; | ||
| 40 | |||
| 41 | printk(KERN_WARNING "CE: %s increased min_delta_ns to %llu nsec\n", | ||
| 42 | dev->name ? dev->name : "?", | ||
| 43 | (unsigned long long) dev->min_delta_ns); | ||
| 44 | return 0; | ||
| 45 | } | ||
| 46 | |||
| 47 | /** | ||
| 48 | * tick_program_event internal worker function | ||
| 49 | */ | ||
| 50 | int tick_dev_program_event(struct clock_event_device *dev, ktime_t expires, | ||
| 51 | int force) | ||
| 52 | { | ||
| 53 | ktime_t now = ktime_get(); | ||
| 54 | int i; | ||
| 55 | |||
| 56 | for (i = 0;;) { | ||
| 57 | int ret = clockevents_program_event(dev, expires, now); | ||
| 58 | |||
| 59 | if (!ret || !force) | ||
| 60 | return ret; | ||
| 61 | |||
| 62 | dev->retries++; | ||
| 63 | /* | ||
| 64 | * We tried 3 times to program the device with the given | ||
| 65 | * min_delta_ns. If that's not working then we increase it | ||
| 66 | * and emit a warning. | ||
| 67 | */ | ||
| 68 | if (++i > 2) { | ||
| 69 | /* Increase the min. delta and try again */ | ||
| 70 | if (tick_increase_min_delta(dev)) { | ||
| 71 | /* | ||
| 72 | * Get out of the loop if min_delta_ns | ||
| 73 | * hit the limit already. That's | ||
| 74 | * better than staying here forever. | ||
| 75 | * | ||
| 76 | * We clear next_event so we have a | ||
| 77 | * chance that the box survives. | ||
| 78 | */ | ||
| 79 | printk(KERN_WARNING | ||
| 80 | "CE: Reprogramming failure. Giving up\n"); | ||
| 81 | dev->next_event.tv64 = KTIME_MAX; | ||
| 82 | return -ETIME; | ||
| 83 | } | ||
| 84 | i = 0; | ||
| 85 | } | ||
| 86 | |||
| 87 | now = ktime_get(); | ||
| 88 | expires = ktime_add_ns(now, dev->min_delta_ns); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | /** | 24 | /** |
| 93 | * tick_program_event | 25 | * tick_program_event |
| 94 | */ | 26 | */ |
| @@ -96,7 +28,7 @@ int tick_program_event(ktime_t expires, int force) | |||
| 96 | { | 28 | { |
| 97 | struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev); | 29 | struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev); |
| 98 | 30 | ||
| 99 | return tick_dev_program_event(dev, expires, force); | 31 | return clockevents_program_event(dev, expires, force); |
| 100 | } | 32 | } |
| 101 | 33 | ||
| 102 | /** | 34 | /** |
| @@ -104,11 +36,10 @@ int tick_program_event(ktime_t expires, int force) | |||
| 104 | */ | 36 | */ |
| 105 | void tick_resume_oneshot(void) | 37 | void tick_resume_oneshot(void) |
| 106 | { | 38 | { |
| 107 | struct tick_device *td = &__get_cpu_var(tick_cpu_device); | 39 | struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev); |
| 108 | struct clock_event_device *dev = td->evtdev; | ||
| 109 | 40 | ||
| 110 | clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); | 41 | clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); |
| 111 | tick_program_event(ktime_get(), 1); | 42 | clockevents_program_event(dev, ktime_get(), true); |
| 112 | } | 43 | } |
| 113 | 44 | ||
| 114 | /** | 45 | /** |
| @@ -120,7 +51,7 @@ void tick_setup_oneshot(struct clock_event_device *newdev, | |||
| 120 | { | 51 | { |
| 121 | newdev->event_handler = handler; | 52 | newdev->event_handler = handler; |
| 122 | clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT); | 53 | clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT); |
| 123 | tick_dev_program_event(newdev, next_event, 1); | 54 | clockevents_program_event(newdev, next_event, true); |
| 124 | } | 55 | } |
| 125 | 56 | ||
| 126 | /** | 57 | /** |
