aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-05 09:39:26 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-05 09:39:26 -0800
commit8fdb05de0e2db89d8f56144c60ab784812e8c3b7 (patch)
tree89d2d6564d3e401fc43217a574e08ba7ed1e7b8d
parentb20624608f350c5dadd74577629e90715d351e2c (diff)
parentbbf4a17ad9ffc4e3d7ec13d73ecd59dea149ed25 (diff)
downloadlinux-master.tar.gz
Merge tag 'net-6.19-rc9' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/netHEADmaster
Pull networking fixes from Jakub Kicinski: "Including fixes from wireless and Netfilter. Previous releases - regressions: - eth: stmmac: fix stm32 (and potentially others) resume regression - nf_tables: fix inverted genmask check in nft_map_catchall_activate() - usb: r8152: fix resume reset deadlock - fix reporting RXH_XFRM_NO_CHANGE as input_xfrm for RSS contexts Previous releases - always broken: - sched: cls_u32: use skb_header_pointer_careful() to avoid OOB reads with malicious u32 rules - eth: ice: timestamping related fixes" * tag 'net-6.19-rc9' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (38 commits) ipv6: Fix ECMP sibling count mismatch when clearing RTF_ADDRCONF netfilter: nf_tables: fix inverted genmask check in nft_map_catchall_activate() net: cpsw: Execute ndo_set_rx_mode callback in a work queue net: cpsw_new: Execute ndo_set_rx_mode callback in a work queue gve: Correct ethtool rx_dropped calculation gve: Fix stats report corruption on queue count change selftest: net: add a test-case for encap segmentation after GRO net: gro: fix outer network offset net: add proper RCU protection to /proc/net/ptype net: ethernet: adi: adin1110: Check return value of devm_gpiod_get_optional() in adin1110_check_spi() wifi: iwlwifi: mvm: pause TCM on fast resume wifi: iwlwifi: mld: cancel mlo_scan_start_wk net: spacemit: k1-emac: fix jumbo frame support net: enetc: Convert 16-bit register reads to 32-bit for ENETC v4 net: enetc: Convert 16-bit register writes to 32-bit for ENETC v4 net: enetc: Remove CBDR cacheability AXI settings for ENETC v4 net: enetc: Remove SI/BDR cacheability AXI settings for ENETC v4 tipc: use kfree_sensitive() for session key material net: stmmac: fix stm32 (and potentially others) resume regression net: rss: fix reporting RXH_XFRM_NO_CHANGE as input_xfrm for contexts ...
-rw-r--r--MAINTAINERS12
-rw-r--r--drivers/net/ethernet/adi/adin1110.c3
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_main.c39
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_vf_main.c4
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c10
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc.c11
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc4_pf.c6
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_cbdr.c4
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_hw.h17
-rw-r--r--drivers/net/ethernet/google/gve/gve_ethtool.c77
-rw-r--r--drivers/net/ethernet/google/gve/gve_main.c4
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c1
-rw-r--r--drivers/net/ethernet/intel/ice/ice_main.c26
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ptp.c179
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ptp.h18
-rw-r--r--drivers/net/ethernet/spacemit/k1_emac.c21
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c3
-rw-r--r--drivers/net/ethernet/ti/cpsw.c41
-rw-r--r--drivers/net/ethernet/ti/cpsw_new.c34
-rw-r--r--drivers/net/ethernet/ti/cpsw_priv.h1
-rw-r--r--drivers/net/macvlan.c5
-rw-r--r--drivers/net/phy/sfp.c2
-rw-r--r--drivers/net/usb/r8152.c29
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/iface.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mac80211.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/d3.c6
-rw-r--r--include/linux/skbuff.h12
-rw-r--r--net/core/filter.c8
-rw-r--r--net/core/gro.c2
-rw-r--r--net/core/link_watch.c20
-rw-r--r--net/core/net-procfs.c50
-rw-r--r--net/ethtool/common.c3
-rw-r--r--net/ethtool/rss.c9
-rw-r--r--net/ipv6/ip6_fib.c3
-rw-r--r--net/netfilter/nf_tables_api.c2
-rw-r--r--net/sched/cls_u32.c13
-rw-r--r--net/tipc/crypto.c4
-rwxr-xr-xtools/testing/selftests/net/udpgro_fwd.sh64
38 files changed, 513 insertions, 234 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 36abe938f960df..e0876732376362 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20978,6 +20978,18 @@ F: Documentation/devicetree/bindings/net/pse-pd/
F: drivers/net/pse-pd/
F: net/ethtool/pse-pd.c
+PSP SECURITY PROTOCOL
+M: Daniel Zahka <daniel.zahka@gmail.com>
+M: Jakub Kicinski <kuba@kernel.org>
+M: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
+F: Documentation/netlink/specs/psp.yaml
+F: Documentation/networking/psp.rst
+F: include/net/psp/
+F: include/net/psp.h
+F: include/uapi/linux/psp.h
+F: net/psp/
+K: struct\ psp(_assoc|_dev|hdr)\b
+
PSTORE FILESYSTEM
M: Kees Cook <kees@kernel.org>
R: Tony Luck <tony.luck@intel.com>
diff --git a/drivers/net/ethernet/adi/adin1110.c b/drivers/net/ethernet/adi/adin1110.c
index 30f9d271e59531..71a2397edf2bbf 100644
--- a/drivers/net/ethernet/adi/adin1110.c
+++ b/drivers/net/ethernet/adi/adin1110.c
@@ -1089,6 +1089,9 @@ static int adin1110_check_spi(struct adin1110_priv *priv)
reset_gpio = devm_gpiod_get_optional(&priv->spidev->dev, "reset",
GPIOD_OUT_LOW);
+ if (IS_ERR(reset_gpio))
+ return dev_err_probe(&priv->spidev->dev, PTR_ERR(reset_gpio),
+ "failed to get reset gpio\n");
if (reset_gpio) {
/* MISO pin is used for internal configuration, can't have
* anyone else disturbing the SDO line.
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c
index 0732440eeacd62..c1a3df22525492 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c
@@ -3505,6 +3505,23 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
*/
netdev->netdev_ops = &lionetdevops;
+ lio = GET_LIO(netdev);
+
+ memset(lio, 0, sizeof(struct lio));
+
+ lio->ifidx = ifidx_or_pfnum;
+
+ props = &octeon_dev->props[i];
+ props->gmxport = resp->cfg_info.linfo.gmxport;
+ props->netdev = netdev;
+
+ /* Point to the properties for octeon device to which this
+ * interface belongs.
+ */
+ lio->oct_dev = octeon_dev;
+ lio->octprops = props;
+ lio->netdev = netdev;
+
retval = netif_set_real_num_rx_queues(netdev, num_oqueues);
if (retval) {
dev_err(&octeon_dev->pci_dev->dev,
@@ -3521,16 +3538,6 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
goto setup_nic_dev_free;
}
- lio = GET_LIO(netdev);
-
- memset(lio, 0, sizeof(struct lio));
-
- lio->ifidx = ifidx_or_pfnum;
-
- props = &octeon_dev->props[i];
- props->gmxport = resp->cfg_info.linfo.gmxport;
- props->netdev = netdev;
-
lio->linfo.num_rxpciq = num_oqueues;
lio->linfo.num_txpciq = num_iqueues;
for (j = 0; j < num_oqueues; j++) {
@@ -3596,13 +3603,6 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
netdev->min_mtu = LIO_MIN_MTU_SIZE;
netdev->max_mtu = LIO_MAX_MTU_SIZE;
- /* Point to the properties for octeon device to which this
- * interface belongs.
- */
- lio->oct_dev = octeon_dev;
- lio->octprops = props;
- lio->netdev = netdev;
-
dev_dbg(&octeon_dev->pci_dev->dev,
"if%d gmx: %d hw_addr: 0x%llx\n", i,
lio->linfo.gmxport, CVM_CAST64(lio->linfo.hw_addr));
@@ -3750,6 +3750,7 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
if (!devlink) {
device_unlock(&octeon_dev->pci_dev->dev);
dev_err(&octeon_dev->pci_dev->dev, "devlink alloc failed\n");
+ i--;
goto setup_nic_dev_free;
}
@@ -3765,11 +3766,11 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
setup_nic_dev_free:
- while (i--) {
+ do {
dev_err(&octeon_dev->pci_dev->dev,
"NIC ifidx:%d Setup failed\n", i);
liquidio_destroy_nic_device(octeon_dev, i);
- }
+ } while (i--);
setup_nic_dev_done:
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
index e02942dbbcce53..43c595f3b84e45 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
@@ -2212,11 +2212,11 @@ static int setup_nic_devices(struct octeon_device *octeon_dev)
setup_nic_dev_free:
- while (i--) {
+ do {
dev_err(&octeon_dev->pci_dev->dev,
"NIC ifidx:%d Setup failed\n", i);
liquidio_destroy_nic_device(octeon_dev, i);
- }
+ } while (i--);
setup_nic_dev_done:
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
index b1e1ad9e4b48e6..66240c340492ce 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
@@ -1531,6 +1531,10 @@ static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg)
}
if_id = (status & 0xFFFF0000) >> 16;
+ if (if_id >= ethsw->sw_attr.num_ifs) {
+ dev_err(dev, "Invalid if_id %d in IRQ status\n", if_id);
+ goto out;
+ }
port_priv = ethsw->ports[if_id];
if (status & DPSW_IRQ_EVENT_LINK_CHANGED)
@@ -3024,6 +3028,12 @@ static int dpaa2_switch_init(struct fsl_mc_device *sw_dev)
goto err_close;
}
+ if (!ethsw->sw_attr.num_ifs) {
+ dev_err(dev, "DPSW device has no interfaces\n");
+ err = -ENODEV;
+ goto err_close;
+ }
+
err = dpsw_get_api_version(ethsw->mc_io, 0,
&ethsw->major,
&ethsw->minor);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index 53b26cece16a80..e380a4f3985560 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -2512,10 +2512,13 @@ int enetc_configure_si(struct enetc_ndev_priv *priv)
struct enetc_hw *hw = &si->hw;
int err;
- /* set SI cache attributes */
- enetc_wr(hw, ENETC_SICAR0,
- ENETC_SICAR_RD_COHERENT | ENETC_SICAR_WR_COHERENT);
- enetc_wr(hw, ENETC_SICAR1, ENETC_SICAR_MSI);
+ if (is_enetc_rev1(si)) {
+ /* set SI cache attributes */
+ enetc_wr(hw, ENETC_SICAR0,
+ ENETC_SICAR_RD_COHERENT | ENETC_SICAR_WR_COHERENT);
+ enetc_wr(hw, ENETC_SICAR1, ENETC_SICAR_MSI);
+ }
+
/* enable SI */
enetc_wr(hw, ENETC_SIMR, ENETC_SIMR_EN);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index 498346dd996aa9..5850540634b0cd 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -59,10 +59,10 @@ static void enetc4_pf_set_si_primary_mac(struct enetc_hw *hw, int si,
if (si != 0) {
__raw_writel(upper, hw->port + ENETC4_PSIPMAR0(si));
- __raw_writew(lower, hw->port + ENETC4_PSIPMAR1(si));
+ __raw_writel(lower, hw->port + ENETC4_PSIPMAR1(si));
} else {
__raw_writel(upper, hw->port + ENETC4_PMAR0);
- __raw_writew(lower, hw->port + ENETC4_PMAR1);
+ __raw_writel(lower, hw->port + ENETC4_PMAR1);
}
}
@@ -73,7 +73,7 @@ static void enetc4_pf_get_si_primary_mac(struct enetc_hw *hw, int si,
u16 lower;
upper = __raw_readl(hw->port + ENETC4_PSIPMAR0(si));
- lower = __raw_readw(hw->port + ENETC4_PSIPMAR1(si));
+ lower = __raw_readl(hw->port + ENETC4_PSIPMAR1(si));
put_unaligned_le32(upper, addr);
put_unaligned_le16(lower, addr + 4);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c b/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c
index 3d5f31879d5c64..a635bfdc30afc2 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c
@@ -74,10 +74,6 @@ int enetc4_setup_cbdr(struct enetc_si *si)
if (!user->ring)
return -ENOMEM;
- /* set CBDR cache attributes */
- enetc_wr(hw, ENETC_SICAR2,
- ENETC_SICAR_RD_COHERENT | ENETC_SICAR_WR_COHERENT);
-
regs.pir = hw->reg + ENETC_SICBDRPIR;
regs.cir = hw->reg + ENETC_SICBDRCIR;
regs.mr = hw->reg + ENETC_SICBDRMR;
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index 7b882b8921fe94..662e4fbafb74c7 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -708,13 +708,24 @@ struct enetc_cmd_rfse {
#define ENETC_RFSE_EN BIT(15)
#define ENETC_RFSE_MODE_BD 2
+static inline void enetc_get_primary_mac_addr(struct enetc_hw *hw, u8 *addr)
+{
+ u32 upper;
+ u16 lower;
+
+ upper = __raw_readl(hw->reg + ENETC_SIPMAR0);
+ lower = __raw_readl(hw->reg + ENETC_SIPMAR1);
+
+ put_unaligned_le32(upper, addr);
+ put_unaligned_le16(lower, addr + 4);
+}
+
static inline void enetc_load_primary_mac_addr(struct enetc_hw *hw,
struct net_device *ndev)
{
- u8 addr[ETH_ALEN] __aligned(4);
+ u8 addr[ETH_ALEN];
- *(u32 *)addr = __raw_readl(hw->reg + ENETC_SIPMAR0);
- *(u16 *)(addr + 4) = __raw_readw(hw->reg + ENETC_SIPMAR1);
+ enetc_get_primary_mac_addr(hw, addr);
eth_hw_addr_set(ndev, addr);
}
diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c
index 311b106160b20a..66ddc4413f8dff 100644
--- a/drivers/net/ethernet/google/gve/gve_ethtool.c
+++ b/drivers/net/ethernet/google/gve/gve_ethtool.c
@@ -152,11 +152,13 @@ gve_get_ethtool_stats(struct net_device *netdev,
u64 tmp_rx_pkts, tmp_rx_hsplit_pkt, tmp_rx_bytes, tmp_rx_hsplit_bytes,
tmp_rx_skb_alloc_fail, tmp_rx_buf_alloc_fail,
tmp_rx_desc_err_dropped_pkt, tmp_rx_hsplit_unsplit_pkt,
- tmp_tx_pkts, tmp_tx_bytes;
+ tmp_tx_pkts, tmp_tx_bytes,
+ tmp_xdp_tx_errors, tmp_xdp_redirect_errors;
u64 rx_buf_alloc_fail, rx_desc_err_dropped_pkt, rx_hsplit_unsplit_pkt,
rx_pkts, rx_hsplit_pkt, rx_skb_alloc_fail, rx_bytes, tx_pkts, tx_bytes,
- tx_dropped;
- int stats_idx, base_stats_idx, max_stats_idx;
+ tx_dropped, xdp_tx_errors, xdp_redirect_errors;
+ int rx_base_stats_idx, max_rx_stats_idx, max_tx_stats_idx;
+ int stats_idx, stats_region_len, nic_stats_len;
struct stats *report_stats;
int *rx_qid_to_stats_idx;
int *tx_qid_to_stats_idx;
@@ -198,6 +200,7 @@ gve_get_ethtool_stats(struct net_device *netdev,
for (rx_pkts = 0, rx_bytes = 0, rx_hsplit_pkt = 0,
rx_skb_alloc_fail = 0, rx_buf_alloc_fail = 0,
rx_desc_err_dropped_pkt = 0, rx_hsplit_unsplit_pkt = 0,
+ xdp_tx_errors = 0, xdp_redirect_errors = 0,
ring = 0;
ring < priv->rx_cfg.num_queues; ring++) {
if (priv->rx) {
@@ -215,6 +218,9 @@ gve_get_ethtool_stats(struct net_device *netdev,
rx->rx_desc_err_dropped_pkt;
tmp_rx_hsplit_unsplit_pkt =
rx->rx_hsplit_unsplit_pkt;
+ tmp_xdp_tx_errors = rx->xdp_tx_errors;
+ tmp_xdp_redirect_errors =
+ rx->xdp_redirect_errors;
} while (u64_stats_fetch_retry(&priv->rx[ring].statss,
start));
rx_pkts += tmp_rx_pkts;
@@ -224,6 +230,8 @@ gve_get_ethtool_stats(struct net_device *netdev,
rx_buf_alloc_fail += tmp_rx_buf_alloc_fail;
rx_desc_err_dropped_pkt += tmp_rx_desc_err_dropped_pkt;
rx_hsplit_unsplit_pkt += tmp_rx_hsplit_unsplit_pkt;
+ xdp_tx_errors += tmp_xdp_tx_errors;
+ xdp_redirect_errors += tmp_xdp_redirect_errors;
}
}
for (tx_pkts = 0, tx_bytes = 0, tx_dropped = 0, ring = 0;
@@ -249,8 +257,8 @@ gve_get_ethtool_stats(struct net_device *netdev,
data[i++] = rx_bytes;
data[i++] = tx_bytes;
/* total rx dropped packets */
- data[i++] = rx_skb_alloc_fail + rx_buf_alloc_fail +
- rx_desc_err_dropped_pkt;
+ data[i++] = rx_skb_alloc_fail + rx_desc_err_dropped_pkt +
+ xdp_tx_errors + xdp_redirect_errors;
data[i++] = tx_dropped;
data[i++] = priv->tx_timeo_cnt;
data[i++] = rx_skb_alloc_fail;
@@ -265,20 +273,38 @@ gve_get_ethtool_stats(struct net_device *netdev,
data[i++] = priv->stats_report_trigger_cnt;
i = GVE_MAIN_STATS_LEN;
- /* For rx cross-reporting stats, start from nic rx stats in report */
- base_stats_idx = GVE_TX_STATS_REPORT_NUM * num_tx_queues +
- GVE_RX_STATS_REPORT_NUM * priv->rx_cfg.num_queues;
- /* The boundary between driver stats and NIC stats shifts if there are
- * stopped queues.
- */
- base_stats_idx += NIC_RX_STATS_REPORT_NUM * num_stopped_rxqs +
- NIC_TX_STATS_REPORT_NUM * num_stopped_txqs;
- max_stats_idx = NIC_RX_STATS_REPORT_NUM *
- (priv->rx_cfg.num_queues - num_stopped_rxqs) +
- base_stats_idx;
+ rx_base_stats_idx = 0;
+ max_rx_stats_idx = 0;
+ max_tx_stats_idx = 0;
+ stats_region_len = priv->stats_report_len -
+ sizeof(struct gve_stats_report);
+ nic_stats_len = (NIC_RX_STATS_REPORT_NUM * priv->rx_cfg.num_queues +
+ NIC_TX_STATS_REPORT_NUM * num_tx_queues) * sizeof(struct stats);
+ if (unlikely((stats_region_len -
+ nic_stats_len) % sizeof(struct stats))) {
+ net_err_ratelimited("Starting index of NIC stats should be multiple of stats size");
+ } else {
+ /* For rx cross-reporting stats,
+ * start from nic rx stats in report
+ */
+ rx_base_stats_idx = (stats_region_len - nic_stats_len) /
+ sizeof(struct stats);
+ /* The boundary between driver stats and NIC stats
+ * shifts if there are stopped queues
+ */
+ rx_base_stats_idx += NIC_RX_STATS_REPORT_NUM *
+ num_stopped_rxqs + NIC_TX_STATS_REPORT_NUM *
+ num_stopped_txqs;
+ max_rx_stats_idx = NIC_RX_STATS_REPORT_NUM *
+ (priv->rx_cfg.num_queues - num_stopped_rxqs) +
+ rx_base_stats_idx;
+ max_tx_stats_idx = NIC_TX_STATS_REPORT_NUM *
+ (num_tx_queues - num_stopped_txqs) +
+ max_rx_stats_idx;
+ }
/* Preprocess the stats report for rx, map queue id to start index */
skip_nic_stats = false;
- for (stats_idx = base_stats_idx; stats_idx < max_stats_idx;
+ for (stats_idx = rx_base_stats_idx; stats_idx < max_rx_stats_idx;
stats_idx += NIC_RX_STATS_REPORT_NUM) {
u32 stat_name = be32_to_cpu(report_stats[stats_idx].stat_name);
u32 queue_id = be32_to_cpu(report_stats[stats_idx].queue_id);
@@ -311,6 +337,9 @@ gve_get_ethtool_stats(struct net_device *netdev,
tmp_rx_buf_alloc_fail = rx->rx_buf_alloc_fail;
tmp_rx_desc_err_dropped_pkt =
rx->rx_desc_err_dropped_pkt;
+ tmp_xdp_tx_errors = rx->xdp_tx_errors;
+ tmp_xdp_redirect_errors =
+ rx->xdp_redirect_errors;
} while (u64_stats_fetch_retry(&priv->rx[ring].statss,
start));
data[i++] = tmp_rx_bytes;
@@ -321,8 +350,9 @@ gve_get_ethtool_stats(struct net_device *netdev,
data[i++] = rx->rx_frag_alloc_cnt;
/* rx dropped packets */
data[i++] = tmp_rx_skb_alloc_fail +
- tmp_rx_buf_alloc_fail +
- tmp_rx_desc_err_dropped_pkt;
+ tmp_rx_desc_err_dropped_pkt +
+ tmp_xdp_tx_errors +
+ tmp_xdp_redirect_errors;
data[i++] = rx->rx_copybreak_pkt;
data[i++] = rx->rx_copied_pkt;
/* stats from NIC */
@@ -354,14 +384,9 @@ gve_get_ethtool_stats(struct net_device *netdev,
i += priv->rx_cfg.num_queues * NUM_GVE_RX_CNTS;
}
- /* For tx cross-reporting stats, start from nic tx stats in report */
- base_stats_idx = max_stats_idx;
- max_stats_idx = NIC_TX_STATS_REPORT_NUM *
- (num_tx_queues - num_stopped_txqs) +
- max_stats_idx;
- /* Preprocess the stats report for tx, map queue id to start index */
skip_nic_stats = false;
- for (stats_idx = base_stats_idx; stats_idx < max_stats_idx;
+ /* NIC TX stats start right after NIC RX stats */
+ for (stats_idx = max_rx_stats_idx; stats_idx < max_tx_stats_idx;
stats_idx += NIC_TX_STATS_REPORT_NUM) {
u32 stat_name = be32_to_cpu(report_stats[stats_idx].stat_name);
u32 queue_id = be32_to_cpu(report_stats[stats_idx].queue_id);
diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c
index 52c5e4942cd489..dbc84de39b7078 100644
--- a/drivers/net/ethernet/google/gve/gve_main.c
+++ b/drivers/net/ethernet/google/gve/gve_main.c
@@ -283,9 +283,9 @@ static int gve_alloc_stats_report(struct gve_priv *priv)
int tx_stats_num, rx_stats_num;
tx_stats_num = (GVE_TX_STATS_REPORT_NUM + NIC_TX_STATS_REPORT_NUM) *
- gve_num_tx_queues(priv);
+ priv->tx_cfg.max_queues;
rx_stats_num = (GVE_RX_STATS_REPORT_NUM + NIC_RX_STATS_REPORT_NUM) *
- priv->rx_cfg.num_queues;
+ priv->rx_cfg.max_queues;
priv->stats_report_len = struct_size(priv->stats_report, stats,
size_add(tx_stats_num, rx_stats_num));
priv->stats_report =
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 0b1cc0481027ab..d3bc3207054f90 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -9030,7 +9030,6 @@ int i40e_open(struct net_device *netdev)
TCP_FLAG_FIN |
TCP_FLAG_CWR) >> 16);
wr32(&pf->hw, I40E_GLLAN_TSOMSK_L, be32_to_cpu(TCP_FLAG_CWR) >> 16);
- udp_tunnel_get_rx_info(netdev);
return 0;
}
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 71c6d53b461e9f..d04605d3e61af1 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -3314,18 +3314,20 @@ static irqreturn_t ice_misc_intr_thread_fn(int __always_unused irq, void *data)
if (ice_is_reset_in_progress(pf->state))
goto skip_irq;
- if (test_and_clear_bit(ICE_MISC_THREAD_TX_TSTAMP, pf->misc_thread)) {
- /* Process outstanding Tx timestamps. If there is more work,
- * re-arm the interrupt to trigger again.
- */
- if (ice_ptp_process_ts(pf) == ICE_TX_TSTAMP_WORK_PENDING) {
- wr32(hw, PFINT_OICR, PFINT_OICR_TSYN_TX_M);
- ice_flush(hw);
- }
- }
+ if (test_and_clear_bit(ICE_MISC_THREAD_TX_TSTAMP, pf->misc_thread))
+ ice_ptp_process_ts(pf);
skip_irq:
ice_irq_dynamic_ena(hw, NULL, NULL);
+ ice_flush(hw);
+
+ if (ice_ptp_tx_tstamps_pending(pf)) {
+ /* If any new Tx timestamps happened while in interrupt,
+ * re-arm the interrupt to trigger it again.
+ */
+ wr32(hw, PFINT_OICR, PFINT_OICR_TSYN_TX_M);
+ ice_flush(hw);
+ }
return IRQ_HANDLED;
}
@@ -7807,6 +7809,9 @@ static void ice_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type)
/* Restore timestamp mode settings after VSI rebuild */
ice_ptp_restore_timestamp_mode(pf);
+
+ /* Start PTP periodic work after VSI is fully rebuilt */
+ ice_ptp_queue_work(pf);
return;
err_vsi_rebuild:
@@ -9657,9 +9662,6 @@ int ice_open_internal(struct net_device *netdev)
netdev_err(netdev, "Failed to open VSI 0x%04X on switch 0x%04X\n",
vsi->vsi_num, vsi->vsw->sw_id);
- /* Update existing tunnels information */
- udp_tunnel_get_rx_info(netdev);
-
return err;
}
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 4c8d20f2d2c0a4..27268300147625 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -573,6 +573,9 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
pf = ptp_port_to_pf(ptp_port);
hw = &pf->hw;
+ if (!tx->init)
+ return;
+
/* Read the Tx ready status first */
if (tx->has_ready_bitmap) {
err = ice_get_phy_tx_tstamp_ready(hw, tx->block, &tstamp_ready);
@@ -674,14 +677,9 @@ skip_ts_read:
pf->ptp.tx_hwtstamp_good += tstamp_good;
}
-/**
- * ice_ptp_tx_tstamp_owner - Process Tx timestamps for all ports on the device
- * @pf: Board private structure
- */
-static enum ice_tx_tstamp_work ice_ptp_tx_tstamp_owner(struct ice_pf *pf)
+static void ice_ptp_tx_tstamp_owner(struct ice_pf *pf)
{
struct ice_ptp_port *port;
- unsigned int i;
mutex_lock(&pf->adapter->ports.lock);
list_for_each_entry(port, &pf->adapter->ports.ports, list_node) {
@@ -693,49 +691,6 @@ static enum ice_tx_tstamp_work ice_ptp_tx_tstamp_owner(struct ice_pf *pf)
ice_ptp_process_tx_tstamp(tx);
}
mutex_unlock(&pf->adapter->ports.lock);
-
- for (i = 0; i < ICE_GET_QUAD_NUM(pf->hw.ptp.num_lports); i++) {
- u64 tstamp_ready;
- int err;
-
- /* Read the Tx ready status first */
- err = ice_get_phy_tx_tstamp_ready(&pf->hw, i, &tstamp_ready);
- if (err)
- break;
- else if (tstamp_ready)
- return ICE_TX_TSTAMP_WORK_PENDING;
- }
-
- return ICE_TX_TSTAMP_WORK_DONE;
-}
-
-/**
- * ice_ptp_tx_tstamp - Process Tx timestamps for this function.
- * @tx: Tx tracking structure to initialize
- *
- * Returns: ICE_TX_TSTAMP_WORK_PENDING if there are any outstanding incomplete
- * Tx timestamps, or ICE_TX_TSTAMP_WORK_DONE otherwise.
- */
-static enum ice_tx_tstamp_work ice_ptp_tx_tstamp(struct ice_ptp_tx *tx)
-{
- bool more_timestamps;
- unsigned long flags;
-
- if (!tx->init)
- return ICE_TX_TSTAMP_WORK_DONE;
-
- /* Process the Tx timestamp tracker */
- ice_ptp_process_tx_tstamp(tx);
-
- /* Check if there are outstanding Tx timestamps */
- spin_lock_irqsave(&tx->lock, flags);
- more_timestamps = tx->init && !bitmap_empty(tx->in_use, tx->len);
- spin_unlock_irqrestore(&tx->lock, flags);
-
- if (more_timestamps)
- return ICE_TX_TSTAMP_WORK_PENDING;
-
- return ICE_TX_TSTAMP_WORK_DONE;
}
/**
@@ -1347,9 +1302,12 @@ void ice_ptp_link_change(struct ice_pf *pf, bool linkup)
/* Do not reconfigure E810 or E830 PHY */
return;
case ICE_MAC_GENERIC:
- case ICE_MAC_GENERIC_3K_E825:
ice_ptp_port_phy_restart(ptp_port);
return;
+ case ICE_MAC_GENERIC_3K_E825:
+ if (linkup)
+ ice_ptp_port_phy_restart(ptp_port);
+ return;
default:
dev_warn(ice_pf_to_dev(pf), "%s: Unknown PHY type\n", __func__);
}
@@ -2663,30 +2621,92 @@ s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb)
return idx + tx->offset;
}
-/**
- * ice_ptp_process_ts - Process the PTP Tx timestamps
- * @pf: Board private structure
- *
- * Returns: ICE_TX_TSTAMP_WORK_PENDING if there are any outstanding Tx
- * timestamps that need processing, and ICE_TX_TSTAMP_WORK_DONE otherwise.
- */
-enum ice_tx_tstamp_work ice_ptp_process_ts(struct ice_pf *pf)
+void ice_ptp_process_ts(struct ice_pf *pf)
{
switch (pf->ptp.tx_interrupt_mode) {
case ICE_PTP_TX_INTERRUPT_NONE:
/* This device has the clock owner handle timestamps for it */
- return ICE_TX_TSTAMP_WORK_DONE;
+ return;
case ICE_PTP_TX_INTERRUPT_SELF:
/* This device handles its own timestamps */
- return ice_ptp_tx_tstamp(&pf->ptp.port.tx);
+ ice_ptp_process_tx_tstamp(&pf->ptp.port.tx);
+ return;
case ICE_PTP_TX_INTERRUPT_ALL:
/* This device handles timestamps for all ports */
- return ice_ptp_tx_tstamp_owner(pf);
+ ice_ptp_tx_tstamp_owner(pf);
+ return;
+ default:
+ WARN_ONCE(1, "Unexpected Tx timestamp interrupt mode %u\n",
+ pf->ptp.tx_interrupt_mode);
+ return;
+ }
+}
+
+static bool ice_port_has_timestamps(struct ice_ptp_tx *tx)
+{
+ bool more_timestamps;
+
+ scoped_guard(spinlock_irqsave, &tx->lock) {
+ if (!tx->init)
+ return false;
+
+ more_timestamps = !bitmap_empty(tx->in_use, tx->len);
+ }
+
+ return more_timestamps;
+}
+
+static bool ice_any_port_has_timestamps(struct ice_pf *pf)
+{
+ struct ice_ptp_port *port;
+
+ 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;
+
+ if (ice_port_has_timestamps(tx))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf)
+{
+ struct ice_hw *hw = &pf->hw;
+ unsigned int i;
+
+ /* Check software indicator */
+ switch (pf->ptp.tx_interrupt_mode) {
+ case ICE_PTP_TX_INTERRUPT_NONE:
+ return false;
+ case ICE_PTP_TX_INTERRUPT_SELF:
+ if (ice_port_has_timestamps(&pf->ptp.port.tx))
+ return true;
+ break;
+ case ICE_PTP_TX_INTERRUPT_ALL:
+ if (ice_any_port_has_timestamps(pf))
+ return true;
+ break;
default:
WARN_ONCE(1, "Unexpected Tx timestamp interrupt mode %u\n",
pf->ptp.tx_interrupt_mode);
- return ICE_TX_TSTAMP_WORK_DONE;
+ break;
+ }
+
+ /* Check hardware indicator */
+ for (i = 0; i < ICE_GET_QUAD_NUM(hw->ptp.num_lports); i++) {
+ u64 tstamp_ready = 0;
+ int err;
+
+ err = ice_get_phy_tx_tstamp_ready(&pf->hw, i, &tstamp_ready);
+ if (err || tstamp_ready)
+ return true;
}
+
+ return false;
}
/**
@@ -2738,7 +2758,9 @@ irqreturn_t ice_ptp_ts_irq(struct ice_pf *pf)
return IRQ_WAKE_THREAD;
case ICE_MAC_E830:
/* E830 can read timestamps in the top half using rd32() */
- if (ice_ptp_process_ts(pf) == ICE_TX_TSTAMP_WORK_PENDING) {
+ ice_ptp_process_ts(pf);
+
+ if (ice_ptp_tx_tstamps_pending(pf)) {
/* Process outstanding Tx timestamps. If there
* is more work, re-arm the interrupt to trigger again.
*/
@@ -2818,6 +2840,20 @@ static void ice_ptp_periodic_work(struct kthread_work *work)
}
/**
+ * ice_ptp_queue_work - Queue PTP periodic work for a PF
+ * @pf: Board private structure
+ *
+ * Helper function to queue PTP periodic work after VSI rebuild completes.
+ * This ensures that PTP work only runs when VSI structures are ready.
+ */
+void ice_ptp_queue_work(struct ice_pf *pf)
+{
+ if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags) &&
+ pf->ptp.state == ICE_PTP_READY)
+ kthread_queue_delayed_work(pf->ptp.kworker, &pf->ptp.work, 0);
+}
+
+/**
* ice_ptp_prepare_rebuild_sec - Prepare second NAC for PTP reset or rebuild
* @pf: Board private structure
* @rebuild: rebuild if true, prepare if false
@@ -2835,10 +2871,15 @@ static void ice_ptp_prepare_rebuild_sec(struct ice_pf *pf, bool rebuild,
struct ice_pf *peer_pf = ptp_port_to_pf(port);
if (!ice_is_primary(&peer_pf->hw)) {
- if (rebuild)
+ if (rebuild) {
+ /* TODO: When implementing rebuild=true:
+ * 1. Ensure secondary PFs' VSIs are rebuilt
+ * 2. Call ice_ptp_queue_work(peer_pf) after VSI rebuild
+ */
ice_ptp_rebuild(peer_pf, reset_type);
- else
+ } else {
ice_ptp_prepare_for_reset(peer_pf, reset_type);
+ }
}
}
}
@@ -2984,9 +3025,6 @@ void ice_ptp_rebuild(struct ice_pf *pf, enum ice_reset_req reset_type)
ptp->state = ICE_PTP_READY;
- /* Start periodic work going */
- kthread_queue_delayed_work(ptp->kworker, &ptp->work, 0);
-
dev_info(ice_pf_to_dev(pf), "PTP reset successful\n");
return;
@@ -3191,8 +3229,9 @@ static void ice_ptp_init_tx_interrupt_mode(struct ice_pf *pf)
{
switch (pf->hw.mac_type) {
case ICE_MAC_GENERIC:
- /* E822 based PHY has the clock owner process the interrupt
- * for all ports.
+ case ICE_MAC_GENERIC_3K_E825:
+ /* E82x hardware has the clock owner process timestamps for
+ * all ports.
*/
if (ice_pf_src_tmr_owned(pf))
pf->ptp.tx_interrupt_mode = ICE_PTP_TX_INTERRUPT_ALL;
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h
index 27016aac4f1e8a..8c44bd758a4fe3 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.h
@@ -304,8 +304,9 @@ void ice_ptp_extts_event(struct ice_pf *pf);
s8 ice_ptp_request_ts(struct ice_ptp_tx *tx, struct sk_buff *skb);
void ice_ptp_req_tx_single_tstamp(struct ice_ptp_tx *tx, u8 idx);
void ice_ptp_complete_tx_single_tstamp(struct ice_ptp_tx *tx);
-enum ice_tx_tstamp_work ice_ptp_process_ts(struct ice_pf *pf);
+void ice_ptp_process_ts(struct ice_pf *pf);
irqreturn_t ice_ptp_ts_irq(struct ice_pf *pf);
+bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf);
u64 ice_ptp_read_src_clk_reg(struct ice_pf *pf,
struct ptp_system_timestamp *sts);
@@ -317,6 +318,7 @@ void ice_ptp_prepare_for_reset(struct ice_pf *pf,
void ice_ptp_init(struct ice_pf *pf);
void ice_ptp_release(struct ice_pf *pf);
void ice_ptp_link_change(struct ice_pf *pf, bool linkup);
+void ice_ptp_queue_work(struct ice_pf *pf);
#else /* IS_ENABLED(CONFIG_PTP_1588_CLOCK) */
static inline int ice_ptp_hwtstamp_get(struct net_device *netdev,
@@ -345,16 +347,18 @@ static inline void ice_ptp_req_tx_single_tstamp(struct ice_ptp_tx *tx, u8 idx)
static inline void ice_ptp_complete_tx_single_tstamp(struct ice_ptp_tx *tx) { }
-static inline bool ice_ptp_process_ts(struct ice_pf *pf)
-{
- return true;
-}
+static inline void ice_ptp_process_ts(struct ice_pf *pf) { }
static inline irqreturn_t ice_ptp_ts_irq(struct ice_pf *pf)
{
return IRQ_HANDLED;
}
+static inline bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf)
+{
+ return false;
+}
+
static inline u64 ice_ptp_read_src_clk_reg(struct ice_pf *pf,
struct ptp_system_timestamp *sts)
{
@@ -383,6 +387,10 @@ static inline void ice_ptp_link_change(struct ice_pf *pf, bool linkup)
{
}
+static inline void ice_ptp_queue_work(struct ice_pf *pf)
+{
+}
+
static inline int ice_ptp_clock_index(struct ice_pf *pf)
{
return -1;
diff --git a/drivers/net/ethernet/spacemit/k1_emac.c b/drivers/net/ethernet/spacemit/k1_emac.c
index 88e9424d2d51aa..b49c4708bf9eb1 100644
--- a/drivers/net/ethernet/spacemit/k1_emac.c
+++ b/drivers/net/ethernet/spacemit/k1_emac.c
@@ -12,6 +12,7 @@
#include <linux/dma-mapping.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
@@ -38,7 +39,7 @@
#define EMAC_DEFAULT_BUFSIZE 1536
#define EMAC_RX_BUF_2K 2048
-#define EMAC_RX_BUF_4K 4096
+#define EMAC_RX_BUF_MAX FIELD_MAX(RX_DESC_1_BUFFER_SIZE_1_MASK)
/* Tuning parameters from SpacemiT */
#define EMAC_TX_FRAMES 64
@@ -202,8 +203,7 @@ static void emac_init_hw(struct emac_priv *priv)
{
/* Destination address for 802.3x Ethernet flow control */
u8 fc_dest_addr[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x01 };
-
- u32 rxirq = 0, dma = 0;
+ u32 rxirq = 0, dma = 0, frame_sz;
regmap_set_bits(priv->regmap_apmu,
priv->regmap_apmu_offset + APMU_EMAC_CTRL_REG,
@@ -228,6 +228,15 @@ static void emac_init_hw(struct emac_priv *priv)
DEFAULT_TX_THRESHOLD);
emac_wr(priv, MAC_RECEIVE_PACKET_START_THRESHOLD, DEFAULT_RX_THRESHOLD);
+ /* Set maximum frame size and jabber size based on configured MTU,
+ * accounting for Ethernet header, double VLAN tags, and FCS.
+ */
+ frame_sz = priv->ndev->mtu + ETH_HLEN + 2 * VLAN_HLEN + ETH_FCS_LEN;
+
+ emac_wr(priv, MAC_MAXIMUM_FRAME_SIZE, frame_sz);
+ emac_wr(priv, MAC_TRANSMIT_JABBER_SIZE, frame_sz);
+ emac_wr(priv, MAC_RECEIVE_JABBER_SIZE, frame_sz);
+
/* Configure flow control (enabled in emac_adjust_link() later) */
emac_set_mac_addr_reg(priv, fc_dest_addr, MAC_FC_SOURCE_ADDRESS_HIGH);
emac_wr(priv, MAC_FC_PAUSE_HIGH_THRESHOLD, DEFAULT_FC_FIFO_HIGH);
@@ -924,14 +933,14 @@ static int emac_change_mtu(struct net_device *ndev, int mtu)
return -EBUSY;
}
- frame_len = mtu + ETH_HLEN + ETH_FCS_LEN;
+ frame_len = mtu + ETH_HLEN + 2 * VLAN_HLEN + ETH_FCS_LEN;
if (frame_len <= EMAC_DEFAULT_BUFSIZE)
priv->dma_buf_sz = EMAC_DEFAULT_BUFSIZE;
else if (frame_len <= EMAC_RX_BUF_2K)
priv->dma_buf_sz = EMAC_RX_BUF_2K;
else
- priv->dma_buf_sz = EMAC_RX_BUF_4K;
+ priv->dma_buf_sz = EMAC_RX_BUF_MAX;
ndev->mtu = mtu;
@@ -2025,7 +2034,7 @@ static int emac_probe(struct platform_device *pdev)
ndev->hw_features = NETIF_F_SG;
ndev->features |= ndev->hw_features;
- ndev->max_mtu = EMAC_RX_BUF_4K - (ETH_HLEN + ETH_FCS_LEN);
+ ndev->max_mtu = EMAC_RX_BUF_MAX - (ETH_HLEN + 2 * VLAN_HLEN + ETH_FCS_LEN);
ndev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS;
priv = netdev_priv(ndev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 3f42843cd9ed01..a379221b96a348 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -8042,7 +8042,7 @@ int stmmac_suspend(struct device *dev)
u32 chan;
if (!ndev || !netif_running(ndev))
- return 0;
+ goto suspend_bsp;
mutex_lock(&priv->lock);
@@ -8082,6 +8082,7 @@ int stmmac_suspend(struct device *dev)
if (stmmac_fpe_supported(priv))
ethtool_mmsv_stop(&priv->fpe_cfg.mmsv);
+suspend_bsp:
if (priv->plat->suspend)
return priv->plat->suspend(dev, priv->plat->bsp_priv);
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 54c24cd3d3be63..b0e18bdc2c8510 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -305,12 +305,19 @@ static int cpsw_purge_all_mc(struct net_device *ndev, const u8 *addr, int num)
return 0;
}
-static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
+static void cpsw_ndo_set_rx_mode_work(struct work_struct *work)
{
- struct cpsw_priv *priv = netdev_priv(ndev);
+ struct cpsw_priv *priv = container_of(work, struct cpsw_priv, rx_mode_work);
struct cpsw_common *cpsw = priv->cpsw;
+ struct net_device *ndev = priv->ndev;
int slave_port = -1;
+ rtnl_lock();
+ if (!netif_running(ndev))
+ goto unlock_rtnl;
+
+ netif_addr_lock_bh(ndev);
+
if (cpsw->data.dual_emac)
slave_port = priv->emac_port + 1;
@@ -318,7 +325,7 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
/* Enable promiscuous mode */
cpsw_set_promiscious(ndev, true);
cpsw_ale_set_allmulti(cpsw->ale, IFF_ALLMULTI, slave_port);
- return;
+ goto unlock_addr;
} else {
/* Disable promiscuous mode */
cpsw_set_promiscious(ndev, false);
@@ -331,6 +338,18 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
/* add/remove mcast address either for real netdev or for vlan */
__hw_addr_ref_sync_dev(&ndev->mc, ndev, cpsw_add_mc_addr,
cpsw_del_mc_addr);
+
+unlock_addr:
+ netif_addr_unlock_bh(ndev);
+unlock_rtnl:
+ rtnl_unlock();
+}
+
+static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
+{
+ struct cpsw_priv *priv = netdev_priv(ndev);
+
+ schedule_work(&priv->rx_mode_work);
}
static unsigned int cpsw_rxbuf_total_len(unsigned int len)
@@ -1472,6 +1491,7 @@ static int cpsw_probe_dual_emac(struct cpsw_priv *priv)
priv_sl2->ndev = ndev;
priv_sl2->dev = &ndev->dev;
priv_sl2->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
+ INIT_WORK(&priv_sl2->rx_mode_work, cpsw_ndo_set_rx_mode_work);
if (is_valid_ether_addr(data->slave_data[1].mac_addr)) {
memcpy(priv_sl2->mac_addr, data->slave_data[1].mac_addr,
@@ -1653,6 +1673,7 @@ static int cpsw_probe(struct platform_device *pdev)
priv->dev = dev;
priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
priv->emac_port = 0;
+ INIT_WORK(&priv->rx_mode_work, cpsw_ndo_set_rx_mode_work);
if (is_valid_ether_addr(data->slave_data[0].mac_addr)) {
memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN);
@@ -1758,6 +1779,8 @@ clean_runtime_disable_ret:
static void cpsw_remove(struct platform_device *pdev)
{
struct cpsw_common *cpsw = platform_get_drvdata(pdev);
+ struct net_device *ndev;
+ struct cpsw_priv *priv;
int i, ret;
ret = pm_runtime_resume_and_get(&pdev->dev);
@@ -1770,9 +1793,15 @@ static void cpsw_remove(struct platform_device *pdev)
return;
}
- for (i = 0; i < cpsw->data.slaves; i++)
- if (cpsw->slaves[i].ndev)
- unregister_netdev(cpsw->slaves[i].ndev);
+ for (i = 0; i < cpsw->data.slaves; i++) {
+ ndev = cpsw->slaves[i].ndev;
+ if (!ndev)
+ continue;
+
+ priv = netdev_priv(ndev);
+ unregister_netdev(ndev);
+ disable_work_sync(&priv->rx_mode_work);
+ }
cpts_release(cpsw->cpts);
cpdma_ctlr_destroy(cpsw->dma);
diff --git a/drivers/net/ethernet/ti/cpsw_new.c b/drivers/net/ethernet/ti/cpsw_new.c
index ab88d4c02cbde7..21af0a10626aaf 100644
--- a/drivers/net/ethernet/ti/cpsw_new.c
+++ b/drivers/net/ethernet/ti/cpsw_new.c
@@ -248,16 +248,22 @@ static int cpsw_purge_all_mc(struct net_device *ndev, const u8 *addr, int num)
return 0;
}
-static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
+static void cpsw_ndo_set_rx_mode_work(struct work_struct *work)
{
- struct cpsw_priv *priv = netdev_priv(ndev);
+ struct cpsw_priv *priv = container_of(work, struct cpsw_priv, rx_mode_work);
struct cpsw_common *cpsw = priv->cpsw;
+ struct net_device *ndev = priv->ndev;
+ rtnl_lock();
+ if (!netif_running(ndev))
+ goto unlock_rtnl;
+
+ netif_addr_lock_bh(ndev);
if (ndev->flags & IFF_PROMISC) {
/* Enable promiscuous mode */
cpsw_set_promiscious(ndev, true);
cpsw_ale_set_allmulti(cpsw->ale, IFF_ALLMULTI, priv->emac_port);
- return;
+ goto unlock_addr;
}
/* Disable promiscuous mode */
@@ -270,6 +276,18 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
/* add/remove mcast address either for real netdev or for vlan */
__hw_addr_ref_sync_dev(&ndev->mc, ndev, cpsw_add_mc_addr,
cpsw_del_mc_addr);
+
+unlock_addr:
+ netif_addr_unlock_bh(ndev);
+unlock_rtnl:
+ rtnl_unlock();
+}
+
+static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
+{
+ struct cpsw_priv *priv = netdev_priv(ndev);
+
+ schedule_work(&priv->rx_mode_work);
}
static unsigned int cpsw_rxbuf_total_len(unsigned int len)
@@ -1398,6 +1416,7 @@ static int cpsw_create_ports(struct cpsw_common *cpsw)
priv->msg_enable = netif_msg_init(debug_level, CPSW_DEBUG);
priv->emac_port = i + 1;
priv->tx_packet_min = CPSW_MIN_PACKET_SIZE;
+ INIT_WORK(&priv->rx_mode_work, cpsw_ndo_set_rx_mode_work);
if (is_valid_ether_addr(slave_data->mac_addr)) {
ether_addr_copy(priv->mac_addr, slave_data->mac_addr);
@@ -1447,13 +1466,18 @@ static int cpsw_create_ports(struct cpsw_common *cpsw)
static void cpsw_unregister_ports(struct cpsw_common *cpsw)
{
+ struct net_device *ndev;
+ struct cpsw_priv *priv;
int i = 0;
for (i = 0; i < cpsw->data.slaves; i++) {
- if (!cpsw->slaves[i].ndev)
+ ndev = cpsw->slaves[i].ndev;
+ if (!ndev)
continue;
- unregister_netdev(cpsw->slaves[i].ndev);
+ priv = netdev_priv(ndev);
+ unregister_netdev(ndev);
+ disable_work_sync(&priv->rx_mode_work);
}
}
diff --git a/drivers/net/ethernet/ti/cpsw_priv.h b/drivers/net/ethernet/ti/cpsw_priv.h
index 91add8925e235c..acb6181c5c9e1b 100644
--- a/drivers/net/ethernet/ti/cpsw_priv.h
+++ b/drivers/net/ethernet/ti/cpsw_priv.h
@@ -391,6 +391,7 @@ struct cpsw_priv {
u32 tx_packet_min;
struct cpsw_ale_ratelimit ale_bc_ratelimit;
struct cpsw_ale_ratelimit ale_mc_ratelimit;
+ struct work_struct rx_mode_work;
};
#define ndev_to_cpsw(ndev) (((struct cpsw_priv *)netdev_priv(ndev))->cpsw)
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index b4df7e184791d0..c509228be84d1b 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -1567,9 +1567,10 @@ destroy_macvlan_port:
/* the macvlan port may be freed by macvlan_uninit when fail to register.
* so we destroy the macvlan port only when it's valid.
*/
- if (create && macvlan_port_get_rtnl(lowerdev)) {
+ if (macvlan_port_get_rtnl(lowerdev)) {
macvlan_flush_sources(port, vlan);
- macvlan_port_destroy(port->dev);
+ if (create)
+ macvlan_port_destroy(port->dev);
}
return err;
}
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index 47f095bd91ceab..3e023723887c4b 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -479,6 +479,8 @@ static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id,
linkmode_zero(caps->link_modes);
linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
caps->link_modes);
+ phy_interface_zero(caps->interfaces);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX, caps->interfaces);
}
#define SFP_QUIRK(_v, _p, _s, _f) \
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index fa519258386070..2f3baa5f6e9c94 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -8535,19 +8535,6 @@ static int rtl8152_system_resume(struct r8152 *tp)
usb_submit_urb(tp->intr_urb, GFP_NOIO);
}
- /* If the device is RTL8152_INACCESSIBLE here then we should do a
- * reset. This is important because the usb_lock_device_for_reset()
- * that happens as a result of usb_queue_reset_device() will silently
- * fail if the device was suspended or if too much time passed.
- *
- * NOTE: The device is locked here so we can directly do the reset.
- * We don't need usb_lock_device_for_reset() because that's just a
- * wrapper over device_lock() and device_resume() (which calls us)
- * does that for us.
- */
- if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
- usb_reset_device(tp->udev);
-
return 0;
}
@@ -8658,19 +8645,33 @@ static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message)
static int rtl8152_resume(struct usb_interface *intf)
{
struct r8152 *tp = usb_get_intfdata(intf);
+ bool runtime_resume = test_bit(SELECTIVE_SUSPEND, &tp->flags);
int ret;
mutex_lock(&tp->control);
rtl_reset_ocp_base(tp);
- if (test_bit(SELECTIVE_SUSPEND, &tp->flags))
+ if (runtime_resume)
ret = rtl8152_runtime_resume(tp);
else
ret = rtl8152_system_resume(tp);
mutex_unlock(&tp->control);
+ /* If the device is RTL8152_INACCESSIBLE here then we should do a
+ * reset. This is important because the usb_lock_device_for_reset()
+ * that happens as a result of usb_queue_reset_device() will silently
+ * fail if the device was suspended or if too much time passed.
+ *
+ * NOTE: The device is locked here so we can directly do the reset.
+ * We don't need usb_lock_device_for_reset() because that's just a
+ * wrapper over device_lock() and device_resume() (which calls us)
+ * does that for us.
+ */
+ if (!runtime_resume && test_bit(RTL8152_INACCESSIBLE, &tp->flags))
+ usb_reset_device(tp->udev);
+
return ret;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index a5ececfc13e449..f15d1f5d1bf593 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -55,8 +55,6 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
ieee80211_iter_keys(mld->hw, vif, iwl_mld_cleanup_keys_iter, NULL);
- wiphy_delayed_work_cancel(mld->wiphy, &mld_vif->mlo_scan_start_wk);
-
CLEANUP_STRUCT(mld_vif);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
index 55b484c162807f..cd0dce8de85690 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -1759,6 +1759,8 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld,
wiphy_work_cancel(mld->wiphy, &mld_vif->emlsr.unblock_tpt_wk);
wiphy_delayed_work_cancel(mld->wiphy,
&mld_vif->emlsr.check_tpt_wk);
+ wiphy_delayed_work_cancel(mld->wiphy,
+ &mld_vif->mlo_scan_start_wk);
iwl_mld_reset_cca_40mhz_workaround(mld, vif);
iwl_mld_smps_workaround(mld, vif, true);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index 07f1a84c274efe..af1a45845999bb 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2026 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -3239,6 +3239,8 @@ void iwl_mvm_fast_suspend(struct iwl_mvm *mvm)
IWL_DEBUG_WOWLAN(mvm, "Starting fast suspend flow\n");
+ iwl_mvm_pause_tcm(mvm, true);
+
mvm->fast_resume = true;
set_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
@@ -3295,6 +3297,8 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm)
mvm->trans->state = IWL_TRANS_NO_FW;
}
+ iwl_mvm_resume_tcm(mvm);
+
out:
clear_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
mvm->fast_resume = false;
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 86737076101d4a..112e48970338fa 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -4301,6 +4301,18 @@ skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer)
skb_headlen(skb), buffer);
}
+/* Variant of skb_header_pointer() where @offset is user-controlled
+ * and potentially negative.
+ */
+static inline void * __must_check
+skb_header_pointer_careful(const struct sk_buff *skb, int offset,
+ int len, void *buffer)
+{
+ if (unlikely(offset < 0 && -offset > skb_headroom(skb)))
+ return NULL;
+ return skb_header_pointer(skb, offset, len, buffer);
+}
+
static inline void * __must_check
skb_pointer_if_linear(const struct sk_buff *skb, int offset, int len)
{
diff --git a/net/core/filter.c b/net/core/filter.c
index bcd73d9bd76491..029e560e32ce3e 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2289,12 +2289,12 @@ static int __bpf_redirect_neigh_v6(struct sk_buff *skb, struct net_device *dev,
err = bpf_out_neigh_v6(net, skb, dev, nh);
if (unlikely(net_xmit_eval(err)))
- DEV_STATS_INC(dev, tx_errors);
+ dev_core_stats_tx_dropped_inc(dev);
else
ret = NET_XMIT_SUCCESS;
goto out_xmit;
out_drop:
- DEV_STATS_INC(dev, tx_errors);
+ dev_core_stats_tx_dropped_inc(dev);
kfree_skb(skb);
out_xmit:
return ret;
@@ -2396,12 +2396,12 @@ static int __bpf_redirect_neigh_v4(struct sk_buff *skb, struct net_device *dev,
err = bpf_out_neigh_v4(net, skb, dev, nh);
if (unlikely(net_xmit_eval(err)))
- DEV_STATS_INC(dev, tx_errors);
+ dev_core_stats_tx_dropped_inc(dev);
else
ret = NET_XMIT_SUCCESS;
goto out_xmit;
out_drop:
- DEV_STATS_INC(dev, tx_errors);
+ dev_core_stats_tx_dropped_inc(dev);
kfree_skb(skb);
out_xmit:
return ret;
diff --git a/net/core/gro.c b/net/core/gro.c
index 76f9c371242210..482fa7d7f5981a 100644
--- a/net/core/gro.c
+++ b/net/core/gro.c
@@ -265,6 +265,8 @@ static void gro_complete(struct gro_node *gro, struct sk_buff *skb)
goto out;
}
+ /* NICs can feed encapsulated packets into GRO */
+ skb->encapsulation = 0;
rcu_read_lock();
list_for_each_entry_rcu(ptype, head, list) {
if (ptype->type != type || !ptype->callbacks.gro_complete)
diff --git a/net/core/link_watch.c b/net/core/link_watch.c
index 212cde35affa7b..25c455c10a01cf 100644
--- a/net/core/link_watch.c
+++ b/net/core/link_watch.c
@@ -185,10 +185,6 @@ static void linkwatch_do_dev(struct net_device *dev)
netif_state_change(dev);
}
- /* Note: our callers are responsible for calling netdev_tracker_free().
- * This is the reason we use __dev_put() instead of dev_put().
- */
- __dev_put(dev);
}
static void __linkwatch_run_queue(int urgent_only)
@@ -243,6 +239,11 @@ static void __linkwatch_run_queue(int urgent_only)
netdev_lock_ops(dev);
linkwatch_do_dev(dev);
netdev_unlock_ops(dev);
+ /* Use __dev_put() because netdev_tracker_free() was already
+ * called above. Must be after netdev_unlock_ops() to prevent
+ * netdev_run_todo() from freeing the device while still in use.
+ */
+ __dev_put(dev);
do_dev--;
spin_lock_irq(&lweventlist_lock);
}
@@ -278,8 +279,13 @@ void __linkwatch_sync_dev(struct net_device *dev)
{
netdev_ops_assert_locked(dev);
- if (linkwatch_clean_dev(dev))
+ if (linkwatch_clean_dev(dev)) {
linkwatch_do_dev(dev);
+ /* Use __dev_put() because netdev_tracker_free() was already
+ * called inside linkwatch_clean_dev().
+ */
+ __dev_put(dev);
+ }
}
void linkwatch_sync_dev(struct net_device *dev)
@@ -288,6 +294,10 @@ void linkwatch_sync_dev(struct net_device *dev)
netdev_lock_ops(dev);
linkwatch_do_dev(dev);
netdev_unlock_ops(dev);
+ /* Use __dev_put() because netdev_tracker_free() was already
+ * called inside linkwatch_clean_dev().
+ */
+ __dev_put(dev);
}
}
diff --git a/net/core/net-procfs.c b/net/core/net-procfs.c
index 70e0e9a3b650c0..7dbfa6109f0b87 100644
--- a/net/core/net-procfs.c
+++ b/net/core/net-procfs.c
@@ -170,8 +170,14 @@ static const struct seq_operations softnet_seq_ops = {
.show = softnet_seq_show,
};
+struct ptype_iter_state {
+ struct seq_net_private p;
+ struct net_device *dev;
+};
+
static void *ptype_get_idx(struct seq_file *seq, loff_t pos)
{
+ struct ptype_iter_state *iter = seq->private;
struct list_head *ptype_list = NULL;
struct packet_type *pt = NULL;
struct net_device *dev;
@@ -181,12 +187,16 @@ static void *ptype_get_idx(struct seq_file *seq, loff_t pos)
for_each_netdev_rcu(seq_file_net(seq), dev) {
ptype_list = &dev->ptype_all;
list_for_each_entry_rcu(pt, ptype_list, list) {
- if (i == pos)
+ if (i == pos) {
+ iter->dev = dev;
return pt;
+ }
++i;
}
}
+ iter->dev = NULL;
+
list_for_each_entry_rcu(pt, &seq_file_net(seq)->ptype_all, list) {
if (i == pos)
return pt;
@@ -218,6 +228,7 @@ static void *ptype_seq_start(struct seq_file *seq, loff_t *pos)
static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
+ struct ptype_iter_state *iter = seq->private;
struct net *net = seq_file_net(seq);
struct net_device *dev;
struct packet_type *pt;
@@ -229,19 +240,21 @@ static void *ptype_seq_next(struct seq_file *seq, void *v, loff_t *pos)
return ptype_get_idx(seq, 0);
pt = v;
- nxt = pt->list.next;
- if (pt->dev) {
- if (nxt != &pt->dev->ptype_all)
+ nxt = READ_ONCE(pt->list.next);
+ dev = iter->dev;
+ if (dev) {
+ if (nxt != &dev->ptype_all)
goto found;
- dev = pt->dev;
for_each_netdev_continue_rcu(seq_file_net(seq), dev) {
- if (!list_empty(&dev->ptype_all)) {
- nxt = dev->ptype_all.next;
+ nxt = READ_ONCE(dev->ptype_all.next);
+ if (nxt != &dev->ptype_all) {
+ iter->dev = dev;
goto found;
}
}
- nxt = net->ptype_all.next;
+ iter->dev = NULL;
+ nxt = READ_ONCE(net->ptype_all.next);
goto net_ptype_all;
}
@@ -252,20 +265,20 @@ net_ptype_all:
if (nxt == &net->ptype_all) {
/* continue with ->ptype_specific if it's not empty */
- nxt = net->ptype_specific.next;
+ nxt = READ_ONCE(net->ptype_specific.next);
if (nxt != &net->ptype_specific)
goto found;
}
hash = 0;
- nxt = ptype_base[0].next;
+ nxt = READ_ONCE(ptype_base[0].next);
} else
hash = ntohs(pt->type) & PTYPE_HASH_MASK;
while (nxt == &ptype_base[hash]) {
if (++hash >= PTYPE_HASH_SIZE)
return NULL;
- nxt = ptype_base[hash].next;
+ nxt = READ_ONCE(ptype_base[hash].next);
}
found:
return list_entry(nxt, struct packet_type, list);
@@ -279,19 +292,24 @@ static void ptype_seq_stop(struct seq_file *seq, void *v)
static int ptype_seq_show(struct seq_file *seq, void *v)
{
+ struct ptype_iter_state *iter = seq->private;
struct packet_type *pt = v;
+ struct net_device *dev;
- if (v == SEQ_START_TOKEN)
+ if (v == SEQ_START_TOKEN) {
seq_puts(seq, "Type Device Function\n");
- else if ((!pt->af_packet_net || net_eq(pt->af_packet_net, seq_file_net(seq))) &&
- (!pt->dev || net_eq(dev_net(pt->dev), seq_file_net(seq)))) {
+ return 0;
+ }
+ dev = iter->dev;
+ if ((!pt->af_packet_net || net_eq(pt->af_packet_net, seq_file_net(seq))) &&
+ (!dev || net_eq(dev_net(dev), seq_file_net(seq)))) {
if (pt->type == htons(ETH_P_ALL))
seq_puts(seq, "ALL ");
else
seq_printf(seq, "%04x", ntohs(pt->type));
seq_printf(seq, " %-8s %ps\n",
- pt->dev ? pt->dev->name : "", pt->func);
+ dev ? dev->name : "", pt->func);
}
return 0;
@@ -315,7 +333,7 @@ static int __net_init dev_proc_net_init(struct net *net)
&softnet_seq_ops))
goto out_dev;
if (!proc_create_net("ptype", 0444, net->proc_net, &ptype_seq_ops,
- sizeof(struct seq_net_private)))
+ sizeof(struct ptype_iter_state)))
goto out_softnet;
if (wext_proc_init(net))
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index 369c05cf8163c1..d47a279eb8b999 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -862,9 +862,6 @@ ethtool_rxfh_ctx_alloc(const struct ethtool_ops *ops,
ctx->key_off = key_off;
ctx->priv_size = ops->rxfh_priv_size;
- ctx->hfunc = ETH_RSS_HASH_NO_CHANGE;
- ctx->input_xfrm = RXH_XFRM_NO_CHANGE;
-
return ctx;
}
diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c
index 4dced53be4b3bb..da5934cceb0757 100644
--- a/net/ethtool/rss.c
+++ b/net/ethtool/rss.c
@@ -824,8 +824,8 @@ rss_set_ctx_update(struct ethtool_rxfh_context *ctx, struct nlattr **tb,
static int
ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
{
- bool indir_reset = false, indir_mod, xfrm_sym = false;
struct rss_req_info *request = RSS_REQINFO(req_info);
+ bool indir_reset = false, indir_mod, xfrm_sym;
struct ethtool_rxfh_context *ctx = NULL;
struct net_device *dev = req_info->dev;
bool mod = false, fields_mod = false;
@@ -860,12 +860,7 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
rxfh.input_xfrm = data.input_xfrm;
ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod);
- /* For drivers which don't support input_xfrm it will be set to 0xff
- * in the RSS context info. In all other case input_xfrm != 0 means
- * symmetric hashing is requested.
- */
- if (!request->rss_context || ops->rxfh_per_ctx_key)
- xfrm_sym = rxfh.input_xfrm || data.input_xfrm;
+ xfrm_sym = rxfh.input_xfrm || data.input_xfrm;
if (rxfh.input_xfrm == data.input_xfrm)
rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 2111af022d946d..c6439e30e892af 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -1138,7 +1138,8 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
fib6_set_expires(iter, rt->expires);
fib6_add_gc_list(iter);
}
- if (!(rt->fib6_flags & (RTF_ADDRCONF | RTF_PREFIX_RT))) {
+ if (!(rt->fib6_flags & (RTF_ADDRCONF | RTF_PREFIX_RT)) &&
+ !iter->fib6_nh->fib_nh_gw_family) {
iter->fib6_flags &= ~RTF_ADDRCONF;
iter->fib6_flags &= ~RTF_PREFIX_RT;
}
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 729a92781a1a47..be92750e2af3ad 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -5914,7 +5914,7 @@ static void nft_map_catchall_activate(const struct nft_ctx *ctx,
list_for_each_entry(catchall, &set->catchall_list, list) {
ext = nft_set_elem_ext(set, catchall->elem);
- if (!nft_set_elem_active(ext, genmask))
+ if (nft_set_elem_active(ext, genmask))
continue;
nft_clear(ctx->net, ext);
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index 2a1c00048fd6f4..58e849c0acf412 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -161,10 +161,8 @@ next_knode:
int toff = off + key->off + (off2 & key->offmask);
__be32 *data, hdata;
- if (skb_headroom(skb) + toff > INT_MAX)
- goto out;
-
- data = skb_header_pointer(skb, toff, 4, &hdata);
+ data = skb_header_pointer_careful(skb, toff, 4,
+ &hdata);
if (!data)
goto out;
if ((*data ^ key->val) & key->mask) {
@@ -214,8 +212,9 @@ check_terminal:
if (ht->divisor) {
__be32 *data, hdata;
- data = skb_header_pointer(skb, off + n->sel.hoff, 4,
- &hdata);
+ data = skb_header_pointer_careful(skb,
+ off + n->sel.hoff,
+ 4, &hdata);
if (!data)
goto out;
sel = ht->divisor & u32_hash_fold(*data, &n->sel,
@@ -229,7 +228,7 @@ check_terminal:
if (n->sel.flags & TC_U32_VAROFFSET) {
__be16 *data, hdata;
- data = skb_header_pointer(skb,
+ data = skb_header_pointer_careful(skb,
off + n->sel.offoff,
2, &hdata);
if (!data)
diff --git a/net/tipc/crypto.c b/net/tipc/crypto.c
index 751904f10aab00..970db62bd029b2 100644
--- a/net/tipc/crypto.c
+++ b/net/tipc/crypto.c
@@ -1219,7 +1219,7 @@ void tipc_crypto_key_flush(struct tipc_crypto *c)
rx = c;
tx = tipc_net(rx->net)->crypto_tx;
if (cancel_delayed_work(&rx->work)) {
- kfree(rx->skey);
+ kfree_sensitive(rx->skey);
rx->skey = NULL;
atomic_xchg(&rx->key_distr, 0);
tipc_node_put(rx->node);
@@ -2394,7 +2394,7 @@ static void tipc_crypto_work_rx(struct work_struct *work)
break;
default:
synchronize_rcu();
- kfree(rx->skey);
+ kfree_sensitive(rx->skey);
rx->skey = NULL;
break;
}
diff --git a/tools/testing/selftests/net/udpgro_fwd.sh b/tools/testing/selftests/net/udpgro_fwd.sh
index a39fdc4aa2ffb3..9b722c1e4b0fe9 100755
--- a/tools/testing/selftests/net/udpgro_fwd.sh
+++ b/tools/testing/selftests/net/udpgro_fwd.sh
@@ -162,6 +162,39 @@ run_test() {
echo " ok"
}
+run_test_csum() {
+ local -r msg="$1"
+ local -r dst="$2"
+ local csum_error_filter=UdpInCsumErrors
+ local csum_errors
+
+ printf "%-40s" "$msg"
+
+ is_ipv6 "$dst" && csum_error_filter=Udp6InCsumErrors
+
+ ip netns exec "$NS_DST" iperf3 -s -1 >/dev/null &
+ wait_local_port_listen "$NS_DST" 5201 tcp
+ local spid="$!"
+ ip netns exec "$NS_SRC" iperf3 -c "$dst" -t 2 >/dev/null
+ local retc="$?"
+ wait "$spid"
+ local rets="$?"
+ if [ "$rets" -ne 0 ] || [ "$retc" -ne 0 ]; then
+ echo " fail client exit code $retc, server $rets"
+ ret=1
+ return
+ fi
+
+ csum_errors=$(ip netns exec "$NS_DST" nstat -as "$csum_error_filter" |
+ grep "$csum_error_filter" | awk '{print $2}')
+ if [ -n "$csum_errors" ] && [ "$csum_errors" -gt 0 ]; then
+ echo " fail - csum error on receive $csum_errors, expected 0"
+ ret=1
+ return
+ fi
+ echo " ok"
+}
+
run_bench() {
local -r msg=$1
local -r dst=$2
@@ -260,6 +293,37 @@ for family in 4 6; do
ip netns exec $NS_SRC $PING -q -c 1 $OL_NET$DST_NAT >/dev/null
run_test "GRO fwd over UDP tunnel" $OL_NET$DST_NAT 10 10 $OL_NET$DST
cleanup
+
+ # force segmentation and re-aggregation
+ create_vxlan_pair
+ ip netns exec "$NS_DST" ethtool -K veth"$DST" generic-receive-offload on
+ ip netns exec "$NS_SRC" ethtool -K veth"$SRC" tso off
+ ip -n "$NS_SRC" link set dev veth"$SRC" mtu 1430
+
+ # forward to a 2nd veth pair
+ ip -n "$NS_DST" link add br0 type bridge
+ ip -n "$NS_DST" link set dev veth"$DST" master br0
+
+ # segment the aggregated TSO packet, without csum offload
+ ip -n "$NS_DST" link add veth_segment type veth peer veth_rx
+ for FEATURE in tso tx-udp-segmentation tx-checksumming; do
+ ip netns exec "$NS_DST" ethtool -K veth_segment "$FEATURE" off
+ done
+ ip -n "$NS_DST" link set dev veth_segment master br0 up
+ ip -n "$NS_DST" link set dev br0 up
+ ip -n "$NS_DST" link set dev veth_rx up
+
+ # move the lower layer IP in the last added veth
+ for ADDR in "$BM_NET_V4$DST/24" "$BM_NET_V6$DST/64"; do
+ # the dad argument will let iproute emit a unharmful warning
+ # with ipv4 addresses
+ ip -n "$NS_DST" addr del dev veth"$DST" "$ADDR"
+ ip -n "$NS_DST" addr add dev veth_rx "$ADDR" \
+ nodad 2>/dev/null
+ done
+
+ run_test_csum "GSO after GRO" "$OL_NET$DST"
+ cleanup
done
exit $ret