summaryrefslogtreecommitdiff
path: root/drivers/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/bonding/bond_3ad.c18
-rw-r--r--drivers/net/bonding/bond_main.c10
-rw-r--r--drivers/net/bonding/bond_netlink.c4
-rw-r--r--drivers/net/bonding/bond_procfs.c8
-rw-r--r--drivers/net/dsa/mt7530.c235
-rw-r--r--drivers/net/dsa/mt7530.h8
-rw-r--r--drivers/net/ethernet/3com/3c509.c1543
-rw-r--r--drivers/net/ethernet/3com/Kconfig14
-rw-r--r--drivers/net/ethernet/3com/Makefile1
-rw-r--r--drivers/net/ethernet/airoha/airoha_eth.c18
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_com.c7
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_phc.c5
-rw-r--r--drivers/net/ethernet/amd/pcnet32.c4
-rw-r--r--drivers/net/ethernet/amd/pds_core/debugfs.c7
-rw-r--r--drivers/net/ethernet/amd/pds_core/dev.c11
-rw-r--r--drivers/net/ethernet/amd/pds_core/devlink.c6
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe.h4
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_hw.c4
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c2
-rw-r--r--drivers/net/ethernet/atheros/ag71xx.c3
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.c52
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c29
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c10
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c9
-rw-r--r--drivers/net/ethernet/cirrus/cs89x0.c2
-rw-r--r--drivers/net/ethernet/cortina/gemini.c21
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc.h1
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_hw.h15
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_msg.c106
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_pf.c75
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_pf.h1
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_vf.c42
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c3
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c3
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c6
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e.h1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c2
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ptp.c3
-rw-r--r--drivers/net/ethernet/intel/ice/ice_dcb_lib.c4
-rw-r--r--drivers/net/ethernet/intel/ice/ice_dpll.c5
-rw-r--r--drivers/net/ethernet/intel/ice/ice_dpll.h32
-rw-r--r--drivers/net/ethernet/intel/ice/ice_main.c10
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ptp_hw.c33
-rw-r--r--drivers/net/ethernet/intel/ice/virt/queues.c2
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_idc.c6
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_ptp.c4
-rw-r--r--drivers/net/ethernet/intel/igc/igc_tsn.c9
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c1
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cgx.c7
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c33
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c374
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h24
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu.c2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu.h2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c3
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c269
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c32
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c8
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/rep.c3
-rw-r--r--drivers/net/ethernet/mediatek/mtk_eth_soc.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/srq.c13
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cmd.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c7
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c36
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_main.c30
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.c21
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c114
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c4
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_netdev.c3
-rw-r--r--drivers/net/ethernet/microchip/lan743x_main.c32
-rw-r--r--drivers/net/ethernet/microchip/lan743x_main.h1
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_main.c8
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_main.h10
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_port.c3
-rw-r--r--drivers/net/ethernet/microsoft/mana/gdma_main.c40
-rw-r--r--drivers/net/ethernet/microsoft/mana/hw_channel.c29
-rw-r--r--drivers/net/ethernet/microsoft/mana/mana_en.c86
-rw-r--r--drivers/net/ethernet/microsoft/mana/shm_channel.c5
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_cxt.c2
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_dev.c7
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c8
-rw-r--r--drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h1
-rw-r--r--drivers/net/ethernet/realtek/rtase/rtase_main.c7
-rw-r--r--drivers/net/ethernet/renesas/ravb_main.c9
-rw-r--r--drivers/net/ethernet/renesas/rtsn.c6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c126
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-nuvoton.c2
-rw-r--r--drivers/net/ethernet/ti/icssm/icssm_prueth.c1
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_hw.c7
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_vf_common.c4
-rw-r--r--drivers/net/fddi/defza.c11
-rw-r--r--drivers/net/geneve.c2
-rw-r--r--drivers/net/ifb.c11
-rw-r--r--drivers/net/macsec.c42
-rw-r--r--drivers/net/net_failover.c12
-rw-r--r--drivers/net/netdevsim/netdev.c3
-rw-r--r--drivers/net/netdevsim/netdevsim.h4
-rw-r--r--drivers/net/netdevsim/psp.c65
-rw-r--r--drivers/net/ovpn/io.c19
-rw-r--r--drivers/net/ovpn/main.c12
-rw-r--r--drivers/net/ovpn/netlink.c8
-rw-r--r--drivers/net/ovpn/peer.c23
-rw-r--r--drivers/net/ovpn/peer.h1
-rw-r--r--drivers/net/ovpn/stats.h16
-rw-r--r--drivers/net/ovpn/tcp.c19
-rw-r--r--drivers/net/ovpn/udp.c2
-rw-r--r--drivers/net/pcs/pcs-mtk-lynxi.c3
-rw-r--r--drivers/net/phy/air_en8811h.c153
-rw-r--r--drivers/net/phy/bcm-phy-lib.c9
-rw-r--r--drivers/net/phy/bcm-phy-lib.h1
-rw-r--r--drivers/net/phy/bcm7xxx.c14
-rw-r--r--drivers/net/phy/broadcom.c5
-rw-r--r--drivers/net/phy/dp83tc811.c1
-rw-r--r--drivers/net/phy/micrel.c15
-rw-r--r--drivers/net/phy/phy-c45.c8
-rw-r--r--drivers/net/phy/phy_device.c6
-rw-r--r--drivers/net/phy/sfp.c1
-rw-r--r--drivers/net/pse-pd/pse_core.c2
-rw-r--r--drivers/net/tap.c4
-rw-r--r--drivers/net/team/team_core.c45
-rw-r--r--drivers/net/tun.c5
-rw-r--r--drivers/net/usb/asix_devices.c2
-rw-r--r--drivers/net/usb/cdc_ncm.c8
-rw-r--r--drivers/net/usb/r8152.c1
-rw-r--r--drivers/net/veth.c3
-rw-r--r--drivers/net/vxlan/vxlan_core.c4
-rw-r--r--drivers/net/vxlan/vxlan_vnifilter.c5
-rw-r--r--drivers/net/wan/fsl_ucc_hdlc.c16
-rw-r--r--drivers/net/wireguard/send.c20
-rw-r--r--drivers/net/wireless/ath/ath10k/Kconfig1
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c15
-rw-r--r--drivers/net/wireless/ath/ath11k/dp_rx.c9
-rw-r--r--drivers/net/wireless/ath/ath11k/hal.c14
-rw-r--r--drivers/net/wireless/ath/ath11k/hal_rx.c5
-rw-r--r--drivers/net/wireless/ath/ath11k/testmode.c1
-rw-r--r--drivers/net/wireless/ath/ath11k/wmi.c131
-rw-r--r--drivers/net/wireless/ath/ath12k/core.c77
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_rx.c5
-rw-r--r--drivers/net/wireless/ath/ath12k/mac.c10
-rw-r--r--drivers/net/wireless/ath/ath12k/p2p.c2
-rw-r--r--drivers/net/wireless/ath/ath12k/wmi.c105
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c3
-rw-r--r--drivers/net/wireless/broadcom/b43/xmit.c3
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/xmit.c3
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/ap.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/constants.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/d3.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/link.c13
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mac80211.c7
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/power.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/tx.c15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw.c26
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c27
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ops.c6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/utils.c14
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/drv.c56
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c6
-rw-r--r--drivers/net/wireless/marvell/libertas/if_usb.c6
-rw-r--r--drivers/net/wireless/microchip/wilc1000/wlan.c2
-rw-r--r--drivers/net/wireless/rsi/rsi_common.h5
-rw-r--r--drivers/net/wireless/st/cw1200/pm.c2
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_imem.c2
-rw-r--r--drivers/net/wwan/t7xx/t7xx_modem_ops.c20
-rw-r--r--drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c18
-rw-r--r--drivers/net/wwan/t7xx/t7xx_port_proxy.h2
177 files changed, 4058 insertions, 974 deletions
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index f0aa7d2f2171..985ef66dc333 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -1386,8 +1386,8 @@ static void ad_churn_machine(struct port *port)
{
if (port->sm_vars & AD_PORT_CHURNED) {
port->sm_vars &= ~AD_PORT_CHURNED;
- port->sm_churn_actor_state = AD_CHURN_MONITOR;
- port->sm_churn_partner_state = AD_CHURN_MONITOR;
+ WRITE_ONCE(port->sm_churn_actor_state, AD_CHURN_MONITOR);
+ WRITE_ONCE(port->sm_churn_partner_state, AD_CHURN_MONITOR);
port->sm_churn_actor_timer_counter =
__ad_timer_to_ticks(AD_ACTOR_CHURN_TIMER, 0);
port->sm_churn_partner_timer_counter =
@@ -1398,20 +1398,22 @@ static void ad_churn_machine(struct port *port)
!(--port->sm_churn_actor_timer_counter) &&
port->sm_churn_actor_state == AD_CHURN_MONITOR) {
if (port->actor_oper_port_state & LACP_STATE_SYNCHRONIZATION) {
- port->sm_churn_actor_state = AD_NO_CHURN;
+ WRITE_ONCE(port->sm_churn_actor_state, AD_NO_CHURN);
} else {
- port->churn_actor_count++;
- port->sm_churn_actor_state = AD_CHURN;
+ WRITE_ONCE(port->churn_actor_count,
+ port->churn_actor_count + 1);
+ WRITE_ONCE(port->sm_churn_actor_state, AD_CHURN);
}
}
if (port->sm_churn_partner_timer_counter &&
!(--port->sm_churn_partner_timer_counter) &&
port->sm_churn_partner_state == AD_CHURN_MONITOR) {
if (port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) {
- port->sm_churn_partner_state = AD_NO_CHURN;
+ WRITE_ONCE(port->sm_churn_partner_state, AD_NO_CHURN);
} else {
- port->churn_partner_count++;
- port->sm_churn_partner_state = AD_CHURN;
+ WRITE_ONCE(port->churn_partner_count,
+ port->churn_partner_count + 1);
+ WRITE_ONCE(port->sm_churn_partner_state, AD_CHURN);
}
}
}
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index af82a3df2c5d..8e75453ce0ef 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1890,6 +1890,12 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
struct sockaddr_storage ss;
int res = 0, i;
+ if (slave_dev->type == ARPHRD_CAN) {
+ BOND_NL_ERR(bond_dev, extack,
+ "CAN devices cannot be enslaved");
+ return -EPERM;
+ }
+
if (slave_dev->flags & IFF_MASTER &&
!netif_is_bond_master(slave_dev)) {
BOND_NL_ERR(bond_dev, extack,
@@ -4615,11 +4621,11 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd
slave_dev = __dev_get_by_name(net, ifr->ifr_slave);
- slave_dbg(bond_dev, slave_dev, "slave_dev=%p:\n", slave_dev);
-
if (!slave_dev)
return -ENODEV;
+ slave_dbg(bond_dev, slave_dev, "slave_dev=%p:\n", slave_dev);
+
switch (cmd) {
case SIOCBONDENSLAVE:
res = bond_enslave(bond_dev, slave_dev, NULL);
diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
index c7d3e0602c83..90365d3f7ebf 100644
--- a/drivers/net/bonding/bond_netlink.c
+++ b/drivers/net/bonding/bond_netlink.c
@@ -82,10 +82,10 @@ static int bond_fill_slave_info(struct sk_buff *skb,
goto nla_put_failure_rcu;
if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_CHURN_ACTOR_STATE,
- ad_port->sm_churn_actor_state))
+ READ_ONCE(ad_port->sm_churn_actor_state)))
goto nla_put_failure_rcu;
if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_CHURN_PARTNER_STATE,
- ad_port->sm_churn_partner_state))
+ READ_ONCE(ad_port->sm_churn_partner_state)))
goto nla_put_failure_rcu;
}
rcu_read_unlock();
diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c
index 3714aab1a3d9..3607b62f9b63 100644
--- a/drivers/net/bonding/bond_procfs.c
+++ b/drivers/net/bonding/bond_procfs.c
@@ -221,13 +221,13 @@ static void bond_info_show_slave(struct seq_file *seq,
seq_printf(seq, "Aggregator ID: %d\n",
agg->aggregator_identifier);
seq_printf(seq, "Actor Churn State: %s\n",
- bond_3ad_churn_desc(port->sm_churn_actor_state));
+ bond_3ad_churn_desc(READ_ONCE(port->sm_churn_actor_state)));
seq_printf(seq, "Partner Churn State: %s\n",
- bond_3ad_churn_desc(port->sm_churn_partner_state));
+ bond_3ad_churn_desc(READ_ONCE(port->sm_churn_partner_state)));
seq_printf(seq, "Actor Churned Count: %d\n",
- port->churn_actor_count);
+ READ_ONCE(port->churn_actor_count));
seq_printf(seq, "Partner Churned Count: %d\n",
- port->churn_partner_count);
+ READ_ONCE(port->churn_partner_count));
if (capable(CAP_NET_ADMIN)) {
seq_puts(seq, "details actor lacp pdu:\n");
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
index b9423389c2ef..3c2a3029b10c 100644
--- a/drivers/net/dsa/mt7530.c
+++ b/drivers/net/dsa/mt7530.c
@@ -25,6 +25,9 @@
#include "mt7530.h"
+#define MT7530_STATS_POLL_INTERVAL (1 * HZ)
+#define MT7530_STATS_RATE_LIMIT (HZ / 10)
+
static struct mt753x_pcs *pcs_to_mt753x_pcs(struct phylink_pcs *pcs)
{
return container_of(pcs, struct mt753x_pcs, pcs);
@@ -906,10 +909,9 @@ static void mt7530_get_rmon_stats(struct dsa_switch *ds, int port,
*ranges = mt7530_rmon_ranges;
}
-static void mt7530_get_stats64(struct dsa_switch *ds, int port,
- struct rtnl_link_stats64 *storage)
+static void mt7530_read_port_stats64(struct mt7530_priv *priv, int port,
+ struct rtnl_link_stats64 *storage)
{
- struct mt7530_priv *priv = ds->priv;
uint64_t data;
/* MIB counter doesn't provide a FramesTransmittedOK but instead
@@ -951,6 +953,54 @@ static void mt7530_get_stats64(struct dsa_switch *ds, int port,
&storage->rx_crc_errors);
}
+static void mt7530_stats_refresh(struct mt7530_priv *priv)
+{
+ struct rtnl_link_stats64 stats = {};
+ struct dsa_port *dp;
+ int port;
+
+ dsa_switch_for_each_user_port(dp, priv->ds) {
+ port = dp->index;
+
+ mt7530_read_port_stats64(priv, port, &stats);
+
+ spin_lock_bh(&priv->stats_lock);
+ priv->ports[port].stats = stats;
+ priv->stats_last = jiffies;
+ spin_unlock_bh(&priv->stats_lock);
+ }
+}
+
+static void mt7530_stats_poll(struct work_struct *work)
+{
+ struct mt7530_priv *priv = container_of(work, struct mt7530_priv,
+ stats_work.work);
+
+ mt7530_stats_refresh(priv);
+ schedule_delayed_work(&priv->stats_work,
+ MT7530_STATS_POLL_INTERVAL);
+}
+
+static void mt7530_get_stats64(struct dsa_switch *ds, int port,
+ struct rtnl_link_stats64 *storage)
+{
+ struct mt7530_priv *priv = ds->priv;
+ bool refresh;
+
+ if (priv->bus) {
+ spin_lock_bh(&priv->stats_lock);
+ *storage = priv->ports[port].stats;
+ refresh = time_after(jiffies, priv->stats_last +
+ MT7530_STATS_RATE_LIMIT);
+ spin_unlock_bh(&priv->stats_lock);
+ if (refresh)
+ mod_delayed_work(system_percpu_wq,
+ &priv->stats_work, 0);
+ } else {
+ mt7530_read_port_stats64(priv, port, storage);
+ }
+}
+
static void mt7530_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_ctrl_stats *ctrl_stats)
{
@@ -973,12 +1023,16 @@ mt7530_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
unsigned int age_count;
unsigned int age_unit;
- /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds */
- if (secs < 1 || secs > (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1))
- return -ERANGE;
-
- /* iterate through all possible age_count to find the closest pair */
- for (tmp_age_count = 0; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) {
+ /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds.
+ * The DSA core has already validated the range using
+ * ds->ageing_time_min and ds->ageing_time_max.
+ *
+ * Iterate through all possible age_count values to find the closest
+ * pair. Start from 1 because the per-entry aging counter is
+ * initialized to AGE_CNT and a value of 0 means the entry will
+ * never be aged out.
+ */
+ for (tmp_age_count = 1; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) {
unsigned int tmp_age_unit = secs / (tmp_age_count + 1) - 1;
if (tmp_age_unit <= AGE_UNIT_MAX) {
@@ -1246,37 +1300,40 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface)
static void
mt753x_trap_frames(struct mt7530_priv *priv)
{
- /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress them
- * VLAN-untagged.
+ /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress
+ * them with the EG_TAG attribute set to disabled (system default)
+ * so that any VLAN tags in the frame are not modified by the
+ * switch egress VLAN tag processing. This preserves VLAN tags
+ * for reception on VLAN sub-interfaces.
*/
mt7530_rmw(priv, MT753X_BPC,
PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK |
BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK,
- PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
+ PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_DISABLED) |
PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) |
- BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
+ BPDU_EG_TAG(MT7530_VLAN_EG_DISABLED) |
TO_CPU_FW_CPU_ONLY);
- /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress
- * them VLAN-untagged.
+ /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and
+ * egress them with EG_TAG disabled.
*/
mt7530_rmw(priv, MT753X_RGAC1,
R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK |
R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK,
- R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
+ R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_DISABLED) |
R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR |
- R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
+ R01_EG_TAG(MT7530_VLAN_EG_DISABLED) |
TO_CPU_FW_CPU_ONLY);
- /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress
- * them VLAN-untagged.
+ /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and
+ * egress them with EG_TAG disabled.
*/
mt7530_rmw(priv, MT753X_RGAC2,
R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK |
R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK,
- R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
+ R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_DISABLED) |
R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR |
- R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) |
+ R03_EG_TAG(MT7530_VLAN_EG_DISABLED) |
TO_CPU_FW_CPU_ONLY);
}
@@ -1566,6 +1623,49 @@ mt7530_port_bridge_join(struct dsa_switch *ds, int port,
return 0;
}
+static int
+mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid)
+{
+ struct mt7530_dummy_poll p;
+ u32 val;
+ int ret;
+
+ val = VTCR_BUSY | VTCR_FUNC(cmd) | vid;
+ mt7530_write(priv, MT7530_VTCR, val);
+
+ INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_VTCR);
+ ret = readx_poll_timeout(_mt7530_read, &p, val,
+ !(val & VTCR_BUSY), 20, 20000);
+ if (ret < 0) {
+ dev_err(priv->dev, "poll timeout\n");
+ return ret;
+ }
+
+ val = mt7530_read(priv, MT7530_VTCR);
+ if (val & VTCR_INVALID) {
+ dev_err(priv->dev, "read VTCR invalid\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+mt7530_setup_vlan0(struct mt7530_priv *priv)
+{
+ u32 val;
+
+ /* Validate the entry with independent learning, keep the original
+ * ingress tag attribute.
+ */
+ val = IVL_MAC | EG_CON | PORT_MEM(MT7530_ALL_MEMBERS) | FID(FID_BRIDGED) |
+ VLAN_VALID;
+ mt7530_write(priv, MT7530_VAWD1, val);
+ mt7530_write(priv, MT7530_VAWD2, 0);
+
+ return mt7530_vlan_cmd(priv, MT7530_VTCR_WR_VID, 0);
+}
+
static void
mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)
{
@@ -1591,6 +1691,8 @@ mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)
G0_PORT_VID_DEF);
for (i = 0; i < priv->ds->num_ports; i++) {
+ if (i == port)
+ continue;
if (dsa_is_user_port(ds, i) &&
dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) {
all_user_ports_removed = false;
@@ -1602,13 +1704,9 @@ mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)
* the CPU port get out of VLAN filtering mode.
*/
if (all_user_ports_removed) {
- struct dsa_port *dp = dsa_to_port(ds, port);
- struct dsa_port *cpu_dp = dp->cpu_dp;
-
- mt7530_write(priv, MT7530_PCR_P(cpu_dp->index),
- PCR_MATRIX(dsa_user_ports(priv->ds)));
- mt7530_write(priv, MT7530_PVC_P(cpu_dp->index), PORT_SPEC_TAG
- | PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
+ mutex_lock(&priv->reg_mutex);
+ mt7530_setup_vlan0(priv);
+ mutex_unlock(&priv->reg_mutex);
}
}
@@ -1797,33 +1895,6 @@ mt7530_port_mdb_del(struct dsa_switch *ds, int port,
}
static int
-mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid)
-{
- struct mt7530_dummy_poll p;
- u32 val;
- int ret;
-
- val = VTCR_BUSY | VTCR_FUNC(cmd) | vid;
- mt7530_write(priv, MT7530_VTCR, val);
-
- INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_VTCR);
- ret = readx_poll_timeout(_mt7530_read, &p, val,
- !(val & VTCR_BUSY), 20, 20000);
- if (ret < 0) {
- dev_err(priv->dev, "poll timeout\n");
- return ret;
- }
-
- val = mt7530_read(priv, MT7530_VTCR);
- if (val & VTCR_INVALID) {
- dev_err(priv->dev, "read VTCR invalid\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int
mt7530_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
struct netlink_ext_ack *extack)
{
@@ -1928,21 +1999,6 @@ mt7530_hw_vlan_update(struct mt7530_priv *priv, u16 vid,
}
static int
-mt7530_setup_vlan0(struct mt7530_priv *priv)
-{
- u32 val;
-
- /* Validate the entry with independent learning, keep the original
- * ingress tag attribute.
- */
- val = IVL_MAC | EG_CON | PORT_MEM(MT7530_ALL_MEMBERS) | FID(FID_BRIDGED) |
- VLAN_VALID;
- mt7530_write(priv, MT7530_VAWD1, val);
-
- return mt7530_vlan_cmd(priv, MT7530_VTCR_WR_VID, 0);
-}
-
-static int
mt7530_port_vlan_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct netlink_ext_ack *extack)
@@ -1954,9 +2010,18 @@ mt7530_port_vlan_add(struct dsa_switch *ds, int port,
mutex_lock(&priv->reg_mutex);
+ /* VID 0 is managed exclusively by mt7530_setup_vlan0() for
+ * VLAN-unaware bridge operation. Don't let the bridge overwrite
+ * its EG_CON flag with VTAG_EN and corrupt PORT_MEM.
+ */
+ if (vlan->vid == 0)
+ goto skip_vlan_table;
+
mt7530_hw_vlan_entry_init(&new_entry, port, untagged);
mt7530_hw_vlan_update(priv, vlan->vid, &new_entry, mt7530_hw_vlan_add);
+skip_vlan_table:
+
if (pvid) {
priv->ports[port].pvid = vlan->vid;
@@ -1996,10 +2061,15 @@ mt7530_port_vlan_del(struct dsa_switch *ds, int port,
mutex_lock(&priv->reg_mutex);
+ /* VID 0 is managed exclusively by mt7530_setup_vlan0(). */
+ if (vlan->vid == 0)
+ goto skip_vlan_table;
+
mt7530_hw_vlan_entry_init(&target_entry, port, 0);
mt7530_hw_vlan_update(priv, vlan->vid, &target_entry,
mt7530_hw_vlan_del);
+skip_vlan_table:
/* PVID is being restored to the default whenever the PVID port
* is being removed from the VLAN.
*/
@@ -2377,7 +2447,10 @@ mt7530_setup(struct dsa_switch *ds)
}
ds->assisted_learning_on_cpu_port = true;
+ ds->untag_vlan_aware_bridge_pvid = true;
ds->mtu_enforcement_ingress = true;
+ ds->ageing_time_min = 2 * 1000;
+ ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000;
if (priv->id == ID_MT7530) {
regulator_set_voltage(priv->core_pwr, 1000000, 1000000);
@@ -2566,7 +2639,10 @@ mt7531_setup_common(struct dsa_switch *ds)
int ret, i;
ds->assisted_learning_on_cpu_port = true;
+ ds->untag_vlan_aware_bridge_pvid = true;
ds->mtu_enforcement_ingress = true;
+ ds->ageing_time_min = 2 * 1000;
+ ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000;
mt753x_trap_frames(priv);
@@ -3137,9 +3213,24 @@ mt753x_setup(struct dsa_switch *ds)
if (ret && priv->irq_domain)
mt7530_free_mdio_irq(priv);
+ if (!ret && priv->bus) {
+ mt7530_stats_refresh(priv);
+ schedule_delayed_work(&priv->stats_work,
+ MT7530_STATS_POLL_INTERVAL);
+ }
+
return ret;
}
+static void
+mt753x_teardown(struct dsa_switch *ds)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ if (priv->bus)
+ cancel_delayed_work_sync(&priv->stats_work);
+}
+
static int mt753x_set_mac_eee(struct dsa_switch *ds, int port,
struct ethtool_keee *e)
{
@@ -3257,6 +3348,7 @@ static int mt7988_setup(struct dsa_switch *ds)
static const struct dsa_switch_ops mt7530_switch_ops = {
.get_tag_protocol = mtk_get_tag_protocol,
.setup = mt753x_setup,
+ .teardown = mt753x_teardown,
.preferred_default_local_cpu_port = mt753x_preferred_default_local_cpu_port,
.get_strings = mt7530_get_strings,
.get_ethtool_stats = mt7530_get_ethtool_stats,
@@ -3395,6 +3487,9 @@ mt7530_probe_common(struct mt7530_priv *priv)
priv->ds->ops = &mt7530_switch_ops;
priv->ds->phylink_mac_ops = &mt753x_phylink_mac_ops;
mutex_init(&priv->reg_mutex);
+ spin_lock_init(&priv->stats_lock);
+ INIT_DELAYED_WORK(&priv->stats_work, mt7530_stats_poll);
+
dev_set_drvdata(dev, priv);
return 0;
diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h
index 3e0090bed298..dd33b0df3419 100644
--- a/drivers/net/dsa/mt7530.h
+++ b/drivers/net/dsa/mt7530.h
@@ -796,6 +796,7 @@ struct mt7530_fdb {
* @pvid: The VLAN specified is to be considered a PVID at ingress. Any
* untagged frames will be assigned to the related VLAN.
* @sgmii_pcs: Pointer to PCS instance for SerDes ports
+ * @stats: Cached port statistics for MDIO-connected switches
*/
struct mt7530_port {
bool enable;
@@ -803,6 +804,7 @@ struct mt7530_port {
u32 pm;
u16 pvid;
struct phylink_pcs *sgmii_pcs;
+ struct rtnl_link_stats64 stats;
};
/* Port 5 mode definitions of the MT7530 switch */
@@ -875,6 +877,9 @@ struct mt753x_info {
* @create_sgmii: Pointer to function creating SGMII PCS instance(s)
* @active_cpu_ports: Holding the active CPU ports
* @mdiodev: The pointer to the MDIO device structure
+ * @stats_lock: Protects cached per-port stats from concurrent access
+ * @stats_work: Delayed work for polling MIB counters on MDIO switches
+ * @stats_last: Jiffies timestamp of last MIB counter poll
*/
struct mt7530_priv {
struct device *dev;
@@ -900,6 +905,9 @@ struct mt7530_priv {
int (*create_sgmii)(struct mt7530_priv *priv);
u8 active_cpu_ports;
struct mdio_device *mdiodev;
+ spinlock_t stats_lock; /* protects cached stats counters */
+ struct delayed_work stats_work;
+ unsigned long stats_last;
};
struct mt7530_hw_vlan_entry {
diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c
new file mode 100644
index 000000000000..f23be7425daf
--- /dev/null
+++ b/drivers/net/ethernet/3com/3c509.c
@@ -0,0 +1,1543 @@
+// SPDX-License-Identifier: GPL-2.0
+/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
+/*
+ * Written 1993-2000 by Donald Becker.
+ *
+ * Copyright 1994-2000 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may be used and
+ * distributed according to the terms of the GNU General Public License,
+ * incorporated herein by reference.
+ *
+ * This driver is for the 3Com EtherLinkIII series.
+ *
+ * The author may be reached as becker@scyld.com, or C/O
+ * Scyld Computing Corporation
+ * 410 Severn Ave., Suite 210
+ * Annapolis MD 21403
+ *
+ * Known limitations:
+ * Because of the way 3c509 ISA detection works it's difficult to predict
+ * a priori which of several ISA-mode cards will be detected first.
+ *
+ * This driver does not use predictive interrupt mode, resulting in higher
+ * packet latency but lower overhead. If interrupts are disabled for an
+ * unusually long time it could also result in missed packets, but in
+ * practice this rarely happens.
+ *
+ *
+ * FIXES:
+ * Alan Cox: Removed the 'Unexpected interrupt' bug.
+ * Michael Meskes: Upgraded to Donald Becker's version 1.07.
+ * Alan Cox: Increased the eeprom delay. Regardless of
+ * what the docs say some people definitely
+ * get problems with lower (but in card spec)
+ * delays.
+ * v1.10 4/21/97 Fixed module code so that multiple cards may be
+ * detected, other cleanups. -djb
+ * Andrea Arcangeli: Upgraded to Donald Becker's version 1.12.
+ * Rick Payne: Fixed SMP race condition.
+ * v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable
+ * variable. -djb
+ * v1.14 10/15/97 Avoided waiting..discard message for fast
+ * machines. -djb
+ * v1.15 1/31/98 Faster recovery for Tx errors. -djb
+ * v1.16 2/3/98 Different ID port handling to avoid sound
+ * cards. -djb
+ * v1.18 12Mar2001 Andrew Morton
+ * - Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz)
+ * - Reviewed against 1.18 from scyld.com
+ * v1.18a 17Nov2001 Jeff Garzik <jgarzik@pobox.com>
+ * - ethtool support.
+ * v1.18b 1Mar2002 Zwane Mwaikambo <zwane@commfireservices.com>
+ * - Power Management support.
+ * v1.18c 1Mar2002 David Ruggiero <jdr@farfalle.com>
+ * - Full duplex support.
+ * v1.19 16Oct2002 Zwane Mwaikambo <zwane@linuxpower.ca>
+ * - Additional ethtool features.
+ * v1.19a 28Oct2002 David Ruggiero <jdr@farfalle.com>
+ * - Increase *read_eeprom udelay to workaround oops with
+ * 2 cards.
+ * v1.19b 08Nov2002 Marc Zyngier <maz@wild-wind.fr.eu.org>
+ * - Introduce driver model for EISA cards.
+ * v1.20 04Feb2008 Ondrej Zary <linux@rainbow-software.org>
+ * - convert to isa_driver and pnp_driver and some
+ * cleanups.
+ */
+
+#define DRV_NAME "3c509"
+
+/* A few values that may be tweaked. */
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (400 * HZ / 1000)
+
+#include <linux/bitops.h>
+#include <linux/delay.h> /* for udelay() */
+#include <linux/device.h>
+#include <linux/eisa.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/in.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/isa.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/pm.h>
+#include <linux/pnp.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+
+#include <asm/irq.h>
+
+#ifdef EL3_DEBUG
+static int el3_debug = EL3_DEBUG;
+#else
+static int el3_debug = 2;
+#endif
+
+/* Used to do a global count of all the cards in the system. Must be
+ * a global variable so that the eisa probe routines can increment it.
+ */
+static int el3_cards;
+#define EL3_MAX_CARDS 8
+
+/* To minimize the size of the driver source I only define operating
+ * constants if they are used several times. You'll need the manual
+ * anyway if you want to understand driver details.
+ */
+/* Offsets from base I/O address. */
+#define EL3_DATA 0x00
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+#define EEPROM_READ 0x80
+
+#define EL3_IO_EXTENT 16
+
+#define EL3WINDOW(win_num) outw(SELECT_WINDOW + (win_num), ioaddr + EL3_CMD)
+
+/* The top five bits written to EL3_CMD are a command, the lower
+ * 11 bits are the parameter, if applicable.
+ */
+enum c509cmd {
+ TOTAL_RESET = 0 << 11,
+ SELECT_WINDOW = 1 << 11,
+ START_COAX = 2 << 11,
+ RX_DISABLE = 3 << 11,
+ RX_ENABLE = 4 << 11,
+ RX_RESET = 5 << 11,
+ RX_DISCARD = 8 << 11,
+ TX_ENABLE = 9 << 11,
+ TX_DISABLE = 10 << 11,
+ TX_RESET = 11 << 11,
+ FAKE_INTR = 12 << 11,
+ ACK_INTR = 13 << 11,
+ SET_INTR_ENB = 14 << 11,
+ SET_STATUS_ENB = 15 << 11,
+ SET_RX_FILTER = 16 << 11,
+ SET_RX_THRESHOLD = 17 << 11,
+ SET_TX_THRESHOLD = 18 << 11,
+ SET_TX_START = 19 << 11,
+ STATS_ENABLE = 21 << 11,
+ STATS_DISABLE = 22 << 11,
+ STOP_COAX = 23 << 11,
+ POWER_UP = 27 << 11,
+ POWER_DOWN = 28 << 11,
+ POWER_AUTO = 29 << 11,
+};
+
+enum c509status {
+ INT_LATCH = 0x0001,
+ ADAPTER_FAILURE = 0x0002,
+ TX_COMPLETE = 0x0004,
+ TX_AVAILABLE = 0x0008,
+ RX_COMPLETE = 0x0010,
+ RX_EARLY = 0x0020,
+ INT_REQ = 0x0040,
+ STATS_FULL = 0x0080,
+ CMD_BUSY = 0x1000,
+};
+
+/* The SET_RX_FILTER command accepts the following classes: */
+enum rx_filter {
+ RX_STATION = 1,
+ RX_MULTICAST = 2,
+ RX_BROADCAST = 4,
+ RX_PROM = 8,
+};
+
+/* Register window 1 offsets, the window used in normal operation. */
+#define TX_FIFO 0x00
+#define RX_FIFO 0x00
+#define RX_STATUS 0x08
+#define TX_STATUS 0x0B
+#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */
+
+#define WN0_CONF_CTRL 0x04 /* Window 0: Configuration control register. */
+#define WN0_ADDR_CONF 0x06 /* Window 0: Address configuration register. */
+#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */
+#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */
+#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
+#define WN4_NETDIAG 0x06 /* Window 4: Net diagnostic. */
+#define FD_ENABLE 0x8000 /* Enable full-duplex ("external loopback"). */
+
+/*
+ * Must be a power of two (we use a binary and in the
+ * circular queue).
+ */
+#define SKB_QUEUE_SIZE 64
+
+enum el3_cardtype { EL3_ISA, EL3_PNP, EL3_EISA };
+
+struct el3_private {
+ /* for device access */
+ spinlock_t lock;
+ /* skb send-queue */
+ int head, size;
+ struct sk_buff *queue[SKB_QUEUE_SIZE];
+ enum el3_cardtype type;
+};
+
+static int id_port;
+static int current_tag;
+static struct net_device *el3_devs[EL3_MAX_CARDS];
+
+/* Parameters that may be passed into the module. */
+static int debug = -1;
+static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 10;
+#ifdef CONFIG_PNP
+static int nopnp;
+#endif
+
+static int el3_common_init(struct net_device *dev);
+static void el3_common_remove(struct net_device *dev);
+static ushort id_read_eeprom(int index);
+static ushort read_eeprom(int ioaddr, int index);
+static int el3_open(struct net_device *dev);
+static netdev_tx_t el3_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t el3_interrupt(int irq, void *dev_id);
+static void update_stats(struct net_device *dev);
+static struct net_device_stats *el3_get_stats(struct net_device *dev);
+static int el3_rx(struct net_device *dev);
+static int el3_close(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue);
+static void el3_down(struct net_device *dev);
+static void el3_up(struct net_device *dev);
+static const struct ethtool_ops ethtool_ops;
+#ifdef CONFIG_PM
+static int el3_suspend(struct device *, pm_message_t);
+static int el3_resume(struct device *);
+#else
+#define el3_suspend NULL
+#define el3_resume NULL
+#endif
+
+/* Generic device remove for all device types. */
+static int el3_device_remove(struct device *device);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void el3_poll_controller(struct net_device *dev);
+#endif
+
+/* Return 0 on success, 1 on error, 2 when found already detected PnP card. */
+static int el3_isa_id_sequence(__be16 *phys_addr)
+{
+ short lrs_state = 0xff;
+ int i;
+
+ /* ISA boards are detected by sending the ID sequence to the
+ * ID_PORT. We find cards past the first by setting the 'current_tag'
+ * on cards as they are found. Cards with their tag set will not
+ * respond to subsequent ID sequences.
+ */
+ outb(0x00, id_port);
+ outb(0x00, id_port);
+ for (i = 0; i < 255; i++) {
+ outb(lrs_state, id_port);
+ lrs_state <<= 1;
+ lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
+ }
+ /* For the first probe, clear all board's tag registers. */
+ if (current_tag == 0)
+ outb(0xd0, id_port);
+ else /* Otherwise kill off already-found boards. */
+ outb(0xd8, id_port);
+ if (id_read_eeprom(7) != 0x6d50)
+ return 1;
+ /* Read in EEPROM data, which does contention-select.
+ * Only the lowest address board will stay "on-line".
+ * 3Com got the byte order backwards.
+ */
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(id_read_eeprom(i));
+#ifdef CONFIG_PNP
+ if (!nopnp) {
+ /* The ISA PnP 3c509 cards respond to the ID sequence too.
+ * This check is needed in order not to register them twice.
+ */
+ for (i = 0; i < el3_cards; i++) {
+ struct el3_private *lp = netdev_priv(el3_devs[i]);
+
+ if (lp->type == EL3_PNP &&
+ ether_addr_equal((u8 *)phys_addr,
+ el3_devs[i]->dev_addr)) {
+ if (el3_debug > 3)
+ pr_debug("3c509 with address %02x %02x %02x %02x %02x %02x was found by ISAPnP\n",
+ phys_addr[0] & 0xff,
+ phys_addr[0] >> 8,
+ phys_addr[1] & 0xff,
+ phys_addr[1] >> 8,
+ phys_addr[2] & 0xff,
+ phys_addr[2] >> 8);
+ /* Set the adaptor tag so that the next card
+ * can be found.
+ */
+ outb(0xd0 + ++current_tag, id_port);
+ return 2;
+ }
+ }
+ }
+#endif /* CONFIG_PNP */
+ return 0;
+}
+
+static void el3_dev_fill(struct net_device *dev, __be16 *phys_addr, int ioaddr,
+ int irq, int if_port, enum el3_cardtype type)
+{
+ struct el3_private *lp = netdev_priv(dev);
+
+ eth_hw_addr_set(dev, (u8 *)phys_addr);
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ dev->if_port = if_port;
+ lp->type = type;
+}
+
+static int el3_isa_match(struct device *pdev, unsigned int ndev)
+{
+ int ioaddr, isa_irq, if_port, err;
+ struct net_device *dev;
+ unsigned int iobase;
+ __be16 phys_addr[3];
+
+ while ((err = el3_isa_id_sequence(phys_addr)) == 2)
+ ; /* Skip to next card when PnP card found */
+ if (err == 1)
+ return 0;
+
+ iobase = id_read_eeprom(8);
+ if_port = iobase >> 14;
+ ioaddr = 0x200 + ((iobase & 0x1f) << 4);
+ if (irq[el3_cards] > 1 && irq[el3_cards] < 16)
+ isa_irq = irq[el3_cards];
+ else
+ isa_irq = id_read_eeprom(9) >> 12;
+
+ dev = alloc_etherdev(sizeof(struct el3_private));
+ if (!dev)
+ return -ENOMEM;
+
+ SET_NETDEV_DEV(dev, pdev);
+
+ if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-isa")) {
+ free_netdev(dev);
+ return 0;
+ }
+
+ /* Set the adaptor tag so that the next card can be found. */
+ outb(0xd0 + ++current_tag, id_port);
+
+ /* Activate the adaptor at the EEPROM location. */
+ outb((ioaddr >> 4) | 0xe0, id_port);
+
+ EL3WINDOW(0);
+ if (inw(ioaddr) != 0x6d50) {
+ free_netdev(dev);
+ return 0;
+ }
+
+ /* Free the interrupt so that some other card can use it. */
+ outw(0x0f00, ioaddr + WN0_IRQ);
+
+ el3_dev_fill(dev, phys_addr, ioaddr, isa_irq, if_port, EL3_ISA);
+ dev_set_drvdata(pdev, dev);
+ if (el3_common_init(dev)) {
+ free_netdev(dev);
+ return 0;
+ }
+
+ el3_devs[el3_cards++] = dev;
+ return 1;
+}
+
+static void el3_isa_remove(struct device *pdev, unsigned int ndev)
+{
+ el3_device_remove(pdev);
+ dev_set_drvdata(pdev, NULL);
+}
+
+#ifdef CONFIG_PM
+static int el3_isa_suspend(struct device *dev, unsigned int n,
+ pm_message_t state)
+{
+ current_tag = 0;
+ return el3_suspend(dev, state);
+}
+
+static int el3_isa_resume(struct device *dev, unsigned int n)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ int ioaddr = ndev->base_addr, err;
+ __be16 phys_addr[3];
+
+ while ((err = el3_isa_id_sequence(phys_addr)) == 2)
+ ; /* Skip to next card when PnP card found */
+ if (err == 1)
+ return 0;
+ /* Set the adaptor tag so that the next card can be found. */
+ outb(0xd0 + ++current_tag, id_port);
+ /* Enable the card */
+ outb((ioaddr >> 4) | 0xe0, id_port);
+ EL3WINDOW(0);
+ if (inw(ioaddr) != 0x6d50)
+ return 1;
+ /* Free the interrupt so that some other card can use it. */
+ outw(0x0f00, ioaddr + WN0_IRQ);
+ return el3_resume(dev);
+}
+#endif
+
+static struct isa_driver el3_isa_driver = {
+ .match = el3_isa_match,
+ .remove = el3_isa_remove,
+#ifdef CONFIG_PM
+ .suspend = el3_isa_suspend,
+ .resume = el3_isa_resume,
+#endif
+ .driver = {
+ .name = "3c509"
+ },
+};
+
+static int isa_registered;
+
+#ifdef CONFIG_PNP
+static const struct pnp_device_id el3_pnp_ids[] = {
+ { .id = "TCM5090" }, /* 3Com Etherlink III (TP) */
+ { .id = "TCM5091" }, /* 3Com Etherlink III */
+ { .id = "TCM5094" }, /* 3Com Etherlink III (combo) */
+ { .id = "TCM5095" }, /* 3Com Etherlink III (TPO) */
+ { .id = "TCM5098" }, /* 3Com Etherlink III (TPC) */
+ { .id = "PNP80f7" }, /* 3Com Etherlink III compatible */
+ { .id = "PNP80f8" }, /* 3Com Etherlink III compatible */
+ { .id = "" }
+};
+MODULE_DEVICE_TABLE(pnp, el3_pnp_ids);
+
+static int el3_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *id)
+{
+ struct net_device *dev = NULL;
+ int ioaddr, irq, if_port;
+ __be16 phys_addr[3];
+ short i;
+ int err;
+
+ ioaddr = pnp_port_start(pdev, 0);
+ if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-pnp"))
+ return -EBUSY;
+ irq = pnp_irq(pdev, 0);
+ EL3WINDOW(0);
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(read_eeprom(ioaddr, i));
+ if_port = read_eeprom(ioaddr, 8) >> 14;
+ dev = alloc_etherdev(sizeof(struct el3_private));
+ if (!dev) {
+ release_region(ioaddr, EL3_IO_EXTENT);
+ return -ENOMEM;
+ }
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_PNP);
+ pnp_set_drvdata(pdev, dev);
+ err = el3_common_init(dev);
+
+ if (err) {
+ pnp_set_drvdata(pdev, NULL);
+ free_netdev(dev);
+ return err;
+ }
+
+ el3_devs[el3_cards++] = dev;
+ return 0;
+}
+
+static void el3_pnp_remove(struct pnp_dev *pdev)
+{
+ el3_common_remove(pnp_get_drvdata(pdev));
+ pnp_set_drvdata(pdev, NULL);
+}
+
+#ifdef CONFIG_PM
+static int el3_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
+{
+ return el3_suspend(&pdev->dev, state);
+}
+
+static int el3_pnp_resume(struct pnp_dev *pdev)
+{
+ return el3_resume(&pdev->dev);
+}
+#endif
+
+static struct pnp_driver el3_pnp_driver = {
+ .name = "3c509",
+ .id_table = el3_pnp_ids,
+ .probe = el3_pnp_probe,
+ .remove = el3_pnp_remove,
+#ifdef CONFIG_PM
+ .suspend = el3_pnp_suspend,
+ .resume = el3_pnp_resume,
+#endif
+};
+
+static int pnp_registered;
+#endif /* CONFIG_PNP */
+
+#ifdef CONFIG_EISA
+static const struct eisa_device_id el3_eisa_ids[] = {
+ { "TCM5090" },
+ { "TCM5091" },
+ { "TCM5092" },
+ { "TCM5093" },
+ { "TCM5094" },
+ { "TCM5095" },
+ { "TCM5098" },
+ { "" }
+};
+MODULE_DEVICE_TABLE(eisa, el3_eisa_ids);
+
+static int el3_eisa_probe(struct device *device);
+
+static struct eisa_driver el3_eisa_driver = {
+ .id_table = el3_eisa_ids,
+ .driver = {
+ .name = "3c579",
+ .probe = el3_eisa_probe,
+ .remove = el3_device_remove,
+ .suspend = el3_suspend,
+ .resume = el3_resume,
+ }
+};
+
+static int eisa_registered;
+#endif
+
+static const struct net_device_ops netdev_ops = {
+ .ndo_open = el3_open,
+ .ndo_stop = el3_close,
+ .ndo_start_xmit = el3_start_xmit,
+ .ndo_get_stats = el3_get_stats,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_tx_timeout = el3_tx_timeout,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = el3_poll_controller,
+#endif
+};
+
+static int el3_common_init(struct net_device *dev)
+{
+ static const char *const if_names[] = {
+ "10baseT", "AUI", "undefined", "BNC"
+ };
+ struct el3_private *lp = netdev_priv(dev);
+ int err;
+
+ spin_lock_init(&lp->lock);
+
+ if (dev->mem_start & 0x05) { /* xcvr codes 1/3/4/12 */
+ dev->if_port = (dev->mem_start & 0x0f);
+ } else { /* xcvr codes 0/8 */
+ /* use eeprom value, but save user's full-duplex selection */
+ dev->if_port |= (dev->mem_start & 0x08);
+ }
+
+ /* The EL3-specific entries in the device structure. */
+ dev->netdev_ops = &netdev_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
+ dev->ethtool_ops = &ethtool_ops;
+
+ err = register_netdev(dev);
+ if (err) {
+ pr_err("Failed to register 3c5x9 at %#3.3lx, IRQ %d.\n",
+ dev->base_addr, dev->irq);
+ release_region(dev->base_addr, EL3_IO_EXTENT);
+ return err;
+ }
+
+ pr_info("%s: 3c5x9 found at %#3.3lx, %s port, address %pM, IRQ %d.\n",
+ dev->name, dev->base_addr, if_names[(dev->if_port & 0x03)],
+ dev->dev_addr, dev->irq);
+
+ return 0;
+}
+
+static void el3_common_remove(struct net_device *dev)
+{
+ unregister_netdev(dev);
+ release_region(dev->base_addr, EL3_IO_EXTENT);
+ free_netdev(dev);
+}
+
+#ifdef CONFIG_EISA
+static int el3_eisa_probe(struct device *device)
+{
+ struct net_device *dev = NULL;
+ struct eisa_device *edev;
+ int ioaddr, irq, if_port;
+ __be16 phys_addr[3];
+ short i;
+ int err;
+
+ /* Yeepee, The driver framework is calling us ! */
+ edev = to_eisa_device(device);
+ ioaddr = edev->base_addr;
+
+ if (!request_region(ioaddr, EL3_IO_EXTENT, "3c579-eisa"))
+ return -EBUSY;
+
+ /* Change the register set to the configuration window 0. */
+ outw(SELECT_WINDOW | 0, ioaddr + 0xC80 + EL3_CMD);
+
+ irq = inw(ioaddr + WN0_IRQ) >> 12;
+ if_port = inw(ioaddr + 6) >> 14;
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(read_eeprom(ioaddr, i));
+
+ /* Restore the "Product ID" to the EEPROM read register. */
+ read_eeprom(ioaddr, 3);
+
+ dev = alloc_etherdev(sizeof(struct el3_private));
+ if (!dev) {
+ release_region(ioaddr, EL3_IO_EXTENT);
+ return -ENOMEM;
+ }
+
+ SET_NETDEV_DEV(dev, device);
+
+ el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_EISA);
+ eisa_set_drvdata(edev, dev);
+ err = el3_common_init(dev);
+
+ if (err) {
+ eisa_set_drvdata(edev, NULL);
+ free_netdev(dev);
+ return err;
+ }
+
+ el3_devs[el3_cards++] = dev;
+ return 0;
+}
+#endif
+
+/* This remove works for all device types.
+ *
+ * The net dev must be stored in the driver data field.
+ */
+static int el3_device_remove(struct device *device)
+{
+ struct net_device *dev;
+
+ dev = dev_get_drvdata(device);
+
+ el3_common_remove(dev);
+ return 0;
+}
+
+/* Read a word from the EEPROM using the regular EEPROM access register.
+ * Assume that we are in register window zero.
+ */
+static ushort read_eeprom(int ioaddr, int index)
+{
+ outw(EEPROM_READ + index, ioaddr + 10);
+ /* Pause for at least 162 us for the read to take place.
+ * Some chips seem to require much longer.
+ */
+ mdelay(2);
+ return inw(ioaddr + 12);
+}
+
+/* Read a word from the EEPROM when in the ISA ID probe state. */
+static ushort id_read_eeprom(int index)
+{
+ int bit, word = 0;
+
+ /* Issue read command, and pause for at least 162 us for it to
+ * complete. Assume extra-fast 16MHz bus.
+ */
+ outb(EEPROM_READ + index, id_port);
+
+ /* Pause for at least 162 us for the read to take place.
+ * Some chips seem to require much longer.
+ */
+ mdelay(4);
+
+ for (bit = 15; bit >= 0; bit--)
+ word = (word << 1) + (inb(id_port) & 0x01);
+
+ if (el3_debug > 3)
+ pr_debug(" 3c509 EEPROM word %d %#4.4x.\n", index, word);
+
+ return word;
+}
+
+static int el3_open(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ int i;
+
+ outw(TX_RESET, ioaddr + EL3_CMD);
+ outw(RX_RESET, ioaddr + EL3_CMD);
+ outw(SET_STATUS_ENB | 0x00, ioaddr + EL3_CMD);
+
+ i = request_irq(dev->irq, el3_interrupt, 0, dev->name, dev);
+ if (i)
+ return i;
+
+ EL3WINDOW(0);
+ if (el3_debug > 3)
+ pr_debug("%s: Opening, IRQ %d status@%x %4.4x.\n",
+ dev->name, dev->irq,
+ ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));
+
+ el3_up(dev);
+
+ if (el3_debug > 3)
+ pr_debug("%s: Opened 3c509 IRQ %d status %4.4x.\n",
+ dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
+
+ return 0;
+}
+
+static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue)
+{
+ int ioaddr = dev->base_addr;
+
+ /* Transmitter timeout, serious problems. */
+ pr_warn("%s: transmit timed out, Tx_status %2.2x status %4.4x Tx FIFO room %d\n",
+ dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS),
+ inw(ioaddr + TX_FREE));
+ dev->stats.tx_errors++;
+ netif_trans_update(dev); /* prevent tx timeout */
+ /* Issue TX_RESET and TX_START commands. */
+ outw(TX_RESET, ioaddr + EL3_CMD);
+ outw(TX_ENABLE, ioaddr + EL3_CMD);
+ netif_wake_queue(dev);
+}
+
+static netdev_tx_t el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+ unsigned long flags;
+
+ netif_stop_queue(dev);
+
+ dev->stats.tx_bytes += skb->len;
+
+ if (el3_debug > 4) {
+ pr_debug("%s: el3_start_xmit(length = %u) called, status %4.4x.\n",
+ dev->name, skb->len, inw(ioaddr + EL3_STATUS));
+ }
+ /*
+ * We lock the driver against other processors. Note
+ * we don't need to lock versus the IRQ as we suspended
+ * that. This means that we lose the ability to take
+ * an RX during a TX upload. That sucks a bit with SMP
+ * on an original 3c509 (2K buffer).
+ *
+ * Using disable_irq stops us crapping on other
+ * time sensitive devices.
+ */
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ /* Put out the doubleword header... */
+ outw(skb->len, ioaddr + TX_FIFO);
+ outw(0x00, ioaddr + TX_FIFO);
+ /* ... and the packet rounded to a doubleword. */
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+
+ if (inw(ioaddr + TX_FREE) > 1536) {
+ netif_start_queue(dev);
+ } else {
+ /* Interrupt us when the FIFO has room for max-sized packet. */
+ outw(SET_TX_THRESHOLD + 1536, ioaddr + EL3_CMD);
+ }
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ dev_consume_skb_any(skb);
+
+ /* Clear the Tx status stack. */
+ {
+ short tx_status;
+ int i = 4;
+
+ while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
+ if (tx_status & 0x38)
+ dev->stats.tx_aborted_errors++;
+ if (tx_status & 0x30)
+ outw(TX_RESET, ioaddr + EL3_CMD);
+ if (tx_status & 0x3C)
+ outw(TX_ENABLE, ioaddr + EL3_CMD);
+ /* Pop the status stack. */
+ outb(0x00, ioaddr + TX_STATUS);
+ }
+ }
+ return NETDEV_TX_OK;
+}
+
+/* The EL3 interrupt handler. */
+static irqreturn_t el3_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ int i = max_interrupt_work;
+ struct el3_private *lp;
+ int ioaddr, status;
+
+ lp = netdev_priv(dev);
+ spin_lock(&lp->lock);
+
+ ioaddr = dev->base_addr;
+
+ if (el3_debug > 4) {
+ status = inw(ioaddr + EL3_STATUS);
+ pr_debug("%s: interrupt, status %4.4x.\n", dev->name, status);
+ }
+
+ while ((status = inw(ioaddr + EL3_STATUS)) &
+ (INT_LATCH | RX_COMPLETE | STATS_FULL)) {
+
+ if (status & RX_COMPLETE)
+ el3_rx(dev);
+
+ if (status & TX_AVAILABLE) {
+ if (el3_debug > 5)
+ pr_debug(" TX room bit was handled.\n");
+ /* There's room in the FIFO for a full-sized packet. */
+ outw(ACK_INTR | TX_AVAILABLE, ioaddr + EL3_CMD);
+ netif_wake_queue(dev);
+ }
+ if (status &
+ (ADAPTER_FAILURE | RX_EARLY | STATS_FULL | TX_COMPLETE)) {
+ /* Handle all uncommon interrupts. */
+ if (status & STATS_FULL) {
+ /* Empty statistics. */
+ update_stats(dev);
+ }
+ if (status & RX_EARLY) {
+ /* Rx early is unused. */
+ el3_rx(dev);
+ outw(ACK_INTR | RX_EARLY, ioaddr + EL3_CMD);
+ }
+ if (status & TX_COMPLETE) {
+ /* Really Tx error. */
+ short tx_status;
+ int i = 4;
+
+ while (--i > 0 &&
+ ((tx_status = inb(ioaddr + TX_STATUS))
+ > 0)) {
+ if (tx_status & 0x38)
+ dev->stats.tx_aborted_errors++;
+ if (tx_status & 0x30)
+ outw(TX_RESET,
+ ioaddr + EL3_CMD);
+ if (tx_status & 0x3C)
+ outw(TX_ENABLE,
+ ioaddr + EL3_CMD);
+ /* Pop the status stack. */
+ outb(0x00, ioaddr + TX_STATUS);
+ }
+ }
+ if (status & ADAPTER_FAILURE) {
+ /* Adapter failure requires Rx reset
+ * and reinit.
+ */
+ outw(RX_RESET, ioaddr + EL3_CMD);
+ /* Set the Rx filter to the current state. */
+ outw((SET_RX_FILTER | RX_STATION |
+ RX_BROADCAST |
+ (dev->flags & IFF_ALLMULTI ?
+ RX_MULTICAST : 0) |
+ (dev->flags & IFF_PROMISC ?
+ RX_PROM : 0)),
+ ioaddr + EL3_CMD);
+ /* Re-enable the receiver. */
+ outw(RX_ENABLE, ioaddr + EL3_CMD);
+ outw(ACK_INTR | ADAPTER_FAILURE,
+ ioaddr + EL3_CMD);
+ }
+ }
+
+ if (--i < 0) {
+ pr_err("%s: Infinite loop in interrupt, status %4.4x.\n",
+ dev->name, status);
+ /* Clear all interrupts. */
+ outw(ACK_INTR | 0xFF, ioaddr + EL3_CMD);
+ break;
+ }
+ /* Acknowledge the IRQ. */
+ outw(ACK_INTR | INT_REQ | INT_LATCH, ioaddr + EL3_CMD);
+ }
+
+ if (el3_debug > 4) {
+ pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name,
+ inw(ioaddr + EL3_STATUS));
+ }
+ spin_unlock(&lp->lock);
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling receive - used by netconsole and other diagnostic tools
+ * to allow network i/o with interrupts disabled.
+ */
+static void el3_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ el3_interrupt(dev->irq, dev);
+ enable_irq(dev->irq);
+}
+#endif
+
+static struct net_device_stats *el3_get_stats(struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ unsigned long flags;
+
+ /* This is fast enough not to bother with disable IRQ stuff. */
+ spin_lock_irqsave(&lp->lock, flags);
+ update_stats(dev);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ return &dev->stats;
+}
+
+/* Update statistics. We change to register window 6, so this should be run
+ * single-threaded if the device is active. This is expected to be a rare
+ * operation, and it's simpler for the rest of the driver to assume that
+ * window 1 is always valid rather than use a special window-state variable.
+ */
+static void update_stats(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (el3_debug > 5)
+ pr_debug(" Updating the statistics.\n");
+ /* Turn off statistics updates while reading. */
+ outw(STATS_DISABLE, ioaddr + EL3_CMD);
+ /* Switch to the stats window, and read everything. */
+ EL3WINDOW(6);
+ dev->stats.tx_carrier_errors += inb(ioaddr + 0);
+ dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
+ /* Multiple collisions. */ inb(ioaddr + 2);
+ dev->stats.collisions += inb(ioaddr + 3);
+ dev->stats.tx_window_errors += inb(ioaddr + 4);
+ dev->stats.rx_fifo_errors += inb(ioaddr + 5);
+ dev->stats.tx_packets += inb(ioaddr + 6);
+ /* Rx packets */ inb(ioaddr + 7);
+ /* Tx deferrals */ inb(ioaddr + 8);
+ inw(ioaddr + 10); /* Total Rx and Tx octets. */
+ inw(ioaddr + 12);
+
+ /* Back to window 1, and turn statistics back on. */
+ EL3WINDOW(1);
+ outw(STATS_ENABLE, ioaddr + EL3_CMD);
+}
+
+static int el3_rx(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ short rx_status;
+
+ if (el3_debug > 5)
+ pr_debug(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ inw(ioaddr + EL3_STATUS), inw(ioaddr + RX_STATUS));
+ while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
+ if (rx_status & 0x4000) {
+ /* Error, update stats. */
+ short error = rx_status & 0x3800;
+
+ outw(RX_DISCARD, ioaddr + EL3_CMD);
+ dev->stats.rx_errors++;
+ switch (error) {
+ case 0x0000:
+ dev->stats.rx_over_errors++;
+ break;
+ case 0x0800:
+ dev->stats.rx_length_errors++;
+ break;
+ case 0x1000:
+ dev->stats.rx_frame_errors++;
+ break;
+ case 0x1800:
+ dev->stats.rx_length_errors++;
+ break;
+ case 0x2000:
+ dev->stats.rx_frame_errors++;
+ break;
+ case 0x2800:
+ dev->stats.rx_crc_errors++; break;
+ }
+ } else {
+ short pkt_len = rx_status & 0x7ff;
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(dev, pkt_len + 5);
+ if (el3_debug > 4)
+ pr_debug("Receiving packet size %d status %4.4x.\n",
+ pkt_len, rx_status);
+ if (skb) {
+ /* Align IP on 16 byte. */
+ skb_reserve(skb, 2);
+
+ /* 'skb->data' points to the start of sk_buff
+ * data area.
+ */
+ insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len),
+ (pkt_len + 3) >> 2);
+
+ /* Pop top Rx packet. */
+ outw(RX_DISCARD, ioaddr + EL3_CMD);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_bytes += pkt_len;
+ dev->stats.rx_packets++;
+ continue;
+ }
+ outw(RX_DISCARD, ioaddr + EL3_CMD);
+ dev->stats.rx_dropped++;
+ if (el3_debug)
+ pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, pkt_len);
+ }
+ inw(ioaddr + EL3_STATUS); /* Delay. */
+ while (inw(ioaddr + EL3_STATUS) & 0x1000)
+ pr_debug(" Waiting for 3c509 to discard packet, status %x.\n",
+ inw(ioaddr + EL3_STATUS));
+ }
+
+ return 0;
+}
+
+/* Set or clear the multicast filter for this adaptor. */
+static void set_multicast_list(struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+ int mc_count = netdev_mc_count(dev);
+ unsigned long flags;
+
+ if (el3_debug > 1) {
+ static int old;
+
+ if (old != mc_count) {
+ old = mc_count;
+ pr_debug("%s: Setting Rx mode to %d addresses.\n",
+ dev->name, mc_count);
+ }
+ }
+ spin_lock_irqsave(&lp->lock, flags);
+ if (dev->flags & IFF_PROMISC) {
+ outw((SET_RX_FILTER | RX_STATION | RX_MULTICAST |
+ RX_BROADCAST | RX_PROM),
+ ioaddr + EL3_CMD);
+ } else if (mc_count || (dev->flags & IFF_ALLMULTI)) {
+ outw(SET_RX_FILTER | RX_STATION | RX_MULTICAST | RX_BROADCAST,
+ ioaddr + EL3_CMD);
+ } else {
+ outw(SET_RX_FILTER | RX_STATION | RX_BROADCAST,
+ ioaddr + EL3_CMD);
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+static int el3_close(struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ int ioaddr = dev->base_addr;
+
+ if (el3_debug > 2)
+ pr_debug("%s: Shutting down ethercard.\n", dev->name);
+
+ el3_down(dev);
+
+ free_irq(dev->irq, dev);
+ /* Switching back to window 0 disables the IRQ. */
+ EL3WINDOW(0);
+ if (lp->type != EL3_EISA) {
+ /* But we explicitly zero the IRQ line select anyway. Don't do
+ * it on EISA cards, it prevents the module from getting an
+ * IRQ after unload+reload...
+ */
+ outw(0x0f00, ioaddr + WN0_IRQ);
+ }
+
+ return 0;
+}
+
+static int el3_link_ok(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ u16 tmp;
+
+ EL3WINDOW(4);
+ tmp = inw(ioaddr + WN4_MEDIA);
+ EL3WINDOW(1);
+ return tmp & (1 << 11);
+}
+
+static void el3_netdev_get_ecmd(struct net_device *dev,
+ struct ethtool_link_ksettings *cmd)
+{
+ int ioaddr = dev->base_addr;
+ u32 supported;
+ u16 tmp;
+
+ EL3WINDOW(0);
+ /* Obtain current transceiver via WN4_MEDIA? */
+ tmp = inw(ioaddr + WN0_ADDR_CONF);
+ switch (tmp >> 14) {
+ case 0:
+ cmd->base.port = PORT_TP;
+ break;
+ case 1:
+ cmd->base.port = PORT_AUI;
+ break;
+ case 3:
+ cmd->base.port = PORT_BNC;
+ break;
+ default:
+ break;
+ }
+
+ cmd->base.duplex = DUPLEX_HALF;
+ supported = 0;
+ tmp = inw(ioaddr + WN0_CONF_CTRL);
+ if (tmp & (1 << 13))
+ supported |= SUPPORTED_AUI;
+ if (tmp & (1 << 12))
+ supported |= SUPPORTED_BNC;
+ if (tmp & (1 << 9)) {
+ supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full; /* hmm... */
+ EL3WINDOW(4);
+ tmp = inw(ioaddr + WN4_NETDIAG);
+ if (tmp & FD_ENABLE)
+ cmd->base.duplex = DUPLEX_FULL;
+ }
+
+ ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
+ supported);
+ cmd->base.speed = SPEED_10;
+ EL3WINDOW(1);
+}
+
+static int el3_netdev_set_ecmd(struct net_device *dev,
+ const struct ethtool_link_ksettings *cmd)
+{
+ int ioaddr = dev->base_addr;
+ u16 tmp;
+
+ if (cmd->base.speed != SPEED_10)
+ return -EINVAL;
+ if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL)
+ return -EINVAL;
+
+ /* change XCVR type */
+ EL3WINDOW(0);
+ tmp = inw(ioaddr + WN0_ADDR_CONF);
+ switch (cmd->base.port) {
+ case PORT_TP:
+ tmp &= ~(3 << 14);
+ dev->if_port = 0;
+ break;
+ case PORT_AUI:
+ tmp &= ~(3 << 14);
+ tmp |= 1 << 14;
+ dev->if_port = 1;
+ break;
+ case PORT_BNC:
+ tmp |= 3 << 14;
+ dev->if_port = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ outw(tmp, ioaddr + WN0_ADDR_CONF);
+ if (dev->if_port == 3) {
+ /* Fire up the DC-DC converter if BNC gets enabled. */
+ tmp = inw(ioaddr + WN0_ADDR_CONF);
+ if (tmp & (3 << 14)) {
+ outw(START_COAX, ioaddr + EL3_CMD);
+ udelay(800);
+ } else {
+ return -EIO;
+ }
+ }
+
+ EL3WINDOW(4);
+ tmp = inw(ioaddr + WN4_NETDIAG);
+ if (cmd->base.duplex == DUPLEX_FULL)
+ tmp |= FD_ENABLE;
+ else
+ tmp &= ~FD_ENABLE;
+ outw(tmp, ioaddr + WN4_NETDIAG);
+ EL3WINDOW(1);
+
+ return 0;
+}
+
+static void el3_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strscpy(info->driver, DRV_NAME, sizeof(info->driver));
+}
+
+static int el3_get_link_ksettings(struct net_device *dev,
+ struct ethtool_link_ksettings *cmd)
+{
+ struct el3_private *lp = netdev_priv(dev);
+
+ spin_lock_irq(&lp->lock);
+ el3_netdev_get_ecmd(dev, cmd);
+ spin_unlock_irq(&lp->lock);
+ return 0;
+}
+
+static int el3_set_link_ksettings(struct net_device *dev,
+ const struct ethtool_link_ksettings *cmd)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ int ret;
+
+ spin_lock_irq(&lp->lock);
+ ret = el3_netdev_set_ecmd(dev, cmd);
+ spin_unlock_irq(&lp->lock);
+ return ret;
+}
+
+static u32 el3_get_link(struct net_device *dev)
+{
+ struct el3_private *lp = netdev_priv(dev);
+ u32 ret;
+
+ spin_lock_irq(&lp->lock);
+ ret = el3_link_ok(dev);
+ spin_unlock_irq(&lp->lock);
+ return ret;
+}
+
+static u32 el3_get_msglevel(struct net_device *dev)
+{
+ return el3_debug;
+}
+
+static void el3_set_msglevel(struct net_device *dev, u32 v)
+{
+ el3_debug = v;
+}
+
+static const struct ethtool_ops ethtool_ops = {
+ .get_drvinfo = el3_get_drvinfo,
+ .get_link = el3_get_link,
+ .get_msglevel = el3_get_msglevel,
+ .set_msglevel = el3_set_msglevel,
+ .get_link_ksettings = el3_get_link_ksettings,
+ .set_link_ksettings = el3_set_link_ksettings,
+};
+
+static void el3_down(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ netif_stop_queue(dev);
+
+ /* Turn off statistics ASAP. We update lp->stats below. */
+ outw(STATS_DISABLE, ioaddr + EL3_CMD);
+
+ /* Disable the receiver and transmitter. */
+ outw(RX_DISABLE, ioaddr + EL3_CMD);
+ outw(TX_DISABLE, ioaddr + EL3_CMD);
+
+ if (dev->if_port == 3) {
+ /* Turn off thinnet power. Green! */
+ outw(STOP_COAX, ioaddr + EL3_CMD);
+ } else if (dev->if_port == 0) {
+ /* Disable link beat and jabber, if_port may change here next
+ * open().
+ */
+ EL3WINDOW(4);
+ outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
+ }
+
+ outw(SET_INTR_ENB | 0x0000, ioaddr + EL3_CMD);
+
+ update_stats(dev);
+}
+
+static void el3_up(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ int i, sw_info, net_diag;
+
+ /* Activating the board required and does no harm otherwise. */
+ outw(0x0001, ioaddr + 4);
+
+ /* Set the IRQ line. */
+ outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
+
+ /* Set the station address in window 2 each time opened. */
+ EL3WINDOW(2);
+
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + i);
+
+ if ((dev->if_port & 0x03) == 3) {
+ /* BNC interface */
+
+ /* Start the thinnet transceiver. We should really wait
+ * 50ms...
+ */
+ outw(START_COAX, ioaddr + EL3_CMD);
+ } else if ((dev->if_port & 0x03) == 0) {
+ /* 10baseT interface */
+
+ /* Combine secondary sw_info word (the adapter level) and
+ * primary sw_info word (duplex setting plus other useless
+ * bits).
+ */
+ EL3WINDOW(0);
+ sw_info = (read_eeprom(ioaddr, 0x14) & 0x400f) |
+ (read_eeprom(ioaddr, 0x0d) & 0xBff0);
+
+ EL3WINDOW(4);
+ net_diag = inw(ioaddr + WN4_NETDIAG);
+ /* Temporarily assume full-duplex will be set. */
+ net_diag = (net_diag | FD_ENABLE);
+ pr_info("%s: ", dev->name);
+ switch (dev->if_port & 0x0c) {
+ case 12:
+ /* Force full-duplex mode if 3c5x9b. */
+ if (sw_info & 0x000f) {
+ pr_cont("Forcing 3c5x9b full-duplex mode");
+ break;
+ }
+ fallthrough;
+ case 8:
+ /* Set full-duplex mode based on eeprom config
+ * setting.
+ */
+ if ((sw_info & 0x000f) && (sw_info & 0x8000)) {
+ pr_cont("Setting 3c5x9b full-duplex mode (from EEPROM configuration bit)");
+ break;
+ }
+ fallthrough;
+ default:
+ /* xcvr = (0 || 4) OR user has an old 3c5x9 non "B"
+ * model.
+ */
+ pr_cont("Setting 3c5x9/3c5x9B half-duplex mode");
+ /* Disable full duplex. */
+ net_diag = (net_diag & ~FD_ENABLE);
+ }
+
+ outw(net_diag, ioaddr + WN4_NETDIAG);
+ pr_cont(" if_port: %d, sw_info: %4.4x\n",
+ dev->if_port, sw_info);
+ if (el3_debug > 3)
+ pr_debug("%s: 3c5x9 net diag word is now: %4.4x.\n",
+ dev->name, net_diag);
+ /* Enable link beat and jabber check. */
+ outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
+ }
+
+ /* Switch to the stats window, and clear all stats by reading. */
+ outw(STATS_DISABLE, ioaddr + EL3_CMD);
+ EL3WINDOW(6);
+ for (i = 0; i < 9; i++)
+ inb(ioaddr + i);
+ inw(ioaddr + 10);
+ inw(ioaddr + 12);
+
+ /* Switch to register set 1 for normal use. */
+ EL3WINDOW(1);
+
+ /* Accept b-case and phys addr only. */
+ outw(SET_RX_FILTER | RX_STATION | RX_BROADCAST, ioaddr + EL3_CMD);
+ /* Turn on statistics. */
+ outw(STATS_ENABLE, ioaddr + EL3_CMD);
+
+ /* Enable the receiver. */
+ outw(RX_ENABLE, ioaddr + EL3_CMD);
+ /* Enable transmitter. */
+ outw(TX_ENABLE, ioaddr + EL3_CMD);
+ /* Allow status bits to be seen. */
+ outw(SET_STATUS_ENB | 0xff, ioaddr + EL3_CMD);
+ /* Ack all pending events, and set active indicator mask. */
+ outw(ACK_INTR | INT_LATCH | TX_AVAILABLE | RX_EARLY | INT_REQ,
+ ioaddr + EL3_CMD);
+ outw((SET_INTR_ENB | INT_LATCH | TX_AVAILABLE | TX_COMPLETE |
+ RX_COMPLETE | STATS_FULL),
+ ioaddr + EL3_CMD);
+
+ netif_start_queue(dev);
+}
+
+/* Power Management support functions */
+#ifdef CONFIG_PM
+
+static int el3_suspend(struct device *pdev, pm_message_t state)
+{
+ struct net_device *dev;
+ struct el3_private *lp;
+ unsigned long flags;
+ int ioaddr;
+
+ dev = dev_get_drvdata(pdev);
+ lp = netdev_priv(dev);
+ ioaddr = dev->base_addr;
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ if (netif_running(dev))
+ netif_device_detach(dev);
+
+ el3_down(dev);
+ outw(POWER_DOWN, ioaddr + EL3_CMD);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+ return 0;
+}
+
+static int el3_resume(struct device *pdev)
+{
+ struct net_device *dev;
+ struct el3_private *lp;
+ unsigned long flags;
+ int ioaddr;
+
+ dev = dev_get_drvdata(pdev);
+ lp = netdev_priv(dev);
+ ioaddr = dev->base_addr;
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ outw(POWER_UP, ioaddr + EL3_CMD);
+ EL3WINDOW(0);
+ el3_up(dev);
+
+ if (netif_running(dev))
+ netif_device_attach(dev);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+module_param(debug, int, 0);
+module_param_hw_array(irq, int, irq, NULL, 0);
+module_param(max_interrupt_work, int, 0);
+MODULE_PARM_DESC(debug, "debug level (0-6)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt");
+#ifdef CONFIG_PNP
+module_param(nopnp, int, 0);
+MODULE_PARM_DESC(nopnp, "disable ISA PnP support (0-1)");
+#endif /* CONFIG_PNP */
+MODULE_DESCRIPTION("3Com Etherlink III (3c509, 3c509B, 3c529, 3c579) ethernet driver");
+MODULE_LICENSE("GPL");
+
+static int __init el3_init_module(void)
+{
+ int ret = 0;
+
+ if (debug >= 0)
+ el3_debug = debug;
+
+#ifdef CONFIG_PNP
+ if (!nopnp) {
+ ret = pnp_register_driver(&el3_pnp_driver);
+ if (!ret)
+ pnp_registered = 1;
+ }
+#endif
+ /* Select an open I/O location at 0x1*0 to do ISA contention select. */
+ /* Start with 0x110 to avoid some sound cards.*/
+ for (id_port = 0x110; id_port < 0x200; id_port += 0x10) {
+ if (!request_region(id_port, 1, "3c509-control"))
+ continue;
+ outb(0x00, id_port);
+ outb(0xff, id_port);
+ if (inb(id_port) & 0x01)
+ break;
+ release_region(id_port, 1);
+ }
+ if (id_port >= 0x200) {
+ id_port = 0;
+ pr_err("No I/O port available for 3c509 activation.\n");
+ } else {
+ ret = isa_register_driver(&el3_isa_driver, EL3_MAX_CARDS);
+ if (!ret)
+ isa_registered = 1;
+ }
+#ifdef CONFIG_EISA
+ ret = eisa_driver_register(&el3_eisa_driver);
+ if (!ret)
+ eisa_registered = 1;
+#endif
+
+#ifdef CONFIG_PNP
+ if (pnp_registered)
+ ret = 0;
+#endif
+ if (isa_registered)
+ ret = 0;
+#ifdef CONFIG_EISA
+ if (eisa_registered)
+ ret = 0;
+#endif
+ return ret;
+}
+
+static void __exit el3_cleanup_module(void)
+{
+#ifdef CONFIG_PNP
+ if (pnp_registered)
+ pnp_unregister_driver(&el3_pnp_driver);
+#endif
+ if (isa_registered)
+ isa_unregister_driver(&el3_isa_driver);
+ if (id_port)
+ release_region(id_port, 1);
+#ifdef CONFIG_EISA
+ if (eisa_registered)
+ eisa_driver_unregister(&el3_eisa_driver);
+#endif
+}
+
+module_init(el3_init_module);
+module_exit(el3_cleanup_module);
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index 399cb6c56198..81db16744f94 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -17,6 +17,20 @@ config NET_VENDOR_3COM
if NET_VENDOR_3COM
+config EL3
+ tristate "3c509/3c579 \"EtherLink III\" support"
+ depends on (ISA || EISA)
+ help
+ If you have a network (Ethernet) card belonging to the 3Com
+ EtherLinkIII series, say Y here.
+
+ If your card is not working you may need to use the DOS
+ setup disk to disable Plug & Play mode, and to select the default
+ media type.
+
+ To compile this driver as a module, choose M here. The module
+ will be called 3c509.
+
config VORTEX
tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support"
depends on (PCI || EISA) && HAS_IOPORT_MAP
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
index 5c4d07f1d456..2c65e472196f 100644
--- a/drivers/net/ethernet/3com/Makefile
+++ b/drivers/net/ethernet/3com/Makefile
@@ -3,5 +3,6 @@
# Makefile for the 3Com Ethernet device drivers
#
+obj-$(CONFIG_EL3) += 3c509.o
obj-$(CONFIG_VORTEX) += 3c59x.o
obj-$(CONFIG_TYPHOON) += typhoon.o
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index f8b3d53bccad..eab6a98d62b9 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1793,11 +1793,8 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port)
u32 val, pse_port, chan;
int i, src_port;
- /* Forward the traffic to the proper GDM port */
- pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3
- : FE_PSE_PORT_GDM4;
airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
- pse_port);
+ FE_PSE_PORT_DROP);
airoha_fe_clear(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
GDM_STRIP_CRC_MASK);
@@ -1815,6 +1812,11 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port)
GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK,
FIELD_PREP(GDM_SHORT_LEN_MASK, 60) |
FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU));
+ /* Forward the traffic to the proper GDM port */
+ pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3
+ : FE_PSE_PORT_GDM4;
+ airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX),
+ pse_port);
/* Disable VIP and IFC for GDM2 */
airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX));
@@ -2120,14 +2122,12 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
error_unmap:
- while (!list_empty(&tx_list)) {
- e = list_first_entry(&tx_list, struct airoha_queue_entry,
- list);
+ list_for_each_entry(e, &tx_list, list) {
dma_unmap_single(dev->dev.parent, e->dma_addr, e->dma_len,
DMA_TO_DEVICE);
e->dma_addr = 0;
- list_move_tail(&e->list, &q->tx_list);
}
+ list_splice(&tx_list, &q->tx_list);
spin_unlock_bh(&q->lock);
error:
@@ -2936,7 +2936,7 @@ static void airoha_metadata_dst_free(struct airoha_gdm_port *port)
if (!port->dsa_meta[i])
continue;
- metadata_dst_free(port->dsa_meta[i]);
+ dst_release(&port->dsa_meta[i]->dst);
}
}
diff --git a/drivers/net/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c
index e67b592e5697..8c86789d867a 100644
--- a/drivers/net/ethernet/amazon/ena/ena_com.c
+++ b/drivers/net/ethernet/amazon/ena/ena_com.c
@@ -1782,20 +1782,23 @@ void ena_com_phc_destroy(struct ena_com_dev *ena_dev)
int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp)
{
- volatile struct ena_admin_phc_resp *resp = ena_dev->phc.virt_addr;
const ktime_t zero_system_time = ktime_set(0, 0);
struct ena_com_phc_info *phc = &ena_dev->phc;
+ volatile struct ena_admin_phc_resp *resp;
ktime_t expire_time;
ktime_t block_time;
unsigned long flags = 0;
int ret = 0;
+ spin_lock_irqsave(&phc->lock, flags);
+
if (!phc->active) {
+ spin_unlock_irqrestore(&phc->lock, flags);
netdev_err(ena_dev->net_device, "PHC feature is not active in the device\n");
return -EOPNOTSUPP;
}
- spin_lock_irqsave(&phc->lock, flags);
+ resp = ena_dev->phc.virt_addr;
/* Check if PHC is in blocked state */
if (unlikely(ktime_compare(phc->system_time, zero_system_time))) {
diff --git a/drivers/net/ethernet/amazon/ena/ena_phc.c b/drivers/net/ethernet/amazon/ena/ena_phc.c
index 7867e893fd15..c2a3ff1ef645 100644
--- a/drivers/net/ethernet/amazon/ena/ena_phc.c
+++ b/drivers/net/ethernet/amazon/ena/ena_phc.c
@@ -46,9 +46,12 @@ static int ena_phc_gettimex64(struct ptp_clock_info *clock_info,
spin_unlock_irqrestore(&phc_info->lock, flags);
+ if (rc)
+ return rc;
+
*ts = ns_to_timespec64(timestamp_nsec);
- return rc;
+ return 0;
}
static int ena_phc_settime64(struct ptp_clock_info *clock_info,
diff --git a/drivers/net/ethernet/amd/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c
index 911808ab13a7..4f3076d4ea34 100644
--- a/drivers/net/ethernet/amd/pcnet32.c
+++ b/drivers/net/ethernet/amd/pcnet32.c
@@ -1407,8 +1407,10 @@ static int pcnet32_poll(struct napi_struct *napi, int budget)
pcnet32_restart(dev, CSR0_START);
netif_wake_queue(dev);
}
+ spin_unlock_irqrestore(&lp->lock, flags);
if (work_done < budget && napi_complete_done(napi, work_done)) {
+ spin_lock_irqsave(&lp->lock, flags);
/* clear interrupt masks */
val = lp->a->read_csr(ioaddr, CSR3);
val &= 0x00ff;
@@ -1416,9 +1418,9 @@ static int pcnet32_poll(struct napi_struct *napi, int budget)
/* Set interrupt enable. */
lp->a->write_csr(ioaddr, CSR0, CSR0_INTEN);
+ spin_unlock_irqrestore(&lp->lock, flags);
}
- spin_unlock_irqrestore(&lp->lock, flags);
return work_done;
}
diff --git a/drivers/net/ethernet/amd/pds_core/debugfs.c b/drivers/net/ethernet/amd/pds_core/debugfs.c
index 04c5e3abd8d7..810a0cd9bcac 100644
--- a/drivers/net/ethernet/amd/pds_core/debugfs.c
+++ b/drivers/net/ethernet/amd/pds_core/debugfs.c
@@ -64,9 +64,14 @@ DEFINE_SHOW_ATTRIBUTE(identity);
void pdsc_debugfs_add_ident(struct pdsc *pdsc)
{
+ struct dentry *dentry;
+
/* This file will already exist in the reset flow */
- if (debugfs_lookup("identity", pdsc->dentry))
+ dentry = debugfs_lookup("identity", pdsc->dentry);
+ if (!IS_ERR_OR_NULL(dentry)) {
+ dput(dentry);
return;
+ }
debugfs_create_file("identity", 0400, pdsc->dentry,
pdsc, &identity_fops);
diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c
index 2e1d0d01d03a..bded6b33289c 100644
--- a/drivers/net/ethernet/amd/pds_core/dev.c
+++ b/drivers/net/ethernet/amd/pds_core/dev.c
@@ -162,12 +162,19 @@ static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds)
dev_dbg(dev, "DEVCMD %d %s after %ld secs\n",
opcode, pdsc_devcmd_str(opcode), duration / HZ);
- if ((!done || timeout) && running) {
+ if (!running) {
+ dev_err(dev, "DEVCMD %d %s fw not running\n",
+ opcode, pdsc_devcmd_str(opcode));
+ pdsc_devcmd_clean(pdsc);
+ return -ENXIO;
+ }
+
+ if (!done || timeout) {
dev_err(dev, "DEVCMD %d %s timeout, done %d timeout %d max_seconds=%d\n",
opcode, pdsc_devcmd_str(opcode), done, timeout,
max_seconds);
- err = -ETIMEDOUT;
pdsc_devcmd_clean(pdsc);
+ return -ETIMEDOUT;
}
status = pdsc_devcmd_status(pdsc);
diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c
index b576be626a29..3f0e56b951bf 100644
--- a/drivers/net/ethernet/amd/pds_core/devlink.c
+++ b/drivers/net/ethernet/amd/pds_core/devlink.c
@@ -122,12 +122,14 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
listlen = min(fw_list.num_fw_slots, ARRAY_SIZE(fw_list.fw_names));
for (i = 0; i < listlen; i++) {
+ char *fw_ver = fw_list.fw_names[i].fw_version;
+
if (i < ARRAY_SIZE(fw_slotnames))
strscpy(buf, fw_slotnames[i], sizeof(buf));
else
snprintf(buf, sizeof(buf), "fw.slot_%d", i);
- err = devlink_info_version_stored_put(req, buf,
- fw_list.fw_names[i].fw_version);
+ fw_ver[sizeof(fw_list.fw_names[i].fw_version) - 1] = '\0';
+ err = devlink_info_version_stored_put(req, buf, fw_ver);
if (err)
return err;
}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index 60b7e53206d1..3d3b09010d48 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -135,11 +135,11 @@
*/
#define XGBE_TSTAMP_SSINC 20
#define XGBE_TSTAMP_SNSINC 0
-#define XGBE_PTP_ACT_CLK_FREQ 500000000
+#define XGBE_PTP_ACT_CLK_FREQ (NSEC_PER_SEC / XGBE_TSTAMP_SSINC)
#define XGBE_V2_TSTAMP_SSINC 0xA
#define XGBE_V2_TSTAMP_SNSINC 0
-#define XGBE_V2_PTP_ACT_CLK_FREQ 1000000000
+#define XGBE_V2_PTP_ACT_CLK_FREQ (NSEC_PER_SEC / XGBE_V2_TSTAMP_SSINC)
/* Define maximum supported values */
#define XGBE_MAX_PPS_OUT 4
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
index b854b6b42d77..2926e1e59941 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
@@ -910,7 +910,9 @@ static int xgene_mdiobus_register(struct xgene_enet_pdata *pdata,
return -ENXIO;
}
- return of_mdiobus_register(mdio, mdio_np);
+ ret = of_mdiobus_register(mdio, mdio_np);
+ of_node_put(mdio_np);
+ return ret;
}
/* Mask out all PHYs from auto probing. */
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c
index e9e38af680c3..39e1b606a75a 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c
@@ -371,7 +371,7 @@ static void aq_pci_shutdown(struct pci_dev *pdev)
pci_disable_device(pdev);
if (system_state == SYSTEM_POWER_OFF) {
- pci_wake_from_d3(pdev, false);
+ pci_wake_from_d3(pdev, self->aq_hw->aq_nic_cfg->wol);
pci_set_power_state(pdev, PCI_D3hot);
}
}
diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c
index a5ab99474179..4e4794c4dfdc 100644
--- a/drivers/net/ethernet/atheros/ag71xx.c
+++ b/drivers/net/ethernet/atheros/ag71xx.c
@@ -1856,6 +1856,9 @@ static int ag71xx_probe(struct platform_device *pdev)
ag71xx_int_disable(ag, AG71XX_INT_POLL);
ndev->irq = platform_get_irq(pdev, 0);
+ if (ndev->irq < 0)
+ return ndev->irq;
+
err = devm_request_irq(&pdev->dev, ndev->irq, ag71xx_interrupt,
0x0, dev_name(&pdev->dev), ndev);
if (err) {
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 8c55874f44ca..35e1f8f663c7 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -3825,7 +3825,10 @@ static int bnxt_alloc_tpa_info(struct bnxt *bp)
if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) {
if (!bp->max_tpa_v2)
return 0;
- bp->max_tpa = max_t(u16, bp->max_tpa_v2, MAX_TPA_P5);
+ bp->max_tpa = min_t(u16, bp->max_tpa_v2, MAX_TPA_P5);
+ /* Older P5 FW sets max_tpa_v2 low by mistake except NPAR */
+ if (bp->max_tpa <= 32 && BNXT_CHIP_P5(bp) && !BNXT_NPAR(bp))
+ bp->max_tpa = MAX_TPA_P5;
}
for (i = 0; i < bp->rx_nr_rings; i++) {
@@ -14385,13 +14388,28 @@ static void bnxt_unlock_sp(struct bnxt *bp)
netdev_unlock(bp->dev);
}
+/* Same as bnxt_lock_sp() with additional rtnl_lock */
+static void bnxt_rtnl_lock_sp(struct bnxt *bp)
+{
+ clear_bit(BNXT_STATE_IN_SP_TASK, &bp->state);
+ rtnl_lock();
+ netdev_lock(bp->dev);
+}
+
+static void bnxt_rtnl_unlock_sp(struct bnxt *bp)
+{
+ set_bit(BNXT_STATE_IN_SP_TASK, &bp->state);
+ netdev_unlock(bp->dev);
+ rtnl_unlock();
+}
+
/* Only called from bnxt_sp_task() */
static void bnxt_reset(struct bnxt *bp, bool silent)
{
- bnxt_lock_sp(bp);
+ bnxt_rtnl_lock_sp(bp);
if (test_bit(BNXT_STATE_OPEN, &bp->state))
bnxt_reset_task(bp, silent);
- bnxt_unlock_sp(bp);
+ bnxt_rtnl_unlock_sp(bp);
}
/* Only called from bnxt_sp_task() */
@@ -14399,9 +14417,9 @@ static void bnxt_rx_ring_reset(struct bnxt *bp)
{
int i;
- bnxt_lock_sp(bp);
+ bnxt_rtnl_lock_sp(bp);
if (!test_bit(BNXT_STATE_OPEN, &bp->state)) {
- bnxt_unlock_sp(bp);
+ bnxt_rtnl_unlock_sp(bp);
return;
}
/* Disable and flush TPA before resetting the RX ring */
@@ -14440,7 +14458,7 @@ static void bnxt_rx_ring_reset(struct bnxt *bp)
}
if (bp->flags & BNXT_FLAG_TPA)
bnxt_set_tpa(bp, true);
- bnxt_unlock_sp(bp);
+ bnxt_rtnl_unlock_sp(bp);
}
static void bnxt_fw_fatal_close(struct bnxt *bp)
@@ -15355,15 +15373,17 @@ static void bnxt_fw_reset_task(struct work_struct *work)
bp->fw_reset_state = BNXT_FW_RESET_STATE_OPENING;
fallthrough;
case BNXT_FW_RESET_STATE_OPENING:
- while (!netdev_trylock(bp->dev)) {
+ while (!rtnl_trylock()) {
bnxt_queue_fw_reset_work(bp, HZ / 10);
return;
}
+ netdev_lock(bp->dev);
rc = bnxt_open(bp->dev);
if (rc) {
netdev_err(bp->dev, "bnxt_open() failed during FW reset\n");
bnxt_fw_reset_abort(bp, rc);
netdev_unlock(bp->dev);
+ rtnl_unlock();
goto ulp_start;
}
@@ -15383,6 +15403,7 @@ static void bnxt_fw_reset_task(struct work_struct *work)
bnxt_dl_health_fw_status_update(bp, true);
}
netdev_unlock(bp->dev);
+ rtnl_unlock();
bnxt_ulp_start(bp);
bnxt_reenable_sriov(bp);
netdev_lock(bp->dev);
@@ -16376,7 +16397,7 @@ err_reset:
rc);
napi_enable_locked(&bnapi->napi);
bnxt_db_nq_arm(bp, &cpr->cp_db, cpr->cp_raw_cons);
- bnxt_reset_task(bp, true);
+ netif_close(dev);
return rc;
}
@@ -17227,6 +17248,7 @@ static int bnxt_resume(struct device *device)
struct bnxt *bp = netdev_priv(dev);
int rc = 0;
+ rtnl_lock();
netdev_lock(dev);
rc = pci_enable_device(bp->pdev);
if (rc) {
@@ -17271,6 +17293,7 @@ static int bnxt_resume(struct device *device)
resume_exit:
netdev_unlock(bp->dev);
+ rtnl_unlock();
if (!rc) {
bnxt_ulp_start(bp);
bnxt_reenable_sriov(bp);
@@ -17360,9 +17383,14 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev)
netdev_info(bp->dev, "PCI Slot Reset\n");
- if (!(bp->flags & BNXT_FLAG_CHIP_P5_PLUS) &&
- test_bit(BNXT_STATE_PCI_CHANNEL_IO_FROZEN, &bp->state))
- msleep(900);
+ if (test_bit(BNXT_STATE_PCI_CHANNEL_IO_FROZEN, &bp->state)) {
+ /* After DPC, the chip should return CRS when the vendor ID
+ * config register is read until it is ready. On all chips,
+ * this is not happening reliably so add a 5-second delay as a
+ * workaround.
+ */
+ msleep(5000);
+ }
netdev_lock(netdev);
@@ -17437,6 +17465,7 @@ static void bnxt_io_resume(struct pci_dev *pdev)
int err;
netdev_info(bp->dev, "PCI Slot Resume\n");
+ rtnl_lock();
netdev_lock(netdev);
err = bnxt_hwrm_func_qcaps(bp);
@@ -17454,6 +17483,7 @@ static void bnxt_io_resume(struct pci_dev *pdev)
netif_device_attach(netdev);
netdev_unlock(netdev);
+ rtnl_unlock();
if (!err) {
bnxt_ulp_start(bp);
bnxt_reenable_sriov(bp);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
index 53f336db4fcc..5d41dc1bc782 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
@@ -419,31 +419,13 @@ void bnxt_ptp_reapply_pps(struct bnxt *bp)
}
}
-static int bnxt_get_target_cycles(struct bnxt_ptp_cfg *ptp, u64 target_ns,
- u64 *cycles_delta)
-{
- u64 cycles_now;
- u64 nsec_now, nsec_delta;
- int rc;
-
- rc = bnxt_refclk_read(ptp->bp, NULL, &cycles_now);
- if (rc)
- return rc;
-
- nsec_now = bnxt_timecounter_cyc2time(ptp, cycles_now);
-
- nsec_delta = target_ns - nsec_now;
- *cycles_delta = div64_u64(nsec_delta << ptp->cc.shift, ptp->cc.mult);
- return 0;
-}
-
static int bnxt_ptp_perout_cfg(struct bnxt_ptp_cfg *ptp,
struct ptp_clock_request *rq)
{
struct hwrm_func_ptp_cfg_input *req;
struct bnxt *bp = ptp->bp;
struct timespec64 ts;
- u64 target_ns, delta;
+ u64 target_ns;
u16 enables;
int rc;
@@ -451,10 +433,6 @@ static int bnxt_ptp_perout_cfg(struct bnxt_ptp_cfg *ptp,
ts.tv_nsec = rq->perout.start.nsec;
target_ns = timespec64_to_ns(&ts);
- rc = bnxt_get_target_cycles(ptp, target_ns, &delta);
- if (rc)
- return rc;
-
rc = hwrm_req_init(bp, req, HWRM_FUNC_PTP_CFG);
if (rc)
return rc;
@@ -468,7 +446,10 @@ static int bnxt_ptp_perout_cfg(struct bnxt_ptp_cfg *ptp,
req->ptp_freq_adj_dll_phase = 0;
req->ptp_freq_adj_ext_period = cpu_to_le32(NSEC_PER_SEC);
req->ptp_freq_adj_ext_up = 0;
- req->ptp_freq_adj_ext_phase_lower = cpu_to_le32(delta);
+ req->ptp_freq_adj_ext_phase_lower =
+ cpu_to_le32(lower_32_bits(target_ns));
+ req->ptp_freq_adj_ext_phase_upper =
+ cpu_to_le32(upper_32_bits(target_ns));
return hwrm_req_send(bp, req);
}
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
index 052bf69cfa4c..5c751933da6a 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
@@ -175,8 +175,14 @@ int bnxt_register_dev(struct bnxt_en_dev *edev,
ulp->handle = handle;
rcu_assign_pointer(ulp->ulp_ops, ulp_ops);
- if (test_bit(BNXT_STATE_OPEN, &bp->state))
- bnxt_hwrm_vnic_cfg(bp, &bp->vnic_info[BNXT_VNIC_DEFAULT]);
+ if (test_bit(BNXT_STATE_OPEN, &bp->state)) {
+ rc = bnxt_hwrm_vnic_cfg(bp, &bp->vnic_info[BNXT_VNIC_DEFAULT]);
+ if (rc) {
+ netdev_err(dev, "Failed to configure dual VNIC mode\n");
+ RCU_INIT_POINTER(ulp->ulp_ops, NULL);
+ goto exit;
+ }
+ }
edev->ulp_tbl->msix_requested = bnxt_get_ulp_msix_num(bp);
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index 54f71b1e85fc..7c11cf916762 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -1368,13 +1368,12 @@ void bcmgenet_eee_enable_set(struct net_device *dev, bool enable)
reg &= ~(TBUF_EEE_EN | TBUF_PM_EN);
bcmgenet_writel(reg, priv->base + off);
- /* Do the same for thing for RBUF */
+ /* RBUF EEE/PM can break the RX path on GENET. Keep it disabled. */
reg = bcmgenet_rbuf_readl(priv, RBUF_ENERGY_CTRL);
- if (enable)
- reg |= RBUF_EEE_EN | RBUF_PM_EN;
- else
+ if (reg & (RBUF_EEE_EN | RBUF_PM_EN)) {
reg &= ~(RBUF_EEE_EN | RBUF_PM_EN);
- bcmgenet_rbuf_writel(priv, reg, RBUF_ENERGY_CTRL);
+ bcmgenet_rbuf_writel(priv, reg, RBUF_ENERGY_CTRL);
+ }
if (!enable && priv->clk_eee_enabled) {
clk_disable_unprepare(priv->clk_eee);
diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c
index fa5857923db4..b4bfd6c174e7 100644
--- a/drivers/net/ethernet/cirrus/cs89x0.c
+++ b/drivers/net/ethernet/cirrus/cs89x0.c
@@ -1271,7 +1271,6 @@ static const struct net_device_ops net_ops = {
static void __init reset_chip(struct net_device *dev)
{
-#if !defined(CONFIG_MACH_MX31ADS)
struct net_local *lp = netdev_priv(dev);
unsigned long reset_start_time;
@@ -1298,7 +1297,6 @@ static void __init reset_chip(struct net_device *dev)
while ((readreg(dev, PP_SelfST) & INIT_DONE) == 0 &&
time_before(jiffies, reset_start_time + 2))
;
-#endif /* !CONFIG_MACH_MX31ADS */
}
/* This is the real probe routine.
diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c
index 4824232f4890..4c762229ce42 100644
--- a/drivers/net/ethernet/cortina/gemini.c
+++ b/drivers/net/ethernet/cortina/gemini.c
@@ -122,6 +122,9 @@ struct gemini_ethernet_port {
struct napi_struct napi;
struct hrtimer rx_coalesce_timer;
unsigned int rx_coalesce_nsecs;
+ struct sk_buff *rx_skb;
+ unsigned int rx_frag_nr;
+
unsigned int freeq_refill;
struct gmac_txq txq[TX_QUEUE_NUM];
unsigned int txq_order;
@@ -1442,10 +1445,11 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
unsigned short m = (1 << port->rxq_order) - 1;
struct gemini_ethernet *geth = port->geth;
void __iomem *ptr_reg = port->rxq_rwptr;
+ unsigned int frag_nr = port->rx_frag_nr;
+ struct sk_buff *skb = port->rx_skb;
unsigned int frame_len, frag_len;
struct gmac_rxdesc *rx = NULL;
struct gmac_queue_page *gpage;
- static struct sk_buff *skb;
union gmac_rxdesc_0 word0;
union gmac_rxdesc_1 word1;
union gmac_rxdesc_3 word3;
@@ -1455,7 +1459,6 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
unsigned short r, w;
union dma_rwptr rw;
dma_addr_t mapping;
- int frag_nr = 0;
spin_lock_irqsave(&geth->irq_lock, flags);
rw.bits32 = readl(ptr_reg);
@@ -1491,6 +1494,12 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
gpage = gmac_get_queue_page(geth, port, mapping + PAGE_SIZE);
if (!gpage) {
dev_err(geth->dev, "could not find mapping\n");
+ port->stats.rx_dropped++;
+ if (skb) {
+ napi_free_frags(&port->napi);
+ skb = NULL;
+ frag_nr = 0;
+ }
continue;
}
page = gpage->page;
@@ -1499,6 +1508,8 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
if (skb) {
napi_free_frags(&port->napi);
port->stats.rx_dropped++;
+ skb = NULL;
+ frag_nr = 0;
}
skb = gmac_skb_if_good_frame(port, word0, frame_len);
@@ -1533,6 +1544,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget)
if (word3.bits32 & EOF_BIT) {
napi_gro_frags(&port->napi);
skb = NULL;
+ frag_nr = 0;
--budget;
}
continue;
@@ -1541,6 +1553,7 @@ err_drop:
if (skb) {
napi_free_frags(&port->napi);
skb = NULL;
+ frag_nr = 0;
}
if (mapping)
@@ -1549,6 +1562,8 @@ err_drop:
port->stats.rx_dropped++;
}
+ port->rx_skb = skb;
+ port->rx_frag_nr = frag_nr;
writew(r, ptr_reg);
return budget;
}
@@ -1876,6 +1891,8 @@ static int gmac_stop(struct net_device *netdev)
gmac_disable_tx_rx(netdev);
gmac_stop_dma(port);
napi_disable(&port->napi);
+ port->rx_skb = NULL;
+ port->rx_frag_nr = 0;
gmac_enable_irq(netdev, 0);
gmac_cleanup_rxq(netdev);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index e663bb5e614e..e691144e8756 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -330,6 +330,7 @@ struct enetc_si {
struct workqueue_struct *workqueue;
struct work_struct rx_mode_task;
struct dentry *debugfs_root;
+ struct enetc_msg_swbd msg; /* Only valid for VSI */
};
#define ENETC_SI_ALIGN 32
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index 662e4fbafb74..e58cc81d199d 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -56,11 +56,21 @@ static inline u32 enetc_vsi_set_msize(u32 size)
}
#define ENETC_PSIMSGRR 0x204
-#define ENETC_PSIMSGRR_MR_MASK GENMASK(2, 1)
-#define ENETC_PSIMSGRR_MR(n) BIT((n) + 1) /* n = VSI index */
#define ENETC_PSIVMSGRCVAR0(n) (0x210 + (n) * 0x8) /* n = VSI index */
#define ENETC_PSIVMSGRCVAR1(n) (0x214 + (n) * 0x8)
+/* Message received mask, n is the active number of VSIs.
+ * It is available for ENETC_PSIMSGRR, ENETC_PSIIER, and
+ * ENETC_PSIIDR registers.
+ */
+#define ENETC_PSIMR_MASK(n) \
+ ({ typeof(n) _n = (n); (_n) ? GENMASK((_n), 1) : 0; })
+
+/* Message received bit, n is VSI index. It is available for
+ * ENETC_PSIMSGRR, ENETC_PSIIER, and ENETC_PSIIDR registers.
+ */
+#define ENETC_PSIMR_BIT(n) BIT((n) + 1)
+
#define ENETC_VSIMSGSR 0x204 /* RO */
#define ENETC_VSIMSGSR_MB BIT(0)
#define ENETC_VSIMSGSR_MS BIT(1)
@@ -94,7 +104,6 @@ static inline u32 enetc_vsi_set_msize(u32 size)
#define ENETC_SICAPR1 0x904
#define ENETC_PSIIER 0xa00
-#define ENETC_PSIIER_MR_MASK GENMASK(2, 1)
#define ENETC_PSIIDR 0xa08
#define ENETC_SITXIDR 0xa18
#define ENETC_SIRXIDR 0xa28
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_msg.c b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
index 40d22ebe9224..c09635e7eb3d 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_msg.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_msg.c
@@ -3,18 +3,25 @@
#include "enetc_pf.h"
-static void enetc_msg_disable_mr_int(struct enetc_hw *hw)
+static void enetc_msg_disable_mr_int(struct enetc_pf *pf)
{
- u32 psiier = enetc_rd(hw, ENETC_PSIIER);
+ struct enetc_hw *hw = &pf->si->hw;
+ u32 psiier;
+
+ psiier = enetc_rd(hw, ENETC_PSIIER) & ~ENETC_PSIMR_MASK(pf->num_vfs);
+
/* disable MR int source(s) */
- enetc_wr(hw, ENETC_PSIIER, psiier & ~ENETC_PSIIER_MR_MASK);
+ enetc_wr(hw, ENETC_PSIIER, psiier);
}
-static void enetc_msg_enable_mr_int(struct enetc_hw *hw)
+static void enetc_msg_enable_mr_int(struct enetc_pf *pf)
{
- u32 psiier = enetc_rd(hw, ENETC_PSIIER);
+ struct enetc_hw *hw = &pf->si->hw;
+ u32 psiier;
+
+ psiier = enetc_rd(hw, ENETC_PSIIER) | ENETC_PSIMR_MASK(pf->num_vfs);
- enetc_wr(hw, ENETC_PSIIER, psiier | ENETC_PSIIER_MR_MASK);
+ enetc_wr(hw, ENETC_PSIIER, psiier);
}
static irqreturn_t enetc_msg_psi_msix(int irq, void *data)
@@ -22,7 +29,7 @@ static irqreturn_t enetc_msg_psi_msix(int irq, void *data)
struct enetc_si *si = (struct enetc_si *)data;
struct enetc_pf *pf = enetc_si_priv(si);
- enetc_msg_disable_mr_int(&si->hw);
+ enetc_msg_disable_mr_int(pf);
schedule_work(&pf->msg_task);
return IRQ_HANDLED;
@@ -31,33 +38,35 @@ static irqreturn_t enetc_msg_psi_msix(int irq, void *data)
static void enetc_msg_task(struct work_struct *work)
{
struct enetc_pf *pf = container_of(work, struct enetc_pf, msg_task);
+ u32 mr_mask = ENETC_PSIMR_MASK(pf->num_vfs);
struct enetc_hw *hw = &pf->si->hw;
- unsigned long mr_mask;
+ u32 mr_status;
int i;
- for (;;) {
- mr_mask = enetc_rd(hw, ENETC_PSIMSGRR) & ENETC_PSIMSGRR_MR_MASK;
- if (!mr_mask) {
- /* re-arm MR interrupts, w1c the IDR reg */
- enetc_wr(hw, ENETC_PSIIDR, ENETC_PSIIER_MR_MASK);
- enetc_msg_enable_mr_int(hw);
- return;
- }
+ mr_status = (enetc_rd(hw, ENETC_PSIMSGRR) & mr_mask) |
+ (enetc_rd(hw, ENETC_PSIIDR) & mr_mask);
+ if (!mr_status)
+ goto out;
- for (i = 0; i < pf->num_vfs; i++) {
- u32 psimsgrr;
- u16 msg_code;
+ for (i = 0; i < pf->num_vfs; i++) {
+ u32 psimsgrr;
+ u16 msg_code;
+
+ if (!(ENETC_PSIMR_BIT(i) & mr_status))
+ continue;
- if (!(ENETC_PSIMSGRR_MR(i) & mr_mask))
- continue;
+ enetc_msg_handle_rxmsg(pf, i, &msg_code);
- enetc_msg_handle_rxmsg(pf, i, &msg_code);
+ /* w1c to clear the corresponding VF MR bit */
+ enetc_wr(hw, ENETC_PSIIDR, ENETC_PSIMR_BIT(i));
- psimsgrr = ENETC_SIMSGSR_SET_MC(msg_code);
- psimsgrr |= ENETC_PSIMSGRR_MR(i); /* w1c */
- enetc_wr(hw, ENETC_PSIMSGRR, psimsgrr);
- }
+ psimsgrr = ENETC_SIMSGSR_SET_MC(msg_code);
+ psimsgrr |= ENETC_PSIMR_BIT(i); /* w1c */
+ enetc_wr(hw, ENETC_PSIMSGRR, psimsgrr);
}
+
+out:
+ enetc_msg_enable_mr_int(pf);
}
/* Init */
@@ -96,12 +105,12 @@ static void enetc_msg_free_mbx(struct enetc_si *si, int idx)
struct enetc_hw *hw = &si->hw;
struct enetc_msg_swbd *msg;
+ enetc_wr(hw, ENETC_PSIVMSGRCVAR0(idx), 0);
+ enetc_wr(hw, ENETC_PSIVMSGRCVAR1(idx), 0);
+
msg = &pf->rxmsg[idx];
dma_free_coherent(&si->pdev->dev, msg->size, msg->vaddr, msg->dma);
memset(msg, 0, sizeof(*msg));
-
- enetc_wr(hw, ENETC_PSIVMSGRCVAR0(idx), 0);
- enetc_wr(hw, ENETC_PSIVMSGRCVAR1(idx), 0);
}
int enetc_msg_psi_init(struct enetc_pf *pf)
@@ -109,6 +118,15 @@ int enetc_msg_psi_init(struct enetc_pf *pf)
struct enetc_si *si = pf->si;
int vector, i, err;
+ for (i = 0; i < pf->num_vfs; i++) {
+ err = enetc_msg_alloc_mbx(si, i);
+ if (err)
+ goto free_mbx;
+ }
+
+ /* initialize PSI mailbox */
+ INIT_WORK(&pf->msg_task, enetc_msg_task);
+
/* register message passing interrupt handler */
snprintf(pf->msg_int_name, sizeof(pf->msg_int_name), "%s-vfmsg",
si->ndev->name);
@@ -117,32 +135,21 @@ int enetc_msg_psi_init(struct enetc_pf *pf)
if (err) {
dev_err(&si->pdev->dev,
"PSI messaging: request_irq() failed!\n");
- return err;
+ goto free_mbx;
}
/* set one IRQ entry for PSI message receive notification (SI int) */
enetc_wr(&si->hw, ENETC_SIMSIVR, ENETC_SI_INT_IDX);
- /* initialize PSI mailbox */
- INIT_WORK(&pf->msg_task, enetc_msg_task);
-
- for (i = 0; i < pf->num_vfs; i++) {
- err = enetc_msg_alloc_mbx(si, i);
- if (err)
- goto err_init_mbx;
- }
-
/* enable MR interrupts */
- enetc_msg_enable_mr_int(&si->hw);
+ enetc_msg_enable_mr_int(pf);
return 0;
-err_init_mbx:
+free_mbx:
for (i--; i >= 0; i--)
enetc_msg_free_mbx(si, i);
- free_irq(vector, si);
-
return err;
}
@@ -151,14 +158,17 @@ void enetc_msg_psi_free(struct enetc_pf *pf)
struct enetc_si *si = pf->si;
int i;
+ /* disable MR interrupts */
+ enetc_msg_disable_mr_int(pf);
+
+ /* de-register message passing interrupt handler */
+ free_irq(pci_irq_vector(si->pdev, ENETC_SI_INT_IDX), si);
+
cancel_work_sync(&pf->msg_task);
- /* disable MR interrupts */
- enetc_msg_disable_mr_int(&si->hw);
+ /* MR interrupts may be re-enabled by workqueue */
+ enetc_msg_disable_mr_int(pf);
for (i = 0; i < pf->num_vfs; i++)
enetc_msg_free_mbx(si, i);
-
- /* de-register message passing interrupt handler */
- free_irq(pci_irq_vector(si->pdev, ENETC_SI_INT_IDX), si);
}
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
index a12fd54a475f..3206b3daa1a0 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
@@ -252,8 +252,12 @@ static int enetc_pf_set_vf_mac(struct net_device *ndev, int vf, u8 *mac)
return -EADDRNOTAVAIL;
vf_state = &pf->vf_state[vf];
+
+ mutex_lock(&vf_state->lock);
vf_state->flags |= ENETC_VF_FLAG_PF_SET_MAC;
enetc_pf_set_primary_mac_addr(&priv->si->hw, vf + 1, mac);
+ mutex_unlock(&vf_state->lock);
+
return 0;
}
@@ -478,49 +482,77 @@ static void enetc_configure_port(struct enetc_pf *pf)
/* Messaging */
static u16 enetc_msg_pf_set_vf_primary_mac_addr(struct enetc_pf *pf,
- int vf_id)
+ int vf_id, void *msg)
{
struct enetc_vf_state *vf_state = &pf->vf_state[vf_id];
- struct enetc_msg_swbd *msg = &pf->rxmsg[vf_id];
- struct enetc_msg_cmd_set_primary_mac *cmd;
+ struct enetc_msg_cmd_set_primary_mac *cmd = msg;
struct device *dev = &pf->si->pdev->dev;
- u16 cmd_id;
+ u16 cmd_id = cmd->header.id;
char *addr;
- cmd = (struct enetc_msg_cmd_set_primary_mac *)msg->vaddr;
- cmd_id = cmd->header.id;
if (cmd_id != ENETC_MSG_CMD_MNG_ADD)
return ENETC_MSG_CMD_STATUS_FAIL;
addr = cmd->mac.sa_data;
- if (vf_state->flags & ENETC_VF_FLAG_PF_SET_MAC)
- dev_warn(dev, "Attempt to override PF set mac addr for VF%d\n",
- vf_id);
- else
- enetc_pf_set_primary_mac_addr(&pf->si->hw, vf_id + 1, addr);
+ if (!is_valid_ether_addr(addr)) {
+ dev_err_ratelimited(dev, "VF%d attempted to set invalid MAC\n",
+ vf_id);
+ return ENETC_MSG_CMD_STATUS_FAIL;
+ }
+
+ mutex_lock(&vf_state->lock);
+ if (vf_state->flags & ENETC_VF_FLAG_PF_SET_MAC) {
+ mutex_unlock(&vf_state->lock);
+ dev_err_ratelimited(dev,
+ "VF%d attempted to override PF set MAC\n",
+ vf_id);
+ return ENETC_MSG_CMD_STATUS_FAIL;
+ }
+
+ enetc_pf_set_primary_mac_addr(&pf->si->hw, vf_id + 1, addr);
+ mutex_unlock(&vf_state->lock);
return ENETC_MSG_CMD_STATUS_OK;
}
void enetc_msg_handle_rxmsg(struct enetc_pf *pf, int vf_id, u16 *status)
{
- struct enetc_msg_swbd *msg = &pf->rxmsg[vf_id];
+ struct enetc_msg_swbd *msg_swbd = &pf->rxmsg[vf_id];
struct device *dev = &pf->si->pdev->dev;
struct enetc_msg_cmd_header *cmd_hdr;
u16 cmd_type;
+ u8 *msg;
- *status = ENETC_MSG_CMD_STATUS_OK;
- cmd_hdr = (struct enetc_msg_cmd_header *)msg->vaddr;
+ msg = kzalloc_objs(*msg, msg_swbd->size);
+ if (!msg) {
+ dev_err_ratelimited(dev,
+ "Failed to allocate message buffer\n");
+ *status = ENETC_MSG_CMD_STATUS_FAIL;
+ return;
+ }
+
+ /* Currently, only ENETC_MSG_CMD_MNG_MAC command is supported, so
+ * only sizeof(struct enetc_msg_cmd_set_primary_mac) bytes need to
+ * be copied. This data already includes the cmd_type field, so it
+ * can correctly return an error code.
+ */
+ memcpy(msg, msg_swbd->vaddr,
+ sizeof(struct enetc_msg_cmd_set_primary_mac));
+ cmd_hdr = (struct enetc_msg_cmd_header *)msg;
cmd_type = cmd_hdr->type;
switch (cmd_type) {
case ENETC_MSG_CMD_MNG_MAC:
- *status = enetc_msg_pf_set_vf_primary_mac_addr(pf, vf_id);
+ *status = enetc_msg_pf_set_vf_primary_mac_addr(pf, vf_id, msg);
break;
default:
- dev_err(dev, "command not supported (cmd_type: 0x%x)\n",
- cmd_type);
+ *status = ENETC_MSG_CMD_STATUS_FAIL;
+ dev_err_ratelimited(dev,
+ "command not supported (cmd_type: 0x%x)\n",
+ cmd_type);
}
+
+ kfree(msg);
}
#ifdef CONFIG_PCI_IOV
@@ -531,9 +563,9 @@ static int enetc_sriov_configure(struct pci_dev *pdev, int num_vfs)
int err;
if (!num_vfs) {
+ pci_disable_sriov(pdev);
enetc_msg_psi_free(pf);
pf->num_vfs = 0;
- pci_disable_sriov(pdev);
} else {
pf->num_vfs = num_vfs;
@@ -960,8 +992,13 @@ static int enetc_pf_probe(struct pci_dev *pdev,
if (pf->total_vfs) {
pf->vf_state = kzalloc_objs(struct enetc_vf_state,
pf->total_vfs);
- if (!pf->vf_state)
+ if (!pf->vf_state) {
+ err = -ENOMEM;
goto err_alloc_vf_state;
+ }
+
+ for (int i = 0; i < pf->total_vfs; i++)
+ mutex_init(&pf->vf_state[i].lock);
}
err = enetc_setup_mac_addresses(node, pf);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
index ae407e9e9ee7..35d484858c7b 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
@@ -14,6 +14,7 @@ enum enetc_vf_flags {
};
struct enetc_vf_state {
+ struct mutex lock; /* Prevent concurrent access */
enum enetc_vf_flags flags;
};
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
index 6c4b374bcb0e..df8e95cc47d0 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
@@ -17,11 +17,36 @@ static void enetc_msg_vsi_write_msg(struct enetc_hw *hw,
enetc_wr(hw, ENETC_VSIMSGSNDAR0, val);
}
+static void enetc_msg_dma_free(struct device *dev, struct enetc_msg_swbd *msg)
+{
+ if (msg->vaddr) {
+ dma_free_coherent(dev, msg->size, msg->vaddr, msg->dma);
+ msg->vaddr = NULL;
+ }
+}
+
static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg)
{
+ struct device *dev = &si->pdev->dev;
int timeout = 100;
u32 vsimsgsr;
+ /* The VSI mailbox may be busy if last message was not yet processed
+ * by PSI. So need to check the mailbox status before sending.
+ */
+ vsimsgsr = enetc_rd(&si->hw, ENETC_VSIMSGSR);
+ if (vsimsgsr & ENETC_VSIMSGSR_MB) {
+ /* It is safe to free the DMA buffer here, the caller does
+ * not access the DMA buffer if enetc_msg_vsi_send() fails.
+ */
+ enetc_msg_dma_free(dev, msg);
+ dev_err(dev, "VSI mailbox is busy\n");
+ return -EIO;
+ }
+
+ /* Free the DMA buffer of the last message */
+ enetc_msg_dma_free(dev, &si->msg);
+ si->msg = *msg;
enetc_msg_vsi_write_msg(&si->hw, msg);
do {
@@ -32,12 +57,15 @@ static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg)
usleep_range(1000, 2000);
} while (--timeout);
- if (!timeout)
+ if (!timeout) {
+ dev_err(dev, "VSI mailbox timeout\n");
+
return -ETIMEDOUT;
+ }
/* check for message delivery error */
if (vsimsgsr & ENETC_VSIMSGSR_MS) {
- dev_err(&si->pdev->dev, "VSI command execute error: %d\n",
+ dev_err(dev, "VSI command execute error: %d\n",
ENETC_SIMSGSR_GET_MC(vsimsgsr));
return -EIO;
}
@@ -50,7 +78,6 @@ static int enetc_msg_vsi_set_primary_mac_addr(struct enetc_ndev_priv *priv,
{
struct enetc_msg_cmd_set_primary_mac *cmd;
struct enetc_msg_swbd msg;
- int err;
msg.size = ALIGN(sizeof(struct enetc_msg_cmd_set_primary_mac), 64);
msg.vaddr = dma_alloc_coherent(priv->dev, msg.size, &msg.dma,
@@ -67,11 +94,7 @@ static int enetc_msg_vsi_set_primary_mac_addr(struct enetc_ndev_priv *priv,
memcpy(&cmd->mac, saddr, sizeof(struct sockaddr));
/* send the command and wait */
- err = enetc_msg_vsi_send(priv->si, &msg);
-
- dma_free_coherent(priv->dev, msg.size, msg.vaddr, msg.dma);
-
- return err;
+ return enetc_msg_vsi_send(priv->si, &msg);
}
static int enetc_vf_set_mac_addr(struct net_device *ndev, void *addr)
@@ -259,6 +282,7 @@ static void enetc_vf_remove(struct pci_dev *pdev)
{
struct enetc_si *si = pci_get_drvdata(pdev);
struct enetc_ndev_priv *priv;
+ struct enetc_msg_swbd msg;
priv = netdev_priv(si->ndev);
unregister_netdev(si->ndev);
@@ -270,7 +294,9 @@ static void enetc_vf_remove(struct pci_dev *pdev)
free_netdev(si->ndev);
+ msg = si->msg;
enetc_pci_remove(pdev);
+ enetc_msg_dma_free(&pdev->dev, &msg);
}
static const struct pci_device_id enetc_vf_id_table[] = {
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index f89aa94ce020..6ebde65d7f1b 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -5594,6 +5594,7 @@ static int fec_resume(struct device *dev)
if (fep->rpm_active)
pm_runtime_force_resume(dev);
+ pinctrl_pm_select_default_state(&fep->pdev->dev);
ret = fec_enet_clk_enable(ndev, true);
if (ret) {
rtnl_unlock();
@@ -5610,8 +5611,6 @@ static int fec_resume(struct device *dev)
val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
writel(val, fep->hwp + FEC_ECNTRL);
fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON;
- } else {
- pinctrl_pm_select_default_state(&fep->pdev->dev);
}
fec_restart(ndev);
netif_tx_lock_bh(ndev);
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
index 068da2fd1fea..f721e9893804 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
@@ -420,6 +420,9 @@ static int hbg_pci_init(struct pci_dev *pdev)
return -ENOMEM;
pci_set_master(pdev);
+ pcie_capability_clear_word(pdev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_RELAX_EN);
+ pci_save_state(pdev);
return 0;
}
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c
index a4ea92c31c2f..0ae314994676 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c
@@ -452,12 +452,12 @@ static bool hbg_sync_data_from_hw(struct hbg_priv *priv,
{
struct hbg_rx_desc *rx_desc;
- /* make sure HW write desc complete */
- dma_rmb();
-
dma_sync_single_for_cpu(&priv->pdev->dev, buffer->page_dma,
buffer->page_size, DMA_FROM_DEVICE);
+ /* make sure HW write desc complete */
+ dma_rmb();
+
rx_desc = (struct hbg_rx_desc *)buffer->page_addr;
return FIELD_GET(HBG_RX_DESC_W2_PKT_LEN_M, rx_desc->word2) != 0;
}
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index dcb50c2e1aa2..83e780919ac9 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -1318,6 +1318,7 @@ void i40e_ptp_restore_hw_time(struct i40e_pf *pf);
void i40e_ptp_init(struct i40e_pf *pf);
void i40e_ptp_stop(struct i40e_pf *pf);
int i40e_ptp_alloc_pins(struct i40e_pf *pf);
+void i40e_ptp_free_pins(struct i40e_pf *pf);
int i40e_update_adq_vsi_queues(struct i40e_vsi *vsi, int vsi_offset);
int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi);
int i40e_get_partition_bw_setting(struct i40e_pf *pf);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 028bd500603a..6d4f9218dc68 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -16108,9 +16108,11 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* Unwind what we've done if something failed in the setup */
err_vsis:
set_bit(__I40E_DOWN, pf->state);
+ i40e_ptp_stop(pf);
i40e_clear_interrupt_scheme(pf);
kfree(pf->vsi);
err_switch_setup:
+ i40e_ptp_free_pins(pf);
i40e_reset_interrupt_capability(pf);
timer_shutdown_sync(&pf->service_timer);
err_mac_addr:
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
index 404a716db8da..7d07c389bb23 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
@@ -940,12 +940,13 @@ int i40e_ptp_hwtstamp_get(struct net_device *netdev,
*
* Release memory allocated for PTP pins.
**/
-static void i40e_ptp_free_pins(struct i40e_pf *pf)
+void i40e_ptp_free_pins(struct i40e_pf *pf)
{
if (i40e_is_ptp_pin_dev(&pf->hw)) {
kfree(pf->ptp_pins);
kfree(pf->ptp_caps.pin_config);
pf->ptp_pins = NULL;
+ pf->ptp_caps.pin_config = NULL;
}
}
diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
index 16aa25535152..0bc6dd375687 100644
--- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
@@ -537,14 +537,14 @@ void ice_dcb_rebuild(struct ice_pf *pf)
struct ice_dcbx_cfg *err_cfg;
int ret;
+ mutex_lock(&pf->tc_mutex);
+
ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL);
if (ret) {
dev_err(dev, "Query Port ETS failed\n");
goto dcb_error;
}
- mutex_lock(&pf->tc_mutex);
-
if (!pf->hw.port_info->qos_cfg.is_sw_lldp)
ice_cfg_etsrec_defaults(pf->hw.port_info);
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
index 27b460926bac..892bc7c2e28b 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -2523,6 +2523,8 @@ ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin, void *pin_priv,
if (hw_idx < 0)
goto unlock;
hw_idx -= pf->dplls.base_rclk_idx;
+ if (hw_idx >= ICE_DPLL_RCLK_NUM_MAX)
+ goto unlock;
if ((enable && p->state[hw_idx] == DPLL_PIN_STATE_CONNECTED) ||
(!enable && p->state[hw_idx] == DPLL_PIN_STATE_DISCONNECTED)) {
@@ -2586,6 +2588,9 @@ ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv,
hw_idx = ice_dpll_pin_get_parent_idx(p, parent_pin);
if (hw_idx < 0)
goto unlock;
+ hw_idx -= pf->dplls.base_rclk_idx;
+ if (hw_idx >= ICE_DPLL_RCLK_NUM_MAX)
+ goto unlock;
ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_INPUT,
extack);
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethernet/intel/ice/ice_dpll.h
index ae42cdea0ee1..8678575359b9 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.h
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
@@ -8,6 +8,22 @@
#define ICE_DPLL_RCLK_NUM_MAX 4
+#define ICE_CGU_R10 0x28
+#define ICE_CGU_R10_SYNCE_CLKO_SEL GENMASK(8, 5)
+#define ICE_CGU_R10_SYNCE_CLKODIV_M1 GENMASK(13, 9)
+#define ICE_CGU_R10_SYNCE_CLKODIV_LOAD BIT(14)
+#define ICE_CGU_R10_SYNCE_DCK_RST BIT(15)
+#define ICE_CGU_R10_SYNCE_ETHCLKO_SEL GENMASK(18, 16)
+#define ICE_CGU_R10_SYNCE_ETHDIV_M1 GENMASK(23, 19)
+#define ICE_CGU_R10_SYNCE_ETHDIV_LOAD BIT(24)
+#define ICE_CGU_R10_SYNCE_DCK2_RST BIT(25)
+#define ICE_CGU_R10_SYNCE_S_REF_CLK GENMASK(31, 27)
+
+#define ICE_CGU_R11 0x2C
+#define ICE_CGU_R11_SYNCE_S_BYP_CLK GENMASK(6, 1)
+
+#define ICE_CGU_BYPASS_MUX_OFFSET_E825C 3
+
/**
* enum ice_dpll_pin_sw - enumerate ice software pin indices:
* @ICE_DPLL_PIN_SW_1_IDX: index of first SW pin
@@ -157,19 +173,3 @@ static inline void ice_dpll_deinit(struct ice_pf *pf) { }
#endif
#endif
-
-#define ICE_CGU_R10 0x28
-#define ICE_CGU_R10_SYNCE_CLKO_SEL GENMASK(8, 5)
-#define ICE_CGU_R10_SYNCE_CLKODIV_M1 GENMASK(13, 9)
-#define ICE_CGU_R10_SYNCE_CLKODIV_LOAD BIT(14)
-#define ICE_CGU_R10_SYNCE_DCK_RST BIT(15)
-#define ICE_CGU_R10_SYNCE_ETHCLKO_SEL GENMASK(18, 16)
-#define ICE_CGU_R10_SYNCE_ETHDIV_M1 GENMASK(23, 19)
-#define ICE_CGU_R10_SYNCE_ETHDIV_LOAD BIT(24)
-#define ICE_CGU_R10_SYNCE_DCK2_RST BIT(25)
-#define ICE_CGU_R10_SYNCE_S_REF_CLK GENMASK(31, 27)
-
-#define ICE_CGU_R11 0x2C
-#define ICE_CGU_R11_SYNCE_S_BYP_CLK GENMASK(6, 1)
-
-#define ICE_CGU_BYPASS_MUX_OFFSET_E825C 3
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 1d1947a7fe11..e2fbe111f849 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -3682,7 +3682,7 @@ int ice_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
ret = ice_fltr_set_vsi_promisc(&vsi->back->hw, vsi->idx,
ICE_MCAST_VLAN_PROMISC_BITS,
vid);
- if (ret)
+ if (ret && ret != -EEXIST)
goto finish;
}
@@ -4104,6 +4104,12 @@ int ice_vsi_recfg_qs(struct ice_vsi *vsi, int new_rx, int new_tx, bool locked)
}
ice_pf_dcb_recfg(pf, locked);
ice_vsi_open(vsi);
+ /* Rx rings are reallocated during VSI rebuild and lose their ptp_rx
+ * flag. Restore timestamp mode so newly allocated rings are set up
+ * for hardware Rx timestamping.
+ */
+ if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
+ ice_ptp_restore_timestamp_mode(pf);
goto done;
rebuild_err:
@@ -8046,7 +8052,7 @@ int ice_set_rss_hfunc(struct ice_vsi *vsi, u8 hfunc)
ctx->info.q_opt_rss |=
FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_HASH_M, hfunc);
ctx->info.q_opt_tc = vsi->info.q_opt_tc;
- ctx->info.q_opt_flags = vsi->info.q_opt_rss;
+ ctx->info.q_opt_flags = vsi->info.q_opt_flags;
err = ice_update_vsi(hw, vsi->idx, ctx, NULL);
if (err) {
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
index 24fb7a3e14d6..2c18e16fe053 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -2141,16 +2141,23 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port)
}
incval = (u64)hi << 32 | lo;
+ if (!ice_ptp_lock(hw)) {
+ dev_err(ice_hw_to_dev(hw), "Failed to acquire PTP semaphore\n");
+ return -EBUSY;
+ }
+
err = ice_write_40b_ptp_reg_eth56g(hw, port, PHY_REG_TIMETUS_L, incval);
if (err)
- return err;
+ goto err_ptp_unlock;
err = ice_ptp_one_port_cmd(hw, port, ICE_PTP_INIT_INCVAL);
if (err)
- return err;
+ goto err_ptp_unlock;
ice_ptp_exec_tmr_cmd(hw);
+ ice_ptp_unlock(hw);
+
err = ice_sync_phy_timer_eth56g(hw, port);
if (err)
return err;
@@ -2166,6 +2173,10 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port)
ice_debug(hw, ICE_DBG_PTP, "Enabled clock on PHY port %u\n", port);
return 0;
+
+err_ptp_unlock:
+ ice_ptp_unlock(hw);
+ return err;
}
/**
@@ -4503,18 +4514,17 @@ static int
ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
{
struct ice_e810_params *params = &hw->ptp.phy.e810;
- unsigned long flags;
u32 val;
int err;
- spin_lock_irqsave(&params->atqbal_wq.lock, flags);
+ spin_lock_irq(&params->atqbal_wq.lock);
/* Wait for any pending in-progress low latency interrupt */
err = wait_event_interruptible_locked_irq(params->atqbal_wq,
!(params->atqbal_flags &
ATQBAL_FLAGS_INTR_IN_PROGRESS));
if (err) {
- spin_unlock_irqrestore(&params->atqbal_wq.lock, flags);
+ spin_unlock_irq(&params->atqbal_wq.lock);
return err;
}
@@ -4529,7 +4539,7 @@ ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
REG_LL_PROXY_H);
if (err) {
ice_debug(hw, ICE_DBG_PTP, "Failed to read PTP timestamp using low latency read\n");
- spin_unlock_irqrestore(&params->atqbal_wq.lock, flags);
+ spin_unlock_irq(&params->atqbal_wq.lock);
return err;
}
@@ -4539,7 +4549,7 @@ ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo)
/* Read the low 32 bit value and set the TS valid bit */
*lo = rd32(hw, REG_LL_PROXY_L) | TS_VALID;
- spin_unlock_irqrestore(&params->atqbal_wq.lock, flags);
+ spin_unlock_irq(&params->atqbal_wq.lock);
return 0;
}
@@ -5254,9 +5264,13 @@ static void ice_ptp_init_phy_e830(struct ice_ptp_hw *ptp)
*/
bool ice_ptp_lock(struct ice_hw *hw)
{
+ struct ice_pf *pf = container_of(hw, struct ice_pf, hw);
u32 hw_lock;
int i;
+ if (!ice_is_primary(hw))
+ hw = ice_get_primary_hw(pf);
+
#define MAX_TRIES 15
for (i = 0; i < MAX_TRIES; i++) {
@@ -5283,6 +5297,11 @@ bool ice_ptp_lock(struct ice_hw *hw)
*/
void ice_ptp_unlock(struct ice_hw *hw)
{
+ struct ice_pf *pf = container_of(hw, struct ice_pf, hw);
+
+ if (!ice_is_primary(hw))
+ hw = ice_get_primary_hw(pf);
+
wr32(hw, PFTSYN_SEM + (PFTSYN_SEM_BYTES * hw->pf_id), 0);
}
diff --git a/drivers/net/ethernet/intel/ice/virt/queues.c b/drivers/net/ethernet/intel/ice/virt/queues.c
index f73d5a3e83d4..31be2f76181c 100644
--- a/drivers/net/ethernet/intel/ice/virt/queues.c
+++ b/drivers/net/ethernet/intel/ice/virt/queues.c
@@ -840,7 +840,7 @@ int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg)
if (qpi->rxq.databuffer_size != 0 &&
(qpi->rxq.databuffer_size > ((16 * 1024) - 128) ||
- qpi->rxq.databuffer_size < 1024))
+ qpi->rxq.databuffer_size < 128))
goto error_param;
ring->rx_buf_len = qpi->rxq.databuffer_size;
diff --git a/drivers/net/ethernet/intel/idpf/idpf_idc.c b/drivers/net/ethernet/intel/idpf/idpf_idc.c
index 7e4f4ac92653..b7d6b08fc89e 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_idc.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_idc.c
@@ -90,7 +90,10 @@ static int idpf_plug_vport_aux_dev(struct iidc_rdma_core_dev_info *cdev_info,
return 0;
err_aux_dev_add:
+ ida_free(&idpf_idc_ida, adev->id);
+ vdev_info->adev = NULL;
auxiliary_device_uninit(adev);
+ return ret;
err_aux_dev_init:
ida_free(&idpf_idc_ida, adev->id);
err_ida_alloc:
@@ -228,7 +231,10 @@ static int idpf_plug_core_aux_dev(struct iidc_rdma_core_dev_info *cdev_info)
return 0;
err_aux_dev_add:
+ ida_free(&idpf_idc_ida, adev->id);
+ cdev_info->adev = NULL;
auxiliary_device_uninit(adev);
+ return ret;
err_aux_dev_init:
ida_free(&idpf_idc_ida, adev->id);
err_ida_alloc:
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
index eec91c4f0a75..4a51d2727547 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
@@ -952,6 +952,8 @@ int idpf_ptp_init(struct idpf_adapter *adapter)
goto free_ptp;
}
+ spin_lock_init(&adapter->ptp->read_dev_clk_lock);
+
err = idpf_ptp_create_clock(adapter);
if (err)
goto free_ptp;
@@ -977,8 +979,6 @@ int idpf_ptp_init(struct idpf_adapter *adapter)
goto remove_clock;
}
- spin_lock_init(&adapter->ptp->read_dev_clk_lock);
-
pci_dbg(adapter->pdev, "PTP init successful\n");
return 0;
diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c b/drivers/net/ethernet/intel/igc/igc_tsn.c
index 8a110145bfee..52de2bcbadbe 100644
--- a/drivers/net/ethernet/intel/igc/igc_tsn.c
+++ b/drivers/net/ethernet/intel/igc/igc_tsn.c
@@ -34,6 +34,7 @@ static int igc_fpe_init_smd_frame(struct igc_ring *ring,
return -ENOMEM;
}
+ buffer->type = IGC_TX_BUFFER_TYPE_SKB;
buffer->skb = skb;
buffer->protocol = 0;
buffer->bytecount = skb->len;
@@ -109,10 +110,16 @@ static int igc_fpe_xmit_smd_frame(struct igc_adapter *adapter,
__netif_tx_lock(nq, cpu);
err = igc_fpe_init_tx_descriptor(ring, skb, type);
- igc_flush_tx_descriptors(ring);
+ if (err)
+ goto err_free_skb_any;
+ igc_flush_tx_descriptors(ring);
__netif_tx_unlock(nq);
+ return 0;
+err_free_skb_any:
+ __netif_tx_unlock(nq);
+ dev_kfree_skb_any(skb);
return err;
}
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
index 42f89a179a3f..4ba3be961ab6 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
@@ -1221,6 +1221,7 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector,
ether_addr_equal(rx_ring->netdev->dev_addr,
eth_hdr(skb)->h_source)) {
dev_kfree_skb_irq(skb);
+ skb = NULL;
continue;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
index 4f33a816bc7a..2e94d5105016 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
@@ -1294,13 +1294,18 @@ static inline void link_status_user_format(u64 lstat,
struct cgx_link_user_info *linfo,
struct cgx *cgx, u8 lmac_id)
{
+ unsigned int speed;
+
linfo->link_up = FIELD_GET(RESP_LINKSTAT_UP, lstat);
linfo->full_duplex = FIELD_GET(RESP_LINKSTAT_FDUPLEX, lstat);
- linfo->speed = cgx_speed_mbps[FIELD_GET(RESP_LINKSTAT_SPEED, lstat)];
linfo->an = FIELD_GET(RESP_LINKSTAT_AN, lstat);
linfo->fec = FIELD_GET(RESP_LINKSTAT_FEC, lstat);
linfo->lmac_type_id = FIELD_GET(RESP_LINKSTAT_LMAC_TYPE, lstat);
+ speed = FIELD_GET(RESP_LINKSTAT_SPEED, lstat);
+ linfo->speed = speed < ARRAY_SIZE(cgx_speed_mbps) ?
+ cgx_speed_mbps[speed] : 0;
+
if (linfo->lmac_type_id >= LMAC_MODE_MAX) {
dev_err(&cgx->pdev->dev, "Unknown lmac_type_id %d reported by firmware on cgx port%d:%d",
linfo->lmac_type_id, cgx->cgx_id, lmac_id);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c
index 3debf2fae1a4..6f13296303cb 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c
@@ -249,34 +249,21 @@ DEFINE_SHOW_ATTRIBUTE(npc_defrag);
int npc_cn20k_debugfs_init(struct rvu *rvu)
{
struct npc_priv_t *npc_priv = npc_priv_get();
- struct dentry *npc_dentry;
- npc_dentry = debugfs_create_file("mcam_layout", 0444, rvu->rvu_dbg.npc,
- npc_priv, &npc_mcam_layout_fops);
+ debugfs_create_file("mcam_layout", 0444, rvu->rvu_dbg.npc,
+ npc_priv, &npc_mcam_layout_fops);
- if (!npc_dentry)
- return -EFAULT;
+ debugfs_create_file("mcam_default", 0444, rvu->rvu_dbg.npc,
+ rvu, &npc_mcam_default_fops);
- npc_dentry = debugfs_create_file("mcam_default", 0444, rvu->rvu_dbg.npc,
- rvu, &npc_mcam_default_fops);
+ debugfs_create_file("vidx2idx", 0444, rvu->rvu_dbg.npc,
+ npc_priv, &npc_vidx2idx_map_fops);
- if (!npc_dentry)
- return -EFAULT;
+ debugfs_create_file("idx2vidx", 0444, rvu->rvu_dbg.npc,
+ npc_priv, &npc_idx2vidx_map_fops);
- npc_dentry = debugfs_create_file("vidx2idx", 0444, rvu->rvu_dbg.npc,
- npc_priv, &npc_vidx2idx_map_fops);
- if (!npc_dentry)
- return -EFAULT;
-
- npc_dentry = debugfs_create_file("idx2vidx", 0444, rvu->rvu_dbg.npc,
- npc_priv, &npc_idx2vidx_map_fops);
- if (!npc_dentry)
- return -EFAULT;
-
- npc_dentry = debugfs_create_file("defrag", 0444, rvu->rvu_dbg.npc,
- npc_priv, &npc_defrag_fops);
- if (!npc_dentry)
- return -EFAULT;
+ debugfs_create_file("defrag", 0444, rvu->rvu_dbg.npc,
+ npc_priv, &npc_defrag_fops);
return 0;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
index 7291fdb89b03..6b3f453fd500 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
@@ -798,7 +798,7 @@ program_mkex_extr:
iounmap(mkex_prfl_addr);
}
-void
+int
npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
int index, bool enable)
{
@@ -808,7 +808,12 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
u64 cfg, hw_prio;
u8 kw_type;
- npc_mcam_idx_2_key_type(rvu, index, &kw_type);
+ if (index < 0 || index >= mcam->total_entries)
+ return -EINVAL;
+
+ if (npc_mcam_idx_2_key_type(rvu, index, &kw_type))
+ return -EINVAL;
+
if (kw_type == NPC_MCAM_KEY_X2) {
cfg = rvu_read64(rvu, blkaddr,
NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx,
@@ -819,7 +824,7 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
rvu_write64(rvu, blkaddr,
NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
cfg);
- return;
+ return 0;
}
/* For NPC_CN20K_MCAM_KEY_X4 keys, both the banks
@@ -836,10 +841,12 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
cfg);
}
+
+ return 0;
}
-void
-npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int bank, int index)
+static void
+npc_clear_x2_entry(struct rvu *rvu, int blkaddr, int bank, int index)
{
rvu_write64(rvu, blkaddr,
NPC_AF_CN20K_MCAMEX_BANKX_CAMX_INTF_EXT(index, bank, 1),
@@ -873,6 +880,33 @@ npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int bank, int index)
NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(index, bank), 0);
}
+int
+npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int mcam_idx)
+{
+ struct npc_mcam *mcam = &rvu->hw->mcam;
+ int bank = npc_get_bank(mcam, mcam_idx);
+ u8 kw_type;
+ int index;
+
+ if (npc_mcam_idx_2_key_type(rvu, mcam_idx, &kw_type))
+ return -EINVAL;
+
+ index = mcam_idx & (mcam->banksize - 1);
+
+ if (kw_type == NPC_MCAM_KEY_X2) {
+ npc_clear_x2_entry(rvu, blkaddr, bank, index);
+ return 0;
+ }
+
+ /* For NPC_MCAM_KEY_X4 keys, both the banks
+ * need to be programmed with the same value.
+ */
+ for (bank = 0; bank < mcam->banks_per_entry; bank++)
+ npc_clear_x2_entry(rvu, blkaddr, bank, index);
+
+ return 0;
+}
+
static void npc_cn20k_get_keyword(struct cn20k_mcam_entry *entry, int idx,
u64 *cam0, u64 *cam1)
{
@@ -1014,48 +1048,27 @@ static void npc_cn20k_config_kw_x4(struct rvu *rvu, struct npc_mcam *mcam,
kw, req_kw_type);
}
-static void
-npc_cn20k_set_mcam_bank_cfg(struct rvu *rvu, int blkaddr, int mcam_idx,
- int bank, u8 kw_type, bool enable, u8 hw_prio)
-{
- struct npc_mcam *mcam = &rvu->hw->mcam;
- u64 bank_cfg;
-
- bank_cfg = (u64)hw_prio << 24;
- if (enable)
- bank_cfg |= 0x1;
-
- if (kw_type == NPC_MCAM_KEY_X2) {
- rvu_write64(rvu, blkaddr,
- NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
- bank_cfg);
- return;
- }
-
- /* For NPC_MCAM_KEY_X4 keys, both the banks
- * need to be programmed with the same value.
- */
- for (bank = 0; bank < mcam->banks_per_entry; bank++) {
- rvu_write64(rvu, blkaddr,
- NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
- bank_cfg);
- }
-}
-
-void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
- u8 intf, struct cn20k_mcam_entry *entry,
- bool enable, u8 hw_prio, u8 req_kw_type)
+int npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
+ u8 intf, struct cn20k_mcam_entry *entry,
+ bool enable, u8 hw_prio, u8 req_kw_type)
{
struct npc_mcam *mcam = &rvu->hw->mcam;
int mcam_idx = index % mcam->banksize;
int bank = index / mcam->banksize;
+ u64 bank_cfg = (u64)hw_prio << 24;
int kw = 0;
u8 kw_type;
+ if (index < 0 || index >= mcam->total_entries)
+ return -EINVAL;
+
+ if (npc_mcam_idx_2_key_type(rvu, index, &kw_type))
+ return -EINVAL;
+
/* Disable before mcam entry update */
- npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, false);
+ if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, false))
+ return -EINVAL;
- npc_mcam_idx_2_key_type(rvu, index, &kw_type);
/* CAM1 takes the comparison value and
* CAM0 specifies match for a bit in key being '0' or '1' or 'dontcare'.
* CAM1<n> = 0 & CAM0<n> = 1 => match if key<n> = 0
@@ -1064,7 +1077,7 @@ void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
*/
if (kw_type == NPC_MCAM_KEY_X2) {
/* Clear mcam entry to avoid writes being suppressed by NPC */
- npc_cn20k_clear_mcam_entry(rvu, blkaddr, bank, mcam_idx);
+ npc_clear_x2_entry(rvu, blkaddr, bank, mcam_idx);
npc_cn20k_config_kw_x2(rvu, mcam, blkaddr,
mcam_idx, intf, entry,
bank, kw_type, kw, req_kw_type);
@@ -1085,44 +1098,55 @@ void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
bank, 1),
entry->vtag_action);
- goto set_cfg;
- }
-
- /* Clear mcam entry to avoid writes being suppressed by NPC */
- npc_cn20k_clear_mcam_entry(rvu, blkaddr, 0, mcam_idx);
- npc_cn20k_clear_mcam_entry(rvu, blkaddr, 1, mcam_idx);
- npc_cn20k_config_kw_x4(rvu, mcam, blkaddr,
- mcam_idx, intf, entry,
- kw_type, req_kw_type);
- for (bank = 0; bank < mcam->banks_per_entry; bank++) {
- /* Set 'action' */
+ /* Set HW priority */
rvu_write64(rvu, blkaddr,
- NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
- bank, 0),
- entry->action);
+ NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
+ bank_cfg);
- /* Set TAG 'action' */
- rvu_write64(rvu, blkaddr,
- NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
- bank, 1),
- entry->vtag_action);
+ } else {
+ /* Clear mcam entry to avoid writes being suppressed by NPC */
+ npc_clear_x2_entry(rvu, blkaddr, 0, mcam_idx);
+ npc_clear_x2_entry(rvu, blkaddr, 1, mcam_idx);
- /* Set 'action2' for inline receive */
- rvu_write64(rvu, blkaddr,
- NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
- bank, 2),
- entry->action2);
+ npc_cn20k_config_kw_x4(rvu, mcam, blkaddr,
+ mcam_idx, intf, entry,
+ kw_type, req_kw_type);
+ for (bank = 0; bank < mcam->banks_per_entry; bank++) {
+ /* Set 'action' */
+ rvu_write64(rvu, blkaddr,
+ NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
+ bank, 0),
+ entry->action);
+
+ /* Set TAG 'action' */
+ rvu_write64(rvu, blkaddr,
+ NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
+ bank, 1),
+ entry->vtag_action);
+
+ /* Set 'action2' for inline receive */
+ rvu_write64(rvu, blkaddr,
+ NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx,
+ bank, 2),
+ entry->action2);
+
+ /* Set HW priority */
+ rvu_write64(rvu, blkaddr,
+ NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank),
+ bank_cfg);
+ }
}
-set_cfg:
/* TODO: */
/* PF installing VF rule */
- npc_cn20k_set_mcam_bank_cfg(rvu, blkaddr, mcam_idx, bank,
- kw_type, enable, hw_prio);
+ if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, enable))
+ return -EINVAL;
+
+ return 0;
}
-void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest)
+int npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest)
{
struct npc_mcam *mcam = &rvu->hw->mcam;
u64 cfg, sreg, dreg, soff, doff;
@@ -1130,12 +1154,20 @@ void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest)
int bank, i, sb, db;
int dbank, sbank;
+ if (src >= mcam->total_entries || dest >= mcam->total_entries)
+ return -EINVAL;
+
dbank = npc_get_bank(mcam, dest);
sbank = npc_get_bank(mcam, src);
- npc_mcam_idx_2_key_type(rvu, src, &src_kwtype);
- npc_mcam_idx_2_key_type(rvu, dest, &dest_kwtype);
+
+ if (npc_mcam_idx_2_key_type(rvu, src, &src_kwtype))
+ return -EINVAL;
+
+ if (npc_mcam_idx_2_key_type(rvu, dest, &dest_kwtype))
+ return -EINVAL;
+
if (src_kwtype != dest_kwtype)
- return;
+ return -EINVAL;
src &= (mcam->banksize - 1);
dest &= (mcam->banksize - 1);
@@ -1170,6 +1202,8 @@ void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest)
if (src_kwtype == NPC_MCAM_KEY_X2)
break;
}
+
+ return 0;
}
static void npc_cn20k_fill_entryword(struct cn20k_mcam_entry *entry, int idx,
@@ -1179,21 +1213,37 @@ static void npc_cn20k_fill_entryword(struct cn20k_mcam_entry *entry, int idx,
entry->kw_mask[idx] = cam1 ^ cam0;
}
-void npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
- struct cn20k_mcam_entry *entry,
- u8 *intf, u8 *ena, u8 *hw_prio)
+int npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
+ struct cn20k_mcam_entry *entry,
+ u8 *intf, u8 *ena, u8 *hw_prio)
{
struct npc_mcam *mcam = &rvu->hw->mcam;
u64 cam0, cam1, bank_cfg, cfg;
int kw = 0, bank;
u8 kw_type;
- npc_mcam_idx_2_key_type(rvu, index, &kw_type);
+ if (index >= mcam->total_entries)
+ return -EINVAL;
+
+ if (npc_mcam_idx_2_key_type(rvu, index, &kw_type))
+ return -EINVAL;
bank = npc_get_bank(mcam, index);
index &= (mcam->banksize - 1);
cfg = rvu_read64(rvu, blkaddr,
+ NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, bank, 0));
+ entry->action = cfg;
+
+ cfg = rvu_read64(rvu, blkaddr,
+ NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, bank, 1));
+ entry->vtag_action = cfg;
+
+ cfg = rvu_read64(rvu, blkaddr,
+ NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, bank, 2));
+ entry->action2 = cfg;
+
+ cfg = rvu_read64(rvu, blkaddr,
NPC_AF_CN20K_MCAMEX_BANKX_CAMX_INTF_EXT(index,
bank, 1)) & 3;
*intf = cfg;
@@ -1242,7 +1292,7 @@ void npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
bank,
0));
npc_cn20k_fill_entryword(entry, kw + 3, cam0, cam1);
- goto read_action;
+ return 0;
}
for (bank = 0; bank < mcam->banks_per_entry; bank++, kw = kw + 4) {
@@ -1287,17 +1337,7 @@ void npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
npc_cn20k_fill_entryword(entry, kw + 3, cam0, cam1);
}
-read_action:
- /* 'action' is set to same value for both bank '0' and '1'.
- * Hence, reading bank '0' should be enough.
- */
- cfg = rvu_read64(rvu, blkaddr,
- NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, 0, 0));
- entry->action = cfg;
-
- cfg = rvu_read64(rvu, blkaddr,
- NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, 0, 1));
- entry->vtag_action = cfg;
+ return 0;
}
int rvu_mbox_handler_npc_cn20k_mcam_write_entry(struct rvu *rvu,
@@ -1335,11 +1375,10 @@ int rvu_mbox_handler_npc_cn20k_mcam_write_entry(struct rvu *rvu,
if (is_pffunc_af(req->hdr.pcifunc))
nix_intf = req->intf;
- npc_cn20k_config_mcam_entry(rvu, blkaddr, req->entry, nix_intf,
- &req->entry_data, req->enable_entry,
- req->hw_prio, req->req_kw_type);
+ rc = npc_cn20k_config_mcam_entry(rvu, blkaddr, req->entry, nix_intf,
+ &req->entry_data, req->enable_entry,
+ req->hw_prio, req->req_kw_type);
- rc = 0;
exit:
mutex_unlock(&mcam->lock);
return rc;
@@ -1361,11 +1400,13 @@ int rvu_mbox_handler_npc_cn20k_mcam_read_entry(struct rvu *rvu,
mutex_lock(&mcam->lock);
rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry);
- if (!rc)
- npc_cn20k_read_mcam_entry(rvu, blkaddr, req->entry,
- &rsp->entry_data, &rsp->intf,
- &rsp->enable, &rsp->hw_prio);
+ if (rc)
+ goto fail;
+ rc = npc_cn20k_read_mcam_entry(rvu, blkaddr, req->entry,
+ &rsp->entry_data, &rsp->intf,
+ &rsp->enable, &rsp->hw_prio);
+fail:
mutex_unlock(&mcam->lock);
return rc;
}
@@ -1375,11 +1416,13 @@ int rvu_mbox_handler_npc_cn20k_mcam_alloc_and_write_entry(struct rvu *rvu,
struct npc_mcam_alloc_and_write_entry_rsp *rsp)
{
struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc);
+ struct npc_mcam_free_entry_req free_req = { 0 };
struct npc_mcam_alloc_entry_req entry_req;
struct npc_mcam_alloc_entry_rsp entry_rsp;
struct npc_mcam *mcam = &rvu->hw->mcam;
u16 entry = NPC_MCAM_ENTRY_INVALID;
- int blkaddr, rc;
+ struct msg_rsp free_rsp;
+ int blkaddr, rc, err;
u8 nix_intf;
blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
@@ -1415,12 +1458,23 @@ int rvu_mbox_handler_npc_cn20k_mcam_alloc_and_write_entry(struct rvu *rvu,
else
nix_intf = pfvf->nix_rx_intf;
- npc_cn20k_config_mcam_entry(rvu, blkaddr, entry, nix_intf,
- &req->entry_data, req->enable_entry,
- req->hw_prio, req->req_kw_type);
+ rc = npc_cn20k_config_mcam_entry(rvu, blkaddr, entry, nix_intf,
+ &req->entry_data, req->enable_entry,
+ req->hw_prio, req->req_kw_type);
mutex_unlock(&mcam->lock);
+ if (rc) {
+ free_req.hdr.pcifunc = req->hdr.pcifunc;
+ free_req.entry = entry_rsp.entry;
+ err = rvu_mbox_handler_npc_mcam_free_entry(rvu, &free_req, &free_rsp);
+ if (err)
+ dev_err(rvu->dev,
+ "%s: Error to free mcam idx %u\n",
+ __func__, entry_rsp.entry);
+ return rc;
+ }
+
rsp->entry = entry_rsp.entry;
return 0;
}
@@ -1480,9 +1534,9 @@ int rvu_mbox_handler_npc_cn20k_read_base_steer_rule(struct rvu *rvu,
read_entry:
/* Read the mcam entry */
- npc_cn20k_read_mcam_entry(rvu, blkaddr, index,
- &rsp->entry, &intf,
- &enable, &hw_prio);
+ rc = npc_cn20k_read_mcam_entry(rvu, blkaddr, index,
+ &rsp->entry, &intf,
+ &enable, &hw_prio);
mutex_unlock(&mcam->lock);
out:
return rc;
@@ -2305,6 +2359,7 @@ err2:
__npc_subbank_mark_free(rvu, sb);
err1:
kfree(save);
+ *alloc_cnt = 0;
return rc;
}
@@ -3482,7 +3537,7 @@ static int npc_defrag_alloc_free_slots(struct rvu *rvu,
{
int alloc_cnt1, alloc_cnt2;
struct npc_subbank *sb;
- int rc, sb_off, i;
+ int rc, sb_off, i, err;
bool deleted;
sb = &npc_priv.sb[f->idx];
@@ -3496,6 +3551,7 @@ static int npc_defrag_alloc_free_slots(struct rvu *rvu,
NPC_MCAM_LOWER_PRIO,
false, cnt, save, cnt, true,
&alloc_cnt1);
+
if (alloc_cnt1 < cnt) {
rc = __npc_subbank_alloc(rvu, sb,
NPC_MCAM_KEY_X2, sb->b1b,
@@ -3511,15 +3567,17 @@ static int npc_defrag_alloc_free_slots(struct rvu *rvu,
dev_err(rvu->dev,
"%s: Failed to alloc cnt=%u alloc_cnt1=%u alloc_cnt2=%u\n",
__func__, cnt, alloc_cnt1, alloc_cnt2);
+ rc = -ENOSPC;
goto fail_free_alloc;
}
+
return 0;
fail_free_alloc:
for (i = 0; i < alloc_cnt1 + alloc_cnt2; i++) {
- rc = npc_mcam_idx_2_subbank_idx(rvu, save[i],
- &sb, &sb_off);
- if (rc) {
+ err = npc_mcam_idx_2_subbank_idx(rvu, save[i],
+ &sb, &sb_off);
+ if (err) {
dev_err(rvu->dev,
"%s: Error to find subbank for mcam idx=%u\n",
__func__, save[i]);
@@ -3565,9 +3623,10 @@ int npc_defrag_move_vdx_to_free(struct rvu *rvu,
struct npc_defrag_node *v,
int cnt, u16 *save)
{
+ u16 new_midx, old_midx, vidx, target_pf;
struct npc_mcam *mcam = &rvu->hw->mcam;
+ struct rvu_npc_mcam_rule *rule, *tmp;
int i, vidx_cnt, rc, sb_off;
- u16 new_midx, old_midx, vidx;
struct npc_subbank *sb;
bool deleted;
u16 pcifunc;
@@ -3607,9 +3666,30 @@ int npc_defrag_move_vdx_to_free(struct rvu *rvu,
NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(midx,
bank));
- npc_cn20k_enable_mcam_entry(rvu, blkaddr, old_midx, false);
- npc_cn20k_copy_mcam_entry(rvu, blkaddr, old_midx, new_midx);
- npc_cn20k_enable_mcam_entry(rvu, blkaddr, new_midx, true);
+ /* If bug happened during copy/enable mcam, then there is a bug in allocation
+ * algorithm itself. There is no point in rewinding and returning, as it
+ * will face further issue. Return error after printing error
+ */
+ if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, old_midx, false)) {
+ dev_err(rvu->dev,
+ "%s: Error happened while disabling old_mid=%u\n",
+ __func__, old_midx);
+ return -EFAULT;
+ }
+
+ if (npc_cn20k_copy_mcam_entry(rvu, blkaddr, old_midx, new_midx)) {
+ dev_err(rvu->dev,
+ "%s: Error happened while copying old_midx=%u new_midx=%u\n",
+ __func__, old_midx, new_midx);
+ return -EFAULT;
+ }
+
+ if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, new_midx, true)) {
+ dev_err(rvu->dev,
+ "%s: Error happened while enabling new_mid=%u\n",
+ __func__, new_midx);
+ return -EFAULT;
+ }
midx = new_midx % mcam->banksize;
bank = new_midx / mcam->banksize;
@@ -3665,8 +3745,21 @@ int npc_defrag_move_vdx_to_free(struct rvu *rvu,
mcam->entry2pfvf_map[new_midx] = pcifunc;
/* Counter is not preserved */
mcam->entry2cntr_map[new_midx] = new_midx;
+ target_pf = mcam->entry2target_pffunc[old_midx];
+ mcam->entry2target_pffunc[new_midx] = target_pf;
+ mcam->entry2target_pffunc[old_midx] = NPC_MCAM_INVALID_MAP;
+
npc_mcam_set_bit(mcam, new_midx);
+ /* Note: list order is not functionally required for mcam_rules */
+ list_for_each_entry_safe(rule, tmp, &mcam->mcam_rules, list) {
+ if (rule->entry != old_midx)
+ continue;
+
+ rule->entry = new_midx;
+ break;
+ }
+
/* Mark as invalid */
v->vidx[vidx_cnt - i - 1] = -1;
save[cnt - i - 1] = -1;
@@ -3935,6 +4028,13 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast,
void *val;
int i, j;
+ for (i = 0; i < ARRAY_SIZE(ptr); i++) {
+ if (!ptr[i])
+ continue;
+
+ *ptr[i] = USHRT_MAX;
+ }
+
if (!npc_priv.init_done)
return 0;
@@ -3950,7 +4050,6 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast,
npc_dft_rule_name[NPC_DFT_RULE_PROMISC_ID],
pcifunc);
- *ptr[0] = USHRT_MAX;
return -ESRCH;
}
@@ -3970,7 +4069,6 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast,
npc_dft_rule_name[NPC_DFT_RULE_UCAST_ID],
pcifunc);
- *ptr[3] = USHRT_MAX;
return -ESRCH;
}
@@ -3990,7 +4088,6 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast,
__func__,
npc_dft_rule_name[i], pcifunc);
- *ptr[j] = USHRT_MAX;
continue;
}
@@ -4085,7 +4182,7 @@ int rvu_mbox_handler_npc_get_dft_rl_idxs(struct rvu *rvu, struct msg_req *req,
return 0;
}
-static bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc)
+bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc)
{
return is_pf_cgxmapped(rvu, rvu_get_pf(rvu->pdev, pcifunc)) ||
is_lbk_vf(rvu, pcifunc);
@@ -4093,11 +4190,11 @@ static bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc)
void npc_cn20k_dft_rules_free(struct rvu *rvu, u16 pcifunc)
{
- struct npc_mcam_free_entry_req free_req = { 0 };
+ struct npc_mcam *mcam = &rvu->hw->mcam;
+ u16 ptr[4] = {[0 ... 3] = USHRT_MAX};
+ struct rvu_npc_mcam_rule *rule, *tmp;
unsigned long index;
- struct msg_rsp rsp;
- u16 ptr[4];
- int rc, i;
+ int blkaddr, rc, i;
void *map;
if (!npc_priv.init_done)
@@ -4155,14 +4252,43 @@ void npc_cn20k_dft_rules_free(struct rvu *rvu, u16 pcifunc)
}
free_rules:
+ blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+ if (blkaddr < 0)
+ return;
+ for (int i = 0; i < 4; i++) {
+ if (ptr[i] == USHRT_MAX)
+ continue;
- free_req.hdr.pcifunc = pcifunc;
- free_req.all = 1;
- rc = rvu_mbox_handler_npc_mcam_free_entry(rvu, &free_req, &rsp);
- if (rc)
- dev_err(rvu->dev,
- "%s: Error deleting default entries (pcifunc=%#x\n",
- __func__, pcifunc);
+ mutex_lock(&mcam->lock);
+ npc_mcam_clear_bit(mcam, ptr[i]);
+ mcam->entry2pfvf_map[ptr[i]] = NPC_MCAM_INVALID_MAP;
+ npc_cn20k_enable_mcam_entry(rvu, blkaddr, ptr[i], false);
+ mcam->entry2target_pffunc[ptr[i]] = 0x0;
+ mutex_unlock(&mcam->lock);
+
+ rc = npc_cn20k_idx_free(rvu, &ptr[i], 1);
+ if (rc) {
+ /* Non recoverable error. Let us WARN and return. Keep system alive to
+ * enable debugging
+ */
+ WARN(1, "%s Error deleting default entries (pcifunc=%#x) mcam_idx=%u\n",
+ __func__, pcifunc, ptr[i]);
+ return;
+ }
+ }
+
+ mutex_lock(&mcam->lock);
+ list_for_each_entry_safe(rule, tmp, &mcam->mcam_rules, list) {
+ for (int i = 0; i < 4; i++) {
+ if (ptr[i] != rule->entry)
+ continue;
+
+ list_del(&rule->list);
+ kfree(rule);
+ break;
+ }
+ }
+ mutex_unlock(&mcam->lock);
}
int npc_cn20k_dft_rules_alloc(struct rvu *rvu, u16 pcifunc)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h
index 815d0b257a7e..3d5eb952cc07 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h
@@ -320,21 +320,21 @@ void npc_cn20k_dft_rules_free(struct rvu *rvu, u16 pcifunc);
int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast,
u16 *mcast, u16 *promisc, u16 *ucast);
-void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
- u8 intf, struct cn20k_mcam_entry *entry,
- bool enable, u8 hw_prio, u8 req_kw_type);
-void npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
- int index, bool enable);
-void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr,
- u16 src, u16 dest);
-void npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
- struct cn20k_mcam_entry *entry, u8 *intf,
- u8 *ena, u8 *hw_prio);
-void npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr,
- int bank, int index);
+int npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index,
+ u8 intf, struct cn20k_mcam_entry *entry,
+ bool enable, u8 hw_prio, u8 req_kw_type);
+int npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr,
+ int index, bool enable);
+int npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr,
+ u16 src, u16 dest);
+int npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index,
+ struct cn20k_mcam_entry *entry, u8 *intf,
+ u8 *ena, u8 *hw_prio);
+int npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int index);
int npc_mcam_idx_2_key_type(struct rvu *rvu, u16 mcam_idx, u8 *key_type);
u16 npc_cn20k_vidx2idx(u16 index);
u16 npc_cn20k_idx2vidx(u16 idx);
int npc_cn20k_defrag(struct rvu *rvu);
+bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc);
#endif /* NPC_CN20K_H */
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
index e40b79076358..3cf131508ecf 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
@@ -436,7 +436,7 @@ struct rvu_pfvf *rvu_get_pfvf(struct rvu *rvu, int pcifunc)
return &rvu->pf[rvu_get_pf(rvu->pdev, pcifunc)];
}
-static bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc)
+bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc)
{
int pf, vf, nvfs;
u64 cfg;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
index a466181cf908..65397daae4c2 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
@@ -917,6 +917,7 @@ u16 rvu_get_rsrc_mapcount(struct rvu_pfvf *pfvf, int blkaddr);
struct rvu_pfvf *rvu_get_pfvf(struct rvu *rvu, int pcifunc);
void rvu_get_pf_numvfs(struct rvu *rvu, int pf, int *numvfs, int *hwvf);
bool is_block_implemented(struct rvu_hwinfo *hw, int blkaddr);
+bool is_pf_func_valid(struct rvu *rvu, u16 pcifunc);
bool is_pffunc_map_valid(struct rvu *rvu, u16 pcifunc, int blktype);
int rvu_get_lf(struct rvu *rvu, struct rvu_block *block, u16 pcifunc, u16 slot);
int rvu_lf_reset(struct rvu *rvu, struct rvu_block *block, int lf);
@@ -1144,6 +1145,7 @@ int rvu_cpt_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int lf,
int slot);
int rvu_cpt_ctx_flush(struct rvu *rvu, u16 pcifunc);
int rvu_cpt_init(struct rvu *rvu);
+u32 rvu_get_cpt_chan_mask(struct rvu *rvu);
#define NDC_AF_BANK_MASK GENMASK_ULL(7, 0)
#define NDC_AF_BANK_LINE_MASK GENMASK_ULL(31, 16)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
index ef5b081162eb..f977734ae712 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
@@ -3577,6 +3577,9 @@ static int nix_update_mce_rule(struct rvu *rvu, u16 pcifunc,
mcam_index = npc_get_nixlf_mcam_index(mcam,
pcifunc & ~RVU_PFVF_FUNC_MASK,
nixlf, type);
+ if (mcam_index < 0)
+ return -EINVAL;
+
err = nix_update_mce_list(rvu, pcifunc, mce_list,
mce_idx, mcam_index, add);
return err;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
index c2ca5ed1d028..d301a3f0f87a 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
@@ -163,14 +163,35 @@ int npc_get_nixlf_mcam_index(struct npc_mcam *mcam,
if (rc)
return -EFAULT;
+ if (is_lbk_vf(rvu, pcifunc)) {
+ if (promisc == USHRT_MAX)
+ return -EINVAL;
+ return promisc;
+ }
+
+ if (is_cgx_vf(rvu, pcifunc)) {
+ if (ucast == USHRT_MAX)
+ return -EINVAL;
+
+ return ucast;
+ }
+
switch (type) {
case NIXLF_BCAST_ENTRY:
+ if (bcast == USHRT_MAX)
+ return -EINVAL;
return bcast;
case NIXLF_ALLMULTI_ENTRY:
+ if (mcast == USHRT_MAX)
+ return -EINVAL;
return mcast;
case NIXLF_PROMISC_ENTRY:
+ if (promisc == USHRT_MAX)
+ return -EINVAL;
return promisc;
case NIXLF_UCAST_ENTRY:
+ if (ucast == USHRT_MAX)
+ return -EINVAL;
return ucast;
default:
return -EINVAL;
@@ -238,10 +259,10 @@ void npc_enable_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
int actbank = bank;
if (is_cn20k(rvu->pdev)) {
- if (index < 0 || index >= mcam->banksize * mcam->banks)
- return;
-
- return npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, enable);
+ if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, enable))
+ dev_err(rvu->dev, "Error to %s mcam %u entry\n",
+ enable ? "enable" : "disable", index);
+ return;
}
index &= (mcam->banksize - 1);
@@ -258,6 +279,13 @@ static void npc_clear_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
int bank = npc_get_bank(mcam, index);
int actbank = bank;
+ if (is_cn20k(rvu->pdev)) {
+ if (npc_cn20k_clear_mcam_entry(rvu, blkaddr, index))
+ dev_err(rvu->dev, "%s Failed to clear mcam %u\n",
+ __func__, index);
+ return;
+ }
+
index &= (mcam->banksize - 1);
for (; bank < (actbank + mcam->banks_per_entry); bank++) {
rvu_write64(rvu, blkaddr,
@@ -424,6 +452,15 @@ static u64 npc_get_default_entry_action(struct rvu *rvu, struct npc_mcam *mcam,
index = npc_get_nixlf_mcam_index(mcam, pf_func, nixlf,
NIXLF_UCAST_ENTRY);
+
+ if (index < 0) {
+ dev_err(rvu->dev,
+ "%s: failed to get ucast entry pcifunc:0x%x\n",
+ __func__, pf_func);
+ /* Action 0 is drop */
+ return 0;
+ }
+
bank = npc_get_bank(mcam, index);
index &= (mcam->banksize - 1);
@@ -589,8 +626,8 @@ void npc_read_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
NPC_AF_MCAMEX_BANKX_CFG(src, sbank)) & 1;
}
-static void npc_copy_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
- int blkaddr, u16 src, u16 dest)
+static int npc_copy_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
+ int blkaddr, u16 src, u16 dest)
{
int dbank = npc_get_bank(mcam, dest);
int sbank = npc_get_bank(mcam, src);
@@ -630,6 +667,7 @@ static void npc_copy_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam,
NPC_AF_MCAMEX_BANKX_CFG(src, sbank));
rvu_write64(rvu, blkaddr,
NPC_AF_MCAMEX_BANKX_CFG(dest, dbank), cfg);
+ return 0;
}
u64 npc_get_mcam_action(struct rvu *rvu, struct npc_mcam *mcam,
@@ -663,6 +701,19 @@ void npc_set_mcam_action(struct rvu *rvu, struct npc_mcam *mcam,
return rvu_write64(rvu, blkaddr, reg, cfg);
}
+u32 rvu_get_cpt_chan_mask(struct rvu *rvu)
+{
+ /* For cn10k the upper two bits of the channel number are
+ * cpt channel number. with masking out these bits in the
+ * mcam entry, same entry used for NIX will allow packets
+ * received from cpt for parsing.
+ */
+ if (!is_rvu_otx2(rvu))
+ return NIX_CHAN_CPT_X2P_MASK;
+ else
+ return 0xFFFu;
+}
+
void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc,
int nixlf, u64 chan, u8 *mac_addr)
{
@@ -689,6 +740,12 @@ void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc,
index = npc_get_nixlf_mcam_index(mcam, pcifunc,
nixlf, NIXLF_UCAST_ENTRY);
+ if (index < 0) {
+ dev_err(rvu->dev,
+ "%s: Error to get ucast entry for pcifunc=%#x\n",
+ __func__, pcifunc);
+ return;
+ }
/* Don't change the action if entry is already enabled
* Otherwise RSS action may get overwritten.
@@ -706,7 +763,7 @@ void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc,
eth_broadcast_addr((u8 *)&req.mask.dmac);
req.features = BIT_ULL(NPC_DMAC);
req.channel = chan;
- req.chan_mask = 0xFFFU;
+ req.chan_mask = rvu_get_cpt_chan_mask(rvu);
req.intf = pfvf->nix_rx_intf;
req.op = action.op;
req.hdr.pcifunc = 0; /* AF is requester */
@@ -744,16 +801,38 @@ void rvu_npc_install_promisc_entry(struct rvu *rvu, u16 pcifunc,
index = npc_get_nixlf_mcam_index(mcam, pcifunc,
nixlf, NIXLF_PROMISC_ENTRY);
+ /* In cn20k, default indexes are installed only for CGX mapped
+ * and lbk interfaces
+ */
if (is_cgx_vf(rvu, pcifunc))
index = npc_get_nixlf_mcam_index(mcam,
pcifunc & ~RVU_PFVF_FUNC_MASK,
nixlf, NIXLF_PROMISC_ENTRY);
+ if (index < 0) {
+ dev_err(rvu->dev,
+ "%s: Error to get promisc entry for pcifunc=%#x\n",
+ __func__, pcifunc);
+ return;
+ }
+
/* If the corresponding PF's ucast action is RSS,
* use the same action for promisc also
+ * Please note that for lbk(s) "index" and "ucast_idx"
+ * will be same.
*/
- ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc,
- nixlf, NIXLF_UCAST_ENTRY);
+ if (is_lbk_vf(rvu, pcifunc))
+ ucast_idx = index;
+ else
+ ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc,
+ nixlf, NIXLF_UCAST_ENTRY);
+ if (ucast_idx < 0) {
+ dev_err(rvu->dev,
+ "%s: Error to get ucast/promisc entry for pcifunc=%#x\n",
+ __func__, pcifunc);
+ return;
+ }
+
if (is_mcam_entry_enabled(rvu, mcam, blkaddr, ucast_idx))
*(u64 *)&action = npc_get_mcam_action(rvu, mcam,
blkaddr, ucast_idx);
@@ -779,11 +858,7 @@ void rvu_npc_install_promisc_entry(struct rvu *rvu, u16 pcifunc,
* mcam entry, same entry used for NIX will allow packets
* received from cpt for parsing.
*/
- if (!is_rvu_otx2(rvu)) {
- req.chan_mask = NIX_CHAN_CPT_X2P_MASK;
- } else {
- req.chan_mask = 0xFFFU;
- }
+ req.chan_mask = rvu_get_cpt_chan_mask(rvu);
if (chan_cnt > 1) {
if (!is_power_of_2(chan_cnt)) {
@@ -827,6 +902,14 @@ void rvu_npc_enable_promisc_entry(struct rvu *rvu, u16 pcifunc,
index = npc_get_nixlf_mcam_index(mcam, pcifunc,
nixlf, NIXLF_PROMISC_ENTRY);
+
+ if (index < 0) {
+ dev_err(rvu->dev,
+ "%s: Error to get promisc entry for pcifunc=%#x\n",
+ __func__, pcifunc);
+ return;
+ }
+
npc_enable_mcam_entry(rvu, mcam, blkaddr, index, enable);
}
@@ -867,6 +950,12 @@ void rvu_npc_install_bcast_match_entry(struct rvu *rvu, u16 pcifunc,
index = npc_get_nixlf_mcam_index(mcam, pcifunc,
nixlf, NIXLF_BCAST_ENTRY);
+ if (index < 0) {
+ dev_err(rvu->dev,
+ "%s: Error to get bcast entry for pcifunc=%#x\n",
+ __func__, pcifunc);
+ return;
+ }
if (!hw->cap.nix_rx_multicast) {
/* Early silicon doesn't support pkt replication,
@@ -910,7 +999,7 @@ void rvu_npc_install_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf,
u16 vf_func;
/* Only CGX PF/VF can add allmulticast entry */
- if (is_lbk_vf(rvu, pcifunc) && is_sdp_vf(rvu, pcifunc))
+ if (is_lbk_vf(rvu, pcifunc) || is_sdp_vf(rvu, pcifunc))
return;
blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
@@ -931,12 +1020,25 @@ void rvu_npc_install_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf,
index = npc_get_nixlf_mcam_index(mcam, pcifunc,
nixlf, NIXLF_ALLMULTI_ENTRY);
+ if (index < 0) {
+ dev_err(rvu->dev,
+ "%s: Error to get mcast entry for pcifunc=%#x\n",
+ __func__, pcifunc);
+ return;
+ }
/* If the corresponding PF's ucast action is RSS,
* use the same action for multicast entry also
*/
ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc,
nixlf, NIXLF_UCAST_ENTRY);
+ if (ucast_idx < 0) {
+ dev_err(rvu->dev,
+ "%s: Error to get ucast entry for pcifunc=%#x\n",
+ __func__, pcifunc);
+ return;
+ }
+
if (is_mcam_entry_enabled(rvu, mcam, blkaddr, ucast_idx))
*(u64 *)&action = npc_get_mcam_action(rvu, mcam,
blkaddr, ucast_idx);
@@ -960,16 +1062,7 @@ void rvu_npc_install_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf,
ether_addr_copy(req.mask.dmac, mac_addr);
req.features = BIT_ULL(NPC_DMAC);
- /* For cn10k the upper two bits of the channel number are
- * cpt channel number. with masking out these bits in the
- * mcam entry, same entry used for NIX will allow packets
- * received from cpt for parsing.
- */
- if (!is_rvu_otx2(rvu))
- req.chan_mask = NIX_CHAN_CPT_X2P_MASK;
- else
- req.chan_mask = 0xFFFU;
-
+ req.chan_mask = rvu_get_cpt_chan_mask(rvu);
req.channel = chan;
req.intf = pfvf->nix_rx_intf;
req.entry = index;
@@ -1001,6 +1094,13 @@ void rvu_npc_enable_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf,
index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf,
NIXLF_ALLMULTI_ENTRY);
+ if (index < 0) {
+ dev_err(rvu->dev,
+ "%s: Error to get mcast entry for pcifunc=%#x\n",
+ __func__, pcifunc);
+ return;
+ }
+
npc_enable_mcam_entry(rvu, mcam, blkaddr, index, enable);
}
@@ -1113,8 +1213,12 @@ void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf,
index = mcam_index;
}
- if (index >= mcam->total_entries)
+ if (index < 0 || index >= mcam->total_entries) {
+ dev_err(rvu->dev,
+ "%s: Invalid mcam index, pcifunc=%#x\n",
+ __func__, pcifunc);
return;
+ }
bank = npc_get_bank(mcam, index);
index &= (mcam->banksize - 1);
@@ -1158,16 +1262,18 @@ void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf,
/* If PF's promiscuous entry is enabled,
* Set RSS action for that entry as well
*/
- npc_update_rx_action_with_alg_idx(rvu, action, pfvf, index,
- blkaddr, alg_idx);
+ if (index >= 0)
+ npc_update_rx_action_with_alg_idx(rvu, action, pfvf, index,
+ blkaddr, alg_idx);
index = npc_get_nixlf_mcam_index(mcam, pcifunc,
nixlf, NIXLF_ALLMULTI_ENTRY);
/* If PF's allmulti entry is enabled,
* Set RSS action for that entry as well
*/
- npc_update_rx_action_with_alg_idx(rvu, action, pfvf, index,
- blkaddr, alg_idx);
+ if (index >= 0)
+ npc_update_rx_action_with_alg_idx(rvu, action, pfvf, index,
+ blkaddr, alg_idx);
}
}
@@ -1180,12 +1286,22 @@ void npc_enadis_default_mce_entry(struct rvu *rvu, u16 pcifunc,
int index, blkaddr, mce_idx;
struct rvu_pfvf *pfvf;
+ /* multicast pkt replication is not enabled for AF's VFs & SDP links */
+ if (is_lbk_vf(rvu, pcifunc) || is_sdp_pfvf(rvu, pcifunc))
+ return;
+
blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
if (blkaddr < 0)
return;
index = npc_get_nixlf_mcam_index(mcam, pcifunc & ~RVU_PFVF_FUNC_MASK,
nixlf, type);
+ if (index < 0) {
+ dev_err(rvu->dev,
+ "%s: Error to get entry for pcifunc=%#x, type=%u\n",
+ __func__, pcifunc, type);
+ return;
+ }
/* disable MCAM entry when packet replication is not supported by hw */
if (!hw->cap.nix_rx_multicast && !is_vf(pcifunc)) {
@@ -1214,6 +1330,10 @@ static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc,
struct npc_mcam *mcam = &rvu->hw->mcam;
int index, blkaddr;
+ /* only CGX or LBK interfaces have default entries */
+ if (is_cn20k(rvu->pdev) && !npc_is_cgx_or_lbk(rvu, pcifunc))
+ return;
+
blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
if (blkaddr < 0)
return;
@@ -1223,6 +1343,12 @@ static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc,
pfvf->nix_rx_intf)) {
index = npc_get_nixlf_mcam_index(mcam, pcifunc,
nixlf, NIXLF_UCAST_ENTRY);
+ if (index < 0) {
+ dev_err(rvu->dev,
+ "%s: Error to get ucast entry for pcifunc=%#x\n",
+ __func__, pcifunc);
+ return;
+ }
npc_enable_mcam_entry(rvu, mcam, blkaddr, index, enable);
}
@@ -2066,8 +2192,8 @@ int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr)
goto free_entry_cntr_map;
/* Alloc memory for saving target device of mcam rule */
- mcam->entry2target_pffunc = kmalloc_array(mcam->total_entries,
- sizeof(u16), GFP_KERNEL);
+ mcam->entry2target_pffunc = kcalloc(mcam->total_entries,
+ sizeof(u16), GFP_KERNEL);
if (!mcam->entry2target_pffunc)
goto free_cntr_refcnt;
@@ -2504,33 +2630,58 @@ void npc_mcam_clear_bit(struct npc_mcam *mcam, u16 index)
static void npc_mcam_free_all_entries(struct rvu *rvu, struct npc_mcam *mcam,
int blkaddr, u16 pcifunc)
{
+ u16 dft_idxs[NPC_DFT_RULE_MAX_ID] = {[0 ... NPC_DFT_RULE_MAX_ID - 1] = USHRT_MAX};
+ bool cn20k_dft_rl;
u16 index, cntr;
int rc;
+ npc_cn20k_dft_rules_idx_get(rvu, pcifunc,
+ &dft_idxs[NPC_DFT_RULE_BCAST_ID],
+ &dft_idxs[NPC_DFT_RULE_MCAST_ID],
+ &dft_idxs[NPC_DFT_RULE_PROMISC_ID],
+ &dft_idxs[NPC_DFT_RULE_UCAST_ID]);
+
/* Scan all MCAM entries and free the ones mapped to 'pcifunc' */
for (index = 0; index < mcam->bmap_entries; index++) {
- if (mcam->entry2pfvf_map[index] == pcifunc) {
+ if (mcam->entry2pfvf_map[index] != pcifunc)
+ continue;
+
+ cn20k_dft_rl = false;
+
+ if (is_cn20k(rvu->pdev)) {
+ if (dft_idxs[NPC_DFT_RULE_BCAST_ID] == index ||
+ dft_idxs[NPC_DFT_RULE_MCAST_ID] == index ||
+ dft_idxs[NPC_DFT_RULE_PROMISC_ID] == index ||
+ dft_idxs[NPC_DFT_RULE_UCAST_ID] == index) {
+ cn20k_dft_rl = true;
+ }
+ }
+
+ /* Disable the entry */
+ npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
+
+ if (!cn20k_dft_rl) {
mcam->entry2pfvf_map[index] = NPC_MCAM_INVALID_MAP;
/* Free the entry in bitmap */
npc_mcam_clear_bit(mcam, index);
- /* Disable the entry */
- npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
-
- /* Update entry2counter mapping */
- cntr = mcam->entry2cntr_map[index];
- if (cntr != NPC_MCAM_INVALID_MAP)
- npc_unmap_mcam_entry_and_cntr(rvu, mcam,
- blkaddr, index,
- cntr);
mcam->entry2target_pffunc[index] = 0x0;
- if (is_cn20k(rvu->pdev)) {
- rc = npc_cn20k_idx_free(rvu, &index, 1);
- if (rc)
- dev_err(rvu->dev,
- "Failed to free mcam idx=%u pcifunc=%#x\n",
- index, pcifunc);
- }
}
+
+ /* Update entry2counter mapping */
+ cntr = mcam->entry2cntr_map[index];
+ if (cntr != NPC_MCAM_INVALID_MAP)
+ npc_unmap_mcam_entry_and_cntr(rvu, mcam,
+ blkaddr, index,
+ cntr);
+
+ if (!is_cn20k(rvu->pdev) || cn20k_dft_rl)
+ continue;
+
+ rc = npc_cn20k_idx_free(rvu, &index, 1);
+ if (rc)
+ dev_err(rvu->dev,
+ "Failed to free mcam idx=%u pcifunc=%#x\n",
+ index, pcifunc);
}
}
@@ -3266,7 +3417,10 @@ int rvu_mbox_handler_npc_mcam_shift_entry(struct rvu *rvu,
npc_enable_mcam_entry(rvu, mcam, blkaddr, new_entry, false);
/* Copy rule from old entry to new entry */
- npc_copy_mcam_entry(rvu, mcam, blkaddr, old_entry, new_entry);
+ if (npc_copy_mcam_entry(rvu, mcam, blkaddr, old_entry, new_entry)) {
+ rc = NPC_MCAM_INVALID_REQ;
+ break;
+ }
/* Copy counter mapping, if any */
cntr = mcam->entry2cntr_map[old_entry];
@@ -3284,7 +3438,8 @@ int rvu_mbox_handler_npc_mcam_shift_entry(struct rvu *rvu,
/* If shift has failed then report the failed index */
if (index != req->shift_count) {
- rc = NPC_MCAM_PERM_DENIED;
+ if (!rc)
+ rc = NPC_MCAM_PERM_DENIED;
rsp->failed_entry_idx = index;
}
@@ -3851,6 +4006,12 @@ int rvu_mbox_handler_npc_read_base_steer_rule(struct rvu *rvu,
/* Read the default ucast entry if there is no pkt steering rule */
index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf,
NIXLF_UCAST_ENTRY);
+ if (index < 0) {
+ mutex_unlock(&mcam->lock);
+ rc = NIX_AF_ERR_AF_LF_INVALID;
+ goto out;
+ }
+
read_entry:
/* Read the mcam entry */
npc_read_mcam_entry(rvu, mcam, blkaddr, index, &rsp->entry, &intf,
@@ -3924,6 +4085,12 @@ void rvu_npc_clear_ucast_entry(struct rvu *rvu, int pcifunc, int nixlf)
ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc,
nixlf, NIXLF_UCAST_ENTRY);
+ if (ucast_idx < 0) {
+ dev_err(rvu->dev,
+ "%s: Error to get ucast entry for pcifunc=%#x\n",
+ __func__, pcifunc);
+ return;
+ }
npc_enable_mcam_entry(rvu, mcam, blkaddr, ucast_idx, false);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
index b45798d9fdab..34f1e066707b 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
@@ -1444,7 +1444,7 @@ static int npc_install_flow(struct rvu *rvu, int blkaddr, u16 target,
struct msg_rsp write_rsp;
struct mcam_entry *entry;
bool new = false;
- u16 entry_index;
+ int entry_index;
int err;
installed_features = req->features;
@@ -1477,6 +1477,14 @@ static int npc_install_flow(struct rvu *rvu, int blkaddr, u16 target,
if (req->default_rule) {
entry_index = npc_get_nixlf_mcam_index(mcam, target, nixlf,
NIXLF_UCAST_ENTRY);
+
+ if (entry_index < 0) {
+ dev_err(rvu->dev,
+ "%s: Error to get ucast entry for target=%#x\n",
+ __func__, target);
+ return -EINVAL;
+ }
+
enable = is_mcam_entry_enabled(rvu, mcam, blkaddr, entry_index);
}
@@ -1812,7 +1820,7 @@ process_flow:
/* ignore chan_mask in case pf func is not AF, revisit later */
if (!is_pffunc_af(req->hdr.pcifunc))
- req->chan_mask = 0xFFF;
+ req->chan_mask = rvu_get_cpt_chan_mask(rvu);
err = npc_check_unsupported_flows(rvu, req->features, req->intf);
if (err) {
@@ -1980,13 +1988,15 @@ static int npc_update_dmac_value(struct rvu *rvu, int npcblkaddr,
ether_addr_copy(rule->packet.dmac, pfvf->mac_addr);
- if (is_cn20k(rvu->pdev))
- npc_cn20k_read_mcam_entry(rvu, npcblkaddr, rule->entry,
- cn20k_entry, &intf,
- &enable, &hw_prio);
- else
+ if (is_cn20k(rvu->pdev)) {
+ if (npc_cn20k_read_mcam_entry(rvu, npcblkaddr, rule->entry,
+ cn20k_entry, &intf,
+ &enable, &hw_prio))
+ return -EINVAL;
+ } else {
npc_read_mcam_entry(rvu, mcam, npcblkaddr, rule->entry,
entry, &intf, &enable);
+ }
npc_update_entry(rvu, NPC_DMAC, &mdata,
ether_addr_to_u64(pfvf->mac_addr), 0,
@@ -2038,8 +2048,12 @@ void npc_mcam_enable_flows(struct rvu *rvu, u16 target)
continue;
}
- if (rule->vfvlan_cfg)
- npc_update_dmac_value(rvu, blkaddr, rule, pfvf);
+ if (rule->vfvlan_cfg) {
+ if (npc_update_dmac_value(rvu, blkaddr, rule, pfvf))
+ dev_err(rvu->dev,
+ "Update dmac failed for %u, target=%#x\n",
+ rule->entry, target);
+ }
if (rule->rx_action.op == NIX_RX_ACTION_DEFAULT) {
if (!def_ucast_rule)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c
index 901f6fd40fd4..a2781e0f504e 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c
@@ -97,6 +97,14 @@ int rvu_mbox_handler_rep_event_notify(struct rvu *rvu, struct rep_event *req,
{
struct rep_evtq_ent *qentry;
+ /* The mailbox dispatcher normalises only the header pcifunc; the
+ * nested struct rep_event::pcifunc body field is sender-controlled
+ * and is later used by rvu_rep_up_notify() to index rvu->pf[] /
+ * rvu->hwvf[]. Reject out-of-range body selectors before queueing.
+ */
+ if (!is_pf_func_valid(rvu, req->pcifunc))
+ return -EINVAL;
+
qentry = kmalloc_obj(*qentry, GFP_ATOMIC);
if (!qentry)
return -ENOMEM;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c
index a5a8f4558717..dbf173196608 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c
@@ -619,11 +619,13 @@ static int cn20k_pool_aq_init(struct otx2_nic *pfvf, u16 pool_id,
err = otx2_sync_mbox_msg(&pfvf->mbox);
if (err) {
qmem_free(pfvf->dev, pool->stack);
+ pool->stack = NULL;
return err;
}
aq = otx2_mbox_alloc_msg_npa_cn20k_aq_enq(&pfvf->mbox);
if (!aq) {
qmem_free(pfvf->dev, pool->stack);
+ pool->stack = NULL;
return -ENOMEM;
}
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
index 971fcab1c248..3d253132a17f 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
@@ -1482,11 +1482,13 @@ int otx2_pool_aq_init(struct otx2_nic *pfvf, u16 pool_id,
err = otx2_sync_mbox_msg(&pfvf->mbox);
if (err) {
qmem_free(pfvf->dev, pool->stack);
+ pool->stack = NULL;
return err;
}
aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox);
if (!aq) {
qmem_free(pfvf->dev, pool->stack);
+ pool->stack = NULL;
return -ENOMEM;
}
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
index ee623476e5ff..f9fbf0c17648 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
@@ -3473,7 +3473,7 @@ static void otx2_ndc_sync(struct otx2_nic *pf)
req->nix_lf_rx_sync = 1;
req->npa_lf_sync = 1;
- if (!otx2_sync_mbox_msg(mbox))
+ if (otx2_sync_mbox_msg(mbox))
dev_err(pf->dev, "NDC sync operation failed\n");
mutex_unlock(&mbox->lock);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c
index 94f155ffb17f..0f5d5642d3f7 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c
@@ -609,7 +609,7 @@ static int rvu_rep_rsrc_init(struct otx2_nic *priv)
err = otx2_init_hw_resources(priv);
if (err)
- goto err_free_rsrc;
+ goto err_free_mem;
/* Set maximum frame size allowed in HW */
err = otx2_hw_set_mtu(priv, priv->hw.max_mtu);
@@ -621,6 +621,7 @@ static int rvu_rep_rsrc_init(struct otx2_nic *priv)
err_free_rsrc:
otx2_free_hw_resources(priv);
+err_free_mem:
otx2_free_queue_mem(qset);
return err;
}
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 8d225bc9f063..7d771168b990 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -4491,7 +4491,7 @@ static int mtk_free_dev(struct mtk_eth *eth)
for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) {
if (!eth->dsa_meta[i])
break;
- metadata_dst_free(eth->dsa_meta[i]);
+ dst_release(&eth->dsa_meta[i]->dst);
}
return 0;
diff --git a/drivers/net/ethernet/mellanox/mlx4/srq.c b/drivers/net/ethernet/mellanox/mlx4/srq.c
index dd890f5d7b72..8711689120f3 100644
--- a/drivers/net/ethernet/mellanox/mlx4/srq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/srq.c
@@ -44,13 +44,14 @@ void mlx4_srq_event(struct mlx4_dev *dev, u32 srqn, int event_type)
{
struct mlx4_srq_table *srq_table = &mlx4_priv(dev)->srq_table;
struct mlx4_srq *srq;
+ unsigned long flags;
- rcu_read_lock();
+ spin_lock_irqsave(&srq_table->lock, flags);
srq = radix_tree_lookup(&srq_table->tree, srqn & (dev->caps.num_srqs - 1));
- rcu_read_unlock();
- if (srq)
- refcount_inc(&srq->refcount);
- else {
+ if (!srq || !refcount_inc_not_zero(&srq->refcount))
+ srq = NULL;
+ spin_unlock_irqrestore(&srq_table->lock, flags);
+ if (!srq) {
mlx4_warn(dev, "Async event for bogus SRQ %08x\n", srqn);
return;
}
@@ -203,8 +204,8 @@ int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, u32 cqn, u16 xrcd,
if (err)
goto err_radix;
- refcount_set(&srq->refcount, 1);
init_completion(&srq->free);
+ refcount_set_release(&srq->refcount, 1);
return 0;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
index c89417c1a1f9..e2895972cc82 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
@@ -1002,12 +1002,13 @@ static void cmd_work_handler(struct work_struct *work)
ent->callback(-EBUSY, ent->context);
mlx5_free_cmd_msg(dev, ent->out);
free_msg(dev, ent->in);
+ complete(&ent->slotted);
cmd_ent_put(ent);
} else {
ent->ret = -EBUSY;
complete(&ent->done);
+ complete(&ent->slotted);
}
- complete(&ent->slotted);
return;
}
alloc_ret = cmd_alloc_index(cmd, ent);
@@ -1017,13 +1018,14 @@ static void cmd_work_handler(struct work_struct *work)
ent->callback(-EAGAIN, ent->context);
mlx5_free_cmd_msg(dev, ent->out);
free_msg(dev, ent->in);
+ complete(&ent->slotted);
cmd_ent_put(ent);
} else {
ent->ret = -EAGAIN;
complete(&ent->done);
+ complete(&ent->slotted);
}
up(&cmd->vars.sem);
- complete(&ent->slotted);
return;
}
} else {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
index afdeb1b3d425..8409ae73768f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
@@ -160,13 +160,13 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
* channels are being closed for other reason and this work is not
* relevant anymore.
*/
- while (!netdev_trylock(sq->netdev)) {
+ while (!netdev_trylock(priv->netdev)) {
if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state))
return 0;
msleep(20);
}
- err = mlx5e_health_channel_eq_recover(sq->netdev, eq, sq->cq.ch_stats);
+ err = mlx5e_health_channel_eq_recover(priv->netdev, eq, sq->cq.ch_stats);
if (!err) {
to_ctx->status = 0; /* this sq recovered */
goto out;
@@ -186,7 +186,7 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx)
"mlx5e_safe_reopen_channels failed recovering from a tx_timeout, err(%d).\n",
err);
out:
- netdev_unlock(sq->netdev);
+ netdev_unlock(priv->netdev);
return err;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
index 190b8b66b3ce..d3bab198c99c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
@@ -708,7 +708,7 @@ static void mlx5e_free_xdpsq_desc(struct mlx5e_xdpsq *sq,
xdpi = mlx5e_xdpi_fifo_pop(xdpi_fifo);
page = xdpi.page.page;
- /* No need to check PageNetpp() as we
+ /* No need to check page_pool_page_is_pp() as we
* know this is a page_pool page.
*/
page_pool_recycle_direct(pp_page_to_nmdesc(page)->pp,
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 a52e12c3c95a..db260e3d1412 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
@@ -792,8 +792,10 @@ static int mlx5e_xfrm_add_state(struct net_device *dev,
sa_entry->dev = dev;
sa_entry->ipsec = ipsec;
/* Check if this SA is originated from acquire flow temporary SA */
- if (x->xso.flags & XFRM_DEV_OFFLOAD_FLAG_ACQ)
- goto out;
+ if (x->xso.flags & XFRM_DEV_OFFLOAD_FLAG_ACQ) {
+ x->xso.offload_handle = (unsigned long)sa_entry;
+ return 0;
+ }
err = mlx5e_xfrm_validate_state(priv->mdev, x, extack);
if (err)
@@ -870,7 +872,6 @@ static int mlx5e_xfrm_add_state(struct net_device *dev,
xa_unlock_bh(&ipsec->sadb);
}
-out:
x->xso.offload_handle = (unsigned long)sa_entry;
if (allow_tunnel_mode)
mlx5_eswitch_unblock_encap(priv->mdev);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c
index 6a50b6dec0fa..d9adb993e64d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c
@@ -1070,29 +1070,37 @@ static struct psp_dev_ops mlx5_psp_ops = {
void mlx5e_psp_unregister(struct mlx5e_priv *priv)
{
- if (!priv->psp || !priv->psp->psp)
+ struct mlx5e_psp *psp = priv->psp;
+
+ if (!psp || !psp->psp)
return;
- psp_dev_unregister(priv->psp->psp);
+ psp_dev_unregister(psp->psp);
+ psp->psp = NULL;
}
void mlx5e_psp_register(struct mlx5e_priv *priv)
{
+ struct mlx5e_psp *psp = priv->psp;
+ struct psp_dev *psd;
+
/* FW Caps missing */
if (!priv->psp)
return;
- priv->psp->caps.assoc_drv_spc = sizeof(u32);
- priv->psp->caps.versions = 1 << PSP_VERSION_HDR0_AES_GCM_128;
+ psp->caps.assoc_drv_spc = sizeof(u32);
+ psp->caps.versions = 1 << PSP_VERSION_HDR0_AES_GCM_128;
if (MLX5_CAP_PSP(priv->mdev, psp_crypto_esp_aes_gcm_256_encrypt) &&
MLX5_CAP_PSP(priv->mdev, psp_crypto_esp_aes_gcm_256_decrypt))
- priv->psp->caps.versions |= 1 << PSP_VERSION_HDR0_AES_GCM_256;
+ psp->caps.versions |= 1 << PSP_VERSION_HDR0_AES_GCM_256;
- priv->psp->psp = psp_dev_create(priv->netdev, &mlx5_psp_ops,
- &priv->psp->caps, NULL);
- if (IS_ERR(priv->psp->psp))
+ psd = psp_dev_create(priv->netdev, &mlx5_psp_ops, &psp->caps, NULL);
+ if (IS_ERR(psd)) {
mlx5_core_err(priv->mdev, "PSP failed to register due to %pe\n",
- priv->psp->psp);
+ psd);
+ return;
+ }
+ psp->psp = psd;
}
int mlx5e_psp_init(struct mlx5e_priv *priv)
@@ -1131,22 +1139,18 @@ int mlx5e_psp_init(struct mlx5e_priv *priv)
if (!psp)
return -ENOMEM;
- priv->psp = psp;
fs = mlx5e_accel_psp_fs_init(priv);
if (IS_ERR(fs)) {
err = PTR_ERR(fs);
- goto out_err;
+ kfree(psp);
+ return err;
}
psp->fs = fs;
+ priv->psp = psp;
mlx5_core_dbg(priv->mdev, "PSP attached to netdevice\n");
return 0;
-
-out_err:
- priv->psp = NULL;
- kfree(psp);
- return err;
}
void mlx5e_psp_cleanup(struct mlx5e_priv *priv)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
index bb61e2179078..99a0034b9b20 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
@@ -1574,8 +1574,11 @@ static int mlx5e_create_rxfh_context(struct net_device *dev,
rxfh->indir, rxfh->key,
hfunc == ETH_RSS_HASH_NO_CHANGE ? NULL : &hfunc,
rxfh->input_xfrm == RXH_XFRM_NO_CHANGE ? NULL : &symmetric);
- if (err)
+ if (err) {
+ WARN_ON(mlx5e_rx_res_rss_destroy(priv->rx_res,
+ rxfh->rss_context));
goto unlock;
+ }
mlx5e_rx_res_rss_get_rxfh(priv->rx_res, rxfh->rss_context,
ethtool_rxfh_context_indir(ctx),
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 5a46870c4b74..8f2b3abe0092 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -6023,7 +6023,6 @@ static int mlx5e_nic_init(struct mlx5_core_dev *mdev,
if (take_rtnl)
rtnl_lock();
- mlx5e_psp_register(priv);
/* update XDP supported features */
mlx5e_set_xdp_feature(priv);
@@ -6036,7 +6035,6 @@ static int mlx5e_nic_init(struct mlx5_core_dev *mdev,
static void mlx5e_nic_cleanup(struct mlx5e_priv *priv)
{
mlx5e_health_destroy_reporters(priv);
- mlx5e_psp_unregister(priv);
mlx5e_ktls_cleanup(priv);
mlx5e_psp_cleanup(priv);
mlx5e_fs_cleanup(priv->fs);
@@ -6160,6 +6158,7 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv)
mlx5e_fs_init_l2_addr(priv->fs, netdev);
mlx5e_ipsec_init(priv);
+ mlx5e_psp_register(priv);
err = mlx5e_macsec_init(priv);
if (err)
@@ -6230,6 +6229,7 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv)
mlx5_lag_remove_netdev(mdev, priv->netdev);
mlx5_vxlan_reset_to_default(mdev->vxlan);
mlx5e_macsec_cleanup(priv);
+ mlx5e_psp_unregister(priv);
mlx5e_ipsec_cleanup(priv);
}
@@ -6774,9 +6774,11 @@ static int mlx5e_resume(struct auxiliary_device *adev)
return err;
actual_adev = mlx5_sd_get_adev(mdev, adev, edev->idx);
- if (actual_adev)
- return _mlx5e_resume(actual_adev);
- return 0;
+ if (actual_adev) {
+ err = _mlx5e_resume(actual_adev);
+ mlx5_sd_put_adev(actual_adev, adev);
+ }
+ return err;
}
static int _mlx5e_suspend(struct auxiliary_device *adev, bool pre_netdev_reg)
@@ -6815,6 +6817,8 @@ static int mlx5e_suspend(struct auxiliary_device *adev, pm_message_t state)
err = _mlx5e_suspend(actual_adev, false);
mlx5_sd_cleanup(mdev);
+ if (actual_adev)
+ mlx5_sd_put_adev(actual_adev, adev);
return err;
}
@@ -6912,9 +6916,19 @@ static int mlx5e_probe(struct auxiliary_device *adev,
return err;
actual_adev = mlx5_sd_get_adev(mdev, adev, edev->idx);
- if (actual_adev)
- return _mlx5e_probe(actual_adev);
+ if (actual_adev) {
+ err = _mlx5e_probe(actual_adev);
+ if (err)
+ goto sd_cleanup;
+ mlx5_sd_put_adev(actual_adev, adev);
+ }
return 0;
+
+sd_cleanup:
+ mlx5_sd_cleanup(mdev);
+ if (actual_adev)
+ mlx5_sd_put_adev(actual_adev, adev);
+ return err;
}
static void _mlx5e_remove(struct auxiliary_device *adev)
@@ -6966,6 +6980,8 @@ static void mlx5e_remove(struct auxiliary_device *adev)
_mlx5e_remove(actual_adev);
mlx5_sd_cleanup(mdev);
+ if (actual_adev)
+ mlx5_sd_put_adev(actual_adev, adev);
}
static const struct auxiliary_device_id mlx5e_id_table[] = {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
index b31f689fe271..e90c6c6df835 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
@@ -252,7 +252,7 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
mlx5e_cq_arm(&c->xdpsq->cq);
if (unlikely(aff_change && busy_xsk)) {
- mlx5e_trigger_irq(&c->icosq);
+ mlx5e_trigger_napi_async_icosq(c);
ch_stats->force_irq++;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c
index 3cfe743610d3..ab50d2c734ed 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c
@@ -142,7 +142,8 @@ static int mlx5_esw_ipsec_modify_flow_dests(struct mlx5_eswitch *esw,
attr = flow->attr;
esw_attr = attr->esw_attr;
- if (esw_attr->out_count - esw_attr->split_count > 1)
+ if (!esw_attr->out_count ||
+ esw_attr->out_count - esw_attr->split_count > 1)
return 0;
err = mlx5_eswitch_restore_ipsec_rule(esw, flow->rule[0], esw_attr,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
index 123c96716a54..7c8311f41232 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
@@ -908,6 +908,24 @@ static void esw_vport_cleanup(struct mlx5_eswitch *esw, struct mlx5_vport *vport
esw_vport_cleanup_acl(esw, vport);
}
+static void mlx5_esw_vport_set_max_tx_speed(struct mlx5_eswitch *esw,
+ struct mlx5_vport *vport)
+{
+ int ret;
+
+ if (!MLX5_CAP_ESW(esw->dev, esw_vport_state_max_tx_speed))
+ return;
+
+ ret = mlx5_modify_vport_max_tx_speed(esw->dev,
+ MLX5_VPORT_STATE_OP_MOD_ESW_VPORT,
+ vport->vport, true,
+ vport->agg_max_tx_speed);
+ if (ret)
+ mlx5_core_dbg(esw->dev,
+ "Failed to set vport %d speed %d, err=%d\n",
+ vport->vport, vport->agg_max_tx_speed, ret);
+}
+
int mlx5_esw_vport_enable(struct mlx5_eswitch *esw, struct mlx5_vport *vport,
enum mlx5_eswitch_vport_event enabled_events)
{
@@ -948,6 +966,9 @@ int mlx5_esw_vport_enable(struct mlx5_eswitch *esw, struct mlx5_vport *vport,
esw->enabled_vports++;
esw_debug(esw->dev, "Enabled VPORT(%d)\n", vport_num);
+
+ if (vport->agg_max_tx_speed)
+ mlx5_esw_vport_set_max_tx_speed(esw, vport);
done:
mutex_unlock(&esw->state_lock);
return ret;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
index 5128f5020dae..e9cf7c592ce9 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
@@ -247,6 +247,7 @@ struct mlx5_vport {
enum mlx5_eswitch_vport_event enabled_events;
int index;
struct mlx5_devlink_port *dl_port;
+ u32 agg_max_tx_speed;
};
struct mlx5_esw_indir_table;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
index 449e4bd86c06..f8e70ac5a85b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
@@ -1274,6 +1274,11 @@ static void mlx5_lag_modify_device_vports_speed(struct mlx5_core_dev *mdev,
if (vport->vport == MLX5_VPORT_UPLINK)
continue;
+ vport->agg_max_tx_speed = speed;
+
+ if (!vport->enabled)
+ continue;
+
ret = mlx5_modify_vport_max_tx_speed(mdev, op_mod,
vport->vport, true, speed);
if (ret)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c
index 762c783156b4..6e199161b008 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c
@@ -18,6 +18,7 @@ struct mlx5_sd {
u8 host_buses;
struct mlx5_devcom_comp_dev *devcom;
struct dentry *dfs;
+ u8 state;
bool primary;
union {
struct { /* primary */
@@ -31,6 +32,11 @@ struct mlx5_sd {
};
};
+enum mlx5_sd_state {
+ MLX5_SD_STATE_DOWN = 0,
+ MLX5_SD_STATE_UP,
+};
+
static int mlx5_sd_get_host_buses(struct mlx5_core_dev *dev)
{
struct mlx5_sd *sd = mlx5_get_sd(dev);
@@ -270,9 +276,6 @@ static void sd_unregister(struct mlx5_core_dev *dev)
{
struct mlx5_sd *sd = mlx5_get_sd(dev);
- mlx5_devcom_comp_lock(sd->devcom);
- mlx5_devcom_comp_set_ready(sd->devcom, false);
- mlx5_devcom_comp_unlock(sd->devcom);
mlx5_devcom_unregister_component(sd->devcom);
}
@@ -426,6 +429,7 @@ int mlx5_sd_init(struct mlx5_core_dev *dev)
struct mlx5_core_dev *primary, *pos, *to;
struct mlx5_sd *sd = mlx5_get_sd(dev);
u8 alias_key[ACCESS_KEY_LEN];
+ struct mlx5_sd *primary_sd;
int err, i;
err = sd_init(dev);
@@ -440,10 +444,17 @@ int mlx5_sd_init(struct mlx5_core_dev *dev)
if (err)
goto err_sd_cleanup;
+ mlx5_devcom_comp_lock(sd->devcom);
if (!mlx5_devcom_comp_is_ready(sd->devcom))
- return 0;
+ goto out;
primary = mlx5_sd_get_primary(dev);
+ if (!primary)
+ goto out;
+
+ primary_sd = mlx5_get_sd(primary);
+ if (primary_sd->state != MLX5_SD_STATE_DOWN)
+ goto out;
for (i = 0; i < ACCESS_KEY_LEN; i++)
alias_key[i] = get_random_u8();
@@ -452,9 +463,13 @@ int mlx5_sd_init(struct mlx5_core_dev *dev)
if (err)
goto err_sd_unregister;
- sd->dfs = debugfs_create_dir("multi-pf", mlx5_debugfs_get_dev_root(primary));
- debugfs_create_x32("group_id", 0400, sd->dfs, &sd->group_id);
- debugfs_create_file("primary", 0400, sd->dfs, primary, &dev_fops);
+ primary_sd->dfs =
+ debugfs_create_dir("multi-pf",
+ mlx5_debugfs_get_dev_root(primary));
+ debugfs_create_x32("group_id", 0400, primary_sd->dfs,
+ &primary_sd->group_id);
+ debugfs_create_file("primary", 0400, primary_sd->dfs, primary,
+ &dev_fops);
mlx5_sd_for_each_secondary(i, primary, pos) {
char name[32];
@@ -464,7 +479,8 @@ int mlx5_sd_init(struct mlx5_core_dev *dev)
goto err_unset_secondaries;
snprintf(name, sizeof(name), "secondary_%d", i - 1);
- debugfs_create_file(name, 0400, sd->dfs, pos, &dev_fops);
+ debugfs_create_file(name, 0400, primary_sd->dfs, pos,
+ &dev_fops);
}
@@ -472,6 +488,9 @@ int mlx5_sd_init(struct mlx5_core_dev *dev)
sd->group_id, mlx5_devcom_comp_get_size(sd->devcom));
sd_print_group(primary);
+ primary_sd->state = MLX5_SD_STATE_UP;
+out:
+ mlx5_devcom_comp_unlock(sd->devcom);
return 0;
err_unset_secondaries:
@@ -479,8 +498,18 @@ err_unset_secondaries:
mlx5_sd_for_each_secondary_to(i, primary, to, pos)
sd_cmd_unset_secondary(pos);
sd_cmd_unset_primary(primary);
- debugfs_remove_recursive(sd->dfs);
+ debugfs_remove_recursive(primary_sd->dfs);
+ primary_sd->dfs = NULL;
err_sd_unregister:
+ mlx5_sd_for_each_secondary(i, primary, pos) {
+ struct mlx5_sd *peer_sd = mlx5_get_sd(pos);
+
+ primary_sd->secondaries[i - 1] = NULL;
+ peer_sd->primary_dev = NULL;
+ }
+ primary_sd->primary = false;
+ mlx5_devcom_comp_set_ready(sd->devcom, false);
+ mlx5_devcom_comp_unlock(sd->devcom);
sd_unregister(dev);
err_sd_cleanup:
sd_cleanup(dev);
@@ -491,42 +520,97 @@ void mlx5_sd_cleanup(struct mlx5_core_dev *dev)
{
struct mlx5_sd *sd = mlx5_get_sd(dev);
struct mlx5_core_dev *primary, *pos;
+ struct mlx5_sd *primary_sd;
int i;
if (!sd)
return;
+ mlx5_devcom_comp_lock(sd->devcom);
if (!mlx5_devcom_comp_is_ready(sd->devcom))
- goto out;
+ goto out_unlock;
primary = mlx5_sd_get_primary(dev);
+ if (!primary)
+ goto out_ready_false;
+
+ primary_sd = mlx5_get_sd(primary);
+ if (primary_sd->state != MLX5_SD_STATE_UP)
+ goto out_clear_peers;
+
mlx5_sd_for_each_secondary(i, primary, pos)
sd_cmd_unset_secondary(pos);
sd_cmd_unset_primary(primary);
- debugfs_remove_recursive(sd->dfs);
+ debugfs_remove_recursive(primary_sd->dfs);
+ primary_sd->dfs = NULL;
sd_info(primary, "group id %#x, uncombined\n", sd->group_id);
-out:
+ primary_sd->state = MLX5_SD_STATE_DOWN;
+out_clear_peers:
+ mlx5_sd_for_each_secondary(i, primary, pos) {
+ struct mlx5_sd *peer_sd = mlx5_get_sd(pos);
+
+ primary_sd->secondaries[i - 1] = NULL;
+ peer_sd->primary_dev = NULL;
+ }
+ primary_sd->primary = false;
+out_ready_false:
+ mlx5_devcom_comp_set_ready(sd->devcom, false);
+out_unlock:
+ mlx5_devcom_comp_unlock(sd->devcom);
sd_unregister(dev);
sd_cleanup(dev);
}
+/* Lock order:
+ * primary: actual_adev_lock -> SD devcom comp lock
+ * secondary: SD devcom comp lock -> (drop) -> actual_adev_lock
+ * The two locks are never held together, so no ABBA.
+ */
struct auxiliary_device *mlx5_sd_get_adev(struct mlx5_core_dev *dev,
struct auxiliary_device *adev,
int idx)
{
struct mlx5_sd *sd = mlx5_get_sd(dev);
struct mlx5_core_dev *primary;
+ struct mlx5_adev *primary_adev;
if (!sd)
return adev;
- if (!mlx5_devcom_comp_is_ready(sd->devcom))
+ mlx5_devcom_comp_lock(sd->devcom);
+ if (!mlx5_devcom_comp_is_ready(sd->devcom)) {
+ mlx5_devcom_comp_unlock(sd->devcom);
return NULL;
+ }
primary = mlx5_sd_get_primary(dev);
- if (dev == primary)
+ if (!primary || dev == primary) {
+ mlx5_devcom_comp_unlock(sd->devcom);
return adev;
+ }
+
+ primary_adev = primary->priv.adev[idx];
+ get_device(&primary_adev->adev.dev);
+ mlx5_devcom_comp_unlock(sd->devcom);
+
+ device_lock(&primary_adev->adev.dev);
+ /* Primary may have completed remove between dropping devcom and
+ * acquiring device_lock; recheck.
+ */
+ if (!mlx5_devcom_comp_is_ready(sd->devcom)) {
+ device_unlock(&primary_adev->adev.dev);
+ put_device(&primary_adev->adev.dev);
+ return NULL;
+ }
+ return &primary_adev->adev;
+}
- return &primary->priv.adev[idx]->adev;
+void mlx5_sd_put_adev(struct auxiliary_device *actual_adev,
+ struct auxiliary_device *adev)
+{
+ if (actual_adev != adev) {
+ device_unlock(&actual_adev->dev);
+ put_device(&actual_adev->dev);
+ }
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h
index 137efaf9aabc..9bfd5b9756b5 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h
@@ -15,6 +15,8 @@ struct mlx5_core_dev *mlx5_sd_ch_ix_get_dev(struct mlx5_core_dev *primary, int c
struct auxiliary_device *mlx5_sd_get_adev(struct mlx5_core_dev *dev,
struct auxiliary_device *adev,
int idx);
+void mlx5_sd_put_adev(struct auxiliary_device *actual_adev,
+ struct auxiliary_device *adev);
int mlx5_sd_init(struct mlx5_core_dev *dev);
void mlx5_sd_cleanup(struct mlx5_core_dev *dev);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c
index aca77853abb8..5a172c572a68 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c
@@ -1320,8 +1320,10 @@ mlx5_cmd_hws_packet_reformat_alloc(struct mlx5_flow_root_namespace *ns,
break;
case MLX5_REFORMAT_TYPE_REMOVE_HDR:
hws_action = mlx5_fs_get_action_remove_header_vlan(fs_ctx, params);
- if (!hws_action)
+ if (!hws_action) {
mlx5_core_err(dev, "Only vlan remove header supported\n");
+ return -EOPNOTSUPP;
+ }
break;
default:
mlx5_core_err(ns->dev, "Packet-reformat not supported(%d)\n",
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c
index c406a3b56b37..4dea2bb58d2f 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c
@@ -826,7 +826,8 @@ struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd)
netif_tx_stop_all_queues(netdev);
if (fbnic_phylink_create(netdev)) {
- fbnic_netdev_free(fbd);
+ free_netdev(netdev);
+ fbd->netdev = NULL;
return NULL;
}
diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c
index f3332417162e..ffac22883e49 100644
--- a/drivers/net/ethernet/microchip/lan743x_main.c
+++ b/drivers/net/ethernet/microchip/lan743x_main.c
@@ -1219,6 +1219,36 @@ static void lan743x_mac_set_address(struct lan743x_adapter *adapter,
"MAC address set to %pM\n", addr);
}
+static void lan743x_mac_rx_enable_fse(struct lan743x_adapter *adapter)
+{
+ u32 mac_rx;
+ bool rxen;
+
+ mac_rx = lan743x_csr_read(adapter, MAC_RX);
+ if (mac_rx & MAC_RX_FSE_)
+ return;
+
+ rxen = mac_rx & MAC_RX_RXEN_;
+ if (rxen) {
+ mac_rx &= ~MAC_RX_RXEN_;
+ lan743x_csr_write(adapter, MAC_RX, mac_rx);
+ lan743x_csr_wait_for_bit(adapter, MAC_RX, MAC_RX_RXD_,
+ 1, 1000, 20000, 100);
+ }
+
+ /* Per AN2948, hardware prevents modification of the FSE bit while the
+ * MAC receiver is enabled (RXEN bit set). Use separate register write
+ * to assert the FSE bit before enabling the RXEN bit in MAC_RX
+ */
+ mac_rx |= MAC_RX_FSE_;
+ lan743x_csr_write(adapter, MAC_RX, mac_rx);
+
+ if (rxen) {
+ mac_rx |= MAC_RX_RXEN_;
+ lan743x_csr_write(adapter, MAC_RX, mac_rx);
+ }
+}
+
static int lan743x_mac_init(struct lan743x_adapter *adapter)
{
bool mac_address_valid = true;
@@ -1258,6 +1288,8 @@ static int lan743x_mac_init(struct lan743x_adapter *adapter)
lan743x_mac_set_address(adapter, adapter->mac_address);
eth_hw_addr_set(netdev, adapter->mac_address);
+ lan743x_mac_rx_enable_fse(adapter);
+
return 0;
}
diff --git a/drivers/net/ethernet/microchip/lan743x_main.h b/drivers/net/ethernet/microchip/lan743x_main.h
index 160d94a7cee6..1573c8f9c993 100644
--- a/drivers/net/ethernet/microchip/lan743x_main.h
+++ b/drivers/net/ethernet/microchip/lan743x_main.h
@@ -182,6 +182,7 @@
#define MAC_RX (0x104)
#define MAC_RX_MAX_SIZE_SHIFT_ (16)
#define MAC_RX_MAX_SIZE_MASK_ (0x3FFF0000)
+#define MAC_RX_FSE_ BIT(2)
#define MAC_RX_RXD_ BIT(1)
#define MAC_RX_RXEN_ BIT(0)
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
index 47752d3fde0b..1179a6e127c5 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
@@ -749,11 +749,10 @@ static void lan966x_cleanup_ports(struct lan966x *lan966x)
for (p = 0; p < lan966x->num_phys_ports; p++) {
port = lan966x->ports[p];
- if (!port)
+ if (!port || !port->dev)
continue;
- if (port->dev)
- unregister_netdev(port->dev);
+ unregister_netdev(port->dev);
lan966x_xdp_port_deinit(port);
if (lan966x->fdma && lan966x->fdma_ndev == port->dev)
@@ -873,6 +872,9 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p,
err = register_netdev(dev);
if (err) {
dev_err(lan966x->dev, "register_netdev failed\n");
+ phylink_destroy(phylink);
+ port->phylink = NULL;
+ port->dev = NULL;
return err;
}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index 6a745bb71b5c..eb57b86fbe22 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -31,11 +31,11 @@ enum spx5_target_chiptype {
SPX5_TARGET_CT_7552 = 0x7552, /* SparX-5-128 Enterprise */
SPX5_TARGET_CT_7556 = 0x7556, /* SparX-5-160 Enterprise */
SPX5_TARGET_CT_7558 = 0x7558, /* SparX-5-200 Enterprise */
- SPX5_TARGET_CT_7546TSN = 0x47546, /* SparX-5-64i Industrial */
- SPX5_TARGET_CT_7549TSN = 0x47549, /* SparX-5-90i Industrial */
- SPX5_TARGET_CT_7552TSN = 0x47552, /* SparX-5-128i Industrial */
- SPX5_TARGET_CT_7556TSN = 0x47556, /* SparX-5-160i Industrial */
- SPX5_TARGET_CT_7558TSN = 0x47558, /* SparX-5-200i Industrial */
+ SPX5_TARGET_CT_7546TSN = 0x0546, /* SparX-5-64i Industrial */
+ SPX5_TARGET_CT_7549TSN = 0x0549, /* SparX-5-90i Industrial */
+ SPX5_TARGET_CT_7552TSN = 0x0552, /* SparX-5-128i Industrial */
+ SPX5_TARGET_CT_7556TSN = 0x0556, /* SparX-5-160i Industrial */
+ SPX5_TARGET_CT_7558TSN = 0x0558, /* SparX-5-200i Industrial */
SPX5_TARGET_CT_LAN9694 = 0x9694, /* lan969x-40 */
SPX5_TARGET_CT_LAN9691VAO = 0x9691, /* lan969x-40-VAO */
SPX5_TARGET_CT_LAN9694TSN = 0x9695, /* lan969x-40-TSN */
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c
index 04bc8fffaf96..62c49893de3c 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c
@@ -1128,7 +1128,8 @@ int sparx5_port_init(struct sparx5 *sparx5,
DEV2G5_PCS1G_SD_CFG(port->portno));
if (conf->portmode == PHY_INTERFACE_MODE_QSGMII ||
- conf->portmode == PHY_INTERFACE_MODE_SGMII) {
+ conf->portmode == PHY_INTERFACE_MODE_SGMII ||
+ conf->portmode == PHY_INTERFACE_MODE_1000BASEX) {
err = sparx5_serdes_set(sparx5, port, conf);
if (err)
return err;
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index 098fbda0d128..d8e816882f02 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -43,8 +43,9 @@ static u64 mana_gd_r64(struct gdma_context *g, u64 offset)
static int mana_gd_init_pf_regs(struct pci_dev *pdev)
{
struct gdma_context *gc = pci_get_drvdata(pdev);
- void __iomem *sriov_base_va;
+ u64 remaining_barsize;
u64 sriov_base_off;
+ u64 sriov_shm_off;
gc->db_page_size = mana_gd_r32(gc, GDMA_PF_REG_DB_PAGE_SIZE) & 0xFFFF;
@@ -73,10 +74,28 @@ static int mana_gd_init_pf_regs(struct pci_dev *pdev)
gc->phys_db_page_base = gc->bar0_pa + gc->db_page_off;
sriov_base_off = mana_gd_r64(gc, GDMA_SRIOV_REG_CFG_BASE_OFF);
+ if (sriov_base_off >= gc->bar0_size ||
+ gc->bar0_size - sriov_base_off <
+ GDMA_PF_REG_SHM_OFF + sizeof(u64) ||
+ !IS_ALIGNED(sriov_base_off, sizeof(u64))) {
+ dev_err(gc->dev,
+ "SRIOV base offset 0x%llx out of range or unaligned (BAR0 size 0x%llx)\n",
+ sriov_base_off, (u64)gc->bar0_size);
+ return -EPROTO;
+ }
- sriov_base_va = gc->bar0_va + sriov_base_off;
- gc->shm_base = sriov_base_va +
- mana_gd_r64(gc, sriov_base_off + GDMA_PF_REG_SHM_OFF);
+ remaining_barsize = gc->bar0_size - sriov_base_off;
+ sriov_shm_off = mana_gd_r64(gc, sriov_base_off + GDMA_PF_REG_SHM_OFF);
+ if (sriov_shm_off >= remaining_barsize ||
+ remaining_barsize - sriov_shm_off < SMC_APERTURE_SIZE ||
+ !IS_ALIGNED(sriov_shm_off, sizeof(u32))) {
+ dev_err(gc->dev,
+ "SRIOV SHM offset 0x%llx out of range or unaligned (BAR0 size 0x%llx)\n",
+ sriov_shm_off, (u64)gc->bar0_size);
+ return -EPROTO;
+ }
+
+ gc->shm_base = gc->bar0_va + sriov_base_off + sriov_shm_off;
return 0;
}
@@ -84,6 +103,7 @@ static int mana_gd_init_pf_regs(struct pci_dev *pdev)
static int mana_gd_init_vf_regs(struct pci_dev *pdev)
{
struct gdma_context *gc = pci_get_drvdata(pdev);
+ u64 shm_off;
gc->db_page_size = mana_gd_r32(gc, GDMA_REG_DB_PAGE_SIZE) & 0xFFFF;
@@ -111,7 +131,17 @@ static int mana_gd_init_vf_regs(struct pci_dev *pdev)
gc->db_page_base = gc->bar0_va + gc->db_page_off;
gc->phys_db_page_base = gc->bar0_pa + gc->db_page_off;
- gc->shm_base = gc->bar0_va + mana_gd_r64(gc, GDMA_REG_SHM_OFFSET);
+ shm_off = mana_gd_r64(gc, GDMA_REG_SHM_OFFSET);
+ if (shm_off >= gc->bar0_size ||
+ gc->bar0_size - shm_off < SMC_APERTURE_SIZE ||
+ !IS_ALIGNED(shm_off, sizeof(u32))) {
+ dev_err(gc->dev,
+ "SHM offset 0x%llx out of range or unaligned (BAR0 size 0x%llx)\n",
+ shm_off, (u64)gc->bar0_size);
+ return -EPROTO;
+ }
+
+ gc->shm_base = gc->bar0_va + shm_off;
return 0;
}
diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c
index dbbde0fa57e7..e3c24d50dad0 100644
--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c
+++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c
@@ -77,21 +77,19 @@ static int mana_hwc_post_rx_wqe(const struct hwc_wq *hwc_rxq,
}
static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len,
- struct hwc_work_request *rx_req)
+ struct hwc_work_request *rx_req, u16 msg_id)
{
const struct gdma_resp_hdr *resp_msg = rx_req->buf_va;
struct hwc_caller_ctx *ctx;
int err;
- if (!test_bit(resp_msg->response.hwc_msg_id,
- hwc->inflight_msg_res.map)) {
- dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n",
- resp_msg->response.hwc_msg_id);
+ if (!test_bit(msg_id, hwc->inflight_msg_res.map)) {
+ dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", msg_id);
mana_hwc_post_rx_wqe(hwc->rxq, rx_req);
return;
}
- ctx = hwc->caller_ctx + resp_msg->response.hwc_msg_id;
+ ctx = hwc->caller_ctx + msg_id;
err = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len);
if (err)
goto out;
@@ -251,6 +249,7 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id,
struct gdma_sge *sge;
u64 rq_base_addr;
u64 rx_req_idx;
+ u16 msg_id;
u8 *wqe;
if (WARN_ON_ONCE(hwc_rxq->gdma_wq->id != gdma_rxq_id))
@@ -266,16 +265,26 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id,
rq_base_addr = hwc_rxq->msg_buf->mem_info.dma_handle;
rx_req_idx = (sge->address - rq_base_addr) / hwc->max_req_msg_size;
+ if (rx_req_idx >= hwc_rxq->msg_buf->num_reqs) {
+ dev_err(hwc->dev, "HWC RX: wrong rx_req_idx=%llu, num_reqs=%u\n",
+ rx_req_idx, hwc_rxq->msg_buf->num_reqs);
+ return;
+ }
+
rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx];
resp = (struct gdma_resp_hdr *)rx_req->buf_va;
- if (resp->response.hwc_msg_id >= hwc->num_inflight_msg) {
- dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n",
- resp->response.hwc_msg_id);
+ /* Read msg_id once from DMA buffer to prevent TOCTOU:
+ * DMA memory is shared/unencrypted in CVMs - host can
+ * modify it between reads.
+ */
+ msg_id = READ_ONCE(resp->response.hwc_msg_id);
+ if (msg_id >= hwc->num_inflight_msg) {
+ dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n", msg_id);
return;
}
- mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req);
+ mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req, msg_id);
/* Can no longer use 'resp', because the buffer is posted to the HW
* in mana_hwc_handle_resp() above.
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index a654b3699c4c..c9b1df1ed109 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -1727,6 +1727,9 @@ static void mana_fence_rqs(struct mana_port_context *apc)
struct mana_rxq *rxq;
int err;
+ if (!apc->rxqs)
+ return;
+
for (rxq_idx = 0; rxq_idx < apc->num_queues; rxq_idx++) {
rxq = apc->rxqs[rxq_idx];
err = mana_fence_rq(apc, rxq);
@@ -2520,9 +2523,12 @@ static void mana_destroy_rxq(struct mana_port_context *apc,
napi_disable_locked(napi);
netif_napi_del_locked(napi);
}
- xdp_rxq_info_unreg(&rxq->xdp_rxq);
- mana_destroy_wq_obj(apc, GDMA_RQ, rxq->rxobj);
+ if (xdp_rxq_info_is_reg(&rxq->xdp_rxq))
+ xdp_rxq_info_unreg(&rxq->xdp_rxq);
+
+ if (rxq->rxobj != INVALID_MANA_HANDLE)
+ mana_destroy_wq_obj(apc, GDMA_RQ, rxq->rxobj);
mana_deinit_cq(apc, &rxq->rx_cq);
@@ -2796,9 +2802,6 @@ out:
mana_destroy_rxq(apc, rxq, false);
- if (cq)
- mana_deinit_cq(apc, cq);
-
return NULL;
}
@@ -2858,13 +2861,16 @@ static void mana_destroy_vport(struct mana_port_context *apc)
struct mana_rxq *rxq;
u32 rxq_idx;
- for (rxq_idx = 0; rxq_idx < apc->num_queues; rxq_idx++) {
- rxq = apc->rxqs[rxq_idx];
- if (!rxq)
- continue;
+ if (apc->rxqs) {
+
+ for (rxq_idx = 0; rxq_idx < apc->num_queues; rxq_idx++) {
+ rxq = apc->rxqs[rxq_idx];
+ if (!rxq)
+ continue;
- mana_destroy_rxq(apc, rxq, true);
- apc->rxqs[rxq_idx] = NULL;
+ mana_destroy_rxq(apc, rxq, true);
+ apc->rxqs[rxq_idx] = NULL;
+ }
}
mana_destroy_txq(apc);
@@ -3269,7 +3275,8 @@ static int mana_dealloc_queues(struct net_device *ndev)
if (apc->port_is_up)
return -EINVAL;
- mana_chn_setxdp(apc, NULL);
+ if (apc->rxqs)
+ mana_chn_setxdp(apc, NULL);
if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode)
mana_pf_deregister_filter(apc);
@@ -3287,33 +3294,38 @@ static int mana_dealloc_queues(struct net_device *ndev)
* number of queues.
*/
- for (i = 0; i < apc->num_queues; i++) {
- txq = &apc->tx_qp[i].txq;
- tsleep = 1000;
- while (atomic_read(&txq->pending_sends) > 0 &&
- time_before(jiffies, timeout)) {
- usleep_range(tsleep, tsleep + 1000);
- tsleep <<= 1;
- }
- if (atomic_read(&txq->pending_sends)) {
- err = pcie_flr(to_pci_dev(gd->gdma_context->dev));
- if (err) {
- netdev_err(ndev, "flr failed %d with %d pkts pending in txq %u\n",
- err, atomic_read(&txq->pending_sends),
- txq->gdma_txq_id);
+ if (apc->tx_qp) {
+ for (i = 0; i < apc->num_queues; i++) {
+ txq = &apc->tx_qp[i].txq;
+ tsleep = 1000;
+ while (atomic_read(&txq->pending_sends) > 0 &&
+ time_before(jiffies, timeout)) {
+ usleep_range(tsleep, tsleep + 1000);
+ tsleep <<= 1;
+ }
+ if (atomic_read(&txq->pending_sends)) {
+ err =
+ pcie_flr(to_pci_dev(gd->gdma_context->dev));
+ if (err) {
+ netdev_err(ndev, "flr failed %d with %d pkts pending in txq %u\n",
+ err,
+ atomic_read(&txq->pending_sends),
+ txq->gdma_txq_id);
+ }
+ break;
}
- break;
}
- }
- for (i = 0; i < apc->num_queues; i++) {
- txq = &apc->tx_qp[i].txq;
- while ((skb = skb_dequeue(&txq->pending_skbs))) {
- mana_unmap_skb(skb, apc);
- dev_kfree_skb_any(skb);
+ for (i = 0; i < apc->num_queues; i++) {
+ txq = &apc->tx_qp[i].txq;
+ while ((skb = skb_dequeue(&txq->pending_skbs))) {
+ mana_unmap_skb(skb, apc);
+ dev_kfree_skb_any(skb);
+ }
+ atomic_set(&txq->pending_sends, 0);
}
- atomic_set(&txq->pending_sends, 0);
}
+
/* We're 100% sure the queues can no longer be woken up, because
* we're sure now mana_poll_tx_cq() can't be running.
*/
@@ -3338,6 +3350,12 @@ int mana_detach(struct net_device *ndev, bool from_close)
ASSERT_RTNL();
+ /* If already detached (indicates detach succeeded but attach failed
+ * previously). Now skip mana detach and just retry mana_attach.
+ */
+ if (!from_close && !netif_device_present(ndev))
+ return 0;
+
apc->port_st_save = apc->port_is_up;
apc->port_is_up = false;
diff --git a/drivers/net/ethernet/microsoft/mana/shm_channel.c b/drivers/net/ethernet/microsoft/mana/shm_channel.c
index 0f1679ebad96..d21b5db06e50 100644
--- a/drivers/net/ethernet/microsoft/mana/shm_channel.c
+++ b/drivers/net/ethernet/microsoft/mana/shm_channel.c
@@ -61,11 +61,6 @@ union smc_proto_hdr {
};
}; /* HW DATA */
-#define SMC_APERTURE_BITS 256
-#define SMC_BASIC_UNIT (sizeof(u32))
-#define SMC_APERTURE_DWORDS (SMC_APERTURE_BITS / (SMC_BASIC_UNIT * 8))
-#define SMC_LAST_DWORD (SMC_APERTURE_DWORDS - 1)
-
static int mana_smc_poll_register(void __iomem *base, bool reset)
{
void __iomem *ptr = base + SMC_LAST_DWORD * SMC_BASIC_UNIT;
diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c
index 9861daa82d9e..b70262e70baf 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c
@@ -1036,11 +1036,13 @@ static void qed_cid_map_free(struct qed_hwfn *p_hwfn)
for (type = 0; type < MAX_CONN_TYPES; type++) {
bitmap_free(p_mngr->acquired[type].cid_map);
+ p_mngr->acquired[type].cid_map = NULL;
p_mngr->acquired[type].max_count = 0;
p_mngr->acquired[type].start_cid = 0;
for (vf = 0; vf < MAX_NUM_VFS; vf++) {
bitmap_free(p_mngr->acquired_vf[type][vf].cid_map);
+ p_mngr->acquired_vf[type][vf].cid_map = NULL;
p_mngr->acquired_vf[type][vf].max_count = 0;
p_mngr->acquired_vf[type][vf].start_cid = 0;
}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c
index 42c6dcfb1f0f..dd75c47758e1 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_dev.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c
@@ -5103,6 +5103,13 @@ static int qed_init_wfq_param(struct qed_hwfn *p_hwfn,
return -EINVAL;
}
+ /* All vports are already or become configured, nothing to distribute */
+ if (non_requested_count == 0) {
+ p_hwfn->qm_info.wfq_data[vport_id].min_speed = req_rate;
+ p_hwfn->qm_info.wfq_data[vport_id].configured = true;
+ return 0;
+ }
+
total_left_rate = min_pf_rate - total_req_min_rate;
left_rate_per_vp = total_left_rate / non_requested_count;
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
index 269c0449760c..78d4df55740a 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
@@ -213,8 +213,8 @@ static void rmnet_dellink(struct net_device *dev, struct list_head *head)
ep = rmnet_get_endpoint(real_port, mux_id);
if (ep) {
hlist_del_init_rcu(&ep->hlnode);
- rmnet_vnd_dellink(mux_id, real_port, ep);
- kfree(ep);
+ real_port->nr_rmnet_devs--;
+ kfree_rcu(ep, rcu);
}
netdev_upper_dev_unlink(real_dev, dev);
@@ -238,9 +238,9 @@ static void rmnet_force_unassociate_device(struct net_device *real_dev)
hash_for_each_safe(port->muxed_ep, bkt_ep, tmp_ep, ep, hlnode) {
unregister_netdevice_queue(ep->egress_dev, &list);
netdev_upper_dev_unlink(real_dev, ep->egress_dev);
- rmnet_vnd_dellink(ep->mux_id, port, ep);
hlist_del_init_rcu(&ep->hlnode);
- kfree(ep);
+ port->nr_rmnet_devs--;
+ kfree_rcu(ep, rcu);
}
rmnet_unregister_real_device(real_dev);
unregister_netdevice_many(&list);
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
index ed112d51ac5a..f50fae1c6bdd 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h
@@ -18,6 +18,7 @@ struct rmnet_endpoint {
u8 mux_id;
struct net_device *egress_dev;
struct hlist_node hlnode;
+ struct rcu_head rcu;
};
struct rmnet_egress_agg_params {
diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c
index ef13109c49cf..55105d34bc79 100644
--- a/drivers/net/ethernet/realtek/rtase/rtase_main.c
+++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c
@@ -239,6 +239,8 @@ static void rtase_tx_clear(struct rtase_private *tp)
rtase_tx_clear_range(ring, ring->dirty_idx, RTASE_NUM_DESC);
ring->cur_idx = 0;
ring->dirty_idx = 0;
+
+ netdev_tx_reset_subqueue(tp->dev, i);
}
}
@@ -1563,8 +1565,9 @@ static void rtase_dump_tally_counter(const struct rtase_private *tp)
rtase_w32(tp, RTASE_DTCCR0, cmd);
rtase_w32(tp, RTASE_DTCCR0, cmd | RTASE_COUNTER_DUMP);
- err = read_poll_timeout(rtase_r32, val, !(val & RTASE_COUNTER_DUMP),
- 10, 250, false, tp, RTASE_DTCCR0);
+ err = read_poll_timeout_atomic(rtase_r32, val,
+ !(val & RTASE_COUNTER_DUMP),
+ 10, 250, false, tp, RTASE_DTCCR0);
if (err == -ETIMEDOUT)
netdev_err(tp->dev, "error occurred in dump tally counter\n");
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index 1dbfadb2a881..5f88733094d0 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -1108,9 +1108,12 @@ static int ravb_stop_dma(struct net_device *ndev)
/* Request for transmission suspension */
ravb_modify(ndev, CCC, CCC_DTSR, CCC_DTSR);
- error = ravb_wait(ndev, CSR, CSR_DTS, CSR_DTS);
- if (error)
- netdev_err(ndev, "failed to stop AXI BUS\n");
+ /* Access to URAM will not be suspended if WoL is enabled. */
+ if (!priv->wol_enabled) {
+ error = ravb_wait(ndev, CSR, CSR_DTS, CSR_DTS);
+ if (error)
+ netdev_err(ndev, "failed to stop AXI BUS\n");
+ }
/* Stop AVB-DMAC process */
return ravb_set_opmode(ndev, CCC_OPC_CONFIG);
diff --git a/drivers/net/ethernet/renesas/rtsn.c b/drivers/net/ethernet/renesas/rtsn.c
index 03a2669f0518..ee8381b60b8d 100644
--- a/drivers/net/ethernet/renesas/rtsn.c
+++ b/drivers/net/ethernet/renesas/rtsn.c
@@ -797,11 +797,11 @@ static int rtsn_mdio_alloc(struct rtsn_private *priv)
/* Enter config mode before registering the MDIO bus */
ret = rtsn_reset(priv);
if (ret)
- goto out_free_bus;
+ goto out_put_node;
ret = rtsn_change_mode(priv, OCR_OPC_CONFIG);
if (ret)
- goto out_free_bus;
+ goto out_put_node;
rtsn_modify(priv, MPIC, MPIC_PSMCS_MASK | MPIC_PSMHT_MASK,
MPIC_PSMCS_DEFAULT | MPIC_PSMHT_DEFAULT);
@@ -824,6 +824,8 @@ static int rtsn_mdio_alloc(struct rtsn_private *priv)
return 0;
+out_put_node:
+ of_node_put(mdio_node);
out_free_bus:
mdiobus_free(mii);
return ret;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c
index bcb8e000e720..4ac979d874d6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c
@@ -28,13 +28,16 @@
/*
* TX/RX Clock Delay Bit Masks:
- * - TX Delay: bits [14:8] — TX_CLK delay (unit: 0.1ns per bit)
- * - RX Delay: bits [30:24] — RX_CLK delay (unit: 0.1ns per bit)
+ * - TX Delay: bits [14:8] — TX_CLK delay (unit: 0.02ns per bit)
+ * - RX Delay: bits [30:24] — RX_CLK delay (unit: 0.02ns per bit)
*/
#define EIC7700_ETH_TX_ADJ_DELAY GENMASK(14, 8)
#define EIC7700_ETH_RX_ADJ_DELAY GENMASK(30, 24)
-#define EIC7700_MAX_DELAY_UNIT 0x7F
+#define EIC7700_MAX_DELAY_STEPS 0x7F
+#define EIC7700_DELAY_STEP_PS 20
+#define EIC7700_MAX_DELAY_PS \
+ (EIC7700_MAX_DELAY_STEPS * EIC7700_DELAY_STEP_PS)
static const char * const eic7700_clk_names[] = {
"tx", "axi", "cfg",
@@ -42,6 +45,15 @@ static const char * const eic7700_clk_names[] = {
struct eic7700_qos_priv {
struct plat_stmmacenet_data *plat_dat;
+ struct regmap *eic7700_hsp_regmap;
+ u32 eth_axi_lp_ctrl_offset;
+ u32 eth_phy_ctrl_offset;
+ u32 eth_clk_offset;
+ u32 eth_txd_offset;
+ u32 eth_rxd_offset;
+ u32 eth_clk_dly_param;
+ bool has_txd_offset;
+ bool has_rxd_offset;
};
static int eic7700_clks_config(void *priv, bool enabled)
@@ -61,8 +73,34 @@ static int eic7700_clks_config(void *priv, bool enabled)
static int eic7700_dwmac_init(struct device *dev, void *priv)
{
struct eic7700_qos_priv *dwc = priv;
+ int ret;
+
+ ret = eic7700_clks_config(dwc, true);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(dwc->eic7700_hsp_regmap,
+ dwc->eth_phy_ctrl_offset,
+ EIC7700_ETH_TX_CLK_SEL |
+ EIC7700_ETH_PHY_INTF_SELI);
+ if (ret) {
+ eic7700_clks_config(dwc, false);
+ return ret;
+ }
- return eic7700_clks_config(dwc, true);
+ regmap_write(dwc->eic7700_hsp_regmap, dwc->eth_axi_lp_ctrl_offset,
+ EIC7700_ETH_CSYSREQ_VAL);
+
+ if (dwc->has_txd_offset)
+ regmap_write(dwc->eic7700_hsp_regmap, dwc->eth_txd_offset, 0);
+
+ if (dwc->has_rxd_offset)
+ regmap_write(dwc->eic7700_hsp_regmap, dwc->eth_rxd_offset, 0);
+
+ regmap_write(dwc->eic7700_hsp_regmap, dwc->eth_clk_offset,
+ dwc->eth_clk_dly_param);
+
+ return 0;
}
static void eic7700_dwmac_exit(struct device *dev, void *priv)
@@ -93,13 +131,7 @@ static int eic7700_dwmac_probe(struct platform_device *pdev)
struct plat_stmmacenet_data *plat_dat;
struct stmmac_resources stmmac_res;
struct eic7700_qos_priv *dwc_priv;
- struct regmap *eic7700_hsp_regmap;
- u32 eth_axi_lp_ctrl_offset;
- u32 eth_phy_ctrl_offset;
- u32 eth_phy_ctrl_regset;
- u32 eth_rxd_dly_offset;
- u32 eth_dly_param = 0;
- u32 delay_ps;
+ u32 delay_ps, val;
int i, ret;
ret = stmmac_get_platform_resources(pdev, &stmmac_res);
@@ -119,10 +151,20 @@ static int eic7700_dwmac_probe(struct platform_device *pdev)
/* Read rx-internal-delay-ps and update rx_clk delay */
if (!of_property_read_u32(pdev->dev.of_node,
"rx-internal-delay-ps", &delay_ps)) {
- u32 val = min(delay_ps / 100, EIC7700_MAX_DELAY_UNIT);
+ if (delay_ps % EIC7700_DELAY_STEP_PS)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "rx delay must be multiple of %dps\n",
+ EIC7700_DELAY_STEP_PS);
+
+ if (delay_ps > EIC7700_MAX_DELAY_PS)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "rx delay out of range\n");
- eth_dly_param &= ~EIC7700_ETH_RX_ADJ_DELAY;
- eth_dly_param |= FIELD_PREP(EIC7700_ETH_RX_ADJ_DELAY, val);
+ val = delay_ps / EIC7700_DELAY_STEP_PS;
+
+ dwc_priv->eth_clk_dly_param &= ~EIC7700_ETH_RX_ADJ_DELAY;
+ dwc_priv->eth_clk_dly_param |=
+ FIELD_PREP(EIC7700_ETH_RX_ADJ_DELAY, val);
} else {
return dev_err_probe(&pdev->dev, -EINVAL,
"missing required property rx-internal-delay-ps\n");
@@ -131,55 +173,65 @@ static int eic7700_dwmac_probe(struct platform_device *pdev)
/* Read tx-internal-delay-ps and update tx_clk delay */
if (!of_property_read_u32(pdev->dev.of_node,
"tx-internal-delay-ps", &delay_ps)) {
- u32 val = min(delay_ps / 100, EIC7700_MAX_DELAY_UNIT);
+ if (delay_ps % EIC7700_DELAY_STEP_PS)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "tx delay must be multiple of %dps\n",
+ EIC7700_DELAY_STEP_PS);
+
+ if (delay_ps > EIC7700_MAX_DELAY_PS)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "tx delay out of range\n");
- eth_dly_param &= ~EIC7700_ETH_TX_ADJ_DELAY;
- eth_dly_param |= FIELD_PREP(EIC7700_ETH_TX_ADJ_DELAY, val);
+ val = delay_ps / EIC7700_DELAY_STEP_PS;
+
+ dwc_priv->eth_clk_dly_param &= ~EIC7700_ETH_TX_ADJ_DELAY;
+ dwc_priv->eth_clk_dly_param |=
+ FIELD_PREP(EIC7700_ETH_TX_ADJ_DELAY, val);
} else {
return dev_err_probe(&pdev->dev, -EINVAL,
"missing required property tx-internal-delay-ps\n");
}
- eic7700_hsp_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
- "eswin,hsp-sp-csr");
- if (IS_ERR(eic7700_hsp_regmap))
+ dwc_priv->eic7700_hsp_regmap =
+ syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "eswin,hsp-sp-csr");
+ if (IS_ERR(dwc_priv->eic7700_hsp_regmap))
return dev_err_probe(&pdev->dev,
- PTR_ERR(eic7700_hsp_regmap),
+ PTR_ERR(dwc_priv->eic7700_hsp_regmap),
"Failed to get hsp-sp-csr regmap\n");
ret = of_property_read_u32_index(pdev->dev.of_node,
"eswin,hsp-sp-csr",
- 1, &eth_phy_ctrl_offset);
+ 1, &dwc_priv->eth_phy_ctrl_offset);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"can't get eth_phy_ctrl_offset\n");
- regmap_read(eic7700_hsp_regmap, eth_phy_ctrl_offset,
- &eth_phy_ctrl_regset);
- eth_phy_ctrl_regset |=
- (EIC7700_ETH_TX_CLK_SEL | EIC7700_ETH_PHY_INTF_SELI);
- regmap_write(eic7700_hsp_regmap, eth_phy_ctrl_offset,
- eth_phy_ctrl_regset);
-
ret = of_property_read_u32_index(pdev->dev.of_node,
"eswin,hsp-sp-csr",
- 2, &eth_axi_lp_ctrl_offset);
+ 2, &dwc_priv->eth_axi_lp_ctrl_offset);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"can't get eth_axi_lp_ctrl_offset\n");
- regmap_write(eic7700_hsp_regmap, eth_axi_lp_ctrl_offset,
- EIC7700_ETH_CSYSREQ_VAL);
-
ret = of_property_read_u32_index(pdev->dev.of_node,
"eswin,hsp-sp-csr",
- 3, &eth_rxd_dly_offset);
+ 3, &dwc_priv->eth_clk_offset);
if (ret)
return dev_err_probe(&pdev->dev, ret,
- "can't get eth_rxd_dly_offset\n");
+ "can't get eth_clk_offset\n");
+
+ ret = of_property_read_u32_index(pdev->dev.of_node,
+ "eswin,hsp-sp-csr",
+ 4, &dwc_priv->eth_txd_offset);
+ if (!ret)
+ dwc_priv->has_txd_offset = true;
- regmap_write(eic7700_hsp_regmap, eth_rxd_dly_offset,
- eth_dly_param);
+ ret = of_property_read_u32_index(pdev->dev.of_node,
+ "eswin,hsp-sp-csr",
+ 5, &dwc_priv->eth_rxd_offset);
+ if (!ret)
+ dwc_priv->has_rxd_offset = true;
plat_dat->num_clks = ARRAY_SIZE(eic7700_clk_names);
plat_dat->clks = devm_kcalloc(&pdev->dev,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-nuvoton.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-nuvoton.c
index e2240b68ad98..2ab6ecac6422 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-nuvoton.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-nuvoton.c
@@ -100,6 +100,8 @@ static int nvt_gmac_probe(struct platform_device *pdev)
if (!priv)
return dev_err_probe(dev, -ENOMEM, "Failed to allocate private data\n");
+ priv->dev = dev;
+
priv->regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node, "nuvoton,sys",
1, &priv->macid);
if (IS_ERR(priv->regmap))
diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.c b/drivers/net/ethernet/ti/icssm/icssm_prueth.c
index 53bbd9290904..b7e94244355a 100644
--- a/drivers/net/ethernet/ti/icssm/icssm_prueth.c
+++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.c
@@ -1825,6 +1825,7 @@ static int icssm_prueth_probe(struct platform_device *pdev)
dev_err(dev, "%pOF error reading port_id %d\n",
eth_node, ret);
of_node_put(eth_node);
+ of_node_put(eth_ports_node);
return ret;
}
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
index d3772d01e00b..2451f6b20b11 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
@@ -2480,8 +2480,11 @@ int wx_sw_init(struct wx *wx)
wx->oem_svid = pdev->subsystem_vendor;
wx->oem_ssid = pdev->subsystem_device;
wx->bus.device = PCI_SLOT(pdev->devfn);
- wx->bus.func = FIELD_GET(WX_CFG_PORT_ST_LANID,
- rd32(wx, WX_CFG_PORT_ST));
+ if (pdev->is_virtfn)
+ wx->bus.func = PCI_FUNC(pdev->devfn);
+ else
+ wx->bus.func = FIELD_GET(WX_CFG_PORT_ST_LANID,
+ rd32(wx, WX_CFG_PORT_ST));
if (wx->oem_svid == PCI_VENDOR_ID_WANGXUN ||
pdev->is_virtfn) {
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
index 29cdbed2e5ec..94ff8f5f0b4c 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
@@ -99,8 +99,8 @@ int wx_request_msix_irqs_vf(struct wx *wx)
}
}
- err = request_threaded_irq(wx->msix_entry->vector, wx_msix_misc_vf,
- NULL, IRQF_ONESHOT, netdev->name, wx);
+ err = request_irq(wx->msix_entry->vector, wx_msix_misc_vf,
+ 0, netdev->name, wx);
if (err) {
wx_err(wx, "request_irq for msix_other failed: %d\n", err);
goto free_queue_irqs;
diff --git a/drivers/net/fddi/defza.c b/drivers/net/fddi/defza.c
index 064fa484f797..9bfecc87d6b2 100644
--- a/drivers/net/fddi/defza.c
+++ b/drivers/net/fddi/defza.c
@@ -984,7 +984,7 @@ static irqreturn_t fza_interrupt(int irq, void *dev_id)
case FZA_STATE_UNINITIALIZED:
netif_carrier_off(dev);
- timer_delete_sync(&fp->reset_timer);
+ timer_delete_sync_try(&fp->reset_timer);
fp->ring_cmd_index = 0;
fp->ring_uns_index = 0;
fp->ring_rmc_tx_index = 0;
@@ -1018,7 +1018,9 @@ static irqreturn_t fza_interrupt(int irq, void *dev_id)
fp->queue_active = 0;
netif_stop_queue(dev);
pr_debug("%s: queue stopped\n", fp->name);
- timer_delete_sync(&fp->reset_timer);
+
+ spin_lock(&fp->lock);
+ timer_delete(&fp->reset_timer);
pr_warn("%s: halted, reason: %x\n", fp->name,
FZA_STATUS_GET_HALT(status));
fza_regs_dump(fp);
@@ -1027,6 +1029,8 @@ static irqreturn_t fza_interrupt(int irq, void *dev_id)
fp->timer_state = 0;
fp->reset_timer.expires = jiffies + 45 * HZ;
add_timer(&fp->reset_timer);
+ spin_unlock(&fp->lock);
+
break;
default:
@@ -1046,7 +1050,9 @@ static irqreturn_t fza_interrupt(int irq, void *dev_id)
static void fza_reset_timer(struct timer_list *t)
{
struct fza_private *fp = timer_container_of(fp, t, reset_timer);
+ unsigned long flags;
+ spin_lock_irqsave(&fp->lock, flags);
if (!fp->timer_state) {
pr_err("%s: RESET timed out!\n", fp->name);
pr_info("%s: trying harder...\n", fp->name);
@@ -1069,6 +1075,7 @@ static void fza_reset_timer(struct timer_list *t)
fp->reset_timer.expires = jiffies + 45 * HZ;
}
add_timer(&fp->reset_timer);
+ spin_unlock_irqrestore(&fp->lock, flags);
}
static int fza_set_mac_address(struct net_device *dev, void *addr)
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index c6563367d382..715180c3a1b3 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -632,7 +632,7 @@ static int geneve_post_decap_hint(const struct sock *sk, struct sk_buff *skb,
uh = udp_hdr(skb);
uh->len = htons(skb->len - gro_hint->nested_tp_offset);
if (uh->check) {
- len = skb->len - gro_hint->nested_nh_offset;
+ len = skb->len - gro_hint->nested_tp_offset;
skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL_CSUM;
if (gro_hint->nested_is_v6)
uh->check = ~udp_v6_check(len, &ipv6h->saddr,
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
index 5407d2ed71b3..43aa1bfd41cf 100644
--- a/drivers/net/ifb.c
+++ b/drivers/net/ifb.c
@@ -211,12 +211,12 @@ static void ifb_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
switch (stringset) {
case ETH_SS_STATS:
- for (i = 0; i < dev->real_num_rx_queues; i++)
+ for (i = 0; i < dev->num_tx_queues; i++)
for (j = 0; j < IFB_Q_STATS_LEN; j++)
ethtool_sprintf(&p, "rx_queue_%u_%.18s",
i, ifb_q_stats_desc[j].desc);
- for (i = 0; i < dev->real_num_tx_queues; i++)
+ for (i = 0; i < dev->num_tx_queues; i++)
for (j = 0; j < IFB_Q_STATS_LEN; j++)
ethtool_sprintf(&p, "tx_queue_%u_%.18s",
i, ifb_q_stats_desc[j].desc);
@@ -229,8 +229,7 @@ static int ifb_get_sset_count(struct net_device *dev, int sset)
{
switch (sset) {
case ETH_SS_STATS:
- return IFB_Q_STATS_LEN * (dev->real_num_rx_queues +
- dev->real_num_tx_queues);
+ return IFB_Q_STATS_LEN * dev->num_tx_queues * 2;
default:
return -EOPNOTSUPP;
}
@@ -262,12 +261,12 @@ static void ifb_get_ethtool_stats(struct net_device *dev,
struct ifb_q_private *txp;
int i;
- for (i = 0; i < dev->real_num_rx_queues; i++) {
+ for (i = 0; i < dev->num_tx_queues; i++) {
txp = dp->tx_private + i;
ifb_fill_stats_data(&data, &txp->rx_stats);
}
- for (i = 0; i < dev->real_num_tx_queues; i++) {
+ for (i = 0; i < dev->num_tx_queues; i++) {
txp = dp->tx_private + i;
ifb_fill_stats_data(&data, &txp->tx_stats);
}
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index 6147ee8b1d78..fb009120a924 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -26,6 +26,8 @@
#include <uapi/linux/if_macsec.h>
+static struct workqueue_struct *macsec_wq;
+
/* SecTAG length = macsec_eth_header without the optional SCI */
#define MACSEC_TAG_LEN 6
@@ -174,9 +176,10 @@ static void macsec_rxsc_put(struct macsec_rx_sc *sc)
call_rcu(&sc->rcu_head, free_rx_sc_rcu);
}
-static void free_rxsa(struct rcu_head *head)
+static void free_rxsa_work(struct work_struct *work)
{
- struct macsec_rx_sa *sa = container_of(head, struct macsec_rx_sa, rcu);
+ struct macsec_rx_sa *sa =
+ container_of(to_rcu_work(work), struct macsec_rx_sa, destroy_work);
crypto_free_aead(sa->key.tfm);
free_percpu(sa->stats);
@@ -186,7 +189,7 @@ static void free_rxsa(struct rcu_head *head)
static void macsec_rxsa_put(struct macsec_rx_sa *sa)
{
if (refcount_dec_and_test(&sa->refcnt))
- call_rcu(&sa->rcu, free_rxsa);
+ queue_rcu_work(macsec_wq, &sa->destroy_work);
}
static struct macsec_tx_sa *macsec_txsa_get(struct macsec_tx_sa __rcu *ptr)
@@ -202,9 +205,10 @@ static struct macsec_tx_sa *macsec_txsa_get(struct macsec_tx_sa __rcu *ptr)
return sa;
}
-static void free_txsa(struct rcu_head *head)
+static void free_txsa_work(struct work_struct *work)
{
- struct macsec_tx_sa *sa = container_of(head, struct macsec_tx_sa, rcu);
+ struct macsec_tx_sa *sa =
+ container_of(to_rcu_work(work), struct macsec_tx_sa, destroy_work);
crypto_free_aead(sa->key.tfm);
free_percpu(sa->stats);
@@ -214,7 +218,7 @@ static void free_txsa(struct rcu_head *head)
static void macsec_txsa_put(struct macsec_tx_sa *sa)
{
if (refcount_dec_and_test(&sa->refcnt))
- call_rcu(&sa->rcu, free_txsa);
+ queue_rcu_work(macsec_wq, &sa->destroy_work);
}
static struct macsec_cb *macsec_skb_cb(struct sk_buff *skb)
@@ -804,7 +808,8 @@ static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u
if (pn + 1 > rx_sa->next_pn_halves.lower) {
rx_sa->next_pn_halves.lower = pn + 1;
} else if (secy->xpn &&
- !pn_same_half(pn, rx_sa->next_pn_halves.lower)) {
+ (pn + 1 == 0 ||
+ !pn_same_half(pn, rx_sa->next_pn_halves.lower))) {
rx_sa->next_pn_halves.upper++;
rx_sa->next_pn_halves.lower = pn + 1;
}
@@ -1407,6 +1412,7 @@ static int init_rx_sa(struct macsec_rx_sa *rx_sa, char *sak, int key_len,
rx_sa->next_pn = 1;
refcount_set(&rx_sa->refcnt, 1);
spin_lock_init(&rx_sa->lock);
+ INIT_RCU_WORK(&rx_sa->destroy_work, free_rxsa_work);
return 0;
}
@@ -1506,6 +1512,7 @@ static int init_tx_sa(struct macsec_tx_sa *tx_sa, char *sak, int key_len,
tx_sa->active = false;
refcount_set(&tx_sa->refcnt, 1);
spin_lock_init(&tx_sa->lock);
+ INIT_RCU_WORK(&tx_sa->destroy_work, free_txsa_work);
return 0;
}
@@ -4505,25 +4512,35 @@ static int __init macsec_init(void)
{
int err;
+ macsec_wq = alloc_workqueue("macsec", WQ_UNBOUND, 0);
+ if (!macsec_wq)
+ return -ENOMEM;
+
pr_info("MACsec IEEE 802.1AE\n");
err = register_netdevice_notifier(&macsec_notifier);
if (err)
- return err;
+ goto err_destroy_wq;
err = rtnl_link_register(&macsec_link_ops);
if (err)
- goto notifier;
+ goto err_notifier;
err = genl_register_family(&macsec_fam);
if (err)
- goto rtnl;
+ goto err_rtnl;
return 0;
-rtnl:
+err_rtnl:
rtnl_link_unregister(&macsec_link_ops);
-notifier:
+err_notifier:
unregister_netdevice_notifier(&macsec_notifier);
+err_destroy_wq:
+ /* Precautionary, mirrors macsec_exit() to stay safe if work
+ * ever becomes queueable before this point in the future.
+ */
+ rcu_barrier();
+ destroy_workqueue(macsec_wq);
return err;
}
@@ -4533,6 +4550,7 @@ static void __exit macsec_exit(void)
rtnl_link_unregister(&macsec_link_ops);
unregister_netdevice_notifier(&macsec_notifier);
rcu_barrier();
+ destroy_workqueue(macsec_wq);
}
module_init(macsec_init);
diff --git a/drivers/net/net_failover.c b/drivers/net/net_failover.c
index d0361aaf25ef..3f7d31033bae 100644
--- a/drivers/net/net_failover.c
+++ b/drivers/net/net_failover.c
@@ -502,7 +502,7 @@ static int net_failover_slave_register(struct net_device *slave_dev,
/* Align MTU of slave with failover dev */
orig_mtu = slave_dev->mtu;
- err = dev_set_mtu(slave_dev, failover_dev->mtu);
+ err = netif_set_mtu(slave_dev, failover_dev->mtu);
if (err) {
netdev_err(failover_dev, "unable to change mtu of %s to %u register failed\n",
slave_dev->name, failover_dev->mtu);
@@ -512,11 +512,11 @@ static int net_failover_slave_register(struct net_device *slave_dev,
dev_hold(slave_dev);
if (netif_running(failover_dev)) {
- err = dev_open(slave_dev, NULL);
+ err = netif_open(slave_dev, NULL);
if (err && (err != -EBUSY)) {
netdev_err(failover_dev, "Opening slave %s failed err:%d\n",
slave_dev->name, err);
- goto err_dev_open;
+ goto err_netif_open;
}
}
@@ -562,10 +562,10 @@ static int net_failover_slave_register(struct net_device *slave_dev,
err_vlan_add:
dev_uc_unsync(slave_dev, failover_dev);
dev_mc_unsync(slave_dev, failover_dev);
- dev_close(slave_dev);
-err_dev_open:
+ netif_close(slave_dev);
+err_netif_open:
dev_put(slave_dev);
- dev_set_mtu(slave_dev, orig_mtu);
+ netif_set_mtu(slave_dev, orig_mtu);
done:
return err;
}
diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index a05af192caf3..a750768912b5 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -1182,7 +1182,8 @@ void nsim_destroy(struct netdevsim *ns)
unregister_netdevice_notifier_dev_net(ns->netdev, &ns->nb,
&ns->nn);
- nsim_psp_uninit(ns);
+ if (nsim_dev_port_is_pf(ns->nsim_dev_port))
+ nsim_psp_uninit(ns);
rtnl_lock();
peer = rtnl_dereference(ns->peer);
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index 7e129dddbbe7..d909c4160ea1 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -120,7 +120,9 @@ struct netdevsim {
u64_stats_t tx_packets;
u64_stats_t tx_bytes;
struct u64_stats_sync syncp;
- struct psp_dev *dev;
+ struct psp_dev __rcu *dev;
+ struct dentry *rereg;
+ struct mutex rereg_lock;
u32 spi;
u32 assoc_cnt;
} psp;
diff --git a/drivers/net/netdevsim/psp.c b/drivers/net/netdevsim/psp.c
index 0b4d717253b0..6936ecb8173e 100644
--- a/drivers/net/netdevsim/psp.c
+++ b/drivers/net/netdevsim/psp.c
@@ -19,6 +19,7 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns,
struct netdevsim *peer_ns, struct skb_ext **psp_ext)
{
enum skb_drop_reason rc = 0;
+ struct psp_dev *peer_psd;
struct psp_assoc *pas;
struct net *net;
void **ptr;
@@ -48,7 +49,8 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns,
}
/* Now pretend we just received this frame */
- if (peer_ns->psp.dev->config.versions & (1 << pas->version)) {
+ peer_psd = rcu_dereference(peer_ns->psp.dev);
+ if (peer_psd && peer_psd->config.versions & (1 << pas->version)) {
bool strip_icv = false;
u8 generation;
@@ -61,8 +63,7 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns,
skb_ext_reset(skb);
skb->mac_len = ETH_HLEN;
- if (psp_dev_rcv(skb, peer_ns->psp.dev->id, generation,
- strip_icv)) {
+ if (psp_dev_rcv(skb, peer_psd->id, generation, strip_icv)) {
rc = SKB_DROP_REASON_PSP_OUTPUT;
goto out_unlock;
}
@@ -209,26 +210,50 @@ static struct psp_dev_caps nsim_psp_caps = {
.assoc_drv_spc = sizeof(void *),
};
-void nsim_psp_uninit(struct netdevsim *ns)
+static void __nsim_psp_uninit(struct netdevsim *ns, bool teardown)
{
- if (!IS_ERR(ns->psp.dev))
- psp_dev_unregister(ns->psp.dev);
+ struct psp_dev *psd;
+
+ psd = rcu_dereference_protected(ns->psp.dev,
+ teardown ||
+ lockdep_is_held(&ns->psp.rereg_lock));
+ if (psd) {
+ rcu_assign_pointer(ns->psp.dev, NULL);
+ synchronize_rcu();
+ psp_dev_unregister(psd);
+ }
WARN_ON(ns->psp.assoc_cnt);
}
+void nsim_psp_uninit(struct netdevsim *ns)
+{
+ debugfs_remove(ns->psp.rereg);
+ mutex_destroy(&ns->psp.rereg_lock);
+ __nsim_psp_uninit(ns, true);
+}
+
static ssize_t
nsim_psp_rereg_write(struct file *file, const char __user *data, size_t count,
loff_t *ppos)
{
struct netdevsim *ns = file->private_data;
- int err;
+ struct psp_dev *psd;
+ ssize_t ret;
+
+ mutex_lock(&ns->psp.rereg_lock);
+ __nsim_psp_uninit(ns, false);
- nsim_psp_uninit(ns);
+ psd = psp_dev_create(ns->netdev, &nsim_psp_ops, &nsim_psp_caps, ns);
+ if (IS_ERR(psd)) {
+ ret = PTR_ERR(psd);
+ goto out;
+ }
- ns->psp.dev = psp_dev_create(ns->netdev, &nsim_psp_ops,
- &nsim_psp_caps, ns);
- err = PTR_ERR_OR_ZERO(ns->psp.dev);
- return err ?: count;
+ rcu_assign_pointer(ns->psp.dev, psd);
+ ret = count;
+out:
+ mutex_unlock(&ns->psp.rereg_lock);
+ return ret;
}
static const struct file_operations nsim_psp_rereg_fops = {
@@ -241,14 +266,16 @@ static const struct file_operations nsim_psp_rereg_fops = {
int nsim_psp_init(struct netdevsim *ns)
{
struct dentry *ddir = ns->nsim_dev_port->ddir;
- int err;
+ struct psp_dev *psd;
+
+ psd = psp_dev_create(ns->netdev, &nsim_psp_ops, &nsim_psp_caps, ns);
+ if (IS_ERR(psd))
+ return PTR_ERR(psd);
- ns->psp.dev = psp_dev_create(ns->netdev, &nsim_psp_ops,
- &nsim_psp_caps, ns);
- err = PTR_ERR_OR_ZERO(ns->psp.dev);
- if (err)
- return err;
+ rcu_assign_pointer(ns->psp.dev, psd);
- debugfs_create_file("psp_rereg", 0200, ddir, ns, &nsim_psp_rereg_fops);
+ mutex_init(&ns->psp.rereg_lock);
+ ns->psp.rereg = debugfs_create_file("psp_rereg", 0200, ddir, ns,
+ &nsim_psp_rereg_fops);
return 0;
}
diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c
index db43a1f8a07a..a6b777a9c2d9 100644
--- a/drivers/net/ovpn/io.c
+++ b/drivers/net/ovpn/io.c
@@ -85,17 +85,24 @@ static void ovpn_netdev_write(struct ovpn_peer *peer, struct sk_buff *skb)
skb_scrub_packet(skb, true);
/* network header reset in ovpn_decrypt_post() */
+ skb_reset_mac_header(skb);
skb_reset_transport_header(skb);
skb_reset_inner_headers(skb);
/* cause packet to be "received" by the interface */
pkt_len = skb->len;
+ /* we may get here in process context in case of TCP connections,
+ * therefore we have to disable BHs to ensure gro_cells_receive()
+ * and dev_dstats_rx_add() do not get corrupted or enter deadlock
+ */
+ local_bh_disable();
ret = gro_cells_receive(&peer->ovpn->gro_cells, skb);
if (likely(ret == NET_RX_SUCCESS)) {
/* update RX stats with the size of decrypted packet */
ovpn_peer_stats_increment_rx(&peer->vpn_stats, pkt_len);
dev_dstats_rx_add(peer->ovpn->dev, pkt_len);
}
+ local_bh_enable();
}
void ovpn_decrypt_post(void *data, int ret)
@@ -194,7 +201,7 @@ void ovpn_decrypt_post(void *data, int ret)
skb = NULL;
drop:
if (unlikely(skb))
- dev_dstats_rx_dropped(peer->ovpn->dev);
+ ovpn_dev_dstats_rx_dropped(peer->ovpn->dev);
kfree_skb(skb);
drop_nocount:
if (likely(peer))
@@ -218,7 +225,7 @@ void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb)
net_info_ratelimited("%s: no available key for peer %u, key-id: %u\n",
netdev_name(peer->ovpn->dev), peer->id,
key_id);
- dev_dstats_rx_dropped(peer->ovpn->dev);
+ ovpn_dev_dstats_rx_dropped(peer->ovpn->dev);
kfree_skb(skb);
ovpn_peer_put(peer);
return;
@@ -294,7 +301,7 @@ err_unlock:
rcu_read_unlock();
err:
if (unlikely(skb))
- dev_dstats_tx_dropped(peer->ovpn->dev);
+ ovpn_dev_dstats_tx_dropped(peer->ovpn->dev);
if (likely(peer))
ovpn_peer_put(peer);
if (likely(ks))
@@ -336,7 +343,7 @@ static void ovpn_send(struct ovpn_priv *ovpn, struct sk_buff *skb,
*/
skb_list_walk_safe(skb, curr, next) {
if (unlikely(!ovpn_encrypt_one(peer, curr))) {
- dev_dstats_tx_dropped(ovpn->dev);
+ ovpn_dev_dstats_tx_dropped(ovpn->dev);
kfree_skb(curr);
}
}
@@ -407,7 +414,7 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(!curr)) {
net_err_ratelimited("%s: skb_share_check failed for payload packet\n",
netdev_name(dev));
- dev_dstats_tx_dropped(ovpn->dev);
+ ovpn_dev_dstats_tx_dropped(ovpn->dev);
continue;
}
@@ -433,7 +440,7 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev)
drop:
ovpn_peer_put(peer);
drop_no_peer:
- dev_dstats_tx_dropped(ovpn->dev);
+ ovpn_dev_dstats_tx_dropped(ovpn->dev);
skb_tx_error(skb);
kfree_skb_list(skb);
return NETDEV_TX_OK;
diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c
index 2e0420febda0..9993c1dfe471 100644
--- a/drivers/net/ovpn/main.c
+++ b/drivers/net/ovpn/main.c
@@ -92,6 +92,8 @@ static void ovpn_net_uninit(struct net_device *dev)
{
struct ovpn_priv *ovpn = netdev_priv(dev);
+ disable_delayed_work_sync(&ovpn->keepalive_work);
+ ovpn_peers_free(ovpn, NULL, OVPN_DEL_PEER_REASON_TEARDOWN);
gro_cells_destroy(&ovpn->gro_cells);
}
@@ -208,15 +210,6 @@ static int ovpn_newlink(struct net_device *dev,
return register_netdevice(dev);
}
-static void ovpn_dellink(struct net_device *dev, struct list_head *head)
-{
- struct ovpn_priv *ovpn = netdev_priv(dev);
-
- cancel_delayed_work_sync(&ovpn->keepalive_work);
- ovpn_peers_free(ovpn, NULL, OVPN_DEL_PEER_REASON_TEARDOWN);
- unregister_netdevice_queue(dev, head);
-}
-
static int ovpn_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
struct ovpn_priv *ovpn = netdev_priv(dev);
@@ -235,7 +228,6 @@ static struct rtnl_link_ops ovpn_link_ops = {
.policy = ovpn_policy,
.maxtype = IFLA_OVPN_MAX,
.newlink = ovpn_newlink,
- .dellink = ovpn_dellink,
.fill_info = ovpn_fill_info,
};
diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c
index 291e2e5bb450..4c66c1ec497e 100644
--- a/drivers/net/ovpn/netlink.c
+++ b/drivers/net/ovpn/netlink.c
@@ -462,10 +462,12 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info)
sock_release:
ovpn_socket_release(peer);
peer_release:
- /* release right away because peer was not yet hashed, thus it is not
- * used in any context
+ /* For UDP, the peer is unreachable until added to the hashtables, so
+ * dropping the initial reference is enough. For TCP, the peer may be
+ * concurrently reachable via sk_user_data->peer until
+ * ovpn_socket_release() detaches; rely on the refcount.
*/
- ovpn_peer_release(peer);
+ ovpn_peer_put(peer);
return ret;
}
diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index c02dfab51a6e..a09d61296425 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -354,7 +354,7 @@ static void ovpn_peer_release_rcu(struct rcu_head *head)
* ovpn_peer_release - release peer private members
* @peer: the peer to release
*/
-void ovpn_peer_release(struct ovpn_peer *peer)
+static void ovpn_peer_release(struct ovpn_peer *peer)
{
ovpn_crypto_state_release(&peer->crypto);
spin_lock_bh(&peer->lock);
@@ -1034,14 +1034,29 @@ static int ovpn_peer_add_p2p(struct ovpn_priv *ovpn, struct ovpn_peer *peer)
*/
int ovpn_peer_add(struct ovpn_priv *ovpn, struct ovpn_peer *peer)
{
+ int ret = -ENODEV;
+
+ /* Prevent adding new peers while destroying the ovpn interface.
+ * Failing to do so would end up holding the device reference
+ * endlessly hostage of the new peer object with no chance of
+ * release..
+ */
+ netdev_lock(ovpn->dev);
+ if (ovpn->dev->reg_state != NETREG_REGISTERED)
+ goto out;
+
switch (ovpn->mode) {
case OVPN_MODE_MP:
- return ovpn_peer_add_mp(ovpn, peer);
+ ret = ovpn_peer_add_mp(ovpn, peer);
+ break;
case OVPN_MODE_P2P:
- return ovpn_peer_add_p2p(ovpn, peer);
+ ret = ovpn_peer_add_p2p(ovpn, peer);
+ break;
}
+out:
+ netdev_unlock(ovpn->dev);
- return -EOPNOTSUPP;
+ return ret;
}
/**
diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h
index 328401570cba..86c8cffada6d 100644
--- a/drivers/net/ovpn/peer.h
+++ b/drivers/net/ovpn/peer.h
@@ -127,7 +127,6 @@ static inline bool ovpn_peer_hold(struct ovpn_peer *peer)
return kref_get_unless_zero(&peer->refcount);
}
-void ovpn_peer_release(struct ovpn_peer *peer);
void ovpn_peer_release_kref(struct kref *kref);
/**
diff --git a/drivers/net/ovpn/stats.h b/drivers/net/ovpn/stats.h
index 53433d8b6c33..3a45b97c0056 100644
--- a/drivers/net/ovpn/stats.h
+++ b/drivers/net/ovpn/stats.h
@@ -11,6 +11,8 @@
#ifndef _NET_OVPN_OVPNSTATS_H_
#define _NET_OVPN_OVPNSTATS_H_
+#include <linux/netdevice.h>
+
/* one stat */
struct ovpn_peer_stat {
atomic64_t bytes;
@@ -44,4 +46,18 @@ static inline void ovpn_peer_stats_increment_tx(struct ovpn_peer_stats *stats,
ovpn_peer_stats_increment(&stats->tx, n);
}
+static inline void ovpn_dev_dstats_tx_dropped(struct net_device *dev)
+{
+ local_bh_disable();
+ dev_dstats_tx_dropped(dev);
+ local_bh_enable();
+}
+
+static inline void ovpn_dev_dstats_rx_dropped(struct net_device *dev)
+{
+ local_bh_disable();
+ dev_dstats_rx_dropped(dev);
+ local_bh_enable();
+}
+
#endif /* _NET_OVPN_OVPNSTATS_H_ */
diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c
index 65054cc84be5..433bd07a4f1b 100644
--- a/drivers/net/ovpn/tcp.c
+++ b/drivers/net/ovpn/tcp.c
@@ -152,7 +152,7 @@ err:
if (WARN_ON(!ovpn_peer_hold(peer)))
goto err_nopeer;
schedule_work(&peer->tcp.defer_del_work);
- dev_dstats_rx_dropped(peer->ovpn->dev);
+ ovpn_dev_dstats_rx_dropped(peer->ovpn->dev);
err_nopeer:
kfree_skb(skb);
}
@@ -298,9 +298,9 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk)
} while (peer->tcp.out_msg.len > 0);
if (!peer->tcp.out_msg.len) {
- preempt_disable();
+ local_bh_disable();
dev_dstats_tx_add(peer->ovpn->dev, skb->len);
- preempt_enable();
+ local_bh_enable();
}
kfree_skb(peer->tcp.out_msg.skb);
@@ -331,7 +331,7 @@ static void ovpn_tcp_send_sock_skb(struct ovpn_peer *peer, struct sock *sk,
ovpn_tcp_send_sock(peer, sk);
if (peer->tcp.out_msg.skb) {
- dev_dstats_tx_dropped(peer->ovpn->dev);
+ ovpn_dev_dstats_tx_dropped(peer->ovpn->dev);
kfree_skb(skb);
return;
}
@@ -353,7 +353,7 @@ void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct sock *sk,
if (sock_owned_by_user(sk)) {
if (skb_queue_len(&peer->tcp.out_queue) >=
READ_ONCE(net_hotdata.max_backlog)) {
- dev_dstats_tx_dropped(peer->ovpn->dev);
+ ovpn_dev_dstats_tx_dropped(peer->ovpn->dev);
kfree_skb(skb);
goto unlock;
}
@@ -581,14 +581,19 @@ static void ovpn_tcp_close(struct sock *sk, long timeout)
rcu_read_lock();
sock = rcu_dereference_sk_user_data(sk);
- if (!sock || !sock->peer || !ovpn_peer_hold(sock->peer)) {
+ if (!sock) {
rcu_read_unlock();
return;
}
+
peer = sock->peer;
+ if (!peer || !ovpn_peer_hold(peer)) {
+ rcu_read_unlock();
+ return;
+ }
rcu_read_unlock();
- ovpn_peer_del(sock->peer, OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT);
+ ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT);
peer->tcp.sk_cb.prot->close(sk, timeout);
ovpn_peer_put(peer);
}
diff --git a/drivers/net/ovpn/udp.c b/drivers/net/ovpn/udp.c
index 059e896b4a2f..8811aa9eedeb 100644
--- a/drivers/net/ovpn/udp.c
+++ b/drivers/net/ovpn/udp.c
@@ -125,7 +125,7 @@ static int ovpn_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
return 0;
drop:
- dev_dstats_rx_dropped(ovpn->dev);
+ ovpn_dev_dstats_rx_dropped(ovpn->dev);
drop_noovpn:
kfree_skb(skb);
return 0;
diff --git a/drivers/net/pcs/pcs-mtk-lynxi.c b/drivers/net/pcs/pcs-mtk-lynxi.c
index c12f8087af9b..a753bd88cbc2 100644
--- a/drivers/net/pcs/pcs-mtk-lynxi.c
+++ b/drivers/net/pcs/pcs-mtk-lynxi.c
@@ -129,6 +129,9 @@ static int mtk_pcs_config_polarity(struct mtk_pcs_lynxi *mpcs,
unsigned int val = 0;
int ret;
+ if (!fwnode)
+ return 0;
+
if (fwnode_property_read_bool(fwnode, "mediatek,pnswap"))
default_pol = PHY_POL_INVERT;
diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c
index 29ae73e65caa..a86129ce693c 100644
--- a/drivers/net/phy/air_en8811h.c
+++ b/drivers/net/phy/air_en8811h.c
@@ -17,6 +17,7 @@
#include <linux/phy.h>
#include <linux/phy/phy-common-props.h>
#include <linux/firmware.h>
+#include <linux/bitfield.h>
#include <linux/property.h>
#include <linux/wordpart.h>
#include <linux/unaligned.h>
@@ -170,9 +171,23 @@
#define AN8811HB_CLK_DRV_CKO_LDPWD BIT(13)
#define AN8811HB_CLK_DRV_CKO_LPPWD BIT(14)
+#define AN8811HB_MCU_SW_RST 0x5cf9f8
+#define AN8811HB_MCU_SW_RST_HOLD BIT(16)
+#define AN8811HB_MCU_SW_RST_RUN (BIT(16) | BIT(0))
+#define AN8811HB_MCU_SW_START 0x5cf9fc
+#define AN8811HB_MCU_SW_START_EN BIT(16)
+
+/* MII register constants for PBUS access (PHY addr + 8) */
+#define AIR_PBUS_ADDR_HIGH 0x1c
+#define AIR_PBUS_DATA_HIGH 0x10
+#define AIR_PBUS_REG_ADDR_HIGH_MASK GENMASK(15, 6)
+#define AIR_PBUS_REG_ADDR_LOW_MASK GENMASK(5, 2)
+
/* Led definitions */
#define EN8811H_LED_COUNT 3
+#define EN8811H_PBUS_ADDR_OFFS 8
+
/* Default LED setup:
* GPIO5 <-> LED0 On: Link detected, blink Rx/Tx
* GPIO4 <-> LED1 On: Link detected at 2500 or 1000 Mbps
@@ -201,6 +216,7 @@ struct en8811h_priv {
struct clk_hw hw;
struct phy_device *phydev;
unsigned int cko_is_enabled;
+ struct mdio_device *pbusdev;
};
enum {
@@ -254,6 +270,31 @@ static int air_phy_write_page(struct phy_device *phydev, int page)
return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page);
}
+static int __air_pbus_reg_write(struct mdio_device *mdiodev,
+ u32 pbus_reg, u32 pbus_data)
+{
+ int ret;
+
+ ret = __mdiobus_write(mdiodev->bus, mdiodev->addr, AIR_EXT_PAGE_ACCESS,
+ upper_16_bits(pbus_reg));
+ if (ret < 0)
+ return ret;
+
+ ret = __mdiobus_write(mdiodev->bus, mdiodev->addr, AIR_PBUS_ADDR_HIGH,
+ FIELD_GET(AIR_PBUS_REG_ADDR_HIGH_MASK, pbus_reg));
+ if (ret < 0)
+ return ret;
+
+ ret = __mdiobus_write(mdiodev->bus, mdiodev->addr,
+ FIELD_GET(AIR_PBUS_REG_ADDR_LOW_MASK, pbus_reg),
+ lower_16_bits(pbus_data));
+ if (ret < 0)
+ return ret;
+
+ return __mdiobus_write(mdiodev->bus, mdiodev->addr, AIR_PBUS_DATA_HIGH,
+ upper_16_bits(pbus_data));
+}
+
static int __air_buckpbus_reg_write(struct phy_device *phydev,
u32 pbus_address, u32 pbus_data)
{
@@ -570,10 +611,67 @@ static int an8811hb_load_file(struct phy_device *phydev, const char *name,
return ret;
}
+static int an8811hb_mcu_assert(struct phy_device *phydev)
+{
+ struct en8811h_priv *priv = phydev->priv;
+ int ret;
+
+ phy_lock_mdio_bus(phydev);
+
+ ret = __air_pbus_reg_write(priv->pbusdev, AN8811HB_MCU_SW_RST,
+ AN8811HB_MCU_SW_RST_HOLD);
+ if (ret < 0)
+ goto unlock;
+
+ ret = __air_pbus_reg_write(priv->pbusdev, AN8811HB_MCU_SW_START, 0);
+ if (ret < 0)
+ goto unlock;
+
+ msleep(50);
+ phydev_dbg(phydev, "MCU asserted\n");
+
+unlock:
+ phy_unlock_mdio_bus(phydev);
+ return ret;
+}
+
+static int an8811hb_mcu_deassert(struct phy_device *phydev)
+{
+ struct en8811h_priv *priv = phydev->priv;
+ int ret;
+
+ phy_lock_mdio_bus(phydev);
+
+ ret = __air_pbus_reg_write(priv->pbusdev, AN8811HB_MCU_SW_START,
+ AN8811HB_MCU_SW_START_EN);
+ if (ret < 0)
+ goto unlock;
+
+ ret = __air_pbus_reg_write(priv->pbusdev, AN8811HB_MCU_SW_RST,
+ AN8811HB_MCU_SW_RST_RUN);
+ if (ret < 0)
+ goto unlock;
+
+ msleep(50);
+ phydev_dbg(phydev, "MCU deasserted\n");
+
+unlock:
+ phy_unlock_mdio_bus(phydev);
+ return ret;
+}
+
static int an8811hb_load_firmware(struct phy_device *phydev)
{
int ret;
+ ret = an8811hb_mcu_assert(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = an8811hb_mcu_deassert(phydev);
+ if (ret < 0)
+ return ret;
+
ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
EN8811H_FW_CTRL_1_START);
if (ret < 0)
@@ -662,6 +760,16 @@ static int en8811h_restart_mcu(struct phy_device *phydev)
{
int ret;
+ if (phy_id_compare_model(phydev->phy_id, AN8811HB_PHY_ID)) {
+ ret = an8811hb_mcu_assert(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = an8811hb_mcu_deassert(phydev);
+ if (ret < 0)
+ return ret;
+ }
+
ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
EN8811H_FW_CTRL_1_START);
if (ret < 0)
@@ -1166,6 +1274,7 @@ static int en8811h_leds_setup(struct phy_device *phydev)
static int an8811hb_probe(struct phy_device *phydev)
{
+ struct mdio_device *mdiodev;
struct en8811h_priv *priv;
int ret;
@@ -1175,10 +1284,28 @@ static int an8811hb_probe(struct phy_device *phydev)
return -ENOMEM;
phydev->priv = priv;
+ /*
+ * The AN8811HB PHY address is restricted to 8-15 (decimal),
+ * depending on the board hardware strapping.
+ * This means the PBUS address is only in the range 16-21 (decimal),
+ * so we do not need to handle the case
+ * where the PBUS address exceeds 31 (decimal).
+ */
+ mdiodev = mdio_device_create(phydev->mdio.bus,
+ phydev->mdio.addr + EN8811H_PBUS_ADDR_OFFS);
+ if (IS_ERR(mdiodev))
+ return PTR_ERR(mdiodev);
+
+ ret = mdio_device_register(mdiodev);
+ if (ret)
+ goto err_dev_free;
+
+ priv->pbusdev = mdiodev;
+
ret = an8811hb_load_firmware(phydev);
if (ret < 0) {
phydev_err(phydev, "Load firmware failed: %d\n", ret);
- return ret;
+ goto err_dev_create;
}
en8811h_print_fw_version(phydev);
@@ -1191,22 +1318,29 @@ static int an8811hb_probe(struct phy_device *phydev)
ret = en8811h_leds_setup(phydev);
if (ret < 0)
- return ret;
+ goto err_dev_create;
priv->phydev = phydev;
/* Co-Clock Output */
ret = an8811hb_clk_provider_setup(&phydev->mdio.dev, &priv->hw);
if (ret)
- return ret;
+ goto err_dev_create;
/* Configure led gpio pins as output */
ret = air_buckpbus_reg_modify(phydev, AN8811HB_GPIO_OUTPUT,
AN8811HB_GPIO_OUTPUT_345,
AN8811HB_GPIO_OUTPUT_345);
if (ret < 0)
- return ret;
+ goto err_dev_create;
return 0;
+
+err_dev_create:
+ mdio_device_remove(mdiodev);
+
+err_dev_free:
+ mdio_device_free(mdiodev);
+ return ret;
}
static int en8811h_probe(struct phy_device *phydev)
@@ -1561,6 +1695,16 @@ static int en8811h_suspend(struct phy_device *phydev)
return genphy_suspend(phydev);
}
+static void an8811hb_remove(struct phy_device *phydev)
+{
+ struct en8811h_priv *priv = phydev->priv;
+
+ if (priv->pbusdev) {
+ mdio_device_remove(priv->pbusdev);
+ mdio_device_free(priv->pbusdev);
+ }
+}
+
static struct phy_driver en8811h_driver[] = {
{
PHY_ID_MATCH_MODEL(EN8811H_PHY_ID),
@@ -1587,6 +1731,7 @@ static struct phy_driver en8811h_driver[] = {
PHY_ID_MATCH_MODEL(AN8811HB_PHY_ID),
.name = "Airoha AN8811HB",
.probe = an8811hb_probe,
+ .remove = an8811hb_remove,
.get_features = en8811h_get_features,
.config_init = an8811hb_config_init,
.get_rate_matching = en8811h_get_rate_matching,
diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c
index 5198d66dbbc0..b64beade8dd9 100644
--- a/drivers/net/phy/bcm-phy-lib.c
+++ b/drivers/net/phy/bcm-phy-lib.c
@@ -563,6 +563,15 @@ void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow,
}
EXPORT_SYMBOL_GPL(bcm_phy_get_stats);
+void bcm_phy_update_stats_shadow(struct phy_device *phydev, u64 *shadow)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
+ bcm_phy_get_stat(phydev, shadow, i);
+}
+EXPORT_SYMBOL_GPL(bcm_phy_update_stats_shadow);
+
void bcm_phy_r_rc_cal_reset(struct phy_device *phydev)
{
/* Reset R_CAL/RC_CAL Engine */
diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h
index bceddbc860eb..bba94ce96195 100644
--- a/drivers/net/phy/bcm-phy-lib.h
+++ b/drivers/net/phy/bcm-phy-lib.h
@@ -85,6 +85,7 @@ int bcm_phy_get_sset_count(struct phy_device *phydev);
void bcm_phy_get_strings(struct phy_device *phydev, u8 *data);
void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow,
struct ethtool_stats *stats, u64 *data);
+void bcm_phy_update_stats_shadow(struct phy_device *phydev, u64 *shadow);
void bcm_phy_r_rc_cal_reset(struct phy_device *phydev);
int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev);
int bcm_phy_enable_jumbo(struct phy_device *phydev);
diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c
index 00e8fa14aa77..71a163f62c0e 100644
--- a/drivers/net/phy/bcm7xxx.c
+++ b/drivers/net/phy/bcm7xxx.c
@@ -807,6 +807,17 @@ static void bcm7xxx_28nm_get_phy_stats(struct phy_device *phydev,
bcm_phy_get_stats(phydev, priv->stats, stats, data);
}
+static int bcm7xxx_28nm_suspend(struct phy_device *phydev)
+{
+ struct bcm7xxx_phy_priv *priv = phydev->priv;
+
+ mutex_lock(&phydev->lock);
+ bcm_phy_update_stats_shadow(phydev, priv->stats);
+ mutex_unlock(&phydev->lock);
+
+ return genphy_suspend(phydev);
+}
+
static int bcm7xxx_28nm_probe(struct phy_device *phydev)
{
struct bcm7xxx_phy_priv *priv;
@@ -849,6 +860,7 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev)
.flags = PHY_IS_INTERNAL, \
.config_init = bcm7xxx_28nm_config_init, \
.resume = bcm7xxx_28nm_resume, \
+ .suspend = bcm7xxx_28nm_suspend, \
.get_tunable = bcm7xxx_28nm_get_tunable, \
.set_tunable = bcm7xxx_28nm_set_tunable, \
.get_sset_count = bcm_phy_get_sset_count, \
@@ -866,6 +878,7 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev)
.flags = PHY_IS_INTERNAL, \
.config_init = bcm7xxx_28nm_ephy_config_init, \
.resume = bcm7xxx_28nm_ephy_resume, \
+ .suspend = bcm7xxx_28nm_suspend, \
.get_sset_count = bcm_phy_get_sset_count, \
.get_strings = bcm_phy_get_strings, \
.get_stats = bcm7xxx_28nm_get_phy_stats, \
@@ -902,6 +915,7 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev)
.config_aneg = genphy_config_aneg, \
.read_status = genphy_read_status, \
.resume = bcm7xxx_16nm_ephy_resume, \
+ .suspend = bcm7xxx_28nm_suspend, \
}
static struct phy_driver bcm7xxx_driver[] = {
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index bf0c6a04481e..d1a4edb34ad2 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -592,8 +592,13 @@ static int bcm54xx_set_wakeup_irq(struct phy_device *phydev, bool state)
static int bcm54xx_suspend(struct phy_device *phydev)
{
+ struct bcm54xx_phy_priv *priv = phydev->priv;
int ret = 0;
+ mutex_lock(&phydev->lock);
+ bcm_phy_update_stats_shadow(phydev, priv->stats);
+ mutex_unlock(&phydev->lock);
+
bcm54xx_ptp_stop(phydev);
/* Acknowledge any Wake-on-LAN interrupt prior to suspend */
diff --git a/drivers/net/phy/dp83tc811.c b/drivers/net/phy/dp83tc811.c
index e480c2a07450..252fb12b3e68 100644
--- a/drivers/net/phy/dp83tc811.c
+++ b/drivers/net/phy/dp83tc811.c
@@ -393,6 +393,7 @@ static struct phy_driver dp83811_driver[] = {
.config_init = dp83811_config_init,
.config_aneg = dp83811_config_aneg,
.soft_reset = dp83811_phy_reset,
+ .get_features = genphy_c45_pma_read_ext_abilities,
.get_wol = dp83811_get_wol,
.set_wol = dp83811_set_wol,
.config_intr = dp83811_config_intr,
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 2aa1dedd21b8..e211a523c258 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -4548,6 +4548,13 @@ static int lan8814_config_init(struct phy_device *phydev)
struct kszphy_priv *lan8814 = phydev->priv;
int ret;
+ if (phy_package_init_once(phydev))
+ /* Reset the PHY */
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_QSGMII_SOFT_RESET,
+ LAN8814_QSGMII_SOFT_RESET_BIT,
+ LAN8814_QSGMII_SOFT_RESET_BIT);
+
/* Based on the interface type select how the advertise ability is
* encoded, to set as SGMII or as USGMII.
*/
@@ -4655,13 +4662,7 @@ static int lan8814_probe(struct phy_device *phydev)
priv->is_ptp_available = err == LAN8814_REV_LAN8814 ||
err == LAN8814_REV_LAN8818;
- if (phy_package_init_once(phydev)) {
- /* Reset the PHY */
- lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
- LAN8814_QSGMII_SOFT_RESET,
- LAN8814_QSGMII_SOFT_RESET_BIT,
- LAN8814_QSGMII_SOFT_RESET_BIT);
-
+ if (phy_package_probe_once(phydev)) {
err = lan8814_release_coma_mode(phydev);
if (err)
return err;
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index d48aa7231b37..126951741428 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -940,6 +940,14 @@ EXPORT_SYMBOL_GPL(genphy_c45_read_eee_abilities);
*/
int genphy_c45_an_config_eee_aneg(struct phy_device *phydev)
{
+ /* Writing MMD AN advertisements while autoneg is disabled has no
+ * effect on link-partner negotiation, but on some PHYs (e.g. the
+ * Broadcom BCM54213PE) the write itself disturbs the receive
+ * datapath. Skip it.
+ */
+ if (phydev->autoneg == AUTONEG_DISABLE)
+ return 0;
+
if (!phydev->eee_cfg.eee_enabled) {
__ETHTOOL_DECLARE_LINK_MODE_MASK(adv) = {};
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index c2cdf1ae3542..3370eb822017 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -2877,7 +2877,8 @@ EXPORT_SYMBOL(phy_advertise_supported);
*/
void phy_advertise_eee_all(struct phy_device *phydev)
{
- linkmode_copy(phydev->advertising_eee, phydev->supported_eee);
+ linkmode_andnot(phydev->advertising_eee, phydev->supported_eee,
+ phydev->eee_disabled_modes);
}
EXPORT_SYMBOL_GPL(phy_advertise_eee_all);
@@ -2903,7 +2904,8 @@ EXPORT_SYMBOL_GPL(phy_advertise_eee_all);
*/
void phy_support_eee(struct phy_device *phydev)
{
- linkmode_copy(phydev->advertising_eee, phydev->supported_eee);
+ linkmode_andnot(phydev->advertising_eee, phydev->supported_eee,
+ phydev->eee_disabled_modes);
phydev->eee_cfg.tx_lpi_enabled = true;
phydev->eee_cfg.eee_enabled = true;
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index bd970f753beb..b94b9c433a21 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -822,6 +822,7 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
return -EINVAL;
}
+ sfp->i2c_block_size = sfp->i2c_max_block_size;
return 0;
}
diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index 87aa4f4e9724..69dbdbde9d71 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -210,7 +210,7 @@ static int of_load_pse_pis(struct pse_controller_dev *pcdev)
ret = of_load_pse_pi_pairsets(node, &pi, ret);
if (ret)
goto out;
- } else if (ret != ENOENT) {
+ } else if (ret != -ENOENT) {
dev_err(pcdev->dev,
"error: wrong number of pairsets. Should be 1 or 2, got %d (%pOF)\n",
ret, node);
diff --git a/drivers/net/tap.c b/drivers/net/tap.c
index b8240737dc51..fae115915c8e 100644
--- a/drivers/net/tap.c
+++ b/drivers/net/tap.c
@@ -919,11 +919,11 @@ static long tap_ioctl(struct file *file, unsigned int cmd,
struct tap_queue *q = file->private_data;
struct tap_dev *tap;
void __user *argp = (void __user *)arg;
+ struct sockaddr_storage ss = {};
struct ifreq __user *ifr = argp;
unsigned int __user *up = argp;
unsigned short u;
int __user *sp = argp;
- struct sockaddr_storage ss;
int s;
int ret;
@@ -1052,6 +1052,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
int err, depth;
if (unlikely(xdp->data_end - xdp->data < ETH_HLEN)) {
+ put_page(virt_to_head_page(xdp->data));
err = -EINVAL;
goto err;
}
@@ -1061,6 +1062,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
skb = build_skb(xdp->data_hard_start, buflen);
if (!skb) {
+ put_page(virt_to_head_page(xdp->data));
err = -ENOMEM;
goto err;
}
diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c
index 0c87f9972457..f51388d50307 100644
--- a/drivers/net/team/team_core.c
+++ b/drivers/net/team/team_core.c
@@ -534,21 +534,23 @@ static void team_adjust_ops(struct team *team)
if (!team->tx_en_port_count || !team_is_mode_set(team) ||
!team->mode->ops->transmit)
- team->ops.transmit = team_dummy_transmit;
+ WRITE_ONCE(team->ops.transmit, team_dummy_transmit);
else
- team->ops.transmit = team->mode->ops->transmit;
+ WRITE_ONCE(team->ops.transmit, team->mode->ops->transmit);
if (!team->rx_en_port_count || !team_is_mode_set(team) ||
!team->mode->ops->receive)
- team->ops.receive = team_dummy_receive;
+ WRITE_ONCE(team->ops.receive, team_dummy_receive);
else
- team->ops.receive = team->mode->ops->receive;
+ WRITE_ONCE(team->ops.receive, team->mode->ops->receive);
}
/*
- * We can benefit from the fact that it's ensured no port is present
- * at the time of mode change. Therefore no packets are in fly so there's no
- * need to set mode operations in any special way.
+ * team_change_mode() ensures no ports are present during mode change,
+ * but lockless readers can still reach team_xmit(). Avoid touching
+ * transmit/receive -- they are already set to dummies by
+ * team_adjust_ops() since no ports are enabled. synchronize_net()
+ * drains in-flight readers before destroying old mode state.
*/
static int __team_change_mode(struct team *team,
const struct team_mode *new_mode)
@@ -557,9 +559,21 @@ static int __team_change_mode(struct team *team,
if (team_is_mode_set(team)) {
void (*exit_op)(struct team *team) = team->ops.exit;
- /* Clear ops area so no callback is called any longer */
- memset(&team->ops, 0, sizeof(struct team_mode_ops));
- team_adjust_ops(team);
+ /* Clear cold-path ops used only under RTNL. transmit and
+ * receive are already dummies (no ports) so leave them
+ * alone -- overwriting them is the source of the race.
+ */
+ team->ops.init = NULL;
+ team->ops.exit = NULL;
+ team->ops.port_enter = NULL;
+ team->ops.port_leave = NULL;
+ team->ops.port_change_dev_addr = NULL;
+ team->ops.port_tx_disabled = NULL;
+
+ /* Wait for in-flight readers before tearing down mode
+ * state they may reference.
+ */
+ synchronize_net();
if (exit_op)
exit_op(team);
@@ -582,7 +596,12 @@ static int __team_change_mode(struct team *team,
}
team->mode = new_mode;
- memcpy(&team->ops, new_mode->ops, sizeof(struct team_mode_ops));
+ team->ops.init = new_mode->ops->init;
+ team->ops.exit = new_mode->ops->exit;
+ team->ops.port_enter = new_mode->ops->port_enter;
+ team->ops.port_leave = new_mode->ops->port_leave;
+ team->ops.port_change_dev_addr = new_mode->ops->port_change_dev_addr;
+ team->ops.port_tx_disabled = new_mode->ops->port_tx_disabled;
team_adjust_ops(team);
return 0;
@@ -743,7 +762,7 @@ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
/* allow exact match delivery for disabled ports */
res = RX_HANDLER_EXACT;
} else {
- res = team->ops.receive(team, port, skb);
+ res = READ_ONCE(team->ops.receive)(team, port, skb);
}
if (res == RX_HANDLER_ANOTHER) {
struct team_pcpu_stats *pcpu_stats;
@@ -1845,7 +1864,7 @@ static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev)
tx_success = team_queue_override_transmit(team, skb);
if (!tx_success)
- tx_success = team->ops.transmit(team, skb);
+ tx_success = READ_ONCE(team->ops.transmit)(team, skb);
if (tx_success) {
struct team_pcpu_stats *pcpu_stats;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index b183189f1853..9e7744eb57a3 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -2394,8 +2394,10 @@ static int tun_xdp_one(struct tun_struct *tun,
bool skb_xdp = false;
struct page *page;
- if (unlikely(datasize < ETH_HLEN))
+ if (unlikely(datasize < ETH_HLEN)) {
+ put_page(virt_to_head_page(xdp->data));
return -EINVAL;
+ }
xdp_prog = rcu_dereference(tun->xdp_prog);
if (xdp_prog) {
@@ -2437,6 +2439,7 @@ static int tun_xdp_one(struct tun_struct *tun,
build:
skb = build_skb(xdp->data_hard_start, buflen);
if (!skb) {
+ put_page(virt_to_head_page(xdp->data));
ret = -ENOMEM;
goto out;
}
diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
index df0bcfedddbc..293ef80c4e30 100644
--- a/drivers/net/usb/asix_devices.c
+++ b/drivers/net/usb/asix_devices.c
@@ -756,6 +756,7 @@ static void ax88772_mac_link_down(struct phylink_config *config,
struct usbnet *dev = netdev_priv(to_net_dev(config->dev));
asix_write_medium_mode(dev, 0, 0);
+ usbnet_link_change(dev, false, false);
}
static void ax88772_mac_link_up(struct phylink_config *config,
@@ -786,6 +787,7 @@ static void ax88772_mac_link_up(struct phylink_config *config,
m |= AX_MEDIUM_RFC;
asix_write_medium_mode(dev, m, 0);
+ usbnet_link_change(dev, true, false);
}
static const struct phylink_mac_ops ax88772_phylink_mac_ops = {
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index bb9929727eb9..0223a172851e 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -2012,6 +2012,14 @@ static const struct usb_device_id cdc_devs[] = {
.driver_info = (unsigned long)&apple_private_interface_info,
},
+ /* Mac */
+ { USB_DEVICE_INTERFACE_NUMBER(0x05ac, 0x1905, 0),
+ .driver_info = (unsigned long)&apple_private_interface_info,
+ },
+ { USB_DEVICE_INTERFACE_NUMBER(0x05ac, 0x1905, 2),
+ .driver_info = (unsigned long)&apple_private_interface_info,
+ },
+
/* Ericsson MBM devices like F5521gw */
{ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
| USB_DEVICE_ID_MATCH_VENDOR,
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 7337bf1b7d6a..1ace1d2398c9 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -10138,6 +10138,7 @@ static const struct usb_device_id rtl8152_table[] = {
{ USB_DEVICE(VENDOR_ID_DELL, 0xb097) },
{ USB_DEVICE(VENDOR_ID_ASUS, 0x1976) },
{ USB_DEVICE(VENDOR_ID_TRENDNET, 0xe02b) },
+ { USB_DEVICE(VENDOR_ID_TRENDNET, 0xe02c) },
{}
};
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index e35df717e65e..0cfb19b760dd 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -972,7 +972,8 @@ static int veth_poll(struct napi_struct *napi, int budget)
/* NAPI functions as RCU section */
peer_dev = rcu_dereference_check(priv->peer, rcu_read_lock_bh_held());
- peer_txq = peer_dev ? netdev_get_tx_queue(peer_dev, queue_idx) : NULL;
+ peer_txq = (peer_dev && queue_idx < peer_dev->real_num_tx_queues) ?
+ netdev_get_tx_queue(peer_dev, queue_idx) : NULL;
xdp_set_return_frame_no_direct();
done = veth_xdp_rcv(rq, budget, &bq, &stats);
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index e88798497503..b5b1253ac08b 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -2531,7 +2531,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
goto out_unlock;
}
- tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
+ tos = ip_tunnel_ecn_encap(tos, ip_hdr(skb), skb);
ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
err = vxlan_build_skb(skb, ndst, sizeof(struct iphdr),
vni, md, flags, udp_sum);
@@ -2605,7 +2605,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
goto out_unlock;
}
- tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
+ tos = ip_tunnel_ecn_encap(tos, ip_hdr(skb), skb);
ttl = ttl ? : ip6_dst_hoplimit(ndst);
skb_scrub_packet(skb, xnet);
err = vxlan_build_skb(skb, ndst, sizeof(struct ipv6hdr),
diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
index 2042369379ff..3e76f4e21094 100644
--- a/drivers/net/vxlan/vxlan_vnifilter.c
+++ b/drivers/net/vxlan/vxlan_vnifilter.c
@@ -661,7 +661,7 @@ static int vxlan_vni_update(struct vxlan_dev *vxlan,
if (ret)
return ret;
- if (changed)
+ if (*changed)
vxlan_vnifilter_notify(vxlan, vninode, RTM_NEWTUNNEL);
return 0;
@@ -759,8 +759,7 @@ static int vxlan_vni_add(struct vxlan_dev *vxlan,
err = vxlan_vni_update_group(vxlan, vninode, group, true, &changed,
extack);
- if (changed)
- vxlan_vnifilter_notify(vxlan, vninode, RTM_NEWTUNNEL);
+ vxlan_vnifilter_notify(vxlan, vninode, RTM_NEWTUNNEL);
return err;
}
diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c
index 3bd57527b1be..809f21fb93f5 100644
--- a/drivers/net/wan/fsl_ucc_hdlc.c
+++ b/drivers/net/wan/fsl_ucc_hdlc.c
@@ -740,6 +740,8 @@ static int uhdlc_open(struct net_device *dev)
static void uhdlc_memclean(struct ucc_hdlc_private *priv)
{
+ int i;
+
qe_muram_free(ioread16be(&priv->ucc_pram->riptr));
qe_muram_free(ioread16be(&priv->ucc_pram->tiptr));
@@ -770,14 +772,14 @@ static void uhdlc_memclean(struct ucc_hdlc_private *priv)
kfree(priv->rx_skbuff);
priv->rx_skbuff = NULL;
+ for (i = 0; i < TX_BD_RING_LEN; i++) {
+ dev_kfree_skb(priv->tx_skbuff[i]);
+ priv->tx_skbuff[i] = NULL;
+ }
+
kfree(priv->tx_skbuff);
priv->tx_skbuff = NULL;
- if (priv->uf_regs) {
- iounmap(priv->uf_regs);
- priv->uf_regs = NULL;
- }
-
if (priv->uccf) {
ucc_fast_free(priv->uccf);
priv->uccf = NULL;
@@ -1255,12 +1257,12 @@ static void ucc_hdlc_remove(struct platform_device *pdev)
uhdlc_memclean(priv);
- if (priv->utdm->si_regs) {
+ if (priv->utdm && priv->utdm->si_regs) {
iounmap(priv->utdm->si_regs);
priv->utdm->si_regs = NULL;
}
- if (priv->utdm->siram) {
+ if (priv->utdm && priv->utdm->siram) {
iounmap(priv->utdm->siram);
priv->utdm->siram = NULL;
}
diff --git a/drivers/net/wireguard/send.c b/drivers/net/wireguard/send.c
index 26e09c30d596..67d01478eb76 100644
--- a/drivers/net/wireguard/send.c
+++ b/drivers/net/wireguard/send.c
@@ -177,16 +177,6 @@ static bool encrypt_packet(struct sk_buff *skb, struct noise_keypair *keypair)
trailer_len = padding_len + noise_encrypted_len(0);
plaintext_len = skb->len + padding_len;
- /* Expand data section to have room for padding and auth tag. */
- num_frags = skb_cow_data(skb, trailer_len, &trailer);
- if (unlikely(num_frags < 0 || num_frags > ARRAY_SIZE(sg)))
- return false;
-
- /* Set the padding to zeros, and make sure it and the auth tag are part
- * of the skb.
- */
- memset(skb_tail_pointer(trailer), 0, padding_len);
-
/* Expand head section to have room for our header and the network
* stack's headers.
*/
@@ -198,6 +188,16 @@ static bool encrypt_packet(struct sk_buff *skb, struct noise_keypair *keypair)
skb_checksum_help(skb)))
return false;
+ /* Expand data section to have room for padding and auth tag. */
+ num_frags = skb_cow_data(skb, trailer_len, &trailer);
+ if (unlikely(num_frags < 0 || num_frags > ARRAY_SIZE(sg)))
+ return false;
+
+ /* Set the padding to zeros, and make sure it and the auth tag are part
+ * of the skb.
+ */
+ memset(skb_tail_pointer(trailer), 0, padding_len);
+
/* Only after checksumming can we safely add on the padding at the end
* and the header.
*/
diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
index 876aed765833..efb9f022d8c6 100644
--- a/drivers/net/wireless/ath/ath10k/Kconfig
+++ b/drivers/net/wireless/ath/ath10k/Kconfig
@@ -46,6 +46,7 @@ config ATH10K_SNOC
depends on ARCH_QCOM || COMPILE_TEST
depends on QCOM_SMEM
depends on QCOM_RPROC_COMMON || QCOM_RPROC_COMMON=n
+ select POWER_SEQUENCING
select QCOM_SCM
select QCOM_QMI_HELPERS
help
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index 0bdb38edd915..e57588c19c80 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -3,7 +3,6 @@
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
@@ -1947,15 +1946,15 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id)
ret = -ESHUTDOWN;
ath10k_dbg(ar, ATH10K_DBG_WMI,
"drop wmi command %d, hardware is wedged\n", cmd_id);
- }
- /* try to send pending beacons first. they take priority */
- ath10k_wmi_tx_beacons_nowait(ar);
+ } else {
+ /* try to send pending beacons first. they take priority */
+ ath10k_wmi_tx_beacons_nowait(ar);
- ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id);
-
- if (ret && test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
- ret = -ESHUTDOWN;
+ ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id);
+ if (ret && test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ ret = -ESHUTDOWN;
+ }
(ret != -EAGAIN);
}), 3 * HZ);
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index fe79109adc70..2a413e3a07a7 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -1761,6 +1761,7 @@ static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar,
int buf_first_hdr_len, buf_first_len;
struct hal_rx_desc *ldesc;
int space_extra, rem_len, buf_len;
+ bool is_continuation;
u32 hal_rx_desc_sz = ar->ab->hw_params.hal_desc_sz;
/* As the msdu is spread across multiple rx buffers,
@@ -1810,7 +1811,8 @@ static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar,
rem_len = msdu_len - buf_first_len;
while ((skb = __skb_dequeue(msdu_list)) != NULL && rem_len > 0) {
rxcb = ATH11K_SKB_RXCB(skb);
- if (rxcb->is_continuation)
+ is_continuation = rxcb->is_continuation;
+ if (is_continuation)
buf_len = DP_RX_BUFFER_SIZE - hal_rx_desc_sz;
else
buf_len = rem_len;
@@ -1828,7 +1830,7 @@ static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar,
dev_kfree_skb_any(skb);
rem_len -= buf_len;
- if (!rxcb->is_continuation)
+ if (!is_continuation)
break;
}
@@ -2214,8 +2216,7 @@ ath11k_dp_rx_h_find_peer(struct ath11k_base *ab, struct sk_buff *msdu)
lockdep_assert_held(&ab->base_lock);
- if (rxcb->peer_id)
- peer = ath11k_peer_find_by_id(ab, rxcb->peer_id);
+ peer = ath11k_peer_find_by_id(ab, rxcb->peer_id);
if (peer)
return peer;
diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c
index e821e5a62c1c..98bd9e3f0aae 100644
--- a/drivers/net/wireless/ath/ath11k/hal.c
+++ b/drivers/net/wireless/ath/ath11k/hal.c
@@ -1387,14 +1387,22 @@ EXPORT_SYMBOL(ath11k_hal_srng_deinit);
void ath11k_hal_srng_clear(struct ath11k_base *ab)
{
- /* No need to memset rdp and wrp memory since each individual
- * segment would get cleared in ath11k_hal_srng_src_hw_init()
- * and ath11k_hal_srng_dst_hw_init().
+ /*
+ * Preserve the shared pointer buffers, but clear the previous
+ * firmware instance's hp/tp state before handing them back to FW.
+ * LMAC rings reuse this shared memory without going through the
+ * normal SRNG hw-init path that zeros non-LMAC ring pointers.
*/
memset(ab->hal.srng_list, 0,
sizeof(ab->hal.srng_list));
memset(ab->hal.shadow_reg_addr, 0,
sizeof(ab->hal.shadow_reg_addr));
+ if (ab->hal.rdp.vaddr)
+ memset(ab->hal.rdp.vaddr, 0,
+ sizeof(*ab->hal.rdp.vaddr) * HAL_SRNG_RING_ID_MAX);
+ if (ab->hal.wrp.vaddr)
+ memset(ab->hal.wrp.vaddr, 0,
+ sizeof(*ab->hal.wrp.vaddr) * HAL_SRNG_NUM_LMAC_RINGS);
ab->hal.avail_blk_resource = 0;
ab->hal.current_blk_index = 0;
ab->hal.num_shadow_reg_configured = 0;
diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c
index 753bd93f0212..51e0840bc0d1 100644
--- a/drivers/net/wireless/ath/ath11k/hal_rx.c
+++ b/drivers/net/wireless/ath/ath11k/hal_rx.c
@@ -1467,11 +1467,8 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab,
case HAL_RX_MPDU_START: {
struct hal_rx_mpdu_info *mpdu_info =
(struct hal_rx_mpdu_info *)tlv_data;
- u16 peer_id;
- peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info);
- if (peer_id)
- ppdu_info->peer_id = peer_id;
+ ppdu_info->peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info);
break;
}
case HAL_RXPCU_PPDU_END_INFO: {
diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c
index a9751ea2a0b7..c72eed358f6d 100644
--- a/drivers/net/wireless/ath/ath11k/testmode.c
+++ b/drivers/net/wireless/ath/ath11k/testmode.c
@@ -457,6 +457,7 @@ static int ath11k_tm_cmd_wmi_ftm(struct ath11k *ar, struct nlattr *tb[])
ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id);
if (ret) {
ath11k_warn(ar->ab, "failed to send wmi ftm command: %d\n", ret);
+ dev_kfree_skb(skb);
goto out;
}
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 40747fba3b0c..dca6e011cc40 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -9299,7 +9299,7 @@ int ath11k_wmi_hw_data_filter_cmd(struct ath11k *ar, u32 vdev_id,
{
struct wmi_hw_data_filter_cmd *cmd;
struct sk_buff *skb;
- int len;
+ int ret, len;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -9324,7 +9324,13 @@ int ath11k_wmi_hw_data_filter_cmd(struct ath11k *ar, u32 vdev_id,
"hw data filter enable %d filter_bitmap 0x%x\n",
enable, filter_bitmap);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_HW_DATA_FILTER_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)
@@ -9332,6 +9338,7 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)
struct wmi_wow_host_wakeup_ind *cmd;
struct sk_buff *skb;
size_t len;
+ int ret;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -9345,14 +9352,20 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow host wakeup ind\n");
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_wow_enable(struct ath11k *ar)
{
struct wmi_wow_enable_cmd *cmd;
struct sk_buff *skb;
- int len;
+ int ret, len;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -9367,7 +9380,13 @@ int ath11k_wmi_wow_enable(struct ath11k *ar)
cmd->pause_iface_config = WOW_IFACE_PAUSE_ENABLED;
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow enable\n");
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
@@ -9376,7 +9395,7 @@ int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
struct sk_buff *skb;
struct wmi_scan_prob_req_oui_cmd *cmd;
u32 prob_req_oui;
- int len;
+ int ret, len;
prob_req_oui = (((u32)mac_addr[0]) << 16) |
(((u32)mac_addr[1]) << 8) | mac_addr[2];
@@ -9395,7 +9414,13 @@ int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "scan prob req oui %d\n",
prob_req_oui);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SCAN_PROB_REQ_OUI_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SCAN_PROB_REQ_OUI_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_SCAN_PROB_REQ_OUI_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id,
@@ -9405,6 +9430,7 @@ int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id,
struct wmi_wow_add_del_event_cmd *cmd;
struct sk_buff *skb;
size_t len;
+ int ret;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -9422,7 +9448,13 @@ int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id,
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow add wakeup event %s enable %d vdev_id %d\n",
wow_wakeup_event(event), enable, vdev_id);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id,
@@ -9435,6 +9467,7 @@ int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id,
struct sk_buff *skb;
u8 *ptr;
size_t len;
+ int ret;
len = sizeof(*cmd) +
sizeof(*tlv) + /* array struct */
@@ -9527,7 +9560,13 @@ int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id,
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow add pattern vdev_id %d pattern_id %d pattern_offset %d\n",
vdev_id, pattern_id, pattern_offset);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_WOW_ADD_WAKE_PATTERN_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id)
@@ -9535,6 +9574,7 @@ int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id)
struct wmi_wow_del_pattern_cmd *cmd;
struct sk_buff *skb;
size_t len;
+ int ret;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -9553,7 +9593,13 @@ int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id)
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow del pattern vdev_id %d pattern_id %d\n",
vdev_id, pattern_id);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_WOW_DEL_WAKE_PATTERN_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
static struct sk_buff *
@@ -9697,6 +9743,7 @@ int ath11k_wmi_wow_config_pno(struct ath11k *ar, u32 vdev_id,
struct wmi_pno_scan_req *pno_scan)
{
struct sk_buff *skb;
+ int ret;
if (pno_scan->enable)
skb = ath11k_wmi_op_gen_config_pno_start(ar, vdev_id, pno_scan);
@@ -9706,7 +9753,13 @@ int ath11k_wmi_wow_config_pno(struct ath11k *ar, u32 vdev_id,
if (IS_ERR_OR_NULL(skb))
return -ENOMEM;
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
static void ath11k_wmi_fill_ns_offload(struct ath11k *ar,
@@ -9824,6 +9877,7 @@ int ath11k_wmi_arp_ns_offload(struct ath11k *ar,
u8 *buf_ptr;
size_t len;
u8 ns_cnt, ns_ext_tuples = 0;
+ int ret;
offload = &arvif->arp_ns_offload;
ns_cnt = offload->ipv6_count;
@@ -9862,7 +9916,13 @@ int ath11k_wmi_arp_ns_offload(struct ath11k *ar,
if (ns_ext_tuples)
ath11k_wmi_fill_ns_offload(ar, offload, &buf_ptr, enable, 1);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_SET_ARP_NS_OFFLOAD_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_gtk_rekey_offload(struct ath11k *ar,
@@ -9870,7 +9930,7 @@ int ath11k_wmi_gtk_rekey_offload(struct ath11k *ar,
{
struct wmi_gtk_rekey_offload_cmd *cmd;
struct ath11k_rekey_data *rekey_data = &arvif->rekey_data;
- int len;
+ int ret, len;
struct sk_buff *skb;
__le64 replay_ctr;
@@ -9904,14 +9964,20 @@ int ath11k_wmi_gtk_rekey_offload(struct ath11k *ar,
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "offload gtk rekey vdev: %d %d\n",
arvif->vdev_id, enable);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID offload\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar,
struct ath11k_vif *arvif)
{
struct wmi_gtk_rekey_offload_cmd *cmd;
- int len;
+ int ret, len;
struct sk_buff *skb;
len = sizeof(*cmd);
@@ -9928,7 +9994,13 @@ int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar,
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "get gtk rekey vdev_id: %d\n",
arvif->vdev_id);
- return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+ ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID getinfo\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_val)
@@ -9938,6 +10010,7 @@ int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_va
struct sk_buff *skb;
u8 *buf_ptr;
u32 len, sar_len_aligned, rsvd_len_aligned;
+ int ret;
sar_len_aligned = roundup(BIOS_SAR_TABLE_LEN, sizeof(u32));
rsvd_len_aligned = roundup(BIOS_SAR_RSVD1_LEN, sizeof(u32));
@@ -9968,7 +10041,13 @@ int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_va
tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
FIELD_PREP(WMI_TLV_LEN, rsvd_len_aligned);
- return ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID);
+ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar)
@@ -9979,6 +10058,7 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar)
struct sk_buff *skb;
u8 *buf_ptr;
u32 len, rsvd_len_aligned;
+ int ret;
rsvd_len_aligned = roundup(BIOS_SAR_RSVD2_LEN, sizeof(u32));
len = sizeof(*cmd) + TLV_HDR_SIZE + rsvd_len_aligned;
@@ -9998,7 +10078,13 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar)
tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
FIELD_PREP(WMI_TLV_LEN, rsvd_len_aligned);
- return ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID);
+ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath11k_wmi_sta_keepalive(struct ath11k *ar,
@@ -10009,6 +10095,7 @@ int ath11k_wmi_sta_keepalive(struct ath11k *ar,
struct wmi_sta_keepalive_arp_resp *arp;
struct sk_buff *skb;
size_t len;
+ int ret;
len = sizeof(*cmd) + sizeof(*arp);
skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
@@ -10040,7 +10127,13 @@ int ath11k_wmi_sta_keepalive(struct ath11k *ar,
"sta keepalive vdev %d enabled %d method %d interval %d\n",
arg->vdev_id, arg->enabled, arg->method, arg->interval);
- return ath11k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID);
+ ret = ath11k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to send WMI_STA_KEEPALIVE_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
bool ath11k_wmi_supports_6ghz_cc_ext(struct ath11k *ar)
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index 2519e2400d58..980a12fb2c6e 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -1838,10 +1838,22 @@ static struct ath12k_hw_group *ath12k_core_hw_group_alloc(struct ath12k_base *ab
return ag;
}
+static void ath12k_core_free_wsi_info(struct ath12k_hw_group *ag)
+{
+ int i;
+
+ for (i = 0; i < ag->num_devices; i++) {
+ of_node_put(ag->wsi_node[i]);
+ ag->wsi_node[i] = NULL;
+ }
+ ag->num_devices = 0;
+}
+
static void ath12k_core_hw_group_free(struct ath12k_hw_group *ag)
{
mutex_lock(&ath12k_hw_group_mutex);
+ ath12k_core_free_wsi_info(ag);
list_del(&ag->list);
kfree(ag);
@@ -1867,52 +1879,59 @@ static struct ath12k_hw_group *ath12k_core_hw_group_find_by_dt(struct ath12k_bas
static int ath12k_core_get_wsi_info(struct ath12k_hw_group *ag,
struct ath12k_base *ab)
{
- struct device_node *wsi_dev = ab->dev->of_node, *next_wsi_dev;
- struct device_node *tx_endpoint, *next_rx_endpoint;
- int device_count = 0;
-
- next_wsi_dev = wsi_dev;
+ struct device_node *next_wsi_dev;
+ int device_count = 0, ret = 0;
+ struct device_node *wsi_dev;
- if (!next_wsi_dev)
+ wsi_dev = of_node_get(ab->dev->of_node);
+ if (!wsi_dev)
return -ENODEV;
do {
- ag->wsi_node[device_count] = next_wsi_dev;
+ if (device_count >= ATH12K_MAX_DEVICES) {
+ ath12k_warn(ab, "device count in DT %d is more than limit %d\n",
+ device_count, ATH12K_MAX_DEVICES);
+ ret = -EINVAL;
+ break;
+ }
+
+ ag->wsi_node[device_count++] = of_node_get(wsi_dev);
- tx_endpoint = of_graph_get_endpoint_by_regs(next_wsi_dev, 0, -1);
+ struct device_node *tx_endpoint __free(device_node) =
+ of_graph_get_endpoint_by_regs(wsi_dev, 0, -1);
if (!tx_endpoint) {
- of_node_put(next_wsi_dev);
- return -ENODEV;
+ ret = -ENODEV;
+ break;
}
- next_rx_endpoint = of_graph_get_remote_endpoint(tx_endpoint);
+ struct device_node *next_rx_endpoint __free(device_node) =
+ of_graph_get_remote_endpoint(tx_endpoint);
if (!next_rx_endpoint) {
- of_node_put(next_wsi_dev);
- of_node_put(tx_endpoint);
- return -ENODEV;
+ ret = -ENODEV;
+ break;
}
- of_node_put(tx_endpoint);
- of_node_put(next_wsi_dev);
-
next_wsi_dev = of_graph_get_port_parent(next_rx_endpoint);
if (!next_wsi_dev) {
- of_node_put(next_rx_endpoint);
- return -ENODEV;
+ ret = -ENODEV;
+ break;
}
- of_node_put(next_rx_endpoint);
+ of_node_put(wsi_dev);
+ wsi_dev = next_wsi_dev;
+ } while (ab->dev->of_node != wsi_dev);
- device_count++;
- if (device_count > ATH12K_MAX_DEVICES) {
- ath12k_warn(ab, "device count in DT %d is more than limit %d\n",
- device_count, ATH12K_MAX_DEVICES);
- of_node_put(next_wsi_dev);
- return -EINVAL;
+ if (ret) {
+ while (--device_count >= 0) {
+ of_node_put(ag->wsi_node[device_count]);
+ ag->wsi_node[device_count] = NULL;
}
- } while (wsi_dev != next_wsi_dev);
- of_node_put(next_wsi_dev);
+ of_node_put(wsi_dev);
+ return ret;
+ }
+
+ of_node_put(wsi_dev);
ag->num_devices = device_count;
return 0;
@@ -1983,9 +2002,9 @@ static struct ath12k_hw_group *ath12k_core_hw_group_assign(struct ath12k_base *a
ath12k_core_get_wsi_index(ag, ab)) {
ath12k_dbg(ab, ATH12K_DBG_BOOT,
"unable to get wsi info from dt, grouping single device");
+ ath12k_core_free_wsi_info(ag);
ag->id = ATH12K_INVALID_GROUP_ID;
ag->num_devices = 1;
- memset(ag->wsi_node, 0, sizeof(ag->wsi_node));
wsi->index = 0;
}
diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c
index 250459facff3..b108ccd0f637 100644
--- a/drivers/net/wireless/ath/ath12k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/dp_rx.c
@@ -565,6 +565,9 @@ static int ath12k_dp_prepare_reo_update_elem(struct ath12k_dp *dp,
lockdep_assert_held(&dp->dp_lock);
+ if (!peer->primary_link)
+ return 0;
+
elem = kzalloc_obj(*elem, GFP_ATOMIC);
if (!elem)
return -ENOMEM;
@@ -1337,7 +1340,7 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
bool is_mcbc = rxcb->is_mcbc;
bool is_eapol = rxcb->is_eapol;
- peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rx_info->peer_id);
+ peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id);
pubsta = peer ? peer->sta : NULL;
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index fbdfe6424fd7..2cff9485c95a 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -788,7 +788,7 @@ struct ath12k_link_vif *ath12k_mac_get_arvif(struct ath12k *ar, u32 vdev_id)
/* To use the arvif returned, caller must have held rcu read lock.
*/
- WARN_ON(!rcu_read_lock_any_held());
+ lockdep_assert_in_rcu_read_lock();
arvif_iter.vdev_id = vdev_id;
arvif_iter.ar = ar;
@@ -3446,7 +3446,9 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
arg->peer_eht_mcs_count++;
fallthrough;
default:
- if (!(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
+ if ((vif->type == NL80211_IFTYPE_AP ||
+ vif->type == NL80211_IFTYPE_MESH_POINT) &&
+ !(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
bw_20 = &eht_cap->eht_mcs_nss_supp.only_20mhz;
@@ -3475,7 +3477,9 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
arg->punct_bitmap = ~arvif->punct_bitmap;
arg->eht_disable_mcs15 = link_conf->eht_disable_mcs15;
- if (!(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
+ if ((vif->type == NL80211_IFTYPE_AP ||
+ vif->type == NL80211_IFTYPE_MESH_POINT) &&
+ !(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
if (bw_20->rx_tx_mcs13_max_nss)
max_nss = max(max_nss, u8_get_bits(bw_20->rx_tx_mcs13_max_nss,
diff --git a/drivers/net/wireless/ath/ath12k/p2p.c b/drivers/net/wireless/ath/ath12k/p2p.c
index 59589748f1a8..19ebcd1d8eb2 100644
--- a/drivers/net/wireless/ath/ath12k/p2p.c
+++ b/drivers/net/wireless/ath/ath12k/p2p.c
@@ -123,7 +123,7 @@ static void ath12k_p2p_noa_update_vdev_iter(void *data, u8 *mac,
struct ath12k_p2p_noa_arg *arg = data;
struct ath12k_link_vif *arvif;
- WARN_ON(!rcu_read_lock_any_held());
+ lockdep_assert_in_rcu_read_lock();
arvif = &ahvif->deflink;
if (!arvif->is_created || arvif->ar != arg->ar || arvif->vdev_id != arg->vdev_id)
return;
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index 65a05a9520ff..b5e904a55aea 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -9778,7 +9778,7 @@ static void
ath12k_wmi_rssi_dbm_conversion_params_info_event(struct ath12k_base *ab,
struct sk_buff *skb)
{
- struct ath12k_wmi_rssi_dbm_conv_info_arg rssi_info;
+ struct ath12k_wmi_rssi_dbm_conv_info_arg rssi_info = {};
struct ath12k *ar;
s32 noise_floor;
u32 pdev_id;
@@ -10251,7 +10251,7 @@ int ath12k_wmi_hw_data_filter_cmd(struct ath12k *ar, struct wmi_hw_data_filter_a
{
struct wmi_hw_data_filter_cmd *cmd;
struct sk_buff *skb;
- int len;
+ int ret, len;
len = sizeof(*cmd);
skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -10275,7 +10275,13 @@ int ath12k_wmi_hw_data_filter_cmd(struct ath12k *ar, struct wmi_hw_data_filter_a
"wmi hw data filter enable %d filter_bitmap 0x%x\n",
arg->enable, arg->hw_filter_bitmap);
- return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID);
+ ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to send WMI_HW_DATA_FILTER_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar)
@@ -10283,6 +10289,7 @@ int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar)
struct wmi_wow_host_wakeup_cmd *cmd;
struct sk_buff *skb;
size_t len;
+ int ret;
len = sizeof(*cmd);
skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -10295,14 +10302,20 @@ int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar)
ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow host wakeup ind\n");
- return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID);
+ ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath12k_wmi_wow_enable(struct ath12k *ar)
{
struct wmi_wow_enable_cmd *cmd;
struct sk_buff *skb;
- int len;
+ int ret, len;
len = sizeof(*cmd);
skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -10317,7 +10330,13 @@ int ath12k_wmi_wow_enable(struct ath12k *ar)
cmd->pause_iface_config = cpu_to_le32(WOW_IFACE_PAUSE_ENABLED);
ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow enable\n");
- return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID);
+ ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id,
@@ -10327,6 +10346,7 @@ int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id,
struct wmi_wow_add_del_event_cmd *cmd;
struct sk_buff *skb;
size_t len;
+ int ret;
len = sizeof(*cmd);
skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -10343,7 +10363,13 @@ int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id,
ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow add wakeup event %s enable %d vdev_id %d\n",
wow_wakeup_event(event), enable, vdev_id);
- return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID);
+ ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id,
@@ -10356,6 +10382,7 @@ int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id,
struct sk_buff *skb;
void *ptr;
size_t len;
+ int ret;
len = sizeof(*cmd) +
sizeof(*tlv) + /* array struct */
@@ -10435,7 +10462,13 @@ int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id,
ath12k_dbg_dump(ar->ab, ATH12K_DBG_WMI, NULL, "wow bitmask: ",
bitmap->bitmaskbuf, pattern_len);
- return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID);
+ ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to send WMI_WOW_ADD_WAKE_PATTERN_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id)
@@ -10443,6 +10476,7 @@ int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id)
struct wmi_wow_del_pattern_cmd *cmd;
struct sk_buff *skb;
size_t len;
+ int ret;
len = sizeof(*cmd);
skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -10459,7 +10493,13 @@ int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id)
ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow del pattern vdev_id %d pattern_id %d\n",
vdev_id, pattern_id);
- return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID);
+ ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to send WMI_WOW_DEL_WAKE_PATTERN_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
static struct sk_buff *
@@ -10595,6 +10635,7 @@ int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id,
struct wmi_pno_scan_req_arg *pno_scan)
{
struct sk_buff *skb;
+ int ret;
if (pno_scan->enable)
skb = ath12k_wmi_op_gen_config_pno_start(ar, vdev_id, pno_scan);
@@ -10604,7 +10645,13 @@ int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id,
if (IS_ERR_OR_NULL(skb))
return -ENOMEM;
- return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID);
+ ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to send WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
static void ath12k_wmi_fill_ns_offload(struct ath12k *ar,
@@ -10717,6 +10764,7 @@ int ath12k_wmi_arp_ns_offload(struct ath12k *ar,
void *buf_ptr;
size_t len;
u8 ns_cnt, ns_ext_tuples = 0;
+ int ret;
ns_cnt = offload->ipv6_count;
@@ -10752,7 +10800,13 @@ int ath12k_wmi_arp_ns_offload(struct ath12k *ar,
if (ns_ext_tuples)
ath12k_wmi_fill_ns_offload(ar, offload, &buf_ptr, enable, 1);
- return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID);
+ ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to send WMI_SET_ARP_NS_OFFLOAD_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar,
@@ -10762,7 +10816,7 @@ int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar,
struct wmi_gtk_rekey_offload_cmd *cmd;
struct sk_buff *skb;
__le64 replay_ctr;
- int len;
+ int ret, len;
len = sizeof(*cmd);
skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -10789,7 +10843,13 @@ int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar,
ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "offload gtk rekey vdev: %d %d\n",
arvif->vdev_id, enable);
- return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+ ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID offload\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar,
@@ -10797,7 +10857,7 @@ int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar,
{
struct wmi_gtk_rekey_offload_cmd *cmd;
struct sk_buff *skb;
- int len;
+ int ret, len;
len = sizeof(*cmd);
skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@@ -10811,7 +10871,13 @@ int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar,
ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "get gtk rekey vdev_id: %d\n",
arvif->vdev_id);
- return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+ ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID getinfo\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath12k_wmi_sta_keepalive(struct ath12k *ar,
@@ -10822,6 +10888,7 @@ int ath12k_wmi_sta_keepalive(struct ath12k *ar,
struct wmi_sta_keepalive_cmd *cmd;
struct sk_buff *skb;
size_t len;
+ int ret;
len = sizeof(*cmd) + sizeof(*arp);
skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len);
@@ -10849,7 +10916,13 @@ int ath12k_wmi_sta_keepalive(struct ath12k *ar,
"wmi sta keepalive vdev %d enabled %d method %d interval %d\n",
arg->vdev_id, arg->enabled, arg->method, arg->interval);
- return ath12k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID);
+ ret = ath12k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to send WMI_STA_KEEPALIVE_CMDID\n");
+ dev_kfree_skb(skb);
+ }
+
+ return ret;
}
int ath12k_wmi_mlo_setup(struct ath12k *ar, struct wmi_mlo_setup_arg *mlo_params)
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 05c9c07591fc..6ca31d4ea437 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -1738,7 +1738,8 @@ ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb,
}
info->status.rates[ts->ts_final_idx].count = ts->ts_final_retry;
- info->status.rates[ts->ts_final_idx + 1].idx = -1;
+ if (ts->ts_final_idx + 1 < IEEE80211_TX_MAX_RATES)
+ info->status.rates[ts->ts_final_idx + 1].idx = -1;
if (unlikely(ts->ts_status)) {
ah->stats.ack_fail++;
diff --git a/drivers/net/wireless/broadcom/b43/xmit.c b/drivers/net/wireless/broadcom/b43/xmit.c
index 7651b1bdb592..f0b082596637 100644
--- a/drivers/net/wireless/broadcom/b43/xmit.c
+++ b/drivers/net/wireless/broadcom/b43/xmit.c
@@ -702,7 +702,8 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
* key index, but the ucode passed it slightly different.
*/
keyidx = b43_kidx_to_raw(dev, keyidx);
- B43_WARN_ON(keyidx >= ARRAY_SIZE(dev->key));
+ if (B43_WARN_ON(keyidx >= ARRAY_SIZE(dev->key)))
+ goto drop;
if (dev->key[keyidx].algorithm != B43_SEC_ALGO_NONE) {
wlhdr_len = ieee80211_hdrlen(fctl);
diff --git a/drivers/net/wireless/broadcom/b43legacy/xmit.c b/drivers/net/wireless/broadcom/b43legacy/xmit.c
index efd63f4ce74f..ee199d4eaf03 100644
--- a/drivers/net/wireless/broadcom/b43legacy/xmit.c
+++ b/drivers/net/wireless/broadcom/b43legacy/xmit.c
@@ -476,7 +476,8 @@ void b43legacy_rx(struct b43legacy_wldev *dev,
* key index, but the ucode passed it slightly different.
*/
keyidx = b43legacy_kidx_to_raw(dev, keyidx);
- B43legacy_WARN_ON(keyidx >= dev->max_nr_keys);
+ if (B43legacy_WARN_ON(keyidx >= dev->max_nr_keys))
+ goto drop;
if (dev->key[keyidx].algorithm != B43legacy_SEC_ALGO_NONE) {
/* Remove PROTECTED flag to mark it as decrypted. */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index 30f6fcb68632..8fb595733b9c 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -2476,8 +2476,9 @@ static void brcmf_sdio_bus_stop(struct device *dev)
brcmf_dbg(TRACE, "Enter\n");
if (bus->watchdog_tsk) {
+ get_task_struct(bus->watchdog_tsk);
send_sig(SIGTERM, bus->watchdog_tsk, 1);
- kthread_stop(bus->watchdog_tsk);
+ kthread_stop_put(bus->watchdog_tsk);
bus->watchdog_tsk = NULL;
}
@@ -4567,8 +4568,9 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus)
if (bus) {
/* Stop watchdog task */
if (bus->watchdog_tsk) {
+ get_task_struct(bus->watchdog_tsk);
send_sig(SIGTERM, bus->watchdog_tsk, 1);
- kthread_stop(bus->watchdog_tsk);
+ kthread_stop_put(bus->watchdog_tsk);
bus->watchdog_tsk = NULL;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ap.c b/drivers/net/wireless/intel/iwlwifi/mld/ap.c
index 5c59acc8c4c5..6598d9333333 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/ap.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/ap.c
@@ -9,7 +9,6 @@
#include "ap.h"
#include "hcmd.h"
#include "tx.h"
-#include "power.h"
#include "key.h"
#include "phy.h"
#include "iwl-utils.h"
@@ -273,9 +272,6 @@ int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx;
int ret;
- if (vif->type == NL80211_IFTYPE_AP)
- iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link);
-
ret = iwl_mld_update_beacon_template(mld, vif, link);
if (ret)
return ret;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/constants.h b/drivers/net/wireless/intel/iwlwifi/mld/constants.h
index e2a5eecc18c3..890abcab3837 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/constants.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/constants.h
@@ -1,11 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
*/
#ifndef __iwl_mld_constants_h__
#define __iwl_mld_constants_h__
-#define IWL_MLD_MISSED_BEACONS_SINCE_RX_THOLD 4
+#define IWL_MLD_MISSED_BEACONS_SINCE_RX_THOLD 6
#define IWL_MLD_MISSED_BEACONS_THRESHOLD 8
#define IWL_MLD_MISSED_BEACONS_THRESHOLD_LONG 19
#define IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS 5
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c
index ef98efc8fb1b..3a595a1c2e00 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c
@@ -1930,12 +1930,12 @@ int iwl_mld_wowlan_suspend(struct iwl_mld *mld, struct cfg80211_wowlan *wowlan)
if (WARN_ON(!wowlan))
return 1;
- IWL_DEBUG_WOWLAN(mld, "Starting the wowlan suspend flow\n");
-
bss_vif = iwl_mld_get_bss_vif(mld);
- if (WARN_ON(!bss_vif))
+ if (!bss_vif)
return 1;
+ IWL_DEBUG_WOWLAN(mld, "Starting the wowlan suspend flow\n");
+
if (!bss_vif->cfg.assoc) {
int ret;
/* If we're not associated, this must be netdetect */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c
index b66e84d2365f..be2cdf43c72e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
*/
#include "constants.h"
@@ -504,7 +504,6 @@ void iwl_mld_remove_link(struct iwl_mld *mld,
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif);
struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf);
bool is_deflink = link == &mld_vif->deflink;
- u8 fw_id = link->fw_id;
if (WARN_ON(!link || link->active))
return;
@@ -512,15 +511,15 @@ void iwl_mld_remove_link(struct iwl_mld *mld,
iwl_mld_rm_link_from_fw(mld, bss_conf);
/* Continue cleanup on failure */
- if (!is_deflink)
- kfree_rcu(link, rcu_head);
-
RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL);
- if (WARN_ON(fw_id >= mld->fw->ucode_capa.num_links))
+ if (WARN_ON(link->fw_id >= mld->fw->ucode_capa.num_links))
return;
- RCU_INIT_POINTER(mld->fw_id_to_bss_conf[fw_id], NULL);
+ RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL);
+
+ if (!is_deflink)
+ kfree_rcu(link, rcu_head);
}
void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
index da6fd7471568..3c8daddc0bcb 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -1150,6 +1150,13 @@ int iwl_mld_assign_vif_chanctx(struct ieee80211_hw *hw,
if (iwl_mld_can_activate_link(mld, vif, link)) {
iwl_mld_tlc_update_phy(mld, vif, link);
+ /* FW requires AP_TX_POWER_CONSTRAINTS_CMD before link
+ * activation for AP and after link activation for STA,
+ * for an unknown reason.
+ */
+ if (vif->type == NL80211_IFTYPE_AP)
+ iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link);
+
ret = iwl_mld_activate_link(mld, link);
if (ret)
goto err;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.c b/drivers/net/wireless/intel/iwlwifi/mld/power.c
index 49b0d9f8f865..266fe16bb95d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/power.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/power.c
@@ -366,7 +366,7 @@ iwl_mld_send_ap_tx_power_constraint_cmd(struct iwl_mld *mld,
lockdep_assert_wiphy(mld->wiphy);
- if (!mld_link->active)
+ if (!mld_link->active && vif->type != NL80211_IFTYPE_AP)
return;
if (link->chanreq.oper.chan->band != NL80211_BAND_6GHZ)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c
index 546d09a38dab..0bcb1ae69468 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c
@@ -834,7 +834,7 @@ static int iwl_mld_tx_tso_segment(struct iwl_mld *mld, struct sk_buff *skb,
return -EINVAL;
max_tid_amsdu_len = sta->cur->max_tid_amsdu_len[tid];
- if (!max_tid_amsdu_len)
+ if (!max_tid_amsdu_len || max_tid_amsdu_len == 1)
return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs);
/* Sub frame header + SNAP + IP header + TCP header + MSS */
@@ -846,6 +846,9 @@ static int iwl_mld_tx_tso_segment(struct iwl_mld *mld, struct sk_buff *skb,
*/
num_subframes = (max_tid_amsdu_len + pad) / (subf_len + pad);
+ if (WARN_ON_ONCE(!num_subframes))
+ return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs);
+
if (sta->max_amsdu_subframes &&
num_subframes > sta->max_amsdu_subframes)
num_subframes = sta->max_amsdu_subframes;
@@ -971,6 +974,16 @@ void iwl_mld_tx_from_txq(struct iwl_mld *mld, struct ieee80211_txq *txq)
u8 zero_addr[ETH_ALEN] = {};
/*
+ * Don't transmit during firmware restart. The firmware is dead,
+ * so iwl_trans_tx() would return -EIO for each frame. Avoid the
+ * overhead of dequeuing from mac80211 only to immediately free
+ * the skbs, and the potential memory pressure from rapid skb
+ * allocation churn during high-throughput restart scenarios.
+ */
+ if (unlikely(mld->fw_status.in_hw_restart))
+ return;
+
+ /*
* No need for threads to be pending here, they can leave the first
* taker all the work.
*
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index f05df3a3300e..6e507d6dcdd2 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2026 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -459,9 +459,14 @@ static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm,
static void iwl_mvm_uats_init(struct iwl_mvm *mvm)
{
+ struct iwl_mcc_allowed_ap_type_cmd_v1 *cmd __free(kfree) = NULL;
int cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP,
MCC_ALLOWED_AP_TYPE_CMD);
- struct iwl_mcc_allowed_ap_type_cmd_v1 cmd = {};
+ struct iwl_host_cmd hcmd = {
+ .id = cmd_id,
+ .len[0] = sizeof(*cmd),
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+ };
u8 cmd_ver;
int ret;
@@ -485,14 +490,25 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm)
if (!mvm->fwrt.ap_type_cmd_valid)
return;
+ /* Since we free the command immediately after iwl_mvm_send_cmd, we
+ * must send this command in SYNC mode.
+ */
+ lockdep_assert_held(&mvm->mutex);
+
+ cmd = kzalloc_obj(*cmd);
+ if (!cmd)
+ return;
+
BUILD_BUG_ON(sizeof(mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map) !=
- sizeof(cmd.mcc_to_ap_type_map));
+ sizeof(cmd->mcc_to_ap_type_map));
- memcpy(cmd.mcc_to_ap_type_map,
+ memcpy(cmd->mcc_to_ap_type_map,
mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map,
sizeof(mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map));
- ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd);
+ hcmd.data[0] = cmd;
+
+ ret = iwl_mvm_send_cmd(mvm, &hcmd);
if (ret < 0)
IWL_ERR(mvm, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n",
ret);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
index c523c5e82d4a..8ffa72aca3cf 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2026 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -927,13 +927,18 @@ u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct iwl_mvm *mvm,
u16 iwl_mvm_mac_ctxt_get_beacon_flags(const struct iwl_fw *fw, u8 rate_idx)
{
- u16 flags = iwl_mvm_mac80211_idx_to_hwrate(fw, rate_idx);
bool is_new_rate = iwl_fw_lookup_cmd_ver(fw, BEACON_TEMPLATE_CMD, 0) > 10;
+ u16 flags = 0;
if (rate_idx <= IWL_LAST_CCK_RATE)
flags |= is_new_rate ? IWL_MAC_BEACON_CCK
: IWL_MAC_BEACON_CCK_V1;
+ if (iwl_fw_lookup_cmd_ver(fw, TX_CMD, 0) > 8)
+ flags |= iwl_mvm_mac80211_idx_to_hwrate(fw, rate_idx);
+ else
+ flags |= iwl_fw_rate_idx_to_plcp(rate_idx);
+
return flags;
}
@@ -962,6 +967,7 @@ static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm,
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct ieee80211_tx_info *info;
+ u32 rate_n_flags = 0;
u8 rate;
u32 tx_flags;
@@ -981,18 +987,21 @@ static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm,
IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION)) {
iwl_mvm_toggle_tx_ant(mvm, &mvm->mgmt_last_antenna_idx);
- tx_params->rate_n_flags =
- cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) <<
- RATE_MCS_ANT_POS);
+ rate_n_flags |= BIT(mvm->mgmt_last_antenna_idx) <<
+ RATE_MCS_ANT_POS;
}
rate = iwl_mvm_mac_ctxt_get_beacon_rate(mvm, info, vif);
- tx_params->rate_n_flags |=
- cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(mvm->fw, rate));
- if (rate == IWL_FIRST_CCK_RATE)
- tx_params->rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK_V1);
+ if (rate < IWL_FIRST_OFDM_RATE)
+ rate_n_flags |= RATE_MCS_MOD_TYPE_CCK;
+ else
+ rate_n_flags |= RATE_MCS_MOD_TYPE_LEGACY_OFDM;
+
+ rate_n_flags |= iwl_mvm_mac80211_idx_to_hwrate(mvm->fw, rate);
+ tx_params->rate_n_flags = iwl_mvm_v3_rate_to_fw(rate_n_flags,
+ mvm->fw_rates_ver);
}
int iwl_mvm_mac_ctxt_send_beacon_cmd(struct iwl_mvm *mvm,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
index ae177477b201..384bed95835d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
@@ -1416,6 +1416,12 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_FW_RESET_HANDSHAKE);
+ /* Those firmware versions claim to support the fw_reset_handshake
+ * but they are buggy.
+ */
+ if (IWL_UCODE_MAJOR(mvm->fw->ucode_ver) <= 77)
+ trans->conf.fw_reset_handshake = false;
+
trans->conf.queue_alloc_cmd_ver =
iwl_fw_lookup_cmd_ver(mvm->fw,
WIDE_ID(DATA_PATH_GROUP,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
index 4a33a032c2a7..f052537e9567 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2026 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -159,15 +159,9 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
u8 iwl_mvm_mac80211_idx_to_hwrate(const struct iwl_fw *fw, int rate_idx)
{
- if (iwl_fw_lookup_cmd_ver(fw, TX_CMD, 0) > 8)
- /* In the new rate legacy rates are indexed:
- * 0 - 3 for CCK and 0 - 7 for OFDM.
- */
- return (rate_idx >= IWL_FIRST_OFDM_RATE ?
- rate_idx - IWL_FIRST_OFDM_RATE :
- rate_idx);
-
- return iwl_fw_rate_idx_to_plcp(rate_idx);
+ return rate_idx >= IWL_FIRST_OFDM_RATE ?
+ rate_idx - IWL_FIRST_OFDM_RATE :
+ rate_idx;
}
u8 iwl_mvm_mac80211_ac_to_ucode_ac(enum ieee80211_ac_numbers ac)
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
index dc99e7ac4726..eb3c5a6dd088 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
@@ -1225,33 +1225,41 @@ static int _iwl_pci_resume(struct device *device, bool restore)
if (!trans->op_mode)
return 0;
- /*
- * Scratch value was altered, this means the device was powered off, we
- * need to reset it completely.
- * Note: MAC (bits 0:7) will be cleared upon suspend even with wowlan,
- * but not bits [15:8]. So if we have bits set in lower word, assume
- * the device is alive.
- * Alternatively, if the scratch value is 0xFFFFFFFF, then we no longer
- * have access to the device and consider it powered off.
- * For older devices, just try silently to grab the NIC.
- */
- if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
- u32 scratch = iwl_read32(trans, CSR_FUNC_SCRATCH);
-
- if (!(scratch & CSR_FUNC_SCRATCH_POWER_OFF_MASK) ||
- scratch == ~0U)
- device_was_powered_off = true;
- } else {
+ if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
/*
- * bh are re-enabled by iwl_trans_pcie_release_nic_access,
- * so re-enable them if _iwl_trans_pcie_grab_nic_access fails.
+ * Scratch value was altered, this means the device was powered
+ * off, we need to reset it completely.
+ * Note: MAC (bits 0:7) will be cleared upon suspend even with
+ * wowlan, but not bits [15:8]. So if we have bits set in lower
+ * word, assume the device is alive.
+ * Alternatively, if the scratch value is 0xFFFFFFFF, then we
+ * no longer have access to the device and consider it powered
+ * off.
+ * For older devices, just try silently to grab the NIC.
*/
- local_bh_disable();
- if (_iwl_trans_pcie_grab_nic_access(trans, true)) {
- iwl_trans_pcie_release_nic_access(trans);
+ if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
+ u32 scratch = iwl_read32(trans, CSR_FUNC_SCRATCH);
+
+ if (!(scratch & CSR_FUNC_SCRATCH_POWER_OFF_MASK) ||
+ scratch == ~0U) {
+ IWL_DEBUG_WOWLAN(trans,
+ "Scratch 0x%08x indicates device was powered off\n",
+ scratch);
+ device_was_powered_off = true;
+ }
} else {
- device_was_powered_off = true;
- local_bh_enable();
+ /*
+ * bh are re-enabled by iwl_trans_pcie_release_nic_access,
+ * so re-enable them if _iwl_trans_pcie_grab_nic_access
+ * fails.
+ */
+ local_bh_disable();
+ if (_iwl_trans_pcie_grab_nic_access(trans, true)) {
+ iwl_trans_pcie_release_nic_access(trans);
+ } else {
+ device_was_powered_off = true;
+ local_bh_enable();
+ }
}
}
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c
index a50e845cea42..64262bcca55d 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans-gen2.c
@@ -398,9 +398,9 @@ void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans)
mutex_unlock(&trans_pcie->mutex);
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
- trans->step_urm = !!(iwl_read_umac_prph(trans,
- CNVI_PMU_STEP_FLOW) &
- CNVI_PMU_STEP_FLOW_FORCE_URM);
+ trans->step_urm = !!(iwl_read_prph(trans,
+ CNVI_PMU_STEP_FLOW) &
+ CNVI_PMU_STEP_FLOW_FORCE_URM);
}
static bool iwl_pcie_set_ltr(struct iwl_trans *trans)
diff --git a/drivers/net/wireless/marvell/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c
index 4fae0e335136..5cc0c5cac257 100644
--- a/drivers/net/wireless/marvell/libertas/if_usb.c
+++ b/drivers/net/wireless/marvell/libertas/if_usb.c
@@ -310,6 +310,7 @@ static void if_usb_disconnect(struct usb_interface *intf)
struct lbs_private *priv = cardp->priv;
cardp->surprise_removed = 1;
+ wake_up(&cardp->fw_wq);
if (priv) {
lbs_stop_card(priv);
@@ -633,9 +634,10 @@ static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff,
unsigned long flags;
u8 i;
- if (recvlength > LBS_CMD_BUFFER_SIZE) {
+ if (recvlength < MESSAGE_HEADER_LEN ||
+ recvlength > LBS_CMD_BUFFER_SIZE) {
lbs_deb_usbd(&cardp->udev->dev,
- "The receive buffer is too large\n");
+ "The receive buffer is invalid: %d\n", recvlength);
kfree_skb(skb);
return;
}
diff --git a/drivers/net/wireless/microchip/wilc1000/wlan.c b/drivers/net/wireless/microchip/wilc1000/wlan.c
index 3fa8592eb250..4b116fe6f9ea 100644
--- a/drivers/net/wireless/microchip/wilc1000/wlan.c
+++ b/drivers/net/wireless/microchip/wilc1000/wlan.c
@@ -1265,7 +1265,7 @@ int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer,
ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
if (ret)
- return ret;
+ goto fail;
wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
reg &= ~BIT(10);
diff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h
index 591602beeec6..3cdf9ded876d 100644
--- a/drivers/net/wireless/rsi/rsi_common.h
+++ b/drivers/net/wireless/rsi/rsi_common.h
@@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common,
return 0;
}
-static inline int rsi_kill_thread(struct rsi_thread *handle)
+static inline void rsi_kill_thread(struct rsi_thread *handle)
{
atomic_inc(&handle->thread_done);
rsi_set_event(&handle->event);
-
- return kthread_stop(handle->task);
+ wait_for_completion(&handle->completion);
}
void rsi_mac80211_detach(struct rsi_hw *hw);
diff --git a/drivers/net/wireless/st/cw1200/pm.c b/drivers/net/wireless/st/cw1200/pm.c
index 84eb15d729c7..120f0379f81d 100644
--- a/drivers/net/wireless/st/cw1200/pm.c
+++ b/drivers/net/wireless/st/cw1200/pm.c
@@ -264,14 +264,12 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
wiphy_err(priv->hw->wiphy,
"PM request failed: %d. WoW is disabled.\n", ret);
cw1200_wow_resume(hw);
- mutex_unlock(&priv->conf_mutex);
return -EBUSY;
}
/* Force resume if event is coming from the device. */
if (atomic_read(&priv->bh_rx)) {
cw1200_wow_resume(hw);
- mutex_unlock(&priv->conf_mutex);
return -EAGAIN;
}
diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem.c b/drivers/net/wwan/iosm/iosm_ipc_imem.c
index 1b7bc7d63a2e..4405c8531888 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_imem.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_imem.c
@@ -1425,6 +1425,8 @@ imem_config_fail:
protocol_init_fail:
cancel_work_sync(&ipc_imem->run_state_worker);
ipc_task_deinit(ipc_imem->ipc_task);
+ if (ipc_imem->ipc_protocol)
+ ipc_protocol_deinit(ipc_imem->ipc_protocol);
ipc_task_init_fail:
kfree(ipc_imem->ipc_task);
ipc_task_fail:
diff --git a/drivers/net/wwan/t7xx/t7xx_modem_ops.c b/drivers/net/wwan/t7xx/t7xx_modem_ops.c
index 7968e208dd37..adb29d30c63f 100644
--- a/drivers/net/wwan/t7xx/t7xx_modem_ops.c
+++ b/drivers/net/wwan/t7xx/t7xx_modem_ops.c
@@ -457,8 +457,20 @@ static int t7xx_parse_host_rt_data(struct t7xx_fsm_ctl *ctl, struct t7xx_sys_inf
offset = sizeof(struct feature_query);
for (i = 0; i < FEATURE_COUNT && offset < data_length; i++) {
+ size_t remaining = data_length - offset;
+ size_t feat_data_len, feat_total;
+
+ if (remaining < sizeof(*rt_feature))
+ break;
+
rt_feature = data + offset;
- offset += sizeof(*rt_feature) + le32_to_cpu(rt_feature->data_len);
+ feat_data_len = le32_to_cpu(rt_feature->data_len);
+
+ if (feat_data_len > remaining - sizeof(*rt_feature))
+ break;
+
+ feat_total = sizeof(*rt_feature) + feat_data_len;
+ offset += feat_total;
ft_spt_cfg = FIELD_GET(FEATURE_MSK, core->feature_set[i]);
if (ft_spt_cfg != MTK_FEATURE_MUST_BE_SUPPORTED)
@@ -468,8 +480,10 @@ static int t7xx_parse_host_rt_data(struct t7xx_fsm_ctl *ctl, struct t7xx_sys_inf
if (ft_spt_st != MTK_FEATURE_MUST_BE_SUPPORTED)
return -EINVAL;
- if (i == RT_ID_MD_PORT_ENUM || i == RT_ID_AP_PORT_ENUM)
- t7xx_port_enum_msg_handler(ctl->md, rt_feature->data);
+ if (i == RT_ID_MD_PORT_ENUM || i == RT_ID_AP_PORT_ENUM) {
+ t7xx_port_enum_msg_handler(ctl->md, rt_feature->data,
+ feat_data_len);
+ }
}
return 0;
diff --git a/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c b/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
index ae632ef96698..f869e4ed9ee9 100644
--- a/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
+++ b/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
@@ -117,6 +117,7 @@ static int fsm_ee_message_handler(struct t7xx_port *port, struct t7xx_fsm_ctl *c
* t7xx_port_enum_msg_handler() - Parse the port enumeration message to create/remove nodes.
* @md: Modem context.
* @msg: Message.
+ * @msg_len: Length of @msg in bytes.
*
* Used to control create/remove device node.
*
@@ -124,12 +125,18 @@ static int fsm_ee_message_handler(struct t7xx_port *port, struct t7xx_fsm_ctl *c
* * 0 - Success.
* * -EFAULT - Message check failure.
*/
-int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg)
+int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg, size_t msg_len)
{
struct device *dev = &md->t7xx_dev->pdev->dev;
unsigned int version, port_count, i;
struct port_msg *port_msg = msg;
+ if (msg_len < sizeof(*port_msg)) {
+ dev_err(dev, "Port enum msg too short for header: need %zu, have %zu\n",
+ sizeof(*port_msg), msg_len);
+ return -EINVAL;
+ }
+
version = FIELD_GET(PORT_MSG_VERSION, le32_to_cpu(port_msg->info));
if (version != PORT_ENUM_VER ||
le32_to_cpu(port_msg->head_pattern) != PORT_ENUM_HEAD_PATTERN ||
@@ -141,6 +148,13 @@ int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg)
}
port_count = FIELD_GET(PORT_MSG_PRT_CNT, le32_to_cpu(port_msg->info));
+
+ if (msg_len < struct_size(port_msg, data, port_count)) {
+ dev_err(dev, "Port enum msg too short: need %zu, have %zu\n",
+ struct_size(port_msg, data, port_count), msg_len);
+ return -EINVAL;
+ }
+
for (i = 0; i < port_count; i++) {
u32 port_info = le32_to_cpu(port_msg->data[i]);
unsigned int ch_id;
@@ -191,7 +205,7 @@ static int control_msg_handler(struct t7xx_port *port, struct sk_buff *skb)
case CTL_ID_PORT_ENUM:
skb_pull(skb, sizeof(*ctrl_msg_h));
- ret = t7xx_port_enum_msg_handler(ctl->md, (struct port_msg *)skb->data);
+ ret = t7xx_port_enum_msg_handler(ctl->md, (struct port_msg *)skb->data, skb->len);
if (!ret)
ret = port_ctl_send_msg_to_md(port, CTL_ID_PORT_ENUM, 0);
else
diff --git a/drivers/net/wwan/t7xx/t7xx_port_proxy.h b/drivers/net/wwan/t7xx/t7xx_port_proxy.h
index f0918b36e899..7c3190bf0fcf 100644
--- a/drivers/net/wwan/t7xx/t7xx_port_proxy.h
+++ b/drivers/net/wwan/t7xx/t7xx_port_proxy.h
@@ -103,7 +103,7 @@ void t7xx_port_proxy_reset(struct port_proxy *port_prox);
void t7xx_port_proxy_uninit(struct port_proxy *port_prox);
int t7xx_port_proxy_init(struct t7xx_modem *md);
void t7xx_port_proxy_md_status_notify(struct port_proxy *port_prox, unsigned int state);
-int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg);
+int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg, size_t msg_len);
int t7xx_port_proxy_chl_enable_disable(struct port_proxy *port_prox, unsigned int ch_id,
bool en_flag);
void t7xx_port_proxy_set_cfg(struct t7xx_modem *md, enum port_cfg_id cfg_id);