There are no standard CSR registers to provide cache information, the
way for RISC-V is to get this information from DT. Currently, AT_L1I_X,
AT_L1D_X and AT_L2_X are present in glibc header, and sysconf syscall
could use them to get information of cache through AUX vector. We
exploit 'struct cacheinfo' to obtain the information of cache, then we
don't need additional variable or data structure to record it.
We also need some works in glibc, but we have to support the function in
kernel first by rule of glibc, then post the patch to glibc site.
The result of 'getconf -a' as follows:
LEVEL1_ICACHE_SIZE 32768
LEVEL1_ICACHE_ASSOC 8
LEVEL1_ICACHE_LINESIZE 64
LEVEL1_DCACHE_SIZE 32768
LEVEL1_DCACHE_ASSOC 8
LEVEL1_DCACHE_LINESIZE 64
LEVEL2_CACHE_SIZE 2097152
LEVEL2_CACHE_ASSOC 32
LEVEL2_CACHE_LINESIZE 64
Changed in v2:
- Add error checking for parsing cache properties.
Zong Li (3):
riscv: Set more data to cacheinfo
riscv: Define AT_VECTOR_SIZE_ARCH for ARCH_DLINFO
riscv: Add cache information in AUX vector
arch/riscv/include/asm/cacheinfo.h | 5 ++
arch/riscv/include/asm/elf.h | 13 ++++
arch/riscv/include/uapi/asm/auxvec.h | 24 ++++++++
arch/riscv/kernel/cacheinfo.c | 91 +++++++++++++++++++++++-----
4 files changed, 117 insertions(+), 16 deletions(-)
--
2.28.0
Set cacheinfo.{size,sets,line_size} for each cache node, then we can
get these information from userland through auxiliary vector.
Signed-off-by: Zong Li <[email protected]>
---
arch/riscv/kernel/cacheinfo.c | 59 ++++++++++++++++++++++++++---------
1 file changed, 44 insertions(+), 15 deletions(-)
diff --git a/arch/riscv/kernel/cacheinfo.c b/arch/riscv/kernel/cacheinfo.c
index bd0f122965c3..8b85abfbd77a 100644
--- a/arch/riscv/kernel/cacheinfo.c
+++ b/arch/riscv/kernel/cacheinfo.c
@@ -25,12 +25,46 @@ cache_get_priv_group(struct cacheinfo *this_leaf)
return NULL;
}
-static void ci_leaf_init(struct cacheinfo *this_leaf,
- struct device_node *node,
- enum cache_type type, unsigned int level)
+static void ci_leaf_init(struct cacheinfo *this_leaf, enum cache_type type,
+ unsigned int level, unsigned int size,
+ unsigned int sets, unsigned int line_size)
{
this_leaf->level = level;
this_leaf->type = type;
+ this_leaf->size = size;
+ this_leaf->number_of_sets = sets;
+ this_leaf->coherency_line_size = line_size;
+
+ /*
+ * If the cache is fully associative, there is no need to
+ * check the other properties.
+ */
+ if (!(sets == 1) && (sets > 0 && size > 0 && line_size > 0))
+ this_leaf->ways_of_associativity = (size / sets) / line_size;
+}
+
+static void fill_cacheinfo(struct cacheinfo **this_leaf,
+ struct device_node *node, unsigned int level)
+{
+ unsigned int size, sets, line_size;
+
+ if (!of_property_read_u32(node, "cache-size", &size) &&
+ !of_property_read_u32(node, "cache-block-size", &line_size) &&
+ !of_property_read_u32(node, "cache-sets", &sets)) {
+ ci_leaf_init((*this_leaf)++, CACHE_TYPE_UNIFIED, level, size, sets, line_size);
+ }
+
+ if (!of_property_read_u32(node, "i-cache-size", &size) &&
+ !of_property_read_u32(node, "i-cache-sets", &sets) &&
+ !of_property_read_u32(node, "i-cache-block-size", &line_size)) {
+ ci_leaf_init((*this_leaf)++, CACHE_TYPE_INST, level, size, sets, line_size);
+ }
+
+ if (!of_property_read_u32(node, "d-cache-size", &size) &&
+ !of_property_read_u32(node, "d-cache-sets", &sets) &&
+ !of_property_read_u32(node, "d-cache-block-size", &line_size)) {
+ ci_leaf_init((*this_leaf)++, CACHE_TYPE_DATA, level, size, sets, line_size);
+ }
}
static int __init_cache_level(unsigned int cpu)
@@ -83,29 +117,24 @@ static int __populate_cache_leaves(unsigned int cpu)
struct device_node *prev = NULL;
int levels = 1, level = 1;
- if (of_property_read_bool(np, "cache-size"))
- ci_leaf_init(this_leaf++, np, CACHE_TYPE_UNIFIED, level);
- if (of_property_read_bool(np, "i-cache-size"))
- ci_leaf_init(this_leaf++, np, CACHE_TYPE_INST, level);
- if (of_property_read_bool(np, "d-cache-size"))
- ci_leaf_init(this_leaf++, np, CACHE_TYPE_DATA, level);
+ /* Level 1 caches in cpu node */
+ fill_cacheinfo(&this_leaf, np, level);
+ /* Next level caches in cache nodes */
prev = np;
while ((np = of_find_next_cache_node(np))) {
of_node_put(prev);
prev = np;
+
if (!of_device_is_compatible(np, "cache"))
break;
if (of_property_read_u32(np, "cache-level", &level))
break;
if (level <= levels)
break;
- if (of_property_read_bool(np, "cache-size"))
- ci_leaf_init(this_leaf++, np, CACHE_TYPE_UNIFIED, level);
- if (of_property_read_bool(np, "i-cache-size"))
- ci_leaf_init(this_leaf++, np, CACHE_TYPE_INST, level);
- if (of_property_read_bool(np, "d-cache-size"))
- ci_leaf_init(this_leaf++, np, CACHE_TYPE_DATA, level);
+
+ fill_cacheinfo(&this_leaf, np, level);
+
levels = level;
}
of_node_put(np);
--
2.28.0
There are no standard CSR registers to provide cache information, the
way for RISC-V is to get this information from DT. Currently, AT_L1I_X,
AT_L1D_X and AT_L2_X are present in glibc header, and sysconf syscall
could use them to get information of cache through AUX vector.
The result of 'getconf -a' as follows:
LEVEL1_ICACHE_SIZE 32768
LEVEL1_ICACHE_ASSOC 8
LEVEL1_ICACHE_LINESIZE 64
LEVEL1_DCACHE_SIZE 32768
LEVEL1_DCACHE_ASSOC 8
LEVEL1_DCACHE_LINESIZE 64
LEVEL2_CACHE_SIZE 2097152
LEVEL2_CACHE_ASSOC 32
LEVEL2_CACHE_LINESIZE 64
Signed-off-by: Zong Li <[email protected]>
Reviewed-by: Palmer Dabbelt <[email protected]>
---
arch/riscv/include/asm/cacheinfo.h | 5 +++++
arch/riscv/include/asm/elf.h | 13 +++++++++++
arch/riscv/include/uapi/asm/auxvec.h | 23 +++++++++++++++++++-
arch/riscv/kernel/cacheinfo.c | 32 +++++++++++++++++++++++++++-
4 files changed, 71 insertions(+), 2 deletions(-)
diff --git a/arch/riscv/include/asm/cacheinfo.h b/arch/riscv/include/asm/cacheinfo.h
index 5d9662e9aba8..d1a365215ec0 100644
--- a/arch/riscv/include/asm/cacheinfo.h
+++ b/arch/riscv/include/asm/cacheinfo.h
@@ -1,4 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020 SiFive
+ */
#ifndef _ASM_RISCV_CACHEINFO_H
#define _ASM_RISCV_CACHEINFO_H
@@ -11,5 +14,7 @@ struct riscv_cacheinfo_ops {
};
void riscv_set_cacheinfo_ops(struct riscv_cacheinfo_ops *ops);
+uintptr_t get_cache_size(u32 level, enum cache_type type);
+uintptr_t get_cache_geometry(u32 level, enum cache_type type);
#endif /* _ASM_RISCV_CACHEINFO_H */
diff --git a/arch/riscv/include/asm/elf.h b/arch/riscv/include/asm/elf.h
index d83a4efd052b..5c725e1df58b 100644
--- a/arch/riscv/include/asm/elf.h
+++ b/arch/riscv/include/asm/elf.h
@@ -11,6 +11,7 @@
#include <uapi/asm/elf.h>
#include <asm/auxvec.h>
#include <asm/byteorder.h>
+#include <asm/cacheinfo.h>
/*
* These are used to set parameters in the core dumps.
@@ -61,6 +62,18 @@ extern unsigned long elf_hwcap;
do { \
NEW_AUX_ENT(AT_SYSINFO_EHDR, \
(elf_addr_t)current->mm->context.vdso); \
+ NEW_AUX_ENT(AT_L1I_CACHESIZE, \
+ get_cache_size(1, CACHE_TYPE_INST)); \
+ NEW_AUX_ENT(AT_L1I_CACHEGEOMETRY, \
+ get_cache_geometry(1, CACHE_TYPE_INST)); \
+ NEW_AUX_ENT(AT_L1D_CACHESIZE, \
+ get_cache_size(1, CACHE_TYPE_DATA)); \
+ NEW_AUX_ENT(AT_L1D_CACHEGEOMETRY, \
+ get_cache_geometry(1, CACHE_TYPE_DATA)); \
+ NEW_AUX_ENT(AT_L2_CACHESIZE, \
+ get_cache_size(2, CACHE_TYPE_UNIFIED)); \
+ NEW_AUX_ENT(AT_L2_CACHEGEOMETRY, \
+ get_cache_geometry(2, CACHE_TYPE_UNIFIED)); \
} while (0)
#define ARCH_HAS_SETUP_ADDITIONAL_PAGES
struct linux_binprm;
diff --git a/arch/riscv/include/uapi/asm/auxvec.h b/arch/riscv/include/uapi/asm/auxvec.h
index 22e0ae888406..32c73ba1d531 100644
--- a/arch/riscv/include/uapi/asm/auxvec.h
+++ b/arch/riscv/include/uapi/asm/auxvec.h
@@ -10,7 +10,28 @@
/* vDSO location */
#define AT_SYSINFO_EHDR 33
+/*
+ * The set of entries below represent more extensive information
+ * about the caches, in the form of two entry per cache type,
+ * one entry containing the cache size in bytes, and the other
+ * containing the cache line size in bytes in the bottom 16 bits
+ * and the cache associativity in the next 16 bits.
+ *
+ * The associativity is such that if N is the 16-bit value, the
+ * cache is N way set associative. A value if 0xffff means fully
+ * associative, a value of 1 means directly mapped.
+ *
+ * For all these fields, a value of 0 means that the information
+ * is not known.
+ */
+#define AT_L1I_CACHESIZE 40
+#define AT_L1I_CACHEGEOMETRY 41
+#define AT_L1D_CACHESIZE 42
+#define AT_L1D_CACHEGEOMETRY 43
+#define AT_L2_CACHESIZE 44
+#define AT_L2_CACHEGEOMETRY 45
+
/* entries in ARCH_DLINFO */
-#define AT_VECTOR_SIZE_ARCH 1
+#define AT_VECTOR_SIZE_ARCH 7
#endif /* _UAPI_ASM_RISCV_AUXVEC_H */
diff --git a/arch/riscv/kernel/cacheinfo.c b/arch/riscv/kernel/cacheinfo.c
index 8b85abfbd77a..13cd23cb8548 100644
--- a/arch/riscv/kernel/cacheinfo.c
+++ b/arch/riscv/kernel/cacheinfo.c
@@ -3,7 +3,6 @@
* Copyright (C) 2017 SiFive
*/
-#include <linux/cacheinfo.h>
#include <linux/cpu.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -25,6 +24,37 @@ cache_get_priv_group(struct cacheinfo *this_leaf)
return NULL;
}
+static struct cacheinfo *get_cacheinfo(u32 level, enum cache_type type)
+{
+ struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(smp_processor_id());
+ struct cacheinfo *this_leaf;
+ int index;
+
+ for (index = 0; index < this_cpu_ci->num_leaves; index++) {
+ this_leaf = this_cpu_ci->info_list + index;
+ if (this_leaf->level == level && this_leaf->type == type)
+ return this_leaf;
+ }
+
+ return 0;
+}
+
+uintptr_t get_cache_size(u32 level, enum cache_type type)
+{
+ struct cacheinfo *this_leaf = get_cacheinfo(level, type);
+
+ return this_leaf ? this_leaf->size : 0;
+}
+
+uintptr_t get_cache_geometry(u32 level, enum cache_type type)
+{
+ struct cacheinfo *this_leaf = get_cacheinfo(level, type);
+ uintptr_t ret = (this_leaf->ways_of_associativity << 16 |
+ this_leaf->coherency_line_size);
+
+ return this_leaf ? ret : 0;
+}
+
static void ci_leaf_init(struct cacheinfo *this_leaf, enum cache_type type,
unsigned int level, unsigned int size,
unsigned int sets, unsigned int line_size)
--
2.28.0
AT_VECTOR_SIZE_ARCH should be defined with the maximum number of
NEW_AUX_ENT entries that ARCH_DLINFO can contain, but it wasn't defined
for RISC-V at all even though ARCH_DLINFO will contain one NEW_AUX_ENT
for the VDSO address.
Signed-off-by: Zong Li <[email protected]>
Reviewed-by: Palmer Dabbelt <[email protected]>
---
arch/riscv/include/uapi/asm/auxvec.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/riscv/include/uapi/asm/auxvec.h b/arch/riscv/include/uapi/asm/auxvec.h
index d86cb17bbabe..22e0ae888406 100644
--- a/arch/riscv/include/uapi/asm/auxvec.h
+++ b/arch/riscv/include/uapi/asm/auxvec.h
@@ -10,4 +10,7 @@
/* vDSO location */
#define AT_SYSINFO_EHDR 33
+/* entries in ARCH_DLINFO */
+#define AT_VECTOR_SIZE_ARCH 1
+
#endif /* _UAPI_ASM_RISCV_AUXVEC_H */
--
2.28.0
Hi Zong,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on linus/master]
[also build test WARNING on v5.9-rc2 next-20200827]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Zong-Li/Get-cache-information-from-userland/20200827-162439
base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 15bc20c6af4ceee97a1f90b43c0e386643c071b4
config: riscv-randconfig-s032-20200827 (attached as .config)
compiler: riscv64-linux-gcc (GCC) 9.3.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# apt-get install sparse
# sparse version: v0.6.2-191-g10164920-dirty
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' ARCH=riscv
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>
sparse warnings: (new ones prefixed by >>)
>> arch/riscv/kernel/cacheinfo.c:39:16: sparse: sparse: Using plain integer as NULL pointer
# https://github.com/0day-ci/linux/commit/a51c248ba0626069792c3f84c8879f685f4a1ff6
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Zong-Li/Get-cache-information-from-userland/20200827-162439
git checkout a51c248ba0626069792c3f84c8879f685f4a1ff6
vim +39 arch/riscv/kernel/cacheinfo.c
26
27 static struct cacheinfo *get_cacheinfo(u32 level, enum cache_type type)
28 {
29 struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(smp_processor_id());
30 struct cacheinfo *this_leaf;
31 int index;
32
33 for (index = 0; index < this_cpu_ci->num_leaves; index++) {
34 this_leaf = this_cpu_ci->info_list + index;
35 if (this_leaf->level == level && this_leaf->type == type)
36 return this_leaf;
37 }
38
> 39 return 0;
40 }
41
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]
Hi Zong,
url: https://github.com/0day-ci/linux/commits/Zong-Li/Get-cache-information-from-userland/20200827-162439
base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 15bc20c6af4ceee97a1f90b43c0e386643c071b4
config: riscv-randconfig-m031-20200828 (attached as .config)
compiler: riscv64-linux-gcc (GCC) 9.3.0
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <[email protected]>
Reported-by: Dan Carpenter <[email protected]>
smatch warnings:
arch/riscv/kernel/cacheinfo.c:55 get_cache_geometry() warn: variable dereferenced before check 'this_leaf' (see line 52)
# https://github.com/0day-ci/linux/commit/a51c248ba0626069792c3f84c8879f685f4a1ff6
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Zong-Li/Get-cache-information-from-userland/20200827-162439
git checkout a51c248ba0626069792c3f84c8879f685f4a1ff6
vim +/this_leaf +55 arch/riscv/kernel/cacheinfo.c
a51c248ba06260 Zong Li 2020-08-27 49 uintptr_t get_cache_geometry(u32 level, enum cache_type type)
a51c248ba06260 Zong Li 2020-08-27 50 {
a51c248ba06260 Zong Li 2020-08-27 51 struct cacheinfo *this_leaf = get_cacheinfo(level, type);
a51c248ba06260 Zong Li 2020-08-27 @52 uintptr_t ret = (this_leaf->ways_of_associativity << 16 |
a51c248ba06260 Zong Li 2020-08-27 53 this_leaf->coherency_line_size);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Dereferenced
a51c248ba06260 Zong Li 2020-08-27 54
a51c248ba06260 Zong Li 2020-08-27 @55 return this_leaf ? ret : 0;
^^^^^^^^^
Checked too late.
a51c248ba06260 Zong Li 2020-08-27 56 }
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/[email protected]
On Thu, 27 Aug 2020 01:22:25 PDT (-0700), [email protected] wrote:
> There are no standard CSR registers to provide cache information, the
> way for RISC-V is to get this information from DT. Currently, AT_L1I_X,
> AT_L1D_X and AT_L2_X are present in glibc header, and sysconf syscall
> could use them to get information of cache through AUX vector. We
> exploit 'struct cacheinfo' to obtain the information of cache, then we
> don't need additional variable or data structure to record it.
>
> We also need some works in glibc, but we have to support the function in
> kernel first by rule of glibc, then post the patch to glibc site.
>
> The result of 'getconf -a' as follows:
>
> LEVEL1_ICACHE_SIZE 32768
> LEVEL1_ICACHE_ASSOC 8
> LEVEL1_ICACHE_LINESIZE 64
> LEVEL1_DCACHE_SIZE 32768
> LEVEL1_DCACHE_ASSOC 8
> LEVEL1_DCACHE_LINESIZE 64
> LEVEL2_CACHE_SIZE 2097152
> LEVEL2_CACHE_ASSOC 32
> LEVEL2_CACHE_LINESIZE 64
>
> Changed in v2:
> - Add error checking for parsing cache properties.
>
> Zong Li (3):
> riscv: Set more data to cacheinfo
> riscv: Define AT_VECTOR_SIZE_ARCH for ARCH_DLINFO
> riscv: Add cache information in AUX vector
>
> arch/riscv/include/asm/cacheinfo.h | 5 ++
> arch/riscv/include/asm/elf.h | 13 ++++
> arch/riscv/include/uapi/asm/auxvec.h | 24 ++++++++
> arch/riscv/kernel/cacheinfo.c | 91 +++++++++++++++++++++++-----
> 4 files changed, 117 insertions(+), 16 deletions(-)
Thanks, these are on for-next.