Add a facility for associating optional descriptions with active interrupt
handlers. This is primarily intended as a way to allow devices that use multiple interrupts (e.g. MSI) to meaningfully distinguish the various interrupt handlers. - Add a new BUS_DESCRIBE_INTR() method to the bus interface to associate a description with an active interrupt handler setup by BUS_SETUP_INTR. It has a default method (bus_generic_describe_intr()) which simply passes the request up to the parent device. - Add a bus_describe_intr() wrapper around BUS_DESCRIBE_INTR() that supports printf(9) style formatting using var args. - Reserve MAXCOMLEN bytes in the intr_handler structure to hold the name of an interrupt handler and copy the name passed to intr_event_add_handler() into that buffer instead of just saving the pointer to the name. - Add a new intr_event_describe_handler() which appends a description string to an interrupt handler's name. - Implement support for interrupt descriptions on amd64 and i386 by having the nexus(4) driver supply a custom bus_describe_intr method that invokes a new intr_describe() MD routine which in turn looks up the associated interrupt event and invokes intr_event_describe_handler(). Requested by: many Reviewed by: scottl MFC after: 2 weeks
This commit is contained in:
parent
54dd21686d
commit
37b8ef16cd
104
share/man/man9/BUS_DESCRIBE_INTR.9
Normal file
104
share/man/man9/BUS_DESCRIBE_INTR.9
Normal file
@ -0,0 +1,104 @@
|
||||
.\" -*- nroff -*-
|
||||
.\"
|
||||
.\" Copyright (c) 2009 Advanced Computing Technologies LLC
|
||||
.\" Written by: John H. Baldwin <jhb@FreeBSD.org>
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd October 14, 2009
|
||||
.Dt BUS_DESCRIBE_INTR 9
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm BUS_DESCRIBE_INTR ,
|
||||
.Nm bus_describe_intr
|
||||
.Nd "associate a description with an active interrupt handler"
|
||||
.Sh SYNOPSIS
|
||||
.In sys/param.h
|
||||
.In sys/bus.h
|
||||
.Ft int
|
||||
.Fo BUS_BIND_INTR
|
||||
.Fa "device_t dev" "device_t child" "struct resource *irq" "void *cookie"
|
||||
.Fa "const char *descr"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo bus_describe_intr
|
||||
.Fa "device_t dev" "struct resource *irq" "void *cookie" "const char *fmt"
|
||||
.Fa ...
|
||||
.Fc
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fn BUS_DESCRIBE_INTR
|
||||
method associates a description with an active interrupt handler.
|
||||
The
|
||||
.Fa cookie
|
||||
parameter must be the value returned by a successful call to
|
||||
.Xr BUS_SETUP_INTR 9
|
||||
for the interrupt
|
||||
.Fa irq .
|
||||
.Pp
|
||||
The
|
||||
.Fn bus_describe_intr
|
||||
function is a simple wrapper around
|
||||
.Fn BUS_DESCRIBE_INTR .
|
||||
As a convenience,
|
||||
.Fn bus_describe_intr
|
||||
allows the caller to use
|
||||
.Xr printf 9
|
||||
style formatting to build the description string using
|
||||
.Fa fmt .
|
||||
.Pp
|
||||
When an interrupt handler is established by
|
||||
.Xr BUS_SETUP_INTR 9 ,
|
||||
the handler is named after the device the handler is established for.
|
||||
This name is then used in various places such as interrupt statistics
|
||||
displayed by
|
||||
.Xr systat 1
|
||||
and
|
||||
.Xr vmstat 8 .
|
||||
For devices that use a single interrupt,
|
||||
the device name is sufficiently unique to identify the interrupt handler.
|
||||
However, for devices that use multiple interrupts it can be useful to
|
||||
distinguish the interrupt handlers.
|
||||
When a description is set for an active interrupt handler,
|
||||
a colon followed by the description is appended to the device name to form
|
||||
the interrupt handler name.
|
||||
.Sh RETURN VALUES
|
||||
Zero is returned on success, otherwise an appropriate error is returned.
|
||||
.Sh SEE ALSO
|
||||
.Xr BUS_SETUP_INTR 9 ,
|
||||
.Xr device 9 ,
|
||||
.Xr printf 9 ,
|
||||
.Xr systat 1 ,
|
||||
.Xr vmstat 8
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Fn BUS_DESCRIBE_INTR
|
||||
method and
|
||||
.Fn bus_describe_intr
|
||||
functions first appeared in
|
||||
.Fx 9.0 .
|
||||
.Sh BUGS
|
||||
It is not currently possible to remove a description from an active interrupt
|
||||
handler.
|
@ -26,6 +26,7 @@ MAN= accept_filter.9 \
|
||||
BUS_BIND_INTR.9 \
|
||||
bus_child_present.9 \
|
||||
BUS_CONFIG_INTR.9 \
|
||||
BUS_DESCRIBE_INTR.9 \
|
||||
bus_dma.9 \
|
||||
bus_generic_attach.9 \
|
||||
bus_generic_detach.9 \
|
||||
@ -405,6 +406,7 @@ MLINKS+=buf.9 bp.9
|
||||
MLINKS+=bus_activate_resource.9 bus_deactivate_resource.9
|
||||
MLINKS+=bus_alloc_resource.9 bus_alloc_resource_any.9
|
||||
MLINKS+=BUS_BIND_INTR.9 bus_bind_intr.9
|
||||
MLINKS+=BUS_DESCRIBE_INTR.9 bus_describe_intr.9
|
||||
MLINKS+=bus_dma.9 busdma.9 \
|
||||
bus_dma.9 bus_dmamap_create.9 \
|
||||
bus_dma.9 bus_dmamap_destroy.9 \
|
||||
|
@ -466,6 +466,23 @@ intr_bind(u_int vector, u_char cpu)
|
||||
return (intr_event_bind(isrc->is_event, cpu));
|
||||
}
|
||||
|
||||
/* Add a description to an active interrupt handler. */
|
||||
int
|
||||
intr_describe(u_int vector, void *ih, const char *descr)
|
||||
{
|
||||
struct intsrc *isrc;
|
||||
int error;
|
||||
|
||||
isrc = intr_lookup_source(vector);
|
||||
if (isrc == NULL)
|
||||
return (EINVAL);
|
||||
error = intr_event_describe_handler(isrc->is_event, ih, descr);
|
||||
if (error)
|
||||
return (error);
|
||||
intrcnt_updatename(isrc);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a CPU to our mask of valid CPUs that can be destinations of
|
||||
* interrupts.
|
||||
|
@ -92,6 +92,9 @@ static int nexus_bind_intr(device_t, device_t, struct resource *, int);
|
||||
#endif
|
||||
static int nexus_config_intr(device_t, int, enum intr_trigger,
|
||||
enum intr_polarity);
|
||||
static int nexus_describe_intr(device_t dev, device_t child,
|
||||
struct resource *irq, void *cookie,
|
||||
const char *descr);
|
||||
static int nexus_activate_resource(device_t, device_t, int, int,
|
||||
struct resource *);
|
||||
static int nexus_deactivate_resource(device_t, device_t, int, int,
|
||||
@ -135,6 +138,7 @@ static device_method_t nexus_methods[] = {
|
||||
DEVMETHOD(bus_bind_intr, nexus_bind_intr),
|
||||
#endif
|
||||
DEVMETHOD(bus_config_intr, nexus_config_intr),
|
||||
DEVMETHOD(bus_describe_intr, nexus_describe_intr),
|
||||
DEVMETHOD(bus_get_resource_list, nexus_get_reslist),
|
||||
DEVMETHOD(bus_set_resource, nexus_set_resource),
|
||||
DEVMETHOD(bus_get_resource, nexus_get_resource),
|
||||
@ -479,6 +483,14 @@ nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
|
||||
return (intr_config_intr(irq, trig, pol));
|
||||
}
|
||||
|
||||
static int
|
||||
nexus_describe_intr(device_t dev, device_t child, struct resource *irq,
|
||||
void *cookie, const char *descr)
|
||||
{
|
||||
|
||||
return (intr_describe(rman_get_start(irq), cookie, descr));
|
||||
}
|
||||
|
||||
static struct resource_list *
|
||||
nexus_get_reslist(device_t dev, device_t child)
|
||||
{
|
||||
|
@ -151,6 +151,7 @@ int intr_bind(u_int vector, u_char cpu);
|
||||
#endif
|
||||
int intr_config_intr(int vector, enum intr_trigger trig,
|
||||
enum intr_polarity pol);
|
||||
int intr_describe(u_int vector, void *ih, const char *descr);
|
||||
void intr_execute_handlers(struct intsrc *isrc, struct trapframe *frame);
|
||||
u_int intr_next_cpu(void);
|
||||
struct intsrc *intr_lookup_source(int vector);
|
||||
|
@ -432,6 +432,23 @@ intr_bind(u_int vector, u_char cpu)
|
||||
return (intr_event_bind(isrc->is_event, cpu));
|
||||
}
|
||||
|
||||
/* Add a description to an active interrupt handler. */
|
||||
int
|
||||
intr_describe(u_int vector, void *ih, const char *descr)
|
||||
{
|
||||
struct intsrc *isrc;
|
||||
int error;
|
||||
|
||||
isrc = intr_lookup_source(vector);
|
||||
if (isrc == NULL)
|
||||
return (EINVAL);
|
||||
error = intr_event_describe_handler(isrc->is_event, ih, descr);
|
||||
if (error)
|
||||
return (error);
|
||||
intrcnt_updatename(isrc);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a CPU to our mask of valid CPUs that can be destinations of
|
||||
* interrupts.
|
||||
|
@ -96,6 +96,9 @@ static int nexus_bind_intr(device_t, device_t, struct resource *, int);
|
||||
#endif
|
||||
static int nexus_config_intr(device_t, int, enum intr_trigger,
|
||||
enum intr_polarity);
|
||||
static int nexus_describe_intr(device_t dev, device_t child,
|
||||
struct resource *irq, void *cookie,
|
||||
const char *descr);
|
||||
static int nexus_activate_resource(device_t, device_t, int, int,
|
||||
struct resource *);
|
||||
static int nexus_deactivate_resource(device_t, device_t, int, int,
|
||||
@ -141,6 +144,7 @@ static device_method_t nexus_methods[] = {
|
||||
DEVMETHOD(bus_bind_intr, nexus_bind_intr),
|
||||
#endif
|
||||
DEVMETHOD(bus_config_intr, nexus_config_intr),
|
||||
DEVMETHOD(bus_describe_intr, nexus_describe_intr),
|
||||
DEVMETHOD(bus_get_resource_list, nexus_get_reslist),
|
||||
DEVMETHOD(bus_set_resource, nexus_set_resource),
|
||||
DEVMETHOD(bus_get_resource, nexus_get_resource),
|
||||
@ -526,6 +530,14 @@ nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
|
||||
return (intr_config_intr(irq, trig, pol));
|
||||
}
|
||||
|
||||
static int
|
||||
nexus_describe_intr(device_t dev, device_t child, struct resource *irq,
|
||||
void *cookie, const char *descr)
|
||||
{
|
||||
|
||||
return (intr_describe(rman_get_start(irq), cookie, descr));
|
||||
}
|
||||
|
||||
static struct resource_list *
|
||||
nexus_get_reslist(device_t dev, device_t child)
|
||||
{
|
||||
|
@ -138,6 +138,7 @@ int intr_bind(u_int vector, u_char cpu);
|
||||
#endif
|
||||
int intr_config_intr(int vector, enum intr_trigger trig,
|
||||
enum intr_polarity pol);
|
||||
int intr_describe(u_int vector, void *ih, const char *descr);
|
||||
void intr_execute_handlers(struct intsrc *isrc, struct trapframe *frame);
|
||||
u_int intr_next_cpu(void);
|
||||
struct intsrc *intr_lookup_source(int vector);
|
||||
|
@ -509,7 +509,6 @@ METHOD int bind_intr {
|
||||
int _cpu;
|
||||
} DEFAULT bus_generic_bind_intr;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Allow (bus) drivers to specify the trigger mode and polarity
|
||||
* of the specified interrupt.
|
||||
@ -526,6 +525,25 @@ METHOD int config_intr {
|
||||
enum intr_polarity _pol;
|
||||
} DEFAULT bus_generic_config_intr;
|
||||
|
||||
/**
|
||||
* @brief Allow drivers to associate a description with an active
|
||||
* interrupt handler.
|
||||
*
|
||||
* @param _dev the parent device of @p _child
|
||||
* @param _child the device which allocated the resource
|
||||
* @param _irq the resource representing the interrupt
|
||||
* @param _cookie the cookie value returned when the interrupt
|
||||
* was originally registered
|
||||
* @param _descr the description to associate with the interrupt
|
||||
*/
|
||||
METHOD int describe_intr {
|
||||
device_t _dev;
|
||||
device_t _child;
|
||||
struct resource *_irq;
|
||||
void *_cookie;
|
||||
const char *_descr;
|
||||
} DEFAULT bus_generic_describe_intr;
|
||||
|
||||
/**
|
||||
* @brief Notify a (bus) driver about a child that the hints mechanism
|
||||
* believes it has discovered.
|
||||
|
@ -524,7 +524,7 @@ intr_event_add_handler(struct intr_event *ie, const char *name,
|
||||
ih->ih_filter = filter;
|
||||
ih->ih_handler = handler;
|
||||
ih->ih_argument = arg;
|
||||
ih->ih_name = name;
|
||||
strlcpy(ih->ih_name, name, sizeof(ih->ih_name));
|
||||
ih->ih_event = ie;
|
||||
ih->ih_pri = pri;
|
||||
if (flags & INTR_EXCL)
|
||||
@ -597,7 +597,7 @@ intr_event_add_handler(struct intr_event *ie, const char *name,
|
||||
ih->ih_filter = filter;
|
||||
ih->ih_handler = handler;
|
||||
ih->ih_argument = arg;
|
||||
ih->ih_name = name;
|
||||
strlcpy(ih->ih_name, name, sizeof(ih->ih_name));
|
||||
ih->ih_event = ie;
|
||||
ih->ih_pri = pri;
|
||||
if (flags & INTR_EXCL)
|
||||
@ -664,6 +664,61 @@ intr_event_add_handler(struct intr_event *ie, const char *name,
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Append a description preceded by a ':' to the name of the specified
|
||||
* interrupt handler.
|
||||
*/
|
||||
int
|
||||
intr_event_describe_handler(struct intr_event *ie, void *cookie,
|
||||
const char *descr)
|
||||
{
|
||||
struct intr_handler *ih;
|
||||
size_t space;
|
||||
char *start;
|
||||
|
||||
mtx_lock(&ie->ie_lock);
|
||||
#ifdef INVARIANTS
|
||||
TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) {
|
||||
if (ih == cookie)
|
||||
break;
|
||||
}
|
||||
if (ih == NULL) {
|
||||
mtx_unlock(&ie->ie_lock);
|
||||
panic("handler %p not find in interrupt event %p", cookie, ie);
|
||||
}
|
||||
#endif
|
||||
ih = cookie;
|
||||
|
||||
/*
|
||||
* Look for an existing description by checking for an
|
||||
* existing ":". This assumes device names do not include
|
||||
* colons. If one is found, prepare to insert the new
|
||||
* description at that point. If one is not found, find the
|
||||
* end of the name to use as the insertion point.
|
||||
*/
|
||||
start = index(ih->ih_name, ':');
|
||||
if (start == NULL)
|
||||
start = index(ih->ih_name, 0);
|
||||
|
||||
/*
|
||||
* See if there is enough remaining room in the string for the
|
||||
* description + ":". The "- 1" leaves room for the trailing
|
||||
* '\0'. The "+ 1" accounts for the colon.
|
||||
*/
|
||||
space = sizeof(ih->ih_name) - (start - ih->ih_name) - 1;
|
||||
if (strlen(descr) + 1 > space) {
|
||||
mtx_unlock(&ie->ie_lock);
|
||||
return (ENOSPC);
|
||||
}
|
||||
|
||||
/* Append a colon followed by the description. */
|
||||
*start = ':';
|
||||
strcpy(start + 1, descr);
|
||||
intr_event_update(ie);
|
||||
mtx_unlock(&ie->ie_lock);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the ie_source field from the intr_event an intr_handler is
|
||||
* associated with.
|
||||
|
@ -3519,6 +3519,24 @@ bus_generic_config_intr(device_t dev, int irq, enum intr_trigger trig,
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function for implementing BUS_DESCRIBE_INTR().
|
||||
*
|
||||
* This simple implementation of BUS_DESCRIBE_INTR() simply calls the
|
||||
* BUS_DESCRIBE_INTR() method of the parent of @p dev.
|
||||
*/
|
||||
int
|
||||
bus_generic_describe_intr(device_t dev, device_t child, struct resource *irq,
|
||||
void *cookie, const char *descr)
|
||||
{
|
||||
|
||||
/* Propagate up the bus hierarchy until someone handles it. */
|
||||
if (dev->parent)
|
||||
return (BUS_DESCRIBE_INTR(dev->parent, child, irq, cookie,
|
||||
descr));
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function for implementing BUS_GET_DMA_TAG().
|
||||
*
|
||||
@ -3823,6 +3841,28 @@ bus_bind_intr(device_t dev, struct resource *r, int cpu)
|
||||
return (BUS_BIND_INTR(dev->parent, dev, r, cpu));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wrapper function for BUS_DESCRIBE_INTR().
|
||||
*
|
||||
* This function first formats the requested description into a
|
||||
* temporary buffer and then calls the BUS_DESCRIBE_INTR() method of
|
||||
* the parent of @p dev.
|
||||
*/
|
||||
int
|
||||
bus_describe_intr(device_t dev, struct resource *irq, void *cookie,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
char descr[MAXCOMLEN];
|
||||
va_list ap;
|
||||
|
||||
if (dev->parent == NULL)
|
||||
return (EINVAL);
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(descr, sizeof(descr), fmt, ap);
|
||||
va_end(ap);
|
||||
return (BUS_DESCRIBE_INTR(dev->parent, dev, irq, cookie, descr));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wrapper function for BUS_SET_RESOURCE().
|
||||
*
|
||||
|
@ -292,6 +292,9 @@ int bus_generic_bind_intr(device_t dev, device_t child,
|
||||
int bus_generic_child_present(device_t dev, device_t child);
|
||||
int bus_generic_config_intr(device_t, int, enum intr_trigger,
|
||||
enum intr_polarity);
|
||||
int bus_generic_describe_intr(device_t dev, device_t child,
|
||||
struct resource *irq, void *cookie,
|
||||
const char *descr);
|
||||
int bus_generic_deactivate_resource(device_t dev, device_t child, int type,
|
||||
int rid, struct resource *r);
|
||||
int bus_generic_detach(device_t dev);
|
||||
@ -363,6 +366,8 @@ int bus_setup_intr(device_t dev, struct resource *r, int flags,
|
||||
void *arg, void **cookiep);
|
||||
int bus_teardown_intr(device_t dev, struct resource *r, void *cookie);
|
||||
int bus_bind_intr(device_t dev, struct resource *r, int cpu);
|
||||
int bus_describe_intr(device_t dev, struct resource *irq, void *cookie,
|
||||
const char *fmt, ...);
|
||||
int bus_set_resource(device_t dev, int type, int rid,
|
||||
u_long start, u_long count);
|
||||
int bus_get_resource(device_t dev, int type, int rid,
|
||||
|
@ -47,7 +47,7 @@ struct intr_handler {
|
||||
driver_intr_t *ih_handler; /* Threaded handler function. */
|
||||
void *ih_argument; /* Argument to pass to handlers. */
|
||||
int ih_flags;
|
||||
const char *ih_name; /* Name of handler. */
|
||||
char ih_name[MAXCOMLEN]; /* Name of handler. */
|
||||
struct intr_event *ih_event; /* Event we are connected to. */
|
||||
int ih_need; /* Needs service. */
|
||||
TAILQ_ENTRY(intr_handler) ih_next; /* Next handler for this event. */
|
||||
@ -172,6 +172,8 @@ int intr_event_destroy(struct intr_event *ie);
|
||||
void intr_event_execute_handlers(struct proc *p, struct intr_event *ie);
|
||||
int intr_event_handle(struct intr_event *ie, struct trapframe *frame);
|
||||
int intr_event_remove_handler(void *cookie);
|
||||
int intr_event_describe_handler(struct intr_event *ie, void *cookie,
|
||||
const char *descr);
|
||||
int intr_getaffinity(int irq, void *mask);
|
||||
void *intr_handler_source(void *cookie);
|
||||
int intr_setaffinity(int irq, void *mask);
|
||||
|
Loading…
Reference in New Issue
Block a user