diff options
Diffstat (limited to 'kernel/time/clockevents.c')
-rw-r--r-- | kernel/time/clockevents.c | 129 |
1 files changed, 115 insertions, 14 deletions
diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index e4c699dfa4e8..1ecd6ba36d6c 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c | |||
@@ -94,42 +94,143 @@ 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 | /* Shortcut for clockevent devices that can deal with ktime. */ |
125 | delta = dev->max_delta_ns; | 220 | if (dev->features & CLOCK_EVT_FEAT_KTIME) |
126 | if (delta < dev->min_delta_ns) | 221 | return dev->set_next_ktime(expires, dev); |
127 | delta = dev->min_delta_ns; | 222 | |
223 | delta = ktime_to_ns(ktime_sub(expires, ktime_get())); | ||
224 | if (delta <= 0) | ||
225 | return force ? clockevents_program_min_delta(dev) : -ETIME; | ||
128 | 226 | ||
129 | clc = delta * dev->mult; | 227 | delta = min(delta, (int64_t) dev->max_delta_ns); |
130 | clc >>= dev->shift; | 228 | delta = max(delta, (int64_t) dev->min_delta_ns); |
131 | 229 | ||
132 | return dev->set_next_event((unsigned long) clc, dev); | 230 | clc = ((unsigned long long) delta * dev->mult) >> dev->shift; |
231 | rc = dev->set_next_event((unsigned long) clc, dev); | ||
232 | |||
233 | return (rc && force) ? clockevents_program_min_delta(dev) : rc; | ||
133 | } | 234 | } |
134 | 235 | ||
135 | /** | 236 | /** |
@@ -258,7 +359,7 @@ int clockevents_update_freq(struct clock_event_device *dev, u32 freq) | |||
258 | if (dev->mode != CLOCK_EVT_MODE_ONESHOT) | 359 | if (dev->mode != CLOCK_EVT_MODE_ONESHOT) |
259 | return 0; | 360 | return 0; |
260 | 361 | ||
261 | return clockevents_program_event(dev, dev->next_event, ktime_get()); | 362 | return clockevents_program_event(dev, dev->next_event, false); |
262 | } | 363 | } |
263 | 364 | ||
264 | /* | 365 | /* |