summaryrefslogtreecommitdiffstats
path: root/net/mac80211/tx.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/tx.c')
-rw-r--r--net/mac80211/tx.c109
1 files changed, 47 insertions, 62 deletions
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index a2cb6a302cc7..fe644f91ae05 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -329,6 +329,8 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
if (sdata->vif.type == NL80211_IFTYPE_AP)
ps = &sdata->u.ap.ps;
+ else if (ieee80211_vif_is_mesh(&sdata->vif))
+ ps = &sdata->u.mesh.ps;
else
continue;
@@ -372,18 +374,20 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
/*
* broadcast/multicast frame
*
- * If any of the associated stations is in power save mode,
+ * If any of the associated/peer stations is in power save mode,
* the frame is buffered to be sent after DTIM beacon frame.
* This is done either by the hardware or us.
*/
- /* powersaving STAs currently only in AP/VLAN mode */
+ /* powersaving STAs currently only in AP/VLAN/mesh mode */
if (tx->sdata->vif.type == NL80211_IFTYPE_AP ||
tx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
if (!tx->sdata->bss)
return TX_CONTINUE;
ps = &tx->sdata->bss->ps;
+ } else if (ieee80211_vif_is_mesh(&tx->sdata->vif)) {
+ ps = &tx->sdata->u.mesh.ps;
} else {
return TX_CONTINUE;
}
@@ -594,7 +598,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
break;
}
- if (unlikely(tx->key && tx->key->flags & KEY_FLAG_TAINTED))
+ if (unlikely(tx->key && tx->key->flags & KEY_FLAG_TAINTED &&
+ !ieee80211_is_deauth(hdr->frame_control)))
return TX_DROP;
if (!skip_hw && tx->key &&
@@ -1225,6 +1230,21 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local,
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
if (local->queue_stop_reasons[q] ||
(!txpending && !skb_queue_empty(&local->pending[q]))) {
+ if (unlikely(info->flags &
+ IEEE80211_TX_INTFL_OFFCHAN_TX_OK &&
+ local->queue_stop_reasons[q] &
+ ~BIT(IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL))) {
+ /*
+ * Drop off-channel frames if queues are stopped
+ * for any reason other than off-channel
+ * operation. Never queue them.
+ */
+ spin_unlock_irqrestore(
+ &local->queue_stop_reason_lock, flags);
+ ieee80211_purge_tx_queue(&local->hw, skbs);
+ return true;
+ }
+
/*
* Since queue is stopped, queue up frames for later
* transmission from the tx-pending tasklet when the
@@ -1472,12 +1492,14 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
hdr = (struct ieee80211_hdr *) skb->data;
info->control.vif = &sdata->vif;
- if (ieee80211_vif_is_mesh(&sdata->vif) &&
- ieee80211_is_data(hdr->frame_control) &&
- !is_multicast_ether_addr(hdr->addr1) &&
- mesh_nexthop_resolve(skb, sdata)) {
- /* skb queued: don't free */
- return;
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ if (ieee80211_is_data(hdr->frame_control) &&
+ is_unicast_ether_addr(hdr->addr1)) {
+ if (mesh_nexthop_resolve(skb, sdata))
+ return; /* skb queued: don't free */
+ } else {
+ ieee80211_mps_set_frame_flags(sdata, NULL, hdr);
+ }
}
ieee80211_set_qos_hdr(sdata, skb);
@@ -2342,11 +2364,9 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
if (local->tim_in_locked_section) {
__ieee80211_beacon_add_tim(sdata, ps, skb);
} else {
- unsigned long flags;
-
- spin_lock_irqsave(&local->tim_lock, flags);
+ spin_lock(&local->tim_lock);
__ieee80211_beacon_add_tim(sdata, ps, skb);
- spin_unlock_irqrestore(&local->tim_lock, flags);
+ spin_unlock(&local->tim_lock);
}
return 0;
@@ -2424,66 +2444,26 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_BEACON);
} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
- struct ieee80211_mgmt *mgmt;
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- u8 *pos;
- int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) +
- sizeof(mgmt->u.beacon);
+ struct beacon_data *bcn = rcu_dereference(ifmsh->beacon);
-#ifdef CONFIG_MAC80211_MESH
- if (!sdata->u.mesh.mesh_id_len)
+ if (!bcn)
goto out;
-#endif
if (ifmsh->sync_ops)
ifmsh->sync_ops->adjust_tbtt(
sdata);
skb = dev_alloc_skb(local->tx_headroom +
- hdr_len +
- 2 + /* NULL SSID */
- 2 + 8 + /* supported rates */
- 2 + 3 + /* DS params */
- 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
- 2 + sizeof(struct ieee80211_ht_cap) +
- 2 + sizeof(struct ieee80211_ht_operation) +
- 2 + sdata->u.mesh.mesh_id_len +
- 2 + sizeof(struct ieee80211_meshconf_ie) +
- sdata->u.mesh.ie_len);
+ bcn->head_len +
+ 256 + /* TIM IE */
+ bcn->tail_len);
if (!skb)
goto out;
-
- skb_reserve(skb, local->hw.extra_tx_headroom);
- mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
- memset(mgmt, 0, hdr_len);
- mgmt->frame_control =
- cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON);
- eth_broadcast_addr(mgmt->da);
- memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
- memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
- mgmt->u.beacon.beacon_int =
- cpu_to_le16(sdata->vif.bss_conf.beacon_int);
- mgmt->u.beacon.capab_info |= cpu_to_le16(
- sdata->u.mesh.security ? WLAN_CAPABILITY_PRIVACY : 0);
-
- pos = skb_put(skb, 2);
- *pos++ = WLAN_EID_SSID;
- *pos++ = 0x0;
-
- band = chanctx_conf->def.chan->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) ||
- mesh_add_rsn_ie(skb, sdata) ||
- mesh_add_ht_cap_ie(skb, sdata) ||
- mesh_add_ht_oper_ie(skb, sdata) ||
- mesh_add_meshid_ie(skb, sdata) ||
- mesh_add_meshconf_ie(skb, sdata) ||
- mesh_add_vendor_ies(skb, sdata)) {
- pr_err("o11s: couldn't add ies!\n");
- goto out;
- }
+ skb_reserve(skb, local->tx_headroom);
+ memcpy(skb_put(skb, bcn->head_len), bcn->head, bcn->head_len);
+ ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb);
+ memcpy(skb_put(skb, bcn->tail_len), bcn->tail, bcn->tail_len);
} else {
WARN_ON(1);
goto out;
@@ -2733,6 +2713,8 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
goto out;
ps = &sdata->u.ap.ps;
+ } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
+ ps = &sdata->u.mesh.ps;
} else {
goto out;
}
@@ -2756,6 +2738,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
cpu_to_le16(IEEE80211_FCTL_MOREDATA);
}
+ sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
if (!ieee80211_tx_prepare(sdata, &tx, skb))
break;
dev_kfree_skb_any(skb);
@@ -2788,6 +2771,8 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
skb_set_queue_mapping(skb, ac);
skb->priority = tid;
+ skb->dev = sdata->dev;
+
/*
* The other path calling ieee80211_xmit is from the tasklet,
* and while we can handle concurrent transmissions locking