1609 lines · 45.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright (c) 2016 Mellanox Technologies. All rights reserved. * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> */ #include "devl_internal.h" #define DEVLINK_PORT_FN_CAPS_VALID_MASK \	(_BITUL(__DEVLINK_PORT_FN_ATTR_CAPS_MAX) - 1) static const struct nla_policy devlink_function_nl_policy[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1] = {	[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR] = { .type = NLA_BINARY },	[DEVLINK_PORT_FN_ATTR_STATE] =		NLA_POLICY_RANGE(NLA_U8, DEVLINK_PORT_FN_STATE_INACTIVE,				 DEVLINK_PORT_FN_STATE_ACTIVE),	[DEVLINK_PORT_FN_ATTR_CAPS] =		NLA_POLICY_BITFIELD32(DEVLINK_PORT_FN_CAPS_VALID_MASK),	[DEVLINK_PORT_FN_ATTR_MAX_IO_EQS] = { .type = NLA_U32 },}; #define ASSERT_DEVLINK_PORT_REGISTERED(devlink_port)				\	WARN_ON_ONCE(!(devlink_port)->registered)#define ASSERT_DEVLINK_PORT_NOT_REGISTERED(devlink_port)			\	WARN_ON_ONCE((devlink_port)->registered) struct devlink_port *devlink_port_get_by_index(struct devlink *devlink,					       unsigned int port_index){	return xa_load(&devlink->ports, port_index);} struct devlink_port *devlink_port_get_from_attrs(struct devlink *devlink,						 struct nlattr **attrs){	if (attrs[DEVLINK_ATTR_PORT_INDEX]) {		u32 port_index = nla_get_u32(attrs[DEVLINK_ATTR_PORT_INDEX]);		struct devlink_port *devlink_port; 		devlink_port = devlink_port_get_by_index(devlink, port_index);		if (!devlink_port)			return ERR_PTR(-ENODEV);		return devlink_port;	}	return ERR_PTR(-EINVAL);} struct devlink_port *devlink_port_get_from_info(struct devlink *devlink,						struct genl_info *info){	return devlink_port_get_from_attrs(devlink, info->attrs);} static void devlink_port_fn_cap_fill(struct nla_bitfield32 *caps,				     u32 cap, bool is_enable){	caps->selector |= cap;	if (is_enable)		caps->value |= cap;} static int devlink_port_fn_roce_fill(struct devlink_port *devlink_port,				     struct nla_bitfield32 *caps,				     struct netlink_ext_ack *extack){	bool is_enable;	int err; 	if (!devlink_port->ops->port_fn_roce_get)		return 0; 	err = devlink_port->ops->port_fn_roce_get(devlink_port, &is_enable,						  extack);	if (err) {		if (err == -EOPNOTSUPP)			return 0;		return err;	} 	devlink_port_fn_cap_fill(caps, DEVLINK_PORT_FN_CAP_ROCE, is_enable);	return 0;} static int devlink_port_fn_migratable_fill(struct devlink_port *devlink_port,					   struct nla_bitfield32 *caps,					   struct netlink_ext_ack *extack){	bool is_enable;	int err; 	if (!devlink_port->ops->port_fn_migratable_get ||	    devlink_port->attrs.flavour != DEVLINK_PORT_FLAVOUR_PCI_VF)		return 0; 	err = devlink_port->ops->port_fn_migratable_get(devlink_port,							&is_enable, extack);	if (err) {		if (err == -EOPNOTSUPP)			return 0;		return err;	} 	devlink_port_fn_cap_fill(caps, DEVLINK_PORT_FN_CAP_MIGRATABLE, is_enable);	return 0;} static int devlink_port_fn_ipsec_crypto_fill(struct devlink_port *devlink_port,					     struct nla_bitfield32 *caps,					     struct netlink_ext_ack *extack){	bool is_enable;	int err; 	if (!devlink_port->ops->port_fn_ipsec_crypto_get ||	    devlink_port->attrs.flavour != DEVLINK_PORT_FLAVOUR_PCI_VF)		return 0; 	err = devlink_port->ops->port_fn_ipsec_crypto_get(devlink_port, &is_enable, extack);	if (err) {		if (err == -EOPNOTSUPP)			return 0;		return err;	} 	devlink_port_fn_cap_fill(caps, DEVLINK_PORT_FN_CAP_IPSEC_CRYPTO, is_enable);	return 0;} static int devlink_port_fn_ipsec_packet_fill(struct devlink_port *devlink_port,					     struct nla_bitfield32 *caps,					     struct netlink_ext_ack *extack){	bool is_enable;	int err; 	if (!devlink_port->ops->port_fn_ipsec_packet_get ||	    devlink_port->attrs.flavour != DEVLINK_PORT_FLAVOUR_PCI_VF)		return 0; 	err = devlink_port->ops->port_fn_ipsec_packet_get(devlink_port, &is_enable, extack);	if (err) {		if (err == -EOPNOTSUPP)			return 0;		return err;	} 	devlink_port_fn_cap_fill(caps, DEVLINK_PORT_FN_CAP_IPSEC_PACKET, is_enable);	return 0;} static int devlink_port_fn_caps_fill(struct devlink_port *devlink_port,				     struct sk_buff *msg,				     struct netlink_ext_ack *extack,				     bool *msg_updated){	struct nla_bitfield32 caps = {};	int err; 	err = devlink_port_fn_roce_fill(devlink_port, &caps, extack);	if (err)		return err; 	err = devlink_port_fn_migratable_fill(devlink_port, &caps, extack);	if (err)		return err; 	err = devlink_port_fn_ipsec_crypto_fill(devlink_port, &caps, extack);	if (err)		return err; 	err = devlink_port_fn_ipsec_packet_fill(devlink_port, &caps, extack);	if (err)		return err; 	if (!caps.selector)		return 0;	err = nla_put_bitfield32(msg, DEVLINK_PORT_FN_ATTR_CAPS, caps.value,				 caps.selector);	if (err)		return err; 	*msg_updated = true;	return 0;} static int devlink_port_fn_max_io_eqs_fill(struct devlink_port *port,					   struct sk_buff *msg,					   struct netlink_ext_ack *extack,					   bool *msg_updated){	u32 max_io_eqs;	int err; 	if (!port->ops->port_fn_max_io_eqs_get)		return 0; 	err = port->ops->port_fn_max_io_eqs_get(port, &max_io_eqs, extack);	if (err) {		if (err == -EOPNOTSUPP)			return 0;		return err;	}	err = nla_put_u32(msg, DEVLINK_PORT_FN_ATTR_MAX_IO_EQS, max_io_eqs);	if (err)		return err;	*msg_updated = true;	return 0;} int devlink_nl_port_handle_fill(struct sk_buff *msg, struct devlink_port *devlink_port){	if (devlink_nl_put_handle(msg, devlink_port->devlink))		return -EMSGSIZE;	if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, devlink_port->index))		return -EMSGSIZE;	return 0;} size_t devlink_nl_port_handle_size(struct devlink_port *devlink_port){	struct devlink *devlink = devlink_port->devlink; 	return nla_total_size(strlen(devlink_bus_name(devlink)) + 1) /* DEVLINK_ATTR_BUS_NAME */	     + nla_total_size(strlen(devlink_dev_name(devlink)) + 1) /* DEVLINK_ATTR_DEV_NAME */	     + nla_total_size(8) /* DEVLINK_ATTR_INDEX */	     + nla_total_size(4); /* DEVLINK_ATTR_PORT_INDEX */} static int devlink_nl_port_attrs_put(struct sk_buff *msg,				     struct devlink_port *devlink_port){	struct devlink_port_attrs *attrs = &devlink_port->attrs; 	if (!devlink_port->attrs_set)		return 0;	if (attrs->lanes) {		if (nla_put_u32(msg, DEVLINK_ATTR_PORT_LANES, attrs->lanes))			return -EMSGSIZE;	}	if (nla_put_u8(msg, DEVLINK_ATTR_PORT_SPLITTABLE, attrs->splittable))		return -EMSGSIZE;	if (nla_put_u16(msg, DEVLINK_ATTR_PORT_FLAVOUR, attrs->flavour))		return -EMSGSIZE;	switch (devlink_port->attrs.flavour) {	case DEVLINK_PORT_FLAVOUR_PCI_PF:		if (nla_put_u32(msg, DEVLINK_ATTR_PORT_CONTROLLER_NUMBER,				attrs->pci_pf.controller) ||		    nla_put_u16(msg, DEVLINK_ATTR_PORT_PCI_PF_NUMBER, attrs->pci_pf.pf))			return -EMSGSIZE;		if (nla_put_u8(msg, DEVLINK_ATTR_PORT_EXTERNAL, attrs->pci_pf.external))			return -EMSGSIZE;		break;	case DEVLINK_PORT_FLAVOUR_PCI_VF:		if (nla_put_u32(msg, DEVLINK_ATTR_PORT_CONTROLLER_NUMBER,				attrs->pci_vf.controller) ||		    nla_put_u16(msg, DEVLINK_ATTR_PORT_PCI_PF_NUMBER, attrs->pci_vf.pf) ||		    nla_put_u16(msg, DEVLINK_ATTR_PORT_PCI_VF_NUMBER, attrs->pci_vf.vf))			return -EMSGSIZE;		if (nla_put_u8(msg, DEVLINK_ATTR_PORT_EXTERNAL, attrs->pci_vf.external))			return -EMSGSIZE;		break;	case DEVLINK_PORT_FLAVOUR_PCI_SF:		if (nla_put_u32(msg, DEVLINK_ATTR_PORT_CONTROLLER_NUMBER,				attrs->pci_sf.controller) ||		    nla_put_u16(msg, DEVLINK_ATTR_PORT_PCI_PF_NUMBER,				attrs->pci_sf.pf) ||		    nla_put_u32(msg, DEVLINK_ATTR_PORT_PCI_SF_NUMBER,				attrs->pci_sf.sf))			return -EMSGSIZE;		if (nla_put_u8(msg, DEVLINK_ATTR_PORT_EXTERNAL, attrs->pci_sf.external))			return -EMSGSIZE;		break;	case DEVLINK_PORT_FLAVOUR_PHYSICAL:	case DEVLINK_PORT_FLAVOUR_CPU:	case DEVLINK_PORT_FLAVOUR_DSA:		if (nla_put_u32(msg, DEVLINK_ATTR_PORT_NUMBER,				attrs->phys.port_number))			return -EMSGSIZE;		if (!attrs->split)			return 0;		if (nla_put_u32(msg, DEVLINK_ATTR_PORT_SPLIT_GROUP,				attrs->phys.port_number))			return -EMSGSIZE;		if (nla_put_u32(msg, DEVLINK_ATTR_PORT_SPLIT_SUBPORT_NUMBER,				attrs->phys.split_subport_number))			return -EMSGSIZE;		break;	default:		break;	}	return 0;} static int devlink_port_fn_hw_addr_fill(struct devlink_port *port,					struct sk_buff *msg,					struct netlink_ext_ack *extack,					bool *msg_updated){	u8 hw_addr[MAX_ADDR_LEN];	int hw_addr_len;	int err; 	if (!port->ops->port_fn_hw_addr_get)		return 0; 	err = port->ops->port_fn_hw_addr_get(port, hw_addr, &hw_addr_len,					     extack);	if (err) {		if (err == -EOPNOTSUPP)			return 0;		return err;	}	err = nla_put(msg, DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, hw_addr_len, hw_addr);	if (err)		return err;	*msg_updated = true;	return 0;} static booldevlink_port_fn_state_valid(enum devlink_port_fn_state state){	return state == DEVLINK_PORT_FN_STATE_INACTIVE ||	       state == DEVLINK_PORT_FN_STATE_ACTIVE;} static booldevlink_port_fn_opstate_valid(enum devlink_port_fn_opstate opstate){	return opstate == DEVLINK_PORT_FN_OPSTATE_DETACHED ||	       opstate == DEVLINK_PORT_FN_OPSTATE_ATTACHED;} static int devlink_port_fn_state_fill(struct devlink_port *port,				      struct sk_buff *msg,				      struct netlink_ext_ack *extack,				      bool *msg_updated){	enum devlink_port_fn_opstate opstate;	enum devlink_port_fn_state state;	int err; 	if (!port->ops->port_fn_state_get)		return 0; 	err = port->ops->port_fn_state_get(port, &state, &opstate, extack);	if (err) {		if (err == -EOPNOTSUPP)			return 0;		return err;	}	if (!devlink_port_fn_state_valid(state)) {		WARN_ON_ONCE(1);		NL_SET_ERR_MSG(extack, "Invalid state read from driver");		return -EINVAL;	}	if (!devlink_port_fn_opstate_valid(opstate)) {		WARN_ON_ONCE(1);		NL_SET_ERR_MSG(extack, "Invalid operational state read from driver");		return -EINVAL;	}	if (nla_put_u8(msg, DEVLINK_PORT_FN_ATTR_STATE, state) ||	    nla_put_u8(msg, DEVLINK_PORT_FN_ATTR_OPSTATE, opstate))		return -EMSGSIZE;	*msg_updated = true;	return 0;} static intdevlink_port_fn_mig_set(struct devlink_port *devlink_port, bool enable,			struct netlink_ext_ack *extack){	return devlink_port->ops->port_fn_migratable_set(devlink_port, enable,							 extack);} static intdevlink_port_fn_roce_set(struct devlink_port *devlink_port, bool enable,			 struct netlink_ext_ack *extack){	return devlink_port->ops->port_fn_roce_set(devlink_port, enable,						   extack);} static intdevlink_port_fn_ipsec_crypto_set(struct devlink_port *devlink_port, bool enable,				 struct netlink_ext_ack *extack){	return devlink_port->ops->port_fn_ipsec_crypto_set(devlink_port, enable, extack);} static intdevlink_port_fn_ipsec_packet_set(struct devlink_port *devlink_port, bool enable,				 struct netlink_ext_ack *extack){	return devlink_port->ops->port_fn_ipsec_packet_set(devlink_port, enable, extack);} static int devlink_port_fn_caps_set(struct devlink_port *devlink_port,				    const struct nlattr *attr,				    struct netlink_ext_ack *extack){	struct nla_bitfield32 caps;	u32 caps_value;	int err; 	caps = nla_get_bitfield32(attr);	caps_value = caps.value & caps.selector;	if (caps.selector & DEVLINK_PORT_FN_CAP_ROCE) {		err = devlink_port_fn_roce_set(devlink_port,					       caps_value & DEVLINK_PORT_FN_CAP_ROCE,					       extack);		if (err)			return err;	}	if (caps.selector & DEVLINK_PORT_FN_CAP_MIGRATABLE) {		err = devlink_port_fn_mig_set(devlink_port, caps_value &					      DEVLINK_PORT_FN_CAP_MIGRATABLE,					      extack);		if (err)			return err;	}	if (caps.selector & DEVLINK_PORT_FN_CAP_IPSEC_CRYPTO) {		err = devlink_port_fn_ipsec_crypto_set(devlink_port, caps_value &						       DEVLINK_PORT_FN_CAP_IPSEC_CRYPTO,						       extack);		if (err)			return err;	}	if (caps.selector & DEVLINK_PORT_FN_CAP_IPSEC_PACKET) {		err = devlink_port_fn_ipsec_packet_set(devlink_port, caps_value &						       DEVLINK_PORT_FN_CAP_IPSEC_PACKET,						       extack);		if (err)			return err;	}	return 0;} static intdevlink_port_fn_max_io_eqs_set(struct devlink_port *devlink_port,			       const struct nlattr *attr,			       struct netlink_ext_ack *extack){	u32 max_io_eqs; 	max_io_eqs = nla_get_u32(attr);	return devlink_port->ops->port_fn_max_io_eqs_set(devlink_port,							 max_io_eqs, extack);} static intdevlink_nl_port_function_attrs_put(struct sk_buff *msg, struct devlink_port *port,				   struct netlink_ext_ack *extack){	struct nlattr *function_attr;	bool msg_updated = false;	int err; 	function_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_PORT_FUNCTION);	if (!function_attr)		return -EMSGSIZE; 	err = devlink_port_fn_hw_addr_fill(port, msg, extack, &msg_updated);	if (err)		goto out;	err = devlink_port_fn_caps_fill(port, msg, extack, &msg_updated);	if (err)		goto out;	err = devlink_port_fn_state_fill(port, msg, extack, &msg_updated);	if (err)		goto out;	err = devlink_port_fn_max_io_eqs_fill(port, msg, extack, &msg_updated);	if (err)		goto out;	err = devlink_rel_devlink_handle_put(msg, port->devlink,					     port->rel_index,					     DEVLINK_PORT_FN_ATTR_DEVLINK,					     &msg_updated); out:	if (err || !msg_updated)		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_port *devlink_port,				enum devlink_command cmd, u32 portid, u32 seq,				int flags, struct netlink_ext_ack *extack){	struct devlink *devlink = devlink_port->devlink;	void *hdr; 	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);	if (!hdr)		return -EMSGSIZE; 

Lines 1–500 of 1609.