diff options
Diffstat (limited to 'kernel/irq/msi.c')
| -rw-r--r-- | kernel/irq/msi.c | 33 | 
1 files changed, 24 insertions, 9 deletions
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 2a8571f72b17..4ca2fd46645d 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -76,6 +76,19 @@ static inline void irq_chip_write_msi_msg(struct irq_data *data,  	data->chip->irq_write_msi_msg(data, msg);  } +static void msi_check_level(struct irq_domain *domain, struct msi_msg *msg) +{ +	struct msi_domain_info *info = domain->host_data; + +	/* +	 * If the MSI provider has messed with the second message and +	 * not advertized that it is level-capable, signal the breakage. +	 */ +	WARN_ON(!((info->flags & MSI_FLAG_LEVEL_CAPABLE) && +		  (info->chip->flags & IRQCHIP_SUPPORTS_LEVEL_MSI)) && +		(msg[1].address_lo || msg[1].address_hi || msg[1].data)); +} +  /**   * msi_domain_set_affinity - Generic affinity setter function for MSI domains   * @irq_data:	The irq data associated to the interrupt @@ -89,13 +102,14 @@ int msi_domain_set_affinity(struct irq_data *irq_data,  			    const struct cpumask *mask, bool force)  {  	struct irq_data *parent = irq_data->parent_data; -	struct msi_msg msg; +	struct msi_msg msg[2] = { [1] = { }, };  	int ret;  	ret = parent->chip->irq_set_affinity(parent, mask, force);  	if (ret >= 0 && ret != IRQ_SET_MASK_OK_DONE) { -		BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg)); -		irq_chip_write_msi_msg(irq_data, &msg); +		BUG_ON(irq_chip_compose_msi_msg(irq_data, msg)); +		msi_check_level(irq_data->domain, msg); +		irq_chip_write_msi_msg(irq_data, msg);  	}  	return ret; @@ -104,20 +118,21 @@ int msi_domain_set_affinity(struct irq_data *irq_data,  static int msi_domain_activate(struct irq_domain *domain,  			       struct irq_data *irq_data, bool early)  { -	struct msi_msg msg; +	struct msi_msg msg[2] = { [1] = { }, }; -	BUG_ON(irq_chip_compose_msi_msg(irq_data, &msg)); -	irq_chip_write_msi_msg(irq_data, &msg); +	BUG_ON(irq_chip_compose_msi_msg(irq_data, msg)); +	msi_check_level(irq_data->domain, msg); +	irq_chip_write_msi_msg(irq_data, msg);  	return 0;  }  static void msi_domain_deactivate(struct irq_domain *domain,  				  struct irq_data *irq_data)  { -	struct msi_msg msg; +	struct msi_msg msg[2]; -	memset(&msg, 0, sizeof(msg)); -	irq_chip_write_msi_msg(irq_data, &msg); +	memset(msg, 0, sizeof(msg)); +	irq_chip_write_msi_msg(irq_data, msg);  }  static int msi_domain_alloc(struct irq_domain *domain, unsigned int virq,  | 
