]> www.infradead.org Git - users/hch/misc.git/commitdiff
bng_en: Configure default VNIC
authorBhargava Marreddy <bhargava.marreddy@broadcom.com>
Fri, 19 Sep 2025 17:47:41 +0000 (23:17 +0530)
committerJakub Kicinski <kuba@kernel.org>
Tue, 23 Sep 2025 00:51:28 +0000 (17:51 -0700)
Add functions to add a filter to the VNIC to configure unicast
addresses. Also, add multicast, broadcast, and promiscuous settings
to the default VNIC.

Signed-off-by: Bhargava Marreddy <bhargava.marreddy@broadcom.com>
Reviewed-by: Vikas Gupta <vikas.gupta@broadcom.com>
Reviewed-by: Rajashekar Hudumula <rajashekar.hudumula@broadcom.com>
Link: https://patch.msgid.link/20250919174742.24969-11-bhargava.marreddy@broadcom.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.c
drivers/net/ethernet/broadcom/bnge/bnge_hwrm_lib.h
drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
drivers/net/ethernet/broadcom/bnge/bnge_netdev.h

index ae780939828f2cf50cb166d27b5aea6067eff667..198f49b40dbf0071eaf196b1bc0870d0865f6fac 100644 (file)
@@ -854,6 +854,78 @@ void bnge_hwrm_update_rss_hash_cfg(struct bnge_net *bn)
        bnge_hwrm_req_drop(bd, req);
 }
 
+int bnge_hwrm_l2_filter_free(struct bnge_dev *bd, struct bnge_l2_filter *fltr)
+{
+       struct hwrm_cfa_l2_filter_free_input *req;
+       int rc;
+
+       rc = bnge_hwrm_req_init(bd, req, HWRM_CFA_L2_FILTER_FREE);
+       if (rc)
+               return rc;
+
+       req->l2_filter_id = fltr->base.filter_id;
+       return bnge_hwrm_req_send(bd, req);
+}
+
+int bnge_hwrm_l2_filter_alloc(struct bnge_dev *bd, struct bnge_l2_filter *fltr)
+{
+       struct hwrm_cfa_l2_filter_alloc_output *resp;
+       struct hwrm_cfa_l2_filter_alloc_input *req;
+       int rc;
+
+       rc = bnge_hwrm_req_init(bd, req, HWRM_CFA_L2_FILTER_ALLOC);
+       if (rc)
+               return rc;
+
+       req->flags = cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_FLAGS_PATH_RX);
+
+       req->flags |= cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_FLAGS_OUTERMOST);
+       req->dst_id = cpu_to_le16(fltr->base.fw_vnic_id);
+       req->enables =
+               cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR |
+                           CFA_L2_FILTER_ALLOC_REQ_ENABLES_DST_ID |
+                           CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_ADDR_MASK);
+       ether_addr_copy(req->l2_addr, fltr->l2_key.dst_mac_addr);
+       eth_broadcast_addr(req->l2_addr_mask);
+
+       if (fltr->l2_key.vlan) {
+               req->enables |=
+                       cpu_to_le32(CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_IVLAN |
+                               CFA_L2_FILTER_ALLOC_REQ_ENABLES_L2_IVLAN_MASK |
+                               CFA_L2_FILTER_ALLOC_REQ_ENABLES_NUM_VLANS);
+               req->num_vlans = 1;
+               req->l2_ivlan = cpu_to_le16(fltr->l2_key.vlan);
+               req->l2_ivlan_mask = cpu_to_le16(0xfff);
+       }
+
+       resp = bnge_hwrm_req_hold(bd, req);
+       rc = bnge_hwrm_req_send(bd, req);
+       if (!rc)
+               fltr->base.filter_id = resp->l2_filter_id;
+
+       bnge_hwrm_req_drop(bd, req);
+       return rc;
+}
+
+int bnge_hwrm_cfa_l2_set_rx_mask(struct bnge_dev *bd,
+                                struct bnge_vnic_info *vnic)
+{
+       struct hwrm_cfa_l2_set_rx_mask_input *req;
+       int rc;
+
+       rc = bnge_hwrm_req_init(bd, req, HWRM_CFA_L2_SET_RX_MASK);
+       if (rc)
+               return rc;
+
+       req->vnic_id = cpu_to_le32(vnic->fw_vnic_id);
+       if (vnic->rx_mask & CFA_L2_SET_RX_MASK_REQ_MASK_MCAST) {
+               req->num_mc_entries = cpu_to_le32(vnic->mc_list_count);
+               req->mc_tbl_addr = cpu_to_le64(vnic->mc_list_mapping);
+       }
+       req->mask = cpu_to_le32(vnic->rx_mask);
+       return bnge_hwrm_req_send_silent(bd, req);
+}
+
 int bnge_hwrm_vnic_alloc(struct bnge_dev *bd, struct bnge_vnic_info *vnic,
                         unsigned int nr_rings)
 {
index 09517ffb1a2184b7c664fb40633c4973478edc09..042f28e84a05be11c8d32dc962afb7db38561cd5 100644 (file)
@@ -43,6 +43,10 @@ int bnge_hwrm_vnic_alloc(struct bnge_dev *bd, struct bnge_vnic_info *vnic,
 void bnge_hwrm_vnic_free_one(struct bnge_dev *bd, struct bnge_vnic_info *vnic);
 void bnge_hwrm_vnic_ctx_free_one(struct bnge_dev *bd,
                                 struct bnge_vnic_info *vnic, u16 ctx_idx);
+int bnge_hwrm_l2_filter_free(struct bnge_dev *bd, struct bnge_l2_filter *fltr);
+int bnge_hwrm_l2_filter_alloc(struct bnge_dev *bd, struct bnge_l2_filter *fltr);
+int bnge_hwrm_cfa_l2_set_rx_mask(struct bnge_dev *bd,
+                                struct bnge_vnic_info *vnic);
 void bnge_hwrm_stat_ctx_free(struct bnge_net *bn);
 int bnge_hwrm_stat_ctx_alloc(struct bnge_net *bn);
 int hwrm_ring_free_send_msg(struct bnge_net *bn, struct bnge_ring_struct *ring,
index accc62aec66d16d877a30b79a473421767baced6..832eeb960bd2dcc0be225394a933f04c513ccf40 100644 (file)
@@ -1531,6 +1531,230 @@ static int bnge_setup_vnic(struct bnge_net *bn, struct bnge_vnic_info *vnic)
        return rc;
 }
 
+static void bnge_del_l2_filter(struct bnge_net *bn, struct bnge_l2_filter *fltr)
+{
+       if (!refcount_dec_and_test(&fltr->refcnt))
+               return;
+       hlist_del_rcu(&fltr->base.hash);
+       kfree_rcu(fltr, base.rcu);
+}
+
+static void bnge_init_l2_filter(struct bnge_net *bn,
+                               struct bnge_l2_filter *fltr,
+                               struct bnge_l2_key *key, u32 idx)
+{
+       struct hlist_head *head;
+
+       ether_addr_copy(fltr->l2_key.dst_mac_addr, key->dst_mac_addr);
+       fltr->l2_key.vlan = key->vlan;
+       fltr->base.type = BNGE_FLTR_TYPE_L2;
+
+       head = &bn->l2_fltr_hash_tbl[idx];
+       hlist_add_head_rcu(&fltr->base.hash, head);
+       refcount_set(&fltr->refcnt, 1);
+}
+
+static struct bnge_l2_filter *__bnge_lookup_l2_filter(struct bnge_net *bn,
+                                                     struct bnge_l2_key *key,
+                                                     u32 idx)
+{
+       struct bnge_l2_filter *fltr;
+       struct hlist_head *head;
+
+       head = &bn->l2_fltr_hash_tbl[idx];
+       hlist_for_each_entry_rcu(fltr, head, base.hash) {
+               struct bnge_l2_key *l2_key = &fltr->l2_key;
+
+               if (ether_addr_equal(l2_key->dst_mac_addr, key->dst_mac_addr) &&
+                   l2_key->vlan == key->vlan)
+                       return fltr;
+       }
+       return NULL;
+}
+
+static struct bnge_l2_filter *bnge_lookup_l2_filter(struct bnge_net *bn,
+                                                   struct bnge_l2_key *key,
+                                                   u32 idx)
+{
+       struct bnge_l2_filter *fltr;
+
+       rcu_read_lock();
+       fltr = __bnge_lookup_l2_filter(bn, key, idx);
+       if (fltr)
+               refcount_inc(&fltr->refcnt);
+       rcu_read_unlock();
+       return fltr;
+}
+
+static struct bnge_l2_filter *bnge_alloc_l2_filter(struct bnge_net *bn,
+                                                  struct bnge_l2_key *key,
+                                                  gfp_t gfp)
+{
+       struct bnge_l2_filter *fltr;
+       u32 idx;
+
+       idx = jhash2(&key->filter_key, BNGE_L2_KEY_SIZE, bn->hash_seed) &
+             BNGE_L2_FLTR_HASH_MASK;
+       fltr = bnge_lookup_l2_filter(bn, key, idx);
+       if (fltr)
+               return fltr;
+
+       fltr = kzalloc(sizeof(*fltr), gfp);
+       if (!fltr)
+               return ERR_PTR(-ENOMEM);
+
+       bnge_init_l2_filter(bn, fltr, key, idx);
+       return fltr;
+}
+
+static int bnge_hwrm_set_vnic_filter(struct bnge_net *bn, u16 vnic_id, u16 idx,
+                                    const u8 *mac_addr)
+{
+       struct bnge_l2_filter *fltr;
+       struct bnge_l2_key key;
+       int rc;
+
+       ether_addr_copy(key.dst_mac_addr, mac_addr);
+       key.vlan = 0;
+       fltr = bnge_alloc_l2_filter(bn, &key, GFP_KERNEL);
+       if (IS_ERR(fltr))
+               return PTR_ERR(fltr);
+
+       fltr->base.fw_vnic_id = bn->vnic_info[vnic_id].fw_vnic_id;
+       rc = bnge_hwrm_l2_filter_alloc(bn->bd, fltr);
+       if (rc)
+               goto err_del_l2_filter;
+       bn->vnic_info[vnic_id].l2_filters[idx] = fltr;
+       return rc;
+
+err_del_l2_filter:
+       bnge_del_l2_filter(bn, fltr);
+       return rc;
+}
+
+static bool bnge_mc_list_updated(struct bnge_net *bn, u32 *rx_mask)
+{
+       struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT];
+       struct net_device *dev = bn->netdev;
+       struct netdev_hw_addr *ha;
+       int mc_count = 0, off = 0;
+       bool update = false;
+       u8 *haddr;
+
+       netdev_for_each_mc_addr(ha, dev) {
+               if (mc_count >= BNGE_MAX_MC_ADDRS) {
+                       *rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST;
+                       vnic->mc_list_count = 0;
+                       return false;
+               }
+               haddr = ha->addr;
+               if (!ether_addr_equal(haddr, vnic->mc_list + off)) {
+                       memcpy(vnic->mc_list + off, haddr, ETH_ALEN);
+                       update = true;
+               }
+               off += ETH_ALEN;
+               mc_count++;
+       }
+       if (mc_count)
+               *rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_MCAST;
+
+       if (mc_count != vnic->mc_list_count) {
+               vnic->mc_list_count = mc_count;
+               update = true;
+       }
+       return update;
+}
+
+static bool bnge_uc_list_updated(struct bnge_net *bn)
+{
+       struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT];
+       struct net_device *dev = bn->netdev;
+       struct netdev_hw_addr *ha;
+       int off = 0;
+
+       if (netdev_uc_count(dev) != (vnic->uc_filter_count - 1))
+               return true;
+
+       netdev_for_each_uc_addr(ha, dev) {
+               if (!ether_addr_equal(ha->addr, vnic->uc_list + off))
+                       return true;
+
+               off += ETH_ALEN;
+       }
+       return false;
+}
+
+static bool bnge_promisc_ok(struct bnge_net *bn)
+{
+       return true;
+}
+
+static int bnge_cfg_def_vnic(struct bnge_net *bn)
+{
+       struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT];
+       struct net_device *dev = bn->netdev;
+       struct bnge_dev *bd = bn->bd;
+       struct netdev_hw_addr *ha;
+       int i, off = 0, rc;
+       bool uc_update;
+
+       netif_addr_lock_bh(dev);
+       uc_update = bnge_uc_list_updated(bn);
+       netif_addr_unlock_bh(dev);
+
+       if (!uc_update)
+               goto skip_uc;
+
+       for (i = 1; i < vnic->uc_filter_count; i++) {
+               struct bnge_l2_filter *fltr = vnic->l2_filters[i];
+
+               bnge_hwrm_l2_filter_free(bd, fltr);
+               bnge_del_l2_filter(bn, fltr);
+       }
+
+       vnic->uc_filter_count = 1;
+
+       netif_addr_lock_bh(dev);
+       if (netdev_uc_count(dev) > (BNGE_MAX_UC_ADDRS - 1)) {
+               vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS;
+       } else {
+               netdev_for_each_uc_addr(ha, dev) {
+                       memcpy(vnic->uc_list + off, ha->addr, ETH_ALEN);
+                       off += ETH_ALEN;
+                       vnic->uc_filter_count++;
+               }
+       }
+       netif_addr_unlock_bh(dev);
+
+       for (i = 1, off = 0; i < vnic->uc_filter_count; i++, off += ETH_ALEN) {
+               rc = bnge_hwrm_set_vnic_filter(bn, 0, i, vnic->uc_list + off);
+               if (rc) {
+                       netdev_err(dev, "HWRM vnic filter failure rc: %d\n", rc);
+                       vnic->uc_filter_count = i;
+                       return rc;
+               }
+       }
+
+skip_uc:
+       if ((vnic->rx_mask & CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS) &&
+           !bnge_promisc_ok(bn))
+               vnic->rx_mask &= ~CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS;
+       rc = bnge_hwrm_cfa_l2_set_rx_mask(bd, vnic);
+       if (rc && (vnic->rx_mask & CFA_L2_SET_RX_MASK_REQ_MASK_MCAST)) {
+               netdev_info(dev, "Failed setting MC filters rc: %d, turning on ALL_MCAST mode\n",
+                           rc);
+               vnic->rx_mask &= ~CFA_L2_SET_RX_MASK_REQ_MASK_MCAST;
+               vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST;
+               vnic->mc_list_count = 0;
+               rc = bnge_hwrm_cfa_l2_set_rx_mask(bd, vnic);
+       }
+       if (rc)
+               netdev_err(dev, "HWRM cfa l2 rx mask failure rc: %d\n",
+                          rc);
+
+       return rc;
+}
+
 static void bnge_hwrm_vnic_free(struct bnge_net *bn)
 {
        int i;
@@ -1554,8 +1778,24 @@ static void bnge_hwrm_vnic_ctx_free(struct bnge_net *bn)
        bn->rsscos_nr_ctxs = 0;
 }
 
