Commit c485b19d authored by Jason Gunthorpe's avatar Jason Gunthorpe
Browse files

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: 3e032c0e ("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: default avatarYishai Hadas <yishaih@mellanox.com>
Signed-off-by: default avatarLeon Romanovsky <leonro@mellanox.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com>
parent c8b1f340
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -459,7 +459,8 @@ alloc_begin_fd_uobject(const struct uverbs_api_object *obj,
	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);
+1 −0
Original line number Diff line number Diff line
@@ -219,6 +219,7 @@ void ib_uverbs_init_event_queue(struct ib_uverbs_event_queue *ev_queue);
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);
+1 −1
Original line number Diff line number Diff line
@@ -346,7 +346,7 @@ const struct file_operations uverbs_async_event_fops = {
	.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,
};
+25 −1
Original line number Diff line number Diff line
@@ -26,10 +26,34 @@ static int uverbs_async_event_destroy_uobj(struct ib_uobject *uobj,
		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,