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.
Unified · Split
| @@ -473,6 +473,19 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, · 472 unmodified lines | |||
| 473 | init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc); | 473 | init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc); |
| 474 | 474 | ||
| 475 | spin_lock_irq(&pt->pt_irqfds_lock); | 475 | spin_lock_irq(&pt->pt_irqfds_lock); |
| 476 | ret = 0; | ||
| 477 | hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) { | ||
| 478 | if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx) | ||
| 479 | continue; | ||
| 480 | /* This fd is used for another irq already. */ | ||
| 481 | ret = -EBUSY; | ||
| 482 | spin_unlock_irq(&pt->pt_irqfds_lock); | ||
| 483 | goto fail; | ||
| 484 | } | ||
| 485 | |||
| 486 | idx = srcu_read_lock(&pt->pt_irq_srcu); | ||
| 487 | mshv_irqfd_update(pt, irqfd); | ||
| 488 | |||
| 476 | #if IS_ENABLED(CONFIG_X86) | 489 | #if IS_ENABLED(CONFIG_X86) |
| 477 | if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) && | 490 | if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) && |
| 478 | !irqfd->irqfd_lapic_irq.lapic_control.level_triggered) { | 491 | !irqfd->irqfd_lapic_irq.lapic_control.level_triggered) { |
| @@ -481,22 +494,12 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, · 2 unmodified lines | |||
| 481 | * Otherwise return with failure | 494 | * Otherwise return with failure |
| 482 | */ | 495 | */ |
| 483 | spin_unlock_irq(&pt->pt_irqfds_lock); | 496 | spin_unlock_irq(&pt->pt_irqfds_lock); |
| 497 | srcu_read_unlock(&pt->pt_irq_srcu, idx); | ||
| 484 | ret = -EINVAL; | 498 | ret = -EINVAL; |
| 485 | goto fail; | 499 | goto fail; |
| 486 | } | 500 | } |
| 487 | #endif | 501 | #endif |
| 488 | ret = 0; | ||
| 489 | hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) { | ||
| 490 | if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx) | ||
| 491 | continue; | ||
| 492 | /* This fd is used for another irq already. */ | ||
| 493 | ret = -EBUSY; | ||
| 494 | spin_unlock_irq(&pt->pt_irqfds_lock); | ||
| 495 | goto fail; | ||
| 496 | } | ||
| 497 | 502 | ||
| 498 | idx = srcu_read_lock(&pt->pt_irq_srcu); | ||
| 499 | mshv_irqfd_update(pt, irqfd); | ||
| 500 | hlist_add_head(&irqfd->irqfd_hnode, &pt->pt_irqfds_list); | 503 | hlist_add_head(&irqfd->irqfd_hnode, &pt->pt_irqfds_list); |
| 501 | spin_unlock_irq(&pt->pt_irqfds_lock); | 504 | spin_unlock_irq(&pt->pt_irqfds_lock); |
| 502 | 505 | ||