Skip to content

Commit

Permalink
irqchip: gicv3-its: Allocate enough memory for the full range of Devi…
Browse files Browse the repository at this point in the history
…ceID

The ITS table allocator is only allocating a single page per table.
This works fine for most things, but leads to silent lack of
interrupt delivery if we end-up with a device that has an ID that is
out of the range defined by a single page of memory. Even worse, depending
on the page size, behaviour changes, which is not a very good experience.

A solution is actually to allocate memory for the full range of ID that
the ITS supports. A massive waste memory wise, but at least a safe bet.

Tested on a Phytium SoC.

Tested-by: Chen Baozi <chenbaozi@kylinos.com.cn>
Acked-by: Chen Baozi <chenbaozi@kylinos.com.cn>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Link: https://lkml.kernel.org/r/1425659870-11832-3-git-send-email-marc.zyngier@arm.com
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
  • Loading branch information
Marc Zyngier authored and Jason Cooper committed Mar 8, 2015
1 parent 16acae7 commit f54b97e
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 4 deletions.
25 changes: 21 additions & 4 deletions drivers/irqchip/irq-gic-v3-its.c
Original file line number Diff line number Diff line change
Expand Up @@ -806,14 +806,31 @@ static int its_alloc_tables(struct its_node *its)
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 = 0;
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);
}

alloc_size = (1 << order) * PAGE_SIZE;
base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order);
if (!base) {
err = -ENOMEM;
goto out_free;
Expand Down Expand Up @@ -841,7 +858,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);
Expand Down Expand Up @@ -882,7 +899,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);
Expand Down
2 changes: 2 additions & 0 deletions include/linux/irqchip/arm-gic-v3.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@

#define GITS_TRANSLATER 0x10040

#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)
Expand Down

0 comments on commit f54b97e

Please sign in to comment.