net/mlx5: Geneve, Manage Geneve TLV options
authorYevgeny Kliteynik <kliteyn@mellanox.com>
Wed, 30 Jan 2019 15:21:55 +0000 (17:21 +0200)
committerSaeed Mahameed <saeedm@mellanox.com>
Fri, 31 May 2019 20:04:25 +0000 (13:04 -0700)
Use Geneve TLV Options object to manage the flex parser matching
on the 32-bit options data.

When the first flow with a certain class/type values is requested to
be offloaded, create a FW object with FW command (Geneve TLV Options
general object) and start counting the number of flows using this object.

During this time, any request with a different class/type values will
fail to be offloaded.
Once the refcount reaches 0, destroy the TLV options general object,
and can now offload a flow with any class/type parameters.

Geneve TLV Options object is added to core device.
It is currently used to manage Geneve TLV options general
object allocation in FW and its reference counting only.
In the future it will also be used for managing geneve ports
by registering callbacks for ndo_udp_tunnel_add/del.

Reviewed-by: Oz Shlomo <ozsh@mellanox.com>
Signed-off-by: Yevgeny Kliteynik <kliteyn@mellanox.com>
Signed-off-by: Saeed Mahameed <saeedm@mellanox.com>
drivers/net/ethernet/mellanox/mlx5/core/Makefile
drivers/net/ethernet/mellanox/mlx5/core/lib/geneve.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/lib/geneve.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/main.c
include/linux/mlx5/driver.h

index 243368dc23db087c2b39de0531c431cb6ec8395d..e31027277a6e8ef1ac3e7284bca5364b9f2f7eb4 100644 (file)
@@ -31,7 +31,8 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \
 mlx5_core-$(CONFIG_MLX5_EN_ARFS)     += en_arfs.o
 mlx5_core-$(CONFIG_MLX5_EN_RXNFC)    += en_fs_ethtool.o
 mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o en/port_buffer.o
-mlx5_core-$(CONFIG_MLX5_ESWITCH)     += en_rep.o en_tc.o en/tc_tun.o lib/port_tun.o lag_mp.o
+mlx5_core-$(CONFIG_MLX5_ESWITCH)     += en_rep.o en_tc.o en/tc_tun.o lib/port_tun.o lag_mp.o \
+                                       lib/geneve.o
 
 #
 # Core extra
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/geneve.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/geneve.c
new file mode 100644 (file)
index 0000000..23361a9
--- /dev/null
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* Copyright (c) 2019 Mellanox Technologies. */
+
+#include <linux/kernel.h>
+#include "mlx5_core.h"
+#include "geneve.h"
+
+struct mlx5_geneve {
+       struct mlx5_core_dev *mdev;
+       __be16 opt_class;
+       u8 opt_type;
+       u32 obj_id;
+       struct mutex sync_lock; /* protect GENEVE obj operations */
+       u32 refcount;
+};
+
+static int mlx5_geneve_tlv_option_create(struct mlx5_core_dev *mdev,
+                                        __be16 class,
+                                        u8 type,
+                                        u8 len)
+{
+       u32 in[MLX5_ST_SZ_DW(create_geneve_tlv_option_in)] = {};
+       u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)] = {};
+       u64 general_obj_types;
+       void *hdr, *opt;
+       u16 obj_id;
+       int err;
+
+       general_obj_types = MLX5_CAP_GEN_64(mdev, general_obj_types);
+       if (!(general_obj_types & MLX5_GENERAL_OBJ_TYPES_CAP_GENEVE_TLV_OPT))
+               return -EINVAL;
+
+       hdr = MLX5_ADDR_OF(create_geneve_tlv_option_in, in, hdr);
+       opt = MLX5_ADDR_OF(create_geneve_tlv_option_in, in, geneve_tlv_opt);
+
+       MLX5_SET(general_obj_in_cmd_hdr, hdr, opcode, MLX5_CMD_OP_CREATE_GENERAL_OBJECT);
+       MLX5_SET(general_obj_in_cmd_hdr, hdr, obj_type, MLX5_OBJ_TYPE_GENEVE_TLV_OPT);
+
+       MLX5_SET(geneve_tlv_option, opt, option_class, be16_to_cpu(class));
+       MLX5_SET(geneve_tlv_option, opt, option_type, type);
+       MLX5_SET(geneve_tlv_option, opt, option_data_length, len);
+
+       err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+       if (err)
+               return err;
+
+       obj_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id);
+       return obj_id;
+}
+
+static void mlx5_geneve_tlv_option_destroy(struct mlx5_core_dev *mdev, u16 obj_id)
+{
+       u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)] = {};
+       u32 in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {};
+
+       MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_DESTROY_GENERAL_OBJECT);
+       MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_OBJ_TYPE_GENEVE_TLV_OPT);
+       MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, obj_id);
+
+       mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
+}
+
+int mlx5_geneve_tlv_option_add(struct mlx5_geneve *geneve, struct geneve_opt *opt)
+{
+       int res = 0;
+
+       if (IS_ERR_OR_NULL(geneve))
+               return -EOPNOTSUPP;
+
+       mutex_lock(&geneve->sync_lock);
+
+       if (geneve->refcount) {
+               if (geneve->opt_class == opt->opt_class &&
+                   geneve->opt_type == opt->type) {
+                       /* We already have TLV options obj allocated */
+                       geneve->refcount++;
+               } else {
+                       /* TLV options obj allocated, but its params
+                        * do not match the new request.
+                        * We support only one such object.
+                        */
+                       mlx5_core_warn(geneve->mdev,
+                                      "Won't create Geneve TLV opt object with class:type:len = 0x%x:0x%x:%d (another class:type already exists)\n",
+                                      be16_to_cpu(opt->opt_class),
+                                      opt->type,
+                                      opt->length);
+                       res = -EOPNOTSUPP;
+                       goto unlock;
+               }
+       } else {
+               /* We don't have any TLV options obj allocated */
+
+               res = mlx5_geneve_tlv_option_create(geneve->mdev,
+                                                   opt->opt_class,
+                                                   opt->type,
+                                                   opt->length);
+               if (res < 0) {
+                       mlx5_core_warn(geneve->mdev,
+                                      "Failed creating Geneve TLV opt object class:type:len = 0x%x:0x%x:%d (err=%d)\n",
+                                      be16_to_cpu(opt->opt_class),
+                                      opt->type, opt->length, res);
+                       goto unlock;
+               }
+               geneve->opt_class = opt->opt_class;
+               geneve->opt_type = opt->type;
+               geneve->obj_id = res;
+               geneve->refcount++;
+       }
+
+unlock:
+       mutex_unlock(&geneve->sync_lock);
+       return res;
+}
+
+void mlx5_geneve_tlv_option_del(struct mlx5_geneve *geneve)
+{
+       if (IS_ERR_OR_NULL(geneve))
+               return;
+
+       mutex_lock(&geneve->sync_lock);
+       if (--geneve->refcount == 0) {
+               /* We've just removed the last user of Geneve option.
+                * Now delete the object in FW.
+                */
+               mlx5_geneve_tlv_option_destroy(geneve->mdev, geneve->obj_id);
+
+               geneve->opt_class = 0;
+               geneve->opt_type = 0;
+               geneve->obj_id = 0;
+       }
+       mutex_unlock(&geneve->sync_lock);
+}
+
+struct mlx5_geneve *mlx5_geneve_create(struct mlx5_core_dev *mdev)
+{
+       struct mlx5_geneve *geneve =
+               kzalloc(sizeof(*geneve), GFP_KERNEL);
+
+       if (!geneve)
+               return ERR_PTR(-ENOMEM);
+       geneve->mdev = mdev;
+       mutex_init(&geneve->sync_lock);
+
+       return geneve;
+}
+
+void mlx5_geneve_destroy(struct mlx5_geneve *geneve)
+{
+       if (IS_ERR_OR_NULL(geneve))
+               return;
+
+       /* Lockless since we are unloading */
+       if (geneve->refcount)
+               mlx5_geneve_tlv_option_destroy(geneve->mdev, geneve->obj_id);
+
+       kfree(geneve);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/geneve.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/geneve.h
new file mode 100644 (file)
index 0000000..adee0cb
--- /dev/null
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2019 Mellanox Technologies. */
+
+#ifndef __MLX5_GENEVE_H__
+#define __MLX5_GENEVE_H__
+
+#include <net/geneve.h>
+#include <linux/mlx5/driver.h>
+
+struct mlx5_geneve;
+
+#ifdef CONFIG_MLX5_ESWITCH
+
+struct mlx5_geneve *mlx5_geneve_create(struct mlx5_core_dev *mdev);
+void mlx5_geneve_destroy(struct mlx5_geneve *geneve);
+
+int mlx5_geneve_tlv_option_add(struct mlx5_geneve *geneve, struct geneve_opt *opt);
+void mlx5_geneve_tlv_option_del(struct mlx5_geneve *geneve);
+
+#else /* CONFIG_MLX5_ESWITCH */
+
+static inline struct mlx5_geneve
+*mlx5_geneve_create(struct mlx5_core_dev *mdev) { return NULL; }
+static inline void
+mlx5_geneve_destroy(struct mlx5_geneve *geneve) {}
+static inline int
+mlx5_geneve_tlv_option_add(struct mlx5_geneve *geneve, struct geneve_opt *opt) { return 0; }
+static inline void
+mlx5_geneve_tlv_option_del(struct mlx5_geneve *geneve) {}
+
+#endif /* CONFIG_MLX5_ESWITCH */
+
+#endif /* __MLX5_GENEVE_H__ */
index 23d53163ce15a9ed8e3ff52457d41040fe2aab4f..b27f9537256c52c1c1f4c1212276678ed38cd7d1 100644 (file)
@@ -63,6 +63,7 @@
 #include "accel/tls.h"
 #include "lib/clock.h"
 #include "lib/vxlan.h"
+#include "lib/geneve.h"
 #include "lib/devcom.h"
 #include "diag/fw_tracer.h"
 #include "ecpf.h"
@@ -821,6 +822,7 @@ static int mlx5_init_once(struct mlx5_core_dev *dev)
        mlx5_init_clock(dev);
 
        dev->vxlan = mlx5_vxlan_create(dev);
+       dev->geneve = mlx5_geneve_create(dev);
 
        err = mlx5_init_rl_table(dev);
        if (err) {
@@ -865,6 +867,7 @@ err_mpfs_cleanup:
 err_rl_cleanup:
        mlx5_cleanup_rl_table(dev);
 err_tables_cleanup:
+       mlx5_geneve_destroy(dev->geneve);
        mlx5_vxlan_destroy(dev->vxlan);
        mlx5_cleanup_mkey_table(dev);
        mlx5_cleanup_qp_table(dev);
@@ -887,6 +890,7 @@ static void mlx5_cleanup_once(struct mlx5_core_dev *dev)
        mlx5_eswitch_cleanup(dev->priv.eswitch);
        mlx5_mpfs_cleanup(dev);
        mlx5_cleanup_rl_table(dev);
+       mlx5_geneve_destroy(dev->geneve);
        mlx5_vxlan_destroy(dev->vxlan);
        mlx5_cleanup_clock(dev);
        mlx5_cleanup_reserved_gids(dev);
index b5431f7d97cbb8b24414eea50be28cbb05c271fe..3a810bf043fec9ab9cdfc5deb1e37b6498befe8a 100644 (file)
@@ -647,6 +647,7 @@ struct mlx5_clock {
 
 struct mlx5_fw_tracer;
 struct mlx5_vxlan;
+struct mlx5_geneve;
 
 struct mlx5_core_dev {
        struct device *device;
@@ -681,6 +682,7 @@ struct mlx5_core_dev {
        u32                     issi;
        struct mlx5e_resources  mlx5e_res;
        struct mlx5_vxlan       *vxlan;
+       struct mlx5_geneve      *geneve;
        struct {
                struct mlx5_rsvd_gids   reserved_gids;
                u32                     roce_en;