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