diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-08 15:02:28 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-08 15:02:28 -0400 |
commit | d586c86d50cefa0897a51a2dbc714060ccedae76 (patch) | |
tree | 76a7f454637badb74390047aebca5c071c0988fe /drivers/s390/char | |
parent | e9f37d3a8d126e73f5737ef548cdf6f618e295e4 (diff) | |
parent | 457f2180951cdcbfb4657ddcc83b486e93497f56 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull second set of s390 patches from Martin Schwidefsky:
"The second part of Heikos uaccess rework, the page table walker for
uaccess is now a thing of the past (yay!)
The code change to fix the theoretical TLB flush problem allows us to
add a TLB flush optimization for zEC12, this machine has new
instructions that allow to do CPU local TLB flushes for single pages
and for all pages of a specific address space.
Plus the usual bug fixing and some more cleanup"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux:
s390/uaccess: rework uaccess code - fix locking issues
s390/mm,tlb: optimize TLB flushing for zEC12
s390/mm,tlb: safeguard against speculative TLB creation
s390/irq: Use defines for external interruption codes
s390/irq: Add defines for external interruption codes
s390/sclp: add timeout for queued requests
kvm/s390: also set guest pages back to stable on kexec/kdump
lcs: Add missing destroy_timer_on_stack()
s390/tape: Add missing destroy_timer_on_stack()
s390/tape: Use del_timer_sync()
s390/3270: fix crash with multiple reset device requests
s390/bitops,atomic: add missing memory barriers
s390/zcrypt: add length check for aligned data to avoid overflow in msg-type 6
Diffstat (limited to 'drivers/s390/char')
-rw-r--r-- | drivers/s390/char/raw3270.c | 9 | ||||
-rw-r--r-- | drivers/s390/char/sclp.c | 88 | ||||
-rw-r--r-- | drivers/s390/char/sclp.h | 9 | ||||
-rw-r--r-- | drivers/s390/char/sclp_cmd.c | 17 | ||||
-rw-r--r-- | drivers/s390/char/tape_std.c | 3 |
5 files changed, 115 insertions, 11 deletions
diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 9f849df4381e..15b3459f8656 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c | |||
@@ -632,6 +632,8 @@ raw3270_reset_device_cb(struct raw3270_request *rq, void *data) | |||
632 | raw3270_size_device_done(rp); | 632 | raw3270_size_device_done(rp); |
633 | } else | 633 | } else |
634 | raw3270_writesf_readpart(rp); | 634 | raw3270_writesf_readpart(rp); |
635 | memset(&rp->init_reset, 0, sizeof(rp->init_reset)); | ||
636 | memset(&rp->init_data, 0, sizeof(rp->init_data)); | ||
635 | } | 637 | } |
636 | 638 | ||
637 | static int | 639 | static int |
@@ -639,9 +641,10 @@ __raw3270_reset_device(struct raw3270 *rp) | |||
639 | { | 641 | { |
640 | int rc; | 642 | int rc; |
641 | 643 | ||
644 | /* Check if reset is already pending */ | ||
645 | if (rp->init_reset.view) | ||
646 | return -EBUSY; | ||
642 | /* Store reset data stream to init_data/init_reset */ | 647 | /* Store reset data stream to init_data/init_reset */ |
643 | memset(&rp->init_reset, 0, sizeof(rp->init_reset)); | ||
644 | memset(&rp->init_data, 0, sizeof(rp->init_data)); | ||
645 | rp->init_data[0] = TW_KR; | 648 | rp->init_data[0] = TW_KR; |
646 | rp->init_reset.ccw.cmd_code = TC_EWRITEA; | 649 | rp->init_reset.ccw.cmd_code = TC_EWRITEA; |
647 | rp->init_reset.ccw.flags = CCW_FLAG_SLI; | 650 | rp->init_reset.ccw.flags = CCW_FLAG_SLI; |
@@ -850,7 +853,7 @@ raw3270_create_device(struct ccw_device *cdev) | |||
850 | char *ascebc; | 853 | char *ascebc; |
851 | int rc; | 854 | int rc; |
852 | 855 | ||
853 | rp = kmalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA); | 856 | rp = kzalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA); |
854 | if (!rp) | 857 | if (!rp) |
855 | return ERR_PTR(-ENOMEM); | 858 | return ERR_PTR(-ENOMEM); |
856 | ascebc = kmalloc(256, GFP_KERNEL); | 859 | ascebc = kmalloc(256, GFP_KERNEL); |
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; |
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) { |
diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c index 981a99fd8d42..3478e19ae194 100644 --- a/drivers/s390/char/tape_std.c +++ b/drivers/s390/char/tape_std.c | |||
@@ -78,7 +78,8 @@ tape_std_assign(struct tape_device *device) | |||
78 | 78 | ||
79 | rc = tape_do_io_interruptible(device, request); | 79 | rc = tape_do_io_interruptible(device, request); |
80 | 80 | ||
81 | del_timer(&timeout); | 81 | del_timer_sync(&timeout); |
82 | destroy_timer_on_stack(&timeout); | ||
82 | 83 | ||
83 | if (rc != 0) { | 84 | if (rc != 0) { |
84 | DBF_EVENT(3, "%08x: assign failed - device might be busy\n", | 85 | DBF_EVENT(3, "%08x: assign failed - device might be busy\n", |