From 0403eceee14bb6eb4e417102cae830b7509b1554 Mon Sep 17 00:00:00 2001 From: Shivani Soni 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 --- 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 @@ -1439,6 +1439,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) @@ -1469,13 +1473,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); @@ -1620,6 +1636,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; } @@ -1837,6 +1863,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) { @@ -1950,13 +1978,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); @@ -1985,18 +2017,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; + } } } @@ -2355,6 +2390,7 @@ err_undo_flags: return res; } +EXPORT_SYMBOL(bond_enslave); /* Try to release the slave device from the bond device * It is legal to access curr_active_slave without a lock because all the function @@ -2476,13 +2512,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); @@ -2549,6 +2595,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. @@ -2570,6 +2617,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); @@ -4221,6 +4291,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 @@ -4261,6 +4349,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; } @@ -5057,20 +5148,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; + } } } @@ -5734,6 +5831,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); @@ -5762,6 +5880,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)); @@ -6104,6 +6224,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) @@ -6680,6 +6809,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 @@ -94,6 +94,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 #include #include +#include #include #include @@ -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 */