]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/infiniband/hw/mthca/mthca_catas.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/penberg...
[linux-2.6-omap-h63xx.git] / drivers / infiniband / hw / mthca / mthca_catas.c
index 25ebab64bc4257a29548d8e398249f38d91e67a8..cc440f90000becfd62928c8aee85e07d4ef30653 100644 (file)
  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
- *
- * $Id$
  */
 
 #include <linux/jiffies.h>
 #include <linux/timer.h>
+#include <linux/workqueue.h>
 
 #include "mthca_dev.h"
 
@@ -48,9 +47,41 @@ enum {
 
 static DEFINE_SPINLOCK(catas_lock);
 
+static LIST_HEAD(catas_list);
+static struct workqueue_struct *catas_wq;
+static struct work_struct catas_work;
+
+static int catas_reset_disable;
+module_param_named(catas_reset_disable, catas_reset_disable, int, 0644);
+MODULE_PARM_DESC(catas_reset_disable, "disable reset on catastrophic event if nonzero");
+
+static void catas_reset(struct work_struct *work)
+{
+       struct mthca_dev *dev, *tmpdev;
+       LIST_HEAD(tlist);
+       int ret;
+
+       mutex_lock(&mthca_device_mutex);
+
+       spin_lock_irq(&catas_lock);
+       list_splice_init(&catas_list, &tlist);
+       spin_unlock_irq(&catas_lock);
+
+       list_for_each_entry_safe(dev, tmpdev, &tlist, catas_err.list) {
+               ret = __mthca_restart_one(dev->pdev);
+               if (ret)
+                       mthca_err(dev, "Reset failed (%d)\n", ret);
+               else
+                       mthca_dbg(dev, "Reset succeeded\n");
+       }
+
+       mutex_unlock(&mthca_device_mutex);
+}
+
 static void handle_catas(struct mthca_dev *dev)
 {
        struct ib_event event;
+       unsigned long flags;
        const char *type;
        int i;
 
@@ -82,12 +113,19 @@ static void handle_catas(struct mthca_dev *dev)
        for (i = 0; i < dev->catas_err.size; ++i)
                mthca_err(dev, "  buf[%02x]: %08x\n",
                          i, swab32(readl(dev->catas_err.map + i)));
+
+       if (catas_reset_disable)
+               return;
+
+       spin_lock_irqsave(&catas_lock, flags);
+       list_add(&dev->catas_err.list, &catas_list);
+       queue_work(catas_wq, &catas_work);
+       spin_unlock_irqrestore(&catas_lock, flags);
 }
 
 static void poll_catas(unsigned long dev_ptr)
 {
        struct mthca_dev *dev = (struct mthca_dev *) dev_ptr;
-       unsigned long flags;
        int i;
 
        for (i = 0; i < dev->catas_err.size; ++i)
@@ -96,13 +134,8 @@ static void poll_catas(unsigned long dev_ptr)
                        return;
                }
 
-       spin_lock_irqsave(&catas_lock, flags);
-       if (dev->catas_err.stop)
-               mod_timer(&dev->catas_err.timer,
-                         jiffies + MTHCA_CATAS_POLL_INTERVAL);
-       spin_unlock_irqrestore(&catas_lock, flags);
-
-       return;
+       mod_timer(&dev->catas_err.timer,
+                 round_jiffies(jiffies + MTHCA_CATAS_POLL_INTERVAL));
 }
 
 void mthca_start_catas_poll(struct mthca_dev *dev)
@@ -110,7 +143,6 @@ void mthca_start_catas_poll(struct mthca_dev *dev)
        unsigned long addr;
 
        init_timer(&dev->catas_err.timer);
-       dev->catas_err.stop = 0;
        dev->catas_err.map  = NULL;
 
        addr = pci_resource_start(dev->pdev, 0) +
@@ -135,15 +167,12 @@ void mthca_start_catas_poll(struct mthca_dev *dev)
        dev->catas_err.timer.data     = (unsigned long) dev;
        dev->catas_err.timer.function = poll_catas;
        dev->catas_err.timer.expires  = jiffies + MTHCA_CATAS_POLL_INTERVAL;
+       INIT_LIST_HEAD(&dev->catas_err.list);
        add_timer(&dev->catas_err.timer);
 }
 
 void mthca_stop_catas_poll(struct mthca_dev *dev)
 {
-       spin_lock_irq(&catas_lock);
-       dev->catas_err.stop = 1;
-       spin_unlock_irq(&catas_lock);
-
        del_timer_sync(&dev->catas_err.timer);
 
        if (dev->catas_err.map) {
@@ -153,4 +182,24 @@ void mthca_stop_catas_poll(struct mthca_dev *dev)
                                    dev->catas_err.addr),
                                   dev->catas_err.size * 4);
        }
+
+       spin_lock_irq(&catas_lock);
+       list_del(&dev->catas_err.list);
+       spin_unlock_irq(&catas_lock);
+}
+
+int __init mthca_catas_init(void)
+{
+       INIT_WORK(&catas_work, catas_reset);
+
+       catas_wq = create_singlethread_workqueue("mthca_catas");
+       if (!catas_wq)
+               return -ENOMEM;
+
+       return 0;
+}
+
+void mthca_catas_cleanup(void)
+{
+       destroy_workqueue(catas_wq);
 }