Skip to contentSkip to conversation

arm64: add helper functions to read I-cache attributes

PATCH Superseded Ard Biesheuvel 2 participants 4 messages Aug 6, 2014 – Aug 7, 2014 arm64

v3 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

PATCH v3 1/2 arm64: add helper functions to read I-cache attributes

This adds helper functions and #defines to <asm/cachetype.h> to read the line size and the number of sets from the level 1 instruction cache.

---
v3: add WARN_ON(preemptible()), move icache_get_ccsidr() to cpuinfo.c to
prevent #include header recursion hell
v2: put () around macro args, use 64-bit types for asm() mrs/msr calls

 arch/arm64/include/asm/cachetype.h | 20 ++++++++++++++++++++
 arch/arm64/kernel/cpuinfo.c        | 14 ++++++++++++++
 2 files changed, 34 insertions(+)
arch/arm64/include/asm/cachetype.h +20 -0
index 7a2e0762cb40..4c631a0a3609 100644
@@ -39,6 +39,26 @@
 
 extern unsigned long __icache_flags;
 
+#define CCSIDR_EL1_LINESIZE_MASK	0x7
+#define CCSIDR_EL1_LINESIZE(x)		((x) & CCSIDR_EL1_LINESIZE_MASK)
+
+#define CCSIDR_EL1_NUMSETS_SHIFT	13
+#define CCSIDR_EL1_NUMSETS_MASK		(0x7fff << CCSIDR_EL1_NUMSETS_SHIFT)
+#define CCSIDR_EL1_NUMSETS(x) \
+	(((x) & CCSIDR_EL1_NUMSETS_MASK) >> CCSIDR_EL1_NUMSETS_SHIFT)
+
+extern u64 __attribute_const__ icache_get_ccsidr(void);
+
+static inline int icache_get_linesize(void)
+{
+	return 16 << CCSIDR_EL1_LINESIZE(icache_get_ccsidr());
+}
+
+static inline int icache_get_numsets(void)
+{
+	return 1 + CCSIDR_EL1_NUMSETS(icache_get_ccsidr());
+}
+
 /*
  * Whilst the D-side always behaves as PIPT on AArch64, aliasing is
  * permitted in the I-cache.
arch/arm64/kernel/cpuinfo.c +14 -0
index f798f66634af..319255ff536d 100644
@@ -20,8 +20,10 @@
 #include <asm/cputype.h>
 
 #include <linux/bitops.h>
+#include <linux/bug.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/preempt.h>
 #include <linux/printk.h>
 #include <linux/smp.h>
 
@@ -190,3 +192,15 @@ void __init cpuinfo_store_boot_cpu(void)
 
 	boot_cpu_data = *info;
 }
+
+u64 __attribute_const__ icache_get_ccsidr(void)
+{
+	u64 ccsidr;
+
+	WARN_ON(preemptible());
+
+	/* Select L1 I-cache and read its size ID register */
+	asm("msr csselr_el1, %1; isb; mrs %0, ccsidr_el1"
+	    : "=r"(ccsidr) : "r"(1L));
+	return ccsidr;
+}
Signature · 1 line
1.8.3.2
#1 lore raw

PATCH v3 2/2 arm64: don't flag non-aliasing VIPT I-caches as aliasing

VIPT caches are non-aliasing if the index is derived from address bits that are always equal between VA and PA. Classifying these as aliasing results in unnecessary flushing which may hurt performance.

---
v2, v3: no changes

 arch/arm64/kernel/cpuinfo.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
arch/arm64/kernel/cpuinfo.c +7 -1
index 319255ff536d..a5b6dce48094 100644
@@ -49,7 +49,13 @@ static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info)
 	unsigned int cpu = smp_processor_id();
 	u32 l1ip = CTR_L1IP(info->reg_ctr);
 
-	if (l1ip != ICACHE_POLICY_PIPT)
+	/*
+	 * VIPT caches are non-aliasing if the VA always equals the PA in all
+	 * bit positions that are covered by the index, i.e., if num_sets_shift
+	 * is less than or equal to PAGE_SHIFT minus line_size_shift.
+	 */
+	if (l1ip != ICACHE_POLICY_PIPT && !(l1ip == ICACHE_POLICY_VIPT &&
+	    icache_get_linesize() * icache_get_numsets() <= PAGE_SIZE))
 		set_bit(ICACHEF_ALIASING, &__icache_flags);
 	if (l1ip == ICACHE_POLICY_AIVIVT);
 		set_bit(ICACHEF_AIVIVT, &__icache_flags);
Signature · 1 line
1.8.3.2
#2 lore raw

PATCH v3 2/2 arm64: don't flag non-aliasing VIPT I-caches as aliasing

Quoted 17 lines · Ard Biesheuvel

VIPT caches are non-aliasing if the index is derived from address bits that are always equal between VA and PA. Classifying these as aliasing results in unnecessary flushing which may hurt performance.

---
v2, v3: no changes

 arch/arm64/kernel/cpuinfo.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
arch/arm64/kernel/cpuinfo.c +0 -0
index 319255ff536d..a5b6dce48094 100644
@@ -49,7 +49,13 @@ static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info)
 	unsigned int cpu = smp_processor_id();

arch/arm64/kernel/cpuinfo.c static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info) · line 58

Quoted 10 lines
arch/arm64/kernel/cpuinfo.c +7 -1
 	u32 l1ip = CTR_L1IP(info->reg_ctr);
 
-	if (l1ip != ICACHE_POLICY_PIPT)
+	/*
+	 * VIPT caches are non-aliasing if the VA always equals the PA in all
+	 * bit positions that are covered by the index, i.e., if num_sets_shift
+	 * is less than or equal to PAGE_SHIFT minus line_size_shift.
+	 */
+	if (l1ip != ICACHE_POLICY_PIPT && !(l1ip == ICACHE_POLICY_VIPT &&
+	    icache_get_linesize() * icache_get_numsets() <= PAGE_SIZE))

Might just be me, but I'd find this a lot easier to understand if you had a local `way_size' variable and set it to linesize * num_sets.

Either way:

Will

#3 lore raw

PATCH v3 1/2 arm64: add helper functions to read I-cache attributes

In reply to Ard Biesheuvel
Quoted 4 lines · Ard Biesheuvel

This adds helper functions and #defines to <asm/cachetype.h> to read the line size and the number of sets from the level 1 instruction cache.

Quoted 8 lines

---
v3: add WARN_ON(preemptible()), move icache_get_ccsidr() to cpuinfo.c to
prevent #include header recursion hell
v2: put () around macro args, use 64-bit types for asm() mrs/msr calls

 arch/arm64/include/asm/cachetype.h | 20 ++++++++++++++++++++
 arch/arm64/kernel/cpuinfo.c        | 14 ++++++++++++++
 2 files changed, 34 insertions(+)

Will