[PATCH 01/10] IB/ehca: Support for multiple event queues

Joachim Fenkes
2007-07-12T15:46:49Z

From: Hoang-Nam Nguyen <hnguyen@de.ibm.com>

The eHCA driver can now handle multiple event queues (read: interrupt
sources) instead of one. The number of available EQs is selected via the
nr_eqs module parameter.

CQs are either assigned to the EQs based on the comp_vector index or, if the
dist_eqs module parameter is supplied, using a round-robin scheme.

Signed-off-by: Joachim Fenkes <fenkes@de.ibm.com>
---
drivers/infiniband/hw/ehca/ehca_classes.h | 13 +++-
drivers/infiniband/hw/ehca/ehca_cq.c | 16 +++-
drivers/infiniband/hw/ehca/ehca_eq.c | 139 ++++++++++++++++++-----------
drivers/infiniband/hw/ehca/ehca_irq.c | 36 +++-----
drivers/infiniband/hw/ehca/ehca_irq.h | 8 +-
drivers/infiniband/hw/ehca/ehca_iverbs.h | 9 +-
drivers/infiniband/hw/ehca/ehca_main.c | 118 ++++++++++++++++++++-----
drivers/infiniband/hw/ehca/ehca_qp.c | 2 +-
8 files changed, 233 insertions(+), 108 deletions(-)

diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/infiniband/hw/ehca/ehca_classes.h
index daf823e..b2d614a 100644
--- a/drivers/infiniband/hw/ehca/ehca_classes.h
+++ b/drivers/infiniband/hw/ehca/ehca_classes.h
@@ -72,7 +72,11 @@ struct ehca_eqe_cache_entry {
struct ehca_cq *cq;
};

