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