+static void bnge_hwrm_clear_vnic_filter(struct bnge_net *bn)
+{
+       struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT];
+       int i;
+
+       for (i = 0; i < vnic->uc_filter_count; i++) {
+               struct bnge_l2_filter *fltr = vnic->l2_filters[i];
+
+               bnge_hwrm_l2_filter_free(bn->bd, fltr);
+               bnge_del_l2_filter(bn, fltr);
+       }
+
+       vnic->uc_filter_count = 0;
+}
+
 static void bnge_clear_vnic(struct bnge_net *bn)
 {
+       bnge_hwrm_clear_vnic_filter(bn);
        bnge_hwrm_vnic_free(bn);
        bnge_hwrm_vnic_ctx_free(bn);
 }
@@ -1808,6 +2048,36 @@ static int bnge_init_chip(struct bnge_net *bn)
 
        if (bd->rss_cap & BNGE_RSS_CAP_RSS_HASH_TYPE_DELTA)
                bnge_hwrm_update_rss_hash_cfg(bn);
+
+       /* Filter for default vnic 0 */
+       rc = bnge_hwrm_set_vnic_filter(bn, 0, 0, bn->netdev->dev_addr);
+       if (rc) {
+               netdev_err(bn->netdev, "HWRM vnic filter failure rc: %d\n", rc);
+               goto err_out;
+       }
+       vnic->uc_filter_count = 1;
+
+       vnic->rx_mask = 0;
+
+       if (bn->netdev->flags & IFF_BROADCAST)
+               vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_BCAST;
+
+       if (bn->netdev->flags & IFF_PROMISC)
+               vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS;
+
+       if (bn->netdev->flags & IFF_ALLMULTI) {
+               vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST;
+               vnic->mc_list_count = 0;
+       } else if (bn->netdev->flags & IFF_MULTICAST) {
+               u32 mask = 0;
+
+               bnge_mc_list_updated(bn, &mask);
+               vnic->rx_mask |= mask;
+       }
+
+       rc = bnge_cfg_def_vnic(bn);
+       if (rc)
+               goto err_out;
        return 0;
 
 err_out:
