Skip to content

Commit 87ad80a

Browse files
committed
IB/uverbs: Consolidate uobject destruction
There are several flows that can destroy a uobject and each one is minimized and sprinkled throughout the code base, making it difficult to understand and very hard to modify the destroy path. Consolidate all of these into uverbs_destroy_uobject() and call it in all cases where a uobject has to be destroyed. This makes one change to the lifecycle, during any abort (eg when alloc_commit is not called) we always call out to alloc_abort, even if remove_commit needs to be called to delete a HW object. This also renames RDMA_REMOVE_DURING_CLEANUP to RDMA_REMOVE_ABORT to clarify its actual usage and revises some of the comments to reflect what the life cycle is for the type implementation. Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent 32ed5c0 commit 87ad80a

File tree

3 files changed

+157
-168
lines changed

3 files changed

+157
-168
lines changed

drivers/infiniband/core/rdma_core.c

Lines changed: 122 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,95 @@ static int uverbs_try_lock_object(struct ib_uobject *uobj, bool exclusive)
129129
return atomic_cmpxchg(&uobj->usecnt, 0, -1) == 0 ? 0 : -EBUSY;
130130
}
131131

132+
static void assert_uverbs_usecnt(struct ib_uobject *uobj, bool exclusive)
133+
{
134+
#ifdef CONFIG_LOCKDEP
135+
if (exclusive)
136+
WARN_ON(atomic_read(&uobj->usecnt) != -1);
137+
else
138+
WARN_ON(atomic_read(&uobj->usecnt) <= 0);
139+
#endif
140+
}
141+
142+
/*
143+
* This must be called with the hw_destroy_rwsem locked (except for
144+
* RDMA_REMOVE_ABORT) for read or write, also The uobject itself must be
145+
* locked for write.
146+
*
147+
* Upon return the HW object is guaranteed to be destroyed.
148+
*
149+
* For RDMA_REMOVE_ABORT, the hw_destroy_rwsem is not required to be held,
150+
* however the type's allocat_commit function cannot have been called and the
151+
* uobject cannot be on the uobjects_lists
152+
*
153+
* For RDMA_REMOVE_DESTROY the caller shold be holding a kref (eg via
154+
* rdma_lookup_get_uobject) and the object is left in a state where the caller
155+
* needs to call rdma_lookup_put_uobject.
156+
*
157+
* For all other destroy modes this function internally unlocks the uobject
158+
* and consumes the kref on the uobj.
159+
*/
160+
static int uverbs_destroy_uobject(struct ib_uobject *uobj,
161+
enum rdma_remove_reason reason)
162+
{
163+
struct ib_uverbs_file *ufile = uobj->ufile;
164+
unsigned long flags;
165+
int ret;
166+
167+
assert_uverbs_usecnt(uobj, true);
168+
169+
if (uobj->object) {
170+
ret = uobj->type->type_class->remove_commit(uobj, reason);
171+
if (ret) {
172+
if (ib_is_destroy_retryable(ret, reason, uobj))
173+
return ret;
174+
175+
/* Nothing to be done, dangle the memory and move on */
176+
WARN(true,
177+
"ib_uverbs: failed to remove uobject id %d, driver err=%d",
178+
uobj->id, ret);
179+
}
180+
181+
uobj->object = NULL;
182+
}
183+
184+
if (reason == RDMA_REMOVE_ABORT) {
185+
WARN_ON(!list_empty(&uobj->list));
186+
WARN_ON(!uobj->context);
187+
uobj->type->type_class->alloc_abort(uobj);
188+
}
189+
190+
uobj->context = NULL;
191+
192+
/*
193+
* For DESTROY the usecnt is held write locked, the caller is expected
194+
* to put it unlock and put the object when done with it.
195+
*/
196+
if (reason != RDMA_REMOVE_DESTROY)
197+
atomic_set(&uobj->usecnt, 0);
198+
199+
if (!list_empty(&uobj->list)) {
200+
spin_lock_irqsave(&ufile->uobjects_lock, flags);
201+
list_del_init(&uobj->list);
202+
spin_unlock_irqrestore(&ufile->uobjects_lock, flags);
203+
204+
/*
205+
* Pairs with the get in rdma_alloc_commit_uobject(), could
206+
* destroy uobj.
207+
*/
208+
uverbs_uobject_put(uobj);
209+
}
210+
211+
/*
212+
* When aborting the stack kref remains owned by the core code, and is
213+
* not transferred into the type. Pairs with the get in alloc_uobj
214+
*/
215+
if (reason == RDMA_REMOVE_ABORT)
216+
uverbs_uobject_put(uobj);
217+
218+
return 0;
219+
}
220+
132221
/*
133222
* uobj_get_destroy destroys the HW object and returns a handle to the uobj
134223
* with a NULL object pointer. The caller must pair this with
@@ -171,6 +260,7 @@ int __uobj_perform_destroy(const struct uverbs_obj_type *type, u32 id,
171260
return success_res;
172261
}
173262

263+
/* alloc_uobj must be undone by uverbs_destroy_uobject() */
174264
static struct ib_uobject *alloc_uobj(struct ib_uverbs_file *ufile,
175265
const struct uverbs_obj_type *type)
176266
{
@@ -379,6 +469,16 @@ struct ib_uobject *rdma_alloc_begin_uobject(const struct uverbs_obj_type *type,
379469
return type->type_class->alloc_begin(type, ufile);
380470
}
381471

472+
static void alloc_abort_idr_uobject(struct ib_uobject *uobj)
473+
{
474+
ib_rdmacg_uncharge(&uobj->cg_obj, uobj->context->device,
475+
RDMACG_RESOURCE_HCA_OBJECT);
476+
477+
spin_lock(&uobj->ufile->idr_lock);
478+
idr_remove(&uobj->ufile->idr, uobj->id);
479+
spin_unlock(&uobj->ufile->idr_lock);
480+
}
481+
382482
static int __must_check remove_commit_idr_uobject(struct ib_uobject *uobj,
383483
enum rdma_remove_reason why)
384484
{
@@ -395,25 +495,19 @@ static int __must_check remove_commit_idr_uobject(struct ib_uobject *uobj,
395495
if (ib_is_destroy_retryable(ret, why, uobj))
396496
return ret;
397497

398-
ib_rdmacg_uncharge(&uobj->cg_obj, uobj->context->device,
399-
RDMACG_RESOURCE_HCA_OBJECT);
400-
401-
spin_lock(&uobj->ufile->idr_lock);
402-
idr_remove(&uobj->ufile->idr, uobj->id);
403-
spin_unlock(&uobj->ufile->idr_lock);
498+
if (why == RDMA_REMOVE_ABORT)
499+
return 0;
404500

501+
alloc_abort_idr_uobject(uobj);
405502
/* Matches the kref in alloc_commit_idr_uobject */
406503
uverbs_uobject_put(uobj);
407504

408-
return ret;
505+
return 0;
409506
}
410507

411508
static void alloc_abort_fd_uobject(struct ib_uobject *uobj)
412509
{
413510
put_unused_fd(uobj->id);
414-
415-
/* Pairs with the kref from alloc_begin_idr_uobject */
416-
uverbs_uobject_put(uobj);
417511
}
418512

419513
static int __must_check remove_commit_fd_uobject(struct ib_uobject *uobj,
@@ -426,47 +520,7 @@ static int __must_check remove_commit_fd_uobject(struct ib_uobject *uobj,
426520
if (ib_is_destroy_retryable(ret, why, uobj))
427521
return ret;
428522

429-
if (why == RDMA_REMOVE_DURING_CLEANUP) {
430-
alloc_abort_fd_uobject(uobj);
431-
return ret;
432-
}
433-
434-
uobj->context = NULL;
435-
return ret;
436-
}
437-
438-
static void assert_uverbs_usecnt(struct ib_uobject *uobj, bool exclusive)
439-
{
440-
#ifdef CONFIG_LOCKDEP
441-
if (exclusive)
442-
WARN_ON(atomic_read(&uobj->usecnt) != -1);
443-
else
444-
WARN_ON(atomic_read(&uobj->usecnt) <= 0);
445-
#endif
446-
}
447-
448-
static int __must_check _rdma_remove_commit_uobject(struct ib_uobject *uobj,
449-
enum rdma_remove_reason why)
450-
{
451-
struct ib_uverbs_file *ufile = uobj->ufile;
452-
int ret;
453-
454-
if (!uobj->object)
455-
return 0;
456-
457-
ret = uobj->type->type_class->remove_commit(uobj, why);
458-
if (ib_is_destroy_retryable(ret, why, uobj))
459-
return ret;
460-
461-
uobj->object = NULL;
462-
463-
spin_lock_irq(&ufile->uobjects_lock);
464-
list_del(&uobj->list);
465-
spin_unlock_irq(&ufile->uobjects_lock);
466-
/* Pairs with the get in rdma_alloc_commit_uobject() */
467-
uverbs_uobject_put(uobj);
468-
469-
return ret;
523+
return 0;
470524
}
471525

472526
int rdma_explicit_destroy(struct ib_uobject *uobject)
@@ -479,8 +533,8 @@ int rdma_explicit_destroy(struct ib_uobject *uobject)
479533
WARN(true, "ib_uverbs: Cleanup is running while removing an uobject\n");
480534
return 0;
481535
}
482-
assert_uverbs_usecnt(uobject, true);
483-
ret = _rdma_remove_commit_uobject(uobject, RDMA_REMOVE_DESTROY);
536+
537+
ret = uverbs_destroy_uobject(uobject, RDMA_REMOVE_DESTROY);
484538

485539
up_read(&ufile->hw_destroy_rwsem);
486540
return ret;
@@ -554,24 +608,14 @@ int __must_check rdma_alloc_commit_uobject(struct ib_uobject *uobj)
554608
/* Cleanup is running. Calling this should have been impossible */
555609
if (!down_read_trylock(&ufile->hw_destroy_rwsem)) {
556610
WARN(true, "ib_uverbs: Cleanup is running while allocating an uobject\n");
557-
ret = uobj->type->type_class->remove_commit(uobj,
558-
RDMA_REMOVE_DURING_CLEANUP);
559-
if (ret)
560-
pr_warn("ib_uverbs: cleanup of idr object %d failed\n",
561-
uobj->id);
562-
return ret;
611+
uverbs_destroy_uobject(uobj, RDMA_REMOVE_ABORT);
612+
return -EINVAL;
563613
}
564614

565-
assert_uverbs_usecnt(uobj, true);
566-
567615
/* alloc_commit consumes the uobj kref */
568616
ret = uobj->type->type_class->alloc_commit(uobj);
569617
if (ret) {
570-
if (uobj->type->type_class->remove_commit(
571-
uobj, RDMA_REMOVE_DURING_CLEANUP))
572-
pr_warn("ib_uverbs: cleanup of idr object %d failed\n",
573-
uobj->id);
574-
up_read(&ufile->hw_destroy_rwsem);
618+
uverbs_destroy_uobject(uobj, RDMA_REMOVE_ABORT);
575619
return ret;
576620
}
577621

@@ -589,27 +633,14 @@ int __must_check rdma_alloc_commit_uobject(struct ib_uobject *uobj)
589633
return 0;
590634
}
591635

592-
static void alloc_abort_idr_uobject(struct ib_uobject *uobj)
593-
{
594-
ib_rdmacg_uncharge(&uobj->cg_obj, uobj->context->device,
595-
RDMACG_RESOURCE_HCA_OBJECT);
596-
597-
spin_lock(&uobj->ufile->idr_lock);
598-
/* The value of the handle in the IDR is NULL at this point. */
599-
idr_remove(&uobj->ufile->idr, uobj->id);
600-
spin_unlock(&uobj->ufile->idr_lock);
601-
602-
/* Pairs with the kref from alloc_begin_idr_uobject */
603-
uverbs_uobject_put(uobj);
604-
}
605-
606636
/*
607637
* This consumes the kref for uobj. It is up to the caller to unwind the HW
608638
* object and anything else connected to uobj before calling this.
609639
*/
610640
void rdma_alloc_abort_uobject(struct ib_uobject *uobj)
611641
{
612-
uobj->type->type_class->alloc_abort(uobj);
642+
uobj->object = NULL;
643+
uverbs_destroy_uobject(uobj, RDMA_REMOVE_ABORT);
613644
}
614645

615646
static void lookup_put_idr_uobject(struct ib_uobject *uobj, bool exclusive)
@@ -667,45 +698,23 @@ const struct uverbs_obj_type_class uverbs_idr_class = {
667698
};
668699
EXPORT_SYMBOL(uverbs_idr_class);
669700

670-
static void _uverbs_close_fd(struct ib_uobject *uobj)
671-
{
672-
int ret;
673-
674-
/*
675-
* uobject was already cleaned up, remove_commit_fd_uobject
676-
* sets this
677-
*/
678-
if (!uobj->context)
679-
return;
680-
681-
/*
682-
* lookup_get_fd_uobject holds the kref on the struct file any time a
683-
* FD uobj is locked, which prevents this release method from being
684-
* invoked. Meaning we can always get the write lock here, or we have
685-
* a kernel bug. If so dangle the pointers and bail.
686-
*/
687-
ret = uverbs_try_lock_object(uobj, true);
688-
if (WARN(ret, "uverbs_close_fd() racing with lookup_get_fd_uobject()"))
689-
return;
690-
691-
ret = _rdma_remove_commit_uobject(uobj, RDMA_REMOVE_CLOSE);
692-
if (ret)
693-
pr_warn("Unable to clean up uobject file in %s\n", __func__);
694-
695-
atomic_set(&uobj->usecnt, 0);
696-
}
697-
698701
void uverbs_close_fd(struct file *f)
699702
{
700703
struct ib_uobject *uobj = f->private_data;
701704
struct ib_uverbs_file *ufile = uobj->ufile;
702705

703706
if (down_read_trylock(&ufile->hw_destroy_rwsem)) {
704-
_uverbs_close_fd(uobj);
707+
/*
708+
* lookup_get_fd_uobject holds the kref on the struct file any
709+
* time a FD uobj is locked, which prevents this release
710+
* method from being invoked. Meaning we can always get the
711+
* write lock here, or we have a kernel bug.
712+
*/
713+
WARN_ON(uverbs_try_lock_object(uobj, true));
714+
uverbs_destroy_uobject(uobj, RDMA_REMOVE_CLOSE);
705715
up_read(&ufile->hw_destroy_rwsem);
706716
}
707717

708-
uobj->object = NULL;
709718
/* Matches the get in alloc_begin_fd_uobject */
710719
kref_put(&ufile->ref, ib_uverbs_release_file);
711720

@@ -783,7 +792,6 @@ static int __uverbs_cleanup_ufile(struct ib_uverbs_file *ufile,
783792
{
784793
struct ib_uobject *obj, *next_obj;
785794
int ret = -EINVAL;
786-
int err = 0;
787795

788796
/*
789797
* This shouldn't run while executing other commands on this
@@ -800,23 +808,8 @@ static int __uverbs_cleanup_ufile(struct ib_uverbs_file *ufile,
800808
* racing with a lookup_get.
801809
*/
802810
WARN_ON(uverbs_try_lock_object(obj, true));
803-
err = obj->type->type_class->remove_commit(obj, reason);
804-
805-
if (ib_is_destroy_retryable(err, reason, obj)) {
806-
pr_debug("ib_uverbs: failed to remove uobject id %d err %d\n",
807-
obj->id, err);
808-
atomic_set(&obj->usecnt, 0);
809-
continue;
810-
}
811-
812-
if (err)
813-
pr_err("ib_uverbs: unable to remove uobject id %d err %d\n",
814-
obj->id, err);
815-
816-
list_del(&obj->list);
817-
/* Pairs with the get in rdma_alloc_commit_uobject() */
818-
uverbs_uobject_put(obj);
819-
ret = 0;
811+
if (!uverbs_destroy_uobject(obj, reason))
812+
ret = 0;
820813
}
821814
return ret;
822815
}

include/rdma/ib_verbs.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,8 +1467,8 @@ enum rdma_remove_reason {
14671467
RDMA_REMOVE_CLOSE,
14681468
/* Driver is being hot-unplugged. This call should delete the actual object itself */
14691469
RDMA_REMOVE_DRIVER_REMOVE,
1470-
/* Context is being cleaned-up, but commit was just completed */
1471-
RDMA_REMOVE_DURING_CLEANUP,
1470+
/* uobj is being cleaned-up before being committed */
1471+
RDMA_REMOVE_ABORT,
14721472
};
14731473

14741474
struct ib_rdmacg_object {

0 commit comments

Comments
 (0)