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 | |
| 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>
| -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.*/ |
