.. | .. |
---|
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 |
---|