net/mlx5: support query of age action
[dpdk.git] / drivers / net / mlx5 / mlx5_flow_dv.c
index b61a361..d3a3f23 100644 (file)
@@ -1908,6 +1908,120 @@ flow_dv_validate_item_ipv4(const struct rte_flow_item *item,
        return 0;
 }
 
+/**
+ * Validate IPV6 fragment extension item.
+ *
+ * @param[in] item
+ *   Item specification.
+ * @param[in] item_flags
+ *   Bit-fields that holds the items detected until now.
+ * @param[out] error
+ *   Pointer to error structure.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+flow_dv_validate_item_ipv6_frag_ext(const struct rte_flow_item *item,
+                                   uint64_t item_flags,
+                                   struct rte_flow_error *error)
+{
+       const struct rte_flow_item_ipv6_frag_ext *spec = item->spec;
+       const struct rte_flow_item_ipv6_frag_ext *last = item->last;
+       const struct rte_flow_item_ipv6_frag_ext *mask = item->mask;
+       rte_be16_t frag_data_spec = 0;
+       rte_be16_t frag_data_last = 0;
+       const int tunnel = !!(item_flags & MLX5_FLOW_LAYER_TUNNEL);
+       const uint64_t l4m = tunnel ? MLX5_FLOW_LAYER_INNER_L4 :
+                                     MLX5_FLOW_LAYER_OUTER_L4;
+       int ret = 0;
+       struct rte_flow_item_ipv6_frag_ext nic_mask = {
+               .hdr = {
+                       .next_header = 0xff,
+                       .frag_data = RTE_BE16(0xffff),
+               },
+       };
+
+       if (item_flags & l4m)
+               return rte_flow_error_set(error, EINVAL,
+                                         RTE_FLOW_ERROR_TYPE_ITEM, item,
+                                         "ipv6 fragment extension item cannot "
+                                         "follow L4 item.");
+       if ((tunnel && !(item_flags & MLX5_FLOW_LAYER_INNER_L3_IPV6)) ||
+           (!tunnel && !(item_flags & MLX5_FLOW_LAYER_OUTER_L3_IPV6)))
+               return rte_flow_error_set(error, EINVAL,
+                                         RTE_FLOW_ERROR_TYPE_ITEM, item,
+                                         "ipv6 fragment extension item must "
+                                         "follow ipv6 item");
+       if (spec && mask)
+               frag_data_spec = spec->hdr.frag_data & mask->hdr.frag_data;
+       if (!frag_data_spec)
+               return 0;
+       /*
+        * spec and mask are valid, enforce using full mask to make sure the
+        * complete value is used correctly.
+        */
+       if ((mask->hdr.frag_data & RTE_BE16(RTE_IPV6_FRAG_USED_MASK)) !=
+                               RTE_BE16(RTE_IPV6_FRAG_USED_MASK))
+               return rte_flow_error_set(error, EINVAL,
+                                         RTE_FLOW_ERROR_TYPE_ITEM_MASK,
+                                         item, "must use full mask for"
+                                         " frag_data");
+       /*
+        * Match on frag_data 0x00001 means M is 1 and frag-offset is 0.
+        * This is 1st fragment of fragmented packet.
+        */
+       if (frag_data_spec == RTE_BE16(RTE_IPV6_EHDR_MF_MASK))
+               return rte_flow_error_set(error, ENOTSUP,
+                                         RTE_FLOW_ERROR_TYPE_ITEM, item,
+                                         "match on first fragment not "
+                                         "supported");
+       if (frag_data_spec && !last)
+               return rte_flow_error_set(error, EINVAL,
+                                         RTE_FLOW_ERROR_TYPE_ITEM, item,
+                                         "specified value not supported");
+       ret = mlx5_flow_item_acceptable
+                               (item, (const uint8_t *)mask,
+                                (const uint8_t *)&nic_mask,
+                                sizeof(struct rte_flow_item_ipv6_frag_ext),
+                                MLX5_ITEM_RANGE_ACCEPTED, error);
+       if (ret)
+               return ret;
+       /* spec and last are valid, validate the specified range. */
+       frag_data_last = last->hdr.frag_data & mask->hdr.frag_data;
+       /*
+        * Match on frag_data spec 0x0009 and last 0xfff9
+        * means M is 1 and frag-offset is > 0.
+        * This packet is fragment 2nd and onward, excluding last.
+        * This is not yet supported in MLX5, return appropriate
+        * error message.
+        */
+       if (frag_data_spec == RTE_BE16(RTE_IPV6_EHDR_FO_ALIGN |
+                                      RTE_IPV6_EHDR_MF_MASK) &&
+           frag_data_last == RTE_BE16(RTE_IPV6_FRAG_USED_MASK))
+               return rte_flow_error_set(error, ENOTSUP,
+                                         RTE_FLOW_ERROR_TYPE_ITEM_LAST,
+                                         last, "match on following "
+                                         "fragments not supported");
+       /*
+        * Match on frag_data spec 0x0008 and last 0xfff8
+        * means M is 0 and frag-offset is > 0.
+        * This packet is last fragment of fragmented packet.
+        * This is not yet supported in MLX5, return appropriate
+        * error message.
+        */
+       if (frag_data_spec == RTE_BE16(RTE_IPV6_EHDR_FO_ALIGN) &&
+           frag_data_last == RTE_BE16(RTE_IPV6_EHDR_FO_MASK))
+               return rte_flow_error_set(error, ENOTSUP,
+                                         RTE_FLOW_ERROR_TYPE_ITEM_LAST,
+                                         last, "match on last "
+                                         "fragment not supported");
+       /* Other range values are invalid and rejected. */
+       return rte_flow_error_set(error, EINVAL,
+                                 RTE_FLOW_ERROR_TYPE_ITEM_LAST, last,
+                                 "specified range not supported");
+}
+
 /**
  * Validate the pop VLAN action.
  *
@@ -4065,14 +4179,14 @@ flow_dv_validate_action_age(uint64_t action_flags,
                return rte_flow_error_set(error, EINVAL,
                                          RTE_FLOW_ERROR_TYPE_ACTION, action,
                                          "configuration cannot be null");
-       if (age->timeout >= UINT16_MAX / 2 / 10)
-               return rte_flow_error_set(error, ENOTSUP,
+       if (!(age->timeout))
+               return rte_flow_error_set(error, EINVAL,
                                          RTE_FLOW_ERROR_TYPE_ACTION, action,
-                                         "Max age time: 3275 seconds");
+                                         "invalid timeout value 0");
        if (action_flags & MLX5_FLOW_ACTION_AGE)
                return rte_flow_error_set(error, EINVAL,
                                          RTE_FLOW_ERROR_TYPE_ACTION, NULL,
-                                         "Duplicate age ctions set");
+                                         "duplicate age actions set");
        return 0;
 }
 
@@ -4812,6 +4926,7 @@ flow_dv_pool_create(struct rte_eth_dev *dev, struct mlx5_devx_obj *dcs,
        TAILQ_INIT(&pool->counters[1]);
        TAILQ_INSERT_HEAD(&cont->pool_list, pool, next);
        pool->index = n_valid;
+       pool->time_of_last_age_check = MLX5_CURR_TIME_SEC;
        cont->pools[n_valid] = pool;
        if (!batch) {
                int base = RTE_ALIGN_FLOOR(dcs->id, MLX5_COUNTERS_PER_POOL);
@@ -4934,7 +5049,7 @@ retry:
                               (MLX5_CNT_CONTAINER
                               (priv->sh, batch, (age ^ 0x1)), dcs->id);
                        /*
-                        * Pool eixsts, counter will be added to the other
+                        * Pool exists, counter will be added to the other
                         * container, need to reallocate it later.
                         */
                        if (pool) {
@@ -5215,13 +5330,13 @@ flow_dv_counter_remove_from_age(struct rte_eth_dev *dev,
        struct mlx5_age_info *age_info;
        struct mlx5_age_param *age_param;
        struct mlx5_priv *priv = dev->data->dev_private;
+       uint16_t expected = AGE_CANDIDATE;
 
        age_info = GET_PORT_AGE_INFO(priv);
        age_param = flow_dv_counter_idx_get_age(dev, counter);
-       if (rte_atomic16_cmpset((volatile uint16_t *)
-                       &age_param->state,
-                       AGE_CANDIDATE, AGE_FREE)
-                       != AGE_CANDIDATE) {
+       if (!__atomic_compare_exchange_n(&age_param->state, &expected,
+                                        AGE_FREE, false, __ATOMIC_RELAXED,
+                                        __ATOMIC_RELAXED)) {
                /**
                 * We need the lock even it is age timeout,
                 * since counter may still in process.
@@ -5229,9 +5344,10 @@ flow_dv_counter_remove_from_age(struct rte_eth_dev *dev,
                rte_spinlock_lock(&age_info->aged_sl);
                TAILQ_REMOVE(&age_info->aged_counters, cnt, next);
                rte_spinlock_unlock(&age_info->aged_sl);
+               __atomic_store_n(&age_param->state, AGE_FREE, __ATOMIC_RELAXED);
        }
-       rte_atomic16_set(&age_param->state, AGE_FREE);
 }
+
 /**
  * Release a flow counter.
  *
@@ -5555,6 +5671,29 @@ flow_dv_validate(struct rte_eth_dev *dev, const struct rte_flow_attr *attr,
                                next_protocol = 0xff;
                        }
                        break;
+               case RTE_FLOW_ITEM_TYPE_IPV6_FRAG_EXT:
+                       ret = flow_dv_validate_item_ipv6_frag_ext(items,
+                                                                 item_flags,
+                                                                 error);
+                       if (ret < 0)
+                               return ret;
+                       last_item = tunnel ?
+                                       MLX5_FLOW_LAYER_INNER_L3_IPV6_FRAG_EXT :
+                                       MLX5_FLOW_LAYER_OUTER_L3_IPV6_FRAG_EXT;
+                       if (items->mask != NULL &&
+                           ((const struct rte_flow_item_ipv6_frag_ext *)
+                            items->mask)->hdr.next_header) {
+                               next_protocol =
+                               ((const struct rte_flow_item_ipv6_frag_ext *)
+                                items->spec)->hdr.next_header;
+                               next_protocol &=
+                               ((const struct rte_flow_item_ipv6_frag_ext *)
+                                items->mask)->hdr.next_header;
+                       } else {
+                               /* Reset for inner layer. */
+                               next_protocol = 0xff;
+                       }
+                       break;
                case RTE_FLOW_ITEM_TYPE_TCP:
                        ret = mlx5_flow_validate_item_tcp
                                                (items, item_flags,
@@ -6741,6 +6880,57 @@ flow_dv_translate_item_ipv6(void *matcher, void *key,
                 !!(ipv6_v->has_frag_ext & ipv6_m->has_frag_ext));
 }
 
+/**
+ * Add IPV6 fragment extension item to matcher and to the value.
+ *
+ * @param[in, out] matcher
+ *   Flow matcher.
+ * @param[in, out] key
+ *   Flow matcher value.
+ * @param[in] item
+ *   Flow pattern to translate.
+ * @param[in] inner
+ *   Item is inner pattern.
+ */
+static void
+flow_dv_translate_item_ipv6_frag_ext(void *matcher, void *key,
+                                    const struct rte_flow_item *item,
+                                    int inner)
+{
+       const struct rte_flow_item_ipv6_frag_ext *ipv6_frag_ext_m = item->mask;
+       const struct rte_flow_item_ipv6_frag_ext *ipv6_frag_ext_v = item->spec;
+       const struct rte_flow_item_ipv6_frag_ext nic_mask = {
+               .hdr = {
+                       .next_header = 0xff,
+                       .frag_data = RTE_BE16(0xffff),
+               },
+       };
+       void *headers_m;
+       void *headers_v;
+
+       if (inner) {
+               headers_m = MLX5_ADDR_OF(fte_match_param, matcher,
+                                        inner_headers);
+               headers_v = MLX5_ADDR_OF(fte_match_param, key, inner_headers);
+       } else {
+               headers_m = MLX5_ADDR_OF(fte_match_param, matcher,
+                                        outer_headers);
+               headers_v = MLX5_ADDR_OF(fte_match_param, key, outer_headers);
+       }
+       /* IPv6 fragment extension item exists, so packet is IP fragment. */
+       MLX5_SET(fte_match_set_lyr_2_4, headers_m, frag, 1);
+       MLX5_SET(fte_match_set_lyr_2_4, headers_v, frag, 1);
+       if (!ipv6_frag_ext_v)
+               return;
+       if (!ipv6_frag_ext_m)
+               ipv6_frag_ext_m = &nic_mask;
+       MLX5_SET(fte_match_set_lyr_2_4, headers_m, ip_protocol,
+                ipv6_frag_ext_m->hdr.next_header);
+       MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol,
+                ipv6_frag_ext_v->hdr.next_header &
+                ipv6_frag_ext_m->hdr.next_header);
+}
+
 /**
  * Add TCP item to matcher and to the value.
  *
@@ -8344,22 +8534,12 @@ flow_dv_translate_create_counter(struct rte_eth_dev *dev,
        if (!counter || age == NULL)
                return counter;
        age_param  = flow_dv_counter_idx_get_age(dev, counter);
-       /*
-        * The counter age accuracy may have a bit delay. Have 3/4
-        * second bias on the timeount in order to let it age in time.
-        */
        age_param->context = age->context ? age->context :
                (void *)(uintptr_t)(dev_flow->flow_idx);
-       /*
-        * The counter age accuracy may have a bit delay. Have 3/4
-        * second bias on the timeount in order to let it age in time.
-        */
-       age_param->timeout = age->timeout * 10 - MLX5_AGING_TIME_DELAY;
-       /* Set expire time in unit of 0.1 sec. */
+       age_param->timeout = age->timeout;
        age_param->port_id = dev->data->port_id;
-       age_param->expire = age_param->timeout +
-                       rte_rdtsc() / (rte_get_tsc_hz() / 10);
-       rte_atomic16_set(&age_param->state, AGE_CANDIDATE);
+       __atomic_store_n(&age_param->sec_since_last_hit, 0, __ATOMIC_RELAXED);
+       __atomic_store_n(&age_param->state, AGE_CANDIDATE, __ATOMIC_RELAXED);
        return counter;
 }
 /**
@@ -9844,6 +10024,27 @@ __flow_dv_translate(struct rte_eth_dev *dev,
                                next_protocol = 0xff;
                        }
                        break;
+               case RTE_FLOW_ITEM_TYPE_IPV6_FRAG_EXT:
+                       flow_dv_translate_item_ipv6_frag_ext(match_mask,
+                                                            match_value,
+                                                            items, tunnel);
+                       last_item = tunnel ?
+                                       MLX5_FLOW_LAYER_INNER_L3_IPV6_FRAG_EXT :
+                                       MLX5_FLOW_LAYER_OUTER_L3_IPV6_FRAG_EXT;
+                       if (items->mask != NULL &&
+                           ((const struct rte_flow_item_ipv6_frag_ext *)
+                            items->mask)->hdr.next_header) {
+                               next_protocol =
+                               ((const struct rte_flow_item_ipv6_frag_ext *)
+                                items->spec)->hdr.next_header;
+                               next_protocol &=
+                               ((const struct rte_flow_item_ipv6_frag_ext *)
+                                items->mask)->hdr.next_header;
+                       } else {
+                               /* Reset for inner layer. */
+                               next_protocol = 0xff;
+                       }
+                       break;
                case RTE_FLOW_ITEM_TYPE_TCP:
                        flow_dv_translate_item_tcp(match_mask, match_value,
                                                   items, tunnel);
