Skip to content

Commit 0f50d88

Browse files
committed
IB/uverbs: Allow all DESTROY commands to succeed after disassociate
The disassociate function was broken by design because it failed all commands. This prevents userspace from calling destroy on a uobject after it has detected a device fatal error and thus reclaiming the resources in userspace is prevented. This fix is now straightforward, when anything destroys a uobject that is not the user the object remains on the IDR with a NULL context and object pointer. All lookup locking modes other than DESTROY will fail. When the user ultimately calls the destroy function it is simply dropped from the IDR while any related information is returned. Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent a9b66d6 commit 0f50d88

File tree

4 files changed

+66
-16
lines changed

4 files changed

+66
-16
lines changed

drivers/infiniband/core/rdma_core.c

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ static int uverbs_destroy_uobject(struct ib_uobject *uobj,
180180
assert_uverbs_usecnt(uobj, UVERBS_LOOKUP_WRITE);
181181

182182
if (uobj->object) {
183-
ret = uobj->type->type_class->remove_commit(uobj, reason);
183+
ret = uobj->type->type_class->destroy_hw(uobj, reason);
184184
if (ret) {
185185
if (ib_is_destroy_retryable(ret, reason, uobj))
186186
return ret;
@@ -204,10 +204,13 @@ static int uverbs_destroy_uobject(struct ib_uobject *uobj,
204204

205205
/*
206206
* For DESTROY the usecnt is held write locked, the caller is expected
207-
* to put it unlock and put the object when done with it.
207+
* to put it unlock and put the object when done with it. Only DESTROY
208+
* can remove the IDR handle.
208209
*/
209210
if (reason != RDMA_REMOVE_DESTROY)
210211
atomic_set(&uobj->usecnt, 0);
212+
else
213+
uobj->type->type_class->remove_handle(uobj);
211214

212215
if (!list_empty(&uobj->list)) {
213216
spin_lock_irqsave(&ufile->uobjects_lock, flags);
@@ -554,8 +557,8 @@ static void alloc_abort_idr_uobject(struct ib_uobject *uobj)
554557
spin_unlock(&uobj->ufile->idr_lock);
555558
}
556559

557-
static int __must_check remove_commit_idr_uobject(struct ib_uobject *uobj,
558-
enum rdma_remove_reason why)
560+
static int __must_check destroy_hw_idr_uobject(struct ib_uobject *uobj,
561+
enum rdma_remove_reason why)
559562
{
560563
const struct uverbs_obj_idr_type *idr_type =
561564
container_of(uobj->type, struct uverbs_obj_idr_type,
@@ -573,20 +576,28 @@ static int __must_check remove_commit_idr_uobject(struct ib_uobject *uobj,
573576
if (why == RDMA_REMOVE_ABORT)
574577
return 0;
575578

576-
alloc_abort_idr_uobject(uobj);
577-
/* Matches the kref in alloc_commit_idr_uobject */
578-
uverbs_uobject_put(uobj);
579+
ib_rdmacg_uncharge(&uobj->cg_obj, uobj->context->device,
580+
RDMACG_RESOURCE_HCA_OBJECT);
579581

580582
return 0;
581583
}
582584

585+
static void remove_handle_idr_uobject(struct ib_uobject *uobj)
586+
{
587+
spin_lock(&uobj->ufile->idr_lock);
588+
idr_remove(&uobj->ufile->idr, uobj->id);
589+
spin_unlock(&uobj->ufile->idr_lock);
590+
/* Matches the kref in alloc_commit_idr_uobject */
591+
uverbs_uobject_put(uobj);
592+
}
593+
583594
static void alloc_abort_fd_uobject(struct ib_uobject *uobj)
584595
{
585596
put_unused_fd(uobj->id);
586597
}
587598

588-
static int __must_check remove_commit_fd_uobject(struct ib_uobject *uobj,
589-
enum rdma_remove_reason why)
599+
static int __must_check destroy_hw_fd_uobject(struct ib_uobject *uobj,
600+
enum rdma_remove_reason why)
590601
{
591602
const struct uverbs_obj_fd_type *fd_type =
592603
container_of(uobj->type, struct uverbs_obj_fd_type, type);
@@ -598,6 +609,10 @@ static int __must_check remove_commit_fd_uobject(struct ib_uobject *uobj,
598609
return 0;
599610
}
600611

612+
static void remove_handle_fd_uobject(struct ib_uobject *uobj)
613+
{
614+
}
615+
601616
static int alloc_commit_idr_uobject(struct ib_uobject *uobj)
602617
{
603618
struct ib_uverbs_file *ufile = uobj->ufile;
@@ -741,13 +756,41 @@ void rdma_lookup_put_uobject(struct ib_uobject *uobj,
741756
uverbs_uobject_put(uobj);
742757
}
743758

759+
void setup_ufile_idr_uobject(struct ib_uverbs_file *ufile)
760+
{
761+
spin_lock_init(&ufile->idr_lock);
762+
idr_init(&ufile->idr);
763+
}
764+
765+
void release_ufile_idr_uobject(struct ib_uverbs_file *ufile)
766+
{
767+
struct ib_uobject *entry;
768+
int id;
769+
770+
/*
771+
* At this point uverbs_cleanup_ufile() is guaranteed to have run, and
772+
* there are no HW objects left, however the IDR is still populated
773+
* with anything that has not been cleaned up by userspace. Since the
774+
* kref on ufile is 0, nothing is allowed to call lookup_get.
775+
*
776+
* This is an optimized equivalent to remove_handle_idr_uobject
777+
*/
778+
idr_for_each_entry(&ufile->idr, entry, id) {
779+
WARN_ON(entry->object);
780+
uverbs_uobject_put(entry);
781+
}
782+
783+
idr_destroy(&ufile->idr);
784+
}
785+
744786
const struct uverbs_obj_type_class uverbs_idr_class = {
745787
.alloc_begin = alloc_begin_idr_uobject,
746788
.lookup_get = lookup_get_idr_uobject,
747789
.alloc_commit = alloc_commit_idr_uobject,
748790
.alloc_abort = alloc_abort_idr_uobject,
749791
.lookup_put = lookup_put_idr_uobject,
750-
.remove_commit = remove_commit_idr_uobject,
792+
.destroy_hw = destroy_hw_idr_uobject,
793+
.remove_handle = remove_handle_idr_uobject,
751794
/*
752795
* When we destroy an object, we first just lock it for WRITE and
753796
* actually DESTROY it in the finalize stage. So, the problematic
@@ -945,7 +988,8 @@ const struct uverbs_obj_type_class uverbs_fd_class = {
945988
.alloc_commit = alloc_commit_fd_uobject,
946989
.alloc_abort = alloc_abort_fd_uobject,
947990
.lookup_put = lookup_put_fd_uobject,
948-
.remove_commit = remove_commit_fd_uobject,
991+
.destroy_hw = destroy_hw_fd_uobject,
992+
.remove_handle = remove_handle_fd_uobject,
949993
.needs_kfree_rcu = false,
950994
};
951995
EXPORT_SYMBOL(uverbs_fd_class);

drivers/infiniband/core/rdma_core.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,7 @@ int uverbs_finalize_object(struct ib_uobject *uobj,
110110
enum uverbs_obj_access access,
111111
bool commit);
112112

113+
void setup_ufile_idr_uobject(struct ib_uverbs_file *ufile);
114+
void release_ufile_idr_uobject(struct ib_uverbs_file *ufile);
115+
113116
#endif /* RDMA_CORE_H */

drivers/infiniband/core/uverbs_main.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,8 @@ void ib_uverbs_release_file(struct kref *ref)
253253
struct ib_device *ib_dev;
254254
int srcu_key;
255255

256+
release_ufile_idr_uobject(file);
257+
256258
srcu_key = srcu_read_lock(&file->device->disassociate_srcu);
257259
ib_dev = srcu_dereference(file->device->ib_dev,
258260
&file->device->disassociate_srcu);
@@ -867,8 +869,6 @@ static int ib_uverbs_open(struct inode *inode, struct file *filp)
867869
}
868870

869871
file->device = dev;
870-
spin_lock_init(&file->idr_lock);
871-
idr_init(&file->idr);
872872
kref_init(&file->ref);
873873
mutex_init(&file->ucontext_lock);
874874

@@ -885,6 +885,8 @@ static int ib_uverbs_open(struct inode *inode, struct file *filp)
885885
file->uverbs_cmd_mask = ib_dev->uverbs_cmd_mask;
886886
file->uverbs_ex_cmd_mask = ib_dev->uverbs_ex_cmd_mask;
887887

888+
setup_ufile_idr_uobject(file);
889+
888890
return nonseekable_open(inode, filp);
889891

890892
err_module:
@@ -904,7 +906,6 @@ static int ib_uverbs_close(struct inode *inode, struct file *filp)
904906
struct ib_uverbs_file *file = filp->private_data;
905907

906908
uverbs_destroy_ufile_hw(file, RDMA_REMOVE_CLOSE);
907-
idr_destroy(&file->idr);
908909

909910
mutex_lock(&file->device->lists_mutex);
910911
if (!file->is_closed) {

include/rdma/uverbs_types.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ enum rdma_lookup_mode {
6161
* Destruction flow:
6262
* lookup_get(exclusive=true) & uverbs_try_lock_object
6363
* remove_commit
64+
* remove_handle (optional)
6465
* lookup_put(exclusive=true) via rdma_lookup_put_uobject
6566
*
6667
* Allocate Error flow #1
@@ -92,8 +93,9 @@ struct uverbs_obj_type_class {
9293
enum rdma_lookup_mode mode);
9394
void (*lookup_put)(struct ib_uobject *uobj, enum rdma_lookup_mode mode);
9495
/* This does not consume the kref on uobj */
95-
int __must_check (*remove_commit)(struct ib_uobject *uobj,
96-
enum rdma_remove_reason why);
96+
int __must_check (*destroy_hw)(struct ib_uobject *uobj,
97+
enum rdma_remove_reason why);
98+
void (*remove_handle)(struct ib_uobject *uobj);
9799
u8 needs_kfree_rcu;
98100
};
99101

0 commit comments

Comments
 (0)