net/mlx5e: Vxlan, rename struct mlx5e_vxlan to mlx5_vxlan_port
[sfrench/cifs-2.6.git] / drivers / net / ethernet / mellanox / mlx5 / core / vxlan.c
index 2f74953e4561511e23d8fe3219db89104e3dd9e3..a2b48ad77f26ad552792196fb9e945fc67fde8d3 100644 (file)
 #include "mlx5_core.h"
 #include "vxlan.h"
 
+struct mlx5_vxlan_port {
+       struct hlist_node hlist;
+       atomic_t refcount;
+       u16 udp_port;
+};
+
 void mlx5e_vxlan_init(struct mlx5e_priv *priv)
 {
        struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan;
 
        spin_lock_init(&vxlan_db->lock);
-       INIT_RADIX_TREE(&vxlan_db->tree, GFP_ATOMIC);
+       hash_init(vxlan_db->htable);
+
+       if (mlx5e_vxlan_allowed(priv->mdev))
+               /* Hardware adds 4789 by default.
+                * Lockless since we are the only hash table consumers, wq and TX are disabled.
+                */
+               mlx5e_vxlan_add_port(priv, 4789);
+}
+
+static inline u8 mlx5e_vxlan_max_udp_ports(struct mlx5_core_dev *mdev)
+{
+       return MLX5_CAP_ETH(mdev, max_vxlan_udp_ports) ?: 4;
 }
 
 static int mlx5e_vxlan_core_add_port_cmd(struct mlx5_core_dev *mdev, u16 port)
@@ -66,80 +83,84 @@ static int mlx5e_vxlan_core_del_port_cmd(struct mlx5_core_dev *mdev, u16 port)
        return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
 }
 
-struct mlx5e_vxlan *mlx5e_vxlan_lookup_port(struct mlx5e_priv *priv, u16 port)
+static struct mlx5_vxlan_port*
+mlx5e_vxlan_lookup_port_locked(struct mlx5e_priv *priv, u16 port)
+{
+       struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan;
+       struct mlx5_vxlan_port *vxlanp;
+
+       hash_for_each_possible(vxlan_db->htable, vxlanp, hlist, port) {
+               if (vxlanp->udp_port == port)
+                       return vxlanp;
+       }
+
+       return NULL;
+}
+
+struct mlx5_vxlan_port *mlx5e_vxlan_lookup_port(struct mlx5e_priv *priv, u16 port)
 {
        struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan;
-       struct mlx5e_vxlan *vxlan;
+       struct mlx5_vxlan_port *vxlanp;
 
        spin_lock_bh(&vxlan_db->lock);
-       vxlan = radix_tree_lookup(&vxlan_db->tree, port);
+       vxlanp = mlx5e_vxlan_lookup_port_locked(priv, port);
        spin_unlock_bh(&vxlan_db->lock);
 
-       return vxlan;
+       return vxlanp;
 }
 
