diff options
author | Ralph Wuerthner <rwuerthn@de.ibm.com> | 2007-07-10 05:24:19 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2007-07-10 05:24:52 -0400 |
commit | af512ed0f8a7e6a3c6fd93b2b5882c8e837a6939 (patch) | |
tree | 2bd15176832b8c6eb671519334d9e1ab5d994d68 /drivers/s390/crypto/ap_bus.c | |
parent | 987ad70a4d90cf0e70dba43ece02c2e2219e092c (diff) |
[S390] zcrypt: fix request timeout handling
Under very high load zcrypt requests may timeout while waiting on the
request queue. Modify zcrypt that timeouts are based on crypto adapter
responses. A timeout occurs only if a crypto adapter does not respond
within a given time frame to sumitted requests.
Signed-off-by: Ralph Wuerthner <rwuerthn@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/crypto/ap_bus.c')
-rw-r--r-- | drivers/s390/crypto/ap_bus.c | 98 |
1 files changed, 89 insertions, 9 deletions
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 5aac0ec3636..90bd2201451 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c | |||
@@ -43,6 +43,7 @@ static void ap_poll_all(unsigned long); | |||
43 | static void ap_poll_timeout(unsigned long); | 43 | static void ap_poll_timeout(unsigned long); |
44 | static int ap_poll_thread_start(void); | 44 | static int ap_poll_thread_start(void); |
45 | static void ap_poll_thread_stop(void); | 45 | static void ap_poll_thread_stop(void); |
46 | static void ap_request_timeout(unsigned long); | ||
46 | 47 | ||
47 | /** | 48 | /** |
48 | * Module description. | 49 | * Module description. |
@@ -189,6 +190,7 @@ int ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) | |||
189 | case AP_RESPONSE_NORMAL: | 190 | case AP_RESPONSE_NORMAL: |
190 | return 0; | 191 | return 0; |
191 | case AP_RESPONSE_Q_FULL: | 192 | case AP_RESPONSE_Q_FULL: |
193 | case AP_RESPONSE_RESET_IN_PROGRESS: | ||
192 | return -EBUSY; | 194 | return -EBUSY; |
193 | default: /* Device is gone. */ | 195 | default: /* Device is gone. */ |
194 | return -ENODEV; | 196 | return -ENODEV; |
@@ -252,6 +254,8 @@ int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length) | |||
252 | if (status.queue_empty) | 254 | if (status.queue_empty) |
253 | return -ENOENT; | 255 | return -ENOENT; |
254 | return -EBUSY; | 256 | return -EBUSY; |
257 | case AP_RESPONSE_RESET_IN_PROGRESS: | ||
258 | return -EBUSY; | ||
255 | default: | 259 | default: |
256 | return -ENODEV; | 260 | return -ENODEV; |
257 | } | 261 | } |
@@ -326,11 +330,12 @@ static int ap_init_queue(ap_qid_t qid) | |||
326 | i = AP_MAX_RESET; /* return with -ENODEV */ | 330 | i = AP_MAX_RESET; /* return with -ENODEV */ |
327 | break; | 331 | break; |
328 | case AP_RESPONSE_RESET_IN_PROGRESS: | 332 | case AP_RESPONSE_RESET_IN_PROGRESS: |
333 | rc = -EBUSY; | ||
329 | case AP_RESPONSE_BUSY: | 334 | case AP_RESPONSE_BUSY: |
330 | default: | 335 | default: |
331 | break; | 336 | break; |
332 | } | 337 | } |
333 | if (rc != -ENODEV) | 338 | if (rc != -ENODEV && rc != -EBUSY) |
334 | break; | 339 | break; |
335 | if (i < AP_MAX_RESET - 1) { | 340 | if (i < AP_MAX_RESET - 1) { |
336 | udelay(5); | 341 | udelay(5); |
@@ -341,6 +346,40 @@ static int ap_init_queue(ap_qid_t qid) | |||
341 | } | 346 | } |
342 | 347 | ||
343 | /** | 348 | /** |
349 | * Arm request timeout if a AP device was idle and a new request is submitted. | ||
350 | */ | ||
351 | static void ap_increase_queue_count(struct ap_device *ap_dev) | ||
352 | { | ||
353 | int timeout = ap_dev->drv->request_timeout; | ||
354 | |||
355 | ap_dev->queue_count++; | ||
356 | if (ap_dev->queue_count == 1) { | ||
357 | mod_timer(&ap_dev->timeout, jiffies + timeout); | ||
358 | ap_dev->reset = AP_RESET_ARMED; | ||
359 | } | ||
360 | } | ||
361 | |||
362 | /** | ||
363 | * AP device is still alive, re-schedule request timeout if there are still | ||
364 | * pending requests. | ||
365 | */ | ||
366 | static void ap_decrease_queue_count(struct ap_device *ap_dev) | ||
367 | { | ||
368 | int timeout = ap_dev->drv->request_timeout; | ||
369 | |||
370 | ap_dev->queue_count--; | ||
371 | if (ap_dev->queue_count > 0) | ||
372 | mod_timer(&ap_dev->timeout, jiffies + timeout); | ||
373 | else | ||
374 | /** | ||
375 | * The timeout timer should to be disabled now - since | ||
376 | * del_timer_sync() is very expensive, we just tell via the | ||
377 | * reset flag to ignore the pending timeout timer. | ||
378 | */ | ||
379 | ap_dev->reset = AP_RESET_IGNORE; | ||
380 | } | ||
381 | |||
382 | /** | ||
344 | * AP device related attributes. | 383 | * AP device related attributes. |
345 | */ | 384 | */ |
346 | static ssize_t ap_hwtype_show(struct device *dev, | 385 | static ssize_t ap_hwtype_show(struct device *dev, |
@@ -498,6 +537,7 @@ static int ap_device_remove(struct device *dev) | |||
498 | struct ap_driver *ap_drv = ap_dev->drv; | 537 | struct ap_driver *ap_drv = ap_dev->drv; |
499 | 538 | ||
500 | ap_flush_queue(ap_dev); | 539 | ap_flush_queue(ap_dev); |
540 | del_timer_sync(&ap_dev->timeout); | ||
501 | if (ap_drv->remove) | 541 | if (ap_drv->remove) |
502 | ap_drv->remove(ap_dev); | 542 | ap_drv->remove(ap_dev); |
503 | spin_lock_bh(&ap_device_lock); | 543 | spin_lock_bh(&ap_device_lock); |
@@ -759,17 +799,21 @@ static void ap_scan_bus(struct work_struct *unused) | |||
759 | __ap_scan_bus); | 799 | __ap_scan_bus); |
760 | rc = ap_query_queue(qid, &queue_depth, &device_type); | 800 | rc = ap_query_queue(qid, &queue_depth, &device_type); |
761 | if (dev) { | 801 | if (dev) { |
802 | if (rc == -EBUSY) { | ||
803 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
804 | schedule_timeout(AP_RESET_TIMEOUT); | ||
805 | rc = ap_query_queue(qid, &queue_depth, | ||
806 | &device_type); | ||
807 | } | ||
762 | ap_dev = to_ap_dev(dev); | 808 | ap_dev = to_ap_dev(dev); |
763 | spin_lock_bh(&ap_dev->lock); | 809 | spin_lock_bh(&ap_dev->lock); |
764 | if (rc || ap_dev->unregistered) { | 810 | if (rc || ap_dev->unregistered) { |
765 | spin_unlock_bh(&ap_dev->lock); | 811 | spin_unlock_bh(&ap_dev->lock); |
766 | put_device(dev); | ||
767 | device_unregister(dev); | 812 | device_unregister(dev); |
813 | put_device(dev); | ||
768 | continue; | 814 | continue; |
769 | } else | 815 | } |
770 | spin_unlock_bh(&ap_dev->lock); | 816 | spin_unlock_bh(&ap_dev->lock); |
771 | } | ||
772 | if (dev) { | ||
773 | put_device(dev); | 817 | put_device(dev); |
774 | continue; | 818 | continue; |
775 | } | 819 | } |
@@ -788,6 +832,8 @@ static void ap_scan_bus(struct work_struct *unused) | |||
788 | INIT_LIST_HEAD(&ap_dev->pendingq); | 832 | INIT_LIST_HEAD(&ap_dev->pendingq); |
789 | INIT_LIST_HEAD(&ap_dev->requestq); | 833 | INIT_LIST_HEAD(&ap_dev->requestq); |
790 | INIT_LIST_HEAD(&ap_dev->list); | 834 | INIT_LIST_HEAD(&ap_dev->list); |
835 | setup_timer(&ap_dev->timeout, ap_request_timeout, | ||
836 | (unsigned long) ap_dev); | ||
791 | if (device_type == 0) | 837 | if (device_type == 0) |
792 | ap_probe_device_type(ap_dev); | 838 | ap_probe_device_type(ap_dev); |
793 | else | 839 | else |
@@ -853,7 +899,7 @@ static int ap_poll_read(struct ap_device *ap_dev, unsigned long *flags) | |||
853 | switch (status.response_code) { | 899 | switch (status.response_code) { |
854 | case AP_RESPONSE_NORMAL: | 900 | case AP_RESPONSE_NORMAL: |
855 | atomic_dec(&ap_poll_requests); | 901 | atomic_dec(&ap_poll_requests); |
856 | ap_dev->queue_count--; | 902 | ap_decrease_queue_count(ap_dev); |
857 | list_for_each_entry(ap_msg, &ap_dev->pendingq, list) { | 903 | list_for_each_entry(ap_msg, &ap_dev->pendingq, list) { |
858 | if (ap_msg->psmid != ap_dev->reply->psmid) | 904 | if (ap_msg->psmid != ap_dev->reply->psmid) |
859 | continue; | 905 | continue; |
@@ -904,7 +950,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags) | |||
904 | switch (status.response_code) { | 950 | switch (status.response_code) { |
905 | case AP_RESPONSE_NORMAL: | 951 | case AP_RESPONSE_NORMAL: |
906 | atomic_inc(&ap_poll_requests); | 952 | atomic_inc(&ap_poll_requests); |
907 | ap_dev->queue_count++; | 953 | ap_increase_queue_count(ap_dev); |
908 | list_move_tail(&ap_msg->list, &ap_dev->pendingq); | 954 | list_move_tail(&ap_msg->list, &ap_dev->pendingq); |
909 | ap_dev->requestq_count--; | 955 | ap_dev->requestq_count--; |
910 | ap_dev->pendingq_count++; | 956 | ap_dev->pendingq_count++; |
@@ -914,6 +960,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags) | |||
914 | *flags |= 2; | 960 | *flags |= 2; |
915 | break; | 961 | break; |
916 | case AP_RESPONSE_Q_FULL: | 962 | case AP_RESPONSE_Q_FULL: |
963 | case AP_RESPONSE_RESET_IN_PROGRESS: | ||
917 | *flags |= 2; | 964 | *flags |= 2; |
918 | break; | 965 | break; |
919 | case AP_RESPONSE_MESSAGE_TOO_BIG: | 966 | case AP_RESPONSE_MESSAGE_TOO_BIG: |
@@ -960,10 +1007,11 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms | |||
960 | list_add_tail(&ap_msg->list, &ap_dev->pendingq); | 1007 | list_add_tail(&ap_msg->list, &ap_dev->pendingq); |
961 | atomic_inc(&ap_poll_requests); | 1008 | atomic_inc(&ap_poll_requests); |
962 | ap_dev->pendingq_count++; | 1009 | ap_dev->pendingq_count++; |
963 | ap_dev->queue_count++; | 1010 | ap_increase_queue_count(ap_dev); |
964 | ap_dev->total_request_count++; | 1011 | ap_dev->total_request_count++; |
965 | break; | 1012 | break; |
966 | case AP_RESPONSE_Q_FULL: | 1013 | case AP_RESPONSE_Q_FULL: |
1014 | case AP_RESPONSE_RESET_IN_PROGRESS: | ||
967 | list_add_tail(&ap_msg->list, &ap_dev->requestq); | 1015 | list_add_tail(&ap_msg->list, &ap_dev->requestq); |
968 | ap_dev->requestq_count++; | 1016 | ap_dev->requestq_count++; |
969 | ap_dev->total_request_count++; | 1017 | ap_dev->total_request_count++; |
@@ -1046,6 +1094,25 @@ static void ap_poll_timeout(unsigned long unused) | |||
1046 | } | 1094 | } |
1047 | 1095 | ||
1048 | /** | 1096 | /** |
1097 | * Reset a not responding AP device and move all requests from the | ||
1098 | * pending queue to the request queue. | ||
1099 | */ | ||
1100 | static void ap_reset(struct ap_device *ap_dev) | ||
1101 | { | ||
1102 | int rc; | ||
1103 | |||
1104 | ap_dev->reset = AP_RESET_IGNORE; | ||
1105 | atomic_sub(ap_dev->queue_count, &ap_poll_requests); | ||
1106 | ap_dev->queue_count = 0; | ||
1107 | list_splice_init(&ap_dev->pendingq, &ap_dev->requestq); | ||
1108 | ap_dev->requestq_count += ap_dev->pendingq_count; | ||
1109 | ap_dev->pendingq_count = 0; | ||
1110 | rc = ap_init_queue(ap_dev->qid); | ||
1111 | if (rc == -ENODEV) | ||
1112 | ap_dev->unregistered = 1; | ||
1113 | } | ||
1114 | |||
1115 | /** | ||
1049 | * Poll all AP devices on the bus in a round robin fashion. Continue | 1116 | * Poll all AP devices on the bus in a round robin fashion. Continue |
1050 | * polling until bit 2^0 of the control flags is not set. If bit 2^1 | 1117 | * polling until bit 2^0 of the control flags is not set. If bit 2^1 |
1051 | * of the control flags has been set arm the poll timer. | 1118 | * of the control flags has been set arm the poll timer. |
@@ -1056,6 +1123,8 @@ static int __ap_poll_all(struct ap_device *ap_dev, unsigned long *flags) | |||
1056 | if (!ap_dev->unregistered) { | 1123 | if (!ap_dev->unregistered) { |
1057 | if (ap_poll_queue(ap_dev, flags)) | 1124 | if (ap_poll_queue(ap_dev, flags)) |
1058 | ap_dev->unregistered = 1; | 1125 | ap_dev->unregistered = 1; |
1126 | if (ap_dev->reset == AP_RESET_DO) | ||
1127 | ap_reset(ap_dev); | ||
1059 | } | 1128 | } |
1060 | spin_unlock(&ap_dev->lock); | 1129 | spin_unlock(&ap_dev->lock); |
1061 | return 0; | 1130 | return 0; |
@@ -1147,6 +1216,17 @@ static void ap_poll_thread_stop(void) | |||
1147 | mutex_unlock(&ap_poll_thread_mutex); | 1216 | mutex_unlock(&ap_poll_thread_mutex); |
1148 | } | 1217 | } |
1149 | 1218 | ||
1219 | /** | ||
1220 | * Handling of request timeouts | ||
1221 | */ | ||
1222 | static void ap_request_timeout(unsigned long data) | ||
1223 | { | ||
1224 | struct ap_device *ap_dev = (struct ap_device *) data; | ||
1225 | |||
1226 | if (ap_dev->reset == AP_RESET_ARMED) | ||
1227 | ap_dev->reset = AP_RESET_DO; | ||
1228 | } | ||
1229 | |||
1150 | static void ap_reset_domain(void) | 1230 | static void ap_reset_domain(void) |
1151 | { | 1231 | { |
1152 | int i; | 1232 | int i; |