netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/5] d80211: better fragmentation handling
@ 2006-06-12 19:16 Jiri Benc
  2006-06-12 19:16 ` [PATCH 1/5] d80211: per-queue TX flow control Jiri Benc
                   ` (5 more replies)
  0 siblings, 6 replies; 12+ messages in thread
From: Jiri Benc @ 2006-06-12 19:16 UTC (permalink / raw)
  To: netdev; +Cc: John W. Linville, Michael Buesch

Following patches allow proper handling of situation when hw queue gets
filled up while sending 802.11 fragments.

This breaks drivers; an example how to fix them is in second patch. Also,
get_tx_stats callback is optional now.

-- 
Jiri Benc
SUSE Labs

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/5] d80211: per-queue TX flow control
  2006-06-12 19:16 [PATCH 0/5] d80211: better fragmentation handling Jiri Benc
@ 2006-06-12 19:16 ` Jiri Benc
  2006-06-12 19:16 ` [incomplete 2/5] bcm43xx-d80211: " Jiri Benc
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Jiri Benc @ 2006-06-12 19:16 UTC (permalink / raw)
  To: netdev; +Cc: John W. Linville, Michael Buesch

Currently, before a packet is passed to the driver, the driver is asked
about status of its TX queues (i.e. how many packets are queued in each
queue and how large are queues).

This is different from the way generic networking works in Linux and it
doesn't allow easy implementation of resubmitting fragments to the driver
when the queue gets filled up during transmitting.

This patch changes the stack not to ask driver about queue status but
require driver to do TX flow control.

Please note that this breaks drivers.

Signed-off-by: Jiri Benc <jbenc@suse.cz>

---
 include/net/d80211.h     |   30 ++++++++++++++++++++++++++++++
 net/d80211/ieee80211.c   |   27 +++++++++++++++++++++++++++
 net/d80211/ieee80211_i.h |    6 ++++++
 net/d80211/wme.c         |    6 +-----
 4 files changed, 64 insertions(+), 5 deletions(-)

--- dscape.orig/include/net/d80211.h
+++ dscape/include/net/d80211.h
@@ -781,6 +781,7 @@ int ieee80211_get_hdrlen(u16 fc);
  * netdevices for each hardware device. The low-level driver does not "see"
  * these interfaces, so it should use this function to perform netif
  * operations on all interface. */
