On Wed, 2019-04-10 at 09:35 +0200, John Crispin wrote:
> The driver
> needs to enable the support on a per vif basis if it finds that all
> pre-reqs are meet.
> + * @IEEE80211_HW_SUPPORTS_80211_ENCAP: Hardware/driver supports 802.11
> + * encap for data frames.
I'm still not sure I can reconcile these ... why do you need the latter
if the driver has to enable per vif anyway?
> +int ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable);
Add documentation please. Would be better to return bool too, to be
symmetric.
> +++ b/net/mac80211/cfg.c
> @@ -2379,6 +2379,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;
Why not do this like TKIP and disable encap?
> +int 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 (enable == sdata->hw_80211_encap)
> + return enable;
It feels like this is missing some locking? Or at least a lock
assertion, if it's always called with a certain lock held already.
> + if (!sdata->dev)
> + return 0;
> +
> + if (!ieee80211_hw_check(&local->hw, SUPPORTS_80211_ENCAP))
> + enable = 0;
> +
> + switch (vif->type) {
> + case NL80211_IFTYPE_STATION:
> + if (sdata->u.mgd.use_4addr)
> + enable = 0;
> + break;
> + case NL80211_IFTYPE_AP_VLAN:
> + if (sdata->wdev.use_4addr)
> + enable = 0;
> + break;
> + default:
> + break;
> + }
> +
> + if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) &&
> + (local->hw.wiphy->frag_threshold != (u32)-1))
> + enable = 0;
Shouldn't there be some kind of TKIP check here?
> +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);
This might be better with RCU, though for proper usage you'd actually
have to call it with the mutex held, otherwise it can change while
you're iterating ...
> + /* TKIP countermeasures wont work on encap offload mode */
> + if (key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)
> + ieee80211_set_hw_80211_encap(&sdata->vif, 0);
false.
> @@ -197,6 +201,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;
Yeah, and this means you're also missing a "do we have a SW crypto key
right now" check above in ieee80211_set_hw_80211_encap().
> + 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);
> + }
Ok, maybe this addresses my question above about why we need both -
though it's not really clear what happens if the driver actually does
want to still enable monitor mode - which certainly we (iwlwifi) would.
> +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);
no need for !! with bool :-)
> + /* check for driver support and ieee80211 encap offload */
> + if (!ieee80211_hw_check(&local->hw, SUPPORT_FAST_XMIT) ||
> + sdata->hw_80211_encap)
> return;
That comment isn't very useful? :-)
> @@ -3573,6 +3576,9 @@ struct sk_buff *ieee80211_tx_dequeue(struct
> ieee80211_hw *hw,
> goto begin;
> }
>
> + if (info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP)
> + goto encap_out;
> +
> if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags))
> info->flags |= IEEE80211_TX_CTL_AMPDU;
Wouldn't you still want this flag, perhaps?
Not with ath10k I guess, which offloads aggregation sessions, but still?
That's not necessarily a requirement for encap offload, is it?
> + 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)]++;
> + }
This is something to think about, aren't we usually doing that after
encapsulation? So the counters will be off a bit now?
> + if (WARN_ON(unlikely(!sdata->hw_80211_encap))) {
I feel I'm repeating myself - WARN_ON() includes unlikely().
johannes