@@ -410,6 +410,23 @@ static int veth_get_iflink(const struct net_device *dev)
return iflink;
}
+static netdev_features_t veth_fix_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct veth_priv *priv = netdev_priv(dev);
+ struct net_device *peer;
+
+ peer = rtnl_dereference(priv->peer);
+ if (peer) {
+ struct veth_priv *peer_priv = netdev_priv(peer);
+
+ if (rtnl_dereference(peer_priv->xdp_prog))
+ features &= ~NETIF_F_GSO_SOFTWARE;
+ }
+
+ return features;
+}
+
static void veth_set_rx_headroom(struct net_device *dev, int new_hr)
{
struct veth_priv *peer_priv, *priv = netdev_priv(dev);
@@ -438,13 +455,32 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog,
{
struct veth_priv *priv = netdev_priv(dev);
struct bpf_prog *old_prog;
+ struct net_device *peer;
old_prog = rtnl_dereference(priv->xdp_prog);
+ peer = rtnl_dereference(priv->peer);
+
+ if (!old_prog && prog && peer) {
+ peer->hw_features &= ~NETIF_F_GSO_SOFTWARE;
+ peer->max_mtu = PAGE_SIZE - VETH_XDP_HEADROOM -
+ peer->hard_header_len -
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+ if (peer->mtu > peer->max_mtu)
+ dev_set_mtu(peer, peer->max_mtu);
+ }
rcu_assign_pointer(priv->xdp_prog, prog);
- if (old_prog)
+ if (old_prog) {
bpf_prog_put(old_prog);
+ if (!prog && peer) {
+ peer->hw_features |= NETIF_F_GSO_SOFTWARE;
+ peer->max_mtu = ETH_MAX_MTU;
+ }
+ }
+
+ if ((!!old_prog ^ !!prog) && peer)
+ netdev_update_features(peer);
return 0;
}
@@ -487,6 +523,7 @@ static const struct net_device_ops veth_netdev_ops = {
.ndo_poll_controller = veth_poll_controller,
#endif
.ndo_get_iflink = veth_get_iflink,
+ .ndo_fix_features = veth_fix_features,
.ndo_features_check = passthru_features_check,
.ndo_set_rx_headroom = veth_set_rx_headroom,
.ndo_bpf = veth_xdp,