Commit 39ad6eea authored by Sunil Goutham's avatar Sunil Goutham Committed by David S. Miller

net: thunderx: Rework interrupt handling

Rework interrupt handler to avoid checking IRQ affinity of
CQ interrupts. Now separate handlers are registered for each IRQ
including RBDR. Register interrupt handlers for only those
which are being used. Add nicvf_dump_intr_status() and use it
in irq handlers.
Signed-off-by: default avatarSunil Goutham <sgoutham@cavium.com>
Signed-off-by: default avatarAleksey Makarov <aleksey.makarov@caviumnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent aa2e259b
...@@ -135,6 +135,7 @@ ...@@ -135,6 +135,7 @@
#define NICVF_TX_TIMEOUT (50 * HZ) #define NICVF_TX_TIMEOUT (50 * HZ)
struct nicvf_cq_poll { struct nicvf_cq_poll {
struct nicvf *nicvf;
u8 cq_idx; /* Completion queue index */ u8 cq_idx; /* Completion queue index */
struct napi_struct napi; struct napi_struct napi;
}; };
......
...@@ -653,11 +653,20 @@ static void nicvf_handle_qs_err(unsigned long data) ...@@ -653,11 +653,20 @@ static void nicvf_handle_qs_err(unsigned long data)
nicvf_enable_intr(nic, NICVF_INTR_QS_ERR, 0); nicvf_enable_intr(nic, NICVF_INTR_QS_ERR, 0);
} }
static void nicvf_dump_intr_status(struct nicvf *nic)
{
if (netif_msg_intr(nic))
netdev_info(nic->netdev, "%s: interrupt status 0x%llx\n",
nic->netdev->name, nicvf_reg_read(nic, NIC_VF_INT));
}
static irqreturn_t nicvf_misc_intr_handler(int irq, void *nicvf_irq) static irqreturn_t nicvf_misc_intr_handler(int irq, void *nicvf_irq)
{ {
struct nicvf *nic = (struct nicvf *)nicvf_irq; struct nicvf *nic = (struct nicvf *)nicvf_irq;
u64 intr; u64 intr;
nicvf_dump_intr_status(nic);
intr = nicvf_reg_read(nic, NIC_VF_INT); intr = nicvf_reg_read(nic, NIC_VF_INT);
/* Check for spurious interrupt */ /* Check for spurious interrupt */
if (!(intr & NICVF_INTR_MBOX_MASK)) if (!(intr & NICVF_INTR_MBOX_MASK))
...@@ -668,59 +677,58 @@ static irqreturn_t nicvf_misc_intr_handler(int irq, void *nicvf_irq) ...@@ -668,59 +677,58 @@ static irqreturn_t nicvf_misc_intr_handler(int irq, void *nicvf_irq)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static irqreturn_t nicvf_intr_handler(int irq, void *nicvf_irq) static irqreturn_t nicvf_intr_handler(int irq, void *cq_irq)
{ {
u64 qidx, intr, clear_intr = 0; struct nicvf_cq_poll *cq_poll = (struct nicvf_cq_poll *)cq_irq;
u64 cq_intr, rbdr_intr, qs_err_intr; struct nicvf *nic = cq_poll->nicvf;
struct nicvf *nic = (struct nicvf *)nicvf_irq; int qidx = cq_poll->cq_idx;
struct queue_set *qs = nic->qs;
struct nicvf_cq_poll *cq_poll = NULL;
intr = nicvf_reg_read(nic, NIC_VF_INT); nicvf_dump_intr_status(nic);
if (netif_msg_intr(nic))
netdev_info(nic->netdev, "%s: interrupt status 0x%llx\n",
nic->netdev->name, intr);
qs_err_intr = intr & NICVF_INTR_QS_ERR_MASK;
if (qs_err_intr) {
/* Disable Qset err interrupt and schedule softirq */
nicvf_disable_intr(nic, NICVF_INTR_QS_ERR, 0);
tasklet_hi_schedule(&nic->qs_err_task);
clear_intr |= qs_err_intr;
}
/* Disable interrupts and start polling */
cq_intr = (intr & NICVF_INTR_CQ_MASK) >> NICVF_INTR_CQ_SHIFT;
for (qidx = 0; qidx < qs->cq_cnt; qidx++) {
if (!(cq_intr & (1 << qidx)))
continue;
if (!nicvf_is_intr_enabled(nic, NICVF_INTR_CQ, qidx))
continue;
/* Disable interrupts */
nicvf_disable_intr(nic, NICVF_INTR_CQ, qidx); nicvf_disable_intr(nic, NICVF_INTR_CQ, qidx);
clear_intr |= ((1 << qidx) << NICVF_INTR_CQ_SHIFT);
cq_poll = nic->napi[qidx];
/* Schedule NAPI */ /* Schedule NAPI */
if (cq_poll)
napi_schedule(&cq_poll->napi); napi_schedule(&cq_poll->napi);
}
/* Handle RBDR interrupts */ /* Clear interrupt */
rbdr_intr = (intr & NICVF_INTR_RBDR_MASK) >> NICVF_INTR_RBDR_SHIFT; nicvf_clear_intr(nic, NICVF_INTR_CQ, qidx);
if (rbdr_intr) {
return IRQ_HANDLED;
}
static irqreturn_t nicvf_rbdr_intr_handler(int irq, void *nicvf_irq)
{
struct nicvf *nic = (struct nicvf *)nicvf_irq;
u8 qidx;
nicvf_dump_intr_status(nic);
/* Disable RBDR interrupt and schedule softirq */ /* Disable RBDR interrupt and schedule softirq */
for (qidx = 0; qidx < qs->rbdr_cnt; qidx++) { for (qidx = 0; qidx < nic->qs->rbdr_cnt; qidx++) {
if (!nicvf_is_intr_enabled(nic, NICVF_INTR_RBDR, qidx)) if (!nicvf_is_intr_enabled(nic, NICVF_INTR_RBDR, qidx))
continue; continue;
nicvf_disable_intr(nic, NICVF_INTR_RBDR, qidx); nicvf_disable_intr(nic, NICVF_INTR_RBDR, qidx);
tasklet_hi_schedule(&nic->rbdr_task); tasklet_hi_schedule(&nic->rbdr_task);
clear_intr |= ((1 << qidx) << NICVF_INTR_RBDR_SHIFT); /* Clear interrupt */
} nicvf_clear_intr(nic, NICVF_INTR_RBDR, qidx);
} }
/* Clear interrupts */ return IRQ_HANDLED;
nicvf_reg_write(nic, NIC_VF_INT, clear_intr); }
static irqreturn_t nicvf_qs_err_intr_handler(int irq, void *nicvf_irq)
{
struct nicvf *nic = (struct nicvf *)nicvf_irq;
nicvf_dump_intr_status(nic);
/* Disable Qset err interrupt and schedule softirq */
nicvf_disable_intr(nic, NICVF_INTR_QS_ERR, 0);
tasklet_hi_schedule(&nic->qs_err_task);
nicvf_clear_intr(nic, NICVF_INTR_QS_ERR, 0);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -754,7 +762,7 @@ static void nicvf_disable_msix(struct nicvf *nic) ...@@ -754,7 +762,7 @@ static void nicvf_disable_msix(struct nicvf *nic)
static int nicvf_register_interrupts(struct nicvf *nic) static int nicvf_register_interrupts(struct nicvf *nic)
{ {
int irq, free, ret = 0; int irq, ret = 0;
int vector; int vector;
for_each_cq_irq(irq) for_each_cq_irq(irq)
...@@ -769,44 +777,42 @@ static int nicvf_register_interrupts(struct nicvf *nic) ...@@ -769,44 +777,42 @@ static int nicvf_register_interrupts(struct nicvf *nic)
sprintf(nic->irq_name[irq], "NICVF%d RBDR%d", sprintf(nic->irq_name[irq], "NICVF%d RBDR%d",
nic->vf_id, irq - NICVF_INTR_ID_RBDR); nic->vf_id, irq - NICVF_INTR_ID_RBDR);
/* Register all interrupts except mailbox */ /* Register CQ interrupts */
for (irq = 0; irq < NICVF_INTR_ID_SQ; irq++) { for (irq = 0; irq < nic->qs->cq_cnt; irq++) {
vector = nic->msix_entries[irq].vector; vector = nic->msix_entries[irq].vector;
ret = request_irq(vector, nicvf_intr_handler, ret = request_irq(vector, nicvf_intr_handler,
0, nic->irq_name[irq], nic); 0, nic->irq_name[irq], nic->napi[irq]);
if (ret) if (ret)
break; goto err;
nic->irq_allocated[irq] = true; nic->irq_allocated[irq] = true;
} }
for (irq = NICVF_INTR_ID_SQ; irq < NICVF_INTR_ID_MISC; irq++) { /* Register RBDR interrupt */
for (irq = NICVF_INTR_ID_RBDR;
irq < (NICVF_INTR_ID_RBDR + nic->qs->rbdr_cnt); irq++) {
vector = nic->msix_entries[irq].vector; vector = nic->msix_entries[irq].vector;
ret = request_irq(vector, nicvf_intr_handler, ret = request_irq(vector, nicvf_rbdr_intr_handler,
0, nic->irq_name[irq], nic); 0, nic->irq_name[irq], nic);
if (ret) if (ret)
break; goto err;
nic->irq_allocated[irq] = true; nic->irq_allocated[irq] = true;
} }
/* Register QS error interrupt */
sprintf(nic->irq_name[NICVF_INTR_ID_QS_ERR], sprintf(nic->irq_name[NICVF_INTR_ID_QS_ERR],
"NICVF%d Qset error", nic->vf_id); "NICVF%d Qset error", nic->vf_id);
if (!ret) {
vector = nic->msix_entries[NICVF_INTR_ID_QS_ERR].vector;
irq = NICVF_INTR_ID_QS_ERR; irq = NICVF_INTR_ID_QS_ERR;
ret = request_irq(vector, nicvf_intr_handler, ret = request_irq(nic->msix_entries[irq].vector,
nicvf_qs_err_intr_handler,
0, nic->irq_name[irq], nic); 0, nic->irq_name[irq], nic);
if (!ret) if (!ret)
nic->irq_allocated[irq] = true; nic->irq_allocated[irq] = true;
}
if (ret) { err:
netdev_err(nic->netdev, "Request irq failed\n"); if (ret)
for (free = 0; free < irq; free++) netdev_err(nic->netdev, "request_irq failed, vector %d\n", irq);
free_irq(nic->msix_entries[free].vector, nic);
return ret;
}
return 0; return ret;
} }
static void nicvf_unregister_interrupts(struct nicvf *nic) static void nicvf_unregister_interrupts(struct nicvf *nic)
...@@ -815,8 +821,14 @@ static void nicvf_unregister_interrupts(struct nicvf *nic) ...@@ -815,8 +821,14 @@ static void nicvf_unregister_interrupts(struct nicvf *nic)
/* Free registered interrupts */ /* Free registered interrupts */
for (irq = 0; irq < nic->num_vec; irq++) { for (irq = 0; irq < nic->num_vec; irq++) {
if (nic->irq_allocated[irq]) if (!nic->irq_allocated[irq])
continue;
if (irq < NICVF_INTR_ID_SQ)
free_irq(nic->msix_entries[irq].vector, nic->napi[irq]);
else
free_irq(nic->msix_entries[irq].vector, nic); free_irq(nic->msix_entries[irq].vector, nic);
nic->irq_allocated[irq] = false; nic->irq_allocated[irq] = false;
} }
...@@ -888,6 +900,20 @@ static netdev_tx_t nicvf_xmit(struct sk_buff *skb, struct net_device *netdev) ...@@ -888,6 +900,20 @@ static netdev_tx_t nicvf_xmit(struct sk_buff *skb, struct net_device *netdev)
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
static inline void nicvf_free_cq_poll(struct nicvf *nic)
{
struct nicvf_cq_poll *cq_poll;
int qidx;
for (qidx = 0; qidx < nic->qs->cq_cnt; qidx++) {
cq_poll = nic->napi[qidx];
if (!cq_poll)
continue;
nic->napi[qidx] = NULL;
kfree(cq_poll);
}
}
int nicvf_stop(struct net_device *netdev) int nicvf_stop(struct net_device *netdev)
{ {
int irq, qidx; int irq, qidx;
...@@ -922,7 +948,6 @@ int nicvf_stop(struct net_device *netdev) ...@@ -922,7 +948,6 @@ int nicvf_stop(struct net_device *netdev)
cq_poll = nic->napi[qidx]; cq_poll = nic->napi[qidx];
if (!cq_poll) if (!cq_poll)
continue; continue;
nic->napi[qidx] = NULL;
napi_synchronize(&cq_poll->napi); napi_synchronize(&cq_poll->napi);
/* CQ intr is enabled while napi_complete, /* CQ intr is enabled while napi_complete,
* so disable it now * so disable it now
...@@ -931,7 +956,6 @@ int nicvf_stop(struct net_device *netdev) ...@@ -931,7 +956,6 @@ int nicvf_stop(struct net_device *netdev)
nicvf_clear_intr(nic, NICVF_INTR_CQ, qidx); nicvf_clear_intr(nic, NICVF_INTR_CQ, qidx);
napi_disable(&cq_poll->napi); napi_disable(&cq_poll->napi);
netif_napi_del(&cq_poll->napi); netif_napi_del(&cq_poll->napi);
kfree(cq_poll);
} }
netif_tx_disable(netdev); netif_tx_disable(netdev);
...@@ -947,6 +971,8 @@ int nicvf_stop(struct net_device *netdev) ...@@ -947,6 +971,8 @@ int nicvf_stop(struct net_device *netdev)
nicvf_unregister_interrupts(nic); nicvf_unregister_interrupts(nic);
nicvf_free_cq_poll(nic);
return 0; return 0;
} }
...@@ -973,6 +999,7 @@ int nicvf_open(struct net_device *netdev) ...@@ -973,6 +999,7 @@ int nicvf_open(struct net_device *netdev)
goto napi_del; goto napi_del;
} }
cq_poll->cq_idx = qidx; cq_poll->cq_idx = qidx;
cq_poll->nicvf = nic;
netif_napi_add(netdev, &cq_poll->napi, nicvf_poll, netif_napi_add(netdev, &cq_poll->napi, nicvf_poll,
NAPI_POLL_WEIGHT); NAPI_POLL_WEIGHT);
napi_enable(&cq_poll->napi); napi_enable(&cq_poll->napi);
...@@ -1040,6 +1067,8 @@ int nicvf_open(struct net_device *netdev) ...@@ -1040,6 +1067,8 @@ int nicvf_open(struct net_device *netdev)
cleanup: cleanup:
nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0); nicvf_disable_intr(nic, NICVF_INTR_MBOX, 0);
nicvf_unregister_interrupts(nic); nicvf_unregister_interrupts(nic);
tasklet_kill(&nic->qs_err_task);
tasklet_kill(&nic->rbdr_task);
napi_del: napi_del:
for (qidx = 0; qidx < qs->cq_cnt; qidx++) { for (qidx = 0; qidx < qs->cq_cnt; qidx++) {
cq_poll = nic->napi[qidx]; cq_poll = nic->napi[qidx];
...@@ -1047,9 +1076,8 @@ int nicvf_open(struct net_device *netdev) ...@@ -1047,9 +1076,8 @@ int nicvf_open(struct net_device *netdev)
continue; continue;
napi_disable(&cq_poll->napi); napi_disable(&cq_poll->napi);
netif_napi_del(&cq_poll->napi); netif_napi_del(&cq_poll->napi);
kfree(cq_poll);
nic->napi[qidx] = NULL;
} }
nicvf_free_cq_poll(nic);
return err; return err;
} }
......
...@@ -251,6 +251,7 @@ struct cmp_queue { ...@@ -251,6 +251,7 @@ struct cmp_queue {
void *desc; void *desc;
struct q_desc_mem dmem; struct q_desc_mem dmem;
struct cmp_queue_stats stats; struct cmp_queue_stats stats;
int irq;
} ____cacheline_aligned_in_smp; } ____cacheline_aligned_in_smp;
struct snd_queue { struct snd_queue {
......
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