Commit 389cc2f3 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'devlink-Support-get-set-mac-address-of-a-port-function'

Parav Pandit says:

====================
devlink: Support get,set mac address of a port function

Currently, ip link set dev <pfndev> vf <vf_num> <param> <value> has
below few limitations.

1. Command is limited to set VF parameters only.
It cannot set the default MAC address for the PCI PF.

2. It can be set only on system where PCI SR-IOV capability exists.
In smartnic based system, eswitch of a NIC resides on a different
embedded cpu which has the VF and PF representors for the SR-IOV
functions of a host system in which this smartnic is plugged-in.

3. It cannot setup the function attributes of sub-function described
in detail in comprehensive RFC [1] and [2].

This series covers the first small part to let user query and set MAC
address (hardware address) of a PCI PF/VF which is represented by
devlink port pcipf, pcivf port flavours respectively.

Whenever a devlink port manages a function connected to a devlink port,
it allows to query and set its hardware address.

Driver implements necessary get/set callback functions if it supports
port function for a given port type.

Patch summary:
Patch-1 Prepares devlink port fill routines for extack
Patch-2 and 3 extended devlink interface to get/set port function
attributes, mainly hardware address to start with.

Patch-2 Extended port dump command to query port function hardware
address
Patch-3 Introduces a command to set the hardware address of a port
function

Patch-4 to 9 refactors and implement devlink callbacks in mlx5_core
driver.
Patch-4 Constify the mac address pointer in set routines
Patch-5 Introduces eswich check helper to use in devlink facing
callbacks
Patch-6 Moves port index, port number conversion routine to eswitch
header file
Patch-7 Implements port function query devlink callback
Patch-8 Refactors mac address setting routine to uniformly use
state_lock
Patch-9 Implements port function set devlink callback

[1] https://lore.kernel.org/netdev/20200519092258.GF4655@nanopsycho/
[2] https://marc.info/?l=linux-netdev&m=158555928517777&w=2


====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 29a720c1 330077d1
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -113,6 +113,8 @@ static const struct devlink_ops mlx5_devlink_ops = {
	.eswitch_inline_mode_get = mlx5_devlink_eswitch_inline_mode_get,
	.eswitch_inline_mode_get = mlx5_devlink_eswitch_inline_mode_get,
	.eswitch_encap_mode_set = mlx5_devlink_eswitch_encap_mode_set,
	.eswitch_encap_mode_set = mlx5_devlink_eswitch_encap_mode_set,
	.eswitch_encap_mode_get = mlx5_devlink_eswitch_encap_mode_get,
	.eswitch_encap_mode_get = mlx5_devlink_eswitch_encap_mode_get,
	.port_function_hw_addr_get = mlx5_devlink_port_function_hw_addr_get,
	.port_function_hw_addr_set = mlx5_devlink_port_function_hw_addr_set,
#endif
#endif
	.flash_update = mlx5_devlink_flash_update,
	.flash_update = mlx5_devlink_flash_update,
	.info_get = mlx5_devlink_info_get,
	.info_get = mlx5_devlink_info_get,
+1 −7
Original line number Original line Diff line number Diff line
@@ -1181,12 +1181,6 @@ is_devlink_port_supported(const struct mlx5_core_dev *dev,
	       mlx5_eswitch_is_vf_vport(dev->priv.eswitch, rpriv->rep->vport);
	       mlx5_eswitch_is_vf_vport(dev->priv.eswitch, rpriv->rep->vport);
}
}


static unsigned int
vport_to_devlink_port_index(const struct mlx5_core_dev *dev, u16 vport_num)
{
	return (MLX5_CAP_GEN(dev, vhca_id) << 16) | vport_num;
}

