]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: add a common function to compute features for upper devices
authorHangbin Liu <liuhangbin@gmail.com>
Fri, 17 Oct 2025 03:41:52 +0000 (03:41 +0000)
committerJakub Kicinski <kuba@kernel.org>
Wed, 22 Oct 2025 01:08:23 +0000 (18:08 -0700)
Some high level software drivers need to compute features from lower
devices. But each has their own implementations and may lost some
feature compute. Let's use one common function to compute features
for kinds of these devices.

The new helper uses the current bond implementation as the reference
one, as the latter already handles all the relevant aspects: netdev
features, TSO limits and dst retention.

Suggested-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Link: https://patch.msgid.link/20251017034155.61990-2-liuhangbin@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/linux/netdev_features.h
include/linux/netdevice.h
net/core/dev.c

index 7a01c518e5730ae5611a454af7ca96a7791944ce..93e4da7046a1055b49538576cb5bb5a00b8ed29c 100644 (file)
@@ -255,6 +255,24 @@ static inline int find_next_netdev_feature(u64 feature, unsigned long start)
                                 NETIF_F_GSO_UDP_TUNNEL |               \
                                 NETIF_F_GSO_UDP_TUNNEL_CSUM)
 
+/* virtual device features */
+#define MASTER_UPPER_DEV_VLAN_FEATURES  (NETIF_F_HW_CSUM | NETIF_F_SG | \
+                                         NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | \
+                                         NETIF_F_GSO_ENCAP_ALL | \
+                                         NETIF_F_HIGHDMA | NETIF_F_LRO)
+
+#define MASTER_UPPER_DEV_ENC_FEATURES   (NETIF_F_HW_CSUM | NETIF_F_SG | \
+                                         NETIF_F_RXCSUM | NETIF_F_GSO_SOFTWARE | \
+                                         NETIF_F_GSO_PARTIAL)
+
+#define MASTER_UPPER_DEV_MPLS_FEATURES  (NETIF_F_HW_CSUM | NETIF_F_SG | \
+                                         NETIF_F_GSO_SOFTWARE)
+
+#define MASTER_UPPER_DEV_XFRM_FEATURES  (NETIF_F_HW_ESP | NETIF_F_HW_ESP_TX_CSUM | \
+                                         NETIF_F_GSO_ESP)
+
+#define MASTER_UPPER_DEV_GSO_PARTIAL_FEATURES (NETIF_F_GSO_ESP)
+
 static inline netdev_features_t netdev_base_features(netdev_features_t features)
 {
        features &= ~NETIF_F_ONE_FOR_ALL;
index d1a687444b275d45d105e336d2ede264fd310f1b..7f5aad5cc9a1994f95ba9037d3a4af27eef9d5e3 100644 (file)
@@ -5304,6 +5304,7 @@ static inline netdev_features_t netdev_add_tso_features(netdev_features_t featur
 int __netdev_update_features(struct net_device *dev);
 void netdev_update_features(struct net_device *dev);
 void netdev_change_features(struct net_device *dev);
+void netdev_compute_master_upper_features(struct net_device *dev, bool update_header);
 
 void netif_stacked_transfer_operstate(const struct net_device *rootdev,
                                        struct net_device *dev);
index 9482b905c66a53501ad3b737ad4461533b9e7a4e..378c2d010faf251ffd874ebf0cc3dd6968eee447 100644 (file)
@@ -12693,6 +12693,94 @@ netdev_features_t netdev_increment_features(netdev_features_t all,
 }
 EXPORT_SYMBOL(netdev_increment_features);
 
+/**
+ *     netdev_compute_master_upper_features - compute feature from lowers
+ *     @dev: the upper device
+ *     @update_header: whether to update upper device's header_len/headroom/tailroom
+ *
+ *     Recompute the upper device's feature based on all lower devices.
+ */
+void netdev_compute_master_upper_features(struct net_device *dev, bool update_header)
+{
+       unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM;
+       netdev_features_t gso_partial_features = MASTER_UPPER_DEV_GSO_PARTIAL_FEATURES;
+       netdev_features_t xfrm_features = MASTER_UPPER_DEV_XFRM_FEATURES;
+       netdev_features_t mpls_features = MASTER_UPPER_DEV_MPLS_FEATURES;
+       netdev_features_t vlan_features = MASTER_UPPER_DEV_VLAN_FEATURES;
+       netdev_features_t enc_features = MASTER_UPPER_DEV_ENC_FEATURES;
+       unsigned short max_header_len = ETH_HLEN;
+       unsigned int tso_max_size = TSO_MAX_SIZE;
+       unsigned short max_headroom = 0;
+       unsigned short max_tailroom = 0;
+       u16 tso_max_segs = TSO_MAX_SEGS;
+       struct net_device *lower_dev;
+       struct list_head *iter;
+
+       mpls_features = netdev_base_features(mpls_features);
+       vlan_features = netdev_base_features(vlan_features);
+       enc_features = netdev_base_features(enc_features);
+
+       netdev_for_each_lower_dev(dev, lower_dev, iter) {
+               gso_partial_features = netdev_increment_features(gso_partial_features,
+                                                                lower_dev->gso_partial_features,
+                                                                MASTER_UPPER_DEV_GSO_PARTIAL_FEATURES);
+
+               vlan_features = netdev_increment_features(vlan_features,
+                                                         lower_dev->vlan_features,
+                                                         MASTER_UPPER_DEV_VLAN_FEATURES);
+
+               enc_features = netdev_increment_features(enc_features,
+                                                        lower_dev->hw_enc_features,
+                                                        MASTER_UPPER_DEV_ENC_FEATURES);
+
+               if (IS_ENABLED(CONFIG_XFRM_OFFLOAD))
+                       xfrm_features = netdev_increment_features(xfrm_features,
+                                                                 lower_dev->hw_enc_features,
+                                                                 MASTER_UPPER_DEV_XFRM_FEATURES);
+
+               mpls_features = netdev_increment_features(mpls_features,
+                                                         lower_dev->mpls_features,
+                                                         MASTER_UPPER_DEV_MPLS_FEATURES);
+
+               dst_release_flag &= lower_dev->priv_flags;
+
+               if (update_header) {
+                       max_header_len = max(max_header_len, lower_dev->hard_header_len);
+                       max_headroom = max(max_headroom, lower_dev->needed_headroom);
+                       max_tailroom = max(max_tailroom, lower_dev->needed_tailroom);
+               }
+
+               tso_max_size = min(tso_max_size, lower_dev->tso_max_size);
+               tso_max_segs = min(tso_max_segs, lower_dev->tso_max_segs);
+       }
+
+       dev->gso_partial_features = gso_partial_features;
+       dev->vlan_features = vlan_features;
+       dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL |
+                              NETIF_F_HW_VLAN_CTAG_TX |
+                              NETIF_F_HW_VLAN_STAG_TX;
+       if (IS_ENABLED(CONFIG_XFRM_OFFLOAD))
+               dev->hw_enc_features |= xfrm_features;
+       dev->mpls_features = mpls_features;
+
+       dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+       if ((dev->priv_flags & IFF_XMIT_DST_RELEASE_PERM) &&
+           dst_release_flag == (IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM))
+               dev->priv_flags |= IFF_XMIT_DST_RELEASE;
+
+       if (update_header) {
+               dev->hard_header_len = max_header_len;
+               dev->needed_headroom = max_headroom;
+               dev->needed_tailroom = max_tailroom;
+       }
+
+       netif_set_tso_max_segs(dev, tso_max_segs);
+       netif_set_tso_max_size(dev, tso_max_size);
+
+       netdev_change_features(dev);
+}
+EXPORT_SYMBOL(netdev_compute_master_upper_features);
+
 static struct hlist_head * __net_init netdev_create_hash(void)
 {
        int i;