+struct ehca_shca;
+
struct ehca_eq {
+ struct ehca_shca *shca;
+ char name[17];
u32 length;
struct ipz_queue ipz_queue;
struct ipz_eq_handle ipz_eq_handle;
@@ -100,6 +104,7 @@ struct ehca_sport {
struct ehca_sma_attr saved_attr;
};

+#define EHCA_MAX_NR_EQS 512
struct ehca_shca {
struct ib_device ib_device;
struct ibmebus_dev *ibmebus_dev;
@@ -108,14 +113,16 @@ struct ehca_shca {
struct list_head shca_list;
struct ipz_adapter_handle ipz_hca_handle;
struct ehca_sport sport[2];
- struct ehca_eq eq;
- struct ehca_eq neq;
+ struct ehca_eq **eqs;
+ struct ehca_eq *aeq; /* async event for qps */
+ struct ehca_eq *neq;
struct ehca_mr *maxmr;
struct ehca_pd *pd;
struct h_galpas galpas;
struct mutex modify_mutex;
u64 hca_cap;
int max_mtu;
+ atomic_t cur_eq_idx;
};

struct ehca_pd {
@@ -290,6 +297,8 @@ struct ehca_ucontext {

int ehca_init_pd_cache(void);
void ehca_cleanup_pd_cache(void);
+int ehca_init_eq_cache(void);
+void ehca_cleanup_eq_cache(void);
int ehca_init_cq_cache(void);
void ehca_cleanup_cq_cache(void);
int ehca_init_qp_cache(void);
diff --git a/drivers/infiniband/hw/ehca/ehca_cq.c b/drivers/infiniband/hw/ehca/ehca_cq.c
index 01d4a14..97da51e 100644
--- a/drivers/infiniband/hw/ehca/ehca_cq.c
+++ b/drivers/infiniband/hw/ehca/ehca_cq.c
@@ -117,6 +117,8 @@ struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector,
struct ib_ucontext *context,
struct ib_udata *udata)
{
+ extern int ehca_nr_eqs;
+ extern int ehca_dist_eqs;
static const u32 additional_cqe = 20;
struct ib_cq *cq;
struct ehca_cq *my_cq;
@@ -134,6 +136,12 @@ struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector,
if (cqe >= 0xFFFFFFFF - 64 - additional_cqe)
return ERR_PTR(-EINVAL);

+ if (comp_vector < 0 || comp_vector >= ehca_nr_eqs) {
+ ehca_err(device, "Invalid comp_vector=%x ehca_nr_eqs=%x",
+ comp_vector, ehca_nr_eqs);
+ return ERR_PTR(-EINVAL);
+ }
+
my_cq = kmem_cache_zalloc(cq_cache, GFP_KERNEL);
if (!my_cq) {
ehca_err(device, "Out of memory for ehca_cq struct device=%p",
@@ -153,7 +161,13 @@ struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector,
cq = &my_cq->ib_cq;

adapter_handle = shca->ipz_hca_handle;
- param.eq_handle = shca->eq.ipz_eq_handle;
+ if (!ehca_dist_eqs)
+ param.eq_handle = shca->eqs[comp_vector]->ipz_eq_handle;
+ else {
+ u32 eq_idx = atomic_inc_return(&shca->cur_eq_idx) % ehca_nr_eqs;
+ param.eq_handle = shca->eqs[eq_idx]->ipz_eq_handle;
+ ehca_dbg(device, "assigned comp_vector=%x", eq_idx);
+ }

do {
if (!idr_pre_get(&ehca_cq_idr, GFP_KERNEL)) {
diff --git a/drivers/infiniband/hw/ehca/ehca_eq.c b/drivers/infiniband/hw/ehca/ehca_eq.c
index 4961eb8..d443bcb 100644
--- a/drivers/infiniband/hw/ehca/ehca_eq.c
+++ b/drivers/infiniband/hw/ehca/ehca_eq.c
@@ -8,6 +8,7 @@
* Reinhard Ernst <rernst@de.ibm.com>
* Heiko J Schick <schickhj@de.ibm.com>
* Hoang-Nam Nguyen <hnguyen@de.ibm.com>
+ * Joachim Fenkes <fenkes@de.ibm.com>
*
*
* Copyright (c) 2005 IBM Corporation
@@ -50,40 +51,54 @@
#include "hcp_if.h"
#include "ipz_pt_fn.h"

-int ehca_create_eq(struct ehca_shca *shca,
- struct ehca_eq *eq,
- const enum ehca_eq_type type, const u32 length)
+static struct kmem_cache *eq_cache;
+
+struct ehca_eq *ehca_create_eq(struct ehca_shca *shca,
+ const enum ehca_eq_type type, const u32 length)
{
- u64 ret;
+ struct ehca_eq *eq = NULL;
+ int ret;
+ u64 h_ret;
u32 nr_pages;
u32 i;
void *vpage;
struct ib_device *ib_dev = &shca->ib_device;

- spin_lock_init(&eq->spinlock);
- spin_lock_init(&eq->irq_spinlock);
- eq->is_initialized = 0;
+ if (!length) {
+ ehca_err(ib_dev, "EQ length must not be zero.");
+ return ERR_PTR(-EINVAL);
+ }

if (type != EHCA_EQ && type != EHCA_NEQ) {
- ehca_err(ib_dev, "Invalid EQ type %x. eq=%p", type, eq);
- return -EINVAL;
+ ehca_err(ib_dev, "Invalid EQ type %x", type);
+ return ERR_PTR(-EINVAL);
}
- if (!length) {
- ehca_err(ib_dev, "EQ length must not be zero. eq=%p", eq);
- return -EINVAL;
+
+ eq = kmem_cache_zalloc(eq_cache, GFP_KERNEL);
+ if (!eq) {
+ ehca_err(ib_dev, "Out of memory for ehca_eq struct device=%p",
+ ib_dev);
+ return ERR_PTR(-ENOMEM);
}

- ret = hipz_h_alloc_resource_eq(shca->ipz_hca_handle,
- &eq->pf,
- type,
- length,
- &eq->ipz_eq_handle,
- &eq->length,
- &nr_pages, &eq->ist);
+ spin_lock_init(&eq->spinlock);
+ spin_lock_init(&eq->irq_spinlock);
+ eq->is_initialized = 0;
+ eq->shca = shca;

- if (ret != H_SUCCESS) {
- ehca_err(ib_dev, "Can't allocate EQ/NEQ. eq=%p", eq);
- return -EINVAL;
+ h_ret = hipz_h_alloc_resource_eq(shca->ipz_hca_handle,
+ &eq->pf,
+ type,
+ length,
+ &eq->ipz_eq_handle,
+ &eq->length,
+ &nr_pages, &eq->ist);
+
+ if (h_ret != H_SUCCESS) {
+ ehca_err(ib_dev, "Can't allocate EQ/NEQ. eq=%p h_ret=%lx",
+ eq, h_ret);
+ ret = -EINVAL;
+ goto create_eq_exit0;
}

ret = ipz_queue_ctor(&eq->ipz_queue, nr_pages,
@@ -97,51 +112,51 @@ int ehca_create_eq(struct ehca_shca *shca,
u64 rpage;

if (!(vpage = ipz_qpageit_get_inc(&eq->ipz_queue))) {
- ret = H_RESOURCE;
+ ret = -ENOMEM;
goto create_eq_exit2;
}

rpage = virt_to_abs(vpage);
- ret = hipz_h_register_rpage_eq(shca->ipz_hca_handle,
- eq->ipz_eq_handle,
- &eq->pf,
- 0, 0, rpage, 1);
+ h_ret = hipz_h_register_rpage_eq(shca->ipz_hca_handle,
+ eq->ipz_eq_handle,
+ &eq->pf,
+ 0, 0, rpage, 1);

if (i == (nr_pages - 1)) {
/* last page */
vpage = ipz_qpageit_get_inc(&eq->ipz_queue);
- if (ret != H_SUCCESS || vpage)
+ if (h_ret != H_SUCCESS || vpage) {
+ ret = -ENOMEM;
goto create_eq_exit2;
+ }
} else {
- if (ret != H_PAGE_REGISTERED || !vpage)
+ if (h_ret != H_PAGE_REGISTERED || !vpage) {
+ ret = -ENOMEM;
goto create_eq_exit2;
+ }
}
}

ipz_qeit_reset(&eq->ipz_queue);

/* register interrupt handlers and initialize work queues */
- if (type == EHCA_EQ) {
- ret = ibmebus_request_irq(NULL, eq->ist, ehca_interrupt_eq,
- IRQF_DISABLED, "ehca_eq",
- (void *)shca);
- if (ret < 0)
- ehca_err(ib_dev, "Can't map interrupt handler.");
-
- tasklet_init(&eq->interrupt_task, ehca_tasklet_eq, (long)shca);
- } else if (type == EHCA_NEQ) {
- ret = ibmebus_request_irq(NULL, eq->ist, ehca_interrupt_neq,
- IRQF_DISABLED, "ehca_neq",
- (void *)shca);
- if (ret < 0)
- ehca_err(ib_dev, "Can't map interrupt handler.");
-
- tasklet_init(&eq->interrupt_task, ehca_tasklet_neq, (long)shca);
- }
+ if (type == EHCA_EQ)
+ snprintf(eq->name, sizeof(eq->name), "ehca_eq_%x", eq->ist);
+ else
+ snprintf(eq->name, sizeof(eq->name), "ehca_neq");
+
+ ret = ibmebus_request_irq(NULL, eq->ist, ehca_interrupt,
+ IRQF_DISABLED, eq->name, (void *)eq);
+ if (ret < 0)
+ ehca_err(ib_dev, "Can't map interrupt handler.");
+
+ tasklet_init(&eq->interrupt_task,
+ (type == EHCA_EQ) ? ehca_tasklet_eq : ehca_tasklet_neq,
+ (long)eq);

eq->is_initialized = 1;

- return 0;
+ return eq;

create_eq_exit2:
ipz_queue_dtor(&eq->ipz_queue);
@@ -149,10 +164,13 @@ create_eq_exit2:
create_eq_exit1:
hipz_h_destroy_eq(shca->ipz_hca_handle, eq);

- return -EINVAL;
+create_eq_exit0:
+ kmem_cache_free(eq_cache, eq);
+
+ return ERR_PTR(ret);
}

-void *ehca_poll_eq(struct ehca_shca *shca, struct ehca_eq *eq)
+void *ehca_poll_eq(struct ehca_eq *eq)
{
unsigned long flags;
void *eqe;
@@ -164,13 +182,14 @@ void *ehca_poll_eq(struct ehca_shca *shca, struct ehca_eq *eq)
return eqe;
}

-int ehca_destroy_eq(struct ehca_shca *shca, struct ehca_eq *eq)
+int ehca_destroy_eq(struct ehca_eq *eq)
{
+ struct ehca_shca *shca = eq->shca;
unsigned long flags;
u64 h_ret;

spin_lock_irqsave(&eq->spinlock, flags);
- ibmebus_free_irq(NULL, eq->ist, (void *)shca);
+ ibmebus_free_irq(NULL, eq->ist, (void *)eq);

h_ret = hipz_h_destroy_eq(shca->ipz_hca_handle, eq);

@@ -181,6 +200,24 @@ int ehca_destroy_eq(struct ehca_shca *shca, struct ehca_eq *eq)
return -EINVAL;
}
ipz_queue_dtor(&eq->ipz_queue);
+ kmem_cache_free(eq_cache, eq);

return 0;
}
+
+int ehca_init_eq_cache(void)
+{
+ eq_cache = kmem_cache_create("ehca_cache_eq",
+ sizeof(struct ehca_eq), 0,
+ SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (!eq_cache)
+ return -ENOMEM;
+ return 0;
+}
+
+void ehca_cleanup_eq_cache(void)
+{
+ if (eq_cache)
+ kmem_cache_destroy(eq_cache);
+}
diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c
index 96eba38..7a4071a 100644
--- a/drivers/infiniband/hw/ehca/ehca_irq.c
+++ b/drivers/infiniband/hw/ehca/ehca_irq.c
@@ -389,32 +389,24 @@ static inline void reset_eq_pending(struct ehca_cq *cq)
return;
}

-irqreturn_t ehca_interrupt_neq(int irq, void *dev_id)
-{
- struct ehca_shca *shca = (struct ehca_shca*)dev_id;
-
- tasklet_hi_schedule(&shca->neq.interrupt_task);
-
- return IRQ_HANDLED;
-}
-
void ehca_tasklet_neq(unsigned long data)
{
- struct ehca_shca *shca = (struct ehca_shca*)data;
+ struct ehca_eq *neq = (struct ehca_eq *)data;
+ struct ehca_shca *shca = neq->shca;
struct ehca_eqe *eqe;
u64 ret;

- eqe = (struct ehca_eqe *)ehca_poll_eq(shca, &shca->neq);
+ eqe = (struct ehca_eqe *)ehca_poll_eq(neq);

while (eqe) {
if (!EHCA_BMASK_GET(NEQE_COMPLETION_EVENT, eqe->entry))
parse_ec(shca, eqe->entry);

- eqe = (struct ehca_eqe *)ehca_poll_eq(shca, &shca->neq);
+ eqe = (struct ehca_eqe *)ehca_poll_eq(neq);
}

ret = hipz_h_reset_event(shca->ipz_hca_handle,
- shca->neq.ipz_eq_handle, 0xFFFFFFFFFFFFFFFFL);
+ neq->ipz_eq_handle, 0xFFFFFFFFFFFFFFFFL);

if (ret != H_SUCCESS)
ehca_err(&shca->ib_device, "Can't clear notification events.");
@@ -422,11 +414,11 @@ void ehca_tasklet_neq(unsigned long data)
return;
}

-irqreturn_t ehca_interrupt_eq(int irq, void *dev_id)
+irqreturn_t ehca_interrupt(int irq, void *dev_id)
{
- struct ehca_shca *shca = (struct ehca_shca*)dev_id;
+ struct ehca_eq *eq = (struct ehca_eq *)dev_id;

- tasklet_hi_schedule(&shca->eq.interrupt_task);
+ tasklet_hi_schedule(&eq->interrupt_task);

return IRQ_HANDLED;
}
@@ -468,9 +460,9 @@ static inline void process_eqe(struct ehca_shca *shca, struct ehca_eqe *eqe)
}
}

-void ehca_process_eq(struct ehca_shca *shca, int is_irq)
+void ehca_process_eq(struct ehca_eq *eq, int is_irq)
{
- struct ehca_eq *eq = &shca->eq;
+ struct ehca_shca *shca = eq->shca;
struct ehca_eqe_cache_entry *eqe_cache = eq->eqe_cache;
u64 eqe_value;
unsigned long flags;
@@ -498,7 +490,7 @@ void ehca_process_eq(struct ehca_shca *shca, int is_irq)
do {
u32 token;
eqe_cache[eqe_cnt].eqe =
- (struct ehca_eqe *)ehca_poll_eq(shca, eq);
+ (struct ehca_eqe *)ehca_poll_eq(eq);
if (!eqe_cache[eqe_cnt].eqe)
break;
eqe_value = eqe_cache[eqe_cnt].eqe->entry;
@@ -535,7 +527,7 @@ void ehca_process_eq(struct ehca_shca *shca, int is_irq)
}
/* check eq */
spin_lock(&eq->spinlock);
- eq_empty = (!ipz_eqit_eq_peek_valid(&shca->eq.ipz_queue));
+ eq_empty = (!ipz_eqit_eq_peek_valid(&eq->ipz_queue));
spin_unlock(&eq->spinlock);
/* call completion handler for cached eqes */
for (i = 0; i < eqe_cnt; i++)
@@ -557,7 +549,7 @@ void ehca_process_eq(struct ehca_shca *shca, int is_irq)
goto unlock_irq_spinlock;
do {
struct ehca_eqe *eqe;
- eqe = (struct ehca_eqe *)ehca_poll_eq(shca, &shca->eq);
+ eqe = (struct ehca_eqe *)ehca_poll_eq(eq);
if (!eqe)
break;
process_eqe(shca, eqe);
@@ -569,7 +561,7 @@ unlock_irq_spinlock:

void ehca_tasklet_eq(unsigned long data)
{
- ehca_process_eq((struct ehca_shca*)data, 1);
+ ehca_process_eq((struct ehca_eq *)data, 1);
}

static inline int find_next_online_cpu(struct ehca_comp_pool* pool)
diff --git a/drivers/infiniband/hw/ehca/ehca_irq.h b/drivers/infiniband/hw/ehca/ehca_irq.h
index 3346cb0..18d5397 100644
--- a/drivers/infiniband/hw/ehca/ehca_irq.h
+++ b/drivers/infiniband/hw/ehca/ehca_irq.h
@@ -50,12 +50,10 @@ struct ehca_shca;

int ehca_error_data(struct ehca_shca *shca, void *data, u64 resource);

-irqreturn_t ehca_interrupt_neq(int irq, void *dev_id);
-void ehca_tasklet_neq(unsigned long data);
-
-irqreturn_t ehca_interrupt_eq(int irq, void *dev_id);
+irqreturn_t ehca_interrupt(int irq, void *dev_id);
void ehca_tasklet_eq(unsigned long data);
-void ehca_process_eq(struct ehca_shca *shca, int is_irq);
+void ehca_tasklet_neq(unsigned long data);
+void ehca_process_eq(struct ehca_eq *eq, int is_irq);

struct ehca_cpu_comp_task {
wait_queue_head_t wait_queue;
diff --git a/drivers/infiniband/hw/ehca/ehca_iverbs.h b/drivers/infiniband/hw/ehca/ehca_iverbs.h
index 77aeca6..bf8fbf7 100644
--- a/drivers/infiniband/hw/ehca/ehca_iverbs.h
+++ b/drivers/infiniband/hw/ehca/ehca_iverbs.h
@@ -117,13 +117,12 @@ enum ehca_eq_type {
EHCA_NEQ /* Notification Event Queue */
};

-int ehca_create_eq(struct ehca_shca *shca, struct ehca_eq *eq,
- enum ehca_eq_type type, const u32 length);
+struct ehca_eq *ehca_create_eq(struct ehca_shca *shca,
+ const enum ehca_eq_type type, const u32 length);

-int ehca_destroy_eq(struct ehca_shca *shca, struct ehca_eq *eq);
-
-void *ehca_poll_eq(struct ehca_shca *shca, struct ehca_eq *eq);
+int ehca_destroy_eq(struct ehca_eq *eq);

+void *ehca_poll_eq(struct ehca_eq *eq);

struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector,
struct ib_ucontext *context,
diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c
index 28ba2dd..d9a37dc 100644
--- a/drivers/infiniband/hw/ehca/ehca_main.c
+++ b/drivers/infiniband/hw/ehca/ehca_main.c
@@ -63,6 +63,8 @@ int ehca_port_act_time = 30;
int ehca_poll_all_eqs = 1;
int ehca_static_rate = -1;
int ehca_scaling_code = 0;
+int ehca_nr_eqs = 2;
+int ehca_dist_eqs = 0;

module_param_named(open_aqp1, ehca_open_aqp1, int, 0);
module_param_named(debug_level, ehca_debug_level, int, 0);
@@ -72,7 +74,9 @@ module_param_named(use_hp_mr, ehca_use_hp_mr, int, 0);
module_param_named(port_act_time, ehca_port_act_time, int, 0);
module_param_named(poll_all_eqs, ehca_poll_all_eqs, int, 0);
module_param_named(static_rate, ehca_static_rate, int, 0);
-module_param_named(scaling_code, ehca_scaling_code, int, 0);
+module_param_named(scaling_code, ehca_scaling_code, int, 0);
+module_param_named(nr_eqs, ehca_nr_eqs, int, 0);
+module_param_named(dist_eqs, ehca_dist_eqs, int, 0);

MODULE_PARM_DESC(open_aqp1,
"AQP1 on startup (0: no (default), 1: yes)");
@@ -95,6 +99,11 @@ MODULE_PARM_DESC(static_rate,
"set permanent static rate (default: disabled)");
MODULE_PARM_DESC(scaling_code,
"set scaling code (0: disabled/default, 1: enabled)");
+MODULE_PARM_DESC(nr_eqs,
+ "set number of event queues (default : 2)");
+MODULE_PARM_DESC(dist_eqs,
+ "enable distributing EQs across CQs "
+ "(0: disabled/default, 1: enabled)");

DEFINE_RWLOCK(ehca_qp_idr_lock);
DEFINE_RWLOCK(ehca_cq_idr_lock);
@@ -135,6 +144,12 @@ static int ehca_create_slab_caches(void)
return ret;
}

+ ret = ehca_init_eq_cache();
+ if (ret) {
+ ehca_gen_err("Cannot create EQ SLAB cache.");
+ goto create_slab_caches1;
+ }
+
ret = ehca_init_cq_cache();
if (ret) {
ehca_gen_err("Cannot create CQ SLAB cache.");
@@ -182,6 +197,9 @@ create_slab_caches3:
ehca_cleanup_cq_cache();

create_slab_caches2:
+ ehca_cleanup_eq_cache();
+
+create_slab_caches1:
ehca_cleanup_pd_cache();

return ret;
@@ -193,6 +211,7 @@ static void ehca_destroy_slab_caches(void)
ehca_cleanup_av_cache();
ehca_cleanup_qp_cache();
ehca_cleanup_cq_cache();
+ ehca_cleanup_eq_cache();
ehca_cleanup_pd_cache();
#ifdef CONFIG_PPC_64K_PAGES
if (ctblk_cache)
@@ -362,7 +381,7 @@ int ehca_init_device(struct ehca_shca *shca)

shca->ib_device.node_type = RDMA_NODE_IB_CA;
shca->ib_device.phys_port_cnt = shca->num_ports;
- shca->ib_device.num_comp_vectors = 1;
+ shca->ib_device.num_comp_vectors = ehca_nr_eqs;
shca->ib_device.dma_device = &shca->ibmebus_dev->ofdev.dev;
shca->ib_device.query_device = ehca_query_device;
shca->ib_device.query_port = ehca_query_port;
@@ -585,6 +604,15 @@ static ssize_t ehca_show_adapter_handle(struct device *dev,
}
static DEVICE_ATTR(adapter_handle, S_IRUGO, ehca_show_adapter_handle, NULL);

+static ssize_t ehca_show_nr_eqs(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", ehca_nr_eqs);
+}
+
+static DEVICE_ATTR(nr_eqs, S_IRUGO, ehca_show_nr_eqs, NULL);
+
static struct attribute *ehca_dev_attrs[] = {
&dev_attr_adapter_handle.attr,
&dev_attr_num_ports.attr,
@@ -601,6 +629,7 @@ static struct attribute *ehca_dev_attrs[] = {
&dev_attr_cur_mw.attr,
&dev_attr_max_pd.attr,
&dev_attr_max_ah.attr,
+ &dev_attr_nr_eqs.attr,
NULL
};

@@ -608,13 +637,27 @@ static struct attribute_group ehca_dev_attr_grp = {
.attrs = ehca_dev_attrs
};

+static void destroy_all_eqs(struct ehca_shca *shca)
+{
+ int ret, i;
+
+ for (i = 0; i < ehca_nr_eqs && shca->eqs[i]; i++) {
+ ret = ehca_destroy_eq(shca->eqs[i]);
+ if (ret)
+ ehca_err(&shca->ib_device, "Cannot destroy EQ "
+ "ret=%x i=%x eq=%p", ret, i, shca->eqs[i]);
+ }
+
+ kfree(shca->eqs);
+}
+
static int __devinit ehca_probe(struct ibmebus_dev *dev,
const struct of_device_id *id)
{
struct ehca_shca *shca;
const u64 *handle;
struct ib_pd *ibpd;
- int ret;
+ int ret, i;

handle = of_get_property(dev->ofdev.node, "ibm,hca-handle", NULL);
if (!handle) {
@@ -648,19 +691,35 @@ static int __devinit ehca_probe(struct ibmebus_dev *dev,

ret = ehca_init_device(shca);
if (ret) {
- ehca_gen_err("Cannot init ehca device struct");
+ ehca_gen_err("Cannot init ehca device struct");
goto probe1;
}

/* create event queues */
- ret = ehca_create_eq(shca, &shca->eq, EHCA_EQ, 2048);
- if (ret) {
- ehca_err(&shca->ib_device, "Cannot create EQ.");
+ shca->eqs = kzalloc(ehca_nr_eqs * sizeof(*shca->eqs), GFP_KERNEL);
+ if (!shca->eqs) {
+ ehca_gen_err("Cannot alloc eqs array");
goto probe1;
}

- ret = ehca_create_eq(shca, &shca->neq, EHCA_NEQ, 513);
- if (ret) {
+ for (i = 0; i < ehca_nr_eqs; i++) {
+ shca->eqs[i] = ehca_create_eq(shca, EHCA_EQ, 2048);
+ if (IS_ERR(shca->eqs[i])) {
+ ehca_err(&shca->ib_device, "Cannot create EQ.");
+ ret = PTR_ERR(shca->eqs[i]);
+ shca->eqs[i] = NULL;
+ goto probe2;
+ }
+ }
+
+ shca->aeq = ehca_create_eq(shca, EHCA_EQ, 2048);
+ if (IS_ERR(shca->aeq)) {
+ ehca_err(&shca->ib_device, "Cannot create AEQ.");
+ goto probe2;
+ }
+
+ shca->neq = ehca_create_eq(shca, EHCA_NEQ, 513);
+ if (IS_ERR(shca->neq)) {
ehca_err(&shca->ib_device, "Cannot create NEQ.");
goto probe3;
}
@@ -747,16 +806,20 @@ probe5:
"Cannot destroy internal PD. ret=%x", ret);

probe4:
- ret = ehca_destroy_eq(shca, &shca->neq);
+ ret = ehca_destroy_eq(shca->neq);
if (ret)
ehca_err(&shca->ib_device,
"Cannot destroy NEQ. ret=%x", ret);

probe3:
- ret = ehca_destroy_eq(shca, &shca->eq);
+ ret = ehca_destroy_eq(shca->aeq);
if (ret)
ehca_err(&shca->ib_device,
- "Cannot destroy EQ. ret=%x", ret);
+ "Cannot destroy AEQ. ret=%x", ret);
+
+probe2:
+ if (shca->eqs)
+ destroy_all_eqs(shca);

probe1:
ib_dealloc_device(&shca->ib_device);
@@ -767,12 +830,11 @@ probe1:
static int __devexit ehca_remove(struct ibmebus_dev *dev)
{
struct ehca_shca *shca = dev->ofdev.dev.driver_data;
- int ret;
+ int ret, i;

sysfs_remove_group(&dev->ofdev.dev.kobj, &ehca_dev_attr_grp);

if (ehca_open_aqp1 == 1) {
- int i;
for (i = 0; i < shca->num_ports; i++) {
ret = ehca_destroy_aqp1(&shca->sport[i]);
if (ret)
@@ -794,11 +856,14 @@ static int __devexit ehca_remove(struct ibmebus_dev *dev)
ehca_err(&shca->ib_device,
"Cannot destroy internal PD. ret=%x", ret);

- ret = ehca_destroy_eq(shca, &shca->eq);
+ if (shca->eqs)
+ destroy_all_eqs(shca);
+
+ ret = ehca_destroy_eq(shca->aeq);
if (ret)
- ehca_err(&shca->ib_device, "Cannot destroy EQ. ret=%x", ret);
+ ehca_err(&shca->ib_device, "Canot destroy AEQ. ret=%x", ret);

- ret = ehca_destroy_eq(shca, &shca->neq);
+ ret = ehca_destroy_eq(shca->neq);
if (ret)
ehca_err(&shca->ib_device, "Canot destroy NEQ. ret=%x", ret);

@@ -829,16 +894,20 @@ static struct ibmebus_driver ehca_driver = {

void ehca_poll_eqs(unsigned long data)
{
+ extern int ehca_nr_eqs;
struct ehca_shca *shca;

spin_lock(&shca_list_lock);
list_for_each_entry(shca, &shca_list, shca_list) {
- if (shca->eq.is_initialized) {
- /* call deadman proc only if eq ptr does not change */
- struct ehca_eq *eq = &shca->eq;
+ int i;
+ for (i = 0; i < ehca_nr_eqs; i++) {
+ struct ehca_eq *eq = shca->eqs[i];
int max = 3;
volatile u64 q_ofs, q_ofs2;
u64 flags;
+ if (!eq || !eq->is_initialized)
+ continue;
+ /* call deadman proc only if eq ptr does not change */
spin_lock_irqsave(&eq->spinlock, flags);
q_ofs = eq->ipz_queue.current_q_offset;
spin_unlock_irqrestore(&eq->spinlock, flags);
@@ -849,7 +918,7 @@ void ehca_poll_eqs(unsigned long data)
max--;
} while (q_ofs == q_ofs2 && max > 0);
if (q_ofs == q_ofs2)
- ehca_process_eq(shca, 0);
+ ehca_process_eq(eq, 0);
}
}
mod_timer(&poll_eqs_timer, jiffies + HZ);
@@ -863,6 +932,13 @@ int __init ehca_module_init(void)
printk(KERN_INFO "eHCA Infiniband Device Driver "
"(Rel.: SVNEHCA_0023)\n");

+ if (ehca_nr_eqs < 1 || ehca_nr_eqs > EHCA_MAX_NR_EQS) {
+ ehca_gen_err("Invalid option nr_eqs=%x. "
+ "Specify a number in range [1-%d].",
+ ehca_nr_eqs, EHCA_MAX_NR_EQS);
+ return -EINVAL;
+ }
+
if ((ret = ehca_create_comp_pool())) {
ehca_gen_err("Cannot create comp pool.");
return ret;
diff --git a/drivers/infiniband/hw/ehca/ehca_qp.c b/drivers/infiniband/hw/ehca/ehca_qp.c
index 7467125..f6f4ef6 100644
--- a/drivers/infiniband/hw/ehca/ehca_qp.c
+++ b/drivers/infiniband/hw/ehca/ehca_qp.c
@@ -545,7 +545,7 @@ struct ehca_qp *internal_create_qp(struct ib_pd *pd,
}

parms.token = my_qp->token;
- parms.eq_handle = shca->eq.ipz_eq_handle;
+ parms.eq_handle = shca->aeq->ipz_eq_handle;
parms.pd = my_pd->fw_pd;
if (my_qp->send_cq)
parms.send_cq_handle = my_qp->send_cq->ipz_cq_handle;
--
1.5.2