+static inline void
+i40e_fdir_programming_status_cleanup(struct i40e_rx_queue *rxq)
+{
+ uint16_t retry_count = 0;
+
+ /* capture the previous error report(if any) from rx ring */
+ while ((i40e_check_fdir_programming_status(rxq) < 0) &&
+ (++retry_count < I40E_FDIR_NUM_RX_DESC))
+ PMD_DRV_LOG(INFO, "error report captured.");
+}
+
+static int
+i40e_fdir_filter_convert(const struct i40e_fdir_filter_conf *input,
+ struct i40e_fdir_filter *filter)
+{
+ rte_memcpy(&filter->fdir, input, sizeof(struct i40e_fdir_filter_conf));
+ if (input->input.flow_ext.pkt_template) {
+ filter->fdir.input.flow.raw_flow.packet = NULL;
+ filter->fdir.input.flow.raw_flow.length =
+ rte_hash_crc(input->input.flow.raw_flow.packet,
+ input->input.flow.raw_flow.length,
+ input->input.flow.raw_flow.pctype);
+ }
+ return 0;
+}
+
+/* Check if there exists the flow director filter */
+static struct i40e_fdir_filter *
+i40e_sw_fdir_filter_lookup(struct i40e_fdir_info *fdir_info,
+ const struct i40e_fdir_input *input)
+{
+ int ret;
+
+ if (input->flow_ext.pkt_template)
+ ret = rte_hash_lookup_with_hash(fdir_info->hash_table,
+ (const void *)input,
+ input->flow.raw_flow.length);
+ else
+ ret = rte_hash_lookup(fdir_info->hash_table,
+ (const void *)input);
+ if (ret < 0)
+ return NULL;
+
+ return fdir_info->hash_map[ret];
+}
+
+/* Add a flow director filter into the SW list */
+static int
+i40e_sw_fdir_filter_insert(struct i40e_pf *pf, struct i40e_fdir_filter *filter)
+{
+ struct i40e_fdir_info *fdir_info = &pf->fdir;
+ struct i40e_fdir_filter *hash_filter;
+ int ret;
+
+ if (filter->fdir.input.flow_ext.pkt_template)
+ ret = rte_hash_add_key_with_hash(fdir_info->hash_table,
+ &filter->fdir.input,
+ filter->fdir.input.flow.raw_flow.length);
+ else
+ ret = rte_hash_add_key(fdir_info->hash_table,
+ &filter->fdir.input);
+ if (ret < 0) {
+ PMD_DRV_LOG(ERR,
+ "Failed to insert fdir filter to hash table %d!",
+ ret);
+ return ret;
+ }
+
+ if (fdir_info->hash_map[ret])
+ return -1;
+
+ hash_filter = &fdir_info->fdir_filter_array[ret];
+ rte_memcpy(hash_filter, filter, sizeof(*filter));
+ fdir_info->hash_map[ret] = hash_filter;
+ TAILQ_INSERT_TAIL(&fdir_info->fdir_list, hash_filter, rules);
+
+ return 0;
+}
+
+/* Delete a flow director filter from the SW list */
+int
+i40e_sw_fdir_filter_del(struct i40e_pf *pf, struct i40e_fdir_input *input)
+{
+ struct i40e_fdir_info *fdir_info = &pf->fdir;
+ struct i40e_fdir_filter *filter;
+ int ret;
+
+ if (input->flow_ext.pkt_template)
+ ret = rte_hash_del_key_with_hash(fdir_info->hash_table,
+ input,
+ input->flow.raw_flow.length);
+ else
+ ret = rte_hash_del_key(fdir_info->hash_table, input);
+ if (ret < 0) {
+ PMD_DRV_LOG(ERR,
+ "Failed to delete fdir filter to hash table %d!",
+ ret);
+ return ret;
+ }
+ filter = fdir_info->hash_map[ret];
+ fdir_info->hash_map[ret] = NULL;
+
+ TAILQ_REMOVE(&fdir_info->fdir_list, filter, rules);
+
+ return 0;
+}
+
+struct rte_flow *
+i40e_fdir_entry_pool_get(struct i40e_fdir_info *fdir_info)
+{
+ struct rte_flow *flow = NULL;
+ uint64_t slab = 0;
+ uint32_t pos = 0;
+ uint32_t i = 0;
+ int ret;
+
+ if (fdir_info->fdir_actual_cnt >=
+ fdir_info->fdir_space_size) {
+ PMD_DRV_LOG(ERR, "Fdir space full");
+ return NULL;
+ }
+
+ ret = rte_bitmap_scan(fdir_info->fdir_flow_pool.bitmap, &pos,
+ &slab);
+
+ /* normally this won't happen as the fdir_actual_cnt should be
+ * same with the number of the set bits in fdir_flow_pool,
+ * but anyway handle this error condition here for safe
+ */
+ if (ret == 0) {
+ PMD_DRV_LOG(ERR, "fdir_actual_cnt out of sync");
+ return NULL;
+ }
+
+ i = rte_bsf64(slab);
+ pos += i;
+ rte_bitmap_clear(fdir_info->fdir_flow_pool.bitmap, pos);
+ flow = &fdir_info->fdir_flow_pool.pool[pos].flow;
+
+ memset(flow, 0, sizeof(struct rte_flow));
+
+ return flow;
+}
+
+void
+i40e_fdir_entry_pool_put(struct i40e_fdir_info *fdir_info,
+ struct rte_flow *flow)
+{
+ struct i40e_fdir_entry *f;
+
+ f = FLOW_TO_FLOW_BITMAP(flow);
+ 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 inline unsigned char *
+i40e_find_available_buffer(struct rte_eth_dev *dev)
+{
+ struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+ struct i40e_fdir_info *fdir_info = &pf->fdir;
+ struct i40e_tx_queue *txq = pf->fdir.txq;
+
+ /* no available buffer
+ * search for more available buffers from the current
+ * descriptor, until an unavailable one
+ */
+ if (fdir_info->txq_available_buf_count <= 0) {
+ uint16_t tmp_tail;
+ volatile struct i40e_tx_desc *tmp_txdp;
+
+ tmp_tail = txq->tx_tail;
+ tmp_txdp = &txq->tx_ring[tmp_tail + 1];
+
+ do {
+ if ((tmp_txdp->cmd_type_offset_bsz &
+ rte_cpu_to_le_64(I40E_TXD_QW1_DTYPE_MASK)) ==
+ rte_cpu_to_le_64(I40E_TX_DESC_DTYPE_DESC_DONE))
+ fdir_info->txq_available_buf_count++;
+ else
+ break;
+
+ tmp_tail += 2;
+ if (tmp_tail >= txq->nb_tx_desc)
+ tmp_tail = 0;
+ } while (tmp_tail != txq->tx_tail);
+ }
+
+ if (fdir_info->txq_available_buf_count > 0)
+ fdir_info->txq_available_buf_count--;
+ else
+ return NULL;
+ return (unsigned char *)fdir_info->prg_pkt[txq->tx_tail >> 1];
+}
+
+/**
+ * i40e_flow_add_del_fdir_filter - add or remove a flow director filter.