Merge branch '100GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue

Tony Nguyen says:

====================
Intel Wired LAN Driver Updates 2024-04-01 (ice)

This series contains updates to ice driver only.

Michal Schmidt changes flow for gettimex64 to use host-side spinlock
rather than hardware semaphore for lighter-weight locking.

Steven adds ability for switch recipes to be re-used when firmware
supports it.

Thorsten Blum removes unwanted newlines in netlink messaging.

Michal Swiatkowski and Piotr re-organize devlink related code; renaming,
moving, and consolidating it to a single location. Michal also
simplifies the devlink init and cleanup path to occur under a single
lock call.

* '100GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue:
  ice: hold devlink lock for whole init/cleanup
  ice: move devlink port code to a separate file
  ice: move ice_devlink.[ch] to devlink folder
  ice: Remove newlines in NL_SET_ERR_MSG_MOD
  ice: Add switch recipe reusing feature
  ice: fold ice_ptp_read_time into ice_ptp_gettimex64
  ice: avoid the PTP hardware semaphore in gettimex64 path
  ice: add ice_adapter for shared data across PFs on the same NIC
====================

Link: https://lore.kernel.org/r/20240401172421.1401696-1-anthony.l.nguyen@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2024-04-02 19:15:34 -07:00
commit 84c41dcaae
20 changed files with 814 additions and 500 deletions

View File

@ -5,6 +5,7 @@
# Makefile for the Intel(R) Ethernet Connection E800 Series Linux Driver
#
subdir-ccflags-y += -I$(src)
obj-$(CONFIG_ICE) += ice.o
ice-y := ice_main.o \
@ -28,7 +29,8 @@ ice-y := ice_main.o \
ice_flex_pipe.o \
ice_flow.o \
ice_idc.o \
ice_devlink.o \
devlink/devlink.o \
devlink/devlink_port.o \
ice_ddp.o \
ice_fw_update.o \
ice_lag.o \
@ -36,7 +38,8 @@ ice-y := ice_main.o \
ice_repr.o \
ice_tc_lib.o \
ice_fwlog.o \
ice_debugfs.o
ice_debugfs.o \
ice_adapter.o
ice-$(CONFIG_PCI_IOV) += \
ice_sriov.o \
ice_virtchnl.o \

View File

