* [PATCH 0/2] Fix ieee80211_color_collision_detection_work() locking
@ 2024-09-24 19:28 Remi Pommarel
2024-09-24 19:28 ` [PATCH 1/2] wifi: cfg80211: Add wiphy_delayed_work_pending() Remi Pommarel
2024-09-24 19:28 ` [PATCH 2/2] wifi: mac80211: Convert color collision detection to wiphy work Remi Pommarel
0 siblings, 2 replies; 3+ messages in thread
From: Remi Pommarel @ 2024-09-24 19:28 UTC (permalink / raw)
To: linux-wireless, linux-kernel
Cc: Johannes Berg, Nicolas Cavallari, Nicolas Escande, Remi Pommarel
Call to ieee80211_color_collision_detection_work() needs wiphy lock to be
held. This lock cannot be taken directly in the delayed work because it
would cause a potential deadlock. Indeed ieee80211_link_stop() is calling
cancel_delayed_work_sync() with the lock held. To avoid such deadlock one
can use a wiphy delayed work instead.
Thus this patchset convert ieee80211_color_collision_detection_work() to
use wiphy delayed work instead of classical delayed work. But because
this work is used to ratelimit cfg80211_obss_color_collision_notify() a
way to tell if it has already been scheduled is needed as discussed in
[0]. In this thread three different solutions has been considered:
- Implementing wiphy_delayed_work_pending() to detect if work can be
queued without postponing a previous schedule
- Using a __ratelimit or an equivalent to queue the work
- Modifying wiphy_delayed_work_queue() to mimic queue_delayed_work() by
not queuing the work if already queued (while also possibly having to
add wiphy_delayed_work_mod() for those who need the mod_delayed_work()
behavior).
This patchset implement the first solution.
[0]: https://lore.kernel.org/linux-wireless/D4A40Q44OAY2.W3SIF6UEPBUN@freebox.fr/
Remi Pommarel (2):
wifi: cfg80211: Add wiphy_delayed_work_pending()
wifi: mac80211: Convert color collision detection to wiphy work
include/net/cfg80211.h | 44 ++++++++++++++++++++++++++++++++++++++
net/mac80211/cfg.c | 17 ++++++++-------
net/mac80211/ieee80211_i.h | 5 +++--
net/mac80211/link.c | 7 +++---
net/wireless/core.c | 7 ++++++
5 files changed, 67 insertions(+), 13 deletions(-)
--
2.46.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 1/2] wifi: cfg80211: Add wiphy_delayed_work_pending()
2024-09-24 19:28 [PATCH 0/2] Fix ieee80211_color_collision_detection_work() locking Remi Pommarel
@ 2024-09-24 19:28 ` Remi Pommarel
2024-09-24 19:28 ` [PATCH 2/2] wifi: mac80211: Convert color collision detection to wiphy work Remi Pommarel
1 sibling, 0 replies; 3+ messages in thread
From: Remi Pommarel @ 2024-09-24 19:28 UTC (permalink / raw)
To: linux-wireless, linux-kernel
Cc: Johannes Berg, Nicolas Cavallari, Nicolas Escande, Remi Pommarel
Add wiphy_delayed_work_pending() to check if any delayed work timer is
pending, that can be used to be sure that wiphy_delayed_work_queue()
won't postpone an already pending delayed work.
Signed-off-by: Remi Pommarel <repk@triplefau.lt>
---
include/net/cfg80211.h | 44 ++++++++++++++++++++++++++++++++++++++++++
net/wireless/core.c | 7 +++++++
2 files changed, 51 insertions(+)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 192d72c8b465..01697b624095 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -6129,6 +6129,50 @@ void wiphy_delayed_work_cancel(struct wiphy *wiphy,
void wiphy_delayed_work_flush(struct wiphy *wiphy,
struct wiphy_delayed_work *dwork);
+/**
+ * wiphy_delayed_work_pending - Find out whether a wiphy delayable
+ * work item is currently pending.
+ *
+ * @wiphy: the wiphy, for debug purposes
+ * @dwork: the delayed work in question
+ *
+ * return value: true if timer is pending, false otherwise
+ *
+ * How wiphy_delayed_work_queue() works is by setting a timer which
+ * when it expires calls wiphy_work_queue() to queue the wiphy work.
+ * Because wiphy_delayed_work_queue() uses mod_timer(), if it is
+ * called twice and the second call happens before the first call
+ * deadline, the work will rescheduled for the second deadline and
+ * won't run before that.
+ *
+ * wiphy_delayed_work_pending() can be used to detect if calling
+ * wiphy_work_delayed_work_queue() would start a new work schedule
+ * or delayed a previous one. As seen below it cannot be used to
+ * detect precisely if the work has finished to execute nor if it
+ * is currently executing.
+ *
+ * CPU0 CPU1
+ * wiphy_delayed_work_queue(wk)
+ * mod_timer(wk->timer)
+ * wiphy_delayed_work_pending(wk) -> true
+ *
+ * [...]
+ * expire_timers(wk->timer)
+ * detach_timer(wk->timer)
+ * wiphy_delayed_work_pending(wk) -> false
+ * wk->timer->function() |
+ * wiphy_work_queue(wk) | delayed work pending
+ * list_add_tail() | returns false but
+ * queue_work(cfg80211_wiphy_work) | wk->func() has not
+ * | been run yet
+ * [...] |
+ * cfg80211_wiphy_work() |
+ * wk->func() V
+ *
+ */
+bool wiphy_delayed_work_pending(struct wiphy *wiphy,
+ struct wiphy_delayed_work *dwork);
+
/**
* enum ieee80211_ap_reg_power - regulatory power for an Access Point
*
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 4d5d351bd0b5..c9a1158dbdd1 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -1704,6 +1704,13 @@ void wiphy_delayed_work_flush(struct wiphy *wiphy,
}
EXPORT_SYMBOL_GPL(wiphy_delayed_work_flush);
+bool wiphy_delayed_work_pending(struct wiphy *wiphy,
+ struct wiphy_delayed_work *dwork)
+{
+ return timer_pending(&dwork->timer);
+}
+EXPORT_SYMBOL_GPL(wiphy_delayed_work_pending);
+
static int __init cfg80211_init(void)
{
int err;
--
2.46.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 2/2] wifi: mac80211: Convert color collision detection to wiphy work
2024-09-24 19:28 [PATCH 0/2] Fix ieee80211_color_collision_detection_work() locking Remi Pommarel
2024-09-24 19:28 ` [PATCH 1/2] wifi: cfg80211: Add wiphy_delayed_work_pending() Remi Pommarel
@ 2024-09-24 19:28 ` Remi Pommarel
1 sibling, 0 replies; 3+ messages in thread
From: Remi Pommarel @ 2024-09-24 19:28 UTC (permalink / raw)
To: linux-wireless, linux-kernel
Cc: Johannes Berg, Nicolas Cavallari, Nicolas Escande, Remi Pommarel
Call to ieee80211_color_collision_detection_work() needs wiphy lock to
be held (see lockdep assert in cfg80211_bss_color_notify()). Not locking
wiphy causes the following lockdep error:
WARNING: CPU: 2 PID: 42 at net/wireless/nl80211.c:19505 cfg80211_bss_color_notify+0x1a4/0x25c
Modules linked in:
CPU: 2 PID: 42 Comm: kworker/u8:3 Tainted: G W 6.4.0-02327-g36c6cb260481 #1048
Hardware name:
Workqueue: phy1 ieee80211_color_collision_detection_work
pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : cfg80211_bss_color_notify+0x1a4/0x25c
lr : cfg80211_bss_color_notify+0x1a0/0x25c
sp : ffff000002947d00
x29: ffff000002947d00 x28: ffff800008e1a000 x27: ffff000002bd4705
x26: ffff00000d034000 x25: ffff80000903cf40 x24: 0000000000000000
x23: ffff00000cb70720 x22: 0000000000800000 x21: ffff800008dfb008
x20: 000000000000008d x19: ffff00000d035fa8 x18: 0000000000000010
x17: 0000000000000001 x16: 000003564b1ce96a x15: 000d69696d057970
x14: 000000000000003b x13: 0000000000000001 x12: 0000000000040000
x11: 0000000000000001 x10: ffff80000978f9c0 x9 : ffff0000028d3174
x8 : ffff800008e30000 x7 : 0000000000000000 x6 : 0000000000000028
x5 : 000000000002f498 x4 : ffff00000d034a80 x3 : 0000000000800000
x2 : ffff800016143000 x1 : 0000000000000000 x0 : 0000000000000000
Call trace:
cfg80211_bss_color_notify+0x1a4/0x25c
ieee80211_color_collision_detection_work+0x20/0x118
process_one_work+0x294/0x554
worker_thread+0x70/0x440
kthread+0xf4/0xf8
ret_from_fork+0x10/0x20
irq event stamp: 77372
hardirqs last enabled at (77371): [<ffff800008a346fc>] _raw_spin_unlock_irq+0x2c/0x4c
hardirqs last disabled at (77372): [<ffff800008a28754>] el1_dbg+0x20/0x48
softirqs last enabled at (77350): [<ffff8000089e120c>] batadv_send_outstanding_bcast_packet+0xb8/0x120
softirqs last disabled at (77348): [<ffff8000089e11d4>] batadv_send_outstanding_bcast_packet+0x80/0x120
The wiphy lock cannot be taken directly from color collision detection
delayed work (ieee80211_color_collision_detection_work()) because this
work is cancel_delayed_work_sync() under this wiphy lock causing a
potential deadlock( see [0] for details).
To fix that ieee80211_color_collision_detection_work() could be
converted to a wiphy work and cancel_delayed_work_sync() can be simply
replaced by wiphy_delayed_work_cancel() serving the same purpose under
wiphy lock.
This could potentially fix [1].
[0]: https://lore.kernel.org/linux-wireless/D4A40Q44OAY2.W3SIF6UEPBUN@freebox.fr/
[1]: https://lore.kernel.org/lkml/000000000000612f290618eee3e5@google.com/
Reported-by: Nicolas Escande <nescande@freebox.fr>
Signed-off-by: Remi Pommarel <repk@triplefau.lt>
---
net/mac80211/cfg.c | 17 +++++++++--------
net/mac80211/ieee80211_i.h | 5 +++--
net/mac80211/link.c | 7 ++++---
3 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index b02b84ce2130..09025d59cdbe 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -4819,12 +4819,12 @@ void ieee80211_color_change_finalize_work(struct wiphy *wiphy,
ieee80211_color_change_finalize(link);
}
-void ieee80211_color_collision_detection_work(struct work_struct *work)
+void ieee80211_color_collision_detection_work(struct wiphy *wiphy,
+ struct wiphy_work *work)
{
- struct delayed_work *delayed_work = to_delayed_work(work);
struct ieee80211_link_data *link =
- container_of(delayed_work, struct ieee80211_link_data,
- color_collision_detect_work);
+ container_of(work, struct ieee80211_link_data,
+ color_collision_detect_work.work);
struct ieee80211_sub_if_data *sdata = link->sdata;
cfg80211_obss_color_collision_notify(sdata->dev, link->color_bitmap,
@@ -4877,7 +4877,8 @@ ieee80211_obss_color_collision_notify(struct ieee80211_vif *vif,
return;
}
- if (delayed_work_pending(&link->color_collision_detect_work)) {
+ if (wiphy_delayed_work_pending(sdata->local->hw.wiphy,
+ &link->color_collision_detect_work)) {
rcu_read_unlock();
return;
}
@@ -4886,9 +4887,9 @@ ieee80211_obss_color_collision_notify(struct ieee80211_vif *vif,
/* queue the color collision detection event every 500 ms in order to
* avoid sending too much netlink messages to userspace.
*/
- ieee80211_queue_delayed_work(&sdata->local->hw,
- &link->color_collision_detect_work,
- msecs_to_jiffies(500));
+ wiphy_delayed_work_queue(sdata->local->hw.wiphy,
+ &link->color_collision_detect_work,
+ msecs_to_jiffies(500));
rcu_read_unlock();
}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index a3485e4c6132..8650b3e8f1a9 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1051,7 +1051,7 @@ struct ieee80211_link_data {
} csa;
struct wiphy_work color_change_finalize_work;
- struct delayed_work color_collision_detect_work;
+ struct wiphy_delayed_work color_collision_detect_work;
u64 color_bitmap;
/* context reservation -- protected with wiphy mutex */
@@ -2004,7 +2004,8 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
/* color change handling */
void ieee80211_color_change_finalize_work(struct wiphy *wiphy,
struct wiphy_work *work);
-void ieee80211_color_collision_detection_work(struct work_struct *work);
+void ieee80211_color_collision_detection_work(struct wiphy *wiphy,
+ struct wiphy_work *work);
/* interface handling */
#define MAC80211_SUPPORTED_FEATURES_TX (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \
diff --git a/net/mac80211/link.c b/net/mac80211/link.c
index 1a211b8d4057..67f65ff79a71 100644
--- a/net/mac80211/link.c
+++ b/net/mac80211/link.c
@@ -41,8 +41,8 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
ieee80211_csa_finalize_work);
wiphy_work_init(&link->color_change_finalize_work,
ieee80211_color_change_finalize_work);
- INIT_DELAYED_WORK(&link->color_collision_detect_work,
- ieee80211_color_collision_detection_work);
+ wiphy_delayed_work_init(&link->color_collision_detect_work,
+ ieee80211_color_collision_detection_work);
INIT_LIST_HEAD(&link->assigned_chanctx_list);
INIT_LIST_HEAD(&link->reserved_chanctx_list);
@@ -70,7 +70,8 @@ void ieee80211_link_stop(struct ieee80211_link_data *link)
if (link->sdata->vif.type == NL80211_IFTYPE_STATION)
ieee80211_mgd_stop_link(link);
- cancel_delayed_work_sync(&link->color_collision_detect_work);
+ wiphy_delayed_work_cancel(link->sdata->local->hw.wiphy,
+ &link->color_collision_detect_work);
wiphy_work_cancel(link->sdata->local->hw.wiphy,
&link->color_change_finalize_work);
wiphy_work_cancel(link->sdata->local->hw.wiphy,
--
2.46.0
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2024-09-24 19:27 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-09-24 19:28 [PATCH 0/2] Fix ieee80211_color_collision_detection_work() locking Remi Pommarel
2024-09-24 19:28 ` [PATCH 1/2] wifi: cfg80211: Add wiphy_delayed_work_pending() Remi Pommarel
2024-09-24 19:28 ` [PATCH 2/2] wifi: mac80211: Convert color collision detection to wiphy work Remi Pommarel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®