#include "en/params.h"
 #include "en/xsk/pool.h"
 
+static int flow_type_to_traffic_type(u32 flow_type);
+
+static u32 flow_type_mask(u32 flow_type)
+{
+       return flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS);
+}
+
 struct mlx5e_ethtool_rule {
        struct list_head             list;
        struct ethtool_rx_flow_spec  flow_spec;
        struct mlx5_flow_handle      *rule;
        struct mlx5e_ethtool_table   *eth_ft;
+       struct mlx5e_rss             *rss;
 };
 
 static void put_flow_table(struct mlx5e_ethtool_table *eth_ft)
        int table_size;
        int prio;
 
-       switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
+       switch (flow_type_mask(fs->flow_type)) {
        case TCP_V4_FLOW:
        case UDP_V4_FLOW:
        case TCP_V6_FLOW:
                                             outer_headers);
        void *outer_headers_v = MLX5_ADDR_OF(fte_match_param, match_v,
                                             outer_headers);
-       u32 flow_type = fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
+       u32 flow_type = flow_type_mask(fs->flow_type);
 
        switch (flow_type) {
        case TCP_V4_FLOW:
                                                  size - 1);
 }
 
+static int flow_get_tirn(struct mlx5e_priv *priv,
+                        struct mlx5e_ethtool_rule *eth_rule,
+                        struct ethtool_rx_flow_spec *fs,
+                        u32 rss_context, u32 *tirn)
+{
+       if (fs->flow_type & FLOW_RSS) {
+               struct mlx5e_lro_param lro_param;
+               struct mlx5e_rss *rss;
+               u32 flow_type;
+               int err;
+               int tt;
+
+               rss = mlx5e_rx_res_rss_get(priv->rx_res, rss_context);
+               if (!rss)
+                       return -ENOENT;
+
+               flow_type = flow_type_mask(fs->flow_type);
+               tt = flow_type_to_traffic_type(flow_type);
+               if (tt < 0)
+                       return -EINVAL;
+
+               lro_param = mlx5e_get_lro_param(&priv->channels.params);
+               err = mlx5e_rss_obtain_tirn(rss, tt, &lro_param, false, tirn);
+               if (err)
+                       return err;
+               eth_rule->rss = rss;
+               mlx5e_rss_refcnt_inc(eth_rule->rss);
+       } else {
+               struct mlx5e_params *params = &priv->channels.params;
+               enum mlx5e_rq_group group;
+               u16 ix;
+
+               mlx5e_qid_get_ch_and_group(params, fs->ring_cookie, &ix, &group);
+
+               *tirn = group == MLX5E_RQ_GROUP_XSK ?
+                       mlx5e_rx_res_get_tirn_xsk(priv->rx_res, ix) :
+                       mlx5e_rx_res_get_tirn_direct(priv->rx_res, ix);
+       }
+
+       return 0;
+}
+
 static struct mlx5_flow_handle *
 add_ethtool_flow_rule(struct mlx5e_priv *priv,
+                     struct mlx5e_ethtool_rule *eth_rule,
                      struct mlx5_flow_table *ft,
-                     struct ethtool_rx_flow_spec *fs)
+                     struct ethtool_rx_flow_spec *fs, u32 rss_context)
 {
        struct mlx5_flow_act flow_act = { .flags = FLOW_ACT_NO_APPEND };
        struct mlx5_flow_destination *dst = NULL;
        if (fs->ring_cookie == RX_CLS_FLOW_DISC) {
                flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
        } else {
-               struct mlx5e_params *params = &priv->channels.params;
-               enum mlx5e_rq_group group;
-               u16 ix;
-
-               mlx5e_qid_get_ch_and_group(params, fs->ring_cookie, &ix, &group);
-
                dst = kzalloc(sizeof(*dst), GFP_KERNEL);
                if (!dst) {
                        err = -ENOMEM;
                        goto free;
                }
 
+               err = flow_get_tirn(priv, eth_rule, fs, rss_context, &dst->tir_num);
+               if (err)
+                       goto free;
+
                dst->type = MLX5_FLOW_DESTINATION_TYPE_TIR;
-               if (group == MLX5E_RQ_GROUP_XSK)
-                       dst->tir_num = mlx5e_rx_res_get_tirn_xsk(priv->rx_res, ix);
-               else
-                       dst->tir_num = mlx5e_rx_res_get_tirn_direct(priv->rx_res, ix);
                flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
        }
 
 {
        if (eth_rule->rule)
                mlx5_del_flow_rules(eth_rule->rule);
+       if (eth_rule->rss)
+               mlx5e_rss_refcnt_dec(eth_rule->rss);
        list_del(ð_rule->list);
        priv->fs.ethtool.tot_num_rules--;
        put_flow_table(eth_rule->eth_ft);
                                        fs->ring_cookie))
                        return -EINVAL;
 
-       switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT)) {
+       switch (flow_type_mask(fs->flow_type)) {
        case ETHER_FLOW:
                num_tuples += validate_ethter(fs);
                break;
 
 static int
 mlx5e_ethtool_flow_replace(struct mlx5e_priv *priv,
-                          struct ethtool_rx_flow_spec *fs)
+                          struct ethtool_rx_flow_spec *fs, u32 rss_context)
 {
        struct mlx5e_ethtool_table *eth_ft;
        struct mlx5e_ethtool_rule *eth_rule;
                err = -EINVAL;
                goto del_ethtool_rule;
        }
-       rule = add_ethtool_flow_rule(priv, eth_ft->ft, fs);
+       rule = add_ethtool_flow_rule(priv, eth_rule, eth_ft->ft, fs, rss_context);
        if (IS_ERR(rule)) {
                err = PTR_ERR(rule);
                goto del_ethtool_rule;
                return -EINVAL;
 
        list_for_each_entry(eth_rule, &priv->fs.ethtool.rules, list) {
-               if (eth_rule->flow_spec.location == location) {
-                       info->fs = eth_rule->flow_spec;
+               int index;
+
+               if (eth_rule->flow_spec.location != location)
+                       continue;
+               if (!info)
                        return 0;
-               }
+               info->fs = eth_rule->flow_spec;
+               if (!eth_rule->rss)
+                       return 0;
+               index = mlx5e_rx_res_rss_index(priv->rx_res, eth_rule->rss);
+               if (index < 0)
+                       return index;
+               info->rss_context = index;
+               return 0;
        }
 
        return -ENOENT;
 
        info->data = MAX_NUM_OF_ETHTOOL_RULES;
        while ((!err || err == -ENOENT) && idx < info->rule_cnt) {
-               err = mlx5e_ethtool_get_flow(priv, info, location);
+               err = mlx5e_ethtool_get_flow(priv, NULL, location);
                if (!err)
                        rule_locs[idx++] = location;
                location++;
 
        switch (cmd->cmd) {
        case ETHTOOL_SRXCLSRLINS:
-               err = mlx5e_ethtool_flow_replace(priv, &cmd->fs);
+               err = mlx5e_ethtool_flow_replace(priv, &cmd->fs, cmd->rss_context);
                break;
        case ETHTOOL_SRXCLSRLDEL:
                err = mlx5e_ethtool_flow_remove(priv, cmd->fs.location);