When the video device is unregistered any filehandles waiting on an event (i.e. in VIDIOC_DQEVENT) will never wake up. Wake them up and detect that the video device is unregistered in __v4l2_event_dequeue() and return -ENODEV in that case. Signed-off-by: Hans Verkuil <hans.verkuil@xxxxxxxxx> --- Note: this is a quick hack, just for events. We have the same problem with vb2 (waiting for a buffer) and cec (waiting for an event). Not sure if rc or dvb are also suffering from the same problem. I wonder if there is an easier way to wake up all fhs that are waiting for some event. Michael: most applications use non-blocking mode and poll/select to wait for something to happen. In such applications the poll/select will return when the device is unregistered and this problem does not occur. --- diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index d5e0e536ef04..42574ccbd770 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -1017,6 +1017,9 @@ EXPORT_SYMBOL(__video_register_device); */ void video_unregister_device(struct video_device *vdev) { + unsigned long flags; + struct v4l2_fh *fh; + /* Check if vdev was ever registered at all */ if (!vdev || !video_is_registered(vdev)) return; @@ -1027,6 +1030,12 @@ void video_unregister_device(struct video_device *vdev) */ clear_bit(V4L2_FL_REGISTERED, &vdev->flags); mutex_unlock(&videodev_lock); + + spin_lock_irqsave(&vdev->fh_lock, flags); + list_for_each_entry(fh, &vdev->fh_list, list) + wake_up_all(&fh->wait); + spin_unlock_irqrestore(&vdev->fh_lock, flags); + device_unregister(&vdev->dev); } EXPORT_SYMBOL(video_unregister_device); diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c index 968c2eb08b5a..d04977cd1bfb 100644 --- a/drivers/media/v4l2-core/v4l2-event.c +++ b/drivers/media/v4l2-core/v4l2-event.c @@ -36,12 +36,17 @@ static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event) { struct v4l2_kevent *kev; unsigned long flags; + int ret = 0; spin_lock_irqsave(&fh->vdev->fh_lock, flags); + if (!video_is_registered(fh->vdev)) { + ret = -ENODEV; + goto err; + } if (list_empty(&fh->available)) { - spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - return -ENOENT; + ret = -ENOENT; + goto err; } WARN_ON(fh->navailable == 0); @@ -54,10 +59,10 @@ static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event) *event = kev->event; kev->sev->first = sev_pos(kev->sev, 1); kev->sev->in_use--; - +err: spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - return 0; + return ret; } int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,