Skip to content

Commit 03c4044

Browse files
yishaihdledford
authored andcommitted
IB/uverbs: Fix reference counting usage of event files
Fix the reference counting usage to be handled in the event file creation/destruction function, instead of being done by the caller. This is done for both async/non-async event files. Based on Jason Gunthorpe report at https://www.mail-archive.com/ linux-rdma@vger.kernel.org/msg24680.html: "The existing code for this is broken, in ib_uverbs_get_context all the error paths between ib_uverbs_alloc_event_file and the kref_get(file->ref) are wrong - this will result in fput() which will call ib_uverbs_event_close, which will try to do kref_put and ib_unregister_event_handler - which are no longer paired." Signed-off-by: Yishai Hadas <yishaih@mellanox.com> Signed-off-by: Shachar Raindel <raindel@mellanox.com> Reviewed-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com> Signed-off-by: Doug Ledford <dledford@redhat.com>
1 parent 7dd7864 commit 03c4044

File tree

3 files changed

+40
-16
lines changed

3 files changed

+40
-16
lines changed

drivers/infiniband/core/uverbs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ void idr_remove_uobj(struct idr *idp, struct ib_uobject *uobj);
178178

179179
struct file *ib_uverbs_alloc_event_file(struct ib_uverbs_file *uverbs_file,
180180
int is_async);
181+
void ib_uverbs_free_async_event_file(struct ib_uverbs_file *uverbs_file);
181182
struct ib_uverbs_event_file *ib_uverbs_lookup_comp_file(int fd);
182183

183184
void ib_uverbs_release_ucq(struct ib_uverbs_file *file,

drivers/infiniband/core/uverbs_cmd.c

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -367,16 +367,6 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,
367367
goto err_file;
368368
}
369369

370-
file->async_file = filp->private_data;
371-
372-
INIT_IB_EVENT_HANDLER(&file->event_handler, file->device->ib_dev,
373-
ib_uverbs_event_handler);
374-
ret = ib_register_event_handler(&file->event_handler);
375-
if (ret)
376-
goto err_file;
377-
378-
kref_get(&file->async_file->ref);
379-
kref_get(&file->ref);
380370
file->ucontext = ucontext;
381371

382372
fd_install(resp.async_fd, filp);
@@ -386,6 +376,7 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,
386376
return in_len;
387377

388378
err_file:
379+
ib_uverbs_free_async_event_file(file);
389380
fput(filp);
390381

391382
err_fd:

drivers/infiniband/core/uverbs_main.c

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -406,10 +406,9 @@ static int ib_uverbs_event_close(struct inode *inode, struct file *filp)
406406
}
407407
spin_unlock_irq(&file->lock);
408408

409-
if (file->is_async) {
409+
if (file->is_async)
410410
ib_unregister_event_handler(&file->uverbs_file->event_handler);
411-
kref_put(&file->uverbs_file->ref, ib_uverbs_release_file);
412-
}
411+
kref_put(&file->uverbs_file->ref, ib_uverbs_release_file);
413412
kref_put(&file->ref, ib_uverbs_release_event_file);
414413

415414
return 0;
@@ -541,13 +540,20 @@ void ib_uverbs_event_handler(struct ib_event_handler *handler,
541540
NULL, NULL);
542541
}
543542

543+
void ib_uverbs_free_async_event_file(struct ib_uverbs_file *file)
544+
{
545+
kref_put(&file->async_file->ref, ib_uverbs_release_event_file);
546+
file->async_file = NULL;
547+
}
548+
544549
struct file *ib_uverbs_alloc_event_file(struct ib_uverbs_file *uverbs_file,
545550
int is_async)
546551
{
547552
struct ib_uverbs_event_file *ev_file;
548553
struct file *filp;
554+
int ret;
549555

550-
ev_file = kmalloc(sizeof *ev_file, GFP_KERNEL);
556+
ev_file = kzalloc(sizeof(*ev_file), GFP_KERNEL);
551557
if (!ev_file)
552558
return ERR_PTR(-ENOMEM);
553559

@@ -556,15 +562,41 @@ struct file *ib_uverbs_alloc_event_file(struct ib_uverbs_file *uverbs_file,
556562
INIT_LIST_HEAD(&ev_file->event_list);
557563
init_waitqueue_head(&ev_file->poll_wait);
558564
ev_file->uverbs_file = uverbs_file;
565+
kref_get(&ev_file->uverbs_file->ref);
559566
ev_file->async_queue = NULL;
560-
ev_file->is_async = is_async;
561567
ev_file->is_closed = 0;
562568

563569
filp = anon_inode_getfile("[infinibandevent]", &uverbs_event_fops,
564570
ev_file, O_RDONLY);
565571
if (IS_ERR(filp))
566-
kfree(ev_file);
572+
goto err_put_refs;
573+
574+
if (is_async) {
575+
WARN_ON(uverbs_file->async_file);
576+
uverbs_file->async_file = ev_file;
577+
kref_get(&uverbs_file->async_file->ref);
578+
INIT_IB_EVENT_HANDLER(&uverbs_file->event_handler,
579+
uverbs_file->device->ib_dev,
580+
ib_uverbs_event_handler);
581+
ret = ib_register_event_handler(&uverbs_file->event_handler);
582+
if (ret)
583+
goto err_put_file;
584+
585+
/* At that point async file stuff was fully set */
586+
ev_file->is_async = 1;
587+
}
588+
589+
return filp;
590+
591+
err_put_file:
592+
fput(filp);
593+
kref_put(&uverbs_file->async_file->ref, ib_uverbs_release_event_file);
594+
uverbs_file->async_file = NULL;
595+
return ERR_PTR(ret);
567596

597+
err_put_refs:
598+
kref_put(&ev_file->uverbs_file->ref, ib_uverbs_release_file);
599+
kref_put(&ev_file->ref, ib_uverbs_release_event_file);
568600
return filp;
569601
}
570602

0 commit comments

Comments
 (0)