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 | |
| parent | 46806af187f448b0d5481f284e7c900a57ee6a6d (diff) | |
| parent | 8f44631ba02afe9fff2f0a0fb66560ab19df87fd (diff) | |
| download | linux-4dd16e31135ea8437a720222df34e8745f385d6d.tar.gz linux-4dd16e31135ea8437a720222df34e8745f385d6d.zip | |
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
...
142 files changed, 1592 insertions, 680 deletions
diff --git a/Documentation/netlink/specs/gtp.yaml b/Documentation/netlink/specs/gtp.yaml index 7871f5def054..b65e18ae5df2 100644 --- a/Documentation/netlink/specs/gtp.yaml +++ b/Documentation/netlink/specs/gtp.yaml @@ -194,3 +194,4 @@ mcast-groups: list: - name: gtp + flags: [uns-admin-perm] diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst index 4b19c8d3efda..d3523c9064a2 100644 --- a/Documentation/networking/ip-sysctl.rst +++ b/Documentation/networking/ip-sysctl.rst @@ -1128,6 +1128,8 @@ tcp_tso_rtt_log - INTEGER If you want to use the old autosizing, set this sysctl to 0. + Possible values are [0, 31], inclusive. + Default: 9 (2^9 = 512 usec) tcp_pacing_ss_ratio - INTEGER diff --git a/Documentation/networking/tls-offload.rst b/Documentation/networking/tls-offload.rst index e5802bcd4d22..cf0e599c06a1 100644 --- a/Documentation/networking/tls-offload.rst +++ b/Documentation/networking/tls-offload.rst @@ -71,12 +71,16 @@ During driver initialization device sets the ``NETIF_F_HW_TLS_RX`` and pointer in the :c:member:`tlsdev_ops` member of the :c:type:`struct net_device <net_device>`. -When TLS cryptographic connection state is installed on a ``ktls`` socket -(note that it is done twice, once for RX and once for TX direction, -and the two are completely independent), the kernel checks if the underlying -network device is offload-capable and attempts the offload. In case offload -fails the connection is handled entirely in software using the same mechanism -as if the offload was never tried. +TLS cryptographic connection state is installed separately for RX and TX +on a ``ktls`` socket. For each direction, the kernel checks if the underlying +network device is offload-capable and attempts the offload. If offload fails, +that direction is handled in software as if offload had never been tried. + +Both hardware directions share one device binding. Once one direction is +offloaded, the other direction must use the same device. If a route change +selects a different device, or ``NETDEV_DOWN`` has cleared the binding, +hardware offload of the other direction is declined and it uses software +instead. Offload request is performed via the :c:member:`tls_dev_add` callback of :c:type:`struct tlsdev_ops <tlsdev_ops>`: diff --git a/MAINTAINERS b/MAINTAINERS index 748ce4d1c6a2..09713dc4c6a7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -27136,6 +27136,8 @@ F: drivers/net/ethernet/synopsys/ SYNOPSYS DESIGNWARE ETHERNET XPCS DRIVER L: netdev@vger.kernel.org S: Orphan +F: Documentation/devicetree/bindings/net/pcs/snps,dw-xpcs.yaml +F: drivers/net/pcs/pcs-xpcs-plat.c F: drivers/net/pcs/pcs-xpcs.c F: drivers/net/pcs/pcs-xpcs.h F: include/linux/pcs/pcs-xpcs.h 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. */ diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index df8f88f63a70..1f833ebd71ed 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -38,6 +38,34 @@ int fwnode_get_mac_address(struct fwnode_handle *fwnode, char *addr); u32 eth_get_headlen(const struct net_device *dev, const void *data, u32 len); __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev); + +/** + * eth_type_trans_careful - eth_type_trans() for skbs coming from the stack + * @skb: buffer, possibly with less than ETH_HLEN bytes in its linear area + * @dev: receiving network device + * + * eth_type_trans() expects ETH_HLEN bytes in skb->head, which NIC drivers + * provide. Devices looping skbs from their transmit path back to the + * receive path (loopback, OVS internal ports) get skbs built by the stack, + * whose link-layer header might have been popped by tc or OVS actions. + * + * Sets skb->protocol on success. + * + * Return: SKB_NOT_DROPPED_YET on success, or a drop reason. In the latter + * case, the caller must free @skb. + */ +static inline enum skb_drop_reason +eth_type_trans_careful(struct sk_buff *skb, struct net_device *dev) +{ + enum skb_drop_reason reason = pskb_may_pull_reason(skb, ETH_HLEN); + + if (unlikely(reason)) + return reason; + + skb->protocol = eth_type_trans(skb, dev); + return SKB_NOT_DROPPED_YET; +} + extern const struct header_ops eth_header_ops; int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, diff --git a/include/linux/ppp_channel.h b/include/linux/ppp_channel.h index 2f63e9a6cc88..15bc3aa4f171 100644 --- a/include/linux/ppp_channel.h +++ b/include/linux/ppp_channel.h @@ -43,6 +43,7 @@ struct ppp_channel { void *ppp; /* opaque to channel */ int speed; /* transfer rate (bytes/second) */ bool direct_xmit; /* no qdisc, xmit directly */ + bool xmit_offload; /* accepts GSO and CHECKSUM_PARTIAL skbs */ }; #ifdef __KERNEL__ diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 365925c9d262..b02d2472b003 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -171,6 +171,7 @@ extern const u8 ip_frag_ecn_table[16]; #define IPFRAG_OK 0 #define IPFRAG_DUP 1 #define IPFRAG_OVERLAP 2 +#define IPFRAG_MIXED_SECPATH 3 int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb, int offset, int end); void *inet_frag_reasm_prepare(struct inet_frag_queue *q, struct sk_buff *skb, diff --git a/include/net/ip.h b/include/net/ip.h index 618b9806c10b..0acc4ba5e5b3 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -513,31 +513,6 @@ out: return res; } -/* Configured/administrative MTU of a route, for advertising the TCP MSS. - * - * Unlike ip_dst_mtu_maybe_forward(), this deliberately ignores the - * ICMP-learned path MTU (rt->rt_pmtu). The advertised MSS bounds what the - * peer may send to us and must reflect our receive capability (the device or - * route-configured MTU), not a path MTU learned on the reverse (send) - * direction, which may not apply to the peer->us path and outlives the fnhe - * for the whole connection. See RFC 2923 section 2.3 and the comment above - * tcp_advertise_mss(). - */ -static inline unsigned int ip_dst_mtu_configured(const struct dst_entry *dst) -{ - unsigned int mtu, res; - - rcu_read_lock(); - mtu = dst_metric_raw(dst, RTAX_MTU); - if (!mtu) - mtu = READ_ONCE(dst_dev_rcu(dst)->mtu); - mtu = min_t(unsigned int, mtu, IP_MAX_MTU); - res = mtu - lwtunnel_headroom(dst->lwtstate, mtu); - rcu_read_unlock(); - - return res; -} - static inline unsigned int ip_skb_dst_mtu(struct sock *sk, const struct sk_buff *skb) { diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 5f47cef0be46..95f936be32c6 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -396,43 +396,6 @@ out: return mtu - lwtunnel_headroom(dst->lwtstate, mtu); } -/* Configured/administrative MTU of a route, for advertising the TCP MSS. - * - * Unlike ip6_dst_mtu_maybe_forward(), this ignores any ICMPv6-learned path - * MTU (which is kept on the RTF_CACHE exception route) and returns the MTU of - * the underlying route (fib6_pmtu) or the egress device. The advertised MSS - * bounds what the peer may send to us and must reflect our receive - * capability, not a path MTU learned on the reverse (send) direction. See - * RFC 2923 section 2.3 and the comment above tcp_advertise_mss(). - */ -static inline unsigned int ip6_dst_mtu_configured(const struct dst_entry *dst) -{ - const struct rt6_info *rt = dst_rt6_info(dst); - const struct fib6_info *from; - struct inet6_dev *idev; - unsigned int mtu = 0; - - rcu_read_lock(); - /* IPv6 keeps the learned PMTU and the configured MTU in the same - * RTAX_MTU slot: the learned value sits on this (possibly RTF_CACHE) - * dst, the configured one on the underlying route. Reach the latter - * via ->from (fib6_pmtu), populated by ip6_route_info_create(). - */ - from = rcu_dereference(rt->from); - if (from) - mtu = from->fib6_pmtu; - if (!mtu) { - mtu = IPV6_MIN_MTU; - idev = __in6_dev_get(dst_dev_rcu(dst)); - if (idev) - mtu = max_t(unsigned int, mtu, READ_ONCE(idev->cnf.mtu6)); - } - rcu_read_unlock(); - - mtu = min_t(unsigned int, mtu, IP6_MAX_MTU); - return mtu - lwtunnel_headroom(dst->lwtstate, mtu); -} - u32 ip6_mtu_from_fib6(const struct fib6_result *res, const struct in6_addr *daddr, const struct in6_addr *saddr); diff --git a/include/net/xfrm.h b/include/net/xfrm.h index a6d69aaa6cd2..298ca9bb0083 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1201,6 +1201,23 @@ secpath_reset(struct sk_buff *skb) #endif } +static inline bool +secpath_equal(const struct sk_buff *skb1, const struct sk_buff *skb2) +{ + const struct sec_path *sp1 = skb_sec_path(skb1); + const struct sec_path *sp2 = skb_sec_path(skb2); + int i; + + if (!sp1 || !sp2) + return sp1 == sp2; + if (sp1->len != sp2->len) + return false; + for (i = 0; i < sp1->len; i++) + if (sp1->xvec[i] != sp2->xvec[i]) + return false; + return true; +} + static inline int xfrm_addr_any(const xfrm_address_t *addr, unsigned short family) { diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index f65b8b6ca97c..79c4a54990e2 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -540,7 +540,7 @@ static int br_fill_ifinfo(struct sk_buff *skb, else err = br_fill_ifvlaninfo(skb, vg); - if (port && test_bit(BR_VLAN_TUNNEL_BIT, &port->flags)) + if (!err && port && test_bit(BR_VLAN_TUNNEL_BIT, &port->flags)) err = br_fill_vlan_tunnel_info(skb, vg); rcu_read_unlock(); if (err) diff --git a/net/core/dev.c b/net/core/dev.c index 1a09e7869118..3ac700d37e59 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2501,14 +2501,22 @@ EXPORT_SYMBOL_GPL(is_skb_forwardable); static int __dev_forward_skb2(struct net_device *dev, struct sk_buff *skb, bool check_mtu) { - int ret = ____dev_forward_skb(dev, skb, check_mtu); + enum skb_drop_reason reason; + int ret; + + ret = ____dev_forward_skb(dev, skb, check_mtu); + if (unlikely(ret)) + return ret; - if (likely(!ret)) { - skb->protocol = eth_type_trans(skb, dev); - skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); + reason = eth_type_trans_careful(skb, dev); + if (unlikely(reason)) { + dev_core_stats_rx_dropped_inc(dev); + kfree_skb_reason(skb, reason); + return NET_RX_DROP; } + skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); - return ret; + return 0; } int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb) @@ -3041,7 +3049,7 @@ int __netif_set_xps_queue(struct net_device *dev, const unsigned long *mask, goto out_no_old_maps; for (j = 0; j < dev_maps->nr_ids; j++) { - for (i = num_tc, tci = j * dev_maps->num_tc; i--; tci++) { + for (i = dev_maps->num_tc, tci = j * dev_maps->num_tc; i--; tci++) { map = xmap_dereference(dev_maps->attr_map[tci]); if (!map) continue; @@ -3478,6 +3486,9 @@ static void __netif_reschedule(struct Qdisc *q) void __netif_schedule(struct Qdisc *q) { + if (unlikely(test_bit(__QDISC_STATE_DEACTIVATED, &q->state))) + return; + /* If q->defer_list is not empty, at least one thread is * in __dev_xmit_skb() before llist_del_all(&q->defer_list). * This thread will attempt to run the queue. @@ -4283,7 +4294,7 @@ static enum skb_drop_reason qdisc_pkt_len_segs_init(struct sk_buff *skb) { struct skb_shared_info *shinfo = skb_shinfo(skb); unsigned int hdr_len, tlen; - u16 gso_segs; + u32 gso_segs; qdisc_skb_cb(skb)->pkt_len = skb->len; if (!shinfo->gso_size) { @@ -4334,6 +4345,15 @@ static enum skb_drop_reason qdisc_pkt_len_segs_init(struct sk_buff *skb) if (payload <= 0) return SKB_DROP_REASON_SKB_BAD_GSO; gso_segs = DIV_ROUND_UP(payload, shinfo->gso_size); + /* shinfo->gso_segs is a 16bit field. + * Also make sure untrusted gso_size/hdr_len can not inflate + * qdisc_pkt_len() beyond QDISC_PKT_LEN_MAX, or deficit + * schedulers could spin in their refill loops. + */ + if (unlikely(gso_segs > GSO_MAX_SEGS || + skb->len + (u64)(gso_segs - 1) * hdr_len > + QDISC_PKT_LEN_MAX)) + return SKB_DROP_REASON_SKB_BAD_GSO; shinfo->gso_segs = gso_segs; qdisc_skb_cb(skb)->pkt_segs = gso_segs; } @@ -6306,6 +6326,8 @@ skip_classify: } if (unlikely(skb_vlan_tag_present(skb)) && !netdev_uses_dsa(skb->dev)) { + unsigned int priority_tag_depth = 1; + check_vlan_id: if (skb_vlan_tag_get_id(skb)) { /* Vlan id is non 0 and vlan_do_receive() above couldn't @@ -6317,6 +6339,12 @@ check_vlan_id: * 802.1Q or 802.1AD and vlan_do_receive() above could * not find vlan dev for vlan id 0. */ + if (unlikely(priority_tag_depth >= VLAN_MAX_DEPTH)) { + drop_reason = SKB_DROP_REASON_RECURSION_LIMIT; + goto drop; + } + + priority_tag_depth++; __vlan_hwaccel_clear_tag(skb); skb = skb_vlan_untag(skb); if (unlikely(!skb)) diff --git a/net/core/gro.c b/net/core/gro.c index ff828a3dce3c..1b033855a20d 100644 --- a/net/core/gro.c +++ b/net/core/gro.c @@ -513,9 +513,10 @@ found_ptype: NAPI_GRO_CB(skb)->count = 1; if (unlikely(skb_is_gso(skb))) { NAPI_GRO_CB(skb)->count = skb_shinfo(skb)->gso_segs; - /* Only support TCP and non DODGY users. */ + /* Only support TCP and non DODGY users, with a sane gso_segs. */ if (!skb_is_gso_tcp(skb) || - (skb_shinfo(skb)->gso_type & SKB_GSO_DODGY)) + (skb_shinfo(skb)->gso_type & SKB_GSO_DODGY) || + !NAPI_GRO_CB(skb)->count) NAPI_GRO_CB(skb)->flush = 1; } diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index b9eeb9972f2b..5c0402389c6d 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1110,6 +1110,13 @@ static ssize_t store_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue, while ((mask | (mask >> 1)) != mask) mask |= (mask >> 1); + /* Require at least two entries. A one-entry table rounds up + * to mask == 0, which encodes log == 0 in the tag. + * get_rps_cpu() then computes hash_32(hash, 0), a shift by 32. + */ + if (!mask) + return -EINVAL; + /* Do not accept too large tables. */ if (mask > (INT_MAX / sizeof(*table) - 1)) return -EINVAL; diff --git a/net/core/netdev-genl.c b/net/core/netdev-genl.c index c3aa367214ac..384fdc5ae514 100644 --- a/net/core/netdev-genl.c +++ b/net/core/netdev-genl.c @@ -1506,6 +1506,12 @@ static int netdev_genl_netdevice_event(struct notifier_block *nb, netdev_unlock_full_to_ops(netdev); break; case NETDEV_UNREGISTER: + /* netdev_wait_allrefs_any() rebroadcasts NETDEV_UNREGISTER + * while waiting for the last references to be dropped; the + * netns may already be torn down by then. Notify only once. + */ + if (READ_ONCE(netdev->reg_state) == NETREG_UNREGISTERED) + break; netdev_lock(netdev); netdev_genl_dev_notify(netdev, NETDEV_CMD_DEV_DEL_NTF); netdev_unlock(netdev); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index eb9fcc4b1fd1..48f676411724 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3966,6 +3966,8 @@ skb_zerocopy(struct sk_buff *to, struct sk_buff *from, int len, int hlen) } skb_shinfo(to)->nr_frags = j; + skb_shinfo(to)->flags |= skb_shinfo(from)->flags & SKBFL_SHARED_FRAG; + if (i > 0 && from->unreadable) to->unreadable = 1; @@ -4780,10 +4782,13 @@ struct sk_buff *skb_segment_list(struct sk_buff *skb, else tail->next = nskb; - if (unlikely(err)) { - nskb->next = list_skb; + /* nskb may be a clone with a NULL ->next: keep the remaining + * members linked so that err_linearize frees them. + */ + nskb->next = list_skb; + + if (unlikely(err)) goto err_linearize; - } tail = nskb; diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index ab71f5312876..ad0aebe0ad3d 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -281,6 +281,7 @@ static int dcbnl_getpfccfg(struct net_device *netdev, struct nlmsghdr *nlh, if (!getall && !data[i]) continue; + value = 0; netdev->dcbnl_ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0, &value); ret = nla_put_u8(skb, i, value); @@ -1395,6 +1396,7 @@ static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev) goto nla_put_failure; for (i = DCB_PFC_UP_ATTR_0; i <= DCB_PFC_UP_ATTR_7; i++) { + value = 0; ops->getpfccfg(netdev, i - DCB_PFC_UP_ATTR_0, &value); if (nla_put_u8(skb, i, value)) goto nla_put_failure; diff --git a/net/devlink/region.c b/net/devlink/region.c index 537779bbff07..c22016f23a4a 100644 --- a/net/devlink/region.c +++ b/net/devlink/region.c @@ -719,6 +719,11 @@ int devlink_nl_region_new_doit(struct sk_buff *skb, struct genl_info *info) if (!snapshot_id_attr) { struct sk_buff *msg; + /* Drop the allocation reference, the snapshot + * now holds its own. + */ + __devlink_snapshot_id_decrement(devlink, snapshot_id); + snapshot = devlink_region_snapshot_get_by_id(region, snapshot_id); if (WARN_ON(!snapshot)) { diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 8151c638a24c..93b306d91b6d 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -446,7 +446,8 @@ void inet_csk_update_fastreuse(const struct sock *sk, { bool reuse = sk->sk_reuse && sk->sk_state != TCP_LISTEN; - if (hlist_empty(&tb->bhash2)) { + if (hlist_is_singular_node(&tb2->bhash_node, &tb->bhash2) && + hlist_empty(&tb2->owners)) { tb->fastreuse = reuse; if (sk->sk_reuseport) { tb->fastreuseport = FASTREUSEPORT_ANY; diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index b286ee429da8..c18da91c1f03 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -23,6 +23,7 @@ #include <net/inet_ecn.h> #include <net/ip.h> #include <net/ipv6.h> +#include <net/xfrm.h> /* Use skb->cb to track consecutive/adjacent fragments coming at * the end of the queue. Nodes in the rb-tree queue will @@ -441,6 +442,10 @@ int inet_frag_queue_insert(struct inet_frag_queue *q, struct sk_buff *skb, { struct sk_buff *last = q->fragments_tail; + /* The reassembled skb keeps the first fragment's secpath. */ + if (last && !secpath_equal(last, skb)) + return IPFRAG_MIXED_SECPATH; + /* An IP fragment is never a GSO packet, but an untrusted source * (virtio_net_hdr) may have attached GSO metadata to it. Do not let * that reach the reassembled skb, whose head keeps the first diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index c790d2f49487..36105dd19282 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -378,8 +378,9 @@ insert_error: err = -EINVAL; goto err; } + if (err == IPFRAG_OVERLAP) + __IP_INC_STATS(net, IPSTATS_MIB_REASM_OVERLAPS); err = -EINVAL; - __IP_INC_STATS(net, IPSTATS_MIB_REASM_OVERLAPS); discard_qp: inet_frag_kill(&qp->q, refs); __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index e4c51ca47301..e8f7d3b573d0 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -539,6 +539,12 @@ static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) }; int err; + if (skb->protocol != htons(ETH_P_IP) || !pskb_inet_may_pull(skb)) { + DEV_STATS_INC(dev, tx_errors); + kfree_skb(skb); + return NETDEV_TX_OK; + } + rcu_read_lock(); err = ipmr_fib_lookup(net, &fl4, &mrt); if (err < 0) { @@ -550,6 +556,11 @@ static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) DEV_STATS_ADD(dev, tx_bytes, skb->len); DEV_STATS_INC(dev, tx_packets); + /* skb->cb[] holds qdisc/tc state at this point, not IPCB. + * ipmr_cache_report() reads it as IPCB to build IP_PKTINFO. + */ + memset(IPCB(skb), 0, sizeof(*IPCB(skb))); + /* Pairs with WRITE_ONCE() in vif_add() and vif_delete() */ ipmr_cache_report(mrt, skb, READ_ONCE(mrt->mroute_reg_vif_num), IGMPMSG_WHOLEPKT); @@ -1063,6 +1074,7 @@ static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt, static int ipmr_cache_report(const struct mr_table *mrt, struct sk_buff *pkt, vifi_t vifi, int assert) { + unsigned char pkt_cb[sizeof(pkt->cb)]; const int ihl = ip_hdrlen(pkt); struct sock *mroute_sk; struct igmphdr *igmp; @@ -1118,8 +1130,6 @@ static int ipmr_cache_report(const struct mr_table *mrt, msg = (struct igmpmsg *)skb_network_header(skb); msg->im_vif = vifi; msg->im_vif_hi = vifi >> 8; - ipv4_pktinfo_prepare(mroute_sk, pkt, false); - memcpy(skb->cb, pkt->cb, sizeof(skb->cb)); /* Add our header. * Note that code, csum and group fields are cleared. */ @@ -1130,6 +1140,15 @@ static int ipmr_cache_report(const struct mr_table *mrt, skb->transport_header = skb->network_header; } + memset(skb->cb, 0, sizeof(skb->cb)); + /* Route queries from ipmr_get_route() carry NETLINK_CB, not IPCB. */ + if (ip_hdr(pkt)->version) { + memcpy(pkt_cb, pkt->cb, sizeof(pkt_cb)); + ipv4_pktinfo_prepare(mroute_sk, pkt, false); + *PKTINFO_SKB_CB(skb) = *PKTINFO_SKB_CB(pkt); + memcpy(pkt->cb, pkt_cb, sizeof(pkt_cb)); + } + igmpmsg_netlink_event(mrt, skb); /* Deliver to mrouted */ @@ -2070,7 +2089,8 @@ static void ip_mr_forward(struct net *net, struct mr_table *mrt, } /* Wrong interface: drop packet and (maybe) send PIM assert. */ - if (rcu_access_pointer(mrt->vif_table[vif].dev) != dev) { + if (true_vifi < 0 || + rcu_access_pointer(mrt->vif_table[vif].dev) != dev) { if (rt_is_output_route(skb_rtable(skb))) { /* It is our own packet, looped back. * Very complicated situation... @@ -2498,6 +2518,7 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb, skb_push(skb2, sizeof(struct iphdr)); skb_reset_network_header(skb2); iph = ip_hdr(skb2); + memset(iph, 0, sizeof(*iph)); iph->ihl = sizeof(struct iphdr) >> 2; iph->saddr = saddr; iph->daddr = daddr; diff --git a/net/ipv4/metrics.c b/net/ipv4/metrics.c index ad40762a8b38..4052a90249c6 100644 --- a/net/ipv4/metrics.c +++ b/net/ipv4/metrics.c @@ -60,6 +60,11 @@ static int ip_metrics_convert(struct nlattr *fc_mx, if (ecn_ca) metrics[RTAX_FEATURES - 1] |= DST_FEATURE_ECN_CA; + if ((metrics[RTAX_LOCK - 1] & (1 << RTAX_CWND)) && !metrics[RTAX_CWND - 1]) { + NL_SET_ERR_MSG(extack, "Locked cwnd metric must be non zero"); + return -EINVAL; + } + return 0; } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 595a3b7f1783..93b9f4189525 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1365,8 +1365,8 @@ static unsigned int ipv4_default_advmss(const struct dst_entry *dst) rcu_read_lock(); net = dst_dev_net_rcu(dst); - advmss = max_t(unsigned int, ip_dst_mtu_configured(dst) - header_size, - net->ipv4.ip_rt_min_advmss); + advmss = max_t(unsigned int, ipv4_mtu(dst) - header_size, + net->ipv4.ip_rt_min_advmss); rcu_read_unlock(); return min(advmss, IPV4_MAX_PMTU - header_size); diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index e3760daa3470..bf193e7ec516 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -26,6 +26,7 @@ static int ip_local_port_range_max[] = { 65535, 65535 }; static int tcp_adv_win_scale_min = -31; static int tcp_adv_win_scale_max = 31; static int tcp_app_win_max = 31; +static int tcp_tso_rtt_log_max = 31; static int tcp_min_snd_mss_min = TCP_MIN_SND_MSS; static int tcp_min_snd_mss_max = 65535; static int tcp_rto_max_max = TCP_RTO_MAX_SEC * MSEC_PER_SEC; @@ -1406,6 +1407,7 @@ static const struct ctl_table ipv4_net_table[] = { .maxlen = sizeof(u8), .mode = 0644, .proc_handler = proc_dou8vec_minmax, + .extra2 = &tcp_tso_rtt_log_max, }, { .procname = "tcp_min_rtt_wlen", diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 59e52161c577..b9de7c302ba8 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1172,14 +1172,18 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size) zc = MSG_SPLICE_PAGES; } - if (!sockc_err && sockc.dmabuf_id && (zc != MSG_ZEROCOPY || !binding)) { - err = -EINVAL; + if (sockc.dmabuf_id && (zc != MSG_ZEROCOPY || !binding)) { + err = sockc_err ?: -EINVAL; goto out_err; } if (unlikely(flags & MSG_FASTOPEN || inet_test_bit(DEFER_CONNECT, sk)) && !tp->repair) { + if (binding) { + err = -EINVAL; + goto out_err; + } err = tcp_sendmsg_fastopen(sk, msg, &copied_syn, size, uarg); if (err == -EINPROGRESS && copied_syn > 0) goto out; @@ -1203,6 +1207,10 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size) } if (unlikely(tp->repair)) { + if (binding) { + err = -EINVAL; + goto out_err; + } if (tp->repair_queue == TCP_RECV_QUEUE) { copied = tcp_send_rcvq(sk, msg, size); goto out_nopush; @@ -5114,6 +5122,8 @@ void tcp_done(struct sock *sk) int tcp_abort(struct sock *sk, int err) { int state = inet_sk_state_load(sk); + bool listener = false; + int ret = 0; if (state == TCP_NEW_SYN_RECV) { struct request_sock *req = inet_reqsk(sk); @@ -5148,21 +5158,31 @@ int tcp_abort(struct sock *sk, int err) if (sk->sk_state == TCP_LISTEN) { tcp_set_state(sk, TCP_CLOSE); inet_csk_listen_stop(sk); + listener = true; } /* Don't race with BH socket closes such as inet_csk_listen_stop. */ local_bh_disable(); bh_lock_sock(sk); + /* An unaccepted child might have been closed by + * inet_csk_listen_stop() while we were not holding its BH lock. + */ + if (!listener && sk->sk_state == TCP_CLOSE) { + ret = -ENOENT; + goto out; + } + if (tcp_need_reset(sk->sk_state)) tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE); tcp_done_with_error(sk, err); +out: bh_unlock_sock(sk); local_bh_enable(); if (!has_current_bpf_ctx()) release_sock(sk); - return 0; + return ret; } EXPORT_SYMBOL_GPL(tcp_abort); diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 8e83ef81fc18..e3f462f161dd 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -288,12 +288,12 @@ int tcp_set_default_congestion_control(struct net *net, const char *name) ca = tcp_ca_find_autoload(name); if (!ca) { ret = -ENOENT; - } else if (!bpf_try_module_get(ca, ca->owner)) { - ret = -EBUSY; } else if (!net_eq(net, &init_net) && !(ca->flags & TCP_CONG_NON_RESTRICTED)) { /* Only init netns can set default to a restricted algorithm */ ret = -EPERM; + } else if (!bpf_try_module_get(ca, ca->owner)) { + ret = -EBUSY; } else { prev = xchg(&net->ipv4.tcp_congestion_control, ca); if (prev) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index ebe5aa70374a..a7f9242f1e42 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1723,6 +1723,12 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb, if (READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_reflect_tos)) newinet->tos = tcp_rsk(req)->syn_tos & ~INET_ECN_MASK; +#if IS_ENABLED(CONFIG_IPV6) + /* Detach IPv6 state before a route lookup can fail. */ + if (opt_child_init) + opt_child_init(newsk, sk); +#endif + if (!dst) { dst = inet_csk_route_child_sock(sk, newsk, req); if (!dst) @@ -1732,10 +1738,6 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb, } sk_setup_caps(newsk, dst); -#if IS_ENABLED(CONFIG_IPV6) - if (opt_child_init) - opt_child_init(newsk, sk); -#endif tcp_ca_openreq_child(newsk, dst); tcp_sync_mss(newsk, dst4_mtu(dst)); diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c index e74d99ca9fac..b84fcd07c723 100644 --- a/net/ipv4/tcp_offload.c +++ b/net/ipv4/tcp_offload.c @@ -315,14 +315,20 @@ struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb, mss = skb_shinfo(p)->gso_size; - /* If skb is a GRO packet, make sure its gso_size matches prior packet mss. + /* If skb is a GRO packet, make sure its gso_size matches prior packet + * mss, and that its payload is consistent with its gso_segs + * (the last segment can be short). * If it is a single frame, do not aggregate it if its length * is bigger than our mss. */ - if (unlikely(skb_is_gso(skb))) + if (unlikely(skb_is_gso(skb))) { + unsigned int segs = NAPI_GRO_CB(skb)->count; + flush |= (mss != skb_shinfo(skb)->gso_size); - else + flush |= len <= (segs - 1) * mss || len > segs * mss; + } else { flush |= (len - 1) >= mss; + } flush |= (ntohl(th2->seq) + skb_gro_len(p)) ^ ntohl(th->seq); flush |= skb_cmp_decrypted(p, skb); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index df3aaba8574f..cf3ddc055d48 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2776,7 +2776,7 @@ static int tcp_mtu_probe(struct sock *sk) mss_now = tcp_current_mss(sk); probe_size = tcp_mtu_to_mss(sk, (icsk->icsk_mtup.search_high + icsk->icsk_mtup.search_low) >> 1); - size_needed = probe_size + (tp->reordering + 1) * (u64)tp->mss_cache; + size_needed = probe_size + ((u64)tp->reordering + 1) * tp->mss_cache; interval = icsk->icsk_mtup.search_high - icsk->icsk_mtup.search_low; /* When misfortune happens, we are reprobing actively, * and then reprobe timer has expired. We stick with current diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 0dc6ebee6e56..f7c69c9df411 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -1228,9 +1228,7 @@ next_iter: fib6_nsiblings++; } BUG_ON(fib6_nsiblings != rt->fib6_nsiblings); - rcu_read_lock(); rt6_multipath_rebalance(temp_sibling); - rcu_read_unlock(); } /* @@ -1274,9 +1272,7 @@ add: sibling->fib6_nsiblings - 1); WRITE_ONCE(rt->fib6_nsiblings, 0); list_del_rcu(&rt->fib6_siblings); - rcu_read_lock(); rt6_multipath_rebalance(next_sibling); - rcu_read_unlock(); return err; } } diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 3a551be30f15..af968a2c2a78 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -642,7 +642,7 @@ static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, .flowi6_mark = skb->mark, }; - if (!pskb_inet_may_pull(skb)) + if (skb->protocol != htons(ETH_P_IPV6) || !pskb_inet_may_pull(skb)) goto tx_err; rcu_read_lock(); @@ -2225,7 +2225,8 @@ static void ip6_mr_forward(struct net *net, struct mr_table *mrt, /* * Wrong interface: drop packet and (maybe) send PIM assert. */ - if (rcu_access_pointer(mrt->vif_table[vif].dev) != dev) { + if (true_vifi < 0 || + rcu_access_pointer(mrt->vif_table[vif].dev) != dev) { atomic_long_inc(&c->_c.mfc_un.res.wrong_if); if (true_vifi >= 0 && READ_ONCE(mrt->mroute_do_assert) && diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index a7964125c764..8418ed9423f4 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -241,8 +241,9 @@ insert_error: err = -EINVAL; goto err; } + if (err == IPFRAG_OVERLAP) + __IP6_INC_STATS(net, idev, IPSTATS_MIB_REASM_OVERLAPS); err = -EINVAL; - __IP6_INC_STATS(net, idev, IPSTATS_MIB_REASM_OVERLAPS); discard_fq: inet_frag_kill(&fq->q, refs); __IP6_INC_STATS(net, idev, IPSTATS_MIB_REASMFAILS); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 94e5ef1c4f83..616b86f89f63 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3239,7 +3239,7 @@ void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk) static unsigned int ip6_default_advmss(const struct dst_entry *dst) { - unsigned int mtu = ip6_dst_mtu_configured(dst); + unsigned int mtu = dst6_mtu(dst); struct net *net; mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); @@ -4875,8 +4875,10 @@ void rt6_multipath_rebalance(struct fib6_info *rt) if (WARN_ON_ONCE(!first)) return; + rcu_read_lock(); total = rt6_multipath_total_weight(first); rt6_multipath_upper_bound_set(first, total); + rcu_read_unlock(); } static int fib6_ifup(struct fib6_info *rt, void *p_arg) @@ -5416,6 +5418,7 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, struct fib6_info *rt_notif = NULL, *rt_last = NULL; struct nl_info *info = &cfg->fc_nlinfo; struct rt6_nh *nh, *nh_safe; + struct fib6_table *table; struct fib6_config r_cfg; struct rtnexthop *rtnh; LIST_HEAD(rt6_nh_list); @@ -5502,10 +5505,12 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, */ info->skip_notify_kernel = 1; + table = rt->fib6_table; + spin_lock_bh(&table->tb6_lock); + err_nh = NULL; list_for_each_entry(nh, &rt6_nh_list, list) { - err = __ip6_ins_rt(nh->fib6_info, info, extack); - + err = fib6_add(&table->tb6_root, nh->fib6_info, info, extack); if (err) { if (replace && nhn) NL_SET_ERR_MSG_MOD(extack, @@ -5560,11 +5565,13 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, /* success ... tell user about new route */ ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags); + + spin_unlock_bh(&table->tb6_lock); goto cleanup; add_errout: /* send notification for routes that were added so that - * the delete notifications sent by ip6_route_del are + * the delete notifications sent by fib6_del() are * coherent */ if (rt_notif) @@ -5574,9 +5581,11 @@ add_errout: list_for_each_entry(nh, &rt6_nh_list, list) { if (err_nh == nh) break; - ip6_route_del(&nh->r_cfg, extack); + fib6_del(nh->fib6_info, &nh->r_cfg.fc_nlinfo, + RT_DEL_REASON_UNSPEC); } + spin_unlock_bh(&table->tb6_lock); cleanup: list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, list) { fib6_info_release(nh->fib6_info); diff --git a/net/mctp/route.c b/net/mctp/route.c index d18ed9d0db1d..96fd75ea791b 100644 --- a/net/mctp/route.c +++ b/net/mctp/route.c @@ -552,8 +552,16 @@ static int mctp_dst_input(struct mctp_dst *dst, struct sk_buff *skb) * can do is drop. */ rc = mctp_key_add(key, msk); - if (!rc) + if (!rc) { trace_mctp_key_acquire(key); + } else { + /* key_add failed: the key is not observable by + * any cleanup path, so free the fragment we + * just queued on it to avoid leaking the skb. + */ + kfree_skb(key->reasm_head); + key->reasm_head = NULL; + } /* we don't need to release key->lock on exit, so * clean up here and suppress the unlock via diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index a904dc3bcad6..1281c42a3456 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2157,8 +2157,12 @@ void __netlink_clear_multicast_users(struct sock *ksk, unsigned int group) struct netlink_table *tbl = &nl_table[ksk->sk_protocol]; struct hlist_node *tmp; - sk_for_each_bound_safe(sk, tmp, &tbl->mc_list) + sk_for_each_bound_safe(sk, tmp, &tbl->mc_list) { + /* bitmap predates the group, can't be a member */ + if (group - 1 >= nlk_sk(sk)->ngroups) + continue; netlink_update_socket_mc(nlk_sk(sk), group, 0); + } } struct nlmsghdr * @@ -2318,9 +2322,13 @@ static int netlink_dump(struct sock *sk, bool lock_taken) if (!skb) goto errout_skb; + /* Check the limit before charging the skb: netlink_recvmsg() continues + * the dump at or below half of sk_rcvbuf, and the skb must be queued + * then. Keep sk_rmem_alloc at or below INT_MAX. + */ rcvbuf = READ_ONCE(sk->sk_rcvbuf); rmem = atomic_add_return(skb->truesize, &sk->sk_rmem_alloc); - if (rmem != skb->truesize && rmem >= rcvbuf) { + if (rmem - skb->truesize >= rcvbuf || rmem > INT_MAX) { atomic_sub(skb->truesize, &sk->sk_rmem_alloc); goto errout_skb; } diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c index 77a16d5ebd54..8f035087f0ee 100644 --- a/net/openvswitch/vport-internal_dev.c +++ b/net/openvswitch/vport-internal_dev.c @@ -188,6 +188,7 @@ static void internal_dev_destroy(struct vport *vport) static int internal_dev_recv(struct sk_buff *skb) { struct net_device *netdev = skb->dev; + enum skb_drop_reason reason; if (unlikely(!(netdev->flags & IFF_UP))) { kfree_skb(skb); @@ -199,7 +200,12 @@ static int internal_dev_recv(struct sk_buff *skb) nf_reset_ct(skb); skb->pkt_type = PACKET_HOST; - skb->protocol = eth_type_trans(skb, netdev); + reason = eth_type_trans_careful(skb, netdev); + if (unlikely(reason)) { + dev_core_stats_rx_dropped_inc(netdev); + kfree_skb_reason(skb, reason); + return NETDEV_TX_OK; + } skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); dev_sw_netstats_rx_add(netdev, skb->len); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 823e0911c910..cf6429f09477 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2655,6 +2655,12 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, } offset = offset_in_page(data); + if (unlikely(to_write > MAX_SKB_FRAGS * PAGE_SIZE - offset)) { + net_warn_ratelimited("Packet exceed the number of skb frags(%u)\n", + (unsigned int)MAX_SKB_FRAGS); + return -EFAULT; + } + len_max = PAGE_SIZE - offset; len = ((to_write > len_max) ? len_max : to_write); @@ -2666,12 +2672,6 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, while (likely(to_write)) { nr_frags = skb_shinfo(skb)->nr_frags; - if (unlikely(nr_frags >= MAX_SKB_FRAGS)) { - pr_err("Packet exceed the number of skb frags(%u)\n", - (unsigned int)MAX_SKB_FRAGS); - return -EFAULT; - } - page = pgv_to_page(data); data += len; flush_dcache_page(page); diff --git a/net/rds/af_rds.c b/net/rds/af_rds.c index d5defe9172e3..fb6b8ddb2d11 100644 --- a/net/rds/af_rds.c +++ b/net/rds/af_rds.c @@ -136,8 +136,7 @@ static int rds_getname(struct socket *sock, struct sockaddr *uaddr, sin6->sin6_port = rs->rs_conn_port; sin6->sin6_addr = rs->rs_conn_addr; sin6->sin6_flowinfo = 0; - /* scope_id is the same as in the bound address. */ - sin6->sin6_scope_id = rs->rs_bound_scope_id; + sin6->sin6_scope_id = rs->rs_conn_scope_id; uaddr_len = sizeof(*sin6); } } else { @@ -572,6 +571,7 @@ static int rds_connect(struct socket *sock, struct sockaddr_unsized *uaddr, } ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &rs->rs_conn_addr); rs->rs_conn_port = sin->sin_port; + rs->rs_conn_scope_id = 0; break; #if IS_ENABLED(CONFIG_IPV6) @@ -616,11 +616,14 @@ static int rds_connect(struct socket *sock, struct sockaddr_unsized *uaddr, ret = -EINVAL; break; } - /* Remember the connected address scope ID. It will - * be checked against the binding local address when - * the socket is bound. + /* Remember the connected address scope ID. It is + * checked against the binding local address when + * the socket is bound, and gives a send without a + * destination its scope. */ - rs->rs_bound_scope_id = sin6->sin6_scope_id; + rs->rs_conn_scope_id = sin6->sin6_scope_id; + } else { + rs->rs_conn_scope_id = 0; } rs->rs_conn_addr = sin6->sin6_addr; rs->rs_conn_port = sin6->sin6_port; @@ -922,8 +925,11 @@ static void rds6_sock_info(struct socket *sock, unsigned int len, } #endif +static bool rds_initialized; + static void rds_exit(void) { + rds_initialized = false; sock_unregister(rds_family_ops.family); proto_unregister(&rds_proto); rds_conn_exit(); @@ -944,6 +950,16 @@ module_exit(rds_exit); u32 rds_gen_num; +/* When RDS is built in, a failed rds_init() does not stop the rds_rdma + * and rds_tcp initcalls from running against a core that has already + * been torn down (rds_wq in particular). Let them bail out early. + */ +bool rds_is_initialized(void) +{ + return rds_initialized; +} +EXPORT_SYMBOL_GPL(rds_is_initialized); + static int __init rds_init(void) { int ret; @@ -980,6 +996,7 @@ static int __init rds_init(void) rds_info_register_func(RDS6_INFO_SOCKETS, rds6_sock_info); rds_info_register_func(RDS6_INFO_RECV_MESSAGES, rds6_sock_inc_info); #endif + rds_initialized = true; goto out; diff --git a/net/rds/bind.c b/net/rds/bind.c index f800d920d969..3ac59cd512a2 100644 --- a/net/rds/bind.c +++ b/net/rds/bind.c @@ -233,8 +233,8 @@ int rds_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len) * non-link local address (scope_id is 0). */ if (!ipv6_addr_any(&rs->rs_conn_addr) && scope_id && - rs->rs_bound_scope_id && - scope_id != rs->rs_bound_scope_id) { + rs->rs_conn_scope_id && + scope_id != rs->rs_conn_scope_id) { ret = -EINVAL; goto out; } diff --git a/net/rds/ib_sysctl.c b/net/rds/ib_sysctl.c index 2af678e71e3c..bc97a3eecad6 100644 --- a/net/rds/ib_sysctl.c +++ b/net/rds/ib_sysctl.c @@ -114,7 +114,7 @@ void rds_ib_sysctl_exit(void) int rds_ib_sysctl_init(void) { rds_ib_sysctl_hdr = register_net_sysctl(&init_net, "net/rds/ib", rds_ib_sysctl_table); - if (!rds_ib_sysctl_hdr) + if (!rds_ib_sysctl_hdr && IS_ENABLED(CONFIG_SYSCTL)) return -ENOMEM; return 0; } diff --git a/net/rds/rdma_transport.c b/net/rds/rdma_transport.c index 12559a8ca912..75532734c054 100644 --- a/net/rds/rdma_transport.c +++ b/net/rds/rdma_transport.c @@ -336,6 +336,9 @@ static int __init rds_rdma_init(void) { int ret; + if (!rds_is_initialized()) + return -ENODEV; + ret = rds_ib_init(); if (ret) goto out; diff --git a/net/rds/rds.h b/net/rds/rds.h index 2db49573dacd..e3937c1ff3a1 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -646,6 +646,8 @@ struct rds_sock { struct in6_addr rs_conn_addr; #define rs_conn_addr_v4 rs_conn_addr.s6_addr32[3] __be16 rs_conn_port; + /* scope of rs_conn_addr when it is link-local, 0 otherwise */ + __u32 rs_conn_scope_id; struct rds_transport *rs_transport; /* @@ -1040,6 +1042,7 @@ extern unsigned int rds_sysctl_trace_level; int rds_threads_init(void); void rds_threads_exit(void); extern struct workqueue_struct *rds_wq; +bool rds_is_initialized(void); void rds_queue_reconnect(struct rds_conn_path *cp); void rds_connect_worker(struct work_struct *); void rds_shutdown_worker(struct work_struct *); diff --git a/net/rds/recv.c b/net/rds/recv.c index 93ee4546ae36..00ab60f2d996 100644 --- a/net/rds/recv.c +++ b/net/rds/recv.c @@ -791,7 +791,16 @@ int rds_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, sin6->sin6_port = inc->i_hdr.h_sport; sin6->sin6_addr = inc->i_saddr; sin6->sin6_flowinfo = 0; - sin6->sin6_scope_id = rs->rs_bound_scope_id; + /* A link-local sender is reported with the + * interface recorded on its connection (the + * accepting socket's device for TCP, the one + * resolved for the local address on IB); any + * other sender has no scope. + */ + sin6->sin6_scope_id = + (ipv6_addr_type(&inc->i_saddr) & + IPV6_ADDR_LINKLOCAL) ? + inc->i_conn->c_dev_if : 0; msg->msg_namelen = sizeof(*sin6); } } diff --git a/net/rds/send.c b/net/rds/send.c index 1afa981e5c06..f38967f65af1 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -1256,7 +1256,11 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) lock_sock(sk); daddr = rs->rs_conn_addr; dport = rs->rs_conn_port; - scope_id = rs->rs_bound_scope_id; + /* The connected peer's scope for a link-local peer, else + * the bound scope, as an explicit send to that peer would + * compute below. + */ + scope_id = rs->rs_conn_scope_id ?: rs->rs_bound_scope_id; release_sock(sk); } @@ -1342,7 +1346,8 @@ int rds_sendmsg(struct socket *sock, struct msghdr *msg, size_t payload_len) /* rds_conn_create has a spinlock that runs with IRQ off. * Caching the conn in the socket helps a lot. */ if (rs->rs_conn && ipv6_addr_equal(&rs->rs_conn->c_faddr, &daddr) && - rs->rs_tos == rs->rs_conn->c_tos) { + rs->rs_tos == rs->rs_conn->c_tos && + rs->rs_conn->c_dev_if == scope_id) { conn = rs->rs_conn; } else { conn = rds_conn_create_outgoing(sock_net(sock->sk), diff --git a/net/rds/sysctl.c b/net/rds/sysctl.c index 025f518a4349..968d6279bbd3 100644 --- a/net/rds/sysctl.c +++ b/net/rds/sysctl.c @@ -103,7 +103,7 @@ int rds_sysctl_init(void) rds_sysctl_reg_table = register_net_sysctl(&init_net, "net/rds", rds_sysctl_rds_table); - if (!rds_sysctl_reg_table) + if (!rds_sysctl_reg_table && IS_ENABLED(CONFIG_SYSCTL)) return -ENOMEM; return 0; } diff --git a/net/rds/tcp.c b/net/rds/tcp.c index 774a71f88d37..c1ad37505417 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -637,7 +637,7 @@ static __net_init int rds_tcp_init_net(struct net *net) tbl[RDS_TCP_RCVBUF].data = &rtn->rcvbuf_size; rtn->rds_tcp_sysctl = register_net_sysctl_sz(net, "net/rds/tcp", tbl, ARRAY_SIZE(rds_tcp_sysctl_table)); - if (!rtn->rds_tcp_sysctl) { + if (!rtn->rds_tcp_sysctl && IS_ENABLED(CONFIG_SYSCTL)) { pr_warn("could not register sysctl\n"); err = -ENOMEM; goto fail; @@ -815,6 +815,9 @@ static int __init rds_tcp_init(void) { int ret; + if (!rds_is_initialized()) + return -ENODEV; + rds_tcp_conn_slab = KMEM_CACHE(rds_tcp_connection, 0); if (!rds_tcp_conn_slab) { ret = -ENOMEM; diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 841e4584f0fe..e142d33dc5ef 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -1560,6 +1560,9 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla, err = -EINVAL; goto err; } + /* Other filters share an existing action */ + if (init_res[i - 1] != ACT_P_CREATED) + continue; if (skip_sw) act->tcfa_flags |= TCA_ACT_FLAGS_SKIP_SW; if (skip_hw) diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c index 2afd68983ece..3946a9589bb8 100644 --- a/net/sched/act_ife.c +++ b/net/sched/act_ife.c @@ -769,6 +769,10 @@ static int tcf_ife_decode(struct sk_buff *skb, const struct tc_action *a, skb->protocol = eth_type_trans(skb, skb->dev); skb_reset_network_header(skb); + if (!skb_at_tc_ingress(skb)) { + skb_push(skb, ETH_HLEN); + skb_reset_mac_len(skb); + } return action; } diff --git a/net/sched/em_ipt.c b/net/sched/em_ipt.c index 3650117da47f..26f1c4269a48 100644 --- a/net/sched/em_ipt.c +++ b/net/sched/em_ipt.c @@ -141,6 +141,9 @@ static int em_ipt_change(struct net *net, void *data, int data_len, !tb[TCA_EM_IPT_MATCH_DATA] || !tb[TCA_EM_IPT_NFPROTO]) return -EINVAL; + if (nla_get_u32(tb[TCA_EM_IPT_HOOK]) >= NF_INET_NUMHOOKS) + return -EINVAL; + nfproto = nla_get_u8(tb[TCA_EM_IPT_NFPROTO]); switch (nfproto) { case NFPROTO_IPV4: diff --git a/net/sched/sch_etf.c b/net/sched/sch_etf.c index ada87a81da6a..aaafcf640bc7 100644 --- a/net/sched/sch_etf.c +++ b/net/sched/sch_etf.c @@ -147,6 +147,7 @@ static void report_sock_error(struct sk_buff *skb, u32 err, u8 code) return; serr = SKB_EXT_ERR(clone); + memset(serr, 0, sizeof(*serr)); serr->ee.ee_errno = err; serr->ee.ee_origin = SO_EE_ORIGIN_TXTIME; serr->ee.ee_type = 0; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 6f6a6f0d5eb0..3e8ee4a691c3 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -168,8 +168,11 @@ static inline void qdisc_enqueue_skb_bad_txq(struct Qdisc *q, static inline void dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q) { + struct sk_buff_head requeue; spinlock_t *lock = NULL; + __skb_queue_head_init(&requeue); + if (q->flags & TCQ_F_NOLOCK) { lock = qdisc_lock(q); spin_lock(lock); @@ -178,7 +181,7 @@ static inline void dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q) while (skb) { struct sk_buff *next = skb->next; - __skb_queue_tail(&q->gso_skb, skb); + __skb_queue_tail(&requeue, skb); /* it's still part of the queue */ if (qdisc_is_percpu_stats(q)) { @@ -194,6 +197,11 @@ static inline void dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q) skb = next; } + /* the skbs may come from the head of gso_skb, so put them back there: + * appending them would let the ones still parked overtake them + */ + skb_queue_splice(&requeue, &q->gso_skb); + if (lock) { spin_unlock(lock); set_bit(__QDISC_STATE_MISSED, &q->state); @@ -1280,6 +1288,10 @@ static void transition_one_qdisc(struct net_device *dev, if (need_watchdog_p) { WRITE_ONCE(dev_queue->trans_start, jiffies); *need_watchdog_p = 1; + + /* Resume backlog left by a suppressed schedule while deactivated. */ + if (!qdisc_is_empty(new_qdisc)) + netif_schedule_queue(dev_queue); } } diff --git a/net/sched/sch_gred.c b/net/sched/sch_gred.c index f04f425c6c44..f3d9a7252949 100644 --- a/net/sched/sch_gred.c +++ b/net/sched/sch_gred.c @@ -442,13 +442,6 @@ static int gred_change_table_def(struct Qdisc *sch, struct nlattr *dps, red_flags_changed = table->red_flags != sopt->flags; table->red_flags = sopt->flags; - /* - * Every entry point to GRED is synchronized with the above code - * and the DP is checked against DPs, i.e. shadowed VQs can no - * longer be found so we can unlock right here. - */ - sch_tree_unlock(sch); - if (sopt->grio) { gred_enable_rio_mode(table); gred_disable_wred_mode(table); @@ -474,6 +467,15 @@ static int gred_change_table_def(struct Qdisc *sch, struct nlattr *dps, } } + /* + * Keep the lock until every field the datapath reads (the + * RIO/WRED mode bits, the per-VQ red_flags and the tab[] slots) + * has been updated: gred_enqueue()/gred_dequeue() read them under + * the same lock, so publishing them after the unlock races with + * the enqueue path. + */ + sch_tree_unlock(sch); + gred_offload(sch, TC_GRED_REPLACE); return 0; } diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index 8e7fb6d0fc00..86740f4e82f5 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -2586,7 +2586,11 @@ static int __init taprio_module_init(void) if (err) return err; - return register_qdisc(&taprio_qdisc_ops); + err = register_qdisc(&taprio_qdisc_ops); + if (err) + unregister_netdevice_notifier(&taprio_device_notifier); + + return err; } static void __exit taprio_module_exit(void) diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index e9cb7d408ca0..70c948a26f80 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -55,7 +55,7 @@ struct teql_master { struct Qdisc_ops qops; struct net_device *dev; struct Qdisc __rcu *slaves; - spinlock_t slaves_lock; /* serializes writes to ->slaves */ + raw_spinlock_t slaves_lock; /* serializes writes to ->slaves */ struct list_head master_list; unsigned long tx_bytes; unsigned long tx_packets; @@ -94,6 +94,7 @@ teql_dequeue(struct Qdisc *sch) { struct teql_sched_data *dat = qdisc_priv(sch); struct netdev_queue *dat_queue; + unsigned long flags; struct sk_buff *skb; struct Qdisc *q; @@ -104,9 +105,9 @@ teql_dequeue(struct Qdisc *sch) if (skb == NULL) { struct net_device *m = qdisc_dev(q); if (m) { - spin_lock_bh(&dat->m->slaves_lock); + raw_spin_lock_irqsave(&dat->m->slaves_lock, flags); rcu_assign_pointer(dat->m->slaves, sch); - spin_unlock_bh(&dat->m->slaves_lock); + raw_spin_unlock_irqrestore(&dat->m->slaves_lock, flags); netif_wake_queue(m); } } else { @@ -139,11 +140,13 @@ teql_destroy(struct Qdisc *sch) struct teql_master *master = dat->m; struct netdev_queue *txq = NULL; bool reset_master_queue = false; + unsigned long flags; + bool purge = false; if (!master) return; - spin_lock_bh(&master->slaves_lock); + raw_spin_lock_irqsave(&master->slaves_lock, flags); prev = rcu_dereference_protected(master->slaves, lockdep_is_held(&master->slaves_lock)); if (prev) { @@ -171,12 +174,18 @@ teql_destroy(struct Qdisc *sch) reset_master_queue = true; } } - skb_queue_purge(&dat->q); + purge = true; break; } while (prev != rcu_dereference_protected(master->slaves, lockdep_is_held(&master->slaves_lock))); } - spin_unlock_bh(&master->slaves_lock); + raw_spin_unlock_irqrestore(&master->slaves_lock, flags); + + /* skb_queue_purge() takes the queue's spinlock_t, which cannot nest + * inside a raw lock on PREEMPT_RT; purge after the raw section. + */ + if (purge) + skb_queue_purge(&dat->q); if (reset_master_queue) dev_reset_queue(master->dev, txq, NULL); @@ -189,6 +198,7 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, struct teql_master *m = (struct teql_master *)sch->ops; struct teql_sched_data *q = qdisc_priv(sch); struct Qdisc *first; + unsigned long flags; if (dev->hard_header_len > m->dev->hard_header_len) return -EINVAL; @@ -205,7 +215,7 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, skb_queue_head_init(&q->q); - spin_lock_bh(&m->slaves_lock); + raw_spin_lock_irqsave(&m->slaves_lock, flags); first = rcu_dereference_protected(m->slaves, lockdep_is_held(&m->slaves_lock)); if (first) { if (m->dev->flags & IFF_UP) { @@ -216,7 +226,7 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, (m->dev->flags & IFF_MULTICAST && !(dev->flags & IFF_MULTICAST)) || dev->mtu < m->dev->mtu) { - spin_unlock_bh(&m->slaves_lock); + raw_spin_unlock_irqrestore(&m->slaves_lock, flags); return -EINVAL; } } else { @@ -239,7 +249,7 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, m->dev->mtu = dev->mtu; m->dev->flags = (m->dev->flags&~FMASK)|(dev->flags&FMASK); } - spin_unlock_bh(&m->slaves_lock); + raw_spin_unlock_irqrestore(&m->slaves_lock, flags); return 0; } @@ -315,11 +325,12 @@ static inline int teql_resolve(struct sk_buff *skb, static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) { struct teql_master *master = netdev_priv(dev); - struct Qdisc *start, *q; - int busy; - int nores; int subq = skb_get_queue_mapping(skb); struct sk_buff *skb_res = NULL; + struct Qdisc *start, *q; + unsigned long flags; + int nores; + int busy; restart: nores = 0; @@ -349,38 +360,43 @@ restart: case 0: if (__netif_tx_trylock(slave_txq)) { unsigned int length = qdisc_pkt_len(skb); + int rc = NETDEV_TX_BUSY; skb->dev = slave; - if (!netif_xmit_frozen_or_stopped(slave_txq) && - netdev_start_xmit(skb, slave, slave_txq, false) == - NETDEV_TX_OK) { - __netif_tx_unlock(slave_txq); - spin_lock(&master->slaves_lock); + if (!netif_xmit_frozen_or_stopped(slave_txq)) + rc = netdev_start_xmit(skb, slave, slave_txq, false); + __netif_tx_unlock(slave_txq); + if (dev_xmit_complete(rc)) { + if (rc != NETDEV_TX_OK && rc != NET_XMIT_CN) { + master->tx_dropped++; + rcu_read_unlock(); + return NETDEV_TX_OK; + } + raw_spin_lock_irqsave(&master->slaves_lock, flags); if (rcu_dereference_protected(master->slaves, lockdep_is_held(&master->slaves_lock)) == q) rcu_assign_pointer(master->slaves, rcu_dereference_protected(NEXT_SLAVE(q), lockdep_is_held(&master->slaves_lock))); - spin_unlock(&master->slaves_lock); + raw_spin_unlock_irqrestore(&master->slaves_lock, flags); netif_wake_queue(dev); master->tx_packets++; master->tx_bytes += length; rcu_read_unlock(); return NETDEV_TX_OK; } - __netif_tx_unlock(slave_txq); } if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0))) busy = 1; break; case 1: - spin_lock(&master->slaves_lock); + raw_spin_lock_irqsave(&master->slaves_lock, flags); if (rcu_dereference_protected(master->slaves, lockdep_is_held(&master->slaves_lock)) == q) rcu_assign_pointer(master->slaves, rcu_dereference_protected(NEXT_SLAVE(q), lockdep_is_held(&master->slaves_lock))); - spin_unlock(&master->slaves_lock); + raw_spin_unlock_irqrestore(&master->slaves_lock, flags); rcu_read_unlock(); return NETDEV_TX_OK; default: @@ -502,7 +518,7 @@ static __init void teql_master_setup(struct net_device *dev) struct teql_master *master = netdev_priv(dev); struct Qdisc_ops *ops = &master->qops; - spin_lock_init(&master->slaves_lock); + raw_spin_lock_init(&master->slaves_lock); master->dev = dev; ops->priv_size = sizeof(struct teql_sched_data); diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index ef26878f1282..97390569c4bd 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -725,7 +725,8 @@ out: * SCTP. * * Output: - * Return 0 - If the address is a non-unicast or an illegal address. + * Return 0 - If the address is a non-unicast or an illegal address, + * or a non-v4-mapped address on a non-PF_INET6 socket. * Return 1 - If the address is a unicast. */ static int sctp_v6_addr_valid(union sctp_addr *addr, @@ -749,6 +750,9 @@ static int sctp_v6_addr_valid(union sctp_addr *addr, if (!(ret & IPV6_ADDR_UNICAST)) return 0; + if (sp && sctp_opt2sk(sp)->sk_family != PF_INET6) + return 0; + return 1; } diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index ddc31a5dbad3..d64c3ae33298 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1733,7 +1733,9 @@ struct sctp_association *sctp_unpack_cookie( struct sctp_cookie *bear_cookie; struct sctp_chunkhdr *ch; unsigned int len, chlen; + union sctp_addr paddr; enum sctp_scope scope; + struct sctp_af *af; ktime_t kt; /* Header size is static data prior to the actual cookie, including @@ -1842,6 +1844,11 @@ struct sctp_association *sctp_unpack_cookie( goto fail; } + paddr = bear_cookie->peer_addr; + af = sctp_get_af_specific(paddr.sa.sa_family); + if (!af || !af->addr_valid(&paddr, sctp_sk(ep->base.sk), NULL)) + goto malformed; + /* Make a new base association. */ scope = sctp_scope(sctp_source(chunk)); retval = sctp_association_new(ep, ep->base.sk, scope, gfp); @@ -1858,6 +1865,7 @@ struct sctp_association *sctp_unpack_cookie( /* Populate the association from the cookie. */ memcpy(&retval->c, bear_cookie, sizeof(*bear_cookie)); + retval->c.peer_addr = paddr; if (sctp_assoc_set_bind_addr_from_cookie(retval, bear_cookie, GFP_ATOMIC) < 0) { @@ -2382,8 +2390,8 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk, (param.p->type == SCTP_PARAM_IPV4_ADDRESS || param.p->type == SCTP_PARAM_IPV6_ADDRESS)) { af = sctp_get_af_specific(param_type2af(param.p->type)); - if (!af->from_addr_param(&addr, param.addr, - chunk->sctp_hdr->source, 0)) + if (!af || !af->from_addr_param(&addr, param.addr, + chunk->sctp_hdr->source, 0)) continue; if (sctp_cmp_addr_exact(sctp_source(chunk), &addr)) src_match = 1; @@ -2555,17 +2563,11 @@ static int sctp_process_param(struct sctp_association *asoc, */ switch (param.p->type) { case SCTP_PARAM_IPV6_ADDRESS: - if (PF_INET6 != asoc->base.sk->sk_family) - break; - goto do_addr_param; - case SCTP_PARAM_IPV4_ADDRESS: - /* v4 addresses are not allowed on v6-only socket */ - if (ipv6_only_sock(asoc->base.sk)) - break; -do_addr_param: af = sctp_get_af_specific(param_type2af(param.p->type)); - if (!af->from_addr_param(&addr, param.addr, htons(asoc->peer.port), 0)) + if (!af || !af->from_addr_param(&addr, param.addr, + htons(asoc->peer.port), 0) || + !af->addr_valid(&addr, sctp_sk(asoc->base.sk), NULL)) break; scope = sctp_scope(peer_addr); if (sctp_in_scope(net, &addr, scope)) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 8dc65498763c..b2f609f177fb 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -814,7 +814,7 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, * effects--it is safe to run them here. */ if (!sctp_process_init(new_asoc, chunk, - &chunk->subh.cookie_hdr->c.peer_addr, + &new_asoc->c.peer_addr, peer_init, GFP_ATOMIC)) goto nomem_init; diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 65fa18928fe3..774f68d1a2b3 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -661,6 +661,10 @@ static int tipc_l2_rcv_msg(struct sk_buff *skb, struct net_device *dev, { struct tipc_bearer *b; + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + rcu_read_lock(); b = rcu_dereference(dev->tipc_ptr) ?: rcu_dereference(orig_dev->tipc_ptr); diff --git a/net/tls/tls_device.c b/net/tls/tls_device.c index 1a5f328d80a5..4cdfbb57ecff 100644 --- a/net/tls/tls_device.c +++ b/net/tls/tls_device.c @@ -1031,6 +1031,20 @@ int tls_device_decrypted(struct sock *sk, struct tls_context *tls_ctx) return tls_device_reencrypt(sk, tls_ctx); } +/* Both directions share ctx->netdev. A second direction must use that + * device; after NETDEV_DOWN clears it, no new direction can be offloaded. + */ +static bool tls_device_can_attach(struct tls_context *ctx, struct sock *sk, + struct net_device *netdev) +{ + struct net_device *ctx_netdev; + + ctx_netdev = rcu_dereference_protected(ctx->netdev, + lockdep_is_held(&device_offload_lock)); + return sk->sk_destruct != tls_device_sk_destruct || + ctx_netdev == netdev; +} + static void tls_device_attach(struct tls_context *ctx, struct sock *sk, struct net_device *netdev) { @@ -1166,6 +1180,11 @@ int tls_set_device_offload(struct sock *sk) goto release_lock; } + if (!tls_device_can_attach(ctx, sk, netdev)) { + rc = -EOPNOTSUPP; + goto release_lock; + } + ctx->priv_ctx_tx = offload_ctx; rc = netdev->tlsdev_ops->tls_dev_add(netdev, sk, TLS_OFFLOAD_CTX_DIR_TX, &ctx->crypto_send.info, @@ -1236,6 +1255,11 @@ int tls_set_device_offload_rx(struct sock *sk, struct tls_context *ctx) goto release_lock; } + if (!tls_device_can_attach(ctx, sk, netdev)) { + rc = -EOPNOTSUPP; + goto release_lock; + } + context = kzalloc_obj(*context); if (!context) { rc = -ENOMEM; diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index 8bdb78718a8d..b8d5a843f761 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -699,8 +699,8 @@ static int do_tls_setsockopt_conf(struct sock *sk, sockptr_t optval, goto err_crypto_info; } - rc = copy_from_sockptr_offset(crypto_info + 1, optval, - sizeof(*crypto_info), + rc = copy_from_sockptr_offset((u8 *)crypto_ctx + sizeof(*crypto_info), + optval, sizeof(*crypto_info), optlen - sizeof(*crypto_info)); if (rc) { rc = -EFAULT; @@ -831,15 +831,26 @@ static int do_tls_setsockopt_no_pad(struct sock *sk, sockptr_t optval, return rc; } +static bool tls_tx_record_is_open(struct tls_context *ctx) +{ + switch (ctx->tx_conf) { + case TLS_SW: + return !!tls_sw_ctx_tx(ctx)->open_rec; + case TLS_HW: + return !!tls_offload_ctx_tx(ctx)->open_record; + default: + return false; + } +} + static int do_tls_setsockopt_tx_payload_len(struct sock *sk, sockptr_t optval, unsigned int optlen) { struct tls_context *ctx = tls_get_ctx(sk); - struct tls_sw_context_tx *sw_ctx = tls_sw_ctx_tx(ctx); u16 value; bool tls_13 = ctx->prot_info.version == TLS_1_3_VERSION; - if (sw_ctx && sw_ctx->open_rec) + if (tls_tx_record_is_open(ctx)) return -EBUSY; if (sockptr_is_null(optval) || optlen != sizeof(value)) @@ -860,6 +871,7 @@ static int do_tls_setsockopt_tx_payload_len(struct sock *sk, sockptr_t optval, static int do_tls_setsockopt(struct sock *sk, int optname, sockptr_t optval, unsigned int optlen) { + struct tls_context *ctx; int rc = 0; switch (optname) { @@ -879,9 +891,15 @@ static int do_tls_setsockopt(struct sock *sk, int optname, sockptr_t optval, rc = do_tls_setsockopt_no_pad(sk, optval, optlen); break; case TLS_TX_MAX_PAYLOAD_LEN: + ctx = tls_get_ctx(sk); + /* Senders sleep with tx_lock held, so take it interruptible */ + rc = mutex_lock_interruptible(&ctx->tx_lock); + if (rc) + break; lock_sock(sk); rc = do_tls_setsockopt_tx_payload_len(sk, optval, optlen); release_sock(sk); + mutex_unlock(&ctx->tx_lock); break; default: rc = -ENOPROTOOPT; diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 312e51270f29..771e02bff519 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -2007,10 +2007,14 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, struct tls_msg *tlm; struct sk_buff *skb; ssize_t copied = 0; + bool nonblock; int chunk; int err; - err = tls_rx_reader_lock(sk, ctx, flags & SPLICE_F_NONBLOCK); + nonblock = flags & SPLICE_F_NONBLOCK || + sock->file->f_flags & O_NONBLOCK; + + err = tls_rx_reader_lock(sk, ctx, nonblock); if (err < 0) return err; @@ -2024,8 +2028,7 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, } else { struct tls_decrypt_arg darg; - err = tls_rx_rec_wait(sk, flags & SPLICE_F_NONBLOCK, - true, false); + err = tls_rx_rec_wait(sk, nonblock, true, false); if (err <= 0) goto splice_read_end; diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c index 0ebd4d46f7ab..7750d2ec4ee2 100644 --- a/net/xdp/xsk_buff_pool.c +++ b/net/xdp/xsk_buff_pool.c @@ -166,6 +166,7 @@ int xp_assign_dev(struct xsk_buff_pool *pool, { u32 needed = netdev->mtu + ETH_PAD_LEN; u32 segs = netdev->xdp_zc_max_segs; + u8 umem_flags = pool->umem->flags; bool mbuf = flags & XDP_USE_SG; bool force_zc, force_copy; struct netdev_bpf bpf; @@ -268,6 +269,7 @@ err_unreg_pool: if (!force_zc) err = 0; /* fallback to copy mode */ if (err) { + pool->umem->flags = umem_flags; xsk_clear_pool_at_qid(netdev, queue_id); hlist_del(&pool->dev_node); pool->netdev = NULL; diff --git a/tools/testing/selftests/bpf/prog_tests/empty_skb.c b/tools/testing/selftests/bpf/prog_tests/empty_skb.c index c9fcb70cbafb..a3938c16557b 100644 --- a/tools/testing/selftests/bpf/prog_tests/empty_skb.c +++ b/tools/testing/selftests/bpf/prog_tests/empty_skb.c @@ -24,7 +24,7 @@ void test_empty_skb(void) int *ifindex; int err; int ret; - int lwt_egress_ret; /* expected retval at lwt/egress */ + int lwt_ret; /* expected retval at lwt */ __be16 h_proto; bool success_on_tc; bool adjust_room; @@ -82,7 +82,7 @@ void test_empty_skb(void) .data_size_in = sizeof(eth_hlen), .ifindex = &veth_ifindex, .ret = -ERANGE, - .lwt_egress_ret = -ERANGE, + .lwt_ret = -ERANGE, .success_on_tc = true, }, { @@ -96,17 +96,23 @@ void test_empty_skb(void) .data_size_in = sizeof(eth_hlen), .ifindex = &ipip_ifindex, .ret = -ERANGE, - .lwt_egress_ret = -ERANGE, + .lwt_ret = -ERANGE, }, - /* ETH_HLEN+1-sized packet should be redirected. */ + /* ETH_HLEN+1-sized packet should be redirected at TC. + * At LWT, the Ethernet header is not part of the skb, and + * a 1-byte packet is too short for an Ethernet device. + */ { .msg = "veth ETH_HLEN+1 packet ingress", .data_in = eth_hlen_pp, .data_size_in = sizeof(eth_hlen_pp), .ifindex = &veth_ifindex, - .lwt_egress_ret = 1, /* veth_xmit NET_XMIT_DROP */ + /* lwt: skb->len=1, dropped by veth_xmit() at egress + * and by __dev_forward_skb2() at ingress. + */ + .lwt_ret = 1, /* NET_XMIT_DROP / NET_RX_DROP */ }, { .msg = "ipip ETH_HLEN+1 packet ingress", @@ -144,7 +150,6 @@ void test_empty_skb(void) eth_hlen.h_proto = tests[i].h_proto; bpf_object__for_each_program(prog, bpf_obj->obj) { - bool at_egress = strstr(bpf_program__name(prog), "egress") != NULL; bool at_tc = !strncmp(bpf_program__section_name(prog), "tc", 2); bool is_adjust_room = !strcmp(bpf_program__name(prog), "tc_adjust_room"); int expected_ret; @@ -153,7 +158,7 @@ void test_empty_skb(void) if (tests[i].adjust_room != is_adjust_room) continue; - expected_ret = at_egress && !at_tc ? tests[i].lwt_egress_ret : tests[i].ret; + expected_ret = at_tc ? tests[i].ret : tests[i].lwt_ret; tattr.data_in = tests[i].data_in; tattr.data_size_in = tests[i].data_size_in; diff --git a/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh b/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh index c4cd2078a8db..c07c2af5df94 100755 --- a/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh +++ b/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh @@ -383,7 +383,8 @@ vlmc_startup_query_test() echo -n $goutput | jq -e ".[].vlans[] | select(.vlan == 10 and \ - .mcast_startup_query_interval == 3125) " &>/dev/null + .mcast_startup_query_interval >= 3124 and \ + .mcast_startup_query_interval <= 3126) " &>/dev/null check_err $? "Wrong default mcast_startup_query_interval global vlan option value" log_test "Vlan mcast_startup_query_interval global option default value" diff --git a/tools/testing/selftests/net/forwarding/vxlan_reserved.sh b/tools/testing/selftests/net/forwarding/vxlan_reserved.sh index 709845123727..0ce264cc57f8 100755 --- a/tools/testing/selftests/net/forwarding/vxlan_reserved.sh +++ b/tools/testing/selftests/net/forwarding/vxlan_reserved.sh @@ -40,6 +40,7 @@ reserved_31_test reserved_56_test reserved_63_test + gbp_changelink_test "} NUM_NETIFS=4 @@ -338,6 +339,20 @@ reserved_63_test() reserved_test 63 } +gbp_changelink_test_do() +{ + vxlan_device_add gbp + __default_test_do 25 "gbp" + ip link set dev vx1 type vxlan ageing 300 + __default_test_do 25 "gbp after changelink" +} + +gbp_changelink_test() +{ + in_defer_scope \ + gbp_changelink_test_do +} + trap cleanup EXIT setup_prepare diff --git a/tools/testing/selftests/net/lib/gro.c b/tools/testing/selftests/net/lib/gro.c index fb6a72275636..5aaf7a897b0b 100644 --- a/tools/testing/selftests/net/lib/gro.c +++ b/tools/testing/selftests/net/lib/gro.c @@ -1339,7 +1339,6 @@ static void check_capacity_pkts(int fd) int coalesced[num_flows]; struct tcphdr *tcph; int ip_ext_len = 0; - int total_data = 0; int pkt_size = -1; int data_len = 0; int flow_id; @@ -1402,7 +1401,6 @@ static void check_capacity_pkts(int fd) } num_pkt++; - total_data += data_len; } /* Check flow ordering. We expect to see all non-coalesced first segs diff --git a/tools/testing/selftests/net/netlink-dumps.c b/tools/testing/selftests/net/netlink-dumps.c index 51129c564d0a..4a94a25167e2 100644 --- a/tools/testing/selftests/net/netlink-dumps.c +++ b/tools/testing/selftests/net/netlink-dumps.c @@ -2,9 +2,12 @@ #define _GNU_SOURCE +#include <errno.h> #include <fcntl.h> +#include <sched.h> #include <stdio.h> #include <string.h> +#include <sys/ioctl.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/syscall.h> @@ -260,4 +263,103 @@ TEST(close_with_ref) usleep(100 * 1000); } +static int lo_set_up(int sock, bool up) +{ + struct ifreq ifr = {}; + + strcpy(ifr.ifr_name, "lo"); + if (ioctl(sock, SIOCGIFFLAGS, &ifr)) + return -1; + if (up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + return ioctl(sock, SIOCSIFFLAGS, &ifr); +} + +static const struct { + struct nlmsghdr nlhdr; + struct ifinfomsg ifm; +} dump_links = { + .nlhdr = { + .nlmsg_len = sizeof(dump_links), + .nlmsg_type = RTM_GETLINK, + .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP, + .nlmsg_seq = 1, + }, + .ifm = { + .ifi_family = AF_UNSPEC, + }, +}; + +// A dump started while a notification is still queued must make progress +// for a reader which peeks before reading, like libnl does +TEST(dump_with_queued_ntf) +{ + struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + .nl_groups = RTMGRP_LINK, + }; + int netlink_sock, inet_sock, i, ret, links = 0; + int rcvbuf = 2048; + bool done = false; + char buf[32768]; + ssize_t n; + + ret = unshare(CLONE_NEWNET); + if (ret && errno == EPERM) + SKIP(return, "Needs CAP_SYS_ADMIN for a netns"); + ASSERT_EQ(ret, 0); + + inet_sock = socket(AF_INET, SOCK_DGRAM, 0); + ASSERT_GE(inet_sock, 0); + netlink_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + ASSERT_GE(netlink_sock, 0); + ASSERT_EQ(setsockopt(netlink_sock, SOL_SOCKET, SO_RCVBUF, + &rcvbuf, sizeof(rcvbuf)), 0); + ASSERT_EQ(bind(netlink_sock, (struct sockaddr *)&addr, + sizeof(addr)), 0); + + // A large read makes the kernel attempt large dump skbs + ASSERT_EQ(lo_set_up(inet_sock, true), 0); + n = recv(netlink_sock, buf, sizeof(buf), 0); + ASSERT_GE(n, (ssize_t)sizeof(struct nlmsghdr)); + + // Leave one notification queued, then start the dump + ASSERT_EQ(lo_set_up(inet_sock, false), 0); + n = send(netlink_sock, &dump_links, sizeof(dump_links), 0); + ASSERT_EQ(n, sizeof(dump_links)); + + for (i = 0; i < 100 && !done; i++) { + struct nlmsghdr *nlh; + + n = recv(netlink_sock, buf, sizeof(buf), + MSG_PEEK | MSG_TRUNC | MSG_DONTWAIT); + if (n < 0) + continue; + n = recv(netlink_sock, buf, sizeof(buf), MSG_DONTWAIT); + if (n < 0) + continue; + + for (nlh = (struct nlmsghdr *)buf; NLMSG_OK(nlh, n); + nlh = NLMSG_NEXT(nlh, n)) { + if (nlh->nlmsg_seq != dump_links.nlhdr.nlmsg_seq) + continue; + if (nlh->nlmsg_type == NLMSG_ERROR) + EXPECT_EQ(*(int *)NLMSG_DATA(nlh), 0); + if (nlh->nlmsg_type == RTM_NEWLINK) + links++; + if (nlh->nlmsg_type == NLMSG_DONE) { + EXPECT_EQ(*(int *)NLMSG_DATA(nlh), 0); + done = true; + } + } + } + EXPECT_TRUE(done); + EXPECT_GE(links, 1); + + close(netlink_sock); + close(inet_sock); +} + TEST_HARNESS_MAIN diff --git a/tools/testing/selftests/net/packetdrill/tcp_advmss_pmtu_ipv4.pkt b/tools/testing/selftests/net/packetdrill/tcp_advmss_pmtu_ipv4.pkt deleted file mode 100644 index f2ef931b77a1..000000000000 --- a/tools/testing/selftests/net/packetdrill/tcp_advmss_pmtu_ipv4.pkt +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Test that IPv4 advertised MSS in SYN-ACK is derived from the configured -// interface MTU (1500 -> MSS 1460), not the ICMP-learned Path MTU. - ---ip_version=ipv4 - -`./defaults.sh -ethtool -K tun0 tso off -` - -// -// Connection 1: Learn PMTU exception (MTU 1200 -> MSS 1160) -// - 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 - +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 - +0 bind(3, ..., ...) = 0 - +0 listen(3, 1) = 0 - - +0 < S 0:0(0) win 65535 <mss 1460,sackOK,nop,nop,nop,wscale 8> - +0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 8> - +.1 < . 1:1(0) ack 1 win 257 - +0 accept(3, ..., ...) = 4 - -// Send a full 1460-byte segment - +0 write(4, ..., 1460) = 1460 - +0 > P. 1:1461(1460) ack 1 - -// ICMP Fragmentation Needed arrives indicating next-hop MTU 1200 - +0 < icmp unreachable frag_needed mtu 1200 [1:1461(1460)] - -// Local host retransmits using the learned MTU 1200 (MSS = 1200 - 40 = 1160) - +0 > . 1:1161(1160) ack 1 - +0 > P. 1161:1461(300) ack 1 - +0 < R 1:1(0) ack 1461 win 0 - -// Close connection 1 and listener - +0 close(4) = 0 - +0 close(3) = 0 - -// -// Connection 2: New connection from the same peer -// - +0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 - +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 - +0 bind(3, ..., ...) = 0 - +0 listen(3, 1) = 0 - - +0 < S 0:0(0) win 65535 <mss 1460,sackOK,nop,nop,nop,wscale 8> - -// Verify: SYN-ACK MUST advertise configured MSS 1460, NOT the learned PMTU MSS 1160 - +0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 8> - +0 < . 1:1(0) ack 1 win 257 - +0 accept(3, ..., ...) = 4 - -// Verify: Outgoing transmit MSS is still constrained by the learned PMTU 1200 - +0 write(4, ..., 1460) = 1460 - +0 > . 1:1161(1160) ack 1 - +0 > P. 1161:1461(300) ack 1 - +0 < . 1:1(0) ack 1461 win 257 - -// Clean up - +0 close(4) = 0 - +0 > F. 1461:1461(0) ack 1 - +0 < F. 1:1(0) ack 1462 win 257 - +0 > . 1462:1462(0) ack 2 - +0 close(3) = 0 diff --git a/tools/testing/selftests/net/packetdrill/tcp_advmss_pmtu_ipv6.pkt b/tools/testing/selftests/net/packetdrill/tcp_advmss_pmtu_ipv6.pkt deleted file mode 100644 index c7638b11a815..000000000000 --- a/tools/testing/selftests/net/packetdrill/tcp_advmss_pmtu_ipv6.pkt +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Test that IPv6 advertised MSS in SYN-ACK is derived from the configured -// interface MTU (1520 -> MSS 1460), not the ICMPv6-learned Path MTU. - ---ip_version=ipv6 - -`./defaults.sh -ethtool -K tun0 tso off -` - -// -// Connection 1: Learn PMTU exception (MTU 1280 -> MSS 1220) -// - 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 - +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 - +0 bind(3, ..., ...) = 0 - +0 listen(3, 1) = 0 - - +0 < S 0:0(0) win 65535 <mss 1460,sackOK,nop,nop,nop,wscale 8> - +0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 8> - +.1 < . 1:1(0) ack 1 win 257 - +0 accept(3, ..., ...) = 4 - -// Send a full 1460-byte segment - +0 write(4, ..., 1460) = 1460 - +0 > P. 1:1461(1460) ack 1 - -// ICMPv6 Packet Too Big arrives indicating next-hop MTU 1280 - +0 < icmp packet_too_big mtu 1280 [1:1461(1460)] - -// Local host retransmits using the learned MTU 1280 (MSS = 1280 - 40 - 20 = 1220) - +0 > . 1:1221(1220) ack 1 - +0 > P. 1221:1461(240) ack 1 - +0 < R 1:1(0) ack 1461 win 0 - -// Close connection 1 and listener - +0 close(4) = 0 - +0 close(3) = 0 - -// -// Connection 2: New connection from the same peer -// - +0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 - +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 - +0 bind(3, ..., ...) = 0 - +0 listen(3, 1) = 0 - - +0 < S 0:0(0) win 65535 <mss 1460,sackOK,nop,nop,nop,wscale 8> - -// Verify: SYN-ACK MUST advertise configured MSS 1460, NOT the learned PMTU MSS 1220 - +0 > S. 0:0(0) ack 1 <mss 1460,nop,nop,sackOK,nop,wscale 8> - +0 < . 1:1(0) ack 1 win 257 - +0 accept(3, ..., ...) = 4 - -// Verify: Outgoing transmit MSS is still constrained by the learned PMTU 1280 - +0 write(4, ..., 1460) = 1460 - +0 > . 1:1221(1220) ack 1 - +0 > P. 1221:1461(240) ack 1 - +0 < . 1:1(0) ack 1461 win 257 - -// Clean up - +0 close(4) = 0 - +0 > F. 1461:1461(0) ack 1 - +0 < F. 1:1(0) ack 1462 win 257 - +0 > . 1462:1462(0) ack 2 - +0 close(3) = 0 diff --git a/tools/testing/selftests/net/tcp_mmap.c b/tools/testing/selftests/net/tcp_mmap.c index 487ae659a1f1..86241bed7f08 100644 --- a/tools/testing/selftests/net/tcp_mmap.c +++ b/tools/testing/selftests/net/tcp_mmap.c @@ -85,7 +85,6 @@ static int rcvbuf; /* Default: autotuning. Can be set with -r <integer> option static int sndbuf; /* Default: autotuning. Can be set with -w <integer> option */ static int zflg; /* zero copy option. (MSG_ZEROCOPY for sender, mmap() for receiver */ static int xflg; /* hash received data (simple xor) (-h option) */ -static int keepflag; /* -k option: receiver shall keep all received file in memory (no munmap() calls) */ static int integrity; /* -i option: sender and receiver compute sha256 over the data.*/ static size_t chunk_size = 512*1024; @@ -456,7 +455,7 @@ int main(int argc, char *argv[]) int sflg = 0; int mss = 0; - while ((c = getopt(argc, argv, "46p:svr:w:H:zxkP:M:C:a:i")) != -1) { + while ((c = getopt(argc, argv, "46p:svr:w:H:zxP:M:C:a:i")) != -1) { switch (c) { case '4': cfg_family = PF_INET; @@ -490,9 +489,6 @@ int main(int argc, char *argv[]) case 'x': xflg = 1; break; - case 'k': - keepflag = 1; - break; case 'P': max_pacing_rate = atoi(optarg) ; break; diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index 9d3cd4fff062..c4203401c4bf 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -889,6 +889,37 @@ TEST_F(tls, splice_to_pipe) EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0); } +/* The socket's O_NONBLOCK governs the record wait, as on a plain TCP + * socket, even without SPLICE_F_NONBLOCK. + */ +TEST_F(tls, splice_nonblock_socket) +{ + struct pollfd pfd = { .fd = self->cfd, .events = POLLIN }; + int send_len = TLS_PAYLOAD_MAX_LEN; + char mem_send[TLS_PAYLOAD_MAX_LEN]; + char mem_recv[TLS_PAYLOAD_MAX_LEN]; + int flags, p[2]; + + flags = fcntl(self->cfd, F_GETFL, 0); + ASSERT_GE(flags, 0); + ASSERT_EQ(fcntl(self->cfd, F_SETFL, flags | O_NONBLOCK), 0); + + memrnd(mem_send, sizeof(mem_send)); + + ASSERT_GE(pipe(p), 0); + EXPECT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), -1); + EXPECT_EQ(errno, EAGAIN); + + /* The record may still be in flight, wait for it. A failed splice() + * would leave the read() below blocking until the harness timeout. + */ + EXPECT_EQ(send(self->fd, mem_send, send_len, 0), send_len); + ASSERT_EQ(poll(&pfd, 1, 5000), 1); + ASSERT_EQ(splice(self->cfd, NULL, p[1], NULL, send_len, 0), send_len); + EXPECT_EQ(read(p[0], mem_recv, send_len), send_len); + EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0); +} + TEST_F(tls, splice_cmsg_to_pipe) { char *test_str = "test_read"; diff --git a/tools/testing/selftests/net/txtimestamp.c b/tools/testing/selftests/net/txtimestamp.c index 170be192f5c7..f7bdf30b78d9 100644 --- a/tools/testing/selftests/net/txtimestamp.c +++ b/tools/testing/selftests/net/txtimestamp.c @@ -418,19 +418,25 @@ static uint16_t get_ip_csum(const uint16_t *start, int num_words, return ~sum; } -static uint16_t get_udp_csum(const struct udphdr *udph, int alen) +static uint16_t get_udp_csum(const struct udphdr *udph, bool is_ipv4) { - unsigned long pseudo_sum, csum_len; - const void *csum_start = udph; + const void *addr = is_ipv4 ? (void *)&daddr.sin_addr : &daddr6.sin6_addr; + int alen = is_ipv4 ? sizeof(struct in_addr) : sizeof(struct in6_addr); + uint16_t addr16[sizeof(struct in6_addr) / sizeof(uint16_t)]; + unsigned long pseudo_sum; + int i; pseudo_sum = htons(IPPROTO_UDP); pseudo_sum += udph->len; - /* checksum ip(v6) addresses + udp header + payload */ - csum_start -= alen * 2; - csum_len = ntohs(udph->len) + alen * 2; + /* pseudo-header source and destination: both are daddr */ + memcpy(addr16, addr, alen); + for (i = 0; i < alen / 2; i++) + pseudo_sum += 2 * addr16[i]; - return get_ip_csum(csum_start, csum_len >> 1, pseudo_sum); + /* checksum udp header + payload */ + return get_ip_csum((const uint16_t *)udph, ntohs(udph->len) >> 1, + pseudo_sum); } static int fill_header_ipv4(void *p) @@ -479,8 +485,7 @@ static void fill_header_udp(void *p, bool is_ipv4) udph->len = ntohs(sizeof(*udph) + cfg_payload_len); udph->check = 0; - udph->check = get_udp_csum(udph, is_ipv4 ? sizeof(struct in_addr) : - sizeof(struct in6_addr)); + udph->check = get_udp_csum(udph, is_ipv4); } static void do_test(int family, unsigned int report_opt) diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/matchall.json b/tools/testing/selftests/tc-testing/tc-tests/filters/matchall.json index f8d28c415bc3..6b3eea401fc8 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/filters/matchall.json +++ b/tools/testing/selftests/tc-testing/tc-tests/filters/matchall.json @@ -482,6 +482,77 @@ ] }, { + "id": "230c", + "name": "Keep the flags of a gact action reused by a matchall filter with skip_hw", + "category": [ + "filter", + "matchall" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DUMMY ingress", + "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv4 matchall action gact pass index 199" + ], + "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x2 prio 2 protocol ipv4 matchall skip_hw action gact pass index 199", + "expExitCode": "0", + "verifyCmd": "$TC actions get action gact index 199", + "matchPattern": "skip_hw", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DUMMY ingress" + ] + }, + { + "id": "1f7a", + "name": "Keep the flags of a gact action reused by a rejected matchall filter with skip_hw", + "category": [ + "filter", + "matchall" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DUMMY ingress", + "$TC actions add action gact pass index 200", + "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv4 matchall action gact pass index 199" + ], + "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x2 prio 2 protocol ipv4 matchall skip_hw action gact pass index 199 action gact pass index 200", + "expExitCode": "2", + "verifyCmd": "$TC actions get action gact index 199", + "matchPattern": "skip_hw", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DUMMY ingress", + "$TC actions del action gact index 200" + ] + }, + { + "id": "9117", + "name": "Reject the reuse of a skip_hw gact action by a matchall filter without skip_hw", + "category": [ + "filter", + "matchall" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DUMMY ingress", + "$TC filter add dev $DUMMY parent ffff: handle 0x1 prio 1 protocol ipv4 matchall skip_hw action gact pass index 199" + ], + "cmdUnderTest": "$TC filter add dev $DUMMY parent ffff: handle 0x2 prio 2 protocol ipv4 matchall action gact pass index 199", + "expExitCode": "2", + "verifyCmd": "$TC filter get dev $DUMMY parent ffff: handle 2 prio 2 protocol ipv4 matchall", + "matchPattern": "^filter parent ffff: protocol ip pref 2 matchall.*handle 0x2.*", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DUMMY ingress" + ] + }, + { "id": "2638", "name": "Add matchall and try to get it", "category": [ |
