| From c485b19d52c4ba269dfd027945dee81755fdd530 Mon Sep 17 00:00:00 2001 |
| From: Jason Gunthorpe <jgg@ziepe.ca> |
| Date: Thu, 7 May 2020 09:33:47 +0300 |
| Subject: RDMA/uverbs: Do not discard the IB_EVENT_DEVICE_FATAL event |
| |
| From: Jason Gunthorpe <jgg@mellanox.com> |
| |
| commit c485b19d52c4ba269dfd027945dee81755fdd530 upstream. |
| |
| The commit below moved all of the destruction to the disassociate step and |
| cleaned up the event channel during destroy_uobj. |
| |
| However, when ib_uverbs_free_hw_resources() pushes IB_EVENT_DEVICE_FATAL |
| and then immediately goes to destroy all uobjects this causes |
| ib_uverbs_free_event_queue() to discard the queued event if userspace |
| hasn't already read() it. |
| |
| Unlike all other event queues async FD needs to defer the |
| ib_uverbs_free_event_queue() until FD release. This still unregisters the |
| handler from the IB device during disassociation. |
| |
| Fixes: 3e032c0e92aa ("RDMA/core: Make ib_uverbs_async_event_file into a uobject") |
| Link: https://lore.kernel.org/r/20200507063348.98713-2-leon@kernel.org |
| Signed-off-by: Yishai Hadas <yishaih@mellanox.com> |
| Signed-off-by: Leon Romanovsky <leonro@mellanox.com> |
| Signed-off-by: Jason Gunthorpe <jgg@mellanox.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/infiniband/core/rdma_core.c | 3 +- |
| drivers/infiniband/core/uverbs.h | 1 |
| drivers/infiniband/core/uverbs_main.c | 2 - |
| drivers/infiniband/core/uverbs_std_types_async_fd.c | 26 +++++++++++++++++++- |
| 4 files changed, 29 insertions(+), 3 deletions(-) |
| |
| --- a/drivers/infiniband/core/rdma_core.c |
| +++ b/drivers/infiniband/core/rdma_core.c |
| @@ -459,7 +459,8 @@ alloc_begin_fd_uobject(const struct uver |
| struct ib_uobject *uobj; |
| struct file *filp; |
| |
| - if (WARN_ON(fd_type->fops->release != &uverbs_uobject_fd_release)) |
| + if (WARN_ON(fd_type->fops->release != &uverbs_uobject_fd_release && |
| + fd_type->fops->release != &uverbs_async_event_release)) |
| return ERR_PTR(-EINVAL); |
| |
| new_fd = get_unused_fd_flags(O_CLOEXEC); |
| --- a/drivers/infiniband/core/uverbs.h |
| +++ b/drivers/infiniband/core/uverbs.h |
| @@ -219,6 +219,7 @@ void ib_uverbs_init_event_queue(struct i |
| void ib_uverbs_init_async_event_file(struct ib_uverbs_async_event_file *ev_file); |
| void ib_uverbs_free_event_queue(struct ib_uverbs_event_queue *event_queue); |
| void ib_uverbs_flow_resources_free(struct ib_uflow_resources *uflow_res); |
| +int uverbs_async_event_release(struct inode *inode, struct file *filp); |
| |
| int ib_alloc_ucontext(struct uverbs_attr_bundle *attrs); |
| int ib_init_ucontext(struct uverbs_attr_bundle *attrs); |
| --- a/drivers/infiniband/core/uverbs_main.c |
| +++ b/drivers/infiniband/core/uverbs_main.c |
| @@ -346,7 +346,7 @@ const struct file_operations uverbs_asyn |
| .owner = THIS_MODULE, |
| .read = ib_uverbs_async_event_read, |
| .poll = ib_uverbs_async_event_poll, |
| - .release = uverbs_uobject_fd_release, |
| + .release = uverbs_async_event_release, |
| .fasync = ib_uverbs_async_event_fasync, |
| .llseek = no_llseek, |
| }; |
| --- a/drivers/infiniband/core/uverbs_std_types_async_fd.c |
| +++ b/drivers/infiniband/core/uverbs_std_types_async_fd.c |
| @@ -26,10 +26,34 @@ static int uverbs_async_event_destroy_uo |
| container_of(uobj, struct ib_uverbs_async_event_file, uobj); |
| |
| ib_unregister_event_handler(&event_file->event_handler); |
| - ib_uverbs_free_event_queue(&event_file->ev_queue); |
| return 0; |
| } |
| |
| +int uverbs_async_event_release(struct inode *inode, struct file *filp) |
| +{ |
| + struct ib_uverbs_async_event_file *event_file; |
| + struct ib_uobject *uobj = filp->private_data; |
| + int ret; |
| + |
| + if (!uobj) |
| + return uverbs_uobject_fd_release(inode, filp); |
| + |
| + event_file = |
| + container_of(uobj, struct ib_uverbs_async_event_file, uobj); |
| + |
| + /* |
| + * The async event FD has to deliver IB_EVENT_DEVICE_FATAL even after |
| + * disassociation, so cleaning the event list must only happen after |
| + * release. The user knows it has reached the end of the event stream |
| + * when it sees IB_EVENT_DEVICE_FATAL. |
| + */ |
| + uverbs_uobject_get(uobj); |
| + ret = uverbs_uobject_fd_release(inode, filp); |
| + ib_uverbs_free_event_queue(&event_file->ev_queue); |
| + uverbs_uobject_put(uobj); |
| + return ret; |
| +} |
| + |
| DECLARE_UVERBS_NAMED_METHOD( |
| UVERBS_METHOD_ASYNC_EVENT_ALLOC, |
| UVERBS_ATTR_FD(UVERBS_ATTR_ASYNC_EVENT_ALLOC_FD_HANDLE, |