diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-29 08:17:05 -1000 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-29 08:17:05 -1000 |
| commit | 4dd16e31135ea8437a720222df34e8745f385d6d (patch) | |
| tree | 004c93006bf6fdd3618249deedab3fbbdc8bee30 /drivers | |
| parent | 46806af187f448b0d5481f284e7c900a57ee6a6d (diff) | |
| parent | 8f44631ba02afe9fff2f0a0fb66560ab19df87fd (diff) | |
Merge tag 'net-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Pull networking fixes from Jakub Kicinski:
"Current release - new code bugs:
- mctp: usblib: fix assertion breaking esoteric builds
Previous releases - regressions:
- Revert advertising the TCP MSS from the configured MTU
- netlink: check sk_rcvbuf before charging the dump skb
- eth:
- ti: am65-cpts: fix support for PTP_EXTTS_REQUEST2 ioctl
- mlx4: always create the eth and ib auxiliary devices
- tg3: invert restart quirk logic to restrict SNP power-down bypass
- bnxt: recycle agg buffers when Rx head buffer allocation fails
Previous releases - always broken:
- net: fix frag_list leak in skb_segment_list() error path
- sctp: reject forged cookie peer_addr with unknown address family
- vxlan: keep reserved_bits across changelink
- net: limit priority VLAN untag depth
- inet: frags: prevent acceptance of partially authenticated reassembly
- gro: do not GRO GSO packets with inconsistent payload
- tcp: avoid double destroy in tcp_abort() of an unaccepted child"
* tag 'net-7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (137 commits)
tun: drop batched packets while the device is down
selftests: net: netlink-dumps: start a dump with a notification queued
netlink: check sk_rcvbuf before charging the dump skb
net/sched: act_ife: do not consume the inner header on egress
openvswitch: use eth_type_trans_careful() in internal_dev_recv()
net: use eth_type_trans_careful() in __dev_forward_skb2()
loopback: use eth_type_trans_careful()
net: add eth_type_trans_careful()
tcp: initialize mapped children before route lookup
tcp: fix reordering addition overflow in MTU probing
tcp: avoid double destroy in tcp_abort() of an unaccepted child
net: bgmac: fix TX DMA unmapping and slot cleanup
gro: do not GRO GSO packets with inconsistent payload
net/packet: check TX_RING fragments before memory accounting
eth: bnxt: hand XDP frags over to the copybreak skb
eth: bnxt: free XDP frags when xdp_do_redirect() fails
eth: bnxt: don't leak buffers when XDP agg buffer allocation fails
eth: bnxt: resize Rx head buffers when rx-copybreak changes
eth: bnxt: recycle agg buffers when Rx head buffer allocation fails
netdev-genl: ignore NETDEV_UNREGISTER rebroadcasts
...
Diffstat (limited to 'drivers')
68 files changed, 874 insertions, 334 deletions
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 09aa45d71fab..7019e5d8fdc8 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -6273,7 +6273,7 @@ static int __init bond_check_params(struct bond_params *params) } for (arp_ip_count = 0, i = 0; - (arp_ip_count < BOND_MAX_ARP_TARGETS) && arp_ip_target[i]; i++) { + (i < BOND_MAX_ARP_TARGETS) && arp_ip_target[i]; i++) { __be32 ip; /* not a complete check, but good enough to catch mistakes */ diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c index d4fc20c52a58..33e5fc127244 100644 --- a/drivers/net/dsa/motorcomm/chip.c +++ b/drivers/net/dsa/motorcomm/chip.c @@ -679,8 +679,8 @@ yt921x_fdb_flush_port(struct yt921x_priv *priv, int port, bool flush_static) } static int -yt921x_fdb_add_index_in12(struct yt921x_priv *priv, u16 index, u16 ctrl1, - u16 ctrl2) +yt921x_fdb_add_index_in12(struct yt921x_priv *priv, u16 index, u32 ctrl1, + u32 ctrl2) { u32 ctrl; u32 val; diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 7781a63b4e6f..d4714bc102ee 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -3300,10 +3300,13 @@ mt753x_conduit_state_change(struct dsa_switch *ds, /* Set the CPU port to trap frames to for MT7530. Trapped frames will be * forwarded to the numerically smallest CPU port whose conduit - * interface is up. + * interface is up. On EN7581, trapped BPDUs do not reach the CPU port + * without this, even with the CPU port bitmap set. AN7583 is set up + * the same way as EN7581. */ if (priv->id != ID_MT7530 && priv->id != ID_MT7621 && - priv->id != ID_EN7528) + priv->id != ID_EN7528 && + priv->id != ID_EN7581 && priv->id != ID_AN7583) return; mask = BIT(cpu_dp->index); diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 2a975ecd79c7..46aef6dfe77e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -4611,7 +4611,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_get_cmode = mv88e6352_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, - .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -4715,7 +4715,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_get_cmode = mv88e6352_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, - .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -5381,7 +5381,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_get_cmode = mv88e6352_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, - .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -5427,7 +5427,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .port_get_cmode = mv88e6352_port_get_cmode, .port_setup_message_port = mv88e6xxx_setup_message_port, - .stats_snapshot = mv88e6320_g1_stats_snapshot, + .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -6023,7 +6023,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 9, .g2_irqs = 10, - .stats_type = STATS_TYPE_BANK0 | STATS_TYPE_BANK1, + .stats_type = STATS_TYPE_BANK0 | STATS_TYPE_PORT, .atu_move_port_mask = 0xf, .pvt = true, .multi_chip = true, diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c index 04b3ef840b36..7a4afd25d240 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c @@ -342,9 +342,9 @@ static void aq_pci_remove(struct pci_dev *pdev) struct aq_nic_s *self = pci_get_drvdata(pdev); if (self->ndev) { - aq_clear_rxnfc_all_rules(self); if (self->ndev->reg_state == NETREG_REGISTERED) unregister_netdev(self->ndev); + aq_clear_rxnfc_all_rules(self); #if IS_ENABLED(CONFIG_MACSEC) aq_macsec_free(self); diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index b3a92ae95de3..669eb7ce7d40 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -208,19 +208,24 @@ static netdev_tx_t bgmac_dma_tx_add(struct bgmac *bgmac, return NETDEV_TX_OK; err_dma: - dma_unmap_single(dma_dev, slot->dma_addr, skb_headlen(skb), - DMA_TO_DEVICE); + slot->dma_addr = 0; while (i-- > 0) { - int index = (ring->end + i) % BGMAC_TX_RING_SLOTS; - struct bgmac_slot_info *slot = &ring->slots[index]; - u32 ctl1 = le32_to_cpu(ring->cpu_base[index].ctl1); - int len = ctl1 & BGMAC_DESC_CTL1_LEN; - - dma_unmap_page(dma_dev, slot->dma_addr, len, DMA_TO_DEVICE); + index = (ring->end + 1 + i) % BGMAC_TX_RING_SLOTS; + slot = &ring->slots[index]; + dma_unmap_page(dma_dev, slot->dma_addr, + skb_frag_size(&skb_shinfo(skb)->frags[i]), + DMA_TO_DEVICE); + slot->dma_addr = 0; } + slot = &ring->slots[ring->end % BGMAC_TX_RING_SLOTS]; + dma_unmap_single(dma_dev, slot->dma_addr, skb_headlen(skb), + DMA_TO_DEVICE); + err_dma_head: + slot->dma_addr = 0; + netdev_err(bgmac->net_dev, "Mapping error of skb on ring 0x%X\n", ring->mmio_base); @@ -537,17 +542,25 @@ static void bgmac_dma_tx_ring_free(struct bgmac *bgmac, unsigned int len = ctl1 & BGMAC_DESC_CTL1_LEN; slot = &ring->slots[i]; - dev_kfree_skb(slot->skb); - if (!slot->dma_addr) - continue; + if (slot->dma_addr) { + if (le32_to_cpu(dma_desc[i].ctl0) & BGMAC_DESC_CTL0_SOF) + dma_unmap_single(dma_dev, slot->dma_addr, + len, DMA_TO_DEVICE); + else + dma_unmap_page(dma_dev, slot->dma_addr, + len, DMA_TO_DEVICE); + slot->dma_addr = 0; + } + } - if (slot->skb) - dma_unmap_single(dma_dev, slot->dma_addr, - len, DMA_TO_DEVICE); - else - dma_unmap_page(dma_dev, slot->dma_addr, - len, DMA_TO_DEVICE); + /* A packet can wrap around the end of the ring with its skb on the + * last slot, so free the skbs only once every slot is unmapped. + */ + for (i = 0; i < BGMAC_TX_RING_SLOTS; i++) { + slot = &ring->slots[i]; + dev_kfree_skb(slot->skb); + slot->skb = NULL; } } diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c index 7c26943c5cb3..65ef34536e36 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c @@ -182,8 +182,9 @@ int bnge_cal_nr_rss_ctxs(u16 rx_rings) if (!rx_rings) return 0; - return bnge_adjust_pow_two(rx_rings - 1, - BNGE_RSS_TABLE_ENTRIES); + return min_t(int, bnge_adjust_pow_two(rx_rings - 1, + BNGE_RSS_TABLE_ENTRIES), + BNGE_RSS_TABLE_MAX_TBL); } static u16 bnge_rss_ctxs_in_use(struct bnge_dev *bd, @@ -459,6 +460,7 @@ static void _bnge_get_max_rings(struct bnge_dev *bd, u16 *max_rx, } *max_rx = min_t(int, *max_rx, max_ring_grps); + *max_rx = min_t(int, *max_rx, BNGE_MAX_RSS_TABLE_ENTRIES); } static int bnge_get_max_rings(struct bnge_dev *bd, u16 *max_rx, diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_resc.h b/drivers/net/ethernet/broadcom/bnge/bnge_resc.h index b62a634669f6..c0b6992d7f82 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_resc.h +++ b/drivers/net/ethernet/broadcom/bnge/bnge_resc.h @@ -81,13 +81,10 @@ bnge_adjust_pow_two(u32 total_ent, u16 ent_per_blk) { u32 blks = total_ent / ent_per_blk; - if (blks == 0 || blks == 1) - return ++blks; + if (!blks) + return 1; - if (!is_power_of_2(blks)) - blks = roundup_pow_of_two(blks); - - return blks; + return roundup_pow_of_two(blks + 1); } #define BNGE_MAX_ROCE_MSIX 64 diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 0682060b048c..757c7266ce15 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1215,7 +1215,15 @@ static struct sk_buff *bnxt_rx_multi_page_skb(struct bnxt *bp, bp->rx_dir); skb = napi_build_skb(frag_start, rxr->rx_page_size); if (!skb) { - page_pool_recycle_direct(rxr->page_pool, page); + struct page *new_page; + + new_page = rxr->rx_buf_ring[RING_RX(bp, prod)].data; + /* The caller may still need the XDP frags from shinfo at the + * end of the head page. Release the new buffer instead and + * put the head back on the ring. + */ + page_pool_recycle_direct(rxr->page_pool, new_page); + bnxt_reuse_rx_data(rxr, cons, data); return NULL; } skb_mark_for_recycle(skb); @@ -1419,16 +1427,19 @@ static u32 bnxt_rx_agg_netmems_xdp(struct bnxt *bp, struct skb_shared_info *shinfo = xdp_get_shared_info_from_buff(xdp); u32 total_frag_len = 0; - if (!xdp_buff_has_frags(xdp)) + if (!xdp_buff_has_frags(xdp)) { + xdp_buff_set_frags_flag(xdp); shinfo->nr_frags = 0; + } total_frag_len = __bnxt_rx_agg_netmems(bp, cpr, idx, agg_bufs, tpa, NULL, xdp); - if (total_frag_len) { - xdp_buff_set_frags_flag(xdp); - shinfo->nr_frags = agg_bufs; - shinfo->xdp_frags_size = total_frag_len; + if (!total_frag_len) { + bnxt_xdp_buff_frags_free(cpr->bnapi->rx_ring, xdp); + return 0; } + shinfo->nr_frags = agg_bufs; + shinfo->xdp_frags_size = total_frag_len; return total_frag_len; } @@ -1500,6 +1511,14 @@ static struct sk_buff *bnxt_copy_xdp(struct bnxt_napi *bnapi, __skb_pull(skb, metasize); } + if (unlikely(xdp_buff_has_frags(xdp))) { + struct skb_shared_info *sinfo; + + sinfo = xdp_get_shared_info_from_buff(xdp); + memcpy(skb_shinfo(skb)->frags, sinfo->frags, + sinfo->nr_frags * sizeof(skb_frag_t)); + } + return skb; } @@ -2285,8 +2304,10 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr, cp_cons, agg_bufs, false); - if (!frag_len) + if (!frag_len) { + bnxt_reuse_rx_data(rxr, cons, data); goto oom_next_rx; + } } xdp_active = true; @@ -2313,17 +2334,8 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr, skb = bnxt_copy_xdp(bnapi, &bnxt_xdp.xdp, len, dma_addr); bnxt_reuse_rx_data(rxr, cons, data); - if (!skb) { - if (agg_bufs) { - if (!xdp_active) - bnxt_reuse_rx_agg_bufs(cpr, cp_cons, 0, - agg_bufs, false); - else - bnxt_xdp_buff_frags_free(rxr, - &bnxt_xdp.xdp); - } - goto oom_next_rx; - } + if (!skb) + goto oom_next_rx_agg; } else { u32 payload; @@ -2334,7 +2346,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr, skb = bp->rx_skb_func(bp, rxr, cons, data, data_ptr, dma_addr, payload | len); if (!skb) - goto oom_next_rx; + goto oom_next_rx_agg; } if (agg_bufs) { @@ -2424,6 +2436,14 @@ next_rx_no_prod_no_len: return rc; +oom_next_rx_agg: + if (agg_bufs) { + if (!xdp_active) + bnxt_reuse_rx_agg_bufs(cpr, cp_cons, 0, agg_bufs, + false); + else + bnxt_xdp_buff_frags_free(rxr, &bnxt_xdp.xdp); + } oom_next_rx: cpr->sw_stats->rx.rx_oom_discards += 1; rc = -ENOMEM; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 622e89587e5d..ef4bea4a0f8c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -4601,6 +4601,7 @@ static int bnxt_set_tunable(struct net_device *dev, if (netif_running(dev)) return -EBUSY; bp->rx_copybreak = rx_copybreak; + bnxt_set_ring_params(bp); } return 0; case ETHTOOL_PFC_PREVENTION_TOUT: diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index 96845c8646e0..161e0dd62be6 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -307,6 +307,7 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, if (xdp_do_redirect(bp->dev, xdp, xdp_prog)) { trace_xdp_exception(bp->dev, xdp_prog, act); + bnxt_xdp_buff_frags_free(rxr, xdp); page_pool_recycle_direct(rxr->page_pool, page); return true; } diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index a6b7797558b0..4d2be2a21ed1 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -931,10 +931,13 @@ static int bcmgenet_set_coalesce(struct net_device *dev, /* Program all TX queues with the same values, as there is no * ethtool knob to do coalescing on a per-queue basis */ - for (i = 0; i <= priv->hw_params->tx_queues; i++) + for (i = 0; i <= priv->hw_params->tx_queues; i++) { + priv->tx_rings[i].tx_max_coalesced_frames = + ec->tx_max_coalesced_frames; bcmgenet_tdma_ring_writel(priv, i, ec->tx_max_coalesced_frames, DMA_MBUF_DONE_THRESH); + } for (i = 0; i <= priv->hw_params->rx_queues; i++) bcmgenet_set_ring_rx_coalesce(&priv->rx_rings[i], ec); @@ -2753,7 +2756,8 @@ static void bcmgenet_init_tx_ring(struct bcmgenet_priv *priv, bcmgenet_tdma_ring_writel(priv, index, 0, TDMA_PROD_INDEX); bcmgenet_tdma_ring_writel(priv, index, 0, TDMA_CONS_INDEX); - bcmgenet_tdma_ring_writel(priv, index, 1, DMA_MBUF_DONE_THRESH); + bcmgenet_tdma_ring_writel(priv, index, ring->tx_max_coalesced_frames, + DMA_MBUF_DONE_THRESH); /* Disable rate control for now */ bcmgenet_tdma_ring_writel(priv, index, flow_period_val, TDMA_FLOW_PERIOD); @@ -3112,7 +3116,9 @@ static void bcmgenet_fini_dma(struct bcmgenet_priv *priv) bcmgenet_free_rx_buffers(priv); bcmgenet_destroy_rx_page_pools(priv); kfree(priv->rx_cbs); + priv->rx_cbs = NULL; kfree(priv->tx_cbs); + priv->tx_cbs = NULL; } /* init_edma: Initialize DMA control register */ @@ -3170,6 +3176,7 @@ static int bcmgenet_init_dma(struct bcmgenet_priv *priv, bool flush_rx) priv->tx_cbs = kzalloc_objs(struct enet_cb, priv->num_tx_bds); if (!priv->tx_cbs) { kfree(priv->rx_cbs); + priv->rx_cbs = NULL; return -ENOMEM; } @@ -3189,7 +3196,9 @@ static int bcmgenet_init_dma(struct bcmgenet_priv *priv, bool flush_rx) bcmgenet_free_rx_buffers(priv); bcmgenet_destroy_rx_page_pools(priv); kfree(priv->rx_cbs); + priv->rx_cbs = NULL; kfree(priv->tx_cbs); + priv->tx_cbs = NULL; return ret; } @@ -3498,7 +3507,9 @@ static int bcmgenet_close(struct net_device *dev) netif_dbg(priv, ifdown, dev, "bcmgenet_close\n"); - bcmgenet_netif_stop(dev, false); + /* A failed resume has torn the datapath down already */ + if (priv->tx_cbs) + bcmgenet_netif_stop(dev, false); /* Really kill the PHY state machine and disconnect from it */ phy_disconnect(dev->phydev); @@ -3506,6 +3517,9 @@ static int bcmgenet_close(struct net_device *dev) free_irq(priv->irq0, priv); free_irq(priv->irq1, priv); + /* A failed resume left the device detached, __dev_open() refuses that */ + netif_device_attach(dev); + if (priv->internal_phy) ret = bcmgenet_power_down(priv, GENET_POWER_PASSIVE); @@ -4161,6 +4175,8 @@ static int bcmgenet_probe(struct platform_device *pdev) priv->rx_rings[i].index = i; priv->rx_rings[i].rx_max_coalesced_frames = 1; } + for (i = 0; i <= priv->hw_params->tx_queues; i++) + priv->tx_rings[i].tx_max_coalesced_frames = 1; /* Initialize u64 stats seq counter for 32bit machines */ for (i = 0; i <= GENET_MAX_MQ_CNT; i++) { @@ -4263,7 +4279,11 @@ static int bcmgenet_resume(struct device *d) if (device_may_wakeup(d) && priv->wolopts) { reg = bcmgenet_umac_readl(priv, UMAC_CMD); - if (reg & CMD_RX_EN) { + + /* The WoL power down sets CMD_RX_EN itself, so the bit + * alone does not mean the rings survived + */ + if (priv->tx_cbs && (reg & CMD_RX_EN)) { /* Successfully exited WoL, just resume data flows */ list_for_each_entry(rule, &priv->rxnfc_list, list) if (rule->state == BCMGENET_RXNFC_STATE_ENABLED) @@ -4293,6 +4313,9 @@ static int bcmgenet_resume(struct device *d) bcmgenet_rdma_disable(priv); bcmgenet_intr_disable(priv); bcmgenet_fini_dma(priv); + if (phy_is_started(dev->phydev) || + dev->phydev->state == PHY_ERROR) + phy_stop(dev->phydev); enable_irq(priv->irq1); } @@ -4315,8 +4338,9 @@ static int bcmgenet_resume(struct device *d) /* Reinitialize TDMA and RDMA and SW housekeeping */ ret = bcmgenet_init_dma(priv, false); if (ret) { + /* Leave the block clocked, close and suspend tear it down */ netdev_err(dev, "failed to initialize DMA\n"); - goto out_clk_disable; + return ret; } if (!device_may_wakeup(d)) @@ -4327,24 +4351,57 @@ static int bcmgenet_resume(struct device *d) netif_device_attach(dev); return 0; +} -out_clk_disable: - if (priv->internal_phy) - bcmgenet_power_down(priv, GENET_POWER_PASSIVE); - clk_disable_unprepare(priv->clk); - return ret; +/* Arm the configured wake filters and drop the rest, then clear old + * matches so that only a new one wakes the system + */ +static void bcmgenet_wol_arm_filters(struct bcmgenet_priv *priv) +{ + struct bcmgenet_rxnfc_rule *rule; + u32 reg, hfb_enable = 0; + + if (priv->wolopts & WAKE_FILTER) + list_for_each_entry(rule, &priv->rxnfc_list, list) + if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE && + rule->state == BCMGENET_RXNFC_STATE_ENABLED) + hfb_enable |= 1 << rule->fs.location; + + reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL); + if (GENET_IS_V1(priv) || GENET_IS_V2(priv)) { + reg &= ~RBUF_HFB_FILTER_EN_MASK; + reg |= hfb_enable << (RBUF_HFB_FILTER_EN_SHIFT + 1); + } else { + bcmgenet_hfb_reg_writel(priv, hfb_enable << 1, + HFB_FLT_ENABLE_V3PLUS + 4); + } + if (!hfb_enable) + reg &= ~RBUF_HFB_EN; + bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL); + + bcmgenet_intrl2_0_writel(priv, 0xFFFFFFFF, INTRL2_CPU_MASK_SET); + bcmgenet_intrl2_0_writel(priv, 0xFFFFFFFF, INTRL2_CPU_CLEAR); } static int bcmgenet_suspend(struct device *d) { struct net_device *dev = dev_get_drvdata(d); struct bcmgenet_priv *priv = netdev_priv(dev); - struct bcmgenet_rxnfc_rule *rule; - u32 reg, hfb_enable = 0; if (!netif_running(dev)) return 0; + /* A failed resume has torn the datapath down already. Resume still + * enables irq1 on the WoL path, so it has to be disabled here. + */ + if (!priv->tx_cbs) { + if (device_may_wakeup(d) && priv->wolopts) { + bcmgenet_wol_arm_filters(priv); + disable_irq(priv->irq1); + } + return 0; + } + netif_device_detach(dev); if (device_may_wakeup(d) && priv->wolopts) { @@ -4353,26 +4410,7 @@ static int bcmgenet_suspend(struct device *d) netif_tx_disable(dev); /* Suspend non-wake Rx data flows */ - if (priv->wolopts & WAKE_FILTER) - list_for_each_entry(rule, &priv->rxnfc_list, list) - if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE && - rule->state == BCMGENET_RXNFC_STATE_ENABLED) - hfb_enable |= 1 << rule->fs.location; - reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL); - if (GENET_IS_V1(priv) || GENET_IS_V2(priv)) { - reg &= ~RBUF_HFB_FILTER_EN_MASK; - reg |= hfb_enable << (RBUF_HFB_FILTER_EN_SHIFT + 1); - } else { - bcmgenet_hfb_reg_writel(priv, hfb_enable << 1, - HFB_FLT_ENABLE_V3PLUS + 4); - } - if (!hfb_enable) - reg &= ~RBUF_HFB_EN; - bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL); - - /* Clear any old filter matches so only new matches wake */ - bcmgenet_intrl2_0_writel(priv, 0xFFFFFFFF, INTRL2_CPU_MASK_SET); - bcmgenet_intrl2_0_writel(priv, 0xFFFFFFFF, INTRL2_CPU_CLEAR); + bcmgenet_wol_arm_filters(priv); if (-ETIMEDOUT == bcmgenet_tdma_disable(priv)) netdev_warn(priv->dev, diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index 86f2aed20dbe..e48ce39d3e7c 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -553,6 +553,7 @@ struct bcmgenet_tx_ring { unsigned int prod_index; /* Tx ring producer index SW copy */ unsigned int cb_ptr; /* Tx ring initial CB ptr */ unsigned int end_ptr; /* Tx ring end CB ptr */ + unsigned int tx_max_coalesced_frames; struct bcmgenet_priv *priv; }; diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index ce108705bbaf..95827df4cf6a 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -18256,29 +18256,11 @@ unlock: static SIMPLE_DEV_PM_OPS(tg3_pm_ops, tg3_suspend, tg3_resume); -/* Systems where ACPI _PTS (Prepare To Sleep) S5 will result in a fatal - * PCIe AER event on the tg3 device if the tg3 device is not, or cannot - * be, powered down. +/* Systems where powering down a tg3 device on restarts triggers + * NMIs and fatal hangs during reboot if the network adapters were + * initialized under SNP. */ -static const struct dmi_system_id tg3_restart_aer_quirk_table[] = { - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge R440"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge R540"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge R640"), - }, - }, +static const struct dmi_system_id tg3_restart_snp_quirk_table[] = { { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), @@ -18288,21 +18270,9 @@ static const struct dmi_system_id tg3_restart_aer_quirk_table[] = { { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge R740"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge R750"), }, }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "datto"), - DMI_MATCH(DMI_PRODUCT_NAME, "SIRIS 5 1U Server"), - }, - }, {} }; @@ -18320,14 +18290,18 @@ static void tg3_shutdown(struct pci_dev *pdev) if (netif_running(dev)) dev_close(dev); - if (system_state == SYSTEM_POWER_OFF) + if (!dmi_first_match(tg3_restart_snp_quirk_table) || + system_state == SYSTEM_POWER_OFF) { tg3_power_down(tp); - else if (system_state == SYSTEM_RESTART && - dmi_first_match(tg3_restart_aer_quirk_table) && + } else if (system_state == SYSTEM_RESTART && pdev->current_state != PCI_D3cold && pdev->current_state != PCI_UNKNOWN) { /* Disable PCIe AER on the tg3 to avoid a fatal * error during this system restart. + * Needed since on restarts for systems in + * tg3_restart_snp_quirk_table skipping on powering down the + * tg3 device causes a fatal AER error during ACPI _PTS + * (Prepare To Sleep). */ pcie_capability_clear_word(pdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_CERE | diff --git a/drivers/net/ethernet/google/gve/gve_flow_rule.c b/drivers/net/ethernet/google/gve/gve_flow_rule.c index fae552f4ad6f..d7f9aa898a80 100644 --- a/drivers/net/ethernet/google/gve/gve_flow_rule.c +++ b/drivers/net/ethernet/google/gve/gve_flow_rule.c @@ -206,6 +206,7 @@ int gve_get_flow_rule_entry(struct gve_priv *priv, struct ethtool_rxnfc *cmd) return -EOPNOTSUPP; if (!priv->flow_rules_cache.rules_cache_synced || + !*cache_num || fsp->location < be32_to_cpu(rules_cache[0].location) || fsp->location > be32_to_cpu(rules_cache[*cache_num - 1].location)) { err = gve_adminq_query_flow_rules(priv, GVE_FLOW_RULE_QUERY_RULES, fsp->location); diff --git a/drivers/net/ethernet/hisilicon/hisi_femac.c b/drivers/net/ethernet/hisilicon/hisi_femac.c index 016605e86f0b..a44ea0a2b1ee 100644 --- a/drivers/net/ethernet/hisilicon/hisi_femac.c +++ b/drivers/net/ethernet/hisilicon/hisi_femac.c @@ -491,6 +491,7 @@ static int hisi_femac_net_close(struct net_device *dev) netif_stop_queue(dev); napi_disable(&priv->napi); + hisi_femac_port_reset(priv); hisi_femac_free_skb_rings(priv); return 0; diff --git a/drivers/net/ethernet/i825xx/82596.c b/drivers/net/ethernet/i825xx/82596.c index 773d7aa29ef5..bb8798b0d3ae 100644 --- a/drivers/net/ethernet/i825xx/82596.c +++ b/drivers/net/ethernet/i825xx/82596.c @@ -196,11 +196,10 @@ enum commands { #define TX_TIMEOUT (HZ/20) -struct i596_reg { - unsigned short porthi; - unsigned short portlo; - unsigned long ca; -}; +/* MVME16x: the 82596 PORT and CA registers */ +#define MVME_I596_PORT_HI 0x00 +#define MVME_I596_PORT_LO 0x02 +#define MVME_I596_CA 0x04 #define EOF 0x8000 #define SIZE_MASK 0x3fff @@ -375,7 +374,9 @@ static inline void CA(struct net_device *dev) { #ifdef ENABLE_MVME16x_NET if (MACH_IS_MVME16x) { - ((struct i596_reg *) dev->base_addr)->ca = 1; + void __iomem *regs = (void __iomem *)dev->base_addr; + + __raw_writel(1, regs + MVME_I596_CA); } #endif #ifdef ENABLE_BVME6000_NET @@ -392,9 +393,15 @@ static inline void MPU_PORT(struct net_device *dev, int c, volatile void *x) { #ifdef ENABLE_MVME16x_NET if (MACH_IS_MVME16x) { - struct i596_reg *p = (struct i596_reg *) (dev->base_addr); - p->porthi = ((c) | (u32) (x)) & 0xffff; - p->portlo = ((c) | (u32) (x)) >> 16; + void __iomem *regs = (void __iomem *)dev->base_addr; + u32 v = (u32)c | (u32)x; + + /* + * The 32bit value is latched on the write of the lo register, + * so the 16bit writes must happen in this order. + */ + __raw_writew(v & 0xffff, regs + MVME_I596_PORT_HI); + __raw_writew(v >> 16, regs + MVME_I596_PORT_LO); } #endif #ifdef ENABLE_BVME6000_NET diff --git a/drivers/net/ethernet/intel/ice/ice_adapter.c b/drivers/net/ethernet/intel/ice/ice_adapter.c index 2dc3629d6d0f..536923b6ae97 100644 --- a/drivers/net/ethernet/intel/ice/ice_adapter.c +++ b/drivers/net/ethernet/intel/ice/ice_adapter.c @@ -6,6 +6,7 @@ #include <linux/pci.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/srcu.h> #include <linux/xarray.h> #include "ice_adapter.h" #include "ice.h" @@ -54,11 +55,18 @@ static unsigned long ice_adapter_xa_index(struct pci_dev *pdev) static struct ice_adapter *ice_adapter_new(struct pci_dev *pdev) { struct ice_adapter *adapter; + int err; adapter = kzalloc_obj(*adapter); if (!adapter) return NULL; + err = init_srcu_struct(&adapter->ports.srcu); + if (err) { + kfree(adapter); + return NULL; + } + adapter->index = ice_adapter_index(pdev); spin_lock_init(&adapter->ptp_gltsyn_time_lock); spin_lock_init(&adapter->txq_ctx_lock); @@ -66,18 +74,19 @@ static struct ice_adapter *ice_adapter_new(struct pci_dev *pdev) mutex_init(&adapter->cpi_phy_lock[i]); refcount_set(&adapter->refcount, 1); - mutex_init(&adapter->ports.lock); - INIT_LIST_HEAD(&adapter->ports.ports); + spin_lock_init(&adapter->ports.lock); + INIT_LIST_HEAD(&adapter->ports.list); return adapter; } static void ice_adapter_free(struct ice_adapter *adapter) { - WARN_ON(!list_empty(&adapter->ports.ports)); + WARN_ON(!list_empty(&adapter->ports.list)); for (int i = 0; i < ARRAY_SIZE(adapter->cpi_phy_lock); i++) mutex_destroy(&adapter->cpi_phy_lock[i]); - mutex_destroy(&adapter->ports.lock); + + cleanup_srcu_struct(&adapter->ports.srcu); kfree(adapter); } diff --git a/drivers/net/ethernet/intel/ice/ice_adapter.h b/drivers/net/ethernet/intel/ice/ice_adapter.h index 4f695f32da3d..c61bd645e460 100644 --- a/drivers/net/ethernet/intel/ice/ice_adapter.h +++ b/drivers/net/ethernet/intel/ice/ice_adapter.h @@ -6,6 +6,7 @@ #include <linux/types.h> #include <linux/mutex.h> +#include <linux/srcu.h> #include <linux/spinlock_types.h> #include <linux/refcount_types.h> @@ -17,15 +18,19 @@ struct ice_pf; /** * struct ice_port_list - data used to store the list of adapter ports * - * This structure contains data used to maintain a list of adapter ports + * This structure contains data used to maintain a list of adapter ports. + * Writers modifying the list *must* acquire the lock, and use SRCU safe list + * operations. Readers should use srcu_read_lock() on the provided domain. * - * @ports: list of ports - * @lock: protect access to the ports list + * @list: list of ports + * @lock: protect write access to the list + * @srcu: Sleepable RCU domain for this adapter */ struct ice_port_list { - struct list_head ports; - /* To synchronize the ports list operations */ - struct mutex lock; + struct list_head list; + /* To synchronize write operations on the port list */ + spinlock_t lock; + struct srcu_struct srcu; }; /** diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index b781004f856d..7980eaf9ce25 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2021, Intel Corporation. */ +#include <linux/rculist.h> +#include <linux/srcu.h> +#include <linux/wait_bit.h> #include "ice.h" #include "ice_lib.h" #include "ice_trace.h" @@ -367,7 +370,7 @@ ice_ptp_is_tx_tracker_up(struct ice_ptp_tx *tx) { lockdep_assert_held(&tx->lock); - return tx->init && !tx->calibrating; + return tx->init && !tx->disabled; } /** @@ -592,6 +595,12 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx) bool drop_ts = !link_up; struct sk_buff *skb; + /* Paired with smp_mb__before_atomic() in ice_ptp_request_ts() + * to ensure ordering the in_use bitmap and the indexed fields + * in the tstamps array. + */ + smp_rmb(); + /* Drop packets which have waited for more than 2 seconds */ if (time_is_before_jiffies(tx->tstamps[idx].start + 2 * HZ)) { drop_ts = true; @@ -676,20 +685,37 @@ skip_ts_read: pf->ptp.tx_hwtstamp_good += tstamp_good; } +static void ice_ptp_release_port_srcu(struct kref *ref) +{ + /* wake_up_var() requires full ordering but kref_put only provides + * release ordering. + */ + smp_mb__after_atomic(); + wake_up_var(ref); +} + static void ice_ptp_tx_tstamp_owner(struct ice_pf *pf) { + struct ice_port_list *ports = &pf->adapter->ports; struct ice_ptp_port *port; + int srcu_idx; - mutex_lock(&pf->adapter->ports.lock); - list_for_each_entry(port, &pf->adapter->ports.ports, list_node) { + srcu_idx = srcu_read_lock(&ports->srcu); + list_for_each_entry_srcu(port, &ports->list, list_node, + srcu_read_lock_held(&ports->srcu)) { struct ice_ptp_tx *tx = &port->tx; - if (!tx || !tx->init) + if (!tx->init) + continue; + + if (!kref_get_unless_zero(&port->ref)) continue; ice_ptp_process_tx_tstamp(tx); + + kref_put(&port->ref, ice_ptp_release_port_srcu); } - mutex_unlock(&pf->adapter->ports.lock); + srcu_read_unlock(&ports->srcu, srcu_idx); } /** @@ -809,10 +835,19 @@ ice_ptp_mark_tx_tracker_stale(struct ice_ptp_tx *tx) static void ice_ptp_flush_all_tx_tracker(struct ice_pf *pf) { + struct ice_port_list *ports = &pf->adapter->ports; struct ice_ptp_port *port; + int srcu_idx; - list_for_each_entry(port, &pf->adapter->ports.ports, list_node) + srcu_idx = srcu_read_lock(&ports->srcu); + list_for_each_entry_srcu(port, &ports->list, list_node, + srcu_read_lock_held(&ports->srcu)) { + if (!kref_get_unless_zero(&port->ref)) + continue; ice_ptp_flush_tx_tracker(ptp_port_to_pf(port), &port->tx); + kref_put(&port->ref, ice_ptp_release_port_srcu); + } + srcu_read_unlock(&ports->srcu, srcu_idx); } /** @@ -1136,6 +1171,7 @@ static int ice_ptp_check_tx_fifo(struct ice_ptp_port *port) static void ice_ptp_wait_for_offsets(struct kthread_work *work) { struct ice_ptp_port *port; + unsigned long flags; struct ice_pf *pf; struct ice_hw *hw; int tx_err; @@ -1156,9 +1192,26 @@ static void ice_ptp_wait_for_offsets(struct kthread_work *work) tx_err = ice_ptp_check_tx_fifo(port); if (!tx_err) tx_err = ice_phy_cfg_tx_offset_e82x(hw, port->port_num); + if (!tx_err) { + /* Tx offset has been configured, re-enable Tx timestamps */ + spin_lock_irqsave(&port->tx.lock, flags); + if (port->tx.disabled) { + port->tx.disabled = false; + dev_dbg(ice_pf_to_dev(pf), "PTP Tx offset valid for port %u, Tx timestamps enabled\n", + port->port_num); + } + spin_unlock_irqrestore(&port->tx.lock, flags); + } + rx_err = ice_phy_cfg_rx_offset_e82x(hw, port->port_num); if (tx_err || rx_err) { - /* Tx and/or Rx offset not yet configured, try again later */ + /* Tx and/or Rx offset not yet configured, try again later. + * This is expected during normal link-up: the vernier offset + * calibration cannot complete until at least one packet has + * been transmitted, so the first retries routinely land here. + */ + dev_dbg(ice_pf_to_dev(pf), "PTP offset not yet valid for port %u (tx_err=%d rx_err=%d)\n", + port->port_num, tx_err, rx_err); kthread_queue_delayed_work(pf->ptp.kworker, &port->ov_work, msecs_to_jiffies(100)); @@ -1236,11 +1289,9 @@ ice_ptp_port_phy_restart(struct ice_ptp_port *ptp_port) /* Start the PHY timer in Vernier mode */ kthread_cancel_delayed_work_sync(&ptp_port->ov_work); - /* temporarily disable Tx timestamps while calibrating - * PHY offset - */ + /* Disable Tx timestamps until PHY calibration completes */ spin_lock_irqsave(&ptp_port->tx.lock, flags); - ptp_port->tx.calibrating = true; + ptp_port->tx.disabled = true; spin_unlock_irqrestore(&ptp_port->tx.lock, flags); ptp_port->tx_fifo_busy_cnt = 0; @@ -1249,11 +1300,10 @@ ice_ptp_port_phy_restart(struct ice_ptp_port *ptp_port) if (err) break; - /* Enable Tx timestamps right away */ - spin_lock_irqsave(&ptp_port->tx.lock, flags); - ptp_port->tx.calibrating = false; - spin_unlock_irqrestore(&ptp_port->tx.lock, flags); - + /* Do not clear disabled flag here. Tx timestamp requests + * remain disabled until ice_ptp_wait_for_offsets() has + * verified that the Tx offset calibration has completed. + */ kthread_queue_delayed_work(pf->ptp.kworker, &ptp_port->ov_work, 0); break; @@ -1427,16 +1477,22 @@ static void ice_ptp_reset_phy_timestamping(struct ice_pf *pf) */ static void ice_ptp_restart_all_phy(struct ice_pf *pf) { - struct list_head *entry; + struct ice_port_list *ports = &pf->adapter->ports; + struct ice_ptp_port *port; + int srcu_idx; - list_for_each(entry, &pf->adapter->ports.ports) { - struct ice_ptp_port *port = list_entry(entry, - struct ice_ptp_port, - list_node); + srcu_idx = srcu_read_lock(&ports->srcu); + list_for_each_entry_srcu(port, &ports->list, list_node, + srcu_read_lock_held(&ports->srcu)) { + if (!kref_get_unless_zero(&port->ref)) + continue; if (port->link_up) ice_ptp_port_phy_restart(port); + + kref_put(&port->ref, ice_ptp_release_port_srcu); } + srcu_read_unlock(&ports->srcu, srcu_idx); } /** @@ -2643,11 +2699,13 @@ s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb) * a reference to the skb and the start time to allow discarding old * requests. */ - set_bit(idx, tx->in_use); - clear_bit(idx, tx->stale); tx->tstamps[idx].start = jiffies; tx->tstamps[idx].skb = skb_get(skb); skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + clear_bit(idx, tx->stale); + /* Ensure index is setup before marking it as used */ + smp_mb__before_atomic(); + set_bit(idx, tx->in_use); ice_trace(tx_tstamp_request, skb, idx); } @@ -2699,19 +2757,28 @@ static bool ice_port_has_timestamps(struct ice_ptp_tx *tx) static bool ice_any_port_has_timestamps(struct ice_pf *pf) { + struct ice_port_list *ports = &pf->adapter->ports; + bool have_tstamps = false; struct ice_ptp_port *port; + int srcu_idx; - scoped_guard(mutex, &pf->adapter->ports.lock) { - list_for_each_entry(port, &pf->adapter->ports.ports, - list_node) { - struct ice_ptp_tx *tx = &port->tx; + srcu_idx = srcu_read_lock(&ports->srcu); + list_for_each_entry_srcu(port, &ports->list, list_node, + srcu_read_lock_held(&ports->srcu)) { + if (!kref_get_unless_zero(&port->ref)) + continue; - if (ice_port_has_timestamps(tx)) - return true; - } + if (ice_port_has_timestamps(&port->tx)) + have_tstamps = true; + + kref_put(&port->ref, ice_ptp_release_port_srcu); + + if (have_tstamps) + break; } + srcu_read_unlock(&ports->srcu, srcu_idx); - return false; + return have_tstamps; } bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf) @@ -2970,14 +3037,18 @@ void ice_ptp_queue_work(struct ice_pf *pf) static void ice_ptp_prepare_rebuild_sec(struct ice_pf *pf, bool rebuild, enum ice_reset_req reset_type) { - struct list_head *entry; + struct ice_port_list *ports = &pf->adapter->ports; + struct ice_ptp_port *port; + int srcu_idx; - list_for_each(entry, &pf->adapter->ports.ports) { - struct ice_ptp_port *port = list_entry(entry, - struct ice_ptp_port, - list_node); + srcu_idx = srcu_read_lock(&ports->srcu); + list_for_each_entry_srcu(port, &ports->list, list_node, + srcu_read_lock_held(&ports->srcu)) { struct ice_pf *peer_pf = ptp_port_to_pf(port); + if (!kref_get_unless_zero(&port->ref)) + continue; + if (!ice_is_primary(&peer_pf->hw)) { if (rebuild) { /* TODO: When implementing rebuild=true: @@ -2989,7 +3060,10 @@ static void ice_ptp_prepare_rebuild_sec(struct ice_pf *pf, bool rebuild, ice_ptp_prepare_for_reset(peer_pf, reset_type); } } + + kref_put(&port->ref, ice_ptp_release_port_srcu); } + srcu_read_unlock(&ports->srcu, srcu_idx); } /** @@ -3169,11 +3243,11 @@ static int ice_ptp_setup_pf(struct ice_pf *pf) return -ENODEV; INIT_LIST_HEAD(&ptp->port.list_node); - mutex_lock(&pf->adapter->ports.lock); + kref_init(&ptp->port.ref); - list_add(&ptp->port.list_node, - &pf->adapter->ports.ports); - mutex_unlock(&pf->adapter->ports.lock); + spin_lock(&pf->adapter->ports.lock); + list_add_rcu(&ptp->port.list_node, &pf->adapter->ports.list); + spin_unlock(&pf->adapter->ports.lock); /* Seed the per-PHY Tx reference clock usage map for this port. * Only meaningful on E825 (other MAC types don't expose tx-clk @@ -3195,13 +3269,23 @@ static int ice_ptp_setup_pf(struct ice_pf *pf) static void ice_ptp_cleanup_pf(struct ice_pf *pf) { + struct ice_port_list *ports = &pf->adapter->ports; struct ice_ptp *ptp = &pf->ptp; + struct kref *ref; - if (pf->hw.mac_type != ICE_MAC_UNKNOWN) { - mutex_lock(&pf->adapter->ports.lock); - list_del(&ptp->port.list_node); - mutex_unlock(&pf->adapter->ports.lock); - } + if (pf->hw.mac_type == ICE_MAC_UNKNOWN) + return; + + spin_lock(&ports->lock); + list_del_rcu(&ptp->port.list_node); + spin_unlock(&ports->lock); + + ref = &ptp->port.ref; + kref_put(ref, ice_ptp_release_port_srcu); + + wait_var_event(ref, !kref_read(ref)); + + synchronize_srcu(&ports->srcu); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h index 0e40fef3b4e8..f0839d90e9d2 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp.h @@ -5,6 +5,8 @@ #define _ICE_PTP_H_ #include <linux/ptp_clock_kernel.h> +#include <linux/rculist.h> +#include <linux/kref.h> #include <linux/kthread.h> #include "ice_ptp_hw.h" @@ -104,8 +106,8 @@ enum ice_tx_tstamp_work { * @offset: offset into timestamp block to get the real index * @len: length of the tstamps and in_use fields. * @init: if true, the tracker is initialized; - * @calibrating: if true, the PHY is calibrating the Tx offset. During this - * window, timestamps are temporarily disabled. + * @disabled: set to true when new Tx timestamp requests are disabled, such as + * while the PHY Tx offset configuration has not yet completed. * @has_ready_bitmap: if true, the hardware has a valid Tx timestamp ready * bitmap register. If false, fall back to verifying new * timestamp values against previously cached copy. @@ -120,7 +122,7 @@ struct ice_ptp_tx { u8 offset; u8 len; u8 init : 1; - u8 calibrating : 1; + u8 disabled : 1; u8 has_ready_bitmap : 1; s8 last_ll_ts_idx_read; }; @@ -138,6 +140,7 @@ struct ice_ptp_tx { * and determine when the port's PHY offset is valid. * * @list_node: list member structure + * @ref: reference counter for use with adapter ports list * @tx: Tx timestamp tracking for this port * @ov_work: delayed work task for tracking when PHY offset is valid * @ps_lock: mutex used to protect the overall PTP PHY start procedure @@ -149,6 +152,7 @@ struct ice_ptp_tx { */ struct ice_ptp_port { struct list_head list_node; + struct kref ref; struct ice_ptp_tx tx; struct kthread_delayed_work ov_work; struct mutex ps_lock; /* protects overall PTP PHY start procedure */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index f91856498eb2..b9496f0bee13 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9444,23 +9444,6 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb, } protocol = vlan_get_protocol(skb); - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && - adapter->ptp_clock) { - if (adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON && - !test_and_set_bit_lock(__IXGBE_PTP_TX_IN_PROGRESS, - &adapter->state)) { - skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; - tx_flags |= IXGBE_TX_FLAGS_TSTAMP; - - /* schedule check for Tx timestamp */ - adapter->ptp_tx_skb = skb_get(skb); - adapter->ptp_tx_start = jiffies; - schedule_work(&adapter->ptp_tx_work); - } else { - adapter->tx_hwtstamp_skipped++; - } - } - #ifdef CONFIG_PCI_IOV /* * Use the l2switch_enable flag - would be false if the DMA @@ -9525,6 +9508,29 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb, #ifdef IXGBE_FCOE xmit_fcoe: #endif /* IXGBE_FCOE */ + /* Reserve the Tx timestamp only after all skb preparation: + * skb_get() makes the skb shared, and pskb_expand_head() + * (reached from skb_cow_head() or skb_checksum_help()) + * refuses shared skbs. + */ + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && + adapter->ptp_clock) { + if (adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON && + !test_and_set_bit_lock(__IXGBE_PTP_TX_IN_PROGRESS, + &adapter->state)) { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + tx_flags |= IXGBE_TX_FLAGS_TSTAMP; + first->tx_flags |= IXGBE_TX_FLAGS_TSTAMP; + + /* schedule check for Tx timestamp */ + adapter->ptp_tx_skb = skb_get(skb); + adapter->ptp_tx_start = jiffies; + schedule_work(&adapter->ptp_tx_work); + } else { + adapter->tx_hwtstamp_skipped++; + } + } + if (ixgbe_tx_map(tx_ring, first, hdr_len)) goto cleanup_tx_timestamp; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index f4e232a73de8..29559db68929 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -4111,9 +4111,12 @@ static int set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg) case NIX_FLOW_KEY_TYPE_INNR_IPV4: field->lid = NPC_LID_LC; field->ltype_match = NPC_LT_LC_IP; + field->ltype_mask = NPC_LT_LC_IP_MATCH_MSK; + if (key_type == NIX_FLOW_KEY_TYPE_INNR_IPV4) { field->lid = NPC_LID_LG; field->ltype_match = NPC_LT_LG_TU_IP; + field->ltype_mask = 0xF; } field->hdr_offset = 12; /* SIP offset */ field->bytesm1 = 7; /* SIP + DIP, 8 bytes */ @@ -4132,16 +4135,18 @@ static int set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg) field->bytesm1 = 3; /* DIP, 4 bytes */ } } - field->ltype_mask = NPC_LT_LC_IP_MATCH_MSK; keyoff_marker = false; break; case NIX_FLOW_KEY_TYPE_IPV6: case NIX_FLOW_KEY_TYPE_INNR_IPV6: field->lid = NPC_LID_LC; field->ltype_match = NPC_LT_LC_IP6; + field->ltype_mask = NPC_LT_LC_IP6_MATCH_MSK; + if (key_type == NIX_FLOW_KEY_TYPE_INNR_IPV6) { field->lid = NPC_LID_LG; field->ltype_match = NPC_LT_LG_TU_IP6; + field->ltype_mask = 0xF; } field->hdr_offset = 8; /* SIP offset */ field->bytesm1 = 31; /* SIP + DIP, 32 bytes */ @@ -4161,7 +4166,6 @@ static int set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg) field->bytesm1 = 15; /* DIP,16 bytes */ } } - field->ltype_mask = NPC_LT_LC_IP6_MATCH_MSK; break; case NIX_FLOW_KEY_TYPE_TCP: case NIX_FLOW_KEY_TYPE_UDP: diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c index 77543d472345..443435608781 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c @@ -1037,6 +1037,9 @@ bool cn10k_ipsec_transmit(struct otx2_nic *pf, struct netdev_queue *txq, cn10k_cpt_inst_flush(pf, &inst, sizeof(struct cpt_inst_s)); return true; drop: + otx2_dma_unmap_skb_frags(pf, &sq->sg[sq->head], skb); + sq->sg[sq->head].skb = (u64)NULL; + atomic_long_inc(&pf->hw.dev_stats.tx_discards); dev_kfree_skb_any(skb); - return false; + return true; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 4e418c7456b7..c85436457286 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -2115,10 +2115,8 @@ dma_addr_t otx2_dma_map_skb_frag(struct otx2_nic *pfvf, int offset; /* Crypto hardware need write permission for ipsec crypto offload */ - if (unlikely(xfrm_offload(skb))) { + if (unlikely(xfrm_offload(skb))) dir = DMA_BIDIRECTIONAL; - skb = skb_unclone_full(skb, GFP_ATOMIC); - } /* First segment is always skb->data */ if (!seg) { @@ -2134,13 +2132,12 @@ dma_addr_t otx2_dma_map_skb_frag(struct otx2_nic *pfvf, return otx2_dma_map_page(pfvf, page, offset, *len, dir); } -void otx2_dma_unmap_skb_frags(struct otx2_nic *pfvf, struct sg_list *sg) +void otx2_dma_unmap_skb_frags(struct otx2_nic *pfvf, struct sg_list *sg, + struct sk_buff *skb) { enum dma_data_direction dir = DMA_TO_DEVICE; - struct sk_buff *skb = NULL; int seg; - skb = (struct sk_buff *)sg->skb; if (unlikely(xfrm_offload(skb))) dir = DMA_BIDIRECTIONAL; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 3c7275d545bb..8417af94a02a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -1252,7 +1252,8 @@ static inline int mcam_entry_cmp(const void *a, const void *b) dma_addr_t otx2_dma_map_skb_frag(struct otx2_nic *pfvf, struct sk_buff *skb, int seg, int *len); -void otx2_dma_unmap_skb_frags(struct otx2_nic *pfvf, struct sg_list *sg); +void otx2_dma_unmap_skb_frags(struct otx2_nic *pfvf, struct sg_list *sg, + struct sk_buff *skb); int otx2_read_free_sqe(struct otx2_nic *pfvf, u16 qidx); void otx2_queue_vf_work(struct mbox *mw, struct workqueue_struct *mbox_wq, int first, int mdevs, u64 intr); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index 8d2d607bc92f..90af9b9044aa 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -160,7 +160,7 @@ static void otx2_snd_pkt_handler(struct otx2_nic *pfvf, *tx_bytes += skb->len; (*tx_pkts)++; - otx2_dma_unmap_skb_frags(pfvf, sg); + otx2_dma_unmap_skb_frags(pfvf, sg, skb); napi_consume_skb(skb, budget); sg->skb = (u64)NULL; } @@ -872,7 +872,7 @@ static int otx2_dma_map_tso_skb(struct otx2_nic *pfvf, } return 0; unmap: - otx2_dma_unmap_skb_frags(pfvf, sg); + otx2_dma_unmap_skb_frags(pfvf, sg, skb); return -EINVAL; } @@ -1205,6 +1205,15 @@ bool otx2_sq_append_skb(void *dev, struct netdev_queue *txq, if (free_desc < otx2_get_sqe_count(pfvf, skb)) return false; + /* Crypto hardware need write permission for ipsec crypto offload */ + if (unlikely(xfrm_offload(skb)) && + (skb_cloned(skb) || skb_is_nonlinear(skb)) && + skb_ensure_writable(skb, skb->len)) { + atomic_long_inc(&pfvf->hw.dev_stats.tx_discards); + dev_kfree_skb_any(skb); + return true; + } + num_segs = skb_shinfo(skb)->nr_frags + 1; /* If SKB doesn't fit in a single SQE, linearize it. @@ -1252,7 +1261,7 @@ bool otx2_sq_append_skb(void *dev, struct netdev_queue *txq, ret = otx2_sqe_add_sg(pfvf, sq, skb, num_segs, &offset); if (!ret) { - otx2_dma_unmap_skb_frags(pfvf, &sq->sg[sq->head]); + otx2_dma_unmap_skb_frags(pfvf, &sq->sg[sq->head], skb); return false; } @@ -1344,7 +1353,7 @@ void otx2_cleanup_tx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq) if (skb) { tx_bytes += skb->len; tx_pkts++; - otx2_dma_unmap_skb_frags(pfvf, sg); + otx2_dma_unmap_skb_frags(pfvf, sg, skb); dev_kfree_skb_any(skb); sg->skb = (u64)NULL; } @@ -1399,7 +1408,7 @@ void otx2_free_pending_sqe(struct otx2_nic *pfvf) if (skb) { tx_bytes += skb->len; tx_pkts++; - otx2_dma_unmap_skb_frags(pfvf, sg); + otx2_dma_unmap_skb_frags(pfvf, sg, skb); dev_kfree_skb_any(skb); sg->skb = (u64)NULL; } diff --git a/drivers/net/ethernet/mellanox/mlx4/intf.c b/drivers/net/ethernet/mellanox/mlx4/intf.c index 82e467c45d23..31860b4abe43 100644 --- a/drivers/net/ethernet/mellanox/mlx4/intf.c +++ b/drivers/net/ethernet/mellanox/mlx4/intf.c @@ -43,23 +43,12 @@ static DEFINE_IDA(mlx4_adev_ida); static bool is_eth_supported(struct mlx4_dev *dev) { - for (int port = 1; port <= dev->caps.num_ports; port++) - if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) - return true; - - return false; + return true; } static bool is_ib_supported(struct mlx4_dev *dev) { - for (int port = 1; port <= dev->caps.num_ports; port++) - if (dev->caps.port_type[port] == MLX4_PORT_TYPE_IB) - return true; - - if (dev->caps.flags & MLX4_DEV_CAP_FLAG_IBOE) - return true; - - return false; + return true; } static const struct mlx4_adev_device { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c index b9026cc64383..30a97e48dda7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c @@ -542,6 +542,12 @@ static int mlx5_devlink_num_doorbells_validate(struct devlink *devlink, u32 id, u32 val32 = val->vu32; u32 max_num_channels; + if (mlx5_dev_is_lightweight(mdev)) { + NL_SET_ERR_MSG_MOD(extack, + "Setting num_doorbells requires full device initialization; reload the device first"); + return -EOPNOTSUPP; + } + max_num_channels = mlx5e_get_max_num_channels(mdev); if (val32 > max_num_channels) { NL_SET_ERR_MSG_FMT_MOD(extack, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h index 3f212e46fc2f..5a340f1435e6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h @@ -218,6 +218,12 @@ static inline void mlx5e_accel_tx_finish(struct mlx5e_txqsq *sq, #endif } +static inline bool +mlx5e_accel_shampo_can_merge(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe) +{ + return mlx5e_psp_shampo_can_merge(rq->hw_gro_data->skb, cqe); +} + static inline int mlx5e_accel_init_rx(struct mlx5e_priv *priv) { return mlx5e_ktls_init_rx(priv); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index 16edd3263aed..7e991a09b3ff 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -958,6 +958,9 @@ static int mlx5e_ipsec_netevent_event(struct notifier_block *nb, xa_for_each_marked(&ipsec->sadb, idx, sa_entry, MLX5E_IPSEC_TUNNEL_SA) { attrs = &sa_entry->attrs; + if (attrs->addrs.family != n->tbl->family) + continue; + if (attrs->addrs.family == AF_INET) { if (!neigh_key_eq32(n, &attrs->addrs.saddr.a4) && !neigh_key_eq32(n, &attrs->addrs.daddr.a4)) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.h index 2b080c39cc37..9fd1ee789de0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.h @@ -85,6 +85,13 @@ static inline bool mlx5e_psp_is_rx_flow(struct mlx5_cqe64 *cqe) return MLX5_PSP_METADATA_MARKER(be32_to_cpu(cqe->ft_metadata)); } +static inline bool +mlx5e_psp_shampo_can_merge(struct sk_buff *skb, struct mlx5_cqe64 *cqe) +{ + /* PSP packets cannot be merged. */ + return !skb_ext_exist(skb, SKB_EXT_PSP) && !mlx5e_psp_is_rx_flow(cqe); +} + bool mlx5e_psp_offload_handle_rx_skb(struct net_device *netdev, struct sk_buff *skb, struct mlx5_cqe64 *cqe); #else @@ -110,6 +117,12 @@ static inline bool mlx5e_psp_is_rx_flow(struct mlx5_cqe64 *cqe) return false; } +static inline bool +mlx5e_psp_shampo_can_merge(struct sk_buff *skb, struct mlx5_cqe64 *cqe) +{ + return true; +} + static inline bool mlx5e_psp_offload_handle_rx_skb(struct net_device *netdev, struct sk_buff *skb, struct mlx5_cqe64 *cqe) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index f8a6efd56c51..22fea4739a2c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -49,6 +49,7 @@ #include "en_rep.h" #include "en/rep/tc.h" #include "ipoib/ipoib.h" +#include "en_accel/en_accel.h" #include "en_accel/ipsec.h" #include "en_accel/macsec.h" #include "en_accel/psp_rxtx.h" @@ -2268,8 +2269,9 @@ static void mlx5e_handle_rx_cqe_mpwrq_shampo(struct mlx5e_rq *rq, struct mlx5_cq } if (*skb && - !(match && mlx5e_hw_gro_skb_has_enough_space(*skb, data_bcnt, - page_size))) { + !(match && + mlx5e_hw_gro_skb_has_enough_space(*skb, data_bcnt, page_size) && + mlx5e_accel_shampo_can_merge(rq, cqe))) { match = false; mlx5e_shampo_flush_skb(rq, cqe, match); } diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c index c62d885a678a..c572a0062e77 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c @@ -1025,7 +1025,7 @@ static void fbnic_pkt_prepare(struct fbnic_napi_vector *nv, u64 rcd, /* Build frame around buffer */ hdr_start = page_address(page) + hdr_pg_start; - net_prefetch(pkt->buff.data); + net_prefetch(hdr_start + headroom); xdp_prepare_buff(&pkt->buff, hdr_start, headroom, len - FBNIC_RX_PAD, true); diff --git a/drivers/net/ethernet/microchip/fdma/fdma_api.c b/drivers/net/ethernet/microchip/fdma/fdma_api.c index f7a42348e932..fed41da550c5 100644 --- a/drivers/net/ethernet/microchip/fdma/fdma_api.c +++ b/drivers/net/ethernet/microchip/fdma/fdma_api.c @@ -43,9 +43,19 @@ int __fdma_dcb_add(struct fdma *fdma, int dcb_idx, u64 info, u64 status, u64 *dataptr)) { struct fdma_dcb *dcb = fdma_dcb_get(fdma, dcb_idx); + struct fdma_dcb *prev = fdma->last_dcb; u64 nextptr; int i, err; + /* Per the FDMA datasheet, adding a DCB to an active channel takes + * three steps: + * 1) Write NEXT_PTR of the current last DCB with the LSB forced to + * FDMA_DCB_INVALID_DATA, and flush that write. + * 2) Clear the LSB to complete the link. + * 3) Reload the channel (FDMA_CH_RELOAD); this is done by the caller + * once the chain has been extended. + */ + for (i = 0; i < fdma->n_dbs; i++) { err = __fdma_db_add(fdma, dcb_idx, i, status, db_cb); if (unlikely(err)) @@ -56,13 +66,25 @@ int __fdma_dcb_add(struct fdma *fdma, int dcb_idx, u64 info, u64 status, if (unlikely(err)) return err; - fdma->last_dcb->nextptr = cpu_to_le64(nextptr); - - fdma->last_dcb = dcb; - dcb->nextptr = cpu_to_le64(FDMA_DCB_INVALID_DATA); dcb->info = cpu_to_le64(info); + /* On the first DCB of fdma_dcbs_init() prev is the DCB itself; + * don't link it to itself. + */ + if (prev != dcb) { + /* Write NEXT_PTR of the current last DCB with the LSB forced + * to FDMA_DCB_INVALID_DATA, and flush that write. + */ + prev->nextptr = cpu_to_le64(nextptr | FDMA_DCB_INVALID_DATA); + dma_wmb(); + + /* Clear the LSB to complete the link. */ + prev->nextptr = cpu_to_le64(nextptr); + } + + fdma->last_dcb = dcb; + return 0; } EXPORT_SYMBOL_GPL(__fdma_dcb_add); diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c index a7940eca5df3..21668bbcc906 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c @@ -336,14 +336,13 @@ static void lan966x_fdma_tx_clear_buf(struct lan966x *lan966x, int weight) struct lan966x_tx_dcb_buf *dcb_buf; struct fdma *fdma = &tx->fdma; struct xdp_frame_bulk bq; - unsigned long flags; bool clear = false; struct fdma_db *db; int i; xdp_frame_bulk_init(&bq); - spin_lock_irqsave(&lan966x->tx_lock, flags); + spin_lock(&lan966x->tx_lock); for (i = 0; i < fdma->n_dcbs; ++i) { dcb_buf = &tx->dcbs_buf[i]; @@ -388,7 +387,7 @@ static void lan966x_fdma_tx_clear_buf(struct lan966x *lan966x, int weight) if (clear) lan966x_fdma_wakeup_netdev(lan966x); - spin_unlock_irqrestore(&lan966x->tx_lock, flags); + spin_unlock(&lan966x->tx_lock); } static int lan966x_fdma_rx_check_frame(struct lan966x_rx *rx, u64 *src_port) @@ -686,7 +685,8 @@ int lan966x_fdma_xmit_xdpf(struct lan966x_port *port, void *ptr, u32 len) FDMA_DCB_STATUS_BLOCKO(0) | FDMA_DCB_STATUS_BLOCKL(next_dcb_buf->len), &fdma_nextptr_cb, - &lan966x_fdma_xdp_tx_dataptr_cb); + len ? &lan966x_fdma_xdp_tx_dataptr_cb : + &lan966x_fdma_tx_dataptr_cb); /* Start the transmission */ lan966x_fdma_tx_start(tx); diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_mac.c b/drivers/net/ethernet/microchip/lan966x/lan966x_mac.c index 7a2294a0d099..986c60ac2343 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_mac.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_mac.c @@ -447,7 +447,7 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row, lan966x_mac_process_raw_entry(&raw_entries[column], mac, &vid, &dest_idx); - if (WARN_ON(dest_idx >= lan966x->num_phys_ports)) + if (dest_idx >= lan966x->num_phys_ports) continue; /* If the entry in SW is found, then there is nothing @@ -501,7 +501,7 @@ static void lan966x_mac_irq_process(struct lan966x *lan966x, u32 row, lan966x_mac_process_raw_entry(&raw_entries[column], mac, &vid, &dest_idx); - if (WARN_ON(dest_idx >= lan966x->num_phys_ports)) + if (dest_idx >= lan966x->num_phys_ports) continue; spin_lock(&lan966x->mac_lock); diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index e4c9c5812cca..c276fec2f9d6 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -263,9 +263,8 @@ static int lan966x_port_ifh_xmit(struct sk_buff *skb, lan_wr((__force u32)ifh[i], lan966x, QS_INJ_WR(grp)); } - /* Write frame */ - count = DIV_ROUND_UP(skb->len, 4); - last = skb->len % 4; + /* Write the whole words of the frame */ + count = skb->len / 4; for (i = 0; i < count; ++i) { /* Wait until the fifo is ready */ err = lan966x_port_inj_ready(lan966x, grp); @@ -275,6 +274,20 @@ static int lan966x_port_ifh_xmit(struct sk_buff *skb, lan_wr(((u32 *)skb->data)[i], lan966x, QS_INJ_WR(grp)); } + /* Write the last 1 to 3 bytes, zero padded to a whole word */ + last = skb->len % 4; + if (last) { + /* Wait until the fifo is ready */ + err = lan966x_port_inj_ready(lan966x, grp); + if (err) + goto err; + + val = 0; + memcpy(&val, skb->data + count * 4, last); + lan_wr(val, lan966x, QS_INJ_WR(grp)); + ++i; + } + /* Add padding */ while (i < (LAN966X_BUFFER_MIN_SZ / 4)) { /* Wait until the fifo is ready */ diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c index 8c40db90ee8f..9be2cfe6feeb 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c @@ -376,9 +376,10 @@ static void lan966x_ptp_classify(struct lan966x_port *port, struct sk_buff *skb, *rew_op = IFH_REW_OP_TWO_STEP_PTP; } -static void lan966x_ptp_txtstamp_old_release(struct lan966x_port *port) +static unsigned int lan966x_ptp_txtstamp_old_release(struct lan966x_port *port) { struct sk_buff *skb, *skb_tmp; + unsigned int purged = 0; unsigned long flags; spin_lock_irqsave(&port->tx_skbs.lock, flags); @@ -389,14 +390,18 @@ static void lan966x_ptp_txtstamp_old_release(struct lan966x_port *port) __skb_unlink(skb, &port->tx_skbs); dev_kfree_skb_any(skb); + purged++; } spin_unlock_irqrestore(&port->tx_skbs.lock, flags); + + return purged; } int lan966x_ptp_txtstamp_request(struct lan966x_port *port, struct sk_buff *skb) { struct lan966x *lan966x = port->lan966x; + unsigned int purged; unsigned long flags; u8 pdu_type; u8 rew_op; @@ -408,9 +413,10 @@ int lan966x_ptp_txtstamp_request(struct lan966x_port *port, if (rew_op != IFH_REW_OP_TWO_STEP_PTP) return 0; - lan966x_ptp_txtstamp_old_release(port); + purged = lan966x_ptp_txtstamp_old_release(port); spin_lock_irqsave(&lan966x->ptp_ts_id_lock, flags); + lan966x->ptp_skbs -= purged; if (lan966x->ptp_skbs == LAN966X_MAX_PTP_ID) { spin_unlock_irqrestore(&lan966x->ptp_ts_id_lock, flags); return -EBUSY; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c index dbe86f937b21..15ec9b053824 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c @@ -217,7 +217,6 @@ int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb, struct fdma *fdma = &tx->fdma; void *virt_addr; - fdma_dcb_advance(fdma); if (!fdma_db_is_done(fdma_db_get(fdma, fdma->dcb_index, 0))) return -EINVAL; @@ -235,6 +234,8 @@ int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb, FDMA_DCB_STATUS_BLOCKO(0) | FDMA_DCB_STATUS_BLOCKL(skb->len + IFH_LEN * 4 + 4)); + fdma_dcb_advance(fdma); + sparx5_fdma_reload(sparx5, fdma); return NETDEV_TX_OK; diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c index f713656f1fae..14c9ae673e35 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_packet.c @@ -169,8 +169,8 @@ static int sparx5_inject(struct sparx5 *sparx5, struct sk_buff *skb, struct net_device *ndev) { + u32 val, w, count, last; int grp = INJ_QUEUE; - u32 val, w, count; u8 *buf; val = spx5_rd(sparx5, QS_INJ_STATUS); @@ -189,14 +189,23 @@ static int sparx5_inject(struct sparx5 *sparx5, for (w = 0; w < IFH_LEN; w++) spx5_wr(ifh[w], sparx5, QS_INJ_WR(grp)); - /* Write words, round up */ - count = DIV_ROUND_UP(skb->len, 4); + /* Write the whole words of the frame */ + count = skb->len / 4; buf = skb->data; for (w = 0; w < count; w++, buf += 4) { val = get_unaligned((const u32 *)buf); spx5_wr(val, sparx5, QS_INJ_WR(grp)); } + /* Write the last 1 to 3 bytes, zero padded to a whole word */ + last = skb->len % 4; + if (last) { + val = 0; + memcpy(&val, buf, last); + spx5_wr(val, sparx5, QS_INJ_WR(grp)); + w++; + } + /* Add padding */ while (w < (60 / 4)) { spx5_wr(0, sparx5, QS_INJ_WR(grp)); diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c index 683e0ea74113..c6ce9dbee3d0 100644 --- a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c +++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c @@ -392,8 +392,11 @@ static void mucse_mbx_fw_req_handler(struct mucse_hw *hw, u16 speed = le16_to_cpu(req->link_stat.st.speed); spin_lock_irqsave(&mucse->link_lock, flags); - if (magic != ST_VALID_MAGIC) { - /* Do not change the cached state for an invalid event. + /* Link-down events do not carry valid speed/duplex information + * and may have a zero port_magic. Validate it only for link up. + */ + if (mucse_link_is_up(hw, req) && magic != ST_VALID_MAGIC) { + /* Keep the cached state for an invalid link-up event. * Use an invalid speed encoding to make firmware report * again. */ diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index 2b7c947ff4f2..bd4399758ed4 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -94,6 +94,7 @@ struct nfp_fl_stats_id { * @ipv6_off_lock: Lock for the IPv6 address list * @mac_off_ids: IDA to manage id assignment for offloaded MACs * @neigh_nb: Notifier to monitor neighbour state + * @neigh_wq: Workqueue for neighbour updates */ struct nfp_fl_tunnel_offloads { struct rhashtable offloaded_macs; @@ -103,6 +104,7 @@ struct nfp_fl_tunnel_offloads { struct mutex ipv6_off_lock; struct ida mac_off_ids; struct notifier_block neigh_nb; + struct workqueue_struct *neigh_wq; }; /** diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c index 3b47e9fe64e1..46b6cda7f3d3 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c @@ -736,7 +736,7 @@ nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event, if (!update_work) return NOTIFY_DONE; - queue_work(system_highpri_wq, &update_work->work); + queue_work(app_priv->tun.neigh_wq, &update_work->work); return NOTIFY_DONE; } @@ -1509,6 +1509,13 @@ int nfp_tunnel_config_start(struct nfp_app *app) if (err) return err; + priv->tun.neigh_wq = alloc_workqueue("nfp-flower-neigh", + WQ_HIGHPRI | WQ_PERCPU, 0); + if (!priv->tun.neigh_wq) { + err = -ENOMEM; + goto err_free_macs; + } + ida_init(&priv->tun.mac_off_ids); /* Initialise priv data for IPv4/v6 offloading. */ @@ -1521,13 +1528,17 @@ int nfp_tunnel_config_start(struct nfp_app *app) priv->tun.neigh_nb.notifier_call = nfp_tun_neigh_event_handler; err = register_netevent_notifier(&priv->tun.neigh_nb); - if (err) { - rhashtable_free_and_destroy(&priv->tun.offloaded_macs, - nfp_check_rhashtable_empty, NULL); - return err; - } + if (err) + goto err_destroy_neigh_wq; return 0; + +err_destroy_neigh_wq: + destroy_workqueue(priv->tun.neigh_wq); +err_free_macs: + rhashtable_free_and_destroy(&priv->tun.offloaded_macs, + nfp_check_rhashtable_empty, NULL); + return err; } void nfp_tunnel_config_stop(struct nfp_app *app) @@ -1537,6 +1548,7 @@ void nfp_tunnel_config_stop(struct nfp_app *app) struct list_head *ptr, *storage; unregister_netevent_notifier(&priv->tun.neigh_nb); + destroy_workqueue(priv->tun.neigh_wq); ida_destroy(&priv->tun.mac_off_ids); diff --git a/drivers/net/ethernet/stmicro/stmmac/descs.h b/drivers/net/ethernet/stmicro/stmmac/descs.h index 846007cc245f..0af819a98fda 100644 --- a/drivers/net/ethernet/stmicro/stmmac/descs.h +++ b/drivers/net/ethernet/stmicro/stmmac/descs.h @@ -116,7 +116,7 @@ #define ETDES1_BUFFER2_SIZE_MASK GENMASK(28, 16) /* Extended Receive descriptor definitions */ -#define ERDES4_IP_PAYLOAD_TYPE_MASK GENMASK(6, 2) +#define ERDES4_IP_PAYLOAD_TYPE_MASK GENMASK(2, 0) #define ERDES4_IP_HDR_ERR BIT(3) #define ERDES4_IP_PAYLOAD_ERR BIT(4) #define ERDES4_IP_CSUM_BYPASSED BIT(5) @@ -147,6 +147,10 @@ #define RDES_PTP_SIGNALING 0xa #define RDES_PTP_PKT_RESERVED_TYPE 0xf +#define RDES_EXT_PAYLOAD_UDP 0x1 +#define RDES_EXT_PAYLOAD_TCP 0x2 +#define RDES_EXT_PAYLOAD_ICMP 0x3 + /* Basic descriptor structure for normal and alternate descriptors */ struct dma_desc { __le32 des0; diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index 051253601225..023b54b72478 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c @@ -109,8 +109,25 @@ static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err) return ret; } -static void enh_desc_get_ext_status(struct stmmac_extra_stats *x, - struct dma_extended_desc *p) +static int enh_desc_ext_rx_csum(u32 rdes0, u32 rdes4, int status) +{ + u32 type = FIELD_GET(ERDES4_IP_PAYLOAD_TYPE_MASK, rdes4); + + if (status != csum_none || !(rdes0 & RDES0_FRAME_TYPE)) + return status; + if (!(rdes4 & (ERDES4_IPV4_PKT_RCVD | ERDES4_IPV6_PKT_RCVD)) || + (rdes4 & (ERDES4_IP_HDR_ERR | ERDES4_IP_PAYLOAD_ERR | + ERDES4_IP_CSUM_BYPASSED))) + return status; + if (type != RDES_EXT_PAYLOAD_UDP && type != RDES_EXT_PAYLOAD_TCP && + type != RDES_EXT_PAYLOAD_ICMP) + return status; + + return good_frame; +} + +static int enh_desc_get_ext_status(struct stmmac_extra_stats *x, + struct dma_extended_desc *p, int status) { u32 rdes0 = le32_to_cpu(p->basic.des0); u32 rdes4 = le32_to_cpu(p->des4); @@ -170,7 +187,11 @@ static void enh_desc_get_ext_status(struct stmmac_extra_stats *x, x->l4_filter_match++; if (rdes4 & ERDES4_L3_L4_FILT_NO_MATCH_MASK) x->l3_l4_filter_no_match++; + + status = enh_desc_ext_rx_csum(rdes0, rdes4, status); } + + return status; } static int enh_desc_get_rx_status(struct stmmac_extra_stats *x, diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index 50b590d67d87..b2e557788803 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -70,8 +70,8 @@ struct stmmac_desc_ops { /* Return the reception status looking at the RDES1 */ int (*rx_status)(struct stmmac_extra_stats *x, struct dma_desc *p); - void (*rx_extended_status)(struct stmmac_extra_stats *x, - struct dma_extended_desc *p); + int (*rx_extended_status)(struct stmmac_extra_stats *x, + struct dma_extended_desc *p, int status); /* Set tx timestamp enable bit */ void (*enable_tx_timestamp) (struct dma_desc *p); /* get tx timestamp status */ @@ -128,7 +128,7 @@ struct stmmac_desc_ops { #define stmmac_rx_status(__priv, __args...) \ stmmac_do_callback(__priv, desc, rx_status, __args) #define stmmac_rx_extended_status(__priv, __args...) \ - stmmac_do_void_callback(__priv, desc, rx_extended_status, __args) + stmmac_do_callback(__priv, desc, rx_extended_status, __args) #define stmmac_enable_tx_timestamp(__priv, __args...) \ stmmac_do_void_callback(__priv, desc, enable_tx_timestamp, __args) #define stmmac_get_tx_timestamp_status(__priv, __args...) \ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index b20e5038eead..9ded31150fbe 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -5700,9 +5700,13 @@ read_again: if (!buf->xdp) break; - if (priv->extend_desc) - stmmac_rx_extended_status(priv, &priv->xstats, - rx_q->dma_erx + entry); + if (priv->extend_desc) { + /* Read the extended status after the own bit */ + dma_rmb(); + status = stmmac_rx_extended_status(priv, &priv->xstats, + &rx_q->dma_erx[entry], + status); + } if (unlikely(status == discard_frame)) { xsk_buff_free(buf->xdp); buf->xdp = NULL; @@ -5883,8 +5887,13 @@ read_again: prefetch(np); - if (priv->extend_desc) - stmmac_rx_extended_status(priv, &priv->xstats, rx_q->dma_erx + entry); + if (priv->extend_desc) { + /* Read the extended status after the own bit */ + dma_rmb(); + status = stmmac_rx_extended_status(priv, &priv->xstats, + &rx_q->dma_erx[entry], + status); + } if (unlikely(status == discard_frame)) { page_pool_put_page(rx_q->page_pool, buf->page, 0, true); buf->page = NULL; diff --git a/drivers/net/ethernet/ti/am65-cpts.c b/drivers/net/ethernet/ti/am65-cpts.c index efbf2ef976a3..df5659ead72f 100644 --- a/drivers/net/ethernet/ti/am65-cpts.c +++ b/drivers/net/ethernet/ti/am65-cpts.c @@ -779,6 +779,7 @@ static long am65_cpts_ts_work(struct ptp_clock_info *ptp); static struct ptp_clock_info am65_ptp_info = { .owner = THIS_MODULE, .name = "CTPS timer", + .supported_extts_flags = PTP_RISING_EDGE | PTP_STRICT_FLAGS, .adjfine = am65_cpts_ptp_adjfine, .adjtime = am65_cpts_ptp_adjtime, .gettimex64 = am65_cpts_ptp_gettimex, diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 15b5d845fc3b..1dd32e979cf5 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -118,7 +118,8 @@ enum gtp_multicast_groups { }; static const struct genl_multicast_group gtp_genl_mcgrps[] = { - [GTP_GENL_MCGRP] = { .name = GTP_GENL_MCGRP_NAME }, + [GTP_GENL_MCGRP] = { .name = GTP_GENL_MCGRP_NAME, + .flags = GENL_MCAST_CAP_NET_ADMIN, }, }; static void pdp_context_delete(struct pdp_ctx *pctx); @@ -2355,9 +2356,9 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) struct gtp_dev *gtp; struct flowi4 fl4; struct rtable *rt; + int len, ret = 0; struct sock *sk; __be16 port; - int len; if (!info->attrs[GTPA_VERSION] || !info->attrs[GTPA_LINK] || @@ -2369,14 +2370,21 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) dst_ip = nla_get_be32(info->attrs[GTPA_PEER_ADDRESS]); src_ip = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); + rcu_read_lock(); gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); - if (!gtp) - return -ENODEV; + if (!gtp) { + ret = -ENODEV; + goto unlock; + } - if (!gtp->sk_created) - return -EOPNOTSUPP; - if (!(gtp->dev->flags & IFF_UP)) - return -ENETDOWN; + if (!gtp->sk_created) { + ret = -EOPNOTSUPP; + goto unlock; + } + if (!(gtp->dev->flags & IFF_UP)) { + ret = -ENETDOWN; + goto unlock; + } if (version == GTP_V0) { struct gtp0_header *gtp0_h; @@ -2385,8 +2393,10 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) sizeof(struct iphdr) + sizeof(struct udphdr); skb_to_send = netdev_alloc_skb_ip_align(gtp->dev, len); - if (!skb_to_send) - return -ENOMEM; + if (!skb_to_send) { + ret = -ENOMEM; + goto unlock; + } sk = gtp->sk0; port = htons(GTP0_PORT); @@ -2402,8 +2412,10 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) sizeof(struct iphdr) + sizeof(struct udphdr); skb_to_send = netdev_alloc_skb_ip_align(gtp->dev, len); - if (!skb_to_send) - return -ENOMEM; + if (!skb_to_send) { + ret = -ENOMEM; + goto unlock; + } sk = gtp->sk1u; port = htons(GTP1U_PORT); @@ -2413,7 +2425,8 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) memset(gtp1u_h, 0, sizeof(struct gtp1_header_long)); gtp1u_build_echo_msg(gtp1u_h, GTP_ECHO_REQ); } else { - return -ENODEV; + ret = -ENODEV; + goto unlock; } rt = ip4_route_output_gtp(&fl4, sk, dst_ip, src_ip); @@ -2421,7 +2434,8 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) netdev_dbg(gtp->dev, "no route for echo request to %pI4\n", &dst_ip); kfree_skb(skb_to_send); - return -ENODEV; + ret = -ENODEV; + goto unlock; } local_bh_disable(); @@ -2435,7 +2449,10 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) dev_net(gtp->dev)), false, 0); local_bh_enable(); - return 0; + +unlock: + rcu_read_unlock(); + return ret; } static const struct nla_policy gtp_genl_policy[GTPA_MAX + 1] = { diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 55ecd5ebac0f..8a6cb34e6b5f 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -70,6 +70,7 @@ EXPORT_SYMBOL(blackhole_netdev); static netdev_tx_t loopback_xmit(struct sk_buff *skb, struct net_device *dev) { + enum skb_drop_reason reason; int len; skb_tx_timestamp(skb); @@ -84,7 +85,12 @@ static netdev_tx_t loopback_xmit(struct sk_buff *skb, */ skb_dst_force(skb); - skb->protocol = eth_type_trans(skb, dev); + reason = eth_type_trans_careful(skb, dev); + if (unlikely(reason)) { + dev_core_stats_tx_dropped_inc(dev); + kfree_skb_reason(skb, reason); + return NETDEV_TX_OK; + } len = skb->len; if (likely(__netif_rx(skb) == NET_RX_SUCCESS)) diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index afad90b9222a..a48c9e662fe9 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -1664,7 +1664,8 @@ static int macvlan_changelink(struct net_device *dev, if (data && data[IFLA_MACVLAN_FLAGS]) { __u16 flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]); bool promisc = (flags ^ vlan->flags) & MACVLAN_FLAG_NOPROMISC; - if (macvlan_passthru(vlan->port) && promisc) { + if (macvlan_passthru(vlan->port) && promisc && + netif_running(dev)) { int err; if (flags & MACVLAN_FLAG_NOPROMISC) diff --git a/drivers/net/mctp/mctp-usblib-test.c b/drivers/net/mctp/mctp-usblib-test.c index b7e8743c561f..3a48b10164b0 100644 --- a/drivers/net/mctp/mctp-usblib-test.c +++ b/drivers/net/mctp/mctp-usblib-test.c @@ -816,8 +816,8 @@ static void mctp_usblib_test_tx_multi_push(struct kunit *test) }; int rc; - static_assert(!sends[ARRAY_SIZE(sends) - 1].more, - "The last push must claim there will be no more"); + KUNIT_ASSERT_TRUE_MSG(test, !sends[ARRAY_SIZE(sends) - 1].more, + "The last push must claim there will be no more"); max_length = 0; for (i = 0; i < ARRAY_SIZE(sends); i++) { diff --git a/drivers/net/pcs/pcs-xpcs-plat.c b/drivers/net/pcs/pcs-xpcs-plat.c index e669a7b248ac..c4404500bac6 100644 --- a/drivers/net/pcs/pcs-xpcs-plat.c +++ b/drivers/net/pcs/pcs-xpcs-plat.c @@ -275,6 +275,15 @@ static int xpcs_plat_init_res(struct dw_xpcs_plat *pxpcs) return 0; } +#ifdef CONFIG_PM +static void xpcs_plat_exit_clk(void *data) +{ + struct dw_xpcs_plat *pxpcs = data; + struct device *dev = &pxpcs->pdev->dev; + + pm_runtime_force_suspend(dev); +} + static int xpcs_plat_init_clk(struct dw_xpcs_plat *pxpcs) { struct device *dev = &pxpcs->pdev->dev; @@ -285,15 +294,50 @@ static int xpcs_plat_init_clk(struct dw_xpcs_plat *pxpcs) return dev_err_probe(dev, PTR_ERR(pxpcs->cclk), "Failed to get CSR clock\n"); - pm_runtime_set_active(dev); - ret = devm_pm_runtime_enable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + + ret = devm_add_action_or_reset(dev, xpcs_plat_exit_clk, pxpcs); + if (ret) { + dev_err(dev, "Failed to register devm for xpcs_plat_exit_clk: %d\n", ret); + return ret; + } + + return 0; +} +#else +static void xpcs_plat_exit_clk(void *data) +{ + struct dw_xpcs_plat *pxpcs = data; + + clk_disable_unprepare(pxpcs->cclk); +} + +static int xpcs_plat_init_clk(struct dw_xpcs_plat *pxpcs) +{ + struct device *dev = &pxpcs->pdev->dev; + int ret; + + pxpcs->cclk = devm_clk_get_optional(dev, "csr"); + if (IS_ERR(pxpcs->cclk)) + return dev_err_probe(dev, PTR_ERR(pxpcs->cclk), + "Failed to get CSR clock\n"); + + ret = clk_prepare_enable(pxpcs->cclk); + if (ret) { + dev_err(dev, "Failed to enable CSR clock: %d\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(dev, xpcs_plat_exit_clk, pxpcs); if (ret) { - dev_err(dev, "Failed to enable runtime-PM\n"); + dev_err(dev, "Failed to register devm for xpcs_plat_exit_clk: %d\n", ret); return ret; } return 0; } +#endif static int xpcs_plat_init_bus(struct dw_xpcs_plat *pxpcs) { @@ -398,7 +442,7 @@ static int xpcs_plat_probe(struct platform_device *pdev) return 0; } -static int __maybe_unused xpcs_plat_pm_runtime_suspend(struct device *dev) +static int xpcs_plat_pm_runtime_suspend(struct device *dev) { struct dw_xpcs_plat *pxpcs = dev_get_drvdata(dev); @@ -407,7 +451,7 @@ static int __maybe_unused xpcs_plat_pm_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused xpcs_plat_pm_runtime_resume(struct device *dev) +static int xpcs_plat_pm_runtime_resume(struct device *dev) { struct dw_xpcs_plat *pxpcs = dev_get_drvdata(dev); @@ -415,9 +459,9 @@ static int __maybe_unused xpcs_plat_pm_runtime_resume(struct device *dev) } static const struct dev_pm_ops xpcs_plat_pm_ops = { - SET_RUNTIME_PM_OPS(xpcs_plat_pm_runtime_suspend, - xpcs_plat_pm_runtime_resume, - NULL) + RUNTIME_PM_OPS(xpcs_plat_pm_runtime_suspend, + xpcs_plat_pm_runtime_resume, + NULL) }; DW_XPCS_INFO_DECLARE(xpcs_generic, DW_XPCS_ID_NATIVE, DW_XPCS_PMA_ID_NATIVE); @@ -446,7 +490,7 @@ static struct platform_driver xpcs_plat_driver = { .probe = xpcs_plat_probe, .driver = { .name = "dwxpcs", - .pm = &xpcs_plat_pm_ops, + .pm = pm_ptr(&xpcs_plat_pm_ops), .of_match_table = xpcs_of_ids, }, }; diff --git a/drivers/net/pcs/pcs-xpcs-rk.c b/drivers/net/pcs/pcs-xpcs-rk.c index 698a43b75334..6f0a110e32d2 100644 --- a/drivers/net/pcs/pcs-xpcs-rk.c +++ b/drivers/net/pcs/pcs-xpcs-rk.c @@ -365,7 +365,7 @@ static int xpcs_rk_init_clk(struct dw_xpcs_rk *pxpcs) ret = devm_add_action_or_reset(dev, xpcs_rk_exit_clk, pxpcs); if (ret) { - dev_err(dev, "Failed to register devm for EEE clock: %d\n", ret); + dev_err(dev, "Failed to register devm for xpcs_rk_exit_clk: %d\n", ret); return ret; } diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 1a610a18893b..29847dc4bb45 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -45,6 +45,7 @@ #include <linux/slab.h> #include <linux/file.h> #include <linux/unaligned.h> +#include <net/gso.h> #include <net/netdev_lock.h> #include <net/slhc_vj.h> #include <linux/atomic.h> @@ -2278,6 +2279,73 @@ static bool ppp_decompress_proto(struct sk_buff *skb) return pskb_may_pull(skb, 2); } +#define PPP_BRIDGE_GSO_TYPES (SKB_GSO_TCPV4 | SKB_GSO_TCPV6 | \ + SKB_GSO_TCP_ECN | SKB_GSO_TCP_ACCECN | \ + SKB_GSO_TCP_FIXEDID | SKB_GSO_UDP_L4 | \ + SKB_GSO_FRAGLIST | SKB_GSO_DODGY) + +/* Segment a GSO packet (built by PPPoE GRO, or injected with a + * virtio_net_hdr) before sending it to a bridged channel that does + * not accept GSO packets. + * skb->data points to the PPP protocol field, which is replicated + * in front of each segment. + * Returns a list of segments, or NULL if the packet was dropped. + */ +static struct sk_buff *ppp_bridge_gso_segment(struct sk_buff *skb, + netdev_features_t features) +{ + struct sk_buff *segs; + + /* skb->dev is NULL here (cleared by __sk_receive_skb() for PPPoE), + * and some GSO handlers (tunnels, IPv6 UFO) dereference it. + */ + if (skb->encapsulation || + (skb_shinfo(skb)->gso_type & ~PPP_BRIDGE_GSO_TYPES)) + goto drop; + + if (!pskb_may_pull(skb, PPP_PROTO_LEN)) + goto drop; + + switch (get_unaligned((__be16 *)skb->data)) { + case htons(PPP_IP): + skb->protocol = htons(ETH_P_IP); + break; + case htons(PPP_IPV6): + skb->protocol = htons(ETH_P_IPV6); + break; + default: + goto drop; + } + skb_set_network_header(skb, PPP_PROTO_LEN); + + /* No checksum offload: skb_segment() computes the checksums. */ + segs = skb_gso_segment(skb, features); + if (IS_ERR_OR_NULL(segs)) + goto drop; + + consume_skb(skb); + return segs; + +drop: + kfree_skb(skb); + return NULL; +} + +static void ppp_bridge_xmit(struct ppp_channel *chan, struct sk_buff *skb) +{ + if (!chan->direct_xmit && skb_linearize(skb)) + goto drop; + + if (!chan->xmit_offload && skb->ip_summed == CHECKSUM_PARTIAL && + skb_csum_hwoffload_help(skb, 0)) + goto drop; + + if (chan->ops->start_xmit(chan, skb)) + return; +drop: + kfree_skb(skb); +} + /* Attempt to handle a frame via. a bridged channel, if one exists. * If the channel is bridged, the frame is consumed by the bridge. * If not, the caller must handle the frame by normal recv mechanisms. @@ -2285,6 +2353,8 @@ static bool ppp_decompress_proto(struct sk_buff *skb) */ static bool ppp_channel_bridge_input(struct channel *pch, struct sk_buff *skb) { + struct sk_buff *segs, *next; + struct ppp_channel *chan; struct channel *pchb; rcu_read_lock(); @@ -2300,8 +2370,29 @@ static bool ppp_channel_bridge_input(struct channel *pch, struct sk_buff *skb) } skb_scrub_packet(skb, !net_eq(pch->chan_net, pchb->chan_net)); - if (!pchb->chan->ops->start_xmit(pchb->chan, skb)) - kfree_skb(skb); + + /* Receive paths such as L2TP pull headers without updating + * the checksum state: drop a stale CHECKSUM_PARTIAL state + * pointing before skb->data, as skb_postpull_rcsum() does. + */ + if (skb->ip_summed == CHECKSUM_PARTIAL && + skb_checksum_start_offset(skb) < 0) + skb->ip_summed = CHECKSUM_NONE; + + chan = pchb->chan; + if (!chan->xmit_offload && skb_is_gso(skb)) { + /* Channels needing linear packets get linear segments, + * others keep references to the original pages. + */ + segs = ppp_bridge_gso_segment(skb, chan->direct_xmit ? + NETIF_F_SG : 0); + skb_list_walk_safe(segs, skb, next) { + skb_mark_not_on_list(skb); + ppp_bridge_xmit(chan, skb); + } + } else { + ppp_bridge_xmit(chan, skb); + } outl: spin_unlock_bh(&pchb->downl); @@ -3157,6 +3248,7 @@ ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound) len = CCP_LENGTH(dp); if (!pskb_may_pull(skb, len + 2)) return; /* too short */ + dp = skb->data + 2; dp += CCP_HDRLEN; len -= CCP_HDRLEN; if (len < CCP_OPT_MINLEN || len < CCP_OPT_LENGTH(dp)) diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index ee0e318d6a2a..67c28ac763ef 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -460,6 +460,9 @@ static int pppoe_disc_rcv(struct sk_buff *skb, struct net_device *dev, if (skb->pkt_type != PACKET_HOST) goto abort; + if (skb_mac_header_len(skb) < ETH_HLEN) + goto abort; + if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr))) goto abort; @@ -618,6 +621,10 @@ static int pppoe_connect(struct socket *sock, struct sockaddr_unsized *uservaddr pppox_unbind_sock(sk); pn = pppoe_pernet(sock_net(sk)); delete_item(pn, po); + + /* wait for concurrent readers before clearing the fields */ + synchronize_net(); + if (po->pppoe_dev) { dev_put(po->pppoe_dev); po->pppoe_dev = NULL; @@ -664,6 +671,7 @@ static int pppoe_connect(struct socket *sock, struct sockaddr_unsized *uservaddr po->chan.private = sk; po->chan.ops = &pppoe_chan_ops; po->chan.direct_xmit = true; + po->chan.xmit_offload = true; error = ppp_register_net_channel(dev_net(dev), &po->chan); if (error) { diff --git a/drivers/net/pse-pd/si3474.c b/drivers/net/pse-pd/si3474.c index efb0af041599..35152d462e82 100644 --- a/drivers/net/pse-pd/si3474.c +++ b/drivers/net/pse-pd/si3474.c @@ -250,9 +250,9 @@ static int si3474_get_of_channels(struct si3474_priv *priv) "Failed to read channel reg property\n"); return ret; } - if (chan_id > SI3474_MAX_CHANS) { + if (chan_id >= SI3474_MAX_CHANS) { dev_err(&priv->client[0]->dev, - "Incorrect channel number: %d\n", chan_id); + "Incorrect channel number: %u\n", chan_id); return -EINVAL; } @@ -267,6 +267,16 @@ static int si3474_get_of_channels(struct si3474_priv *priv) pi->np); return -EINVAL; } + + /* The 2P/4P mode bit pairs channels 0+1 and 2+3 of a quad */ + if (pairset_found && + (priv->pi[pi_no].chan[0] ^ 1) != priv->pi[pi_no].chan[1]) { + dev_err(&priv->client[0]->dev, + "PI %pOF: channels %u and %u are not a channel pair\n", + pi->np, priv->pi[pi_no].chan[0], + priv->pi[pi_no].chan[1]); + return -EINVAL; + } } return 0; @@ -312,8 +322,13 @@ static int si3474_pi_enable(struct pse_controller_dev *pcdev, int id) /* DETECT_CLASS_ENABLE must be set when using AUTO mode, * otherwise PI does not power up - datasheet section 2.10.2 */ - val = CHAN_BIT(chan0) | CHAN_UPPER_BIT(chan0) | - CHAN_BIT(chan1) | CHAN_UPPER_BIT(chan1); + ret = i2c_smbus_read_byte_data(client, DETECT_CLASS_ENABLE_REG); + if (ret < 0) + return ret; + + val = (u8)ret; + val |= CHAN_BIT(chan0) | CHAN_UPPER_BIT(chan0) | + CHAN_BIT(chan1) | CHAN_UPPER_BIT(chan1); ret = i2c_smbus_write_byte_data(client, DETECT_CLASS_ENABLE_REG, val); if (ret) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 439565a264ee..dcec7311e601 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -688,14 +688,18 @@ static void tun_detach_all(struct net_device *dev) synchronize_net(); for (i = 0; i < n; i++) { tfile = rtnl_dereference(tun->tfiles[i]); + mutex_lock(&tfile->napi_mutex); tun_napi_del(tfile); + mutex_unlock(&tfile->napi_mutex); /* Drop read queue */ tun_queue_purge(tfile); xdp_rxq_info_unreg(&tfile->xdp_rxq); sock_put(&tfile->sk); } list_for_each_entry_safe(tfile, tmp, &tun->disabled, next) { + mutex_lock(&tfile->napi_mutex); tun_napi_del(tfile); + mutex_unlock(&tfile->napi_mutex); tun_enable_queue(tfile); tun_queue_purge(tfile); xdp_rxq_info_unreg(&tfile->xdp_rxq); @@ -2551,6 +2555,12 @@ static int tun_xdp_one(struct tun_struct *tun, return -EINVAL; } + if (unlikely(!(READ_ONCE(tun->dev->flags) & IFF_UP))) { + put_page(virt_to_head_page(xdp->data)); + dev_core_stats_rx_dropped_inc(tun->dev); + return -EIO; + } + xdp_prog = rcu_dereference(tun->xdp_prog); if (xdp_prog) { if (gso->gso_type) { diff --git a/drivers/net/usb/aqc111.c b/drivers/net/usb/aqc111.c index dd53f413c38f..12a99df77f0d 100644 --- a/drivers/net/usb/aqc111.c +++ b/drivers/net/usb/aqc111.c @@ -471,8 +471,12 @@ static int aqc111_set_mac_addr(struct net_device *net, void *p) return ret; /* Set the MAC address */ - return aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_NODE_ID, ETH_ALEN, - ETH_ALEN, net->dev_addr); + ret = aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_NODE_ID, + ETH_ALEN, ETH_ALEN, net->dev_addr); + if (ret < 0) + return ret; + + return 0; } static int aqc111_vlan_rx_kill_vid(struct net_device *net, diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index a7010a0664c7..8f5734ac35b8 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -289,9 +289,10 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb } } - spin_lock_bh(&ctx->mtx); + /* BHs or IRQs are always off here, see cdc_ncm_tx_fixup() */ + spin_lock(&ctx->mtx); skb_out = cdc_ncm_fill_tx_frame(dev, skb, sign); - spin_unlock_bh(&ctx->mtx); + spin_unlock(&ctx->mtx); return skb_out; error: diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 35db38cb3e4a..19db5a668dcb 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -1535,14 +1535,17 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) if (ctx == NULL) goto error; - spin_lock_bh(&ctx->mtx); + /* BHs or IRQs are always off here. netpoll only disables IRQs, and + * spin_unlock_bh() would then run softirqs with the txq lock held. + */ + spin_lock(&ctx->mtx); if (ctx->is_ndp16) skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)); else skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP32_NOCRC_SIGN)); - spin_unlock_bh(&ctx->mtx); + spin_unlock(&ctx->mtx); return skb_out; error: @@ -2019,6 +2022,12 @@ static const struct usb_device_id cdc_devs[] = { { USB_DEVICE_INTERFACE_NUMBER(0x05ac, 0x1902, 2), .driver_info = (unsigned long)&apple_private_interface_info, }, + { USB_DEVICE_INTERFACE_NUMBER(0x05ac, 0x1903, 0), + .driver_info = (unsigned long)&apple_private_interface_info, + }, + { USB_DEVICE_INTERFACE_NUMBER(0x05ac, 0x1903, 2), + .driver_info = (unsigned long)&apple_private_interface_info, + }, { USB_DEVICE_INTERFACE_NUMBER(0x05ac, 0x1905, 0), .driver_info = (unsigned long)&apple_private_interface_info, }, diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 3b92ec5916ad..181e8c087655 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1767,6 +1767,12 @@ static void lan78xx_get_wol(struct net_device *netdev, u32 buf; struct lan78xx_priv *pdata = (struct lan78xx_priv *)(dev->data[0]); + if (!netdev->phydev) { + wol->supported = 0; + wol->wolopts = 0; + return; + } + if (usb_autopm_get_interface(dev->intf) < 0) return; @@ -1798,6 +1804,9 @@ static int lan78xx_set_wol(struct net_device *netdev, if (wol->wolopts & ~WAKE_ALL) return -EINVAL; + if (!netdev->phydev) + return -EOPNOTSUPP; + ret = usb_autopm_get_interface(dev->intf); if (ret < 0) return ret; diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index aba1a640fc26..68e4b6658dd2 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -461,6 +461,7 @@ static void read_bulk_callback(struct urb *urb) u8 *buf = urb->transfer_buffer; int rx_status, count = urb->actual_length; int status = urb->status; + int rx_offset = 0; __u16 pkt_len; if (!pegasus) @@ -510,7 +511,7 @@ static void read_bulk_callback(struct urb *urb) if (pegasus->chip == 0x8513) { pkt_len = le32_to_cpu(*(__le32 *)urb->transfer_buffer); pkt_len &= 0x0fff; - pegasus->rx_skb->data += 2; + rx_offset = 2; } else { pkt_len = buf[count - 3] << 8; pkt_len += buf[count - 4]; @@ -519,16 +520,23 @@ static void read_bulk_callback(struct urb *urb) } /* - * If the packet is unreasonably long, quietly drop it rather than - * kernel panicing by calling skb_put. + * Drop frames shorter than an Ethernet header, because + * eth_type_trans() reads the header whatever the length, and frames + * longer than what was actually received. Passing either up would + * expose stale buffer contents, and skb_put() could panic on the + * latter. count is at most PEGASUS_MTU. */ - if (pkt_len > PEGASUS_MTU) + if (pkt_len < ETH_HLEN || pkt_len + rx_offset > count) { + net->stats.rx_errors++; + net->stats.rx_length_errors++; goto goon; + } /* * at this point we are sure pegasus->rx_skb != NULL * so we go ahead and pass up the packet. */ + skb_reserve(pegasus->rx_skb, rx_offset); skb_put(pegasus->rx_skb, pkt_len); pegasus->rx_skb->protocol = eth_type_trans(pegasus->rx_skb, net); netif_rx(pegasus->rx_skb); diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 2077ddfa5eb0..d3bb7ee9c9bf 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -6958,11 +6958,8 @@ static void set_carrier(struct r8152 *tp) { struct net_device *netdev = tp->netdev; struct napi_struct *napi = &tp->napi; - u16 speed; - - speed = rtl8152_get_speed(tp); - if (speed & LINK_STATUS) { + if (r8152_mdio_read(tp, MII_BMSR) & BMSR_LSTATUS) { if (!netif_carrier_ok(netdev)) { tp->rtl_ops.enable(tp); netif_stop_queue(netdev); @@ -6988,6 +6985,11 @@ static void set_carrier(struct r8152 *tp) tasklet_enable(&tp->tx_tl); netif_info(tp, link, netdev, "carrier off\n"); } + + if (rtl8152_get_speed(tp) & LINK_STATUS) { + set_bit(RTL8152_LINK_CHG, &tp->flags); + schedule_delayed_work(&tp->schedule, 0); + } } } diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 84f97f448b2d..97a30dd22c54 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -329,9 +329,14 @@ void usbnet_skb_return(struct usbnet *dev, struct sk_buff *skb) unsigned long flags; int status; - if (test_bit(EVENT_RX_PAUSED, &dev->flags)) { - skb_queue_tail(&dev->rxq_pause, skb); - return; + if (unlikely(test_bit(EVENT_RX_PAUSED, &dev->flags))) { + spin_lock_irqsave(&dev->rxq_pause.lock, flags); + if (test_bit(EVENT_RX_PAUSED, &dev->flags)) { + __skb_queue_tail(&dev->rxq_pause, skb); + spin_unlock_irqrestore(&dev->rxq_pause.lock, flags); + return; + } + spin_unlock_irqrestore(&dev->rxq_pause.lock, flags); } /* only update if unset to allow minidriver rx_fixup override */ @@ -698,13 +703,20 @@ EXPORT_SYMBOL_GPL(usbnet_pause_rx); void usbnet_resume_rx(struct usbnet *dev) { + struct sk_buff_head q; struct sk_buff *skb; + unsigned long flags; int num = 0; + __skb_queue_head_init(&q); + local_bh_disable(); + spin_lock_irqsave(&dev->rxq_pause.lock, flags); clear_bit(EVENT_RX_PAUSED, &dev->flags); + skb_queue_splice_init(&dev->rxq_pause, &q); + spin_unlock_irqrestore(&dev->rxq_pause.lock, flags); - while ((skb = skb_dequeue(&dev->rxq_pause)) != NULL) { + while ((skb = __skb_dequeue(&q))) { usbnet_skb_return(dev, skb); num++; } diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 6205742f88d9..3eca92d3dc60 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -1627,10 +1627,9 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog, } } - if (!old_prog) { + if (!old_prog) peer->hw_features &= ~NETIF_F_GSO_SOFTWARE; - peer->max_mtu = max_mtu; - } + peer->max_mtu = max_mtu; xdp_features_set_redirect_target(peer, true); } diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 278d41f58d89..223539099778 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -4488,7 +4488,7 @@ static int vxlan_nl2conf(struct nlattr *tb[], struct nlattr *data[], } conf->reserved_bits = reserved_bits; - } else { + } else if (!changelink) { /* For backwards compatibility, only allow reserved fields to be * used by VXLAN extensions if explicitly requested. */ |
