Linux Kernel Selftest development
 help / color / mirror / Atom feed
From: Leon Hwang <leon.hwang@linux.dev>
To: bpf@vger.kernel.org
Cc: Alexei Starovoitov <ast@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>,
	Andrii Nakryiko <andrii@kernel.org>,
	Eduard Zingerman <eddyz87@gmail.com>,
	Kumar Kartikeya Dwivedi <memxor@gmail.com>,
	Martin KaFai Lau <martin.lau@linux.dev>,
	Song Liu <song@kernel.org>,
	Yonghong Song <yonghong.song@linux.dev>,
	Jiri Olsa <jolsa@kernel.org>,
	Emil Tsalapatis <emil@etsalapatis.com>,
	Ihor Solodrai <ihor.solodrai@linux.dev>,
	Shuah Khan <shuah@kernel.org>, Leon Hwang <leon.hwang@linux.dev>,
	Rong Tao <rongtao@cestc.cn>,
	Yuzuki Ishiyama <ishiyama@hpc.is.uec.ac.jp>,
	Viktor Malik <vmalik@redhat.com>,
	linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org
Subject: [RFC PATCH bpf-next 2/6] bpf: Optimize string comparison kfuncs
Date: Tue, 28 Jul 2026 22:57:23 +0800	[thread overview]
Message-ID: <20260728145727.45153-3-leon.hwang@linux.dev> (raw)
In-Reply-To: <20260728145727.45153-1-leon.hwang@linux.dev>

Use aligned nofault word loads for the strcmp and strcasecmp kfunc
families when both operands have the same relative alignment. Skip
identical NUL-free words directly and inspect locally loaded bytes only
for mismatches and case folding.

Reuse the string scan expansion macro for alignment, word bounds, and
byte fallback while keeping the comparison actions expanded at the call
site.

Keep byte-at-a-time access for differently aligned operands, KMSAN, and
word-load faults. This preserves comparison order and the existing error
semantics.

Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
 kernel/bpf/helpers.c | 72 +++++++++++++++++++++++++++++++++++---------
 1 file changed, 57 insertions(+), 15 deletions(-)

diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 36075c683166..406aca61789a 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -3818,32 +3818,74 @@ static __always_inline int bpf_str_find(const char *s, size_t limit, unsigned ch
 	return -EFAULT;
 }
 
+static __always_inline bool
+bpf_str_cmp_byte(unsigned char byte1, unsigned char byte2, bool ignore_case, int *ret)
+{
+	char c1 = byte1, c2 = byte2;
+
+	if (ignore_case) {
+		c1 = tolower(c1);
+		c2 = tolower(c2);
+	}
+	if (c1 != c2) {
+		*ret = c1 < c2 ? -1 : 1;
+		return true;
+	}
+	if (c1 == '\0') {
+		*ret = 0;
+		return true;
+	}
+	return false;
+}
+
 static int __bpf_strncasecmp(const char *s1, const char *s2, bool ignore_case, size_t len)
 {
-	char c1, c2;
-	int i;
+	const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
+	unsigned char byte1, byte2, *bytes1, *bytes2;
+	unsigned long word1, word2, data;
+	size_t limit, pos = 0, i;
+	int ret;
 
 	if (!copy_from_kernel_nofault_allowed(s1, 1) ||
 	    !copy_from_kernel_nofault_allowed(s2, 1)) {
 		return -ERANGE;
 	}
 
+	limit = min_t(size_t, len, XATTR_SIZE_MAX);
 	guard(pagefault)();
-	for (i = 0; i < len && i < XATTR_SIZE_MAX; i++) {
-		__get_kernel_nofault(&c1, s1, char, err_out);
-		__get_kernel_nofault(&c2, s2, char, err_out);
-		if (ignore_case) {
-			c1 = tolower(c1);
-			c2 = tolower(c2);
+
+	if (!IS_ALIGNED((unsigned long)s1 ^ (unsigned long)s2, sizeof(word1))) {
+		for (; pos < limit; pos++) {
+			__get_kernel_nofault(&byte1, s1 + pos, unsigned char, err_out);
+			__get_kernel_nofault(&byte2, s2 + pos, unsigned char, err_out);
+			if (bpf_str_cmp_byte(byte1, byte2, ignore_case, &ret))
+				return ret;
 		}
-		if (c1 != c2)
-			return c1 < c2 ? -1 : 1;
-		if (c1 == '\0')
-			return 0;
-		s1++;
-		s2++;
+		return pos == XATTR_SIZE_MAX ? -E2BIG : 0;
 	}
-	return i == XATTR_SIZE_MAX ? -E2BIG : 0;
+
+	bpf_str_for_each_word(s1, limit, pos, word1, byte1, byte_at_a_time, ({
+		__get_kernel_nofault(&byte2, s2 + pos, unsigned char, err_out);
+		if (bpf_str_cmp_byte(byte1, byte2, ignore_case, &ret))
+			return ret;
+	}), ({
+		__get_kernel_nofault(&word2, s2 + pos, unsigned long, byte_at_a_time);
+		if (word1 == word2) {
+			if (has_zero(word1, &data, &constants))
+				return 0;
+			continue;
+		}
+
+		bytes1 = (unsigned char *)&word1;
+		bytes2 = (unsigned char *)&word2;
+		for (i = 0; i < sizeof(word1); i++) {
+			if (bpf_str_cmp_byte(bytes1[i], bytes2[i], ignore_case, &ret))
+				return ret;
+		}
+	}), err_out);
+
+	return pos == XATTR_SIZE_MAX ? -E2BIG : 0;
+
 err_out:
 	return -EFAULT;
 }
-- 
2.55.0


  parent reply	other threads:[~2026-07-28 14:58 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-28 14:57 [RFC PATCH bpf-next 0/6] bpf: Optimize string kfuncs Leon Hwang
2026-07-28 14:57 ` [RFC PATCH bpf-next 1/6] bpf: Optimize string scan kfuncs Leon Hwang
2026-07-28 14:57 ` Leon Hwang [this message]
2026-07-28 14:57 ` [RFC PATCH bpf-next 3/6] bpf: Optimize string span kfuncs Leon Hwang
2026-07-28 14:57 ` [RFC PATCH bpf-next 4/6] bpf: Optimize string substring kfuncs Leon Hwang
2026-07-28 14:57 ` [RFC PATCH bpf-next 5/6] selftests/bpf: Exercise word-at-a-time string kfuncs Leon Hwang
2026-07-28 14:57 ` [RFC PATCH bpf-next 6/6] selftests/bpf: Benchmark " Leon Hwang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260728145727.45153-3-leon.hwang@linux.dev \
    --to=leon.hwang@linux.dev \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=emil@etsalapatis.com \
    --cc=ihor.solodrai@linux.dev \
    --cc=ishiyama@hpc.is.uec.ac.jp \
    --cc=jolsa@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=martin.lau@linux.dev \
    --cc=memxor@gmail.com \
    --cc=rongtao@cestc.cn \
    --cc=shuah@kernel.org \
    --cc=song@kernel.org \
    --cc=vmalik@redhat.com \
    --cc=yonghong.song@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox