net/i40e: fix flow director config after flow validate
[dpdk.git] / drivers / net / i40e / i40e_fdir.c
index b61e605..da089ba 100644 (file)
@@ -11,7 +11,7 @@
 #include <stdarg.h>
 
 #include <rte_ether.h>
-#include <rte_ethdev_driver.h>
+#include <ethdev_driver.h>
 #include <rte_log.h>
 #include <rte_memzone.h>
 #include <rte_malloc.h>
@@ -116,7 +116,7 @@ i40e_fdir_rx_queue_init(struct i40e_rx_queue *rxq)
 #endif
        rx_ctx.dtype = i40e_header_split_none;
        rx_ctx.hsplit_0 = I40E_HEADER_SPLIT_NONE;
-       rx_ctx.rxmax = RTE_ETHER_MAX_LEN;
+       rx_ctx.rxmax = I40E_ETH_MAX_LEN;
        rx_ctx.tphrdesc_ena = 1;
        rx_ctx.tphwdesc_ena = 1;
        rx_ctx.tphdata_ena = 1;
@@ -355,6 +355,7 @@ i40e_init_flx_pld(struct i40e_pf *pf)
                        I40E_PRTQF_FLX_PIT(index + 1), 0x0000FC29);/*non-used*/
                I40E_WRITE_REG(hw,
                        I40E_PRTQF_FLX_PIT(index + 2), 0x0000FC2A);/*non-used*/
+               pf->fdir.flex_pit_flag[i] = 0;
        }
 
        /* initialize the masks */
@@ -1442,6 +1443,228 @@ i40e_fdir_entry_pool_put(struct i40e_fdir_info *fdir_info,
        rte_bitmap_set(fdir_info->fdir_flow_pool.bitmap, f->idx);
 }
 
