]>
Commit | Line | Data |
---|---|---|
ff3d1419 GKH |
1 | From 92539d3eda2c090b382699bbb896d4b54e9bdece Mon Sep 17 00:00:00 2001 |
2 | From: Sakari Ailus <sakari.ailus@linux.intel.com> | |
3 | Date: Mon, 5 Nov 2018 09:35:44 -0500 | |
4 | Subject: media: v4l: event: Add subscription to list before calling "add" operation | |
5 | ||
6 | From: Sakari Ailus <sakari.ailus@linux.intel.com> | |
7 | ||
8 | commit 92539d3eda2c090b382699bbb896d4b54e9bdece upstream. | |
9 | ||
10 | Patch ad608fbcf166 changed how events were subscribed to address an issue | |
11 | elsewhere. As a side effect of that change, the "add" callback was called | |
12 | before the event subscription was added to the list of subscribed events, | |
13 | causing the first event queued by the add callback (and possibly other | |
14 | events arriving soon afterwards) to be lost. | |
15 | ||
16 | Fix this by adding the subscription to the list before calling the "add" | |
17 | callback, and clean up afterwards if that fails. | |
18 | ||
19 | Fixes: ad608fbcf166 ("media: v4l: event: Prevent freeing event subscriptions while accessed") | |
20 | ||
21 | Reported-by: Dave Stevenson <dave.stevenson@raspberrypi.org> | |
22 | Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> | |
23 | Tested-by: Dave Stevenson <dave.stevenson@raspberrypi.org> | |
24 | Reviewed-by: Hans Verkuil <hans.verkuil@cisco.com> | |
25 | Tested-by: Hans Verkuil <hans.verkuil@cisco.com> | |
26 | Cc: stable@vger.kernel.org (for 4.14 and up) | |
27 | Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org> | |
28 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
29 | ||
30 | --- | |
31 | drivers/media/v4l2-core/v4l2-event.c | 43 +++++++++++++++++++---------------- | |
32 | 1 file changed, 24 insertions(+), 19 deletions(-) | |
33 | ||
34 | --- a/drivers/media/v4l2-core/v4l2-event.c | |
35 | +++ b/drivers/media/v4l2-core/v4l2-event.c | |
36 | @@ -194,6 +194,22 @@ int v4l2_event_pending(struct v4l2_fh *f | |
37 | } | |
38 | EXPORT_SYMBOL_GPL(v4l2_event_pending); | |
39 | ||
40 | +static void __v4l2_event_unsubscribe(struct v4l2_subscribed_event *sev) | |
41 | +{ | |
42 | + struct v4l2_fh *fh = sev->fh; | |
43 | + unsigned int i; | |
44 | + | |
45 | + lockdep_assert_held(&fh->subscribe_lock); | |
46 | + assert_spin_locked(&fh->vdev->fh_lock); | |
47 | + | |
48 | + /* Remove any pending events for this subscription */ | |
49 | + for (i = 0; i < sev->in_use; i++) { | |
50 | + list_del(&sev->events[sev_pos(sev, i)].list); | |
51 | + fh->navailable--; | |
52 | + } | |
53 | + list_del(&sev->list); | |
54 | +} | |
55 | + | |
56 | int v4l2_event_subscribe(struct v4l2_fh *fh, | |
57 | const struct v4l2_event_subscription *sub, unsigned elems, | |
58 | const struct v4l2_subscribed_event_ops *ops) | |
59 | @@ -225,27 +241,23 @@ int v4l2_event_subscribe(struct v4l2_fh | |
60 | ||
61 | spin_lock_irqsave(&fh->vdev->fh_lock, flags); | |
62 | found_ev = v4l2_event_subscribed(fh, sub->type, sub->id); | |
63 | + if (!found_ev) | |
64 | + list_add(&sev->list, &fh->subscribed); | |
65 | spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); | |
66 | ||
67 | if (found_ev) { | |
68 | /* Already listening */ | |
69 | kfree(sev); | |
70 | - goto out_unlock; | |
71 | - } | |
72 | - | |
73 | - if (sev->ops && sev->ops->add) { | |
74 | + } else if (sev->ops && sev->ops->add) { | |
75 | ret = sev->ops->add(sev, elems); | |
76 | if (ret) { | |
77 | + spin_lock_irqsave(&fh->vdev->fh_lock, flags); | |
78 | + __v4l2_event_unsubscribe(sev); | |
79 | + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); | |
80 | kfree(sev); | |
81 | - goto out_unlock; | |
82 | } | |
83 | } | |
84 | ||
85 | - spin_lock_irqsave(&fh->vdev->fh_lock, flags); | |
86 | - list_add(&sev->list, &fh->subscribed); | |
87 | - spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); | |
88 | - | |
89 | -out_unlock: | |
90 | mutex_unlock(&fh->subscribe_lock); | |
91 | ||
92 | return ret; | |
93 | @@ -280,7 +292,6 @@ int v4l2_event_unsubscribe(struct v4l2_f | |
94 | { | |
95 | struct v4l2_subscribed_event *sev; | |
96 | unsigned long flags; | |
97 | - int i; | |
98 | ||
99 | if (sub->type == V4L2_EVENT_ALL) { | |
100 | v4l2_event_unsubscribe_all(fh); | |
101 | @@ -292,14 +303,8 @@ int v4l2_event_unsubscribe(struct v4l2_f | |
102 | spin_lock_irqsave(&fh->vdev->fh_lock, flags); | |
103 | ||
104 | sev = v4l2_event_subscribed(fh, sub->type, sub->id); | |
105 | - if (sev != NULL) { | |
106 | - /* Remove any pending events for this subscription */ | |
107 | - for (i = 0; i < sev->in_use; i++) { | |
108 | - list_del(&sev->events[sev_pos(sev, i)].list); | |
109 | - fh->navailable--; | |
110 | - } | |
111 | - list_del(&sev->list); | |
112 | - } | |
113 | + if (sev != NULL) | |
114 | + __v4l2_event_unsubscribe(sev); | |
115 | ||
116 | spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); | |
117 |