Skip to content

Commit

Permalink
wifi: mac80211: fix monitor channel with chanctx emulation
Browse files Browse the repository at this point in the history
After the channel context emulation, there were reports that
changing the monitor channel no longer works. This is because
those drivers don't have WANT_MONITOR_VIF, so the setting the
channel always exits out quickly.

Fix this by always allocating the virtual monitor sdata, and
simply not telling the driver about it unless it wanted to.
This way, we have an interface/sdata to bind the chanctx to,
and the emulation can work correctly.

Cc: stable@vger.kernel.org
Fixes: 0a44dfc ("wifi: mac80211: simplify non-chanctx drivers")
Reported-by: Savyasaachi Vanga <savyasaachiv@gmail.com>
Closes: https://lore.kernel.org/r/chwoymvpzwtbmzryrlitpwmta5j6mtndocxsyqvdyikqu63lon@gfds653hkknl
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Cherry-picked-for: https://gitlab.archlinux.org/archlinux/packaging/packages/linux/-/issues/54
  • Loading branch information
jmberg-intel authored and heftig committed Jun 21, 2024
1 parent 21a9744 commit bfa1a62
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 13 deletions.
17 changes: 17 additions & 0 deletions net/mac80211/driver-ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,18 @@ int drv_assign_vif_chanctx(struct ieee80211_local *local,
might_sleep();
lockdep_assert_wiphy(local->hw.wiphy);

/*
* We should perhaps push emulate chanctx down and only
* make it call ->config() when the chanctx is actually
* assigned here (and unassigned below), but that's yet
* another change to all drivers to add assign/unassign
* emulation callbacks. Maybe later.
*/
if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
local->emulate_chanctx &&
!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
return 0;

if (!check_sdata_in_driver(sdata))
return -EIO;

Expand Down Expand Up @@ -338,6 +350,11 @@ void drv_unassign_vif_chanctx(struct ieee80211_local *local,
might_sleep();
lockdep_assert_wiphy(local->hw.wiphy);

if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
local->emulate_chanctx &&
!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
return;

if (!check_sdata_in_driver(sdata))
return;

Expand Down
21 changes: 9 additions & 12 deletions net/mac80211/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -1121,9 +1121,6 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
struct ieee80211_sub_if_data *sdata;
int ret;

if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
return 0;

ASSERT_RTNL();
lockdep_assert_wiphy(local->hw.wiphy);

Expand All @@ -1145,11 +1142,13 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)

ieee80211_set_default_queues(sdata);

ret = drv_add_interface(local, sdata);
if (WARN_ON(ret)) {
/* ok .. stupid driver, it asked for this! */
kfree(sdata);
return ret;
if (ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF)) {
ret = drv_add_interface(local, sdata);
if (WARN_ON(ret)) {
/* ok .. stupid driver, it asked for this! */
kfree(sdata);
return ret;
}
}

set_bit(SDATA_STATE_RUNNING, &sdata->state);
Expand Down Expand Up @@ -1187,9 +1186,6 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;

if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
return;

ASSERT_RTNL();
lockdep_assert_wiphy(local->hw.wiphy);

Expand All @@ -1209,7 +1205,8 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local)

ieee80211_link_release_channel(&sdata->deflink);

drv_remove_interface(local, sdata);
if (ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
drv_remove_interface(local, sdata);

kfree(sdata);
}
Expand Down
2 changes: 1 addition & 1 deletion net/mac80211/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1841,7 +1841,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)

/* add interfaces */
sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata);
if (sdata) {
if (sdata && ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF)) {
/* in HW restart it exists already */
WARN_ON(local->resuming);
res = drv_add_interface(local, sdata);
Expand Down

0 comments on commit bfa1a62

Please sign in to comment.