diff options
| author | 2015-03-15 10:41:30 -0700 | |
|---|---|---|
| committer | 2015-03-15 10:41:30 -0700 | |
| commit | 71c87bd06258fe2e94220a070f0599d84ad88d03 (patch) | |
| tree | dd97a4d166a40403e60c63ca386cb897cb0487f0 | |
| parent | Merge branch 'drm-fixes' of git://people.freedesktop.org/~airlied/linux (diff) | |
| parent | Merge branch 'irqchip/urgent-gic' into irqchip/urgent (diff) | |
| download | wireguard-linux-71c87bd06258fe2e94220a070f0599d84ad88d03.tar.xz wireguard-linux-71c87bd06258fe2e94220a070f0599d84ad88d03.zip  | |
Merge tag 'irqchip-fixes-4.0' of git://git.infradead.org/users/jcooper/linux
Pull irqchip fixes from Jason Cooper:
 "armada-370-xp:
   - Chained per-cpu interrupts
  gic{,-v3,v3-its}"
   - Various fixes for safer operation"
* tag 'irqchip-fixes-4.0' of git://git.infradead.org/users/jcooper/linux:
  irqchip: gicv3-its: Support safe initialization
  irqchip: gicv3-its: Define macros for GITS_CTLR fields
  irqchip: gicv3-its: Add limitation to page order
  irqchip: gicv3-its: Use 64KB page as default granule
  irqchip: gicv3-its: Zero itt before handling to hardware
  irqchip: gic-v3: Fix out of bounds access to cpu_logical_map
  irqchip: gic: Fix unsafe locking reported by lockdep
  irqchip: gicv3-its: Fix unsafe locking reported by lockdep
  irqchip: gicv3-its: Iterate over PCI aliases to generate ITS configuration
  irqchip: gicv3-its: Allocate enough memory for the full range of DeviceID
  irqchip: gicv3-its: Fix ITS CPU init
  irqchip: armada-370-xp: Fix chained per-cpu interrupts
| -rw-r--r-- | drivers/irqchip/irq-armada-370-xp.c | 21 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-v3-its.c | 157 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-v3.c | 2 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic.c | 20 | ||||
| -rw-r--r-- | include/linux/irqchip/arm-gic-v3.h | 5 | 
5 files changed, 166 insertions, 39 deletions
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 463c235acbdc..4387dae14e45 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -69,6 +69,7 @@ static void __iomem *per_cpu_int_base;  static void __iomem *main_int_base;  static struct irq_domain *armada_370_xp_mpic_domain;  static u32 doorbell_mask_reg; +static int parent_irq;  #ifdef CONFIG_PCI_MSI  static struct irq_domain *armada_370_xp_msi_domain;  static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); @@ -356,6 +357,7 @@ static int armada_xp_mpic_secondary_init(struct notifier_block *nfb,  {  	if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)  		armada_xp_mpic_smp_cpu_init(); +  	return NOTIFY_OK;  } @@ -364,6 +366,20 @@ static struct notifier_block armada_370_xp_mpic_cpu_notifier = {  	.priority = 100,  }; +static int mpic_cascaded_secondary_init(struct notifier_block *nfb, +					unsigned long action, void *hcpu) +{ +	if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) +		enable_percpu_irq(parent_irq, IRQ_TYPE_NONE); + +	return NOTIFY_OK; +} + +static struct notifier_block mpic_cascaded_cpu_notifier = { +	.notifier_call = mpic_cascaded_secondary_init, +	.priority = 100, +}; +  #endif /* CONFIG_SMP */  static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { @@ -539,7 +555,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,  					     struct device_node *parent)  {  	struct resource main_int_res, per_cpu_int_res; -	int parent_irq, nr_irqs, i; +	int nr_irqs, i;  	u32 control;  	BUG_ON(of_address_to_resource(node, 0, &main_int_res)); @@ -587,6 +603,9 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,  		register_cpu_notifier(&armada_370_xp_mpic_cpu_notifier);  #endif  	} else { +#ifdef CONFIG_SMP +		register_cpu_notifier(&mpic_cascaded_cpu_notifier); +#endif  		irq_set_chained_handler(parent_irq,  					armada_370_xp_mpic_handle_cascade_irq);  	} diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index d8996bdf0f61..596b0a9eee99 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -416,13 +416,14 @@ static void its_send_single_command(struct its_node *its,  {  	struct its_cmd_block *cmd, *sync_cmd, *next_cmd;  	struct its_collection *sync_col; +	unsigned long flags; -	raw_spin_lock(&its->lock); +	raw_spin_lock_irqsave(&its->lock, flags);  	cmd = its_allocate_entry(its);  	if (!cmd) {		/* We're soooooo screewed... */  		pr_err_ratelimited("ITS can't allocate, dropping command\n"); -		raw_spin_unlock(&its->lock); +		raw_spin_unlock_irqrestore(&its->lock, flags);  		return;  	}  	sync_col = builder(cmd, desc); @@ -442,7 +443,7 @@ static void its_send_single_command(struct its_node *its,  post:  	next_cmd = its_post_commands(its); -	raw_spin_unlock(&its->lock); +	raw_spin_unlock_irqrestore(&its->lock, flags);  	its_wait_for_range_completion(its, cmd, next_cmd);  } @@ -799,21 +800,43 @@ static int its_alloc_tables(struct its_node *its)  {  	int err;  	int i; -	int psz = PAGE_SIZE; +	int psz = SZ_64K;  	u64 shr = GITS_BASER_InnerShareable;  	for (i = 0; i < GITS_BASER_NR_REGS; i++) {  		u64 val = readq_relaxed(its->base + GITS_BASER + i * 8);  		u64 type = GITS_BASER_TYPE(val);  		u64 entry_size = GITS_BASER_ENTRY_SIZE(val); +		int order = get_order(psz); +		int alloc_size;  		u64 tmp;  		void *base;  		if (type == GITS_BASER_TYPE_NONE)  			continue; -		/* We're lazy and only allocate a single page for now */ -		base = (void *)get_zeroed_page(GFP_KERNEL); +		/* +		 * Allocate as many entries as required to fit the +		 * range of device IDs that the ITS can grok... The ID +		 * space being incredibly sparse, this results in a +		 * massive waste of memory. +		 * +		 * For other tables, only allocate a single page. +		 */ +		if (type == GITS_BASER_TYPE_DEVICE) { +			u64 typer = readq_relaxed(its->base + GITS_TYPER); +			u32 ids = GITS_TYPER_DEVBITS(typer); + +			order = get_order((1UL << ids) * entry_size); +			if (order >= MAX_ORDER) { +				order = MAX_ORDER - 1; +				pr_warn("%s: Device Table too large, reduce its page order to %u\n", +					its->msi_chip.of_node->full_name, order); +			} +		} + +		alloc_size = (1 << order) * PAGE_SIZE; +		base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order);  		if (!base) {  			err = -ENOMEM;  			goto out_free; @@ -841,7 +864,7 @@ retry_baser:  			break;  		} -		val |= (PAGE_SIZE / psz) - 1; +		val |= (alloc_size / psz) - 1;  		writeq_relaxed(val, its->base + GITS_BASER + i * 8);  		tmp = readq_relaxed(its->base + GITS_BASER + i * 8); @@ -882,7 +905,7 @@ retry_baser:  		}  		pr_info("ITS: allocated %d %s @%lx (psz %dK, shr %d)\n", -			(int)(PAGE_SIZE / entry_size), +			(int)(alloc_size / entry_size),  			its_base_type_string[type],  			(unsigned long)virt_to_phys(base),  			psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT); @@ -1020,8 +1043,9 @@ static void its_cpu_init_collection(void)  static struct its_device *its_find_device(struct its_node *its, u32 dev_id)  {  	struct its_device *its_dev = NULL, *tmp; +	unsigned long flags; -	raw_spin_lock(&its->lock); +	raw_spin_lock_irqsave(&its->lock, flags);  	list_for_each_entry(tmp, &its->its_device_list, entry) {  		if (tmp->device_id == dev_id) { @@ -1030,7 +1054,7 @@ static struct its_device *its_find_device(struct its_node *its, u32 dev_id)  		}  	} -	raw_spin_unlock(&its->lock); +	raw_spin_unlock_irqrestore(&its->lock, flags);  	return its_dev;  } @@ -1040,6 +1064,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,  {  	struct its_device *dev;  	unsigned long *lpi_map; +	unsigned long flags;  	void *itt;  	int lpi_base;  	int nr_lpis; @@ -1056,7 +1081,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,  	nr_ites = max(2UL, roundup_pow_of_two(nvecs));  	sz = nr_ites * its->ite_size;  	sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1; -	itt = kmalloc(sz, GFP_KERNEL); +	itt = kzalloc(sz, GFP_KERNEL);  	lpi_map = its_lpi_alloc_chunks(nvecs, &lpi_base, &nr_lpis);  	if (!dev || !itt || !lpi_map) { @@ -1075,9 +1100,9 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,  	dev->device_id = dev_id;  	INIT_LIST_HEAD(&dev->entry); -	raw_spin_lock(&its->lock); +	raw_spin_lock_irqsave(&its->lock, flags);  	list_add(&dev->entry, &its->its_device_list); -	raw_spin_unlock(&its->lock); +	raw_spin_unlock_irqrestore(&its->lock, flags);  	/* Bind the device to the first possible CPU */  	cpu = cpumask_first(cpu_online_mask); @@ -1091,9 +1116,11 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,  static void its_free_device(struct its_device *its_dev)  { -	raw_spin_lock(&its_dev->its->lock); +	unsigned long flags; + +	raw_spin_lock_irqsave(&its_dev->its->lock, flags);  	list_del(&its_dev->entry); -	raw_spin_unlock(&its_dev->its->lock); +	raw_spin_unlock_irqrestore(&its_dev->its->lock, flags);  	kfree(its_dev->itt);  	kfree(its_dev);  } @@ -1112,31 +1139,69 @@ static int its_alloc_device_irq(struct its_device *dev, irq_hw_number_t *hwirq)  	return 0;  } +struct its_pci_alias { +	struct pci_dev	*pdev; +	u32		dev_id; +	u32		count; +}; + +static int its_pci_msi_vec_count(struct pci_dev *pdev) +{ +	int msi, msix; + +	msi = max(pci_msi_vec_count(pdev), 0); +	msix = max(pci_msix_vec_count(pdev), 0); + +	return max(msi, msix); +} + +static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data) +{ +	struct its_pci_alias *dev_alias = data; + +	dev_alias->dev_id = alias; +	if (pdev != dev_alias->pdev) +		dev_alias->count += its_pci_msi_vec_count(dev_alias->pdev); + +	return 0; +} +  static int its_msi_prepare(struct irq_domain *domain, struct device *dev,  			   int nvec, msi_alloc_info_t *info)  {  	struct pci_dev *pdev;  	struct its_node *its; -	u32 dev_id;  	struct its_device *its_dev; +	struct its_pci_alias dev_alias;  	if (!dev_is_pci(dev))  		return -EINVAL;  	pdev = to_pci_dev(dev); -	dev_id = PCI_DEVID(pdev->bus->number, pdev->devfn); +	dev_alias.pdev = pdev; +	dev_alias.count = nvec; + +	pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias);  	its = domain->parent->host_data; -	its_dev = its_find_device(its, dev_id); -	if (WARN_ON(its_dev)) -		return -EINVAL; +	its_dev = its_find_device(its, dev_alias.dev_id); +	if (its_dev) { +		/* +		 * We already have seen this ID, probably through +		 * another alias (PCI bridge of some sort). No need to +		 * create the device. +		 */ +		dev_dbg(dev, "Reusing ITT for devID %x\n", dev_alias.dev_id); +		goto out; +	} -	its_dev = its_create_device(its, dev_id, nvec); +	its_dev = its_create_device(its, dev_alias.dev_id, dev_alias.count);  	if (!its_dev)  		return -ENOMEM; -	dev_dbg(&pdev->dev, "ITT %d entries, %d bits\n", nvec, ilog2(nvec)); - +	dev_dbg(&pdev->dev, "ITT %d entries, %d bits\n", +		dev_alias.count, ilog2(dev_alias.count)); +out:  	info->scratchpad[0].ptr = its_dev;  	info->scratchpad[1].ptr = dev;  	return 0; @@ -1255,6 +1320,34 @@ static const struct irq_domain_ops its_domain_ops = {  	.deactivate		= its_irq_domain_deactivate,  }; +static int its_force_quiescent(void __iomem *base) +{ +	u32 count = 1000000;	/* 1s */ +	u32 val; + +	val = readl_relaxed(base + GITS_CTLR); +	if (val & GITS_CTLR_QUIESCENT) +		return 0; + +	/* Disable the generation of all interrupts to this ITS */ +	val &= ~GITS_CTLR_ENABLE; +	writel_relaxed(val, base + GITS_CTLR); + +	/* Poll GITS_CTLR and wait until ITS becomes quiescent */ +	while (1) { +		val = readl_relaxed(base + GITS_CTLR); +		if (val & GITS_CTLR_QUIESCENT) +			return 0; + +		count--; +		if (!count) +			return -EBUSY; + +		cpu_relax(); +		udelay(1); +	} +} +  static int its_probe(struct device_node *node, struct irq_domain *parent)  {  	struct resource res; @@ -1283,6 +1376,13 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)  		goto out_unmap;  	} +	err = its_force_quiescent(its_base); +	if (err) { +		pr_warn("%s: failed to quiesce, giving up\n", +			node->full_name); +		goto out_unmap; +	} +  	pr_info("ITS: %s\n", node->full_name);  	its = kzalloc(sizeof(*its), GFP_KERNEL); @@ -1323,7 +1423,7 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)  	writeq_relaxed(baser, its->base + GITS_CBASER);  	tmp = readq_relaxed(its->base + GITS_CBASER);  	writeq_relaxed(0, its->base + GITS_CWRITER); -	writel_relaxed(1, its->base + GITS_CTLR); +	writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR);  	if ((tmp ^ baser) & GITS_BASER_SHAREABILITY_MASK) {  		pr_info("ITS: using cache flushing for cmd queue\n"); @@ -1382,12 +1482,11 @@ static bool gic_rdists_supports_plpis(void)  int its_cpu_init(void)  { -	if (!gic_rdists_supports_plpis()) { -		pr_info("CPU%d: LPIs not supported\n", smp_processor_id()); -		return -ENXIO; -	} -  	if (!list_empty(&its_nodes)) { +		if (!gic_rdists_supports_plpis()) { +			pr_info("CPU%d: LPIs not supported\n", smp_processor_id()); +			return -ENXIO; +		}  		its_cpu_init_lpis();  		its_cpu_init_collection();  	} diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 1c6dea2fbc34..fd8850def1b8 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -466,7 +466,7 @@ static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask,  		tlist |= 1 << (mpidr & 0xf);  		cpu = cpumask_next(cpu, mask); -		if (cpu == nr_cpu_ids) +		if (cpu >= nr_cpu_ids)  			goto out;  		mpidr = cpu_logical_map(cpu); diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 4634cf7d0ec3..471e1cdc1933 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -154,23 +154,25 @@ static inline unsigned int gic_irq(struct irq_data *d)  static void gic_mask_irq(struct irq_data *d)  {  	u32 mask = 1 << (gic_irq(d) % 32); +	unsigned long flags; -	raw_spin_lock(&irq_controller_lock); +	raw_spin_lock_irqsave(&irq_controller_lock, flags);  	writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4);  	if (gic_arch_extn.irq_mask)  		gic_arch_extn.irq_mask(d); -	raw_spin_unlock(&irq_controller_lock); +	raw_spin_unlock_irqrestore(&irq_controller_lock, flags);  }  static void gic_unmask_irq(struct irq_data *d)  {  	u32 mask = 1 << (gic_irq(d) % 32); +	unsigned long flags; -	raw_spin_lock(&irq_controller_lock); +	raw_spin_lock_irqsave(&irq_controller_lock, flags);  	if (gic_arch_extn.irq_unmask)  		gic_arch_extn.irq_unmask(d);  	writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4); -	raw_spin_unlock(&irq_controller_lock); +	raw_spin_unlock_irqrestore(&irq_controller_lock, flags);  }  static void gic_eoi_irq(struct irq_data *d) @@ -188,6 +190,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type)  {  	void __iomem *base = gic_dist_base(d);  	unsigned int gicirq = gic_irq(d); +	unsigned long flags;  	int ret;  	/* Interrupt configuration for SGIs can't be changed */ @@ -199,14 +202,14 @@ static int gic_set_type(struct irq_data *d, unsigned int type)  			    type != IRQ_TYPE_EDGE_RISING)  		return -EINVAL; -	raw_spin_lock(&irq_controller_lock); +	raw_spin_lock_irqsave(&irq_controller_lock, flags);  	if (gic_arch_extn.irq_set_type)  		gic_arch_extn.irq_set_type(d, type);  	ret = gic_configure_irq(gicirq, type, base, NULL); -	raw_spin_unlock(&irq_controller_lock); +	raw_spin_unlock_irqrestore(&irq_controller_lock, flags);  	return ret;  } @@ -227,6 +230,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,  	void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3);  	unsigned int cpu, shift = (gic_irq(d) % 4) * 8;  	u32 val, mask, bit; +	unsigned long flags;  	if (!force)  		cpu = cpumask_any_and(mask_val, cpu_online_mask); @@ -236,12 +240,12 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,  	if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids)  		return -EINVAL; -	raw_spin_lock(&irq_controller_lock); +	raw_spin_lock_irqsave(&irq_controller_lock, flags);  	mask = 0xff << shift;  	bit = gic_cpu_map[cpu] << shift;  	val = readl_relaxed(reg) & ~mask;  	writel_relaxed(val | bit, reg); -	raw_spin_unlock(&irq_controller_lock); +	raw_spin_unlock_irqrestore(&irq_controller_lock, flags);  	return IRQ_SET_MASK_OK;  } diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index 800544bc7bfd..781974afff9f 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h @@ -166,6 +166,11 @@  #define GITS_TRANSLATER			0x10040 +#define GITS_CTLR_ENABLE		(1U << 0) +#define GITS_CTLR_QUIESCENT		(1U << 31) + +#define GITS_TYPER_DEVBITS_SHIFT	13 +#define GITS_TYPER_DEVBITS(r)		((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)  #define GITS_TYPER_PTA			(1UL << 19)  #define GITS_CBASER_VALID		(1UL << 63)  | 
