xref: /illumos-gate/usr/src/uts/common/io/ena/ena_intr.c (revision dd72704bd9e794056c558153663c739e2012d721)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2021 Oxide Computer Company
14  */
15 #include "ena.h"
16 
17 /*
18  * We currently limit the number of Tx/Rx queues to the number of
19  * available interrupts (minus one for the admin queue).
20  */
21 static uint_t
22 ena_io_intr(caddr_t arg1, caddr_t arg2)
23 {
24 	ena_t *ena = (ena_t *)arg1;
25 	uint16_t vector = (uintptr_t)(void *)arg2;
26 	ASSERT3U(vector, >, 0);
27 	ASSERT3U(vector, <, ena->ena_num_intrs);
28 	ena_txq_t *txq = &ena->ena_txqs[vector - 1];
29 	ena_rxq_t *rxq = &ena->ena_rxqs[vector - 1];
30 	uint32_t intr_ctrl;
31 
32 	ASSERT3P(txq, !=, NULL);
33 	ASSERT3P(rxq, !=, NULL);
34 	ena_tx_intr_work(txq);
35 	ena_rx_intr_work(rxq);
36 
37 	/*
38 	 * The Rx/Tx queue share the same interrupt, only need to
39 	 * unmask interrupts for one of them.
40 	 */
41 	intr_ctrl = ena_hw_abs_read32(ena, txq->et_cq_unmask_addr);
42 	ENAHW_REG_INTR_UNMASK(intr_ctrl);
43 	ena_hw_abs_write32(ena, txq->et_cq_unmask_addr, intr_ctrl);
44 	return (DDI_INTR_CLAIMED);
45 }
46 
47 static uint_t
48 ena_admin_intr(caddr_t arg1, caddr_t arg2)
49 {
50 	ena_t *ena = (ena_t *)arg1;
51 
52 	ena_aenq_work(ena);
53 	return (DDI_INTR_CLAIMED);
54 }
55 
56 void
57 ena_intr_remove_handlers(ena_t *ena)
58 {
59 	for (int i = 0; i < ena->ena_num_intrs; i++) {
60 		int ret = ddi_intr_remove_handler(ena->ena_intr_handles[i]);
61 
62 		/* Nothing we can really do except log. */
63 		if (ret != DDI_SUCCESS) {
64 			ena_err(ena, "failed to remove interrupt handler for "
65 			    "vector %d: %d", i, ret);
66 		}
67 	}
68 }
69 
70 /*
71  * The ena driver uses separate interrupt handlers for the admin queue
72  * and I/O queues.
73  */
74 boolean_t
75 ena_intr_add_handlers(ena_t *ena)
76 {
77 	ASSERT3S(ena->ena_num_intrs, >=, 2);
78 	if (ddi_intr_add_handler(ena->ena_intr_handles[0], ena_admin_intr, ena,
79 	    (void *)(uintptr_t)0) != DDI_SUCCESS) {
80 		ena_err(ena, "failed to add admin interrupt handler");
81 		return (B_FALSE);
82 	}
83 
84 	for (int i = 1; i < ena->ena_num_intrs; i++) {
85 		caddr_t vector = (void *)(uintptr_t)(i);
86 		int ret = ddi_intr_add_handler(ena->ena_intr_handles[i],
87 		    ena_io_intr, ena, vector);
88 
89 		if (ret != DDI_SUCCESS) {
90 			ena_err(ena, "failed to add I/O interrupt handler "
91 			    "for vector %u", i);
92 
93 			/*
94 			 * If we fail to add any I/O handler, then all
95 			 * successfully added handlers are removed,
96 			 * including the admin handler. For example,
97 			 * when i=2 we remove handler 1 (the first I/O
98 			 * handler), and when i=1 we remove handler 0
99 			 * (the admin handler).
100 			 */
101 			while (i >= 1) {
102 				i--;
103 				(void) ddi_intr_remove_handler(
104 				    ena->ena_intr_handles[i]);
105 			}
106 
107 			return (B_FALSE);
108 		}
109 	}
110 
111 	return (B_TRUE);
112 }
113 
114 boolean_t
115 ena_intrs_disable(ena_t *ena)
116 {
117 	int ret;
118 
119 	if (ena->ena_intr_caps & DDI_INTR_FLAG_BLOCK) {
120 		if ((ret = ddi_intr_block_disable(ena->ena_intr_handles,
121 		    ena->ena_num_intrs)) != DDI_SUCCESS) {
122 			ena_err(ena, "failed to block disable interrupts: %d",
123 			    ret);
124 			return (B_FALSE);
125 		}
126 	} else {
127 		for (int i = 0; i < ena->ena_num_intrs; i++) {
128 			ret = ddi_intr_disable(ena->ena_intr_handles[i]);
129 			if (ret != DDI_SUCCESS) {
130 				ena_err(ena, "failed to disable interrupt "
131 				    "%d: %d", i, ret);
132 				return (B_FALSE);
133 			}
134 		}
135 	}
136 
137 	return (B_TRUE);
138 }
139 
140 boolean_t
141 ena_intrs_enable(ena_t *ena)
142 {
143 	int ret;
144 
145 	if (ena->ena_intr_caps & DDI_INTR_FLAG_BLOCK) {
146 		if ((ret = ddi_intr_block_enable(ena->ena_intr_handles,
147 		    ena->ena_num_intrs)) != DDI_SUCCESS) {
148 			ena_err(ena, "failed to block enable interrupts: %d",
149 			    ret);
150 			return (B_FALSE);
151 		}
152 	} else {
153 		for (int i = 0; i < ena->ena_num_intrs; i++) {
154 			if ((ret = ddi_intr_enable(ena->ena_intr_handles[i])) !=
155 			    DDI_SUCCESS) {
156 				ena_err(ena, "failed to enable interrupt "
157 				    "%d: %d", i, ret);
158 
159 				/*
160 				 * If we fail to enable any interrupt,
161 				 * then all interrupts are disabled.
162 				 */
163 				while (i >= 1) {
164 					i--;
165 					(void) ddi_intr_disable(
166 					    ena->ena_intr_handles[i]);
167 				}
168 
169 				return (B_FALSE);
170 			}
171 		}
172 	}
173 
174 	return (B_TRUE);
175 }
176