[PATCH 4/5] netdev: implement infrastructure for threadable napi irq

From: Paolo Abeni
Date: Wed Jun 15 2016 - 09:43:00 EST


This commit adds the infrastructure needed for threadable
rx interrupt. A reference to the irq thread is used to
mark the threaded irq mode.
In threaded mode the poll loop is invoked directly from
__napi_schedule().
napi drivers which want to support threadable irq interrupts
must provide an irq mode change handler which actually set
napi->thread and register it after requesting the irq.

Signed-off-by: Paolo Abeni <pabeni@xxxxxxxxxx>
Signed-off-by: Hannes Frederic Sowa <hannes@xxxxxxxxxxxxxxxxxxx>
---
include/linux/netdevice.h | 4 ++++
net/core/dev.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 63 insertions(+)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d101e4d..5da53be 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -322,6 +322,9 @@ struct napi_struct {
struct list_head dev_list;
struct hlist_node napi_hash_node;
unsigned int napi_id;
+#ifdef CONFIG_IRQ_FORCED_THREADING
+ struct task_struct *thread;
+#endif
};

enum {
@@ -330,6 +333,7 @@ enum {
NAPI_STATE_NPSVC, /* Netpoll - don't dequeue from poll_list */
NAPI_STATE_HASHED, /* In NAPI hash (busy polling possible) */
NAPI_STATE_NO_BUSY_POLL,/* Do not add in napi_hash, no busy polling */
+ NAPI_STATE_SCHED_THREAD, /* The poll thread is scheduled */
};

enum gro_result {
diff --git a/net/core/dev.c b/net/core/dev.c
index b148357..40ea1e7 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -93,6 +93,7 @@
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/notifier.h>
+#include <linux/kthread.h>
#include <linux/skbuff.h>
#include <net/net_namespace.h>
#include <net/sock.h>
@@ -3453,10 +3454,68 @@ int netdev_tstamp_prequeue __read_mostly = 1;
int netdev_budget __read_mostly = 300;
int weight_p __read_mostly = 64; /* old backlog weight */

+#if CONFIG_IRQ_FORCED_THREADING
+static int napi_poll(struct napi_struct *n, struct list_head *repoll);
+
+static void napi_threaded_poll(struct napi_struct *napi)
+{
+ unsigned long time_limit = jiffies + 2;
+ struct list_head dummy_repoll;
+ int budget = netdev_budget;
+ bool again = true;
+
+ if (test_and_set_bit(NAPI_STATE_SCHED_THREAD, &napi->state))
+ return;
+
+ local_irq_enable();
+ INIT_LIST_HEAD(&dummy_repoll);
+
+ while (again) {
+ /* ensure that the poll list is not empty */
+ if (list_empty(&dummy_repoll))
+ list_add(&napi->poll_list, &dummy_repoll);
+
+ budget -= napi_poll(napi, &dummy_repoll);
+
+ if (napi_disable_pending(napi))
+ again = false;
+ else if (!test_bit(NAPI_STATE_SCHED, &napi->state))
+ again = false;
+ else if (kthread_should_stop())
+ again = false;
+
+ if (!again || unlikely(budget <= 0 ||
+ time_after_eq(jiffies, time_limit))) {
+ /* no need to reschedule if we are going to stop */
+ if (again)
+ cond_resched_softirq();
+ time_limit = jiffies + 2;
+ budget = netdev_budget;
+ rcu_bh_qs();
+ __kfree_skb_flush();
+ }
+ }
+
+ clear_bit(NAPI_STATE_SCHED_THREAD, &napi->state);
+ local_irq_disable();
+}
+
+static inline bool napi_is_threaded(struct napi_struct *napi)
+{
+ return current == napi->thread;
+}
+#else
+#define napi_is_threaded(napi) 0
+#endif
+
/* Called with irq disabled */
static inline void ____napi_schedule(struct softnet_data *sd,
struct napi_struct *napi)
{
+ if (napi_is_threaded(napi)) {
+ napi_threaded_poll(napi);
+ return;
+ }
list_add_tail(&napi->poll_list, &sd->poll_list);
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
}
--
1.8.3.1