1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 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);
}
|