Page MenuHomeFreeBSD

D15755.id43588.diff
No OneTemporary

D15755.id43588.diff

Index: sys/dev/pci/pci.c
===================================================================
--- sys/dev/pci/pci.c
+++ sys/dev/pci/pci.c
@@ -4371,6 +4371,7 @@
pci_suspend_child(device_t dev, device_t child)
{
struct pci_devinfo *dinfo;
+ struct resource_list_entry *rle;
int error;
dinfo = device_get_ivars(child);
@@ -4387,8 +4388,20 @@
if (error)
return (error);
- if (pci_do_power_suspend)
+ if (pci_do_power_suspend) {
+ /*
+ * Make sure this device's interrupt handler is not invoked
+ * in the case the device uses a shared interrupt that can
+ * be raised by some other device.
+ * This is applicable only to regular (legacy) PCI interrupts
+ * as MSI/MSI-X interrupts are never shared.
+ */
+ rle = resource_list_find(&dinfo->resources,
+ SYS_RES_IRQ, 0);
+ if (rle != NULL && rle->res != NULL)
+ (void)bus_suspend_intr(child, rle->res);
pci_set_power_child(dev, child, PCI_POWERSTATE_D3);
+ }
return (0);
}
@@ -4397,6 +4410,7 @@
pci_resume_child(device_t dev, device_t child)
{
struct pci_devinfo *dinfo;
+ struct resource_list_entry *rle;
if (pci_do_power_resume)
pci_set_power_child(dev, child, PCI_POWERSTATE_D0);
@@ -4405,6 +4419,13 @@
pci_cfg_restore(child, dinfo);
if (!device_is_attached(child))
pci_cfg_save(child, dinfo, 1);
+
+ if (pci_do_power_suspend) {
+ /* See pci_suspend_child for details. */
+ rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, 0);
+ if (rle != NULL && rle->res != NULL)
+ (void)bus_resume_intr(child, rle->res);
+ }
bus_generic_resume_child(dev, child);
Index: sys/kern/bus_if.m
===================================================================
--- sys/kern/bus_if.m
+++ sys/kern/bus_if.m
@@ -472,6 +472,46 @@
};
/**
+ * @brief Suspend an interrupt handler
+ *
+ * This method is used to mark a handler as suspended in the case
+ * that the associated device is powered down and cannot be a source
+ * for the, typically shared, interrupt.
+ * The value of @p _cookie must be the value returned from a previous
+ * call to BUS_SETUP_INTR().
+ *
+ * @param _dev the parent device of @p _child
+ * @param _child the device which allocated the resource
+ * @param _cookie the cookie value returned when the interrupt
+ * was originally registered
+ */
+METHOD int suspend_intr {
+ device_t _dev;
+ device_t _child;
+ struct resource *_irq;
+} DEFAULT bus_generic_suspend_intr;
+
+/**
+ * @brief Resume an interrupt handler
+ *
+ * This method is used to clear suspended state of a handler when
+ * the associated device is powered up and can be an interrupt source
+ * again.
+ * The value of @p _cookie must be the value returned from a previous
+ * call to BUS_SETUP_INTR().
+ *
+ * @param _dev the parent device of @p _child
+ * @param _child the device which allocated the resource
+ * @param _cookie the cookie value returned when the interrupt
+ * was originally registered
+ */
+METHOD int resume_intr {
+ device_t _dev;
+ device_t _child;
+ struct resource *_irq;
+} DEFAULT bus_generic_resume_intr;
+
+/**
* @brief Define a resource which can be allocated with
* BUS_ALLOC_RESOURCE().
*
Index: sys/kern/kern_intr.c
===================================================================
--- sys/kern/kern_intr.c
+++ sys/kern/kern_intr.c
@@ -817,6 +817,42 @@
return (0);
}
+int
+intr_event_suspend_handler(void *cookie)
+{
+ struct intr_handler *handler = (struct intr_handler *)cookie;
+ struct intr_event *ie;
+
+ if (handler == NULL)
+ return (EINVAL);
+ ie = handler->ih_event;
+ KASSERT(ie != NULL,
+ ("interrupt handler \"%s\" has a NULL interrupt event",
+ handler->ih_name));
+ mtx_lock(&ie->ie_lock);
+ handler->ih_flags |= IH_SUSP;
+ mtx_unlock(&ie->ie_lock);
+ return (0);
+}
+
+int
+intr_event_resume_handler(void *cookie)
+{
+ struct intr_handler *handler = (struct intr_handler *)cookie;
+ struct intr_event *ie;
+
+ if (handler == NULL)
+ return (EINVAL);
+ ie = handler->ih_event;
+ KASSERT(ie != NULL,
+ ("interrupt handler \"%s\" has a NULL interrupt event",
+ handler->ih_name));
+ mtx_lock(&ie->ie_lock);
+ handler->ih_flags &= ~IH_SUSP;
+ mtx_unlock(&ie->ie_lock);
+ return (0);
+}
+
static int
intr_event_schedule_thread(struct intr_event *ie)
{
@@ -990,6 +1026,10 @@
if (ih->ih_handler == NULL)
continue;
+ /* Skip suspended handlers */
+ if ((ih->ih_flags & IH_SUSP) != 0)
+ continue;
+
/*
* For software interrupt threads, we only execute
* handlers that have their need flag set. Hardware
@@ -1148,7 +1188,8 @@
struct intr_handler *ih;
struct trapframe *oldframe;
struct thread *td;
- int ret, thread;
+ int ret;
+ bool filter, thread;
td = curthread;
@@ -1167,14 +1208,17 @@
* a trapframe as its argument.
*/
td->td_intr_nesting_level++;
- thread = 0;
+ filter = false;
+ thread = false;
ret = 0;
critical_enter();
oldframe = td->td_intr_frame;
td->td_intr_frame = frame;
TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) {
+ if ((ih->ih_flags & IH_SUSP) != 0)
+ continue;
if (ih->ih_filter == NULL) {
- thread = 1;
+ thread = true;
continue;
}
CTR4(KTR_INTR, "%s: exec %p(%p) for %s", __func__,
@@ -1189,24 +1233,25 @@
(ret & ~(FILTER_SCHEDULE_THREAD | FILTER_HANDLED)) == 0),
("%s: incorrect return value %#x from %s", __func__, ret,
ih->ih_name));
+ filter = filter || ret == FILTER_HANDLED;
- /*
+ /*
* Wrapper handler special handling:
*
- * in some particular cases (like pccard and pccbb),
+ * in some particular cases (like pccard and pccbb),
* the _real_ device handler is wrapped in a couple of
* functions - a filter wrapper and an ithread wrapper.
- * In this case (and just in this case), the filter wrapper
+ * In this case (and just in this case), the filter wrapper
* could ask the system to schedule the ithread and mask
* the interrupt source if the wrapped handler is composed
* of just an ithread handler.
*
- * TODO: write a generic wrapper to avoid people rolling
- * their own
+ * TODO: write a generic wrapper to avoid people rolling
+ * their own.
*/
if (!thread) {
if (ret == FILTER_SCHEDULE_THREAD)
- thread = 1;
+ thread = true;
}
}
td->td_intr_frame = oldframe;
@@ -1218,7 +1263,7 @@
if (ie->ie_post_filter != NULL)
ie->ie_post_filter(ie->ie_source);
}
-
+
/* Schedule the ithread if needed. */
if (thread) {
int error __unused;
@@ -1228,6 +1273,10 @@
}
critical_exit();
td->td_intr_nesting_level--;
+
+ /* The interrupt is not aknowledged by any filter and has no ithread. */
+ if (!thread && !filter)
+ return (EINVAL);
return (0);
}
Index: sys/kern/subr_bus.c
===================================================================
--- sys/kern/subr_bus.c
+++ sys/kern/subr_bus.c
@@ -4047,6 +4047,36 @@
}
/**
+ * @brief Helper function for implementing BUS_SUSPEND_INTR().
+ *
+ * This simple implementation of BUS_SUSPEND_INTR() simply calls the
+ * BUS_SUSPEND_INTR() method of the parent of @p dev.
+ */
+int
+bus_generic_suspend_intr(device_t dev, device_t child, struct resource *irq)
+{
+ /* Propagate up the bus hierarchy until someone handles it. */
+ if (dev->parent)
+ return (BUS_SUSPEND_INTR(dev->parent, child, irq));
+ return (EINVAL);
+}
+
+/**
+ * @brief Helper function for implementing BUS_RESUME_INTR().
+ *
+ * This simple implementation of BUS_RESUME_INTR() simply calls the
+ * BUS_RESUME_INTR() method of the parent of @p dev.
+ */
+int
+bus_generic_resume_intr(device_t dev, device_t child, struct resource *irq)
+{
+ /* Propagate up the bus hierarchy until someone handles it. */
+ if (dev->parent)
+ return (BUS_RESUME_INTR(dev->parent, child, irq));
+ return (EINVAL);
+}
+
+/**
* @brief Helper function for implementing BUS_ADJUST_RESOURCE().
*
* This simple implementation of BUS_ADJUST_RESOURCE() simply calls the
@@ -4611,6 +4641,34 @@
if (dev->parent == NULL)
return (EINVAL);
return (BUS_TEARDOWN_INTR(dev->parent, dev, r, cookie));
+}
+
+/**
+ * @brief Wrapper function for BUS_SUSPEND_INTR().
+ *
+ * This function simply calls the BUS_SUSPEND_INTR() method of the
+ * parent of @p dev.
+ */
+int
+bus_suspend_intr(device_t dev, struct resource *r)
+{
+ if (dev->parent == NULL)
+ return (EINVAL);
+ return (BUS_SUSPEND_INTR(dev->parent, dev, r));
+}
+
+/**
+ * @brief Wrapper function for BUS_RESUME_INTR().
+ *
+ * This function simply calls the BUS_RESUME_INTR() method of the
+ * parent of @p dev.
+ */
+int
+bus_resume_intr(device_t dev, struct resource *r)
+{
+ if (dev->parent == NULL)
+ return (EINVAL);
+ return (BUS_RESUME_INTR(dev->parent, dev, r));
}
/**
Index: sys/sys/bus.h
===================================================================
--- sys/sys/bus.h
+++ sys/sys/bus.h
@@ -480,6 +480,10 @@
int bus_generic_suspend_child(device_t dev, device_t child);
int bus_generic_teardown_intr(device_t dev, device_t child,
struct resource *irq, void *cookie);
+int bus_generic_suspend_intr(device_t dev, device_t child,
+ struct resource *irq);
+int bus_generic_resume_intr(device_t dev, device_t child,
+ struct resource *irq);
int bus_generic_unmap_resource(device_t dev, device_t child, int type,
struct resource *r,
struct resource_map *map);
@@ -530,6 +534,8 @@
driver_filter_t filter, driver_intr_t handler,
void *arg, void **cookiep);
int bus_teardown_intr(device_t dev, struct resource *r, void *cookie);
+int bus_suspend_intr(device_t dev, struct resource *r);
+int bus_resume_intr(device_t dev, struct resource *r);
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, ...) __printflike(4, 5);
Index: sys/sys/interrupt.h
===================================================================
--- sys/sys/interrupt.h
+++ sys/sys/interrupt.h
@@ -61,6 +61,7 @@
#define IH_EXCLUSIVE 0x00000002 /* Exclusive interrupt. */
#define IH_ENTROPY 0x00000004 /* Device is a good entropy source. */
#define IH_DEAD 0x00000008 /* Handler should be removed. */
+#define IH_SUSP 0x00000010 /* Device is powered down. */
#define IH_MPSAFE 0x80000000 /* Handler does not need Giant. */
/*
@@ -177,6 +178,8 @@
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_suspend_handler(void *cookie);
+int intr_event_resume_handler(void *cookie);
int intr_getaffinity(int irq, int mode, void *mask);
void *intr_handler_source(void *cookie);
int intr_setaffinity(int irq, int mode, void *mask);
Index: sys/x86/x86/nexus.c
===================================================================
--- sys/x86/x86/nexus.c
+++ sys/x86/x86/nexus.c
@@ -124,6 +124,8 @@
void **);
static int nexus_teardown_intr(device_t, device_t, struct resource *,
void *);
+static int nexus_suspend_intr(device_t, device_t, struct resource *);
+static int nexus_resume_intr(device_t, device_t, struct resource *);
static struct resource_list *nexus_get_reslist(device_t dev, device_t child);
static int nexus_set_resource(device_t, device_t, int, int,
rman_res_t, rman_res_t);
@@ -161,6 +163,8 @@
DEVMETHOD(bus_unmap_resource, nexus_unmap_resource),
DEVMETHOD(bus_setup_intr, nexus_setup_intr),
DEVMETHOD(bus_teardown_intr, nexus_teardown_intr),
+ DEVMETHOD(bus_suspend_intr, nexus_suspend_intr),
+ DEVMETHOD(bus_resume_intr, nexus_resume_intr),
#ifdef SMP
DEVMETHOD(bus_bind_intr, nexus_bind_intr),
#endif
@@ -595,6 +599,8 @@
error = intr_add_handler(device_get_nameunit(child),
rman_get_start(irq), filter, ihand, arg, flags, cookiep, domain);
+ if (error == 0)
+ rman_set_virtual(irq, *cookiep);
return (error);
}
@@ -602,7 +608,24 @@
static int
nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
{
- return (intr_remove_handler(ih));
+ int error;
+
+ error = intr_remove_handler(ih);
+ if (error == 0)
+ rman_set_virtual(r, NULL);
+ return (error);
+}
+
+static int
+nexus_suspend_intr(device_t dev, device_t child, struct resource *irq)
+{
+ return (intr_event_suspend_handler(rman_get_virtual(irq)));
+}
+
+static int
+nexus_resume_intr(device_t dev, device_t child, struct resource *irq)
+{
+ return (intr_event_resume_handler(rman_get_virtual(irq)));
}
#ifdef SMP

File Metadata

Mime Type
text/plain
Expires
Sun, Feb 8, 11:17 PM (17 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28509342
Default Alt Text
D15755.id43588.diff (12 KB)

Event Timeline