Skip to content

Commit

Permalink
mac80211: add 802.11 encapsulation offloading support
Browse files Browse the repository at this point in the history
This patch adds a new transmit path for hardware that supports 802.11
encapsulation offloading. In those cases 802.3 frames get passed
directly to the driver allowing the hardware to handle the encapsulation.
Some features such as monitor mode and TKIP would break when encapsulation
offloading is enabled. If any of these get enabled, the code will alwyas
fallback to the normal sw encapsulation data path.

The patch defines a secondary netdev_ops struct that the device gets
assigned 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 <vthiagar@qti.qualcomm.com>
Signed-off-by: John Crispin <john@phrozen.org>
Link: https://lore.kernel.org/r/20191125100438.16539-1-john@phrozen.org
[reword comments, remove SUPPORTS_80211_ENCAP HW flag, minor cleanups]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
blogic authored and jmberg-intel committed Dec 13, 2019
1 parent 9bcb084 commit 50ff477
Show file tree
Hide file tree
Showing 6 changed files with 356 additions and 4 deletions.
32 changes: 32 additions & 0 deletions include/net/mac80211.h
Expand Up @@ -826,6 +826,7 @@ enum mac80211_tx_control_flags {
IEEE80211_TX_CTRL_AMSDU = BIT(3),
IEEE80211_TX_CTRL_FAST_XMIT = BIT(4),
IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP = BIT(5),
IEEE80211_TX_CTRL_HW_80211_ENCAP = BIT(6),
};

/*
Expand Down Expand Up @@ -4660,6 +4661,26 @@ static inline void ieee80211_tx_status_ni(struct ieee80211_hw *hw,
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 and all
* calls in the same tx status family.
*
* @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
*
Expand Down Expand Up @@ -6480,5 +6501,16 @@ u32 ieee80211_calc_rx_airtime(struct ieee80211_hw *hw,
u32 ieee80211_calc_tx_airtime(struct ieee80211_hw *hw,
struct ieee80211_tx_info *info,
int len);
/**
* ieee80211_set_hw_80211_encap - enable hardware encapsulation offloading.
*
* This function is used to notify mac80211 that a vif can be passed raw 802.3
* frames. The driver needs to then handle the 802.11 encapsulation inside the
* hardware or firmware.
*
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
* @enable: indicate if the feature should be turned on or off
*/
bool ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable);

#endif /* MAC80211_H */
9 changes: 9 additions & 0 deletions net/mac80211/ieee80211_i.h
Expand Up @@ -984,6 +984,8 @@ struct ieee80211_sub_if_data {
} debugfs;
#endif

bool hw_80211_encap;

/* must be last, dynamically sized area in this! */
struct ieee80211_vif vif;
};
Expand Down Expand Up @@ -1762,6 +1764,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,
Expand Down Expand Up @@ -1948,6 +1952,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,
Expand Down
67 changes: 67 additions & 0 deletions net/mac80211/iface.c
Expand Up @@ -1205,6 +1205,72 @@ static const struct net_device_ops ieee80211_monitorif_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,
};

static void __ieee80211_set_hw_80211_encap(struct ieee80211_sub_if_data *sdata,
bool enable)
{
sdata->dev->netdev_ops = enable ? &ieee80211_dataif_8023_ops :
&ieee80211_dataif_ops;
sdata->hw_80211_encap = enable;
}

bool 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;
struct ieee80211_sub_if_data *iter;
struct ieee80211_key *key;

mutex_lock(&local->iflist_mtx);
list_for_each_entry(iter, &local->interfaces, list) {
struct ieee80211_sub_if_data *disable = NULL;

if (vif->type == NL80211_IFTYPE_MONITOR) {
disable = iter;
__ieee80211_set_hw_80211_encap(iter, false);
} else if (iter->vif.type == NL80211_IFTYPE_MONITOR) {
disable = sdata;
enable = false;
}
if (disable)
sdata_dbg(disable,
"disable hw 80211 encap due to mon co-exist\n");
}
mutex_unlock(&local->iflist_mtx);

if (enable == sdata->hw_80211_encap)
return enable;

if (!sdata->dev)
return false;

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

mutex_lock(&sdata->local->key_mtx);
list_for_each_entry(key, &sdata->key_list, list) {
if (key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)
enable = false;
}
mutex_unlock(&sdata->local->key_mtx);

__ieee80211_set_hw_80211_encap(sdata, enable);

return enable;
}
EXPORT_SYMBOL(ieee80211_set_hw_80211_encap);

static void ieee80211_if_free(struct net_device *dev)
{
free_percpu(dev->tstats);
Expand Down Expand Up @@ -1404,6 +1470,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) {
Expand Down
11 changes: 11 additions & 0 deletions net/mac80211/key.c
Expand Up @@ -177,6 +177,13 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
}
}

/* TKIP countermeasures don't work in encap offload mode */
if (key->conf.cipher == WLAN_CIPHER_SUITE_TKIP &&
sdata->hw_80211_encap) {
sdata_dbg(sdata, "TKIP is not allowed in hw 80211 encap mode\n");
return -EINVAL;
}

ret = drv_set_key(key->local, SET_KEY, sdata,
sta ? &sta->sta : NULL, &key->conf);

Expand All @@ -203,6 +210,10 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
key->conf.keyidx,
sta ? sta->sta.addr : bcast_addr, ret);

/* cannot do software crypto with encapsulation offload */
if (sdata->hw_80211_encap)
return -EINVAL;

out_unsupported:
switch (key->conf.cipher) {
case WLAN_CIPHER_SUITE_WEP40:
Expand Down
71 changes: 71 additions & 0 deletions net/mac80211/status.c
Expand Up @@ -1198,6 +1198,77 @@ 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;

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))
goto counters_update;

if (IS_ERR(sta))
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);
}
}

counters_update:
rcu_read_unlock();
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);
Expand Down

0 comments on commit 50ff477

Please sign in to comment.