diff options
Diffstat (limited to 'drivers/s390/char/sclp.c')
-rw-r--r-- | drivers/s390/char/sclp.c | 88 |
1 files changed, 85 insertions, 3 deletions
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 1fe264379e0d..1990285296c6 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c | |||
@@ -91,6 +91,9 @@ static struct sclp_req sclp_suspend_req; | |||
91 | /* Timer for request retries. */ | 91 | /* Timer for request retries. */ |
92 | static struct timer_list sclp_request_timer; | 92 | static struct timer_list sclp_request_timer; |
93 | 93 | ||
94 | /* Timer for queued requests. */ | ||
95 | static struct timer_list sclp_queue_timer; | ||
96 | |||
94 | /* Internal state: is the driver initialized? */ | 97 | /* Internal state: is the driver initialized? */ |
95 | static volatile enum sclp_init_state_t { | 98 | static volatile enum sclp_init_state_t { |
96 | sclp_init_state_uninitialized, | 99 | sclp_init_state_uninitialized, |
@@ -215,6 +218,76 @@ sclp_request_timeout(unsigned long data) | |||
215 | sclp_process_queue(); | 218 | sclp_process_queue(); |
216 | } | 219 | } |
217 | 220 | ||
221 | /* | ||
222 | * Returns the expire value in jiffies of the next pending request timeout, | ||
223 | * if any. Needs to be called with sclp_lock. | ||
224 | */ | ||
225 | static unsigned long __sclp_req_queue_find_next_timeout(void) | ||
226 | { | ||
227 | unsigned long expires_next = 0; | ||
228 | struct sclp_req *req; | ||
229 | |||
230 | list_for_each_entry(req, &sclp_req_queue, list) { | ||
231 | if (!req->queue_expires) | ||
232 | continue; | ||
233 | if (!expires_next || | ||
234 | (time_before(req->queue_expires, expires_next))) | ||
235 | expires_next = req->queue_expires; | ||
236 | } | ||
237 | return expires_next; | ||
238 | } | ||
239 | |||
240 | /* | ||
241 | * Returns expired request, if any, and removes it from the list. | ||
242 | */ | ||
243 | static struct sclp_req *__sclp_req_queue_remove_expired_req(void) | ||
244 | { | ||
245 | unsigned long flags, now; | ||
246 | struct sclp_req *req; | ||
247 | |||
248 | spin_lock_irqsave(&sclp_lock, flags); | ||
249 | now = jiffies; | ||
250 | /* Don't need list_for_each_safe because we break out after list_del */ | ||
251 | list_for_each_entry(req, &sclp_req_queue, list) { | ||
252 | if (!req->queue_expires) | ||
253 | continue; | ||
254 | if (time_before_eq(req->queue_expires, now)) { | ||
255 | if (req->status == SCLP_REQ_QUEUED) { | ||
256 | req->status = SCLP_REQ_QUEUED_TIMEOUT; | ||
257 | list_del(&req->list); | ||
258 | goto out; | ||
259 | } | ||
260 | } | ||
261 | } | ||
262 | req = NULL; | ||
263 | out: | ||
264 | spin_unlock_irqrestore(&sclp_lock, flags); | ||
265 | return req; | ||
266 | } | ||
267 | |||
268 | /* | ||
269 | * Timeout handler for queued requests. Removes request from list and | ||
270 | * invokes callback. This timer can be set per request in situations where | ||
271 | * waiting too long would be harmful to the system, e.g. during SE reboot. | ||
272 | */ | ||
273 | static void sclp_req_queue_timeout(unsigned long data) | ||
274 | { | ||
275 | unsigned long flags, expires_next; | ||
276 | struct sclp_req *req; | ||
277 | |||
278 | do { | ||
279 | req = __sclp_req_queue_remove_expired_req(); | ||
280 | if (req && req->callback) | ||
281 | req->callback(req, req->callback_data); | ||
282 | } while (req); | ||
283 | |||
284 | spin_lock_irqsave(&sclp_lock, flags); | ||
285 | expires_next = __sclp_req_queue_find_next_timeout(); | ||
286 | if (expires_next) | ||
287 | mod_timer(&sclp_queue_timer, expires_next); | ||
288 | spin_unlock_irqrestore(&sclp_lock, flags); | ||
289 | } | ||
290 | |||
218 | /* Try to start a request. Return zero if the request was successfully | 291 | /* Try to start a request. Return zero if the request was successfully |
219 | * started or if it will be started at a later time. Return non-zero otherwise. | 292 | * started or if it will be started at a later time. Return non-zero otherwise. |
220 | * Called while sclp_lock is locked. */ | 293 | * Called while sclp_lock is locked. */ |
@@ -317,6 +390,13 @@ sclp_add_request(struct sclp_req *req) | |||
317 | req->start_count = 0; | 390 | req->start_count = 0; |
318 | list_add_tail(&req->list, &sclp_req_queue); | 391 | list_add_tail(&req->list, &sclp_req_queue); |
319 | rc = 0; | 392 | rc = 0; |
393 | if (req->queue_timeout) { | ||
394 | req->queue_expires = jiffies + req->queue_timeout * HZ; | ||
395 | if (!timer_pending(&sclp_queue_timer) || | ||
396 | time_after(sclp_queue_timer.expires, req->queue_expires)) | ||
397 | mod_timer(&sclp_queue_timer, req->queue_expires); | ||
398 | } else | ||
399 | req->queue_expires = 0; | ||
320 | /* Start if request is first in list */ | 400 | /* Start if request is first in list */ |
321 | if (sclp_running_state == sclp_running_state_idle && | 401 | if (sclp_running_state == sclp_running_state_idle && |
322 | req->list.prev == &sclp_req_queue) { | 402 | req->list.prev == &sclp_req_queue) { |
@@ -892,7 +972,7 @@ sclp_check_interface(void) | |||
892 | 972 | ||
893 | spin_lock_irqsave(&sclp_lock, flags); | 973 | spin_lock_irqsave(&sclp_lock, flags); |
894 | /* Prepare init mask command */ | 974 | /* Prepare init mask command */ |
895 | rc = register_external_interrupt(0x2401, sclp_check_handler); | 975 | rc = register_external_irq(EXT_IRQ_SERVICE_SIG, sclp_check_handler); |
896 | if (rc) { | 976 | if (rc) { |
897 | spin_unlock_irqrestore(&sclp_lock, flags); | 977 | spin_unlock_irqrestore(&sclp_lock, flags); |
898 | return rc; | 978 | return rc; |
@@ -925,7 +1005,7 @@ sclp_check_interface(void) | |||
925 | } else | 1005 | } else |
926 | rc = -EBUSY; | 1006 | rc = -EBUSY; |
927 | } | 1007 | } |
928 | unregister_external_interrupt(0x2401, sclp_check_handler); | 1008 | unregister_external_irq(EXT_IRQ_SERVICE_SIG, sclp_check_handler); |
929 | spin_unlock_irqrestore(&sclp_lock, flags); | 1009 | spin_unlock_irqrestore(&sclp_lock, flags); |
930 | return rc; | 1010 | return rc; |
931 | } | 1011 | } |
@@ -1113,6 +1193,8 @@ sclp_init(void) | |||
1113 | INIT_LIST_HEAD(&sclp_reg_list); | 1193 | INIT_LIST_HEAD(&sclp_reg_list); |
1114 | list_add(&sclp_state_change_event.list, &sclp_reg_list); | 1194 | list_add(&sclp_state_change_event.list, &sclp_reg_list); |
1115 | init_timer(&sclp_request_timer); | 1195 | init_timer(&sclp_request_timer); |
1196 | init_timer(&sclp_queue_timer); | ||
1197 | sclp_queue_timer.function = sclp_req_queue_timeout; | ||
1116 | /* Check interface */ | 1198 | /* Check interface */ |
1117 | spin_unlock_irqrestore(&sclp_lock, flags); | 1199 | spin_unlock_irqrestore(&sclp_lock, flags); |
1118 | rc = sclp_check_interface(); | 1200 | rc = sclp_check_interface(); |
@@ -1124,7 +1206,7 @@ sclp_init(void) | |||
1124 | if (rc) | 1206 | if (rc) |
1125 | goto fail_init_state_uninitialized; | 1207 | goto fail_init_state_uninitialized; |
1126 | /* Register interrupt handler */ | 1208 | /* Register interrupt handler */ |
1127 | rc = register_external_interrupt(0x2401, sclp_interrupt_handler); | 1209 | rc = register_external_irq(EXT_IRQ_SERVICE_SIG, sclp_interrupt_handler); |
1128 | if (rc) | 1210 | if (rc) |
1129 | goto fail_unregister_reboot_notifier; | 1211 | goto fail_unregister_reboot_notifier; |
1130 | sclp_init_state = sclp_init_state_initialized; | 1212 | sclp_init_state = sclp_init_state_initialized; |