[PATCH 3/5] d80211: handle full queue when sending fragments

2006-06-23 Thread Jiri Benc
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

2006-06-12 Thread Jiri Benc
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;
-   }
-
-