-static void mlx5e_vxlan_add_port(struct work_struct *work)
+void mlx5e_vxlan_add_port(struct mlx5e_priv *priv, u16 port)
 {
-       struct mlx5e_vxlan_work *vxlan_work =
-               container_of(work, struct mlx5e_vxlan_work, work);
-       struct mlx5e_priv *priv = vxlan_work->priv;
        struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan;
-       u16 port = vxlan_work->port;
-       struct mlx5e_vxlan *vxlan;
-       int err;
-
-       mutex_lock(&priv->state_lock);
-       vxlan = mlx5e_vxlan_lookup_port(priv, port);
-       if (vxlan) {
-               atomic_inc(&vxlan->refcount);
-               goto free_work;
+       struct mlx5_vxlan_port *vxlanp;
+
+       vxlanp = mlx5e_vxlan_lookup_port(priv, port);
+       if (vxlanp) {
+               atomic_inc(&vxlanp->refcount);
+               return;
+       }
+
+       if (vxlan_db->num_ports >= mlx5e_vxlan_max_udp_ports(priv->mdev)) {
+               netdev_info(priv->netdev,
+                           "UDP port (%d) not offloaded, max number of UDP ports (%d) are already offloaded\n",
+                           port, mlx5e_vxlan_max_udp_ports(priv->mdev));
+               return;
        }
 
        if (mlx5e_vxlan_core_add_port_cmd(priv->mdev, port))
-               goto free_work;
+               return;
 
-       vxlan = kzalloc(sizeof(*vxlan), GFP_KERNEL);
-       if (!vxlan)
+       vxlanp = kzalloc(sizeof(*vxlanp), GFP_KERNEL);
+       if (!vxlanp)
                goto err_delete_port;
 
-       vxlan->udp_port = port;
-       atomic_set(&vxlan->refcount, 1);
+       vxlanp->udp_port = port;
+       atomic_set(&vxlanp->refcount, 1);
 
        spin_lock_bh(&vxlan_db->lock);
-       err = radix_tree_insert(&vxlan_db->tree, vxlan->udp_port, vxlan);
+       hash_add(vxlan_db->htable, &vxlanp->hlist, port);
        spin_unlock_bh(&vxlan_db->lock);
-       if (err)
-               goto err_free;
 
-       goto free_work;
+       vxlan_db->num_ports++;
+       return;
 
-err_free:
-       kfree(vxlan);
 err_delete_port:
        mlx5e_vxlan_core_del_port_cmd(priv->mdev, port);
-free_work:
-       mutex_unlock(&priv->state_lock);
-       kfree(vxlan_work);
 }
 
-static void mlx5e_vxlan_del_port(struct work_struct *work)
+void mlx5e_vxlan_del_port(struct mlx5e_priv *priv, u16 port)
 {
-       struct mlx5e_vxlan_work *vxlan_work =
-               container_of(work, struct mlx5e_vxlan_work, work);
-       struct mlx5e_priv *priv         = vxlan_work->priv;
        struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan;
-       u16 port = vxlan_work->port;
-       struct mlx5e_vxlan *vxlan;
+       struct mlx5_vxlan_port *vxlanp;
        bool remove = false;
 
-       mutex_lock(&priv->state_lock);
        spin_lock_bh(&vxlan_db->lock);
-       vxlan = radix_tree_lookup(&vxlan_db->tree, port);
-       if (!vxlan)
+       vxlanp = mlx5e_vxlan_lookup_port_locked(priv, port);
+       if (!vxlanp)
                goto out_unlock;
 
-       if (atomic_dec_and_test(&vxlan->refcount)) {
-               radix_tree_delete(&vxlan_db->tree, port);
+       if (atomic_dec_and_test(&vxlanp->refcount)) {
+               hash_del(&vxlanp->hlist);
                remove = true;
        }
 
@@ -148,43 +169,22 @@ out_unlock:
 
        if (remove) {
                mlx5e_vxlan_core_del_port_cmd(priv->mdev, port);
-               kfree(vxlan);
+               kfree(vxlanp);
+               vxlan_db->num_ports--;
        }
-       mutex_unlock(&priv->state_lock);
-       kfree(vxlan_work);
-}
-
-void mlx5e_vxlan_queue_work(struct mlx5e_priv *priv, sa_family_t sa_family,
-                           u16 port, int add)
-{
-       struct mlx5e_vxlan_work *vxlan_work;
-
-       vxlan_work = kmalloc(sizeof(*vxlan_work), GFP_ATOMIC);
-       if (!vxlan_work)
-               return;
-
-       if (add)
-               INIT_WORK(&vxlan_work->work, mlx5e_vxlan_add_port);
-       else
-               INIT_WORK(&vxlan_work->work, mlx5e_vxlan_del_port);
-
-       vxlan_work->priv = priv;
-       vxlan_work->port = port;
-       vxlan_work->sa_family = sa_family;
-       queue_work(priv->wq, &vxlan_work->work);
 }
 
 void mlx5e_vxlan_cleanup(struct mlx5e_priv *priv)
 {
        struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan;
-       struct mlx5e_vxlan *vxlan;
-       unsigned int port = 0;
-
-       /* Lockless since we are the only radix-tree consumers, wq is disabled */
-       while (radix_tree_gang_lookup(&vxlan_db->tree, (void **)&vxlan, port, 1)) {
-               port = vxlan->udp_port;
-               radix_tree_delete(&vxlan_db->tree, port);
-               mlx5e_vxlan_core_del_port_cmd(priv->mdev, port);
-               kfree(vxlan);
+       struct mlx5_vxlan_port *vxlanp;
+       struct hlist_node *tmp;
+       int bkt;
+
+       /* Lockless since we are the only hash table consumers, wq and TX are disabled */
+       hash_for_each_safe(vxlan_db->htable, bkt, tmp, vxlanp, hlist) {
+               hash_del(&vxlanp->hlist);
+               mlx5e_vxlan_core_del_port_cmd(priv->mdev, vxlanp->udp_port);
+               kfree(vxlanp);
        }
 }