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 | 473 | init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc); |
| 474 | 474 | |
| 475 | 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 | 489 | #if IS_ENABLED(CONFIG_X86) |
| 477 | 490 | if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) && |
| 478 | 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 | 494 | * Otherwise return with failure |
| 482 | 495 | */ |
| 483 | 496 | spin_unlock_irq(&pt->pt_irqfds_lock); |
| 497 | + srcu_read_unlock(&pt->pt_irq_srcu, idx); | |
| 484 | 498 | ret = -EINVAL; |
| 485 | 499 | goto fail; |
| 486 | 500 | } |
| 487 | 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 | 503 | hlist_add_head(&irqfd->irqfd_hnode, &pt->pt_irqfds_list); |
| 501 | 504 | spin_unlock_irq(&pt->pt_irqfds_lock); |
| 502 | 505 | |