162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <rdma/rdma_cm.h> 762306a36Sopenharmony_ci#include <rdma/ib_verbs.h> 862306a36Sopenharmony_ci#include <rdma/restrack.h> 962306a36Sopenharmony_ci#include <rdma/rdma_counter.h> 1062306a36Sopenharmony_ci#include <linux/mutex.h> 1162306a36Sopenharmony_ci#include <linux/sched/task.h> 1262306a36Sopenharmony_ci#include <linux/pid_namespace.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "cma_priv.h" 1562306a36Sopenharmony_ci#include "restrack.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/** 1862306a36Sopenharmony_ci * rdma_restrack_init() - initialize and allocate resource tracking 1962306a36Sopenharmony_ci * @dev: IB device 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Return: 0 on success 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ciint rdma_restrack_init(struct ib_device *dev) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct rdma_restrack_root *rt; 2662306a36Sopenharmony_ci int i; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci dev->res = kcalloc(RDMA_RESTRACK_MAX, sizeof(*rt), GFP_KERNEL); 2962306a36Sopenharmony_ci if (!dev->res) 3062306a36Sopenharmony_ci return -ENOMEM; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci rt = dev->res; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci for (i = 0; i < RDMA_RESTRACK_MAX; i++) 3562306a36Sopenharmony_ci xa_init_flags(&rt[i].xa, XA_FLAGS_ALLOC); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci return 0; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic const char *type2str(enum rdma_restrack_type type) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci static const char * const names[RDMA_RESTRACK_MAX] = { 4362306a36Sopenharmony_ci [RDMA_RESTRACK_PD] = "PD", 4462306a36Sopenharmony_ci [RDMA_RESTRACK_CQ] = "CQ", 4562306a36Sopenharmony_ci [RDMA_RESTRACK_QP] = "QP", 4662306a36Sopenharmony_ci [RDMA_RESTRACK_CM_ID] = "CM_ID", 4762306a36Sopenharmony_ci [RDMA_RESTRACK_MR] = "MR", 4862306a36Sopenharmony_ci [RDMA_RESTRACK_CTX] = "CTX", 4962306a36Sopenharmony_ci [RDMA_RESTRACK_COUNTER] = "COUNTER", 5062306a36Sopenharmony_ci [RDMA_RESTRACK_SRQ] = "SRQ", 5162306a36Sopenharmony_ci }; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return names[type]; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/** 5762306a36Sopenharmony_ci * rdma_restrack_clean() - clean resource tracking 5862306a36Sopenharmony_ci * @dev: IB device 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_civoid rdma_restrack_clean(struct ib_device *dev) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct rdma_restrack_root *rt = dev->res; 6362306a36Sopenharmony_ci struct rdma_restrack_entry *e; 6462306a36Sopenharmony_ci char buf[TASK_COMM_LEN]; 6562306a36Sopenharmony_ci bool found = false; 6662306a36Sopenharmony_ci const char *owner; 6762306a36Sopenharmony_ci int i; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci for (i = 0 ; i < RDMA_RESTRACK_MAX; i++) { 7062306a36Sopenharmony_ci struct xarray *xa = &dev->res[i].xa; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (!xa_empty(xa)) { 7362306a36Sopenharmony_ci unsigned long index; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (!found) { 7662306a36Sopenharmony_ci pr_err("restrack: %s", CUT_HERE); 7762306a36Sopenharmony_ci dev_err(&dev->dev, "BUG: RESTRACK detected leak of resources\n"); 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci xa_for_each(xa, index, e) { 8062306a36Sopenharmony_ci if (rdma_is_kernel_res(e)) { 8162306a36Sopenharmony_ci owner = e->kern_name; 8262306a36Sopenharmony_ci } else { 8362306a36Sopenharmony_ci /* 8462306a36Sopenharmony_ci * There is no need to call get_task_struct here, 8562306a36Sopenharmony_ci * because we can be here only if there are more 8662306a36Sopenharmony_ci * get_task_struct() call than put_task_struct(). 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci get_task_comm(buf, e->task); 8962306a36Sopenharmony_ci owner = buf; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci pr_err("restrack: %s %s object allocated by %s is not freed\n", 9362306a36Sopenharmony_ci rdma_is_kernel_res(e) ? "Kernel" : 9462306a36Sopenharmony_ci "User", 9562306a36Sopenharmony_ci type2str(e->type), owner); 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci found = true; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci xa_destroy(xa); 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci if (found) 10262306a36Sopenharmony_ci pr_err("restrack: %s", CUT_HERE); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci kfree(rt); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/** 10862306a36Sopenharmony_ci * rdma_restrack_count() - the current usage of specific object 10962306a36Sopenharmony_ci * @dev: IB device 11062306a36Sopenharmony_ci * @type: actual type of object to operate 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ciint rdma_restrack_count(struct ib_device *dev, enum rdma_restrack_type type) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct rdma_restrack_root *rt = &dev->res[type]; 11562306a36Sopenharmony_ci struct rdma_restrack_entry *e; 11662306a36Sopenharmony_ci XA_STATE(xas, &rt->xa, 0); 11762306a36Sopenharmony_ci u32 cnt = 0; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci xa_lock(&rt->xa); 12062306a36Sopenharmony_ci xas_for_each(&xas, e, U32_MAX) 12162306a36Sopenharmony_ci cnt++; 12262306a36Sopenharmony_ci xa_unlock(&rt->xa); 12362306a36Sopenharmony_ci return cnt; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_restrack_count); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic struct ib_device *res_to_dev(struct rdma_restrack_entry *res) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci switch (res->type) { 13062306a36Sopenharmony_ci case RDMA_RESTRACK_PD: 13162306a36Sopenharmony_ci return container_of(res, struct ib_pd, res)->device; 13262306a36Sopenharmony_ci case RDMA_RESTRACK_CQ: 13362306a36Sopenharmony_ci return container_of(res, struct ib_cq, res)->device; 13462306a36Sopenharmony_ci case RDMA_RESTRACK_QP: 13562306a36Sopenharmony_ci return container_of(res, struct ib_qp, res)->device; 13662306a36Sopenharmony_ci case RDMA_RESTRACK_CM_ID: 13762306a36Sopenharmony_ci return container_of(res, struct rdma_id_private, 13862306a36Sopenharmony_ci res)->id.device; 13962306a36Sopenharmony_ci case RDMA_RESTRACK_MR: 14062306a36Sopenharmony_ci return container_of(res, struct ib_mr, res)->device; 14162306a36Sopenharmony_ci case RDMA_RESTRACK_CTX: 14262306a36Sopenharmony_ci return container_of(res, struct ib_ucontext, res)->device; 14362306a36Sopenharmony_ci case RDMA_RESTRACK_COUNTER: 14462306a36Sopenharmony_ci return container_of(res, struct rdma_counter, res)->device; 14562306a36Sopenharmony_ci case RDMA_RESTRACK_SRQ: 14662306a36Sopenharmony_ci return container_of(res, struct ib_srq, res)->device; 14762306a36Sopenharmony_ci default: 14862306a36Sopenharmony_ci WARN_ONCE(true, "Wrong resource tracking type %u\n", res->type); 14962306a36Sopenharmony_ci return NULL; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/** 15462306a36Sopenharmony_ci * rdma_restrack_attach_task() - attach the task onto this resource, 15562306a36Sopenharmony_ci * valid for user space restrack entries. 15662306a36Sopenharmony_ci * @res: resource entry 15762306a36Sopenharmony_ci * @task: the task to attach 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_cistatic void rdma_restrack_attach_task(struct rdma_restrack_entry *res, 16062306a36Sopenharmony_ci struct task_struct *task) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci if (WARN_ON_ONCE(!task)) 16362306a36Sopenharmony_ci return; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (res->task) 16662306a36Sopenharmony_ci put_task_struct(res->task); 16762306a36Sopenharmony_ci get_task_struct(task); 16862306a36Sopenharmony_ci res->task = task; 16962306a36Sopenharmony_ci res->user = true; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/** 17362306a36Sopenharmony_ci * rdma_restrack_set_name() - set the task for this resource 17462306a36Sopenharmony_ci * @res: resource entry 17562306a36Sopenharmony_ci * @caller: kernel name, the current task will be used if the caller is NULL. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_civoid rdma_restrack_set_name(struct rdma_restrack_entry *res, const char *caller) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci if (caller) { 18062306a36Sopenharmony_ci res->kern_name = caller; 18162306a36Sopenharmony_ci return; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci rdma_restrack_attach_task(res, current); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_restrack_set_name); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/** 18962306a36Sopenharmony_ci * rdma_restrack_parent_name() - set the restrack name properties based 19062306a36Sopenharmony_ci * on parent restrack 19162306a36Sopenharmony_ci * @dst: destination resource entry 19262306a36Sopenharmony_ci * @parent: parent resource entry 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_civoid rdma_restrack_parent_name(struct rdma_restrack_entry *dst, 19562306a36Sopenharmony_ci const struct rdma_restrack_entry *parent) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci if (rdma_is_kernel_res(parent)) 19862306a36Sopenharmony_ci dst->kern_name = parent->kern_name; 19962306a36Sopenharmony_ci else 20062306a36Sopenharmony_ci rdma_restrack_attach_task(dst, parent->task); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_restrack_parent_name); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/** 20562306a36Sopenharmony_ci * rdma_restrack_new() - Initializes new restrack entry to allow _put() interface 20662306a36Sopenharmony_ci * to release memory in fully automatic way. 20762306a36Sopenharmony_ci * @res: Entry to initialize 20862306a36Sopenharmony_ci * @type: REstrack type 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_civoid rdma_restrack_new(struct rdma_restrack_entry *res, 21162306a36Sopenharmony_ci enum rdma_restrack_type type) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci kref_init(&res->kref); 21462306a36Sopenharmony_ci init_completion(&res->comp); 21562306a36Sopenharmony_ci res->type = type; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_restrack_new); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/** 22062306a36Sopenharmony_ci * rdma_restrack_add() - add object to the reource tracking database 22162306a36Sopenharmony_ci * @res: resource entry 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_civoid rdma_restrack_add(struct rdma_restrack_entry *res) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct ib_device *dev = res_to_dev(res); 22662306a36Sopenharmony_ci struct rdma_restrack_root *rt; 22762306a36Sopenharmony_ci int ret = 0; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (!dev) 23062306a36Sopenharmony_ci return; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (res->no_track) 23362306a36Sopenharmony_ci goto out; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci rt = &dev->res[res->type]; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (res->type == RDMA_RESTRACK_QP) { 23862306a36Sopenharmony_ci /* Special case to ensure that LQPN points to right QP */ 23962306a36Sopenharmony_ci struct ib_qp *qp = container_of(res, struct ib_qp, res); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci WARN_ONCE(qp->qp_num >> 24 || qp->port >> 8, 24262306a36Sopenharmony_ci "QP number 0x%0X and port 0x%0X", qp->qp_num, 24362306a36Sopenharmony_ci qp->port); 24462306a36Sopenharmony_ci res->id = qp->qp_num; 24562306a36Sopenharmony_ci if (qp->qp_type == IB_QPT_SMI || qp->qp_type == IB_QPT_GSI) 24662306a36Sopenharmony_ci res->id |= qp->port << 24; 24762306a36Sopenharmony_ci ret = xa_insert(&rt->xa, res->id, res, GFP_KERNEL); 24862306a36Sopenharmony_ci if (ret) 24962306a36Sopenharmony_ci res->id = 0; 25062306a36Sopenharmony_ci } else if (res->type == RDMA_RESTRACK_COUNTER) { 25162306a36Sopenharmony_ci /* Special case to ensure that cntn points to right counter */ 25262306a36Sopenharmony_ci struct rdma_counter *counter; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci counter = container_of(res, struct rdma_counter, res); 25562306a36Sopenharmony_ci ret = xa_insert(&rt->xa, counter->id, res, GFP_KERNEL); 25662306a36Sopenharmony_ci res->id = ret ? 0 : counter->id; 25762306a36Sopenharmony_ci } else { 25862306a36Sopenharmony_ci ret = xa_alloc_cyclic(&rt->xa, &res->id, res, xa_limit_32b, 25962306a36Sopenharmony_ci &rt->next_id, GFP_KERNEL); 26062306a36Sopenharmony_ci ret = (ret < 0) ? ret : 0; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ciout: 26462306a36Sopenharmony_ci if (!ret) 26562306a36Sopenharmony_ci res->valid = true; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_restrack_add); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ciint __must_check rdma_restrack_get(struct rdma_restrack_entry *res) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci return kref_get_unless_zero(&res->kref); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_restrack_get); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci/** 27662306a36Sopenharmony_ci * rdma_restrack_get_byid() - translate from ID to restrack object 27762306a36Sopenharmony_ci * @dev: IB device 27862306a36Sopenharmony_ci * @type: resource track type 27962306a36Sopenharmony_ci * @id: ID to take a look 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * Return: Pointer to restrack entry or -ENOENT in case of error. 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_cistruct rdma_restrack_entry * 28462306a36Sopenharmony_cirdma_restrack_get_byid(struct ib_device *dev, 28562306a36Sopenharmony_ci enum rdma_restrack_type type, u32 id) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct rdma_restrack_root *rt = &dev->res[type]; 28862306a36Sopenharmony_ci struct rdma_restrack_entry *res; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci xa_lock(&rt->xa); 29162306a36Sopenharmony_ci res = xa_load(&rt->xa, id); 29262306a36Sopenharmony_ci if (!res || !rdma_restrack_get(res)) 29362306a36Sopenharmony_ci res = ERR_PTR(-ENOENT); 29462306a36Sopenharmony_ci xa_unlock(&rt->xa); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return res; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_restrack_get_byid); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic void restrack_release(struct kref *kref) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct rdma_restrack_entry *res; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci res = container_of(kref, struct rdma_restrack_entry, kref); 30562306a36Sopenharmony_ci if (res->task) { 30662306a36Sopenharmony_ci put_task_struct(res->task); 30762306a36Sopenharmony_ci res->task = NULL; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci complete(&res->comp); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ciint rdma_restrack_put(struct rdma_restrack_entry *res) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci return kref_put(&res->kref, restrack_release); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_restrack_put); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/** 31962306a36Sopenharmony_ci * rdma_restrack_del() - delete object from the reource tracking database 32062306a36Sopenharmony_ci * @res: resource entry 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_civoid rdma_restrack_del(struct rdma_restrack_entry *res) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct rdma_restrack_entry *old; 32562306a36Sopenharmony_ci struct rdma_restrack_root *rt; 32662306a36Sopenharmony_ci struct ib_device *dev; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (!res->valid) { 32962306a36Sopenharmony_ci if (res->task) { 33062306a36Sopenharmony_ci put_task_struct(res->task); 33162306a36Sopenharmony_ci res->task = NULL; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci return; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (res->no_track) 33762306a36Sopenharmony_ci goto out; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci dev = res_to_dev(res); 34062306a36Sopenharmony_ci if (WARN_ON(!dev)) 34162306a36Sopenharmony_ci return; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci rt = &dev->res[res->type]; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci old = xa_erase(&rt->xa, res->id); 34662306a36Sopenharmony_ci WARN_ON(old != res); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ciout: 34962306a36Sopenharmony_ci res->valid = false; 35062306a36Sopenharmony_ci rdma_restrack_put(res); 35162306a36Sopenharmony_ci wait_for_completion(&res->comp); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ciEXPORT_SYMBOL(rdma_restrack_del); 354