* [PATCH 0/5] riscv: optimize string functions and add kunit tests
@ 2026-01-07 2:35 Feng Jiang
2026-01-07 2:35 ` [PATCH 1/5] lib/string_kunit: add test case for strlen Feng Jiang
` (4 more replies)
0 siblings, 5 replies; 12+ messages in thread
From: Feng Jiang @ 2026-01-07 2:35 UTC (permalink / raw)
To: pjw, palmer, aou, alex, kees, andy, jiangfeng, ebiggers,
martin.petersen, Jason, conor.dooley, samuel.holland, charlie,
ajones, nathan, linus.walleij
Cc: linux-riscv, linux-kernel, linux-hardening
This series introduces optimized assembly implementations for several
string functions on the RISC-V architecture (strnlen, strchr, and strrchr).
To ensure correctness and prevent regressions, it also extends the
string_kunit test suite with new cases.
The patchset is organized as follows:
- Patches 1-2: Enhance lib/string_kunit.c with new test cases for
strlen and strnlen to establish a baseline for verification.
- Patches 3-5: Provide the RISC-V specific optimized implementations
for strnlen, strchr, and strrchr.
Testing:
All patches have been verified using the KUnit framework on QEMU
virt machine (riscv64). All string-related tests passed.
Changes:
- v1: Initial submission.
---
Feng Jiang (5):
lib/string_kunit: add test case for strlen
lib/string_kunit: add test case for strnlen
riscv: lib: add strnlen implementation
riscv: lib: add strchr implementation
riscv: lib: add strrchr implementation
arch/riscv/include/asm/string.h | 9 ++
arch/riscv/lib/Makefile | 3 +
arch/riscv/lib/strchr.S | 35 +++++++
arch/riscv/lib/strnlen.S | 164 ++++++++++++++++++++++++++++++++
arch/riscv/lib/strrchr.S | 37 +++++++
arch/riscv/purgatory/Makefile | 11 ++-
lib/tests/string_kunit.c | 53 +++++++++++
7 files changed, 311 insertions(+), 1 deletion(-)
create mode 100644 arch/riscv/lib/strchr.S
create mode 100644 arch/riscv/lib/strnlen.S
create mode 100644 arch/riscv/lib/strrchr.S
--
2.25.1
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 1/5] lib/string_kunit: add test case for strlen
2026-01-07 2:35 [PATCH 0/5] riscv: optimize string functions and add kunit tests Feng Jiang
@ 2026-01-07 2:35 ` Feng Jiang
2026-01-07 11:48 ` Andy Shevchenko
2026-01-07 2:35 ` [PATCH 2/5] lib/string_kunit: add test case for strnlen Feng Jiang
` (3 subsequent siblings)
4 siblings, 1 reply; 12+ messages in thread
From: Feng Jiang @ 2026-01-07 2:35 UTC (permalink / raw)
To: pjw, palmer, aou, alex, kees, andy, jiangfeng, ebiggers,
martin.petersen, Jason, conor.dooley, samuel.holland, charlie,
ajones, nathan, linus.walleij
Cc: linux-riscv, linux-kernel, linux-hardening
Add a KUnit test for strlen() to verify correctness across
different string lengths and memory alignments.
Signed-off-by: Feng Jiang <jiangfeng@kylinos.cn>
---
lib/tests/string_kunit.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/lib/tests/string_kunit.c b/lib/tests/string_kunit.c
index f9a8e557ba77..9f4fc72f0886 100644
--- a/lib/tests/string_kunit.c
+++ b/lib/tests/string_kunit.c
@@ -104,6 +104,28 @@ static void string_test_memset64(struct kunit *test)
}
}
+static void string_test_strlen(struct kunit *test)
+{
+ char *s;
+ size_t len, offset;
+ const size_t buf_size = 16 + 128 + 1; /* max_offset + max_len + '\0' */
+
+ s = kunit_kzalloc(test, buf_size, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, s);
+
+ memset(s, 'A', buf_size);
+ s[buf_size - 1] = '\0';
+
+ for (offset = 0; offset < 16; offset++) {
+ for (len = 0; len <= 128; len++) {
+ s[offset + len] = '\0';
+ KUNIT_EXPECT_EQ_MSG(test, strlen(s + offset), len,
+ "offset:%zu len:%zu", offset, len);
+ s[offset + len] = 'A';
+ }
+ }
+}
+
static void string_test_strchr(struct kunit *test)
{
const char *test_string = "abcdefghijkl";
@@ -618,6 +640,7 @@ static struct kunit_case string_test_cases[] = {
KUNIT_CASE(string_test_memset16),
KUNIT_CASE(string_test_memset32),
KUNIT_CASE(string_test_memset64),
+ KUNIT_CASE(string_test_strlen),
KUNIT_CASE(string_test_strchr),
KUNIT_CASE(string_test_strnchr),
KUNIT_CASE(string_test_strspn),
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 2/5] lib/string_kunit: add test case for strnlen
2026-01-07 2:35 [PATCH 0/5] riscv: optimize string functions and add kunit tests Feng Jiang
2026-01-07 2:35 ` [PATCH 1/5] lib/string_kunit: add test case for strlen Feng Jiang
@ 2026-01-07 2:35 ` Feng Jiang
2026-01-07 11:56 ` Andy Shevchenko
2026-01-07 2:35 ` [PATCH 3/5] riscv: lib: add strnlen implementation Feng Jiang
` (2 subsequent siblings)
4 siblings, 1 reply; 12+ messages in thread
From: Feng Jiang @ 2026-01-07 2:35 UTC (permalink / raw)
To: pjw, palmer, aou, alex, kees, andy, jiangfeng, ebiggers,
martin.petersen, Jason, conor.dooley, samuel.holland, charlie,
ajones, nathan, linus.walleij
Cc: linux-riscv, linux-kernel, linux-hardening
Add a KUnit test for strnlen() to verify correctness across
different string lengths and memory alignments.
Signed-off-by: Feng Jiang <jiangfeng@kylinos.cn>
---
lib/tests/string_kunit.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/lib/tests/string_kunit.c b/lib/tests/string_kunit.c
index 9f4fc72f0886..1d08850e439c 100644
--- a/lib/tests/string_kunit.c
+++ b/lib/tests/string_kunit.c
@@ -126,6 +126,35 @@ static void string_test_strlen(struct kunit *test)
}
}
+static void string_test_strnlen(struct kunit *test)
+{
+ char *s;
+ size_t len, offset;
+ const size_t buf_size = 16 + 128 + 1; /* max_offset + max_len + '\0' */
+
+ s = kunit_kzalloc(test, buf_size, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, s);
+
+ memset(s, 'A', buf_size);
+ s[buf_size - 1] = '\0';
+
+ for (offset = 0; offset < 16; offset++) {
+ for (len = 0; len <= 128; len++) {
+ s[offset + len] = '\0';
+
+ if (len > 0)
+ KUNIT_EXPECT_EQ(test, strnlen(s + offset, len - 1), len - 1);
+
+ KUNIT_EXPECT_EQ(test, strnlen(s + offset, len), len);
+
+ KUNIT_EXPECT_EQ(test, strnlen(s + offset, len + 1), len);
+ KUNIT_EXPECT_EQ(test, strnlen(s + offset, len + 10), len);
+
+ s[offset + len] = 'A';
+ }
+ }
+}
+
static void string_test_strchr(struct kunit *test)
{
const char *test_string = "abcdefghijkl";
@@ -641,6 +670,7 @@ static struct kunit_case string_test_cases[] = {
KUNIT_CASE(string_test_memset32),
KUNIT_CASE(string_test_memset64),
KUNIT_CASE(string_test_strlen),
+ KUNIT_CASE(string_test_strnlen),
KUNIT_CASE(string_test_strchr),
KUNIT_CASE(string_test_strnchr),
KUNIT_CASE(string_test_strspn),
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 3/5] riscv: lib: add strnlen implementation
2026-01-07 2:35 [PATCH 0/5] riscv: optimize string functions and add kunit tests Feng Jiang
2026-01-07 2:35 ` [PATCH 1/5] lib/string_kunit: add test case for strlen Feng Jiang
2026-01-07 2:35 ` [PATCH 2/5] lib/string_kunit: add test case for strnlen Feng Jiang
@ 2026-01-07 2:35 ` Feng Jiang
2026-01-07 2:35 ` [PATCH 4/5] riscv: lib: add strchr implementation Feng Jiang
2026-01-07 2:35 ` [PATCH 5/5] riscv: lib: add strrchr implementation Feng Jiang
4 siblings, 0 replies; 12+ messages in thread
From: Feng Jiang @ 2026-01-07 2:35 UTC (permalink / raw)
To: pjw, palmer, aou, alex, kees, andy, jiangfeng, ebiggers,
martin.petersen, Jason, conor.dooley, samuel.holland, charlie,
ajones, nathan, linus.walleij
Cc: linux-riscv, linux-kernel, linux-hardening
Add strnlen() implementation for RISC-V with both generic and
Zbb-optimized versions, derived from strlen.S.
Signed-off-by: Feng Jiang <jiangfeng@kylinos.cn>
---
arch/riscv/include/asm/string.h | 3 +
arch/riscv/lib/Makefile | 1 +
arch/riscv/lib/strnlen.S | 164 ++++++++++++++++++++++++++++++++
arch/riscv/purgatory/Makefile | 5 +-
4 files changed, 172 insertions(+), 1 deletion(-)
create mode 100644 arch/riscv/lib/strnlen.S
diff --git a/arch/riscv/include/asm/string.h b/arch/riscv/include/asm/string.h
index 5ba77f60bf0b..16634d67c217 100644
--- a/arch/riscv/include/asm/string.h
+++ b/arch/riscv/include/asm/string.h
@@ -28,6 +28,9 @@ extern asmlinkage __kernel_size_t strlen(const char *);
#define __HAVE_ARCH_STRNCMP
extern asmlinkage int strncmp(const char *cs, const char *ct, size_t count);
+
+#define __HAVE_ARCH_STRNLEN
+extern asmlinkage __kernel_size_t strnlen(const char *, size_t);
#endif
/* For those files which don't want to check by kasan. */
diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
index bbc031124974..0969d8136df0 100644
--- a/arch/riscv/lib/Makefile
+++ b/arch/riscv/lib/Makefile
@@ -7,6 +7,7 @@ ifeq ($(CONFIG_KASAN_GENERIC)$(CONFIG_KASAN_SW_TAGS),)
lib-y += strcmp.o
lib-y += strlen.o
lib-y += strncmp.o
+lib-y += strnlen.o
endif
lib-y += csum.o
ifeq ($(CONFIG_MMU), y)
diff --git a/arch/riscv/lib/strnlen.S b/arch/riscv/lib/strnlen.S
new file mode 100644
index 000000000000..4af0df9442f1
--- /dev/null
+++ b/arch/riscv/lib/strnlen.S
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ * Base on arch/riscv/lib/strlen.S
+ *
+ * Copyright (C) Feng Jiang <jiangfeng@kylinos.cn>
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm.h>
+#include <asm/alternative-macros.h>
+#include <asm/hwcap.h>
+
+/* size_t strnlen(const char *s, size_t count) */
+SYM_FUNC_START(strnlen)
+
+ __ALTERNATIVE_CFG("nop", "j strnlen_zbb", 0, RISCV_ISA_EXT_ZBB,
+ IS_ENABLED(CONFIG_RISCV_ISA_ZBB) && IS_ENABLED(CONFIG_TOOLCHAIN_HAS_ZBB))
+
+
+ /*
+ * Returns
+ * a0 - String length
+ *
+ * Parameters
+ * a0 - String to measure
+ * a1 - Max length of string
+ *
+ * Clobbers
+ * t0, t1, t2
+ */
+ addi t1, a0, -1
+ add t2, a0, a1
+1:
+ addi t1, t1, 1
+ beq t1, t2, 2f
+ lbu t0, 0(t1)
+ bnez t0, 1b
+2:
+ sub a0, t1, a0
+ ret
+
+
+/*
+ * Variant of strnlen using the ZBB extension if available
+ */
+#if defined(CONFIG_RISCV_ISA_ZBB) && defined(CONFIG_TOOLCHAIN_HAS_ZBB)
+strnlen_zbb:
+
+#ifdef CONFIG_CPU_BIG_ENDIAN
+# define CZ clz
+# define SHIFT sll
+#else
+# define CZ ctz
+# define SHIFT srl
+#endif
+
+.option push
+.option arch,+zbb
+
+ /*
+ * Returns
+ * a0 - String length
+ *
+ * Parameters
+ * a0 - String to measure
+ * a1 - Max length of string
+ *
+ * Clobbers
+ * t0, t1, t2, t3, t4
+ */
+
+ /* If maxlen is 0, return 0. */
+ beqz a1, 3f
+
+ /* Number of irrelevant bytes in the first word. */
+ andi t2, a0, SZREG-1
+
+ /* Align pointer. */
+ andi t0, a0, -SZREG
+
+ li t3, SZREG
+ sub t3, t3, t2
+ slli t2, t2, 3
+
+ /* Aligned boundary. */
+ add t4, a0, a1
+ andi t4, t4, -SZREG
+
+ /* Get the first word. */
+ REG_L t1, 0(t0)
+
+ /*
+ * Shift away the partial data we loaded to remove the irrelevant bytes
+ * preceding the string with the effect of adding NUL bytes at the
+ * end of the string's first word.
+ */
+ SHIFT t1, t1, t2
+
+ /* Convert non-NUL into 0xff and NUL into 0x00. */
+ orc.b t1, t1
+
+ /* Convert non-NUL into 0x00 and NUL into 0xff. */
+ not t1, t1
+
+ /*
+ * Search for the first set bit (corresponding to a NUL byte in the
+ * original chunk).
+ */
+ CZ t1, t1
+
+ /*
+ * The first chunk is special: compare against the number
+ * of valid bytes in this chunk.
+ */
+ srli a0, t1, 3
+
+ /* Limit the result by maxlen. */
+ bleu a1, a0, 3f
+
+ bgtu t3, a0, 2f
+
+ /* Prepare for the word comparison loop. */
+ addi t2, t0, SZREG
+ li t3, -1
+
+ /*
+ * Our critical loop is 4 instructions and processes data in
+ * 4 byte or 8 byte chunks.
+ */
+ .p2align 3
+1:
+ REG_L t1, SZREG(t0)
+ addi t0, t0, SZREG
+ orc.b t1, t1
+ bgeu t0, t4, 4f
+ beq t1, t3, 1b
+4:
+ not t1, t1
+ CZ t1, t1
+ srli t1, t1, 3
+
+ /* Get number of processed bytes. */
+ sub t2, t0, t2
+
+ /* Add number of characters in the first word. */
+ add a0, a0, t2
+
+ /* Add number of characters in the last word. */
+ add a0, a0, t1
+
+ /* Ensure the final result does not exceed maxlen. */
+ bgeu a0, a1, 3f
+2:
+ ret
+3:
+ mv a0, a1
+ ret
+
+.option pop
+#endif
+SYM_FUNC_END(strnlen)
+SYM_FUNC_ALIAS(__pi_strnlen, strnlen)
+EXPORT_SYMBOL(strnlen)
diff --git a/arch/riscv/purgatory/Makefile b/arch/riscv/purgatory/Makefile
index 530e497ca2f9..d7c0533108be 100644
--- a/arch/riscv/purgatory/Makefile
+++ b/arch/riscv/purgatory/Makefile
@@ -2,7 +2,7 @@
purgatory-y := purgatory.o sha256.o entry.o string.o ctype.o memcpy.o memset.o
ifeq ($(CONFIG_KASAN_GENERIC)$(CONFIG_KASAN_SW_TAGS),)
-purgatory-y += strcmp.o strlen.o strncmp.o
+purgatory-y += strcmp.o strlen.o strncmp.o strnlen.o
endif
targets += $(purgatory-y)
@@ -32,6 +32,9 @@ $(obj)/strncmp.o: $(srctree)/arch/riscv/lib/strncmp.S FORCE
$(obj)/sha256.o: $(srctree)/lib/crypto/sha256.c FORCE
$(call if_changed_rule,cc_o_c)
+$(obj)/strnlen.o: $(srctree)/arch/riscv/lib/strnlen.S FORCE
+ $(call if_changed_rule,as_o_S)
+
CFLAGS_sha256.o := -D__DISABLE_EXPORTS -D__NO_FORTIFY
CFLAGS_string.o := -D__DISABLE_EXPORTS
CFLAGS_ctype.o := -D__DISABLE_EXPORTS
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 4/5] riscv: lib: add strchr implementation
2026-01-07 2:35 [PATCH 0/5] riscv: optimize string functions and add kunit tests Feng Jiang
` (2 preceding siblings ...)
2026-01-07 2:35 ` [PATCH 3/5] riscv: lib: add strnlen implementation Feng Jiang
@ 2026-01-07 2:35 ` Feng Jiang
2026-01-07 2:35 ` [PATCH 5/5] riscv: lib: add strrchr implementation Feng Jiang
4 siblings, 0 replies; 12+ messages in thread
From: Feng Jiang @ 2026-01-07 2:35 UTC (permalink / raw)
To: pjw, palmer, aou, alex, kees, andy, jiangfeng, ebiggers,
martin.petersen, Jason, conor.dooley, samuel.holland, charlie,
ajones, nathan, linus.walleij
Cc: linux-riscv, linux-kernel, linux-hardening
Add a basic assembly implementation of strchr() for RISC-V.
This provides a functional byte-by-byte version as a foundation
for future optimizations.
Signed-off-by: Feng Jiang <jiangfeng@kylinos.cn>
---
arch/riscv/include/asm/string.h | 3 +++
arch/riscv/lib/Makefile | 1 +
arch/riscv/lib/strchr.S | 35 +++++++++++++++++++++++++++++++++
arch/riscv/purgatory/Makefile | 5 ++++-
4 files changed, 43 insertions(+), 1 deletion(-)
create mode 100644 arch/riscv/lib/strchr.S
diff --git a/arch/riscv/include/asm/string.h b/arch/riscv/include/asm/string.h
index 16634d67c217..ca3ade82b124 100644
--- a/arch/riscv/include/asm/string.h
+++ b/arch/riscv/include/asm/string.h
@@ -31,6 +31,9 @@ extern asmlinkage int strncmp(const char *cs, const char *ct, size_t count);
#define __HAVE_ARCH_STRNLEN
extern asmlinkage __kernel_size_t strnlen(const char *, size_t);
+
+#define __HAVE_ARCH_STRCHR
+extern asmlinkage char *strchr(const char *, int);
#endif
/* For those files which don't want to check by kasan. */
diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
index 0969d8136df0..b7f804dce1c3 100644
--- a/arch/riscv/lib/Makefile
+++ b/arch/riscv/lib/Makefile
@@ -8,6 +8,7 @@ lib-y += strcmp.o
lib-y += strlen.o
lib-y += strncmp.o
lib-y += strnlen.o
+lib-y += strchr.o
endif
lib-y += csum.o
ifeq ($(CONFIG_MMU), y)
diff --git a/arch/riscv/lib/strchr.S b/arch/riscv/lib/strchr.S
new file mode 100644
index 000000000000..48c3a9da53e3
--- /dev/null
+++ b/arch/riscv/lib/strchr.S
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ * Copyright (C) 2025 Feng Jiang <jiangfeng@kylinos.cn>
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm.h>
+
+/* char *strchr(const char *s, int c) */
+SYM_FUNC_START(strchr)
+ /*
+ * Parameters
+ * a0 - The string to be searched
+ * a1 - The character to search for
+ *
+ * Returns
+ * a0 - Address of first occurrence of 'c' or 0
+ *
+ * Clobbers
+ * t0
+ */
+ andi a1, a1, 0xff
+1:
+ lbu t0, 0(a0)
+ beq t0, a1, 2f
+ addi a0, a0, 1
+ bnez t0, 1b
+ li a0, 0
+2:
+ ret
+SYM_FUNC_END(strchr)
+
+SYM_FUNC_ALIAS_WEAK(__pi_strchr, strchr)
+EXPORT_SYMBOL(strchr)
diff --git a/arch/riscv/purgatory/Makefile b/arch/riscv/purgatory/Makefile
index d7c0533108be..e7b3d748c913 100644
--- a/arch/riscv/purgatory/Makefile
+++ b/arch/riscv/purgatory/Makefile
@@ -2,7 +2,7 @@
purgatory-y := purgatory.o sha256.o entry.o string.o ctype.o memcpy.o memset.o
ifeq ($(CONFIG_KASAN_GENERIC)$(CONFIG_KASAN_SW_TAGS),)
-purgatory-y += strcmp.o strlen.o strncmp.o strnlen.o
+purgatory-y += strcmp.o strlen.o strncmp.o strnlen.o strchr.o
endif
targets += $(purgatory-y)
@@ -35,6 +35,9 @@ $(obj)/sha256.o: $(srctree)/lib/crypto/sha256.c FORCE
$(obj)/strnlen.o: $(srctree)/arch/riscv/lib/strnlen.S FORCE
$(call if_changed_rule,as_o_S)
+$(obj)/strchr.o: $(srctree)/arch/riscv/lib/strchr.S FORCE
+ $(call if_changed_rule,as_o_S)
+
CFLAGS_sha256.o := -D__DISABLE_EXPORTS -D__NO_FORTIFY
CFLAGS_string.o := -D__DISABLE_EXPORTS
CFLAGS_ctype.o := -D__DISABLE_EXPORTS
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 5/5] riscv: lib: add strrchr implementation
2026-01-07 2:35 [PATCH 0/5] riscv: optimize string functions and add kunit tests Feng Jiang
` (3 preceding siblings ...)
2026-01-07 2:35 ` [PATCH 4/5] riscv: lib: add strchr implementation Feng Jiang
@ 2026-01-07 2:35 ` Feng Jiang
4 siblings, 0 replies; 12+ messages in thread
From: Feng Jiang @ 2026-01-07 2:35 UTC (permalink / raw)
To: pjw, palmer, aou, alex, kees, andy, jiangfeng, ebiggers,
martin.petersen, Jason, conor.dooley, samuel.holland, charlie,
ajones, nathan, linus.walleij
Cc: linux-riscv, linux-kernel, linux-hardening
Add a basic assembly implementation of strrchr() for RISC-V.
This provides a functional byte-by-byte version as a foundation
for future optimizations.
Signed-off-by: Feng Jiang <jiangfeng@kylinos.cn>
---
arch/riscv/include/asm/string.h | 3 +++
arch/riscv/lib/Makefile | 1 +
arch/riscv/lib/strrchr.S | 37 +++++++++++++++++++++++++++++++++
arch/riscv/purgatory/Makefile | 5 ++++-
4 files changed, 45 insertions(+), 1 deletion(-)
create mode 100644 arch/riscv/lib/strrchr.S
diff --git a/arch/riscv/include/asm/string.h b/arch/riscv/include/asm/string.h
index ca3ade82b124..764ffe8f6479 100644
--- a/arch/riscv/include/asm/string.h
+++ b/arch/riscv/include/asm/string.h
@@ -34,6 +34,9 @@ extern asmlinkage __kernel_size_t strnlen(const char *, size_t);
#define __HAVE_ARCH_STRCHR
extern asmlinkage char *strchr(const char *, int);
+
+#define __HAVE_ARCH_STRRCHR
+extern asmlinkage char *strrchr(const char *, int);
#endif
/* For those files which don't want to check by kasan. */
diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
index b7f804dce1c3..735d0b665536 100644
--- a/arch/riscv/lib/Makefile
+++ b/arch/riscv/lib/Makefile
@@ -9,6 +9,7 @@ lib-y += strlen.o
lib-y += strncmp.o
lib-y += strnlen.o
lib-y += strchr.o
+lib-y += strrchr.o
endif
lib-y += csum.o
ifeq ($(CONFIG_MMU), y)
diff --git a/arch/riscv/lib/strrchr.S b/arch/riscv/lib/strrchr.S
new file mode 100644
index 000000000000..ac58b20ca21d
--- /dev/null
+++ b/arch/riscv/lib/strrchr.S
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ * Copyright (C) 2025 Feng Jiang <jiangfeng@kylinos.cn>
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm.h>
+
+/* char *strrchr(const char *s, int c) */
+SYM_FUNC_START(strrchr)
+ /*
+ * Parameters
+ * a0 - The string to be searched
+ * a1 - The character to seaerch for
+ *
+ * Returns
+ * a0 - Address of last occurrence of 'c' or 0
+ *
+ * Clobbers
+ * t0, t1
+ */
+ andi a1, a1, 0xff
+ mv t1, a0
+ li a0, 0
+1:
+ lbu t0, 0(t1)
+ bne t0, a1, 2f
+ mv a0, t1
+2:
+ addi t1, t1, 1
+ bnez t0, 1b
+ ret
+SYM_FUNC_END(strrchr)
+
+SYM_FUNC_ALIAS_WEAK(__pi_strrchr, strrchr)
+EXPORT_SYMBOL(strrchr)
diff --git a/arch/riscv/purgatory/Makefile b/arch/riscv/purgatory/Makefile
index e7b3d748c913..b0358a78f11a 100644
--- a/arch/riscv/purgatory/Makefile
+++ b/arch/riscv/purgatory/Makefile
@@ -2,7 +2,7 @@
purgatory-y := purgatory.o sha256.o entry.o string.o ctype.o memcpy.o memset.o
ifeq ($(CONFIG_KASAN_GENERIC)$(CONFIG_KASAN_SW_TAGS),)
-purgatory-y += strcmp.o strlen.o strncmp.o strnlen.o strchr.o
+purgatory-y += strcmp.o strlen.o strncmp.o strnlen.o strchr.o strrchr.o
endif
targets += $(purgatory-y)
@@ -38,6 +38,9 @@ $(obj)/strnlen.o: $(srctree)/arch/riscv/lib/strnlen.S FORCE
$(obj)/strchr.o: $(srctree)/arch/riscv/lib/strchr.S FORCE
$(call if_changed_rule,as_o_S)
+$(obj)/strrchr.o: $(srctree)/arch/riscv/lib/strrchr.S FORCE
+ $(call if_changed_rule,as_o_S)
+
CFLAGS_sha256.o := -D__DISABLE_EXPORTS -D__NO_FORTIFY
CFLAGS_string.o := -D__DISABLE_EXPORTS
CFLAGS_ctype.o := -D__DISABLE_EXPORTS
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 1/5] lib/string_kunit: add test case for strlen
2026-01-07 2:35 ` [PATCH 1/5] lib/string_kunit: add test case for strlen Feng Jiang
@ 2026-01-07 11:48 ` Andy Shevchenko
0 siblings, 0 replies; 12+ messages in thread
From: Andy Shevchenko @ 2026-01-07 11:48 UTC (permalink / raw)
To: Feng Jiang
Cc: pjw, palmer, aou, alex, kees, andy, ebiggers, martin.petersen,
Jason, conor.dooley, samuel.holland, charlie, ajones, nathan,
linus.walleij, linux-riscv, linux-kernel, linux-hardening
On Wed, Jan 7, 2026 at 4:35 AM Feng Jiang <jiangfeng@kylinos.cn> wrote:
>
> Add a KUnit test for strlen() to verify correctness across
> different string lengths and memory alignments.
I assume you also wanted the optimisation tests, i.e. something that
does random filling and measure two algorithms by execution time.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/5] lib/string_kunit: add test case for strnlen
2026-01-07 2:35 ` [PATCH 2/5] lib/string_kunit: add test case for strnlen Feng Jiang
@ 2026-01-07 11:56 ` Andy Shevchenko
2026-01-08 6:53 ` Feng Jiang
0 siblings, 1 reply; 12+ messages in thread
From: Andy Shevchenko @ 2026-01-07 11:56 UTC (permalink / raw)
To: Feng Jiang
Cc: pjw, palmer, aou, alex, kees, andy, ebiggers, martin.petersen,
Jason, conor.dooley, samuel.holland, charlie, ajones, nathan,
linus.walleij, linux-riscv, linux-kernel, linux-hardening
On Wed, Jan 7, 2026 at 4:35 AM Feng Jiang <jiangfeng@kylinos.cn> wrote:
>
> Add a KUnit test for strnlen() to verify correctness across
> different string lengths and memory alignments.
Same comment as per patch 1 (it would probably require to call for
arch_strnlen() or something like this).
...
> + for (offset = 0; offset < 16; offset++) {
> + for (len = 0; len <= 128; len++) {
You want to define these two limits to avoid the possible issues in
the future if the code gets changed.
> + }
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/5] lib/string_kunit: add test case for strnlen
2026-01-07 11:56 ` Andy Shevchenko
@ 2026-01-08 6:53 ` Feng Jiang
2026-01-08 7:26 ` Andy Shevchenko
0 siblings, 1 reply; 12+ messages in thread
From: Feng Jiang @ 2026-01-08 6:53 UTC (permalink / raw)
To: Andy Shevchenko
Cc: pjw, palmer, aou, alex, kees, andy, ebiggers, martin.petersen,
Jason, conor.dooley, samuel.holland, charlie, ajones, nathan,
linus.walleij, linux-riscv, linux-kernel, linux-hardening
On 2026/1/7 19:56, Andy Shevchenko wrote:
> On Wed, Jan 7, 2026 at 4:35 AM Feng Jiang <jiangfeng@kylinos.cn> wrote:
>>
>> Add a KUnit test for strnlen() to verify correctness across
>> different string lengths and memory alignments.
>
> Same comment as per patch 1 (it would probably require to call for
> arch_strnlen() or something like this).
Thanks, makes sense.
I'll add the performance benchmarking (random filling + timing) in V2.
Since string functions are typically exported directly by each architecture
without an arch_ prefix, I'll introduce a generic_strnlen() (based on
lib/string.c) within the test for comparison.
> ...
>
>> + for (offset = 0; offset < 16; offset++) {
>> + for (len = 0; len <= 128; len++) {
>
> You want to define these two limits to avoid the possible issues in
> the future if the code gets changed.
Got it, will define these limits as macros in V2. Thanks!
--
With Best Regards,
Feng Jiang
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/5] lib/string_kunit: add test case for strnlen
2026-01-08 6:53 ` Feng Jiang
@ 2026-01-08 7:26 ` Andy Shevchenko
2026-01-08 10:00 ` Feng Jiang
0 siblings, 1 reply; 12+ messages in thread
From: Andy Shevchenko @ 2026-01-08 7:26 UTC (permalink / raw)
To: Feng Jiang
Cc: Andy Shevchenko, pjw, palmer, aou, alex, kees, andy, ebiggers,
martin.petersen, Jason, conor.dooley, samuel.holland, charlie,
ajones, nathan, linus.walleij, linux-riscv, linux-kernel,
linux-hardening
On Thu, Jan 08, 2026 at 02:53:58PM +0800, Feng Jiang wrote:
> On 2026/1/7 19:56, Andy Shevchenko wrote:
> > On Wed, Jan 7, 2026 at 4:35 AM Feng Jiang <jiangfeng@kylinos.cn> wrote:
...
> >> Add a KUnit test for strnlen() to verify correctness across
> >> different string lengths and memory alignments.
> >
> > Same comment as per patch 1 (it would probably require to call for
> > arch_strnlen() or something like this).
>
> Thanks, makes sense.
>
> I'll add the performance benchmarking (random filling + timing) in V2.
>
> Since string functions are typically exported directly by each architecture
> without an arch_ prefix, I'll introduce a generic_strnlen() (based on
> lib/string.c) within the test for comparison.
Probably you want to make the existing one to have that name and use it inside
the test and in the fallback wrapper. We don't want to have duplicate code, it
is bad from maintenance perspective.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/5] lib/string_kunit: add test case for strnlen
2026-01-08 7:26 ` Andy Shevchenko
@ 2026-01-08 10:00 ` Feng Jiang
2026-01-08 10:42 ` Andy Shevchenko
0 siblings, 1 reply; 12+ messages in thread
From: Feng Jiang @ 2026-01-08 10:00 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Andy Shevchenko, pjw, palmer, aou, alex, kees, andy, ebiggers,
martin.petersen, Jason, conor.dooley, samuel.holland, charlie,
ajones, nathan, linus.walleij, linux-riscv, linux-kernel,
linux-hardening
On 2026/1/8 15:26, Andy Shevchenko wrote:
> On Thu, Jan 08, 2026 at 02:53:58PM +0800, Feng Jiang wrote:
>> On 2026/1/7 19:56, Andy Shevchenko wrote:
>>> On Wed, Jan 7, 2026 at 4:35 AM Feng Jiang <jiangfeng@kylinos.cn> wrote:
>
> ...
>
>>>> Add a KUnit test for strnlen() to verify correctness across
>>>> different string lengths and memory alignments.
>>>
>>> Same comment as per patch 1 (it would probably require to call for
>>> arch_strnlen() or something like this).
>>
>> Thanks, makes sense.
>>
>> I'll add the performance benchmarking (random filling + timing) in V2.
>>
>> Since string functions are typically exported directly by each architecture
>> without an arch_ prefix, I'll introduce a generic_strnlen() (based on
>> lib/string.c) within the test for comparison.
>
> Probably you want to make the existing one to have that name and use it inside
> the test and in the fallback wrapper. We don't want to have duplicate code, it
> is bad from maintenance perspective.
>
Thanks for the suggestions!
To avoid code duplication, I'll rename the generic implementation in lib/string.c
to __generic_strnlen() and keep the original strnlen() as a wrapper. Then I'll use
the generic one in the KUnit test for comparison.
Does this approach look good to you?
--
With Best Regards,
Feng Jiang
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 2/5] lib/string_kunit: add test case for strnlen
2026-01-08 10:00 ` Feng Jiang
@ 2026-01-08 10:42 ` Andy Shevchenko
0 siblings, 0 replies; 12+ messages in thread
From: Andy Shevchenko @ 2026-01-08 10:42 UTC (permalink / raw)
To: Feng Jiang
Cc: Andy Shevchenko, pjw, palmer, aou, alex, kees, andy, ebiggers,
martin.petersen, Jason, conor.dooley, samuel.holland, charlie,
ajones, nathan, linus.walleij, linux-riscv, linux-kernel,
linux-hardening
On Thu, Jan 08, 2026 at 06:00:21PM +0800, Feng Jiang wrote:
> On 2026/1/8 15:26, Andy Shevchenko wrote:
> > On Thu, Jan 08, 2026 at 02:53:58PM +0800, Feng Jiang wrote:
> >> On 2026/1/7 19:56, Andy Shevchenko wrote:
> >>> On Wed, Jan 7, 2026 at 4:35 AM Feng Jiang <jiangfeng@kylinos.cn> wrote:
...
> >>>> Add a KUnit test for strnlen() to verify correctness across
> >>>> different string lengths and memory alignments.
> >>>
> >>> Same comment as per patch 1 (it would probably require to call for
> >>> arch_strnlen() or something like this).
> >>
> >> Thanks, makes sense.
> >>
> >> I'll add the performance benchmarking (random filling + timing) in V2.
> >>
> >> Since string functions are typically exported directly by each architecture
> >> without an arch_ prefix, I'll introduce a generic_strnlen() (based on
> >> lib/string.c) within the test for comparison.
> >
> > Probably you want to make the existing one to have that name and use it inside
> > the test and in the fallback wrapper. We don't want to have duplicate code, it
> > is bad from maintenance perspective.
>
> Thanks for the suggestions!
>
> To avoid code duplication, I'll rename the generic implementation in lib/string.c
> to __generic_strnlen() and keep the original strnlen() as a wrapper. Then I'll use
> the generic one in the KUnit test for comparison.
>
> Does this approach look good to you?
To me, yes. To others, we will know later on when you send a new version.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-01-08 10:42 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-07 2:35 [PATCH 0/5] riscv: optimize string functions and add kunit tests Feng Jiang
2026-01-07 2:35 ` [PATCH 1/5] lib/string_kunit: add test case for strlen Feng Jiang
2026-01-07 11:48 ` Andy Shevchenko
2026-01-07 2:35 ` [PATCH 2/5] lib/string_kunit: add test case for strnlen Feng Jiang
2026-01-07 11:56 ` Andy Shevchenko
2026-01-08 6:53 ` Feng Jiang
2026-01-08 7:26 ` Andy Shevchenko
2026-01-08 10:00 ` Feng Jiang
2026-01-08 10:42 ` Andy Shevchenko
2026-01-07 2:35 ` [PATCH 3/5] riscv: lib: add strnlen implementation Feng Jiang
2026-01-07 2:35 ` [PATCH 4/5] riscv: lib: add strchr implementation Feng Jiang
2026-01-07 2:35 ` [PATCH 5/5] riscv: lib: add strrchr implementation Feng Jiang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox