* [PATCH 0/5] d80211: pull request - fragmentation, fixes
@ 2006-06-23 13:01 Jiri Benc
2006-06-23 13:01 ` [PATCH 1/5] d80211: update tx.skb after TX handler calls Jiri Benc
` (4 more replies)
0 siblings, 5 replies; 7+ messages in thread
From: Jiri Benc @ 2006-06-23 13:01 UTC (permalink / raw)
To: John W. Linville; +Cc: netdev
Please pull from
git://git.kernel.org/pub/scm/linux/kernel/git/jbenc/dscape.git master
to obtain following patches:
Jiri Benc:
d80211: update tx.skb after TX handler calls
d80211: per-queue TX flow control
d80211: handle full queue when sending fragments
d80211: add first_fragment flag to ieee80211_tx_control
Michael Buesch:
d80211: allow NULL for control in beacon_get
include/net/d80211.h | 32 +++++
net/d80211/ieee80211.c | 293 ++++++++++++++++++++++++++++++++++++----------
net/d80211/ieee80211_i.h | 20 +++
net/d80211/wme.c | 9 +
4 files changed, 285 insertions(+), 69 deletions(-)
--
Jiri Benc
SUSE Labs
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH 1/5] d80211: update tx.skb after TX handler calls 2006-06-23 13:01 [PATCH 0/5] d80211: pull request - fragmentation, fixes Jiri Benc @ 2006-06-23 13:01 ` Jiri Benc 2006-06-23 13:01 ` [PATCH 2/5] d80211: per-queue TX flow control Jiri Benc ` (3 subsequent siblings) 4 siblings, 0 replies; 7+ messages in thread From: Jiri Benc @ 2006-06-23 13:01 UTC (permalink / raw) To: John W. Linville; +Cc: netdev TX and RX handlers are allowed to change skb. Fix (hopefully) the last place where this is not taken into account. This is similar to e81b1bc0f272a50458ab6ae8777f6327af0248e5. Signed-off-by: Jiri Benc <jbenc@suse.cz> --- net/d80211/ieee80211.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) 9372ebe184ff953be4f9d05b029477a57dd43bbd diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c index a8ca8d1..944b89f 100644 --- a/net/d80211/ieee80211.c +++ b/net/d80211/ieee80211.c @@ -1760,6 +1760,7 @@ ieee80211_get_buffered_bc(struct net_dev break; } dev_put(tx.dev); + skb = tx.skb; /* handlers are allowed to change skb */ if (res == TXRX_DROP) { I802_DEBUG_INC(local->tx_handlers_drop); -- 1.3.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/5] d80211: per-queue TX flow control 2006-06-23 13:01 [PATCH 0/5] d80211: pull request - fragmentation, fixes Jiri Benc 2006-06-23 13:01 ` [PATCH 1/5] d80211: update tx.skb after TX handler calls Jiri Benc @ 2006-06-23 13:01 ` Jiri Benc 2006-06-23 13:01 ` [PATCH 3/5] d80211: handle full queue when sending fragments Jiri Benc ` (2 subsequent siblings) 4 siblings, 0 replies; 7+ messages in thread From: Jiri Benc @ 2006-06-23 13:01 UTC (permalink / raw) To: John W. Linville; +Cc: netdev 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(-) 39ee8c857aff2f97fb8c27cee2ee9001833f5a2b diff --git a/include/net/d80211.h b/include/net/d80211.h index 4bdbdbe..141f776 100644 --- a/include/net/d80211.h +++ b/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(). diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c index 944b89f..65f32a8 100644 --- a/net/d80211/ieee80211.c +++ b/net/d80211/ieee80211.c @@ -4421,6 +4421,30 @@ #endif 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); diff --git a/net/d80211/ieee80211_i.h b/net/d80211/ieee80211_i.h index c1d7422..722860c 100644 --- a/net/d80211/ieee80211_i.h +++ b/net/d80211/ieee80211_i.h @@ -353,6 +353,8 @@ #define IEEE80211_IRQSAFE_QUEUE_LIMIT 12 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 @@ #endif /* CONFIG_D80211_DEBUG_COUNTERS * 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); diff --git a/net/d80211/wme.c b/net/d80211/wme.c index 138f892..87437cc 100644 --- a/net/d80211/wme.c +++ b/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 */ -- 1.3.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 3/5] d80211: handle full queue when sending fragments 2006-06-23 13:01 [PATCH 0/5] d80211: pull request - fragmentation, fixes Jiri Benc 2006-06-23 13:01 ` [PATCH 1/5] d80211: update tx.skb after TX handler calls Jiri Benc 2006-06-23 13:01 ` [PATCH 2/5] d80211: per-queue TX flow control Jiri Benc @ 2006-06-23 13:01 ` Jiri Benc 2006-06-23 13:01 ` [PATCH 4/5] d80211: add first_fragment flag to ieee80211_tx_control Jiri Benc 2006-06-23 13:01 ` [PATCH 5/5] d80211: allow NULL for control in beacon_get Jiri Benc 4 siblings, 0 replies; 7+ messages in thread From: Jiri Benc @ 2006-06-23 13:01 UTC (permalink / raw) To: John W. Linville; +Cc: netdev 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 | 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_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; +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; } - kfree(tx.u.tx.extra_frag); - } - if (ret == -1) - ret = 0; - return ret; + 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 @@ #endif /* IEEE80211_LEDS */ 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) diff --git a/net/d80211/ieee80211_i.h b/net/d80211/ieee80211_i.h index 722860c..0409c52 100644 --- a/net/d80211/ieee80211_i.h +++ b/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 @@ #define IEEE80211_IRQSAFE_QUEUE_LIMIT 12 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 @@ #endif /* CONFIG_D80211_DEBUG_COUNTERS * enum ieee80211_link_state_t { IEEE80211_LINK_STATE_XOFF = 0, + IEEE80211_LINK_STATE_PENDING, }; struct sta_attribute { diff --git a/net/d80211/wme.c b/net/d80211/wme.c index 87437cc..76de062 100644 --- a/net/d80211/wme.c +++ b/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 */ -- 1.3.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 4/5] d80211: add first_fragment flag to ieee80211_tx_control 2006-06-23 13:01 [PATCH 0/5] d80211: pull request - fragmentation, fixes Jiri Benc ` (2 preceding siblings ...) 2006-06-23 13:01 ` [PATCH 3/5] d80211: handle full queue when sending fragments Jiri Benc @ 2006-06-23 13:01 ` Jiri Benc 2006-06-23 13:01 ` [PATCH 5/5] d80211: allow NULL for control in beacon_get Jiri Benc 4 siblings, 0 replies; 7+ messages in thread From: Jiri Benc @ 2006-06-23 13:01 UTC (permalink / raw) To: John W. Linville; +Cc: netdev 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(+), 0 deletions(-) e360f2605cbb71f359de21a59eff1bcbab150e7f diff --git a/include/net/d80211.h b/include/net/d80211.h index 141f776..6ae4325 100644 --- a/include/net/d80211.h +++ b/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 diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c index 0f01311..9fc3518 100644 --- a/net/d80211/ieee80211.c +++ b/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 @@ #endif /* IEEE80211_LEDS */ 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; -- 1.3.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 5/5] d80211: allow NULL for control in beacon_get 2006-06-23 13:01 [PATCH 0/5] d80211: pull request - fragmentation, fixes Jiri Benc ` (3 preceding siblings ...) 2006-06-23 13:01 ` [PATCH 4/5] d80211: add first_fragment flag to ieee80211_tx_control Jiri Benc @ 2006-06-23 13:01 ` Jiri Benc 4 siblings, 0 replies; 7+ messages in thread From: Jiri Benc @ 2006-06-23 13:01 UTC (permalink / raw) To: John W. Linville; +Cc: netdev From: Michael Buesch <mb@bu3sch.de> bcm43xx has no use for the "control" data provided by ieee80211_beacon_get(), so allow passing a NULL pointer to avoid setting up a dummy struct and throwing the data away afterwards in the driver. Signed-off-by: Michael Buesch <mb@bu3sch.de> Signed-off-by: Jiri Benc <jbenc@suse.cz> --- net/d80211/ieee80211.c | 40 +++++++++++++++++++++------------------- 1 files changed, 21 insertions(+), 19 deletions(-) 634f9d6f4bbafbcf218ac303087a0e83c5c95cda diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c index 9fc3518..ffb0f18 100644 --- a/net/d80211/ieee80211.c +++ b/net/d80211/ieee80211.c @@ -1806,28 +1806,30 @@ #endif /* CONFIG_D80211_VERBOSE_DEBUG */ memcpy(skb_put(skb, bt_len), b_tail, bt_len); } - memset(&extra, 0, sizeof(extra)); - extra.endidx = local->num_curr_rates; + if (control) { + memset(&extra, 0, sizeof(extra)); + extra.endidx = local->num_curr_rates; - rate = rate_control_get_rate(dev, skb, &extra); - if (rate == NULL) { - if (net_ratelimit()) { - printk(KERN_DEBUG "%s: ieee80211_beacon_get: no rate " - "found\n", dev->name); + rate = rate_control_get_rate(dev, skb, &extra); + if (rate == NULL) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: ieee80211_beacon_get: no rate " + "found\n", dev->name); + } + dev_kfree_skb(skb); + return NULL; } - dev_kfree_skb(skb); - return NULL; - } - control->tx_rate = (local->short_preamble && - (rate->flags & IEEE80211_RATE_PREAMBLE2)) ? - rate->val2 : rate->val; - control->antenna_sel = local->conf.antenna_sel; - control->power_level = local->conf.power_level; - control->no_ack = 1; - control->retry_limit = 1; - control->rts_cts_duration = 0; - control->clear_dst_mask = 1; + control->tx_rate = (local->short_preamble && + (rate->flags & IEEE80211_RATE_PREAMBLE2)) ? + rate->val2 : rate->val; + control->antenna_sel = local->conf.antenna_sel; + control->power_level = local->conf.power_level; + control->no_ack = 1; + control->retry_limit = 1; + control->rts_cts_duration = 0; + control->clear_dst_mask = 1; + } ap->num_beacons++; return skb; -- 1.3.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 0/5] d80211: better fragmentation handling @ 2006-06-12 19:16 Jiri Benc 2006-06-12 19:16 ` [PATCH 3/5] d80211: handle full queue when sending fragments Jiri Benc 0 siblings, 1 reply; 7+ 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] 7+ 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 ` Jiri Benc 0 siblings, 0 replies; 7+ 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] 7+ messages in thread
end of thread, other threads:[~2006-06-23 12:59 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2006-06-23 13:01 [PATCH 0/5] d80211: pull request - fragmentation, fixes Jiri Benc 2006-06-23 13:01 ` [PATCH 1/5] d80211: update tx.skb after TX handler calls Jiri Benc 2006-06-23 13:01 ` [PATCH 2/5] d80211: per-queue TX flow control Jiri Benc 2006-06-23 13:01 ` [PATCH 3/5] d80211: handle full queue when sending fragments Jiri Benc 2006-06-23 13:01 ` [PATCH 4/5] d80211: add first_fragment flag to ieee80211_tx_control Jiri Benc 2006-06-23 13:01 ` [PATCH 5/5] d80211: allow NULL for control in beacon_get Jiri Benc -- strict thread matches above, loose matches on Subject: below -- 2006-06-12 19:16 [PATCH 0/5] d80211: better fragmentation handling Jiri Benc 2006-06-12 19:16 ` [PATCH 3/5] d80211: handle full queue when sending fragments Jiri Benc
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox