lede/target/linux/qualcommax/patches-6.1/0600-8-qca-nss-ecm-support-MLO-bonding.patch
2024-12-08 16:01:09 +08:00

537 lines
17 KiB
Diff

From 0403eceee14bb6eb4e417102cae830b7509b1554 Mon Sep 17 00:00:00 2001
From: Shivani Soni <quic_soni@quicinc.com>
Date: Thu, 17 Nov 2022 00:16:38 +0530
Subject: [PATCH] arm/arm64: Add support for MLO bonding
1. Introduced BOND_MODE_MLO to support MLO bonding
2. Transmit handling according to new mode
Change-Id: Ib272e77cce56ee50b0a13305fac8fae76743c206
Signed-off-by: Shivani Soni <quic_soni@quicinc.com>
---
drivers/net/bonding/bond_main.c | 212 ++++++++++++++++++++++++-----
drivers/net/bonding/bond_options.c | 1 +
include/net/bonding.h | 30 +++-
include/uapi/linux/if_bonding.h | 1 +
4 files changed, 210 insertions(+), 34 deletions(-)
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1471,6 +1471,10 @@ static netdev_features_t bond_fix_featur
return features;
}
+#define BOND_MLO_VLAN_FEATURES (NETIF_F_SG | \
+ NETIF_F_FRAGLIST | \
+ NETIF_F_HIGHDMA | NETIF_F_LRO)
+
#define BOND_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | \
NETIF_F_HIGHDMA | NETIF_F_LRO)
@@ -1501,13 +1505,25 @@ static void bond_compute_features(struct
if (!bond_has_slaves(bond))
goto done;
+
+ /*
+ * Use features specific to bond MLO
+ */
+ if (BOND_MODE(bond) == BOND_MODE_MLO) {
+ vlan_features = BOND_MLO_VLAN_FEATURES;
+ }
+
vlan_features &= NETIF_F_ALL_FOR_ALL;
mpls_features &= NETIF_F_ALL_FOR_ALL;
bond_for_each_slave(bond, slave, iter) {
- vlan_features = netdev_increment_features(vlan_features,
- slave->dev->vlan_features, BOND_VLAN_FEATURES);
-
+ if (BOND_MODE(bond) == BOND_MODE_MLO) {
+ vlan_features = netdev_increment_features(vlan_features,
+ slave->dev->vlan_features, BOND_MLO_VLAN_FEATURES);
+ } else {
+ vlan_features = netdev_increment_features(vlan_features,
+ slave->dev->vlan_features, BOND_VLAN_FEATURES);
+ }
enc_features = netdev_increment_features(enc_features,
slave->dev->hw_enc_features,
BOND_ENC_FEATURES);
@@ -1652,6 +1668,16 @@ static rx_handler_result_t bond_handle_f
bond->dev->addr_len);
}
+ /*
+ * Set the PACKET_HOST for MLO mode as
+ * MLO bond netdevice needs to support routing
+ */
+ if (BOND_MODE(bond) == BOND_MODE_MLO) {
+ if (ether_addr_equal(bond->dev->dev_addr, eth_hdr(skb)->h_dest)) {
+ skb->pkt_type = PACKET_HOST;
+ }
+ }
+
return ret;
}
@@ -1869,6 +1895,8 @@ int bond_enslave(struct net_device *bond
return -EPERM;
}
+ ASSERT_RTNL();
+
if (!bond->params.use_carrier &&
slave_dev->ethtool_ops->get_link == NULL &&
slave_ops->ndo_eth_ioctl == NULL) {
@@ -1982,13 +2010,17 @@ int bond_enslave(struct net_device *bond
call_netdevice_notifiers(NETDEV_JOIN, slave_dev);
/* If this is the first slave, then we need to set the master's hardware
- * address to be the same as the slave's.
+ * address to be the same as the slave's except for BOND_MODE_MLO.
+ * For BOND_MODE_MLO, master's mac address is MLD address which should
+ * not be same as slave's address.
*/
- if (!bond_has_slaves(bond) &&
- bond->dev->addr_assign_type == NET_ADDR_RANDOM) {
- res = bond_set_dev_addr(bond->dev, slave_dev);
- if (res)
- goto err_undo_flags;
+ if (BOND_MODE(bond) != BOND_MODE_MLO) {
+ if (!bond_has_slaves(bond) &&
+ bond->dev->addr_assign_type == NET_ADDR_RANDOM) {
+ res = bond_set_dev_addr(bond->dev, slave_dev);
+ if (res)
+ goto err_undo_flags;
+ }
}
new_slave = bond_alloc_slave(bond, slave_dev);
@@ -2017,18 +2049,21 @@ int bond_enslave(struct net_device *bond
bond_hw_addr_copy(new_slave->perm_hwaddr, slave_dev->dev_addr,
slave_dev->addr_len);
- if (!bond->params.fail_over_mac ||
- BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
- /* Set slave to master's mac address. The application already
- * set the master's mac address to that of the first slave
- */
- memcpy(ss.__data, bond_dev->dev_addr, bond_dev->addr_len);
- ss.ss_family = slave_dev->type;
- res = dev_set_mac_address(slave_dev, (struct sockaddr *)&ss,
- extack);
- if (res) {
- slave_err(bond_dev, slave_dev, "Error %d calling set_mac_address\n", res);
- goto err_restore_mtu;
+ /* Set slave to master's mac address except for BOND_MODE_MLO
+ * as for MLO mode master's mac address is not same as slave's mac address.
+ * The application already set the master's mac address to that of the first slave
+ */
+ if (BOND_MODE(bond) != BOND_MODE_MLO) {
+ if (!bond->params.fail_over_mac ||
+ BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
+ memcpy(ss.__data, bond_dev->dev_addr, bond_dev->addr_len);
+ ss.ss_family = slave_dev->type;
+ res = dev_set_mac_address(slave_dev, (struct sockaddr *)&ss,
+ extack);
+ if (res) {
+ slave_err(bond_dev, slave_dev, "Error %d calling set_mac_address\n", res);
+ goto err_restore_mtu;
+ }
}
}
@@ -2391,6 +2426,7 @@ err_undo_flags:
return res;
}
+EXPORT_SYMBOL(bond_enslave);
/* Try to release the slave device <slave> from the bond device <master>
* It is legal to access curr_active_slave without a lock because all the function
@@ -2518,13 +2554,23 @@ static int __bond_release_one(struct net
}
bond_set_carrier(bond);
- if (!bond_has_slaves(bond))
- eth_hw_addr_random(bond_dev);
+
+ /*
+ * Avoid changing the mac address of bond netdevice for MLO case,
+ * This will only be supported from wifi driver.
+ */
+ if (BOND_MODE(bond) != BOND_MODE_MLO) {
+ if (!bond_has_slaves(bond))
+ eth_hw_addr_random(bond_dev);
+ }
unblock_netpoll_tx();
synchronize_rcu();
bond->slave_cnt--;
+ /*
+ * TODO: Avoid MAC address change notification for BOND_MODE_MLO
+ */
if (!bond_has_slaves(bond)) {
call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev);
call_netdevice_notifiers(NETDEV_RELEASE, bond->dev);
@@ -2591,6 +2637,7 @@ int bond_release(struct net_device *bond
{
return __bond_release_one(bond_dev, slave_dev, false, false);
}
+EXPORT_SYMBOL(bond_release);
/* First release a slave and then destroy the bond if no more slaves are left.
* Must be under rtnl_lock when this function is called.
@@ -2612,6 +2659,29 @@ static int bond_release_and_destroy(stru
return ret;
}
+/* Destroy the bond for BOND_MODE_MLO if no more slaves are left.
+ * Must be under rtnl_lock when this function is called.
+ */
+bool bond_destroy_mlo(struct net_device *bond_dev)
+{
+ struct bonding *bond = netdev_priv(bond_dev);
+
+ ASSERT_RTNL();
+
+ if (!bond_has_slaves(bond)) {
+ bond_dev->priv_flags |= IFF_DISABLE_NETPOLL;
+ netdev_info(bond_dev, "Destroying bond as no slaves are present\n");
+ bond_remove_proc_entry(bond);
+ unregister_netdevice(bond_dev);
+ return true;
+ }
+
+ pr_err("%p: Not able to destroy bond netdevice: %s as slaves are present\n", bond_dev, bond_dev->name);
+
+ return false;
+}
+EXPORT_SYMBOL(bond_destroy_mlo);
+
static void bond_info_query(struct net_device *bond_dev, struct ifbond *info)
{
struct bonding *bond = netdev_priv(bond_dev);
@@ -4263,6 +4333,24 @@ static struct net_device *bond_xor_get_t
return NULL;
}
+/* Transmit function for BOND_MODE_MLO.
+ * Get transmit link interface from registered callback.
+ */
+struct net_device *bond_mlo_get_tx_dev(struct net_device *bond_dev, u8 *dst_mac)
+{
+ struct net_device *slave_dev = NULL;
+ struct mlo_bond_info *mlo_info = NULL;
+ void *bond_mlo_ctx;
+
+ mlo_info = bond_get_mlo_priv(bond_dev);
+ if (mlo_info->bond_get_mlo_tx_netdev) {
+ bond_mlo_ctx = bond_get_mlo_ctx(bond_dev);
+ slave_dev = mlo_info->bond_get_mlo_tx_netdev(bond_mlo_ctx, dst_mac);
+ }
+
+ return slave_dev;
+}
+
/* bond_get_tx_dev - Calculate egress interface for a given packet.
*
* Supports 802.3AD and balance-xor modes
@@ -4303,6 +4391,9 @@ struct net_device *bond_get_tx_dev(struc
return bond_3ad_get_tx_dev(skb, src_mac, dst_mac,
src, dst, protocol,
bond_dev, layer4hdr);
+ case BOND_MODE_MLO:
+ return bond_mlo_get_tx_dev(bond_dev, dst_mac);
+
default:
return NULL;
}
@@ -5099,20 +5190,26 @@ static int bond_set_mac_address(struct n
if (!is_valid_ether_addr(ss->__data))
return -EADDRNOTAVAIL;
- bond_for_each_slave(bond, slave, iter) {
- slave_dbg(bond_dev, slave->dev, "%s: slave=%p\n",
- __func__, slave);
- res = dev_set_mac_address(slave->dev, addr, NULL);
- if (res) {
- /* TODO: consider downing the slave
- * and retry ?
- * User should expect communications
- * breakage anyway until ARP finish
- * updating, so...
- */
- slave_dbg(bond_dev, slave->dev, "%s: err %d\n",
- __func__, res);
- goto unwind;
+ /*
+ * Do not allow mac address change for slave netdevice for BOND_MODE_MLO
+ * as master's mac address is not same as slave's mac address.
+ */
+ if (BOND_MODE(bond) != BOND_MODE_MLO) {
+ bond_for_each_slave(bond, slave, iter) {
+ slave_dbg(bond_dev, slave->dev, "%s: slave=%p\n",
+ __func__, slave);
+ res = dev_set_mac_address(slave->dev, addr, NULL);
+ if (res) {
+ /* TODO: consider downing the slave
+ * and retry ?
+ * User should expect communications
+ * breakage anyway until ARP finish
+ * updating, so...
+ */
+ slave_dbg(bond_dev, slave->dev, "%s: err %d\n",
+ __func__, res);
+ goto unwind;
+ }
}
}
@@ -5776,6 +5873,27 @@ static netdev_tx_t bond_tls_device_xmit(
}
#endif
+/* In bond_xmit_mlo(), we send the packet and bond netdevice
+ * to registered callback for final xmit.
+ */
+static netdev_tx_t bond_xmit_mlo(struct sk_buff *skb, struct net_device *bond_dev)
+{
+ struct bonding *bond = netdev_priv(bond_dev);
+ int slave_cnt, ret;
+ struct mlo_bond_info *mlo_info = bond_get_mlo_priv(bond_dev);
+
+ slave_cnt = READ_ONCE(bond->slave_cnt);
+ if (unlikely(slave_cnt == 0) || unlikely(!mlo_info->bond_mlo_xmit_netdev)) {
+ bond_tx_drop(bond_dev, skb);
+ } else {
+ ret = mlo_info->bond_mlo_xmit_netdev(skb, bond_dev);
+ if (ret != NET_XMIT_SUCCESS)
+ netdev_err(bond_dev, "Xmit failed with mode %d %p\n", BOND_MODE(bond), skb);
+ }
+
+ return NETDEV_TX_OK;
+}
+
static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct bonding *bond = netdev_priv(dev);
@@ -5804,6 +5922,8 @@ static netdev_tx_t __bond_start_xmit(str
return bond_alb_xmit(skb, dev);
case BOND_MODE_TLB:
return bond_tlb_xmit(skb, dev);
+ case BOND_MODE_MLO:
+ return bond_xmit_mlo(skb, dev);
default:
/* Should never happen, mode already checked */
netdev_err(dev, "Unknown bonding mode %d\n", BOND_MODE(bond));
@@ -6146,6 +6266,15 @@ static void bond_destructor(struct net_d
if (bond->id != (~0U))
clear_bit(bond->id, &bond_id_mask);
/* QCA NSS ECM bonding support */
+
+ /*
+ * Wifi driver registered callback to destroy wiphy for MLO bond netdevice
+ */
+ if (bond_is_mlo_device(bond_dev)) {
+ if (bond->mlo_info.bond_mlo_netdev_priv_destructor) {
+ bond->mlo_info.bond_mlo_netdev_priv_destructor(bond_dev);
+ }
+ }
}
void bond_setup(struct net_device *bond_dev)
@@ -6722,6 +6851,76 @@ out:
return res;
}
+/* bond_create_mlo()
+ * Create bond netdevice for BOND_MODE_MLO with MLO specific callback and context.
+ */
+struct net_device *bond_create_mlo(struct net *net, const char *name, struct mlo_bond_info *mlo_info)
+{
+ struct net_device *bond_dev;
+ struct bonding *bond;
+ int res;
+
+ ASSERT_RTNL();
+
+ bond_dev = alloc_netdev_mq(sizeof(struct bonding),
+ name ? name : "bond%d", NET_NAME_UNKNOWN,
+ bond_setup, tx_queues);
+ if (!bond_dev) {
+ pr_err("%s: eek! can't alloc netdev!\n", name);
+ return NULL;
+ }
+
+ bond = netdev_priv(bond_dev);
+
+ dev_net_set(bond_dev, net);
+ bond_dev->rtnl_link_ops = &bond_link_ops;
+
+ /*
+ * MLO specific initialization.
+ */
+ bond_dev->ieee80211_ptr = mlo_info->wdev;
+ bond->params.mode = BOND_MODE_MLO;
+ mlo_info->wdev->netdev = bond_dev;
+
+ memcpy((void *)&bond->mlo_info, (void *)mlo_info, sizeof(*mlo_info));
+ eth_hw_addr_random(bond_dev);
+
+ /*
+ * Disable HW CSUM as wlan driver doesn't support
+ */
+ bond_dev->hw_features &= ~(NETIF_F_HW_CSUM);
+ bond_dev->features &= ~(NETIF_F_HW_CSUM);
+
+ res = register_netdevice(bond_dev);
+ if (res < 0) {
+ free_netdev(bond_dev);
+ return NULL;
+ }
+
+ netif_carrier_off(bond_dev);
+ bond_work_init_all(bond);
+
+ bond->id = ~0U;
+ if (bond_id_mask != (~0UL)) {
+ bond->id = (u32)ffz(bond_id_mask);
+ set_bit(bond->id, &bond_id_mask);
+ }
+
+ return bond_dev;
+}
+EXPORT_SYMBOL(bond_create_mlo);
+
+/* bond_get_mlo_ctx
+ * Returns MLO context stored in netdev priv of bond netdevice
+ */
+void *bond_get_mlo_ctx(struct net_device *bond_dev)
+{
+ struct mlo_bond_info *mlo_info = bond_get_mlo_priv(bond_dev);
+
+ return mlo_info->bond_mlo_ctx;
+}
+EXPORT_SYMBOL(bond_get_mlo_ctx);
+
static int __net_init bond_net_init(struct net *net)
{
struct bond_net *bn = net_generic(net, bond_net_id);
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -95,6 +95,7 @@ static const struct bond_opt_value bond_
{ "802.3ad", BOND_MODE_8023AD, 0},
{ "balance-tlb", BOND_MODE_TLB, 0},
{ "balance-alb", BOND_MODE_ALB, 0},
+ { "mode mlo", BOND_MODE_MLO, 0},
{ NULL, -1, 0},
};
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -25,6 +25,7 @@
#include <linux/etherdevice.h>
#include <linux/reciprocal_div.h>
#include <linux/if_link.h>
+#include <net/cfg80211.h>
#include <net/bond_3ad.h>
#include <net/bond_alb.h>
@@ -203,6 +204,22 @@ struct bond_up_slave {
struct slave *arr[];
};
+/**
+ * mlo_bond_info - mlo_bond_info structure maintains members corresponding to wifi 7
+ * @bond_mlo_xmit_netdev: Callback function to provide skb to wifi driver for xmit
+ * @bond_get_mlo_tx_netdev: Callback function to get link interface from wifi driver for transmit
+ * @bond_mlo_ctx: Private member for wifi driver
+ * @wdev: ieee80211_ptr for wifi VAP
+ * @bond_mlo_netdev_priv_destructor: Callback function to remove wiphy instance from wifi driver
+ */
+struct mlo_bond_info {
+ int (*bond_mlo_xmit_netdev)(struct sk_buff *skb, struct net_device *bond_dev);
+ struct net_device *(*bond_get_mlo_tx_netdev)(void *bond_mlo_ctx, void *dst);
+ void *bond_mlo_ctx;
+ struct wireless_dev *wdev;
+ void (*bond_mlo_netdev_priv_destructor)(struct net_device *bond_dev);
+};
+
/*
* Link pseudo-state only used internally by monitors
*/
@@ -268,6 +285,8 @@ struct bonding {
#endif /* CONFIG_XFRM_OFFLOAD */
struct bpf_prog *xdp_prog;
u32 id; /* QCA NSS ECM bonding support */
+ /* MLO mode info */
+ struct mlo_bond_info mlo_info;
};
#define bond_slave_get_rcu(dev) \
@@ -287,6 +306,19 @@ struct bond_vlan_tag {
bool bond_sk_check(struct bonding *bond);
/**
+ * Returns False if the net_device is not MLO bond netdvice
+ *
+ */
+static inline bool bond_is_mlo_device(struct net_device *bond_dev)
+{
+ struct bonding *bond = netdev_priv(bond_dev);
+ if (BOND_MODE(bond) == BOND_MODE_MLO)
+ return true;
+
+ return false;
+}
+
+/**
* Returns NULL if the net_device does not belong to any of the bond's slaves
*
* Caller must hold bond lock for read
@@ -650,6 +682,12 @@ static inline __be32 bond_confirm_addr(s
return addr;
}
+static inline struct mlo_bond_info *bond_get_mlo_priv(struct net_device *bond_dev)
+{
+ struct bonding *bond = netdev_priv(bond_dev);
+ return &bond->mlo_info;
+}
+
struct bond_net {
struct net *net; /* Associated network namespace */
struct list_head dev_list;
@@ -663,14 +701,17 @@ int bond_rcv_validate(const struct sk_bu
netdev_tx_t bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev);
int bond_get_id(struct net_device *bond_dev); /* QCA NSS ECM bonding support */
int bond_create(struct net *net, const char *name);
+extern struct net_device *bond_create_mlo(struct net *net, const char *name, struct mlo_bond_info *mlo_info);
+extern void *bond_get_mlo_ctx(struct net_device *bond_dev);
+extern bool bond_destroy_mlo(struct net_device *bond_dev);
int bond_create_sysfs(struct bond_net *net);
void bond_destroy_sysfs(struct bond_net *net);
void bond_prepare_sysfs_group(struct bonding *bond);
int bond_sysfs_slave_add(struct slave *slave);
void bond_sysfs_slave_del(struct slave *slave);
-int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
+extern int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
struct netlink_ext_ack *extack);
-int bond_release(struct net_device *bond_dev, struct net_device *slave_dev);
+extern int bond_release(struct net_device *bond_dev, struct net_device *slave_dev);
u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb);
int bond_set_carrier(struct bonding *bond);
void bond_select_active_slave(struct bonding *bond);
--- a/include/uapi/linux/if_bonding.h
+++ b/include/uapi/linux/if_bonding.h
@@ -71,6 +71,7 @@
#define BOND_MODE_8023AD 4
#define BOND_MODE_TLB 5
#define BOND_MODE_ALB 6 /* TLB + RLB (receive load balancing) */
+#define BOND_MODE_MLO 7 /* MLO (Multi link) mode for Wi-Fi 7 AP links */
/* each slave's link has 4 states */
#define BOND_LINK_UP 0 /* link is up and running */