Commit eda69d65 authored by Piotr Raczynski's avatar Piotr Raczynski Committed by Tony Nguyen
Browse files

ice: add basic devlink subfunctions support



Implement devlink port handlers responsible for ethernet type devlink
subfunctions. Create subfunction devlink port and setup all resources
needed for a subfunction netdev to operate. Configure new VSI for each
new subfunction, initialize and configure interrupts and Tx/Rx resources.
Set correct MAC filters and create new netdev.

For now, subfunction is limited to only one Tx/Rx queue pair.

Only allocate new subfunction VSI with devlink port new command.
Allocate and free subfunction MSIX interrupt vectors using new API
calls with pci_msix_alloc_irq_at and pci_msix_free_irq.

Support both automatic and manual subfunction numbers. If no subfunction
number is provided, use xa_alloc to pick a number automatically. This
will find the first free index and use that as the number. This reduces
burden on users in the simple case where a specific number is not
required. It may also be slightly faster to check that a number exists
since xarray lookup should be faster than a linear scan of the dyn_ports
xarray.

Co-developed-by: default avatarJacob Keller <jacob.e.keller@intel.com>
Signed-off-by: default avatarJacob Keller <jacob.e.keller@intel.com>
Signed-off-by: default avatarPiotr Raczynski <piotr.raczynski@intel.com>
Signed-off-by: default avatarMichal Swiatkowski <michal.swiatkowski@linux.intel.com>
Tested-by: default avatarRafal Romanowski <rafal.romanowski@intel.com>
Signed-off-by: default avatarTony Nguyen <anthony.l.nguyen@intel.com>
parent 004688c4
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
#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"
@@ -1277,6 +1278,8 @@ 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 int
+288 −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,288 @@ void ice_devlink_destroy_vf_port(struct ice_vf *vf)
	devl_rate_leaf_destroy(&vf->devlink_port);
	devl_port_unregister(&vf->devlink_port);
}

/**
 * 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;

	xa_erase(&pf->sf_nums, devlink_port->attrs.pci_sf.sf);
	devl_port_unregister(devlink_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;
}

static const struct devlink_port_ops ice_devlink_port_sf_ops = {
	.port_del = ice_devlink_port_del,
};

/**
 * 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_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_devlink_create_sf_port(dyn_port);
	if (err) {
		NL_SET_ERR_MSG_MOD(extack, "Port registration failed");
		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);
}
+34 −0
Original line number Diff line number Diff line
@@ -4,9 +4,43 @@
#ifndef _DEVLINK_PORT_H_
#define _DEVLINK_PORT_H_

#include "../ice.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
 * @devlink_port: the associated devlink port structure
 * @pf: pointer to the PF private structure
 * @vsi: the VSI associated with this port
 * @sfnum: the subfunction ID
 *
 * An instance of a dynamically added devlink port. Each port flavour
 */
struct ice_dynamic_port {
	u8 hw_addr[ETH_ALEN];
	u8 active: 1;
	struct devlink_port devlink_port;
	struct ice_pf *pf;
	struct ice_vsi *vsi;
	u32 sfnum;
};

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);

#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_ */
+4 −0
Original line number Diff line number Diff line
@@ -652,6 +652,9 @@ struct ice_pf {
	struct ice_eswitch eswitch;
	struct ice_esw_br_port *br_port;

	struct xarray dyn_ports;
	struct xarray sf_nums;

#define ICE_INVALID_AGG_NODE_ID		0
#define ICE_PF_AGG_NODE_ID_START	1
#define ICE_MAX_PF_AGG_NODES		32
@@ -918,6 +921,7 @@ int ice_vsi_open(struct ice_vsi *vsi);
void ice_set_ethtool_ops(struct net_device *netdev);
void ice_set_ethtool_repr_ops(struct net_device *netdev);
void ice_set_ethtool_safe_mode_ops(struct net_device *netdev);
void ice_set_ethtool_sf_ops(struct net_device *netdev);
u16 ice_get_avail_txq_count(struct ice_pf *pf);
u16 ice_get_avail_rxq_count(struct ice_pf *pf);
int ice_vsi_recfg_qs(struct ice_vsi *vsi, int new_rx, int new_tx, bool locked);
+3 −2
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
#include "ice_lib.h"
#include "ice_fltr.h"
#include "ice_dcb_lib.h"
#include "ice_type.h"
#include "ice_vsi_vlan_ops.h"

/**
@@ -432,7 +433,7 @@ static int ice_vsi_alloc_ring_stats(struct ice_vsi *vsi)
 * This deallocates the VSI's queue resources, removes it from the PF's
 * VSI array if necessary, and deallocates the VSI
 */
static void ice_vsi_free(struct ice_vsi *vsi)
void ice_vsi_free(struct ice_vsi *vsi)
{
	struct ice_pf *pf = NULL;
	struct device *dev;
@@ -605,7 +606,7 @@ ice_vsi_alloc_def(struct ice_vsi *vsi, struct ice_channel *ch)
 *
 * returns a pointer to a VSI on success, NULL on failure.
 */
static struct ice_vsi *ice_vsi_alloc(struct ice_pf *pf)
struct ice_vsi *ice_vsi_alloc(struct ice_pf *pf)
{
	struct device *dev = ice_pf_to_dev(pf);
	struct ice_vsi *vsi = NULL;
Loading