1 /*
2 * Copyright (c) 2016, Mellanox Technologies, Ltd. All rights reserved.
3 *
4 * This software is available to you under a choice of one of two
5 * licenses. You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
8 * OpenIB.org BSD license below:
9 *
10 * Redistribution and use in source and binary forms, with or
11 * without modification, are permitted provided that the following
12 * conditions are met:
13 *
14 * - Redistributions of source code must retain the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer.
17 *
18 * - Redistributions in binary form must reproduce the above
19 * copyright notice, this list of conditions and the following
20 * disclaimer in the documentation and/or other materials
21 * provided with the distribution.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
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
41 struct mlx5_vxlan {
42 struct mlx5_core_dev *mdev;
43 spinlock_t lock; /* protect vxlan table */
44 /* max_num_ports is usuallly 4, 16 buckets is more than enough */
45 DECLARE_HASHTABLE(htable, 4);
46 int num_ports;
47 struct mutex sync_lock; /* sync add/del port HW operations */
48 };
49
50 struct mlx5_vxlan_port {
51 struct hlist_node hlist;
52 refcount_t refcount;
53 u16 udp_port;
54 };
55
mlx5_vxlan_max_udp_ports(struct mlx5_core_dev * mdev)56 static 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
mlx5_vxlan_core_add_port_cmd(struct mlx5_core_dev * mdev,u16 port)61 static 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
mlx5_vxlan_core_del_port_cmd(struct mlx5_core_dev * mdev,u16 port)72 static 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
83 static struct mlx5_vxlan_port*
mlx5_vxlan_lookup_port_locked(struct mlx5_vxlan * vxlan,u16 port)84 mlx5_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
mlx5_vxlan_lookup_port(struct mlx5_vxlan * vxlan,u16 port)96 struct 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
mlx5_vxlan_add_port(struct mlx5_vxlan * vxlan,u16 port)110 int 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
151 err_delete_port:
152 mlx5_vxlan_core_del_port_cmd(vxlan->mdev, port);
153
154 unlock:
155 mutex_unlock(&vxlan->sync_lock);
156 return ret;
157 }
158
mlx5_vxlan_del_port(struct mlx5_vxlan * vxlan,u16 port)159 int 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
179 out_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
mlx5_vxlan_create(struct mlx5_core_dev * mdev)193 struct 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 /* Hardware adds 4789 (IANA_VXLAN_UDP_PORT) by default */
210 mlx5_vxlan_add_port(vxlan, IANA_VXLAN_UDP_PORT);
211
212 return vxlan;
213 }
214
mlx5_vxlan_destroy(struct mlx5_vxlan * vxlan)215 void 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 /* Lockless since we are the only hash table consumers*/
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