Commit 9afad4a1 authored by Bhargava Marreddy's avatar Bhargava Marreddy Committed by Jakub Kicinski
Browse files

bng_en: Configure default VNIC



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: default avatarBhargava Marreddy <bhargava.marreddy@broadcom.com>
Reviewed-by: default avatarVikas Gupta <vikas.gupta@broadcom.com>
Reviewed-by: default avatarRajashekar Hudumula <rajashekar.hudumula@broadcom.com>
Link: https://patch.msgid.link/20250919174742.24969-11-bhargava.marreddy@broadcom.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 58930c03
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
@@ -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)
{
+4 −0
Original line number Diff line number Diff line
@@ -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,
+270 −0
Original line number Diff line number Diff line
@@ -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:
+40 −0
Original line number Diff line number Diff line
@@ -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);