summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2009-10-15 14:54:35 +0000
committerjhb <jhb@FreeBSD.org>2009-10-15 14:54:35 +0000
commit45688ed39df1a016dde3d3f3cb4f19e562a683c2 (patch)
tree822343884edf055be1b27eb1097d39bdd975cf43 /sys
parentc369f72e79562af70cc2ac640a8099842519403d (diff)
downloadFreeBSD-src-45688ed39df1a016dde3d3f3cb4f19e562a683c2.zip
FreeBSD-src-45688ed39df1a016dde3d3f3cb4f19e562a683c2.tar.gz
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
Diffstat (limited to 'sys')
-rw-r--r--sys/amd64/amd64/intr_machdep.c17
-rw-r--r--sys/amd64/amd64/nexus.c12
-rw-r--r--sys/amd64/include/intr_machdep.h1
-rw-r--r--sys/i386/i386/intr_machdep.c17
-rw-r--r--sys/i386/i386/nexus.c12
-rw-r--r--sys/i386/include/intr_machdep.h1
-rw-r--r--sys/kern/bus_if.m20
-rw-r--r--sys/kern/kern_intr.c59
-rw-r--r--sys/kern/subr_bus.c40
-rw-r--r--sys/sys/bus.h5
-rw-r--r--sys/sys/interrupt.h4
11 files changed, 184 insertions, 4 deletions
diff --git a/sys/amd64/amd64/intr_machdep.c b/sys/amd64/amd64/intr_machdep.c
index 212ac0d..e72e13a 100644
--- a/sys/amd64/amd64/intr_machdep.c
+++ b/sys/amd64/amd64/intr_machdep.c
@@ -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.
diff --git a/sys/amd64/amd64/nexus.c b/sys/amd64/amd64/nexus.c
index 5eafd3b..61cb587 100644
--- a/sys/amd64/amd64/nexus.c
+++ b/sys/amd64/amd64/nexus.c
@@ -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)
{
diff --git a/sys/amd64/include/intr_machdep.h b/sys/amd64/include/intr_machdep.h
index 634db19..6cd4eee 100644
--- a/sys/amd64/include/intr_machdep.h
+++ b/sys/amd64/include/intr_machdep.h
@@ -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);
diff --git a/sys/i386/i386/intr_machdep.c b/sys/i386/i386/intr_machdep.c
index 3d1b0c4..5a74a9d 100644
--- a/sys/i386/i386/intr_machdep.c
+++ b/sys/i386/i386/intr_machdep.c
@@ -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.
diff --git a/sys/i386/i386/nexus.c b/sys/i386/i386/nexus.c
index 98adc93..8eacc6a 100644
--- a/sys/i386/i386/nexus.c
+++ b/sys/i386/i386/nexus.c
@@ -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)
{
diff --git a/sys/i386/include/intr_machdep.h b/sys/i386/include/intr_machdep.h
index f21e0bcf..bcff6c2 100644
--- a/sys/i386/include/intr_machdep.h
+++ b/sys/i386/include/intr_machdep.h
@@ -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);
diff --git a/sys/kern/bus_if.m b/sys/kern/bus_if.m
index c1c0e34..c0924c8 100644
--- a/sys/kern/bus_if.m
+++ b/sys/kern/bus_if.m
@@ -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.
@@ -527,6 +526,25 @@ METHOD int config_intr {
} 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.
*
diff --git a/sys/kern/kern_intr.c b/sys/kern/kern_intr.c
index 3eb2c6c..42d5f16 100644
--- a/sys/kern/kern_intr.c
+++ b/sys/kern/kern_intr.c
@@ -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)
@@ -665,6 +665,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.
*/
diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c
index 3f51c5c..e4d1f9b 100644
--- a/sys/kern/subr_bus.c
+++ b/sys/kern/subr_bus.c
@@ -3520,6 +3520,24 @@ bus_generic_config_intr(device_t dev, int irq, enum intr_trigger trig,
}
/**
+ * @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().
*
* This simple implementation of BUS_GET_DMA_TAG() simply calls the
@@ -3824,6 +3842,28 @@ bus_bind_intr(device_t dev, struct resource *r, int 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().
*
* This function simply calls the BUS_SET_RESOURCE() method of the
diff --git a/sys/sys/bus.h b/sys/sys/bus.h
index 794dc47..454f510 100644
--- a/sys/sys/bus.h
+++ b/sys/sys/bus.h
@@ -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,
diff --git a/sys/sys/interrupt.h b/sys/sys/interrupt.h
index 92c0f03..18f73d9 100644
--- a/sys/sys/interrupt.h
+++ b/sys/sys/interrupt.h
@@ -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);
OpenPOWER on IntegriCloud