Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F137814376
D15755.id46842.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D15755.id46842.diff
View Options
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);
@@ -4407,6 +4421,16 @@
pci_cfg_save(child, dinfo, 1);
bus_generic_resume_child(dev, child);
+
+ /*
+ * Allow interrupts only after fully resuming the driver and hardware.
+ */
+ 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);
+ }
return (0);
}
Index: sys/kern/bus_if.m
===================================================================
--- sys/kern/bus_if.m
+++ sys/kern/bus_if.m
@@ -472,6 +472,44 @@
};
/**
+ * @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 _irq must be the interrupt resource passed
+ * to a previous call to BUS_SETUP_INTR().
+ *
+ * @param _dev the parent device of @p _child
+ * @param _child the device which allocated the resource
+ * @param _irq the resource representing the interrupt
+ */
+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 _irq must be the interrupt resource passed
+ * to a previous call to BUS_SETUP_INTR().
+ *
+ * @param _dev the parent device of @p _child
+ * @param _child the device which allocated the resource
+ * @param _irq the resource representing the interrupt
+ */
+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
@@ -721,6 +721,28 @@
atomic_thread_fence_acq();
}
+static void
+intr_handler_barrier(struct intr_handler *handler)
+{
+ struct intr_event *ie;
+
+ ie = handler->ih_event;
+ mtx_assert(&ie->ie_lock, MA_OWNED);
+ KASSERT((handler->ih_flags & IH_DEAD) == 0,
+ ("update for a removed handler"));
+
+ if (ie->ie_thread == NULL) {
+ intr_event_barrier(ie);
+ return;
+ }
+ if ((handler->ih_flags & IH_CHANGED) == 0) {
+ handler->ih_flags |= IH_CHANGED;
+ intr_event_schedule_thread(ie);
+ }
+ while ((handler->ih_flags & IH_CHANGED) != 0)
+ msleep(handler, &ie->ie_lock, 0, "ih_barr", 0);
+}
+
/*
* Sleep until an ithread finishes executing an interrupt handler.
*
@@ -842,6 +864,49 @@
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;
+ intr_handler_barrier(handler);
+ 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));
+
+ /*
+ * intr_handler_barrier() acts not only as a barrier,
+ * it also allows to check for any pending interrupts.
+ */
+ mtx_lock(&ie->ie_lock);
+ handler->ih_flags &= ~IH_SUSP;
+ intr_handler_barrier(handler);
+ mtx_unlock(&ie->ie_lock);
+ return (0);
+}
+
static int
intr_event_schedule_thread(struct intr_event *ie)
{
@@ -1016,10 +1081,21 @@
*/
ihp = ih;
+ if ((ih->ih_flags & IH_CHANGED) != 0) {
+ mtx_lock(&ie->ie_lock);
+ ih->ih_flags &= ~IH_CHANGED;
+ wakeup(ih);
+ mtx_unlock(&ie->ie_lock);
+ }
+
/* Skip filter only handlers */
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
@@ -1178,8 +1254,9 @@
struct intr_handler *ih;
struct trapframe *oldframe;
struct thread *td;
- int ret, thread;
int phase;
+ int ret;
+ bool filter, thread;
td = curthread;
@@ -1198,7 +1275,8 @@
* 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;
@@ -1214,8 +1292,10 @@
atomic_thread_fence_seq_cst();
CK_SLIST_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__,
@@ -1230,24 +1310,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;
}
}
atomic_add_rel_int(&ie->ie_active[phase], -1);
@@ -1271,6 +1352,11 @@
}
critical_exit();
td->td_intr_nesting_level--;
+#ifdef notyet
+ /* The interrupt is not aknowledged by any filter and has no ithread. */
+ if (!thread && !filter)
+ return (EINVAL);
+#endif
return (0);
}
Index: sys/kern/subr_bus.c
===================================================================
--- sys/kern/subr_bus.c
+++ sys/kern/subr_bus.c
@@ -4038,6 +4038,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
@@ -4602,6 +4632,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
@@ -481,6 +481,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);
@@ -531,6 +535,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
@@ -62,6 +62,8 @@
#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_CHANGED 0x40000000 /* Handler state is changed. */
#define IH_MPSAFE 0x80000000 /* Handler does not need Giant. */
/*
@@ -179,6 +181,8 @@
int intr_event_destroy(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
Details
Attached
Mime Type
text/plain
Expires
Thu, Nov 27, 1:55 AM (1 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
26239017
Default Alt Text
D15755.id46842.diff (13 KB)
Attached To
Mode
D15755: add support for marking interrupt handlers as suspended
Attached
Detach File
Event Timeline
Log In to Comment