When chan->direct_xmit is true, and no compressors are in use, PPP prepends its header to a skb, and calls dev_queue_xmit directly. In this mode the skb does not need to be linearized. Enable NETIF_F_SG and NETIF_F_FRAGLIST, and add .ndo_fix_features() callback to conditionally disable them if a linear skb is required. This is required to support PPPoE GSO. Signed-off-by: Qingfang Deng --- v1: Remove the test for SC_CCP_OPEN and instead test for xc_state changes. Link to RFC v2: https://lore.kernel.org/netdev/20250909012742.424771-1-dqfext@gmail.com/ RFC v2: Dynamically update netdev features with ndo_fix_features() callback. Link to RFC v1: https://lore.kernel.org/netdev/20250904021328.24329-1-dqfext@gmail.com/ drivers/net/ppp/ppp_generic.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index f9f0f16c41d1..1132159a8b92 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -835,6 +835,10 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ppp_unlock(ppp); if (cflags & SC_CCP_OPEN) ppp_ccp_closed(ppp); + + rtnl_lock(); + netdev_update_features(ppp->dev); + rtnl_unlock(); err = 0; break; @@ -1545,6 +1549,22 @@ ppp_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64) dev_fetch_sw_netstats(stats64, dev->tstats); } +static netdev_features_t +ppp_fix_features(struct net_device *dev, netdev_features_t features) +{ + struct ppp *ppp = netdev_priv(dev); + + ppp_xmit_lock(ppp); + /* Allow SG/FRAGLIST only when we have direct-xmit, and no compression + * path that wants a linear skb. + */ + if (!(dev->priv_flags & IFF_NO_QUEUE) || ppp->xc_state || + ppp->flags & (SC_COMP_TCP | SC_CCP_UP)) + features &= ~(NETIF_F_SG | NETIF_F_FRAGLIST); + ppp_xmit_unlock(ppp); + return features; +} + static int ppp_dev_init(struct net_device *dev) { struct ppp *ppp; @@ -1619,6 +1639,7 @@ static const struct net_device_ops ppp_netdev_ops = { .ndo_start_xmit = ppp_start_xmit, .ndo_siocdevprivate = ppp_net_siocdevprivate, .ndo_get_stats64 = ppp_get_stats64, + .ndo_fix_features = ppp_fix_features, .ndo_fill_forward_path = ppp_fill_forward_path, }; @@ -1641,6 +1662,8 @@ static void ppp_setup(struct net_device *dev) dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; dev->priv_destructor = ppp_dev_priv_destructor; dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; + dev->features = NETIF_F_SG | NETIF_F_FRAGLIST; + dev->hw_features = dev->features; netif_keep_dst(dev); } @@ -3081,6 +3104,9 @@ ppp_set_compress(struct ppp *ppp, struct ppp_option_data *data) ocomp->comp_free(ostate); module_put(ocomp->owner); } + rtnl_lock(); + netdev_update_features(ppp->dev); + rtnl_unlock(); err = 0; } else module_put(cp->owner); @@ -3537,6 +3563,12 @@ ppp_connect_channel(struct channel *pch, int unit) spin_unlock(&pch->upl); out: mutex_unlock(&pn->all_ppp_mutex); + if (ret == 0) { + rtnl_lock(); + netdev_update_features(ppp->dev); + rtnl_unlock(); + } + return ret; } -- 2.43.0