IB/qib: Reduce sdma_lock contention
[linux.git] / drivers / infiniband / hw / qib / qib_init.c
index dc14e100a7f1a560a86ea82ab38929eadccd3cc4..306e65e99e9999087423f1caba84a56a1c4ab654 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 2006, 2007, 2008, 2009, 2010 QLogic Corporation.
- * All rights reserved.
+ * Copyright (c) 2012 Intel Corporation.  All rights reserved.
+ * Copyright (c) 2006 - 2012 QLogic Corporation. All rights reserved.
  * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
  *
  * This software is available to you under a choice of one of two
@@ -210,6 +210,8 @@ void qib_init_pportdata(struct qib_pportdata *ppd, struct qib_devdata *dd,
        init_timer(&ppd->symerr_clear_timer);
        ppd->symerr_clear_timer.function = qib_clear_symerror_on_linkup;
        ppd->symerr_clear_timer.data = (unsigned long)ppd;
+
+       ppd->qib_wq = NULL;
 }
 
 static int init_pioavailregs(struct qib_devdata *dd)
@@ -482,6 +484,42 @@ static void init_piobuf_state(struct qib_devdata *dd)
        dd->f_initvl15_bufs(dd);
 }
 
+/**
+ * qib_create_workqueues - create per port workqueues
+ * @dd: the qlogic_ib device
+ */
+static int qib_create_workqueues(struct qib_devdata *dd)
+{
+       int pidx;
+       struct qib_pportdata *ppd;
+
+       for (pidx = 0; pidx < dd->num_pports; ++pidx) {
+               ppd = dd->pport + pidx;
+               if (!ppd->qib_wq) {
+                       char wq_name[8]; /* 3 + 2 + 1 + 1 + 1 */
+                       snprintf(wq_name, sizeof(wq_name), "qib%d_%d",
+                               dd->unit, pidx);
+                       ppd->qib_wq =
+                               create_singlethread_workqueue(wq_name);
+                       if (!ppd->qib_wq)
+                               goto wq_error;
+               }
+       }
+       return 0;
+wq_error:
+       pr_err(
+        QIB_DRV_NAME ": create_singlethread_workqueue failed for port %d\n",
+        pidx + 1);
+       for (pidx = 0; pidx < dd->num_pports; ++pidx) {
+               ppd = dd->pport + pidx;
+               if (ppd->qib_wq) {
+                       destroy_workqueue(ppd->qib_wq);
+                       ppd->qib_wq = NULL;
+               }
+       }
+       return -ENOMEM;
+}
+
 /**
  * qib_init - do the actual initialization sequence on the chip
  * @dd: the qlogic_ib device
@@ -764,6 +802,11 @@ static void qib_shutdown_device(struct qib_devdata *dd)
                 * We can't count on interrupts since we are stopping.
                 */
                dd->f_quiet_serdes(ppd);
+
+               if (ppd->qib_wq) {
+                       destroy_workqueue(ppd->qib_wq);
+                       ppd->qib_wq = NULL;
+               }
        }
 
        qib_update_eeprom_log(dd);
@@ -1249,6 +1292,10 @@ static int __devinit qib_init_one(struct pci_dev *pdev,
        if (ret)
                goto bail; /* error already printed */
 
+       ret = qib_create_workqueues(dd);
+       if (ret)
+               goto bail;
+
        /* do the generic initialization */
        initfail = qib_init(dd, 0);