162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2012 Mellanox Technologies. All rights reserved.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This software is available to you under a choice of one of two
562306a36Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
662306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
762306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the
862306a36Sopenharmony_ci * OpenIB.org BSD license below:
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
1162306a36Sopenharmony_ci *     without modification, are permitted provided that the following
1262306a36Sopenharmony_ci *     conditions are met:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
1562306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
1662306a36Sopenharmony_ci *        disclaimer.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
1962306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2062306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
2162306a36Sopenharmony_ci *        provided with the distribution.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2462306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2562306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2662306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2762306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2862306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
2962306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3062306a36Sopenharmony_ci * SOFTWARE.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <rdma/ib_mad.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <linux/mlx4/cmd.h>
3662306a36Sopenharmony_ci#include <linux/rbtree.h>
3762306a36Sopenharmony_ci#include <linux/idr.h>
3862306a36Sopenharmony_ci#include <rdma/ib_cm.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#include "mlx4_ib.h"
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define CM_CLEANUP_CACHE_TIMEOUT  (30 * HZ)
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct id_map_entry {
4562306a36Sopenharmony_ci	struct rb_node node;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	u32 sl_cm_id;
4862306a36Sopenharmony_ci	u32 pv_cm_id;
4962306a36Sopenharmony_ci	int slave_id;
5062306a36Sopenharmony_ci	int scheduled_delete;
5162306a36Sopenharmony_ci	struct mlx4_ib_dev *dev;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	struct list_head list;
5462306a36Sopenharmony_ci	struct delayed_work timeout;
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistruct rej_tmout_entry {
5862306a36Sopenharmony_ci	int slave;
5962306a36Sopenharmony_ci	u32 rem_pv_cm_id;
6062306a36Sopenharmony_ci	struct delayed_work timeout;
6162306a36Sopenharmony_ci	struct xarray *xa_rej_tmout;
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistruct cm_generic_msg {
6562306a36Sopenharmony_ci	struct ib_mad_hdr hdr;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	__be32 local_comm_id;
6862306a36Sopenharmony_ci	__be32 remote_comm_id;
6962306a36Sopenharmony_ci	unsigned char unused[2];
7062306a36Sopenharmony_ci	__be16 rej_reason;
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistruct cm_sidr_generic_msg {
7462306a36Sopenharmony_ci	struct ib_mad_hdr hdr;
7562306a36Sopenharmony_ci	__be32 request_id;
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistruct cm_req_msg {
7962306a36Sopenharmony_ci	unsigned char unused[0x60];
8062306a36Sopenharmony_ci	union ib_gid primary_path_sgid;
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic struct workqueue_struct *cm_wq;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic void set_local_comm_id(struct ib_mad *mad, u32 cm_id)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	if (mad->mad_hdr.attr_id == CM_SIDR_REQ_ATTR_ID) {
8862306a36Sopenharmony_ci		struct cm_sidr_generic_msg *msg =
8962306a36Sopenharmony_ci			(struct cm_sidr_generic_msg *)mad;
9062306a36Sopenharmony_ci		msg->request_id = cpu_to_be32(cm_id);
9162306a36Sopenharmony_ci	} else if (mad->mad_hdr.attr_id == CM_SIDR_REP_ATTR_ID) {
9262306a36Sopenharmony_ci		pr_err("trying to set local_comm_id in SIDR_REP\n");
9362306a36Sopenharmony_ci		return;
9462306a36Sopenharmony_ci	} else {
9562306a36Sopenharmony_ci		struct cm_generic_msg *msg = (struct cm_generic_msg *)mad;
9662306a36Sopenharmony_ci		msg->local_comm_id = cpu_to_be32(cm_id);
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic u32 get_local_comm_id(struct ib_mad *mad)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	if (mad->mad_hdr.attr_id == CM_SIDR_REQ_ATTR_ID) {
10362306a36Sopenharmony_ci		struct cm_sidr_generic_msg *msg =
10462306a36Sopenharmony_ci			(struct cm_sidr_generic_msg *)mad;
10562306a36Sopenharmony_ci		return be32_to_cpu(msg->request_id);
10662306a36Sopenharmony_ci	} else if (mad->mad_hdr.attr_id == CM_SIDR_REP_ATTR_ID) {
10762306a36Sopenharmony_ci		pr_err("trying to set local_comm_id in SIDR_REP\n");
10862306a36Sopenharmony_ci		return -1;
10962306a36Sopenharmony_ci	} else {
11062306a36Sopenharmony_ci		struct cm_generic_msg *msg = (struct cm_generic_msg *)mad;
11162306a36Sopenharmony_ci		return be32_to_cpu(msg->local_comm_id);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic void set_remote_comm_id(struct ib_mad *mad, u32 cm_id)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	if (mad->mad_hdr.attr_id == CM_SIDR_REP_ATTR_ID) {
11862306a36Sopenharmony_ci		struct cm_sidr_generic_msg *msg =
11962306a36Sopenharmony_ci			(struct cm_sidr_generic_msg *)mad;
12062306a36Sopenharmony_ci		msg->request_id = cpu_to_be32(cm_id);
12162306a36Sopenharmony_ci	} else if (mad->mad_hdr.attr_id == CM_SIDR_REQ_ATTR_ID) {
12262306a36Sopenharmony_ci		pr_err("trying to set remote_comm_id in SIDR_REQ\n");
12362306a36Sopenharmony_ci		return;
12462306a36Sopenharmony_ci	} else {
12562306a36Sopenharmony_ci		struct cm_generic_msg *msg = (struct cm_generic_msg *)mad;
12662306a36Sopenharmony_ci		msg->remote_comm_id = cpu_to_be32(cm_id);
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic u32 get_remote_comm_id(struct ib_mad *mad)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	if (mad->mad_hdr.attr_id == CM_SIDR_REP_ATTR_ID) {
13362306a36Sopenharmony_ci		struct cm_sidr_generic_msg *msg =
13462306a36Sopenharmony_ci			(struct cm_sidr_generic_msg *)mad;
13562306a36Sopenharmony_ci		return be32_to_cpu(msg->request_id);
13662306a36Sopenharmony_ci	} else if (mad->mad_hdr.attr_id == CM_SIDR_REQ_ATTR_ID) {
13762306a36Sopenharmony_ci		pr_err("trying to set remote_comm_id in SIDR_REQ\n");
13862306a36Sopenharmony_ci		return -1;
13962306a36Sopenharmony_ci	} else {
14062306a36Sopenharmony_ci		struct cm_generic_msg *msg = (struct cm_generic_msg *)mad;
14162306a36Sopenharmony_ci		return be32_to_cpu(msg->remote_comm_id);
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic union ib_gid gid_from_req_msg(struct ib_device *ibdev, struct ib_mad *mad)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct cm_req_msg *msg = (struct cm_req_msg *)mad;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return msg->primary_path_sgid;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci/* Lock should be taken before called */
15362306a36Sopenharmony_cistatic struct id_map_entry *
15462306a36Sopenharmony_ciid_map_find_by_sl_id(struct ib_device *ibdev, u32 slave_id, u32 sl_cm_id)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct rb_root *sl_id_map = &to_mdev(ibdev)->sriov.sl_id_map;
15762306a36Sopenharmony_ci	struct rb_node *node = sl_id_map->rb_node;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	while (node) {
16062306a36Sopenharmony_ci		struct id_map_entry *id_map_entry =
16162306a36Sopenharmony_ci			rb_entry(node, struct id_map_entry, node);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci		if (id_map_entry->sl_cm_id > sl_cm_id)
16462306a36Sopenharmony_ci			node = node->rb_left;
16562306a36Sopenharmony_ci		else if (id_map_entry->sl_cm_id < sl_cm_id)
16662306a36Sopenharmony_ci			node = node->rb_right;
16762306a36Sopenharmony_ci		else if (id_map_entry->slave_id > slave_id)
16862306a36Sopenharmony_ci			node = node->rb_left;
16962306a36Sopenharmony_ci		else if (id_map_entry->slave_id < slave_id)
17062306a36Sopenharmony_ci			node = node->rb_right;
17162306a36Sopenharmony_ci		else
17262306a36Sopenharmony_ci			return id_map_entry;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci	return NULL;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void id_map_ent_timeout(struct work_struct *work)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct delayed_work *delay = to_delayed_work(work);
18062306a36Sopenharmony_ci	struct id_map_entry *ent = container_of(delay, struct id_map_entry, timeout);
18162306a36Sopenharmony_ci	struct id_map_entry *found_ent;
18262306a36Sopenharmony_ci	struct mlx4_ib_dev *dev = ent->dev;
18362306a36Sopenharmony_ci	struct mlx4_ib_sriov *sriov = &dev->sriov;
18462306a36Sopenharmony_ci	struct rb_root *sl_id_map = &sriov->sl_id_map;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	spin_lock(&sriov->id_map_lock);
18762306a36Sopenharmony_ci	if (!xa_erase(&sriov->pv_id_table, ent->pv_cm_id))
18862306a36Sopenharmony_ci		goto out;
18962306a36Sopenharmony_ci	found_ent = id_map_find_by_sl_id(&dev->ib_dev, ent->slave_id, ent->sl_cm_id);
19062306a36Sopenharmony_ci	if (found_ent && found_ent == ent)
19162306a36Sopenharmony_ci		rb_erase(&found_ent->node, sl_id_map);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ciout:
19462306a36Sopenharmony_ci	list_del(&ent->list);
19562306a36Sopenharmony_ci	spin_unlock(&sriov->id_map_lock);
19662306a36Sopenharmony_ci	kfree(ent);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void sl_id_map_add(struct ib_device *ibdev, struct id_map_entry *new)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct rb_root *sl_id_map = &to_mdev(ibdev)->sriov.sl_id_map;
20262306a36Sopenharmony_ci	struct rb_node **link = &sl_id_map->rb_node, *parent = NULL;
20362306a36Sopenharmony_ci	struct id_map_entry *ent;
20462306a36Sopenharmony_ci	int slave_id = new->slave_id;
20562306a36Sopenharmony_ci	int sl_cm_id = new->sl_cm_id;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	ent = id_map_find_by_sl_id(ibdev, slave_id, sl_cm_id);
20862306a36Sopenharmony_ci	if (ent) {
20962306a36Sopenharmony_ci		pr_debug("overriding existing sl_id_map entry (cm_id = %x)\n",
21062306a36Sopenharmony_ci			 sl_cm_id);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		rb_replace_node(&ent->node, &new->node, sl_id_map);
21362306a36Sopenharmony_ci		return;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* Go to the bottom of the tree */
21762306a36Sopenharmony_ci	while (*link) {
21862306a36Sopenharmony_ci		parent = *link;
21962306a36Sopenharmony_ci		ent = rb_entry(parent, struct id_map_entry, node);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		if (ent->sl_cm_id > sl_cm_id || (ent->sl_cm_id == sl_cm_id && ent->slave_id > slave_id))
22262306a36Sopenharmony_ci			link = &(*link)->rb_left;
22362306a36Sopenharmony_ci		else
22462306a36Sopenharmony_ci			link = &(*link)->rb_right;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	rb_link_node(&new->node, parent, link);
22862306a36Sopenharmony_ci	rb_insert_color(&new->node, sl_id_map);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic struct id_map_entry *
23262306a36Sopenharmony_ciid_map_alloc(struct ib_device *ibdev, int slave_id, u32 sl_cm_id)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	int ret;
23562306a36Sopenharmony_ci	struct id_map_entry *ent;
23662306a36Sopenharmony_ci	struct mlx4_ib_sriov *sriov = &to_mdev(ibdev)->sriov;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	ent = kmalloc(sizeof (struct id_map_entry), GFP_KERNEL);
23962306a36Sopenharmony_ci	if (!ent)
24062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	ent->sl_cm_id = sl_cm_id;
24362306a36Sopenharmony_ci	ent->slave_id = slave_id;
24462306a36Sopenharmony_ci	ent->scheduled_delete = 0;
24562306a36Sopenharmony_ci	ent->dev = to_mdev(ibdev);
24662306a36Sopenharmony_ci	INIT_DELAYED_WORK(&ent->timeout, id_map_ent_timeout);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	ret = xa_alloc_cyclic(&sriov->pv_id_table, &ent->pv_cm_id, ent,
24962306a36Sopenharmony_ci			xa_limit_32b, &sriov->pv_id_next, GFP_KERNEL);
25062306a36Sopenharmony_ci	if (ret >= 0) {
25162306a36Sopenharmony_ci		spin_lock(&sriov->id_map_lock);
25262306a36Sopenharmony_ci		sl_id_map_add(ibdev, ent);
25362306a36Sopenharmony_ci		list_add_tail(&ent->list, &sriov->cm_list);
25462306a36Sopenharmony_ci		spin_unlock(&sriov->id_map_lock);
25562306a36Sopenharmony_ci		return ent;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/*error flow*/
25962306a36Sopenharmony_ci	kfree(ent);
26062306a36Sopenharmony_ci	mlx4_ib_warn(ibdev, "Allocation failed (err:0x%x)\n", ret);
26162306a36Sopenharmony_ci	return ERR_PTR(-ENOMEM);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic struct id_map_entry *
26562306a36Sopenharmony_ciid_map_get(struct ib_device *ibdev, int *pv_cm_id, int slave_id, int sl_cm_id)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct id_map_entry *ent;
26862306a36Sopenharmony_ci	struct mlx4_ib_sriov *sriov = &to_mdev(ibdev)->sriov;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	spin_lock(&sriov->id_map_lock);
27162306a36Sopenharmony_ci	if (*pv_cm_id == -1) {
27262306a36Sopenharmony_ci		ent = id_map_find_by_sl_id(ibdev, slave_id, sl_cm_id);
27362306a36Sopenharmony_ci		if (ent)
27462306a36Sopenharmony_ci			*pv_cm_id = (int) ent->pv_cm_id;
27562306a36Sopenharmony_ci	} else
27662306a36Sopenharmony_ci		ent = xa_load(&sriov->pv_id_table, *pv_cm_id);
27762306a36Sopenharmony_ci	spin_unlock(&sriov->id_map_lock);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return ent;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic void schedule_delayed(struct ib_device *ibdev, struct id_map_entry *id)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	struct mlx4_ib_sriov *sriov = &to_mdev(ibdev)->sriov;
28562306a36Sopenharmony_ci	unsigned long flags;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	spin_lock(&sriov->id_map_lock);
28862306a36Sopenharmony_ci	spin_lock_irqsave(&sriov->going_down_lock, flags);
28962306a36Sopenharmony_ci	/*make sure that there is no schedule inside the scheduled work.*/
29062306a36Sopenharmony_ci	if (!sriov->is_going_down && !id->scheduled_delete) {
29162306a36Sopenharmony_ci		id->scheduled_delete = 1;
29262306a36Sopenharmony_ci		queue_delayed_work(cm_wq, &id->timeout, CM_CLEANUP_CACHE_TIMEOUT);
29362306a36Sopenharmony_ci	} else if (id->scheduled_delete) {
29462306a36Sopenharmony_ci		/* Adjust timeout if already scheduled */
29562306a36Sopenharmony_ci		mod_delayed_work(cm_wq, &id->timeout, CM_CLEANUP_CACHE_TIMEOUT);
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci	spin_unlock_irqrestore(&sriov->going_down_lock, flags);
29862306a36Sopenharmony_ci	spin_unlock(&sriov->id_map_lock);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci#define REJ_REASON(m) be16_to_cpu(((struct cm_generic_msg *)(m))->rej_reason)
30262306a36Sopenharmony_ciint mlx4_ib_multiplex_cm_handler(struct ib_device *ibdev, int port, int slave_id,
30362306a36Sopenharmony_ci		struct ib_mad *mad)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct id_map_entry *id;
30662306a36Sopenharmony_ci	u32 sl_cm_id;
30762306a36Sopenharmony_ci	int pv_cm_id = -1;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (mad->mad_hdr.attr_id == CM_REQ_ATTR_ID ||
31062306a36Sopenharmony_ci	    mad->mad_hdr.attr_id == CM_REP_ATTR_ID ||
31162306a36Sopenharmony_ci	    mad->mad_hdr.attr_id == CM_MRA_ATTR_ID ||
31262306a36Sopenharmony_ci	    mad->mad_hdr.attr_id == CM_SIDR_REQ_ATTR_ID ||
31362306a36Sopenharmony_ci	    (mad->mad_hdr.attr_id == CM_REJ_ATTR_ID && REJ_REASON(mad) == IB_CM_REJ_TIMEOUT)) {
31462306a36Sopenharmony_ci		sl_cm_id = get_local_comm_id(mad);
31562306a36Sopenharmony_ci		id = id_map_get(ibdev, &pv_cm_id, slave_id, sl_cm_id);
31662306a36Sopenharmony_ci		if (id)
31762306a36Sopenharmony_ci			goto cont;
31862306a36Sopenharmony_ci		id = id_map_alloc(ibdev, slave_id, sl_cm_id);
31962306a36Sopenharmony_ci		if (IS_ERR(id)) {
32062306a36Sopenharmony_ci			mlx4_ib_warn(ibdev, "%s: id{slave: %d, sl_cm_id: 0x%x} Failed to id_map_alloc\n",
32162306a36Sopenharmony_ci				__func__, slave_id, sl_cm_id);
32262306a36Sopenharmony_ci			return PTR_ERR(id);
32362306a36Sopenharmony_ci		}
32462306a36Sopenharmony_ci	} else if (mad->mad_hdr.attr_id == CM_REJ_ATTR_ID ||
32562306a36Sopenharmony_ci		   mad->mad_hdr.attr_id == CM_SIDR_REP_ATTR_ID) {
32662306a36Sopenharmony_ci		return 0;
32762306a36Sopenharmony_ci	} else {
32862306a36Sopenharmony_ci		sl_cm_id = get_local_comm_id(mad);
32962306a36Sopenharmony_ci		id = id_map_get(ibdev, &pv_cm_id, slave_id, sl_cm_id);
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (!id) {
33362306a36Sopenharmony_ci		pr_debug("id{slave: %d, sl_cm_id: 0x%x} is NULL! attr_id: 0x%x\n",
33462306a36Sopenharmony_ci			 slave_id, sl_cm_id, be16_to_cpu(mad->mad_hdr.attr_id));
33562306a36Sopenharmony_ci		return -EINVAL;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cicont:
33962306a36Sopenharmony_ci	set_local_comm_id(mad, id->pv_cm_id);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	if (mad->mad_hdr.attr_id == CM_DREQ_ATTR_ID)
34262306a36Sopenharmony_ci		schedule_delayed(ibdev, id);
34362306a36Sopenharmony_ci	return 0;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic void rej_tmout_timeout(struct work_struct *work)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct delayed_work *delay = to_delayed_work(work);
34962306a36Sopenharmony_ci	struct rej_tmout_entry *item = container_of(delay, struct rej_tmout_entry, timeout);
35062306a36Sopenharmony_ci	struct rej_tmout_entry *deleted;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	deleted = xa_cmpxchg(item->xa_rej_tmout, item->rem_pv_cm_id, item, NULL, 0);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (deleted != item)
35562306a36Sopenharmony_ci		pr_debug("deleted(%p) != item(%p)\n", deleted, item);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	kfree(item);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic int alloc_rej_tmout(struct mlx4_ib_sriov *sriov, u32 rem_pv_cm_id, int slave)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct rej_tmout_entry *item;
36362306a36Sopenharmony_ci	struct rej_tmout_entry *old;
36462306a36Sopenharmony_ci	int ret = 0;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	xa_lock(&sriov->xa_rej_tmout);
36762306a36Sopenharmony_ci	item = xa_load(&sriov->xa_rej_tmout, (unsigned long)rem_pv_cm_id);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (item) {
37062306a36Sopenharmony_ci		if (xa_err(item))
37162306a36Sopenharmony_ci			ret =  xa_err(item);
37262306a36Sopenharmony_ci		else
37362306a36Sopenharmony_ci			/* If a retry, adjust delayed work */
37462306a36Sopenharmony_ci			mod_delayed_work(cm_wq, &item->timeout, CM_CLEANUP_CACHE_TIMEOUT);
37562306a36Sopenharmony_ci		goto err_or_exists;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci	xa_unlock(&sriov->xa_rej_tmout);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	item = kmalloc(sizeof(*item), GFP_KERNEL);
38062306a36Sopenharmony_ci	if (!item)
38162306a36Sopenharmony_ci		return -ENOMEM;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	INIT_DELAYED_WORK(&item->timeout, rej_tmout_timeout);
38462306a36Sopenharmony_ci	item->slave = slave;
38562306a36Sopenharmony_ci	item->rem_pv_cm_id = rem_pv_cm_id;
38662306a36Sopenharmony_ci	item->xa_rej_tmout = &sriov->xa_rej_tmout;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	old = xa_cmpxchg(&sriov->xa_rej_tmout, (unsigned long)rem_pv_cm_id, NULL, item, GFP_KERNEL);
38962306a36Sopenharmony_ci	if (old) {
39062306a36Sopenharmony_ci		pr_debug(
39162306a36Sopenharmony_ci			"Non-null old entry (%p) or error (%d) when inserting\n",
39262306a36Sopenharmony_ci			old, xa_err(old));
39362306a36Sopenharmony_ci		kfree(item);
39462306a36Sopenharmony_ci		return xa_err(old);
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	queue_delayed_work(cm_wq, &item->timeout, CM_CLEANUP_CACHE_TIMEOUT);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	return 0;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cierr_or_exists:
40262306a36Sopenharmony_ci	xa_unlock(&sriov->xa_rej_tmout);
40362306a36Sopenharmony_ci	return ret;
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic int lookup_rej_tmout_slave(struct mlx4_ib_sriov *sriov, u32 rem_pv_cm_id)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct rej_tmout_entry *item;
40962306a36Sopenharmony_ci	int slave;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	xa_lock(&sriov->xa_rej_tmout);
41262306a36Sopenharmony_ci	item = xa_load(&sriov->xa_rej_tmout, (unsigned long)rem_pv_cm_id);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	if (!item || xa_err(item)) {
41562306a36Sopenharmony_ci		pr_debug("Could not find slave. rem_pv_cm_id 0x%x error: %d\n",
41662306a36Sopenharmony_ci			 rem_pv_cm_id, xa_err(item));
41762306a36Sopenharmony_ci		slave = !item ? -ENOENT : xa_err(item);
41862306a36Sopenharmony_ci	} else {
41962306a36Sopenharmony_ci		slave = item->slave;
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci	xa_unlock(&sriov->xa_rej_tmout);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	return slave;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ciint mlx4_ib_demux_cm_handler(struct ib_device *ibdev, int port, int *slave,
42762306a36Sopenharmony_ci			     struct ib_mad *mad)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	struct mlx4_ib_sriov *sriov = &to_mdev(ibdev)->sriov;
43062306a36Sopenharmony_ci	u32 rem_pv_cm_id = get_local_comm_id(mad);
43162306a36Sopenharmony_ci	u32 pv_cm_id;
43262306a36Sopenharmony_ci	struct id_map_entry *id;
43362306a36Sopenharmony_ci	int sts;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (mad->mad_hdr.attr_id == CM_REQ_ATTR_ID ||
43662306a36Sopenharmony_ci	    mad->mad_hdr.attr_id == CM_SIDR_REQ_ATTR_ID) {
43762306a36Sopenharmony_ci		union ib_gid gid;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		if (!slave)
44062306a36Sopenharmony_ci			return 0;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		gid = gid_from_req_msg(ibdev, mad);
44362306a36Sopenharmony_ci		*slave = mlx4_ib_find_real_gid(ibdev, port, gid.global.interface_id);
44462306a36Sopenharmony_ci		if (*slave < 0) {
44562306a36Sopenharmony_ci			mlx4_ib_warn(ibdev, "failed matching slave_id by gid (0x%llx)\n",
44662306a36Sopenharmony_ci				     be64_to_cpu(gid.global.interface_id));
44762306a36Sopenharmony_ci			return -ENOENT;
44862306a36Sopenharmony_ci		}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		sts = alloc_rej_tmout(sriov, rem_pv_cm_id, *slave);
45162306a36Sopenharmony_ci		if (sts)
45262306a36Sopenharmony_ci			/* Even if this fails, we pass on the REQ to the slave */
45362306a36Sopenharmony_ci			pr_debug("Could not allocate rej_tmout entry. rem_pv_cm_id 0x%x slave %d status %d\n",
45462306a36Sopenharmony_ci				 rem_pv_cm_id, *slave, sts);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		return 0;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	pv_cm_id = get_remote_comm_id(mad);
46062306a36Sopenharmony_ci	id = id_map_get(ibdev, (int *)&pv_cm_id, -1, -1);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	if (!id) {
46362306a36Sopenharmony_ci		if (mad->mad_hdr.attr_id == CM_REJ_ATTR_ID &&
46462306a36Sopenharmony_ci		    REJ_REASON(mad) == IB_CM_REJ_TIMEOUT && slave) {
46562306a36Sopenharmony_ci			*slave = lookup_rej_tmout_slave(sriov, rem_pv_cm_id);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci			return (*slave < 0) ? *slave : 0;
46862306a36Sopenharmony_ci		}
46962306a36Sopenharmony_ci		pr_debug("Couldn't find an entry for pv_cm_id 0x%x, attr_id 0x%x\n",
47062306a36Sopenharmony_ci			 pv_cm_id, be16_to_cpu(mad->mad_hdr.attr_id));
47162306a36Sopenharmony_ci		return -ENOENT;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (slave)
47562306a36Sopenharmony_ci		*slave = id->slave_id;
47662306a36Sopenharmony_ci	set_remote_comm_id(mad, id->sl_cm_id);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	if (mad->mad_hdr.attr_id == CM_DREQ_ATTR_ID ||
47962306a36Sopenharmony_ci	    mad->mad_hdr.attr_id == CM_REJ_ATTR_ID)
48062306a36Sopenharmony_ci		schedule_delayed(ibdev, id);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	return 0;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_civoid mlx4_ib_cm_paravirt_init(struct mlx4_ib_dev *dev)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	spin_lock_init(&dev->sriov.id_map_lock);
48862306a36Sopenharmony_ci	INIT_LIST_HEAD(&dev->sriov.cm_list);
48962306a36Sopenharmony_ci	dev->sriov.sl_id_map = RB_ROOT;
49062306a36Sopenharmony_ci	xa_init_flags(&dev->sriov.pv_id_table, XA_FLAGS_ALLOC);
49162306a36Sopenharmony_ci	xa_init(&dev->sriov.xa_rej_tmout);
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_cistatic void rej_tmout_xa_cleanup(struct mlx4_ib_sriov *sriov, int slave)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct rej_tmout_entry *item;
49762306a36Sopenharmony_ci	bool flush_needed = false;
49862306a36Sopenharmony_ci	unsigned long id;
49962306a36Sopenharmony_ci	int cnt = 0;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	xa_lock(&sriov->xa_rej_tmout);
50262306a36Sopenharmony_ci	xa_for_each(&sriov->xa_rej_tmout, id, item) {
50362306a36Sopenharmony_ci		if (slave < 0 || slave == item->slave) {
50462306a36Sopenharmony_ci			mod_delayed_work(cm_wq, &item->timeout, 0);
50562306a36Sopenharmony_ci			flush_needed = true;
50662306a36Sopenharmony_ci			++cnt;
50762306a36Sopenharmony_ci		}
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci	xa_unlock(&sriov->xa_rej_tmout);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (flush_needed) {
51262306a36Sopenharmony_ci		flush_workqueue(cm_wq);
51362306a36Sopenharmony_ci		pr_debug("Deleted %d entries in xarray for slave %d during cleanup\n",
51462306a36Sopenharmony_ci			 cnt, slave);
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (slave < 0)
51862306a36Sopenharmony_ci		WARN_ON(!xa_empty(&sriov->xa_rej_tmout));
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci/* slave = -1 ==> all slaves */
52262306a36Sopenharmony_ci/* TBD -- call paravirt clean for single slave.  Need for slave RESET event */
52362306a36Sopenharmony_civoid mlx4_ib_cm_paravirt_clean(struct mlx4_ib_dev *dev, int slave)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct mlx4_ib_sriov *sriov = &dev->sriov;
52662306a36Sopenharmony_ci	struct rb_root *sl_id_map = &sriov->sl_id_map;
52762306a36Sopenharmony_ci	struct list_head lh;
52862306a36Sopenharmony_ci	struct rb_node *nd;
52962306a36Sopenharmony_ci	int need_flush = 0;
53062306a36Sopenharmony_ci	struct id_map_entry *map, *tmp_map;
53162306a36Sopenharmony_ci	/* cancel all delayed work queue entries */
53262306a36Sopenharmony_ci	INIT_LIST_HEAD(&lh);
53362306a36Sopenharmony_ci	spin_lock(&sriov->id_map_lock);
53462306a36Sopenharmony_ci	list_for_each_entry_safe(map, tmp_map, &dev->sriov.cm_list, list) {
53562306a36Sopenharmony_ci		if (slave < 0 || slave == map->slave_id) {
53662306a36Sopenharmony_ci			if (map->scheduled_delete)
53762306a36Sopenharmony_ci				need_flush |= !cancel_delayed_work(&map->timeout);
53862306a36Sopenharmony_ci		}
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	spin_unlock(&sriov->id_map_lock);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (need_flush)
54462306a36Sopenharmony_ci		flush_workqueue(cm_wq); /* make sure all timers were flushed */
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	/* now, remove all leftover entries from databases*/
54762306a36Sopenharmony_ci	spin_lock(&sriov->id_map_lock);
54862306a36Sopenharmony_ci	if (slave < 0) {
54962306a36Sopenharmony_ci		while (rb_first(sl_id_map)) {
55062306a36Sopenharmony_ci			struct id_map_entry *ent =
55162306a36Sopenharmony_ci				rb_entry(rb_first(sl_id_map),
55262306a36Sopenharmony_ci					 struct id_map_entry, node);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci			rb_erase(&ent->node, sl_id_map);
55562306a36Sopenharmony_ci			xa_erase(&sriov->pv_id_table, ent->pv_cm_id);
55662306a36Sopenharmony_ci		}
55762306a36Sopenharmony_ci		list_splice_init(&dev->sriov.cm_list, &lh);
55862306a36Sopenharmony_ci	} else {
55962306a36Sopenharmony_ci		/* first, move nodes belonging to slave to db remove list */
56062306a36Sopenharmony_ci		nd = rb_first(sl_id_map);
56162306a36Sopenharmony_ci		while (nd) {
56262306a36Sopenharmony_ci			struct id_map_entry *ent =
56362306a36Sopenharmony_ci				rb_entry(nd, struct id_map_entry, node);
56462306a36Sopenharmony_ci			nd = rb_next(nd);
56562306a36Sopenharmony_ci			if (ent->slave_id == slave)
56662306a36Sopenharmony_ci				list_move_tail(&ent->list, &lh);
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci		/* remove those nodes from databases */
56962306a36Sopenharmony_ci		list_for_each_entry_safe(map, tmp_map, &lh, list) {
57062306a36Sopenharmony_ci			rb_erase(&map->node, sl_id_map);
57162306a36Sopenharmony_ci			xa_erase(&sriov->pv_id_table, map->pv_cm_id);
57262306a36Sopenharmony_ci		}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		/* add remaining nodes from cm_list */
57562306a36Sopenharmony_ci		list_for_each_entry_safe(map, tmp_map, &dev->sriov.cm_list, list) {
57662306a36Sopenharmony_ci			if (slave == map->slave_id)
57762306a36Sopenharmony_ci				list_move_tail(&map->list, &lh);
57862306a36Sopenharmony_ci		}
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	spin_unlock(&sriov->id_map_lock);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/* free any map entries left behind due to cancel_delayed_work above */
58462306a36Sopenharmony_ci	list_for_each_entry_safe(map, tmp_map, &lh, list) {
58562306a36Sopenharmony_ci		list_del(&map->list);
58662306a36Sopenharmony_ci		kfree(map);
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	rej_tmout_xa_cleanup(sriov, slave);
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ciint mlx4_ib_cm_init(void)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	cm_wq = alloc_workqueue("mlx4_ib_cm", 0, 0);
59562306a36Sopenharmony_ci	if (!cm_wq)
59662306a36Sopenharmony_ci		return -ENOMEM;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	return 0;
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_civoid mlx4_ib_cm_destroy(void)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	destroy_workqueue(cm_wq);
60462306a36Sopenharmony_ci}
605