aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/char/sclp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/char/sclp.c')
-rw-r--r--drivers/s390/char/sclp.c88
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. */
92static struct timer_list sclp_request_timer; 92static struct timer_list sclp_request_timer;
93 93
94/* Timer for queued requests. */
95static struct timer_list sclp_queue_timer;
96
94/* Internal state: is the driver initialized? */ 97/* Internal state: is the driver initialized? */
95static volatile enum sclp_init_state_t { 98static 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 */
225static 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 */
243static 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;
263out:
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 */
273static 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;