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