Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=cf352ce0b9104dc6a1c11df52db9f910f1f6cd2e
Commit:     cf352ce0b9104dc6a1c11df52db9f910f1f6cd2e
Parent:     fb1c171992041e1d4bfb8c010548fa97f2106827
Author:     Ralph Wuerthner <[EMAIL PROTECTED]>
AuthorDate: Mon Mar 19 13:19:14 2007 +0100
Committer:  Martin Schwidefsky <[EMAIL PROTECTED]>
CommitDate: Mon Mar 19 13:19:14 2007 +0100

    [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 <[EMAIL PROTECTED]>
    Signed-off-by: Martin Schwidefsky <[EMAIL PROTECTED]>
---
 drivers/s390/crypto/ap_bus.c |   30 +++++++++++++++++++++++-------
 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 c7d1355..181b517 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);
 MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 1 (on).");
 
 static struct device *ap_root_device = NULL;
+static DEFINE_SPINLOCK(ap_device_lock);
+static LIST_HEAD(ap_device_list);
 
 /**
  * Workqueue & timer for bus rescan.
@@ -457,6 +459,9 @@ static int ap_device_probe(struct device *dev)
        int rc;
 
        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;
        return rc;
 }
@@ -497,6 +502,9 @@ static int ap_device_remove(struct device *dev)
        ap_flush_queue(ap_dev);
        if (ap_drv->remove)
                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;
 }
 
@@ -772,6 +780,7 @@ static void ap_scan_bus(struct work_struct *unused)
                spin_lock_init(&ap_dev->lock);
                INIT_LIST_HEAD(&ap_dev->pendingq);
                INIT_LIST_HEAD(&ap_dev->requestq);
+               INIT_LIST_HEAD(&ap_dev->list);
                if (device_type == 0)
                        ap_probe_device_type(ap_dev);
                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
  * 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;
 
        spin_lock(&ap_dev->lock);
        if (!ap_dev->unregistered) {
-               rc = ap_poll_queue(to_ap_dev(dev), (unsigned long *) data);
+               rc = ap_poll_queue(ap_dev, flags);
                if (rc)
                        ap_dev->unregistered = 1;
        } else
@@ -1054,10 +1062,15 @@ static int __ap_poll_all(struct device *dev, void *data)
 static void ap_poll_all(unsigned long dummy)
 {
        unsigned long flags;
+       struct ap_device *ap_dev;
 
        do {
                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);
        if (flags & 2)
                ap_schedule_poll_timer();
@@ -1075,6 +1088,7 @@ static int ap_poll_thread(void *data)
        DECLARE_WAITQUEUE(wait, current);
        unsigned long flags;
        int requests;
+       struct ap_device *ap_dev;
 
        set_user_nice(current, 19);
        while (1) {
@@ -1092,10 +1106,12 @@ static int ap_poll_thread(void *data)
                set_current_state(TASK_RUNNING);
                remove_wait_queue(&ap_poll_wait, &wait);
 
-               local_bh_disable();
                flags = 0;
-               bus_for_each_dev(&ap_bus_type, NULL, &flags, __ap_poll_all);
-               local_bh_enable();
+               spin_lock_bh(&ap_device_lock);
+               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);
        remove_wait_queue(&ap_poll_wait, &wait);
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index 83b69c0..008559e 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -106,6 +106,7 @@ struct ap_device {
        struct device device;
        struct ap_driver *drv;          /* Pointer to AP device driver. */
        spinlock_t lock;                /* Per device lock. */
+       struct list_head list;          /* private list of all AP devices. */
 
        ap_qid_t qid;                   /* AP queue id. */
        int queue_depth;                /* AP queue depth.*/
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to