@ -5,13 +5,11 @@
#include "ice.h"
#include "ice_lib.h"
#include "ice_devlink.h"
#include "devlink.h"
#include "ice_eswitch.h"
#include "ice_fw_update.h"
#include "ice_dcb_lib.h"
static int ice_active_port_option = -1;
/* context for devlink info version reporting */
struct ice_info_ctx {
char buf[128];
@ -478,17 +476,17 @@ ice_devlink_reload_down(struct devlink *devlink, bool netns_change,
case DEVLINK_RELOAD_ACTION_DRIVER_REINIT:
if (ice_is_eswitch_mode_switchdev(pf)) {
NL_SET_ERR_MSG_MOD(extack,
"Go to legacy mode before doing reinit\n");
"Go to legacy mode before doing reinit");
return -EOPNOTSUPP;
}
if (ice_is_adq_active(pf)) {
NL_SET_ERR_MSG_MOD(extack,
"Turn off ADQ before doing reinit\n");
"Turn off ADQ before doing reinit");
return -EOPNOTSUPP;
}
if (ice_has_vfs(pf)) {
NL_SET_ERR_MSG_MOD(extack,
"Remove all VFs before doing reinit\n");
"Remove all VFs before doing reinit");
return -EOPNOTSUPP;
}
ice_devlink_reinit_down(pf);
@ -525,251 +523,6 @@ ice_devlink_reload_empr_finish(struct ice_pf *pf,
return 0;
}
/**
* ice_devlink_port_opt_speed_str - convert speed to a string
* @speed: speed value
*/
static const char *ice_devlink_port_opt_speed_str(u8 speed)
{
switch (speed & ICE_AQC_PORT_OPT_MAX_LANE_M) {
case ICE_AQC_PORT_OPT_MAX_LANE_100M:
return "0.1";
case ICE_AQC_PORT_OPT_MAX_LANE_1G:
return "1";
case ICE_AQC_PORT_OPT_MAX_LANE_2500M:
return "2.5";
case ICE_AQC_PORT_OPT_MAX_LANE_5G:
return "5";
case ICE_AQC_PORT_OPT_MAX_LANE_10G:
return "10";
case ICE_AQC_PORT_OPT_MAX_LANE_25G:
return "25";
case ICE_AQC_PORT_OPT_MAX_LANE_50G:
return "50";
case ICE_AQC_PORT_OPT_MAX_LANE_100G:
return "100";
}
return "-";
}
#define ICE_PORT_OPT_DESC_LEN 50
/**
* ice_devlink_port_options_print - Print available port split options
* @pf: the PF to print split port options
*
* Prints a table with available port split options and max port speeds
*/
static void ice_devlink_port_options_print(struct ice_pf *pf)
{
u8 i, j, options_count, cnt, speed, pending_idx, active_idx;
struct ice_aqc_get_port_options_elem *options, *opt;
struct device *dev = ice_pf_to_dev(pf);
bool active_valid, pending_valid;
char desc[ICE_PORT_OPT_DESC_LEN];
const char *str;
int status;
options = kcalloc(ICE_AQC_PORT_OPT_MAX * ICE_MAX_PORT_PER_PCI_DEV,
sizeof(*options), GFP_KERNEL);
if (!options)
return;
for (i = 0; i < ICE_MAX_PORT_PER_PCI_DEV; i++) {
opt = options + i * ICE_AQC_PORT_OPT_MAX;
options_count = ICE_AQC_PORT_OPT_MAX;
active_valid = 0;
status = ice_aq_get_port_options(&pf->hw, opt, &options_count,
i, true, &active_idx,
&active_valid, &pending_idx,
&pending_valid);
if (status) {
dev_dbg(dev, "Couldn't read port option for port %d, err %d\n",
i, status);
goto err;
}
}
dev_dbg(dev, "Available port split options and max port speeds (Gbps):\n");
dev_dbg(dev, "Status Split Quad 0 Quad 1\n");
dev_dbg(dev, " count L0 L1 L2 L3 L4 L5 L6 L7\n");
for (i = 0; i < options_count; i++) {
cnt = 0;
if (i == ice_active_port_option)
str = "Active";
else if ((i == pending_idx) && pending_valid)
str = "Pending";
else
str = "";
cnt += snprintf(&desc[cnt], ICE_PORT_OPT_DESC_LEN - cnt,
"%-8s", str);
cnt += snprintf(&desc[cnt], ICE_PORT_OPT_DESC_LEN - cnt,
"%-6u", options[i].pmd);
for (j = 0; j < ICE_MAX_PORT_PER_PCI_DEV; ++j) {
speed = options[i + j * ICE_AQC_PORT_OPT_MAX].max_lane_speed;
str = ice_devlink_port_opt_speed_str(speed);
cnt += snprintf(&desc[cnt], ICE_PORT_OPT_DESC_LEN - cnt,
"%3s ", str);
}
dev_dbg(dev, "%s\n", desc);
}
err:
kfree(options);
}
/**
* ice_devlink_aq_set_port_option - Send set port option admin queue command
* @pf: the PF to print split port options
* @option_idx: selected port option
* @extack: extended netdev ack structure
*
* Sends set port option admin queue command with selected port option and
* calls NVM write activate.
*/
static int
ice_devlink_aq_set_port_option(struct ice_pf *pf, u8 option_idx,
struct netlink_ext_ack *extack)
{
struct device *dev = ice_pf_to_dev(pf);
int status;
status = ice_aq_set_port_option(&pf->hw, 0, true, option_idx);
if (status) {
dev_dbg(dev, "ice_aq_set_port_option, err %d aq_err %d\n",
status, pf->hw.adminq.sq_last_status);
NL_SET_ERR_MSG_MOD(extack, "Port split request failed");
return -EIO;
}
status = ice_acquire_nvm(&pf->hw, ICE_RES_WRITE);
if (status) {
dev_dbg(dev, "ice_acquire_nvm failed, err %d aq_err %d\n",
status, pf->hw.adminq.sq_last_status);
NL_SET_ERR_MSG_MOD(extack, "Failed to acquire NVM semaphore");
return -EIO;
}
status = ice_nvm_write_activate(&pf->hw, ICE_AQC_NVM_ACTIV_REQ_EMPR, NULL);
if (status) {
dev_dbg(dev, "ice_nvm_write_activate failed, err %d aq_err %d\n",
status, pf->hw.adminq.sq_last_status);
NL_SET_ERR_MSG_MOD(extack, "Port split request failed to save data");
ice_release_nvm(&pf->hw);
return -EIO;
}
ice_release_nvm(&pf->hw);
NL_SET_ERR_MSG_MOD(extack, "Reboot required to finish port split");
return 0;
}
/**
* ice_devlink_port_split - .port_split devlink handler
* @devlink: devlink instance structure
* @port: devlink port structure
* @count: number of ports to split to
* @extack: extended netdev ack structure
*
* Callback for the devlink .port_split operation.
*
* Unfortunately, the devlink expression of available options is limited
* to just a number, so search for an FW port option which supports
* the specified number. As there could be multiple FW port options with
* the same port split count, allow switching between them. When the same
* port split count request is issued again, switch to the next FW port
* option with the same port split count.
*
* Return: zero on success or an error code on failure.
*/
static int
ice_devlink_port_split(struct devlink *devlink, struct devlink_port *port,
unsigned int count, struct netlink_ext_ack *extack)
{
struct ice_aqc_get_port_options_elem options[ICE_AQC_PORT_OPT_MAX];
u8 i, j, active_idx, pending_idx, new_option;
struct ice_pf *pf = devlink_priv(devlink);
u8 option_count = ICE_AQC_PORT_OPT_MAX;
struct device *dev = ice_pf_to_dev(pf);
bool active_valid, pending_valid;
int status;
status = ice_aq_get_port_options(&pf->hw, options, &option_count,
0, true, &active_idx, &active_valid,
&pending_idx, &pending_valid);
if (status) {
dev_dbg(dev, "Couldn't read port split options, err = %d\n",
status);
NL_SET_ERR_MSG_MOD(extack, "Failed to get available port split options");
return -EIO;
}
new_option = ICE_AQC_PORT_OPT_MAX;
active_idx = pending_valid ? pending_idx : active_idx;
for (i = 1; i <= option_count; i++) {
/* In order to allow switching between FW port options with
* the same port split count, search for a new option starting
* from the active/pending option (with array wrap around).
*/
j = (active_idx + i) % option_count;
if (count == options[j].pmd) {
new_option = j;
break;
}
}
if (new_option == active_idx) {
dev_dbg(dev, "request to split: count: %u is already set and there are no other options\n",
count);
NL_SET_ERR_MSG_MOD(extack, "Requested split count is already set");
ice_devlink_port_options_print(pf);
return -EINVAL;
}
if (new_option == ICE_AQC_PORT_OPT_MAX) {
dev_dbg(dev, "request to split: count: %u not found\n", count);
NL_SET_ERR_MSG_MOD(extack, "Port split requested unsupported port config");
ice_devlink_port_options_print(pf);
return -EINVAL;
}
status = ice_devlink_aq_set_port_option(pf, new_option, extack);
if (status)
return status;
ice_devlink_port_options_print(pf);
return 0;
}
/**
* ice_devlink_port_unsplit - .port_unsplit devlink handler
* @devlink: devlink instance structure
* @port: devlink port structure
* @extack: extended netdev ack structure
*
* Callback for the devlink .port_unsplit operation.
* Calls ice_devlink_port_split with split count set to 1.
* There could be no FW option available with split count 1.
*
* Return: zero on success or an error code on failure.
*/
static int
ice_devlink_port_unsplit(struct devlink *devlink, struct devlink_port *port,
struct netlink_ext_ack *extack)
{
return ice_devlink_port_split(devlink, port, 1, extack);
}
/**
* ice_tear_down_devlink_rate_tree - removes devlink-rate exported tree
* @pf: pf struct
@ -1534,7 +1287,7 @@ void ice_devlink_register(struct ice_pf *pf)
{
struct devlink *devlink = priv_to_devlink(pf);
devlink_register(devlink);
devl_register(devlink);
}
/**
@ -1545,197 +1298,21 @@ void ice_devlink_register(struct ice_pf *pf)
*/
void ice_devlink_unregister(struct ice_pf *pf)
{
devlink_unregister(priv_to_devlink(pf));
}
/**
* ice_devlink_set_switch_id - Set unique switch id based on pci dsn
* @pf: the PF to create a devlink port for
* @ppid: struct with switch id information
*/
static void
ice_devlink_set_switch_id(struct ice_pf *pf, struct netdev_phys_item_id *ppid)
{
struct pci_dev *pdev = pf->pdev;
u64 id;
id = pci_get_dsn(pdev);
ppid->id_len = sizeof(id);
put_unaligned_be64(id, &ppid->id);
devl_unregister(priv_to_devlink(pf));
}
int ice_devlink_register_params(struct ice_pf *pf)
{
struct devlink *devlink = priv_to_devlink(pf);
return devlink_params_register(devlink, ice_devlink_params,
ARRAY_SIZE(ice_devlink_params));
return devl_params_register(devlink, ice_devlink_params,
ARRAY_SIZE(ice_devlink_params));
}
void ice_devlink_unregister_params(struct ice_pf *pf)
{
devlink_params_unregister(priv_to_devlink(pf), ice_devlink_params,
ARRAY_SIZE(ice_devlink_params));
}
/**
* ice_devlink_set_port_split_options - Set port split options
* @pf: the PF to set port split options
* @attrs: devlink attributes
*
* Sets devlink port split options based on available FW port options
*/
static void
ice_devlink_set_port_split_options(struct ice_pf *pf,
struct devlink_port_attrs *attrs)
{
struct ice_aqc_get_port_options_elem options[ICE_AQC_PORT_OPT_MAX];
u8 i, active_idx, pending_idx, option_count = ICE_AQC_PORT_OPT_MAX;
bool active_valid, pending_valid;
int status;
status = ice_aq_get_port_options(&pf->hw, options, &option_count,
0, true, &active_idx, &active_valid,
&pending_idx, &pending_valid);
if (status) {
dev_dbg(ice_pf_to_dev(pf), "Couldn't read port split options, err = %d\n",
status);
return;
}
/* find the biggest available port split count */
for (i = 0; i < option_count; i++)
attrs->lanes = max_t(int, attrs->lanes, options[i].pmd);
attrs->splittable = attrs->lanes ? 1 : 0;
ice_active_port_option = active_idx;
}
static const struct devlink_port_ops ice_devlink_port_ops = {
.port_split = ice_devlink_port_split,
.port_unsplit = ice_devlink_port_unsplit,
};
/**
* ice_devlink_create_pf_port - Create a devlink port for this PF
* @pf: the PF to create a devlink port for
*
* Create and register a devlink_port for this PF.
* This function has to be called under devl_lock.
*
* Return: zero on success or an error code on failure.
*/
int ice_devlink_create_pf_port(struct ice_pf *pf)
{
struct devlink_port_attrs attrs = {};
struct devlink_port *devlink_port;
struct devlink *devlink;
struct ice_vsi *vsi;
struct device *dev;
int err;
devlink = priv_to_devlink(pf);
dev = ice_pf_to_dev(pf);
devlink_port = &pf->devlink_port;
vsi = ice_get_main_vsi(pf);
if (!vsi)
return -EIO;
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
attrs.phys.port_number = pf->hw.bus.func;
/* As FW supports only port split options for whole device,
* set port split options only for first PF.
*/
if (pf->hw.pf_id == 0)
ice_devlink_set_port_split_options(pf, &attrs);
ice_devlink_set_switch_id(pf, &attrs.switch_id);
devlink_port_attrs_set(devlink_port, &attrs);
err = devl_port_register_with_ops(devlink, devlink_port, vsi->idx,
&ice_devlink_port_ops);
if (err) {
dev_err(dev, "Failed to create devlink port for PF %d, error %d\n",
pf->hw.pf_id, err);
return err;
}
return 0;
}
/**
* ice_devlink_destroy_pf_port - Destroy the devlink_port for this PF
* @pf: the PF to cleanup
*
* Unregisters the devlink_port structure associated with this PF.
* This function has to be called under devl_lock.
*/
void ice_devlink_destroy_pf_port(struct ice_pf *pf)
{
devl_port_unregister(&pf->devlink_port);
}
/**
* ice_devlink_create_vf_port - Create a devlink port for this VF
* @vf: the VF to create a port for
*
* Create and register a devlink_port for this VF.
*
* Return: zero on success or an error code on failure.
*/
int ice_devlink_create_vf_port(struct ice_vf *vf)
{
struct devlink_port_attrs attrs = {};
struct devlink_port *devlink_port;
struct devlink *devlink;
struct ice_vsi *vsi;
struct device *dev;
struct ice_pf *pf;
int err;
pf = vf->pf;
dev = ice_pf_to_dev(pf);
devlink_port = &vf->devlink_port;
vsi = ice_get_vf_vsi(vf);
if (!vsi)
return -EINVAL;
attrs.flavour = DEVLINK_PORT_FLAVOUR_PCI_VF;
attrs.pci_vf.pf = pf->hw.bus.func;
attrs.pci_vf.vf = vf->vf_id;
ice_devlink_set_switch_id(pf, &attrs.switch_id);
devlink_port_attrs_set(devlink_port, &attrs);
devlink = priv_to_devlink(pf);
err = devlink_port_register(devlink, devlink_port, vsi->idx);
if (err) {
dev_err(dev, "Failed to create devlink port for VF %d, error %d\n",
vf->vf_id, err);
return err;
}
return 0;
}
/**
* ice_devlink_destroy_vf_port - Destroy the devlink_port for this VF
* @vf: the VF to cleanup
*
* Unregisters the devlink_port structure associated with this VF.
*/
void ice_devlink_destroy_vf_port(struct ice_vf *vf)
{
devl_rate_leaf_destroy(&vf->devlink_port);
devlink_port_unregister(&vf->devlink_port);
devl_params_unregister(priv_to_devlink(pf), ice_devlink_params,
ARRAY_SIZE(ice_devlink_params));
}
#define ICE_DEVLINK_READ_BLK_SIZE (1024 * 1024)
@ -1976,8 +1553,8 @@ void ice_devlink_init_regions(struct ice_pf *pf)
u64 nvm_size, sram_size;
nvm_size = pf->hw.flash.flash_size;
pf->nvm_region = devlink_region_create(devlink, &ice_nvm_region_ops, 1,
nvm_size);
pf->nvm_region = devl_region_create(devlink, &ice_nvm_region_ops, 1,
nvm_size);
if (IS_ERR(pf->nvm_region)) {
dev_err(dev, "failed to create NVM devlink region, err %ld\n",
PTR_ERR(pf->nvm_region));
@ -1985,17 +1562,17 @@ void ice_devlink_init_regions(struct ice_pf *pf)
}
sram_size = pf->hw.flash.sr_words * 2u;
pf->sram_region = devlink_region_create(devlink, &ice_sram_region_ops,
1, sram_size);
pf->sram_region = devl_region_create(devlink, &ice_sram_region_ops,
1, sram_size);
if (IS_ERR(pf->sram_region)) {
dev_err(dev, "failed to create shadow-ram devlink region, err %ld\n",
PTR_ERR(pf->sram_region));
pf->sram_region = NULL;
}
pf->devcaps_region = devlink_region_create(devlink,
&ice_devcaps_region_ops, 10,
ICE_AQ_MAX_BUF_LEN);
pf->devcaps_region = devl_region_create(devlink,
&ice_devcaps_region_ops, 10,
ICE_AQ_MAX_BUF_LEN);
if (IS_ERR(pf->devcaps_region)) {
dev_err(dev, "failed to create device-caps devlink region, err %ld\n",
PTR_ERR(pf->devcaps_region));
@ -2012,11 +1589,11 @@ void ice_devlink_init_regions(struct ice_pf *pf)
void ice_devlink_destroy_regions(struct ice_pf *pf)
{
if (pf->nvm_region)
devlink_region_destroy(pf->nvm_region);
devl_region_destroy(pf->nvm_region);
if (pf->sram_region)
devlink_region_destroy(pf->sram_region);
devl_region_destroy(pf->sram_region);
if (pf->devcaps_region)
devlink_region_destroy(pf->devcaps_region);
devl_region_destroy(pf->devcaps_region);
}

View File

@ -0,0 +1,430 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2024, Intel Corporation. */
#include <linux/vmalloc.h>
#include "ice.h"
#include "devlink.h"
static int ice_active_port_option = -1;
/**
* ice_devlink_port_opt_speed_str - convert speed to a string
* @speed: speed value
*/
static const char *ice_devlink_port_opt_speed_str(u8 speed)
{
switch (speed & ICE_AQC_PORT_OPT_MAX_LANE_M) {
case ICE_AQC_PORT_OPT_MAX_LANE_100M:
return "0.1";
case ICE_AQC_PORT_OPT_MAX_LANE_1G:
return "1";
case ICE_AQC_PORT_OPT_MAX_LANE_2500M:
return "2.5";
case ICE_AQC_PORT_OPT_MAX_LANE_5G:
return "5";
case ICE_AQC_PORT_OPT_MAX_LANE_10G:
return "10";
case ICE_AQC_PORT_OPT_MAX_LANE_25G:
return "25";
case ICE_AQC_PORT_OPT_MAX_LANE_50G:
return "50";
case ICE_AQC_PORT_OPT_MAX_LANE_100G:
return "100";
}
return "-";
}
#define ICE_PORT_OPT_DESC_LEN 50
/**
* ice_devlink_port_options_print - Print available port split options
* @pf: the PF to print split port options
*
* Prints a table with available port split options and max port speeds
*/
static void ice_devlink_port_options_print(struct ice_pf *pf)
{
u8 i, j, options_count, cnt, speed, pending_idx, active_idx;
struct ice_aqc_get_port_options_elem *options, *opt;
struct device *dev = ice_pf_to_dev(pf);
bool active_valid, pending_valid;
char desc[ICE_PORT_OPT_DESC_LEN];
const char *str;
int status;
options = kcalloc(ICE_AQC_PORT_OPT_MAX * ICE_MAX_PORT_PER_PCI_DEV,
sizeof(*options), GFP_KERNEL);
if (!options)
return;
for (i = 0; i < ICE_MAX_PORT_PER_PCI_DEV; i++) {
opt = options + i * ICE_AQC_PORT_OPT_MAX;
options_count = ICE_AQC_PORT_OPT_MAX;
active_valid = 0;
status = ice_aq_get_port_options(&pf->hw, opt, &options_count,
i, true, &active_idx,
&active_valid, &pending_idx,
&pending_valid);
if (status) {
dev_dbg(dev, "Couldn't read port option for port %d, err %d\n",
i, status);
goto err;
}
}
dev_dbg(dev, "Available port split options and max port speeds (Gbps):\n");
dev_dbg(dev, "Status Split Quad 0 Quad 1\n");
dev_dbg(dev, " count L0 L1 L2 L3 L4 L5 L6 L7\n");
for (i = 0; i < options_count; i++) {
cnt = 0;
if (i == ice_active_port_option)
str = "Active";
else if ((i == pending_idx) && pending_valid)
str = "Pending";
else
str = "";
cnt += snprintf(&desc[cnt], ICE_PORT_OPT_DESC_LEN - cnt,
"%-8s", str);
cnt += snprintf(&desc[cnt], ICE_PORT_OPT_DESC_LEN - cnt,
"%-6u", options[i].pmd);
for (j = 0; j < ICE_MAX_PORT_PER_PCI_DEV; ++j) {
speed = options[i + j * ICE_AQC_PORT_OPT_MAX].max_lane_speed;
str = ice_devlink_port_opt_speed_str(speed);
cnt += snprintf(&desc[cnt], ICE_PORT_OPT_DESC_LEN - cnt,
"%3s ", str);
}
dev_dbg(dev, "%s\n", desc);
}
err:
kfree(options);
}
/**
* ice_devlink_aq_set_port_option - Send set port option admin queue command
* @pf: the PF to print split port options
* @option_idx: selected port option
* @extack: extended netdev ack structure
*
* Sends set port option admin queue command with selected port option and
* calls NVM write activate.
*/
static int
ice_devlink_aq_set_port_option(struct ice_pf *pf, u8 option_idx,
struct netlink_ext_ack *extack)
{
struct device *dev = ice_pf_to_dev(pf);
int status;
status = ice_aq_set_port_option(&pf->hw, 0, true, option_idx);
if (status) {
dev_dbg(dev, "ice_aq_set_port_option, err %d aq_err %d\n",
status, pf->hw.adminq.sq_last_status);
NL_SET_ERR_MSG_MOD(extack, "Port split request failed");
return -EIO;
}
status = ice_acquire_nvm(&pf->hw, ICE_RES_WRITE);
if (status) {
dev_dbg(dev, "ice_acquire_nvm failed, err %d aq_err %d\n",
status, pf->hw.adminq.sq_last_status);
NL_SET_ERR_MSG_MOD(extack, "Failed to acquire NVM semaphore");
return -EIO;
}
status = ice_nvm_write_activate(&pf->hw, ICE_AQC_NVM_ACTIV_REQ_EMPR, NULL);
if (status) {
dev_dbg(dev, "ice_nvm_write_activate failed, err %d aq_err %d\n",
status, pf->hw.adminq.sq_last_status);
NL_SET_ERR_MSG_MOD(extack, "Port split request failed to save data");
ice_release_nvm(&pf->hw);
return -EIO;
}
ice_release_nvm(&pf->hw);
NL_SET_ERR_MSG_MOD(extack, "Reboot required to finish port split");
return 0;
}
/**
* ice_devlink_port_split - .port_split devlink handler
* @devlink: devlink instance structure
* @port: devlink port structure
* @count: number of ports to split to
* @extack: extended netdev ack structure
*
* Callback for the devlink .port_split operation.
*
* Unfortunately, the devlink expression of available options is limited
* to just a number, so search for an FW port option which supports
* the specified number. As there could be multiple FW port options with
* the same port split count, allow switching between them. When the same
* port split count request is issued again, switch to the next FW port
* option with the same port split count.
*
* Return: zero on success or an error code on failure.
*/
static int
ice_devlink_port_split(struct devlink *devlink, struct devlink_port *port,
unsigned int count, struct netlink_ext_ack *extack)
{
struct ice_aqc_get_port_options_elem options[ICE_AQC_PORT_OPT_MAX];
u8 i, j, active_idx, pending_idx, new_option;
struct ice_pf *pf = devlink_priv(devlink);
u8 option_count = ICE_AQC_PORT_OPT_MAX;
struct device *dev = ice_pf_to_dev(pf);
bool active_valid, pending_valid;
int status;
status = ice_aq_get_port_options(&pf->hw, options, &option_count,
0, true, &active_idx, &active_valid,
&pending_idx, &pending_valid);
if (status) {
dev_dbg(dev, "Couldn't read port split options, err = %d\n",
status);
NL_SET_ERR_MSG_MOD(extack, "Failed to get available port split options");
return -EIO;
}
new_option = ICE_AQC_PORT_OPT_MAX;
active_idx = pending_valid ? pending_idx : active_idx;
for (i = 1; i <= option_count; i++) {
/* In order to allow switching between FW port options with
* the same port split count, search for a new option starting
* from the active/pending option (with array wrap around).
*/
j = (active_idx + i) % option_count;
if (count == options[j].pmd) {
new_option = j;
break;
}
}
if (new_option == active_idx) {
dev_dbg(dev, "request to split: count: %u is already set and there are no other options\n",
count);
NL_SET_ERR_MSG_MOD(extack, "Requested split count is already set");
ice_devlink_port_options_print(pf);
return -EINVAL;
}
if (new_option == ICE_AQC_PORT_OPT_MAX) {
dev_dbg(dev, "request to split: count: %u not found\n", count);
NL_SET_ERR_MSG_MOD(extack, "Port split requested unsupported port config");
ice_devlink_port_options_print(pf);
return -EINVAL;
}
status = ice_devlink_aq_set_port_option(pf, new_option, extack);
if (status)
return status;
ice_devlink_port_options_print(pf);
return 0;
}
/**
* ice_devlink_port_unsplit - .port_unsplit devlink handler
* @devlink: devlink instance structure
* @port: devlink port structure
* @extack: extended netdev ack structure
*
* Callback for the devlink .port_unsplit operation.
* Calls ice_devlink_port_split with split count set to 1.
* There could be no FW option available with split count 1.
*
* Return: zero on success or an error code on failure.
*/
static int
ice_devlink_port_unsplit(struct devlink *devlink, struct devlink_port *port,
struct netlink_ext_ack *extack)
{
return ice_devlink_port_split(devlink, port, 1, extack);
}
/**
* ice_devlink_set_port_split_options - Set port split options
* @pf: the PF to set port split options
* @attrs: devlink attributes
*
* Sets devlink port split options based on available FW port options
*/
static void
ice_devlink_set_port_split_options(struct ice_pf *pf,
struct devlink_port_attrs *attrs)
{
struct ice_aqc_get_port_options_elem options[ICE_AQC_PORT_OPT_MAX];
u8 i, active_idx, pending_idx, option_count = ICE_AQC_PORT_OPT_MAX;
bool active_valid, pending_valid;
int status;
status = ice_aq_get_port_options(&pf->hw, options, &option_count,
0, true, &active_idx, &active_valid,
&pending_idx, &pending_valid);
if (status) {
dev_dbg(ice_pf_to_dev(pf), "Couldn't read port split options, err = %d\n",
status);
return;
}
/* find the biggest available port split count */
for (i = 0; i < option_count; i++)
attrs->lanes = max_t(int, attrs->lanes, options[i].pmd);
attrs->splittable = attrs->lanes ? 1 : 0;
ice_active_port_option = active_idx;
}
static const struct devlink_port_ops ice_devlink_port_ops = {
.port_split = ice_devlink_port_split,
.port_unsplit = ice_devlink_port_unsplit,
};
/**
* ice_devlink_set_switch_id - Set unique switch id based on pci dsn
* @pf: the PF to create a devlink port for
* @ppid: struct with switch id information
*/
static void
ice_devlink_set_switch_id(struct ice_pf *pf, struct netdev_phys_item_id *ppid)
{
struct pci_dev *pdev = pf->pdev;
u64 id;
id = pci_get_dsn(pdev);
ppid->id_len = sizeof(id);
put_unaligned_be64(id, &ppid->id);
}
/**
* ice_devlink_create_pf_port - Create a devlink port for this PF
* @pf: the PF to create a devlink port for
*
* Create and register a devlink_port for this PF.
* This function has to be called under devl_lock.
*
* Return: zero on success or an error code on failure.
*/
int ice_devlink_create_pf_port(struct ice_pf *pf)
{
struct devlink_port_attrs attrs = {};
struct devlink_port *devlink_port;
struct devlink *devlink;
struct ice_vsi *vsi;
struct device *dev;
int err;
devlink = priv_to_devlink(pf);
dev = ice_pf_to_dev(pf);
devlink_port = &pf->devlink_port;
vsi = ice_get_main_vsi(pf);
if (!vsi)
return -EIO;
attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
attrs.phys.port_number = pf->hw.bus.func;
/* As FW supports only port split options for whole device,
* set port split options only for first PF.
*/
if (pf->hw.pf_id == 0)
ice_devlink_set_port_split_options(pf, &attrs);
ice_devlink_set_switch_id(pf, &attrs.switch_id);
devlink_port_attrs_set(devlink_port, &attrs);
err = devl_port_register_with_ops(devlink, devlink_port, vsi->idx,
&ice_devlink_port_ops);
if (err) {
dev_err(dev, "Failed to create devlink port for PF %d, error %d\n",
pf->hw.pf_id, err);
return err;
}
return 0;
}
/**
* ice_devlink_destroy_pf_port - Destroy the devlink_port for this PF
* @pf: the PF to cleanup
*
* Unregisters the devlink_port structure associated with this PF.
* This function has to be called under devl_lock.
*/
void ice_devlink_destroy_pf_port(struct ice_pf *pf)
{
devl_port_unregister(&pf->devlink_port);
}
/**
* ice_devlink_create_vf_port - Create a devlink port for this VF
* @vf: the VF to create a port for
*
* Create and register a devlink_port for this VF.
*
* Return: zero on success or an error code on failure.
*/
int ice_devlink_create_vf_port(struct ice_vf *vf)
{
struct devlink_port_attrs attrs = {};
struct devlink_port *devlink_port;
struct devlink *devlink;
struct ice_vsi *vsi;
struct device *dev;
struct ice_pf *pf;
int err;
pf = vf->pf;
dev = ice_pf_to_dev(pf);
devlink_port = &vf->devlink_port;
vsi = ice_get_vf_vsi(vf);
if (!vsi)
return -EINVAL;
attrs.flavour = DEVLINK_PORT_FLAVOUR_PCI_VF;
attrs.pci_vf.pf = pf->hw.bus.func;
attrs.pci_vf.vf = vf->vf_id;
ice_devlink_set_switch_id(pf, &attrs.switch_id);
devlink_port_attrs_set(devlink_port, &attrs);
devlink = priv_to_devlink(pf);
err = devlink_port_register(devlink, devlink_port, vsi->idx);
if (err) {
dev_err(dev, "Failed to create devlink port for VF %d, error %d\n",
vf->vf_id, err);
return err;
}
return 0;
}
/**
* ice_devlink_destroy_vf_port - Destroy the devlink_port for this VF
* @vf: the VF to cleanup
*
* Unregisters the devlink_port structure associated with this VF.
*/
void ice_devlink_destroy_vf_port(struct ice_vf *vf)
{
devl_rate_leaf_destroy(&vf->devlink_port);
devlink_port_unregister(&vf->devlink_port);
}

View File

@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024, Intel Corporation. */
#ifndef _DEVLINK_PORT_H_
#define _DEVLINK_PORT_H_
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);
#endif /* _DEVLINK_PORT_H_ */

