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 | /** |