Commit f1ff4e80 authored by David S. Miller's avatar David S. Miller

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

Jeff Kirsher says:

====================
100GbE Intel Wired LAN Driver Updates 2019-11-08

Another series that contains updates to the ice driver only.

Anirudh cleans up the code of kernel config of ifdef wrappers by moving
code that is needed by DCB to disable and enable the PF VSI for
configuration.  Implements ice_vsi_type_str() to convert an VSI type
enum value to its string equivalent to help identify VSI types from
module print statements.

Usha and Tarun add support for setting the maximum per-queue bit rate
for transmit queues.

Dave implements dcb_nl set functions and supporting software DCB
functions to support the callbacks defined in the dcbnl_rtnl_ops
structure.

Henry adds a check to ensure we are not resetting the device when trying
to configure it, and to return -EBUSY during a reset.

Usha fixes a call trace caused by the receive/transmit descriptor size
change request via ethtool when DCB is configured by using the number of
enabled queues and not the total number of allocated queues.

Paul cleans up and refactors the software LLDP configuration to handle
when firmware DCBX is disabled.

Akeem adds checks to ensure the VF or PF is not disabled before honoring
mailbox messages to configure the VF.

Brett corrects the check to make sure the vector_id passed down from
iavf is less than the max allowed interrupts per VF.  Updates a flag bit
to align with the current specification.

Bruce updates a switch statement to use the correct status of the
Download Package AQ command.  Does some housekeeping by cleaning up a
conditional check that is not needed.

Mitch shortens up the delay for SQ responses to resolve issues with VF
resets failing.

Jake cleans up the code reducing namespace pollution and to simplify
ice_debug_cq() since it always uses the same mask, not need to pass it
in.  Improve debugging by adding the command opcode in the debug
messages that print an error code.

