summaryrefslogtreecommitdiff
path: root/net/mac80211/tx.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/tx.c')
-rw-r--r--net/mac80211/tx.c183
1 files changed, 115 insertions, 68 deletions
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index c9bf83f..eee448a 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -324,11 +324,6 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
- /*
- * virtual interfaces are protected by RCU
- */
- rcu_read_lock();
-
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
struct ieee80211_if_ap *ap;
if (sdata->vif.type != NL80211_IFTYPE_AP)
@@ -360,8 +355,6 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
}
}
- rcu_read_unlock();
-
local->total_ps_buffered = total;
ps_dbg_hw(&local->hw, "PS buffers full - purged %d frames\n", purged);
}
@@ -1372,7 +1365,8 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
* Returns false if the frame couldn't be transmitted but was queued instead.
*/
static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, bool txpending)
+ struct sk_buff *skb, bool txpending,
+ enum ieee80211_band band)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_data tx;
@@ -1386,20 +1380,18 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
return true;
}
- rcu_read_lock();
-
/* initialises tx */
led_len = skb->len;
res_prepare = ieee80211_tx_prepare(sdata, &tx, skb);
if (unlikely(res_prepare == TX_DROP)) {
ieee80211_free_txskb(&local->hw, skb);
- goto out;
+ return true;
} else if (unlikely(res_prepare == TX_QUEUED)) {
- goto out;
+ return true;
}
- info->band = local->hw.conf.channel->band;
+ info->band = band;
/* set up hw_queue value early */
if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) ||
@@ -1410,8 +1402,7 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
if (!invoke_tx_handlers(&tx))
result = __ieee80211_tx(local, &tx.skbs, led_len,
tx.sta, txpending);
- out:
- rcu_read_unlock();
+
return result;
}
@@ -1446,7 +1437,8 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
return 0;
}
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+ enum ieee80211_band band)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1454,8 +1446,6 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
int headroom;
bool may_encrypt;
- rcu_read_lock();
-
may_encrypt = !(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT);
headroom = local->tx_headroom;
@@ -1466,7 +1456,6 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
if (ieee80211_skb_resize(sdata, skb, headroom, may_encrypt)) {
ieee80211_free_txskb(&local->hw, skb);
- rcu_read_unlock();
return;
}
@@ -1478,13 +1467,11 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
!is_multicast_ether_addr(hdr->addr1) &&
mesh_nexthop_resolve(skb, sdata)) {
/* skb queued: don't free */
- rcu_read_unlock();
return;
}
ieee80211_set_qos_hdr(sdata, skb);
- ieee80211_tx(sdata, skb, false);
- rcu_read_unlock();
+ ieee80211_tx(sdata, skb, false, band);
}
static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
@@ -1574,7 +1561,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_channel *chan = local->hw.conf.channel;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *chan;
struct ieee80211_radiotap_header *prthdr =
(struct ieee80211_radiotap_header *)skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1583,26 +1571,6 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
u16 len_rthdr;
int hdrlen;
- /*
- * Frame injection is not allowed if beaconing is not allowed
- * or if we need radar detection. Beaconing is usually not allowed when
- * the mode or operation (Adhoc, AP, Mesh) does not support DFS.
- * Passive scan is also used in world regulatory domains where
- * your country is not known and as such it should be treated as
- * NO TX unless the channel is explicitly allowed in which case
- * your current regulatory domain would not have the passive scan
- * flag.
- *
- * Since AP mode uses monitor interfaces to inject/TX management
- * frames we can make AP mode the exception to this rule once it
- * supports radar detection as its implementation can deal with
- * radar detection by itself. We can do that later by adding a
- * monitor flag interfaces used for AP support.
- */
- if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR |
- IEEE80211_CHAN_PASSIVE_SCAN)))
- goto fail;
-
/* check for not even having the fixed radiotap header part */
if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header)))
goto fail; /* too short to be possibly valid */
@@ -1688,11 +1656,45 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
}
}
- ieee80211_xmit(sdata, skb);
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf) {
+ tmp_sdata = rcu_dereference(local->monitor_sdata);
+ if (tmp_sdata)
+ chanctx_conf =
+ rcu_dereference(tmp_sdata->vif.chanctx_conf);
+ }
+ if (!chanctx_conf)
+ goto fail_rcu;
+
+ chan = chanctx_conf->channel;
+
+ /*
+ * Frame injection is not allowed if beaconing is not allowed
+ * or if we need radar detection. Beaconing is usually not allowed when
+ * the mode or operation (Adhoc, AP, Mesh) does not support DFS.
+ * Passive scan is also used in world regulatory domains where
+ * your country is not known and as such it should be treated as
+ * NO TX unless the channel is explicitly allowed in which case
+ * your current regulatory domain would not have the passive scan
+ * flag.
+ *
+ * Since AP mode uses monitor interfaces to inject/TX management
+ * frames we can make AP mode the exception to this rule once it
+ * supports radar detection as its implementation can deal with
+ * radar detection by itself. We can do that later by adding a
+ * monitor flag interfaces used for AP support.
+ */
+ if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR |
+ IEEE80211_CHAN_PASSIVE_SCAN)))
+ goto fail_rcu;
+
+ ieee80211_xmit(sdata, skb, chan->band);
rcu_read_unlock();
return NETDEV_TX_OK;
+fail_rcu:
+ rcu_read_unlock();
fail:
dev_kfree_skb(skb);
return NETDEV_TX_OK; /* meaning, we dealt with the skb */
@@ -1734,6 +1736,9 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
bool multicast;
u32 info_flags = 0;
u16 info_id = 0;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_sub_if_data *ap_sdata;
+ enum ieee80211_band band;
if (unlikely(skb->len < ETH_HLEN))
goto fail;
@@ -1743,9 +1748,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
ethertype = (skb->data[12] << 8) | skb->data[13];
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
+ rcu_read_lock();
+
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
- rcu_read_lock();
sta = rcu_dereference(sdata->u.vlan.sta);
if (sta) {
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
@@ -1758,7 +1764,12 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
wme_sta = test_sta_flag(sta, WLAN_STA_WME);
}
- rcu_read_unlock();
+ ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
+ u.ap);
+ chanctx_conf = rcu_dereference(ap_sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->channel->band;
if (sta)
break;
/* fall through */
@@ -1769,6 +1780,11 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);
memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
hdrlen = 24;
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->channel->band;
break;
case NL80211_IFTYPE_WDS:
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
@@ -1778,15 +1794,20 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr3, skb->data, ETH_ALEN);
memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
hdrlen = 30;
+ /*
+ * This is the exception! WDS style interfaces are prohibited
+ * when channel contexts are in used so this must be valid
+ */
+ band = local->hw.conf.channel->band;
break;
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
if (!sdata->u.mesh.mshcfg.dot11MeshTTL) {
/* Do not send frames with mesh_ttl == 0 */
sdata->u.mesh.mshstats.dropped_frames_ttl++;
- goto fail;
+ goto fail_rcu;
}
- rcu_read_lock();
+
if (!is_multicast_ether_addr(skb->data)) {
mpath = mesh_path_lookup(skb->data, sdata);
if (!mpath)
@@ -1803,7 +1824,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
!(mppath && !ether_addr_equal(mppath->mpp, skb->data))) {
hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
skb->data, skb->data + ETH_ALEN);
- rcu_read_unlock();
meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
sdata, NULL, NULL);
} else {
@@ -1819,7 +1839,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
mesh_da = mppath->mpp;
else if (mpath)
mesh_da = mpath->dst;
- rcu_read_unlock();
hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
mesh_da, sdata->vif.addr);
@@ -1839,13 +1858,16 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
skb->data + ETH_ALEN);
}
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->channel->band;
break;
#endif
case NL80211_IFTYPE_STATION:
if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
bool tdls_peer = false;
- rcu_read_lock();
sta = sta_info_get(sdata, skb->data);
if (sta) {
authorized = test_sta_flag(sta,
@@ -1856,7 +1878,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
tdls_auth = test_sta_flag(sta,
WLAN_STA_TDLS_PEER_AUTH);
}
- rcu_read_unlock();
/*
* If the TDLS link is enabled, send everything
@@ -1871,7 +1892,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
if (tdls_direct) {
/* link during setup - throw out frames to peer */
if (!tdls_auth)
- goto fail;
+ goto fail_rcu;
/* DA SA BSSID */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
@@ -1896,6 +1917,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr3, skb->data, ETH_ALEN);
hdrlen = 24;
}
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->channel->band;
break;
case NL80211_IFTYPE_ADHOC:
/* DA SA BSSID */
@@ -1903,9 +1928,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN);
hdrlen = 24;
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->channel->band;
break;
default:
- goto fail;
+ goto fail_rcu;
}
/*
@@ -1915,13 +1944,11 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
*/
multicast = is_multicast_ether_addr(hdr.addr1);
if (!multicast) {
- rcu_read_lock();
sta = sta_info_get(sdata, hdr.addr1);
if (sta) {
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
wme_sta = test_sta_flag(sta, WLAN_STA_WME);
}
- rcu_read_unlock();
}
/* For mesh, the use of the QoS header is mandatory */
@@ -1949,7 +1976,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
I802_DEBUG_INC(local->tx_handlers_drop_unauth_port);
- goto fail;
+ goto fail_rcu;
}
if (unlikely(!multicast && skb->sk &&
@@ -2004,7 +2031,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
kfree_skb(tmp_skb);
if (!skb)
- goto fail;
+ goto fail_rcu;
}
hdr.frame_control = fc;
@@ -2052,7 +2079,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
head_need = max_t(int, 0, head_need);
if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
ieee80211_free_txskb(&local->hw, skb);
- return NETDEV_TX_OK;
+ goto fail_rcu;
}
}
@@ -2104,10 +2131,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
info->flags = info_flags;
info->ack_frame_id = info_id;
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, skb, band);
+ rcu_read_unlock();
return NETDEV_TX_OK;
+ fail_rcu:
+ rcu_read_unlock();
fail:
dev_kfree_skb(skb);
return NETDEV_TX_OK;
@@ -2139,11 +2169,18 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
struct sta_info *sta;
struct ieee80211_hdr *hdr;
bool result;
+ struct ieee80211_chanctx_conf *chanctx_conf;
sdata = vif_to_sdata(info->control.vif);
if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) {
- result = ieee80211_tx(sdata, skb, true);
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (unlikely(!chanctx_conf)) {
+ dev_kfree_skb(skb);
+ return true;
+ }
+ result = ieee80211_tx(sdata, skb, true,
+ chanctx_conf->channel->band);
} else {
struct sk_buff_head skbs;
@@ -2285,14 +2322,16 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
struct ieee80211_sub_if_data *sdata = NULL;
struct ieee80211_if_ap *ap = NULL;
struct beacon_data *beacon;
- enum ieee80211_band band = local->oper_channel->band;
+ enum ieee80211_band band;
struct ieee80211_tx_rate_control txrc;
+ struct ieee80211_chanctx_conf *chanctx_conf;
rcu_read_lock();
sdata = vif_to_sdata(vif);
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!ieee80211_sdata_running(sdata))
+ if (!ieee80211_sdata_running(sdata) || !chanctx_conf)
goto out;
if (tim_offset)
@@ -2409,6 +2448,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
*pos++ = WLAN_EID_SSID;
*pos++ = 0x0;
+ band = chanctx_conf->channel->band;
+
if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
mesh_add_ds_params_ie(skb, sdata) ||
ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
@@ -2426,6 +2467,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
goto out;
}
+ band = chanctx_conf->channel->band;
+
info = IEEE80211_SKB_CB(skb);
info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
@@ -2656,14 +2699,17 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
struct ieee80211_if_ap *bss = NULL;
struct beacon_data *beacon;
struct ieee80211_tx_info *info;
+ struct ieee80211_chanctx_conf *chanctx_conf;
sdata = vif_to_sdata(vif);
bss = &sdata->u.ap;
rcu_read_lock();
beacon = rcu_dereference(bss->beacon);
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (sdata->vif.type != NL80211_IFTYPE_AP || !beacon || !beacon->head)
+ if (sdata->vif.type != NL80211_IFTYPE_AP || !beacon || !beacon->head ||
+ !chanctx_conf)
goto out;
if (bss->dtim_count != 0 || !bss->dtim_bc_mc)
@@ -2693,7 +2739,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
info = IEEE80211_SKB_CB(skb);
tx.flags |= IEEE80211_TX_PS_BUFFERED;
- info->band = local->oper_channel->band;
+ info->band = chanctx_conf->channel->band;
if (invoke_tx_handlers(&tx))
skb = NULL;
@@ -2704,8 +2750,9 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_get_buffered_bc);
-void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, int tid)
+void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, int tid,
+ enum ieee80211_band band)
{
int ac = ieee802_1d_to_ac[tid & 7];
@@ -2722,6 +2769,6 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
* requirements are that we do not come into tx with bhs on.
*/
local_bh_disable();
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, skb, band);
local_bh_enable();
}