1ddi_intr_dup_handler(9F) Kernel Functions for Drivers ddi_intr_dup_handler(9F)
2
3
4
6 ddi_intr_dup_handler - reuse interrupt handler and arguments for MSI-X
7 interrupts
8
10 #include <sys/types.h>
11 #include <sys/conf.h>
12 #include <sys/ddi.h>
13 #include <sys/sunddi.h>
14
15 int ddi_intr_dup_handler(ddi_intr_handle_t primary, int vector,
16 ddi_intr_handle_t *new);
17
18
20 Solaris DDI specific (Solaris DDI).
21
23 primary Original DDI interrupt handle
24
25
26 vector Interrupt number to duplicate
27
28
29 new Pointer to new DDI interrupt handle
30
31
33 The ddi_intr_dup_handler() function is a feature for MSI-X interrupts
34 that allows an unallocated interrupt vector of a device to use a previ‐
35 ously initialized or added primary MSI-X interrupt vector in order to
36 share the same vector address, vector data, interrupt handler, and han‐
37 dler arguments. This feature allows a driver to alias the resources
38 provided by the Solaris Operating System to the unallocated interrupt
39 vectors on an associated device. For example, if 2 MSI-X interrupts
40 were allocated to a driver and 32 interrupts were supported on the
41 device, the driver could alias the 2 interrupts it received to the 30
42 remaining on the device.
43
44
45 The ddi_intr_dup_handler() function must be called after the primary
46 interrupt handle has been added to the system or enabled by
47 ddi_intr_add_handler(9F) and ddi_intr_enable(9F) calls, respectively.
48 If successful, the function returns the new interrupt handle for a
49 given vector in the new argument passed to the function. The new inter‐
50 rupt handle must not have been previously allocated with
51 ddi_intr_alloc(9F). Otherwise, the ddi_intr_dup_handler() call will
52 fail.
53
54
55 The only supported calls on dup-ed interrupt handles are
56 ddi_intr_set_mask(9F), ddi_intr_clr_mask(9F), ddi_intr_get_pending(9F),
57 ddi_intr_enable(9F), ddi_intr_disable(9F), and ddi_intr_free(9F).
58
59
60 A call to ddi_intr_dup_handler() does not imply that the interrupt
61 source is automatically enabled. Initially, the dup-ed handle is in the
62 disabled state and must be enabled before it can be used by calling
63 ddi_intr_enable(). Likewise, ddi_intr_disable() must be called to dis‐
64 able the enabled dup-ed interrupt source.
65
66
67 A dup-ed interrupt is removed by calling ddi_intr_free() after it has
68 been disabled. The ddi_intr_remove_handler(9F) call is not required for
69 a dup-ed handle.
70
71
72 Before removing the original MSI-X interrupt handler, all dup-ed inter‐
73 rupt handlers associated with this MSI-X interrupt must have been dis‐
74 abled and freed. Otherwise, calls to ddi_intr_remove_handler() will
75 fail with DDI_FAILURE.
76
77
78 See the EXAMPLES section for code that illustrates the use of the
79 ddi_intr_dup_handler() function.
80
82 The ddi_intr_dup_handler() function returns:
83
84 DDI_SUCCESS On success.
85
86 Note that the interface should be verified to ensure
87 that the return value is not equal to DDI_SUCCESS.
88 Incomplete checking for failure codes could result in
89 inconsistent behavior among platforms.
90
91
92 DDI_EINVAL On encountering invalid input parameters. DDI_EINVAL is
93 also returned if a dup is attempted from a dup-ed inter‐
94 rupt or if the hardware device is found not to support
95 MSI-X interrupts.
96
97
98 DDI_FAILURE On any implementation specific failure.
99
100
102 Example 1 Using the ddi_intr_dup_handler() function
103
104 int
105 add_msix_interrupts(intr_state_t *state)
106 {
107 int x, y;
108
109 /*
110 * For this example, assume the device supports multiple
111 * interrupt vectors, but only request to be allocated
112 * 1 MSI-X to use and then dup the rest.
113 */
114 if (ddi_intr_get_nintrs(state->dip, DDI_INTR_TYPE_MSIX,
115 &state->intr_count) != DDI_SUCCESS) {
116 cmn_err(CE_WARN, "Failed to retrieve the MSI-X interrupt count");
117 return (DDI_FAILURE);
118 }
119
120 state->intr_size = state->intr_count * sizeof (ddi_intr_handle_t);
121 state->intr_htable = kmem_zalloc(state->intr_size, KM_SLEEP);
122
123 /* Allocate one MSI-X interrupt handle */
124 if (ddi_intr_alloc(state->dip, state->intr_htable,
125 DDI_INTR_TYPE_MSIX, state->inum, 1, &state->actual,
126 DDI_INTR_ALLOC_STRICT) != DDI_SUCCESS) {
127 cmn_err(CE_WARN, "Failed to allocate MSI-X interrupt");
128 kmem_free(state->intr_htable, state->intr_size);
129 return (DDI_FAILURE);
130 }
131
132 /* Get the count of how many MSI-X interrupts we dup */
133 state->dup_cnt = state->intr_count - state->actual;
134
135 if (ddi_intr_get_pri(state->intr_htable[0],
136 &state->intr_pri) != DDI_SUCCESS) {
137 cmn_err(CE_WARN, "Failed to get interrupt priority");
138 goto error1;
139 }
140
141 /* Make sure the MSI-X priority is below 'high level' */
142 if (state->intr_pri >= ddi_intr_get_hilevel_pri()) {
143 cmn_err(CE_WARN, "Interrupt PRI is too high");
144 goto error1;
145 }
146
147 /*
148 * Add the handler for the interrupt
149 */
150 if (ddi_intr_add_handler(state->intr_htable[0],
151 (ddi_intr_handler_t *)intr_isr, (caddr_t)state,
152 NULL) != DDI_SUCCESS) {
153 cmn_err(CE_WARN, "Failed to add interrupt handler");
154 goto error1;
155 }
156
157 /* Enable the main MSI-X handle first */
158 if (ddi_intr_enable(state->intr_htable[0]) != DDI_SUCCESS) {
159 cmn_err(CE_WARN, "Failed to enable interrupt");
160 goto error2;
161 }
162
163 /*
164 * Create and enable dups of the original MSI-X handler, note
165 * that the inum we are using starts at 0.
166 */
167 for (x = 1; x < state->dup_cnt; x++) {
168 if (ddi_intr_dup_handler(state->intr_htable[0],
169 state->inum + x, &state->intr_htable[x]) != DDI_SUCCESS) {
170 for (y = x - 1; y > 0; y--) {
171 (void) ddi_intr_disable(state->intr_htable[y]);
172 (void) ddi_intr_free(state->intr_htable[y]);
173 }
174
175 goto error2;
176 }
177 if (ddi_intr_enable(state->intr_htable[x]) != DDI_SUCCESS) {
178 for (y = x; y > 0; y--) {
179 (void) ddi_intr_disable(state->intr_htable[y]);
180 (void) ddi_intr_free(state->intr_htable[y]);
181 }
182
183 goto error2;
184 }
185 }
186
187 return (DDI_SUCCESS);
188
189 error2:
190 (void) ddi_intr_remove_handler(state->intr_htable[0]);
191 error1:
192 (void) ddi_intr_free(state->intr_htable[0]);
193
194 kmem_free(state->intr_htable, state->intr_size);
195 return (DDI_FAILURE);
196 }
197
198 void
199 remove_msix_interrupts(intr_state_t *state)
200 {
201 int x;
202
203 /*
204 * Disable all the handles and free the dup-ed handles
205 * before we can remove the main MSI-X interrupt handle.
206 */
207 for (x = 1; x < state->dup_cnt; x++) {
208 (void) ddi_intr_disable(state->intr_htable[x]);
209 (void) ddi_intr_free(state->intr_htable[x]);
210 }
211
212 /*
213 * We can remove and free the main MSI-X handler now
214 * that all the dups have been freed.
215 */
216 (void) ddi_intr_disable(state->intr_htable[0]);
217 (void) ddi_intr_remove_handler(state->intr_htable[0]);
218 (void) ddi_intr_free(state->intr_htable[0]);
219
220 kmem_free(state->intr_htable, state->intr_size);
221 }
222
223
225 The ddi_intr_dup_handler() function can be called from kernel non-
226 interrupt context.
227
229 See attributes(5) for descriptions of the following attributes:
230
231
232
233
234 ┌─────────────────────────────┬─────────────────────────────┐
235 │ ATTRIBUTE TYPE │ ATTRIBUTE VALUE │
236 ├─────────────────────────────┼─────────────────────────────┤
237 │Interface Stability │Committed │
238 └─────────────────────────────┴─────────────────────────────┘
239
241 attributes(5), ddi_intr_add_handler(9F), ddi_intr_alloc(9F),
242 ddi_intr_clr_mask(9F), ddi_intr_disable(9F), ddi_intr_enable(9F),
243 ddi_intr_free(9F), ddi_intr_get_pending(9F), ddi_intr_get_sup‐
244 ported_types(9F), ddi_intr_set_mask(9F)
245
246
247 Writing Device Drivers
248
249
250
251SunOS 5.11 09 May 2006 ddi_intr_dup_handler(9F)