v2: fixed reverse christmas tree issue in patch 3 of the series.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents bbab7ef2 fb0254b2
......@@ -20,5 +20,5 @@ ice-y := ice_main.o \
ice_flex_pipe.o \
ice_ethtool.o
ice-$(CONFIG_PCI_IOV) += ice_virtchnl_pf.o ice_sriov.o
ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_lib.o
ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o
ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o
......@@ -180,6 +180,7 @@ enum ice_state {
__ICE_NEEDS_RESTART,
__ICE_PREPARED_FOR_RESET, /* set by driver when prepared */
__ICE_RESET_OICR_RECV, /* set by driver after rcv reset OICR */
__ICE_DCBNL_DEVRESET, /* set by dcbnl devreset */
__ICE_PFR_REQ, /* set by driver and peers */
__ICE_CORER_REQ, /* set by driver and peers */
__ICE_GLOBR_REQ, /* set by driver and peers */
......@@ -365,6 +366,7 @@ struct ice_pf {
struct work_struct serv_task;
struct mutex avail_q_mutex; /* protects access to avail_[rx|tx]qs */
struct mutex sw_mutex; /* lock for protecting VSI alloc flow */
struct mutex tc_mutex; /* lock to protect TC changes */
u32 msg_enable;
u32 hw_csum_rx_error;
u32 oicr_idx; /* Other interrupt cause MSIX vector index */
......@@ -502,10 +504,6 @@ int ice_set_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size);
int ice_get_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size);
void ice_fill_rss_lut(u8 *lut, u16 rss_table_size, u16 rss_size);
void ice_print_link_msg(struct ice_vsi *vsi, bool isup);
#ifdef CONFIG_DCB
int ice_pf_ena_all_vsi(struct ice_pf *pf, bool locked);
void ice_pf_dis_all_vsi(struct ice_pf *pf, bool locked);
#endif /* CONFIG_DCB */
int ice_open(struct net_device *netdev);
int ice_stop(struct net_device *netdev);
......
......@@ -742,6 +742,10 @@ struct ice_aqc_add_elem {
struct ice_aqc_txsched_elem_data generic[1];
};
struct ice_aqc_conf_elem {
struct ice_aqc_txsched_elem_data generic[1];
};
struct ice_aqc_get_elem {
struct ice_aqc_txsched_elem_data generic[1];
};
......@@ -783,6 +787,44 @@ struct ice_aqc_port_ets_elem {
__le32 tc_node_teid[8]; /* Used for response, reserved in command */
};
/* Rate limiting profile for
* Add RL profile (indirect 0x0410)
* Query RL profile (indirect 0x0411)
* Remove RL profile (indirect 0x0415)
* These indirect commands acts on single or multiple
* RL profiles with specified data.
*/
struct ice_aqc_rl_profile {
__le16 num_profiles;
__le16 num_processed; /* Only for response. Reserved in Command. */
u8 reserved[4];
__le32 addr_high;
__le32 addr_low;
};
struct ice_aqc_rl_profile_elem {
u8 level;
u8 flags;
#define ICE_AQC_RL_PROFILE_TYPE_S 0x0
#define ICE_AQC_RL_PROFILE_TYPE_M (0x3 << ICE_AQC_RL_PROFILE_TYPE_S)
#define ICE_AQC_RL_PROFILE_TYPE_CIR 0
#define ICE_AQC_RL_PROFILE_TYPE_EIR 1
#define ICE_AQC_RL_PROFILE_TYPE_SRL 2
/* The following flag is used for Query RL Profile Data */
#define ICE_AQC_RL_PROFILE_INVAL_S 0x7
#define ICE_AQC_RL_PROFILE_INVAL_M (0x1 << ICE_AQC_RL_PROFILE_INVAL_S)
__le16 profile_id;
__le16 max_burst_size;
__le16 rl_multiply;
__le16 wake_up_calc;
__le16 rl_encode;
};
struct ice_aqc_rl_profile_generic_elem {
struct ice_aqc_rl_profile_elem generic[1];
};
/* Query Scheduler Resource Allocation (indirect 0x0412)
* This indirect command retrieves the scheduler resources allocated by
* EMP Firmware to the given PF.
......@@ -1657,6 +1699,7 @@ struct ice_aq_desc {
struct ice_aqc_sched_elem_cmd sched_elem_cmd;
struct ice_aqc_query_txsched_res query_sched_res;
struct ice_aqc_query_port_ets port_ets;
struct ice_aqc_rl_profile rl_profile;
struct ice_aqc_nvm nvm;
struct ice_aqc_nvm_checksum nvm_checksum;
struct ice_aqc_pf_vf_msg virt;
......@@ -1758,12 +1801,15 @@ enum ice_adminq_opc {
/* transmit scheduler commands */
ice_aqc_opc_get_dflt_topo = 0x0400,
ice_aqc_opc_add_sched_elems = 0x0401,
ice_aqc_opc_cfg_sched_elems = 0x0403,
ice_aqc_opc_get_sched_elems = 0x0404,
ice_aqc_opc_suspend_sched_elems = 0x0409,
ice_aqc_opc_resume_sched_elems = 0x040A,
ice_aqc_opc_query_port_ets = 0x040E,
ice_aqc_opc_delete_sched_elems = 0x040F,
ice_aqc_opc_add_rl_profiles = 0x0410,
ice_aqc_opc_query_sched_res = 0x0412,
ice_aqc_opc_remove_rl_profiles = 0x0415,
/* PHY commands */
ice_aqc_opc_get_phy_caps = 0x0600,
......
......@@ -855,6 +855,9 @@ enum ice_status ice_init_hw(struct ice_hw *hw)
goto err_unroll_sched;
}
INIT_LIST_HEAD(&hw->agg_list);
/* Initialize max burst size */
if (!hw->max_burst_size)
ice_cfg_rl_burst_size(hw, ICE_SCHED_DFLT_BURST_SIZE);
status = ice_init_fltr_mgmt_struct(hw);
if (status)
......@@ -1248,56 +1251,6 @@ const struct ice_ctx_ele ice_tlan_ctx_info[] = {
{ 0 }
};
/**
* ice_debug_cq
* @hw: pointer to the hardware structure
* @mask: debug mask
* @desc: pointer to control queue descriptor
* @buf: pointer to command buffer
* @buf_len: max length of buf
*
* Dumps debug log about control command with descriptor contents.
*/
void
ice_debug_cq(struct ice_hw *hw, u32 __maybe_unused mask, void *desc, void *buf,
u16 buf_len)
{
struct ice_aq_desc *cq_desc = (struct ice_aq_desc *)desc;
u16 len;
#ifndef CONFIG_DYNAMIC_DEBUG
if (!(mask & hw->debug_mask))
return;
#endif
if (!desc)
return;
len = le16_to_cpu(cq_desc->datalen);
ice_debug(hw, mask,
"CQ CMD: opcode 0x%04X, flags 0x%04X, datalen 0x%04X, retval 0x%04X\n",
le16_to_cpu(cq_desc->opcode),
le16_to_cpu(cq_desc->flags),
le16_to_cpu(cq_desc->datalen), le16_to_cpu(cq_desc->retval));
ice_debug(hw, mask, "\tcookie (h,l) 0x%08X 0x%08X\n",
le32_to_cpu(cq_desc->cookie_high),
le32_to_cpu(cq_desc->cookie_low));
ice_debug(hw, mask, "\tparam (0,1) 0x%08X 0x%08X\n",
le32_to_cpu(cq_desc->params.generic.param0),
le32_to_cpu(cq_desc->params.generic.param1));
ice_debug(hw, mask, "\taddr (h,l) 0x%08X 0x%08X\n",
le32_to_cpu(cq_desc->params.generic.addr_high),
le32_to_cpu(cq_desc->params.generic.addr_low));
if (buf && cq_desc->datalen != 0) {
ice_debug(hw, mask, "Buffer:\n");
if (buf_len < len)
len = buf_len;
ice_debug_array(hw, mask, 16, 1, (u8 *)buf, len);
}
}
/* FW Admin Queue command wrappers */
/* Software lock/mutex that is meant to be held while the Global Config Lock
......@@ -3260,7 +3213,7 @@ ice_set_ctx(u8 *src_ctx, u8 *dest_ctx, const struct ice_ctx_ele *ce_info)
* @tc: TC number
* @q_handle: software queue handle
*/
static struct ice_q_ctx *
struct ice_q_ctx *
ice_get_lan_q_ctx(struct ice_hw *hw, u16 vsi_handle, u8 tc, u16 q_handle)
{
struct ice_vsi_ctx *vsi;
......@@ -3357,9 +3310,12 @@ ice_ena_vsi_txq(struct ice_port_info *pi, u16 vsi_handle, u8 tc, u16 q_handle,
node.node_teid = buf->txqs[0].q_teid;
node.data.elem_type = ICE_AQC_ELEM_TYPE_LEAF;
q_ctx->q_handle = q_handle;
q_ctx->q_teid = le32_to_cpu(node.node_teid);
/* add a leaf node into schduler tree queue layer */
/* add a leaf node into scheduler tree queue layer */
status = ice_sched_add_node(pi, hw->num_tx_sched_layers - 1, &node);
if (!status)
status = ice_sched_replay_q_bw(pi, q_ctx);
ena_txq_exit:
mutex_unlock(&pi->sched_lock);
......
......@@ -13,8 +13,6 @@
enum ice_status ice_nvm_validate_checksum(struct ice_hw *hw);
void
ice_debug_cq(struct ice_hw *hw, u32 mask, void *desc, void *buf, u16 buf_len);
enum ice_status ice_init_hw(struct ice_hw *hw);
void ice_deinit_hw(struct ice_hw *hw);
enum ice_status
......@@ -141,6 +139,8 @@ ice_ena_vsi_txq(struct ice_port_info *pi, u16 vsi_handle, u8 tc, u16 q_handle,
enum ice_status ice_replay_vsi(struct ice_hw *hw, u16 vsi_handle);
void ice_replay_post(struct ice_hw *hw);
void ice_output_fw_log(struct ice_hw *hw, struct ice_aq_desc *desc, void *buf);
struct ice_q_ctx *
ice_get_lan_q_ctx(struct ice_hw *hw, u16 vsi_handle, u8 tc, u16 q_handle);
void
ice_stat_update40(struct ice_hw *hw, u32 reg, bool prev_stat_loaded,
u64 *prev_stat, u64 *cur_stat);
......
......@@ -809,6 +809,52 @@ static u16 ice_clean_sq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
return ICE_CTL_Q_DESC_UNUSED(sq);
}
/**
* ice_debug_cq
* @hw: pointer to the hardware structure
* @desc: pointer to control queue descriptor
* @buf: pointer to command buffer
* @buf_len: max length of buf
*
* Dumps debug log about control command with descriptor contents.
*/
static void ice_debug_cq(struct ice_hw *hw, void *desc, void *buf, u16 buf_len)
{
struct ice_aq_desc *cq_desc = (struct ice_aq_desc *)desc;
u16 len;
if (!IS_ENABLED(CONFIG_DYNAMIC_DEBUG) &&
!((ICE_DBG_AQ_DESC | ICE_DBG_AQ_DESC_BUF) & hw->debug_mask))
return;
if (!desc)
return;
len = le16_to_cpu(cq_desc->datalen);
ice_debug(hw, ICE_DBG_AQ_DESC,
"CQ CMD: opcode 0x%04X, flags 0x%04X, datalen 0x%04X, retval 0x%04X\n",
le16_to_cpu(cq_desc->opcode),
le16_to_cpu(cq_desc->flags),
le16_to_cpu(cq_desc->datalen), le16_to_cpu(cq_desc->retval));
ice_debug(hw, ICE_DBG_AQ_DESC, "\tcookie (h,l) 0x%08X 0x%08X\n",
le32_to_cpu(cq_desc->cookie_high),
le32_to_cpu(cq_desc->cookie_low));
ice_debug(hw, ICE_DBG_AQ_DESC, "\tparam (0,1) 0x%08X 0x%08X\n",
le32_to_cpu(cq_desc->params.generic.param0),
le32_to_cpu(cq_desc->params.generic.param1));
ice_debug(hw, ICE_DBG_AQ_DESC, "\taddr (h,l) 0x%08X 0x%08X\n",
le32_to_cpu(cq_desc->params.generic.addr_high),
le32_to_cpu(cq_desc->params.generic.addr_low));
if (buf && cq_desc->datalen != 0) {
ice_debug(hw, ICE_DBG_AQ_DESC_BUF, "Buffer:\n");
if (buf_len < len)
len = buf_len;
ice_debug_array(hw, ICE_DBG_AQ_DESC_BUF, 16, 1, (u8 *)buf, len);
}
}
/**
* ice_sq_done - check if FW has processed the Admin Send Queue (ATQ)
* @hw: pointer to the HW struct
......@@ -934,10 +980,10 @@ ice_sq_send_cmd(struct ice_hw *hw, struct ice_ctl_q_info *cq,
}
/* Debug desc and buffer */
ice_debug(hw, ICE_DBG_AQ_MSG,
ice_debug(hw, ICE_DBG_AQ_DESC,
"ATQ: Control Send queue desc and buffer:\n");
ice_debug_cq(hw, ICE_DBG_AQ_CMD, (void *)desc_on_ring, buf, buf_size);
ice_debug_cq(hw, (void *)desc_on_ring, buf, buf_size);
(cq->sq.next_to_use)++;
if (cq->sq.next_to_use == cq->sq.count)
......@@ -948,7 +994,7 @@ ice_sq_send_cmd(struct ice_hw *hw, struct ice_ctl_q_info *cq,
if (ice_sq_done(hw, cq))
break;
mdelay(1);
udelay(ICE_CTL_Q_SQ_CMD_USEC);
total_delay++;
} while (total_delay < cq->sq_cmd_timeout);
......@@ -971,7 +1017,8 @@ ice_sq_send_cmd(struct ice_hw *hw, struct ice_ctl_q_info *cq,
retval = le16_to_cpu(desc->retval);
if (retval) {
ice_debug(hw, ICE_DBG_AQ_MSG,
"Control Send Queue command completed with error 0x%x\n",
"Control Send Queue command 0x%04X completed with error 0x%X\n",
le16_to_cpu(desc->opcode),
retval);
/* strip off FW internal code */
......@@ -986,7 +1033,7 @@ ice_sq_send_cmd(struct ice_hw *hw, struct ice_ctl_q_info *cq,
ice_debug(hw, ICE_DBG_AQ_MSG,
"ATQ: desc and buffer writeback:\n");
ice_debug_cq(hw, ICE_DBG_AQ_CMD, (void *)desc, buf, buf_size);
ice_debug_cq(hw, (void *)desc, buf, buf_size);
/* save writeback AQ if requested */
if (details->wb_desc)
......@@ -1075,7 +1122,8 @@ ice_clean_rq_elem(struct ice_hw *hw, struct ice_ctl_q_info *cq,
if (flags & ICE_AQ_FLAG_ERR) {
ret_code = ICE_ERR_AQ_ERROR;
ice_debug(hw, ICE_DBG_AQ_MSG,
"Control Receive Queue Event received with error 0x%x\n",
"Control Receive Queue Event 0x%04X received with error 0x%X\n",
le16_to_cpu(desc->opcode),
cq->rq_last_status);
}
memcpy(&e->desc, desc, sizeof(e->desc));
......@@ -1084,10 +1132,9 @@ ice_clean_rq_elem(struct ice_hw *hw, struct ice_ctl_q_info *cq,
if (e->msg_buf && e->msg_len)
memcpy(e->msg_buf, cq->rq.r.rq_bi[desc_idx].va, e->msg_len);
ice_debug(hw, ICE_DBG_AQ_MSG, "ARQ: desc and buffer:\n");
ice_debug(hw, ICE_DBG_AQ_DESC, "ARQ: desc and buffer:\n");
ice_debug_cq(hw, ICE_DBG_AQ_CMD, (void *)desc, e->msg_buf,
cq->rq_buf_size);
ice_debug_cq(hw, (void *)desc, e->msg_buf, cq->rq_buf_size);
/* Restore the original datalen and buffer address in the desc,
* FW updates datalen to indicate the event message size
......
......@@ -31,8 +31,9 @@ enum ice_ctl_q {
ICE_CTL_Q_MAILBOX,
};
/* Control Queue default settings */
#define ICE_CTL_Q_SQ_CMD_TIMEOUT 250 /* msecs */
/* Control Queue timeout settings - max delay 250ms */
#define ICE_CTL_Q_SQ_CMD_TIMEOUT 2500 /* Count 2500 times */
#define ICE_CTL_Q_SQ_CMD_USEC 100 /* Check every 100usec */
struct ice_ctl_q_ring {
void *dma_head; /* Virtual address to DMA head */
......
......@@ -2,6 +2,7 @@
/* Copyright (c) 2019, Intel Corporation. */
#include "ice_dcb_lib.h"
#include "ice_dcb_nl.h"
static void ice_pf_dcb_recfg(struct ice_pf *pf);
......@@ -101,6 +102,16 @@ u8 ice_dcb_get_num_tc(struct ice_dcbx_cfg *dcbcfg)
return ret;
}
/**
* ice_dcb_get_tc - Get the TC associated with the queue
* @vsi: ptr to the VSI
* @queue_index: queue number associated with VSI
*/
u8 ice_dcb_get_tc(struct ice_vsi *vsi, int queue_index)
{
return vsi->tx_rings[queue_index]->dcb_tc;
}
/**
* ice_vsi_cfg_dcb_rings - Update rings to reflect DCB TC
* @vsi: VSI owner of rings being updated
......@@ -145,15 +156,19 @@ void ice_vsi_cfg_dcb_rings(struct ice_vsi *vsi)
* @new_cfg: DCBX config to apply
* @locked: is the RTNL held
*/
static
int ice_pf_dcb_cfg(struct ice_pf *pf, struct ice_dcbx_cfg *new_cfg, bool locked)
{
struct ice_dcbx_cfg *old_cfg, *curr_cfg;
struct ice_aqc_port_ets_elem buf = { 0 };
int ret = 0;
struct ice_dcbx_cfg *old_cfg, *curr_cfg;
int ret = ICE_DCB_NO_HW_CHG;
struct ice_vsi *pf_vsi;
curr_cfg = &pf->hw.port_info->local_dcbx_cfg;
/* FW does not care if change happened */
if (!pf->hw.port_info->is_sw_lldp)
ret = ICE_DCB_HW_CHG_RST;
/* Enable DCB tagging only when more than one TC */
if (ice_dcb_get_num_tc(new_cfg) > 1) {
dev_dbg(&pf->pdev->dev, "DCB tagging enabled (num TC > 1)\n");
......@@ -169,18 +184,28 @@ int ice_pf_dcb_cfg(struct ice_pf *pf, struct ice_dcbx_cfg *new_cfg, bool locked)
}
/* Store old config in case FW config fails */
old_cfg = devm_kzalloc(&pf->pdev->dev, sizeof(*old_cfg), GFP_KERNEL);
memcpy(old_cfg, curr_cfg, sizeof(*old_cfg));
old_cfg = kmemdup(curr_cfg, sizeof(*old_cfg), GFP_KERNEL);
if (!old_cfg)
return -ENOMEM;
dev_info(&pf->pdev->dev, "Commit DCB Configuration to the hardware\n");
pf_vsi = ice_get_main_vsi(pf);
if (!pf_vsi) {
dev_dbg(&pf->pdev->dev, "PF VSI doesn't exist\n");
ret = -EINVAL;
goto free_cfg;
}
/* avoid race conditions by holding the lock while disabling and
* re-enabling the VSI
*/
if (!locked)
rtnl_lock();
ice_pf_dis_all_vsi(pf, true);
ice_dis_vsi(pf_vsi, true);
memcpy(curr_cfg, new_cfg, sizeof(*curr_cfg));
memcpy(&curr_cfg->etsrec, &curr_cfg->etscfg, sizeof(curr_cfg->etsrec));
memcpy(&new_cfg->etsrec, &curr_cfg->etscfg, sizeof(curr_cfg->etsrec));
/* Only send new config to HW if we are in SW LLDP mode. Otherwise,
* the new config came from the HW in the first place.
......@@ -204,10 +229,11 @@ int ice_pf_dcb_cfg(struct ice_pf *pf, struct ice_dcbx_cfg *new_cfg, bool locked)
ice_pf_dcb_recfg(pf);
out:
ice_pf_ena_all_vsi(pf, true);
ice_ena_vsi(pf_vsi, true);
if (!locked)
rtnl_unlock();
devm_kfree(&pf->pdev->dev, old_cfg);
free_cfg:
kfree(old_cfg);
return ret;
}
......@@ -539,6 +565,8 @@ static void ice_pf_dcb_recfg(struct ice_pf *pf)
}
ice_vsi_map_rings_to_vectors(pf->vsi[v]);
if (pf->vsi[v]->type == ICE_VSI_PF)
ice_dcbnl_set_all(pf->vsi[v]);
}
}
......@@ -566,6 +594,8 @@ int ice_init_pf_dcb(struct ice_pf *pf, bool locked)
"DCB is enabled in the hardware, max number of TCs supported on this port are %d\n",
pf->hw.func_caps.common_cap.maxtc);
if (err) {
struct ice_vsi *pf_vsi;
/* FW LLDP is disabled, activate SW DCBX/LLDP mode */
dev_info(&pf->pdev->dev,
"FW LLDP is disabled, DCBx/LLDP in SW mode.\n");
......@@ -578,6 +608,19 @@ int ice_init_pf_dcb(struct ice_pf *pf, bool locked)
goto dcb_init_err;
}
/* If the FW DCBX engine is not running then Rx LLDP packets
* need to be redirected up the stack.
*/
pf_vsi = ice_get_main_vsi(pf);
if (!pf_vsi) {
dev_err(&pf->pdev->dev,
"Failed to set local DCB config\n");
err = -EIO;
goto dcb_init_err;
}
ice_cfg_sw_lldp(pf_vsi, false, true);
pf->dcbx_cap = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE;
return 0;
}
......@@ -690,6 +733,7 @@ ice_dcb_process_lldp_set_mib_change(struct ice_pf *pf,
struct ice_dcbx_cfg tmp_dcbx_cfg;
bool need_reconfig = false;
struct ice_port_info *pi;
struct ice_vsi *pf_vsi;
u8 type;
int ret;
......@@ -749,6 +793,7 @@ ice_dcb_process_lldp_set_mib_change(struct ice_pf *pf,
need_reconfig = ice_dcb_need_recfg(pf, &tmp_dcbx_cfg,
&pi->local_dcbx_cfg);
ice_dcbnl_flush_apps(pf, &tmp_dcbx_cfg, &pi->local_dcbx_cfg);
if (!need_reconfig)
return;
......@@ -761,8 +806,14 @@ ice_dcb_process_lldp_set_mib_change(struct ice_pf *pf,
clear_bit(ICE_FLAG_DCB_ENA, pf->flags);
}
pf_vsi = ice_get_main_vsi(pf);
if (!pf_vsi) {
dev_dbg(&pf->pdev->dev, "PF VSI doesn't exist\n");
return;
}
rtnl_lock();
ice_pf_dis_all_vsi(pf, true);
ice_dis_vsi(pf_vsi, true);
ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL);
if (ret) {
......@@ -774,6 +825,6 @@ ice_dcb_process_lldp_set_mib_change(struct ice_pf *pf,
/* changes in configuration update VSI */
ice_pf_dcb_recfg(pf);
ice_pf_ena_all_vsi(pf, true);
ice_ena_vsi(pf_vsi, true);
rtnl_unlock();
}
......@@ -10,10 +10,16 @@
#ifdef CONFIG_DCB
#define ICE_TC_MAX_BW 100 /* Default Max BW percentage */
#define ICE_DCB_HW_CHG_RST 0 /* DCB configuration changed with reset */
#define ICE_DCB_NO_HW_CHG 1 /* DCB configuration did not change */
#define ICE_DCB_HW_CHG 2 /* DCB configuration changed, no reset */
void ice_dcb_rebuild(struct ice_pf *pf);
u8 ice_dcb_get_ena_tc(struct ice_dcbx_cfg *dcbcfg);
u8 ice_dcb_get_num_tc(struct ice_dcbx_cfg *dcbcfg);
u8 ice_dcb_get_tc(struct ice_vsi *vsi, int queue_index);
int
ice_pf_dcb_cfg(struct ice_pf *pf, struct ice_dcbx_cfg *new_cfg, bool locked);
void ice_vsi_cfg_dcb_rings(struct ice_vsi *vsi);
int ice_init_pf_dcb(struct ice_pf *pf, bool locked);
void ice_update_dcb_stats(struct ice_pf *pf);
......@@ -42,6 +48,13 @@ static inline u8 ice_dcb_get_num_tc(struct ice_dcbx_cfg __always_unused *dcbcfg)
return 1;
}
static inline u8
ice_dcb_get_tc(struct ice_vsi __always_unused *vsi,
int __always_unused queue_index)
{
return 0;
}
static inline int
ice_init_pf_dcb(struct ice_pf *pf, bool __always_unused locked)
{
......@@ -49,6 +62,14 @@ ice_init_pf_dcb(struct ice_pf *pf, bool __always_unused locked)
return -EOPNOTSUPP;
}
static inline int
ice_pf_dcb_cfg(struct ice_pf __always_unused *pf,
struct ice_dcbx_cfg __always_unused *new_cfg,
bool __always_unused locked)
{
return -EOPNOTSUPP;
}
static inline int
ice_tx_prepare_vlan_flags_dcb(struct ice_ring __always_unused *tx_ring,
struct ice_tx_buf __always_unused *first)
......
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2019, Intel Corporation. */
#include "ice.h"
#include "ice_dcb.h"
#include "ice_dcb_lib.h"
#include "ice_dcb_nl.h"
#include <net/dcbnl.h>
#define ICE_APP_PROT_ID_ROCE 0x8915
/**
* ice_dcbnl_devreset - perform enough of a ifdown/ifup to sync DCBNL info
* @netdev: device associated with interface that needs reset
*/
static void ice_dcbnl_devreset(struct net_device *netdev)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
while (ice_is_reset_in_progress(pf->state))
usleep_range(1000, 2000);
set_bit(__ICE_DCBNL_DEVRESET, pf->state);
dev_close(netdev);
netdev_state_change(netdev);
dev_open(netdev, NULL);
netdev_state_change(netdev);
clear_bit(__ICE_DCBNL_DEVRESET, pf->state);
}
/**
* ice_dcbnl_getets - retrieve local ETS configuration
* @netdev: the relevant netdev
* @ets: struct to hold ETS configuration
*/
static int ice_dcbnl_getets(struct net_device *netdev, struct ieee_ets *ets)
{
struct ice_dcbx_cfg *dcbxcfg;
struct ice_port_info *pi;
struct ice_pf *pf;
pf = ice_netdev_to_pf(netdev);
pi = pf->hw.port_info;
dcbxcfg = &pi->local_dcbx_cfg;
ets->willing = dcbxcfg->etscfg.willing;
ets->ets_cap = dcbxcfg->etscfg.maxtcs;
ets->cbs = dcbxcfg->etscfg.cbs;
memcpy(ets->tc_tx_bw, dcbxcfg->etscfg.tcbwtable, sizeof(ets->tc_tx_bw));
memcpy(ets->tc_rx_bw, dcbxcfg->etscfg.tcbwtable, sizeof(ets->tc_rx_bw));
memcpy(ets->tc_tsa, dcbxcfg->etscfg.tsatable, sizeof(ets->tc_tsa));
memcpy(ets->prio_tc, dcbxcfg->etscfg.prio_table, sizeof(ets->prio_tc));
memcpy(ets->tc_reco_bw, dcbxcfg->etsrec.tcbwtable,
sizeof(ets->tc_reco_bw));
memcpy(ets->tc_reco_tsa, dcbxcfg->etsrec.tsatable,
sizeof(ets->tc_reco_tsa));
memcpy(ets->reco_prio_tc, dcbxcfg->etscfg.prio_table,
sizeof(ets->reco_prio_tc));
return 0;
}
/**
* ice_dcbnl_setets - set IEEE ETS configuration
* @netdev: pointer to relevant netdev
* @ets: struct to hold ETS configuration
*/
static int ice_dcbnl_setets(struct net_device *netdev, struct ieee_ets *ets)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_dcbx_cfg *new_cfg;
int bwcfg = 0, bwrec = 0;
int err, i, max_tc = 0;
if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
!(pf->dcbx_cap & DCB_CAP_DCBX_VER_IEEE))
return -EINVAL;
new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
mutex_lock(&pf->tc_mutex);
new_cfg->etscfg.willing = ets->willing;
new_cfg->etscfg.cbs = ets->cbs;
ice_for_each_traffic_class(i) {
new_cfg->etscfg.tcbwtable[i] = ets->tc_tx_bw[i];
bwcfg += ets->tc_tx_bw[i];
new_cfg->etscfg.tsatable[i] = ets->tc_tsa[i];
new_cfg->etscfg.prio_table[i] = ets->prio_tc[i];
if (ets->prio_tc[i] > max_tc)
max_tc = ets->prio_tc[i];
new_cfg->etsrec.tcbwtable[i] = ets->tc_reco_bw[i];
bwrec += ets->tc_reco_bw[i];
new_cfg->etsrec.tsatable[i] = ets->tc_reco_tsa[i];
new_cfg->etsrec.prio_table[i] = ets->reco_prio_tc[i];
}
/* max_tc is a 1-8 value count of number of TC's, not a 0-7 value
* for the TC's index number. Add one to value if not zero, and
* for zero set it to the FW's default value
*/
if (max_tc)
max_tc++;
else
max_tc = IEEE_8021QAZ_MAX_TCS;
new_cfg->etscfg.maxtcs = max_tc;
if (!bwcfg)
new_cfg->etscfg.tcbwtable[0] = 100;
if (!bwrec)
new_cfg->etsrec.tcbwtable[0] = 100;
err = ice_pf_dcb_cfg(pf, new_cfg, true);
/* return of zero indicates new cfg applied */
if (err == ICE_DCB_HW_CHG_RST)
ice_dcbnl_devreset(netdev);
if (err == ICE_DCB_NO_HW_CHG)
err = ICE_DCB_HW_CHG_RST;
mutex_unlock(&pf->tc_mutex);
return err;
}
/**
* ice_dcbnl_getnumtcs - Get max number of traffic classes supported
* @dev: pointer to netdev struct
* @tcid: TC ID
* @num: total number of TCs supported by the adapter
*
* Return the total number of TCs supported
*/
static int
ice_dcbnl_getnumtcs(struct net_device *dev, int __always_unused tcid, u8 *num)
{
struct ice_pf *pf = ice_netdev_to_pf(dev);
if (!test_bit(ICE_FLAG_DCB_CAPABLE, pf->flags))
return -EINVAL;
*num = IEEE_8021QAZ_MAX_TCS;
return 0;
}
/**
* ice_dcbnl_getdcbx - retrieve current DCBX capability
* @netdev: pointer to the netdev struct
*/
static u8 ice_dcbnl_getdcbx(struct net_device *netdev)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
return pf->dcbx_cap;
}
/**
* ice_dcbnl_setdcbx - set required DCBX capability
* @netdev: the corresponding netdev
* @mode: required mode
*/
static u8 ice_dcbnl_setdcbx(struct net_device *netdev, u8 mode)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
/* No support for LLD_MANAGED modes or CEE+IEEE */
if ((mode & DCB_CAP_DCBX_LLD_MANAGED) ||
((mode & DCB_CAP_DCBX_VER_IEEE) && (mode & DCB_CAP_DCBX_VER_CEE)) ||
!(mode & DCB_CAP_DCBX_HOST))
return ICE_DCB_NO_HW_CHG;
/* Already set to the given mode no change */
if (mode == pf->dcbx_cap)
return ICE_DCB_NO_HW_CHG;
pf->dcbx_cap = mode;
if (mode & DCB_CAP_DCBX_VER_CEE)
pf->hw.port_info->local_dcbx_cfg.dcbx_mode = ICE_DCBX_MODE_CEE;
else
pf->hw.port_info->local_dcbx_cfg.dcbx_mode = ICE_DCBX_MODE_IEEE;
dev_info(&pf->pdev->dev, "DCBx mode = 0x%x\n", mode);
return ICE_DCB_HW_CHG_RST;
}
/**
* ice_dcbnl_get_perm_hw_addr - MAC address used by DCBX
* @netdev: pointer to netdev struct
* @perm_addr: buffer to return permanent MAC address
*/
static void ice_dcbnl_get_perm_hw_addr(struct net_device *netdev, u8 *perm_addr)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_port_info *pi = pf->hw.port_info;
int i, j;
memset(perm_addr, 0xff, MAX_ADDR_LEN);
for (i = 0; i < netdev->addr_len; i++)
perm_addr[i] = pi->mac.perm_addr[i];
for (j = 0; j < netdev->addr_len; j++, i++)
perm_addr[i] = pi->mac.perm_addr[j];
}
/**
* ice_get_pfc_delay - Retrieve PFC Link Delay
* @hw: pointer to HW struct
* @delay: holds the PFC Link Delay value
*/
static void ice_get_pfc_delay(struct ice_hw *hw, u16 *delay)
{
u32 val;
val = rd32(hw, PRTDCB_GENC);
*delay = (u16)((val & PRTDCB_GENC_PFCLDA_M) >> PRTDCB_GENC_PFCLDA_S);
}
/**
* ice_dcbnl_getpfc - retrieve local IEEE PFC config
* @netdev: pointer to netdev struct
* @pfc: struct to hold PFC info
*/
static int ice_dcbnl_getpfc(struct net_device *netdev, struct ieee_pfc *pfc)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_port_info *pi = pf->hw.port_info;
struct ice_dcbx_cfg *dcbxcfg;
int i;
dcbxcfg = &pi->local_dcbx_cfg;
pfc->pfc_cap = dcbxcfg->pfc.pfccap;
pfc->pfc_en = dcbxcfg->pfc.pfcena;
pfc->mbc = dcbxcfg->pfc.mbc;
ice_get_pfc_delay(&pf->hw, &pfc->delay);
ice_for_each_traffic_class(i) {
pfc->requests[i] = pf->stats.priority_xoff_tx[i];
pfc->indications[i] = pf->stats.priority_xoff_rx[i];
}
return 0;
}
/**
* ice_dcbnl_setpfc - set local IEEE PFC config
* @netdev: pointer to relevant netdev
* @pfc: pointer to struct holding PFC config
*/
static int ice_dcbnl_setpfc(struct net_device *netdev, struct ieee_pfc *pfc)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_dcbx_cfg *new_cfg;
int err;
if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
!(pf->dcbx_cap & DCB_CAP_DCBX_VER_IEEE))
return -EINVAL;
mutex_lock(&pf->tc_mutex);
new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
if (pfc->pfc_cap)
new_cfg->pfc.pfccap = pfc->pfc_cap;
else
new_cfg->pfc.pfccap = pf->hw.func_caps.common_cap.maxtc;
new_cfg->pfc.pfcena = pfc->pfc_en;
err = ice_pf_dcb_cfg(pf, new_cfg, true);
if (err == ICE_DCB_HW_CHG_RST)
ice_dcbnl_devreset(netdev);
if (err == ICE_DCB_NO_HW_CHG)
err = ICE_DCB_HW_CHG_RST;
mutex_unlock(&pf->tc_mutex);
return err;
}
/**
* ice_dcbnl_get_pfc_cfg - Get CEE PFC config
* @netdev: pointer to netdev struct
* @prio: corresponding user priority
* @setting: the PFC setting for given priority
*/
static void
ice_dcbnl_get_pfc_cfg(struct net_device *netdev, int prio, u8 *setting)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_port_info *pi = pf->hw.port_info;
if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
!(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
return;
if (prio >= ICE_MAX_USER_PRIORITY)
return;
*setting = (pi->local_dcbx_cfg.pfc.pfcena >> prio) & 0x1;
dev_dbg(&pf->pdev->dev,
"Get PFC Config up=%d, setting=%d, pfcenable=0x%x\n",
prio, *setting, pi->local_dcbx_cfg.pfc.pfcena);
}
/**
* ice_dcbnl_set_pfc_cfg - Set CEE PFC config
* @netdev: the corresponding netdev
* @prio: User Priority
* @set: PFC setting to apply
*/
static void ice_dcbnl_set_pfc_cfg(struct net_device *netdev, int prio, u8 set)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_dcbx_cfg *new_cfg;
if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
!(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
return;
if (prio >= ICE_MAX_USER_PRIORITY)
return;
new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
new_cfg->pfc.pfccap = pf->hw.func_caps.common_cap.maxtc;
if (set)
new_cfg->pfc.pfcena |= BIT(prio);
else
new_cfg->pfc.pfcena &= ~BIT(prio);
dev_dbg(&pf->pdev->dev, "Set PFC config UP:%d set:%d pfcena:0x%x\n",
prio, set, new_cfg->pfc.pfcena);
}
/**
* ice_dcbnl_getpfcstate - get CEE PFC mode
* @netdev: pointer to netdev struct
*/
static u8 ice_dcbnl_getpfcstate(struct net_device *netdev)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_port_info *pi = pf->hw.port_info;
/* Return enabled if any UP enabled for PFC */
if (pi->local_dcbx_cfg.pfc.pfcena)
return 1;
return 0;
}
/**
* ice_dcbnl_getstate - get DCB enabled state
* @netdev: pointer to netdev struct
*/
static u8 ice_dcbnl_getstate(struct net_device *netdev)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
u8 state = 0;
state = test_bit(ICE_FLAG_DCB_CAPABLE, pf->flags);
dev_dbg(&pf->pdev->dev, "DCB enabled state = %d\n", state);
return state;
}
/**
* ice_dcbnl_setstate - Set CEE DCB state
* @netdev: pointer to relevant netdev
* @state: state value to set
*/
static u8 ice_dcbnl_setstate(struct net_device *netdev, u8 state)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
!(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
return ICE_DCB_NO_HW_CHG;
/* Nothing to do */
if (!!state == test_bit(ICE_FLAG_DCB_ENA, pf->flags))
return ICE_DCB_NO_HW_CHG;
if (state) {
set_bit(ICE_FLAG_DCB_ENA, pf->flags);
memcpy(&pf->hw.port_info->desired_dcbx_cfg,
&pf->hw.port_info->local_dcbx_cfg,
sizeof(struct ice_dcbx_cfg));
} else {
clear_bit(ICE_FLAG_DCB_ENA, pf->flags);
}
return ICE_DCB_HW_CHG;
}
/**
* ice_dcbnl_get_pg_tc_cfg_tx - get CEE PG Tx config
* @netdev: pointer to netdev struct
* @prio: the corresponding user priority
* @prio_type: traffic priority type
* @pgid: the BW group ID the traffic class belongs to
* @bw_pct: BW percentage for the corresponding BWG
* @up_map: prio mapped to corresponding TC
*/
static void
ice_dcbnl_get_pg_tc_cfg_tx(struct net_device *netdev, int prio,
u8 __always_unused *prio_type, u8 *pgid,
u8 __always_unused *bw_pct,
u8 __always_unused *up_map)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_port_info *pi = pf->hw.port_info;
if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
!(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
return;
if (prio >= ICE_MAX_USER_PRIORITY)
return;
*pgid = pi->local_dcbx_cfg.etscfg.prio_table[prio];
dev_dbg(&pf->pdev->dev,
"Get PG config prio=%d tc=%d\n", prio, *pgid);
}
/**
* ice_dcbnl_set_pg_tc_cfg_tx - set CEE PG Tx config
* @netdev: pointer to relevant netdev
* @tc: the corresponding traffic class
* @prio_type: the traffic priority type
* @bwg_id: the BW group ID the TC belongs to
* @bw_pct: the BW perventage for the BWG
* @up_map: prio mapped to corresponding TC
*/
static void
ice_dcbnl_set_pg_tc_cfg_tx(struct net_device *netdev, int tc,
u8 __always_unused prio_type,
u8 __always_unused bwg_id,
u8 __always_unused bw_pct, u8 up_map)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_dcbx_cfg *new_cfg;
int i;
if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
!(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
return;
if (tc >= ICE_MAX_TRAFFIC_CLASS)
return;
new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
/* prio_type, bwg_id and bw_pct per UP are not supported */
ice_for_each_traffic_class(i) {
if (up_map & BIT(i))
new_cfg->etscfg.prio_table[i] = tc;
}
new_cfg->etscfg.tsatable[tc] = ICE_IEEE_TSA_ETS;
}
/**
* ice_dcbnl_get_pg_bwg_cfg_tx - Get CEE PGBW config
* @netdev: pointer to the netdev struct
* @pgid: corresponding traffic class
* @bw_pct: the BW percentage for the corresponding TC
*/
static void
ice_dcbnl_get_pg_bwg_cfg_tx(struct net_device *netdev, int pgid, u8 *bw_pct)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_port_info *pi = pf->hw.port_info;
if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
!(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
return;
if (pgid >= ICE_MAX_TRAFFIC_CLASS)
return;
*bw_pct = pi->local_dcbx_cfg.etscfg.tcbwtable[pgid];
dev_dbg(&pf->pdev->dev, "Get PG BW config tc=%d bw_pct=%d\n",
pgid, *bw_pct);
}
/**
* ice_dcbnl_set_pg_bwg_cfg_tx - set CEE PG Tx BW config
* @netdev: the corresponding netdev
* @pgid: Correspongind traffic class
* @bw_pct: the BW percentage for the specified TC
*/
static void
ice_dcbnl_set_pg_bwg_cfg_tx(struct net_device *netdev, int pgid, u8 bw_pct)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_dcbx_cfg *new_cfg;
if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
!(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
return;
if (pgid >= ICE_MAX_TRAFFIC_CLASS)
return;
new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
new_cfg->etscfg.tcbwtable[pgid] = bw_pct;
}
/**
* ice_dcbnl_get_pg_tc_cfg_rx - Get CEE PG Rx config
* @netdev: pointer to netdev struct
* @prio: the corresponding user priority
* @prio_type: the traffic priority type
* @pgid: the PG ID
* @bw_pct: the BW percentage for the corresponding BWG
* @up_map: prio mapped to corresponding TC
*/
static void
ice_dcbnl_get_pg_tc_cfg_rx(struct net_device *netdev, int prio,
u8 __always_unused *prio_type, u8 *pgid,
u8 __always_unused *bw_pct,
u8 __always_unused *up_map)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_port_info *pi = pf->hw.port_info;
if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
!(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
return;
if (prio >= ICE_MAX_USER_PRIORITY)
return;
*pgid = pi->local_dcbx_cfg.etscfg.prio_table[prio];
}
/**
* ice_dcbnl_get_pg_bwg_cfg_rx - Get CEE PG BW Rx config
* @netdev: pointer to netdev struct
* @pgid: the corresponding traffic class
* @bw_pct: the BW percentage for the corresponding TC
*/
static void
ice_dcbnl_get_pg_bwg_cfg_rx(struct net_device *netdev, int __always_unused pgid,
u8 *bw_pct)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
!(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
return;
*bw_pct = 0;
}
/**
* ice_dcbnl_get_cap - Get DCBX capabilities of adapter
* @netdev: pointer to netdev struct
* @capid: the capability type
* @cap: the capability value
*/
static u8 ice_dcbnl_get_cap(struct net_device *netdev, int capid, u8 *cap)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
if (!(test_bit(ICE_FLAG_DCB_CAPABLE, pf->flags)))
return ICE_DCB_NO_HW_CHG;
switch (capid) {
case DCB_CAP_ATTR_PG:
*cap = true;
break;
case DCB_CAP_ATTR_PFC:
*cap = true;
break;
case DCB_CAP_ATTR_UP2TC:
*cap = false;
break;
case DCB_CAP_ATTR_PG_TCS:
*cap = 0x80;
break;
case DCB_CAP_ATTR_PFC_TCS:
*cap = 0x80;
break;
case DCB_CAP_ATTR_GSP:
*cap = false;
break;
case DCB_CAP_ATTR_BCN:
*cap = false;
break;
case DCB_CAP_ATTR_DCBX:
*cap = pf->dcbx_cap;
break;
default:
*cap = false;
break;
}
dev_dbg(&pf->pdev->dev, "DCBX Get Capability cap=%d capval=0x%x\n",
capid, *cap);
return 0;
}
/**
* ice_dcbnl_getapp - get CEE APP
* @netdev: pointer to netdev struct
* @idtype: the App selector
* @id: the App ethtype or port number
*/
static int ice_dcbnl_getapp(struct net_device *netdev, u8 idtype, u16 id)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct dcb_app app = {
.selector = idtype,
.protocol = id,
};
if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
!(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
return -EINVAL;
return dcb_getapp(netdev, &app);
}
/**
* ice_dcbnl_find_app - Search for APP in given DCB config
* @cfg: struct to hold DCBX config
* @app: struct to hold app data to look for
*/
static bool
ice_dcbnl_find_app(struct ice_dcbx_cfg *cfg,
struct ice_dcb_app_priority_table *app)
{
int i;
for (i = 0; i < cfg->numapps; i++) {
if (app->selector == cfg->app[i].selector &&
app->prot_id == cfg->app[i].prot_id &&
app->priority == cfg->app[i].priority)
return true;
}
return false;
}
/**
* ice_dcbnl_setapp - set local IEEE App config
* @netdev: relevant netdev struct
* @app: struct to hold app config info
*/
static int ice_dcbnl_setapp(struct net_device *netdev, struct dcb_app *app)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_dcb_app_priority_table new_app;
struct ice_dcbx_cfg *old_cfg, *new_cfg;
int ret;
if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
!(pf->dcbx_cap & DCB_CAP_DCBX_VER_IEEE))
return -EINVAL;
mutex_lock(&pf->tc_mutex);
new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
old_cfg = &pf->hw.port_info->local_dcbx_cfg;
if (old_cfg->numapps == ICE_DCBX_MAX_APPS) {
ret = -EINVAL;
goto setapp_out;
}
ret = dcb_ieee_setapp(netdev, app);
if (ret)
goto setapp_out;
new_app.selector = app->selector;
new_app.prot_id = app->protocol;
new_app.priority = app->priority;
if (ice_dcbnl_find_app(old_cfg, &new_app)) {
ret = 0;
goto setapp_out;
}
new_cfg->app[new_cfg->numapps++] = new_app;
ret = ice_pf_dcb_cfg(pf, new_cfg, true);
/* return of zero indicates new cfg applied */
if (ret == ICE_DCB_HW_CHG_RST)
ice_dcbnl_devreset(netdev);
if (ret == ICE_DCB_NO_HW_CHG)
ret = ICE_DCB_HW_CHG_RST;
setapp_out:
mutex_unlock(&pf->tc_mutex);
return ret;
}
/**
* ice_dcbnl_delapp - Delete local IEEE App config
* @netdev: relevant netdev
* @app: struct to hold app too delete
*
* Will not delete first application required by the FW
*/
static int ice_dcbnl_delapp(struct net_device *netdev, struct dcb_app *app)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_dcbx_cfg *old_cfg, *new_cfg;
int i, j, ret = 0;
if (pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED)
return -EINVAL;
mutex_lock(&pf->tc_mutex);
ret = dcb_ieee_delapp(netdev, app);
if (ret)
goto delapp_out;
old_cfg = &pf->hw.port_info->local_dcbx_cfg;
if (old_cfg->numapps == 1)
goto delapp_out;
new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
for (i = 1; i < new_cfg->numapps; i++) {
if (app->selector == new_cfg->app[i].selector &&
app->protocol == new_cfg->app[i].prot_id &&
app->priority == new_cfg->app[i].priority) {
new_cfg->app[i].selector = 0;
new_cfg->app[i].prot_id = 0;
new_cfg->app[i].priority = 0;
break;
}
}
/* Did not find DCB App */
if (i == new_cfg->numapps) {
ret = -EINVAL;
goto delapp_out;
}
new_cfg->numapps--;
for (j = i; j < new_cfg->numapps; j++) {
new_cfg->app[i].selector = old_cfg->app[j + 1].selector;
new_cfg->app[i].prot_id = old_cfg->app[j + 1].prot_id;
new_cfg->app[i].priority = old_cfg->app[j + 1].priority;
}
ret = ice_pf_dcb_cfg(pf, new_cfg, true);
/* return of zero indicates new cfg applied */
if (ret == ICE_DCB_HW_CHG_RST)
ice_dcbnl_devreset(netdev);
if (ret == ICE_DCB_NO_HW_CHG)
ret = ICE_DCB_HW_CHG_RST;
delapp_out:
mutex_unlock(&pf->tc_mutex);
return ret;
}
/**
* ice_dcbnl_cee_set_all - Commit CEE DCB settings to HW
* @netdev: the corresponding netdev
*/
static u8 ice_dcbnl_cee_set_all(struct net_device *netdev)
{
struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_dcbx_cfg *new_cfg;
int err;
if ((pf->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) ||
!(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
return ICE_DCB_NO_HW_CHG;
new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
mutex_lock(&pf->tc_mutex);
err = ice_pf_dcb_cfg(pf, new_cfg, true);
mutex_unlock(&pf->tc_mutex);
return (err != ICE_DCB_HW_CHG_RST) ? ICE_DCB_NO_HW_CHG : err;
}
static const struct dcbnl_rtnl_ops dcbnl_ops = {
/* IEEE 802.1Qaz std */
.ieee_getets = ice_dcbnl_getets,
.ieee_setets = ice_dcbnl_setets,
.ieee_getpfc = ice_dcbnl_getpfc,
.ieee_setpfc = ice_dcbnl_setpfc,
.ieee_setapp = ice_dcbnl_setapp,
.ieee_delapp = ice_dcbnl_delapp,
/* CEE std */
.getstate = ice_dcbnl_getstate,
.setstate = ice_dcbnl_setstate,
.getpermhwaddr = ice_dcbnl_get_perm_hw_addr,
.setpgtccfgtx = ice_dcbnl_set_pg_tc_cfg_tx,
.setpgbwgcfgtx = ice_dcbnl_set_pg_bwg_cfg_tx,
.getpgtccfgtx = ice_dcbnl_get_pg_tc_cfg_tx,
.getpgbwgcfgtx = ice_dcbnl_get_pg_bwg_cfg_tx,
.getpgtccfgrx = ice_dcbnl_get_pg_tc_cfg_rx,
.getpgbwgcfgrx = ice_dcbnl_get_pg_bwg_cfg_rx,
.setpfccfg = ice_dcbnl_set_pfc_cfg,
.getpfccfg = ice_dcbnl_get_pfc_cfg,
.setall = ice_dcbnl_cee_set_all,
.getcap = ice_dcbnl_get_cap,
.getnumtcs = ice_dcbnl_getnumtcs,
.getpfcstate = ice_dcbnl_getpfcstate,
.getapp = ice_dcbnl_getapp,
/* DCBX configuration */
.getdcbx = ice_dcbnl_getdcbx,
.setdcbx = ice_dcbnl_setdcbx,
};
/**
* ice_dcbnl_set_all - set all the apps and ieee data from DCBX config
* @vsi: pointer to VSI struct
*/
void ice_dcbnl_set_all(struct ice_vsi *vsi)
{
struct net_device *netdev = vsi->netdev;
struct ice_dcbx_cfg *dcbxcfg;
struct ice_port_info *pi;
struct dcb_app sapp;
struct ice_pf *pf;
int i;
if (!netdev)
return;
pf = ice_netdev_to_pf(netdev);
pi = pf->hw.port_info;
/* SW DCB taken care of by SW Default Config */
if (pf->dcbx_cap & DCB_CAP_DCBX_HOST)
return;
/* DCB not enabled */
if (!test_bit(ICE_FLAG_DCB_ENA, pf->flags))
return;
dcbxcfg = &pi->local_dcbx_cfg;
for (i = 0; i < dcbxcfg->numapps; i++) {
u8 prio, tc_map;
prio = dcbxcfg->app[i].priority;
tc_map = BIT(dcbxcfg->etscfg.prio_table[prio]);
/* Add APP only if the TC is enabled for this VSI */
if (tc_map & vsi->tc_cfg.ena_tc) {
sapp.selector = dcbxcfg->app[i].selector;
sapp.protocol = dcbxcfg->app[i].prot_id;
sapp.priority = prio;
dcb_ieee_setapp(netdev, &sapp);
}
}
/* Notify user-space of the changes */
dcbnl_ieee_notify(netdev, RTM_SETDCB, DCB_CMD_IEEE_SET, 0, 0);
}
/**
* ice_dcbnl_vsi_del_app - Delete APP on all VSIs
* @vsi: pointer to the main VSI
* @app: APP to delete
*
* Delete given APP from all the VSIs for given PF
*/
static void
ice_dcbnl_vsi_del_app(struct ice_vsi *vsi,
struct ice_dcb_app_priority_table *app)
{
struct dcb_app sapp;
int err;
sapp.selector = app->selector;
sapp.protocol = app->prot_id;
sapp.priority = app->priority;
err = ice_dcbnl_delapp(vsi->netdev, &sapp);
dev_dbg(&vsi->back->pdev->dev,
"Deleting app for VSI idx=%d err=%d sel=%d proto=0x%x, prio=%d\n",
vsi->idx, err, app->selector, app->prot_id, app->priority);
}
/**
* ice_dcbnl_flush_apps - Delete all removed APPs
* @pf: the corresponding PF
* @old_cfg: old DCBX configuration data
* @new_cfg: new DCBX configuration data
*
* Find and delete all APPS that are not present in the passed
* DCB configuration
*/
void
ice_dcbnl_flush_apps(struct ice_pf *pf, struct ice_dcbx_cfg *old_cfg,
struct ice_dcbx_cfg *new_cfg)
{
struct ice_vsi *main_vsi = ice_get_main_vsi(pf);
int i;
if (!main_vsi)
return;
for (i = 0; i < old_cfg->numapps; i++) {
struct ice_dcb_app_priority_table app = old_cfg->app[i];
/* The APP is not available anymore delete it */
if (!ice_dcbnl_find_app(new_cfg, &app))
ice_dcbnl_vsi_del_app(main_vsi, &app);
}
}
/**
* ice_dcbnl_setup - setup DCBNL
* @vsi: VSI to get associated netdev from
*/
void ice_dcbnl_setup(struct ice_vsi *vsi)
{
struct net_device *netdev = vsi->netdev;
struct ice_pf *pf;
pf = ice_netdev_to_pf(netdev);
if (!test_bit(ICE_FLAG_DCB_CAPABLE, pf->flags))
return;
netdev->dcbnl_ops = &dcbnl_ops;
ice_dcbnl_set_all(vsi);
}
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2019, Intel Corporation. */
#ifndef _ICE_DCB_NL_H_
#define _ICE_DCB_NL_H_
#ifdef CONFIG_DCB
void ice_dcbnl_setup(struct ice_vsi *vsi);
void ice_dcbnl_set_all(struct ice_vsi *vsi);
void
ice_dcbnl_flush_apps(struct ice_pf *pf, struct ice_dcbx_cfg *old_cfg,
struct ice_dcbx_cfg *new_cfg);
#else
#define ice_dcbnl_setup(vsi) do {} while (0)
#define ice_dcbnl_set_all(vsi) do {} while (0)
#define ice_dcbnl_flush_apps(pf, old_cfg, new_cfg) do {} while (0)
#endif /* CONFIG_DCB */
#endif /* _ICE_DCB_NL_H_ */
......@@ -1206,11 +1206,6 @@ static int ice_set_priv_flags(struct net_device *netdev, u32 flags)
status = ice_init_pf_dcb(pf, true);
if (status)
dev_warn(&pf->pdev->dev, "Fail to init DCB\n");
/* Forward LLDP packets to default VSI so that they
* are passed up the stack
*/
ice_cfg_sw_lldp(vsi, false, true);
} else {
enum ice_status status;
bool dcbx_agent_status;
......@@ -2654,14 +2649,14 @@ ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
netdev_info(netdev, "Changing Tx descriptor count from %d to %d\n",
vsi->tx_rings[0]->count, new_tx_cnt);
tx_rings = devm_kcalloc(&pf->pdev->dev, vsi->alloc_txq,
tx_rings = devm_kcalloc(&pf->pdev->dev, vsi->num_txq,
sizeof(*tx_rings), GFP_KERNEL);
if (!tx_rings) {
err = -ENOMEM;
goto done;
}
for (i = 0; i < vsi->alloc_txq; i++) {
ice_for_each_txq(vsi, i) {
/* clone ring and setup updated count */
tx_rings[i] = *vsi->tx_rings[i];
tx_rings[i].count = new_tx_cnt;
......@@ -2714,14 +2709,14 @@ ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
netdev_info(netdev, "Changing Rx descriptor count from %d to %d\n",
vsi->rx_rings[0]->count, new_rx_cnt);
rx_rings = devm_kcalloc(&pf->pdev->dev, vsi->alloc_rxq,
rx_rings = devm_kcalloc(&pf->pdev->dev, vsi->num_rxq,
sizeof(*rx_rings), GFP_KERNEL);
if (!rx_rings) {
err = -ENOMEM;
goto done;
}
for (i = 0; i < vsi->alloc_rxq; i++) {
ice_for_each_rxq(vsi, i) {
/* clone ring and setup updated count */
rx_rings[i] = *vsi->rx_rings[i];
rx_rings[i].count = new_rx_cnt;
......@@ -2759,7 +2754,7 @@ ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
ice_down(vsi);
if (tx_rings) {
for (i = 0; i < vsi->alloc_txq; i++) {
ice_for_each_txq(vsi, i) {
ice_free_tx_ring(vsi->tx_rings[i]);
*vsi->tx_rings[i] = tx_rings[i];
}
......@@ -2767,7 +2762,7 @@ ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
}
if (rx_rings) {
for (i = 0; i < vsi->alloc_rxq; i++) {
ice_for_each_rxq(vsi, i) {
ice_free_rx_ring(vsi->rx_rings[i]);
/* copy the real tail offset */
rx_rings[i].tail = vsi->rx_rings[i]->tail;
......@@ -2801,7 +2796,7 @@ ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
free_tx:
/* error cleanup if the Rx allocations failed after getting Tx */
if (tx_rings) {
for (i = 0; i < vsi->alloc_txq; i++)
ice_for_each_txq(vsi, i)
ice_free_tx_ring(&tx_rings[i]);
devm_kfree(&pf->pdev->dev, tx_rings);
}
......
......@@ -52,6 +52,9 @@
#define PF_MBX_ATQLEN_ATQLEN_M ICE_M(0x3FF, 0)
#define PF_MBX_ATQLEN_ATQENABLE_M BIT(31)
#define PF_MBX_ATQT 0x0022E300
#define PRTDCB_GENC 0x00083000
#define PRTDCB_GENC_PFCLDA_S 16
#define PRTDCB_GENC_PFCLDA_M ICE_M(0xFFFF, 16)
#define PRTDCB_GENS 0x00083020
#define PRTDCB_GENS_DCBX_STATUS_S 0
#define PRTDCB_GENS_DCBX_STATUS_M ICE_M(0x7, 0)
......
......@@ -211,7 +211,7 @@ enum ice_flex_rx_mdid {
/* Rx/Tx Flag64 packet flag bits */
enum ice_flg64_bits {
ICE_FLG_PKT_DSI = 0,
ICE_FLG_EVLAN_x8100 = 15,
ICE_FLG_EVLAN_x8100 = 14,
ICE_FLG_EVLAN_x9100,
ICE_FLG_VLAN_x8100,
ICE_FLG_TNL_MAC = 22,
......
......@@ -6,6 +6,24 @@
#include "ice_lib.h"
#include "ice_dcb_lib.h"
/**
* ice_vsi_type_str - maps VSI type enum to string equivalents
* @type: VSI type enum
*/
const char *ice_vsi_type_str(enum ice_vsi_type type)
{
switch (type) {
case ICE_VSI_PF:
return "ICE_VSI_PF";
case ICE_VSI_VF:
return "ICE_VSI_VF";
case ICE_VSI_LB:
return "ICE_VSI_LB";
default:
return "unknown";
}
}
/**
* ice_vsi_ctrl_rx_rings - Start or stop a VSI's Rx rings
* @vsi: the VSI being configured
......@@ -700,7 +718,8 @@ static void ice_set_rss_vsi_ctx(struct ice_vsi_ctx *ctxt, struct ice_vsi *vsi)
hash_type = ICE_AQ_VSI_Q_OPT_RSS_TPLZ;
break;
case ICE_VSI_LB:
dev_dbg(&pf->pdev->dev, "Unsupported VSI type %d\n", vsi->type);
dev_dbg(&pf->pdev->dev, "Unsupported VSI type %s\n",
ice_vsi_type_str(vsi->type));
return;
default:
dev_warn(&pf->pdev->dev, "Unknown VSI type %d\n", vsi->type);
......@@ -1881,22 +1900,16 @@ ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi,
* out PAUSE or PFC frames. If enabled, FW can still send FC frames.
* The rule is added once for PF VSI in order to create appropriate
* recipe, since VSI/VSI list is ignored with drop action...
* Also add rules to handle LLDP Tx and Rx packets. Tx LLDP packets
* need to be dropped so that VFs cannot send LLDP packets to reconfig
* DCB settings in the HW. Also, if the FW DCBX engine is not running
* then Rx LLDP packets need to be redirected up the stack.
* Also add rules to handle LLDP Tx packets. Tx LLDP packets need to
* be dropped so that VFs cannot send LLDP packets to reconfig DCB
* settings in the HW.
*/
if (!ice_is_safe_mode(pf)) {
if (!ice_is_safe_mode(pf))
if (vsi->type == ICE_VSI_PF) {
ice_vsi_add_rem_eth_mac(vsi, true);
/* Tx LLDP packets */
ice_cfg_sw_lldp(vsi, true, true);
/* Rx LLDP packets */
if (!test_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags))
ice_cfg_sw_lldp(vsi, false, true);
}
}
return vsi;
......@@ -2040,6 +2053,62 @@ void ice_vsi_close(struct ice_vsi *vsi)
ice_vsi_free_rx_rings(vsi);
}
/**
* ice_ena_vsi - resume a VSI
* @vsi: the VSI being resume
* @locked: is the rtnl_lock already held
*/
int ice_ena_vsi(struct ice_vsi *vsi, bool locked)
{
int err = 0;
if (!test_bit(__ICE_NEEDS_RESTART, vsi->state))
return 0;
clear_bit(__ICE_NEEDS_RESTART, vsi->state);
if (vsi->netdev && vsi->type == ICE_VSI_PF) {
if (netif_running(vsi->netdev)) {
if (!locked)
rtnl_lock();
err = ice_open(vsi->netdev);
if (!locked)
rtnl_unlock();
}
}
return err;
}
/**
* ice_dis_vsi - pause a VSI
* @vsi: the VSI being paused
* @locked: is the rtnl_lock already held
*/
void ice_dis_vsi(struct ice_vsi *vsi, bool locked)
{
if (test_bit(__ICE_DOWN, vsi->state))
return;
set_bit(__ICE_NEEDS_RESTART, vsi->state);
if (vsi->type == ICE_VSI_PF && vsi->netdev) {
if (netif_running(vsi->netdev)) {
if (!locked)
rtnl_lock();
ice_stop(vsi->netdev);
if (!locked)
rtnl_unlock();
} else {
ice_vsi_close(vsi);
}
}
}
/**
* ice_free_res - free a block of resources
* @res: pointer to the resource
......@@ -2431,6 +2500,7 @@ int ice_vsi_rebuild(struct ice_vsi *vsi)
bool ice_is_reset_in_progress(unsigned long *state)
{
return test_bit(__ICE_RESET_OICR_RECV, state) ||
test_bit(__ICE_DCBNL_DEVRESET, state) ||
test_bit(__ICE_PFR_REQ, state) ||
test_bit(__ICE_CORER_REQ, state) ||
test_bit(__ICE_GLOBR_REQ, state);
......
......@@ -6,6 +6,8 @@
#include "ice.h"
const char *ice_vsi_type_str(enum ice_vsi_type type);
int
ice_add_mac_to_list(struct ice_vsi *vsi, struct list_head *add_list,
const u8 *macaddr);
......@@ -62,6 +64,10 @@ int ice_vsi_release(struct ice_vsi *vsi);
void ice_vsi_close(struct ice_vsi *vsi);
int ice_ena_vsi(struct ice_vsi *vsi, bool locked);
void ice_dis_vsi(struct ice_vsi *vsi, bool locked);
int ice_free_res(struct ice_res_tracker *res, u16 index, u16 id);
int
......
......@@ -9,6 +9,7 @@
#include "ice_base.h"
#include "ice_lib.h"
#include "ice_dcb_lib.h"
#include "ice_dcb_nl.h"
#define DRV_VERSION_MAJOR 0
#define DRV_VERSION_MINOR 8
......@@ -435,43 +436,12 @@ static void ice_sync_fltr_subtask(struct ice_pf *pf)
}
}
/**
* ice_dis_vsi - pause a VSI
* @vsi: the VSI being paused
* @locked: is the rtnl_lock already held
*/
static void ice_dis_vsi(struct ice_vsi *vsi, bool locked)
{
if (test_bit(__ICE_DOWN, vsi->state))
return;
set_bit(__ICE_NEEDS_RESTART, vsi->state);
if (vsi->type == ICE_VSI_PF && vsi->netdev) {
if (netif_running(vsi->netdev)) {
if (!locked)
rtnl_lock();
ice_stop(vsi->netdev);
if (!locked)
rtnl_unlock();
} else {
ice_vsi_close(vsi);
}
}
}
/**
* ice_pf_dis_all_vsi - Pause all VSIs on a PF
* @pf: the PF
* @locked: is the rtnl_lock already held
*/
#ifdef CONFIG_DCB
void ice_pf_dis_all_vsi(struct ice_pf *pf, bool locked)
#else
static void ice_pf_dis_all_vsi(struct ice_pf *pf, bool locked)
#endif /* CONFIG_DCB */
{
int v;
......@@ -2547,6 +2517,9 @@ static int ice_setup_pf_sw(struct ice_pf *pf)
/* netdev has to be configured before setting frame size */
ice_vsi_cfg_frame_size(vsi);
/* Setup DCB netlink interface */
ice_dcbnl_setup(vsi);
/* registering the NAPI handler requires both the queues and
* netdev to be created, which are done in ice_pf_vsi_setup()
* and ice_cfg_netdev() respectively
......@@ -2627,6 +2600,7 @@ static void ice_deinit_pf(struct ice_pf *pf)
{
ice_service_task_stop(pf);
mutex_destroy(&pf->sw_mutex);
mutex_destroy(&pf->tc_mutex);
mutex_destroy(&pf->avail_q_mutex);
if (pf->avail_txqs) {
......@@ -2676,6 +2650,7 @@ static int ice_init_pf(struct ice_pf *pf)
ice_set_pf_caps(pf);
mutex_init(&pf->sw_mutex);
mutex_init(&pf->tc_mutex);
/* setup service timer and periodic service task */
timer_setup(&pf->serv_tmr, ice_service_timer, 0);
......@@ -2925,7 +2900,7 @@ ice_log_pkg_init(struct ice_hw *hw, enum ice_status *status)
ICE_PKG_SUPP_VER_MAJ, ICE_PKG_SUPP_VER_MNR);
break;
case ICE_ERR_AQ_ERROR:
switch (hw->adminq.sq_last_status) {
switch (hw->pkg_dwnld_status) {
case ICE_AQ_RC_ENOSEC:
case ICE_AQ_RC_EBADSIG:
dev_err(dev,
......@@ -3679,6 +3654,48 @@ static void ice_set_rx_mode(struct net_device *netdev)
ice_service_task_schedule(vsi->back);
}
/**
* ice_set_tx_maxrate - NDO callback to set the maximum per-queue bitrate
* @netdev: network interface device structure
* @queue_index: Queue ID
* @maxrate: maximum bandwidth in Mbps
*/
static int
ice_set_tx_maxrate(struct net_device *netdev, int queue_index, u32 maxrate)
{
struct ice_netdev_priv *np = netdev_priv(netdev);
struct ice_vsi *vsi = np->vsi;
enum ice_status status;
u16 q_handle;
u8 tc;
/* Validate maxrate requested is within permitted range */
if (maxrate && (maxrate > (ICE_SCHED_MAX_BW / 1000))) {
netdev_err(netdev,
"Invalid max rate %d specified for the queue %d\n",
maxrate, queue_index);
return -EINVAL;
}
q_handle = vsi->tx_rings[queue_index]->q_handle;
tc = ice_dcb_get_tc(vsi, queue_index);
/* Set BW back to default, when user set maxrate to 0 */
if (!maxrate)
status = ice_cfg_q_bw_dflt_lmt(vsi->port_info, vsi->idx, tc,
q_handle, ICE_MAX_BW);
else
status = ice_cfg_q_bw_lmt(vsi->port_info, vsi->idx, tc,
q_handle, ICE_MAX_BW, maxrate * 1000);
if (status) {
netdev_err(netdev,
"Unable to set Tx max rate, error %d\n", status);
return -EIO;
}
return 0;
}
/**
* ice_fdb_add - add an entry to the hardware database
* @ndm: the input from the stack
......@@ -3759,6 +3776,7 @@ ice_set_features(struct net_device *netdev, netdev_features_t features)
{
struct ice_netdev_priv *np = netdev_priv(netdev);
struct ice_vsi *vsi = np->vsi;
struct ice_pf *pf = vsi->back;
int ret = 0;
/* Don't set any netdev advanced features with device in Safe Mode */
......@@ -3768,6 +3786,13 @@ ice_set_features(struct net_device *netdev, netdev_features_t features)
return ret;
}
/* Do not change setting during reset */
if (ice_is_reset_in_progress(pf->state)) {
dev_err(&vsi->back->pdev->dev,
"Device is resetting, changing advanced netdev features temporarily unavailable.\n");
return -EBUSY;
}
/* Multiple features can be changed in one call so keep features in
* separate if/else statements to guarantee each feature is checked
*/
......@@ -4440,54 +4465,6 @@ static void ice_vsi_release_all(struct ice_pf *pf)
}
}
/**
* ice_ena_vsi - resume a VSI
* @vsi: the VSI being resume
* @locked: is the rtnl_lock already held
*/
static int ice_ena_vsi(struct ice_vsi *vsi, bool locked)
{
int err = 0;
if (!test_bit(__ICE_NEEDS_RESTART, vsi->state))
return 0;
clear_bit(__ICE_NEEDS_RESTART, vsi->state);
if (vsi->netdev && vsi->type == ICE_VSI_PF) {
if (netif_running(vsi->netdev)) {
if (!locked)
rtnl_lock();
err = ice_open(vsi->netdev);
if (!locked)
rtnl_unlock();
}
}
return err;
}
/**
* ice_pf_ena_all_vsi - Resume all VSIs on a PF
* @pf: the PF
* @locked: is the rtnl_lock already held
*/
#ifdef CONFIG_DCB
int ice_pf_ena_all_vsi(struct ice_pf *pf, bool locked)
{
int v;
ice_for_each_vsi(pf, v)
if (pf->vsi[v])
if (ice_ena_vsi(pf->vsi[v], locked))
return -EIO;
return 0;
}
#endif /* CONFIG_DCB */
/**
* ice_vsi_rebuild_by_type - Rebuild VSI of a given type
* @pf: pointer to the PF instance
......@@ -4510,8 +4487,8 @@ static int ice_vsi_rebuild_by_type(struct ice_pf *pf, enum ice_vsi_type type)
err = ice_vsi_rebuild(vsi);
if (err) {
dev_err(&pf->pdev->dev,
"rebuild VSI failed, err %d, VSI index %d, type %d\n",
err, vsi->idx, type);
"rebuild VSI failed, err %d, VSI index %d, type %s\n",
err, vsi->idx, ice_vsi_type_str(type));
return err;
}
......@@ -4519,8 +4496,8 @@ static int ice_vsi_rebuild_by_type(struct ice_pf *pf, enum ice_vsi_type type)
status = ice_replay_vsi(&pf->hw, vsi->idx);
if (status) {
dev_err(&pf->pdev->dev,
"replay VSI failed, status %d, VSI index %d, type %d\n",
status, vsi->idx, type);
"replay VSI failed, status %d, VSI index %d, type %s\n",
status, vsi->idx, ice_vsi_type_str(type));
return -EIO;
}
......@@ -4533,13 +4510,13 @@ static int ice_vsi_rebuild_by_type(struct ice_pf *pf, enum ice_vsi_type type)
err = ice_ena_vsi(vsi, false);
if (err) {
dev_err(&pf->pdev->dev,
"enable VSI failed, err %d, VSI index %d, type %d\n",
err, vsi->idx, type);
"enable VSI failed, err %d, VSI index %d, type %s\n",
err, vsi->idx, ice_vsi_type_str(type));
return err;
}
dev_info(&pf->pdev->dev, "VSI rebuilt. VSI index %d, type %d\n",
vsi->idx, type);
dev_info(&pf->pdev->dev, "VSI rebuilt. VSI index %d, type %s\n",
vsi->idx, ice_vsi_type_str(type));
}
return 0;
......@@ -5238,6 +5215,7 @@ static const struct net_device_ops ice_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = ice_change_mtu,
.ndo_get_stats64 = ice_get_stats64,
.ndo_set_tx_maxrate = ice_set_tx_maxrate,
.ndo_set_vf_spoofchk = ice_set_vf_spoofchk,
.ndo_set_vf_mac = ice_set_vf_mac,
.ndo_get_vf_config = ice_get_vf_cfg,
......
......@@ -410,6 +410,27 @@ ice_aq_add_sched_elems(struct ice_hw *hw, u16 grps_req,
grps_added, cd);
}
/**
* ice_aq_cfg_sched_elems - configures scheduler elements
* @hw: pointer to the HW struct
* @elems_req: number of elements to configure
* @buf: pointer to buffer
* @buf_size: buffer size in bytes
* @elems_cfgd: returns total number of elements configured
* @cd: pointer to command details structure or NULL
*
* Configure scheduling elements (0x0403)
*/
static enum ice_status
ice_aq_cfg_sched_elems(struct ice_hw *hw, u16 elems_req,
struct ice_aqc_conf_elem *buf, u16 buf_size,
u16 *elems_cfgd, struct ice_sq_cd *cd)
{
return ice_aqc_send_sched_elem_cmd(hw, ice_aqc_opc_cfg_sched_elems,
elems_req, (void *)buf, buf_size,
elems_cfgd, cd);
}
/**
* ice_aq_suspend_sched_elems - suspend scheduler elements
* @hw: pointer to the HW struct
......@@ -556,6 +577,149 @@ ice_alloc_lan_q_ctx(struct ice_hw *hw, u16 vsi_handle, u8 tc, u16 new_numqs)
return 0;
}
/**
* ice_aq_rl_profile - performs a rate limiting task
* @hw: pointer to the HW struct
* @opcode:opcode for add, query, or remove profile(s)
* @num_profiles: the number of profiles
* @buf: pointer to buffer
* @buf_size: buffer size in bytes
* @num_processed: number of processed add or remove profile(s) to return
* @cd: pointer to command details structure
*
* RL profile function to add, query, or remove profile(s)
*/
static enum ice_status
ice_aq_rl_profile(struct ice_hw *hw, enum ice_adminq_opc opcode,
u16 num_profiles, struct ice_aqc_rl_profile_generic_elem *buf,
u16 buf_size, u16 *num_processed, struct ice_sq_cd *cd)
{
struct ice_aqc_rl_profile *cmd;
struct ice_aq_desc desc;
enum ice_status status;
cmd = &desc.params.rl_profile;
ice_fill_dflt_direct_cmd_desc(&desc, opcode);
desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
cmd->num_profiles = cpu_to_le16(num_profiles);
status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
if (!status && num_processed)
*num_processed = le16_to_cpu(cmd->num_processed);
return status;
}
/**
* ice_aq_add_rl_profile - adds rate limiting profile(s)
* @hw: pointer to the HW struct
* @num_profiles: the number of profile(s) to be add
* @buf: pointer to buffer
* @buf_size: buffer size in bytes
* @num_profiles_added: total number of profiles added to return
* @cd: pointer to command details structure
*
* Add RL profile (0x0410)
*/
static enum ice_status
ice_aq_add_rl_profile(struct ice_hw *hw, u16 num_profiles,
struct ice_aqc_rl_profile_generic_elem *buf,
u16 buf_size, u16 *num_profiles_added,
struct ice_sq_cd *cd)
{
return ice_aq_rl_profile(hw, ice_aqc_opc_add_rl_profiles,
num_profiles, buf,
buf_size, num_profiles_added, cd);
}
/**
* ice_aq_remove_rl_profile - removes RL profile(s)
* @hw: pointer to the HW struct
* @num_profiles: the number of profile(s) to remove
* @buf: pointer to buffer
* @buf_size: buffer size in bytes
* @num_profiles_removed: total number of profiles removed to return
* @cd: pointer to command details structure or NULL
*
* Remove RL profile (0x0415)
*/
static enum ice_status
ice_aq_remove_rl_profile(struct ice_hw *hw, u16 num_profiles,
struct ice_aqc_rl_profile_generic_elem *buf,
u16 buf_size, u16 *num_profiles_removed,
struct ice_sq_cd *cd)
{
return ice_aq_rl_profile(hw, ice_aqc_opc_remove_rl_profiles,
num_profiles, buf,
buf_size, num_profiles_removed, cd);
}
/**
* ice_sched_del_rl_profile - remove RL profile
* @hw: pointer to the HW struct
* @rl_info: rate limit profile information
*
* If the profile ID is not referenced anymore, it removes profile ID with
* its associated parameters from HW DB,and locally. The caller needs to
* hold scheduler lock.
*/
static enum ice_status
ice_sched_del_rl_profile(struct ice_hw *hw,
struct ice_aqc_rl_profile_info *rl_info)
{
struct ice_aqc_rl_profile_generic_elem *buf;
u16 num_profiles_removed;
enum ice_status status;
u16 num_profiles = 1;
if (rl_info->prof_id_ref != 0)
return ICE_ERR_IN_USE;
/* Safe to remove profile ID */
buf = (struct ice_aqc_rl_profile_generic_elem *)
&rl_info->profile;
status = ice_aq_remove_rl_profile(hw, num_profiles, buf, sizeof(*buf),
&num_profiles_removed, NULL);
if (status || num_profiles_removed != num_profiles)
return ICE_ERR_CFG;
/* Delete stale entry now */
list_del(&rl_info->list_entry);
devm_kfree(ice_hw_to_dev(hw), rl_info);
return status;
}
/**
* ice_sched_clear_rl_prof - clears RL prof entries
* @pi: port information structure
*
* This function removes all RL profile from HW as well as from SW DB.
*/
static void ice_sched_clear_rl_prof(struct ice_port_info *pi)
{
u16 ln;
for (ln = 0; ln < pi->hw->num_tx_sched_layers; ln++) {
struct ice_aqc_rl_profile_info *rl_prof_elem;
struct ice_aqc_rl_profile_info *rl_prof_tmp;
list_for_each_entry_safe(rl_prof_elem, rl_prof_tmp,
&pi->rl_prof_list[ln], list_entry) {
struct ice_hw *hw = pi->hw;
enum ice_status status;
rl_prof_elem->prof_id_ref = 0;
status = ice_sched_del_rl_profile(hw, rl_prof_elem);
if (status) {
ice_debug(hw, ICE_DBG_SCHED,
"Remove rl profile failed\n");
/* On error, free mem required */
list_del(&rl_prof_elem->list_entry);
devm_kfree(ice_hw_to_dev(hw), rl_prof_elem);
}
}
}
}
/**
* ice_sched_clear_agg - clears the aggregator related information
* @hw: pointer to the hardware structure
......@@ -592,6 +756,8 @@ static void ice_sched_clear_tx_topo(struct ice_port_info *pi)
{
if (!pi)
return;
/* remove RL profiles related lists */
ice_sched_clear_rl_prof(pi);
if (pi->root) {
ice_free_sched_node(pi, pi->root);
pi->root = NULL;
......@@ -1014,6 +1180,8 @@ enum ice_status ice_sched_init_port(struct ice_port_info *pi)
/* initialize the port for handling the scheduler tree */
pi->port_state = ICE_SCHED_PORT_STATE_READY;
mutex_init(&pi->sched_lock);
for (i = 0; i < ICE_AQC_TOPO_MAX_LEVEL_NUM; i++)
INIT_LIST_HEAD(&pi->rl_prof_list[i]);
err_init_port:
if (status && pi->root) {
......@@ -1062,8 +1230,8 @@ enum ice_status ice_sched_query_res_alloc(struct ice_hw *hw)
* and so on. This array will be populated from root (index 0) to
* qgroup layer 7. Leaf node has no children.
*/
for (i = 0; i < hw->num_tx_sched_layers; i++) {
max_sibl = buf->layer_props[i].max_sibl_grp_sz;
for (i = 0; i < hw->num_tx_sched_layers - 1; i++) {
max_sibl = buf->layer_props[i + 1].max_sibl_grp_sz;
hw->max_children[i] = le16_to_cpu(max_sibl);
}
......@@ -1670,3 +1838,1095 @@ enum ice_status ice_rm_vsi_lan_cfg(struct ice_port_info *pi, u16 vsi_handle)
{
return ice_sched_rm_vsi_cfg(pi, vsi_handle, ICE_SCHED_NODE_OWNER_LAN);
}
/**
* ice_sched_rm_unused_rl_prof - remove unused RL profile
* @pi: port information structure
*
* This function removes unused rate limit profiles from the HW and
* SW DB. The caller needs to hold scheduler lock.
*/
static void ice_sched_rm_unused_rl_prof(struct ice_port_info *pi)
{
u16 ln;
for (ln = 0; ln < pi->hw->num_tx_sched_layers; ln++) {
struct ice_aqc_rl_profile_info *rl_prof_elem;
struct ice_aqc_rl_profile_info *rl_prof_tmp;
list_for_each_entry_safe(rl_prof_elem, rl_prof_tmp,
&pi->rl_prof_list[ln], list_entry) {
if (!ice_sched_del_rl_profile(pi->hw, rl_prof_elem))
ice_debug(pi->hw, ICE_DBG_SCHED,
"Removed rl profile\n");
}
}
}
/**
* ice_sched_update_elem - update element
* @hw: pointer to the HW struct
* @node: pointer to node
* @info: node info to update
*
* It updates the HW DB, and local SW DB of node. It updates the scheduling
* parameters of node from argument info data buffer (Info->data buf) and
* returns success or error on config sched element failure. The caller
* needs to hold scheduler lock.
*/
static enum ice_status
ice_sched_update_elem(struct ice_hw *hw, struct ice_sched_node *node,
struct ice_aqc_txsched_elem_data *info)
{
struct ice_aqc_conf_elem buf;
enum ice_status status;
u16 elem_cfgd = 0;
u16 num_elems = 1;
buf.generic[0] = *info;
/* Parent TEID is reserved field in this aq call */
buf.generic[0].parent_teid = 0;
/* Element type is reserved field in this aq call */
buf.generic[0].data.elem_type = 0;
/* Flags is reserved field in this aq call */
buf.generic[0].data.flags = 0;
/* Update HW DB */
/* Configure element node */
status = ice_aq_cfg_sched_elems(hw, num_elems, &buf, sizeof(buf),
&elem_cfgd, NULL);
if (status || elem_cfgd != num_elems) {
ice_debug(hw, ICE_DBG_SCHED, "Config sched elem error\n");
return ICE_ERR_CFG;
}
/* Config success case */
/* Now update local SW DB */
/* Only copy the data portion of info buffer */
node->info.data = info->data;
return status;
}
/**
* ice_sched_cfg_node_bw_alloc - configure node BW weight/alloc params
* @hw: pointer to the HW struct
* @node: sched node to configure
* @rl_type: rate limit type CIR, EIR, or shared
* @bw_alloc: BW weight/allocation
*
* This function configures node element's BW allocation.
*/
static enum ice_status
ice_sched_cfg_node_bw_alloc(struct ice_hw *hw, struct ice_sched_node *node,
enum ice_rl_type rl_type, u8 bw_alloc)
{
struct ice_aqc_txsched_elem_data buf;
struct ice_aqc_txsched_elem *data;
enum ice_status status;
buf = node->info;
data = &buf.data;
if (rl_type == ICE_MIN_BW) {
data->valid_sections |= ICE_AQC_ELEM_VALID_CIR;
data->cir_bw.bw_alloc = cpu_to_le16(bw_alloc);
} else if (rl_type == ICE_MAX_BW) {
data->valid_sections |= ICE_AQC_ELEM_VALID_EIR;
data->eir_bw.bw_alloc = cpu_to_le16(bw_alloc);
} else {
return ICE_ERR_PARAM;
}
/* Configure element */
status = ice_sched_update_elem(hw, node, &buf);
return status;
}
/**
* ice_set_clear_cir_bw - set or clear CIR BW
* @bw_t_info: bandwidth type information structure
* @bw: bandwidth in Kbps - Kilo bits per sec
*
* Save or clear CIR bandwidth (BW) in the passed param bw_t_info.
*/
static void
ice_set_clear_cir_bw(struct ice_bw_type_info *bw_t_info, u32 bw)
{
if (bw == ICE_SCHED_DFLT_BW) {
clear_bit(ICE_BW_TYPE_CIR, bw_t_info->bw_t_bitmap);
bw_t_info->cir_bw.bw = 0;
} else {
/* Save type of BW information */
set_bit(ICE_BW_TYPE_CIR, bw_t_info->bw_t_bitmap);
bw_t_info->cir_bw.bw = bw;
}
}
/**
* ice_set_clear_eir_bw - set or clear EIR BW
* @bw_t_info: bandwidth type information structure
* @bw: bandwidth in Kbps - Kilo bits per sec
*
* Save or clear EIR bandwidth (BW) in the passed param bw_t_info.
*/
static void
ice_set_clear_eir_bw(struct ice_bw_type_info *bw_t_info, u32 bw)
{
if (bw == ICE_SCHED_DFLT_BW) {
clear_bit(ICE_BW_TYPE_EIR, bw_t_info->bw_t_bitmap);
bw_t_info->eir_bw.bw = 0;
} else {
/* EIR BW and Shared BW profiles are mutually exclusive and
* hence only one of them may be set for any given element.
* First clear earlier saved shared BW information.
*/
clear_bit(ICE_BW_TYPE_SHARED, bw_t_info->bw_t_bitmap);
bw_t_info->shared_bw = 0;
/* save EIR BW information */
set_bit(ICE_BW_TYPE_EIR, bw_t_info->bw_t_bitmap);
bw_t_info->eir_bw.bw = bw;
}
}
/**
* ice_set_clear_shared_bw - set or clear shared BW
* @bw_t_info: bandwidth type information structure
* @bw: bandwidth in Kbps - Kilo bits per sec
*
* Save or clear shared bandwidth (BW) in the passed param bw_t_info.
*/
static void
ice_set_clear_shared_bw(struct ice_bw_type_info *bw_t_info, u32 bw)
{
if (bw == ICE_SCHED_DFLT_BW) {
clear_bit(ICE_BW_TYPE_SHARED, bw_t_info->bw_t_bitmap);
bw_t_info->shared_bw = 0;
} else {
/* EIR BW and Shared BW profiles are mutually exclusive and
* hence only one of them may be set for any given element.
* First clear earlier saved EIR BW information.
*/
clear_bit(ICE_BW_TYPE_EIR, bw_t_info->bw_t_bitmap);
bw_t_info->eir_bw.bw = 0;
/* save shared BW information */
set_bit(ICE_BW_TYPE_SHARED, bw_t_info->bw_t_bitmap);
bw_t_info->shared_bw = bw;
}
}
/**
* ice_sched_calc_wakeup - calculate RL profile wakeup parameter
* @bw: bandwidth in Kbps
*
* This function calculates the wakeup parameter of RL profile.
*/
static u16 ice_sched_calc_wakeup(s32 bw)
{
s64 bytes_per_sec, wakeup_int, wakeup_a, wakeup_b, wakeup_f;
s32 wakeup_f_int;
u16 wakeup = 0;
/* Get the wakeup integer value */
bytes_per_sec = div64_long(((s64)bw * 1000), BITS_PER_BYTE);
wakeup_int = div64_long(ICE_RL_PROF_FREQUENCY, bytes_per_sec);
if (wakeup_int > 63) {
wakeup = (u16)((1 << 15) | wakeup_int);
} else {
/* Calculate fraction value up to 4 decimals
* Convert Integer value to a constant multiplier
*/
wakeup_b = (s64)ICE_RL_PROF_MULTIPLIER * wakeup_int;
wakeup_a = div64_long((s64)ICE_RL_PROF_MULTIPLIER *
ICE_RL_PROF_FREQUENCY,
bytes_per_sec);
/* Get Fraction value */
wakeup_f = wakeup_a - wakeup_b;
/* Round up the Fractional value via Ceil(Fractional value) */
if (wakeup_f > div64_long(ICE_RL_PROF_MULTIPLIER, 2))
wakeup_f += 1;
wakeup_f_int = (s32)div64_long(wakeup_f * ICE_RL_PROF_FRACTION,
ICE_RL_PROF_MULTIPLIER);
wakeup |= (u16)(wakeup_int << 9);
wakeup |= (u16)(0x1ff & wakeup_f_int);
}
return wakeup;
}
/**
* ice_sched_bw_to_rl_profile - convert BW to profile parameters
* @bw: bandwidth in Kbps
* @profile: profile parameters to return
*
* This function converts the BW to profile structure format.
*/
static enum ice_status
ice_sched_bw_to_rl_profile(u32 bw, struct ice_aqc_rl_profile_elem *profile)
{
enum ice_status status = ICE_ERR_PARAM;
s64 bytes_per_sec, ts_rate, mv_tmp;
bool found = false;
s32 encode = 0;
s64 mv = 0;
s32 i;
/* Bw settings range is from 0.5Mb/sec to 100Gb/sec */
if (bw < ICE_SCHED_MIN_BW || bw > ICE_SCHED_MAX_BW)
return status;
/* Bytes per second from Kbps */
bytes_per_sec = div64_long(((s64)bw * 1000), BITS_PER_BYTE);
/* encode is 6 bits but really useful are 5 bits */
for (i = 0; i < 64; i++) {
u64 pow_result = BIT_ULL(i);
ts_rate = div64_long((s64)ICE_RL_PROF_FREQUENCY,
pow_result * ICE_RL_PROF_TS_MULTIPLIER);
if (ts_rate <= 0)
continue;
/* Multiplier value */
mv_tmp = div64_long(bytes_per_sec * ICE_RL_PROF_MULTIPLIER,
ts_rate);
/* Round to the nearest ICE_RL_PROF_MULTIPLIER */
mv = round_up_64bit(mv_tmp, ICE_RL_PROF_MULTIPLIER);
/* First multiplier value greater than the given
* accuracy bytes
*/
if (mv > ICE_RL_PROF_ACCURACY_BYTES) {
encode = i;
found = true;
break;
}
}
if (found) {
u16 wm;
wm = ice_sched_calc_wakeup(bw);
profile->rl_multiply = cpu_to_le16(mv);
profile->wake_up_calc = cpu_to_le16(wm);
profile->rl_encode = cpu_to_le16(encode);
status = 0;
} else {
status = ICE_ERR_DOES_NOT_EXIST;
}
return status;
}
/**
* ice_sched_add_rl_profile - add RL profile
* @pi: port information structure
* @rl_type: type of rate limit BW - min, max, or shared
* @bw: bandwidth in Kbps - Kilo bits per sec
* @layer_num: specifies in which layer to create profile
*
* This function first checks the existing list for corresponding BW
* parameter. If it exists, it returns the associated profile otherwise
* it creates a new rate limit profile for requested BW, and adds it to
* the HW DB and local list. It returns the new profile or null on error.
* The caller needs to hold the scheduler lock.
*/
static struct ice_aqc_rl_profile_info *
ice_sched_add_rl_profile(struct ice_port_info *pi,
enum ice_rl_type rl_type, u32 bw, u8 layer_num)
{
struct ice_aqc_rl_profile_generic_elem *buf;
struct ice_aqc_rl_profile_info *rl_prof_elem;
u16 profiles_added = 0, num_profiles = 1;
enum ice_status status;
struct ice_hw *hw;
u8 profile_type;
if (layer_num >= ICE_AQC_TOPO_MAX_LEVEL_NUM)
return NULL;
switch (rl_type) {
case ICE_MIN_BW:
profile_type = ICE_AQC_RL_PROFILE_TYPE_CIR;
break;
case ICE_MAX_BW:
profile_type = ICE_AQC_RL_PROFILE_TYPE_EIR;
break;
case ICE_SHARED_BW:
profile_type = ICE_AQC_RL_PROFILE_TYPE_SRL;
break;
default:
return NULL;
}
if (!pi)
return NULL;
hw = pi->hw;
list_for_each_entry(rl_prof_elem, &pi->rl_prof_list[layer_num],
list_entry)
if (rl_prof_elem->profile.flags == profile_type &&
rl_prof_elem->bw == bw)
/* Return existing profile ID info */
return rl_prof_elem;
/* Create new profile ID */
rl_prof_elem = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*rl_prof_elem),
GFP_KERNEL);
if (!rl_prof_elem)
return NULL;
status = ice_sched_bw_to_rl_profile(bw, &rl_prof_elem->profile);
if (status)
goto exit_add_rl_prof;
rl_prof_elem->bw = bw;
/* layer_num is zero relative, and fw expects level from 1 to 9 */
rl_prof_elem->profile.level = layer_num + 1;
rl_prof_elem->profile.flags = profile_type;
rl_prof_elem->profile.max_burst_size = cpu_to_le16(hw->max_burst_size);
/* Create new entry in HW DB */
buf = (struct ice_aqc_rl_profile_generic_elem *)
&rl_prof_elem->profile;
status = ice_aq_add_rl_profile(hw, num_profiles, buf, sizeof(*buf),
&profiles_added, NULL);
if (status || profiles_added != num_profiles)
goto exit_add_rl_prof;
/* Good entry - add in the list */
rl_prof_elem->prof_id_ref = 0;
list_add(&rl_prof_elem->list_entry, &pi->rl_prof_list[layer_num]);
return rl_prof_elem;
exit_add_rl_prof:
devm_kfree(ice_hw_to_dev(hw), rl_prof_elem);
return NULL;
}
/**
* ice_sched_cfg_node_bw_lmt - configure node sched params
* @hw: pointer to the HW struct
* @node: sched node to configure
* @rl_type: rate limit type CIR, EIR, or shared
* @rl_prof_id: rate limit profile ID
*
* This function configures node element's BW limit.
*/
static enum ice_status
ice_sched_cfg_node_bw_lmt(struct ice_hw *hw, struct ice_sched_node *node,
enum ice_rl_type rl_type, u16 rl_prof_id)
{
struct ice_aqc_txsched_elem_data buf;
struct ice_aqc_txsched_elem *data;
buf = node->info;
data = &buf.data;
switch (rl_type) {
case ICE_MIN_BW:
data->valid_sections |= ICE_AQC_ELEM_VALID_CIR;
data->cir_bw.bw_profile_idx = cpu_to_le16(rl_prof_id);
break;
case ICE_MAX_BW:
/* EIR BW and Shared BW profiles are mutually exclusive and
* hence only one of them may be set for any given element
*/
if (data->valid_sections & ICE_AQC_ELEM_VALID_SHARED)
return ICE_ERR_CFG;
data->valid_sections |= ICE_AQC_ELEM_VALID_EIR;
data->eir_bw.bw_profile_idx = cpu_to_le16(rl_prof_id);
break;
case ICE_SHARED_BW:
/* Check for removing shared BW */
if (rl_prof_id == ICE_SCHED_NO_SHARED_RL_PROF_ID) {
/* remove shared profile */
data->valid_sections &= ~ICE_AQC_ELEM_VALID_SHARED;
data->srl_id = 0; /* clear SRL field */
/* enable back EIR to default profile */
data->valid_sections |= ICE_AQC_ELEM_VALID_EIR;
data->eir_bw.bw_profile_idx =
cpu_to_le16(ICE_SCHED_DFLT_RL_PROF_ID);
break;
}
/* EIR BW and Shared BW profiles are mutually exclusive and
* hence only one of them may be set for any given element
*/
if ((data->valid_sections & ICE_AQC_ELEM_VALID_EIR) &&
(le16_to_cpu(data->eir_bw.bw_profile_idx) !=
ICE_SCHED_DFLT_RL_PROF_ID))
return ICE_ERR_CFG;
/* EIR BW is set to default, disable it */
data->valid_sections &= ~ICE_AQC_ELEM_VALID_EIR;
/* Okay to enable shared BW now */
data->valid_sections |= ICE_AQC_ELEM_VALID_SHARED;
data->srl_id = cpu_to_le16(rl_prof_id);
break;
default:
/* Unknown rate limit type */
return ICE_ERR_PARAM;
}
/* Configure element */
return ice_sched_update_elem(hw, node, &buf);
}
/**
* ice_sched_get_node_rl_prof_id - get node's rate limit profile ID
* @node: sched node
* @rl_type: rate limit type
*
* If existing profile matches, it returns the corresponding rate
* limit profile ID, otherwise it returns an invalid ID as error.
*/
static u16
ice_sched_get_node_rl_prof_id(struct ice_sched_node *node,
enum ice_rl_type rl_type)
{
u16 rl_prof_id = ICE_SCHED_INVAL_PROF_ID;
struct ice_aqc_txsched_elem *data;
data = &node->info.data;
switch (rl_type) {
case ICE_MIN_BW:
if (data->valid_sections & ICE_AQC_ELEM_VALID_CIR)
rl_prof_id = le16_to_cpu(data->cir_bw.bw_profile_idx);
break;
case ICE_MAX_BW:
if (data->valid_sections & ICE_AQC_ELEM_VALID_EIR)
rl_prof_id = le16_to_cpu(data->eir_bw.bw_profile_idx);
break;
case ICE_SHARED_BW:
if (data->valid_sections & ICE_AQC_ELEM_VALID_SHARED)
rl_prof_id = le16_to_cpu(data->srl_id);
break;
default:
break;
}
return rl_prof_id;
}
/**
* ice_sched_get_rl_prof_layer - selects rate limit profile creation layer
* @pi: port information structure
* @rl_type: type of rate limit BW - min, max, or shared
* @layer_index: layer index
*
* This function returns requested profile creation layer.
*/
static u8
ice_sched_get_rl_prof_layer(struct ice_port_info *pi, enum ice_rl_type rl_type,
u8 layer_index)
{
struct ice_hw *hw = pi->hw;
if (layer_index >= hw->num_tx_sched_layers)
return ICE_SCHED_INVAL_LAYER_NUM;
switch (rl_type) {
case ICE_MIN_BW:
if (hw->layer_info[layer_index].max_cir_rl_profiles)
return layer_index;
break;
case ICE_MAX_BW:
if (hw->layer_info[layer_index].max_eir_rl_profiles)
return layer_index;
break;
case ICE_SHARED_BW:
/* if current layer doesn't support SRL profile creation
* then try a layer up or down.
*/
if (hw->layer_info[layer_index].max_srl_profiles)
return layer_index;
else if (layer_index < hw->num_tx_sched_layers - 1 &&
hw->layer_info[layer_index + 1].max_srl_profiles)
return layer_index + 1;
else if (layer_index > 0 &&
hw->layer_info[layer_index - 1].max_srl_profiles)
return layer_index - 1;
break;
default:
break;
}
return ICE_SCHED_INVAL_LAYER_NUM;
}
/**
* ice_sched_get_srl_node - get shared rate limit node
* @node: tree node
* @srl_layer: shared rate limit layer
*
* This function returns SRL node to be used for shared rate limit purpose.
* The caller needs to hold scheduler lock.
*/
static struct ice_sched_node *
ice_sched_get_srl_node(struct ice_sched_node *node, u8 srl_layer)
{
if (srl_layer > node->tx_sched_layer)
return node->children[0];
else if (srl_layer < node->tx_sched_layer)
/* Node can't be created without a parent. It will always
* have a valid parent except root node.
*/
return node->parent;
else
return node;
}
/**
* ice_sched_rm_rl_profile - remove RL profile ID
* @pi: port information structure
* @layer_num: layer number where profiles are saved
* @profile_type: profile type like EIR, CIR, or SRL
* @profile_id: profile ID to remove
*
* This function removes rate limit profile from layer 'layer_num' of type
* 'profile_type' and profile ID as 'profile_id'. The caller needs to hold
* scheduler lock.
*/
static enum ice_status
ice_sched_rm_rl_profile(struct ice_port_info *pi, u8 layer_num, u8 profile_type,
u16 profile_id)
{
struct ice_aqc_rl_profile_info *rl_prof_elem;
enum ice_status status = 0;
if (layer_num >= ICE_AQC_TOPO_MAX_LEVEL_NUM)
return ICE_ERR_PARAM;
/* Check the existing list for RL profile */
list_for_each_entry(rl_prof_elem, &pi->rl_prof_list[layer_num],
list_entry)
if (rl_prof_elem->profile.flags == profile_type &&
le16_to_cpu(rl_prof_elem->profile.profile_id) ==
profile_id) {
if (rl_prof_elem->prof_id_ref)
rl_prof_elem->prof_id_ref--;
/* Remove old profile ID from database */
status = ice_sched_del_rl_profile(pi->hw, rl_prof_elem);
if (status && status != ICE_ERR_IN_USE)
ice_debug(pi->hw, ICE_DBG_SCHED,
"Remove rl profile failed\n");
break;
}
if (status == ICE_ERR_IN_USE)
status = 0;
return status;
}
/**
* ice_sched_set_node_bw_dflt - set node's bandwidth limit to default
* @pi: port information structure
* @node: pointer to node structure
* @rl_type: rate limit type min, max, or shared
* @layer_num: layer number where RL profiles are saved
*
* This function configures node element's BW rate limit profile ID of
* type CIR, EIR, or SRL to default. This function needs to be called
* with the scheduler lock held.
*/
static enum ice_status
ice_sched_set_node_bw_dflt(struct ice_port_info *pi,
struct ice_sched_node *node,
enum ice_rl_type rl_type, u8 layer_num)
{
enum ice_status status;
struct ice_hw *hw;
u8 profile_type;
u16 rl_prof_id;
u16 old_id;
hw = pi->hw;
switch (rl_type) {
case ICE_MIN_BW:
profile_type = ICE_AQC_RL_PROFILE_TYPE_CIR;
rl_prof_id = ICE_SCHED_DFLT_RL_PROF_ID;
break;
case ICE_MAX_BW:
profile_type = ICE_AQC_RL_PROFILE_TYPE_EIR;
rl_prof_id = ICE_SCHED_DFLT_RL_PROF_ID;
break;
case ICE_SHARED_BW:
profile_type = ICE_AQC_RL_PROFILE_TYPE_SRL;
/* No SRL is configured for default case */
rl_prof_id = ICE_SCHED_NO_SHARED_RL_PROF_ID;
break;
default:
return ICE_ERR_PARAM;
}
/* Save existing RL prof ID for later clean up */
old_id = ice_sched_get_node_rl_prof_id(node, rl_type);
/* Configure BW scheduling parameters */
status = ice_sched_cfg_node_bw_lmt(hw, node, rl_type, rl_prof_id);
if (status)
return status;
/* Remove stale RL profile ID */
if (old_id == ICE_SCHED_DFLT_RL_PROF_ID ||
old_id == ICE_SCHED_INVAL_PROF_ID)
return 0;
return ice_sched_rm_rl_profile(pi, layer_num, profile_type, old_id);
}
/**
* ice_sched_set_eir_srl_excl - set EIR/SRL exclusiveness
* @pi: port information structure
* @node: pointer to node structure
* @layer_num: layer number where rate limit profiles are saved
* @rl_type: rate limit type min, max, or shared
* @bw: bandwidth value
*
* This function prepares node element's bandwidth to SRL or EIR exclusively.
* EIR BW and Shared BW profiles are mutually exclusive and hence only one of
* them may be set for any given element. This function needs to be called
* with the scheduler lock held.
*/
static enum ice_status
ice_sched_set_eir_srl_excl(struct ice_port_info *pi,
struct ice_sched_node *node,
u8 layer_num, enum ice_rl_type rl_type, u32 bw)
{
if (rl_type == ICE_SHARED_BW) {
/* SRL node passed in this case, it may be different node */
if (bw == ICE_SCHED_DFLT_BW)
/* SRL being removed, ice_sched_cfg_node_bw_lmt()
* enables EIR to default. EIR is not set in this
* case, so no additional action is required.
*/
return 0;
/* SRL being configured, set EIR to default here.
* ice_sched_cfg_node_bw_lmt() disables EIR when it
* configures SRL
*/
return ice_sched_set_node_bw_dflt(pi, node, ICE_MAX_BW,
layer_num);
} else if (rl_type == ICE_MAX_BW &&
node->info.data.valid_sections & ICE_AQC_ELEM_VALID_SHARED) {
/* Remove Shared profile. Set default shared BW call
* removes shared profile for a node.
*/
return ice_sched_set_node_bw_dflt(pi, node,
ICE_SHARED_BW,
layer_num);
}
return 0;
}
/**
* ice_sched_set_node_bw - set node's bandwidth
* @pi: port information structure
* @node: tree node
* @rl_type: rate limit type min, max, or shared
* @bw: bandwidth in Kbps - Kilo bits per sec
* @layer_num: layer number
*
* This function adds new profile corresponding to requested BW, configures
* node's RL profile ID of type CIR, EIR, or SRL, and removes old profile
* ID from local database. The caller needs to hold scheduler lock.
*/
static enum ice_status
ice_sched_set_node_bw(struct ice_port_info *pi, struct ice_sched_node *node,
enum ice_rl_type rl_type, u32 bw, u8 layer_num)
{
struct ice_aqc_rl_profile_info *rl_prof_info;
enum ice_status status = ICE_ERR_PARAM;
struct ice_hw *hw = pi->hw;
u16 old_id, rl_prof_id;
rl_prof_info = ice_sched_add_rl_profile(pi, rl_type, bw, layer_num);
if (!rl_prof_info)
return status;
rl_prof_id = le16_to_cpu(rl_prof_info->profile.profile_id);
/* Save existing RL prof ID for later clean up */
old_id = ice_sched_get_node_rl_prof_id(node, rl_type);
/* Configure BW scheduling parameters */
status = ice_sched_cfg_node_bw_lmt(hw, node, rl_type, rl_prof_id);
if (status)
return status;
/* New changes has been applied */
/* Increment the profile ID reference count */
rl_prof_info->prof_id_ref++;
/* Check for old ID removal */
if ((old_id == ICE_SCHED_DFLT_RL_PROF_ID && rl_type != ICE_SHARED_BW) ||
old_id == ICE_SCHED_INVAL_PROF_ID || old_id == rl_prof_id)
return 0;
return ice_sched_rm_rl_profile(pi, layer_num,
rl_prof_info->profile.flags,
old_id);
}
/**
* ice_sched_set_node_bw_lmt - set node's BW limit
* @pi: port information structure
* @node: tree node
* @rl_type: rate limit type min, max, or shared
* @bw: bandwidth in Kbps - Kilo bits per sec
*
* It updates node's BW limit parameters like BW RL profile ID of type CIR,
* EIR, or SRL. The caller needs to hold scheduler lock.
*/
static enum ice_status
ice_sched_set_node_bw_lmt(struct ice_port_info *pi, struct ice_sched_node *node,
enum ice_rl_type rl_type, u32 bw)
{
struct ice_sched_node *cfg_node = node;
enum ice_status status;
struct ice_hw *hw;
u8 layer_num;
if (!pi)
return ICE_ERR_PARAM;
hw = pi->hw;
/* Remove unused RL profile IDs from HW and SW DB */
ice_sched_rm_unused_rl_prof(pi);
layer_num = ice_sched_get_rl_prof_layer(pi, rl_type,
node->tx_sched_layer);
if (layer_num >= hw->num_tx_sched_layers)
return ICE_ERR_PARAM;
if (rl_type == ICE_SHARED_BW) {
/* SRL node may be different */
cfg_node = ice_sched_get_srl_node(node, layer_num);
if (!cfg_node)
return ICE_ERR_CFG;
}
/* EIR BW and Shared BW profiles are mutually exclusive and
* hence only one of them may be set for any given element
*/
status = ice_sched_set_eir_srl_excl(pi, cfg_node, layer_num, rl_type,
bw);
if (status)
return status;
if (bw == ICE_SCHED_DFLT_BW)
return ice_sched_set_node_bw_dflt(pi, cfg_node, rl_type,
layer_num);
return ice_sched_set_node_bw(pi, cfg_node, rl_type, bw, layer_num);
}
/**
* ice_sched_set_node_bw_dflt_lmt - set node's BW limit to default
* @pi: port information structure
* @node: pointer to node structure
* @rl_type: rate limit type min, max, or shared
*
* This function configures node element's BW rate limit profile ID of
* type CIR, EIR, or SRL to default. This function needs to be called
* with the scheduler lock held.
*/
static enum ice_status
ice_sched_set_node_bw_dflt_lmt(struct ice_port_info *pi,
struct ice_sched_node *node,
enum ice_rl_type rl_type)
{
return ice_sched_set_node_bw_lmt(pi, node, rl_type,
ICE_SCHED_DFLT_BW);
}
/**
* ice_sched_validate_srl_node - Check node for SRL applicability
* @node: sched node to configure
* @sel_layer: selected SRL layer
*
* This function checks if the SRL can be applied to a selected layer node on
* behalf of the requested node (first argument). This function needs to be
* called with scheduler lock held.
*/
static enum ice_status
ice_sched_validate_srl_node(struct ice_sched_node *node, u8 sel_layer)
{
/* SRL profiles are not available on all layers. Check if the
* SRL profile can be applied to a node above or below the
* requested node. SRL configuration is possible only if the
* selected layer's node has single child.
*/
if (sel_layer == node->tx_sched_layer ||
((sel_layer == node->tx_sched_layer + 1) &&
node->num_children == 1) ||
((sel_layer == node->tx_sched_layer - 1) &&
(node->parent && node->parent->num_children == 1)))
return 0;
return ICE_ERR_CFG;
}
/**
* ice_sched_save_q_bw - save queue node's BW information
* @q_ctx: queue context structure
* @rl_type: rate limit type min, max, or shared
* @bw: bandwidth in Kbps - Kilo bits per sec
*
* Save BW information of queue type node for post replay use.
*/
static enum ice_status
ice_sched_save_q_bw(struct ice_q_ctx *q_ctx, enum ice_rl_type rl_type, u32 bw)
{
switch (rl_type) {
case ICE_MIN_BW:
ice_set_clear_cir_bw(&q_ctx->bw_t_info, bw);
break;
case ICE_MAX_BW:
ice_set_clear_eir_bw(&q_ctx->bw_t_info, bw);
break;
case ICE_SHARED_BW:
ice_set_clear_shared_bw(&q_ctx->bw_t_info, bw);
break;
default:
return ICE_ERR_PARAM;
}
return 0;
}
/**
* ice_sched_set_q_bw_lmt - sets queue BW limit
* @pi: port information structure
* @vsi_handle: sw VSI handle
* @tc: traffic class
* @q_handle: software queue handle
* @rl_type: min, max, or shared
* @bw: bandwidth in Kbps
*
* This function sets BW limit of queue scheduling node.
*/
static enum ice_status
ice_sched_set_q_bw_lmt(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
u16 q_handle, enum ice_rl_type rl_type, u32 bw)
{
enum ice_status status = ICE_ERR_PARAM;
struct ice_sched_node *node;
struct ice_q_ctx *q_ctx;
if (!ice_is_vsi_valid(pi->hw, vsi_handle))
return ICE_ERR_PARAM;
mutex_lock(&pi->sched_lock);
q_ctx = ice_get_lan_q_ctx(pi->hw, vsi_handle, tc, q_handle);
if (!q_ctx)
goto exit_q_bw_lmt;
node = ice_sched_find_node_by_teid(pi->root, q_ctx->q_teid);
if (!node) {
ice_debug(pi->hw, ICE_DBG_SCHED, "Wrong q_teid\n");
goto exit_q_bw_lmt;
}
/* Return error if it is not a leaf node */
if (node->info.data.elem_type != ICE_AQC_ELEM_TYPE_LEAF)
goto exit_q_bw_lmt;
/* SRL bandwidth layer selection */
if (rl_type == ICE_SHARED_BW) {
u8 sel_layer; /* selected layer */
sel_layer = ice_sched_get_rl_prof_layer(pi, rl_type,
node->tx_sched_layer);
if (sel_layer >= pi->hw->num_tx_sched_layers) {
status = ICE_ERR_PARAM;
goto exit_q_bw_lmt;
}
status = ice_sched_validate_srl_node(node, sel_layer);
if (status)
goto exit_q_bw_lmt;
}
if (bw == ICE_SCHED_DFLT_BW)
status = ice_sched_set_node_bw_dflt_lmt(pi, node, rl_type);
else
status = ice_sched_set_node_bw_lmt(pi, node, rl_type, bw);
if (!status)
status = ice_sched_save_q_bw(q_ctx, rl_type, bw);
exit_q_bw_lmt:
mutex_unlock(&pi->sched_lock);
return status;
}
/**
* ice_cfg_q_bw_lmt - configure queue BW limit
* @pi: port information structure
* @vsi_handle: sw VSI handle
* @tc: traffic class
* @q_handle: software queue handle
* @rl_type: min, max, or shared
* @bw: bandwidth in Kbps
*
* This function configures BW limit of queue scheduling node.
*/
enum ice_status
ice_cfg_q_bw_lmt(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
u16 q_handle, enum ice_rl_type rl_type, u32 bw)
{
return ice_sched_set_q_bw_lmt(pi, vsi_handle, tc, q_handle, rl_type,
bw);
}
/**
* ice_cfg_q_bw_dflt_lmt - configure queue BW default limit
* @pi: port information structure
* @vsi_handle: sw VSI handle
* @tc: traffic class
* @q_handle: software queue handle
* @rl_type: min, max, or shared
*
* This function configures BW default limit of queue scheduling node.
*/
enum ice_status
ice_cfg_q_bw_dflt_lmt(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
u16 q_handle, enum ice_rl_type rl_type)
{
return ice_sched_set_q_bw_lmt(pi, vsi_handle, tc, q_handle, rl_type,
ICE_SCHED_DFLT_BW);
}
/**
* ice_cfg_rl_burst_size - Set burst size value
* @hw: pointer to the HW struct
* @bytes: burst size in bytes
*
* This function configures/set the burst size to requested new value. The new
* burst size value is used for future rate limit calls. It doesn't change the
* existing or previously created RL profiles.
*/
enum ice_status ice_cfg_rl_burst_size(struct ice_hw *hw, u32 bytes)
{
u16 burst_size_to_prog;
if (bytes < ICE_MIN_BURST_SIZE_ALLOWED ||
bytes > ICE_MAX_BURST_SIZE_ALLOWED)
return ICE_ERR_PARAM;
if (ice_round_to_num(bytes, 64) <=
ICE_MAX_BURST_SIZE_64_BYTE_GRANULARITY) {
/* 64 byte granularity case */
/* Disable MSB granularity bit */
burst_size_to_prog = ICE_64_BYTE_GRANULARITY;
/* round number to nearest 64 byte granularity */
bytes = ice_round_to_num(bytes, 64);
/* The value is in 64 byte chunks */
burst_size_to_prog |= (u16)(bytes / 64);
} else {
/* k bytes granularity case */
/* Enable MSB granularity bit */
burst_size_to_prog = ICE_KBYTE_GRANULARITY;
/* round number to nearest 1024 granularity */
bytes = ice_round_to_num(bytes, 1024);
/* check rounding doesn't go beyond allowed */
if (bytes > ICE_MAX_BURST_SIZE_KBYTE_GRANULARITY)
bytes = ICE_MAX_BURST_SIZE_KBYTE_GRANULARITY;
/* The value is in k bytes */
burst_size_to_prog |= (u16)(bytes / 1024);
}
hw->max_burst_size = burst_size_to_prog;
return 0;
}
/**
* ice_sched_replay_node_prio - re-configure node priority
* @hw: pointer to the HW struct
* @node: sched node to configure
* @priority: priority value
*
* This function configures node element's priority value. It
* needs to be called with scheduler lock held.
*/
static enum ice_status
ice_sched_replay_node_prio(struct ice_hw *hw, struct ice_sched_node *node,
u8 priority)
{
struct ice_aqc_txsched_elem_data buf;
struct ice_aqc_txsched_elem *data;
enum ice_status status;
buf = node->info;
data = &buf.data;
data->valid_sections |= ICE_AQC_ELEM_VALID_GENERIC;
data->generic = priority;
/* Configure element */
status = ice_sched_update_elem(hw, node, &buf);
return status;
}
/**
* ice_sched_replay_node_bw - replay node(s) BW
* @hw: pointer to the HW struct
* @node: sched node to configure
* @bw_t_info: BW type information
*
* This function restores node's BW from bw_t_info. The caller needs
* to hold the scheduler lock.
*/
static enum ice_status
ice_sched_replay_node_bw(struct ice_hw *hw, struct ice_sched_node *node,
struct ice_bw_type_info *bw_t_info)
{
struct ice_port_info *pi = hw->port_info;
enum ice_status status = ICE_ERR_PARAM;
u16 bw_alloc;
if (!node)
return status;
if (bitmap_empty(bw_t_info->bw_t_bitmap, ICE_BW_TYPE_CNT))
return 0;
if (test_bit(ICE_BW_TYPE_PRIO, bw_t_info->bw_t_bitmap)) {
status = ice_sched_replay_node_prio(hw, node,
bw_t_info->generic);
if (status)
return status;
}
if (test_bit(ICE_BW_TYPE_CIR, bw_t_info->bw_t_bitmap)) {
status = ice_sched_set_node_bw_lmt(pi, node, ICE_MIN_BW,
bw_t_info->cir_bw.bw);
if (status)
return status;
}
if (test_bit(ICE_BW_TYPE_CIR_WT, bw_t_info->bw_t_bitmap)) {
bw_alloc = bw_t_info->cir_bw.bw_alloc;
status = ice_sched_cfg_node_bw_alloc(hw, node, ICE_MIN_BW,
bw_alloc);
if (status)
return status;
}
if (test_bit(ICE_BW_TYPE_EIR, bw_t_info->bw_t_bitmap)) {
status = ice_sched_set_node_bw_lmt(pi, node, ICE_MAX_BW,
bw_t_info->eir_bw.bw);
if (status)
return status;
}
if (test_bit(ICE_BW_TYPE_EIR_WT, bw_t_info->bw_t_bitmap)) {
bw_alloc = bw_t_info->eir_bw.bw_alloc;
status = ice_sched_cfg_node_bw_alloc(hw, node, ICE_MAX_BW,
bw_alloc);
if (status)
return status;
}
if (test_bit(ICE_BW_TYPE_SHARED, bw_t_info->bw_t_bitmap))
status = ice_sched_set_node_bw_lmt(pi, node, ICE_SHARED_BW,
bw_t_info->shared_bw);
return status;
}
/**
* ice_sched_replay_q_bw - replay queue type node BW
* @pi: port information structure
* @q_ctx: queue context structure
*
* This function replays queue type node bandwidth. This function needs to be
* called with scheduler lock held.
*/
enum ice_status
ice_sched_replay_q_bw(struct ice_port_info *pi, struct ice_q_ctx *q_ctx)
{
struct ice_sched_node *q_node;
/* Following also checks the presence of node in tree */
q_node = ice_sched_find_node_by_teid(pi->root, q_ctx->q_teid);
if (!q_node)
return ICE_ERR_PARAM;
return ice_sched_replay_node_bw(pi->hw, q_node, &q_ctx->bw_t_info);
}
......@@ -8,6 +8,36 @@
#define ICE_QGRP_LAYER_OFFSET 2
#define ICE_VSI_LAYER_OFFSET 4
#define ICE_SCHED_INVAL_LAYER_NUM 0xFF
/* Burst size is a 12 bits register that is configured while creating the RL
* profile(s). MSB is a granularity bit and tells the granularity type
* 0 - LSB bits are in 64 bytes granularity
* 1 - LSB bits are in 1K bytes granularity
*/
#define ICE_64_BYTE_GRANULARITY 0
#define ICE_KBYTE_GRANULARITY BIT(11)
#define ICE_MIN_BURST_SIZE_ALLOWED 64 /* In Bytes */
#define ICE_MAX_BURST_SIZE_ALLOWED \
((BIT(11) - 1) * 1024) /* In Bytes */
#define ICE_MAX_BURST_SIZE_64_BYTE_GRANULARITY \
((BIT(11) - 1) * 64) /* In Bytes */
#define ICE_MAX_BURST_SIZE_KBYTE_GRANULARITY ICE_MAX_BURST_SIZE_ALLOWED
#define ICE_RL_PROF_FREQUENCY 446000000
#define ICE_RL_PROF_ACCURACY_BYTES 128
#define ICE_RL_PROF_MULTIPLIER 10000
#define ICE_RL_PROF_TS_MULTIPLIER 32
#define ICE_RL_PROF_FRACTION 512
/* BW rate limit profile parameters list entry along
* with bandwidth maintained per layer in port info
*/
struct ice_aqc_rl_profile_info {
struct ice_aqc_rl_profile_elem profile;
struct list_head list_entry;
u32 bw; /* requested */
u16 prof_id_ref; /* profile ID to node association ref count */
};
struct ice_sched_agg_vsi_info {
struct list_head list_entry;
......@@ -48,4 +78,13 @@ enum ice_status
ice_sched_cfg_vsi(struct ice_port_info *pi, u16 vsi_handle, u8 tc, u16 maxqs,
u8 owner, bool enable);
enum ice_status ice_rm_vsi_lan_cfg(struct ice_port_info *pi, u16 vsi_handle);
enum ice_status
ice_cfg_q_bw_lmt(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
u16 q_handle, enum ice_rl_type rl_type, u32 bw);
enum ice_status
ice_cfg_q_bw_dflt_lmt(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
u16 q_handle, enum ice_rl_type rl_type);
enum ice_status ice_cfg_rl_burst_size(struct ice_hw *hw, u32 bytes);
enum ice_status
ice_sched_replay_q_bw(struct ice_port_info *pi, struct ice_q_ctx *q_ctx);
#endif /* _ICE_SCHED_H_ */
......@@ -416,7 +416,6 @@ ice_add_vsi(struct ice_hw *hw, u16 vsi_handle, struct ice_vsi_ctx *vsi_ctx,
ice_save_vsi_ctx(hw, vsi_handle, tmp_vsi_ctx);
} else {
/* update with new HW VSI num */
if (tmp_vsi_ctx->vsi_num != vsi_ctx->vsi_num)
tmp_vsi_ctx->vsi_num = vsi_ctx->vsi_num;
}
......
......@@ -14,11 +14,6 @@
#define ICE_VSI_INVAL_ID 0xffff
#define ICE_INVAL_Q_HANDLE 0xFFFF
/* VSI queue context structure */
struct ice_q_ctx {
u16 q_handle;
};
/* VSI context structure for add/get/update/free operations */
struct ice_vsi_ctx {
u16 vsi_num;
......
......@@ -19,6 +19,17 @@ static inline bool ice_is_tc_ena(unsigned long bitmap, u8 tc)
return test_bit(tc, &bitmap);
}
static inline u64 round_up_64bit(u64 a, u32 b)
{
return div64_long(((a) + (b) / 2), (b));
}
static inline u32 ice_round_to_num(u32 N, u32 R)
{
return ((((N) % (R)) < ((R) / 2)) ? (((N) / (R)) * (R)) :
((((N) + (R) - 1) / (R)) * (R)));
}
/* Driver always calls main vsi_handle first */
#define ICE_MAIN_VSI_HANDLE 0
......@@ -35,6 +46,8 @@ static inline bool ice_is_tc_ena(unsigned long bitmap, u8 tc)
#define ICE_DBG_PKG BIT_ULL(16)
#define ICE_DBG_RES BIT_ULL(17)
#define ICE_DBG_AQ_MSG BIT_ULL(24)
#define ICE_DBG_AQ_DESC BIT_ULL(25)
#define ICE_DBG_AQ_DESC_BUF BIT_ULL(26)
#define ICE_DBG_AQ_CMD BIT_ULL(27)
#define ICE_DBG_USER BIT_ULL(31)
......@@ -272,10 +285,56 @@ enum ice_agg_type {
ICE_AGG_TYPE_QG
};
/* Rate limit types */
enum ice_rl_type {
ICE_UNKNOWN_BW = 0,
ICE_MIN_BW, /* for CIR profile */
ICE_MAX_BW, /* for EIR profile */
ICE_SHARED_BW /* for shared profile */
};
#define ICE_SCHED_MIN_BW 500 /* in Kbps */
#define ICE_SCHED_MAX_BW 100000000 /* in Kbps */
#define ICE_SCHED_DFLT_BW 0xFFFFFFFF /* unlimited */
#define ICE_SCHED_DFLT_RL_PROF_ID 0
#define ICE_SCHED_NO_SHARED_RL_PROF_ID 0xFFFF
#define ICE_SCHED_DFLT_BW_WT 1
#define ICE_SCHED_INVAL_PROF_ID 0xFFFF
#define ICE_SCHED_DFLT_BURST_SIZE (15 * 1024) /* in bytes (15k) */
/* VSI type list entry to locate corresponding VSI/ag nodes */
/* Data structure for saving BW information */
enum ice_bw_type {
ICE_BW_TYPE_PRIO,
ICE_BW_TYPE_CIR,
ICE_BW_TYPE_CIR_WT,
ICE_BW_TYPE_EIR,
ICE_BW_TYPE_EIR_WT,
ICE_BW_TYPE_SHARED,
ICE_BW_TYPE_CNT /* This must be last */
};
struct ice_bw {
u32 bw;
u16 bw_alloc;
};
struct ice_bw_type_info {
DECLARE_BITMAP(bw_t_bitmap, ICE_BW_TYPE_CNT);
u8 generic;
struct ice_bw cir_bw;
struct ice_bw eir_bw;
u32 shared_bw;
};
/* VSI queue context structure for given TC */
struct ice_q_ctx {
u16 q_handle;
u32 q_teid;
/* bw_t_info saves queue BW information */
struct ice_bw_type_info bw_t_info;
};
/* VSI type list entry to locate corresponding VSI/aggregator nodes */
struct ice_sched_vsi_info {
struct ice_sched_node *vsi_node[ICE_MAX_TRAFFIC_CLASS];
struct ice_sched_node *ag_node[ICE_MAX_TRAFFIC_CLASS];
......@@ -364,6 +423,8 @@ struct ice_port_info {
struct mutex sched_lock; /* protect access to TXSched tree */
struct ice_sched_node *
sib_head[ICE_MAX_TRAFFIC_CLASS][ICE_AQC_TOPO_MAX_LEVEL_NUM];
/* List contain profile ID(s) and other params per layer */
struct list_head rl_prof_list[ICE_AQC_TOPO_MAX_LEVEL_NUM];
struct ice_dcbx_cfg local_dcbx_cfg; /* Oper/Local Cfg */
/* DCBX info */
struct ice_dcbx_cfg remote_dcbx_cfg; /* Peer Cfg */
......@@ -415,6 +476,8 @@ struct ice_hw {
u8 pf_id; /* device profile info */
u16 max_burst_size; /* driver sets this value */
/* Tx Scheduler values */
u16 num_tx_sched_layers;
u16 num_tx_sched_phys_layers;
......
......@@ -1151,6 +1151,25 @@ bool ice_reset_all_vfs(struct ice_pf *pf, bool is_vflr)
return true;
}
/**
* ice_is_vf_disabled
* @vf: pointer to the VF info
*
* Returns true if the PF or VF is disabled, false otherwise.
*/
static bool ice_is_vf_disabled(struct ice_vf *vf)
{
struct ice_pf *pf = vf->pf;
/* If the PF has been disabled, there is no need resetting VF until
* PF is active again. Similarly, if the VF has been disabled, this
* means something else is resetting the VF, so we shouldn't continue.
* Otherwise, set disable VF state bit for actual reset, and continue.
*/
return (test_bit(__ICE_VF_DIS, pf->state) ||
test_bit(ICE_VF_STATE_DIS, vf->vf_states));
}
/**
* ice_reset_vf - Reset a particular VF
* @vf: pointer to the VF structure
......@@ -1168,19 +1187,15 @@ static bool ice_reset_vf(struct ice_vf *vf, bool is_vflr)
u32 reg;
int i;
/* If the PF has been disabled, there is no need resetting VF until
* PF is active again.
*/
if (test_bit(__ICE_VF_DIS, pf->state))
return false;
/* If the VF has been disabled, this means something else is
* resetting the VF, so we shouldn't continue. Otherwise, set
* disable VF state bit for actual reset, and continue.
*/
if (test_and_set_bit(ICE_VF_STATE_DIS, vf->vf_states))
return false;
if (ice_is_vf_disabled(vf)) {
dev_dbg(&pf->pdev->dev,
"VF is already disabled, there is no need for resetting it, telling VM, all is fine %d\n",
vf->vf_id);
return true;
}
/* Set VF disable bit state here, before triggering reset */
set_bit(ICE_VF_STATE_DIS, vf->vf_states);
ice_trigger_vf_reset(vf, is_vflr, false);
vsi = pf->vsi[vf->lan_vsi_idx];
......@@ -2158,9 +2173,11 @@ static int ice_vc_cfg_irq_map_msg(struct ice_vf *vf, u8 *msg)
vector_id = map->vector_id;
vsi_id = map->vsi_id;
/* validate msg params */
if (!(vector_id < pf->hw.func_caps.common_cap
.num_msix_vectors) || !ice_vc_isvalid_vsi_id(vf, vsi_id) ||
/* vector_id is always 0-based for each VF, and can never be
* larger than or equal to the max allowed interrupts per VF
*/
if (!(vector_id < ICE_MAX_INTR_PER_VF) ||
!ice_vc_isvalid_vsi_id(vf, vsi_id) ||
(!vector_id && (map->rxq_map || map->txq_map))) {
v_ret = VIRTCHNL_STATUS_ERR_PARAM;
goto error_param;
......@@ -3122,6 +3139,23 @@ int ice_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool ena)
return ret;
}
/**
* ice_wait_on_vf_reset
* @vf: The VF being resseting
*
* Poll to make sure a given VF is ready after reset
*/
static void ice_wait_on_vf_reset(struct ice_vf *vf)
{
int i;
for (i = 0; i < ICE_MAX_VF_RESET_WAIT; i++) {
if (test_bit(ICE_VF_STATE_INIT, vf->vf_states))
break;
msleep(20);
}
}
/**
* ice_set_vf_mac
* @netdev: network interface device structure
......@@ -3145,6 +3179,15 @@ int ice_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
}
vf = &pf->vf[vf_id];
/* Don't set MAC on disabled VF */
if (ice_is_vf_disabled(vf))
return -EINVAL;
/* In case VF is in reset mode, wait until it is completed. Depending
* on factors like queue disabling routine, this could take ~250ms
*/
ice_wait_on_vf_reset(vf);
if (!test_bit(ICE_VF_STATE_INIT, vf->vf_states)) {
netdev_err(netdev, "VF %d in reset. Try again.\n", vf_id);
return -EBUSY;
......@@ -3192,6 +3235,15 @@ int ice_set_vf_trust(struct net_device *netdev, int vf_id, bool trusted)
}
vf = &pf->vf[vf_id];
/* Don't set Trusted Mode on disabled VF */
if (ice_is_vf_disabled(vf))
return -EINVAL;
/* In case VF is in reset mode, wait until it is completed. Depending
* on factors like queue disabling routine, this could take ~250ms
*/
ice_wait_on_vf_reset(vf);
if (!test_bit(ICE_VF_STATE_INIT, vf->vf_states)) {
dev_err(&pf->pdev->dev, "VF %d in reset. Try again.\n", vf_id);
return -EBUSY;
......
......@@ -38,6 +38,7 @@
#define ICE_MAX_POLICY_INTR_PER_VF 33
#define ICE_MIN_INTR_PER_VF (ICE_MIN_QS_PER_VF + 1)
#define ICE_DFLT_INTR_PER_VF (ICE_DFLT_QS_PER_VF + 1)
#define ICE_MAX_VF_RESET_WAIT 15
/* Specific VF states */
enum ice_vf_states {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment