diff --git a/share/man/man9/BUS_DESCRIBE_INTR.9 b/share/man/man9/BUS_DESCRIBE_INTR.9
new file mode 100644
index 000000000000..3da7c429bdd9
--- /dev/null
+++ b/share/man/man9/BUS_DESCRIBE_INTR.9
@@ -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.
diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile
index ae8b55f485e5..072827a2e0f8 100644
--- a/share/man/man9/Makefile
+++ b/share/man/man9/Makefile
@@ -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 \
diff --git a/sys/amd64/amd64/intr_machdep.c b/sys/amd64/amd64/intr_machdep.c
index 212ac0d7b267..e72e13ab2c11 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 5eafd3baf973..61cb58775aea 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 634db19c5a5c..6cd4eee6664c 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 3d1b0c49dfc0..5a74a9d58303 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 98adc933a23b..8eacc6a7601a 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 f21e0bcfb7bc..bcff6c22e6e8 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 c1c0e348eb07..c0924c882ab7 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.
@@ -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.
diff --git a/sys/kern/kern_intr.c b/sys/kern/kern_intr.c
index 3eb2c6ca3f88..42d5f167bd42 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)
@@ -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.
diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c
index 3f51c5cada88..e4d1f9b5a0da 100644
--- a/sys/kern/subr_bus.c
+++ b/sys/kern/subr_bus.c
@@ -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().
  *
diff --git a/sys/sys/bus.h b/sys/sys/bus.h
index 794dc47a8f13..454f51025371 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 92c0f03bfcd9..18f73d98968c 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);