Commit 85caafe3 authored by Mike Marciniszyn's avatar Mike Marciniszyn Committed by Roland Dreier

IB/qib: Optimize CQ callbacks

The current workqueue implemention has the following performance
deficiencies on QDR HCAs:

- The CQ call backs tend to run on the CPUs processing the
  receive queues
- The single thread queue isn't optimal for multiple HCAs

This patch adds a dedicated per HCA bound thread to process CQ callbacks.
Reviewed-by: default avatarRamkrishna Vepa <ramkrishna.vepa@intel.com>
Signed-off-by: default avatarMike Marciniszyn <mike.marciniszyn@intel.com>
Signed-off-by: default avatarRoland Dreier <roland@purestorage.com>
parent c804f072
#ifndef _QIB_KERNEL_H #ifndef _QIB_KERNEL_H
#define _QIB_KERNEL_H #define _QIB_KERNEL_H
/* /*
* Copyright (c) 2012 Intel Corporation. All rights reserved. * Copyright (c) 2012, 2013 Intel Corporation. All rights reserved.
* Copyright (c) 2006 - 2012 QLogic Corporation. All rights reserved. * Copyright (c) 2006 - 2012 QLogic Corporation. All rights reserved.
* Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
* *
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/kthread.h>
#include "qib_common.h" #include "qib_common.h"
#include "qib_verbs.h" #include "qib_verbs.h"
...@@ -1090,6 +1091,8 @@ struct qib_devdata { ...@@ -1090,6 +1091,8 @@ struct qib_devdata {
u16 psxmitwait_check_rate; u16 psxmitwait_check_rate;
/* high volume overflow errors defered to tasklet */ /* high volume overflow errors defered to tasklet */
struct tasklet_struct error_tasklet; struct tasklet_struct error_tasklet;
/* per device cq worker */
struct kthread_worker *worker;
int assigned_node_id; /* NUMA node closest to HCA */ int assigned_node_id; /* NUMA node closest to HCA */
}; };
......
/* /*
* Copyright (c) 2013 Intel Corporation. All rights reserved.
* Copyright (c) 2006, 2007, 2008, 2010 QLogic Corporation. All rights reserved. * Copyright (c) 2006, 2007, 2008, 2010 QLogic Corporation. All rights reserved.
* Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved. * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved.
* *
...@@ -34,8 +35,10 @@ ...@@ -34,8 +35,10 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/kthread.h>
#include "qib_verbs.h" #include "qib_verbs.h"
#include "qib.h"
/** /**
* qib_cq_enter - add a new entry to the completion queue * qib_cq_enter - add a new entry to the completion queue
...@@ -102,13 +105,18 @@ void qib_cq_enter(struct qib_cq *cq, struct ib_wc *entry, int solicited) ...@@ -102,13 +105,18 @@ void qib_cq_enter(struct qib_cq *cq, struct ib_wc *entry, int solicited)
if (cq->notify == IB_CQ_NEXT_COMP || if (cq->notify == IB_CQ_NEXT_COMP ||
(cq->notify == IB_CQ_SOLICITED && (cq->notify == IB_CQ_SOLICITED &&
(solicited || entry->status != IB_WC_SUCCESS))) { (solicited || entry->status != IB_WC_SUCCESS))) {
cq->notify = IB_CQ_NONE; struct kthread_worker *worker;
cq->triggered++;
/* /*
* This will cause send_complete() to be called in * This will cause send_complete() to be called in
* another thread. * another thread.
*/ */
queue_work(qib_cq_wq, &cq->comptask); smp_rmb();
worker = cq->dd->worker;
if (likely(worker)) {
cq->notify = IB_CQ_NONE;
cq->triggered++;
queue_kthread_work(worker, &cq->comptask);
}
} }
spin_unlock_irqrestore(&cq->lock, flags); spin_unlock_irqrestore(&cq->lock, flags);
...@@ -163,7 +171,7 @@ int qib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) ...@@ -163,7 +171,7 @@ int qib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry)
return npolled; return npolled;
} }
static void send_complete(struct work_struct *work) static void send_complete(struct kthread_work *work)
{ {
struct qib_cq *cq = container_of(work, struct qib_cq, comptask); struct qib_cq *cq = container_of(work, struct qib_cq, comptask);
...@@ -287,11 +295,12 @@ struct ib_cq *qib_create_cq(struct ib_device *ibdev, int entries, ...@@ -287,11 +295,12 @@ struct ib_cq *qib_create_cq(struct ib_device *ibdev, int entries,
* The number of entries should be >= the number requested or return * The number of entries should be >= the number requested or return
* an error. * an error.
*/ */
cq->dd = dd_from_dev(dev);
cq->ibcq.cqe = entries; cq->ibcq.cqe = entries;
cq->notify = IB_CQ_NONE; cq->notify = IB_CQ_NONE;
cq->triggered = 0; cq->triggered = 0;
spin_lock_init(&cq->lock); spin_lock_init(&cq->lock);
INIT_WORK(&cq->comptask, send_complete); init_kthread_work(&cq->comptask, send_complete);
wc->head = 0; wc->head = 0;
wc->tail = 0; wc->tail = 0;
cq->queue = wc; cq->queue = wc;
...@@ -323,7 +332,7 @@ int qib_destroy_cq(struct ib_cq *ibcq) ...@@ -323,7 +332,7 @@ int qib_destroy_cq(struct ib_cq *ibcq)
struct qib_ibdev *dev = to_idev(ibcq->device); struct qib_ibdev *dev = to_idev(ibcq->device);
struct qib_cq *cq = to_icq(ibcq); struct qib_cq *cq = to_icq(ibcq);
flush_work(&cq->comptask); flush_kthread_work(&cq->comptask);
spin_lock(&dev->n_cqs_lock); spin_lock(&dev->n_cqs_lock);
dev->n_cqs_allocated--; dev->n_cqs_allocated--;
spin_unlock(&dev->n_cqs_lock); spin_unlock(&dev->n_cqs_lock);
...@@ -483,3 +492,49 @@ int qib_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) ...@@ -483,3 +492,49 @@ int qib_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata)
bail: bail:
return ret; return ret;
} }
int qib_cq_init(struct qib_devdata *dd)
{
int ret = 0;
int cpu;
struct task_struct *task;
if (dd->worker)
return 0;
dd->worker = kzalloc(sizeof(*dd->worker), GFP_KERNEL);
if (!dd->worker)
return -ENOMEM;
init_kthread_worker(dd->worker);
task = kthread_create_on_node(
kthread_worker_fn,
dd->worker,
dd->assigned_node_id,
"qib_cq%d", dd->unit);
if (IS_ERR(task))
goto task_fail;
cpu = cpumask_first(cpumask_of_node(dd->assigned_node_id));
kthread_bind(task, cpu);
wake_up_process(task);
out:
return ret;
task_fail:
ret = PTR_ERR(task);
kfree(dd->worker);
dd->worker = NULL;
goto out;
}
void qib_cq_exit(struct qib_devdata *dd)
{
struct kthread_worker *worker;
worker = dd->worker;
if (!worker)
return;
/* blocks future queuing from send_complete() */
dd->worker = NULL;
smp_wmb();
flush_kthread_worker(worker);
kthread_stop(worker->task);
kfree(worker);
}
...@@ -97,8 +97,6 @@ unsigned qib_wc_pat = 1; /* default (1) is to use PAT, not MTRR */ ...@@ -97,8 +97,6 @@ unsigned qib_wc_pat = 1; /* default (1) is to use PAT, not MTRR */
module_param_named(wc_pat, qib_wc_pat, uint, S_IRUGO); module_param_named(wc_pat, qib_wc_pat, uint, S_IRUGO);
MODULE_PARM_DESC(wc_pat, "enable write-combining via PAT mechanism"); MODULE_PARM_DESC(wc_pat, "enable write-combining via PAT mechanism");
struct workqueue_struct *qib_cq_wq;
static void verify_interrupt(unsigned long); static void verify_interrupt(unsigned long);
static struct idr qib_unit_table; static struct idr qib_unit_table;
...@@ -445,6 +443,7 @@ static int loadtime_init(struct qib_devdata *dd) ...@@ -445,6 +443,7 @@ static int loadtime_init(struct qib_devdata *dd)
dd->intrchk_timer.function = verify_interrupt; dd->intrchk_timer.function = verify_interrupt;
dd->intrchk_timer.data = (unsigned long) dd; dd->intrchk_timer.data = (unsigned long) dd;
ret = qib_cq_init(dd);
done: done:
return ret; return ret;
} }
...@@ -1215,12 +1214,6 @@ static int __init qlogic_ib_init(void) ...@@ -1215,12 +1214,6 @@ static int __init qlogic_ib_init(void)
if (ret) if (ret)
goto bail; goto bail;
qib_cq_wq = create_singlethread_workqueue("qib_cq");
if (!qib_cq_wq) {
ret = -ENOMEM;
goto bail_dev;
}
/* /*
* These must be called before the driver is registered with * These must be called before the driver is registered with
* the PCI subsystem. * the PCI subsystem.
...@@ -1233,7 +1226,7 @@ static int __init qlogic_ib_init(void) ...@@ -1233,7 +1226,7 @@ static int __init qlogic_ib_init(void)
ret = pci_register_driver(&qib_driver); ret = pci_register_driver(&qib_driver);
if (ret < 0) { if (ret < 0) {
pr_err("Unable to register driver: error %d\n", -ret); pr_err("Unable to register driver: error %d\n", -ret);
goto bail_unit; goto bail_dev;
} }
/* not fatal if it doesn't work */ /* not fatal if it doesn't work */
...@@ -1241,13 +1234,11 @@ static int __init qlogic_ib_init(void) ...@@ -1241,13 +1234,11 @@ static int __init qlogic_ib_init(void)
pr_err("Unable to register ipathfs\n"); pr_err("Unable to register ipathfs\n");
goto bail; /* all OK */ goto bail; /* all OK */
bail_unit: bail_dev:
#ifdef CONFIG_INFINIBAND_QIB_DCA #ifdef CONFIG_INFINIBAND_QIB_DCA
dca_unregister_notify(&dca_notifier); dca_unregister_notify(&dca_notifier);
#endif #endif
idr_destroy(&qib_unit_table); idr_destroy(&qib_unit_table);
destroy_workqueue(qib_cq_wq);
bail_dev:
qib_dev_cleanup(); qib_dev_cleanup();
bail: bail:
return ret; return ret;
...@@ -1273,8 +1264,6 @@ static void __exit qlogic_ib_cleanup(void) ...@@ -1273,8 +1264,6 @@ static void __exit qlogic_ib_cleanup(void)
#endif #endif
pci_unregister_driver(&qib_driver); pci_unregister_driver(&qib_driver);
destroy_workqueue(qib_cq_wq);
qib_cpulist_count = 0; qib_cpulist_count = 0;
kfree(qib_cpulist); kfree(qib_cpulist);
...@@ -1365,6 +1354,7 @@ static void cleanup_device_data(struct qib_devdata *dd) ...@@ -1365,6 +1354,7 @@ static void cleanup_device_data(struct qib_devdata *dd)
} }
kfree(tmp); kfree(tmp);
kfree(dd->boardname); kfree(dd->boardname);
qib_cq_exit(dd);
} }
/* /*
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/kthread.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <rdma/ib_pack.h> #include <rdma/ib_pack.h>
#include <rdma/ib_user_verbs.h> #include <rdma/ib_user_verbs.h>
...@@ -267,7 +268,8 @@ struct qib_cq_wc { ...@@ -267,7 +268,8 @@ struct qib_cq_wc {
*/ */
struct qib_cq { struct qib_cq {
struct ib_cq ibcq; struct ib_cq ibcq;
struct work_struct comptask; struct kthread_work comptask;
struct qib_devdata *dd;
spinlock_t lock; /* protect changes in this struct */ spinlock_t lock; /* protect changes in this struct */
u8 notify; u8 notify;
u8 triggered; u8 triggered;
...@@ -832,8 +834,6 @@ static inline int qib_send_ok(struct qib_qp *qp) ...@@ -832,8 +834,6 @@ static inline int qib_send_ok(struct qib_qp *qp)
!(qp->s_flags & QIB_S_ANY_WAIT_SEND)); !(qp->s_flags & QIB_S_ANY_WAIT_SEND));
} }
extern struct workqueue_struct *qib_cq_wq;
/* /*
* This must be called with s_lock held. * This must be called with s_lock held.
*/ */
...@@ -972,6 +972,10 @@ int qib_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr); ...@@ -972,6 +972,10 @@ int qib_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr);
int qib_destroy_srq(struct ib_srq *ibsrq); int qib_destroy_srq(struct ib_srq *ibsrq);
int qib_cq_init(struct qib_devdata *dd);
void qib_cq_exit(struct qib_devdata *dd);
void qib_cq_enter(struct qib_cq *cq, struct ib_wc *entry, int sig); void qib_cq_enter(struct qib_cq *cq, struct ib_wc *entry, int sig);
int qib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry); int qib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment