--- zzzz-none-000/linux-5.4.213/drivers/net/bonding/bond_main.c 2022-09-15 10:04:56.000000000 +0000 +++ miami-7690-761/linux-5.4.213/drivers/net/bonding/bond_main.c 2024-05-29 11:19:52.000000000 +0000 @@ -199,6 +199,7 @@ #endif unsigned int bond_net_id __read_mostly; +static unsigned long bond_id_mask = 0xFFFFFFF0; /*-------------------------- Forward declarations ---------------------------*/ @@ -231,6 +232,19 @@ return names[mode]; } +int bond_get_id(struct net_device *bond_dev) +{ + struct bonding *bond; + + if (!((bond_dev->priv_flags & IFF_BONDING) && + (bond_dev->flags & IFF_MASTER))) + return -EINVAL; + + bond = netdev_priv(bond_dev); + return bond->id; +} +EXPORT_SYMBOL(bond_get_id); + /*---------------------------------- VLAN -----------------------------------*/ /** @@ -847,6 +861,21 @@ if (BOND_MODE(bond) == BOND_MODE_8023AD) bond_3ad_handle_link_change(new_active, BOND_LINK_UP); + if (bond->params.mode == BOND_MODE_XOR) { + struct bond_cb *lag_cb_main; + + rcu_read_lock(); + lag_cb_main = rcu_dereference(bond_cb); + if (lag_cb_main && + lag_cb_main->bond_cb_link_up) { + struct net_device *dev; + + dev = new_active->dev; + lag_cb_main->bond_cb_link_up(dev); + } + rcu_read_unlock(); + } + if (bond_is_lb(bond)) bond_alb_handle_link_change(bond, new_active, BOND_LINK_UP); } else { @@ -1073,6 +1102,10 @@ 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_ALL_TSO | \ NETIF_F_HIGHDMA | NETIF_F_LRO) @@ -1099,13 +1132,25 @@ 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); @@ -1231,6 +1276,16 @@ 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; } @@ -1407,9 +1462,12 @@ const struct net_device_ops *slave_ops = slave_dev->netdev_ops; struct slave *new_slave = NULL, *prev_slave; struct sockaddr_storage ss; + struct bond_cb *lag_cb_main; int link_reporting; int res = 0, i; + ASSERT_RTNL(); + if (!bond->params.use_carrier && slave_dev->ethtool_ops->get_link == NULL && slave_ops->ndo_do_ioctl == NULL) { @@ -1526,13 +1584,17 @@ 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); @@ -1561,18 +1623,21 @@ 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; + } } } @@ -1811,6 +1876,13 @@ bond_is_active_slave(new_slave) ? "an active" : "a backup", new_slave->link != BOND_LINK_DOWN ? "an up" : "a down"); + rcu_read_lock(); + lag_cb_main = rcu_dereference(bond_cb); + if (lag_cb_main && lag_cb_main->bond_cb_enslave) + lag_cb_main->bond_cb_enslave(slave_dev); + + rcu_read_unlock(); + /* enslave is successful */ bond_queue_slave_event(new_slave); return 0; @@ -1878,8 +1950,16 @@ } } + rcu_read_lock(); + lag_cb_main = rcu_dereference(bond_cb); + if (lag_cb_main && lag_cb_main->bond_cb_enslave) + lag_cb_main->bond_cb_enslave(slave_dev); + + rcu_read_unlock(); + 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 @@ -1899,6 +1979,7 @@ struct bonding *bond = netdev_priv(bond_dev); struct slave *slave, *oldcurrent; struct sockaddr_storage ss; + struct bond_cb *lag_cb_main; int old_flags = bond_dev->flags; netdev_features_t old_features = bond_dev->features; @@ -1921,6 +2002,13 @@ bond_set_slave_inactive_flags(slave, BOND_SLAVE_NOTIFY_NOW); + rcu_read_lock(); + lag_cb_main = rcu_dereference(bond_cb); + if (lag_cb_main && lag_cb_main->bond_cb_release) + lag_cb_main->bond_cb_release(slave_dev); + + rcu_read_unlock(); + bond_sysfs_slave_del(slave); /* recompute stats just before removing the slave */ @@ -1980,13 +2068,23 @@ } 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); @@ -2052,6 +2150,7 @@ { 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. @@ -2073,6 +2172,29 @@ 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); @@ -2217,6 +2339,8 @@ { struct list_head *iter; struct slave *slave, *primary; + struct net_device *slave_dev = NULL; + struct bond_cb *lag_cb_main; bond_for_each_slave(bond, slave, iter) { switch (slave->link_new_state) { @@ -2254,6 +2378,10 @@ bond_set_active_slave(slave); } + if ((bond->params.mode == BOND_MODE_XOR) && + (!slave_dev)) + slave_dev = slave->dev; + slave_info(bond->dev, slave->dev, "link status definitely up, %u Mbps %s duplex\n", slave->speed == SPEED_UNKNOWN ? 0 : slave->speed, slave->duplex ? "full" : "half"); @@ -2300,6 +2428,14 @@ unblock_netpoll_tx(); } + rcu_read_lock(); + lag_cb_main = rcu_dereference(bond_cb); + + if (slave_dev && lag_cb_main && lag_cb_main->bond_cb_link_up) + lag_cb_main->bond_cb_link_up(slave_dev); + + rcu_read_unlock(); + bond_set_carrier(bond); } @@ -3288,11 +3424,241 @@ ep = skb_header_pointer(skb, 0, sizeof(hdr_tmp), &hdr_tmp); if (ep) - return ep->h_dest[5] ^ ep->h_source[5] ^ ep->h_proto; + return ep->h_dest[5] ^ ep->h_source[5]; return 0; } /* Extract the appropriate headers based on bond's xmit policy */ +static bool bond_flow_dissect_without_skb(struct bonding *bond, + u8 *src_mac, u8 *dst_mac, + void *psrc, void *pdst, + u16 protocol, __be16 *layer4hdr, + struct flow_keys *fk) +{ + u32 *src = NULL; + u32 *dst = NULL; + + fk->ports.ports = 0; + src = (uint32_t *)psrc; + dst = (uint32_t *)pdst; + + if (protocol == htons(ETH_P_IP)) { + /* V4 addresses and address type*/ + fk->addrs.v4addrs.src = src[0]; + fk->addrs.v4addrs.dst = dst[0]; + fk->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; + } else if (protocol == htons(ETH_P_IPV6)) { + /* V6 addresses and address type*/ + memcpy(&fk->addrs.v6addrs.src, src, sizeof(struct in6_addr)); + memcpy(&fk->addrs.v6addrs.dst, dst, sizeof(struct in6_addr)); + fk->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; + } else { + return false; + } + if ((bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34) && + (layer4hdr)) + fk->ports.ports = *layer4hdr; + + return true; +} + +/* bond_xmit_hash_without_skb - Applies load balancing algorithm for a packet, + * to calculate hash for a given set of L2/L3 addresses. Does not + * calculate egress interface. + */ +uint32_t bond_xmit_hash_without_skb(u8 *src_mac, u8 *dst_mac, + void *psrc, void *pdst, u16 protocol, + struct net_device *bond_dev, + __be16 *layer4hdr) +{ + struct bonding *bond = netdev_priv(bond_dev); + struct flow_keys flow; + u32 hash = 0; + + if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 || + !bond_flow_dissect_without_skb(bond, src_mac, dst_mac, psrc, + pdst, protocol, layer4hdr, &flow)) + return (dst_mac[5] ^ src_mac[5]); + + if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER23) + hash = dst_mac[5] ^ src_mac[5]; + else if (layer4hdr) + hash = (__force u32)flow.ports.ports; + + hash ^= (__force u32)flow_get_u32_dst(&flow) ^ + (__force u32)flow_get_u32_src(&flow); + hash ^= (hash >> 16); + hash ^= (hash >> 8); + + return hash; +} + +/* bond_xor_get_tx_dev - Calculate egress interface for a given packet for a LAG + * that is configured in balance-xor mode + * @skb: pointer to skb to be egressed + * @src_mac: pointer to source L2 address + * @dst_mac: pointer to destination L2 address + * @src: pointer to source L3 address in network order + * @dst: pointer to destination L3 address in network order + * @protocol: L3 protocol + * @bond_dev: pointer to bond master device + * + * If @skb is NULL, bond_xmit_hash_without_skb is used to calculate hash using + * L2/L3 addresses. + * + * Returns: Either valid slave device, or NULL otherwise + */ +static struct net_device *bond_xor_get_tx_dev(struct sk_buff *skb, + u8 *src_mac, u8 *dst_mac, + void *src, void *dst, + u16 protocol, + struct net_device *bond_dev, + __be16 *layer4hdr) +{ + struct bonding *bond = netdev_priv(bond_dev); + int slave_cnt = READ_ONCE(bond->slave_cnt); + int slave_id = 0, i = 0; + u32 hash; + struct list_head *iter; + struct slave *slave; + + if (slave_cnt == 0) { + pr_debug("%s: Error: No slave is attached to the interface\n", + bond_dev->name); + return NULL; + } + + if (skb) { + hash = bond_xmit_hash(bond, skb); + slave_id = hash % slave_cnt; + } else { + if (bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER23 && + bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER2 && + bond->params.xmit_policy != BOND_XMIT_POLICY_LAYER34) { + pr_debug("%s: Error: Unsupported hash policy for balance-XOR fast path\n", + bond_dev->name); + return NULL; + } + + hash = bond_xmit_hash_without_skb(src_mac, dst_mac, src, + dst, protocol, bond_dev, + layer4hdr); + slave_id = hash % slave_cnt; + } + + i = slave_id; + + /* Here we start from the slave with slave_id */ + bond_for_each_slave_rcu(bond, slave, iter) { + if (--i < 0) { + if (bond_slave_can_tx(slave)) + return slave->dev; + } + } + + /* Here we start from the first slave up to slave_id */ + i = slave_id; + bond_for_each_slave_rcu(bond, slave, iter) { + if (--i < 0) + break; + if (bond_slave_can_tx(slave)) + return slave->dev; + } + + 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 + * + * @skb: pointer to skb to be egressed, if valid + * @src_mac: pointer to source L2 address + * @dst_mac: pointer to destination L2 address + * @src: pointer to source L3 address in network order + * @dst: pointer to destination L3 address in network order + * @protocol: L3 protocol id from L2 header + * @bond_dev: pointer to bond master device + * + * Returns: Either valid slave device, or NULL for un-supported LAG modes + */ +struct net_device *bond_get_tx_dev(struct sk_buff *skb, uint8_t *src_mac, + u8 *dst_mac, void *src, + void *dst, u16 protocol, + struct net_device *bond_dev, + __be16 *layer4hdr) +{ + struct bonding *bond; + + if (!bond_dev) + return NULL; + + if (!((bond_dev->priv_flags & IFF_BONDING) && + (bond_dev->flags & IFF_MASTER))) + return NULL; + + bond = netdev_priv(bond_dev); + + switch (bond->params.mode) { + case BOND_MODE_XOR: + return bond_xor_get_tx_dev(skb, src_mac, dst_mac, + src, dst, protocol, + bond_dev, layer4hdr); + case BOND_MODE_8023AD: + 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; + } +} +EXPORT_SYMBOL(bond_get_tx_dev); + +/* In bond_xmit_xor() , we determine the output device by using a pre- + * determined xmit_hash_policy(), If the selected device is not enabled, + * find the next active slave. + */ +static int bond_xmit_xor(struct sk_buff *skb, struct net_device *dev) +{ + struct bonding *bond = netdev_priv(dev); + struct net_device *outdev; + + outdev = bond_xor_get_tx_dev(skb, NULL, NULL, NULL, + NULL, 0, dev, NULL); + if (!outdev) + goto out; + + bond_dev_queue_xmit(bond, skb, outdev); + goto final; +out: + /* no suitable interface, frame not sent */ + dev_kfree_skb(skb); +final: + return NETDEV_TX_OK; +} + +/* Extract the appropriate headers based on bond's xmit policy */ static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb, struct flow_keys *fk) { @@ -3832,20 +4198,26 @@ 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; + } } } @@ -4141,18 +4513,21 @@ struct net_device *dev) { struct bonding *bond = netdev_priv(dev); - struct slave *slave; - struct bond_up_slave *slaves; - unsigned int count; + struct net_device *outdev = NULL; - slaves = rcu_dereference(bond->slave_arr); - count = slaves ? READ_ONCE(slaves->count) : 0; - if (likely(count)) { - slave = slaves->arr[bond_xmit_hash(bond, skb) % count]; - bond_dev_queue_xmit(bond, skb, slave->dev); - } else { - bond_tx_drop(dev, skb); - } + outdev = bond_3ad_get_tx_dev(skb, NULL, NULL, NULL, + NULL, 0, dev, NULL); + + if (!outdev) + goto out; + + bond_dev_queue_xmit(bond, skb, outdev); + goto final; + +out: + dev_kfree_skb(skb); + +final: return NETDEV_TX_OK; } @@ -4237,6 +4612,27 @@ return txq; } +/* 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); @@ -4250,8 +4646,9 @@ return bond_xmit_roundrobin(skb, dev); case BOND_MODE_ACTIVEBACKUP: return bond_xmit_activebackup(skb, dev); - case BOND_MODE_8023AD: case BOND_MODE_XOR: + return bond_xmit_xor(skb, dev); + case BOND_MODE_8023AD: return bond_3ad_xor_xmit(skb, dev); case BOND_MODE_BROADCAST: return bond_xmit_broadcast(skb, dev); @@ -4259,6 +4656,8 @@ 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)); @@ -4385,6 +4784,18 @@ struct bonding *bond = netdev_priv(bond_dev); if (bond->wq) destroy_workqueue(bond->wq); + + if (bond->id != (~0U)) + clear_bit(bond->id, &bond_id_mask); + + /* + * 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) @@ -4934,9 +5345,87 @@ bond_work_init_all(bond); rtnl_unlock(); + + bond = netdev_priv(bond_dev); + bond->id = ~0U; + if (bond_id_mask != (~0UL)) { + bond->id = (u32)ffz(bond_id_mask); + set_bit(bond->id, &bond_id_mask); + } + return 0; } +/* 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);