Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Felix Beck and committed by
Martin Schwidefsky
cb17a636 320c04c0

+177 -4
+1
arch/s390/include/asm/isc.h
··· 17 17 #define CHSC_SCH_ISC 7 /* CHSC subchannels */ 18 18 /* Adapter interrupts. */ 19 19 #define QDIO_AIRQ_ISC IO_SCH_ISC /* I/O subchannel in qdio mode */ 20 + #define AP_ISC 6 /* adjunct processor (crypto) devices */ 20 21 21 22 /* Functions for registration of I/O interruption subclasses */ 22 23 void isc_register(unsigned int isc);
+171 -3
drivers/s390/crypto/ap_bus.c
··· 5 5 * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> 6 6 * Martin Schwidefsky <schwidefsky@de.ibm.com> 7 7 * Ralph Wuerthner <rwuerthn@de.ibm.com> 8 + * Felix Beck <felix.beck@de.ibm.com> 8 9 * 9 10 * Adjunct processor bus. 10 11 * ··· 35 34 #include <linux/mutex.h> 36 35 #include <asm/s390_rdev.h> 37 36 #include <asm/reset.h> 37 + #include <asm/airq.h> 38 + #include <asm/atomic.h> 39 + #include <asm/system.h> 40 + #include <asm/isc.h> 38 41 #include <linux/hrtimer.h> 39 42 #include <linux/ktime.h> 40 43 ··· 51 46 static int ap_poll_thread_start(void); 52 47 static void ap_poll_thread_stop(void); 53 48 static void ap_request_timeout(unsigned long); 49 + static inline void ap_schedule_poll_timer(void); 54 50 55 51 /* 56 52 * Module description. ··· 86 80 static DECLARE_WORK(ap_config_work, ap_scan_bus); 87 81 88 82 /* 89 - * Tasklet & timer for AP request polling. 83 + * Tasklet & timer for AP request polling and interrupts 90 84 */ 91 85 static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0); 92 86 static atomic_t ap_poll_requests = ATOMIC_INIT(0); 93 87 static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait); 94 88 static struct task_struct *ap_poll_kthread = NULL; 95 89 static DEFINE_MUTEX(ap_poll_thread_mutex); 90 + static void *ap_interrupt_indicator; 96 91 static struct hrtimer ap_poll_timer; 97 92 /* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds. 98 93 * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/ 99 94 static unsigned long long poll_timeout = 250000; 95 + 96 + /** 97 + * ap_using_interrupts() - Returns non-zero if interrupt support is 98 + * available. 99 + */ 100 + static inline int ap_using_interrupts(void) 101 + { 102 + return ap_interrupt_indicator != NULL; 103 + } 100 104 101 105 /** 102 106 * ap_intructions_available() - Test if AP instructions are available. ··· 126 110 EX_TABLE(0b, 1b) 127 111 : "+d" (reg0), "+d" (reg1), "+d" (reg2) : : "cc" ); 128 112 return reg1; 113 + } 114 + 115 + /** 116 + * ap_interrupts_available(): Test if AP interrupts are available. 117 + * 118 + * Returns 1 if AP interrupts are available. 119 + */ 120 + static int ap_interrupts_available(void) 121 + { 122 + unsigned long long facility_bits[2]; 123 + 124 + if (stfle(facility_bits, 2) <= 1) 125 + return 0; 126 + if (!(facility_bits[0] & (1ULL << 61)) || 127 + !(facility_bits[1] & (1ULL << 62))) 128 + return 0; 129 + return 1; 129 130 } 130 131 131 132 /** ··· 183 150 ".long 0xb2af0000" /* PQAP(RAPQ) */ 184 151 : "+d" (reg0), "=d" (reg1), "+d" (reg2) : : "cc"); 185 152 return reg1; 153 + } 154 + 155 + #ifdef CONFIG_64BIT 156 + /** 157 + * ap_queue_interruption_control(): Enable interruption for a specific AP. 158 + * @qid: The AP queue number 159 + * @ind: The notification indicator byte 160 + * 161 + * Returns AP queue status. 162 + */ 163 + static inline struct ap_queue_status 164 + ap_queue_interruption_control(ap_qid_t qid, void *ind) 165 + { 166 + register unsigned long reg0 asm ("0") = qid | 0x03000000UL; 167 + register unsigned long reg1_in asm ("1") = 0x0000800000000000UL | AP_ISC; 168 + register struct ap_queue_status reg1_out asm ("1"); 169 + register void *reg2 asm ("2") = ind; 170 + asm volatile( 171 + ".long 0xb2af0000" /* PQAP(RAPQ) */ 172 + : "+d" (reg0), "+d" (reg1_in), "=d" (reg1_out), "+d" (reg2) 173 + : 174 + : "cc" ); 175 + return reg1_out; 176 + } 177 + #endif 178 + 179 + /** 180 + * ap_queue_enable_interruption(): Enable interruption on an AP. 181 + * @qid: The AP queue number 182 + * @ind: the notification indicator byte 183 + * 184 + * Enables interruption on AP queue via ap_queue_interruption_control(). Based 185 + * on the return value it waits a while and tests the AP queue if interrupts 186 + * have been switched on using ap_test_queue(). 187 + */ 188 + static int ap_queue_enable_interruption(ap_qid_t qid, void *ind) 189 + { 190 + #ifdef CONFIG_64BIT 191 + struct ap_queue_status status; 192 + int t_depth, t_device_type, rc, i; 193 + 194 + rc = -EBUSY; 195 + status = ap_queue_interruption_control(qid, ind); 196 + 197 + for (i = 0; i < AP_MAX_RESET; i++) { 198 + switch (status.response_code) { 199 + case AP_RESPONSE_NORMAL: 200 + if (status.int_enabled) 201 + return 0; 202 + break; 203 + case AP_RESPONSE_RESET_IN_PROGRESS: 204 + case AP_RESPONSE_BUSY: 205 + break; 206 + case AP_RESPONSE_Q_NOT_AVAIL: 207 + case AP_RESPONSE_DECONFIGURED: 208 + case AP_RESPONSE_CHECKSTOPPED: 209 + case AP_RESPONSE_INVALID_ADDRESS: 210 + return -ENODEV; 211 + case AP_RESPONSE_OTHERWISE_CHANGED: 212 + if (status.int_enabled) 213 + return 0; 214 + break; 215 + default: 216 + break; 217 + } 218 + if (i < AP_MAX_RESET - 1) { 219 + udelay(5); 220 + status = ap_test_queue(qid, &t_depth, &t_device_type); 221 + } 222 + } 223 + return rc; 224 + #else 225 + return -EINVAL; 226 + #endif 186 227 } 187 228 188 229 /** ··· 402 295 case AP_RESPONSE_CHECKSTOPPED: 403 296 rc = -ENODEV; 404 297 break; 298 + case AP_RESPONSE_INVALID_ADDRESS: 299 + rc = -ENODEV; 300 + break; 301 + case AP_RESPONSE_OTHERWISE_CHANGED: 302 + break; 405 303 case AP_RESPONSE_BUSY: 406 304 break; 407 305 default: ··· 456 344 udelay(5); 457 345 status = ap_test_queue(qid, &dummy, &dummy); 458 346 } 347 + } 348 + if (rc == 0 && ap_using_interrupts()) { 349 + rc = ap_queue_enable_interruption(qid, ap_interrupt_indicator); 350 + /* If interruption mode is supported by the machine, 351 + * but an AP can not be enabled for interruption then 352 + * the AP will be discarded. */ 353 + if (rc) 354 + pr_err("Registering adapter interrupts for " 355 + "AP %d failed\n", AP_QID_DEVICE(qid)); 459 356 } 460 357 return rc; 461 358 } ··· 720 599 return snprintf(buf, PAGE_SIZE, "%d\n", ap_config_time); 721 600 } 722 601 602 + static ssize_t ap_interrupts_show(struct bus_type *bus, char *buf) 603 + { 604 + return snprintf(buf, PAGE_SIZE, "%d\n", 605 + ap_using_interrupts() ? 1 : 0); 606 + } 607 + 608 + static BUS_ATTR(ap_interrupts, 0444, ap_interrupts_show, NULL); 609 + 723 610 static ssize_t ap_config_time_store(struct bus_type *bus, 724 611 const char *buf, size_t count) 725 612 { ··· 782 653 ktime_t hr_time; 783 654 784 655 /* 120 seconds = maximum poll interval */ 785 - if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 || time > 120000000000) 656 + if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 || 657 + time > 120000000000ULL) 786 658 return -EINVAL; 787 659 poll_timeout = time; 788 660 hr_time = ktime_set(0, poll_timeout); ··· 802 672 &bus_attr_ap_domain, 803 673 &bus_attr_config_time, 804 674 &bus_attr_poll_thread, 675 + &bus_attr_ap_interrupts, 805 676 &bus_attr_poll_timeout, 806 677 NULL, 807 678 }; ··· 945 814 return rc; 946 815 } 947 816 817 + static void ap_interrupt_handler(void *unused1, void *unused2) 818 + { 819 + tasklet_schedule(&ap_tasklet); 820 + } 821 + 948 822 /** 949 823 * __ap_scan_bus(): Scan the AP bus. 950 824 * @dev: Pointer to device ··· 1064 928 */ 1065 929 static inline void ap_schedule_poll_timer(void) 1066 930 { 931 + if (ap_using_interrupts()) 932 + return; 1067 933 if (hrtimer_is_queued(&ap_poll_timer)) 1068 934 return; 1069 935 hrtimer_start(&ap_poll_timer, ktime_set(0, poll_timeout), ··· 1345 1207 unsigned long flags; 1346 1208 struct ap_device *ap_dev; 1347 1209 1210 + /* Reset the indicator if interrupts are used. Thus new interrupts can 1211 + * be received. Doing it in the beginning of the tasklet is therefor 1212 + * important that no requests on any AP get lost. 1213 + */ 1214 + if (ap_using_interrupts()) 1215 + xchg((u8 *)ap_interrupt_indicator, 0); 1348 1216 do { 1349 1217 flags = 0; 1350 1218 spin_lock(&ap_device_lock); ··· 1412 1268 { 1413 1269 int rc; 1414 1270 1271 + if (ap_using_interrupts()) 1272 + return 0; 1415 1273 mutex_lock(&ap_poll_thread_mutex); 1416 1274 if (!ap_poll_kthread) { 1417 1275 ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll"); ··· 1447 1301 { 1448 1302 struct ap_device *ap_dev = (struct ap_device *) data; 1449 1303 1450 - if (ap_dev->reset == AP_RESET_ARMED) 1304 + if (ap_dev->reset == AP_RESET_ARMED) { 1451 1305 ap_dev->reset = AP_RESET_DO; 1306 + 1307 + if (ap_using_interrupts()) 1308 + tasklet_schedule(&ap_tasklet); 1309 + } 1452 1310 } 1453 1311 1454 1312 static void ap_reset_domain(void) ··· 1495 1345 printk(KERN_WARNING "AP instructions not installed.\n"); 1496 1346 return -ENODEV; 1497 1347 } 1348 + if (ap_interrupts_available()) { 1349 + isc_register(AP_ISC); 1350 + ap_interrupt_indicator = s390_register_adapter_interrupt( 1351 + &ap_interrupt_handler, NULL, AP_ISC); 1352 + if (IS_ERR(ap_interrupt_indicator)) { 1353 + ap_interrupt_indicator = NULL; 1354 + isc_unregister(AP_ISC); 1355 + } 1356 + } 1357 + 1498 1358 register_reset_call(&ap_reset_call); 1499 1359 1500 1360 /* Create /sys/bus/ap. */ ··· 1568 1408 bus_unregister(&ap_bus_type); 1569 1409 out: 1570 1410 unregister_reset_call(&ap_reset_call); 1411 + if (ap_using_interrupts()) { 1412 + s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); 1413 + isc_unregister(AP_ISC); 1414 + } 1571 1415 return rc; 1572 1416 } 1573 1417 ··· 1607 1443 bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); 1608 1444 bus_unregister(&ap_bus_type); 1609 1445 unregister_reset_call(&ap_reset_call); 1446 + if (ap_using_interrupts()) { 1447 + s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); 1448 + isc_unregister(AP_ISC); 1449 + } 1610 1450 } 1611 1451 1612 1452 #ifndef CONFIG_ZCRYPT_MONOLITHIC
+5 -1
drivers/s390/crypto/ap_bus.h
··· 5 5 * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> 6 6 * Martin Schwidefsky <schwidefsky@de.ibm.com> 7 7 * Ralph Wuerthner <rwuerthn@de.ibm.com> 8 + * Felix Beck <felix.beck@de.ibm.com> 8 9 * 9 10 * Adjunct processor bus header file. 10 11 * ··· 68 67 unsigned int queue_empty : 1; 69 68 unsigned int replies_waiting : 1; 70 69 unsigned int queue_full : 1; 71 - unsigned int pad1 : 5; 70 + unsigned int pad1 : 4; 71 + unsigned int int_enabled : 1; 72 72 unsigned int response_code : 8; 73 73 unsigned int pad2 : 16; 74 74 }; ··· 80 78 #define AP_RESPONSE_DECONFIGURED 0x03 81 79 #define AP_RESPONSE_CHECKSTOPPED 0x04 82 80 #define AP_RESPONSE_BUSY 0x05 81 + #define AP_RESPONSE_INVALID_ADDRESS 0x06 82 + #define AP_RESPONSE_OTHERWISE_CHANGED 0x07 83 83 #define AP_RESPONSE_Q_FULL 0x10 84 84 #define AP_RESPONSE_NO_PENDING_REPLY 0x10 85 85 #define AP_RESPONSE_INDEX_TOO_BIG 0x11