forked from luck/tmp_suning_uos_patched
[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>
This commit is contained in:
parent
fb1c171992
commit
cf352ce0b9
@ -65,6 +65,8 @@ module_param_named(poll_thread, ap_thread_flag, int, 0000);
|
|||||||
MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 1 (on).");
|
MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 1 (on).");
|
||||||
|
|
||||||
static struct device *ap_root_device = NULL;
|
static struct device *ap_root_device = NULL;
|
||||||
|
static DEFINE_SPINLOCK(ap_device_lock);
|
||||||
|
static LIST_HEAD(ap_device_list);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Workqueue & timer for bus rescan.
|
* Workqueue & timer for bus rescan.
|
||||||
@ -457,6 +459,9 @@ static int ap_device_probe(struct device *dev)
|
|||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
ap_dev->drv = ap_drv;
|
ap_dev->drv = ap_drv;
|
||||||
|
spin_lock_bh(&ap_device_lock);
|
||||||
|
list_add(&ap_dev->list, &ap_device_list);
|
||||||
|
spin_unlock_bh(&ap_device_lock);
|
||||||
rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV;
|
rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@ -497,6 +502,9 @@ static int ap_device_remove(struct device *dev)
|
|||||||
ap_flush_queue(ap_dev);
|
ap_flush_queue(ap_dev);
|
||||||
if (ap_drv->remove)
|
if (ap_drv->remove)
|
||||||
ap_drv->remove(ap_dev);
|
ap_drv->remove(ap_dev);
|
||||||
|
spin_lock_bh(&ap_device_lock);
|
||||||
|
list_del_init(&ap_dev->list);
|
||||||
|
spin_unlock_bh(&ap_device_lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,6 +780,7 @@ static void ap_scan_bus(struct work_struct *unused)
|
|||||||
spin_lock_init(&ap_dev->lock);
|
spin_lock_init(&ap_dev->lock);
|
||||||
INIT_LIST_HEAD(&ap_dev->pendingq);
|
INIT_LIST_HEAD(&ap_dev->pendingq);
|
||||||
INIT_LIST_HEAD(&ap_dev->requestq);
|
INIT_LIST_HEAD(&ap_dev->requestq);
|
||||||
|
INIT_LIST_HEAD(&ap_dev->list);
|
||||||
if (device_type == 0)
|
if (device_type == 0)
|
||||||
ap_probe_device_type(ap_dev);
|
ap_probe_device_type(ap_dev);
|
||||||
else
|
else
|
||||||
@ -1033,14 +1042,13 @@ static void ap_poll_timeout(unsigned long unused)
|
|||||||
* polling until bit 2^0 of the control flags is not set. If bit 2^1
|
* polling until bit 2^0 of the control flags is not set. If bit 2^1
|
||||||
* of the control flags has been set arm the poll timer.
|
* of the control flags has been set arm the poll timer.
|
||||||
*/
|
*/
|
||||||
static int __ap_poll_all(struct device *dev, void *data)
|
static int __ap_poll_all(struct ap_device *ap_dev, unsigned long *flags)
|
||||||
{
|
{
|
||||||
struct ap_device *ap_dev = to_ap_dev(dev);
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
spin_lock(&ap_dev->lock);
|
spin_lock(&ap_dev->lock);
|
||||||
if (!ap_dev->unregistered) {
|
if (!ap_dev->unregistered) {
|
||||||
rc = ap_poll_queue(to_ap_dev(dev), (unsigned long *) data);
|
rc = ap_poll_queue(ap_dev, flags);
|
||||||
if (rc)
|
if (rc)
|
||||||
ap_dev->unregistered = 1;
|
ap_dev->unregistered = 1;
|
||||||
} else
|
} else
|
||||||
@ -1054,10 +1062,15 @@ static int __ap_poll_all(struct device *dev, void *data)
|
|||||||
static void ap_poll_all(unsigned long dummy)
|
static void ap_poll_all(unsigned long dummy)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
struct ap_device *ap_dev;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
flags = 0;
|
flags = 0;
|
||||||
bus_for_each_dev(&ap_bus_type, NULL, &flags, __ap_poll_all);
|
spin_lock(&ap_device_lock);
|
||||||
|
list_for_each_entry(ap_dev, &ap_device_list, list) {
|
||||||
|
__ap_poll_all(ap_dev, &flags);
|
||||||
|
}
|
||||||
|
spin_unlock(&ap_device_lock);
|
||||||
} while (flags & 1);
|
} while (flags & 1);
|
||||||
if (flags & 2)
|
if (flags & 2)
|
||||||
ap_schedule_poll_timer();
|
ap_schedule_poll_timer();
|
||||||
@ -1075,6 +1088,7 @@ static int ap_poll_thread(void *data)
|
|||||||
DECLARE_WAITQUEUE(wait, current);
|
DECLARE_WAITQUEUE(wait, current);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int requests;
|
int requests;
|
||||||
|
struct ap_device *ap_dev;
|
||||||
|
|
||||||
set_user_nice(current, 19);
|
set_user_nice(current, 19);
|
||||||
while (1) {
|
while (1) {
|
||||||
@ -1092,10 +1106,12 @@ static int ap_poll_thread(void *data)
|
|||||||
set_current_state(TASK_RUNNING);
|
set_current_state(TASK_RUNNING);
|
||||||
remove_wait_queue(&ap_poll_wait, &wait);
|
remove_wait_queue(&ap_poll_wait, &wait);
|
||||||
|
|
||||||
local_bh_disable();
|
|
||||||
flags = 0;
|
flags = 0;
|
||||||
bus_for_each_dev(&ap_bus_type, NULL, &flags, __ap_poll_all);
|
spin_lock_bh(&ap_device_lock);
|
||||||
local_bh_enable();
|
list_for_each_entry(ap_dev, &ap_device_list, list) {
|
||||||
|
__ap_poll_all(ap_dev, &flags);
|
||||||
|
}
|
||||||
|
spin_unlock_bh(&ap_device_lock);
|
||||||
}
|
}
|
||||||
set_current_state(TASK_RUNNING);
|
set_current_state(TASK_RUNNING);
|
||||||
remove_wait_queue(&ap_poll_wait, &wait);
|
remove_wait_queue(&ap_poll_wait, &wait);
|
||||||
|
@ -106,6 +106,7 @@ struct ap_device {
|
|||||||
struct device device;
|
struct device device;
|
||||||
struct ap_driver *drv; /* Pointer to AP device driver. */
|
struct ap_driver *drv; /* Pointer to AP device driver. */
|
||||||
spinlock_t lock; /* Per device lock. */
|
spinlock_t lock; /* Per device lock. */
|
||||||
|
struct list_head list; /* private list of all AP devices. */
|
||||||
|
|
||||||
ap_qid_t qid; /* AP queue id. */
|
ap_qid_t qid; /* AP queue id. */
|
||||||
int queue_depth; /* AP queue depth.*/
|
int queue_depth; /* AP queue depth.*/
|
||||||
|
Loading…
Reference in New Issue
Block a user