diff options
author | Holger Dengler <hd@linux.vnet.ibm.com> | 2012-08-28 10:41:50 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2012-09-26 09:44:54 -0400 |
commit | 75014550516b147e5f530b84c71496341e036d6f (patch) | |
tree | de0993eb3ec2ea2b5ce0953b8a91630685f60fc5 /drivers/s390 | |
parent | 48a8ca03f8fd49a4d0c0c8843d4f5a7008dc2656 (diff) |
s390/ap: configuration information exploitation
Query AP configuration information. Improve performance of AP bus
scans by skipping AP device probing, if the AP deviec is not
configured.
Reviewed-by: Ingo Tuchscherer <ingo.tuchscherer@linux.vnet.ibm.com>
Signed-off-by: Holger Dengler <hd@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/crypto/ap_bus.c | 121 | ||||
-rw-r--r-- | drivers/s390/crypto/ap_bus.h | 22 |
2 files changed, 128 insertions, 15 deletions
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index ae258a4b4e5e..047c7327b1b4 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright IBM Corp. 2006 | 2 | * Copyright IBM Corp. 2006, 2012 |
3 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> | 3 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> |
4 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 4 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
5 | * Ralph Wuerthner <rwuerthn@de.ibm.com> | 5 | * Ralph Wuerthner <rwuerthn@de.ibm.com> |
@@ -62,13 +62,14 @@ static void ap_interrupt_handler(void *unused1, void *unused2); | |||
62 | static void ap_reset(struct ap_device *ap_dev); | 62 | static void ap_reset(struct ap_device *ap_dev); |
63 | static void ap_config_timeout(unsigned long ptr); | 63 | static void ap_config_timeout(unsigned long ptr); |
64 | static int ap_select_domain(void); | 64 | static int ap_select_domain(void); |
65 | static void ap_query_configuration(void); | ||
65 | 66 | ||
66 | /* | 67 | /* |
67 | * Module description. | 68 | * Module description. |
68 | */ | 69 | */ |
69 | MODULE_AUTHOR("IBM Corporation"); | 70 | MODULE_AUTHOR("IBM Corporation"); |
70 | MODULE_DESCRIPTION("Adjunct Processor Bus driver, " | 71 | MODULE_DESCRIPTION("Adjunct Processor Bus driver, " \ |
71 | "Copyright IBM Corp. 2006"); | 72 | "Copyright IBM Corp. 2006, 2012"); |
72 | MODULE_LICENSE("GPL"); | 73 | MODULE_LICENSE("GPL"); |
73 | 74 | ||
74 | /* | 75 | /* |
@@ -84,6 +85,7 @@ module_param_named(poll_thread, ap_thread_flag, int, 0000); | |||
84 | MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off)."); | 85 | MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off)."); |
85 | 86 | ||
86 | static struct device *ap_root_device = NULL; | 87 | static struct device *ap_root_device = NULL; |
88 | static struct ap_config_info *ap_configuration; | ||
87 | static DEFINE_SPINLOCK(ap_device_list_lock); | 89 | static DEFINE_SPINLOCK(ap_device_list_lock); |
88 | static LIST_HEAD(ap_device_list); | 90 | static LIST_HEAD(ap_device_list); |
89 | 91 | ||
@@ -158,6 +160,17 @@ static int ap_interrupts_available(void) | |||
158 | } | 160 | } |
159 | 161 | ||
160 | /** | 162 | /** |
163 | * ap_configuration_available(): Test if AP configuration | ||
164 | * information is available. | ||
165 | * | ||
166 | * Returns 1 if AP configuration information is available. | ||
167 | */ | ||
168 | static int ap_configuration_available(void) | ||
169 | { | ||
170 | return test_facility(2) && test_facility(12); | ||
171 | } | ||
172 | |||
173 | /** | ||
161 | * ap_test_queue(): Test adjunct processor queue. | 174 | * ap_test_queue(): Test adjunct processor queue. |
162 | * @qid: The AP queue number | 175 | * @qid: The AP queue number |
163 | * @queue_depth: Pointer to queue depth value | 176 | * @queue_depth: Pointer to queue depth value |
@@ -242,6 +255,26 @@ __ap_query_functions(ap_qid_t qid, unsigned int *functions) | |||
242 | } | 255 | } |
243 | #endif | 256 | #endif |
244 | 257 | ||
258 | #ifdef CONFIG_64BIT | ||
259 | static inline int __ap_query_configuration(struct ap_config_info *config) | ||
260 | { | ||
261 | register unsigned long reg0 asm ("0") = 0x04000000UL; | ||
262 | register unsigned long reg1 asm ("1") = -EINVAL; | ||
263 | register unsigned char *reg2 asm ("2") = (unsigned char *)config; | ||
264 | |||
265 | asm volatile( | ||
266 | ".long 0xb2af0000\n" /* PQAP(QCI) */ | ||
267 | "0: la %1,0\n" | ||
268 | "1:\n" | ||
269 | EX_TABLE(0b, 1b) | ||
270 | : "+d" (reg0), "+d" (reg1), "+d" (reg2) | ||
271 | : | ||
272 | : "cc"); | ||
273 | |||
274 | return reg1; | ||
275 | } | ||
276 | #endif | ||
277 | |||
245 | /** | 278 | /** |
246 | * ap_query_functions(): Query supported functions. | 279 | * ap_query_functions(): Query supported functions. |
247 | * @qid: The AP queue number | 280 | * @qid: The AP queue number |
@@ -305,8 +338,8 @@ int ap_4096_commands_available(ap_qid_t qid) | |||
305 | if (ap_query_functions(qid, &functions)) | 338 | if (ap_query_functions(qid, &functions)) |
306 | return 0; | 339 | return 0; |
307 | 340 | ||
308 | return test_ap_facility(functions, 1) && | 341 | return ap_test_bit(&functions, 1) && |
309 | test_ap_facility(functions, 2); | 342 | ap_test_bit(&functions, 2); |
310 | } | 343 | } |
311 | EXPORT_SYMBOL(ap_4096_commands_available); | 344 | EXPORT_SYMBOL(ap_4096_commands_available); |
312 | 345 | ||
@@ -772,6 +805,7 @@ static int ap_bus_resume(struct device *dev) | |||
772 | ap_suspend_flag = 0; | 805 | ap_suspend_flag = 0; |
773 | if (!ap_interrupts_available()) | 806 | if (!ap_interrupts_available()) |
774 | ap_interrupt_indicator = NULL; | 807 | ap_interrupt_indicator = NULL; |
808 | ap_query_configuration(); | ||
775 | if (!user_set_domain) { | 809 | if (!user_set_domain) { |
776 | ap_domain_index = -1; | 810 | ap_domain_index = -1; |
777 | ap_select_domain(); | 811 | ap_select_domain(); |
@@ -997,6 +1031,65 @@ static struct bus_attribute *const ap_bus_attrs[] = { | |||
997 | NULL, | 1031 | NULL, |
998 | }; | 1032 | }; |
999 | 1033 | ||
1034 | static inline int ap_test_config(unsigned int *field, unsigned int nr) | ||
1035 | { | ||
1036 | if (nr > 0xFFu) | ||
1037 | return 0; | ||
1038 | return ap_test_bit((field + (nr >> 5)), (nr & 0x1f)); | ||
1039 | } | ||
1040 | |||
1041 | /* | ||
1042 | * ap_test_config_card_id(): Test, whether an AP card ID is configured. | ||
1043 | * @id AP card ID | ||
1044 | * | ||
1045 | * Returns 0 if the card is not configured | ||
1046 | * 1 if the card is configured or | ||
1047 | * if the configuration information is not available | ||
1048 | */ | ||
1049 | static inline int ap_test_config_card_id(unsigned int id) | ||
1050 | { | ||
1051 | if (!ap_configuration) | ||
1052 | return 1; | ||
1053 | return ap_test_config(ap_configuration->apm, id); | ||
1054 | } | ||
1055 | |||
1056 | /* | ||
1057 | * ap_test_config_domain(): Test, whether an AP usage domain is configured. | ||
1058 | * @domain AP usage domain ID | ||
1059 | * | ||
1060 | * Returns 0 if the usage domain is not configured | ||
1061 | * 1 if the usage domain is configured or | ||
1062 | * if the configuration information is not available | ||
1063 | */ | ||
1064 | static inline int ap_test_config_domain(unsigned int domain) | ||
1065 | { | ||
1066 | if (!ap_configuration) | ||
1067 | return 1; | ||
1068 | return ap_test_config(ap_configuration->aqm, domain); | ||
1069 | } | ||
1070 | |||
1071 | /** | ||
1072 | * ap_query_configuration(): Query AP configuration information. | ||
1073 | * | ||
1074 | * Query information of installed cards and configured domains from AP. | ||
1075 | */ | ||
1076 | static void ap_query_configuration(void) | ||
1077 | { | ||
1078 | #ifdef CONFIG_64BIT | ||
1079 | if (ap_configuration_available()) { | ||
1080 | if (!ap_configuration) | ||
1081 | ap_configuration = | ||
1082 | kzalloc(sizeof(struct ap_config_info), | ||
1083 | GFP_KERNEL); | ||
1084 | if (ap_configuration) | ||
1085 | __ap_query_configuration(ap_configuration); | ||
1086 | } else | ||
1087 | ap_configuration = NULL; | ||
1088 | #else | ||
1089 | ap_configuration = NULL; | ||
1090 | #endif | ||
1091 | } | ||
1092 | |||
1000 | /** | 1093 | /** |
1001 | * ap_select_domain(): Select an AP domain. | 1094 | * ap_select_domain(): Select an AP domain. |
1002 | * | 1095 | * |
@@ -1005,6 +1098,7 @@ static struct bus_attribute *const ap_bus_attrs[] = { | |||
1005 | static int ap_select_domain(void) | 1098 | static int ap_select_domain(void) |
1006 | { | 1099 | { |
1007 | int queue_depth, device_type, count, max_count, best_domain; | 1100 | int queue_depth, device_type, count, max_count, best_domain; |
1101 | ap_qid_t qid; | ||
1008 | int rc, i, j; | 1102 | int rc, i, j; |
1009 | 1103 | ||
1010 | /* | 1104 | /* |
@@ -1018,9 +1112,13 @@ static int ap_select_domain(void) | |||
1018 | best_domain = -1; | 1112 | best_domain = -1; |
1019 | max_count = 0; | 1113 | max_count = 0; |
1020 | for (i = 0; i < AP_DOMAINS; i++) { | 1114 | for (i = 0; i < AP_DOMAINS; i++) { |
1115 | if (!ap_test_config_domain(i)) | ||
1116 | continue; | ||
1021 | count = 0; | 1117 | count = 0; |
1022 | for (j = 0; j < AP_DEVICES; j++) { | 1118 | for (j = 0; j < AP_DEVICES; j++) { |
1023 | ap_qid_t qid = AP_MKQID(j, i); | 1119 | if (!ap_test_config_card_id(j)) |
1120 | continue; | ||
1121 | qid = AP_MKQID(j, i); | ||
1024 | rc = ap_query_queue(qid, &queue_depth, &device_type); | 1122 | rc = ap_query_queue(qid, &queue_depth, &device_type); |
1025 | if (rc) | 1123 | if (rc) |
1026 | continue; | 1124 | continue; |
@@ -1169,6 +1267,7 @@ static void ap_scan_bus(struct work_struct *unused) | |||
1169 | unsigned int device_functions; | 1267 | unsigned int device_functions; |
1170 | int rc, i; | 1268 | int rc, i; |
1171 | 1269 | ||
1270 | ap_query_configuration(); | ||
1172 | if (ap_select_domain() != 0) | 1271 | if (ap_select_domain() != 0) |
1173 | return; | 1272 | return; |
1174 | for (i = 0; i < AP_DEVICES; i++) { | 1273 | for (i = 0; i < AP_DEVICES; i++) { |
@@ -1176,7 +1275,10 @@ static void ap_scan_bus(struct work_struct *unused) | |||
1176 | dev = bus_find_device(&ap_bus_type, NULL, | 1275 | dev = bus_find_device(&ap_bus_type, NULL, |
1177 | (void *)(unsigned long)qid, | 1276 | (void *)(unsigned long)qid, |
1178 | __ap_scan_bus); | 1277 | __ap_scan_bus); |
1179 | rc = ap_query_queue(qid, &queue_depth, &device_type); | 1278 | if (ap_test_config_card_id(i)) |
1279 | rc = ap_query_queue(qid, &queue_depth, &device_type); | ||
1280 | else | ||
1281 | rc = -ENODEV; | ||
1180 | if (dev) { | 1282 | if (dev) { |
1181 | if (rc == -EBUSY) { | 1283 | if (rc == -EBUSY) { |
1182 | set_current_state(TASK_UNINTERRUPTIBLE); | 1284 | set_current_state(TASK_UNINTERRUPTIBLE); |
@@ -1227,9 +1329,9 @@ static void ap_scan_bus(struct work_struct *unused) | |||
1227 | kfree(ap_dev); | 1329 | kfree(ap_dev); |
1228 | continue; | 1330 | continue; |
1229 | } | 1331 | } |
1230 | if (test_ap_facility(device_functions, 3)) | 1332 | if (ap_test_bit(&device_functions, 3)) |
1231 | ap_dev->device_type = AP_DEVICE_TYPE_CEX3C; | 1333 | ap_dev->device_type = AP_DEVICE_TYPE_CEX3C; |
1232 | else if (test_ap_facility(device_functions, 4)) | 1334 | else if (ap_test_bit(&device_functions, 4)) |
1233 | ap_dev->device_type = AP_DEVICE_TYPE_CEX3A; | 1335 | ap_dev->device_type = AP_DEVICE_TYPE_CEX3A; |
1234 | else { | 1336 | else { |
1235 | kfree(ap_dev); | 1337 | kfree(ap_dev); |
@@ -1785,6 +1887,7 @@ int __init ap_module_init(void) | |||
1785 | goto out_root; | 1887 | goto out_root; |
1786 | } | 1888 | } |
1787 | 1889 | ||
1890 | ap_query_configuration(); | ||
1788 | if (ap_select_domain() == 0) | 1891 | if (ap_select_domain() == 0) |
1789 | ap_scan_bus(NULL); | 1892 | ap_scan_bus(NULL); |
1790 | 1893 | ||
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 52d61995af88..5018f66dada5 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright IBM Corp. 2006 | 2 | * Copyright IBM Corp. 2006, 2012 |
3 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> | 3 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> |
4 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 4 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
5 | * Ralph Wuerthner <rwuerthn@de.ibm.com> | 5 | * Ralph Wuerthner <rwuerthn@de.ibm.com> |
@@ -83,13 +83,12 @@ int ap_queue_status_invalid_test(struct ap_queue_status *status) | |||
83 | return !(memcmp(status, &invalid, sizeof(struct ap_queue_status))); | 83 | return !(memcmp(status, &invalid, sizeof(struct ap_queue_status))); |
84 | } | 84 | } |
85 | 85 | ||
86 | #define MAX_AP_FACILITY 31 | 86 | #define AP_MAX_BITS 31 |
87 | 87 | static inline int ap_test_bit(unsigned int *ptr, unsigned int nr) | |
88 | static inline int test_ap_facility(unsigned int function, unsigned int nr) | ||
89 | { | 88 | { |
90 | if (nr > MAX_AP_FACILITY) | 89 | if (nr > AP_MAX_BITS) |
91 | return 0; | 90 | return 0; |
92 | return function & (unsigned int)(0x80000000 >> nr); | 91 | return (*ptr & (0x80000000u >> nr)) != 0; |
93 | } | 92 | } |
94 | 93 | ||
95 | #define AP_RESPONSE_NORMAL 0x00 | 94 | #define AP_RESPONSE_NORMAL 0x00 |
@@ -183,6 +182,17 @@ struct ap_message { | |||
183 | struct ap_message *); | 182 | struct ap_message *); |
184 | }; | 183 | }; |
185 | 184 | ||
185 | struct ap_config_info { | ||
186 | unsigned int special_command:1; | ||
187 | unsigned int ap_extended:1; | ||
188 | unsigned char reserved1:6; | ||
189 | unsigned char reserved2[15]; | ||
190 | unsigned int apm[8]; /* AP ID mask */ | ||
191 | unsigned int aqm[8]; /* AP queue mask */ | ||
192 | unsigned int adm[8]; /* AP domain mask */ | ||
193 | unsigned char reserved4[16]; | ||
194 | } __packed; | ||
195 | |||
186 | #define AP_DEVICE(dt) \ | 196 | #define AP_DEVICE(dt) \ |
187 | .dev_type=(dt), \ | 197 | .dev_type=(dt), \ |
188 | .match_flags=AP_DEVICE_ID_MATCH_DEVICE_TYPE, | 198 | .match_flags=AP_DEVICE_ID_MATCH_DEVICE_TYPE, |