From: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>
To: <johannes@sipsolutions.net>
Cc: <linux-wireless@vger.kernel.org>,
Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>
Subject: [RFC 2/3] mac80211: Implement data xmit for 802.11 encap offload
Date: Thu, 15 Dec 2016 11:30:07 +0530 [thread overview]
Message-ID: <1481781608-5181-3-git-send-email-vthiagar@qti.qualcomm.com> (raw)
In-Reply-To: <1481781608-5181-1-git-send-email-vthiagar@qti.qualcomm.com>
Driver (or hw) supporting 802.11 encapsulation offload
for data frames can make use of this new xmit path.
This patch defines new ndo_ops, all these callbacks are same as
ieee80211_dataif_ops other than ndo_start_xmit() which does
minimal processing leaving 802.11 encap related to driver.
This patch makes netdev_ops registration dynamic based on the
interface type, at any time the netdev_ops of netdev will be
assigned to either the ndo_ops defined to do 802.11 encap or
the ones defined for 802.11 encap offload. There is a new hw
config notification, IEEE80211_CONF_CHANGE_80211_HDR_OFFL, to make
the driver aware of any configuration change in 802.11 encap/decap
offload.
There is a field, no_80211_encap, added to ieee80211_tx_info:control
to mark if the 802.11 encapsulation is offloaded to driver.
There is also a new callback for tx completion status indication
to handle data frames using 802.11 encap offload. Currently ath10k
fw is capable of doing 802.11 encapsulation/decapsulationi offload.
With the corresponding driver changes, using 802.11 encap/decap offload
might improve performance.
Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>
---
include/net/mac80211.h | 33 ++++++-
net/mac80211/cfg.c | 8 ++
net/mac80211/ieee80211_i.h | 12 +++
net/mac80211/iface.c | 147 +++++++++++++++++++++++++++++
net/mac80211/key.c | 16 +++-
net/mac80211/main.c | 3 +
net/mac80211/status.c | 83 ++++++++++++++++-
net/mac80211/tx.c | 225 ++++++++++++++++++++++++++++++++++++++++++++-
8 files changed, 519 insertions(+), 8 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 1e3c8b5..225abaa 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -910,7 +910,12 @@ struct ieee80211_tx_info {
};
struct ieee80211_key_conf *hw_key;
u32 flags;
- /* 4 bytes free */
+ /* XXX: This frame is not encaptulated with 802.11
+ * header. Should this be added to %IEEE80211_TX_CTRL_*
+ * flags?.
+ */
+ bool no_80211_encap;
+ /* 3 bytes free */
} control;
struct {
u64 cookie;
@@ -1269,6 +1274,8 @@ enum ieee80211_conf_flags {
* @IEEE80211_CONF_CHANGE_SMPS: Spatial multiplexing powersave mode changed
* Note that this is only valid if channel contexts are not used,
* otherwise each channel context has the number of chains listed.
+ * @IEEE80211_CONF_CHANGE_80211_HDR_OFFL: Offload configuration
+ * implementing 802.11 encap/decap for data frames changed.
*/
enum ieee80211_conf_changed {
IEEE80211_CONF_CHANGE_SMPS = BIT(1),
@@ -1279,6 +1286,7 @@ enum ieee80211_conf_changed {
IEEE80211_CONF_CHANGE_CHANNEL = BIT(6),
IEEE80211_CONF_CHANGE_RETRY_LIMITS = BIT(7),
IEEE80211_CONF_CHANGE_IDLE = BIT(8),
+ IEEE80211_CONF_CHANGE_80211_HDR_OFFL = BIT(9),
};
/**
@@ -1333,6 +1341,9 @@ enum ieee80211_smps_mode {
* configured for an HT channel.
* Note that this is only valid if channel contexts are not used,
* otherwise each channel context has the number of chains listed.
+ *
+ * @encap_decap_80211_offloaded: Whether 802.11 header encap/decap offload
+ * is enabled
*/
struct ieee80211_conf {
u32 flags;
@@ -1346,6 +1357,7 @@ struct ieee80211_conf {
struct cfg80211_chan_def chandef;
bool radar_enabled;
enum ieee80211_smps_mode smps_mode;
+ bool encap_decap_80211_offloaded;
};
/**
@@ -4178,6 +4190,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
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 47e99ab8..0e53873 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -107,6 +107,10 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
}
}
+ ieee80211_if_check_80211_hdr_offl(sdata,
+ params ? params->use_4addr : false,
+ true);
+
return 0;
}
@@ -2116,6 +2120,10 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
ieee80211_check_fast_xmit_all(local);
+ if (!local->ops->set_frag_threshold &&
+ local->data_80211_hdr_offloaded)
+ return -EINVAL;
+
err = drv_set_frag_threshold(local, wiphy->frag_threshold);
if (err) {
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index f56d342..8d6abad 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1373,6 +1373,8 @@ struct ieee80211_local {
/* TDLS channel switch */
struct work_struct tdls_chsw_work;
struct sk_buff_head skb_queue_tdls_chsw;
+
+ bool data_80211_hdr_offloaded;
};
static inline struct ieee80211_sub_if_data *
@@ -1641,6 +1643,10 @@ 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);
+void ieee80211_if_check_80211_hdr_offl(struct ieee80211_sub_if_data *sdata,
+ bool use_4addr, bool add);
+void ieee80211_if_config_80211_hdr_offl(struct ieee80211_local *local,
+ bool enable_80211_hdr_offload);
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);
@@ -1668,6 +1674,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_8023_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
void __ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev,
u32 info_flags);
@@ -1822,6 +1830,10 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, int tid,
enum nl80211_band band);
+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 b123a9e..d5f6649 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -698,6 +698,11 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
rcu_assign_pointer(local->p2p_sdata, sdata);
}
+ if (local->open_count == 0 && local->data_80211_hdr_offloaded) {
+ local->hw.conf.encap_decap_80211_offloaded = true;
+ hw_reconf_flags |= IEEE80211_CONF_CHANGE_80211_HDR_OFFL;
+ }
+
/*
* set_multicast_list will be invoked by the networking core
* which will check whether any increments here were done in
@@ -1148,6 +1153,18 @@ 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_8023_start_xmit,
+ .ndo_set_rx_mode = ieee80211_set_multicast_list,
+ .ndo_change_mtu = ieee80211_change_mtu,
+ .ndo_set_mac_address = ieee80211_change_mac,
+ .ndo_select_queue = ieee80211_netdev_select_queue,
+ .ndo_get_stats64 = ieee80211_get_stats64,
+};
+
static u16 ieee80211_monitor_select_queue(struct net_device *dev,
struct sk_buff *skb,
void *accel_priv,
@@ -1703,6 +1720,132 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
mutex_unlock(&local->iflist_mtx);
}
+void ieee80211_if_config_80211_hdr_offl(struct ieee80211_local *local,
+ bool enable_80211_hdr_offl)
+{
+ struct ieee80211_sub_if_data *sdata;
+ unsigned long flags;
+ int n_acs = IEEE80211_NUM_ACS;
+ int ac;
+
+ ASSERT_RTNL();
+
+ if (!ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP_DECAP) ||
+ !(ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)))
+ return;
+
+ if (local->hw.wiphy->frag_threshold != (u32)-1 &&
+ !local->ops->set_frag_threshold)
+ return;
+
+ mutex_lock(&local->iflist_mtx);
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!sdata->dev)
+ continue;
+
+ netif_tx_stop_all_queues(sdata->dev);
+
+ if (enable_80211_hdr_offl)
+ sdata->dev->netdev_ops = &ieee80211_dataif_8023_ops;
+ else
+ sdata->dev->netdev_ops = &ieee80211_dataif_ops;
+ }
+
+ mutex_unlock(&local->iflist_mtx);
+
+ local->data_80211_hdr_offloaded = enable_80211_hdr_offl;
+
+ if (local->started) {
+ if (enable_80211_hdr_offl)
+ local->hw.conf.encap_decap_80211_offloaded = true;
+ else
+ local->hw.conf.encap_decap_80211_offloaded = false;
+ ieee80211_hw_config(local,
+ IEEE80211_CONF_CHANGE_80211_HDR_OFFL);
+ }
+
+ mutex_lock(&local->iflist_mtx);
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!sdata->dev)
+ continue;
+
+ if (local->hw.queues < IEEE80211_NUM_ACS)
+ n_acs = 1;
+
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ if (sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE ||
+ (local->queue_stop_reasons[sdata->vif.cab_queue] == 0 &&
+ skb_queue_empty(&local->pending[sdata->vif.cab_queue]))) {
+ for (ac = 0; ac < n_acs; ac++) {
+ int ac_queue = sdata->vif.hw_queue[ac];
+
+ if (local->queue_stop_reasons[ac_queue] == 0 &&
+ skb_queue_empty(&local->pending[ac_queue]))
+ netif_start_subqueue(sdata->dev, ac);
+ }
+ }
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+ }
+
+ mutex_unlock(&local->iflist_mtx);
+}
+
+void ieee80211_if_check_80211_hdr_offl(struct ieee80211_sub_if_data *sdata,
+ bool use_4addr, bool add)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_sub_if_data *iface;
+ bool supported = false;
+ bool switch_to_80211 = false;
+ int iface_num = 0;
+ int ret;
+
+ ASSERT_RTNL();
+
+ /* TODO: Extend this function to switch data tx/rx mode upon
+ * deletion of an interface.
+ */
+ if (!add)
+ return;
+
+ if (!ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP_DECAP) ||
+ !(ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)))
+ return;
+
+ if (local->hw.wiphy->frag_threshold != (u32)-1 &&
+ !local->ops->set_frag_threshold)
+ return;
+
+ ret = drv_get_vif_80211_hdr_offload(local, sdata, use_4addr,
+ &supported);
+ if (ret)
+ return;
+
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(iface, &local->interfaces, list) {
+ iface_num++;
+ }
+ mutex_unlock(&local->iflist_mtx);
+
+ if (WARN_ON(iface_num == 0))
+ return;
+
+ switch_to_80211 = local->data_80211_hdr_offloaded && !supported;
+
+ if (switch_to_80211) {
+ ieee80211_if_config_80211_hdr_offl(local, false);
+ return;
+ }
+
+ if (!supported || !sdata->dev)
+ return;
+
+ sdata->dev->netdev_ops = &ieee80211_dataif_8023_ops;
+ local->data_80211_hdr_offloaded = true;
+}
+
int ieee80211_if_add(struct ieee80211_local *local, const char *name,
unsigned char name_assign_type,
struct wireless_dev **new_wdev, enum nl80211_iftype type,
@@ -1866,6 +2009,10 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
list_add_tail_rcu(&sdata->list, &local->interfaces);
mutex_unlock(&local->iflist_mtx);
+ ieee80211_if_check_80211_hdr_offl(sdata,
+ params ? params->use_4addr : false,
+ true);
+
if (new_wdev)
*new_wdev = &sdata->wdev;
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index edd6f29..efcb1c4 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -208,13 +208,25 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
case WLAN_CIPHER_SUITE_GCMP_256:
/* all of these we can do in software - if driver can */
if (ret == 1)
- return 0;
+ goto check_8023_txrx;
if (ieee80211_hw_check(&key->local->hw, SW_CRYPTO_CONTROL))
return -EINVAL;
- return 0;
+ goto check_8023_txrx;
default:
return -EINVAL;
}
+
+check_8023_txrx:
+ /* When sw crypto is enabled make sure data tx/rx happens
+ * in 802.11 format.
+ */
+ if (key->local->data_80211_hdr_offloaded) {
+ rtnl_lock();
+ ieee80211_if_config_80211_hdr_offl(key->local, false);
+ rtnl_unlock();
+ }
+
+ return 0;
}
static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 2095d7c..a1dc809 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -164,6 +164,9 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
might_sleep();
+ if (!ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP_DECAP))
+ changed &= ~IEEE80211_CONF_CHANGE_80211_HDR_OFFL;
+
if (!local->use_chanctx)
changed |= ieee80211_hw_conf_chan(local);
else
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index c6d5c72..804fd53 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -506,12 +506,14 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
struct sk_buff *skb, bool dropped)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ieee80211_hdr *hdr;
bool acked = info->flags & IEEE80211_TX_STAT_ACK;
if (dropped)
acked = false;
+ hdr = (void *)skb->data;
+
if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) {
struct ieee80211_sub_if_data *sdata;
@@ -945,6 +947,85 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
}
EXPORT_SYMBOL(ieee80211_tx_status);
+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_DECAP)))
+ goto skip_stats_update;
+
+ sdata = vif_to_sdata(info->control.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 91461c4..d73cf79 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1485,6 +1485,7 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
struct sk_buff *skb = NULL;
struct fq *fq = &local->fq;
struct fq_tin *tin = &txqi->tin;
+ struct ieee80211_tx_info *info;
spin_lock_bh(&fq->lock);
@@ -1497,11 +1498,15 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
ieee80211_set_skb_vif(skb, txqi);
+ info = IEEE80211_SKB_CB(skb);
+
+ if (info->control.no_80211_encap)
+ goto out;
+
hdr = (struct ieee80211_hdr *)skb->data;
if (txq->sta && ieee80211_is_data_qos(hdr->frame_control)) {
struct sta_info *sta = container_of(txq->sta, struct sta_info,
sta);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
hdr->seq_ctrl = ieee80211_tx_next_seq(sta, txq->tid);
if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags))
@@ -2226,9 +2231,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;
@@ -3433,6 +3438,208 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}
+static bool ieee80211_tx_8023(struct ieee80211_local *local,
+ struct sk_buff *skb, int led_len,
+ struct sta_info *sta,
+ bool txpending)
+{
+ struct ieee80211_tx_control control = {};
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_vif *vif = info->control.vif;
+ struct ieee80211_sta *pubsta = NULL;
+ struct ieee80211_txq *txq = NULL;
+ struct fq *fq = &local->fq;
+ unsigned long flags;
+ int q = info->hw_queue;
+
+ if (sta)
+ pubsta = &sta->sta;
+
+ if (pubsta) {
+ u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+
+ txq = pubsta->txq[tid];
+ } else if (vif) {
+ txq = vif->txq;
+ }
+
+ if (txq) {
+ struct txq_info *txqi = to_txq_info(txq);
+
+ info->control.vif = vif;
+
+ spin_lock_bh(&fq->lock);
+ ieee80211_txq_enqueue(local, txqi, skb);
+ spin_unlock_bh(&fq->lock);
+
+ drv_wake_tx_queue(local, txqi);
+
+ 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);
+
+ control.sta = pubsta;
+
+ drv_tx(local, &control, skb);
+
+ /* TODO: Update throughput led trigger with the number of tx bytes */
+
+ 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;
+ u8 ra_addr[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ if (IS_ERR(sta) || (sta && !sta->uploaded))
+ sta = NULL;
+
+ /* XXX: Add a generic helper for this */
+ if (sdata->vif.type == NL80211_IFTYPE_AP ||
+ sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC)
+ ether_addr_copy(ra_addr, ehdr->h_dest);
+
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ tdls_peer = test_sta_flag(sta, WLAN_STA_TDLS_PEER);
+ if (tdls_peer)
+ memcpy(ra_addr, skb->data, ETH_ALEN);
+ else
+ memcpy(ra_addr, sdata->u.mgd.bssid, ETH_ALEN);
+ }
+
+ if (is_zero_ether_addr(ra_addr))
+ goto out_free;
+
+ multicast = is_multicast_ether_addr(ra_addr);
+
+ if (sta)
+ authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
+
+ /* XXX: Should we add new txrx stats for 802.3 to update stats
+ * like if the frame is dropped due to unathourized port,
+ * just like the ones available in tx_handlers?.
+ */
+
+ 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 */
+ if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)
+ goto out_free;
+
+ 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.no_80211_encap = true;
+
+ info->control.vif = &sdata->vif;
+
+ ieee80211_tx_8023(local, skb, skb->len, sta, false);
+
+ return;
+
+out_free:
+ kfree_skb(skb);
+}
+
+netdev_tx_t ieee80211_subif_8023_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
+
+ if (WARN_ON(unlikely(!local->data_80211_hdr_offloaded))) {
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ if (unlikely(skb->len < ETH_HLEN)) {
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ /* TODO: Extend it for Adhoc interface type */
+ if (WARN_ON(dev->ieee80211_ptr->use_4addr ||
+ (sdata->vif.type != NL80211_IFTYPE_STATION &&
+ sdata->vif.type != NL80211_IFTYPE_AP &&
+ sdata->vif.type != NL80211_IFTYPE_AP_VLAN))) {
+ 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)
@@ -3511,6 +3718,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);
+ } else if (info->control.no_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(local, skb, skb->len, sta, true);
} else {
struct sk_buff_head skbs;
--
1.9.1
next prev parent reply other threads:[~2016-12-15 6:00 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-12-15 6:00 [RFC 0/3] Add new data path for ethernet frame format Vasanthakumar Thiagarajan
2016-12-15 6:00 ` [RFC 1/3] mac80211: Add provision for 802.11 encap/decap offload Vasanthakumar Thiagarajan
2016-12-15 9:16 ` Johannes Berg
2016-12-15 10:43 ` Thiagarajan, Vasanthakumar
2016-12-16 9:30 ` Johannes Berg
2016-12-15 6:00 ` Vasanthakumar Thiagarajan [this message]
2016-12-15 9:29 ` [RFC 2/3] mac80211: Implement data xmit for 802.11 encap offload Johannes Berg
2016-12-15 12:01 ` Thiagarajan, Vasanthakumar
2016-12-15 13:32 ` Felix Fietkau
2016-12-15 13:53 ` Johannes Berg
2016-12-16 5:37 ` Thiagarajan, Vasanthakumar
2016-12-16 9:25 ` Johannes Berg
2016-12-19 11:45 ` Kalle Valo
2016-12-19 12:02 ` Thiagarajan, Vasanthakumar
2016-12-15 6:00 ` [RFC 3/3] mac80211: Add receive path for ethernet frame format Vasanthakumar Thiagarajan
2016-12-15 9:38 ` Johannes Berg
2016-12-16 6:47 ` Thiagarajan, Vasanthakumar
2016-12-16 9:13 ` Johannes Berg
2016-12-16 9:14 ` Johannes Berg
2016-12-15 9:08 ` [RFC 0/3] Add new data " Johannes Berg
2016-12-15 10:03 ` Thiagarajan, Vasanthakumar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1481781608-5181-3-git-send-email-vthiagar@qti.qualcomm.com \
--to=vthiagar@qti.qualcomm.com \
--cc=johannes@sipsolutions.net \
--cc=linux-wireless@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).