net/bnxt: fix race between interrupt handler and dev config
[dpdk.git] / drivers / net / bnxt / bnxt_irq.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2014-2018 Broadcom
3  * All rights reserved.
4  */
5
6 #include <inttypes.h>
7
8 #include <rte_cycles.h>
9 #include <rte_malloc.h>
10
11 #include "bnxt.h"
12 #include "bnxt_irq.h"
13 #include "bnxt_ring.h"
14 #include "hsi_struct_def_dpdk.h"
15
16 /*
17  * Interrupts
18  */
19
20 void bnxt_int_handler(void *param)
21 {
22         struct rte_eth_dev *eth_dev = (struct rte_eth_dev *)param;
23         struct bnxt *bp = eth_dev->data->dev_private;
24         struct bnxt_cp_ring_info *cpr = bp->async_cp_ring;
25         struct cmpl_base *cmp;
26         uint32_t raw_cons;
27         uint32_t cons;
28
29         if (cpr == NULL)
30                 return;
31
32         raw_cons = cpr->cp_raw_cons;
33         pthread_mutex_lock(&bp->def_cp_lock);
34         while (1) {
35                 if (!cpr || !cpr->cp_ring_struct || !cpr->cp_db.doorbell) {
36                         pthread_mutex_unlock(&bp->def_cp_lock);
37                         return;
38                 }
39
40                 cons = RING_CMP(cpr->cp_ring_struct, raw_cons);
41                 cmp = &cpr->cp_desc_ring[cons];
42
43                 if (!CMP_VALID(cmp, raw_cons, cpr->cp_ring_struct))
44                         break;
45
46                 bnxt_event_hwrm_resp_handler(bp, cmp);
47                 raw_cons = NEXT_RAW_CMP(raw_cons);
48         }
49
50         cpr->cp_raw_cons = raw_cons;
51         if (BNXT_HAS_NQ(bp))
52                 bnxt_db_nq_arm(cpr);
53         else
54                 B_CP_DB_REARM(cpr, cpr->cp_raw_cons);
55
56         pthread_mutex_unlock(&bp->def_cp_lock);
57 }
58
59 int bnxt_free_int(struct bnxt *bp)
60 {
61         struct rte_intr_handle *intr_handle = &bp->pdev->intr_handle;
62         struct bnxt_irq *irq = bp->irq_tbl;
63         int rc = 0;
64
65         if (!irq)
66                 return 0;
67
68         if (irq->requested) {
69                 int count = 0;
70
71                 /*
72                  * Callback deregistration will fail with rc -EAGAIN if the
73                  * callback is currently active. Retry every 50 ms until
74                  * successful or 500 ms has elapsed.
75                  */
76                 do {
77                         rc = rte_intr_callback_unregister(intr_handle,
78                                                           irq->handler,
79                                                           bp->eth_dev);
80                         if (rc >= 0) {
81                                 irq->requested = 0;
82                                 break;
83                         }
84                         rte_delay_ms(50);
85                 } while (count++ < 10);
86
87                 if (rc < 0) {
88                         PMD_DRV_LOG(ERR, "irq cb unregister failed rc: %d\n",
89                                     rc);
90                         return rc;
91                 }
92         }
93
94         rte_free(bp->irq_tbl);
95         bp->irq_tbl = NULL;
96
97         return 0;
98 }
99
100 void bnxt_disable_int(struct bnxt *bp)
101 {
102         struct bnxt_cp_ring_info *cpr = bp->async_cp_ring;
103
104         if (BNXT_NUM_ASYNC_CPR(bp) == 0)
105                 return;
106
107         if (!cpr || !cpr->cp_db.doorbell)
108                 return;
109
110         /* Only the default completion ring */
111         if (BNXT_HAS_NQ(bp))
112                 bnxt_db_nq(cpr);
113         else
114                 B_CP_DB_DISARM(cpr);
115 }
116
117 void bnxt_enable_int(struct bnxt *bp)
118 {
119         struct bnxt_cp_ring_info *cpr = bp->async_cp_ring;
120
121         if (BNXT_NUM_ASYNC_CPR(bp) == 0)
122                 return;
123
124         if (!cpr || !cpr->cp_db.doorbell)
125                 return;
126
127         /* Only the default completion ring */
128         if (BNXT_HAS_NQ(bp))
129                 bnxt_db_nq_arm(cpr);
130         else
131                 B_CP_DB_ARM(cpr);
132 }
133
134 int bnxt_setup_int(struct bnxt *bp)
135 {
136         uint16_t total_vecs;
137         const int len = sizeof(bp->irq_tbl[0].name);
138         int i;
139
140         /* DPDK host only supports 1 MSI-X vector */
141         total_vecs = 1;
142         bp->irq_tbl = rte_calloc("bnxt_irq_tbl", total_vecs,
143                                  sizeof(struct bnxt_irq), 0);
144         if (bp->irq_tbl) {
145                 for (i = 0; i < total_vecs; i++) {
146                         bp->irq_tbl[i].vector = i;
147                         snprintf(bp->irq_tbl[i].name, len,
148                                  "%s-%d", bp->eth_dev->device->name, i);
149                         bp->irq_tbl[i].handler = bnxt_int_handler;
150                 }
151         } else {
152                 PMD_DRV_LOG(ERR, "bnxt_irq_tbl setup failed\n");
153                 return -ENOMEM;
154         }
155
156         return 0;
157 }
158
159 int bnxt_request_int(struct bnxt *bp)
160 {
161         struct rte_intr_handle *intr_handle = &bp->pdev->intr_handle;
162         struct bnxt_irq *irq = bp->irq_tbl;
163         int rc = 0;
164
165         if (!irq)
166                 return 0;
167
168         if (!irq->requested) {
169                 rc = rte_intr_callback_register(intr_handle,
170                                                 irq->handler,
171                                                 bp->eth_dev);
172                 if (!rc)
173                         irq->requested = 1;
174         }
175
176         return rc;
177 }