123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
- /*
- * Copyright (c) 2016 Mellanox Technologies Ltd. All rights reserved.
- * Copyright (c) 2015 System Fabric Works, Inc. All rights reserved.
- */
- #include "rxe.h"
- #define RXE_POOL_TIMEOUT (200)
- #define RXE_POOL_ALIGN (16)
- static const struct rxe_type_info {
- const char *name;
- size_t size;
- size_t elem_offset;
- void (*cleanup)(struct rxe_pool_elem *elem);
- u32 min_index;
- u32 max_index;
- u32 max_elem;
- } rxe_type_info[RXE_NUM_TYPES] = {
- [RXE_TYPE_UC] = {
- .name = "uc",
- .size = sizeof(struct rxe_ucontext),
- .elem_offset = offsetof(struct rxe_ucontext, elem),
- .min_index = 1,
- .max_index = RXE_MAX_UCONTEXT,
- .max_elem = RXE_MAX_UCONTEXT,
- },
- [RXE_TYPE_PD] = {
- .name = "pd",
- .size = sizeof(struct rxe_pd),
- .elem_offset = offsetof(struct rxe_pd, elem),
- .min_index = 1,
- .max_index = RXE_MAX_PD,
- .max_elem = RXE_MAX_PD,
- },
- [RXE_TYPE_AH] = {
- .name = "ah",
- .size = sizeof(struct rxe_ah),
- .elem_offset = offsetof(struct rxe_ah, elem),
- .min_index = RXE_MIN_AH_INDEX,
- .max_index = RXE_MAX_AH_INDEX,
- .max_elem = RXE_MAX_AH,
- },
- [RXE_TYPE_SRQ] = {
- .name = "srq",
- .size = sizeof(struct rxe_srq),
- .elem_offset = offsetof(struct rxe_srq, elem),
- .cleanup = rxe_srq_cleanup,
- .min_index = RXE_MIN_SRQ_INDEX,
- .max_index = RXE_MAX_SRQ_INDEX,
- .max_elem = RXE_MAX_SRQ,
- },
- [RXE_TYPE_QP] = {
- .name = "qp",
- .size = sizeof(struct rxe_qp),
- .elem_offset = offsetof(struct rxe_qp, elem),
- .cleanup = rxe_qp_cleanup,
- .min_index = RXE_MIN_QP_INDEX,
- .max_index = RXE_MAX_QP_INDEX,
- .max_elem = RXE_MAX_QP,
- },
- [RXE_TYPE_CQ] = {
- .name = "cq",
- .size = sizeof(struct rxe_cq),
- .elem_offset = offsetof(struct rxe_cq, elem),
- .cleanup = rxe_cq_cleanup,
- .min_index = 1,
- .max_index = RXE_MAX_CQ,
- .max_elem = RXE_MAX_CQ,
- },
- [RXE_TYPE_MR] = {
- .name = "mr",
- .size = sizeof(struct rxe_mr),
- .elem_offset = offsetof(struct rxe_mr, elem),
- .cleanup = rxe_mr_cleanup,
- .min_index = RXE_MIN_MR_INDEX,
- .max_index = RXE_MAX_MR_INDEX,
- .max_elem = RXE_MAX_MR,
- },
- [RXE_TYPE_MW] = {
- .name = "mw",
- .size = sizeof(struct rxe_mw),
- .elem_offset = offsetof(struct rxe_mw, elem),
- .cleanup = rxe_mw_cleanup,
- .min_index = RXE_MIN_MW_INDEX,
- .max_index = RXE_MAX_MW_INDEX,
- .max_elem = RXE_MAX_MW,
- },
- };
- void rxe_pool_init(struct rxe_dev *rxe, struct rxe_pool *pool,
- enum rxe_elem_type type)
- {
- const struct rxe_type_info *info = &rxe_type_info[type];
- memset(pool, 0, sizeof(*pool));
- pool->rxe = rxe;
- pool->name = info->name;
- pool->type = type;
- pool->max_elem = info->max_elem;
- pool->elem_size = ALIGN(info->size, RXE_POOL_ALIGN);
- pool->elem_offset = info->elem_offset;
- pool->cleanup = info->cleanup;
- atomic_set(&pool->num_elem, 0);
- xa_init_flags(&pool->xa, XA_FLAGS_ALLOC);
- pool->limit.min = info->min_index;
- pool->limit.max = info->max_index;
- }
- void rxe_pool_cleanup(struct rxe_pool *pool)
- {
- WARN_ON(!xa_empty(&pool->xa));
- }
- void *rxe_alloc(struct rxe_pool *pool)
- {
- struct rxe_pool_elem *elem;
- void *obj;
- int err;
- if (WARN_ON(!(pool->type == RXE_TYPE_MR)))
- return NULL;
- if (atomic_inc_return(&pool->num_elem) > pool->max_elem)
- goto err_cnt;
- obj = kzalloc(pool->elem_size, GFP_KERNEL);
- if (!obj)
- goto err_cnt;
- elem = (struct rxe_pool_elem *)((u8 *)obj + pool->elem_offset);
- elem->pool = pool;
- elem->obj = obj;
- kref_init(&elem->ref_cnt);
- init_completion(&elem->complete);
- /* allocate index in array but leave pointer as NULL so it
- * can't be looked up until rxe_finalize() is called
- */
- err = xa_alloc_cyclic(&pool->xa, &elem->index, NULL, pool->limit,
- &pool->next, GFP_KERNEL);
- if (err < 0)
- goto err_free;
- return obj;
- err_free:
- kfree(obj);
- err_cnt:
- atomic_dec(&pool->num_elem);
- return NULL;
- }
- int __rxe_add_to_pool(struct rxe_pool *pool, struct rxe_pool_elem *elem,
- bool sleepable)
- {
- int err;
- gfp_t gfp_flags;
- if (WARN_ON(pool->type == RXE_TYPE_MR))
- return -EINVAL;
- if (atomic_inc_return(&pool->num_elem) > pool->max_elem)
- goto err_cnt;
- elem->pool = pool;
- elem->obj = (u8 *)elem - pool->elem_offset;
- kref_init(&elem->ref_cnt);
- init_completion(&elem->complete);
- /* AH objects are unique in that the create_ah verb
- * can be called in atomic context. If the create_ah
- * call is not sleepable use GFP_ATOMIC.
- */
- gfp_flags = sleepable ? GFP_KERNEL : GFP_ATOMIC;
- if (sleepable)
- might_sleep();
- err = xa_alloc_cyclic(&pool->xa, &elem->index, NULL, pool->limit,
- &pool->next, gfp_flags);
- if (err < 0)
- goto err_cnt;
- return 0;
- err_cnt:
- atomic_dec(&pool->num_elem);
- return -EINVAL;
- }
- void *rxe_pool_get_index(struct rxe_pool *pool, u32 index)
- {
- struct rxe_pool_elem *elem;
- struct xarray *xa = &pool->xa;
- void *obj;
- rcu_read_lock();
- elem = xa_load(xa, index);
- if (elem && kref_get_unless_zero(&elem->ref_cnt))
- obj = elem->obj;
- else
- obj = NULL;
- rcu_read_unlock();
- return obj;
- }
- static void rxe_elem_release(struct kref *kref)
- {
- struct rxe_pool_elem *elem = container_of(kref, typeof(*elem), ref_cnt);
- complete(&elem->complete);
- }
- int __rxe_cleanup(struct rxe_pool_elem *elem, bool sleepable)
- {
- struct rxe_pool *pool = elem->pool;
- struct xarray *xa = &pool->xa;
- static int timeout = RXE_POOL_TIMEOUT;
- int ret, err = 0;
- void *xa_ret;
- if (sleepable)
- might_sleep();
- /* erase xarray entry to prevent looking up
- * the pool elem from its index
- */
- xa_ret = xa_erase(xa, elem->index);
- WARN_ON(xa_err(xa_ret));
- /* if this is the last call to rxe_put complete the
- * object. It is safe to touch obj->elem after this since
- * it is freed below
- */
- __rxe_put(elem);
- /* wait until all references to the object have been
- * dropped before final object specific cleanup and
- * return to rdma-core
- */
- if (sleepable) {
- if (!completion_done(&elem->complete) && timeout) {
- ret = wait_for_completion_timeout(&elem->complete,
- timeout);
- /* Shouldn't happen. There are still references to
- * the object but, rather than deadlock, free the
- * object or pass back to rdma-core.
- */
- if (WARN_ON(!ret))
- err = -EINVAL;
- }
- } else {
- unsigned long until = jiffies + timeout;
- /* AH objects are unique in that the destroy_ah verb
- * can be called in atomic context. This delay
- * replaces the wait_for_completion call above
- * when the destroy_ah call is not sleepable
- */
- while (!completion_done(&elem->complete) &&
- time_before(jiffies, until))
- mdelay(1);
- if (WARN_ON(!completion_done(&elem->complete)))
- err = -EINVAL;
- }
- if (pool->cleanup)
- pool->cleanup(elem);
- if (pool->type == RXE_TYPE_MR)
- kfree_rcu(elem->obj);
- atomic_dec(&pool->num_elem);
- return err;
- }
- int __rxe_get(struct rxe_pool_elem *elem)
- {
- return kref_get_unless_zero(&elem->ref_cnt);
- }
- int __rxe_put(struct rxe_pool_elem *elem)
- {
- return kref_put(&elem->ref_cnt, rxe_elem_release);
- }
- void __rxe_finalize(struct rxe_pool_elem *elem)
- {
- void *xa_ret;
- xa_ret = xa_store(&elem->pool->xa, elem->index, elem, GFP_KERNEL);
- WARN_ON(xa_err(xa_ret));
- }
|