Commit f3b6129b authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files
Tony Nguyen says:

====================
ice: support devlink subfunction

Michal Swiatkowski says:

Currently ice driver does not allow creating more than one networking
device per physical function. The only way to have more hardware backed
netdev is to use SR-IOV.

Following patchset adds support for devlink port API. For each new
pcisf type port, driver allocates new VSI, configures all resources
needed, including dynamically MSIX vectors, program rules and registers
new netdev.

This series supports only one Tx/Rx queue pair per subfunction.

Example commands:
devlink port add pci/0000:31:00.1 flavour pcisf pfnum 1 sfnum 1000
devlink port function set pci/0000:31:00.1/1 hw_addr 00:00:00:00:03:14
devlink port function set pci/0000:31:00.1/1 state active
devlink port function del pci/0000:31:00.1/1

Make the port representor and eswitch code generic to support
subfunction representor type.

VSI configuration is slightly different between VF and SF. It needs to
be reflected in the code.

* '100GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue:
  ice: subfunction activation and base devlink ops
  ice: basic support for VLAN in subfunctions
  ice: support subfunction devlink Tx topology
  ice: implement netdevice ops for SF representor
  ice: check if SF is ready in ethtool ops
  ice: don't set target VSI for subfunction
  ice: create port representor for SF
  ice: make representor code generic
  ice: implement netdev for subfunction
  ice: base subfunction aux driver
  ice: allocate devlink for subfunction
  ice: treat subfunction VSI the same as PF VSI
  ice: add basic devlink subfunctions support
  ice: export ice ndo_ops functions
  ice: add new VSI type for subfunctions
====================

Link: https://patch.msgid.link/20240906223010.2194591-1-anthony.l.nguyen@intel.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 474bb1aa 13acc5c4
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -33,6 +33,8 @@ ice-y := ice_main.o \
	 ice_idc.o	\
	 devlink/devlink.o	\
	 devlink/devlink_port.o \
	 ice_sf_eth.o	\
	 ice_sf_vsi_vlan_ops.o \
	 ice_ddp.o	\
	 ice_fw_update.o \
	 ice_lag.o	\
+46 −0
Original line number Diff line number Diff line
@@ -6,9 +6,11 @@
#include "ice.h"
#include "ice_lib.h"
#include "devlink.h"
#include "devlink_port.h"
#include "ice_eswitch.h"
#include "ice_fw_update.h"
#include "ice_dcb_lib.h"
#include "ice_sf_eth.h"

/* context for devlink info version reporting */
struct ice_info_ctx {
@@ -744,6 +746,7 @@ static void ice_traverse_tx_tree(struct devlink *devlink, struct ice_sched_node
				 struct ice_sched_node *tc_node, struct ice_pf *pf)
{
	struct devlink_rate *rate_node = NULL;
	struct ice_dynamic_port *sf;
	struct ice_vf *vf;
	int i;

@@ -755,6 +758,7 @@ static void ice_traverse_tx_tree(struct devlink *devlink, struct ice_sched_node
		/* create root node */
		rate_node = devl_rate_node_create(devlink, node, node->name, NULL);
	} else if (node->vsi_handle &&
		   pf->vsi[node->vsi_handle]->type == ICE_VSI_VF &&
		   pf->vsi[node->vsi_handle]->vf) {
		vf = pf->vsi[node->vsi_handle]->vf;
		if (!vf->devlink_port.devlink_rate)
@@ -763,6 +767,16 @@ static void ice_traverse_tx_tree(struct devlink *devlink, struct ice_sched_node
			 */
			devl_rate_leaf_create(&vf->devlink_port, node,
					      node->parent->rate_node);
	} else if (node->vsi_handle &&
		   pf->vsi[node->vsi_handle]->type == ICE_VSI_SF &&
		   pf->vsi[node->vsi_handle]->sf) {
		sf = pf->vsi[node->vsi_handle]->sf;
		if (!sf->devlink_port.devlink_rate)
			/* leaf nodes doesn't have children
			 * so we don't set rate_node
			 */
			devl_rate_leaf_create(&sf->devlink_port, node,
					      node->parent->rate_node);
	} else if (node->info.data.elem_type != ICE_AQC_ELEM_TYPE_LEAF &&
		   node->parent->rate_node) {
		rate_node = devl_rate_node_create(devlink, node, node->name,
@@ -1277,8 +1291,12 @@ static const struct devlink_ops ice_devlink_ops = {

	.rate_leaf_parent_set = ice_devlink_set_parent,
	.rate_node_parent_set = ice_devlink_set_parent,

	.port_new = ice_devlink_port_new,
};

static const struct devlink_ops ice_sf_devlink_ops;

static int
ice_devlink_enable_roce_get(struct devlink *devlink, u32 id,
			    struct devlink_param_gset_ctx *ctx)
@@ -1561,6 +1579,34 @@ struct ice_pf *ice_allocate_pf(struct device *dev)
	return devlink_priv(devlink);
}

/**
 * ice_allocate_sf - Allocate devlink and return SF structure pointer
 * @dev: the device to allocate for
 * @pf: pointer to the PF structure
 *
 * Allocate a devlink instance for SF.
 *
 * Return: ice_sf_priv pointer to allocated memory or ERR_PTR in case of error
 */
struct ice_sf_priv *ice_allocate_sf(struct device *dev, struct ice_pf *pf)
{
	struct devlink *devlink;
	int err;

	devlink = devlink_alloc(&ice_sf_devlink_ops, sizeof(struct ice_sf_priv),
				dev);
	if (!devlink)
		return ERR_PTR(-ENOMEM);

	err = devl_nested_devlink_set(priv_to_devlink(pf), devlink);
	if (err) {
		devlink_free(devlink);
		return ERR_PTR(err);
	}

	return devlink_priv(devlink);
}

/**
 * ice_devlink_register - Register devlink interface for this PF
 * @pf: the PF to register the devlink for.
+1 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
#define _ICE_DEVLINK_H_

struct ice_pf *ice_allocate_pf(struct device *dev);
struct ice_sf_priv *ice_allocate_sf(struct device *dev, struct ice_pf *pf);

void ice_devlink_register(struct ice_pf *pf);
void ice_devlink_unregister(struct ice_pf *pf);
+506 −0
Original line number Diff line number Diff line
@@ -5,6 +5,9 @@

#include "ice.h"
#include "devlink.h"
#include "devlink_port.h"
#include "ice_lib.h"
#include "ice_fltr.h"

static int ice_active_port_option = -1;

@@ -485,3 +488,506 @@ void ice_devlink_destroy_vf_port(struct ice_vf *vf)
	devl_rate_leaf_destroy(&vf->devlink_port);
	devl_port_unregister(&vf->devlink_port);
}

/**
 * ice_devlink_create_sf_dev_port - Register virtual port for a subfunction
 * @sf_dev: the subfunction device to create a devlink port for
 *
 * Register virtual flavour devlink port for the subfunction auxiliary device
 * created after activating a dynamically added devlink port.
 *
 * Return: zero on success or an error code on failure.
 */
int ice_devlink_create_sf_dev_port(struct ice_sf_dev *sf_dev)
{
	struct devlink_port_attrs attrs = {};
	struct ice_dynamic_port *dyn_port;
	struct devlink_port *devlink_port;
	struct devlink *devlink;
	struct ice_vsi *vsi;

	dyn_port = sf_dev->dyn_port;
	vsi = dyn_port->vsi;

	devlink_port = &sf_dev->priv->devlink_port;

	attrs.flavour = DEVLINK_PORT_FLAVOUR_VIRTUAL;

	devlink_port_attrs_set(devlink_port, &attrs);
	devlink = priv_to_devlink(sf_dev->priv);

	return devl_port_register(devlink, devlink_port, vsi->idx);
}

/**
 * ice_devlink_destroy_sf_dev_port - Destroy virtual port for a subfunction
 * @sf_dev: the subfunction device to create a devlink port for
 *
 * Unregisters the virtual port associated with this subfunction.
 */
void ice_devlink_destroy_sf_dev_port(struct ice_sf_dev *sf_dev)
{
	devl_port_unregister(&sf_dev->priv->devlink_port);
}

/**
 * ice_activate_dynamic_port - Activate a dynamic port
 * @dyn_port: dynamic port instance to activate
 * @extack: extack for reporting error messages
 *
 * Activate the dynamic port based on its flavour.
 *
 * Return: zero on success or an error code on failure.
 */
static int
ice_activate_dynamic_port(struct ice_dynamic_port *dyn_port,
			  struct netlink_ext_ack *extack)
{
	int err;

	if (dyn_port->active)
		return 0;

	err = ice_sf_eth_activate(dyn_port, extack);
	if (err)
		return err;

	dyn_port->active = true;

	return 0;
}

/**
 * ice_deactivate_dynamic_port - Deactivate a dynamic port
 * @dyn_port: dynamic port instance to deactivate
 *
 * Undo activation of a dynamic port.
 */
static void ice_deactivate_dynamic_port(struct ice_dynamic_port *dyn_port)
{
	if (!dyn_port->active)
		return;

	ice_sf_eth_deactivate(dyn_port);
	dyn_port->active = false;
}

/**
 * ice_dealloc_dynamic_port - Deallocate and remove a dynamic port
 * @dyn_port: dynamic port instance to deallocate
 *
 * Free resources associated with a dynamically added devlink port. Will
 * deactivate the port if its currently active.
 */
static void ice_dealloc_dynamic_port(struct ice_dynamic_port *dyn_port)
{
	struct devlink_port *devlink_port = &dyn_port->devlink_port;
	struct ice_pf *pf = dyn_port->pf;

	ice_deactivate_dynamic_port(dyn_port);

	xa_erase(&pf->sf_nums, devlink_port->attrs.pci_sf.sf);
	ice_eswitch_detach_sf(pf, dyn_port);
	ice_vsi_free(dyn_port->vsi);
	xa_erase(&pf->dyn_ports, dyn_port->vsi->idx);
	kfree(dyn_port);
}

/**
 * ice_dealloc_all_dynamic_ports - Deallocate all dynamic devlink ports
 * @pf: pointer to the pf structure
 */
void ice_dealloc_all_dynamic_ports(struct ice_pf *pf)
{
	struct ice_dynamic_port *dyn_port;
	unsigned long index;

	xa_for_each(&pf->dyn_ports, index, dyn_port)
		ice_dealloc_dynamic_port(dyn_port);
}

/**
 * ice_devlink_port_new_check_attr - Check that new port attributes are valid
 * @pf: pointer to the PF structure
 * @new_attr: the attributes for the new port
 * @extack: extack for reporting error messages
 *
 * Check that the attributes for the new port are valid before continuing to
 * allocate the devlink port.
 *
 * Return: zero on success or an error code on failure.
 */
static int
ice_devlink_port_new_check_attr(struct ice_pf *pf,
				const struct devlink_port_new_attrs *new_attr,
				struct netlink_ext_ack *extack)
{
	if (new_attr->flavour != DEVLINK_PORT_FLAVOUR_PCI_SF) {
		NL_SET_ERR_MSG_MOD(extack, "Flavour other than pcisf is not supported");
		return -EOPNOTSUPP;
	}

	if (new_attr->controller_valid) {
		NL_SET_ERR_MSG_MOD(extack, "Setting controller is not supported");
		return -EOPNOTSUPP;
	}

	if (new_attr->port_index_valid) {
		NL_SET_ERR_MSG_MOD(extack, "Driver does not support user defined port index assignment");
		return -EOPNOTSUPP;
	}

	if (new_attr->pfnum != pf->hw.pf_id) {
		NL_SET_ERR_MSG_MOD(extack, "Incorrect pfnum supplied");
		return -EINVAL;
	}

	if (!pci_msix_can_alloc_dyn(pf->pdev)) {
		NL_SET_ERR_MSG_MOD(extack, "Dynamic MSIX-X interrupt allocation is not supported");
		return -EOPNOTSUPP;
	}

	return 0;
}

/**
 * ice_devlink_port_del - devlink handler for port delete
 * @devlink: pointer to devlink
 * @port: devlink port to be deleted
 * @extack: pointer to extack
 *
 * Deletes devlink port and deallocates all resources associated with
 * created subfunction.
 *
 * Return: zero on success or an error code on failure.
 */
static int
ice_devlink_port_del(struct devlink *devlink, struct devlink_port *port,
		     struct netlink_ext_ack *extack)
{
	struct ice_dynamic_port *dyn_port;

	dyn_port = ice_devlink_port_to_dyn(port);
	ice_dealloc_dynamic_port(dyn_port);

	return 0;
}

/**
 * ice_devlink_port_fn_hw_addr_set - devlink handler for mac address set
 * @port: pointer to devlink port
 * @hw_addr: hw address to set
 * @hw_addr_len: hw address length
 * @extack: extack for reporting error messages
 *
 * Sets mac address for the port, verifies arguments and copies address
 * to the subfunction structure.
 *
 * Return: zero on success or an error code on failure.
 */
static int
ice_devlink_port_fn_hw_addr_set(struct devlink_port *port, const u8 *hw_addr,
				int hw_addr_len,
				struct netlink_ext_ack *extack)
{
	struct ice_dynamic_port *dyn_port;

	dyn_port = ice_devlink_port_to_dyn(port);

	if (dyn_port->attached) {
		NL_SET_ERR_MSG_MOD(extack,
				   "Ethernet address can be change only in detached state");
		return -EBUSY;
	}

	if (hw_addr_len != ETH_ALEN || !is_valid_ether_addr(hw_addr)) {
		NL_SET_ERR_MSG_MOD(extack, "Invalid ethernet address");
		return -EADDRNOTAVAIL;
	}

	ether_addr_copy(dyn_port->hw_addr, hw_addr);

	return 0;
}

/**
 * ice_devlink_port_fn_hw_addr_get - devlink handler for mac address get
 * @port: pointer to devlink port
 * @hw_addr: hw address to set
 * @hw_addr_len: hw address length
 * @extack: extack for reporting error messages
 *
 * Returns mac address for the port.
 *
 * Return: zero on success or an error code on failure.
 */
static int
ice_devlink_port_fn_hw_addr_get(struct devlink_port *port, u8 *hw_addr,
				int *hw_addr_len,
				struct netlink_ext_ack *extack)
{
	struct ice_dynamic_port *dyn_port;

	dyn_port = ice_devlink_port_to_dyn(port);

	ether_addr_copy(hw_addr, dyn_port->hw_addr);
	*hw_addr_len = ETH_ALEN;

	return 0;
}

/**
 * ice_devlink_port_fn_state_set - devlink handler for port state set
 * @port: pointer to devlink port
 * @state: state to set
 * @extack: extack for reporting error messages
 *
 * Activates or deactivates the port.
 *
 * Return: zero on success or an error code on failure.
 */
static int
ice_devlink_port_fn_state_set(struct devlink_port *port,
			      enum devlink_port_fn_state state,
			      struct netlink_ext_ack *extack)
{
	struct ice_dynamic_port *dyn_port;

	dyn_port = ice_devlink_port_to_dyn(port);

	switch (state) {
	case DEVLINK_PORT_FN_STATE_ACTIVE:
		return ice_activate_dynamic_port(dyn_port, extack);

	case DEVLINK_PORT_FN_STATE_INACTIVE:
		ice_deactivate_dynamic_port(dyn_port);
		break;
	}

	return 0;
}

/**
 * ice_devlink_port_fn_state_get - devlink handler for port state get
 * @port: pointer to devlink port
 * @state: admin configured state of the port
 * @opstate: current port operational state
 * @extack: extack for reporting error messages
 *
 * Gets port state.
 *
 * Return: zero on success or an error code on failure.
 */
static int
ice_devlink_port_fn_state_get(struct devlink_port *port,
			      enum devlink_port_fn_state *state,
			      enum devlink_port_fn_opstate *opstate,
			      struct netlink_ext_ack *extack)
{
	struct ice_dynamic_port *dyn_port;

	dyn_port = ice_devlink_port_to_dyn(port);

	if (dyn_port->active)
		*state = DEVLINK_PORT_FN_STATE_ACTIVE;
	else
		*state = DEVLINK_PORT_FN_STATE_INACTIVE;

	if (dyn_port->attached)
		*opstate = DEVLINK_PORT_FN_OPSTATE_ATTACHED;
	else
		*opstate = DEVLINK_PORT_FN_OPSTATE_DETACHED;

	return 0;
}

static const struct devlink_port_ops ice_devlink_port_sf_ops = {
	.port_del = ice_devlink_port_del,
	.port_fn_hw_addr_get = ice_devlink_port_fn_hw_addr_get,
	.port_fn_hw_addr_set = ice_devlink_port_fn_hw_addr_set,
	.port_fn_state_get = ice_devlink_port_fn_state_get,
	.port_fn_state_set = ice_devlink_port_fn_state_set,
};

/**
 * ice_reserve_sf_num - Reserve a subfunction number for this port
 * @pf: pointer to the pf structure
 * @new_attr: devlink port attributes requested
 * @extack: extack for reporting error messages
 * @sfnum: on success, the sf number reserved
 *
 * Reserve a subfunction number for this port. Only called for
 * DEVLINK_PORT_FLAVOUR_PCI_SF ports.
 *
 * Return: zero on success or an error code on failure.
 */
static int
ice_reserve_sf_num(struct ice_pf *pf,
		   const struct devlink_port_new_attrs *new_attr,
		   struct netlink_ext_ack *extack, u32 *sfnum)
{
	int err;

	/* If user didn't request an explicit number, pick one */
	if (!new_attr->sfnum_valid)
		return xa_alloc(&pf->sf_nums, sfnum, NULL, xa_limit_32b,
				GFP_KERNEL);

	/* Otherwise, check and use the number provided */
	err = xa_insert(&pf->sf_nums, new_attr->sfnum, NULL, GFP_KERNEL);
	if (err) {
		if (err == -EBUSY)
			NL_SET_ERR_MSG_MOD(extack, "Subfunction with given sfnum already exists");
		return err;
	}

	*sfnum = new_attr->sfnum;

	return 0;
}

/**
 * ice_devlink_create_sf_port - Register PCI subfunction devlink port
 * @dyn_port: the dynamic port instance structure for this subfunction
 *
 * Register PCI subfunction flavour devlink port for a dynamically added
 * subfunction port.
 *
 * Return: zero on success or an error code on failure.
 */
int ice_devlink_create_sf_port(struct ice_dynamic_port *dyn_port)
{
	struct devlink_port_attrs attrs = {};
	struct devlink_port *devlink_port;
	struct devlink *devlink;
	struct ice_vsi *vsi;
	struct ice_pf *pf;

	vsi = dyn_port->vsi;
	pf = dyn_port->pf;

	devlink_port = &dyn_port->devlink_port;

	attrs.flavour = DEVLINK_PORT_FLAVOUR_PCI_SF;
	attrs.pci_sf.pf = pf->hw.pf_id;
	attrs.pci_sf.sf = dyn_port->sfnum;

	devlink_port_attrs_set(devlink_port, &attrs);
	devlink = priv_to_devlink(pf);

	return devl_port_register_with_ops(devlink, devlink_port, vsi->idx,
					   &ice_devlink_port_sf_ops);
}

/**
 * ice_devlink_destroy_sf_port - Destroy the devlink_port for this SF
 * @dyn_port: the dynamic port instance structure for this subfunction
 *
 * Unregisters the devlink_port structure associated with this SF.
 */
void ice_devlink_destroy_sf_port(struct ice_dynamic_port *dyn_port)
{
	devl_rate_leaf_destroy(&dyn_port->devlink_port);
	devl_port_unregister(&dyn_port->devlink_port);
}

/**
 * ice_alloc_dynamic_port - Allocate new dynamic port
 * @pf: pointer to the pf structure
 * @new_attr: devlink port attributes requested
 * @extack: extack for reporting error messages
 * @devlink_port: index of newly created devlink port
 *
 * Allocate a new dynamic port instance and prepare it for configuration
 * with devlink.
 *
 * Return: zero on success or an error code on failure.
 */
static int
ice_alloc_dynamic_port(struct ice_pf *pf,
		       const struct devlink_port_new_attrs *new_attr,
		       struct netlink_ext_ack *extack,
		       struct devlink_port **devlink_port)
{
	struct ice_dynamic_port *dyn_port;
	struct ice_vsi *vsi;
	u32 sfnum;
	int err;

	err = ice_reserve_sf_num(pf, new_attr, extack, &sfnum);
	if (err)
		return err;

	dyn_port = kzalloc(sizeof(*dyn_port), GFP_KERNEL);
	if (!dyn_port) {
		err = -ENOMEM;
		goto unroll_reserve_sf_num;
	}

	vsi = ice_vsi_alloc(pf);
	if (!vsi) {
		NL_SET_ERR_MSG_MOD(extack, "Unable to allocate VSI");
		err = -ENOMEM;
		goto unroll_dyn_port_alloc;
	}

	dyn_port->vsi = vsi;
	dyn_port->pf = pf;
	dyn_port->sfnum = sfnum;
	eth_random_addr(dyn_port->hw_addr);

	err = xa_insert(&pf->dyn_ports, vsi->idx, dyn_port, GFP_KERNEL);
	if (err) {
		NL_SET_ERR_MSG_MOD(extack, "Port index reservation failed");
		goto unroll_vsi_alloc;
	}

	err = ice_eswitch_attach_sf(pf, dyn_port);
	if (err) {
		NL_SET_ERR_MSG_MOD(extack, "Failed to attach SF to eswitch");
		goto unroll_xa_insert;
	}

	*devlink_port = &dyn_port->devlink_port;

	return 0;

unroll_xa_insert:
	xa_erase(&pf->dyn_ports, vsi->idx);
unroll_vsi_alloc:
	ice_vsi_free(vsi);
unroll_dyn_port_alloc:
	kfree(dyn_port);
unroll_reserve_sf_num:
	xa_erase(&pf->sf_nums, sfnum);

	return err;
}

/**
 * ice_devlink_port_new - devlink handler for the new port
 * @devlink: pointer to devlink
 * @new_attr: pointer to the port new attributes
 * @extack: extack for reporting error messages
 * @devlink_port: pointer to a new port
 *
 * Creates new devlink port, checks new port attributes and reject
 * any unsupported parameters, allocates new subfunction for that port.
 *
 * Return: zero on success or an error code on failure.
 */
int
ice_devlink_port_new(struct devlink *devlink,
		     const struct devlink_port_new_attrs *new_attr,
		     struct netlink_ext_ack *extack,
		     struct devlink_port **devlink_port)
{
	struct ice_pf *pf = devlink_priv(devlink);
	int err;

	err = ice_devlink_port_new_check_attr(pf, new_attr, extack);
	if (err)
		return err;

	return ice_alloc_dynamic_port(pf, new_attr, extack, devlink_port);
}
+46 −0
Original line number Diff line number Diff line
@@ -4,9 +4,55 @@
#ifndef _DEVLINK_PORT_H_
#define _DEVLINK_PORT_H_

#include "../ice.h"
#include "../ice_sf_eth.h"

/**
 * struct ice_dynamic_port - Track dynamically added devlink port instance
 * @hw_addr: the HW address for this port
 * @active: true if the port has been activated
 * @attached: true it the prot is attached
 * @devlink_port: the associated devlink port structure
 * @pf: pointer to the PF private structure
 * @vsi: the VSI associated with this port
 * @repr_id: the representor ID
 * @sfnum: the subfunction ID
 * @sf_dev: pointer to the subfunction device
 *
 * An instance of a dynamically added devlink port. Each port flavour
 */
struct ice_dynamic_port {
	u8 hw_addr[ETH_ALEN];
	u8 active: 1;
	u8 attached: 1;
	struct devlink_port devlink_port;
	struct ice_pf *pf;
	struct ice_vsi *vsi;
	unsigned long repr_id;
	u32 sfnum;
	/* Flavour-specific implementation data */
	union {
		struct ice_sf_dev *sf_dev;
	};
};

void ice_dealloc_all_dynamic_ports(struct ice_pf *pf);

int ice_devlink_create_pf_port(struct ice_pf *pf);
void ice_devlink_destroy_pf_port(struct ice_pf *pf);
int ice_devlink_create_vf_port(struct ice_vf *vf);
void ice_devlink_destroy_vf_port(struct ice_vf *vf);
int ice_devlink_create_sf_port(struct ice_dynamic_port *dyn_port);
void ice_devlink_destroy_sf_port(struct ice_dynamic_port *dyn_port);
int ice_devlink_create_sf_dev_port(struct ice_sf_dev *sf_dev);
void ice_devlink_destroy_sf_dev_port(struct ice_sf_dev *sf_dev);

#define ice_devlink_port_to_dyn(port) \
	container_of(port, struct ice_dynamic_port, devlink_port)

int
ice_devlink_port_new(struct devlink *devlink,
		     const struct devlink_port_new_attrs *new_attr,
		     struct netlink_ext_ack *extack,
		     struct devlink_port **devlink_port);
#endif /* _DEVLINK_PORT_H_ */
Loading