Commit 1cdab773 authored by Junxin Chen's avatar Junxin Chen Committed by Fengyan
Browse files

UNIC: Supports query, configuration, and management of IP entry

driver inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I850RQ


CVE: NA

----------------------------------------------------------------

When the driver runs in UB mode, it should support get ipv4/6
configuration information from user and configure it to hardware.
As network layer of UNIC, the driver is supposed to deliver IPv4/v6
address to the hardware filtering distribution table which is used to
filter and deliver packets by searching when receive packets.

This patch adds the interface for configuring ip address to hardware
by users through kernel and supports driver IP table management in UB
mode, which means physical function and virtual function are both able
to set IP address to intercept from other source IP address. The
driver do the table management after initialization. The table
size depends on the hardware and will be divided to private table
size of every function and share table size which can be used by
all functions.

Signed-off-by: default avatarFengyan Mu <mufengyan@hisilicon.com>
Signed-off-by: default avatarHaibin Lu <luhaibin10@hisilicon.com>
Signed-off-by: default avatarJunxin Chen <chenjunxin1@huawei.com>
parent f9c29387
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -23,7 +23,8 @@ obj-$(CONFIG_HNS3_HCLGEVF) += hclgevf.o
hclgevf-objs = hns3vf/hclgevf_main.o hns3vf/hclgevf_mbx.o  hns3vf/hclgevf_devlink.o hns3vf/hclgevf_regs.o \
		hns3_common/hclge_comm_cmd.o hns3_common/hclge_comm_rss.o hns3_common/hclge_comm_tqp_stats.o \
		hns3vf/hclgevf_udma.o
hclgevf-$(CONFIG_HNS3_UBL) += hns3_common/hclge_comm_unic_addr.o hns3vf/hclgevf_unic_guid.o
hclgevf-$(CONFIG_HNS3_UBL) += hns3_common/hclge_comm_unic_addr.o hns3vf/hclgevf_unic_ip.o hns3vf/hclgevf_unic_guid.o \
				hns3vf/hclgevf_unic_addr.o

obj-$(CONFIG_HNS3_HCLGE) += hclge.o
hclge-objs = hns3pf/hclge_main.o hns3pf/hclge_mdio.o hns3pf/hclge_tm.o hns3pf/hclge_sysfs.o hns3pf/hclge_regs.o \
@@ -32,5 +33,6 @@ hclge-objs = hns3pf/hclge_main.o hns3pf/hclge_mdio.o hns3pf/hclge_tm.o hns3pf/hc
		hns3pf/hclge_udma.o
hclge-objs += hns3pf/hclge_ext.o

hclge-$(CONFIG_HNS3_UBL) += hns3_common/hclge_comm_unic_addr.o hns3pf/hclge_unic_guid.o
hclge-$(CONFIG_HNS3_UBL) += hns3_common/hclge_comm_unic_addr.o hns3pf/hclge_unic_ip.o hns3pf/hclge_unic_guid.o \
			hns3pf/hclge_unic_addr.o
hclge-$(CONFIG_HNS3_DCB) += hns3pf/hclge_dcb.o
+8 −0
Original line number Diff line number Diff line
@@ -50,6 +50,8 @@ enum HCLGE_MBX_OPCODE {
	HCLGE_MBX_SET_QB = 0x28,        /* (VF -> PF) set queue bonding */
	HCLGE_MBX_PUSH_QB_STATE,        /* (PF -> VF) push qb state */

	HCLGE_UNIC_MBX_SET_IP = 0x51,	/* (VF -> PF) set ip addr */

	HCLGE_MBX_GET_VF_FLR_STATUS = 200, /* (M7 -> PF) get vf flr status */
	HCLGE_MBX_PUSH_LINK_STATUS,	/* (M7 -> PF) get port link status */
	HCLGE_MBX_NCSI_ERROR,		/* (M7 -> PF) receive a NCSI error */
@@ -65,6 +67,12 @@ enum hclge_mbx_mac_vlan_subcode {
	HCLGE_MBX_MAC_VLAN_MC_REMOVE,		/* remove MC mac addr */
};

/* below are per-VF ip table subcodes */
enum hclge_mbx_ip_table_subcode {
	HCLGE_UNIC_MBX_IP_TABLE_ADD = 0,		/* add ip addr */
	HCLGE_UNIC_MBX_IP_TABLE_REMOVE,			/* remove ip addr */
};

/* below are per-VF vlan cfg subcodes */
enum hclge_mbx_vlan_cfg_subcode {
	HCLGE_MBX_VLAN_FILTER = 0,	/* set vlan filter */
+12 −0
Original line number Diff line number Diff line
@@ -428,6 +428,7 @@ struct hnae3_dev_specs {
	u32 mac_stats_num;
	u8 tnl_num;
	u16 guid_tbl_space;
	u16 ip_tbl_space;
};

struct hnae3_client_ops {
@@ -462,6 +463,11 @@ struct hnae3_ae_dev {
	void *priv;
};

enum hnae3_unic_addr_type {
	HNAE3_UNIC_IP_ADDR,
	HNAE3_UNIC_MCGUID_ADDR
};

/* This struct defines the operation on the handle.
 *
 * init_ae_dev(): (mandatory)
@@ -827,6 +833,12 @@ struct hnae3_ae_ops {
		       struct ethtool_wolinfo *wol);
	int (*priv_ops)(struct hnae3_handle *handle, int opcode,
			void *data, size_t length);
	int (*add_addr)(struct hnae3_handle *handle,
			const unsigned char *addr,
			enum hnae3_unic_addr_type addr_type);
	int (*rm_addr)(struct hnae3_handle *handle,
		       const unsigned char *addr,
		       enum hnae3_unic_addr_type addr_type);
	int (*get_func_guid)(struct hnae3_handle *handle, u8 *guid);
	int (*set_func_guid)(struct hnae3_handle *handle, u8 *guid);
};
+2 −0
Original line number Diff line number Diff line
@@ -313,6 +313,8 @@ enum hclge_opcode_type {

	/* UB commands */
	HCLGE_OPC_COMM_GET_FUNC_GUID	= 0xA001,
	HCLGE_OPC_ADD_IP_TBL		= 0xA100,
	HCLGE_OPC_DEL_IP_TBL		= 0xA101,
	HCLGE_OPC_COMM_CFG_FUNC_GUID	= 0xA122,
};

+226 −0
Original line number Diff line number Diff line
@@ -14,6 +14,232 @@

#include "hclge_comm_unic_addr.h"

static struct hclge_comm_unic_addr_node *
hclge_comm_unic_find_addr_node(struct list_head *list, const u8 *addr)
{
	struct hclge_comm_unic_addr_node *addr_node, *tmp;

	list_for_each_entry_safe(addr_node, tmp, list, node)
		if (hclge_comm_unic_addr_equal((const u8 *)&addr_node->unic_addr,
					       addr))
			return addr_node;

	return NULL;
}

static void
hclge_comm_unic_update_addr_node(struct hclge_comm_unic_addr_node *addr_node,
				 enum HCLGE_COMM_ADDR_NODE_STATE state)
{
	switch (state) {
	/* from set_rx_mode or tmp_add_list */
	case HCLGE_COMM_UNIC_ADDR_TO_ADD:
		if (addr_node->state == HCLGE_COMM_UNIC_ADDR_TO_DEL)
			addr_node->state = HCLGE_COMM_UNIC_ADDR_ACTIVE;
		break;
	/* only from set_rx_mode */
	case HCLGE_COMM_UNIC_ADDR_TO_DEL:
		if (addr_node->state == HCLGE_COMM_UNIC_ADDR_TO_ADD) {
			list_del(&addr_node->node);
			kfree(addr_node);
		} else {
			addr_node->state = HCLGE_COMM_UNIC_ADDR_TO_DEL;
		}
		break;
	/* only from tmp_add_list, the addr_node->state won't be
	 * ACTIVE.
	 */
	case HCLGE_COMM_UNIC_ADDR_ACTIVE:
		if (addr_node->state == HCLGE_COMM_UNIC_ADDR_TO_ADD)
			addr_node->state = HCLGE_COMM_UNIC_ADDR_ACTIVE;
		break;
	}
}

void hclge_comm_unic_sync_from_addr_del_list(struct list_head *del_list,
					     struct list_head *addr_list)
{
	struct hclge_comm_unic_addr_node *addr_node, *tmp, *new_node;

	list_for_each_entry_safe(addr_node, tmp, del_list, node) {
		new_node = hclge_comm_unic_find_addr_node(addr_list,
							  addr_node->unic_addr);
		if (new_node) {
			/* If the addr exists in the addr list, it means
			 * received a new TO_ADD request during the time window
			 * of configuring the addr, so we just need
			 * to change the addr node state to ACTIVE.
			 */
			new_node->state = HCLGE_COMM_UNIC_ADDR_ACTIVE;
			list_del(&addr_node->node);
			kfree(addr_node);
		} else {
			list_move_tail(&addr_node->node, addr_list);
		}
	}
}

static void
hclge_comm_unic_sync_from_addr_add_list(struct list_head *add_list,
					struct list_head *addr_list,
					bool *all_added)
{
	struct hclge_comm_unic_addr_node *addr_node, *tmp, *new_node;

	list_for_each_entry_safe(addr_node, tmp, add_list, node) {
		if (*all_added &&
		    addr_node->state == HCLGE_COMM_UNIC_ADDR_TO_ADD)
			*all_added = false;

		/* If the addr from tmp_add_list is not in the
		 * addr_list, it means have received a TO_DEL request
		 * during the time window of adding the addr. If addr_node
		 * state is ACTIVE, then change its state to TO_DEL,
		 * then it will be removed at next time. If is TO_ADD,
		 * it means this address hasn't been added successfully,
		 * so just remove the addr node.
		 */
		new_node = hclge_comm_unic_find_addr_node(addr_list,
							  addr_node->unic_addr);
		if (new_node) {
			hclge_comm_unic_update_addr_node(new_node,
							 addr_node->state);
			list_del(&addr_node->node);
			kfree(addr_node);
		} else if (addr_node->state == HCLGE_COMM_UNIC_ADDR_ACTIVE) {
			addr_node->state = HCLGE_COMM_UNIC_ADDR_TO_DEL;
			list_move_tail(&addr_node->node, addr_list);
		} else {
			list_del(&addr_node->node);
			kfree(addr_node);
		}
	}
}

int hclge_comm_unic_update_addr_list(struct list_head *list,
				     spinlock_t *addr_list_lock,
				     enum HCLGE_COMM_ADDR_NODE_STATE state,
				     const unsigned char *addr)
{
	struct hclge_comm_unic_addr_node *addr_node;

	spin_lock_bh(addr_list_lock);

	/* if the addr is already in the addr list, no need to add a new
	 * one into it, just check the addr state, convert it to a new
	 * state, or just remove it, or do nothing.
	 */
	addr_node = hclge_comm_unic_find_addr_node(list, addr);
	if (addr_node) {
		hclge_comm_unic_update_addr_node(addr_node, state);
		spin_unlock_bh(addr_list_lock);
		return 0;
	}

	/* if this addr is never added, unnecessary to delete */
	if (state == HCLGE_COMM_UNIC_ADDR_TO_DEL) {
		spin_unlock_bh(addr_list_lock);
		return -ENOENT;
	}

	addr_node = kzalloc(sizeof(*addr_node), GFP_ATOMIC);
	if (!addr_node) {
		spin_unlock_bh(addr_list_lock);
		return -ENOMEM;
	}

	addr_node->state = state;
	memcpy(addr_node->unic_addr, addr, UNIC_ADDR_LEN);
	list_add_tail(&addr_node->node, list);

	spin_unlock_bh(addr_list_lock);

	return 0;
}

bool hclge_comm_unic_sync_addr_table(struct hnae3_handle *handle,
				     struct list_head *list,
				     spinlock_t *addr_list_lock,
				     void (*sync)(struct hnae3_handle *,
						  struct list_head *),
				     void (*unsync)(struct hnae3_handle *,
						    struct list_head *))
{
	struct hclge_comm_unic_addr_node *addr_node, *tmp, *new_node;
	struct list_head tmp_add_list, tmp_del_list;
	bool all_added = true;

	INIT_LIST_HEAD(&tmp_add_list);
	INIT_LIST_HEAD(&tmp_del_list);

	/* move the addr to the tmp_add_list and tmp_del_list, then
	 * we can add/delete these addr outside the spin lock
	 */
	spin_lock_bh(addr_list_lock);

	list_for_each_entry_safe(addr_node, tmp, list, node) {
		switch (addr_node->state) {
		case HCLGE_COMM_UNIC_ADDR_TO_DEL:
			list_move_tail(&addr_node->node, &tmp_del_list);
			break;
		case HCLGE_COMM_UNIC_ADDR_TO_ADD:
			new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC);
			if (!new_node)
				goto stop_traverse;
			memcpy(new_node->unic_addr, addr_node->unic_addr,
			       UNIC_ADDR_LEN);
			new_node->state = addr_node->state;
			list_add_tail(&new_node->node, &tmp_add_list);
			break;
		default:
			break;
		}
	}

stop_traverse:
	spin_unlock_bh(addr_list_lock);

	/* delete first, in order to get max addr table space for adding */
	if (unsync)
		unsync(handle, &tmp_del_list);
	if (sync)
		sync(handle, &tmp_add_list);

	/* if some addr were added/deleted fail, move back to the
	 * addr_list, and retry at next time.
	 */
	spin_lock_bh(addr_list_lock);

	hclge_comm_unic_sync_from_addr_del_list(&tmp_del_list, list);
	hclge_comm_unic_sync_from_addr_add_list(&tmp_add_list, list,
						&all_added);

	spin_unlock_bh(addr_list_lock);

	return all_added;
}

int hclge_comm_unic_convert_ip_addr(const struct sockaddr *addr,
				    struct in6_addr *ip_addr)
{
	__be32 v4addr;

	switch (addr->sa_family) {
	case AF_INET:
		/* we transform ipv4 addr to ipv6 addr for later configuring */
		v4addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
		ipv6_addr_set_v4mapped(v4addr, ip_addr);
		break;
	case AF_INET6:
		memcpy(ip_addr, &((struct sockaddr_in6 *)addr)->sin6_addr,
		       sizeof(struct in6_addr));
		break;
	default:
		return -EINVAL;
	}
	return 0;
}

static void
hclge_comm_unic_func_guid_cmd_prepare(u8 *guid,
				      struct hclge_comm_func_guid_cmd *req)
Loading