2019-04-01 13:14:41

by John Crispin

[permalink] [raw]
Subject: [RFC V3 0/2] mac80211: add 802.11 encapsulation offloading

This series picks up prior work from QCA. The patch is currently shipped
inside QSDK as part of the wlan-open package.

The series tries to address all feedback from the V2
-> https://patchwork.kernel.org/patch/9602585/

Changes in V3
* rebase on latest kernel
* various code style clean ups
* give so eof the variables and functions more obvious names
* move the code that disables support for non linear frames to the core
* disable monitor and tkip support

Vasanthakumar Thiagarajan (2):
mac80211: add hw 80211 encapsulation offloading support
ath10k: add tx hw 802.11 encapusaltion offloading support

drivers/net/wireless/ath/ath10k/core.c | 11 ++
drivers/net/wireless/ath/ath10k/core.h | 3 +
drivers/net/wireless/ath/ath10k/htt_tx.c | 24 ++--
drivers/net/wireless/ath/ath10k/mac.c | 78 ++++++++++---
drivers/net/wireless/ath/ath10k/txrx.c | 11 +-
include/net/mac80211.h | 25 ++++
net/mac80211/cfg.c | 12 +-
net/mac80211/debugfs.c | 1 +
net/mac80211/ieee80211_i.h | 10 ++
net/mac80211/iface.c | 54 +++++++++
net/mac80211/key.c | 3 +
net/mac80211/main.c | 10 +-
net/mac80211/status.c | 79 +++++++++++++
net/mac80211/tx.c | 188 ++++++++++++++++++++++++++++++-
14 files changed, 475 insertions(+), 34 deletions(-)

--
2.11.0



2019-04-01 13:14:44

by John Crispin

[permalink] [raw]
Subject: [RFC V3 1/2] mac80211: add hw 80211 encapsulation offloading support

From: Vasanthakumar Thiagarajan <[email protected]>

This patch adds a new transmit path for hardware that supports 802.11
encapsulation offloading. In those cases 802.3a frames get passed
directly to the driver allowing to hardware to handle the encapsulation.

Certain features wont work and the patch masks these out.
* monitor interfaces are not supported if any of the vif is in encap mode.
* amsdu/non-linear frames wont work in encap offloading mode.
* TKIP countermeasures cannot be triggered and hence those keys are not
accepted.

The patch defines a secondary netdev_ops struct that the device is assigned
to the device if 802.11 encap support is available and enabled. The driver
needs to enable the support on a per vif basis if it finds that all
pre-reqs are meet.

Signed-off-by: Vasanthakumar Thiagarajan <[email protected]>
Signed-off-by: John Crispin <[email protected]>
---
include/net/mac80211.h | 25 ++++++
net/mac80211/cfg.c | 12 ++-
net/mac80211/debugfs.c | 1 +
net/mac80211/ieee80211_i.h | 10 +++
net/mac80211/iface.c | 54 +++++++++++++
net/mac80211/key.c | 3 +
net/mac80211/main.c | 10 ++-
net/mac80211/status.c | 79 +++++++++++++++++++
net/mac80211/tx.c | 188 +++++++++++++++++++++++++++++++++++++++++++--
9 files changed, 373 insertions(+), 9 deletions(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index ac2ed8ec662b..3e8929770839 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1021,6 +1021,7 @@ struct ieee80211_tx_info {
struct ieee80211_key_conf *hw_key;
u32 flags;
codel_time_t enqueue_time;
+ u8 hw_80211_encap;
} control;
struct {
u64 cookie;
@@ -2243,6 +2244,9 @@ struct ieee80211_txq {
* @IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID: Hardware supports multi BSSID
* only for HE APs. Applies if @IEEE80211_HW_SUPPORTS_MULTI_BSSID is set.
*
+ * @IEEE80211_HW_SUPPORTS_80211_ENCAP: Hardware/driver supports 802.11
+ * encap for data frames.
+ *
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/
enum ieee80211_hw_flags {
@@ -2294,6 +2298,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN,
IEEE80211_HW_SUPPORTS_MULTI_BSSID,
IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
+ IEEE80211_HW_SUPPORTS_80211_ENCAP,

/* keep last, obviously */
NUM_IEEE80211_HW_FLAGS
@@ -4587,6 +4592,25 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
struct sk_buff *skb);

/**
+ * ieee80211_tx_status_8023 - transmit status callback for 802.3 frame format
+ *
+ * Call this function for all transmitted data frames after their transmit
+ * completion. This callback should only be called for data frames which
+ * are are using driver's (or hardware's) offload capability of encap/decap
+ * 802.11 frames.
+ *
+ * This function may not be called in IRQ context. Calls to this function
+ * for a single hardware must be synchronized against each other.
+ *
+ * @hw: the hardware the frame was transmitted by
+ * @vif: the interface for which the frame was transmitted
+ * @skb: the frame that was transmitted, owned by mac80211 after this call
+ */
+void ieee80211_tx_status_8023(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct sk_buff *skb);
+
+/**
* ieee80211_report_low_ack - report non-responding station
*
* When operating in AP-mode, call this function to report a non-responding
@@ -6359,4 +6383,5 @@ void ieee80211_nan_func_match(struct ieee80211_vif *vif,
struct cfg80211_nan_match_params *match,
gfp_t gfp);

+void ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable);
#endif /* MAC80211_H */
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 09dd1c2860fc..53b56f8fcdfc 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -367,8 +367,15 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,

/* reject WEP and TKIP keys if WEP failed to initialize */
switch (params->cipher) {
- case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_TKIP:
+ /* countermeasures wont work on encap offload mode so reject
+ * TKIP keys
+ */
+ if (ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP))
+ return -EINVAL;
+
+ /* drop through */
+ case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
if (IS_ERR(local->wep_tx_tfm))
return -EINVAL;
@@ -2379,6 +2386,9 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
ieee80211_check_fast_xmit_all(local);

+ if (ieee80211_is_hw_80211_encap(local))
+ return -EINVAL;
+
err = drv_set_frag_threshold(local, wiphy->frag_threshold);

if (err) {
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 2d43bc127043..a4df6bca192f 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -221,6 +221,7 @@ static const char *hw_flag_names[] = {
FLAG(TX_STATUS_NO_AMPDU_LEN),
FLAG(SUPPORTS_MULTI_BSSID),
FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
+ FLAG(SUPPORTS_80211_ENCAP),
#undef FLAG
};

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index e170f986d226..d7569a455a06 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -987,6 +987,8 @@ struct ieee80211_sub_if_data {
} debugfs;
#endif

+ bool hw_80211_encap;
+
/* must be last, dynamically sized area in this! */
struct ieee80211_vif vif;
};
@@ -1732,6 +1734,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
struct vif_params *params);
int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
enum nl80211_iftype type);
+bool ieee80211_is_hw_80211_encap(struct ieee80211_local *local);
void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata);
void ieee80211_remove_interfaces(struct ieee80211_local *local);
u32 ieee80211_idle_off(struct ieee80211_local *local);
@@ -1759,6 +1762,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
struct net_device *dev);
netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev);
+netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
+ struct net_device *dev);
void __ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev,
u32 info_flags);
@@ -1933,6 +1938,11 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, int tid,
enum nl80211_band band, u32 txdata_flags);

+/* sta_out needs to be checked for ERR_PTR() before using */
+int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb,
+ struct sta_info **sta_out);
+
static inline void
ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, int tid,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 4a6ff1482a9f..4090c2c5fde0 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1177,6 +1177,59 @@ static const struct net_device_ops ieee80211_dataif_ops = {
.ndo_get_stats64 = ieee80211_get_stats64,
};

+static const struct net_device_ops ieee80211_dataif_8023_ops = {
+ .ndo_open = ieee80211_open,
+ .ndo_stop = ieee80211_stop,
+ .ndo_uninit = ieee80211_uninit,
+ .ndo_start_xmit = ieee80211_subif_start_xmit_8023,
+ .ndo_set_rx_mode = ieee80211_set_multicast_list,
+ .ndo_set_mac_address = ieee80211_change_mac,
+ .ndo_select_queue = ieee80211_netdev_select_queue,
+ .ndo_get_stats64 = ieee80211_get_stats64,
+};
+
+void ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_local *local = sdata->local;
+
+ if (!sdata->dev)
+ return;
+
+ if (!ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP))
+ enable = 0;
+
+ if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) &&
+ (local->hw.wiphy->frag_threshold != (u32)-1))
+ enable = 0;
+
+ if (enable) {
+ sdata->dev->netdev_ops = &ieee80211_dataif_8023_ops;
+ sdata->hw_80211_encap = true;
+ } else {
+ sdata->dev->netdev_ops = &ieee80211_dataif_ops;
+ sdata->hw_80211_encap = false;
+ }
+}
+EXPORT_SYMBOL(ieee80211_set_hw_80211_encap);
+
+bool ieee80211_is_hw_80211_encap(struct ieee80211_local *local)
+{
+ struct ieee80211_sub_if_data *sdata;
+ bool offloaded = false;
+
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (sdata->hw_80211_encap) {
+ offloaded = true;
+ break;
+ }
+ }
+ mutex_unlock(&local->iflist_mtx);
+
+ return offloaded;
+}
+
static u16 ieee80211_monitor_select_queue(struct net_device *dev,
struct sk_buff *skb,
struct net_device *sb_dev,
@@ -1409,6 +1462,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.idle = true;

sdata->noack_map = 0;
+ sdata->hw_80211_encap = false;

/* only monitor/p2p-device differ */
if (sdata->dev) {
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 4700718e010f..861b67d43eb7 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -197,6 +197,9 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
key->conf.keyidx,
sta ? sta->sta.addr : bcast_addr, ret);

+ if (sdata->hw_80211_encap)
+ return -EINVAL;
+
out_unsupported:
switch (key->conf.cipher) {
case WLAN_CIPHER_SUITE_WEP40:
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 800e67615e2a..a49bcec3891e 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1000,9 +1000,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN);
}

- /* mac80211 always supports monitor */
- hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
- hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
+ if (ieee80211_hw_check(hw, SUPPORTS_80211_ENCAP)) {
+ /* mac80211 always supports monitor unless we do 802.11
+ * encapsulation offloading.
+ */
+ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
+ hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
+ }

/* mac80211 doesn't support more than one IBSS interface right now */
for (i = 0; i < hw->wiphy->n_iface_combinations; i++) {
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 5b9952b1caf3..8feafaab88a4 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -1019,6 +1019,85 @@ void ieee80211_tx_rate_update(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_tx_rate_update);

+void ieee80211_tx_status_8023(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct sk_buff *skb)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct sta_info *sta;
+ int retry_count;
+ int rates_idx;
+ bool acked;
+
+ if (WARN_ON(!ieee80211_hw_check(hw, SUPPORTS_80211_ENCAP)))
+ goto skip_stats_update;
+
+ sdata = vif_to_sdata(vif);
+
+ acked = !!(info->flags & IEEE80211_TX_STAT_ACK);
+ rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
+
+ rcu_read_lock();
+
+ if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
+ rcu_read_unlock();
+ goto counters_update;
+ }
+
+ if (!sta || IS_ERR(sta)) {
+ rcu_read_unlock();
+ goto counters_update;
+ }
+
+ if (!acked)
+ sta->status_stats.retry_failed++;
+
+ if (rates_idx != -1)
+ sta->tx_stats.last_rate = info->status.rates[rates_idx];
+
+ sta->status_stats.retry_count += retry_count;
+
+ if (ieee80211_hw_check(hw, REPORTS_TX_ACK_STATUS)) {
+ if (acked && vif->type == NL80211_IFTYPE_STATION)
+ ieee80211_sta_reset_conn_monitor(sdata);
+
+ sta->status_stats.last_ack = jiffies;
+ if (info->flags & IEEE80211_TX_STAT_ACK) {
+ if (sta->status_stats.lost_packets)
+ sta->status_stats.lost_packets = 0;
+
+ if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
+ sta->status_stats.last_tdls_pkt_time = jiffies;
+ } else {
+ ieee80211_lost_packet(sta, info);
+ }
+ }
+
+ rcu_read_unlock();
+
+counters_update:
+ ieee80211_led_tx(local);
+
+ if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
+ !(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED))
+ goto skip_stats_update;
+
+ I802_DEBUG_INC(local->dot11TransmittedFrameCount);
+ if (is_multicast_ether_addr(skb->data))
+ I802_DEBUG_INC(local->dot11MulticastTransmittedFrameCount);
+ if (retry_count > 0)
+ I802_DEBUG_INC(local->dot11RetryCount);
+ if (retry_count > 1)
+ I802_DEBUG_INC(local->dot11MultipleRetryCount);
+
+skip_stats_update:
+ ieee80211_report_used_skb(local, skb, false);
+ dev_kfree_skb(skb);
+}
+EXPORT_SYMBOL(ieee80211_tx_status_8023);
+
void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets)
{
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 8a49a74c0a37..85356c208c02 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1253,7 +1253,8 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
(info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
return NULL;

- if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
+ if (!info->control.hw_80211_encap &&
+ unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
if ((!ieee80211_is_mgmt(hdr->frame_control) ||
ieee80211_is_bufferable_mmpdu(hdr->frame_control) ||
vif->type == NL80211_IFTYPE_STATION) &&
@@ -1400,6 +1401,7 @@ static void ieee80211_txq_enqueue(struct ieee80211_local *local,
struct fq *fq = &local->fq;
struct fq_tin *tin = &txqi->tin;

+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
ieee80211_set_skb_enqueue_time(skb);
fq_tin_enqueue(fq, tin, skb,
fq_skb_free_func,
@@ -2357,9 +2359,9 @@ static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb)
skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
}

-static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb,
- struct sta_info **sta_out)
+int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb,
+ struct sta_info **sta_out)
{
struct sta_info *sta;

@@ -2855,7 +2857,9 @@ void ieee80211_check_fast_xmit(struct sta_info *sta)
struct ieee80211_chanctx_conf *chanctx_conf;
__le16 fc;

- if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT))
+ /* check for driver support and ieee80211 encap offload */
+ if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT) ||
+ sdata->hw_80211_encap)
return;

