diff options
Diffstat (limited to 'drivers/s390/crypto/ap_bus.c')
-rw-r--r-- | drivers/s390/crypto/ap_bus.c | 1221 |
1 files changed, 1221 insertions, 0 deletions
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c new file mode 100644 index 000000000000..6ed0985c0c91 --- /dev/null +++ b/drivers/s390/crypto/ap_bus.c | |||
@@ -0,0 +1,1221 @@ | |||
1 | /* | ||
2 | * linux/drivers/s390/crypto/ap_bus.c | ||
3 | * | ||
4 | * Copyright (C) 2006 IBM Corporation | ||
5 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> | ||
6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | ||
7 | * Ralph Wuerthner <rwuerthn@de.ibm.com> | ||
8 | * | ||
9 | * Adjunct processor bus. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2, or (at your option) | ||
14 | * any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | */ | ||
25 | |||
26 | #include <linux/module.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/delay.h> | ||
29 | #include <linux/err.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/workqueue.h> | ||
32 | #include <linux/notifier.h> | ||
33 | #include <linux/kthread.h> | ||
34 | #include <linux/mutex.h> | ||
35 | #include <asm/s390_rdev.h> | ||
36 | |||
37 | #include "ap_bus.h" | ||
38 | |||
39 | /* Some prototypes. */ | ||
40 | static void ap_scan_bus(void *); | ||
41 | static void ap_poll_all(unsigned long); | ||
42 | static void ap_poll_timeout(unsigned long); | ||
43 | static int ap_poll_thread_start(void); | ||
44 | static void ap_poll_thread_stop(void); | ||
45 | |||
46 | /** | ||
47 | * Module description. | ||
48 | */ | ||
49 | MODULE_AUTHOR("IBM Corporation"); | ||
50 | MODULE_DESCRIPTION("Adjunct Processor Bus driver, " | ||
51 | "Copyright 2006 IBM Corporation"); | ||
52 | MODULE_LICENSE("GPL"); | ||
53 | |||
54 | /** | ||
55 | * Module parameter | ||
56 | */ | ||
57 | int ap_domain_index = -1; /* Adjunct Processor Domain Index */ | ||
58 | module_param_named(domain, ap_domain_index, int, 0000); | ||
59 | MODULE_PARM_DESC(domain, "domain index for ap devices"); | ||
60 | EXPORT_SYMBOL(ap_domain_index); | ||
61 | |||
62 | static int ap_thread_flag = 1; | ||
63 | module_param_named(poll_thread, ap_thread_flag, int, 0000); | ||
64 | MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 1 (on)."); | ||
65 | |||
66 | static struct device *ap_root_device = NULL; | ||
67 | |||
68 | /** | ||
69 | * Workqueue & timer for bus rescan. | ||
70 | */ | ||
71 | static struct workqueue_struct *ap_work_queue; | ||
72 | static struct timer_list ap_config_timer; | ||
73 | static int ap_config_time = AP_CONFIG_TIME; | ||
74 | static DECLARE_WORK(ap_config_work, ap_scan_bus, NULL); | ||
75 | |||
76 | /** | ||
77 | * Tasklet & timer for AP request polling. | ||
78 | */ | ||
79 | static struct timer_list ap_poll_timer = TIMER_INITIALIZER(ap_poll_timeout,0,0); | ||
80 | static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0); | ||
81 | static atomic_t ap_poll_requests = ATOMIC_INIT(0); | ||
82 | static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait); | ||
83 | static struct task_struct *ap_poll_kthread = NULL; | ||
84 | static DEFINE_MUTEX(ap_poll_thread_mutex); | ||
85 | |||
86 | /** | ||
87 | * Test if ap instructions are available. | ||
88 | * | ||
89 | * Returns 0 if the ap instructions are installed. | ||
90 | */ | ||
91 | static inline int ap_instructions_available(void) | ||
92 | { | ||
93 | register unsigned long reg0 asm ("0") = AP_MKQID(0,0); | ||
94 | register unsigned long reg1 asm ("1") = -ENODEV; | ||
95 | register unsigned long reg2 asm ("2") = 0UL; | ||
96 | |||
97 | asm volatile( | ||
98 | " .long 0xb2af0000\n" /* PQAP(TAPQ) */ | ||
99 | "0: la %1,0\n" | ||
100 | "1:\n" | ||
101 | EX_TABLE(0b, 1b) | ||
102 | : "+d" (reg0), "+d" (reg1), "+d" (reg2) : : "cc" ); | ||
103 | return reg1; | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * Test adjunct processor queue. | ||
108 | * @qid: the ap queue number | ||
109 | * @queue_depth: pointer to queue depth value | ||
110 | * @device_type: pointer to device type value | ||
111 | * | ||
112 | * Returns ap queue status structure. | ||
113 | */ | ||
114 | static inline struct ap_queue_status | ||
115 | ap_test_queue(ap_qid_t qid, int *queue_depth, int *device_type) | ||
116 | { | ||
117 | register unsigned long reg0 asm ("0") = qid; | ||
118 | register struct ap_queue_status reg1 asm ("1"); | ||
119 | register unsigned long reg2 asm ("2") = 0UL; | ||
120 | |||
121 | asm volatile(".long 0xb2af0000" /* PQAP(TAPQ) */ | ||
122 | : "+d" (reg0), "=d" (reg1), "+d" (reg2) : : "cc"); | ||
123 | *device_type = (int) (reg2 >> 24); | ||
124 | *queue_depth = (int) (reg2 & 0xff); | ||
125 | return reg1; | ||
126 | } | ||
127 | |||
128 | /** | ||
129 | * Reset adjunct processor queue. | ||
130 | * @qid: the ap queue number | ||
131 | * | ||
132 | * Returns ap queue status structure. | ||
133 | */ | ||
134 | static inline struct ap_queue_status ap_reset_queue(ap_qid_t qid) | ||
135 | { | ||
136 | register unsigned long reg0 asm ("0") = qid | 0x01000000UL; | ||
137 | register struct ap_queue_status reg1 asm ("1"); | ||
138 | register unsigned long reg2 asm ("2") = 0UL; | ||
139 | |||
140 | asm volatile( | ||
141 | ".long 0xb2af0000" /* PQAP(RAPQ) */ | ||
142 | : "+d" (reg0), "=d" (reg1), "+d" (reg2) : : "cc"); | ||
143 | return reg1; | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * Send message to adjunct processor queue. | ||
148 | * @qid: the ap queue number | ||
149 | * @psmid: the program supplied message identifier | ||
150 | * @msg: the message text | ||
151 | * @length: the message length | ||
152 | * | ||
153 | * Returns ap queue status structure. | ||
154 | * | ||
155 | * Condition code 1 on NQAP can't happen because the L bit is 1. | ||
156 | * | ||
157 | * Condition code 2 on NQAP also means the send is incomplete, | ||
158 | * because a segment boundary was reached. The NQAP is repeated. | ||
159 | */ | ||
160 | static inline struct ap_queue_status | ||
161 | __ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) | ||
162 | { | ||
163 | typedef struct { char _[length]; } msgblock; | ||
164 | register unsigned long reg0 asm ("0") = qid | 0x40000000UL; | ||
165 | register struct ap_queue_status reg1 asm ("1"); | ||
166 | register unsigned long reg2 asm ("2") = (unsigned long) msg; | ||
167 | register unsigned long reg3 asm ("3") = (unsigned long) length; | ||
168 | register unsigned long reg4 asm ("4") = (unsigned int) (psmid >> 32); | ||
169 | register unsigned long reg5 asm ("5") = (unsigned int) psmid; | ||
170 | |||
171 | asm volatile ( | ||
172 | "0: .long 0xb2ad0042\n" /* DQAP */ | ||
173 | " brc 2,0b" | ||
174 | : "+d" (reg0), "=d" (reg1), "+d" (reg2), "+d" (reg3) | ||
175 | : "d" (reg4), "d" (reg5), "m" (*(msgblock *) msg) | ||
176 | : "cc" ); | ||
177 | return reg1; | ||
178 | } | ||
179 | |||
180 | int ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) | ||
181 | { | ||
182 | struct ap_queue_status status; | ||
183 | |||
184 | status = __ap_send(qid, psmid, msg, length); | ||
185 | switch (status.response_code) { | ||
186 | case AP_RESPONSE_NORMAL: | ||
187 | return 0; | ||
188 | case AP_RESPONSE_Q_FULL: | ||
189 | return -EBUSY; | ||
190 | default: /* Device is gone. */ | ||
191 | return -ENODEV; | ||
192 | } | ||
193 | } | ||
194 | EXPORT_SYMBOL(ap_send); | ||
195 | |||
196 | /* | ||
197 | * Receive message from adjunct processor queue. | ||
198 | * @qid: the ap queue number | ||
199 | * @psmid: pointer to program supplied message identifier | ||
200 | * @msg: the message text | ||
201 | * @length: the message length | ||
202 | * | ||
203 | * Returns ap queue status structure. | ||
204 | * | ||
205 | * Condition code 1 on DQAP means the receive has taken place | ||
206 | * but only partially. The response is incomplete, hence the | ||
207 | * DQAP is repeated. | ||
208 | * | ||
209 | * Condition code 2 on DQAP also means the receive is incomplete, | ||
210 | * this time because a segment boundary was reached. Again, the | ||
211 | * DQAP is repeated. | ||
212 | * | ||
213 | * Note that gpr2 is used by the DQAP instruction to keep track of | ||
214 | * any 'residual' length, in case the instruction gets interrupted. | ||
215 | * Hence it gets zeroed before the instruction. | ||
216 | */ | ||
217 | static inline struct ap_queue_status | ||
218 | __ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length) | ||
219 | { | ||
220 | typedef struct { char _[length]; } msgblock; | ||
221 | register unsigned long reg0 asm("0") = qid | 0x80000000UL; | ||
222 | register struct ap_queue_status reg1 asm ("1"); | ||
223 | register unsigned long reg2 asm("2") = 0UL; | ||
224 | register unsigned long reg4 asm("4") = (unsigned long) msg; | ||
225 | register unsigned long reg5 asm("5") = (unsigned long) length; | ||
226 | register unsigned long reg6 asm("6") = 0UL; | ||
227 | register unsigned long reg7 asm("7") = 0UL; | ||
228 | |||
229 | |||
230 | asm volatile( | ||
231 | "0: .long 0xb2ae0064\n" | ||
232 | " brc 6,0b\n" | ||
233 | : "+d" (reg0), "=d" (reg1), "+d" (reg2), | ||
234 | "+d" (reg4), "+d" (reg5), "+d" (reg6), "+d" (reg7), | ||
235 | "=m" (*(msgblock *) msg) : : "cc" ); | ||
236 | *psmid = (((unsigned long long) reg6) << 32) + reg7; | ||
237 | return reg1; | ||
238 | } | ||
239 | |||
240 | int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length) | ||
241 | { | ||
242 | struct ap_queue_status status; | ||
243 | |||
244 | status = __ap_recv(qid, psmid, msg, length); | ||
245 | switch (status.response_code) { | ||
246 | case AP_RESPONSE_NORMAL: | ||
247 | return 0; | ||
248 | case AP_RESPONSE_NO_PENDING_REPLY: | ||
249 | if (status.queue_empty) | ||
250 | return -ENOENT; | ||
251 | return -EBUSY; | ||
252 | default: | ||
253 | return -ENODEV; | ||
254 | } | ||
255 | } | ||
256 | EXPORT_SYMBOL(ap_recv); | ||
257 | |||
258 | /** | ||
259 | * Check if an AP queue is available. The test is repeated for | ||
260 | * AP_MAX_RESET times. | ||
261 | * @qid: the ap queue number | ||
262 | * @queue_depth: pointer to queue depth value | ||
263 | * @device_type: pointer to device type value | ||
264 | */ | ||
265 | static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type) | ||
266 | { | ||
267 | struct ap_queue_status status; | ||
268 | int t_depth, t_device_type, rc, i; | ||
269 | |||
270 | rc = -EBUSY; | ||
271 | for (i = 0; i < AP_MAX_RESET; i++) { | ||
272 | status = ap_test_queue(qid, &t_depth, &t_device_type); | ||
273 | switch (status.response_code) { | ||
274 | case AP_RESPONSE_NORMAL: | ||
275 | *queue_depth = t_depth + 1; | ||
276 | *device_type = t_device_type; | ||
277 | rc = 0; | ||
278 | break; | ||
279 | case AP_RESPONSE_Q_NOT_AVAIL: | ||
280 | rc = -ENODEV; | ||
281 | break; | ||
282 | case AP_RESPONSE_RESET_IN_PROGRESS: | ||
283 | break; | ||
284 | case AP_RESPONSE_DECONFIGURED: | ||
285 | rc = -ENODEV; | ||
286 | break; | ||
287 | case AP_RESPONSE_CHECKSTOPPED: | ||
288 | rc = -ENODEV; | ||
289 | break; | ||
290 | case AP_RESPONSE_BUSY: | ||
291 | break; | ||
292 | default: | ||
293 | BUG(); | ||
294 | } | ||
295 | if (rc != -EBUSY) | ||
296 | break; | ||
297 | if (i < AP_MAX_RESET - 1) | ||
298 | udelay(5); | ||
299 | } | ||
300 | return rc; | ||
301 | } | ||
302 | |||
303 | /** | ||
304 | * Reset an AP queue and wait for it to become available again. | ||
305 | * @qid: the ap queue number | ||
306 | */ | ||
307 | static int ap_init_queue(ap_qid_t qid) | ||
308 | { | ||
309 | struct ap_queue_status status; | ||
310 | int rc, dummy, i; | ||
311 | |||
312 | rc = -ENODEV; | ||
313 | status = ap_reset_queue(qid); | ||
314 | for (i = 0; i < AP_MAX_RESET; i++) { | ||
315 | switch (status.response_code) { | ||
316 | case AP_RESPONSE_NORMAL: | ||
317 | if (status.queue_empty) | ||
318 | rc = 0; | ||
319 | break; | ||
320 | case AP_RESPONSE_Q_NOT_AVAIL: | ||
321 | case AP_RESPONSE_DECONFIGURED: | ||
322 | case AP_RESPONSE_CHECKSTOPPED: | ||
323 | i = AP_MAX_RESET; /* return with -ENODEV */ | ||
324 | break; | ||
325 | case AP_RESPONSE_RESET_IN_PROGRESS: | ||
326 | case AP_RESPONSE_BUSY: | ||
327 | default: | ||
328 | break; | ||
329 | } | ||
330 | if (rc != -ENODEV) | ||
331 | break; | ||
332 | if (i < AP_MAX_RESET - 1) { | ||
333 | udelay(5); | ||
334 | status = ap_test_queue(qid, &dummy, &dummy); | ||
335 | } | ||
336 | } | ||
337 | return rc; | ||
338 | } | ||
339 | |||
340 | /** | ||
341 | * AP device related attributes. | ||
342 | */ | ||
343 | static ssize_t ap_hwtype_show(struct device *dev, | ||
344 | struct device_attribute *attr, char *buf) | ||
345 | { | ||
346 | struct ap_device *ap_dev = to_ap_dev(dev); | ||
347 | return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->device_type); | ||
348 | } | ||
349 | static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL); | ||
350 | |||
351 | static ssize_t ap_depth_show(struct device *dev, struct device_attribute *attr, | ||
352 | char *buf) | ||
353 | { | ||
354 | struct ap_device *ap_dev = to_ap_dev(dev); | ||
355 | return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->queue_depth); | ||
356 | } | ||
357 | static DEVICE_ATTR(depth, 0444, ap_depth_show, NULL); | ||
358 | |||
359 | static ssize_t ap_request_count_show(struct device *dev, | ||
360 | struct device_attribute *attr, | ||
361 | char *buf) | ||
362 | { | ||
363 | struct ap_device *ap_dev = to_ap_dev(dev); | ||
364 | int rc; | ||
365 | |||
366 | spin_lock_bh(&ap_dev->lock); | ||
367 | rc = snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->total_request_count); | ||
368 | spin_unlock_bh(&ap_dev->lock); | ||
369 | return rc; | ||
370 | } | ||
371 | |||
372 | static DEVICE_ATTR(request_count, 0444, ap_request_count_show, NULL); | ||
373 | |||
374 | static ssize_t ap_modalias_show(struct device *dev, | ||
375 | struct device_attribute *attr, char *buf) | ||
376 | { | ||
377 | return sprintf(buf, "ap:t%02X", to_ap_dev(dev)->device_type); | ||
378 | } | ||
379 | |||
380 | static DEVICE_ATTR(modalias, 0444, ap_modalias_show, NULL); | ||
381 | |||
382 | static struct attribute *ap_dev_attrs[] = { | ||
383 | &dev_attr_hwtype.attr, | ||
384 | &dev_attr_depth.attr, | ||
385 | &dev_attr_request_count.attr, | ||
386 | &dev_attr_modalias.attr, | ||
387 | NULL | ||
388 | }; | ||
389 | static struct attribute_group ap_dev_attr_group = { | ||
390 | .attrs = ap_dev_attrs | ||
391 | }; | ||
392 | |||
393 | /** | ||
394 | * AP bus driver registration/unregistration. | ||
395 | */ | ||
396 | static int ap_bus_match(struct device *dev, struct device_driver *drv) | ||
397 | { | ||
398 | struct ap_device *ap_dev = to_ap_dev(dev); | ||
399 | struct ap_driver *ap_drv = to_ap_drv(drv); | ||
400 | struct ap_device_id *id; | ||
401 | |||
402 | /** | ||
403 | * Compare device type of the device with the list of | ||
404 | * supported types of the device_driver. | ||
405 | */ | ||
406 | for (id = ap_drv->ids; id->match_flags; id++) { | ||
407 | if ((id->match_flags & AP_DEVICE_ID_MATCH_DEVICE_TYPE) && | ||
408 | (id->dev_type != ap_dev->device_type)) | ||
409 | continue; | ||
410 | return 1; | ||
411 | } | ||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | /** | ||
416 | * uevent function for AP devices. It sets up a single environment | ||
417 | * variable DEV_TYPE which contains the hardware device type. | ||
418 | */ | ||
419 | static int ap_uevent (struct device *dev, char **envp, int num_envp, | ||
420 | char *buffer, int buffer_size) | ||
421 | { | ||
422 | struct ap_device *ap_dev = to_ap_dev(dev); | ||
423 | int length; | ||
424 | |||
425 | if (!ap_dev) | ||
426 | return -ENODEV; | ||
427 | |||
428 | /* Set up DEV_TYPE environment variable. */ | ||
429 | envp[0] = buffer; | ||
430 | length = scnprintf(buffer, buffer_size, "DEV_TYPE=%04X", | ||
431 | ap_dev->device_type); | ||
432 | if (buffer_size - length <= 0) | ||
433 | return -ENOMEM; | ||
434 | envp[1] = 0; | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | static struct bus_type ap_bus_type = { | ||
439 | .name = "ap", | ||
440 | .match = &ap_bus_match, | ||
441 | .uevent = &ap_uevent, | ||
442 | }; | ||
443 | |||
444 | static int ap_device_probe(struct device *dev) | ||
445 | { | ||
446 | struct ap_device *ap_dev = to_ap_dev(dev); | ||
447 | struct ap_driver *ap_drv = to_ap_drv(dev->driver); | ||
448 | int rc; | ||
449 | |||
450 | ap_dev->drv = ap_drv; | ||
451 | rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV; | ||
452 | if (rc) | ||
453 | ap_dev->unregistered = 1; | ||
454 | return rc; | ||
455 | } | ||
456 | |||
457 | /** | ||
458 | * Flush all requests from the request/pending queue of an AP device. | ||
459 | * @ap_dev: pointer to the AP device. | ||
460 | */ | ||
461 | static inline void __ap_flush_queue(struct ap_device *ap_dev) | ||
462 | { | ||
463 | struct ap_message *ap_msg, *next; | ||
464 | |||
465 | list_for_each_entry_safe(ap_msg, next, &ap_dev->pendingq, list) { | ||
466 | list_del_init(&ap_msg->list); | ||
467 | ap_dev->pendingq_count--; | ||
468 | ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); | ||
469 | } | ||
470 | list_for_each_entry_safe(ap_msg, next, &ap_dev->requestq, list) { | ||
471 | list_del_init(&ap_msg->list); | ||
472 | ap_dev->requestq_count--; | ||
473 | ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); | ||
474 | } | ||
475 | } | ||
476 | |||
477 | void ap_flush_queue(struct ap_device *ap_dev) | ||
478 | { | ||
479 | spin_lock_bh(&ap_dev->lock); | ||
480 | __ap_flush_queue(ap_dev); | ||
481 | spin_unlock_bh(&ap_dev->lock); | ||
482 | } | ||
483 | EXPORT_SYMBOL(ap_flush_queue); | ||
484 | |||
485 | static int ap_device_remove(struct device *dev) | ||
486 | { | ||
487 | struct ap_device *ap_dev = to_ap_dev(dev); | ||
488 | struct ap_driver *ap_drv = ap_dev->drv; | ||
489 | |||
490 | spin_lock_bh(&ap_dev->lock); | ||
491 | __ap_flush_queue(ap_dev); | ||
492 | /** | ||
493 | * set ->unregistered to 1 while holding the lock. This prevents | ||
494 | * new messages to be put on the queue from now on. | ||
495 | */ | ||
496 | ap_dev->unregistered = 1; | ||
497 | spin_unlock_bh(&ap_dev->lock); | ||
498 | if (ap_drv->remove) | ||
499 | ap_drv->remove(ap_dev); | ||
500 | return 0; | ||
501 | } | ||
502 | |||
503 | int ap_driver_register(struct ap_driver *ap_drv, struct module *owner, | ||
504 | char *name) | ||
505 | { | ||
506 | struct device_driver *drv = &ap_drv->driver; | ||
507 | |||
508 | drv->bus = &ap_bus_type; | ||
509 | drv->probe = ap_device_probe; | ||
510 | drv->remove = ap_device_remove; | ||
511 | drv->owner = owner; | ||
512 | drv->name = name; | ||
513 | return driver_register(drv); | ||
514 | } | ||
515 | EXPORT_SYMBOL(ap_driver_register); | ||
516 | |||
517 | void ap_driver_unregister(struct ap_driver *ap_drv) | ||
518 | { | ||
519 | driver_unregister(&ap_drv->driver); | ||
520 | } | ||
521 | EXPORT_SYMBOL(ap_driver_unregister); | ||
522 | |||
523 | /** | ||
524 | * AP bus attributes. | ||
525 | */ | ||
526 | static ssize_t ap_domain_show(struct bus_type *bus, char *buf) | ||
527 | { | ||
528 | return snprintf(buf, PAGE_SIZE, "%d\n", ap_domain_index); | ||
529 | } | ||
530 | |||
531 | static BUS_ATTR(ap_domain, 0444, ap_domain_show, NULL); | ||
532 | |||
533 | static ssize_t ap_config_time_show(struct bus_type *bus, char *buf) | ||
534 | { | ||
535 | return snprintf(buf, PAGE_SIZE, "%d\n", ap_config_time); | ||
536 | } | ||
537 | |||
538 | static ssize_t ap_config_time_store(struct bus_type *bus, | ||
539 | const char *buf, size_t count) | ||
540 | { | ||
541 | int time; | ||
542 | |||
543 | if (sscanf(buf, "%d\n", &time) != 1 || time < 5 || time > 120) | ||
544 | return -EINVAL; | ||
545 | ap_config_time = time; | ||
546 | if (!timer_pending(&ap_config_timer) || | ||
547 | !mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ)) { | ||
548 | ap_config_timer.expires = jiffies + ap_config_time * HZ; | ||
549 | add_timer(&ap_config_timer); | ||
550 | } | ||
551 | return count; | ||
552 | } | ||
553 | |||
554 | static BUS_ATTR(config_time, 0644, ap_config_time_show, ap_config_time_store); | ||
555 | |||
556 | static ssize_t ap_poll_thread_show(struct bus_type *bus, char *buf) | ||
557 | { | ||
558 | return snprintf(buf, PAGE_SIZE, "%d\n", ap_poll_kthread ? 1 : 0); | ||
559 | } | ||
560 | |||
561 | static ssize_t ap_poll_thread_store(struct bus_type *bus, | ||
562 | const char *buf, size_t count) | ||
563 | { | ||
564 | int flag, rc; | ||
565 | |||
566 | if (sscanf(buf, "%d\n", &flag) != 1) | ||
567 | return -EINVAL; | ||
568 | if (flag) { | ||
569 | rc = ap_poll_thread_start(); | ||
570 | if (rc) | ||
571 | return rc; | ||
572 | } | ||
573 | else | ||
574 | ap_poll_thread_stop(); | ||
575 | return count; | ||
576 | } | ||
577 | |||
578 | static BUS_ATTR(poll_thread, 0644, ap_poll_thread_show, ap_poll_thread_store); | ||
579 | |||
580 | static struct bus_attribute *const ap_bus_attrs[] = { | ||
581 | &bus_attr_ap_domain, | ||
582 | &bus_attr_config_time, | ||
583 | &bus_attr_poll_thread, | ||
584 | NULL | ||
585 | }; | ||
586 | |||
587 | /** | ||
588 | * Pick one of the 16 ap domains. | ||
589 | */ | ||
590 | static inline int ap_select_domain(void) | ||
591 | { | ||
592 | int queue_depth, device_type, count, max_count, best_domain; | ||
593 | int rc, i, j; | ||
594 | |||
595 | /** | ||
596 | * We want to use a single domain. Either the one specified with | ||
597 | * the "domain=" parameter or the domain with the maximum number | ||
598 | * of devices. | ||
599 | */ | ||
600 | if (ap_domain_index >= 0 && ap_domain_index < AP_DOMAINS) | ||
601 | /* Domain has already been selected. */ | ||
602 | return 0; | ||
603 | best_domain = -1; | ||
604 | max_count = 0; | ||
605 | for (i = 0; i < AP_DOMAINS; i++) { | ||
606 | count = 0; | ||
607 | for (j = 0; j < AP_DEVICES; j++) { | ||
608 | ap_qid_t qid = AP_MKQID(j, i); | ||
609 | rc = ap_query_queue(qid, &queue_depth, &device_type); | ||
610 | if (rc) | ||
611 | continue; | ||
612 | count++; | ||
613 | } | ||
614 | if (count > max_count) { | ||
615 | max_count = count; | ||
616 | best_domain = i; | ||
617 | } | ||
618 | } | ||
619 | if (best_domain >= 0){ | ||
620 | ap_domain_index = best_domain; | ||
621 | return 0; | ||
622 | } | ||
623 | return -ENODEV; | ||
624 | } | ||
625 | |||
626 | /** | ||
627 | * Find the device type if query queue returned a device type of 0. | ||
628 | * @ap_dev: pointer to the AP device. | ||
629 | */ | ||
630 | static int ap_probe_device_type(struct ap_device *ap_dev) | ||
631 | { | ||
632 | static unsigned char msg[] = { | ||
633 | 0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00, | ||
634 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
635 | 0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00, | ||
636 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
637 | 0x01,0x00,0x43,0x43,0x41,0x2d,0x41,0x50, | ||
638 | 0x50,0x4c,0x20,0x20,0x20,0x01,0x01,0x01, | ||
639 | 0x00,0x00,0x00,0x00,0x50,0x4b,0x00,0x00, | ||
640 | 0x00,0x00,0x01,0x1c,0x00,0x00,0x00,0x00, | ||
641 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
642 | 0x00,0x00,0x05,0xb8,0x00,0x00,0x00,0x00, | ||
643 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
644 | 0x70,0x00,0x41,0x00,0x00,0x00,0x00,0x00, | ||
645 | 0x00,0x00,0x54,0x32,0x01,0x00,0xa0,0x00, | ||
646 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
647 | 0x00,0x00,0x00,0x00,0xb8,0x05,0x00,0x00, | ||
648 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
649 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
650 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
651 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
652 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
653 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
654 | 0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x00, | ||
655 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
656 | 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00, | ||
657 | 0x49,0x43,0x53,0x46,0x20,0x20,0x20,0x20, | ||
658 | 0x50,0x4b,0x0a,0x00,0x50,0x4b,0x43,0x53, | ||
659 | 0x2d,0x31,0x2e,0x32,0x37,0x00,0x11,0x22, | ||
660 | 0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00, | ||
661 | 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88, | ||
662 | 0x99,0x00,0x11,0x22,0x33,0x44,0x55,0x66, | ||
663 | 0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44, | ||
664 | 0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22, | ||
665 | 0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00, | ||
666 | 0x11,0x22,0x33,0x5d,0x00,0x5b,0x00,0x77, | ||
667 | 0x88,0x1e,0x00,0x00,0x57,0x00,0x00,0x00, | ||
668 | 0x00,0x04,0x00,0x00,0x4f,0x00,0x00,0x00, | ||
669 | 0x03,0x02,0x00,0x00,0x40,0x01,0x00,0x01, | ||
670 | 0xce,0x02,0x68,0x2d,0x5f,0xa9,0xde,0x0c, | ||
671 | 0xf6,0xd2,0x7b,0x58,0x4b,0xf9,0x28,0x68, | ||
672 | 0x3d,0xb4,0xf4,0xef,0x78,0xd5,0xbe,0x66, | ||
673 | 0x63,0x42,0xef,0xf8,0xfd,0xa4,0xf8,0xb0, | ||
674 | 0x8e,0x29,0xc2,0xc9,0x2e,0xd8,0x45,0xb8, | ||
675 | 0x53,0x8c,0x6f,0x4e,0x72,0x8f,0x6c,0x04, | ||
676 | 0x9c,0x88,0xfc,0x1e,0xc5,0x83,0x55,0x57, | ||
677 | 0xf7,0xdd,0xfd,0x4f,0x11,0x36,0x95,0x5d, | ||
678 | }; | ||
679 | struct ap_queue_status status; | ||
680 | unsigned long long psmid; | ||
681 | char *reply; | ||
682 | int rc, i; | ||
683 | |||
684 | reply = (void *) get_zeroed_page(GFP_KERNEL); | ||
685 | if (!reply) { | ||
686 | rc = -ENOMEM; | ||
687 | goto out; | ||
688 | } | ||
689 | |||
690 | status = __ap_send(ap_dev->qid, 0x0102030405060708ULL, | ||
691 | msg, sizeof(msg)); | ||
692 | if (status.response_code != AP_RESPONSE_NORMAL) { | ||
693 | rc = -ENODEV; | ||
694 | goto out_free; | ||
695 | } | ||
696 | |||
697 | /* Wait for the test message to complete. */ | ||
698 | for (i = 0; i < 6; i++) { | ||
699 | mdelay(300); | ||
700 | status = __ap_recv(ap_dev->qid, &psmid, reply, 4096); | ||
701 | if (status.response_code == AP_RESPONSE_NORMAL && | ||
702 | psmid == 0x0102030405060708ULL) | ||
703 | break; | ||
704 | } | ||
705 | if (i < 6) { | ||
706 | /* Got an answer. */ | ||
707 | if (reply[0] == 0x00 && reply[1] == 0x86) | ||
708 | ap_dev->device_type = AP_DEVICE_TYPE_PCICC; | ||
709 | else | ||
710 | ap_dev->device_type = AP_DEVICE_TYPE_PCICA; | ||
711 | rc = 0; | ||
712 | } else | ||
713 | rc = -ENODEV; | ||
714 | |||
715 | out_free: | ||
716 | free_page((unsigned long) reply); | ||
717 | out: | ||
718 | return rc; | ||
719 | } | ||
720 | |||
721 | /** | ||
722 | * Scan the ap bus for new devices. | ||
723 | */ | ||
724 | static int __ap_scan_bus(struct device *dev, void *data) | ||
725 | { | ||
726 | return to_ap_dev(dev)->qid == (ap_qid_t)(unsigned long) data; | ||
727 | } | ||
728 | |||
729 | static void ap_device_release(struct device *dev) | ||
730 | { | ||
731 | struct ap_device *ap_dev = to_ap_dev(dev); | ||
732 | |||
733 | kfree(ap_dev); | ||
734 | } | ||
735 | |||
736 | static void ap_scan_bus(void *data) | ||
737 | { | ||
738 | struct ap_device *ap_dev; | ||
739 | struct device *dev; | ||
740 | ap_qid_t qid; | ||
741 | int queue_depth, device_type; | ||
742 | int rc, i; | ||
743 | |||
744 | if (ap_select_domain() != 0) | ||
745 | return; | ||
746 | for (i = 0; i < AP_DEVICES; i++) { | ||
747 | qid = AP_MKQID(i, ap_domain_index); | ||
748 | dev = bus_find_device(&ap_bus_type, NULL, | ||
749 | (void *)(unsigned long)qid, | ||
750 | __ap_scan_bus); | ||
751 | if (dev) { | ||
752 | put_device(dev); | ||
753 | continue; | ||
754 | } | ||
755 | rc = ap_query_queue(qid, &queue_depth, &device_type); | ||
756 | if (rc) | ||
757 | continue; | ||
758 | rc = ap_init_queue(qid); | ||
759 | if (rc) | ||
760 | continue; | ||
761 | ap_dev = kzalloc(sizeof(*ap_dev), GFP_KERNEL); | ||
762 | if (!ap_dev) | ||
763 | break; | ||
764 | ap_dev->qid = qid; | ||
765 | ap_dev->queue_depth = queue_depth; | ||
766 | spin_lock_init(&ap_dev->lock); | ||
767 | INIT_LIST_HEAD(&ap_dev->pendingq); | ||
768 | INIT_LIST_HEAD(&ap_dev->requestq); | ||
769 | if (device_type == 0) | ||
770 | ap_probe_device_type(ap_dev); | ||
771 | else | ||
772 | ap_dev->device_type = device_type; | ||
773 | |||
774 | ap_dev->device.bus = &ap_bus_type; | ||
775 | ap_dev->device.parent = ap_root_device; | ||
776 | snprintf(ap_dev->device.bus_id, BUS_ID_SIZE, "card%02x", | ||
777 | AP_QID_DEVICE(ap_dev->qid)); | ||
778 | ap_dev->device.release = ap_device_release; | ||
779 | rc = device_register(&ap_dev->device); | ||
780 | if (rc) { | ||
781 | kfree(ap_dev); | ||
782 | continue; | ||
783 | } | ||
784 | /* Add device attributes. */ | ||
785 | rc = sysfs_create_group(&ap_dev->device.kobj, | ||
786 | &ap_dev_attr_group); | ||
787 | if (rc) | ||
788 | device_unregister(&ap_dev->device); | ||
789 | } | ||
790 | } | ||
791 | |||
792 | static void | ||
793 | ap_config_timeout(unsigned long ptr) | ||
794 | { | ||
795 | queue_work(ap_work_queue, &ap_config_work); | ||
796 | ap_config_timer.expires = jiffies + ap_config_time * HZ; | ||
797 | add_timer(&ap_config_timer); | ||
798 | } | ||
799 | |||
800 | /** | ||
801 | * Set up the timer to run the poll tasklet | ||
802 | */ | ||
803 | static inline void ap_schedule_poll_timer(void) | ||
804 | { | ||
805 | if (timer_pending(&ap_poll_timer)) | ||
806 | return; | ||
807 | mod_timer(&ap_poll_timer, jiffies + AP_POLL_TIME); | ||
808 | } | ||
809 | |||
810 | /** | ||
811 | * Receive pending reply messages from an AP device. | ||
812 | * @ap_dev: pointer to the AP device | ||
813 | * @flags: pointer to control flags, bit 2^0 is set if another poll is | ||
814 | * required, bit 2^1 is set if the poll timer needs to get armed | ||
815 | * Returns 0 if the device is still present, -ENODEV if not. | ||
816 | */ | ||
817 | static inline int ap_poll_read(struct ap_device *ap_dev, unsigned long *flags) | ||
818 | { | ||
819 | struct ap_queue_status status; | ||
820 | struct ap_message *ap_msg; | ||
821 | |||
822 | if (ap_dev->queue_count <= 0) | ||
823 | return 0; | ||
824 | status = __ap_recv(ap_dev->qid, &ap_dev->reply->psmid, | ||
825 | ap_dev->reply->message, ap_dev->reply->length); | ||
826 | switch (status.response_code) { | ||
827 | case AP_RESPONSE_NORMAL: | ||
828 | atomic_dec(&ap_poll_requests); | ||
829 | ap_dev->queue_count--; | ||
830 | list_for_each_entry(ap_msg, &ap_dev->pendingq, list) { | ||
831 | if (ap_msg->psmid != ap_dev->reply->psmid) | ||
832 | continue; | ||
833 | list_del_init(&ap_msg->list); | ||
834 | ap_dev->pendingq_count--; | ||
835 | ap_dev->drv->receive(ap_dev, ap_msg, ap_dev->reply); | ||
836 | break; | ||
837 | } | ||
838 | if (ap_dev->queue_count > 0) | ||
839 | *flags |= 1; | ||
840 | break; | ||
841 | case AP_RESPONSE_NO_PENDING_REPLY: | ||
842 | if (status.queue_empty) { | ||
843 | /* The card shouldn't forget requests but who knows. */ | ||
844 | ap_dev->queue_count = 0; | ||
845 | list_splice_init(&ap_dev->pendingq, &ap_dev->requestq); | ||
846 | ap_dev->requestq_count += ap_dev->pendingq_count; | ||
847 | ap_dev->pendingq_count = 0; | ||
848 | } else | ||
849 | *flags |= 2; | ||
850 | break; | ||
851 | default: | ||
852 | return -ENODEV; | ||
853 | } | ||
854 | return 0; | ||
855 | } | ||
856 | |||
857 | /** | ||
858 | * Send messages from the request queue to an AP device. | ||
859 | * @ap_dev: pointer to the AP device | ||
860 | * @flags: pointer to control flags, bit 2^0 is set if another poll is | ||
861 | * required, bit 2^1 is set if the poll timer needs to get armed | ||
862 | * Returns 0 if the device is still present, -ENODEV if not. | ||
863 | */ | ||
864 | static inline int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags) | ||
865 | { | ||
866 | struct ap_queue_status status; | ||
867 | struct ap_message *ap_msg; | ||
868 | |||
869 | if (ap_dev->requestq_count <= 0 || | ||
870 | ap_dev->queue_count >= ap_dev->queue_depth) | ||
871 | return 0; | ||
872 | /* Start the next request on the queue. */ | ||
873 | ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list); | ||
874 | status = __ap_send(ap_dev->qid, ap_msg->psmid, | ||
875 | ap_msg->message, ap_msg->length); | ||
876 | switch (status.response_code) { | ||
877 | case AP_RESPONSE_NORMAL: | ||
878 | atomic_inc(&ap_poll_requests); | ||
879 | ap_dev->queue_count++; | ||
880 | list_move_tail(&ap_msg->list, &ap_dev->pendingq); | ||
881 | ap_dev->requestq_count--; | ||
882 | ap_dev->pendingq_count++; | ||
883 | if (ap_dev->queue_count < ap_dev->queue_depth && | ||
884 | ap_dev->requestq_count > 0) | ||
885 | *flags |= 1; | ||
886 | *flags |= 2; | ||
887 | break; | ||
888 | case AP_RESPONSE_Q_FULL: | ||
889 | *flags |= 2; | ||
890 | break; | ||
891 | case AP_RESPONSE_MESSAGE_TOO_BIG: | ||
892 | return -EINVAL; | ||
893 | default: | ||
894 | return -ENODEV; | ||
895 | } | ||
896 | return 0; | ||
897 | } | ||
898 | |||
899 | /** | ||
900 | * Poll AP device for pending replies and send new messages. If either | ||
901 | * ap_poll_read or ap_poll_write returns -ENODEV unregister the device. | ||
902 | * @ap_dev: pointer to the bus device | ||
903 | * @flags: pointer to control flags, bit 2^0 is set if another poll is | ||
904 | * required, bit 2^1 is set if the poll timer needs to get armed | ||
905 | * Returns 0. | ||
906 | */ | ||
907 | static inline int ap_poll_queue(struct ap_device *ap_dev, unsigned long *flags) | ||
908 | { | ||
909 | int rc; | ||
910 | |||
911 | rc = ap_poll_read(ap_dev, flags); | ||
912 | if (rc) | ||
913 | return rc; | ||
914 | return ap_poll_write(ap_dev, flags); | ||
915 | } | ||
916 | |||
917 | /** | ||
918 | * Queue a message to a device. | ||
919 | * @ap_dev: pointer to the AP device | ||
920 | * @ap_msg: the message to be queued | ||
921 | */ | ||
922 | static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg) | ||
923 | { | ||
924 | struct ap_queue_status status; | ||
925 | |||
926 | if (list_empty(&ap_dev->requestq) && | ||
927 | ap_dev->queue_count < ap_dev->queue_depth) { | ||
928 | status = __ap_send(ap_dev->qid, ap_msg->psmid, | ||
929 | ap_msg->message, ap_msg->length); | ||
930 | switch (status.response_code) { | ||
931 | case AP_RESPONSE_NORMAL: | ||
932 | list_add_tail(&ap_msg->list, &ap_dev->pendingq); | ||
933 | atomic_inc(&ap_poll_requests); | ||
934 | ap_dev->pendingq_count++; | ||
935 | ap_dev->queue_count++; | ||
936 | ap_dev->total_request_count++; | ||
937 | break; | ||
938 | case AP_RESPONSE_Q_FULL: | ||
939 | list_add_tail(&ap_msg->list, &ap_dev->requestq); | ||
940 | ap_dev->requestq_count++; | ||
941 | ap_dev->total_request_count++; | ||
942 | return -EBUSY; | ||
943 | case AP_RESPONSE_MESSAGE_TOO_BIG: | ||
944 | ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL)); | ||
945 | return -EINVAL; | ||
946 | default: /* Device is gone. */ | ||
947 | ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); | ||
948 | return -ENODEV; | ||
949 | } | ||
950 | } else { | ||
951 | list_add_tail(&ap_msg->list, &ap_dev->requestq); | ||
952 | ap_dev->requestq_count++; | ||
953 | ap_dev->total_request_count++; | ||
954 | return -EBUSY; | ||
955 | } | ||
956 | ap_schedule_poll_timer(); | ||
957 | return 0; | ||
958 | } | ||
959 | |||
960 | void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg) | ||
961 | { | ||
962 | unsigned long flags; | ||
963 | int rc; | ||
964 | |||
965 | spin_lock_bh(&ap_dev->lock); | ||
966 | if (!ap_dev->unregistered) { | ||
967 | /* Make room on the queue by polling for finished requests. */ | ||
968 | rc = ap_poll_queue(ap_dev, &flags); | ||
969 | if (!rc) | ||
970 | rc = __ap_queue_message(ap_dev, ap_msg); | ||
971 | if (!rc) | ||
972 | wake_up(&ap_poll_wait); | ||
973 | } else { | ||
974 | ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); | ||
975 | rc = 0; | ||
976 | } | ||
977 | spin_unlock_bh(&ap_dev->lock); | ||
978 | if (rc == -ENODEV) | ||
979 | device_unregister(&ap_dev->device); | ||
980 | } | ||
981 | EXPORT_SYMBOL(ap_queue_message); | ||
982 | |||
983 | /** | ||
984 | * Cancel a crypto request. This is done by removing the request | ||
985 | * from the devive pendingq or requestq queue. Note that the | ||
986 | * request stays on the AP queue. When it finishes the message | ||
987 | * reply will be discarded because the psmid can't be found. | ||
988 | * @ap_dev: AP device that has the message queued | ||
989 | * @ap_msg: the message that is to be removed | ||
990 | */ | ||
991 | void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg) | ||
992 | { | ||
993 | struct ap_message *tmp; | ||
994 | |||
995 | spin_lock_bh(&ap_dev->lock); | ||
996 | if (!list_empty(&ap_msg->list)) { | ||
997 | list_for_each_entry(tmp, &ap_dev->pendingq, list) | ||
998 | if (tmp->psmid == ap_msg->psmid) { | ||
999 | ap_dev->pendingq_count--; | ||
1000 | goto found; | ||
1001 | } | ||
1002 | ap_dev->requestq_count--; | ||
1003 | found: | ||
1004 | list_del_init(&ap_msg->list); | ||
1005 | } | ||
1006 | spin_unlock_bh(&ap_dev->lock); | ||
1007 | } | ||
1008 | EXPORT_SYMBOL(ap_cancel_message); | ||
1009 | |||
1010 | /** | ||
1011 | * AP receive polling for finished AP requests | ||
1012 | */ | ||
1013 | static void ap_poll_timeout(unsigned long unused) | ||
1014 | { | ||
1015 | tasklet_schedule(&ap_tasklet); | ||
1016 | } | ||
1017 | |||
1018 | /** | ||
1019 | * Poll all AP devices on the bus in a round robin fashion. Continue | ||
1020 | * polling until bit 2^0 of the control flags is not set. If bit 2^1 | ||
1021 | * of the control flags has been set arm the poll timer. | ||
1022 | */ | ||
1023 | static int __ap_poll_all(struct device *dev, void *data) | ||
1024 | { | ||
1025 | struct ap_device *ap_dev = to_ap_dev(dev); | ||
1026 | int rc; | ||
1027 | |||
1028 | spin_lock(&ap_dev->lock); | ||
1029 | if (!ap_dev->unregistered) { | ||
1030 | rc = ap_poll_queue(to_ap_dev(dev), (unsigned long *) data); | ||
1031 | } else | ||
1032 | rc = 0; | ||
1033 | spin_unlock(&ap_dev->lock); | ||
1034 | if (rc) | ||
1035 | device_unregister(&ap_dev->device); | ||
1036 | return 0; | ||
1037 | } | ||
1038 | |||
1039 | static void ap_poll_all(unsigned long dummy) | ||
1040 | { | ||
1041 | unsigned long flags; | ||
1042 | |||
1043 | do { | ||
1044 | flags = 0; | ||
1045 | bus_for_each_dev(&ap_bus_type, NULL, &flags, __ap_poll_all); | ||
1046 | } while (flags & 1); | ||
1047 | if (flags & 2) | ||
1048 | ap_schedule_poll_timer(); | ||
1049 | } | ||
1050 | |||
1051 | /** | ||
1052 | * AP bus poll thread. The purpose of this thread is to poll for | ||
1053 | * finished requests in a loop if there is a "free" cpu - that is | ||
1054 | * a cpu that doesn't have anything better to do. The polling stops | ||
1055 | * as soon as there is another task or if all messages have been | ||
1056 | * delivered. | ||
1057 | */ | ||
1058 | static int ap_poll_thread(void *data) | ||
1059 | { | ||
1060 | DECLARE_WAITQUEUE(wait, current); | ||
1061 | unsigned long flags; | ||
1062 | int requests; | ||
1063 | |||
1064 | set_user_nice(current, -20); | ||
1065 | while (1) { | ||
1066 | if (need_resched()) { | ||
1067 | schedule(); | ||
1068 | continue; | ||
1069 | } | ||
1070 | add_wait_queue(&ap_poll_wait, &wait); | ||
1071 | set_current_state(TASK_INTERRUPTIBLE); | ||
1072 | if (kthread_should_stop()) | ||
1073 | break; | ||
1074 | requests = atomic_read(&ap_poll_requests); | ||
1075 | if (requests <= 0) | ||
1076 | schedule(); | ||
1077 | set_current_state(TASK_RUNNING); | ||
1078 | remove_wait_queue(&ap_poll_wait, &wait); | ||
1079 | |||
1080 | local_bh_disable(); | ||
1081 | flags = 0; | ||
1082 | bus_for_each_dev(&ap_bus_type, NULL, &flags, __ap_poll_all); | ||
1083 | local_bh_enable(); | ||
1084 | } | ||
1085 | set_current_state(TASK_RUNNING); | ||
1086 | remove_wait_queue(&ap_poll_wait, &wait); | ||
1087 | return 0; | ||
1088 | } | ||
1089 | |||
1090 | static int ap_poll_thread_start(void) | ||
1091 | { | ||
1092 | int rc; | ||
1093 | |||
1094 | mutex_lock(&ap_poll_thread_mutex); | ||
1095 | if (!ap_poll_kthread) { | ||
1096 | ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll"); | ||
1097 | rc = IS_ERR(ap_poll_kthread) ? PTR_ERR(ap_poll_kthread) : 0; | ||
1098 | if (rc) | ||
1099 | ap_poll_kthread = NULL; | ||
1100 | } | ||
1101 | else | ||
1102 | rc = 0; | ||
1103 | mutex_unlock(&ap_poll_thread_mutex); | ||
1104 | return rc; | ||
1105 | } | ||
1106 | |||
1107 | static void ap_poll_thread_stop(void) | ||
1108 | { | ||
1109 | mutex_lock(&ap_poll_thread_mutex); | ||
1110 | if (ap_poll_kthread) { | ||
1111 | kthread_stop(ap_poll_kthread); | ||
1112 | ap_poll_kthread = NULL; | ||
1113 | } | ||
1114 | mutex_unlock(&ap_poll_thread_mutex); | ||
1115 | } | ||
1116 | |||
1117 | /** | ||
1118 | * The module initialization code. | ||
1119 | */ | ||
1120 | int __init ap_module_init(void) | ||
1121 | { | ||
1122 | int rc, i; | ||
1123 | |||
1124 | if (ap_domain_index < -1 || ap_domain_index >= AP_DOMAINS) { | ||
1125 | printk(KERN_WARNING "Invalid param: domain = %d. " | ||
1126 | " Not loading.\n", ap_domain_index); | ||
1127 | return -EINVAL; | ||
1128 | } | ||
1129 | if (ap_instructions_available() != 0) { | ||
1130 | printk(KERN_WARNING "AP instructions not installed.\n"); | ||
1131 | return -ENODEV; | ||
1132 | } | ||
1133 | |||
1134 | /* Create /sys/bus/ap. */ | ||
1135 | rc = bus_register(&ap_bus_type); | ||
1136 | if (rc) | ||
1137 | goto out; | ||
1138 | for (i = 0; ap_bus_attrs[i]; i++) { | ||
1139 | rc = bus_create_file(&ap_bus_type, ap_bus_attrs[i]); | ||
1140 | if (rc) | ||
1141 | goto out_bus; | ||
1142 | } | ||
1143 | |||
1144 | /* Create /sys/devices/ap. */ | ||
1145 | ap_root_device = s390_root_dev_register("ap"); | ||
1146 | rc = IS_ERR(ap_root_device) ? PTR_ERR(ap_root_device) : 0; | ||
1147 | if (rc) | ||
1148 | goto out_bus; | ||
1149 | |||
1150 | ap_work_queue = create_singlethread_workqueue("kapwork"); | ||
1151 | if (!ap_work_queue) { | ||
1152 | rc = -ENOMEM; | ||
1153 | goto out_root; | ||
1154 | } | ||
1155 | |||
1156 | if (ap_select_domain() == 0) | ||
1157 | ap_scan_bus(NULL); | ||
1158 | |||
1159 | /* Setup the ap bus rescan timer. */ | ||
1160 | init_timer(&ap_config_timer); | ||
1161 | ap_config_timer.function = ap_config_timeout; | ||
1162 | ap_config_timer.data = 0; | ||
1163 | ap_config_timer.expires = jiffies + ap_config_time * HZ; | ||
1164 | add_timer(&ap_config_timer); | ||
1165 | |||
1166 | /* Start the low priority AP bus poll thread. */ | ||
1167 | if (ap_thread_flag) { | ||
1168 | rc = ap_poll_thread_start(); | ||
1169 | if (rc) | ||
1170 | goto out_work; | ||
1171 | } | ||
1172 | |||
1173 | return 0; | ||
1174 | |||
1175 | out_work: | ||
1176 | del_timer_sync(&ap_config_timer); | ||
1177 | del_timer_sync(&ap_poll_timer); | ||
1178 | destroy_workqueue(ap_work_queue); | ||
1179 | out_root: | ||
1180 | s390_root_dev_unregister(ap_root_device); | ||
1181 | out_bus: | ||
1182 | while (i--) | ||
1183 | bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); | ||
1184 | bus_unregister(&ap_bus_type); | ||
1185 | out: | ||
1186 | return rc; | ||
1187 | } | ||
1188 | |||
1189 | static int __ap_match_all(struct device *dev, void *data) | ||
1190 | { | ||
1191 | return 1; | ||
1192 | } | ||
1193 | |||
1194 | /** | ||
1195 | * The module termination code | ||
1196 | */ | ||
1197 | void ap_module_exit(void) | ||
1198 | { | ||
1199 | int i; | ||
1200 | struct device *dev; | ||
1201 | |||
1202 | ap_poll_thread_stop(); | ||
1203 | del_timer_sync(&ap_config_timer); | ||
1204 | del_timer_sync(&ap_poll_timer); | ||
1205 | destroy_workqueue(ap_work_queue); | ||
1206 | s390_root_dev_unregister(ap_root_device); | ||
1207 | while ((dev = bus_find_device(&ap_bus_type, NULL, NULL, | ||
1208 | __ap_match_all))) | ||
1209 | { | ||
1210 | device_unregister(dev); | ||
1211 | put_device(dev); | ||
1212 | } | ||
1213 | for (i = 0; ap_bus_attrs[i]; i++) | ||
1214 | bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); | ||
1215 | bus_unregister(&ap_bus_type); | ||
1216 | } | ||
1217 | |||
1218 | #ifndef CONFIG_ZCRYPT_MONOLITHIC | ||
1219 | module_init(ap_module_init); | ||
1220 | module_exit(ap_module_exit); | ||
1221 | #endif | ||