Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright (c) 2016, Mellanox Technologies, Ltd.  All rights reserved.
0003  *
0004  * This software is available to you under a choice of one of two
0005  * licenses.  You may choose to be licensed under the terms of the GNU
0006  * General Public License (GPL) Version 2, available from the file
0007  * COPYING in the main directory of this source tree, or the
0008  * OpenIB.org BSD license below:
0009  *
0010  *     Redistribution and use in source and binary forms, with or
0011  *     without modification, are permitted provided that the following
0012  *     conditions are met:
0013  *
0014  *      - Redistributions of source code must retain the above
0015  *        copyright notice, this list of conditions and the following
0016  *        disclaimer.
0017  *
0018  *      - Redistributions in binary form must reproduce the above
0019  *        copyright notice, this list of conditions and the following
0020  *        disclaimer in the documentation and/or other materials
0021  *        provided with the distribution.
0022  *
0023  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
0024  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
0025  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
0026  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
0027  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
0028  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
0029  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
0030  * SOFTWARE.
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     /* max_num_ports is usually 4, 16 buckets is more than enough */
0043     DECLARE_HASHTABLE(htable, 4);
0044     struct mutex                    sync_lock; /* sync add/del port HW operations */
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     /* Hardware adds 4789 (IANA_VXLAN_UDP_PORT) by default */
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         /* Don't delete default UDP port added by the HW.
0190          * Remove only user configured ports
0191          */
0192         if (vxlanp->udp_port == IANA_VXLAN_UDP_PORT)
0193             continue;
0194         mlx5_vxlan_del_port(vxlan, vxlanp->udp_port);
0195     }
0196 }