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