In mshv_irqfd_assign(), the level-triggered validation for resample
irqfds checks irqfd_lapic_irq.lapic_control.level_triggered before
mshv_irqfd_update() has populated the field. Since the irqfd struct is
zero-allocated, level_triggered is always 0 at that point, causing the
check to always reject resample irqfds with -EINVAL. This makes
level-triggered interrupt resampling — used to avoid interrupt storms
with assigned devices — completely non-functional.

Move the check after the mshv_irqfd_update() call, which resolves the
IRQ routing entry and populates irqfd_lapic_irq with the actual trigger
mode.

Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose 
/dev/mshv to VMMs")
Signed-off-by: Stanislav Kinsburskii <[email protected]>
---
 drivers/hv/mshv_eventfd.c |   25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c
index b92e7f05aa9cd..3165f787994fc 100644
--- a/drivers/hv/mshv_eventfd.c
+++ b/drivers/hv/mshv_eventfd.c
@@ -483,6 +483,19 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
        init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc);
 
        spin_lock_irq(&pt->pt_irqfds_lock);
+       ret = 0;
+       hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) {
+               if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx)
+                       continue;
+               /* This fd is used for another irq already. */
+               ret = -EBUSY;
+               spin_unlock_irq(&pt->pt_irqfds_lock);
+               goto fail;
+       }
+
+       idx = srcu_read_lock(&pt->pt_irq_srcu);
+       mshv_irqfd_update(pt, irqfd);
+
 #if IS_ENABLED(CONFIG_X86)
        if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) &&
            !irqfd->irqfd_lapic_irq.lapic_control.level_triggered) {
@@ -491,22 +504,12 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
                 * Otherwise return with failure
                 */
                spin_unlock_irq(&pt->pt_irqfds_lock);
+               srcu_read_unlock(&pt->pt_irq_srcu, idx);
                ret = -EINVAL;
                goto fail;
        }
 #endif
-       ret = 0;
-       hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) {
-               if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx)
-                       continue;
-               /* This fd is used for another irq already. */
-               ret = -EBUSY;
-               spin_unlock_irq(&pt->pt_irqfds_lock);
-               goto fail;
-       }
 
-       idx = srcu_read_lock(&pt->pt_irq_srcu);
-       mshv_irqfd_update(pt, irqfd);
        hlist_add_head(&irqfd->irqfd_hnode, &pt->pt_irqfds_list);
        spin_unlock_irq(&pt->pt_irqfds_lock);
 



Reply via email to