Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

wifi: mac80211: add support for restricting netdev features per vif

This can be used to selectively disable feature flags for checksum offload,
scatter/gather or GSO by changing vif->netdev_features.
Removing features from vif->netdev_features does not affect the netdev
features themselves, but instead fixes up skbs in the tx path so that the
offloads are not needed in the driver.

Aside from making it easier to deal with vif type based hardware limitations,
this also makes it possible to optimize performance on hardware without native
GSO support by declaring GSO support in hw->netdev_features and removing it
from vif->netdev_features. This allows mac80211 to handle GSO segmentation
after the sta lookup, but before itxq enqueue, thus reducing the number of
unnecessary sta lookups, as well as some other per-packet processing.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
Link: https://lore.kernel.org/r/20221010094338.78070-1-nbd@nbd.name
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Felix Fietkau and committed by
Johannes Berg
7d360f60 209d70d3

+233 -134
+10 -6
include/net/fq_impl.h
··· 200 200 fq_skb_free_t free_func) 201 201 { 202 202 struct fq_flow *flow; 203 + struct sk_buff *next; 203 204 bool oom; 204 205 205 206 lockdep_assert_held(&fq->lock); ··· 215 214 } 216 215 217 216 flow->tin = tin; 218 - flow->backlog += skb->len; 219 - tin->backlog_bytes += skb->len; 220 - tin->backlog_packets++; 221 - fq->memory_usage += skb->truesize; 222 - fq->backlog++; 217 + skb_list_walk_safe(skb, skb, next) { 218 + skb_mark_not_on_list(skb); 219 + flow->backlog += skb->len; 220 + tin->backlog_bytes += skb->len; 221 + tin->backlog_packets++; 222 + fq->memory_usage += skb->truesize; 223 + fq->backlog++; 224 + __skb_queue_tail(&flow->queue, skb); 225 + } 223 226 224 227 if (list_empty(&flow->flowchain)) { 225 228 flow->deficit = fq->quantum; ··· 231 226 &tin->new_flows); 232 227 } 233 228 234 - __skb_queue_tail(&flow->queue, skb); 235 229 oom = (fq->memory_usage > fq->memory_limit); 236 230 while (fq->backlog > fq->limit || oom) { 237 231 flow = fq_find_fattest_flow(fq);
+5
include/net/mac80211.h
··· 1807 1807 * @addr: address of this interface 1808 1808 * @p2p: indicates whether this AP or STA interface is a p2p 1809 1809 * interface, i.e. a GO or p2p-sta respectively 1810 + * @netdev_features: tx netdev features supported by the hardware for this 1811 + * vif. mac80211 initializes this to hw->netdev_features, and the driver 1812 + * can mask out specific tx features. mac80211 will handle software fixup 1813 + * for masked offloads (GSO, CSUM) 1810 1814 * @driver_flags: flags/capabilities the driver has for this interface, 1811 1815 * these need to be set (or cleared) when the interface is added 1812 1816 * or, if supported by the driver, the interface type is changed ··· 1852 1848 1853 1849 struct ieee80211_txq *txq; 1854 1850 1851 + netdev_features_t netdev_features; 1855 1852 u32 driver_flags; 1856 1853 u32 offload_flags; 1857 1854
+1
net/mac80211/iface.c
··· 2178 2178 ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE; 2179 2179 ndev->hw_features |= ndev->features & 2180 2180 MAC80211_SUPPORTED_FEATURES_TX; 2181 + sdata->vif.netdev_features = local->hw.netdev_features; 2181 2182 2182 2183 netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops); 2183 2184
+217 -128
net/mac80211/tx.c
··· 1355 1355 1356 1356 static void ieee80211_set_skb_enqueue_time(struct sk_buff *skb) 1357 1357 { 1358 - IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time(); 1358 + struct sk_buff *next; 1359 + codel_time_t now = codel_get_time(); 1360 + 1361 + skb_list_walk_safe(skb, skb, next) 1362 + IEEE80211_SKB_CB(skb)->control.enqueue_time = now; 1359 1363 } 1360 1364 1361 1365 static u32 codel_skb_len_func(const struct sk_buff *skb) ··· 3582 3578 return TX_CONTINUE; 3583 3579 } 3584 3580 3581 + static netdev_features_t 3582 + ieee80211_sdata_netdev_features(struct ieee80211_sub_if_data *sdata) 3583 + { 3584 + if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN) 3585 + return sdata->vif.netdev_features; 3586 + 3587 + if (!sdata->bss) 3588 + return 0; 3589 + 3590 + sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); 3591 + return sdata->vif.netdev_features; 3592 + } 3593 + 3594 + static struct sk_buff * 3595 + ieee80211_tx_skb_fixup(struct sk_buff *skb, netdev_features_t features) 3596 + { 3597 + if (skb_is_gso(skb)) { 3598 + struct sk_buff *segs; 3599 + 3600 + segs = skb_gso_segment(skb, features); 3601 + if (!segs) 3602 + return skb; 3603 + if (IS_ERR(segs)) 3604 + goto free; 3605 + 3606 + consume_skb(skb); 3607 + return segs; 3608 + } 3609 + 3610 + if (skb_needs_linearize(skb, features) && __skb_linearize(skb)) 3611 + goto free; 3612 + 3613 + if (skb->ip_summed == CHECKSUM_PARTIAL) { 3614 + int ofs = skb_checksum_start_offset(skb); 3615 + 3616 + if (skb->encapsulation) 3617 + skb_set_inner_transport_header(skb, ofs); 3618 + else 3619 + skb_set_transport_header(skb, ofs); 3620 + 3621 + if (skb_csum_hwoffload_help(skb, features)) 3622 + goto free; 3623 + } 3624 + 3625 + skb_mark_not_on_list(skb); 3626 + return skb; 3627 + 3628 + free: 3629 + kfree_skb(skb); 3630 + return NULL; 3631 + } 3632 + 3633 + static void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, 3634 + struct sta_info *sta, 3635 + struct ieee80211_fast_tx *fast_tx, 3636 + struct sk_buff *skb, u8 tid, bool ampdu) 3637 + { 3638 + struct ieee80211_local *local = sdata->local; 3639 + struct ieee80211_hdr *hdr = (void *)fast_tx->hdr; 3640 + struct ieee80211_tx_info *info; 3641 + struct ieee80211_tx_data tx; 3642 + ieee80211_tx_result r; 3643 + int hw_headroom = sdata->local->hw.extra_tx_headroom; 3644 + int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2); 3645 + struct ethhdr eth; 3646 + 3647 + skb = skb_share_check(skb, GFP_ATOMIC); 3648 + if (unlikely(!skb)) 3649 + return; 3650 + 3651 + if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) && 3652 + ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb)) 3653 + return; 3654 + 3655 + /* will not be crypto-handled beyond what we do here, so use false 3656 + * as the may-encrypt argument for the resize to not account for 3657 + * more room than we already have in 'extra_head' 3658 + */ 3659 + if (unlikely(ieee80211_skb_resize(sdata, skb, 3660 + max_t(int, extra_head + hw_headroom - 3661 + skb_headroom(skb), 0), 3662 + ENCRYPT_NO))) 3663 + goto free; 3664 + 3665 + memcpy(&eth, skb->data, ETH_HLEN - 2); 3666 + hdr = skb_push(skb, extra_head); 3667 + memcpy(skb->data, fast_tx->hdr, fast_tx->hdr_len); 3668 + memcpy(skb->data + fast_tx->da_offs, eth.h_dest, ETH_ALEN); 3669 + memcpy(skb->data + fast_tx->sa_offs, eth.h_source, ETH_ALEN); 3670 + 3671 + info = IEEE80211_SKB_CB(skb); 3672 + memset(info, 0, sizeof(*info)); 3673 + info->band = fast_tx->band; 3674 + info->control.vif = &sdata->vif; 3675 + info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT | 3676 + IEEE80211_TX_CTL_DONTFRAG | 3677 + (ampdu ? IEEE80211_TX_CTL_AMPDU : 0); 3678 + info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT | 3679 + u32_encode_bits(IEEE80211_LINK_UNSPECIFIED, 3680 + IEEE80211_TX_CTRL_MLO_LINK); 3681 + 3682 + #ifdef CONFIG_MAC80211_DEBUGFS 3683 + if (local->force_tx_status) 3684 + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; 3685 + #endif 3686 + 3687 + if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { 3688 + tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; 3689 + *ieee80211_get_qos_ctl(hdr) = tid; 3690 + } 3691 + 3692 + __skb_queue_head_init(&tx.skbs); 3693 + 3694 + tx.flags = IEEE80211_TX_UNICAST; 3695 + tx.local = local; 3696 + tx.sdata = sdata; 3697 + tx.sta = sta; 3698 + tx.key = fast_tx->key; 3699 + 3700 + if (ieee80211_queue_skb(local, sdata, sta, skb)) 3701 + return; 3702 + 3703 + tx.skb = skb; 3704 + r = ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs, 3705 + fast_tx->key, &tx); 3706 + tx.skb = NULL; 3707 + if (r == TX_DROP) 3708 + goto free; 3709 + 3710 + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) 3711 + sdata = container_of(sdata->bss, 3712 + struct ieee80211_sub_if_data, u.ap); 3713 + 3714 + __skb_queue_tail(&tx.skbs, skb); 3715 + ieee80211_tx_frags(local, &sdata->vif, sta, &tx.skbs, false); 3716 + return; 3717 + 3718 + free: 3719 + kfree_skb(skb); 3720 + } 3721 + 3585 3722 static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, 3586 3723 struct sta_info *sta, 3587 3724 struct ieee80211_fast_tx *fast_tx, 3588 3725 struct sk_buff *skb) 3589 3726 { 3590 - struct ieee80211_local *local = sdata->local; 3591 3727 u16 ethertype = (skb->data[12] << 8) | skb->data[13]; 3592 - int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2); 3593 - int hw_headroom = sdata->local->hw.extra_tx_headroom; 3594 - struct ethhdr eth; 3595 - struct ieee80211_tx_info *info; 3596 3728 struct ieee80211_hdr *hdr = (void *)fast_tx->hdr; 3597 - struct ieee80211_tx_data tx; 3598 - ieee80211_tx_result r; 3599 3729 struct tid_ampdu_tx *tid_tx = NULL; 3730 + struct sk_buff *next; 3600 3731 u8 tid = IEEE80211_NUM_TIDS; 3601 3732 3602 3733 /* control port protocol needs a lot of special handling */ ··· 3758 3619 } 3759 3620 3760 3621 /* after this point (skb is modified) we cannot return false */ 3761 - 3762 - skb = skb_share_check(skb, GFP_ATOMIC); 3763 - if (unlikely(!skb)) 3622 + skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata)); 3623 + if (!skb) 3764 3624 return true; 3765 3625 3766 - if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) && 3767 - ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb)) 3768 - return true; 3769 - 3770 - /* will not be crypto-handled beyond what we do here, so use false 3771 - * as the may-encrypt argument for the resize to not account for 3772 - * more room than we already have in 'extra_head' 3773 - */ 3774 - if (unlikely(ieee80211_skb_resize(sdata, skb, 3775 - max_t(int, extra_head + hw_headroom - 3776 - skb_headroom(skb), 0), 3777 - ENCRYPT_NO))) { 3778 - kfree_skb(skb); 3779 - return true; 3626 + skb_list_walk_safe(skb, skb, next) { 3627 + skb_mark_not_on_list(skb); 3628 + __ieee80211_xmit_fast(sdata, sta, fast_tx, skb, tid, tid_tx); 3780 3629 } 3781 3630 3782 - memcpy(&eth, skb->data, ETH_HLEN - 2); 3783 - hdr = skb_push(skb, extra_head); 3784 - memcpy(skb->data, fast_tx->hdr, fast_tx->hdr_len); 3785 - memcpy(skb->data + fast_tx->da_offs, eth.h_dest, ETH_ALEN); 3786 - memcpy(skb->data + fast_tx->sa_offs, eth.h_source, ETH_ALEN); 3787 - 3788 - info = IEEE80211_SKB_CB(skb); 3789 - memset(info, 0, sizeof(*info)); 3790 - info->band = fast_tx->band; 3791 - info->control.vif = &sdata->vif; 3792 - info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT | 3793 - IEEE80211_TX_CTL_DONTFRAG | 3794 - (tid_tx ? IEEE80211_TX_CTL_AMPDU : 0); 3795 - info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT | 3796 - u32_encode_bits(IEEE80211_LINK_UNSPECIFIED, 3797 - IEEE80211_TX_CTRL_MLO_LINK); 3798 - 3799 - #ifdef CONFIG_MAC80211_DEBUGFS 3800 - if (local->force_tx_status) 3801 - info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; 3802 - #endif 3803 - 3804 - if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { 3805 - tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; 3806 - *ieee80211_get_qos_ctl(hdr) = tid; 3807 - } 3808 - 3809 - __skb_queue_head_init(&tx.skbs); 3810 - 3811 - tx.flags = IEEE80211_TX_UNICAST; 3812 - tx.local = local; 3813 - tx.sdata = sdata; 3814 - tx.sta = sta; 3815 - tx.key = fast_tx->key; 3816 - 3817 - if (ieee80211_queue_skb(local, sdata, sta, skb)) 3818 - return true; 3819 - 3820 - tx.skb = skb; 3821 - r = ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs, 3822 - fast_tx->key, &tx); 3823 - tx.skb = NULL; 3824 - if (r == TX_DROP) { 3825 - kfree_skb(skb); 3826 - return true; 3827 - } 3828 - 3829 - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) 3830 - sdata = container_of(sdata->bss, 3831 - struct ieee80211_sub_if_data, u.ap); 3832 - 3833 - __skb_queue_tail(&tx.skbs, skb); 3834 - ieee80211_tx_frags(local, &sdata->vif, sta, &tx.skbs, false); 3835 3631 return true; 3836 3632 } 3837 3633 ··· 4266 4192 goto out; 4267 4193 } 4268 4194 4269 - if (skb_is_gso(skb)) { 4270 - struct sk_buff *segs; 4271 - 4272 - segs = skb_gso_segment(skb, 0); 4273 - if (IS_ERR(segs)) { 4274 - goto out_free; 4275 - } else if (segs) { 4276 - consume_skb(skb); 4277 - skb = segs; 4278 - } 4279 - } else { 4280 - /* we cannot process non-linear frames on this path */ 4281 - if (skb_linearize(skb)) 4282 - goto out_free; 4283 - 4284 - /* the frame could be fragmented, software-encrypted, and other 4285 - * things so we cannot really handle checksum offload with it - 4286 - * fix it up in software before we handle anything else. 4287 - */ 4288 - if (skb->ip_summed == CHECKSUM_PARTIAL) { 4289 - skb_set_transport_header(skb, 4290 - skb_checksum_start_offset(skb)); 4291 - if (skb_checksum_help(skb)) 4292 - goto out_free; 4293 - } 4195 + /* the frame could be fragmented, software-encrypted, and other 4196 + * things so we cannot really handle checksum or GSO offload. 4197 + * fix it up in software before we handle anything else. 4198 + */ 4199 + skb = ieee80211_tx_skb_fixup(skb, 0); 4200 + if (!skb) { 4201 + len = 0; 4202 + goto out; 4294 4203 } 4295 4204 4296 4205 skb_list_walk_safe(skb, skb, next) { ··· 4491 4434 return NETDEV_TX_OK; 4492 4435 } 4493 4436 4494 - static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, 4495 - struct sk_buff *skb, struct sta_info *sta, 4496 - bool txpending) 4437 + 4438 + 4439 + static bool __ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, 4440 + struct sk_buff *skb, struct sta_info *sta, 4441 + bool txpending) 4497 4442 { 4498 4443 struct ieee80211_local *local = sdata->local; 4499 4444 struct ieee80211_tx_control control = {}; ··· 4503 4444 struct ieee80211_sta *pubsta = NULL; 4504 4445 unsigned long flags; 4505 4446 int q = info->hw_queue; 4506 - 4507 - if (sta) 4508 - sk_pacing_shift_update(skb->sk, local->hw.tx_sk_pacing_shift); 4509 - 4510 - ieee80211_tpt_led_trig_tx(local, skb->len); 4511 - 4512 - if (ieee80211_queue_skb(local, sdata, sta, skb)) 4513 - return true; 4514 4447 4515 4448 spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 4516 4449 ··· 4530 4479 return true; 4531 4480 } 4532 4481 4482 + static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, 4483 + struct sk_buff *skb, struct sta_info *sta, 4484 + bool txpending) 4485 + { 4486 + struct ieee80211_local *local = sdata->local; 4487 + struct sk_buff *next; 4488 + bool ret = true; 4489 + 4490 + if (ieee80211_queue_skb(local, sdata, sta, skb)) 4491 + return true; 4492 + 4493 + skb_list_walk_safe(skb, skb, next) { 4494 + skb_mark_not_on_list(skb); 4495 + if (!__ieee80211_tx_8023(sdata, skb, sta, txpending)) 4496 + ret = false; 4497 + } 4498 + 4499 + return ret; 4500 + } 4501 + 4533 4502 static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, 4534 4503 struct net_device *dev, struct sta_info *sta, 4535 4504 struct ieee80211_key *key, struct sk_buff *skb) ··· 4557 4486 struct ieee80211_tx_info *info; 4558 4487 struct ieee80211_local *local = sdata->local; 4559 4488 struct tid_ampdu_tx *tid_tx; 4489 + struct sk_buff *seg, *next; 4490 + unsigned int skbs = 0, len = 0; 4491 + u16 queue; 4560 4492 u8 tid; 4561 4493 4562 - skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb)); 4494 + queue = ieee80211_select_queue(sdata, sta, skb); 4495 + skb_set_queue_mapping(skb, queue); 4563 4496 4564 4497 if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) && 4565 4498 test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) ··· 4572 4497 skb = skb_share_check(skb, GFP_ATOMIC); 4573 4498 if (unlikely(!skb)) 4574 4499 return; 4575 - 4576 - info = IEEE80211_SKB_CB(skb); 4577 - memset(info, 0, sizeof(*info)); 4578 4500 4579 4501 ieee80211_aggr_check(sdata, sta, skb); 4580 4502 ··· 4586 4514 return; 4587 4515 } 4588 4516 4589 - info->flags |= IEEE80211_TX_CTL_AMPDU; 4590 4517 if (tid_tx->timeout) 4591 4518 tid_tx->last_tx = jiffies; 4592 4519 } 4593 4520 4594 - if (unlikely(skb->sk && 4595 - skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) 4596 - info->ack_frame_id = ieee80211_store_ack_skb(local, skb, 4597 - &info->flags, NULL); 4521 + skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata)); 4522 + if (!skb) 4523 + return; 4598 4524 4599 - info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)]; 4525 + info = IEEE80211_SKB_CB(skb); 4526 + memset(info, 0, sizeof(*info)); 4527 + if (tid_tx) 4528 + info->flags |= IEEE80211_TX_CTL_AMPDU; 4600 4529 4601 - dev_sw_netstats_tx_add(dev, 1, skb->len); 4602 - 4603 - sta->deflink.tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len; 4604 - sta->deflink.tx_stats.packets[skb_get_queue_mapping(skb)]++; 4530 + info->hw_queue = sdata->vif.hw_queue[queue]; 4605 4531 4606 4532 if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) 4607 4533 sdata = container_of(sdata->bss, ··· 4610 4540 4611 4541 if (key) 4612 4542 info->control.hw_key = &key->conf; 4543 + 4544 + skb_list_walk_safe(skb, seg, next) { 4545 + skbs++; 4546 + len += seg->len; 4547 + if (seg != skb) 4548 + memcpy(IEEE80211_SKB_CB(seg), info, sizeof(*info)); 4549 + } 4550 + 4551 + if (unlikely(skb->sk && 4552 + skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) 4553 + info->ack_frame_id = ieee80211_store_ack_skb(local, skb, 4554 + &info->flags, NULL); 4555 + 4556 + dev_sw_netstats_tx_add(dev, skbs, len); 4557 + sta->deflink.tx_stats.packets[queue] += skbs; 4558 + sta->deflink.tx_stats.bytes[queue] += len; 4559 + 4560 + ieee80211_tpt_led_trig_tx(local, len); 4613 4561 4614 4562 ieee80211_tx_8023(sdata, skb, sta, false); 4615 4563 ··· 4670 4582 key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)) 4671 4583 goto skip_offload; 4672 4584 4585 + sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift); 4673 4586 ieee80211_8023_xmit(sdata, dev, sta, key, skb); 4674 4587 goto out; 4675 4588