diff options
author | Felix Beck <felix.beck@de.ibm.com> | 2008-12-25 07:38:41 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-12-25 07:38:57 -0500 |
commit | cb17a6364a29b4dfe5bbb00696032fb63d780157 (patch) | |
tree | 9bd2d71b6bc80188f3ff1eac9deeacad96b8124f /drivers | |
parent | 320c04c068c62b71fe9ea55e06e4968b4edc9e48 (diff) |
[S390] zcrypt: Use of Thin Interrupts
When the machine supports AP adapter interrupts polling will be
switched off at module initialization and the driver will work in
interrupt mode.
Signed-off-by: Felix Beck <felix.beck@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/s390/crypto/ap_bus.c | 174 | ||||
-rw-r--r-- | drivers/s390/crypto/ap_bus.h | 6 |
2 files changed, 176 insertions, 4 deletions
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index e3fe6838293a..2335be5f9bf7 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> | 5 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> |
6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
7 | * Ralph Wuerthner <rwuerthn@de.ibm.com> | 7 | * Ralph Wuerthner <rwuerthn@de.ibm.com> |
8 | * Felix Beck <felix.beck@de.ibm.com> | ||
8 | * | 9 | * |
9 | * Adjunct processor bus. | 10 | * Adjunct processor bus. |
10 | * | 11 | * |
@@ -34,6 +35,10 @@ | |||
34 | #include <linux/mutex.h> | 35 | #include <linux/mutex.h> |
35 | #include <asm/s390_rdev.h> | 36 | #include <asm/s390_rdev.h> |
36 | #include <asm/reset.h> | 37 | #include <asm/reset.h> |
38 | #include <asm/airq.h> | ||
39 | #include <asm/atomic.h> | ||
40 | #include <asm/system.h> | ||
41 | #include <asm/isc.h> | ||
37 | #include <linux/hrtimer.h> | 42 | #include <linux/hrtimer.h> |
38 | #include <linux/ktime.h> | 43 | #include <linux/ktime.h> |
39 | 44 | ||
@@ -46,6 +51,7 @@ static enum hrtimer_restart ap_poll_timeout(struct hrtimer *); | |||
46 | static int ap_poll_thread_start(void); | 51 | static int ap_poll_thread_start(void); |
47 | static void ap_poll_thread_stop(void); | 52 | static void ap_poll_thread_stop(void); |
48 | static void ap_request_timeout(unsigned long); | 53 | static void ap_request_timeout(unsigned long); |
54 | static inline void ap_schedule_poll_timer(void); | ||
49 | 55 | ||
50 | /* | 56 | /* |
51 | * Module description. | 57 | * Module description. |
@@ -80,19 +86,29 @@ static int ap_config_time = AP_CONFIG_TIME; | |||
80 | static DECLARE_WORK(ap_config_work, ap_scan_bus); | 86 | static DECLARE_WORK(ap_config_work, ap_scan_bus); |
81 | 87 | ||
82 | /* | 88 | /* |
83 | * Tasklet & timer for AP request polling. | 89 | * Tasklet & timer for AP request polling and interrupts |
84 | */ | 90 | */ |
85 | static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0); | 91 | static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0); |
86 | static atomic_t ap_poll_requests = ATOMIC_INIT(0); | 92 | static atomic_t ap_poll_requests = ATOMIC_INIT(0); |
87 | static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait); | 93 | static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait); |
88 | static struct task_struct *ap_poll_kthread = NULL; | 94 | static struct task_struct *ap_poll_kthread = NULL; |
89 | static DEFINE_MUTEX(ap_poll_thread_mutex); | 95 | static DEFINE_MUTEX(ap_poll_thread_mutex); |
96 | static void *ap_interrupt_indicator; | ||
90 | static struct hrtimer ap_poll_timer; | 97 | static struct hrtimer ap_poll_timer; |
91 | /* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds. | 98 | /* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds. |
92 | * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/ | 99 | * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/ |
93 | static unsigned long long poll_timeout = 250000; | 100 | static unsigned long long poll_timeout = 250000; |
94 | 101 | ||
95 | /** | 102 | /** |
103 | * ap_using_interrupts() - Returns non-zero if interrupt support is | ||
104 | * available. | ||
105 | */ | ||
106 | static inline int ap_using_interrupts(void) | ||
107 | { | ||
108 | return ap_interrupt_indicator != NULL; | ||
109 | } | ||
110 | |||
111 | /** | ||
96 | * ap_intructions_available() - Test if AP instructions are available. | 112 | * ap_intructions_available() - Test if AP instructions are available. |
97 | * | 113 | * |
98 | * Returns 0 if the AP instructions are installed. | 114 | * Returns 0 if the AP instructions are installed. |
@@ -113,6 +129,23 @@ static inline int ap_instructions_available(void) | |||
113 | } | 129 | } |
114 | 130 | ||
115 | /** | 131 | /** |
132 | * ap_interrupts_available(): Test if AP interrupts are available. | ||
133 | * | ||
134 | * Returns 1 if AP interrupts are available. | ||
135 | */ | ||
136 | static int ap_interrupts_available(void) | ||
137 | { | ||
138 | unsigned long long facility_bits[2]; | ||
139 | |||
140 | if (stfle(facility_bits, 2) <= 1) | ||
141 | return 0; | ||
142 | if (!(facility_bits[0] & (1ULL << 61)) || | ||
143 | !(facility_bits[1] & (1ULL << 62))) | ||
144 | return 0; | ||
145 | return 1; | ||
146 | } | ||
147 | |||
148 | /** | ||
116 | * ap_test_queue(): Test adjunct processor queue. | 149 | * ap_test_queue(): Test adjunct processor queue. |
117 | * @qid: The AP queue number | 150 | * @qid: The AP queue number |
118 | * @queue_depth: Pointer to queue depth value | 151 | * @queue_depth: Pointer to queue depth value |
@@ -152,6 +185,80 @@ static inline struct ap_queue_status ap_reset_queue(ap_qid_t qid) | |||
152 | return reg1; | 185 | return reg1; |
153 | } | 186 | } |
154 | 187 | ||
188 | #ifdef CONFIG_64BIT | ||
189 | /** | ||
190 | * ap_queue_interruption_control(): Enable interruption for a specific AP. | ||
191 | * @qid: The AP queue number | ||
192 | * @ind: The notification indicator byte | ||
193 | * | ||
194 | * Returns AP queue status. | ||
195 | */ | ||
196 | static inline struct ap_queue_status | ||
197 | ap_queue_interruption_control(ap_qid_t qid, void *ind) | ||
198 | { | ||
199 | register unsigned long reg0 asm ("0") = qid | 0x03000000UL; | ||
200 | register unsigned long reg1_in asm ("1") = 0x0000800000000000UL | AP_ISC; | ||
201 | register struct ap_queue_status reg1_out asm ("1"); | ||
202 | register void *reg2 asm ("2") = ind; | ||
203 | asm volatile( | ||
204 | ".long 0xb2af0000" /* PQAP(RAPQ) */ | ||
205 | : "+d" (reg0), "+d" (reg1_in), "=d" (reg1_out), "+d" (reg2) | ||
206 | : | ||
207 | : "cc" ); | ||
208 | return reg1_out; | ||
209 | } | ||
210 | #endif | ||
211 | |||
212 | /** | ||
213 | * ap_queue_enable_interruption(): Enable interruption on an AP. | ||
214 | * @qid: The AP queue number | ||
215 | * @ind: the notification indicator byte | ||
216 | * | ||
217 | * Enables interruption on AP queue via ap_queue_interruption_control(). Based | ||
218 | * on the return value it waits a while and tests the AP queue if interrupts | ||
219 | * have been switched on using ap_test_queue(). | ||
220 | */ | ||
221 | static int ap_queue_enable_interruption(ap_qid_t qid, void *ind) | ||
222 | { | ||
223 | #ifdef CONFIG_64BIT | ||
224 | struct ap_queue_status status; | ||
225 | int t_depth, t_device_type, rc, i; | ||
226 | |||
227 | rc = -EBUSY; | ||
228 | status = ap_queue_interruption_control(qid, ind); | ||
229 | |||
230 | for (i = 0; i < AP_MAX_RESET; i++) { | ||
231 | switch (status.response_code) { | ||
232 | case AP_RESPONSE_NORMAL: | ||
233 | if (status.int_enabled) | ||
234 | return 0; | ||
235 | break; | ||
236 | case AP_RESPONSE_RESET_IN_PROGRESS: | ||
237 | case AP_RESPONSE_BUSY: | ||
238 | break; | ||
239 | case AP_RESPONSE_Q_NOT_AVAIL: | ||
240 | case AP_RESPONSE_DECONFIGURED: | ||
241 | case AP_RESPONSE_CHECKSTOPPED: | ||
242 | case AP_RESPONSE_INVALID_ADDRESS: | ||
243 | return -ENODEV; | ||
244 | case AP_RESPONSE_OTHERWISE_CHANGED: | ||
245 | if (status.int_enabled) | ||
246 | return 0; | ||
247 | break; | ||
248 | default: | ||
249 | break; | ||
250 | } | ||
251 | if (i < AP_MAX_RESET - 1) { | ||
252 | udelay(5); | ||
253 | status = ap_test_queue(qid, &t_depth, &t_device_type); | ||
254 | } | ||
255 | } | ||
256 | return rc; | ||
257 | #else | ||
258 | return -EINVAL; | ||
259 | #endif | ||
260 | } | ||
261 | |||
155 | /** | 262 | /** |
156 | * __ap_send(): Send message to adjunct processor queue. | 263 | * __ap_send(): Send message to adjunct processor queue. |
157 | * @qid: The AP queue number | 264 | * @qid: The AP queue number |
@@ -295,6 +402,11 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type) | |||
295 | case AP_RESPONSE_CHECKSTOPPED: | 402 | case AP_RESPONSE_CHECKSTOPPED: |
296 | rc = -ENODEV; | 403 | rc = -ENODEV; |
297 | break; | 404 | break; |
405 | case AP_RESPONSE_INVALID_ADDRESS: | ||
406 | rc = -ENODEV; | ||
407 | break; | ||
408 | case AP_RESPONSE_OTHERWISE_CHANGED: | ||
409 | break; | ||
298 | case AP_RESPONSE_BUSY: | 410 | case AP_RESPONSE_BUSY: |
299 | break; | 411 | break; |
300 | default: | 412 | default: |
@@ -345,6 +457,15 @@ static int ap_init_queue(ap_qid_t qid) | |||
345 | status = ap_test_queue(qid, &dummy, &dummy); | 457 | status = ap_test_queue(qid, &dummy, &dummy); |
346 | } | 458 | } |
347 | } | 459 | } |
460 | if (rc == 0 && ap_using_interrupts()) { | ||
461 | rc = ap_queue_enable_interruption(qid, ap_interrupt_indicator); | ||
462 | /* If interruption mode is supported by the machine, | ||
463 | * but an AP can not be enabled for interruption then | ||
464 | * the AP will be discarded. */ | ||
465 | if (rc) | ||
466 | pr_err("Registering adapter interrupts for " | ||
467 | "AP %d failed\n", AP_QID_DEVICE(qid)); | ||
468 | } | ||
348 | return rc; | 469 | return rc; |
349 | } | 470 | } |
350 | 471 | ||
@@ -599,6 +720,14 @@ static ssize_t ap_config_time_show(struct bus_type *bus, char *buf) | |||
599 | return snprintf(buf, PAGE_SIZE, "%d\n", ap_config_time); | 720 | return snprintf(buf, PAGE_SIZE, "%d\n", ap_config_time); |
600 | } | 721 | } |
601 | 722 | ||
723 | static ssize_t ap_interrupts_show(struct bus_type *bus, char *buf) | ||
724 | { | ||
725 | return snprintf(buf, PAGE_SIZE, "%d\n", | ||
726 | ap_using_interrupts() ? 1 : 0); | ||
727 | } | ||
728 | |||
729 | static BUS_ATTR(ap_interrupts, 0444, ap_interrupts_show, NULL); | ||
730 | |||
602 | static ssize_t ap_config_time_store(struct bus_type *bus, | 731 | static ssize_t ap_config_time_store(struct bus_type *bus, |
603 | const char *buf, size_t count) | 732 | const char *buf, size_t count) |
604 | { | 733 | { |
@@ -653,7 +782,8 @@ static ssize_t poll_timeout_store(struct bus_type *bus, const char *buf, | |||
653 | ktime_t hr_time; | 782 | ktime_t hr_time; |
654 | 783 | ||
655 | /* 120 seconds = maximum poll interval */ | 784 | /* 120 seconds = maximum poll interval */ |
656 | if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 || time > 120000000000) | 785 | if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 || |
786 | time > 120000000000ULL) | ||
657 | return -EINVAL; | 787 | return -EINVAL; |
658 | poll_timeout = time; | 788 | poll_timeout = time; |
659 | hr_time = ktime_set(0, poll_timeout); | 789 | hr_time = ktime_set(0, poll_timeout); |
@@ -672,6 +802,7 @@ static struct bus_attribute *const ap_bus_attrs[] = { | |||
672 | &bus_attr_ap_domain, | 802 | &bus_attr_ap_domain, |
673 | &bus_attr_config_time, | 803 | &bus_attr_config_time, |
674 | &bus_attr_poll_thread, | 804 | &bus_attr_poll_thread, |
805 | &bus_attr_ap_interrupts, | ||
675 | &bus_attr_poll_timeout, | 806 | &bus_attr_poll_timeout, |
676 | NULL, | 807 | NULL, |
677 | }; | 808 | }; |
@@ -814,6 +945,11 @@ out: | |||
814 | return rc; | 945 | return rc; |
815 | } | 946 | } |
816 | 947 | ||
948 | static void ap_interrupt_handler(void *unused1, void *unused2) | ||
949 | { | ||
950 | tasklet_schedule(&ap_tasklet); | ||
951 | } | ||
952 | |||
817 | /** | 953 | /** |
818 | * __ap_scan_bus(): Scan the AP bus. | 954 | * __ap_scan_bus(): Scan the AP bus. |
819 | * @dev: Pointer to device | 955 | * @dev: Pointer to device |
@@ -928,6 +1064,8 @@ ap_config_timeout(unsigned long ptr) | |||
928 | */ | 1064 | */ |
929 | static inline void ap_schedule_poll_timer(void) | 1065 | static inline void ap_schedule_poll_timer(void) |
930 | { | 1066 | { |
1067 | if (ap_using_interrupts()) | ||
1068 | return; | ||
931 | if (hrtimer_is_queued(&ap_poll_timer)) | 1069 | if (hrtimer_is_queued(&ap_poll_timer)) |
932 | return; | 1070 | return; |
933 | hrtimer_start(&ap_poll_timer, ktime_set(0, poll_timeout), | 1071 | hrtimer_start(&ap_poll_timer, ktime_set(0, poll_timeout), |
@@ -1207,6 +1345,12 @@ static void ap_poll_all(unsigned long dummy) | |||
1207 | unsigned long flags; | 1345 | unsigned long flags; |
1208 | struct ap_device *ap_dev; | 1346 | struct ap_device *ap_dev; |
1209 | 1347 | ||
1348 | /* Reset the indicator if interrupts are used. Thus new interrupts can | ||
1349 | * be received. Doing it in the beginning of the tasklet is therefor | ||
1350 | * important that no requests on any AP get lost. | ||
1351 | */ | ||
1352 | if (ap_using_interrupts()) | ||
1353 | xchg((u8 *)ap_interrupt_indicator, 0); | ||
1210 | do { | 1354 | do { |
1211 | flags = 0; | 1355 | flags = 0; |
1212 | spin_lock(&ap_device_lock); | 1356 | spin_lock(&ap_device_lock); |
@@ -1268,6 +1412,8 @@ static int ap_poll_thread_start(void) | |||
1268 | { | 1412 | { |
1269 | int rc; | 1413 | int rc; |
1270 | 1414 | ||
1415 | if (ap_using_interrupts()) | ||
1416 | return 0; | ||
1271 | mutex_lock(&ap_poll_thread_mutex); | 1417 | mutex_lock(&ap_poll_thread_mutex); |
1272 | if (!ap_poll_kthread) { | 1418 | if (!ap_poll_kthread) { |
1273 | ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll"); | 1419 | ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll"); |
@@ -1301,8 +1447,12 @@ static void ap_request_timeout(unsigned long data) | |||
1301 | { | 1447 | { |
1302 | struct ap_device *ap_dev = (struct ap_device *) data; | 1448 | struct ap_device *ap_dev = (struct ap_device *) data; |
1303 | 1449 | ||
1304 | if (ap_dev->reset == AP_RESET_ARMED) | 1450 | if (ap_dev->reset == AP_RESET_ARMED) { |
1305 | ap_dev->reset = AP_RESET_DO; | 1451 | ap_dev->reset = AP_RESET_DO; |
1452 | |||
1453 | if (ap_using_interrupts()) | ||
1454 | tasklet_schedule(&ap_tasklet); | ||
1455 | } | ||
1306 | } | 1456 | } |
1307 | 1457 | ||
1308 | static void ap_reset_domain(void) | 1458 | static void ap_reset_domain(void) |
@@ -1345,6 +1495,16 @@ int __init ap_module_init(void) | |||
1345 | printk(KERN_WARNING "AP instructions not installed.\n"); | 1495 | printk(KERN_WARNING "AP instructions not installed.\n"); |
1346 | return -ENODEV; | 1496 | return -ENODEV; |
1347 | } | 1497 | } |
1498 | if (ap_interrupts_available()) { | ||
1499 | isc_register(AP_ISC); | ||
1500 | ap_interrupt_indicator = s390_register_adapter_interrupt( | ||
1501 | &ap_interrupt_handler, NULL, AP_ISC); | ||
1502 | if (IS_ERR(ap_interrupt_indicator)) { | ||
1503 | ap_interrupt_indicator = NULL; | ||
1504 | isc_unregister(AP_ISC); | ||
1505 | } | ||
1506 | } | ||
1507 | |||
1348 | register_reset_call(&ap_reset_call); | 1508 | register_reset_call(&ap_reset_call); |
1349 | 1509 | ||
1350 | /* Create /sys/bus/ap. */ | 1510 | /* Create /sys/bus/ap. */ |
@@ -1408,6 +1568,10 @@ out_bus: | |||
1408 | bus_unregister(&ap_bus_type); | 1568 | bus_unregister(&ap_bus_type); |
1409 | out: | 1569 | out: |
1410 | unregister_reset_call(&ap_reset_call); | 1570 | unregister_reset_call(&ap_reset_call); |
1571 | if (ap_using_interrupts()) { | ||
1572 | s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); | ||
1573 | isc_unregister(AP_ISC); | ||
1574 | } | ||
1411 | return rc; | 1575 | return rc; |
1412 | } | 1576 | } |
1413 | 1577 | ||
@@ -1443,6 +1607,10 @@ void ap_module_exit(void) | |||
1443 | bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); | 1607 | bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); |
1444 | bus_unregister(&ap_bus_type); | 1608 | bus_unregister(&ap_bus_type); |
1445 | unregister_reset_call(&ap_reset_call); | 1609 | unregister_reset_call(&ap_reset_call); |
1610 | if (ap_using_interrupts()) { | ||
1611 | s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); | ||
1612 | isc_unregister(AP_ISC); | ||
1613 | } | ||
1446 | } | 1614 | } |
1447 | 1615 | ||
1448 | #ifndef CONFIG_ZCRYPT_MONOLITHIC | 1616 | #ifndef CONFIG_ZCRYPT_MONOLITHIC |
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 446378b308fc..a35362241805 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h | |||
@@ -5,6 +5,7 @@ | |||
5 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> | 5 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> |
6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
7 | * Ralph Wuerthner <rwuerthn@de.ibm.com> | 7 | * Ralph Wuerthner <rwuerthn@de.ibm.com> |
8 | * Felix Beck <felix.beck@de.ibm.com> | ||
8 | * | 9 | * |
9 | * Adjunct processor bus header file. | 10 | * Adjunct processor bus header file. |
10 | * | 11 | * |
@@ -67,7 +68,8 @@ struct ap_queue_status { | |||
67 | unsigned int queue_empty : 1; | 68 | unsigned int queue_empty : 1; |
68 | unsigned int replies_waiting : 1; | 69 | unsigned int replies_waiting : 1; |
69 | unsigned int queue_full : 1; | 70 | unsigned int queue_full : 1; |
70 | unsigned int pad1 : 5; | 71 | unsigned int pad1 : 4; |
72 | unsigned int int_enabled : 1; | ||
71 | unsigned int response_code : 8; | 73 | unsigned int response_code : 8; |
72 | unsigned int pad2 : 16; | 74 | unsigned int pad2 : 16; |
73 | }; | 75 | }; |
@@ -78,6 +80,8 @@ struct ap_queue_status { | |||
78 | #define AP_RESPONSE_DECONFIGURED 0x03 | 80 | #define AP_RESPONSE_DECONFIGURED 0x03 |
79 | #define AP_RESPONSE_CHECKSTOPPED 0x04 | 81 | #define AP_RESPONSE_CHECKSTOPPED 0x04 |
80 | #define AP_RESPONSE_BUSY 0x05 | 82 | #define AP_RESPONSE_BUSY 0x05 |
83 | #define AP_RESPONSE_INVALID_ADDRESS 0x06 | ||
84 | #define AP_RESPONSE_OTHERWISE_CHANGED 0x07 | ||
81 | #define AP_RESPONSE_Q_FULL 0x10 | 85 | #define AP_RESPONSE_Q_FULL 0x10 |
82 | #define AP_RESPONSE_NO_PENDING_REPLY 0x10 | 86 | #define AP_RESPONSE_NO_PENDING_REPLY 0x10 |
83 | #define AP_RESPONSE_INDEX_TOO_BIG 0x11 | 87 | #define AP_RESPONSE_INDEX_TOO_BIG 0x11 |