]>
Commit | Line | Data |
---|---|---|
9f95a23c | 1 | /* SPDX-License-Identifier: BSD-3-Clause |
11fdf7f2 | 2 | * |
9f95a23c | 3 | * Copyright (c) 2016-2018 Solarflare Communications Inc. |
11fdf7f2 TL |
4 | * All rights reserved. |
5 | * | |
6 | * This software was jointly developed between OKTET Labs (under contract | |
7 | * for Solarflare) and Solarflare Communications, Inc. | |
11fdf7f2 TL |
8 | */ |
9 | ||
10 | /* | |
11 | * At the momemt of writing DPDK v16.07 has notion of two types of | |
12 | * interrupts: LSC (link status change) and RXQ (receive indication). | |
13 | * It allows to register interrupt callback for entire device which is | |
14 | * not intended to be used for receive indication (i.e. link status | |
15 | * change indication only). The handler has no information which HW | |
16 | * interrupt has triggered it, so we don't know which event queue should | |
17 | * be polled/reprimed (except qmask in the case of legacy line interrupt). | |
18 | */ | |
19 | ||
20 | #include <rte_common.h> | |
21 | #include <rte_interrupts.h> | |
22 | ||
23 | #include "efx.h" | |
24 | ||
25 | #include "sfc.h" | |
26 | #include "sfc_log.h" | |
27 | #include "sfc_ev.h" | |
28 | ||
29 | static void | |
30 | sfc_intr_handle_mgmt_evq(struct sfc_adapter *sa) | |
31 | { | |
32 | struct sfc_evq *evq; | |
33 | ||
34 | rte_spinlock_lock(&sa->mgmt_evq_lock); | |
35 | ||
36 | evq = sa->mgmt_evq; | |
37 | ||
9f95a23c TL |
38 | if (!sa->mgmt_evq_running) { |
39 | sfc_log_init(sa, "interrupt on not running management EVQ %u", | |
40 | evq->evq_index); | |
11fdf7f2 TL |
41 | } else { |
42 | sfc_ev_qpoll(evq); | |
43 | ||
44 | if (sfc_ev_qprime(evq) != 0) | |
45 | sfc_err(sa, "cannot prime EVQ %u", evq->evq_index); | |
46 | } | |
47 | ||
48 | rte_spinlock_unlock(&sa->mgmt_evq_lock); | |
49 | } | |
50 | ||
51 | static void | |
52 | sfc_intr_line_handler(void *cb_arg) | |
53 | { | |
54 | struct sfc_adapter *sa = (struct sfc_adapter *)cb_arg; | |
55 | efx_nic_t *enp = sa->nic; | |
56 | boolean_t fatal; | |
57 | uint32_t qmask; | |
58 | unsigned int lsc_seq = sa->port.lsc_seq; | |
9f95a23c | 59 | struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(sa->eth_dev); |
11fdf7f2 TL |
60 | |
61 | sfc_log_init(sa, "entry"); | |
62 | ||
63 | if (sa->state != SFC_ADAPTER_STARTED && | |
64 | sa->state != SFC_ADAPTER_STARTING && | |
65 | sa->state != SFC_ADAPTER_STOPPING) { | |
66 | sfc_log_init(sa, | |
67 | "interrupt on stopped adapter, don't reenable"); | |
68 | goto exit; | |
69 | } | |
70 | ||
71 | efx_intr_status_line(enp, &fatal, &qmask); | |
72 | if (fatal) { | |
73 | (void)efx_intr_disable(enp); | |
74 | (void)efx_intr_fatal(enp); | |
75 | sfc_err(sa, "fatal, interrupts disabled"); | |
76 | goto exit; | |
77 | } | |
78 | ||
79 | if (qmask & (1 << sa->mgmt_evq_index)) | |
80 | sfc_intr_handle_mgmt_evq(sa); | |
81 | ||
82 | if (rte_intr_enable(&pci_dev->intr_handle) != 0) | |
83 | sfc_err(sa, "cannot reenable interrupts"); | |
84 | ||
85 | sfc_log_init(sa, "done"); | |
86 | ||
87 | exit: | |
88 | if (lsc_seq != sa->port.lsc_seq) { | |
9f95a23c | 89 | sfc_notice(sa, "link status change event: link %s", |
11fdf7f2 TL |
90 | sa->eth_dev->data->dev_link.link_status ? |
91 | "UP" : "DOWN"); | |
92 | _rte_eth_dev_callback_process(sa->eth_dev, | |
9f95a23c TL |
93 | RTE_ETH_EVENT_INTR_LSC, |
94 | NULL); | |
11fdf7f2 TL |
95 | } |
96 | } | |
97 | ||
98 | static void | |
99 | sfc_intr_message_handler(void *cb_arg) | |
100 | { | |
101 | struct sfc_adapter *sa = (struct sfc_adapter *)cb_arg; | |
102 | efx_nic_t *enp = sa->nic; | |
103 | boolean_t fatal; | |
104 | unsigned int lsc_seq = sa->port.lsc_seq; | |
9f95a23c | 105 | struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(sa->eth_dev); |
11fdf7f2 TL |
106 | |
107 | sfc_log_init(sa, "entry"); | |
108 | ||
109 | if (sa->state != SFC_ADAPTER_STARTED && | |
110 | sa->state != SFC_ADAPTER_STARTING && | |
111 | sa->state != SFC_ADAPTER_STOPPING) { | |
112 | sfc_log_init(sa, "adapter not-started, don't reenable"); | |
113 | goto exit; | |
114 | } | |
115 | ||
116 | efx_intr_status_message(enp, sa->mgmt_evq_index, &fatal); | |
117 | if (fatal) { | |
118 | (void)efx_intr_disable(enp); | |
119 | (void)efx_intr_fatal(enp); | |
120 | sfc_err(sa, "fatal, interrupts disabled"); | |
121 | goto exit; | |
122 | } | |
123 | ||
124 | sfc_intr_handle_mgmt_evq(sa); | |
125 | ||
126 | if (rte_intr_enable(&pci_dev->intr_handle) != 0) | |
127 | sfc_err(sa, "cannot reenable interrupts"); | |
128 | ||
129 | sfc_log_init(sa, "done"); | |
130 | ||
131 | exit: | |
132 | if (lsc_seq != sa->port.lsc_seq) { | |
9f95a23c | 133 | sfc_notice(sa, "link status change event"); |
11fdf7f2 | 134 | _rte_eth_dev_callback_process(sa->eth_dev, |
9f95a23c TL |
135 | RTE_ETH_EVENT_INTR_LSC, |
136 | NULL); | |
11fdf7f2 TL |
137 | } |
138 | } | |
139 | ||
140 | int | |
141 | sfc_intr_start(struct sfc_adapter *sa) | |
142 | { | |
143 | struct sfc_intr *intr = &sa->intr; | |
144 | struct rte_intr_handle *intr_handle; | |
145 | struct rte_pci_device *pci_dev; | |
146 | int rc; | |
147 | ||
148 | sfc_log_init(sa, "entry"); | |
149 | ||
150 | /* | |
151 | * The EFX common code event queue module depends on the interrupt | |
152 | * module. Ensure that the interrupt module is always initialized | |
153 | * (even if interrupts are not used). Status memory is required | |
154 | * for Siena only and may be NULL for EF10. | |
155 | */ | |
156 | sfc_log_init(sa, "efx_intr_init"); | |
157 | rc = efx_intr_init(sa->nic, intr->type, NULL); | |
158 | if (rc != 0) | |
159 | goto fail_intr_init; | |
160 | ||
9f95a23c | 161 | pci_dev = RTE_ETH_DEV_TO_PCI(sa->eth_dev); |
11fdf7f2 TL |
162 | intr_handle = &pci_dev->intr_handle; |
163 | ||
164 | if (intr->handler != NULL) { | |
165 | sfc_log_init(sa, "rte_intr_callback_register"); | |
166 | rc = rte_intr_callback_register(intr_handle, intr->handler, | |
167 | (void *)sa); | |
168 | if (rc != 0) { | |
169 | sfc_err(sa, | |
170 | "cannot register interrupt handler (rc=%d)", | |
171 | rc); | |
172 | /* | |
173 | * Convert error code from negative returned by RTE API | |
174 | * to positive used in the driver. | |
175 | */ | |
176 | rc = -rc; | |
177 | goto fail_rte_intr_cb_reg; | |
178 | } | |
179 | ||
180 | sfc_log_init(sa, "rte_intr_enable"); | |
181 | rc = rte_intr_enable(intr_handle); | |
182 | if (rc != 0) { | |
183 | sfc_err(sa, "cannot enable interrupts (rc=%d)", rc); | |
184 | /* | |
185 | * Convert error code from negative returned by RTE API | |
186 | * to positive used in the driver. | |
187 | */ | |
188 | rc = -rc; | |
189 | goto fail_rte_intr_enable; | |
190 | } | |
191 | ||
192 | sfc_log_init(sa, "efx_intr_enable"); | |
193 | efx_intr_enable(sa->nic); | |
194 | } | |
195 | ||
196 | sfc_log_init(sa, "done type=%u max_intr=%d nb_efd=%u vec=%p", | |
197 | intr_handle->type, intr_handle->max_intr, | |
198 | intr_handle->nb_efd, intr_handle->intr_vec); | |
199 | return 0; | |
200 | ||
201 | fail_rte_intr_enable: | |
202 | rte_intr_callback_unregister(intr_handle, intr->handler, (void *)sa); | |
203 | ||
204 | fail_rte_intr_cb_reg: | |
205 | efx_intr_fini(sa->nic); | |
206 | ||
207 | fail_intr_init: | |
208 | sfc_log_init(sa, "failed %d", rc); | |
209 | return rc; | |
210 | } | |
211 | ||
212 | void | |
213 | sfc_intr_stop(struct sfc_adapter *sa) | |
214 | { | |
215 | struct sfc_intr *intr = &sa->intr; | |
9f95a23c | 216 | struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(sa->eth_dev); |
11fdf7f2 TL |
217 | |
218 | sfc_log_init(sa, "entry"); | |
219 | ||
220 | if (intr->handler != NULL) { | |
221 | struct rte_intr_handle *intr_handle; | |
222 | int rc; | |
223 | ||
224 | efx_intr_disable(sa->nic); | |
225 | ||
226 | intr_handle = &pci_dev->intr_handle; | |
227 | if (rte_intr_disable(intr_handle) != 0) | |
228 | sfc_err(sa, "cannot disable interrupts"); | |
229 | ||
230 | while ((rc = rte_intr_callback_unregister(intr_handle, | |
231 | intr->handler, (void *)sa)) == -EAGAIN) | |
232 | ; | |
233 | if (rc != 1) | |
234 | sfc_err(sa, | |
235 | "cannot unregister interrupt handler %d", | |
236 | rc); | |
237 | } | |
238 | ||
239 | efx_intr_fini(sa->nic); | |
240 | ||
241 | sfc_log_init(sa, "done"); | |
242 | } | |
243 | ||
244 | int | |
245 | sfc_intr_configure(struct sfc_adapter *sa) | |
246 | { | |
247 | struct sfc_intr *intr = &sa->intr; | |
248 | ||
249 | sfc_log_init(sa, "entry"); | |
250 | ||
251 | intr->handler = NULL; | |
252 | intr->lsc_intr = (sa->eth_dev->data->dev_conf.intr_conf.lsc != 0); | |
253 | if (!intr->lsc_intr) { | |
9f95a23c | 254 | sfc_notice(sa, "LSC tracking using interrupts is disabled"); |
11fdf7f2 TL |
255 | goto done; |
256 | } | |
257 | ||
258 | switch (intr->type) { | |
259 | case EFX_INTR_MESSAGE: | |
260 | intr->handler = sfc_intr_message_handler; | |
261 | break; | |
262 | case EFX_INTR_LINE: | |
263 | intr->handler = sfc_intr_line_handler; | |
264 | break; | |
265 | case EFX_INTR_INVALID: | |
266 | sfc_warn(sa, "interrupts are not supported"); | |
267 | break; | |
268 | default: | |
269 | sfc_panic(sa, "unexpected EFX interrupt type %u\n", intr->type); | |
270 | break; | |
271 | } | |
272 | ||
273 | done: | |
274 | sfc_log_init(sa, "done"); | |
275 | return 0; | |
276 | } | |
277 | ||
278 | void | |
279 | sfc_intr_close(struct sfc_adapter *sa) | |
280 | { | |
281 | sfc_log_init(sa, "entry"); | |
282 | ||
283 | sfc_log_init(sa, "done"); | |
284 | } | |
285 | ||
286 | int | |
287 | sfc_intr_attach(struct sfc_adapter *sa) | |
288 | { | |
289 | struct sfc_intr *intr = &sa->intr; | |
9f95a23c | 290 | struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(sa->eth_dev); |
11fdf7f2 TL |
291 | |
292 | sfc_log_init(sa, "entry"); | |
293 | ||
294 | switch (pci_dev->intr_handle.type) { | |
9f95a23c | 295 | #ifdef RTE_EXEC_ENV_LINUX |
11fdf7f2 TL |
296 | case RTE_INTR_HANDLE_UIO_INTX: |
297 | case RTE_INTR_HANDLE_VFIO_LEGACY: | |
298 | intr->type = EFX_INTR_LINE; | |
299 | break; | |
300 | case RTE_INTR_HANDLE_UIO: | |
301 | case RTE_INTR_HANDLE_VFIO_MSI: | |
302 | case RTE_INTR_HANDLE_VFIO_MSIX: | |
303 | intr->type = EFX_INTR_MESSAGE; | |
304 | break; | |
305 | #endif | |
306 | default: | |
307 | intr->type = EFX_INTR_INVALID; | |
308 | break; | |
309 | } | |
310 | ||
311 | sfc_log_init(sa, "done"); | |
312 | return 0; | |
313 | } | |
314 | ||
315 | void | |
316 | sfc_intr_detach(struct sfc_adapter *sa) | |
317 | { | |
318 | sfc_log_init(sa, "entry"); | |
319 | ||
320 | sa->intr.type = EFX_INTR_INVALID; | |
321 | ||
322 | sfc_log_init(sa, "done"); | |
323 | } |