diff mbox series

[V2,29/46] irqdomain/msi: Allow to override msi_domain_alloc/free_irqs()

Message ID 20200826112333.526797548@linutronix.de
State New
Headers show
Series x86, PCI, XEN, genirq ...: Prepare for device MSI | expand

Commit Message

Thomas Gleixner Aug. 26, 2020, 11:16 a.m. UTC
From: Thomas Gleixner <tglx@linutronix.de>

To support MSI irq domains which do not fit at all into the regular MSI
irqdomain scheme, like the XEN MSI interrupt management for PV/HVM/DOM0,
it's necessary to allow to override the alloc/free implementation.

This is a preperatory step to switch X86 away from arch_*_msi_irqs() and
store the irq domain pointer right in struct device.

No functional change for existing MSI irq domain users.

Aside of the evil XEN wrapper this is also useful for special MSI domains
which need to do extra alloc/free work before/after calling the generic
core function. Work like allocating/freeing MSI descriptors, MSI storage
space etc.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

---
 include/linux/msi.h |   27 ++++++++++++++++++++
 kernel/irq/msi.c    |   70 +++++++++++++++++++++++++++++++++++-----------------
 2 files changed, 75 insertions(+), 22 deletions(-)

Comments

Marc Zyngier Aug. 26, 2020, 7:06 p.m. UTC | #1
On Wed, 26 Aug 2020 12:16:57 +0100,
Thomas Gleixner <tglx@linutronix.de> wrote:
> 
> From: Thomas Gleixner <tglx@linutronix.de>
> 
> To support MSI irq domains which do not fit at all into the regular MSI
> irqdomain scheme, like the XEN MSI interrupt management for PV/HVM/DOM0,
> it's necessary to allow to override the alloc/free implementation.
> 
> This is a preperatory step to switch X86 away from arch_*_msi_irqs() and
> store the irq domain pointer right in struct device.
> 
> No functional change for existing MSI irq domain users.
> 
> Aside of the evil XEN wrapper this is also useful for special MSI domains
> which need to do extra alloc/free work before/after calling the generic
> core function. Work like allocating/freeing MSI descriptors, MSI storage
> space etc.
> 
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> 
> ---
>  include/linux/msi.h |   27 ++++++++++++++++++++
>  kernel/irq/msi.c    |   70 +++++++++++++++++++++++++++++++++++-----------------
>  2 files changed, 75 insertions(+), 22 deletions(-)
> 
> --- a/include/linux/msi.h
> +++ b/include/linux/msi.h
> @@ -241,6 +241,10 @@ struct msi_domain_info;
>   * @msi_finish:		Optional callback to finalize the allocation
>   * @set_desc:		Set the msi descriptor for an interrupt
>   * @handle_error:	Optional error handler if the allocation fails
> + * @domain_alloc_irqs:	Optional function to override the default allocation
> + *			function.
> + * @domain_free_irqs:	Optional function to override the default free
> + *			function.
>   *
>   * @get_hwirq, @msi_init and @msi_free are callbacks used by
>   * msi_create_irq_domain() and related interfaces
> @@ -248,6 +252,22 @@ struct msi_domain_info;
>   * @msi_check, @msi_prepare, @msi_finish, @set_desc and @handle_error
>   * are callbacks used by msi_domain_alloc_irqs() and related
>   * interfaces which are based on msi_desc.
> + *
> + * @domain_alloc_irqs, @domain_free_irqs can be used to override the
> + * default allocation/free functions (__msi_domain_alloc/free_irqs). This
> + * is initially for a wrapper around XENs seperate MSI universe which can't
> + * 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.
> + *
> + * 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().
>   */
>  struct msi_domain_ops {
>  	irq_hw_number_t	(*get_hwirq)(struct msi_domain_info *info,
> @@ -270,6 +290,10 @@ struct msi_domain_ops {
>  				    struct msi_desc *desc);
>  	int		(*handle_error)(struct irq_domain *domain,
>  					struct msi_desc *desc, int error);
> +	int		(*domain_alloc_irqs)(struct irq_domain *domain,
> +					     struct device *dev, int nvec);
> +	void		(*domain_free_irqs)(struct irq_domain *domain,
> +					    struct device *dev);
>  };
>  
>  /**
> @@ -327,8 +351,11 @@ int msi_domain_set_affinity(struct irq_d
>  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(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(struct irq_domain *domain, struct device *dev);
>  struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain);
>  
> --- a/kernel/irq/msi.c
> +++ b/kernel/irq/msi.c
> @@ -229,11 +229,13 @@ static int msi_domain_ops_check(struct i
>  }
>  
>  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,
> +	.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,
> +	.domain_free_irqs	= __msi_domain_free_irqs,
>  };
>  
>  static void msi_domain_update_dom_ops(struct msi_domain_info *info)
> @@ -245,6 +247,14 @@ static void msi_domain_update_dom_ops(st
>  		return;
>  	}
>  
> +	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;
> +
>  	if (ops->get_hwirq == NULL)
>  		ops->get_hwirq = msi_domain_ops_default.get_hwirq;
>  	if (ops->msi_init == NULL)
> @@ -278,8 +288,7 @@ struct irq_domain *msi_create_irq_domain
>  {
>  	struct irq_domain *domain;
>  
> -	if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
> -		msi_domain_update_dom_ops(info);
> +	msi_domain_update_dom_ops(info);
>  	if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
>  		msi_domain_update_chip_ops(info);
>  
> @@ -386,17 +395,8 @@ static bool msi_check_reservation_mode(s
>  	return desc->msi_attrib.is_msix || desc->msi_attrib.maskbit;
>  }
>  
> -/**
> - * 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
> - *
> - * Returns 0 on success or an error code.
> - */
> -int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
> -			  int nvec)
> +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;
> @@ -490,12 +490,24 @@ int msi_domain_alloc_irqs(struct irq_dom
>  }
>  
>  /**
> - * msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @dev
> - * @domain:	The domain to managing the interrupts
> + * 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 free
> + *		are allocated
> + * @nvec:	The number of interrupts to allocate
> + *
> + * Returns 0 on success or an error code.
>   */
> -void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
> +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;

