Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
// Copyright (c) 2020 Mellanox Technologies

#include <linux/jhash.h>
#include "mod_hdr.h"

#define MLX5_MH_ACT_SZ MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)

struct mod_hdr_key {
	int num_actions;
	void *actions;
};

struct mlx5e_mod_hdr_handle {
	/* a node of a hash table which keeps all the mod_hdr entries */
	struct hlist_node mod_hdr_hlist;

	struct mod_hdr_key key;

	struct mlx5_modify_hdr *modify_hdr;

	refcount_t refcnt;
	struct completion res_ready;
	int compl_result;
};

static u32 hash_mod_hdr_info(struct mod_hdr_key *key)
{
	return jhash(key->actions,
		     key->num_actions * MLX5_MH_ACT_SZ, 0);
}

static int cmp_mod_hdr_info(struct mod_hdr_key *a, struct mod_hdr_key *b)
{
	if (a->num_actions != b->num_actions)
		return 1;

	return memcmp(a->actions, b->actions,
		      a->num_actions * MLX5_MH_ACT_SZ);
}

void mlx5e_mod_hdr_tbl_init(struct mod_hdr_tbl *tbl)
{
	mutex_init(&tbl->lock);
	hash_init(tbl->hlist);
}

void mlx5e_mod_hdr_tbl_destroy(struct mod_hdr_tbl *tbl)
{
	mutex_destroy(&tbl->lock);
}

static struct mlx5e_mod_hdr_handle *mod_hdr_get(struct mod_hdr_tbl *tbl,
						struct mod_hdr_key *key,
						u32 hash_key)
{
	struct mlx5e_mod_hdr_handle *mh, *found = NULL;

	hash_for_each_possible(tbl->hlist, mh, mod_hdr_hlist, hash_key) {
		if (!cmp_mod_hdr_info(&mh->key, key)) {
			refcount_inc(&mh->refcnt);
			found = mh;
			break;
		}
	}

	return found;
}

struct mlx5e_mod_hdr_handle *
mlx5e_mod_hdr_attach(struct mlx5_core_dev *mdev,
		     struct mod_hdr_tbl *tbl,
		     enum mlx5_flow_namespace_type namespace,
		     struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts)
{
	int num_actions, actions_size, err;
	struct mlx5e_mod_hdr_handle *mh;
	struct mod_hdr_key key;
	u32 hash_key;

	num_actions  = mod_hdr_acts->num_actions;
	actions_size = MLX5_MH_ACT_SZ * num_actions;

	key.actions = mod_hdr_acts->actions;
	key.num_actions = num_actions;

	hash_key = hash_mod_hdr_info(&key);

	mutex_lock(&tbl->lock);
	mh = mod_hdr_get(tbl, &key, hash_key);
	if (mh) {
		mutex_unlock(&tbl->lock);
		wait_for_completion(&mh->res_ready);

		if (mh->compl_result < 0) {
			err = -EREMOTEIO;
			goto attach_header_err;
		}
		goto attach_header;
	}

	mh = kzalloc(sizeof(*mh) + actions_size, GFP_KERNEL);
	if (!mh) {
		mutex_unlock(&tbl->lock);
		return ERR_PTR(-ENOMEM);
	}

	mh->key.actions = (void *)mh + sizeof(*mh);
	memcpy(mh->key.actions, key.actions, actions_size);
	mh->key.num_actions = num_actions;
	refcount_set(&mh->refcnt, 1);
	init_completion(&mh->res_ready);

	hash_add(tbl->hlist, &mh->mod_hdr_hlist, hash_key);
	mutex_unlock(&tbl->lock);

	mh->modify_hdr = mlx5_modify_header_alloc(mdev, namespace,
						  mh->key.num_actions,
						  mh->key.actions);
	if (IS_ERR(mh->modify_hdr)) {
		err = PTR_ERR(mh->modify_hdr);
		mh->compl_result = err;
		goto alloc_header_err;
	}
	mh->compl_result = 1;
	complete_all(&mh->res_ready);

attach_header:
	return mh;

alloc_header_err:
	complete_all(&mh->res_ready);
attach_header_err:
	mlx5e_mod_hdr_detach(mdev, tbl, mh);
	return ERR_PTR(err);
}

void mlx5e_mod_hdr_detach(struct mlx5_core_dev *mdev,
			  struct mod_hdr_tbl *tbl,
			  struct mlx5e_mod_hdr_handle *mh)
{
	if (!refcount_dec_and_mutex_lock(&mh->refcnt, &tbl->lock))
		return;
	hash_del(&mh->mod_hdr_hlist);
	mutex_unlock(&tbl->lock);

	if (mh->compl_result > 0)
		mlx5_modify_header_dealloc(mdev, mh->modify_hdr);

	kfree(mh);
}

struct mlx5_modify_hdr *mlx5e_mod_hdr_get(struct mlx5e_mod_hdr_handle *mh)
{
	return mh->modify_hdr;
}