diff options
-rw-r--r-- | drivers/s390/char/sclp.c | 82 | ||||
-rw-r--r-- | drivers/s390/char/sclp.h | 9 | ||||
-rw-r--r-- | drivers/s390/char/sclp_cmd.c | 17 |
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. */ |
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) { |
@@ -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); | |||
173 | int sclp_reactivate(void); | 181 | int sclp_reactivate(void); |
174 | int sclp_service_call(sclp_cmdw_t command, void *sccb); | 182 | int sclp_service_call(sclp_cmdw_t command, void *sccb); |
175 | int sclp_sync_request(sclp_cmdw_t command, void *sccb); | 183 | int sclp_sync_request(sclp_cmdw_t command, void *sccb); |
184 | int sclp_sync_request_timeout(sclp_cmdw_t command, void *sccb, int timeout); | ||
176 | 185 | ||
177 | int sclp_sdias_init(void); | 186 | int sclp_sdias_init(void); |
178 | void sclp_sdias_exit(void); | 187 | void 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 | ||
38 | int sclp_sync_request(sclp_cmdw_t cmd, void *sccb) | 38 | int sclp_sync_request(sclp_cmdw_t cmd, void *sccb) |
39 | { | 39 | { |
40 | return sclp_sync_request_timeout(cmd, sccb, 0); | ||
41 | } | ||
42 | |||
43 | int 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) { |