Rework leftovers, I imagine.

> +
> +	return ops->domain_alloc_irqs(domain, dev, nvec);
> +}
> +
> +void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
>  {
>  	struct msi_desc *desc;
>  
> @@ -513,6 +525,20 @@ void msi_domain_free_irqs(struct irq_dom
>  }
>  
>  /**
> + * __msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @dev

Spurious __.

> + * @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)
> +{
> +	struct msi_domain_info *info = domain->host_data;
> +	struct msi_domain_ops *ops = info->ops;

Same thing?

> +
> +	return ops->domain_free_irqs(domain, dev);
> +}
> +
> +/**
>   * msi_get_domain_info - Get the MSI interrupt domain info for @domain
>   * @domain:	The interrupt domain to retrieve data from
>   *

Otherwise looks good to me:

Reviewed-by: Marc Zyngier <maz@kernel.org>

	M.
Thomas Gleixner Aug. 26, 2020, 7:47 p.m. UTC | #2
On Wed, Aug 26 2020 at 20:06, Marc Zyngier wrote:
> On Wed, 26 Aug 2020 12:16:57 +0100,
> Thomas Gleixner <tglx@linutronix.de> wrote:
>>  /**
>> - * msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @dev
>> - * @domain:	The domain to managing the interrupts
>> + * 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 free
>> + *		are allocated
>> + * @nvec:	The number of interrupts to allocate
>> + *
>> + * Returns 0 on success or an error code.
>>   */
>> -void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
>> +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;
>
> Rework leftovers, I imagine.

Hmm, no. How would it call ops->domain_alloc_irqs() without getting the
ops. I know, that the diff is horrible, but don't blame me for it. diff
sucks at times.

>> +
>> +	return ops->domain_alloc_irqs(domain, dev, nvec);
>> +}
>> +
>> +void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
>>  {
>>  	struct msi_desc *desc;
>>  
>> @@ -513,6 +525,20 @@ void msi_domain_free_irqs(struct irq_dom
>>  }
>>  
>>  /**
>> + * __msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @dev
>
> Spurious __.

Yup.

Thanks,

        tglx
Marc Zyngier Aug. 26, 2020, 9:33 p.m. UTC | #3
On Wed, 26 Aug 2020 20:47:38 +0100,
Thomas Gleixner <tglx@linutronix.de> wrote:
> 
> On Wed, Aug 26 2020 at 20:06, Marc Zyngier wrote:
> > On Wed, 26 Aug 2020 12:16:57 +0100,
> > Thomas Gleixner <tglx@linutronix.de> wrote:
> >>  /**
> >> - * msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @dev
> >> - * @domain:	The domain to managing the interrupts
> >> + * 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 free
> >> + *		are allocated
> >> + * @nvec:	The number of interrupts to allocate
> >> + *
> >> + * Returns 0 on success or an error code.
> >>   */
> >> -void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
> >> +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;
> >
> > Rework leftovers, I imagine.
> 
> Hmm, no. How would it call ops->domain_alloc_irqs() without getting the
> ops. I know, that the diff is horrible, but don't blame me for it. diff
> sucks at times.

I can't read. Time to put the laptop away!

Thanks,

	M.
Dey, Megha Aug. 28, 2020, 12:24 a.m. UTC | #4
Hi Thomas,

On 8/26/2020 4:16 AM, Thomas Gleixner wrote:
> From: Thomas Gleixner <tglx@linutronix.de>
>
> To support MSI irq domains which do not fit at all into the regular MSI
> irqdomain scheme, like the XEN MSI interrupt management for PV/HVM/DOM0,
> it's necessary to allow to override the alloc/free implementation.
>
> This is a preperatory step to switch X86 away from arch_*_msi_irqs() and
> store the irq domain pointer right in struct device.
>
> No functional change for existing MSI irq domain users.
>
> Aside of the evil XEN wrapper this is also useful for special MSI domains
> which need to do extra alloc/free work before/after calling the generic
> core function. Work like allocating/freeing MSI descriptors, MSI storage
> space etc.
>
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
>
> ---
>   include/linux/msi.h |   27 ++++++++++++++++++++
>   kernel/irq/msi.c    |   70 +++++++++++++++++++++++++++++++++++-----------------
>   2 files changed, 75 insertions(+), 22 deletions(-)
>
> --- a/include/linux/msi.h
> +++ b/include/linux/msi.h
> @@ -241,6 +241,10 @@ struct msi_domain_info;
>    * @msi_finish:		Optional callback to finalize the allocation
>    * @set_desc:		Set the msi descriptor for an interrupt
>    * @handle_error:	Optional error handler if the allocation fails
> + * @domain_alloc_irqs:	Optional function to override the default allocation
> + *			function.
> + * @domain_free_irqs:	Optional function to override the default free
> + *			function.
>    *
>    * @get_hwirq, @msi_init and @msi_free are callbacks used by
>    * msi_create_irq_domain() and related interfaces
> @@ -248,6 +252,22 @@ struct msi_domain_info;
>    * @msi_check, @msi_prepare, @msi_finish, @set_desc and @handle_error
>    * are callbacks used by msi_domain_alloc_irqs() and related
>    * interfaces which are based on msi_desc.
> + *
> + * @domain_alloc_irqs, @domain_free_irqs can be used to override the
> + * default allocation/free functions (__msi_domain_alloc/free_irqs). This
> + * is initially for a wrapper around XENs seperate MSI universe which can't
> + * 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.
> + *
> + * 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().
>    */
>   struct msi_domain_ops {
>   	irq_hw_number_t	(*get_hwirq)(struct msi_domain_info *info,
> @@ -270,6 +290,10 @@ struct msi_domain_ops {
>   				    struct msi_desc *desc);
>   	int		(*handle_error)(struct irq_domain *domain,
>   					struct msi_desc *desc, int error);
> +	int		(*domain_alloc_irqs)(struct irq_domain *domain,
> +					     struct device *dev, int nvec);
> +	void		(*domain_free_irqs)(struct irq_domain *domain,
> +					    struct device *dev);
>   };
>   
>   /**
> @@ -327,8 +351,11 @@ int msi_domain_set_affinity(struct irq_d
>   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(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(struct irq_domain *domain, struct device *dev);
>   struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain);
>   
> --- a/kernel/irq/msi.c
> +++ b/kernel/irq/msi.c
> @@ -229,11 +229,13 @@ static int msi_domain_ops_check(struct i
>   }
>   
>   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,
> +	.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,
> +	.domain_free_irqs	= __msi_domain_free_irqs,
>   };
>   
>   static void msi_domain_update_dom_ops(struct msi_domain_info *info)
> @@ -245,6 +247,14 @@ static void msi_domain_update_dom_ops(st
>   		return;
>   	}
>   
> +	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;
> +
>   	if (ops->get_hwirq == NULL)
>   		ops->get_hwirq = msi_domain_ops_default.get_hwirq;
>   	if (ops->msi_init == NULL)
> @@ -278,8 +288,7 @@ struct irq_domain *msi_create_irq_domain
>   {
>   	struct irq_domain *domain;
>   
> -	if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
> -		msi_domain_update_dom_ops(info);
> +	msi_domain_update_dom_ops(info);
>   	if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
>   		msi_domain_update_chip_ops(info);
>   
> @@ -386,17 +395,8 @@ static bool msi_check_reservation_mode(s
>   	return desc->msi_attrib.is_msix || desc->msi_attrib.maskbit;
>   }
>   
> -/**
> - * 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
> - *
> - * Returns 0 on success or an error code.
> - */
> -int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
> -			  int nvec)
> +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;
> @@ -490,12 +490,24 @@ int msi_domain_alloc_irqs(struct irq_dom
>   }
>   
>   /**
> - * msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @dev
> - * @domain:	The domain to managing the interrupts
> + * 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 free
> + *		are allocated
> + * @nvec:	The number of interrupts to allocate
> + *
> + * Returns 0 on success or an error code.
>    */
> -void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
> +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;
> +
> +	return ops->domain_alloc_irqs(domain, dev, nvec);
> +}
> +

Since, our upcoming driver will directly call this API, an 
EXPORT_SYMBOL_GPL tag would be required.

Currently there is no use case. I was wondering if we should add this 
change while submitting the

idxd/ims patches or would you add this to this patch?

> +void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
>   {
>   	struct msi_desc *desc;
>   
> @@ -513,6 +525,20 @@ void msi_domain_free_irqs(struct irq_dom
>   }
>   
>   /**
> + * __msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @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)
> +{
> +	struct msi_domain_info *info = domain->host_data;
> +	struct msi_domain_ops *ops = info->ops;
> +
> +	return ops->domain_free_irqs(domain, dev);
> +}
> +
> +/**
>    * msi_get_domain_info - Get the MSI interrupt domain info for @domain
>    * @domain:	The interrupt domain to retrieve data from
>    *
>
>
diff mbox series

Patch

--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -241,6 +241,10 @@  struct msi_domain_info;
  * @msi_finish:		Optional callback to finalize the allocation
  * @set_desc:		Set the msi descriptor for an interrupt
  * @handle_error:	Optional error handler if the allocation fails
+ * @domain_alloc_irqs:	Optional function to override the default allocation
+ *			function.
+ * @domain_free_irqs:	Optional function to override the default free
+ *			function.
  *
  * @get_hwirq, @msi_init and @msi_free are callbacks used by
  * msi_create_irq_domain() and related interfaces
@@ -248,6 +252,22 @@  struct msi_domain_info;
  * @msi_check, @msi_prepare, @msi_finish, @set_desc and @handle_error
  * are callbacks used by msi_domain_alloc_irqs() and related
  * interfaces which are based on msi_desc.
+ *
+ * @domain_alloc_irqs, @domain_free_irqs can be used to override the
+ * default allocation/free functions (__msi_domain_alloc/free_irqs). This
+ * is initially for a wrapper around XENs seperate MSI universe which can't
+ * 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.
+ *
+ * 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().
  */
 struct msi_domain_ops {
 	irq_hw_number_t	(*get_hwirq)(struct msi_domain_info *info,
@@ -270,6 +290,10 @@  struct msi_domain_ops {
 				    struct msi_desc *desc);
 	int		(*handle_error)(struct irq_domain *domain,
 					struct msi_desc *desc, int error);
+	int		(*domain_alloc_irqs)(struct irq_domain *domain,
+					     struct device *dev, int nvec);
+	void		(*domain_free_irqs)(struct irq_domain *domain,
+					    struct device *dev);
 };
 
 /**
@@ -327,8 +351,11 @@  int msi_domain_set_affinity(struct irq_d
 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(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(struct irq_domain *domain, struct device *dev);
 struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain);
 
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -229,11 +229,13 @@  static int msi_domain_ops_check(struct i
 }
 
 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,
+	.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,
+	.domain_free_irqs	= __msi_domain_free_irqs,
 };
 
 static void msi_domain_update_dom_ops(struct msi_domain_info *info)
@@ -245,6 +247,14 @@  static void msi_domain_update_dom_ops(st
 		return;
 	}
 
+	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;
+
 	if (ops->get_hwirq == NULL)
 		ops->get_hwirq = msi_domain_ops_default.get_hwirq;
 	if (ops->msi_init == NULL)
@@ -278,8 +288,7 @@  struct irq_domain *msi_create_irq_domain
 {
 	struct irq_domain *domain;
 
-	if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
-		msi_domain_update_dom_ops(info);
+	msi_domain_update_dom_ops(info);
 	if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
 		msi_domain_update_chip_ops(info);
 
@@ -386,17 +395,8 @@  static bool msi_check_reservation_mode(s
 	return desc->msi_attrib.is_msix || desc->msi_attrib.maskbit;
 }
 
-/**
- * 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
- *
- * Returns 0 on success or an error code.
- */
-int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
-			  int nvec)
+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;
@@ -490,12 +490,24 @@  int msi_domain_alloc_irqs(struct irq_dom
 }
 
 /**
- * msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @dev
- * @domain:	The domain to managing the interrupts
+ * 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 free
+ *		are allocated
+ * @nvec:	The number of interrupts to allocate
+ *
+ * Returns 0 on success or an error code.
  */
-void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
+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;
+
+	return ops->domain_alloc_irqs(domain, dev, nvec);
+}
+
+void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
 {
 	struct msi_desc *desc;
 
@@ -513,6 +525,20 @@  void msi_domain_free_irqs(struct irq_dom
 }
 
 /**
+ * __msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @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)
+{
+	struct msi_domain_info *info = domain->host_data;
+	struct msi_domain_ops *ops = info->ops;
+
+	return ops->domain_free_irqs(domain, dev);
+}
+
+/**
  * msi_get_domain_info - Get the MSI interrupt domain info for @domain
  * @domain:	The interrupt domain to retrieve data from
  *