xref: /illumos-gate/usr/src/man/man9f/ddi_intr_dup_handler.9f (revision 15f90b02bdacbf0ae47fa105944f15b6596f9748)
te
Copyright (c) 2006, Sun Microsystems, Inc. All Rights Reserved.
The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
DDI_INTR_DUP_HANDLER 9F "May 09, 2006"
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