+/* This function is deprecated. */
 typedef enum {
 	NETIF_ATTACH, NETIF_DETACH, NETIF_START, NETIF_STOP, NETIF_WAKE,
 	NETIF_IS_STOPPED, NETIF_UPDATE_TX_START
@@ -788,6 +789,35 @@ typedef enum {
 int ieee80211_netif_oper(struct net_device *dev, Netif_Oper op);
 
 /**
+ * ieee80211_wake_queue - wake specific queue
+ * @dev: pointer to &struct net_device as obtained from
+ *	ieee80211_alloc_hw().
+ * @queue: queue number (counted from zero).
+ *
+ * Drivers should use this function instead of netif_wake_queue.
+ */
+void ieee80211_wake_queue(struct net_device *dev, int queue);
+
+/**
+ * ieee80211_stop_queue - stop specific queue
+ * @dev: pointer to &struct net_device as obtained from
+ *	ieee80211_alloc_hw().
+ * @queue: queue number (counted from zero).
+ *
+ * Drivers should use this function instead of netif_stop_queue.
+ */
+void ieee80211_stop_queue(struct net_device *dev, int queue);
+
+/**
+ * ieee80211_start_queues - start all queues
+ * @dev: pointer to &struct net_device as obtained from
+ *	ieee80211_alloc_hw().
+ *
+ * Drivers should use this function instead of netif_start_queue.
+ */
+void ieee80211_start_queues(struct net_device *dev);
+
+/**
  * ieee80211_get_mc_list_item - iteration over items in multicast list
  * @dev: pointer to &struct net_device as obtained from
  *	ieee80211_alloc_hw().
--- dscape.orig/net/d80211/ieee80211.c
+++ dscape/net/d80211/ieee80211.c
@@ -4421,6 +4421,30 @@ int ieee80211_netif_oper(struct net_devi
         return 0;
 }
 
+void ieee80211_wake_queue(struct net_device *dev, int queue)
+{
+	struct ieee80211_local *local = dev->ieee80211_ptr;
+
+	if (test_and_clear_bit(IEEE80211_LINK_STATE_XOFF,
+			       &local->state[queue]))
+		__netif_schedule(dev);
+}
+
+void ieee80211_stop_queue(struct net_device *dev, int queue)
+{
+	struct ieee80211_local *local = dev->ieee80211_ptr;
+
+	set_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
+}
+
+void ieee80211_start_queues(struct net_device *dev)
+{
+	struct ieee80211_local *local = dev->ieee80211_ptr;
+	int i;
+
+	for (i = 0; i < local->hw->queues; i++)
+		clear_bit(IEEE80211_LINK_STATE_XOFF, &local->state[i]);
+}
 
 void * ieee80211_dev_hw_data(struct net_device *dev)
 {
@@ -4548,6 +4572,9 @@ EXPORT_SYMBOL(ieee80211_tx_status);
 EXPORT_SYMBOL(ieee80211_beacon_get);
 EXPORT_SYMBOL(ieee80211_get_buffered_bc);
 EXPORT_SYMBOL(ieee80211_netif_oper);
+EXPORT_SYMBOL(ieee80211_wake_queue);
+EXPORT_SYMBOL(ieee80211_stop_queue);
+EXPORT_SYMBOL(ieee80211_start_queues);
 EXPORT_SYMBOL(ieee80211_dev_hw_data);
 EXPORT_SYMBOL(ieee80211_dev_stats);
 EXPORT_SYMBOL(ieee80211_get_hw_conf);
--- dscape.orig/net/d80211/ieee80211_i.h
+++ dscape/net/d80211/ieee80211_i.h
@@ -353,6 +353,8 @@ struct ieee80211_local {
 	struct sta_info *sta_hash[STA_HASH_SIZE];
 	struct timer_list sta_cleanup;
 
+	unsigned long state[NUM_TX_DATA_QUEUES];
+
 	int mc_count;	/* total count of multicast entries in all interfaces */
 	int iff_allmultis, iff_promiscs;
 			/* number of interfaces with corresponding IFF_ flags */
@@ -514,6 +516,10 @@ struct ieee80211_local {
 	int user_space_mlme;
 };
 
+enum ieee80211_link_state_t {
+	IEEE80211_LINK_STATE_XOFF = 0,
+};
+
 struct sta_attribute {
 	struct attribute attr;
 	ssize_t (*show)(const struct sta_info *, char *buf);
--- dscape.orig/net/d80211/wme.c
+++ dscape/net/d80211/wme.c
@@ -316,18 +316,14 @@ static struct sk_buff *wme_qdiscop_deque
 	struct net_device *dev = qd->dev;
 	struct ieee80211_local *local = dev->ieee80211_ptr;
 	struct ieee80211_hw *hw = local->hw;
-	struct ieee80211_tx_queue_stats stats;
 	struct sk_buff *skb;
 	struct Qdisc *qdisc;
 	int queue;
 
-	/* find which hardware queues have space in them */
-	hw->get_tx_stats(dev, &stats);
-
 	/* check all the h/w queues in numeric/priority order */
 	for (queue = 0; queue < hw->queues; queue++) {
 		/* see if there is room in this hardware queue */
-		if (stats.data[queue].len >= stats.data[queue].limit)
+		if (test_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]))
 			continue;
 
 		/* there is space - try and get a frame */

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [incomplete 2/5] bcm43xx-d80211: per-queue TX flow control
  2006-06-12 19:16 [PATCH 0/5] d80211: better fragmentation handling Jiri Benc
  2006-06-12 19:16 ` [PATCH 1/5] d80211: per-queue TX flow control Jiri Benc
@ 2006-06-12 19:16 ` Jiri Benc
  2006-06-12 19:16 ` [PATCH 3/5] d80211: handle full queue when sending fragments Jiri Benc
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Jiri Benc @ 2006-06-12 19:16 UTC (permalink / raw)
  To: netdev; +Cc: John W. Linville, Michael Buesch

This is an attempt to fix bcm43xx driver. It is for DMA mode only and
incomplete even for that mode - ieee80211_hw->tx() callback should return
NETDEV_TX_* constants which is not completely fixed by this patch.

---
 drivers/net/wireless/d80211/bcm43xx/bcm43xx_dma.c  |    6 +++++-
 drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c |    1 +
 2 files changed, 6 insertions(+), 1 deletion(-)

--- dscape.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_dma.c
+++ dscape/drivers/net/wireless/d80211/bcm43xx/bcm43xx_dma.c
@@ -778,13 +778,16 @@ int bcm43xx_dma_tx(struct bcm43xx_privat
 		 * recognizes if the device queue is full and does
 		 * not send data anymore.
 		 */
+		ieee80211_stop_queue(bcm->net_dev, 0);
 		printk(KERN_ERR PFX "DMA queue overflow\n");
-		return -ENOMEM;
+		return NETDEV_TX_BUSY;
 	}
 
 	err = dma_tx_fragment(ring, skb, ctl);
 	if (likely(!err))
 		ring->nr_tx_packets++;
+	if (free_slots(ring) < SLOTS_PER_PACKET)
+		ieee80211_stop_queue(bcm->net_dev, 0);
 
 	return err;
 }
@@ -833,6 +836,7 @@ void bcm43xx_dma_handle_xmitstatus(struc
 		slot = next_slot(ring, slot);
 	}
 	bcm->stats.last_tx = jiffies;
+	ieee80211_wake_queue(bcm->net_dev, 0);
 }
 
 void bcm43xx_dma_get_tx_stats(struct bcm43xx_private *bcm,
--- dscape.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c
+++ dscape/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c
@@ -3737,6 +3737,7 @@ static int bcm43xx_init_board(struct bcm
 	bcm43xx_security_init(bcm);
 	bcm43xx_measure_channel_change_time(bcm);
 	ieee80211_update_hw(bcm->net_dev, bcm->ieee);
+	ieee80211_start_queues(bcm->net_dev);
 	ieee80211_netif_oper(bcm->net_dev, NETIF_ATTACH);
 	ieee80211_netif_oper(bcm->net_dev, NETIF_START);
 	ieee80211_netif_oper(bcm->net_dev, NETIF_WAKE);

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 3/5] d80211: handle full queue when sending fragments
  2006-06-12 19:16 [PATCH 0/5] d80211: better fragmentation handling Jiri Benc
  2006-06-12 19:16 ` [PATCH 1/5] d80211: per-queue TX flow control Jiri Benc
  2006-06-12 19:16 ` [incomplete 2/5] bcm43xx-d80211: " Jiri Benc
@ 2006-06-12 19:16 ` Jiri Benc
  2006-06-12 19:16 ` [PATCH 4/5] d80211: add first_fragment flag to ieee80211_tx_control Jiri Benc
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Jiri Benc @ 2006-06-12 19:16 UTC (permalink / raw)
  To: netdev; +Cc: John W. Linville, Michael Buesch

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 <jbenc@suse.cz>

---
 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_OK		0
+#define IEEE80211_TX_AGAIN	1
+#define IEEE80211_TX_FRAG_AGAIN	2
+
+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;
-		}
-
-		skb = NULL; /* skb is now owned by low-level driver */
-		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++) {
 			int next_len, dur;
 			struct ieee80211_hdr *hdr =
 				(struct ieee80211_hdr *)
 				tx.u.tx.extra_frag[i]->data;
-			if (i + 1 < tx.u.tx.num_extra_frag)
+
+			if (i + 1 < tx.u.tx.num_extra_frag) {
 				next_len = tx.u.tx.extra_frag[i + 1]->len;
-			else {
+			} else {
 				next_len = 0;
 				tx.u.tx.rate = tx.u.tx.last_frag_rate;
-				tx.u.tx.control->tx_rate = tx.u.tx.rate->val;
-				tx.u.tx.control->rateidx =
-					tx.u.tx.last_frag_rateidx;
-				tx.u.tx.control->rate_ctrl_probe =
-					tx.u.tx.probe_last_frag;
+				tx.u.tx.last_frag_hwrate = tx.u.tx.rate->val;
 			}
 			dur = ieee80211_duration(&tx, 0, next_len);
 			hdr->duration_id = cpu_to_le16(dur);
-
-			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 > 0)
-				goto drop;
-#ifdef IEEE80211_LEDS
-			if (local->tx_led_counter++ == 0) {
-                                ieee80211_tx_led(1, dev);
-                        }
-#endif /* IEEE80211_LEDS */
-			tx.u.tx.extra_frag[i] = NULL;
 		}
-		kfree(tx.u.tx.extra_frag);
-        }
-        if (ret == -1)
-		ret = 0;
-	return ret;
+	}
+
+retry:
+	ret = __ieee80211_tx(local, skb, &tx);
+	if (ret) {
+		struct ieee80211_tx_stored_packet *store =
+			&local->pending_packet[control->queue];
+
+		if (ret == IEEE80211_TX_FRAG_AGAIN)
+			skb = NULL;
+		set_bit(IEEE80211_LINK_STATE_PENDING,
+			&local->state[control->queue]);
+		smp_mb();
+		/* When the driver gets out of buffers during sending of
+		 * fragments and calls ieee80211_stop_queue, there is
+		 * a small window between IEEE80211_LINK_STATE_XOFF and
+		 * IEEE80211_LINK_STATE_PENDING flags are set. If a buffer
+		 * gets available in that window (i.e. driver calls
+		 * ieee80211_wake_queue), we would end up with ieee80211_tx
+		 * called with IEEE80211_LINK_STATE_PENDING. Prevent this by
+		 * continuing transmitting here when that situation is
+		 * possible to have happened. */
+		if (!__ieee80211_queue_stopped(local, control->queue)) {
+			clear_bit(IEEE80211_LINK_STATE_PENDING,
+				  &local->state[control->queue]);
+			goto retry;
+		}
+		memcpy(&store->control, control,
+		       sizeof(struct ieee80211_tx_control));
+		store->skb = skb;
+		store->extra_frag = tx.u.tx.extra_frag;
+		store->num_extra_frag = tx.u.tx.num_extra_frag;
+		store->last_frag_hwrate = tx.u.tx.last_frag_hwrate;
+		store->last_frag_rateidx = tx.u.tx.last_frag_rateidx;
+		store->last_frag_rate_ctrl_probe = tx.u.tx.probe_last_frag;
+	}
+	return 0;
 
  drop:
 	if (skb)
@@ -1263,6 +1328,62 @@ static int ieee80211_tx(struct net_devic
 	return 0;
 }
 
+static void ieee80211_tx_pending(unsigned long data)
+{
+	struct ieee80211_local *local = (struct ieee80211_local *)data;
+	struct net_device *dev = local->mdev;
+	struct ieee80211_tx_stored_packet *store;
+	struct ieee80211_txrx_data tx;
+	int i, ret, reschedule = 0;
+
+	spin_lock_bh(&dev->xmit_lock);
+	dev->xmit_lock_owner = smp_processor_id();
+	for (i = 0; i < local->hw->queues; i++) {
+		if (__ieee80211_queue_stopped(local, i))
+			continue;
+		if (!__ieee80211_queue_pending(local, i)) {
+			reschedule = 1;
+			continue;
+		}
+		store = &local->pending_packet[i];
+		tx.u.tx.control = &store->control;
+		tx.u.tx.extra_frag = store->extra_frag;
+		tx.u.tx.num_extra_frag = store->num_extra_frag;
+		tx.u.tx.last_frag_hwrate = store->last_frag_hwrate;
+		tx.u.tx.last_frag_rateidx = store->last_frag_rateidx;
+		tx.u.tx.probe_last_frag = store->last_frag_rate_ctrl_probe;
+		ret = __ieee80211_tx(local, store->skb, &tx);
+		if (ret) {
+			if (ret == IEEE80211_TX_FRAG_AGAIN)
+				store->skb = NULL;
+		} else {
+			clear_bit(IEEE80211_LINK_STATE_PENDING,
+				  &local->state[i]);
+			reschedule = 1;
+		}
+	}
+	dev->xmit_lock_owner = -1;
+	spin_unlock_bh(&dev->xmit_lock);
+	if (reschedule)
+		netif_schedule(dev);
+}
+
+static void ieee80211_clear_tx_pending(struct ieee80211_local *local)
+{
+	int i, j;
+	struct ieee80211_tx_stored_packet *store;
+
+	for (i = 0; i < local->hw->queues; i++) {
+		if (!__ieee80211_queue_pending(local, i))
+			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);
+		clear_bit(IEEE80211_LINK_STATE_PENDING, &local->state[i]);
+	}
+}
 
 static int ieee80211_master_start_xmit(struct sk_buff *skb,
 				       struct net_device *dev)
@@ -1999,6 +2120,7 @@ static int ieee80211_master_open(struct 
 	list_for_each_entry(sdata, &local->sub_if_list, list) {
 		if (sdata->dev != dev && netif_running(sdata->dev)) {
 			res = 0;
+			tasklet_enable(&local->tx_pending_tasklet);
 			break;
 		}
 	}
@@ -2010,6 +2132,7 @@ static int ieee80211_master_stop(struct 
 	struct ieee80211_local *local = dev->ieee80211_ptr;
 	struct ieee80211_sub_if_data *sdata;
 
+	tasklet_disable(&local->tx_pending_tasklet);
 	list_for_each_entry(sdata, &local->sub_if_list, list) {
 		if (sdata->dev != dev && netif_running(sdata->dev))
 			return -EOPNOTSUPP;
@@ -4175,6 +4298,10 @@ struct net_device *ieee80211_alloc_hw(si
         sdata->local = local;
         list_add_tail(&sdata->list, &local->sub_if_list);
 
+	tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
+		     (unsigned long)local);
+	tasklet_disable(&local->tx_pending_tasklet);
+
         tasklet_init(&local->tasklet,
 		     ieee80211_tasklet_handler,
 		     (unsigned long) local);
@@ -4344,6 +4471,7 @@ void ieee80211_unregister_hw(struct net_
 	}
 	rtnl_unlock();
 
+	ieee80211_clear_tx_pending(local);
 	sta_info_stop(local);
 	rate_control_remove_attrs(local, local->rate_ctrl_priv,
 				  &local->class_dev.kobj);
@@ -4426,8 +4554,13 @@ void ieee80211_wake_queue(struct net_dev
 	struct ieee80211_local *local = dev->ieee80211_ptr;
 
 	if (test_and_clear_bit(IEEE80211_LINK_STATE_XOFF,
-			       &local->state[queue]))
-		__netif_schedule(dev);
+			       &local->state[queue])) {
+		if (test_bit(IEEE80211_LINK_STATE_PENDING,
+			     &local->state[queue]))
+			tasklet_schedule(&local->tx_pending_tasklet);
+		else
+			__netif_schedule(dev);
+	}
 }
 
 void ieee80211_stop_queue(struct net_device *dev, int queue)
--- dscape.orig/net/d80211/ieee80211_i.h
+++ dscape/net/d80211/ieee80211_i.h
@@ -125,6 +125,7 @@ struct ieee80211_txrx_data {
 			 * when using CTS protection with IEEE 802.11g. */
 			struct ieee80211_rate *last_frag_rate;
 			int last_frag_rateidx;
+			int last_frag_hwrate;
 			int mgmt_interface;
 
 			/* Extra fragments (in addition to the first fragment
@@ -155,6 +156,16 @@ struct ieee80211_tx_packet_data {
 	unsigned int mgmt_iface:1;
 };
 
+struct ieee80211_tx_stored_packet {
+	struct ieee80211_tx_control control;
+	struct sk_buff *skb;
+	int num_extra_frag;
+	struct sk_buff **extra_frag;
+	int last_frag_rateidx;
+	int last_frag_hwrate;
+	unsigned int last_frag_rate_ctrl_probe:1;
+};
+
 struct ieee80211_passive_scan {
         unsigned int in_scan:1; /* this must be cleared before calling
 				 * netif_oper(WAKEUP) */
@@ -354,6 +365,8 @@ struct ieee80211_local {
 	struct timer_list sta_cleanup;
 
 	unsigned long state[NUM_TX_DATA_QUEUES];
+	struct ieee80211_tx_stored_packet pending_packet[NUM_TX_DATA_QUEUES];
+	struct tasklet_struct tx_pending_tasklet;
 
 	int mc_count;	/* total count of multicast entries in all interfaces */
 	int iff_allmultis, iff_promiscs;
@@ -518,6 +531,7 @@ struct ieee80211_local {
 
 enum ieee80211_link_state_t {
 	IEEE80211_LINK_STATE_XOFF = 0,
+	IEEE80211_LINK_STATE_PENDING,
 };
 
 struct sta_attribute {
--- dscape.orig/net/d80211/wme.c
+++ dscape/net/d80211/wme.c
@@ -323,7 +323,10 @@ static struct sk_buff *wme_qdiscop_deque
 	/* check all the h/w queues in numeric/priority order */
 	for (queue = 0; queue < hw->queues; queue++) {
 		/* see if there is room in this hardware queue */
-		if (test_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]))
+		if (test_bit(IEEE80211_LINK_STATE_XOFF,
+			     &local->state[queue]) ||
+		    test_bit(IEEE80211_LINK_STATE_PENDING,
+			     &local->state[queue]))
 			continue;
 
 		/* there is space - try and get a frame */

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 4/5] d80211: add first_fragment flag to ieee80211_tx_control
  2006-06-12 19:16 [PATCH 0/5] d80211: better fragmentation handling Jiri Benc
                   ` (2 preceding siblings ...)
  2006-06-12 19:16 ` [PATCH 3/5] d80211: handle full queue when sending fragments Jiri Benc
@ 2006-06-12 19:16 ` Jiri Benc
  2006-06-12 19:16 ` [PATCH 5/5] bcm43xx-d80211: fix sending of fragments Jiri Benc
  2006-06-14 16:36 ` [PATCH 6/5] rt2x00: per-queue TX flow control Jiri Benc
  5 siblings, 0 replies; 12+ messages in thread
From: Jiri Benc @ 2006-06-12 19:16 UTC (permalink / raw)
  To: netdev; +Cc: John W. Linville, Michael Buesch

If a driver needs to find out if the fragment it is supposed to pass to the
hardware is the first fragment, the only way to do this is to check if
a Fragment Number part of seq_ctrl field in the frame header equals to 0.
Let's make it easier.

Signed-off-by: Jiri Benc <jbenc@suse.cz>

---
 include/net/d80211.h   |    2 ++
 net/d80211/ieee80211.c |    2 ++
 2 files changed, 4 insertions(+)

--- dscape.orig/include/net/d80211.h
+++ dscape/include/net/d80211.h
@@ -158,6 +158,8 @@ struct ieee80211_tx_control {
 	unsigned int rate_ctrl_probe:1;
 	unsigned int clear_dst_mask:1;
 	unsigned int requeue:1;
+	unsigned int first_fragment:1;	/* This is a first fragment of the
+					 * frame */
 	 /* following three flags are only used with Atheros Super A/G */
 	unsigned int compress:1;
 	unsigned int turbo_prime_notify:1; /* notify HostaAPd after frame
--- dscape.orig/net/d80211/ieee80211.c
+++ dscape/net/d80211/ieee80211.c
@@ -1120,6 +1120,7 @@ __ieee80211_tx_prepare(struct ieee80211_
 		u8 *pos = &skb->data[hdrlen + sizeof(rfc1042_header)];
 		tx->ethertype = (pos[0] << 8) | pos[1];
 	}
+	control->first_fragment = 1;
 
 }
 
@@ -1190,6 +1191,7 @@ static int __ieee80211_tx(struct ieee802
 		control->use_rts_cts = 0;
 		control->use_cts_protect = 0;
 		control->clear_dst_mask = 0;
+		control->first_fragment = 0;
 		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
 			if (!tx->u.tx.extra_frag[i])
 				continue;

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 5/5] bcm43xx-d80211: fix sending of fragments
  2006-06-12 19:16 [PATCH 0/5] d80211: better fragmentation handling Jiri Benc
                   ` (3 preceding siblings ...)
  2006-06-12 19:16 ` [PATCH 4/5] d80211: add first_fragment flag to ieee80211_tx_control Jiri Benc
@ 2006-06-12 19:16 ` Jiri Benc
  2006-06-12 19:35   ` Michael Buesch
  2006-06-14 16:36 ` [PATCH 6/5] rt2x00: per-queue TX flow control Jiri Benc
  5 siblings, 1 reply; 12+ messages in thread
From: Jiri Benc @ 2006-06-12 19:16 UTC (permalink / raw)
  To: netdev; +Cc: John W. Linville, Michael Buesch

This makes fragmentation work with bcm43xx.

Signed-off-by: Jiri Benc <jbenc@suse.cz>

---
 drivers/net/wireless/d80211/bcm43xx/bcm43xx_dma.c |    2 +-
 drivers/net/wireless/d80211/bcm43xx/bcm43xx_pio.c |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

--- dscape.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_dma.c
+++ dscape/drivers/net/wireless/d80211/bcm43xx/bcm43xx_dma.c
@@ -703,7 +703,7 @@ static int dma_tx_fragment(struct bcm43x
 	bcm43xx_generate_txhdr(ring->bcm,
 			       (struct bcm43xx_txhdr *)hdr_skb->data,
 			       skb->data, skb->len,
-			       1,//FIXME
+			       ctl->first_fragment,
 			       generate_cookie(ring, slot),
 			       ctl);
 
--- dscape.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_pio.c
+++ dscape/drivers/net/wireless/d80211/bcm43xx/bcm43xx_pio.c
@@ -183,7 +183,7 @@ static void pio_tx_write_fragment(struct
 	assert(skb_shinfo(skb)->nr_frags == 0);
 	bcm43xx_generate_txhdr(queue->bcm,
 			       &txhdr, skb->data, skb->len,
-			       1,//FIXME
+			       packet->ctl->first_fragment,
 			       generate_cookie(queue, pio_txpacket_getindex(packet)),
 			       packet->ctl);
 

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 5/5] bcm43xx-d80211: fix sending of fragments
  2006-06-12 19:16 ` [PATCH 5/5] bcm43xx-d80211: fix sending of fragments Jiri Benc
@ 2006-06-12 19:35   ` Michael Buesch
  2006-06-15 16:55     ` Michael Buesch
  0 siblings, 1 reply; 12+ messages in thread
From: Michael Buesch @ 2006-06-12 19:35 UTC (permalink / raw)
  To: Jiri Benc; +Cc: John W. Linville, netdev

On Monday 12 June 2006 21:16, Jiri Benc wrote:
> This makes fragmentation work with bcm43xx.
> 
> Signed-off-by: Jiri Benc <jbenc@suse.cz>

Signed-off-by: Michael Buesch <mb@bu3sch.de>

The other patch will get my sign-off tomorrow (I think modified, though).
I don't have time to look at it more close, now.

> ---
>  drivers/net/wireless/d80211/bcm43xx/bcm43xx_dma.c |    2 +-
>  drivers/net/wireless/d80211/bcm43xx/bcm43xx_pio.c |    2 +-
>  2 files changed, 2 insertions(+), 2 deletions(-)
> 
> --- dscape.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_dma.c
> +++ dscape/drivers/net/wireless/d80211/bcm43xx/bcm43xx_dma.c
> @@ -703,7 +703,7 @@ static int dma_tx_fragment(struct bcm43x
>  	bcm43xx_generate_txhdr(ring->bcm,
>  			       (struct bcm43xx_txhdr *)hdr_skb->data,
>  			       skb->data, skb->len,
> -			       1,//FIXME
> +			       ctl->first_fragment,
>  			       generate_cookie(ring, slot),
>  			       ctl);
>  
> --- dscape.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_pio.c
> +++ dscape/drivers/net/wireless/d80211/bcm43xx/bcm43xx_pio.c
> @@ -183,7 +183,7 @@ static void pio_tx_write_fragment(struct
>  	assert(skb_shinfo(skb)->nr_frags == 0);
>  	bcm43xx_generate_txhdr(queue->bcm,
>  			       &txhdr, skb->data, skb->len,
> -			       1,//FIXME
> +			       packet->ctl->first_fragment,
>  			       generate_cookie(queue, pio_txpacket_getindex(packet)),
>  			       packet->ctl);
>  
> 

-- 
Greetings Michael.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 6/5] rt2x00: per-queue TX flow control
  2006-06-12 19:16 [PATCH 0/5] d80211: better fragmentation handling Jiri Benc
                   ` (4 preceding siblings ...)
  2006-06-12 19:16 ` [PATCH 5/5] bcm43xx-d80211: fix sending of fragments Jiri Benc
@ 2006-06-14 16:36 ` Jiri Benc
  2006-06-14 18:35   ` Ivo van Doorn
  5 siblings, 1 reply; 12+ messages in thread
From: Jiri Benc @ 2006-06-14 16:36 UTC (permalink / raw)
  To: netdev; +Cc: John W. Linville, Ivo van Doorn

This is a patch for rt2x00 driver to do TX flow control.

It is compile-tested only.

Signed-off-by: Jiri Benc <jbenc@suse.cz>

---
 drivers/net/wireless/d80211/rt2x00/rt2400pci.c |   26 ++++++++++++++++++-------
 drivers/net/wireless/d80211/rt2x00/rt2500pci.c |   26 ++++++++++++++++++-------
 drivers/net/wireless/d80211/rt2x00/rt2500usb.c |   18 +++++++++++++----
 drivers/net/wireless/d80211/rt2x00/rt61pci.c   |   26 ++++++++++++++++++-------
 drivers/net/wireless/d80211/rt2x00/rt73usb.c   |   18 +++++++++++++----
 5 files changed, 85 insertions(+), 29 deletions(-)

--- dscape.orig/drivers/net/wireless/d80211/rt2x00/rt2400pci.c
+++ dscape/drivers/net/wireless/d80211/rt2x00/rt2400pci.c
@@ -1001,7 +1001,7 @@ rt2400pci_txdone(void *data)
 	struct data_entry	*entry;
 	struct txd		*txd;
 	int			tx_status;
-	int			ack;
+	int			ack, wake, queue;
 
 	while (!rt2x00_ring_empty(ring)) {
 		entry = rt2x00_get_data_entry_done(ring);
@@ -1048,7 +1048,11 @@ rt2400pci_txdone(void *data)
 		rt2x00_set_field32(&txd->word0, TXD_W0_VALID, 0);
 		entry->skb = NULL;
 
+		wake = rt2x00_ring_full(ring);
+		queue = entry->tx_status.control.queue;
 		rt2x00_ring_index_done_inc(ring);
+		if (wake)
+			ieee80211_wake_queue(ring->net_dev, queue);
 	}
 
 	/*
@@ -1541,24 +1545,31 @@ rt2400pci_tx(struct net_device *net_dev,
 		ERROR("Attempt to send packet over invalid queue %d.\n"
 			"Please file bug report to %s.\n",
 			control->queue, DRV_PROJECT);
-		return NET_XMIT_DROP;
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_OK;
 	}
 
-	if (rt2x00_ring_full(ring))
-		return NET_XMIT_DROP;
+	if (rt2x00_ring_full(ring)) {
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	entry = rt2x00_get_data_entry(ring);
 	txd = entry->desc_addr;
 
-	if (rt2x00_get_field32(txd->word0, TXD_W0_OWNER_NIC)
-	|| rt2x00_get_field32(txd->word0, TXD_W0_VALID))
-		return NET_XMIT_DROP;
+	if (rt2x00_get_field32(txd->word0, TXD_W0_OWNER_NIC) ||
+	    rt2x00_get_field32(txd->word0, TXD_W0_VALID)) {
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	memcpy(entry->data_addr, skb->data, skb->len);
 	rt2400pci_write_tx_desc(rt2x00pci, txd, skb, control);
 	entry->skb = skb;
 
 	rt2x00_ring_index_inc(ring);
+	if (rt2x00_ring_full(ring))
+		ieee80211_stop_queue(net_dev, control->queue);
 
 	rt2x00_register_read(rt2x00pci, TXCSR0, &reg);
 	if (control->queue == IEEE80211_TX_QUEUE_DATA0)
@@ -1668,6 +1679,7 @@ rt2400pci_open(struct net_device *net_de
 	rt2x00_register_write(rt2x00pci, CSR8, reg);
 
 	SET_FLAG(rt2x00pci, RADIO_ENABLED);
+	ieee80211_start_queues(net_dev);
 
 	return 0;
 
--- dscape.orig/drivers/net/wireless/d80211/rt2x00/rt2500pci.c
+++ dscape/drivers/net/wireless/d80211/rt2x00/rt2500pci.c
@@ -1089,7 +1089,7 @@ rt2500pci_txdone(void *data)
 	struct data_entry	*entry;
 	struct txd		*txd;
 	int			tx_status;
-	int			ack;
+	int			ack, wake, queue;
 
 	while (!rt2x00_ring_empty(ring)) {
 		entry = rt2x00_get_data_entry_done(ring);
@@ -1136,7 +1136,11 @@ rt2500pci_txdone(void *data)
 		rt2x00_set_field32(&txd->word0, TXD_W0_VALID, 0);
 		entry->skb = NULL;
 
+		wake = rt2x00_ring_full(ring);
+		queue = entry->tx_status.control.queue;
 		rt2x00_ring_index_done_inc(ring);
+		if (wake)
+			ieee80211_wake_queue(ring->net_dev, queue);
 	}
 
 	/*
@@ -1664,24 +1668,31 @@ rt2500pci_tx(struct net_device *net_dev,
 		ERROR("Attempt to send packet over invalid queue %d.\n"
 			"Please file bug report to %s.\n",
 			control->queue, DRV_PROJECT);
-		return NET_XMIT_DROP;
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_OK;
 	}
 
-	if (rt2x00_ring_full(ring))
-		return NET_XMIT_DROP;
+	if (rt2x00_ring_full(ring)) {
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	entry = rt2x00_get_data_entry(ring);
 	txd = entry->desc_addr;
 
-	if (rt2x00_get_field32(txd->word0, TXD_W0_OWNER_NIC)
-	|| rt2x00_get_field32(txd->word0, TXD_W0_VALID))
-		return NET_XMIT_DROP;
+	if (rt2x00_get_field32(txd->word0, TXD_W0_OWNER_NIC) ||
+	    rt2x00_get_field32(txd->word0, TXD_W0_VALID)) {
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	memcpy(entry->data_addr, skb->data, skb->len);
 	rt2500pci_write_tx_desc(rt2x00pci, txd, skb, control);
 	entry->skb = skb;
 
 	rt2x00_ring_index_inc(ring);
+	if (rt2x00_ring_full(ring))
+		ieee80211_stop_queue(net_dev, control->queue);
 
 	rt2x00_register_read(rt2x00pci, TXCSR0, &reg);
 	if (control->queue == IEEE80211_TX_QUEUE_DATA0)
@@ -1791,6 +1802,7 @@ rt2500pci_open(struct net_device *net_de
 	rt2x00_register_write(rt2x00pci, CSR8, reg);
 
 	SET_FLAG(rt2x00pci, RADIO_ENABLED);
+	ieee80211_start_queues(net_dev);
 
 	return 0;
 
--- dscape.orig/drivers/net/wireless/d80211/rt2x00/rt2500usb.c
+++ dscape/drivers/net/wireless/d80211/rt2x00/rt2500usb.c
@@ -929,7 +929,7 @@ rt2500usb_txdone(void *data)
 		ieee80211_dev_hw_data(ring->net_dev);
 	struct data_entry	*entry;
 	struct txd		*txd;
-	int			ack;
+	int			ack, wake, queue;
 
 	 while (!rt2x00_ring_empty(ring)) {
 		entry = rt2x00_get_data_entry_done(ring);
@@ -968,7 +968,11 @@ rt2500usb_txdone(void *data)
 	
 		entry->skb = NULL;
 	
+		wake = rt2x00_ring_full(ring);
+		queue = entry->tx_status.control.queue;
 		rt2x00_ring_index_done_inc(entry->ring);
+		if (wake)
+			ieee80211_wake_queue(ring->net_dev, queue);
 	}
 
 	/*
@@ -1376,11 +1380,14 @@ rt2500usb_tx(struct net_device *net_dev,
 		ERROR("Attempt to send packet over invalid queue %d.\n"
 			"Please file bug report to %s.\n",
 			control->queue, DRV_PROJECT);
-		return NET_XMIT_DROP;
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_BUSY;
 	}
 
-	if (rt2x00_ring_full(ring))
-		return NET_XMIT_DROP;
+	if (rt2x00_ring_full(ring)) {
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	entry = rt2x00_get_data_entry(ring);
 	txd = rt2x00usb_txdesc_addr(entry);
@@ -1401,6 +1408,8 @@ rt2500usb_tx(struct net_device *net_dev,
 	usb_submit_urb(entry->urb, GFP_ATOMIC);
 
 	rt2x00_ring_index_inc(ring);
+	if (rt2x00_ring_full(ring))
+		ieee80211_stop_queue(net_dev, control->queue);
 
 	return 0;
 }
@@ -1468,6 +1477,7 @@ rt2500usb_open(struct net_device *net_de
 	rt2500usb_enable_led(rt2x00usb);
 
 	SET_FLAG(rt2x00usb, RADIO_ENABLED);
+	ieee80211_start_queues(net_dev);
 
 	return 0;
 
--- dscape.orig/drivers/net/wireless/d80211/rt2x00/rt61pci.c
+++ dscape/drivers/net/wireless/d80211/rt2x00/rt61pci.c
@@ -1351,7 +1351,7 @@ rt61pci_txdone(void *data)
 	struct data_entry	*entry;
 	struct txd		*txd;
 	int			tx_status;
-	int			ack;
+	int			ack, wake, queue;
 	int			reg;
 
 	while (!rt2x00_ring_empty(ring)) {
@@ -1402,7 +1402,11 @@ rt61pci_txdone(void *data)
 		rt2x00_set_field32(&txd->word0, TXD_W0_VALID, 0);
 		entry->skb = NULL;
 
+		wake = rt2x00_ring_full(ring);
+		queue = entry->tx_status.control.queue;
 		rt2x00_ring_index_done_inc(ring);
+		if (wake)
+			ieee80211_wake_queue(ring->net_dev, queue);
 	}
 
 	/*
@@ -2100,24 +2104,31 @@ rt61pci_tx(struct net_device *net_dev,
 		ERROR("Attempt to send packet over invalid queue %d.\n"
 			"Please file bug report to %s.\n",
 			control->queue, DRV_PROJECT);
-		return NET_XMIT_DROP;
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_OK;
 	}
 
-	if (rt2x00_ring_full(ring))
-		return NET_XMIT_DROP;
+	if (rt2x00_ring_full(ring)) {
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	entry = rt2x00_get_data_entry(ring);
 	txd = entry->desc_addr;
 
-	if (rt2x00_get_field32(txd->word0, TXD_W0_OWNER_NIC)
-	|| rt2x00_get_field32(txd->word0, TXD_W0_VALID))
-		return NET_XMIT_DROP;
+	if (rt2x00_get_field32(txd->word0, TXD_W0_OWNER_NIC) ||
+	    rt2x00_get_field32(txd->word0, TXD_W0_VALID)) {
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	memcpy(entry->data_addr, skb->data, skb->len);
 	rt61pci_write_tx_desc(rt2x00pci, txd, skb, control);
 	entry->skb = skb;
 
 	rt2x00_ring_index_inc(ring);
+	if (rt2x00_ring_full(ring))
+		ieee80211_stop_queue(net_dev, control->queue);
 
 	rt2x00_register_read(rt2x00pci, TX_CNTL_CSR, &reg);
 	if (control->queue == IEEE80211_TX_QUEUE_DATA0)
@@ -2248,6 +2259,7 @@ rt61pci_open(struct net_device *net_dev)
 	rt2x00_register_write(rt2x00pci, INT_SOURCE_CSR, reg);
 
 	SET_FLAG(rt2x00pci, RADIO_ENABLED);
+	ieee80211_start_queues(net_dev);
 
 	return 0;
 
--- dscape.orig/drivers/net/wireless/d80211/rt2x00/rt73usb.c
+++ dscape/drivers/net/wireless/d80211/rt2x00/rt73usb.c
@@ -1093,7 +1093,7 @@ rt73usb_txdone(void *data)
 		ieee80211_dev_hw_data(ring->net_dev);
 	struct data_entry	*entry;
 	struct txd		*txd;
-	int			ack;
+	int			ack, wake, queue;
 
 	while (!rt2x00_ring_empty(ring)) {
 		entry = rt2x00_get_data_entry_done(ring);
@@ -1132,7 +1132,11 @@ rt73usb_txdone(void *data)
 
 		entry->skb = NULL;
 
+		wake = rt2x00_ring_full(ring);
+		queue = entry->tx_status.control.queue;
 		rt2x00_ring_index_done_inc(entry->ring);
+		if (wake)
+			ieee80211_wake_queue(ring->net_dev, queue);
 	}
 
 	/*
@@ -1668,11 +1672,14 @@ rt73usb_tx(struct net_device *net_dev,
 		ERROR("Attempt to send packet over invalid queue %d.\n"
 			"Please file bug report to %s.\n",
 			control->queue, DRV_PROJECT);
-		return NET_XMIT_DROP;
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_OK;
 	}
 
-	if (rt2x00_ring_full(ring))
-		return NET_XMIT_DROP;
+	if (rt2x00_ring_full(ring)) {
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	entry = rt2x00_get_data_entry(ring);
 	txd = rt2x00usb_txdesc_addr(entry);
@@ -1693,6 +1700,8 @@ rt73usb_tx(struct net_device *net_dev,
 	usb_submit_urb(entry->urb, GFP_ATOMIC);
 
 	rt2x00_ring_index_inc(ring);
+	if (rt2x00_ring_full(ring))
+		ieee80211_stop_queue(net_dev, control->queue);
 
 	return 0;
 }
@@ -1765,6 +1774,7 @@ rt73usb_open(struct net_device *net_dev)
 	rt73usb_enable_led(rt2x00usb);
 
 	SET_FLAG(rt2x00usb, RADIO_ENABLED);
+	ieee80211_start_queues(net_dev);
 
 	return 0;
 

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 6/5] rt2x00: per-queue TX flow control
  2006-06-14 16:36 ` [PATCH 6/5] rt2x00: per-queue TX flow control Jiri Benc
@ 2006-06-14 18:35   ` Ivo van Doorn
  0 siblings, 0 replies; 12+ messages in thread
From: Ivo van Doorn @ 2006-06-14 18:35 UTC (permalink / raw)
  To: Jiri Benc; +Cc: netdev, John W. Linville

[-- Attachment #1: Type: text/plain, Size: 17271 bytes --]

Hi,

On Wednesday 14 June 2006 18:36, Jiri Benc wrote:
> This is a patch for rt2x00 driver to do TX flow control.
> 
> It is compile-tested only.
>
> Signed-off-by: Jiri Benc <jbenc@suse.cz>

I'll put my comments for the rt2400pci driver only,
since the same changes are made for each rt2x00 driver.

> --- dscape.orig/drivers/net/wireless/d80211/rt2x00/rt2400pci.c
> +++ dscape/drivers/net/wireless/d80211/rt2x00/rt2400pci.c
> @@ -1001,7 +1001,7 @@ rt2400pci_txdone(void *data)
>  	struct data_entry	*entry;
>  	struct txd		*txd;
>  	int			tx_status;
> -	int			ack;
> +	int			ack, wake, queue;
>  
>  	while (!rt2x00_ring_empty(ring)) {
>  		entry = rt2x00_get_data_entry_done(ring);
> @@ -1048,7 +1048,11 @@ rt2400pci_txdone(void *data)
>  		rt2x00_set_field32(&txd->word0, TXD_W0_VALID, 0);
>  		entry->skb = NULL;
>  
> +		wake = rt2x00_ring_full(ring);
> +		queue = entry->tx_status.control.queue;
>  		rt2x00_ring_index_done_inc(ring);
> +		if (wake)
> +			ieee80211_wake_queue(ring->net_dev, queue);
>  	}

This will not give the correct result I fear, and it would cause (unwanted)
overhead of checking if the queue was full.
Queue_full can be checked when the loop starts, and the
waking of the queue can best be done after freeing all entries
and after the second check if the queue is still not full. (There is no guarentee
the while() loop will end while there are free entries in the queue)

>  	/*
> @@ -1541,24 +1545,31 @@ rt2400pci_tx(struct net_device *net_dev,
>  		ERROR("Attempt to send packet over invalid queue %d.\n"
>  			"Please file bug report to %s.\n",
>  			control->queue, DRV_PROJECT);
> -		return NET_XMIT_DROP;
> +		dev_kfree_skb_any(skb);
> +		return NETDEV_TX_OK;
>  	}
>  
> -	if (rt2x00_ring_full(ring))
> -		return NET_XMIT_DROP;
> +	if (rt2x00_ring_full(ring)) {
> +		ieee80211_stop_queue(net_dev, control->queue);
> +		return NETDEV_TX_BUSY;
> +	}
>  
>  	entry = rt2x00_get_data_entry(ring);
>  	txd = entry->desc_addr;
>  
> -	if (rt2x00_get_field32(txd->word0, TXD_W0_OWNER_NIC)
> -	|| rt2x00_get_field32(txd->word0, TXD_W0_VALID))
> -		return NET_XMIT_DROP;
> +	if (rt2x00_get_field32(txd->word0, TXD_W0_OWNER_NIC) ||
> +	    rt2x00_get_field32(txd->word0, TXD_W0_VALID)) {
> +		ieee80211_stop_queue(net_dev, control->queue);
> +		return NETDEV_TX_BUSY;
> +	}

Not sure if I am happy with this one. When this check is made,
it occurs after the ring_full check. This means that when this statement
is true, the queue is not full. Instead it has more of a meaning that something
has gone wrong with the queue and this should not have happened.

But this is not really a problem in the patch itself, just a problem I only
now recognize thanks to your patch. ;)

For the time being I'll add a debug message, but I need to find a method
to clean up the ring if this occurs.
This check currently does not happen in the rt2570 and rt73 USB drivers,
but it is safer to add them in there as well.

>  	memcpy(entry->data_addr, skb->data, skb->len);
>  	rt2400pci_write_tx_desc(rt2x00pci, txd, skb, control);
>  	entry->skb = skb;
>  
>  	rt2x00_ring_index_inc(ring);
> +	if (rt2x00_ring_full(ring))
> +		ieee80211_stop_queue(net_dev, control->queue);
>  
>  	rt2x00_register_read(rt2x00pci, TXCSR0, &reg);
>  	if (control->queue == IEEE80211_TX_QUEUE_DATA0)
> @@ -1668,6 +1679,7 @@ rt2400pci_open(struct net_device *net_de
>  	rt2x00_register_write(rt2x00pci, CSR8, reg);
>  
>  	SET_FLAG(rt2x00pci, RADIO_ENABLED);
> +	ieee80211_start_queues(net_dev);
>  
>  	return 0;

Based on Jiri's patch for rt2x00 driver to do TX flow control.

Signed-off-by Ivo van Doorn <IvDoorn@gmail.com>

---

diff --git a/drivers/net/wireless/d80211/rt2x00/rt2400pci.c b/drivers/net/wireless/d80211/rt2x00/rt2400pci.c
index 8b856dd..946cf86 100644
--- a/drivers/net/wireless/d80211/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/d80211/rt2x00/rt2400pci.c
@@ -1002,6 +1002,12 @@ rt2400pci_txdone(void *data)
 	struct txd		*txd;
 	int			tx_status;
 	int			ack;
+	int			ring_full;
+
+	/*
+	 * Store the current status of the ring.
+	 */
+	ring_full = rt2x00_ring_full(ring);
 
 	while (!rt2x00_ring_empty(ring)) {
 		entry = rt2x00_get_data_entry_done(ring);
@@ -1062,6 +1068,16 @@ rt2400pci_txdone(void *data)
 		rt2x00pci->scan->status = SCANNING_READY;
 		complete(&rt2x00pci->scan->completion);
 	}
+
+	/*
+	 * If the data ring was full before the txdone handler
+	 * we must make sure the packet queue in the d80211 stack
+	 * is reenabled when the txdone handler has finished.
+	 */
+	entry = ring->entry;
+	if (ring_full && !rt2x00_ring_full(ring))
+		ieee80211_wake_queue(ring->net_dev,
+			entry->tx_status.control.queue);
 }
 
 static irqreturn_t
@@ -1541,18 +1557,26 @@ rt2400pci_tx(struct net_device *net_dev,
 		ERROR("Attempt to send packet over invalid queue %d.\n"
 			"Please file bug report to %s.\n",
 			control->queue, DRV_PROJECT);
-		return NET_XMIT_DROP;
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_OK;
 	}
 
-	if (rt2x00_ring_full(ring))
-		return NET_XMIT_DROP;
+	if (rt2x00_ring_full(ring)) {
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	entry = rt2x00_get_data_entry(ring);
 	txd = entry->desc_addr;
 
 	if (rt2x00_get_field32(txd->word0, TXD_W0_OWNER_NIC)
-	|| rt2x00_get_field32(txd->word0, TXD_W0_VALID))
-		return NET_XMIT_DROP;
+	|| rt2x00_get_field32(txd->word0, TXD_W0_VALID)) {
+		ERROR("Arrived at non-free entry in the non-full queue %d.\n"
+			"Please file bug report to %s.\n",
+			control->queue, DRV_PROJECT);
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	memcpy(entry->data_addr, skb->data, skb->len);
 	rt2400pci_write_tx_desc(rt2x00pci, txd, skb, control);
@@ -1560,6 +1584,9 @@ rt2400pci_tx(struct net_device *net_dev,
 
 	rt2x00_ring_index_inc(ring);
 
+	if (rt2x00_ring_full(ring))
+		ieee80211_stop_queue(net_dev, control->queue);
+
 	rt2x00_register_read(rt2x00pci, TXCSR0, &reg);
 	if (control->queue == IEEE80211_TX_QUEUE_DATA0)
 		rt2x00_set_field32(&reg, TXCSR0_KICK_PRIO, 1);
@@ -1569,7 +1596,7 @@ rt2400pci_tx(struct net_device *net_dev,
 		rt2x00_set_field32(&reg, TXCSR0_KICK_ATIM, 1);
 	rt2x00_register_write(rt2x00pci, TXCSR0, reg);
 
-	return 0;
+	return NETDEV_TX_OK;
 }
 
 static int
@@ -1669,6 +1696,8 @@ rt2400pci_open(struct net_device *net_de
 
 	SET_FLAG(rt2x00pci, RADIO_ENABLED);
 
+	ieee80211_start_queues(net_dev);
+
 	return 0;
 
 exit_fail:
diff --git a/drivers/net/wireless/d80211/rt2x00/rt2500pci.c b/drivers/net/wireless/d80211/rt2x00/rt2500pci.c
index 6aeaf1a..ca0edd5 100644
--- a/drivers/net/wireless/d80211/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/d80211/rt2x00/rt2500pci.c
@@ -1090,6 +1090,12 @@ rt2500pci_txdone(void *data)
 	struct txd		*txd;
 	int			tx_status;
 	int			ack;
+	int			ring_full;
+
+	/*
+	 * Store the current status of the ring.
+	 */
+	ring_full = rt2x00_ring_full(ring);
 
 	while (!rt2x00_ring_empty(ring)) {
 		entry = rt2x00_get_data_entry_done(ring);
@@ -1150,6 +1156,16 @@ rt2500pci_txdone(void *data)
 		rt2x00pci->scan->status = SCANNING_READY;
 		complete(&rt2x00pci->scan->completion);
 	}
+
+	/*
+	 * If the data ring was full before the txdone handler
+	 * we must make sure the packet queue in the d80211 stack
+	 * is reenabled when the txdone handler has finished.
+	 */
+	entry = ring->entry;
+	if (ring_full && !rt2x00_ring_full(ring))
+		ieee80211_wake_queue(ring->net_dev,
+			entry->tx_status.control.queue);
 }
 
 static irqreturn_t
@@ -1664,18 +1680,26 @@ rt2500pci_tx(struct net_device *net_dev,
 		ERROR("Attempt to send packet over invalid queue %d.\n"
 			"Please file bug report to %s.\n",
 			control->queue, DRV_PROJECT);
-		return NET_XMIT_DROP;
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_OK;
 	}
 
-	if (rt2x00_ring_full(ring))
-		return NET_XMIT_DROP;
+	if (rt2x00_ring_full(ring)) {
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	entry = rt2x00_get_data_entry(ring);
 	txd = entry->desc_addr;
 
 	if (rt2x00_get_field32(txd->word0, TXD_W0_OWNER_NIC)
-	|| rt2x00_get_field32(txd->word0, TXD_W0_VALID))
-		return NET_XMIT_DROP;
+	|| rt2x00_get_field32(txd->word0, TXD_W0_VALID)) {
+		ERROR("Arrived at non-free entry in the non-full queue %d.\n"
+			"Please file bug report to %s.\n",
+			control->queue, DRV_PROJECT);
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	memcpy(entry->data_addr, skb->data, skb->len);
 	rt2500pci_write_tx_desc(rt2x00pci, txd, skb, control);
@@ -1683,6 +1707,9 @@ rt2500pci_tx(struct net_device *net_dev,
 
 	rt2x00_ring_index_inc(ring);
 
+	if (rt2x00_ring_full(ring))
+		ieee80211_stop_queue(net_dev, control->queue);
+
 	rt2x00_register_read(rt2x00pci, TXCSR0, &reg);
 	if (control->queue == IEEE80211_TX_QUEUE_DATA0)
 		rt2x00_set_field32(&reg, TXCSR0_KICK_PRIO, 1);
@@ -1692,7 +1719,7 @@ rt2500pci_tx(struct net_device *net_dev,
 		rt2x00_set_field32(&reg, TXCSR0_KICK_ATIM, 1);
 	rt2x00_register_write(rt2x00pci, TXCSR0, reg);
 
-	return 0;
+	return NETDEV_TX_OK;
 }
 
 static int
@@ -1792,6 +1819,8 @@ rt2500pci_open(struct net_device *net_de
 
 	SET_FLAG(rt2x00pci, RADIO_ENABLED);
 
+	ieee80211_start_queues(net_dev);
+
 	return 0;
 
 exit_fail:
diff --git a/drivers/net/wireless/d80211/rt2x00/rt2500usb.c b/drivers/net/wireless/d80211/rt2x00/rt2500usb.c
index 1193e60..76c3a68 100644
--- a/drivers/net/wireless/d80211/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/d80211/rt2x00/rt2500usb.c
@@ -930,6 +930,12 @@ rt2500usb_txdone(void *data)
 	struct data_entry	*entry;
 	struct txd		*txd;
 	int			ack;
+	int			ring_full;
+
+	/*
+	 * Store the current status of the ring.
+	 */
+	ring_full = rt2x00_ring_full(ring);
 
 	 while (!rt2x00_ring_empty(ring)) {
 		entry = rt2x00_get_data_entry_done(ring);
@@ -982,6 +988,16 @@ rt2500usb_txdone(void *data)
 		rt2x00usb->scan->status = SCANNING_READY;
 		complete(&rt2x00usb->scan->completion);
 	}
+
+	/*
+	 * If the data ring was full before the txdone handler
+	 * we must make sure the packet queue in the d80211 stack
+	 * is reenabled when the txdone handler has finished.
+	 */
+	entry = ring->entry;
+	if (ring_full && !rt2x00_ring_full(ring))
+		ieee80211_wake_queue(ring->net_dev,
+			entry->tx_status.control.queue);
 }
 
 static void
@@ -1376,15 +1392,26 @@ rt2500usb_tx(struct net_device *net_dev,
 		ERROR("Attempt to send packet over invalid queue %d.\n"
 			"Please file bug report to %s.\n",
 			control->queue, DRV_PROJECT);
-		return NET_XMIT_DROP;
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_OK;
 	}
 
-	if (rt2x00_ring_full(ring))
-		return NET_XMIT_DROP;
+	if (rt2x00_ring_full(ring)) {
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	entry = rt2x00_get_data_entry(ring);
 	txd = rt2x00usb_txdesc_addr(entry);
 
+	if (GET_FLAG(entry, ENTRY_OWNER_NIC)) {
+		ERROR("Arrived at non-free entry in the non-full queue %d.\n"
+			"Please file bug report to %s.\n",
+			control->queue, DRV_PROJECT);
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
+
 	memcpy(rt2x00usb_txdata_addr(entry), skb->data, skb->len);
 	rt2500usb_write_tx_desc(rt2x00usb, txd, skb, control);
 	entry->skb = skb;
@@ -1402,7 +1429,10 @@ rt2500usb_tx(struct net_device *net_dev,
 
 	rt2x00_ring_index_inc(ring);
 
-	return 0;
+	if (rt2x00_ring_full(ring))
+		ieee80211_stop_queue(net_dev, control->queue);
+
+	return NETDEV_TX_OK;
 }
 
 static inline void
@@ -1469,6 +1499,8 @@ rt2500usb_open(struct net_device *net_de
 
 	SET_FLAG(rt2x00usb, RADIO_ENABLED);
 
+	ieee80211_start_queues(net_dev);
+
 	return 0;
 
 exit_fail:
diff --git a/drivers/net/wireless/d80211/rt2x00/rt61pci.c b/drivers/net/wireless/d80211/rt2x00/rt61pci.c
index fdbfa60..0799f9f 100644
--- a/drivers/net/wireless/d80211/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/d80211/rt2x00/rt61pci.c
@@ -1353,6 +1353,12 @@ rt61pci_txdone(void *data)
 	int			tx_status;
 	int			ack;
 	int			reg;
+	int			ring_full;
+
+	/*
+	 * Store the current status of the ring.
+	 */
+	ring_full = rt2x00_ring_full(ring);
 
 	while (!rt2x00_ring_empty(ring)) {
 		entry = rt2x00_get_data_entry_done(ring);
@@ -1418,6 +1424,16 @@ rt61pci_txdone(void *data)
 		rt2x00pci->scan->status = SCANNING_READY;
 		complete(&rt2x00pci->scan->completion);
 	}
+
+	/*
+	 * If the data ring was full before the txdone handler
+	 * we must make sure the packet queue in the d80211 stack
+	 * is reenabled when the txdone handler has finished.
+	 */
+	entry = ring->entry;
+	if (ring_full && !rt2x00_ring_full(ring))
+		ieee80211_wake_queue(ring->net_dev,
+			entry->tx_status.control.queue);
 }
 
 static irqreturn_t
@@ -2100,18 +2116,26 @@ rt61pci_tx(struct net_device *net_dev,
 		ERROR("Attempt to send packet over invalid queue %d.\n"
 			"Please file bug report to %s.\n",
 			control->queue, DRV_PROJECT);
-		return NET_XMIT_DROP;
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_OK;
 	}
 
-	if (rt2x00_ring_full(ring))
-		return NET_XMIT_DROP;
+	if (rt2x00_ring_full(ring)) {
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	entry = rt2x00_get_data_entry(ring);
 	txd = entry->desc_addr;
 
 	if (rt2x00_get_field32(txd->word0, TXD_W0_OWNER_NIC)
-	|| rt2x00_get_field32(txd->word0, TXD_W0_VALID))
-		return NET_XMIT_DROP;
+	|| rt2x00_get_field32(txd->word0, TXD_W0_VALID)) {
+		ERROR("Arrived at non-free entry in the non-full queue %d.\n"
+			"Please file bug report to %s.\n",
+			control->queue, DRV_PROJECT);
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	memcpy(entry->data_addr, skb->data, skb->len);
 	rt61pci_write_tx_desc(rt2x00pci, txd, skb, control);
@@ -2119,6 +2143,9 @@ rt61pci_tx(struct net_device *net_dev,
 
 	rt2x00_ring_index_inc(ring);
 
+	if (rt2x00_ring_full(ring))
+		ieee80211_stop_queue(net_dev, control->queue);
+
 	rt2x00_register_read(rt2x00pci, TX_CNTL_CSR, &reg);
 	if (control->queue == IEEE80211_TX_QUEUE_DATA0)
 		rt2x00_set_field32(&reg, TX_CNTL_CSR_KICK_TX_AC0, 1);
@@ -2132,7 +2159,7 @@ rt61pci_tx(struct net_device *net_dev,
 		rt2x00_set_field32(&reg, TX_CNTL_CSR_KICK_TX_MGMT, 1);
 	rt2x00_register_write(rt2x00pci, TX_CNTL_CSR, reg);
 
-	return 0;
+	return NETDEV_TX_OK;
 }
 
 static int
@@ -2249,6 +2276,8 @@ rt61pci_open(struct net_device *net_dev)
 
 	SET_FLAG(rt2x00pci, RADIO_ENABLED);
 
+	ieee80211_start_queues(net_dev);
+
 	return 0;
 
 exit_fail:
diff --git a/drivers/net/wireless/d80211/rt2x00/rt73usb.c b/drivers/net/wireless/d80211/rt2x00/rt73usb.c
index 48e9917..1871204 100644
--- a/drivers/net/wireless/d80211/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/d80211/rt2x00/rt73usb.c
@@ -1094,6 +1094,12 @@ rt73usb_txdone(void *data)
 	struct data_entry	*entry;
 	struct txd		*txd;
 	int			ack;
+	int			ring_full;
+
+	/*
+	 * Store the current status of the ring.
+	 */
+	ring_full = rt2x00_ring_full(ring);
 
 	while (!rt2x00_ring_empty(ring)) {
 		entry = rt2x00_get_data_entry_done(ring);
@@ -1148,6 +1154,16 @@ rt73usb_txdone(void *data)
 		rt2x00usb->scan->status = SCANNING_READY;
 		complete(&rt2x00usb->scan->completion);
 	}
+
+	/*
+	 * If the data ring was full before the txdone handler
+	 * we must make sure the packet queue in the d80211 stack
+	 * is reenabled when the txdone handler has finished.
+	 */
+	entry = ring->entry;
+	if (ring_full && !rt2x00_ring_full(ring))
+		ieee80211_wake_queue(ring->net_dev,
+			entry->tx_status.control.queue);
 }
 
 static void
@@ -1668,15 +1684,26 @@ rt73usb_tx(struct net_device *net_dev,
 		ERROR("Attempt to send packet over invalid queue %d.\n"
 			"Please file bug report to %s.\n",
 			control->queue, DRV_PROJECT);
-		return NET_XMIT_DROP;
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_OK;
 	}
 
-	if (rt2x00_ring_full(ring))
-		return NET_XMIT_DROP;
+	if (rt2x00_ring_full(ring)) {
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
 
 	entry = rt2x00_get_data_entry(ring);
 	txd = rt2x00usb_txdesc_addr(entry);
 
+	if (GET_FLAG(entry, ENTRY_OWNER_NIC)) {
+		ERROR("Arrived at non-free entry in the non-full queue %d.\n"
+			"Please file bug report to %s.\n",
+			control->queue, DRV_PROJECT);
+		ieee80211_stop_queue(net_dev, control->queue);
+		return NETDEV_TX_BUSY;
+	}
+
 	memcpy(rt2x00usb_txdata_addr(entry), skb->data, skb->len);
 	rt73usb_write_tx_desc(rt2x00usb, txd, skb, control);
 	entry->skb = skb;
@@ -1694,7 +1721,10 @@ rt73usb_tx(struct net_device *net_dev,
 
 	rt2x00_ring_index_inc(ring);
 
-	return 0;
+	if (rt2x00_ring_full(ring))
+		ieee80211_stop_queue(net_dev, control->queue);
+
+	return NETDEV_TX_OK;
 }
 
 static inline void
@@ -1766,6 +1796,8 @@ rt73usb_open(struct net_device *net_dev)
 
 	SET_FLAG(rt2x00usb, RADIO_ENABLED);
 
+	ieee80211_start_queues(net_dev);
+
 	return 0;
 
 exit_fail:

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 5/5] bcm43xx-d80211: fix sending of fragments
  2006-06-12 19:35   ` Michael Buesch
@ 2006-06-15 16:55     ` Michael Buesch
  2006-06-15 20:27       ` Jiri Benc
  0 siblings, 1 reply; 12+ messages in thread
From: Michael Buesch @ 2006-06-15 16:55 UTC (permalink / raw)
  To: John W. Linville; +Cc: Jiri Benc, netdev

On Monday 12 June 2006 21:35, Michael Buesch wrote:
> On Monday 12 June 2006 21:16, Jiri Benc wrote:
> > This makes fragmentation work with bcm43xx.
> > 
> > Signed-off-by: Jiri Benc <jbenc@suse.cz>
> 
> Signed-off-by: Michael Buesch <mb@bu3sch.de>
> 
> The other patch will get my sign-off tomorrow (I think modified, though).
> I don't have time to look at it more close, now.

Please also merge the other patch. It is too hard to figure out
the dependencies for me now. So simply merge it and I will submit
a fixup patch later, after you pushed it out.
The Subject was:
[incomplete 2/5] bcm43xx-d80211: per-queue TX flow control

It's not so much of trouble, if we break wireless-dev for one or
two days, so this is easiest.
Please notify me after you pushed the stuff to public repository,
so I can pull and fix it.

-- 
Greetings Michael.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 5/5] bcm43xx-d80211: fix sending of fragments
  2006-06-15 16:55     ` Michael Buesch
@ 2006-06-15 20:27       ` Jiri Benc
  2006-06-15 20:35         ` Michael Buesch
  0 siblings, 1 reply; 12+ messages in thread
From: Jiri Benc @ 2006-06-15 20:27 UTC (permalink / raw)
  To: Michael Buesch; +Cc: John W. Linville, netdev

Thu, 15 Jun 2006 18:55:11 +0200, Michael Buesch pise:
> Please also merge the other patch. It is too hard to figure out
> the dependencies for me now. So simply merge it and I will submit
> a fixup patch later, after you pushed it out.
> The Subject was:
> [incomplete 2/5] bcm43xx-d80211: per-queue TX flow control

There is no need to hurry. Let's wait with all my d80211 patches to next
week so people have enough time to go through them and comment. If there
are no objections by start of the next week, I will send a pull request.

Michael, I set up a 'devel' branch in my git repository for you
(git://git.kernel.org/pub/scm/linux/kernel/git/jbenc/dscape.git); it's
without the bcm43xx patch.

I'm going away now and won't be available till Monday.

> It's not so much of trouble, if we break wireless-dev for one or
> two days, so this is easiest.
> Please notify me after you pushed the stuff to public repository,
> so I can pull and fix it.


-- 
Jiri Benc
SUSE Labs

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 5/5] bcm43xx-d80211: fix sending of fragments
  2006-06-15 20:27       ` Jiri Benc
@ 2006-06-15 20:35         ` Michael Buesch
  0 siblings, 0 replies; 12+ messages in thread
From: Michael Buesch @ 2006-06-15 20:35 UTC (permalink / raw)
  To: Jiri Benc; +Cc: John W. Linville, netdev

On Thursday 15 June 2006 22:27, Jiri Benc wrote:
> Thu, 15 Jun 2006 18:55:11 +0200, Michael Buesch pise:
> > Please also merge the other patch. It is too hard to figure out
> > the dependencies for me now. So simply merge it and I will submit
> > a fixup patch later, after you pushed it out.
> > The Subject was:
> > [incomplete 2/5] bcm43xx-d80211: per-queue TX flow control
> 
> There is no need to hurry. Let's wait with all my d80211 patches to next
> week so people have enough time to go through them and comment. If there
> are no objections by start of the next week, I will send a pull request.
> 
> Michael, I set up a 'devel' branch in my git repository for you
> (git://git.kernel.org/pub/scm/linux/kernel/git/jbenc/dscape.git); it's
> without the bcm43xx patch.

Oh, nice. Thanks. I will cook a patch against it.

> I'm going away now and won't be available till Monday.
> 
> > It's not so much of trouble, if we break wireless-dev for one or
> > two days, so this is easiest.
> > Please notify me after you pushed the stuff to public repository,
> > so I can pull and fix it.
> 
> 

-- 
Greetings Michael.

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2006-06-15 20:36 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-06-12 19:16 [PATCH 0/5] d80211: better fragmentation handling Jiri Benc
2006-06-12 19:16 ` [PATCH 1/5] d80211: per-queue TX flow control Jiri Benc
2006-06-12 19:16 ` [incomplete 2/5] bcm43xx-d80211: " Jiri Benc
2006-06-12 19:16 ` [PATCH 3/5] d80211: handle full queue when sending fragments Jiri Benc
2006-06-12 19:16 ` [PATCH 4/5] d80211: add first_fragment flag to ieee80211_tx_control Jiri Benc
2006-06-12 19:16 ` [PATCH 5/5] bcm43xx-d80211: fix sending of fragments Jiri Benc
2006-06-12 19:35   ` Michael Buesch
2006-06-15 16:55     ` Michael Buesch
2006-06-15 20:27       ` Jiri Benc
2006-06-15 20:35         ` Michael Buesch
2006-06-14 16:36 ` [PATCH 6/5] rt2x00: per-queue TX flow control Jiri Benc
2006-06-14 18:35   ` Ivo van Doorn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).