summaryrefslogtreecommitdiff
path: root/net/mac80211/tx.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-03-23 17:28:35 +0100
committerJohn W. Linville <linville@tuxdriver.com>2009-03-27 20:13:21 -0400
commit2de8e0d999b8790861cd3749bec2236ccc1c8110 (patch)
tree18b7f0127b7e1d938b41d1120803cb0af528058f /net/mac80211/tx.c
parent08df05aa9b25f3079585855506022bb33a011183 (diff)
mac80211: rewrite fragmentation
Fragmentation currently uses an allocated array to store the fragment skbs, and then keeps track of which have been sent and which are still pending etc. This is rather complicated; make it simpler by just chaining the fragments into skb->next and removing from that list when sent. Also simplifies all code that needs to touch fragments, since it now only needs to walk the skb->next list. This is a prerequisite for fixing the stored packet code, which I need to do for proper aggregation packet storing. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Reviewed-by: Luis R. Rodriguez <lrodriguez@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/tx.c')
-rw-r--r--net/mac80211/tx.c322
1 files changed, 151 insertions, 171 deletions
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index f3f240c69018..51bf49cc75bc 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -34,8 +34,7 @@
#define IEEE80211_TX_OK 0
#define IEEE80211_TX_AGAIN 1
-#define IEEE80211_TX_FRAG_AGAIN 2
-#define IEEE80211_TX_PENDING 3
+#define IEEE80211_TX_PENDING 2
/* misc utils */
@@ -702,17 +701,62 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
return TX_CONTINUE;
}
+static int ieee80211_fragment(struct ieee80211_local *local,
+ struct sk_buff *skb, int hdrlen,
+ int frag_threshold)
+{
+ struct sk_buff *tail = skb, *tmp;
+ int per_fragm = frag_threshold - hdrlen - FCS_LEN;
+ int pos = hdrlen + per_fragm;
+ int rem = skb->len - hdrlen - per_fragm;
+
+ if (WARN_ON(rem < 0))
+ return -EINVAL;
+
+ while (rem) {
+ int fraglen = per_fragm;
+
+ if (fraglen > rem)
+ fraglen = rem;
+ rem -= fraglen;
+ tmp = dev_alloc_skb(local->tx_headroom +
+ frag_threshold +
+ IEEE80211_ENCRYPT_HEADROOM +
+ IEEE80211_ENCRYPT_TAILROOM);
+ if (!tmp)
+ return -ENOMEM;
+ tail->next = tmp;
+ tail = tmp;
+ skb_reserve(tmp, local->tx_headroom +
+ IEEE80211_ENCRYPT_HEADROOM);
+ /* copy control information */
+ memcpy(tmp->cb, skb->cb, sizeof(tmp->cb));
+ skb_copy_queue_mapping(tmp, skb);
+ tmp->priority = skb->priority;
+ tmp->do_not_encrypt = skb->do_not_encrypt;
+ tmp->dev = skb->dev;
+ tmp->iif = skb->iif;
+
+ /* copy header and data */
+ memcpy(skb_put(tmp, hdrlen), skb->data, hdrlen);
+ memcpy(skb_put(tmp, fraglen), skb->data + pos, fraglen);
+
+ pos += fraglen;
+ }
+
+ skb->len = hdrlen + per_fragm;
+ return 0;
+}
+
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
{
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
- size_t hdrlen, per_fragm, num_fragm, payload_len, left;
- struct sk_buff **frags, *first, *frag;
- int i;
- u16 seq;
- u8 *pos;
+ struct sk_buff *skb = tx->skb;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
int frag_threshold = tx->local->fragmentation_threshold;
+ int hdrlen;
+ int fragnum;
if (!(tx->flags & IEEE80211_TX_FRAGMENTED))
return TX_CONTINUE;
@@ -725,58 +769,35 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
return TX_DROP;
- first = tx->skb;
-
hdrlen = ieee80211_hdrlen(hdr->frame_control);
- payload_len = first->len - hdrlen;
- per_fragm = frag_threshold - hdrlen - FCS_LEN;
- num_fragm = DIV_ROUND_UP(payload_len, per_fragm);
-
- frags = kzalloc(num_fragm * sizeof(struct sk_buff *), GFP_ATOMIC);
- if (!frags)
- goto fail;
-
- hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
- seq = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ;
- pos = first->data + hdrlen + per_fragm;
- left = payload_len - per_fragm;
- for (i = 0; i < num_fragm - 1; i++) {
- struct ieee80211_hdr *fhdr;
- size_t copylen;
-
- if (left <= 0)
- goto fail;
- /* reserve enough extra head and tail room for possible
- * encryption */
- frag = frags[i] =
- dev_alloc_skb(tx->local->tx_headroom +
- frag_threshold +
- IEEE80211_ENCRYPT_HEADROOM +
- IEEE80211_ENCRYPT_TAILROOM);
- if (!frag)
- goto fail;
-
- /* Make sure that all fragments use the same priority so
- * that they end up using the same TX queue */
- frag->priority = first->priority;
+ /* internal error, why is TX_FRAGMENTED set? */
+ if (WARN_ON(skb->len <= frag_threshold))
+ return TX_DROP;
- skb_reserve(frag, tx->local->tx_headroom +
- IEEE80211_ENCRYPT_HEADROOM);
+ /*
+ * Now fragment the frame. This will allocate all the fragments and
+ * chain them (using skb as the first fragment) to skb->next.
+ * During transmission, we will remove the successfully transmitted
+ * fragments from this list. When the low-level driver rejects one
+ * 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))
+ return TX_DROP;
- /* copy TX information */
- info = IEEE80211_SKB_CB(frag);
- memcpy(info, first->cb, sizeof(frag->cb));
+ /* update duration/seq/flags of fragments */
+ fragnum = 0;
+ do {
+ int next_len;
+ const __le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
- /* copy/fill in 802.11 header */
- fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen);
- memcpy(fhdr, first->data, hdrlen);
- fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG));
+ hdr = (void *)skb->data;
+ info = IEEE80211_SKB_CB(skb);
- if (i == num_fragm - 2) {
- /* clear MOREFRAGS bit for the last fragment */
- fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS);
- } else {
+ if (skb->next) {
+ 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.
@@ -787,37 +808,16 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
info->control.rates[4].idx = -1;
BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 5);
info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
+ } else {
+ hdr->frame_control &= ~morefrags;
+ next_len = 0;
}
-
- /* copy data */
- copylen = left > per_fragm ? per_fragm : left;
- memcpy(skb_put(frag, copylen), pos, copylen);
-
- skb_copy_queue_mapping(frag, first);
-
- frag->do_not_encrypt = first->do_not_encrypt;
- frag->dev = first->dev;
- frag->iif = first->iif;
-
- pos += copylen;
- left -= copylen;
- }
- skb_trim(first, hdrlen + per_fragm);
-
- tx->num_extra_frag = num_fragm - 1;
- tx->extra_frag = frags;
+ 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;
-
- fail:
- if (frags) {
- for (i = 0; i < num_fragm - 1; i++)
- if (frags[i])
- dev_kfree_skb(frags[i]);
- kfree(frags);
- }
- I802_DEBUG_INC(tx->local->tx_handlers_drop_fragment);
- return TX_DROP;
}
static ieee80211_tx_result debug_noinline
@@ -845,27 +845,19 @@ 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 ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
- int next_len, i;
- int group_addr = is_multicast_ether_addr(hdr->addr1);
-
- if (!(tx->flags & IEEE80211_TX_FRAGMENTED)) {
- hdr->duration_id = ieee80211_duration(tx, group_addr, 0);
- return TX_CONTINUE;
- }
-
- hdr->duration_id = ieee80211_duration(tx, group_addr,
- tx->extra_frag[0]->len);
+ struct sk_buff *skb = tx->skb;
+ struct ieee80211_hdr *hdr;
+ int next_len;
+ bool group_addr;
- for (i = 0; i < tx->num_extra_frag; i++) {
- if (i + 1 < tx->num_extra_frag)
- next_len = tx->extra_frag[i + 1]->len;
- else
- next_len = 0;
+ do {
+ hdr = (void *) skb->data;
+ next_len = skb->next ? skb->next->len : 0;
+ group_addr = is_multicast_ether_addr(hdr->addr1);
- hdr = (struct ieee80211_hdr *)tx->extra_frag[i]->data;
- hdr->duration_id = ieee80211_duration(tx, 0, next_len);
- }
+ hdr->duration_id =
+ ieee80211_duration(tx, group_addr, next_len);
+ } while ((skb = skb->next));
return TX_CONTINUE;
}
@@ -873,19 +865,16 @@ ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx)
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)
{
- int i;
+ struct sk_buff *skb = tx->skb;
if (!tx->sta)
return TX_CONTINUE;
tx->sta->tx_packets++;
- tx->sta->tx_fragments++;
- tx->sta->tx_bytes += tx->skb->len;
- if (tx->extra_frag) {
- tx->sta->tx_fragments += tx->num_extra_frag;
- for (i = 0; i < tx->num_extra_frag; i++)
- tx->sta->tx_bytes += tx->extra_frag[i]->len;
- }
+ do {
+ tx->sta->tx_fragments++;
+ tx->sta->tx_bytes += skb->len;
+ } while ((skb = skb->next));
return TX_CONTINUE;
}
@@ -1099,45 +1088,36 @@ static int ieee80211_tx_prepare(struct ieee80211_local *local,
return 0;
}
-static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
+static int __ieee80211_tx(struct ieee80211_local *local,
struct ieee80211_tx_data *tx)
{
+ struct sk_buff *skb = tx->skb, *next;
struct ieee80211_tx_info *info;
- int ret, i;
+ int ret;
+ bool fragm = false;
- if (skb) {
+ local->mdev->trans_start = jiffies;
+
+ while (skb) {
if (ieee80211_queue_stopped(&local->hw,
skb_get_queue_mapping(skb)))
return IEEE80211_TX_PENDING;
- ret = local->ops->tx(local_to_hw(local), skb);
- if (ret)
- return IEEE80211_TX_AGAIN;
- local->mdev->trans_start = jiffies;
- ieee80211_led_tx(local, 1);
- }
- if (tx->extra_frag) {
- for (i = 0; i < tx->num_extra_frag; i++) {
- if (!tx->extra_frag[i])
- continue;
- info = IEEE80211_SKB_CB(tx->extra_frag[i]);
+ if (fragm) {
+ info = IEEE80211_SKB_CB(skb);
info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT |
IEEE80211_TX_CTL_FIRST_FRAGMENT);
- if (ieee80211_queue_stopped(&local->hw,
- skb_get_queue_mapping(tx->extra_frag[i])))
- return IEEE80211_TX_FRAG_AGAIN;
-
- ret = local->ops->tx(local_to_hw(local),
- tx->extra_frag[i]);
- if (ret)
- return IEEE80211_TX_FRAG_AGAIN;
- local->mdev->trans_start = jiffies;
- ieee80211_led_tx(local, 1);
- tx->extra_frag[i] = NULL;
}
- kfree(tx->extra_frag);
- tx->extra_frag = NULL;
+
+ next = skb->next;
+ ret = local->ops->tx(local_to_hw(local), skb);
+ if (ret != NETDEV_TX_OK)
+ return IEEE80211_TX_AGAIN;
+ tx->skb = skb = next;
+ ieee80211_led_tx(local, 1);
+ fragm = true;
}
+
return IEEE80211_TX_OK;
}
@@ -1149,7 +1129,6 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
{
struct sk_buff *skb = tx->skb;
ieee80211_tx_result res = TX_DROP;
- int i;
#define CALL_TXH(txh) \
res = txh(tx); \
@@ -1173,11 +1152,13 @@ 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);
- dev_kfree_skb(skb);
- for (i = 0; i < tx->num_extra_frag; i++)
- if (tx->extra_frag[i])
- dev_kfree_skb(tx->extra_frag[i]);
- kfree(tx->extra_frag);
+ while (skb) {
+ struct sk_buff *next;
+
+ next = skb->next;
+ dev_kfree_skb(skb);
+ skb = next;
+ }
return -1;
} else if (unlikely(res == TX_QUEUED)) {
I802_DEBUG_INC(tx->local->tx_handlers_queued);
@@ -1194,7 +1175,7 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb)
struct ieee80211_tx_data tx;
ieee80211_tx_result res_prepare;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- int ret, i;
+ int ret;
u16 queue;
queue = skb_get_queue_mapping(skb);
@@ -1225,7 +1206,7 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb)
goto out;
retry:
- ret = __ieee80211_tx(local, skb, &tx);
+ ret = __ieee80211_tx(local, &tx);
if (ret) {
struct ieee80211_tx_stored_packet *store;
@@ -1240,9 +1221,6 @@ retry:
store = &local->pending_packet[queue];
- if (ret == IEEE80211_TX_FRAG_AGAIN)
- skb = NULL;
-
set_bit(queue, local->queues_pending);
smp_mb();
/*
@@ -1260,22 +1238,23 @@ retry:
clear_bit(queue, local->queues_pending);
goto retry;
}
- store->skb = skb;
- store->extra_frag = tx.extra_frag;
- store->num_extra_frag = tx.num_extra_frag;
+ store->skb = tx.skb;
}
out:
rcu_read_unlock();
return 0;
drop:
- if (skb)
- dev_kfree_skb(skb);
- for (i = 0; i < tx.num_extra_frag; i++)
- if (tx.extra_frag[i])
- dev_kfree_skb(tx.extra_frag[i]);
- kfree(tx.extra_frag);
rcu_read_unlock();
+
+ skb = tx.skb;
+ while (skb) {
+ struct sk_buff *next;
+
+ next = skb->next;
+ dev_kfree_skb(skb);
+ skb = next;
+ }
return 0;
}
@@ -1810,17 +1789,21 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
*/
void ieee80211_clear_tx_pending(struct ieee80211_local *local)
{
- int i, j;
- struct ieee80211_tx_stored_packet *store;
+ struct sk_buff *skb;
+ int i;
for (i = 0; i < local->hw.queues; i++) {
if (!test_bit(i, local->queues_pending))
continue;
- store = &local->pending_packet[i];
- kfree_skb(store->skb);
- for (j = 0; j < store->num_extra_frag; j++)
- kfree_skb(store->extra_frag[j]);
- kfree(store->extra_frag);
+
+ skb = local->pending_packet[i].skb;
+ while (skb) {
+ struct sk_buff *next;
+
+ next = skb->next;
+ dev_kfree_skb(skb);
+ skb = next;
+ }
clear_bit(i, local->queues_pending);
}
}
@@ -1854,14 +1837,11 @@ void ieee80211_tx_pending(unsigned long data)
netif_start_subqueue(local->mdev, i);
store = &local->pending_packet[i];
- tx.extra_frag = store->extra_frag;
- tx.num_extra_frag = store->num_extra_frag;
tx.flags = 0;
- ret = __ieee80211_tx(local, store->skb, &tx);
- if (ret) {
- if (ret == IEEE80211_TX_FRAG_AGAIN)
- store->skb = NULL;
- } else {
+ tx.skb = store->skb;
+ ret = __ieee80211_tx(local, &tx);
+ store->skb = tx.skb;
+ if (!ret) {
clear_bit(i, local->queues_pending);
ieee80211_wake_queue(&local->hw, i);
}