/* Locking here protects both the pointer itself, and against concurrent
@@ -3554,6 +3558,9 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
hdr = (struct ieee80211_hdr *)skb->data;
info = IEEE80211_SKB_CB(skb);

+ if (info->control.hw_80211_encap)
+ goto out;
+
memset(&tx, 0, sizeof(tx));
__skb_queue_head_init(&tx.skbs);
tx.local = local;
@@ -4003,6 +4010,167 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}

+static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, int led_len,
+ struct sta_info *sta,
+ bool txpending)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_tx_control control = {};
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_sta *pubsta = NULL;
+ unsigned long flags;
+ int q = info->hw_queue;
+
+ if (ieee80211_queue_skb(local, sdata, sta, skb))
+ return true;
+
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
+ if (local->queue_stop_reasons[q] ||
+ (!txpending && !skb_queue_empty(&local->pending[q]))) {
+ if (txpending)
+ skb_queue_head(&local->pending[q], skb);
+ else
+ skb_queue_tail(&local->pending[q], skb);
+
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+
+ return false;
+ }
+
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+
+ if (sta && sta->uploaded)
+ pubsta = &sta->sta;
+
+ control.sta = pubsta;
+
+ drv_tx(local, &control, skb);
+
+ return true;
+}
+
+static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
+ struct net_device *dev, struct sta_info *sta,
+ struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ethhdr *ehdr = (struct ethhdr *)skb->data;
+ struct ieee80211_local *local = sdata->local;
+ bool authorized = false;
+ bool multicast;
+ bool tdls_peer;
+ unsigned char *ra = NULL;
+
+ if (IS_ERR(sta) || (sta && !sta->uploaded))
+ sta = NULL;
+
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ tdls_peer = test_sta_flag(sta, WLAN_STA_TDLS_PEER);
+ if (tdls_peer)
+ ra = skb->data;
+ else
+ ra = sdata->u.mgd.bssid;
+ } else {
+ ra = ehdr->h_dest;
+ }
+
+ if (!ra)
+ goto out_free;
+ multicast = is_multicast_ether_addr(ra);
+
+ if (sta)
+ authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
+
+ if (!multicast && !authorized &&
+ ((ehdr->h_proto != sdata->control_port_protocol) ||
+ !ether_addr_equal(sdata->vif.addr, ehdr->h_source)))
+ goto out_free;
+
+ if (multicast && sdata->vif.type == NL80211_IFTYPE_AP &&
+ !atomic_read(&sdata->u.ap.num_mcast_sta))
+ goto out_free;
+
+ if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) &&
+ test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
+ goto out_free;
+
+ /* TODO: Handle frames requiring wifi tx status to be notified */
+
+ memset(info, 0, sizeof(*info));
+
+ if (unlikely(sdata->control_port_protocol == ehdr->h_proto)) {
+ if (sdata->control_port_no_encrypt)
+ info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
+ }
+
+ if (multicast)
+ info->flags |= IEEE80211_TX_CTL_NO_ACK;
+
+ info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
+
+ ieee80211_tx_stats(dev, skb->len);
+
+ if (sta) {
+ sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
+ sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
+ }
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata = container_of(sdata->bss,
+ struct ieee80211_sub_if_data, u.ap);
+
+ info->control.hw_80211_encap = true;
+ info->control.vif = &sdata->vif;
+
+ ieee80211_tx_8023(sdata, skb, skb->len, sta, false);
+
+ return;
+
+out_free:
+ kfree_skb(skb);
+}
+
+netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct sta_info *sta;
+
+ if (WARN_ON(unlikely(!sdata->hw_80211_encap))) {
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ if (unlikely(skb->len < ETH_HLEN)) {
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ if (WARN_ON(dev->ieee80211_ptr->use_4addr)) {
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ rcu_read_lock();
+
+ if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
+ goto out_free;
+
+ ieee80211_8023_xmit(sdata, dev, sta, skb);
+
+ goto out;
+
+out_free:
+ kfree_skb(skb);
+out:
+ rcu_read_unlock();
+
+ return NETDEV_TX_OK;
+}
+
struct sk_buff *
ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, u32 info_flags)
@@ -4081,6 +4249,16 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
}
info->band = chanctx_conf->def.chan->band;
result = ieee80211_tx(sdata, NULL, skb, true, 0);
+ } else if (info->control.hw_80211_encap) {
+ if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
+ dev_kfree_skb(skb);
+ return true;
+ }
+
+ if (IS_ERR(sta) || (sta && !sta->uploaded))
+ sta = NULL;
+
+ result = ieee80211_tx_8023(sdata, skb, skb->len, sta, true);
} else {
struct sk_buff_head skbs;

--
2.11.0


2019-04-01 13:14:45

by John Crispin

[permalink] [raw]
Subject: [RFC V3 2/2] ath10k: add tx hw 802.11 encapusaltion offloading support

From: Vasanthakumar Thiagarajan <[email protected]>

This patch adds support for ethernet rxtx mode to the driver. The feature
is enabled via a new module parameter. If enabled to driver will enable
the feature on a per vif basis if all other requirements were met.

Testing on a IPQ4019 based hardware shows a increase in TCP throughput
of ~20% when the feature is enabled.

Signed-off-by: Vasanthakumar Thiagarajan <[email protected]>
Signed-off-by: John Crispin <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.c | 11 +++++
drivers/net/wireless/ath/ath10k/core.h | 3 ++
drivers/net/wireless/ath/ath10k/htt_tx.c | 24 ++++++----
drivers/net/wireless/ath/ath10k/mac.c | 78 ++++++++++++++++++++++++++------
drivers/net/wireless/ath/ath10k/txrx.c | 11 ++++-
5 files changed, 102 insertions(+), 25 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index ac2cf3f1c7b4..a86466f9cce7 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -30,6 +30,7 @@ static unsigned int ath10k_cryptmode_param;
static bool uart_print;
static bool skip_otp;
static bool rawmode;
+static bool ethernetmode = true;

unsigned long ath10k_coredump_mask = BIT(ATH10K_FW_CRASH_DUMP_REGISTERS) |
BIT(ATH10K_FW_CRASH_DUMP_CE_DATA);
@@ -41,6 +42,7 @@ module_param(uart_print, bool, 0644);
module_param(skip_otp, bool, 0644);
module_param(rawmode, bool, 0644);
module_param_named(coredump_mask, ath10k_coredump_mask, ulong, 0444);
+module_param(ethernetmode, bool, 0644);

MODULE_PARM_DESC(debug_mask, "Debugging mask");
MODULE_PARM_DESC(uart_print, "Uart target debugging");
@@ -48,6 +50,7 @@ MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode");
MODULE_PARM_DESC(cryptmode, "Crypto mode: 0-hardware, 1-software");
MODULE_PARM_DESC(rawmode, "Use raw 802.11 frame datapath");
MODULE_PARM_DESC(coredump_mask, "Bitfield of what to include in firmware crash file");
+MODULE_PARM_DESC(ethernetmode, "Use ethernet frame datapath");

static const struct ath10k_hw_params ath10k_hw_params_list[] = {
{
@@ -2926,6 +2929,14 @@ static void ath10k_core_register_work(struct work_struct *work)
/* peer stats are enabled by default */
set_bit(ATH10K_FLAG_PEER_STATS, &ar->dev_flags);

+ if (ethernetmode && rawmode) {
+ ath10k_err(ar, "ethernet and raw mode cannot co-exist\n");
+ status = -EINVAL;
+ goto err;
+ }
+
+ ar->ethernetmode = ethernetmode;
+
status = ath10k_core_probe_fw(ar);
if (status) {
ath10k_err(ar, "could not probe fw (%d)\n", status);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 27ec5557de88..30199a2d66b4 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -109,6 +109,7 @@ enum ath10k_skb_flags {
ATH10K_SKB_F_MGMT = BIT(3),
ATH10K_SKB_F_QOS = BIT(4),
ATH10K_SKB_F_RAW_TX = BIT(5),
+ ATH10K_SKB_F_HW_80211_ENCAP = BIT(6),
};

struct ath10k_skb_cb {
@@ -1187,6 +1188,8 @@ struct ath10k {
struct work_struct radar_confirmation_work;
struct ath10k_bus_params bus_param;

+ bool ethernetmode;
+
/* must be last */
u8 drv_priv[0] __aligned(sizeof(void *));
};
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index d8e9cc0bb772..a4a85b2b1599 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -1143,6 +1143,10 @@ static u8 ath10k_htt_tx_get_tid(struct sk_buff *skb, bool is_eth)
struct ieee80211_hdr *hdr = (void *)skb->data;
struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);

+ /* Firmware takes care of tid classification for ethernet format */
+ if (cb->flags & ATH10K_SKB_F_HW_80211_ENCAP)
+ return skb->priority % IEEE80211_QOS_CTL_TID_MASK;
+
if (!is_eth && ieee80211_is_mgmt(hdr->frame_control))
return HTT_DATA_TX_EXT_TID_MGMT;
else if (cb->flags & ATH10K_SKB_F_QOS)
@@ -1351,15 +1355,17 @@ static int ath10k_htt_tx_32(struct ath10k_htt *htt,
txbuf_paddr = htt->txbuf.paddr +
(sizeof(struct ath10k_htt_txbuf_32) * msdu_id);

- if ((ieee80211_is_action(hdr->frame_control) ||
- ieee80211_is_deauth(hdr->frame_control) ||
- ieee80211_is_disassoc(hdr->frame_control)) &&
- ieee80211_has_protected(hdr->frame_control)) {
- skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
- } else if (!(skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) &&
- txmode == ATH10K_HW_TXRX_RAW &&
- ieee80211_has_protected(hdr->frame_control)) {
- skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
+ if (!info->control.hw_80211_encap) {
+ if ((ieee80211_is_action(hdr->frame_control) ||
+ ieee80211_is_deauth(hdr->frame_control) ||
+ ieee80211_is_disassoc(hdr->frame_control)) &&
+ ieee80211_has_protected(hdr->frame_control)) {
+ skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
+ } else if (!(skb_cb->flags & ATH10K_SKB_F_NO_HWCRYPT) &&
+ txmode == ATH10K_HW_TXRX_RAW &&
+ ieee80211_has_protected(hdr->frame_control)) {
+ skb_put(msdu, IEEE80211_CCMP_MIC_LEN);
+ }
}

skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len,
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index b73c23d4ce86..f74db7e4cb44 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3386,12 +3386,16 @@ ath10k_mac_tx_h_get_txmode(struct ath10k *ar,
struct sk_buff *skb)
{
const struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
const struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
__le16 fc = hdr->frame_control;

if (!vif || vif->type == NL80211_IFTYPE_MONITOR)
return ATH10K_HW_TXRX_RAW;

+ if (tx_info->control.hw_80211_encap)
+ return ATH10K_HW_TXRX_ETHERNET;
+
if (ieee80211_is_mgmt(fc))
return ATH10K_HW_TXRX_MGMT;

@@ -3539,11 +3543,19 @@ static void ath10k_mac_tx_h_fill_cb(struct ath10k *ar,
{
struct ieee80211_hdr *hdr = (void *)skb->data;
struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
- const struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ const struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
bool is_data = ieee80211_is_data(hdr->frame_control) ||
ieee80211_is_data_qos(hdr->frame_control);

cb->flags = 0;
+ cb->vif = vif;
+ cb->txq = txq;
+
+ if (tx_info->control.hw_80211_encap) {
+ cb->flags |= ATH10K_SKB_F_HW_80211_ENCAP;
+ return;
+ }
+
if (!ath10k_tx_h_use_hwcrypto(vif, skb))
cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;

@@ -3558,13 +3570,11 @@ static void ath10k_mac_tx_h_fill_cb(struct ath10k *ar,
* for a specific VLAN group will always be encrypted in software.
*/
if (is_data && ieee80211_has_protected(hdr->frame_control) &&
- !info->control.hw_key) {
+ !tx_info->control.hw_key) {
cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
cb->flags |= ATH10K_SKB_F_RAW_TX;
}

- cb->vif = vif;
- cb->txq = txq;
cb->airtime_est = airtime;
}

@@ -3675,6 +3685,9 @@ static int ath10k_mac_tx(struct ath10k *ar,
const struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
int ret;

+ if (info->control.hw_80211_encap)
+ goto skip_encap;
+
/* We should disable CCK RATE due to P2P */
if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
@@ -3698,6 +3711,7 @@ static int ath10k_mac_tx(struct ath10k *ar,
}
}

+skip_encap:
if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
if (!ath10k_mac_tx_frm_has_freq(ar)) {
ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %pK\n",
@@ -3747,6 +3761,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
int ret;
unsigned long time_left;
bool tmp_peer_created = false;
+ struct ieee80211_tx_info *info;

/* FW requirement: We must create a peer before FW will send out
* an offchannel frame. Otherwise the frame will be stuck and
@@ -3766,8 +3781,14 @@ void ath10k_offchan_tx_work(struct work_struct *work)
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac offchannel skb %pK\n",
skb);

- hdr = (struct ieee80211_hdr *)skb->data;
- peer_addr = ieee80211_get_DA(hdr);
+ info = IEEE80211_SKB_CB(skb);
+
+ if (info->control.hw_80211_encap) {
+ peer_addr = skb->data;
+ } else {
+ hdr = (struct ieee80211_hdr *)skb->data;
+ peer_addr = ieee80211_get_DA(hdr);
+ }

spin_lock_bh(&ar->data_lock);
vdev_id = ar->scan.vdev_id;
@@ -4295,7 +4316,7 @@ static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif = info->control.vif;
struct ieee80211_sta *sta = control->sta;
struct ieee80211_txq *txq = NULL;
- struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ieee80211_hdr *hdr;
enum ath10k_hw_txrx_mode txmode;
enum ath10k_mac_tx_path txpath;
bool is_htt;
@@ -4303,6 +4324,7 @@ static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
bool is_presp;
int ret;
u16 airtime;
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);

airtime = ath10k_mac_update_airtime(ar, txq, skb);
ath10k_mac_tx_h_fill_cb(ar, vif, txq, skb, airtime);
@@ -4326,14 +4348,20 @@ static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
return;
}

- ret = ath10k_htt_tx_mgmt_inc_pending(htt, is_mgmt, is_presp);
- if (ret) {
- ath10k_dbg(ar, ATH10K_DBG_MAC, "failed to increase tx mgmt pending count: %d, dropping\n",
- ret);
- ath10k_htt_tx_dec_pending(htt);
- spin_unlock_bh(&ar->htt.tx_lock);
- ieee80211_free_txskb(ar->hw, skb);
- return;
+ if (is_mgmt) {
+ hdr = (struct ieee80211_hdr *)skb->data;
+ is_presp = ieee80211_is_probe_resp(hdr->frame_control);
+
+ ret = ath10k_htt_tx_mgmt_inc_pending(htt, is_mgmt,
+ is_presp);
+ if (ret) {
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "failed to increase tx mgmt pending count: %d, dropping\n",
+ ret);
+ ath10k_htt_tx_dec_pending(htt);
+ spin_unlock_bh(&ar->htt.tx_lock);
+ ieee80211_free_txskb(ar->hw, skb);
+ return;
+ }
}
spin_unlock_bh(&ar->htt.tx_lock);
}
@@ -5094,6 +5122,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
struct ath10k_vif *arvif = (void *)vif->drv_priv;
struct ath10k_peer *peer;
enum wmi_sta_powersave_param param;
+ int hw_encap = 0;
int ret = 0;
u32 value;
int bit;
@@ -5185,6 +5214,23 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
break;
}

+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ hw_encap = 1;
+ break;
+ case NL80211_IFTYPE_STATION:
+// if (!sdata->u.mgd.use_4addr)
+ hw_encap = 1;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+// if (!sdata->wdev.use_4addr)
+ hw_encap = 1;
+ break;
+ default:
+ break;
+ }
+ ieee80211_set_hw_80211_encap(vif, ar->ethernetmode & hw_encap);
+
/* Using vdev_id as queue number will make it very easy to do per-vif
* tx queue locking. This shouldn't wrap due to interface combinations
* but do a modulo for correctness sake and prevent using offchannel tx
@@ -8602,6 +8648,8 @@ int ath10k_mac_register(struct ath10k *ar)
ieee80211_hw_set(ar->hw, QUEUE_CONTROL);
ieee80211_hw_set(ar->hw, SUPPORTS_TX_FRAG);
ieee80211_hw_set(ar->hw, REPORTS_LOW_ACK);
+ if (ar->ethernetmode)
+ ieee80211_hw_set(ar->hw, SUPPORTS_80211_ENCAP);

if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags))
ieee80211_hw_set(ar->hw, SW_CRYPTO_CONTROL);
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index c5818d28f55a..bad5e7335c0e 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -50,6 +50,8 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
struct ath10k_skb_cb *skb_cb;
struct ath10k_txq *artxq;
struct sk_buff *msdu;
+ struct ieee80211_vif *vif;
+ u8 flags;

ath10k_dbg(ar, ATH10K_DBG_HTT,
"htt tx completion msdu_id %u status %d\n",
@@ -78,6 +80,9 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
artxq->num_fw_queued--;
}

+ flags = skb_cb->flags;
+ vif = skb_cb->vif;
+
ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
ath10k_htt_tx_dec_pending(htt);
if (htt->num_pending_tx == 0)
@@ -121,7 +126,11 @@ int ath10k_txrx_tx_unref(struct ath10k_htt *htt,
info->status.is_valid_ack_signal = true;
}

- ieee80211_tx_status(htt->ar->hw, msdu);
+ if (flags & ATH10K_SKB_F_HW_80211_ENCAP)
+ ieee80211_tx_status_8023(htt->ar->hw, vif, msdu);
+ else
+ ieee80211_tx_status(htt->ar->hw, msdu);
+
/* we do not own the msdu anymore */

return 0;
--
2.11.0


2019-04-01 13:20:29

by Ben Greear

[permalink] [raw]
Subject: Re: [RFC V3 2/2] ath10k: add tx hw 802.11 encapusaltion offloading support

On 4/1/19 6:14 AM, John Crispin wrote:
> From: Vasanthakumar Thiagarajan <[email protected]>
>
> This patch adds support for ethernet rxtx mode to the driver. The feature
> is enabled via a new module parameter. If enabled to driver will enable
> the feature on a per vif basis if all other requirements were met.
>
> Testing on a IPQ4019 based hardware shows a increase in TCP throughput
> of ~20% when the feature is enabled.

Hello,

Do you have an idea of where this improvement comes from? Is it CPU load on
the host, or does firmware somehow work better in this mode?

And, does UDP also show improvement?

Thanks,
Ben

--
Ben Greear <[email protected]>
Candela Technologies Inc http://www.candelatech.com


2019-04-01 14:03:06

by John Crispin

[permalink] [raw]
Subject: Re: [RFC V3 2/2] ath10k: add tx hw 802.11 encapusaltion offloading support


On 01/04/2019 15:14, John Crispin wrote:
> @@ -5185,6 +5214,23 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
> break;
> }
>
> + switch (vif->type) {
> + case NL80211_IFTYPE_AP:
> + hw_encap = 1;
> + break;
> + case NL80211_IFTYPE_STATION:
> +// if (!sdata->u.mgd.use_4addr)
> + hw_encap = 1;
> + break;
> + case NL80211_IFTYPE_AP_VLAN:
> +// if (!sdata->wdev.use_4addr)
> + hw_encap = 1;
> + break;
> + default:
> + break;
> + }
> + ieee80211_set_hw_80211_encap(vif, ar->ethernetmode & hw_encap);
> +
> /* Using vdev_id as queue number will make it very easy to do per-vif
> * tx queue locking. This shouldn't wrap due to interface combinations
> * but do a modulo for correctness sake and prevent using offchannel tx

Hi

I am not sure what the best way to handle the 4addr mode is. sdata is
part of the subsystem and is not exposed to the drivers.Calling 
ieee80211_set_hw_80211_encap() from within the driver looked best to me
at first glance and would avoid adding an extra op call to figure out if
the if encap is supported. Any pointers on what the preferred solution
would look like are welcome.

    John


2019-04-02 09:27:26

by Sebastian Gottschall

[permalink] [raw]
Subject: Re: [RFC V3 2/2] ath10k: add tx hw 802.11 encapusaltion offloading support

Am 01.04.2019 um 16:02 schrieb John Crispin:

>
> On 01/04/2019 15:14, John Crispin wrote:
>> @@ -5185,6 +5214,23 @@ static int ath10k_add_interface(struct
>> ieee80211_hw *hw,
>>           break;
>>       }
>>   +    switch (vif->type) {
>> +    case NL80211_IFTYPE_AP:
>> +        hw_encap = 1;
>> +        break;
>> +    case NL80211_IFTYPE_STATION:
>> +//        if (!sdata->u.mgd.use_4addr)
>> +            hw_encap = 1;
>> +        break;
>> +    case NL80211_IFTYPE_AP_VLAN:
>> +//        if (!sdata->wdev.use_4addr)
>> +            hw_encap = 1;
>> +        break;
>> +    default:
>> +        break;
>> +    }
>> +    ieee80211_set_hw_80211_encap(vif, ar->ethernetmode & hw_encap);
>> +
>>       /* Using vdev_id as queue number will make it very easy to do
>> per-vif
>>        * tx queue locking. This shouldn't wrap due to interface
>> combinations
>>        * but do a modulo for correctness sake and prevent using
>> offchannel tx
>
> Hi
>
> I am not sure what the best way to handle the 4addr mode is. sdata is
> part of the subsystem and is not exposed to the drivers.Calling 
> ieee80211_set_hw_80211_encap() from within the driver looked best to
> me at first glance and would avoid adding an extra op call to figure
> out if the if encap is supported. Any pointers on what the preferred
> solution would look like are welcome.
>
>     John

you may do the 4addr check within ieee80211_set_hw_80211_encap, there is
no need for dealing with that in ath10k or do i miss something?

Sebastian


2019-04-02 09:31:51

by Sebastian Gottschall

[permalink] [raw]
Subject: Re: [RFC V3 1/2] mac80211: add hw 80211 encapsulation offloading support

the introduced control field hw_80211_encap in ieee80211_tx_info leads
to a compile assert
/include/linux/compiler.h:522:38: error: call to
'__compiletime_assert_1307' declared with attribute error: BUILD_BUG_ON
failed: sizeof(struct ieee80211_tx_info) > sizeof(skb->cb)

skb->cb is to small on x64 devices
i suggest to implement hw_80211_encap in ieee80211_vif which is a member
of control in ieee80211_tx_info

Sebastian

Am 01.04.2019 um 15:14 schrieb John Crispin:
> From: Vasanthakumar Thiagarajan <[email protected]>
>
> This patch adds a new transmit path for hardware that supports 802.11
> encapsulation offloading. In those cases 802.3a frames get passed
> directly to the driver allowing to hardware to handle the encapsulation.
>
> Certain features wont work and the patch masks these out.
> * monitor interfaces are not supported if any of the vif is in encap mode.
> * amsdu/non-linear frames wont work in encap offloading mode.
> * TKIP countermeasures cannot be triggered and hence those keys are not
> accepted.
>
> The patch defines a secondary netdev_ops struct that the device is assigned
> to the device if 802.11 encap support is available and enabled. The driver
> needs to enable the support on a per vif basis if it finds that all
> pre-reqs are meet.
>
> Signed-off-by: Vasanthakumar Thiagarajan <[email protected]>
> Signed-off-by: John Crispin <[email protected]>
> ---
> include/net/mac80211.h | 25 ++++++
> net/mac80211/cfg.c | 12 ++-
> net/mac80211/debugfs.c | 1 +
> net/mac80211/ieee80211_i.h | 10 +++
> net/mac80211/iface.c | 54 +++++++++++++
> net/mac80211/key.c | 3 +
> net/mac80211/main.c | 10 ++-
> net/mac80211/status.c | 79 +++++++++++++++++++
> net/mac80211/tx.c | 188 +++++++++++++++++++++++++++++++++++++++++++--
> 9 files changed, 373 insertions(+), 9 deletions(-)
>
> diff --git a/include/net/mac80211.h b/include/net/mac80211.h
> index ac2ed8ec662b..3e8929770839 100644
> --- a/include/net/mac80211.h
> +++ b/include/net/mac80211.h
> @@ -1021,6 +1021,7 @@ struct ieee80211_tx_info {
> struct ieee80211_key_conf *hw_key;
> u32 flags;
> codel_time_t enqueue_time;
> + u8 hw_80211_encap;
> } control;
> struct {
> u64 cookie;
> @@ -2243,6 +2244,9 @@ struct ieee80211_txq {
> * @IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID: Hardware supports multi BSSID
> * only for HE APs. Applies if @IEEE80211_HW_SUPPORTS_MULTI_BSSID is set.
> *
> + * @IEEE80211_HW_SUPPORTS_80211_ENCAP: Hardware/driver supports 802.11
> + * encap for data frames.
> + *
> * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
> */
> enum ieee80211_hw_flags {
> @@ -2294,6 +2298,7 @@ enum ieee80211_hw_flags {
> IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN,
> IEEE80211_HW_SUPPORTS_MULTI_BSSID,
> IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
> + IEEE80211_HW_SUPPORTS_80211_ENCAP,
>
> /* keep last, obviously */
> NUM_IEEE80211_HW_FLAGS
> @@ -4587,6 +4592,25 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
> struct sk_buff *skb);
>
> /**
> + * ieee80211_tx_status_8023 - transmit status callback for 802.3 frame format
> + *
> + * Call this function for all transmitted data frames after their transmit
> + * completion. This callback should only be called for data frames which
> + * are are using driver's (or hardware's) offload capability of encap/decap
> + * 802.11 frames.
> + *
> + * This function may not be called in IRQ context. Calls to this function
> + * for a single hardware must be synchronized against each other.
> + *
> + * @hw: the hardware the frame was transmitted by
> + * @vif: the interface for which the frame was transmitted
> + * @skb: the frame that was transmitted, owned by mac80211 after this call
> + */
> +void ieee80211_tx_status_8023(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct sk_buff *skb);
> +
> +/**
> * ieee80211_report_low_ack - report non-responding station
> *
> * When operating in AP-mode, call this function to report a non-responding
> @@ -6359,4 +6383,5 @@ void ieee80211_nan_func_match(struct ieee80211_vif *vif,
> struct cfg80211_nan_match_params *match,
> gfp_t gfp);
>
> +void ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable);
> #endif /* MAC80211_H */
> diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
> index 09dd1c2860fc..53b56f8fcdfc 100644
> --- a/net/mac80211/cfg.c
> +++ b/net/mac80211/cfg.c
> @@ -367,8 +367,15 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
>
> /* reject WEP and TKIP keys if WEP failed to initialize */
> switch (params->cipher) {
> - case WLAN_CIPHER_SUITE_WEP40:
> case WLAN_CIPHER_SUITE_TKIP:
> + /* countermeasures wont work on encap offload mode so reject
> + * TKIP keys
> + */
> + if (ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP))
> + return -EINVAL;
> +
> + /* drop through */
> + case WLAN_CIPHER_SUITE_WEP40:
> case WLAN_CIPHER_SUITE_WEP104:
> if (IS_ERR(local->wep_tx_tfm))
> return -EINVAL;
> @@ -2379,6 +2386,9 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
> if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
> ieee80211_check_fast_xmit_all(local);
>
> + if (ieee80211_is_hw_80211_encap(local))
> + return -EINVAL;
> +
> err = drv_set_frag_threshold(local, wiphy->frag_threshold);
>
> if (err) {
> diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
> index 2d43bc127043..a4df6bca192f 100644
> --- a/net/mac80211/debugfs.c
> +++ b/net/mac80211/debugfs.c
> @@ -221,6 +221,7 @@ static const char *hw_flag_names[] = {
> FLAG(TX_STATUS_NO_AMPDU_LEN),
> FLAG(SUPPORTS_MULTI_BSSID),
> FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
> + FLAG(SUPPORTS_80211_ENCAP),
> #undef FLAG
> };
>
> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
> index e170f986d226..d7569a455a06 100644
> --- a/net/mac80211/ieee80211_i.h
> +++ b/net/mac80211/ieee80211_i.h
> @@ -987,6 +987,8 @@ struct ieee80211_sub_if_data {
> } debugfs;
> #endif
>
> + bool hw_80211_encap;
> +
> /* must be last, dynamically sized area in this! */
> struct ieee80211_vif vif;
> };
> @@ -1732,6 +1734,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
> struct vif_params *params);
> int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
> enum nl80211_iftype type);
> +bool ieee80211_is_hw_80211_encap(struct ieee80211_local *local);
> void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata);
> void ieee80211_remove_interfaces(struct ieee80211_local *local);
> u32 ieee80211_idle_off(struct ieee80211_local *local);
> @@ -1759,6 +1762,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
> struct net_device *dev);
> netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
> struct net_device *dev);
> +netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
> + struct net_device *dev);
> void __ieee80211_subif_start_xmit(struct sk_buff *skb,
> struct net_device *dev,
> u32 info_flags);
> @@ -1933,6 +1938,11 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
> struct sk_buff *skb, int tid,
> enum nl80211_band band, u32 txdata_flags);
>
> +/* sta_out needs to be checked for ERR_PTR() before using */
> +int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
> + struct sk_buff *skb,
> + struct sta_info **sta_out);
> +
> static inline void
> ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
> struct sk_buff *skb, int tid,
> diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
> index 4a6ff1482a9f..4090c2c5fde0 100644
> --- a/net/mac80211/iface.c
> +++ b/net/mac80211/iface.c
> @@ -1177,6 +1177,59 @@ static const struct net_device_ops ieee80211_dataif_ops = {
> .ndo_get_stats64 = ieee80211_get_stats64,
> };
>
> +static const struct net_device_ops ieee80211_dataif_8023_ops = {
> + .ndo_open = ieee80211_open,
> + .ndo_stop = ieee80211_stop,
> + .ndo_uninit = ieee80211_uninit,
> + .ndo_start_xmit = ieee80211_subif_start_xmit_8023,
> + .ndo_set_rx_mode = ieee80211_set_multicast_list,
> + .ndo_set_mac_address = ieee80211_change_mac,
> + .ndo_select_queue = ieee80211_netdev_select_queue,
> + .ndo_get_stats64 = ieee80211_get_stats64,
> +};
> +
> +void ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable)
> +{
> + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
> + struct ieee80211_local *local = sdata->local;
> +
> + if (!sdata->dev)
> + return;
> +
> + if (!ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP))
> + enable = 0;
> +
> + if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) &&
> + (local->hw.wiphy->frag_threshold != (u32)-1))
> + enable = 0;
> +
> + if (enable) {
> + sdata->dev->netdev_ops = &ieee80211_dataif_8023_ops;
> + sdata->hw_80211_encap = true;
> + } else {
> + sdata->dev->netdev_ops = &ieee80211_dataif_ops;
> + sdata->hw_80211_encap = false;
> + }
> +}
> +EXPORT_SYMBOL(ieee80211_set_hw_80211_encap);
> +
> +bool ieee80211_is_hw_80211_encap(struct ieee80211_local *local)
> +{
> + struct ieee80211_sub_if_data *sdata;
> + bool offloaded = false;
> +
> + mutex_lock(&local->iflist_mtx);
> + list_for_each_entry(sdata, &local->interfaces, list) {
> + if (sdata->hw_80211_encap) {
> + offloaded = true;
> + break;
> + }
> + }
> + mutex_unlock(&local->iflist_mtx);
> +
> + return offloaded;
> +}
> +
> static u16 ieee80211_monitor_select_queue(struct net_device *dev,
> struct sk_buff *skb,
> struct net_device *sb_dev,
> @@ -1409,6 +1462,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
> sdata->vif.bss_conf.idle = true;
>
> sdata->noack_map = 0;
> + sdata->hw_80211_encap = false;
>
> /* only monitor/p2p-device differ */
> if (sdata->dev) {
> diff --git a/net/mac80211/key.c b/net/mac80211/key.c
> index 4700718e010f..861b67d43eb7 100644
> --- a/net/mac80211/key.c
> +++ b/net/mac80211/key.c
> @@ -197,6 +197,9 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
> key->conf.keyidx,
> sta ? sta->sta.addr : bcast_addr, ret);
>
> + if (sdata->hw_80211_encap)
> + return -EINVAL;
> +
> out_unsupported:
> switch (key->conf.cipher) {
> case WLAN_CIPHER_SUITE_WEP40:
> diff --git a/net/mac80211/main.c b/net/mac80211/main.c
> index 800e67615e2a..a49bcec3891e 100644
> --- a/net/mac80211/main.c
> +++ b/net/mac80211/main.c
> @@ -1000,9 +1000,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
> hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN);
> }
>
> - /* mac80211 always supports monitor */
> - hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
> - hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
> + if (ieee80211_hw_check(hw, SUPPORTS_80211_ENCAP)) {
> + /* mac80211 always supports monitor unless we do 802.11
> + * encapsulation offloading.
> + */
> + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
> + hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
> + }
>
> /* mac80211 doesn't support more than one IBSS interface right now */
> for (i = 0; i < hw->wiphy->n_iface_combinations; i++) {
> diff --git a/net/mac80211/status.c b/net/mac80211/status.c
> index 5b9952b1caf3..8feafaab88a4 100644
> --- a/net/mac80211/status.c
> +++ b/net/mac80211/status.c
> @@ -1019,6 +1019,85 @@ void ieee80211_tx_rate_update(struct ieee80211_hw *hw,
> }
> EXPORT_SYMBOL(ieee80211_tx_rate_update);
>
> +void ieee80211_tx_status_8023(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct sk_buff *skb)
> +{
> + struct ieee80211_local *local = hw_to_local(hw);
> + struct ieee80211_sub_if_data *sdata;
> + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> + struct sta_info *sta;
> + int retry_count;
> + int rates_idx;
> + bool acked;
> +
> + if (WARN_ON(!ieee80211_hw_check(hw, SUPPORTS_80211_ENCAP)))
> + goto skip_stats_update;
> +
> + sdata = vif_to_sdata(vif);
> +
> + acked = !!(info->flags & IEEE80211_TX_STAT_ACK);
> + rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
> +
> + rcu_read_lock();
> +
> + if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
> + rcu_read_unlock();
> + goto counters_update;
> + }
> +
> + if (!sta || IS_ERR(sta)) {
> + rcu_read_unlock();
> + goto counters_update;
> + }
> +
> + if (!acked)
> + sta->status_stats.retry_failed++;
> +
> + if (rates_idx != -1)
> + sta->tx_stats.last_rate = info->status.rates[rates_idx];
> +
> + sta->status_stats.retry_count += retry_count;
> +
> + if (ieee80211_hw_check(hw, REPORTS_TX_ACK_STATUS)) {
> + if (acked && vif->type == NL80211_IFTYPE_STATION)
> + ieee80211_sta_reset_conn_monitor(sdata);
> +
> + sta->status_stats.last_ack = jiffies;
> + if (info->flags & IEEE80211_TX_STAT_ACK) {
> + if (sta->status_stats.lost_packets)
> + sta->status_stats.lost_packets = 0;
> +
> + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
> + sta->status_stats.last_tdls_pkt_time = jiffies;
> + } else {
> + ieee80211_lost_packet(sta, info);
> + }
> + }
> +
> + rcu_read_unlock();
> +
> +counters_update:
> + ieee80211_led_tx(local);
> +
> + if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
> + !(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED))
> + goto skip_stats_update;
> +
> + I802_DEBUG_INC(local->dot11TransmittedFrameCount);
> + if (is_multicast_ether_addr(skb->data))
> + I802_DEBUG_INC(local->dot11MulticastTransmittedFrameCount);
> + if (retry_count > 0)
> + I802_DEBUG_INC(local->dot11RetryCount);
> + if (retry_count > 1)
> + I802_DEBUG_INC(local->dot11MultipleRetryCount);
> +
> +skip_stats_update:
> + ieee80211_report_used_skb(local, skb, false);
> + dev_kfree_skb(skb);
> +}
> +EXPORT_SYMBOL(ieee80211_tx_status_8023);
> +
> void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets)
> {
> struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
> index 8a49a74c0a37..85356c208c02 100644
> --- a/net/mac80211/tx.c
> +++ b/net/mac80211/tx.c
> @@ -1253,7 +1253,8 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
> (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
> return NULL;
>
> - if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
> + if (!info->control.hw_80211_encap &&
> + unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
> if ((!ieee80211_is_mgmt(hdr->frame_control) ||
> ieee80211_is_bufferable_mmpdu(hdr->frame_control) ||
> vif->type == NL80211_IFTYPE_STATION) &&
> @@ -1400,6 +1401,7 @@ static void ieee80211_txq_enqueue(struct ieee80211_local *local,
> struct fq *fq = &local->fq;
> struct fq_tin *tin = &txqi->tin;
>
> + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> ieee80211_set_skb_enqueue_time(skb);
> fq_tin_enqueue(fq, tin, skb,
> fq_skb_free_func,
> @@ -2357,9 +2359,9 @@ static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb)
> skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
> }
>
> -static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
> - struct sk_buff *skb,
> - struct sta_info **sta_out)
> +int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
> + struct sk_buff *skb,
> + struct sta_info **sta_out)
> {
> struct sta_info *sta;
>
> @@ -2855,7 +2857,9 @@ void ieee80211_check_fast_xmit(struct sta_info *sta)
> struct ieee80211_chanctx_conf *chanctx_conf;
> __le16 fc;
>
> - if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT))
> + /* check for driver support and ieee80211 encap offload */
> + if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT) ||
> + sdata->hw_80211_encap)
> return;
>
> /* Locking here protects both the pointer itself, and against concurrent
> @@ -3554,6 +3558,9 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
> hdr = (struct ieee80211_hdr *)skb->data;
> info = IEEE80211_SKB_CB(skb);
>
> + if (info->control.hw_80211_encap)
> + goto out;
> +
> memset(&tx, 0, sizeof(tx));
> __skb_queue_head_init(&tx.skbs);
> tx.local = local;
> @@ -4003,6 +4010,167 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
> return NETDEV_TX_OK;
> }
>
> +static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
> + struct sk_buff *skb, int led_len,
> + struct sta_info *sta,
> + bool txpending)
> +{
> + struct ieee80211_local *local = sdata->local;
> + struct ieee80211_tx_control control = {};
> + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> + struct ieee80211_sta *pubsta = NULL;
> + unsigned long flags;
> + int q = info->hw_queue;
> +
> + if (ieee80211_queue_skb(local, sdata, sta, skb))
> + return true;
> +
> + spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
> +
> + if (local->queue_stop_reasons[q] ||
> + (!txpending && !skb_queue_empty(&local->pending[q]))) {
> + if (txpending)
> + skb_queue_head(&local->pending[q], skb);
> + else
> + skb_queue_tail(&local->pending[q], skb);
> +
> + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
> +
> + return false;
> + }
> +
> + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
> +
> + if (sta && sta->uploaded)
> + pubsta = &sta->sta;
> +
> + control.sta = pubsta;
> +
> + drv_tx(local, &control, skb);
> +
> + return true;
> +}
> +
> +static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
> + struct net_device *dev, struct sta_info *sta,
> + struct sk_buff *skb)
> +{
> + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> + struct ethhdr *ehdr = (struct ethhdr *)skb->data;
> + struct ieee80211_local *local = sdata->local;
> + bool authorized = false;
> + bool multicast;
> + bool tdls_peer;
> + unsigned char *ra = NULL;
> +
> + if (IS_ERR(sta) || (sta && !sta->uploaded))
> + sta = NULL;
> +
> + if (sdata->vif.type == NL80211_IFTYPE_STATION) {
> + tdls_peer = test_sta_flag(sta, WLAN_STA_TDLS_PEER);
> + if (tdls_peer)
> + ra = skb->data;
> + else
> + ra = sdata->u.mgd.bssid;
> + } else {
> + ra = ehdr->h_dest;
> + }
> +
> + if (!ra)
> + goto out_free;
> + multicast = is_multicast_ether_addr(ra);
> +
> + if (sta)
> + authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
> +
> + if (!multicast && !authorized &&
> + ((ehdr->h_proto != sdata->control_port_protocol) ||
> + !ether_addr_equal(sdata->vif.addr, ehdr->h_source)))
> + goto out_free;
> +
> + if (multicast && sdata->vif.type == NL80211_IFTYPE_AP &&
> + !atomic_read(&sdata->u.ap.num_mcast_sta))
> + goto out_free;
> +
> + if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) &&
> + test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
> + goto out_free;
> +
> + /* TODO: Handle frames requiring wifi tx status to be notified */
> +
> + memset(info, 0, sizeof(*info));
> +
> + if (unlikely(sdata->control_port_protocol == ehdr->h_proto)) {
> + if (sdata->control_port_no_encrypt)
> + info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
> + info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
> + }
> +
> + if (multicast)
> + info->flags |= IEEE80211_TX_CTL_NO_ACK;
> +
> + info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
> +
> + ieee80211_tx_stats(dev, skb->len);
> +
> + if (sta) {
> + sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
> + sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
> + }
> +
> + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
> + sdata = container_of(sdata->bss,
> + struct ieee80211_sub_if_data, u.ap);
> +
> + info->control.hw_80211_encap = true;
> + info->control.vif = &sdata->vif;
> +
> + ieee80211_tx_8023(sdata, skb, skb->len, sta, false);
> +
> + return;
> +
> +out_free:
> + kfree_skb(skb);
> +}
> +
> +netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
> + struct net_device *dev)
> +{
> + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
> + struct sta_info *sta;
> +
> + if (WARN_ON(unlikely(!sdata->hw_80211_encap))) {
> + kfree_skb(skb);
> + return NETDEV_TX_OK;
> + }
> +
> + if (unlikely(skb->len < ETH_HLEN)) {
> + kfree_skb(skb);
> + return NETDEV_TX_OK;
> + }
> +
> + if (WARN_ON(dev->ieee80211_ptr->use_4addr)) {
> + kfree_skb(skb);
> + return NETDEV_TX_OK;
> + }
> +
> + rcu_read_lock();
> +
> + if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
> + goto out_free;
> +
> + ieee80211_8023_xmit(sdata, dev, sta, skb);
> +
> + goto out;
> +
> +out_free:
> + kfree_skb(skb);
> +out:
> + rcu_read_unlock();
> +
> + return NETDEV_TX_OK;
> +}
> +
> struct sk_buff *
> ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
> struct sk_buff *skb, u32 info_flags)
> @@ -4081,6 +4249,16 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
> }
> info->band = chanctx_conf->def.chan->band;
> result = ieee80211_tx(sdata, NULL, skb, true, 0);
> + } else if (info->control.hw_80211_encap) {
> + if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
> + dev_kfree_skb(skb);
> + return true;
> + }
> +
> + if (IS_ERR(sta) || (sta && !sta->uploaded))
> + sta = NULL;
> +
> + result = ieee80211_tx_8023(sdata, skb, skb->len, sta, true);
> } else {
> struct sk_buff_head skbs;
>

2019-04-02 09:50:28

by Toke Høiland-Jørgensen

[permalink] [raw]
Subject: Re: [RFC V3 1/2] mac80211: add hw 80211 encapsulation offloading support

John Crispin <[email protected]> writes:

> From: Vasanthakumar Thiagarajan <[email protected]>
>
> This patch adds a new transmit path for hardware that supports 802.11
> encapsulation offloading. In those cases 802.3a frames get passed
> directly to the driver allowing to hardware to handle the encapsulation.
>
> Certain features wont work and the patch masks these out.
> * monitor interfaces are not supported if any of the vif is in encap mode.
> * amsdu/non-linear frames wont work in encap offloading mode.
> * TKIP countermeasures cannot be triggered and hence those keys are not
> accepted.
>
> The patch defines a secondary netdev_ops struct that the device is assigned
> to the device if 802.11 encap support is available and enabled. The driver
> needs to enable the support on a per vif basis if it finds that all
> pre-reqs are meet.
>
> Signed-off-by: Vasanthakumar Thiagarajan <[email protected]>
> Signed-off-by: John Crispin <[email protected]>
> ---
> include/net/mac80211.h | 25 ++++++
> net/mac80211/cfg.c | 12 ++-
> net/mac80211/debugfs.c | 1 +
> net/mac80211/ieee80211_i.h | 10 +++
> net/mac80211/iface.c | 54 +++++++++++++
> net/mac80211/key.c | 3 +
> net/mac80211/main.c | 10 ++-
> net/mac80211/status.c | 79 +++++++++++++++++++
> net/mac80211/tx.c | 188 +++++++++++++++++++++++++++++++++++++++++++--
> 9 files changed, 373 insertions(+), 9 deletions(-)
>
> diff --git a/include/net/mac80211.h b/include/net/mac80211.h
> index ac2ed8ec662b..3e8929770839 100644
> --- a/include/net/mac80211.h
> +++ b/include/net/mac80211.h
> @@ -1021,6 +1021,7 @@ struct ieee80211_tx_info {
> struct ieee80211_key_conf *hw_key;
> u32 flags;
> codel_time_t enqueue_time;
> + u8 hw_80211_encap;
> } control;
> struct {
> u64 cookie;
> @@ -2243,6 +2244,9 @@ struct ieee80211_txq {
> * @IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID: Hardware supports multi BSSID
> * only for HE APs. Applies if @IEEE80211_HW_SUPPORTS_MULTI_BSSID is set.
> *
> + * @IEEE80211_HW_SUPPORTS_80211_ENCAP: Hardware/driver supports 802.11
> + * encap for data frames.
> + *
> * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
> */
> enum ieee80211_hw_flags {
> @@ -2294,6 +2298,7 @@ enum ieee80211_hw_flags {
> IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN,
> IEEE80211_HW_SUPPORTS_MULTI_BSSID,
> IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
> + IEEE80211_HW_SUPPORTS_80211_ENCAP,
>
> /* keep last, obviously */
> NUM_IEEE80211_HW_FLAGS
> @@ -4587,6 +4592,25 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
> struct sk_buff *skb);
>
> /**
> + * ieee80211_tx_status_8023 - transmit status callback for 802.3 frame format
> + *
> + * Call this function for all transmitted data frames after their transmit
> + * completion. This callback should only be called for data frames which
> + * are are using driver's (or hardware's) offload capability of encap/decap
> + * 802.11 frames.
> + *
> + * This function may not be called in IRQ context. Calls to this function
> + * for a single hardware must be synchronized against each other.
> + *
> + * @hw: the hardware the frame was transmitted by
> + * @vif: the interface for which the frame was transmitted
> + * @skb: the frame that was transmitted, owned by mac80211 after this call
> + */
> +void ieee80211_tx_status_8023(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct sk_buff *skb);
> +
> +/**
> * ieee80211_report_low_ack - report non-responding station
> *
> * When operating in AP-mode, call this function to report a non-responding
> @@ -6359,4 +6383,5 @@ void ieee80211_nan_func_match(struct ieee80211_vif *vif,
> struct cfg80211_nan_match_params *match,
> gfp_t gfp);
>
> +void ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable);
> #endif /* MAC80211_H */
> diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
> index 09dd1c2860fc..53b56f8fcdfc 100644
> --- a/net/mac80211/cfg.c
> +++ b/net/mac80211/cfg.c
> @@ -367,8 +367,15 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
>
> /* reject WEP and TKIP keys if WEP failed to initialize */
> switch (params->cipher) {
> - case WLAN_CIPHER_SUITE_WEP40:
> case WLAN_CIPHER_SUITE_TKIP:
> + /* countermeasures wont work on encap offload mode so reject
> + * TKIP keys
> + */
> + if (ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP))
> + return -EINVAL;
> +
> + /* drop through */
> + case WLAN_CIPHER_SUITE_WEP40:
> case WLAN_CIPHER_SUITE_WEP104:
> if (IS_ERR(local->wep_tx_tfm))
> return -EINVAL;
> @@ -2379,6 +2386,9 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
> if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
> ieee80211_check_fast_xmit_all(local);
>
> + if (ieee80211_is_hw_80211_encap(local))
> + return -EINVAL;
> +
> err = drv_set_frag_threshold(local, wiphy->frag_threshold);
>
> if (err) {
> diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
> index 2d43bc127043..a4df6bca192f 100644
> --- a/net/mac80211/debugfs.c
> +++ b/net/mac80211/debugfs.c
> @@ -221,6 +221,7 @@ static const char *hw_flag_names[] = {
> FLAG(TX_STATUS_NO_AMPDU_LEN),
> FLAG(SUPPORTS_MULTI_BSSID),
> FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
> + FLAG(SUPPORTS_80211_ENCAP),
> #undef FLAG
> };
>
> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
> index e170f986d226..d7569a455a06 100644
> --- a/net/mac80211/ieee80211_i.h
> +++ b/net/mac80211/ieee80211_i.h
> @@ -987,6 +987,8 @@ struct ieee80211_sub_if_data {
> } debugfs;
> #endif
>
> + bool hw_80211_encap;
> +
> /* must be last, dynamically sized area in this! */
> struct ieee80211_vif vif;
> };
> @@ -1732,6 +1734,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
> struct vif_params *params);
> int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
> enum nl80211_iftype type);
> +bool ieee80211_is_hw_80211_encap(struct ieee80211_local *local);
> void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata);
> void ieee80211_remove_interfaces(struct ieee80211_local *local);
> u32 ieee80211_idle_off(struct ieee80211_local *local);
> @@ -1759,6 +1762,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
> struct net_device *dev);
> netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
> struct net_device *dev);
> +netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
> + struct net_device *dev);
> void __ieee80211_subif_start_xmit(struct sk_buff *skb,
> struct net_device *dev,
> u32 info_flags);
> @@ -1933,6 +1938,11 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
> struct sk_buff *skb, int tid,
> enum nl80211_band band, u32 txdata_flags);
>
> +/* sta_out needs to be checked for ERR_PTR() before using */
> +int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
> + struct sk_buff *skb,
> + struct sta_info **sta_out);
> +
> static inline void
> ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
> struct sk_buff *skb, int tid,
> diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
> index 4a6ff1482a9f..4090c2c5fde0 100644
> --- a/net/mac80211/iface.c
> +++ b/net/mac80211/iface.c
> @@ -1177,6 +1177,59 @@ static const struct net_device_ops ieee80211_dataif_ops = {
> .ndo_get_stats64 = ieee80211_get_stats64,
> };
>
> +static const struct net_device_ops ieee80211_dataif_8023_ops = {
> + .ndo_open = ieee80211_open,
> + .ndo_stop = ieee80211_stop,
> + .ndo_uninit = ieee80211_uninit,
> + .ndo_start_xmit = ieee80211_subif_start_xmit_8023,
> + .ndo_set_rx_mode = ieee80211_set_multicast_list,
> + .ndo_set_mac_address = ieee80211_change_mac,
> + .ndo_select_queue = ieee80211_netdev_select_queue,
> + .ndo_get_stats64 = ieee80211_get_stats64,
> +};
> +
> +void ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable)
> +{
> + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
> + struct ieee80211_local *local = sdata->local;
> +
> + if (!sdata->dev)
> + return;
> +
> + if (!ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP))
> + enable = 0;
> +
> + if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) &&
> + (local->hw.wiphy->frag_threshold != (u32)-1))
> + enable = 0;
> +
> + if (enable) {
> + sdata->dev->netdev_ops = &ieee80211_dataif_8023_ops;
> + sdata->hw_80211_encap = true;
> + } else {
> + sdata->dev->netdev_ops = &ieee80211_dataif_ops;
> + sdata->hw_80211_encap = false;
> + }
> +}
> +EXPORT_SYMBOL(ieee80211_set_hw_80211_encap);
> +
> +bool ieee80211_is_hw_80211_encap(struct ieee80211_local *local)
> +{
> + struct ieee80211_sub_if_data *sdata;
> + bool offloaded = false;
> +
> + mutex_lock(&local->iflist_mtx);
> + list_for_each_entry(sdata, &local->interfaces, list) {
> + if (sdata->hw_80211_encap) {
> + offloaded = true;
> + break;
> + }
> + }
> + mutex_unlock(&local->iflist_mtx);
> +
> + return offloaded;
> +}
> +
> static u16 ieee80211_monitor_select_queue(struct net_device *dev,
> struct sk_buff *skb,
> struct net_device *sb_dev,
> @@ -1409,6 +1462,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
> sdata->vif.bss_conf.idle = true;
>
> sdata->noack_map = 0;
> + sdata->hw_80211_encap = false;
>
> /* only monitor/p2p-device differ */
> if (sdata->dev) {
> diff --git a/net/mac80211/key.c b/net/mac80211/key.c
> index 4700718e010f..861b67d43eb7 100644
> --- a/net/mac80211/key.c
> +++ b/net/mac80211/key.c
> @@ -197,6 +197,9 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
> key->conf.keyidx,
> sta ? sta->sta.addr : bcast_addr, ret);
>
> + if (sdata->hw_80211_encap)
> + return -EINVAL;
> +
> out_unsupported:
> switch (key->conf.cipher) {
> case WLAN_CIPHER_SUITE_WEP40:
> diff --git a/net/mac80211/main.c b/net/mac80211/main.c
> index 800e67615e2a..a49bcec3891e 100644
> --- a/net/mac80211/main.c
> +++ b/net/mac80211/main.c
> @@ -1000,9 +1000,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
> hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN);
> }
>
> - /* mac80211 always supports monitor */
> - hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
> - hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
> + if (ieee80211_hw_check(hw, SUPPORTS_80211_ENCAP)) {
> + /* mac80211 always supports monitor unless we do 802.11
> + * encapsulation offloading.
> + */
> + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
> + hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
> + }
>
> /* mac80211 doesn't support more than one IBSS interface right now */
> for (i = 0; i < hw->wiphy->n_iface_combinations; i++) {
> diff --git a/net/mac80211/status.c b/net/mac80211/status.c
> index 5b9952b1caf3..8feafaab88a4 100644
> --- a/net/mac80211/status.c
> +++ b/net/mac80211/status.c
> @@ -1019,6 +1019,85 @@ void ieee80211_tx_rate_update(struct ieee80211_hw *hw,
> }
> EXPORT_SYMBOL(ieee80211_tx_rate_update);
>
> +void ieee80211_tx_status_8023(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct sk_buff *skb)
> +{
> + struct ieee80211_local *local = hw_to_local(hw);
> + struct ieee80211_sub_if_data *sdata;
> + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> + struct sta_info *sta;
> + int retry_count;
> + int rates_idx;
> + bool acked;
> +
> + if (WARN_ON(!ieee80211_hw_check(hw, SUPPORTS_80211_ENCAP)))
> + goto skip_stats_update;
> +
> + sdata = vif_to_sdata(vif);
> +
> + acked = !!(info->flags & IEEE80211_TX_STAT_ACK);
> + rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
> +
> + rcu_read_lock();
> +
> + if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
> + rcu_read_unlock();
> + goto counters_update;
> + }
> +
> + if (!sta || IS_ERR(sta)) {
> + rcu_read_unlock();
> + goto counters_update;
> + }
> +
> + if (!acked)
> + sta->status_stats.retry_failed++;
> +
> + if (rates_idx != -1)
> + sta->tx_stats.last_rate = info->status.rates[rates_idx];
> +
> + sta->status_stats.retry_count += retry_count;
> +
> + if (ieee80211_hw_check(hw, REPORTS_TX_ACK_STATUS)) {
> + if (acked && vif->type == NL80211_IFTYPE_STATION)
> + ieee80211_sta_reset_conn_monitor(sdata);
> +
> + sta->status_stats.last_ack = jiffies;
> + if (info->flags & IEEE80211_TX_STAT_ACK) {
> + if (sta->status_stats.lost_packets)
> + sta->status_stats.lost_packets = 0;
> +
> + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
> + sta->status_stats.last_tdls_pkt_time = jiffies;
> + } else {
> + ieee80211_lost_packet(sta, info);
> + }
> + }
> +
> + rcu_read_unlock();
> +
> +counters_update:
> + ieee80211_led_tx(local);
> +
> + if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
> + !(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED))
> + goto skip_stats_update;
> +
> + I802_DEBUG_INC(local->dot11TransmittedFrameCount);
> + if (is_multicast_ether_addr(skb->data))
> + I802_DEBUG_INC(local->dot11MulticastTransmittedFrameCount);
> + if (retry_count > 0)
> + I802_DEBUG_INC(local->dot11RetryCount);
> + if (retry_count > 1)
> + I802_DEBUG_INC(local->dot11MultipleRetryCount);
> +
> +skip_stats_update:
> + ieee80211_report_used_skb(local, skb, false);
> + dev_kfree_skb(skb);
> +}
> +EXPORT_SYMBOL(ieee80211_tx_status_8023);
> +
> void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets)
> {
> struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
> index 8a49a74c0a37..85356c208c02 100644
> --- a/net/mac80211/tx.c
> +++ b/net/mac80211/tx.c
> @@ -1253,7 +1253,8 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
> (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
> return NULL;
>
> - if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
> + if (!info->control.hw_80211_encap &&
> + unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
> if ((!ieee80211_is_mgmt(hdr->frame_control) ||
> ieee80211_is_bufferable_mmpdu(hdr->frame_control) ||
> vif->type == NL80211_IFTYPE_STATION) &&
> @@ -1400,6 +1401,7 @@ static void ieee80211_txq_enqueue(struct ieee80211_local *local,
> struct fq *fq = &local->fq;
> struct fq_tin *tin = &txqi->tin;
>
> + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> ieee80211_set_skb_enqueue_time(skb);
> fq_tin_enqueue(fq, tin, skb,
> fq_skb_free_func,
> @@ -2357,9 +2359,9 @@ static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb)
> skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
> }
>
> -static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
> - struct sk_buff *skb,
> - struct sta_info **sta_out)
> +int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
> + struct sk_buff *skb,
> + struct sta_info **sta_out)
> {
> struct sta_info *sta;
>
> @@ -2855,7 +2857,9 @@ void ieee80211_check_fast_xmit(struct sta_info *sta)
> struct ieee80211_chanctx_conf *chanctx_conf;
> __le16 fc;
>
> - if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT))
> + /* check for driver support and ieee80211 encap offload */
> + if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT) ||
> + sdata->hw_80211_encap)
> return;
>
> /* Locking here protects both the pointer itself, and against concurrent
> @@ -3554,6 +3558,9 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
> hdr = (struct ieee80211_hdr *)skb->data;
> info = IEEE80211_SKB_CB(skb);
>
> + if (info->control.hw_80211_encap)
> + goto out;
> +
> memset(&tx, 0, sizeof(tx));
> __skb_queue_head_init(&tx.skbs);
> tx.local = local;
> @@ -4003,6 +4010,167 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
> return NETDEV_TX_OK;
> }
>
> +static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
> + struct sk_buff *skb, int led_len,
> + struct sta_info *sta,
> + bool txpending)
> +{
> + struct ieee80211_local *local = sdata->local;
> + struct ieee80211_tx_control control = {};
> + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> + struct ieee80211_sta *pubsta = NULL;
> + unsigned long flags;
> + int q = info->hw_queue;
> +
> + if (ieee80211_queue_skb(local, sdata, sta, skb))
> + return true;

ieee80211_queue_skb() calls ieee80211_get_txq() which treats skb->data
like it contains an 802.11 header. That is probably not the intention
here, is it?

I guess we could augment the TXQ stuctures to also handle 802.3 frames
(and introduce ieee80211_queue_skb_8023())? Or would it be better to
have a qdisc on 802.3-mode interfaces and push packets back to that? I
guess we'd still benefit from per-station queueing of packets even if
they are Ethernet frames, which would mean amending the TXQs would be
better?

-Toke

2019-04-02 12:44:47

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC V3 1/2] mac80211: add hw 80211 encapsulation offloading support

On Tue, 2019-04-02 at 11:50 +0200, Toke Høiland-Jørgensen wrote:

[snip, you really should try that sometimes ;-)]

> ieee80211_queue_skb() calls ieee80211_get_txq() which treats skb-
> datalike it contains an 802.11 header. That is probably not the
> intention here, is it?

Indeed, that makes no sense.

> I guess we could augment the TXQ stuctures to also handle 802.3 frames
> (and introduce ieee80211_queue_skb_8023())? Or would it be better to
> have a qdisc on 802.3-mode interfaces and push packets back to that? I
> guess we'd still benefit from per-station queueing of packets even if
> they are Ethernet frames, which would mean amending the TXQs would be
> better?

TXQs would be better, yes. We should do that.

johannes


2019-04-02 13:28:17

by Toke Høiland-Jørgensen

[permalink] [raw]
Subject: Re: [RFC V3 1/2] mac80211: add hw 80211 encapsulation offloading support

Johannes Berg <[email protected]> writes:

> On Tue, 2019-04-02 at 11:50 +0200, Toke Høiland-Jørgensen wrote:
>
> [snip, you really should try that sometimes ;-)]
>
>> ieee80211_queue_skb() calls ieee80211_get_txq() which treats skb-
>> datalike it contains an 802.11 header. That is probably not the
>> intention here, is it?
>
> Indeed, that makes no sense.
>
>> I guess we could augment the TXQ stuctures to also handle 802.3 frames
>> (and introduce ieee80211_queue_skb_8023())? Or would it be better to
>> have a qdisc on 802.3-mode interfaces and push packets back to that? I
>> guess we'd still benefit from per-station queueing of packets even if
>> they are Ethernet frames, which would mean amending the TXQs would be
>> better?
>
> TXQs would be better, yes. We should do that.

Cool. Glad that we agree ;)

-Toke

2019-04-02 14:37:54

by John Crispin

[permalink] [raw]
Subject: Re: [RFC V3 1/2] mac80211: add hw 80211 encapsulation offloading support


On 02/04/2019 15:28, Toke Høiland-Jørgensen wrote:
> Johannes Berg <[email protected]> writes:
>
>> On Tue, 2019-04-02 at 11:50 +0200, Toke Høiland-Jørgensen wrote:
>>
>> [snip, you really should try that sometimes ;-)]
>>
>>> ieee80211_queue_skb() calls ieee80211_get_txq() which treats skb-
>>> datalike it contains an 802.11 header. That is probably not the
>>> intention here, is it?
>> Indeed, that makes no sense.
>>
>>> I guess we could augment the TXQ stuctures to also handle 802.3 frames
>>> (and introduce ieee80211_queue_skb_8023())? Or would it be better to
>>> have a qdisc on 802.3-mode interfaces and push packets back to that? I
>>> guess we'd still benefit from per-station queueing of packets even if
>>> they are Ethernet frames, which would mean amending the TXQs would be
>>> better?
>> TXQs would be better, yes. We should do that.
> Cool. Glad that we agree ;)
>
> -Toke

Hi Toke,

will add that for V4, thanks

    John




2019-04-02 14:38:47

by John Crispin

[permalink] [raw]
Subject: Re: [RFC V3 2/2] ath10k: add tx hw 802.11 encapusaltion offloading support


On 02/04/2019 11:27, Sebastian Gottschall wrote:
> Am 01.04.2019 um 16:02 schrieb John Crispin:
>
>>
>> On 01/04/2019 15:14, John Crispin wrote:
>>> @@ -5185,6 +5214,23 @@ static int ath10k_add_interface(struct
>>> ieee80211_hw *hw,
>>>           break;
>>>       }
>>>   +    switch (vif->type) {
>>> +    case NL80211_IFTYPE_AP:
>>> +        hw_encap = 1;
>>> +        break;
>>> +    case NL80211_IFTYPE_STATION:
>>> +//        if (!sdata->u.mgd.use_4addr)
>>> +            hw_encap = 1;
>>> +        break;
>>> +    case NL80211_IFTYPE_AP_VLAN:
>>> +//        if (!sdata->wdev.use_4addr)
>>> +            hw_encap = 1;
>>> +        break;
>>> +    default:
>>> +        break;
>>> +    }
>>> +    ieee80211_set_hw_80211_encap(vif, ar->ethernetmode & hw_encap);
>>> +
>>>       /* Using vdev_id as queue number will make it very easy to do
>>> per-vif
>>>        * tx queue locking. This shouldn't wrap due to interface
>>> combinations
>>>        * but do a modulo for correctness sake and prevent using
>>> offchannel tx
>>
>> Hi
>>
>> I am not sure what the best way to handle the 4addr mode is. sdata is
>> part of the subsystem and is not exposed to the drivers.Calling 
>> ieee80211_set_hw_80211_encap() from within the driver looked best to
>> me at first glance and would avoid adding an extra op call to figure
>> out if the if encap is supported. Any pointers on what the preferred
>> solution would look like are welcome.
>>
>>     John
>
> you may do the 4addr check within ieee80211_set_hw_80211_encap, there
> is no need for dealing with that in ath10k or do i miss something?
>
> Sebastian
>
Correct, I'll add that in V4

    John



2019-04-02 14:39:58

by John Crispin

[permalink] [raw]
Subject: Re: [RFC V3 1/2] mac80211: add hw 80211 encapsulation offloading support


On 02/04/2019 11:31, Sebastian Gottschall wrote:
> the introduced control field hw_80211_encap in ieee80211_tx_info leads
> to a compile assert
> /include/linux/compiler.h:522:38: error: call to
> '__compiletime_assert_1307' declared with attribute error:
> BUILD_BUG_ON failed: sizeof(struct ieee80211_tx_info) > sizeof(skb->cb)
>
> skb->cb is to small on x64 devices
> i suggest to implement hw_80211_encap in ieee80211_vif which is a
> member of control in ieee80211_tx_info
>
> Sebastian

Hi Sebastian,

stumbled across that aswell while testing on arm64 today. I'll fix it in
V4, Thanks

    John


>
> Am 01.04.2019 um 15:14 schrieb John Crispin:
>> From: Vasanthakumar Thiagarajan <[email protected]>
>>
>> This patch adds a new transmit path for hardware that supports 802.11
>> encapsulation offloading. In those cases 802.3a frames get passed
>> directly to the driver allowing to hardware to handle the encapsulation.
>>
>> Certain features wont work and the patch masks these out.
>> * monitor interfaces are not supported if any of the vif is in encap
>> mode.
>> * amsdu/non-linear frames wont work in encap offloading mode.
>> * TKIP countermeasures cannot be triggered and hence those keys are not
>>    accepted.
>>
>> The patch defines a secondary netdev_ops struct that the device is
>> assigned
>> to the device if 802.11 encap support is available and enabled. The
>> driver
>> needs to enable the support on a per vif basis if it finds that all
>> pre-reqs are meet.
>>
>> Signed-off-by: Vasanthakumar Thiagarajan <[email protected]>
>> Signed-off-by: John Crispin <[email protected]>
>> ---
>>   include/net/mac80211.h     |  25 ++++++
>>   net/mac80211/cfg.c         |  12 ++-
>>   net/mac80211/debugfs.c     |   1 +
>>   net/mac80211/ieee80211_i.h |  10 +++
>>   net/mac80211/iface.c       |  54 +++++++++++++
>>   net/mac80211/key.c         |   3 +
>>   net/mac80211/main.c        |  10 ++-
>>   net/mac80211/status.c      |  79 +++++++++++++++++++
>>   net/mac80211/tx.c          | 188
>> +++++++++++++++++++++++++++++++++++++++++++--
>>   9 files changed, 373 insertions(+), 9 deletions(-)
>>
>> diff --git a/include/net/mac80211.h b/include/net/mac80211.h
>> index ac2ed8ec662b..3e8929770839 100644
>> --- a/include/net/mac80211.h
>> +++ b/include/net/mac80211.h
>> @@ -1021,6 +1021,7 @@ struct ieee80211_tx_info {
>>               struct ieee80211_key_conf *hw_key;
>>               u32 flags;
>>               codel_time_t enqueue_time;
>> +            u8 hw_80211_encap;
>>           } control;
>>           struct {
>>               u64 cookie;
>> @@ -2243,6 +2244,9 @@ struct ieee80211_txq {
>>    * @IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID: Hardware supports
>> multi BSSID
>>    *    only for HE APs. Applies if
>> @IEEE80211_HW_SUPPORTS_MULTI_BSSID is set.
>>    *
>> + * @IEEE80211_HW_SUPPORTS_80211_ENCAP: Hardware/driver supports 802.11
>> + *    encap for data frames.
>> + *
>>    * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for
>> sizing arrays
>>    */
>>   enum ieee80211_hw_flags {
>> @@ -2294,6 +2298,7 @@ enum ieee80211_hw_flags {
>>       IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN,
>>       IEEE80211_HW_SUPPORTS_MULTI_BSSID,
>>       IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
>> +    IEEE80211_HW_SUPPORTS_80211_ENCAP,
>>         /* keep last, obviously */
>>       NUM_IEEE80211_HW_FLAGS
>> @@ -4587,6 +4592,25 @@ void ieee80211_tx_status_irqsafe(struct
>> ieee80211_hw *hw,
>>                    struct sk_buff *skb);
>>     /**
>> + * ieee80211_tx_status_8023 - transmit status callback for 802.3
>> frame format
>> + *
>> + * Call this function for all transmitted data frames after their
>> transmit
>> + * completion. This callback should only be called for data frames
>> which
>> + * are are using driver's (or hardware's) offload capability of
>> encap/decap
>> + * 802.11 frames.
>> + *
>> + * This function may not be called in IRQ context. Calls to this
>> function
>> + * for a single hardware must be synchronized against each other.
>> + *
>> + * @hw: the hardware the frame was transmitted by
>> + * @vif: the interface for which the frame was transmitted
>> + * @skb: the frame that was transmitted, owned by mac80211 after
>> this call
>> + */
>> +void ieee80211_tx_status_8023(struct ieee80211_hw *hw,
>> +                   struct ieee80211_vif *vif,
>> +                   struct sk_buff *skb);
>> +
>> +/**
>>    * ieee80211_report_low_ack - report non-responding station
>>    *
>>    * When operating in AP-mode, call this function to report a
>> non-responding
>> @@ -6359,4 +6383,5 @@ void ieee80211_nan_func_match(struct
>> ieee80211_vif *vif,
>>                     struct cfg80211_nan_match_params *match,
>>                     gfp_t gfp);
>>   +void ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool
>> enable);
>>   #endif /* MAC80211_H */
>> diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
>> index 09dd1c2860fc..53b56f8fcdfc 100644
>> --- a/net/mac80211/cfg.c
>> +++ b/net/mac80211/cfg.c
>> @@ -367,8 +367,15 @@ static int ieee80211_add_key(struct wiphy
>> *wiphy, struct net_device *dev,
>>         /* reject WEP and TKIP keys if WEP failed to initialize */
>>       switch (params->cipher) {
>> -    case WLAN_CIPHER_SUITE_WEP40:
>>       case WLAN_CIPHER_SUITE_TKIP:
>> +        /* countermeasures wont work on encap offload mode so reject
>> +         * TKIP keys
>> +         */
>> +        if (ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP))
>> +            return -EINVAL;
>> +
>> +        /* drop through */
>> +    case WLAN_CIPHER_SUITE_WEP40:
>>       case WLAN_CIPHER_SUITE_WEP104:
>>           if (IS_ERR(local->wep_tx_tfm))
>>               return -EINVAL;
>> @@ -2379,6 +2386,9 @@ static int ieee80211_set_wiphy_params(struct
>> wiphy *wiphy, u32 changed)
>>       if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
>>           ieee80211_check_fast_xmit_all(local);
>>   +        if (ieee80211_is_hw_80211_encap(local))
>> +            return -EINVAL;
>> +
>>           err = drv_set_frag_threshold(local, wiphy->frag_threshold);
>>             if (err) {
>> diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
>> index 2d43bc127043..a4df6bca192f 100644
>> --- a/net/mac80211/debugfs.c
>> +++ b/net/mac80211/debugfs.c
>> @@ -221,6 +221,7 @@ static const char *hw_flag_names[] = {
>>       FLAG(TX_STATUS_NO_AMPDU_LEN),
>>       FLAG(SUPPORTS_MULTI_BSSID),
>>       FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
>> +    FLAG(SUPPORTS_80211_ENCAP),
>>   #undef FLAG
>>   };
>>   diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
>> index e170f986d226..d7569a455a06 100644
>> --- a/net/mac80211/ieee80211_i.h
>> +++ b/net/mac80211/ieee80211_i.h
>> @@ -987,6 +987,8 @@ struct ieee80211_sub_if_data {
>>       } debugfs;
>>   #endif
>>   +    bool hw_80211_encap;
>> +
>>       /* must be last, dynamically sized area in this! */
>>       struct ieee80211_vif vif;
>>   };
>> @@ -1732,6 +1734,7 @@ int ieee80211_if_add(struct ieee80211_local
>> *local, const char *name,
>>                struct vif_params *params);
>>   int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
>>                    enum nl80211_iftype type);
>> +bool ieee80211_is_hw_80211_encap(struct ieee80211_local *local);
>>   void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata);
>>   void ieee80211_remove_interfaces(struct ieee80211_local *local);
>>   u32 ieee80211_idle_off(struct ieee80211_local *local);
>> @@ -1759,6 +1762,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct
>> sk_buff *skb,
>>                        struct net_device *dev);
>>   netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
>>                          struct net_device *dev);
>> +netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
>> +                        struct net_device *dev);
>>   void __ieee80211_subif_start_xmit(struct sk_buff *skb,
>>                     struct net_device *dev,
>>                     u32 info_flags);
>> @@ -1933,6 +1938,11 @@ void __ieee80211_tx_skb_tid_band(struct
>> ieee80211_sub_if_data *sdata,
>>                    struct sk_buff *skb, int tid,
>>                    enum nl80211_band band, u32 txdata_flags);
>>   +/* sta_out needs to be checked for ERR_PTR() before using */
>> +int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
>> +                struct sk_buff *skb,
>> +                struct sta_info **sta_out);
>> +
>>   static inline void
>>   ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
>>                 struct sk_buff *skb, int tid,
>> diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
>> index 4a6ff1482a9f..4090c2c5fde0 100644
>> --- a/net/mac80211/iface.c
>> +++ b/net/mac80211/iface.c
>> @@ -1177,6 +1177,59 @@ static const struct net_device_ops
>> ieee80211_dataif_ops = {
>>       .ndo_get_stats64    = ieee80211_get_stats64,
>>   };
>>   +static const struct net_device_ops ieee80211_dataif_8023_ops = {
>> +    .ndo_open        = ieee80211_open,
>> +    .ndo_stop        = ieee80211_stop,
>> +    .ndo_uninit        = ieee80211_uninit,
>> +    .ndo_start_xmit        = ieee80211_subif_start_xmit_8023,
>> +    .ndo_set_rx_mode    = ieee80211_set_multicast_list,
>> +    .ndo_set_mac_address    = ieee80211_change_mac,
>> +    .ndo_select_queue    = ieee80211_netdev_select_queue,
>> +    .ndo_get_stats64    = ieee80211_get_stats64,
>> +};
>> +
>> +void ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool
>> enable)
>> +{
>> +    struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
>> +    struct ieee80211_local *local = sdata->local;
>> +
>> +    if (!sdata->dev)
>> +        return;
>> +
>> +    if (!ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP))
>> +        enable = 0;
>> +
>> +    if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) &&
>> +        (local->hw.wiphy->frag_threshold != (u32)-1))
>> +        enable = 0;
>> +
>> +    if (enable) {
>> +        sdata->dev->netdev_ops = &ieee80211_dataif_8023_ops;
>> +        sdata->hw_80211_encap = true;
>> +    } else {
>> +        sdata->dev->netdev_ops = &ieee80211_dataif_ops;
>> +        sdata->hw_80211_encap = false;
>> +    }
>> +}
>> +EXPORT_SYMBOL(ieee80211_set_hw_80211_encap);
>> +
>> +bool ieee80211_is_hw_80211_encap(struct ieee80211_local *local)
>> +{
>> +    struct ieee80211_sub_if_data *sdata;
>> +    bool offloaded = false;
>> +
>> +    mutex_lock(&local->iflist_mtx);
>> +    list_for_each_entry(sdata, &local->interfaces, list) {
>> +        if (sdata->hw_80211_encap) {
>> +            offloaded = true;
>> +            break;
>> +        }
>> +    }
>> +    mutex_unlock(&local->iflist_mtx);
>> +
>> +    return offloaded;
>> +}
>> +
>>   static u16 ieee80211_monitor_select_queue(struct net_device *dev,
>>                         struct sk_buff *skb,
>>                         struct net_device *sb_dev,
>> @@ -1409,6 +1462,7 @@ static void ieee80211_setup_sdata(struct
>> ieee80211_sub_if_data *sdata,
>>       sdata->vif.bss_conf.idle = true;
>>         sdata->noack_map = 0;
>> +    sdata->hw_80211_encap = false;
>>         /* only monitor/p2p-device differ */
>>       if (sdata->dev) {
>> diff --git a/net/mac80211/key.c b/net/mac80211/key.c
>> index 4700718e010f..861b67d43eb7 100644
>> --- a/net/mac80211/key.c
>> +++ b/net/mac80211/key.c
>> @@ -197,6 +197,9 @@ static int ieee80211_key_enable_hw_accel(struct
>> ieee80211_key *key)
>>                 key->conf.keyidx,
>>                 sta ? sta->sta.addr : bcast_addr, ret);
>>   +    if (sdata->hw_80211_encap)
>> +        return -EINVAL;
>> +
>>    out_unsupported:
>>       switch (key->conf.cipher) {
>>       case WLAN_CIPHER_SUITE_WEP40:
>> diff --git a/net/mac80211/main.c b/net/mac80211/main.c
>> index 800e67615e2a..a49bcec3891e 100644
>> --- a/net/mac80211/main.c
>> +++ b/net/mac80211/main.c
>> @@ -1000,9 +1000,13 @@ int ieee80211_register_hw(struct ieee80211_hw
>> *hw)
>>           hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN);
>>       }
>>   -    /* mac80211 always supports monitor */
>> -    hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
>> -    hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
>> +    if (ieee80211_hw_check(hw, SUPPORTS_80211_ENCAP)) {
>> +        /* mac80211 always supports monitor unless we do 802.11
>> +         * encapsulation offloading.
>> +         */
>> +        hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
>> +        hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
>> +    }
>>         /* mac80211 doesn't support more than one IBSS interface
>> right now */
>>       for (i = 0; i < hw->wiphy->n_iface_combinations; i++) {
>> diff --git a/net/mac80211/status.c b/net/mac80211/status.c
>> index 5b9952b1caf3..8feafaab88a4 100644
>> --- a/net/mac80211/status.c
>> +++ b/net/mac80211/status.c
>> @@ -1019,6 +1019,85 @@ void ieee80211_tx_rate_update(struct
>> ieee80211_hw *hw,
>>   }
>>   EXPORT_SYMBOL(ieee80211_tx_rate_update);
>>   +void ieee80211_tx_status_8023(struct ieee80211_hw *hw,
>> +                  struct ieee80211_vif *vif,
>> +                  struct sk_buff *skb)
>> +{
>> +    struct ieee80211_local *local = hw_to_local(hw);
>> +    struct ieee80211_sub_if_data *sdata;
>> +    struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
>> +    struct sta_info *sta;
>> +    int retry_count;
>> +    int rates_idx;
>> +    bool acked;
>> +
>> +    if (WARN_ON(!ieee80211_hw_check(hw, SUPPORTS_80211_ENCAP)))
>> +        goto skip_stats_update;
>> +
>> +    sdata = vif_to_sdata(vif);
>> +
>> +    acked = !!(info->flags & IEEE80211_TX_STAT_ACK);
>> +    rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
>> +
>> +    rcu_read_lock();
>> +
>> +    if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
>> +        rcu_read_unlock();
>> +        goto counters_update;
>> +    }
>> +
>> +    if (!sta || IS_ERR(sta)) {
>> +        rcu_read_unlock();
>> +        goto counters_update;
>> +    }
>> +
>> +    if (!acked)
>> +        sta->status_stats.retry_failed++;
>> +
>> +    if (rates_idx != -1)
>> +        sta->tx_stats.last_rate = info->status.rates[rates_idx];
>> +
>> +    sta->status_stats.retry_count += retry_count;
>> +
>> +    if (ieee80211_hw_check(hw, REPORTS_TX_ACK_STATUS)) {
>> +        if (acked && vif->type == NL80211_IFTYPE_STATION)
>> +            ieee80211_sta_reset_conn_monitor(sdata);
>> +
>> +        sta->status_stats.last_ack = jiffies;
>> +        if (info->flags & IEEE80211_TX_STAT_ACK) {
>> +            if (sta->status_stats.lost_packets)
>> +                sta->status_stats.lost_packets = 0;
>> +
>> +            if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
>> +                sta->status_stats.last_tdls_pkt_time = jiffies;
>> +        } else {
>> +            ieee80211_lost_packet(sta, info);
>> +        }
>> +    }
>> +
>> +    rcu_read_unlock();
>> +
>> +counters_update:
>> +    ieee80211_led_tx(local);
>> +
>> +    if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
>> +        !(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED))
>> +        goto skip_stats_update;
>> +
>> +    I802_DEBUG_INC(local->dot11TransmittedFrameCount);
>> +    if (is_multicast_ether_addr(skb->data))
>> + I802_DEBUG_INC(local->dot11MulticastTransmittedFrameCount);
>> +    if (retry_count > 0)
>> +        I802_DEBUG_INC(local->dot11RetryCount);
>> +    if (retry_count > 1)
>> +        I802_DEBUG_INC(local->dot11MultipleRetryCount);
>> +
>> +skip_stats_update:
>> +    ieee80211_report_used_skb(local, skb, false);
>> +    dev_kfree_skb(skb);
>> +}
>> +EXPORT_SYMBOL(ieee80211_tx_status_8023);
>> +
>>   void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32
>> num_packets)
>>   {
>>       struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
>> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
>> index 8a49a74c0a37..85356c208c02 100644
>> --- a/net/mac80211/tx.c
>> +++ b/net/mac80211/tx.c
>> @@ -1253,7 +1253,8 @@ static struct txq_info
>> *ieee80211_get_txq(struct ieee80211_local *local,
>>           (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
>>           return NULL;
>>   -    if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
>> +    if (!info->control.hw_80211_encap &&
>> + unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
>>           if ((!ieee80211_is_mgmt(hdr->frame_control) ||
>> ieee80211_is_bufferable_mmpdu(hdr->frame_control) ||
>>                vif->type == NL80211_IFTYPE_STATION) &&
>> @@ -1400,6 +1401,7 @@ static void ieee80211_txq_enqueue(struct
>> ieee80211_local *local,
>>       struct fq *fq = &local->fq;
>>       struct fq_tin *tin = &txqi->tin;
>>   +    struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
>>       ieee80211_set_skb_enqueue_time(skb);
>>       fq_tin_enqueue(fq, tin, skb,
>>                  fq_skb_free_func,
>> @@ -2357,9 +2359,9 @@ static inline bool
>> ieee80211_is_tdls_setup(struct sk_buff *skb)
>>              skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
>>   }
>>   -static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data
>> *sdata,
>> -                   struct sk_buff *skb,
>> -                   struct sta_info **sta_out)
>> +int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
>> +                struct sk_buff *skb,
>> +                struct sta_info **sta_out)
>>   {
>>       struct sta_info *sta;
>>   @@ -2855,7 +2857,9 @@ void ieee80211_check_fast_xmit(struct
>> sta_info *sta)
>>       struct ieee80211_chanctx_conf *chanctx_conf;
>>       __le16 fc;
>>   -    if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT))
>> +    /* check for driver support and ieee80211 encap offload */
>> +    if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT) ||
>> +        sdata->hw_80211_encap)
>>           return;
>>         /* Locking here protects both the pointer itself, and against
>> concurrent
>> @@ -3554,6 +3558,9 @@ struct sk_buff *ieee80211_tx_dequeue(struct
>> ieee80211_hw *hw,
>>       hdr = (struct ieee80211_hdr *)skb->data;
>>       info = IEEE80211_SKB_CB(skb);
>>   +    if (info->control.hw_80211_encap)
>> +        goto out;
>> +
>>       memset(&tx, 0, sizeof(tx));
>>       __skb_queue_head_init(&tx.skbs);
>>       tx.local = local;
>> @@ -4003,6 +4010,167 @@ netdev_tx_t ieee80211_subif_start_xmit(struct
>> sk_buff *skb,
>>       return NETDEV_TX_OK;
>>   }
>>   +static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
>> +                  struct sk_buff *skb, int led_len,
>> +                  struct sta_info *sta,
>> +                  bool txpending)
>> +{
>> +    struct ieee80211_local *local = sdata->local;
>> +    struct ieee80211_tx_control control = {};
>> +    struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
>> +    struct ieee80211_sta *pubsta = NULL;
>> +    unsigned long flags;
>> +    int q = info->hw_queue;
>> +
>> +    if (ieee80211_queue_skb(local, sdata, sta, skb))
>> +        return true;
>> +
>> +    spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
>> +
>> +    if (local->queue_stop_reasons[q] ||
>> +        (!txpending && !skb_queue_empty(&local->pending[q]))) {
>> +        if (txpending)
>> +            skb_queue_head(&local->pending[q], skb);
>> +        else
>> +            skb_queue_tail(&local->pending[q], skb);
>> +
>> + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
>> +
>> +        return false;
>> +    }
>> +
>> + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
>> +
>> +    if (sta && sta->uploaded)
>> +        pubsta = &sta->sta;
>> +
>> +    control.sta = pubsta;
>> +
>> +    drv_tx(local, &control, skb);
>> +
>> +    return true;
>> +}
>> +
>> +static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
>> +                struct net_device *dev, struct sta_info *sta,
>> +                struct sk_buff *skb)
>> +{
>> +    struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
>> +    struct ethhdr *ehdr = (struct ethhdr *)skb->data;
>> +    struct ieee80211_local *local = sdata->local;
>> +    bool authorized = false;
>> +    bool multicast;
>> +    bool tdls_peer;
>> +    unsigned char *ra = NULL;
>> +
>> +    if (IS_ERR(sta) || (sta && !sta->uploaded))
>> +        sta = NULL;
>> +
>> +    if (sdata->vif.type == NL80211_IFTYPE_STATION) {
>> +        tdls_peer = test_sta_flag(sta, WLAN_STA_TDLS_PEER);
>> +        if (tdls_peer)
>> +            ra = skb->data;
>> +        else
>> +            ra = sdata->u.mgd.bssid;
>> +    } else {
>> +        ra = ehdr->h_dest;
>> +    }
>> +
>> +    if (!ra)
>> +        goto out_free;
>> +    multicast = is_multicast_ether_addr(ra);
>> +
>> +    if (sta)
>> +        authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
>> +
>> +    if (!multicast && !authorized &&
>> +        ((ehdr->h_proto != sdata->control_port_protocol) ||
>> +         !ether_addr_equal(sdata->vif.addr, ehdr->h_source)))
>> +        goto out_free;
>> +
>> +    if (multicast && sdata->vif.type == NL80211_IFTYPE_AP &&
>> +        !atomic_read(&sdata->u.ap.num_mcast_sta))
>> +        goto out_free;
>> +
>> +    if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) &&
>> +        test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
>> +        goto out_free;
>> +
>> +    /* TODO: Handle frames requiring wifi tx status to be notified */
>> +
>> +    memset(info, 0, sizeof(*info));
>> +
>> +    if (unlikely(sdata->control_port_protocol == ehdr->h_proto)) {
>> +        if (sdata->control_port_no_encrypt)
>> +            info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
>> +        info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
>> +    }
>> +
>> +    if (multicast)
>> +        info->flags |= IEEE80211_TX_CTL_NO_ACK;
>> +
>> +    info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
>> +
>> +    ieee80211_tx_stats(dev, skb->len);
>> +
>> +    if (sta) {
>> +        sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
>> +        sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
>> +    }
>> +
>> +    if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
>> +        sdata = container_of(sdata->bss,
>> +                     struct ieee80211_sub_if_data, u.ap);
>> +
>> +    info->control.hw_80211_encap = true;
>> +    info->control.vif = &sdata->vif;
>> +
>> +    ieee80211_tx_8023(sdata, skb, skb->len, sta, false);
>> +
>> +    return;
>> +
>> +out_free:
>> +    kfree_skb(skb);
>> +}
>> +
>> +netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
>> +                        struct net_device *dev)
>> +{
>> +    struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
>> +    struct sta_info *sta;
>> +
>> +    if (WARN_ON(unlikely(!sdata->hw_80211_encap))) {
>> +        kfree_skb(skb);
>> +        return NETDEV_TX_OK;
>> +    }
>> +
>> +    if (unlikely(skb->len < ETH_HLEN)) {
>> +        kfree_skb(skb);
>> +        return NETDEV_TX_OK;
>> +    }
>> +
>> +    if (WARN_ON(dev->ieee80211_ptr->use_4addr)) {
>> +        kfree_skb(skb);
>> +        return NETDEV_TX_OK;
>> +    }
>> +
>> +    rcu_read_lock();
>> +
>> +    if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
>> +        goto out_free;
>> +
>> +    ieee80211_8023_xmit(sdata, dev, sta, skb);
>> +
>> +    goto out;
>> +
>> +out_free:
>> +    kfree_skb(skb);
>> +out:
>> +    rcu_read_unlock();
>> +
>> +    return NETDEV_TX_OK;
>> +}
>> +
>>   struct sk_buff *
>>   ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
>>                     struct sk_buff *skb, u32 info_flags)
>> @@ -4081,6 +4249,16 @@ static bool ieee80211_tx_pending_skb(struct
>> ieee80211_local *local,
>>           }
>>           info->band = chanctx_conf->def.chan->band;
>>           result = ieee80211_tx(sdata, NULL, skb, true, 0);
>> +    } else if (info->control.hw_80211_encap) {
>> +        if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
>> +            dev_kfree_skb(skb);
>> +            return true;
>> +        }
>> +
>> +        if (IS_ERR(sta) || (sta && !sta->uploaded))
>> +            sta = NULL;
>> +
>> +        result = ieee80211_tx_8023(sdata, skb, skb->len, sta, true);
>>       } else {
>>           struct sk_buff_head skbs;

2019-04-02 16:33:38

by Sebastian Gottschall

[permalink] [raw]
Subject: Re: [RFC V3 1/2] mac80211: add hw 80211 encapsulation offloading support

take care about dealing with it. my first private attempt crashed
everything :-)

