IB/mlx5: Support adding flow steering rule by raw description
authorYishai Hadas <yishaih@mellanox.com>
Mon, 23 Jul 2018 12:25:10 +0000 (15:25 +0300)
committerJason Gunthorpe <jgg@mellanox.com>
Tue, 24 Jul 2018 20:03:56 +0000 (14:03 -0600)
Add support to set a public flow steering rule when its destination is a
TIR by using raw specification data.

The logic follows the verbs API but instead of using ib_spec(s) the raw,
device specific, description is used.

This allows supporting specialty matchers without having to define new
matches in the verbs struct based language.

Signed-off-by: Yishai Hadas <yishaih@mellanox.com>
Signed-off-by: Leon Romanovsky <leonro@mellanox.com>
Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
drivers/infiniband/hw/mlx5/main.c
drivers/infiniband/hw/mlx5/mlx5_ib.h

index 6c8b474..68d2801 100644 (file)
@@ -2978,11 +2978,11 @@ static void counters_clear_description(struct ib_counters *counters)
 
 static int mlx5_ib_destroy_flow(struct ib_flow *flow_id)
 {
-       struct mlx5_ib_dev *dev = to_mdev(flow_id->qp->device);
        struct mlx5_ib_flow_handler *handler = container_of(flow_id,
                                                          struct mlx5_ib_flow_handler,
                                                          ibflow);
        struct mlx5_ib_flow_handler *iter, *tmp;
+       struct mlx5_ib_dev *dev = handler->dev;
 
        mutex_lock(&dev->flow_db->lock);
 
@@ -3000,6 +3000,8 @@ static int mlx5_ib_destroy_flow(struct ib_flow *flow_id)
                counters_clear_description(handler->ibcounters);
 
        mutex_unlock(&dev->flow_db->lock);
+       if (handler->flow_matcher)
+               atomic_dec(&handler->flow_matcher->usecnt);
        kfree(handler);
 
        return 0;
@@ -3020,6 +3022,26 @@ enum flow_table_type {
 
 #define MLX5_FS_MAX_TYPES       6
 #define MLX5_FS_MAX_ENTRIES     BIT(16)
+
+static struct mlx5_ib_flow_prio *_get_prio(struct mlx5_flow_namespace *ns,
+                                          struct mlx5_ib_flow_prio *prio,
+                                          int priority,
+                                          int num_entries, int num_groups)
+{
+       struct mlx5_flow_table *ft;
+
+       ft = mlx5_create_auto_grouped_flow_table(ns, priority,
+                                                num_entries,
+                                                num_groups,
+                                                0, 0);
+       if (IS_ERR(ft))
+               return ERR_CAST(ft);
+
+       prio->flow_table = ft;
+       prio->refcount = 0;
+       return prio;
+}
+
 static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev,
                                                struct ib_flow_attr *flow_attr,
                                                enum flow_table_type ft_type)
@@ -3032,7 +3054,6 @@ static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev,
        int num_entries;
        int num_groups;
        int priority;
-       int err = 0;
 
        max_table_size = BIT(MLX5_CAP_FLOWTABLE_NIC_RX(dev->mdev,
                                                       log_max_ft_size));
@@ -3082,21 +3103,10 @@ static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev,
                return ERR_PTR(-ENOMEM);
 
        ft = prio->flow_table;
-       if (!ft) {
-               ft = mlx5_create_auto_grouped_flow_table(ns, priority,
-                                                        num_entries,
-                                                        num_groups,
-                                                        0, 0);
-
-               if (!IS_ERR(ft)) {
-                       prio->refcount = 0;
-                       prio->flow_table = ft;
-               } else {
-                       err = PTR_ERR(ft);
-               }
-       }
+       if (!ft)
+               return _get_prio(ns, prio, priority, num_entries, num_groups);
 
-       return err ? ERR_PTR(err) : prio;
+       return prio;
 }
 
 static void set_underlay_qp(struct mlx5_ib_dev *dev,
@@ -3355,6 +3365,7 @@ static struct mlx5_ib_flow_handler *_create_flow_rule(struct mlx5_ib_dev *dev,
 
        ft_prio->refcount++;
        handler->prio = ft_prio;
+       handler->dev = dev;
 
        ft_prio->flow_table = ft;
 free:
@@ -3641,13 +3652,184 @@ unlock:
        return ERR_PTR(err);
 }
 
+static struct mlx5_ib_flow_prio *_get_flow_table(struct mlx5_ib_dev *dev,
+                                                int priority, bool mcast)
+{
+       int max_table_size;
+       struct mlx5_flow_namespace *ns = NULL;
+       struct mlx5_ib_flow_prio *prio;
+
+       max_table_size = BIT(MLX5_CAP_FLOWTABLE_NIC_RX(dev->mdev,
+                            log_max_ft_size));
+       if (max_table_size < MLX5_FS_MAX_ENTRIES)
+               return ERR_PTR(-ENOMEM);
+
+       if (mcast)
+               priority = MLX5_IB_FLOW_MCAST_PRIO;
+       else
+               priority = ib_prio_to_core_prio(priority, false);
+
+       ns = mlx5_get_flow_namespace(dev->mdev, MLX5_FLOW_NAMESPACE_BYPASS);
+       if (!ns)
+               return ERR_PTR(-ENOTSUPP);
+
+       prio = &dev->flow_db->prios[priority];
+
+       if (prio->flow_table)
+               return prio;
+
+       return _get_prio(ns, prio, priority, MLX5_FS_MAX_ENTRIES,
+                        MLX5_FS_MAX_TYPES);
+}
+
+static struct mlx5_ib_flow_handler *
+_create_raw_flow_rule(struct mlx5_ib_dev *dev,
+                     struct mlx5_ib_flow_prio *ft_prio,
+                     struct mlx5_flow_destination *dst,
+                     struct mlx5_ib_flow_matcher  *fs_matcher,
+                     void *cmd_in, int inlen)
+{
+       struct mlx5_ib_flow_handler *handler;
+       struct mlx5_flow_act flow_act = {.flow_tag = MLX5_FS_DEFAULT_FLOW_TAG};
+       struct mlx5_flow_spec *spec;
+       struct mlx5_flow_table *ft = ft_prio->flow_table;
+       int err = 0;
+
+       spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
+       handler = kzalloc(sizeof(*handler), GFP_KERNEL);
+       if (!handler || !spec) {
+               err = -ENOMEM;
+               goto free;
+       }
+
+       INIT_LIST_HEAD(&handler->list);
+
+       memcpy(spec->match_value, cmd_in, inlen);
+       memcpy(spec->match_criteria, fs_matcher->matcher_mask.match_params,
+              fs_matcher->mask_len);
+       spec->match_criteria_enable = fs_matcher->match_criteria_enable;
+
+       flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+       handler->rule = mlx5_add_flow_rules(ft, spec,
+                                           &flow_act, dst, 1);
+
+       if (IS_ERR(handler->rule)) {
+               err = PTR_ERR(handler->rule);
+               goto free;
+       }
+
+       ft_prio->refcount++;
+       handler->prio = ft_prio;
+       handler->dev = dev;
+       ft_prio->flow_table = ft;
+
+free:
+       if (err)
+               kfree(handler);
+       kvfree(spec);
+       return err ? ERR_PTR(err) : handler;
+}
+
+static bool raw_fs_is_multicast(struct mlx5_ib_flow_matcher *fs_matcher,
+                               void *match_v)
+{
+       void *match_c;
+       void *match_v_set_lyr_2_4, *match_c_set_lyr_2_4;
+       void *dmac, *dmac_mask;
+       void *ipv4, *ipv4_mask;
+
+       if (!(fs_matcher->match_criteria_enable &
+             (1 << MATCH_CRITERIA_ENABLE_OUTER_BIT)))
+               return false;
+
+       match_c = fs_matcher->matcher_mask.match_params;
+       match_v_set_lyr_2_4 = MLX5_ADDR_OF(fte_match_param, match_v,
+                                          outer_headers);
+       match_c_set_lyr_2_4 = MLX5_ADDR_OF(fte_match_param, match_c,
+                                          outer_headers);
+
+       dmac = MLX5_ADDR_OF(fte_match_set_lyr_2_4, match_v_set_lyr_2_4,
+                           dmac_47_16);
+       dmac_mask = MLX5_ADDR_OF(fte_match_set_lyr_2_4, match_c_set_lyr_2_4,
+                                dmac_47_16);
+
+       if (is_multicast_ether_addr(dmac) &&
+           is_multicast_ether_addr(dmac_mask))
+               return true;
+
+       ipv4 = MLX5_ADDR_OF(fte_match_set_lyr_2_4, match_v_set_lyr_2_4,
+                           dst_ipv4_dst_ipv6.ipv4_layout.ipv4);
+
+       ipv4_mask = MLX5_ADDR_OF(fte_match_set_lyr_2_4, match_c_set_lyr_2_4,
+                                dst_ipv4_dst_ipv6.ipv4_layout.ipv4);
+
+       if (ipv4_is_multicast(*(__be32 *)(ipv4)) &&
+           ipv4_is_multicast(*(__be32 *)(ipv4_mask)))
+               return true;
+
+       return false;
+}
+
 struct mlx5_ib_flow_handler *
 mlx5_ib_raw_fs_rule_add(struct mlx5_ib_dev *dev,
                        struct mlx5_ib_flow_matcher *fs_matcher,
                        void *cmd_in, int inlen, int dest_id,
                        int dest_type)
 {
-       return ERR_PTR(-EOPNOTSUPP);
+       struct mlx5_flow_destination *dst;
+       struct mlx5_ib_flow_prio *ft_prio;
+       int priority = fs_matcher->priority;
+       struct mlx5_ib_flow_handler *handler;
+       bool mcast;
+       int err;
+
+       if (fs_matcher->flow_type != MLX5_IB_FLOW_TYPE_NORMAL)
+               return ERR_PTR(-EOPNOTSUPP);
+
+       if (fs_matcher->priority > MLX5_IB_FLOW_LAST_PRIO)
+               return ERR_PTR(-ENOMEM);
+
+       if (dest_type != MLX5_FLOW_DESTINATION_TYPE_TIR)
+               return ERR_PTR(-ENOTSUPP);
+
+       dst = kzalloc(sizeof(*dst), GFP_KERNEL);
+       if (!dst)
+               return ERR_PTR(-ENOMEM);
+
+       mcast = raw_fs_is_multicast(fs_matcher, cmd_in);
+       mutex_lock(&dev->flow_db->lock);
+
+       ft_prio = _get_flow_table(dev, priority, mcast);
+       if (IS_ERR(ft_prio)) {
+               err = PTR_ERR(ft_prio);
+               goto unlock;
+       }
+
+       dst->type = dest_type;
+       dst->tir_num = dest_id;
+       handler = _create_raw_flow_rule(dev, ft_prio, dst, fs_matcher, cmd_in,
+                                       inlen);
+
+       if (IS_ERR(handler)) {
+               err = PTR_ERR(handler);
+               goto destroy_ft;
+       }
+
+       mutex_unlock(&dev->flow_db->lock);
+       atomic_inc(&fs_matcher->usecnt);
+       handler->flow_matcher = fs_matcher;
+
+       kfree(dst);
+
+       return handler;
+
+destroy_ft:
+       put_flow_table(dev, ft_prio, false);
+unlock:
+       mutex_unlock(&dev->flow_db->lock);
+       kfree(dst);
+
+       return ERR_PTR(err);
 }
 
 static u32 mlx5_ib_flow_action_flags_to_accel_xfrm_flags(u32 mlx5_flags)
index 324f4ea..33948b5 100644 (file)
@@ -172,6 +172,8 @@ struct mlx5_ib_flow_handler {
        struct mlx5_ib_flow_prio        *prio;
        struct mlx5_flow_handle         *rule;
        struct ib_counters              *ibcounters;
+       struct mlx5_ib_dev              *dev;
+       struct mlx5_ib_flow_matcher     *flow_matcher;
 };
 
 struct mlx5_ib_flow_matcher {