]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/5.10.215/vfio-platform-create-persistent-irq-handlers.patch
Linux 5.10.215
[thirdparty/kernel/stable-queue.git] / releases / 5.10.215 / vfio-platform-create-persistent-irq-handlers.patch
1 From stable+bounces-35128-greg=kroah.com@vger.kernel.org Mon Apr 1 18:54:07 2024
2 From: Alex Williamson <alex.williamson@redhat.com>
3 Date: Mon, 1 Apr 2024 10:52:59 -0600
4 Subject: vfio/platform: Create persistent IRQ handlers
5 To: stable@vger.kernel.org
6 Cc: Alex Williamson <alex.williamson@redhat.com>, sashal@kernel.org, gregkh@linuxfoundation.org, eric.auger@redhat.com, Kevin Tian <kevin.tian@intel.com>
7 Message-ID: <20240401165302.3699643-6-alex.williamson@redhat.com>
8
9 From: Alex Williamson <alex.williamson@redhat.com>
10
11 [ Upstream commit 675daf435e9f8e5a5eab140a9864dfad6668b375 ]
12
13 The vfio-platform SET_IRQS ioctl currently allows loopback triggering of
14 an interrupt before a signaling eventfd has been configured by the user,
15 which thereby allows a NULL pointer dereference.
16
17 Rather than register the IRQ relative to a valid trigger, register all
18 IRQs in a disabled state in the device open path. This allows mask
19 operations on the IRQ to nest within the overall enable state governed
20 by a valid eventfd signal. This decouples @masked, protected by the
21 @locked spinlock from @trigger, protected via the @igate mutex.
22
23 In doing so, it's guaranteed that changes to @trigger cannot race the
24 IRQ handlers because the IRQ handler is synchronously disabled before
25 modifying the trigger, and loopback triggering of the IRQ via ioctl is
26 safe due to serialization with trigger changes via igate.
27
28 For compatibility, request_irq() failures are maintained to be local to
29 the SET_IRQS ioctl rather than a fatal error in the open device path.
30 This allows, for example, a userspace driver with polling mode support
31 to continue to work regardless of moving the request_irq() call site.
32 This necessarily blocks all SET_IRQS access to the failed index.
33
34 Cc: Eric Auger <eric.auger@redhat.com>
35 Cc: <stable@vger.kernel.org>
36 Fixes: 57f972e2b341 ("vfio/platform: trigger an interrupt via eventfd")
37 Reviewed-by: Kevin Tian <kevin.tian@intel.com>
38 Reviewed-by: Eric Auger <eric.auger@redhat.com>
39 Link: https://lore.kernel.org/r/20240308230557.805580-7-alex.williamson@redhat.com
40 Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
41 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
42 ---
43 drivers/vfio/platform/vfio_platform_irq.c | 101 ++++++++++++++++++++----------
44 1 file changed, 68 insertions(+), 33 deletions(-)
45
46 --- a/drivers/vfio/platform/vfio_platform_irq.c
47 +++ b/drivers/vfio/platform/vfio_platform_irq.c
48 @@ -136,6 +136,16 @@ static int vfio_platform_set_irq_unmask(
49 return 0;
50 }
51
52 +/*
53 + * The trigger eventfd is guaranteed valid in the interrupt path
54 + * and protected by the igate mutex when triggered via ioctl.
55 + */
56 +static void vfio_send_eventfd(struct vfio_platform_irq *irq_ctx)
57 +{
58 + if (likely(irq_ctx->trigger))
59 + eventfd_signal(irq_ctx->trigger, 1);
60 +}
61 +
62 static irqreturn_t vfio_automasked_irq_handler(int irq, void *dev_id)
63 {
64 struct vfio_platform_irq *irq_ctx = dev_id;
65 @@ -155,7 +165,7 @@ static irqreturn_t vfio_automasked_irq_h
66 spin_unlock_irqrestore(&irq_ctx->lock, flags);
67
68 if (ret == IRQ_HANDLED)
69 - eventfd_signal(irq_ctx->trigger, 1);
70 + vfio_send_eventfd(irq_ctx);
71
72 return ret;
73 }
74 @@ -164,22 +174,19 @@ static irqreturn_t vfio_irq_handler(int
75 {
76 struct vfio_platform_irq *irq_ctx = dev_id;
77
78 - eventfd_signal(irq_ctx->trigger, 1);
79 + vfio_send_eventfd(irq_ctx);
80
81 return IRQ_HANDLED;
82 }
83
84 static int vfio_set_trigger(struct vfio_platform_device *vdev, int index,
85 - int fd, irq_handler_t handler)
86 + int fd)
87 {
88 struct vfio_platform_irq *irq = &vdev->irqs[index];
89 struct eventfd_ctx *trigger;
90 - int ret;
91
92 if (irq->trigger) {
93 - irq_clear_status_flags(irq->hwirq, IRQ_NOAUTOEN);
94 - free_irq(irq->hwirq, irq);
95 - kfree(irq->name);
96 + disable_irq(irq->hwirq);
97 eventfd_ctx_put(irq->trigger);
98 irq->trigger = NULL;
99 }
100 @@ -187,30 +194,20 @@ static int vfio_set_trigger(struct vfio_
101 if (fd < 0) /* Disable only */
102 return 0;
103
104 - irq->name = kasprintf(GFP_KERNEL, "vfio-irq[%d](%s)",
105 - irq->hwirq, vdev->name);
106 - if (!irq->name)
107 - return -ENOMEM;
108 -
109 trigger = eventfd_ctx_fdget(fd);
110 - if (IS_ERR(trigger)) {
111 - kfree(irq->name);
112 + if (IS_ERR(trigger))
113 return PTR_ERR(trigger);
114 - }
115
116 irq->trigger = trigger;
117
118 - irq_set_status_flags(irq->hwirq, IRQ_NOAUTOEN);
119 - ret = request_irq(irq->hwirq, handler, 0, irq->name, irq);
120 - if (ret) {
121 - kfree(irq->name);
122 - eventfd_ctx_put(trigger);
123 - irq->trigger = NULL;
124 - return ret;
125 - }
126 -
127 - if (!irq->masked)
128 - enable_irq(irq->hwirq);
129 + /*
130 + * irq->masked effectively provides nested disables within the overall
131 + * enable relative to trigger. Specifically request_irq() is called
132 + * with NO_AUTOEN, therefore the IRQ is initially disabled. The user
133 + * may only further disable the IRQ with a MASK operations because
134 + * irq->masked is initially false.
135 + */
136 + enable_irq(irq->hwirq);
137
138 return 0;
139 }
140 @@ -229,7 +226,7 @@ static int vfio_platform_set_irq_trigger
141 handler = vfio_irq_handler;
142
143 if (!count && (flags & VFIO_IRQ_SET_DATA_NONE))
144 - return vfio_set_trigger(vdev, index, -1, handler);
145 + return vfio_set_trigger(vdev, index, -1);
146
147 if (start != 0 || count != 1)
148 return -EINVAL;
149 @@ -237,7 +234,7 @@ static int vfio_platform_set_irq_trigger
150 if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
151 int32_t fd = *(int32_t *)data;
152
153 - return vfio_set_trigger(vdev, index, fd, handler);
154 + return vfio_set_trigger(vdev, index, fd);
155 }
156
157 if (flags & VFIO_IRQ_SET_DATA_NONE) {
158 @@ -261,6 +258,14 @@ int vfio_platform_set_irqs_ioctl(struct
159 unsigned start, unsigned count, uint32_t flags,
160 void *data) = NULL;
161
162 + /*
163 + * For compatibility, errors from request_irq() are local to the
164 + * SET_IRQS path and reflected in the name pointer. This allows,
165 + * for example, polling mode fallback for an exclusive IRQ failure.
166 + */
167 + if (IS_ERR(vdev->irqs[index].name))
168 + return PTR_ERR(vdev->irqs[index].name);
169 +
170 switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
171 case VFIO_IRQ_SET_ACTION_MASK:
172 func = vfio_platform_set_irq_mask;
173 @@ -281,7 +286,7 @@ int vfio_platform_set_irqs_ioctl(struct
174
175 int vfio_platform_irq_init(struct vfio_platform_device *vdev)
176 {
177 - int cnt = 0, i;
178 + int cnt = 0, i, ret = 0;
179
180 while (vdev->get_irq(vdev, cnt) >= 0)
181 cnt++;
182 @@ -292,29 +297,54 @@ int vfio_platform_irq_init(struct vfio_p
183
184 for (i = 0; i < cnt; i++) {
185 int hwirq = vdev->get_irq(vdev, i);
186 + irq_handler_t handler = vfio_irq_handler;
187
188 - if (hwirq < 0)
189 + if (hwirq < 0) {
190 + ret = -EINVAL;
191 goto err;
192 + }
193
194 spin_lock_init(&vdev->irqs[i].lock);
195
196 vdev->irqs[i].flags = VFIO_IRQ_INFO_EVENTFD;
197
198 - if (irq_get_trigger_type(hwirq) & IRQ_TYPE_LEVEL_MASK)
199 + if (irq_get_trigger_type(hwirq) & IRQ_TYPE_LEVEL_MASK) {
200 vdev->irqs[i].flags |= VFIO_IRQ_INFO_MASKABLE
201 | VFIO_IRQ_INFO_AUTOMASKED;
202 + handler = vfio_automasked_irq_handler;
203 + }
204
205 vdev->irqs[i].count = 1;
206 vdev->irqs[i].hwirq = hwirq;
207 vdev->irqs[i].masked = false;
208 + vdev->irqs[i].name = kasprintf(GFP_KERNEL,
209 + "vfio-irq[%d](%s)", hwirq,
210 + vdev->name);
211 + if (!vdev->irqs[i].name) {
212 + ret = -ENOMEM;
213 + goto err;
214 + }
215 +
216 + ret = request_irq(hwirq, handler, IRQF_NO_AUTOEN,
217 + vdev->irqs[i].name, &vdev->irqs[i]);
218 + if (ret) {
219 + kfree(vdev->irqs[i].name);
220 + vdev->irqs[i].name = ERR_PTR(ret);
221 + }
222 }
223
224 vdev->num_irqs = cnt;
225
226 return 0;
227 err:
228 + for (--i; i >= 0; i--) {
229 + if (!IS_ERR(vdev->irqs[i].name)) {
230 + free_irq(vdev->irqs[i].hwirq, &vdev->irqs[i]);
231 + kfree(vdev->irqs[i].name);
232 + }
233 + }
234 kfree(vdev->irqs);
235 - return -EINVAL;
236 + return ret;
237 }
238
239 void vfio_platform_irq_cleanup(struct vfio_platform_device *vdev)
240 @@ -324,7 +354,12 @@ void vfio_platform_irq_cleanup(struct vf
241 for (i = 0; i < vdev->num_irqs; i++) {
242 vfio_virqfd_disable(&vdev->irqs[i].mask);
243 vfio_virqfd_disable(&vdev->irqs[i].unmask);
244 - vfio_set_trigger(vdev, i, -1, NULL);
245 + if (!IS_ERR(vdev->irqs[i].name)) {
246 + free_irq(vdev->irqs[i].hwirq, &vdev->irqs[i]);
247 + if (vdev->irqs[i].trigger)
248 + eventfd_ctx_put(vdev->irqs[i].trigger);
249 + kfree(vdev->irqs[i].name);
250 + }
251 }
252
253 vdev->num_irqs = 0;