From fd19ce77993a49f3afc56bb4cae7eafb1ec69e0c Mon Sep 17 00:00:00 2001 From: Angus Chen Date: Fri, 7 Oct 2022 18:32:36 +0800 Subject: genirq: Remove unused argument force of irq_set_affinity_deactivated() The force parameter in irq_set_affinity_deactivated() is not used, get rid of it. Signed-off-by: Angus Chen Signed-off-by: Thomas Gleixner Link: https://lore.kernel.org/r/20221007103236.599-1-angus.chen@jaguarmicro.com --- kernel/irq/manage.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'kernel') diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 40fe7806cc8c..5b7cf28df290 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -321,7 +321,7 @@ static int irq_try_set_affinity(struct irq_data *data, } static bool irq_set_affinity_deactivated(struct irq_data *data, - const struct cpumask *mask, bool force) + const struct cpumask *mask) { struct irq_desc *desc = irq_data_to_desc(data); @@ -354,7 +354,7 @@ int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask, if (!chip || !chip->irq_set_affinity) return -EINVAL; - if (irq_set_affinity_deactivated(data, mask, force)) + if (irq_set_affinity_deactivated(data, mask)) return 0; if (irq_can_move_pcntxt(data) && !irqd_is_setaffinity_pending(data)) { -- cgit v1.2.3 From befd780253e774ea9388dd8dfad7c627a0aa7e02 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 11 Nov 2022 14:54:20 +0100 Subject: genirq/msi: Use MSI_DESC_ALL in msi_add_simple_msi_descs() There are no associated MSI descriptors in the requested range when the MSI descriptor allocation fails. Use MSI_DESC_ALL as the filter which prepares the next step to get rid of the filter for freeing. Signed-off-by: Thomas Gleixner Reviewed-by: Ashok Raj Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/20221111122013.831151822@linutronix.de --- kernel/irq/msi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'kernel') diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index a9ee535293eb..bba6359d8450 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -120,7 +120,7 @@ static int msi_add_simple_msi_descs(struct device *dev, unsigned int index, unsi fail_mem: ret = -ENOMEM; fail: - msi_free_msi_descs_range(dev, MSI_DESC_NOTASSOCIATED, index, last); + msi_free_msi_descs_range(dev, MSI_DESC_ALL, index, last); return ret; } -- cgit v1.2.3 From 2f2940d168236a92df524a1bd99fc7b0325918b5 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 11 Nov 2022 14:54:22 +0100 Subject: genirq/msi: Remove filter from msi_free_descs_free_range() When a range of descriptors is freed then all of them are not associated to a linux interrupt. Remove the filter and add a warning to the free function. Signed-off-by: Thomas Gleixner Reviewed-by: Ashok Raj Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/20221111122013.888850936@linutronix.de --- drivers/base/platform-msi.c | 2 +- include/linux/msi.h | 5 ++--- kernel/irq/msi.c | 19 ++++++++++--------- 3 files changed, 13 insertions(+), 13 deletions(-) (limited to 'kernel') diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c index 12b044151298..dddafa197693 100644 --- a/drivers/base/platform-msi.c +++ b/drivers/base/platform-msi.c @@ -325,7 +325,7 @@ void platform_msi_device_domain_free(struct irq_domain *domain, unsigned int vir msi_lock_descs(data->dev); irq_domain_free_irqs_common(domain, virq, nr_irqs); - msi_free_msi_descs_range(data->dev, MSI_DESC_ALL, virq, virq + nr_irqs - 1); + msi_free_msi_descs_range(data->dev, virq, virq + nr_irqs - 1); msi_unlock_descs(data->dev); } diff --git a/include/linux/msi.h b/include/linux/msi.h index fc918a658d48..969ce46369b3 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -247,8 +247,7 @@ static inline void pci_write_msi_msg(unsigned int irq, struct msi_msg *msg) #endif /* CONFIG_PCI_MSI */ int msi_add_msi_desc(struct device *dev, struct msi_desc *init_desc); -void msi_free_msi_descs_range(struct device *dev, enum msi_desc_filter filter, - unsigned int first_index, unsigned int last_index); +void msi_free_msi_descs_range(struct device *dev, unsigned int first_index, unsigned int last_index); /** * msi_free_msi_descs - Free MSI descriptors of a device @@ -256,7 +255,7 @@ void msi_free_msi_descs_range(struct device *dev, enum msi_desc_filter filter, */ static inline void msi_free_msi_descs(struct device *dev) { - msi_free_msi_descs_range(dev, MSI_DESC_ALL, 0, MSI_MAX_INDEX); + msi_free_msi_descs_range(dev, 0, MSI_MAX_INDEX); } void __pci_read_msi_msg(struct msi_desc *entry, struct msi_msg *msg); diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index bba6359d8450..1ca484698bda 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -120,7 +120,7 @@ static int msi_add_simple_msi_descs(struct device *dev, unsigned int index, unsi fail_mem: ret = -ENOMEM; fail: - msi_free_msi_descs_range(dev, MSI_DESC_ALL, index, last); + msi_free_msi_descs_range(dev, index, last); return ret; } @@ -141,12 +141,11 @@ static bool msi_desc_match(struct msi_desc *desc, enum msi_desc_filter filter) /** * msi_free_msi_descs_range - Free MSI descriptors of a device * @dev: Device to free the descriptors - * @filter: Descriptor state filter * @first_index: Index to start freeing from * @last_index: Last index to be freed */ -void msi_free_msi_descs_range(struct device *dev, enum msi_desc_filter filter, - unsigned int first_index, unsigned int last_index) +void msi_free_msi_descs_range(struct device *dev, unsigned int first_index, + unsigned int last_index) { struct xarray *xa = &dev->msi.data->__store; struct msi_desc *desc; @@ -155,10 +154,12 @@ void msi_free_msi_descs_range(struct device *dev, enum msi_desc_filter filter, lockdep_assert_held(&dev->msi.data->mutex); xa_for_each_range(xa, idx, desc, first_index, last_index) { - if (msi_desc_match(desc, filter)) { - xa_erase(xa, idx); - msi_free_desc(desc); - } + xa_erase(xa, idx); + + /* Leak the descriptor when it is still referenced */ + if (WARN_ON_ONCE(msi_desc_match(desc, MSI_DESC_ASSOCIATED))) + continue; + msi_free_desc(desc); } } @@ -739,7 +740,7 @@ int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev, fail: for (--virq; virq >= virq_base; virq--) irq_domain_free_irqs_common(domain, virq, 1); - msi_free_msi_descs_range(dev, MSI_DESC_ALL, virq_base, virq_base + nvec - 1); + msi_free_msi_descs_range(dev, virq_base, virq_base + nvec - 1); unlock: msi_unlock_descs(dev); return ret; -- cgit v1.2.3 From fdd5340411b25450612767270ac3cd0d8454183c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 11 Nov 2022 14:54:23 +0100 Subject: genirq/msi: Add missing kernel doc to msi_next_desc() W=1 complains about this. Signed-off-by: Thomas Gleixner Reviewed-by: Ashok Raj Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/20221111122013.947071142@linutronix.de --- kernel/irq/msi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'kernel') diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 1ca484698bda..8a6d0dcab7a2 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -282,6 +282,7 @@ EXPORT_SYMBOL_GPL(msi_first_desc); /** * msi_next_desc - Get the next MSI descriptor of a device * @dev: Device to operate on + * @filter: Descriptor state filter * * The first invocation of msi_next_desc() has to be preceeded by a * successful invocation of __msi_first_desc(). Consecutive invocations are -- cgit v1.2.3 From 762687ceb31fc296e2e1406559e8bb50251c5277 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 11 Nov 2022 14:54:25 +0100 Subject: genirq/msi: Make __msi_domain_alloc_irqs() static Nothing outside of the core code requires this. Signed-off-by: Thomas Gleixner Reviewed-by: Ashok Raj Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/20221111122014.004725919@linutronix.de --- include/linux/msi.h | 7 ++----- kernel/irq/msi.c | 6 ++++-- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index 969ce46369b3..9b552ee58a37 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -334,9 +334,8 @@ struct msi_domain_info; * MSI_FLAG_USE_DEF_DOM_OPS is not set to avoid breaking existing users and * because these callbacks are obviously mandatory. * - * This is NOT meant to be abused, but it can be useful to build wrappers - * for specialized MSI irq domains which need extra work before and after - * calling __msi_domain_alloc_irqs()/__msi_domain_free_irqs(). + * __msi_domain_free_irqs() is exposed for PPC pseries to handle extra + * work after all interrupts and descriptors have been freed. */ struct msi_domain_ops { irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info, @@ -425,8 +424,6 @@ int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask, struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode, struct msi_domain_info *info, struct irq_domain *parent); -int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, - int nvec); int msi_domain_alloc_irqs_descs_locked(struct irq_domain *domain, struct device *dev, int nvec); int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 8a6d0dcab7a2..3ccc7f68804b 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -462,6 +462,8 @@ static inline void msi_sysfs_remove_desc(struct device *dev, struct msi_desc *de #endif /* !CONFIG_SYSFS */ #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN +static int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, int nvec); + static inline void irq_chip_write_msi_msg(struct irq_data *data, struct msi_msg *msg) { @@ -852,8 +854,8 @@ static int msi_init_virq(struct irq_domain *domain, int virq, unsigned int vflag return 0; } -int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, - int nvec) +static int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, + int nvec) { struct msi_domain_info *info = domain->host_data; struct msi_domain_ops *ops = info->ops; -- cgit v1.2.3 From f6d3486a3d2f3c67d732641834eec872fcc66472 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 11 Nov 2022 14:54:27 +0100 Subject: genirq/msi: Provide msi_domain_ops:: Post_free() To prepare for removing the exposure of __msi_domain_free_irqs() provide a post_free() callback in the MSI domain ops which can be used to solve the problem of the only user of __msi_domain_free_irqs() in arch/powerpc. Signed-off-by: Thomas Gleixner Reviewed-by: Ashok Raj Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/20221111122014.063153448@linutronix.de --- include/linux/msi.h | 4 ++++ kernel/irq/msi.c | 2 ++ 2 files changed, 6 insertions(+) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index 9b552ee58a37..7f6aba9d6b4e 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -315,6 +315,8 @@ struct msi_domain_info; * function. * @domain_free_irqs: Optional function to override the default free * function. + * @msi_post_free: Optional function which is invoked after freeing + * all interrupts. * * @get_hwirq, @msi_init and @msi_free are callbacks used by the underlying * irqdomain. @@ -359,6 +361,8 @@ struct msi_domain_ops { struct device *dev, int nvec); void (*domain_free_irqs)(struct irq_domain *domain, struct device *dev); + void (*msi_post_free)(struct irq_domain *domain, + struct device *dev); }; /** diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 3ccc7f68804b..c71c37d3fa34 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -1026,6 +1026,8 @@ void msi_domain_free_irqs_descs_locked(struct irq_domain *domain, struct device lockdep_assert_held(&dev->msi.data->mutex); ops->domain_free_irqs(domain, dev); + if (ops->msi_post_free) + ops->msi_post_free(domain, dev); msi_domain_free_msi_descs(info, dev); } -- cgit v1.2.3 From 057c97a1cd665ebb38841418adcd35f57b33cc21 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 11 Nov 2022 14:54:30 +0100 Subject: genirq/msi: Make __msi_domain_free_irqs() static Now that the last user is gone, confine it to the core code. Signed-off-by: Thomas Gleixner Reviewed-by: Ashok Raj Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/20221111122014.179595843@linutronix.de --- include/linux/msi.h | 4 ---- kernel/irq/msi.c | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index 7f6aba9d6b4e..ee735ffeabd4 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -335,9 +335,6 @@ struct msi_domain_info; * are set to the default implementation if NULL and even when * MSI_FLAG_USE_DEF_DOM_OPS is not set to avoid breaking existing users and * because these callbacks are obviously mandatory. - * - * __msi_domain_free_irqs() is exposed for PPC pseries to handle extra - * work after all interrupts and descriptors have been freed. */ struct msi_domain_ops { irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info, @@ -432,7 +429,6 @@ int msi_domain_alloc_irqs_descs_locked(struct irq_domain *domain, struct device int nvec); int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, int nvec); -void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev); void msi_domain_free_irqs_descs_locked(struct irq_domain *domain, struct device *dev); void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev); struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain); diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index c71c37d3fa34..a2efa006752e 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -463,6 +463,7 @@ static inline void msi_sysfs_remove_desc(struct device *dev, struct msi_desc *de #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN static int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, int nvec); +static void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev); static inline void irq_chip_write_msi_msg(struct irq_data *data, struct msi_msg *msg) @@ -978,7 +979,7 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, int nve return ret; } -void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev) +static void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev) { struct msi_domain_info *info = domain->host_data; struct irq_data *irqd; -- cgit v1.2.3 From 22db089a4437a72277677f99717af499560b13f2 Mon Sep 17 00:00:00 2001 From: "Ahmed S. Darwish" Date: Fri, 11 Nov 2022 14:54:33 +0100 Subject: genirq/msi: Add bus token to struct msi_domain_info Add a bus token member to struct msi_domain_info and let msi_create_irq_domain() set the bus token. That allows to remove the bus token updates at the call sites. Suggested-by: Thomas Gleixner Signed-off-by: Ahmed S. Darwish Signed-off-by: Thomas Gleixner Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/20221111122014.294554462@linutronix.de --- include/linux/msi.h | 19 +++++++++++-------- kernel/irq/msi.c | 7 +++++-- 2 files changed, 16 insertions(+), 10 deletions(-) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index ee735ffeabd4..2dfd7b2bdfbe 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -16,6 +16,7 @@ * abuse. The only function which is relevant for drivers is msi_get_virq(). */ +#include #include #include #include @@ -365,6 +366,7 @@ struct msi_domain_ops { /** * struct msi_domain_info - MSI interrupt domain data * @flags: Flags to decribe features and capabilities + * @bus_token: The domain bus token * @ops: The callback data structure * @chip: Optional: associated interrupt chip * @chip_data: Optional: associated interrupt chip data @@ -374,14 +376,15 @@ struct msi_domain_ops { * @data: Optional: domain specific data */ struct msi_domain_info { - u32 flags; - struct msi_domain_ops *ops; - struct irq_chip *chip; - void *chip_data; - irq_flow_handler_t handler; - void *handler_data; - const char *handler_name; - void *data; + u32 flags; + enum irq_domain_bus_token bus_token; + struct msi_domain_ops *ops; + struct irq_chip *chip; + void *chip_data; + irq_flow_handler_t handler; + void *handler_data; + const char *handler_name; + void *data; }; /* Flags for msi_domain_info */ diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index a2efa006752e..b46b74788087 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -694,8 +694,11 @@ struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode, domain = irq_domain_create_hierarchy(parent, IRQ_DOMAIN_FLAG_MSI, 0, fwnode, &msi_domain_ops, info); - if (domain && !domain->name && info->chip) - domain->name = info->chip->name; + if (domain) { + if (!domain->name && info->chip) + domain->name = info->chip->name; + irq_domain_update_bus_token(domain, info->bus_token); + } return domain; } -- cgit v1.2.3 From 13e7accb81d6c07993385af8342238ff22b41ac8 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 11 Nov 2022 14:54:40 +0100 Subject: genirq: Get rid of GENERIC_MSI_IRQ_DOMAIN Adjust to reality and remove another layer of pointless Kconfig indirection. CONFIG_GENERIC_MSI_IRQ is good enough to serve all purposes. Signed-off-by: Thomas Gleixner Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/20221111122014.524842979@linutronix.de --- drivers/base/Makefile | 2 +- drivers/bus/fsl-mc/Kconfig | 2 +- drivers/dma/Kconfig | 2 +- drivers/dma/qcom/hidma.c | 8 ++++---- drivers/iommu/Kconfig | 2 +- drivers/irqchip/Kconfig | 6 +++--- drivers/mailbox/Kconfig | 2 +- drivers/pci/Kconfig | 1 - drivers/perf/Kconfig | 2 +- drivers/soc/ti/Kconfig | 2 +- include/asm-generic/msi.h | 4 ++-- include/linux/device.h | 8 +++----- include/linux/gpio/driver.h | 2 +- include/linux/msi.h | 8 +++----- kernel/irq/Kconfig | 7 +------ kernel/irq/msi.c | 3 --- 16 files changed, 24 insertions(+), 37 deletions(-) (limited to 'kernel') diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 83217d243c25..3079bfe53d04 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -22,7 +22,7 @@ obj-$(CONFIG_REGMAP) += regmap/ obj-$(CONFIG_SOC_BUS) += soc.o obj-$(CONFIG_PINCTRL) += pinctrl.o obj-$(CONFIG_DEV_COREDUMP) += devcoredump.o -obj-$(CONFIG_GENERIC_MSI_IRQ_DOMAIN) += platform-msi.o +obj-$(CONFIG_GENERIC_MSI_IRQ) += platform-msi.o obj-$(CONFIG_GENERIC_ARCH_TOPOLOGY) += arch_topology.o obj-$(CONFIG_GENERIC_ARCH_NUMA) += arch_numa.o obj-$(CONFIG_ACPI) += physical_location.o diff --git a/drivers/bus/fsl-mc/Kconfig b/drivers/bus/fsl-mc/Kconfig index b1fd55901c50..9492342e7d13 100644 --- a/drivers/bus/fsl-mc/Kconfig +++ b/drivers/bus/fsl-mc/Kconfig @@ -8,7 +8,7 @@ config FSL_MC_BUS bool "QorIQ DPAA2 fsl-mc bus driver" depends on OF && (ARCH_LAYERSCAPE || (COMPILE_TEST && (ARM || ARM64 || X86_LOCAL_APIC || PPC))) - select GENERIC_MSI_IRQ_DOMAIN + select GENERIC_MSI_IRQ help Driver to enable the bus infrastructure for the QorIQ DPAA2 architecture. The fsl-mc bus driver handles discovery of diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 7524b62a8870..25e111ab21f8 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -462,7 +462,7 @@ config MV_XOR_V2 select DMA_ENGINE select DMA_ENGINE_RAID select ASYNC_TX_ENABLE_CHANNEL_SWITCH - select GENERIC_MSI_IRQ_DOMAIN + select GENERIC_MSI_IRQ help Enable support for the Marvell version 2 XOR engine. diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c index 210f1a9eb441..04d1c33afc12 100644 --- a/drivers/dma/qcom/hidma.c +++ b/drivers/dma/qcom/hidma.c @@ -610,7 +610,7 @@ static irqreturn_t hidma_chirq_handler(int chirq, void *arg) return hidma_ll_inthandler(chirq, lldev); } -#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN +#ifdef CONFIG_GENERIC_MSI_IRQ static irqreturn_t hidma_chirq_handler_msi(int chirq, void *arg) { struct hidma_lldev **lldevp = arg; @@ -671,7 +671,7 @@ static int hidma_sysfs_init(struct hidma_dev *dev) return device_create_file(dev->ddev.dev, dev->chid_attrs); } -#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN +#ifdef CONFIG_GENERIC_MSI_IRQ static void hidma_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) { struct device *dev = msi_desc_to_dev(desc); @@ -687,7 +687,7 @@ static void hidma_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) static void hidma_free_msis(struct hidma_dev *dmadev) { -#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN +#ifdef CONFIG_GENERIC_MSI_IRQ struct device *dev = dmadev->ddev.dev; int i, virq; @@ -704,7 +704,7 @@ static void hidma_free_msis(struct hidma_dev *dmadev) static int hidma_request_msi(struct hidma_dev *dmadev, struct platform_device *pdev) { -#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN +#ifdef CONFIG_GENERIC_MSI_IRQ int rc, i, virq; rc = platform_msi_domain_alloc_irqs(&pdev->dev, HIDMA_MSI_INTS, diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index dc5f7a156ff5..8eaf9b72a995 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -389,7 +389,7 @@ config ARM_SMMU_V3 depends on ARM64 select IOMMU_API select IOMMU_IO_PGTABLE_LPAE - select GENERIC_MSI_IRQ_DOMAIN + select GENERIC_MSI_IRQ help Support for implementations of the ARM System MMU architecture version 3 providing translation support to a PCIe root complex. diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 7ef9f5e696d3..ffea6a8ccece 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -38,7 +38,7 @@ config ARM_GIC_V3 config ARM_GIC_V3_ITS bool - select GENERIC_MSI_IRQ_DOMAIN + select GENERIC_MSI_IRQ default ARM_GIC_V3 config ARM_GIC_V3_ITS_PCI @@ -375,7 +375,7 @@ config MVEBU_ICU config MVEBU_ODMI bool - select GENERIC_MSI_IRQ_DOMAIN + select GENERIC_MSI_IRQ config MVEBU_PIC bool @@ -488,7 +488,7 @@ config IMX_MU_MSI default m if ARCH_MXC select IRQ_DOMAIN select IRQ_DOMAIN_HIERARCHY - select GENERIC_MSI_IRQ_DOMAIN + select GENERIC_MSI_IRQ help Provide a driver for the i.MX Messaging Unit block used as a CPU-to-CPU MSI controller. This requires a specially crafted DT diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 05d6fae800e3..d7af896cbd7b 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -223,7 +223,7 @@ config BCM_FLEXRM_MBOX tristate "Broadcom FlexRM Mailbox" depends on ARM64 depends on ARCH_BCM_IPROC || COMPILE_TEST - select GENERIC_MSI_IRQ_DOMAIN + select GENERIC_MSI_IRQ default m if ARCH_BCM_IPROC help Mailbox implementation of the Broadcom FlexRM ring manager, diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index b7f62214668a..9309f2469b41 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -38,7 +38,6 @@ source "drivers/pci/pcie/Kconfig" config PCI_MSI bool "Message Signaled Interrupts (MSI and MSI-X)" - select GENERIC_MSI_IRQ_DOMAIN select GENERIC_MSI_IRQ help This allows device drivers to enable MSI (Message Signaled diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig index 341010f20b77..692ffd56f5cc 100644 --- a/drivers/perf/Kconfig +++ b/drivers/perf/Kconfig @@ -93,7 +93,7 @@ config ARM_PMU_ACPI config ARM_SMMU_V3_PMU tristate "ARM SMMUv3 Performance Monitors Extension" depends on (ARM64 && ACPI) || (COMPILE_TEST && 64BIT) - depends on GENERIC_MSI_IRQ_DOMAIN + depends on GENERIC_MSI_IRQ help Provides support for the ARM SMMUv3 Performance Monitor Counter Groups (PMCG), which provide monitoring of transactions passing diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig index 7e2fb1c16af1..e9a597e4bdc8 100644 --- a/drivers/soc/ti/Kconfig +++ b/drivers/soc/ti/Kconfig @@ -98,6 +98,6 @@ endif # SOC_TI config TI_SCI_INTA_MSI_DOMAIN bool - select GENERIC_MSI_IRQ_DOMAIN + select GENERIC_MSI_IRQ help Driver to enable Interrupt Aggregator specific MSI Domain. diff --git a/include/asm-generic/msi.h b/include/asm-generic/msi.h index bf910d47e900..124c734ca5d9 100644 --- a/include/asm-generic/msi.h +++ b/include/asm-generic/msi.h @@ -4,7 +4,7 @@ #include -#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN +#ifdef CONFIG_GENERIC_MSI_IRQ #ifndef NUM_MSI_ALLOC_SCRATCHPAD_REGS # define NUM_MSI_ALLOC_SCRATCHPAD_REGS 2 @@ -36,6 +36,6 @@ typedef struct msi_alloc_info { #define GENERIC_MSI_DOMAIN_OPS 1 -#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */ +#endif /* CONFIG_GENERIC_MSI_IRQ */ #endif diff --git a/include/linux/device.h b/include/linux/device.h index 424b55df0272..c90a444be1c4 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -378,10 +378,8 @@ struct dev_links_info { * @data: Pointer to MSI device data */ struct dev_msi_info { -#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN - struct irq_domain *domain; -#endif #ifdef CONFIG_GENERIC_MSI_IRQ + struct irq_domain *domain; struct msi_device_data *data; #endif }; @@ -742,7 +740,7 @@ static inline void set_dev_node(struct device *dev, int node) static inline struct irq_domain *dev_get_msi_domain(const struct device *dev) { -#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN +#ifdef CONFIG_GENERIC_MSI_IRQ return dev->msi.domain; #else return NULL; @@ -751,7 +749,7 @@ static inline struct irq_domain *dev_get_msi_domain(const struct device *dev) static inline void dev_set_msi_domain(struct device *dev, struct irq_domain *d) { -#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN +#ifdef CONFIG_GENERIC_MSI_IRQ dev->msi.domain = d; #endif } diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 6aeea1071b1b..88ae4513abb5 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -27,7 +27,7 @@ struct gpio_chip; union gpio_irq_fwspec { struct irq_fwspec fwspec; -#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN +#ifdef CONFIG_GENERIC_MSI_IRQ msi_alloc_info_t msiinfo; #endif }; diff --git a/include/linux/msi.h b/include/linux/msi.h index 9b8414523916..8b287148e512 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -79,9 +79,7 @@ void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg); #ifdef CONFIG_GENERIC_MSI_IRQ void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg); #else -static inline void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg) -{ -} +static inline void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg) { } #endif typedef void (*irq_write_msi_msg_t)(struct msi_desc *desc, @@ -278,7 +276,7 @@ static inline void msi_device_destroy_sysfs(struct device *dev) { } */ bool arch_restore_msi_irqs(struct pci_dev *dev); -#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN +#ifdef CONFIG_GENERIC_MSI_IRQ #include @@ -451,7 +449,7 @@ int platform_msi_device_domain_alloc(struct irq_domain *domain, unsigned int vir void platform_msi_device_domain_free(struct irq_domain *domain, unsigned int virq, unsigned int nvec); void *platform_msi_get_host_data(struct irq_domain *domain); -#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */ +#endif /* CONFIG_GENERIC_MSI_IRQ */ /* PCI specific interfaces */ #ifdef CONFIG_PCI_MSI diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index db3d174c53d4..b64c44ae4c25 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -86,15 +86,10 @@ config GENERIC_IRQ_IPI depends on SMP select IRQ_DOMAIN_HIERARCHY -# Generic MSI interrupt support -config GENERIC_MSI_IRQ - bool - # Generic MSI hierarchical interrupt domain support -config GENERIC_MSI_IRQ_DOMAIN +config GENERIC_MSI_IRQ bool select IRQ_DOMAIN_HIERARCHY - select GENERIC_MSI_IRQ config IRQ_MSI_IOMMU bool diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index b46b74788087..4fde91703c9b 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -461,7 +461,6 @@ static inline int msi_sysfs_populate_desc(struct device *dev, struct msi_desc *d static inline void msi_sysfs_remove_desc(struct device *dev, struct msi_desc *desc) { } #endif /* !CONFIG_SYSFS */ -#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN static int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, int nvec); static void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev); @@ -1058,5 +1057,3 @@ struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain) { return (struct msi_domain_info *)domain->host_data; } - -#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */ -- cgit v1.2.3 From 2569f62ca473584a8ba389f455fc00805389f7da Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 11 Nov 2022 14:55:15 +0100 Subject: genirq/msi: Remove msi_domain_ops:: Msi_check() No more users. Signed-off-by: Thomas Gleixner Reviewed-by: Jason Gunthorpe Link: https://lore.kernel.org/r/20221111122015.807616900@linutronix.de --- include/linux/msi.h | 4 ---- kernel/irq/msi.c | 17 +---------------- 2 files changed, 1 insertion(+), 20 deletions(-) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index 8b287148e512..1ce9d5e0bfa3 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -292,7 +292,6 @@ struct msi_domain_info; * @get_hwirq: Retrieve the resulting hw irq number * @msi_init: Domain specific init function for MSI interrupts * @msi_free: Domain specific function to free a MSI interrupts - * @msi_check: Callback for verification of the domain/info/dev data * @msi_prepare: Prepare the allocation of the interrupts in the domain * @set_desc: Set the msi descriptor for an interrupt * @domain_alloc_irqs: Optional function to override the default allocation @@ -330,9 +329,6 @@ struct msi_domain_ops { void (*msi_free)(struct irq_domain *domain, struct msi_domain_info *info, unsigned int virq); - int (*msi_check)(struct irq_domain *domain, - struct msi_domain_info *info, - struct device *dev); int (*msi_prepare)(struct irq_domain *domain, struct device *dev, int nvec, msi_alloc_info_t *arg); diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 4fde91703c9b..4b99f37355d5 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -617,17 +617,9 @@ static int msi_domain_ops_init(struct irq_domain *domain, return 0; } -static int msi_domain_ops_check(struct irq_domain *domain, - struct msi_domain_info *info, - struct device *dev) -{ - return 0; -} - static struct msi_domain_ops msi_domain_ops_default = { .get_hwirq = msi_domain_ops_get_hwirq, .msi_init = msi_domain_ops_init, - .msi_check = msi_domain_ops_check, .msi_prepare = msi_domain_ops_prepare, .set_desc = msi_domain_ops_set_desc, .domain_alloc_irqs = __msi_domain_alloc_irqs, @@ -655,8 +647,6 @@ static void msi_domain_update_dom_ops(struct msi_domain_info *info) ops->get_hwirq = msi_domain_ops_default.get_hwirq; if (ops->msi_init == NULL) ops->msi_init = msi_domain_ops_default.msi_init; - if (ops->msi_check == NULL) - ops->msi_check = msi_domain_ops_default.msi_check; if (ops->msi_prepare == NULL) ops->msi_prepare = msi_domain_ops_default.msi_prepare; if (ops->set_desc == NULL) @@ -707,13 +697,8 @@ int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev, { struct msi_domain_info *info = domain->host_data; struct msi_domain_ops *ops = info->ops; - int ret; - - ret = ops->msi_check(domain, info, dev); - if (ret == 0) - ret = ops->msi_prepare(domain, dev, nvec, arg); - return ret; + return ops->msi_prepare(domain, dev, nvec, arg); } int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev, -- cgit v1.2.3 From 9049e1ca41983ab773d7ea244bee86d7835ec9f5 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Mon, 28 Nov 2022 23:16:12 +0800 Subject: genirq/irqdesc: Don't try to remove non-existing sysfs files Fault injection tests trigger warnings like this: kernfs: can not remove 'chip_name', no directory WARNING: CPU: 0 PID: 253 at fs/kernfs/dir.c:1616 kernfs_remove_by_name_ns+0xce/0xe0 RIP: 0010:kernfs_remove_by_name_ns+0xce/0xe0 Call Trace: remove_files.isra.1+0x3f/0xb0 sysfs_remove_group+0x68/0xe0 sysfs_remove_groups+0x41/0x70 __kobject_del+0x45/0xc0 kobject_del+0x29/0x40 free_desc+0x42/0x70 irq_free_descs+0x5e/0x90 The reason is that the interrupt descriptor sysfs handling does not roll back on a failing kobject_add() during allocation. If the descriptor is freed later on, kobject_del() is invoked with a not added kobject resulting in the above warnings. A proper rollback in case of a kobject_add() failure would be the straight forward solution. But this is not possible due to the way how interrupt descriptor sysfs handling works. Interrupt descriptors are allocated before sysfs becomes available. So the sysfs files for the early allocated descriptors are added later in the boot process. At this point there can be nothing useful done about a failing kobject_add(). For consistency the interrupt descriptor allocation always treats kobject_add() failures as non-critical and just emits a warning. To solve this problem, keep track in the interrupt descriptor whether kobject_add() was successful or not and make the invocation of kobject_del() conditional on that. [ tglx: Massage changelog, comments and use a state bit. ] Fixes: ecb3f394c5db ("genirq: Expose interrupt information through sysfs") Signed-off-by: Yang Yingliang Signed-off-by: Thomas Gleixner Reviewed-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20221128151612.1786122-1-yangyingliang@huawei.com --- kernel/irq/internals.h | 2 ++ kernel/irq/irqdesc.c | 15 +++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'kernel') diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index f09c60393e55..5fdc0b557579 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -52,6 +52,7 @@ enum { * IRQS_PENDING - irq is pending and replayed later * IRQS_SUSPENDED - irq is suspended * IRQS_NMI - irq line is used to deliver NMIs + * IRQS_SYSFS - descriptor has been added to sysfs */ enum { IRQS_AUTODETECT = 0x00000001, @@ -64,6 +65,7 @@ enum { IRQS_SUSPENDED = 0x00000800, IRQS_TIMINGS = 0x00001000, IRQS_NMI = 0x00002000, + IRQS_SYSFS = 0x00004000, }; #include "debug.h" diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index a91f9001103c..fd0996274401 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -288,22 +288,25 @@ static void irq_sysfs_add(int irq, struct irq_desc *desc) if (irq_kobj_base) { /* * Continue even in case of failure as this is nothing - * crucial. + * crucial and failures in the late irq_sysfs_init() + * cannot be rolled back. */ if (kobject_add(&desc->kobj, irq_kobj_base, "%d", irq)) pr_warn("Failed to add kobject for irq %d\n", irq); + else + desc->istate |= IRQS_SYSFS; } } static void irq_sysfs_del(struct irq_desc *desc) { /* - * If irq_sysfs_init() has not yet been invoked (early boot), then - * irq_kobj_base is NULL and the descriptor was never added. - * kobject_del() complains about a object with no parent, so make - * it conditional. + * Only invoke kobject_del() when kobject_add() was successfully + * invoked for the descriptor. This covers both early boot, where + * sysfs is not initialized yet, and the case of a failed + * kobject_add() invocation. */ - if (irq_kobj_base) + if (desc->istate & IRQS_SYSFS) kobject_del(&desc->kobj); } -- cgit v1.2.3 From 3dad5f9ad99b77dfd234d31e2ea3d77f620efc09 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:24:08 +0100 Subject: genirq/msi: Move IRQ_DOMAIN_MSI_NOMASK_QUIRK to MSI flags It's truly a MSI only flag and for the upcoming per device MSI domains this must be in the MSI flags so it can be set during domain setup without exposing this quirk outside of x86. Signed-off-by: Thomas Gleixner Reviewed-by: Jason Gunthorpe Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124230313.454246167@linutronix.de --- arch/x86/kernel/apic/msi.c | 5 ++--- include/linux/irqdomain.h | 9 +-------- include/linux/msi.h | 6 ++++++ kernel/irq/msi.c | 2 +- 4 files changed, 10 insertions(+), 12 deletions(-) (limited to 'kernel') diff --git a/arch/x86/kernel/apic/msi.c b/arch/x86/kernel/apic/msi.c index 248a6a5c0ad8..71c87513327e 100644 --- a/arch/x86/kernel/apic/msi.c +++ b/arch/x86/kernel/apic/msi.c @@ -176,7 +176,8 @@ static struct msi_domain_ops pci_msi_domain_ops = { static struct msi_domain_info pci_msi_domain_info = { .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_PCI_MSIX, + MSI_FLAG_PCI_MSIX | MSI_FLAG_NOMASK_QUIRK, + .ops = &pci_msi_domain_ops, .chip = &pci_msi_controller, .handler = handle_edge_irq, @@ -200,8 +201,6 @@ struct irq_domain * __init native_create_pci_msi_domain(void) if (!d) { irq_domain_free_fwnode(fn); pr_warn("Failed to initialize PCI-MSI irqdomain.\n"); - } else { - d->flags |= IRQ_DOMAIN_MSI_NOMASK_QUIRK; } return d; } diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index a4af7f81661b..c42c36947f95 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -186,15 +186,8 @@ enum { /* Irq domain implements MSI remapping */ IRQ_DOMAIN_FLAG_MSI_REMAP = (1 << 5), - /* - * Quirk to handle MSI implementations which do not provide - * masking. Currently known to affect x86, but partially - * handled in core code. - */ - IRQ_DOMAIN_MSI_NOMASK_QUIRK = (1 << 6), - /* Irq domain doesn't translate anything */ - IRQ_DOMAIN_FLAG_NO_MAP = (1 << 7), + IRQ_DOMAIN_FLAG_NO_MAP = (1 << 6), /* * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved diff --git a/include/linux/msi.h b/include/linux/msi.h index 1ce9d5e0bfa3..2d87e000bd45 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -399,6 +399,12 @@ enum { MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS = (1 << 9), /* Free MSI descriptors */ MSI_FLAG_FREE_MSI_DESCS = (1 << 10), + /* + * Quirk to handle MSI implementations which do not provide + * masking. Currently known to affect x86, but has to be partially + * handled in the core MSI code. + */ + MSI_FLAG_NOMASK_QUIRK = (1 << 11), }; int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask, diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 4b99f37355d5..c37c0be4d646 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -875,7 +875,7 @@ static int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev * MSI affinity setting requires a special quirk (X86) when * reservation mode is active. */ - if (domain->flags & IRQ_DOMAIN_MSI_NOMASK_QUIRK) + if (info->flags & MSI_FLAG_NOMASK_QUIRK) vflags |= VIRQ_NOMASK_QUIRK; } -- cgit v1.2.3 From 6a9fc4190ca23fcf327de666e1a98dbdcdd2d210 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:24:12 +0100 Subject: genirq/irqdomain: Rename irq_domain::dev to irq_domain:: Pm_dev irq_domain::dev is a misnomer as it's usually the rule that a device pointer points to something which is directly related to the instance. irq_domain::dev can point to some other device for power management to ensure that this underlying device is not powered down when an interrupt is allocated. The upcoming per device MSI domains really require a pointer to the device which instantiated the irq domain and not to some random other device which is required for power management down the chain. Rename irq_domain::dev to irq_domain::pm_dev and fixup the few sites which use that pointer. Conversion was done with the help of coccinelle. Signed-off-by: Thomas Gleixner Reviewed-by: Jason Gunthorpe Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124230313.574541683@linutronix.de --- drivers/irqchip/irq-gic.c | 4 ++-- include/linux/irqdomain.h | 6 +++--- kernel/irq/chip.c | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'kernel') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 4c7bae0ec8f9..08834e5ac19c 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -401,8 +401,8 @@ static void gic_irq_print_chip(struct irq_data *d, struct seq_file *p) { struct gic_chip_data *gic = irq_data_get_irq_chip_data(d); - if (gic->domain->dev) - seq_printf(p, gic->domain->dev->of_node->name); + if (gic->domain->pm_dev) + seq_printf(p, gic->domain->pm_dev->of_node->name); else seq_printf(p, "GIC-%d", (int)(gic - &gic_data[0])); } diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index b1fdd8d309cf..aa76da82fa36 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -131,7 +131,7 @@ struct irq_domain_chip_generic; * @gc: Pointer to a list of generic chips. There is a helper function for * setting up one or more generic chips for interrupt controllers * drivers using the generic chip library which uses this pointer. - * @dev: Pointer to a device that can be utilized for power management + * @pm_dev: Pointer to a device that can be utilized for power management * purposes related to the irq domain. * @parent: Pointer to parent irq_domain to support hierarchy irq_domains * @@ -153,7 +153,7 @@ struct irq_domain { struct fwnode_handle *fwnode; enum irq_domain_bus_token bus_token; struct irq_domain_chip_generic *gc; - struct device *dev; + struct device *pm_dev; #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY struct irq_domain *parent; #endif @@ -206,7 +206,7 @@ static inline void irq_domain_set_pm_device(struct irq_domain *d, struct device *dev) { if (d) - d->dev = dev; + d->pm_dev = dev; } #ifdef CONFIG_IRQ_DOMAIN diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 8ac37e8e738a..49e7bc871fec 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -1561,10 +1561,10 @@ int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) return 0; } -static struct device *irq_get_parent_device(struct irq_data *data) +static struct device *irq_get_pm_device(struct irq_data *data) { if (data->domain) - return data->domain->dev; + return data->domain->pm_dev; return NULL; } @@ -1578,7 +1578,7 @@ static struct device *irq_get_parent_device(struct irq_data *data) */ int irq_chip_pm_get(struct irq_data *data) { - struct device *dev = irq_get_parent_device(data); + struct device *dev = irq_get_pm_device(data); int retval = 0; if (IS_ENABLED(CONFIG_PM) && dev) @@ -1597,7 +1597,7 @@ int irq_chip_pm_get(struct irq_data *data) */ int irq_chip_pm_put(struct irq_data *data) { - struct device *dev = irq_get_parent_device(data); + struct device *dev = irq_get_pm_device(data); int retval = 0; if (IS_ENABLED(CONFIG_PM) && dev) -- cgit v1.2.3 From 3e86a3a3ed031dd498e614db0fa082a58d700df9 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:24:19 +0100 Subject: genirq/msi: Check for invalid MSI parent domain usage In the upcoming per device MSI domain concept the MSI parent domains are not allowed to be used as regular MSI domains where the MSI allocation/free operations are applicable. Add appropriate checks. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124230313.806128070@linutronix.de --- kernel/irq/msi.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'kernel') diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index c37c0be4d646..5939dc613559 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -937,13 +937,21 @@ int msi_domain_alloc_irqs_descs_locked(struct irq_domain *domain, struct device lockdep_assert_held(&dev->msi.data->mutex); + if (WARN_ON_ONCE(irq_domain_is_msi_parent(domain))) { + ret = -EINVAL; + goto free; + } + + /* Frees allocated descriptors in case of failure. */ ret = msi_domain_add_simple_msi_descs(info, dev, nvec); if (ret) - return ret; + goto free; ret = ops->domain_alloc_irqs(domain, dev, nvec); - if (ret) - msi_domain_free_irqs_descs_locked(domain, dev); + if (!ret) + return 0; +free: + msi_domain_free_irqs_descs_locked(domain, dev); return ret; } @@ -1013,6 +1021,9 @@ void msi_domain_free_irqs_descs_locked(struct irq_domain *domain, struct device lockdep_assert_held(&dev->msi.data->mutex); + if (WARN_ON_ONCE(irq_domain_is_msi_parent(domain))) + return; + ops->domain_free_irqs(domain, dev); if (ops->msi_post_free) ops->msi_post_free(domain, dev); -- cgit v1.2.3 From f1139f905bd2d8bec59be0c1404b592279ae0ac9 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:24:20 +0100 Subject: genirq/msi: Move xarray into a separate struct and create an array The upcoming support for multiple MSI domains per device requires storage for the MSI descriptors and in a second step storage for the irqdomain pointers. Move the xarray into a separate data structure msi_dev_domain and create an array with size 1 in msi_device_data, which can be expanded later when the support for per device domains is implemented. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124230313.864887773@linutronix.de --- include/linux/msi.h | 13 +++++++++++-- include/linux/msi_api.h | 8 ++++++++ kernel/irq/msi.c | 32 ++++++++++++++++++++++---------- 3 files changed, 41 insertions(+), 12 deletions(-) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index 9f72494dcc3e..f7b9c41a4f54 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -173,19 +173,28 @@ enum msi_desc_filter { MSI_DESC_ASSOCIATED, }; + +/** + * struct msi_dev_domain - The internals of MSI domain info per device + * @store: Xarray for storing MSI descriptor pointers + */ +struct msi_dev_domain { + struct xarray store; +}; + /** * msi_device_data - MSI per device data * @properties: MSI properties which are interesting to drivers * @platform_data: Platform-MSI specific data * @mutex: Mutex protecting the MSI descriptor store - * @__store: Xarray for storing MSI descriptor pointers + * @__domains: Internal data for per device MSI domains * @__iter_idx: Index to search the next entry for iterators */ struct msi_device_data { unsigned long properties; struct platform_msi_priv_data *platform_data; struct mutex mutex; - struct xarray __store; + struct msi_dev_domain __domains[MSI_MAX_DEVICE_IRQDOMAINS]; unsigned long __iter_idx; }; diff --git a/include/linux/msi_api.h b/include/linux/msi_api.h index 57d27cfcde2d..4dbbce68a217 100644 --- a/include/linux/msi_api.h +++ b/include/linux/msi_api.h @@ -10,6 +10,14 @@ struct device; +/* + * Per device interrupt domain related constants. + */ +enum msi_domain_ids { + MSI_DEFAULT_DOMAIN, + MSI_MAX_DEVICE_IRQDOMAINS, +}; + unsigned int msi_get_virq(struct device *dev, unsigned int index); #endif diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 5939dc613559..c2bc94ee5c3e 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -60,10 +60,11 @@ static void msi_free_desc(struct msi_desc *desc) static int msi_insert_desc(struct msi_device_data *md, struct msi_desc *desc, unsigned int index) { + struct xarray *xa = &md->__domains[MSI_DEFAULT_DOMAIN].store; int ret; desc->msi_index = index; - ret = xa_insert(&md->__store, index, desc, GFP_KERNEL); + ret = xa_insert(xa, index, desc, GFP_KERNEL); if (ret) msi_free_desc(desc); return ret; @@ -147,7 +148,7 @@ static bool msi_desc_match(struct msi_desc *desc, enum msi_desc_filter filter) void msi_free_msi_descs_range(struct device *dev, unsigned int first_index, unsigned int last_index) { - struct xarray *xa = &dev->msi.data->__store; + struct xarray *xa = &dev->msi.data->__domains[MSI_DEFAULT_DOMAIN].store; struct msi_desc *desc; unsigned long idx; @@ -179,9 +180,12 @@ EXPORT_SYMBOL_GPL(get_cached_msi_msg); static void msi_device_data_release(struct device *dev, void *res) { struct msi_device_data *md = res; + int i; - WARN_ON_ONCE(!xa_empty(&md->__store)); - xa_destroy(&md->__store); + for (i = 0; i < MSI_MAX_DEVICE_IRQDOMAINS; i++) { + WARN_ON_ONCE(!xa_empty(&md->__domains[i].store)); + xa_destroy(&md->__domains[i].store); + } dev->msi.data = NULL; } @@ -198,7 +202,7 @@ static void msi_device_data_release(struct device *dev, void *res) int msi_setup_device_data(struct device *dev) { struct msi_device_data *md; - int ret; + int ret, i; if (dev->msi.data) return 0; @@ -213,7 +217,9 @@ int msi_setup_device_data(struct device *dev) return ret; } - xa_init(&md->__store); + for (i = 0; i < MSI_MAX_DEVICE_IRQDOMAINS; i++) + xa_init(&md->__domains[i].store); + mutex_init(&md->mutex); dev->msi.data = md; devres_add(dev, md); @@ -236,7 +242,7 @@ EXPORT_SYMBOL_GPL(msi_lock_descs); */ void msi_unlock_descs(struct device *dev) { - /* Invalidate the index wich was cached by the iterator */ + /* Invalidate the index which was cached by the iterator */ dev->msi.data->__iter_idx = MSI_MAX_INDEX; mutex_unlock(&dev->msi.data->mutex); } @@ -244,9 +250,10 @@ EXPORT_SYMBOL_GPL(msi_unlock_descs); static struct msi_desc *msi_find_desc(struct msi_device_data *md, enum msi_desc_filter filter) { + struct xarray *xa = &md->__domains[MSI_DEFAULT_DOMAIN].store; struct msi_desc *desc; - xa_for_each_start(&md->__store, md->__iter_idx, desc, md->__iter_idx) { + xa_for_each_start(xa, md->__iter_idx, desc, md->__iter_idx) { if (msi_desc_match(desc, filter)) return desc; } @@ -320,6 +327,7 @@ unsigned int msi_get_virq(struct device *dev, unsigned int index) { struct msi_desc *desc; unsigned int ret = 0; + struct xarray *xa; bool pcimsi; if (!dev->msi.data) @@ -328,7 +336,8 @@ unsigned int msi_get_virq(struct device *dev, unsigned int index) pcimsi = dev_is_pci(dev) ? to_pci_dev(dev)->msi_enabled : false; msi_lock_descs(dev); - desc = xa_load(&dev->msi.data->__store, pcimsi ? 0 : index); + xa = &dev->msi.data->__domains[MSI_DEFAULT_DOMAIN].store; + desc = xa_load(xa, pcimsi ? 0 : index); if (desc && desc->irq) { /* * PCI-MSI has only one descriptor for multiple interrupts. @@ -707,6 +716,7 @@ int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev, struct msi_domain_info *info = domain->host_data; struct msi_domain_ops *ops = info->ops; struct msi_desc *desc; + struct xarray *xa; int ret, virq; msi_lock_descs(dev); @@ -714,8 +724,10 @@ int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev, if (ret) goto unlock; + xa = &dev->msi.data->__domains[MSI_DEFAULT_DOMAIN].store; + for (virq = virq_base; virq < virq_base + nvec; virq++) { - desc = xa_load(&dev->msi.data->__store, virq); + desc = xa_load(xa, virq); desc->irq = virq; ops->set_desc(arg, desc); -- cgit v1.2.3 From 64258eaa442b0b7d7eac8942cf27863ad9e6028e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:24:22 +0100 Subject: genirq/msi: Add pointers for per device irq domains With the upcoming per device MSI interrupt domain support it is necessary to store the domain pointers per device. Instead of delegating that storage to device drivers or subsystems add a domain pointer to the msi_dev_domain array in struct msi_device_data. This pointer is also used to take care of tearing down the irq domains when msi_device_data is cleaned up via devres. The interfaces into the MSI core will be changed from irqdomain pointer based interfaces to domain id based interfaces to support multiple MSI domains on a single device (e.g. PCI/MSI[-X] and PCI/IMS. Once the per device domain support is complete the irq domain pointer in struct device::msi.domain will not longer contain a pointer to the "global" MSI domain. It will contain a pointer to the MSI parent domain instead. It would be a horrible maze of conditionals to evaluate all over the place which domain pointer should be used, i.e. the "global" one in device::msi::domain or one from the internal pointer array. To avoid this evaluate in msi_setup_device_data() whether the irq domain which is associated to a device is a "global" or a parent MSI domain. If it is global then copy the pointer into the first entry of the msi_dev_domain array. This allows to convert interfaces and implementation to domain ids while keeping everything existing working. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124230313.923860399@linutronix.de --- include/linux/msi.h | 3 +++ kernel/irq/msi.c | 9 +++++++++ 2 files changed, 12 insertions(+) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index f7b9c41a4f54..fdbc80d81e26 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -77,6 +77,7 @@ struct msi_desc; struct pci_dev; struct platform_msi_priv_data; struct device_attribute; +struct irq_domain; void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg); #ifdef CONFIG_GENERIC_MSI_IRQ @@ -177,9 +178,11 @@ enum msi_desc_filter { /** * struct msi_dev_domain - The internals of MSI domain info per device * @store: Xarray for storing MSI descriptor pointers + * @irqdomain: Pointer to a per device interrupt domain */ struct msi_dev_domain { struct xarray store; + struct irq_domain *domain; }; /** diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index c2bc94ee5c3e..de65acc5cfb0 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -220,6 +220,15 @@ int msi_setup_device_data(struct device *dev) for (i = 0; i < MSI_MAX_DEVICE_IRQDOMAINS; i++) xa_init(&md->__domains[i].store); + /* + * If @dev::msi::domain is set and is a global MSI domain, copy the + * pointer into the domain array so all code can operate on domain + * ids. The NULL pointer check is required to keep the legacy + * architecture specific PCI/MSI support working. + */ + if (dev->msi.domain && !irq_domain_is_msi_parent(dev->msi.domain)) + md->__domains[MSI_DEFAULT_DOMAIN].domain = dev->msi.domain; + mutex_init(&md->mutex); dev->msi.data = md; devres_add(dev, md); -- cgit v1.2.3 From 94ff94cfea2827e287a42781a47f37c9ef82186b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:24:24 +0100 Subject: genirq/msi: Make MSI descriptor iterators device domain aware To support multiple MSI interrupt domains per device it is necessary to segment the xarray MSI descriptor storage. Each domain gets up to MSI_MAX_INDEX entries. Change the iterators so they operate with domain ids and take the domain offsets into account. The publicly available iterators which are mostly used in legacy implementations and the PCI/MSI core default to MSI_DEFAULT_DOMAIN (0) which is the id for the existing "global" domains. No functional change. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124230313.985498981@linutronix.de --- include/linux/msi.h | 48 ++++++++++++++++++++++++++++++++++++++++++------ kernel/irq/msi.c | 35 +++++++++++++++++++++++------------ 2 files changed, 65 insertions(+), 18 deletions(-) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index fdbc80d81e26..764a4e8828fb 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -206,11 +206,48 @@ int msi_setup_device_data(struct device *dev); void msi_lock_descs(struct device *dev); void msi_unlock_descs(struct device *dev); -struct msi_desc *msi_first_desc(struct device *dev, enum msi_desc_filter filter); -struct msi_desc *msi_next_desc(struct device *dev, enum msi_desc_filter filter); +struct msi_desc *msi_domain_first_desc(struct device *dev, unsigned int domid, + enum msi_desc_filter filter); /** - * msi_for_each_desc - Iterate the MSI descriptors + * msi_first_desc - Get the first MSI descriptor of the default irqdomain + * @dev: Device to operate on + * @filter: Descriptor state filter + * + * Must be called with the MSI descriptor mutex held, i.e. msi_lock_descs() + * must be invoked before the call. + * + * Return: Pointer to the first MSI descriptor matching the search + * criteria, NULL if none found. + */ +static inline struct msi_desc *msi_first_desc(struct device *dev, + enum msi_desc_filter filter) +{ + return msi_domain_first_desc(dev, MSI_DEFAULT_DOMAIN, filter); +} + +struct msi_desc *msi_next_desc(struct device *dev, unsigned int domid, + enum msi_desc_filter filter); + +/** + * msi_domain_for_each_desc - Iterate the MSI descriptors in a specific domain + * + * @desc: struct msi_desc pointer used as iterator + * @dev: struct device pointer - device to iterate + * @domid: The id of the interrupt domain which should be walked. + * @filter: Filter for descriptor selection + * + * Notes: + * - The loop must be protected with a msi_lock_descs()/msi_unlock_descs() + * pair. + * - It is safe to remove a retrieved MSI descriptor in the loop. + */ +#define msi_domain_for_each_desc(desc, dev, domid, filter) \ + for ((desc) = msi_domain_first_desc((dev), (domid), (filter)); (desc); \ + (desc) = msi_next_desc((dev), (domid), (filter))) + +/** + * msi_for_each_desc - Iterate the MSI descriptors in the default irqdomain * * @desc: struct msi_desc pointer used as iterator * @dev: struct device pointer - device to iterate @@ -221,9 +258,8 @@ struct msi_desc *msi_next_desc(struct device *dev, enum msi_desc_filter filter); * pair. * - It is safe to remove a retrieved MSI descriptor in the loop. */ -#define msi_for_each_desc(desc, dev, filter) \ - for ((desc) = msi_first_desc((dev), (filter)); (desc); \ - (desc) = msi_next_desc((dev), (filter))) +#define msi_for_each_desc(desc, dev, filter) \ + msi_domain_for_each_desc((desc), (dev), MSI_DEFAULT_DOMAIN, (filter)) #define msi_desc_to_dev(desc) ((desc)->dev) diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index de65acc5cfb0..ec08d1f22be0 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -19,8 +19,14 @@ #include "internals.h" +/* Invalid Xarray index which is outside of any searchable range */ +#define MSI_XA_MAX_INDEX (ULONG_MAX - 1) +/* The maximum domain size */ +#define MSI_XA_DOMAIN_SIZE (MSI_MAX_INDEX + 1) + static inline int msi_sysfs_create_group(struct device *dev); + /** * msi_alloc_desc - Allocate an initialized msi_desc * @dev: Pointer to the device for which this is allocated @@ -252,27 +258,29 @@ EXPORT_SYMBOL_GPL(msi_lock_descs); void msi_unlock_descs(struct device *dev) { /* Invalidate the index which was cached by the iterator */ - dev->msi.data->__iter_idx = MSI_MAX_INDEX; + dev->msi.data->__iter_idx = MSI_XA_MAX_INDEX; mutex_unlock(&dev->msi.data->mutex); } EXPORT_SYMBOL_GPL(msi_unlock_descs); -static struct msi_desc *msi_find_desc(struct msi_device_data *md, enum msi_desc_filter filter) +static struct msi_desc *msi_find_desc(struct msi_device_data *md, unsigned int domid, + enum msi_desc_filter filter) { - struct xarray *xa = &md->__domains[MSI_DEFAULT_DOMAIN].store; + struct xarray *xa = &md->__domains[domid].store; struct msi_desc *desc; xa_for_each_start(xa, md->__iter_idx, desc, md->__iter_idx) { if (msi_desc_match(desc, filter)) return desc; } - md->__iter_idx = MSI_MAX_INDEX; + md->__iter_idx = MSI_XA_MAX_INDEX; return NULL; } /** - * msi_first_desc - Get the first MSI descriptor of a device + * msi_domain_first_desc - Get the first MSI descriptor of an irqdomain associated to a device * @dev: Device to operate on + * @domid: The id of the interrupt domain which should be walked. * @filter: Descriptor state filter * * Must be called with the MSI descriptor mutex held, i.e. msi_lock_descs() @@ -281,23 +289,25 @@ static struct msi_desc *msi_find_desc(struct msi_device_data *md, enum msi_desc_ * Return: Pointer to the first MSI descriptor matching the search * criteria, NULL if none found. */ -struct msi_desc *msi_first_desc(struct device *dev, enum msi_desc_filter filter) +struct msi_desc *msi_domain_first_desc(struct device *dev, unsigned int domid, + enum msi_desc_filter filter) { struct msi_device_data *md = dev->msi.data; - if (WARN_ON_ONCE(!md)) + if (WARN_ON_ONCE(!md || domid >= MSI_MAX_DEVICE_IRQDOMAINS)) return NULL; lockdep_assert_held(&md->mutex); md->__iter_idx = 0; - return msi_find_desc(md, filter); + return msi_find_desc(md, domid, filter); } -EXPORT_SYMBOL_GPL(msi_first_desc); +EXPORT_SYMBOL_GPL(msi_domain_first_desc); /** * msi_next_desc - Get the next MSI descriptor of a device * @dev: Device to operate on + * @domid: The id of the interrupt domain which should be walked. * @filter: Descriptor state filter * * The first invocation of msi_next_desc() has to be preceeded by a @@ -308,11 +318,12 @@ EXPORT_SYMBOL_GPL(msi_first_desc); * Return: Pointer to the next MSI descriptor matching the search * criteria, NULL if none found. */ -struct msi_desc *msi_next_desc(struct device *dev, enum msi_desc_filter filter) +struct msi_desc *msi_next_desc(struct device *dev, unsigned int domid, + enum msi_desc_filter filter) { struct msi_device_data *md = dev->msi.data; - if (WARN_ON_ONCE(!md)) + if (WARN_ON_ONCE(!md || domid >= MSI_MAX_DEVICE_IRQDOMAINS)) return NULL; lockdep_assert_held(&md->mutex); @@ -321,7 +332,7 @@ struct msi_desc *msi_next_desc(struct device *dev, enum msi_desc_filter filter) return NULL; md->__iter_idx++; - return msi_find_desc(md, filter); + return msi_find_desc(md, domid, filter); } EXPORT_SYMBOL_GPL(msi_next_desc); -- cgit v1.2.3 From 98043704f375f63a47efeff123ab92fcf34b95e6 Mon Sep 17 00:00:00 2001 From: "Ahmed S. Darwish" Date: Fri, 25 Nov 2022 00:24:25 +0100 Subject: genirq/msi: Make msi_get_virq() device domain aware In preparation of the upcoming per device multi MSI domain support, change the interface to support lookups based on domain id and zero based index within the domain. Signed-off-by: Ahmed S. Darwish Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124230314.044613697@linutronix.de --- include/linux/msi_api.h | 14 +++++++++++++- kernel/irq/msi.c | 19 +++++++++++++------ 2 files changed, 26 insertions(+), 7 deletions(-) (limited to 'kernel') diff --git a/include/linux/msi_api.h b/include/linux/msi_api.h index 4dbbce68a217..8640171288a8 100644 --- a/include/linux/msi_api.h +++ b/include/linux/msi_api.h @@ -18,6 +18,18 @@ enum msi_domain_ids { MSI_MAX_DEVICE_IRQDOMAINS, }; -unsigned int msi_get_virq(struct device *dev, unsigned int index); +unsigned int msi_domain_get_virq(struct device *dev, unsigned int domid, unsigned int index); + +/** + * msi_get_virq - Lookup the Linux interrupt number for a MSI index on the default interrupt domain + * @dev: Device for which the lookup happens + * @index: The MSI index to lookup + * + * Return: The Linux interrupt number on success (> 0), 0 if not found + */ +static inline unsigned int msi_get_virq(struct device *dev, unsigned int index) +{ + return msi_domain_get_virq(dev, MSI_DEFAULT_DOMAIN, index); +} #endif diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index ec08d1f22be0..e1593c16518b 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -337,26 +337,32 @@ struct msi_desc *msi_next_desc(struct device *dev, unsigned int domid, EXPORT_SYMBOL_GPL(msi_next_desc); /** - * msi_get_virq - Return Linux interrupt number of a MSI interrupt + * msi_domain_get_virq - Lookup the Linux interrupt number for a MSI index on a interrupt domain * @dev: Device to operate on + * @domid: Domain ID of the interrupt domain associated to the device * @index: MSI interrupt index to look for (0-based) * * Return: The Linux interrupt number on success (> 0), 0 if not found */ -unsigned int msi_get_virq(struct device *dev, unsigned int index) +unsigned int msi_domain_get_virq(struct device *dev, unsigned int domid, unsigned int index) { struct msi_desc *desc; unsigned int ret = 0; + bool pcimsi = false; struct xarray *xa; - bool pcimsi; if (!dev->msi.data) return 0; - pcimsi = dev_is_pci(dev) ? to_pci_dev(dev)->msi_enabled : false; + if (WARN_ON_ONCE(index > MSI_MAX_INDEX || domid >= MSI_MAX_DEVICE_IRQDOMAINS)) + return 0; + + /* This check is only valid for the PCI default MSI domain */ + if (dev_is_pci(dev) && domid == MSI_DEFAULT_DOMAIN) + pcimsi = to_pci_dev(dev)->msi_enabled; msi_lock_descs(dev); - xa = &dev->msi.data->__domains[MSI_DEFAULT_DOMAIN].store; + xa = &dev->msi.data->__domains[domid].store; desc = xa_load(xa, pcimsi ? 0 : index); if (desc && desc->irq) { /* @@ -371,10 +377,11 @@ unsigned int msi_get_virq(struct device *dev, unsigned int index) ret = desc->irq; } } + msi_unlock_descs(dev); return ret; } -EXPORT_SYMBOL_GPL(msi_get_virq); +EXPORT_SYMBOL_GPL(msi_domain_get_virq); #ifdef CONFIG_SYSFS static struct attribute *msi_dev_attrs[] = { -- cgit v1.2.3 From 1c89396300292da4e4a87db63bef45975461fa25 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:24:27 +0100 Subject: genirq/msi: Rename msi_add_msi_desc() to msi_insert_msi_desc() This reflects the functionality better. No functional change. Signed-off-by: Thomas Gleixner Reviewed-by: Jason Gunthorpe Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124230314.103554618@linutronix.de --- drivers/pci/msi/msi.c | 4 ++-- drivers/soc/ti/ti_sci_inta_msi.c | 4 ++-- include/linux/msi.h | 2 +- kernel/irq/msi.c | 6 ++++-- 4 files changed, 9 insertions(+), 7 deletions(-) (limited to 'kernel') diff --git a/drivers/pci/msi/msi.c b/drivers/pci/msi/msi.c index b94f6da3f32f..d107bde58bd6 100644 --- a/drivers/pci/msi/msi.c +++ b/drivers/pci/msi/msi.c @@ -308,7 +308,7 @@ static int msi_setup_msi_desc(struct pci_dev *dev, int nvec, if (desc.pci.msi_attrib.can_mask) pci_read_config_dword(dev, desc.pci.mask_pos, &desc.pci.msi_mask); - return msi_add_msi_desc(&dev->dev, &desc); + return msi_insert_msi_desc(&dev->dev, &desc); } static int msi_verify_entries(struct pci_dev *dev) @@ -591,7 +591,7 @@ static int msix_setup_msi_descs(struct pci_dev *dev, void __iomem *base, desc.pci.msix_ctrl = readl(addr + PCI_MSIX_ENTRY_VECTOR_CTRL); } - ret = msi_add_msi_desc(&dev->dev, &desc); + ret = msi_insert_msi_desc(&dev->dev, &desc); if (ret) break; } diff --git a/drivers/soc/ti/ti_sci_inta_msi.c b/drivers/soc/ti/ti_sci_inta_msi.c index 991c78b34745..255849cb81ed 100644 --- a/drivers/soc/ti/ti_sci_inta_msi.c +++ b/drivers/soc/ti/ti_sci_inta_msi.c @@ -73,13 +73,13 @@ static int ti_sci_inta_msi_alloc_descs(struct device *dev, for (set = 0; set < res->sets; set++) { for (i = 0; i < res->desc[set].num; i++, count++) { msi_desc.msi_index = res->desc[set].start + i; - if (msi_add_msi_desc(dev, &msi_desc)) + if (msi_insert_msi_desc(dev, &msi_desc)) goto fail; } for (i = 0; i < res->desc[set].num_sec; i++, count++) { msi_desc.msi_index = res->desc[set].start_sec + i; - if (msi_add_msi_desc(dev, &msi_desc)) + if (msi_insert_msi_desc(dev, &msi_desc)) goto fail; } } diff --git a/include/linux/msi.h b/include/linux/msi.h index 764a4e8828fb..f8ba85af3542 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -286,7 +286,7 @@ static inline void msi_desc_set_iommu_cookie(struct msi_desc *desc, } #endif -int msi_add_msi_desc(struct device *dev, struct msi_desc *init_desc); +int msi_insert_msi_desc(struct device *dev, struct msi_desc *init_desc); void msi_free_msi_descs_range(struct device *dev, unsigned int first_index, unsigned int last_index); /** diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index e1593c16518b..33b8a6cca3f8 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -77,13 +77,15 @@ static int msi_insert_desc(struct msi_device_data *md, struct msi_desc *desc, un } /** - * msi_add_msi_desc - Allocate and initialize a MSI descriptor + * msi_insert_msi_desc - Allocate and initialize a MSI descriptor and + * insert it at @init_desc->msi_index + * * @dev: Pointer to the device for which the descriptor is allocated * @init_desc: Pointer to an MSI descriptor to initialize the new descriptor * * Return: 0 on success or an appropriate failure code. */ -int msi_add_msi_desc(struct device *dev, struct msi_desc *init_desc) +int msi_insert_msi_desc(struct device *dev, struct msi_desc *init_desc) { struct msi_desc *desc; -- cgit v1.2.3 From fc8ab388325ddfd848d00f3a3383b4304594546a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:24:29 +0100 Subject: genirq/msi: Make descriptor allocation device domain aware Change the descriptor allocation and insertion functions to take a domain id to prepare for the upcoming multi MSI domain per device support. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124230314.163043028@linutronix.de --- include/linux/msi.h | 16 +++++++++++++++- kernel/irq/msi.c | 20 ++++++++++++-------- 2 files changed, 27 insertions(+), 9 deletions(-) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index f8ba85af3542..35e9d0050ca1 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -286,7 +286,21 @@ static inline void msi_desc_set_iommu_cookie(struct msi_desc *desc, } #endif -int msi_insert_msi_desc(struct device *dev, struct msi_desc *init_desc); +int msi_domain_insert_msi_desc(struct device *dev, unsigned int domid, + struct msi_desc *init_desc); +/** + * msi_insert_msi_desc - Allocate and initialize a MSI descriptor in the + * default irqdomain and insert it at @init_desc->msi_index + * @dev: Pointer to the device for which the descriptor is allocated + * @init_desc: Pointer to an MSI descriptor to initialize the new descriptor + * + * Return: 0 on success or an appropriate failure code. + */ +static inline int msi_insert_msi_desc(struct device *dev, struct msi_desc *init_desc) +{ + return msi_domain_insert_msi_desc(dev, MSI_DEFAULT_DOMAIN, init_desc); +} + void msi_free_msi_descs_range(struct device *dev, unsigned int first_index, unsigned int last_index); /** diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 33b8a6cca3f8..e75b07fffbcd 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -39,7 +39,7 @@ static inline int msi_sysfs_create_group(struct device *dev); * Return: pointer to allocated &msi_desc on success or %NULL on failure */ static struct msi_desc *msi_alloc_desc(struct device *dev, int nvec, - const struct irq_affinity_desc *affinity) + const struct irq_affinity_desc *affinity) { struct msi_desc *desc = kzalloc(sizeof(*desc), GFP_KERNEL); @@ -64,9 +64,10 @@ static void msi_free_desc(struct msi_desc *desc) kfree(desc); } -static int msi_insert_desc(struct msi_device_data *md, struct msi_desc *desc, unsigned int index) +static int msi_insert_desc(struct msi_device_data *md, struct msi_desc *desc, + unsigned int domid, unsigned int index) { - struct xarray *xa = &md->__domains[MSI_DEFAULT_DOMAIN].store; + struct xarray *xa = &md->__domains[domid].store; int ret; desc->msi_index = index; @@ -77,15 +78,17 @@ static int msi_insert_desc(struct msi_device_data *md, struct msi_desc *desc, un } /** - * msi_insert_msi_desc - Allocate and initialize a MSI descriptor and - * insert it at @init_desc->msi_index + * msi_domain_insert_msi_desc - Allocate and initialize a MSI descriptor and + * insert it at @init_desc->msi_index * * @dev: Pointer to the device for which the descriptor is allocated + * @domid: The id of the interrupt domain to which the desriptor is added * @init_desc: Pointer to an MSI descriptor to initialize the new descriptor * * Return: 0 on success or an appropriate failure code. */ -int msi_insert_msi_desc(struct device *dev, struct msi_desc *init_desc) +int msi_domain_insert_msi_desc(struct device *dev, unsigned int domid, + struct msi_desc *init_desc) { struct msi_desc *desc; @@ -97,7 +100,8 @@ int msi_insert_msi_desc(struct device *dev, struct msi_desc *init_desc) /* Copy type specific data to the new descriptor. */ desc->pci = init_desc->pci; - return msi_insert_desc(dev->msi.data, desc, init_desc->msi_index); + + return msi_insert_desc(dev->msi.data, desc, domid, init_desc->msi_index); } /** @@ -120,7 +124,7 @@ static int msi_add_simple_msi_descs(struct device *dev, unsigned int index, unsi desc = msi_alloc_desc(dev, 1, NULL); if (!desc) goto fail_mem; - ret = msi_insert_desc(dev->msi.data, desc, idx); + ret = msi_insert_desc(dev->msi.data, desc, MSI_DEFAULT_DOMAIN, idx); if (ret) goto fail; } -- cgit v1.2.3 From 377712c5a45f6de40bffef5ddc31b39cd86cf23f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:24:30 +0100 Subject: genirq/msi: Make descriptor freeing domain aware Change the descriptor free functions to take a domain id to prepare for the upcoming multi MSI domain per device support. To avoid changing and extending the interfaces over and over use an core internal control struct and hand the pointer through the various functions. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124230314.220788011@linutronix.de --- include/linux/msi.h | 19 ++++++++++++++++-- kernel/irq/msi.c | 58 ++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 65 insertions(+), 12 deletions(-) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index 35e9d0050ca1..23172d6354ca 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -301,10 +301,25 @@ static inline int msi_insert_msi_desc(struct device *dev, struct msi_desc *init_ return msi_domain_insert_msi_desc(dev, MSI_DEFAULT_DOMAIN, init_desc); } -void msi_free_msi_descs_range(struct device *dev, unsigned int first_index, unsigned int last_index); +void msi_domain_free_msi_descs_range(struct device *dev, unsigned int domid, + unsigned int first, unsigned int last); /** - * msi_free_msi_descs - Free MSI descriptors of a device + * msi_free_msi_descs_range - Free a range of MSI descriptors of a device + * in the default irqdomain + * + * @dev: Device for which to free the descriptors + * @first: Index to start freeing from (inclusive) + * @last: Last index to be freed (inclusive) + */ +static inline void msi_free_msi_descs_range(struct device *dev, unsigned int first, + unsigned int last) +{ + msi_domain_free_msi_descs_range(dev, MSI_DEFAULT_DOMAIN, first, last); +} + +/** + * msi_free_msi_descs - Free all MSI descriptors of a device in the default irqdomain * @dev: Device to free the descriptors */ static inline void msi_free_msi_descs(struct device *dev) diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index e75b07fffbcd..33ababf8645d 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -19,6 +19,18 @@ #include "internals.h" +/** + * struct msi_ctrl - MSI internal management control structure + * @domid: ID of the domain on which management operations should be done + * @first: First (hardware) slot index to operate on + * @last: Last (hardware) slot index to operate on + */ +struct msi_ctrl { + unsigned int domid; + unsigned int first; + unsigned int last; +}; + /* Invalid Xarray index which is outside of any searchable range */ #define MSI_XA_MAX_INDEX (ULONG_MAX - 1) /* The maximum domain size */ @@ -151,22 +163,29 @@ static bool msi_desc_match(struct msi_desc *desc, enum msi_desc_filter filter) return false; } -/** - * msi_free_msi_descs_range - Free MSI descriptors of a device - * @dev: Device to free the descriptors - * @first_index: Index to start freeing from - * @last_index: Last index to be freed - */ -void msi_free_msi_descs_range(struct device *dev, unsigned int first_index, - unsigned int last_index) +static bool msi_ctrl_valid(struct device *dev, struct msi_ctrl *ctrl) +{ + if (WARN_ON_ONCE(ctrl->domid >= MSI_MAX_DEVICE_IRQDOMAINS || + ctrl->first > ctrl->last || + ctrl->first > MSI_MAX_INDEX || + ctrl->last > MSI_MAX_INDEX)) + return false; + return true; +} + +static void msi_domain_free_descs(struct device *dev, struct msi_ctrl *ctrl) { - struct xarray *xa = &dev->msi.data->__domains[MSI_DEFAULT_DOMAIN].store; struct msi_desc *desc; + struct xarray *xa; unsigned long idx; lockdep_assert_held(&dev->msi.data->mutex); - xa_for_each_range(xa, idx, desc, first_index, last_index) { + if (!msi_ctrl_valid(dev, ctrl)) + return; + + xa = &dev->msi.data->__domains[ctrl->domid].store; + xa_for_each_range(xa, idx, desc, ctrl->first, ctrl->last) { xa_erase(xa, idx); /* Leak the descriptor when it is still referenced */ @@ -176,6 +195,25 @@ void msi_free_msi_descs_range(struct device *dev, unsigned int first_index, } } +/** + * msi_domain_free_msi_descs_range - Free a range of MSI descriptors of a device in an irqdomain + * @dev: Device for which to free the descriptors + * @domid: Id of the domain to operate on + * @first: Index to start freeing from (inclusive) + * @last: Last index to be freed (inclusive) + */ +void msi_domain_free_msi_descs_range(struct device *dev, unsigned int domid, + unsigned int first, unsigned int last) +{ + struct msi_ctrl ctrl = { + .domid = domid, + .first = first, + .last = last, + }; + + msi_domain_free_descs(dev, &ctrl); +} + void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg) { *msg = entry->msg; -- cgit v1.2.3 From 40742716f294449549884421170ea18356a2abe8 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:24:32 +0100 Subject: genirq/msi: Make msi_add_simple_msi_descs() device domain aware Allocating simple interrupt descriptors in the core code has to be multi device irqdomain aware for the upcoming PCI/IMS support. Change the interfaces to take a domain id into account. Use the internal control struct for transport of arguments. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124230314.279112474@linutronix.de --- kernel/irq/msi.c | 98 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 57 insertions(+), 41 deletions(-) (limited to 'kernel') diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 33ababf8645d..64a4cc8123e4 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -116,39 +116,6 @@ int msi_domain_insert_msi_desc(struct device *dev, unsigned int domid, return msi_insert_desc(dev->msi.data, desc, domid, init_desc->msi_index); } -/** - * msi_add_simple_msi_descs - Allocate and initialize MSI descriptors - * @dev: Pointer to the device for which the descriptors are allocated - * @index: Index for the first MSI descriptor - * @ndesc: Number of descriptors to allocate - * - * Return: 0 on success or an appropriate failure code. - */ -static int msi_add_simple_msi_descs(struct device *dev, unsigned int index, unsigned int ndesc) -{ - unsigned int idx, last = index + ndesc - 1; - struct msi_desc *desc; - int ret; - - lockdep_assert_held(&dev->msi.data->mutex); - - for (idx = index; idx <= last; idx++) { - desc = msi_alloc_desc(dev, 1, NULL); - if (!desc) - goto fail_mem; - ret = msi_insert_desc(dev->msi.data, desc, MSI_DEFAULT_DOMAIN, idx); - if (ret) - goto fail; - } - return 0; - -fail_mem: - ret = -ENOMEM; -fail: - msi_free_msi_descs_range(dev, index, last); - return ret; -} - static bool msi_desc_match(struct msi_desc *desc, enum msi_desc_filter filter) { switch (filter) { @@ -166,6 +133,7 @@ static bool msi_desc_match(struct msi_desc *desc, enum msi_desc_filter filter) static bool msi_ctrl_valid(struct device *dev, struct msi_ctrl *ctrl) { if (WARN_ON_ONCE(ctrl->domid >= MSI_MAX_DEVICE_IRQDOMAINS || + !dev->msi.data->__domains[ctrl->domid].domain || ctrl->first > ctrl->last || ctrl->first > MSI_MAX_INDEX || ctrl->last > MSI_MAX_INDEX)) @@ -214,6 +182,41 @@ void msi_domain_free_msi_descs_range(struct device *dev, unsigned int domid, msi_domain_free_descs(dev, &ctrl); } +/** + * msi_domain_add_simple_msi_descs - Allocate and initialize MSI descriptors + * @dev: Pointer to the device for which the descriptors are allocated + * @ctrl: Allocation control struct + * + * Return: 0 on success or an appropriate failure code. + */ +static int msi_domain_add_simple_msi_descs(struct device *dev, struct msi_ctrl *ctrl) +{ + struct msi_desc *desc; + unsigned int idx; + int ret; + + lockdep_assert_held(&dev->msi.data->mutex); + + if (!msi_ctrl_valid(dev, ctrl)) + return -EINVAL; + + for (idx = ctrl->first; idx <= ctrl->last; idx++) { + desc = msi_alloc_desc(dev, 1, NULL); + if (!desc) + goto fail_mem; + ret = msi_insert_desc(dev->msi.data, desc, ctrl->domid, idx); + if (ret) + goto fail; + } + return 0; + +fail_mem: + ret = -ENOMEM; +fail: + msi_domain_free_descs(dev, ctrl); + return ret; +} + void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg) { *msg = entry->msg; @@ -786,16 +789,24 @@ int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev, { struct msi_domain_info *info = domain->host_data; struct msi_domain_ops *ops = info->ops; + struct msi_ctrl ctrl = { + .domid = MSI_DEFAULT_DOMAIN, + .first = virq_base, + .last = virq_base + nvec - 1, + }; struct msi_desc *desc; struct xarray *xa; int ret, virq; + if (!msi_ctrl_valid(dev, &ctrl)) + return -EINVAL; + msi_lock_descs(dev); - ret = msi_add_simple_msi_descs(dev, virq_base, nvec); + ret = msi_domain_add_simple_msi_descs(dev, &ctrl); if (ret) goto unlock; - xa = &dev->msi.data->__domains[MSI_DEFAULT_DOMAIN].store; + xa = &dev->msi.data->__domains[ctrl.domid].store; for (virq = virq_base; virq < virq_base + nvec; virq++) { desc = xa_load(xa, virq); @@ -814,7 +825,7 @@ int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev, fail: for (--virq; virq >= virq_base; virq--) irq_domain_free_irqs_common(domain, virq, 1); - msi_free_msi_descs_range(dev, virq_base, virq_base + nvec - 1); + msi_domain_free_descs(dev, &ctrl); unlock: msi_unlock_descs(dev); return ret; @@ -988,14 +999,19 @@ static int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev return 0; } -static int msi_domain_add_simple_msi_descs(struct msi_domain_info *info, - struct device *dev, - unsigned int num_descs) +static int msi_domain_alloc_simple_msi_descs(struct device *dev, + struct msi_domain_info *info, + unsigned int num_descs) { + struct msi_ctrl ctrl = { + .domid = MSI_DEFAULT_DOMAIN, + .last = num_descs - 1, + }; + if (!(info->flags & MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS)) return 0; - return msi_add_simple_msi_descs(dev, 0, num_descs); + return msi_domain_add_simple_msi_descs(dev, &ctrl); } /** @@ -1026,7 +1042,7 @@ int msi_domain_alloc_irqs_descs_locked(struct irq_domain *domain, struct device } /* Frees allocated descriptors in case of failure. */ - ret = msi_domain_add_simple_msi_descs(info, dev, nvec); + ret = msi_domain_alloc_simple_msi_descs(dev, info, nvec); if (ret) goto free; -- cgit v1.2.3 From 4cd5f4403f283766f73749c4c936801f58ffe77a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:24:33 +0100 Subject: genirq/msi: Provide new domain id based interfaces for freeing interrupts Provide two sorts of interfaces to handle the different use cases: - msi_domain_free_irqs_range(): Handles a caller defined precise range - msi_domain_free_irqs_all(): Frees all interrupts associated to a domain The latter is useful for device teardown and to handle the legacy MSI support which does not have any range information available. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124230314.337844751@linutronix.de --- include/linux/msi.h | 9 ++++ kernel/irq/msi.c | 142 ++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 129 insertions(+), 22 deletions(-) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index 23172d6354ca..74cb0a93de6f 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -498,6 +498,15 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, int nvec); void msi_domain_free_irqs_descs_locked(struct irq_domain *domain, struct device *dev); void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev); + +void msi_domain_free_irqs_range_locked(struct device *dev, unsigned int domid, + unsigned int first, unsigned int last); +void msi_domain_free_irqs_range(struct device *dev, unsigned int domid, + unsigned int first, unsigned int last); + +void msi_domain_free_irqs_all_locked(struct device *dev, unsigned int domid); +void msi_domain_free_irqs_all(struct device *dev, unsigned int domid); + struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain); struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode, diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 64a4cc8123e4..c1ac780b2192 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -545,7 +545,25 @@ static inline void msi_sysfs_remove_desc(struct device *dev, struct msi_desc *de #endif /* !CONFIG_SYSFS */ static int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, int nvec); -static void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev); + +static struct irq_domain *msi_get_device_domain(struct device *dev, unsigned int domid) +{ + struct irq_domain *domain; + + lockdep_assert_held(&dev->msi.data->mutex); + + if (WARN_ON_ONCE(domid >= MSI_MAX_DEVICE_IRQDOMAINS)) + return NULL; + + domain = dev->msi.data->__domains[domid].domain; + if (!domain) + return NULL; + + if (WARN_ON_ONCE(irq_domain_is_msi_parent(domain))) + return NULL; + + return domain; +} static inline void irq_chip_write_msi_msg(struct irq_data *data, struct msi_msg *msg) @@ -706,7 +724,6 @@ static struct msi_domain_ops msi_domain_ops_default = { .msi_prepare = msi_domain_ops_prepare, .set_desc = msi_domain_ops_set_desc, .domain_alloc_irqs = __msi_domain_alloc_irqs, - .domain_free_irqs = __msi_domain_free_irqs, }; static void msi_domain_update_dom_ops(struct msi_domain_info *info) @@ -720,8 +737,6 @@ static void msi_domain_update_dom_ops(struct msi_domain_info *info) if (ops->domain_alloc_irqs == NULL) ops->domain_alloc_irqs = msi_domain_ops_default.domain_alloc_irqs; - if (ops->domain_free_irqs == NULL) - ops->domain_free_irqs = msi_domain_ops_default.domain_free_irqs; if (!(info->flags & MSI_FLAG_USE_DEF_DOM_OPS)) return; @@ -1073,15 +1088,21 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, int nve return ret; } -static void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev) +static void __msi_domain_free_irqs(struct device *dev, struct irq_domain *domain, + struct msi_ctrl *ctrl) { + struct xarray *xa = &dev->msi.data->__domains[ctrl->domid].store; struct msi_domain_info *info = domain->host_data; struct irq_data *irqd; struct msi_desc *desc; + unsigned long idx; int i; - /* Only handle MSI entries which have an interrupt associated */ - msi_for_each_desc(desc, dev, MSI_DESC_ASSOCIATED) { + xa_for_each_range(xa, idx, desc, ctrl->first, ctrl->last) { + /* Only handle MSI entries which have an interrupt associated */ + if (!msi_desc_match(desc, MSI_DESC_ASSOCIATED)) + continue; + /* Make sure all interrupts are deactivated */ for (i = 0; i < desc->nvec_used; i++) { irqd = irq_domain_get_irq_data(domain, desc->irq + i); @@ -1096,11 +1117,99 @@ static void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev } } -static void msi_domain_free_msi_descs(struct msi_domain_info *info, - struct device *dev) +static void msi_domain_free_locked(struct device *dev, struct msi_ctrl *ctrl) { + struct msi_domain_info *info; + struct msi_domain_ops *ops; + struct irq_domain *domain; + + if (!msi_ctrl_valid(dev, ctrl)) + return; + + domain = msi_get_device_domain(dev, ctrl->domid); + if (!domain) + return; + + info = domain->host_data; + ops = info->ops; + + if (ops->domain_free_irqs) + ops->domain_free_irqs(domain, dev); + else + __msi_domain_free_irqs(dev, domain, ctrl); + + if (ops->msi_post_free) + ops->msi_post_free(domain, dev); + if (info->flags & MSI_FLAG_FREE_MSI_DESCS) - msi_free_msi_descs(dev); + msi_domain_free_descs(dev, ctrl); +} + +/** + * msi_domain_free_irqs_range_locked - Free a range of interrupts from a MSI interrupt domain + * associated to @dev with msi_lock held + * @dev: Pointer to device struct of the device for which the interrupts + * are freed + * @domid: Id of the interrupt domain to operate on + * @first: First index to free (inclusive) + * @last: Last index to free (inclusive) + */ +void msi_domain_free_irqs_range_locked(struct device *dev, unsigned int domid, + unsigned int first, unsigned int last) +{ + struct msi_ctrl ctrl = { + .domid = domid, + .first = first, + .last = last, + }; + msi_domain_free_locked(dev, &ctrl); +} + +/** + * msi_domain_free_irqs_range - Free a range of interrupts from a MSI interrupt domain + * associated to @dev + * @dev: Pointer to device struct of the device for which the interrupts + * are freed + * @domid: Id of the interrupt domain to operate on + * @first: First index to free (inclusive) + * @last: Last index to free (inclusive) + */ +void msi_domain_free_irqs_range(struct device *dev, unsigned int domid, + unsigned int first, unsigned int last) +{ + msi_lock_descs(dev); + msi_domain_free_irqs_range_locked(dev, domid, first, last); + msi_unlock_descs(dev); +} + +/** + * msi_domain_free_irqs_all_locked - Free all interrupts from a MSI interrupt domain + * associated to a device + * @dev: Pointer to device struct of the device for which the interrupts + * are freed + * @domid: The id of the domain to operate on + * + * Must be invoked from within a msi_lock_descs() / msi_unlock_descs() + * pair. Use this for MSI irqdomains which implement their own vector + * allocation. + */ +void msi_domain_free_irqs_all_locked(struct device *dev, unsigned int domid) +{ + msi_domain_free_irqs_range_locked(dev, domid, 0, MSI_MAX_INDEX); +} + +/** + * msi_domain_free_irqs_all - Free all interrupts from a MSI interrupt domain + * associated to a device + * @dev: Pointer to device struct of the device for which the interrupts + * are freed + * @domid: The id of the domain to operate on + */ +void msi_domain_free_irqs_all(struct device *dev, unsigned int domid) +{ + msi_lock_descs(dev); + msi_domain_free_irqs_all_locked(dev, domid); + msi_unlock_descs(dev); } /** @@ -1115,18 +1224,7 @@ static void msi_domain_free_msi_descs(struct msi_domain_info *info, */ void msi_domain_free_irqs_descs_locked(struct irq_domain *domain, struct device *dev) { - struct msi_domain_info *info = domain->host_data; - struct msi_domain_ops *ops = info->ops; - - lockdep_assert_held(&dev->msi.data->mutex); - - if (WARN_ON_ONCE(irq_domain_is_msi_parent(domain))) - return; - - ops->domain_free_irqs(domain, dev); - if (ops->msi_post_free) - ops->msi_post_free(domain, dev); - msi_domain_free_msi_descs(info, dev); + msi_domain_free_irqs_range_locked(dev, MSI_DEFAULT_DOMAIN, 0, MSI_MAX_INDEX); } /** -- cgit v1.2.3 From f2480e7dacdcd9aab25641346ae53b7ff03777fc Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:24:35 +0100 Subject: genirq/msi: Provide new domain id allocation functions Provide two sorts of interfaces to handle the different use cases: - msi_domain_alloc_irqs_range(): Handles a caller defined precise range - msi_domain_alloc_irqs_all(): Allocates all interrupts associated to a domain by scanning the allocated MSI descriptors The latter is useful for the existing PCI/MSI support which does not have range information available. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124230314.396497163@linutronix.de --- include/linux/msi.h | 18 +++--- kernel/irq/msi.c | 175 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 146 insertions(+), 47 deletions(-) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index 74cb0a93de6f..611707d619fc 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -383,8 +383,8 @@ struct msi_domain_info; * @get_hwirq, @msi_init and @msi_free are callbacks used by the underlying * irqdomain. * - * @msi_check, @msi_prepare and @set_desc are callbacks used by - * msi_domain_alloc/free_irqs(). + * @msi_check, @msi_prepare and @set_desc are callbacks used by the + * msi_domain_alloc/free_irqs*() variants. * * @domain_alloc_irqs, @domain_free_irqs can be used to override the * default allocation/free functions (__msi_domain_alloc/free_irqs). This @@ -392,11 +392,6 @@ struct msi_domain_info; * be wrapped into the regular irq domains concepts by mere mortals. This * allows to universally use msi_domain_alloc/free_irqs without having to * special case XEN all over the place. - * - * Contrary to other operations @domain_alloc_irqs and @domain_free_irqs - * are set to the default implementation if NULL and even when - * MSI_FLAG_USE_DEF_DOM_OPS is not set to avoid breaking existing users and - * because these callbacks are obviously mandatory. */ struct msi_domain_ops { irq_hw_number_t (*get_hwirq)(struct msi_domain_info *info, @@ -496,14 +491,21 @@ int msi_domain_alloc_irqs_descs_locked(struct irq_domain *domain, struct device int nvec); int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, int nvec); + void msi_domain_free_irqs_descs_locked(struct irq_domain *domain, struct device *dev); void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev); +int msi_domain_alloc_irqs_range_locked(struct device *dev, unsigned int domid, + unsigned int first, unsigned int last); +int msi_domain_alloc_irqs_range(struct device *dev, unsigned int domid, + unsigned int first, unsigned int last); +int msi_domain_alloc_irqs_all_locked(struct device *dev, unsigned int domid, int nirqs); + + void msi_domain_free_irqs_range_locked(struct device *dev, unsigned int domid, unsigned int first, unsigned int last); void msi_domain_free_irqs_range(struct device *dev, unsigned int domid, unsigned int first, unsigned int last); - void msi_domain_free_irqs_all_locked(struct device *dev, unsigned int domid); void msi_domain_free_irqs_all(struct device *dev, unsigned int domid); diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index c1ac780b2192..f857295304bd 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -24,11 +24,14 @@ * @domid: ID of the domain on which management operations should be done * @first: First (hardware) slot index to operate on * @last: Last (hardware) slot index to operate on + * @nirqs: The number of Linux interrupts to allocate. Can be larger + * than the range due to PCI/multi-MSI. */ struct msi_ctrl { unsigned int domid; unsigned int first; unsigned int last; + unsigned int nirqs; }; /* Invalid Xarray index which is outside of any searchable range */ @@ -36,6 +39,7 @@ struct msi_ctrl { /* The maximum domain size */ #define MSI_XA_DOMAIN_SIZE (MSI_MAX_INDEX + 1) +static void msi_domain_free_locked(struct device *dev, struct msi_ctrl *ctrl); static inline int msi_sysfs_create_group(struct device *dev); @@ -544,8 +548,6 @@ static inline int msi_sysfs_populate_desc(struct device *dev, struct msi_desc *d static inline void msi_sysfs_remove_desc(struct device *dev, struct msi_desc *desc) { } #endif /* !CONFIG_SYSFS */ -static int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, int nvec); - static struct irq_domain *msi_get_device_domain(struct device *dev, unsigned int domid) { struct irq_domain *domain; @@ -723,7 +725,6 @@ static struct msi_domain_ops msi_domain_ops_default = { .msi_init = msi_domain_ops_init, .msi_prepare = msi_domain_ops_prepare, .set_desc = msi_domain_ops_set_desc, - .domain_alloc_irqs = __msi_domain_alloc_irqs, }; static void msi_domain_update_dom_ops(struct msi_domain_info *info) @@ -735,9 +736,6 @@ static void msi_domain_update_dom_ops(struct msi_domain_info *info) return; } - if (ops->domain_alloc_irqs == NULL) - ops->domain_alloc_irqs = msi_domain_ops_default.domain_alloc_irqs; - if (!(info->flags & MSI_FLAG_USE_DEF_DOM_OPS)) return; @@ -951,18 +949,19 @@ static int msi_init_virq(struct irq_domain *domain, int virq, unsigned int vflag return 0; } -static int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, - int nvec) +static int __msi_domain_alloc_irqs(struct device *dev, struct irq_domain *domain, + struct msi_ctrl *ctrl) { + struct xarray *xa = &dev->msi.data->__domains[ctrl->domid].store; struct msi_domain_info *info = domain->host_data; struct msi_domain_ops *ops = info->ops; + unsigned int vflags = 0, allocated = 0; msi_alloc_info_t arg = { }; - unsigned int vflags = 0; struct msi_desc *desc; - int allocated = 0; + unsigned long idx; int i, ret, virq; - ret = msi_domain_prepare_irqs(domain, dev, nvec, &arg); + ret = msi_domain_prepare_irqs(domain, dev, ctrl->nirqs, &arg); if (ret) return ret; @@ -988,7 +987,14 @@ static int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev vflags |= VIRQ_NOMASK_QUIRK; } - msi_for_each_desc(desc, dev, MSI_DESC_NOTASSOCIATED) { + xa_for_each_range(xa, idx, desc, ctrl->first, ctrl->last) { + if (!msi_desc_match(desc, MSI_DESC_NOTASSOCIATED)) + continue; + + /* This should return -ECONFUSED... */ + if (WARN_ON_ONCE(allocated >= ctrl->nirqs)) + return -EINVAL; + ops->set_desc(&arg, desc); virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used, @@ -1016,17 +1022,122 @@ static int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev static int msi_domain_alloc_simple_msi_descs(struct device *dev, struct msi_domain_info *info, - unsigned int num_descs) + struct msi_ctrl *ctrl) +{ + if (!(info->flags & MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS)) + return 0; + + return msi_domain_add_simple_msi_descs(dev, ctrl); +} + +static int __msi_domain_alloc_locked(struct device *dev, struct msi_ctrl *ctrl) +{ + struct msi_domain_info *info; + struct msi_domain_ops *ops; + struct irq_domain *domain; + int ret; + + if (!msi_ctrl_valid(dev, ctrl)) + return -EINVAL; + + domain = msi_get_device_domain(dev, ctrl->domid); + if (!domain) + return -ENODEV; + + info = domain->host_data; + + ret = msi_domain_alloc_simple_msi_descs(dev, info, ctrl); + if (ret) + return ret; + + ops = info->ops; + if (ops->domain_alloc_irqs) + return ops->domain_alloc_irqs(domain, dev, ctrl->nirqs); + + return __msi_domain_alloc_irqs(dev, domain, ctrl); +} + +static int msi_domain_alloc_locked(struct device *dev, struct msi_ctrl *ctrl) +{ + int ret = __msi_domain_alloc_locked(dev, ctrl); + + if (ret) + msi_domain_free_locked(dev, ctrl); + return ret; +} + +/** + * msi_domain_alloc_irqs_range_locked - Allocate interrupts from a MSI interrupt domain + * @dev: Pointer to device struct of the device for which the interrupts + * are allocated + * @domid: Id of the interrupt domain to operate on + * @first: First index to allocate (inclusive) + * @last: Last index to allocate (inclusive) + * + * Must be invoked from within a msi_lock_descs() / msi_unlock_descs() + * pair. Use this for MSI irqdomains which implement their own descriptor + * allocation/free. + * + * Return: %0 on success or an error code. + */ +int msi_domain_alloc_irqs_range_locked(struct device *dev, unsigned int domid, + unsigned int first, unsigned int last) { struct msi_ctrl ctrl = { - .domid = MSI_DEFAULT_DOMAIN, - .last = num_descs - 1, + .domid = domid, + .first = first, + .last = last, + .nirqs = last + 1 - first, }; - if (!(info->flags & MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS)) - return 0; + return msi_domain_alloc_locked(dev, &ctrl); +} + +/** + * msi_domain_alloc_irqs_range - Allocate interrupts from a MSI interrupt domain + * @dev: Pointer to device struct of the device for which the interrupts + * are allocated + * @domid: Id of the interrupt domain to operate on + * @first: First index to allocate (inclusive) + * @last: Last index to allocate (inclusive) + * + * Return: %0 on success or an error code. + */ +int msi_domain_alloc_irqs_range(struct device *dev, unsigned int domid, + unsigned int first, unsigned int last) +{ + int ret; + + msi_lock_descs(dev); + ret = msi_domain_alloc_irqs_range_locked(dev, domid, first, last); + msi_unlock_descs(dev); + return ret; +} + +/** + * msi_domain_alloc_irqs_all_locked - Allocate all interrupts from a MSI interrupt domain + * + * @dev: Pointer to device struct of the device for which the interrupts + * are allocated + * @domid: Id of the interrupt domain to operate on + * @nirqs: The number of interrupts to allocate + * + * This function scans all MSI descriptors of the MSI domain and allocates interrupts + * for all unassigned ones. That function is to be used for MSI domain usage where + * the descriptor allocation is handled at the call site, e.g. PCI/MSI[X]. + * + * Return: %0 on success or an error code. + */ +int msi_domain_alloc_irqs_all_locked(struct device *dev, unsigned int domid, int nirqs) +{ + struct msi_ctrl ctrl = { + .domid = domid, + .first = 0, + .last = MSI_MAX_INDEX, + .nirqs = nirqs, + }; - return msi_domain_add_simple_msi_descs(dev, &ctrl); + return msi_domain_alloc_locked(dev, &ctrl); } /** @@ -1045,28 +1156,14 @@ static int msi_domain_alloc_simple_msi_descs(struct device *dev, int msi_domain_alloc_irqs_descs_locked(struct irq_domain *domain, struct device *dev, int nvec) { - struct msi_domain_info *info = domain->host_data; - struct msi_domain_ops *ops = info->ops; - int ret; - - lockdep_assert_held(&dev->msi.data->mutex); - - if (WARN_ON_ONCE(irq_domain_is_msi_parent(domain))) { - ret = -EINVAL; - goto free; - } - - /* Frees allocated descriptors in case of failure. */ - ret = msi_domain_alloc_simple_msi_descs(dev, info, nvec); - if (ret) - goto free; + struct msi_ctrl ctrl = { + .domid = MSI_DEFAULT_DOMAIN, + .first = 0, + .last = MSI_MAX_INDEX, + .nirqs = nvec, + }; - ret = ops->domain_alloc_irqs(domain, dev, nvec); - if (!ret) - return 0; -free: - msi_domain_free_irqs_descs_locked(domain, dev); - return ret; + return msi_domain_alloc_locked(dev, &ctrl); } /** -- cgit v1.2.3 From c459f11f32a022d0f97694030419d16816275a9d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:24:43 +0100 Subject: genirq/msi: Remove unused alloc/free interfaces Now that all users are converted remove the old interfaces. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124230314.694291814@linutronix.de --- include/linux/msi.h | 7 ----- kernel/irq/msi.c | 73 ----------------------------------------------------- 2 files changed, 80 deletions(-) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index 611707d619fc..43b8866c8431 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -487,13 +487,6 @@ int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask, struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode, struct msi_domain_info *info, struct irq_domain *parent); -int msi_domain_alloc_irqs_descs_locked(struct irq_domain *domain, struct device *dev, - int nvec); -int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, - int nvec); - -void msi_domain_free_irqs_descs_locked(struct irq_domain *domain, struct device *dev); -void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev); int msi_domain_alloc_irqs_range_locked(struct device *dev, unsigned int domid, unsigned int first, unsigned int last); diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index f857295304bd..8e653f089f82 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -1140,51 +1140,6 @@ int msi_domain_alloc_irqs_all_locked(struct device *dev, unsigned int domid, int return msi_domain_alloc_locked(dev, &ctrl); } -/** - * msi_domain_alloc_irqs_descs_locked - Allocate interrupts from a MSI interrupt domain - * @domain: The domain to allocate from - * @dev: Pointer to device struct of the device for which the interrupts - * are allocated - * @nvec: The number of interrupts to allocate - * - * Must be invoked from within a msi_lock_descs() / msi_unlock_descs() - * pair. Use this for MSI irqdomains which implement their own vector - * allocation/free. - * - * Return: %0 on success or an error code. - */ -int msi_domain_alloc_irqs_descs_locked(struct irq_domain *domain, struct device *dev, - int nvec) -{ - struct msi_ctrl ctrl = { - .domid = MSI_DEFAULT_DOMAIN, - .first = 0, - .last = MSI_MAX_INDEX, - .nirqs = nvec, - }; - - return msi_domain_alloc_locked(dev, &ctrl); -} - -/** - * msi_domain_alloc_irqs - Allocate interrupts from a MSI interrupt domain - * @domain: The domain to allocate from - * @dev: Pointer to device struct of the device for which the interrupts - * are allocated - * @nvec: The number of interrupts to allocate - * - * Return: %0 on success or an error code. - */ -int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, int nvec) -{ - int ret; - - msi_lock_descs(dev); - ret = msi_domain_alloc_irqs_descs_locked(domain, dev, nvec); - msi_unlock_descs(dev); - return ret; -} - static void __msi_domain_free_irqs(struct device *dev, struct irq_domain *domain, struct msi_ctrl *ctrl) { @@ -1309,34 +1264,6 @@ void msi_domain_free_irqs_all(struct device *dev, unsigned int domid) msi_unlock_descs(dev); } -/** - * msi_domain_free_irqs_descs_locked - Free interrupts from a MSI interrupt @domain associated to @dev - * @domain: The domain to managing the interrupts - * @dev: Pointer to device struct of the device for which the interrupts - * are free - * - * Must be invoked from within a msi_lock_descs() / msi_unlock_descs() - * pair. Use this for MSI irqdomains which implement their own vector - * allocation. - */ -void msi_domain_free_irqs_descs_locked(struct irq_domain *domain, struct device *dev) -{ - msi_domain_free_irqs_range_locked(dev, MSI_DEFAULT_DOMAIN, 0, MSI_MAX_INDEX); -} - -/** - * msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated to @dev - * @domain: The domain to managing the interrupts - * @dev: Pointer to device struct of the device for which the interrupts - * are free - */ -void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev) -{ - msi_lock_descs(dev); - msi_domain_free_irqs_descs_locked(domain, dev); - msi_unlock_descs(dev); -} - /** * msi_get_domain_info - Get the MSI interrupt domain info for @domain * @domain: The interrupt domain to retrieve data from -- cgit v1.2.3 From b78780d93b068706d04f8f2f02bd08db5da01479 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:25:48 +0100 Subject: genirq/msi: Provide struct msi_parent_ops MSI parent domains must have some control over the MSI domains which are built on top. On domain creation they need to fill in e.g. architecture specific chip callbacks or msi domain ops to make the outermost domain parent agnostic which is obviously required for architecture independence etc. The structure contains: 1) A bitfield which exposes the supported functional features. This allows to check for features and is also used in the initialization callback to mask out unsupported features when the actual domain implementation requests a broader range, e.g. on x86 PCI multi-MSI is only supported by remapping domains but not by the underlying vector domain. The PCI/MSI code can then always request multi-MSI support, but the resulting feature set after creation might not have it set. 2) An optional string prefix which is put in front of domain and chip names during creation of the MSI domain. That allows to keep the naming schemes e.g. on x86 where PCI-MSI domains have a IR- prefix when interrupt remapping is enabled. 3) An initialization callback to sanity check the domain info of the to be created MSI domain, to restrict features and to apply changes in MSI ops and interrupt chip callbacks to accomodate to the particular MSI parent implementation and/or the underlying hierarchy. Add a conveniance function to delegate the initialization from the MSI parent domain to an underlying domain in the hierarchy. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124232325.382485843@linutronix.de --- include/linux/irqdomain.h | 5 +++++ include/linux/msi.h | 21 +++++++++++++++++++++ kernel/irq/msi.c | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) (limited to 'kernel') diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 24b76688c2fb..a668cc015874 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -46,6 +46,7 @@ struct irq_desc; struct cpumask; struct seq_file; struct irq_affinity_desc; +struct msi_parent_ops; #define IRQ_DOMAIN_IRQ_SPEC_PARAMS 16 @@ -134,6 +135,7 @@ struct irq_domain_chip_generic; * @pm_dev: Pointer to a device that can be utilized for power management * purposes related to the irq domain. * @parent: Pointer to parent irq_domain to support hierarchy irq_domains + * @msi_parent_ops: Pointer to MSI parent domain methods for per device domain init * * Revmap data, used internally by the irq domain code: * @revmap_size: Size of the linear map table @revmap[] @@ -157,6 +159,9 @@ struct irq_domain { #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY struct irq_domain *parent; #endif +#ifdef CONFIG_GENERIC_MSI_IRQ + const struct msi_parent_ops *msi_parent_ops; +#endif /* reverse map data. The linear map gets appended to the irq_domain */ irq_hw_number_t hwirq_max; diff --git a/include/linux/msi.h b/include/linux/msi.h index a4339ebcc035..9bf3cbad1114 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -500,6 +500,27 @@ enum { }; +/** + * struct msi_parent_ops - MSI parent domain callbacks and configuration info + * + * @supported_flags: Required: The supported MSI flags of the parent domain + * @prefix: Optional: Prefix for the domain and chip name + * @init_dev_msi_info: Required: Callback for MSI parent domains to setup parent + * domain specific domain flags, domain ops and interrupt chip + * callbacks when a per device domain is created. + */ +struct msi_parent_ops { + u32 supported_flags; + const char *prefix; + bool (*init_dev_msi_info)(struct device *dev, struct irq_domain *domain, + struct irq_domain *msi_parent_domain, + struct msi_domain_info *msi_child_info); +}; + +bool msi_parent_init_dev_msi_info(struct device *dev, struct irq_domain *domain, + struct irq_domain *msi_parent_domain, + struct msi_domain_info *msi_child_info); + int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force); diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 8e653f089f82..c368116e8b08 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -788,6 +788,47 @@ struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode, return domain; } +/** + * msi_parent_init_dev_msi_info - Delegate initialization of device MSI info down + * in the domain hierarchy + * @dev: The device for which the domain should be created + * @domain: The domain in the hierarchy this op is being called on + * @msi_parent_domain: The IRQ_DOMAIN_FLAG_MSI_PARENT domain for the child to + * be created + * @msi_child_info: The MSI domain info of the IRQ_DOMAIN_FLAG_MSI_DEVICE + * domain to be created + * + * Return: true on success, false otherwise + * + * This is the most complex problem of per device MSI domains and the + * underlying interrupt domain hierarchy: + * + * The device domain to be initialized requests the broadest feature set + * possible and the underlying domain hierarchy puts restrictions on it. + * + * That's trivial for a simple parent->child relationship, but it gets + * interesting with an intermediate domain: root->parent->child. The + * intermediate 'parent' can expand the capabilities which the 'root' + * domain is providing. So that creates a classic hen and egg problem: + * Which entity is doing the restrictions/expansions? + * + * One solution is to let the root domain handle the initialization that's + * why there is the @domain and the @msi_parent_domain pointer. + */ +bool msi_parent_init_dev_msi_info(struct device *dev, struct irq_domain *domain, + struct irq_domain *msi_parent_domain, + struct msi_domain_info *msi_child_info) +{ + struct irq_domain *parent = domain->parent; + + if (WARN_ON_ONCE(!parent || !parent->msi_parent_ops || + !parent->msi_parent_ops->init_dev_msi_info)) + return false; + + return parent->msi_parent_ops->init_dev_msi_info(dev, parent, msi_parent_domain, + msi_child_info); +} + int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev, int nvec, msi_alloc_info_t *arg) { -- cgit v1.2.3 From 61bf992fc618503c910416f28afa0b015838b72b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:25:51 +0100 Subject: genirq/msi: Add size info to struct msi_domain_info To allow proper range checking especially for dynamic allocations add a size field to struct msi_domain_info. If the field is 0 then the size is unknown or unlimited (up to MSI_MAX_INDEX) to provide backwards compability. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124232325.501144862@linutronix.de --- include/linux/msi.h | 5 +++++ kernel/irq/msi.c | 11 +++++++++++ 2 files changed, 16 insertions(+) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index 7fb87371541a..08a0e2abf048 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -422,6 +422,10 @@ struct msi_domain_ops { * struct msi_domain_info - MSI interrupt domain data * @flags: Flags to decribe features and capabilities * @bus_token: The domain bus token + * @hwsize: The hardware table size or the software index limit. + * If 0 then the size is considered unlimited and + * gets initialized to the maximum software index limit + * by the domain creation code. * @ops: The callback data structure * @chip: Optional: associated interrupt chip * @chip_data: Optional: associated interrupt chip data @@ -433,6 +437,7 @@ struct msi_domain_ops { struct msi_domain_info { u32 flags; enum irq_domain_bus_token bus_token; + unsigned int hwsize; struct msi_domain_ops *ops; struct irq_chip *chip; void *chip_data; diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index c368116e8b08..0a3890598b75 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -772,6 +772,17 @@ struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode, { struct irq_domain *domain; + if (info->hwsize > MSI_XA_DOMAIN_SIZE) + return NULL; + + /* + * Hardware size 0 is valid for backwards compatibility and for + * domains which are not backed by a hardware table. Grant the + * maximum index space. + */ + if (!info->hwsize) + info->hwsize = MSI_XA_DOMAIN_SIZE; + msi_domain_update_dom_ops(info); if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) msi_domain_update_chip_ops(info); -- cgit v1.2.3 From a80c0aceeaffdb3afe9536fe747480e85841da7f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:25:52 +0100 Subject: genirq/msi: Split msi_create_irq_domain() Split the functionality of msi_create_irq_domain() so it can be reused for creating per device irq domains. No functional change. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124232325.559086358@linutronix.de --- kernel/irq/msi.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'kernel') diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 0a3890598b75..0f7fe562b307 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -758,17 +758,10 @@ static void msi_domain_update_chip_ops(struct msi_domain_info *info) chip->irq_set_affinity = msi_domain_set_affinity; } -/** - * msi_create_irq_domain - Create an MSI interrupt domain - * @fwnode: Optional fwnode of the interrupt controller - * @info: MSI domain info - * @parent: Parent irq domain - * - * Return: pointer to the created &struct irq_domain or %NULL on failure - */ -struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode, - struct msi_domain_info *info, - struct irq_domain *parent) +static struct irq_domain *__msi_create_irq_domain(struct fwnode_handle *fwnode, + struct msi_domain_info *info, + unsigned int flags, + struct irq_domain *parent) { struct irq_domain *domain; @@ -787,7 +780,7 @@ struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode, if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS) msi_domain_update_chip_ops(info); - domain = irq_domain_create_hierarchy(parent, IRQ_DOMAIN_FLAG_MSI, 0, + domain = irq_domain_create_hierarchy(parent, flags | IRQ_DOMAIN_FLAG_MSI, 0, fwnode, &msi_domain_ops, info); if (domain) { @@ -799,6 +792,21 @@ struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode, return domain; } +/** + * msi_create_irq_domain - Create an MSI interrupt domain + * @fwnode: Optional fwnode of the interrupt controller + * @info: MSI domain info + * @parent: Parent irq domain + * + * Return: pointer to the created &struct irq_domain or %NULL on failure + */ +struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode, + struct msi_domain_info *info, + struct irq_domain *parent) +{ + return __msi_create_irq_domain(fwnode, info, 0, parent); +} + /** * msi_parent_init_dev_msi_info - Delegate initialization of device MSI info down * in the domain hierarchy -- cgit v1.2.3 From 27a6dea3ebaab3d6f8ded969ec3af710bcbe0c02 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:25:56 +0100 Subject: genirq/msi: Provide msi_create/free_device_irq_domain() Now that all prerequsites are in place, provide the actual interfaces for creating and removing per device interrupt domains. MSI device interrupt domains are created from the provided msi_domain_template which is duplicated so that it can be modified for the particular device. The name of the domain and the name of the interrupt chip are composed by "$(PREFIX)$(CHIPNAME)-$(DEVNAME)" $PREFIX: The optional prefix provided by the underlying MSI parent domain via msi_parent_ops::prefix. $CHIPNAME: The name of the irq_chip in the template $DEVNAME: The name of the device The domain is further initialized through a MSI parent domain callback which fills in the required functionality for the parent domain or domains further down the hierarchy. This initialization can fail, e.g. when the requested feature or MSI domain type cannot be supported. The domain pointer is stored in the pointer array inside of msi_device_data which is attached to the domain. The domain can be removed via the API or left for disposal via devres when the device is torn down. The API removal is useful e.g. for PCI to have seperate domains for MSI and MSI-X, which are mutually exclusive and always occupy the default domain id slot. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124232325.678838546@linutronix.de --- include/linux/msi.h | 6 +++ kernel/irq/msi.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index 08a0e2abf048..ef46a3ee18e4 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -547,6 +547,12 @@ struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode, struct msi_domain_info *info, struct irq_domain *parent); +bool msi_create_device_irq_domain(struct device *dev, unsigned int domid, + const struct msi_domain_template *template, + unsigned int hwsize, void *domain_data, + void *chip_data); +void msi_remove_device_irq_domain(struct device *dev, unsigned int domid); + int msi_domain_alloc_irqs_range_locked(struct device *dev, unsigned int domid, unsigned int first, unsigned int last); int msi_domain_alloc_irqs_range(struct device *dev, unsigned int domid, diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 0f7fe562b307..8b415bdb0425 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -240,6 +240,7 @@ static void msi_device_data_release(struct device *dev, void *res) int i; for (i = 0; i < MSI_MAX_DEVICE_IRQDOMAINS; i++) { + msi_remove_device_irq_domain(dev, i); WARN_ON_ONCE(!xa_empty(&md->__domains[i].store)); xa_destroy(&md->__domains[i].store); } @@ -848,6 +849,143 @@ bool msi_parent_init_dev_msi_info(struct device *dev, struct irq_domain *domain, msi_child_info); } +/** + * msi_create_device_irq_domain - Create a device MSI interrupt domain + * @dev: Pointer to the device + * @domid: Domain id + * @template: MSI domain info bundle used as template + * @hwsize: Maximum number of MSI table entries (0 if unknown or unlimited) + * @domain_data: Optional pointer to domain specific data which is set in + * msi_domain_info::data + * @chip_data: Optional pointer to chip specific data which is set in + * msi_domain_info::chip_data + * + * Return: True on success, false otherwise + * + * There is no firmware node required for this interface because the per + * device domains are software constructs which are actually closer to the + * hardware reality than any firmware can describe them. + * + * The domain name and the irq chip name for a MSI device domain are + * composed by: "$(PREFIX)$(CHIPNAME)-$(DEVNAME)" + * + * $PREFIX: Optional prefix provided by the underlying MSI parent domain + * via msi_parent_ops::prefix. If that pointer is NULL the prefix + * is empty. + * $CHIPNAME: The name of the irq_chip in @template + * $DEVNAME: The name of the device + * + * This results in understandable chip names and hardware interrupt numbers + * in e.g. /proc/interrupts + * + * PCI-MSI-0000:00:1c.0 0-edge Parent domain has no prefix + * IR-PCI-MSI-0000:00:1c.4 0-edge Same with interrupt remapping prefix 'IR-' + * + * IR-PCI-MSIX-0000:3d:00.0 0-edge Hardware interrupt numbers reflect + * IR-PCI-MSIX-0000:3d:00.0 1-edge the real MSI-X index on that device + * IR-PCI-MSIX-0000:3d:00.0 2-edge + * + * On IMS domains the hardware interrupt number is either a table entry + * index or a purely software managed index but it is guaranteed to be + * unique. + * + * The domain pointer is stored in @dev::msi::data::__irqdomains[]. All + * subsequent operations on the domain depend on the domain id. + * + * The domain is automatically freed when the device is removed via devres + * in the context of @dev::msi::data freeing, but it can also be + * independently removed via @msi_remove_device_irq_domain(). + */ +bool msi_create_device_irq_domain(struct device *dev, unsigned int domid, + const struct msi_domain_template *template, + unsigned int hwsize, void *domain_data, + void *chip_data) +{ + struct irq_domain *domain, *parent = dev->msi.domain; + const struct msi_parent_ops *pops; + struct msi_domain_template *bundle; + struct fwnode_handle *fwnode; + + if (!irq_domain_is_msi_parent(parent)) + return false; + + if (domid >= MSI_MAX_DEVICE_IRQDOMAINS) + return false; + + bundle = kmemdup(template, sizeof(*bundle), GFP_KERNEL); + if (!bundle) + return false; + + bundle->info.hwsize = hwsize; + bundle->info.chip = &bundle->chip; + bundle->info.ops = &bundle->ops; + bundle->info.data = domain_data; + bundle->info.chip_data = chip_data; + + pops = parent->msi_parent_ops; + snprintf(bundle->name, sizeof(bundle->name), "%s%s-%s", + pops->prefix ? : "", bundle->chip.name, dev_name(dev)); + bundle->chip.name = bundle->name; + + fwnode = irq_domain_alloc_named_fwnode(bundle->name); + if (!fwnode) + goto free_bundle; + + if (msi_setup_device_data(dev)) + goto free_fwnode; + + msi_lock_descs(dev); + + if (WARN_ON_ONCE(msi_get_device_domain(dev, domid))) + goto fail; + + if (!pops->init_dev_msi_info(dev, parent, parent, &bundle->info)) + goto fail; + + domain = __msi_create_irq_domain(fwnode, &bundle->info, IRQ_DOMAIN_FLAG_MSI_DEVICE, parent); + if (!domain) + goto fail; + + domain->dev = dev; + dev->msi.data->__domains[domid].domain = domain; + msi_unlock_descs(dev); + return true; + +fail: + msi_unlock_descs(dev); +free_fwnode: + kfree(fwnode); +free_bundle: + kfree(bundle); + return false; +} + +/** + * msi_remove_device_irq_domain - Free a device MSI interrupt domain + * @dev: Pointer to the device + * @domid: Domain id + */ +void msi_remove_device_irq_domain(struct device *dev, unsigned int domid) +{ + struct msi_domain_info *info; + struct irq_domain *domain; + + msi_lock_descs(dev); + + domain = msi_get_device_domain(dev, domid); + + if (!domain || !irq_domain_is_msi_device(domain)) + goto unlock; + + dev->msi.data->__domains[domid].domain = NULL; + info = domain->host_data; + irq_domain_remove(domain); + kfree(container_of(info, struct msi_domain_template, info)); + +unlock: + msi_unlock_descs(dev); +} + int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev, int nvec, msi_alloc_info_t *arg) { -- cgit v1.2.3 From 26e91b75bf6108550035355c835bf0c93c885b61 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:25:57 +0100 Subject: genirq/msi: Provide msi_match_device_domain() Provide an interface to match a per device domain bus token. This allows to query which type of domain is installed for a particular domain id. Will be used for PCI to avoid frequent create/remove cycles for the MSI resp. MSI-X domains. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124232325.738047902@linutronix.de --- include/linux/msi.h | 3 +++ kernel/irq/msi.c | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index ef46a3ee18e4..b4ab00562a29 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -553,6 +553,9 @@ bool msi_create_device_irq_domain(struct device *dev, unsigned int domid, void *chip_data); void msi_remove_device_irq_domain(struct device *dev, unsigned int domid); +bool msi_match_device_irq_domain(struct device *dev, unsigned int domid, + enum irq_domain_bus_token bus_token); + int msi_domain_alloc_irqs_range_locked(struct device *dev, unsigned int domid, unsigned int first, unsigned int last); int msi_domain_alloc_irqs_range(struct device *dev, unsigned int domid, diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 8b415bdb0425..74499980cfc2 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -986,6 +986,31 @@ unlock: msi_unlock_descs(dev); } +/** + * msi_match_device_irq_domain - Match a device irq domain against a bus token + * @dev: Pointer to the device + * @domid: Domain id + * @bus_token: Bus token to match against the domain bus token + * + * Return: True if device domain exists and bus tokens match. + */ +bool msi_match_device_irq_domain(struct device *dev, unsigned int domid, + enum irq_domain_bus_token bus_token) +{ + struct msi_domain_info *info; + struct irq_domain *domain; + bool ret = false; + + msi_lock_descs(dev); + domain = msi_get_device_domain(dev, domid); + if (domain && irq_domain_is_msi_device(domain)) { + info = domain->host_data; + ret = info->bus_token == bus_token; + } + msi_unlock_descs(dev); + return ret; +} + int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev, int nvec, msi_alloc_info_t *arg) { -- cgit v1.2.3 From 36db3d9003ea85217b357a658cf7b37920c2c38e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:25:59 +0100 Subject: genirq/msi: Add range checking to msi_insert_desc() Per device domains provide the real domain size to the core code. This allows range checking on insertion of MSI descriptors and also paves the way for dynamic index allocations which are required e.g. for IMS. This avoids external mechanisms like bitmaps on the device side and just utilizes the core internal MSI descriptor storxe for it. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124232325.798556374@linutronix.de --- kernel/irq/msi.c | 53 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 10 deletions(-) (limited to 'kernel') diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 74499980cfc2..ae5869222fe5 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -40,6 +40,7 @@ struct msi_ctrl { #define MSI_XA_DOMAIN_SIZE (MSI_MAX_INDEX + 1) static void msi_domain_free_locked(struct device *dev, struct msi_ctrl *ctrl); +static unsigned int msi_domain_get_hwsize(struct device *dev, unsigned int domid); static inline int msi_sysfs_create_group(struct device *dev); @@ -80,16 +81,28 @@ static void msi_free_desc(struct msi_desc *desc) kfree(desc); } -static int msi_insert_desc(struct msi_device_data *md, struct msi_desc *desc, +static int msi_insert_desc(struct device *dev, struct msi_desc *desc, unsigned int domid, unsigned int index) { + struct msi_device_data *md = dev->msi.data; struct xarray *xa = &md->__domains[domid].store; + unsigned int hwsize; int ret; + hwsize = msi_domain_get_hwsize(dev, domid); + if (index >= hwsize) { + ret = -ERANGE; + goto fail; + } + desc->msi_index = index; ret = xa_insert(xa, index, desc, GFP_KERNEL); if (ret) - msi_free_desc(desc); + goto fail; + return 0; + +fail: + msi_free_desc(desc); return ret; } @@ -117,7 +130,7 @@ int msi_domain_insert_msi_desc(struct device *dev, unsigned int domid, /* Copy type specific data to the new descriptor. */ desc->pci = init_desc->pci; - return msi_insert_desc(dev->msi.data, desc, domid, init_desc->msi_index); + return msi_insert_desc(dev, desc, domid, init_desc->msi_index); } static bool msi_desc_match(struct msi_desc *desc, enum msi_desc_filter filter) @@ -136,11 +149,16 @@ static bool msi_desc_match(struct msi_desc *desc, enum msi_desc_filter filter) static bool msi_ctrl_valid(struct device *dev, struct msi_ctrl *ctrl) { + unsigned int hwsize; + if (WARN_ON_ONCE(ctrl->domid >= MSI_MAX_DEVICE_IRQDOMAINS || - !dev->msi.data->__domains[ctrl->domid].domain || - ctrl->first > ctrl->last || - ctrl->first > MSI_MAX_INDEX || - ctrl->last > MSI_MAX_INDEX)) + !dev->msi.data->__domains[ctrl->domid].domain)) + return false; + + hwsize = msi_domain_get_hwsize(dev, ctrl->domid); + if (WARN_ON_ONCE(ctrl->first > ctrl->last || + ctrl->first >= hwsize || + ctrl->last >= hwsize)) return false; return true; } @@ -208,7 +226,7 @@ static int msi_domain_add_simple_msi_descs(struct device *dev, struct msi_ctrl * desc = msi_alloc_desc(dev, 1, NULL); if (!desc) goto fail_mem; - ret = msi_insert_desc(dev->msi.data, desc, ctrl->domid, idx); + ret = msi_insert_desc(dev, desc, ctrl->domid, idx); if (ret) goto fail; } @@ -568,6 +586,20 @@ static struct irq_domain *msi_get_device_domain(struct device *dev, unsigned int return domain; } +static unsigned int msi_domain_get_hwsize(struct device *dev, unsigned int domid) +{ + struct msi_domain_info *info; + struct irq_domain *domain; + + domain = msi_get_device_domain(dev, domid); + if (domain) { + info = domain->host_data; + return info->hwsize; + } + /* No domain, no size... */ + return 0; +} + static inline void irq_chip_write_msi_msg(struct irq_data *data, struct msi_msg *msg) { @@ -1356,7 +1388,7 @@ int msi_domain_alloc_irqs_all_locked(struct device *dev, unsigned int domid, int struct msi_ctrl ctrl = { .domid = domid, .first = 0, - .last = MSI_MAX_INDEX, + .last = msi_domain_get_hwsize(dev, domid) - 1, .nirqs = nirqs, }; @@ -1470,7 +1502,8 @@ void msi_domain_free_irqs_range(struct device *dev, unsigned int domid, */ void msi_domain_free_irqs_all_locked(struct device *dev, unsigned int domid) { - msi_domain_free_irqs_range_locked(dev, domid, 0, MSI_MAX_INDEX); + msi_domain_free_irqs_range_locked(dev, domid, 0, + msi_domain_get_hwsize(dev, domid) - 1); } /** -- cgit v1.2.3 From bd141a3db40c877e01de8e981edb57c03199d876 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:26:02 +0100 Subject: genirq/msi: Provide BUS_DEVICE_PCI_MSI[X] Provide new bus tokens for the upcoming per device PCI/MSI and PCI/MSIX interrupt domains. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124232325.917219885@linutronix.de --- include/linux/irqdomain_defs.h | 2 ++ kernel/irq/msi.c | 4 ++++ 2 files changed, 6 insertions(+) (limited to 'kernel') diff --git a/include/linux/irqdomain_defs.h b/include/linux/irqdomain_defs.h index 69035b4f740a..b3f4b7ef31f1 100644 --- a/include/linux/irqdomain_defs.h +++ b/include/linux/irqdomain_defs.h @@ -21,6 +21,8 @@ enum irq_domain_bus_token { DOMAIN_BUS_TI_SCI_INTA_MSI, DOMAIN_BUS_WAKEUP, DOMAIN_BUS_VMD_MSI, + DOMAIN_BUS_PCI_DEVICE_MSI, + DOMAIN_BUS_PCI_DEVICE_MSIX, }; #endif /* _LINUX_IRQDOMAIN_DEFS_H */ diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index ae5869222fe5..0749e661e459 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -1118,6 +1118,8 @@ static bool msi_check_reservation_mode(struct irq_domain *domain, switch(domain->bus_token) { case DOMAIN_BUS_PCI_MSI: + case DOMAIN_BUS_PCI_DEVICE_MSI: + case DOMAIN_BUS_PCI_DEVICE_MSIX: case DOMAIN_BUS_VMD_MSI: break; default: @@ -1143,6 +1145,8 @@ static int msi_handle_pci_fail(struct irq_domain *domain, struct msi_desc *desc, { switch(domain->bus_token) { case DOMAIN_BUS_PCI_MSI: + case DOMAIN_BUS_PCI_DEVICE_MSI: + case DOMAIN_BUS_PCI_DEVICE_MSIX: case DOMAIN_BUS_VMD_MSI: if (IS_ENABLED(CONFIG_PCI_MSI)) break; -- cgit v1.2.3 From 8f986fd7755bec8b8c5776824afa1bd1151986d9 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:26:16 +0100 Subject: genirq/msi: Provide msi_domain_ops:: Prepare_desc() The existing MSI domain ops msi_prepare() and set_desc() turned out to be unsuitable for implementing IMS support. msi_prepare() does not operate on the MSI descriptors. set_desc() lacks an irq_domain pointer and has a completely different purpose. Introduce a prepare_desc() op which allows IMS implementations to amend an MSI descriptor which was allocated by the core code, e.g. by adjusting the iomem base or adding some data based on the allocated index. This is way better than requiring that all IMS domain implementations preallocate the MSI descriptor and then allocate the interrupt. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124232326.444560717@linutronix.de --- include/linux/msi.h | 6 +++++- kernel/irq/msi.c | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index dca3b801428f..cb0bee3f43a7 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -410,6 +410,8 @@ struct msi_domain_info; * @msi_init: Domain specific init function for MSI interrupts * @msi_free: Domain specific function to free a MSI interrupts * @msi_prepare: Prepare the allocation of the interrupts in the domain + * @prepare_desc: Optional function to prepare the allocated MSI descriptor + * in the domain * @set_desc: Set the msi descriptor for an interrupt * @domain_alloc_irqs: Optional function to override the default allocation * function. @@ -421,7 +423,7 @@ struct msi_domain_info; * @get_hwirq, @msi_init and @msi_free are callbacks used by the underlying * irqdomain. * - * @msi_check, @msi_prepare and @set_desc are callbacks used by the + * @msi_check, @msi_prepare, @prepare_desc and @set_desc are callbacks used by the * msi_domain_alloc/free_irqs*() variants. * * @domain_alloc_irqs, @domain_free_irqs can be used to override the @@ -444,6 +446,8 @@ struct msi_domain_ops { int (*msi_prepare)(struct irq_domain *domain, struct device *dev, int nvec, msi_alloc_info_t *arg); + void (*prepare_desc)(struct irq_domain *domain, msi_alloc_info_t *arg, + struct msi_desc *desc); void (*set_desc)(msi_alloc_info_t *arg, struct msi_desc *desc); int (*domain_alloc_irqs)(struct irq_domain *domain, diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 0749e661e459..6370ea53ff4a 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -1254,6 +1254,9 @@ static int __msi_domain_alloc_irqs(struct device *dev, struct irq_domain *domain if (WARN_ON_ONCE(allocated >= ctrl->nirqs)) return -EINVAL; + if (ops->prepare_desc) + ops->prepare_desc(domain, &arg, desc); + ops->set_desc(&arg, desc); virq = __irq_domain_alloc_irqs(domain, -1, desc->nvec_used, -- cgit v1.2.3 From 3d393b21740bffbeeae7d4fa534a6b16c3e3e832 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 25 Nov 2022 00:26:18 +0100 Subject: genirq/msi: Provide msi_domain_alloc_irq_at() For supporting post MSI-X enable allocations and for the upcoming PCI/IMS support a separate interface is required which allows not only the allocation of a specific index, but also the allocation of any, i.e. the next free index. The latter is especially required for IMS because IMS completely does away with index to functionality mappings which are often found in MSI/MSI-X implementation. But even with MSI-X there are devices where only the first few indices have a fixed functionality and the rest is freely assignable by software, e.g. to queues. msi_domain_alloc_irq_at() is also different from the range based interfaces as it always enforces that the MSI descriptor is allocated by the core code and not preallocated by the caller like the PCI/MSI[-X] enable code path does. msi_domain_alloc_irq_at() can be invoked with the index argument set to MSI_ANY_INDEX which makes the core code pick the next free index. The irq domain can provide a prepare_desc() operation callback in it's msi_domain_ops to do domain specific post allocation initialization before the actual Linux interrupt and the associated interrupt descriptor and hierarchy alloccations are conducted. The function also takes an optional @icookie argument which is of type union msi_instance_cookie. This cookie is not used by the core code and is stored in the allocated msi_desc::data::icookie. The meaning of the cookie is completely implementation defined. In case of IMS this might be a PASID or a pointer to a device queue, but for the MSI core it's opaque and not used in any way. The function returns a struct msi_map which on success contains the allocated index number and the Linux interrupt number so the caller can spare the index to Linux interrupt number lookup. On failure map::index contains the error code and map::virq is 0. Signed-off-by: Thomas Gleixner Reviewed-by: Kevin Tian Acked-by: Marc Zyngier Link: https://lore.kernel.org/r/20221124232326.501359457@linutronix.de --- include/linux/msi.h | 4 ++ include/linux/msi_api.h | 7 ++++ kernel/irq/msi.c | 105 +++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 106 insertions(+), 10 deletions(-) (limited to 'kernel') diff --git a/include/linux/msi.h b/include/linux/msi.h index cb0bee3f43a7..00c501979ff1 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -80,6 +80,7 @@ struct pci_dev; struct platform_msi_priv_data; struct device_attribute; struct irq_domain; +struct irq_affinity_desc; void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg); #ifdef CONFIG_GENERIC_MSI_IRQ @@ -602,6 +603,9 @@ int msi_domain_alloc_irqs_range(struct device *dev, unsigned int domid, unsigned int first, unsigned int last); int msi_domain_alloc_irqs_all_locked(struct device *dev, unsigned int domid, int nirqs); +struct msi_map msi_domain_alloc_irq_at(struct device *dev, unsigned int domid, unsigned int index, + const struct irq_affinity_desc *affdesc, + union msi_instance_cookie *cookie); void msi_domain_free_irqs_range_locked(struct device *dev, unsigned int domid, unsigned int first, unsigned int last); diff --git a/include/linux/msi_api.h b/include/linux/msi_api.h index 2e4456ec9f6b..5ae72d1912c4 100644 --- a/include/linux/msi_api.h +++ b/include/linux/msi_api.h @@ -48,6 +48,13 @@ struct msi_map { int virq; }; +/* + * Constant to be used for dynamic allocations when the allocation is any + * free MSI index, which is either an entry in a hardware table or a + * software managed index. + */ +#define MSI_ANY_INDEX UINT_MAX + unsigned int msi_domain_get_virq(struct device *dev, unsigned int domid, unsigned int index); /** diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 6370ea53ff4a..bd4d4dd626b4 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -90,17 +90,30 @@ static int msi_insert_desc(struct device *dev, struct msi_desc *desc, int ret; hwsize = msi_domain_get_hwsize(dev, domid); - if (index >= hwsize) { - ret = -ERANGE; - goto fail; - } - desc->msi_index = index; - ret = xa_insert(xa, index, desc, GFP_KERNEL); - if (ret) - goto fail; - return 0; + if (index == MSI_ANY_INDEX) { + struct xa_limit limit = { .min = 0, .max = hwsize - 1 }; + unsigned int index; + /* Let the xarray allocate a free index within the limit */ + ret = xa_alloc(xa, &index, desc, limit, GFP_KERNEL); + if (ret) + goto fail; + + desc->msi_index = index; + return 0; + } else { + if (index >= hwsize) { + ret = -ERANGE; + goto fail; + } + + desc->msi_index = index; + ret = xa_insert(xa, index, desc, GFP_KERNEL); + if (ret) + goto fail; + return 0; + } fail: msi_free_desc(desc); return ret; @@ -294,7 +307,7 @@ int msi_setup_device_data(struct device *dev) } for (i = 0; i < MSI_MAX_DEVICE_IRQDOMAINS; i++) - xa_init(&md->__domains[i].store); + xa_init_flags(&md->__domains[i].store, XA_FLAGS_ALLOC); /* * If @dev::msi::domain is set and is a global MSI domain, copy the @@ -1402,6 +1415,78 @@ int msi_domain_alloc_irqs_all_locked(struct device *dev, unsigned int domid, int return msi_domain_alloc_locked(dev, &ctrl); } +/** + * msi_domain_alloc_irq_at - Allocate an interrupt from a MSI interrupt domain at + * a given index - or at the next free index + * + * @dev: Pointer to device struct of the device for which the interrupts + * are allocated + * @domid: Id of the interrupt domain to operate on + * @index: Index for allocation. If @index == %MSI_ANY_INDEX the allocation + * uses the next free index. + * @affdesc: Optional pointer to an interrupt affinity descriptor structure + * @icookie: Optional pointer to a domain specific per instance cookie. If + * non-NULL the content of the cookie is stored in msi_desc::data. + * Must be NULL for MSI-X allocations + * + * This requires a MSI interrupt domain which lets the core code manage the + * MSI descriptors. + * + * Return: struct msi_map + * + * On success msi_map::index contains the allocated index number and + * msi_map::virq the corresponding Linux interrupt number + * + * On failure msi_map::index contains the error code and msi_map::virq + * is %0. + */ +struct msi_map msi_domain_alloc_irq_at(struct device *dev, unsigned int domid, unsigned int index, + const struct irq_affinity_desc *affdesc, + union msi_instance_cookie *icookie) +{ + struct msi_ctrl ctrl = { .domid = domid, .nirqs = 1, }; + struct irq_domain *domain; + struct msi_map map = { }; + struct msi_desc *desc; + int ret; + + msi_lock_descs(dev); + domain = msi_get_device_domain(dev, domid); + if (!domain) { + map.index = -ENODEV; + goto unlock; + } + + desc = msi_alloc_desc(dev, 1, affdesc); + if (!desc) { + map.index = -ENOMEM; + goto unlock; + } + + if (icookie) + desc->data.icookie = *icookie; + + ret = msi_insert_desc(dev, desc, domid, index); + if (ret) { + map.index = ret; + goto unlock; + } + + ctrl.first = ctrl.last = desc->msi_index; + + ret = __msi_domain_alloc_irqs(dev, domain, &ctrl); + if (ret) { + map.index = ret; + msi_domain_free_locked(dev, &ctrl); + } else { + map.index = desc->msi_index; + map.virq = desc->irq; + } +unlock: + msi_unlock_descs(dev); + return map; +} + static void __msi_domain_free_irqs(struct device *dev, struct irq_domain *domain, struct msi_ctrl *ctrl) { -- cgit v1.2.3