Blob Blame History Raw
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Date: Wed, 21 Aug 2013 17:48:46 +0200
Subject: genirq: Do not invoke the affinity callback via a workqueue on RT
Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/rt/linux-rt-devel.git
Git-commit: f6d13022e05388ce4da8e702e7497efe0dc7c667
Patch-mainline: Queued in subsystem maintainer repository
References: SLE Realtime Extension

Joe Korty reported, that __irq_set_affinity_locked() schedules a
workqueue while holding a rawlock which results in a might_sleep()
warning.
This patch uses swork_queue() instead.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Mike Galbraith <mgalbraith@suse.de>
---
 include/linux/interrupt.h |    6 ++++++
 kernel/irq/manage.c       |   43 ++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 46 insertions(+), 3 deletions(-)

--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -14,6 +14,7 @@
 #include <linux/hrtimer.h>
 #include <linux/kref.h>
 #include <linux/workqueue.h>
+#include <linux/swork.h>
 
 #include <linux/atomic.h>
 #include <asm/ptrace.h>
@@ -218,6 +219,7 @@ extern void resume_device_irqs(void);
  * struct irq_affinity_notify - context for notification of IRQ affinity changes
  * @irq:		Interrupt to which notification applies
  * @kref:		Reference count, for internal use
+ * @swork:		Swork item, for internal use
  * @work:		Work item, for internal use
  * @notify:		Function to be called on change.  This will be
  *			called in process context.
@@ -229,7 +231,11 @@ extern void resume_device_irqs(void);
 struct irq_affinity_notify {
 	unsigned int irq;
 	struct kref kref;
+#ifdef CONFIG_PREEMPT_RT_BASE
+	struct swork_event swork;
+#else
 	struct work_struct work;
+#endif
 	void (*notify)(struct irq_affinity_notify *, const cpumask_t *mask);
 	void (*release)(struct kref *ref);
 };
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -237,7 +237,12 @@ int irq_set_affinity_locked(struct irq_d
 
 	if (desc->affinity_notify) {
 		kref_get(&desc->affinity_notify->kref);
+
+#ifdef CONFIG_PREEMPT_RT_BASE
+		swork_queue(&desc->affinity_notify->swork);
+#else
 		schedule_work(&desc->affinity_notify->work);
+#endif
 	}
 	irqd_set(data, IRQD_AFFINITY_SET);
 
@@ -275,10 +280,8 @@ int irq_set_affinity_hint(unsigned int i
 }
 EXPORT_SYMBOL_GPL(irq_set_affinity_hint);
 
-static void irq_affinity_notify(struct work_struct *work)
+static void _irq_affinity_notify(struct irq_affinity_notify *notify)
 {
-	struct irq_affinity_notify *notify =
-		container_of(work, struct irq_affinity_notify, work);
 	struct irq_desc *desc = irq_to_desc(notify->irq);
 	cpumask_var_t cpumask;
 	unsigned long flags;
@@ -300,6 +303,35 @@ static void irq_affinity_notify(struct w
 	kref_put(&notify->kref, notify->release);
 }
 
+#ifdef CONFIG_PREEMPT_RT_BASE
+static void init_helper_thread(void)
+{
+	static int init_sworker_once;
+
+	if (init_sworker_once)
+		return;
+	if (WARN_ON(swork_get()))
+		return;
+	init_sworker_once = 1;
+}
+
+static void irq_affinity_notify(struct swork_event *swork)
+{
+	struct irq_affinity_notify *notify =
+		container_of(swork, struct irq_affinity_notify, swork);
+	_irq_affinity_notify(notify);
+}
+
+#else
+
+static void irq_affinity_notify(struct work_struct *work)
+{
+	struct irq_affinity_notify *notify =
+		container_of(work, struct irq_affinity_notify, work);
+	_irq_affinity_notify(notify);
+}
+#endif
+
 /**
  *	irq_set_affinity_notifier - control notification of IRQ affinity changes
  *	@irq:		Interrupt for which to enable/disable notification
@@ -328,7 +360,12 @@ irq_set_affinity_notifier(unsigned int i
 	if (notify) {
 		notify->irq = irq;
 		kref_init(&notify->kref);
+#ifdef CONFIG_PREEMPT_RT_BASE
+		INIT_SWORK(&notify->swork, irq_affinity_notify);
+		init_helper_thread();
+#else
 		INIT_WORK(&notify->work, irq_affinity_notify);
+#endif
 	}
 
 	raw_spin_lock_irqsave(&desc->lock, flags);
@@ -374,7 +374,16 @@ irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify)
 	raw_spin_unlock_irqrestore(&desc->lock, flags);
 
 	if (old_notify) {
+#ifndef CONFIG_PREEMPT_RT_BASE
+		/*
+		 * preempt-rt: todo
+		 * swork has no cancel equivalent but the reasons for using
+		 * still exist (calling potentially sleeping function).
+		 * Upstream has not resolved the issue yet and simply
+		 * reintroduced the potential sleep warning.
+		 */
 		cancel_work_sync(&old_notify->work);
+#endif
 		kref_put(&old_notify->kref, old_notify->release);
 	}