summaryrefslogtreecommitdiff
path: root/drivers/infiniband/core/rdma_core.c
diff options
context:
space:
mode:
authorYishai Hadas <yishaih@mellanox.com>2018-06-20 17:11:39 +0300
committerJason Gunthorpe <jgg@mellanox.com>2018-06-29 23:35:46 +0300
commit1c77483e4c50339b0306572167ccbff6b55d051b (patch)
tree3d7396f283f4d903364c949a9d133dfda1c6aebf /drivers/infiniband/core/rdma_core.c
parente620ebfc228dcbef7519e3d16f43c6c6f1a1d0cb (diff)
downloadlinux-1c77483e4c50339b0306572167ccbff6b55d051b.tar.xz
IB: Improve uverbs_cleanup_ucontext algorithm
Improve uverbs_cleanup_ucontext algorithm to work properly when the topology graph of the objects cannot be determined at compile time. This is the case with objects created via the devx interface in mlx5. Typically uverbs objects must be created in a strict topologically sorted order, so that LIFO ordering will generally cause them to be freed properly. There are only a few cases (eg memory windows) where objects can point to things out of the strict LIFO order. Instead of using an explicit ordering scheme where the HW destroy is not allowed to fail, go over the list multiple times and allow the destroy function to fail. If progress halts then a final, desperate, cleanup is done before leaking the memory. This indicates a driver bug. Signed-off-by: Yishai Hadas <yishaih@mellanox.com> Signed-off-by: Leon Romanovsky <leonro@mellanox.com> Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
Diffstat (limited to 'drivers/infiniband/core/rdma_core.c')
-rw-r--r--drivers/infiniband/core/rdma_core.c113
1 files changed, 65 insertions, 48 deletions
diff --git a/drivers/infiniband/core/rdma_core.c b/drivers/infiniband/core/rdma_core.c
index df3c40533252..2ddf1c716ba8 100644
--- a/drivers/infiniband/core/rdma_core.c
+++ b/drivers/infiniband/core/rdma_core.c
@@ -360,9 +360,10 @@ static int __must_check remove_commit_idr_uobject(struct ib_uobject *uobj,
/*
* We can only fail gracefully if the user requested to destroy the
- * object. In the rest of the cases, just remove whatever you can.
+ * object or when a retry may be called upon an error.
+ * In the rest of the cases, just remove whatever you can.
*/
- if (why == RDMA_REMOVE_DESTROY && ret)
+ if (ib_is_destroy_retryable(ret, why, uobj))
return ret;
ib_rdmacg_uncharge(&uobj->cg_obj, uobj->context->device,
@@ -393,7 +394,7 @@ static int __must_check remove_commit_fd_uobject(struct ib_uobject *uobj,
container_of(uobj, struct ib_uobject_file, uobj);
int ret = fd_type->context_closed(uobj_file, why);
- if (why == RDMA_REMOVE_DESTROY && ret)
+ if (ib_is_destroy_retryable(ret, why, uobj))
return ret;
if (why == RDMA_REMOVE_DURING_CLEANUP) {
@@ -422,7 +423,7 @@ static int __must_check _rdma_remove_commit_uobject(struct ib_uobject *uobj,
struct ib_ucontext *ucontext = uobj->context;
ret = uobj->type->type_class->remove_commit(uobj, why);
- if (ret && why == RDMA_REMOVE_DESTROY) {
+ if (ib_is_destroy_retryable(ret, why, uobj)) {
/* We couldn't remove the object, so just unlock the uobject */
atomic_set(&uobj->usecnt, 0);
uobj->type->type_class->lookup_put(uobj, true);
@@ -645,61 +646,77 @@ void uverbs_close_fd(struct file *f)
kref_put(uverbs_file_ref, ib_uverbs_release_file);
}
-void uverbs_cleanup_ucontext(struct ib_ucontext *ucontext, bool device_removed)
+static int __uverbs_cleanup_ucontext(struct ib_ucontext *ucontext,
+ enum rdma_remove_reason reason)
{
- enum rdma_remove_reason reason = device_removed ?
- RDMA_REMOVE_DRIVER_REMOVE : RDMA_REMOVE_CLOSE;
- unsigned int cur_order = 0;
+ struct ib_uobject *obj, *next_obj;
+ int ret = -EINVAL;
+ int err = 0;
+ /*
+ * This shouldn't run while executing other commands on this
+ * context. Thus, the only thing we should take care of is
+ * releasing a FD while traversing this list. The FD could be
+ * closed and released from the _release fop of this FD.
+ * In order to mitigate this, we add a lock.
+ * We take and release the lock per traversal in order to let
+ * other threads (which might still use the FDs) chance to run.
+ */
+ mutex_lock(&ucontext->uobjects_lock);
ucontext->cleanup_reason = reason;
+ list_for_each_entry_safe(obj, next_obj, &ucontext->uobjects, list) {
+ /*
+ * if we hit this WARN_ON, that means we are
+ * racing with a lookup_get.
+ */
+ WARN_ON(uverbs_try_lock_object(obj, true));
+ err = obj->type->type_class->remove_commit(obj, reason);
+
+ if (ib_is_destroy_retryable(err, reason, obj)) {
+ pr_debug("ib_uverbs: failed to remove uobject id %d err %d\n",
+ obj->id, err);
+ atomic_set(&obj->usecnt, 0);
+ continue;
+ }
+
+ if (err)
+ pr_err("ib_uverbs: unable to remove uobject id %d err %d\n",
+ obj->id, err);
+
+ list_del(&obj->list);
+ /* put the ref we took when we created the object */
+ uverbs_uobject_put(obj);
+ ret = 0;
+ }
+ mutex_unlock(&ucontext->uobjects_lock);
+ return ret;
+}
+
+void uverbs_cleanup_ucontext(struct ib_ucontext *ucontext, bool device_removed)
+{
+ enum rdma_remove_reason reason = device_removed ?
+ RDMA_REMOVE_DRIVER_REMOVE :
+ RDMA_REMOVE_CLOSE;
/*
* Waits for all remove_commit and alloc_commit to finish. Logically, We
* want to hold this forever as the context is going to be destroyed,
* but we'll release it since it causes a "held lock freed" BUG message.
*/
down_write(&ucontext->cleanup_rwsem);
+ ucontext->cleanup_retryable = true;
+ while (!list_empty(&ucontext->uobjects))
+ if (__uverbs_cleanup_ucontext(ucontext, reason)) {
+ /*
+ * No entry was cleaned-up successfully during this
+ * iteration
+ */
+ break;
+ }
- while (!list_empty(&ucontext->uobjects)) {
- struct ib_uobject *obj, *next_obj;
- unsigned int next_order = UINT_MAX;
+ ucontext->cleanup_retryable = false;
+ if (!list_empty(&ucontext->uobjects))
+ __uverbs_cleanup_ucontext(ucontext, reason);
- /*
- * This shouldn't run while executing other commands on this
- * context. Thus, the only thing we should take care of is
- * releasing a FD while traversing this list. The FD could be
- * closed and released from the _release fop of this FD.
- * In order to mitigate this, we add a lock.
- * We take and release the lock per order traversal in order
- * to let other threads (which might still use the FDs) chance
- * to run.
- */
- mutex_lock(&ucontext->uobjects_lock);
- list_for_each_entry_safe(obj, next_obj, &ucontext->uobjects,
- list) {
- if (obj->type->destroy_order == cur_order) {
- int ret;
-
- /*
- * if we hit this WARN_ON, that means we are
- * racing with a lookup_get.
- */
- WARN_ON(uverbs_try_lock_object(obj, true));
- ret = obj->type->type_class->remove_commit(obj,
- reason);
- list_del(&obj->list);
- if (ret)
- pr_warn("ib_uverbs: failed to remove uobject id %d order %u\n",
- obj->id, cur_order);
- /* put the ref we took when we created the object */
- uverbs_uobject_put(obj);
- } else {
- next_order = min(next_order,
- obj->type->destroy_order);
- }
- }
- mutex_unlock(&ucontext->uobjects_lock);
- cur_order = next_order;
- }
up_write(&ucontext->cleanup_rwsem);
}