aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/mellanox/mlx5/core/lib/geneve.c
blob: 23361a9ae4fa0c77af9e7e34d05058b64a368556 (plain)
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);
}