static int register_devlink_port(struct mlx5_core_dev *dev,
static int register_devlink_port(struct mlx5_core_dev *dev,
				 struct mlx5e_rep_priv *rpriv)
				 struct mlx5e_rep_priv *rpriv)
{
{
@@ -1200,7 +1194,7 @@ static int register_devlink_port(struct mlx5_core_dev *dev,
		return 0;
		return 0;


	mlx5e_rep_get_port_parent_id(rpriv->netdev, &ppid);
	mlx5e_rep_get_port_parent_id(rpriv->netdev, &ppid);
	dl_port_index = vport_to_devlink_port_index(dev, rep->vport);
	dl_port_index = mlx5_esw_vport_to_devlink_port_index(dev, rep->vport);
	pfnum = PCI_FUNC(dev->pdev->devfn);
	pfnum = PCI_FUNC(dev->pdev->devfn);


	if (rep->vport == MLX5_VPORT_UPLINK)
	if (rep->vport == MLX5_VPORT_UPLINK)
+127 −15
Original line number Original line Diff line number Diff line
@@ -63,6 +63,29 @@ struct vport_addr {
static void esw_destroy_legacy_fdb_table(struct mlx5_eswitch *esw);
static void esw_destroy_legacy_fdb_table(struct mlx5_eswitch *esw);
static void esw_cleanup_vepa_rules(struct mlx5_eswitch *esw);
static void esw_cleanup_vepa_rules(struct mlx5_eswitch *esw);


static int mlx5_eswitch_check(const struct mlx5_core_dev *dev)
{
	if (MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
		return -EOPNOTSUPP;

	if (!MLX5_ESWITCH_MANAGER(dev))
		return -EPERM;

	return 0;
}

struct mlx5_eswitch *mlx5_devlink_eswitch_get(struct devlink *devlink)
{
	struct mlx5_core_dev *dev = devlink_priv(devlink);
	int err;

	err = mlx5_eswitch_check(dev);
	if (err)
		return ERR_PTR(err);

	return dev->priv.eswitch;
}

struct mlx5_vport *__must_check
struct mlx5_vport *__must_check
mlx5_eswitch_get_vport(struct mlx5_eswitch *esw, u16 vport_num)
mlx5_eswitch_get_vport(struct mlx5_eswitch *esw, u16 vport_num)
{
{
@@ -1127,7 +1150,7 @@ int mlx5_esw_modify_vport_rate(struct mlx5_eswitch *esw, u16 vport_num,
						  MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_MAX_AVERAGE_BW);
						  MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_MAX_AVERAGE_BW);
}
}


static void node_guid_gen_from_mac(u64 *node_guid, u8 mac[ETH_ALEN])
static void node_guid_gen_from_mac(u64 *node_guid, const u8 *mac)
{
{
	((u8 *)node_guid)[7] = mac[0];
	((u8 *)node_guid)[7] = mac[0];
	((u8 *)node_guid)[6] = mac[1];
	((u8 *)node_guid)[6] = mac[1];
@@ -1778,46 +1801,135 @@ void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw)
}
}


/* Vport Administration */
/* Vport Administration */
int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
static int
			       u16 vport, u8 mac[ETH_ALEN])
mlx5_esw_set_vport_mac_locked(struct mlx5_eswitch *esw,
			      struct mlx5_vport *evport, const u8 *mac)
{
{
	struct mlx5_vport *evport = mlx5_eswitch_get_vport(esw, vport);
	u16 vport_num = evport->vport;
	u64 node_guid;
	u64 node_guid;
	int err = 0;
	int err = 0;


	if (IS_ERR(evport))
		return PTR_ERR(evport);
	if (is_multicast_ether_addr(mac))
	if (is_multicast_ether_addr(mac))
		return -EINVAL;
		return -EINVAL;


	mutex_lock(&esw->state_lock);

	if (evport->info.spoofchk && !is_valid_ether_addr(mac))
	if (evport->info.spoofchk && !is_valid_ether_addr(mac))
		mlx5_core_warn(esw->dev,
		mlx5_core_warn(esw->dev,
			       "Set invalid MAC while spoofchk is on, vport(%d)\n",
			       "Set invalid MAC while spoofchk is on, vport(%d)\n",
			       vport);
			       vport_num);


	err = mlx5_modify_nic_vport_mac_address(esw->dev, vport, mac);
	err = mlx5_modify_nic_vport_mac_address(esw->dev, vport_num, mac);
	if (err) {
	if (err) {
		mlx5_core_warn(esw->dev,
		mlx5_core_warn(esw->dev,
			       "Failed to mlx5_modify_nic_vport_mac vport(%d) err=(%d)\n",
			       "Failed to mlx5_modify_nic_vport_mac vport(%d) err=(%d)\n",
			       vport, err);
			       vport_num, err);
		goto unlock;
		return err;
	}
	}


	node_guid_gen_from_mac(&node_guid, mac);
	node_guid_gen_from_mac(&node_guid, mac);
	err = mlx5_modify_nic_vport_node_guid(esw->dev, vport, node_guid);
	err = mlx5_modify_nic_vport_node_guid(esw->dev, vport_num, node_guid);
	if (err)
	if (err)
		mlx5_core_warn(esw->dev,
		mlx5_core_warn(esw->dev,
			       "Failed to set vport %d node guid, err = %d. RDMA_CM will not function properly for this VF.\n",
			       "Failed to set vport %d node guid, err = %d. RDMA_CM will not function properly for this VF.\n",
			       vport, err);
			       vport_num, err);


	ether_addr_copy(evport->info.mac, mac);
	ether_addr_copy(evport->info.mac, mac);
	evport->info.node_guid = node_guid;
	evport->info.node_guid = node_guid;
	if (evport->enabled && esw->mode == MLX5_ESWITCH_LEGACY)
	if (evport->enabled && esw->mode == MLX5_ESWITCH_LEGACY)
		err = esw_acl_ingress_lgcy_setup(esw, evport);
		err = esw_acl_ingress_lgcy_setup(esw, evport);


unlock:
	return err;
}

int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
			       u16 vport, const u8 *mac)
{
	struct mlx5_vport *evport = mlx5_eswitch_get_vport(esw, vport);
	int err = 0;

	if (IS_ERR(evport))
		return PTR_ERR(evport);

	mutex_lock(&esw->state_lock);
	err = mlx5_esw_set_vport_mac_locked(esw, evport, mac);
	mutex_unlock(&esw->state_lock);
	return err;
}

static bool
is_port_function_supported(const struct mlx5_eswitch *esw, u16 vport_num)
{
	return vport_num == MLX5_VPORT_PF ||
	       mlx5_eswitch_is_vf_vport(esw, vport_num);
}

int mlx5_devlink_port_function_hw_addr_get(struct devlink *devlink,
					   struct devlink_port *port,
					   u8 *hw_addr, int *hw_addr_len,
					   struct netlink_ext_ack *extack)
{
	struct mlx5_eswitch *esw;
	struct mlx5_vport *vport;
	int err = -EOPNOTSUPP;
	u16 vport_num;

	esw = mlx5_devlink_eswitch_get(devlink);
	if (IS_ERR(esw))
		return PTR_ERR(esw);

	vport_num = mlx5_esw_devlink_port_index_to_vport_num(port->index);
	if (!is_port_function_supported(esw, vport_num))
		return -EOPNOTSUPP;

	vport = mlx5_eswitch_get_vport(esw, vport_num);
	if (IS_ERR(vport)) {
		NL_SET_ERR_MSG_MOD(extack, "Invalid port");
		return PTR_ERR(vport);
	}

	mutex_lock(&esw->state_lock);
	if (vport->enabled) {
		ether_addr_copy(hw_addr, vport->info.mac);
		*hw_addr_len = ETH_ALEN;
		err = 0;
	} else {
		NL_SET_ERR_MSG_MOD(extack, "Eswitch vport is disabled");
	}
	mutex_unlock(&esw->state_lock);
	return err;
}

int mlx5_devlink_port_function_hw_addr_set(struct devlink *devlink,
					   struct devlink_port *port,
					   const u8 *hw_addr, int hw_addr_len,
					   struct netlink_ext_ack *extack)
{
	struct mlx5_eswitch *esw;
	struct mlx5_vport *vport;
	int err = -EOPNOTSUPP;
	u16 vport_num;

	esw = mlx5_devlink_eswitch_get(devlink);
	if (IS_ERR(esw)) {
		NL_SET_ERR_MSG_MOD(extack, "Eswitch doesn't support set hw_addr");
		return PTR_ERR(esw);
	}

	vport_num = mlx5_esw_devlink_port_index_to_vport_num(port->index);
	if (!is_port_function_supported(esw, vport_num)) {
		NL_SET_ERR_MSG_MOD(extack, "Port doesn't support set hw_addr");
		return -EINVAL;
	}
	vport = mlx5_eswitch_get_vport(esw, vport_num);
	if (IS_ERR(vport)) {
		NL_SET_ERR_MSG_MOD(extack, "Invalid port");
		return PTR_ERR(vport);
	}

	mutex_lock(&esw->state_lock);
	if (vport->enabled)
		err = mlx5_esw_set_vport_mac_locked(esw, vport, hw_addr);
	else
		NL_SET_ERR_MSG_MOD(extack, "Eswitch vport is disabled");
	mutex_unlock(&esw->state_lock);
	mutex_unlock(&esw->state_lock);
	return err;
	return err;
}
}
+24 −1
Original line number Original line Diff line number Diff line
@@ -311,7 +311,7 @@ int mlx5_eswitch_enable(struct mlx5_eswitch *esw, int num_vfs);
void mlx5_eswitch_disable_locked(struct mlx5_eswitch *esw, bool clear_vf);
void mlx5_eswitch_disable_locked(struct mlx5_eswitch *esw, bool clear_vf);
void mlx5_eswitch_disable(struct mlx5_eswitch *esw, bool clear_vf);
void mlx5_eswitch_disable(struct mlx5_eswitch *esw, bool clear_vf);
int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
			       u16 vport, u8 mac[ETH_ALEN]);
			       u16 vport, const u8 *mac);