Am 02.04.2019 um 16:39 schrieb John Crispin:
>
> On 02/04/2019 11:31, Sebastian Gottschall wrote:
>> the introduced control field hw_80211_encap in ieee80211_tx_info
>> leads to a compile assert
>> /include/linux/compiler.h:522:38: error: call to
>> '__compiletime_assert_1307' declared with attribute error:
>> BUILD_BUG_ON failed: sizeof(struct ieee80211_tx_info) > sizeof(skb->cb)
>>
>> skb->cb is to small on x64 devices
>> i suggest to implement hw_80211_encap in ieee80211_vif which is a
>> member of control in ieee80211_tx_info
>>
>> Sebastian
>
> Hi Sebastian,
>
> stumbled across that aswell while testing on arm64 today. I'll fix it
> in V4, Thanks
>
>     John
>
>
>>
>> Am 01.04.2019 um 15:14 schrieb John Crispin:
>>> From: Vasanthakumar Thiagarajan <[email protected]>
>>>
>>> This patch adds a new transmit path for hardware that supports 802.11
>>> encapsulation offloading. In those cases 802.3a frames get passed
>>> directly to the driver allowing to hardware to handle the
>>> encapsulation.
>>>
>>> Certain features wont work and the patch masks these out.
>>> * monitor interfaces are not supported if any of the vif is in encap
>>> mode.
>>> * amsdu/non-linear frames wont work in encap offloading mode.
>>> * TKIP countermeasures cannot be triggered and hence those keys are not
>>>    accepted.
>>>
>>> The patch defines a secondary netdev_ops struct that the device is
>>> assigned
>>> to the device if 802.11 encap support is available and enabled. The
>>> driver
>>> needs to enable the support on a per vif basis if it finds that all
>>> pre-reqs are meet.
>>>
>>> Signed-off-by: Vasanthakumar Thiagarajan <[email protected]>
>>> Signed-off-by: John Crispin <[email protected]>
>>> ---
>>>   include/net/mac80211.h     |  25 ++++++
>>>   net/mac80211/cfg.c         |  12 ++-
>>>   net/mac80211/debugfs.c     |   1 +
>>>   net/mac80211/ieee80211_i.h |  10 +++
>>>   net/mac80211/iface.c       |  54 +++++++++++++
>>>   net/mac80211/key.c         |   3 +
>>>   net/mac80211/main.c        |  10 ++-
>>>   net/mac80211/status.c      |  79 +++++++++++++++++++
>>>   net/mac80211/tx.c          | 188
>>> +++++++++++++++++++++++++++++++++++++++++++--
>>>   9 files changed, 373 insertions(+), 9 deletions(-)
>>>
>>> diff --git a/include/net/mac80211.h b/include/net/mac80211.h
>>> index ac2ed8ec662b..3e8929770839 100644
>>> --- a/include/net/mac80211.h
>>> +++ b/include/net/mac80211.h
>>> @@ -1021,6 +1021,7 @@ struct ieee80211_tx_info {
>>>               struct ieee80211_key_conf *hw_key;
>>>               u32 flags;
>>>               codel_time_t enqueue_time;
>>> +            u8 hw_80211_encap;
>>>           } control;
>>>           struct {
>>>               u64 cookie;
>>> @@ -2243,6 +2244,9 @@ struct ieee80211_txq {
>>>    * @IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID: Hardware supports
>>> multi BSSID
>>>    *    only for HE APs. Applies if
>>> @IEEE80211_HW_SUPPORTS_MULTI_BSSID is set.
>>>    *
>>> + * @IEEE80211_HW_SUPPORTS_80211_ENCAP: Hardware/driver supports 802.11
>>> + *    encap for data frames.
>>> + *
>>>    * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for
>>> sizing arrays
>>>    */
>>>   enum ieee80211_hw_flags {
>>> @@ -2294,6 +2298,7 @@ enum ieee80211_hw_flags {
>>>       IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN,
>>>       IEEE80211_HW_SUPPORTS_MULTI_BSSID,
>>>       IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
>>> +    IEEE80211_HW_SUPPORTS_80211_ENCAP,
>>>         /* keep last, obviously */
>>>       NUM_IEEE80211_HW_FLAGS
>>> @@ -4587,6 +4592,25 @@ void ieee80211_tx_status_irqsafe(struct
>>> ieee80211_hw *hw,
>>>                    struct sk_buff *skb);
>>>     /**
>>> + * ieee80211_tx_status_8023 - transmit status callback for 802.3
>>> frame format
>>> + *
>>> + * Call this function for all transmitted data frames after their
>>> transmit
>>> + * completion. This callback should only be called for data frames
>>> which
>>> + * are are using driver's (or hardware's) offload capability of
>>> encap/decap
>>> + * 802.11 frames.
>>> + *
>>> + * This function may not be called in IRQ context. Calls to this
>>> function
>>> + * for a single hardware must be synchronized against each other.
>>> + *
>>> + * @hw: the hardware the frame was transmitted by
>>> + * @vif: the interface for which the frame was transmitted
>>> + * @skb: the frame that was transmitted, owned by mac80211 after
>>> this call
>>> + */
>>> +void ieee80211_tx_status_8023(struct ieee80211_hw *hw,
>>> +                   struct ieee80211_vif *vif,
>>> +                   struct sk_buff *skb);
>>> +
>>> +/**
>>>    * ieee80211_report_low_ack - report non-responding station
>>>    *
>>>    * When operating in AP-mode, call this function to report a
>>> non-responding
>>> @@ -6359,4 +6383,5 @@ void ieee80211_nan_func_match(struct
>>> ieee80211_vif *vif,
>>>                     struct cfg80211_nan_match_params *match,
>>>                     gfp_t gfp);
>>>   +void ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool
>>> enable);
>>>   #endif /* MAC80211_H */
>>> diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
>>> index 09dd1c2860fc..53b56f8fcdfc 100644
>>> --- a/net/mac80211/cfg.c
>>> +++ b/net/mac80211/cfg.c
>>> @@ -367,8 +367,15 @@ static int ieee80211_add_key(struct wiphy
>>> *wiphy, struct net_device *dev,
>>>         /* reject WEP and TKIP keys if WEP failed to initialize */
>>>       switch (params->cipher) {
>>> -    case WLAN_CIPHER_SUITE_WEP40:
>>>       case WLAN_CIPHER_SUITE_TKIP:
>>> +        /* countermeasures wont work on encap offload mode so reject
>>> +         * TKIP keys
>>> +         */
>>> +        if (ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP))
>>> +            return -EINVAL;
>>> +
>>> +        /* drop through */
>>> +    case WLAN_CIPHER_SUITE_WEP40:
>>>       case WLAN_CIPHER_SUITE_WEP104:
>>>           if (IS_ERR(local->wep_tx_tfm))
>>>               return -EINVAL;
>>> @@ -2379,6 +2386,9 @@ static int ieee80211_set_wiphy_params(struct
>>> wiphy *wiphy, u32 changed)
>>>       if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
>>>           ieee80211_check_fast_xmit_all(local);
>>>   +        if (ieee80211_is_hw_80211_encap(local))
>>> +            return -EINVAL;
>>> +
>>>           err = drv_set_frag_threshold(local, wiphy->frag_threshold);
>>>             if (err) {
>>> diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
>>> index 2d43bc127043..a4df6bca192f 100644
>>> --- a/net/mac80211/debugfs.c
>>> +++ b/net/mac80211/debugfs.c
>>> @@ -221,6 +221,7 @@ static const char *hw_flag_names[] = {
>>>       FLAG(TX_STATUS_NO_AMPDU_LEN),
>>>       FLAG(SUPPORTS_MULTI_BSSID),
>>>       FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
>>> +    FLAG(SUPPORTS_80211_ENCAP),
>>>   #undef FLAG
>>>   };
>>>   diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
>>> index e170f986d226..d7569a455a06 100644
>>> --- a/net/mac80211/ieee80211_i.h
>>> +++ b/net/mac80211/ieee80211_i.h
>>> @@ -987,6 +987,8 @@ struct ieee80211_sub_if_data {
>>>       } debugfs;
>>>   #endif
>>>   +    bool hw_80211_encap;
>>> +
>>>       /* must be last, dynamically sized area in this! */
>>>       struct ieee80211_vif vif;
>>>   };
>>> @@ -1732,6 +1734,7 @@ int ieee80211_if_add(struct ieee80211_local
>>> *local, const char *name,
>>>                struct vif_params *params);
>>>   int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
>>>                    enum nl80211_iftype type);
>>> +bool ieee80211_is_hw_80211_encap(struct ieee80211_local *local);
>>>   void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata);
>>>   void ieee80211_remove_interfaces(struct ieee80211_local *local);
>>>   u32 ieee80211_idle_off(struct ieee80211_local *local);
>>> @@ -1759,6 +1762,8 @@ netdev_tx_t
>>> ieee80211_monitor_start_xmit(struct sk_buff *skb,
>>>                        struct net_device *dev);
>>>   netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
>>>                          struct net_device *dev);
>>> +netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
>>> +                        struct net_device *dev);
>>>   void __ieee80211_subif_start_xmit(struct sk_buff *skb,
>>>                     struct net_device *dev,
>>>                     u32 info_flags);
>>> @@ -1933,6 +1938,11 @@ void __ieee80211_tx_skb_tid_band(struct
>>> ieee80211_sub_if_data *sdata,
>>>                    struct sk_buff *skb, int tid,
>>>                    enum nl80211_band band, u32 txdata_flags);
>>>   +/* sta_out needs to be checked for ERR_PTR() before using */
>>> +int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
>>> +                struct sk_buff *skb,
>>> +                struct sta_info **sta_out);
>>> +
>>>   static inline void
>>>   ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
>>>                 struct sk_buff *skb, int tid,
>>> diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
>>> index 4a6ff1482a9f..4090c2c5fde0 100644
>>> --- a/net/mac80211/iface.c
>>> +++ b/net/mac80211/iface.c
>>> @@ -1177,6 +1177,59 @@ static const struct net_device_ops
>>> ieee80211_dataif_ops = {
>>>       .ndo_get_stats64    = ieee80211_get_stats64,
>>>   };
>>>   +static const struct net_device_ops ieee80211_dataif_8023_ops = {
>>> +    .ndo_open        = ieee80211_open,
>>> +    .ndo_stop        = ieee80211_stop,
>>> +    .ndo_uninit        = ieee80211_uninit,
>>> +    .ndo_start_xmit        = ieee80211_subif_start_xmit_8023,
>>> +    .ndo_set_rx_mode    = ieee80211_set_multicast_list,
>>> +    .ndo_set_mac_address    = ieee80211_change_mac,
>>> +    .ndo_select_queue    = ieee80211_netdev_select_queue,
>>> +    .ndo_get_stats64    = ieee80211_get_stats64,
>>> +};
>>> +
>>> +void ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool
>>> enable)
>>> +{
>>> +    struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
>>> +    struct ieee80211_local *local = sdata->local;
>>> +
>>> +    if (!sdata->dev)
>>> +        return;
>>> +
>>> +    if (!ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP))
>>> +        enable = 0;
>>> +
>>> +    if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) &&
>>> +        (local->hw.wiphy->frag_threshold != (u32)-1))
>>> +        enable = 0;
>>> +
>>> +    if (enable) {
>>> +        sdata->dev->netdev_ops = &ieee80211_dataif_8023_ops;
>>> +        sdata->hw_80211_encap = true;
>>> +    } else {
>>> +        sdata->dev->netdev_ops = &ieee80211_dataif_ops;
>>> +        sdata->hw_80211_encap = false;
>>> +    }
>>> +}
>>> +EXPORT_SYMBOL(ieee80211_set_hw_80211_encap);
>>> +
>>> +bool ieee80211_is_hw_80211_encap(struct ieee80211_local *local)
>>> +{
>>> +    struct ieee80211_sub_if_data *sdata;
>>> +    bool offloaded = false;
>>> +
>>> +    mutex_lock(&local->iflist_mtx);
>>> +    list_for_each_entry(sdata, &local->interfaces, list) {
>>> +        if (sdata->hw_80211_encap) {
>>> +            offloaded = true;
>>> +            break;
>>> +        }
>>> +    }
>>> +    mutex_unlock(&local->iflist_mtx);
>>> +
>>> +    return offloaded;
>>> +}
>>> +
>>>   static u16 ieee80211_monitor_select_queue(struct net_device *dev,
>>>                         struct sk_buff *skb,
>>>                         struct net_device *sb_dev,
>>> @@ -1409,6 +1462,7 @@ static void ieee80211_setup_sdata(struct
>>> ieee80211_sub_if_data *sdata,
>>>       sdata->vif.bss_conf.idle = true;
>>>         sdata->noack_map = 0;
>>> +    sdata->hw_80211_encap = false;
>>>         /* only monitor/p2p-device differ */
>>>       if (sdata->dev) {
>>> diff --git a/net/mac80211/key.c b/net/mac80211/key.c
>>> index 4700718e010f..861b67d43eb7 100644
>>> --- a/net/mac80211/key.c
>>> +++ b/net/mac80211/key.c
>>> @@ -197,6 +197,9 @@ static int ieee80211_key_enable_hw_accel(struct
>>> ieee80211_key *key)
>>>                 key->conf.keyidx,
>>>                 sta ? sta->sta.addr : bcast_addr, ret);
>>>   +    if (sdata->hw_80211_encap)
>>> +        return -EINVAL;
>>> +
>>>    out_unsupported:
>>>       switch (key->conf.cipher) {
>>>       case WLAN_CIPHER_SUITE_WEP40:
>>> diff --git a/net/mac80211/main.c b/net/mac80211/main.c
>>> index 800e67615e2a..a49bcec3891e 100644
>>> --- a/net/mac80211/main.c
>>> +++ b/net/mac80211/main.c
>>> @@ -1000,9 +1000,13 @@ int ieee80211_register_hw(struct ieee80211_hw
>>> *hw)
>>>           hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN);
>>>       }
>>>   -    /* mac80211 always supports monitor */
>>> -    hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
>>> -    hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
>>> +    if (ieee80211_hw_check(hw, SUPPORTS_80211_ENCAP)) {
>>> +        /* mac80211 always supports monitor unless we do 802.11
>>> +         * encapsulation offloading.
>>> +         */
>>> +        hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
>>> +        hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
>>> +    }
>>>         /* mac80211 doesn't support more than one IBSS interface
>>> right now */
>>>       for (i = 0; i < hw->wiphy->n_iface_combinations; i++) {
>>> diff --git a/net/mac80211/status.c b/net/mac80211/status.c
>>> index 5b9952b1caf3..8feafaab88a4 100644
>>> --- a/net/mac80211/status.c
>>> +++ b/net/mac80211/status.c
>>> @@ -1019,6 +1019,85 @@ void ieee80211_tx_rate_update(struct
>>> ieee80211_hw *hw,
>>>   }
>>>   EXPORT_SYMBOL(ieee80211_tx_rate_update);
>>>   +void ieee80211_tx_status_8023(struct ieee80211_hw *hw,
>>> +                  struct ieee80211_vif *vif,
>>> +                  struct sk_buff *skb)
>>> +{
>>> +    struct ieee80211_local *local = hw_to_local(hw);
>>> +    struct ieee80211_sub_if_data *sdata;
>>> +    struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
>>> +    struct sta_info *sta;
>>> +    int retry_count;
>>> +    int rates_idx;
>>> +    bool acked;
>>> +
>>> +    if (WARN_ON(!ieee80211_hw_check(hw, SUPPORTS_80211_ENCAP)))
>>> +        goto skip_stats_update;
>>> +
>>> +    sdata = vif_to_sdata(vif);
>>> +
>>> +    acked = !!(info->flags & IEEE80211_TX_STAT_ACK);
>>> +    rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
>>> +
>>> +    rcu_read_lock();
>>> +
>>> +    if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
>>> +        rcu_read_unlock();
>>> +        goto counters_update;
>>> +    }
>>> +
>>> +    if (!sta || IS_ERR(sta)) {
>>> +        rcu_read_unlock();
>>> +        goto counters_update;
>>> +    }
>>> +
>>> +    if (!acked)
>>> +        sta->status_stats.retry_failed++;
>>> +
>>> +    if (rates_idx != -1)
>>> +        sta->tx_stats.last_rate = info->status.rates[rates_idx];
>>> +
>>> +    sta->status_stats.retry_count += retry_count;
>>> +
>>> +    if (ieee80211_hw_check(hw, REPORTS_TX_ACK_STATUS)) {
>>> +        if (acked && vif->type == NL80211_IFTYPE_STATION)
>>> +            ieee80211_sta_reset_conn_monitor(sdata);
>>> +
>>> +        sta->status_stats.last_ack = jiffies;
>>> +        if (info->flags & IEEE80211_TX_STAT_ACK) {
>>> +            if (sta->status_stats.lost_packets)
>>> +                sta->status_stats.lost_packets = 0;
>>> +
>>> +            if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
>>> +                sta->status_stats.last_tdls_pkt_time = jiffies;
>>> +        } else {
>>> +            ieee80211_lost_packet(sta, info);
>>> +        }
>>> +    }
>>> +
>>> +    rcu_read_unlock();
>>> +
>>> +counters_update:
>>> +    ieee80211_led_tx(local);
>>> +
>>> +    if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
>>> +        !(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED))
>>> +        goto skip_stats_update;
>>> +
>>> +    I802_DEBUG_INC(local->dot11TransmittedFrameCount);
>>> +    if (is_multicast_ether_addr(skb->data))
>>> + I802_DEBUG_INC(local->dot11MulticastTransmittedFrameCount);
>>> +    if (retry_count > 0)
>>> +        I802_DEBUG_INC(local->dot11RetryCount);
>>> +    if (retry_count > 1)
>>> +        I802_DEBUG_INC(local->dot11MultipleRetryCount);
>>> +
>>> +skip_stats_update:
>>> +    ieee80211_report_used_skb(local, skb, false);
>>> +    dev_kfree_skb(skb);
>>> +}
>>> +EXPORT_SYMBOL(ieee80211_tx_status_8023);
>>> +
>>>   void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32
>>> num_packets)
>>>   {
>>>       struct sta_info *sta = container_of(pubsta, struct sta_info,
>>> sta);
>>> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
>>> index 8a49a74c0a37..85356c208c02 100644
>>> --- a/net/mac80211/tx.c
>>> +++ b/net/mac80211/tx.c
>>> @@ -1253,7 +1253,8 @@ static struct txq_info
>>> *ieee80211_get_txq(struct ieee80211_local *local,
>>>           (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
>>>           return NULL;
>>>   -    if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
>>> +    if (!info->control.hw_80211_encap &&
>>> + unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
>>>           if ((!ieee80211_is_mgmt(hdr->frame_control) ||
>>> ieee80211_is_bufferable_mmpdu(hdr->frame_control) ||
>>>                vif->type == NL80211_IFTYPE_STATION) &&
>>> @@ -1400,6 +1401,7 @@ static void ieee80211_txq_enqueue(struct
>>> ieee80211_local *local,
>>>       struct fq *fq = &local->fq;
>>>       struct fq_tin *tin = &txqi->tin;
>>>   +    struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
>>>       ieee80211_set_skb_enqueue_time(skb);
>>>       fq_tin_enqueue(fq, tin, skb,
>>>                  fq_skb_free_func,
>>> @@ -2357,9 +2359,9 @@ static inline bool
>>> ieee80211_is_tdls_setup(struct sk_buff *skb)
>>>              skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
>>>   }
>>>   -static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data
>>> *sdata,
>>> -                   struct sk_buff *skb,
>>> -                   struct sta_info **sta_out)
>>> +int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
>>> +                struct sk_buff *skb,
>>> +                struct sta_info **sta_out)
>>>   {
>>>       struct sta_info *sta;
>>>   @@ -2855,7 +2857,9 @@ void ieee80211_check_fast_xmit(struct
>>> sta_info *sta)
>>>       struct ieee80211_chanctx_conf *chanctx_conf;
>>>       __le16 fc;
>>>   -    if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT))
>>> +    /* check for driver support and ieee80211 encap offload */
>>> +    if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT) ||
>>> +        sdata->hw_80211_encap)
>>>           return;
>>>         /* Locking here protects both the pointer itself, and
>>> against concurrent
>>> @@ -3554,6 +3558,9 @@ struct sk_buff *ieee80211_tx_dequeue(struct
>>> ieee80211_hw *hw,
>>>       hdr = (struct ieee80211_hdr *)skb->data;
>>>       info = IEEE80211_SKB_CB(skb);
>>>   +    if (info->control.hw_80211_encap)
>>> +        goto out;
>>> +
>>>       memset(&tx, 0, sizeof(tx));
>>>       __skb_queue_head_init(&tx.skbs);
>>>       tx.local = local;
>>> @@ -4003,6 +4010,167 @@ netdev_tx_t
>>> ieee80211_subif_start_xmit(struct sk_buff *skb,
>>>       return NETDEV_TX_OK;
>>>   }
>>>   +static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata,
>>> +                  struct sk_buff *skb, int led_len,
>>> +                  struct sta_info *sta,
>>> +                  bool txpending)
>>> +{
>>> +    struct ieee80211_local *local = sdata->local;
>>> +    struct ieee80211_tx_control control = {};
>>> +    struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
>>> +    struct ieee80211_sta *pubsta = NULL;
>>> +    unsigned long flags;
>>> +    int q = info->hw_queue;
>>> +
>>> +    if (ieee80211_queue_skb(local, sdata, sta, skb))
>>> +        return true;
>>> +
>>> +    spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
>>> +
>>> +    if (local->queue_stop_reasons[q] ||
>>> +        (!txpending && !skb_queue_empty(&local->pending[q]))) {
>>> +        if (txpending)
>>> +            skb_queue_head(&local->pending[q], skb);
>>> +        else
>>> +            skb_queue_tail(&local->pending[q], skb);
>>> +
>>> + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
>>> +
>>> +        return false;
>>> +    }
>>> +
>>> + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
>>> +
>>> +    if (sta && sta->uploaded)
>>> +        pubsta = &sta->sta;
>>> +
>>> +    control.sta = pubsta;
>>> +
>>> +    drv_tx(local, &control, skb);
>>> +
>>> +    return true;
>>> +}
>>> +
>>> +static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
>>> +                struct net_device *dev, struct sta_info *sta,
>>> +                struct sk_buff *skb)
>>> +{
>>> +    struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
>>> +    struct ethhdr *ehdr = (struct ethhdr *)skb->data;
>>> +    struct ieee80211_local *local = sdata->local;
>>> +    bool authorized = false;
>>> +    bool multicast;
>>> +    bool tdls_peer;
>>> +    unsigned char *ra = NULL;
>>> +
>>> +    if (IS_ERR(sta) || (sta && !sta->uploaded))
>>> +        sta = NULL;
>>> +
>>> +    if (sdata->vif.type == NL80211_IFTYPE_STATION) {
>>> +        tdls_peer = test_sta_flag(sta, WLAN_STA_TDLS_PEER);
>>> +        if (tdls_peer)
>>> +            ra = skb->data;
>>> +        else
>>> +            ra = sdata->u.mgd.bssid;
>>> +    } else {
>>> +        ra = ehdr->h_dest;
>>> +    }
>>> +
>>> +    if (!ra)
>>> +        goto out_free;
>>> +    multicast = is_multicast_ether_addr(ra);
>>> +
>>> +    if (sta)
>>> +        authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
>>> +
>>> +    if (!multicast && !authorized &&
>>> +        ((ehdr->h_proto != sdata->control_port_protocol) ||
>>> +         !ether_addr_equal(sdata->vif.addr, ehdr->h_source)))
>>> +        goto out_free;
>>> +
>>> +    if (multicast && sdata->vif.type == NL80211_IFTYPE_AP &&
>>> +        !atomic_read(&sdata->u.ap.num_mcast_sta))
>>> +        goto out_free;
>>> +
>>> +    if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) &&
>>> +        test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
>>> +        goto out_free;
>>> +
>>> +    /* TODO: Handle frames requiring wifi tx status to be notified */
>>> +
>>> +    memset(info, 0, sizeof(*info));
>>> +
>>> +    if (unlikely(sdata->control_port_protocol == ehdr->h_proto)) {
>>> +        if (sdata->control_port_no_encrypt)
>>> +            info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
>>> +        info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
>>> +    }
>>> +
>>> +    if (multicast)
>>> +        info->flags |= IEEE80211_TX_CTL_NO_ACK;
>>> +
>>> +    info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
>>> +
>>> +    ieee80211_tx_stats(dev, skb->len);
>>> +
>>> +    if (sta) {
>>> +        sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
>>> + sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
>>> +    }
>>> +
>>> +    if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
>>> +        sdata = container_of(sdata->bss,
>>> +                     struct ieee80211_sub_if_data, u.ap);
>>> +
>>> +    info->control.hw_80211_encap = true;
>>> +    info->control.vif = &sdata->vif;
>>> +
>>> +    ieee80211_tx_8023(sdata, skb, skb->len, sta, false);
>>> +
>>> +    return;
>>> +
>>> +out_free:
>>> +    kfree_skb(skb);
>>> +}
>>> +
>>> +netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
>>> +                        struct net_device *dev)
>>> +{
>>> +    struct ieee80211_sub_if_data *sdata =
>>> IEEE80211_DEV_TO_SUB_IF(dev);
>>> +    struct sta_info *sta;
>>> +
>>> +    if (WARN_ON(unlikely(!sdata->hw_80211_encap))) {
>>> +        kfree_skb(skb);
>>> +        return NETDEV_TX_OK;
>>> +    }
>>> +
>>> +    if (unlikely(skb->len < ETH_HLEN)) {
>>> +        kfree_skb(skb);
>>> +        return NETDEV_TX_OK;
>>> +    }
>>> +
>>> +    if (WARN_ON(dev->ieee80211_ptr->use_4addr)) {
>>> +        kfree_skb(skb);
>>> +        return NETDEV_TX_OK;
>>> +    }
>>> +
>>> +    rcu_read_lock();
>>> +
>>> +    if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
>>> +        goto out_free;
>>> +
>>> +    ieee80211_8023_xmit(sdata, dev, sta, skb);
>>> +
>>> +    goto out;
>>> +
>>> +out_free:
>>> +    kfree_skb(skb);
>>> +out:
>>> +    rcu_read_unlock();
>>> +
>>> +    return NETDEV_TX_OK;
>>> +}
>>> +
>>>   struct sk_buff *
>>>   ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
>>>                     struct sk_buff *skb, u32 info_flags)
>>> @@ -4081,6 +4249,16 @@ static bool ieee80211_tx_pending_skb(struct
>>> ieee80211_local *local,
>>>           }
>>>           info->band = chanctx_conf->def.chan->band;
>>>           result = ieee80211_tx(sdata, NULL, skb, true, 0);
>>> +    } else if (info->control.hw_80211_encap) {
>>> +        if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
>>> +            dev_kfree_skb(skb);
>>> +            return true;
>>> +        }
>>> +
>>> +        if (IS_ERR(sta) || (sta && !sta->uploaded))
>>> +            sta = NULL;
>>> +
>>> +        result = ieee80211_tx_8023(sdata, skb, skb->len, sta, true);
>>>       } else {
>>>           struct sk_buff_head skbs;
>

2019-04-08 19:28:49

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC V3 1/2] mac80211: add hw 80211 encapsulation offloading support

