| .. | .. |
|---|
| 33 | 33 | #include <linux/slab.h> |
|---|
| 34 | 34 | #include <linux/irqnr.h> |
|---|
| 35 | 35 | #include <linux/pci.h> |
|---|
| 36 | +#include <linux/rcupdate.h> |
|---|
| 36 | 37 | #include <linux/spinlock.h> |
|---|
| 37 | 38 | #include <linux/cpuhotplug.h> |
|---|
| 38 | 39 | #include <linux/atomic.h> |
|---|
| .. | .. |
|---|
| 94 | 95 | struct irq_info { |
|---|
| 95 | 96 | struct list_head list; |
|---|
| 96 | 97 | struct list_head eoi_list; |
|---|
| 98 | + struct rcu_work rwork; |
|---|
| 97 | 99 | short refcnt; |
|---|
| 98 | 100 | short spurious_cnt; |
|---|
| 99 | 101 | short type; /* type */ |
|---|
| .. | .. |
|---|
| 142 | 144 | static DEFINE_MUTEX(irq_mapping_update_lock); |
|---|
| 143 | 145 | |
|---|
| 144 | 146 | /* |
|---|
| 145 | | - * Lock protecting event handling loop against removing event channels. |
|---|
| 146 | | - * Adding of event channels is no issue as the associated IRQ becomes active |
|---|
| 147 | | - * only after everything is setup (before request_[threaded_]irq() the handler |
|---|
| 148 | | - * can't be entered for an event, as the event channel will be unmasked only |
|---|
| 149 | | - * then). |
|---|
| 150 | | - */ |
|---|
| 151 | | -static DEFINE_RWLOCK(evtchn_rwlock); |
|---|
| 152 | | - |
|---|
| 153 | | -/* |
|---|
| 154 | 147 | * Lock hierarchy: |
|---|
| 155 | 148 | * |
|---|
| 156 | 149 | * irq_mapping_update_lock |
|---|
| 157 | | - * evtchn_rwlock |
|---|
| 158 | | - * IRQ-desc lock |
|---|
| 159 | | - * percpu eoi_list_lock |
|---|
| 160 | | - * irq_info->lock |
|---|
| 150 | + * IRQ-desc lock |
|---|
| 151 | + * percpu eoi_list_lock |
|---|
| 152 | + * irq_info->lock |
|---|
| 161 | 153 | */ |
|---|
| 162 | 154 | |
|---|
| 163 | 155 | static LIST_HEAD(xen_irq_list_head); |
|---|
| .. | .. |
|---|
| 270 | 262 | legacy_info_ptrs[irq] = info; |
|---|
| 271 | 263 | else |
|---|
| 272 | 264 | irq_set_chip_data(irq, info); |
|---|
| 265 | +} |
|---|
| 266 | + |
|---|
| 267 | +static void delayed_free_irq(struct work_struct *work) |
|---|
| 268 | +{ |
|---|
| 269 | + struct irq_info *info = container_of(to_rcu_work(work), struct irq_info, |
|---|
| 270 | + rwork); |
|---|
| 271 | + unsigned int irq = info->irq; |
|---|
| 272 | + |
|---|
| 273 | + /* Remove the info pointer only now, with no potential users left. */ |
|---|
| 274 | + set_info_for_irq(irq, NULL); |
|---|
| 275 | + |
|---|
| 276 | + kfree(info); |
|---|
| 277 | + |
|---|
| 278 | + /* Legacy IRQ descriptors are managed by the arch. */ |
|---|
| 279 | + if (irq >= nr_legacy_irqs()) |
|---|
| 280 | + irq_free_desc(irq); |
|---|
| 273 | 281 | } |
|---|
| 274 | 282 | |
|---|
| 275 | 283 | /* Constructors for packed IRQ information. */ |
|---|
| .. | .. |
|---|
| 606 | 614 | |
|---|
| 607 | 615 | eoi = container_of(to_delayed_work(work), struct lateeoi_work, delayed); |
|---|
| 608 | 616 | |
|---|
| 609 | | - read_lock_irqsave(&evtchn_rwlock, flags); |
|---|
| 617 | + rcu_read_lock(); |
|---|
| 610 | 618 | |
|---|
| 611 | 619 | while (true) { |
|---|
| 612 | | - spin_lock(&eoi->eoi_list_lock); |
|---|
| 620 | + spin_lock_irqsave(&eoi->eoi_list_lock, flags); |
|---|
| 613 | 621 | |
|---|
| 614 | 622 | info = list_first_entry_or_null(&eoi->eoi_list, struct irq_info, |
|---|
| 615 | 623 | eoi_list); |
|---|
| 616 | 624 | |
|---|
| 617 | | - if (info == NULL || now < info->eoi_time) { |
|---|
| 618 | | - spin_unlock(&eoi->eoi_list_lock); |
|---|
| 625 | + if (info == NULL) |
|---|
| 626 | + break; |
|---|
| 627 | + |
|---|
| 628 | + if (now < info->eoi_time) { |
|---|
| 629 | + mod_delayed_work_on(info->eoi_cpu, system_wq, |
|---|
| 630 | + &eoi->delayed, |
|---|
| 631 | + info->eoi_time - now); |
|---|
| 619 | 632 | break; |
|---|
| 620 | 633 | } |
|---|
| 621 | 634 | |
|---|
| 622 | 635 | list_del_init(&info->eoi_list); |
|---|
| 623 | 636 | |
|---|
| 624 | | - spin_unlock(&eoi->eoi_list_lock); |
|---|
| 637 | + spin_unlock_irqrestore(&eoi->eoi_list_lock, flags); |
|---|
| 625 | 638 | |
|---|
| 626 | 639 | info->eoi_time = 0; |
|---|
| 627 | 640 | |
|---|
| 628 | 641 | xen_irq_lateeoi_locked(info, false); |
|---|
| 629 | 642 | } |
|---|
| 630 | 643 | |
|---|
| 631 | | - if (info) |
|---|
| 632 | | - mod_delayed_work_on(info->eoi_cpu, system_wq, |
|---|
| 633 | | - &eoi->delayed, info->eoi_time - now); |
|---|
| 644 | + spin_unlock_irqrestore(&eoi->eoi_list_lock, flags); |
|---|
| 634 | 645 | |
|---|
| 635 | | - read_unlock_irqrestore(&evtchn_rwlock, flags); |
|---|
| 646 | + rcu_read_unlock(); |
|---|
| 636 | 647 | } |
|---|
| 637 | 648 | |
|---|
| 638 | 649 | static void xen_cpu_init_eoi(unsigned int cpu) |
|---|
| .. | .. |
|---|
| 647 | 658 | void xen_irq_lateeoi(unsigned int irq, unsigned int eoi_flags) |
|---|
| 648 | 659 | { |
|---|
| 649 | 660 | struct irq_info *info; |
|---|
| 650 | | - unsigned long flags; |
|---|
| 651 | 661 | |
|---|
| 652 | | - read_lock_irqsave(&evtchn_rwlock, flags); |
|---|
| 662 | + rcu_read_lock(); |
|---|
| 653 | 663 | |
|---|
| 654 | 664 | info = info_for_irq(irq); |
|---|
| 655 | 665 | |
|---|
| 656 | 666 | if (info) |
|---|
| 657 | 667 | xen_irq_lateeoi_locked(info, eoi_flags & XEN_EOI_FLAG_SPURIOUS); |
|---|
| 658 | 668 | |
|---|
| 659 | | - read_unlock_irqrestore(&evtchn_rwlock, flags); |
|---|
| 669 | + rcu_read_unlock(); |
|---|
| 660 | 670 | } |
|---|
| 661 | 671 | EXPORT_SYMBOL_GPL(xen_irq_lateeoi); |
|---|
| 662 | 672 | |
|---|
| .. | .. |
|---|
| 675 | 685 | |
|---|
| 676 | 686 | info->type = IRQT_UNBOUND; |
|---|
| 677 | 687 | info->refcnt = -1; |
|---|
| 688 | + INIT_RCU_WORK(&info->rwork, delayed_free_irq); |
|---|
| 678 | 689 | |
|---|
| 679 | 690 | set_info_for_irq(irq, info); |
|---|
| 680 | 691 | |
|---|
| .. | .. |
|---|
| 727 | 738 | static void xen_free_irq(unsigned irq) |
|---|
| 728 | 739 | { |
|---|
| 729 | 740 | struct irq_info *info = info_for_irq(irq); |
|---|
| 730 | | - unsigned long flags; |
|---|
| 731 | 741 | |
|---|
| 732 | 742 | if (WARN_ON(!info)) |
|---|
| 733 | 743 | return; |
|---|
| 734 | | - |
|---|
| 735 | | - write_lock_irqsave(&evtchn_rwlock, flags); |
|---|
| 736 | 744 | |
|---|
| 737 | 745 | if (!list_empty(&info->eoi_list)) |
|---|
| 738 | 746 | lateeoi_list_del(info); |
|---|
| 739 | 747 | |
|---|
| 740 | 748 | list_del(&info->list); |
|---|
| 741 | 749 | |
|---|
| 742 | | - set_info_for_irq(irq, NULL); |
|---|
| 743 | | - |
|---|
| 744 | 750 | WARN_ON(info->refcnt > 0); |
|---|
| 745 | 751 | |
|---|
| 746 | | - write_unlock_irqrestore(&evtchn_rwlock, flags); |
|---|
| 747 | | - |
|---|
| 748 | | - kfree(info); |
|---|
| 749 | | - |
|---|
| 750 | | - /* Legacy IRQ descriptors are managed by the arch. */ |
|---|
| 751 | | - if (irq < nr_legacy_irqs()) |
|---|
| 752 | | - return; |
|---|
| 753 | | - |
|---|
| 754 | | - irq_free_desc(irq); |
|---|
| 752 | + queue_rcu_work(system_wq, &info->rwork); |
|---|
| 755 | 753 | } |
|---|
| 756 | 754 | |
|---|
| 757 | 755 | static void xen_evtchn_close(evtchn_port_t port) |
|---|
| .. | .. |
|---|
| 1639 | 1637 | int cpu = smp_processor_id(); |
|---|
| 1640 | 1638 | struct evtchn_loop_ctrl ctrl = { 0 }; |
|---|
| 1641 | 1639 | |
|---|
| 1642 | | - read_lock(&evtchn_rwlock); |
|---|
| 1640 | + /* |
|---|
| 1641 | + * When closing an event channel the associated IRQ must not be freed |
|---|
| 1642 | + * until all cpus have left the event handling loop. This is ensured |
|---|
| 1643 | + * by taking the rcu_read_lock() while handling events, as freeing of |
|---|
| 1644 | + * the IRQ is handled via queue_rcu_work() _after_ closing the event |
|---|
| 1645 | + * channel. |
|---|
| 1646 | + */ |
|---|
| 1647 | + rcu_read_lock(); |
|---|
| 1643 | 1648 | |
|---|
| 1644 | 1649 | do { |
|---|
| 1645 | 1650 | vcpu_info->evtchn_upcall_pending = 0; |
|---|
| .. | .. |
|---|
| 1652 | 1657 | |
|---|
| 1653 | 1658 | } while (vcpu_info->evtchn_upcall_pending); |
|---|
| 1654 | 1659 | |
|---|
| 1655 | | - read_unlock(&evtchn_rwlock); |
|---|
| 1660 | + rcu_read_unlock(); |
|---|
| 1656 | 1661 | |
|---|
| 1657 | 1662 | /* |
|---|
| 1658 | 1663 | * Increment irq_epoch only now to defer EOIs only for |
|---|