diff options
Diffstat (limited to 'net/mac80211/tx.c')
-rw-r--r-- | net/mac80211/tx.c | 332 |
1 files changed, 228 insertions, 104 deletions
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 1f8b120146d1..8d31933abe6a 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -36,7 +36,8 @@ /* misc utils */ -static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, +static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, + struct sk_buff *skb, int group_addr, int next_frag_len) { int rate, mrate, erp, dur, i; @@ -44,7 +45,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, struct ieee80211_local *local = tx->local; struct ieee80211_supported_band *sband; struct ieee80211_hdr *hdr; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); /* assume HW handles this */ if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) @@ -76,7 +77,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, * at the highest possible rate belonging to the PHY rates in the * BSSBasicRateSet */ - hdr = (struct ieee80211_hdr *)tx->skb->data; + hdr = (struct ieee80211_hdr *)skb->data; if (ieee80211_is_ctl(hdr->frame_control)) { /* TODO: These control frames are not currently sent by * mac80211, but should they be implemented, this function @@ -572,8 +573,6 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) switch (tx->key->conf.cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: - if (ieee80211_is_auth(hdr->frame_control)) - break; case WLAN_CIPHER_SUITE_TKIP: if (!ieee80211_is_data_present(hdr->frame_control)) tx->key = NULL; @@ -844,11 +843,13 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) return TX_CONTINUE; } -static int ieee80211_fragment(struct ieee80211_local *local, +static int ieee80211_fragment(struct ieee80211_tx_data *tx, struct sk_buff *skb, int hdrlen, int frag_threshold) { - struct sk_buff *tail = skb, *tmp; + struct ieee80211_local *local = tx->local; + struct ieee80211_tx_info *info; + struct sk_buff *tmp; int per_fragm = frag_threshold - hdrlen - FCS_LEN; int pos = hdrlen + per_fragm; int rem = skb->len - hdrlen - per_fragm; @@ -856,6 +857,8 @@ static int ieee80211_fragment(struct ieee80211_local *local, if (WARN_ON(rem < 0)) return -EINVAL; + /* first fragment was already added to queue by caller */ + while (rem) { int fraglen = per_fragm; @@ -868,12 +871,21 @@ static int ieee80211_fragment(struct ieee80211_local *local, IEEE80211_ENCRYPT_TAILROOM); if (!tmp) return -ENOMEM; - tail->next = tmp; - tail = tmp; + + __skb_queue_tail(&tx->skbs, tmp); + skb_reserve(tmp, local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM); /* copy control information */ memcpy(tmp->cb, skb->cb, sizeof(tmp->cb)); + + info = IEEE80211_SKB_CB(tmp); + info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | + IEEE80211_TX_CTL_FIRST_FRAGMENT); + + if (rem) + info->flags |= IEEE80211_TX_CTL_MORE_FRAMES; + skb_copy_queue_mapping(tmp, skb); tmp->priority = skb->priority; tmp->dev = skb->dev; @@ -885,6 +897,7 @@ static int ieee80211_fragment(struct ieee80211_local *local, pos += fraglen; } + /* adjust first fragment's length */ skb->len = hdrlen + per_fragm; return 0; } @@ -899,6 +912,10 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) int hdrlen; int fragnum; + /* no matter what happens, tx->skb moves to tx->skbs */ + __skb_queue_tail(&tx->skbs, skb); + tx->skb = NULL; + if (info->flags & IEEE80211_TX_CTL_DONTFRAG) return TX_CONTINUE; @@ -927,21 +944,21 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) * of the fragments then we will simply pretend to accept the skb * but store it away as pending. */ - if (ieee80211_fragment(tx->local, skb, hdrlen, frag_threshold)) + if (ieee80211_fragment(tx, skb, hdrlen, frag_threshold)) return TX_DROP; /* update duration/seq/flags of fragments */ fragnum = 0; - do { + + skb_queue_walk(&tx->skbs, skb) { int next_len; const __le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS); hdr = (void *)skb->data; info = IEEE80211_SKB_CB(skb); - if (skb->next) { + if (!skb_queue_is_last(&tx->skbs, skb)) { hdr->frame_control |= morefrags; - next_len = skb->next->len; /* * No multi-rate retries for fragmented frames, that * would completely throw off the NAV at other STAs. @@ -956,10 +973,9 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) hdr->frame_control &= ~morefrags; next_len = 0; } - hdr->duration_id = ieee80211_duration(tx, 0, next_len); hdr->seq_ctrl |= cpu_to_le16(fragnum & IEEE80211_SCTL_FRAG); fragnum++; - } while ((skb = skb->next)); + } return TX_CONTINUE; } @@ -967,16 +983,16 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) static ieee80211_tx_result debug_noinline ieee80211_tx_h_stats(struct ieee80211_tx_data *tx) { - struct sk_buff *skb = tx->skb; + struct sk_buff *skb; if (!tx->sta) return TX_CONTINUE; tx->sta->tx_packets++; - do { + skb_queue_walk(&tx->skbs, skb) { tx->sta->tx_fragments++; tx->sta->tx_bytes += skb->len; - } while ((skb = skb->next)); + } return TX_CONTINUE; } @@ -1015,21 +1031,25 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) static ieee80211_tx_result debug_noinline ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx) { - struct sk_buff *skb = tx->skb; + struct sk_buff *skb; struct ieee80211_hdr *hdr; int next_len; bool group_addr; - do { + skb_queue_walk(&tx->skbs, skb) { hdr = (void *) skb->data; if (unlikely(ieee80211_is_pspoll(hdr->frame_control))) break; /* must not overwrite AID */ - next_len = skb->next ? skb->next->len : 0; + if (!skb_queue_is_last(&tx->skbs, skb)) { + struct sk_buff *next = skb_queue_next(&tx->skbs, skb); + next_len = next->len; + } else + next_len = 0; group_addr = is_multicast_ether_addr(hdr->addr1); hdr->duration_id = - ieee80211_duration(tx, group_addr, next_len); - } while ((skb = skb->next)); + ieee80211_duration(tx, skb, group_addr, next_len); + } return TX_CONTINUE; } @@ -1108,6 +1128,7 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, tx->local = local; tx->sdata = sdata; tx->channel = local->hw.conf.channel; + __skb_queue_head_init(&tx->skbs); /* * If this flag is set to true anywhere, and we get here, @@ -1180,22 +1201,18 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, return TX_CONTINUE; } -/* - * Returns false if the frame couldn't be transmitted but was queued instead. - */ -static bool __ieee80211_tx(struct ieee80211_local *local, struct sk_buff **skbp, - struct sta_info *sta, bool txpending) +static bool ieee80211_tx_frags(struct ieee80211_local *local, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct sk_buff_head *skbs, + bool txpending) { - struct sk_buff *skb = *skbp, *next; + struct sk_buff *skb, *tmp; struct ieee80211_tx_info *info; - struct ieee80211_sub_if_data *sdata; unsigned long flags; - int len; - bool fragm = false; - while (skb) { + skb_queue_walk_safe(skbs, skb, tmp) { int q = skb_get_queue_mapping(skb); - __le16 fc; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); if (local->queue_stop_reasons[q] || @@ -1205,24 +1222,10 @@ static bool __ieee80211_tx(struct ieee80211_local *local, struct sk_buff **skbp, * transmission from the tx-pending tasklet when the * queue is woken again. */ - - do { - next = skb->next; - skb->next = NULL; - /* - * NB: If txpending is true, next must already - * be NULL since we must've gone through this - * loop before already; therefore we can just - * queue the frame to the head without worrying - * about reordering of fragments. - */ - if (unlikely(txpending)) - __skb_queue_head(&local->pending[q], - skb); - else - __skb_queue_tail(&local->pending[q], - skb); - } while ((skb = next)); + if (txpending) + skb_queue_splice(skbs, &local->pending[q]); + else + skb_queue_splice_tail(skbs, &local->pending[q]); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); @@ -1231,47 +1234,72 @@ static bool __ieee80211_tx(struct ieee80211_local *local, struct sk_buff **skbp, spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); info = IEEE80211_SKB_CB(skb); + info->control.vif = vif; + info->control.sta = sta; - if (fragm) - info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT | - IEEE80211_TX_CTL_FIRST_FRAGMENT); - - next = skb->next; - len = skb->len; + __skb_unlink(skb, skbs); + drv_tx(local, skb); + } - if (next) - info->flags |= IEEE80211_TX_CTL_MORE_FRAMES; + return true; +} - sdata = vif_to_sdata(info->control.vif); +/* + * Returns false if the frame couldn't be transmitted but was queued instead. + */ +static bool __ieee80211_tx(struct ieee80211_local *local, + struct sk_buff_head *skbs, int led_len, + struct sta_info *sta, bool txpending) +{ + struct ieee80211_tx_info *info; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_vif *vif; + struct ieee80211_sta *pubsta; + struct sk_buff *skb; + bool result = true; + __le16 fc; - switch (sdata->vif.type) { - case NL80211_IFTYPE_MONITOR: - info->control.vif = NULL; - break; - case NL80211_IFTYPE_AP_VLAN: - info->control.vif = &container_of(sdata->bss, - struct ieee80211_sub_if_data, u.ap)->vif; - break; - default: - /* keep */ - break; - } + if (WARN_ON(skb_queue_empty(skbs))) + return true; - if (sta && sta->uploaded) - info->control.sta = &sta->sta; - else - info->control.sta = NULL; + skb = skb_peek(skbs); + fc = ((struct ieee80211_hdr *)skb->data)->frame_control; + info = IEEE80211_SKB_CB(skb); + sdata = vif_to_sdata(info->control.vif); + if (sta && !sta->uploaded) + sta = NULL; - fc = ((struct ieee80211_hdr *)skb->data)->frame_control; - drv_tx(local, skb); + if (sta) + pubsta = &sta->sta; + else + pubsta = NULL; - ieee80211_tpt_led_trig_tx(local, fc, len); - *skbp = skb = next; - ieee80211_led_tx(local, 1); - fragm = true; + switch (sdata->vif.type) { + case NL80211_IFTYPE_MONITOR: + sdata = NULL; + vif = NULL; + break; + case NL80211_IFTYPE_AP_VLAN: + sdata = container_of(sdata->bss, + struct ieee80211_sub_if_data, u.ap); + /* fall through */ + default: + vif = &sdata->vif; + break; } - return true; + if (local->ops->tx_frags) + drv_tx_frags(local, vif, pubsta, skbs); + else + result = ieee80211_tx_frags(local, vif, pubsta, skbs, + txpending); + + ieee80211_tpt_led_trig_tx(local, fc, led_len); + ieee80211_led_tx(local, 1); + + WARN_ON(!skb_queue_empty(skbs)); + + return result; } /* @@ -1280,8 +1308,7 @@ static bool __ieee80211_tx(struct ieee80211_local *local, struct sk_buff **skbp, */ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) { - struct sk_buff *skb = tx->skb; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); ieee80211_tx_result res = TX_DROP; #define CALL_TXH(txh) \ @@ -1315,13 +1342,10 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) txh_done: if (unlikely(res == TX_DROP)) { I802_DEBUG_INC(tx->local->tx_handlers_drop); - while (skb) { - struct sk_buff *next; - - next = skb->next; - dev_kfree_skb(skb); - skb = next; - } + if (tx->skb) + dev_kfree_skb(tx->skb); + else + __skb_queue_purge(&tx->skbs); return -1; } else if (unlikely(res == TX_QUEUED)) { I802_DEBUG_INC(tx->local->tx_handlers_queued); @@ -1342,6 +1366,7 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, ieee80211_tx_result res_prepare; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); bool result = true; + int led_len; if (unlikely(skb->len < 10)) { dev_kfree_skb(skb); @@ -1351,6 +1376,7 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, rcu_read_lock(); /* initialises tx */ + led_len = skb->len; res_prepare = ieee80211_tx_prepare(sdata, &tx, skb); if (unlikely(res_prepare == TX_DROP)) { @@ -1364,7 +1390,8 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, info->band = tx.channel->band; if (!invoke_tx_handlers(&tx)) - result = __ieee80211_tx(local, &tx.skb, tx.sta, txpending); + result = __ieee80211_tx(local, &tx.skbs, led_len, + tx.sta, txpending); out: rcu_read_unlock(); return result; @@ -1685,8 +1712,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, int nh_pos, h_pos; struct sta_info *sta = NULL; bool wme_sta = false, authorized = false, tdls_auth = false; - struct sk_buff *tmp_skb; bool tdls_direct = false; + bool multicast; + u32 info_flags = 0; + u16 info_id = 0; if (unlikely(skb->len < ETH_HLEN)) { ret = NETDEV_TX_OK; @@ -1873,7 +1902,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, * if it is a multicast address (which can only happen * in AP mode) */ - if (!is_multicast_ether_addr(hdr.addr1)) { + multicast = is_multicast_ether_addr(hdr.addr1); + if (!multicast) { rcu_read_lock(); sta = sta_info_get(sdata, hdr.addr1); if (sta) { @@ -1914,11 +1944,54 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, goto fail; } + if (unlikely(!multicast && skb->sk && + skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) { + struct sk_buff *orig_skb = skb; + + skb = skb_clone(skb, GFP_ATOMIC); + if (skb) { + unsigned long flags; + int id, r; + + spin_lock_irqsave(&local->ack_status_lock, flags); + r = idr_get_new_above(&local->ack_status_frames, + orig_skb, 1, &id); + if (r == -EAGAIN) { + idr_pre_get(&local->ack_status_frames, + GFP_ATOMIC); + r = idr_get_new_above(&local->ack_status_frames, + orig_skb, 1, &id); + } + if (WARN_ON(!id) || id > 0xffff) { + idr_remove(&local->ack_status_frames, id); + r = -ERANGE; + } + spin_unlock_irqrestore(&local->ack_status_lock, flags); + + if (!r) { + info_id = id; + info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + } else if (skb_shared(skb)) { + kfree_skb(orig_skb); + } else { + kfree_skb(skb); + skb = orig_skb; + } + } else { + /* couldn't clone -- lose tx status ... */ + skb = orig_skb; + } + } + /* * If the skb is shared we need to obtain our own copy. */ if (skb_shared(skb)) { - tmp_skb = skb; + struct sk_buff *tmp_skb = skb; + + /* can't happen -- skb is a clone if info_id != 0 */ + WARN_ON(info_id); + skb = skb_clone(skb, GFP_ATOMIC); kfree_skb(tmp_skb); @@ -2019,6 +2092,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, memset(info, 0, sizeof(*info)); dev->trans_start = jiffies; + + info->flags = info_flags; + info->ack_frame_id = info_id; + ieee80211_xmit(sdata, skb); return NETDEV_TX_OK; @@ -2062,10 +2139,15 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) { result = ieee80211_tx(sdata, skb, true); } else { + struct sk_buff_head skbs; + + __skb_queue_head_init(&skbs); + __skb_queue_tail(&skbs, skb); + hdr = (struct ieee80211_hdr *)skb->data; sta = sta_info_get(sdata, hdr->addr1); - result = __ieee80211_tx(local, &skb, sta, true); + result = __ieee80211_tx(local, &skbs, skb->len, sta, true); } return result; @@ -2279,22 +2361,31 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, } else if (ieee80211_vif_is_mesh(&sdata->vif)) { struct ieee80211_mgmt *mgmt; u8 *pos; + int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) + + sizeof(mgmt->u.beacon); #ifdef CONFIG_MAC80211_MESH if (!sdata->u.mesh.mesh_id_len) goto out; #endif - /* headroom, head length, tail length and maximum TIM length */ - skb = dev_alloc_skb(local->tx_headroom + 400 + - sdata->u.mesh.ie_len); + 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_info) + + 2 + sdata->u.mesh.mesh_id_len + + 2 + sizeof(struct ieee80211_meshconf_ie) + + sdata->u.mesh.ie_len); if (!skb) goto out; skb_reserve(skb, local->hw.extra_tx_headroom); - mgmt = (struct ieee80211_mgmt *) - skb_put(skb, 24 + sizeof(mgmt->u.beacon)); - memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); + 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); memset(mgmt->da, 0xff, ETH_ALEN); @@ -2313,6 +2404,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, mesh_add_ds_params_ie(skb, sdata) || ieee80211_add_ext_srates_ie(&sdata->vif, skb) || mesh_add_rsn_ie(skb, sdata) || + mesh_add_ht_cap_ie(skb, sdata) || + mesh_add_ht_info_ie(skb, sdata) || mesh_add_meshid_ie(skb, sdata) || mesh_add_meshconf_ie(skb, sdata) || mesh_add_vendor_ies(skb, sdata)) { @@ -2355,6 +2448,37 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_beacon_get_tim); +struct sk_buff *ieee80211_proberesp_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ieee80211_if_ap *ap = NULL; + struct sk_buff *presp = NULL, *skb = NULL; + struct ieee80211_hdr *hdr; + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + if (sdata->vif.type != NL80211_IFTYPE_AP) + return NULL; + + rcu_read_lock(); + + ap = &sdata->u.ap; + presp = rcu_dereference(ap->probe_resp); + if (!presp) + goto out; + + skb = skb_copy(presp, GFP_ATOMIC); + if (!skb) + goto out; + + hdr = (struct ieee80211_hdr *) skb->data; + memset(hdr->addr1, 0, sizeof(hdr->addr1)); + +out: + rcu_read_unlock(); + return skb; +} +EXPORT_SYMBOL(ieee80211_proberesp_get); + struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { |