diff options
author | Ralph Wuerthner <rwuerthn@de.ibm.com> | 2007-03-19 08:19:14 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2007-03-19 08:19:14 -0400 |
commit | cf352ce0b9104dc6a1c11df52db9f910f1f6cd2e (patch) | |
tree | 166082c005e653747eea14d2a761119d8425decb /drivers/s390/crypto | |
parent | fb1c171992041e1d4bfb8c010548fa97f2106827 (diff) |
[S390] zcrypt: fix possible dead lock in AP bus module
AP bus module uses bus_for_each_dev() in software interrupt context to
poll for completed requests which might cause dead locks. Solution: use
private AP device list for polling in software interrupt context.
Signed-off-by: Ralph Wuerthner <rwuerthn@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/crypto')
-rw-r--r-- | drivers/s390/crypto/ap_bus.c | 30 | ||||
-rw-r--r-- | drivers/s390/crypto/ap_bus.h | 1 |
2 files changed, 24 insertions, 7 deletions
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index c7d1355237b6..181b51772b1b 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c | |||
@@ -65,6 +65,8 @@ module_param_named(poll_thread, ap_thread_flag, int, 0000); | |||
65 | MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 1 (on)."); | 65 | MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 1 (on)."); |
66 | 66 | ||
67 | static struct device *ap_root_device = NULL; | 67 | static struct device *ap_root_device = NULL; |
68 | static DEFINE_SPINLOCK(ap_device_lock); | ||
69 | static LIST_HEAD(ap_device_list); | ||
68 | 70 | ||
69 | /** | 71 | /** |
70 | * Workqueue & timer for bus rescan. | 72 | * Workqueue & timer for bus rescan. |
@@ -457,6 +459,9 @@ static int ap_device_probe(struct device *dev) | |||
457 | int rc; | 459 | int rc; |
458 | 460 | ||
459 | ap_dev->drv = ap_drv; | 461 | ap_dev->drv = ap_drv; |
462 | spin_lock_bh(&ap_device_lock); | ||
463 | list_add(&ap_dev->list, &ap_device_list); | ||
464 | spin_unlock_bh(&ap_device_lock); | ||
460 | rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV; | 465 | rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV; |
461 | return rc; | 466 | return rc; |
462 | } | 467 | } |
@@ -497,6 +502,9 @@ static int ap_device_remove(struct device *dev) | |||
497 | ap_flush_queue(ap_dev); | 502 | ap_flush_queue(ap_dev); |
498 | if (ap_drv->remove) | 503 | if (ap_drv->remove) |
499 | ap_drv->remove(ap_dev); | 504 | ap_drv->remove(ap_dev); |
505 | spin_lock_bh(&ap_device_lock); | ||
506 | list_del_init(&ap_dev->list); | ||
507 | spin_unlock_bh(&ap_device_lock); | ||
500 | return 0; | 508 | return 0; |
501 | } | 509 | } |
502 | 510 | ||
@@ -772,6 +780,7 @@ static void ap_scan_bus(struct work_struct *unused) | |||
772 | spin_lock_init(&ap_dev->lock); | 780 | spin_lock_init(&ap_dev->lock); |
773 | INIT_LIST_HEAD(&ap_dev->pendingq); | 781 | INIT_LIST_HEAD(&ap_dev->pendingq); |
774 | INIT_LIST_HEAD(&ap_dev->requestq); | 782 | INIT_LIST_HEAD(&ap_dev->requestq); |
783 | INIT_LIST_HEAD(&ap_dev->list); | ||
775 | if (device_type == 0) | 784 | if (device_type == 0) |
776 | ap_probe_device_type(ap_dev); | 785 | ap_probe_device_type(ap_dev); |
777 | else | 786 | else |
@@ -1033,14 +1042,13 @@ static void ap_poll_timeout(unsigned long unused) | |||
1033 | * polling until bit 2^0 of the control flags is not set. If bit 2^1 | 1042 | * polling until bit 2^0 of the control flags is not set. If bit 2^1 |
1034 | * of the control flags has been set arm the poll timer. | 1043 | * of the control flags has been set arm the poll timer. |
1035 | */ | 1044 | */ |
1036 | static int __ap_poll_all(struct device *dev, void *data) | 1045 | static int __ap_poll_all(struct ap_device *ap_dev, unsigned long *flags) |
1037 | { | 1046 | { |
1038 | struct ap_device *ap_dev = to_ap_dev(dev); | ||
1039 | int rc; | 1047 | int rc; |
1040 | 1048 | ||
1041 | spin_lock(&ap_dev->lock); | 1049 | spin_lock(&ap_dev->lock); |
1042 | if (!ap_dev->unregistered) { | 1050 | if (!ap_dev->unregistered) { |
1043 | rc = ap_poll_queue(to_ap_dev(dev), (unsigned long *) data); | 1051 | rc = ap_poll_queue(ap_dev, flags); |
1044 | if (rc) | 1052 | if (rc) |
1045 | ap_dev->unregistered = 1; | 1053 | ap_dev->unregistered = 1; |
1046 | } else | 1054 | } else |
@@ -1054,10 +1062,15 @@ static int __ap_poll_all(struct device *dev, void *data) | |||
1054 | static void ap_poll_all(unsigned long dummy) | 1062 | static void ap_poll_all(unsigned long dummy) |
1055 | { | 1063 | { |
1056 | unsigned long flags; | 1064 | unsigned long flags; |
1065 | struct ap_device *ap_dev; | ||
1057 | 1066 | ||
1058 | do { | 1067 | do { |
1059 | flags = 0; | 1068 | flags = 0; |
1060 | bus_for_each_dev(&ap_bus_type, NULL, &flags, __ap_poll_all); | 1069 | spin_lock(&ap_device_lock); |
1070 | list_for_each_entry(ap_dev, &ap_device_list, list) { | ||
1071 | __ap_poll_all(ap_dev, &flags); | ||
1072 | } | ||
1073 | spin_unlock(&ap_device_lock); | ||
1061 | } while (flags & 1); | 1074 | } while (flags & 1); |
1062 | if (flags & 2) | 1075 | if (flags & 2) |
1063 | ap_schedule_poll_timer(); | 1076 | ap_schedule_poll_timer(); |
@@ -1075,6 +1088,7 @@ static int ap_poll_thread(void *data) | |||
1075 | DECLARE_WAITQUEUE(wait, current); | 1088 | DECLARE_WAITQUEUE(wait, current); |
1076 | unsigned long flags; | 1089 | unsigned long flags; |
1077 | int requests; | 1090 | int requests; |
1091 | struct ap_device *ap_dev; | ||
1078 | 1092 | ||
1079 | set_user_nice(current, 19); | 1093 | set_user_nice(current, 19); |
1080 | while (1) { | 1094 | while (1) { |
@@ -1092,10 +1106,12 @@ static int ap_poll_thread(void *data) | |||
1092 | set_current_state(TASK_RUNNING); | 1106 | set_current_state(TASK_RUNNING); |
1093 | remove_wait_queue(&ap_poll_wait, &wait); | 1107 | remove_wait_queue(&ap_poll_wait, &wait); |
1094 | 1108 | ||
1095 | local_bh_disable(); | ||
1096 | flags = 0; | 1109 | flags = 0; |
1097 | bus_for_each_dev(&ap_bus_type, NULL, &flags, __ap_poll_all); | 1110 | spin_lock_bh(&ap_device_lock); |
1098 | local_bh_enable(); | 1111 | list_for_each_entry(ap_dev, &ap_device_list, list) { |
1112 | __ap_poll_all(ap_dev, &flags); | ||
1113 | } | ||
1114 | spin_unlock_bh(&ap_device_lock); | ||
1099 | } | 1115 | } |
1100 | set_current_state(TASK_RUNNING); | 1116 | set_current_state(TASK_RUNNING); |
1101 | remove_wait_queue(&ap_poll_wait, &wait); | 1117 | remove_wait_queue(&ap_poll_wait, &wait); |
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 83b69c01cd6e..008559ea742b 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h | |||
@@ -106,6 +106,7 @@ struct ap_device { | |||
106 | struct device device; | 106 | struct device device; |
107 | struct ap_driver *drv; /* Pointer to AP device driver. */ | 107 | struct ap_driver *drv; /* Pointer to AP device driver. */ |
108 | spinlock_t lock; /* Per device lock. */ | 108 | spinlock_t lock; /* Per device lock. */ |
109 | struct list_head list; /* private list of all AP devices. */ | ||
109 | 110 | ||
110 | ap_qid_t qid; /* AP queue id. */ | 111 | ap_qid_t qid; /* AP queue id. */ |
111 | int queue_depth; /* AP queue depth.*/ | 112 | int queue_depth; /* AP queue depth.*/ |