+static int
+i40e_flow_store_flex_pit(struct i40e_pf *pf,
+                        struct i40e_fdir_flex_pit *flex_pit,
+                        enum i40e_flxpld_layer_idx layer_idx,
+                        uint8_t raw_id)
+{
+       uint8_t field_idx;
+
+       field_idx = layer_idx * I40E_MAX_FLXPLD_FIED + raw_id;
+       /* Check if the configuration is conflicted */
+       if (pf->fdir.flex_pit_flag[layer_idx] &&
+           (pf->fdir.flex_set[field_idx].src_offset != flex_pit->src_offset ||
+            pf->fdir.flex_set[field_idx].size != flex_pit->size ||
+            pf->fdir.flex_set[field_idx].dst_offset != flex_pit->dst_offset))
+               return -1;
+
+       /* Check if the configuration exists. */
+       if (pf->fdir.flex_pit_flag[layer_idx] &&
+           (pf->fdir.flex_set[field_idx].src_offset == flex_pit->src_offset &&
+            pf->fdir.flex_set[field_idx].size == flex_pit->size &&
+            pf->fdir.flex_set[field_idx].dst_offset == flex_pit->dst_offset))
+               return 1;
+
+       pf->fdir.flex_set[field_idx].src_offset =
+               flex_pit->src_offset;
+       pf->fdir.flex_set[field_idx].size =
+               flex_pit->size;
+       pf->fdir.flex_set[field_idx].dst_offset =
+               flex_pit->dst_offset;
+
+       return 0;
+}
+
+static void
+i40e_flow_set_fdir_flex_pit(struct i40e_pf *pf,
+                           enum i40e_flxpld_layer_idx layer_idx,
+                           uint8_t raw_id)
+{
+       struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+       uint32_t flx_pit, flx_ort;
+       uint16_t min_next_off = 0;
+       uint8_t field_idx;
+       uint8_t i;
+
+       if (raw_id) {
+               flx_ort = (1 << I40E_GLQF_ORT_FLX_PAYLOAD_SHIFT) |
+                         (raw_id << I40E_GLQF_ORT_FIELD_CNT_SHIFT) |
+                         (layer_idx * I40E_MAX_FLXPLD_FIED);
+               I40E_WRITE_GLB_REG(hw, I40E_GLQF_ORT(33 + layer_idx), flx_ort);
+       }
+
+       /* Set flex pit */
+       for (i = 0; i < raw_id; i++) {
+               field_idx = layer_idx * I40E_MAX_FLXPLD_FIED + i;
+               flx_pit = MK_FLX_PIT(pf->fdir.flex_set[field_idx].src_offset,
+                                    pf->fdir.flex_set[field_idx].size,
+                                    pf->fdir.flex_set[field_idx].dst_offset);
+
+               I40E_WRITE_REG(hw, I40E_PRTQF_FLX_PIT(field_idx), flx_pit);
+               min_next_off = pf->fdir.flex_set[field_idx].src_offset +
+                       pf->fdir.flex_set[field_idx].size;
+       }
+
+       for (; i < I40E_MAX_FLXPLD_FIED; i++) {
+               /* set the non-used register obeying register's constrain */
+               field_idx = layer_idx * I40E_MAX_FLXPLD_FIED + i;
+               flx_pit = MK_FLX_PIT(min_next_off, NONUSE_FLX_PIT_FSIZE,
+                                    NONUSE_FLX_PIT_DEST_OFF);
+               I40E_WRITE_REG(hw, I40E_PRTQF_FLX_PIT(field_idx), flx_pit);
+               min_next_off++;
+       }
+}
+
+static int
+i40e_flow_store_flex_mask(struct i40e_pf *pf,
+                         enum i40e_filter_pctype pctype,
+                         uint8_t *mask)
+{
+       struct i40e_fdir_flex_mask flex_mask;
+       uint8_t nb_bitmask = 0;
+       uint16_t mask_tmp;
+       uint8_t i;
+
+       memset(&flex_mask, 0, sizeof(struct i40e_fdir_flex_mask));
+       for (i = 0; i < I40E_FDIR_MAX_FLEX_LEN; i += sizeof(uint16_t)) {
+               mask_tmp = I40E_WORD(mask[i], mask[i + 1]);
+               if (mask_tmp) {
+                       flex_mask.word_mask |=
+                               I40E_FLEX_WORD_MASK(i / sizeof(uint16_t));
+                       if (mask_tmp != UINT16_MAX) {
+                               flex_mask.bitmask[nb_bitmask].mask = ~mask_tmp;
+                               flex_mask.bitmask[nb_bitmask].offset =
+                                       i / sizeof(uint16_t);
+                               nb_bitmask++;
+                               if (nb_bitmask > I40E_FDIR_BITMASK_NUM_WORD)
+                                       return -1;
+                       }
+               }
+       }
+       flex_mask.nb_bitmask = nb_bitmask;
+
+       if (pf->fdir.flex_mask_flag[pctype] &&
+           (memcmp(&flex_mask, &pf->fdir.flex_mask[pctype],
+                   sizeof(struct i40e_fdir_flex_mask))))
+               return -2;
+       else if (pf->fdir.flex_mask_flag[pctype] &&
+                !(memcmp(&flex_mask, &pf->fdir.flex_mask[pctype],
+                         sizeof(struct i40e_fdir_flex_mask))))
+               return 1;
+
+       memcpy(&pf->fdir.flex_mask[pctype], &flex_mask,
+              sizeof(struct i40e_fdir_flex_mask));
+       return 0;
+}
+
+static void
+i40e_flow_set_fdir_flex_msk(struct i40e_pf *pf,
+                           enum i40e_filter_pctype pctype)
+{
+       struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+       struct i40e_fdir_flex_mask *flex_mask;
+       uint32_t flxinset, fd_mask;
+       uint8_t i;
+
+       /* Set flex mask */
+       flex_mask = &pf->fdir.flex_mask[pctype];
+       flxinset = (flex_mask->word_mask <<
+                   I40E_PRTQF_FD_FLXINSET_INSET_SHIFT) &
+               I40E_PRTQF_FD_FLXINSET_INSET_MASK;
+       i40e_write_rx_ctl(hw, I40E_PRTQF_FD_FLXINSET(pctype), flxinset);
+
+       for (i = 0; i < flex_mask->nb_bitmask; i++) {
+               fd_mask = (flex_mask->bitmask[i].mask <<
+                          I40E_PRTQF_FD_MSK_MASK_SHIFT) &
+                          I40E_PRTQF_FD_MSK_MASK_MASK;
+               fd_mask |= ((flex_mask->bitmask[i].offset +
+                            I40E_FLX_OFFSET_IN_FIELD_VECTOR) <<
+                           I40E_PRTQF_FD_MSK_OFFSET_SHIFT) &
+                               I40E_PRTQF_FD_MSK_OFFSET_MASK;
+               i40e_write_rx_ctl(hw, I40E_PRTQF_FD_MSK(pctype, i), fd_mask);
+       }
+
+       pf->fdir.flex_mask_flag[pctype] = 1;
+}
+
+static int
+i40e_flow_set_fdir_inset(struct i40e_pf *pf,
+                        enum i40e_filter_pctype pctype,
+                        uint64_t input_set)
+{
+       uint32_t mask_reg[I40E_INSET_MASK_NUM_REG] = {0};
+       struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+       uint64_t inset_reg = 0;
+       int i, num;
+
+       /* Check if the input set is valid */
+       if (i40e_validate_input_set(pctype, RTE_ETH_FILTER_FDIR,
+                                   input_set) != 0) {
+               PMD_DRV_LOG(ERR, "Invalid input set");
+               return -EINVAL;
+       }
+
+       /* Check if the configuration is conflicted */
+       if (pf->fdir.inset_flag[pctype] &&
+           memcmp(&pf->fdir.input_set[pctype], &input_set, sizeof(uint64_t)))
+               return -1;
+
+       if (pf->fdir.inset_flag[pctype] &&
+           !memcmp(&pf->fdir.input_set[pctype], &input_set, sizeof(uint64_t)))
+               return 0;
+
+       num = i40e_generate_inset_mask_reg(hw, input_set, mask_reg,
+                                                I40E_INSET_MASK_NUM_REG);
+       if (num < 0)
+               return -EINVAL;
+
+       if (pf->support_multi_driver) {
+               for (i = 0; i < num; i++)
+                       if (i40e_read_rx_ctl(hw,
+                                       I40E_GLQF_FD_MSK(i, pctype)) !=
+                                       mask_reg[i]) {
+                               PMD_DRV_LOG(ERR, "Input set setting is not"
+                                               " supported with"
+                                               " `support-multi-driver`"
+                                               " enabled!");
+                               return -EPERM;
+                       }
+               for (i = num; i < I40E_INSET_MASK_NUM_REG; i++)
+                       if (i40e_read_rx_ctl(hw,
+                                       I40E_GLQF_FD_MSK(i, pctype)) != 0) {
+                               PMD_DRV_LOG(ERR, "Input set setting is not"
+                                               " supported with"
+                                               " `support-multi-driver`"
+                                               " enabled!");
+                               return -EPERM;
+                       }
+
+       } else {
+               for (i = 0; i < num; i++)
+                       i40e_check_write_reg(hw, I40E_GLQF_FD_MSK(i, pctype),
+                               mask_reg[i]);
+               /*clear unused mask registers of the pctype */
+               for (i = num; i < I40E_INSET_MASK_NUM_REG; i++)
+                       i40e_check_write_reg(hw,
+                                       I40E_GLQF_FD_MSK(i, pctype), 0);
+       }
+
+       inset_reg |= i40e_translate_input_set_reg(hw->mac.type, input_set);
+
+       i40e_check_write_reg(hw, I40E_PRTQF_FD_INSET(pctype, 0),
+                            (uint32_t)(inset_reg & UINT32_MAX));
+       i40e_check_write_reg(hw, I40E_PRTQF_FD_INSET(pctype, 1),
+                            (uint32_t)((inset_reg >>
+                                        I40E_32_BIT_WIDTH) & UINT32_MAX));
+
+       I40E_WRITE_FLUSH(hw);
+
+       pf->fdir.input_set[pctype] = input_set;
+       pf->fdir.inset_flag[pctype] = 1;
+       return 0;
+}
+
 static inline unsigned char *
 i40e_find_available_buffer(struct rte_eth_dev *dev)
 {
@@ -1494,13 +1717,19 @@ i40e_flow_add_del_fdir_filter(struct rte_eth_dev *dev,
 {
        struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
        struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
-       unsigned char *pkt = NULL;
-       enum i40e_filter_pctype pctype;
+       enum i40e_flxpld_layer_idx layer_idx = I40E_FLXPLD_L2_IDX;
        struct i40e_fdir_info *fdir_info = &pf->fdir;
-       struct i40e_fdir_filter *node;
+       uint8_t flex_mask[I40E_FDIR_MAX_FLEX_LEN];
        struct i40e_fdir_filter check_filter; /* Check if the filter exists */
+       struct i40e_fdir_flex_pit flex_pit;
+       enum i40e_filter_pctype pctype;
+       struct i40e_fdir_filter *node;
+       unsigned char *pkt = NULL;
+       bool cfg_flex_pit = true;
        bool wait_status = true;
+       uint8_t field_idx;
        int ret = 0;
+       int i;
 
        if (pf->fdir.fdir_vsi == NULL) {
                PMD_DRV_LOG(ERR, "FDIR is not enabled");
@@ -1533,6 +1762,58 @@ i40e_flow_add_del_fdir_filter(struct rte_eth_dev *dev,
        i40e_fdir_filter_convert(filter, &check_filter);
 
        if (add) {
+               if (filter->input.flow_ext.is_flex_flow) {
+                       ret = i40e_flow_set_fdir_inset(pf, pctype,
+                                       filter->input.flow_ext.input_set);
+                       if (ret == -1) {
+                               PMD_DRV_LOG(ERR, "Conflict with the"
+                                           " first rule's input set.");
+                               return -EINVAL;
+                       } else if (ret == -EINVAL) {
+                               PMD_DRV_LOG(ERR, "Invalid pattern mask.");
+                               return -EINVAL;
+                       }
+
+                       for (i = 0; i < filter->input.flow_ext.raw_id; i++) {
+                               layer_idx = filter->input.flow_ext.layer_idx;
+                               field_idx = layer_idx * I40E_MAX_FLXPLD_FIED + i;
+                               flex_pit = filter->input.flow_ext.flex_pit[field_idx];
+
+                               /* Store flex pit to SW */
+                               ret = i40e_flow_store_flex_pit(pf, &flex_pit,
+                                                              layer_idx, i);
+                               if (ret < 0) {
+                                       PMD_DRV_LOG(ERR, "Conflict with the"
+                                                   " first flexible rule.");
+                                       return -EINVAL;
+                               } else if (ret > 0) {
+                                       cfg_flex_pit = false;
+                               }
+                       }
+
+                       if (cfg_flex_pit)
+                               i40e_flow_set_fdir_flex_pit(pf, layer_idx,
+                                               filter->input.flow_ext.raw_id);
+
+                       /* Store flex mask to SW */
+                       for (i = 0; i < I40E_FDIR_MAX_FLEX_LEN; i++)
+                               flex_mask[i] =
+                                       filter->input.flow_ext.flex_mask[i];
+
+                       ret = i40e_flow_store_flex_mask(pf, pctype, flex_mask);
+                       if (ret == -1) {
+                               PMD_DRV_LOG(ERR, "Exceed maximal"
+                                           " number of bitmasks");
+                               return -EINVAL;
+                       } else if (ret == -2) {
+                               PMD_DRV_LOG(ERR, "Conflict with the"
+                                           " first flexible rule");
+                               return -EINVAL;
+                       } else if (ret == 0) {
+                               i40e_flow_set_fdir_flex_msk(pf, pctype);
+                       }
+               }
+
                ret = i40e_sw_fdir_filter_insert(pf, &check_filter);
                if (ret < 0) {
                        PMD_DRV_LOG(ERR,
@@ -1544,6 +1825,9 @@ i40e_flow_add_del_fdir_filter(struct rte_eth_dev *dev,
                                fdir_info->fdir_guarantee_free_space > 0)
                        wait_status = false;
        } else {
+               if (filter->input.flow_ext.is_flex_flow)
+                       layer_idx = filter->input.flow_ext.layer_idx;
+
                node = i40e_sw_fdir_filter_lookup(fdir_info,
                                &check_filter.fdir.input);
                if (!node) {
@@ -1591,6 +1875,17 @@ i40e_flow_add_del_fdir_filter(struct rte_eth_dev *dev,
                goto error_op;
        }
 
+       if (filter->input.flow_ext.is_flex_flow) {
+               if (add) {
+                       fdir_info->flex_flow_count[layer_idx]++;
+                       pf->fdir.flex_pit_flag[layer_idx] = 1;
+               } else {
+                       fdir_info->flex_flow_count[layer_idx]--;
+                       if (!fdir_info->flex_flow_count[layer_idx])
+                               pf->fdir.flex_pit_flag[layer_idx] = 0;
+               }
+       }
+
        if (add) {
                fdir_info->fdir_actual_cnt++;
                if (fdir_info->fdir_invalprio == 1 &&