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#include <linux/kernel.h>
34#include <linux/module.h>
35#include <linux/refcount.h>
36#include <linux/mlx5/driver.h>
37#include <net/vxlan.h>
38#include "mlx5_core.h"
39#include "vxlan.h"
40
41struct mlx5_vxlan {
42 struct mlx5_core_dev *mdev;
43 spinlock_t lock;
44
45 DECLARE_HASHTABLE(htable, 4);
46 int num_ports;
47 struct mutex sync_lock;
48};
49
50struct mlx5_vxlan_port {
51 struct hlist_node hlist;
52 refcount_t refcount;
53 u16 udp_port;
54};
55
56static inline u8 mlx5_vxlan_max_udp_ports(struct mlx5_core_dev *mdev)
57{
58 return MLX5_CAP_ETH(mdev, max_vxlan_udp_ports) ?: 4;
59}
60
61static int mlx5_vxlan_core_add_port_cmd(struct mlx5_core_dev *mdev, u16 port)
62{
63 u32 in[MLX5_ST_SZ_DW(add_vxlan_udp_dport_in)] = {0};
64 u32 out[MLX5_ST_SZ_DW(add_vxlan_udp_dport_out)] = {0};
65
66 MLX5_SET(add_vxlan_udp_dport_in, in, opcode,
67 MLX5_CMD_OP_ADD_VXLAN_UDP_DPORT);
68 MLX5_SET(add_vxlan_udp_dport_in, in, vxlan_udp_port, port);
69 return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
70}
71
72static int mlx5_vxlan_core_del_port_cmd(struct mlx5_core_dev *mdev, u16 port)
73{
74 u32 in[MLX5_ST_SZ_DW(delete_vxlan_udp_dport_in)] = {0};
75 u32 out[MLX5_ST_SZ_DW(delete_vxlan_udp_dport_out)] = {0};
76
77 MLX5_SET(delete_vxlan_udp_dport_in, in, opcode,
78 MLX5_CMD_OP_DELETE_VXLAN_UDP_DPORT);
79 MLX5_SET(delete_vxlan_udp_dport_in, in, vxlan_udp_port, port);
80 return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
81}
82
83static struct mlx5_vxlan_port*
84mlx5_vxlan_lookup_port_locked(struct mlx5_vxlan *vxlan, u16 port)
85{
86 struct mlx5_vxlan_port *vxlanp;
87
88 hash_for_each_possible(vxlan->htable, vxlanp, hlist, port) {
89 if (vxlanp->udp_port == port)
90 return vxlanp;
91 }
92
93 return NULL;
94}
95
96struct mlx5_vxlan_port *mlx5_vxlan_lookup_port(struct mlx5_vxlan *vxlan, u16 port)
97{
98 struct mlx5_vxlan_port *vxlanp;
99
100 if (!mlx5_vxlan_allowed(vxlan))
101 return NULL;
102
103 spin_lock_bh(&vxlan->lock);
104 vxlanp = mlx5_vxlan_lookup_port_locked(vxlan, port);
105 spin_unlock_bh(&vxlan->lock);
106
107 return vxlanp;
108}
109
110int mlx5_vxlan_add_port(struct mlx5_vxlan *vxlan, u16 port)
111{
112 struct mlx5_vxlan_port *vxlanp;
113 int ret = -ENOSPC;
114
115 vxlanp = mlx5_vxlan_lookup_port(vxlan, port);
116 if (vxlanp) {
117 refcount_inc(&vxlanp->refcount);
118 return 0;
119 }
120
121 mutex_lock(&vxlan->sync_lock);
122 if (vxlan->num_ports >= mlx5_vxlan_max_udp_ports(vxlan->mdev)) {
123 mlx5_core_info(vxlan->mdev,
124 "UDP port (%d) not offloaded, max number of UDP ports (%d) are already offloaded\n",
125 port, mlx5_vxlan_max_udp_ports(vxlan->mdev));
126 ret = -ENOSPC;
127 goto unlock;
128 }
129
130 ret = mlx5_vxlan_core_add_port_cmd(vxlan->mdev, port);
131 if (ret)
132 goto unlock;
133
134 vxlanp = kzalloc(sizeof(*vxlanp), GFP_KERNEL);
135 if (!vxlanp) {
136 ret = -ENOMEM;
137 goto err_delete_port;
138 }
139
140 vxlanp->udp_port = port;
141 refcount_set(&vxlanp->refcount, 1);
142
143 spin_lock_bh(&vxlan->lock);
144 hash_add(vxlan->htable, &vxlanp->hlist, port);
145 spin_unlock_bh(&vxlan->lock);
146
147 vxlan->num_ports++;
148 mutex_unlock(&vxlan->sync_lock);
149 return 0;
150
151err_delete_port:
152 mlx5_vxlan_core_del_port_cmd(vxlan->mdev, port);
153
154unlock:
155 mutex_unlock(&vxlan->sync_lock);
156 return ret;
157}
158
159int mlx5_vxlan_del_port(struct mlx5_vxlan *vxlan, u16 port)
160{
161 struct mlx5_vxlan_port *vxlanp;
162 bool remove = false;
163 int ret = 0;
164
165 mutex_lock(&vxlan->sync_lock);
166
167 spin_lock_bh(&vxlan->lock);
168 vxlanp = mlx5_vxlan_lookup_port_locked(vxlan, port);
169 if (!vxlanp) {
170 ret = -ENOENT;
171 goto out_unlock;
172 }
173
174 if (refcount_dec_and_test(&vxlanp->refcount)) {
175 hash_del(&vxlanp->hlist);
176 remove = true;
177 }
178
179out_unlock:
180 spin_unlock_bh(&vxlan->lock);
181
182 if (remove) {
183 mlx5_vxlan_core_del_port_cmd(vxlan->mdev, port);
184 kfree(vxlanp);
185 vxlan->num_ports--;
186 }
187
188 mutex_unlock(&vxlan->sync_lock);
189
190 return ret;
191}
192
193struct mlx5_vxlan *mlx5_vxlan_create(struct mlx5_core_dev *mdev)
194{
195 struct mlx5_vxlan *vxlan;
196
197 if (!MLX5_CAP_ETH(mdev, tunnel_stateless_vxlan) || !mlx5_core_is_pf(mdev))
198 return ERR_PTR(-ENOTSUPP);
199
200 vxlan = kzalloc(sizeof(*vxlan), GFP_KERNEL);
201 if (!vxlan)
202 return ERR_PTR(-ENOMEM);
203
204 vxlan->mdev = mdev;
205 mutex_init(&vxlan->sync_lock);
206 spin_lock_init(&vxlan->lock);
207 hash_init(vxlan->htable);
208
209
210 mlx5_vxlan_add_port(vxlan, IANA_VXLAN_UDP_PORT);
211
212 return vxlan;
213}
214
215void mlx5_vxlan_destroy(struct mlx5_vxlan *vxlan)
216{
217 struct mlx5_vxlan_port *vxlanp;
218 struct hlist_node *tmp;
219 int bkt;
220
221 if (!mlx5_vxlan_allowed(vxlan))
222 return;
223
224
225 hash_for_each_safe(vxlan->htable, bkt, tmp, vxlanp, hlist) {
226 hash_del(&vxlanp->hlist);
227 mlx5_vxlan_core_del_port_cmd(vxlan->mdev, vxlanp->udp_port);
228 kfree(vxlanp);
229 }
230
231 kfree(vxlan);
232}
233