aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatan Barak <matanb@mellanox.com>2016-04-17 10:08:40 -0400
committerDoug Ledford <dledford@redhat.com>2016-05-18 10:45:49 -0400
commit94c6825e0ff75829207af6246782811b7c7af2c0 (patch)
tree695b147b15bbecbb234cbb06c6bd1fdab63c0abe
parente3b6d8cf8de6d07af9a27c86861edfa5b3290cb6 (diff)
net/mlx5_core: Use tasklet for user-space CQ completion events
Previously, we've fired all our completion callbacks straight from our ISR. Some of those callbacks were lightweight (for example, mlx5 Ethernet napi callbacks), but some of them did more work (for example, the user-space RDMA stack uverbs' completion handler). Besides that, doing more than the minimal work in ISR is generally considered wrong, it could even lead to a hard lockup of the system. Since when a lot of completion events are generated by the hardware, the loop over those events could be so long, that we'll get into a hard lockup by the system watchdog. In order to avoid that, add a new way of invoking completion events callbacks. In the interrupt itself, we add the CQs which receive completion event to a per-EQ list and schedule a tasklet. In the tasklet context we loop over all the CQs in the list and invoke the user callback. Signed-off-by: Matan Barak <matanb@mellanox.com> Signed-off-by: Doug Ledford <dledford@redhat.com>
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cq.c59
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eq.c12
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c17
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h2
-rw-r--r--include/linux/mlx5/cq.h5
-rw-r--r--include/linux/mlx5/driver.h10
6 files changed, 104 insertions, 1 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cq.c b/drivers/net/ethernet/mellanox/mlx5/core/cq.c
index b51e42d6fbec..873a631ad155 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/cq.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cq.c
@@ -39,6 +39,53 @@
39#include <linux/mlx5/cq.h> 39#include <linux/mlx5/cq.h>
40#include "mlx5_core.h" 40#include "mlx5_core.h"
41 41
42#define TASKLET_MAX_TIME 2
43#define TASKLET_MAX_TIME_JIFFIES msecs_to_jiffies(TASKLET_MAX_TIME)
44
45void mlx5_cq_tasklet_cb(unsigned long data)
46{
47 unsigned long flags;
48 unsigned long end = jiffies + TASKLET_MAX_TIME_JIFFIES;
49 struct mlx5_eq_tasklet *ctx = (struct mlx5_eq_tasklet *)data;
50 struct mlx5_core_cq *mcq;
51 struct mlx5_core_cq *temp;
52
53 spin_lock_irqsave(&ctx->lock, flags);
54 list_splice_tail_init(&ctx->list, &ctx->process_list);
55 spin_unlock_irqrestore(&ctx->lock, flags);
56
57 list_for_each_entry_safe(mcq, temp, &ctx->process_list,
58 tasklet_ctx.list) {
59 list_del_init(&mcq->tasklet_ctx.list);
60 mcq->tasklet_ctx.comp(mcq);
61 if (atomic_dec_and_test(&mcq->refcount))
62 complete(&mcq->free);
63 if (time_after(jiffies, end))
64 break;
65 }
66
67 if (!list_empty(&ctx->process_list))
68 tasklet_schedule(&ctx->task);
69}
70
71static void mlx5_add_cq_to_tasklet(struct mlx5_core_cq *cq)
72{
73 unsigned long flags;
74 struct mlx5_eq_tasklet *tasklet_ctx = cq->tasklet_ctx.priv;
75
76 spin_lock_irqsave(&tasklet_ctx->lock, flags);
77 /* When migrating CQs between EQs will be implemented, please note
78 * that you need to sync this point. It is possible that
79 * while migrating a CQ, completions on the old EQs could
80 * still arrive.
81 */
82 if (list_empty_careful(&cq->tasklet_ctx.list)) {
83 atomic_inc(&cq->refcount);
84 list_add_tail(&cq->tasklet_ctx.list, &tasklet_ctx->list);
85 }
86 spin_unlock_irqrestore(&tasklet_ctx->lock, flags);
87}
88
42void mlx5_cq_completion(struct mlx5_core_dev *dev, u32 cqn) 89void mlx5_cq_completion(struct mlx5_core_dev *dev, u32 cqn)
43{ 90{
44 struct mlx5_core_cq *cq; 91 struct mlx5_core_cq *cq;
@@ -96,6 +143,13 @@ int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
96 struct mlx5_create_cq_mbox_out out; 143 struct mlx5_create_cq_mbox_out out;
97 struct mlx5_destroy_cq_mbox_in din; 144 struct mlx5_destroy_cq_mbox_in din;
98 struct mlx5_destroy_cq_mbox_out dout; 145 struct mlx5_destroy_cq_mbox_out dout;
146 int eqn = MLX5_GET(cqc, MLX5_ADDR_OF(create_cq_in, in, cq_context),
147 c_eqn);
148 struct mlx5_eq *eq;
149
150 eq = mlx5_eqn2eq(dev, eqn);
151 if (IS_ERR(eq))
152 return PTR_ERR(eq);
99 153
100 in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_CQ); 154 in->hdr.opcode = cpu_to_be16(MLX5_CMD_OP_CREATE_CQ);
101 memset(&out, 0, sizeof(out)); 155 memset(&out, 0, sizeof(out));
@@ -111,6 +165,11 @@ int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
111 cq->arm_sn = 0; 165 cq->arm_sn = 0;
112 atomic_set(&cq->refcount, 1); 166 atomic_set(&cq->refcount, 1);
113 init_completion(&cq->free); 167 init_completion(&cq->free);
168 if (!cq->comp)
169 cq->comp = mlx5_add_cq_to_tasklet;
170 /* assuming CQ will be deleted before the EQ */
171 cq->tasklet_ctx.priv = &eq->tasklet_ctx;
172 INIT_LIST_HEAD(&cq->tasklet_ctx.list);
114 173
115 spin_lock_irq(&table->lock); 174 spin_lock_irq(&table->lock);
116 err = radix_tree_insert(&table->tree, cq->cqn, cq); 175 err = radix_tree_insert(&table->tree, cq->cqn, cq);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c
index 18fccec72c5d..0e30602ef76d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c
@@ -202,7 +202,7 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
202 struct mlx5_eqe *eqe; 202 struct mlx5_eqe *eqe;
203 int eqes_found = 0; 203 int eqes_found = 0;
204 int set_ci = 0; 204 int set_ci = 0;
205 u32 cqn; 205 u32 cqn = -1;
206 u32 rsn; 206 u32 rsn;
207 u8 port; 207 u8 port;
208 208
@@ -320,6 +320,9 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
320 320
321 eq_update_ci(eq, 1); 321 eq_update_ci(eq, 1);
322 322
323 if (cqn != -1)
324 tasklet_schedule(&eq->tasklet_ctx.task);
325
323 return eqes_found; 326 return eqes_found;
324} 327}
325 328
@@ -403,6 +406,12 @@ int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
403 if (err) 406 if (err)
404 goto err_irq; 407 goto err_irq;
405 408
409 INIT_LIST_HEAD(&eq->tasklet_ctx.list);
410 INIT_LIST_HEAD(&eq->tasklet_ctx.process_list);
411 spin_lock_init(&eq->tasklet_ctx.lock);
412 tasklet_init(&eq->tasklet_ctx.task, mlx5_cq_tasklet_cb,
413 (unsigned long)&eq->tasklet_ctx);
414
406 /* EQs are created in ARMED state 415 /* EQs are created in ARMED state
407 */ 416 */
408 eq_update_ci(eq, 1); 417 eq_update_ci(eq, 1);
@@ -436,6 +445,7 @@ int mlx5_destroy_unmap_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
436 mlx5_core_warn(dev, "failed to destroy a previously created eq: eqn %d\n", 445 mlx5_core_warn(dev, "failed to destroy a previously created eq: eqn %d\n",
437 eq->eqn); 446 eq->eqn);
438 synchronize_irq(eq->irqn); 447 synchronize_irq(eq->irqn);
448 tasklet_disable(&eq->tasklet_ctx.task);
439 mlx5_buf_free(dev, &eq->buf); 449 mlx5_buf_free(dev, &eq->buf);
440 450
441 return err; 451 return err;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 6892746fd10d..aa98d0234bd1 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -660,6 +660,23 @@ int mlx5_vector2eqn(struct mlx5_core_dev *dev, int vector, int *eqn,
660} 660}
661EXPORT_SYMBOL(mlx5_vector2eqn); 661EXPORT_SYMBOL(mlx5_vector2eqn);
662 662
663struct mlx5_eq *mlx5_eqn2eq(struct mlx5_core_dev *dev, int eqn)
664{
665 struct mlx5_eq_table *table = &dev->priv.eq_table;
666 struct mlx5_eq *eq;
667
668 spin_lock(&table->lock);
669 list_for_each_entry(eq, &table->comp_eqs_list, list)
670 if (eq->eqn == eqn) {
671 spin_unlock(&table->lock);
672 return eq;
673 }
674
675 spin_unlock(&table->lock);
676
677 return ERR_PTR(-ENOENT);
678}
679
663static void free_comp_eqs(struct mlx5_core_dev *dev) 680static void free_comp_eqs(struct mlx5_core_dev *dev)
664{ 681{
665 struct mlx5_eq_table *table = &dev->priv.eq_table; 682 struct mlx5_eq_table *table = &dev->priv.eq_table;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
index 0b0b226c789e..f0d87046af8e 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
@@ -100,6 +100,8 @@ int mlx5_core_disable_hca(struct mlx5_core_dev *dev, u16 func_id);
100int mlx5_wait_for_vf_pages(struct mlx5_core_dev *dev); 100int mlx5_wait_for_vf_pages(struct mlx5_core_dev *dev);
101cycle_t mlx5_read_internal_timer(struct mlx5_core_dev *dev); 101cycle_t mlx5_read_internal_timer(struct mlx5_core_dev *dev);
102u32 mlx5_get_msix_vec(struct mlx5_core_dev *dev, int vecidx); 102u32 mlx5_get_msix_vec(struct mlx5_core_dev *dev, int vecidx);
103struct mlx5_eq *mlx5_eqn2eq(struct mlx5_core_dev *dev, int eqn);
104void mlx5_cq_tasklet_cb(unsigned long data);
103 105
104void mlx5e_init(void); 106void mlx5e_init(void);
105void mlx5e_cleanup(void); 107void mlx5e_cleanup(void);
diff --git a/include/linux/mlx5/cq.h b/include/linux/mlx5/cq.h
index b2c9fada8eac..2be976dd4966 100644
--- a/include/linux/mlx5/cq.h
+++ b/include/linux/mlx5/cq.h
@@ -53,6 +53,11 @@ struct mlx5_core_cq {
53 unsigned arm_sn; 53 unsigned arm_sn;
54 struct mlx5_rsc_debug *dbg; 54 struct mlx5_rsc_debug *dbg;
55 int pid; 55 int pid;
56 struct {
57 struct list_head list;
58 void (*comp)(struct mlx5_core_cq *);
59 void *priv;
60 } tasklet_ctx;
56}; 61};
57 62
58 63
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index 369c837d40f5..5a41f9003941 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -41,6 +41,7 @@
41#include <linux/slab.h> 41#include <linux/slab.h>
42#include <linux/vmalloc.h> 42#include <linux/vmalloc.h>
43#include <linux/radix-tree.h> 43#include <linux/radix-tree.h>
44#include <linux/interrupt.h>
44 45
45#include <linux/mlx5/device.h> 46#include <linux/mlx5/device.h>
46#include <linux/mlx5/doorbell.h> 47#include <linux/mlx5/doorbell.h>
@@ -304,6 +305,14 @@ struct mlx5_buf {
304 u8 page_shift; 305 u8 page_shift;
305}; 306};
306 307
308struct mlx5_eq_tasklet {
309 struct list_head list;
310 struct list_head process_list;
311 struct tasklet_struct task;
312 /* lock on completion tasklet list */
313 spinlock_t lock;
314};
315
307struct mlx5_eq { 316struct mlx5_eq {
308 struct mlx5_core_dev *dev; 317 struct mlx5_core_dev *dev;
309 __be32 __iomem *doorbell; 318 __be32 __iomem *doorbell;
@@ -317,6 +326,7 @@ struct mlx5_eq {
317 struct list_head list; 326 struct list_head list;
318 int index; 327 int index;
319 struct mlx5_rsc_debug *dbg; 328 struct mlx5_rsc_debug *dbg;
329 struct mlx5_eq_tasklet tasklet_ctx;
320}; 330};
321 331
322struct mlx5_core_psv { 332struct mlx5_core_psv {