--- /dev/null
+From 6914b31e44cec7c5f858af1b9b94177f0b02f8ba Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Feb 2021 13:19:25 -0800
+Subject: ice: Recognize 860 as iSCSI port in CEE mode
+
+From: Chinh T Cao <chinh.t.cao@intel.com>
+
+[ Upstream commit aeac8ce864d9c0836e12ed5b5cc80f62f3cccb7c ]
+
+iSCSI can use both TCP ports 860 and 3260. However, in our current
+implementation, the ice_aqc_opc_get_cee_dcb_cfg (0x0A07) AQ command
+doesn't provide a way to communicate the protocol port number to the
+AQ's caller. Thus, we assume that 3260 is the iSCSI port number at the
+AQ's caller layer.
+
+Rely on the dcbx-willing mode, desired QoS and remote QoS configuration to
+determine which port number that iSCSI will use.
+
+Fixes: 0ebd3ff13cca ("ice: Add code for DCB initialization part 2/4")
+Signed-off-by: Chinh T Cao <chinh.t.cao@intel.com>
+Tested-by: Tony Brelinski <tonyx.brelinski@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/intel/ice/ice_dcb.c | 38 +++++++++++++++++------
+ drivers/net/ethernet/intel/ice/ice_type.h | 1 +
+ 2 files changed, 30 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/net/ethernet/intel/ice/ice_dcb.c b/drivers/net/ethernet/intel/ice/ice_dcb.c
+index e42727941ef5..211ac6f907ad 100644
+--- a/drivers/net/ethernet/intel/ice/ice_dcb.c
++++ b/drivers/net/ethernet/intel/ice/ice_dcb.c
+@@ -738,22 +738,27 @@ ice_aq_get_cee_dcb_cfg(struct ice_hw *hw,
+ /**
+ * ice_cee_to_dcb_cfg
+ * @cee_cfg: pointer to CEE configuration struct
+- * @dcbcfg: DCB configuration struct
++ * @pi: port information structure
+ *
+ * Convert CEE configuration from firmware to DCB configuration
+ */
+ static void
+ ice_cee_to_dcb_cfg(struct ice_aqc_get_cee_dcb_cfg_resp *cee_cfg,
+- struct ice_dcbx_cfg *dcbcfg)
++ struct ice_port_info *pi)
+ {
+ u32 status, tlv_status = le32_to_cpu(cee_cfg->tlv_status);
+ u32 ice_aqc_cee_status_mask, ice_aqc_cee_status_shift;
++ u8 i, j, err, sync, oper, app_index, ice_app_sel_type;
+ u16 app_prio = le16_to_cpu(cee_cfg->oper_app_prio);
+- u8 i, err, sync, oper, app_index, ice_app_sel_type;
+ u16 ice_aqc_cee_app_mask, ice_aqc_cee_app_shift;
++ struct ice_dcbx_cfg *cmp_dcbcfg, *dcbcfg;
+ u16 ice_app_prot_id_type;
+
+- /* CEE PG data to ETS config */
++ dcbcfg = &pi->qos_cfg.local_dcbx_cfg;
++ dcbcfg->dcbx_mode = ICE_DCBX_MODE_CEE;
++ dcbcfg->tlv_status = tlv_status;
++
++ /* CEE PG data */
+ dcbcfg->etscfg.maxtcs = cee_cfg->oper_num_tc;
+
+ /* Note that the FW creates the oper_prio_tc nibbles reversed
+@@ -780,10 +785,16 @@ ice_cee_to_dcb_cfg(struct ice_aqc_get_cee_dcb_cfg_resp *cee_cfg,
+ }
+ }
+
+- /* CEE PFC data to ETS config */
++ /* CEE PFC data */
+ dcbcfg->pfc.pfcena = cee_cfg->oper_pfc_en;
+ dcbcfg->pfc.pfccap = ICE_MAX_TRAFFIC_CLASS;
+
++ /* CEE APP TLV data */
++ if (dcbcfg->app_mode == ICE_DCBX_APPS_NON_WILLING)
++ cmp_dcbcfg = &pi->qos_cfg.desired_dcbx_cfg;
++ else
++ cmp_dcbcfg = &pi->qos_cfg.remote_dcbx_cfg;
++
+ app_index = 0;
+ for (i = 0; i < 3; i++) {
+ if (i == 0) {
+@@ -802,6 +813,18 @@ ice_cee_to_dcb_cfg(struct ice_aqc_get_cee_dcb_cfg_resp *cee_cfg,
+ ice_aqc_cee_app_shift = ICE_AQC_CEE_APP_ISCSI_S;
+ ice_app_sel_type = ICE_APP_SEL_TCPIP;
+ ice_app_prot_id_type = ICE_APP_PROT_ID_ISCSI;
++
++ for (j = 0; j < cmp_dcbcfg->numapps; j++) {
++ u16 prot_id = cmp_dcbcfg->app[j].prot_id;
++ u8 sel = cmp_dcbcfg->app[j].selector;
++
++ if (sel == ICE_APP_SEL_TCPIP &&
++ (prot_id == ICE_APP_PROT_ID_ISCSI ||
++ prot_id == ICE_APP_PROT_ID_ISCSI_860)) {
++ ice_app_prot_id_type = prot_id;
++ break;
++ }
++ }
+ } else {
+ /* FIP APP */
+ ice_aqc_cee_status_mask = ICE_AQC_CEE_FIP_STATUS_M;
+@@ -892,11 +915,8 @@ enum ice_status ice_get_dcb_cfg(struct ice_port_info *pi)
+ ret = ice_aq_get_cee_dcb_cfg(pi->hw, &cee_cfg, NULL);
+ if (!ret) {
+ /* CEE mode */
+- dcbx_cfg = &pi->qos_cfg.local_dcbx_cfg;
+- dcbx_cfg->dcbx_mode = ICE_DCBX_MODE_CEE;
+- dcbx_cfg->tlv_status = le32_to_cpu(cee_cfg.tlv_status);
+- ice_cee_to_dcb_cfg(&cee_cfg, dcbx_cfg);
+ ret = ice_get_ieee_or_cee_dcb_cfg(pi, ICE_DCBX_MODE_CEE);
++ ice_cee_to_dcb_cfg(&cee_cfg, pi);
+ } else if (pi->hw->adminq.sq_last_status == ICE_AQ_RC_ENOENT) {
+ /* CEE mode not enabled try querying IEEE data */
+ dcbx_cfg = &pi->qos_cfg.local_dcbx_cfg;
+diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
+index c09c085f637a..1bed183d96a0 100644
+--- a/drivers/net/ethernet/intel/ice/ice_type.h
++++ b/drivers/net/ethernet/intel/ice/ice_type.h
+@@ -493,6 +493,7 @@ struct ice_dcb_app_priority_table {
+ #define ICE_TLV_STATUS_ERR 0x4
+ #define ICE_APP_PROT_ID_FCOE 0x8906
+ #define ICE_APP_PROT_ID_ISCSI 0x0cbc
++#define ICE_APP_PROT_ID_ISCSI_860 0x035c
+ #define ICE_APP_PROT_ID_FIP 0x8914
+ #define ICE_APP_SEL_ETHTYPE 0x1
+ #define ICE_APP_SEL_TCPIP 0x2
+--
+2.30.2
+
--- /dev/null
+From 6222e8b56c1d55e96cd319e5f8f68adede18ba56 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 20 Nov 2020 16:39:35 -0800
+Subject: ice: Refactor DCB related variables out of the ice_port_info struct
+
+From: Chinh T Cao <chinh.t.cao@intel.com>
+
+[ Upstream commit fc2d1165d4a424dd325ae1f45806565350a58013 ]
+
+Refactor the DCB related variables out of the ice_port_info_struct. The
+goal is to make the ice_port_info struct cleaner.
+
+Signed-off-by: Chinh T Cao <chinh.t.cao@intel.com>
+Co-developed-by: Dave Ertman <david.m.ertman@intel.com>
+Signed-off-by: Dave Ertman <david.m.ertman@intel.com>
+Tested-by: Tony Brelinski <tonyx.brelinski@intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/intel/ice/ice_dcb.c | 40 ++++++++--------
+ drivers/net/ethernet/intel/ice/ice_dcb_lib.c | 47 +++++++++---------
+ drivers/net/ethernet/intel/ice/ice_dcb_nl.c | 50 ++++++++++----------
+ drivers/net/ethernet/intel/ice/ice_ethtool.c | 4 +-
+ drivers/net/ethernet/intel/ice/ice_lib.c | 2 +-
+ drivers/net/ethernet/intel/ice/ice_txrx.c | 2 +-
+ drivers/net/ethernet/intel/ice/ice_type.h | 16 ++++---
+ 7 files changed, 83 insertions(+), 78 deletions(-)
+
+diff --git a/drivers/net/ethernet/intel/ice/ice_dcb.c b/drivers/net/ethernet/intel/ice/ice_dcb.c
+index 2a3147ee0bbb..e42727941ef5 100644
+--- a/drivers/net/ethernet/intel/ice/ice_dcb.c
++++ b/drivers/net/ethernet/intel/ice/ice_dcb.c
+@@ -850,9 +850,9 @@ ice_get_ieee_or_cee_dcb_cfg(struct ice_port_info *pi, u8 dcbx_mode)
+ return ICE_ERR_PARAM;
+
+ if (dcbx_mode == ICE_DCBX_MODE_IEEE)
+- dcbx_cfg = &pi->local_dcbx_cfg;
++ dcbx_cfg = &pi->qos_cfg.local_dcbx_cfg;
+ else if (dcbx_mode == ICE_DCBX_MODE_CEE)
+- dcbx_cfg = &pi->desired_dcbx_cfg;
++ dcbx_cfg = &pi->qos_cfg.desired_dcbx_cfg;
+
+ /* Get Local DCB Config in case of ICE_DCBX_MODE_IEEE
+ * or get CEE DCB Desired Config in case of ICE_DCBX_MODE_CEE
+@@ -863,7 +863,7 @@ ice_get_ieee_or_cee_dcb_cfg(struct ice_port_info *pi, u8 dcbx_mode)
+ goto out;
+
+ /* Get Remote DCB Config */
+- dcbx_cfg = &pi->remote_dcbx_cfg;
++ dcbx_cfg = &pi->qos_cfg.remote_dcbx_cfg;
+ ret = ice_aq_get_dcb_cfg(pi->hw, ICE_AQ_LLDP_MIB_REMOTE,
+ ICE_AQ_LLDP_BRID_TYPE_NEAREST_BRID, dcbx_cfg);
+ /* Don't treat ENOENT as an error for Remote MIBs */
+@@ -892,14 +892,14 @@ enum ice_status ice_get_dcb_cfg(struct ice_port_info *pi)
+ ret = ice_aq_get_cee_dcb_cfg(pi->hw, &cee_cfg, NULL);
+ if (!ret) {
+ /* CEE mode */
+- dcbx_cfg = &pi->local_dcbx_cfg;
++ dcbx_cfg = &pi->qos_cfg.local_dcbx_cfg;
+ dcbx_cfg->dcbx_mode = ICE_DCBX_MODE_CEE;
+ dcbx_cfg->tlv_status = le32_to_cpu(cee_cfg.tlv_status);
+ ice_cee_to_dcb_cfg(&cee_cfg, dcbx_cfg);
+ ret = ice_get_ieee_or_cee_dcb_cfg(pi, ICE_DCBX_MODE_CEE);
+ } else if (pi->hw->adminq.sq_last_status == ICE_AQ_RC_ENOENT) {
+ /* CEE mode not enabled try querying IEEE data */
+- dcbx_cfg = &pi->local_dcbx_cfg;
++ dcbx_cfg = &pi->qos_cfg.local_dcbx_cfg;
+ dcbx_cfg->dcbx_mode = ICE_DCBX_MODE_IEEE;
+ ret = ice_get_ieee_or_cee_dcb_cfg(pi, ICE_DCBX_MODE_IEEE);
+ }
+@@ -916,26 +916,26 @@ enum ice_status ice_get_dcb_cfg(struct ice_port_info *pi)
+ */
+ enum ice_status ice_init_dcb(struct ice_hw *hw, bool enable_mib_change)
+ {
+- struct ice_port_info *pi = hw->port_info;
++ struct ice_qos_cfg *qos_cfg = &hw->port_info->qos_cfg;
+ enum ice_status ret = 0;
+
+ if (!hw->func_caps.common_cap.dcb)
+ return ICE_ERR_NOT_SUPPORTED;
+
+- pi->is_sw_lldp = true;
++ qos_cfg->is_sw_lldp = true;
+
+ /* Get DCBX status */
+- pi->dcbx_status = ice_get_dcbx_status(hw);
++ qos_cfg->dcbx_status = ice_get_dcbx_status(hw);
+
+- if (pi->dcbx_status == ICE_DCBX_STATUS_DONE ||
+- pi->dcbx_status == ICE_DCBX_STATUS_IN_PROGRESS ||
+- pi->dcbx_status == ICE_DCBX_STATUS_NOT_STARTED) {
++ if (qos_cfg->dcbx_status == ICE_DCBX_STATUS_DONE ||
++ qos_cfg->dcbx_status == ICE_DCBX_STATUS_IN_PROGRESS ||
++ qos_cfg->dcbx_status == ICE_DCBX_STATUS_NOT_STARTED) {
+ /* Get current DCBX configuration */
+- ret = ice_get_dcb_cfg(pi);
++ ret = ice_get_dcb_cfg(hw->port_info);
+ if (ret)
+ return ret;
+- pi->is_sw_lldp = false;
+- } else if (pi->dcbx_status == ICE_DCBX_STATUS_DIS) {
++ qos_cfg->is_sw_lldp = false;
++ } else if (qos_cfg->dcbx_status == ICE_DCBX_STATUS_DIS) {
+ return ICE_ERR_NOT_READY;
+ }
+
+@@ -943,7 +943,7 @@ enum ice_status ice_init_dcb(struct ice_hw *hw, bool enable_mib_change)
+ if (enable_mib_change) {
+ ret = ice_aq_cfg_lldp_mib_change(hw, true, NULL);
+ if (ret)
+- pi->is_sw_lldp = true;
++ qos_cfg->is_sw_lldp = true;
+ }
+
+ return ret;
+@@ -958,21 +958,21 @@ enum ice_status ice_init_dcb(struct ice_hw *hw, bool enable_mib_change)
+ */
+ enum ice_status ice_cfg_lldp_mib_change(struct ice_hw *hw, bool ena_mib)
+ {
+- struct ice_port_info *pi = hw->port_info;
++ struct ice_qos_cfg *qos_cfg = &hw->port_info->qos_cfg;
+ enum ice_status ret;
+
+ if (!hw->func_caps.common_cap.dcb)
+ return ICE_ERR_NOT_SUPPORTED;
+
+ /* Get DCBX status */
+- pi->dcbx_status = ice_get_dcbx_status(hw);
++ qos_cfg->dcbx_status = ice_get_dcbx_status(hw);
+
+- if (pi->dcbx_status == ICE_DCBX_STATUS_DIS)
++ if (qos_cfg->dcbx_status == ICE_DCBX_STATUS_DIS)
+ return ICE_ERR_NOT_READY;
+
+ ret = ice_aq_cfg_lldp_mib_change(hw, ena_mib, NULL);
+ if (!ret)
+- pi->is_sw_lldp = !ena_mib;
++ qos_cfg->is_sw_lldp = !ena_mib;
+
+ return ret;
+ }
+@@ -1270,7 +1270,7 @@ enum ice_status ice_set_dcb_cfg(struct ice_port_info *pi)
+ hw = pi->hw;
+
+ /* update the HW local config */
+- dcbcfg = &pi->local_dcbx_cfg;
++ dcbcfg = &pi->qos_cfg.local_dcbx_cfg;
+ /* Allocate the LLDPDU */
+ lldpmib = devm_kzalloc(ice_hw_to_dev(hw), ICE_LLDPDU_SIZE, GFP_KERNEL);
+ if (!lldpmib)
+diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
+index 36abd6b7280c..1e8f71ffc8ce 100644
+--- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
++++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
+@@ -28,7 +28,7 @@ void ice_vsi_cfg_netdev_tc(struct ice_vsi *vsi, u8 ena_tc)
+ if (netdev_set_num_tc(netdev, vsi->tc_cfg.numtc))
+ return;
+
+- dcbcfg = &pf->hw.port_info->local_dcbx_cfg;
++ dcbcfg = &pf->hw.port_info->qos_cfg.local_dcbx_cfg;
+
+ ice_for_each_traffic_class(i)
+ if (vsi->tc_cfg.ena_tc & BIT(i))
+@@ -134,7 +134,7 @@ static u8 ice_dcb_get_mode(struct ice_port_info *port_info, bool host)
+ else
+ mode = DCB_CAP_DCBX_LLD_MANAGED;
+
+- if (port_info->local_dcbx_cfg.dcbx_mode & ICE_DCBX_MODE_CEE)
++ if (port_info->qos_cfg.local_dcbx_cfg.dcbx_mode & ICE_DCBX_MODE_CEE)
+ return mode | DCB_CAP_DCBX_VER_CEE;
+ else
+ return mode | DCB_CAP_DCBX_VER_IEEE;
+@@ -277,10 +277,10 @@ int ice_pf_dcb_cfg(struct ice_pf *pf, struct ice_dcbx_cfg *new_cfg, bool locked)
+ int ret = ICE_DCB_NO_HW_CHG;
+ struct ice_vsi *pf_vsi;
+
+- curr_cfg = &pf->hw.port_info->local_dcbx_cfg;
++ curr_cfg = &pf->hw.port_info->qos_cfg.local_dcbx_cfg;
+
+ /* FW does not care if change happened */
+- if (!pf->hw.port_info->is_sw_lldp)
++ if (!pf->hw.port_info->qos_cfg.is_sw_lldp)
+ ret = ICE_DCB_HW_CHG_RST;
+
+ /* Enable DCB tagging only when more than one TC */
+@@ -327,7 +327,7 @@ int ice_pf_dcb_cfg(struct ice_pf *pf, struct ice_dcbx_cfg *new_cfg, bool locked)
+ /* 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.
+ */
+- if (pf->hw.port_info->is_sw_lldp) {
++ if (pf->hw.port_info->qos_cfg.is_sw_lldp) {
+ ret = ice_set_dcb_cfg(pf->hw.port_info);
+ if (ret) {
+ dev_err(dev, "Set DCB Config failed\n");
+@@ -360,7 +360,7 @@ int ice_pf_dcb_cfg(struct ice_pf *pf, struct ice_dcbx_cfg *new_cfg, bool locked)
+ */
+ static void ice_cfg_etsrec_defaults(struct ice_port_info *pi)
+ {
+- struct ice_dcbx_cfg *dcbcfg = &pi->local_dcbx_cfg;
++ struct ice_dcbx_cfg *dcbcfg = &pi->qos_cfg.local_dcbx_cfg;
+ u8 i;
+
+ /* Ensure ETS recommended DCB configuration is not already set */
+@@ -446,7 +446,7 @@ void ice_dcb_rebuild(struct ice_pf *pf)
+
+ mutex_lock(&pf->tc_mutex);
+
+- if (!pf->hw.port_info->is_sw_lldp)
++ if (!pf->hw.port_info->qos_cfg.is_sw_lldp)
+ ice_cfg_etsrec_defaults(pf->hw.port_info);
+
+ ret = ice_set_dcb_cfg(pf->hw.port_info);
+@@ -455,9 +455,9 @@ void ice_dcb_rebuild(struct ice_pf *pf)
+ goto dcb_error;
+ }
+
+- if (!pf->hw.port_info->is_sw_lldp) {
++ if (!pf->hw.port_info->qos_cfg.is_sw_lldp) {
+ ret = ice_cfg_lldp_mib_change(&pf->hw, true);
+- if (ret && !pf->hw.port_info->is_sw_lldp) {
++ if (ret && !pf->hw.port_info->qos_cfg.is_sw_lldp) {
+ dev_err(dev, "Failed to register for MIB changes\n");
+ goto dcb_error;
+ }
+@@ -510,11 +510,12 @@ static int ice_dcb_init_cfg(struct ice_pf *pf, bool locked)
+ int ret = 0;
+
+ pi = pf->hw.port_info;
+- newcfg = kmemdup(&pi->local_dcbx_cfg, sizeof(*newcfg), GFP_KERNEL);
++ newcfg = kmemdup(&pi->qos_cfg.local_dcbx_cfg, sizeof(*newcfg),
++ GFP_KERNEL);
+ if (!newcfg)
+ return -ENOMEM;
+
+- memset(&pi->local_dcbx_cfg, 0, sizeof(*newcfg));
++ memset(&pi->qos_cfg.local_dcbx_cfg, 0, sizeof(*newcfg));
+
+ dev_info(ice_pf_to_dev(pf), "Configuring initial DCB values\n");
+ if (ice_pf_dcb_cfg(pf, newcfg, locked))
+@@ -545,7 +546,7 @@ static int ice_dcb_sw_dflt_cfg(struct ice_pf *pf, bool ets_willing, bool locked)
+ if (!dcbcfg)
+ return -ENOMEM;
+
+- memset(&pi->local_dcbx_cfg, 0, sizeof(*dcbcfg));
++ memset(&pi->qos_cfg.local_dcbx_cfg, 0, sizeof(*dcbcfg));
+
+ dcbcfg->etscfg.willing = ets_willing ? 1 : 0;
+ dcbcfg->etscfg.maxtcs = hw->func_caps.common_cap.maxtc;
+@@ -608,7 +609,7 @@ static bool ice_dcb_tc_contig(u8 *prio_table)
+ */
+ static int ice_dcb_noncontig_cfg(struct ice_pf *pf)
+ {
+- struct ice_dcbx_cfg *dcbcfg = &pf->hw.port_info->local_dcbx_cfg;
++ struct ice_dcbx_cfg *dcbcfg = &pf->hw.port_info->qos_cfg.local_dcbx_cfg;
+ struct device *dev = ice_pf_to_dev(pf);
+ int ret;
+
+@@ -638,7 +639,7 @@ static int ice_dcb_noncontig_cfg(struct ice_pf *pf)
+ */
+ void ice_pf_dcb_recfg(struct ice_pf *pf)
+ {
+- struct ice_dcbx_cfg *dcbcfg = &pf->hw.port_info->local_dcbx_cfg;
++ struct ice_dcbx_cfg *dcbcfg = &pf->hw.port_info->qos_cfg.local_dcbx_cfg;
+ u8 tc_map = 0;
+ int v, ret;
+
+@@ -691,7 +692,7 @@ int ice_init_pf_dcb(struct ice_pf *pf, bool locked)
+ port_info = hw->port_info;
+
+ err = ice_init_dcb(hw, false);
+- if (err && !port_info->is_sw_lldp) {
++ if (err && !port_info->qos_cfg.is_sw_lldp) {
+ dev_err(dev, "Error initializing DCB %d\n", err);
+ goto dcb_init_err;
+ }
+@@ -858,7 +859,7 @@ ice_dcb_process_lldp_set_mib_change(struct ice_pf *pf,
+ /* Update the remote cached instance and return */
+ ret = ice_aq_get_dcb_cfg(pi->hw, ICE_AQ_LLDP_MIB_REMOTE,
+ ICE_AQ_LLDP_BRID_TYPE_NEAREST_BRID,
+- &pi->remote_dcbx_cfg);
++ &pi->qos_cfg.remote_dcbx_cfg);
+ if (ret) {
+ dev_err(dev, "Failed to get remote DCB config\n");
+ return;
+@@ -868,10 +869,11 @@ ice_dcb_process_lldp_set_mib_change(struct ice_pf *pf,
+ mutex_lock(&pf->tc_mutex);
+
+ /* store the old configuration */
+- tmp_dcbx_cfg = pf->hw.port_info->local_dcbx_cfg;
++ tmp_dcbx_cfg = pf->hw.port_info->qos_cfg.local_dcbx_cfg;
+
+ /* Reset the old DCBX configuration data */
+- memset(&pi->local_dcbx_cfg, 0, sizeof(pi->local_dcbx_cfg));
++ memset(&pi->qos_cfg.local_dcbx_cfg, 0,
++ sizeof(pi->qos_cfg.local_dcbx_cfg));
+
+ /* Get updated DCBX data from firmware */
+ ret = ice_get_dcb_cfg(pf->hw.port_info);
+@@ -881,7 +883,8 @@ ice_dcb_process_lldp_set_mib_change(struct ice_pf *pf,
+ }
+
+ /* No change detected in DCBX configs */
+- if (!memcmp(&tmp_dcbx_cfg, &pi->local_dcbx_cfg, sizeof(tmp_dcbx_cfg))) {
++ if (!memcmp(&tmp_dcbx_cfg, &pi->qos_cfg.local_dcbx_cfg,
++ sizeof(tmp_dcbx_cfg))) {
+ dev_dbg(dev, "No change detected in DCBX configuration.\n");
+ goto out;
+ }
+@@ -889,13 +892,13 @@ ice_dcb_process_lldp_set_mib_change(struct ice_pf *pf,
+ pf->dcbx_cap = ice_dcb_get_mode(pi, false);
+
+ 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);
++ &pi->qos_cfg.local_dcbx_cfg);
++ ice_dcbnl_flush_apps(pf, &tmp_dcbx_cfg, &pi->qos_cfg.local_dcbx_cfg);
+ if (!need_reconfig)
+ goto out;
+
+ /* Enable DCB tagging only when more than one TC */
+- if (ice_dcb_get_num_tc(&pi->local_dcbx_cfg) > 1) {
++ if (ice_dcb_get_num_tc(&pi->qos_cfg.local_dcbx_cfg) > 1) {
+ dev_dbg(dev, "DCB tagging enabled (num TC > 1)\n");
+ set_bit(ICE_FLAG_DCB_ENA, pf->flags);
+ } else {
+diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_nl.c b/drivers/net/ethernet/intel/ice/ice_dcb_nl.c
+index 162348ef090b..4180f1f35fb8 100644
+--- a/drivers/net/ethernet/intel/ice/ice_dcb_nl.c
++++ b/drivers/net/ethernet/intel/ice/ice_dcb_nl.c
+@@ -32,12 +32,10 @@ static void ice_dcbnl_devreset(struct net_device *netdev)
+ 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;
++ dcbxcfg = &pf->hw.port_info->qos_cfg.local_dcbx_cfg;
+
+ ets->willing = dcbxcfg->etscfg.willing;
+ ets->ets_cap = dcbxcfg->etscfg.maxtcs;
+@@ -72,7 +70,7 @@ static int ice_dcbnl_setets(struct net_device *netdev, struct ieee_ets *ets)
+ !(pf->dcbx_cap & DCB_CAP_DCBX_VER_IEEE))
+ return -EINVAL;
+
+- new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
++ new_cfg = &pf->hw.port_info->qos_cfg.desired_dcbx_cfg;
+
+ mutex_lock(&pf->tc_mutex);
+
+@@ -157,6 +155,7 @@ static u8 ice_dcbnl_getdcbx(struct net_device *netdev)
+ static u8 ice_dcbnl_setdcbx(struct net_device *netdev, u8 mode)
+ {
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
++ struct ice_qos_cfg *qos_cfg;
+
+ /* if FW LLDP agent is running, DCBNL not allowed to change mode */
+ if (test_bit(ICE_FLAG_FW_LLDP_AGENT, pf->flags))
+@@ -173,10 +172,11 @@ static u8 ice_dcbnl_setdcbx(struct net_device *netdev, u8 mode)
+ return ICE_DCB_NO_HW_CHG;
+
+ pf->dcbx_cap = mode;
++ qos_cfg = &pf->hw.port_info->qos_cfg;
+ if (mode & DCB_CAP_DCBX_VER_CEE)
+- pf->hw.port_info->local_dcbx_cfg.dcbx_mode = ICE_DCBX_MODE_CEE;
++ qos_cfg->local_dcbx_cfg.dcbx_mode = ICE_DCBX_MODE_CEE;
+ else
+- pf->hw.port_info->local_dcbx_cfg.dcbx_mode = ICE_DCBX_MODE_IEEE;
++ qos_cfg->local_dcbx_cfg.dcbx_mode = ICE_DCBX_MODE_IEEE;
+
+ dev_info(ice_pf_to_dev(pf), "DCBx mode = 0x%x\n", mode);
+ return ICE_DCB_HW_CHG_RST;
+@@ -227,7 +227,7 @@ static int ice_dcbnl_getpfc(struct net_device *netdev, struct ieee_pfc *pfc)
+ struct ice_dcbx_cfg *dcbxcfg;
+ int i;
+
+- dcbxcfg = &pi->local_dcbx_cfg;
++ dcbxcfg = &pi->qos_cfg.local_dcbx_cfg;
+ pfc->pfc_cap = dcbxcfg->pfc.pfccap;
+ pfc->pfc_en = dcbxcfg->pfc.pfcena;
+ pfc->mbc = dcbxcfg->pfc.mbc;
+@@ -258,7 +258,7 @@ static int ice_dcbnl_setpfc(struct net_device *netdev, struct ieee_pfc *pfc)
+
+ mutex_lock(&pf->tc_mutex);
+
+- new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
++ new_cfg = &pf->hw.port_info->qos_cfg.desired_dcbx_cfg;
+
+ if (pfc->pfc_cap)
+ new_cfg->pfc.pfccap = pfc->pfc_cap;
+@@ -295,9 +295,9 @@ ice_dcbnl_get_pfc_cfg(struct net_device *netdev, int prio, u8 *setting)
+ if (prio >= ICE_MAX_USER_PRIORITY)
+ return;
+
+- *setting = (pi->local_dcbx_cfg.pfc.pfcena >> prio) & 0x1;
++ *setting = (pi->qos_cfg.local_dcbx_cfg.pfc.pfcena >> prio) & 0x1;
+ dev_dbg(ice_pf_to_dev(pf), "Get PFC Config up=%d, setting=%d, pfcenable=0x%x\n",
+- prio, *setting, pi->local_dcbx_cfg.pfc.pfcena);
++ prio, *setting, pi->qos_cfg.local_dcbx_cfg.pfc.pfcena);
+ }
+
+ /**
+@@ -318,7 +318,7 @@ static void ice_dcbnl_set_pfc_cfg(struct net_device *netdev, int prio, u8 set)
+ if (prio >= ICE_MAX_USER_PRIORITY)
+ return;
+
+- new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
++ new_cfg = &pf->hw.port_info->qos_cfg.desired_dcbx_cfg;
+
+ new_cfg->pfc.pfccap = pf->hw.func_caps.common_cap.maxtc;
+ if (set)
+@@ -340,7 +340,7 @@ static u8 ice_dcbnl_getpfcstate(struct net_device *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)
++ if (pi->qos_cfg.local_dcbx_cfg.pfc.pfcena)
+ return 1;
+
+ return 0;
+@@ -380,8 +380,8 @@ static u8 ice_dcbnl_setstate(struct net_device *netdev, u8 state)
+
+ 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,
++ memcpy(&pf->hw.port_info->qos_cfg.desired_dcbx_cfg,
++ &pf->hw.port_info->qos_cfg.local_dcbx_cfg,
+ sizeof(struct ice_dcbx_cfg));
+ } else {
+ clear_bit(ICE_FLAG_DCB_ENA, pf->flags);
+@@ -415,7 +415,7 @@ ice_dcbnl_get_pg_tc_cfg_tx(struct net_device *netdev, int prio,
+ if (prio >= ICE_MAX_USER_PRIORITY)
+ return;
+
+- *pgid = pi->local_dcbx_cfg.etscfg.prio_table[prio];
++ *pgid = pi->qos_cfg.local_dcbx_cfg.etscfg.prio_table[prio];
+ dev_dbg(ice_pf_to_dev(pf), "Get PG config prio=%d tc=%d\n", prio,
+ *pgid);
+ }
+@@ -446,7 +446,7 @@ ice_dcbnl_set_pg_tc_cfg_tx(struct net_device *netdev, int tc,
+ if (tc >= ICE_MAX_TRAFFIC_CLASS)
+ return;
+
+- new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
++ new_cfg = &pf->hw.port_info->qos_cfg.desired_dcbx_cfg;
+
+ /* prio_type, bwg_id and bw_pct per UP are not supported */
+
+@@ -476,7 +476,7 @@ ice_dcbnl_get_pg_bwg_cfg_tx(struct net_device *netdev, int pgid, u8 *bw_pct)
+ if (pgid >= ICE_MAX_TRAFFIC_CLASS)
+ return;
+
+- *bw_pct = pi->local_dcbx_cfg.etscfg.tcbwtable[pgid];
++ *bw_pct = pi->qos_cfg.local_dcbx_cfg.etscfg.tcbwtable[pgid];
+ dev_dbg(ice_pf_to_dev(pf), "Get PG BW config tc=%d bw_pct=%d\n",
+ pgid, *bw_pct);
+ }
+@@ -500,7 +500,7 @@ ice_dcbnl_set_pg_bwg_cfg_tx(struct net_device *netdev, int pgid, u8 bw_pct)
+ if (pgid >= ICE_MAX_TRAFFIC_CLASS)
+ return;
+
+- new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
++ new_cfg = &pf->hw.port_info->qos_cfg.desired_dcbx_cfg;
+
+ new_cfg->etscfg.tcbwtable[pgid] = bw_pct;
+ }
+@@ -530,7 +530,7 @@ ice_dcbnl_get_pg_tc_cfg_rx(struct net_device *netdev, int prio,
+ if (prio >= ICE_MAX_USER_PRIORITY)
+ return;
+
+- *pgid = pi->local_dcbx_cfg.etscfg.prio_table[prio];
++ *pgid = pi->qos_cfg.local_dcbx_cfg.etscfg.prio_table[prio];
+ }
+
+ /**
+@@ -701,9 +701,9 @@ static int ice_dcbnl_setapp(struct net_device *netdev, struct dcb_app *app)
+
+ mutex_lock(&pf->tc_mutex);
+
+- new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
++ new_cfg = &pf->hw.port_info->qos_cfg.desired_dcbx_cfg;
+
+- old_cfg = &pf->hw.port_info->local_dcbx_cfg;
++ old_cfg = &pf->hw.port_info->qos_cfg.local_dcbx_cfg;
+
+ if (old_cfg->numapps == ICE_DCBX_MAX_APPS) {
+ ret = -EINVAL;
+@@ -753,7 +753,7 @@ static int ice_dcbnl_delapp(struct net_device *netdev, struct dcb_app *app)
+ return -EINVAL;
+
+ mutex_lock(&pf->tc_mutex);
+- old_cfg = &pf->hw.port_info->local_dcbx_cfg;
++ old_cfg = &pf->hw.port_info->qos_cfg.local_dcbx_cfg;
+
+ if (old_cfg->numapps <= 1)
+ goto delapp_out;
+@@ -762,7 +762,7 @@ static int ice_dcbnl_delapp(struct net_device *netdev, struct dcb_app *app)
+ if (ret)
+ goto delapp_out;
+
+- new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
++ new_cfg = &pf->hw.port_info->qos_cfg.desired_dcbx_cfg;
+
+ for (i = 1; i < new_cfg->numapps; i++) {
+ if (app->selector == new_cfg->app[i].selector &&
+@@ -815,7 +815,7 @@ static u8 ice_dcbnl_cee_set_all(struct net_device *netdev)
+ !(pf->dcbx_cap & DCB_CAP_DCBX_VER_CEE))
+ return ICE_DCB_NO_HW_CHG;
+
+- new_cfg = &pf->hw.port_info->desired_dcbx_cfg;
++ new_cfg = &pf->hw.port_info->qos_cfg.desired_dcbx_cfg;
+
+ mutex_lock(&pf->tc_mutex);
+
+@@ -886,7 +886,7 @@ void ice_dcbnl_set_all(struct ice_vsi *vsi)
+ if (!test_bit(ICE_FLAG_DCB_ENA, pf->flags))
+ return;
+
+- dcbxcfg = &pi->local_dcbx_cfg;
++ dcbxcfg = &pi->qos_cfg.local_dcbx_cfg;
+
+ for (i = 0; i < dcbxcfg->numapps; i++) {
+ u8 prio, tc_map;
+diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c
+index deb62b0c3855..d70573f5072c 100644
+--- a/drivers/net/ethernet/intel/ice/ice_ethtool.c
++++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c
+@@ -2986,7 +2986,7 @@ ice_get_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
+ pause->rx_pause = 0;
+ pause->tx_pause = 0;
+
+- dcbx_cfg = &pi->local_dcbx_cfg;
++ dcbx_cfg = &pi->qos_cfg.local_dcbx_cfg;
+
+ pcaps = kzalloc(sizeof(*pcaps), GFP_KERNEL);
+ if (!pcaps)
+@@ -3038,7 +3038,7 @@ ice_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause)
+
+ pi = vsi->port_info;
+ hw_link_info = &pi->phy.link_info;
+- dcbx_cfg = &pi->local_dcbx_cfg;
++ dcbx_cfg = &pi->qos_cfg.local_dcbx_cfg;
+ link_up = hw_link_info->link_info & ICE_AQ_LINK_UP;
+
+ /* Changing the port's flow control is not supported if this isn't the
+diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c
+index 3417de29facf..170367eaa95a 100644
+--- a/drivers/net/ethernet/intel/ice/ice_lib.c
++++ b/drivers/net/ethernet/intel/ice/ice_lib.c
+@@ -2078,7 +2078,7 @@ int ice_cfg_vlan_pruning(struct ice_vsi *vsi, bool ena, bool vlan_promisc)
+
+ static void ice_vsi_set_tc_cfg(struct ice_vsi *vsi)
+ {
+- struct ice_dcbx_cfg *cfg = &vsi->port_info->local_dcbx_cfg;
++ struct ice_dcbx_cfg *cfg = &vsi->port_info->qos_cfg.local_dcbx_cfg;
+
+ vsi->tc_cfg.ena_tc = ice_dcb_get_ena_tc(cfg);
+ vsi->tc_cfg.numtc = ice_dcb_get_num_tc(cfg);
+diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c
+index af5b7f33db9a..0f2544c420ac 100644
+--- a/drivers/net/ethernet/intel/ice/ice_txrx.c
++++ b/drivers/net/ethernet/intel/ice/ice_txrx.c
+@@ -2421,7 +2421,7 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_ring *tx_ring)
+ /* allow CONTROL frames egress from main VSI if FW LLDP disabled */
+ if (unlikely(skb->priority == TC_PRIO_CONTROL &&
+ vsi->type == ICE_VSI_PF &&
+- vsi->port_info->is_sw_lldp))
++ vsi->port_info->qos_cfg.is_sw_lldp))
+ offload.cd_qw1 |= (u64)(ICE_TX_DESC_DTYPE_CTX |
+ ICE_TX_CTX_DESC_SWTCH_UPLINK <<
+ ICE_TXD_CTX_QW1_CMD_S);
+diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
+index 2226a291a394..c09c085f637a 100644
+--- a/drivers/net/ethernet/intel/ice/ice_type.h
++++ b/drivers/net/ethernet/intel/ice/ice_type.h
+@@ -514,6 +514,14 @@ struct ice_dcbx_cfg {
+ #define ICE_DCBX_APPS_NON_WILLING 0x1
+ };
+
++struct ice_qos_cfg {
++ struct ice_dcbx_cfg local_dcbx_cfg; /* Oper/Local Cfg */
++ struct ice_dcbx_cfg desired_dcbx_cfg; /* CEE Desired Cfg */
++ struct ice_dcbx_cfg remote_dcbx_cfg; /* Peer Cfg */
++ u8 dcbx_status : 3; /* see ICE_DCBX_STATUS_DIS */
++ u8 is_sw_lldp : 1;
++};
++
+ struct ice_port_info {
+ struct ice_sched_node *root; /* Root Node per Port */
+ struct ice_hw *hw; /* back pointer to HW instance */
+@@ -537,13 +545,7 @@ struct ice_port_info {
+ 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 */
+- struct ice_dcbx_cfg desired_dcbx_cfg; /* CEE Desired Cfg */
+- /* LLDP/DCBX Status */
+- u8 dcbx_status:3; /* see ICE_DCBX_STATUS_DIS */
+- u8 is_sw_lldp:1;
++ struct ice_qos_cfg qos_cfg;
+ u8 is_vf:1;
+ };
+
+--
+2.30.2
+
--- /dev/null
+From c0f95b1698553bbfb1e4b9026fef3896d9999458 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 7 Apr 2021 18:36:04 +0300
+Subject: net: sched: fix err handler in tcf_action_init()
+
+From: Vlad Buslov <vladbu@nvidia.com>
+
+[ Upstream commit b3650bf76a32380d4d80a3e21b5583e7303f216c ]
+
+With recent changes that separated action module load from action
+initialization tcf_action_init() function error handling code was modified
+to manually release the loaded modules if loading/initialization of any
+further action in same batch failed. For the case when all modules
+successfully loaded and some of the actions were initialized before one of
+them failed in init handler. In this case for all previous actions the
+module will be released twice by the error handler: First time by the loop
+that manually calls module_put() for all ops, and second time by the action
+destroy code that puts the module after destroying the action.
+
+Reproduction:
+
+$ sudo tc actions add action simple sdata \"2\" index 2
+$ sudo tc actions add action simple sdata \"1\" index 1 \
+ action simple sdata \"2\" index 2
+RTNETLINK answers: File exists
+We have an error talking to the kernel
+$ sudo tc actions ls action simple
+total acts 1
+
+ action order 0: Simple <"2">
+ index 2 ref 1 bind 0
+$ sudo tc actions flush action simple
+$ sudo tc actions ls action simple
+$ sudo tc actions add action simple sdata \"2\" index 2
+Error: Failed to load TC action module.
+We have an error talking to the kernel
+$ lsmod | grep simple
+act_simple 20480 -1
+
+Fix the issue by modifying module reference counting handling in action
+initialization code:
+
+- Get module reference in tcf_idr_create() and put it in tcf_idr_release()
+instead of taking over the reference held by the caller.
+
+- Modify users of tcf_action_init_1() to always release the module
+reference which they obtain before calling init function instead of
+assuming that created action takes over the reference.
+
+- Finally, modify tcf_action_init_1() to not release the module reference
+when overwriting existing action as this is no longer necessary since both
+upper and lower layers obtain and manage their own module references
+independently.
+
+Fixes: d349f9976868 ("net_sched: fix RTNL deadlock again caused by request_module()")
+Suggested-by: Cong Wang <xiyou.wangcong@gmail.com>
+Signed-off-by: Vlad Buslov <vladbu@nvidia.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/act_api.h | 7 +------
+ net/sched/act_api.c | 26 ++++++++++++++++----------
+ net/sched/cls_api.c | 5 ++---
+ 3 files changed, 19 insertions(+), 19 deletions(-)
+
+diff --git a/include/net/act_api.h b/include/net/act_api.h
+index 27786090ae8e..2c88b8af3cdb 100644
+--- a/include/net/act_api.h
++++ b/include/net/act_api.h
+@@ -170,12 +170,7 @@ void tcf_idr_insert_many(struct tc_action *actions[]);
+ void tcf_idr_cleanup(struct tc_action_net *tn, u32 index);
+ int tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index,
+ struct tc_action **a, int bind);
+-int __tcf_idr_release(struct tc_action *a, bool bind, bool strict);
+-
+-static inline int tcf_idr_release(struct tc_action *a, bool bind)
+-{
+- return __tcf_idr_release(a, bind, false);
+-}
++int tcf_idr_release(struct tc_action *a, bool bind);
+
+ int tcf_register_action(struct tc_action_ops *a, struct pernet_operations *ops);
+ int tcf_unregister_action(struct tc_action_ops *a,
+diff --git a/net/sched/act_api.c b/net/sched/act_api.c
+index df1e494574d8..88e14cfeb5d5 100644
+--- a/net/sched/act_api.c
++++ b/net/sched/act_api.c
+@@ -142,7 +142,7 @@ static int __tcf_action_put(struct tc_action *p, bool bind)
+ return 0;
+ }
+
+-int __tcf_idr_release(struct tc_action *p, bool bind, bool strict)
++static int __tcf_idr_release(struct tc_action *p, bool bind, bool strict)
+ {
+ int ret = 0;
+
+@@ -168,7 +168,18 @@ int __tcf_idr_release(struct tc_action *p, bool bind, bool strict)
+
+ return ret;
+ }
+-EXPORT_SYMBOL(__tcf_idr_release);
++
++int tcf_idr_release(struct tc_action *a, bool bind)
++{
++ const struct tc_action_ops *ops = a->ops;
++ int ret;
++
++ ret = __tcf_idr_release(a, bind, false);
++ if (ret == ACT_P_DELETED)
++ module_put(ops->owner);
++ return ret;
++}
++EXPORT_SYMBOL(tcf_idr_release);
+
+ static size_t tcf_action_shared_attrs_size(const struct tc_action *act)
+ {
+@@ -445,6 +456,7 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est,
+ }
+
+ p->idrinfo = idrinfo;
++ __module_get(ops->owner);
+ p->ops = ops;
+ *a = p;
+ return 0;
+@@ -1017,13 +1029,6 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
+ if (!name)
+ a->hw_stats = hw_stats;
+
+- /* module count goes up only when brand new policy is created
+- * if it exists and is only bound to in a_o->init() then
+- * ACT_P_CREATED is not returned (a zero is).
+- */
+- if (err != ACT_P_CREATED)
+- module_put(a_o->owner);
+-
+ return a;
+
+ err_out:
+@@ -1083,7 +1088,8 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
+ tcf_idr_insert_many(actions);
+
+ *attr_size = tcf_action_full_attrs_size(sz);
+- return i - 1;
++ err = i - 1;
++ goto err_mod;
+
+ err:
+ tcf_action_destroy(actions, bind);
+diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
+index b45eb7f6cd49..d48ba4dee9a5 100644
+--- a/net/sched/cls_api.c
++++ b/net/sched/cls_api.c
+@@ -3065,10 +3065,9 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
+ rate_tlv, "police", ovr,
+ TCA_ACT_BIND, a_o, init_res,
+ rtnl_held, extack);
+- if (IS_ERR(act)) {
+- module_put(a_o->owner);
++ module_put(a_o->owner);
++ if (IS_ERR(act))
+ return PTR_ERR(act);
+- }
+
+ act->type = exts->type = TCA_OLD_COMPAT;
+ exts->actions[0] = act;
+--
+2.30.2
+