View File

@ -77,6 +77,7 @@
#include "ice_gnss.h"
#include "ice_irq.h"
#include "ice_dpll.h"
#include "ice_adapter.h"
#define ICE_BAR0 0
#define ICE_REQ_DESC_MULTIPLE 32
@ -537,6 +538,7 @@ struct ice_agg_node {
struct ice_pf {
struct pci_dev *pdev;
struct ice_adapter *adapter;
struct devlink_region *nvm_region;
struct devlink_region *sram_region;

View File

@ -0,0 +1,116 @@
// SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: Copyright Red Hat
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/xarray.h>
#include "ice_adapter.h"
static DEFINE_XARRAY(ice_adapters);
/* PCI bus number is 8 bits. Slot is 5 bits. Domain can have the rest. */
#define INDEX_FIELD_DOMAIN GENMASK(BITS_PER_LONG - 1, 13)
#define INDEX_FIELD_BUS GENMASK(12, 5)
#define INDEX_FIELD_SLOT GENMASK(4, 0)
static unsigned long ice_adapter_index(const struct pci_dev *pdev)
{
unsigned int domain = pci_domain_nr(pdev->bus);
WARN_ON(domain > FIELD_MAX(INDEX_FIELD_DOMAIN));
return FIELD_PREP(INDEX_FIELD_DOMAIN, domain) |
FIELD_PREP(INDEX_FIELD_BUS, pdev->bus->number) |
FIELD_PREP(INDEX_FIELD_SLOT, PCI_SLOT(pdev->devfn));
}
static struct ice_adapter *ice_adapter_new(void)
{
struct ice_adapter *adapter;
adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
if (!adapter)
return NULL;
spin_lock_init(&adapter->ptp_gltsyn_time_lock);
refcount_set(&adapter->refcount, 1);
return adapter;
}
static void ice_adapter_free(struct ice_adapter *adapter)
{
kfree(adapter);
}
DEFINE_FREE(ice_adapter_free, struct ice_adapter*, if (_T) ice_adapter_free(_T))
/**
* ice_adapter_get - Get a shared ice_adapter structure.
* @pdev: Pointer to the pci_dev whose driver is getting the ice_adapter.
*
* Gets a pointer to a shared ice_adapter structure. Physical functions (PFs)
* of the same multi-function PCI device share one ice_adapter structure.
* The ice_adapter is reference-counted. The PF driver must use ice_adapter_put
* to release its reference.
*
* Context: Process, may sleep.
* Return: Pointer to ice_adapter on success.
* ERR_PTR() on error. -ENOMEM is the only possible error.
*/
struct ice_adapter *ice_adapter_get(const struct pci_dev *pdev)
{
struct ice_adapter *ret, __free(ice_adapter_free) *adapter = NULL;
unsigned long index = ice_adapter_index(pdev);
adapter = ice_adapter_new();
if (!adapter)
return ERR_PTR(-ENOMEM);
xa_lock(&ice_adapters);
ret = __xa_cmpxchg(&ice_adapters, index, NULL, adapter, GFP_KERNEL);
if (xa_is_err(ret)) {
ret = ERR_PTR(xa_err(ret));
goto unlock;
}
if (ret) {
refcount_inc(&ret->refcount);
goto unlock;
}
ret = no_free_ptr(adapter);
unlock:
xa_unlock(&ice_adapters);
return ret;
}
/**
* ice_adapter_put - Release a reference to the shared ice_adapter structure.
* @pdev: Pointer to the pci_dev whose driver is releasing the ice_adapter.
*
* Releases the reference to ice_adapter previously obtained with
* ice_adapter_get.
*
* Context: Any.
*/
void ice_adapter_put(const struct pci_dev *pdev)
{
unsigned long index = ice_adapter_index(pdev);
struct ice_adapter *adapter;
xa_lock(&ice_adapters);
adapter = xa_load(&ice_adapters, index);
if (WARN_ON(!adapter))
goto unlock;
if (!refcount_dec_and_test(&adapter->refcount))
goto unlock;
WARN_ON(__xa_erase(&ice_adapters, index) != adapter);
ice_adapter_free(adapter);
unlock:
xa_unlock(&ice_adapters);
}

View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* SPDX-FileCopyrightText: Copyright Red Hat */
#ifndef _ICE_ADAPTER_H_
#define _ICE_ADAPTER_H_
#include <linux/spinlock_types.h>
#include <linux/refcount_types.h>
struct pci_dev;
/**
* struct ice_adapter - PCI adapter resources shared across PFs
* @ptp_gltsyn_time_lock: Spinlock protecting access to the GLTSYN_TIME
* register of the PTP clock.
* @refcount: Reference count. struct ice_pf objects hold the references.
*/
struct ice_adapter {
/* For access to the GLTSYN_TIME register */
spinlock_t ptp_gltsyn_time_lock;
refcount_t refcount;
};
struct ice_adapter *ice_adapter_get(const struct pci_dev *pdev);
void ice_adapter_put(const struct pci_dev *pdev);
#endif /* _ICE_ADAPTER_H */

View File

@ -264,6 +264,8 @@ struct ice_aqc_set_port_params {
#define ICE_AQC_RES_TYPE_FLAG_SHARED BIT(7)
#define ICE_AQC_RES_TYPE_FLAG_SCAN_BOTTOM BIT(12)
#define ICE_AQC_RES_TYPE_FLAG_IGNORE_INDEX BIT(13)
#define ICE_AQC_RES_TYPE_FLAG_SUBSCRIBE_SHARED BIT(14)
#define ICE_AQC_RES_TYPE_FLAG_SUBSCRIBE_CTL BIT(15)
#define ICE_AQC_RES_TYPE_FLAG_DEDICATED 0x00

View File

@ -1142,6 +1142,8 @@ int ice_init_hw(struct ice_hw *hw)
if (status)
goto err_unroll_fltr_mgmt_struct;
mutex_init(&hw->tnl_lock);
ice_init_chk_recipe_reuse_support(hw);
return 0;
err_unroll_fltr_mgmt_struct:

View File

@ -3,7 +3,7 @@
#include "ice_dcb_lib.h"
#include "ice_dcb_nl.h"
#include "ice_devlink.h"
#include "devlink/devlink.h"
/**
* ice_dcb_get_ena_tc - return bitmap of enabled TCs

View File

@ -7,7 +7,7 @@
#include "ice_eswitch_br.h"
#include "ice_fltr.h"
#include "ice_repr.h"
#include "ice_devlink.h"
#include "devlink/devlink.h"
#include "ice_tc_lib.h"
/**

View File

@ -7,7 +7,6 @@
#include "ice_lib.h"
#include "ice_fltr.h"
#include "ice_dcb_lib.h"
#include "ice_devlink.h"
#include "ice_vsi_vlan_ops.h"
/**

View File

@ -13,7 +13,8 @@
#include "ice_fltr.h"
#include "ice_dcb_lib.h"
#include "ice_dcb_nl.h"
#include "ice_devlink.h"
#include "devlink/devlink.h"
#include "devlink/devlink_port.h"
#include "ice_hwmon.h"
/* Including ice_trace.h with CREATE_TRACE_POINTS defined will generate the
* ice tracepoint functions. This must be done exactly once across the
@ -5093,6 +5094,7 @@ static int
ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
{
struct device *dev = &pdev->dev;
struct ice_adapter *adapter;
struct ice_pf *pf;
struct ice_hw *hw;
int err;
@ -5145,7 +5147,12 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
pci_set_master(pdev);
adapter = ice_adapter_get(pdev);
if (IS_ERR(adapter))
return PTR_ERR(adapter);
pf->pdev = pdev;
pf->adapter = adapter;
pci_set_drvdata(pdev, pf);
set_bit(ICE_DOWN, pf->state);
/* Disable service task until DOWN bit is cleared */
@ -5179,23 +5186,23 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
devl_lock(priv_to_devlink(pf));
err = ice_load(pf);
devl_unlock(priv_to_devlink(pf));
if (err)
goto err_load;
err = ice_init_devlink(pf);
if (err)
goto err_init_devlink;
devl_unlock(priv_to_devlink(pf));
return 0;
err_init_devlink:
devl_lock(priv_to_devlink(pf));
ice_unload(pf);
devl_unlock(priv_to_devlink(pf));
err_load:
devl_unlock(priv_to_devlink(pf));
ice_deinit(pf);
err_init:
ice_adapter_put(pdev);
pci_disable_device(pdev);
return err;
}
@ -5290,9 +5297,9 @@ static void ice_remove(struct pci_dev *pdev)
if (!ice_is_safe_mode(pf))
ice_remove_arfs(pf);
devl_lock(priv_to_devlink(pf));
ice_deinit_devlink(pf);
devl_lock(priv_to_devlink(pf));
ice_unload(pf);
devl_unlock(priv_to_devlink(pf));
@ -5302,6 +5309,7 @@ static void ice_remove(struct pci_dev *pdev)
ice_setup_mc_magic_wake(pf);
ice_set_wake(pf);
ice_adapter_put(pdev);
pci_disable_device(pdev);
}

View File

@ -374,6 +374,7 @@ ice_ptp_read_src_clk_reg(struct ice_pf *pf, struct ptp_system_timestamp *sts)
u8 tmr_idx;
tmr_idx = ice_get_ptp_src_clock_index(hw);
guard(spinlock)(&pf->adapter->ptp_gltsyn_time_lock);
/* Read the system timestamp pre PHC read */
ptp_read_system_prets(sts);
@ -1165,26 +1166,6 @@ static void ice_ptp_reset_cached_phctime(struct ice_pf *pf)
ice_ptp_mark_tx_tracker_stale(&pf->ptp.port.tx);
}
/**
* ice_ptp_read_time - Read the time from the device
* @pf: Board private structure
* @ts: timespec structure to hold the current time value
* @sts: Optional parameter for holding a pair of system timestamps from
* the system clock. Will be ignored if NULL is given.
*
* This function reads the source clock registers and stores them in a timespec.
* However, since the registers are 64 bits of nanoseconds, we must convert the
* result to a timespec before we can return.
*/
static void
ice_ptp_read_time(struct ice_pf *pf, struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{
u64 time_ns = ice_ptp_read_src_clk_reg(pf, sts);
*ts = ns_to_timespec64(time_ns);
}
/**
* ice_ptp_write_init - Set PHC time to provided value
* @pf: Board private structure
@ -1925,16 +1906,10 @@ ice_ptp_gettimex64(struct ptp_clock_info *info, struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{
struct ice_pf *pf = ptp_info_to_pf(info);
struct ice_hw *hw = &pf->hw;
if (!ice_ptp_lock(hw)) {
dev_err(ice_pf_to_dev(pf), "PTP failed to get time\n");
return -EBUSY;
}
ice_ptp_read_time(pf, ts, sts);
ice_ptp_unlock(hw);
u64 time_ns;
time_ns = ice_ptp_read_src_clk_reg(pf, sts);
*ts = ns_to_timespec64(time_ns);
return 0;
}

View File

@ -274,6 +274,9 @@ void ice_ptp_src_cmd(struct ice_hw *hw, enum ice_ptp_tmr_cmd cmd)
*/
static void ice_ptp_exec_tmr_cmd(struct ice_hw *hw)
{
struct ice_pf *pf = container_of(hw, struct ice_pf, hw);
guard(spinlock)(&pf->adapter->ptp_gltsyn_time_lock);
wr32(hw, GLTSYN_CMD_SYNC, SYNC_EXEC_CMD);
ice_flush(hw);
}

View File

@ -3,7 +3,8 @@
#include "ice.h"
#include "ice_eswitch.h"
#include "ice_devlink.h"
#include "devlink/devlink.h"
#include "devlink/devlink_port.h"
#include "ice_sriov.h"
#include "ice_tc_lib.h"
#include "ice_dcb_lib.h"

View File

@ -2148,6 +2148,18 @@ ice_aq_get_recipe_to_profile(struct ice_hw *hw, u32 profile_id, u64 *r_assoc,
return status;
}
/**
* ice_init_chk_recipe_reuse_support - check if recipe reuse is supported
* @hw: pointer to the hardware structure
*/
void ice_init_chk_recipe_reuse_support(struct ice_hw *hw)
{
struct ice_nvm_info *nvm = &hw->flash.nvm;
hw->recp_reuse = (nvm->major == 0x4 && nvm->minor >= 0x30) ||
nvm->major > 0x4;
}
/**
* ice_alloc_recipe - add recipe resource
* @hw: pointer to the hardware structure
@ -2157,12 +2169,16 @@ int ice_alloc_recipe(struct ice_hw *hw, u16 *rid)
{
DEFINE_RAW_FLEX(struct ice_aqc_alloc_free_res_elem, sw_buf, elem, 1);
u16 buf_len = __struct_size(sw_buf);
u16 res_type;
int status;
sw_buf->num_elems = cpu_to_le16(1);
sw_buf->res_type = cpu_to_le16((ICE_AQC_RES_TYPE_RECIPE <<
ICE_AQC_RES_TYPE_S) |
ICE_AQC_RES_TYPE_FLAG_SHARED);
res_type = FIELD_PREP(ICE_AQC_RES_TYPE_M, ICE_AQC_RES_TYPE_RECIPE);
if (hw->recp_reuse)
res_type |= ICE_AQC_RES_TYPE_FLAG_SUBSCRIBE_SHARED;
else
res_type |= ICE_AQC_RES_TYPE_FLAG_SHARED;
sw_buf->res_type = cpu_to_le16(res_type);
status = ice_aq_alloc_free_res(hw, sw_buf, buf_len,
ice_aqc_opc_alloc_res);
if (!status)
@ -2171,6 +2187,70 @@ int ice_alloc_recipe(struct ice_hw *hw, u16 *rid)
return status;
}
/**
* ice_free_recipe_res - free recipe resource
* @hw: pointer to the hardware structure
* @rid: recipe ID to free
*
* Return: 0 on success, and others on error
*/
static int ice_free_recipe_res(struct ice_hw *hw, u16 rid)
{
return ice_free_hw_res(hw, ICE_AQC_RES_TYPE_RECIPE, 1, &rid);
}
/**
* ice_release_recipe_res - disassociate and free recipe resource
* @hw: pointer to the hardware structure
* @recp: the recipe struct resource to unassociate and free
*
* Return: 0 on success, and others on error
*/
static int ice_release_recipe_res(struct ice_hw *hw,
struct ice_sw_recipe *recp)
{
DECLARE_BITMAP(r_bitmap, ICE_MAX_NUM_RECIPES);
struct ice_switch_info *sw = hw->switch_info;
u64 recp_assoc;
u32 rid, prof;
int status;
for_each_set_bit(rid, recp->r_bitmap, ICE_MAX_NUM_RECIPES) {
for_each_set_bit(prof, recipe_to_profile[rid],
ICE_MAX_NUM_PROFILES) {
status = ice_aq_get_recipe_to_profile(hw, prof,
&recp_assoc,
NULL);
if (status)
return status;
bitmap_from_arr64(r_bitmap, &recp_assoc,
ICE_MAX_NUM_RECIPES);
bitmap_andnot(r_bitmap, r_bitmap, recp->r_bitmap,
ICE_MAX_NUM_RECIPES);
bitmap_to_arr64(&recp_assoc, r_bitmap,
ICE_MAX_NUM_RECIPES);
ice_aq_map_recipe_to_profile(hw, prof,
recp_assoc, NULL);
clear_bit(rid, profile_to_recipe[prof]);
clear_bit(prof, recipe_to_profile[rid]);
}
status = ice_free_recipe_res(hw, rid);
if (status)
return status;
sw->recp_list[rid].recp_created = false;
sw->recp_list[rid].adv_rule = false;
memset(&sw->recp_list[rid].lkup_exts, 0,
sizeof(sw->recp_list[rid].lkup_exts));
clear_bit(rid, recp->r_bitmap);
}
return 0;
}
/**
* ice_get_recp_to_prof_map - updates recipe to profile mapping
* @hw: pointer to hardware structure
@ -2220,6 +2300,7 @@ ice_collect_result_idx(struct ice_aqc_recipe_data_elem *buf,
* @recps: struct that we need to populate
* @rid: recipe ID that we are populating
* @refresh_required: true if we should get recipe to profile mapping from FW
* @is_add: flag of adding recipe
*
* This function is used to populate all the necessary entries into our
* bookkeeping so that we have a current list of all the recipes that are
@ -2227,7 +2308,7 @@ ice_collect_result_idx(struct ice_aqc_recipe_data_elem *buf,
*/
static int
ice_get_recp_frm_fw(struct ice_hw *hw, struct ice_sw_recipe *recps, u8 rid,
bool *refresh_required)
bool *refresh_required, bool is_add)
{
DECLARE_BITMAP(result_bm, ICE_MAX_FV_WORDS);
struct ice_aqc_recipe_data_elem *tmp;
@ -2344,8 +2425,12 @@ ice_get_recp_frm_fw(struct ice_hw *hw, struct ice_sw_recipe *recps, u8 rid,
recps[idx].chain_idx = ICE_INVAL_CHAIN_IND;
}
if (!is_root)
if (!is_root) {
if (hw->recp_reuse && is_add)
recps[idx].recp_created = true;
continue;
}
/* Only do the following for root recipes entries */
memcpy(recps[idx].r_bitmap, root_bufs.recipe_bitmap,
@ -2369,7 +2454,8 @@ ice_get_recp_frm_fw(struct ice_hw *hw, struct ice_sw_recipe *recps, u8 rid,
/* Copy result indexes */
bitmap_copy(recps[rid].res_idxs, result_bm, ICE_MAX_FV_WORDS);
recps[rid].recp_created = true;
if (is_add)
recps[rid].recp_created = true;
err_unroll:
kfree(tmp);
@ -4653,12 +4739,13 @@ static struct ice_protocol_entry ice_prot_id_tbl[ICE_PROTOCOL_LAST] = {
* @hw: pointer to the hardware structure
* @lkup_exts: extension sequence to match
* @rinfo: information regarding the rule e.g. priority and action info
* @is_add: flag of adding recipe
*
* Returns index of matching recipe, or ICE_MAX_NUM_RECIPES if not found.
*/
static u16
ice_find_recp(struct ice_hw *hw, struct ice_prot_lkup_ext *lkup_exts,
const struct ice_adv_rule_info *rinfo)
const struct ice_adv_rule_info *rinfo, bool is_add)
{
bool refresh_required = true;
struct ice_sw_recipe *recp;
@ -4672,11 +4759,12 @@ ice_find_recp(struct ice_hw *hw, struct ice_prot_lkup_ext *lkup_exts,
* entry update it in our SW bookkeeping and continue with the
* matching.
*/
if (!recp[i].recp_created)
if (hw->recp_reuse) {
if (ice_get_recp_frm_fw(hw,
hw->switch_info->recp_list, i,
&refresh_required))
&refresh_required, is_add))
continue;
}
/* Skip inverse action recipes */
if (recp[i].root_buf && recp[i].root_buf->content.act_ctrl &
@ -5360,6 +5448,49 @@ ice_get_compat_fv_bitmap(struct ice_hw *hw, struct ice_adv_rule_info *rinfo,
ice_get_sw_fv_bitmap(hw, prof_type, bm);
}
/**
* ice_subscribe_recipe - subscribe to an existing recipe
* @hw: pointer to the hardware structure
* @rid: recipe ID to subscribe to
*
* Return: 0 on success, and others on error
*/
static int ice_subscribe_recipe(struct ice_hw *hw, u16 rid)
{
DEFINE_RAW_FLEX(struct ice_aqc_alloc_free_res_elem, sw_buf, elem, 1);
u16 buf_len = __struct_size(sw_buf);
u16 res_type;
int status;
/* Prepare buffer to allocate resource */
sw_buf->num_elems = cpu_to_le16(1);
res_type = FIELD_PREP(ICE_AQC_RES_TYPE_M, ICE_AQC_RES_TYPE_RECIPE) |
ICE_AQC_RES_TYPE_FLAG_SUBSCRIBE_SHARED |
ICE_AQC_RES_TYPE_FLAG_SUBSCRIBE_CTL;
sw_buf->res_type = cpu_to_le16(res_type);
sw_buf->elem[0].e.sw_resp = cpu_to_le16(rid);
status = ice_aq_alloc_free_res(hw, sw_buf, buf_len,
ice_aqc_opc_alloc_res);
return status;
}
/**
* ice_subscribable_recp_shared - share an existing subscribable recipe
* @hw: pointer to the hardware structure
* @rid: recipe ID to subscribe to
*/
static void ice_subscribable_recp_shared(struct ice_hw *hw, u16 rid)
{
struct ice_sw_recipe *recps = hw->switch_info->recp_list;
u16 sub_rid;
for_each_set_bit(sub_rid, recps[rid].r_bitmap, ICE_MAX_NUM_RECIPES)
ice_subscribe_recipe(hw, sub_rid);
}
/**
* ice_add_adv_recipe - Add an advanced recipe that is not part of the default
* @hw: pointer to hardware structure
@ -5382,6 +5513,7 @@ ice_add_adv_recipe(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
struct ice_sw_fv_list_entry *tmp;
struct ice_sw_recipe *rm;
int status = 0;
u16 rid_tmp;
u8 i;
if (!lkups_cnt)
@ -5459,10 +5591,14 @@ ice_add_adv_recipe(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
}
/* Look for a recipe which matches our requested fv / mask list */
*rid = ice_find_recp(hw, lkup_exts, rinfo);
if (*rid < ICE_MAX_NUM_RECIPES)
*rid = ice_find_recp(hw, lkup_exts, rinfo, true);
if (*rid < ICE_MAX_NUM_RECIPES) {
/* Success if found a recipe that match the existing criteria */
if (hw->recp_reuse)
ice_subscribable_recp_shared(hw, *rid);
goto err_unroll;
}
rm->tun_type = rinfo->tun_type;
/* Recipe we need does not exist, add a recipe */
@ -5481,14 +5617,14 @@ ice_add_adv_recipe(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
status = ice_aq_get_recipe_to_profile(hw, fvit->profile_id,
&recp_assoc, NULL);
if (status)
goto err_unroll;
goto err_free_recipe;
bitmap_from_arr64(r_bitmap, &recp_assoc, ICE_MAX_NUM_RECIPES);
bitmap_or(r_bitmap, r_bitmap, rm->r_bitmap,
ICE_MAX_NUM_RECIPES);
status = ice_acquire_change_lock(hw, ICE_RES_WRITE);
if (status)
goto err_unroll;
goto err_free_recipe;
bitmap_to_arr64(&recp_assoc, r_bitmap, ICE_MAX_NUM_RECIPES);
status = ice_aq_map_recipe_to_profile(hw, fvit->profile_id,
@ -5496,7 +5632,7 @@ ice_add_adv_recipe(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
ice_release_change_lock(hw);
if (status)
goto err_unroll;
goto err_free_recipe;
/* Update profile to recipe bitmap array */
bitmap_copy(profile_to_recipe[fvit->profile_id], r_bitmap,
@ -5510,6 +5646,16 @@ ice_add_adv_recipe(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
*rid = rm->root_rid;
memcpy(&hw->switch_info->recp_list[*rid].lkup_exts, lkup_exts,
sizeof(*lkup_exts));
goto err_unroll;
err_free_recipe:
if (hw->recp_reuse) {
for_each_set_bit(rid_tmp, rm->r_bitmap, ICE_MAX_NUM_RECIPES) {
if (!ice_free_recipe_res(hw, rid_tmp))
clear_bit(rid_tmp, rm->r_bitmap);
}
}
err_unroll:
list_for_each_entry_safe(r_entry, r_tmp, &rm->rg_list, l_entry) {
list_del(&r_entry->l_entry);
@ -6529,7 +6675,7 @@ ice_rem_adv_rule(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
return -EIO;
}
rid = ice_find_recp(hw, &lkup_exts, rinfo);
rid = ice_find_recp(hw, &lkup_exts, rinfo, false);
/* If did not find a recipe that match the existing criteria */
if (rid == ICE_MAX_NUM_RECIPES)
return -EINVAL;
@ -6573,14 +6719,21 @@ ice_rem_adv_rule(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
ice_aqc_opc_remove_sw_rules, NULL);
if (!status || status == -ENOENT) {
struct ice_switch_info *sw = hw->switch_info;
struct ice_sw_recipe *r_list = sw->recp_list;
mutex_lock(rule_lock);
list_del(&list_elem->list_entry);
devm_kfree(ice_hw_to_dev(hw), list_elem->lkups);
devm_kfree(ice_hw_to_dev(hw), list_elem);
mutex_unlock(rule_lock);
if (list_empty(&sw->recp_list[rid].filt_rules))
sw->recp_list[rid].adv_rule = false;
if (list_empty(&r_list[rid].filt_rules)) {
r_list[rid].adv_rule = false;
/* All rules for this recipe are now removed */
if (hw->recp_reuse)
ice_release_recipe_res(hw,
&r_list[rid]);
}
}
kfree(s_rule);
}

View File

@ -432,5 +432,6 @@ ice_aq_get_recipe_to_profile(struct ice_hw *hw, u32 profile_id, u64 *r_assoc,
int
ice_aq_map_recipe_to_profile(struct ice_hw *hw, u32 profile_id, u64 r_assoc,
struct ice_sq_cd *cd);
void ice_init_chk_recipe_reuse_support(struct ice_hw *hw);
#endif /* _ICE_SWITCH_H_ */

View File

@ -848,6 +848,8 @@ struct ice_hw {
u16 max_burst_size; /* driver sets this value */
u8 recp_reuse:1; /* indicates whether FW supports recipe reuse */
/* Tx Scheduler values */
u8 num_tx_sched_layers;
u8 num_tx_sched_phys_layers;