Skip to content

Commit 2fdf340

Browse files
Junxian Huangrleon
authored andcommitted
RDMA/hns: Fix soft lockup under heavy CEQE load
CEQEs are handled in interrupt handler currently. This may cause the CPU core staying in interrupt context too long and lead to soft lockup under heavy load. Handle CEQEs in BH workqueue and set an upper limit for the number of CEQE handled by a single call of work handler. Fixes: a5073d6 ("RDMA/hns: Add eq support of hip08") Signed-off-by: Junxian Huang <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Leon Romanovsky <[email protected]>
1 parent 6afa2c0 commit 2fdf340

File tree

2 files changed

+54
-36
lines changed

2 files changed

+54
-36
lines changed

drivers/infiniband/hw/hns/hns_roce_device.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,7 @@ struct hns_roce_eq {
717717
int shift;
718718
int event_type;
719719
int sub_type;
720+
struct work_struct work;
720721
};
721722

722723
struct hns_roce_eq_table {

drivers/infiniband/hw/hns/hns_roce_hw_v2.c

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <linux/iopoll.h>
3737
#include <linux/kernel.h>
3838
#include <linux/types.h>
39+
#include <linux/workqueue.h>
3940
#include <net/addrconf.h>
4041
#include <rdma/ib_addr.h>
4142
#include <rdma/ib_cache.h>
@@ -6140,33 +6141,11 @@ static struct hns_roce_ceqe *next_ceqe_sw_v2(struct hns_roce_eq *eq)
61406141
!!(eq->cons_index & eq->entries)) ? ceqe : NULL;
61416142
}
61426143

6143-
static irqreturn_t hns_roce_v2_ceq_int(struct hns_roce_dev *hr_dev,
6144-
struct hns_roce_eq *eq)
6144+
static irqreturn_t hns_roce_v2_ceq_int(struct hns_roce_eq *eq)
61456145
{
6146-
struct hns_roce_ceqe *ceqe = next_ceqe_sw_v2(eq);
6147-
irqreturn_t ceqe_found = IRQ_NONE;
6148-
u32 cqn;
6149-
6150-
while (ceqe) {
6151-
/* Make sure we read CEQ entry after we have checked the
6152-
* ownership bit
6153-
*/
6154-
dma_rmb();
6155-
6156-
cqn = hr_reg_read(ceqe, CEQE_CQN);
6157-
6158-
hns_roce_cq_completion(hr_dev, cqn);
6159-
6160-
++eq->cons_index;
6161-
ceqe_found = IRQ_HANDLED;
6162-
atomic64_inc(&hr_dev->dfx_cnt[HNS_ROCE_DFX_CEQE_CNT]);
6163-
6164-
ceqe = next_ceqe_sw_v2(eq);
6165-
}
6146+
queue_work(system_bh_wq, &eq->work);
61666147

6167-
update_eq_db(eq);
6168-
6169-
return IRQ_RETVAL(ceqe_found);
6148+
return IRQ_HANDLED;
61706149
}
61716150

61726151
static irqreturn_t hns_roce_v2_msix_interrupt_eq(int irq, void *eq_ptr)
@@ -6177,7 +6156,7 @@ static irqreturn_t hns_roce_v2_msix_interrupt_eq(int irq, void *eq_ptr)
61776156

61786157
if (eq->type_flag == HNS_ROCE_CEQ)
61796158
/* Completion event interrupt */
6180-
int_work = hns_roce_v2_ceq_int(hr_dev, eq);
6159+
int_work = hns_roce_v2_ceq_int(eq);
61816160
else
61826161
/* Asynchronous event interrupt */
61836162
int_work = hns_roce_v2_aeq_int(hr_dev, eq);
@@ -6545,6 +6524,34 @@ static int hns_roce_v2_create_eq(struct hns_roce_dev *hr_dev,
65456524
return ret;
65466525
}
65476526

6527+
static void hns_roce_ceq_work(struct work_struct *work)
6528+
{
6529+
struct hns_roce_eq *eq = from_work(eq, work, work);
6530+
struct hns_roce_ceqe *ceqe = next_ceqe_sw_v2(eq);
6531+
struct hns_roce_dev *hr_dev = eq->hr_dev;
6532+
int ceqe_num = 0;
6533+
u32 cqn;
6534+
6535+
while (ceqe && ceqe_num < hr_dev->caps.ceqe_depth) {
6536+
/* Make sure we read CEQ entry after we have checked the
6537+
* ownership bit
6538+
*/
6539+
dma_rmb();
6540+
6541+
cqn = hr_reg_read(ceqe, CEQE_CQN);
6542+
6543+
hns_roce_cq_completion(hr_dev, cqn);
6544+
6545+
++eq->cons_index;
6546+
++ceqe_num;
6547+
atomic64_inc(&hr_dev->dfx_cnt[HNS_ROCE_DFX_CEQE_CNT]);
6548+
6549+
ceqe = next_ceqe_sw_v2(eq);
6550+
}
6551+
6552+
update_eq_db(eq);
6553+
}
6554+
65486555
static int __hns_roce_request_irq(struct hns_roce_dev *hr_dev, int irq_num,
65496556
int comp_num, int aeq_num, int other_num)
65506557
{
@@ -6576,21 +6583,24 @@ static int __hns_roce_request_irq(struct hns_roce_dev *hr_dev, int irq_num,
65766583
j - other_num - aeq_num);
65776584

65786585
for (j = 0; j < irq_num; j++) {
6579-
if (j < other_num)
6586+
if (j < other_num) {
65806587
ret = request_irq(hr_dev->irq[j],
65816588
hns_roce_v2_msix_interrupt_abn,
65826589
0, hr_dev->irq_names[j], hr_dev);
6583-
6584-
else if (j < (other_num + comp_num))
6590+
} else if (j < (other_num + comp_num)) {
6591+
INIT_WORK(&eq_table->eq[j - other_num].work,
6592+
hns_roce_ceq_work);
65856593
ret = request_irq(eq_table->eq[j - other_num].irq,
65866594
hns_roce_v2_msix_interrupt_eq,
65876595
0, hr_dev->irq_names[j + aeq_num],
65886596
&eq_table->eq[j - other_num]);
6589-
else
6597+
} else {
65906598
ret = request_irq(eq_table->eq[j - other_num].irq,
65916599
hns_roce_v2_msix_interrupt_eq,
65926600
0, hr_dev->irq_names[j - comp_num],
65936601
&eq_table->eq[j - other_num]);
6602+
}
6603+
65946604
if (ret) {
65956605
dev_err(hr_dev->dev, "request irq error!\n");
65966606
goto err_request_failed;
@@ -6600,12 +6610,16 @@ static int __hns_roce_request_irq(struct hns_roce_dev *hr_dev, int irq_num,
66006610
return 0;
66016611

66026612
err_request_failed:
6603-
for (j -= 1; j >= 0; j--)
6604-
if (j < other_num)
6613+
for (j -= 1; j >= 0; j--) {
6614+
if (j < other_num) {
66056615
free_irq(hr_dev->irq[j], hr_dev);
6606-
else
6607-
free_irq(eq_table->eq[j - other_num].irq,
6608-
&eq_table->eq[j - other_num]);
6616+
continue;
6617+
}
6618+
free_irq(eq_table->eq[j - other_num].irq,
6619+
&eq_table->eq[j - other_num]);
6620+
if (j < other_num + comp_num)
6621+
cancel_work_sync(&eq_table->eq[j - other_num].work);
6622+
}
66096623

66106624
err_kzalloc_failed:
66116625
for (i -= 1; i >= 0; i--)
@@ -6626,8 +6640,11 @@ static void __hns_roce_free_irq(struct hns_roce_dev *hr_dev)
66266640
for (i = 0; i < hr_dev->caps.num_other_vectors; i++)
66276641
free_irq(hr_dev->irq[i], hr_dev);
66286642

6629-
for (i = 0; i < eq_num; i++)
6643+
for (i = 0; i < eq_num; i++) {
66306644
free_irq(hr_dev->eq_table.eq[i].irq, &hr_dev->eq_table.eq[i]);
6645+
if (i < hr_dev->caps.num_comp_vectors)
6646+
cancel_work_sync(&hr_dev->eq_table.eq[i].work);
6647+
}
66316648

66326649
for (i = 0; i < irq_num; i++)
66336650
kfree(hr_dev->irq_names[i]);

0 commit comments

Comments
 (0)