xref: /illumos-gate/usr/src/uts/common/io/bnx/bnxint.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * Copyright 2014-2017 Cavium, Inc.
3  * The contents of this file are subject to the terms of the Common Development
4  * and Distribution License, v.1,  (the "License").
5  *
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the License at available
9  * at http://opensource.org/licenses/CDDL-1.0
10  *
11  * See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14 
15 /*
16  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
17  * Copyright (c) 2019, Joyent, Inc.
18  */
19 
20 #include "bnxint.h"
21 #include "bnxsnd.h"
22 #include "bnxrcv.h"
23 
24 
25 #define	BNX_INTR_NUMBER 0
26 
27 /*
28  * Name:    bnx_intr_priv
29  *
30  * Input:   ptr to um_device_t
31  *
32  * Return:  Interrupt status
33  *
34  * Description:
35  *          This routine is called from ISR and POLL API routines to consume
36  *          any pending events. This function determines if there is any
37  *          pending status and calls corresponding LM functions to consume
38  *          the event. L2 driver consumes three events - L2 Tx compete,
39  *          L2 Rx indication and link status change.
40  */
41 static lm_interrupt_status_t
42 bnx_intr_priv(um_device_t *const umdevice)
43 {
44 	u32_t idx;
45 	lm_device_t *lmdevice;
46 	lm_interrupt_status_t intrstat;
47 
48 	lmdevice = &(umdevice->lm_dev);
49 
50 	/*
51 	 * Following LM routine checks for pending interrupts and
52 	 * returns corresponding bits set in a 32bit integer value.
53 	 */
54 	intrstat = lm_get_interrupt_status(lmdevice);
55 
56 	if (intrstat & LM_KNOCK_KNOCK_EVENT) {
57 		um_send_driver_pulse(umdevice);
58 	}
59 
60 	if (intrstat & LM_RX_EVENT_MASK) {
61 		for (idx = RX_CHAIN_IDX0; idx < NUM_RX_CHAIN; idx++) {
62 			if (intrstat & (LM_RX0_EVENT_ACTIVE << idx)) {
63 				s_list_t *waitq;
64 
65 				waitq = &(_RX_QINFO(umdevice, idx).waitq);
66 
67 				mutex_enter(&umdevice->os_param.rcv_mutex);
68 				(void) lm_get_packets_rcvd(lmdevice, idx, 0,
69 				    waitq);
70 				mutex_exit(&umdevice->os_param.rcv_mutex);
71 			}
72 		}
73 	}
74 
75 	if (intrstat & LM_TX_EVENT_MASK) {
76 		for (idx = TX_CHAIN_IDX0; idx < NUM_TX_CHAIN; idx++) {
77 			if (intrstat & (LM_TX0_EVENT_ACTIVE << idx)) {
78 				/* This call is mutex protected internally. */
79 				bnx_xmit_ring_intr(umdevice, idx);
80 			}
81 		}
82 	}
83 
84 	if (intrstat & LM_PHY_EVENT_ACTIVE) {
85 		mutex_enter(&umdevice->os_param.phy_mutex);
86 		lm_service_phy_int(lmdevice, FALSE);
87 		mutex_exit(&umdevice->os_param.phy_mutex);
88 	}
89 
90 	return (intrstat);
91 }
92 
93 /*
94  * Description:
95  *
96  * This function sends rx traffic up the stack and replenishes the hardware
97  * rx buffers.  Although we share the responsibility of replenishing the
98  * rx buffers with the timer, we still need to wait here indefinitely.  This
99  * is the only place where we send rx traffic back up the stack.
100  *
101  * We go through a lot of mental gymnastics to make sure we are not holding a
102  * lock while calling gld_recv().  We can deadlock in the following scenario
103  * if we aren't careful :
104  *
105  * Thread 1:
106  *          bnx_intr_disable()
107  *              bnx_intr_wait()
108  *                  mutex_enter(intr_*_mutex)
109  *
110  * Thread 2:
111  *          bnx_intr_[soft|1lvl]()
112  *              bnx_intr_recv()
113  *                  mutex_enter(rcv_mutex)
114  *
115  * Thread 3:
116  *          bnx_intr_[soft|1lvl]()
117  *              mutex_enter(intr_*_mutex)
118  *              mutex_enter(rcv_mutex)
119  *
120  * Return:
121  */
122 static void
123 bnx_intr_recv(um_device_t * const umdevice)
124 {
125 	mutex_enter(&umdevice->os_param.rcv_mutex);
126 
127 	if (umdevice->intr_enabled == B_TRUE) {
128 		/*
129 		 * Send the rx packets up.  This function will release and
130 		 * acquire the receive mutex across calls to gld_recv().
131 		 */
132 		bnx_rxpkts_intr(umdevice);
133 	}
134 
135 	/*
136 	 * Since gld_recv() can hang while decommisioning the driver, we
137 	 * need to double check that interrupts are still enabled before
138 	 * attempting to replenish the rx buffers.
139 	 */
140 	if (umdevice->intr_enabled == B_TRUE) {
141 		/* This function does an implicit *_fill(). */
142 		bnx_rxpkts_post(umdevice);
143 	}
144 
145 	mutex_exit(&umdevice->os_param.rcv_mutex);
146 }
147 
148 static void
149 bnx_intr_xmit(um_device_t *const umdevice)
150 {
151 	mutex_enter(&umdevice->os_param.xmit_mutex);
152 
153 	if (umdevice->intr_enabled == B_TRUE) {
154 		/*
155 		 * Send the tx packets in waitq & notify the GLD.
156 		 */
157 		bnx_txpkts_intr(umdevice);
158 	}
159 
160 	mutex_exit(&umdevice->os_param.xmit_mutex);
161 }
162 
163 static unsigned int
164 bnx_intr_1lvl(caddr_t arg1, caddr_t arg2)
165 {
166 	lm_device_t *lmdevice;
167 	um_device_t *umdevice;
168 	lm_interrupt_status_t intrstat = 0;
169 	u32_t value32;
170 	umdevice = (um_device_t *)arg1;
171 
172 	lmdevice = &(umdevice->lm_dev);
173 
174 	mutex_enter(&umdevice->intr_mutex);
175 
176 	if (umdevice->intr_enabled != B_TRUE) {
177 		/*
178 		 * The interrupt cannot be ours.  Interrupts
179 		 * from our device have been disabled.
180 		 */
181 		mutex_exit(&umdevice->intr_mutex);
182 		umdevice->intr_in_disabled++;
183 		return (DDI_INTR_UNCLAIMED);
184 	}
185 
186 	/* Make sure we are working with current data. */
187 	(void) ddi_dma_sync(*(umdevice->os_param.status_block_dma_hdl), 0,
188 	    STATUS_BLOCK_BUFFER_SIZE, DDI_DMA_SYNC_FORKERNEL);
189 
190 	/* Make sure it is our device that is interrupting. */
191 	if (lmdevice->vars.status_virt->deflt.status_idx ==
192 	    umdevice->dev_var.processed_status_idx) {
193 		/*
194 		 * It is possible that we could have arrived at the ISR
195 		 * before the status block had a chance to be DMA'd into
196 		 * host memory.  Reading the status of the INTA line will
197 		 * implicitly force the DMA, and inform us of whether we
198 		 * are truly interrupting.  INTA is active low.
199 		 */
200 		REG_RD(lmdevice, pci_config.pcicfg_misc_status, &value32);
201 		if (value32 & PCICFG_MISC_STATUS_INTA_VALUE) {
202 			/* This isn't our interrupt. */
203 			umdevice->intr_no_change++;
204 			mutex_exit(&umdevice->intr_mutex);
205 			return (DDI_INTR_UNCLAIMED);
206 		}
207 	}
208 
209 	umdevice->intrFired++;
210 
211 	/* Disable interrupt and enqueue soft intr processing. */
212 	REG_WR(lmdevice, pci_config.pcicfg_int_ack_cmd,
213 	    (PCICFG_INT_ACK_CMD_USE_INT_HC_PARAM |
214 	    PCICFG_INT_ACK_CMD_MASK_INT));
215 
216 	FLUSHPOSTEDWRITES(lmdevice);
217 
218 	umdevice->dev_var.processed_status_idx =
219 	    lmdevice->vars.status_virt->deflt.status_idx;
220 
221 	/* Service the interrupts. */
222 	intrstat = bnx_intr_priv(umdevice);
223 
224 	value32 = umdevice->dev_var.processed_status_idx;
225 	value32 |= PCICFG_INT_ACK_CMD_INDEX_VALID;
226 
227 	/*
228 	 * Inform the hardware of the last interrupt event we processed
229 	 * and reinstate the hardware's ability to assert interrupts.
230 	 */
231 	REG_WR(lmdevice, pci_config.pcicfg_int_ack_cmd, value32);
232 
233 	FLUSHPOSTEDWRITES(lmdevice);
234 
235 	umdevice->intr_count++;
236 
237 	if (intrstat & LM_RX_EVENT_MASK) {
238 		bnx_intr_recv(umdevice);
239 	}
240 
241 	if (intrstat & LM_TX_EVENT_MASK) {
242 		bnx_intr_xmit(umdevice);
243 	}
244 
245 	mutex_exit(&umdevice->intr_mutex);
246 
247 	return (DDI_INTR_CLAIMED);
248 }
249 
250 void
251 bnx_intr_enable(um_device_t * const umdevice)
252 {
253 	int rc;
254 
255 	umdevice->intr_count = 0;
256 
257 	/*
258 	 * Allow interrupts to touch the hardware.
259 	 */
260 	umdevice->intr_enabled = B_TRUE;
261 
262 	if ((rc = ddi_intr_enable(umdevice->pIntrBlock[0])) != DDI_SUCCESS) {
263 		cmn_err(CE_WARN, "%s: Failed to enable default isr block (%d)",
264 		    umdevice->dev_name, rc);
265 		return; /* XXX return error */
266 	}
267 
268 	/* Allow the hardware to generate interrupts. */
269 	lm_enable_int(&(umdevice->lm_dev));
270 
271 	FLUSHPOSTEDWRITES(&(umdevice->lm_dev));
272 
273 	/*
274 	 * XXX This delay is here because of a discovered problem regarding a
275 	 * call to ddi_intr_disable immediately after enabling interrupts.  This
276 	 * can occur with the "ifconfig -a plumb up" command which brings an
277 	 * interface up/down/up/down/up.  There seems to be a race condition
278 	 * between the ddi_intr_enable/lm_enable_int and ddi_intr_disable
279 	 * routines that results in interrupts to no longer fire on the
280 	 * interface and a REBOOT IS REQUIRED to fix!
281 	 */
282 	drv_usecwait(2000000);
283 }
284 
285 /*
286  * Description:
287  *
288  * This function makes sure the ISR no longer touches the hardware.  It
289  * accomplishes this by making sure the ISR either completes, or that it
290  * acknowledges the intr_enabled status change.
291  *
292  * Return:
293  */
294 static void
295 bnx_intr_wait(um_device_t * const umdevice)
296 {
297 	if (mutex_tryenter(&umdevice->intr_mutex)) {
298 		/*
299 		 * If we were able to get the hardware interrupt mutex, then it
300 		 * means that either the ISR wasn't processing at this time, or
301 		 * that it was at the end, processing the receive packets. If it
302 		 * the latter case, then all we need to do is acquire the
303 		 * rcv_mutex.  If we can acquire it, it means the receive
304 		 * processing is stalled, waiting for a GLD mutex, or that the
305 		 * ISR is not processing RX packets.
306 		 */
307 		mutex_enter(&umdevice->os_param.rcv_mutex);
308 		mutex_exit(&umdevice->os_param.rcv_mutex);
309 	} else {
310 		/*
311 		 * We couldn't acquire the hardware interrupt mutex. This means
312 		 * the ISR is running.  Wait for it to complete by
313 		 * (re)attempting to acquire the interrupt mutex. Whether we
314 		 * acquire it immediately or not, we will know the ISR has
315 		 * acknowledged the intr_enabled status change.
316 		 */
317 		mutex_enter(&umdevice->intr_mutex);
318 	}
319 	mutex_exit(&umdevice->intr_mutex);
320 }
321 
322 
323 void
324 bnx_intr_disable(um_device_t * const umdevice)
325 {
326 	int rc;
327 
328 	/*
329 	 * Prevent any future interrupts to no longer touch the hardware.
330 	 */
331 	umdevice->intr_enabled = B_FALSE;
332 
333 	/*
334 	 * Wait for any currently running interrupt to complete.
335 	 */
336 	bnx_intr_wait(umdevice);
337 
338 	/* Stop the device from generating any interrupts. */
339 	lm_disable_int(&(umdevice->lm_dev));
340 
341 	FLUSHPOSTEDWRITES(&(umdevice->lm_dev));
342 
343 	if ((rc = ddi_intr_disable(umdevice->pIntrBlock[0])) != DDI_SUCCESS) {
344 		cmn_err(CE_WARN, "%s: Failed to disable default isr (%d)",
345 		    umdevice->dev_name, rc);
346 	}
347 }
348 
349 int
350 bnxIntrInit(um_device_t *umdevice)
351 {
352 	dev_info_t *pDev = umdevice->os_param.dip;
353 	int intrActual, rc;
354 
355 	if ((umdevice->pIntrBlock = kmem_zalloc(sizeof (ddi_intr_handle_t),
356 	    KM_SLEEP)) == NULL) {
357 		cmn_err(CE_WARN, "%s: Failed to allocate interrupt handle "
358 		    "block!", umdevice->dev_name);
359 		return (-1);
360 	}
361 
362 	umdevice->intrType = (umdevice->dev_var.disableMsix) ?
363 	    DDI_INTR_TYPE_FIXED : DDI_INTR_TYPE_MSIX;
364 
365 	while (1) {
366 		if ((rc = ddi_intr_alloc(pDev, umdevice->pIntrBlock,
367 		    umdevice->intrType, 0, 1, &intrActual,
368 		    DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) {
369 			cmn_err(CE_WARN, "!%s: Failed to initialize default "
370 			    "%s isr handle block (%d)", umdevice->dev_name,
371 			    (umdevice->intrType == DDI_INTR_TYPE_MSIX) ?
372 			    "MSIX" : "Fixed", rc);
373 
374 			if (umdevice->intrType == DDI_INTR_TYPE_MSIX) {
375 				cmn_err(CE_WARN, "!%s: Reverting to Fixed "
376 				    "level interrupts", umdevice->dev_name);
377 
378 				umdevice->intrType = DDI_INTR_TYPE_FIXED;
379 				continue;
380 			} else {
381 				kmem_free(umdevice->pIntrBlock,
382 				    sizeof (ddi_intr_handle_t));
383 				return (-1);
384 			}
385 		}
386 		break;
387 	}
388 
389 	if (intrActual != 1) {
390 		cmn_err(CE_WARN, "%s: Failed to alloc minimum default "
391 		    "isr handler!", umdevice->dev_name);
392 		(void) ddi_intr_free(umdevice->pIntrBlock[0]);
393 		kmem_free(umdevice->pIntrBlock, sizeof (ddi_intr_handle_t));
394 		return (-1);
395 	}
396 
397 	if ((rc = ddi_intr_get_pri(umdevice->pIntrBlock[0],
398 	    &umdevice->intrPriority)) != DDI_SUCCESS) {
399 		cmn_err(CE_WARN, "%s: Failed to get isr priority (%d)",
400 		    umdevice->dev_name, rc);
401 		(void) ddi_intr_free(umdevice->pIntrBlock[0]);
402 		kmem_free(umdevice->pIntrBlock, sizeof (ddi_intr_handle_t));
403 		return (-1);
404 	}
405 
406 	if (umdevice->intrPriority >= ddi_intr_get_hilevel_pri()) {
407 		cmn_err(CE_WARN, "%s: Interrupt priority is too high",
408 		    umdevice->dev_name);
409 		(void) ddi_intr_free(umdevice->pIntrBlock[0]);
410 		kmem_free(umdevice->pIntrBlock, sizeof (ddi_intr_handle_t));
411 		return (-1);
412 	}
413 
414 	if ((rc = ddi_intr_add_handler(umdevice->pIntrBlock[0], bnx_intr_1lvl,
415 	    (caddr_t)umdevice, NULL)) != DDI_SUCCESS) {
416 		cmn_err(CE_WARN, "%s: Failed to add the default isr "
417 		    "handler (%d)", umdevice->dev_name, rc);
418 		(void) ddi_intr_free(umdevice->pIntrBlock[0]);
419 		kmem_free(umdevice->pIntrBlock, sizeof (ddi_intr_handle_t));
420 		return (-1);
421 	}
422 
423 	/* Intialize the mutex used by the hardware interrupt handler. */
424 	mutex_init(&umdevice->intr_mutex, NULL, MUTEX_DRIVER,
425 	    DDI_INTR_PRI(umdevice->intrPriority));
426 
427 	umdevice->lm_dev.vars.interrupt_mode =
428 	    (umdevice->intrType == DDI_INTR_TYPE_FIXED) ?
429 	    IRQ_MODE_LINE_BASED : IRQ_MODE_MSIX_BASED;
430 	return (0);
431 }
432 
433 void
434 bnxIntrFini(um_device_t *umdevice)
435 {
436 	int ret;
437 
438 	if ((ret = ddi_intr_disable(umdevice->pIntrBlock[0])) != 0) {
439 		dev_err(umdevice->os_param.dip, CE_WARN,
440 		    "failed to disable interrupt: %d", ret);
441 	}
442 	if ((ret = ddi_intr_remove_handler(umdevice->pIntrBlock[0])) != 0) {
443 		dev_err(umdevice->os_param.dip, CE_WARN,
444 		    "failed to remove interrupt: %d", ret);
445 	}
446 	if ((ret = ddi_intr_free(umdevice->pIntrBlock[0])) != 0) {
447 		dev_err(umdevice->os_param.dip, CE_WARN,
448 		    "failed to free interrupt: %d", ret);
449 	}
450 	kmem_free(umdevice->pIntrBlock, sizeof (ddi_intr_handle_t));
451 
452 	umdevice->pIntrBlock = NULL;
453 
454 	mutex_destroy(&umdevice->intr_mutex);
455 }
456