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

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
......@@ -113,6 +113,8 @@ static const struct devlink_ops mlx5_devlink_ops = {
.eswitch_inline_mode_get = mlx5_devlink_eswitch_inline_mode_get,
.eswitch_encap_mode_set = mlx5_devlink_eswitch_encap_mode_set,
.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
.flash_update = mlx5_devlink_flash_update,
.info_get = mlx5_devlink_info_get,
......
......@@ -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);
}
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,
struct mlx5e_rep_priv *rpriv)
{
......@@ -1200,7 +1194,7 @@ static int register_devlink_port(struct mlx5_core_dev *dev,
return 0;
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);
if (rep->vport == MLX5_VPORT_UPLINK)
......
......@@ -63,6 +63,29 @@ struct vport_addr {
static void esw_destroy_legacy_fdb_table(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
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);
}
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)[6] = mac[1];
......@@ -1778,46 +1801,135 @@ void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw)
}
/* Vport Administration */
int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
u16 vport, u8 mac[ETH_ALEN])
static int
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;
int err = 0;
if (IS_ERR(evport))
return PTR_ERR(evport);
if (is_multicast_ether_addr(mac))
return -EINVAL;
mutex_lock(&esw->state_lock);
if (evport->info.spoofchk && !is_valid_ether_addr(mac))
mlx5_core_warn(esw->dev,
"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) {
mlx5_core_warn(esw->dev,
"Failed to mlx5_modify_nic_vport_mac vport(%d) err=(%d)\n",
vport, err);
goto unlock;
vport_num, err);
return err;
}
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)
mlx5_core_warn(esw->dev,
"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);
evport->info.node_guid = node_guid;
if (evport->enabled && esw->mode == MLX5_ESWITCH_LEGACY)
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);
return err;
}
......
......@@ -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(struct mlx5_eswitch *esw, bool clear_vf);
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,
u16 vport, int link_state);
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);
int mlx5_devlink_eswitch_encap_mode_get(struct devlink *devlink,
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);
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;
}
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 */
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); \
(vport) >= (esw)->first_host_vport; (vport)--)
struct mlx5_eswitch *mlx5_devlink_eswitch_get(struct devlink *devlink);
struct mlx5_vport *__must_check
mlx5_eswitch_get_vport(struct mlx5_eswitch *esw, u16 vport_num);
......
......@@ -2279,17 +2279,6 @@ static int esw_inline_mode_to_devlink(u8 mlx5_mode, u8 *mode)
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)
{
/* 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,
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;
struct mlx5_eswitch *esw;
int err;
err = mlx5_eswitch_check(dev);
if (err)
return err;
esw = mlx5_devlink_eswitch_get(devlink);
if (IS_ERR(esw))
return PTR_ERR(esw);
if (esw_mode_from_devlink(mode, &mlx5_mode))
return -EINVAL;
......@@ -2338,16 +2326,15 @@ int mlx5_devlink_eswitch_mode_set(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 = dev->priv.eswitch;
struct mlx5_eswitch *esw;
int err;
err = mlx5_eswitch_check(dev);
if (err)
return err;
esw = mlx5_devlink_eswitch_get(devlink);
if (IS_ERR(esw))
return PTR_ERR(esw);
mutex_lock(&esw->mode_lock);
err = eswitch_devlink_esw_mode_check(dev->priv.eswitch);
err = eswitch_devlink_esw_mode_check(esw);
if (err)
goto unlock;
......@@ -2361,13 +2348,13 @@ int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode,
struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
struct mlx5_eswitch *esw = dev->priv.eswitch;
int err, vport, num_vport;
struct mlx5_eswitch *esw;
u8 mlx5_mode;
err = mlx5_eswitch_check(dev);
if (err)
return err;
esw = mlx5_devlink_eswitch_get(devlink);
if (IS_ERR(esw))
return PTR_ERR(esw);
mutex_lock(&esw->mode_lock);
err = eswitch_devlink_esw_mode_check(esw);
......@@ -2424,13 +2411,12 @@ int mlx5_devlink_eswitch_inline_mode_set(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 = dev->priv.eswitch;
struct mlx5_eswitch *esw;
int err;
err = mlx5_eswitch_check(dev);
if (err)
return err;
esw = mlx5_devlink_eswitch_get(devlink);
if (IS_ERR(esw))
return PTR_ERR(esw);
mutex_lock(&esw->mode_lock);
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 mlx5_core_dev *dev = devlink_priv(devlink);
struct mlx5_eswitch *esw = dev->priv.eswitch;
struct mlx5_eswitch *esw;
int err;
err = mlx5_eswitch_check(dev);
if (err)
return err;
esw = mlx5_devlink_eswitch_get(devlink);
if (IS_ERR(esw))
return PTR_ERR(esw);
mutex_lock(&esw->mode_lock);
err = eswitch_devlink_esw_mode_check(esw);
......@@ -2508,13 +2494,13 @@ int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink,
int mlx5_devlink_eswitch_encap_mode_get(struct devlink *devlink,
enum devlink_eswitch_encap_mode *encap)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
struct mlx5_eswitch *esw = dev->priv.eswitch;
struct mlx5_eswitch *esw;
int err;
err = mlx5_eswitch_check(dev);
if (err)
return err;
esw = mlx5_devlink_eswitch_get(devlink);
if (IS_ERR(esw))
return PTR_ERR(esw);
mutex_lock(&esw->mode_lock);
err = eswitch_devlink_esw_mode_check(esw);
......
......@@ -173,7 +173,7 @@ int mlx5_query_mac_address(struct mlx5_core_dev *mdev, u8 *addr)
EXPORT_SYMBOL_GPL(mlx5_query_mac_address);
int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *mdev,
u16 vport, u8 *addr)
u16 vport, const u8 *addr)
{
void *in;
int inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in);
......
......@@ -75,7 +75,7 @@ void mlx5_query_min_inline(struct mlx5_core_dev *mdev, u8 *min_inline);
int mlx5_modify_nic_vport_min_inline(struct mlx5_core_dev *mdev,
u16 vport, u8 min_inline);
int mlx5_modify_nic_vport_mac_address(struct mlx5_core_dev *dev,
u16 vport, u8 *addr);
u16 vport, const u8 *addr);
int mlx5_query_nic_vport_mtu(struct mlx5_core_dev *mdev, u16 *mtu);
int mlx5_modify_nic_vport_mtu(struct mlx5_core_dev *mdev, u16 mtu);
int mlx5_query_nic_vport_system_image_guid(struct mlx5_core_dev *mdev,
......
......@@ -1107,6 +1107,28 @@ struct devlink_ops {
int (*trap_policer_counter_get)(struct devlink *devlink,
const struct devlink_trap_policer *policer,
u64 *p_drops);
/**
* @port_function_hw_addr_get: Port function's hardware address get function.
*
* Should be used by device drivers to report the hardware address of a function managed
* by the devlink port. Driver should return -EOPNOTSUPP if it doesn't support port
* function handling for a particular port.
*
* Note: @extack can be NULL when port notifier queries the port function.
*/
int (*port_function_hw_addr_get)(struct devlink *devlink, struct devlink_port *port,
u8 *hw_addr, int *hw_addr_len,
struct netlink_ext_ack *extack);
/**
* @port_function_hw_addr_set: Port function's hardware address set function.
*
* Should be used by device drivers to set the hardware address of a function managed
* by the devlink port. Driver should return -EOPNOTSUPP if it doesn't support port
* function handling for a particular port.
*/
int (*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);
};
static inline void *devlink_priv(struct devlink *devlink)
......
......@@ -451,6 +451,8 @@ enum devlink_attr {
DEVLINK_ATTR_TRAP_POLICER_RATE, /* u64 */
DEVLINK_ATTR_TRAP_POLICER_BURST, /* u64 */
DEVLINK_ATTR_PORT_FUNCTION, /* nested */
/* add new attributes above here, update the policy in devlink.c */
__DEVLINK_ATTR_MAX,
......@@ -497,4 +499,12 @@ enum devlink_resource_unit {
DEVLINK_RESOURCE_UNIT_ENTRY,
};
enum devlink_port_function_attr {
DEVLINK_PORT_FUNCTION_ATTR_UNSPEC,
DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, /* binary */
__DEVLINK_PORT_FUNCTION_ATTR_MAX,
DEVLINK_PORT_FUNCTION_ATTR_MAX = __DEVLINK_PORT_FUNCTION_ATTR_MAX - 1
};
#endif /* _UAPI_LINUX_DEVLINK_H_ */
......@@ -85,6 +85,10 @@ EXPORT_SYMBOL(devlink_dpipe_header_ipv6);
EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg);
EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwerr);
static const struct nla_policy devlink_function_nl_policy[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1] = {
[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR] = { .type = NLA_BINARY },
};
static LIST_HEAD(devlink_list);
/* devlink_mutex
......@@ -563,10 +567,54 @@ static int devlink_nl_port_attrs_put(struct sk_buff *msg,
return 0;
}
static int
devlink_nl_port_function_attrs_put(struct sk_buff *msg, struct devlink_port *port,
struct netlink_ext_ack *extack)
{
struct devlink *devlink = port->devlink;
const struct devlink_ops *ops;
struct nlattr *function_attr;
bool empty_nest = true;
int err = 0;
function_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_PORT_FUNCTION);
if (!function_attr)
return -EMSGSIZE;
ops = devlink->ops;
if (ops->port_function_hw_addr_get) {
int uninitialized_var(hw_addr_len);
u8 hw_addr[MAX_ADDR_LEN];
err = ops->port_function_hw_addr_get(devlink, port, hw_addr, &hw_addr_len, extack);
if (err == -EOPNOTSUPP) {
/* Port function attributes are optional for a port. If port doesn't
* support function attribute, returning -EOPNOTSUPP is not an error.
*/
err = 0;
goto out;
} else if (err) {
goto out;
}
err = nla_put(msg, DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, hw_addr_len, hw_addr);
if (err)
goto out;
empty_nest = false;
}
out:
if (err || empty_nest)
nla_nest_cancel(msg, function_attr);
else
nla_nest_end(msg, function_attr);
return err;
}
static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink,
struct devlink_port *devlink_port,
enum devlink_command cmd, u32 portid,
u32 seq, int flags)
u32 seq, int flags,
struct netlink_ext_ack *extack)
{
void *hdr;
......@@ -607,6 +655,8 @@ static int devlink_nl_port_fill(struct sk_buff *msg, struct devlink *devlink,
spin_unlock_bh(&devlink_port->type_lock);
if (devlink_nl_port_attrs_put(msg, devlink_port))
goto nla_put_failure;
if (devlink_nl_port_function_attrs_put(msg, devlink_port, extack))
goto nla_put_failure;
genlmsg_end(msg, hdr);
return 0;
......@@ -634,7 +684,8 @@ static void devlink_port_notify(struct devlink_port *devlink_port,
if (!msg)
return;
err = devlink_nl_port_fill(msg, devlink, devlink_port, cmd, 0, 0, 0);
err = devlink_nl_port_fill(msg, devlink, devlink_port, cmd, 0, 0, 0,
NULL);
if (err) {
nlmsg_free(msg);
return;
......@@ -708,7 +759,8 @@ static int devlink_nl_cmd_port_get_doit(struct sk_buff *skb,
err = devlink_nl_port_fill(msg, devlink, devlink_port,
DEVLINK_CMD_PORT_NEW,
info->snd_portid, info->snd_seq, 0);
info->snd_portid, info->snd_seq, 0,
info->extack);
if (err) {
nlmsg_free(msg);
return err;
......@@ -740,7 +792,8 @@ static int devlink_nl_cmd_port_get_dumpit(struct sk_buff *msg,
DEVLINK_CMD_NEW,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
NLM_F_MULTI);
NLM_F_MULTI,
cb->extack);
if (err) {
mutex_unlock(&devlink->lock);
goto out;
......@@ -778,6 +831,67 @@ static int devlink_port_type_set(struct devlink *devlink,
return -EOPNOTSUPP;
}
static int
devlink_port_function_hw_addr_set(struct devlink *devlink, struct devlink_port *port,
const struct nlattr *attr, struct netlink_ext_ack *extack)
{
const struct devlink_ops *ops;
const u8 *hw_addr;
int hw_addr_len;
int err;
hw_addr = nla_data(attr);
hw_addr_len = nla_len(attr);
if (hw_addr_len > MAX_ADDR_LEN) {
NL_SET_ERR_MSG_MOD(extack, "Port function hardware address too long");
return -EINVAL;
}
if (port->type == DEVLINK_PORT_TYPE_ETH) {
if (hw_addr_len != ETH_ALEN) {
NL_SET_ERR_MSG_MOD(extack, "Address must be 6 bytes for Ethernet device");
return -EINVAL;
}
if (!is_unicast_ether_addr(hw_addr)) {
NL_SET_ERR_MSG_MOD(extack, "Non-unicast hardware address unsupported");
return -EINVAL;
}
}
ops = devlink->ops;
if (!ops->port_function_hw_addr_set) {
NL_SET_ERR_MSG_MOD(extack, "Port doesn't support function attributes");
return -EOPNOTSUPP;
}
err = ops->port_function_hw_addr_set(devlink, port, hw_addr, hw_addr_len, extack);
if (err)
return err;
devlink_port_notify(port, DEVLINK_CMD_PORT_NEW);
return 0;
}
static int
devlink_port_function_set(struct devlink *devlink, struct devlink_port *port,
const struct nlattr *attr, struct netlink_ext_ack *extack)
{
struct nlattr *tb[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1];
int err;
err = nla_parse_nested(tb, DEVLINK_PORT_FUNCTION_ATTR_MAX, attr,
devlink_function_nl_policy, extack);
if (err < 0) {
NL_SET_ERR_MSG_MOD(extack, "Fail to parse port function attributes");
return err;
}
attr = tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR];
if (attr)
err = devlink_port_function_hw_addr_set(devlink, port, attr, extack);
return err;
}
static int devlink_nl_cmd_port_set_doit(struct sk_buff *skb,
struct genl_info *info)
{
......@@ -793,6 +907,16 @@ static int devlink_nl_cmd_port_set_doit(struct sk_buff *skb,
if (err)
return err;
}
if (info->attrs[DEVLINK_ATTR_PORT_FUNCTION]) {
struct nlattr *attr = info->attrs[DEVLINK_ATTR_PORT_FUNCTION];
struct netlink_ext_ack *extack = info->extack;
err = devlink_port_function_set(devlink, devlink_port, attr, extack);
if (err)
return err;
}
return 0;
}
......@@ -6709,6 +6833,7 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
[DEVLINK_ATTR_TRAP_POLICER_ID] = { .type = NLA_U32 },
[DEVLINK_ATTR_TRAP_POLICER_RATE] = { .type = NLA_U64 },
[DEVLINK_ATTR_TRAP_POLICER_BURST] = { .type = NLA_U64 },
[DEVLINK_ATTR_PORT_FUNCTION] = { .type = NLA_NESTED },
};
static const struct genl_ops devlink_nl_ops[] = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment