| From 14e23bd6d22123f6f3b2747701fa6cd4c6d05873 Mon Sep 17 00:00:00 2001 |
| From: Jason Gunthorpe <jgg@ziepe.ca> |
| Date: Wed, 8 Jan 2020 19:22:03 +0200 |
| Subject: RDMA/core: Fix locking in ib_uverbs_event_read |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Jason Gunthorpe <jgg@mellanox.com> |
| |
| commit 14e23bd6d22123f6f3b2747701fa6cd4c6d05873 upstream. |
| |
| This should not be using ib_dev to test for disassociation, during |
| disassociation is_closed is set under lock and the waitq is triggered. |
| |
| Instead check is_closed and be sure to re-obtain the lock to test the |
| value after the wait_event returns. |
| |
| Fixes: 036b10635739 ("IB/uverbs: Enable device removal when there are active user space applications") |
| Link: https://lore.kernel.org/r/1578504126-9400-12-git-send-email-yishaih@mellanox.com |
| Signed-off-by: Yishai Hadas <yishaih@mellanox.com> |
| Reviewed-by: Håkon Bugge <haakon.bugge@oracle.com> |
| Signed-off-by: Jason Gunthorpe <jgg@mellanox.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/infiniband/core/uverbs_main.c | 32 ++++++++++++++------------------ |
| 1 file changed, 14 insertions(+), 18 deletions(-) |
| |
| --- a/drivers/infiniband/core/uverbs_main.c |
| +++ b/drivers/infiniband/core/uverbs_main.c |
| @@ -220,7 +220,6 @@ void ib_uverbs_release_file(struct kref |
| } |
| |
| static ssize_t ib_uverbs_event_read(struct ib_uverbs_event_queue *ev_queue, |
| - struct ib_uverbs_file *uverbs_file, |
| struct file *filp, char __user *buf, |
| size_t count, loff_t *pos, |
| size_t eventsz) |
| @@ -238,19 +237,16 @@ static ssize_t ib_uverbs_event_read(stru |
| |
| if (wait_event_interruptible(ev_queue->poll_wait, |
| (!list_empty(&ev_queue->event_list) || |
| - /* The barriers built into wait_event_interruptible() |
| - * and wake_up() guarentee this will see the null set |
| - * without using RCU |
| - */ |
| - !uverbs_file->device->ib_dev))) |
| + ev_queue->is_closed))) |
| return -ERESTARTSYS; |
| |
| + spin_lock_irq(&ev_queue->lock); |
| + |
| /* If device was disassociated and no event exists set an error */ |
| - if (list_empty(&ev_queue->event_list) && |
| - !uverbs_file->device->ib_dev) |
| + if (list_empty(&ev_queue->event_list) && ev_queue->is_closed) { |
| + spin_unlock_irq(&ev_queue->lock); |
| return -EIO; |
| - |
| - spin_lock_irq(&ev_queue->lock); |
| + } |
| } |
| |
| event = list_entry(ev_queue->event_list.next, struct ib_uverbs_event, list); |
| @@ -285,8 +281,7 @@ static ssize_t ib_uverbs_async_event_rea |
| { |
| struct ib_uverbs_async_event_file *file = filp->private_data; |
| |
| - return ib_uverbs_event_read(&file->ev_queue, file->uverbs_file, filp, |
| - buf, count, pos, |
| + return ib_uverbs_event_read(&file->ev_queue, filp, buf, count, pos, |
| sizeof(struct ib_uverbs_async_event_desc)); |
| } |
| |
| @@ -296,9 +291,8 @@ static ssize_t ib_uverbs_comp_event_read |
| struct ib_uverbs_completion_event_file *comp_ev_file = |
| filp->private_data; |
| |
| - return ib_uverbs_event_read(&comp_ev_file->ev_queue, |
| - comp_ev_file->uobj.ufile, filp, |
| - buf, count, pos, |
| + return ib_uverbs_event_read(&comp_ev_file->ev_queue, filp, buf, count, |
| + pos, |
| sizeof(struct ib_uverbs_comp_event_desc)); |
| } |
| |
| @@ -321,7 +315,9 @@ static __poll_t ib_uverbs_event_poll(str |
| static __poll_t ib_uverbs_async_event_poll(struct file *filp, |
| struct poll_table_struct *wait) |
| { |
| - return ib_uverbs_event_poll(filp->private_data, filp, wait); |
| + struct ib_uverbs_async_event_file *file = filp->private_data; |
| + |
| + return ib_uverbs_event_poll(&file->ev_queue, filp, wait); |
| } |
| |
| static __poll_t ib_uverbs_comp_event_poll(struct file *filp, |
| @@ -335,9 +331,9 @@ static __poll_t ib_uverbs_comp_event_pol |
| |
| static int ib_uverbs_async_event_fasync(int fd, struct file *filp, int on) |
| { |
| - struct ib_uverbs_event_queue *ev_queue = filp->private_data; |
| + struct ib_uverbs_async_event_file *file = filp->private_data; |
| |
| - return fasync_helper(fd, filp, on, &ev_queue->async_queue); |
| + return fasync_helper(fd, filp, on, &file->ev_queue.async_queue); |
| } |
| |
| static int ib_uverbs_comp_event_fasync(int fd, struct file *filp, int on) |