162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2006, 2007 Cisco Systems, Inc.  All rights reserved.
362306a36Sopenharmony_ci * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This software is available to you under a choice of one of two
662306a36Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
762306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
862306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the
962306a36Sopenharmony_ci * OpenIB.org BSD license below:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
1262306a36Sopenharmony_ci *     without modification, are permitted provided that the following
1362306a36Sopenharmony_ci *     conditions are met:
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
1662306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
1762306a36Sopenharmony_ci *        disclaimer.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
2062306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2162306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
2262306a36Sopenharmony_ci *        provided with the distribution.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2562306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2662306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2762306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2862306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2962306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
3062306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3162306a36Sopenharmony_ci * SOFTWARE.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <linux/string.h>
3562306a36Sopenharmony_ci#include <linux/etherdevice.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include <linux/mlx4/cmd.h>
3862306a36Sopenharmony_ci#include <linux/mlx4/qp.h>
3962306a36Sopenharmony_ci#include <linux/export.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include "mlx4.h"
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ciint mlx4_get_mgm_entry_size(struct mlx4_dev *dev)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	return 1 << dev->oper_log_mgm_entry_size;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ciint mlx4_get_qp_per_mgm(struct mlx4_dev *dev)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	return 4 * (mlx4_get_mgm_entry_size(dev) / 16 - 2);
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic int mlx4_QP_FLOW_STEERING_ATTACH(struct mlx4_dev *dev,
5462306a36Sopenharmony_ci					struct mlx4_cmd_mailbox *mailbox,
5562306a36Sopenharmony_ci					u32 size,
5662306a36Sopenharmony_ci					u64 *reg_id)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	u64 imm;
5962306a36Sopenharmony_ci	int err = 0;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	err = mlx4_cmd_imm(dev, mailbox->dma, &imm, size, 0,
6262306a36Sopenharmony_ci			   MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A,
6362306a36Sopenharmony_ci			   MLX4_CMD_NATIVE);
6462306a36Sopenharmony_ci	if (err)
6562306a36Sopenharmony_ci		return err;
6662306a36Sopenharmony_ci	*reg_id = imm;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return err;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic int mlx4_QP_FLOW_STEERING_DETACH(struct mlx4_dev *dev, u64 regid)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	int err = 0;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	err = mlx4_cmd(dev, regid, 0, 0,
7662306a36Sopenharmony_ci		       MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A,
7762306a36Sopenharmony_ci		       MLX4_CMD_NATIVE);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return err;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic int mlx4_READ_ENTRY(struct mlx4_dev *dev, int index,
8362306a36Sopenharmony_ci			   struct mlx4_cmd_mailbox *mailbox)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	return mlx4_cmd_box(dev, 0, mailbox->dma, index, 0, MLX4_CMD_READ_MCG,
8662306a36Sopenharmony_ci			    MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int mlx4_WRITE_ENTRY(struct mlx4_dev *dev, int index,
9062306a36Sopenharmony_ci			    struct mlx4_cmd_mailbox *mailbox)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	return mlx4_cmd(dev, mailbox->dma, index, 0, MLX4_CMD_WRITE_MCG,
9362306a36Sopenharmony_ci			MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE);
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic int mlx4_WRITE_PROMISC(struct mlx4_dev *dev, u8 port, u8 steer,
9762306a36Sopenharmony_ci			      struct mlx4_cmd_mailbox *mailbox)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	u32 in_mod;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	in_mod = (u32) port << 16 | steer << 1;
10262306a36Sopenharmony_ci	return mlx4_cmd(dev, mailbox->dma, in_mod, 0x1,
10362306a36Sopenharmony_ci			MLX4_CMD_WRITE_MCG, MLX4_CMD_TIME_CLASS_A,
10462306a36Sopenharmony_ci			MLX4_CMD_NATIVE);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic int mlx4_GID_HASH(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox,
10862306a36Sopenharmony_ci			 u16 *hash, u8 op_mod)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	u64 imm;
11162306a36Sopenharmony_ci	int err;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	err = mlx4_cmd_imm(dev, mailbox->dma, &imm, 0, op_mod,
11462306a36Sopenharmony_ci			   MLX4_CMD_MGID_HASH, MLX4_CMD_TIME_CLASS_A,
11562306a36Sopenharmony_ci			   MLX4_CMD_NATIVE);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (!err)
11862306a36Sopenharmony_ci		*hash = imm;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	return err;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic struct mlx4_promisc_qp *get_promisc_qp(struct mlx4_dev *dev, u8 port,
12462306a36Sopenharmony_ci					      enum mlx4_steer_type steer,
12562306a36Sopenharmony_ci					      u32 qpn)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct mlx4_steer *s_steer;
12862306a36Sopenharmony_ci	struct mlx4_promisc_qp *pqp;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (port < 1 || port > dev->caps.num_ports)
13162306a36Sopenharmony_ci		return NULL;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	s_steer = &mlx4_priv(dev)->steer[port - 1];
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	list_for_each_entry(pqp, &s_steer->promisc_qps[steer], list) {
13662306a36Sopenharmony_ci		if (pqp->qpn == qpn)
13762306a36Sopenharmony_ci			return pqp;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci	/* not found */
14062306a36Sopenharmony_ci	return NULL;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/*
14462306a36Sopenharmony_ci * Add new entry to steering data structure.
14562306a36Sopenharmony_ci * All promisc QPs should be added as well
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_cistatic int new_steering_entry(struct mlx4_dev *dev, u8 port,
14862306a36Sopenharmony_ci			      enum mlx4_steer_type steer,
14962306a36Sopenharmony_ci			      unsigned int index, u32 qpn)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct mlx4_steer *s_steer;
15262306a36Sopenharmony_ci	struct mlx4_cmd_mailbox *mailbox;
15362306a36Sopenharmony_ci	struct mlx4_mgm *mgm;
15462306a36Sopenharmony_ci	u32 members_count;
15562306a36Sopenharmony_ci	struct mlx4_steer_index *new_entry;
15662306a36Sopenharmony_ci	struct mlx4_promisc_qp *pqp;
15762306a36Sopenharmony_ci	struct mlx4_promisc_qp *dqp = NULL;
15862306a36Sopenharmony_ci	u32 prot;
15962306a36Sopenharmony_ci	int err;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (port < 1 || port > dev->caps.num_ports)
16262306a36Sopenharmony_ci		return -EINVAL;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	s_steer = &mlx4_priv(dev)->steer[port - 1];
16562306a36Sopenharmony_ci	new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL);
16662306a36Sopenharmony_ci	if (!new_entry)
16762306a36Sopenharmony_ci		return -ENOMEM;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	INIT_LIST_HEAD(&new_entry->duplicates);
17062306a36Sopenharmony_ci	new_entry->index = index;
17162306a36Sopenharmony_ci	list_add_tail(&new_entry->list, &s_steer->steer_entries[steer]);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* If the given qpn is also a promisc qp,
17462306a36Sopenharmony_ci	 * it should be inserted to duplicates list
17562306a36Sopenharmony_ci	 */
17662306a36Sopenharmony_ci	pqp = get_promisc_qp(dev, port, steer, qpn);
17762306a36Sopenharmony_ci	if (pqp) {
17862306a36Sopenharmony_ci		dqp = kmalloc(sizeof(*dqp), GFP_KERNEL);
17962306a36Sopenharmony_ci		if (!dqp) {
18062306a36Sopenharmony_ci			err = -ENOMEM;
18162306a36Sopenharmony_ci			goto out_alloc;
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci		dqp->qpn = qpn;
18462306a36Sopenharmony_ci		list_add_tail(&dqp->list, &new_entry->duplicates);
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/* if no promisc qps for this vep, we are done */
18862306a36Sopenharmony_ci	if (list_empty(&s_steer->promisc_qps[steer]))
18962306a36Sopenharmony_ci		return 0;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* now need to add all the promisc qps to the new
19262306a36Sopenharmony_ci	 * steering entry, as they should also receive the packets
19362306a36Sopenharmony_ci	 * destined to this address */
19462306a36Sopenharmony_ci	mailbox = mlx4_alloc_cmd_mailbox(dev);
19562306a36Sopenharmony_ci	if (IS_ERR(mailbox)) {
19662306a36Sopenharmony_ci		err = -ENOMEM;
19762306a36Sopenharmony_ci		goto out_alloc;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci	mgm = mailbox->buf;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	err = mlx4_READ_ENTRY(dev, index, mailbox);
20262306a36Sopenharmony_ci	if (err)
20362306a36Sopenharmony_ci		goto out_mailbox;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	members_count = be32_to_cpu(mgm->members_count) & 0xffffff;
20662306a36Sopenharmony_ci	prot = be32_to_cpu(mgm->members_count) >> 30;
20762306a36Sopenharmony_ci	list_for_each_entry(pqp, &s_steer->promisc_qps[steer], list) {
20862306a36Sopenharmony_ci		/* don't add already existing qpn */
20962306a36Sopenharmony_ci		if (pqp->qpn == qpn)
21062306a36Sopenharmony_ci			continue;
21162306a36Sopenharmony_ci		if (members_count == dev->caps.num_qp_per_mgm) {
21262306a36Sopenharmony_ci			/* out of space */
21362306a36Sopenharmony_ci			err = -ENOMEM;
21462306a36Sopenharmony_ci			goto out_mailbox;
21562306a36Sopenharmony_ci		}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		/* add the qpn */
21862306a36Sopenharmony_ci		mgm->qp[members_count++] = cpu_to_be32(pqp->qpn & MGM_QPN_MASK);
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci	/* update the qps count and update the entry with all the promisc qps*/
22162306a36Sopenharmony_ci	mgm->members_count = cpu_to_be32(members_count | (prot << 30));
22262306a36Sopenharmony_ci	err = mlx4_WRITE_ENTRY(dev, index, mailbox);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ciout_mailbox:
22562306a36Sopenharmony_ci	mlx4_free_cmd_mailbox(dev, mailbox);
22662306a36Sopenharmony_ci	if (!err)
22762306a36Sopenharmony_ci		return 0;
22862306a36Sopenharmony_ciout_alloc:
22962306a36Sopenharmony_ci	if (dqp) {
23062306a36Sopenharmony_ci		list_del(&dqp->list);
23162306a36Sopenharmony_ci		kfree(dqp);
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci	list_del(&new_entry->list);
23462306a36Sopenharmony_ci	kfree(new_entry);
23562306a36Sopenharmony_ci	return err;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/* update the data structures with existing steering entry */
23962306a36Sopenharmony_cistatic int existing_steering_entry(struct mlx4_dev *dev, u8 port,
24062306a36Sopenharmony_ci				   enum mlx4_steer_type steer,
24162306a36Sopenharmony_ci				   unsigned int index, u32 qpn)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct mlx4_steer *s_steer;
24462306a36Sopenharmony_ci	struct mlx4_steer_index *tmp_entry, *entry = NULL;
24562306a36Sopenharmony_ci	struct mlx4_promisc_qp *pqp;
24662306a36Sopenharmony_ci	struct mlx4_promisc_qp *dqp;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (port < 1 || port > dev->caps.num_ports)
24962306a36Sopenharmony_ci		return -EINVAL;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	s_steer = &mlx4_priv(dev)->steer[port - 1];
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	pqp = get_promisc_qp(dev, port, steer, qpn);
25462306a36Sopenharmony_ci	if (!pqp)
25562306a36Sopenharmony_ci		return 0; /* nothing to do */
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	list_for_each_entry(tmp_entry, &s_steer->steer_entries[steer], list) {
25862306a36Sopenharmony_ci		if (tmp_entry->index == index) {
25962306a36Sopenharmony_ci			entry = tmp_entry;
26062306a36Sopenharmony_ci			break;
26162306a36Sopenharmony_ci		}
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci	if (unlikely(!entry)) {
26462306a36Sopenharmony_ci		mlx4_warn(dev, "Steering entry at index %x is not registered\n", index);
26562306a36Sopenharmony_ci		return -EINVAL;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/* the given qpn is listed as a promisc qpn
26962306a36Sopenharmony_ci	 * we need to add it as a duplicate to this entry
27062306a36Sopenharmony_ci	 * for future references */
27162306a36Sopenharmony_ci	list_for_each_entry(dqp, &entry->duplicates, list) {
27262306a36Sopenharmony_ci		if (qpn == dqp->qpn)
27362306a36Sopenharmony_ci			return 0; /* qp is already duplicated */
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* add the qp as a duplicate on this index */
27762306a36Sopenharmony_ci	dqp = kmalloc(sizeof(*dqp), GFP_KERNEL);
27862306a36Sopenharmony_ci	if (!dqp)
27962306a36Sopenharmony_ci		return -ENOMEM;
28062306a36Sopenharmony_ci	dqp->qpn = qpn;
28162306a36Sopenharmony_ci	list_add_tail(&dqp->list, &entry->duplicates);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	return 0;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci/* Check whether a qpn is a duplicate on steering entry
28762306a36Sopenharmony_ci * If so, it should not be removed from mgm */
28862306a36Sopenharmony_cistatic bool check_duplicate_entry(struct mlx4_dev *dev, u8 port,
28962306a36Sopenharmony_ci				  enum mlx4_steer_type steer,
29062306a36Sopenharmony_ci				  unsigned int index, u32 qpn)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct mlx4_steer *s_steer;
29362306a36Sopenharmony_ci	struct mlx4_steer_index *tmp_entry, *entry = NULL;
29462306a36Sopenharmony_ci	struct mlx4_promisc_qp *dqp, *tmp_dqp;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (port < 1 || port > dev->caps.num_ports)
29762306a36Sopenharmony_ci		return false;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	s_steer = &mlx4_priv(dev)->steer[port - 1];
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/* if qp is not promisc, it cannot be duplicated */
30262306a36Sopenharmony_ci	if (!get_promisc_qp(dev, port, steer, qpn))
30362306a36Sopenharmony_ci		return false;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* The qp is promisc qp so it is a duplicate on this index
30662306a36Sopenharmony_ci	 * Find the index entry, and remove the duplicate */
30762306a36Sopenharmony_ci	list_for_each_entry(tmp_entry, &s_steer->steer_entries[steer], list) {
30862306a36Sopenharmony_ci		if (tmp_entry->index == index) {
30962306a36Sopenharmony_ci			entry = tmp_entry;
31062306a36Sopenharmony_ci			break;
31162306a36Sopenharmony_ci		}
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci	if (unlikely(!entry)) {
31462306a36Sopenharmony_ci		mlx4_warn(dev, "Steering entry for index %x is not registered\n", index);
31562306a36Sopenharmony_ci		return false;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci	list_for_each_entry_safe(dqp, tmp_dqp, &entry->duplicates, list) {
31862306a36Sopenharmony_ci		if (dqp->qpn == qpn) {
31962306a36Sopenharmony_ci			list_del(&dqp->list);
32062306a36Sopenharmony_ci			kfree(dqp);
32162306a36Sopenharmony_ci		}
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci	return true;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci/* Returns true if all the QPs != tqpn contained in this entry
32762306a36Sopenharmony_ci * are Promisc QPs. Returns false otherwise.
32862306a36Sopenharmony_ci */
32962306a36Sopenharmony_cistatic bool promisc_steering_entry(struct mlx4_dev *dev, u8 port,
33062306a36Sopenharmony_ci				   enum mlx4_steer_type steer,
33162306a36Sopenharmony_ci				   unsigned int index, u32 tqpn,
33262306a36Sopenharmony_ci				   u32 *members_count)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct mlx4_cmd_mailbox *mailbox;
33562306a36Sopenharmony_ci	struct mlx4_mgm *mgm;
33662306a36Sopenharmony_ci	u32 m_count;
33762306a36Sopenharmony_ci	bool ret = false;
33862306a36Sopenharmony_ci	int i;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (port < 1 || port > dev->caps.num_ports)
34162306a36Sopenharmony_ci		return false;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	mailbox = mlx4_alloc_cmd_mailbox(dev);
34462306a36Sopenharmony_ci	if (IS_ERR(mailbox))
34562306a36Sopenharmony_ci		return false;
34662306a36Sopenharmony_ci	mgm = mailbox->buf;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (mlx4_READ_ENTRY(dev, index, mailbox))
34962306a36Sopenharmony_ci		goto out;
35062306a36Sopenharmony_ci	m_count = be32_to_cpu(mgm->members_count) & 0xffffff;
35162306a36Sopenharmony_ci	if (members_count)
35262306a36Sopenharmony_ci		*members_count = m_count;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	for (i = 0;  i < m_count; i++) {
35562306a36Sopenharmony_ci		u32 qpn = be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK;
35662306a36Sopenharmony_ci		if (!get_promisc_qp(dev, port, steer, qpn) && qpn != tqpn) {
35762306a36Sopenharmony_ci			/* the qp is not promisc, the entry can't be removed */
35862306a36Sopenharmony_ci			goto out;
35962306a36Sopenharmony_ci		}
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci	ret = true;
36262306a36Sopenharmony_ciout:
36362306a36Sopenharmony_ci	mlx4_free_cmd_mailbox(dev, mailbox);
36462306a36Sopenharmony_ci	return ret;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci/* IF a steering entry contains only promisc QPs, it can be removed. */
36862306a36Sopenharmony_cistatic bool can_remove_steering_entry(struct mlx4_dev *dev, u8 port,
36962306a36Sopenharmony_ci				      enum mlx4_steer_type steer,
37062306a36Sopenharmony_ci				      unsigned int index, u32 tqpn)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct mlx4_steer *s_steer;
37362306a36Sopenharmony_ci	struct mlx4_steer_index *entry = NULL, *tmp_entry;
37462306a36Sopenharmony_ci	u32 members_count;
37562306a36Sopenharmony_ci	bool ret = false;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (port < 1 || port > dev->caps.num_ports)
37862306a36Sopenharmony_ci		return false;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	s_steer = &mlx4_priv(dev)->steer[port - 1];
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (!promisc_steering_entry(dev, port, steer, index,
38362306a36Sopenharmony_ci				    tqpn, &members_count))
38462306a36Sopenharmony_ci		goto out;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	/* All the qps currently registered for this entry are promiscuous,
38762306a36Sopenharmony_ci	  * Checking for duplicates */
38862306a36Sopenharmony_ci	ret = true;
38962306a36Sopenharmony_ci	list_for_each_entry_safe(entry, tmp_entry, &s_steer->steer_entries[steer], list) {
39062306a36Sopenharmony_ci		if (entry->index == index) {
39162306a36Sopenharmony_ci			if (list_empty(&entry->duplicates) ||
39262306a36Sopenharmony_ci			    members_count == 1) {
39362306a36Sopenharmony_ci				struct mlx4_promisc_qp *pqp, *tmp_pqp;
39462306a36Sopenharmony_ci				/* If there is only 1 entry in duplicates then
39562306a36Sopenharmony_ci				 * this is the QP we want to delete, going over
39662306a36Sopenharmony_ci				 * the list and deleting the entry.
39762306a36Sopenharmony_ci				 */
39862306a36Sopenharmony_ci				list_del(&entry->list);
39962306a36Sopenharmony_ci				list_for_each_entry_safe(pqp, tmp_pqp,
40062306a36Sopenharmony_ci							 &entry->duplicates,
40162306a36Sopenharmony_ci							 list) {
40262306a36Sopenharmony_ci					list_del(&pqp->list);
40362306a36Sopenharmony_ci					kfree(pqp);
40462306a36Sopenharmony_ci				}
40562306a36Sopenharmony_ci				kfree(entry);
40662306a36Sopenharmony_ci			} else {
40762306a36Sopenharmony_ci				/* This entry contains duplicates so it shouldn't be removed */
40862306a36Sopenharmony_ci				ret = false;
40962306a36Sopenharmony_ci				goto out;
41062306a36Sopenharmony_ci			}
41162306a36Sopenharmony_ci		}
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ciout:
41562306a36Sopenharmony_ci	return ret;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic int add_promisc_qp(struct mlx4_dev *dev, u8 port,
41962306a36Sopenharmony_ci			  enum mlx4_steer_type steer, u32 qpn)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	struct mlx4_steer *s_steer;
42262306a36Sopenharmony_ci	struct mlx4_cmd_mailbox *mailbox;
42362306a36Sopenharmony_ci	struct mlx4_mgm *mgm;
42462306a36Sopenharmony_ci	struct mlx4_steer_index *entry;
42562306a36Sopenharmony_ci	struct mlx4_promisc_qp *pqp;
42662306a36Sopenharmony_ci	struct mlx4_promisc_qp *dqp;
42762306a36Sopenharmony_ci	u32 members_count;
42862306a36Sopenharmony_ci	u32 prot;
42962306a36Sopenharmony_ci	int i;
43062306a36Sopenharmony_ci	bool found;
43162306a36Sopenharmony_ci	int err;
43262306a36Sopenharmony_ci	struct mlx4_priv *priv = mlx4_priv(dev);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (port < 1 || port > dev->caps.num_ports)
43562306a36Sopenharmony_ci		return -EINVAL;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	s_steer = &mlx4_priv(dev)->steer[port - 1];
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	mutex_lock(&priv->mcg_table.mutex);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (get_promisc_qp(dev, port, steer, qpn)) {
44262306a36Sopenharmony_ci		err = 0;  /* Noting to do, already exists */
44362306a36Sopenharmony_ci		goto out_mutex;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	pqp = kmalloc(sizeof(*pqp), GFP_KERNEL);
44762306a36Sopenharmony_ci	if (!pqp) {
44862306a36Sopenharmony_ci		err = -ENOMEM;
44962306a36Sopenharmony_ci		goto out_mutex;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci	pqp->qpn = qpn;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	mailbox = mlx4_alloc_cmd_mailbox(dev);
45462306a36Sopenharmony_ci	if (IS_ERR(mailbox)) {
45562306a36Sopenharmony_ci		err = -ENOMEM;
45662306a36Sopenharmony_ci		goto out_alloc;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci	mgm = mailbox->buf;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	if (!(mlx4_is_mfunc(dev) && steer == MLX4_UC_STEER)) {
46162306a36Sopenharmony_ci		/* The promisc QP needs to be added for each one of the steering
46262306a36Sopenharmony_ci		 * entries. If it already exists, needs to be added as
46362306a36Sopenharmony_ci		 * a duplicate for this entry.
46462306a36Sopenharmony_ci		 */
46562306a36Sopenharmony_ci		list_for_each_entry(entry,
46662306a36Sopenharmony_ci				    &s_steer->steer_entries[steer],
46762306a36Sopenharmony_ci				    list) {
46862306a36Sopenharmony_ci			err = mlx4_READ_ENTRY(dev, entry->index, mailbox);
46962306a36Sopenharmony_ci			if (err)
47062306a36Sopenharmony_ci				goto out_mailbox;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci			members_count = be32_to_cpu(mgm->members_count) &
47362306a36Sopenharmony_ci					0xffffff;
47462306a36Sopenharmony_ci			prot = be32_to_cpu(mgm->members_count) >> 30;
47562306a36Sopenharmony_ci			found = false;
47662306a36Sopenharmony_ci			for (i = 0; i < members_count; i++) {
47762306a36Sopenharmony_ci				if ((be32_to_cpu(mgm->qp[i]) &
47862306a36Sopenharmony_ci				     MGM_QPN_MASK) == qpn) {
47962306a36Sopenharmony_ci					/* Entry already exists.
48062306a36Sopenharmony_ci					 * Add to duplicates.
48162306a36Sopenharmony_ci					 */
48262306a36Sopenharmony_ci					dqp = kmalloc(sizeof(*dqp), GFP_KERNEL);
48362306a36Sopenharmony_ci					if (!dqp) {
48462306a36Sopenharmony_ci						err = -ENOMEM;
48562306a36Sopenharmony_ci						goto out_mailbox;
48662306a36Sopenharmony_ci					}
48762306a36Sopenharmony_ci					dqp->qpn = qpn;
48862306a36Sopenharmony_ci					list_add_tail(&dqp->list,
48962306a36Sopenharmony_ci						      &entry->duplicates);
49062306a36Sopenharmony_ci					found = true;
49162306a36Sopenharmony_ci				}
49262306a36Sopenharmony_ci			}
49362306a36Sopenharmony_ci			if (!found) {
49462306a36Sopenharmony_ci				/* Need to add the qpn to mgm */
49562306a36Sopenharmony_ci				if (members_count ==
49662306a36Sopenharmony_ci				    dev->caps.num_qp_per_mgm) {
49762306a36Sopenharmony_ci					/* entry is full */
49862306a36Sopenharmony_ci					err = -ENOMEM;
49962306a36Sopenharmony_ci					goto out_mailbox;
50062306a36Sopenharmony_ci				}
50162306a36Sopenharmony_ci				mgm->qp[members_count++] =
50262306a36Sopenharmony_ci					cpu_to_be32(qpn & MGM_QPN_MASK);
50362306a36Sopenharmony_ci				mgm->members_count =
50462306a36Sopenharmony_ci					cpu_to_be32(members_count |
50562306a36Sopenharmony_ci						    (prot << 30));
50662306a36Sopenharmony_ci				err = mlx4_WRITE_ENTRY(dev, entry->index,
50762306a36Sopenharmony_ci						       mailbox);
50862306a36Sopenharmony_ci				if (err)
50962306a36Sopenharmony_ci					goto out_mailbox;
51062306a36Sopenharmony_ci			}
51162306a36Sopenharmony_ci		}
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* add the new qpn to list of promisc qps */
51562306a36Sopenharmony_ci	list_add_tail(&pqp->list, &s_steer->promisc_qps[steer]);
51662306a36Sopenharmony_ci	/* now need to add all the promisc qps to default entry */
51762306a36Sopenharmony_ci	memset(mgm, 0, sizeof(*mgm));
51862306a36Sopenharmony_ci	members_count = 0;
51962306a36Sopenharmony_ci	list_for_each_entry(dqp, &s_steer->promisc_qps[steer], list) {
52062306a36Sopenharmony_ci		if (members_count == dev->caps.num_qp_per_mgm) {
52162306a36Sopenharmony_ci			/* entry is full */
52262306a36Sopenharmony_ci			err = -ENOMEM;
52362306a36Sopenharmony_ci			goto out_list;
52462306a36Sopenharmony_ci		}
52562306a36Sopenharmony_ci		mgm->qp[members_count++] = cpu_to_be32(dqp->qpn & MGM_QPN_MASK);
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci	mgm->members_count = cpu_to_be32(members_count | MLX4_PROT_ETH << 30);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	err = mlx4_WRITE_PROMISC(dev, port, steer, mailbox);
53062306a36Sopenharmony_ci	if (err)
53162306a36Sopenharmony_ci		goto out_list;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	mlx4_free_cmd_mailbox(dev, mailbox);
53462306a36Sopenharmony_ci	mutex_unlock(&priv->mcg_table.mutex);
53562306a36Sopenharmony_ci	return 0;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ciout_list:
53862306a36Sopenharmony_ci	list_del(&pqp->list);
53962306a36Sopenharmony_ciout_mailbox:
54062306a36Sopenharmony_ci	mlx4_free_cmd_mailbox(dev, mailbox);
54162306a36Sopenharmony_ciout_alloc:
54262306a36Sopenharmony_ci	kfree(pqp);
54362306a36Sopenharmony_ciout_mutex:
54462306a36Sopenharmony_ci	mutex_unlock(&priv->mcg_table.mutex);
54562306a36Sopenharmony_ci	return err;
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic int remove_promisc_qp(struct mlx4_dev *dev, u8 port,
54962306a36Sopenharmony_ci			     enum mlx4_steer_type steer, u32 qpn)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	struct mlx4_priv *priv = mlx4_priv(dev);
55262306a36Sopenharmony_ci	struct mlx4_steer *s_steer;
55362306a36Sopenharmony_ci	struct mlx4_cmd_mailbox *mailbox;
55462306a36Sopenharmony_ci	struct mlx4_mgm *mgm;
55562306a36Sopenharmony_ci	struct mlx4_steer_index *entry, *tmp_entry;
55662306a36Sopenharmony_ci	struct mlx4_promisc_qp *pqp;
55762306a36Sopenharmony_ci	struct mlx4_promisc_qp *dqp;
55862306a36Sopenharmony_ci	u32 members_count;
55962306a36Sopenharmony_ci	bool found;
56062306a36Sopenharmony_ci	bool back_to_list = false;
56162306a36Sopenharmony_ci	int i;
56262306a36Sopenharmony_ci	int err;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	if (port < 1 || port > dev->caps.num_ports)
56562306a36Sopenharmony_ci		return -EINVAL;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	s_steer = &mlx4_priv(dev)->steer[port - 1];
56862306a36Sopenharmony_ci	mutex_lock(&priv->mcg_table.mutex);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	pqp = get_promisc_qp(dev, port, steer, qpn);
57162306a36Sopenharmony_ci	if (unlikely(!pqp)) {
57262306a36Sopenharmony_ci		mlx4_warn(dev, "QP %x is not promiscuous QP\n", qpn);
57362306a36Sopenharmony_ci		/* nothing to do */
57462306a36Sopenharmony_ci		err = 0;
57562306a36Sopenharmony_ci		goto out_mutex;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/*remove from list of promisc qps */
57962306a36Sopenharmony_ci	list_del(&pqp->list);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	/* set the default entry not to include the removed one */
58262306a36Sopenharmony_ci	mailbox = mlx4_alloc_cmd_mailbox(dev);
58362306a36Sopenharmony_ci	if (IS_ERR(mailbox)) {
58462306a36Sopenharmony_ci		err = -ENOMEM;
58562306a36Sopenharmony_ci		back_to_list = true;
58662306a36Sopenharmony_ci		goto out_list;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci	mgm = mailbox->buf;
58962306a36Sopenharmony_ci	members_count = 0;
59062306a36Sopenharmony_ci	list_for_each_entry(dqp, &s_steer->promisc_qps[steer], list)
59162306a36Sopenharmony_ci		mgm->qp[members_count++] = cpu_to_be32(dqp->qpn & MGM_QPN_MASK);
59262306a36Sopenharmony_ci	mgm->members_count = cpu_to_be32(members_count | MLX4_PROT_ETH << 30);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	err = mlx4_WRITE_PROMISC(dev, port, steer, mailbox);
59562306a36Sopenharmony_ci	if (err)
59662306a36Sopenharmony_ci		goto out_mailbox;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	if (!(mlx4_is_mfunc(dev) && steer == MLX4_UC_STEER)) {
59962306a36Sopenharmony_ci		/* Remove the QP from all the steering entries */
60062306a36Sopenharmony_ci		list_for_each_entry_safe(entry, tmp_entry,
60162306a36Sopenharmony_ci					 &s_steer->steer_entries[steer],
60262306a36Sopenharmony_ci					 list) {
60362306a36Sopenharmony_ci			found = false;
60462306a36Sopenharmony_ci			list_for_each_entry(dqp, &entry->duplicates, list) {
60562306a36Sopenharmony_ci				if (dqp->qpn == qpn) {
60662306a36Sopenharmony_ci					found = true;
60762306a36Sopenharmony_ci					break;
60862306a36Sopenharmony_ci				}
60962306a36Sopenharmony_ci			}
61062306a36Sopenharmony_ci			if (found) {
61162306a36Sopenharmony_ci				/* A duplicate, no need to change the MGM,
61262306a36Sopenharmony_ci				 * only update the duplicates list
61362306a36Sopenharmony_ci				 */
61462306a36Sopenharmony_ci				list_del(&dqp->list);
61562306a36Sopenharmony_ci				kfree(dqp);
61662306a36Sopenharmony_ci			} else {
61762306a36Sopenharmony_ci				int loc = -1;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci				err = mlx4_READ_ENTRY(dev,
62062306a36Sopenharmony_ci						      entry->index,
62162306a36Sopenharmony_ci						      mailbox);
62262306a36Sopenharmony_ci				if (err)
62362306a36Sopenharmony_ci					goto out_mailbox;
62462306a36Sopenharmony_ci				members_count =
62562306a36Sopenharmony_ci					be32_to_cpu(mgm->members_count) &
62662306a36Sopenharmony_ci					0xffffff;
62762306a36Sopenharmony_ci				if (!members_count) {
62862306a36Sopenharmony_ci					mlx4_warn(dev, "QP %06x wasn't found in entry %x mcount=0. deleting entry...\n",
62962306a36Sopenharmony_ci						  qpn, entry->index);
63062306a36Sopenharmony_ci					list_del(&entry->list);
63162306a36Sopenharmony_ci					kfree(entry);
63262306a36Sopenharmony_ci					continue;
63362306a36Sopenharmony_ci				}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci				for (i = 0; i < members_count; ++i)
63662306a36Sopenharmony_ci					if ((be32_to_cpu(mgm->qp[i]) &
63762306a36Sopenharmony_ci					     MGM_QPN_MASK) == qpn) {
63862306a36Sopenharmony_ci						loc = i;
63962306a36Sopenharmony_ci						break;
64062306a36Sopenharmony_ci					}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci				if (loc < 0) {
64362306a36Sopenharmony_ci					mlx4_err(dev, "QP %06x wasn't found in entry %d\n",
64462306a36Sopenharmony_ci						 qpn, entry->index);
64562306a36Sopenharmony_ci					err = -EINVAL;
64662306a36Sopenharmony_ci					goto out_mailbox;
64762306a36Sopenharmony_ci				}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci				/* Copy the last QP in this MGM
65062306a36Sopenharmony_ci				 * over removed QP
65162306a36Sopenharmony_ci				 */
65262306a36Sopenharmony_ci				mgm->qp[loc] = mgm->qp[members_count - 1];
65362306a36Sopenharmony_ci				mgm->qp[members_count - 1] = 0;
65462306a36Sopenharmony_ci				mgm->members_count =
65562306a36Sopenharmony_ci					cpu_to_be32(--members_count |
65662306a36Sopenharmony_ci						    (MLX4_PROT_ETH << 30));
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci				err = mlx4_WRITE_ENTRY(dev,
65962306a36Sopenharmony_ci						       entry->index,
66062306a36Sopenharmony_ci						       mailbox);
66162306a36Sopenharmony_ci				if (err)
66262306a36Sopenharmony_ci					goto out_mailbox;
66362306a36Sopenharmony_ci			}
66462306a36Sopenharmony_ci		}
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ciout_mailbox:
66862306a36Sopenharmony_ci	mlx4_free_cmd_mailbox(dev, mailbox);
66962306a36Sopenharmony_ciout_list:
67062306a36Sopenharmony_ci	if (back_to_list)
67162306a36Sopenharmony_ci		list_add_tail(&pqp->list, &s_steer->promisc_qps[steer]);
67262306a36Sopenharmony_ci	else
67362306a36Sopenharmony_ci		kfree(pqp);
67462306a36Sopenharmony_ciout_mutex:
67562306a36Sopenharmony_ci	mutex_unlock(&priv->mcg_table.mutex);
67662306a36Sopenharmony_ci	return err;
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci/*
68062306a36Sopenharmony_ci * Caller must hold MCG table semaphore.  gid and mgm parameters must
68162306a36Sopenharmony_ci * be properly aligned for command interface.
68262306a36Sopenharmony_ci *
68362306a36Sopenharmony_ci *  Returns 0 unless a firmware command error occurs.
68462306a36Sopenharmony_ci *
68562306a36Sopenharmony_ci * If GID is found in MGM or MGM is empty, *index = *hash, *prev = -1
68662306a36Sopenharmony_ci * and *mgm holds MGM entry.
68762306a36Sopenharmony_ci *
68862306a36Sopenharmony_ci * if GID is found in AMGM, *index = index in AMGM, *prev = index of
68962306a36Sopenharmony_ci * previous entry in hash chain and *mgm holds AMGM entry.
69062306a36Sopenharmony_ci *
69162306a36Sopenharmony_ci * If no AMGM exists for given gid, *index = -1, *prev = index of last
69262306a36Sopenharmony_ci * entry in hash chain and *mgm holds end of hash chain.
69362306a36Sopenharmony_ci */
69462306a36Sopenharmony_cistatic int find_entry(struct mlx4_dev *dev, u8 port,
69562306a36Sopenharmony_ci		      u8 *gid, enum mlx4_protocol prot,
69662306a36Sopenharmony_ci		      struct mlx4_cmd_mailbox *mgm_mailbox,
69762306a36Sopenharmony_ci		      int *prev, int *index)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	struct mlx4_cmd_mailbox *mailbox;
70062306a36Sopenharmony_ci	struct mlx4_mgm *mgm = mgm_mailbox->buf;
70162306a36Sopenharmony_ci	u8 *mgid;
70262306a36Sopenharmony_ci	int err;
70362306a36Sopenharmony_ci	u16 hash;
70462306a36Sopenharmony_ci	u8 op_mod = (prot == MLX4_PROT_ETH) ?
70562306a36Sopenharmony_ci		!!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) : 0;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	mailbox = mlx4_alloc_cmd_mailbox(dev);
70862306a36Sopenharmony_ci	if (IS_ERR(mailbox))
70962306a36Sopenharmony_ci		return -ENOMEM;
71062306a36Sopenharmony_ci	mgid = mailbox->buf;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	memcpy(mgid, gid, 16);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	err = mlx4_GID_HASH(dev, mailbox, &hash, op_mod);
71562306a36Sopenharmony_ci	mlx4_free_cmd_mailbox(dev, mailbox);
71662306a36Sopenharmony_ci	if (err)
71762306a36Sopenharmony_ci		return err;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	if (0)
72062306a36Sopenharmony_ci		mlx4_dbg(dev, "Hash for %pI6 is %04x\n", gid, hash);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	*index = hash;
72362306a36Sopenharmony_ci	*prev  = -1;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	do {
72662306a36Sopenharmony_ci		err = mlx4_READ_ENTRY(dev, *index, mgm_mailbox);
72762306a36Sopenharmony_ci		if (err)
72862306a36Sopenharmony_ci			return err;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci		if (!(be32_to_cpu(mgm->members_count) & 0xffffff)) {
73162306a36Sopenharmony_ci			if (*index != hash) {
73262306a36Sopenharmony_ci				mlx4_err(dev, "Found zero MGID in AMGM\n");
73362306a36Sopenharmony_ci				err = -EINVAL;
73462306a36Sopenharmony_ci			}
73562306a36Sopenharmony_ci			return err;
73662306a36Sopenharmony_ci		}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci		if (!memcmp(mgm->gid, gid, 16) &&
73962306a36Sopenharmony_ci		    be32_to_cpu(mgm->members_count) >> 30 == prot)
74062306a36Sopenharmony_ci			return err;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		*prev = *index;
74362306a36Sopenharmony_ci		*index = be32_to_cpu(mgm->next_gid_index) >> 6;
74462306a36Sopenharmony_ci	} while (*index);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	*index = -1;
74762306a36Sopenharmony_ci	return err;
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic const u8 __promisc_mode[] = {
75162306a36Sopenharmony_ci	[MLX4_FS_REGULAR]   = 0x0,
75262306a36Sopenharmony_ci	[MLX4_FS_ALL_DEFAULT] = 0x1,
75362306a36Sopenharmony_ci	[MLX4_FS_MC_DEFAULT] = 0x3,
75462306a36Sopenharmony_ci	[MLX4_FS_MIRROR_RX_PORT] = 0x4,
75562306a36Sopenharmony_ci	[MLX4_FS_MIRROR_SX_PORT] = 0x5,
75662306a36Sopenharmony_ci	[MLX4_FS_UC_SNIFFER] = 0x6,
75762306a36Sopenharmony_ci	[MLX4_FS_MC_SNIFFER] = 0x7,
75862306a36Sopenharmony_ci};
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ciint mlx4_map_sw_to_hw_steering_mode(struct mlx4_dev *dev,
76162306a36Sopenharmony_ci				    enum mlx4_net_trans_promisc_mode flow_type)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	if (flow_type >= MLX4_FS_MODE_NUM) {
76462306a36Sopenharmony_ci		mlx4_err(dev, "Invalid flow type. type = %d\n", flow_type);
76562306a36Sopenharmony_ci		return -EINVAL;
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci	return __promisc_mode[flow_type];
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_map_sw_to_hw_steering_mode);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_cistatic void trans_rule_ctrl_to_hw(struct mlx4_net_trans_rule *ctrl,
77262306a36Sopenharmony_ci				  struct mlx4_net_trans_rule_hw_ctrl *hw)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	u8 flags = 0;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	flags = ctrl->queue_mode == MLX4_NET_TRANS_Q_LIFO ? 1 : 0;
77762306a36Sopenharmony_ci	flags |= ctrl->exclusive ? (1 << 2) : 0;
77862306a36Sopenharmony_ci	flags |= ctrl->allow_loopback ? (1 << 3) : 0;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	hw->flags = flags;
78162306a36Sopenharmony_ci	hw->type = __promisc_mode[ctrl->promisc_mode];
78262306a36Sopenharmony_ci	hw->prio = cpu_to_be16(ctrl->priority);
78362306a36Sopenharmony_ci	hw->port = ctrl->port;
78462306a36Sopenharmony_ci	hw->qpn = cpu_to_be32(ctrl->qpn);
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ciconst u16 __sw_id_hw[] = {
78862306a36Sopenharmony_ci	[MLX4_NET_TRANS_RULE_ID_ETH]     = 0xE001,
78962306a36Sopenharmony_ci	[MLX4_NET_TRANS_RULE_ID_IB]      = 0xE005,
79062306a36Sopenharmony_ci	[MLX4_NET_TRANS_RULE_ID_IPV6]    = 0xE003,
79162306a36Sopenharmony_ci	[MLX4_NET_TRANS_RULE_ID_IPV4]    = 0xE002,
79262306a36Sopenharmony_ci	[MLX4_NET_TRANS_RULE_ID_TCP]     = 0xE004,
79362306a36Sopenharmony_ci	[MLX4_NET_TRANS_RULE_ID_UDP]     = 0xE006,
79462306a36Sopenharmony_ci	[MLX4_NET_TRANS_RULE_ID_VXLAN]	 = 0xE008
79562306a36Sopenharmony_ci};
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ciint mlx4_map_sw_to_hw_steering_id(struct mlx4_dev *dev,
79862306a36Sopenharmony_ci				  enum mlx4_net_trans_rule_id id)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	if (id >= MLX4_NET_TRANS_RULE_NUM) {
80162306a36Sopenharmony_ci		mlx4_err(dev, "Invalid network rule id. id = %d\n", id);
80262306a36Sopenharmony_ci		return -EINVAL;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci	return __sw_id_hw[id];
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_map_sw_to_hw_steering_id);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic const int __rule_hw_sz[] = {
80962306a36Sopenharmony_ci	[MLX4_NET_TRANS_RULE_ID_ETH] =
81062306a36Sopenharmony_ci		sizeof(struct mlx4_net_trans_rule_hw_eth),
81162306a36Sopenharmony_ci	[MLX4_NET_TRANS_RULE_ID_IB] =
81262306a36Sopenharmony_ci		sizeof(struct mlx4_net_trans_rule_hw_ib),
81362306a36Sopenharmony_ci	[MLX4_NET_TRANS_RULE_ID_IPV6] = 0,
81462306a36Sopenharmony_ci	[MLX4_NET_TRANS_RULE_ID_IPV4] =
81562306a36Sopenharmony_ci		sizeof(struct mlx4_net_trans_rule_hw_ipv4),
81662306a36Sopenharmony_ci	[MLX4_NET_TRANS_RULE_ID_TCP] =
81762306a36Sopenharmony_ci		sizeof(struct mlx4_net_trans_rule_hw_tcp_udp),
81862306a36Sopenharmony_ci	[MLX4_NET_TRANS_RULE_ID_UDP] =
81962306a36Sopenharmony_ci		sizeof(struct mlx4_net_trans_rule_hw_tcp_udp),
82062306a36Sopenharmony_ci	[MLX4_NET_TRANS_RULE_ID_VXLAN] =
82162306a36Sopenharmony_ci		sizeof(struct mlx4_net_trans_rule_hw_vxlan)
82262306a36Sopenharmony_ci};
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ciint mlx4_hw_rule_sz(struct mlx4_dev *dev,
82562306a36Sopenharmony_ci	       enum mlx4_net_trans_rule_id id)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	if (id >= MLX4_NET_TRANS_RULE_NUM) {
82862306a36Sopenharmony_ci		mlx4_err(dev, "Invalid network rule id. id = %d\n", id);
82962306a36Sopenharmony_ci		return -EINVAL;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	return __rule_hw_sz[id];
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_hw_rule_sz);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic int parse_trans_rule(struct mlx4_dev *dev, struct mlx4_spec_list *spec,
83762306a36Sopenharmony_ci			    struct _rule_hw *rule_hw)
83862306a36Sopenharmony_ci{
83962306a36Sopenharmony_ci	if (mlx4_hw_rule_sz(dev, spec->id) < 0)
84062306a36Sopenharmony_ci		return -EINVAL;
84162306a36Sopenharmony_ci	memset(rule_hw, 0, mlx4_hw_rule_sz(dev, spec->id));
84262306a36Sopenharmony_ci	rule_hw->id = cpu_to_be16(__sw_id_hw[spec->id]);
84362306a36Sopenharmony_ci	rule_hw->size = mlx4_hw_rule_sz(dev, spec->id) >> 2;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	switch (spec->id) {
84662306a36Sopenharmony_ci	case MLX4_NET_TRANS_RULE_ID_ETH:
84762306a36Sopenharmony_ci		memcpy(rule_hw->eth.dst_mac, spec->eth.dst_mac, ETH_ALEN);
84862306a36Sopenharmony_ci		memcpy(rule_hw->eth.dst_mac_msk, spec->eth.dst_mac_msk,
84962306a36Sopenharmony_ci		       ETH_ALEN);
85062306a36Sopenharmony_ci		memcpy(rule_hw->eth.src_mac, spec->eth.src_mac, ETH_ALEN);
85162306a36Sopenharmony_ci		memcpy(rule_hw->eth.src_mac_msk, spec->eth.src_mac_msk,
85262306a36Sopenharmony_ci		       ETH_ALEN);
85362306a36Sopenharmony_ci		if (spec->eth.ether_type_enable) {
85462306a36Sopenharmony_ci			rule_hw->eth.ether_type_enable = 1;
85562306a36Sopenharmony_ci			rule_hw->eth.ether_type = spec->eth.ether_type;
85662306a36Sopenharmony_ci		}
85762306a36Sopenharmony_ci		rule_hw->eth.vlan_tag = spec->eth.vlan_id;
85862306a36Sopenharmony_ci		rule_hw->eth.vlan_tag_msk = spec->eth.vlan_id_msk;
85962306a36Sopenharmony_ci		break;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	case MLX4_NET_TRANS_RULE_ID_IB:
86262306a36Sopenharmony_ci		rule_hw->ib.l3_qpn = spec->ib.l3_qpn;
86362306a36Sopenharmony_ci		rule_hw->ib.qpn_mask = spec->ib.qpn_msk;
86462306a36Sopenharmony_ci		memcpy(&rule_hw->ib.dst_gid, &spec->ib.dst_gid, 16);
86562306a36Sopenharmony_ci		memcpy(&rule_hw->ib.dst_gid_msk, &spec->ib.dst_gid_msk, 16);
86662306a36Sopenharmony_ci		break;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	case MLX4_NET_TRANS_RULE_ID_IPV6:
86962306a36Sopenharmony_ci		return -EOPNOTSUPP;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	case MLX4_NET_TRANS_RULE_ID_IPV4:
87262306a36Sopenharmony_ci		rule_hw->ipv4.src_ip = spec->ipv4.src_ip;
87362306a36Sopenharmony_ci		rule_hw->ipv4.src_ip_msk = spec->ipv4.src_ip_msk;
87462306a36Sopenharmony_ci		rule_hw->ipv4.dst_ip = spec->ipv4.dst_ip;
87562306a36Sopenharmony_ci		rule_hw->ipv4.dst_ip_msk = spec->ipv4.dst_ip_msk;
87662306a36Sopenharmony_ci		break;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	case MLX4_NET_TRANS_RULE_ID_TCP:
87962306a36Sopenharmony_ci	case MLX4_NET_TRANS_RULE_ID_UDP:
88062306a36Sopenharmony_ci		rule_hw->tcp_udp.dst_port = spec->tcp_udp.dst_port;
88162306a36Sopenharmony_ci		rule_hw->tcp_udp.dst_port_msk = spec->tcp_udp.dst_port_msk;
88262306a36Sopenharmony_ci		rule_hw->tcp_udp.src_port = spec->tcp_udp.src_port;
88362306a36Sopenharmony_ci		rule_hw->tcp_udp.src_port_msk = spec->tcp_udp.src_port_msk;
88462306a36Sopenharmony_ci		break;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	case MLX4_NET_TRANS_RULE_ID_VXLAN:
88762306a36Sopenharmony_ci		rule_hw->vxlan.vni =
88862306a36Sopenharmony_ci			cpu_to_be32(be32_to_cpu(spec->vxlan.vni) << 8);
88962306a36Sopenharmony_ci		rule_hw->vxlan.vni_mask =
89062306a36Sopenharmony_ci			cpu_to_be32(be32_to_cpu(spec->vxlan.vni_mask) << 8);
89162306a36Sopenharmony_ci		break;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	default:
89462306a36Sopenharmony_ci		return -EINVAL;
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	return __rule_hw_sz[spec->id];
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_cistatic void mlx4_err_rule(struct mlx4_dev *dev, char *str,
90162306a36Sopenharmony_ci			  struct mlx4_net_trans_rule *rule)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci#define BUF_SIZE 256
90462306a36Sopenharmony_ci	struct mlx4_spec_list *cur;
90562306a36Sopenharmony_ci	char buf[BUF_SIZE];
90662306a36Sopenharmony_ci	int len = 0;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	mlx4_err(dev, "%s", str);
90962306a36Sopenharmony_ci	len += scnprintf(buf + len, BUF_SIZE - len,
91062306a36Sopenharmony_ci			 "port = %d prio = 0x%x qp = 0x%x ",
91162306a36Sopenharmony_ci			 rule->port, rule->priority, rule->qpn);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	list_for_each_entry(cur, &rule->list, list) {
91462306a36Sopenharmony_ci		switch (cur->id) {
91562306a36Sopenharmony_ci		case MLX4_NET_TRANS_RULE_ID_ETH:
91662306a36Sopenharmony_ci			len += scnprintf(buf + len, BUF_SIZE - len,
91762306a36Sopenharmony_ci					 "dmac = %pM ", &cur->eth.dst_mac);
91862306a36Sopenharmony_ci			if (cur->eth.ether_type)
91962306a36Sopenharmony_ci				len += scnprintf(buf + len, BUF_SIZE - len,
92062306a36Sopenharmony_ci						 "ethertype = 0x%x ",
92162306a36Sopenharmony_ci						 be16_to_cpu(cur->eth.ether_type));
92262306a36Sopenharmony_ci			if (cur->eth.vlan_id)
92362306a36Sopenharmony_ci				len += scnprintf(buf + len, BUF_SIZE - len,
92462306a36Sopenharmony_ci						 "vlan-id = %d ",
92562306a36Sopenharmony_ci						 be16_to_cpu(cur->eth.vlan_id));
92662306a36Sopenharmony_ci			break;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci		case MLX4_NET_TRANS_RULE_ID_IPV4:
92962306a36Sopenharmony_ci			if (cur->ipv4.src_ip)
93062306a36Sopenharmony_ci				len += scnprintf(buf + len, BUF_SIZE - len,
93162306a36Sopenharmony_ci						 "src-ip = %pI4 ",
93262306a36Sopenharmony_ci						 &cur->ipv4.src_ip);
93362306a36Sopenharmony_ci			if (cur->ipv4.dst_ip)
93462306a36Sopenharmony_ci				len += scnprintf(buf + len, BUF_SIZE - len,
93562306a36Sopenharmony_ci						 "dst-ip = %pI4 ",
93662306a36Sopenharmony_ci						 &cur->ipv4.dst_ip);
93762306a36Sopenharmony_ci			break;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci		case MLX4_NET_TRANS_RULE_ID_TCP:
94062306a36Sopenharmony_ci		case MLX4_NET_TRANS_RULE_ID_UDP:
94162306a36Sopenharmony_ci			if (cur->tcp_udp.src_port)
94262306a36Sopenharmony_ci				len += scnprintf(buf + len, BUF_SIZE - len,
94362306a36Sopenharmony_ci						 "src-port = %d ",
94462306a36Sopenharmony_ci						 be16_to_cpu(cur->tcp_udp.src_port));
94562306a36Sopenharmony_ci			if (cur->tcp_udp.dst_port)
94662306a36Sopenharmony_ci				len += scnprintf(buf + len, BUF_SIZE - len,
94762306a36Sopenharmony_ci						 "dst-port = %d ",
94862306a36Sopenharmony_ci						 be16_to_cpu(cur->tcp_udp.dst_port));
94962306a36Sopenharmony_ci			break;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci		case MLX4_NET_TRANS_RULE_ID_IB:
95262306a36Sopenharmony_ci			len += scnprintf(buf + len, BUF_SIZE - len,
95362306a36Sopenharmony_ci					 "dst-gid = %pI6\n", cur->ib.dst_gid);
95462306a36Sopenharmony_ci			len += scnprintf(buf + len, BUF_SIZE - len,
95562306a36Sopenharmony_ci					 "dst-gid-mask = %pI6\n",
95662306a36Sopenharmony_ci					 cur->ib.dst_gid_msk);
95762306a36Sopenharmony_ci			break;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci		case MLX4_NET_TRANS_RULE_ID_VXLAN:
96062306a36Sopenharmony_ci			len += scnprintf(buf + len, BUF_SIZE - len,
96162306a36Sopenharmony_ci					 "VNID = %d ", be32_to_cpu(cur->vxlan.vni));
96262306a36Sopenharmony_ci			break;
96362306a36Sopenharmony_ci		case MLX4_NET_TRANS_RULE_ID_IPV6:
96462306a36Sopenharmony_ci			break;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci		default:
96762306a36Sopenharmony_ci			break;
96862306a36Sopenharmony_ci		}
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci	len += scnprintf(buf + len, BUF_SIZE - len, "\n");
97162306a36Sopenharmony_ci	mlx4_err(dev, "%s", buf);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	if (len >= BUF_SIZE)
97462306a36Sopenharmony_ci		mlx4_err(dev, "Network rule error message was truncated, print buffer is too small\n");
97562306a36Sopenharmony_ci}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ciint mlx4_flow_attach(struct mlx4_dev *dev,
97862306a36Sopenharmony_ci		     struct mlx4_net_trans_rule *rule, u64 *reg_id)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	struct mlx4_cmd_mailbox *mailbox;
98162306a36Sopenharmony_ci	struct mlx4_spec_list *cur;
98262306a36Sopenharmony_ci	u32 size = 0;
98362306a36Sopenharmony_ci	int ret;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	mailbox = mlx4_alloc_cmd_mailbox(dev);
98662306a36Sopenharmony_ci	if (IS_ERR(mailbox))
98762306a36Sopenharmony_ci		return PTR_ERR(mailbox);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	if (!mlx4_qp_lookup(dev, rule->qpn)) {
99062306a36Sopenharmony_ci		mlx4_err_rule(dev, "QP doesn't exist\n", rule);
99162306a36Sopenharmony_ci		ret = -EINVAL;
99262306a36Sopenharmony_ci		goto out;
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	trans_rule_ctrl_to_hw(rule, mailbox->buf);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	size += sizeof(struct mlx4_net_trans_rule_hw_ctrl);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	list_for_each_entry(cur, &rule->list, list) {
100062306a36Sopenharmony_ci		ret = parse_trans_rule(dev, cur, mailbox->buf + size);
100162306a36Sopenharmony_ci		if (ret < 0)
100262306a36Sopenharmony_ci			goto out;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci		size += ret;
100562306a36Sopenharmony_ci	}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	ret = mlx4_QP_FLOW_STEERING_ATTACH(dev, mailbox, size >> 2, reg_id);
100862306a36Sopenharmony_ci	if (ret == -ENOMEM) {
100962306a36Sopenharmony_ci		mlx4_err_rule(dev,
101062306a36Sopenharmony_ci			      "mcg table is full. Fail to register network rule\n",
101162306a36Sopenharmony_ci			      rule);
101262306a36Sopenharmony_ci	} else if (ret) {
101362306a36Sopenharmony_ci		if (ret == -ENXIO) {
101462306a36Sopenharmony_ci			if (dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED)
101562306a36Sopenharmony_ci				mlx4_err_rule(dev,
101662306a36Sopenharmony_ci					      "DMFS is not enabled, "
101762306a36Sopenharmony_ci					      "failed to register network rule.\n",
101862306a36Sopenharmony_ci					      rule);
101962306a36Sopenharmony_ci			else
102062306a36Sopenharmony_ci				mlx4_err_rule(dev,
102162306a36Sopenharmony_ci					      "Rule exceeds the dmfs_high_rate_mode limitations, "
102262306a36Sopenharmony_ci					      "failed to register network rule.\n",
102362306a36Sopenharmony_ci					      rule);
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci		} else {
102662306a36Sopenharmony_ci			mlx4_err_rule(dev, "Fail to register network rule.\n", rule);
102762306a36Sopenharmony_ci		}
102862306a36Sopenharmony_ci	}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ciout:
103162306a36Sopenharmony_ci	mlx4_free_cmd_mailbox(dev, mailbox);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	return ret;
103462306a36Sopenharmony_ci}
103562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_flow_attach);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ciint mlx4_flow_detach(struct mlx4_dev *dev, u64 reg_id)
103862306a36Sopenharmony_ci{
103962306a36Sopenharmony_ci	int err;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	err = mlx4_QP_FLOW_STEERING_DETACH(dev, reg_id);
104262306a36Sopenharmony_ci	if (err)
104362306a36Sopenharmony_ci		mlx4_err(dev, "Fail to detach network rule. registration id = 0x%llx\n",
104462306a36Sopenharmony_ci			 reg_id);
104562306a36Sopenharmony_ci	return err;
104662306a36Sopenharmony_ci}
104762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_flow_detach);
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ciint mlx4_tunnel_steer_add(struct mlx4_dev *dev, const unsigned char *addr,
105062306a36Sopenharmony_ci			  int port, int qpn, u16 prio, u64 *reg_id)
105162306a36Sopenharmony_ci{
105262306a36Sopenharmony_ci	int err;
105362306a36Sopenharmony_ci	struct mlx4_spec_list spec_eth_outer = { {NULL} };
105462306a36Sopenharmony_ci	struct mlx4_spec_list spec_vxlan     = { {NULL} };
105562306a36Sopenharmony_ci	struct mlx4_spec_list spec_eth_inner = { {NULL} };
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	struct mlx4_net_trans_rule rule = {
105862306a36Sopenharmony_ci		.queue_mode = MLX4_NET_TRANS_Q_FIFO,
105962306a36Sopenharmony_ci		.exclusive = 0,
106062306a36Sopenharmony_ci		.allow_loopback = 1,
106162306a36Sopenharmony_ci		.promisc_mode = MLX4_FS_REGULAR,
106262306a36Sopenharmony_ci	};
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	__be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	rule.port = port;
106762306a36Sopenharmony_ci	rule.qpn = qpn;
106862306a36Sopenharmony_ci	rule.priority = prio;
106962306a36Sopenharmony_ci	INIT_LIST_HEAD(&rule.list);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	spec_eth_outer.id = MLX4_NET_TRANS_RULE_ID_ETH;
107262306a36Sopenharmony_ci	memcpy(spec_eth_outer.eth.dst_mac, addr, ETH_ALEN);
107362306a36Sopenharmony_ci	memcpy(spec_eth_outer.eth.dst_mac_msk, &mac_mask, ETH_ALEN);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	spec_vxlan.id = MLX4_NET_TRANS_RULE_ID_VXLAN;    /* any vxlan header */
107662306a36Sopenharmony_ci	spec_eth_inner.id = MLX4_NET_TRANS_RULE_ID_ETH;	 /* any inner eth header */
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	list_add_tail(&spec_eth_outer.list, &rule.list);
107962306a36Sopenharmony_ci	list_add_tail(&spec_vxlan.list,     &rule.list);
108062306a36Sopenharmony_ci	list_add_tail(&spec_eth_inner.list, &rule.list);
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	err = mlx4_flow_attach(dev, &rule, reg_id);
108362306a36Sopenharmony_ci	return err;
108462306a36Sopenharmony_ci}
108562306a36Sopenharmony_ciEXPORT_SYMBOL(mlx4_tunnel_steer_add);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ciint mlx4_FLOW_STEERING_IB_UC_QP_RANGE(struct mlx4_dev *dev, u32 min_range_qpn,
108862306a36Sopenharmony_ci				      u32 max_range_qpn)
108962306a36Sopenharmony_ci{
109062306a36Sopenharmony_ci	int err;
109162306a36Sopenharmony_ci	u64 in_param;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	in_param = ((u64) min_range_qpn) << 32;
109462306a36Sopenharmony_ci	in_param |= ((u64) max_range_qpn) & 0xFFFFFFFF;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	err = mlx4_cmd(dev, in_param, 0, 0,
109762306a36Sopenharmony_ci			MLX4_FLOW_STEERING_IB_UC_QP_RANGE,
109862306a36Sopenharmony_ci			MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	return err;
110162306a36Sopenharmony_ci}
110262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_FLOW_STEERING_IB_UC_QP_RANGE);
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ciint mlx4_qp_attach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
110562306a36Sopenharmony_ci			  int block_mcast_loopback, enum mlx4_protocol prot,
110662306a36Sopenharmony_ci			  enum mlx4_steer_type steer)
110762306a36Sopenharmony_ci{
110862306a36Sopenharmony_ci	struct mlx4_priv *priv = mlx4_priv(dev);
110962306a36Sopenharmony_ci	struct mlx4_cmd_mailbox *mailbox;
111062306a36Sopenharmony_ci	struct mlx4_mgm *mgm;
111162306a36Sopenharmony_ci	u32 members_count;
111262306a36Sopenharmony_ci	int index = -1, prev;
111362306a36Sopenharmony_ci	int link = 0;
111462306a36Sopenharmony_ci	int i;
111562306a36Sopenharmony_ci	int err;
111662306a36Sopenharmony_ci	u8 port = gid[5];
111762306a36Sopenharmony_ci	u8 new_entry = 0;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	mailbox = mlx4_alloc_cmd_mailbox(dev);
112062306a36Sopenharmony_ci	if (IS_ERR(mailbox))
112162306a36Sopenharmony_ci		return PTR_ERR(mailbox);
112262306a36Sopenharmony_ci	mgm = mailbox->buf;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	mutex_lock(&priv->mcg_table.mutex);
112562306a36Sopenharmony_ci	err = find_entry(dev, port, gid, prot,
112662306a36Sopenharmony_ci			 mailbox, &prev, &index);
112762306a36Sopenharmony_ci	if (err)
112862306a36Sopenharmony_ci		goto out;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	if (index != -1) {
113162306a36Sopenharmony_ci		if (!(be32_to_cpu(mgm->members_count) & 0xffffff)) {
113262306a36Sopenharmony_ci			new_entry = 1;
113362306a36Sopenharmony_ci			memcpy(mgm->gid, gid, 16);
113462306a36Sopenharmony_ci		}
113562306a36Sopenharmony_ci	} else {
113662306a36Sopenharmony_ci		link = 1;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci		index = mlx4_bitmap_alloc(&priv->mcg_table.bitmap);
113962306a36Sopenharmony_ci		if (index == -1) {
114062306a36Sopenharmony_ci			mlx4_err(dev, "No AMGM entries left\n");
114162306a36Sopenharmony_ci			err = -ENOMEM;
114262306a36Sopenharmony_ci			goto out;
114362306a36Sopenharmony_ci		}
114462306a36Sopenharmony_ci		index += dev->caps.num_mgms;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci		new_entry = 1;
114762306a36Sopenharmony_ci		memset(mgm, 0, sizeof(*mgm));
114862306a36Sopenharmony_ci		memcpy(mgm->gid, gid, 16);
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	members_count = be32_to_cpu(mgm->members_count) & 0xffffff;
115262306a36Sopenharmony_ci	if (members_count == dev->caps.num_qp_per_mgm) {
115362306a36Sopenharmony_ci		mlx4_err(dev, "MGM at index %x is full\n", index);
115462306a36Sopenharmony_ci		err = -ENOMEM;
115562306a36Sopenharmony_ci		goto out;
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	for (i = 0; i < members_count; ++i)
115962306a36Sopenharmony_ci		if ((be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK) == qp->qpn) {
116062306a36Sopenharmony_ci			mlx4_dbg(dev, "QP %06x already a member of MGM\n", qp->qpn);
116162306a36Sopenharmony_ci			err = 0;
116262306a36Sopenharmony_ci			goto out;
116362306a36Sopenharmony_ci		}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	if (block_mcast_loopback)
116662306a36Sopenharmony_ci		mgm->qp[members_count++] = cpu_to_be32((qp->qpn & MGM_QPN_MASK) |
116762306a36Sopenharmony_ci						       (1U << MGM_BLCK_LB_BIT));
116862306a36Sopenharmony_ci	else
116962306a36Sopenharmony_ci		mgm->qp[members_count++] = cpu_to_be32(qp->qpn & MGM_QPN_MASK);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	mgm->members_count = cpu_to_be32(members_count | (u32) prot << 30);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	err = mlx4_WRITE_ENTRY(dev, index, mailbox);
117462306a36Sopenharmony_ci	if (err)
117562306a36Sopenharmony_ci		goto out;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	if (!link)
117862306a36Sopenharmony_ci		goto out;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	err = mlx4_READ_ENTRY(dev, prev, mailbox);
118162306a36Sopenharmony_ci	if (err)
118262306a36Sopenharmony_ci		goto out;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	mgm->next_gid_index = cpu_to_be32(index << 6);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	err = mlx4_WRITE_ENTRY(dev, prev, mailbox);
118762306a36Sopenharmony_ci	if (err)
118862306a36Sopenharmony_ci		goto out;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ciout:
119162306a36Sopenharmony_ci	if (prot == MLX4_PROT_ETH && index != -1) {
119262306a36Sopenharmony_ci		/* manage the steering entry for promisc mode */
119362306a36Sopenharmony_ci		if (new_entry)
119462306a36Sopenharmony_ci			err = new_steering_entry(dev, port, steer,
119562306a36Sopenharmony_ci						 index, qp->qpn);
119662306a36Sopenharmony_ci		else
119762306a36Sopenharmony_ci			err = existing_steering_entry(dev, port, steer,
119862306a36Sopenharmony_ci						      index, qp->qpn);
119962306a36Sopenharmony_ci	}
120062306a36Sopenharmony_ci	if (err && link && index != -1) {
120162306a36Sopenharmony_ci		if (index < dev->caps.num_mgms)
120262306a36Sopenharmony_ci			mlx4_warn(dev, "Got AMGM index %d < %d\n",
120362306a36Sopenharmony_ci				  index, dev->caps.num_mgms);
120462306a36Sopenharmony_ci		else
120562306a36Sopenharmony_ci			mlx4_bitmap_free(&priv->mcg_table.bitmap,
120662306a36Sopenharmony_ci					 index - dev->caps.num_mgms, MLX4_USE_RR);
120762306a36Sopenharmony_ci	}
120862306a36Sopenharmony_ci	mutex_unlock(&priv->mcg_table.mutex);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	mlx4_free_cmd_mailbox(dev, mailbox);
121162306a36Sopenharmony_ci	return err;
121262306a36Sopenharmony_ci}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ciint mlx4_qp_detach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
121562306a36Sopenharmony_ci			  enum mlx4_protocol prot, enum mlx4_steer_type steer)
121662306a36Sopenharmony_ci{
121762306a36Sopenharmony_ci	struct mlx4_priv *priv = mlx4_priv(dev);
121862306a36Sopenharmony_ci	struct mlx4_cmd_mailbox *mailbox;
121962306a36Sopenharmony_ci	struct mlx4_mgm *mgm;
122062306a36Sopenharmony_ci	u32 members_count;
122162306a36Sopenharmony_ci	int prev, index;
122262306a36Sopenharmony_ci	int i, loc = -1;
122362306a36Sopenharmony_ci	int err;
122462306a36Sopenharmony_ci	u8 port = gid[5];
122562306a36Sopenharmony_ci	bool removed_entry = false;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	mailbox = mlx4_alloc_cmd_mailbox(dev);
122862306a36Sopenharmony_ci	if (IS_ERR(mailbox))
122962306a36Sopenharmony_ci		return PTR_ERR(mailbox);
123062306a36Sopenharmony_ci	mgm = mailbox->buf;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	mutex_lock(&priv->mcg_table.mutex);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	err = find_entry(dev, port, gid, prot,
123562306a36Sopenharmony_ci			 mailbox, &prev, &index);
123662306a36Sopenharmony_ci	if (err)
123762306a36Sopenharmony_ci		goto out;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	if (index == -1) {
124062306a36Sopenharmony_ci		mlx4_err(dev, "MGID %pI6 not found\n", gid);
124162306a36Sopenharmony_ci		err = -EINVAL;
124262306a36Sopenharmony_ci		goto out;
124362306a36Sopenharmony_ci	}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	/* If this QP is also a promisc QP, it shouldn't be removed only if
124662306a36Sopenharmony_ci	 * at least one none promisc QP is also attached to this MCG
124762306a36Sopenharmony_ci	 */
124862306a36Sopenharmony_ci	if (prot == MLX4_PROT_ETH &&
124962306a36Sopenharmony_ci	    check_duplicate_entry(dev, port, steer, index, qp->qpn) &&
125062306a36Sopenharmony_ci	    !promisc_steering_entry(dev, port, steer, index, qp->qpn, NULL))
125162306a36Sopenharmony_ci			goto out;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	members_count = be32_to_cpu(mgm->members_count) & 0xffffff;
125462306a36Sopenharmony_ci	for (i = 0; i < members_count; ++i)
125562306a36Sopenharmony_ci		if ((be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK) == qp->qpn) {
125662306a36Sopenharmony_ci			loc = i;
125762306a36Sopenharmony_ci			break;
125862306a36Sopenharmony_ci		}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	if (loc == -1) {
126162306a36Sopenharmony_ci		mlx4_err(dev, "QP %06x not found in MGM\n", qp->qpn);
126262306a36Sopenharmony_ci		err = -EINVAL;
126362306a36Sopenharmony_ci		goto out;
126462306a36Sopenharmony_ci	}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	/* copy the last QP in this MGM over removed QP */
126762306a36Sopenharmony_ci	mgm->qp[loc] = mgm->qp[members_count - 1];
126862306a36Sopenharmony_ci	mgm->qp[members_count - 1] = 0;
126962306a36Sopenharmony_ci	mgm->members_count = cpu_to_be32(--members_count | (u32) prot << 30);
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	if (prot == MLX4_PROT_ETH)
127262306a36Sopenharmony_ci		removed_entry = can_remove_steering_entry(dev, port, steer,
127362306a36Sopenharmony_ci								index, qp->qpn);
127462306a36Sopenharmony_ci	if (members_count && (prot != MLX4_PROT_ETH || !removed_entry)) {
127562306a36Sopenharmony_ci		err = mlx4_WRITE_ENTRY(dev, index, mailbox);
127662306a36Sopenharmony_ci		goto out;
127762306a36Sopenharmony_ci	}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	/* We are going to delete the entry, members count should be 0 */
128062306a36Sopenharmony_ci	mgm->members_count = cpu_to_be32((u32) prot << 30);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	if (prev == -1) {
128362306a36Sopenharmony_ci		/* Remove entry from MGM */
128462306a36Sopenharmony_ci		int amgm_index = be32_to_cpu(mgm->next_gid_index) >> 6;
128562306a36Sopenharmony_ci		if (amgm_index) {
128662306a36Sopenharmony_ci			err = mlx4_READ_ENTRY(dev, amgm_index, mailbox);
128762306a36Sopenharmony_ci			if (err)
128862306a36Sopenharmony_ci				goto out;
128962306a36Sopenharmony_ci		} else
129062306a36Sopenharmony_ci			memset(mgm->gid, 0, 16);
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci		err = mlx4_WRITE_ENTRY(dev, index, mailbox);
129362306a36Sopenharmony_ci		if (err)
129462306a36Sopenharmony_ci			goto out;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci		if (amgm_index) {
129762306a36Sopenharmony_ci			if (amgm_index < dev->caps.num_mgms)
129862306a36Sopenharmony_ci				mlx4_warn(dev, "MGM entry %d had AMGM index %d < %d\n",
129962306a36Sopenharmony_ci					  index, amgm_index, dev->caps.num_mgms);
130062306a36Sopenharmony_ci			else
130162306a36Sopenharmony_ci				mlx4_bitmap_free(&priv->mcg_table.bitmap,
130262306a36Sopenharmony_ci						 amgm_index - dev->caps.num_mgms, MLX4_USE_RR);
130362306a36Sopenharmony_ci		}
130462306a36Sopenharmony_ci	} else {
130562306a36Sopenharmony_ci		/* Remove entry from AMGM */
130662306a36Sopenharmony_ci		int cur_next_index = be32_to_cpu(mgm->next_gid_index) >> 6;
130762306a36Sopenharmony_ci		err = mlx4_READ_ENTRY(dev, prev, mailbox);
130862306a36Sopenharmony_ci		if (err)
130962306a36Sopenharmony_ci			goto out;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci		mgm->next_gid_index = cpu_to_be32(cur_next_index << 6);
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci		err = mlx4_WRITE_ENTRY(dev, prev, mailbox);
131462306a36Sopenharmony_ci		if (err)
131562306a36Sopenharmony_ci			goto out;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci		if (index < dev->caps.num_mgms)
131862306a36Sopenharmony_ci			mlx4_warn(dev, "entry %d had next AMGM index %d < %d\n",
131962306a36Sopenharmony_ci				  prev, index, dev->caps.num_mgms);
132062306a36Sopenharmony_ci		else
132162306a36Sopenharmony_ci			mlx4_bitmap_free(&priv->mcg_table.bitmap,
132262306a36Sopenharmony_ci					 index - dev->caps.num_mgms, MLX4_USE_RR);
132362306a36Sopenharmony_ci	}
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ciout:
132662306a36Sopenharmony_ci	mutex_unlock(&priv->mcg_table.mutex);
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	mlx4_free_cmd_mailbox(dev, mailbox);
132962306a36Sopenharmony_ci	if (err && dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR)
133062306a36Sopenharmony_ci		/* In case device is under an error, return success as a closing command */
133162306a36Sopenharmony_ci		err = 0;
133262306a36Sopenharmony_ci	return err;
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_cistatic int mlx4_QP_ATTACH(struct mlx4_dev *dev, struct mlx4_qp *qp,
133662306a36Sopenharmony_ci			  u8 gid[16], u8 attach, u8 block_loopback,
133762306a36Sopenharmony_ci			  enum mlx4_protocol prot)
133862306a36Sopenharmony_ci{
133962306a36Sopenharmony_ci	struct mlx4_cmd_mailbox *mailbox;
134062306a36Sopenharmony_ci	int err = 0;
134162306a36Sopenharmony_ci	int qpn;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	if (!mlx4_is_mfunc(dev))
134462306a36Sopenharmony_ci		return -EBADF;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	mailbox = mlx4_alloc_cmd_mailbox(dev);
134762306a36Sopenharmony_ci	if (IS_ERR(mailbox))
134862306a36Sopenharmony_ci		return PTR_ERR(mailbox);
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	memcpy(mailbox->buf, gid, 16);
135162306a36Sopenharmony_ci	qpn = qp->qpn;
135262306a36Sopenharmony_ci	qpn |= (prot << 28);
135362306a36Sopenharmony_ci	if (attach && block_loopback)
135462306a36Sopenharmony_ci		qpn |= (1 << 31);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	err = mlx4_cmd(dev, mailbox->dma, qpn, attach,
135762306a36Sopenharmony_ci		       MLX4_CMD_QP_ATTACH, MLX4_CMD_TIME_CLASS_A,
135862306a36Sopenharmony_ci		       MLX4_CMD_WRAPPED);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	mlx4_free_cmd_mailbox(dev, mailbox);
136162306a36Sopenharmony_ci	if (err && !attach &&
136262306a36Sopenharmony_ci	    dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR)
136362306a36Sopenharmony_ci		err = 0;
136462306a36Sopenharmony_ci	return err;
136562306a36Sopenharmony_ci}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ciint mlx4_trans_to_dmfs_attach(struct mlx4_dev *dev, struct mlx4_qp *qp,
136862306a36Sopenharmony_ci			      u8 gid[16], u8 port,
136962306a36Sopenharmony_ci			      int block_mcast_loopback,
137062306a36Sopenharmony_ci			      enum mlx4_protocol prot, u64 *reg_id)
137162306a36Sopenharmony_ci{
137262306a36Sopenharmony_ci		struct mlx4_spec_list spec = { {NULL} };
137362306a36Sopenharmony_ci		__be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16);
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci		struct mlx4_net_trans_rule rule = {
137662306a36Sopenharmony_ci			.queue_mode = MLX4_NET_TRANS_Q_FIFO,
137762306a36Sopenharmony_ci			.exclusive = 0,
137862306a36Sopenharmony_ci			.promisc_mode = MLX4_FS_REGULAR,
137962306a36Sopenharmony_ci			.priority = MLX4_DOMAIN_NIC,
138062306a36Sopenharmony_ci		};
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci		rule.allow_loopback = !block_mcast_loopback;
138362306a36Sopenharmony_ci		rule.port = port;
138462306a36Sopenharmony_ci		rule.qpn = qp->qpn;
138562306a36Sopenharmony_ci		INIT_LIST_HEAD(&rule.list);
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci		switch (prot) {
138862306a36Sopenharmony_ci		case MLX4_PROT_ETH:
138962306a36Sopenharmony_ci			spec.id = MLX4_NET_TRANS_RULE_ID_ETH;
139062306a36Sopenharmony_ci			memcpy(spec.eth.dst_mac, &gid[10], ETH_ALEN);
139162306a36Sopenharmony_ci			memcpy(spec.eth.dst_mac_msk, &mac_mask, ETH_ALEN);
139262306a36Sopenharmony_ci			break;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci		case MLX4_PROT_IB_IPV6:
139562306a36Sopenharmony_ci			spec.id = MLX4_NET_TRANS_RULE_ID_IB;
139662306a36Sopenharmony_ci			memcpy(spec.ib.dst_gid, gid, 16);
139762306a36Sopenharmony_ci			memset(&spec.ib.dst_gid_msk, 0xff, 16);
139862306a36Sopenharmony_ci			break;
139962306a36Sopenharmony_ci		default:
140062306a36Sopenharmony_ci			return -EINVAL;
140162306a36Sopenharmony_ci		}
140262306a36Sopenharmony_ci		list_add_tail(&spec.list, &rule.list);
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci		return mlx4_flow_attach(dev, &rule, reg_id);
140562306a36Sopenharmony_ci}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ciint mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
140862306a36Sopenharmony_ci			  u8 port, int block_mcast_loopback,
140962306a36Sopenharmony_ci			  enum mlx4_protocol prot, u64 *reg_id)
141062306a36Sopenharmony_ci{
141162306a36Sopenharmony_ci	switch (dev->caps.steering_mode) {
141262306a36Sopenharmony_ci	case MLX4_STEERING_MODE_A0:
141362306a36Sopenharmony_ci		if (prot == MLX4_PROT_ETH)
141462306a36Sopenharmony_ci			return 0;
141562306a36Sopenharmony_ci		fallthrough;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	case MLX4_STEERING_MODE_B0:
141862306a36Sopenharmony_ci		if (prot == MLX4_PROT_ETH)
141962306a36Sopenharmony_ci			gid[7] |= (MLX4_MC_STEER << 1);
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci		if (mlx4_is_mfunc(dev))
142262306a36Sopenharmony_ci			return mlx4_QP_ATTACH(dev, qp, gid, 1,
142362306a36Sopenharmony_ci					      block_mcast_loopback, prot);
142462306a36Sopenharmony_ci		return mlx4_qp_attach_common(dev, qp, gid,
142562306a36Sopenharmony_ci					     block_mcast_loopback, prot,
142662306a36Sopenharmony_ci					     MLX4_MC_STEER);
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	case MLX4_STEERING_MODE_DEVICE_MANAGED:
142962306a36Sopenharmony_ci		return mlx4_trans_to_dmfs_attach(dev, qp, gid, port,
143062306a36Sopenharmony_ci						 block_mcast_loopback,
143162306a36Sopenharmony_ci						 prot, reg_id);
143262306a36Sopenharmony_ci	default:
143362306a36Sopenharmony_ci		return -EINVAL;
143462306a36Sopenharmony_ci	}
143562306a36Sopenharmony_ci}
143662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_multicast_attach);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ciint mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16],
143962306a36Sopenharmony_ci			  enum mlx4_protocol prot, u64 reg_id)
144062306a36Sopenharmony_ci{
144162306a36Sopenharmony_ci	switch (dev->caps.steering_mode) {
144262306a36Sopenharmony_ci	case MLX4_STEERING_MODE_A0:
144362306a36Sopenharmony_ci		if (prot == MLX4_PROT_ETH)
144462306a36Sopenharmony_ci			return 0;
144562306a36Sopenharmony_ci		fallthrough;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	case MLX4_STEERING_MODE_B0:
144862306a36Sopenharmony_ci		if (prot == MLX4_PROT_ETH)
144962306a36Sopenharmony_ci			gid[7] |= (MLX4_MC_STEER << 1);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci		if (mlx4_is_mfunc(dev))
145262306a36Sopenharmony_ci			return mlx4_QP_ATTACH(dev, qp, gid, 0, 0, prot);
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci		return mlx4_qp_detach_common(dev, qp, gid, prot,
145562306a36Sopenharmony_ci					     MLX4_MC_STEER);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	case MLX4_STEERING_MODE_DEVICE_MANAGED:
145862306a36Sopenharmony_ci		return mlx4_flow_detach(dev, reg_id);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	default:
146162306a36Sopenharmony_ci		return -EINVAL;
146262306a36Sopenharmony_ci	}
146362306a36Sopenharmony_ci}
146462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_multicast_detach);
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ciint mlx4_flow_steer_promisc_add(struct mlx4_dev *dev, u8 port,
146762306a36Sopenharmony_ci				u32 qpn, enum mlx4_net_trans_promisc_mode mode)
146862306a36Sopenharmony_ci{
146962306a36Sopenharmony_ci	struct mlx4_net_trans_rule rule = {
147062306a36Sopenharmony_ci		.queue_mode = MLX4_NET_TRANS_Q_FIFO,
147162306a36Sopenharmony_ci		.exclusive = 0,
147262306a36Sopenharmony_ci		.allow_loopback = 1,
147362306a36Sopenharmony_ci	};
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	u64 *regid_p;
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	switch (mode) {
147862306a36Sopenharmony_ci	case MLX4_FS_ALL_DEFAULT:
147962306a36Sopenharmony_ci		regid_p = &dev->regid_promisc_array[port];
148062306a36Sopenharmony_ci		break;
148162306a36Sopenharmony_ci	case MLX4_FS_MC_DEFAULT:
148262306a36Sopenharmony_ci		regid_p = &dev->regid_allmulti_array[port];
148362306a36Sopenharmony_ci		break;
148462306a36Sopenharmony_ci	default:
148562306a36Sopenharmony_ci		return -1;
148662306a36Sopenharmony_ci	}
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	if (*regid_p != 0)
148962306a36Sopenharmony_ci		return -1;
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	rule.promisc_mode = mode;
149262306a36Sopenharmony_ci	rule.port = port;
149362306a36Sopenharmony_ci	rule.qpn = qpn;
149462306a36Sopenharmony_ci	INIT_LIST_HEAD(&rule.list);
149562306a36Sopenharmony_ci	mlx4_info(dev, "going promisc on %x\n", port);
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	return  mlx4_flow_attach(dev, &rule, regid_p);
149862306a36Sopenharmony_ci}
149962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_flow_steer_promisc_add);
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ciint mlx4_flow_steer_promisc_remove(struct mlx4_dev *dev, u8 port,
150262306a36Sopenharmony_ci				   enum mlx4_net_trans_promisc_mode mode)
150362306a36Sopenharmony_ci{
150462306a36Sopenharmony_ci	int ret;
150562306a36Sopenharmony_ci	u64 *regid_p;
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	switch (mode) {
150862306a36Sopenharmony_ci	case MLX4_FS_ALL_DEFAULT:
150962306a36Sopenharmony_ci		regid_p = &dev->regid_promisc_array[port];
151062306a36Sopenharmony_ci		break;
151162306a36Sopenharmony_ci	case MLX4_FS_MC_DEFAULT:
151262306a36Sopenharmony_ci		regid_p = &dev->regid_allmulti_array[port];
151362306a36Sopenharmony_ci		break;
151462306a36Sopenharmony_ci	default:
151562306a36Sopenharmony_ci		return -1;
151662306a36Sopenharmony_ci	}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	if (*regid_p == 0)
151962306a36Sopenharmony_ci		return -1;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	ret =  mlx4_flow_detach(dev, *regid_p);
152262306a36Sopenharmony_ci	if (ret == 0)
152362306a36Sopenharmony_ci		*regid_p = 0;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	return ret;
152662306a36Sopenharmony_ci}
152762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_flow_steer_promisc_remove);
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ciint mlx4_unicast_attach(struct mlx4_dev *dev,
153062306a36Sopenharmony_ci			struct mlx4_qp *qp, u8 gid[16],
153162306a36Sopenharmony_ci			int block_mcast_loopback, enum mlx4_protocol prot)
153262306a36Sopenharmony_ci{
153362306a36Sopenharmony_ci	if (prot == MLX4_PROT_ETH)
153462306a36Sopenharmony_ci		gid[7] |= (MLX4_UC_STEER << 1);
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	if (mlx4_is_mfunc(dev))
153762306a36Sopenharmony_ci		return mlx4_QP_ATTACH(dev, qp, gid, 1,
153862306a36Sopenharmony_ci					block_mcast_loopback, prot);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	return mlx4_qp_attach_common(dev, qp, gid, block_mcast_loopback,
154162306a36Sopenharmony_ci					prot, MLX4_UC_STEER);
154262306a36Sopenharmony_ci}
154362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_unicast_attach);
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ciint mlx4_unicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp,
154662306a36Sopenharmony_ci			       u8 gid[16], enum mlx4_protocol prot)
154762306a36Sopenharmony_ci{
154862306a36Sopenharmony_ci	if (prot == MLX4_PROT_ETH)
154962306a36Sopenharmony_ci		gid[7] |= (MLX4_UC_STEER << 1);
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	if (mlx4_is_mfunc(dev))
155262306a36Sopenharmony_ci		return mlx4_QP_ATTACH(dev, qp, gid, 0, 0, prot);
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci	return mlx4_qp_detach_common(dev, qp, gid, prot, MLX4_UC_STEER);
155562306a36Sopenharmony_ci}
155662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_unicast_detach);
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ciint mlx4_PROMISC_wrapper(struct mlx4_dev *dev, int slave,
155962306a36Sopenharmony_ci			 struct mlx4_vhcr *vhcr,
156062306a36Sopenharmony_ci			 struct mlx4_cmd_mailbox *inbox,
156162306a36Sopenharmony_ci			 struct mlx4_cmd_mailbox *outbox,
156262306a36Sopenharmony_ci			 struct mlx4_cmd_info *cmd)
156362306a36Sopenharmony_ci{
156462306a36Sopenharmony_ci	u32 qpn = (u32) vhcr->in_param & 0xffffffff;
156562306a36Sopenharmony_ci	int port = mlx4_slave_convert_port(dev, slave, vhcr->in_param >> 62);
156662306a36Sopenharmony_ci	enum mlx4_steer_type steer = vhcr->in_modifier;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	if (port < 0)
156962306a36Sopenharmony_ci		return -EINVAL;
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	/* Promiscuous unicast is not allowed in mfunc */
157262306a36Sopenharmony_ci	if (mlx4_is_mfunc(dev) && steer == MLX4_UC_STEER)
157362306a36Sopenharmony_ci		return 0;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	if (vhcr->op_modifier)
157662306a36Sopenharmony_ci		return add_promisc_qp(dev, port, steer, qpn);
157762306a36Sopenharmony_ci	else
157862306a36Sopenharmony_ci		return remove_promisc_qp(dev, port, steer, qpn);
157962306a36Sopenharmony_ci}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_cistatic int mlx4_PROMISC(struct mlx4_dev *dev, u32 qpn,
158262306a36Sopenharmony_ci			enum mlx4_steer_type steer, u8 add, u8 port)
158362306a36Sopenharmony_ci{
158462306a36Sopenharmony_ci	return mlx4_cmd(dev, (u64) qpn | (u64) port << 62, (u32) steer, add,
158562306a36Sopenharmony_ci			MLX4_CMD_PROMISC, MLX4_CMD_TIME_CLASS_A,
158662306a36Sopenharmony_ci			MLX4_CMD_WRAPPED);
158762306a36Sopenharmony_ci}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ciint mlx4_multicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port)
159062306a36Sopenharmony_ci{
159162306a36Sopenharmony_ci	if (mlx4_is_mfunc(dev))
159262306a36Sopenharmony_ci		return mlx4_PROMISC(dev, qpn, MLX4_MC_STEER, 1, port);
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	return add_promisc_qp(dev, port, MLX4_MC_STEER, qpn);
159562306a36Sopenharmony_ci}
159662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_multicast_promisc_add);
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ciint mlx4_multicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port)
159962306a36Sopenharmony_ci{
160062306a36Sopenharmony_ci	if (mlx4_is_mfunc(dev))
160162306a36Sopenharmony_ci		return mlx4_PROMISC(dev, qpn, MLX4_MC_STEER, 0, port);
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	return remove_promisc_qp(dev, port, MLX4_MC_STEER, qpn);
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_multicast_promisc_remove);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ciint mlx4_unicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port)
160862306a36Sopenharmony_ci{
160962306a36Sopenharmony_ci	if (mlx4_is_mfunc(dev))
161062306a36Sopenharmony_ci		return mlx4_PROMISC(dev, qpn, MLX4_UC_STEER, 1, port);
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	return add_promisc_qp(dev, port, MLX4_UC_STEER, qpn);
161362306a36Sopenharmony_ci}
161462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_unicast_promisc_add);
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ciint mlx4_unicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port)
161762306a36Sopenharmony_ci{
161862306a36Sopenharmony_ci	if (mlx4_is_mfunc(dev))
161962306a36Sopenharmony_ci		return mlx4_PROMISC(dev, qpn, MLX4_UC_STEER, 0, port);
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	return remove_promisc_qp(dev, port, MLX4_UC_STEER, qpn);
162262306a36Sopenharmony_ci}
162362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mlx4_unicast_promisc_remove);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ciint mlx4_init_mcg_table(struct mlx4_dev *dev)
162662306a36Sopenharmony_ci{
162762306a36Sopenharmony_ci	struct mlx4_priv *priv = mlx4_priv(dev);
162862306a36Sopenharmony_ci	int err;
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	/* No need for mcg_table when fw managed the mcg table*/
163162306a36Sopenharmony_ci	if (dev->caps.steering_mode ==
163262306a36Sopenharmony_ci	    MLX4_STEERING_MODE_DEVICE_MANAGED)
163362306a36Sopenharmony_ci		return 0;
163462306a36Sopenharmony_ci	err = mlx4_bitmap_init(&priv->mcg_table.bitmap, dev->caps.num_amgms,
163562306a36Sopenharmony_ci			       dev->caps.num_amgms - 1, 0, 0);
163662306a36Sopenharmony_ci	if (err)
163762306a36Sopenharmony_ci		return err;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	mutex_init(&priv->mcg_table.mutex);
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	return 0;
164262306a36Sopenharmony_ci}
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_civoid mlx4_cleanup_mcg_table(struct mlx4_dev *dev)
164562306a36Sopenharmony_ci{
164662306a36Sopenharmony_ci	if (dev->caps.steering_mode !=
164762306a36Sopenharmony_ci	    MLX4_STEERING_MODE_DEVICE_MANAGED)
164862306a36Sopenharmony_ci		mlx4_bitmap_cleanup(&mlx4_priv(dev)->mcg_table.bitmap);
164962306a36Sopenharmony_ci}
1650