1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2 /* Copyright (c) 2019 Mellanox Technologies. */ 3 4 #include <linux/kernel.h> 5 #include "mlx5_core.h" 6 #include "geneve.h" 7 8 struct mlx5_geneve { 9 struct mlx5_core_dev *mdev; 10 __be16 opt_class; 11 u8 opt_type; 12 u32 obj_id; 13 struct mutex sync_lock; /* protect GENEVE obj operations */ 14 u32 refcount; 15 }; 16 17 static int mlx5_geneve_tlv_option_create(struct mlx5_core_dev *mdev, 18 __be16 class, 19 u8 type, 20 u8 len) 21 { 22 u32 in[MLX5_ST_SZ_DW(create_geneve_tlv_option_in)] = {}; 23 u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)] = {}; 24 u64 general_obj_types; 25 void *hdr, *opt; 26 u16 obj_id; 27 int err; 28 29 general_obj_types = MLX5_CAP_GEN_64(mdev, general_obj_types); 30 if (!(general_obj_types & MLX5_GENERAL_OBJ_TYPES_CAP_GENEVE_TLV_OPT)) 31 return -EINVAL; 32 33 hdr = MLX5_ADDR_OF(create_geneve_tlv_option_in, in, hdr); 34 opt = MLX5_ADDR_OF(create_geneve_tlv_option_in, in, geneve_tlv_opt); 35 36 MLX5_SET(general_obj_in_cmd_hdr, hdr, opcode, MLX5_CMD_OP_CREATE_GENERAL_OBJECT); 37 MLX5_SET(general_obj_in_cmd_hdr, hdr, obj_type, MLX5_OBJ_TYPE_GENEVE_TLV_OPT); 38 39 MLX5_SET(geneve_tlv_option, opt, option_class, be16_to_cpu(class)); 40 MLX5_SET(geneve_tlv_option, opt, option_type, type); 41 MLX5_SET(geneve_tlv_option, opt, option_data_length, len); 42 43 err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); 44 if (err) 45 return err; 46 47 obj_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id); 48 return obj_id; 49 } 50 51 static void mlx5_geneve_tlv_option_destroy(struct mlx5_core_dev *mdev, u16 obj_id) 52 { 53 u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)] = {}; 54 u32 in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {}; 55 56 MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_DESTROY_GENERAL_OBJECT); 57 MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_OBJ_TYPE_GENEVE_TLV_OPT); 58 MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, obj_id); 59 60 mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); 61 } 62 63 int mlx5_geneve_tlv_option_add(struct mlx5_geneve *geneve, struct geneve_opt *opt) 64 { 65 int res = 0; 66 67 if (IS_ERR_OR_NULL(geneve)) 68 return -EOPNOTSUPP; 69 70 mutex_lock(&geneve->sync_lock); 71 72 if (geneve->refcount) { 73 if (geneve->opt_class == opt->opt_class && 74 geneve->opt_type == opt->type) { 75 /* We already have TLV options obj allocated */ 76 geneve->refcount++; 77 } else { 78 /* TLV options obj allocated, but its params 79 * do not match the new request. 80 * We support only one such object. 81 */ 82 mlx5_core_warn(geneve->mdev, 83 "Won't create Geneve TLV opt object with class:type:len = 0x%x:0x%x:%d (another class:type already exists)\n", 84 be16_to_cpu(opt->opt_class), 85 opt->type, 86 opt->length); 87 res = -EOPNOTSUPP; 88 goto unlock; 89 } 90 } else { 91 /* We don't have any TLV options obj allocated */ 92 93 res = mlx5_geneve_tlv_option_create(geneve->mdev, 94 opt->opt_class, 95 opt->type, 96 opt->length); 97 if (res < 0) { 98 mlx5_core_warn(geneve->mdev, 99 "Failed creating Geneve TLV opt object class:type:len = 0x%x:0x%x:%d (err=%d)\n", 100 be16_to_cpu(opt->opt_class), 101 opt->type, opt->length, res); 102 goto unlock; 103 } 104 geneve->opt_class = opt->opt_class; 105 geneve->opt_type = opt->type; 106 geneve->obj_id = res; 107 geneve->refcount++; 108 res = 0; 109 } 110 111 unlock: 112 mutex_unlock(&geneve->sync_lock); 113 return res; 114 } 115 116 void mlx5_geneve_tlv_option_del(struct mlx5_geneve *geneve) 117 { 118 if (IS_ERR_OR_NULL(geneve)) 119 return; 120 121 mutex_lock(&geneve->sync_lock); 122 if (--geneve->refcount == 0) { 123 /* We've just removed the last user of Geneve option. 124 * Now delete the object in FW. 125 */ 126 mlx5_geneve_tlv_option_destroy(geneve->mdev, geneve->obj_id); 127 128 geneve->opt_class = 0; 129 geneve->opt_type = 0; 130 geneve->obj_id = 0; 131 } 132 mutex_unlock(&geneve->sync_lock); 133 } 134 135 struct mlx5_geneve *mlx5_geneve_create(struct mlx5_core_dev *mdev) 136 { 137 struct mlx5_geneve *geneve = 138 kzalloc(sizeof(*geneve), GFP_KERNEL); 139 140 if (!geneve) 141 return ERR_PTR(-ENOMEM); 142 geneve->mdev = mdev; 143 mutex_init(&geneve->sync_lock); 144 145 return geneve; 146 } 147 148 void mlx5_geneve_destroy(struct mlx5_geneve *geneve) 149 { 150 if (IS_ERR_OR_NULL(geneve)) 151 return; 152 153 /* Lockless since we are unloading */ 154 if (geneve->refcount) 155 mlx5_geneve_tlv_option_destroy(geneve->mdev, geneve->obj_id); 156 157 kfree(geneve); 158 } 159