diff options
author | Marc Zyngier <marc.zyngier@arm.com> | 2015-03-06 11:37:41 -0500 |
---|---|---|
committer | Jason Cooper <jason@lakedaemon.net> | 2015-03-08 00:32:52 -0500 |
commit | f54b97ed0b17d3da5f98ba8188cd5646415a922d (patch) | |
tree | 4664ec806519ff6d594d547d8b31be5bc774fc0a | |
parent | 16acae729564ee0c3918342d8556cc42eeb29942 (diff) |
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>
-rw-r--r-- | drivers/irqchip/irq-gic-v3-its.c | 25 | ||||
-rw-r--r-- | include/linux/irqchip/arm-gic-v3.h | 2 |
2 files changed, 23 insertions, 4 deletions
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index c217ebcf7a48..733b32fda390 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c | |||
@@ -806,14 +806,31 @@ static int its_alloc_tables(struct its_node *its) | |||
806 | u64 val = readq_relaxed(its->base + GITS_BASER + i * 8); | 806 | u64 val = readq_relaxed(its->base + GITS_BASER + i * 8); |
807 | u64 type = GITS_BASER_TYPE(val); | 807 | u64 type = GITS_BASER_TYPE(val); |
808 | u64 entry_size = GITS_BASER_ENTRY_SIZE(val); | 808 | u64 entry_size = GITS_BASER_ENTRY_SIZE(val); |
809 | int order = 0; | ||
810 | int alloc_size; | ||
809 | u64 tmp; | 811 | u64 tmp; |
810 | void *base; | 812 | void *base; |
811 | 813 | ||
812 | if (type == GITS_BASER_TYPE_NONE) | 814 | if (type == GITS_BASER_TYPE_NONE) |
813 | continue; | 815 | continue; |
814 | 816 | ||
815 | /* We're lazy and only allocate a single page for now */ | 817 | /* |
816 | base = (void *)get_zeroed_page(GFP_KERNEL); | 818 | * Allocate as many entries as required to fit the |
819 | * range of device IDs that the ITS can grok... The ID | ||
820 | * space being incredibly sparse, this results in a | ||
821 | * massive waste of memory. | ||
822 | * | ||
823 | * For other tables, only allocate a single page. | ||
824 | */ | ||
825 | if (type == GITS_BASER_TYPE_DEVICE) { | ||
826 | u64 typer = readq_relaxed(its->base + GITS_TYPER); | ||
827 | u32 ids = GITS_TYPER_DEVBITS(typer); | ||
828 | |||
829 | order = get_order((1UL << ids) * entry_size); | ||
830 | } | ||
831 | |||
832 | alloc_size = (1 << order) * PAGE_SIZE; | ||
833 | base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order); | ||
817 | if (!base) { | 834 | if (!base) { |
818 | err = -ENOMEM; | 835 | err = -ENOMEM; |
819 | goto out_free; | 836 | goto out_free; |
@@ -841,7 +858,7 @@ retry_baser: | |||
841 | break; | 858 | break; |
842 | } | 859 | } |
843 | 860 | ||
844 | val |= (PAGE_SIZE / psz) - 1; | 861 | val |= (alloc_size / psz) - 1; |
845 | 862 | ||
846 | writeq_relaxed(val, its->base + GITS_BASER + i * 8); | 863 | writeq_relaxed(val, its->base + GITS_BASER + i * 8); |
847 | tmp = readq_relaxed(its->base + GITS_BASER + i * 8); | 864 | tmp = readq_relaxed(its->base + GITS_BASER + i * 8); |
@@ -882,7 +899,7 @@ retry_baser: | |||
882 | } | 899 | } |
883 | 900 | ||
884 | pr_info("ITS: allocated %d %s @%lx (psz %dK, shr %d)\n", | 901 | pr_info("ITS: allocated %d %s @%lx (psz %dK, shr %d)\n", |
885 | (int)(PAGE_SIZE / entry_size), | 902 | (int)(alloc_size / entry_size), |
886 | its_base_type_string[type], | 903 | its_base_type_string[type], |
887 | (unsigned long)virt_to_phys(base), | 904 | (unsigned long)virt_to_phys(base), |
888 | psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT); | 905 | psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT); |
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index 800544bc7bfd..cbdd440d486d 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h | |||
@@ -166,6 +166,8 @@ | |||
166 | 166 | ||
167 | #define GITS_TRANSLATER 0x10040 | 167 | #define GITS_TRANSLATER 0x10040 |
168 | 168 | ||
169 | #define GITS_TYPER_DEVBITS_SHIFT 13 | ||
170 | #define GITS_TYPER_DEVBITS(r) ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1) | ||
169 | #define GITS_TYPER_PTA (1UL << 19) | 171 | #define GITS_TYPER_PTA (1UL << 19) |
170 | 172 | ||
171 | #define GITS_CBASER_VALID (1UL << 63) | 173 | #define GITS_CBASER_VALID (1UL << 63) |