[RFC 3/3] softirq: participate in cpuidle polling

From: Stefan Hajnoczi
Date: Tue Jul 13 2021 - 12:19:33 EST


Normally softirqs are invoked when exiting irqs. When polling in the
cpuidle driver there may be no irq. Therefore pending softirqs go
unnoticed and polling continues without invoking them.

Add a softirq_poll() function to explicitly check for and invoke
softirqs.

Signed-off-by: Stefan Hajnoczi <stefanha@xxxxxxxxxx>
---
This commit is not needed for virtio-blk. I added it when I realized
virtio-net's NAPI scheduling might not be detected by the cpuidle busy
wait loop because it is unaware of softirqs. However, even after doing
this virtio-net's NAPI polling doesn't combine with cpuidle haltpoll.

Perhaps this patch is still desirable for cpuidle poll_state in case a
softirq is raised?
---
include/linux/interrupt.h | 2 ++
drivers/cpuidle/poll_source.c | 3 +++
kernel/softirq.c | 14 ++++++++++++++
3 files changed, 19 insertions(+)

diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 4777850a6dc7..9bfdcc466ba8 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -573,6 +573,8 @@ struct softirq_action
asmlinkage void do_softirq(void);
asmlinkage void __do_softirq(void);

+extern void softirq_poll(void);
+
extern void open_softirq(int nr, void (*action)(struct softirq_action *));
extern void softirq_init(void);
extern void __raise_softirq_irqoff(unsigned int nr);
diff --git a/drivers/cpuidle/poll_source.c b/drivers/cpuidle/poll_source.c
index 46100e5a71e4..ed200feb0daa 100644
--- a/drivers/cpuidle/poll_source.c
+++ b/drivers/cpuidle/poll_source.c
@@ -6,6 +6,7 @@
#include <linux/lockdep.h>
#include <linux/percpu.h>
#include <linux/poll_source.h>
+#include <linux/interrupt.h>

/* The per-cpu list of registered poll sources */
DEFINE_PER_CPU(struct list_head, poll_source_list);
@@ -26,6 +27,8 @@ void poll_source_run_once(void)

list_for_each_entry(src, this_cpu_ptr(&poll_source_list), node)
src->ops->poll(src);
+
+ softirq_poll();
}

/* Called from idle task with TIF_POLLING_NRFLAG set and irqs enabled */
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 4992853ef53d..f45bf0204218 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -611,6 +611,20 @@ void irq_enter(void)
irq_enter_rcu();
}

+/**
+ * softirq_poll() - invoke pending softirqs
+ *
+ * Normally it is not necessary to explicitly poll for softirqs, but in the
+ * cpuidle driver a polling function may have raised a softirq with no irq exit
+ * to invoke it. Therefore it is necessary to poll for pending softirqs and
+ * invoke them explicitly.
+ */
+void softirq_poll(void)
+{
+ if (!in_interrupt() && local_softirq_pending())
+ invoke_softirq();
+}
+
static inline void tick_irq_exit(void)
{
#ifdef CONFIG_NO_HZ_COMMON
--
2.31.1