summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-29 08:17:05 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-29 08:17:05 -1000
commit4dd16e31135ea8437a720222df34e8745f385d6d (patch)
tree004c93006bf6fdd3618249deedab3fbbdc8bee30
parent46806af187f448b0d5481f284e7c900a57ee6a6d (diff)
parent8f44631ba02afe9fff2f0a0fb66560ab19df87fd (diff)
downloadlinux-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 ...
-rw-r--r--Documentation/netlink/specs/gtp.yaml1
-rw-r--r--Documentation/networking/ip-sysctl.rst2
-rw-r--r--Documentation/networking/tls-offload.rst16
-rw-r--r--MAINTAINERS2
-rw-r--r--drivers/net/bonding/bond_main.c2
-rw-r--r--drivers/net/dsa/motorcomm/chip.c4
-rw-r--r--drivers/net/dsa/mt7530.c7
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.c10
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c2
-rw-r--r--drivers/net/ethernet/broadcom/bgmac.c47
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_resc.c6
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_resc.h9
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.c58
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c1
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c1
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c102
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.h1
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c48
-rw-r--r--drivers/net/ethernet/google/gve/gve_flow_rule.c1
-rw-r--r--drivers/net/ethernet/hisilicon/hisi_femac.c1
-rw-r--r--drivers/net/ethernet/i825xx/82596.c25
-rw-r--r--drivers/net/ethernet/intel/ice/ice_adapter.c17
-rw-r--r--drivers/net/ethernet/intel/ice/ice_adapter.h17
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ptp.c174
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ptp.h10
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c40
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c8
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c5
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c9
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h3
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c19
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/intf.c15
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/devlink.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.h13
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rx.c6
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_txrx.c2
-rw-r--r--drivers/net/ethernet/microchip/fdma/fdma_api.c30
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c8
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_mac.c4
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_main.c19
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c10
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c3
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_packet.c15
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c7
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/main.h2
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c24
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/descs.h6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/enh_desc.c25
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/hwif.h6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c19
-rw-r--r--drivers/net/ethernet/ti/am65-cpts.c1
-rw-r--r--drivers/net/gtp.c47
-rw-r--r--drivers/net/loopback.c8
-rw-r--r--drivers/net/macvlan.c3
-rw-r--r--drivers/net/mctp/mctp-usblib-test.c4
-rw-r--r--drivers/net/pcs/pcs-xpcs-plat.c62
-rw-r--r--drivers/net/pcs/pcs-xpcs-rk.c2
-rw-r--r--drivers/net/ppp/ppp_generic.c96
-rw-r--r--drivers/net/ppp/pppoe.c8
-rw-r--r--drivers/net/pse-pd/si3474.c23
-rw-r--r--drivers/net/tun.c10
-rw-r--r--drivers/net/usb/aqc111.c8
-rw-r--r--drivers/net/usb/cdc_mbim.c5
-rw-r--r--drivers/net/usb/cdc_ncm.c13
-rw-r--r--drivers/net/usb/lan78xx.c9
-rw-r--r--drivers/net/usb/pegasus.c16
-rw-r--r--drivers/net/usb/r8152.c10
-rw-r--r--drivers/net/usb/usbnet.c20
-rw-r--r--drivers/net/veth.c5
-rw-r--r--drivers/net/vxlan/vxlan_core.c2
-rw-r--r--include/linux/etherdevice.h28
-rw-r--r--include/linux/ppp_channel.h1
-rw-r--r--include/net/inet_frag.h1
-rw-r--r--include/net/ip.h25
-rw-r--r--include/net/ip6_route.h37
-rw-r--r--include/net/xfrm.h17
-rw-r--r--net/bridge/br_netlink.c2
-rw-r--r--net/core/dev.c42
-rw-r--r--net/core/gro.c5
-rw-r--r--net/core/net-sysfs.c7
-rw-r--r--net/core/netdev-genl.c6
-rw-r--r--net/core/skbuff.c11
-rw-r--r--net/dcb/dcbnl.c2
-rw-r--r--net/devlink/region.c5
-rw-r--r--net/ipv4/inet_connection_sock.c3
-rw-r--r--net/ipv4/inet_fragment.c5
-rw-r--r--net/ipv4/ip_fragment.c3
-rw-r--r--net/ipv4/ipmr.c27
-rw-r--r--net/ipv4/metrics.c5
-rw-r--r--net/ipv4/route.c4
-rw-r--r--net/ipv4/sysctl_net_ipv4.c2
-rw-r--r--net/ipv4/tcp.c26
-rw-r--r--net/ipv4/tcp_cong.c4
-rw-r--r--net/ipv4/tcp_ipv4.c10
-rw-r--r--net/ipv4/tcp_offload.c12
-rw-r--r--net/ipv4/tcp_output.c2
-rw-r--r--net/ipv6/ip6_fib.c4
-rw-r--r--net/ipv6/ip6mr.c5
-rw-r--r--net/ipv6/reassembly.c3
-rw-r--r--net/ipv6/route.c19
-rw-r--r--net/mctp/route.c10
-rw-r--r--net/netlink/af_netlink.c12
-rw-r--r--net/openvswitch/vport-internal_dev.c8
-rw-r--r--net/packet/af_packet.c12
-rw-r--r--net/rds/af_rds.c29
-rw-r--r--net/rds/bind.c4
-rw-r--r--net/rds/ib_sysctl.c2
-rw-r--r--net/rds/rdma_transport.c3
-rw-r--r--net/rds/rds.h3
-rw-r--r--net/rds/recv.c11
-rw-r--r--net/rds/send.c9
-rw-r--r--net/rds/sysctl.c2
-rw-r--r--net/rds/tcp.c5
-rw-r--r--net/sched/act_api.c3
-rw-r--r--net/sched/act_ife.c4
-rw-r--r--net/sched/em_ipt.c3
-rw-r--r--net/sched/sch_etf.c1
-rw-r--r--net/sched/sch_generic.c14
-rw-r--r--net/sched/sch_gred.c16
-rw-r--r--net/sched/sch_taprio.c6
-rw-r--r--net/sched/sch_teql.c60
-rw-r--r--net/sctp/ipv6.c6
-rw-r--r--net/sctp/sm_make_chunk.c24
-rw-r--r--net/sctp/sm_statefuns.c2
-rw-r--r--net/tipc/bearer.c4
-rw-r--r--net/tls/tls_device.c24
-rw-r--r--net/tls/tls_main.c26
-rw-r--r--net/tls/tls_sw.c9
-rw-r--r--net/xdp/xsk_buff_pool.c2
-rw-r--r--tools/testing/selftests/bpf/prog_tests/empty_skb.c19
-rwxr-xr-xtools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh3
-rwxr-xr-xtools/testing/selftests/net/forwarding/vxlan_reserved.sh15
-rw-r--r--tools/testing/selftests/net/lib/gro.c2
-rw-r--r--tools/testing/selftests/net/netlink-dumps.c102
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_advmss_pmtu_ipv4.pkt67
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_advmss_pmtu_ipv6.pkt67
-rw-r--r--tools/testing/selftests/net/tcp_mmap.c6
-rw-r--r--tools/testing/selftests/net/tls.c31
-rw-r--r--tools/testing/selftests/net/txtimestamp.c23
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/filters/matchall.json71
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": [