aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2011-08-23 09:29:42 -0400
committerThomas Gleixner <tglx@linutronix.de>2011-09-08 05:10:56 -0400
commitd1748302f70be7469809809283fe164156a34231 (patch)
treec81137f4126f2fe1451c28441415c1cd0fa0f7cd /kernel
parent29c158e81c733ac7d6a75c5ee929f34fb9f92983 (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/Kconfig2
-rw-r--r--kernel/time/clockevents.c125
-rw-r--r--kernel/time/tick-broadcast.c4
-rw-r--r--kernel/time/tick-common.c4
-rw-r--r--kernel/time/tick-internal.h2
-rw-r--r--kernel/time/tick-oneshot.c77
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
30config 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 */
108static 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 */
137static 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 */
176static 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 */
103int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, 202int 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
379int tick_resume_broadcast_oneshot(struct clock_event_device *bc) 379int 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);
26extern void tick_setup_oneshot(struct clock_event_device *newdev, 26extern 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);
29extern int tick_dev_program_event(struct clock_event_device *dev,
30 ktime_t expires, int force);
31extern int tick_program_event(ktime_t expires, int force); 29extern int tick_program_event(ktime_t expires, int force);
32extern void tick_oneshot_notify(void); 30extern void tick_oneshot_notify(void);
33extern int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *)); 31extern 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
27static 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 */
50int 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 */
105void tick_resume_oneshot(void) 37void 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/**