// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB /* Copyright (c) 2019 Mellanox Technologies. */ #include #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++; res = 0; } 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); }