mac80211: fix change_interface queue assignments

Jouni reported that with mac80211_hwsim, multicast TX was causing
crashes due to invalid vif->cab_queue assignment. It turns out that
this is caused by change_interface() getting invoked and not having
the vif->type/vif->p2p assigned correctly before calling the queue
check (ieee80211_check_queues). Fix this by passing the 'external'
interface type to the function and adjusting it accordingly.

While at it, also fix the error path in change_interface, it wasn't
correctly resetting to the external type but using the internal one
instead.

Fortunately this affects on hwsim because all other drivers set the
vif->type/vif->p2p variables when changing iftype. This shouldn't
be needed, but almost all implementations actually do it for their
own internal handling.

Reported-by: Jouni Malinen <j@w1.fi>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2013-08-26 09:30:32 +02:00
parent bcdd822007
commit a986553877
1 changed files with 11 additions and 8 deletions

View File

@ -308,12 +308,13 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
return 0; return 0;
} }
static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata) static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata,
enum nl80211_iftype iftype)
{ {
int n_queues = sdata->local->hw.queues; int n_queues = sdata->local->hw.queues;
int i; int i;
if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) { if (iftype != NL80211_IFTYPE_P2P_DEVICE) {
for (i = 0; i < IEEE80211_NUM_ACS; i++) { for (i = 0; i < IEEE80211_NUM_ACS; i++) {
if (WARN_ON_ONCE(sdata->vif.hw_queue[i] == if (WARN_ON_ONCE(sdata->vif.hw_queue[i] ==
IEEE80211_INVAL_HW_QUEUE)) IEEE80211_INVAL_HW_QUEUE))
@ -324,8 +325,9 @@ static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata)
} }
} }
if ((sdata->vif.type != NL80211_IFTYPE_AP && if ((iftype != NL80211_IFTYPE_AP &&
sdata->vif.type != NL80211_IFTYPE_MESH_POINT) || iftype != NL80211_IFTYPE_P2P_GO &&
iftype != NL80211_IFTYPE_MESH_POINT) ||
!(sdata->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)) { !(sdata->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)) {
sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE; sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE;
return 0; return 0;
@ -408,7 +410,7 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
return ret; return ret;
} }
ret = ieee80211_check_queues(sdata); ret = ieee80211_check_queues(sdata, NL80211_IFTYPE_MONITOR);
if (ret) { if (ret) {
kfree(sdata); kfree(sdata);
return ret; return ret;
@ -592,7 +594,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
res = drv_add_interface(local, sdata); res = drv_add_interface(local, sdata);
if (res) if (res)
goto err_stop; goto err_stop;
res = ieee80211_check_queues(sdata); res = ieee80211_check_queues(sdata,
ieee80211_vif_type_p2p(&sdata->vif));
if (res) if (res)
goto err_del_interface; goto err_del_interface;
} }
@ -1389,14 +1392,14 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
ret = drv_change_interface(local, sdata, internal_type, p2p); ret = drv_change_interface(local, sdata, internal_type, p2p);
if (ret) if (ret)
type = sdata->vif.type; type = ieee80211_vif_type_p2p(&sdata->vif);
/* /*
* Ignore return value here, there's not much we can do since * Ignore return value here, there's not much we can do since
* the driver changed the interface type internally already. * the driver changed the interface type internally already.
* The warnings will hopefully make driver authors fix it :-) * The warnings will hopefully make driver authors fix it :-)
*/ */
ieee80211_check_queues(sdata); ieee80211_check_queues(sdata, type);
ieee80211_setup_sdata(sdata, type); ieee80211_setup_sdata(sdata, type);