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