This patchset adds support for hardware TX fragmentation offload
to mac80211 based drivers. A callback is added to ieee80211_ops
to receive fragmentation threshold updates.
The added functionality is very similar to the one removed by
patch f546638c3f80.
This patchset is required for wl1271 based cards. The firmware
does not support fragmentation in software and in fact clears
the MOREFRAGS flag for TX packets.
Arik Nemtsov (4):
mac80211: add fragmentation offload definitions
mac80211: notify drivers about frag threshold changes
mac80211: don't fragment packets when HW-fragmentation is on
wl1271: add support for HW TX fragmentation
drivers/net/wireless/wl12xx/wl1271_acx.c | 4 +-
drivers/net/wireless/wl12xx/wl1271_acx.h | 2 +-
drivers/net/wireless/wl12xx/wl1271_init.c | 2 +-
drivers/net/wireless/wl12xx/wl1271_main.c | 34 +++++++++++++++++++++++++++-
include/net/mac80211.h | 12 ++++++++++
net/mac80211/cfg.c | 7 ++++++
net/mac80211/driver-ops.h | 14 ++++++++++++
net/mac80211/driver-trace.h | 21 +++++++++++++++++
net/mac80211/tx.c | 8 +++++-
net/mac80211/util.c | 3 ++
10 files changed, 99 insertions(+), 8 deletions(-)
On Thu, 2010-11-04 at 06:34 +0100, ext Arik Nemtsov wrote:
> On Thu, Nov 4, 2010 at 04:15, Johannes Berg <[email protected]> wrote:
> >
> > On Wed, 2010-11-03 at 23:50 +0200, Arik Nemtsov wrote:
> > > If the driver supports hardware TX fragmentation, don't fragment
> > > packets in the stack.
> >
> > I'm not sure why you have three patches? Seems like it could all be a
> > single mac80211 patch.
>
> Sure. I'll send a v2 with the mac80211 patches bundled into a single one.
>
> >
> > > @@ -1181,8 +1183,10 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
> > > /*
> > > * Set this flag (used below to indicate "automatic fragmentation"),
> > > * it will be cleared/left by radiotap as desired.
> > > + * Only valid when fragmentation is done by the stack.
> > > */
> > > - tx->flags |= IEEE80211_TX_FRAGMENTED;
> > > + if (!(local->hw.flags & IEEE80211_HW_TX_FRAGMENTATION))
> > > + tx->flags |= IEEE80211_TX_FRAGMENTED;
> >
> > Do we really need the hw flag? Couldn't we go off the callback, like we
> > used to? I'm not really sure which one would perform better though, I
> > guess the flag might ...
>
> I think the flag may be less confusing for new driver writers.
> Someone can implement the callback as a no-op and have a bug in his
> code as a result.
Well, if someone implements the fragmentation setting function as a
no-op, it is a bug in the driver and it should be fixed. It's easy to
make that clear in the documentation. "If the hardware doesn't support
fragmentation, set this function to NULL" or something.
That's how it's done already with other ops, such as hw_scan and so on.
I don't see a need to have another flag.
Performance-wise, if there's an impact it will be almost insignificant,
I believe.
--
Cheers,
Luca.
Indicate to mac80211 we support HW fragmentation.
Support updates of the fragmentation threshold via the
set_frag_threshold callback.
Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271_acx.c | 4 +-
drivers/net/wireless/wl12xx/wl1271_acx.h | 2 +-
drivers/net/wireless/wl12xx/wl1271_init.c | 2 +-
drivers/net/wireless/wl12xx/wl1271_main.c | 34 +++++++++++++++++++++++++++-
4 files changed, 36 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c
index bd7f95f..bbaf066 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.c
@@ -862,7 +862,7 @@ out:
return ret;
}
-int wl1271_acx_frag_threshold(struct wl1271 *wl)
+int wl1271_acx_frag_threshold(struct wl1271 *wl, u16 frag_threshold)
{
struct acx_frag_threshold *acx;
int ret = 0;
@@ -876,7 +876,7 @@ int wl1271_acx_frag_threshold(struct wl1271 *wl)
goto out;
}
- acx->frag_threshold = cpu_to_le16(wl->conf.tx.frag_threshold);
+ acx->frag_threshold = cpu_to_le16(frag_threshold);
ret = wl1271_cmd_configure(wl, ACX_FRAG_CFG, acx, sizeof(*acx));
if (ret < 0) {
wl1271_warning("Setting of frag threshold failed: %d", ret);
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h
index b7c4908..5963dea 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.h
@@ -1161,7 +1161,7 @@ int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max,
int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type,
u8 tsid, u8 ps_scheme, u8 ack_policy,
u32 apsd_conf0, u32 apsd_conf1);
-int wl1271_acx_frag_threshold(struct wl1271 *wl);
+int wl1271_acx_frag_threshold(struct wl1271 *wl, u16 frag_threshold);
int wl1271_acx_tx_config_options(struct wl1271 *wl);
int wl1271_acx_mem_cfg(struct wl1271 *wl);
int wl1271_acx_init_mem_config(struct wl1271 *wl);
diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c
index 8044bba..3ba7acd 100644
--- a/drivers/net/wireless/wl12xx/wl1271_init.c
+++ b/drivers/net/wireless/wl12xx/wl1271_init.c
@@ -290,7 +290,7 @@ int wl1271_hw_init(struct wl1271 *wl)
goto out_free_memmap;
/* Default fragmentation threshold */
- ret = wl1271_acx_frag_threshold(wl);
+ ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold);
if (ret < 0)
goto out_free_memmap;
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index f5b1d19..1758e2e 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -404,7 +404,7 @@ static int wl1271_plt_init(struct wl1271 *wl)
goto out_free_memmap;
/* Default fragmentation threshold */
- ret = wl1271_acx_frag_threshold(wl);
+ ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold);
if (ret < 0)
goto out_free_memmap;
@@ -1724,6 +1724,34 @@ out:
return ret;
}
+static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
+{
+ struct wl1271 *wl = hw->priv;
+ int ret = 0;
+
+ mutex_lock(&wl->mutex);
+
+ if (unlikely(wl->state == WL1271_STATE_OFF)) {
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ ret = wl1271_ps_elp_wakeup(wl, false);
+ if (ret < 0)
+ goto out;
+
+ ret = wl1271_acx_frag_threshold(wl, (u16)value);
+ if (ret < 0)
+ wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret);
+
+ wl1271_ps_elp_sleep(wl);
+
+out:
+ mutex_unlock(&wl->mutex);
+
+ return ret;
+}
+
static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
{
struct wl1271 *wl = hw->priv;
@@ -2406,6 +2434,7 @@ static const struct ieee80211_ops wl1271_ops = {
.set_key = wl1271_op_set_key,
.hw_scan = wl1271_op_hw_scan,
.bss_info_changed = wl1271_op_bss_info_changed,
+ .set_frag_threshold = wl1271_op_set_frag_threshold,
.set_rts_threshold = wl1271_op_set_rts_threshold,
.conf_tx = wl1271_op_conf_tx,
.get_tsf = wl1271_op_get_tsf,
@@ -2576,7 +2605,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
IEEE80211_HW_SUPPORTS_UAPSD |
IEEE80211_HW_HAS_RATE_CONTROL |
IEEE80211_HW_CONNECTION_MONITOR |
- IEEE80211_HW_SUPPORTS_CQM_RSSI;
+ IEEE80211_HW_SUPPORTS_CQM_RSSI |
+ IEEE80211_HW_TX_FRAGMENTATION;
wl->hw->wiphy->cipher_suites = cipher_suites;
wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
--
1.7.1
If the driver supports hardware TX fragmentation, don't fragment
packets in the stack.
Signed-off-by: Arik Nemtsov <[email protected]>
---
net/mac80211/tx.c | 8 ++++++--
1 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 96c5943..b7d38c5 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1033,6 +1033,7 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
struct ieee80211_radiotap_header *rthdr =
(struct ieee80211_radiotap_header *) skb->data;
struct ieee80211_supported_band *sband;
+ u32 hw_flags = tx->local->hw.flags;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len,
NULL);
@@ -1078,7 +1079,8 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
}
if (*iterator.this_arg & IEEE80211_RADIOTAP_F_WEP)
info->flags &= ~IEEE80211_TX_INTFL_DONT_ENCRYPT;
- if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG)
+ if ((*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG) &&
+ !(hw_flags & IEEE80211_HW_TX_FRAGMENTATION))
tx->flags |= IEEE80211_TX_FRAGMENTED;
break;
@@ -1181,8 +1183,10 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
/*
* Set this flag (used below to indicate "automatic fragmentation"),
* it will be cleared/left by radiotap as desired.
+ * Only valid when fragmentation is done by the stack.
*/
- tx->flags |= IEEE80211_TX_FRAGMENTED;
+ if (!(local->hw.flags & IEEE80211_HW_TX_FRAGMENTATION))
+ tx->flags |= IEEE80211_TX_FRAGMENTED;
/* process and remove the injection radiotap header */
if (unlikely(info->flags & IEEE80211_TX_INTFL_HAS_RADIOTAP)) {
--
1.7.1
On Thu, Nov 4, 2010 at 04:15, Johannes Berg <[email protected]> wrote:
>
> On Wed, 2010-11-03 at 23:50 +0200, Arik Nemtsov wrote:
> > If the driver supports hardware TX fragmentation, don't fragment
> > packets in the stack.
>
> I'm not sure why you have three patches? Seems like it could all be a
> single mac80211 patch.
Sure. I'll send a v2 with the mac80211 patches bundled into a single one.
>
> > @@ -1181,8 +1183,10 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
> > ? ? ? /*
> > ? ? ? ?* Set this flag (used below to indicate "automatic fragmentation"),
> > ? ? ? ?* it will be cleared/left by radiotap as desired.
> > + ? ? ?* Only valid when fragmentation is done by the stack.
> > ? ? ? ?*/
> > - ? ? tx->flags |= IEEE80211_TX_FRAGMENTED;
> > + ? ? if (!(local->hw.flags & IEEE80211_HW_TX_FRAGMENTATION))
> > + ? ? ? ? ? ? tx->flags |= IEEE80211_TX_FRAGMENTED;
>
> Do we really need the hw flag? Couldn't we go off the callback, like we
> used to? I'm not really sure which one would perform better though, I
> guess the flag might ...
I think the flag may be less confusing for new driver writers.
Someone can implement the callback as a no-op and have a bug in his
code as a result.
Regards,
Arik
On Wed, 2010-11-03 at 23:50 +0200, Arik Nemtsov wrote:
> If the driver supports hardware TX fragmentation, don't fragment
> packets in the stack.
I'm not sure why you have three patches? Seems like it could all be a
single mac80211 patch.
> @@ -1181,8 +1183,10 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
> /*
> * Set this flag (used below to indicate "automatic fragmentation"),
> * it will be cleared/left by radiotap as desired.
> + * Only valid when fragmentation is done by the stack.
> */
> - tx->flags |= IEEE80211_TX_FRAGMENTED;
> + if (!(local->hw.flags & IEEE80211_HW_TX_FRAGMENTATION))
> + tx->flags |= IEEE80211_TX_FRAGMENTED;
Do we really need the hw flag? Couldn't we go off the callback, like we
used to? I'm not really sure which one would perform better though, I
guess the flag might ...
johannes
Define a flag to indicate hardware TX fragmentation and a callback in
mac80211_ops to update the fragmentation threshold in the driver.
Signed-off-by: Arik Nemtsov <[email protected]>
---
include/net/mac80211.h | 12 ++++++++++++
1 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 9fdf982..25ced5c 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1048,6 +1048,11 @@ enum ieee80211_tkip_key_type {
* to decrypt group addressed frames, then IBSS RSN support is still
* possible but software crypto will be used. Advertise the wiphy flag
* only in that case.
+ *
+ * @IEEE80211_HW_TX_FRAGMENTATION: Packet fragmentation is done in hardware.
+ * The stack does not fragment packets in this case. The driver must
+ * update its internal fragmentation threshold by implementing the
+ * set_frag_threshold callback.
*/
enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
@@ -1072,6 +1077,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_CONNECTION_MONITOR = 1<<19,
IEEE80211_HW_SUPPORTS_CQM_RSSI = 1<<20,
IEEE80211_HW_SUPPORTS_PER_STA_GTK = 1<<21,
+ IEEE80211_HW_TX_FRAGMENTATION = 1<<22,
};
/**
@@ -1652,6 +1658,11 @@ enum ieee80211_ampdu_mlme_action {
* and IV16) for the given key from hardware.
* The callback must be atomic.
*
+ * @set_frag_threshold: Configuration of fragmentation threshold. Assign this if
+ * the device does fragmentation by itself; if this method is assigned then
+ * the stack will not do fragmentation.
+ * The callback can sleep.
+ *
* @set_rts_threshold: Configuration of RTS threshold (if device needs it)
* The callback can sleep.
*
@@ -1765,6 +1776,7 @@ struct ieee80211_ops {
struct ieee80211_low_level_stats *stats);
void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx,
u32 *iv32, u16 *iv16);
+ int (*set_frag_threshold)(struct ieee80211_hw *hw, u32 value);
int (*set_rts_threshold)(struct ieee80211_hw *hw, u32 value);
int (*sta_add)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
--
1.7.1
The lower driver is notified when the fragmentation threshold changes
and upon a reconfig of the interface.
Signed-off-by: Arik Nemtsov <[email protected]>
---
net/mac80211/cfg.c | 7 +++++++
net/mac80211/driver-ops.h | 14 ++++++++++++++
net/mac80211/driver-trace.h | 21 +++++++++++++++++++++
net/mac80211/util.c | 3 +++
4 files changed, 45 insertions(+), 0 deletions(-)
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 18bd0e5..3df12f7 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1299,6 +1299,13 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
struct ieee80211_local *local = wiphy_priv(wiphy);
int err;
+ if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
+ err = drv_set_frag_threshold(local, wiphy->frag_threshold);
+
+ if (err)
+ return err;
+ }
+
if (changed & WIPHY_PARAM_COVERAGE_CLASS) {
err = drv_set_coverage_class(local, wiphy->coverage_class);
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 1698382..79019f9 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -233,6 +233,20 @@ static inline void drv_get_tkip_seq(struct ieee80211_local *local,
trace_drv_get_tkip_seq(local, hw_key_idx, iv32, iv16);
}
+static inline int drv_set_frag_threshold(struct ieee80211_local *local,
+ u32 value)
+{
+ int ret = 0;
+
+ might_sleep();
+
+ trace_drv_set_frag_threshold(local, value);
+ if (local->ops->set_frag_threshold)
+ ret = local->ops->set_frag_threshold(&local->hw, value);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
static inline int drv_set_rts_threshold(struct ieee80211_local *local,
u32 value)
{
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index 6831fb1..431d655 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -531,6 +531,27 @@ TRACE_EVENT(drv_get_tkip_seq,
)
);
+TRACE_EVENT(drv_set_frag_threshold,
+ TP_PROTO(struct ieee80211_local *local, u32 value),
+
+ TP_ARGS(local, value),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(u32, value)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->value = value;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " value:%d",
+ LOCAL_PR_ARG, __entry->value
+ )
+);
+
TRACE_EVENT(drv_set_rts_threshold,
TP_PROTO(struct ieee80211_local *local, u32 value),
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 0b6fc92..e486286 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1152,6 +1152,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
}
mutex_unlock(&local->sta_mtx);
+ /* setup fragmentation threshold */
+ drv_set_frag_threshold(local, hw->wiphy->frag_threshold);
+
/* setup RTS threshold */
drv_set_rts_threshold(local, hw->wiphy->rts_threshold);
--
1.7.1