Page MenuHomeFreeBSD

D30549.id90124.diff
No OneTemporary

D30549.id90124.diff

Index: sys/compat/linuxkpi/common/include/linux/device.h
===================================================================
--- sys/compat/linuxkpi/common/include/linux/device.h
+++ sys/compat/linuxkpi/common/include/linux/device.h
@@ -114,6 +114,7 @@
#define LINUX_IRQ_INVALID 65535
unsigned int irq_start;
unsigned int irq_end;
+ struct proc *irq_proc;
const struct attribute_group **groups;
struct fwnode_handle *fwnode;
struct cdev *backlight_dev;
Index: sys/compat/linuxkpi/common/include/linux/interrupt.h
===================================================================
--- sys/compat/linuxkpi/common/include/linux/interrupt.h
+++ sys/compat/linuxkpi/common/include/linux/interrupt.h
@@ -37,9 +37,11 @@
#include <linux/irqreturn.h>
#include <linux/hardirq.h>
+#include <sys/param.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/interrupt.h>
+#include <sys/kthread.h>
typedef irqreturn_t (*irq_handler_t)(int, void *);
@@ -51,10 +53,25 @@
struct resource *res;
void *arg;
irqreturn_t (*handler)(int, void *);
+ irqreturn_t (*thread_handler)(int, void *);
void *tag;
unsigned int irq;
+ unsigned char td_state;
+#define IRQETD_RUNNING 0x01
+#define IRQETD_SLEEPING 0x02
+#define IRQETD_STOP 0x04
+#define IRQETD_STOPPED 0x08
+ unsigned char _spare;
+ unsigned short td_level;
+ struct thread *td;
+ struct mtx td_lock;
+ struct cv td_cv;
};
+void linux_irq_handler(void *);
+void linux_irq_worker(void *);
+void lkpi_devm_irq_release(struct device *, void *);
+
static inline int
linux_irq_rid(struct device *dev, unsigned int irq)
{
@@ -65,8 +82,6 @@
return (0);
}
-extern void linux_irq_handler(void *);
-
static inline struct irq_ent *
linux_irq_ent(struct device *dev, unsigned int irq)
{
@@ -80,8 +95,9 @@
}
static inline int
-request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
- const char *name, void *arg)
+_request_irq(struct device *xdev, unsigned int irq,
+ irq_handler_t handler, irq_handler_t thread_handler,
+ unsigned long flags, const char *name, void *arg)
{
struct resource *res;
struct irq_ent *irqe;
@@ -92,27 +108,95 @@
dev = linux_pci_find_irq_dev(irq);
if (dev == NULL)
return -ENXIO;
+ if (xdev != NULL && xdev != dev)
+ return -ENXIO;
rid = linux_irq_rid(dev, irq);
res = bus_alloc_resource_any(dev->bsddev, SYS_RES_IRQ, &rid,
flags | RF_ACTIVE);
if (res == NULL)
return (-ENXIO);
- irqe = kmalloc(sizeof(*irqe), GFP_KERNEL);
+ if (xdev != NULL)
+ irqe = lkpi_devres_alloc(lkpi_devm_irq_release, sizeof(*irqe),
+ GFP_KERNEL | __GFP_ZERO);
+ else
+ irqe = kzalloc(sizeof(*irqe), GFP_KERNEL);
irqe->dev = dev;
irqe->res = res;
irqe->arg = arg;
irqe->handler = handler;
+ irqe->thread_handler = thread_handler;
irqe->irq = irq;
+
+ if (irqe->thread_handler != NULL) {
+ /* Start a kthread. */
+ mtx_init(&irqe->td_lock, "irqe td lock", NULL, MTX_DEF);
+ cv_init(&irqe->td_cv, "irqe td cv");
+ error = kproc_kthread_add(linux_irq_worker, irqe,
+ &dev->irq_proc, &irqe->td, 0, 0, "lirqwt",
+ "%s irq %d", name, irq);
+ if (error != 0)
+ goto errout;
+ }
+
error = bus_setup_intr(dev->bsddev, res, INTR_TYPE_NET | INTR_MPSAFE,
NULL, linux_irq_handler, irqe, &irqe->tag);
- if (error) {
- bus_release_resource(dev->bsddev, SYS_RES_IRQ, rid, irqe->res);
- kfree(irqe);
- return (-error);
- }
+ if (error)
+ goto errout;
+ else if (name != NULL)
+ bus_describe_intr(dev->bsddev, irqe->res, irqe->tag,
+ "%s irq %d", name, irq);
list_add(&irqe->links, &dev->irqents);
+ if (xdev != NULL)
+ devres_add(xdev, irqe);
return 0;
+
+errout:
+ if (irqe->thread_handler != NULL) {
+ mtx_lock(&irqe->td_lock);
+ irqe->td_state = IRQETD_STOP;
+ cv_signal(&irqe->td_cv);
+ do {
+ cv_wait(&irqe->td_cv, &irqe->td_lock);
+ } while (irqe->td_state != IRQETD_STOPPED);
+ mtx_unlock(&irqe->td_lock);
+ cv_destroy(&irqe->td_cv);
+ mtx_destroy(&irqe->td_lock);
+ }
+ bus_release_resource(dev->bsddev, SYS_RES_IRQ, rid, irqe->res);
+ if (xdev != NULL)
+ devres_free(irqe);
+ else
+ kfree(irqe);
+ return (-error);
+}
+
+static inline int
+request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
+ const char *name, void *arg)
+{
+
+ return (_request_irq(NULL, irq, handler, NULL, flags, name, arg));
+}
+
+static inline int
+request_threaded_irq(int irq, irq_handler_t handler,
+ irq_handler_t thread_handler, unsigned long flags,
+ const char *name, void *arg)
+{
+
+ return (_request_irq(NULL, irq, handler, thread_handler,
+ flags, name, arg));
+}
+
+static inline int
+devm_request_threaded_irq(struct device *dev, int irq,
+ irq_handler_t handler, irq_handler_t thread_handler,
+ unsigned long flags, const char *name, void *arg)
+{
+
+ return (_request_irq(dev, irq, handler, thread_handler,
+ flags, name, arg));
}
static inline int
@@ -165,27 +249,44 @@
return (-bus_bind_intr(dev->bsddev, irqe->res, cpu_id));
}
+void lkpi_irq_release(struct device *, struct irq_ent *);
+
static inline void
-free_irq(unsigned int irq, void *device)
+free_irq(unsigned int irq, void *device __unused)
{
struct irq_ent *irqe;
struct device *dev;
- int rid;
dev = linux_pci_find_irq_dev(irq);
if (dev == NULL)
return;
- rid = linux_irq_rid(dev, irq);
irqe = linux_irq_ent(dev, irq);
if (irqe == NULL)
return;
- if (irqe->tag != NULL)
- bus_teardown_intr(dev->bsddev, irqe->res, irqe->tag);
- bus_release_resource(dev->bsddev, SYS_RES_IRQ, rid, irqe->res);
- list_del(&irqe->links);
+ lkpi_irq_release(dev, irqe);
kfree(irqe);
}
+static inline void
+devm_free_irq(struct device *xdev, unsigned int irq, void *p)
+{
+ struct device *dev;
+ struct irq_ent *irqe;
+
+ dev = linux_pci_find_irq_dev(irq);
+ if (dev == NULL)
+ return;
+ if (xdev != dev)
+ return;
+ irqe = linux_irq_ent(dev, irq);
+ if (irqe == NULL)
+ return;
+ lkpi_irq_release(dev, irqe);
+ lkpi_devres_unlink(dev, irqe);
+ lkpi_devres_free(irqe);
+ return;
+}
+
static inline int
irq_set_affinity_hint(int vector, cpumask_t *mask)
{
Index: sys/compat/linuxkpi/common/src/linux_compat.c
===================================================================
--- sys/compat/linuxkpi/common/src/linux_compat.c
+++ sys/compat/linuxkpi/common/src/linux_compat.c
@@ -2458,16 +2458,108 @@
free(ar, M_KMALLOC);
}
+void
+lkpi_irq_release(struct device *dev, struct irq_ent *irqe)
+{
+
+ if (irqe->tag != NULL)
+ bus_teardown_intr(dev->bsddev, irqe->res, irqe->tag);
+ if (irqe->thread_handler != NULL) {
+ mtx_lock(&irqe->td_lock);
+ irqe->td_state = IRQETD_STOP;
+ cv_signal(&irqe->td_cv);
+ do {
+ cv_wait(&irqe->td_cv, &irqe->td_lock);
+ } while (irqe->td_state != IRQETD_STOPPED);
+ mtx_unlock(&irqe->td_lock);
+ cv_destroy(&irqe->td_cv);
+ mtx_destroy(&irqe->td_lock);
+ }
+ if (irqe->res != NULL)
+ bus_release_resource(dev->bsddev, SYS_RES_IRQ,
+ rman_get_rid(irqe->res), irqe->res);
+ list_del(&irqe->links);
+}
+
+void
+lkpi_devm_irq_release(struct device *dev, void *p)
+{
+ struct irq_ent *irqe;
+
+ if (dev == NULL || p == NULL)
+ return;
+
+ irqe = p;
+ lkpi_irq_release(dev, irqe);
+}
+
+void
+linux_irq_worker(void *ent)
+{
+ struct irq_ent *irqe;
+ int rc;
+
+ irqe = ent;
+
+ mtx_lock(&irqe->td_lock);
+ MPASS(irqe->thread_handler != NULL);
+ MPASS(irqe->td_state == 0);
+ irqe->td_state = IRQETD_RUNNING;
+ cv_signal(&irqe->td_cv);
+
+ do {
+ while (irqe->td_level > 0) {
+ irqe->td_level--;
+ mtx_unlock(&irqe->td_lock);
+ /* Run the threadded interrupt handler. */
+ rc = irqe->thread_handler(irqe->irq, irqe->arg);
+ if (rc != IRQ_HANDLED)
+ device_printf(irqe->dev->bsddev, "%s: "
+ "thread_handler returned %d (%s)\n",
+ __func__, rc,
+ (rc == IRQ_HANDLED) ? "IRQ_HANDLED" :
+ (rc == IRQ_WAKE_THREAD) ?
+ "IRQ_WAKE_THREAD" : "???");
+ mtx_lock(&irqe->td_lock);
+ }
+
+ if (irqe->td_state == IRQETD_STOP)
+ break;
+
+ irqe->td_state = IRQETD_SLEEPING;
+ cv_wait(&irqe->td_cv, &irqe->td_lock);
+
+ } while (irqe->td_state != IRQETD_STOP);
+
+ mtx_assert(&irqe->td_lock, MA_OWNED);
+ irqe->td_state = IRQETD_STOPPED;
+ cv_signal(&irqe->td_cv);
+ mtx_unlock(&irqe->td_lock);
+ kthread_exit();
+}
+
void
linux_irq_handler(void *ent)
{
struct irq_ent *irqe;
+ irqreturn_t rc;
if (linux_set_current_flags(curthread, M_NOWAIT))
return;
irqe = ent;
- irqe->handler(irqe->irq, irqe->arg);
+ rc = irqe->handler(irqe->irq, irqe->arg);
+ if (rc == IRQ_WAKE_THREAD) {
+ if (irqe->thread_handler != NULL) {
+ mtx_lock(&irqe->td_lock);
+ irqe->td_level++;
+ if (irqe->td_state == IRQETD_SLEEPING) {
+ irqe->td_state = IRQETD_RUNNING;
+ cv_signal(&irqe->td_cv);
+ }
+ mtx_unlock(&irqe->td_lock);
+ }
+ }
}
#if defined(__i386__) || defined(__amd64__)

File Metadata

Mime Type
text/plain
Expires
Thu, Mar 12, 7:16 PM (11 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29592248
Default Alt Text
D30549.id90124.diff (8 KB)

Event Timeline