Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

RDMA/uverbs: Do not discard the IB_EVENT_DEVICE_FATAL event

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>

+29 -3
+2 -1
drivers/infiniband/core/rdma_core.c
··· 459 459 struct ib_uobject *uobj; 460 460 struct file *filp; 461 461 462 - if (WARN_ON(fd_type->fops->release != &uverbs_uobject_fd_release)) 462 + if (WARN_ON(fd_type->fops->release != &uverbs_uobject_fd_release && 463 + fd_type->fops->release != &uverbs_async_event_release)) 463 464 return ERR_PTR(-EINVAL); 464 465 465 466 new_fd = get_unused_fd_flags(O_CLOEXEC);
+1
drivers/infiniband/core/uverbs.h
··· 219 219 void ib_uverbs_init_async_event_file(struct ib_uverbs_async_event_file *ev_file); 220 220 void ib_uverbs_free_event_queue(struct ib_uverbs_event_queue *event_queue); 221 221 void ib_uverbs_flow_resources_free(struct ib_uflow_resources *uflow_res); 222 + int uverbs_async_event_release(struct inode *inode, struct file *filp); 222 223 223 224 int ib_alloc_ucontext(struct uverbs_attr_bundle *attrs); 224 225 int ib_init_ucontext(struct uverbs_attr_bundle *attrs);
+1 -1
drivers/infiniband/core/uverbs_main.c
··· 346 346 .owner = THIS_MODULE, 347 347 .read = ib_uverbs_async_event_read, 348 348 .poll = ib_uverbs_async_event_poll, 349 - .release = uverbs_uobject_fd_release, 349 + .release = uverbs_async_event_release, 350 350 .fasync = ib_uverbs_async_event_fasync, 351 351 .llseek = no_llseek, 352 352 };
+25 -1
drivers/infiniband/core/uverbs_std_types_async_fd.c
··· 26 26 container_of(uobj, struct ib_uverbs_async_event_file, uobj); 27 27 28 28 ib_unregister_event_handler(&event_file->event_handler); 29 - ib_uverbs_free_event_queue(&event_file->ev_queue); 30 29 return 0; 30 + } 31 + 32 + int uverbs_async_event_release(struct inode *inode, struct file *filp) 33 + { 34 + struct ib_uverbs_async_event_file *event_file; 35 + struct ib_uobject *uobj = filp->private_data; 36 + int ret; 37 + 38 + if (!uobj) 39 + return uverbs_uobject_fd_release(inode, filp); 40 + 41 + event_file = 42 + container_of(uobj, struct ib_uverbs_async_event_file, uobj); 43 + 44 + /* 45 + * The async event FD has to deliver IB_EVENT_DEVICE_FATAL even after 46 + * disassociation, so cleaning the event list must only happen after 47 + * release. The user knows it has reached the end of the event stream 48 + * when it sees IB_EVENT_DEVICE_FATAL. 49 + */ 50 + uverbs_uobject_get(uobj); 51 + ret = uverbs_uobject_fd_release(inode, filp); 52 + ib_uverbs_free_event_queue(&event_file->ev_queue); 53 + uverbs_uobject_put(uobj); 54 + return ret; 31 55 } 32 56 33 57 DECLARE_UVERBS_NAMED_METHOD(