index 53979914c512bfdcdffab485784f8ef4dcb46e8c..fb3b961536ba7b1922f1cac45b70b703609a2a6c 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/bnxt/hsi.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/refcount.h>
 #include "bnge_db.h"
 
 struct tx_bd {
@@ -383,6 +384,9 @@ struct bnge_vnic_info {
 #define BNGE_MAX_CTX_PER_VNIC  8
        u16             fw_rss_cos_lb_ctx[BNGE_MAX_CTX_PER_VNIC];
        u16             mru;
+       /* index 0 always dev_addr */
+       struct bnge_l2_filter *l2_filters[BNGE_MAX_UC_ADDRS];
+       u16             uc_filter_count;
        u8              *uc_list;
        dma_addr_t      rss_table_dma_addr;
        __le16          *rss_table;
@@ -394,6 +398,7 @@ struct bnge_vnic_info {
 #define BNGE_RSS_TABLE_MAX_TBL         8
 #define BNGE_MAX_RSS_TABLE_SIZE                        \
        (BNGE_RSS_TABLE_SIZE * BNGE_RSS_TABLE_MAX_TBL)
+       u32             rx_mask;
 
        u8              *mc_list;
        int             mc_list_size;
@@ -408,6 +413,41 @@ struct bnge_vnic_info {
        u32             vnic_id;
 };
 
+struct bnge_filter_base {
+       struct hlist_node       hash;
+       struct list_head        list;
+       __le64                  filter_id;
+       u8                      type;
+#define BNGE_FLTR_TYPE_L2      2
+       u8                      flags;
+       u16                     rxq;
+       u16                     fw_vnic_id;
+       u16                     vf_idx;
+       unsigned long           state;
+#define BNGE_FLTR_VALID                0
+#define BNGE_FLTR_FW_DELETED   2
+
+       struct rcu_head         rcu;
+};
+
+struct bnge_l2_key {
+       union {
+               struct {
+                       u8      dst_mac_addr[ETH_ALEN];
+                       u16     vlan;
+               };
+               u32     filter_key;
+       };
+};
+
+#define BNGE_L2_KEY_SIZE       (sizeof(struct bnge_l2_key) / 4)
+struct bnge_l2_filter {
+       /* base filter must be the first member */
+       struct bnge_filter_base base;
+       struct bnge_l2_key      l2_key;
+       refcount_t              refcnt;
+};
+
 u16 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr);
 u16 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr);
 void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic);