[PATCH 3/5] d80211: handle full queue when sending fragments
When the queue gets filled up while sending fragments, do not discard the frame. Partially sent frames are stored in a buffer in ieee80211_local (there is place for one frame for each queue there). When the space in hw queue gets available again, stored frame for that queue is sent first. Also, the case when driver returns NETDEV_TX_BUSY is handled properly now. Signed-off-by: Jiri Benc [EMAIL PROTECTED] --- net/d80211/ieee80211.c | 227 -- net/d80211/ieee80211_i.h | 14 +++ net/d80211/wme.c |5 + 3 files changed, 198 insertions(+), 48 deletions(-) 975a964398a0beb665747691350282b0a0b809c1 diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c index 65f32a8..0f01311 100644 --- a/net/d80211/ieee80211.c +++ b/net/d80211/ieee80211.c @@ -1153,6 +1153,74 @@ static void inline ieee80211_tx_prepare( __ieee80211_tx_prepare(tx, skb, dev, control); } +static inline int __ieee80211_queue_stopped(struct ieee80211_local *local, + int queue) +{ + return test_bit(IEEE80211_LINK_STATE_XOFF, local-state[queue]); +} + +static inline int __ieee80211_queue_pending(struct ieee80211_local *local, + int queue) +{ + return test_bit(IEEE80211_LINK_STATE_PENDING, local-state[queue]); +} + +#define IEEE80211_TX_OK0 +#define IEEE80211_TX_AGAIN 1 +#define IEEE80211_TX_FRAG_AGAIN2 + +static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb, + struct ieee80211_txrx_data *tx) +{ + struct ieee80211_tx_control *control = tx-u.tx.control; + int ret, i; + + if (skb) { + ieee80211_dump_frame(local-mdev-name, TX to low-level driver, skb); + ret = local-hw-tx(local-mdev, skb, control); + if (ret) + return IEEE80211_TX_AGAIN; +#ifdef IEEE80211_LEDS + if (local-tx_led_counter++ == 0) { + ieee80211_tx_led(1, local-mdev); + } +#endif /* IEEE80211_LEDS */ + } + if (tx-u.tx.extra_frag) { + control-use_rts_cts = 0; + control-use_cts_protect = 0; + control-clear_dst_mask = 0; + for (i = 0; i tx-u.tx.num_extra_frag; i++) { + if (!tx-u.tx.extra_frag[i]) + continue; + if (__ieee80211_queue_stopped(local, control-queue)) + return IEEE80211_TX_FRAG_AGAIN; + if (i == tx-u.tx.num_extra_frag) { + control-tx_rate = tx-u.tx.last_frag_hwrate; + control-rateidx = tx-u.tx.last_frag_rateidx; + control-rate_ctrl_probe = + tx-u.tx.probe_last_frag; + } + + ieee80211_dump_frame(local-mdev-name, +TX to low-level driver, skb); + ret = local-hw-tx(local-mdev, tx-u.tx.extra_frag[i], + control); + if (ret) + return IEEE80211_TX_FRAG_AGAIN; +#ifdef IEEE80211_LEDS + if (local-tx_led_counter++ == 0) { + ieee80211_tx_led(1, local-mdev); + } +#endif /* IEEE80211_LEDS */ + tx-u.tx.extra_frag[i] = NULL; + } + kfree(tx-u.tx.extra_frag); + tx-u.tx.extra_frag = NULL; + } + return IEEE80211_TX_OK; +} + static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb, struct ieee80211_tx_control *control, int mgmt) { @@ -1163,6 +1231,8 @@ static int ieee80211_tx(struct net_devic ieee80211_txrx_result res = TXRX_DROP; int ret, i; + WARN_ON(__ieee80211_queue_pending(local, control-queue)); + if (unlikely(skb-len 10)) { dev_kfree_skb(skb); return 0; @@ -1193,65 +1263,60 @@ static int ieee80211_tx(struct net_devic return 0; } - ieee80211_dump_frame(local-mdev-name, TX to low-level driver, skb); - ret = local-hw-tx(local-mdev, skb, control); -#ifdef IEEE80211_LEDS - if (!ret local-tx_led_counter++ == 0) { -ieee80211_tx_led(1, dev); -} -#endif /* IEEE80211_LEDS */ if (tx.u.tx.extra_frag) { - if (ret 0) { - /* Must free all fragments and return 0 since skb data -* has been fragmented into multiple buffers. -* TODO: could free extra fragments and restore skb to -* the original form since the data is still there and -* then return nonzero so that Linux
[PATCH 3/5] d80211: handle full queue when sending fragments
When the queue gets filled up while sending fragments, do not discard the frame. Partially sent frames are stored in a buffer in ieee80211_local (there is place for one frame for each queue there). When the space in hw queue gets available again, stored frame for that queue is sent first. Also, the case when driver returns NETDEV_TX_BUSY is handled properly now. Signed-off-by: Jiri Benc [EMAIL PROTECTED] --- net/d80211/ieee80211.c | 229 +-- net/d80211/ieee80211_i.h | 14 ++ net/d80211/wme.c |5 - 3 files changed, 199 insertions(+), 49 deletions(-) --- dscape.orig/net/d80211/ieee80211.c +++ dscape/net/d80211/ieee80211.c @@ -1153,6 +1153,74 @@ static void inline ieee80211_tx_prepare( __ieee80211_tx_prepare(tx, skb, dev, control); } +static inline int __ieee80211_queue_stopped(struct ieee80211_local *local, + int queue) +{ + return test_bit(IEEE80211_LINK_STATE_XOFF, local-state[queue]); +} + +static inline int __ieee80211_queue_pending(struct ieee80211_local *local, + int queue) +{ + return test_bit(IEEE80211_LINK_STATE_PENDING, local-state[queue]); +} + +#define IEEE80211_TX_OK0 +#define IEEE80211_TX_AGAIN 1 +#define IEEE80211_TX_FRAG_AGAIN2 + +static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb, + struct ieee80211_txrx_data *tx) +{ + struct ieee80211_tx_control *control = tx-u.tx.control; + int ret, i; + + if (skb) { + ieee80211_dump_frame(local-mdev-name, TX to low-level driver, skb); + ret = local-hw-tx(local-mdev, skb, control); + if (ret) + return IEEE80211_TX_AGAIN; +#ifdef IEEE80211_LEDS + if (local-tx_led_counter++ == 0) { + ieee80211_tx_led(1, local-mdev); + } +#endif /* IEEE80211_LEDS */ + } + if (tx-u.tx.extra_frag) { + control-use_rts_cts = 0; + control-use_cts_protect = 0; + control-clear_dst_mask = 0; + for (i = 0; i tx-u.tx.num_extra_frag; i++) { + if (!tx-u.tx.extra_frag[i]) + continue; + if (__ieee80211_queue_stopped(local, control-queue)) + return IEEE80211_TX_FRAG_AGAIN; + if (i == tx-u.tx.num_extra_frag) { + control-tx_rate = tx-u.tx.last_frag_hwrate; + control-rateidx = tx-u.tx.last_frag_rateidx; + control-rate_ctrl_probe = + tx-u.tx.probe_last_frag; + } + + ieee80211_dump_frame(local-mdev-name, +TX to low-level driver, skb); + ret = local-hw-tx(local-mdev, tx-u.tx.extra_frag[i], + control); + if (ret) + return IEEE80211_TX_FRAG_AGAIN; +#ifdef IEEE80211_LEDS + if (local-tx_led_counter++ == 0) { + ieee80211_tx_led(1, local-mdev); + } +#endif /* IEEE80211_LEDS */ + tx-u.tx.extra_frag[i] = NULL; + } + kfree(tx-u.tx.extra_frag); + tx-u.tx.extra_frag = NULL; + } + return IEEE80211_TX_OK; +} + static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb, struct ieee80211_tx_control *control, int mgmt) { @@ -1163,6 +1231,8 @@ static int ieee80211_tx(struct net_devic ieee80211_txrx_result res = TXRX_DROP; int ret, i; + WARN_ON(__ieee80211_queue_pending(local, control-queue)); + if (unlikely(skb-len 10)) { dev_kfree_skb(skb); return 0; @@ -1193,65 +1263,60 @@ static int ieee80211_tx(struct net_devic return 0; } - ieee80211_dump_frame(local-mdev-name, TX to low-level driver, skb); - ret = local-hw-tx(local-mdev, skb, control); -#ifdef IEEE80211_LEDS - if (!ret local-tx_led_counter++ == 0) { -ieee80211_tx_led(1, dev); -} -#endif /* IEEE80211_LEDS */ if (tx.u.tx.extra_frag) { - if (ret 0) { - /* Must free all fragments and return 0 since skb data -* has been fragmented into multiple buffers. -* TODO: could free extra fragments and restore skb to -* the original form since the data is still there and -* then return nonzero so that Linux netif would -* retry. */ - goto drop; - } - -