aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/s390/char/sclp.c82
-rw-r--r--drivers/s390/char/sclp.h9
-rw-r--r--drivers/s390/char/sclp_cmd.c17
3 files changed, 104 insertions, 4 deletions
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index 1fe264379e0d..e9ede5980994 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) {
@@ -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();
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index fea76aed9eea..a68b5ec7d042 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -133,6 +133,11 @@ struct sclp_req {
133 /* Callback that is called after reaching final status. */ 133 /* Callback that is called after reaching final status. */
134 void (*callback)(struct sclp_req *, void *data); 134 void (*callback)(struct sclp_req *, void *data);
135 void *callback_data; 135 void *callback_data;
136 int queue_timeout; /* request queue timeout (sec), set by
137 caller of sclp_add_request(), if
138 needed */
139 /* Internal fields */
140 unsigned long queue_expires; /* request queue timeout (jiffies) */
136}; 141};
137 142
138#define SCLP_REQ_FILLED 0x00 /* request is ready to be processed */ 143#define SCLP_REQ_FILLED 0x00 /* request is ready to be processed */
@@ -140,6 +145,9 @@ struct sclp_req {
140#define SCLP_REQ_RUNNING 0x02 /* request is currently running */ 145#define SCLP_REQ_RUNNING 0x02 /* request is currently running */
141#define SCLP_REQ_DONE 0x03 /* request is completed successfully */ 146#define SCLP_REQ_DONE 0x03 /* request is completed successfully */
142#define SCLP_REQ_FAILED 0x05 /* request is finally failed */ 147#define SCLP_REQ_FAILED 0x05 /* request is finally failed */
148#define SCLP_REQ_QUEUED_TIMEOUT 0x06 /* request on queue timed out */
149
150#define SCLP_QUEUE_INTERVAL 5 /* timeout interval for request queue */
143 151
144/* function pointers that a high level driver has to use for registration */ 152/* function pointers that a high level driver has to use for registration */
145/* of some routines it wants to be called from the low level driver */ 153/* of some routines it wants to be called from the low level driver */
@@ -173,6 +181,7 @@ int sclp_deactivate(void);
173int sclp_reactivate(void); 181int sclp_reactivate(void);
174int sclp_service_call(sclp_cmdw_t command, void *sccb); 182int sclp_service_call(sclp_cmdw_t command, void *sccb);
175int sclp_sync_request(sclp_cmdw_t command, void *sccb); 183int sclp_sync_request(sclp_cmdw_t command, void *sccb);
184int sclp_sync_request_timeout(sclp_cmdw_t command, void *sccb, int timeout);
176 185
177int sclp_sdias_init(void); 186int sclp_sdias_init(void);
178void sclp_sdias_exit(void); 187void sclp_sdias_exit(void);
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index 49af8eeb90ea..6e8f90f84e49 100644
--- a/drivers/s390/char/sclp_cmd.c
+++ b/drivers/s390/char/sclp_cmd.c
@@ -37,6 +37,11 @@ static void sclp_sync_callback(struct sclp_req *req, void *data)
37 37
38int sclp_sync_request(sclp_cmdw_t cmd, void *sccb) 38int sclp_sync_request(sclp_cmdw_t cmd, void *sccb)
39{ 39{
40 return sclp_sync_request_timeout(cmd, sccb, 0);
41}
42
43int sclp_sync_request_timeout(sclp_cmdw_t cmd, void *sccb, int timeout)
44{
40 struct completion completion; 45 struct completion completion;
41 struct sclp_req *request; 46 struct sclp_req *request;
42 int rc; 47 int rc;
@@ -44,6 +49,8 @@ int sclp_sync_request(sclp_cmdw_t cmd, void *sccb)
44 request = kzalloc(sizeof(*request), GFP_KERNEL); 49 request = kzalloc(sizeof(*request), GFP_KERNEL);
45 if (!request) 50 if (!request)
46 return -ENOMEM; 51 return -ENOMEM;
52 if (timeout)
53 request->queue_timeout = timeout;
47 request->command = cmd; 54 request->command = cmd;
48 request->sccb = sccb; 55 request->sccb = sccb;
49 request->status = SCLP_REQ_FILLED; 56 request->status = SCLP_REQ_FILLED;
@@ -110,7 +117,8 @@ int sclp_get_cpu_info(struct sclp_cpu_info *info)
110 if (!sccb) 117 if (!sccb)
111 return -ENOMEM; 118 return -ENOMEM;
112 sccb->header.length = sizeof(*sccb); 119 sccb->header.length = sizeof(*sccb);
113 rc = sclp_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb); 120 rc = sclp_sync_request_timeout(SCLP_CMDW_READ_CPU_INFO, sccb,
121 SCLP_QUEUE_INTERVAL);
114 if (rc) 122 if (rc)
115 goto out; 123 goto out;
116 if (sccb->header.response_code != 0x0010) { 124 if (sccb->header.response_code != 0x0010) {
@@ -144,7 +152,7 @@ static int do_cpu_configure(sclp_cmdw_t cmd)
144 if (!sccb) 152 if (!sccb)
145 return -ENOMEM; 153 return -ENOMEM;
146 sccb->header.length = sizeof(*sccb); 154 sccb->header.length = sizeof(*sccb);
147 rc = sclp_sync_request(cmd, sccb); 155 rc = sclp_sync_request_timeout(cmd, sccb, SCLP_QUEUE_INTERVAL);
148 if (rc) 156 if (rc)
149 goto out; 157 goto out;
150 switch (sccb->header.response_code) { 158 switch (sccb->header.response_code) {
@@ -214,7 +222,7 @@ static int do_assign_storage(sclp_cmdw_t cmd, u16 rn)
214 return -ENOMEM; 222 return -ENOMEM;
215 sccb->header.length = PAGE_SIZE; 223 sccb->header.length = PAGE_SIZE;
216 sccb->rn = rn; 224 sccb->rn = rn;
217 rc = sclp_sync_request(cmd, sccb); 225 rc = sclp_sync_request_timeout(cmd, sccb, SCLP_QUEUE_INTERVAL);
218 if (rc) 226 if (rc)
219 goto out; 227 goto out;
220 switch (sccb->header.response_code) { 228 switch (sccb->header.response_code) {
@@ -269,7 +277,8 @@ static int sclp_attach_storage(u8 id)
269 if (!sccb) 277 if (!sccb)
270 return -ENOMEM; 278 return -ENOMEM;
271 sccb->header.length = PAGE_SIZE; 279 sccb->header.length = PAGE_SIZE;
272 rc = sclp_sync_request(0x00080001 | id << 8, sccb); 280 rc = sclp_sync_request_timeout(0x00080001 | id << 8, sccb,
281 SCLP_QUEUE_INTERVAL);
273 if (rc) 282 if (rc)
274 goto out; 283 goto out;
275 switch (sccb->header.response_code) { 284 switch (sccb->header.response_code) {