irqchip: gicv3-its: Allocate enough memory for the full range of DeviceID
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>
This commit is contained in:
parent
16acae7295
commit
f54b97ed0b
@ -806,14 +806,31 @@ static int its_alloc_tables(struct its_node *its)
|
|||||||
u64 val = readq_relaxed(its->base + GITS_BASER + i * 8);
|
u64 val = readq_relaxed(its->base + GITS_BASER + i * 8);
|
||||||
u64 type = GITS_BASER_TYPE(val);
|
u64 type = GITS_BASER_TYPE(val);
|
||||||
u64 entry_size = GITS_BASER_ENTRY_SIZE(val);
|
u64 entry_size = GITS_BASER_ENTRY_SIZE(val);
|
||||||
|
int order = 0;
|
||||||
|
int alloc_size;
|
||||||
u64 tmp;
|
u64 tmp;
|
||||||
void *base;
|
void *base;
|
||||||
|
|
||||||
if (type == GITS_BASER_TYPE_NONE)
|
if (type == GITS_BASER_TYPE_NONE)
|
||||||
continue;
|
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) {
|
if (!base) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto out_free;
|
goto out_free;
|
||||||
@ -841,7 +858,7 @@ retry_baser:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
val |= (PAGE_SIZE / psz) - 1;
|
val |= (alloc_size / psz) - 1;
|
||||||
|
|
||||||
writeq_relaxed(val, its->base + GITS_BASER + i * 8);
|
writeq_relaxed(val, its->base + GITS_BASER + i * 8);
|
||||||
tmp = readq_relaxed(its->base + GITS_BASER + i * 8);
|
tmp = readq_relaxed(its->base + GITS_BASER + i * 8);
|
||||||
@ -882,7 +899,7 @@ retry_baser:
|
|||||||
}
|
}
|
||||||
|
|
||||||
pr_info("ITS: allocated %d %s @%lx (psz %dK, shr %d)\n",
|
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],
|
its_base_type_string[type],
|
||||||
(unsigned long)virt_to_phys(base),
|
(unsigned long)virt_to_phys(base),
|
||||||
psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT);
|
psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT);
|
||||||
|
@ -166,6 +166,8 @@
|
|||||||
|
|
||||||
#define GITS_TRANSLATER 0x10040
|
#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_TYPER_PTA (1UL << 19)
|
||||||
|
|
||||||
#define GITS_CBASER_VALID (1UL << 63)
|
#define GITS_CBASER_VALID (1UL << 63)
|
||||||
|
Loading…
Reference in New Issue
Block a user