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 ppp_update_dev_features() to conditionally disable them if a linear skb is required. This is required to support PPPoE GSO. Signed-off-by: Qingfang Deng --- v1 -> v2: Changes dev->features under the TX spinlock to avoid races. - https://lore.kernel.org/netdev/20250912095928.1532113-1-dqfext@gmail.com/ drivers/net/ppp/ppp_generic.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 854e1a95d29a..389542f0af5f 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -498,6 +498,17 @@ static ssize_t ppp_read(struct file *file, char __user *buf, return ret; } +static void ppp_update_dev_features(struct ppp *ppp) +{ + struct net_device *dev = ppp->dev; + + if (!(dev->priv_flags & IFF_NO_QUEUE) || ppp->xc_state || + ppp->flags & (SC_COMP_TCP | SC_CCP_UP)) + dev->features &= ~(NETIF_F_SG | NETIF_F_FRAGLIST); + else + dev->features |= NETIF_F_SG | NETIF_F_FRAGLIST; +} + static bool ppp_check_packet(struct sk_buff *skb, size_t count) { /* LCP packets must include LCP header which 4 bytes long: @@ -824,6 +835,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PPPIOCSFLAGS: if (get_user(val, p)) break; + rtnl_lock(); ppp_lock(ppp); cflags = ppp->flags & ~val; #ifdef CONFIG_PPP_MULTILINK @@ -834,6 +846,12 @@ 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); + + ppp_xmit_lock(ppp); + ppp_update_dev_features(ppp); + ppp_xmit_unlock(ppp); + netdev_update_features(ppp->dev); + rtnl_unlock(); err = 0; break; @@ -1650,6 +1668,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); } @@ -3112,13 +3132,17 @@ ppp_set_compress(struct ppp *ppp, struct ppp_option_data *data) if (data->transmit) { state = cp->comp_alloc(ccp_option, data->length); if (state) { + rtnl_lock(); ppp_xmit_lock(ppp); ppp->xstate &= ~SC_COMP_RUN; ocomp = ppp->xcomp; ostate = ppp->xc_state; ppp->xcomp = cp; ppp->xc_state = state; + ppp_update_dev_features(ppp); ppp_xmit_unlock(ppp); + netdev_update_features(ppp->dev); + rtnl_unlock(); if (ostate) { ocomp->comp_free(ostate); module_put(ocomp->owner); @@ -3539,6 +3563,7 @@ ppp_connect_channel(struct channel *pch, int unit) pn = ppp_pernet(pch->chan_net); + rtnl_lock(); mutex_lock(&pn->all_ppp_mutex); ppp = ppp_find_unit(pn, unit); if (!ppp) @@ -3562,6 +3587,7 @@ ppp_connect_channel(struct channel *pch, int unit) ppp->dev->priv_flags |= IFF_NO_QUEUE; else ppp->dev->priv_flags &= ~IFF_NO_QUEUE; + ppp_update_dev_features(ppp); spin_unlock_bh(&pch->downl); if (pch->file.hdrlen > ppp->file.hdrlen) ppp->file.hdrlen = pch->file.hdrlen; @@ -3579,6 +3605,10 @@ ppp_connect_channel(struct channel *pch, int unit) spin_unlock(&pch->upl); out: mutex_unlock(&pn->all_ppp_mutex); + if (ret == 0) + netdev_update_features(ppp->dev); + rtnl_unlock(); + return ret; } -- 2.43.0