Linux-libre 5.4.39-gnu
[librecmc/linux-libre.git] / drivers / net / ethernet / mellanox / mlx5 / core / lib / geneve.c
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         }
109
110 unlock:
111         mutex_unlock(&geneve->sync_lock);
112         return res;
113 }
114
115 void mlx5_geneve_tlv_option_del(struct mlx5_geneve *geneve)
116 {
117         if (IS_ERR_OR_NULL(geneve))
118                 return;
119
120         mutex_lock(&geneve->sync_lock);
121         if (--geneve->refcount == 0) {
122                 /* We've just removed the last user of Geneve option.
123                  * Now delete the object in FW.
124                  */
125                 mlx5_geneve_tlv_option_destroy(geneve->mdev, geneve->obj_id);
126
127                 geneve->opt_class = 0;
128                 geneve->opt_type = 0;
129                 geneve->obj_id = 0;
130         }
131         mutex_unlock(&geneve->sync_lock);
132 }
133
134 struct mlx5_geneve *mlx5_geneve_create(struct mlx5_core_dev *mdev)
135 {
136         struct mlx5_geneve *geneve =
137                 kzalloc(sizeof(*geneve), GFP_KERNEL);
138
139         if (!geneve)
140                 return ERR_PTR(-ENOMEM);
141         geneve->mdev = mdev;
142         mutex_init(&geneve->sync_lock);
143
144         return geneve;
145 }
146
147 void mlx5_geneve_destroy(struct mlx5_geneve *geneve)
148 {
149         if (IS_ERR_OR_NULL(geneve))
150                 return;
151
152         /* Lockless since we are unloading */
153         if (geneve->refcount)
154                 mlx5_geneve_tlv_option_destroy(geneve->mdev, geneve->obj_id);
155
156         kfree(geneve);
157 }