@@ -10746,6 +10947,52 @@ flow_dv_query_count(struct rte_eth_dev *dev, struct rte_flow *flow,
                                  "counters are not available");
 }
 
+/**
+ * Query a flow rule AGE action for aging information.
+ *
+ * @param[in] dev
+ *   Pointer to Ethernet device.
+ * @param[in] flow
+ *   Pointer to the sub flow.
+ * @param[out] data
+ *   data retrieved by the query.
+ * @param[out] error
+ *   Perform verbose error reporting if not NULL.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+static int
+flow_dv_query_age(struct rte_eth_dev *dev, struct rte_flow *flow,
+                 void *data, struct rte_flow_error *error)
+{
+       struct rte_flow_query_age *resp = data;
+
+       if (flow->counter) {
+               struct mlx5_age_param *age_param =
+                               flow_dv_counter_idx_get_age(dev, flow->counter);
+
+               if (!age_param || !age_param->timeout)
+                       return rte_flow_error_set
+                                       (error, EINVAL,
+                                        RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+                                        NULL, "cannot read age data");
+               resp->aged = __atomic_load_n(&age_param->state,
+                                            __ATOMIC_RELAXED) ==
+                                                       AGE_TMOUT ? 1 : 0;
+               resp->sec_since_last_hit_valid = !resp->aged;
+               if (resp->sec_since_last_hit_valid)
+                       resp->sec_since_last_hit =
+                               __atomic_load_n(&age_param->sec_since_last_hit,
+                                               __ATOMIC_RELAXED);
+               return 0;
+       }
+       return rte_flow_error_set(error, EINVAL,
+                                 RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+                                 NULL,
+                                 "age data not available");
+}
+
 /**
  * Query a flow.
  *
@@ -10768,6 +11015,9 @@ flow_dv_query(struct rte_eth_dev *dev,
                case RTE_FLOW_ACTION_TYPE_COUNT:
                        ret = flow_dv_query_count(dev, flow, data, error);
                        break;
+               case RTE_FLOW_ACTION_TYPE_AGE:
+                       ret = flow_dv_query_age(dev, flow, data, error);
+                       break;
                default:
                        return rte_flow_error_set(error, ENOTSUP,
                                                  RTE_FLOW_ERROR_TYPE_ACTION,