FAILED: patch "[PATCH] batman-adv: Drop unmanaged ELP metric worker" failed to apply to 5.4-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.4-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.4.y
git checkout FETCH_HEAD
git cherry-pick -x 8c8ecc98f5c65947b0070a24bac11e12e47cc65d
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2025021842-crusher-corrode-54d0@gregkh' --subject-prefix 'PATCH 5.4.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 8c8ecc98f5c65947b0070a24bac11e12e47cc65d Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <[email protected]>
Date: Mon, 20 Jan 2025 00:06:11 +0100
Subject: [PATCH] batman-adv: Drop unmanaged ELP metric worker
The ELP worker needs to calculate new metric values for all neighbors "reachable" over an interface. Some of the used metric sources require locks which might need to sleep. This sleep is incompatible with the RCU list iterator used for the recorded neighbors. The initial approach to work around of this problem was to queue another work item per neighbor and then run this in a new context.
Even when this solved the RCU vs might_sleep() conflict, it has a major
problems: Nothing was stopping the work item in case it is not needed anymore - for example because one of the related interfaces was removed or the batman-adv module was unloaded - resulting in potential invalid memory accesses.
Directly canceling the metric worker also has various problems:
* cancel_work_sync for a to-be-deactivated interface is called with rtnl_lock held. But the code in the ELP metric worker also tries to use rtnl_lock() - which will never return in this case. This also means that cancel_work_sync would never return because it is waiting for the worker to finish.
* iterating over the neighbor list for the to-be-deactivated interface is currently done using the RCU specific methods. Which means that it is possible to miss items when iterating over it without the associated spinlock - a behaviour which is acceptable for a periodic metric check but not for a cleanup routine (which must "stop" all still running workers)
The better approch is to get rid of the per interface neighbor metric worker and handle everything in the interface worker. The original problems are solved by:
* creating a list of neighbors which require new metric information inside the RCU protected context, gathering the metric according to the new list outside the RCU protected context
* only use rcu_trylock inside metric gathering code to avoid a deadlock when the cancel_delayed_work_sync is called in the interface removal code (which is called with the rtnl_lock held)
net/batman-adv/bat_v.c +0 -2
@@ -113,8 +113,6 @@ static void batadv_v_hardif_neigh_init(struct batadv_hardif_neigh_node *hardif_neigh) {ewma_throughput_init (&hardif_neigh->bat_v.throughput); -INIT_WORK (&hardif_neigh->bat_v.metric_work, - batadv_v_elp_throughput_metric_update); }/**
net/batman-adv/bat_v_elp.c +48 -23
143 lines not on this page · raw message
net/batman-adv/bat_v_elp.h +0 -2
@@ -10,7 +10,6 @@#include "main.h" #include <linux/skbuff.h> -#include <linux/workqueue.h> int batadv_v_elp_iface_enable (struct batadv_hard_iface *hard_iface);void batadv_v_elp_iface_disable (struct batadv_hard_iface *hard_iface);
@@ -19,6 +18,5 @@ void batadv_v_elp_iface_activate(struct batadv_hard_iface *primary_iface,void batadv_v_elp_primary_iface_set (struct batadv_hard_iface *primary_iface);int batadv_v_elp_packet_recv (struct sk_buff *skb,struct batadv_hard_iface *if_incoming); -void batadv_v_elp_throughput_metric_update (struct work_struct *work);#endif /* _NET_BATMAN_ADV_BAT_V_ELP_H_ */
net/batman-adv/types.h +0 -3
@@ -596,9 +596,6 @@ struct batadv_hardif_neigh_node_bat_v {* neighbor */ unsigned long last_unicast_tx; - -/** @metric_work: work queue callback item for metric update */ -struct work_struct metric_work; };/**
Sven Eckelmann
maintainerPATCH 1/2 batman-adv: Ignore neighbor throughput metrics in error case
commit e7e34ffc976aaae4f465b7898303241b81ceefc3 upstream.
If a temporary error happened in the evaluation of the neighbor throughput information, then the invalid throughput result should not be stored in the throughtput EWMA.
--- net/batman-adv/bat_v_elp.c | 41 ++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 13 deletions(-)
net/batman-adv/bat_v_elp.c +28 -13
@@ -58,11 +58,13 @@ static void batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface)/** * batadv_v_elp_get_throughput() - get the throughput towards a neighbour * @neigh: the neighbour for which the throughput has to be obtained +* @pthroughput: calculated throughput towards the given neighbour in multiples +* of 100kpbs (a value of '1' equals 0.1Mbps, '10' equals 1Mbps, etc). * -* Return: The throughput towards the given neighbour in multiples of 100kpbs -* (a value of '1' equals to 0.1Mbps, '10' equals 1Mbps, etc). +* Return: true when value behind @pthroughput was set */ -static u32 batadv_v_elp_get_throughput (struct batadv_hardif_neigh_node *neigh) +static bool batadv_v_elp_get_throughput (struct batadv_hardif_neigh_node *neigh, +u32 *pthroughput) {struct batadv_hard_iface *hard_iface = neigh->if_incoming;struct net_device *soft_iface = hard_iface->soft_iface;
@@ -76,14 +78,16 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)* batman-adv interface */ if (!soft_iface) -return BATADV_THROUGHPUT_DEFAULT_VALUE ; +return false;/* if the user specified a customised value for this interface, then * return it directly */ throughput =atomic_read (&hard_iface->bat_v.throughput_override); -if (throughput !=0 ) -return throughput; +if (throughput !=0 ) { + *pthroughput = throughput; +return true; + }/* if this is a wireless device, then ask its throughput through * cfg80211 API
@@ -110,14 +114,16 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh)* possible to delete this neighbor. For now set * the throughput metric to 0. */ -return 0 ; + *pthroughput =0 ; +return true; }if (ret) goto default_throughput;if (!(sinfo.filled &BIT (NL80211_STA_INFO_EXPECTED_THROUGHPUT ))) goto default_throughput; -return sinfo.expected_throughput /100 ; + *pthroughput = sinfo.expected_throughput /100 ; +return true; }/* if not a wifi interface, check if this device provides data via
@@ -135,8 +141,10 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh) hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX ; throughput = link_settings.base.speed; -if (throughput && throughput !=SPEED_UNKNOWN ) -return throughput *10 ; +if (throughput && throughput !=SPEED_UNKNOWN ) { + *pthroughput = throughput *10 ; +return true; + } } default_throughput:
@@ -150,7 +158,8 @@ static u32 batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh) }/* if none of the above cases apply, return the base_throughput */ -return BATADV_THROUGHPUT_DEFAULT_VALUE ; + *pthroughput =BATADV_THROUGHPUT_DEFAULT_VALUE ; +return true; }/**
@@ -162,15 +171,21 @@ void batadv_v_elp_throughput_metric_update(struct work_struct *work) {struct batadv_hardif_neigh_node_bat_v *neigh_bat_v;struct batadv_hardif_neigh_node *neigh; +u32 throughput; +bool valid; neigh_bat_v = container_of(work, struct batadv_hardif_neigh_node_bat_v, metric_work); neigh = container_of(neigh_bat_v, struct batadv_hardif_neigh_node, bat_v); -ewma_throughput_add (&neigh->bat_v.throughput, -batadv_v_elp_get_throughput (neigh)); + valid =batadv_v_elp_get_throughput (neigh, &throughput); +if (!valid) + goto put_neigh; + +ewma_throughput_add (&neigh->bat_v.throughput, throughput); +put_neigh:/* decrement refcounter to balance increment performed before scheduling * this task */
Signature · 1 line
2.39.5
Sven Eckelmann
maintainerPATCH 2/2 batman-adv: Drop unmanaged ELP metric worker
commit 8c8ecc98f5c65947b0070a24bac11e12e47cc65d upstream.
The ELP worker needs to calculate new metric values for all neighbors "reachable" over an interface. Some of the used metric sources require locks which might need to sleep. This sleep is incompatible with the RCU list iterator used for the recorded neighbors. The initial approach to work around of this problem was to queue another work item per neighbor and then run this in a new context.
Even when this solved the RCU vs might_sleep() conflict, it has a major
problems: Nothing was stopping the work item in case it is not needed anymore - for example because one of the related interfaces was removed or the batman-adv module was unloaded - resulting in potential invalid memory accesses.
Directly canceling the metric worker also has various problems:
* cancel_work_sync for a to-be-deactivated interface is called with rtnl_lock held. But the code in the ELP metric worker also tries to use rtnl_lock() - which will never return in this case. This also means that cancel_work_sync would never return because it is waiting for the worker to finish.
* iterating over the neighbor list for the to-be-deactivated interface is currently done using the RCU specific methods. Which means that it is possible to miss items when iterating over it without the associated spinlock - a behaviour which is acceptable for a periodic metric check but not for a cleanup routine (which must "stop" all still running workers)
The better approch is to get rid of the per interface neighbor metric worker and handle everything in the interface worker. The original problems are solved by:
* creating a list of neighbors which require new metric information inside the RCU protected context, gathering the metric according to the new list outside the RCU protected context
* only use rcu_trylock inside metric gathering code to avoid a deadlock when the cancel_delayed_work_sync is called in the interface removal code (which is called with the rtnl_lock held)
--- net/batman-adv/bat_v.c | 2 -- net/batman-adv/bat_v_elp.c | 74 +++++++++++++++++++++++++------------- net/batman-adv/bat_v_elp.h | 2 -- net/batman-adv/types.h | 3 -- 4 files changed, 50 insertions(+), 31 deletions(-)
net/batman-adv/bat_v.c +0 -2
@@ -115,8 +115,6 @@ static void batadv_v_hardif_neigh_init(struct batadv_hardif_neigh_node *hardif_neigh) {ewma_throughput_init (&hardif_neigh->bat_v.throughput); -INIT_WORK (&hardif_neigh->bat_v.metric_work, - batadv_v_elp_throughput_metric_update); }#ifdef CONFIG_BATMAN_ADV_DEBUGFS
net/batman-adv/bat_v_elp.c +50 -24
146 lines not on this page · raw message
net/batman-adv/bat_v_elp.h +0 -2
@@ -10,7 +10,6 @@#include "main.h" #include <linux/skbuff.h> -#include <linux/workqueue.h> int batadv_v_elp_iface_enable (struct batadv_hard_iface *hard_iface);void batadv_v_elp_iface_disable (struct batadv_hard_iface *hard_iface);
@@ -19,6 +18,5 @@ void batadv_v_elp_iface_activate(struct batadv_hard_iface *primary_iface,void batadv_v_elp_primary_iface_set (struct batadv_hard_iface *primary_iface);int batadv_v_elp_packet_recv (struct sk_buff *skb,struct batadv_hard_iface *if_incoming); -void batadv_v_elp_throughput_metric_update (struct work_struct *work);#endif /* _NET_BATMAN_ADV_BAT_V_ELP_H_ */
net/batman-adv/types.h +0 -3
@@ -603,9 +603,6 @@ struct batadv_hardif_neigh_node_bat_v {* neighbor */ unsigned long last_unicast_tx; - -/** @metric_work: work queue callback item for metric update */ -struct work_struct metric_work; };/**
Signature · 1 line
2.39.5