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