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:
John Baldwin 2009-10-15 14:54:35 +00:00
parent 54dd21686d
commit 37b8ef16cd
13 changed files with 290 additions and 4 deletions

View 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.

View File

@ -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 \

View File

@ -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.

View File

@ -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)
{

View File

@ -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);

View File

@ -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.

View File

@ -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)
{

View File

@ -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);

View File

@ -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.

View File

@ -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.

View File

@ -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().
*

View File

@ -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,

View File

@ -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);