Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

irqchip/gic-v3-its: Add VLPI configuration handling

When a VLPI is reconfigured (enabled, disabled, change in priority),
the full configuration byte must be written, and the caches invalidated.

Also, when using the irq_mask/irq_unmask methods, it is necessary
to disable the doorbell for that particular interrupt (by mapping it
to 1023) on top of clearing the Enable bit.

Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>

+70 -5
+70 -5
drivers/irqchip/irq-gic-v3-its.c
··· 812 return d->hwirq - its_dev->event_map.lpi_base; 813 } 814 815 - static void lpi_update_config(struct irq_data *d, u8 clr, u8 set) 816 { 817 - struct its_device *its_dev = irq_data_get_irq_chip_data(d); 818 - irq_hw_number_t hwirq = d->hwirq; 819 struct page *prop_page; 820 u8 *cfg; 821 822 - prop_page = gic_rdists->prop_page; 823 824 cfg = page_address(prop_page) + hwirq - 8192; 825 *cfg &= ~clr; 826 - *cfg |= set; 827 828 /* 829 * Make the above write visible to the redistributors. ··· 842 gic_flush_dcache_to_poc(cfg, sizeof(*cfg)); 843 else 844 dsb(ishst); 845 its_send_inv(its_dev, its_get_event_id(d)); 846 } 847 848 static void its_mask_irq(struct irq_data *d) 849 { 850 lpi_update_config(d, LPI_PROP_ENABLED, 0); 851 } 852 853 static void its_unmask_irq(struct irq_data *d) 854 { 855 lpi_update_config(d, 0, LPI_PROP_ENABLED); 856 } 857 ··· 899 struct its_device *its_dev = irq_data_get_irq_chip_data(d); 900 struct its_collection *target_col; 901 u32 id = its_get_event_id(d); 902 903 /* lpi cannot be routed to a redistributor that is on a foreign node */ 904 if (its_dev->its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) { ··· 1072 return ret; 1073 } 1074 1075 static int its_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu_info) 1076 { 1077 struct its_device *its_dev = irq_data_get_irq_chip_data(d); ··· 1110 1111 case PROP_UPDATE_VLPI: 1112 case PROP_UPDATE_AND_INV_VLPI: 1113 1114 default: 1115 return -EINVAL;
··· 812 return d->hwirq - its_dev->event_map.lpi_base; 813 } 814 815 + static void lpi_write_config(struct irq_data *d, u8 clr, u8 set) 816 { 817 + irq_hw_number_t hwirq; 818 struct page *prop_page; 819 u8 *cfg; 820 821 + if (irqd_is_forwarded_to_vcpu(d)) { 822 + struct its_device *its_dev = irq_data_get_irq_chip_data(d); 823 + u32 event = its_get_event_id(d); 824 + 825 + prop_page = its_dev->event_map.vm->vprop_page; 826 + hwirq = its_dev->event_map.vlpi_maps[event].vintid; 827 + } else { 828 + prop_page = gic_rdists->prop_page; 829 + hwirq = d->hwirq; 830 + } 831 832 cfg = page_address(prop_page) + hwirq - 8192; 833 *cfg &= ~clr; 834 + *cfg |= set | LPI_PROP_GROUP1; 835 836 /* 837 * Make the above write visible to the redistributors. ··· 834 gic_flush_dcache_to_poc(cfg, sizeof(*cfg)); 835 else 836 dsb(ishst); 837 + } 838 + 839 + static void lpi_update_config(struct irq_data *d, u8 clr, u8 set) 840 + { 841 + struct its_device *its_dev = irq_data_get_irq_chip_data(d); 842 + 843 + lpi_write_config(d, clr, set); 844 its_send_inv(its_dev, its_get_event_id(d)); 845 + } 846 + 847 + static void its_vlpi_set_doorbell(struct irq_data *d, bool enable) 848 + { 849 + struct its_device *its_dev = irq_data_get_irq_chip_data(d); 850 + u32 event = its_get_event_id(d); 851 + 852 + if (its_dev->event_map.vlpi_maps[event].db_enabled == enable) 853 + return; 854 + 855 + its_dev->event_map.vlpi_maps[event].db_enabled = enable; 856 + 857 + /* 858 + * More fun with the architecture: 859 + * 860 + * Ideally, we'd issue a VMAPTI to set the doorbell to its LPI 861 + * value or to 1023, depending on the enable bit. But that 862 + * would be issueing a mapping for an /existing/ DevID+EventID 863 + * pair, which is UNPREDICTABLE. Instead, let's issue a VMOVI 864 + * to the /same/ vPE, using this opportunity to adjust the 865 + * doorbell. Mouahahahaha. We loves it, Precious. 866 + */ 867 + its_send_vmovi(its_dev, event); 868 } 869 870 static void its_mask_irq(struct irq_data *d) 871 { 872 + if (irqd_is_forwarded_to_vcpu(d)) 873 + its_vlpi_set_doorbell(d, false); 874 + 875 lpi_update_config(d, LPI_PROP_ENABLED, 0); 876 } 877 878 static void its_unmask_irq(struct irq_data *d) 879 { 880 + if (irqd_is_forwarded_to_vcpu(d)) 881 + its_vlpi_set_doorbell(d, true); 882 + 883 lpi_update_config(d, 0, LPI_PROP_ENABLED); 884 } 885 ··· 855 struct its_device *its_dev = irq_data_get_irq_chip_data(d); 856 struct its_collection *target_col; 857 u32 id = its_get_event_id(d); 858 + 859 + /* A forwarded interrupt should use irq_set_vcpu_affinity */ 860 + if (irqd_is_forwarded_to_vcpu(d)) 861 + return -EINVAL; 862 863 /* lpi cannot be routed to a redistributor that is on a foreign node */ 864 if (its_dev->its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_23144) { ··· 1024 return ret; 1025 } 1026 1027 + static int its_vlpi_prop_update(struct irq_data *d, struct its_cmd_info *info) 1028 + { 1029 + struct its_device *its_dev = irq_data_get_irq_chip_data(d); 1030 + 1031 + if (!its_dev->event_map.vm || !irqd_is_forwarded_to_vcpu(d)) 1032 + return -EINVAL; 1033 + 1034 + if (info->cmd_type == PROP_UPDATE_AND_INV_VLPI) 1035 + lpi_update_config(d, 0xff, info->config); 1036 + else 1037 + lpi_write_config(d, 0xff, info->config); 1038 + its_vlpi_set_doorbell(d, !!(info->config & LPI_PROP_ENABLED)); 1039 + 1040 + return 0; 1041 + } 1042 + 1043 static int its_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu_info) 1044 { 1045 struct its_device *its_dev = irq_data_get_irq_chip_data(d); ··· 1046 1047 case PROP_UPDATE_VLPI: 1048 case PROP_UPDATE_AND_INV_VLPI: 1049 + return its_vlpi_prop_update(d, info); 1050 1051 default: 1052 return -EINVAL;