On Mon, 2019-04-01 at 15:14 +0200, John Crispin wrote:
>
> + * @IEEE80211_HW_SUPPORTS_80211_ENCAP: Hardware/driver supports 802.11
> + * encap for data frames.

What's this needed for,

> +void ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable);

if you have this?

But then again this one's missing docs so I don't yet know what it's
actually doing :-)

> /* reject WEP and TKIP keys if WEP failed to initialize */
> switch (params->cipher) {
> - case WLAN_CIPHER_SUITE_WEP40:
> case WLAN_CIPHER_SUITE_TKIP:
> + /* countermeasures wont work on encap offload mode so reject
> + * TKIP keys
> + */
> + if (ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP))
> + return -EINVAL;
> +
> + /* drop through */

"fall through" I believe is needed to shut up the compiler about it.

But is this really a good idea? To do this completely only on the
support check, vs. what's currently active?

And I think it should probably just disable the encap offload, rather
than reject TKIP.

> @@ -2379,6 +2386,9 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
> if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
> ieee80211_check_fast_xmit_all(local);
>
> + if (ieee80211_is_hw_80211_encap(local))
> + return -EINVAL;

That seems misplaced? After checking fast_xmit? But again, similar as
above - IMHO it'd be better to disable encap offload.

> +void ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable)
> +{
> + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
> + struct ieee80211_local *local = sdata->local;
> +
> + if (!sdata->dev)
> + return;
> +
> + if (!ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP))
> + enable = 0;

false?

This should probably be a WARN_ON(), after all the driver is calling
this?

> + if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) &&
> + (local->hw.wiphy->frag_threshold != (u32)-1))
> + enable = 0;

The driver might need to know that you overrode it, so return the actual
value?

This is also asymmetric with my comment above - if you don't allow
enabling it while fragmentation is enabled, but at the same time don't
disable it when trying to enable fragmentation, suddenly the order of
the two operations starts to matter and that might be rather confusing.

> + if (enable) {
> + sdata->dev->netdev_ops = &ieee80211_dataif_8023_ops;

Can we really play with that pointer like that? Though I guess the two
are identical except for the TX so it probably won't really matter.

> +bool ieee80211_is_hw_80211_encap(struct ieee80211_local *local)

Maybe rename to "have_vif_with" instead of "is"? That gets a bit long,
but you get the point.

> +++ b/net/mac80211/key.c
> @@ -197,6 +197,9 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
> key->conf.keyidx,
> sta ? sta->sta.addr : bcast_addr, ret);
>
> + if (sdata->hw_80211_encap)
> + return -EINVAL;
> +

Seems that should be inside the "out_unsupported" label:

> out_unsupported:
> switch (key->conf.cipher) {
> case WLAN_CIPHER_SUITE_WEP40:

or possibly even inside the switch.

> + if (ieee80211_hw_check(hw, SUPPORTS_80211_ENCAP)) {
> + /* mac80211 always supports monitor unless we do 802.11
> + * encapsulation offloading.
> + */
> + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
> + hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
> + }

I really think we need this to be more flexible, Intel hardware for
example can do encap offload per queue, so theoretically we could even
enable/disable it per *station*, but I'd settle for per *interface*.

Certainly we don't want to suddenly find that monitor mode is limited
when we enable this?

Also, you still always have a way to disable it, right?

> /* mac80211 doesn't support more than one IBSS interface right now */
> for (i = 0; i < hw->wiphy->n_iface_combinations; i++) {
> diff --git a/net/mac80211/status.c b/net/mac80211/status.c
> index 5b9952b1caf3..8feafaab88a4 100644
> --- a/net/mac80211/status.c
> +++ b/net/mac80211/status.c
> @@ -1019,6 +1019,85 @@ void ieee80211_tx_rate_update(struct ieee80211_hw *hw,
> }
> EXPORT_SYMBOL(ieee80211_tx_rate_update);
>
> +void ieee80211_tx_status_8023(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct sk_buff *skb)

Do we really need this, vs. just using ieee80211_tx_status_noskb()?

> --- a/net/mac80211/tx.c
> +++ b/net/mac80211/tx.c
> @@ -1253,7 +1253,8 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
> (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
> return NULL;
>
> - if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
> + if (!info->control.hw_80211_encap &&
> + unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
> if ((!ieee80211_is_mgmt(hdr->frame_control) ||
> ieee80211_is_bufferable_mmpdu(hdr->frame_control) ||
> vif->type == NL80211_IFTYPE_STATION) &&

That could use a comment, I had to think about it for a while ;-)

> @@ -1400,6 +1401,7 @@ static void ieee80211_txq_enqueue(struct ieee80211_local *local,
> struct fq *fq = &local->fq;
> struct fq_tin *tin = &txqi->tin;
>
> + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);

blank line should be after this, not before :)

> @@ -2357,9 +2359,9 @@ static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb)
> skb->data[14] == WLAN_TDLS_SNAP_RFTYPE;
> }
>
> -static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
> - struct sk_buff *skb,
> - struct sta_info **sta_out)
> +int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
> + struct sk_buff *skb,
> + struct sta_info **sta_out)
> {
> struct sta_info *sta;
>
> @@ -2855,7 +2857,9 @@ void ieee80211_check_fast_xmit(struct sta_info *sta)
> struct ieee80211_chanctx_conf *chanctx_conf;
> __le16 fc;
>
> - if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT))
> + /* check for driver support and ieee80211 encap offload */
> + if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT) ||
> + sdata->hw_80211_encap)
> return;

Maybe we should treat encap offload as a sort of hardware-accelerated
fast-xmit, and enable/disable based on whether we'd normally
enable/disable fast-xmit? That way we already have all the
infrastructure for keeping track of whether we can use it or not, etc.

> /* Locking here protects both the pointer itself, and against concurrent
> @@ -3554,6 +3558,9 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
> hdr = (struct ieee80211_hdr *)skb->data;
> info = IEEE80211_SKB_CB(skb);
>
> + if (info->control.hw_80211_encap)
> + goto out;

I'm not sure this is right, you would probably still at least do the
ieee80211_tx_h_select_key() call a bit later, and possibly the A-MPDU
stuff?

You also don't assign the right vif pointer, which will probably work in
some cases but quite likely not in general.

Again, maybe treating it like fast-xmit-offload would make sense here,
and we could do this in ieee80211_xmit_fast_finish().

But that might very well not be the right model since fast-xmit is per
station, though the conditions are mostly not depending on the station,
except for auth/assoc state - which has the same issues with encap
offload though! Like ... you probably need to send EAPOL frames through
a non-encap-offload path?

> + /* TODO: Handle frames requiring wifi tx status to be notified */

:)

Maybe that means you do need ieee80211_tx_status_8023().

> + memset(info, 0, sizeof(*info));
> +
> + if (unlikely(sdata->control_port_protocol == ehdr->h_proto)) {
> + if (sdata->control_port_no_encrypt)
> + info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
> + info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
> + }
> +
> + if (multicast)
> + info->flags |= IEEE80211_TX_CTL_NO_ACK;
> +
> + info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
> +
> + ieee80211_tx_stats(dev, skb->len);
> +
> + if (sta) {
> + sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
> + sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
> + }
> +
> + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
> + sdata = container_of(sdata->bss,
> + struct ieee80211_sub_if_data, u.ap);
> +
> + info->control.hw_80211_encap = true;
> + info->control.vif = &sdata->vif;

There's lots of duplicate code here, maybe split that out? Perhaps it
should be inlined, but then it can be an inline function to keep all the
stats etc.

> +netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
> + struct net_device *dev)
> +{
> + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
> + struct sta_info *sta;
> +
> + if (WARN_ON(unlikely(!sdata->hw_80211_encap))) {

WARN_ON() contains unlikely()

> + if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
> + goto out_free;
> +
> + ieee80211_8023_xmit(sdata, dev, sta, skb);

Does that handle ERR_PTR() sta correctly?

> @@ -4081,6 +4249,16 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
> }
> info->band = chanctx_conf->def.chan->band;
> result = ieee80211_tx(sdata, NULL, skb, true, 0);
> + } else if (info->control.hw_80211_encap) {
> + if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
> + dev_kfree_skb(skb);
> + return true;
> + }
> +
> + if (IS_ERR(sta) || (sta && !sta->uploaded))
> + sta = NULL;
> +
> + result = ieee80211_tx_8023(sdata, skb, skb->len, sta, true);

This doesn't seem to make sense, how can you TX to a non-STA with encap
offload, you don't even know where to send the frame then?

johannes



2019-04-10 07:28:35

by John Crispin

[permalink] [raw]
Subject: Re: [RFC V3 1/2] mac80211: add hw 80211 encapsulation offloading support


On 02/04/2019 11:50, Toke Høiland-Jørgensen wrote:
> ieee80211_queue_skb() calls ieee80211_get_txq() which treats skb->data
> like it contains an 802.11 header. That is probably not the intention
> here, is it?
>
> I guess we could augment the TXQ stuctures to also handle 802.3 frames
> (and introduce ieee80211_queue_skb_8023())? Or would it be better to
> have a qdisc on 802.3-mode interfaces and push packets back to that? I
> guess we'd still benefit from per-station queueing of packets even if
> they are Ethernet frames, which would mean amending the TXQs would be
> better?
>
> -Toke

Hi Toke

This patch guards the usage of the hdr pointer. hdr is only used for non
data frames which are not relevant when doing encap mode. The code will
then drop into the same path as for 80211 encapsulated frames. IMHO the
patch is correct and we do not need an additional
ieee80211_queue_skb_8023()  to benefit from per station TXQs. Let me
know if i missed something.

    John



2019-04-10 10:16:32

by Toke Høiland-Jørgensen

[permalink] [raw]
Subject: Re: [RFC V3 1/2] mac80211: add hw 80211 encapsulation offloading support

John Crispin <[email protected]> writes:

> On 02/04/2019 11:50, Toke Høiland-Jørgensen wrote:
>> ieee80211_queue_skb() calls ieee80211_get_txq() which treats skb->data
>> like it contains an 802.11 header. That is probably not the intention
>> here, is it?
>>
>> I guess we could augment the TXQ stuctures to also handle 802.3 frames
>> (and introduce ieee80211_queue_skb_8023())? Or would it be better to
>> have a qdisc on 802.3-mode interfaces and push packets back to that? I
>> guess we'd still benefit from per-station queueing of packets even if
>> they are Ethernet frames, which would mean amending the TXQs would be
>> better?
>>
>> -Toke
>
> Hi Toke
>
> This patch guards the usage of the hdr pointer. hdr is only used for non
> data frames which are not relevant when doing encap mode. The code will
> then drop into the same path as for 80211 encapsulated frames. IMHO the
> patch is correct and we do not need an additional
> ieee80211_queue_skb_8023()  to benefit from per station TXQs. Let me
> know if i missed something.

Ah, right, sorry, missed the change to ieee80211_get_txq() :)

-Toke