xref: /dpdk/drivers/net/sfc/sfc_intr.c (revision 0857b942113874c69dc3db5df11a828ee3cc9b6b)
1 /*-
2  *   BSD LICENSE
3  *
4  * Copyright (c) 2016-2017 Solarflare Communications Inc.
5  * All rights reserved.
6  *
7  * This software was jointly developed between OKTET Labs (under contract
8  * for Solarflare) and Solarflare Communications, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright notice,
14  *    this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright notice,
16  *    this list of conditions and the following disclaimer in the documentation
17  *    and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
29  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * At the momemt of writing DPDK v16.07 has notion of two types of
34  * interrupts: LSC (link status change) and RXQ (receive indication).
35  * It allows to register interrupt callback for entire device which is
36  * not intended to be used for receive indication (i.e. link status
37  * change indication only). The handler has no information which HW
38  * interrupt has triggered it, so we don't know which event queue should
39  * be polled/reprimed (except qmask in the case of legacy line interrupt).
40  */
41 
42 #include <rte_common.h>
43 #include <rte_interrupts.h>
44 
45 #include "efx.h"
46 
47 #include "sfc.h"
48 #include "sfc_log.h"
49 #include "sfc_ev.h"
50 
51 static void
52 sfc_intr_handle_mgmt_evq(struct sfc_adapter *sa)
53 {
54 	struct sfc_evq *evq;
55 
56 	rte_spinlock_lock(&sa->mgmt_evq_lock);
57 
58 	evq = sa->mgmt_evq;
59 
60 	if (evq->init_state != SFC_EVQ_STARTED) {
61 		sfc_log_init(sa, "interrupt on stopped EVQ %u", evq->evq_index);
62 	} else {
63 		sfc_ev_qpoll(evq);
64 
65 		if (sfc_ev_qprime(evq) != 0)
66 			sfc_err(sa, "cannot prime EVQ %u", evq->evq_index);
67 	}
68 
69 	rte_spinlock_unlock(&sa->mgmt_evq_lock);
70 }
71 
72 static void
73 sfc_intr_line_handler(struct rte_intr_handle *intr_handle, void *cb_arg)
74 {
75 	struct sfc_adapter *sa = (struct sfc_adapter *)cb_arg;
76 	efx_nic_t *enp = sa->nic;
77 	boolean_t fatal;
78 	uint32_t qmask;
79 	unsigned int lsc_seq = sa->port.lsc_seq;
80 
81 	sfc_log_init(sa, "entry");
82 
83 	if (sa->state != SFC_ADAPTER_STARTED &&
84 	    sa->state != SFC_ADAPTER_STARTING &&
85 	    sa->state != SFC_ADAPTER_STOPPING) {
86 		sfc_log_init(sa,
87 			     "interrupt on stopped adapter, don't reenable");
88 		goto exit;
89 	}
90 
91 	efx_intr_status_line(enp, &fatal, &qmask);
92 	if (fatal) {
93 		(void)efx_intr_disable(enp);
94 		(void)efx_intr_fatal(enp);
95 		sfc_err(sa, "fatal, interrupts disabled");
96 		goto exit;
97 	}
98 
99 	if (qmask & (1 << sa->mgmt_evq_index))
100 		sfc_intr_handle_mgmt_evq(sa);
101 
102 	if (rte_intr_enable(intr_handle) != 0)
103 		sfc_err(sa, "cannot reenable interrupts");
104 
105 	sfc_log_init(sa, "done");
106 
107 exit:
108 	if (lsc_seq != sa->port.lsc_seq) {
109 		sfc_info(sa, "link status change event: link %s",
110 			 sa->eth_dev->data->dev_link.link_status ?
111 			 "UP" : "DOWN");
112 		_rte_eth_dev_callback_process(sa->eth_dev,
113 					      RTE_ETH_EVENT_INTR_LSC, NULL);
114 	}
115 }
116 
117 static void
118 sfc_intr_message_handler(struct rte_intr_handle *intr_handle, void *cb_arg)
119 {
120 	struct sfc_adapter *sa = (struct sfc_adapter *)cb_arg;
121 	efx_nic_t *enp = sa->nic;
122 	boolean_t fatal;
123 	unsigned int lsc_seq = sa->port.lsc_seq;
124 
125 	sfc_log_init(sa, "entry");
126 
127 	if (sa->state != SFC_ADAPTER_STARTED &&
128 	    sa->state != SFC_ADAPTER_STARTING &&
129 	    sa->state != SFC_ADAPTER_STOPPING) {
130 		sfc_log_init(sa, "adapter not-started, don't reenable");
131 		goto exit;
132 	}
133 
134 	efx_intr_status_message(enp, sa->mgmt_evq_index, &fatal);
135 	if (fatal) {
136 		(void)efx_intr_disable(enp);
137 		(void)efx_intr_fatal(enp);
138 		sfc_err(sa, "fatal, interrupts disabled");
139 		goto exit;
140 	}
141 
142 	sfc_intr_handle_mgmt_evq(sa);
143 
144 	if (rte_intr_enable(intr_handle) != 0)
145 		sfc_err(sa, "cannot reenable interrupts");
146 
147 	sfc_log_init(sa, "done");
148 
149 exit:
150 	if (lsc_seq != sa->port.lsc_seq) {
151 		sfc_info(sa, "link status change event");
152 		_rte_eth_dev_callback_process(sa->eth_dev,
153 					      RTE_ETH_EVENT_INTR_LSC, NULL);
154 	}
155 }
156 
157 int
158 sfc_intr_start(struct sfc_adapter *sa)
159 {
160 	struct sfc_intr *intr = &sa->intr;
161 	struct rte_intr_handle *intr_handle;
162 	struct rte_pci_device *pci_dev;
163 	int rc;
164 
165 	sfc_log_init(sa, "entry");
166 
167 	/*
168 	 * The EFX common code event queue module depends on the interrupt
169 	 * module. Ensure that the interrupt module is always initialized
170 	 * (even if interrupts are not used).  Status memory is required
171 	 * for Siena only and may be NULL for EF10.
172 	 */
173 	sfc_log_init(sa, "efx_intr_init");
174 	rc = efx_intr_init(sa->nic, intr->type, NULL);
175 	if (rc != 0)
176 		goto fail_intr_init;
177 
178 	pci_dev = SFC_DEV_TO_PCI(sa->eth_dev);
179 	intr_handle = &pci_dev->intr_handle;
180 
181 	if (intr->handler != NULL) {
182 		sfc_log_init(sa, "rte_intr_callback_register");
183 		rc = rte_intr_callback_register(intr_handle, intr->handler,
184 						(void *)sa);
185 		if (rc != 0) {
186 			sfc_err(sa,
187 				"cannot register interrupt handler (rc=%d)",
188 				rc);
189 			/*
190 			 * Convert error code from negative returned by RTE API
191 			 * to positive used in the driver.
192 			 */
193 			rc = -rc;
194 			goto fail_rte_intr_cb_reg;
195 		}
196 
197 		sfc_log_init(sa, "rte_intr_enable");
198 		rc = rte_intr_enable(intr_handle);
199 		if (rc != 0) {
200 			sfc_err(sa, "cannot enable interrupts (rc=%d)", rc);
201 			/*
202 			 * Convert error code from negative returned by RTE API
203 			 * to positive used in the driver.
204 			 */
205 			rc = -rc;
206 			goto fail_rte_intr_enable;
207 		}
208 
209 		sfc_log_init(sa, "efx_intr_enable");
210 		efx_intr_enable(sa->nic);
211 	}
212 
213 	sfc_log_init(sa, "done type=%u max_intr=%d nb_efd=%u vec=%p",
214 		     intr_handle->type, intr_handle->max_intr,
215 		     intr_handle->nb_efd, intr_handle->intr_vec);
216 	return 0;
217 
218 fail_rte_intr_enable:
219 	rte_intr_callback_unregister(intr_handle, intr->handler, (void *)sa);
220 
221 fail_rte_intr_cb_reg:
222 	efx_intr_fini(sa->nic);
223 
224 fail_intr_init:
225 	sfc_log_init(sa, "failed %d", rc);
226 	return rc;
227 }
228 
229 void
230 sfc_intr_stop(struct sfc_adapter *sa)
231 {
232 	struct sfc_intr *intr = &sa->intr;
233 	struct rte_pci_device *pci_dev = SFC_DEV_TO_PCI(sa->eth_dev);
234 
235 	sfc_log_init(sa, "entry");
236 
237 	if (intr->handler != NULL) {
238 		struct rte_intr_handle *intr_handle;
239 		int rc;
240 
241 		efx_intr_disable(sa->nic);
242 
243 		intr_handle = &pci_dev->intr_handle;
244 		if (rte_intr_disable(intr_handle) != 0)
245 			sfc_err(sa, "cannot disable interrupts");
246 
247 		while ((rc = rte_intr_callback_unregister(intr_handle,
248 				intr->handler, (void *)sa)) == -EAGAIN)
249 			;
250 		if (rc != 1)
251 			sfc_err(sa,
252 				"cannot unregister interrupt handler %d",
253 				rc);
254 	}
255 
256 	efx_intr_fini(sa->nic);
257 
258 	sfc_log_init(sa, "done");
259 }
260 
261 int
262 sfc_intr_configure(struct sfc_adapter *sa)
263 {
264 	struct sfc_intr *intr = &sa->intr;
265 
266 	sfc_log_init(sa, "entry");
267 
268 	intr->handler = NULL;
269 	intr->lsc_intr = (sa->eth_dev->data->dev_conf.intr_conf.lsc != 0);
270 	if (!intr->lsc_intr) {
271 		sfc_info(sa, "LSC tracking using interrupts is disabled");
272 		goto done;
273 	}
274 
275 	switch (intr->type) {
276 	case EFX_INTR_MESSAGE:
277 		intr->handler = sfc_intr_message_handler;
278 		break;
279 	case EFX_INTR_LINE:
280 		intr->handler = sfc_intr_line_handler;
281 		break;
282 	case EFX_INTR_INVALID:
283 		sfc_warn(sa, "interrupts are not supported");
284 		break;
285 	default:
286 		sfc_panic(sa, "unexpected EFX interrupt type %u\n", intr->type);
287 		break;
288 	}
289 
290 done:
291 	sfc_log_init(sa, "done");
292 	return 0;
293 }
294 
295 void
296 sfc_intr_close(struct sfc_adapter *sa)
297 {
298 	sfc_log_init(sa, "entry");
299 
300 	sfc_log_init(sa, "done");
301 }
302 
303 int
304 sfc_intr_attach(struct sfc_adapter *sa)
305 {
306 	struct sfc_intr *intr = &sa->intr;
307 	struct rte_pci_device *pci_dev = SFC_DEV_TO_PCI(sa->eth_dev);
308 
309 	sfc_log_init(sa, "entry");
310 
311 	switch (pci_dev->intr_handle.type) {
312 #ifdef RTE_EXEC_ENV_LINUXAPP
313 	case RTE_INTR_HANDLE_VFIO_LEGACY:
314 		intr->type = EFX_INTR_LINE;
315 		break;
316 	case RTE_INTR_HANDLE_VFIO_MSI:
317 	case RTE_INTR_HANDLE_VFIO_MSIX:
318 		intr->type = EFX_INTR_MESSAGE;
319 		break;
320 #endif
321 	default:
322 		intr->type = EFX_INTR_INVALID;
323 		break;
324 	}
325 
326 	sfc_log_init(sa, "done");
327 	return 0;
328 }
329 
330 void
331 sfc_intr_detach(struct sfc_adapter *sa)
332 {
333 	sfc_log_init(sa, "entry");
334 
335 	sa->intr.type = EFX_INTR_INVALID;
336 
337 	sfc_log_init(sa, "done");
338 }
339