#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)
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;
}
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);
}
}