int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw,
int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw,
				 u16 vport, int link_state);
				 u16 vport, int link_state);
int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
@@ -450,6 +450,15 @@ int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink,
					struct netlink_ext_ack *extack);
					struct netlink_ext_ack *extack);
int mlx5_devlink_eswitch_encap_mode_get(struct devlink *devlink,
int mlx5_devlink_eswitch_encap_mode_get(struct devlink *devlink,
					enum devlink_eswitch_encap_mode *encap);
					enum devlink_eswitch_encap_mode *encap);
int mlx5_devlink_port_function_hw_addr_get(struct devlink *devlink,
					   struct devlink_port *port,
					   u8 *hw_addr, int *hw_addr_len,
					   struct netlink_ext_ack *extack);
int mlx5_devlink_port_function_hw_addr_set(struct devlink *devlink,
					   struct devlink_port *port,
					   const u8 *hw_addr, int hw_addr_len,
					   struct netlink_ext_ack *extack);

void *mlx5_eswitch_get_uplink_priv(struct mlx5_eswitch *esw, u8 rep_type);
void *mlx5_eswitch_get_uplink_priv(struct mlx5_eswitch *esw, u8 rep_type);


int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw,
int mlx5_eswitch_add_vlan_action(struct mlx5_eswitch *esw,
@@ -565,6 +574,19 @@ static inline u16 mlx5_eswitch_index_to_vport_num(struct mlx5_eswitch *esw,
	return index;
	return index;
}
}


static inline unsigned int
mlx5_esw_vport_to_devlink_port_index(const struct mlx5_core_dev *dev,
				     u16 vport_num)
{
	return (MLX5_CAP_GEN(dev, vhca_id) << 16) | vport_num;
}

static inline u16
mlx5_esw_devlink_port_index_to_vport_num(unsigned int dl_port_index)
{
	return dl_port_index & 0xffff;
}

/* TODO: This mlx5e_tc function shouldn't be called by eswitch */
/* TODO: This mlx5e_tc function shouldn't be called by eswitch */
void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw);
void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw);


@@ -634,6 +656,7 @@ void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw);
	for ((vport) = (nvfs);						\
	for ((vport) = (nvfs);						\
	     (vport) >= (esw)->first_host_vport; (vport)--)
	     (vport) >= (esw)->first_host_vport; (vport)--)


struct mlx5_eswitch *mlx5_devlink_eswitch_get(struct devlink *devlink);
struct mlx5_vport *__must_check
struct mlx5_vport *__must_check
mlx5_eswitch_get_vport(struct mlx5_eswitch *esw, u16 vport_num);
mlx5_eswitch_get_vport(struct mlx5_eswitch *esw, u16 vport_num);


+26 −40
Original line number Original line Diff line number Diff line
@@ -2279,17 +2279,6 @@ static int esw_inline_mode_to_devlink(u8 mlx5_mode, u8 *mode)
	return 0;
	return 0;
}
}


static int mlx5_eswitch_check(const struct mlx5_core_dev *dev)
{
	if (MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
		return -EOPNOTSUPP;

	if(!MLX5_ESWITCH_MANAGER(dev))
		return -EPERM;

	return 0;
}

static int eswitch_devlink_esw_mode_check(const struct mlx5_eswitch *esw)
static int eswitch_devlink_esw_mode_check(const struct mlx5_eswitch *esw)
{
{
	/* devlink commands in NONE eswitch mode are currently supported only
	/* devlink commands in NONE eswitch mode are currently supported only
@@ -2302,14 +2291,13 @@ static int eswitch_devlink_esw_mode_check(const struct mlx5_eswitch *esw)
int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
				  struct netlink_ext_ack *extack)
				  struct netlink_ext_ack *extack)
{
{
	struct mlx5_core_dev *dev = devlink_priv(devlink);
	struct mlx5_eswitch *esw = dev->priv.eswitch;
	u16 cur_mlx5_mode, mlx5_mode = 0;
	u16 cur_mlx5_mode, mlx5_mode = 0;
	struct mlx5_eswitch *esw;
	int err;
	int err;


	err = mlx5_eswitch_check(dev);
	esw = mlx5_devlink_eswitch_get(devlink);
	if (err)
	if (IS_ERR(esw))
		return err;
		return PTR_ERR(esw);


	if (esw_mode_from_devlink(mode, &mlx5_mode))
	if (esw_mode_from_devlink(mode, &mlx5_mode))
		return -EINVAL;
		return -EINVAL;
@@ -2338,16 +2326,15 @@ unlock:


int mlx5_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode)
int mlx5_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode)
{
{
	struct mlx5_core_dev *dev = devlink_priv(devlink);
	struct mlx5_eswitch *esw;
	struct mlx5_eswitch *esw = dev->priv.eswitch;
	int err;
	int err;


	err = mlx5_eswitch_check(dev);
	esw = mlx5_devlink_eswitch_get(devlink);
	if (err)
	if (IS_ERR(esw))
		return err;
		return PTR_ERR(esw);


	mutex_lock(&esw->mode_lock);
	mutex_lock(&esw->mode_lock);
	err = eswitch_devlink_esw_mode_check(dev->priv.eswitch);
	err = eswitch_devlink_esw_mode_check(esw);
	if (err)
	if (err)
		goto unlock;
		goto unlock;


@@ -2361,13 +2348,13 @@ int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode,
					 struct netlink_ext_ack *extack)
					 struct netlink_ext_ack *extack)
{
{
	struct mlx5_core_dev *dev = devlink_priv(devlink);
	struct mlx5_core_dev *dev = devlink_priv(devlink);
	struct mlx5_eswitch *esw = dev->priv.eswitch;
	int err, vport, num_vport;
	int err, vport, num_vport;
	struct mlx5_eswitch *esw;
	u8 mlx5_mode;
	u8 mlx5_mode;


	err = mlx5_eswitch_check(dev);
	esw = mlx5_devlink_eswitch_get(devlink);
	if (err)
	if (IS_ERR(esw))
		return err;
		return PTR_ERR(esw);


	mutex_lock(&esw->mode_lock);
	mutex_lock(&esw->mode_lock);
	err = eswitch_devlink_esw_mode_check(esw);
	err = eswitch_devlink_esw_mode_check(esw);
@@ -2424,13 +2411,12 @@ out:


int mlx5_devlink_eswitch_inline_mode_get(struct devlink *devlink, u8 *mode)
int mlx5_devlink_eswitch_inline_mode_get(struct devlink *devlink, u8 *mode)
{
{
	struct mlx5_core_dev *dev = devlink_priv(devlink);
	struct mlx5_eswitch *esw;
	struct mlx5_eswitch *esw = dev->priv.eswitch;
	int err;
	int err;


	err = mlx5_eswitch_check(dev);
	esw = mlx5_devlink_eswitch_get(devlink);
	if (err)
	if (IS_ERR(esw))
		return err;
		return PTR_ERR(esw);


	mutex_lock(&esw->mode_lock);
	mutex_lock(&esw->mode_lock);
	err = eswitch_devlink_esw_mode_check(esw);
	err = eswitch_devlink_esw_mode_check(esw);
@@ -2448,12 +2434,12 @@ int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink,
					struct netlink_ext_ack *extack)
					struct netlink_ext_ack *extack)
{
{
	struct mlx5_core_dev *dev = devlink_priv(devlink);
	struct mlx5_core_dev *dev = devlink_priv(devlink);
	struct mlx5_eswitch *esw = dev->priv.eswitch;
	struct mlx5_eswitch *esw;
	int err;
	int err;


	err = mlx5_eswitch_check(dev);
	esw = mlx5_devlink_eswitch_get(devlink);
	if (err)
	if (IS_ERR(esw))
		return err;
		return PTR_ERR(esw);


	mutex_lock(&esw->mode_lock);
	mutex_lock(&esw->mode_lock);
	err = eswitch_devlink_esw_mode_check(esw);
	err = eswitch_devlink_esw_mode_check(esw);
@@ -2508,13 +2494,13 @@ unlock:
int mlx5_devlink_eswitch_encap_mode_get(struct devlink *devlink,
int mlx5_devlink_eswitch_encap_mode_get(struct devlink *devlink,
					enum devlink_eswitch_encap_mode *encap)
					enum devlink_eswitch_encap_mode *encap)
{
{
	struct mlx5_core_dev *dev = devlink_priv(devlink);
	struct mlx5_eswitch *esw;
	struct mlx5_eswitch *esw = dev->priv.eswitch;
	int err;
	int err;


	err = mlx5_eswitch_check(dev);
	esw = mlx5_devlink_eswitch_get(devlink);
	if (err)
	if (IS_ERR(esw))
		return err;
		return PTR_ERR(esw);



	mutex_lock(&esw->mode_lock);
	mutex_lock(&esw->mode_lock);
	err = eswitch_devlink_esw_mode_check(esw);
	err = eswitch_devlink_esw_mode_check(esw);
Loading