summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-03 15:44:28 +0100
committerMark Brown <broonie@kernel.org>2026-07-03 15:44:28 +0100
commit5f9618618bce70008bcfb142e5502f111ddeb72c (patch)
tree93fae2a33bc02a4fdb59209ce6af56edeaf3ceb6
parenta7d435fd4c2f7e42117f5f31c3c40010b12d855a (diff)
parent6fb33632323a396c9dc2bb9bea483e013e547d57 (diff)
downloadlinux-next-5f9618618bce70008bcfb142e5502f111ddeb72c.tar.gz
linux-next-5f9618618bce70008bcfb142e5502f111ddeb72c.zip
Merge branch 'main' of https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git
# Conflicts: # MAINTAINERS
-rw-r--r--Documentation/netlink/specs/rt-addr.yaml4
-rw-r--r--MAINTAINERS5
-rw-r--r--arch/powerpc/configs/ppc64_defconfig1
-rw-r--r--arch/powerpc/mm/mem.c6
-rw-r--r--drivers/net/bonding/bond_3ad.c24
-rw-r--r--drivers/net/bonding/bond_netlink.c109
-rw-r--r--drivers/net/bonding/bond_options.c8
-rw-r--r--drivers/net/dsa/b53/b53_priv.h3
-rw-r--r--drivers/net/dsa/microchip/ksz8.c2
-rw-r--r--drivers/net/dsa/qca/qca8k-leds.c3
-rw-r--r--drivers/net/dsa/realtek/rtl8366rb.c268
-rw-r--r--drivers/net/dsa/realtek/rtl83xx.c26
-rw-r--r--drivers/net/ethernet/allwinner/sun4i-emac.c2
-rw-r--r--drivers/net/ethernet/apm/xgene/xgene_enet_main.c2
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c931
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h42
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h18
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpsw.c60
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpsw.h30
-rw-r--r--drivers/net/ethernet/ibm/Kconfig9
-rw-r--r--drivers/net/ethernet/ibm/Makefile1
-rw-r--r--drivers/net/ethernet/ibm/ehea/Makefile7
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea.h477
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_ethtool.c277
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_hw.h253
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_main.c3581
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_phyp.c612
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_phyp.h433
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_qmr.c999
-rw-r--r--drivers/net/ethernet/ibm/ehea/ehea_qmr.h390
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c18
-rw-r--r--drivers/net/ethernet/marvell/mvneta_bm.c72
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/mbox.h2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/npc.h4
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu.h2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c9
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c43
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c1
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.h2
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c2
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c7
-rw-r--r--drivers/net/ethernet/sgi/ioc3-eth.c9
-rw-r--r--drivers/net/netconsole.c2
-rw-r--r--drivers/net/phy/mdio_device.c2
-rw-r--r--drivers/net/phy/phylink.c15
-rw-r--r--drivers/net/usb/rtl8150.c4
-rw-r--r--include/linux/net.h23
-rw-r--r--include/net/bond_3ad.h4
-rw-r--r--include/net/bonding.h8
-rw-r--r--include/net/fib_rules.h4
-rw-r--r--include/net/ip_fib.h3
-rw-r--r--include/net/netns/ipv4.h1
-rw-r--r--include/net/udp.h2
-rw-r--r--include/uapi/linux/if_addr.h1
-rw-r--r--net/batman-adv/bat_iv_ogm.c12
-rw-r--r--net/batman-adv/bat_v_elp.c9
-rw-r--r--net/batman-adv/bat_v_ogm.c33
-rw-r--r--net/batman-adv/bridge_loop_avoidance.c9
-rw-r--r--net/batman-adv/hard-interface.c165
-rw-r--r--net/batman-adv/hard-interface.h10
-rw-r--r--net/batman-adv/main.c9
-rw-r--r--net/batman-adv/main.h3
-rw-r--r--net/batman-adv/mesh-interface.c13
-rw-r--r--net/batman-adv/netlink.c4
-rw-r--r--net/batman-adv/originator.c4
-rw-r--r--net/batman-adv/tp_meter.c217
-rw-r--r--net/batman-adv/tvlv.c86
-rw-r--r--net/batman-adv/types.h28
-rw-r--r--net/core/fib_rules.c82
-rw-r--r--net/core/netpoll.c2
-rw-r--r--net/ipv4/fib_frontend.c66
-rw-r--r--net/ipv4/fib_rules.c20
-rw-r--r--net/ipv4/fib_trie.c3
-rw-r--r--net/ipv4/igmp.c2
-rw-r--r--net/ipv4/raw.c41
-rw-r--r--net/ipv4/udp.c39
-rw-r--r--net/ipv6/addrconf.c1
-rw-r--r--net/ipv6/fib6_rules.c17
-rw-r--r--net/ipv6/mcast.c1
-rw-r--r--net/ipv6/udp.c19
-rw-r--r--net/vmw_vsock/virtio_transport_common.c47
-rw-r--r--tools/net/ynl/pyynl/__init__.py9
-rwxr-xr-xtools/net/ynl/pyynl/cli.py56
-rw-r--r--tools/net/ynl/pyynl/lib/__init__.py3
-rw-r--r--tools/net/ynl/pyynl/lib/nlspec.py22
-rw-r--r--tools/net/ynl/pyynl/lib/specdir.py51
-rw-r--r--tools/net/ynl/pyynl/lib/ynl.py19
-rwxr-xr-xtools/net/ynl/tests/ethtool.py7
-rw-r--r--tools/testing/selftests/bpf/prog_tests/test_xsk.c4
-rwxr-xr-xtools/testing/selftests/drivers/net/hw/toeplitz.py22
-rw-r--r--tools/testing/selftests/net/getsockopt_iter.c97
-rw-r--r--tools/testing/selftests/net/lib.sh19
-rwxr-xr-xtools/testing/selftests/net/rtnetlink.py101
95 files changed, 2200 insertions, 7979 deletions
diff --git a/Documentation/netlink/specs/rt-addr.yaml b/Documentation/netlink/specs/rt-addr.yaml
index 163a106c41bb..0ecbd24c890c 100644
--- a/Documentation/netlink/specs/rt-addr.yaml
+++ b/Documentation/netlink/specs/rt-addr.yaml
@@ -123,6 +123,9 @@ attribute-sets:
-
name: proto
type: u8
+ -
+ name: mc-users
+ type: u32
operations:
@@ -176,6 +179,7 @@ operations:
value: 58
attributes: &mcaddr-attrs
- multicast
+ - mc-users
- cacheinfo
dump:
request:
diff --git a/MAINTAINERS b/MAINTAINERS
index 3635163e31ce..8e2551fd5cba 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9495,11 +9495,6 @@ L: linux-fbdev@vger.kernel.org
S: Maintained
F: drivers/video/fbdev/efifb.c
-EHEA (IBM pSeries eHEA 10Gb ethernet adapter) DRIVER
-L: netdev@vger.kernel.org
-S: Orphan
-F: drivers/net/ethernet/ibm/ehea/
-
ELM327 CAN NETWORK DRIVER
M: Max Staudt <max@enpas.org>
L: linux-can@vger.kernel.org
diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig
index f795b74602ec..1eb8e3457e8b 100644
--- a/arch/powerpc/configs/ppc64_defconfig
+++ b/arch/powerpc/configs/ppc64_defconfig
@@ -210,7 +210,6 @@ CONFIG_BNX2X=m
CONFIG_CHELSIO_T1=m
CONFIG_BE2NET=m
CONFIG_IBMVETH=m
-CONFIG_EHEA=m
CONFIG_IBMVNIC=m
CONFIG_E100=y
CONFIG_E1000=y
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index 4c1afab91996..b617b69452cd 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -371,12 +371,6 @@ int devmem_is_allowed(unsigned long pfn)
}
#endif /* CONFIG_STRICT_DEVMEM */
-/*
- * This is defined in kernel/resource.c but only powerpc needs to export it, for
- * the EHEA driver. Drop this when drivers/net/ethernet/ibm/ehea is removed.
- */
-EXPORT_SYMBOL_GPL(walk_system_ram_range);
-
#ifdef CONFIG_EXECMEM
static struct execmem_info execmem_info __ro_after_init;
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index acbba08dbdfa..b8e4b4d68dd6 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -760,14 +760,14 @@ static int __agg_usable_ports(struct aggregator *agg)
return valid;
}
-static int __agg_active_ports(struct aggregator *agg)
+static int __agg_active_ports(const struct aggregator *agg)
{
- struct port *port;
+ const struct port *port;
int active = 0;
for (port = agg->lag_ports; port;
port = port->next_port_in_aggregator) {
- if (port->is_enabled)
+ if (READ_ONCE(port->is_enabled))
active++;
}
@@ -2801,11 +2801,11 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
* some of he adaptors(ce1000.lan) report.
*/
if (link == BOND_LINK_UP) {
- port->is_enabled = true;
+ WRITE_ONCE(port->is_enabled, true);
ad_update_actor_keys(port, false);
} else {
/* link has failed */
- port->is_enabled = false;
+ WRITE_ONCE(port->is_enabled, false);
ad_update_actor_keys(port, true);
}
agg = __get_first_agg(port);
@@ -2878,16 +2878,20 @@ out:
* Returns: 0 on success
* < 0 on error
*/
-int __bond_3ad_get_active_agg_info(struct bonding *bond,
+int __bond_3ad_get_active_agg_info(const struct bonding *bond,
struct ad_info *ad_info)
{
- struct aggregator *aggregator = NULL, *tmp;
+ const struct aggregator *aggregator = NULL, *tmp;
+ struct ad_slave_info *ad_slave_info;
+ const struct port *port;
struct list_head *iter;
struct slave *slave;
- struct port *port;
bond_for_each_slave_rcu(bond, slave, iter) {
- port = &(SLAVE_AD_INFO(slave)->port);
+ ad_slave_info = SLAVE_AD_INFO(slave);
+ if (!ad_slave_info)
+ continue;
+ port = &ad_slave_info->port;
tmp = rcu_dereference(port->aggregator);
if (tmp && tmp->is_active) {
aggregator = tmp;
@@ -2907,7 +2911,7 @@ int __bond_3ad_get_active_agg_info(struct bonding *bond,
return 0;
}
-int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info)
+int bond_3ad_get_active_agg_info(const struct bonding *bond, struct ad_info *ad_info)
{
int ret;
diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
index 4a11572f663d..55d2f8a539d4 100644
--- a/drivers/net/bonding/bond_netlink.c
+++ b/drivers/net/bonding/bond_netlink.c
@@ -686,53 +686,58 @@ static size_t bond_get_size(const struct net_device *bond_dev)
0;
}
-static int bond_option_active_slave_get_ifindex(struct bonding *bond)
+static int bond_option_active_slave_get_ifindex_rcu(const struct bonding *bond)
{
- const struct net_device *slave;
- int ifindex;
+ const struct net_device *dev = NULL;
+ const struct slave *slave;
- rcu_read_lock();
- slave = bond_option_active_slave_get_rcu(bond);
- ifindex = slave ? slave->ifindex : 0;
- rcu_read_unlock();
- return ifindex;
+ slave = rcu_dereference(bond->curr_active_slave);
+ if (slave)
+ dev = slave->dev;
+ return dev ? dev->ifindex : 0;
}
static int bond_fill_info(struct sk_buff *skb,
const struct net_device *bond_dev)
{
- struct bonding *bond = netdev_priv(bond_dev);
- unsigned int packets_per_slave;
- int ifindex, i, targets_added;
+ const struct bonding *bond = netdev_priv(bond_dev);
+ int i, targets_added, miimon, mode;
+ const struct slave *primary;
struct nlattr *targets;
- struct slave *primary;
- if (nla_put_u8(skb, IFLA_BOND_MODE, BOND_MODE(bond)))
+ rcu_read_lock();
+ mode = READ_ONCE(bond->params.mode);
+ if (nla_put_u8(skb, IFLA_BOND_MODE, mode))
goto nla_put_failure;
- ifindex = bond_option_active_slave_get_ifindex(bond);
- if (ifindex && nla_put_u32(skb, IFLA_BOND_ACTIVE_SLAVE, ifindex))
- goto nla_put_failure;
+ if (bond_mode_uses_primary(mode)) {
+ int ifindex = bond_option_active_slave_get_ifindex_rcu(bond);
+
+ if (ifindex && nla_put_u32(skb, IFLA_BOND_ACTIVE_SLAVE, ifindex))
+ goto nla_put_failure;
+ }
- if (nla_put_u32(skb, IFLA_BOND_MIIMON, bond->params.miimon))
+ miimon = READ_ONCE(bond->params.miimon);
+ if (nla_put_u32(skb, IFLA_BOND_MIIMON, miimon))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_BOND_UPDELAY,
- bond->params.updelay * bond->params.miimon))
+ READ_ONCE(bond->params.updelay) * miimon))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_BOND_DOWNDELAY,
- bond->params.downdelay * bond->params.miimon))
+ READ_ONCE(bond->params.downdelay) * miimon))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_BOND_PEER_NOTIF_DELAY,
- bond->params.peer_notif_delay * bond->params.miimon))
+ READ_ONCE(bond->params.peer_notif_delay) * miimon))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_USE_CARRIER, 1))
goto nla_put_failure;
- if (nla_put_u32(skb, IFLA_BOND_ARP_INTERVAL, bond->params.arp_interval))
+ if (nla_put_u32(skb, IFLA_BOND_ARP_INTERVAL,
+ READ_ONCE(bond->params.arp_interval)))
goto nla_put_failure;
targets = nla_nest_start_noflag(skb, IFLA_BOND_ARP_IP_TARGET);
@@ -741,8 +746,10 @@ static int bond_fill_info(struct sk_buff *skb,
targets_added = 0;
for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) {
- if (bond->params.arp_targets[i]) {
- if (nla_put_be32(skb, i, bond->params.arp_targets[i]))
+ __be32 t = READ_ONCE(bond->params.arp_targets[i]);
+
+ if (t) {
+ if (nla_put_be32(skb, i, t))
goto nla_put_failure;
targets_added = 1;
}
@@ -753,11 +760,12 @@ static int bond_fill_info(struct sk_buff *skb,
else
nla_nest_cancel(skb, targets);
- if (nla_put_u32(skb, IFLA_BOND_ARP_VALIDATE, bond->params.arp_validate))
+ if (nla_put_u32(skb, IFLA_BOND_ARP_VALIDATE,
+ READ_ONCE(bond->params.arp_validate)))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_BOND_ARP_ALL_TARGETS,
- bond->params.arp_all_targets))
+ READ_ONCE(bond->params.arp_all_targets)))
goto nla_put_failure;
#if IS_ENABLED(CONFIG_IPV6)
@@ -767,6 +775,9 @@ static int bond_fill_info(struct sk_buff *skb,
targets_added = 0;
for (i = 0; i < BOND_MAX_NS_TARGETS; i++) {
+ /* Note: IPv6 addresses can not be read in an atomic READ_ONCE() yet.
+ * We accept this minor race for the moment.
+ */
if (!ipv6_addr_any(&bond->params.ns_targets[i])) {
if (nla_put_in6_addr(skb, i, &bond->params.ns_targets[i]))
goto nla_put_failure;
@@ -780,97 +791,97 @@ static int bond_fill_info(struct sk_buff *skb,
nla_nest_cancel(skb, targets);
#endif
- primary = rtnl_dereference(bond->primary_slave);
+ primary = rcu_dereference(bond->primary_slave);
if (primary &&
nla_put_u32(skb, IFLA_BOND_PRIMARY, primary->dev->ifindex))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_PRIMARY_RESELECT,
- bond->params.primary_reselect))
+ READ_ONCE(bond->params.primary_reselect)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_FAIL_OVER_MAC,
- bond->params.fail_over_mac))
+ READ_ONCE(bond->params.fail_over_mac)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_XMIT_HASH_POLICY,
- bond->params.xmit_policy))
+ READ_ONCE(bond->params.xmit_policy)))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_BOND_RESEND_IGMP,
- bond->params.resend_igmp))
+ READ_ONCE(bond->params.resend_igmp)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_NUM_PEER_NOTIF,
- bond->params.num_peer_notif))
+ READ_ONCE(bond->params.num_peer_notif)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_ALL_SLAVES_ACTIVE,
- bond->params.all_slaves_active))
+ READ_ONCE(bond->params.all_slaves_active)))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_BOND_MIN_LINKS,
- bond->params.min_links))
+ READ_ONCE(bond->params.min_links)))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_BOND_LP_INTERVAL,
- bond->params.lp_interval))
+ READ_ONCE(bond->params.lp_interval)))
goto nla_put_failure;
- packets_per_slave = bond->params.packets_per_slave;
if (nla_put_u32(skb, IFLA_BOND_PACKETS_PER_SLAVE,
- packets_per_slave))
+ READ_ONCE(bond->params.packets_per_slave)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_AD_LACP_ACTIVE,
- bond->params.lacp_active))
+ READ_ONCE(bond->params.lacp_active)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_AD_LACP_RATE,
- bond->params.lacp_fast))
+ READ_ONCE(bond->params.lacp_fast)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_AD_SELECT,
- bond->params.ad_select))
+ READ_ONCE(bond->params.ad_select)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_TLB_DYNAMIC_LB,
- bond->params.tlb_dynamic_lb))
+ READ_ONCE(bond->params.tlb_dynamic_lb)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_MISSED_MAX,
- bond->params.missed_max))
+ READ_ONCE(bond->params.missed_max)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_COUPLED_CONTROL,
- bond->params.coupled_control))
+ READ_ONCE(bond->params.coupled_control)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_BROADCAST_NEIGH,
- bond->params.broadcast_neighbor))
+ READ_ONCE(bond->params.broadcast_neighbor)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_BOND_LACP_STRICT,
- bond->params.lacp_strict))
+ READ_ONCE(bond->params.lacp_strict)))
goto nla_put_failure;
- if (BOND_MODE(bond) == BOND_MODE_8023AD) {
+ if (mode == BOND_MODE_8023AD) {
struct ad_info info;
if (capable(CAP_NET_ADMIN)) {
if (nla_put_u16(skb, IFLA_BOND_AD_ACTOR_SYS_PRIO,
- bond->params.ad_actor_sys_prio))
+ READ_ONCE(bond->params.ad_actor_sys_prio)))
goto nla_put_failure;
if (nla_put_u16(skb, IFLA_BOND_AD_USER_PORT_KEY,
- bond->params.ad_user_port_key))
+ READ_ONCE(bond->params.ad_user_port_key)))
goto nla_put_failure;
+ /* Small race here, this is a minor trade off. */
if (nla_put(skb, IFLA_BOND_AD_ACTOR_SYSTEM,
ETH_ALEN, &bond->params.ad_actor_system))
goto nla_put_failure;
}
- if (!bond_3ad_get_active_agg_info(bond, &info)) {
+ if (!__bond_3ad_get_active_agg_info(bond, &info)) {
struct nlattr *nest;
nest = nla_nest_start_noflag(skb, IFLA_BOND_AD_INFO);
@@ -898,9 +909,11 @@ static int bond_fill_info(struct sk_buff *skb,
}
}
+ rcu_read_unlock();
return 0;
nla_put_failure:
+ rcu_read_unlock();
return -EMSGSIZE;
}
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index e590c8dee86e..36b8d89387ee 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -934,14 +934,14 @@ static int bond_option_mode_set(struct bonding *bond,
/* don't cache arp_validate between modes */
WRITE_ONCE(bond->params.arp_validate, BOND_ARP_VALIDATE_NONE);
- bond->params.mode = newval->value;
+ WRITE_ONCE(bond->params.mode, newval->value);
/* When changing mode, the bond device is down, we may reduce
* the bond_bcast_neigh_enabled in bond_close() if broadcast_neighbor
* enabled in 8023ad mode. Therefore, only clear broadcast_neighbor
* to 0.
*/
- bond->params.broadcast_neighbor = 0;
+ WRITE_ONCE(bond->params.broadcast_neighbor, 0);
if (bond->dev->reg_state == NETREG_REGISTERED) {
bool update = false;
@@ -1706,7 +1706,7 @@ static int bond_option_lacp_strict_set(struct bonding *bond,
{
netdev_dbg(bond->dev, "Setting LACP fallback to %s (%llu)\n",
newval->string, newval->value);
- bond->params.lacp_strict = newval->value;
+ WRITE_ONCE(bond->params.lacp_strict, newval->value);
bond_3ad_set_carrier(bond);
return 0;
@@ -1927,7 +1927,7 @@ static int bond_option_broadcast_neigh_set(struct bonding *bond,
if (bond->params.broadcast_neighbor == newval->value)
return 0;
- bond->params.broadcast_neighbor = newval->value;
+ WRITE_ONCE(bond->params.broadcast_neighbor, newval->value);
if (bond->dev->flags & IFF_UP) {
if (bond->params.broadcast_neighbor)
static_branch_inc(&bond_bcast_neigh_enabled);
diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h
index cd27a7344e89..29cca7945df7 100644
--- a/drivers/net/dsa/b53/b53_priv.h
+++ b/drivers/net/dsa/b53/b53_priv.h
@@ -23,6 +23,7 @@
#include <linux/mutex.h>
#include <linux/phylink.h>
#include <linux/etherdevice.h>
+#include <linux/gpio/consumer.h>
#include <net/dsa.h>
#include "b53_regs.h"
@@ -467,7 +468,7 @@ static inline void b53_arl_search_read(struct b53_device *dev, u8 idx,
#ifdef CONFIG_BCM47XX
#include <linux/bcm47xx_nvram.h>
-#include <linux/gpio.h>
+#include <linux/gpio/legacy.h>
#include <bcm47xx_board.h>
static inline struct gpio_desc *b53_switch_get_reset_gpio(struct b53_device *dev)
{
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index 138f2ab0774e..586916570a84 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -18,7 +18,7 @@
#include <linux/delay.h>
#include <linux/dsa/ksz_common.h>
#include <linux/export.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/if_vlan.h>
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/net/dsa/qca/qca8k-leds.c b/drivers/net/dsa/qca/qca8k-leds.c
index ef496e345a4e..0ada28377f46 100644
--- a/drivers/net/dsa/qca/qca8k-leds.c
+++ b/drivers/net/dsa/qca/qca8k-leds.c
@@ -457,6 +457,9 @@ qca8k_setup_led_ctrl(struct qca8k_priv *priv)
int ret;
ports = device_get_named_child_node(priv->dev, "ports");
+ if (!ports)
+ ports = device_get_named_child_node(priv->dev, "ethernet-ports");
+
if (!ports) {
dev_info(priv->dev, "No ports node specified in device tree!");
return 0;
diff --git a/drivers/net/dsa/realtek/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c
index 103039fe3086..d2fa8ff6a5d0 100644
--- a/drivers/net/dsa/realtek/rtl8366rb.c
+++ b/drivers/net/dsa/realtek/rtl8366rb.c
@@ -791,11 +791,87 @@ static int rtl8366rb_setup_all_leds_off(struct realtek_priv *priv)
return ret;
}
+static int rtl8366rb_port_set_isolation(struct realtek_priv *priv, int port,
+ u32 mask)
+{
+ /* Bit 0 enables isolation so set this if we enable isolation
+ * any of the ports an clear it if we disable on all of them.
+ */
+ if (mask)
+ mask = RTL8366RB_PORT_ISO_PORTS(mask) | RTL8366RB_PORT_ISO_EN;
+
+ return regmap_write(priv->map, RTL8366RB_PORT_ISO(port),
+ mask);
+}
+
+static int rtl8366rb_port_add_isolation(struct realtek_priv *priv, int port,
+ u32 mask)
+{
+ /* We assume isolation bit is on */
+ return regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(port),
+ RTL8366RB_PORT_ISO_PORTS(mask),
+ RTL8366RB_PORT_ISO_PORTS(mask));
+}
+
+static int rtl8366rb_port_remove_isolation(struct realtek_priv *priv, int port,
+ u32 mask)
+{
+ return regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(port),
+ RTL8366RB_PORT_ISO_PORTS(mask), 0);
+}
+
+static void
+rtl8366rb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+{
+ struct realtek_priv *priv = ds->priv;
+ u32 val;
+ int i;
+
+ switch (state) {
+ case BR_STATE_DISABLED:
+ val = RTL8366RB_STP_STATE_DISABLED;
+ break;
+ case BR_STATE_BLOCKING:
+ case BR_STATE_LISTENING:
+ val = RTL8366RB_STP_STATE_BLOCKING;
+ break;
+ case BR_STATE_LEARNING:
+ val = RTL8366RB_STP_STATE_LEARNING;
+ break;
+ case BR_STATE_FORWARDING:
+ val = RTL8366RB_STP_STATE_FORWARDING;
+ break;
+ default:
+ dev_err(priv->dev, "unknown bridge state requested\n");
+ return;
+ }
+
+ /* Set the same status for the port on all the FIDs */
+ for (i = 0; i < RTL8366RB_NUM_FIDS; i++) {
+ regmap_update_bits(priv->map, RTL8366RB_STP_STATE_BASE + i,
+ RTL8366RB_STP_STATE_MASK(port),
+ RTL8366RB_STP_STATE(port, val));
+ }
+}
+
+static int rtl8366rb_port_set_learning(struct realtek_priv *priv, int port,
+ bool enable)
+{
+ /* Notice inverted semantics in this register: setting a bit disables
+ * learning instead of enabling it.
+ */
+ return regmap_update_bits(priv->map, RTL8366RB_PORT_LEARNDIS_CTRL,
+ BIT(port), enable ? 0 : BIT(port));
+}
+
static int rtl8366rb_setup(struct dsa_switch *ds)
{
struct realtek_priv *priv = ds->priv;
const struct rtl8366rb_jam_tbl_entry *jam_table;
+ u32 downports_mask = 0;
struct rtl8366rb *rb;
+ u32 upports_mask = 0;
+ struct dsa_port *dp;
u32 chip_ver = 0;
u32 chip_id = 0;
int jam_size;
@@ -866,20 +942,58 @@ static int rtl8366rb_setup(struct dsa_switch *ds)
if (ret)
return ret;
- /* Isolate all user ports so they can only send packets to itself and the CPU port */
- for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) {
- ret = regmap_write(priv->map, RTL8366RB_PORT_ISO(i),
- RTL8366RB_PORT_ISO_PORTS(BIT(RTL8366RB_PORT_NUM_CPU)) |
- RTL8366RB_PORT_ISO_EN);
+ /* Start with all ports blocked, including unused ports */
+ dsa_switch_for_each_port(dp, ds) {
+ /* Set the initial STP state of all ports to DISABLED, otherwise
+ * ports will still forward frames to the CPU despite being
+ * administratively down by default.
+ */
+ rtl8366rb_port_stp_state_set(ds, dp->index, BR_STATE_DISABLED);
+
+ /* Start with all ports completely isolated */
+ ret = rtl8366rb_port_set_isolation(priv, dp->index, 0);
+ if (ret)
+ return ret;
+
+ /* Disable learning */
+ ret = rtl8366rb_port_set_learning(priv, dp->index, false);
+ if (ret)
+ return ret;
+
+ /* Collect CPU ports. If we support cascade switches, it should
+ * also include the upstream DSA ports.
+ */
+ if (!dsa_port_is_cpu(dp))
+ continue;
+
+ upports_mask |= BIT(dp->index);
+ }
+
+ /* Configure user ports */
+ dsa_switch_for_each_port(dp, ds) {
+ if (!dsa_port_is_user(dp))
+ continue;
+
+ /* Forward only to the CPU */
+ ret = rtl8366rb_port_set_isolation(priv, dp->index, upports_mask);
+ if (ret)
+ return ret;
+
+ /* If we support cascade switches, it should also include the
+ * downstream DSA ports.
+ */
+ downports_mask |= BIT(dp->index);
+ }
+
+ /* Configure CPU ports. If we support cascade switches, this will also
+ * include DSA ports.
+ */
+ dsa_switch_for_each_cpu_port(dp, ds) {
+ /* Forward to all user ports */
+ ret = rtl8366rb_port_set_isolation(priv, dp->index, downports_mask);
if (ret)
return ret;
}
- /* CPU port can send packets to all ports */
- ret = regmap_write(priv->map, RTL8366RB_PORT_ISO(RTL8366RB_PORT_NUM_CPU),
- RTL8366RB_PORT_ISO_PORTS(dsa_user_ports(ds)) |
- RTL8366RB_PORT_ISO_EN);
- if (ret)
- return ret;
/* Set up the "green ethernet" feature */
ret = rtl8366rb_jam_table(rtl8366rb_green_jam,
@@ -938,12 +1052,6 @@ static int rtl8366rb_setup(struct dsa_switch *ds)
rb->max_mtu[i] = ETH_DATA_LEN;
}
- /* Disable learning for all ports */
- ret = regmap_write(priv->map, RTL8366RB_PORT_LEARNDIS_CTRL,
- RTL8366RB_PORT_ALL);
- if (ret)
- return ret;
-
/* Enable auto ageing for all ports */
ret = regmap_write(priv->map, RTL8366RB_SECURITY_CTRL, 0);
if (ret)
@@ -1184,70 +1292,6 @@ rtl8366rb_port_disable(struct dsa_switch *ds, int port)
return;
}
-static int
-rtl8366rb_port_bridge_join(struct dsa_switch *ds, int port,
- struct dsa_bridge bridge,
- bool *tx_fwd_offload,
- struct netlink_ext_ack *extack)
-{
- struct realtek_priv *priv = ds->priv;
- unsigned int port_bitmap = 0;
- int ret, i;
-
- /* Loop over all other ports than the current one */
- for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) {
- /* Current port handled last */
- if (i == port)
- continue;
- /* Not on this bridge */
- if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
- continue;
- /* Join this port to each other port on the bridge */
- ret = regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(i),
- RTL8366RB_PORT_ISO_PORTS(BIT(port)),
- RTL8366RB_PORT_ISO_PORTS(BIT(port)));
- if (ret)
- dev_err(priv->dev, "failed to join port %d\n", port);
-
- port_bitmap |= BIT(i);
- }
-
- /* Set the bits for the ports we can access */
- return regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(port),
- RTL8366RB_PORT_ISO_PORTS(port_bitmap),
- RTL8366RB_PORT_ISO_PORTS(port_bitmap));
-}
-
-static void
-rtl8366rb_port_bridge_leave(struct dsa_switch *ds, int port,
- struct dsa_bridge bridge)
-{
- struct realtek_priv *priv = ds->priv;
- unsigned int port_bitmap = 0;
- int ret, i;
-
- /* Loop over all other ports than this one */
- for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) {
- /* Current port handled last */
- if (i == port)
- continue;
- /* Not on this bridge */
- if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
- continue;
- /* Remove this port from any other port on the bridge */
- ret = regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(i),
- RTL8366RB_PORT_ISO_PORTS(BIT(port)), 0);
- if (ret)
- dev_err(priv->dev, "failed to leave port %d\n", port);
-
- port_bitmap |= BIT(i);
- }
-
- /* Clear the bits for the ports we can not access, leave ourselves */
- regmap_update_bits(priv->map, RTL8366RB_PORT_ISO(port),
- RTL8366RB_PORT_ISO_PORTS(port_bitmap), 0);
-}
-
/**
* rtl8366rb_drop_untagged() - make the switch drop untagged and C-tagged frames
* @priv: SMI state container
@@ -1306,59 +1350,6 @@ rtl8366rb_port_pre_bridge_flags(struct dsa_switch *ds, int port,
return 0;
}
-static int
-rtl8366rb_port_bridge_flags(struct dsa_switch *ds, int port,
- struct switchdev_brport_flags flags,
- struct netlink_ext_ack *extack)
-{
- struct realtek_priv *priv = ds->priv;
- int ret;
-
- if (flags.mask & BR_LEARNING) {
- ret = regmap_update_bits(priv->map, RTL8366RB_PORT_LEARNDIS_CTRL,
- BIT(port),
- (flags.val & BR_LEARNING) ? 0 : BIT(port));
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static void
-rtl8366rb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
-{
- struct realtek_priv *priv = ds->priv;
- u32 val;
- int i;
-
- switch (state) {
- case BR_STATE_DISABLED:
- val = RTL8366RB_STP_STATE_DISABLED;
- break;
- case BR_STATE_BLOCKING:
- case BR_STATE_LISTENING:
- val = RTL8366RB_STP_STATE_BLOCKING;
- break;
- case BR_STATE_LEARNING:
- val = RTL8366RB_STP_STATE_LEARNING;
- break;
- case BR_STATE_FORWARDING:
- val = RTL8366RB_STP_STATE_FORWARDING;
- break;
- default:
- dev_err(priv->dev, "unknown bridge state requested\n");
- return;
- }
-
- /* Set the same status for the port on all the FIDs */
- for (i = 0; i < RTL8366RB_NUM_FIDS; i++) {
- regmap_update_bits(priv->map, RTL8366RB_STP_STATE_BASE + i,
- RTL8366RB_STP_STATE_MASK(port),
- RTL8366RB_STP_STATE(port, val));
- }
-}
-
static void
rtl8366rb_port_fast_age(struct dsa_switch *ds, int port)
{
@@ -1801,15 +1792,15 @@ static const struct dsa_switch_ops rtl8366rb_switch_ops = {
.get_strings = rtl8366_get_strings,
.get_ethtool_stats = rtl8366_get_ethtool_stats,
.get_sset_count = rtl8366_get_sset_count,
- .port_bridge_join = rtl8366rb_port_bridge_join,
- .port_bridge_leave = rtl8366rb_port_bridge_leave,
+ .port_bridge_join = rtl83xx_port_bridge_join,
+ .port_bridge_leave = rtl83xx_port_bridge_leave,
.port_vlan_filtering = rtl8366rb_vlan_filtering,
.port_vlan_add = rtl8366_vlan_add,
.port_vlan_del = rtl8366_vlan_del,
.port_enable = rtl8366rb_port_enable,
.port_disable = rtl8366rb_port_disable,
.port_pre_bridge_flags = rtl8366rb_port_pre_bridge_flags,
- .port_bridge_flags = rtl8366rb_port_bridge_flags,
+ .port_bridge_flags = rtl83xx_port_bridge_flags,
.port_stp_state_set = rtl8366rb_port_stp_state_set,
.port_fast_age = rtl8366rb_port_fast_age,
.port_change_mtu = rtl8366rb_change_mtu,
@@ -1830,6 +1821,9 @@ static const struct realtek_ops rtl8366rb_ops = {
.is_vlan_valid = rtl8366rb_is_vlan_valid,
.enable_vlan = rtl8366rb_enable_vlan,
.enable_vlan4k = rtl8366rb_enable_vlan4k,
+ .port_add_isolation = rtl8366rb_port_add_isolation,
+ .port_remove_isolation = rtl8366rb_port_remove_isolation,
+ .port_set_learning = rtl8366rb_port_set_learning,
.phy_read = rtl8366rb_phy_read,
.phy_write = rtl8366rb_phy_write,
};
diff --git a/drivers/net/dsa/realtek/rtl83xx.c b/drivers/net/dsa/realtek/rtl83xx.c
index 71124ecca92f..90843d52c5a8 100644
--- a/drivers/net/dsa/realtek/rtl83xx.c
+++ b/drivers/net/dsa/realtek/rtl83xx.c
@@ -356,9 +356,6 @@ int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port,
if (!priv->ops->port_add_isolation)
return -EOPNOTSUPP;
- if (!priv->ops->port_set_learning)
- return -EOPNOTSUPP;
-
dev_dbg(priv->dev, "bridge %d join port %d\n", bridge.num, port);
/* Add this port to the isolation group of every other port
@@ -396,9 +393,11 @@ int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port,
goto undo_self_isolation;
}
- ret = priv->ops->port_set_learning(priv, port, true);
- if (ret)
- goto undo_efid;
+ if (priv->ops->port_set_learning) {
+ ret = priv->ops->port_set_learning(priv, port, true);
+ if (ret)
+ goto undo_efid;
+ }
return 0;
@@ -443,9 +442,6 @@ void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port,
if (!priv->ops->port_remove_isolation)
return;
- if (!priv->ops->port_set_learning)
- return;
-
dev_dbg(priv->dev, "bridge %d leave port %d\n", bridge.num, port);
/* Remove this port from the isolation group of every other
@@ -474,11 +470,13 @@ void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port,
* downstream DSA ports from the isolation group.
*/
- ret = priv->ops->port_set_learning(priv, port, false);
- if (ret)
- dev_err(priv->dev,
- "failed to disable learning on port %d: %pe\n",
- port, ERR_PTR(ret));
+ if (priv->ops->port_set_learning) {
+ ret = priv->ops->port_set_learning(priv, port, false);
+ if (ret)
+ dev_err(priv->dev,
+ "failed to disable learning on port %d: %pe\n",
+ port, ERR_PTR(ret));
+ }
/* Remove those ports from the isolation group of this port */
ret = priv->ops->port_remove_isolation(priv, port, mask);
diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c
index fc7341a5cbb7..42174249ef61 100644
--- a/drivers/net/ethernet/allwinner/sun4i-emac.c
+++ b/drivers/net/ethernet/allwinner/sun4i-emac.c
@@ -15,7 +15,7 @@
#include <linux/clk.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mii.h>
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
index 3b2951030a38..507db46daf2b 100644
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
@@ -7,7 +7,7 @@
* Keyur Chudgar <kchudgar@apm.com>
*/
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include "xgene_enet_main.h"
#include "xgene_enet_hw.h"
#include "xgene_enet_sgmac.h"
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
index 858ba844ac51..8320b26c3f72 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
@@ -25,6 +25,9 @@
#define DEFAULT_VLAN_ID 1
+static struct notifier_block dpaa2_switch_port_switchdev_nb;
+static struct notifier_block dpaa2_switch_port_switchdev_blocking_nb;
+
static u16 dpaa2_switch_port_get_fdb_id(struct ethsw_port_priv *port_priv)
{
return port_priv->fdb->fdb_id;
@@ -51,6 +54,17 @@ dpaa2_switch_filter_block_get_unused(struct ethsw_core *ethsw)
return NULL;
}
+static struct dpaa2_switch_lag *
+dpaa2_switch_lag_get_unused(struct ethsw_core *ethsw)
+{
+ int i;
+
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
+ if (!ethsw->lags[i].in_use)
+ return &ethsw->lags[i];
+ return NULL;
+}
+
static bool dpaa2_switch_fdb_in_use_by_others(struct ethsw_core *ethsw,
struct dpaa2_switch_fdb *fdb,
struct ethsw_port_priv *except)
@@ -71,9 +85,9 @@ static struct dpaa2_switch_fdb *
dpaa2_switch_fdb_for_join(struct ethsw_port_priv *port_priv,
struct net_device *upper_dev)
{
- struct ethsw_port_priv *other_port_priv;
- struct net_device *other_dev;
- struct list_head *iter;
+ struct ethsw_port_priv *other_port_priv = NULL;
+ struct net_device *other_dev, *other_dev2;
+ struct list_head *iter, *iter2;
/* The below call to netdev_for_each_lower_dev() demands the RTNL lock
* being held. Assert on it so that it's easier to catch new code
@@ -82,17 +96,32 @@ dpaa2_switch_fdb_for_join(struct ethsw_port_priv *port_priv,
ASSERT_RTNL();
/* If part of a bridge, use the FDB of the first dpaa2 switch interface
- * to be present in that bridge
+ * to be present in that bridge. The search descends one level through
+ * a bridged bond's lowers as well.
*/
netdev_for_each_lower_dev(upper_dev, other_dev, iter) {
- if (!dpaa2_switch_port_dev_check(other_dev))
- continue;
+ if (netif_is_lag_master(other_dev)) {
+ netdev_for_each_lower_dev(other_dev, other_dev2, iter2) {
+ if (!dpaa2_switch_port_dev_check(other_dev2))
+ continue;
- if (other_dev == port_priv->netdev)
- continue;
+ if (other_dev2 == port_priv->netdev)
+ continue;
- other_port_priv = netdev_priv(other_dev);
- return other_port_priv->fdb;
+ other_port_priv = netdev_priv(other_dev2);
+ break;
+ }
+ } else {
+ if (!dpaa2_switch_port_dev_check(other_dev))
+ continue;
+
+ if (other_dev == port_priv->netdev)
+ continue;
+
+ other_port_priv = netdev_priv(other_dev);
+ }
+ if (other_port_priv)
+ return other_port_priv->fdb;
}
return port_priv->fdb;
@@ -537,6 +566,103 @@ static int dpaa2_switch_port_fdb_del_mc(struct ethsw_port_priv *port_priv,
return err;
}
+static int dpaa2_switch_port_fdb_add(struct ethsw_port_priv *port_priv,
+ const unsigned char *addr)
+{
+ int err;
+
+ if (is_unicast_ether_addr(addr))
+ err = dpaa2_switch_port_fdb_add_uc(port_priv, addr);
+ else
+ err = dpaa2_switch_port_fdb_add_mc(port_priv, addr);
+
+ return err;
+}
+
+static int dpaa2_switch_port_fdb_del(struct ethsw_port_priv *port_priv,
+ const unsigned char *addr)
+{
+ if (is_unicast_ether_addr(addr))
+ return dpaa2_switch_port_fdb_del_uc(port_priv, addr);
+ else
+ return dpaa2_switch_port_fdb_del_mc(port_priv, addr);
+}
+
+static struct dpaa2_mac_addr *
+dpaa2_switch_mac_addr_find(struct list_head *addr_list,
+ const unsigned char *addr, u16 vid)
+{
+ struct dpaa2_mac_addr *a;
+
+ list_for_each_entry(a, addr_list, list)
+ if (ether_addr_equal(a->addr, addr) && a->vid == vid)
+ return a;
+
+ return NULL;
+}
+
+static int dpaa2_switch_lag_fdb_add(struct dpaa2_switch_lag *lag,
+ const unsigned char *addr, u16 vid)
+{
+ struct ethsw_port_priv *port_priv = lag->primary;
+ struct dpaa2_mac_addr *a;
+ int err = 0;
+
+ mutex_lock(&lag->fdb_lock);
+
+ a = dpaa2_switch_mac_addr_find(&lag->fdbs, addr, vid);
+ if (a) {
+ refcount_inc(&a->refcount);
+ goto out;
+ }
+
+ a = kzalloc(sizeof(*a), GFP_KERNEL);
+ if (!a) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = dpaa2_switch_port_fdb_add(port_priv, addr);
+ if (err) {
+ kfree(a);
+ goto out;
+ }
+
+ ether_addr_copy(a->addr, addr);
+ a->vid = vid;
+ refcount_set(&a->refcount, 1);
+ list_add_tail(&a->list, &lag->fdbs);
+
+out:
+ mutex_unlock(&lag->fdb_lock);
+
+ return err;
+}
+
+static void dpaa2_switch_lag_fdb_del(struct dpaa2_switch_lag *lag,
+ const unsigned char *addr, u16 vid)
+{
+ struct ethsw_port_priv *port_priv = lag->primary;
+ struct dpaa2_mac_addr *a;
+
+ mutex_lock(&lag->fdb_lock);
+
+ a = dpaa2_switch_mac_addr_find(&lag->fdbs, addr, vid);
+ if (!a)
+ goto out;
+
+ if (!refcount_dec_and_test(&a->refcount))
+ goto out;
+
+ list_del(&a->list);
+ kfree(a);
+
+ dpaa2_switch_port_fdb_del(port_priv, addr);
+
+out:
+ mutex_unlock(&lag->fdb_lock);
+}
+
static void dpaa2_switch_port_get_stats(struct net_device *netdev,
struct rtnl_link_stats64 *stats)
{
@@ -1485,6 +1611,33 @@ bool dpaa2_switch_port_dev_check(const struct net_device *netdev)
return netdev->netdev_ops == &dpaa2_switch_port_ops;
}
+static bool dpaa2_switch_foreign_dev_check(const struct net_device *dev,
+ const struct net_device *foreign_dev)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(dev);
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct ethsw_port_priv *other_port;
+ int i;
+
+ if (netif_is_bridge_master(foreign_dev))
+ if (port_priv->fdb->bridge_dev == foreign_dev)
+ return false;
+
+ if (netif_is_bridge_port(foreign_dev)) {
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ other_port = ethsw->ports[i];
+
+ if (!other_port)
+ continue;
+ if (dpaa2_switch_port_offloads_bridge_port(other_port,
+ foreign_dev))
+ return false;
+ }
+ }
+
+ return true;
+}
+
static int dpaa2_switch_port_connect_mac(struct ethsw_port_priv *port_priv)
{
struct fsl_mc_device *dpsw_port_dev, *dpmac_dev;
@@ -1860,51 +2013,32 @@ int dpaa2_switch_port_vlans_add(struct net_device *netdev,
vlan->changed);
}
-static int dpaa2_switch_port_lookup_address(struct net_device *netdev, int is_uc,
- const unsigned char *addr)
-{
- struct netdev_hw_addr_list *list = (is_uc) ? &netdev->uc : &netdev->mc;
- struct netdev_hw_addr *ha;
-
- netif_addr_lock_bh(netdev);
- list_for_each_entry(ha, &list->list, list) {
- if (ether_addr_equal(ha->addr, addr)) {
- netif_addr_unlock_bh(netdev);
- return 1;
- }
- }
- netif_addr_unlock_bh(netdev);
- return 0;
-}
-
static int dpaa2_switch_port_mdb_add(struct net_device *netdev,
const struct switchdev_obj_port_mdb *mdb)
{
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
- int err;
+ struct dpaa2_switch_lag *lag;
- /* Check if address is already set on this port */
- if (dpaa2_switch_port_lookup_address(netdev, 0, mdb->addr))
- return -EEXIST;
-
- err = dpaa2_switch_port_fdb_add_mc(port_priv, mdb->addr);
- if (err)
- return err;
-
- err = dev_mc_add(netdev, mdb->addr);
- if (err) {
- netdev_err(netdev, "dev_mc_add err %d\n", err);
- dpaa2_switch_port_fdb_del_mc(port_priv, mdb->addr);
- }
-
- return err;
+ lag = rtnl_dereference(port_priv->lag);
+ if (lag)
+ return dpaa2_switch_lag_fdb_add(lag, mdb->addr, mdb->vid);
+ else
+ return dpaa2_switch_port_fdb_add(port_priv, mdb->addr);
}
-static int dpaa2_switch_port_obj_add(struct net_device *netdev,
- const struct switchdev_obj *obj)
+static int dpaa2_switch_port_obj_add(struct net_device *netdev, const void *ctx,
+ const struct switchdev_obj *obj,
+ struct netlink_ext_ack *extack)
{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
int err;
+ if (ctx && ctx != port_priv)
+ return 0;
+
+ if (!dpaa2_switch_port_offloads_bridge_port(port_priv, obj->orig_dev))
+ return -EOPNOTSUPP;
+
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
err = dpaa2_switch_port_vlans_add(netdev,
@@ -2000,29 +2134,29 @@ static int dpaa2_switch_port_mdb_del(struct net_device *netdev,
const struct switchdev_obj_port_mdb *mdb)
{
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
- int err;
-
- if (!dpaa2_switch_port_lookup_address(netdev, 0, mdb->addr))
- return -ENOENT;
-
- err = dpaa2_switch_port_fdb_del_mc(port_priv, mdb->addr);
- if (err)
- return err;
+ struct dpaa2_switch_lag *lag;
- err = dev_mc_del(netdev, mdb->addr);
- if (err) {
- netdev_err(netdev, "dev_mc_del err %d\n", err);
- return err;
- }
+ lag = rtnl_dereference(port_priv->lag);
+ if (lag)
+ dpaa2_switch_lag_fdb_del(lag, mdb->addr, mdb->vid);
+ else
+ dpaa2_switch_port_fdb_del(port_priv, mdb->addr);
- return err;
+ return 0;
}
-static int dpaa2_switch_port_obj_del(struct net_device *netdev,
+static int dpaa2_switch_port_obj_del(struct net_device *netdev, const void *ctx,
const struct switchdev_obj *obj)
{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
int err;
+ if (ctx && ctx != port_priv)
+ return 0;
+
+ if (!dpaa2_switch_port_offloads_bridge_port(port_priv, obj->orig_dev))
+ return -EOPNOTSUPP;
+
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
err = dpaa2_switch_port_vlans_del(netdev, SWITCHDEV_OBJ_PORT_VLAN(obj));
@@ -2048,6 +2182,21 @@ static int dpaa2_switch_port_attr_set_event(struct net_device *netdev,
return notifier_from_errno(err);
}
+static struct net_device *
+dpaa2_switch_port_to_bridge_port(struct ethsw_port_priv *port_priv)
+{
+ struct dpaa2_switch_lag *lag;
+
+ if (!port_priv->fdb->bridge_dev)
+ return NULL;
+
+ lag = rtnl_dereference(port_priv->lag);
+ if (lag)
+ return lag->bond_dev;
+
+ return port_priv->netdev;
+}
+
static int dpaa2_switch_port_bridge_join(struct net_device *netdev,
struct net_device *upper_dev,
struct netlink_ext_ack *extack)
@@ -2055,6 +2204,7 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev,
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
struct dpaa2_switch_fdb *old_fdb = port_priv->fdb;
struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct net_device *brport_dev;
bool learn_ena;
int err;
@@ -2066,7 +2216,8 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev,
dpaa2_switch_port_set_fdb(port_priv, upper_dev, true);
/* Inherit the initial bridge port learning state */
- learn_ena = br_port_flag_is_set(netdev, BR_LEARNING);
+ brport_dev = dpaa2_switch_port_to_bridge_port(port_priv);
+ learn_ena = br_port_flag_is_set(brport_dev, BR_LEARNING);
err = dpaa2_switch_port_set_learning(port_priv, learn_ena);
port_priv->learn_ena = learn_ena;
@@ -2080,8 +2231,11 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev,
if (err)
goto err_egress_flood;
- err = switchdev_bridge_port_offload(netdev, netdev, NULL,
- NULL, NULL, false, extack);
+ brport_dev = dpaa2_switch_port_to_bridge_port(port_priv);
+ err = switchdev_bridge_port_offload(brport_dev, netdev, port_priv,
+ &dpaa2_switch_port_switchdev_nb,
+ &dpaa2_switch_port_switchdev_blocking_nb,
+ false, extack);
if (err)
goto err_switchdev_offload;
@@ -2115,7 +2269,22 @@ static int dpaa2_switch_port_restore_rxvlan(struct net_device *vdev, int vid, vo
static void dpaa2_switch_port_pre_bridge_leave(struct net_device *netdev)
{
- switchdev_bridge_port_unoffload(netdev, NULL, NULL, NULL);
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct net_device *brport_dev;
+
+ brport_dev = dpaa2_switch_port_to_bridge_port(port_priv);
+ if (!brport_dev)
+ return;
+
+ switchdev_bridge_port_unoffload(brport_dev, port_priv,
+ &dpaa2_switch_port_switchdev_nb,
+ &dpaa2_switch_port_switchdev_blocking_nb);
+
+ /* Make sure that any FDB add/del operations are completed before the
+ * bridge layout changes
+ */
+ flush_workqueue(ethsw->workqueue);
}
static int dpaa2_switch_port_bridge_leave(struct net_device *netdev)
@@ -2177,30 +2346,53 @@ static int dpaa2_switch_port_bridge_leave(struct net_device *netdev)
false);
}
+static int
+dpaa2_switch_have_vlan_upper(struct net_device *upper_dev,
+ __always_unused struct netdev_nested_priv *priv)
+{
+ return is_vlan_dev(upper_dev);
+}
+
static int dpaa2_switch_prevent_bridging_with_8021q_upper(struct net_device *netdev)
{
- struct net_device *upper_dev;
- struct list_head *iter;
+ struct netdev_nested_priv priv = {};
/* RCU read lock not necessary because we have write-side protection
- * (rtnl_mutex), however a non-rcu iterator does not exist.
+ * (rtnl_mutex), however a non-rcu iterator does not exist. Walk the
+ * entire upper chain so that a VLAN device stacked on a intermediate
+ * bond is caught too.
*/
- netdev_for_each_upper_dev_rcu(netdev, upper_dev, iter)
- if (is_vlan_dev(upper_dev))
- return -EOPNOTSUPP;
+ if (netdev_walk_all_upper_dev_rcu(netdev, dpaa2_switch_have_vlan_upper,
+ &priv))
+ return -EOPNOTSUPP;
return 0;
}
+static int dpaa2_switch_check_dpsw_instance(struct net_device *dev,
+ struct netdev_nested_priv *priv)
+{
+ struct ethsw_port_priv *port_priv = (struct ethsw_port_priv *)priv->data;
+ struct ethsw_port_priv *other_priv = netdev_priv(dev);
+
+ if (!dpaa2_switch_port_dev_check(dev))
+ return 0;
+
+ if (other_priv->ethsw_data == port_priv->ethsw_data)
+ return 0;
+
+ return 1;
+}
+
static int
dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev,
struct net_device *upper_dev,
struct netlink_ext_ack *extack)
{
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
- struct ethsw_port_priv *other_port_priv;
- struct net_device *other_dev;
- struct list_head *iter;
+ struct netdev_nested_priv data = {
+ .data = (void *)port_priv,
+ };
int err;
if (!br_vlan_enabled(upper_dev)) {
@@ -2215,6 +2407,70 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev,
return err;
}
+ err = netdev_walk_all_lower_dev(upper_dev,
+ dpaa2_switch_check_dpsw_instance,
+ &data);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Interface from a different DPSW is in the bridge already");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dpaa2_switch_pre_lag_join(struct net_device *netdev,
+ struct net_device *upper_dev,
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct ethsw_port_priv *other_port_priv;
+ struct dpaa2_switch_lag *lag = NULL;
+ struct dpsw_lag_cfg cfg = {0};
+ struct net_device *other_dev;
+ int i, num_ifs = 0, err;
+ struct list_head *iter;
+
+ if (!(ethsw->features & ETHSW_FEATURE_LAG_OFFLOAD)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "LAG offload is supported only for DPSW >= v8.13");
+ return -EOPNOTSUPP;
+ }
+
+ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Can only offload LAG using hash TX type");
+ return -EOPNOTSUPP;
+ }
+
+ if (info->hash_type != NETDEV_LAG_HASH_L23) {
+ NL_SET_ERR_MSG_MOD(extack, "Can only offload L2+L3 Tx hash");
+ return -EOPNOTSUPP;
+ }
+
+ if (!dpaa2_switch_port_has_mac(port_priv)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Only switch interfaces connected to MACs can be under a LAG");
+ return -EINVAL;
+ }
+
+ if (vlan_uses_dev(upper_dev)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Cannot join a LAG upper that has a VLAN");
+ return -EOPNOTSUPP;
+ }
+
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ if (!ethsw->lags[i].in_use)
+ continue;
+ if (ethsw->lags[i].bond_dev != upper_dev)
+ continue;
+ lag = &ethsw->lags[i];
+ break;
+ }
+
netdev_for_each_lower_dev(upper_dev, other_dev, iter) {
if (!dpaa2_switch_port_dev_check(other_dev))
continue;
@@ -2222,9 +2478,248 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev,
other_port_priv = netdev_priv(other_dev);
if (other_port_priv->ethsw_data != port_priv->ethsw_data) {
NL_SET_ERR_MSG_MOD(extack,
- "Interface from a different DPSW is in the bridge already");
+ "Interface from a different DPSW is in the bond already");
return -EINVAL;
}
+
+ cfg.if_id[num_ifs++] = other_port_priv->idx;
+
+ if (num_ifs >= DPSW_MAX_LAG_IFS) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Cannot add more than 8 DPAA2 switch ports under the same bond");
+ return -EINVAL;
+ }
+ }
+
+ if (lag) {
+ cfg.group_id = lag->id;
+ cfg.if_id[num_ifs++] = port_priv->idx;
+ cfg.num_ifs = num_ifs;
+ cfg.phase = DPSW_LAG_SET_PHASE_CHECK;
+
+ err = dpsw_lag_set(ethsw->mc_io, 0, ethsw->dpsw_handle, &cfg);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Cannot offload LAG configuration");
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+}
+
+static void dpaa2_switch_port_set_lag_group(struct ethsw_port_priv *port_priv,
+ struct net_device *bond_dev)
+{
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct ethsw_port_priv *other_port_priv = NULL;
+ struct dpaa2_switch_lag *lag = NULL;
+ struct dpaa2_switch_lag *other_lag;
+ struct net_device *other_dev;
+ struct list_head *iter;
+
+ netdev_for_each_lower_dev(bond_dev, other_dev, iter) {
+ if (!dpaa2_switch_port_dev_check(other_dev))
+ continue;
+
+ other_port_priv = netdev_priv(other_dev);
+ other_lag = rtnl_dereference(other_port_priv->lag);
+ if (!other_lag)
+ continue;
+
+ if (other_lag->bond_dev == bond_dev) {
+ rcu_assign_pointer(port_priv->lag, other_lag);
+ return;
+ }
+ }
+
+ /* This is the first interface to be added under a bond device. Find an
+ * unused LAG group. No need to check for NULL since there are the same
+ * amount of DPSW ports as LAG groups, meaning that each port can have
+ * its own LAG group.
+ */
+ lag = dpaa2_switch_lag_get_unused(ethsw);
+ lag->in_use = true;
+ lag->bond_dev = bond_dev;
+ lag->primary = port_priv;
+ rcu_assign_pointer(port_priv->lag, lag);
+}
+
+static bool dpaa2_switch_port_in_lag(struct ethsw_port_priv *port_priv,
+ struct net_device *bond_dev)
+{
+ struct dpaa2_switch_lag *lag;
+
+ if (!port_priv)
+ return false;
+
+ lag = rtnl_dereference(port_priv->lag);
+ return lag && lag->bond_dev == bond_dev;
+}
+
+static int dpaa2_switch_set_lag_cfg(struct net_device *bond_dev, u8 lag_id,
+ struct ethsw_core *ethsw)
+{
+ struct dpaa2_switch_lag *lag = &ethsw->lags[lag_id - 1];
+ struct ethsw_port_priv *primary, *port_priv;
+ struct ethsw_port_priv *new_primary = NULL;
+ struct dpsw_lag_cfg cfg = {0};
+ struct dpaa2_mac_addr *a;
+ u8 num_ifs = 0;
+ int err, i;
+
+ cfg.group_id = lag_id;
+
+ /* Determine the primary port. The caller clears ->lag on the port that
+ * is leaving, so a NULL ->lag on the current primary means it is the
+ * one leaving: elect the first remaining member as the new primary.
+ * Otherwise keep the current primary.
+ */
+ if (rtnl_dereference(lag->primary->lag)) {
+ primary = lag->primary;
+ } else {
+ primary = NULL;
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ if (dpaa2_switch_port_in_lag(ethsw->ports[i], bond_dev)) {
+ new_primary = ethsw->ports[i];
+ primary = new_primary;
+ break;
+ }
+ }
+ }
+
+ /* Build the interface list, always placing the primary first */
+ if (primary)
+ cfg.if_id[num_ifs++] = primary->idx;
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ port_priv = ethsw->ports[i];
+ if (port_priv == primary)
+ continue;
+ if (!dpaa2_switch_port_in_lag(port_priv, bond_dev))
+ continue;
+
+ cfg.if_id[num_ifs++] = port_priv->idx;
+ }
+ cfg.num_ifs = num_ifs;
+
+ /* No more interfaces under this LAG group, mark it as not in use. Wait
+ * for a grace period so that any readers of the lag structure finished.
+ */
+ if (!num_ifs) {
+ synchronize_net();
+
+ lag->bond_dev = NULL;
+ lag->primary = NULL;
+ lag->in_use = false;
+ }
+
+ /* When the primary changes, migrate the FDB entries from the old
+ * primary to the new one: remove them before reconfiguring the LAG in
+ * hardware and re-add them on the new primary afterwards. We do not
+ * touch any refcounting since the intention is to change the HW entry,
+ * not the parallel software tracking.
+ */
+ if (new_primary) {
+ mutex_lock(&lag->fdb_lock);
+ list_for_each_entry(a, &lag->fdbs, list)
+ dpaa2_switch_port_fdb_del(lag->primary, a->addr);
+ mutex_unlock(&lag->fdb_lock);
+ }
+
+ err = dpsw_lag_set(ethsw->mc_io, 0, ethsw->dpsw_handle, &cfg);
+ if (err)
+ return err;
+
+ if (new_primary) {
+ mutex_lock(&lag->fdb_lock);
+ list_for_each_entry(a, &lag->fdbs, list) {
+ err = dpaa2_switch_port_fdb_add(new_primary, a->addr);
+ if (err)
+ netdev_err(new_primary->netdev, "Unable to migrate FDB\n");
+ }
+ mutex_unlock(&lag->fdb_lock);
+
+ synchronize_net();
+ lag->primary = new_primary;
+ }
+
+ return 0;
+}
+
+static int dpaa2_switch_port_bond_join(struct net_device *netdev,
+ struct net_device *bond_dev,
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct net_device *bridge_dev;
+ struct dpaa2_switch_lag *lag;
+ int err = 0;
+ u8 lag_id;
+
+ /* Setup the port_priv->lag pointer for this switch port */
+ dpaa2_switch_port_set_lag_group(port_priv, bond_dev);
+
+ /* Create the LAG configuration and apply it in MC */
+ lag = rtnl_dereference(port_priv->lag);
+ lag_id = lag->id;
+ err = dpaa2_switch_set_lag_cfg(bond_dev, lag_id, ethsw);
+ if (err)
+ goto err_lag_cfg;
+
+ /* If the bond device is a switch port, join the bridge as well */
+ bridge_dev = netdev_master_upper_dev_get(bond_dev);
+ if (!bridge_dev || !netif_is_bridge_master(bridge_dev))
+ return 0;
+
+ err = dpaa2_switch_port_bridge_join(netdev, bridge_dev, extack);
+ if (err)
+ goto err_lag_cfg;
+
+ return err;
+
+err_lag_cfg:
+ rcu_assign_pointer(port_priv->lag, NULL);
+ dpaa2_switch_set_lag_cfg(bond_dev, lag_id, ethsw);
+
+ return err;
+}
+
+static int dpaa2_switch_port_bond_leave(struct net_device *netdev,
+ struct net_device *bond_dev)
+{
+ struct net_device *bridge_dev = netdev_master_upper_dev_get(bond_dev);
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ struct dpaa2_switch_lag *lag = rtnl_dereference(port_priv->lag);
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct net_device *brpdev;
+ bool learn_ena;
+ int err;
+
+ if (!lag)
+ return 0;
+
+ /* Recreate the LAG configuration for the LAG group that we left. */
+ rcu_assign_pointer(port_priv->lag, NULL);
+ dpaa2_switch_set_lag_cfg(bond_dev, lag->id, ethsw);
+
+ if (bridge_dev && netif_is_bridge_master(bridge_dev)) {
+ /* Make sure that the new primary inherits the learning state */
+ if (lag->primary) {
+ brpdev = dpaa2_switch_port_to_bridge_port(lag->primary);
+ learn_ena = br_port_flag_is_set(brpdev, BR_LEARNING);
+ err = dpaa2_switch_port_set_learning(lag->primary,
+ learn_ena);
+ if (err)
+ return err;
+ lag->primary->learn_ena = learn_ena;
+ }
+
+ /* In case the bond is a bridge port, leave the upper bridge as
+ * well.
+ */
+ return dpaa2_switch_port_bridge_leave(netdev);
}
return 0;
@@ -2234,8 +2729,8 @@ static int dpaa2_switch_port_prechangeupper(struct net_device *netdev,
struct netdev_notifier_changeupper_info *info)
{
struct ethsw_port_priv *port_priv;
+ struct net_device *upper_dev, *br;
struct netlink_ext_ack *extack;
- struct net_device *upper_dev;
int err;
if (!dpaa2_switch_port_dev_check(netdev))
@@ -2252,6 +2747,24 @@ static int dpaa2_switch_port_prechangeupper(struct net_device *netdev,
if (!info->linking)
dpaa2_switch_port_pre_bridge_leave(netdev);
+ } else if (netif_is_lag_master(upper_dev)) {
+ if (!info->linking) {
+ if (netif_is_bridge_port(upper_dev))
+ dpaa2_switch_port_pre_bridge_leave(netdev);
+ return 0;
+ }
+
+ if (netif_is_bridge_port(upper_dev)) {
+ br = netdev_master_upper_dev_get(upper_dev);
+ err = dpaa2_switch_prechangeupper_sanity_checks(netdev,
+ br,
+ extack);
+ if (err)
+ return err;
+ }
+
+ return dpaa2_switch_pre_lag_join(netdev, upper_dev,
+ info->upper_info, extack);
} else if (is_vlan_dev(upper_dev)) {
port_priv = netdev_priv(netdev);
if (port_priv->fdb->bridge_dev) {
@@ -2283,6 +2796,80 @@ static int dpaa2_switch_port_changeupper(struct net_device *netdev,
extack);
else
return dpaa2_switch_port_bridge_leave(netdev);
+ } else if (netif_is_lag_master(upper_dev)) {
+ if (info->linking)
+ return dpaa2_switch_port_bond_join(netdev, upper_dev,
+ info->upper_info,
+ extack);
+ else
+ return dpaa2_switch_port_bond_leave(netdev, upper_dev);
+ }
+
+ return 0;
+}
+
+static int
+dpaa2_switch_lag_prechangeupper(struct net_device *netdev,
+ struct netdev_notifier_changeupper_info *info)
+{
+ struct net_device *lower;
+ struct list_head *iter;
+ int err = 0;
+
+ if (!netif_is_lag_master(netdev))
+ return 0;
+
+ netdev_for_each_lower_dev(netdev, lower, iter) {
+ if (!dpaa2_switch_port_dev_check(lower))
+ continue;
+
+ err = dpaa2_switch_port_prechangeupper(lower, info);
+ if (err)
+ return err;
+ }
+
+ return err;
+}
+
+static int
+dpaa2_switch_lag_changeupper(struct net_device *netdev,
+ struct netdev_notifier_changeupper_info *info)
+{
+ struct net_device *lower;
+ struct list_head *iter;
+ int err = 0;
+
+ if (!netif_is_lag_master(netdev))
+ return 0;
+
+ netdev_for_each_lower_dev(netdev, lower, iter) {
+ if (!dpaa2_switch_port_dev_check(lower))
+ continue;
+
+ err = dpaa2_switch_port_changeupper(lower, info);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int
+dpaa2_switch_port_changelowerstate(struct net_device *netdev,
+ struct netdev_lag_lower_state_info *linfo)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ int err;
+
+ if (!rtnl_dereference(port_priv->lag))
+ return 0;
+
+ err = dpsw_if_set_lag_state(ethsw->mc_io, 0, ethsw->dpsw_handle,
+ port_priv->idx, linfo->tx_enabled ? 1 : 0);
+ if (err) {
+ netdev_err(netdev, "dpsw_if_set_lag_state() = %d\n", err);
+ return err;
}
return 0;
@@ -2292,6 +2879,7 @@ static int dpaa2_switch_port_netdevice_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
+ struct netdev_notifier_changelowerstate_info *info;
int err = 0;
switch (event) {
@@ -2300,13 +2888,29 @@ static int dpaa2_switch_port_netdevice_event(struct notifier_block *nb,
if (err)
return notifier_from_errno(err);
+ err = dpaa2_switch_lag_prechangeupper(netdev, ptr);
+ if (err)
+ return notifier_from_errno(err);
+
break;
case NETDEV_CHANGEUPPER:
err = dpaa2_switch_port_changeupper(netdev, ptr);
if (err)
return notifier_from_errno(err);
+ err = dpaa2_switch_lag_changeupper(netdev, ptr);
+ if (err)
+ return notifier_from_errno(err);
+
break;
+ case NETDEV_CHANGELOWERSTATE:
+ info = ptr;
+ if (!dpaa2_switch_port_dev_check(netdev))
+ break;
+
+ err = dpaa2_switch_port_changelowerstate(netdev,
+ info->lower_state_info);
+ return notifier_from_errno(err);
}
return NOTIFY_DONE;
@@ -2316,80 +2920,97 @@ struct ethsw_switchdev_event_work {
struct work_struct work;
struct switchdev_notifier_fdb_info fdb_info;
struct net_device *dev;
+ struct net_device *orig_dev;
unsigned long event;
+ u16 vid;
};
static void dpaa2_switch_event_work(struct work_struct *work)
{
struct ethsw_switchdev_event_work *switchdev_work =
container_of(work, struct ethsw_switchdev_event_work, work);
+ struct net_device *orig_dev = switchdev_work->orig_dev;
struct net_device *dev = switchdev_work->dev;
+ struct ethsw_port_priv *port_priv = netdev_priv(dev);
struct switchdev_notifier_fdb_info *fdb_info;
+ struct dpaa2_switch_lag *lag;
int err;
- rtnl_lock();
fdb_info = &switchdev_work->fdb_info;
+ /* The lag structures are freed only from dpaa2_switch_remove(), which
+ * first flushes this workqueue, so the pointer stays valid for the
+ * lifetime of the work item. Only the dereference needs the RCU
+ * read-side lock; the FDB helpers below can sleep and must run outside
+ * of it.
+ */
+ rcu_read_lock();
+ lag = rcu_dereference(port_priv->lag);
+ rcu_read_unlock();
+
switch (switchdev_work->event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE:
- if (!fdb_info->added_by_user || fdb_info->is_local)
- break;
- if (is_unicast_ether_addr(fdb_info->addr))
- err = dpaa2_switch_port_fdb_add_uc(netdev_priv(dev),
- fdb_info->addr);
+ if (lag)
+ err = dpaa2_switch_lag_fdb_add(lag, fdb_info->addr,
+ switchdev_work->vid);
else
- err = dpaa2_switch_port_fdb_add_mc(netdev_priv(dev),
- fdb_info->addr);
+ err = dpaa2_switch_port_fdb_add(port_priv,
+ fdb_info->addr);
if (err)
break;
fdb_info->offloaded = true;
- call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev,
+ call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, orig_dev,
&fdb_info->info, NULL);
break;
case SWITCHDEV_FDB_DEL_TO_DEVICE:
- if (!fdb_info->added_by_user || fdb_info->is_local)
- break;
- if (is_unicast_ether_addr(fdb_info->addr))
- dpaa2_switch_port_fdb_del_uc(netdev_priv(dev), fdb_info->addr);
+ if (lag)
+ dpaa2_switch_lag_fdb_del(lag, fdb_info->addr,
+ switchdev_work->vid);
else
- dpaa2_switch_port_fdb_del_mc(netdev_priv(dev), fdb_info->addr);
+ dpaa2_switch_port_fdb_del(port_priv, fdb_info->addr);
break;
}
- rtnl_unlock();
kfree(switchdev_work->fdb_info.addr);
kfree(switchdev_work);
dev_put(dev);
+ dev_put(orig_dev);
}
-/* Called under rcu_read_lock() */
-static int dpaa2_switch_port_event(struct notifier_block *nb,
- unsigned long event, void *ptr)
+static int
+dpaa2_switch_port_fdb_event(struct net_device *dev,
+ struct net_device *orig_dev,
+ unsigned long event, const void *ctx,
+ const struct switchdev_notifier_fdb_info *fdb_info)
{
- struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
struct ethsw_port_priv *port_priv = netdev_priv(dev);
struct ethsw_switchdev_event_work *switchdev_work;
- struct switchdev_notifier_fdb_info *fdb_info = ptr;
struct ethsw_core *ethsw = port_priv->ethsw_data;
- if (event == SWITCHDEV_PORT_ATTR_SET)
- return dpaa2_switch_port_attr_set_event(dev, ptr);
+ if (ctx && ctx != port_priv)
+ return 0;
- if (!dpaa2_switch_port_dev_check(dev))
- return NOTIFY_DONE;
+ /* For the moment, do nothing with entries towards foreign devices */
+ if (dpaa2_switch_foreign_dev_check(dev, orig_dev))
+ return 0;
+
+ if (!fdb_info->added_by_user || fdb_info->is_local)
+ return 0;
switchdev_work = kzalloc_obj(*switchdev_work, GFP_ATOMIC);
if (!switchdev_work)
- return NOTIFY_BAD;
+ return -ENOMEM;
INIT_WORK(&switchdev_work->work, dpaa2_switch_event_work);
switchdev_work->dev = dev;
switchdev_work->event = event;
+ switchdev_work->orig_dev = orig_dev;
+ switchdev_work->vid = fdb_info->vid;
switch (event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE:
case SWITCHDEV_FDB_DEL_TO_DEVICE:
- memcpy(&switchdev_work->fdb_info, ptr,
+ memcpy(&switchdev_work->fdb_info, fdb_info,
sizeof(switchdev_work->fdb_info));
switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
if (!switchdev_work->fdb_info.addr)
@@ -2400,52 +3021,61 @@ static int dpaa2_switch_port_event(struct notifier_block *nb,
/* Take a reference on the device to avoid being freed. */
dev_hold(dev);
+ dev_hold(orig_dev);
break;
default:
kfree(switchdev_work);
- return NOTIFY_DONE;
+ return 0;
}
queue_work(ethsw->workqueue, &switchdev_work->work);
- return NOTIFY_DONE;
+ return 0;
err_addr_alloc:
kfree(switchdev_work);
- return NOTIFY_BAD;
+ return -ENOMEM;
}
-static int dpaa2_switch_port_obj_event(unsigned long event,
- struct net_device *netdev,
- struct switchdev_notifier_port_obj_info *port_obj_info)
+/* Called under rcu_read_lock() */
+static int dpaa2_switch_port_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
{
- int err = -EOPNOTSUPP;
-
- if (!dpaa2_switch_port_dev_check(netdev))
- return NOTIFY_DONE;
+ struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+ int err;
switch (event) {
- case SWITCHDEV_PORT_OBJ_ADD:
- err = dpaa2_switch_port_obj_add(netdev, port_obj_info->obj);
- break;
- case SWITCHDEV_PORT_OBJ_DEL:
- err = dpaa2_switch_port_obj_del(netdev, port_obj_info->obj);
- break;
+ case SWITCHDEV_PORT_ATTR_SET:
+ return dpaa2_switch_port_attr_set_event(dev, ptr);
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ err = switchdev_handle_fdb_event_to_device(dev, event, ptr,
+ dpaa2_switch_port_dev_check,
+ dpaa2_switch_foreign_dev_check,
+ dpaa2_switch_port_fdb_event);
+ return notifier_from_errno(err);
+ default:
+ return NOTIFY_DONE;
}
-
- port_obj_info->handled = true;
- return notifier_from_errno(err);
}
static int dpaa2_switch_port_blocking_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+ int err;
switch (event) {
case SWITCHDEV_PORT_OBJ_ADD:
+ err = switchdev_handle_port_obj_add(dev, ptr,
+ dpaa2_switch_port_dev_check,
+ dpaa2_switch_port_obj_add);
+ return notifier_from_errno(err);
case SWITCHDEV_PORT_OBJ_DEL:
- return dpaa2_switch_port_obj_event(event, dev, ptr);
+ err = switchdev_handle_port_obj_del(dev, ptr,
+ dpaa2_switch_port_dev_check,
+ dpaa2_switch_port_obj_del);
+ return notifier_from_errno(err);
case SWITCHDEV_PORT_ATTR_SET:
return dpaa2_switch_port_attr_set_event(dev, ptr);
}
@@ -2490,19 +3120,22 @@ static void dpaa2_switch_rx(struct dpaa2_switch_fq *fq,
dma_addr_t addr = dpaa2_fd_get_addr(fd);
struct ethsw_core *ethsw = fq->ethsw;
struct ethsw_port_priv *port_priv;
+ struct dpaa2_switch_lag *lag;
struct net_device *netdev;
struct vlan_ethhdr *hdr;
struct sk_buff *skb;
u16 vlan_tci, vid;
int if_id, err;
void *vaddr;
+ u64 flc;
vaddr = dpaa2_iova_to_virt(ethsw->iommu_domain, addr);
dma_unmap_page(ethsw->dev, addr, DPAA2_SWITCH_RX_BUF_SIZE,
DMA_FROM_DEVICE);
/* get switch ingress interface ID */
- if_id = upper_32_bits(dpaa2_fd_get_flc(fd)) & 0x0000FFFF;
+ flc = dpaa2_fd_get_flc(fd);
+ if_id = DPAA2_ETHSW_FLC_IF_ID(flc);
if (if_id >= ethsw->sw_attr.num_ifs) {
dev_err(ethsw->dev, "Frame received from unknown interface!\n");
goto err_free_fd;
@@ -2541,12 +3174,20 @@ static void dpaa2_switch_rx(struct dpaa2_switch_fq *fq,
}
}
- skb->dev = netdev;
+ rcu_read_lock();
+
+ lag = rcu_dereference(port_priv->lag);
+ if (DPAA2_ETHSW_FLC_IMPRECISE_IF_ID(flc) && lag)
+ skb->dev = lag->bond_dev;
+ else
+ skb->dev = netdev;
skb->protocol = eth_type_trans(skb, skb->dev);
/* Setup the offload_fwd_mark only if the port is under a bridge */
skb->offload_fwd_mark = !!(port_priv->fdb->bridge_dev);
+ rcu_read_unlock();
+
netif_receive_skb(skb);
return;
@@ -2561,6 +3202,9 @@ static void dpaa2_switch_detect_features(struct ethsw_core *ethsw)
if (ethsw->major > 8 || (ethsw->major == 8 && ethsw->minor >= 6))
ethsw->features |= ETHSW_FEATURE_MAC_ADDR;
+
+ if (ethsw->major > 8 || (ethsw->major == 8 && ethsw->minor >= 13))
+ ethsw->features |= ETHSW_FEATURE_LAG_OFFLOAD;
}
static int dpaa2_switch_setup_fqs(struct ethsw_core *ethsw)
@@ -3195,17 +3839,15 @@ err_close:
return err;
}
-/* Add an ACL to redirect frames with specific destination MAC address to
- * control interface
- */
+/* Add an ACL to redirect frames to control interface based on the dst MAC */
static int dpaa2_switch_port_trap_mac_addr(struct ethsw_port_priv *port_priv,
- const char *mac)
+ const u8 *mac, const u8 *mask)
{
struct dpaa2_switch_acl_entry acl_entry = {0};
/* Match on the destination MAC address */
ether_addr_copy(acl_entry.key.match.l2_dest_mac, mac);
- eth_broadcast_addr(acl_entry.key.mask.l2_dest_mac);
+ ether_addr_copy(acl_entry.key.mask.l2_dest_mac, mask);
/* Trap to CPU */
acl_entry.cfg.precedence = 0;
@@ -3216,7 +3858,8 @@ static int dpaa2_switch_port_trap_mac_addr(struct ethsw_port_priv *port_priv,
static int dpaa2_switch_port_init(struct ethsw_port_priv *port_priv, u16 port)
{
- const char stpa[ETH_ALEN] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00};
+ const u8 ll_mac[ETH_ALEN] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00};
+ const u8 ll_mask[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xf0};
struct switchdev_obj_port_vlan vlan = {
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.vid = DEFAULT_VLAN_ID,
@@ -3291,7 +3934,7 @@ static int dpaa2_switch_port_init(struct ethsw_port_priv *port_priv, u16 port)
if (err)
return err;
- err = dpaa2_switch_port_trap_mac_addr(port_priv, stpa);
+ err = dpaa2_switch_port_trap_mac_addr(port_priv, ll_mac, ll_mask);
if (err)
return err;
@@ -3331,6 +3974,9 @@ static void dpaa2_switch_remove(struct fsl_mc_device *sw_dev)
dev = &sw_dev->dev;
ethsw = dev_get_drvdata(dev);
+ /* Make sure that all events were handled before we kfree anything */
+ flush_workqueue(ethsw->workqueue);
+
dpaa2_switch_teardown_irqs(sw_dev);
dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle);
@@ -3344,12 +3990,15 @@ static void dpaa2_switch_remove(struct fsl_mc_device *sw_dev)
for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++)
netif_napi_del(&ethsw->fq[i].napi);
- for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
dpaa2_switch_remove_port(ethsw, i);
+ mutex_destroy(&ethsw->lags[i].fdb_lock);
+ }
kfree(ethsw->fdbs);
kfree(ethsw->filter_blocks);
kfree(ethsw->ports);
+ kfree(ethsw->lags);
dpaa2_switch_teardown(sw_dev);
@@ -3377,6 +4026,7 @@ static int dpaa2_switch_probe_port(struct ethsw_core *ethsw,
port_priv = netdev_priv(port_netdev);
port_priv->netdev = port_netdev;
port_priv->ethsw_data = ethsw;
+ rcu_assign_pointer(port_priv->lag, NULL);
mutex_init(&port_priv->mac_lock);
@@ -3484,6 +4134,21 @@ static int dpaa2_switch_probe(struct fsl_mc_device *sw_dev)
goto err_free_fdbs;
}
+ ethsw->lags = kcalloc(ethsw->sw_attr.num_ifs, sizeof(*ethsw->lags),
+ GFP_KERNEL);
+ if (!ethsw->lags) {
+ err = -ENOMEM;
+ goto err_free_filter;
+ }
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ ethsw->lags[i].bond_dev = NULL;
+ ethsw->lags[i].ethsw = ethsw;
+ ethsw->lags[i].id = i + 1;
+ ethsw->lags[i].in_use = 0;
+ mutex_init(&ethsw->lags[i].fdb_lock);
+ INIT_LIST_HEAD(&ethsw->lags[i].fdbs);
+ }
+
for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
err = dpaa2_switch_probe_port(ethsw, i);
if (err)
@@ -3530,6 +4195,10 @@ err_stop:
err_free_netdev:
for (i--; i >= 0; i--)
dpaa2_switch_remove_port(ethsw, i);
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
+ mutex_destroy(&ethsw->lags[i].fdb_lock);
+ kfree(ethsw->lags);
+err_free_filter:
kfree(ethsw->filter_blocks);
err_free_fdbs:
kfree(ethsw->fdbs);
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h
index 42b3ca73f55d..63b702b0000c 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h
@@ -41,7 +41,8 @@
#define ETHSW_MAX_FRAME_LENGTH (DPAA2_MFL - VLAN_ETH_HLEN - ETH_FCS_LEN)
#define ETHSW_L2_MAX_FRM(mtu) ((mtu) + VLAN_ETH_HLEN + ETH_FCS_LEN)
-#define ETHSW_FEATURE_MAC_ADDR BIT(0)
+#define ETHSW_FEATURE_MAC_ADDR BIT(0)
+#define ETHSW_FEATURE_LAG_OFFLOAD BIT(1)
/* Number of receive queues (one RX and one TX_CONF) */
#define DPAA2_SWITCH_RX_NUM_FQS 2
@@ -86,6 +87,9 @@
#define DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE 256
+#define DPAA2_ETHSW_FLC_IF_ID(flc) (((flc) >> 32) & GENMASK(15, 0))
+#define DPAA2_ETHSW_FLC_IMPRECISE_IF_ID(flc) ((flc) & BIT_ULL(63))
+
extern const struct ethtool_ops dpaa2_switch_port_ethtool_ops;
struct ethsw_core;
@@ -99,12 +103,30 @@ struct dpaa2_switch_fq {
u32 fqid;
};
+struct dpaa2_mac_addr {
+ unsigned char addr[ETH_ALEN];
+ u16 vid;
+ refcount_t refcount;
+ struct list_head list;
+};
+
struct dpaa2_switch_fdb {
struct net_device *bridge_dev;
u16 fdb_id;
bool in_use;
};
+struct dpaa2_switch_lag {
+ struct ethsw_core *ethsw;
+ struct net_device *bond_dev;
+ bool in_use;
+ u8 id;
+ struct ethsw_port_priv *primary;
+ /* Protects the list of fdbs installed on this LAG */
+ struct mutex fdb_lock;
+ struct list_head fdbs;
+};
+
struct dpaa2_switch_acl_entry {
struct list_head list;
u16 prio;
@@ -163,6 +185,8 @@ struct ethsw_port_priv {
struct dpaa2_mac *mac;
/* Protects against changes to port_priv->mac */
struct mutex mac_lock;
+
+ struct dpaa2_switch_lag __rcu *lag;
};
/* Switch data */
@@ -190,6 +214,8 @@ struct ethsw_core {
struct dpaa2_switch_fdb *fdbs;
struct dpaa2_switch_filter_block *filter_blocks;
u16 mirror_port;
+
+ struct dpaa2_switch_lag *lags;
};
static inline int dpaa2_switch_get_index(struct ethsw_core *ethsw,
@@ -274,4 +300,18 @@ int dpaa2_switch_block_offload_mirror(struct dpaa2_switch_filter_block *block,
int dpaa2_switch_block_unoffload_mirror(struct dpaa2_switch_filter_block *block,
struct ethsw_port_priv *port_priv);
+
+static inline bool
+dpaa2_switch_port_offloads_bridge_port(struct ethsw_port_priv *port_priv,
+ const struct net_device *dev)
+{
+ struct dpaa2_switch_lag *lag = rcu_dereference_rtnl(port_priv->lag);
+
+ if (lag && lag->bond_dev == dev)
+ return true;
+ if (port_priv->netdev == dev)
+ return true;
+ return false;
+}
+
#endif /* __ETHSW_H */
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h b/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h
index 397d55f2bd99..9a2055c64983 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h
@@ -12,7 +12,7 @@
/* DPSW Version */
#define DPSW_VER_MAJOR 8
-#define DPSW_VER_MINOR 9
+#define DPSW_VER_MINOR 13
#define DPSW_CMD_BASE_VERSION 1
#define DPSW_CMD_VERSION_2 2
@@ -92,11 +92,14 @@
#define DPSW_CMDID_CTRL_IF_SET_POOLS DPSW_CMD_ID(0x0A1)
#define DPSW_CMDID_CTRL_IF_ENABLE DPSW_CMD_ID(0x0A2)
#define DPSW_CMDID_CTRL_IF_DISABLE DPSW_CMD_ID(0x0A3)
+#define DPSW_CMDID_SET_LAG DPSW_CMD_V2(0x0A4)
#define DPSW_CMDID_CTRL_IF_SET_QUEUE DPSW_CMD_ID(0x0A6)
#define DPSW_CMDID_SET_EGRESS_FLOOD DPSW_CMD_ID(0x0AC)
#define DPSW_CMDID_IF_SET_LEARNING_MODE DPSW_CMD_ID(0x0AD)
+#define DPSW_CMDID_IF_SET_LAG_STATE DPSW_CMD_ID(0x0B0)
+
/* Macros for accessing command fields smaller than 1byte */
#define DPSW_MASK(field) \
GENMASK(DPSW_##field##_SHIFT + DPSW_##field##_SIZE - 1, \
@@ -552,5 +555,18 @@ struct dpsw_cmd_if_reflection {
/* only 2 bits from the LSB */
u8 filter;
};
+
+struct dpsw_cmd_lag {
+ u8 group_id;
+ u8 num_ifs;
+ u8 pad[6];
+ u8 if_id[DPSW_MAX_LAG_IFS];
+ u8 phase;
+};
+
+struct dpsw_cmd_if_set_lag_state {
+ __le16 if_id;
+ u8 tx_enabled;
+};
#pragma pack(pop)
#endif /* __FSL_DPSW_CMD_H */
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpsw.c b/drivers/net/ethernet/freescale/dpaa2/dpsw.c
index ab921d75deb2..f75cbdce42ba 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpsw.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpsw.c
@@ -1659,3 +1659,63 @@ int dpsw_if_remove_reflection(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
return mc_send_command(mc_io, &cmd);
}
+
+/**
+ * dpsw_lag_set() - Set LAG configuration
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @cfg: pointer to LAG configuration
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpsw_lag_set(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+ const struct dpsw_lag_cfg *cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_lag *cmd_params;
+ int i = 0;
+
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_SET_LAG, cmd_flags, token);
+
+ if (cfg->num_ifs > DPSW_MAX_LAG_IFS)
+ return -EOPNOTSUPP;
+
+ cmd_params = (struct dpsw_cmd_lag *)cmd.params;
+ cmd_params->group_id = cfg->group_id;
+ cmd_params->num_ifs = cfg->num_ifs;
+ cmd_params->phase = cfg->phase;
+
+ for (i = 0; i < cfg->num_ifs; i++)
+ cmd_params->if_id[i] = cfg->if_id[i];
+
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_if_set_lag_state() - Change per port LAG state
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @if_id: ID of the switch interface
+ * @tx_enabled: Value of the per port LAG state
+ * - 0 if the interface will not be active as part of the LAG group
+ * - 1 if the interface will be active in the LAG group
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpsw_if_set_lag_state(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+ u16 if_id, u8 tx_enabled)
+{
+ struct dpsw_cmd_if_set_lag_state *cmd_params;
+ struct fsl_mc_command cmd = { 0 };
+
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_SET_LAG_STATE,
+ cmd_flags, token);
+
+ cmd_params = (struct dpsw_cmd_if_set_lag_state *)cmd.params;
+ cmd_params->if_id = cpu_to_le16(if_id);
+ cmd_params->tx_enabled = tx_enabled;
+
+ return mc_send_command(mc_io, &cmd);
+}
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpsw.h b/drivers/net/ethernet/freescale/dpaa2/dpsw.h
index b90bd363f47a..89f0267de8e9 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpsw.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpsw.h
@@ -20,6 +20,8 @@ struct fsl_mc_io;
#define DPSW_MAX_IF 64
+#define DPSW_MAX_LAG_IFS 8
+
int dpsw_open(struct fsl_mc_io *mc_io, u32 cmd_flags, int dpsw_id, u16 *token);
int dpsw_close(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token);
@@ -788,4 +790,32 @@ int dpsw_if_add_reflection(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
int dpsw_if_remove_reflection(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
u16 if_id, const struct dpsw_reflection_cfg *cfg);
+
+/* Link Aggregation Group configuration */
+
+#define DPSW_LAG_SET_PHASE_APPLY 0
+#define DPSW_LAG_SET_PHASE_CHECK 1
+
+/**
+ * struct dpsw_lag_cfg - Configuration structure for a LAG group
+ * @group_id: Link aggregation group ID. Valid values are in the
+ * [1, DPSW_MAX_LAG_IFS] range.
+ * @num_ifs: Number of interfaces in this LAG group, valid range is
+ * [0, DPSW_MAX_LAG_IFS].
+ * @if_id: Array containing the interface IDs of the ports part of a LAG group
+ * @phase: Use DPSW_LAG_SET_PHASE_APPLY for LAG configuration processing or
+ * DPSW_LAG_SET_PHASE_CHECK for LAG configuration validation.
+ */
+struct dpsw_lag_cfg {
+ u8 group_id;
+ u8 num_ifs;
+ u8 if_id[DPSW_MAX_LAG_IFS];
+ u8 phase;
+};
+
+int dpsw_lag_set(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+ const struct dpsw_lag_cfg *cfg);
+
+int dpsw_if_set_lag_state(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+ u16 if_id, u8 tx_enabled);
#endif /* __FSL_DPSW_H */
diff --git a/drivers/net/ethernet/ibm/Kconfig b/drivers/net/ethernet/ibm/Kconfig
index 4f4b23465c47..8e55faac2035 100644
--- a/drivers/net/ethernet/ibm/Kconfig
+++ b/drivers/net/ethernet/ibm/Kconfig
@@ -42,15 +42,6 @@ config IBMVETH_KUNIT_TEST
source "drivers/net/ethernet/ibm/emac/Kconfig"
-config EHEA
- tristate "eHEA Ethernet support"
- depends on IBMEBUS && SPARSEMEM
- help
- This driver supports the IBM pSeries eHEA ethernet adapter.
-
- To compile the driver as a module, choose M here. The module
- will be called ehea.
-
config IBMVNIC
tristate "IBM Virtual NIC support"
depends on PPC_PSERIES
diff --git a/drivers/net/ethernet/ibm/Makefile b/drivers/net/ethernet/ibm/Makefile
index 1d17d0c33d4d..c7e5d891c946 100644
--- a/drivers/net/ethernet/ibm/Makefile
+++ b/drivers/net/ethernet/ibm/Makefile
@@ -6,4 +6,3 @@
obj-$(CONFIG_IBMVETH) += ibmveth.o
obj-$(CONFIG_IBMVNIC) += ibmvnic.o
obj-$(CONFIG_IBM_EMAC) += emac/
-obj-$(CONFIG_EHEA) += ehea/
diff --git a/drivers/net/ethernet/ibm/ehea/Makefile b/drivers/net/ethernet/ibm/ehea/Makefile
deleted file mode 100644
index 9e1e5c7aafe2..000000000000
--- a/drivers/net/ethernet/ibm/ehea/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# Makefile for the eHEA ethernet device driver for IBM eServer System p
-#
-ehea-y = ehea_main.o ehea_phyp.o ehea_qmr.o ehea_ethtool.o
-obj-$(CONFIG_EHEA) += ehea.o
-
diff --git a/drivers/net/ethernet/ibm/ehea/ehea.h b/drivers/net/ethernet/ibm/ehea/ehea.h
deleted file mode 100644
index 208c440a602b..000000000000
--- a/drivers/net/ethernet/ibm/ehea/ehea.h
+++ /dev/null
@@ -1,477 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * linux/drivers/net/ethernet/ibm/ehea/ehea.h
- *
- * eHEA ethernet device driver for IBM eServer System p
- *
- * (C) Copyright IBM Corp. 2006
- *
- * Authors:
- * Christoph Raisch <raisch@de.ibm.com>
- * Jan-Bernd Themann <themann@de.ibm.com>
- * Thomas Klein <tklein@de.ibm.com>
- */
-
-#ifndef __EHEA_H__
-#define __EHEA_H__
-
-#include <linux/module.h>
-#include <linux/ethtool.h>
-#include <linux/vmalloc.h>
-#include <linux/if_vlan.h>
-#include <linux/platform_device.h>
-
-#include <asm/ibmebus.h>
-#include <asm/io.h>
-
-#define DRV_NAME "ehea"
-#define DRV_VERSION "EHEA_0107"
-
-/* eHEA capability flags */
-#define DLPAR_PORT_ADD_REM 1
-#define DLPAR_MEM_ADD 2
-#define DLPAR_MEM_REM 4
-#define EHEA_CAPABILITIES (DLPAR_PORT_ADD_REM | DLPAR_MEM_ADD | DLPAR_MEM_REM)
-
-#define EHEA_MSG_DEFAULT (NETIF_MSG_LINK | NETIF_MSG_TIMER \
- | NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR)
-
-#define EHEA_MAX_ENTRIES_RQ1 32767
-#define EHEA_MAX_ENTRIES_RQ2 16383
-#define EHEA_MAX_ENTRIES_RQ3 16383
-#define EHEA_MAX_ENTRIES_SQ 32767
-#define EHEA_MIN_ENTRIES_QP 127
-
-#define EHEA_SMALL_QUEUES
-
-#ifdef EHEA_SMALL_QUEUES
-#define EHEA_MAX_CQE_COUNT 1023
-#define EHEA_DEF_ENTRIES_SQ 1023
-#define EHEA_DEF_ENTRIES_RQ1 1023
-#define EHEA_DEF_ENTRIES_RQ2 1023
-#define EHEA_DEF_ENTRIES_RQ3 511
-#else
-#define EHEA_MAX_CQE_COUNT 4080
-#define EHEA_DEF_ENTRIES_SQ 4080
-#define EHEA_DEF_ENTRIES_RQ1 8160
-#define EHEA_DEF_ENTRIES_RQ2 2040
-#define EHEA_DEF_ENTRIES_RQ3 2040
-#endif
-
-#define EHEA_MAX_ENTRIES_EQ 20
-
-#define EHEA_SG_SQ 2
-#define EHEA_SG_RQ1 1
-#define EHEA_SG_RQ2 0
-#define EHEA_SG_RQ3 0
-
-#define EHEA_MAX_PACKET_SIZE 9022 /* for jumbo frames */
-#define EHEA_RQ2_PKT_SIZE 2048
-#define EHEA_L_PKT_SIZE 256 /* low latency */
-
-/* Send completion signaling */
-
-/* Protection Domain Identifier */
-#define EHEA_PD_ID 0xaabcdeff
-
-#define EHEA_RQ2_THRESHOLD 1
-#define EHEA_RQ3_THRESHOLD 4 /* use RQ3 threshold of 2048 bytes */
-
-#define EHEA_SPEED_10G 10000
-#define EHEA_SPEED_1G 1000
-#define EHEA_SPEED_100M 100
-#define EHEA_SPEED_10M 10
-#define EHEA_SPEED_AUTONEG 0
-
-/* Broadcast/Multicast registration types */
-#define EHEA_BCMC_SCOPE_ALL 0x08
-#define EHEA_BCMC_SCOPE_SINGLE 0x00
-#define EHEA_BCMC_MULTICAST 0x04
-#define EHEA_BCMC_BROADCAST 0x00
-#define EHEA_BCMC_UNTAGGED 0x02
-#define EHEA_BCMC_TAGGED 0x00
-#define EHEA_BCMC_VLANID_ALL 0x01
-#define EHEA_BCMC_VLANID_SINGLE 0x00
-
-#define EHEA_CACHE_LINE 128
-
-/* Memory Regions */
-#define EHEA_MR_ACC_CTRL 0x00800000
-
-#define EHEA_BUSMAP_START 0x8000000000000000ULL
-#define EHEA_INVAL_ADDR 0xFFFFFFFFFFFFFFFFULL
-#define EHEA_DIR_INDEX_SHIFT 13 /* 8k Entries in 64k block */
-#define EHEA_TOP_INDEX_SHIFT (EHEA_DIR_INDEX_SHIFT * 2)
-#define EHEA_MAP_ENTRIES (1 << EHEA_DIR_INDEX_SHIFT)
-#define EHEA_MAP_SIZE (0x10000) /* currently fixed map size */
-#define EHEA_INDEX_MASK (EHEA_MAP_ENTRIES - 1)
-
-
-#define EHEA_WATCH_DOG_TIMEOUT 10*HZ
-
-/* utility functions */
-
-void ehea_dump(void *adr, int len, char *msg);
-
-#define EHEA_BMASK(pos, length) (((pos) << 16) + (length))
-
-#define EHEA_BMASK_IBM(from, to) (((63 - to) << 16) + ((to) - (from) + 1))
-
-#define EHEA_BMASK_SHIFTPOS(mask) (((mask) >> 16) & 0xffff)
-
-#define EHEA_BMASK_MASK(mask) \
- (0xffffffffffffffffULL >> ((64 - (mask)) & 0xffff))
-
-#define EHEA_BMASK_SET(mask, value) \
- ((EHEA_BMASK_MASK(mask) & ((u64)(value))) << EHEA_BMASK_SHIFTPOS(mask))
-
-#define EHEA_BMASK_GET(mask, value) \
- (EHEA_BMASK_MASK(mask) & (((u64)(value)) >> EHEA_BMASK_SHIFTPOS(mask)))
-
-/*
- * Generic ehea page
- */
-struct ehea_page {
- u8 entries[PAGE_SIZE];
-};
-
-/*
- * Generic queue in linux kernel virtual memory
- */
-struct hw_queue {
- u64 current_q_offset; /* current queue entry */
- struct ehea_page **queue_pages; /* array of pages belonging to queue */
- u32 qe_size; /* queue entry size */
- u32 queue_length; /* queue length allocated in bytes */
- u32 pagesize;
- u32 toggle_state; /* toggle flag - per page */
- u32 reserved; /* 64 bit alignment */
-};
-
-/*
- * For pSeries this is a 64bit memory address where
- * I/O memory is mapped into CPU address space
- */
-struct h_epa {
- void __iomem *addr;
-};
-
-struct h_epa_user {
- u64 addr;
-};
-
-struct h_epas {
- struct h_epa kernel; /* kernel space accessible resource,
- set to 0 if unused */
- struct h_epa_user user; /* user space accessible resource
- set to 0 if unused */
-};
-
-/*
- * Memory map data structures
- */
-struct ehea_dir_bmap
-{
- u64 ent[EHEA_MAP_ENTRIES];
-};
-struct ehea_top_bmap
-{
- struct ehea_dir_bmap *dir[EHEA_MAP_ENTRIES];
-};
-struct ehea_bmap
-{
- struct ehea_top_bmap *top[EHEA_MAP_ENTRIES];
-};
-
-struct ehea_qp;
-struct ehea_cq;
-struct ehea_eq;
-struct ehea_port;
-struct ehea_av;
-
-/*
- * Queue attributes passed to ehea_create_qp()
- */
-struct ehea_qp_init_attr {
- /* input parameter */
- u32 qp_token; /* queue token */
- u8 low_lat_rq1;
- u8 signalingtype; /* cqe generation flag */
- u8 rq_count; /* num of receive queues */
- u8 eqe_gen; /* eqe generation flag */
- u16 max_nr_send_wqes; /* max number of send wqes */
- u16 max_nr_rwqes_rq1; /* max number of receive wqes */
- u16 max_nr_rwqes_rq2;
- u16 max_nr_rwqes_rq3;
- u8 wqe_size_enc_sq;
- u8 wqe_size_enc_rq1;
- u8 wqe_size_enc_rq2;
- u8 wqe_size_enc_rq3;
- u8 swqe_imm_data_len; /* immediate data length for swqes */
- u16 port_nr;
- u16 rq2_threshold;
- u16 rq3_threshold;
- u64 send_cq_handle;
- u64 recv_cq_handle;
- u64 aff_eq_handle;
-
- /* output parameter */
- u32 qp_nr;
- u16 act_nr_send_wqes;
- u16 act_nr_rwqes_rq1;
- u16 act_nr_rwqes_rq2;
- u16 act_nr_rwqes_rq3;
- u8 act_wqe_size_enc_sq;
- u8 act_wqe_size_enc_rq1;
- u8 act_wqe_size_enc_rq2;
- u8 act_wqe_size_enc_rq3;
- u32 nr_sq_pages;
- u32 nr_rq1_pages;
- u32 nr_rq2_pages;
- u32 nr_rq3_pages;
- u32 liobn_sq;
- u32 liobn_rq1;
- u32 liobn_rq2;
- u32 liobn_rq3;
-};
-
-/*
- * Event Queue attributes, passed as parameter
- */
-struct ehea_eq_attr {
- u32 type;
- u32 max_nr_of_eqes;
- u8 eqe_gen; /* generate eqe flag */
- u64 eq_handle;
- u32 act_nr_of_eqes;
- u32 nr_pages;
- u32 ist1; /* Interrupt service token */
- u32 ist2;
- u32 ist3;
- u32 ist4;
-};
-
-
-/*
- * Event Queue
- */
-struct ehea_eq {
- struct ehea_adapter *adapter;
- struct hw_queue hw_queue;
- u64 fw_handle;
- struct h_epas epas;
- spinlock_t spinlock;
- struct ehea_eq_attr attr;
-};
-
-/*
- * HEA Queues
- */
-struct ehea_qp {
- struct ehea_adapter *adapter;
- u64 fw_handle; /* QP handle for firmware calls */
- struct hw_queue hw_squeue;
- struct hw_queue hw_rqueue1;
- struct hw_queue hw_rqueue2;
- struct hw_queue hw_rqueue3;
- struct h_epas epas;
- struct ehea_qp_init_attr init_attr;
-};
-
-/*
- * Completion Queue attributes
- */
-struct ehea_cq_attr {
- /* input parameter */
- u32 max_nr_of_cqes;
- u32 cq_token;
- u64 eq_handle;
-
- /* output parameter */
- u32 act_nr_of_cqes;
- u32 nr_pages;
-};
-
-/*
- * Completion Queue
- */
-struct ehea_cq {
- struct ehea_adapter *adapter;
- u64 fw_handle;
- struct hw_queue hw_queue;
- struct h_epas epas;
- struct ehea_cq_attr attr;
-};
-
-/*
- * Memory Region
- */
-struct ehea_mr {
- struct ehea_adapter *adapter;
- u64 handle;
- u64 vaddr;
- u32 lkey;
-};
-
-/*
- * Port state information
- */
-struct port_stats {
- int poll_receive_errors;
- int queue_stopped;
- int err_tcp_cksum;
- int err_ip_cksum;
- int err_frame_crc;
-};
-
-#define EHEA_IRQ_NAME_SIZE 20
-
-/*
- * Queue SKB Array
- */
-struct ehea_q_skb_arr {
- struct sk_buff **arr; /* skb array for queue */
- int len; /* array length */
- int index; /* array index */
- int os_skbs; /* rq2/rq3 only: outstanding skbs */
-};
-
-/*
- * Port resources
- */
-struct ehea_port_res {
- struct napi_struct napi;
- struct port_stats p_stats;
- struct ehea_mr send_mr; /* send memory region */
- struct ehea_mr recv_mr; /* receive memory region */
- struct ehea_port *port;
- char int_recv_name[EHEA_IRQ_NAME_SIZE];
- char int_send_name[EHEA_IRQ_NAME_SIZE];
- struct ehea_qp *qp;
- struct ehea_cq *send_cq;
- struct ehea_cq *recv_cq;
- struct ehea_eq *eq;
- struct ehea_q_skb_arr rq1_skba;
- struct ehea_q_skb_arr rq2_skba;
- struct ehea_q_skb_arr rq3_skba;
- struct ehea_q_skb_arr sq_skba;
- int sq_skba_size;
- int swqe_refill_th;
- atomic_t swqe_avail;
- int swqe_ll_count;
- u32 swqe_id_counter;
- u64 tx_packets;
- u64 tx_bytes;
- u64 rx_packets;
- u64 rx_bytes;
- int sq_restart_flag;
-};
-
-
-#define EHEA_MAX_PORTS 16
-
-#define EHEA_NUM_PORTRES_FW_HANDLES 6 /* QP handle, SendCQ handle,
- RecvCQ handle, EQ handle,
- SendMR handle, RecvMR handle */
-#define EHEA_NUM_PORT_FW_HANDLES 1 /* EQ handle */
-#define EHEA_NUM_ADAPTER_FW_HANDLES 2 /* MR handle, NEQ handle */
-
-struct ehea_adapter {
- u64 handle;
- struct platform_device *ofdev;
- struct ehea_port *port[EHEA_MAX_PORTS];
- struct ehea_eq *neq; /* notification event queue */
- struct tasklet_struct neq_tasklet;
- struct ehea_mr mr;
- u32 pd; /* protection domain */
- u64 max_mc_mac; /* max number of multicast mac addresses */
- int active_ports;
- struct list_head list;
-};
-
-
-struct ehea_mc_list {
- struct list_head list;
- u64 macaddr;
-};
-
-/* kdump support */
-struct ehea_fw_handle_entry {
- u64 adh; /* Adapter Handle */
- u64 fwh; /* Firmware Handle */
-};
-
-struct ehea_fw_handle_array {
- struct ehea_fw_handle_entry *arr;
- int num_entries;
- struct mutex lock;
-};
-
-struct ehea_bcmc_reg_entry {
- u64 adh; /* Adapter Handle */
- u32 port_id; /* Logical Port Id */
- u8 reg_type; /* Registration Type */
- u64 macaddr;
-};
-
-struct ehea_bcmc_reg_array {
- struct ehea_bcmc_reg_entry *arr;
- int num_entries;
- spinlock_t lock;
-};
-
-#define EHEA_PORT_UP 1
-#define EHEA_PORT_DOWN 0
-#define EHEA_PHY_LINK_UP 1
-#define EHEA_PHY_LINK_DOWN 0
-#define EHEA_MAX_PORT_RES 16
-struct ehea_port {
- struct ehea_adapter *adapter; /* adapter that owns this port */
- struct net_device *netdev;
- struct rtnl_link_stats64 stats;
- struct ehea_port_res port_res[EHEA_MAX_PORT_RES];
- struct platform_device ofdev; /* Open Firmware Device */
- struct ehea_mc_list *mc_list; /* Multicast MAC addresses */
- struct ehea_eq *qp_eq;
- struct work_struct reset_task;
- struct delayed_work stats_work;
- struct mutex port_lock;
- char int_aff_name[EHEA_IRQ_NAME_SIZE];
- int allmulti; /* Indicates IFF_ALLMULTI state */
- int promisc; /* Indicates IFF_PROMISC state */
- int num_mcs;
- int resets;
- unsigned long flags;
- u64 mac_addr;
- u32 logical_port_id;
- u32 port_speed;
- u32 msg_enable;
- u32 sig_comp_iv;
- u32 state;
- u8 phy_link;
- u8 full_duplex;
- u8 autoneg;
- u8 num_def_qps;
- wait_queue_head_t swqe_avail_wq;
- wait_queue_head_t restart_wq;
-};
-
-struct port_res_cfg {
- int max_entries_rcq;
- int max_entries_scq;
- int max_entries_sq;
- int max_entries_rq1;
- int max_entries_rq2;
- int max_entries_rq3;
-};
-
-enum ehea_flag_bits {
- __EHEA_STOP_XFER,
- __EHEA_DISABLE_PORT_RESET
-};
-
-void ehea_set_ethtool_ops(struct net_device *netdev);
-int ehea_sense_port_attr(struct ehea_port *port);
-int ehea_set_portspeed(struct ehea_port *port, u32 port_speed);
-
-#endif /* __EHEA_H__ */
diff --git a/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c b/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c
deleted file mode 100644
index 1db5b6790a41..000000000000
--- a/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c
+++ /dev/null
@@ -1,277 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * linux/drivers/net/ethernet/ibm/ehea/ehea_ethtool.c
- *
- * eHEA ethernet device driver for IBM eServer System p
- *
- * (C) Copyright IBM Corp. 2006
- *
- * Authors:
- * Christoph Raisch <raisch@de.ibm.com>
- * Jan-Bernd Themann <themann@de.ibm.com>
- * Thomas Klein <tklein@de.ibm.com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include "ehea.h"
-#include "ehea_phyp.h"
-
-static int ehea_get_link_ksettings(struct net_device *dev,
- struct ethtool_link_ksettings *cmd)
-{
- struct ehea_port *port = netdev_priv(dev);
- u32 supported, advertising;
- u32 speed;
- int ret;
-
- ret = ehea_sense_port_attr(port);
-
- if (ret)
- return ret;
-
- if (netif_carrier_ok(dev)) {
- switch (port->port_speed) {
- case EHEA_SPEED_10M:
- speed = SPEED_10;
- break;
- case EHEA_SPEED_100M:
- speed = SPEED_100;
- break;
- case EHEA_SPEED_1G:
- speed = SPEED_1000;
- break;
- case EHEA_SPEED_10G:
- speed = SPEED_10000;
- break;
- default:
- speed = -1;
- break; /* BUG */
- }
- cmd->base.duplex = port->full_duplex == 1 ?
- DUPLEX_FULL : DUPLEX_HALF;
- } else {
- speed = SPEED_UNKNOWN;
- cmd->base.duplex = DUPLEX_UNKNOWN;
- }
- cmd->base.speed = speed;
-
- if (cmd->base.speed == SPEED_10000) {
- supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE);
- advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE);
- cmd->base.port = PORT_FIBRE;
- } else {
- supported = (SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full
- | SUPPORTED_100baseT_Half | SUPPORTED_10baseT_Full
- | SUPPORTED_10baseT_Half | SUPPORTED_Autoneg
- | SUPPORTED_TP);
- advertising = (ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg
- | ADVERTISED_TP);
- cmd->base.port = PORT_TP;
- }
-
- cmd->base.autoneg = port->autoneg == 1 ?
- AUTONEG_ENABLE : AUTONEG_DISABLE;
-
- ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
- supported);
- ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
- advertising);
-
- return 0;
-}
-
-static int ehea_set_link_ksettings(struct net_device *dev,
- const struct ethtool_link_ksettings *cmd)
-{
- struct ehea_port *port = netdev_priv(dev);
- int ret = 0;
- u32 sp;
-
- if (cmd->base.autoneg == AUTONEG_ENABLE) {
- sp = EHEA_SPEED_AUTONEG;
- goto doit;
- }
-
- switch (cmd->base.speed) {
- case SPEED_10:
- if (cmd->base.duplex == DUPLEX_FULL)
- sp = H_SPEED_10M_F;
- else
- sp = H_SPEED_10M_H;
- break;
-
- case SPEED_100:
- if (cmd->base.duplex == DUPLEX_FULL)
- sp = H_SPEED_100M_F;
- else
- sp = H_SPEED_100M_H;
- break;
-
- case SPEED_1000:
- if (cmd->base.duplex == DUPLEX_FULL)
- sp = H_SPEED_1G_F;
- else
- ret = -EINVAL;
- break;
-
- case SPEED_10000:
- if (cmd->base.duplex == DUPLEX_FULL)
- sp = H_SPEED_10G_F;
- else
- ret = -EINVAL;
- break;
-
- default:
- ret = -EINVAL;
- break;
- }
-
- if (ret)
- goto out;
-doit:
- ret = ehea_set_portspeed(port, sp);
-
- if (!ret)
- netdev_info(dev,
- "Port speed successfully set: %dMbps %s Duplex\n",
- port->port_speed,
- port->full_duplex == 1 ? "Full" : "Half");
-out:
- return ret;
-}
-
-static int ehea_nway_reset(struct net_device *dev)
-{
- struct ehea_port *port = netdev_priv(dev);
- int ret;
-
- ret = ehea_set_portspeed(port, EHEA_SPEED_AUTONEG);
-
- if (!ret)
- netdev_info(port->netdev,
- "Port speed successfully set: %dMbps %s Duplex\n",
- port->port_speed,
- port->full_duplex == 1 ? "Full" : "Half");
- return ret;
-}
-
-static void ehea_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *info)
-{
- strscpy(info->driver, DRV_NAME, sizeof(info->driver));
- strscpy(info->version, DRV_VERSION, sizeof(info->version));
-}
-
-static u32 ehea_get_msglevel(struct net_device *dev)
-{
- struct ehea_port *port = netdev_priv(dev);
- return port->msg_enable;
-}
-
-static void ehea_set_msglevel(struct net_device *dev, u32 value)
-{
- struct ehea_port *port = netdev_priv(dev);
- port->msg_enable = value;
-}
-
-static const char ehea_ethtool_stats_keys[][ETH_GSTRING_LEN] = {
- {"sig_comp_iv"},
- {"swqe_refill_th"},
- {"port resets"},
- {"Receive errors"},
- {"TCP cksum errors"},
- {"IP cksum errors"},
- {"Frame cksum errors"},
- {"num SQ stopped"},
- {"PR0 free_swqes"},
- {"PR1 free_swqes"},
- {"PR2 free_swqes"},
- {"PR3 free_swqes"},
- {"PR4 free_swqes"},
- {"PR5 free_swqes"},
- {"PR6 free_swqes"},
- {"PR7 free_swqes"},
- {"PR8 free_swqes"},
- {"PR9 free_swqes"},
- {"PR10 free_swqes"},
- {"PR11 free_swqes"},
- {"PR12 free_swqes"},
- {"PR13 free_swqes"},
- {"PR14 free_swqes"},
- {"PR15 free_swqes"},
-};
-
-static void ehea_get_strings(struct net_device *dev, u32 stringset, u8 *data)
-{
- if (stringset == ETH_SS_STATS) {
- memcpy(data, &ehea_ethtool_stats_keys,
- sizeof(ehea_ethtool_stats_keys));
- }
-}
-
-static int ehea_get_sset_count(struct net_device *dev, int sset)
-{
- switch (sset) {
- case ETH_SS_STATS:
- return ARRAY_SIZE(ehea_ethtool_stats_keys);
- default:
- return -EOPNOTSUPP;
- }
-}
-
-static void ehea_get_ethtool_stats(struct net_device *dev,
- struct ethtool_stats *stats, u64 *data)
-{
- int i, k, tmp;
- struct ehea_port *port = netdev_priv(dev);
-
- for (i = 0; i < ehea_get_sset_count(dev, ETH_SS_STATS); i++)
- data[i] = 0;
- i = 0;
-
- data[i++] = port->sig_comp_iv;
- data[i++] = port->port_res[0].swqe_refill_th;
- data[i++] = port->resets;
-
- for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
- tmp += port->port_res[k].p_stats.poll_receive_errors;
- data[i++] = tmp;
-
- for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
- tmp += port->port_res[k].p_stats.err_tcp_cksum;
- data[i++] = tmp;
-
- for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
- tmp += port->port_res[k].p_stats.err_ip_cksum;
- data[i++] = tmp;
-
- for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
- tmp += port->port_res[k].p_stats.err_frame_crc;
- data[i++] = tmp;
-
- for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++)
- tmp += port->port_res[k].p_stats.queue_stopped;
- data[i++] = tmp;
-
- for (k = 0; k < 16; k++)
- data[i++] = atomic_read(&port->port_res[k].swqe_avail);
-}
-
-static const struct ethtool_ops ehea_ethtool_ops = {
- .get_drvinfo = ehea_get_drvinfo,
- .get_msglevel = ehea_get_msglevel,
- .set_msglevel = ehea_set_msglevel,
- .get_link = ethtool_op_get_link,
- .get_strings = ehea_get_strings,
- .get_sset_count = ehea_get_sset_count,
- .get_ethtool_stats = ehea_get_ethtool_stats,
- .nway_reset = ehea_nway_reset, /* Restart autonegotiation */
- .get_link_ksettings = ehea_get_link_ksettings,
- .set_link_ksettings = ehea_set_link_ksettings,
-};
-
-void ehea_set_ethtool_ops(struct net_device *netdev)
-{
- netdev->ethtool_ops = &ehea_ethtool_ops;
-}
diff --git a/drivers/net/ethernet/ibm/ehea/ehea_hw.h b/drivers/net/ethernet/ibm/ehea/ehea_hw.h
deleted file mode 100644
index 590933a45d65..000000000000
--- a/drivers/net/ethernet/ibm/ehea/ehea_hw.h
+++ /dev/null
@@ -1,253 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * linux/drivers/net/ethernet/ibm/ehea/ehea_hw.h
- *
- * eHEA ethernet device driver for IBM eServer System p
- *
- * (C) Copyright IBM Corp. 2006
- *
- * Authors:
- * Christoph Raisch <raisch@de.ibm.com>
- * Jan-Bernd Themann <themann@de.ibm.com>
- * Thomas Klein <tklein@de.ibm.com>
- */
-
-#ifndef __EHEA_HW_H__
-#define __EHEA_HW_H__
-
-#define QPX_SQA_VALUE EHEA_BMASK_IBM(48, 63)
-#define QPX_RQ1A_VALUE EHEA_BMASK_IBM(48, 63)
-#define QPX_RQ2A_VALUE EHEA_BMASK_IBM(48, 63)
-#define QPX_RQ3A_VALUE EHEA_BMASK_IBM(48, 63)
-
-#define QPTEMM_OFFSET(x) offsetof(struct ehea_qptemm, x)
-
-struct ehea_qptemm {
- u64 qpx_hcr;
- u64 qpx_c;
- u64 qpx_herr;
- u64 qpx_aer;
- u64 qpx_sqa;
- u64 qpx_sqc;
- u64 qpx_rq1a;
- u64 qpx_rq1c;
- u64 qpx_st;
- u64 qpx_aerr;
- u64 qpx_tenure;
- u64 qpx_reserved1[(0x098 - 0x058) / 8];
- u64 qpx_portp;
- u64 qpx_reserved2[(0x100 - 0x0A0) / 8];
- u64 qpx_t;
- u64 qpx_sqhp;
- u64 qpx_sqptp;
- u64 qpx_reserved3[(0x140 - 0x118) / 8];
- u64 qpx_sqwsize;
- u64 qpx_reserved4[(0x170 - 0x148) / 8];
- u64 qpx_sqsize;
- u64 qpx_reserved5[(0x1B0 - 0x178) / 8];
- u64 qpx_sigt;
- u64 qpx_wqecnt;
- u64 qpx_rq1hp;
- u64 qpx_rq1ptp;
- u64 qpx_rq1size;
- u64 qpx_reserved6[(0x220 - 0x1D8) / 8];
- u64 qpx_rq1wsize;
- u64 qpx_reserved7[(0x240 - 0x228) / 8];
- u64 qpx_pd;
- u64 qpx_scqn;
- u64 qpx_rcqn;
- u64 qpx_aeqn;
- u64 reserved49;
- u64 qpx_ram;
- u64 qpx_reserved8[(0x300 - 0x270) / 8];
- u64 qpx_rq2a;
- u64 qpx_rq2c;
- u64 qpx_rq2hp;
- u64 qpx_rq2ptp;
- u64 qpx_rq2size;
- u64 qpx_rq2wsize;
- u64 qpx_rq2th;
- u64 qpx_rq3a;
- u64 qpx_rq3c;
- u64 qpx_rq3hp;
- u64 qpx_rq3ptp;
- u64 qpx_rq3size;
- u64 qpx_rq3wsize;
- u64 qpx_rq3th;
- u64 qpx_lpn;
- u64 qpx_reserved9[(0x400 - 0x378) / 8];
- u64 reserved_ext[(0x500 - 0x400) / 8];
- u64 reserved2[(0x1000 - 0x500) / 8];
-};
-
-#define MRx_HCR_LPARID_VALID EHEA_BMASK_IBM(0, 0)
-
-#define MRMWMM_OFFSET(x) offsetof(struct ehea_mrmwmm, x)
-
-struct ehea_mrmwmm {
- u64 mrx_hcr;
- u64 mrx_c;
- u64 mrx_herr;
- u64 mrx_aer;
- u64 mrx_pp;
- u64 reserved1;
- u64 reserved2;
- u64 reserved3;
- u64 reserved4[(0x200 - 0x40) / 8];
- u64 mrx_ctl[64];
-};
-
-#define QPEDMM_OFFSET(x) offsetof(struct ehea_qpedmm, x)
-
-struct ehea_qpedmm {
-
- u64 reserved0[(0x400) / 8];
- u64 qpedx_phh;
- u64 qpedx_ppsgp;
- u64 qpedx_ppsgu;
- u64 qpedx_ppdgp;
- u64 qpedx_ppdgu;
- u64 qpedx_aph;
- u64 qpedx_apsgp;
- u64 qpedx_apsgu;
- u64 qpedx_apdgp;
- u64 qpedx_apdgu;
- u64 qpedx_apav;
- u64 qpedx_apsav;
- u64 qpedx_hcr;
- u64 reserved1[4];
- u64 qpedx_rrl0;
- u64 qpedx_rrrkey0;
- u64 qpedx_rrva0;
- u64 reserved2;
- u64 qpedx_rrl1;
- u64 qpedx_rrrkey1;
- u64 qpedx_rrva1;
- u64 reserved3;
- u64 qpedx_rrl2;
- u64 qpedx_rrrkey2;
- u64 qpedx_rrva2;
- u64 reserved4;
- u64 qpedx_rrl3;
- u64 qpedx_rrrkey3;
- u64 qpedx_rrva3;
-};
-
-#define CQX_FECADDER EHEA_BMASK_IBM(32, 63)
-#define CQX_FEC_CQE_CNT EHEA_BMASK_IBM(32, 63)
-#define CQX_N1_GENERATE_COMP_EVENT EHEA_BMASK_IBM(0, 0)
-#define CQX_EP_EVENT_PENDING EHEA_BMASK_IBM(0, 0)
-
-#define CQTEMM_OFFSET(x) offsetof(struct ehea_cqtemm, x)
-
-struct ehea_cqtemm {
- u64 cqx_hcr;
- u64 cqx_c;
- u64 cqx_herr;
- u64 cqx_aer;
- u64 cqx_ptp;
- u64 cqx_tp;
- u64 cqx_fec;
- u64 cqx_feca;
- u64 cqx_ep;
- u64 cqx_eq;
- u64 reserved1;
- u64 cqx_n0;
- u64 cqx_n1;
- u64 reserved2[(0x1000 - 0x60) / 8];
-};
-
-#define EQTEMM_OFFSET(x) offsetof(struct ehea_eqtemm, x)
-
-struct ehea_eqtemm {
- u64 eqx_hcr;
- u64 eqx_c;
- u64 eqx_herr;
- u64 eqx_aer;
- u64 eqx_ptp;
- u64 eqx_tp;
- u64 eqx_ssba;
- u64 eqx_psba;
- u64 eqx_cec;
- u64 eqx_meql;
- u64 eqx_xisbi;
- u64 eqx_xisc;
- u64 eqx_it;
-};
-
-/*
- * These access functions will be changed when the dissuccsion about
- * the new access methods for POWER has settled.
- */
-
-static inline u64 epa_load(struct h_epa epa, u32 offset)
-{
- return __raw_readq((void __iomem *)(epa.addr + offset));
-}
-
-static inline void epa_store(struct h_epa epa, u32 offset, u64 value)
-{
- __raw_writeq(value, (void __iomem *)(epa.addr + offset));
- epa_load(epa, offset); /* synchronize explicitly to eHEA */
-}
-
-static inline void epa_store_acc(struct h_epa epa, u32 offset, u64 value)
-{
- __raw_writeq(value, (void __iomem *)(epa.addr + offset));
-}
-
-#define epa_store_cq(epa, offset, value)\
- epa_store(epa, CQTEMM_OFFSET(offset), value)
-#define epa_load_cq(epa, offset)\
- epa_load(epa, CQTEMM_OFFSET(offset))
-
-static inline void ehea_update_sqa(struct ehea_qp *qp, u16 nr_wqes)
-{
- struct h_epa epa = qp->epas.kernel;
- epa_store_acc(epa, QPTEMM_OFFSET(qpx_sqa),
- EHEA_BMASK_SET(QPX_SQA_VALUE, nr_wqes));
-}
-
-static inline void ehea_update_rq3a(struct ehea_qp *qp, u16 nr_wqes)
-{
- struct h_epa epa = qp->epas.kernel;
- epa_store_acc(epa, QPTEMM_OFFSET(qpx_rq3a),
- EHEA_BMASK_SET(QPX_RQ1A_VALUE, nr_wqes));
-}
-
-static inline void ehea_update_rq2a(struct ehea_qp *qp, u16 nr_wqes)
-{
- struct h_epa epa = qp->epas.kernel;
- epa_store_acc(epa, QPTEMM_OFFSET(qpx_rq2a),
- EHEA_BMASK_SET(QPX_RQ2A_VALUE, nr_wqes));
-}
-
-static inline void ehea_update_rq1a(struct ehea_qp *qp, u16 nr_wqes)
-{
- struct h_epa epa = qp->epas.kernel;
- epa_store_acc(epa, QPTEMM_OFFSET(qpx_rq1a),
- EHEA_BMASK_SET(QPX_RQ3A_VALUE, nr_wqes));
-}
-
-static inline void ehea_update_feca(struct ehea_cq *cq, u32 nr_cqes)
-{
- struct h_epa epa = cq->epas.kernel;
- epa_store_acc(epa, CQTEMM_OFFSET(cqx_feca),
- EHEA_BMASK_SET(CQX_FECADDER, nr_cqes));
-}
-
-static inline void ehea_reset_cq_n1(struct ehea_cq *cq)
-{
- struct h_epa epa = cq->epas.kernel;
- epa_store_cq(epa, cqx_n1,
- EHEA_BMASK_SET(CQX_N1_GENERATE_COMP_EVENT, 1));
-}
-
-static inline void ehea_reset_cq_ep(struct ehea_cq *my_cq)
-{
- struct h_epa epa = my_cq->epas.kernel;
- epa_store_acc(epa, CQTEMM_OFFSET(cqx_ep),
- EHEA_BMASK_SET(CQX_EP_EVENT_PENDING, 0));
-}
-
-#endif /* __EHEA_HW_H__ */
diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c
deleted file mode 100644
index bfc8699a05b9..000000000000
--- a/drivers/net/ethernet/ibm/ehea/ehea_main.c
+++ /dev/null
@@ -1,3581 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * linux/drivers/net/ethernet/ibm/ehea/ehea_main.c
- *
- * eHEA ethernet device driver for IBM eServer System p
- *
- * (C) Copyright IBM Corp. 2006
- *
- * Authors:
- * Christoph Raisch <raisch@de.ibm.com>
- * Jan-Bernd Themann <themann@de.ibm.com>
- * Thomas Klein <tklein@de.ibm.com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/device.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#include <linux/udp.h>
-#include <linux/if.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/if_ether.h>
-#include <linux/notifier.h>
-#include <linux/reboot.h>
-#include <linux/memory.h>
-#include <asm/kexec.h>
-#include <linux/mutex.h>
-#include <linux/prefetch.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-
-#include <net/ip.h>
-
-#include "ehea.h"
-#include "ehea_qmr.h"
-#include "ehea_phyp.h"
-
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Christoph Raisch <raisch@de.ibm.com>");
-MODULE_DESCRIPTION("IBM eServer HEA Driver");
-MODULE_VERSION(DRV_VERSION);
-
-
-static int msg_level = -1;
-static int rq1_entries = EHEA_DEF_ENTRIES_RQ1;
-static int rq2_entries = EHEA_DEF_ENTRIES_RQ2;
-static int rq3_entries = EHEA_DEF_ENTRIES_RQ3;
-static int sq_entries = EHEA_DEF_ENTRIES_SQ;
-static int use_mcs = 1;
-static int prop_carrier_state;
-
-module_param(msg_level, int, 0);
-module_param(rq1_entries, int, 0);
-module_param(rq2_entries, int, 0);
-module_param(rq3_entries, int, 0);
-module_param(sq_entries, int, 0);
-module_param(prop_carrier_state, int, 0);
-module_param(use_mcs, int, 0);
-
-MODULE_PARM_DESC(msg_level, "msg_level");
-MODULE_PARM_DESC(prop_carrier_state, "Propagate carrier state of physical "
- "port to stack. 1:yes, 0:no. Default = 0 ");
-MODULE_PARM_DESC(rq3_entries, "Number of entries for Receive Queue 3 "
- "[2^x - 1], x = [7..14]. Default = "
- __MODULE_STRING(EHEA_DEF_ENTRIES_RQ3) ")");
-MODULE_PARM_DESC(rq2_entries, "Number of entries for Receive Queue 2 "
- "[2^x - 1], x = [7..14]. Default = "
- __MODULE_STRING(EHEA_DEF_ENTRIES_RQ2) ")");
-MODULE_PARM_DESC(rq1_entries, "Number of entries for Receive Queue 1 "
- "[2^x - 1], x = [7..14]. Default = "
- __MODULE_STRING(EHEA_DEF_ENTRIES_RQ1) ")");
-MODULE_PARM_DESC(sq_entries, " Number of entries for the Send Queue "
- "[2^x - 1], x = [7..14]. Default = "
- __MODULE_STRING(EHEA_DEF_ENTRIES_SQ) ")");
-MODULE_PARM_DESC(use_mcs, " Multiple receive queues, 1: enable, 0: disable, "
- "Default = 1");
-
-static int port_name_cnt;
-static LIST_HEAD(adapter_list);
-static unsigned long ehea_driver_flags;
-static DEFINE_MUTEX(dlpar_mem_lock);
-static struct ehea_fw_handle_array ehea_fw_handles;
-static struct ehea_bcmc_reg_array ehea_bcmc_regs;
-
-
-static int ehea_probe_adapter(struct platform_device *dev);
-
-static void ehea_remove(struct platform_device *dev);
-
-static const struct of_device_id ehea_module_device_table[] = {
- {
- .name = "lhea",
- .compatible = "IBM,lhea",
- },
- {
- .type = "network",
- .compatible = "IBM,lhea-ethernet",
- },
- {},
-};
-MODULE_DEVICE_TABLE(of, ehea_module_device_table);
-
-static const struct of_device_id ehea_device_table[] = {
- {
- .name = "lhea",
- .compatible = "IBM,lhea",
- },
- {},
-};
-MODULE_DEVICE_TABLE(of, ehea_device_table);
-
-static struct platform_driver ehea_driver = {
- .driver = {
- .name = "ehea",
- .owner = THIS_MODULE,
- .of_match_table = ehea_device_table,
- },
- .probe = ehea_probe_adapter,
- .remove = ehea_remove,
-};
-
-void ehea_dump(void *adr, int len, char *msg)
-{
- int x;
- unsigned char *deb = adr;
- for (x = 0; x < len; x += 16) {
- pr_info("%s adr=%p ofs=%04x %016llx %016llx\n",
- msg, deb, x, *((u64 *)&deb[0]), *((u64 *)&deb[8]));
- deb += 16;
- }
-}
-
-static void ehea_schedule_port_reset(struct ehea_port *port)
-{
- if (!test_bit(__EHEA_DISABLE_PORT_RESET, &port->flags))
- schedule_work(&port->reset_task);
-}
-
-static void ehea_update_firmware_handles(void)
-{
- struct ehea_fw_handle_entry *arr = NULL;
- struct ehea_adapter *adapter;
- int num_adapters = 0;
- int num_ports = 0;
- int num_portres = 0;
- int i = 0;
- int num_fw_handles, k, l;
-
- /* Determine number of handles */
- mutex_lock(&ehea_fw_handles.lock);
-
- list_for_each_entry(adapter, &adapter_list, list) {
- num_adapters++;
-
- for (k = 0; k < EHEA_MAX_PORTS; k++) {
- struct ehea_port *port = adapter->port[k];
-
- if (!port || (port->state != EHEA_PORT_UP))
- continue;
-
- num_ports++;
- num_portres += port->num_def_qps;
- }
- }
-
- num_fw_handles = num_adapters * EHEA_NUM_ADAPTER_FW_HANDLES +
- num_ports * EHEA_NUM_PORT_FW_HANDLES +
- num_portres * EHEA_NUM_PORTRES_FW_HANDLES;
-
- if (num_fw_handles) {
- arr = kzalloc_objs(*arr, num_fw_handles);
- if (!arr)
- goto out; /* Keep the existing array */
- } else
- goto out_update;
-
- list_for_each_entry(adapter, &adapter_list, list) {
- if (num_adapters == 0)
- break;
-
- for (k = 0; k < EHEA_MAX_PORTS; k++) {
- struct ehea_port *port = adapter->port[k];
-
- if (!port || (port->state != EHEA_PORT_UP) ||
- (num_ports == 0))
- continue;
-
- for (l = 0; l < port->num_def_qps; l++) {
- struct ehea_port_res *pr = &port->port_res[l];
-
- arr[i].adh = adapter->handle;
- arr[i++].fwh = pr->qp->fw_handle;
- arr[i].adh = adapter->handle;
- arr[i++].fwh = pr->send_cq->fw_handle;
- arr[i].adh = adapter->handle;
- arr[i++].fwh = pr->recv_cq->fw_handle;
- arr[i].adh = adapter->handle;
- arr[i++].fwh = pr->eq->fw_handle;
- arr[i].adh = adapter->handle;
- arr[i++].fwh = pr->send_mr.handle;
- arr[i].adh = adapter->handle;
- arr[i++].fwh = pr->recv_mr.handle;
- }
- arr[i].adh = adapter->handle;
- arr[i++].fwh = port->qp_eq->fw_handle;
- num_ports--;
- }
-
- arr[i].adh = adapter->handle;
- arr[i++].fwh = adapter->neq->fw_handle;
-
- if (adapter->mr.handle) {
- arr[i].adh = adapter->handle;
- arr[i++].fwh = adapter->mr.handle;
- }
- num_adapters--;
- }
-
-out_update:
- kfree(ehea_fw_handles.arr);
- ehea_fw_handles.arr = arr;
- ehea_fw_handles.num_entries = i;
-out:
- mutex_unlock(&ehea_fw_handles.lock);
-}
-
-static void ehea_update_bcmc_registrations(void)
-{
- unsigned long flags;
- struct ehea_bcmc_reg_entry *arr = NULL;
- struct ehea_adapter *adapter;
- struct ehea_mc_list *mc_entry;
- int num_registrations = 0;
- int i = 0;
- int k;
-
- spin_lock_irqsave(&ehea_bcmc_regs.lock, flags);
-
- /* Determine number of registrations */
- list_for_each_entry(adapter, &adapter_list, list)
- for (k = 0; k < EHEA_MAX_PORTS; k++) {
- struct ehea_port *port = adapter->port[k];
-
- if (!port || (port->state != EHEA_PORT_UP))
- continue;
-
- num_registrations += 2; /* Broadcast registrations */
-
- list_for_each_entry(mc_entry, &port->mc_list->list,list)
- num_registrations += 2;
- }
-
- if (num_registrations) {
- arr = kzalloc_objs(*arr, num_registrations, GFP_ATOMIC);
- if (!arr)
- goto out; /* Keep the existing array */
- } else
- goto out_update;
-
- list_for_each_entry(adapter, &adapter_list, list) {
- for (k = 0; k < EHEA_MAX_PORTS; k++) {
- struct ehea_port *port = adapter->port[k];
-
- if (!port || (port->state != EHEA_PORT_UP))
- continue;
-
- if (num_registrations == 0)
- goto out_update;
-
- arr[i].adh = adapter->handle;
- arr[i].port_id = port->logical_port_id;
- arr[i].reg_type = EHEA_BCMC_BROADCAST |
- EHEA_BCMC_UNTAGGED;
- arr[i++].macaddr = port->mac_addr;
-
- arr[i].adh = adapter->handle;
- arr[i].port_id = port->logical_port_id;
- arr[i].reg_type = EHEA_BCMC_BROADCAST |
- EHEA_BCMC_VLANID_ALL;
- arr[i++].macaddr = port->mac_addr;
- num_registrations -= 2;
-
- list_for_each_entry(mc_entry,
- &port->mc_list->list, list) {
- if (num_registrations == 0)
- goto out_update;
-
- arr[i].adh = adapter->handle;
- arr[i].port_id = port->logical_port_id;
- arr[i].reg_type = EHEA_BCMC_MULTICAST |
- EHEA_BCMC_UNTAGGED;
- if (mc_entry->macaddr == 0)
- arr[i].reg_type |= EHEA_BCMC_SCOPE_ALL;
- arr[i++].macaddr = mc_entry->macaddr;
-
- arr[i].adh = adapter->handle;
- arr[i].port_id = port->logical_port_id;
- arr[i].reg_type = EHEA_BCMC_MULTICAST |
- EHEA_BCMC_VLANID_ALL;
- if (mc_entry->macaddr == 0)
- arr[i].reg_type |= EHEA_BCMC_SCOPE_ALL;
- arr[i++].macaddr = mc_entry->macaddr;
- num_registrations -= 2;
- }
- }
- }
-
-out_update:
- kfree(ehea_bcmc_regs.arr);
- ehea_bcmc_regs.arr = arr;
- ehea_bcmc_regs.num_entries = i;
-out:
- spin_unlock_irqrestore(&ehea_bcmc_regs.lock, flags);
-}
-
-static void ehea_get_stats64(struct net_device *dev,
- struct rtnl_link_stats64 *stats)
-{
- struct ehea_port *port = netdev_priv(dev);
- u64 rx_packets = 0, tx_packets = 0, rx_bytes = 0, tx_bytes = 0;
- int i;
-
- for (i = 0; i < port->num_def_qps; i++) {
- rx_packets += port->port_res[i].rx_packets;
- rx_bytes += port->port_res[i].rx_bytes;
- }
-
- for (i = 0; i < port->num_def_qps; i++) {
- tx_packets += port->port_res[i].tx_packets;
- tx_bytes += port->port_res[i].tx_bytes;
- }
-
- stats->tx_packets = tx_packets;
- stats->rx_bytes = rx_bytes;
- stats->tx_bytes = tx_bytes;
- stats->rx_packets = rx_packets;
-
- stats->multicast = port->stats.multicast;
- stats->rx_errors = port->stats.rx_errors;
-}
-
-static void ehea_update_stats(struct work_struct *work)
-{
- struct ehea_port *port =
- container_of(work, struct ehea_port, stats_work.work);
- struct net_device *dev = port->netdev;
- struct rtnl_link_stats64 *stats = &port->stats;
- struct hcp_ehea_port_cb2 *cb2;
- u64 hret;
-
- cb2 = (void *)get_zeroed_page(GFP_KERNEL);
- if (!cb2) {
- netdev_err(dev, "No mem for cb2. Some interface statistics were not updated\n");
- goto resched;
- }
-
- hret = ehea_h_query_ehea_port(port->adapter->handle,
- port->logical_port_id,
- H_PORT_CB2, H_PORT_CB2_ALL, cb2);
- if (hret != H_SUCCESS) {
- netdev_err(dev, "query_ehea_port failed\n");
- goto out_herr;
- }
-
- if (netif_msg_hw(port))
- ehea_dump(cb2, sizeof(*cb2), "net_device_stats");
-
- stats->multicast = cb2->rxmcp;
- stats->rx_errors = cb2->rxuerr;
-
-out_herr:
- free_page((unsigned long)cb2);
-resched:
- schedule_delayed_work(&port->stats_work,
- round_jiffies_relative(msecs_to_jiffies(1000)));
-}
-
-static void ehea_refill_rq1(struct ehea_port_res *pr, int index, int nr_of_wqes)
-{
- struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
- struct net_device *dev = pr->port->netdev;
- int max_index_mask = pr->rq1_skba.len - 1;
- int fill_wqes = pr->rq1_skba.os_skbs + nr_of_wqes;
- int adder = 0;
- int i;
-
- pr->rq1_skba.os_skbs = 0;
-
- if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))) {
- if (nr_of_wqes > 0)
- pr->rq1_skba.index = index;
- pr->rq1_skba.os_skbs = fill_wqes;
- return;
- }
-
- for (i = 0; i < fill_wqes; i++) {
- if (!skb_arr_rq1[index]) {
- skb_arr_rq1[index] = netdev_alloc_skb(dev,
- EHEA_L_PKT_SIZE);
- if (!skb_arr_rq1[index]) {
- pr->rq1_skba.os_skbs = fill_wqes - i;
- break;
- }
- }
- index--;
- index &= max_index_mask;
- adder++;
- }
-
- if (adder == 0)
- return;
-
- /* Ring doorbell */
- ehea_update_rq1a(pr->qp, adder);
-}
-
-static void ehea_init_fill_rq1(struct ehea_port_res *pr, int nr_rq1a)
-{
- struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
- struct net_device *dev = pr->port->netdev;
- int i;
-
- if (nr_rq1a > pr->rq1_skba.len) {
- netdev_err(dev, "NR_RQ1A bigger than skb array len\n");
- return;
- }
-
- for (i = 0; i < nr_rq1a; i++) {
- skb_arr_rq1[i] = netdev_alloc_skb(dev, EHEA_L_PKT_SIZE);
- if (!skb_arr_rq1[i])
- break;
- }
- /* Ring doorbell */
- ehea_update_rq1a(pr->qp, i - 1);
-}
-
-static int ehea_refill_rq_def(struct ehea_port_res *pr,
- struct ehea_q_skb_arr *q_skba, int rq_nr,
- int num_wqes, int wqe_type, int packet_size)
-{
- struct net_device *dev = pr->port->netdev;
- struct ehea_qp *qp = pr->qp;
- struct sk_buff **skb_arr = q_skba->arr;
- struct ehea_rwqe *rwqe;
- int i, index, max_index_mask, fill_wqes;
- int adder = 0;
- int ret = 0;
-
- fill_wqes = q_skba->os_skbs + num_wqes;
- q_skba->os_skbs = 0;
-
- if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))) {
- q_skba->os_skbs = fill_wqes;
- return ret;
- }
-
- index = q_skba->index;
- max_index_mask = q_skba->len - 1;
- for (i = 0; i < fill_wqes; i++) {
- u64 tmp_addr;
- struct sk_buff *skb;
-
- skb = netdev_alloc_skb_ip_align(dev, packet_size);
- if (!skb) {
- q_skba->os_skbs = fill_wqes - i;
- if (q_skba->os_skbs == q_skba->len - 2) {
- netdev_info(pr->port->netdev,
- "rq%i ran dry - no mem for skb\n",
- rq_nr);
- ret = -ENOMEM;
- }
- break;
- }
-
- skb_arr[index] = skb;
- tmp_addr = ehea_map_vaddr(skb->data);
- if (tmp_addr == -1) {
- dev_consume_skb_any(skb);
- q_skba->os_skbs = fill_wqes - i;
- ret = 0;
- break;
- }
-
- rwqe = ehea_get_next_rwqe(qp, rq_nr);
- rwqe->wr_id = EHEA_BMASK_SET(EHEA_WR_ID_TYPE, wqe_type)
- | EHEA_BMASK_SET(EHEA_WR_ID_INDEX, index);
- rwqe->sg_list[0].l_key = pr->recv_mr.lkey;
- rwqe->sg_list[0].vaddr = tmp_addr;
- rwqe->sg_list[0].len = packet_size;
- rwqe->data_segments = 1;
-
- index++;
- index &= max_index_mask;
- adder++;
- }
-
- q_skba->index = index;
- if (adder == 0)
- goto out;
-
- /* Ring doorbell */
- iosync();
- if (rq_nr == 2)
- ehea_update_rq2a(pr->qp, adder);
- else
- ehea_update_rq3a(pr->qp, adder);
-out:
- return ret;
-}
-
-
-static int ehea_refill_rq2(struct ehea_port_res *pr, int nr_of_wqes)
-{
- return ehea_refill_rq_def(pr, &pr->rq2_skba, 2,
- nr_of_wqes, EHEA_RWQE2_TYPE,
- EHEA_RQ2_PKT_SIZE);
-}
-
-
-static int ehea_refill_rq3(struct ehea_port_res *pr, int nr_of_wqes)
-{
- return ehea_refill_rq_def(pr, &pr->rq3_skba, 3,
- nr_of_wqes, EHEA_RWQE3_TYPE,
- EHEA_MAX_PACKET_SIZE);
-}
-
-static inline int ehea_check_cqe(struct ehea_cqe *cqe, int *rq_num)
-{
- *rq_num = (cqe->type & EHEA_CQE_TYPE_RQ) >> 5;
- if ((cqe->status & EHEA_CQE_STAT_ERR_MASK) == 0)
- return 0;
- if (((cqe->status & EHEA_CQE_STAT_ERR_TCP) != 0) &&
- (cqe->header_length == 0))
- return 0;
- return -EINVAL;
-}
-
-static inline void ehea_fill_skb(struct net_device *dev,
- struct sk_buff *skb, struct ehea_cqe *cqe,
- struct ehea_port_res *pr)
-{
- int length = cqe->num_bytes_transfered - 4; /*remove CRC */
-
- skb_put(skb, length);
- skb->protocol = eth_type_trans(skb, dev);
-
- /* The packet was not an IPV4 packet so a complemented checksum was
- calculated. The value is found in the Internet Checksum field. */
- if (cqe->status & EHEA_CQE_BLIND_CKSUM) {
- skb->ip_summed = CHECKSUM_COMPLETE;
- skb->csum = csum_unfold(~cqe->inet_checksum_value);
- } else
- skb->ip_summed = CHECKSUM_UNNECESSARY;
-
- skb_record_rx_queue(skb, pr - &pr->port->port_res[0]);
-}
-
-static inline struct sk_buff *get_skb_by_index(struct sk_buff **skb_array,
- int arr_len,
- struct ehea_cqe *cqe)
-{
- int skb_index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id);
- struct sk_buff *skb;
- void *pref;
- int x;
-
- x = skb_index + 1;
- x &= (arr_len - 1);
-
- pref = skb_array[x];
- if (pref) {
- prefetchw(pref);
- prefetchw(pref + EHEA_CACHE_LINE);
-
- pref = (skb_array[x]->data);
- prefetch(pref);
- prefetch(pref + EHEA_CACHE_LINE);
- prefetch(pref + EHEA_CACHE_LINE * 2);
- prefetch(pref + EHEA_CACHE_LINE * 3);
- }
-
- skb = skb_array[skb_index];
- skb_array[skb_index] = NULL;
- return skb;
-}
-
-static inline struct sk_buff *get_skb_by_index_ll(struct sk_buff **skb_array,
- int arr_len, int wqe_index)
-{
- struct sk_buff *skb;
- void *pref;
- int x;
-
- x = wqe_index + 1;
- x &= (arr_len - 1);
-
- pref = skb_array[x];
- if (pref) {
- prefetchw(pref);
- prefetchw(pref + EHEA_CACHE_LINE);
-
- pref = (skb_array[x]->data);
- prefetchw(pref);
- prefetchw(pref + EHEA_CACHE_LINE);
- }
-
- skb = skb_array[wqe_index];
- skb_array[wqe_index] = NULL;
- return skb;
-}
-
-static int ehea_treat_poll_error(struct ehea_port_res *pr, int rq,
- struct ehea_cqe *cqe, int *processed_rq2,
- int *processed_rq3)
-{
- struct sk_buff *skb;
-
- if (cqe->status & EHEA_CQE_STAT_ERR_TCP)
- pr->p_stats.err_tcp_cksum++;
- if (cqe->status & EHEA_CQE_STAT_ERR_IP)
- pr->p_stats.err_ip_cksum++;
- if (cqe->status & EHEA_CQE_STAT_ERR_CRC)
- pr->p_stats.err_frame_crc++;
-
- if (rq == 2) {
- *processed_rq2 += 1;
- skb = get_skb_by_index(pr->rq2_skba.arr, pr->rq2_skba.len, cqe);
- dev_kfree_skb(skb);
- } else if (rq == 3) {
- *processed_rq3 += 1;
- skb = get_skb_by_index(pr->rq3_skba.arr, pr->rq3_skba.len, cqe);
- dev_kfree_skb(skb);
- }
-
- if (cqe->status & EHEA_CQE_STAT_FAT_ERR_MASK) {
- if (netif_msg_rx_err(pr->port)) {
- pr_err("Critical receive error for QP %d. Resetting port.\n",
- pr->qp->init_attr.qp_nr);
- ehea_dump(cqe, sizeof(*cqe), "CQE");
- }
- ehea_schedule_port_reset(pr->port);
- return 1;
- }
-
- return 0;
-}
-
-static int ehea_proc_rwqes(struct net_device *dev,
- struct ehea_port_res *pr,
- int budget)
-{
- struct ehea_port *port = pr->port;
- struct ehea_qp *qp = pr->qp;
- struct ehea_cqe *cqe;
- struct sk_buff *skb;
- struct sk_buff **skb_arr_rq1 = pr->rq1_skba.arr;
- struct sk_buff **skb_arr_rq2 = pr->rq2_skba.arr;
- struct sk_buff **skb_arr_rq3 = pr->rq3_skba.arr;
- int skb_arr_rq1_len = pr->rq1_skba.len;
- int skb_arr_rq2_len = pr->rq2_skba.len;
- int skb_arr_rq3_len = pr->rq3_skba.len;
- int processed, processed_rq1, processed_rq2, processed_rq3;
- u64 processed_bytes = 0;
- int wqe_index, last_wqe_index, rq, port_reset;
-
- processed = processed_rq1 = processed_rq2 = processed_rq3 = 0;
- last_wqe_index = 0;
-
- cqe = ehea_poll_rq1(qp, &wqe_index);
- while ((processed < budget) && cqe) {
- ehea_inc_rq1(qp);
- processed_rq1++;
- processed++;
- if (netif_msg_rx_status(port))
- ehea_dump(cqe, sizeof(*cqe), "CQE");
-
- last_wqe_index = wqe_index;
- rmb();
- if (!ehea_check_cqe(cqe, &rq)) {
- if (rq == 1) {
- /* LL RQ1 */
- skb = get_skb_by_index_ll(skb_arr_rq1,
- skb_arr_rq1_len,
- wqe_index);
- if (unlikely(!skb)) {
- netif_info(port, rx_err, dev,
- "LL rq1: skb=NULL\n");
-
- skb = netdev_alloc_skb(dev,
- EHEA_L_PKT_SIZE);
- if (!skb)
- break;
- }
- skb_copy_to_linear_data(skb, ((char *)cqe) + 64,
- cqe->num_bytes_transfered - 4);
- ehea_fill_skb(dev, skb, cqe, pr);
- } else if (rq == 2) {
- /* RQ2 */
- skb = get_skb_by_index(skb_arr_rq2,
- skb_arr_rq2_len, cqe);
- if (unlikely(!skb)) {
- netif_err(port, rx_err, dev,
- "rq2: skb=NULL\n");
- break;
- }
- ehea_fill_skb(dev, skb, cqe, pr);
- processed_rq2++;
- } else {
- /* RQ3 */
- skb = get_skb_by_index(skb_arr_rq3,
- skb_arr_rq3_len, cqe);
- if (unlikely(!skb)) {
- netif_err(port, rx_err, dev,
- "rq3: skb=NULL\n");
- break;
- }
- ehea_fill_skb(dev, skb, cqe, pr);
- processed_rq3++;
- }
-
- processed_bytes += skb->len;
-
- if (cqe->status & EHEA_CQE_VLAN_TAG_XTRACT)
- __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
- cqe->vlan_tag);
-
- napi_gro_receive(&pr->napi, skb);
- } else {
- pr->p_stats.poll_receive_errors++;
- port_reset = ehea_treat_poll_error(pr, rq, cqe,
- &processed_rq2,
- &processed_rq3);
- if (port_reset)
- break;
- }
- cqe = ehea_poll_rq1(qp, &wqe_index);
- }
-
- pr->rx_packets += processed;
- pr->rx_bytes += processed_bytes;
-
- ehea_refill_rq1(pr, last_wqe_index, processed_rq1);
- ehea_refill_rq2(pr, processed_rq2);
- ehea_refill_rq3(pr, processed_rq3);
-
- return processed;
-}
-
-#define SWQE_RESTART_CHECK 0xdeadbeaff00d0000ull
-
-static void reset_sq_restart_flag(struct ehea_port *port)
-{
- int i;
-
- for (i = 0; i < port->num_def_qps; i++) {
- struct ehea_port_res *pr = &port->port_res[i];
- pr->sq_restart_flag = 0;
- }
- wake_up(&port->restart_wq);
-}
-
-static void check_sqs(struct ehea_port *port)
-{
- struct ehea_swqe *swqe;
- int swqe_index;
- int i;
-
- for (i = 0; i < port->num_def_qps; i++) {
- struct ehea_port_res *pr = &port->port_res[i];
- int ret;
- swqe = ehea_get_swqe(pr->qp, &swqe_index);
- memset(swqe, 0, SWQE_HEADER_SIZE);
- atomic_dec(&pr->swqe_avail);
-
- swqe->tx_control |= EHEA_SWQE_PURGE;
- swqe->wr_id = SWQE_RESTART_CHECK;
- swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION;
- swqe->tx_control |= EHEA_SWQE_IMM_DATA_PRESENT;
- swqe->immediate_data_length = 80;
-
- ehea_post_swqe(pr->qp, swqe);
-
- ret = wait_event_timeout(port->restart_wq,
- pr->sq_restart_flag == 0,
- msecs_to_jiffies(100));
-
- if (!ret) {
- pr_err("HW/SW queues out of sync\n");
- ehea_schedule_port_reset(pr->port);
- return;
- }
- }
-}
-
-
-static struct ehea_cqe *ehea_proc_cqes(struct ehea_port_res *pr, int my_quota)
-{
- struct sk_buff *skb;
- struct ehea_cq *send_cq = pr->send_cq;
- struct ehea_cqe *cqe;
- int quota = my_quota;
- int cqe_counter = 0;
- int swqe_av = 0;
- int index;
- struct netdev_queue *txq = netdev_get_tx_queue(pr->port->netdev,
- pr - &pr->port->port_res[0]);
-
- cqe = ehea_poll_cq(send_cq);
- while (cqe && (quota > 0)) {
- ehea_inc_cq(send_cq);
-
- cqe_counter++;
- rmb();
-
- if (cqe->wr_id == SWQE_RESTART_CHECK) {
- pr->sq_restart_flag = 1;
- swqe_av++;
- break;
- }
-
- if (cqe->status & EHEA_CQE_STAT_ERR_MASK) {
- pr_err("Bad send completion status=0x%04X\n",
- cqe->status);
-
- if (netif_msg_tx_err(pr->port))
- ehea_dump(cqe, sizeof(*cqe), "Send CQE");
-
- if (cqe->status & EHEA_CQE_STAT_RESET_MASK) {
- pr_err("Resetting port\n");
- ehea_schedule_port_reset(pr->port);
- break;
- }
- }
-
- if (netif_msg_tx_done(pr->port))
- ehea_dump(cqe, sizeof(*cqe), "CQE");
-
- if (likely(EHEA_BMASK_GET(EHEA_WR_ID_TYPE, cqe->wr_id)
- == EHEA_SWQE2_TYPE)) {
-
- index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id);
- skb = pr->sq_skba.arr[index];
- dev_consume_skb_any(skb);
- pr->sq_skba.arr[index] = NULL;
- }
-
- swqe_av += EHEA_BMASK_GET(EHEA_WR_ID_REFILL, cqe->wr_id);
- quota--;
-
- cqe = ehea_poll_cq(send_cq);
- }
-
- ehea_update_feca(send_cq, cqe_counter);
- atomic_add(swqe_av, &pr->swqe_avail);
-
- if (unlikely(netif_tx_queue_stopped(txq) &&
- (atomic_read(&pr->swqe_avail) >= pr->swqe_refill_th))) {
- __netif_tx_lock(txq, smp_processor_id());
- if (netif_tx_queue_stopped(txq) &&
- (atomic_read(&pr->swqe_avail) >= pr->swqe_refill_th))
- netif_tx_wake_queue(txq);
- __netif_tx_unlock(txq);
- }
-
- wake_up(&pr->port->swqe_avail_wq);
-
- return cqe;
-}
-
-#define EHEA_POLL_MAX_CQES 65535
-
-static int ehea_poll(struct napi_struct *napi, int budget)
-{
- struct ehea_port_res *pr = container_of(napi, struct ehea_port_res,
- napi);
- struct net_device *dev = pr->port->netdev;
- struct ehea_cqe *cqe;
- struct ehea_cqe *cqe_skb = NULL;
- int wqe_index;
- int rx = 0;
-
- cqe_skb = ehea_proc_cqes(pr, EHEA_POLL_MAX_CQES);
- rx += ehea_proc_rwqes(dev, pr, budget - rx);
-
- while (rx != budget) {
- napi_complete(napi);
- ehea_reset_cq_ep(pr->recv_cq);
- ehea_reset_cq_ep(pr->send_cq);
- ehea_reset_cq_n1(pr->recv_cq);
- ehea_reset_cq_n1(pr->send_cq);
- rmb();
- cqe = ehea_poll_rq1(pr->qp, &wqe_index);
- cqe_skb = ehea_poll_cq(pr->send_cq);
-
- if (!cqe && !cqe_skb)
- return rx;
-
- if (!napi_schedule(napi))
- return rx;
-
- cqe_skb = ehea_proc_cqes(pr, EHEA_POLL_MAX_CQES);
- rx += ehea_proc_rwqes(dev, pr, budget - rx);
- }
-
- return rx;
-}
-
-static irqreturn_t ehea_recv_irq_handler(int irq, void *param)
-{
- struct ehea_port_res *pr = param;
-
- napi_schedule(&pr->napi);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t ehea_qp_aff_irq_handler(int irq, void *param)
-{
- struct ehea_port *port = param;
- struct ehea_eqe *eqe;
- struct ehea_qp *qp;
- u32 qp_token;
- u64 resource_type, aer, aerr;
- int reset_port = 0;
-
- eqe = ehea_poll_eq(port->qp_eq);
-
- while (eqe) {
- qp_token = EHEA_BMASK_GET(EHEA_EQE_QP_TOKEN, eqe->entry);
- pr_err("QP aff_err: entry=0x%llx, token=0x%x\n",
- eqe->entry, qp_token);
-
- qp = port->port_res[qp_token].qp;
-
- resource_type = ehea_error_data(port->adapter, qp->fw_handle,
- &aer, &aerr);
-
- if (resource_type == EHEA_AER_RESTYPE_QP) {
- if ((aer & EHEA_AER_RESET_MASK) ||
- (aerr & EHEA_AERR_RESET_MASK))
- reset_port = 1;
- } else
- reset_port = 1; /* Reset in case of CQ or EQ error */
-
- eqe = ehea_poll_eq(port->qp_eq);
- }
-
- if (reset_port) {
- pr_err("Resetting port\n");
- ehea_schedule_port_reset(port);
- }
-
- return IRQ_HANDLED;
-}
-
-static struct ehea_port *ehea_get_port(struct ehea_adapter *adapter,
- int logical_port)
-{
- int i;
-
- for (i = 0; i < EHEA_MAX_PORTS; i++)
- if (adapter->port[i])
- if (adapter->port[i]->logical_port_id == logical_port)
- return adapter->port[i];
- return NULL;
-}
-
-int ehea_sense_port_attr(struct ehea_port *port)
-{
- int ret;
- u64 hret;
- struct hcp_ehea_port_cb0 *cb0;
-
- /* may be called via ehea_neq_tasklet() */
- cb0 = (void *)get_zeroed_page(GFP_ATOMIC);
- if (!cb0) {
- pr_err("no mem for cb0\n");
- ret = -ENOMEM;
- goto out;
- }
-
- hret = ehea_h_query_ehea_port(port->adapter->handle,
- port->logical_port_id, H_PORT_CB0,
- EHEA_BMASK_SET(H_PORT_CB0_ALL, 0xFFFF),
- cb0);
- if (hret != H_SUCCESS) {
- ret = -EIO;
- goto out_free;
- }
-
- /* MAC address */
- port->mac_addr = cb0->port_mac_addr << 16;
-
- if (!is_valid_ether_addr((u8 *)&port->mac_addr)) {
- ret = -EADDRNOTAVAIL;
- goto out_free;
- }
-
- /* Port speed */
- switch (cb0->port_speed) {
- case H_SPEED_10M_H:
- port->port_speed = EHEA_SPEED_10M;
- port->full_duplex = 0;
- break;
- case H_SPEED_10M_F:
- port->port_speed = EHEA_SPEED_10M;
- port->full_duplex = 1;
- break;
- case H_SPEED_100M_H:
- port->port_speed = EHEA_SPEED_100M;
- port->full_duplex = 0;
- break;
- case H_SPEED_100M_F:
- port->port_speed = EHEA_SPEED_100M;
- port->full_duplex = 1;
- break;
- case H_SPEED_1G_F:
- port->port_speed = EHEA_SPEED_1G;
- port->full_duplex = 1;
- break;
- case H_SPEED_10G_F:
- port->port_speed = EHEA_SPEED_10G;
- port->full_duplex = 1;
- break;
- default:
- port->port_speed = 0;
- port->full_duplex = 0;
- break;
- }
-
- port->autoneg = 1;
- port->num_mcs = cb0->num_default_qps;
-
- /* Number of default QPs */
- if (use_mcs)
- port->num_def_qps = cb0->num_default_qps;
- else
- port->num_def_qps = 1;
-
- if (!port->num_def_qps) {
- ret = -EINVAL;
- goto out_free;
- }
-
- ret = 0;
-out_free:
- if (ret || netif_msg_probe(port))
- ehea_dump(cb0, sizeof(*cb0), "ehea_sense_port_attr");
- free_page((unsigned long)cb0);
-out:
- return ret;
-}
-
-int ehea_set_portspeed(struct ehea_port *port, u32 port_speed)
-{
- struct hcp_ehea_port_cb4 *cb4;
- u64 hret;
- int ret = 0;
-
- cb4 = (void *)get_zeroed_page(GFP_KERNEL);
- if (!cb4) {
- pr_err("no mem for cb4\n");
- ret = -ENOMEM;
- goto out;
- }
-
- cb4->port_speed = port_speed;
-
- netif_carrier_off(port->netdev);
-
- hret = ehea_h_modify_ehea_port(port->adapter->handle,
- port->logical_port_id,
- H_PORT_CB4, H_PORT_CB4_SPEED, cb4);
- if (hret == H_SUCCESS) {
- port->autoneg = port_speed == EHEA_SPEED_AUTONEG ? 1 : 0;
-
- hret = ehea_h_query_ehea_port(port->adapter->handle,
- port->logical_port_id,
- H_PORT_CB4, H_PORT_CB4_SPEED,
- cb4);
- if (hret == H_SUCCESS) {
- switch (cb4->port_speed) {
- case H_SPEED_10M_H:
- port->port_speed = EHEA_SPEED_10M;
- port->full_duplex = 0;
- break;
- case H_SPEED_10M_F:
- port->port_speed = EHEA_SPEED_10M;
- port->full_duplex = 1;
- break;
- case H_SPEED_100M_H:
- port->port_speed = EHEA_SPEED_100M;
- port->full_duplex = 0;
- break;
- case H_SPEED_100M_F:
- port->port_speed = EHEA_SPEED_100M;
- port->full_duplex = 1;
- break;
- case H_SPEED_1G_F:
- port->port_speed = EHEA_SPEED_1G;
- port->full_duplex = 1;
- break;
- case H_SPEED_10G_F:
- port->port_speed = EHEA_SPEED_10G;
- port->full_duplex = 1;
- break;
- default:
- port->port_speed = 0;
- port->full_duplex = 0;
- break;
- }
- } else {
- pr_err("Failed sensing port speed\n");
- ret = -EIO;
- }
- } else {
- if (hret == H_AUTHORITY) {
- pr_info("Hypervisor denied setting port speed\n");
- ret = -EPERM;
- } else {
- ret = -EIO;
- pr_err("Failed setting port speed\n");
- }
- }
- if (!prop_carrier_state || (port->phy_link == EHEA_PHY_LINK_UP))
- netif_carrier_on(port->netdev);
-
- free_page((unsigned long)cb4);
-out:
- return ret;
-}
-
-static void ehea_parse_eqe(struct ehea_adapter *adapter, u64 eqe)
-{
- int ret;
- u8 ec;
- u8 portnum;
- struct ehea_port *port;
- struct net_device *dev;
-
- ec = EHEA_BMASK_GET(NEQE_EVENT_CODE, eqe);
- portnum = EHEA_BMASK_GET(NEQE_PORTNUM, eqe);
- port = ehea_get_port(adapter, portnum);
- if (!port) {
- netdev_err(NULL, "unknown portnum %x\n", portnum);
- return;
- }
- dev = port->netdev;
-
- switch (ec) {
- case EHEA_EC_PORTSTATE_CHG: /* port state change */
-
- if (EHEA_BMASK_GET(NEQE_PORT_UP, eqe)) {
- if (!netif_carrier_ok(dev)) {
- ret = ehea_sense_port_attr(port);
- if (ret) {
- netdev_err(dev, "failed resensing port attributes\n");
- break;
- }
-
- netif_info(port, link, dev,
- "Logical port up: %dMbps %s Duplex\n",
- port->port_speed,
- port->full_duplex == 1 ?
- "Full" : "Half");
-
- netif_carrier_on(dev);
- netif_wake_queue(dev);
- }
- } else
- if (netif_carrier_ok(dev)) {
- netif_info(port, link, dev,
- "Logical port down\n");
- netif_carrier_off(dev);
- netif_tx_disable(dev);
- }
-
- if (EHEA_BMASK_GET(NEQE_EXTSWITCH_PORT_UP, eqe)) {
- port->phy_link = EHEA_PHY_LINK_UP;
- netif_info(port, link, dev,
- "Physical port up\n");
- if (prop_carrier_state)
- netif_carrier_on(dev);
- } else {
- port->phy_link = EHEA_PHY_LINK_DOWN;
- netif_info(port, link, dev,
- "Physical port down\n");
- if (prop_carrier_state)
- netif_carrier_off(dev);
- }
-
- if (EHEA_BMASK_GET(NEQE_EXTSWITCH_PRIMARY, eqe))
- netdev_info(dev,
- "External switch port is primary port\n");
- else
- netdev_info(dev,
- "External switch port is backup port\n");
-
- break;
- case EHEA_EC_ADAPTER_MALFUNC:
- netdev_err(dev, "Adapter malfunction\n");
- break;
- case EHEA_EC_PORT_MALFUNC:
- netdev_info(dev, "Port malfunction\n");
- netif_carrier_off(dev);
- netif_tx_disable(dev);
- break;
- default:
- netdev_err(dev, "unknown event code %x, eqe=0x%llX\n", ec, eqe);
- break;
- }
-}
-
-static void ehea_neq_tasklet(struct tasklet_struct *t)
-{
- struct ehea_adapter *adapter = from_tasklet(adapter, t, neq_tasklet);
- struct ehea_eqe *eqe;
- u64 event_mask;
-
- eqe = ehea_poll_eq(adapter->neq);
- pr_debug("eqe=%p\n", eqe);
-
- while (eqe) {
- pr_debug("*eqe=%lx\n", (unsigned long) eqe->entry);
- ehea_parse_eqe(adapter, eqe->entry);
- eqe = ehea_poll_eq(adapter->neq);
- pr_debug("next eqe=%p\n", eqe);
- }
-
- event_mask = EHEA_BMASK_SET(NELR_PORTSTATE_CHG, 1)
- | EHEA_BMASK_SET(NELR_ADAPTER_MALFUNC, 1)
- | EHEA_BMASK_SET(NELR_PORT_MALFUNC, 1);
-
- ehea_h_reset_events(adapter->handle,
- adapter->neq->fw_handle, event_mask);
-}
-
-static irqreturn_t ehea_interrupt_neq(int irq, void *param)
-{
- struct ehea_adapter *adapter = param;
- tasklet_hi_schedule(&adapter->neq_tasklet);
- return IRQ_HANDLED;
-}
-
-
-static int ehea_fill_port_res(struct ehea_port_res *pr)
-{
- int ret;
- struct ehea_qp_init_attr *init_attr = &pr->qp->init_attr;
-
- ehea_init_fill_rq1(pr, pr->rq1_skba.len);
-
- ret = ehea_refill_rq2(pr, init_attr->act_nr_rwqes_rq2 - 1);
-
- ret |= ehea_refill_rq3(pr, init_attr->act_nr_rwqes_rq3 - 1);
-
- return ret;
-}
-
-static int ehea_reg_interrupts(struct net_device *dev)
-{
- struct ehea_port *port = netdev_priv(dev);
- struct ehea_port_res *pr;
- int i, ret;
-
-
- snprintf(port->int_aff_name, EHEA_IRQ_NAME_SIZE - 1, "%s-aff",
- dev->name);
-
- ret = ibmebus_request_irq(port->qp_eq->attr.ist1,
- ehea_qp_aff_irq_handler,
- 0, port->int_aff_name, port);
- if (ret) {
- netdev_err(dev, "failed registering irq for qp_aff_irq_handler:ist=%X\n",
- port->qp_eq->attr.ist1);
- goto out_free_qpeq;
- }
-
- netif_info(port, ifup, dev,
- "irq_handle 0x%X for function qp_aff_irq_handler registered\n",
- port->qp_eq->attr.ist1);
-
-
- for (i = 0; i < port->num_def_qps; i++) {
- pr = &port->port_res[i];
- snprintf(pr->int_send_name, EHEA_IRQ_NAME_SIZE - 1,
- "%s-queue%d", dev->name, i);
- ret = ibmebus_request_irq(pr->eq->attr.ist1,
- ehea_recv_irq_handler,
- 0, pr->int_send_name, pr);
- if (ret) {
- netdev_err(dev, "failed registering irq for ehea_queue port_res_nr:%d, ist=%X\n",
- i, pr->eq->attr.ist1);
- goto out_free_req;
- }
- netif_info(port, ifup, dev,
- "irq_handle 0x%X for function ehea_queue_int %d registered\n",
- pr->eq->attr.ist1, i);
- }
-out:
- return ret;
-
-
-out_free_req:
- while (--i >= 0) {
- u32 ist = port->port_res[i].eq->attr.ist1;
- ibmebus_free_irq(ist, &port->port_res[i]);
- }
-
-out_free_qpeq:
- ibmebus_free_irq(port->qp_eq->attr.ist1, port);
- i = port->num_def_qps;
-
- goto out;
-
-}
-
-static void ehea_free_interrupts(struct net_device *dev)
-{
- struct ehea_port *port = netdev_priv(dev);
- struct ehea_port_res *pr;
- int i;
-
- /* send */
-
- for (i = 0; i < port->num_def_qps; i++) {
- pr = &port->port_res[i];
- ibmebus_free_irq(pr->eq->attr.ist1, pr);
- netif_info(port, intr, dev,
- "free send irq for res %d with handle 0x%X\n",
- i, pr->eq->attr.ist1);
- }
-
- /* associated events */
- ibmebus_free_irq(port->qp_eq->attr.ist1, port);
- netif_info(port, intr, dev,
- "associated event interrupt for handle 0x%X freed\n",
- port->qp_eq->attr.ist1);
-}
-
-static int ehea_configure_port(struct ehea_port *port)
-{
- int ret, i;
- u64 hret, mask;
- struct hcp_ehea_port_cb0 *cb0;
-
- ret = -ENOMEM;
- cb0 = (void *)get_zeroed_page(GFP_KERNEL);
- if (!cb0)
- goto out;
-
- cb0->port_rc = EHEA_BMASK_SET(PXLY_RC_VALID, 1)
- | EHEA_BMASK_SET(PXLY_RC_IP_CHKSUM, 1)
- | EHEA_BMASK_SET(PXLY_RC_TCP_UDP_CHKSUM, 1)
- | EHEA_BMASK_SET(PXLY_RC_VLAN_XTRACT, 1)
- | EHEA_BMASK_SET(PXLY_RC_VLAN_TAG_FILTER,
- PXLY_RC_VLAN_FILTER)
- | EHEA_BMASK_SET(PXLY_RC_JUMBO_FRAME, 1);
-
- for (i = 0; i < port->num_mcs; i++)
- if (use_mcs)
- cb0->default_qpn_arr[i] =
- port->port_res[i].qp->init_attr.qp_nr;
- else
- cb0->default_qpn_arr[i] =
- port->port_res[0].qp->init_attr.qp_nr;
-
- if (netif_msg_ifup(port))
- ehea_dump(cb0, sizeof(*cb0), "ehea_configure_port");
-
- mask = EHEA_BMASK_SET(H_PORT_CB0_PRC, 1)
- | EHEA_BMASK_SET(H_PORT_CB0_DEFQPNARRAY, 1);
-
- hret = ehea_h_modify_ehea_port(port->adapter->handle,
- port->logical_port_id,
- H_PORT_CB0, mask, cb0);
- ret = -EIO;
- if (hret != H_SUCCESS)
- goto out_free;
-
- ret = 0;
-
-out_free:
- free_page((unsigned long)cb0);
-out:
- return ret;
-}
-
-static int ehea_gen_smrs(struct ehea_port_res *pr)
-{
- int ret;
- struct ehea_adapter *adapter = pr->port->adapter;
-
- ret = ehea_gen_smr(adapter, &adapter->mr, &pr->send_mr);
- if (ret)
- goto out;
-
- ret = ehea_gen_smr(adapter, &adapter->mr, &pr->recv_mr);
- if (ret)
- goto out_free;
-
- return 0;
-
-out_free:
- ehea_rem_mr(&pr->send_mr);
-out:
- pr_err("Generating SMRS failed\n");
- return -EIO;
-}
-
-static int ehea_rem_smrs(struct ehea_port_res *pr)
-{
- if ((ehea_rem_mr(&pr->send_mr)) ||
- (ehea_rem_mr(&pr->recv_mr)))
- return -EIO;
- else
- return 0;
-}
-
-static int ehea_init_q_skba(struct ehea_q_skb_arr *q_skba, int max_q_entries)
-{
- int arr_size = sizeof(void *) * max_q_entries;
-
- q_skba->arr = vzalloc(arr_size);
- if (!q_skba->arr)
- return -ENOMEM;
-
- q_skba->len = max_q_entries;
- q_skba->index = 0;
- q_skba->os_skbs = 0;
-
- return 0;
-}
-
-static int ehea_init_port_res(struct ehea_port *port, struct ehea_port_res *pr,
- struct port_res_cfg *pr_cfg, int queue_token)
-{
- struct ehea_adapter *adapter = port->adapter;
- enum ehea_eq_type eq_type = EHEA_EQ;
- struct ehea_qp_init_attr *init_attr = NULL;
- int ret = -EIO;
- u64 tx_bytes, rx_bytes, tx_packets, rx_packets;
-
- tx_bytes = pr->tx_bytes;
- tx_packets = pr->tx_packets;
- rx_bytes = pr->rx_bytes;
- rx_packets = pr->rx_packets;
-
- memset(pr, 0, sizeof(struct ehea_port_res));
-
- pr->tx_bytes = tx_bytes;
- pr->tx_packets = tx_packets;
- pr->rx_bytes = rx_bytes;
- pr->rx_packets = rx_packets;
-
- pr->port = port;
-
- pr->eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0);
- if (!pr->eq) {
- pr_err("create_eq failed (eq)\n");
- goto out_free;
- }
-
- pr->recv_cq = ehea_create_cq(adapter, pr_cfg->max_entries_rcq,
- pr->eq->fw_handle,
- port->logical_port_id);
- if (!pr->recv_cq) {
- pr_err("create_cq failed (cq_recv)\n");
- goto out_free;
- }
-
- pr->send_cq = ehea_create_cq(adapter, pr_cfg->max_entries_scq,
- pr->eq->fw_handle,
- port->logical_port_id);
- if (!pr->send_cq) {
- pr_err("create_cq failed (cq_send)\n");
- goto out_free;
- }
-
- if (netif_msg_ifup(port))
- pr_info("Send CQ: act_nr_cqes=%d, Recv CQ: act_nr_cqes=%d\n",
- pr->send_cq->attr.act_nr_of_cqes,
- pr->recv_cq->attr.act_nr_of_cqes);
-
- init_attr = kzalloc_obj(*init_attr);
- if (!init_attr) {
- ret = -ENOMEM;
- pr_err("no mem for ehea_qp_init_attr\n");
- goto out_free;
- }
-
- init_attr->low_lat_rq1 = 1;
- init_attr->signalingtype = 1; /* generate CQE if specified in WQE */
- init_attr->rq_count = 3;
- init_attr->qp_token = queue_token;
- init_attr->max_nr_send_wqes = pr_cfg->max_entries_sq;
- init_attr->max_nr_rwqes_rq1 = pr_cfg->max_entries_rq1;
- init_attr->max_nr_rwqes_rq2 = pr_cfg->max_entries_rq2;
- init_attr->max_nr_rwqes_rq3 = pr_cfg->max_entries_rq3;
- init_attr->wqe_size_enc_sq = EHEA_SG_SQ;
- init_attr->wqe_size_enc_rq1 = EHEA_SG_RQ1;
- init_attr->wqe_size_enc_rq2 = EHEA_SG_RQ2;
- init_attr->wqe_size_enc_rq3 = EHEA_SG_RQ3;
- init_attr->rq2_threshold = EHEA_RQ2_THRESHOLD;
- init_attr->rq3_threshold = EHEA_RQ3_THRESHOLD;
- init_attr->port_nr = port->logical_port_id;
- init_attr->send_cq_handle = pr->send_cq->fw_handle;
- init_attr->recv_cq_handle = pr->recv_cq->fw_handle;
- init_attr->aff_eq_handle = port->qp_eq->fw_handle;
-
- pr->qp = ehea_create_qp(adapter, adapter->pd, init_attr);
- if (!pr->qp) {
- pr_err("create_qp failed\n");
- ret = -EIO;
- goto out_free;
- }
-
- if (netif_msg_ifup(port))
- pr_info("QP: qp_nr=%d\n act_nr_snd_wqe=%d\n nr_rwqe_rq1=%d\n nr_rwqe_rq2=%d\n nr_rwqe_rq3=%d\n",
- init_attr->qp_nr,
- init_attr->act_nr_send_wqes,
- init_attr->act_nr_rwqes_rq1,
- init_attr->act_nr_rwqes_rq2,
- init_attr->act_nr_rwqes_rq3);
-
- pr->sq_skba_size = init_attr->act_nr_send_wqes + 1;
-
- ret = ehea_init_q_skba(&pr->sq_skba, pr->sq_skba_size);
- ret |= ehea_init_q_skba(&pr->rq1_skba, init_attr->act_nr_rwqes_rq1 + 1);
- ret |= ehea_init_q_skba(&pr->rq2_skba, init_attr->act_nr_rwqes_rq2 + 1);
- ret |= ehea_init_q_skba(&pr->rq3_skba, init_attr->act_nr_rwqes_rq3 + 1);
- if (ret)
- goto out_free;
-
- pr->swqe_refill_th = init_attr->act_nr_send_wqes / 10;
- if (ehea_gen_smrs(pr) != 0) {
- ret = -EIO;
- goto out_free;
- }
-
- atomic_set(&pr->swqe_avail, init_attr->act_nr_send_wqes - 1);
-
- kfree(init_attr);
-
- netif_napi_add(pr->port->netdev, &pr->napi, ehea_poll);
-
- ret = 0;
- goto out;
-
-out_free:
- kfree(init_attr);
- vfree(pr->sq_skba.arr);
- vfree(pr->rq1_skba.arr);
- vfree(pr->rq2_skba.arr);
- vfree(pr->rq3_skba.arr);
- ehea_destroy_qp(pr->qp);
- ehea_destroy_cq(pr->send_cq);
- ehea_destroy_cq(pr->recv_cq);
- ehea_destroy_eq(pr->eq);
-out:
- return ret;
-}
-
-static int ehea_clean_portres(struct ehea_port *port, struct ehea_port_res *pr)
-{
- int ret, i;
-
- if (pr->qp)
- netif_napi_del(&pr->napi);
-
- ret = ehea_destroy_qp(pr->qp);
-
- if (!ret) {
- ehea_destroy_cq(pr->send_cq);
- ehea_destroy_cq(pr->recv_cq);
- ehea_destroy_eq(pr->eq);
-
- for (i = 0; i < pr->rq1_skba.len; i++)
- dev_kfree_skb(pr->rq1_skba.arr[i]);
-
- for (i = 0; i < pr->rq2_skba.len; i++)
- dev_kfree_skb(pr->rq2_skba.arr[i]);
-
- for (i = 0; i < pr->rq3_skba.len; i++)
- dev_kfree_skb(pr->rq3_skba.arr[i]);
-
- for (i = 0; i < pr->sq_skba.len; i++)
- dev_kfree_skb(pr->sq_skba.arr[i]);
-
- vfree(pr->rq1_skba.arr);
- vfree(pr->rq2_skba.arr);
- vfree(pr->rq3_skba.arr);
- vfree(pr->sq_skba.arr);
- ret = ehea_rem_smrs(pr);
- }
- return ret;
-}
-
-static void write_swqe2_immediate(struct sk_buff *skb, struct ehea_swqe *swqe,
- u32 lkey)
-{
- int skb_data_size = skb_headlen(skb);
- u8 *imm_data = &swqe->u.immdata_desc.immediate_data[0];
- struct ehea_vsgentry *sg1entry = &swqe->u.immdata_desc.sg_entry;
- unsigned int immediate_len = SWQE2_MAX_IMM;
-
- swqe->descriptors = 0;
-
- if (skb_is_gso(skb)) {
- swqe->tx_control |= EHEA_SWQE_TSO;
- swqe->mss = skb_shinfo(skb)->gso_size;
- /*
- * For TSO packets we only copy the headers into the
- * immediate area.
- */
- immediate_len = skb_tcp_all_headers(skb);
- }
-
- if (skb_is_gso(skb) || skb_data_size >= SWQE2_MAX_IMM) {
- skb_copy_from_linear_data(skb, imm_data, immediate_len);
- swqe->immediate_data_length = immediate_len;
-
- if (skb_data_size > immediate_len) {
- sg1entry->l_key = lkey;
- sg1entry->len = skb_data_size - immediate_len;
- sg1entry->vaddr =
- ehea_map_vaddr(skb->data + immediate_len);
- swqe->descriptors++;
- }
- } else {
- skb_copy_from_linear_data(skb, imm_data, skb_data_size);
- swqe->immediate_data_length = skb_data_size;
- }
-}
-
-static inline void write_swqe2_data(struct sk_buff *skb, struct net_device *dev,
- struct ehea_swqe *swqe, u32 lkey)
-{
- struct ehea_vsgentry *sg_list, *sg1entry, *sgentry;
- skb_frag_t *frag;
- int nfrags, sg1entry_contains_frag_data, i;
-
- nfrags = skb_shinfo(skb)->nr_frags;
- sg1entry = &swqe->u.immdata_desc.sg_entry;
- sg_list = (struct ehea_vsgentry *)&swqe->u.immdata_desc.sg_list;
- sg1entry_contains_frag_data = 0;
-
- write_swqe2_immediate(skb, swqe, lkey);
-
- /* write descriptors */
- if (nfrags > 0) {
- if (swqe->descriptors == 0) {
- /* sg1entry not yet used */
- frag = &skb_shinfo(skb)->frags[0];
-
- /* copy sg1entry data */
- sg1entry->l_key = lkey;
- sg1entry->len = skb_frag_size(frag);
- sg1entry->vaddr =
- ehea_map_vaddr(skb_frag_address(frag));
- swqe->descriptors++;
- sg1entry_contains_frag_data = 1;
- }
-
- for (i = sg1entry_contains_frag_data; i < nfrags; i++) {
-
- frag = &skb_shinfo(skb)->frags[i];
- sgentry = &sg_list[i - sg1entry_contains_frag_data];
-
- sgentry->l_key = lkey;
- sgentry->len = skb_frag_size(frag);
- sgentry->vaddr = ehea_map_vaddr(skb_frag_address(frag));
- swqe->descriptors++;
- }
- }
-}
-
-static int ehea_broadcast_reg_helper(struct ehea_port *port, u32 hcallid)
-{
- int ret = 0;
- u64 hret;
- u8 reg_type;
-
- /* De/Register untagged packets */
- reg_type = EHEA_BCMC_BROADCAST | EHEA_BCMC_UNTAGGED;
- hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
- port->logical_port_id,
- reg_type, port->mac_addr, 0, hcallid);
- if (hret != H_SUCCESS) {
- pr_err("%sregistering bc address failed (tagged)\n",
- hcallid == H_REG_BCMC ? "" : "de");
- ret = -EIO;
- goto out_herr;
- }
-
- /* De/Register VLAN packets */
- reg_type = EHEA_BCMC_BROADCAST | EHEA_BCMC_VLANID_ALL;
- hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
- port->logical_port_id,
- reg_type, port->mac_addr, 0, hcallid);
- if (hret != H_SUCCESS) {
- pr_err("%sregistering bc address failed (vlan)\n",
- hcallid == H_REG_BCMC ? "" : "de");
- ret = -EIO;
- }
-out_herr:
- return ret;
-}
-
-static int ehea_set_mac_addr(struct net_device *dev, void *sa)
-{
- struct ehea_port *port = netdev_priv(dev);
- struct sockaddr *mac_addr = sa;
- struct hcp_ehea_port_cb0 *cb0;
- int ret;
- u64 hret;
-
- if (!is_valid_ether_addr(mac_addr->sa_data)) {
- ret = -EADDRNOTAVAIL;
- goto out;
- }
-
- cb0 = (void *)get_zeroed_page(GFP_KERNEL);
- if (!cb0) {
- pr_err("no mem for cb0\n");
- ret = -ENOMEM;
- goto out;
- }
-
- memcpy(&(cb0->port_mac_addr), &(mac_addr->sa_data[0]), ETH_ALEN);
-
- cb0->port_mac_addr = cb0->port_mac_addr >> 16;
-
- hret = ehea_h_modify_ehea_port(port->adapter->handle,
- port->logical_port_id, H_PORT_CB0,
- EHEA_BMASK_SET(H_PORT_CB0_MAC, 1), cb0);
- if (hret != H_SUCCESS) {
- ret = -EIO;
- goto out_free;
- }
-
- eth_hw_addr_set(dev, mac_addr->sa_data);
-
- /* Deregister old MAC in pHYP */
- if (port->state == EHEA_PORT_UP) {
- ret = ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
- if (ret)
- goto out_upregs;
- }
-
- port->mac_addr = cb0->port_mac_addr << 16;
-
- /* Register new MAC in pHYP */
- if (port->state == EHEA_PORT_UP) {
- ret = ehea_broadcast_reg_helper(port, H_REG_BCMC);
- if (ret)
- goto out_upregs;
- }
-
- ret = 0;
-
-out_upregs:
- ehea_update_bcmc_registrations();
-out_free:
- free_page((unsigned long)cb0);
-out:
- return ret;
-}
-
-static void ehea_promiscuous_error(u64 hret, int enable)
-{
- if (hret == H_AUTHORITY)
- pr_info("Hypervisor denied %sabling promiscuous mode\n",
- enable == 1 ? "en" : "dis");
- else
- pr_err("failed %sabling promiscuous mode\n",
- enable == 1 ? "en" : "dis");
-}
-
-static void ehea_promiscuous(struct net_device *dev, int enable)
-{
- struct ehea_port *port = netdev_priv(dev);
- struct hcp_ehea_port_cb7 *cb7;
- u64 hret;
-
- if (enable == port->promisc)
- return;
-
- cb7 = (void *)get_zeroed_page(GFP_ATOMIC);
- if (!cb7) {
- pr_err("no mem for cb7\n");
- goto out;
- }
-
- /* Modify Pxs_DUCQPN in CB7 */
- cb7->def_uc_qpn = enable == 1 ? port->port_res[0].qp->fw_handle : 0;
-
- hret = ehea_h_modify_ehea_port(port->adapter->handle,
- port->logical_port_id,
- H_PORT_CB7, H_PORT_CB7_DUCQPN, cb7);
- if (hret) {
- ehea_promiscuous_error(hret, enable);
- goto out;
- }
-
- port->promisc = enable;
-out:
- free_page((unsigned long)cb7);
-}
-
-static u64 ehea_multicast_reg_helper(struct ehea_port *port, u64 mc_mac_addr,
- u32 hcallid)
-{
- u64 hret;
- u8 reg_type;
-
- reg_type = EHEA_BCMC_MULTICAST | EHEA_BCMC_UNTAGGED;
- if (mc_mac_addr == 0)
- reg_type |= EHEA_BCMC_SCOPE_ALL;
-
- hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
- port->logical_port_id,
- reg_type, mc_mac_addr, 0, hcallid);
- if (hret)
- goto out;
-
- reg_type = EHEA_BCMC_MULTICAST | EHEA_BCMC_VLANID_ALL;
- if (mc_mac_addr == 0)
- reg_type |= EHEA_BCMC_SCOPE_ALL;
-
- hret = ehea_h_reg_dereg_bcmc(port->adapter->handle,
- port->logical_port_id,
- reg_type, mc_mac_addr, 0, hcallid);
-out:
- return hret;
-}
-
-static int ehea_drop_multicast_list(struct net_device *dev)
-{
- struct ehea_port *port = netdev_priv(dev);
- struct ehea_mc_list *mc_entry = port->mc_list;
- struct list_head *pos;
- struct list_head *temp;
- int ret = 0;
- u64 hret;
-
- list_for_each_safe(pos, temp, &(port->mc_list->list)) {
- mc_entry = list_entry(pos, struct ehea_mc_list, list);
-
- hret = ehea_multicast_reg_helper(port, mc_entry->macaddr,
- H_DEREG_BCMC);
- if (hret) {
- pr_err("failed deregistering mcast MAC\n");
- ret = -EIO;
- }
-
- list_del(pos);
- kfree(mc_entry);
- }
- return ret;
-}
-
-static void ehea_allmulti(struct net_device *dev, int enable)
-{
- struct ehea_port *port = netdev_priv(dev);
- u64 hret;
-
- if (!port->allmulti) {
- if (enable) {
- /* Enable ALLMULTI */
- ehea_drop_multicast_list(dev);
- hret = ehea_multicast_reg_helper(port, 0, H_REG_BCMC);
- if (!hret)
- port->allmulti = 1;
- else
- netdev_err(dev,
- "failed enabling IFF_ALLMULTI\n");
- }
- } else {
- if (!enable) {
- /* Disable ALLMULTI */
- hret = ehea_multicast_reg_helper(port, 0, H_DEREG_BCMC);
- if (!hret)
- port->allmulti = 0;
- else
- netdev_err(dev,
- "failed disabling IFF_ALLMULTI\n");
- }
- }
-}
-
-static void ehea_add_multicast_entry(struct ehea_port *port, u8 *mc_mac_addr)
-{
- struct ehea_mc_list *ehea_mcl_entry;
- u64 hret;
-
- ehea_mcl_entry = kzalloc_obj(*ehea_mcl_entry, GFP_ATOMIC);
- if (!ehea_mcl_entry)
- return;
-
- INIT_LIST_HEAD(&ehea_mcl_entry->list);
-
- memcpy(&ehea_mcl_entry->macaddr, mc_mac_addr, ETH_ALEN);
-
- hret = ehea_multicast_reg_helper(port, ehea_mcl_entry->macaddr,
- H_REG_BCMC);
- if (!hret)
- list_add(&ehea_mcl_entry->list, &port->mc_list->list);
- else {
- pr_err("failed registering mcast MAC\n");
- kfree(ehea_mcl_entry);
- }
-}
-
-static void ehea_set_multicast_list(struct net_device *dev)
-{
- struct ehea_port *port = netdev_priv(dev);
- struct netdev_hw_addr *ha;
- int ret;
-
- ehea_promiscuous(dev, !!(dev->flags & IFF_PROMISC));
-
- if (dev->flags & IFF_ALLMULTI) {
- ehea_allmulti(dev, 1);
- goto out;
- }
- ehea_allmulti(dev, 0);
-
- if (!netdev_mc_empty(dev)) {
- ret = ehea_drop_multicast_list(dev);
- if (ret) {
- /* Dropping the current multicast list failed.
- * Enabling ALL_MULTI is the best we can do.
- */
- ehea_allmulti(dev, 1);
- }
-
- if (netdev_mc_count(dev) > port->adapter->max_mc_mac) {
- pr_info("Mcast registration limit reached (0x%llx). Use ALLMULTI!\n",
- port->adapter->max_mc_mac);
- goto out;
- }
-
- netdev_for_each_mc_addr(ha, dev)
- ehea_add_multicast_entry(port, ha->addr);
-
- }
-out:
- ehea_update_bcmc_registrations();
-}
-
-static void xmit_common(struct sk_buff *skb, struct ehea_swqe *swqe)
-{
- swqe->tx_control |= EHEA_SWQE_IMM_DATA_PRESENT | EHEA_SWQE_CRC;
-
- if (vlan_get_protocol(skb) != htons(ETH_P_IP))
- return;
-
- if (skb->ip_summed == CHECKSUM_PARTIAL)
- swqe->tx_control |= EHEA_SWQE_IP_CHECKSUM;
-
- swqe->ip_start = skb_network_offset(skb);
- swqe->ip_end = swqe->ip_start + ip_hdrlen(skb) - 1;
-
- switch (ip_hdr(skb)->protocol) {
- case IPPROTO_UDP:
- if (skb->ip_summed == CHECKSUM_PARTIAL)
- swqe->tx_control |= EHEA_SWQE_TCP_CHECKSUM;
-
- swqe->tcp_offset = swqe->ip_end + 1 +
- offsetof(struct udphdr, check);
- break;
-
- case IPPROTO_TCP:
- if (skb->ip_summed == CHECKSUM_PARTIAL)
- swqe->tx_control |= EHEA_SWQE_TCP_CHECKSUM;
-
- swqe->tcp_offset = swqe->ip_end + 1 +
- offsetof(struct tcphdr, check);
- break;
- }
-}
-
-static void ehea_xmit2(struct sk_buff *skb, struct net_device *dev,
- struct ehea_swqe *swqe, u32 lkey)
-{
- swqe->tx_control |= EHEA_SWQE_DESCRIPTORS_PRESENT;
-
- xmit_common(skb, swqe);
-
- write_swqe2_data(skb, dev, swqe, lkey);
-}
-
-static void ehea_xmit3(struct sk_buff *skb, struct net_device *dev,
- struct ehea_swqe *swqe)
-{
- u8 *imm_data = &swqe->u.immdata_nodesc.immediate_data[0];
-
- xmit_common(skb, swqe);
-
- if (!skb->data_len)
- skb_copy_from_linear_data(skb, imm_data, skb->len);
- else
- skb_copy_bits(skb, 0, imm_data, skb->len);
-
- swqe->immediate_data_length = skb->len;
- dev_consume_skb_any(skb);
-}
-
-static netdev_tx_t ehea_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct ehea_port *port = netdev_priv(dev);
- struct ehea_swqe *swqe;
- u32 lkey;
- int swqe_index;
- struct ehea_port_res *pr;
- struct netdev_queue *txq;
-
- pr = &port->port_res[skb_get_queue_mapping(skb)];
- txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
-
- swqe = ehea_get_swqe(pr->qp, &swqe_index);
- memset(swqe, 0, SWQE_HEADER_SIZE);
- atomic_dec(&pr->swqe_avail);
-
- if (skb_vlan_tag_present(skb)) {
- swqe->tx_control |= EHEA_SWQE_VLAN_INSERT;
- swqe->vlan_tag = skb_vlan_tag_get(skb);
- }
-
- pr->tx_packets++;
- pr->tx_bytes += skb->len;
-
- if (skb->len <= SWQE3_MAX_IMM) {
- u32 sig_iv = port->sig_comp_iv;
- u32 swqe_num = pr->swqe_id_counter;
- ehea_xmit3(skb, dev, swqe);
- swqe->wr_id = EHEA_BMASK_SET(EHEA_WR_ID_TYPE, EHEA_SWQE3_TYPE)
- | EHEA_BMASK_SET(EHEA_WR_ID_COUNT, swqe_num);
- if (pr->swqe_ll_count >= (sig_iv - 1)) {
- swqe->wr_id |= EHEA_BMASK_SET(EHEA_WR_ID_REFILL,
- sig_iv);
- swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION;
- pr->swqe_ll_count = 0;
- } else
- pr->swqe_ll_count += 1;
- } else {
- swqe->wr_id =
- EHEA_BMASK_SET(EHEA_WR_ID_TYPE, EHEA_SWQE2_TYPE)
- | EHEA_BMASK_SET(EHEA_WR_ID_COUNT, pr->swqe_id_counter)
- | EHEA_BMASK_SET(EHEA_WR_ID_REFILL, 1)
- | EHEA_BMASK_SET(EHEA_WR_ID_INDEX, pr->sq_skba.index);
- pr->sq_skba.arr[pr->sq_skba.index] = skb;
-
- pr->sq_skba.index++;
- pr->sq_skba.index &= (pr->sq_skba.len - 1);
-
- lkey = pr->send_mr.lkey;
- ehea_xmit2(skb, dev, swqe, lkey);
- swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION;
- }
- pr->swqe_id_counter += 1;
-
- netif_info(port, tx_queued, dev,
- "post swqe on QP %d\n", pr->qp->init_attr.qp_nr);
- if (netif_msg_tx_queued(port))
- ehea_dump(swqe, 512, "swqe");
-
- if (unlikely(test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))) {
- netif_tx_stop_queue(txq);
- swqe->tx_control |= EHEA_SWQE_PURGE;
- }
-
- ehea_post_swqe(pr->qp, swqe);
-
- if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) {
- pr->p_stats.queue_stopped++;
- netif_tx_stop_queue(txq);
- }
-
- return NETDEV_TX_OK;
-}
-
-static int ehea_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
-{
- struct ehea_port *port = netdev_priv(dev);
- struct ehea_adapter *adapter = port->adapter;
- struct hcp_ehea_port_cb1 *cb1;
- int index;
- u64 hret;
- int err = 0;
-
- cb1 = (void *)get_zeroed_page(GFP_KERNEL);
- if (!cb1) {
- pr_err("no mem for cb1\n");
- err = -ENOMEM;
- goto out;
- }
-
- hret = ehea_h_query_ehea_port(adapter->handle, port->logical_port_id,
- H_PORT_CB1, H_PORT_CB1_ALL, cb1);
- if (hret != H_SUCCESS) {
- pr_err("query_ehea_port failed\n");
- err = -EINVAL;
- goto out;
- }
-
- index = (vid / 64);
- cb1->vlan_filter[index] |= ((u64)(0x8000000000000000 >> (vid & 0x3F)));
-
- hret = ehea_h_modify_ehea_port(adapter->handle, port->logical_port_id,
- H_PORT_CB1, H_PORT_CB1_ALL, cb1);
- if (hret != H_SUCCESS) {
- pr_err("modify_ehea_port failed\n");
- err = -EINVAL;
- }
-out:
- free_page((unsigned long)cb1);
- return err;
-}
-
-static int ehea_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
-{
- struct ehea_port *port = netdev_priv(dev);
- struct ehea_adapter *adapter = port->adapter;
- struct hcp_ehea_port_cb1 *cb1;
- int index;
- u64 hret;
- int err = 0;
-
- cb1 = (void *)get_zeroed_page(GFP_KERNEL);
- if (!cb1) {
- pr_err("no mem for cb1\n");
- err = -ENOMEM;
- goto out;
- }
-
- hret = ehea_h_query_ehea_port(adapter->handle, port->logical_port_id,
- H_PORT_CB1, H_PORT_CB1_ALL, cb1);
- if (hret != H_SUCCESS) {
- pr_err("query_ehea_port failed\n");
- err = -EINVAL;
- goto out;
- }
-
- index = (vid / 64);
- cb1->vlan_filter[index] &= ~((u64)(0x8000000000000000 >> (vid & 0x3F)));
-
- hret = ehea_h_modify_ehea_port(adapter->handle, port->logical_port_id,
- H_PORT_CB1, H_PORT_CB1_ALL, cb1);
- if (hret != H_SUCCESS) {
- pr_err("modify_ehea_port failed\n");
- err = -EINVAL;
- }
-out:
- free_page((unsigned long)cb1);
- return err;
-}
-
-static int ehea_activate_qp(struct ehea_adapter *adapter, struct ehea_qp *qp)
-{
- int ret = -EIO;
- u64 hret;
- u16 dummy16 = 0;
- u64 dummy64 = 0;
- struct hcp_modify_qp_cb0 *cb0;
-
- cb0 = (void *)get_zeroed_page(GFP_KERNEL);
- if (!cb0) {
- ret = -ENOMEM;
- goto out;
- }
-
- hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
- EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
- if (hret != H_SUCCESS) {
- pr_err("query_ehea_qp failed (1)\n");
- goto out;
- }
-
- cb0->qp_ctl_reg = H_QP_CR_STATE_INITIALIZED;
- hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
- EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG, 1), cb0,
- &dummy64, &dummy64, &dummy16, &dummy16);
- if (hret != H_SUCCESS) {
- pr_err("modify_ehea_qp failed (1)\n");
- goto out;
- }
-
- hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
- EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
- if (hret != H_SUCCESS) {
- pr_err("query_ehea_qp failed (2)\n");
- goto out;
- }
-
- cb0->qp_ctl_reg = H_QP_CR_ENABLED | H_QP_CR_STATE_INITIALIZED;
- hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
- EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG, 1), cb0,
- &dummy64, &dummy64, &dummy16, &dummy16);
- if (hret != H_SUCCESS) {
- pr_err("modify_ehea_qp failed (2)\n");
- goto out;
- }
-
- hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
- EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
- if (hret != H_SUCCESS) {
- pr_err("query_ehea_qp failed (3)\n");
- goto out;
- }
-
- cb0->qp_ctl_reg = H_QP_CR_ENABLED | H_QP_CR_STATE_RDY2SND;
- hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
- EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG, 1), cb0,
- &dummy64, &dummy64, &dummy16, &dummy16);
- if (hret != H_SUCCESS) {
- pr_err("modify_ehea_qp failed (3)\n");
- goto out;
- }
-
- hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
- EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF), cb0);
- if (hret != H_SUCCESS) {
- pr_err("query_ehea_qp failed (4)\n");
- goto out;
- }
-
- ret = 0;
-out:
- free_page((unsigned long)cb0);
- return ret;
-}
-
-static int ehea_port_res_setup(struct ehea_port *port, int def_qps)
-{
- int ret, i;
- struct port_res_cfg pr_cfg, pr_cfg_small_rx;
- enum ehea_eq_type eq_type = EHEA_EQ;
-
- port->qp_eq = ehea_create_eq(port->adapter, eq_type,
- EHEA_MAX_ENTRIES_EQ, 1);
- if (!port->qp_eq) {
- ret = -EINVAL;
- pr_err("ehea_create_eq failed (qp_eq)\n");
- goto out_kill_eq;
- }
-
- pr_cfg.max_entries_rcq = rq1_entries + rq2_entries + rq3_entries;
- pr_cfg.max_entries_scq = sq_entries * 2;
- pr_cfg.max_entries_sq = sq_entries;
- pr_cfg.max_entries_rq1 = rq1_entries;
- pr_cfg.max_entries_rq2 = rq2_entries;
- pr_cfg.max_entries_rq3 = rq3_entries;
-
- pr_cfg_small_rx.max_entries_rcq = 1;
- pr_cfg_small_rx.max_entries_scq = sq_entries;
- pr_cfg_small_rx.max_entries_sq = sq_entries;
- pr_cfg_small_rx.max_entries_rq1 = 1;
- pr_cfg_small_rx.max_entries_rq2 = 1;
- pr_cfg_small_rx.max_entries_rq3 = 1;
-
- for (i = 0; i < def_qps; i++) {
- ret = ehea_init_port_res(port, &port->port_res[i], &pr_cfg, i);
- if (ret)
- goto out_clean_pr;
- }
- for (i = def_qps; i < def_qps; i++) {
- ret = ehea_init_port_res(port, &port->port_res[i],
- &pr_cfg_small_rx, i);
- if (ret)
- goto out_clean_pr;
- }
-
- return 0;
-
-out_clean_pr:
- while (--i >= 0)
- ehea_clean_portres(port, &port->port_res[i]);
-
-out_kill_eq:
- ehea_destroy_eq(port->qp_eq);
- return ret;
-}
-
-static int ehea_clean_all_portres(struct ehea_port *port)
-{
- int ret = 0;
- int i;
-
- for (i = 0; i < port->num_def_qps; i++)
- ret |= ehea_clean_portres(port, &port->port_res[i]);
-
- ret |= ehea_destroy_eq(port->qp_eq);
-
- return ret;
-}
-
-static void ehea_remove_adapter_mr(struct ehea_adapter *adapter)
-{
- if (adapter->active_ports)
- return;
-
- ehea_rem_mr(&adapter->mr);
-}
-
-static int ehea_add_adapter_mr(struct ehea_adapter *adapter)
-{
- if (adapter->active_ports)
- return 0;
-
- return ehea_reg_kernel_mr(adapter, &adapter->mr);
-}
-
-static int ehea_up(struct net_device *dev)
-{
- int ret, i;
- struct ehea_port *port = netdev_priv(dev);
-
- if (port->state == EHEA_PORT_UP)
- return 0;
-
- ret = ehea_port_res_setup(port, port->num_def_qps);
- if (ret) {
- netdev_err(dev, "port_res_failed\n");
- goto out;
- }
-
- /* Set default QP for this port */
- ret = ehea_configure_port(port);
- if (ret) {
- netdev_err(dev, "ehea_configure_port failed. ret:%d\n", ret);
- goto out_clean_pr;
- }
-
- ret = ehea_reg_interrupts(dev);
- if (ret) {
- netdev_err(dev, "reg_interrupts failed. ret:%d\n", ret);
- goto out_clean_pr;
- }
-
- for (i = 0; i < port->num_def_qps; i++) {
- ret = ehea_activate_qp(port->adapter, port->port_res[i].qp);
- if (ret) {
- netdev_err(dev, "activate_qp failed\n");
- goto out_free_irqs;
- }
- }
-
- for (i = 0; i < port->num_def_qps; i++) {
- ret = ehea_fill_port_res(&port->port_res[i]);
- if (ret) {
- netdev_err(dev, "out_free_irqs\n");
- goto out_free_irqs;
- }
- }
-
- ret = ehea_broadcast_reg_helper(port, H_REG_BCMC);
- if (ret) {
- ret = -EIO;
- goto out_free_irqs;
- }
-
- port->state = EHEA_PORT_UP;
-
- ret = 0;
- goto out;
-
-out_free_irqs:
- ehea_free_interrupts(dev);
-
-out_clean_pr:
- ehea_clean_all_portres(port);
-out:
- if (ret)
- netdev_info(dev, "Failed starting. ret=%i\n", ret);
-
- ehea_update_bcmc_registrations();
- ehea_update_firmware_handles();
-
- return ret;
-}
-
-static void port_napi_disable(struct ehea_port *port)
-{
- int i;
-
- for (i = 0; i < port->num_def_qps; i++)
- napi_disable(&port->port_res[i].napi);
-}
-
-static void port_napi_enable(struct ehea_port *port)
-{
- int i;
-
- for (i = 0; i < port->num_def_qps; i++)
- napi_enable(&port->port_res[i].napi);
-}
-
-static int ehea_open(struct net_device *dev)
-{
- int ret;
- struct ehea_port *port = netdev_priv(dev);
-
- mutex_lock(&port->port_lock);
-
- netif_info(port, ifup, dev, "enabling port\n");
-
- netif_carrier_off(dev);
-
- ret = ehea_up(dev);
- if (!ret) {
- port_napi_enable(port);
- netif_tx_start_all_queues(dev);
- }
-
- mutex_unlock(&port->port_lock);
- schedule_delayed_work(&port->stats_work,
- round_jiffies_relative(msecs_to_jiffies(1000)));
-
- return ret;
-}
-
-static int ehea_down(struct net_device *dev)
-{
- int ret;
- struct ehea_port *port = netdev_priv(dev);
-
- if (port->state == EHEA_PORT_DOWN)
- return 0;
-
- ehea_drop_multicast_list(dev);
- ehea_allmulti(dev, 0);
- ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
-
- ehea_free_interrupts(dev);
-
- port->state = EHEA_PORT_DOWN;
-
- ehea_update_bcmc_registrations();
-
- ret = ehea_clean_all_portres(port);
- if (ret)
- netdev_info(dev, "Failed freeing resources. ret=%i\n", ret);
-
- ehea_update_firmware_handles();
-
- return ret;
-}
-
-static int ehea_stop(struct net_device *dev)
-{
- int ret;
- struct ehea_port *port = netdev_priv(dev);
-
- netif_info(port, ifdown, dev, "disabling port\n");
-
- set_bit(__EHEA_DISABLE_PORT_RESET, &port->flags);
- cancel_work_sync(&port->reset_task);
- cancel_delayed_work_sync(&port->stats_work);
- mutex_lock(&port->port_lock);
- netif_tx_stop_all_queues(dev);
- port_napi_disable(port);
- ret = ehea_down(dev);
- mutex_unlock(&port->port_lock);
- clear_bit(__EHEA_DISABLE_PORT_RESET, &port->flags);
- return ret;
-}
-
-static void ehea_purge_sq(struct ehea_qp *orig_qp)
-{
- struct ehea_qp qp = *orig_qp;
- struct ehea_qp_init_attr *init_attr = &qp.init_attr;
- struct ehea_swqe *swqe;
- int wqe_index;
- int i;
-
- for (i = 0; i < init_attr->act_nr_send_wqes; i++) {
- swqe = ehea_get_swqe(&qp, &wqe_index);
- swqe->tx_control |= EHEA_SWQE_PURGE;
- }
-}
-
-static void ehea_flush_sq(struct ehea_port *port)
-{
- int i;
-
- for (i = 0; i < port->num_def_qps; i++) {
- struct ehea_port_res *pr = &port->port_res[i];
- int swqe_max = pr->sq_skba_size - 2 - pr->swqe_ll_count;
- int ret;
-
- ret = wait_event_timeout(port->swqe_avail_wq,
- atomic_read(&pr->swqe_avail) >= swqe_max,
- msecs_to_jiffies(100));
-
- if (!ret) {
- pr_err("WARNING: sq not flushed completely\n");
- break;
- }
- }
-}
-
-static int ehea_stop_qps(struct net_device *dev)
-{
- struct ehea_port *port = netdev_priv(dev);
- struct ehea_adapter *adapter = port->adapter;
- struct hcp_modify_qp_cb0 *cb0;
- int ret = -EIO;
- int dret;
- int i;
- u64 hret;
- u64 dummy64 = 0;
- u16 dummy16 = 0;
-
- cb0 = (void *)get_zeroed_page(GFP_KERNEL);
- if (!cb0) {
- ret = -ENOMEM;
- goto out;
- }
-
- for (i = 0; i < (port->num_def_qps); i++) {
- struct ehea_port_res *pr = &port->port_res[i];
- struct ehea_qp *qp = pr->qp;
-
- /* Purge send queue */
- ehea_purge_sq(qp);
-
- /* Disable queue pair */
- hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
- EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
- cb0);
- if (hret != H_SUCCESS) {
- pr_err("query_ehea_qp failed (1)\n");
- goto out;
- }
-
- cb0->qp_ctl_reg = (cb0->qp_ctl_reg & H_QP_CR_RES_STATE) << 8;
- cb0->qp_ctl_reg &= ~H_QP_CR_ENABLED;
-
- hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
- EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG,
- 1), cb0, &dummy64,
- &dummy64, &dummy16, &dummy16);
- if (hret != H_SUCCESS) {
- pr_err("modify_ehea_qp failed (1)\n");
- goto out;
- }
-
- hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
- EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
- cb0);
- if (hret != H_SUCCESS) {
- pr_err("query_ehea_qp failed (2)\n");
- goto out;
- }
-
- /* deregister shared memory regions */
- dret = ehea_rem_smrs(pr);
- if (dret) {
- pr_err("unreg shared memory region failed\n");
- goto out;
- }
- }
-
- ret = 0;
-out:
- free_page((unsigned long)cb0);
-
- return ret;
-}
-
-static void ehea_update_rqs(struct ehea_qp *orig_qp, struct ehea_port_res *pr)
-{
- struct ehea_qp qp = *orig_qp;
- struct ehea_qp_init_attr *init_attr = &qp.init_attr;
- struct ehea_rwqe *rwqe;
- struct sk_buff **skba_rq2 = pr->rq2_skba.arr;
- struct sk_buff **skba_rq3 = pr->rq3_skba.arr;
- struct sk_buff *skb;
- u32 lkey = pr->recv_mr.lkey;
-
-
- int i;
- int index;
-
- for (i = 0; i < init_attr->act_nr_rwqes_rq2 + 1; i++) {
- rwqe = ehea_get_next_rwqe(&qp, 2);
- rwqe->sg_list[0].l_key = lkey;
- index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, rwqe->wr_id);
- skb = skba_rq2[index];
- if (skb)
- rwqe->sg_list[0].vaddr = ehea_map_vaddr(skb->data);
- }
-
- for (i = 0; i < init_attr->act_nr_rwqes_rq3 + 1; i++) {
- rwqe = ehea_get_next_rwqe(&qp, 3);
- rwqe->sg_list[0].l_key = lkey;
- index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, rwqe->wr_id);
- skb = skba_rq3[index];
- if (skb)
- rwqe->sg_list[0].vaddr = ehea_map_vaddr(skb->data);
- }
-}
-
-static int ehea_restart_qps(struct net_device *dev)
-{
- struct ehea_port *port = netdev_priv(dev);
- struct ehea_adapter *adapter = port->adapter;
- int ret = 0;
- int i;
-
- struct hcp_modify_qp_cb0 *cb0;
- u64 hret;
- u64 dummy64 = 0;
- u16 dummy16 = 0;
-
- cb0 = (void *)get_zeroed_page(GFP_KERNEL);
- if (!cb0)
- return -ENOMEM;
-
- for (i = 0; i < (port->num_def_qps); i++) {
- struct ehea_port_res *pr = &port->port_res[i];
- struct ehea_qp *qp = pr->qp;
-
- ret = ehea_gen_smrs(pr);
- if (ret) {
- netdev_err(dev, "creation of shared memory regions failed\n");
- goto out;
- }
-
- ehea_update_rqs(qp, pr);
-
- /* Enable queue pair */
- hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
- EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
- cb0);
- if (hret != H_SUCCESS) {
- netdev_err(dev, "query_ehea_qp failed (1)\n");
- ret = -EFAULT;
- goto out;
- }
-
- cb0->qp_ctl_reg = (cb0->qp_ctl_reg & H_QP_CR_RES_STATE) << 8;
- cb0->qp_ctl_reg |= H_QP_CR_ENABLED;
-
- hret = ehea_h_modify_ehea_qp(adapter->handle, 0, qp->fw_handle,
- EHEA_BMASK_SET(H_QPCB0_QP_CTL_REG,
- 1), cb0, &dummy64,
- &dummy64, &dummy16, &dummy16);
- if (hret != H_SUCCESS) {
- netdev_err(dev, "modify_ehea_qp failed (1)\n");
- ret = -EFAULT;
- goto out;
- }
-
- hret = ehea_h_query_ehea_qp(adapter->handle, 0, qp->fw_handle,
- EHEA_BMASK_SET(H_QPCB0_ALL, 0xFFFF),
- cb0);
- if (hret != H_SUCCESS) {
- netdev_err(dev, "query_ehea_qp failed (2)\n");
- ret = -EFAULT;
- goto out;
- }
-
- /* refill entire queue */
- ehea_refill_rq1(pr, pr->rq1_skba.index, 0);
- ehea_refill_rq2(pr, 0);
- ehea_refill_rq3(pr, 0);
- }
-out:
- free_page((unsigned long)cb0);
-
- return ret;
-}
-
-static void ehea_reset_port(struct work_struct *work)
-{
- int ret;
- struct ehea_port *port =
- container_of(work, struct ehea_port, reset_task);
- struct net_device *dev = port->netdev;
-
- mutex_lock(&dlpar_mem_lock);
- port->resets++;
- mutex_lock(&port->port_lock);
- netif_tx_disable(dev);
-
- port_napi_disable(port);
-
- ehea_down(dev);
-
- ret = ehea_up(dev);
- if (ret)
- goto out;
-
- ehea_set_multicast_list(dev);
-
- netif_info(port, timer, dev, "reset successful\n");
-
- port_napi_enable(port);
-
- netif_tx_wake_all_queues(dev);
-out:
- mutex_unlock(&port->port_lock);
- mutex_unlock(&dlpar_mem_lock);
-}
-
-static void ehea_rereg_mrs(void)
-{
- int ret, i;
- struct ehea_adapter *adapter;
-
- pr_info("LPAR memory changed - re-initializing driver\n");
-
- list_for_each_entry(adapter, &adapter_list, list)
- if (adapter->active_ports) {
- /* Shutdown all ports */
- for (i = 0; i < EHEA_MAX_PORTS; i++) {
- struct ehea_port *port = adapter->port[i];
- struct net_device *dev;
-
- if (!port)
- continue;
-
- dev = port->netdev;
-
- if (dev->flags & IFF_UP) {
- mutex_lock(&port->port_lock);
- netif_tx_disable(dev);
- ehea_flush_sq(port);
- ret = ehea_stop_qps(dev);
- if (ret) {
- mutex_unlock(&port->port_lock);
- goto out;
- }
- port_napi_disable(port);
- mutex_unlock(&port->port_lock);
- }
- reset_sq_restart_flag(port);
- }
-
- /* Unregister old memory region */
- ret = ehea_rem_mr(&adapter->mr);
- if (ret) {
- pr_err("unregister MR failed - driver inoperable!\n");
- goto out;
- }
- }
-
- clear_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
-
- list_for_each_entry(adapter, &adapter_list, list)
- if (adapter->active_ports) {
- /* Register new memory region */
- ret = ehea_reg_kernel_mr(adapter, &adapter->mr);
- if (ret) {
- pr_err("register MR failed - driver inoperable!\n");
- goto out;
- }
-
- /* Restart all ports */
- for (i = 0; i < EHEA_MAX_PORTS; i++) {
- struct ehea_port *port = adapter->port[i];
-
- if (port) {
- struct net_device *dev = port->netdev;
-
- if (dev->flags & IFF_UP) {
- mutex_lock(&port->port_lock);
- ret = ehea_restart_qps(dev);
- if (!ret) {
- check_sqs(port);
- port_napi_enable(port);
- netif_tx_wake_all_queues(dev);
- } else {
- netdev_err(dev, "Unable to restart QPS\n");
- }
- mutex_unlock(&port->port_lock);
- }
- }
- }
- }
- pr_info("re-initializing driver complete\n");
-out:
- return;
-}
-
-static void ehea_tx_watchdog(struct net_device *dev, unsigned int txqueue)
-{
- struct ehea_port *port = netdev_priv(dev);
-
- if (netif_carrier_ok(dev) &&
- !test_bit(__EHEA_STOP_XFER, &ehea_driver_flags))
- ehea_schedule_port_reset(port);
-}
-
-static int ehea_sense_adapter_attr(struct ehea_adapter *adapter)
-{
- struct hcp_query_ehea *cb;
- u64 hret;
- int ret;
-
- cb = (void *)get_zeroed_page(GFP_KERNEL);
- if (!cb) {
- ret = -ENOMEM;
- goto out;
- }
-
- hret = ehea_h_query_ehea(adapter->handle, cb);
-
- if (hret != H_SUCCESS) {
- ret = -EIO;
- goto out_herr;
- }
-
- adapter->max_mc_mac = cb->max_mc_mac - 1;
- ret = 0;
-
-out_herr:
- free_page((unsigned long)cb);
-out:
- return ret;
-}
-
-static int ehea_get_jumboframe_status(struct ehea_port *port, int *jumbo)
-{
- struct hcp_ehea_port_cb4 *cb4;
- u64 hret;
- int ret = 0;
-
- *jumbo = 0;
-
- /* (Try to) enable *jumbo frames */
- cb4 = (void *)get_zeroed_page(GFP_KERNEL);
- if (!cb4) {
- pr_err("no mem for cb4\n");
- ret = -ENOMEM;
- goto out;
- } else {
- hret = ehea_h_query_ehea_port(port->adapter->handle,
- port->logical_port_id,
- H_PORT_CB4,
- H_PORT_CB4_JUMBO, cb4);
- if (hret == H_SUCCESS) {
- if (cb4->jumbo_frame)
- *jumbo = 1;
- else {
- cb4->jumbo_frame = 1;
- hret = ehea_h_modify_ehea_port(port->adapter->
- handle,
- port->
- logical_port_id,
- H_PORT_CB4,
- H_PORT_CB4_JUMBO,
- cb4);
- if (hret == H_SUCCESS)
- *jumbo = 1;
- }
- } else
- ret = -EINVAL;
-
- free_page((unsigned long)cb4);
- }
-out:
- return ret;
-}
-
-static ssize_t log_port_id_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct ehea_port *port = container_of(dev, struct ehea_port, ofdev.dev);
- return sprintf(buf, "%d", port->logical_port_id);
-}
-
-static DEVICE_ATTR_RO(log_port_id);
-
-static void logical_port_release(struct device *dev)
-{
- struct ehea_port *port = container_of(dev, struct ehea_port, ofdev.dev);
- of_node_put(port->ofdev.dev.of_node);
-}
-
-static struct device *ehea_register_port(struct ehea_port *port,
- struct device_node *dn)
-{
- int ret;
-
- port->ofdev.dev.of_node = of_node_get(dn);
- port->ofdev.dev.parent = &port->adapter->ofdev->dev;
- port->ofdev.dev.bus = &ibmebus_bus_type;
-
- dev_set_name(&port->ofdev.dev, "port%d", port_name_cnt++);
- port->ofdev.dev.release = logical_port_release;
-
- ret = of_device_register(&port->ofdev);
- if (ret) {
- pr_err("failed to register device. ret=%d\n", ret);
- put_device(&port->ofdev.dev);
- goto out;
- }
-
- ret = device_create_file(&port->ofdev.dev, &dev_attr_log_port_id);
- if (ret) {
- pr_err("failed to register attributes, ret=%d\n", ret);
- goto out_unreg_of_dev;
- }
-
- return &port->ofdev.dev;
-
-out_unreg_of_dev:
- of_device_unregister(&port->ofdev);
-out:
- return NULL;
-}
-
-static void ehea_unregister_port(struct ehea_port *port)
-{
- device_remove_file(&port->ofdev.dev, &dev_attr_log_port_id);
- of_device_unregister(&port->ofdev);
-}
-
-static const struct net_device_ops ehea_netdev_ops = {
- .ndo_open = ehea_open,
- .ndo_stop = ehea_stop,
- .ndo_start_xmit = ehea_start_xmit,
- .ndo_get_stats64 = ehea_get_stats64,
- .ndo_set_mac_address = ehea_set_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
- .ndo_set_rx_mode = ehea_set_multicast_list,
- .ndo_vlan_rx_add_vid = ehea_vlan_rx_add_vid,
- .ndo_vlan_rx_kill_vid = ehea_vlan_rx_kill_vid,
- .ndo_tx_timeout = ehea_tx_watchdog,
-};
-
-static struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter,
- u32 logical_port_id,
- struct device_node *dn)
-{
- int ret;
- struct net_device *dev;
- struct ehea_port *port;
- struct device *port_dev;
- int jumbo;
-
- /* allocate memory for the port structures */
- dev = alloc_etherdev_mq(sizeof(struct ehea_port), EHEA_MAX_PORT_RES);
-
- if (!dev) {
- ret = -ENOMEM;
- goto out_err;
- }
-
- port = netdev_priv(dev);
-
- mutex_init(&port->port_lock);
- port->state = EHEA_PORT_DOWN;
- port->sig_comp_iv = sq_entries / 10;
-
- port->adapter = adapter;
- port->netdev = dev;
- port->logical_port_id = logical_port_id;
-
- port->msg_enable = netif_msg_init(msg_level, EHEA_MSG_DEFAULT);
-
- port->mc_list = kzalloc_obj(struct ehea_mc_list);
- if (!port->mc_list) {
- ret = -ENOMEM;
- goto out_free_ethdev;
- }
-
- INIT_LIST_HEAD(&port->mc_list->list);
-
- ret = ehea_sense_port_attr(port);
- if (ret)
- goto out_free_mc_list;
-
- netif_set_real_num_rx_queues(dev, port->num_def_qps);
- netif_set_real_num_tx_queues(dev, port->num_def_qps);
-
- port_dev = ehea_register_port(port, dn);
- if (!port_dev)
- goto out_free_mc_list;
-
- SET_NETDEV_DEV(dev, port_dev);
-
- /* initialize net_device structure */
- eth_hw_addr_set(dev, (u8 *)&port->mac_addr);
-
- dev->netdev_ops = &ehea_netdev_ops;
- ehea_set_ethtool_ops(dev);
-
- dev->hw_features = NETIF_F_SG | NETIF_F_TSO |
- NETIF_F_IP_CSUM | NETIF_F_HW_VLAN_CTAG_TX;
- dev->features = NETIF_F_SG | NETIF_F_TSO |
- NETIF_F_HIGHDMA | NETIF_F_IP_CSUM |
- NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
- NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXCSUM;
- dev->vlan_features = NETIF_F_SG | NETIF_F_TSO | NETIF_F_HIGHDMA |
- NETIF_F_IP_CSUM;
- dev->watchdog_timeo = EHEA_WATCH_DOG_TIMEOUT;
-
- /* MTU range: 68 - 9022 */
- dev->min_mtu = ETH_MIN_MTU;
- dev->max_mtu = EHEA_MAX_PACKET_SIZE;
-
- INIT_WORK(&port->reset_task, ehea_reset_port);
- INIT_DELAYED_WORK(&port->stats_work, ehea_update_stats);
-
- init_waitqueue_head(&port->swqe_avail_wq);
- init_waitqueue_head(&port->restart_wq);
-
- ret = register_netdev(dev);
- if (ret) {
- pr_err("register_netdev failed. ret=%d\n", ret);
- goto out_unreg_port;
- }
-
- ret = ehea_get_jumboframe_status(port, &jumbo);
- if (ret)
- netdev_err(dev, "failed determining jumbo frame status\n");
-
- netdev_info(dev, "Jumbo frames are %sabled\n",
- jumbo == 1 ? "en" : "dis");
-
- adapter->active_ports++;
-
- return port;
-
-out_unreg_port:
- ehea_unregister_port(port);
-
-out_free_mc_list:
- kfree(port->mc_list);
-
-out_free_ethdev:
- free_netdev(dev);
-
-out_err:
- pr_err("setting up logical port with id=%d failed, ret=%d\n",
- logical_port_id, ret);
- return NULL;
-}
-
-static void ehea_shutdown_single_port(struct ehea_port *port)
-{
- struct ehea_adapter *adapter = port->adapter;
-
- cancel_work_sync(&port->reset_task);
- cancel_delayed_work_sync(&port->stats_work);
- unregister_netdev(port->netdev);
- ehea_unregister_port(port);
- kfree(port->mc_list);
- free_netdev(port->netdev);
- adapter->active_ports--;
-}
-
-static int ehea_setup_ports(struct ehea_adapter *adapter)
-{
- struct device_node *lhea_dn;
- struct device_node *eth_dn;
-
- const u32 *dn_log_port_id;
- int i = 0;
-
- lhea_dn = adapter->ofdev->dev.of_node;
- for_each_child_of_node(lhea_dn, eth_dn) {
- dn_log_port_id = of_get_property(eth_dn, "ibm,hea-port-no",
- NULL);
- if (!dn_log_port_id) {
- pr_err("bad device node: eth_dn name=%pOF\n", eth_dn);
- continue;
- }
-
- if (ehea_add_adapter_mr(adapter)) {
- pr_err("creating MR failed\n");
- of_node_put(eth_dn);
- return -EIO;
- }
-
- adapter->port[i] = ehea_setup_single_port(adapter,
- *dn_log_port_id,
- eth_dn);
- if (adapter->port[i])
- netdev_info(adapter->port[i]->netdev,
- "logical port id #%d\n", *dn_log_port_id);
- else
- ehea_remove_adapter_mr(adapter);
-
- i++;
- }
- return 0;
-}
-
-static struct device_node *ehea_get_eth_dn(struct ehea_adapter *adapter,
- u32 logical_port_id)
-{
- struct device_node *lhea_dn;
- struct device_node *eth_dn;
- const u32 *dn_log_port_id;
-
- lhea_dn = adapter->ofdev->dev.of_node;
- for_each_child_of_node(lhea_dn, eth_dn) {
- dn_log_port_id = of_get_property(eth_dn, "ibm,hea-port-no",
- NULL);
- if (dn_log_port_id)
- if (*dn_log_port_id == logical_port_id)
- return eth_dn;
- }
-
- return NULL;
-}
-
-static ssize_t probe_port_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ehea_adapter *adapter = dev_get_drvdata(dev);
- struct ehea_port *port;
- struct device_node *eth_dn = NULL;
- int i;
-
- u32 logical_port_id;
-
- sscanf(buf, "%d", &logical_port_id);
-
- port = ehea_get_port(adapter, logical_port_id);
-
- if (port) {
- netdev_info(port->netdev, "adding port with logical port id=%d failed: port already configured\n",
- logical_port_id);
- return -EINVAL;
- }
-
- eth_dn = ehea_get_eth_dn(adapter, logical_port_id);
-
- if (!eth_dn) {
- pr_info("no logical port with id %d found\n", logical_port_id);
- return -EINVAL;
- }
-
- if (ehea_add_adapter_mr(adapter)) {
- pr_err("creating MR failed\n");
- of_node_put(eth_dn);
- return -EIO;
- }
-
- port = ehea_setup_single_port(adapter, logical_port_id, eth_dn);
-
- of_node_put(eth_dn);
-
- if (port) {
- for (i = 0; i < EHEA_MAX_PORTS; i++)
- if (!adapter->port[i]) {
- adapter->port[i] = port;
- break;
- }
-
- netdev_info(port->netdev, "added: (logical port id=%d)\n",
- logical_port_id);
- } else {
- ehea_remove_adapter_mr(adapter);
- return -EIO;
- }
-
- return (ssize_t) count;
-}
-
-static ssize_t remove_port_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct ehea_adapter *adapter = dev_get_drvdata(dev);
- struct ehea_port *port;
- int i;
- u32 logical_port_id;
-
- sscanf(buf, "%d", &logical_port_id);
-
- port = ehea_get_port(adapter, logical_port_id);
-
- if (port) {
- netdev_info(port->netdev, "removed: (logical port id=%d)\n",
- logical_port_id);
-
- ehea_shutdown_single_port(port);
-
- for (i = 0; i < EHEA_MAX_PORTS; i++)
- if (adapter->port[i] == port) {
- adapter->port[i] = NULL;
- break;
- }
- } else {
- pr_err("removing port with logical port id=%d failed. port not configured.\n",
- logical_port_id);
- return -EINVAL;
- }
-
- ehea_remove_adapter_mr(adapter);
-
- return (ssize_t) count;
-}
-
-static DEVICE_ATTR_WO(probe_port);
-static DEVICE_ATTR_WO(remove_port);
-
-static int ehea_create_device_sysfs(struct platform_device *dev)
-{
- int ret = device_create_file(&dev->dev, &dev_attr_probe_port);
- if (ret)
- goto out;
-
- ret = device_create_file(&dev->dev, &dev_attr_remove_port);
- if (ret)
- device_remove_file(&dev->dev, &dev_attr_probe_port);
-out:
- return ret;
-}
-
-static void ehea_remove_device_sysfs(struct platform_device *dev)
-{
- device_remove_file(&dev->dev, &dev_attr_probe_port);
- device_remove_file(&dev->dev, &dev_attr_remove_port);
-}
-
-static int ehea_reboot_notifier(struct notifier_block *nb,
- unsigned long action, void *unused)
-{
- if (action == SYS_RESTART) {
- pr_info("Reboot: freeing all eHEA resources\n");
- ibmebus_unregister_driver(&ehea_driver);
- }
- return NOTIFY_DONE;
-}
-
-static struct notifier_block ehea_reboot_nb = {
- .notifier_call = ehea_reboot_notifier,
-};
-
-static int ehea_mem_notifier(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- int ret = NOTIFY_BAD;
- struct memory_notify *arg = data;
-
- mutex_lock(&dlpar_mem_lock);
-
- switch (action) {
- case MEM_CANCEL_OFFLINE:
- pr_info("memory offlining canceled");
- fallthrough; /* re-add canceled memory block */
-
- case MEM_ONLINE:
- pr_info("memory is going online");
- set_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
- if (ehea_add_sect_bmap(arg->start_pfn, arg->nr_pages))
- goto out_unlock;
- ehea_rereg_mrs();
- break;
-
- case MEM_GOING_OFFLINE:
- pr_info("memory is going offline");
- set_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
- if (ehea_rem_sect_bmap(arg->start_pfn, arg->nr_pages))
- goto out_unlock;
- ehea_rereg_mrs();
- break;
-
- default:
- break;
- }
-
- ehea_update_firmware_handles();
- ret = NOTIFY_OK;
-
-out_unlock:
- mutex_unlock(&dlpar_mem_lock);
- return ret;
-}
-
-static struct notifier_block ehea_mem_nb = {
- .notifier_call = ehea_mem_notifier,
-};
-
-static void ehea_crash_handler(void)
-{
- int i;
-
- if (ehea_fw_handles.arr)
- for (i = 0; i < ehea_fw_handles.num_entries; i++)
- ehea_h_free_resource(ehea_fw_handles.arr[i].adh,
- ehea_fw_handles.arr[i].fwh,
- FORCE_FREE);
-
- if (ehea_bcmc_regs.arr)
- for (i = 0; i < ehea_bcmc_regs.num_entries; i++)
- ehea_h_reg_dereg_bcmc(ehea_bcmc_regs.arr[i].adh,
- ehea_bcmc_regs.arr[i].port_id,
- ehea_bcmc_regs.arr[i].reg_type,
- ehea_bcmc_regs.arr[i].macaddr,
- 0, H_DEREG_BCMC);
-}
-
-static atomic_t ehea_memory_hooks_registered;
-
-/* Register memory hooks on probe of first adapter */
-static int ehea_register_memory_hooks(void)
-{
- int ret = 0;
-
- if (atomic_inc_return(&ehea_memory_hooks_registered) > 1)
- return 0;
-
- ret = ehea_create_busmap();
- if (ret) {
- pr_info("ehea_create_busmap failed\n");
- goto out;
- }
-
- ret = register_reboot_notifier(&ehea_reboot_nb);
- if (ret) {
- pr_info("register_reboot_notifier failed\n");
- goto out;
- }
-
- ret = register_memory_notifier(&ehea_mem_nb);
- if (ret) {
- pr_info("register_memory_notifier failed\n");
- goto out2;
- }
-
- ret = crash_shutdown_register(ehea_crash_handler);
- if (ret) {
- pr_info("crash_shutdown_register failed\n");
- goto out3;
- }
-
- return 0;
-
-out3:
- unregister_memory_notifier(&ehea_mem_nb);
-out2:
- unregister_reboot_notifier(&ehea_reboot_nb);
-out:
- atomic_dec(&ehea_memory_hooks_registered);
- return ret;
-}
-
-static void ehea_unregister_memory_hooks(void)
-{
- /* Only remove the hooks if we've registered them */
- if (atomic_read(&ehea_memory_hooks_registered) == 0)
- return;
-
- unregister_reboot_notifier(&ehea_reboot_nb);
- if (crash_shutdown_unregister(ehea_crash_handler))
- pr_info("failed unregistering crash handler\n");
- unregister_memory_notifier(&ehea_mem_nb);
-}
-
-static int ehea_probe_adapter(struct platform_device *dev)
-{
- struct ehea_adapter *adapter;
- const u64 *adapter_handle;
- int ret;
- int i;
-
- ret = ehea_register_memory_hooks();
- if (ret)
- return ret;
-
- if (!dev || !dev->dev.of_node) {
- pr_err("Invalid ibmebus device probed\n");
- return -EINVAL;
- }
-
- adapter = devm_kzalloc(&dev->dev, sizeof(*adapter), GFP_KERNEL);
- if (!adapter) {
- ret = -ENOMEM;
- dev_err(&dev->dev, "no mem for ehea_adapter\n");
- goto out;
- }
-
- list_add(&adapter->list, &adapter_list);
-
- adapter->ofdev = dev;
-
- adapter_handle = of_get_property(dev->dev.of_node, "ibm,hea-handle",
- NULL);
- if (adapter_handle)
- adapter->handle = *adapter_handle;
-
- if (!adapter->handle) {
- dev_err(&dev->dev, "failed getting handle for adapter"
- " '%pOF'\n", dev->dev.of_node);
- ret = -ENODEV;
- goto out_free_ad;
- }
-
- adapter->pd = EHEA_PD_ID;
-
- platform_set_drvdata(dev, adapter);
-
-
- /* initialize adapter and ports */
- /* get adapter properties */
- ret = ehea_sense_adapter_attr(adapter);
- if (ret) {
- dev_err(&dev->dev, "sense_adapter_attr failed: %d\n", ret);
- goto out_free_ad;
- }
-
- adapter->neq = ehea_create_eq(adapter,
- EHEA_NEQ, EHEA_MAX_ENTRIES_EQ, 1);
- if (!adapter->neq) {
- ret = -EIO;
- dev_err(&dev->dev, "NEQ creation failed\n");
- goto out_free_ad;
- }
-
- tasklet_setup(&adapter->neq_tasklet, ehea_neq_tasklet);
-
- ret = ehea_create_device_sysfs(dev);
- if (ret)
- goto out_kill_eq;
-
- ret = ehea_setup_ports(adapter);
- if (ret) {
- dev_err(&dev->dev, "setup_ports failed\n");
- goto out_rem_dev_sysfs;
- }
-
- ret = ibmebus_request_irq(adapter->neq->attr.ist1,
- ehea_interrupt_neq, 0,
- "ehea_neq", adapter);
- if (ret) {
- dev_err(&dev->dev, "requesting NEQ IRQ failed\n");
- goto out_shutdown_ports;
- }
-
- /* Handle any events that might be pending. */
- tasklet_hi_schedule(&adapter->neq_tasklet);
-
- ret = 0;
- goto out;
-
-out_shutdown_ports:
- for (i = 0; i < EHEA_MAX_PORTS; i++)
- if (adapter->port[i]) {
- ehea_shutdown_single_port(adapter->port[i]);
- adapter->port[i] = NULL;
- }
-
-out_rem_dev_sysfs:
- ehea_remove_device_sysfs(dev);
-
-out_kill_eq:
- ehea_destroy_eq(adapter->neq);
-
-out_free_ad:
- list_del(&adapter->list);
-
-out:
- ehea_update_firmware_handles();
-
- return ret;
-}
-
-static void ehea_remove(struct platform_device *dev)
-{
- struct ehea_adapter *adapter = platform_get_drvdata(dev);
- int i;
-
- for (i = 0; i < EHEA_MAX_PORTS; i++)
- if (adapter->port[i]) {
- ehea_shutdown_single_port(adapter->port[i]);
- adapter->port[i] = NULL;
- }
-
- ehea_remove_device_sysfs(dev);
-
- ibmebus_free_irq(adapter->neq->attr.ist1, adapter);
- tasklet_kill(&adapter->neq_tasklet);
-
- ehea_destroy_eq(adapter->neq);
- ehea_remove_adapter_mr(adapter);
- list_del(&adapter->list);
-
- ehea_update_firmware_handles();
-}
-
-static int check_module_parm(void)
-{
- int ret = 0;
-
- if ((rq1_entries < EHEA_MIN_ENTRIES_QP) ||
- (rq1_entries > EHEA_MAX_ENTRIES_RQ1)) {
- pr_info("Bad parameter: rq1_entries\n");
- ret = -EINVAL;
- }
- if ((rq2_entries < EHEA_MIN_ENTRIES_QP) ||
- (rq2_entries > EHEA_MAX_ENTRIES_RQ2)) {
- pr_info("Bad parameter: rq2_entries\n");
- ret = -EINVAL;
- }
- if ((rq3_entries < EHEA_MIN_ENTRIES_QP) ||
- (rq3_entries > EHEA_MAX_ENTRIES_RQ3)) {
- pr_info("Bad parameter: rq3_entries\n");
- ret = -EINVAL;
- }
- if ((sq_entries < EHEA_MIN_ENTRIES_QP) ||
- (sq_entries > EHEA_MAX_ENTRIES_SQ)) {
- pr_info("Bad parameter: sq_entries\n");
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static ssize_t capabilities_show(struct device_driver *drv, char *buf)
-{
- return sprintf(buf, "%d", EHEA_CAPABILITIES);
-}
-
-static DRIVER_ATTR_RO(capabilities);
-
-static int __init ehea_module_init(void)
-{
- int ret;
-
- pr_info("IBM eHEA ethernet device driver (Release %s)\n", DRV_VERSION);
-
- memset(&ehea_fw_handles, 0, sizeof(ehea_fw_handles));
- memset(&ehea_bcmc_regs, 0, sizeof(ehea_bcmc_regs));
-
- mutex_init(&ehea_fw_handles.lock);
- spin_lock_init(&ehea_bcmc_regs.lock);
-
- ret = check_module_parm();
- if (ret)
- goto out;
-
- ret = ibmebus_register_driver(&ehea_driver);
- if (ret) {
- pr_err("failed registering eHEA device driver on ebus\n");
- goto out;
- }
-
- ret = driver_create_file(&ehea_driver.driver,
- &driver_attr_capabilities);
- if (ret) {
- pr_err("failed to register capabilities attribute, ret=%d\n",
- ret);
- goto out2;
- }
-
- return ret;
-
-out2:
- ibmebus_unregister_driver(&ehea_driver);
-out:
- return ret;
-}
-
-static void __exit ehea_module_exit(void)
-{
- driver_remove_file(&ehea_driver.driver, &driver_attr_capabilities);
- ibmebus_unregister_driver(&ehea_driver);
- ehea_unregister_memory_hooks();
- kfree(ehea_fw_handles.arr);
- kfree(ehea_bcmc_regs.arr);
- ehea_destroy_busmap();
-}
-
-module_init(ehea_module_init);
-module_exit(ehea_module_exit);
diff --git a/drivers/net/ethernet/ibm/ehea/ehea_phyp.c b/drivers/net/ethernet/ibm/ehea/ehea_phyp.c
deleted file mode 100644
index e63716e139f5..000000000000
--- a/drivers/net/ethernet/ibm/ehea/ehea_phyp.c
+++ /dev/null
@@ -1,612 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * linux/drivers/net/ethernet/ibm/ehea/ehea_phyp.c
- *
- * eHEA ethernet device driver for IBM eServer System p
- *
- * (C) Copyright IBM Corp. 2006
- *
- * Authors:
- * Christoph Raisch <raisch@de.ibm.com>
- * Jan-Bernd Themann <themann@de.ibm.com>
- * Thomas Klein <tklein@de.ibm.com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include "ehea_phyp.h"
-
-
-static inline u16 get_order_of_qentries(u16 queue_entries)
-{
- u8 ld = 1; /* logarithmus dualis */
- while (((1U << ld) - 1) < queue_entries)
- ld++;
- return ld - 1;
-}
-
-/* Defines for H_CALL H_ALLOC_RESOURCE */
-#define H_ALL_RES_TYPE_QP 1
-#define H_ALL_RES_TYPE_CQ 2
-#define H_ALL_RES_TYPE_EQ 3
-#define H_ALL_RES_TYPE_MR 5
-#define H_ALL_RES_TYPE_MW 6
-
-static long ehea_plpar_hcall_norets(unsigned long opcode,
- unsigned long arg1,
- unsigned long arg2,
- unsigned long arg3,
- unsigned long arg4,
- unsigned long arg5,
- unsigned long arg6,
- unsigned long arg7)
-{
- long ret;
- int i, sleep_msecs;
-
- for (i = 0; i < 5; i++) {
- ret = plpar_hcall_norets(opcode, arg1, arg2, arg3, arg4,
- arg5, arg6, arg7);
-
- if (H_IS_LONG_BUSY(ret)) {
- sleep_msecs = get_longbusy_msecs(ret);
- msleep_interruptible(sleep_msecs);
- continue;
- }
-
- if (ret < H_SUCCESS)
- pr_err("opcode=%lx ret=%lx"
- " arg1=%lx arg2=%lx arg3=%lx arg4=%lx"
- " arg5=%lx arg6=%lx arg7=%lx\n",
- opcode, ret,
- arg1, arg2, arg3, arg4, arg5, arg6, arg7);
-
- return ret;
- }
-
- return H_BUSY;
-}
-
-static long ehea_plpar_hcall9(unsigned long opcode,
- unsigned long *outs, /* array of 9 outputs */
- unsigned long arg1,
- unsigned long arg2,
- unsigned long arg3,
- unsigned long arg4,
- unsigned long arg5,
- unsigned long arg6,
- unsigned long arg7,
- unsigned long arg8,
- unsigned long arg9)
-{
- long ret;
- int i, sleep_msecs;
- u8 cb_cat;
-
- for (i = 0; i < 5; i++) {
- ret = plpar_hcall9(opcode, outs,
- arg1, arg2, arg3, arg4, arg5,
- arg6, arg7, arg8, arg9);
-
- if (H_IS_LONG_BUSY(ret)) {
- sleep_msecs = get_longbusy_msecs(ret);
- msleep_interruptible(sleep_msecs);
- continue;
- }
-
- cb_cat = EHEA_BMASK_GET(H_MEHEAPORT_CAT, arg2);
-
- if ((ret < H_SUCCESS) && !(((ret == H_AUTHORITY)
- && (opcode == H_MODIFY_HEA_PORT))
- && (((cb_cat == H_PORT_CB4) && ((arg3 == H_PORT_CB4_JUMBO)
- || (arg3 == H_PORT_CB4_SPEED))) || ((cb_cat == H_PORT_CB7)
- && (arg3 == H_PORT_CB7_DUCQPN)))))
- pr_err("opcode=%lx ret=%lx"
- " arg1=%lx arg2=%lx arg3=%lx arg4=%lx"
- " arg5=%lx arg6=%lx arg7=%lx arg8=%lx"
- " arg9=%lx"
- " out1=%lx out2=%lx out3=%lx out4=%lx"
- " out5=%lx out6=%lx out7=%lx out8=%lx"
- " out9=%lx\n",
- opcode, ret,
- arg1, arg2, arg3, arg4, arg5,
- arg6, arg7, arg8, arg9,
- outs[0], outs[1], outs[2], outs[3], outs[4],
- outs[5], outs[6], outs[7], outs[8]);
- return ret;
- }
-
- return H_BUSY;
-}
-
-u64 ehea_h_query_ehea_qp(const u64 adapter_handle, const u8 qp_category,
- const u64 qp_handle, const u64 sel_mask, void *cb_addr)
-{
- return ehea_plpar_hcall_norets(H_QUERY_HEA_QP,
- adapter_handle, /* R4 */
- qp_category, /* R5 */
- qp_handle, /* R6 */
- sel_mask, /* R7 */
- __pa(cb_addr), /* R8 */
- 0, 0);
-}
-
-/* input param R5 */
-#define H_ALL_RES_QP_EQPO EHEA_BMASK_IBM(9, 11)
-#define H_ALL_RES_QP_QPP EHEA_BMASK_IBM(12, 12)
-#define H_ALL_RES_QP_RQR EHEA_BMASK_IBM(13, 15)
-#define H_ALL_RES_QP_EQEG EHEA_BMASK_IBM(16, 16)
-#define H_ALL_RES_QP_LL_QP EHEA_BMASK_IBM(17, 17)
-#define H_ALL_RES_QP_DMA128 EHEA_BMASK_IBM(19, 19)
-#define H_ALL_RES_QP_HSM EHEA_BMASK_IBM(20, 21)
-#define H_ALL_RES_QP_SIGT EHEA_BMASK_IBM(22, 23)
-#define H_ALL_RES_QP_TENURE EHEA_BMASK_IBM(48, 55)
-#define H_ALL_RES_QP_RES_TYP EHEA_BMASK_IBM(56, 63)
-
-/* input param R9 */
-#define H_ALL_RES_QP_TOKEN EHEA_BMASK_IBM(0, 31)
-#define H_ALL_RES_QP_PD EHEA_BMASK_IBM(32, 63)
-
-/* input param R10 */
-#define H_ALL_RES_QP_MAX_SWQE EHEA_BMASK_IBM(4, 7)
-#define H_ALL_RES_QP_MAX_R1WQE EHEA_BMASK_IBM(12, 15)
-#define H_ALL_RES_QP_MAX_R2WQE EHEA_BMASK_IBM(20, 23)
-#define H_ALL_RES_QP_MAX_R3WQE EHEA_BMASK_IBM(28, 31)
-/* Max Send Scatter Gather Elements */
-#define H_ALL_RES_QP_MAX_SSGE EHEA_BMASK_IBM(37, 39)
-#define H_ALL_RES_QP_MAX_R1SGE EHEA_BMASK_IBM(45, 47)
-/* Max Receive SG Elements RQ1 */
-#define H_ALL_RES_QP_MAX_R2SGE EHEA_BMASK_IBM(53, 55)
-#define H_ALL_RES_QP_MAX_R3SGE EHEA_BMASK_IBM(61, 63)
-
-/* input param R11 */
-#define H_ALL_RES_QP_SWQE_IDL EHEA_BMASK_IBM(0, 7)
-/* max swqe immediate data length */
-#define H_ALL_RES_QP_PORT_NUM EHEA_BMASK_IBM(48, 63)
-
-/* input param R12 */
-#define H_ALL_RES_QP_TH_RQ2 EHEA_BMASK_IBM(0, 15)
-/* Threshold RQ2 */
-#define H_ALL_RES_QP_TH_RQ3 EHEA_BMASK_IBM(16, 31)
-/* Threshold RQ3 */
-
-/* output param R6 */
-#define H_ALL_RES_QP_ACT_SWQE EHEA_BMASK_IBM(0, 15)
-#define H_ALL_RES_QP_ACT_R1WQE EHEA_BMASK_IBM(16, 31)
-#define H_ALL_RES_QP_ACT_R2WQE EHEA_BMASK_IBM(32, 47)
-#define H_ALL_RES_QP_ACT_R3WQE EHEA_BMASK_IBM(48, 63)
-
-/* output param, R7 */
-#define H_ALL_RES_QP_ACT_SSGE EHEA_BMASK_IBM(0, 7)
-#define H_ALL_RES_QP_ACT_R1SGE EHEA_BMASK_IBM(8, 15)
-#define H_ALL_RES_QP_ACT_R2SGE EHEA_BMASK_IBM(16, 23)
-#define H_ALL_RES_QP_ACT_R3SGE EHEA_BMASK_IBM(24, 31)
-#define H_ALL_RES_QP_ACT_SWQE_IDL EHEA_BMASK_IBM(32, 39)
-
-/* output param R8,R9 */
-#define H_ALL_RES_QP_SIZE_SQ EHEA_BMASK_IBM(0, 31)
-#define H_ALL_RES_QP_SIZE_RQ1 EHEA_BMASK_IBM(32, 63)
-#define H_ALL_RES_QP_SIZE_RQ2 EHEA_BMASK_IBM(0, 31)
-#define H_ALL_RES_QP_SIZE_RQ3 EHEA_BMASK_IBM(32, 63)
-
-/* output param R11,R12 */
-#define H_ALL_RES_QP_LIOBN_SQ EHEA_BMASK_IBM(0, 31)
-#define H_ALL_RES_QP_LIOBN_RQ1 EHEA_BMASK_IBM(32, 63)
-#define H_ALL_RES_QP_LIOBN_RQ2 EHEA_BMASK_IBM(0, 31)
-#define H_ALL_RES_QP_LIOBN_RQ3 EHEA_BMASK_IBM(32, 63)
-
-u64 ehea_h_alloc_resource_qp(const u64 adapter_handle,
- struct ehea_qp_init_attr *init_attr, const u32 pd,
- u64 *qp_handle, struct h_epas *h_epas)
-{
- u64 hret;
- unsigned long outs[PLPAR_HCALL9_BUFSIZE];
-
- u64 allocate_controls =
- EHEA_BMASK_SET(H_ALL_RES_QP_EQPO, init_attr->low_lat_rq1 ? 1 : 0)
- | EHEA_BMASK_SET(H_ALL_RES_QP_QPP, 0)
- | EHEA_BMASK_SET(H_ALL_RES_QP_RQR, 6) /* rq1 & rq2 & rq3 */
- | EHEA_BMASK_SET(H_ALL_RES_QP_EQEG, 0) /* EQE gen. disabled */
- | EHEA_BMASK_SET(H_ALL_RES_QP_LL_QP, init_attr->low_lat_rq1)
- | EHEA_BMASK_SET(H_ALL_RES_QP_DMA128, 0)
- | EHEA_BMASK_SET(H_ALL_RES_QP_HSM, 0)
- | EHEA_BMASK_SET(H_ALL_RES_QP_SIGT, init_attr->signalingtype)
- | EHEA_BMASK_SET(H_ALL_RES_QP_RES_TYP, H_ALL_RES_TYPE_QP);
-
- u64 r9_reg = EHEA_BMASK_SET(H_ALL_RES_QP_PD, pd)
- | EHEA_BMASK_SET(H_ALL_RES_QP_TOKEN, init_attr->qp_token);
-
- u64 max_r10_reg =
- EHEA_BMASK_SET(H_ALL_RES_QP_MAX_SWQE,
- get_order_of_qentries(init_attr->max_nr_send_wqes))
- | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R1WQE,
- get_order_of_qentries(init_attr->max_nr_rwqes_rq1))
- | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R2WQE,
- get_order_of_qentries(init_attr->max_nr_rwqes_rq2))
- | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R3WQE,
- get_order_of_qentries(init_attr->max_nr_rwqes_rq3))
- | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_SSGE, init_attr->wqe_size_enc_sq)
- | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R1SGE,
- init_attr->wqe_size_enc_rq1)
- | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R2SGE,
- init_attr->wqe_size_enc_rq2)
- | EHEA_BMASK_SET(H_ALL_RES_QP_MAX_R3SGE,
- init_attr->wqe_size_enc_rq3);
-
- u64 r11_in =
- EHEA_BMASK_SET(H_ALL_RES_QP_SWQE_IDL, init_attr->swqe_imm_data_len)
- | EHEA_BMASK_SET(H_ALL_RES_QP_PORT_NUM, init_attr->port_nr);
- u64 threshold =
- EHEA_BMASK_SET(H_ALL_RES_QP_TH_RQ2, init_attr->rq2_threshold)
- | EHEA_BMASK_SET(H_ALL_RES_QP_TH_RQ3, init_attr->rq3_threshold);
-
- hret = ehea_plpar_hcall9(H_ALLOC_HEA_RESOURCE,
- outs,
- adapter_handle, /* R4 */
- allocate_controls, /* R5 */
- init_attr->send_cq_handle, /* R6 */
- init_attr->recv_cq_handle, /* R7 */
- init_attr->aff_eq_handle, /* R8 */
- r9_reg, /* R9 */
- max_r10_reg, /* R10 */
- r11_in, /* R11 */
- threshold); /* R12 */
-
- *qp_handle = outs[0];
- init_attr->qp_nr = (u32)outs[1];
-
- init_attr->act_nr_send_wqes =
- (u16)EHEA_BMASK_GET(H_ALL_RES_QP_ACT_SWQE, outs[2]);
- init_attr->act_nr_rwqes_rq1 =
- (u16)EHEA_BMASK_GET(H_ALL_RES_QP_ACT_R1WQE, outs[2]);
- init_attr->act_nr_rwqes_rq2 =
- (u16)EHEA_BMASK_GET(H_ALL_RES_QP_ACT_R2WQE, outs[2]);
- init_attr->act_nr_rwqes_rq3 =
- (u16)EHEA_BMASK_GET(H_ALL_RES_QP_ACT_R3WQE, outs[2]);
-
- init_attr->act_wqe_size_enc_sq = init_attr->wqe_size_enc_sq;
- init_attr->act_wqe_size_enc_rq1 = init_attr->wqe_size_enc_rq1;
- init_attr->act_wqe_size_enc_rq2 = init_attr->wqe_size_enc_rq2;
- init_attr->act_wqe_size_enc_rq3 = init_attr->wqe_size_enc_rq3;
-
- init_attr->nr_sq_pages =
- (u32)EHEA_BMASK_GET(H_ALL_RES_QP_SIZE_SQ, outs[4]);
- init_attr->nr_rq1_pages =
- (u32)EHEA_BMASK_GET(H_ALL_RES_QP_SIZE_RQ1, outs[4]);
- init_attr->nr_rq2_pages =
- (u32)EHEA_BMASK_GET(H_ALL_RES_QP_SIZE_RQ2, outs[5]);
- init_attr->nr_rq3_pages =
- (u32)EHEA_BMASK_GET(H_ALL_RES_QP_SIZE_RQ3, outs[5]);
-
- init_attr->liobn_sq =
- (u32)EHEA_BMASK_GET(H_ALL_RES_QP_LIOBN_SQ, outs[7]);
- init_attr->liobn_rq1 =
- (u32)EHEA_BMASK_GET(H_ALL_RES_QP_LIOBN_RQ1, outs[7]);
- init_attr->liobn_rq2 =
- (u32)EHEA_BMASK_GET(H_ALL_RES_QP_LIOBN_RQ2, outs[8]);
- init_attr->liobn_rq3 =
- (u32)EHEA_BMASK_GET(H_ALL_RES_QP_LIOBN_RQ3, outs[8]);
-
- if (!hret)
- hcp_epas_ctor(h_epas, outs[6], outs[6]);
-
- return hret;
-}
-
-u64 ehea_h_alloc_resource_cq(const u64 adapter_handle,
- struct ehea_cq_attr *cq_attr,
- u64 *cq_handle, struct h_epas *epas)
-{
- u64 hret;
- unsigned long outs[PLPAR_HCALL9_BUFSIZE];
-
- hret = ehea_plpar_hcall9(H_ALLOC_HEA_RESOURCE,
- outs,
- adapter_handle, /* R4 */
- H_ALL_RES_TYPE_CQ, /* R5 */
- cq_attr->eq_handle, /* R6 */
- cq_attr->cq_token, /* R7 */
- cq_attr->max_nr_of_cqes, /* R8 */
- 0, 0, 0, 0); /* R9-R12 */
-
- *cq_handle = outs[0];
- cq_attr->act_nr_of_cqes = outs[3];
- cq_attr->nr_pages = outs[4];
-
- if (!hret)
- hcp_epas_ctor(epas, outs[5], outs[6]);
-
- return hret;
-}
-
-/* Defines for H_CALL H_ALLOC_RESOURCE */
-#define H_ALL_RES_TYPE_QP 1
-#define H_ALL_RES_TYPE_CQ 2
-#define H_ALL_RES_TYPE_EQ 3
-#define H_ALL_RES_TYPE_MR 5
-#define H_ALL_RES_TYPE_MW 6
-
-/* input param R5 */
-#define H_ALL_RES_EQ_NEQ EHEA_BMASK_IBM(0, 0)
-#define H_ALL_RES_EQ_NON_NEQ_ISN EHEA_BMASK_IBM(6, 7)
-#define H_ALL_RES_EQ_INH_EQE_GEN EHEA_BMASK_IBM(16, 16)
-#define H_ALL_RES_EQ_RES_TYPE EHEA_BMASK_IBM(56, 63)
-/* input param R6 */
-#define H_ALL_RES_EQ_MAX_EQE EHEA_BMASK_IBM(32, 63)
-
-/* output param R6 */
-#define H_ALL_RES_EQ_LIOBN EHEA_BMASK_IBM(32, 63)
-
-/* output param R7 */
-#define H_ALL_RES_EQ_ACT_EQE EHEA_BMASK_IBM(32, 63)
-
-/* output param R8 */
-#define H_ALL_RES_EQ_ACT_PS EHEA_BMASK_IBM(32, 63)
-
-/* output param R9 */
-#define H_ALL_RES_EQ_ACT_EQ_IST_C EHEA_BMASK_IBM(30, 31)
-#define H_ALL_RES_EQ_ACT_EQ_IST_1 EHEA_BMASK_IBM(40, 63)
-
-/* output param R10 */
-#define H_ALL_RES_EQ_ACT_EQ_IST_2 EHEA_BMASK_IBM(40, 63)
-
-/* output param R11 */
-#define H_ALL_RES_EQ_ACT_EQ_IST_3 EHEA_BMASK_IBM(40, 63)
-
-/* output param R12 */
-#define H_ALL_RES_EQ_ACT_EQ_IST_4 EHEA_BMASK_IBM(40, 63)
-
-u64 ehea_h_alloc_resource_eq(const u64 adapter_handle,
- struct ehea_eq_attr *eq_attr, u64 *eq_handle)
-{
- u64 hret, allocate_controls;
- unsigned long outs[PLPAR_HCALL9_BUFSIZE];
-
- /* resource type */
- allocate_controls =
- EHEA_BMASK_SET(H_ALL_RES_EQ_RES_TYPE, H_ALL_RES_TYPE_EQ)
- | EHEA_BMASK_SET(H_ALL_RES_EQ_NEQ, eq_attr->type ? 1 : 0)
- | EHEA_BMASK_SET(H_ALL_RES_EQ_INH_EQE_GEN, !eq_attr->eqe_gen)
- | EHEA_BMASK_SET(H_ALL_RES_EQ_NON_NEQ_ISN, 1);
-
- hret = ehea_plpar_hcall9(H_ALLOC_HEA_RESOURCE,
- outs,
- adapter_handle, /* R4 */
- allocate_controls, /* R5 */
- eq_attr->max_nr_of_eqes, /* R6 */
- 0, 0, 0, 0, 0, 0); /* R7-R10 */
-
- *eq_handle = outs[0];
- eq_attr->act_nr_of_eqes = outs[3];
- eq_attr->nr_pages = outs[4];
- eq_attr->ist1 = outs[5];
- eq_attr->ist2 = outs[6];
- eq_attr->ist3 = outs[7];
- eq_attr->ist4 = outs[8];
-
- return hret;
-}
-
-u64 ehea_h_modify_ehea_qp(const u64 adapter_handle, const u8 cat,
- const u64 qp_handle, const u64 sel_mask,
- void *cb_addr, u64 *inv_attr_id, u64 *proc_mask,
- u16 *out_swr, u16 *out_rwr)
-{
- u64 hret;
- unsigned long outs[PLPAR_HCALL9_BUFSIZE];
-
- hret = ehea_plpar_hcall9(H_MODIFY_HEA_QP,
- outs,
- adapter_handle, /* R4 */
- (u64) cat, /* R5 */
- qp_handle, /* R6 */
- sel_mask, /* R7 */
- __pa(cb_addr), /* R8 */
- 0, 0, 0, 0); /* R9-R12 */
-
- *inv_attr_id = outs[0];
- *out_swr = outs[3];
- *out_rwr = outs[4];
- *proc_mask = outs[5];
-
- return hret;
-}
-
-u64 ehea_h_register_rpage(const u64 adapter_handle, const u8 pagesize,
- const u8 queue_type, const u64 resource_handle,
- const u64 log_pageaddr, u64 count)
-{
- u64 reg_control;
-
- reg_control = EHEA_BMASK_SET(H_REG_RPAGE_PAGE_SIZE, pagesize)
- | EHEA_BMASK_SET(H_REG_RPAGE_QT, queue_type);
-
- return ehea_plpar_hcall_norets(H_REGISTER_HEA_RPAGES,
- adapter_handle, /* R4 */
- reg_control, /* R5 */
- resource_handle, /* R6 */
- log_pageaddr, /* R7 */
- count, /* R8 */
- 0, 0); /* R9-R10 */
-}
-
-u64 ehea_h_register_smr(const u64 adapter_handle, const u64 orig_mr_handle,
- const u64 vaddr_in, const u32 access_ctrl, const u32 pd,
- struct ehea_mr *mr)
-{
- u64 hret;
- unsigned long outs[PLPAR_HCALL9_BUFSIZE];
-
- hret = ehea_plpar_hcall9(H_REGISTER_SMR,
- outs,
- adapter_handle , /* R4 */
- orig_mr_handle, /* R5 */
- vaddr_in, /* R6 */
- (((u64)access_ctrl) << 32ULL), /* R7 */
- pd, /* R8 */
- 0, 0, 0, 0); /* R9-R12 */
-
- mr->handle = outs[0];
- mr->lkey = (u32)outs[2];
-
- return hret;
-}
-
-u64 ehea_h_disable_and_get_hea(const u64 adapter_handle, const u64 qp_handle)
-{
- unsigned long outs[PLPAR_HCALL9_BUFSIZE];
-
- return ehea_plpar_hcall9(H_DISABLE_AND_GET_HEA,
- outs,
- adapter_handle, /* R4 */
- H_DISABLE_GET_EHEA_WQE_P, /* R5 */
- qp_handle, /* R6 */
- 0, 0, 0, 0, 0, 0); /* R7-R12 */
-}
-
-u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle,
- u64 force_bit)
-{
- return ehea_plpar_hcall_norets(H_FREE_RESOURCE,
- adapter_handle, /* R4 */
- res_handle, /* R5 */
- force_bit,
- 0, 0, 0, 0); /* R7-R10 */
-}
-
-u64 ehea_h_alloc_resource_mr(const u64 adapter_handle, const u64 vaddr,
- const u64 length, const u32 access_ctrl,
- const u32 pd, u64 *mr_handle, u32 *lkey)
-{
- u64 hret;
- unsigned long outs[PLPAR_HCALL9_BUFSIZE];
-
- hret = ehea_plpar_hcall9(H_ALLOC_HEA_RESOURCE,
- outs,
- adapter_handle, /* R4 */
- 5, /* R5 */
- vaddr, /* R6 */
- length, /* R7 */
- (((u64) access_ctrl) << 32ULL), /* R8 */
- pd, /* R9 */
- 0, 0, 0); /* R10-R12 */
-
- *mr_handle = outs[0];
- *lkey = (u32)outs[2];
- return hret;
-}
-
-u64 ehea_h_register_rpage_mr(const u64 adapter_handle, const u64 mr_handle,
- const u8 pagesize, const u8 queue_type,
- const u64 log_pageaddr, const u64 count)
-{
- if ((count > 1) && (log_pageaddr & ~PAGE_MASK)) {
- pr_err("not on pageboundary\n");
- return H_PARAMETER;
- }
-
- return ehea_h_register_rpage(adapter_handle, pagesize,
- queue_type, mr_handle,
- log_pageaddr, count);
-}
-
-u64 ehea_h_query_ehea(const u64 adapter_handle, void *cb_addr)
-{
- u64 hret, cb_logaddr;
-
- cb_logaddr = __pa(cb_addr);
-
- hret = ehea_plpar_hcall_norets(H_QUERY_HEA,
- adapter_handle, /* R4 */
- cb_logaddr, /* R5 */
- 0, 0, 0, 0, 0); /* R6-R10 */
-#ifdef DEBUG
- ehea_dump(cb_addr, sizeof(struct hcp_query_ehea), "hcp_query_ehea");
-#endif
- return hret;
-}
-
-u64 ehea_h_query_ehea_port(const u64 adapter_handle, const u16 port_num,
- const u8 cb_cat, const u64 select_mask,
- void *cb_addr)
-{
- u64 port_info;
- u64 cb_logaddr = __pa(cb_addr);
- u64 arr_index = 0;
-
- port_info = EHEA_BMASK_SET(H_MEHEAPORT_CAT, cb_cat)
- | EHEA_BMASK_SET(H_MEHEAPORT_PN, port_num);
-
- return ehea_plpar_hcall_norets(H_QUERY_HEA_PORT,
- adapter_handle, /* R4 */
- port_info, /* R5 */
- select_mask, /* R6 */
- arr_index, /* R7 */
- cb_logaddr, /* R8 */
- 0, 0); /* R9-R10 */
-}
-
-u64 ehea_h_modify_ehea_port(const u64 adapter_handle, const u16 port_num,
- const u8 cb_cat, const u64 select_mask,
- void *cb_addr)
-{
- unsigned long outs[PLPAR_HCALL9_BUFSIZE];
- u64 port_info;
- u64 arr_index = 0;
- u64 cb_logaddr = __pa(cb_addr);
-
- port_info = EHEA_BMASK_SET(H_MEHEAPORT_CAT, cb_cat)
- | EHEA_BMASK_SET(H_MEHEAPORT_PN, port_num);
-#ifdef DEBUG
- ehea_dump(cb_addr, sizeof(struct hcp_ehea_port_cb0), "Before HCALL");
-#endif
- return ehea_plpar_hcall9(H_MODIFY_HEA_PORT,
- outs,
- adapter_handle, /* R4 */
- port_info, /* R5 */
- select_mask, /* R6 */
- arr_index, /* R7 */
- cb_logaddr, /* R8 */
- 0, 0, 0, 0); /* R9-R12 */
-}
-
-u64 ehea_h_reg_dereg_bcmc(const u64 adapter_handle, const u16 port_num,
- const u8 reg_type, const u64 mc_mac_addr,
- const u16 vlan_id, const u32 hcall_id)
-{
- u64 r5_port_num, r6_reg_type, r7_mc_mac_addr, r8_vlan_id;
- u64 mac_addr = mc_mac_addr >> 16;
-
- r5_port_num = EHEA_BMASK_SET(H_REGBCMC_PN, port_num);
- r6_reg_type = EHEA_BMASK_SET(H_REGBCMC_REGTYPE, reg_type);
- r7_mc_mac_addr = EHEA_BMASK_SET(H_REGBCMC_MACADDR, mac_addr);
- r8_vlan_id = EHEA_BMASK_SET(H_REGBCMC_VLANID, vlan_id);
-
- return ehea_plpar_hcall_norets(hcall_id,
- adapter_handle, /* R4 */
- r5_port_num, /* R5 */
- r6_reg_type, /* R6 */
- r7_mc_mac_addr, /* R7 */
- r8_vlan_id, /* R8 */
- 0, 0); /* R9-R12 */
-}
-
-u64 ehea_h_reset_events(const u64 adapter_handle, const u64 neq_handle,
- const u64 event_mask)
-{
- return ehea_plpar_hcall_norets(H_RESET_EVENTS,
- adapter_handle, /* R4 */
- neq_handle, /* R5 */
- event_mask, /* R6 */
- 0, 0, 0, 0); /* R7-R12 */
-}
-
-u64 ehea_h_error_data(const u64 adapter_handle, const u64 ressource_handle,
- void *rblock)
-{
- return ehea_plpar_hcall_norets(H_ERROR_DATA,
- adapter_handle, /* R4 */
- ressource_handle, /* R5 */
- __pa(rblock), /* R6 */
- 0, 0, 0, 0); /* R7-R12 */
-}
diff --git a/drivers/net/ethernet/ibm/ehea/ehea_phyp.h b/drivers/net/ethernet/ibm/ehea/ehea_phyp.h
deleted file mode 100644
index e8b56c103410..000000000000
--- a/drivers/net/ethernet/ibm/ehea/ehea_phyp.h
+++ /dev/null
@@ -1,433 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * linux/drivers/net/ethernet/ibm/ehea/ehea_phyp.h
- *
- * eHEA ethernet device driver for IBM eServer System p
- *
- * (C) Copyright IBM Corp. 2006
- *
- * Authors:
- * Christoph Raisch <raisch@de.ibm.com>
- * Jan-Bernd Themann <themann@de.ibm.com>
- * Thomas Klein <tklein@de.ibm.com>
- */
-
-#ifndef __EHEA_PHYP_H__
-#define __EHEA_PHYP_H__
-
-#include <linux/delay.h>
-#include <asm/hvcall.h>
-#include "ehea.h"
-#include "ehea_hw.h"
-
-/* Some abbreviations used here:
- *
- * hcp_* - structures, variables and functions releated to Hypervisor Calls
- */
-
-/* Number of pages which can be registered at once by H_REGISTER_HEA_RPAGES */
-#define EHEA_MAX_RPAGE 512
-
-/* Notification Event Queue (NEQ) Entry bit masks */
-#define NEQE_EVENT_CODE EHEA_BMASK_IBM(2, 7)
-#define NEQE_PORTNUM EHEA_BMASK_IBM(32, 47)
-#define NEQE_PORT_UP EHEA_BMASK_IBM(16, 16)
-#define NEQE_EXTSWITCH_PORT_UP EHEA_BMASK_IBM(17, 17)
-#define NEQE_EXTSWITCH_PRIMARY EHEA_BMASK_IBM(18, 18)
-#define NEQE_PLID EHEA_BMASK_IBM(16, 47)
-
-/* Notification Event Codes */
-#define EHEA_EC_PORTSTATE_CHG 0x30
-#define EHEA_EC_ADAPTER_MALFUNC 0x32
-#define EHEA_EC_PORT_MALFUNC 0x33
-
-/* Notification Event Log Register (NELR) bit masks */
-#define NELR_PORT_MALFUNC EHEA_BMASK_IBM(61, 61)
-#define NELR_ADAPTER_MALFUNC EHEA_BMASK_IBM(62, 62)
-#define NELR_PORTSTATE_CHG EHEA_BMASK_IBM(63, 63)
-
-static inline void hcp_epas_ctor(struct h_epas *epas, u64 paddr_kernel,
- u64 paddr_user)
-{
- /* To support 64k pages we must round to 64k page boundary */
- epas->kernel.addr = ioremap((paddr_kernel & PAGE_MASK), PAGE_SIZE) +
- (paddr_kernel & ~PAGE_MASK);
- epas->user.addr = paddr_user;
-}
-
-static inline void hcp_epas_dtor(struct h_epas *epas)
-{
- if (epas->kernel.addr)
- iounmap((void __iomem *)((u64)epas->kernel.addr & PAGE_MASK));
-
- epas->user.addr = 0;
- epas->kernel.addr = 0;
-}
-
-struct hcp_modify_qp_cb0 {
- u64 qp_ctl_reg; /* 00 */
- u32 max_swqe; /* 02 */
- u32 max_rwqe; /* 03 */
- u32 port_nb; /* 04 */
- u32 reserved0; /* 05 */
- u64 qp_aer; /* 06 */
- u64 qp_tenure; /* 08 */
-};
-
-/* Hcall Query/Modify Queue Pair Control Block 0 Selection Mask Bits */
-#define H_QPCB0_ALL EHEA_BMASK_IBM(0, 5)
-#define H_QPCB0_QP_CTL_REG EHEA_BMASK_IBM(0, 0)
-#define H_QPCB0_MAX_SWQE EHEA_BMASK_IBM(1, 1)
-#define H_QPCB0_MAX_RWQE EHEA_BMASK_IBM(2, 2)
-#define H_QPCB0_PORT_NB EHEA_BMASK_IBM(3, 3)
-#define H_QPCB0_QP_AER EHEA_BMASK_IBM(4, 4)
-#define H_QPCB0_QP_TENURE EHEA_BMASK_IBM(5, 5)
-
-/* Queue Pair Control Register Status Bits */
-#define H_QP_CR_ENABLED 0x8000000000000000ULL /* QP enabled */
- /* QP States: */
-#define H_QP_CR_STATE_RESET 0x0000010000000000ULL /* Reset */
-#define H_QP_CR_STATE_INITIALIZED 0x0000020000000000ULL /* Initialized */
-#define H_QP_CR_STATE_RDY2RCV 0x0000030000000000ULL /* Ready to recv */
-#define H_QP_CR_STATE_RDY2SND 0x0000050000000000ULL /* Ready to send */
-#define H_QP_CR_STATE_ERROR 0x0000800000000000ULL /* Error */
-#define H_QP_CR_RES_STATE 0x0000007F00000000ULL /* Resultant state */
-
-struct hcp_modify_qp_cb1 {
- u32 qpn; /* 00 */
- u32 qp_asyn_ev_eq_nb; /* 01 */
- u64 sq_cq_handle; /* 02 */
- u64 rq_cq_handle; /* 04 */
- /* sgel = scatter gather element */
- u32 sgel_nb_sq; /* 06 */
- u32 sgel_nb_rq1; /* 07 */
- u32 sgel_nb_rq2; /* 08 */
- u32 sgel_nb_rq3; /* 09 */
-};
-
-/* Hcall Query/Modify Queue Pair Control Block 1 Selection Mask Bits */
-#define H_QPCB1_ALL EHEA_BMASK_IBM(0, 7)
-#define H_QPCB1_QPN EHEA_BMASK_IBM(0, 0)
-#define H_QPCB1_ASYN_EV_EQ_NB EHEA_BMASK_IBM(1, 1)
-#define H_QPCB1_SQ_CQ_HANDLE EHEA_BMASK_IBM(2, 2)
-#define H_QPCB1_RQ_CQ_HANDLE EHEA_BMASK_IBM(3, 3)
-#define H_QPCB1_SGEL_NB_SQ EHEA_BMASK_IBM(4, 4)
-#define H_QPCB1_SGEL_NB_RQ1 EHEA_BMASK_IBM(5, 5)
-#define H_QPCB1_SGEL_NB_RQ2 EHEA_BMASK_IBM(6, 6)
-#define H_QPCB1_SGEL_NB_RQ3 EHEA_BMASK_IBM(7, 7)
-
-struct hcp_query_ehea {
- u32 cur_num_qps; /* 00 */
- u32 cur_num_cqs; /* 01 */
- u32 cur_num_eqs; /* 02 */
- u32 cur_num_mrs; /* 03 */
- u32 auth_level; /* 04 */
- u32 max_num_qps; /* 05 */
- u32 max_num_cqs; /* 06 */
- u32 max_num_eqs; /* 07 */
- u32 max_num_mrs; /* 08 */
- u32 reserved0; /* 09 */
- u32 int_clock_freq; /* 10 */
- u32 max_num_pds; /* 11 */
- u32 max_num_addr_handles; /* 12 */
- u32 max_num_cqes; /* 13 */
- u32 max_num_wqes; /* 14 */
- u32 max_num_sgel_rq1wqe; /* 15 */
- u32 max_num_sgel_rq2wqe; /* 16 */
- u32 max_num_sgel_rq3wqe; /* 17 */
- u32 mr_page_size; /* 18 */
- u32 reserved1; /* 19 */
- u64 max_mr_size; /* 20 */
- u64 reserved2; /* 22 */
- u32 num_ports; /* 24 */
- u32 reserved3; /* 25 */
- u32 reserved4; /* 26 */
- u32 reserved5; /* 27 */
- u64 max_mc_mac; /* 28 */
- u64 ehea_cap; /* 30 */
- u32 max_isn_per_eq; /* 32 */
- u32 max_num_neq; /* 33 */
- u64 max_num_vlan_ids; /* 34 */
- u32 max_num_port_group; /* 36 */
- u32 max_num_phys_port; /* 37 */
-
-};
-
-/* Hcall Query/Modify Port Control Block defines */
-#define H_PORT_CB0 0
-#define H_PORT_CB1 1
-#define H_PORT_CB2 2
-#define H_PORT_CB3 3
-#define H_PORT_CB4 4
-#define H_PORT_CB5 5
-#define H_PORT_CB6 6
-#define H_PORT_CB7 7
-
-struct hcp_ehea_port_cb0 {
- u64 port_mac_addr;
- u64 port_rc;
- u64 reserved0;
- u32 port_op_state;
- u32 port_speed;
- u32 ext_swport_op_state;
- u32 neg_tpf_prpf;
- u32 num_default_qps;
- u32 reserved1;
- u64 default_qpn_arr[16];
-};
-
-/* Hcall Query/Modify Port Control Block 0 Selection Mask Bits */
-#define H_PORT_CB0_ALL EHEA_BMASK_IBM(0, 7) /* Set all bits */
-#define H_PORT_CB0_MAC EHEA_BMASK_IBM(0, 0) /* MAC address */
-#define H_PORT_CB0_PRC EHEA_BMASK_IBM(1, 1) /* Port Recv Control */
-#define H_PORT_CB0_DEFQPNARRAY EHEA_BMASK_IBM(7, 7) /* Default QPN Array */
-
-/* Hcall Query Port: Returned port speed values */
-#define H_SPEED_10M_H 1 /* 10 Mbps, Half Duplex */
-#define H_SPEED_10M_F 2 /* 10 Mbps, Full Duplex */
-#define H_SPEED_100M_H 3 /* 100 Mbps, Half Duplex */
-#define H_SPEED_100M_F 4 /* 100 Mbps, Full Duplex */
-#define H_SPEED_1G_F 6 /* 1 Gbps, Full Duplex */
-#define H_SPEED_10G_F 8 /* 10 Gbps, Full Duplex */
-
-/* Port Receive Control Status Bits */
-#define PXLY_RC_VALID EHEA_BMASK_IBM(49, 49)
-#define PXLY_RC_VLAN_XTRACT EHEA_BMASK_IBM(50, 50)
-#define PXLY_RC_TCP_6_TUPLE EHEA_BMASK_IBM(51, 51)
-#define PXLY_RC_UDP_6_TUPLE EHEA_BMASK_IBM(52, 52)
-#define PXLY_RC_TCP_3_TUPLE EHEA_BMASK_IBM(53, 53)
-#define PXLY_RC_TCP_2_TUPLE EHEA_BMASK_IBM(54, 54)
-#define PXLY_RC_LLC_SNAP EHEA_BMASK_IBM(55, 55)
-#define PXLY_RC_JUMBO_FRAME EHEA_BMASK_IBM(56, 56)
-#define PXLY_RC_FRAG_IP_PKT EHEA_BMASK_IBM(57, 57)
-#define PXLY_RC_TCP_UDP_CHKSUM EHEA_BMASK_IBM(58, 58)
-#define PXLY_RC_IP_CHKSUM EHEA_BMASK_IBM(59, 59)
-#define PXLY_RC_MAC_FILTER EHEA_BMASK_IBM(60, 60)
-#define PXLY_RC_UNTAG_FILTER EHEA_BMASK_IBM(61, 61)
-#define PXLY_RC_VLAN_TAG_FILTER EHEA_BMASK_IBM(62, 63)
-
-#define PXLY_RC_VLAN_FILTER 2
-#define PXLY_RC_VLAN_PERM 0
-
-
-#define H_PORT_CB1_ALL 0x8000000000000000ULL
-
-struct hcp_ehea_port_cb1 {
- u64 vlan_filter[64];
-};
-
-#define H_PORT_CB2_ALL 0xFFE0000000000000ULL
-
-struct hcp_ehea_port_cb2 {
- u64 rxo;
- u64 rxucp;
- u64 rxufd;
- u64 rxuerr;
- u64 rxftl;
- u64 rxmcp;
- u64 rxbcp;
- u64 txo;
- u64 txucp;
- u64 txmcp;
- u64 txbcp;
-};
-
-struct hcp_ehea_port_cb3 {
- u64 vlan_bc_filter[64];
- u64 vlan_mc_filter[64];
- u64 vlan_un_filter[64];
- u64 port_mac_hash_array[64];
-};
-
-#define H_PORT_CB4_ALL 0xF000000000000000ULL
-#define H_PORT_CB4_JUMBO 0x1000000000000000ULL
-#define H_PORT_CB4_SPEED 0x8000000000000000ULL
-
-struct hcp_ehea_port_cb4 {
- u32 port_speed;
- u32 pause_frame;
- u32 ens_port_op_state;
- u32 jumbo_frame;
- u32 ens_port_wrap;
-};
-
-/* Hcall Query/Modify Port Control Block 5 Selection Mask Bits */
-#define H_PORT_CB5_RCU 0x0001000000000000ULL
-#define PXS_RCU EHEA_BMASK_IBM(61, 63)
-
-struct hcp_ehea_port_cb5 {
- u64 prc; /* 00 */
- u64 uaa; /* 01 */
- u64 macvc; /* 02 */
- u64 xpcsc; /* 03 */
- u64 xpcsp; /* 04 */
- u64 pcsid; /* 05 */
- u64 xpcsst; /* 06 */
- u64 pthlb; /* 07 */
- u64 pthrb; /* 08 */
- u64 pqu; /* 09 */
- u64 pqd; /* 10 */
- u64 prt; /* 11 */
- u64 wsth; /* 12 */
- u64 rcb; /* 13 */
- u64 rcm; /* 14 */
- u64 rcu; /* 15 */
- u64 macc; /* 16 */
- u64 pc; /* 17 */
- u64 pst; /* 18 */
- u64 ducqpn; /* 19 */
- u64 mcqpn; /* 20 */
- u64 mma; /* 21 */
- u64 pmc0h; /* 22 */
- u64 pmc0l; /* 23 */
- u64 lbc; /* 24 */
-};
-
-#define H_PORT_CB6_ALL 0xFFFFFE7FFFFF8000ULL
-
-struct hcp_ehea_port_cb6 {
- u64 rxo; /* 00 */
- u64 rx64; /* 01 */
- u64 rx65; /* 02 */
- u64 rx128; /* 03 */
- u64 rx256; /* 04 */
- u64 rx512; /* 05 */
- u64 rx1024; /* 06 */
- u64 rxbfcs; /* 07 */
- u64 rxime; /* 08 */
- u64 rxrle; /* 09 */
- u64 rxorle; /* 10 */
- u64 rxftl; /* 11 */
- u64 rxjab; /* 12 */
- u64 rxse; /* 13 */
- u64 rxce; /* 14 */
- u64 rxrf; /* 15 */
- u64 rxfrag; /* 16 */
- u64 rxuoc; /* 17 */
- u64 rxcpf; /* 18 */
- u64 rxsb; /* 19 */
- u64 rxfd; /* 20 */
- u64 rxoerr; /* 21 */
- u64 rxaln; /* 22 */
- u64 ducqpn; /* 23 */
- u64 reserved0; /* 24 */
- u64 rxmcp; /* 25 */
- u64 rxbcp; /* 26 */
- u64 txmcp; /* 27 */
- u64 txbcp; /* 28 */
- u64 txo; /* 29 */
- u64 tx64; /* 30 */
- u64 tx65; /* 31 */
- u64 tx128; /* 32 */
- u64 tx256; /* 33 */
- u64 tx512; /* 34 */
- u64 tx1024; /* 35 */
- u64 txbfcs; /* 36 */
- u64 txcpf; /* 37 */
- u64 txlf; /* 38 */
- u64 txrf; /* 39 */
- u64 txime; /* 40 */
- u64 txsc; /* 41 */
- u64 txmc; /* 42 */
- u64 txsqe; /* 43 */
- u64 txdef; /* 44 */
- u64 txlcol; /* 45 */
- u64 txexcol; /* 46 */
- u64 txcse; /* 47 */
- u64 txbor; /* 48 */
-};
-
-#define H_PORT_CB7_DUCQPN 0x8000000000000000ULL
-
-struct hcp_ehea_port_cb7 {
- u64 def_uc_qpn;
-};
-
-u64 ehea_h_query_ehea_qp(const u64 adapter_handle,
- const u8 qp_category,
- const u64 qp_handle, const u64 sel_mask,
- void *cb_addr);
-
-u64 ehea_h_modify_ehea_qp(const u64 adapter_handle,
- const u8 cat,
- const u64 qp_handle,
- const u64 sel_mask,
- void *cb_addr,
- u64 *inv_attr_id,
- u64 *proc_mask, u16 *out_swr, u16 *out_rwr);
-
-u64 ehea_h_alloc_resource_eq(const u64 adapter_handle,
- struct ehea_eq_attr *eq_attr, u64 *eq_handle);
-
-u64 ehea_h_alloc_resource_cq(const u64 adapter_handle,
- struct ehea_cq_attr *cq_attr,
- u64 *cq_handle, struct h_epas *epas);
-
-u64 ehea_h_alloc_resource_qp(const u64 adapter_handle,
- struct ehea_qp_init_attr *init_attr,
- const u32 pd,
- u64 *qp_handle, struct h_epas *h_epas);
-
-#define H_REG_RPAGE_PAGE_SIZE EHEA_BMASK_IBM(48, 55)
-#define H_REG_RPAGE_QT EHEA_BMASK_IBM(62, 63)
-
-u64 ehea_h_register_rpage(const u64 adapter_handle,
- const u8 pagesize,
- const u8 queue_type,
- const u64 resource_handle,
- const u64 log_pageaddr, u64 count);
-
-#define H_DISABLE_GET_EHEA_WQE_P 1
-#define H_DISABLE_GET_SQ_WQE_P 2
-#define H_DISABLE_GET_RQC 3
-
-u64 ehea_h_disable_and_get_hea(const u64 adapter_handle, const u64 qp_handle);
-
-#define FORCE_FREE 1
-#define NORMAL_FREE 0
-
-u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle,
- u64 force_bit);
-
-u64 ehea_h_alloc_resource_mr(const u64 adapter_handle, const u64 vaddr,
- const u64 length, const u32 access_ctrl,
- const u32 pd, u64 *mr_handle, u32 *lkey);
-
-u64 ehea_h_register_rpage_mr(const u64 adapter_handle, const u64 mr_handle,
- const u8 pagesize, const u8 queue_type,
- const u64 log_pageaddr, const u64 count);
-
-u64 ehea_h_register_smr(const u64 adapter_handle, const u64 orig_mr_handle,
- const u64 vaddr_in, const u32 access_ctrl, const u32 pd,
- struct ehea_mr *mr);
-
-u64 ehea_h_query_ehea(const u64 adapter_handle, void *cb_addr);
-
-/* output param R5 */
-#define H_MEHEAPORT_CAT EHEA_BMASK_IBM(40, 47)
-#define H_MEHEAPORT_PN EHEA_BMASK_IBM(48, 63)
-
-u64 ehea_h_query_ehea_port(const u64 adapter_handle, const u16 port_num,
- const u8 cb_cat, const u64 select_mask,
- void *cb_addr);
-
-u64 ehea_h_modify_ehea_port(const u64 adapter_handle, const u16 port_num,
- const u8 cb_cat, const u64 select_mask,
- void *cb_addr);
-
-#define H_REGBCMC_PN EHEA_BMASK_IBM(48, 63)
-#define H_REGBCMC_REGTYPE EHEA_BMASK_IBM(60, 63)
-#define H_REGBCMC_MACADDR EHEA_BMASK_IBM(16, 63)
-#define H_REGBCMC_VLANID EHEA_BMASK_IBM(52, 63)
-
-u64 ehea_h_reg_dereg_bcmc(const u64 adapter_handle, const u16 port_num,
- const u8 reg_type, const u64 mc_mac_addr,
- const u16 vlan_id, const u32 hcall_id);
-
-u64 ehea_h_reset_events(const u64 adapter_handle, const u64 neq_handle,
- const u64 event_mask);
-
-u64 ehea_h_error_data(const u64 adapter_handle, const u64 ressource_handle,
- void *rblock);
-
-#endif /* __EHEA_PHYP_H__ */
diff --git a/drivers/net/ethernet/ibm/ehea/ehea_qmr.c b/drivers/net/ethernet/ibm/ehea/ehea_qmr.c
deleted file mode 100644
index 60629a0032b2..000000000000
--- a/drivers/net/ethernet/ibm/ehea/ehea_qmr.c
+++ /dev/null
@@ -1,999 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * linux/drivers/net/ethernet/ibm/ehea/ehea_qmr.c
- *
- * eHEA ethernet device driver for IBM eServer System p
- *
- * (C) Copyright IBM Corp. 2006
- *
- * Authors:
- * Christoph Raisch <raisch@de.ibm.com>
- * Jan-Bernd Themann <themann@de.ibm.com>
- * Thomas Klein <tklein@de.ibm.com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include "ehea.h"
-#include "ehea_phyp.h"
-#include "ehea_qmr.h"
-
-static struct ehea_bmap *ehea_bmap;
-
-static void *hw_qpageit_get_inc(struct hw_queue *queue)
-{
- void *retvalue = hw_qeit_get(queue);
-
- queue->current_q_offset += queue->pagesize;
- if (queue->current_q_offset > queue->queue_length) {
- queue->current_q_offset -= queue->pagesize;
- retvalue = NULL;
- } else if (((u64) retvalue) & (EHEA_PAGESIZE-1)) {
- pr_err("not on pageboundary\n");
- retvalue = NULL;
- }
- return retvalue;
-}
-
-static int hw_queue_ctor(struct hw_queue *queue, const u32 nr_of_pages,
- const u32 pagesize, const u32 qe_size)
-{
- int pages_per_kpage = PAGE_SIZE / pagesize;
- int i, k;
-
- if ((pagesize > PAGE_SIZE) || (!pages_per_kpage)) {
- pr_err("pagesize conflict! kernel pagesize=%d, ehea pagesize=%d\n",
- (int)PAGE_SIZE, (int)pagesize);
- return -EINVAL;
- }
-
- queue->queue_length = nr_of_pages * pagesize;
- queue->queue_pages = kmalloc_array(nr_of_pages, sizeof(void *),
- GFP_KERNEL);
- if (!queue->queue_pages)
- return -ENOMEM;
-
- /*
- * allocate pages for queue:
- * outer loop allocates whole kernel pages (page aligned) and
- * inner loop divides a kernel page into smaller hea queue pages
- */
- i = 0;
- while (i < nr_of_pages) {
- u8 *kpage = (u8 *)get_zeroed_page(GFP_KERNEL);
- if (!kpage)
- goto out_nomem;
- for (k = 0; k < pages_per_kpage && i < nr_of_pages; k++) {
- (queue->queue_pages)[i] = (struct ehea_page *)kpage;
- kpage += pagesize;
- i++;
- }
- }
-
- queue->current_q_offset = 0;
- queue->qe_size = qe_size;
- queue->pagesize = pagesize;
- queue->toggle_state = 1;
-
- return 0;
-out_nomem:
- for (i = 0; i < nr_of_pages; i += pages_per_kpage) {
- if (!(queue->queue_pages)[i])
- break;
- free_page((unsigned long)(queue->queue_pages)[i]);
- }
- return -ENOMEM;
-}
-
-static void hw_queue_dtor(struct hw_queue *queue)
-{
- int pages_per_kpage;
- int i, nr_pages;
-
- if (!queue || !queue->queue_pages)
- return;
-
- pages_per_kpage = PAGE_SIZE / queue->pagesize;
-
- nr_pages = queue->queue_length / queue->pagesize;
-
- for (i = 0; i < nr_pages; i += pages_per_kpage)
- free_page((unsigned long)(queue->queue_pages)[i]);
-
- kfree(queue->queue_pages);
-}
-
-struct ehea_cq *ehea_create_cq(struct ehea_adapter *adapter,
- int nr_of_cqe, u64 eq_handle, u32 cq_token)
-{
- struct ehea_cq *cq;
- u64 hret, rpage;
- u32 counter;
- int ret;
- void *vpage;
-
- cq = kzalloc_obj(*cq);
- if (!cq)
- goto out_nomem;
-
- cq->attr.max_nr_of_cqes = nr_of_cqe;
- cq->attr.cq_token = cq_token;
- cq->attr.eq_handle = eq_handle;
-
- cq->adapter = adapter;
-
- hret = ehea_h_alloc_resource_cq(adapter->handle, &cq->attr,
- &cq->fw_handle, &cq->epas);
- if (hret != H_SUCCESS) {
- pr_err("alloc_resource_cq failed\n");
- goto out_freemem;
- }
-
- ret = hw_queue_ctor(&cq->hw_queue, cq->attr.nr_pages,
- EHEA_PAGESIZE, sizeof(struct ehea_cqe));
- if (ret)
- goto out_freeres;
-
- for (counter = 0; counter < cq->attr.nr_pages; counter++) {
- vpage = hw_qpageit_get_inc(&cq->hw_queue);
- if (!vpage) {
- pr_err("hw_qpageit_get_inc failed\n");
- goto out_kill_hwq;
- }
-
- rpage = __pa(vpage);
- hret = ehea_h_register_rpage(adapter->handle,
- 0, EHEA_CQ_REGISTER_ORIG,
- cq->fw_handle, rpage, 1);
- if (hret < H_SUCCESS) {
- pr_err("register_rpage_cq failed ehea_cq=%p hret=%llx counter=%i act_pages=%i\n",
- cq, hret, counter, cq->attr.nr_pages);
- goto out_kill_hwq;
- }
-
- if (counter == (cq->attr.nr_pages - 1)) {
- vpage = hw_qpageit_get_inc(&cq->hw_queue);
-
- if ((hret != H_SUCCESS) || (vpage)) {
- pr_err("registration of pages not complete hret=%llx\n",
- hret);
- goto out_kill_hwq;
- }
- } else {
- if (hret != H_PAGE_REGISTERED) {
- pr_err("CQ: registration of page failed hret=%llx\n",
- hret);
- goto out_kill_hwq;
- }
- }
- }
-
- hw_qeit_reset(&cq->hw_queue);
- ehea_reset_cq_ep(cq);
- ehea_reset_cq_n1(cq);
-
- return cq;
-
-out_kill_hwq:
- hw_queue_dtor(&cq->hw_queue);
-
-out_freeres:
- ehea_h_free_resource(adapter->handle, cq->fw_handle, FORCE_FREE);
-
-out_freemem:
- kfree(cq);
-
-out_nomem:
- return NULL;
-}
-
-static u64 ehea_destroy_cq_res(struct ehea_cq *cq, u64 force)
-{
- u64 hret;
- u64 adapter_handle = cq->adapter->handle;
-
- /* deregister all previous registered pages */
- hret = ehea_h_free_resource(adapter_handle, cq->fw_handle, force);
- if (hret != H_SUCCESS)
- return hret;
-
- hw_queue_dtor(&cq->hw_queue);
- kfree(cq);
-
- return hret;
-}
-
-int ehea_destroy_cq(struct ehea_cq *cq)
-{
- u64 hret, aer, aerr;
- if (!cq)
- return 0;
-
- hcp_epas_dtor(&cq->epas);
- hret = ehea_destroy_cq_res(cq, NORMAL_FREE);
- if (hret == H_R_STATE) {
- ehea_error_data(cq->adapter, cq->fw_handle, &aer, &aerr);
- hret = ehea_destroy_cq_res(cq, FORCE_FREE);
- }
-
- if (hret != H_SUCCESS) {
- pr_err("destroy CQ failed\n");
- return -EIO;
- }
-
- return 0;
-}
-
-struct ehea_eq *ehea_create_eq(struct ehea_adapter *adapter,
- const enum ehea_eq_type type,
- const u32 max_nr_of_eqes, const u8 eqe_gen)
-{
- int ret, i;
- u64 hret, rpage;
- void *vpage;
- struct ehea_eq *eq;
-
- eq = kzalloc_obj(*eq);
- if (!eq)
- return NULL;
-
- eq->adapter = adapter;
- eq->attr.type = type;
- eq->attr.max_nr_of_eqes = max_nr_of_eqes;
- eq->attr.eqe_gen = eqe_gen;
- spin_lock_init(&eq->spinlock);
-
- hret = ehea_h_alloc_resource_eq(adapter->handle,
- &eq->attr, &eq->fw_handle);
- if (hret != H_SUCCESS) {
- pr_err("alloc_resource_eq failed\n");
- goto out_freemem;
- }
-
- ret = hw_queue_ctor(&eq->hw_queue, eq->attr.nr_pages,
- EHEA_PAGESIZE, sizeof(struct ehea_eqe));
- if (ret) {
- pr_err("can't allocate eq pages\n");
- goto out_freeres;
- }
-
- for (i = 0; i < eq->attr.nr_pages; i++) {
- vpage = hw_qpageit_get_inc(&eq->hw_queue);
- if (!vpage) {
- pr_err("hw_qpageit_get_inc failed\n");
- hret = H_RESOURCE;
- goto out_kill_hwq;
- }
-
- rpage = __pa(vpage);
-
- hret = ehea_h_register_rpage(adapter->handle, 0,
- EHEA_EQ_REGISTER_ORIG,
- eq->fw_handle, rpage, 1);
-
- if (i == (eq->attr.nr_pages - 1)) {
- /* last page */
- vpage = hw_qpageit_get_inc(&eq->hw_queue);
- if ((hret != H_SUCCESS) || (vpage))
- goto out_kill_hwq;
-
- } else {
- if (hret != H_PAGE_REGISTERED)
- goto out_kill_hwq;
-
- }
- }
-
- hw_qeit_reset(&eq->hw_queue);
- return eq;
-
-out_kill_hwq:
- hw_queue_dtor(&eq->hw_queue);
-
-out_freeres:
- ehea_h_free_resource(adapter->handle, eq->fw_handle, FORCE_FREE);
-
-out_freemem:
- kfree(eq);
- return NULL;
-}
-
-struct ehea_eqe *ehea_poll_eq(struct ehea_eq *eq)
-{
- struct ehea_eqe *eqe;
- unsigned long flags;
-
- spin_lock_irqsave(&eq->spinlock, flags);
- eqe = hw_eqit_eq_get_inc_valid(&eq->hw_queue);
- spin_unlock_irqrestore(&eq->spinlock, flags);
-
- return eqe;
-}
-
-static u64 ehea_destroy_eq_res(struct ehea_eq *eq, u64 force)
-{
- u64 hret;
- unsigned long flags;
-
- spin_lock_irqsave(&eq->spinlock, flags);
-
- hret = ehea_h_free_resource(eq->adapter->handle, eq->fw_handle, force);
- spin_unlock_irqrestore(&eq->spinlock, flags);
-
- if (hret != H_SUCCESS)
- return hret;
-
- hw_queue_dtor(&eq->hw_queue);
- kfree(eq);
-
- return hret;
-}
-
-int ehea_destroy_eq(struct ehea_eq *eq)
-{
- u64 hret, aer, aerr;
- if (!eq)
- return 0;
-
- hcp_epas_dtor(&eq->epas);
-
- hret = ehea_destroy_eq_res(eq, NORMAL_FREE);
- if (hret == H_R_STATE) {
- ehea_error_data(eq->adapter, eq->fw_handle, &aer, &aerr);
- hret = ehea_destroy_eq_res(eq, FORCE_FREE);
- }
-
- if (hret != H_SUCCESS) {
- pr_err("destroy EQ failed\n");
- return -EIO;
- }
-
- return 0;
-}
-
-/* allocates memory for a queue and registers pages in phyp */
-static int ehea_qp_alloc_register(struct ehea_qp *qp, struct hw_queue *hw_queue,
- int nr_pages, int wqe_size, int act_nr_sges,
- struct ehea_adapter *adapter, int h_call_q_selector)
-{
- u64 hret, rpage;
- int ret, cnt;
- void *vpage;
-
- ret = hw_queue_ctor(hw_queue, nr_pages, EHEA_PAGESIZE, wqe_size);
- if (ret)
- return ret;
-
- for (cnt = 0; cnt < nr_pages; cnt++) {
- vpage = hw_qpageit_get_inc(hw_queue);
- if (!vpage) {
- pr_err("hw_qpageit_get_inc failed\n");
- goto out_kill_hwq;
- }
- rpage = __pa(vpage);
- hret = ehea_h_register_rpage(adapter->handle,
- 0, h_call_q_selector,
- qp->fw_handle, rpage, 1);
- if (hret < H_SUCCESS) {
- pr_err("register_rpage_qp failed\n");
- goto out_kill_hwq;
- }
- }
- hw_qeit_reset(hw_queue);
- return 0;
-
-out_kill_hwq:
- hw_queue_dtor(hw_queue);
- return -EIO;
-}
-
-static inline u32 map_wqe_size(u8 wqe_enc_size)
-{
- return 128 << wqe_enc_size;
-}
-
-struct ehea_qp *ehea_create_qp(struct ehea_adapter *adapter,
- u32 pd, struct ehea_qp_init_attr *init_attr)
-{
- int ret;
- u64 hret;
- struct ehea_qp *qp;
- u32 wqe_size_in_bytes_sq, wqe_size_in_bytes_rq1;
- u32 wqe_size_in_bytes_rq2, wqe_size_in_bytes_rq3;
-
-
- qp = kzalloc_obj(*qp);
- if (!qp)
- return NULL;
-
- qp->adapter = adapter;
-
- hret = ehea_h_alloc_resource_qp(adapter->handle, init_attr, pd,
- &qp->fw_handle, &qp->epas);
- if (hret != H_SUCCESS) {
- pr_err("ehea_h_alloc_resource_qp failed\n");
- goto out_freemem;
- }
-
- wqe_size_in_bytes_sq = map_wqe_size(init_attr->act_wqe_size_enc_sq);
- wqe_size_in_bytes_rq1 = map_wqe_size(init_attr->act_wqe_size_enc_rq1);
- wqe_size_in_bytes_rq2 = map_wqe_size(init_attr->act_wqe_size_enc_rq2);
- wqe_size_in_bytes_rq3 = map_wqe_size(init_attr->act_wqe_size_enc_rq3);
-
- ret = ehea_qp_alloc_register(qp, &qp->hw_squeue, init_attr->nr_sq_pages,
- wqe_size_in_bytes_sq,
- init_attr->act_wqe_size_enc_sq, adapter,
- 0);
- if (ret) {
- pr_err("can't register for sq ret=%x\n", ret);
- goto out_freeres;
- }
-
- ret = ehea_qp_alloc_register(qp, &qp->hw_rqueue1,
- init_attr->nr_rq1_pages,
- wqe_size_in_bytes_rq1,
- init_attr->act_wqe_size_enc_rq1,
- adapter, 1);
- if (ret) {
- pr_err("can't register for rq1 ret=%x\n", ret);
- goto out_kill_hwsq;
- }
-
- if (init_attr->rq_count > 1) {
- ret = ehea_qp_alloc_register(qp, &qp->hw_rqueue2,
- init_attr->nr_rq2_pages,
- wqe_size_in_bytes_rq2,
- init_attr->act_wqe_size_enc_rq2,
- adapter, 2);
- if (ret) {
- pr_err("can't register for rq2 ret=%x\n", ret);
- goto out_kill_hwr1q;
- }
- }
-
- if (init_attr->rq_count > 2) {
- ret = ehea_qp_alloc_register(qp, &qp->hw_rqueue3,
- init_attr->nr_rq3_pages,
- wqe_size_in_bytes_rq3,
- init_attr->act_wqe_size_enc_rq3,
- adapter, 3);
- if (ret) {
- pr_err("can't register for rq3 ret=%x\n", ret);
- goto out_kill_hwr2q;
- }
- }
-
- qp->init_attr = *init_attr;
-
- return qp;
-
-out_kill_hwr2q:
- hw_queue_dtor(&qp->hw_rqueue2);
-
-out_kill_hwr1q:
- hw_queue_dtor(&qp->hw_rqueue1);
-
-out_kill_hwsq:
- hw_queue_dtor(&qp->hw_squeue);
-
-out_freeres:
- ehea_h_disable_and_get_hea(adapter->handle, qp->fw_handle);
- ehea_h_free_resource(adapter->handle, qp->fw_handle, FORCE_FREE);
-
-out_freemem:
- kfree(qp);
- return NULL;
-}
-
-static u64 ehea_destroy_qp_res(struct ehea_qp *qp, u64 force)
-{
- u64 hret;
- struct ehea_qp_init_attr *qp_attr = &qp->init_attr;
-
-
- ehea_h_disable_and_get_hea(qp->adapter->handle, qp->fw_handle);
- hret = ehea_h_free_resource(qp->adapter->handle, qp->fw_handle, force);
- if (hret != H_SUCCESS)
- return hret;
-
- hw_queue_dtor(&qp->hw_squeue);
- hw_queue_dtor(&qp->hw_rqueue1);
-
- if (qp_attr->rq_count > 1)
- hw_queue_dtor(&qp->hw_rqueue2);
- if (qp_attr->rq_count > 2)
- hw_queue_dtor(&qp->hw_rqueue3);
- kfree(qp);
-
- return hret;
-}
-
-int ehea_destroy_qp(struct ehea_qp *qp)
-{
- u64 hret, aer, aerr;
- if (!qp)
- return 0;
-
- hcp_epas_dtor(&qp->epas);
-
- hret = ehea_destroy_qp_res(qp, NORMAL_FREE);
- if (hret == H_R_STATE) {
- ehea_error_data(qp->adapter, qp->fw_handle, &aer, &aerr);
- hret = ehea_destroy_qp_res(qp, FORCE_FREE);
- }
-
- if (hret != H_SUCCESS) {
- pr_err("destroy QP failed\n");
- return -EIO;
- }
-
- return 0;
-}
-
-static inline int ehea_calc_index(unsigned long i, unsigned long s)
-{
- return (i >> s) & EHEA_INDEX_MASK;
-}
-
-static inline int ehea_init_top_bmap(struct ehea_top_bmap *ehea_top_bmap,
- int dir)
-{
- if (!ehea_top_bmap->dir[dir]) {
- ehea_top_bmap->dir[dir] =
- kzalloc_obj(struct ehea_dir_bmap);
- if (!ehea_top_bmap->dir[dir])
- return -ENOMEM;
- }
- return 0;
-}
-
-static inline int ehea_init_bmap(struct ehea_bmap *ehea_bmap, int top, int dir)
-{
- if (!ehea_bmap->top[top]) {
- ehea_bmap->top[top] =
- kzalloc_obj(struct ehea_top_bmap);
- if (!ehea_bmap->top[top])
- return -ENOMEM;
- }
- return ehea_init_top_bmap(ehea_bmap->top[top], dir);
-}
-
-static DEFINE_MUTEX(ehea_busmap_mutex);
-static unsigned long ehea_mr_len;
-
-#define EHEA_BUSMAP_ADD_SECT 1
-#define EHEA_BUSMAP_REM_SECT 0
-
-static void ehea_rebuild_busmap(void)
-{
- u64 vaddr = EHEA_BUSMAP_START;
- int top, dir, idx;
-
- for (top = 0; top < EHEA_MAP_ENTRIES; top++) {
- struct ehea_top_bmap *ehea_top;
- int valid_dir_entries = 0;
-
- if (!ehea_bmap->top[top])
- continue;
- ehea_top = ehea_bmap->top[top];
- for (dir = 0; dir < EHEA_MAP_ENTRIES; dir++) {
- struct ehea_dir_bmap *ehea_dir;
- int valid_entries = 0;
-
- if (!ehea_top->dir[dir])
- continue;
- valid_dir_entries++;
- ehea_dir = ehea_top->dir[dir];
- for (idx = 0; idx < EHEA_MAP_ENTRIES; idx++) {
- if (!ehea_dir->ent[idx])
- continue;
- valid_entries++;
- ehea_dir->ent[idx] = vaddr;
- vaddr += EHEA_SECTSIZE;
- }
- if (!valid_entries) {
- ehea_top->dir[dir] = NULL;
- kfree(ehea_dir);
- }
- }
- if (!valid_dir_entries) {
- ehea_bmap->top[top] = NULL;
- kfree(ehea_top);
- }
- }
-}
-
-static int ehea_update_busmap(unsigned long pfn, unsigned long nr_pages, int add)
-{
- unsigned long i, start_section, end_section;
-
- if (!nr_pages)
- return 0;
-
- if (!ehea_bmap) {
- ehea_bmap = kzalloc_obj(struct ehea_bmap);
- if (!ehea_bmap)
- return -ENOMEM;
- }
-
- start_section = (pfn * PAGE_SIZE) / EHEA_SECTSIZE;
- end_section = start_section + ((nr_pages * PAGE_SIZE) / EHEA_SECTSIZE);
- /* Mark entries as valid or invalid only; address is assigned later */
- for (i = start_section; i < end_section; i++) {
- u64 flag;
- int top = ehea_calc_index(i, EHEA_TOP_INDEX_SHIFT);
- int dir = ehea_calc_index(i, EHEA_DIR_INDEX_SHIFT);
- int idx = i & EHEA_INDEX_MASK;
-
- if (add) {
- int ret = ehea_init_bmap(ehea_bmap, top, dir);
- if (ret)
- return ret;
- flag = 1; /* valid */
- ehea_mr_len += EHEA_SECTSIZE;
- } else {
- if (!ehea_bmap->top[top])
- continue;
- if (!ehea_bmap->top[top]->dir[dir])
- continue;
- flag = 0; /* invalid */
- ehea_mr_len -= EHEA_SECTSIZE;
- }
-
- ehea_bmap->top[top]->dir[dir]->ent[idx] = flag;
- }
- ehea_rebuild_busmap(); /* Assign contiguous addresses for mr */
- return 0;
-}
-
-int ehea_add_sect_bmap(unsigned long pfn, unsigned long nr_pages)
-{
- int ret;
-
- mutex_lock(&ehea_busmap_mutex);
- ret = ehea_update_busmap(pfn, nr_pages, EHEA_BUSMAP_ADD_SECT);
- mutex_unlock(&ehea_busmap_mutex);
- return ret;
-}
-
-int ehea_rem_sect_bmap(unsigned long pfn, unsigned long nr_pages)
-{
- int ret;
-
- mutex_lock(&ehea_busmap_mutex);
- ret = ehea_update_busmap(pfn, nr_pages, EHEA_BUSMAP_REM_SECT);
- mutex_unlock(&ehea_busmap_mutex);
- return ret;
-}
-
-static int ehea_is_hugepage(unsigned long pfn)
-{
- if (pfn & EHEA_HUGEPAGE_PFN_MASK)
- return 0;
-
- if (page_shift(pfn_to_page(pfn)) != EHEA_HUGEPAGESHIFT)
- return 0;
-
- return 1;
-}
-
-static int ehea_create_busmap_callback(unsigned long initial_pfn,
- unsigned long total_nr_pages, void *arg)
-{
- int ret;
- unsigned long pfn, start_pfn, end_pfn, nr_pages;
-
- if ((total_nr_pages * PAGE_SIZE) < EHEA_HUGEPAGE_SIZE)
- return ehea_update_busmap(initial_pfn, total_nr_pages,
- EHEA_BUSMAP_ADD_SECT);
-
- /* Given chunk is >= 16GB -> check for hugepages */
- start_pfn = initial_pfn;
- end_pfn = initial_pfn + total_nr_pages;
- pfn = start_pfn;
-
- while (pfn < end_pfn) {
- if (ehea_is_hugepage(pfn)) {
- /* Add mem found in front of the hugepage */
- nr_pages = pfn - start_pfn;
- ret = ehea_update_busmap(start_pfn, nr_pages,
- EHEA_BUSMAP_ADD_SECT);
- if (ret)
- return ret;
-
- /* Skip the hugepage */
- pfn += (EHEA_HUGEPAGE_SIZE / PAGE_SIZE);
- start_pfn = pfn;
- } else
- pfn += (EHEA_SECTSIZE / PAGE_SIZE);
- }
-
- /* Add mem found behind the hugepage(s) */
- nr_pages = pfn - start_pfn;
- return ehea_update_busmap(start_pfn, nr_pages, EHEA_BUSMAP_ADD_SECT);
-}
-
-int ehea_create_busmap(void)
-{
- int ret;
-
- mutex_lock(&ehea_busmap_mutex);
- ehea_mr_len = 0;
- ret = walk_system_ram_range(0, 1ULL << MAX_PHYSMEM_BITS, NULL,
- ehea_create_busmap_callback);
- mutex_unlock(&ehea_busmap_mutex);
- return ret;
-}
-
-void ehea_destroy_busmap(void)
-{
- int top, dir;
- mutex_lock(&ehea_busmap_mutex);
- if (!ehea_bmap)
- goto out_destroy;
-
- for (top = 0; top < EHEA_MAP_ENTRIES; top++) {
- if (!ehea_bmap->top[top])
- continue;
-
- for (dir = 0; dir < EHEA_MAP_ENTRIES; dir++) {
- if (!ehea_bmap->top[top]->dir[dir])
- continue;
-
- kfree(ehea_bmap->top[top]->dir[dir]);
- }
-
- kfree(ehea_bmap->top[top]);
- }
-
- kfree(ehea_bmap);
- ehea_bmap = NULL;
-out_destroy:
- mutex_unlock(&ehea_busmap_mutex);
-}
-
-u64 ehea_map_vaddr(void *caddr)
-{
- int top, dir, idx;
- unsigned long index, offset;
-
- if (!ehea_bmap)
- return EHEA_INVAL_ADDR;
-
- index = __pa(caddr) >> SECTION_SIZE_BITS;
- top = (index >> EHEA_TOP_INDEX_SHIFT) & EHEA_INDEX_MASK;
- if (!ehea_bmap->top[top])
- return EHEA_INVAL_ADDR;
-
- dir = (index >> EHEA_DIR_INDEX_SHIFT) & EHEA_INDEX_MASK;
- if (!ehea_bmap->top[top]->dir[dir])
- return EHEA_INVAL_ADDR;
-
- idx = index & EHEA_INDEX_MASK;
- if (!ehea_bmap->top[top]->dir[dir]->ent[idx])
- return EHEA_INVAL_ADDR;
-
- offset = (unsigned long)caddr & (EHEA_SECTSIZE - 1);
- return ehea_bmap->top[top]->dir[dir]->ent[idx] | offset;
-}
-
-static inline void *ehea_calc_sectbase(int top, int dir, int idx)
-{
- unsigned long ret = idx;
- ret |= dir << EHEA_DIR_INDEX_SHIFT;
- ret |= top << EHEA_TOP_INDEX_SHIFT;
- return __va(ret << SECTION_SIZE_BITS);
-}
-
-static u64 ehea_reg_mr_section(int top, int dir, int idx, u64 *pt,
- struct ehea_adapter *adapter,
- struct ehea_mr *mr)
-{
- void *pg;
- u64 j, m, hret;
- unsigned long k = 0;
- u64 pt_abs = __pa(pt);
-
- void *sectbase = ehea_calc_sectbase(top, dir, idx);
-
- for (j = 0; j < (EHEA_PAGES_PER_SECTION / EHEA_MAX_RPAGE); j++) {
-
- for (m = 0; m < EHEA_MAX_RPAGE; m++) {
- pg = sectbase + ((k++) * EHEA_PAGESIZE);
- pt[m] = __pa(pg);
- }
- hret = ehea_h_register_rpage_mr(adapter->handle, mr->handle, 0,
- 0, pt_abs, EHEA_MAX_RPAGE);
-
- if ((hret != H_SUCCESS) &&
- (hret != H_PAGE_REGISTERED)) {
- ehea_h_free_resource(adapter->handle, mr->handle,
- FORCE_FREE);
- pr_err("register_rpage_mr failed\n");
- return hret;
- }
- }
- return hret;
-}
-
-static u64 ehea_reg_mr_sections(int top, int dir, u64 *pt,
- struct ehea_adapter *adapter,
- struct ehea_mr *mr)
-{
- u64 hret = H_SUCCESS;
- int idx;
-
- for (idx = 0; idx < EHEA_MAP_ENTRIES; idx++) {
- if (!ehea_bmap->top[top]->dir[dir]->ent[idx])
- continue;
-
- hret = ehea_reg_mr_section(top, dir, idx, pt, adapter, mr);
- if ((hret != H_SUCCESS) && (hret != H_PAGE_REGISTERED))
- return hret;
- }
- return hret;
-}
-
-static u64 ehea_reg_mr_dir_sections(int top, u64 *pt,
- struct ehea_adapter *adapter,
- struct ehea_mr *mr)
-{
- u64 hret = H_SUCCESS;
- int dir;
-
- for (dir = 0; dir < EHEA_MAP_ENTRIES; dir++) {
- if (!ehea_bmap->top[top]->dir[dir])
- continue;
-
- hret = ehea_reg_mr_sections(top, dir, pt, adapter, mr);
- if ((hret != H_SUCCESS) && (hret != H_PAGE_REGISTERED))
- return hret;
- }
- return hret;
-}
-
-int ehea_reg_kernel_mr(struct ehea_adapter *adapter, struct ehea_mr *mr)
-{
- int ret;
- u64 *pt;
- u64 hret;
- u32 acc_ctrl = EHEA_MR_ACC_CTRL;
-
- unsigned long top;
-
- pt = (void *)get_zeroed_page(GFP_KERNEL);
- if (!pt) {
- pr_err("no mem\n");
- ret = -ENOMEM;
- goto out;
- }
-
- hret = ehea_h_alloc_resource_mr(adapter->handle, EHEA_BUSMAP_START,
- ehea_mr_len, acc_ctrl, adapter->pd,
- &mr->handle, &mr->lkey);
-
- if (hret != H_SUCCESS) {
- pr_err("alloc_resource_mr failed\n");
- ret = -EIO;
- goto out;
- }
-
- if (!ehea_bmap) {
- ehea_h_free_resource(adapter->handle, mr->handle, FORCE_FREE);
- pr_err("no busmap available\n");
- ret = -EIO;
- goto out;
- }
-
- for (top = 0; top < EHEA_MAP_ENTRIES; top++) {
- if (!ehea_bmap->top[top])
- continue;
-
- hret = ehea_reg_mr_dir_sections(top, pt, adapter, mr);
- if((hret != H_PAGE_REGISTERED) && (hret != H_SUCCESS))
- break;
- }
-
- if (hret != H_SUCCESS) {
- ehea_h_free_resource(adapter->handle, mr->handle, FORCE_FREE);
- pr_err("registering mr failed\n");
- ret = -EIO;
- goto out;
- }
-
- mr->vaddr = EHEA_BUSMAP_START;
- mr->adapter = adapter;
- ret = 0;
-out:
- free_page((unsigned long)pt);
- return ret;
-}
-
-int ehea_rem_mr(struct ehea_mr *mr)
-{
- u64 hret;
-
- if (!mr || !mr->adapter)
- return -EINVAL;
-
- hret = ehea_h_free_resource(mr->adapter->handle, mr->handle,
- FORCE_FREE);
- if (hret != H_SUCCESS) {
- pr_err("destroy MR failed\n");
- return -EIO;
- }
-
- return 0;
-}
-
-int ehea_gen_smr(struct ehea_adapter *adapter, struct ehea_mr *old_mr,
- struct ehea_mr *shared_mr)
-{
- u64 hret;
-
- hret = ehea_h_register_smr(adapter->handle, old_mr->handle,
- old_mr->vaddr, EHEA_MR_ACC_CTRL,
- adapter->pd, shared_mr);
- if (hret != H_SUCCESS)
- return -EIO;
-
- shared_mr->adapter = adapter;
-
- return 0;
-}
-
-static void print_error_data(u64 *data)
-{
- int length;
- u64 type = EHEA_BMASK_GET(ERROR_DATA_TYPE, data[2]);
- u64 resource = data[1];
-
- length = EHEA_BMASK_GET(ERROR_DATA_LENGTH, data[0]);
-
- if (length > EHEA_PAGESIZE)
- length = EHEA_PAGESIZE;
-
- if (type == EHEA_AER_RESTYPE_QP)
- pr_err("QP (resource=%llX) state: AER=0x%llX, AERR=0x%llX, port=%llX\n",
- resource, data[6], data[12], data[22]);
- else if (type == EHEA_AER_RESTYPE_CQ)
- pr_err("CQ (resource=%llX) state: AER=0x%llX\n",
- resource, data[6]);
- else if (type == EHEA_AER_RESTYPE_EQ)
- pr_err("EQ (resource=%llX) state: AER=0x%llX\n",
- resource, data[6]);
-
- ehea_dump(data, length, "error data");
-}
-
-u64 ehea_error_data(struct ehea_adapter *adapter, u64 res_handle,
- u64 *aer, u64 *aerr)
-{
- unsigned long ret;
- u64 *rblock;
- u64 type = 0;
-
- rblock = (void *)get_zeroed_page(GFP_KERNEL);
- if (!rblock) {
- pr_err("Cannot allocate rblock memory\n");
- goto out;
- }
-
- ret = ehea_h_error_data(adapter->handle, res_handle, rblock);
-
- if (ret == H_SUCCESS) {
- type = EHEA_BMASK_GET(ERROR_DATA_TYPE, rblock[2]);
- *aer = rblock[6];
- *aerr = rblock[12];
- print_error_data(rblock);
- } else if (ret == H_R_STATE) {
- pr_err("No error data available: %llX\n", res_handle);
- } else
- pr_err("Error data could not be fetched: %llX\n", res_handle);
-
- free_page((unsigned long)rblock);
-out:
- return type;
-}
diff --git a/drivers/net/ethernet/ibm/ehea/ehea_qmr.h b/drivers/net/ethernet/ibm/ehea/ehea_qmr.h
deleted file mode 100644
index 7c7cccd820f7..000000000000
--- a/drivers/net/ethernet/ibm/ehea/ehea_qmr.h
+++ /dev/null
@@ -1,390 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * linux/drivers/net/ethernet/ibm/ehea/ehea_qmr.h
- *
- * eHEA ethernet device driver for IBM eServer System p
- *
- * (C) Copyright IBM Corp. 2006
- *
- * Authors:
- * Christoph Raisch <raisch@de.ibm.com>
- * Jan-Bernd Themann <themann@de.ibm.com>
- * Thomas Klein <tklein@de.ibm.com>
- */
-
-#ifndef __EHEA_QMR_H__
-#define __EHEA_QMR_H__
-
-#include <linux/prefetch.h>
-#include "ehea.h"
-#include "ehea_hw.h"
-
-/*
- * page size of ehea hardware queues
- */
-
-#define EHEA_PAGESHIFT 12
-#define EHEA_PAGESIZE (1UL << EHEA_PAGESHIFT)
-#define EHEA_SECTSIZE (1UL << 24)
-#define EHEA_PAGES_PER_SECTION (EHEA_SECTSIZE >> EHEA_PAGESHIFT)
-#define EHEA_HUGEPAGESHIFT 34
-#define EHEA_HUGEPAGE_SIZE (1UL << EHEA_HUGEPAGESHIFT)
-#define EHEA_HUGEPAGE_PFN_MASK ((EHEA_HUGEPAGE_SIZE - 1) >> PAGE_SHIFT)
-
-#if ((1UL << SECTION_SIZE_BITS) < EHEA_SECTSIZE)
-#error eHEA module cannot work if kernel sectionsize < ehea sectionsize
-#endif
-
-/* Some abbreviations used here:
- *
- * WQE - Work Queue Entry
- * SWQE - Send Work Queue Entry
- * RWQE - Receive Work Queue Entry
- * CQE - Completion Queue Entry
- * EQE - Event Queue Entry
- * MR - Memory Region
- */
-
-/* Use of WR_ID field for EHEA */
-#define EHEA_WR_ID_COUNT EHEA_BMASK_IBM(0, 19)
-#define EHEA_WR_ID_TYPE EHEA_BMASK_IBM(20, 23)
-#define EHEA_SWQE2_TYPE 0x1
-#define EHEA_SWQE3_TYPE 0x2
-#define EHEA_RWQE2_TYPE 0x3
-#define EHEA_RWQE3_TYPE 0x4
-#define EHEA_WR_ID_INDEX EHEA_BMASK_IBM(24, 47)
-#define EHEA_WR_ID_REFILL EHEA_BMASK_IBM(48, 63)
-
-struct ehea_vsgentry {
- u64 vaddr;
- u32 l_key;
- u32 len;
-};
-
-/* maximum number of sg entries allowed in a WQE */
-#define EHEA_MAX_WQE_SG_ENTRIES 252
-#define SWQE2_MAX_IMM (0xD0 - 0x30)
-#define SWQE3_MAX_IMM 224
-
-/* tx control flags for swqe */
-#define EHEA_SWQE_CRC 0x8000
-#define EHEA_SWQE_IP_CHECKSUM 0x4000
-#define EHEA_SWQE_TCP_CHECKSUM 0x2000
-#define EHEA_SWQE_TSO 0x1000
-#define EHEA_SWQE_SIGNALLED_COMPLETION 0x0800
-#define EHEA_SWQE_VLAN_INSERT 0x0400
-#define EHEA_SWQE_IMM_DATA_PRESENT 0x0200
-#define EHEA_SWQE_DESCRIPTORS_PRESENT 0x0100
-#define EHEA_SWQE_WRAP_CTL_REC 0x0080
-#define EHEA_SWQE_WRAP_CTL_FORCE 0x0040
-#define EHEA_SWQE_BIND 0x0020
-#define EHEA_SWQE_PURGE 0x0010
-
-/* sizeof(struct ehea_swqe) less the union */
-#define SWQE_HEADER_SIZE 32
-
-struct ehea_swqe {
- u64 wr_id;
- u16 tx_control;
- u16 vlan_tag;
- u8 reserved1;
- u8 ip_start;
- u8 ip_end;
- u8 immediate_data_length;
- u8 tcp_offset;
- u8 reserved2;
- u16 reserved2b;
- u8 wrap_tag;
- u8 descriptors; /* number of valid descriptors in WQE */
- u16 reserved3;
- u16 reserved4;
- u16 mss;
- u32 reserved5;
- union {
- /* Send WQE Format 1 */
- struct {
- struct ehea_vsgentry sg_list[EHEA_MAX_WQE_SG_ENTRIES];
- } no_immediate_data;
-
- /* Send WQE Format 2 */
- struct {
- struct ehea_vsgentry sg_entry;
- /* 0x30 */
- u8 immediate_data[SWQE2_MAX_IMM];
- /* 0xd0 */
- struct ehea_vsgentry sg_list[EHEA_MAX_WQE_SG_ENTRIES-1];
- } immdata_desc __packed;
-
- /* Send WQE Format 3 */
- struct {
- u8 immediate_data[SWQE3_MAX_IMM];
- } immdata_nodesc;
- } u;
-};
-
-struct ehea_rwqe {
- u64 wr_id; /* work request ID */
- u8 reserved1[5];
- u8 data_segments;
- u16 reserved2;
- u64 reserved3;
- u64 reserved4;
- struct ehea_vsgentry sg_list[EHEA_MAX_WQE_SG_ENTRIES];
-};
-
-#define EHEA_CQE_VLAN_TAG_XTRACT 0x0400
-
-#define EHEA_CQE_TYPE_RQ 0x60
-#define EHEA_CQE_STAT_ERR_MASK 0x700F
-#define EHEA_CQE_STAT_FAT_ERR_MASK 0xF
-#define EHEA_CQE_BLIND_CKSUM 0x8000
-#define EHEA_CQE_STAT_ERR_TCP 0x4000
-#define EHEA_CQE_STAT_ERR_IP 0x2000
-#define EHEA_CQE_STAT_ERR_CRC 0x1000
-
-/* Defines which bad send cqe stati lead to a port reset */
-#define EHEA_CQE_STAT_RESET_MASK 0x0002
-
-struct ehea_cqe {
- u64 wr_id; /* work request ID from WQE */
- u8 type;
- u8 valid;
- u16 status;
- u16 reserved1;
- u16 num_bytes_transfered;
- u16 vlan_tag;
- u16 inet_checksum_value;
- u8 reserved2;
- u8 header_length;
- u16 reserved3;
- u16 page_offset;
- u16 wqe_count;
- u32 qp_token;
- u32 timestamp;
- u32 reserved4;
- u64 reserved5[3];
-};
-
-#define EHEA_EQE_VALID EHEA_BMASK_IBM(0, 0)
-#define EHEA_EQE_IS_CQE EHEA_BMASK_IBM(1, 1)
-#define EHEA_EQE_IDENTIFIER EHEA_BMASK_IBM(2, 7)
-#define EHEA_EQE_QP_CQ_NUMBER EHEA_BMASK_IBM(8, 31)
-#define EHEA_EQE_QP_TOKEN EHEA_BMASK_IBM(32, 63)
-#define EHEA_EQE_CQ_TOKEN EHEA_BMASK_IBM(32, 63)
-#define EHEA_EQE_KEY EHEA_BMASK_IBM(32, 63)
-#define EHEA_EQE_PORT_NUMBER EHEA_BMASK_IBM(56, 63)
-#define EHEA_EQE_EQ_NUMBER EHEA_BMASK_IBM(48, 63)
-#define EHEA_EQE_SM_ID EHEA_BMASK_IBM(48, 63)
-#define EHEA_EQE_SM_MECH_NUMBER EHEA_BMASK_IBM(48, 55)
-#define EHEA_EQE_SM_PORT_NUMBER EHEA_BMASK_IBM(56, 63)
-
-#define EHEA_AER_RESTYPE_QP 0x8
-#define EHEA_AER_RESTYPE_CQ 0x4
-#define EHEA_AER_RESTYPE_EQ 0x3
-
-/* Defines which affiliated errors lead to a port reset */
-#define EHEA_AER_RESET_MASK 0xFFFFFFFFFEFFFFFFULL
-#define EHEA_AERR_RESET_MASK 0xFFFFFFFFFFFFFFFFULL
-
-struct ehea_eqe {
- u64 entry;
-};
-
-#define ERROR_DATA_LENGTH EHEA_BMASK_IBM(52, 63)
-#define ERROR_DATA_TYPE EHEA_BMASK_IBM(0, 7)
-
-static inline void *hw_qeit_calc(struct hw_queue *queue, u64 q_offset)
-{
- struct ehea_page *current_page;
-
- if (q_offset >= queue->queue_length)
- q_offset -= queue->queue_length;
- current_page = (queue->queue_pages)[q_offset >> EHEA_PAGESHIFT];
- return &current_page->entries[q_offset & (EHEA_PAGESIZE - 1)];
-}
-
-static inline void *hw_qeit_get(struct hw_queue *queue)
-{
- return hw_qeit_calc(queue, queue->current_q_offset);
-}
-
-static inline void hw_qeit_inc(struct hw_queue *queue)
-{
- queue->current_q_offset += queue->qe_size;
- if (queue->current_q_offset >= queue->queue_length) {
- queue->current_q_offset = 0;
- /* toggle the valid flag */
- queue->toggle_state = (~queue->toggle_state) & 1;
- }
-}
-
-static inline void *hw_qeit_get_inc(struct hw_queue *queue)
-{
- void *retvalue = hw_qeit_get(queue);
- hw_qeit_inc(queue);
- return retvalue;
-}
-
-static inline void *hw_qeit_get_inc_valid(struct hw_queue *queue)
-{
- struct ehea_cqe *retvalue = hw_qeit_get(queue);
- u8 valid = retvalue->valid;
- void *pref;
-
- if ((valid >> 7) == (queue->toggle_state & 1)) {
- /* this is a good one */
- hw_qeit_inc(queue);
- pref = hw_qeit_calc(queue, queue->current_q_offset);
- prefetch(pref);
- prefetch(pref + 128);
- } else
- retvalue = NULL;
- return retvalue;
-}
-
-static inline void *hw_qeit_get_valid(struct hw_queue *queue)
-{
- struct ehea_cqe *retvalue = hw_qeit_get(queue);
- void *pref;
- u8 valid;
-
- pref = hw_qeit_calc(queue, queue->current_q_offset);
- prefetch(pref);
- prefetch(pref + 128);
- prefetch(pref + 256);
- valid = retvalue->valid;
- if (!((valid >> 7) == (queue->toggle_state & 1)))
- retvalue = NULL;
- return retvalue;
-}
-
-static inline void *hw_qeit_reset(struct hw_queue *queue)
-{
- queue->current_q_offset = 0;
- return hw_qeit_get(queue);
-}
-
-static inline void *hw_qeit_eq_get_inc(struct hw_queue *queue)
-{
- u64 last_entry_in_q = queue->queue_length - queue->qe_size;
- void *retvalue;
-
- retvalue = hw_qeit_get(queue);
- queue->current_q_offset += queue->qe_size;
- if (queue->current_q_offset > last_entry_in_q) {
- queue->current_q_offset = 0;
- queue->toggle_state = (~queue->toggle_state) & 1;
- }
- return retvalue;
-}
-
-static inline void *hw_eqit_eq_get_inc_valid(struct hw_queue *queue)
-{
- void *retvalue = hw_qeit_get(queue);
- u32 qe = *(u8 *)retvalue;
- if ((qe >> 7) == (queue->toggle_state & 1))
- hw_qeit_eq_get_inc(queue);
- else
- retvalue = NULL;
- return retvalue;
-}
-
-static inline struct ehea_rwqe *ehea_get_next_rwqe(struct ehea_qp *qp,
- int rq_nr)
-{
- struct hw_queue *queue;
-
- if (rq_nr == 1)
- queue = &qp->hw_rqueue1;
- else if (rq_nr == 2)
- queue = &qp->hw_rqueue2;
- else
- queue = &qp->hw_rqueue3;
-
- return hw_qeit_get_inc(queue);
-}
-
-static inline struct ehea_swqe *ehea_get_swqe(struct ehea_qp *my_qp,
- int *wqe_index)
-{
- struct hw_queue *queue = &my_qp->hw_squeue;
- struct ehea_swqe *wqe_p;
-
- *wqe_index = (queue->current_q_offset) >> (7 + EHEA_SG_SQ);
- wqe_p = hw_qeit_get_inc(&my_qp->hw_squeue);
-
- return wqe_p;
-}
-
-static inline void ehea_post_swqe(struct ehea_qp *my_qp, struct ehea_swqe *swqe)
-{
- iosync();
- ehea_update_sqa(my_qp, 1);
-}
-
-static inline struct ehea_cqe *ehea_poll_rq1(struct ehea_qp *qp, int *wqe_index)
-{
- struct hw_queue *queue = &qp->hw_rqueue1;
-
- *wqe_index = (queue->current_q_offset) >> (7 + EHEA_SG_RQ1);
- return hw_qeit_get_valid(queue);
-}
-
-static inline void ehea_inc_cq(struct ehea_cq *cq)
-{
- hw_qeit_inc(&cq->hw_queue);
-}
-
-static inline void ehea_inc_rq1(struct ehea_qp *qp)
-{
- hw_qeit_inc(&qp->hw_rqueue1);
-}
-
-static inline struct ehea_cqe *ehea_poll_cq(struct ehea_cq *my_cq)
-{
- return hw_qeit_get_valid(&my_cq->hw_queue);
-}
-
-#define EHEA_CQ_REGISTER_ORIG 0
-#define EHEA_EQ_REGISTER_ORIG 0
-
-enum ehea_eq_type {
- EHEA_EQ = 0, /* event queue */
- EHEA_NEQ /* notification event queue */
-};
-
-struct ehea_eq *ehea_create_eq(struct ehea_adapter *adapter,
- enum ehea_eq_type type,
- const u32 length, const u8 eqe_gen);
-
-int ehea_destroy_eq(struct ehea_eq *eq);
-
-struct ehea_eqe *ehea_poll_eq(struct ehea_eq *eq);
-
-struct ehea_cq *ehea_create_cq(struct ehea_adapter *adapter, int cqe,
- u64 eq_handle, u32 cq_token);
-
-int ehea_destroy_cq(struct ehea_cq *cq);
-
-struct ehea_qp *ehea_create_qp(struct ehea_adapter *adapter, u32 pd,
- struct ehea_qp_init_attr *init_attr);
-
-int ehea_destroy_qp(struct ehea_qp *qp);
-
-int ehea_reg_kernel_mr(struct ehea_adapter *adapter, struct ehea_mr *mr);
-
-int ehea_gen_smr(struct ehea_adapter *adapter, struct ehea_mr *old_mr,
- struct ehea_mr *shared_mr);
-
-int ehea_rem_mr(struct ehea_mr *mr);
-
-u64 ehea_error_data(struct ehea_adapter *adapter, u64 res_handle,
- u64 *aer, u64 *aerr);
-
-int ehea_add_sect_bmap(unsigned long pfn, unsigned long nr_pages);
-int ehea_rem_sect_bmap(unsigned long pfn, unsigned long nr_pages);
-int ehea_create_busmap(void);
-void ehea_destroy_busmap(void);
-u64 ehea_map_vaddr(void *caddr);
-
-#endif /* __EHEA_QMR_H__ */
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 744d6585a949..543e566425c1 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -5678,6 +5678,24 @@ static int mvneta_probe(struct platform_device *pdev)
"use SW buffer management\n");
mvneta_bm_put(pp->bm_priv);
pp->bm_priv = NULL;
+ } else if (!device_link_add(&pdev->dev,
+ &pp->bm_priv->pdev->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER)) {
+ /*
+ * Link guarantees BM resumes before mvneta.
+ * Without it, BM may not be ready when
+ * mvneta_bm_port_init() runs on resume,
+ * causing stale buffer addresses and a crash.
+ * Fall back to SW management to be safe.
+ */
+ dev_warn(&pdev->dev,
+ "failed to link to BM, use SW buffer management\n");
+ mvneta_bm_pool_destroy(pp->bm_priv,
+ pp->pool_long, 1 << pp->id);
+ mvneta_bm_pool_destroy(pp->bm_priv,
+ pp->pool_short, 1 << pp->id);
+ mvneta_bm_put(pp->bm_priv);
+ pp->bm_priv = NULL;
}
}
/* Set RX packet offset correction for platforms, whose
diff --git a/drivers/net/ethernet/marvell/mvneta_bm.c b/drivers/net/ethernet/marvell/mvneta_bm.c
index 6bb380494919..e0c693c0a910 100644
--- a/drivers/net/ethernet/marvell/mvneta_bm.c
+++ b/drivers/net/ethernet/marvell/mvneta_bm.c
@@ -129,6 +129,7 @@ static int mvneta_bm_pool_create(struct mvneta_bm *priv,
if (!IS_ALIGNED((u32)bm_pool->virt_addr, MVNETA_BM_POOL_PTR_ALIGN)) {
dma_free_coherent(&pdev->dev, size_bytes, bm_pool->virt_addr,
bm_pool->phys_addr);
+ bm_pool->virt_addr = NULL;
dev_err(&pdev->dev, "BM pool %d is not %d bytes aligned\n",
bm_pool->id, MVNETA_BM_POOL_PTR_ALIGN);
return -ENOMEM;
@@ -139,6 +140,7 @@ static int mvneta_bm_pool_create(struct mvneta_bm *priv,
if (err < 0) {
dma_free_coherent(&pdev->dev, size_bytes, bm_pool->virt_addr,
bm_pool->phys_addr);
+ bm_pool->virt_addr = NULL;
return err;
}
@@ -477,6 +479,75 @@ static void mvneta_bm_remove(struct platform_device *pdev)
clk_disable_unprepare(priv->clk);
}
+static int mvneta_bm_suspend(struct device *dev)
+{
+ struct mvneta_bm *priv = dev_get_drvdata(dev);
+ int i;
+
+ /* Drain buffers and free pool resources while BM is still clocked */
+ for (i = 0; i < MVNETA_BM_POOLS_NUM; i++) {
+ struct mvneta_bm_pool *bm_pool = &priv->bm_pools[i];
+ int size_bytes;
+
+ if (bm_pool->type == MVNETA_BM_FREE)
+ continue;
+
+ mvneta_bm_bufs_free(priv, bm_pool, bm_pool->port_map);
+ if (bm_pool->hwbm_pool.buf_num)
+ dev_warn(&priv->pdev->dev,
+ "pool %d: %d buffers not freed\n",
+ bm_pool->id, bm_pool->hwbm_pool.buf_num);
+
+ mvneta_bm_pool_disable(priv, bm_pool->id);
+
+ if (bm_pool->virt_addr) {
+ size_bytes = sizeof(u32) * bm_pool->hwbm_pool.size;
+ dma_free_coherent(&priv->pdev->dev, size_bytes,
+ bm_pool->virt_addr,
+ bm_pool->phys_addr);
+ bm_pool->virt_addr = NULL;
+ }
+ /*
+ * Safe to destroy: device_link guarantees all mvneta ports
+ * have already suspended, so no hwbm_pool_add() can be in
+ * progress holding buf_lock. Pairs with mutex_init() in
+ * mvneta_bm_pool_use() on resume.
+ */
+ mutex_destroy(&bm_pool->hwbm_pool.buf_lock);
+ bm_pool->type = MVNETA_BM_FREE;
+ }
+
+ mvneta_bm_write(priv, MVNETA_BM_COMMAND_REG, MVNETA_BM_STOP_MASK);
+ clk_disable_unprepare(priv->clk);
+ return 0;
+}
+
+static int mvneta_bm_resume(struct device *dev)
+{
+ struct mvneta_bm *priv = dev_get_drvdata(dev);
+ int i, err;
+
+ err = clk_prepare_enable(priv->clk);
+ if (err)
+ return err;
+
+ /* Reinitialize BM hardware; pools are refilled by mvneta_resume() */
+ mvneta_bm_default_set(priv);
+
+ /* Restore pool registers lost during clock gating */
+ for (i = 0; i < MVNETA_BM_POOLS_NUM; i++) {
+ mvneta_bm_write(priv, MVNETA_BM_POOL_READ_PTR_REG(i), 0);
+ mvneta_bm_write(priv, MVNETA_BM_POOL_WRITE_PTR_REG(i), 0);
+ mvneta_bm_write(priv, MVNETA_BM_POOL_SIZE_REG(i),
+ priv->bm_pools[i].hwbm_pool.size);
+ }
+
+ mvneta_bm_write(priv, MVNETA_BM_COMMAND_REG, MVNETA_BM_START_MASK);
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(mvneta_bm_pm_ops, mvneta_bm_suspend, mvneta_bm_resume);
+
static const struct of_device_id mvneta_bm_match[] = {
{ .compatible = "marvell,armada-380-neta-bm" },
{ }
@@ -489,6 +560,7 @@ static struct platform_driver mvneta_bm_driver = {
.driver = {
.name = MVNETA_BM_DRIVER_NAME,
.of_match_table = mvneta_bm_match,
+ .pm = pm_sleep_ptr(&mvneta_bm_pm_ops),
},
};
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
index 714e47f68d93..f87cdf1b971d 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
@@ -803,6 +803,7 @@ struct npc_set_pkind {
*/
u8 var_len_off_mask; /* Mask for length with in offset */
u8 shift_dir; /* shift direction to get length of the header at var_len_off */
+ u8 skip_size; /* l2 size to skip */
};
/* NPA mbox message formats */
@@ -1267,6 +1268,7 @@ struct nix_rss_flowkey_cfg {
#define NIX_FLOW_KEY_TYPE_IPV4_PROTO BIT(21)
#define NIX_FLOW_KEY_TYPE_AH BIT(22)
#define NIX_FLOW_KEY_TYPE_ESP BIT(23)
+#define NIX_FLOW_KEY_TYPE_ROCEV2 BIT(24)
#define NIX_FLOW_KEY_TYPE_L4_DST_ONLY BIT(28)
#define NIX_FLOW_KEY_TYPE_L4_SRC_ONLY BIT(29)
#define NIX_FLOW_KEY_TYPE_L3_DST_ONLY BIT(30)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/npc.h
index eaed172f1606..719b3618eeb5 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/npc.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/npc.h
@@ -161,10 +161,12 @@ enum npc_kpu_lh_ltype {
* Software assigns pkind for each incoming port such as CGX
* Ethernet interfaces, LBK interfaces, etc.
*/
-#define NPC_UNRESERVED_PKIND_COUNT NPC_RX_CPT_HDR_PTP_PKIND
+#define NPC_UNRESERVED_PKIND_COUNT NPC_RX_SKIP_SIZE_PKIND
enum npc_pkind_type {
NPC_RX_LBK_PKIND = 0ULL,
+ NPC_RX_SKIP_SIZE_PKIND = 46ULL,
+ NPC_RX_CPT_SKIP_SIZE_PKIND = 50ULL,
NPC_RX_CPT_HDR_PTP_PKIND = 54ULL,
NPC_RX_CUSTOM_PRE_L2_PKIND = 55ULL,
NPC_RX_VLAN_EXDSA_PKIND = 56ULL,
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
index 7f3505ae6860..c5610f242687 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
@@ -1181,7 +1181,7 @@ void rvu_switch_enable_lbk_link(struct rvu *rvu, u16 pcifunc, bool ena);
int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir,
u64 pkind, u8 var_len_off, u8 var_len_off_mask,
- u8 shift_dir);
+ u8 shift_dir, u8 skip_size);
int rvu_get_hwvf(struct rvu *rvu, int pcifunc);
/* CN10K MCS */
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
index 0297c7ab0614..8e3bb47eb3ba 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
@@ -4305,6 +4305,13 @@ static int set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg)
keyoff_marker = false;
}
break;
+ case NIX_FLOW_KEY_TYPE_ROCEV2:
+ field->hdr_offset = 5;
+ field->bytesm1 = 2; /* Destination QP */
+ field->ltype_mask = 0xF;
+ field->lid = NPC_LID_LE;
+ field->ltype_match = NPC_LT_LE_ROCEV2;
+ break;
}
field->ena = 1;
@@ -5392,7 +5399,7 @@ void rvu_nix_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int nixlf)
/* reset HW config done for Switch headers */
rvu_npc_set_parse_mode(rvu, pcifunc, OTX2_PRIV_FLAGS_DEFAULT,
- (PKIND_TX | PKIND_RX), 0, 0, 0, 0);
+ (PKIND_TX | PKIND_RX), 0, 0, 0, 0, 0);
/* Disabling CGX and NPC config done for PTP */
if (pfvf->hw_rx_tstamp_en) {
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
index c7bc0b3a29b9..08b83de9beb4 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
@@ -4194,10 +4194,40 @@ npc_set_var_len_offset_pkind(struct rvu *rvu, u16 pcifunc, u64 pkind,
return 0;
}
+static int npc_set_skip_size_pkind(struct rvu *rvu, u16 pcifunc, u64 pkind,
+ u8 skip_size)
+{
+ struct npc_kpu_action0 *act0;
+ int blkaddr;
+ u64 val;
+
+ blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, pcifunc);
+ if (blkaddr < 0) {
+ dev_err(rvu->dev, "%s: NPC block not implemented\n", __func__);
+ return -EINVAL;
+ }
+
+ val = rvu_read64(rvu, blkaddr, NPC_AF_PKINDX_ACTION0(pkind));
+ act0 = (struct npc_kpu_action0 *)&val;
+ act0->ptr_advance = skip_size;
+ rvu_write64(rvu, blkaddr, NPC_AF_PKINDX_ACTION0(pkind), val);
+
+ /* Update CPT_HR new PKIND */
+ val = rvu_read64(rvu, blkaddr, NPC_AF_PKINDX_ACTION0(pkind + 4));
+ act0 = (struct npc_kpu_action0 *)&val;
+ act0->ptr_advance = (skip_size + 40);
+ act0->next_state = NPC_S_KPU1_CPT_HDR;
+ act0->var_len_offset = (skip_size + 6);
+ act0->var_len_mask = 0xe0;
+ act0->var_len_shift = 0x5;
+ act0->var_len_right = 0x1;
+ rvu_write64(rvu, blkaddr, NPC_AF_PKINDX_ACTION0(pkind + 4), val);
+ return 0;
+}
+
int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir,
u64 pkind, u8 var_len_off, u8 var_len_off_mask,
- u8 shift_dir)
-
+ u8 shift_dir, u8 skip_size)
{
struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, pcifunc);
int blkaddr, nixlf, rc, intf_mode;
@@ -4218,6 +4248,12 @@ int rvu_npc_set_parse_mode(struct rvu *rvu, u16 pcifunc, u64 mode, u8 dir,
shift_dir);
if (rc)
return rc;
+ } else if (pkind >= NPC_RX_SKIP_SIZE_PKIND &&
+ pkind <= NPC_RX_SKIP_SIZE_PKIND + 3) {
+ rc = npc_set_skip_size_pkind(rvu, pcifunc, pkind,
+ skip_size);
+ if (rc)
+ return rc;
}
rxpkind = pkind;
txpkind = pkind;
@@ -4254,7 +4290,8 @@ int rvu_mbox_handler_npc_set_pkind(struct rvu *rvu, struct npc_set_pkind *req,
{
return rvu_npc_set_parse_mode(rvu, req->hdr.pcifunc, req->mode,
req->dir, req->pkind, req->var_len_off,
- req->var_len_off_mask, req->shift_dir);
+ req->var_len_off_mask, req->shift_dir,
+ req->skip_size);
}
int rvu_mbox_handler_npc_read_base_steer_rule(struct rvu *rvu,
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c
index dbf173196608..8e41431c7f9c 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c
@@ -656,6 +656,7 @@ static int cn20k_pool_aq_init(struct otx2_nic *pfvf, u16 pool_id,
pp_params.nid = NUMA_NO_NODE;
pp_params.dev = pfvf->dev;
pp_params.dma_dir = DMA_FROM_DEVICE;
+ pp_params.netdev = pfvf->netdev;
pool->page_pool = page_pool_create(&pp_params);
if (IS_ERR(pool->page_pool)) {
netdev_err(pfvf->netdev, "Creation of page pool failed\n");
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
index 3d253132a17f..ca73a94db794 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
@@ -1035,7 +1035,6 @@ int otx2_sq_init(struct otx2_nic *pfvf, u16 qidx, u16 sqb_aura)
if (qidx > pfvf->hw.xdp_queues)
otx2_attach_xsk_buff(pfvf, sq, (qidx - pfvf->hw.xdp_queues));
-
chan_offset = qidx % pfvf->hw.tx_chan_cnt;
err = pfvf->hw_ops->sq_aq_init(pfvf, qidx, chan_offset, sqb_aura);
if (err) {
@@ -1517,6 +1516,7 @@ int otx2_pool_aq_init(struct otx2_nic *pfvf, u16 pool_id,
pp_params.nid = NUMA_NO_NODE;
pp_params.dev = pfvf->dev;
pp_params.dma_dir = DMA_FROM_DEVICE;
+ pp_params.netdev = pfvf->netdev;
pool->page_pool = page_pool_create(&pp_params);
if (IS_ERR(pool->page_pool)) {
netdev_err(pfvf->netdev, "Creation of page pool failed\n");
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c
index cbb272a96359..0d0cd093b3c6 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c
@@ -1118,7 +1118,7 @@ u8 mlxsw_sp_acl_erp_delta_value(const struct mlxsw_sp_acl_erp_delta *delta,
}
void mlxsw_sp_acl_erp_delta_clear(const struct mlxsw_sp_acl_erp_delta *delta,
- const char *enc_key)
+ char *enc_key)
{
u16 start = delta->start;
u8 mask = delta->mask;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.h
index 010204f73ea4..67cc7a5737dd 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.h
@@ -245,7 +245,7 @@ u8 mlxsw_sp_acl_erp_delta_mask(const struct mlxsw_sp_acl_erp_delta *delta);
u8 mlxsw_sp_acl_erp_delta_value(const struct mlxsw_sp_acl_erp_delta *delta,
const char *enc_key);
void mlxsw_sp_acl_erp_delta_clear(const struct mlxsw_sp_acl_erp_delta *delta,
- const char *enc_key);
+ char *enc_key);
struct mlxsw_sp_acl_erp_mask;
diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
index 48b94ce77490..88c5c52e0e38 100644
--- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
@@ -16,7 +16,7 @@
#include <linux/net_tstamp.h>
#include <linux/ptp_classify.h>
#include <linux/ptp_pch.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#define PCH_GBE_MAR_ENTRIES 16
#define PCH_GBE_SHORT_PKT 64
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c
index 528114877677..c999754afb5f 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c
@@ -558,18 +558,15 @@ struct sync_item {
void ionic_rx_filter_sync(struct ionic_lif *lif)
{
struct device *dev = lif->ionic->dev;
- struct list_head sync_add_list;
- struct list_head sync_del_list;
struct sync_item *sync_item;
struct ionic_rx_filter *f;
+ LIST_HEAD(sync_add_list);
+ LIST_HEAD(sync_del_list);
struct hlist_head *head;
struct hlist_node *tmp;
struct sync_item *spos;
unsigned int i;
- INIT_LIST_HEAD(&sync_add_list);
- INIT_LIST_HEAD(&sync_del_list);
-
clear_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state);
/* Copy the filters to be added and deleted
diff --git a/drivers/net/ethernet/sgi/ioc3-eth.c b/drivers/net/ethernet/sgi/ioc3-eth.c
index 39731069d99e..009f37105eaf 100644
--- a/drivers/net/ethernet/sgi/ioc3-eth.c
+++ b/drivers/net/ethernet/sgi/ioc3-eth.c
@@ -967,11 +967,12 @@ static void ioc3eth_remove(struct platform_device *pdev)
struct net_device *dev = platform_get_drvdata(pdev);
struct ioc3_private *ip = netdev_priv(dev);
+ unregister_netdev(dev);
+ timer_delete_sync(&ip->ioc3_timer);
+
dma_free_coherent(ip->dma_dev, RX_RING_SIZE, ip->rxr, ip->rxr_dma);
dma_free_coherent(ip->dma_dev, TX_RING_SIZE + SZ_16K - 1, ip->tx_ring, ip->txr_dma);
- unregister_netdev(dev);
- timer_delete_sync(&ip->ioc3_timer);
free_netdev(dev);
}
@@ -1061,9 +1062,9 @@ static netdev_tx_t ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev)
d1 = dma_map_single(ip->dma_dev, skb->data, s1, DMA_TO_DEVICE);
if (dma_mapping_error(ip->dma_dev, d1))
goto drop_packet;
- d2 = dma_map_single(ip->dma_dev, (void *)b2, s1, DMA_TO_DEVICE);
+ d2 = dma_map_single(ip->dma_dev, (void *)b2, s2, DMA_TO_DEVICE);
if (dma_mapping_error(ip->dma_dev, d2)) {
- dma_unmap_single(ip->dma_dev, d1, len, DMA_TO_DEVICE);
+ dma_unmap_single(ip->dma_dev, d1, s1, DMA_TO_DEVICE);
goto drop_packet;
}
desc->p1 = cpu_to_be64(ioc3_map(d1, PCI64_ATTR_PREF));
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 862001d09aa8..c1812a98365b 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -1737,7 +1737,7 @@ static struct sk_buff *find_skb(struct netpoll *np, int len, int reserve)
netpoll_zap_completion_queue();
repeat:
- skb = alloc_skb(len, GFP_ATOMIC);
+ skb = alloc_skb(len, GFP_ATOMIC | __GFP_NOWARN);
if (!skb)
skb = netcons_skb_pop(np, len);
diff --git a/drivers/net/phy/mdio_device.c b/drivers/net/phy/mdio_device.c
index 56080d3d2d25..a18263d5bb02 100644
--- a/drivers/net/phy/mdio_device.c
+++ b/drivers/net/phy/mdio_device.c
@@ -8,7 +8,7 @@
#include <linux/delay.h>
#include <linux/errno.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 087ac63f9193..59dfe35afa54 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -153,8 +153,7 @@ static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
* phylink_set_port_modes() - set the port type modes in the ethtool mask
* @mask: ethtool link mode mask
*
- * Sets all the port type modes in the ethtool mask. MAC drivers should
- * use this in their 'validate' callback.
+ * Sets all the port type modes in the ethtool mask.
*/
void phylink_set_port_modes(unsigned long *mask)
{
@@ -2095,9 +2094,9 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
/*
* This is the new way of dealing with flow control for PHYs,
* as described by Timur Tabi in commit 529ed1275263 ("net: phy:
- * phy drivers should not set SUPPORTED_[Asym_]Pause") except
- * using our validate call to the MAC, we rely upon the MAC
- * clearing the bits from both supported and advertising fields.
+ * phy drivers should not set SUPPORTED_[Asym_]Pause"). MAC drivers
+ * set their support using the MAC_SYM_PAUSE and MAC_ASYM_PAUSE
+ * capabilities and must NOT change the phy's pause settings directly.
*/
phy_support_asym_pause(phy);
@@ -3931,9 +3930,9 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
/*
* This is the new way of dealing with flow control for PHYs,
* as described by Timur Tabi in commit 529ed1275263 ("net: phy:
- * phy drivers should not set SUPPORTED_[Asym_]Pause") except
- * using our validate call to the MAC, we rely upon the MAC
- * clearing the bits from both supported and advertising fields.
+ * phy drivers should not set SUPPORTED_[Asym_]Pause"). MAC drivers
+ * set their support using the MAC_SYM_PAUSE and MAC_ASYM_PAUSE
+ * capabilities and must NOT change the phy's pause settings directly.
*/
phy_support_asym_pause(phy);
diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c
index c880c95c41a5..d51e43170e03 100644
--- a/drivers/net/usb/rtl8150.c
+++ b/drivers/net/usb/rtl8150.c
@@ -732,7 +732,9 @@ static void set_carrier(struct net_device *netdev)
rtl8150_t *dev = netdev_priv(netdev);
short tmp;
- get_registers(dev, CSCR, 2, &tmp);
+ if (get_registers(dev, CSCR, 2, &tmp))
+ return;
+
if (tmp & CSCR_LINK_STATUS)
netif_carrier_on(netdev);
else
diff --git a/include/linux/net.h b/include/linux/net.h
index fdcf9956805c..3d82966e2243 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -47,6 +47,29 @@ typedef struct sockopt {
int optlen;
} sockopt_t;
+/*
+ * Initialize a user-backed sockopt_t from the (optval, optlen) __user pair of
+ * a getsockopt() callback. Used by transitional __user getsockopt wrappers
+ * while the proto-layer callbacks are converted to take a sockopt_t; the
+ * caller writes opt->optlen back to the user optlen after the callback.
+ */
+static inline int sockopt_init_user(sockopt_t *opt, char __user *optval,
+ int __user *optlen)
+{
+ int len;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+ if (len < 0)
+ return -EINVAL;
+
+ iov_iter_ubuf(&opt->iter_out, ITER_DEST, optval, len);
+ iov_iter_ubuf(&opt->iter_in, ITER_SOURCE, optval, len);
+ opt->optlen = len;
+
+ return 0;
+}
+
struct poll_table_struct;
struct pipe_inode_info;
struct inode;
diff --git a/include/net/bond_3ad.h b/include/net/bond_3ad.h
index 05572c19e14b..ef667dff2972 100644
--- a/include/net/bond_3ad.h
+++ b/include/net/bond_3ad.h
@@ -302,8 +302,8 @@ void bond_3ad_state_machine_handler(struct work_struct *);
void bond_3ad_initiate_agg_selection(struct bonding *bond, int timeout);
void bond_3ad_adapter_speed_duplex_changed(struct slave *slave);
void bond_3ad_handle_link_change(struct slave *slave, char link);
-int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info);
-int __bond_3ad_get_active_agg_info(struct bonding *bond,
+int bond_3ad_get_active_agg_info(const struct bonding *bond, struct ad_info *ad_info);
+int __bond_3ad_get_active_agg_info(const struct bonding *bond,
struct ad_info *ad_info);
int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond,
struct slave *slave);
diff --git a/include/net/bonding.h b/include/net/bonding.h
index 2c54a36a8477..598d56b1bc97 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -345,14 +345,14 @@ static inline bool bond_mode_uses_primary(int mode)
mode == BOND_MODE_ALB;
}
-static inline bool bond_uses_primary(struct bonding *bond)
+static inline bool bond_uses_primary(const struct bonding *bond)
{
return bond_mode_uses_primary(BOND_MODE(bond));
}
-static inline struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond)
+static inline struct net_device *bond_option_active_slave_get_rcu(const struct bonding *bond)
{
- struct slave *slave = rcu_dereference_rtnl(bond->curr_active_slave);
+ const struct slave *slave = rcu_dereference_rtnl(bond->curr_active_slave);
return bond_uses_primary(bond) && slave ? slave->dev : NULL;
}
@@ -703,7 +703,7 @@ void bond_setup(struct net_device *bond_dev);
unsigned int bond_get_num_tx_queues(void);
int bond_netlink_init(void);
void bond_netlink_fini(void);
-struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond);
+struct net_device *bond_option_active_slave_get_rcu(const struct bonding *bond);
const char *bond_slave_link_status(s8 link);
struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
struct net_device *end_dev,
diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h
index 7dee0ae616e3..c6b94790fa81 100644
--- a/include/net/fib_rules.h
+++ b/include/net/fib_rules.h
@@ -82,7 +82,7 @@ struct fib_rules_ops {
struct fib_rule_hdr *,
struct nlattr **,
struct netlink_ext_ack *);
- int (*delete)(struct fib_rule *);
+ void (*delete)(struct fib_rule *);
int (*compare)(struct fib_rule *,
struct fib_rule_hdr *,
struct nlattr **);
@@ -93,11 +93,13 @@ struct fib_rules_ops {
/* Called after modifications to the rules set, must flush
* the route cache if one exists. */
void (*flush_cache)(struct fib_rules_ops *ops);
+ bool (*need_rtnl)(struct net *net);
int nlgroup;
struct list_head rules_list;
struct module *owner;
struct net *fro_net;
+ struct mutex lock;
struct rcu_head rcu;
};
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index c63a3c4967ae..0a35355fb0f3 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -302,7 +302,8 @@ static inline struct fib_table *fib_get_table(struct net *net, u32 id)
&net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX] :
&net->ipv4.fib_table_hash[TABLE_MAIN_INDEX];
- tb_hlist = rcu_dereference_rtnl(hlist_first_rcu(ptr));
+ /* Only fib4_rules_init() adds fib_table. */
+ tb_hlist = rcu_dereference_protected(hlist_first_rcu(ptr), true);
return hlist_entry(tb_hlist, struct fib_table, tb_hlist);
}
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index 6e27c56514df..59506320558a 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -127,6 +127,7 @@ struct netns_ipv4 {
atomic_t fib_num_tclassid_users;
#endif
struct hlist_head *fib_table_hash;
+ spinlock_t fib_table_hash_lock;
struct sock *fibnl;
struct hlist_head *fib_info_hash;
unsigned int fib_info_hash_bits;
diff --git a/include/net/udp.h b/include/net/udp.h
index 8262e2b215b4..1fee17274745 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -430,7 +430,7 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
netdev_features_t features,
bool is_ipv6);
int udp_lib_getsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int __user *optlen);
+ sockopt_t *opt);
int udp_lib_setsockopt(struct sock *sk, int level, int optname,
sockptr_t optval, unsigned int optlen,
int (*push_pending_frames)(struct sock *));
diff --git a/include/uapi/linux/if_addr.h b/include/uapi/linux/if_addr.h
index aa7958b4e41d..7fb630b7fe31 100644
--- a/include/uapi/linux/if_addr.h
+++ b/include/uapi/linux/if_addr.h
@@ -36,6 +36,7 @@ enum {
IFA_RT_PRIORITY, /* u32, priority/metric for prefix route */
IFA_TARGET_NETNSID,
IFA_PROTO, /* u8, address protocol */
+ IFA_MC_USERS, /* u32, multicast group users */
__IFA_MAX,
};
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index bb2f012b454e..22622283f59b 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -404,23 +404,14 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
/* send a batman ogm packet */
static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet)
{
- struct net_device *mesh_iface;
-
if (!forw_packet->if_incoming) {
pr_err("Error - can't forward packet: incoming iface not specified\n");
return;
}
- mesh_iface = forw_packet->if_incoming->mesh_iface;
-
if (WARN_ON(!forw_packet->if_outgoing))
return;
- if (forw_packet->if_outgoing->mesh_iface != mesh_iface) {
- pr_warn("%s: mesh interface switch for queued OGM\n", __func__);
- return;
- }
-
if (forw_packet->if_incoming->if_status != BATADV_IF_ACTIVE)
return;
@@ -910,8 +901,7 @@ out:
static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
{
- if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
- hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
+ if (hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
return;
mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c
index 4841f0f1a9b1..262e40040007 100644
--- a/net/batman-adv/bat_v_elp.c
+++ b/net/batman-adv/bat_v_elp.c
@@ -90,12 +90,6 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh,
u32 throughput;
int ret;
- /* don't query throughput when no longer associated with any
- * batman-adv interface
- */
- if (!mesh_iface)
- return false;
-
/* if the user specified a customised value for this interface, then
* return it directly
*/
@@ -311,8 +305,7 @@ static void batadv_v_elp_periodic_work(struct work_struct *work)
goto out;
/* we are in the process of shutting this interface down */
- if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
- hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
+ if (hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
goto out;
/* the interface was enabled but may not be ready yet */
diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
index 037921aad35d..e921d49f7ece 100644
--- a/net/batman-adv/bat_v_ogm.c
+++ b/net/batman-adv/bat_v_ogm.c
@@ -115,14 +115,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)
/**
* batadv_v_ogm_send_to_if() - send a batman ogm using a given interface
- * @bat_priv: the bat priv with all the mesh interface information
* @skb: the OGM to send
* @hard_iface: the interface to use to send the OGM
*/
-static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv,
- struct sk_buff *skb,
+static void batadv_v_ogm_send_to_if(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface)
{
+ struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
+
if (hard_iface->if_status != BATADV_IF_ACTIVE) {
kfree_skb(skb);
return;
@@ -189,7 +189,6 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
/**
* batadv_v_ogm_aggr_send() - flush & send aggregation queue
- * @bat_priv: the bat priv with all the mesh interface information
* @hard_iface: the interface with the aggregation queue to flush
*
* Aggregates all OGMv2 packets currently in the aggregation queue into a
@@ -199,8 +198,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
*
* Caller needs to hold the hard_iface->bat_v.aggr_list.lock.
*/
-static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
- struct batadv_hard_iface *hard_iface)
+static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
{
unsigned int aggr_len = hard_iface->bat_v.aggr_len;
struct sk_buff *skb_aggr;
@@ -230,26 +228,21 @@ static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
consume_skb(skb);
}
- batadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface);
+ batadv_v_ogm_send_to_if(skb_aggr, hard_iface);
}
/**
* batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface
- * @bat_priv: the bat priv with all the mesh interface information
* @skb: the OGM to queue
* @hard_iface: the interface to queue the OGM on
*/
-static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
- struct sk_buff *skb,
+static void batadv_v_ogm_queue_on_if(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface)
{
- if (hard_iface->mesh_iface != bat_priv->mesh_iface) {
- kfree_skb(skb);
- return;
- }
+ struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
if (!READ_ONCE(bat_priv->aggregated_ogms)) {
- batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface);
+ batadv_v_ogm_send_to_if(skb, hard_iface);
return;
}
@@ -260,7 +253,7 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
}
if (!batadv_v_ogm_queue_left(skb, hard_iface))
- batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+ batadv_v_ogm_aggr_send(hard_iface);
hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
__skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
@@ -357,7 +350,7 @@ static void batadv_v_ogm_send_meshif(struct batadv_priv *bat_priv)
break;
}
- batadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface);
+ batadv_v_ogm_queue_on_if(skb_tmp, hard_iface);
batadv_hardif_put(hard_iface);
}
rcu_read_unlock();
@@ -397,14 +390,12 @@ void batadv_v_ogm_aggr_work(struct work_struct *work)
{
struct batadv_hard_iface_bat_v *batv;
struct batadv_hard_iface *hard_iface;
- struct batadv_priv *bat_priv;
batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work);
hard_iface = container_of(batv, struct batadv_hard_iface, bat_v);
- bat_priv = netdev_priv(hard_iface->mesh_iface);
spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
- batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+ batadv_v_ogm_aggr_send(hard_iface);
spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
batadv_v_ogm_start_queue_timer(hard_iface);
@@ -601,7 +592,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,
if_outgoing->net_dev->name, ntohl(ogm_forward->throughput),
ogm_forward->ttl, if_incoming->net_dev->name);
- batadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing);
+ batadv_v_ogm_queue_on_if(skb, if_outgoing);
out:
batadv_orig_ifinfo_put(orig_ifinfo);
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index 5c73f6ba16cf..f9a1fadf8de9 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -344,7 +344,6 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
struct sk_buff *skb;
struct ethhdr *ethhdr;
struct batadv_hard_iface *primary_if;
- struct net_device *mesh_iface;
u8 *hw_src;
struct batadv_bla_claim_dst local_claim_dest;
__be32 zeroip = 0;
@@ -357,14 +356,10 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
sizeof(local_claim_dest));
local_claim_dest.type = claimtype;
- mesh_iface = READ_ONCE(primary_if->mesh_iface);
- if (!mesh_iface)
- goto out;
-
skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
/* IP DST: 0.0.0.0 */
zeroip,
- mesh_iface,
+ primary_if->mesh_iface,
/* IP SRC: 0.0.0.0 */
zeroip,
/* Ethernet DST: Broadcast */
@@ -442,7 +437,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
}
skb_reset_mac_header(skb);
- skb->protocol = eth_type_trans(skb, mesh_iface);
+ skb->protocol = eth_type_trans(skb, primary_if->mesh_iface);
batadv_inc_counter(bat_priv, BATADV_CNT_RX);
batadv_add_counter(bat_priv, BATADV_CNT_RX_BYTES,
skb->len + ETH_HLEN);
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 03d01c20a954..b6867576bbaf 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -63,6 +63,7 @@ void batadv_hardif_release(struct kref *ref)
struct batadv_hard_iface *hard_iface;
hard_iface = container_of(ref, struct batadv_hard_iface, refcount);
+ netdev_put(hard_iface->mesh_iface, &hard_iface->meshif_dev_tracker);
netdev_put(hard_iface->net_dev, &hard_iface->dev_tracker);
kfree_rcu(hard_iface, rcu);
@@ -75,21 +76,21 @@ void batadv_hardif_release(struct kref *ref)
* Return: batadv_hard_iface of net_dev (with increased refcnt), NULL on errors
*/
struct batadv_hard_iface *
-batadv_hardif_get_by_netdev(const struct net_device *net_dev)
+batadv_hardif_get_by_netdev(struct net_device *net_dev)
{
struct batadv_hard_iface *hard_iface;
+ struct net_device *mesh_iface;
- rcu_read_lock();
- list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
- if (hard_iface->net_dev == net_dev &&
- kref_get_unless_zero(&hard_iface->refcount))
- goto out;
- }
+ ASSERT_RTNL();
- hard_iface = NULL;
+ mesh_iface = netdev_master_upper_dev_get(net_dev);
+ if (!mesh_iface || !batadv_meshif_is_valid(mesh_iface))
+ return NULL;
+
+ hard_iface = netdev_lower_dev_get_private(mesh_iface, net_dev);
+ if (!kref_get_unless_zero(&hard_iface->refcount))
+ return NULL;
-out:
- rcu_read_unlock();
return hard_iface;
}
@@ -245,7 +246,7 @@ struct net_device *__batadv_get_real_netdev(struct net_device *netdev)
}
hard_iface = batadv_hardif_get_by_netdev(netdev);
- if (!hard_iface || !hard_iface->mesh_iface)
+ if (!hard_iface)
goto out;
net = dev_net(hard_iface->mesh_iface);
@@ -539,16 +540,10 @@ static void batadv_check_known_mac_addr(const struct batadv_hard_iface *hard_ifa
const struct batadv_hard_iface *tmp_hard_iface;
struct list_head *iter;
- if (!mesh_iface)
- return;
-
netdev_for_each_lower_private(mesh_iface, tmp_hard_iface, iter) {
if (tmp_hard_iface == hard_iface)
continue;
- if (tmp_hard_iface->if_status == BATADV_IF_NOT_IN_USE)
- continue;
-
if (!batadv_compare_eth(tmp_hard_iface->net_dev->dev_addr,
hard_iface->net_dev->dev_addr))
continue;
@@ -574,9 +569,6 @@ static void batadv_hardif_recalc_extra_skbroom(struct net_device *mesh_iface)
rcu_read_lock();
netdev_for_each_lower_private_rcu(mesh_iface, hard_iface, iter) {
- if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
- continue;
-
lower_header_len = max_t(unsigned short, lower_header_len,
hard_iface->net_dev->hard_header_len);
@@ -723,38 +715,63 @@ batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)
}
/**
- * batadv_hardif_enable_interface() - Enslave hard interface to mesh interface
- * @hard_iface: hard interface to add to mesh interface
+ * batadv_hardif_enable_interface() - Enslave interface to mesh interface
+ * @net_dev: netdev struct of the interface to add to mesh interface
* @mesh_iface: netdev struct of the mesh interface
*
* Return: 0 on success or negative error number in case of failure
*/
-int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
+int batadv_hardif_enable_interface(struct net_device *net_dev,
struct net_device *mesh_iface)
{
struct batadv_priv *bat_priv;
__be16 ethertype = htons(ETH_P_BATMAN);
int max_header_len = batadv_max_header_len();
+ struct batadv_hard_iface *hard_iface;
unsigned int required_mtu;
unsigned int hardif_mtu;
bool fragmentation;
int ret;
- hardif_mtu = READ_ONCE(hard_iface->net_dev->mtu);
+ ASSERT_RTNL();
+
+ if (!batadv_is_valid_iface(net_dev))
+ return -EINVAL;
+
+ hardif_mtu = READ_ONCE(net_dev->mtu);
required_mtu = READ_ONCE(mesh_iface->mtu) + max_header_len;
if (hardif_mtu < ETH_MIN_MTU + max_header_len)
return -EINVAL;
- if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
- goto out;
+ hard_iface = kzalloc_obj(*hard_iface, GFP_ATOMIC);
+ if (!hard_iface)
+ return -ENOMEM;
- kref_get(&hard_iface->refcount);
+ netdev_hold(net_dev, &hard_iface->dev_tracker, GFP_ATOMIC);
+ hard_iface->net_dev = net_dev;
+
+ hard_iface->if_status = BATADV_IF_INACTIVE;
+
+ INIT_HLIST_HEAD(&hard_iface->neigh_list);
+
+ mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
+ spin_lock_init(&hard_iface->neigh_list_lock);
+ kref_init(&hard_iface->refcount);
+
+ hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
+ if (batadv_is_wifi_hardif(hard_iface))
+ hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
+
+ WRITE_ONCE(hard_iface->hop_penalty, 0);
+
+ batadv_v_hardif_init(hard_iface);
netdev_hold(mesh_iface, &hard_iface->meshif_dev_tracker, GFP_ATOMIC);
hard_iface->mesh_iface = mesh_iface;
bat_priv = netdev_priv(hard_iface->mesh_iface);
+ bat_priv->hardif_generation++;
ret = netdev_master_upper_dev_link(hard_iface->net_dev,
mesh_iface, hard_iface, NULL, NULL);
if (ret)
@@ -764,9 +781,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
if (ret < 0)
goto err_upper;
- hard_iface->if_status = BATADV_IF_INACTIVE;
-
- kref_get(&hard_iface->refcount);
hard_iface->batman_adv_ptype.type = ethertype;
hard_iface->batman_adv_ptype.func = batadv_batman_skb_recv;
hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
@@ -802,14 +816,11 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
if (bat_priv->algo_ops->iface.enabled)
bat_priv->algo_ops->iface.enabled(hard_iface);
-out:
return 0;
err_upper:
netdev_upper_dev_unlink(hard_iface->net_dev, mesh_iface);
err_dev:
- hard_iface->mesh_iface = NULL;
- netdev_put(mesh_iface, &hard_iface->meshif_dev_tracker);
batadv_hardif_put(hard_iface);
return ret;
}
@@ -821,17 +832,18 @@ err_dev:
void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
- struct batadv_hard_iface *primary_if = NULL;
+ struct batadv_hard_iface *primary_if;
+
+ ASSERT_RTNL();
batadv_hardif_deactivate_interface(hard_iface);
if (hard_iface->if_status != BATADV_IF_INACTIVE)
- goto out;
+ return;
batadv_info(hard_iface->mesh_iface, "Removing interface: %s\n",
hard_iface->net_dev->name);
dev_remove_pack(&hard_iface->batman_adv_ptype);
- batadv_hardif_put(hard_iface);
primary_if = batadv_primary_if_get_selected(bat_priv);
if (hard_iface == primary_if) {
@@ -842,15 +854,16 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
batadv_hardif_put(new_if);
}
+ batadv_hardif_put(primary_if);
bat_priv->algo_ops->iface.disable(hard_iface);
- hard_iface->if_status = BATADV_IF_NOT_IN_USE;
+ hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
/* delete all references to this hard_iface */
batadv_purge_orig_ref(bat_priv);
batadv_purge_outstanding_packets(bat_priv, hard_iface);
- netdev_put(hard_iface->mesh_iface, &hard_iface->meshif_dev_tracker);
+ bat_priv->hardif_generation++;
netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->mesh_iface);
batadv_hardif_recalc_extra_skbroom(hard_iface->mesh_iface);
@@ -858,67 +871,6 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
if (list_empty(&hard_iface->mesh_iface->adj_list.lower))
batadv_gw_check_client_stop(bat_priv);
- hard_iface->mesh_iface = NULL;
- batadv_hardif_put(hard_iface);
-
-out:
- batadv_hardif_put(primary_if);
-}
-
-static struct batadv_hard_iface *
-batadv_hardif_add_interface(struct net_device *net_dev)
-{
- struct batadv_hard_iface *hard_iface;
-
- ASSERT_RTNL();
-
- if (!batadv_is_valid_iface(net_dev))
- return NULL;
-
- hard_iface = kzalloc_obj(*hard_iface, GFP_ATOMIC);
- if (!hard_iface)
- return NULL;
-
- netdev_hold(net_dev, &hard_iface->dev_tracker, GFP_ATOMIC);
- hard_iface->net_dev = net_dev;
-
- hard_iface->mesh_iface = NULL;
- hard_iface->if_status = BATADV_IF_NOT_IN_USE;
-
- INIT_LIST_HEAD(&hard_iface->list);
- INIT_HLIST_HEAD(&hard_iface->neigh_list);
-
- mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
- spin_lock_init(&hard_iface->neigh_list_lock);
- kref_init(&hard_iface->refcount);
-
- hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
- if (batadv_is_wifi_hardif(hard_iface))
- hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
-
- WRITE_ONCE(hard_iface->hop_penalty, 0);
-
- batadv_v_hardif_init(hard_iface);
-
- kref_get(&hard_iface->refcount);
- list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
- batadv_hardif_generation++;
-
- return hard_iface;
-}
-
-static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
-{
- ASSERT_RTNL();
-
- /* first deactivate interface */
- if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
- batadv_hardif_disable_interface(hard_iface);
-
- if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
- return;
-
- hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
batadv_hardif_put(hard_iface);
}
@@ -1082,10 +1034,6 @@ static int batadv_hard_if_event(struct notifier_block *this,
batadv_wifi_net_device_event(event, net_dev);
hard_iface = batadv_hardif_get_by_netdev(net_dev);
- if (!hard_iface && (event == NETDEV_REGISTER ||
- event == NETDEV_POST_TYPE_CHANGE))
- hard_iface = batadv_hardif_add_interface(net_dev);
-
if (!hard_iface)
goto out;
@@ -1099,19 +1047,12 @@ static int batadv_hard_if_event(struct notifier_block *this,
break;
case NETDEV_UNREGISTER:
case NETDEV_PRE_TYPE_CHANGE:
- list_del_rcu(&hard_iface->list);
- batadv_hardif_generation++;
-
- batadv_hardif_remove_interface(hard_iface);
+ batadv_hardif_disable_interface(hard_iface);
break;
case NETDEV_CHANGEMTU:
- if (hard_iface->mesh_iface)
- batadv_update_min_mtu(hard_iface->mesh_iface);
+ batadv_update_min_mtu(hard_iface->mesh_iface);
break;
case NETDEV_CHANGEADDR:
- if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
- goto hardif_put;
-
batadv_check_known_mac_addr(hard_iface);
bat_priv = netdev_priv(hard_iface->mesh_iface);
diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h
index af31696c3978..935f47ca9a48 100644
--- a/net/batman-adv/hard-interface.h
+++ b/net/batman-adv/hard-interface.h
@@ -22,12 +22,6 @@
*/
enum batadv_hard_if_state {
/**
- * @BATADV_IF_NOT_IN_USE: interface is not used as slave interface of a
- * batman-adv mesh interface
- */
- BATADV_IF_NOT_IN_USE,
-
- /**
* @BATADV_IF_TO_BE_REMOVED: interface will be removed from mesh
* interface
*/
@@ -74,8 +68,8 @@ u32 batadv_netdev_get_wifi_flags(struct net_device *net_dev);
u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface);
bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface);
struct batadv_hard_iface*
-batadv_hardif_get_by_netdev(const struct net_device *net_dev);
-int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
+batadv_hardif_get_by_netdev(struct net_device *net_dev);
+int batadv_hardif_enable_interface(struct net_device *net_dev,
struct net_device *mesh_iface);
void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface);
int batadv_hardif_min_mtu(struct net_device *mesh_iface);
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index 4d3807a645b7..fb2b0e35e349 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -59,11 +59,6 @@
#include "tp_meter.h"
#include "translation-table.h"
-/* List manipulations on hardif_list have to be rtnl_lock()'ed,
- * list traversals just rcu-locked
- */
-struct list_head batadv_hardif_list;
-unsigned int batadv_hardif_generation;
static int (*batadv_rx_handler[256])(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
@@ -95,7 +90,6 @@ static int __init batadv_init(void)
if (ret < 0)
return ret;
- INIT_LIST_HEAD(&batadv_hardif_list);
batadv_algo_init();
batadv_recv_handler_init();
@@ -450,9 +444,6 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
if (unlikely(skb->mac_len != ETH_HLEN || !skb_mac_header(skb)))
goto err_free;
- if (!hard_iface->mesh_iface)
- goto err_free;
-
bat_priv = netdev_priv(hard_iface->mesh_iface);
if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index f68fc8b7239c..e738758ee4a7 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -226,9 +226,6 @@ static inline int batadv_print_vid(unsigned short vid)
return -1;
}
-extern struct list_head batadv_hardif_list;
-extern unsigned int batadv_hardif_generation;
-
extern struct workqueue_struct *batadv_event_workqueue;
int batadv_mesh_init(struct net_device *mesh_iface);
diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c
index 511f70e0706a..5cc72bbc4570 100644
--- a/net/batman-adv/mesh-interface.c
+++ b/net/batman-adv/mesh-interface.c
@@ -837,18 +837,7 @@ static int batadv_meshif_slave_add(struct net_device *dev,
struct net_device *slave_dev,
struct netlink_ext_ack *extack)
{
- struct batadv_hard_iface *hard_iface;
- int ret = -EINVAL;
-
- hard_iface = batadv_hardif_get_by_netdev(slave_dev);
- if (!hard_iface || hard_iface->mesh_iface)
- goto out;
-
- ret = batadv_hardif_enable_interface(hard_iface, dev);
-
-out:
- batadv_hardif_put(hard_iface);
- return ret;
+ return batadv_hardif_enable_interface(slave_dev, dev);
}
/**
diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
index 4cf9e3c54ad3..d2bc48c70714 100644
--- a/net/batman-adv/netlink.c
+++ b/net/batman-adv/netlink.c
@@ -968,7 +968,7 @@ batadv_netlink_dump_hardif(struct sk_buff *msg, struct netlink_callback *cb)
bat_priv = netdev_priv(mesh_iface);
rtnl_lock();
- cb->seq = batadv_hardif_generation << 1 | 1;
+ cb->seq = bat_priv->hardif_generation << 1 | 1;
netdev_for_each_lower_private(mesh_iface, hard_iface, iter) {
if (i++ < skip)
@@ -1211,7 +1211,9 @@ batadv_netlink_get_hardif_from_ifindex(struct batadv_priv *bat_priv,
if (!hard_dev)
return ERR_PTR(-ENODEV);
+ rtnl_lock();
hard_iface = batadv_hardif_get_by_netdev(hard_dev);
+ rtnl_unlock();
if (!hard_iface)
goto err_put_harddev;
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index 9b38bd9e8da7..48f837cf665a 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -1033,7 +1033,6 @@ batadv_purge_neigh_ifinfo(struct batadv_priv *bat_priv,
/* don't purge if the interface is not (going) down */
if (if_outgoing->if_status != BATADV_IF_INACTIVE &&
- if_outgoing->if_status != BATADV_IF_NOT_IN_USE &&
if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED)
continue;
@@ -1077,7 +1076,6 @@ batadv_purge_orig_ifinfo(struct batadv_priv *bat_priv,
/* don't purge if the interface is not (going) down */
if (if_outgoing->if_status != BATADV_IF_INACTIVE &&
- if_outgoing->if_status != BATADV_IF_NOT_IN_USE &&
if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED)
continue;
@@ -1127,10 +1125,8 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
if (batadv_has_timed_out(last_seen, BATADV_PURGE_TIMEOUT) ||
if_incoming->if_status == BATADV_IF_INACTIVE ||
- if_incoming->if_status == BATADV_IF_NOT_IN_USE ||
if_incoming->if_status == BATADV_IF_TO_BE_REMOVED) {
if (if_incoming->if_status == BATADV_IF_INACTIVE ||
- if_incoming->if_status == BATADV_IF_NOT_IN_USE ||
if_incoming->if_status == BATADV_IF_TO_BE_REMOVED)
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"neighbor purge: originator %pM, neighbor: %pM, iface: %s\n",
diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
index c2eea7dbc488..00467aa79de9 100644
--- a/net/batman-adv/tp_meter.c
+++ b/net/batman-adv/tp_meter.c
@@ -358,28 +358,16 @@ batadv_tp_list_find_sender_session(struct batadv_priv *bat_priv, const u8 *dst,
}
/**
- * batadv_tp_vars_common_release() - release batadv_tp_vars_common from lists
+ * batadv_tp_sender_release() - release batadv_tp_sender
* and queue for free after rcu grace period
- * @ref: kref pointer of the batadv_tp_vars_common
+ * @ref: kref pointer of the batadv_tp_sender
*/
-static void batadv_tp_vars_common_release(struct kref *ref)
+static void batadv_tp_sender_release(struct kref *ref)
{
- struct batadv_tp_vars_common *tp_vars;
- struct batadv_tp_unacked *un, *safe;
-
- tp_vars = container_of(ref, struct batadv_tp_vars_common, refcount);
-
- /* lock should not be needed because this object is now out of any
- * context!
- */
- spin_lock_bh(&tp_vars->unacked_lock);
- list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
- list_del(&un->list);
- kfree(un);
- }
- spin_unlock_bh(&tp_vars->unacked_lock);
+ struct batadv_tp_sender *tp_vars;
- kfree_rcu(tp_vars, rcu);
+ tp_vars = container_of(ref, struct batadv_tp_sender, common.refcount);
+ kfree_rcu(tp_vars, common.rcu);
}
/**
@@ -392,7 +380,7 @@ static void batadv_tp_sender_put(struct batadv_tp_sender *tp_vars)
if (!tp_vars)
return;
- kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release);
+ kref_put(&tp_vars->common.refcount, batadv_tp_sender_release);
}
/**
@@ -1145,9 +1133,6 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
init_waitqueue_head(&tp_vars->more_bytes);
init_completion(&tp_vars->finished);
- spin_lock_init(&tp_vars->common.unacked_lock);
- INIT_LIST_HEAD(&tp_vars->common.unacked_list);
-
spin_lock_init(&tp_vars->cc_lock);
tp_vars->prerandom_offset = 0;
@@ -1252,6 +1237,33 @@ batadv_tp_list_find_receiver_session(struct batadv_priv *bat_priv, const u8 *dst
}
/**
+ * batadv_tp_receiver_release() - release batadv_tp_receiver
+ * and queue for free after rcu grace period
+ * @ref: kref pointer of the batadv_tp_receiver
+ */
+static void batadv_tp_receiver_release(struct kref *ref)
+{
+ struct batadv_tp_receiver *tp_vars;
+ struct batadv_tp_unacked *safe;
+ struct batadv_tp_unacked *un;
+
+ tp_vars = container_of(ref, struct batadv_tp_receiver, common.refcount);
+
+ /* lock should not be needed because this object is now out of any
+ * context!
+ */
+ spin_lock_bh(&tp_vars->ack_seqno_lock);
+ list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+ list_del(&un->list);
+ kfree(un);
+ tp_vars->unacked_count--;
+ }
+ spin_unlock_bh(&tp_vars->ack_seqno_lock);
+
+ kfree_rcu(tp_vars, common.rcu);
+}
+
+/**
* batadv_tp_receiver_put() - decrement the batadv_tp_receiver
* refcounter and possibly release it
* @tp_vars: the private data of the current TP meter session to be free'd
@@ -1261,7 +1273,7 @@ static void batadv_tp_receiver_put(struct batadv_tp_receiver *tp_vars)
if (!tp_vars)
return;
- kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release);
+ kref_put(&tp_vars->common.refcount, batadv_tp_receiver_release);
}
/**
@@ -1304,13 +1316,13 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
if (batadv_tp_list_detach(&tp_vars->common))
batadv_tp_receiver_put(tp_vars);
- spin_lock_bh(&tp_vars->common.unacked_lock);
- list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) {
+ spin_lock_bh(&tp_vars->ack_seqno_lock);
+ list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
list_del(&un->list);
kfree(un);
- tp_vars->common.unacked_count--;
+ tp_vars->unacked_count--;
}
- spin_unlock_bh(&tp_vars->common.unacked_lock);
+ spin_unlock_bh(&tp_vars->ack_seqno_lock);
/* drop reference of timer */
if (WARN_ON(atomic_xchg(&tp_vars->receiving, 0) != 1))
@@ -1403,69 +1415,106 @@ out:
*/
static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
u32 seqno, u32 payload_len)
- __must_hold(&tp_vars->common.unacked_lock)
+ __must_hold(&tp_vars->ack_seqno_lock)
{
- struct batadv_tp_unacked *un, *new;
- bool added = false;
-
- new = kmalloc_obj(*new, GFP_ATOMIC);
- if (unlikely(!new))
- return false;
-
- new->seqno = seqno;
- new->len = payload_len;
-
- /* if the list is empty immediately attach this new object */
- if (list_empty(&tp_vars->common.unacked_list)) {
- list_add(&new->list, &tp_vars->common.unacked_list);
- tp_vars->common.unacked_count++;
- return true;
- }
-
- /* otherwise loop over the list and either drop the packet because this
- * is a duplicate or store it at the right position.
+ struct list_head *pos = &tp_vars->unacked_list;
+ struct batadv_tp_unacked *new = NULL;
+ u32 end_seqno = seqno + payload_len;
+ struct batadv_tp_unacked *safe;
+ struct batadv_tp_unacked *un;
+
+ /* loop over the list to find either an existing entry which the new
+ * seqno range can be merged with or the position at which a new entry
+ * has to be inserted.
*
* The iteration is done in the reverse way because it is likely that
* the last received packet (the one being processed now) has a bigger
* seqno than all the others already stored.
*/
- list_for_each_entry_reverse(un, &tp_vars->common.unacked_list, list) {
- /* check for duplicates */
- if (new->seqno == un->seqno) {
- if (new->len > un->len)
- un->len = new->len;
- kfree(new);
- added = true;
- break;
- }
-
- /* look for the right position */
- if (batadv_seq_before(new->seqno, un->seqno))
+ list_for_each_entry_reverse(un, &tp_vars->unacked_list, list) {
+ /* look for the right position - an un which is smaller */
+ if (batadv_seq_before(seqno, un->seqno))
continue;
- /* as soon as an entry having a bigger seqno is found, the new
- * one is attached _after_ it. In this way the list is kept in
- * ascending order
+ /* smaller/equal seqno was found but they might be directly
+ * after another or overlapping. keep only a single entry
+ *
+ * It is already known that:
+ *
+ * un->seqno <= seqno
+ *
+ * When establishing that:
+ *
+ * seqno <= un->seqno + un->len
+ *
+ * Then it is not necessary to add a new entry because the
+ * smaller/equal seqno of un might already contain the new
+ * received packet or we only add new data directly after
+ * the end of un. The latter can be identified using:
+ *
+ * un->seqno + un->len <= end_seqno
*/
- list_add(&new->list, &un->list);
- added = true;
- tp_vars->common.unacked_count++;
+ if (!batadv_seq_before(un->seqno + un->len, seqno)) {
+ /* new data directly after un? */
+ if (!batadv_seq_before(end_seqno, un->seqno + un->len))
+ un->len = end_seqno - un->seqno;
+
+ /* un now represents both old un + new range and has to
+ * be used to check if the gap to the next seqno range
+ * was closed
+ */
+ new = un;
+ } else {
+ /* as soon as an entry having a smaller seqno is found,
+ * the new one is attached _after_ it. In this way the
+ * list is kept in ascending order
+ */
+ pos = &un->list;
+ }
+
break;
}
- /* received packet with smallest seqno out of order; add it to front */
- if (!added) {
- list_add(&new->list, &tp_vars->common.unacked_list);
- tp_vars->common.unacked_count++;
+ /* no entry to merge with was found; insert a new one after the entry
+ * with the next smaller seqno (or at the front of the list when the
+ * new seqno is the smallest or the list is empty)
+ */
+ if (!new) {
+ new = kmalloc_obj(*new, GFP_ATOMIC);
+ if (unlikely(!new))
+ return false;
+
+ new->seqno = seqno;
+ new->len = payload_len;
+
+ list_add(&new->list, pos);
+ tp_vars->unacked_count++;
+ }
+
+ /* check if new filled the gap to the next list entries */
+ un = new;
+ list_for_each_entry_safe_continue(un, safe, &tp_vars->unacked_list, list) {
+ if (batadv_seq_before(end_seqno, un->seqno))
+ break;
+
+ /* next entry is overlapping or adjacent - combine both */
+ if (batadv_seq_before(end_seqno, un->seqno + un->len)) {
+ end_seqno = un->seqno + un->len;
+ new->len = end_seqno - new->seqno;
+ }
+
+ list_del(&un->list);
+ kfree(un);
+ tp_vars->unacked_count--;
}
/* remove the last (biggest) unacked seqno when list is too large */
- if (tp_vars->common.unacked_count > BATADV_TP_MAX_UNACKED) {
- un = list_last_entry(&tp_vars->common.unacked_list,
+ if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) {
+ un = list_last_entry(&tp_vars->unacked_list,
struct batadv_tp_unacked, list);
list_del(&un->list);
kfree(un);
- tp_vars->common.unacked_count--;
+ tp_vars->unacked_count--;
}
return true;
@@ -1477,7 +1526,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
* @tp_vars: the private data of the current TP meter session
*/
static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
- __must_hold(&tp_vars->common.unacked_lock)
+ __must_hold(&tp_vars->ack_seqno_lock)
{
struct batadv_tp_unacked *un, *safe;
u32 to_ack;
@@ -1485,7 +1534,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
/* go through the unacked packet list and possibly ACK them as
* well
*/
- list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) {
+ list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
/* the list is ordered, therefore it is possible to stop as soon
* there is a gap between the last acked seqno and the seqno of
* the packet under inspection
@@ -1493,14 +1542,14 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
if (batadv_seq_before(tp_vars->last_recv, un->seqno))
break;
- to_ack = un->seqno + un->len - tp_vars->last_recv;
+ to_ack = un->seqno + un->len;
- if (batadv_seq_before(tp_vars->last_recv, un->seqno + un->len))
- tp_vars->last_recv += to_ack;
+ if (batadv_seq_before(tp_vars->last_recv, to_ack))
+ tp_vars->last_recv = to_ack;
list_del(&un->list);
kfree(un);
- tp_vars->common.unacked_count--;
+ tp_vars->unacked_count--;
}
}
@@ -1547,9 +1596,9 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
tp_vars->common.bat_priv = bat_priv;
kref_init(&tp_vars->common.refcount);
- spin_lock_init(&tp_vars->common.unacked_lock);
- INIT_LIST_HEAD(&tp_vars->common.unacked_list);
- tp_vars->common.unacked_count = 0;
+ spin_lock_init(&tp_vars->ack_seqno_lock);
+ INIT_LIST_HEAD(&tp_vars->unacked_list);
+ tp_vars->unacked_count = 0;
kref_get(&tp_vars->common.refcount);
timer_setup(&tp_vars->common.timer, batadv_tp_receiver_shutdown, 0);
@@ -1609,7 +1658,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
WRITE_ONCE(tp_vars->last_recv_time, jiffies);
}
- spin_lock_bh(&tp_vars->common.unacked_lock);
+ spin_lock_bh(&tp_vars->ack_seqno_lock);
/* if the packet is a duplicate, it may be the case that an ACK has been
* lost. Resend the ACK
@@ -1625,7 +1674,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
* not been enqueued correctly
*/
if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) {
- spin_unlock_bh(&tp_vars->common.unacked_lock);
+ spin_unlock_bh(&tp_vars->ack_seqno_lock);
goto out;
}
@@ -1641,7 +1690,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
send_ack:
to_ack = tp_vars->last_recv;
- spin_unlock_bh(&tp_vars->common.unacked_lock);
+ spin_unlock_bh(&tp_vars->ack_seqno_lock);
/* send the ACK. If the received packet was out of order, the ACK that
* is going to be sent is a duplicate (the sender will count them and
diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
index 1c9fb21985f6..49bf2ed9ecdc 100644
--- a/net/batman-adv/tvlv.c
+++ b/net/batman-adv/tvlv.c
@@ -443,6 +443,54 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
}
/**
+ * batadv_tvlv_hdr_next() - move a tvlv buffer cursor to the next container
+ * @tvlv_value: cursor into the tvlv buffer, advanced past the returned
+ * container's content on success
+ * @tvlv_value_len: remaining length of the tvlv buffer, reduced by the returned
+ * container's size on success
+ *
+ * Parses a single container header at the current cursor position and, if a
+ * complete container is available, advances the cursor and remaining length
+ * past it. The returned header stays valid; its content is located at
+ * (returned header + 1) and is ntohs(hdr->len) bytes long.
+ *
+ * Return: pointer to the next tvlv container header, or NULL if no further
+ * complete container is present in the buffer.
+ */
+static struct batadv_tvlv_hdr *batadv_tvlv_hdr_next(void **tvlv_value, u16 *tvlv_value_len)
+{
+ struct batadv_tvlv_hdr *tvlv_hdr;
+ u16 tvlv_value_cont_len;
+ void *tvlv_value_cont;
+ u16 tvlv_len;
+
+ tvlv_value_cont = *tvlv_value;
+ tvlv_len = *tvlv_value_len;
+
+ if (tvlv_len < sizeof(*tvlv_hdr))
+ return NULL;
+
+ tvlv_hdr = tvlv_value_cont;
+ tvlv_value_cont_len = ntohs(tvlv_hdr->len);
+ tvlv_value_cont = tvlv_hdr + 1;
+ tvlv_len -= sizeof(*tvlv_hdr);
+
+ if (tvlv_value_cont_len > tvlv_len)
+ return NULL;
+
+ /* the next tvlv header is accessed assuming (at least) 2-byte
+ * alignment, so it must start at an even offset.
+ */
+ if (tvlv_value_cont_len & 1)
+ return NULL;
+
+ *tvlv_value = (u8 *)tvlv_value_cont + tvlv_value_cont_len;
+ *tvlv_value_len = tvlv_len - tvlv_value_cont_len;
+
+ return tvlv_hdr;
+}
+
+/**
* batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container
* @tvlv_value: tvlv content
* @tvlv_value_len: tvlv content length
@@ -457,28 +505,10 @@ static bool batadv_tvlv_containers_contain(void *tvlv_value,
u8 version)
{
struct batadv_tvlv_hdr *tvlv_hdr;
- u16 tvlv_value_cont_len;
-
- while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
- tvlv_hdr = tvlv_value;
- tvlv_value_cont_len = ntohs(tvlv_hdr->len);
- tvlv_value = tvlv_hdr + 1;
- tvlv_value_len -= sizeof(*tvlv_hdr);
-
- if (tvlv_value_cont_len > tvlv_value_len)
- break;
-
- /* the next tvlv header is accessed assuming (at least) 2-byte
- * alignment, so it must start at an even offset.
- */
- if (tvlv_value_cont_len & 1)
- break;
+ while ((tvlv_hdr = batadv_tvlv_hdr_next(&tvlv_value, &tvlv_value_len))) {
if (tvlv_hdr->type == type && tvlv_hdr->version == version)
return true;
-
- tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
- tvlv_value_len -= tvlv_value_cont_len;
}
return false;
@@ -511,20 +541,8 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
int ret = NET_RX_SUCCESS;
- while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
- tvlv_hdr = tvlv_value;
+ while ((tvlv_hdr = batadv_tvlv_hdr_next(&tvlv_value, &tvlv_value_len))) {
tvlv_value_cont_len = ntohs(tvlv_hdr->len);
- tvlv_value = tvlv_hdr + 1;
- tvlv_value_len -= sizeof(*tvlv_hdr);
-
- if (tvlv_value_cont_len > tvlv_value_len)
- break;
-
- /* the next tvlv header is accessed assuming (at least) 2-byte
- * alignment, so it must start at an even offset.
- */
- if (tvlv_value_cont_len & 1)
- break;
tvlv_handler = batadv_tvlv_handler_get(bat_priv,
tvlv_hdr->type,
@@ -532,11 +550,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
ret |= batadv_tvlv_call_handler(bat_priv, tvlv_handler,
packet_type, orig_node, skb,
- tvlv_value,
+ tvlv_hdr + 1,
tvlv_value_cont_len);
batadv_tvlv_handler_put(tvlv_handler);
- tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
- tvlv_value_len -= tvlv_value_cont_len;
}
if (packet_type != BATADV_IV_OGM &&
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index b1f9f8964c3f..cd12755d21f3 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -214,9 +214,6 @@ struct batadv_wifi_net_device_state {
* struct batadv_hard_iface - network device known to batman-adv
*/
struct batadv_hard_iface {
- /** @list: list node for batadv_hardif_list */
- struct list_head list;
-
/** @if_status: status of the interface for batman-adv */
char if_status;
@@ -1335,9 +1332,9 @@ struct batadv_tp_unacked {
u32 seqno;
/** @len: length of the packet */
- u16 len;
+ u32 len;
- /** @list: list node for &batadv_tp_vars_common.unacked_list */
+ /** @list: list node for &batadv_tp_receiver.unacked_list */
struct list_head list;
};
@@ -1360,15 +1357,6 @@ struct batadv_tp_vars_common {
/** @session: TP session identifier */
u8 session[2];
- /** @unacked_list: list of unacked packets (meta-info only) */
- struct list_head unacked_list;
-
- /** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
- spinlock_t unacked_lock;
-
- /** @unacked_count: number of unacked entries */
- size_t unacked_count;
-
/** @refcount: number of context where the object is used */
struct kref refcount;
@@ -1482,6 +1470,15 @@ struct batadv_tp_receiver {
/** @last_recv_time: time (jiffies) a msg was received */
unsigned long last_recv_time;
+
+ /** @unacked_list: list of unacked packets (meta-info only) */
+ struct list_head unacked_list;
+
+ /** @ack_seqno_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
+ spinlock_t ack_seqno_lock;
+
+ /** @unacked_count: number of unacked entries */
+ size_t unacked_count;
};
/**
@@ -1679,6 +1676,9 @@ struct batadv_priv {
/** @tp_num: number of currently active tp sessions */
atomic_t tp_num;
+ /** @hardif_generation: generation counter added to netlink hardif dumps */
+ unsigned int hardif_generation;
+
/** @orig_work: work queue callback item for orig node purging */
struct delayed_work orig_work;
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
index cf374c208732..22e5e5e1a9c4 100644
--- a/net/core/fib_rules.c
+++ b/net/core/fib_rules.c
@@ -51,7 +51,6 @@ bool fib_rule_matchall(const struct fib_rule *rule)
return false;
return true;
}
-EXPORT_SYMBOL_GPL(fib_rule_matchall);
int fib_default_rule_add(struct fib_rules_ops *ops,
u32 pref, u32 table)
@@ -78,7 +77,6 @@ int fib_default_rule_add(struct fib_rules_ops *ops,
list_add_tail(&r->list, &ops->rules_list);
return 0;
}
-EXPORT_SYMBOL(fib_default_rule_add);
static u32 fib_default_rule_pref(struct fib_rules_ops *ops)
{
@@ -172,6 +170,7 @@ fib_rules_register(const struct fib_rules_ops *tmpl, struct net *net)
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&ops->rules_list);
+ mutex_init(&ops->lock);
ops->fro_net = net;
err = __fib_rules_register(ops);
@@ -182,7 +181,6 @@ fib_rules_register(const struct fib_rules_ops *tmpl, struct net *net)
return ops;
}
-EXPORT_SYMBOL_GPL(fib_rules_register);
static void fib_rules_cleanup_ops(struct fib_rules_ops *ops)
{
@@ -207,7 +205,6 @@ void fib_rules_unregister(struct fib_rules_ops *ops)
fib_rules_cleanup_ops(ops);
kfree_rcu(ops, rcu);
}
-EXPORT_SYMBOL_GPL(fib_rules_unregister);
static int uid_range_set(struct fib_kuid_range *range)
{
@@ -363,7 +360,6 @@ out:
return err;
}
-EXPORT_SYMBOL_GPL(fib_rules_lookup);
static int call_fib_rule_notifier(struct notifier_block *nb,
enum fib_event_type event_type,
@@ -391,7 +387,7 @@ static int call_fib_rule_notifiers(struct net *net,
.rule = rule,
};
- ASSERT_RTNL_NET(net);
+ lockdep_assert_held(&ops->lock);
/* Paired with READ_ONCE() in fib_rules_seq() */
WRITE_ONCE(ops->fib_rules_seq, ops->fib_rules_seq + 1);
@@ -423,7 +419,6 @@ int fib_rules_dump(struct net *net, struct notifier_block *nb, int family,
return err;
}
-EXPORT_SYMBOL_GPL(fib_rules_dump);
unsigned int fib_rules_seq_read(const struct net *net, int family)
{
@@ -439,7 +434,6 @@ unsigned int fib_rules_seq_read(const struct net *net, int family)
return fib_rules_seq;
}
-EXPORT_SYMBOL_GPL(fib_rules_seq_read);
static struct fib_rule *rule_find(struct fib_rules_ops *ops,
struct fib_rule_hdr *frh,
@@ -740,10 +734,10 @@ errout:
return err;
}
-static int fib_nl2rule_rtnl(struct fib_rule *nlrule,
- struct fib_rules_ops *ops,
- struct nlattr *tb[],
- struct netlink_ext_ack *extack)
+static int fib_nl2rule_locked(struct fib_rule *nlrule,
+ struct fib_rules_ops *ops,
+ struct nlattr *tb[],
+ struct netlink_ext_ack *extack)
{
if (!tb[FRA_PRIORITY])
nlrule->pref = fib_default_rule_pref(ops);
@@ -754,12 +748,14 @@ static int fib_nl2rule_rtnl(struct fib_rule *nlrule,
return -EINVAL;
}
+ rcu_read_lock();
+
if (tb[FRA_IIFNAME]) {
struct net_device *dev;
- dev = __dev_get_by_name(nlrule->fr_net, nlrule->iifname);
+ dev = dev_get_by_name_rcu(nlrule->fr_net, nlrule->iifname);
if (dev) {
- nlrule->iifindex = dev->ifindex;
+ nlrule->iifindex = READ_ONCE(dev->ifindex);
nlrule->iif_is_l3_master = netif_is_l3_master(dev);
}
}
@@ -767,13 +763,15 @@ static int fib_nl2rule_rtnl(struct fib_rule *nlrule,
if (tb[FRA_OIFNAME]) {
struct net_device *dev;
- dev = __dev_get_by_name(nlrule->fr_net, nlrule->oifname);
+ dev = dev_get_by_name_rcu(nlrule->fr_net, nlrule->oifname);
if (dev) {
- nlrule->oifindex = dev->ifindex;
+ nlrule->oifindex = READ_ONCE(dev->ifindex);
nlrule->oif_is_l3_master = netif_is_l3_master(dev);
}
}
+ rcu_read_unlock();
+
return 0;
}
@@ -883,6 +881,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr *tb[FRA_MAX + 1];
bool user_priority = false;
struct fib_rule_hdr *frh;
+ bool unlock_rtnl = false;
frh = nlmsg_payload(nlh, sizeof(*frh));
if (!frh) {
@@ -908,10 +907,13 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
if (err)
goto errout;
- if (!rtnl_held)
+ if (!rtnl_held && ops->need_rtnl && ops->need_rtnl(net)) {
+ unlock_rtnl = true;
rtnl_net_lock(net);
+ }
+ mutex_lock(&ops->lock);
- err = fib_nl2rule_rtnl(rule, ops, tb, extack);
+ err = fib_nl2rule_locked(rule, ops, tb, extack);
if (err)
goto errout_free;
@@ -959,7 +961,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
list_for_each_entry(r, &ops->rules_list, list) {
if (r->action == FR_ACT_GOTO &&
r->target == rule->pref &&
- rtnl_dereference(r->ctarget) == NULL) {
+ !rcu_access_pointer(r->ctarget)) {
rcu_assign_pointer(r->ctarget, rule);
if (--ops->unresolved_rules == 0)
break;
@@ -978,7 +980,8 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
fib_rule_get(rule);
- if (!rtnl_held)
+ mutex_unlock(&ops->lock);
+ if (unlock_rtnl)
rtnl_net_unlock(net);
notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).portid);
@@ -988,7 +991,8 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
return 0;
errout_free:
- if (!rtnl_held)
+ mutex_unlock(&ops->lock);
+ if (unlock_rtnl)
rtnl_net_unlock(net);
kfree(rule);
errout:
@@ -1037,10 +1041,9 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
if (err)
goto errout;
- if (!rtnl_held)
- rtnl_net_lock(net);
+ mutex_lock(&ops->lock);
- err = fib_nl2rule_rtnl(nlrule, ops, tb, extack);
+ err = fib_nl2rule_locked(nlrule, ops, tb, extack);
if (err)
goto errout_free;
@@ -1055,11 +1058,8 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
goto errout_free;
}
- if (ops->delete) {
- err = ops->delete(rule);
- if (err)
- goto errout_free;
- }
+ if (ops->delete)
+ ops->delete(rule);
if (rule->tun_id)
ip_tunnel_unneed_metadata();
@@ -1068,7 +1068,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
if (rule->action == FR_ACT_GOTO) {
ops->nr_goto_rules--;
- if (rtnl_dereference(rule->ctarget) == NULL)
+ if (!rcu_access_pointer(rule->ctarget))
ops->unresolved_rules--;
}
@@ -1086,7 +1086,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
if (&n->list == &ops->rules_list || n->pref != rule->pref)
n = NULL;
list_for_each_entry(r, &ops->rules_list, list) {
- if (rtnl_dereference(r->ctarget) != rule)
+ if (rcu_access_pointer(r->ctarget) != rule)
continue;
rcu_assign_pointer(r->ctarget, n);
if (!n)
@@ -1096,8 +1096,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL, rule, ops, NULL);
- if (!rtnl_held)
- rtnl_net_unlock(net);
+ mutex_unlock(&ops->lock);
notify_rule_change(RTM_DELRULE, rule, ops, nlh, NETLINK_CB(skb).portid);
fib_rule_put(rule);
@@ -1107,8 +1106,7 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
return 0;
errout_free:
- if (!rtnl_held)
- rtnl_net_unlock(net);
+ mutex_unlock(&ops->lock);
kfree(nlrule);
errout:
rules_ops_put(ops);
@@ -1402,24 +1400,30 @@ static int fib_rules_event(struct notifier_block *this, unsigned long event,
struct net *net = dev_net(dev);
struct fib_rules_ops *ops;
- ASSERT_RTNL();
-
switch (event) {
case NETDEV_REGISTER:
- list_for_each_entry(ops, &net->rules_ops, list)
+ list_for_each_entry(ops, &net->rules_ops, list) {
+ mutex_lock(&ops->lock);
attach_rules(&ops->rules_list, dev);
+ mutex_unlock(&ops->lock);
+ }
break;
case NETDEV_CHANGENAME:
list_for_each_entry(ops, &net->rules_ops, list) {
+ mutex_lock(&ops->lock);
detach_rules(&ops->rules_list, dev);
attach_rules(&ops->rules_list, dev);
+ mutex_unlock(&ops->lock);
}
break;
case NETDEV_UNREGISTER:
- list_for_each_entry(ops, &net->rules_ops, list)
+ list_for_each_entry(ops, &net->rules_ops, list) {
+ mutex_lock(&ops->lock);
detach_rules(&ops->rules_list, dev);
+ mutex_unlock(&ops->lock);
+ }
break;
}
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 96d5945e6a30..aed415d3cd74 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -221,7 +221,7 @@ static void refill_skbs(struct netpoll *np)
skb_pool = &np->skb_pool;
while (READ_ONCE(skb_pool->qlen) < MAX_SKBS) {
- skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC);
+ skb = alloc_skb(MAX_SKB_SIZE, GFP_ATOMIC | __GFP_NOWARN);
if (!skb)
break;
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 42212970d735..a5e739d32d59 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -76,7 +76,7 @@ fail:
struct fib_table *fib_new_table(struct net *net, u32 id)
{
- struct fib_table *tb, *alias = NULL;
+ struct fib_table *tb, *new_tb, *alias = NULL;
unsigned int h;
if (id == 0)
@@ -85,14 +85,27 @@ struct fib_table *fib_new_table(struct net *net, u32 id)
if (tb)
return tb;
+ if (!check_net(net))
+ return NULL;
+
if (id == RT_TABLE_LOCAL && !net->ipv4.fib_has_custom_rules)
alias = fib_new_table(net, RT_TABLE_MAIN);
- if (check_net(net))
- tb = fib_trie_table(id, alias);
- if (!tb)
+ new_tb = fib_trie_table(id, alias);
+ if (!new_tb)
return NULL;
+ spin_lock(&net->ipv4.fib_table_hash_lock);
+
+ tb = fib_get_table(net, id);
+ if (tb) {
+ spin_unlock(&net->ipv4.fib_table_hash_lock);
+ fib_free_table(new_tb);
+ return tb;
+ }
+
+ tb = new_tb;
+
switch (id) {
case RT_TABLE_MAIN:
rcu_assign_pointer(net->ipv4.fib_main, tb);
@@ -106,28 +119,35 @@ struct fib_table *fib_new_table(struct net *net, u32 id)
h = id & (FIB_TABLE_HASHSZ - 1);
hlist_add_head_rcu(&tb->tb_hlist, &net->ipv4.fib_table_hash[h]);
+
+ spin_unlock(&net->ipv4.fib_table_hash_lock);
+
return tb;
}
EXPORT_SYMBOL_GPL(fib_new_table);
-/* caller must hold either rtnl or rcu read lock */
struct fib_table *fib_get_table(struct net *net, u32 id)
{
- struct fib_table *tb;
+ struct fib_table *tb = NULL;
struct hlist_head *head;
unsigned int h;
if (id == 0)
id = RT_TABLE_MAIN;
h = id & (FIB_TABLE_HASHSZ - 1);
-
head = &net->ipv4.fib_table_hash[h];
- hlist_for_each_entry_rcu(tb, head, tb_hlist,
- lockdep_rtnl_is_held()) {
+
+ /* fib_table is not destroyed until ip_fib_net_exit()
+ * except for the merged main/local table.
+ * fib_unmerge() is called under RTNL, so other readers
+ * under RTNL (e.g. fib_flush(), fib_info_notify_update())
+ * can safely traverse the list with rcu_dereference_raw().
+ */
+ hlist_for_each_entry_rcu(tb, head, tb_hlist, true)
if (tb->tb_id == id)
- return tb;
- }
- return NULL;
+ break;
+
+ return tb;
}
#endif /* CONFIG_IP_MULTIPLE_TABLES */
@@ -190,10 +210,9 @@ void fib_flush(struct net *net)
for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
struct hlist_head *head = &net->ipv4.fib_table_hash[h];
- struct hlist_node *tmp;
struct fib_table *tb;
- hlist_for_each_entry_safe(tb, tmp, head, tb_hlist)
+ hlist_for_each_entry_rcu(tb, head, tb_hlist, true)
flushed += fib_table_flush(net, tb, false);
}
@@ -1019,10 +1038,11 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
.dump_routes = true,
.dump_exceptions = true,
};
- unsigned int e = 0, s_e, h, s_h;
struct hlist_head *head;
int dumped = 0, err = 0;
struct fib_table *tb;
+ unsigned int h, s_h;
+ u32 s_id;
rcu_read_lock();
if (cb->strict_check) {
@@ -1054,29 +1074,28 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
}
s_h = cb->args[0];
- s_e = cb->args[1];
+ s_id = cb->args[1];
err = 0;
- for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) {
- e = 0;
+ for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_id = 0) {
head = &net->ipv4.fib_table_hash[h];
hlist_for_each_entry_rcu(tb, head, tb_hlist) {
- if (e < s_e)
- goto next;
+ if (s_id && tb->tb_id != s_id)
+ continue;
+
+ s_id = 0;
if (dumped)
memset(&cb->args[2], 0, sizeof(cb->args) -
2 * sizeof(cb->args[0]));
+ cb->args[1] = tb->tb_id;
err = fib_table_dump(tb, skb, cb, &filter);
if (err < 0)
goto out;
dumped = 1;
-next:
- e++;
}
}
out:
- cb->args[1] = e;
cb->args[0] = h;
unlock:
@@ -1565,6 +1584,7 @@ static int __net_init ip_fib_net_init(struct net *net)
net->ipv4.sysctl_fib_multipath_hash_fields =
FIB_MULTIPATH_HASH_FIELD_DEFAULT_MASK;
#endif
+ spin_lock_init(&net->ipv4.fib_table_hash_lock);
/* Avoid false sharing : Use at least a full cache line */
size = max_t(size_t, size, L1_CACHE_BYTES);
diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c
index e068a5bace73..4edb0dca7be8 100644
--- a/net/ipv4/fib_rules.c
+++ b/net/ipv4/fib_rules.c
@@ -301,10 +301,12 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
fib4_nl2rule_dscp_mask(tb[FRA_DSCP_MASK], rule4, extack) < 0)
goto errout;
- /* split local/main if they are not already split */
- err = fib_unmerge(net);
- if (err)
- goto errout;
+ if (!net->ipv4.fib_has_custom_rules) {
+ /* split local/main if they are not already split */
+ err = fib_unmerge(net);
+ if (err)
+ goto errout;
+ }
if (rule->table == RT_TABLE_UNSPEC && !rule->l3mdev) {
if (rule->action == FR_ACT_TO_TBL) {
@@ -349,7 +351,7 @@ errout:
return err;
}
-static int fib4_rule_delete(struct fib_rule *rule)
+static void fib4_rule_delete(struct fib_rule *rule)
{
struct net *net = rule->fr_net;
@@ -361,8 +363,6 @@ static int fib4_rule_delete(struct fib_rule *rule)
if (net->ipv4.fib_rules_require_fldissect &&
fib_rule_requires_fldissect(rule))
net->ipv4.fib_rules_require_fldissect--;
-
- return 0;
}
static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
@@ -460,6 +460,11 @@ static void fib4_rule_flush_cache(struct fib_rules_ops *ops)
rt_cache_flush(ops->fro_net);
}
+static bool fib4_rule_need_rtnl(struct net *net)
+{
+ return !net->ipv4.fib_has_custom_rules;
+}
+
static const struct fib_rules_ops __net_initconst fib4_rules_ops_template = {
.family = AF_INET,
.rule_size = sizeof(struct fib4_rule),
@@ -473,6 +478,7 @@ static const struct fib_rules_ops __net_initconst fib4_rules_ops_template = {
.fill = fib4_rule_fill,
.nlmsg_payload = fib4_rule_nlmsg_payload,
.flush_cache = fib4_rule_flush_cache,
+ .need_rtnl = fib4_rule_need_rtnl,
.nlgroup = RTNLGRP_IPV4_RULE,
.owner = THIS_MODULE,
};
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index e11dc86ceda0..d1d342d7148e 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -2137,8 +2137,7 @@ void fib_info_notify_update(struct net *net, struct nl_info *info)
struct hlist_head *head = &net->ipv4.fib_table_hash[h];
struct fib_table *tb;
- hlist_for_each_entry_rcu(tb, head, tb_hlist,
- lockdep_rtnl_is_held())
+ hlist_for_each_entry_rcu(tb, head, tb_hlist, true)
__fib_info_notify_update(net, tb, info);
}
}
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index b6337a47c141..116ce7cec80e 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -1473,6 +1473,7 @@ int inet_fill_ifmcaddr(struct sk_buff *skb, struct net_device *dev,
ci.ifa_valid = INFINITY_LIFE_TIME;
if (nla_put_in_addr(skb, IFA_MULTICAST, im->multiaddr) < 0 ||
+ nla_put_u32(skb, IFA_MC_USERS, READ_ONCE(im->users)) < 0 ||
nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci) < 0) {
nlmsg_cancel(skb, nlh);
return -EMSGSIZE;
@@ -1494,6 +1495,7 @@ static void inet_ifmcaddr_notify(struct net_device *dev,
skb = nlmsg_new(NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
nla_total_size(sizeof(__be32)) +
+ nla_total_size(sizeof(u32)) +
nla_total_size(sizeof(struct ifa_cacheinfo)),
GFP_KERNEL);
if (!skb)
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index e9fbab6ad914..2aebaf8297e0 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -809,23 +809,18 @@ static int raw_seticmpfilter(struct sock *sk, sockptr_t optval, int optlen)
return 0;
}
-static int raw_geticmpfilter(struct sock *sk, char __user *optval, int __user *optlen)
+static int raw_geticmpfilter(struct sock *sk, sockopt_t *opt)
{
- int len, ret = -EFAULT;
+ int len = opt->optlen;
- if (get_user(len, optlen))
- goto out;
- ret = -EINVAL;
if (len < 0)
- goto out;
+ return -EINVAL;
if (len > sizeof(struct icmp_filter))
len = sizeof(struct icmp_filter);
- ret = -EFAULT;
- if (put_user(len, optlen) ||
- copy_to_user(optval, &raw_sk(sk)->filter, len))
- goto out;
- ret = 0;
-out: return ret;
+ opt->optlen = len;
+ if (copy_to_iter(&raw_sk(sk)->filter, len, &opt->iter_out) != len)
+ return -EFAULT;
+ return 0;
}
static int do_raw_setsockopt(struct sock *sk, int optname,
@@ -848,14 +843,13 @@ static int raw_setsockopt(struct sock *sk, int level, int optname,
return do_raw_setsockopt(sk, optname, optval, optlen);
}
-static int do_raw_getsockopt(struct sock *sk, int optname,
- char __user *optval, int __user *optlen)
+static int do_raw_getsockopt(struct sock *sk, int optname, sockopt_t *opt)
{
if (optname == ICMP_FILTER) {
if (inet_sk(sk)->inet_num != IPPROTO_ICMP)
return -EOPNOTSUPP;
else
- return raw_geticmpfilter(sk, optval, optlen);
+ return raw_geticmpfilter(sk, opt);
}
return -ENOPROTOOPT;
}
@@ -863,9 +857,24 @@ static int do_raw_getsockopt(struct sock *sk, int optname,
static int raw_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
+ sockopt_t opt;
+ int err;
+
if (level != SOL_RAW)
return ip_getsockopt(sk, level, optname, optval, optlen);
- return do_raw_getsockopt(sk, optname, optval, optlen);
+
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = do_raw_getsockopt(sk, optname, &opt);
+ if (err)
+ return err;
+
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
+
+ return 0;
}
static int raw_ioctl(struct sock *sk, int cmd, int *karg)
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 70f6cbd4ef73..59248a59358c 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -76,6 +76,7 @@
#include <linux/bpf-cgroup.h>
#include <linux/uaccess.h>
+#include <linux/uio.h>
#include <asm/ioctls.h>
#include <linux/memblock.h>
#include <linux/highmem.h>
@@ -2995,14 +2996,13 @@ static int udp_setsockopt(struct sock *sk, int level, int optname, sockptr_t opt
}
int udp_lib_getsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int __user *optlen)
+ sockopt_t *opt)
{
struct udp_sock *up = udp_sk(sk);
int val, len;
- if (get_user(len, optlen))
- return -EFAULT;
-
+ len = opt->optlen;
+ /* keep the check so direct sockopt_t callers stay covered. */
if (len < 0)
return -EINVAL;
@@ -3037,9 +3037,8 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
return -ENOPROTOOPT;
}
- if (put_user(len, optlen))
- return -EFAULT;
- if (copy_to_user(optval, &val, len))
+ opt->optlen = len;
+ if (copy_to_iter(&val, len, &opt->iter_out) != len)
return -EFAULT;
return 0;
}
@@ -3047,9 +3046,29 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
static int udp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
- if (level == SOL_UDP)
- return udp_lib_getsockopt(sk, level, optname, optval, optlen);
- return ip_getsockopt(sk, level, optname, optval, optlen);
+ sockopt_t opt;
+ int err;
+
+ /*
+ * keep the old __user pointers, until ip_getsockopt() moves
+ * to sockopt_t
+ */
+ if (level != SOL_UDP)
+ return ip_getsockopt(sk, level, optname, optval, optlen);
+
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = udp_lib_getsockopt(sk, level, optname, &opt);
+ if (err)
+ return err;
+
+ /* optval was written by copy_to_iter() in udp_lib_getsockopt() */
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
+
+ return 0;
}
/**
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index cbe681de3818..f1fe9ede1edb 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -5264,6 +5264,7 @@ int inet6_fill_ifmcaddr(struct sk_buff *skb,
put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex);
if (nla_put_in6_addr(skb, IFA_MULTICAST, &ifmca->mca_addr) < 0 ||
+ nla_put_u32(skb, IFA_MC_USERS, READ_ONCE(ifmca->mca_users)) < 0 ||
put_cacheinfo(skb, ifmca->mca_cstamp, READ_ONCE(ifmca->mca_tstamp),
INFINITY_LIFE_TIME, INFINITY_LIFE_TIME) < 0) {
nlmsg_cancel(skb, nlh);
diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c
index e1b2b4fa6e18..04dab9329d0c 100644
--- a/net/ipv6/fib6_rules.c
+++ b/net/ipv6/fib6_rules.c
@@ -480,15 +480,13 @@ errout:
return err;
}
-static int fib6_rule_delete(struct fib_rule *rule)
+static void fib6_rule_delete(struct fib_rule *rule)
{
struct net *net = rule->fr_net;
if (net->ipv6.fib6_rules_require_fldissect &&
fib_rule_requires_fldissect(rule))
net->ipv6.fib6_rules_require_fldissect--;
-
- return 0;
}
static int fib6_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
@@ -637,21 +635,14 @@ out_fib6_rules_ops:
goto out;
}
-static void __net_exit fib6_rules_net_exit_batch(struct list_head *net_list)
+static void __net_exit fib6_rules_net_exit(struct net *net)
{
- struct net *net;
-
- rtnl_lock();
- list_for_each_entry(net, net_list, exit_list) {
- fib_rules_unregister(net->ipv6.fib6_rules_ops);
- cond_resched();
- }
- rtnl_unlock();
+ fib_rules_unregister(net->ipv6.fib6_rules_ops);
}
static struct pernet_operations fib6_rules_net_ops = {
.init = fib6_rules_net_init,
- .exit_batch = fib6_rules_net_exit_batch,
+ .exit = fib6_rules_net_exit,
};
int __init fib6_rules_init(void)
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 04b811b3be97..774f4c72a6fa 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -908,6 +908,7 @@ static void inet6_ifmcaddr_notify(struct net_device *dev,
skb = nlmsg_new(NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
nla_total_size(sizeof(struct in6_addr)) +
+ nla_total_size(sizeof(u32)) +
nla_total_size(sizeof(struct ifa_cacheinfo)),
GFP_KERNEL);
if (!skb)
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 15e032194ecc..392e18b97045 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1826,9 +1826,22 @@ static int udpv6_setsockopt(struct sock *sk, int level, int optname,
static int udpv6_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
- if (level == SOL_UDP)
- return udp_lib_getsockopt(sk, level, optname, optval, optlen);
- return ipv6_getsockopt(sk, level, optname, optval, optlen);
+ sockopt_t opt;
+ int err;
+
+ if (level != SOL_UDP)
+ return ipv6_getsockopt(sk, level, optname, optval, optlen);
+
+ err = sockopt_init_user(&opt, optval, optlen);
+ if (err)
+ return err;
+
+ err = udp_lib_getsockopt(sk, level, optname, &opt);
+ if (err)
+ return err;
+ if (put_user(opt.optlen, optlen))
+ return -EFAULT;
+ return 0;
}
diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
index 09475007165b..41c2a0b82a8e 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -328,38 +328,35 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk,
if (pkt_len == 0 && info->op == VIRTIO_VSOCK_OP_RW)
return pkt_len;
- if (info->msg) {
- /* If zerocopy is not enabled by 'setsockopt()', we behave as
- * there is no MSG_ZEROCOPY flag set.
+ if (info->msg && (info->msg->msg_flags & MSG_ZEROCOPY)) {
+ /* If 'info->msg' is not NULL, this is only VIRTIO_VSOCK_OP_RW.
+ * 'MSG_ZEROCOPY' flag handling here is based on the same flag
+ * handling from 'tcp_sendmsg_locked()'.
*/
- if (!sock_flag(sk_vsock(vsk), SOCK_ZEROCOPY))
- info->msg->msg_flags &= ~MSG_ZEROCOPY;
+ if (info->msg->msg_ubuf) {
+ uarg = info->msg->msg_ubuf;
+ can_zcopy = virtio_transport_can_zcopy(t_ops, info, pkt_len);
+ } else if (sock_flag(sk_vsock(vsk), SOCK_ZEROCOPY)) {
+ uarg = msg_zerocopy_realloc(sk_vsock(vsk), pkt_len,
+ NULL, false);
+ if (!uarg) {
+ virtio_transport_put_credit(vvs, pkt_len);
+ return -ENOMEM;
+ }
- if (info->msg->msg_flags & MSG_ZEROCOPY)
can_zcopy = virtio_transport_can_zcopy(t_ops, info, pkt_len);
+ if (!can_zcopy)
+ uarg_to_msgzc(uarg)->zerocopy = 0;
+ have_uref = true;
+ }
+
+ /* 'can_zcopy' means that this transmission will be
+ * in zerocopy way (e.g. using 'frags' array).
+ */
if (can_zcopy)
max_skb_len = min_t(u32, VIRTIO_VSOCK_MAX_PKT_BUF_SIZE,
(MAX_SKB_FRAGS * PAGE_SIZE));
-
- if (info->msg->msg_flags & MSG_ZEROCOPY &&
- info->op == VIRTIO_VSOCK_OP_RW) {
- uarg = info->msg->msg_ubuf;
-
- if (!uarg) {
- uarg = msg_zerocopy_realloc(sk_vsock(vsk),
- pkt_len, NULL, false);
- if (!uarg) {
- virtio_transport_put_credit(vvs, pkt_len);
- return -ENOMEM;
- }
-
- if (!can_zcopy)
- uarg_to_msgzc(uarg)->zerocopy = 0;
-
- have_uref = true;
- }
- }
}
rest_len = pkt_len;
diff --git a/tools/net/ynl/pyynl/__init__.py b/tools/net/ynl/pyynl/__init__.py
index e69de29bb2d1..d8f59c132ab7 100644
--- a/tools/net/ynl/pyynl/__init__.py
+++ b/tools/net/ynl/pyynl/__init__.py
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+""" Python YNL (YAML Netlink) library. """
+
+# Re-export the public library API so it can be imported straight from the
+# package, e.g. `from pyynl import YnlFamily`.
+# pylint: disable=wildcard-import,unused-wildcard-import
+from .lib import *
+from .lib import __all__
diff --git a/tools/net/ynl/pyynl/cli.py b/tools/net/ynl/pyynl/cli.py
index 8275a806cf73..b6a6ce12b4a7 100755
--- a/tools/net/ynl/pyynl/cli.py
+++ b/tools/net/ynl/pyynl/cli.py
@@ -17,9 +17,7 @@ import textwrap
# pylint: disable=no-name-in-module,wrong-import-position
sys.path.append(pathlib.Path(__file__).resolve().parent.as_posix())
from lib import YnlFamily, Netlink, NlError, SpecFamily, SpecException, YnlException
-
-SYS_SCHEMA_DIR='/usr/share/ynl'
-RELATIVE_SCHEMA_DIR='../../../../Documentation/netlink'
+from lib import list_families
# pylint: disable=too-few-public-methods,too-many-locals
class Colors:
@@ -48,30 +46,6 @@ def term_width():
""" Get terminal width in columns (80 if stdout is not a terminal) """
return shutil.get_terminal_size().columns
-def schema_dir():
- """
- Return the effective schema directory, preferring in-tree before
- system schema directory.
- """
- script_dir = os.path.dirname(os.path.abspath(__file__))
- schema_dir_ = os.path.abspath(f"{script_dir}/{RELATIVE_SCHEMA_DIR}")
- if not os.path.isdir(schema_dir_):
- schema_dir_ = SYS_SCHEMA_DIR
- if not os.path.isdir(schema_dir_):
- raise YnlException(f"Schema directory {schema_dir_} does not exist")
- return schema_dir_
-
-def spec_dir():
- """
- Return the effective spec directory, relative to the effective
- schema directory.
- """
- spec_dir_ = schema_dir() + '/specs'
- if not os.path.isdir(spec_dir_):
- raise YnlException(f"Spec directory {spec_dir_} does not exist")
- return spec_dir_
-
-
class YnlEncoder(json.JSONEncoder):
"""A custom encoder for emitting JSON with ynl-specific instance types"""
def default(self, o):
@@ -272,9 +246,8 @@ def main():
pprint.pprint(msg, width=term_width(), compact=True)
if args.list_families:
- for filename in sorted(os.listdir(spec_dir())):
- if filename.endswith('.yaml'):
- print(filename.removesuffix('.yaml'))
+ for family in list_families():
+ print(family)
return
if args.no_schema:
@@ -284,28 +257,23 @@ def main():
if args.json_text:
attrs = json.loads(args.json_text)
- if args.family:
- spec = f"{spec_dir()}/{args.family}.yaml"
- else:
- spec = args.spec
- if not os.path.isfile(spec):
- raise YnlException(f"Spec file {spec} does not exist")
+ if args.spec and not os.path.isfile(args.spec):
+ raise YnlException(f"Spec file {args.spec} does not exist")
+ # Spec/YnlFamily will raise if both or neither spec and family are given
if args.validate:
+ # Force validation even for installed specs (schema=True), unless the
+ # user explicitly picked a schema or opted out with --no-schema.
+ schema = True if args.schema is None else args.schema
try:
- SpecFamily(spec, args.schema)
+ SpecFamily(args.spec, schema_path=schema, family=args.family)
except SpecException as error:
print(error)
sys.exit(1)
return
- if args.family: # set behaviour when using installed specs
- if args.schema is None and spec.startswith(SYS_SCHEMA_DIR):
- args.schema = '' # disable schema validation when installed
- if args.process_unknown is None:
- args.process_unknown = True
-
- ynl = YnlFamily(spec, args.schema, args.process_unknown,
+ ynl = YnlFamily(args.spec, schema=args.schema, family=args.family,
+ process_unknown=args.process_unknown,
recv_size=args.dbg_small_recv)
if args.dbg_small_recv:
ynl.set_recv_dbg(True)
diff --git a/tools/net/ynl/pyynl/lib/__init__.py b/tools/net/ynl/pyynl/lib/__init__.py
index be741985ae4e..aa4263c8cba9 100644
--- a/tools/net/ynl/pyynl/lib/__init__.py
+++ b/tools/net/ynl/pyynl/lib/__init__.py
@@ -5,12 +5,13 @@
from .nlspec import SpecAttr, SpecAttrSet, SpecEnumEntry, SpecEnumSet, \
SpecFamily, SpecOperation, SpecSubMessage, SpecSubMessageFormat, \
SpecException
+from .specdir import list_families
from .ynl import YnlFamily, Netlink, NlError, NlPolicy, YnlException
from .doc_generator import YnlDocGenerator
__all__ = ["SpecAttr", "SpecAttrSet", "SpecEnumEntry", "SpecEnumSet",
"SpecFamily", "SpecOperation", "SpecSubMessage", "SpecSubMessageFormat",
- "SpecException",
+ "SpecException", "list_families",
"YnlFamily", "Netlink", "NlError", "NlPolicy", "YnlException",
"YnlDocGenerator"]
diff --git a/tools/net/ynl/pyynl/lib/nlspec.py b/tools/net/ynl/pyynl/lib/nlspec.py
index 0469a0e270d0..b4ec59814ab1 100644
--- a/tools/net/ynl/pyynl/lib/nlspec.py
+++ b/tools/net/ynl/pyynl/lib/nlspec.py
@@ -12,6 +12,8 @@ import importlib
import os
import yaml as pyyaml
+from .specdir import find_spec, SYS_SCHEMA_DIR
+
class SpecException(Exception):
"""Netlink spec exception.
@@ -444,7 +446,23 @@ class SpecFamily(SpecElement):
except AttributeError:
_yaml_loader = pyyaml.SafeLoader
- def __init__(self, spec_path, schema_path=None, exclude_ops=None):
+ def __init__(self, spec_path=None, schema_path=None, exclude_ops=None,
+ family=None):
+ # schema_path selects how the spec is validated:
+ # None -- no preference: validate against the default schema,
+ # but trust (skip) installed specs selected by family=
+ # True -- always validate against the default schema
+ # path -- validate against this schema
+ # '' -- do not validate
+ if (spec_path is None) == (family is None):
+ raise ValueError("Specify exactly one of spec path or family name")
+ if family is not None:
+ spec_path = find_spec(family)
+ # Installed specs are assumed correct, so skip schema validation
+ # to save cycles unless the caller asked to validate.
+ if schema_path is None and spec_path.startswith(SYS_SCHEMA_DIR):
+ schema_path = ''
+
with open(spec_path, "r", encoding='utf-8') as stream:
prefix = '# SPDX-License-Identifier: '
first = stream.readline().strip()
@@ -465,7 +483,7 @@ class SpecFamily(SpecElement):
self.proto = self.yaml.get('protocol', 'genetlink')
self.msg_id_model = self.yaml['operations'].get('enum-model', 'unified')
- if schema_path is None:
+ if schema_path is None or schema_path is True:
schema_path = os.path.dirname(os.path.dirname(spec_path)) + f'/{self.proto}.yaml'
if schema_path:
with open(schema_path, "r", encoding='utf-8') as stream:
diff --git a/tools/net/ynl/pyynl/lib/specdir.py b/tools/net/ynl/pyynl/lib/specdir.py
new file mode 100644
index 000000000000..fcea9b9fb7b0
--- /dev/null
+++ b/tools/net/ynl/pyynl/lib/specdir.py
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+"""
+Locating YNL spec and schema files on disk.
+
+Resolves the directory holding the YAML specs (preferring an in-tree copy
+over the installed system path) and maps family names to spec files.
+"""
+
+import os
+
+SYS_SCHEMA_DIR='/usr/share/ynl'
+RELATIVE_SCHEMA_DIR='../../../../../Documentation/netlink'
+
+
+def schema_dir():
+ """
+ Return the effective schema directory, preferring in-tree before
+ system schema directory.
+ """
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ schema_dir_ = os.path.abspath(f"{script_dir}/{RELATIVE_SCHEMA_DIR}")
+ if not os.path.isdir(schema_dir_):
+ schema_dir_ = SYS_SCHEMA_DIR
+ if not os.path.isdir(schema_dir_):
+ raise FileNotFoundError(f"Schema directory {schema_dir_} does not exist")
+ return schema_dir_
+
+def spec_dir():
+ """
+ Return the effective spec directory, relative to the effective
+ schema directory.
+ """
+ spec_dir_ = schema_dir() + '/specs'
+ if not os.path.isdir(spec_dir_):
+ raise FileNotFoundError(f"Spec directory {spec_dir_} does not exist")
+ return spec_dir_
+
+
+def find_spec(family):
+ """ Return the path to the YAML spec file for a family by name. """
+ spec = f"{spec_dir()}/{family}.yaml"
+ if not os.path.isfile(spec):
+ raise FileNotFoundError(f"Spec for family '{family}' not found at {spec}")
+ return spec
+
+
+def list_families():
+ """ Return the sorted names of all families with an installed spec. """
+ return sorted(f.removesuffix('.yaml')
+ for f in os.listdir(spec_dir()) if f.endswith('.yaml'))
diff --git a/tools/net/ynl/pyynl/lib/ynl.py b/tools/net/ynl/pyynl/lib/ynl.py
index 092d132edec1..8682bf588e1f 100644
--- a/tools/net/ynl/pyynl/lib/ynl.py
+++ b/tools/net/ynl/pyynl/lib/ynl.py
@@ -661,6 +661,14 @@ class YnlFamily(SpecFamily):
"""
YNL family -- a Netlink interface built from a YAML spec.
+ The spec can be selected either by file path (def_path=) or, when it
+ ships in a well-known location, by family name (family="xyz"); exactly
+ one of the two must be given. For example:
+
+ from pyynl import YnlFamily
+
+ ynl = YnlFamily(family="netdev")
+
Primary use of the class is to execute Netlink commands:
ynl.<op_name>(attrs, ...)
@@ -691,11 +699,16 @@ class YnlFamily(SpecFamily):
ynl.get_policy(op_name, mode) -- query kernel policy for an op
"""
- def __init__(self, def_path, schema=None, process_unknown=False,
- recv_size=0):
- super().__init__(def_path, schema)
+ def __init__(self, def_path=None, schema=None, process_unknown=None,
+ recv_size=0, family=None):
+ super().__init__(def_path, schema, family=family)
self.include_raw = False
+ # Specs from /usr (selected by family=) have a higher chance of being
+ # stale, default to ignoring unknown attrs. In-tree users, and users
+ # who bundle the spec need to make a conscious decision.
+ if process_unknown is None:
+ process_unknown = family is not None
self.process_unknown = process_unknown
try:
diff --git a/tools/net/ynl/tests/ethtool.py b/tools/net/ynl/tests/ethtool.py
index db3b62c652e7..0ee0c8e87686 100755
--- a/tools/net/ynl/tests/ethtool.py
+++ b/tools/net/ynl/tests/ethtool.py
@@ -11,12 +11,10 @@ import pathlib
import pprint
import sys
import re
-import os
# pylint: disable=no-name-in-module,wrong-import-position
sys.path.append(pathlib.Path(__file__).resolve().parent.parent.joinpath('pyynl').as_posix())
# pylint: disable=import-error
-from cli import schema_dir, spec_dir
from lib import YnlFamily
@@ -173,10 +171,7 @@ def main():
args = parser.parse_args()
- spec = os.path.join(spec_dir(), 'ethtool.yaml')
- schema = os.path.join(schema_dir(), 'genetlink-legacy.yaml')
-
- ynl = YnlFamily(spec, schema, recv_size=args.dbg_small_recv)
+ ynl = YnlFamily(family='ethtool', recv_size=args.dbg_small_recv)
if args.dbg_small_recv:
ynl.set_recv_dbg(True)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_xsk.c b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
index 6eb9096d084c..477aedbb01ba 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_xsk.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_xsk.c
@@ -1164,8 +1164,8 @@ static int __send_pkts(struct ifobject *ifobject, struct xsk_socket_info *xsk,
bool test_timeout)
{
u32 i, idx = 0, valid_pkts = 0, valid_frags = 0, buffer_len;
+ struct xsk_umem_info *umem = ifobject->xsk_arr[0].umem_real;
struct pkt_stream *pkt_stream = xsk->pkt_stream;
- struct xsk_umem_info *umem = xsk->umem;
bool use_poll = ifobject->use_poll;
struct pollfd fds = { };
int ret;
@@ -1514,7 +1514,7 @@ static int thread_common_ops_tx(struct test_spec *test, struct ifobject *ifobjec
umem_tx->base_addr = 0;
umem_tx->next_buffer = 0;
- ret = xsk_configure(test, ifobject, umem_tx, true);
+ ret = xsk_configure(test, ifobject, umem_rx, true);
if (ret)
return ret;
ifobject->xsk = &ifobject->xsk_arr[0];
diff --git a/tools/testing/selftests/drivers/net/hw/toeplitz.py b/tools/testing/selftests/drivers/net/hw/toeplitz.py
index cd7e080e6f84..571732198b93 100755
--- a/tools/testing/selftests/drivers/net/hw/toeplitz.py
+++ b/tools/testing/selftests/drivers/net/hw/toeplitz.py
@@ -21,6 +21,8 @@ from lib.py import ksft_variants, KsftNamedVariant, KsftSkipEx, KsftFailEx
ETH_RSS_HASH_TOP = 1
# Must match RPS_MAX_CPUS in toeplitz.c
RPS_MAX_CPUS = 16
+# Cap Rx queues so IRQ pinning leaves free CPUs in the RPS_MAX_CPUS range
+QUEUE_CAP = 8
def _check_rps_and_rfs_not_configured(cfg):
@@ -48,6 +50,25 @@ def _get_cpu_for_irq(irq):
return int(data)
+def _cap_queue_count(cfg):
+ ehdr = {"header": {"dev-index": cfg.ifindex}}
+ chans = cfg.ethnl.channels_get(ehdr)
+
+ config = {}
+ restore = {}
+ for key in ("combined-count", "rx-count"):
+ cur = chans.get(key, 0)
+ if cur > QUEUE_CAP:
+ config[key] = QUEUE_CAP
+ restore[key] = cur
+
+ if not config:
+ return
+
+ cfg.ethnl.channels_set(ehdr | config)
+ defer(cfg.ethnl.channels_set, ehdr | restore)
+
+
def _get_irq_cpus(cfg):
"""
Read the list of IRQs for the device Rx queues.
@@ -177,6 +198,7 @@ def test(cfg, proto_flag, ipver, grp):
]
if grp:
+ _cap_queue_count(cfg)
_check_rps_and_rfs_not_configured(cfg)
if grp == "rss":
irq_cpus = ",".join([str(x) for x in _get_irq_cpus(cfg)])
diff --git a/tools/testing/selftests/net/getsockopt_iter.c b/tools/testing/selftests/net/getsockopt_iter.c
index 209569354d0e..fe5a5268bc34 100644
--- a/tools/testing/selftests/net/getsockopt_iter.c
+++ b/tools/testing/selftests/net/getsockopt_iter.c
@@ -11,6 +11,8 @@
* that always reports the required buffer length back via optlen,
* even when the user buffer is too small to receive any group bits.
* - vsock: SO_VM_SOCKETS_BUFFER_SIZE covers the u64 path.
+ * - raw: ICMP_FILTER covers a fixed-size struct payload that clamps
+ * the length down on a short buffer instead of failing.
*
* Author: Breno Leitao <leitao@debian.org>
*/
@@ -24,12 +26,20 @@
#include <linux/rtnetlink.h>
#include <linux/time_types.h>
#include <linux/vm_sockets.h>
+#include <linux/icmp.h>
+#include <netinet/in.h>
#include <sys/socket.h>
#include "kselftest_harness.h"
#ifndef AF_VSOCK
#define AF_VSOCK 40
#endif
+#ifndef SOL_RAW
+#define SOL_RAW 255
+#endif
+#ifndef ICMP_FILTER
+#define ICMP_FILTER 1
+#endif
/* ---------- netlink ---------- */
@@ -297,4 +307,91 @@ TEST_F(vsock, connect_timeout_old_exact)
ASSERT_EQ(sizeof(tv), optlen);
}
+/* ---------- raw (ipv4) ---------- */
+
+FIXTURE(raw)
+{
+ int fd;
+};
+
+FIXTURE_SETUP(raw)
+{
+ struct icmp_filter filt = { .data = 0xdeadbeef };
+
+ self->fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (self->fd < 0)
+ SKIP(return, "SOCK_RAW/ICMP socket: %s", strerror(errno));
+
+ if (setsockopt(self->fd, SOL_RAW, ICMP_FILTER, &filt, sizeof(filt)) < 0)
+ SKIP(return, "set ICMP_FILTER: %s", strerror(errno));
+}
+
+FIXTURE_TEARDOWN(raw)
+{
+ if (self->fd >= 0)
+ close(self->fd);
+}
+
+TEST_F(raw, icmpfilter_exact)
+{
+ struct icmp_filter filt = {};
+ socklen_t optlen = sizeof(filt);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_RAW, ICMP_FILTER,
+ &filt, &optlen));
+ ASSERT_EQ(sizeof(filt), optlen);
+ ASSERT_EQ(0xdeadbeef, filt.data);
+}
+
+TEST_F(raw, icmpfilter_oversize_clamped)
+{
+ char buf[16] = {};
+ socklen_t optlen = sizeof(buf);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_RAW, ICMP_FILTER,
+ buf, &optlen));
+ ASSERT_EQ(sizeof(struct icmp_filter), optlen);
+}
+
+/* Unlike the int/u64 options above, ICMP_FILTER clamps the length down
+ * to the user buffer instead of returning EINVAL: a short buffer
+ * succeeds and reports the truncated length back via optlen.
+ */
+TEST_F(raw, icmpfilter_undersize_clamped)
+{
+ char buf[2] = {};
+ socklen_t optlen = sizeof(buf);
+
+ ASSERT_EQ(0, getsockopt(self->fd, SOL_RAW, ICMP_FILTER,
+ buf, &optlen));
+ ASSERT_EQ(sizeof(buf), optlen);
+}
+
+TEST_F(raw, icmpfilter_wrong_proto)
+{
+ struct icmp_filter filt;
+ socklen_t optlen = sizeof(filt);
+ int fd;
+
+ fd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
+ if (fd < 0)
+ SKIP(return, "SOCK_RAW/UDP socket: %s", strerror(errno));
+
+ ASSERT_EQ(-1, getsockopt(fd, SOL_RAW, ICMP_FILTER, &filt, &optlen));
+ ASSERT_EQ(EOPNOTSUPP, errno);
+ close(fd);
+}
+
+TEST_F(raw, bad_optname)
+{
+ socklen_t optlen;
+ int val;
+
+ optlen = sizeof(val);
+
+ ASSERT_EQ(-1, getsockopt(self->fd, SOL_RAW, 0x7fff, &val, &optlen));
+ ASSERT_EQ(ENOPROTOOPT, errno);
+ ASSERT_EQ(sizeof(val), optlen);
+}
+
TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/net/lib.sh b/tools/testing/selftests/net/lib.sh
index b3827b43782b..d389a965d8f1 100644
--- a/tools/testing/selftests/net/lib.sh
+++ b/tools/testing/selftests/net/lib.sh
@@ -70,12 +70,27 @@ ksft_exit_status_merge()
$ksft_xfail $ksft_pass $ksft_skip $ksft_fail
}
+timestamp_ms()
+{
+ local now=$(date -u +%s:%N)
+ local seconds=${now%:*}
+ local nanoseconds=${now#*:}
+
+ if [[ $nanoseconds =~ ^[0-9]+$ ]]; then
+ nanoseconds=${nanoseconds:0:9}
+ else
+ nanoseconds=0
+ fi
+
+ echo $((seconds * 1000 + 10#$nanoseconds / 1000000))
+}
+
loopy_wait()
{
local sleep_cmd=$1; shift
local timeout_ms=$1; shift
- local start_time="$(date -u +%s%3N)"
+ local start_time=$(timestamp_ms)
while true
do
local out
@@ -84,7 +99,7 @@ loopy_wait()
return 0
fi
- local current_time="$(date -u +%s%3N)"
+ local current_time=$(timestamp_ms)
if ((current_time - start_time > timeout_ms)); then
echo -n "$out"
return 1
diff --git a/tools/testing/selftests/net/rtnetlink.py b/tools/testing/selftests/net/rtnetlink.py
index 3622413d793d..0c67c7c00d84 100755
--- a/tools/testing/selftests/net/rtnetlink.py
+++ b/tools/testing/selftests/net/rtnetlink.py
@@ -2,27 +2,106 @@
# SPDX-License-Identifier: GPL-2.0
import socket
+import struct
import time
-from lib.py import bkg, ip, ksft_exit, ksft_run, ksft_ge, ksft_true, KsftSkipEx
+from lib.py import bkg, ip, ksft_exit, ksft_run, ksft_eq, ksft_ge, ksft_true, KsftSkipEx
from lib.py import CmdExitFailure, NetNS, NetNSEnter, RtnlAddrFamily
IPV4_ALL_HOSTS_MULTICAST = b'\xe0\x00\x00\x01'
+IPV4_TEST_MULTICAST = b'\xef\x01\x01\x01'
+IPV6_TEST_MULTICAST = bytes.fromhex('ff020000000000000000000000000123')
+
+
+def _users_for(rtnl: RtnlAddrFamily, family: int, grp: bytes, ifindex: int):
+ """Return mc-users for grp on ifindex, or 0 if absent."""
+
+ addrs = rtnl.getmulticast({"ifa-family": family}, dump=True)
+ matches = [addr for addr in addrs
+ if addr['multicast'] == grp and addr['ifa-index'] == ifindex]
+ if not matches:
+ return 0
+ if 'mc-users' not in matches[0]:
+ return None
+
+ return matches[0]['mc-users']
+
def dump_mcaddr_check() -> None:
"""
- Verify that at least one interface has the IPv4 all-hosts multicast address.
- At least the loopback interface should have this address.
+ Verify IPv4 multicast addresses and their user counts in RTM_GETMULTICAST.
+ """
+
+ with NetNS() as ns:
+ with NetNSEnter(str(ns)):
+ ip("link set lo up")
+ rtnl = RtnlAddrFamily()
+ lo_idx = socket.if_nametoindex('lo')
+ addresses = rtnl.getmulticast({"ifa-family": socket.AF_INET}, dump=True)
+
+ all_host_multicasts = [
+ addr for addr in addresses
+ if addr['multicast'] == IPV4_ALL_HOSTS_MULTICAST
+ ]
+
+ ksft_ge(len(all_host_multicasts), 1,
+ "No interface found with the IPv4 all-hosts multicast address")
+
+ mreq = IPV4_TEST_MULTICAST + socket.inet_aton('127.0.0.1')
+ before = _users_for(rtnl, socket.AF_INET, IPV4_TEST_MULTICAST, lo_idx)
+ if before is None:
+ raise KsftSkipEx("kernel does not expose IFA_MC_USERS")
+
+ s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ try:
+ s1.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
+ s2.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
+
+ after_join = _users_for(rtnl, socket.AF_INET,
+ IPV4_TEST_MULTICAST, lo_idx)
+ if after_join is None:
+ raise KsftSkipEx("kernel does not expose IFA_MC_USERS")
+ ksft_eq(after_join - before, 2,
+ f"users delta != 2 after two joins "
+ f"(before={before}, after={after_join})")
+ finally:
+ s1.close()
+ s2.close()
+
+
+def dump_mcaddr6_check() -> None:
+ """
+ Verify IPv6 multicast addresses and their user counts in RTM_GETMULTICAST.
"""
- rtnl = RtnlAddrFamily()
- addresses = rtnl.getmulticast({"ifa-family": socket.AF_INET}, dump=True)
+ with NetNS() as ns:
+ with NetNSEnter(str(ns)):
+ ip("link set lo up")
+ rtnl = RtnlAddrFamily()
+ lo_idx = socket.if_nametoindex('lo')
+ before = _users_for(rtnl, socket.AF_INET6,
+ IPV6_TEST_MULTICAST, lo_idx)
+ if before is None:
+ raise KsftSkipEx("kernel does not expose IFA_MC_USERS for IPv6")
+
+ mreq = IPV6_TEST_MULTICAST + struct.pack('=I', lo_idx)
+ s1 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
+ s2 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
+ try:
+ s1.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
+ s2.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
- all_host_multicasts = [
- addr for addr in addresses if addr['multicast'] == IPV4_ALL_HOSTS_MULTICAST
- ]
+ after_join = _users_for(rtnl, socket.AF_INET6,
+ IPV6_TEST_MULTICAST, lo_idx)
+ if after_join is None:
+ raise KsftSkipEx("kernel does not expose IFA_MC_USERS for IPv6")
+ ksft_eq(after_join - before, 2,
+ f"IPv6 users delta != 2 after two joins "
+ f"(before={before}, after={after_join})")
+ finally:
+ s1.close()
+ s2.close()
- ksft_ge(len(all_host_multicasts), 1,
- "No interface found with the IPv4 all-hosts multicast address")
def ipv4_devconf_notify() -> None:
"""
@@ -56,7 +135,7 @@ def ipv4_devconf_notify() -> None:
f"No 'forwarding on' notificiation found for interface {ifname}")
def main() -> None:
- ksft_run([dump_mcaddr_check, ipv4_devconf_notify])
+ ksft_run([dump_mcaddr_check, dump_mcaddr6_check, ipv4_devconf_notify])
ksft_exit()
if __name__ == "__main__":