Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 71c87bd0 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
parents 7cd9beb3 aaa95f74
Loading
Loading
Loading
Loading
+20 −1
Original line number Diff line number Diff line
@@ -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);
	}
+128 −29
Original line number Diff line number Diff line
@@ -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 @@ static int its_alloc_tables(struct its_node *its)
			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 @@ static int its_alloc_tables(struct its_node *its)
		}

		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 (!list_empty(&its_nodes)) {
		if (!gic_rdists_supports_plpis()) {
			pr_info("CPU%d: LPIs not supported\n", smp_processor_id());
			return -ENXIO;
		}

	if (!list_empty(&its_nodes)) {
		its_cpu_init_lpis();
		its_cpu_init_collection();
	}
+1 −1
Original line number Diff line number Diff line
@@ -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);
+12 −8
Original line number Diff line number Diff line
@@ -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;
}
+5 −0
Original line number Diff line number Diff line
@@ -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)