xref: /linux/drivers/net/ethernet/mellanox/mlx5/core/lib/geneve.c (revision 42874e4eb35bdfc54f8514685e50434098ba4f6c)
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