mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-10-29 09:32:34 +00:00
ath11k: disable napi threading
The code causes 15% softirq in idle causing issue, drop until resolved. Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
@@ -1,88 +0,0 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Mon, 8 Feb 2021 11:34:08 -0800
|
||||
Subject: [PATCH] net: extract napi poll functionality to __napi_poll()
|
||||
|
||||
This commit introduces a new function __napi_poll() which does the main
|
||||
logic of the existing napi_poll() function, and will be called by other
|
||||
functions in later commits.
|
||||
This idea and implementation is done by Felix Fietkau <nbd@nbd.name> and
|
||||
is proposed as part of the patch to move napi work to work_queue
|
||||
context.
|
||||
This commit by itself is a code restructure.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
Signed-off-by: Wei Wang <weiwan@google.com>
|
||||
Reviewed-by: Alexander Duyck <alexanderduyck@fb.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/net/core/dev.c
|
||||
+++ b/net/core/dev.c
|
||||
@@ -6322,15 +6322,10 @@ void netif_napi_del(struct napi_struct *
|
||||
}
|
||||
EXPORT_SYMBOL(netif_napi_del);
|
||||
|
||||
-static int napi_poll(struct napi_struct *n, struct list_head *repoll)
|
||||
+static int __napi_poll(struct napi_struct *n, bool *repoll)
|
||||
{
|
||||
- void *have;
|
||||
int work, weight;
|
||||
|
||||
- list_del_init(&n->poll_list);
|
||||
-
|
||||
- have = netpoll_poll_lock(n);
|
||||
-
|
||||
weight = n->weight;
|
||||
|
||||
/* This NAPI_STATE_SCHED test is for avoiding a race
|
||||
@@ -6348,7 +6343,7 @@ static int napi_poll(struct napi_struct
|
||||
WARN_ON_ONCE(work > weight);
|
||||
|
||||
if (likely(work < weight))
|
||||
- goto out_unlock;
|
||||
+ return work;
|
||||
|
||||
/* Drivers must not modify the NAPI state if they
|
||||
* consume the entire weight. In such cases this code
|
||||
@@ -6357,7 +6352,7 @@ static int napi_poll(struct napi_struct
|
||||
*/
|
||||
if (unlikely(napi_disable_pending(n))) {
|
||||
napi_complete(n);
|
||||
- goto out_unlock;
|
||||
+ return work;
|
||||
}
|
||||
|
||||
if (n->gro_bitmask) {
|
||||
@@ -6375,12 +6370,29 @@ static int napi_poll(struct napi_struct
|
||||
if (unlikely(!list_empty(&n->poll_list))) {
|
||||
pr_warn_once("%s: Budget exhausted after napi rescheduled\n",
|
||||
n->dev ? n->dev->name : "backlog");
|
||||
- goto out_unlock;
|
||||
+ return work;
|
||||
}
|
||||
|
||||
- list_add_tail(&n->poll_list, repoll);
|
||||
+ *repoll = true;
|
||||
+
|
||||
+ return work;
|
||||
+}
|
||||
+
|
||||
+static int napi_poll(struct napi_struct *n, struct list_head *repoll)
|
||||
+{
|
||||
+ bool do_repoll = false;
|
||||
+ void *have;
|
||||
+ int work;
|
||||
+
|
||||
+ list_del_init(&n->poll_list);
|
||||
+
|
||||
+ have = netpoll_poll_lock(n);
|
||||
+
|
||||
+ work = __napi_poll(n, &do_repoll);
|
||||
+
|
||||
+ if (do_repoll)
|
||||
+ list_add_tail(&n->poll_list, repoll);
|
||||
|
||||
-out_unlock:
|
||||
netpoll_poll_unlock(have);
|
||||
|
||||
return work;
|
||||
@@ -1,245 +0,0 @@
|
||||
From: Wei Wang <weiwan@google.com>
|
||||
Date: Mon, 8 Feb 2021 11:34:09 -0800
|
||||
Subject: [PATCH] net: implement threaded-able napi poll loop support
|
||||
|
||||
This patch allows running each napi poll loop inside its own
|
||||
kernel thread.
|
||||
The kthread is created during netif_napi_add() if dev->threaded
|
||||
is set. And threaded mode is enabled in napi_enable(). We will
|
||||
provide a way to set dev->threaded and enable threaded mode
|
||||
without a device up/down in the following patch.
|
||||
|
||||
Once that threaded mode is enabled and the kthread is
|
||||
started, napi_schedule() will wake-up such thread instead
|
||||
of scheduling the softirq.
|
||||
|
||||
The threaded poll loop behaves quite likely the net_rx_action,
|
||||
but it does not have to manipulate local irqs and uses
|
||||
an explicit scheduling point based on netdev_budget.
|
||||
|
||||
Co-developed-by: Paolo Abeni <pabeni@redhat.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
Co-developed-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
|
||||
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
|
||||
Co-developed-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Wei Wang <weiwan@google.com>
|
||||
Reviewed-by: Alexander Duyck <alexanderduyck@fb.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/include/linux/netdevice.h
|
||||
+++ b/include/linux/netdevice.h
|
||||
@@ -319,6 +319,7 @@ struct napi_struct {
|
||||
struct list_head dev_list;
|
||||
struct hlist_node napi_hash_node;
|
||||
unsigned int napi_id;
|
||||
+ struct task_struct *thread;
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -326,6 +327,7 @@ enum {
|
||||
NAPI_STATE_DISABLE, /* Disable pending */
|
||||
NAPI_STATE_NPSVC, /* Netpoll - don't dequeue from poll_list */
|
||||
NAPI_STATE_HASHED, /* In NAPI hash */
|
||||
+ NAPI_STATE_THREADED, /* The poll is performed inside its own thread*/
|
||||
};
|
||||
|
||||
enum gro_result {
|
||||
@@ -501,13 +503,7 @@ void napi_disable(struct napi_struct *n)
|
||||
* Resume NAPI from being scheduled on this context.
|
||||
* Must be paired with napi_disable.
|
||||
*/
|
||||
-static inline void napi_enable(struct napi_struct *n)
|
||||
-{
|
||||
- BUG_ON(!test_bit(NAPI_STATE_SCHED, &n->state));
|
||||
- smp_mb__before_atomic();
|
||||
- clear_bit(NAPI_STATE_SCHED, &n->state);
|
||||
- clear_bit(NAPI_STATE_NPSVC, &n->state);
|
||||
-}
|
||||
+void napi_enable(struct napi_struct *n);
|
||||
|
||||
/**
|
||||
* napi_synchronize - wait until NAPI is not running
|
||||
@@ -1573,6 +1569,8 @@ enum netdev_priv_flags_ext {
|
||||
* switch driver and used to set the phys state of the
|
||||
* switch port.
|
||||
*
|
||||
+ * @threaded: napi threaded mode is enabled
|
||||
+ *
|
||||
* FIXME: cleanup struct net_device such that network protocol info
|
||||
* moves out.
|
||||
*/
|
||||
@@ -1852,6 +1850,7 @@ struct net_device {
|
||||
struct phy_device *phydev;
|
||||
struct lock_class_key *qdisc_tx_busylock;
|
||||
bool proto_down;
|
||||
+ unsigned threaded:1;
|
||||
};
|
||||
#define to_net_dev(d) container_of(d, struct net_device, dev)
|
||||
|
||||
--- a/net/core/dev.c
|
||||
+++ b/net/core/dev.c
|
||||
@@ -94,6 +94,7 @@
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/skbuff.h>
|
||||
+#include <linux/kthread.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/sock.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
@@ -1304,6 +1305,27 @@ void netdev_notify_peers(struct net_devi
|
||||
}
|
||||
EXPORT_SYMBOL(netdev_notify_peers);
|
||||
|
||||
+static int napi_threaded_poll(void *data);
|
||||
+
|
||||
+static int napi_kthread_create(struct napi_struct *n)
|
||||
+{
|
||||
+ int err = 0;
|
||||
+
|
||||
+ /* Create and wake up the kthread once to put it in
|
||||
+ * TASK_INTERRUPTIBLE mode to avoid the blocked task
|
||||
+ * warning and work with loadavg.
|
||||
+ */
|
||||
+ n->thread = kthread_run(napi_threaded_poll, n, "napi/%s-%d",
|
||||
+ n->dev->name, n->napi_id);
|
||||
+ if (IS_ERR(n->thread)) {
|
||||
+ err = PTR_ERR(n->thread);
|
||||
+ pr_err("kthread_run failed with err %d\n", err);
|
||||
+ n->thread = NULL;
|
||||
+ }
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
static int __dev_open(struct net_device *dev)
|
||||
{
|
||||
const struct net_device_ops *ops = dev->netdev_ops;
|
||||
@@ -3248,6 +3270,21 @@ int weight_p __read_mostly = 64;
|
||||
static inline void ____napi_schedule(struct softnet_data *sd,
|
||||
struct napi_struct *napi)
|
||||
{
|
||||
+ struct task_struct *thread;
|
||||
+
|
||||
+ if (test_bit(NAPI_STATE_THREADED, &napi->state)) {
|
||||
+ /* Paired with smp_mb__before_atomic() in
|
||||
+ * napi_enable(). Use READ_ONCE() to guarantee
|
||||
+ * a complete read on napi->thread. Only call
|
||||
+ * wake_up_process() when it's not NULL.
|
||||
+ */
|
||||
+ thread = READ_ONCE(napi->thread);
|
||||
+ if (thread) {
|
||||
+ wake_up_process(thread);
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
list_add_tail(&napi->poll_list, &sd->poll_list);
|
||||
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
|
||||
}
|
||||
@@ -4828,9 +4865,33 @@ void netif_napi_add(struct net_device *d
|
||||
napi->poll_owner = -1;
|
||||
#endif
|
||||
set_bit(NAPI_STATE_SCHED, &napi->state);
|
||||
+ /* Create kthread for this napi if dev->threaded is set.
|
||||
+ * Clear dev->threaded if kthread creation failed so that
|
||||
+ * threaded mode will not be enabled in napi_enable().
|
||||
+ */
|
||||
+ if (dev->threaded && napi_kthread_create(napi))
|
||||
+ dev->threaded = 0;
|
||||
}
|
||||
EXPORT_SYMBOL(netif_napi_add);
|
||||
|
||||
+/**
|
||||
+ * napi_enable - enable NAPI scheduling
|
||||
+ * @n: NAPI context
|
||||
+ *
|
||||
+ * Resume NAPI from being scheduled on this context.
|
||||
+ * Must be paired with napi_disable.
|
||||
+ */
|
||||
+void napi_enable(struct napi_struct *n)
|
||||
+{
|
||||
+ BUG_ON(!test_bit(NAPI_STATE_SCHED, &n->state));
|
||||
+ smp_mb__before_atomic();
|
||||
+ clear_bit(NAPI_STATE_SCHED, &n->state);
|
||||
+ clear_bit(NAPI_STATE_NPSVC, &n->state);
|
||||
+ if (n->dev->threaded && n->thread)
|
||||
+ set_bit(NAPI_STATE_THREADED, &n->state);
|
||||
+}
|
||||
+EXPORT_SYMBOL(napi_enable);
|
||||
+
|
||||
void napi_disable(struct napi_struct *n)
|
||||
{
|
||||
might_sleep();
|
||||
@@ -4844,6 +4905,7 @@ void napi_disable(struct napi_struct *n)
|
||||
hrtimer_cancel(&n->timer);
|
||||
|
||||
clear_bit(NAPI_STATE_DISABLE, &n->state);
|
||||
+ clear_bit(NAPI_STATE_THREADED, &n->state);
|
||||
}
|
||||
EXPORT_SYMBOL(napi_disable);
|
||||
|
||||
@@ -4855,6 +4917,11 @@ void netif_napi_del(struct napi_struct *
|
||||
kfree_skb_list(napi->gro_list);
|
||||
napi->gro_list = NULL;
|
||||
napi->gro_count = 0;
|
||||
+
|
||||
+ if (napi->thread) {
|
||||
+ kthread_stop(napi->thread);
|
||||
+ napi->thread = NULL;
|
||||
+ }
|
||||
}
|
||||
EXPORT_SYMBOL(netif_napi_del);
|
||||
|
||||
@@ -4940,6 +5007,50 @@ static int napi_poll(struct napi_struct
|
||||
return work;
|
||||
}
|
||||
|
||||
+static int napi_thread_wait(struct napi_struct *napi)
|
||||
+{
|
||||
+ set_current_state(TASK_INTERRUPTIBLE);
|
||||
+
|
||||
+ while (!kthread_should_stop() && !napi_disable_pending(napi)) {
|
||||
+ if (test_bit(NAPI_STATE_SCHED, &napi->state)) {
|
||||
+ WARN_ON(!list_empty(&napi->poll_list));
|
||||
+ __set_current_state(TASK_RUNNING);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ schedule();
|
||||
+ set_current_state(TASK_INTERRUPTIBLE);
|
||||
+ }
|
||||
+ __set_current_state(TASK_RUNNING);
|
||||
+ return -1;
|
||||
+}
|
||||
+
|
||||
+static int napi_threaded_poll(void *data)
|
||||
+{
|
||||
+ struct napi_struct *napi = data;
|
||||
+ void *have;
|
||||
+
|
||||
+ while (!napi_thread_wait(napi)) {
|
||||
+ for (;;) {
|
||||
+ bool repoll = false;
|
||||
+
|
||||
+ local_bh_disable();
|
||||
+
|
||||
+ have = netpoll_poll_lock(napi);
|
||||
+ __napi_poll(napi, &repoll);
|
||||
+ netpoll_poll_unlock(have);
|
||||
+
|
||||
+ local_bh_enable();
|
||||
+
|
||||
+ if (!repoll)
|
||||
+ break;
|
||||
+
|
||||
+ cond_resched();
|
||||
+ }
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static void net_rx_action(struct softirq_action *h)
|
||||
{
|
||||
struct softnet_data *sd = this_cpu_ptr(&softnet_data);
|
||||
@@ -1,156 +0,0 @@
|
||||
From: Wei Wang <weiwan@google.com>
|
||||
Date: Mon, 8 Feb 2021 11:34:10 -0800
|
||||
Subject: [PATCH] net: add sysfs attribute to control napi threaded mode
|
||||
|
||||
This patch adds a new sysfs attribute to the network device class.
|
||||
Said attribute provides a per-device control to enable/disable the
|
||||
threaded mode for all the napi instances of the given network device,
|
||||
without the need for a device up/down.
|
||||
User sets it to 1 or 0 to enable or disable threaded mode.
|
||||
Note: when switching between threaded and the current softirq based mode
|
||||
for a napi instance, it will not immediately take effect if the napi is
|
||||
currently being polled. The mode switch will happen for the next time
|
||||
napi_schedule() is called.
|
||||
|
||||
Co-developed-by: Paolo Abeni <pabeni@redhat.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
Co-developed-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
|
||||
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
|
||||
Co-developed-by: Felix Fietkau <nbd@nbd.name>
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
Signed-off-by: Wei Wang <weiwan@google.com>
|
||||
Reviewed-by: Alexander Duyck <alexanderduyck@fb.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
|
||||
--- a/include/linux/netdevice.h
|
||||
+++ b/include/linux/netdevice.h
|
||||
@@ -496,6 +496,8 @@ void napi_hash_del(struct napi_struct *n
|
||||
*/
|
||||
void napi_disable(struct napi_struct *n);
|
||||
|
||||
+int dev_set_threaded(struct net_device *dev, bool threaded);
|
||||
+
|
||||
/**
|
||||
* napi_enable - enable NAPI scheduling
|
||||
* @n: napi context
|
||||
--- a/net/core/dev.c
|
||||
+++ b/net/core/dev.c
|
||||
@@ -3274,8 +3274,9 @@ static inline void ____napi_schedule(str
|
||||
|
||||
if (test_bit(NAPI_STATE_THREADED, &napi->state)) {
|
||||
/* Paired with smp_mb__before_atomic() in
|
||||
- * napi_enable(). Use READ_ONCE() to guarantee
|
||||
- * a complete read on napi->thread. Only call
|
||||
+ * napi_enable()/dev_set_threaded().
|
||||
+ * Use READ_ONCE() to guarantee a complete
|
||||
+ * read on napi->thread. Only call
|
||||
* wake_up_process() when it's not NULL.
|
||||
*/
|
||||
thread = READ_ONCE(napi->thread);
|
||||
@@ -4844,6 +4845,49 @@ static enum hrtimer_restart napi_watchdo
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
+int dev_set_threaded(struct net_device *dev, bool threaded)
|
||||
+{
|
||||
+ struct napi_struct *napi;
|
||||
+ int err = 0;
|
||||
+
|
||||
+ if (dev->threaded == threaded)
|
||||
+ return 0;
|
||||
+
|
||||
+ if (threaded) {
|
||||
+ list_for_each_entry(napi, &dev->napi_list, dev_list) {
|
||||
+ if (!napi->thread) {
|
||||
+ err = napi_kthread_create(napi);
|
||||
+ if (err) {
|
||||
+ threaded = false;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ dev->threaded = threaded;
|
||||
+
|
||||
+ /* Make sure kthread is created before THREADED bit
|
||||
+ * is set.
|
||||
+ */
|
||||
+ smp_mb__before_atomic();
|
||||
+
|
||||
+ /* Setting/unsetting threaded mode on a napi might not immediately
|
||||
+ * take effect, if the current napi instance is actively being
|
||||
+ * polled. In this case, the switch between threaded mode and
|
||||
+ * softirq mode will happen in the next round of napi_schedule().
|
||||
+ * This should not cause hiccups/stalls to the live traffic.
|
||||
+ */
|
||||
+ list_for_each_entry(napi, &dev->napi_list, dev_list) {
|
||||
+ if (threaded)
|
||||
+ set_bit(NAPI_STATE_THREADED, &napi->state);
|
||||
+ else
|
||||
+ clear_bit(NAPI_STATE_THREADED, &napi->state);
|
||||
+ }
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
|
||||
int (*poll)(struct napi_struct *, int), int weight)
|
||||
{
|
||||
--- a/net/core/net-sysfs.c
|
||||
+++ b/net/core/net-sysfs.c
|
||||
@@ -486,6 +486,45 @@ static ssize_t phys_switch_id_show(struc
|
||||
}
|
||||
static DEVICE_ATTR_RO(phys_switch_id);
|
||||
|
||||
+static ssize_t threaded_show(struct device *dev,
|
||||
+ struct device_attribute *attr, char *buf)
|
||||
+{
|
||||
+ struct net_device *netdev = to_net_dev(dev);
|
||||
+ ssize_t ret = -EINVAL;
|
||||
+
|
||||
+ if (!rtnl_trylock())
|
||||
+ return restart_syscall();
|
||||
+
|
||||
+ if (dev_isalive(netdev))
|
||||
+ ret = sprintf(buf, fmt_dec, netdev->threaded);
|
||||
+
|
||||
+ rtnl_unlock();
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int modify_napi_threaded(struct net_device *dev, unsigned long val)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ if (list_empty(&dev->napi_list))
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ if (val != 0 && val != 1)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ ret = dev_set_threaded(dev, val);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static ssize_t threaded_store(struct device *dev,
|
||||
+ struct device_attribute *attr,
|
||||
+ const char *buf, size_t len)
|
||||
+{
|
||||
+ return netdev_store(dev, attr, buf, len, modify_napi_threaded);
|
||||
+}
|
||||
+static DEVICE_ATTR_RW(threaded);
|
||||
+
|
||||
static struct attribute *net_class_attrs[] = {
|
||||
&dev_attr_netdev_group.attr,
|
||||
&dev_attr_type.attr,
|
||||
@@ -514,6 +553,7 @@ static struct attribute *net_class_attrs
|
||||
&dev_attr_phys_port_name.attr,
|
||||
&dev_attr_phys_switch_id.attr,
|
||||
&dev_attr_proto_down.attr,
|
||||
+ &dev_attr_threaded.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(net_class);
|
||||
@@ -1,75 +0,0 @@
|
||||
From: Wei Wang <weiwan@google.com>
|
||||
Date: Mon, 1 Mar 2021 17:21:13 -0800
|
||||
Subject: [PATCH] net: fix race between napi kthread mode and busy poll
|
||||
|
||||
Currently, napi_thread_wait() checks for NAPI_STATE_SCHED bit to
|
||||
determine if the kthread owns this napi and could call napi->poll() on
|
||||
it. However, if socket busy poll is enabled, it is possible that the
|
||||
busy poll thread grabs this SCHED bit (after the previous napi->poll()
|
||||
invokes napi_complete_done() and clears SCHED bit) and tries to poll
|
||||
on the same napi. napi_disable() could grab the SCHED bit as well.
|
||||
This patch tries to fix this race by adding a new bit
|
||||
NAPI_STATE_SCHED_THREADED in napi->state. This bit gets set in
|
||||
____napi_schedule() if the threaded mode is enabled, and gets cleared
|
||||
in napi_complete_done(), and we only poll the napi in kthread if this
|
||||
bit is set. This helps distinguish the ownership of the napi between
|
||||
kthread and other scenarios and fixes the race issue.
|
||||
|
||||
Fixes: 29863d41bb6e ("net: implement threaded-able napi poll loop support")
|
||||
Reported-by: Martin Zaharinov <micron10@gmail.com>
|
||||
Suggested-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Wei Wang <weiwan@google.com>
|
||||
Cc: Alexander Duyck <alexanderduyck@fb.com>
|
||||
Cc: Eric Dumazet <edumazet@google.com>
|
||||
Cc: Paolo Abeni <pabeni@redhat.com>
|
||||
Cc: Hannes Frederic Sowa <hannes@stressinduktion.org>
|
||||
---
|
||||
|
||||
--- a/include/linux/netdevice.h
|
||||
+++ b/include/linux/netdevice.h
|
||||
@@ -328,6 +328,7 @@ enum {
|
||||
NAPI_STATE_NPSVC, /* Netpoll - don't dequeue from poll_list */
|
||||
NAPI_STATE_HASHED, /* In NAPI hash */
|
||||
NAPI_STATE_THREADED, /* The poll is performed inside its own thread*/
|
||||
+ NAPI_STATE_SCHED_THREADED, /* Napi is currently scheduled in threaded mode */
|
||||
};
|
||||
|
||||
enum gro_result {
|
||||
--- a/net/core/dev.c
|
||||
+++ b/net/core/dev.c
|
||||
@@ -3281,6 +3281,8 @@ static inline void ____napi_schedule(str
|
||||
*/
|
||||
thread = READ_ONCE(napi->thread);
|
||||
if (thread) {
|
||||
+ if (thread->state != TASK_INTERRUPTIBLE)
|
||||
+ set_bit(NAPI_STATE_SCHED_THREADED, &napi->state);
|
||||
wake_up_process(thread);
|
||||
return;
|
||||
}
|
||||
@@ -5053,16 +5055,25 @@ static int napi_poll(struct napi_struct
|
||||
|
||||
static int napi_thread_wait(struct napi_struct *napi)
|
||||
{
|
||||
+ bool woken = false;
|
||||
+
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
while (!kthread_should_stop() && !napi_disable_pending(napi)) {
|
||||
- if (test_bit(NAPI_STATE_SCHED, &napi->state)) {
|
||||
+ /* Testing SCHED_THREADED bit here to make sure the current
|
||||
+ * kthread owns this napi and could poll on this napi.
|
||||
+ * Testing SCHED bit is not enough because SCHED bit might be
|
||||
+ * set by some other busy poll thread or by napi_disable().
|
||||
+ */
|
||||
+ if (test_bit(NAPI_STATE_SCHED_THREADED, &napi->state) || woken) {
|
||||
WARN_ON(!list_empty(&napi->poll_list));
|
||||
__set_current_state(TASK_RUNNING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
schedule();
|
||||
+ /* woken being true indicates this thread owns this napi. */
|
||||
+ woken = true;
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
}
|
||||
__set_current_state(TASK_RUNNING);
|
||||
@@ -1,53 +0,0 @@
|
||||
From: Paolo Abeni <pabeni@redhat.com>
|
||||
Date: Fri, 9 Apr 2021 17:24:17 +0200
|
||||
Subject: [PATCH] net: fix hangup on napi_disable for threaded napi
|
||||
|
||||
napi_disable() is subject to an hangup, when the threaded
|
||||
mode is enabled and the napi is under heavy traffic.
|
||||
|
||||
If the relevant napi has been scheduled and the napi_disable()
|
||||
kicks in before the next napi_threaded_wait() completes - so
|
||||
that the latter quits due to the napi_disable_pending() condition,
|
||||
the existing code leaves the NAPI_STATE_SCHED bit set and the
|
||||
napi_disable() loop waiting for such bit will hang.
|
||||
|
||||
This patch addresses the issue by dropping the NAPI_STATE_DISABLE
|
||||
bit test in napi_thread_wait(). The later napi_threaded_poll()
|
||||
iteration will take care of clearing the NAPI_STATE_SCHED.
|
||||
|
||||
This also addresses a related problem reported by Jakub:
|
||||
before this patch a napi_disable()/napi_enable() pair killed
|
||||
the napi thread, effectively disabling the threaded mode.
|
||||
On the patched kernel napi_disable() simply stops scheduling
|
||||
the relevant thread.
|
||||
|
||||
v1 -> v2:
|
||||
- let the main napi_thread_poll() loop clear the SCHED bit
|
||||
|
||||
Reported-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Fixes: 29863d41bb6e ("net: implement threaded-able napi poll loop support")
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
Reviewed-by: Eric Dumazet <edumazet@google.com>
|
||||
Link: https://lore.kernel.org/r/883923fa22745a9589e8610962b7dc59df09fb1f.1617981844.git.pabeni@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
|
||||
--- a/net/core/dev.c
|
||||
+++ b/net/core/dev.c
|
||||
@@ -5059,7 +5059,7 @@ static int napi_thread_wait(struct napi_
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
- while (!kthread_should_stop() && !napi_disable_pending(napi)) {
|
||||
+ while (!kthread_should_stop()) {
|
||||
/* Testing SCHED_THREADED bit here to make sure the current
|
||||
* kthread owns this napi and could poll on this napi.
|
||||
* Testing SCHED bit is not enough because SCHED bit might be
|
||||
@@ -5077,6 +5077,7 @@ static int napi_thread_wait(struct napi_
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
}
|
||||
__set_current_state(TASK_RUNNING);
|
||||
+
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
--- a/drivers/net/wireless/ath/ath11k/ahb.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/ahb.c
|
||||
@@ -526,6 +526,7 @@ static int ath11k_ahb_ext_irq_config(str
|
||||
int irq;
|
||||
int ret;
|
||||
bool nss_offload;
|
||||
+ static int devidx = 0;
|
||||
|
||||
/* TCL Completion, REO Dest, ERR, Exception and h2rxdma rings are offloaded
|
||||
* to nss when its enabled, hence don't enable these interrupts
|
||||
@@ -539,6 +540,9 @@ static int ath11k_ahb_ext_irq_config(str
|
||||
irq_grp->ab = ab;
|
||||
irq_grp->grp_id = i;
|
||||
init_dummy_netdev(&irq_grp->napi_ndev);
|
||||
+ snprintf(irq_grp->napi_ndev.name, sizeof(irq_grp->napi_ndev.name), "%s%d:%d",
|
||||
+ "ath11k_ahb", devidx, i);
|
||||
+ irq_grp->napi_ndev.threaded = 1;
|
||||
netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi,
|
||||
ath11k_ahb_ext_grp_napi_poll, NAPI_POLL_WEIGHT);
|
||||
|
||||
@@ -604,6 +608,8 @@ static int ath11k_ahb_ext_irq_config(str
|
||||
}
|
||||
}
|
||||
|
||||
+ devidx++;
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath11k/pci.c
|
||||
+++ b/drivers/net/wireless/ath/ath11k/pci.c
|
||||
@@ -806,6 +806,7 @@ static int ath11k_pci_ext_irq_config(str
|
||||
int i, j, ret, num_vectors = 0;
|
||||
u32 user_base_data = 0, base_vector = 0, base_idx;
|
||||
u8 domain_id;
|
||||
+ static int devidx = 0;
|
||||
|
||||
domain_id = ath11k_pci_get_domain_id(ab);
|
||||
base_idx = ATH11K_PCI_IRQ_CE0_OFFSET + CE_COUNT_MAX;
|
||||
@@ -822,6 +823,9 @@ static int ath11k_pci_ext_irq_config(str
|
||||
irq_grp->ab = ab;
|
||||
irq_grp->grp_id = i;
|
||||
init_dummy_netdev(&irq_grp->napi_ndev);
|
||||
+ snprintf(irq_grp->napi_ndev.name, sizeof(irq_grp->napi_ndev.name), "%s%d:%d",
|
||||
+ "ath11k_pci", devidx, i);
|
||||
+ irq_grp->napi_ndev.threaded = 1;
|
||||
netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi,
|
||||
ath11k_pci_ext_grp_napi_poll, NAPI_POLL_WEIGHT);
|
||||
|
||||
@@ -868,6 +872,7 @@ static int ath11k_pci_ext_irq_config(str
|
||||
}
|
||||
}
|
||||
|
||||
+ devidx++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user