162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
462306a36Sopenharmony_ci * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "rxe.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define RXE_POOL_TIMEOUT	(200)
1062306a36Sopenharmony_ci#define RXE_POOL_ALIGN		(16)
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic const struct rxe_type_info {
1362306a36Sopenharmony_ci	const char *name;
1462306a36Sopenharmony_ci	size_t size;
1562306a36Sopenharmony_ci	size_t elem_offset;
1662306a36Sopenharmony_ci	void (*cleanup)(struct rxe_pool_elem *elem);
1762306a36Sopenharmony_ci	u32 min_index;
1862306a36Sopenharmony_ci	u32 max_index;
1962306a36Sopenharmony_ci	u32 max_elem;
2062306a36Sopenharmony_ci} rxe_type_info[RXE_NUM_TYPES] = {
2162306a36Sopenharmony_ci	[RXE_TYPE_UC] = {
2262306a36Sopenharmony_ci		.name		= "uc",
2362306a36Sopenharmony_ci		.size		= sizeof(struct rxe_ucontext),
2462306a36Sopenharmony_ci		.elem_offset	= offsetof(struct rxe_ucontext, elem),
2562306a36Sopenharmony_ci		.min_index	= 1,
2662306a36Sopenharmony_ci		.max_index	= RXE_MAX_UCONTEXT,
2762306a36Sopenharmony_ci		.max_elem	= RXE_MAX_UCONTEXT,
2862306a36Sopenharmony_ci	},
2962306a36Sopenharmony_ci	[RXE_TYPE_PD] = {
3062306a36Sopenharmony_ci		.name		= "pd",
3162306a36Sopenharmony_ci		.size		= sizeof(struct rxe_pd),
3262306a36Sopenharmony_ci		.elem_offset	= offsetof(struct rxe_pd, elem),
3362306a36Sopenharmony_ci		.min_index	= 1,
3462306a36Sopenharmony_ci		.max_index	= RXE_MAX_PD,
3562306a36Sopenharmony_ci		.max_elem	= RXE_MAX_PD,
3662306a36Sopenharmony_ci	},
3762306a36Sopenharmony_ci	[RXE_TYPE_AH] = {
3862306a36Sopenharmony_ci		.name		= "ah",
3962306a36Sopenharmony_ci		.size		= sizeof(struct rxe_ah),
4062306a36Sopenharmony_ci		.elem_offset	= offsetof(struct rxe_ah, elem),
4162306a36Sopenharmony_ci		.min_index	= RXE_MIN_AH_INDEX,
4262306a36Sopenharmony_ci		.max_index	= RXE_MAX_AH_INDEX,
4362306a36Sopenharmony_ci		.max_elem	= RXE_MAX_AH,
4462306a36Sopenharmony_ci	},
4562306a36Sopenharmony_ci	[RXE_TYPE_SRQ] = {
4662306a36Sopenharmony_ci		.name		= "srq",
4762306a36Sopenharmony_ci		.size		= sizeof(struct rxe_srq),
4862306a36Sopenharmony_ci		.elem_offset	= offsetof(struct rxe_srq, elem),
4962306a36Sopenharmony_ci		.cleanup	= rxe_srq_cleanup,
5062306a36Sopenharmony_ci		.min_index	= RXE_MIN_SRQ_INDEX,
5162306a36Sopenharmony_ci		.max_index	= RXE_MAX_SRQ_INDEX,
5262306a36Sopenharmony_ci		.max_elem	= RXE_MAX_SRQ,
5362306a36Sopenharmony_ci	},
5462306a36Sopenharmony_ci	[RXE_TYPE_QP] = {
5562306a36Sopenharmony_ci		.name		= "qp",
5662306a36Sopenharmony_ci		.size		= sizeof(struct rxe_qp),
5762306a36Sopenharmony_ci		.elem_offset	= offsetof(struct rxe_qp, elem),
5862306a36Sopenharmony_ci		.cleanup	= rxe_qp_cleanup,
5962306a36Sopenharmony_ci		.min_index	= RXE_MIN_QP_INDEX,
6062306a36Sopenharmony_ci		.max_index	= RXE_MAX_QP_INDEX,
6162306a36Sopenharmony_ci		.max_elem	= RXE_MAX_QP,
6262306a36Sopenharmony_ci	},
6362306a36Sopenharmony_ci	[RXE_TYPE_CQ] = {
6462306a36Sopenharmony_ci		.name		= "cq",
6562306a36Sopenharmony_ci		.size		= sizeof(struct rxe_cq),
6662306a36Sopenharmony_ci		.elem_offset	= offsetof(struct rxe_cq, elem),
6762306a36Sopenharmony_ci		.cleanup	= rxe_cq_cleanup,
6862306a36Sopenharmony_ci		.min_index	= 1,
6962306a36Sopenharmony_ci		.max_index	= RXE_MAX_CQ,
7062306a36Sopenharmony_ci		.max_elem	= RXE_MAX_CQ,
7162306a36Sopenharmony_ci	},
7262306a36Sopenharmony_ci	[RXE_TYPE_MR] = {
7362306a36Sopenharmony_ci		.name		= "mr",
7462306a36Sopenharmony_ci		.size		= sizeof(struct rxe_mr),
7562306a36Sopenharmony_ci		.elem_offset	= offsetof(struct rxe_mr, elem),
7662306a36Sopenharmony_ci		.cleanup	= rxe_mr_cleanup,
7762306a36Sopenharmony_ci		.min_index	= RXE_MIN_MR_INDEX,
7862306a36Sopenharmony_ci		.max_index	= RXE_MAX_MR_INDEX,
7962306a36Sopenharmony_ci		.max_elem	= RXE_MAX_MR,
8062306a36Sopenharmony_ci	},
8162306a36Sopenharmony_ci	[RXE_TYPE_MW] = {
8262306a36Sopenharmony_ci		.name		= "mw",
8362306a36Sopenharmony_ci		.size		= sizeof(struct rxe_mw),
8462306a36Sopenharmony_ci		.elem_offset	= offsetof(struct rxe_mw, elem),
8562306a36Sopenharmony_ci		.cleanup	= rxe_mw_cleanup,
8662306a36Sopenharmony_ci		.min_index	= RXE_MIN_MW_INDEX,
8762306a36Sopenharmony_ci		.max_index	= RXE_MAX_MW_INDEX,
8862306a36Sopenharmony_ci		.max_elem	= RXE_MAX_MW,
8962306a36Sopenharmony_ci	},
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_civoid rxe_pool_init(struct rxe_dev *rxe, struct rxe_pool *pool,
9362306a36Sopenharmony_ci		   enum rxe_elem_type type)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	const struct rxe_type_info *info = &rxe_type_info[type];
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	memset(pool, 0, sizeof(*pool));
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	pool->rxe		= rxe;
10062306a36Sopenharmony_ci	pool->name		= info->name;
10162306a36Sopenharmony_ci	pool->type		= type;
10262306a36Sopenharmony_ci	pool->max_elem		= info->max_elem;
10362306a36Sopenharmony_ci	pool->elem_size		= ALIGN(info->size, RXE_POOL_ALIGN);
10462306a36Sopenharmony_ci	pool->elem_offset	= info->elem_offset;
10562306a36Sopenharmony_ci	pool->cleanup		= info->cleanup;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	atomic_set(&pool->num_elem, 0);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	xa_init_flags(&pool->xa, XA_FLAGS_ALLOC);
11062306a36Sopenharmony_ci	pool->limit.min = info->min_index;
11162306a36Sopenharmony_ci	pool->limit.max = info->max_index;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_civoid rxe_pool_cleanup(struct rxe_pool *pool)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	WARN_ON(!xa_empty(&pool->xa));
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ciint __rxe_add_to_pool(struct rxe_pool *pool, struct rxe_pool_elem *elem,
12062306a36Sopenharmony_ci				bool sleepable)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	int err;
12362306a36Sopenharmony_ci	gfp_t gfp_flags;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (atomic_inc_return(&pool->num_elem) > pool->max_elem)
12662306a36Sopenharmony_ci		goto err_cnt;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	elem->pool = pool;
12962306a36Sopenharmony_ci	elem->obj = (u8 *)elem - pool->elem_offset;
13062306a36Sopenharmony_ci	kref_init(&elem->ref_cnt);
13162306a36Sopenharmony_ci	init_completion(&elem->complete);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	/* AH objects are unique in that the create_ah verb
13462306a36Sopenharmony_ci	 * can be called in atomic context. If the create_ah
13562306a36Sopenharmony_ci	 * call is not sleepable use GFP_ATOMIC.
13662306a36Sopenharmony_ci	 */
13762306a36Sopenharmony_ci	gfp_flags = sleepable ? GFP_KERNEL : GFP_ATOMIC;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (sleepable)
14062306a36Sopenharmony_ci		might_sleep();
14162306a36Sopenharmony_ci	err = xa_alloc_cyclic(&pool->xa, &elem->index, NULL, pool->limit,
14262306a36Sopenharmony_ci			      &pool->next, gfp_flags);
14362306a36Sopenharmony_ci	if (err < 0)
14462306a36Sopenharmony_ci		goto err_cnt;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return 0;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cierr_cnt:
14962306a36Sopenharmony_ci	atomic_dec(&pool->num_elem);
15062306a36Sopenharmony_ci	return -EINVAL;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_civoid *rxe_pool_get_index(struct rxe_pool *pool, u32 index)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct rxe_pool_elem *elem;
15662306a36Sopenharmony_ci	struct xarray *xa = &pool->xa;
15762306a36Sopenharmony_ci	void *obj;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	rcu_read_lock();
16062306a36Sopenharmony_ci	elem = xa_load(xa, index);
16162306a36Sopenharmony_ci	if (elem && kref_get_unless_zero(&elem->ref_cnt))
16262306a36Sopenharmony_ci		obj = elem->obj;
16362306a36Sopenharmony_ci	else
16462306a36Sopenharmony_ci		obj = NULL;
16562306a36Sopenharmony_ci	rcu_read_unlock();
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	return obj;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void rxe_elem_release(struct kref *kref)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct rxe_pool_elem *elem = container_of(kref, typeof(*elem), ref_cnt);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	complete(&elem->complete);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ciint __rxe_cleanup(struct rxe_pool_elem *elem, bool sleepable)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	struct rxe_pool *pool = elem->pool;
18062306a36Sopenharmony_ci	struct xarray *xa = &pool->xa;
18162306a36Sopenharmony_ci	static int timeout = RXE_POOL_TIMEOUT;
18262306a36Sopenharmony_ci	int ret, err = 0;
18362306a36Sopenharmony_ci	void *xa_ret;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (sleepable)
18662306a36Sopenharmony_ci		might_sleep();
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* erase xarray entry to prevent looking up
18962306a36Sopenharmony_ci	 * the pool elem from its index
19062306a36Sopenharmony_ci	 */
19162306a36Sopenharmony_ci	xa_ret = xa_erase(xa, elem->index);
19262306a36Sopenharmony_ci	WARN_ON(xa_err(xa_ret));
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/* if this is the last call to rxe_put complete the
19562306a36Sopenharmony_ci	 * object. It is safe to touch obj->elem after this since
19662306a36Sopenharmony_ci	 * it is freed below
19762306a36Sopenharmony_ci	 */
19862306a36Sopenharmony_ci	__rxe_put(elem);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* wait until all references to the object have been
20162306a36Sopenharmony_ci	 * dropped before final object specific cleanup and
20262306a36Sopenharmony_ci	 * return to rdma-core
20362306a36Sopenharmony_ci	 */
20462306a36Sopenharmony_ci	if (sleepable) {
20562306a36Sopenharmony_ci		if (!completion_done(&elem->complete) && timeout) {
20662306a36Sopenharmony_ci			ret = wait_for_completion_timeout(&elem->complete,
20762306a36Sopenharmony_ci					timeout);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci			/* Shouldn't happen. There are still references to
21062306a36Sopenharmony_ci			 * the object but, rather than deadlock, free the
21162306a36Sopenharmony_ci			 * object or pass back to rdma-core.
21262306a36Sopenharmony_ci			 */
21362306a36Sopenharmony_ci			if (WARN_ON(!ret))
21462306a36Sopenharmony_ci				err = -EINVAL;
21562306a36Sopenharmony_ci		}
21662306a36Sopenharmony_ci	} else {
21762306a36Sopenharmony_ci		unsigned long until = jiffies + timeout;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		/* AH objects are unique in that the destroy_ah verb
22062306a36Sopenharmony_ci		 * can be called in atomic context. This delay
22162306a36Sopenharmony_ci		 * replaces the wait_for_completion call above
22262306a36Sopenharmony_ci		 * when the destroy_ah call is not sleepable
22362306a36Sopenharmony_ci		 */
22462306a36Sopenharmony_ci		while (!completion_done(&elem->complete) &&
22562306a36Sopenharmony_ci				time_before(jiffies, until))
22662306a36Sopenharmony_ci			mdelay(1);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		if (WARN_ON(!completion_done(&elem->complete)))
22962306a36Sopenharmony_ci			err = -EINVAL;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (pool->cleanup)
23362306a36Sopenharmony_ci		pool->cleanup(elem);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	atomic_dec(&pool->num_elem);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return err;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ciint __rxe_get(struct rxe_pool_elem *elem)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	return kref_get_unless_zero(&elem->ref_cnt);
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ciint __rxe_put(struct rxe_pool_elem *elem)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	return kref_put(&elem->ref_cnt, rxe_elem_release);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_civoid __rxe_finalize(struct rxe_pool_elem *elem)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	void *xa_ret;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	xa_ret = xa_store(&elem->pool->xa, elem->index, elem, GFP_KERNEL);
25562306a36Sopenharmony_ci	WARN_ON(xa_err(xa_ret));
25662306a36Sopenharmony_ci}
257