linux/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
   2/* Copyright (c) 2019 Mellanox Technologies. */
   3
   4#include "dr_types.h"
   5
   6enum dr_action_domain {
   7        DR_ACTION_DOMAIN_NIC_INGRESS,
   8        DR_ACTION_DOMAIN_NIC_EGRESS,
   9        DR_ACTION_DOMAIN_FDB_INGRESS,
  10        DR_ACTION_DOMAIN_FDB_EGRESS,
  11        DR_ACTION_DOMAIN_MAX,
  12};
  13
  14enum dr_action_valid_state {
  15        DR_ACTION_STATE_ERR,
  16        DR_ACTION_STATE_NO_ACTION,
  17        DR_ACTION_STATE_REFORMAT,
  18        DR_ACTION_STATE_MODIFY_HDR,
  19        DR_ACTION_STATE_MODIFY_VLAN,
  20        DR_ACTION_STATE_NON_TERM,
  21        DR_ACTION_STATE_TERM,
  22        DR_ACTION_STATE_MAX,
  23};
  24
  25static const enum dr_action_valid_state
  26next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX] = {
  27        [DR_ACTION_DOMAIN_NIC_INGRESS] = {
  28                [DR_ACTION_STATE_NO_ACTION] = {
  29                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
  30                        [DR_ACTION_TYP_QP]              = DR_ACTION_STATE_TERM,
  31                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
  32                        [DR_ACTION_TYP_TAG]             = DR_ACTION_STATE_NON_TERM,
  33                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_NON_TERM,
  34                        [DR_ACTION_TYP_TNL_L2_TO_L2]    = DR_ACTION_STATE_REFORMAT,
  35                        [DR_ACTION_TYP_TNL_L3_TO_L2]    = DR_ACTION_STATE_REFORMAT,
  36                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
  37                        [DR_ACTION_TYP_POP_VLAN]        = DR_ACTION_STATE_MODIFY_VLAN,
  38                },
  39                [DR_ACTION_STATE_REFORMAT] = {
  40                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
  41                        [DR_ACTION_TYP_QP]              = DR_ACTION_STATE_TERM,
  42                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
  43                        [DR_ACTION_TYP_TAG]             = DR_ACTION_STATE_REFORMAT,
  44                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_REFORMAT,
  45                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
  46                        [DR_ACTION_TYP_POP_VLAN]        = DR_ACTION_STATE_MODIFY_VLAN,
  47                },
  48                [DR_ACTION_STATE_MODIFY_HDR] = {
  49                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
  50                        [DR_ACTION_TYP_QP]              = DR_ACTION_STATE_TERM,
  51                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
  52                        [DR_ACTION_TYP_TAG]             = DR_ACTION_STATE_MODIFY_HDR,
  53                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_MODIFY_HDR,
  54                },
  55                [DR_ACTION_STATE_MODIFY_VLAN] = {
  56                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
  57                        [DR_ACTION_TYP_QP]              = DR_ACTION_STATE_TERM,
  58                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
  59                        [DR_ACTION_TYP_TAG]             = DR_ACTION_STATE_MODIFY_VLAN,
  60                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_MODIFY_VLAN,
  61                        [DR_ACTION_TYP_POP_VLAN]        = DR_ACTION_STATE_MODIFY_VLAN,
  62                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
  63                },
  64                [DR_ACTION_STATE_NON_TERM] = {
  65                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
  66                        [DR_ACTION_TYP_QP]              = DR_ACTION_STATE_TERM,
  67                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
  68                        [DR_ACTION_TYP_TAG]             = DR_ACTION_STATE_NON_TERM,
  69                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_NON_TERM,
  70                        [DR_ACTION_TYP_TNL_L2_TO_L2]    = DR_ACTION_STATE_REFORMAT,
  71                        [DR_ACTION_TYP_TNL_L3_TO_L2]    = DR_ACTION_STATE_REFORMAT,
  72                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
  73                        [DR_ACTION_TYP_POP_VLAN]        = DR_ACTION_STATE_MODIFY_VLAN,
  74                },
  75                [DR_ACTION_STATE_TERM] = {
  76                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_TERM,
  77                },
  78        },
  79        [DR_ACTION_DOMAIN_NIC_EGRESS] = {
  80                [DR_ACTION_STATE_NO_ACTION] = {
  81                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
  82                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
  83                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_NON_TERM,
  84                        [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_REFORMAT,
  85                        [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_REFORMAT,
  86                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
  87                        [DR_ACTION_TYP_PUSH_VLAN]       = DR_ACTION_STATE_MODIFY_VLAN,
  88                },
  89                [DR_ACTION_STATE_REFORMAT] = {
  90                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
  91                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
  92                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_REFORMAT,
  93                },
  94                [DR_ACTION_STATE_MODIFY_HDR] = {
  95                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
  96                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
  97                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_MODIFY_HDR,
  98                        [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_REFORMAT,
  99                        [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_REFORMAT,
 100                        [DR_ACTION_TYP_PUSH_VLAN]       = DR_ACTION_STATE_MODIFY_VLAN,
 101                },
 102                [DR_ACTION_STATE_MODIFY_VLAN] = {
 103                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
 104                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
 105                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_MODIFY_VLAN,
 106                        [DR_ACTION_TYP_PUSH_VLAN]       = DR_ACTION_STATE_MODIFY_VLAN,
 107                        [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_REFORMAT,
 108                        [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_REFORMAT,
 109                },
 110                [DR_ACTION_STATE_NON_TERM] = {
 111                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
 112                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
 113                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_NON_TERM,
 114                        [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_REFORMAT,
 115                        [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_REFORMAT,
 116                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
 117                        [DR_ACTION_TYP_PUSH_VLAN]       = DR_ACTION_STATE_MODIFY_VLAN,
 118                },
 119                [DR_ACTION_STATE_TERM] = {
 120                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_TERM,
 121                },
 122        },
 123        [DR_ACTION_DOMAIN_FDB_INGRESS] = {
 124                [DR_ACTION_STATE_NO_ACTION] = {
 125                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
 126                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
 127                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_NON_TERM,
 128                        [DR_ACTION_TYP_TNL_L2_TO_L2]    = DR_ACTION_STATE_REFORMAT,
 129                        [DR_ACTION_TYP_TNL_L3_TO_L2]    = DR_ACTION_STATE_REFORMAT,
 130                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
 131                        [DR_ACTION_TYP_POP_VLAN]        = DR_ACTION_STATE_MODIFY_VLAN,
 132                        [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
 133                },
 134                [DR_ACTION_STATE_REFORMAT] = {
 135                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
 136                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
 137                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_REFORMAT,
 138                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
 139                        [DR_ACTION_TYP_POP_VLAN]        = DR_ACTION_STATE_MODIFY_VLAN,
 140                        [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
 141                },
 142                [DR_ACTION_STATE_MODIFY_HDR] = {
 143                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
 144                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
 145                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_MODIFY_HDR,
 146                        [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
 147                },
 148                [DR_ACTION_STATE_MODIFY_VLAN] = {
 149                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
 150                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
 151                        [DR_ACTION_TYP_POP_VLAN]        = DR_ACTION_STATE_MODIFY_VLAN,
 152                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_MODIFY_VLAN,
 153                        [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
 154                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
 155                },
 156                [DR_ACTION_STATE_NON_TERM] = {
 157                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
 158                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
 159                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_NON_TERM,
 160                        [DR_ACTION_TYP_TNL_L2_TO_L2]    = DR_ACTION_STATE_REFORMAT,
 161                        [DR_ACTION_TYP_TNL_L3_TO_L2]    = DR_ACTION_STATE_REFORMAT,
 162                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
 163                        [DR_ACTION_TYP_POP_VLAN]        = DR_ACTION_STATE_MODIFY_VLAN,
 164                        [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
 165                },
 166                [DR_ACTION_STATE_TERM] = {
 167                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_TERM,
 168                },
 169        },
 170        [DR_ACTION_DOMAIN_FDB_EGRESS] = {
 171                [DR_ACTION_STATE_NO_ACTION] = {
 172                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
 173                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
 174                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_NON_TERM,
 175                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
 176                        [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_REFORMAT,
 177                        [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_REFORMAT,
 178                        [DR_ACTION_TYP_PUSH_VLAN]       = DR_ACTION_STATE_MODIFY_VLAN,
 179                        [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
 180                },
 181                [DR_ACTION_STATE_REFORMAT] = {
 182                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
 183                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
 184                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_REFORMAT,
 185                        [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
 186                },
 187                [DR_ACTION_STATE_MODIFY_HDR] = {
 188                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
 189                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
 190                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_MODIFY_HDR,
 191                        [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_REFORMAT,
 192                        [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_REFORMAT,
 193                        [DR_ACTION_TYP_PUSH_VLAN]       = DR_ACTION_STATE_MODIFY_VLAN,
 194                        [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
 195                },
 196                [DR_ACTION_STATE_MODIFY_VLAN] = {
 197                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
 198                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
 199                        [DR_ACTION_TYP_PUSH_VLAN]       = DR_ACTION_STATE_MODIFY_VLAN,
 200                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_MODIFY_VLAN,
 201                        [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_REFORMAT,
 202                        [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_REFORMAT,
 203                        [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
 204                },
 205                [DR_ACTION_STATE_NON_TERM] = {
 206                        [DR_ACTION_TYP_DROP]            = DR_ACTION_STATE_TERM,
 207                        [DR_ACTION_TYP_FT]              = DR_ACTION_STATE_TERM,
 208                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_NON_TERM,
 209                        [DR_ACTION_TYP_MODIFY_HDR]      = DR_ACTION_STATE_MODIFY_HDR,
 210                        [DR_ACTION_TYP_L2_TO_TNL_L2]    = DR_ACTION_STATE_REFORMAT,
 211                        [DR_ACTION_TYP_L2_TO_TNL_L3]    = DR_ACTION_STATE_REFORMAT,
 212                        [DR_ACTION_TYP_PUSH_VLAN]       = DR_ACTION_STATE_MODIFY_VLAN,
 213                        [DR_ACTION_TYP_VPORT]           = DR_ACTION_STATE_TERM,
 214                },
 215                [DR_ACTION_STATE_TERM] = {
 216                        [DR_ACTION_TYP_CTR]             = DR_ACTION_STATE_TERM,
 217                },
 218        },
 219};
 220
 221static int
 222dr_action_reformat_to_action_type(enum mlx5dr_action_reformat_type reformat_type,
 223                                  enum mlx5dr_action_type *action_type)
 224{
 225        switch (reformat_type) {
 226        case DR_ACTION_REFORMAT_TYP_TNL_L2_TO_L2:
 227                *action_type = DR_ACTION_TYP_TNL_L2_TO_L2;
 228                break;
 229        case DR_ACTION_REFORMAT_TYP_L2_TO_TNL_L2:
 230                *action_type = DR_ACTION_TYP_L2_TO_TNL_L2;
 231                break;
 232        case DR_ACTION_REFORMAT_TYP_TNL_L3_TO_L2:
 233                *action_type = DR_ACTION_TYP_TNL_L3_TO_L2;
 234                break;
 235        case DR_ACTION_REFORMAT_TYP_L2_TO_TNL_L3:
 236                *action_type = DR_ACTION_TYP_L2_TO_TNL_L3;
 237                break;
 238        default:
 239                return -EINVAL;
 240        }
 241
 242        return 0;
 243}
 244
 245/* Apply the actions on the rule STE array starting from the last_ste.
 246 * Actions might require more than one STE, new_num_stes will return
 247 * the new size of the STEs array, rule with actions.
 248 */
 249static void dr_actions_apply(struct mlx5dr_domain *dmn,
 250                             enum mlx5dr_ste_entry_type ste_type,
 251                             u8 *action_type_set,
 252                             u8 *last_ste,
 253                             struct mlx5dr_ste_actions_attr *attr,
 254                             u32 *new_num_stes)
 255{
 256        struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx;
 257        u32 added_stes = 0;
 258
 259        if (ste_type == MLX5DR_STE_TYPE_RX)
 260                mlx5dr_ste_set_actions_rx(ste_ctx, dmn, action_type_set,
 261                                          last_ste, attr, &added_stes);
 262        else
 263                mlx5dr_ste_set_actions_tx(ste_ctx, dmn, action_type_set,
 264                                          last_ste, attr, &added_stes);
 265
 266        *new_num_stes += added_stes;
 267}
 268
 269static enum dr_action_domain
 270dr_action_get_action_domain(enum mlx5dr_domain_type domain,
 271                            enum mlx5dr_ste_entry_type ste_type)
 272{
 273        switch (domain) {
 274        case MLX5DR_DOMAIN_TYPE_NIC_RX:
 275                return DR_ACTION_DOMAIN_NIC_INGRESS;
 276        case MLX5DR_DOMAIN_TYPE_NIC_TX:
 277                return DR_ACTION_DOMAIN_NIC_EGRESS;
 278        case MLX5DR_DOMAIN_TYPE_FDB:
 279                if (ste_type == MLX5DR_STE_TYPE_RX)
 280                        return DR_ACTION_DOMAIN_FDB_INGRESS;
 281                return DR_ACTION_DOMAIN_FDB_EGRESS;
 282        default:
 283                WARN_ON(true);
 284                return DR_ACTION_DOMAIN_MAX;
 285        }
 286}
 287
 288static
 289int dr_action_validate_and_get_next_state(enum dr_action_domain action_domain,
 290                                          u32 action_type,
 291                                          u32 *state)
 292{
 293        u32 cur_state = *state;
 294
 295        /* Check action state machine is valid */
 296        *state = next_action_state[action_domain][cur_state][action_type];
 297
 298        if (*state == DR_ACTION_STATE_ERR)
 299                return -EOPNOTSUPP;
 300
 301        return 0;
 302}
 303
 304static int dr_action_handle_cs_recalc(struct mlx5dr_domain *dmn,
 305                                      struct mlx5dr_action *dest_action,
 306                                      u64 *final_icm_addr)
 307{
 308        int ret;
 309
 310        switch (dest_action->action_type) {
 311        case DR_ACTION_TYP_FT:
 312                /* Allow destination flow table only if table is a terminating
 313                 * table, since there is an *assumption* that in such case FW
 314                 * will recalculate the CS.
 315                 */
 316                if (dest_action->dest_tbl->is_fw_tbl) {
 317                        *final_icm_addr = dest_action->dest_tbl->fw_tbl.rx_icm_addr;
 318                } else {
 319                        mlx5dr_dbg(dmn,
 320                                   "Destination FT should be terminating when modify TTL is used\n");
 321                        return -EINVAL;
 322                }
 323                break;
 324
 325        case DR_ACTION_TYP_VPORT:
 326                /* If destination is vport we will get the FW flow table
 327                 * that recalculates the CS and forwards to the vport.
 328                 */
 329                ret = mlx5dr_domain_cache_get_recalc_cs_ft_addr(dest_action->vport->dmn,
 330                                                                dest_action->vport->caps->num,
 331                                                                final_icm_addr);
 332                if (ret) {
 333                        mlx5dr_err(dmn, "Failed to get FW cs recalc flow table\n");
 334                        return ret;
 335                }
 336                break;
 337
 338        default:
 339                break;
 340        }
 341
 342        return 0;
 343}
 344
 345#define WITH_VLAN_NUM_HW_ACTIONS 6
 346
 347int mlx5dr_actions_build_ste_arr(struct mlx5dr_matcher *matcher,
 348                                 struct mlx5dr_matcher_rx_tx *nic_matcher,
 349                                 struct mlx5dr_action *actions[],
 350                                 u32 num_actions,
 351                                 u8 *ste_arr,
 352                                 u32 *new_hw_ste_arr_sz)
 353{
 354        struct mlx5dr_domain_rx_tx *nic_dmn = nic_matcher->nic_tbl->nic_dmn;
 355        bool rx_rule = nic_dmn->ste_type == MLX5DR_STE_TYPE_RX;
 356        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
 357        u8 action_type_set[DR_ACTION_TYP_MAX] = {};
 358        struct mlx5dr_ste_actions_attr attr = {};
 359        struct mlx5dr_action *dest_action = NULL;
 360        u32 state = DR_ACTION_STATE_NO_ACTION;
 361        enum dr_action_domain action_domain;
 362        bool recalc_cs_required = false;
 363        u8 *last_ste;
 364        int i, ret;
 365
 366        attr.gvmi = dmn->info.caps.gvmi;
 367        attr.hit_gvmi = dmn->info.caps.gvmi;
 368        attr.final_icm_addr = nic_dmn->default_icm_addr;
 369        action_domain = dr_action_get_action_domain(dmn->type, nic_dmn->ste_type);
 370
 371        for (i = 0; i < num_actions; i++) {
 372                struct mlx5dr_action_dest_tbl *dest_tbl;
 373                struct mlx5dr_action *action;
 374                int max_actions_type = 1;
 375                u32 action_type;
 376
 377                action = actions[i];
 378                action_type = action->action_type;
 379
 380                switch (action_type) {
 381                case DR_ACTION_TYP_DROP:
 382                        attr.final_icm_addr = nic_dmn->drop_icm_addr;
 383                        break;
 384                case DR_ACTION_TYP_FT:
 385                        dest_action = action;
 386                        dest_tbl = action->dest_tbl;
 387                        if (!dest_tbl->is_fw_tbl) {
 388                                if (dest_tbl->tbl->dmn != dmn) {
 389                                        mlx5dr_err(dmn,
 390                                                   "Destination table belongs to a different domain\n");
 391                                        goto out_invalid_arg;
 392                                }
 393                                if (dest_tbl->tbl->level <= matcher->tbl->level) {
 394                                        mlx5_core_warn_once(dmn->mdev,
 395                                                            "Connecting table to a lower/same level destination table\n");
 396                                        mlx5dr_dbg(dmn,
 397                                                   "Connecting table at level %d to a destination table at level %d\n",
 398                                                   matcher->tbl->level,
 399                                                   dest_tbl->tbl->level);
 400                                }
 401                                attr.final_icm_addr = rx_rule ?
 402                                        dest_tbl->tbl->rx.s_anchor->chunk->icm_addr :
 403                                        dest_tbl->tbl->tx.s_anchor->chunk->icm_addr;
 404                        } else {
 405                                struct mlx5dr_cmd_query_flow_table_details output;
 406                                int ret;
 407
 408                                /* get the relevant addresses */
 409                                if (!action->dest_tbl->fw_tbl.rx_icm_addr) {
 410                                        ret = mlx5dr_cmd_query_flow_table(dmn->mdev,
 411                                                                          dest_tbl->fw_tbl.type,
 412                                                                          dest_tbl->fw_tbl.id,
 413                                                                          &output);
 414                                        if (!ret) {
 415                                                dest_tbl->fw_tbl.tx_icm_addr =
 416                                                        output.sw_owner_icm_root_1;
 417                                                dest_tbl->fw_tbl.rx_icm_addr =
 418                                                        output.sw_owner_icm_root_0;
 419                                        } else {
 420                                                mlx5dr_err(dmn,
 421                                                           "Failed mlx5_cmd_query_flow_table ret: %d\n",
 422                                                           ret);
 423                                                return ret;
 424                                        }
 425                                }
 426                                attr.final_icm_addr = rx_rule ?
 427                                        dest_tbl->fw_tbl.rx_icm_addr :
 428                                        dest_tbl->fw_tbl.tx_icm_addr;
 429                        }
 430                        break;
 431                case DR_ACTION_TYP_QP:
 432                        mlx5dr_info(dmn, "Domain doesn't support QP\n");
 433                        goto out_invalid_arg;
 434                case DR_ACTION_TYP_CTR:
 435                        attr.ctr_id = action->ctr->ctr_id +
 436                                action->ctr->offeset;
 437                        break;
 438                case DR_ACTION_TYP_TAG:
 439                        attr.flow_tag = action->flow_tag->flow_tag;
 440                        break;
 441                case DR_ACTION_TYP_TNL_L2_TO_L2:
 442                        break;
 443                case DR_ACTION_TYP_TNL_L3_TO_L2:
 444                        attr.decap_index = action->rewrite->index;
 445                        attr.decap_actions = action->rewrite->num_of_actions;
 446                        attr.decap_with_vlan =
 447                                attr.decap_actions == WITH_VLAN_NUM_HW_ACTIONS;
 448                        break;
 449                case DR_ACTION_TYP_MODIFY_HDR:
 450                        attr.modify_index = action->rewrite->index;
 451                        attr.modify_actions = action->rewrite->num_of_actions;
 452                        recalc_cs_required = action->rewrite->modify_ttl &&
 453                                             !mlx5dr_ste_supp_ttl_cs_recalc(&dmn->info.caps);
 454                        break;
 455                case DR_ACTION_TYP_L2_TO_TNL_L2:
 456                case DR_ACTION_TYP_L2_TO_TNL_L3:
 457                        attr.reformat_size = action->reformat->reformat_size;
 458                        attr.reformat_id = action->reformat->reformat_id;
 459                        break;
 460                case DR_ACTION_TYP_VPORT:
 461                        attr.hit_gvmi = action->vport->caps->vhca_gvmi;
 462                        dest_action = action;
 463                        if (rx_rule) {
 464                                /* Loopback on WIRE vport is not supported */
 465                                if (action->vport->caps->num == WIRE_PORT)
 466                                        goto out_invalid_arg;
 467
 468                                attr.final_icm_addr = action->vport->caps->icm_address_rx;
 469                        } else {
 470                                attr.final_icm_addr = action->vport->caps->icm_address_tx;
 471                        }
 472                        break;
 473                case DR_ACTION_TYP_POP_VLAN:
 474                        max_actions_type = MLX5DR_MAX_VLANS;
 475                        attr.vlans.count++;
 476                        break;
 477                case DR_ACTION_TYP_PUSH_VLAN:
 478                        max_actions_type = MLX5DR_MAX_VLANS;
 479                        if (attr.vlans.count == MLX5DR_MAX_VLANS)
 480                                return -EINVAL;
 481
 482                        attr.vlans.headers[attr.vlans.count++] = action->push_vlan->vlan_hdr;
 483                        break;
 484                default:
 485                        goto out_invalid_arg;
 486                }
 487
 488                /* Check action duplication */
 489                if (++action_type_set[action_type] > max_actions_type) {
 490                        mlx5dr_err(dmn, "Action type %d supports only max %d time(s)\n",
 491                                   action_type, max_actions_type);
 492                        goto out_invalid_arg;
 493                }
 494
 495                /* Check action state machine is valid */
 496                if (dr_action_validate_and_get_next_state(action_domain,
 497                                                          action_type,
 498                                                          &state)) {
 499                        mlx5dr_err(dmn, "Invalid action sequence provided\n");
 500                        return -EOPNOTSUPP;
 501                }
 502        }
 503
 504        *new_hw_ste_arr_sz = nic_matcher->num_of_builders;
 505        last_ste = ste_arr + DR_STE_SIZE * (nic_matcher->num_of_builders - 1);
 506
 507        /* Due to a HW bug in some devices, modifying TTL on RX flows will
 508         * cause an incorrect checksum calculation. In this case we will
 509         * use a FW table to recalculate.
 510         */
 511        if (dmn->type == MLX5DR_DOMAIN_TYPE_FDB &&
 512            rx_rule && recalc_cs_required && dest_action) {
 513                ret = dr_action_handle_cs_recalc(dmn, dest_action, &attr.final_icm_addr);
 514                if (ret) {
 515                        mlx5dr_err(dmn,
 516                                   "Failed to handle checksum recalculation err %d\n",
 517                                   ret);
 518                        return ret;
 519                }
 520        }
 521
 522        dr_actions_apply(dmn,
 523                         nic_dmn->ste_type,
 524                         action_type_set,
 525                         last_ste,
 526                         &attr,
 527                         new_hw_ste_arr_sz);
 528
 529        return 0;
 530
 531out_invalid_arg:
 532        return -EINVAL;
 533}
 534
 535static unsigned int action_size[DR_ACTION_TYP_MAX] = {
 536        [DR_ACTION_TYP_TNL_L2_TO_L2] = sizeof(struct mlx5dr_action_reformat),
 537        [DR_ACTION_TYP_L2_TO_TNL_L2] = sizeof(struct mlx5dr_action_reformat),
 538        [DR_ACTION_TYP_TNL_L3_TO_L2] = sizeof(struct mlx5dr_action_rewrite),
 539        [DR_ACTION_TYP_L2_TO_TNL_L3] = sizeof(struct mlx5dr_action_reformat),
 540        [DR_ACTION_TYP_FT]           = sizeof(struct mlx5dr_action_dest_tbl),
 541        [DR_ACTION_TYP_CTR]          = sizeof(struct mlx5dr_action_ctr),
 542        [DR_ACTION_TYP_TAG]          = sizeof(struct mlx5dr_action_flow_tag),
 543        [DR_ACTION_TYP_MODIFY_HDR]   = sizeof(struct mlx5dr_action_rewrite),
 544        [DR_ACTION_TYP_VPORT]        = sizeof(struct mlx5dr_action_vport),
 545        [DR_ACTION_TYP_PUSH_VLAN]    = sizeof(struct mlx5dr_action_push_vlan),
 546};
 547
 548static struct mlx5dr_action *
 549dr_action_create_generic(enum mlx5dr_action_type action_type)
 550{
 551        struct mlx5dr_action *action;
 552        int extra_size;
 553
 554        if (action_type < DR_ACTION_TYP_MAX)
 555                extra_size = action_size[action_type];
 556        else
 557                return NULL;
 558
 559        action = kzalloc(sizeof(*action) + extra_size, GFP_KERNEL);
 560        if (!action)
 561                return NULL;
 562
 563        action->action_type = action_type;
 564        refcount_set(&action->refcount, 1);
 565        action->data = action + 1;
 566
 567        return action;
 568}
 569
 570struct mlx5dr_action *mlx5dr_action_create_drop(void)
 571{
 572        return dr_action_create_generic(DR_ACTION_TYP_DROP);
 573}
 574
 575struct mlx5dr_action *
 576mlx5dr_action_create_dest_table_num(struct mlx5dr_domain *dmn, u32 table_num)
 577{
 578        struct mlx5dr_action *action;
 579
 580        action = dr_action_create_generic(DR_ACTION_TYP_FT);
 581        if (!action)
 582                return NULL;
 583
 584        action->dest_tbl->is_fw_tbl = true;
 585        action->dest_tbl->fw_tbl.dmn = dmn;
 586        action->dest_tbl->fw_tbl.id = table_num;
 587        action->dest_tbl->fw_tbl.type = FS_FT_FDB;
 588        refcount_inc(&dmn->refcount);
 589
 590        return action;
 591}
 592
 593struct mlx5dr_action *
 594mlx5dr_action_create_dest_table(struct mlx5dr_table *tbl)
 595{
 596        struct mlx5dr_action *action;
 597
 598        refcount_inc(&tbl->refcount);
 599
 600        action = dr_action_create_generic(DR_ACTION_TYP_FT);
 601        if (!action)
 602                goto dec_ref;
 603
 604        action->dest_tbl->tbl = tbl;
 605
 606        return action;
 607
 608dec_ref:
 609        refcount_dec(&tbl->refcount);
 610        return NULL;
 611}
 612
 613struct mlx5dr_action *
 614mlx5dr_action_create_mult_dest_tbl(struct mlx5dr_domain *dmn,
 615                                   struct mlx5dr_action_dest *dests,
 616                                   u32 num_of_dests)
 617{
 618        struct mlx5dr_cmd_flow_destination_hw_info *hw_dests;
 619        struct mlx5dr_action **ref_actions;
 620        struct mlx5dr_action *action;
 621        bool reformat_req = false;
 622        u32 num_of_ref = 0;
 623        int ret;
 624        int i;
 625
 626        if (dmn->type != MLX5DR_DOMAIN_TYPE_FDB) {
 627                mlx5dr_err(dmn, "Multiple destination support is for FDB only\n");
 628                return NULL;
 629        }
 630
 631        hw_dests = kzalloc(sizeof(*hw_dests) * num_of_dests, GFP_KERNEL);
 632        if (!hw_dests)
 633                return NULL;
 634
 635        ref_actions = kzalloc(sizeof(*ref_actions) * num_of_dests * 2, GFP_KERNEL);
 636        if (!ref_actions)
 637                goto free_hw_dests;
 638
 639        for (i = 0; i < num_of_dests; i++) {
 640                struct mlx5dr_action *reformat_action = dests[i].reformat;
 641                struct mlx5dr_action *dest_action = dests[i].dest;
 642
 643                ref_actions[num_of_ref++] = dest_action;
 644
 645                switch (dest_action->action_type) {
 646                case DR_ACTION_TYP_VPORT:
 647                        hw_dests[i].vport.flags = MLX5_FLOW_DEST_VPORT_VHCA_ID;
 648                        hw_dests[i].type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
 649                        hw_dests[i].vport.num = dest_action->vport->caps->num;
 650                        hw_dests[i].vport.vhca_id = dest_action->vport->caps->vhca_gvmi;
 651                        if (reformat_action) {
 652                                reformat_req = true;
 653                                hw_dests[i].vport.reformat_id =
 654                                        reformat_action->reformat->reformat_id;
 655                                ref_actions[num_of_ref++] = reformat_action;
 656                                hw_dests[i].vport.flags |= MLX5_FLOW_DEST_VPORT_REFORMAT_ID;
 657                        }
 658                        break;
 659
 660                case DR_ACTION_TYP_FT:
 661                        hw_dests[i].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
 662                        if (dest_action->dest_tbl->is_fw_tbl)
 663                                hw_dests[i].ft_id = dest_action->dest_tbl->fw_tbl.id;
 664                        else
 665                                hw_dests[i].ft_id = dest_action->dest_tbl->tbl->table_id;
 666                        break;
 667
 668                default:
 669                        mlx5dr_dbg(dmn, "Invalid multiple destinations action\n");
 670                        goto free_ref_actions;
 671                }
 672        }
 673
 674        action = dr_action_create_generic(DR_ACTION_TYP_FT);
 675        if (!action)
 676                goto free_ref_actions;
 677
 678        ret = mlx5dr_fw_create_md_tbl(dmn,
 679                                      hw_dests,
 680                                      num_of_dests,
 681                                      reformat_req,
 682                                      &action->dest_tbl->fw_tbl.id,
 683                                      &action->dest_tbl->fw_tbl.group_id);
 684        if (ret)
 685                goto free_action;
 686
 687        refcount_inc(&dmn->refcount);
 688
 689        for (i = 0; i < num_of_ref; i++)
 690                refcount_inc(&ref_actions[i]->refcount);
 691
 692        action->dest_tbl->is_fw_tbl = true;
 693        action->dest_tbl->fw_tbl.dmn = dmn;
 694        action->dest_tbl->fw_tbl.type = FS_FT_FDB;
 695        action->dest_tbl->fw_tbl.ref_actions = ref_actions;
 696        action->dest_tbl->fw_tbl.num_of_ref_actions = num_of_ref;
 697
 698        kfree(hw_dests);
 699
 700        return action;
 701
 702free_action:
 703        kfree(action);
 704free_ref_actions:
 705        kfree(ref_actions);
 706free_hw_dests:
 707        kfree(hw_dests);
 708        return NULL;
 709}
 710
 711struct mlx5dr_action *
 712mlx5dr_action_create_dest_flow_fw_table(struct mlx5dr_domain *dmn,
 713                                        struct mlx5_flow_table *ft)
 714{
 715        struct mlx5dr_action *action;
 716
 717        action = dr_action_create_generic(DR_ACTION_TYP_FT);
 718        if (!action)
 719                return NULL;
 720
 721        action->dest_tbl->is_fw_tbl = 1;
 722        action->dest_tbl->fw_tbl.type = ft->type;
 723        action->dest_tbl->fw_tbl.id = ft->id;
 724        action->dest_tbl->fw_tbl.dmn = dmn;
 725
 726        refcount_inc(&dmn->refcount);
 727
 728        return action;
 729}
 730
 731struct mlx5dr_action *
 732mlx5dr_action_create_flow_counter(u32 counter_id)
 733{
 734        struct mlx5dr_action *action;
 735
 736        action = dr_action_create_generic(DR_ACTION_TYP_CTR);
 737        if (!action)
 738                return NULL;
 739
 740        action->ctr->ctr_id = counter_id;
 741
 742        return action;
 743}
 744
 745struct mlx5dr_action *mlx5dr_action_create_tag(u32 tag_value)
 746{
 747        struct mlx5dr_action *action;
 748
 749        action = dr_action_create_generic(DR_ACTION_TYP_TAG);
 750        if (!action)
 751                return NULL;
 752
 753        action->flow_tag->flow_tag = tag_value & 0xffffff;
 754
 755        return action;
 756}
 757
 758static int
 759dr_action_verify_reformat_params(enum mlx5dr_action_type reformat_type,
 760                                 struct mlx5dr_domain *dmn,
 761                                 size_t data_sz,
 762                                 void *data)
 763{
 764        if ((!data && data_sz) || (data && !data_sz) || reformat_type >
 765                DR_ACTION_TYP_L2_TO_TNL_L3) {
 766                mlx5dr_dbg(dmn, "Invalid reformat parameter!\n");
 767                goto out_err;
 768        }
 769
 770        if (dmn->type == MLX5DR_DOMAIN_TYPE_FDB)
 771                return 0;
 772
 773        if (dmn->type == MLX5DR_DOMAIN_TYPE_NIC_RX) {
 774                if (reformat_type != DR_ACTION_TYP_TNL_L2_TO_L2 &&
 775                    reformat_type != DR_ACTION_TYP_TNL_L3_TO_L2) {
 776                        mlx5dr_dbg(dmn, "Action reformat type not support on RX domain\n");
 777                        goto out_err;
 778                }
 779        } else if (dmn->type == MLX5DR_DOMAIN_TYPE_NIC_TX) {
 780                if (reformat_type != DR_ACTION_TYP_L2_TO_TNL_L2 &&
 781                    reformat_type != DR_ACTION_TYP_L2_TO_TNL_L3) {
 782                        mlx5dr_dbg(dmn, "Action reformat type not support on TX domain\n");
 783                        goto out_err;
 784                }
 785        }
 786
 787        return 0;
 788
 789out_err:
 790        return -EINVAL;
 791}
 792
 793#define ACTION_CACHE_LINE_SIZE 64
 794
 795static int
 796dr_action_create_reformat_action(struct mlx5dr_domain *dmn,
 797                                 size_t data_sz, void *data,
 798                                 struct mlx5dr_action *action)
 799{
 800        u32 reformat_id;
 801        int ret;
 802
 803        switch (action->action_type) {
 804        case DR_ACTION_TYP_L2_TO_TNL_L2:
 805        case DR_ACTION_TYP_L2_TO_TNL_L3:
 806        {
 807                enum mlx5_reformat_ctx_type rt;
 808
 809                if (action->action_type == DR_ACTION_TYP_L2_TO_TNL_L2)
 810                        rt = MLX5_REFORMAT_TYPE_L2_TO_L2_TUNNEL;
 811                else
 812                        rt = MLX5_REFORMAT_TYPE_L2_TO_L3_TUNNEL;
 813
 814                ret = mlx5dr_cmd_create_reformat_ctx(dmn->mdev, rt, data_sz, data,
 815                                                     &reformat_id);
 816                if (ret)
 817                        return ret;
 818
 819                action->reformat->reformat_id = reformat_id;
 820                action->reformat->reformat_size = data_sz;
 821                return 0;
 822        }
 823        case DR_ACTION_TYP_TNL_L2_TO_L2:
 824        {
 825                return 0;
 826        }
 827        case DR_ACTION_TYP_TNL_L3_TO_L2:
 828        {
 829                u8 hw_actions[ACTION_CACHE_LINE_SIZE] = {};
 830                int ret;
 831
 832                ret = mlx5dr_ste_set_action_decap_l3_list(dmn->ste_ctx,
 833                                                          data, data_sz,
 834                                                          hw_actions,
 835                                                          ACTION_CACHE_LINE_SIZE,
 836                                                          &action->rewrite->num_of_actions);
 837                if (ret) {
 838                        mlx5dr_dbg(dmn, "Failed creating decap l3 action list\n");
 839                        return ret;
 840                }
 841
 842                action->rewrite->chunk = mlx5dr_icm_alloc_chunk(dmn->action_icm_pool,
 843                                                                DR_CHUNK_SIZE_8);
 844                if (!action->rewrite->chunk) {
 845                        mlx5dr_dbg(dmn, "Failed allocating modify header chunk\n");
 846                        return -ENOMEM;
 847                }
 848
 849                action->rewrite->data = (void *)hw_actions;
 850                action->rewrite->index = (action->rewrite->chunk->icm_addr -
 851                                         dmn->info.caps.hdr_modify_icm_addr) /
 852                                         ACTION_CACHE_LINE_SIZE;
 853
 854                ret = mlx5dr_send_postsend_action(dmn, action);
 855                if (ret) {
 856                        mlx5dr_dbg(dmn, "Writing decap l3 actions to ICM failed\n");
 857                        mlx5dr_icm_free_chunk(action->rewrite->chunk);
 858                        return ret;
 859                }
 860                return 0;
 861        }
 862        default:
 863                mlx5dr_info(dmn, "Reformat type is not supported %d\n", action->action_type);
 864                return -EINVAL;
 865        }
 866}
 867
 868#define CVLAN_ETHERTYPE 0x8100
 869#define SVLAN_ETHERTYPE 0x88a8
 870
 871struct mlx5dr_action *mlx5dr_action_create_pop_vlan(void)
 872{
 873        return dr_action_create_generic(DR_ACTION_TYP_POP_VLAN);
 874}
 875
 876struct mlx5dr_action *mlx5dr_action_create_push_vlan(struct mlx5dr_domain *dmn,
 877                                                     __be32 vlan_hdr)
 878{
 879        u32 vlan_hdr_h = ntohl(vlan_hdr);
 880        u16 ethertype = vlan_hdr_h >> 16;
 881        struct mlx5dr_action *action;
 882
 883        if (ethertype != SVLAN_ETHERTYPE && ethertype != CVLAN_ETHERTYPE) {
 884                mlx5dr_dbg(dmn, "Invalid vlan ethertype\n");
 885                return NULL;
 886        }
 887
 888        action = dr_action_create_generic(DR_ACTION_TYP_PUSH_VLAN);
 889        if (!action)
 890                return NULL;
 891
 892        action->push_vlan->vlan_hdr = vlan_hdr_h;
 893        return action;
 894}
 895
 896struct mlx5dr_action *
 897mlx5dr_action_create_packet_reformat(struct mlx5dr_domain *dmn,
 898                                     enum mlx5dr_action_reformat_type reformat_type,
 899                                     size_t data_sz,
 900                                     void *data)
 901{
 902        enum mlx5dr_action_type action_type;
 903        struct mlx5dr_action *action;
 904        int ret;
 905
 906        refcount_inc(&dmn->refcount);
 907
 908        /* General checks */
 909        ret = dr_action_reformat_to_action_type(reformat_type, &action_type);
 910        if (ret) {
 911                mlx5dr_dbg(dmn, "Invalid reformat_type provided\n");
 912                goto dec_ref;
 913        }
 914
 915        ret = dr_action_verify_reformat_params(action_type, dmn, data_sz, data);
 916        if (ret)
 917                goto dec_ref;
 918
 919        action = dr_action_create_generic(action_type);
 920        if (!action)
 921                goto dec_ref;
 922
 923        action->reformat->dmn = dmn;
 924
 925        ret = dr_action_create_reformat_action(dmn,
 926                                               data_sz,
 927                                               data,
 928                                               action);
 929        if (ret) {
 930                mlx5dr_dbg(dmn, "Failed creating reformat action %d\n", ret);
 931                goto free_action;
 932        }
 933
 934        return action;
 935
 936free_action:
 937        kfree(action);
 938dec_ref:
 939        refcount_dec(&dmn->refcount);
 940        return NULL;
 941}
 942
 943static int
 944dr_action_modify_sw_to_hw_add(struct mlx5dr_domain *dmn,
 945                              __be64 *sw_action,
 946                              __be64 *hw_action,
 947                              const struct mlx5dr_ste_action_modify_field **ret_hw_info)
 948{
 949        const struct mlx5dr_ste_action_modify_field *hw_action_info;
 950        u8 max_length;
 951        u16 sw_field;
 952        u32 data;
 953
 954        /* Get SW modify action data */
 955        sw_field = MLX5_GET(set_action_in, sw_action, field);
 956        data = MLX5_GET(set_action_in, sw_action, data);
 957
 958        /* Convert SW data to HW modify action format */
 959        hw_action_info = mlx5dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, sw_field);
 960        if (!hw_action_info) {
 961                mlx5dr_dbg(dmn, "Modify add action invalid field given\n");
 962                return -EINVAL;
 963        }
 964
 965        max_length = hw_action_info->end - hw_action_info->start + 1;
 966
 967        mlx5dr_ste_set_action_add(dmn->ste_ctx,
 968                                  hw_action,
 969                                  hw_action_info->hw_field,
 970                                  hw_action_info->start,
 971                                  max_length,
 972                                  data);
 973
 974        *ret_hw_info = hw_action_info;
 975
 976        return 0;
 977}
 978
 979static int
 980dr_action_modify_sw_to_hw_set(struct mlx5dr_domain *dmn,
 981                              __be64 *sw_action,
 982                              __be64 *hw_action,
 983                              const struct mlx5dr_ste_action_modify_field **ret_hw_info)
 984{
 985        const struct mlx5dr_ste_action_modify_field *hw_action_info;
 986        u8 offset, length, max_length;
 987        u16 sw_field;
 988        u32 data;
 989
 990        /* Get SW modify action data */
 991        length = MLX5_GET(set_action_in, sw_action, length);
 992        offset = MLX5_GET(set_action_in, sw_action, offset);
 993        sw_field = MLX5_GET(set_action_in, sw_action, field);
 994        data = MLX5_GET(set_action_in, sw_action, data);
 995
 996        /* Convert SW data to HW modify action format */
 997        hw_action_info = mlx5dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, sw_field);
 998        if (!hw_action_info) {
 999                mlx5dr_dbg(dmn, "Modify set action invalid field given\n");
1000                return -EINVAL;
1001        }
1002
1003        /* PRM defines that length zero specific length of 32bits */
1004        length = length ? length : 32;
1005
1006        max_length = hw_action_info->end - hw_action_info->start + 1;
1007
1008        if (length + offset > max_length) {
1009                mlx5dr_dbg(dmn, "Modify action length + offset exceeds limit\n");
1010                return -EINVAL;
1011        }
1012
1013        mlx5dr_ste_set_action_set(dmn->ste_ctx,
1014                                  hw_action,
1015                                  hw_action_info->hw_field,
1016                                  hw_action_info->start + offset,
1017                                  length,
1018                                  data);
1019
1020        *ret_hw_info = hw_action_info;
1021
1022        return 0;
1023}
1024
1025static int
1026dr_action_modify_sw_to_hw_copy(struct mlx5dr_domain *dmn,
1027                               __be64 *sw_action,
1028                               __be64 *hw_action,
1029                               const struct mlx5dr_ste_action_modify_field **ret_dst_hw_info,
1030                               const struct mlx5dr_ste_action_modify_field **ret_src_hw_info)
1031{
1032        u8 src_offset, dst_offset, src_max_length, dst_max_length, length;
1033        const struct mlx5dr_ste_action_modify_field *hw_dst_action_info;
1034        const struct mlx5dr_ste_action_modify_field *hw_src_action_info;
1035        u16 src_field, dst_field;
1036
1037        /* Get SW modify action data */
1038        src_field = MLX5_GET(copy_action_in, sw_action, src_field);
1039        dst_field = MLX5_GET(copy_action_in, sw_action, dst_field);
1040        src_offset = MLX5_GET(copy_action_in, sw_action, src_offset);
1041        dst_offset = MLX5_GET(copy_action_in, sw_action, dst_offset);
1042        length = MLX5_GET(copy_action_in, sw_action, length);
1043
1044        /* Convert SW data to HW modify action format */
1045        hw_src_action_info = mlx5dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, src_field);
1046        hw_dst_action_info = mlx5dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, dst_field);
1047        if (!hw_src_action_info || !hw_dst_action_info) {
1048                mlx5dr_dbg(dmn, "Modify copy action invalid field given\n");
1049                return -EINVAL;
1050        }
1051
1052        /* PRM defines that length zero specific length of 32bits */
1053        length = length ? length : 32;
1054
1055        src_max_length = hw_src_action_info->end -
1056                         hw_src_action_info->start + 1;
1057        dst_max_length = hw_dst_action_info->end -
1058                         hw_dst_action_info->start + 1;
1059
1060        if (length + src_offset > src_max_length ||
1061            length + dst_offset > dst_max_length) {
1062                mlx5dr_dbg(dmn, "Modify action length + offset exceeds limit\n");
1063                return -EINVAL;
1064        }
1065
1066        mlx5dr_ste_set_action_copy(dmn->ste_ctx,
1067                                   hw_action,
1068                                   hw_dst_action_info->hw_field,
1069                                   hw_dst_action_info->start + dst_offset,
1070                                   length,
1071                                   hw_src_action_info->hw_field,
1072                                   hw_src_action_info->start + src_offset);
1073
1074        *ret_dst_hw_info = hw_dst_action_info;
1075        *ret_src_hw_info = hw_src_action_info;
1076
1077        return 0;
1078}
1079
1080static int
1081dr_action_modify_sw_to_hw(struct mlx5dr_domain *dmn,
1082                          __be64 *sw_action,
1083                          __be64 *hw_action,
1084                          const struct mlx5dr_ste_action_modify_field **ret_dst_hw_info,
1085                          const struct mlx5dr_ste_action_modify_field **ret_src_hw_info)
1086{
1087        u8 action;
1088        int ret;
1089
1090        *hw_action = 0;
1091        *ret_src_hw_info = NULL;
1092
1093        /* Get SW modify action type */
1094        action = MLX5_GET(set_action_in, sw_action, action_type);
1095
1096        switch (action) {
1097        case MLX5_ACTION_TYPE_SET:
1098                ret = dr_action_modify_sw_to_hw_set(dmn, sw_action,
1099                                                    hw_action,
1100                                                    ret_dst_hw_info);
1101                break;
1102
1103        case MLX5_ACTION_TYPE_ADD:
1104                ret = dr_action_modify_sw_to_hw_add(dmn, sw_action,
1105                                                    hw_action,
1106                                                    ret_dst_hw_info);
1107                break;
1108
1109        case MLX5_ACTION_TYPE_COPY:
1110                ret = dr_action_modify_sw_to_hw_copy(dmn, sw_action,
1111                                                     hw_action,
1112                                                     ret_dst_hw_info,
1113                                                     ret_src_hw_info);
1114                break;
1115
1116        default:
1117                mlx5dr_info(dmn, "Unsupported action_type for modify action\n");
1118                ret = -EOPNOTSUPP;
1119        }
1120
1121        return ret;
1122}
1123
1124static int
1125dr_action_modify_check_set_field_limitation(struct mlx5dr_action *action,
1126                                            const __be64 *sw_action)
1127{
1128        u16 sw_field = MLX5_GET(set_action_in, sw_action, field);
1129        struct mlx5dr_domain *dmn = action->rewrite->dmn;
1130
1131        if (sw_field == MLX5_ACTION_IN_FIELD_METADATA_REG_A) {
1132                action->rewrite->allow_rx = 0;
1133                if (dmn->type != MLX5DR_DOMAIN_TYPE_NIC_TX) {
1134                        mlx5dr_dbg(dmn, "Unsupported field %d for RX/FDB set action\n",
1135                                   sw_field);
1136                        return -EINVAL;
1137                }
1138        } else if (sw_field == MLX5_ACTION_IN_FIELD_METADATA_REG_B) {
1139                action->rewrite->allow_tx = 0;
1140                if (dmn->type != MLX5DR_DOMAIN_TYPE_NIC_RX) {
1141                        mlx5dr_dbg(dmn, "Unsupported field %d for TX/FDB set action\n",
1142                                   sw_field);
1143                        return -EINVAL;
1144                }
1145        }
1146
1147        if (!action->rewrite->allow_rx && !action->rewrite->allow_tx) {
1148                mlx5dr_dbg(dmn, "Modify SET actions not supported on both RX and TX\n");
1149                return -EINVAL;
1150        }
1151
1152        return 0;
1153}
1154
1155static int
1156dr_action_modify_check_add_field_limitation(struct mlx5dr_action *action,
1157                                            const __be64 *sw_action)
1158{
1159        u16 sw_field = MLX5_GET(set_action_in, sw_action, field);
1160        struct mlx5dr_domain *dmn = action->rewrite->dmn;
1161
1162        if (sw_field != MLX5_ACTION_IN_FIELD_OUT_IP_TTL &&
1163            sw_field != MLX5_ACTION_IN_FIELD_OUT_IPV6_HOPLIMIT &&
1164            sw_field != MLX5_ACTION_IN_FIELD_OUT_TCP_SEQ_NUM &&
1165            sw_field != MLX5_ACTION_IN_FIELD_OUT_TCP_ACK_NUM) {
1166                mlx5dr_dbg(dmn, "Unsupported field %d for add action\n",
1167                           sw_field);
1168                return -EINVAL;
1169        }
1170
1171        return 0;
1172}
1173
1174static int
1175dr_action_modify_check_copy_field_limitation(struct mlx5dr_action *action,
1176                                             const __be64 *sw_action)
1177{
1178        struct mlx5dr_domain *dmn = action->rewrite->dmn;
1179        u16 sw_fields[2];
1180        int i;
1181
1182        sw_fields[0] = MLX5_GET(copy_action_in, sw_action, src_field);
1183        sw_fields[1] = MLX5_GET(copy_action_in, sw_action, dst_field);
1184
1185        for (i = 0; i < 2; i++) {
1186                if (sw_fields[i] == MLX5_ACTION_IN_FIELD_METADATA_REG_A) {
1187                        action->rewrite->allow_rx = 0;
1188                        if (dmn->type != MLX5DR_DOMAIN_TYPE_NIC_TX) {
1189                                mlx5dr_dbg(dmn, "Unsupported field %d for RX/FDB set action\n",
1190                                           sw_fields[i]);
1191                                return -EINVAL;
1192                        }
1193                } else if (sw_fields[i] == MLX5_ACTION_IN_FIELD_METADATA_REG_B) {
1194                        action->rewrite->allow_tx = 0;
1195                        if (dmn->type != MLX5DR_DOMAIN_TYPE_NIC_RX) {
1196                                mlx5dr_dbg(dmn, "Unsupported field %d for TX/FDB set action\n",
1197                                           sw_fields[i]);
1198                                return -EINVAL;
1199                        }
1200                }
1201        }
1202
1203        if (!action->rewrite->allow_rx && !action->rewrite->allow_tx) {
1204                mlx5dr_dbg(dmn, "Modify copy actions not supported on both RX and TX\n");
1205                return -EINVAL;
1206        }
1207
1208        return 0;
1209}
1210
1211static int
1212dr_action_modify_check_field_limitation(struct mlx5dr_action *action,
1213                                        const __be64 *sw_action)
1214{
1215        struct mlx5dr_domain *dmn = action->rewrite->dmn;
1216        u8 action_type;
1217        int ret;
1218
1219        action_type = MLX5_GET(set_action_in, sw_action, action_type);
1220
1221        switch (action_type) {
1222        case MLX5_ACTION_TYPE_SET:
1223                ret = dr_action_modify_check_set_field_limitation(action,
1224                                                                  sw_action);
1225                break;
1226
1227        case MLX5_ACTION_TYPE_ADD:
1228                ret = dr_action_modify_check_add_field_limitation(action,
1229                                                                  sw_action);
1230                break;
1231
1232        case MLX5_ACTION_TYPE_COPY:
1233                ret = dr_action_modify_check_copy_field_limitation(action,
1234                                                                   sw_action);
1235                break;
1236
1237        default:
1238                mlx5dr_info(dmn, "Unsupported action %d modify action\n",
1239                            action_type);
1240                ret = -EOPNOTSUPP;
1241        }
1242
1243        return ret;
1244}
1245
1246static bool
1247dr_action_modify_check_is_ttl_modify(const void *sw_action)
1248{
1249        u16 sw_field = MLX5_GET(set_action_in, sw_action, field);
1250
1251        return sw_field == MLX5_ACTION_IN_FIELD_OUT_IP_TTL;
1252}
1253
1254static int dr_actions_convert_modify_header(struct mlx5dr_action *action,
1255                                            u32 max_hw_actions,
1256                                            u32 num_sw_actions,
1257                                            __be64 sw_actions[],
1258                                            __be64 hw_actions[],
1259                                            u32 *num_hw_actions,
1260                                            bool *modify_ttl)
1261{
1262        const struct mlx5dr_ste_action_modify_field *hw_dst_action_info;
1263        const struct mlx5dr_ste_action_modify_field *hw_src_action_info;
1264        struct mlx5dr_domain *dmn = action->rewrite->dmn;
1265        int ret, i, hw_idx = 0;
1266        __be64 *sw_action;
1267        __be64 hw_action;
1268        u16 hw_field = 0;
1269        u32 l3_type = 0;
1270        u32 l4_type = 0;
1271
1272        *modify_ttl = false;
1273
1274        action->rewrite->allow_rx = 1;
1275        action->rewrite->allow_tx = 1;
1276
1277        for (i = 0; i < num_sw_actions; i++) {
1278                sw_action = &sw_actions[i];
1279
1280                ret = dr_action_modify_check_field_limitation(action,
1281                                                              sw_action);
1282                if (ret)
1283                        return ret;
1284
1285                if (!(*modify_ttl))
1286                        *modify_ttl = dr_action_modify_check_is_ttl_modify(sw_action);
1287
1288                /* Convert SW action to HW action */
1289                ret = dr_action_modify_sw_to_hw(dmn,
1290                                                sw_action,
1291                                                &hw_action,
1292                                                &hw_dst_action_info,
1293                                                &hw_src_action_info);
1294                if (ret)
1295                        return ret;
1296
1297                /* Due to a HW limitation we cannot modify 2 different L3 types */
1298                if (l3_type && hw_dst_action_info->l3_type &&
1299                    hw_dst_action_info->l3_type != l3_type) {
1300                        mlx5dr_dbg(dmn, "Action list can't support two different L3 types\n");
1301                        return -EINVAL;
1302                }
1303                if (hw_dst_action_info->l3_type)
1304                        l3_type = hw_dst_action_info->l3_type;
1305
1306                /* Due to a HW limitation we cannot modify two different L4 types */
1307                if (l4_type && hw_dst_action_info->l4_type &&
1308                    hw_dst_action_info->l4_type != l4_type) {
1309                        mlx5dr_dbg(dmn, "Action list can't support two different L4 types\n");
1310                        return -EINVAL;
1311                }
1312                if (hw_dst_action_info->l4_type)
1313                        l4_type = hw_dst_action_info->l4_type;
1314
1315                /* HW reads and executes two actions at once this means we
1316                 * need to create a gap if two actions access the same field
1317                 */
1318                if ((hw_idx % 2) && (hw_field == hw_dst_action_info->hw_field ||
1319                                     (hw_src_action_info &&
1320                                      hw_field == hw_src_action_info->hw_field))) {
1321                        /* Check if after gap insertion the total number of HW
1322                         * modify actions doesn't exceeds the limit
1323                         */
1324                        hw_idx++;
1325                        if ((num_sw_actions + hw_idx - i) >= max_hw_actions) {
1326                                mlx5dr_dbg(dmn, "Modify header action number exceeds HW limit\n");
1327                                return -EINVAL;
1328                        }
1329                }
1330                hw_field = hw_dst_action_info->hw_field;
1331
1332                hw_actions[hw_idx] = hw_action;
1333                hw_idx++;
1334        }
1335
1336        *num_hw_actions = hw_idx;
1337
1338        return 0;
1339}
1340
1341static int dr_action_create_modify_action(struct mlx5dr_domain *dmn,
1342                                          size_t actions_sz,
1343                                          __be64 actions[],
1344                                          struct mlx5dr_action *action)
1345{
1346        struct mlx5dr_icm_chunk *chunk;
1347        u32 max_hw_actions;
1348        u32 num_hw_actions;
1349        u32 num_sw_actions;
1350        __be64 *hw_actions;
1351        bool modify_ttl;
1352        int ret;
1353
1354        num_sw_actions = actions_sz / DR_MODIFY_ACTION_SIZE;
1355        max_hw_actions = mlx5dr_icm_pool_chunk_size_to_entries(DR_CHUNK_SIZE_16);
1356
1357        if (num_sw_actions > max_hw_actions) {
1358                mlx5dr_dbg(dmn, "Max number of actions %d exceeds limit %d\n",
1359                           num_sw_actions, max_hw_actions);
1360                return -EINVAL;
1361        }
1362
1363        chunk = mlx5dr_icm_alloc_chunk(dmn->action_icm_pool, DR_CHUNK_SIZE_16);
1364        if (!chunk)
1365                return -ENOMEM;
1366
1367        hw_actions = kcalloc(1, max_hw_actions * DR_MODIFY_ACTION_SIZE, GFP_KERNEL);
1368        if (!hw_actions) {
1369                ret = -ENOMEM;
1370                goto free_chunk;
1371        }
1372
1373        ret = dr_actions_convert_modify_header(action,
1374                                               max_hw_actions,
1375                                               num_sw_actions,
1376                                               actions,
1377                                               hw_actions,
1378                                               &num_hw_actions,
1379                                               &modify_ttl);
1380        if (ret)
1381                goto free_hw_actions;
1382
1383        action->rewrite->chunk = chunk;
1384        action->rewrite->modify_ttl = modify_ttl;
1385        action->rewrite->data = (u8 *)hw_actions;
1386        action->rewrite->num_of_actions = num_hw_actions;
1387        action->rewrite->index = (chunk->icm_addr -
1388                                  dmn->info.caps.hdr_modify_icm_addr) /
1389                                  ACTION_CACHE_LINE_SIZE;
1390
1391        ret = mlx5dr_send_postsend_action(dmn, action);
1392        if (ret)
1393                goto free_hw_actions;
1394
1395        return 0;
1396
1397free_hw_actions:
1398        kfree(hw_actions);
1399free_chunk:
1400        mlx5dr_icm_free_chunk(chunk);
1401        return ret;
1402}
1403
1404struct mlx5dr_action *
1405mlx5dr_action_create_modify_header(struct mlx5dr_domain *dmn,
1406                                   u32 flags,
1407                                   size_t actions_sz,
1408                                   __be64 actions[])
1409{
1410        struct mlx5dr_action *action;
1411        int ret = 0;
1412
1413        refcount_inc(&dmn->refcount);
1414
1415        if (actions_sz % DR_MODIFY_ACTION_SIZE) {
1416                mlx5dr_dbg(dmn, "Invalid modify actions size provided\n");
1417                goto dec_ref;
1418        }
1419
1420        action = dr_action_create_generic(DR_ACTION_TYP_MODIFY_HDR);
1421        if (!action)
1422                goto dec_ref;
1423
1424        action->rewrite->dmn = dmn;
1425
1426        ret = dr_action_create_modify_action(dmn,
1427                                             actions_sz,
1428                                             actions,
1429                                             action);
1430        if (ret) {
1431                mlx5dr_dbg(dmn, "Failed creating modify header action %d\n", ret);
1432                goto free_action;
1433        }
1434
1435        return action;
1436
1437free_action:
1438        kfree(action);
1439dec_ref:
1440        refcount_dec(&dmn->refcount);
1441        return NULL;
1442}
1443
1444struct mlx5dr_action *
1445mlx5dr_action_create_dest_vport(struct mlx5dr_domain *dmn,
1446                                u32 vport, u8 vhca_id_valid,
1447                                u16 vhca_id)
1448{
1449        struct mlx5dr_cmd_vport_cap *vport_cap;
1450        struct mlx5dr_domain *vport_dmn;
1451        struct mlx5dr_action *action;
1452        u8 peer_vport;
1453
1454        peer_vport = vhca_id_valid && (vhca_id != dmn->info.caps.gvmi);
1455        vport_dmn = peer_vport ? dmn->peer_dmn : dmn;
1456        if (!vport_dmn) {
1457                mlx5dr_dbg(dmn, "No peer vport domain for given vhca_id\n");
1458                return NULL;
1459        }
1460
1461        if (vport_dmn->type != MLX5DR_DOMAIN_TYPE_FDB) {
1462                mlx5dr_dbg(dmn, "Domain doesn't support vport actions\n");
1463                return NULL;
1464        }
1465
1466        vport_cap = mlx5dr_get_vport_cap(&vport_dmn->info.caps, vport);
1467        if (!vport_cap) {
1468                mlx5dr_dbg(dmn, "Failed to get vport %d caps\n", vport);
1469                return NULL;
1470        }
1471
1472        action = dr_action_create_generic(DR_ACTION_TYP_VPORT);
1473        if (!action)
1474                return NULL;
1475
1476        action->vport->dmn = vport_dmn;
1477        action->vport->caps = vport_cap;
1478
1479        return action;
1480}
1481
1482int mlx5dr_action_destroy(struct mlx5dr_action *action)
1483{
1484        if (refcount_read(&action->refcount) > 1)
1485                return -EBUSY;
1486
1487        switch (action->action_type) {
1488        case DR_ACTION_TYP_FT:
1489                if (action->dest_tbl->is_fw_tbl)
1490                        refcount_dec(&action->dest_tbl->fw_tbl.dmn->refcount);
1491                else
1492                        refcount_dec(&action->dest_tbl->tbl->refcount);
1493
1494                if (action->dest_tbl->is_fw_tbl &&
1495                    action->dest_tbl->fw_tbl.num_of_ref_actions) {
1496                        struct mlx5dr_action **ref_actions;
1497                        int i;
1498
1499                        ref_actions = action->dest_tbl->fw_tbl.ref_actions;
1500                        for (i = 0; i < action->dest_tbl->fw_tbl.num_of_ref_actions; i++)
1501                                refcount_dec(&ref_actions[i]->refcount);
1502
1503                        kfree(ref_actions);
1504
1505                        mlx5dr_fw_destroy_md_tbl(action->dest_tbl->fw_tbl.dmn,
1506                                                 action->dest_tbl->fw_tbl.id,
1507                                                 action->dest_tbl->fw_tbl.group_id);
1508                }
1509                break;
1510        case DR_ACTION_TYP_TNL_L2_TO_L2:
1511                refcount_dec(&action->reformat->dmn->refcount);
1512                break;
1513        case DR_ACTION_TYP_TNL_L3_TO_L2:
1514                mlx5dr_icm_free_chunk(action->rewrite->chunk);
1515                refcount_dec(&action->rewrite->dmn->refcount);
1516                break;
1517        case DR_ACTION_TYP_L2_TO_TNL_L2:
1518        case DR_ACTION_TYP_L2_TO_TNL_L3:
1519                mlx5dr_cmd_destroy_reformat_ctx((action->reformat->dmn)->mdev,
1520                                                action->reformat->reformat_id);
1521                refcount_dec(&action->reformat->dmn->refcount);
1522                break;
1523        case DR_ACTION_TYP_MODIFY_HDR:
1524                mlx5dr_icm_free_chunk(action->rewrite->chunk);
1525                kfree(action->rewrite->data);
1526                refcount_dec(&action->rewrite->dmn->refcount);
1527                break;
1528        default:
1529                break;
1530        }
1531
1532        kfree(action);
1533        return 0;
1534}
1535