net/ice/base: support NVM access commands
[dpdk.git] / drivers / net / ice / base / ice_flex_pipe.c
index ecc2f57..7daaf10 100644 (file)
@@ -706,7 +706,7 @@ ice_acquire_global_cfg_lock(struct ice_hw *hw,
 {
        enum ice_status status;
 
-       ice_debug(hw, ICE_DBG_TRACE, "ice_acquire_global_cfg_lock");
+       ice_debug(hw, ICE_DBG_TRACE, "%s\n", __func__);
 
        status = ice_acquire_res(hw, ICE_GLOBAL_CFG_LOCK_RES_ID, access,
                                 ICE_GLOBAL_CFG_LOCK_TIMEOUT);
@@ -739,7 +739,7 @@ static void ice_release_global_cfg_lock(struct ice_hw *hw)
 enum ice_status
 ice_acquire_change_lock(struct ice_hw *hw, enum ice_aq_res_access_type access)
 {
-       ice_debug(hw, ICE_DBG_TRACE, "ice_acquire_change_lock");
+       ice_debug(hw, ICE_DBG_TRACE, "%s\n", __func__);
 
        return ice_acquire_res(hw, ICE_CHANGE_LOCK_RES_ID, access,
                               ICE_CHANGE_LOCK_TIMEOUT);
@@ -753,7 +753,7 @@ ice_acquire_change_lock(struct ice_hw *hw, enum ice_aq_res_access_type access)
  */
 void ice_release_change_lock(struct ice_hw *hw)
 {
-       ice_debug(hw, ICE_DBG_TRACE, "ice_release_change_lock");
+       ice_debug(hw, ICE_DBG_TRACE, "%s\n", __func__);
 
        ice_release_res(hw, ICE_CHANGE_LOCK_RES_ID);
 }
@@ -779,7 +779,7 @@ ice_aq_download_pkg(struct ice_hw *hw, struct ice_buf_hdr *pkg_buf,
        struct ice_aq_desc desc;
        enum ice_status status;
 
-       ice_debug(hw, ICE_DBG_TRACE, "ice_aq_download_pkg");
+       ice_debug(hw, ICE_DBG_TRACE, "%s\n", __func__);
 
        if (error_offset)
                *error_offset = 0;
@@ -830,7 +830,7 @@ ice_aq_update_pkg(struct ice_hw *hw, struct ice_buf_hdr *pkg_buf, u16 buf_size,
        struct ice_aq_desc desc;
        enum ice_status status;
 
-       ice_debug(hw, ICE_DBG_TRACE, "ice_aq_update_pkg");
+       ice_debug(hw, ICE_DBG_TRACE, "%s\n", __func__);
 
        if (error_offset)
                *error_offset = 0;
@@ -1018,7 +1018,7 @@ ice_aq_get_pkg_info_list(struct ice_hw *hw,
 {
        struct ice_aq_desc desc;
 
-       ice_debug(hw, ICE_DBG_TRACE, "ice_aq_get_pkg_info_list");
+       ice_debug(hw, ICE_DBG_TRACE, "%s\n", __func__);
        ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_pkg_info_list);
 
        return ice_aq_send_cmd(hw, &desc, pkg_info, buf_size, cd);
@@ -1144,6 +1144,7 @@ static enum ice_status ice_get_pkg_info(struct ice_hw *hw)
                                   pkg_info->pkg_info[i].name,
                                   sizeof(hw->active_pkg_name),
                                   ICE_NONDMA_TO_NONDMA);
+                       hw->active_pkg_in_nvm = pkg_info->pkg_info[i].is_in_nvm;
                }
                if (pkg_info->pkg_info[i].is_active_at_boot)
                        flags[place++] = 'B';
@@ -1708,6 +1709,234 @@ static struct ice_buf *ice_pkg_buf(struct ice_buf_build *bld)
        return &bld->buf;
 }
 
+/**
+ * ice_tunnel_port_in_use
+ * @hw: pointer to the HW structure
+ * @port: port to search for
+ * @index: optionally returns index
+ *
+ * Returns whether a port is already in use as a tunnel, and optionally its
+ * index
+ */
+bool ice_tunnel_port_in_use(struct ice_hw *hw, u16 port, u16 *index)
+{
+       u16 i;
+
+       for (i = 0; i < hw->tnl.count && i < ICE_TUNNEL_MAX_ENTRIES; i++)
+               if (hw->tnl.tbl[i].in_use && hw->tnl.tbl[i].port == port) {
+                       if (index)
+                               *index = i;
+                       return true;
+               }
+
+       return false;
+}
+
+/**
+ * ice_tunnel_get_type
+ * @hw: pointer to the HW structure
+ * @port: port to search for
+ * @type: returns tunnel index
+ *
+ * For a given port number, will return the type of tunnel.
+ */
+bool
+ice_tunnel_get_type(struct ice_hw *hw, u16 port, enum ice_tunnel_type *type)
+{
+       u16 i;
+
+       for (i = 0; i < hw->tnl.count && i < ICE_TUNNEL_MAX_ENTRIES; i++)
+               if (hw->tnl.tbl[i].in_use && hw->tnl.tbl[i].port == port) {
+                       *type = hw->tnl.tbl[i].type;
+                       return true;
+               }
+
+       return false;
+}
+
+/**
+ * ice_find_free_tunnel_entry
+ * @hw: pointer to the HW structure
+ * @type: tunnel type
+ * @index: optionally returns index
+ *
+ * Returns whether there is a free tunnel entry, and optionally its index
+ */
+static bool
+ice_find_free_tunnel_entry(struct ice_hw *hw, enum ice_tunnel_type type,
+                          u16 *index)
+{
+       u16 i;
+
+       for (i = 0; i < hw->tnl.count && i < ICE_TUNNEL_MAX_ENTRIES; i++)
+               if (hw->tnl.tbl[i].valid && !hw->tnl.tbl[i].in_use &&
+                   hw->tnl.tbl[i].type == type) {
+                       if (index)
+                               *index = i;
+                       return true;
+               }
+
+       return false;
+}
+
+/**
+ * ice_create_tunnel
+ * @hw: pointer to the HW structure
+ * @type: type of tunnel
+ * @port: port to use for vxlan tunnel
+ *
+ * Creates a tunnel
+ */
+enum ice_status
+ice_create_tunnel(struct ice_hw *hw, enum ice_tunnel_type type, u16 port)
+{
+       struct ice_boost_tcam_section *sect_rx, *sect_tx;
+       enum ice_status status = ICE_ERR_MAX_LIMIT;
+       struct ice_buf_build *bld;
+       u16 index;
+
+       if (ice_tunnel_port_in_use(hw, port, NULL))
+               return ICE_ERR_ALREADY_EXISTS;
+
+       if (!ice_find_free_tunnel_entry(hw, type, &index))
+               return ICE_ERR_OUT_OF_RANGE;
+
+       bld = ice_pkg_buf_alloc(hw);
+       if (!bld)
+               return ICE_ERR_NO_MEMORY;
+
+       /* allocate 2 sections, one for RX parser, one for TX parser */
+       if (ice_pkg_buf_reserve_section(bld, 2))
+               goto ice_create_tunnel_err;
+
+       sect_rx = (struct ice_boost_tcam_section *)
+               ice_pkg_buf_alloc_section(bld, ICE_SID_RXPARSER_BOOST_TCAM,
+                                         sizeof(*sect_rx));
+       if (!sect_rx)
+               goto ice_create_tunnel_err;
+       sect_rx->count = CPU_TO_LE16(1);
+
+       sect_tx = (struct ice_boost_tcam_section *)
+               ice_pkg_buf_alloc_section(bld, ICE_SID_TXPARSER_BOOST_TCAM,
+                                         sizeof(*sect_tx));
+       if (!sect_tx)
+               goto ice_create_tunnel_err;
+       sect_tx->count = CPU_TO_LE16(1);
+
+       /* copy original boost entry to update package buffer */
+       ice_memcpy(sect_rx->tcam, hw->tnl.tbl[index].boost_entry,
+                  sizeof(*sect_rx->tcam), ICE_NONDMA_TO_NONDMA);
+
+       /* over-write the never-match dest port key bits with the encoded port
+        * bits
+        */
+       ice_set_key((u8 *)&sect_rx->tcam[0].key, sizeof(sect_rx->tcam[0].key),
+                   (u8 *)&port, NULL, NULL, NULL,
+                   offsetof(struct ice_boost_key_value, hv_dst_port_key),
+                   sizeof(sect_rx->tcam[0].key.key.hv_dst_port_key));
+
+       /* exact copy of entry to TX section entry */
+       ice_memcpy(sect_tx->tcam, sect_rx->tcam, sizeof(*sect_tx->tcam),
+                  ICE_NONDMA_TO_NONDMA);
+
+       status = ice_update_pkg(hw, ice_pkg_buf(bld), 1);
+       if (!status) {
+               hw->tnl.tbl[index].port = port;
+               hw->tnl.tbl[index].in_use = true;
+       }
+
+ice_create_tunnel_err:
+       ice_pkg_buf_free(hw, bld);
+
+       return status;
+}
+
+/**
+ * ice_destroy_tunnel
+ * @hw: pointer to the HW structure
+ * @port: port of tunnel to destroy (ignored if the all parameter is true)
+ * @all: flag that states to destroy all tunnels
+ *
+ * Destroys a tunnel or all tunnels by creating an update package buffer
+ * targeting the specific updates requested and then performing an update
+ * package.
+ */
+enum ice_status ice_destroy_tunnel(struct ice_hw *hw, u16 port, bool all)
+{
+       struct ice_boost_tcam_section *sect_rx, *sect_tx;
+       enum ice_status status = ICE_ERR_MAX_LIMIT;
+       struct ice_buf_build *bld;
+       u16 count = 0;
+       u16 size;
+       u16 i;
+
+       /* determine count */
+       for (i = 0; i < hw->tnl.count && i < ICE_TUNNEL_MAX_ENTRIES; i++)
+               if (hw->tnl.tbl[i].valid && hw->tnl.tbl[i].in_use &&
+                   (all || hw->tnl.tbl[i].port == port))
+                       count++;
+
+       if (!count)
+               return ICE_ERR_PARAM;
+
+       /* size of section - there is at least one entry */
+       size = (count - 1) * sizeof(*sect_rx->tcam) + sizeof(*sect_rx);
+
+       bld = ice_pkg_buf_alloc(hw);
+       if (!bld)
+               return ICE_ERR_NO_MEMORY;
+
+       /* allocate 2 sections, one for RX parser, one for TX parser */
+       if (ice_pkg_buf_reserve_section(bld, 2))
+               goto ice_destroy_tunnel_err;
+
+       sect_rx = (struct ice_boost_tcam_section *)
+               ice_pkg_buf_alloc_section(bld, ICE_SID_RXPARSER_BOOST_TCAM,
+                                         size);
+       if (!sect_rx)
+               goto ice_destroy_tunnel_err;
+       sect_rx->count = CPU_TO_LE16(1);
+
+       sect_tx = (struct ice_boost_tcam_section *)
+               ice_pkg_buf_alloc_section(bld, ICE_SID_TXPARSER_BOOST_TCAM,
+                                         size);
+       if (!sect_tx)
+               goto ice_destroy_tunnel_err;
+       sect_tx->count = CPU_TO_LE16(1);
+
+       /* copy original boost entry to update package buffer, one copy to RX
+        * section, another copy to the TX section
+        */
+       for (i = 0; i < hw->tnl.count && i < ICE_TUNNEL_MAX_ENTRIES; i++)
+               if (hw->tnl.tbl[i].valid && hw->tnl.tbl[i].in_use &&
+                   (all || hw->tnl.tbl[i].port == port)) {
+                       ice_memcpy(sect_rx->tcam + i,
+                                  hw->tnl.tbl[i].boost_entry,
+                                  sizeof(*sect_rx->tcam),
+                                  ICE_NONDMA_TO_NONDMA);
+                       ice_memcpy(sect_tx->tcam + i,
+                                  hw->tnl.tbl[i].boost_entry,
+                                  sizeof(*sect_tx->tcam),
+                                  ICE_NONDMA_TO_NONDMA);
+                       hw->tnl.tbl[i].marked = true;
+               }
+
+       status = ice_update_pkg(hw, ice_pkg_buf(bld), 1);
+       if (!status)
+               for (i = 0; i < hw->tnl.count &&
+                    i < ICE_TUNNEL_MAX_ENTRIES; i++)
+                       if (hw->tnl.tbl[i].marked) {
+                               hw->tnl.tbl[i].port = 0;
+                               hw->tnl.tbl[i].in_use = false;
+                               hw->tnl.tbl[i].marked = false;
+                       }
+
+ice_destroy_tunnel_err:
+       ice_pkg_buf_free(hw, bld);
+
+       return status;
+}
+
 /**
  * ice_find_prot_off - find prot ID and offset pair, based on prof and FV index
  * @hw: pointer to the hardware structure
@@ -2270,6 +2499,102 @@ ice_vsig_add_mv_vsi(struct ice_hw *hw, enum ice_block blk, u16 vsi, u16 vsig)
        return ICE_SUCCESS;
 }
 
+/**
+ * ice_prof_has_mask_idx - determine if profile index masking is identical
+ * @hw: pointer to the hardware structure
+ * @blk: HW block
+ * @prof: profile to check
+ * @idx: profile index to check
+ * @masks: masks to match
+ */
+static bool
+ice_prof_has_mask_idx(struct ice_hw *hw, enum ice_block blk, u8 prof, u16 idx,
+                     u16 mask)
+{
+       bool expect_no_mask = false;
+       bool found = false;
+       bool match = false;
+       u16 i;
+
+       /* If mask is 0x0000 or 0xffff, then there is no masking */
+       if (mask == 0 || mask == 0xffff)
+               expect_no_mask = true;
+
+       /* Scan the enabled masks on this profile, for the specified idx */
+       for (i = 0; i < ICE_PROFILE_MASK_COUNT; i++)
+               if (hw->blk[blk].es.mask_ena[prof] & BIT(i))
+                       if (hw->blk[blk].masks.masks[i].in_use &&
+                           hw->blk[blk].masks.masks[i].idx == idx) {
+                               found = true;
+                               if (hw->blk[blk].masks.masks[i].mask == mask)
+                                       match = true;
+                               break;
+                       }
+
+       if (expect_no_mask) {
+               if (found)
+                       return false;
+       } else {
+               if (!match)
+                       return false;
+       }
+
+       return true;
+}
+
+/**
+ * ice_prof_has_mask - determine if profile masking is identical
+ * @hw: pointer to the hardware structure
+ * @blk: HW block
+ * @prof: profile to check
+ * @masks: masks to match
+ */
+static bool
+ice_prof_has_mask(struct ice_hw *hw, enum ice_block blk, u8 prof, u16 *masks)
+{
+       u16 i;
+
+       /* es->mask_ena[prof] will have the mask */
+       for (i = 0; i < hw->blk[blk].es.fvw; i++)
+               if (!ice_prof_has_mask_idx(hw, blk, prof, i, masks[i]))
+                       return false;
+
+       return true;
+}
+
+/**
+ * ice_find_prof_id_with_mask - find profile ID for a given field vector
+ * @hw: pointer to the hardware structure
+ * @blk: HW block
+ * @fv: field vector to search for
+ * @masks: masks for fv
+ * @prof_id: receives the profile ID
+ */
+static enum ice_status
+ice_find_prof_id_with_mask(struct ice_hw *hw, enum ice_block blk,
+                          struct ice_fv_word *fv, u16 *masks, u8 *prof_id)
+{
+       struct ice_es *es = &hw->blk[blk].es;
+       u16 i;
+
+       for (i = 0; i < es->count; i++) {
+               u16 off = i * es->fvw;
+               u16 j;
+
+               if (memcmp(&es->t[off], fv, es->fvw * sizeof(*fv)))
+                       continue;
+
+               /* check if masks settings are the same for this profile */
+               if (!ice_prof_has_mask(hw, blk, i, masks))
+                       continue;
+
+               *prof_id = i;
+               return ICE_SUCCESS;
+       }
+
+       return ICE_ERR_DOES_NOT_EXIST;
+}
+
 /**
  * ice_find_prof_id - find profile ID for a given field vector
  * @hw: pointer to the hardware structure
@@ -2457,6 +2782,334 @@ ice_prof_inc_ref(struct ice_hw *hw, enum ice_block blk, u8 prof_id)
        return ICE_SUCCESS;
 }
 
+/**
+ * ice_write_prof_mask_reg - write profile mask register
+ * @hw: pointer to the HW struct
+ * @blk: hardware block
+ * @mask_idx: mask index
+ * @idx: index of the FV which will use the mask
+ * @mask: the 16-bit mask
+ */
+static void
+ice_write_prof_mask_reg(struct ice_hw *hw, enum ice_block blk, u16 mask_idx,
+                       u16 idx, u16 mask)
+{
+       u32 offset;
+       u32 val;
+
+       switch (blk) {
+       case ICE_BLK_RSS:
+               offset = GLQF_HMASK(mask_idx);
+               val = (idx << GLQF_HMASK_MSK_INDEX_S) &
+                       GLQF_HMASK_MSK_INDEX_M;
+               val |= (mask << GLQF_HMASK_MASK_S) & GLQF_HMASK_MASK_M;
+               break;
+       case ICE_BLK_FD:
+               offset = GLQF_FDMASK(mask_idx);
+               val = (idx << GLQF_FDMASK_MSK_INDEX_S) &
+                       GLQF_FDMASK_MSK_INDEX_M;
+               val |= (mask << GLQF_FDMASK_MASK_S) &
+                       GLQF_FDMASK_MASK_M;
+               break;
+       default:
+               ice_debug(hw, ICE_DBG_PKG, "No profile masks for block %d\n",
+                         blk);
+               return;
+       }
+
+       wr32(hw, offset, val);
+       ice_debug(hw, ICE_DBG_PKG, "write mask, blk %d (%d): %x = %x\n",
+                 blk, idx, offset, val);
+}
+
+/**
+ * ice_write_prof_mask_enable_res - write profile mask enable register
+ * @hw: pointer to the HW struct
+ * @blk: hardware block
+ * @prof_id: profile id
+ * @enable_mask: enable mask
+ */
+static void
+ice_write_prof_mask_enable_res(struct ice_hw *hw, enum ice_block blk,
+                              u16 prof_id, u32 enable_mask)
+{
+       u32 offset;
+
+       switch (blk) {
+       case ICE_BLK_RSS:
+               offset = GLQF_HMASK_SEL(prof_id);
+               break;
+       case ICE_BLK_FD:
+               offset = GLQF_FDMASK_SEL(prof_id);
+               break;
+       default:
+               ice_debug(hw, ICE_DBG_PKG, "No profile masks for block %d\n",
+                         blk);
+               return;
+       }
+
+       wr32(hw, offset, enable_mask);
+       ice_debug(hw, ICE_DBG_PKG, "write mask enable, blk %d (%d): %x = %x\n",
+                 blk, prof_id, offset, enable_mask);
+}
+
+/**
+ * ice_init_prof_masks - initial prof masks
+ * @hw: pointer to the HW struct
+ * @blk: hardware block
+ */
+static void ice_init_prof_masks(struct ice_hw *hw, enum ice_block blk)
+{
+#define MAX_NUM_PORTS    8
+       u16 num_ports = MAX_NUM_PORTS;
+       u16 i;
+
+       ice_init_lock(&hw->blk[blk].masks.lock);
+
+       hw->blk[blk].masks.count = ICE_PROFILE_MASK_COUNT / num_ports;
+       hw->blk[blk].masks.first = hw->pf_id * hw->blk[blk].masks.count;
+
+       ice_memset(hw->blk[blk].masks.masks, 0,
+                  sizeof(hw->blk[blk].masks.masks), ICE_NONDMA_MEM);
+
+       for (i = hw->blk[blk].masks.first;
+            i < hw->blk[blk].masks.first + hw->blk[blk].masks.count; i++)
+               ice_write_prof_mask_reg(hw, blk, i, 0, 0);
+}
+
+/**
+ * ice_init_all_prof_masks - initial all prof masks
+ * @hw: pointer to the HW struct
+ */
+void ice_init_all_prof_masks(struct ice_hw *hw)
+{
+       ice_init_prof_masks(hw, ICE_BLK_RSS);
+       ice_init_prof_masks(hw, ICE_BLK_FD);
+}
+
+/**
+ * ice_alloc_prof_mask - allocate profile mask
+ * @hw: pointer to the HW struct
+ * @blk: hardware block
+ * @idx: index of FV which will use the mask
+ * @mask: the 16-bit mask
+ * @mask_idx: variable to receive the mask index
+ */
+static enum ice_status
+ice_alloc_prof_mask(struct ice_hw *hw, enum ice_block blk, u16 idx, u16 mask,
+                   u16 *mask_idx)
+{
+       bool found_unused = false, found_copy = false;
+       enum ice_status status = ICE_ERR_MAX_LIMIT;
+       u16 unused_idx = 0, copy_idx = 0;
+       u16 i;
+
+       if (blk != ICE_BLK_RSS && blk != ICE_BLK_FD)
+               return ICE_ERR_PARAM;
+
+       ice_acquire_lock(&hw->blk[blk].masks.lock);
+
+       for (i = hw->blk[blk].masks.first;
+            i < hw->blk[blk].masks.first + hw->blk[blk].masks.count; i++)
+               if (hw->blk[blk].masks.masks[i].in_use) {
+                       /* if mask is in use and it exactly duplicates the
+                        * desired mask and index, then in can be reused
+                        */
+                       if (hw->blk[blk].masks.masks[i].mask == mask &&
+                           hw->blk[blk].masks.masks[i].idx == idx) {
+                               found_copy = true;
+                               copy_idx = i;
+                               break;
+                       }
+               } else {
+                       /* save off unused index, but keep searching in case
+                        * there is an exact match later on
+                        */
+                       if (!found_unused) {
+                               found_unused = true;
+                               unused_idx = i;
+                       }
+               }
+
+       if (found_copy)
+               i = copy_idx;
+       else if (found_unused)
+               i = unused_idx;
+       else
+               goto err_ice_alloc_prof_mask;
+
+       /* update mask for a new entry */
+       if (found_unused) {
+               hw->blk[blk].masks.masks[i].in_use = true;
+               hw->blk[blk].masks.masks[i].mask = mask;
+               hw->blk[blk].masks.masks[i].idx = idx;
+               hw->blk[blk].masks.masks[i].ref = 0;
+               ice_write_prof_mask_reg(hw, blk, i, idx, mask);
+       }
+
+       hw->blk[blk].masks.masks[i].ref++;
+       *mask_idx = i;
+       status = ICE_SUCCESS;
+
+err_ice_alloc_prof_mask:
+       ice_release_lock(&hw->blk[blk].masks.lock);
+
+       return status;
+}
+
+/**
+ * ice_free_prof_mask - free profile mask
+ * @hw: pointer to the HW struct
+ * @blk: hardware block
+ * @mask_idx: index of mask
+ */
+static enum ice_status
+ice_free_prof_mask(struct ice_hw *hw, enum ice_block blk, u16 mask_idx)
+{
+       if (blk != ICE_BLK_RSS && blk != ICE_BLK_FD)
+               return ICE_ERR_PARAM;
+
+       if (!(mask_idx >= hw->blk[blk].masks.first &&
+             mask_idx < hw->blk[blk].masks.first + hw->blk[blk].masks.count))
+               return ICE_ERR_DOES_NOT_EXIST;
+
+       ice_acquire_lock(&hw->blk[blk].masks.lock);
+
+       if (!hw->blk[blk].masks.masks[mask_idx].in_use)
+               goto exit_ice_free_prof_mask;
+
+       if (hw->blk[blk].masks.masks[mask_idx].ref > 1) {
+               hw->blk[blk].masks.masks[mask_idx].ref--;
+               goto exit_ice_free_prof_mask;
+       }
+
+       /* remove mask */
+       hw->blk[blk].masks.masks[mask_idx].in_use = false;
+       hw->blk[blk].masks.masks[mask_idx].mask = 0;
+       hw->blk[blk].masks.masks[mask_idx].idx = 0;
+
+       /* update mask as unused entry */
+       ice_debug(hw, ICE_DBG_PKG, "Free mask, blk %d, mask %d", blk, mask_idx);
+       ice_write_prof_mask_reg(hw, blk, mask_idx, 0, 0);
+
+exit_ice_free_prof_mask:
+       ice_release_lock(&hw->blk[blk].masks.lock);
+
+       return ICE_SUCCESS;
+}
+
+/**
+ * ice_free_prof_masks - free all profile masks for a profile
+ * @hw: pointer to the HW struct
+ * @blk: hardware block
+ * @prof_id: profile id
+ */
+static enum ice_status
+ice_free_prof_masks(struct ice_hw *hw, enum ice_block blk, u16 prof_id)
+{
+       u32 mask_bm;
+       u16 i;
+
+       if (blk != ICE_BLK_RSS && blk != ICE_BLK_FD)
+               return ICE_ERR_PARAM;
+
+       mask_bm = hw->blk[blk].es.mask_ena[prof_id];
+       for (i = 0; i < BITS_PER_BYTE * sizeof(mask_bm); i++)
+               if (mask_bm & BIT(i))
+                       ice_free_prof_mask(hw, blk, i);
+
+       return ICE_SUCCESS;
+}
+
+/**
+ * ice_shutdown_prof_masks - releases lock for masking
+ * @hw: pointer to the HW struct
+ * @blk: hardware block
+ *
+ * This should be called before unloading the driver
+ */
+static void ice_shutdown_prof_masks(struct ice_hw *hw, enum ice_block blk)
+{
+       u16 i;
+
+       ice_acquire_lock(&hw->blk[blk].masks.lock);
+
+       for (i = hw->blk[blk].masks.first;
+            i < hw->blk[blk].masks.first + hw->blk[blk].masks.count; i++) {
+               ice_write_prof_mask_reg(hw, blk, i, 0, 0);
+
+               hw->blk[blk].masks.masks[i].in_use = false;
+               hw->blk[blk].masks.masks[i].idx = 0;
+               hw->blk[blk].masks.masks[i].mask = 0;
+       }
+
+       ice_release_lock(&hw->blk[blk].masks.lock);
+       ice_destroy_lock(&hw->blk[blk].masks.lock);
+}
+
+/**
+ * ice_shutdown_all_prof_masks - releases all locks for masking
+ * @hw: pointer to the HW struct
+ * @blk: hardware block
+ *
+ * This should be called before unloading the driver
+ */
+void ice_shutdown_all_prof_masks(struct ice_hw *hw)
+{
+       ice_shutdown_prof_masks(hw, ICE_BLK_RSS);
+       ice_shutdown_prof_masks(hw, ICE_BLK_FD);
+}
+
+/**
+ * ice_update_prof_masking - set registers according to masking
+ * @hw: pointer to the HW struct
+ * @blk: hardware block
+ * @prof_id: profile id
+ * @es: field vector
+ * @masks: masks
+ */
+static enum ice_status
+ice_update_prof_masking(struct ice_hw *hw, enum ice_block blk, u16 prof_id,
+                       struct ice_fv_word *es, u16 *masks)
+{
+       bool err = false;
+       u32 ena_mask = 0;
+       u16 idx;
+       u16 i;
+
+       /* Only support FD and RSS masking, otherwise nothing to be done */
+       if (blk != ICE_BLK_RSS && blk != ICE_BLK_FD)
+               return ICE_SUCCESS;
+
+       for (i = 0; i < hw->blk[blk].es.fvw; i++)
+               if (masks[i] && masks[i] != 0xFFFF) {
+                       if (!ice_alloc_prof_mask(hw, blk, i, masks[i], &idx)) {
+                               ena_mask |= BIT(idx);
+                       } else {
+                               /* not enough bitmaps */
+                               err = true;
+                               break;
+                       }
+               }
+
+       if (err) {
+               /* free any bitmaps we have allocated */
+               for (i = 0; i < BITS_PER_BYTE * sizeof(ena_mask); i++)
+                       if (ena_mask & BIT(i))
+                               ice_free_prof_mask(hw, blk, i);
+
+               return ICE_ERR_OUT_OF_RANGE;
+       }
+
+       /* enable the masks for this profile */
+       ice_write_prof_mask_enable_res(hw, blk, prof_id, ena_mask);
+
+       /* store enabled masks with profile so that they can be freed later */
+       hw->blk[blk].es.mask_ena[prof_id] = ena_mask;
+
+       return ICE_SUCCESS;
+}
+
 /**
  * ice_write_es - write an extraction sequence to hardware
  * @hw: pointer to the HW struct
@@ -2496,6 +3149,7 @@ ice_prof_dec_ref(struct ice_hw *hw, enum ice_block blk, u8 prof_id)
        if (hw->blk[blk].es.ref_count[prof_id] > 0) {
                if (!--hw->blk[blk].es.ref_count[prof_id]) {
                        ice_write_es(hw, blk, prof_id, NULL);
+                       ice_free_prof_masks(hw, blk, prof_id);
                        return ice_free_prof_id(hw, blk, prof_id);
                }
        }
@@ -2760,6 +3414,26 @@ void ice_fill_blk_tbls(struct ice_hw *hw)
        ice_init_sw_db(hw);
 }
 
+/**
+ * ice_free_prof_map - free profile map
+ * @hw: pointer to the hardware structure
+ * @blk_idx: HW block index
+ */
+static void ice_free_prof_map(struct ice_hw *hw, u8 blk_idx)
+{
+       struct ice_es *es = &hw->blk[blk_idx].es;
+       struct ice_prof_map *del, *tmp;
+
+       ice_acquire_lock(&es->prof_map_lock);
+       LIST_FOR_EACH_ENTRY_SAFE(del, tmp, &es->prof_map,
+                                ice_prof_map, list) {
+               LIST_DEL(&del->list);
+               ice_free(hw, del);
+       }
+       INIT_LIST_HEAD(&es->prof_map);
+       ice_release_lock(&es->prof_map_lock);
+}
+
 /**
  * ice_free_flow_profs - free flow profile entries
  * @hw: pointer to the hardware structure
@@ -2769,10 +3443,7 @@ static void ice_free_flow_profs(struct ice_hw *hw, u8 blk_idx)
 {
        struct ice_flow_prof *p, *tmp;
 
-       /* This call is being made as part of resource deallocation
-        * during unload. Lock acquire and release will not be
-        * necessary here.
-        */
+       ice_acquire_lock(&hw->fl_profs_locks[blk_idx]);
        LIST_FOR_EACH_ENTRY_SAFE(p, tmp, &hw->fl_profs[blk_idx],
                                 ice_flow_prof, l_entry) {
                struct ice_flow_entry *e, *t;
@@ -2786,6 +3457,7 @@ static void ice_free_flow_profs(struct ice_hw *hw, u8 blk_idx)
                        ice_free(hw, p->acts);
                ice_free(hw, p);
        }
+       ice_release_lock(&hw->fl_profs_locks[blk_idx]);
 
        /* if driver is in reset and tables are being cleared
         * re-initialize the flow profile list heads
@@ -2822,17 +3494,12 @@ void ice_free_hw_tbls(struct ice_hw *hw)
        for (i = 0; i < ICE_BLK_COUNT; i++) {
                if (hw->blk[i].is_list_init) {
                        struct ice_es *es = &hw->blk[i].es;
-                       struct ice_prof_map *del, *tmp;
-
-                       LIST_FOR_EACH_ENTRY_SAFE(del, tmp, &es->prof_map,
-                                                ice_prof_map, list) {
-                               LIST_DEL(&del->list);
-                               ice_free(hw, del);
-                       }
 
+                       ice_free_prof_map(hw, i);
                        ice_destroy_lock(&es->prof_map_lock);
                        ice_free_flow_profs(hw, i);
                        ice_destroy_lock(&hw->fl_profs_locks[i]);
+
                        hw->blk[i].is_list_init = false;
                }
                ice_free_vsig_tbl(hw, (enum ice_block)i);
@@ -2847,6 +3514,7 @@ void ice_free_hw_tbls(struct ice_hw *hw)
                ice_free(hw, hw->blk[i].es.t);
                ice_free(hw, hw->blk[i].es.ref_count);
                ice_free(hw, hw->blk[i].es.written);
+               ice_free(hw, hw->blk[i].es.mask_ena);
        }
 
        LIST_FOR_EACH_ENTRY_SAFE(r, rt, &hw->rss_list_head,
@@ -2855,6 +3523,7 @@ void ice_free_hw_tbls(struct ice_hw *hw)
                ice_free(hw, r);
        }
        ice_destroy_lock(&hw->rss_locks);
+       ice_shutdown_all_prof_masks(hw);
        ice_memset(hw->blk, 0, sizeof(hw->blk), ICE_NONDMA_MEM);
 }
 
@@ -2879,6 +3548,7 @@ enum ice_status ice_init_hw_tbls(struct ice_hw *hw)
 
        ice_init_lock(&hw->rss_locks);
        INIT_LIST_HEAD(&hw->rss_list_head);
+       ice_init_all_prof_masks(hw);
        for (i = 0; i < ICE_BLK_COUNT; i++) {
                struct ice_prof_redir *prof_redir = &hw->blk[i].prof_redir;
                struct ice_prof_tcam *prof = &hw->blk[i].prof;
@@ -2970,7 +3640,8 @@ enum ice_status ice_init_hw_tbls(struct ice_hw *hw)
 
                es->written = (u8 *)
                        ice_calloc(hw, es->count, sizeof(*es->written));
-
+               es->mask_ena = (u32 *)
+                       ice_calloc(hw, es->count, sizeof(*es->mask_ena));
                if (!es->ref_count)
                        goto err;
        }
@@ -3484,7 +4155,7 @@ ice_update_fd_swap(struct ice_hw *hw, u16 prof_id, struct ice_fv_word *es)
        u32 mask_sel = 0;
        u8 i, j, k;
 
-       ice_memset(pair_list, 0, sizeof(pair_list), ICE_NONDMA_MEM);
+       ice_zero_bitmap(pair_list, ICE_FD_SRC_DST_PAIR_COUNT);
 
        ice_init_fd_mask_regs(hw);
 
@@ -3529,7 +4200,7 @@ ice_update_fd_swap(struct ice_hw *hw, u16 prof_id, struct ice_fv_word *es)
                                index = i + 1;
 
                        /* check for room */
-                       if (first_free + 1 < ice_fd_pairs[index].count)
+                       if (first_free + 1 < (s8)ice_fd_pairs[index].count)
                                return ICE_ERR_MAX_LIMIT;
 
                        /* place in extraction sequence */
@@ -3539,6 +4210,9 @@ ice_update_fd_swap(struct ice_hw *hw, u16 prof_id, struct ice_fv_word *es)
                                es[first_free - k].off =
                                        ice_fd_pairs[index].off + (k * 2);
 
+                               if (k > first_free)
+                                       return ICE_ERR_OUT_OF_RANGE;
+
                                /* keep track of non-relevant fields */
                                mask_sel |= 1 << (first_free - k);
                        }
@@ -3611,6 +4285,115 @@ ice_update_fd_swap(struct ice_hw *hw, u16 prof_id, struct ice_fv_word *es)
        return ICE_SUCCESS;
 }
 
+/**
+ * ice_add_prof_with_mask - add profile
+ * @hw: pointer to the HW struct
+ * @blk: hardware block
+ * @id: profile tracking ID
+ * @ptypes: array of bitmaps indicating ptypes (ICE_FLOW_PTYPE_MAX bits)
+ * @es: extraction sequence (length of array is determined by the block)
+ * @masks: extraction sequence (length of array is determined by the block)
+ *
+ * This function registers a profile, which matches a set of PTYPES with a
+ * particular extraction sequence. While the hardware profile is allocated
+ * it will not be written until the first call to ice_add_flow that specifies
+ * the ID value used here.
+ */
+enum ice_status
+ice_add_prof_with_mask(struct ice_hw *hw, enum ice_block blk, u64 id,
+                      u8 ptypes[], struct ice_fv_word *es, u16 *masks)
+{
+       u32 bytes = DIVIDE_AND_ROUND_UP(ICE_FLOW_PTYPE_MAX, BITS_PER_BYTE);
+       struct ice_prof_map *prof;
+       enum ice_status status;
+       u32 byte = 0;
+       u8 prof_id;
+
+       ice_acquire_lock(&hw->blk[blk].es.prof_map_lock);
+
+       /* search for existing profile */
+       status = ice_find_prof_id_with_mask(hw, blk, es, masks, &prof_id);
+       if (status) {
+               /* allocate profile ID */
+               status = ice_alloc_prof_id(hw, blk, &prof_id);
+               if (status)
+                       goto err_ice_add_prof;
+               if (blk == ICE_BLK_FD) {
+                       /* For Flow Director block, the extraction sequence may
+                        * need to be altered in the case where there are paired
+                        * fields that have no match. This is necessary because
+                        * for Flow Director, src and dest fields need to paired
+                        * for filter programming and these values are swapped
+                        * during Tx.
+                        */
+                       status = ice_update_fd_swap(hw, prof_id, es);
+                       if (status)
+                               goto err_ice_add_prof;
+               }
+               status = ice_update_prof_masking(hw, blk, prof_id, es, masks);
+               if (status)
+                       goto err_ice_add_prof;
+
+               /* and write new es */
+               ice_write_es(hw, blk, prof_id, es);
+       }
+
+       ice_prof_inc_ref(hw, blk, prof_id);
+
+       /* add profile info */
+
+       prof = (struct ice_prof_map *)ice_malloc(hw, sizeof(*prof));
+       if (!prof)
+               goto err_ice_add_prof;
+
+       prof->profile_cookie = id;
+       prof->prof_id = prof_id;
+       prof->ptype_count = 0;
+       prof->context = 0;
+
+       /* build list of ptgs */
+       while (bytes && prof->ptype_count < ICE_MAX_PTYPE_PER_PROFILE) {
+               u32 bit;
+
+               if (!ptypes[byte]) {
+                       bytes--;
+                       byte++;
+                       continue;
+               }
+               /* Examine 8 bits per byte */
+               for (bit = 0; bit < 8; bit++) {
+                       if (ptypes[byte] & BIT(bit)) {
+                               u16 ptype;
+                               u8 m;
+
+                               ptype = byte * BITS_PER_BYTE + bit;
+                               if (ptype < ICE_FLOW_PTYPE_MAX) {
+                                       prof->ptype[prof->ptype_count] = ptype;
+
+                                       if (++prof->ptype_count >=
+                                               ICE_MAX_PTYPE_PER_PROFILE)
+                                               break;
+                               }
+
+                               /* nothing left in byte, then exit */
+                               m = ~((1 << (bit + 1)) - 1);
+                               if (!(ptypes[byte] & m))
+                                       break;
+                       }
+               }
+
+               bytes--;
+               byte++;
+       }
+
+       LIST_ADD(&prof->list, &hw->blk[blk].es.prof_map);
+       status = ICE_SUCCESS;
+
+err_ice_add_prof:
+       ice_release_lock(&hw->blk[blk].es.prof_map_lock);
+       return status;
+}
+
 /**
  * ice_add_prof - add profile
  * @hw: pointer to the HW struct
@@ -4135,7 +4918,10 @@ ice_add_prof_to_lst(struct ice_hw *hw, enum ice_block blk,
                p->tcam[i].prof_id = map->prof_id;
                p->tcam[i].tcam_idx = ICE_INVALID_TCAM;
 
-               ice_ptg_find_ptype(hw, blk, map->ptype[i], &ptg);
+               if (ice_ptg_find_ptype(hw, blk, map->ptype[i], &ptg)) {
+                       ice_free(hw, p);
+                       return ICE_ERR_CFG;
+               }
 
                p->tcam[i].ptg = ptg;
        }
@@ -4260,7 +5046,7 @@ ice_adj_prof_priorities(struct ice_hw *hw, enum ice_block blk, u16 vsig,
        enum ice_status status;
        u16 idx;
 
-       ice_memset(ptgs_used, 0, sizeof(ptgs_used), ICE_NONDMA_MEM);
+       ice_zero_bitmap(ptgs_used, ICE_XLT1_CNT);
        idx = vsig & ICE_VSIG_IDX_M;
 
        /* Priority is based on the order in which the profiles are added. The
@@ -4528,6 +5314,47 @@ ice_find_prof_vsig(struct ice_hw *hw, enum ice_block blk, u64 hdl, u16 *vsig)
        return status == ICE_SUCCESS;
 }
 
+/**
+ * ice_add_vsi_flow - add VSI flow
+ * @hw: pointer to the HW struct
+ * @blk: hardware block
+ * @vsi: input VSI
+ * @vsig: target VSIG to include the input VSI
+ *
+ * Calling this function will add the VSI to a given VSIG and
+ * update the HW tables accordingly. This call can be used to
+ * add multiple VSIs to a VSIG if we know beforehand that those
+ * VSIs have the same characteristics of the VSIG. This will
+ * save time in generating a new VSIG and TCAMs till a match is
+ * found and subsequent rollback when a matching VSIG is found.
+ */
+enum ice_status
+ice_add_vsi_flow(struct ice_hw *hw, enum ice_block blk, u16 vsi, u16 vsig)
+{
+       struct ice_chs_chg *tmp, *del;
+       struct LIST_HEAD_TYPE chg;
+       enum ice_status status;
+
+       /* if target VSIG is default the move is invalid */
+       if ((vsig & ICE_VSIG_IDX_M) == ICE_DEFAULT_VSIG)
+               return ICE_ERR_PARAM;
+
+       INIT_LIST_HEAD(&chg);
+
+       /* move VSI to the VSIG that matches */
+       status = ice_move_vsi(hw, blk, vsi, vsig, &chg);
+       /* update hardware if success */
+       if (!status)
+               status = ice_upd_prof_hw(hw, blk, &chg);
+
+       LIST_FOR_EACH_ENTRY_SAFE(del, tmp, &chg, ice_chs_chg, list_entry) {
+               LIST_DEL(&del->list_entry);
+               ice_free(hw, del);
+       }
+
+       return status;
+}
+
 /**
  * ice_add_prof_id_flow - add profile flow
  * @hw: pointer to the HW struct