DDI_INTR_DUP_HANDLER(9F) Kernel Functions for Drivers

NAME


ddi_intr_dup_handler - reuse interrupt handler and arguments for MSI-
X interrupts

SYNOPSIS


#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>

int ddi_intr_dup_handler(ddi_intr_handle_t primary, int vector,
ddi_intr_handle_t *new);


INTERFACE LEVEL


illumos DDI specific (illumos DDI).

PARAMETERS


primary
Original DDI interrupt handle


vector
Interrupt number to duplicate


new
Pointer to new DDI interrupt handle


DESCRIPTION


The ddi_intr_dup_handler() function is a feature for MSI-X interrupts
that allows an unallocated interrupt vector of a device to use a
previously initialized or added primary MSI-X interrupt vector in
order to share the same vector address, vector data, interrupt
handler, and handler arguments. This feature allows a driver to alias
the resources provided by the illumos Operating System to the
unallocated interrupt vectors on an associated device. For example,
if 2 MSI-X interrupts were allocated to a driver and 32 interrupts
were supported on the device, the driver could alias the 2 interrupts
it received to the 30 remaining on the device.


The ddi_intr_dup_handler() function must be called after the primary
interrupt handle has been added to the system or enabled by
ddi_intr_add_handler(9F) and ddi_intr_enable(9F) calls, respectively.
If successful, the function returns the new interrupt handle for a
given vector in the new argument passed to the function. The new
interrupt handle must not have been previously allocated with
ddi_intr_alloc(9F). Otherwise, the ddi_intr_dup_handler() call will
fail.


The only supported calls on dup-ed interrupt handles are
ddi_intr_set_mask(9F), ddi_intr_clr_mask(9F),
ddi_intr_get_pending(9F), ddi_intr_enable(9F), ddi_intr_disable(9F),
and ddi_intr_free(9F).


A call to ddi_intr_dup_handler() does not imply that the interrupt
source is automatically enabled. Initially, the dup-ed handle is in
the disabled state and must be enabled before it can be used by
calling ddi_intr_enable(). Likewise, ddi_intr_disable() must be
called to disable the enabled dup-ed interrupt source.


A dup-ed interrupt is removed by calling ddi_intr_free() after it has
been disabled. The ddi_intr_remove_handler(9F) call is not required
for a dup-ed handle.


Before removing the original MSI-X interrupt handler, all dup-ed
interrupt handlers associated with this MSI-X interrupt must have
been disabled and freed. Otherwise, calls to
ddi_intr_remove_handler() will fail with DDI_FAILURE.


See the EXAMPLES section for code that illustrates the use of the
ddi_intr_dup_handler() function.

RETURN VALUES


The ddi_intr_dup_handler() function returns:

DDI_SUCCESS
On success.

Note that the interface should be verified to ensure
that the return value is not equal to DDI_SUCCESS.
Incomplete checking for failure codes could result in
inconsistent behavior among platforms.


DDI_EINVAL
On encountering invalid input parameters. DDI_EINVAL
is also returned if a dup is attempted from a dup-ed
interrupt or if the hardware device is found not to
support MSI-X interrupts.


DDI_FAILURE
On any implementation specific failure.


EXAMPLES


Example 1: Using the ddi_intr_dup_handler() function



int
add_msix_interrupts(intr_state_t *state)
{
int x, y;

/*
* For this example, assume the device supports multiple
* interrupt vectors, but only request to be allocated
* 1 MSI-X to use and then dup the rest.
*/
if (ddi_intr_get_nintrs(state->dip, DDI_INTR_TYPE_MSIX,
&state->intr_count) != DDI_SUCCESS) {
cmn_err(CE_WARN, "Failed to retrieve the MSI-X interrupt count");
return (DDI_FAILURE);
}

state->intr_size = state->intr_count * sizeof (ddi_intr_handle_t);
state->intr_htable = kmem_zalloc(state->intr_size, KM_SLEEP);

/* Allocate one MSI-X interrupt handle */
if (ddi_intr_alloc(state->dip, state->intr_htable,
DDI_INTR_TYPE_MSIX, state->inum, 1, &state->actual,
DDI_INTR_ALLOC_STRICT) != DDI_SUCCESS) {
cmn_err(CE_WARN, "Failed to allocate MSI-X interrupt");
kmem_free(state->intr_htable, state->intr_size);
return (DDI_FAILURE);
}

/* Get the count of how many MSI-X interrupts we dup */
state->dup_cnt = state->intr_count - state->actual;

if (ddi_intr_get_pri(state->intr_htable[0],
&state->intr_pri) != DDI_SUCCESS) {
cmn_err(CE_WARN, "Failed to get interrupt priority");
goto error1;
}

/* Make sure the MSI-X priority is below 'high level' */
if (state->intr_pri >= ddi_intr_get_hilevel_pri()) {
cmn_err(CE_WARN, "Interrupt PRI is too high");
goto error1;
}

/*
* Add the handler for the interrupt
*/
if (ddi_intr_add_handler(state->intr_htable[0],
(ddi_intr_handler_t *)intr_isr, (caddr_t)state,
NULL) != DDI_SUCCESS) {
cmn_err(CE_WARN, "Failed to add interrupt handler");
goto error1;
}

/* Enable the main MSI-X handle first */
if (ddi_intr_enable(state->intr_htable[0]) != DDI_SUCCESS) {
cmn_err(CE_WARN, "Failed to enable interrupt");
goto error2;
}

/*
* Create and enable dups of the original MSI-X handler, note
* that the inum we are using starts at 0.
*/
for (x = 1; x < state->dup_cnt; x++) {
if (ddi_intr_dup_handler(state->intr_htable[0],
state->inum + x, &state->intr_htable[x]) != DDI_SUCCESS) {
for (y = x - 1; y > 0; y--) {
(void) ddi_intr_disable(state->intr_htable[y]);
(void) ddi_intr_free(state->intr_htable[y]);
}

goto error2;
}
if (ddi_intr_enable(state->intr_htable[x]) != DDI_SUCCESS) {
for (y = x; y > 0; y--) {
(void) ddi_intr_disable(state->intr_htable[y]);
(void) ddi_intr_free(state->intr_htable[y]);
}

goto error2;
}
}

return (DDI_SUCCESS);

error2:
(void) ddi_intr_remove_handler(state->intr_htable[0]);
error1:
(void) ddi_intr_free(state->intr_htable[0]);

kmem_free(state->intr_htable, state->intr_size);
return (DDI_FAILURE);
}

void
remove_msix_interrupts(intr_state_t *state)
{
int x;

/*
* Disable all the handles and free the dup-ed handles
* before we can remove the main MSI-X interrupt handle.
*/
for (x = 1; x < state->dup_cnt; x++) {
(void) ddi_intr_disable(state->intr_htable[x]);
(void) ddi_intr_free(state->intr_htable[x]);
}

/*
* We can remove and free the main MSI-X handler now
* that all the dups have been freed.
*/
(void) ddi_intr_disable(state->intr_htable[0]);
(void) ddi_intr_remove_handler(state->intr_htable[0]);
(void) ddi_intr_free(state->intr_htable[0]);

kmem_free(state->intr_htable, state->intr_size);
}


CONTEXT


The ddi_intr_dup_handler() function can be called from kernel non-
interrupt context.

ATTRIBUTES


See attributes(7) for descriptions of the following attributes:


+--------------------+-----------------+
| ATTRIBUTE TYPE | ATTRIBUTE VALUE |
+--------------------+-----------------+
|Interface Stability | Committed |
+--------------------+-----------------+

SEE ALSO


attributes(7), ddi_intr_add_handler(9F), ddi_intr_alloc(9F),
ddi_intr_clr_mask(9F), ddi_intr_disable(9F), ddi_intr_enable(9F),
ddi_intr_free(9F), ddi_intr_get_pending(9F),
ddi_intr_get_supported_types(9F), ddi_intr_set_mask(9F)


Writing Device Drivers

May 9, 2006 DDI_INTR_DUP_HANDLER(9F)

tribblix@gmail.com :: GitHub :: Privacy