0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033 #include <linux/kernel.h>
0034 #include <linux/refcount.h>
0035 #include <linux/mlx5/driver.h>
0036 #include <net/vxlan.h>
0037 #include "mlx5_core.h"
0038 #include "vxlan.h"
0039
0040 struct mlx5_vxlan {
0041 struct mlx5_core_dev *mdev;
0042
0043 DECLARE_HASHTABLE(htable, 4);
0044 struct mutex sync_lock;
0045 };
0046
0047 struct mlx5_vxlan_port {
0048 struct hlist_node hlist;
0049 u16 udp_port;
0050 };
0051
0052 static int mlx5_vxlan_core_add_port_cmd(struct mlx5_core_dev *mdev, u16 port)
0053 {
0054 u32 in[MLX5_ST_SZ_DW(add_vxlan_udp_dport_in)] = {};
0055
0056 MLX5_SET(add_vxlan_udp_dport_in, in, opcode,
0057 MLX5_CMD_OP_ADD_VXLAN_UDP_DPORT);
0058 MLX5_SET(add_vxlan_udp_dport_in, in, vxlan_udp_port, port);
0059 return mlx5_cmd_exec_in(mdev, add_vxlan_udp_dport, in);
0060 }
0061
0062 static int mlx5_vxlan_core_del_port_cmd(struct mlx5_core_dev *mdev, u16 port)
0063 {
0064 u32 in[MLX5_ST_SZ_DW(delete_vxlan_udp_dport_in)] = {};
0065
0066 MLX5_SET(delete_vxlan_udp_dport_in, in, opcode,
0067 MLX5_CMD_OP_DELETE_VXLAN_UDP_DPORT);
0068 MLX5_SET(delete_vxlan_udp_dport_in, in, vxlan_udp_port, port);
0069 return mlx5_cmd_exec_in(mdev, delete_vxlan_udp_dport, in);
0070 }
0071
0072 bool mlx5_vxlan_lookup_port(struct mlx5_vxlan *vxlan, u16 port)
0073 {
0074 struct mlx5_vxlan_port *vxlanp;
0075 bool found = false;
0076
0077 if (!mlx5_vxlan_allowed(vxlan))
0078 return NULL;
0079
0080 rcu_read_lock();
0081 hash_for_each_possible_rcu(vxlan->htable, vxlanp, hlist, port)
0082 if (vxlanp->udp_port == port) {
0083 found = true;
0084 break;
0085 }
0086 rcu_read_unlock();
0087
0088 return found;
0089 }
0090
0091 static struct mlx5_vxlan_port *vxlan_lookup_port(struct mlx5_vxlan *vxlan, u16 port)
0092 {
0093 struct mlx5_vxlan_port *vxlanp;
0094
0095 hash_for_each_possible(vxlan->htable, vxlanp, hlist, port)
0096 if (vxlanp->udp_port == port)
0097 return vxlanp;
0098 return NULL;
0099 }
0100
0101 int mlx5_vxlan_add_port(struct mlx5_vxlan *vxlan, u16 port)
0102 {
0103 struct mlx5_vxlan_port *vxlanp;
0104 int ret;
0105
0106 vxlanp = kzalloc(sizeof(*vxlanp), GFP_KERNEL);
0107 if (!vxlanp)
0108 return -ENOMEM;
0109 vxlanp->udp_port = port;
0110
0111 ret = mlx5_vxlan_core_add_port_cmd(vxlan->mdev, port);
0112 if (ret) {
0113 kfree(vxlanp);
0114 return ret;
0115 }
0116
0117 mutex_lock(&vxlan->sync_lock);
0118 hash_add_rcu(vxlan->htable, &vxlanp->hlist, port);
0119 mutex_unlock(&vxlan->sync_lock);
0120
0121 return 0;
0122 }
0123
0124 int mlx5_vxlan_del_port(struct mlx5_vxlan *vxlan, u16 port)
0125 {
0126 struct mlx5_vxlan_port *vxlanp;
0127 int ret = 0;
0128
0129 mutex_lock(&vxlan->sync_lock);
0130
0131 vxlanp = vxlan_lookup_port(vxlan, port);
0132 if (WARN_ON(!vxlanp)) {
0133 ret = -ENOENT;
0134 goto out_unlock;
0135 }
0136
0137 hash_del_rcu(&vxlanp->hlist);
0138 synchronize_rcu();
0139 mlx5_vxlan_core_del_port_cmd(vxlan->mdev, port);
0140 kfree(vxlanp);
0141
0142 out_unlock:
0143 mutex_unlock(&vxlan->sync_lock);
0144 return ret;
0145 }
0146
0147 struct mlx5_vxlan *mlx5_vxlan_create(struct mlx5_core_dev *mdev)
0148 {
0149 struct mlx5_vxlan *vxlan;
0150
0151 if (!MLX5_CAP_ETH(mdev, tunnel_stateless_vxlan) || !mlx5_core_is_pf(mdev))
0152 return ERR_PTR(-ENOTSUPP);
0153
0154 vxlan = kzalloc(sizeof(*vxlan), GFP_KERNEL);
0155 if (!vxlan)
0156 return ERR_PTR(-ENOMEM);
0157
0158 vxlan->mdev = mdev;
0159 mutex_init(&vxlan->sync_lock);
0160 hash_init(vxlan->htable);
0161
0162
0163 mlx5_vxlan_add_port(vxlan, IANA_VXLAN_UDP_PORT);
0164
0165 return vxlan;
0166 }
0167
0168 void mlx5_vxlan_destroy(struct mlx5_vxlan *vxlan)
0169 {
0170 if (!mlx5_vxlan_allowed(vxlan))
0171 return;
0172
0173 mlx5_vxlan_del_port(vxlan, IANA_VXLAN_UDP_PORT);
0174 WARN_ON(!hash_empty(vxlan->htable));
0175
0176 kfree(vxlan);
0177 }
0178
0179 void mlx5_vxlan_reset_to_default(struct mlx5_vxlan *vxlan)
0180 {
0181 struct mlx5_vxlan_port *vxlanp;
0182 struct hlist_node *tmp;
0183 int bkt;
0184
0185 if (!mlx5_vxlan_allowed(vxlan))
0186 return;
0187
0188 hash_for_each_safe(vxlan->htable, bkt, tmp, vxlanp, hlist) {
0189
0190
0191
0192 if (vxlanp->udp_port == IANA_VXLAN_UDP_PORT)
0193 continue;
0194 mlx5_vxlan_del_port(vxlan, vxlanp->udp_port);
0195 }
0196 }