When composing device command for modify header action, provided mask
should be taken more accurate into account thus length and offset
in action should be set accordingly at precise bit-wise boundaries.

For the future use, metadata register copy action is also added.

Signed-off-by: Yongseok Koh <ys...@mellanox.com>
Signed-off-by: Viacheslav Ovsiienko <viachesl...@mellanox.com>
Acked-by: Matan Azrad <ma...@mellanox.com>
---
 drivers/net/mlx5/mlx5_flow_dv.c | 150 ++++++++++++++++++++++++++++++----------
 drivers/net/mlx5/mlx5_prm.h     |  18 +++--
 2 files changed, 128 insertions(+), 40 deletions(-)

diff --git a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c
index 42c265f..6a3850a 100644
--- a/drivers/net/mlx5/mlx5_flow_dv.c
+++ b/drivers/net/mlx5/mlx5_flow_dv.c
@@ -240,12 +240,62 @@ struct field_modify_info modify_tcp[] = {
 }
 
 /**
+ * Fetch 1, 2, 3 or 4 byte field from the byte array
+ * and return as unsigned integer in host-endian format.
+ *
+ * @param[in] data
+ *   Pointer to data array.
+ * @param[in] size
+ *   Size of field to extract.
+ *
+ * @return
+ *   converted field in host endian format.
+ */
+static inline uint32_t
+flow_dv_fetch_field(const uint8_t *data, uint32_t size)
+{
+       uint32_t ret;
+
+       switch (size) {
+       case 1:
+               ret = *data;
+               break;
+       case 2:
+               ret = rte_be_to_cpu_16(*(const unaligned_uint16_t *)data);
+               break;
+       case 3:
+               ret = rte_be_to_cpu_16(*(const unaligned_uint16_t *)data);
+               ret = (ret << 8) | *(data + sizeof(uint16_t));
+               break;
+       case 4:
+               ret = rte_be_to_cpu_32(*(const unaligned_uint32_t *)data);
+               break;
+       default:
+               assert(false);
+               ret = 0;
+               break;
+       }
+       return ret;
+}
+
+/**
  * Convert modify-header action to DV specification.
  *
+ * Data length of each action is determined by provided field description
+ * and the item mask. Data bit offset and width of each action is determined
+ * by provided item mask.
+ *
  * @param[in] item
  *   Pointer to item specification.
  * @param[in] field
  *   Pointer to field modification information.
+ *     For MLX5_MODIFICATION_TYPE_SET specifies destination field.
+ *     For MLX5_MODIFICATION_TYPE_ADD specifies destination field.
+ *     For MLX5_MODIFICATION_TYPE_COPY specifies source field.
+ * @param[in] dcopy
+ *   Destination field info for MLX5_MODIFICATION_TYPE_COPY in @type.
+ *   Negative offset value sets the same offset as source offset.
+ *   size field is ignored, value is taken from source field.
  * @param[in,out] resource
  *   Pointer to the modify-header resource.
  * @param[in] type
@@ -259,38 +309,68 @@ struct field_modify_info modify_tcp[] = {
 static int
 flow_dv_convert_modify_action(struct rte_flow_item *item,
                              struct field_modify_info *field,
+                             struct field_modify_info *dcopy,
                              struct mlx5_flow_dv_modify_hdr_resource *resource,
-                             uint32_t type,
-                             struct rte_flow_error *error)
+                             uint32_t type, struct rte_flow_error *error)
 {
        uint32_t i = resource->actions_num;
        struct mlx5_modification_cmd *actions = resource->actions;
-       const uint8_t *spec = item->spec;
-       const uint8_t *mask = item->mask;
-       uint32_t set;
-
-       while (field->size) {
-               set = 0;
-               /* Generate modify command for each mask segment. */
-               memcpy(&set, &mask[field->offset], field->size);
-               if (set) {
-                       if (i >= MLX5_MODIFY_NUM)
-                               return rte_flow_error_set(error, EINVAL,
-                                        RTE_FLOW_ERROR_TYPE_ACTION, NULL,
-                                        "too many items to modify");
-                       actions[i].action_type = type;
-                       actions[i].field = field->id;
-                       actions[i].length = field->size ==
-                                       4 ? 0 : field->size * 8;
-                       rte_memcpy(&actions[i].data[4 - field->size],
-                                  &spec[field->offset], field->size);
-                       actions[i].data0 = rte_cpu_to_be_32(actions[i].data0);
-                       ++i;
+
+       /*
+        * The item and mask are provided in big-endian format.
+        * The fields should be presented as in big-endian format either.
+        * Mask must be always present, it defines the actual field width.
+        */
+       assert(item->mask);
+       assert(field->size);
+       do {
+               unsigned int size_b;
+               unsigned int off_b;
+               uint32_t mask;
+               uint32_t data;
+
+               if (i >= MLX5_MODIFY_NUM)
+                       return rte_flow_error_set(error, EINVAL,
+                                RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+                                "too many items to modify");
+               /* Fetch variable byte size mask from the array. */
+               mask = flow_dv_fetch_field((const uint8_t *)item->mask +
+                                          field->offset, field->size);
+               if (!mask) {
+                       ++field;
+                       continue;
                }
-               if (resource->actions_num != i)
-                       resource->actions_num = i;
-               field++;
-       }
+               /* Deduce actual data width in bits from mask value. */
+               off_b = rte_bsf32(mask);
+               size_b = sizeof(uint32_t) * CHAR_BIT -
+                        off_b - __builtin_clz(mask);
+               assert(size_b);
+               size_b = size_b == sizeof(uint32_t) * CHAR_BIT ? 0 : size_b;
+               actions[i].action_type = type;
+               actions[i].field = field->id;
+               actions[i].offset = off_b;
+               actions[i].length = size_b;
+               /* Convert entire record to expected big-endian format. */
+               actions[i].data0 = rte_cpu_to_be_32(actions[i].data0);
+               if (type == MLX5_MODIFICATION_TYPE_COPY) {
+                       assert(dcopy);
+                       actions[i].dst_field = dcopy->id;
+                       actions[i].dst_offset =
+                               (int)dcopy->offset < 0 ? off_b : dcopy->offset;
+                       /* Convert entire record to big-endian format. */
+                       actions[i].data1 = rte_cpu_to_be_32(actions[i].data1);
+               } else {
+                       assert(item->spec);
+                       data = flow_dv_fetch_field((const uint8_t *)item->spec +
+                                                  field->offset, field->size);
+                       /* Shift out the trailing masked bits from data. */
+                       data = (data & mask) >> off_b;
+                       actions[i].data1 = rte_cpu_to_be_32(data);
+               }
+               ++i;
+               ++field;
+       } while (field->size);
+       resource->actions_num = i;
        if (!resource->actions_num)
                return rte_flow_error_set(error, EINVAL,
                                          RTE_FLOW_ERROR_TYPE_ACTION, NULL,
@@ -334,7 +414,7 @@ struct field_modify_info modify_tcp[] = {
        }
        item.spec = &ipv4;
        item.mask = &ipv4_mask;
-       return flow_dv_convert_modify_action(&item, modify_ipv4, resource,
+       return flow_dv_convert_modify_action(&item, modify_ipv4, NULL, resource,
                                             MLX5_MODIFICATION_TYPE_SET, error);
 }
 
@@ -380,7 +460,7 @@ struct field_modify_info modify_tcp[] = {
        }
        item.spec = &ipv6;
        item.mask = &ipv6_mask;
-       return flow_dv_convert_modify_action(&item, modify_ipv6, resource,
+       return flow_dv_convert_modify_action(&item, modify_ipv6, NULL, resource,
                                             MLX5_MODIFICATION_TYPE_SET, error);
 }
 
@@ -426,7 +506,7 @@ struct field_modify_info modify_tcp[] = {
        }
        item.spec = &eth;
        item.mask = &eth_mask;
-       return flow_dv_convert_modify_action(&item, modify_eth, resource,
+       return flow_dv_convert_modify_action(&item, modify_eth, NULL, resource,
                                             MLX5_MODIFICATION_TYPE_SET, error);
 }
 
@@ -540,7 +620,7 @@ struct field_modify_info modify_tcp[] = {
                item.mask = &tcp_mask;
                field = modify_tcp;
        }
-       return flow_dv_convert_modify_action(&item, field, resource,
+       return flow_dv_convert_modify_action(&item, field, NULL, resource,
                                             MLX5_MODIFICATION_TYPE_SET, error);
 }
 
@@ -600,7 +680,7 @@ struct field_modify_info modify_tcp[] = {
                item.mask = &ipv6_mask;
                field = modify_ipv6;
        }
-       return flow_dv_convert_modify_action(&item, field, resource,
+       return flow_dv_convert_modify_action(&item, field, NULL, resource,
                                             MLX5_MODIFICATION_TYPE_SET, error);
 }
 
@@ -657,7 +737,7 @@ struct field_modify_info modify_tcp[] = {
                item.mask = &ipv6_mask;
                field = modify_ipv6;
        }
-       return flow_dv_convert_modify_action(&item, field, resource,
+       return flow_dv_convert_modify_action(&item, field, NULL, resource,
                                             MLX5_MODIFICATION_TYPE_ADD, error);
 }
 
@@ -702,7 +782,7 @@ struct field_modify_info modify_tcp[] = {
        item.type = RTE_FLOW_ITEM_TYPE_TCP;
        item.spec = &tcp;
        item.mask = &tcp_mask;
-       return flow_dv_convert_modify_action(&item, modify_tcp, resource,
+       return flow_dv_convert_modify_action(&item, modify_tcp, NULL, resource,
                                             MLX5_MODIFICATION_TYPE_ADD, error);
 }
 
@@ -747,7 +827,7 @@ struct field_modify_info modify_tcp[] = {
        item.type = RTE_FLOW_ITEM_TYPE_TCP;
        item.spec = &tcp;
        item.mask = &tcp_mask;
-       return flow_dv_convert_modify_action(&item, modify_tcp, resource,
+       return flow_dv_convert_modify_action(&item, modify_tcp, NULL, resource,
                                             MLX5_MODIFICATION_TYPE_ADD, error);
 }
 
diff --git a/drivers/net/mlx5/mlx5_prm.h b/drivers/net/mlx5/mlx5_prm.h
index 96b9166..b9e53f5 100644
--- a/drivers/net/mlx5/mlx5_prm.h
+++ b/drivers/net/mlx5/mlx5_prm.h
@@ -383,11 +383,12 @@ struct mlx5_cqe {
 /* CQE format value. */
 #define MLX5_COMPRESSED 0x3
 
-/* Write a specific data value to a field. */
-#define MLX5_MODIFICATION_TYPE_SET 1
-
-/* Add a specific data value to a field. */
-#define MLX5_MODIFICATION_TYPE_ADD 2
+/* Action type of header modification. */
+enum {
+       MLX5_MODIFICATION_TYPE_SET = 0x1,
+       MLX5_MODIFICATION_TYPE_ADD = 0x2,
+       MLX5_MODIFICATION_TYPE_COPY = 0x3,
+};
 
 /* The field of packet to be modified. */
 enum mlx5_modification_field {
@@ -470,6 +471,13 @@ struct mlx5_modification_cmd {
        union {
                uint32_t data1;
                uint8_t data[4];
+               struct {
+                       unsigned int rsvd2:8;
+                       unsigned int dst_offset:5;
+                       unsigned int rsvd3:3;
+                       unsigned int dst_field:12;
+                       unsigned int rsvd4:4;
+               };
        };
 };
 
-- 
1.8.3.1

Reply via email to