From 9794de4500e7dc8686f4ae5f2d11d76e43f299c5 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Wed, 22 Apr 2026 22:17:44 +0300 Subject: [PATCH 001/108] proc: add tgid_iter.pid_ns member next_tgid() accepts pid namespace as an argument, but it never changes during readdir (which would be unthinkable thing to do anyway). Move it inside iterator type and hide from direct usage. Link: https://lore.kernel.org/20260422191745.435556-1-adobriyan@gmail.com Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton --- fs/proc/base.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index d9acfa89c894..f2db455dbbfd 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3543,8 +3543,10 @@ struct dentry *proc_pid_lookup(struct dentry *dentry, unsigned int flags) struct tgid_iter { unsigned int tgid; struct task_struct *task; + struct pid_namespace *pid_ns; }; -static struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter iter) + +static struct tgid_iter next_tgid(struct tgid_iter iter) { struct pid *pid; @@ -3553,9 +3555,9 @@ static struct tgid_iter next_tgid(struct pid_namespace *ns, struct tgid_iter ite rcu_read_lock(); retry: iter.task = NULL; - pid = find_ge_pid(iter.tgid, ns); + pid = find_ge_pid(iter.tgid, iter.pid_ns); if (pid) { - iter.tgid = pid_nr_ns(pid, ns); + iter.tgid = pid_nr_ns(pid, iter.pid_ns); iter.task = pid_task(pid, PIDTYPE_TGID); if (!iter.task) { iter.tgid += 1; @@ -3574,7 +3576,7 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx) { struct tgid_iter iter; struct proc_fs_info *fs_info = proc_sb_info(file_inode(file)->i_sb); - struct pid_namespace *ns = proc_pid_ns(file_inode(file)->i_sb); + struct pid_namespace *pid_ns = proc_pid_ns(file_inode(file)->i_sb); loff_t pos = ctx->pos; if (pos >= PID_MAX_LIMIT + TGID_OFFSET) @@ -3592,9 +3594,10 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx) } iter.tgid = pos - TGID_OFFSET; iter.task = NULL; - for (iter = next_tgid(ns, iter); + iter.pid_ns = pid_ns; + for (iter = next_tgid(iter); iter.task; - iter.tgid += 1, iter = next_tgid(ns, iter)) { + iter.tgid += 1, iter = next_tgid(iter)) { char name[10 + 1]; unsigned int len; From f7027ed76dfcf4905225272029a83cc74fd1816d Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Wed, 22 Apr 2026 22:17:45 +0300 Subject: [PATCH 002/108] proc: rewrite next_tgid() * deduplicate "iter.tgid += 1" line, Right now it is done once inside next_tgid() itself and second time inside "for" loop. * deduplicate next_tgid() call itself with different loop style: auto it = make_tgid_iter(); while (next_tgid(&it)) { ... } gcc seems to inline it twice: $ ./scripts/bloat-o-meter ../vmlinux-000 ../obj/vmlinux add/remove: 0/1 grow/shrink: 1/0 up/down: 100/-245 (-145) Function old new delta proc_pid_readdir 531 631 +100 next_tgid 245 - -245 * make tgid_iter.pid_ns const it never changes during readdir anyway [akpm@linux-foundation.org: remove newline] Link: https://lore.kernel.org/20260422191745.435556-2-adobriyan@gmail.com Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton --- fs/proc/base.c | 60 ++++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index f2db455dbbfd..49344de10158 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3543,30 +3543,42 @@ struct dentry *proc_pid_lookup(struct dentry *dentry, unsigned int flags) struct tgid_iter { unsigned int tgid; struct task_struct *task; - struct pid_namespace *pid_ns; + struct pid_namespace *const pid_ns; }; -static struct tgid_iter next_tgid(struct tgid_iter iter) +static struct tgid_iter +make_tgid_iter(unsigned int init_tgid, struct pid_namespace *pid_ns) { - struct pid *pid; + return (struct tgid_iter){ + .tgid = init_tgid - 1, + .pid_ns = pid_ns, + }; +} - if (iter.task) - put_task_struct(iter.task); - rcu_read_lock(); -retry: - iter.task = NULL; - pid = find_ge_pid(iter.tgid, iter.pid_ns); - if (pid) { - iter.tgid = pid_nr_ns(pid, iter.pid_ns); - iter.task = pid_task(pid, PIDTYPE_TGID); - if (!iter.task) { - iter.tgid += 1; - goto retry; - } - get_task_struct(iter.task); +static bool next_tgid(struct tgid_iter *it) +{ + if (it->task) { + put_task_struct(it->task); + it->task = NULL; + } + + rcu_read_lock(); + while (1) { + it->tgid += 1; + const auto pid = find_ge_pid(it->tgid, it->pid_ns); + if (pid) { + it->tgid = pid_nr_ns(pid, it->pid_ns); + it->task = pid_task(pid, PIDTYPE_TGID); + if (it->task) { + get_task_struct(it->task); + rcu_read_unlock(); + return true; + } + } else { + rcu_read_unlock(); + return false; + } } - rcu_read_unlock(); - return iter; } #define TGID_OFFSET (FIRST_PROCESS_ENTRY + 2) @@ -3574,7 +3586,6 @@ static struct tgid_iter next_tgid(struct tgid_iter iter) /* for the /proc/ directory itself, after non-process stuff has been done */ int proc_pid_readdir(struct file *file, struct dir_context *ctx) { - struct tgid_iter iter; struct proc_fs_info *fs_info = proc_sb_info(file_inode(file)->i_sb); struct pid_namespace *pid_ns = proc_pid_ns(file_inode(file)->i_sb); loff_t pos = ctx->pos; @@ -3592,12 +3603,9 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx) return 0; ctx->pos = pos = pos + 1; } - iter.tgid = pos - TGID_OFFSET; - iter.task = NULL; - iter.pid_ns = pid_ns; - for (iter = next_tgid(iter); - iter.task; - iter.tgid += 1, iter = next_tgid(iter)) { + + auto iter = make_tgid_iter(pos - TGID_OFFSET, pid_ns); + while (next_tgid(&iter)) { char name[10 + 1]; unsigned int len; From 2ddfdfe1ad31ba5be5a1c3b4b2b950cf6b6d7c35 Mon Sep 17 00:00:00 2001 From: Petr Vorel Date: Tue, 21 Apr 2026 23:14:06 +0200 Subject: [PATCH 003/108] checkpatch: allow passing config directory checkpatch.pl searches for .checkpatch.conf in $CWD, $HOME and $CWD/.scripts. Allow passing a single directory via CHECKPATCH_CONFIG_DIR environment variable (empty value is ignored). This allows to directly use project configuration file for projects which vendored checkpatch.pl (e.g. LTP or u-boot). Although it'd be more convenient for user to have --conf-dir option (instead of using environment variable), code would get ugly because options from the configuration file needs to be read before processing command line options with Getopt::Long. While at it, document directories and environment variable in -h help and HTML doc. Link: https://lore.kernel.org/20260421211408.383972-1-pvorel@suse.cz Signed-off-by: Petr Vorel Reviewed-by: Simon Glass Acked-by: Joe Perches Cc: Dwaipayan Ray Cc: Lukas Bulwahn Signed-off-by: Andrew Morton --- Documentation/dev-tools/checkpatch.rst | 7 +++++++ scripts/checkpatch.pl | 20 +++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Documentation/dev-tools/checkpatch.rst b/Documentation/dev-tools/checkpatch.rst index dccede68698c..010dfcd615af 100644 --- a/Documentation/dev-tools/checkpatch.rst +++ b/Documentation/dev-tools/checkpatch.rst @@ -210,6 +210,13 @@ Available options: Display the help text. +Configuration file +================== + +Default configuration options can be stored in ``.checkpatch.conf``, search +path: ``.:$HOME:.scripts`` or in a directory specified by ``$CHECKPATCH_CONFIG_DIR`` +environment variable (falling back to the default search path). + Message Levels ============== diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 0492d6afc9a1..8b44b3a6a4dd 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -57,6 +57,9 @@ my %ignore_type = (); my @ignore = (); my $help = 0; my $configuration_file = ".checkpatch.conf"; +my $def_configuration_dirs_help = '.:$HOME:.scripts'; +(my $def_configuration_dirs = $def_configuration_dirs_help) =~ s/\$(\w+)/$ENV{$1}/g; +my $env_config_dir = 'CHECKPATCH_CONFIG_DIR'; my $max_line_length = 100; my $ignore_perl_version = 0; my $minimum_perl_version = 5.10.0; @@ -146,6 +149,11 @@ Options: -h, --help, --version display this help and exit When FILE is - read standard input. + +CONFIGURATION FILE +Default configuration options can be stored in $configuration_file, +search path: '$def_configuration_dirs_help' or in a directory specified by +\$$env_config_dir environment variable (fallback to the default search path). EOM exit($exitcode); @@ -237,7 +245,7 @@ sub list_types { exit($exitcode); } -my $conf = which_conf($configuration_file); +my $conf = which_conf($configuration_file, $env_config_dir, $def_configuration_dirs); if (-f $conf) { my @conf_args; open(my $conffile, '<', "$conf") @@ -1531,9 +1539,15 @@ sub which { } sub which_conf { - my ($conf) = @_; + my ($conf, $env_key, $paths) = @_; + my $env_dir = $ENV{$env_key}; - foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { + if (defined($env_dir) && $env_dir ne "") { + return "$env_dir/$conf" if (-e "$env_dir/$conf"); + warn "$P: Can't find a readable $conf in '$env_dir', falling back to default search paths\n"; + } + + foreach my $path (split(/:/, $paths)) { if (-e "$path/$conf") { return "$path/$conf"; } From b8979a02f9c56d489e27690981d2aa29a3e79542 Mon Sep 17 00:00:00 2001 From: Petr Vorel Date: Tue, 21 Apr 2026 23:14:07 +0200 Subject: [PATCH 004/108] checkpatch: add option to not force /* */ for SPDX Add option --spdx-cxx-comments to not force C comments (/* */) for SPDX, but allow also C++ comments (//). As documented in aa19a176df95d6, this is required for some old toolchains still have older assembler tools which cannot handle C++ style comments. This avoids forcing this for projects which vendored checkpatch.pl (e.g. LTP or u-boot). Link: https://lore.kernel.org/20260421211408.383972-2-pvorel@suse.cz Signed-off-by: Petr Vorel Reviewed-by: Simon Glass Acked-by: Joe Perches Cc: Dwaipayan Ray Cc: Lukas Bulwahn Signed-off-by: Andrew Morton --- Documentation/dev-tools/checkpatch.rst | 7 +++++++ scripts/checkpatch.pl | 23 ++++++++++++++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Documentation/dev-tools/checkpatch.rst b/Documentation/dev-tools/checkpatch.rst index 010dfcd615af..6139a08c34cd 100644 --- a/Documentation/dev-tools/checkpatch.rst +++ b/Documentation/dev-tools/checkpatch.rst @@ -184,6 +184,13 @@ Available options: Override checking of perl version. Runtime errors may be encountered after enabling this flag if the perl version does not meet the minimum specified. + - --spdx-cxx-comments + + Don't force C comments ``/* */`` for SPDX license (required by old + toolchains), allow also C++ comments ``//``. + + NOTE: it should *not* be used for Linux mainline. + - --codespell Use the codespell dictionary for checking spelling errors. diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 8b44b3a6a4dd..0d18771f1b01 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -62,6 +62,7 @@ my $def_configuration_dirs_help = '.:$HOME:.scripts'; my $env_config_dir = 'CHECKPATCH_CONFIG_DIR'; my $max_line_length = 100; my $ignore_perl_version = 0; +my $spdx_cxx_comments = 0; my $minimum_perl_version = 5.10.0; my $min_conf_desc_length = 4; my $spelling_file = "$D/spelling.txt"; @@ -138,6 +139,10 @@ Options: file. It's your fault if there's no backup or git --ignore-perl-version override checking of perl version. expect runtime errors. + --spdx-cxx-comments don't force C comments (/* */) for SPDX license + (required by old toolchains), allow also C++ + comments (//). + NOTE: it should *not* be used for Linux mainline. --codespell Use the codespell dictionary for spelling/typos (default:$codespellfile) --codespellfile Use this codespell dictionary @@ -347,6 +352,7 @@ GetOptions( 'fix!' => \$fix, 'fix-inplace!' => \$fix_inplace, 'ignore-perl-version!' => \$ignore_perl_version, + 'spdx-cxx-comments!' => \$spdx_cxx_comments, 'debug=s' => \%debug, 'test-only=s' => \$tst_only, 'codespell!' => \$codespell, @@ -3815,26 +3821,33 @@ sub process { $checklicenseline = 2; } elsif ($rawline =~ /^\+/) { my $comment = ""; - if ($realfile =~ /\.(h|s|S)$/) { - $comment = '/*'; - } elsif ($realfile =~ /\.(c|rs|dts|dtsi)$/) { + if ($realfile =~ /\.(c|rs|dts|dtsi)$/) { $comment = '//'; } elsif (($checklicenseline == 2) || $realfile =~ /\.(sh|pl|py|awk|tc|yaml)$/) { $comment = '#'; } elsif ($realfile =~ /\.rst$/) { $comment = '..'; } + my $pattern = qr{\Q$comment\E}; + if ($realfile =~ /\.(h|s|S)$/) { + $comment = '/*'; + $pattern = qr{/\*}; + if ($spdx_cxx_comments) { + $comment = '// or /*'; + $pattern = qr{//|/\*}; + } + } # check SPDX comment style for .[chsS] files if ($realfile =~ /\.[chsS]$/ && $rawline =~ /SPDX-License-Identifier:/ && - $rawline !~ m@^\+\s*\Q$comment\E\s*@) { + $rawline !~ m@^\+\s*$pattern\s*@) { WARN("SPDX_LICENSE_TAG", "Improper SPDX comment style for '$realfile', please use '$comment' instead\n" . $herecurr); } if ($comment !~ /^$/ && - $rawline !~ m@^\+\Q$comment\E SPDX-License-Identifier: @) { + $rawline !~ m@^\+$pattern SPDX-License-Identifier: @) { WARN("SPDX_LICENSE_TAG", "Missing or malformed SPDX-License-Identifier tag in line $checklicenseline\n" . $herecurr); } elsif ($rawline =~ /(SPDX-License-Identifier: .*)/) { From de5d0652d0633daaaa1f8f322721f600e65b1fb1 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 21 Apr 2026 14:26:47 +0200 Subject: [PATCH 005/108] proc: use strnlen() for name validation in __proc_create Replace strlen(fn) with strnlen(fn, NAME_MAX + 1) when validating the final path component in __proc_create(). This preserves the existing name limit while bounding the length scan to one byte past the maximum name length. Handle empty names separately, and treat names longer than NAME_MAX as too long. Link: https://lore.kernel.org/20260421122648.56723-2-thorsten.blum@linux.dev Signed-off-by: Thorsten Blum Reviewed-by: Jan Kara Cc: Alexey Dobriyan Cc: Al Viro Cc: Christian Brauner Cc: Thorsten Blum Cc: wangzijie Signed-off-by: Andrew Morton --- fs/proc/generic.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 8bb81e58c9d8..3063080f3bb2 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -427,9 +427,13 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent, if (xlate_proc_name(name, parent, &fn) != 0) goto out; qstr.name = fn; - qstr.len = strlen(fn); - if (qstr.len == 0 || qstr.len >= 256) { - WARN(1, "name len %u\n", qstr.len); + qstr.len = strnlen(fn, NAME_MAX + 1); + if (qstr.len == 0) { + WARN(1, "empty name\n"); + return NULL; + } + if (qstr.len > NAME_MAX) { + WARN(1, "name too long\n"); return NULL; } if (qstr.len == 1 && fn[0] == '.') { From 0e75190fe40093d85c0c698d9f8dca66df842605 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Thu, 23 Apr 2026 23:11:39 +0800 Subject: [PATCH 006/108] tools/accounting/getdelays: fix -Wformat-truncation warning in format_timespec Reproduce with GCC 13.3.0: $ cd tools/accounting $ make This emits: getdelays.c: In function `format_timespec': getdelays.c:218:67: warning: `:' directive output may be truncated writing 1 byte into a region of size between 0 and 16 [-Wformat-truncation=] 218 | snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02d", | getdelays.c:218:9: note: `snprintf' output between 20 and 72 bytes into a destination of size 32 The problem is that %04d and %02d specify minimum field widths only. GCC cannot prove that formatting tm_year + 1900 and the other struct tm fields will always fit in the fixed 32-byte buffer, so it warns about possible truncation. Fix this by replacing the manual snprintf() formatting with strftime("%Y-%m-%dT%H:%M:%S", ...). That matches the data we already have in struct tm, keeps the intended timestamp format, and avoids the warning when building tools/accounting with GCC. Link: https://lore.kernel.org/87d9723e0b59d816ee2e4bd7cddd58a54c6c9f91.1776956545.git.cyyzero16@gmail.com Signed-off-by: Yiyang Chen Cc: Fan Yu Cc: Wang Yaxin Signed-off-by: Andrew Morton --- tools/accounting/getdelays.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tools/accounting/getdelays.c b/tools/accounting/getdelays.c index 368a622ca027..caa5fe9dd573 100644 --- a/tools/accounting/getdelays.c +++ b/tools/accounting/getdelays.c @@ -241,13 +241,7 @@ static const char *format_timespec(struct __kernel_timespec *ts) if (localtime_r(&time_sec, &tm_info) == NULL) return "N/A"; - snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02d", - tm_info.tm_year + 1900, - tm_info.tm_mon + 1, - tm_info.tm_mday, - tm_info.tm_hour, - tm_info.tm_min, - tm_info.tm_sec); + strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S", &tm_info); return buffer; } From 93c8c6ea90be9e9df8fe14048ad4e3caad0770a6 Mon Sep 17 00:00:00 2001 From: Tristan Madani Date: Sat, 18 Apr 2026 13:10:48 +0000 Subject: [PATCH 007/108] ocfs2: use kzalloc for quota recovery bitmap allocation ocfs2 quota recovery allocates a bitmap buffer with kmalloc and does not fully initialize it. This can lead to use of uninitialized bits during quota recovery from a corrupted filesystem image. Use kzalloc instead to ensure the bitmap is zero-initialized. Link: https://lore.kernel.org/20260418131048.1052507-1-tristmd@gmail.com Reported-by: syzbot+7ea0b96c4ddb49fd1a70@syzkaller.appspotmail.com Signed-off-by: Tristan Madani Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/quota_local.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c index 12cbb4fccda0..f55810c59b1b 100644 --- a/fs/ocfs2/quota_local.c +++ b/fs/ocfs2/quota_local.c @@ -302,7 +302,7 @@ static int ocfs2_add_recovery_chunk(struct super_block *sb, if (!rc) return -ENOMEM; rc->rc_chunk = chunk; - rc->rc_bitmap = kmalloc(sb->s_blocksize, GFP_NOFS); + rc->rc_bitmap = kzalloc(sb->s_blocksize, GFP_NOFS); if (!rc->rc_bitmap) { kfree(rc); return -ENOMEM; From 1877a09b64f89278ce3dc83e0cf6a8b1516660cb Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 18 Apr 2026 10:34:59 -0700 Subject: [PATCH 008/108] checkpatch: add check for function pointer arrays in declarations checkpatch did not allow function pointer arrays when testing declaration blocks. Add it. Link: https://lore.kernel.org/eb62763085eb42193a611bca00a62d6f0ae72e1e.1776530118.git.joe@perches.com Signed-off-by: Joe Perches Cc: Andy Whitcroft Cc: Dan Carpenter Cc: Dwaipayan Ray Cc: Lukas Bulwahn Signed-off-by: Andrew Morton --- scripts/checkpatch.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 0d18771f1b01..3727156e4cca 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -4183,7 +4183,7 @@ sub process { $pl =~ s/\b(?:$Attribute|$Sparse)\b//g; if (($pl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || # function pointer declarations - $pl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || + $pl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident(?:\s*\[\s*(?:$Ident|$Constant)?\s*\])?\s*\)\s*[=,;:\[\(]/ || # foo bar; where foo is some local typedef or #define $pl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || # known declaration macros @@ -4197,7 +4197,7 @@ sub process { # looks like a declaration !($sl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || # function pointer declarations - $sl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || + $sl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident(?:\s*\[\s*(?:$Ident|$Constant)?\s*\])?\s*\)\s*[=,;:\[\(]/ || # foo bar; where foo is some local typedef or #define $sl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || # known declaration macros From 410002f8139cbf902f1567f0a8cbf0c5b6f43da2 Mon Sep 17 00:00:00 2001 From: Adi Nata Date: Sun, 5 Apr 2026 09:19:20 +0800 Subject: [PATCH 009/108] kunit: fat: test cluster and directory i_pos layout helpers Add KUnit coverage for fat_clus_to_blknr() and fat_get_blknr_offset() using stub msdos_sb_info values so cluster-to-sector and i_pos split math stays correct. Link: https://lore.kernel.org/20260405011920.28622-1-adinata.softwareengineer@gmail.com Signed-off-by: Adi Nata Acked-by: OGAWA Hirofumi Cc: Christian Brauner Signed-off-by: Andrew Morton --- fs/fat/fat_test.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/fs/fat/fat_test.c b/fs/fat/fat_test.c index 886bf044a9f1..4eeed9dca549 100644 --- a/fs/fat/fat_test.c +++ b/fs/fat/fat_test.c @@ -20,6 +20,37 @@ static void fat_checksum_test(struct kunit *test) KUNIT_EXPECT_EQ(test, fat_checksum("ABCDEFGHA "), (u8)98); } +static void fat_clus_to_blknr_test(struct kunit *test) +{ + struct msdos_sb_info sbi = { + .sec_per_clus = 4, + .data_start = 100, + }; + + KUNIT_EXPECT_EQ(test, (sector_t)100, + fat_clus_to_blknr(&sbi, FAT_START_ENT)); + KUNIT_EXPECT_EQ(test, (sector_t)112, fat_clus_to_blknr(&sbi, 5)); +} + +static void fat_get_blknr_offset_test(struct kunit *test) +{ + struct msdos_sb_info sbi = { + .dir_per_block = 16, + .dir_per_block_bits = 4, + }; + + sector_t blknr; + int offset; + + fat_get_blknr_offset(&sbi, 0, &blknr, &offset); + KUNIT_EXPECT_EQ(test, (sector_t)0, blknr); + KUNIT_EXPECT_EQ(test, 0, offset); + + fat_get_blknr_offset(&sbi, (10 << 4) | 7, &blknr, &offset); + KUNIT_EXPECT_EQ(test, (sector_t)10, blknr); + KUNIT_EXPECT_EQ(test, 7, offset); +} + struct fat_timestamp_testcase { const char *name; struct timespec64 ts; @@ -341,6 +372,8 @@ static void fat_truncate_atime_test(struct kunit *test) static struct kunit_case fat_test_cases[] = { KUNIT_CASE(fat_checksum_test), + KUNIT_CASE(fat_clus_to_blknr_test), + KUNIT_CASE(fat_get_blknr_offset_test), KUNIT_CASE_PARAM(fat_time_fat2unix_test, fat_time_gen_params), KUNIT_CASE_PARAM(fat_time_unix2fat_test, fat_time_gen_params), KUNIT_CASE_PARAM(fat_time_unix2fat_clamp_test, From 99df2a8eba347e901e5355ee66bdb2ade76ff847 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 13 Apr 2026 11:23:48 -0700 Subject: [PATCH 010/108] clang-format: fix formatting of guard() and scoped_guard() statements Without this patch clang-format formats guard() and scoped_guard() statements as follows: guard(...)(...) { ... } With this patch clang-format formats guard() and scoped_guard() statements as follows: guard(...)(...) { ... } Link: https://lore.kernel.org/20260413182348.1865138-1-bvanassche@acm.org Signed-off-by: Bart Van Assche Cc: Peter Zijlstra Cc: Ingo Molnar Signed-off-by: Andrew Morton --- .clang-format | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.clang-format b/.clang-format index 1cc151e2adcc..6a3de86ab27a 100644 --- a/.clang-format +++ b/.clang-format @@ -481,6 +481,7 @@ ForEachMacros: - 'genradix_for_each' - 'genradix_for_each_from' - 'genradix_for_each_reverse' + - 'guard' - 'hash_for_each' - 'hash_for_each_possible' - 'hash_for_each_possible_rcu' @@ -674,6 +675,7 @@ ForEachMacros: - 'rq_list_for_each' - 'rq_list_for_each_safe' - 'sample_read_group__for_each' + - 'scoped_guard' - 'scsi_for_each_prot_sg' - 'scsi_for_each_sg' - 'sctp_for_each_hentry' From b3e4fbb04220efc3bc022bcf31b5689d39c6b111 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Mon, 13 Apr 2026 23:45:44 +0800 Subject: [PATCH 011/108] taskstats: retain dead thread stats in TGID queries Patch series "taskstats: fix TGID dead-thread stat retention", v3. This series fixes a taskstats TGID aggregation bug where fields added in the TGID query path were not preserved after thread exit, and adds a kselftest covering the regression. The first patch keeps the cached TGID aggregate used for dead threads in step with the fields already accumulated for live threads, and also fixes the final TGID exit notification emitted when group_dead is true. The second patch adds a kselftest that verifies TGID CPU stats do not regress after a worker thread exits and has been reaped. This patch (of 2): fill_stats_for_tgid() builds TGID stats from two sources: the cached aggregate in signal->stats and a scan of the live threads in the group. However, fill_tgid_exit() only accumulates delay accounting into signal->stats. This means that once a thread exits, TGID queries lose the fields that fill_stats_for_tgid() adds for live threads. This gap was introduced incrementally by two earlier changes that extended fill_stats_for_tgid() but did not make the corresponding update to fill_tgid_exit(): - commit 8c733420bdd5 ("taskstats: add e/u/stime for TGID command") added ac_etime, ac_utime, and ac_stime to the TGID query path. - commit b663a79c1915 ("taskstats: add context-switch counters") added nvcsw and nivcsw to the TGID query path. As a result, those fields were accounted for live threads in TGID queries, but were dropped from the cached TGID aggregate after thread exit. The final TGID exit notification emitted when group_dead is true also copies that cached aggregate, so it loses the same fields. Factor the per-task TGID accumulation into tgid_stats_add_task() and use it in both fill_stats_for_tgid() and fill_tgid_exit(). This keeps the cached aggregate used for dead threads aligned with the live-thread accumulation used by TGID queries. Link: https://lore.kernel.org/cover.1776094300.git.cyyzero16@gmail.com Link: https://lore.kernel.org/abd2a15d33343636ab5ba43d540bcfe508bd66c7.1776094300.git.cyyzero16@gmail.com Fixes: 8c733420bdd5 ("taskstats: add e/u/stime for TGID command") Fixes: b663a79c1915 ("taskstats: add context-switch counters") Signed-off-by: Yiyang Chen Acked-by: Balbir Singh Cc: Dr. Thomas Orgis Cc: Oleg Nesterov Cc: Wang Yaxin Cc: Yang Yang Cc: Signed-off-by: Andrew Morton --- kernel/taskstats.c | 62 ++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 73bd6a6a7893..2cd0172d0516 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -210,13 +210,39 @@ static int fill_stats_for_pid(pid_t pid, struct taskstats *stats) return 0; } +static void tgid_stats_add_task(struct taskstats *stats, + struct task_struct *tsk, u64 now_ns) +{ + u64 delta, utime, stime; + + /* + * Each accounting subsystem calls its functions here to + * accumulate its per-task stats for tsk, into the per-tgid structure + * + * per-task-foo(stats, tsk); + */ + delayacct_add_tsk(stats, tsk); + + /* calculate task elapsed time in nsec */ + delta = now_ns - tsk->start_time; + /* Convert to micro seconds */ + do_div(delta, NSEC_PER_USEC); + stats->ac_etime += delta; + + task_cputime(tsk, &utime, &stime); + stats->ac_utime += div_u64(utime, NSEC_PER_USEC); + stats->ac_stime += div_u64(stime, NSEC_PER_USEC); + + stats->nvcsw += tsk->nvcsw; + stats->nivcsw += tsk->nivcsw; +} + static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) { struct task_struct *tsk, *first; unsigned long flags; int rc = -ESRCH; - u64 delta, utime, stime; - u64 start_time; + u64 now_ns; /* * Add additional stats from live tasks except zombie thread group @@ -233,30 +259,12 @@ static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) else memset(stats, 0, sizeof(*stats)); - start_time = ktime_get_ns(); + now_ns = ktime_get_ns(); for_each_thread(first, tsk) { if (tsk->exit_state) continue; - /* - * Accounting subsystem can call its functions here to - * fill in relevant parts of struct taskstsats as follows - * - * per-task-foo(stats, tsk); - */ - delayacct_add_tsk(stats, tsk); - /* calculate task elapsed time in nsec */ - delta = start_time - tsk->start_time; - /* Convert to micro seconds */ - do_div(delta, NSEC_PER_USEC); - stats->ac_etime += delta; - - task_cputime(tsk, &utime, &stime); - stats->ac_utime += div_u64(utime, NSEC_PER_USEC); - stats->ac_stime += div_u64(stime, NSEC_PER_USEC); - - stats->nvcsw += tsk->nvcsw; - stats->nivcsw += tsk->nivcsw; + tgid_stats_add_task(stats, tsk, now_ns); } unlock_task_sighand(first, &flags); @@ -275,18 +283,14 @@ static int fill_stats_for_tgid(pid_t tgid, struct taskstats *stats) static void fill_tgid_exit(struct task_struct *tsk) { unsigned long flags; + u64 now_ns; spin_lock_irqsave(&tsk->sighand->siglock, flags); if (!tsk->signal->stats) goto ret; - /* - * Each accounting subsystem calls its functions here to - * accumalate its per-task stats for tsk, into the per-tgid structure - * - * per-task-foo(tsk->signal->stats, tsk); - */ - delayacct_add_tsk(tsk->signal->stats, tsk); + now_ns = ktime_get_ns(); + tgid_stats_add_task(tsk->signal->stats, tsk, now_ns); ret: spin_unlock_irqrestore(&tsk->sighand->siglock, flags); return; From 20e4b31f8e837d923eee91cd5458f823a11cca9a Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Mon, 13 Apr 2026 23:45:45 +0800 Subject: [PATCH 012/108] selftests/acct: add taskstats TGID retention test Add a kselftest for the taskstats TGID aggregation fix. The test creates a worker thread, snapshots TGID taskstats while the worker is still alive, lets the worker exit, and then verifies that the TGID CPU total does not regress after the thread has been reaped. The pass/fail check intentionally keys off ac_utime + ac_stime only, which is the primary user-visible regression fixed by the taskstats change and is less sensitive to scheduling noise than context-switch counters. Link: https://lore.kernel.org/0d55354911c54cd1b9f10a09f6fd378af85c8d43.1776094300.git.cyyzero16@gmail.com Signed-off-by: Yiyang Chen Acked-by: Balbir Singh Cc: Dr. Thomas Orgis Cc: Oleg Nesterov Cc: Wang Yaxin Cc: Yang Yang Signed-off-by: Andrew Morton --- tools/testing/selftests/acct/.gitignore | 3 +- tools/testing/selftests/acct/Makefile | 7 +- .../acct/taskstats_fill_stats_tgid.c | 375 ++++++++++++++++++ 3 files changed, 382 insertions(+), 3 deletions(-) create mode 100644 tools/testing/selftests/acct/taskstats_fill_stats_tgid.c diff --git a/tools/testing/selftests/acct/.gitignore b/tools/testing/selftests/acct/.gitignore index 7e78aac19038..9e9c61c5bfd6 100644 --- a/tools/testing/selftests/acct/.gitignore +++ b/tools/testing/selftests/acct/.gitignore @@ -1,3 +1,4 @@ acct_syscall +taskstats_fill_stats_tgid config -process_log \ No newline at end of file +process_log diff --git a/tools/testing/selftests/acct/Makefile b/tools/testing/selftests/acct/Makefile index 7e025099cf65..083cab5ddb72 100644 --- a/tools/testing/selftests/acct/Makefile +++ b/tools/testing/selftests/acct/Makefile @@ -1,5 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 TEST_GEN_PROGS := acct_syscall -CFLAGS += -Wall +TEST_GEN_PROGS += taskstats_fill_stats_tgid -include ../lib.mk \ No newline at end of file +CFLAGS += -Wall +LDLIBS += -lpthread + +include ../lib.mk diff --git a/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c b/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c new file mode 100644 index 000000000000..d6cab4ae26f2 --- /dev/null +++ b/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kselftest.h" + +#ifndef NLA_ALIGN +#define NLA_ALIGNTO 4 +#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) +#define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr))) +#endif + +#define BUSY_NS (200ULL * 1000 * 1000) + +struct worker_ctx { + pthread_mutex_t lock; + pthread_cond_t cond; + bool ready; + bool release; +}; + +static unsigned long busy_sink; + +static void *taskstats_nla_data(const struct nlattr *na) +{ + return (void *)((char *)na + NLA_HDRLEN); +} + +static bool taskstats_nla_ok(const struct nlattr *na, int remaining) +{ + return remaining >= (int)sizeof(*na) && + na->nla_len >= sizeof(*na) && + na->nla_len <= remaining; +} + +static struct nlattr *taskstats_nla_next(const struct nlattr *na, int *remaining) +{ + int aligned_len = NLA_ALIGN(na->nla_len); + + *remaining -= aligned_len; + return (struct nlattr *)((char *)na + aligned_len); +} + +static uint64_t timespec_diff_ns(const struct timespec *start, + const struct timespec *end) +{ + return (uint64_t)(end->tv_sec - start->tv_sec) * 1000000000ULL + + (uint64_t)(end->tv_nsec - start->tv_nsec); +} + +static void burn_cpu_for_ns(uint64_t runtime_ns) +{ + struct timespec start, now; + unsigned long acc = 0; + + if (clock_gettime(CLOCK_MONOTONIC, &start)) { + perror("clock_gettime"); + exit(EXIT_FAILURE); + } + + do { + for (int i = 0; i < 100000; i++) + acc += i; + if (clock_gettime(CLOCK_MONOTONIC, &now)) { + perror("clock_gettime"); + exit(EXIT_FAILURE); + } + } while (timespec_diff_ns(&start, &now) < runtime_ns); + + busy_sink = acc; +} + +static int netlink_open(void) +{ + struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + .nl_pid = getpid(), + }; + int fd; + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); + if (fd < 0) + return -errno; + + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + int err = -errno; + + close(fd); + return err; + } + + return fd; +} + +static int send_request(int fd, void *buf, size_t len) +{ + struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + }; + + if (sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) + return -errno; + + return 0; +} + +static int get_family_id(int fd, const char *name) +{ + struct { + struct nlmsghdr nlh; + struct genlmsghdr genl; + char buf[256]; + } req = { 0 }; + char resp[8192]; + struct nlmsghdr *nlh; + struct genlmsghdr *genl; + struct nlattr *na; + int len; + int rem; + int ret; + + req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + req.nlh.nlmsg_type = GENL_ID_CTRL; + req.nlh.nlmsg_flags = NLM_F_REQUEST; + req.nlh.nlmsg_seq = 1; + req.nlh.nlmsg_pid = getpid(); + + req.genl.cmd = CTRL_CMD_GETFAMILY; + req.genl.version = 1; + + na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len)); + na->nla_type = CTRL_ATTR_FAMILY_NAME; + na->nla_len = NLA_HDRLEN + strlen(name) + 1; + memcpy(taskstats_nla_data(na), name, strlen(name) + 1); + req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len); + + ret = send_request(fd, &req, req.nlh.nlmsg_len); + if (ret) + return ret; + + len = recv(fd, resp, sizeof(resp), 0); + if (len < 0) + return -errno; + + for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len); + nlh = NLMSG_NEXT(nlh, len)) { + if (nlh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = NLMSG_DATA(nlh); + + return err->error ? err->error : -ENOENT; + } + + genl = (struct genlmsghdr *)NLMSG_DATA(nlh); + rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN; + na = (struct nlattr *)((char *)genl + GENL_HDRLEN); + while (taskstats_nla_ok(na, rem)) { + if (na->nla_type == CTRL_ATTR_FAMILY_ID) + return *(uint16_t *)taskstats_nla_data(na); + na = taskstats_nla_next(na, &rem); + } + } + + return -ENOENT; +} + +static int get_taskstats(int fd, int family_id, uint16_t attr_type, uint32_t id, + struct taskstats *stats) +{ + struct { + struct nlmsghdr nlh; + struct genlmsghdr genl; + char buf[256]; + } req = { 0 }; + char resp[16384]; + struct nlmsghdr *nlh; + struct genlmsghdr *genl; + struct nlattr *na; + struct nlattr *nested; + int len; + int rem; + int nrem; + int ret; + + memset(stats, 0, sizeof(*stats)); + + req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); + req.nlh.nlmsg_type = family_id; + req.nlh.nlmsg_flags = NLM_F_REQUEST; + req.nlh.nlmsg_seq = 2; + req.nlh.nlmsg_pid = getpid(); + + req.genl.cmd = TASKSTATS_CMD_GET; + req.genl.version = 1; + + na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len)); + na->nla_type = attr_type; + na->nla_len = NLA_HDRLEN + sizeof(id); + memcpy(taskstats_nla_data(na), &id, sizeof(id)); + req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len); + + ret = send_request(fd, &req, req.nlh.nlmsg_len); + if (ret) + return ret; + + len = recv(fd, resp, sizeof(resp), 0); + if (len < 0) + return -errno; + + for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len); + nlh = NLMSG_NEXT(nlh, len)) { + if (nlh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = NLMSG_DATA(nlh); + + return err->error ? err->error : -ENOENT; + } + + genl = (struct genlmsghdr *)NLMSG_DATA(nlh); + rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN; + na = (struct nlattr *)((char *)genl + GENL_HDRLEN); + while (taskstats_nla_ok(na, rem)) { + if (na->nla_type == TASKSTATS_TYPE_AGGR_PID || + na->nla_type == TASKSTATS_TYPE_AGGR_TGID) { + nested = (struct nlattr *)taskstats_nla_data(na); + nrem = na->nla_len - NLA_HDRLEN; + while (taskstats_nla_ok(nested, nrem)) { + if (nested->nla_type == TASKSTATS_TYPE_STATS) { + memcpy(stats, taskstats_nla_data(nested), + sizeof(*stats)); + return 0; + } + nested = taskstats_nla_next(nested, &nrem); + } + } + na = taskstats_nla_next(na, &rem); + } + } + + return -ENOENT; +} + +static uint64_t cpu_total(const struct taskstats *stats) +{ + return (uint64_t)stats->ac_utime + (uint64_t)stats->ac_stime; +} + +static void print_stats(const char *label, const struct taskstats *stats) +{ + ksft_print_msg("%s: cpu_total=%llu nvcsw=%llu nivcsw=%llu\n", + label, (unsigned long long)cpu_total(stats), + (unsigned long long)stats->nvcsw, + (unsigned long long)stats->nivcsw); +} + +static void *worker_thread(void *arg) +{ + struct worker_ctx *ctx = arg; + + burn_cpu_for_ns(BUSY_NS); + + pthread_mutex_lock(&ctx->lock); + ctx->ready = true; + pthread_cond_broadcast(&ctx->cond); + while (!ctx->release) + pthread_cond_wait(&ctx->cond, &ctx->lock); + pthread_mutex_unlock(&ctx->lock); + + return NULL; +} + +int main(void) +{ + struct worker_ctx ctx = { + .lock = PTHREAD_MUTEX_INITIALIZER, + .cond = PTHREAD_COND_INITIALIZER, + }; + struct taskstats before, after; + pthread_t thread; + pid_t tgid = getpid(); + int family_id; + int fd; + int ret; + + ksft_print_header(); + ksft_set_plan(1); + + if (geteuid()) + ksft_exit_skip("taskstats_fill_stats_tgid needs root\n"); + + fd = netlink_open(); + if (fd < 0) + ksft_exit_skip("failed to open generic netlink socket: %s\n", + strerror(-fd)); + + family_id = get_family_id(fd, TASKSTATS_GENL_NAME); + if (family_id < 0) + ksft_exit_skip("taskstats generic netlink family unavailable: %s\n", + strerror(-family_id)); + + /* Create worker thread that burns 200ms of CPU */ + if (pthread_create(&thread, NULL, worker_thread, &ctx) != 0) + ksft_exit_fail_msg("pthread_create failed: %s\n", strerror(errno)); + + /* Wait for worker to finish generating activity */ + pthread_mutex_lock(&ctx.lock); + while (!ctx.ready) + pthread_cond_wait(&ctx.cond, &ctx.lock); + pthread_mutex_unlock(&ctx.lock); + + /* + * Snapshot A: TGID stats while worker is alive and sleeping. + * Contains main thread + worker contributions. + */ + ret = get_taskstats(fd, family_id, TASKSTATS_CMD_ATTR_TGID, tgid, &before); + if (ret) + ksft_exit_fail_msg("TGID query before exit failed: %s\n", + strerror(-ret)); + + /* Release worker so it can exit, then join (deterministic wait). + * + * Kernel exit path ordering guarantees: + * do_exit() + * taskstats_exit() -> fill_tgid_exit() (accumulates worker into signal->stats) + * exit_notify() (releases the thread) + * do_task_dead() -> __schedule() (wakes joiner) + * + * So pthread_join() returns only after fill_tgid_exit() has completed. + */ + pthread_mutex_lock(&ctx.lock); + ctx.release = true; + pthread_cond_broadcast(&ctx.cond); + pthread_mutex_unlock(&ctx.lock); + + pthread_join(thread, NULL); + + /* + * Snapshot B: TGID stats after worker has exited. + * fill_stats_for_tgid() does: + * memcpy(signal->stats) <- includes fill_tgid_exit accumulation + * + scan live threads <- only main thread now + */ + ret = get_taskstats(fd, family_id, TASKSTATS_CMD_ATTR_TGID, tgid, &after); + if (ret) + ksft_exit_fail_msg("TGID query after exit failed: %s\n", + strerror(-ret)); + + print_stats("TGID before worker exit", &before); + print_stats("TGID after worker exit", &after); + + /* + * The worker burned 200ms of CPU before the first snapshot. + * If the kernel correctly retained its contribution via + * fill_tgid_exit(), then the TGID CPU total after exit must be at + * least as large as the TGID CPU total before exit. + */ + ksft_test_result(cpu_total(&after) >= cpu_total(&before), + "TGID CPU stats should not regress after thread exit\n"); + + close(fd); + ksft_finished(); + return ksft_get_fail_cnt() ? KSFT_FAIL : KSFT_PASS; +} From f13f1b0cb56491547243e502988282e5be50bc44 Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Thu, 9 Apr 2026 02:08:51 +0545 Subject: [PATCH 013/108] seq_buf: export seq_buf_putmem_hex() and add KUnit tests The seq_buf KUnit suite does not exercise seq_buf_putmem_hex(). Add one test for the len > 8 chunking path and one overflow test where a later chunk no longer fits in the buffer. Export seq_buf_putmem_hex() as well so SEQ_BUF_KUNIT_TEST=m links cleanly. Without the export, modpost reports seq_buf_putmem_hex as undefined when seq_buf_kunit is built as a module. Link: https://lore.kernel.org/20260408202351.21829-1-shuvampandey1@gmail.com Signed-off-by: Shuvam Pandey Acked-by: Steven Rostedt (Google) Cc: David Gow Cc: "Masami Hiramatsu (Google)" Cc: Mathieu Desnoyers Signed-off-by: Andrew Morton --- lib/seq_buf.c | 1 + lib/tests/seq_buf_kunit.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lib/seq_buf.c b/lib/seq_buf.c index f3f3436d60a9..b59488fa8135 100644 --- a/lib/seq_buf.c +++ b/lib/seq_buf.c @@ -298,6 +298,7 @@ int seq_buf_putmem_hex(struct seq_buf *s, const void *mem, } return 0; } +EXPORT_SYMBOL_GPL(seq_buf_putmem_hex); /** * seq_buf_path - copy a path into the sequence buffer diff --git a/lib/tests/seq_buf_kunit.c b/lib/tests/seq_buf_kunit.c index 8a01579a978e..eb466386bbef 100644 --- a/lib/tests/seq_buf_kunit.c +++ b/lib/tests/seq_buf_kunit.c @@ -184,6 +184,38 @@ static void seq_buf_get_buf_commit_test(struct kunit *test) KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s)); } +static void seq_buf_putmem_hex_test(struct kunit *test) +{ + DECLARE_SEQ_BUF(s, 24); + const u8 data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; +#ifdef __BIG_ENDIAN + const char *expected = "0001020304050607 0809 "; +#else + const char *expected = "0706050403020100 0908 "; +#endif + + KUNIT_EXPECT_EQ(test, seq_buf_putmem_hex(&s, data, sizeof(data)), 0); + KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(&s)); + KUNIT_EXPECT_EQ(test, seq_buf_used(&s), strlen(expected)); + KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), expected); +} + +static void seq_buf_putmem_hex_overflow_test(struct kunit *test) +{ + DECLARE_SEQ_BUF(s, 20); + const u8 data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; +#ifdef __BIG_ENDIAN + const char *expected = "0001020304050607 "; +#else + const char *expected = "0706050403020100 "; +#endif + + KUNIT_EXPECT_EQ(test, seq_buf_putmem_hex(&s, data, sizeof(data)), -1); + KUNIT_EXPECT_TRUE(test, seq_buf_has_overflowed(&s)); + KUNIT_EXPECT_EQ(test, seq_buf_used(&s), 20); + KUNIT_EXPECT_STREQ(test, seq_buf_str(&s), expected); +} + static struct kunit_case seq_buf_test_cases[] = { KUNIT_CASE(seq_buf_init_test), KUNIT_CASE(seq_buf_declare_test), @@ -194,6 +226,8 @@ static struct kunit_case seq_buf_test_cases[] = { KUNIT_CASE(seq_buf_printf_test), KUNIT_CASE(seq_buf_printf_overflow_test), KUNIT_CASE(seq_buf_get_buf_commit_test), + KUNIT_CASE(seq_buf_putmem_hex_test), + KUNIT_CASE(seq_buf_putmem_hex_overflow_test), {} }; From 09d8b39563e84fc245d642182715a7e0e024b0f2 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 8 Apr 2026 15:45:42 -0400 Subject: [PATCH 014/108] get_maintainer: add --json output mode Add a --json flag to get_maintainer.pl that emits structured JSON output, making results machine-parseable for CI systems, IDE integrations, and AI-assisted development tools. The JSON output includes a maintainers array with structured name, email, and role fields, plus optional arrays for scm, status, subsystem, web, and bug information when those flags are enabled. Normal text output behavior is completely unchanged when --json is not specified. Assisted-by: Claude:claude-opus-4-6 Link: https://lore.kernel.org/20260408194542.1354549-1-sashal@kernel.org Signed-off-by: Sasha Levin Acked-by: Joe Perches Signed-off-by: Andrew Morton --- scripts/get_maintainer.pl | 71 +++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl index f0ca0db6ddc2..16b80a700d4a 100755 --- a/scripts/get_maintainer.pl +++ b/scripts/get_maintainer.pl @@ -21,6 +21,7 @@ use Cwd; use File::Find; use File::Spec::Functions; use open qw(:std :encoding(UTF-8)); +use JSON::PP; my $cur_path = fastgetcwd() . '/'; my $lk_path = "./"; @@ -68,6 +69,7 @@ my $pattern_depth = 0; my $self_test = undef; my $version = 0; my $help = 0; +my $json = 0; my $find_maintainer_files = 0; my $maintainer_path; my $vcs_used = 0; @@ -285,6 +287,7 @@ if (!GetOptions( 'find-maintainer-files' => \$find_maintainer_files, 'mpath|maintainer-path=s' => \$maintainer_path, 'self-test:s' => \$self_test, + 'json!' => \$json, 'v|version' => \$version, 'h|help|usage' => \$help, )) { @@ -650,39 +653,48 @@ my %deduplicate_name_hash = (); my %deduplicate_address_hash = (); my @maintainers = get_maintainers(); -if (@maintainers) { - @maintainers = merge_email(@maintainers); - output(@maintainers); -} -if ($scm) { - @scm = uniq(@scm); - output(@scm); -} +@maintainers = merge_email(@maintainers) if (@maintainers); +@scm = uniq(@scm) if ($scm); +@substatus = uniq(@substatus) if ($output_substatus); +@status = uniq(@status) if ($status); +@subsystem = uniq(@subsystem) if ($subsystem); +@web = uniq(@web) if ($web); +@bug = uniq(@bug) if ($bug); -if ($output_substatus) { - @substatus = uniq(@substatus); - output(@substatus); -} +if ($json) { + my @json_maintainers; + for my $m (@maintainers) { + my ($addr, $role); + if ($output_roles && $m =~ /^(.*?)\s+\((.+)\)\s*$/) { + $addr = $1; + $role = $2; + } else { + $addr = $m; + } + my ($name, $email_addr) = parse_email($addr); + my %entry = (name => $name, email => $email_addr); + $entry{role} = $role if (defined $role && $role ne ''); + push(@json_maintainers, \%entry); + } -if ($status) { - @status = uniq(@status); - output(@status); -} + my %result = (maintainers => \@json_maintainers); + $result{scm} = \@scm if ($scm); + $result{status} = \@status if ($status); + $result{subsystem} = \@subsystem if ($subsystem); + $result{web} = \@web if ($web); + $result{bug} = \@bug if ($bug); -if ($subsystem) { - @subsystem = uniq(@subsystem); - output(@subsystem); -} - -if ($web) { - @web = uniq(@web); - output(@web); -} - -if ($bug) { - @bug = uniq(@bug); - output(@bug); + my $json_encoder = JSON::PP->new->canonical->utf8; + print($json_encoder->encode(\%result) . "\n"); +} else { + output(@maintainers) if (@maintainers); + output(@scm) if ($scm); + output(@substatus) if ($output_substatus); + output(@status) if ($status); + output(@subsystem) if ($subsystem); + output(@web) if ($web); + output(@bug) if ($bug); } exit($exit); @@ -1104,6 +1116,7 @@ Output type options: --separator [, ] => separator for multiple entries on 1 line using --separator also sets --nomultiline if --separator is not [, ] --multiline => print 1 entry per line + --json => output results as JSON Other options: --pattern-depth => Number of pattern directory traversals (default: 0 (all)) From 97bd80a91858ea72be5aa4565af2721fb732eba6 Mon Sep 17 00:00:00 2001 From: Anand Moon Date: Tue, 7 Apr 2026 11:09:34 +0530 Subject: [PATCH 015/108] treewide: fix indentation and whitespace in Kconfig files Clean up inconsistent indentation (mixing tabs and spaces) and remove extraneous whitespace in several Kconfig files across the tree. This is a purely cosmetic change to improve readability. Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Link: https://lore.kernel.org/20260407053945.14116-1-linux.amoon@gmail.com Signed-off-by: Anand Moon Reviewed-by: Jan Kara [fs] Reviewed-by: Liam R. Howlett [mm] Reviewed-by: Lorenzo Stoakes [mm] Reviewed-by: Christian Brauner Signed-off-by: Andrew Morton --- certs/Kconfig | 14 +++++++------- fs/Kconfig | 6 +++--- init/Kconfig | 24 ++++++++++++------------ lib/Kconfig | 2 +- mm/Kconfig | 4 ++-- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/certs/Kconfig b/certs/Kconfig index 8e39a80c7abe..9d2bf7fb5b9e 100644 --- a/certs/Kconfig +++ b/certs/Kconfig @@ -6,14 +6,14 @@ config MODULE_SIG_KEY default "certs/signing_key.pem" depends on MODULE_SIG || (IMA_APPRAISE_MODSIG && MODULES) help - Provide the file name of a private key/certificate in PEM format, - or a PKCS#11 URI according to RFC7512. The file should contain, or - the URI should identify, both the certificate and its corresponding - private key. + Provide the file name of a private key/certificate in PEM format, + or a PKCS#11 URI according to RFC7512. The file should contain, or + the URI should identify, both the certificate and its corresponding + private key. - If this option is unchanged from its default "certs/signing_key.pem", - then the kernel will automatically generate the private key and - certificate as described in Documentation/admin-guide/module-signing.rst + If this option is unchanged from its default "certs/signing_key.pem", + then the kernel will automatically generate the private key and + certificate as described in Documentation/admin-guide/module-signing.rst choice prompt "Type of module signing key to be generated" diff --git a/fs/Kconfig b/fs/Kconfig index 43cb06de297f..cf6ae64776e6 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -78,7 +78,7 @@ config FS_DAX --map=mem: https://docs.pmem.io/ndctl-user-guide/ndctl-man-pages/ndctl-create-namespace - For ndctl to work CONFIG_DEV_DAX needs to be enabled as well. For most + For ndctl to work CONFIG_DEV_DAX needs to be enabled as well. For most file systems DAX support needs to be manually enabled globally or per-inode using a mount option as well. See the file documentation in Documentation/filesystems/dax.rst for details. @@ -116,8 +116,8 @@ config FILE_LOCKING default y help This option enables standard file locking support, required - for filesystems like NFS and for the flock() system - call. Disabling this option saves about 11k. + for filesystems like NFS and for the flock() system + call. Disabling this option saves about 11k. source "fs/crypto/Kconfig" diff --git a/init/Kconfig b/init/Kconfig index 2937c4d308ae..624d93506190 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1050,14 +1050,14 @@ config PAGE_COUNTER bool config CGROUP_FAVOR_DYNMODS - bool "Favor dynamic modification latency reduction by default" - help - This option enables the "favordynmods" mount option by default - which reduces the latencies of dynamic cgroup modifications such - as task migrations and controller on/offs at the cost of making - hot path operations such as forks and exits more expensive. + bool "Favor dynamic modification latency reduction by default" + help + This option enables the "favordynmods" mount option by default + which reduces the latencies of dynamic cgroup modifications such + as task migrations and controller on/offs at the cost of making + hot path operations such as forks and exits more expensive. - Say N if unsure. + Say N if unsure. config MEMCG bool "Memory controller" @@ -1139,7 +1139,7 @@ config GROUP_SCHED_WEIGHT def_bool n config GROUP_SCHED_BANDWIDTH - def_bool n + def_bool n config FAIR_GROUP_SCHED bool "Group scheduling for SCHED_OTHER" @@ -1645,10 +1645,10 @@ config LD_ORPHAN_WARN depends on $(ld-option,--orphan-handling=error) config LD_ORPHAN_WARN_LEVEL - string - depends on LD_ORPHAN_WARN - default "error" if WERROR - default "warn" + string + depends on LD_ORPHAN_WARN + default "error" if WERROR + default "warn" config SYSCTL bool diff --git a/lib/Kconfig b/lib/Kconfig index 00a9509636c1..ed31919a5a0b 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -590,7 +590,7 @@ config OBJAGG config LWQ_TEST bool "Boot-time test for lwq queuing" help - Run boot-time test of light-weight queuing. + Run boot-time test of light-weight queuing. endmenu diff --git a/mm/Kconfig b/mm/Kconfig index e8bf1e9e6ad9..6c2217ea3523 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -1303,7 +1303,7 @@ config ARCH_HAS_PTE_SPECIAL bool config MAPPING_DIRTY_HELPERS - bool + bool config KMAP_LOCAL bool @@ -1434,7 +1434,7 @@ config ARCH_HAS_USER_SHADOW_STACK bool help The architecture has hardware support for userspace shadow call - stacks (eg, x86 CET, arm64 GCS or RISC-V Zicfiss). + stacks (eg, x86 CET, arm64 GCS or RISC-V Zicfiss). config HAVE_ARCH_TLB_REMOVE_TABLE def_bool n From f53718d5a23b72149c9bb7287c9ca078ff4d4aab Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 6 Apr 2026 21:32:47 +0200 Subject: [PATCH 016/108] lib/tests: string_helpers: decouple unescape and escape cases Patch series "lib/tests: string_helpers: Slight improvements". Two ad-hoc patches to improve the test module. It was induced by another patch that poorly tried to add (existing) test cases and make me revisit string_helpers_kunit.c. This patch (of 2): Currently the escape and unescape test cases go in one step. Decouple them for the better granularity and understanding test coverage in the results. Link: https://lore.kernel.org/20260406193425.1534197-1-andriy.shevchenko@linux.intel.com Link: https://lore.kernel.org/20260406193425.1534197-2-andriy.shevchenko@linux.intel.com Signed-off-by: Andy Shevchenko Cc: Kees Cook Signed-off-by: Andrew Morton --- lib/tests/string_helpers_kunit.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/tests/string_helpers_kunit.c b/lib/tests/string_helpers_kunit.c index c853046183d2..cd08e79a857d 100644 --- a/lib/tests/string_helpers_kunit.c +++ b/lib/tests/string_helpers_kunit.c @@ -601,6 +601,11 @@ static void test_unescape(struct kunit *test) test_string_unescape(test, "unescape", i, false); test_string_unescape(test, "unescape inplace", get_random_u32_below(UNESCAPE_ALL_MASK + 1), true); +} + +static void test_escape(struct kunit *test) +{ + unsigned int i; /* Without dictionary */ for (i = 0; i < ESCAPE_ALL_MASK + 1; i++) @@ -615,6 +620,7 @@ static struct kunit_case string_helpers_test_cases[] = { KUNIT_CASE(test_get_size), KUNIT_CASE(test_upper_lower), KUNIT_CASE(test_unescape), + KUNIT_CASE(test_escape), {} }; From fe495c4e2ee34f84d856c06d524d43512e1f4f98 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 6 Apr 2026 21:32:48 +0200 Subject: [PATCH 017/108] lib/tests: string_helpers: don't use "proxy" headers Update header inclusions to follow IWYU (Include What You Use) principle. Link: https://lore.kernel.org/20260406193425.1534197-3-andriy.shevchenko@linux.intel.com Signed-off-by: Andy Shevchenko Cc: Kees Cook Signed-off-by: Andrew Morton --- lib/tests/string_helpers_kunit.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/tests/string_helpers_kunit.c b/lib/tests/string_helpers_kunit.c index cd08e79a857d..9fbe91079c7e 100644 --- a/lib/tests/string_helpers_kunit.c +++ b/lib/tests/string_helpers_kunit.c @@ -5,11 +5,16 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include + #include -#include +#include +#include +#include #include -#include +#include +#include #include +#include static void test_string_check_buf(struct kunit *test, const char *name, unsigned int flags, From cd2464a059d6e88eecda87d71d3dbc87175289f0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 31 Mar 2026 16:28:38 +0200 Subject: [PATCH 018/108] init.h: discard exitcall symbols early Any __exitcall() and built-in module_exit() handler is marked as __used, which leads to the code being included in the object file and later discarded at link time. As far as I can tell, this was originally added at the same time as initcalls were marked the same way, to prevent them from getting dropped with gcc-3.4, but it was never actaully necessary to keep exit functions around. Mark them as __maybe_unused instead, which lets the compiler treat the exitcalls as entirely unused, and make better decisions about dropping specializing static functions called from these. Link: https://lore.kernel.org/all/acruxMNdnUlyRHiy@google.com/ Link: https://lore.kernel.org/20260331142846.3187706-1-arnd@kernel.org Signed-off-by: Arnd Bergmann Acked-by: Nicolas Schier Cc: Andriy Shevchenko Cc: Dmitry Torokhov Cc: Josh Poimboeuf Cc: Kees Cook Cc: Marco Elver Cc: Nathan Chancellor Cc: Peter Zijlstra Cc: Petr Mladek Signed-off-by: Andrew Morton --- include/linux/init.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/init.h b/include/linux/init.h index 40331923b9f4..6326c61e2332 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -47,7 +47,7 @@ #define __initdata __section(".init.data") #define __initconst __section(".init.rodata") #define __exitdata __section(".exit.data") -#define __exit_call __used __section(".exitcall.exit") +#define __exit_call __maybe_unused __section(".exitcall.exit") /* * modpost check for section mismatches during the kernel build. From cae29a5787e38ce7ec7727a87ac7e393a85cb1ef Mon Sep 17 00:00:00 2001 From: Josh Law Date: Tue, 24 Mar 2026 22:32:09 +0000 Subject: [PATCH 019/108] lib/base64: validate before writing in decode tail path Patch series "lib/base64: decode fixes", v2. Two small fixes for lib/base64.c: 1. base64_decode() writes a decoded byte to the output buffer before validating the input in the trailing-bytes path. Move the validity checks before any writes so dst is untouched on invalid input. 2. The @padding kernel-doc for base64_decode() was copy-pasted from base64_encode() and describes the wrong direction. This patch (of 2): The trailing-bytes path in base64_decode() writes a decoded byte to the output buffer before checking whether the input characters are valid. If the input is malformed, garbage is written to dst before the function returns -1. Move the validity checks before any writes so the output buffer is left untouched on invalid input. Link: https://lore.kernel.org/20260324223210.47676-1-objecting@objecting.org Link: https://lore.kernel.org/20260324223210.47676-2-objecting@objecting.org Fixes: 9c7d3cf94d33 ("lib/base64: rework encode/decode for speed and stricter validation") Signed-off-by: Josh Law Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton --- lib/base64.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/base64.c b/lib/base64.c index 41961a444028..20dacee25f65 100644 --- a/lib/base64.c +++ b/lib/base64.c @@ -168,15 +168,16 @@ int base64_decode(const char *src, int srclen, u8 *dst, bool padding, enum base6 return -1; val = (base64_rev_tables[s[0]] << 12) | (base64_rev_tables[s[1]] << 6); - *bp++ = val >> 10; if (srclen == 2) { if (val & 0x800003ff) return -1; + *bp++ = val >> 10; } else { val |= base64_rev_tables[s[2]]; if (val & 0x80000003) return -1; + *bp++ = val >> 10; *bp++ = val >> 2; } return bp - dst; From f424800c2a23d9fdc1355bdaf46ad2bc89741436 Mon Sep 17 00:00:00 2001 From: Josh Law Date: Tue, 24 Mar 2026 22:32:10 +0000 Subject: [PATCH 020/108] lib/base64: fix copy-pasted @padding doc in base64_decode() The @padding kernel-doc for base64_decode() says "whether to append '=' padding characters", which was copy-pasted from base64_encode(). In the decode context, it controls whether the input is expected to include padding, not whether to append it. Link: https://lore.kernel.org/20260324223210.47676-3-objecting@objecting.org Signed-off-by: Josh Law Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton --- lib/base64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/base64.c b/lib/base64.c index 20dacee25f65..325c7332b049 100644 --- a/lib/base64.c +++ b/lib/base64.c @@ -122,7 +122,7 @@ EXPORT_SYMBOL_GPL(base64_encode); * @src: the string to decode. Doesn't need to be NUL-terminated. * @srclen: the length of @src in bytes * @dst: (output) the decoded binary data - * @padding: whether to append '=' padding characters + * @padding: whether the input is expected to include '=' padding characters * @variant: which base64 variant to use * * Decodes a string using the selected Base64 variant. From 47d020e2337461f8a2991a0dc301f10a71903710 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 26 Feb 2026 16:05:25 +0000 Subject: [PATCH 021/108] kselftest/filelock: use ksft_perror() Patch series "selftests/filelock: Make output more kselftestish", v4. This series makes the output from the ofdlocks test a bit easier for tooling to work with, and also ignores the generated file while we're here. This patch (of 3): The ofdlocks test reports some errors via perror() which does not produce KTAP output, convert to ksft_perror() which does. Link: https://lore.kernel.org/20260226-selftest-filelock-ktap-v4-0-db8ae192ff42@kernel.org Link: https://lore.kernel.org/20260226-selftest-filelock-ktap-v4-1-db8ae192ff42@kernel.org Signed-off-by: Mark Brown Cc: Jeff Layton Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/filelock/ofdlocks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/filelock/ofdlocks.c b/tools/testing/selftests/filelock/ofdlocks.c index ff8d47fc373a..2d3b06ce5e5e 100644 --- a/tools/testing/selftests/filelock/ofdlocks.c +++ b/tools/testing/selftests/filelock/ofdlocks.c @@ -16,7 +16,7 @@ static int lock_set(int fd, struct flock *fl) fl->l_whence = SEEK_SET; ret = fcntl(fd, F_OFD_SETLK, fl); if (ret) - perror("fcntl()"); + ksft_perror("fcntl()"); return ret; } @@ -28,7 +28,7 @@ static int lock_get(int fd, struct flock *fl) fl->l_whence = SEEK_SET; ret = fcntl(fd, F_OFD_GETLK, fl); if (ret) - perror("fcntl()"); + ksft_perror("fcntl()"); return ret; } From 33d5b13098fb8db5ff120d4e2124c8b214696859 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 26 Feb 2026 16:05:26 +0000 Subject: [PATCH 022/108] kselftest/filelock: report each test in oftlocks separately The filelock test checks four different things but only reports an overall status, convert to use ksft_test_result() for these individual tests. Each test depends on the previous ones so we still bail out if any of them fail but we get a bit more information from UIs parsing the results. Link: https://lore.kernel.org/20260226-selftest-filelock-ktap-v4-2-db8ae192ff42@kernel.org Signed-off-by: Mark Brown Cc: Jeff Layton Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/filelock/ofdlocks.c | 90 +++++++++------------ 1 file changed, 39 insertions(+), 51 deletions(-) diff --git a/tools/testing/selftests/filelock/ofdlocks.c b/tools/testing/selftests/filelock/ofdlocks.c index 2d3b06ce5e5e..68bac28b234b 100644 --- a/tools/testing/selftests/filelock/ofdlocks.c +++ b/tools/testing/selftests/filelock/ofdlocks.c @@ -39,94 +39,82 @@ int main(void) int fd = open("/tmp/aa", O_RDWR | O_CREAT | O_EXCL, 0600); int fd2 = open("/tmp/aa", O_RDONLY); + ksft_print_header(); + ksft_set_plan(4); + unlink("/tmp/aa"); assert(fd != -1); assert(fd2 != -1); - ksft_print_msg("[INFO] opened fds %i %i\n", fd, fd2); + ksft_print_msg("opened fds %i %i\n", fd, fd2); /* Set some read lock */ fl.l_type = F_RDLCK; fl.l_start = 5; fl.l_len = 3; rc = lock_set(fd, &fl); - if (rc == 0) { - ksft_print_msg - ("[SUCCESS] set OFD read lock on first fd\n"); - } else { - ksft_print_msg("[FAIL] to set OFD read lock on first fd\n"); - return -1; - } + ksft_test_result(rc == 0, "set OFD read lock on first fd\n"); + if (rc != 0) + ksft_finished(); + /* Make sure read locks do not conflict on different fds. */ fl.l_type = F_RDLCK; fl.l_start = 5; fl.l_len = 1; rc = lock_get(fd2, &fl); if (rc != 0) - return -1; - if (fl.l_type != F_UNLCK) { - ksft_print_msg("[FAIL] read locks conflicted\n"); - return -1; - } + ksft_finished(); + if (fl.l_type != F_UNLCK) + ksft_exit_fail_msg("read locks conflicted\n"); + /* Make sure read/write locks do conflict on different fds. */ fl.l_type = F_WRLCK; fl.l_start = 5; fl.l_len = 1; rc = lock_get(fd2, &fl); if (rc != 0) - return -1; - if (fl.l_type != F_UNLCK) { - ksft_print_msg - ("[SUCCESS] read and write locks conflicted\n"); - } else { - ksft_print_msg - ("[SUCCESS] read and write locks not conflicted\n"); - return -1; - } + ksft_finished(); + ksft_test_result(fl.l_type != F_UNLCK, + "read and write locks conflicted\n"); + if (fl.l_type == F_UNLCK) + ksft_finished(); + /* Get info about the lock on first fd. */ fl.l_type = F_UNLCK; fl.l_start = 5; fl.l_len = 1; rc = lock_get(fd, &fl); - if (rc != 0) { - ksft_print_msg - ("[FAIL] F_OFD_GETLK with F_UNLCK not supported\n"); - return -1; - } - if (fl.l_type != F_UNLCK) { - ksft_print_msg - ("[SUCCESS] F_UNLCK test returns: locked, type %i pid %i len %zi\n", - fl.l_type, fl.l_pid, fl.l_len); - } else { - ksft_print_msg - ("[FAIL] F_OFD_GETLK with F_UNLCK did not return lock info\n"); - return -1; - } + if (rc != 0) + ksft_exit_fail_msg("F_OFD_GETLK with F_UNLCK not supported\n"); + ksft_test_result(fl.l_type != F_UNLCK, + "F_OFD_GETLK with F_UNLCK returned lock info\n"); + if (fl.l_type == F_UNLCK) + ksft_exit_fail(); + ksft_print_msg("F_UNLCK test returns: locked, type %i pid %i len %zi\n", + fl.l_type, fl.l_pid, fl.l_len); + /* Try the same but by locking everything by len==0. */ fl2.l_type = F_UNLCK; fl2.l_start = 0; fl2.l_len = 0; rc = lock_get(fd, &fl2); - if (rc != 0) { - ksft_print_msg - ("[FAIL] F_OFD_GETLK with F_UNLCK not supported\n"); - return -1; - } + if (rc != 0) + ksft_exit_fail_msg + ("F_OFD_GETLK with F_UNLCK not supported\n"); + ksft_test_result(memcmp(&fl, &fl2, sizeof(fl)) == 0, + "F_UNLCK with len==0 returned the same\n"); if (memcmp(&fl, &fl2, sizeof(fl))) { - ksft_print_msg - ("[FAIL] F_UNLCK test returns: locked, type %i pid %i len %zi\n", + ksft_exit_fail_msg + ("F_UNLCK test returns: locked, type %i pid %i len %zi\n", fl.l_type, fl.l_pid, fl.l_len); - return -1; } - ksft_print_msg("[SUCCESS] F_UNLCK with len==0 returned the same\n"); + /* Get info about the lock on second fd - no locks on it. */ fl.l_type = F_UNLCK; fl.l_start = 0; fl.l_len = 0; lock_get(fd2, &fl); - if (fl.l_type != F_UNLCK) { - ksft_print_msg - ("[FAIL] F_OFD_GETLK with F_UNLCK return lock info from another fd\n"); - return -1; - } - return 0; + ksft_test_result(fl.l_type == F_UNLCK, + "F_OFD_GETLK with F_UNLCK return lock info from another fd\n"); + + ksft_finished(); } From b3c726f9f46600868bdbe4bb7f1d770fd7ebb2e6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 26 Feb 2026 16:05:27 +0000 Subject: [PATCH 023/108] kselftest/filelock: add a .gitignore file Tell git to ignore the generated binary for the test. Link: https://lore.kernel.org/20260226-selftest-filelock-ktap-v4-3-db8ae192ff42@kernel.org Signed-off-by: Mark Brown Cc: Jeff Layton Cc: Shuah Khan Signed-off-by: Andrew Morton --- tools/testing/selftests/filelock/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 tools/testing/selftests/filelock/.gitignore diff --git a/tools/testing/selftests/filelock/.gitignore b/tools/testing/selftests/filelock/.gitignore new file mode 100644 index 000000000000..825e899a121b --- /dev/null +++ b/tools/testing/selftests/filelock/.gitignore @@ -0,0 +1 @@ +ofdlocks From f829d4d911cc296b32d14436a8a517e907228475 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Fri, 24 Apr 2026 22:08:55 -0400 Subject: [PATCH 024/108] rust: uaccess: use INLINE_COPY_TO_USER to guard copy_to_user() Patch series "uaccess: unify inline vs outline copy_{from,to}_user() selection", v2. The kernel allows arches to select between inline and outline implementations of the copy_{from,to}_user() by defining individual INLINE_COPY_FROM_USER and INLINE_COPY_TO_USER, correspondingly. However, all arches enable or disable them always together. Without the real use-case for one helper being inlined while the other outlined, having independent controls is excessive and error prone. The first patch of the series fixes rust/uaccess coppy_to_user() wrapper guarded with INLINE_COPY_FROM_USER. The 2nd patch switches codebase to the unified INLINE_COPY_USER. And the last patch cleans up ifdefery in the include/linux/uaccess.h This patch (of 3): The copy_to_user() rust helper is only needed when the main kernel inlines the function. It is controlled by INLINE_COPY_TO_USER, but the rust helper is protected with INLINE_COPY_FROM_USER. Fix that. Link: https://lore.kernel.org/20260425020857.356850-1-ynorov@nvidia.com Link: https://lore.kernel.org/20260425020857.356850-2-ynorov@nvidia.com Fixes: d99dc586ca7c7 ("uaccess: decouple INLINE_COPY_FROM_USER and CONFIG_RUST") Signed-off-by: Yury Norov Reported-by: Christophe Leroy (CS GROUP) Closes: https://lore.kernel.org/all/746c9c50-20c4-4dc9-a539-bf1310ff9414@kernel.org/ Cc: Alice Ryhl Cc: Mathieu Desnoyers Cc: Peter Zijlstra Cc: Randy Dunlap Cc: Viktor Malik Cc: Arnd Bergmann Signed-off-by: Andrew Morton --- rust/helpers/uaccess.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/helpers/uaccess.c b/rust/helpers/uaccess.c index d9625b9ee046..aff22f16ab38 100644 --- a/rust/helpers/uaccess.c +++ b/rust/helpers/uaccess.c @@ -20,7 +20,9 @@ unsigned long rust_helper__copy_from_user(void *to, const void __user *from, uns { return _inline_copy_from_user(to, from, n); } +#endif +#ifdef INLINE_COPY_TO_USER __rust_helper unsigned long rust_helper__copy_to_user(void __user *to, const void *from, unsigned long n) { From c02be2ad2b88c67c5d7c06b6aa7083b5b40e1077 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Fri, 24 Apr 2026 22:08:56 -0400 Subject: [PATCH 025/108] uaccess: unify inline vs outline copy_{from,to}_user() selection The kernel allows arches to select between inline and outline implementations of the copy_{from,to}_user() by defining individual INLINE_COPY_FROM_USER and INLINE_COPY_TO_USER, correspondingly. However, all arches enable or disable them always together. Without the real use-case for one helper being inlined while the other outlined, having independent controls is excessive and error prone. Switch the codebase to the single unified INLINE_COPY_USER control. Link: https://lore.kernel.org/20260425020857.356850-3-ynorov@nvidia.com Signed-off-by: Yury Norov Tested-by: Alice Ryhl Cc: Arnd Bergmann Cc: Christophe Leroy (CS GROUP) Cc: Mathieu Desnoyers Cc: Peter Zijlstra Cc: Randy Dunlap Cc: Viktor Malik Signed-off-by: Andrew Morton --- arch/arc/include/asm/uaccess.h | 3 +-- arch/arm/include/asm/uaccess.h | 3 +-- arch/arm64/include/asm/uaccess.h | 3 +-- arch/hexagon/include/asm/uaccess.h | 3 +-- arch/loongarch/include/asm/uaccess.h | 3 +-- arch/m68k/include/asm/uaccess.h | 3 +-- arch/microblaze/include/asm/uaccess.h | 3 +-- arch/mips/include/asm/uaccess.h | 3 +-- arch/nios2/include/asm/uaccess.h | 3 +-- arch/openrisc/include/asm/uaccess.h | 3 +-- arch/parisc/include/asm/uaccess.h | 3 +-- arch/s390/include/asm/uaccess.h | 3 +-- arch/sh/include/asm/uaccess.h | 3 +-- arch/sparc/include/asm/uaccess_32.h | 3 +-- arch/sparc/include/asm/uaccess_64.h | 3 +-- arch/um/include/asm/uaccess.h | 3 +-- arch/xtensa/include/asm/uaccess.h | 3 +-- include/asm-generic/uaccess.h | 3 +-- include/linux/uaccess.h | 12 ++++++------ lib/usercopy.c | 4 +--- rust/helpers/uaccess.c | 4 +--- 21 files changed, 26 insertions(+), 48 deletions(-) diff --git a/arch/arc/include/asm/uaccess.h b/arch/arc/include/asm/uaccess.h index 1e8809ea000a..6df2209541ac 100644 --- a/arch/arc/include/asm/uaccess.h +++ b/arch/arc/include/asm/uaccess.h @@ -628,8 +628,7 @@ static inline unsigned long __clear_user(void __user *to, unsigned long n) return res; } -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER +#define INLINE_COPY_USER #define __clear_user __clear_user diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h index d6ae80b5df36..1593cf3b9800 100644 --- a/arch/arm/include/asm/uaccess.h +++ b/arch/arm/include/asm/uaccess.h @@ -616,8 +616,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) } #define __clear_user(addr, n) (memset((void __force *)addr, 0, n), 0) #endif -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER +#define INLINE_COPY_USER static inline unsigned long __must_check clear_user(void __user *to, unsigned long n) { diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index b0c83a08dda9..9f5bd9c69c24 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -456,8 +456,7 @@ do { \ unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \ } while (0) -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER +#define INLINE_COPY_USER extern unsigned long __must_check __arch_clear_user(void __user *to, unsigned long n); static inline unsigned long __must_check __clear_user(void __user *to, unsigned long n) diff --git a/arch/hexagon/include/asm/uaccess.h b/arch/hexagon/include/asm/uaccess.h index bff77efc0d9a..1aecf60ec4f5 100644 --- a/arch/hexagon/include/asm/uaccess.h +++ b/arch/hexagon/include/asm/uaccess.h @@ -26,8 +26,7 @@ unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n); unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n); -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER __kernel_size_t __clear_user_hexagon(void __user *dest, unsigned long count); #define __clear_user(a, s) __clear_user_hexagon((a), (s)) diff --git a/arch/loongarch/include/asm/uaccess.h b/arch/loongarch/include/asm/uaccess.h index 438269313e78..428f373feabf 100644 --- a/arch/loongarch/include/asm/uaccess.h +++ b/arch/loongarch/include/asm/uaccess.h @@ -292,8 +292,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) return __copy_user((__force void *)to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER /* * __clear_user: - Zero a block of memory in user space, with less checking. diff --git a/arch/m68k/include/asm/uaccess.h b/arch/m68k/include/asm/uaccess.h index 64914872a5c9..31d133faa45e 100644 --- a/arch/m68k/include/asm/uaccess.h +++ b/arch/m68k/include/asm/uaccess.h @@ -377,8 +377,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) return __constant_copy_to_user(to, from, n); return __generic_copy_to_user(to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER #define __get_kernel_nofault(dst, src, type, err_label) \ do { \ diff --git a/arch/microblaze/include/asm/uaccess.h b/arch/microblaze/include/asm/uaccess.h index 3aab2f17e046..afa0dd8d013f 100644 --- a/arch/microblaze/include/asm/uaccess.h +++ b/arch/microblaze/include/asm/uaccess.h @@ -250,8 +250,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) { return __copy_tofrom_user(to, (__force const void __user *)from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER /* * Copy a null terminated string from userspace. diff --git a/arch/mips/include/asm/uaccess.h b/arch/mips/include/asm/uaccess.h index c0cede273c7c..f00c36676b73 100644 --- a/arch/mips/include/asm/uaccess.h +++ b/arch/mips/include/asm/uaccess.h @@ -433,8 +433,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) return __cu_len_r; } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER extern __kernel_size_t __bzero(void __user *addr, __kernel_size_t size); diff --git a/arch/nios2/include/asm/uaccess.h b/arch/nios2/include/asm/uaccess.h index 6ccc9a232c23..5e6e05cc6efc 100644 --- a/arch/nios2/include/asm/uaccess.h +++ b/arch/nios2/include/asm/uaccess.h @@ -57,8 +57,7 @@ extern unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n); extern unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n); -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER extern long strncpy_from_user(char *__to, const char __user *__from, long __len); diff --git a/arch/openrisc/include/asm/uaccess.h b/arch/openrisc/include/asm/uaccess.h index d6500a374e18..db934ebc0069 100644 --- a/arch/openrisc/include/asm/uaccess.h +++ b/arch/openrisc/include/asm/uaccess.h @@ -218,8 +218,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long size) { return __copy_tofrom_user((__force void *)to, from, size); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER extern unsigned long __clear_user(void __user *addr, unsigned long size); diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h index 6c531d2c847e..0d17f81c8b27 100644 --- a/arch/parisc/include/asm/uaccess.h +++ b/arch/parisc/include/asm/uaccess.h @@ -197,7 +197,6 @@ unsigned long __must_check raw_copy_to_user(void __user *dst, const void *src, unsigned long len); unsigned long __must_check raw_copy_from_user(void *dst, const void __user *src, unsigned long len); -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER +#define INLINE_COPY_USER #endif /* __PARISC_UACCESS_H */ diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h index dff035372601..a9f32c53f699 100644 --- a/arch/s390/include/asm/uaccess.h +++ b/arch/s390/include/asm/uaccess.h @@ -30,8 +30,7 @@ void debug_user_asce(int exit); #define uaccess_kmsan_or_inline __always_inline #endif -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER static uaccess_kmsan_or_inline __must_check unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long size) diff --git a/arch/sh/include/asm/uaccess.h b/arch/sh/include/asm/uaccess.h index a79609eb14be..02e7a066538e 100644 --- a/arch/sh/include/asm/uaccess.h +++ b/arch/sh/include/asm/uaccess.h @@ -95,8 +95,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) { return __copy_user((__force void *)to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER /* * Clear the area and return remaining number of bytes diff --git a/arch/sparc/include/asm/uaccess_32.h b/arch/sparc/include/asm/uaccess_32.h index 43284b6ec46a..5542d5b32994 100644 --- a/arch/sparc/include/asm/uaccess_32.h +++ b/arch/sparc/include/asm/uaccess_32.h @@ -190,8 +190,7 @@ static inline unsigned long raw_copy_from_user(void *to, const void __user *from return __copy_user((__force void __user *) to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER static inline unsigned long __clear_user(void __user *addr, unsigned long size) { diff --git a/arch/sparc/include/asm/uaccess_64.h b/arch/sparc/include/asm/uaccess_64.h index b825a5dd0210..e2989cfba626 100644 --- a/arch/sparc/include/asm/uaccess_64.h +++ b/arch/sparc/include/asm/uaccess_64.h @@ -231,8 +231,7 @@ unsigned long __must_check raw_copy_from_user(void *to, unsigned long __must_check raw_copy_to_user(void __user *to, const void *from, unsigned long size); -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER unsigned long __must_check raw_copy_in_user(void __user *to, const void __user *from, diff --git a/arch/um/include/asm/uaccess.h b/arch/um/include/asm/uaccess.h index 0df9ea4abda8..4417c8b1d37a 100644 --- a/arch/um/include/asm/uaccess.h +++ b/arch/um/include/asm/uaccess.h @@ -27,8 +27,7 @@ static inline int __access_ok(const void __user *ptr, unsigned long size); #define __access_ok __access_ok #define __clear_user __clear_user -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER #include diff --git a/arch/xtensa/include/asm/uaccess.h b/arch/xtensa/include/asm/uaccess.h index 56aec6d504fe..6538a29a2bbd 100644 --- a/arch/xtensa/include/asm/uaccess.h +++ b/arch/xtensa/include/asm/uaccess.h @@ -237,8 +237,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) prefetch(from); return __xtensa_copy_user((__force void *)to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER /* * We need to return the number of bytes not cleared. Our memset() diff --git a/include/asm-generic/uaccess.h b/include/asm-generic/uaccess.h index b276f783494c..4569045e7139 100644 --- a/include/asm-generic/uaccess.h +++ b/include/asm-generic/uaccess.h @@ -91,8 +91,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) memcpy((void __force *)to, from, n); return 0; } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER +#define INLINE_COPY_USER #endif /* CONFIG_UACCESS_MEMCPY */ /* diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index 56328601218c..6100f1046546 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -84,7 +84,7 @@ * the 6 functions (copy_{to,from}_user(), __copy_{to,from}_user_inatomic()) * that are used instead. Out of those, __... ones are inlined. Plain * copy_{to,from}_user() might or might not be inlined. If you want them - * inlined, have asm/uaccess.h define INLINE_COPY_{TO,FROM}_USER. + * inlined, have asm/uaccess.h define INLINE_COPY_USER. * * NOTE: only copy_from_user() zero-pads the destination in case of short copy. * Neither __copy_from_user() nor __copy_from_user_inatomic() zero anything @@ -157,7 +157,7 @@ __copy_to_user(void __user *to, const void *from, unsigned long n) } /* - * Architectures that #define INLINE_COPY_TO_USER use this function + * Architectures that #define INLINE_COPY_USER use this function * directly in the normal copy_to/from_user(), the other ones go * through an extern _copy_to/from_user(), which expands the same code * here. @@ -190,7 +190,7 @@ _inline_copy_from_user(void *to, const void __user *from, unsigned long n) memset(to + (n - res), 0, res); return res; } -#ifndef INLINE_COPY_FROM_USER +#ifndef INLINE_COPY_USER extern __must_check unsigned long _copy_from_user(void *, const void __user *, unsigned long); #endif @@ -207,7 +207,7 @@ _inline_copy_to_user(void __user *to, const void *from, unsigned long n) } return n; } -#ifndef INLINE_COPY_TO_USER +#ifndef INLINE_COPY_USER extern __must_check unsigned long _copy_to_user(void __user *, const void *, unsigned long); #endif @@ -217,7 +217,7 @@ copy_from_user(void *to, const void __user *from, unsigned long n) { if (!check_copy_size(to, n, false)) return n; -#ifdef INLINE_COPY_FROM_USER +#ifdef INLINE_COPY_USER return _inline_copy_from_user(to, from, n); #else return _copy_from_user(to, from, n); @@ -230,7 +230,7 @@ copy_to_user(void __user *to, const void *from, unsigned long n) if (!check_copy_size(from, n, true)) return n; -#ifdef INLINE_COPY_TO_USER +#ifdef INLINE_COPY_USER return _inline_copy_to_user(to, from, n); #else return _copy_to_user(to, from, n); diff --git a/lib/usercopy.c b/lib/usercopy.c index b00a3a957de6..e2f0bf104a59 100644 --- a/lib/usercopy.c +++ b/lib/usercopy.c @@ -12,15 +12,13 @@ /* out-of-line parts */ -#if !defined(INLINE_COPY_FROM_USER) +#if !defined(INLINE_COPY_USER) unsigned long _copy_from_user(void *to, const void __user *from, unsigned long n) { return _inline_copy_from_user(to, from, n); } EXPORT_SYMBOL(_copy_from_user); -#endif -#if !defined(INLINE_COPY_TO_USER) unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n) { return _inline_copy_to_user(to, from, n); diff --git a/rust/helpers/uaccess.c b/rust/helpers/uaccess.c index aff22f16ab38..6e59cc9c665c 100644 --- a/rust/helpers/uaccess.c +++ b/rust/helpers/uaccess.c @@ -14,15 +14,13 @@ rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n) return copy_to_user(to, from, n); } -#ifdef INLINE_COPY_FROM_USER +#ifdef INLINE_COPY_USER __rust_helper unsigned long rust_helper__copy_from_user(void *to, const void __user *from, unsigned long n) { return _inline_copy_from_user(to, from, n); } -#endif -#ifdef INLINE_COPY_TO_USER __rust_helper unsigned long rust_helper__copy_to_user(void __user *to, const void *from, unsigned long n) { From bd99fcfc6219ebe36ae4d0bf5333b5ecc17b53df Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Fri, 24 Apr 2026 22:08:57 -0400 Subject: [PATCH 026/108] uaccess: minimize INLINE_COPY_USER-related ifdefery Now that we've got the same config selecting inline vs outline copy_to_user() and copy_from_user(), we can simplify the corresponding logic in the uaccess.h. Link: https://lore.kernel.org/20260425020857.356850-4-ynorov@nvidia.com Fixes: 1f9a8286bc0c ("uaccess: always export _copy_[from|to]_user with CONFIG_RUST") Signed-off-by: Yury Norov Tested-by: Alice Ryhl Cc: Arnd Bergmann Cc: Christophe Leroy (CS GROUP) Cc: Mathieu Desnoyers Cc: Peter Zijlstra Cc: Randy Dunlap Cc: Viktor Malik Signed-off-by: Andrew Morton --- include/linux/uaccess.h | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index 6100f1046546..e0c3d6e29301 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -190,10 +190,6 @@ _inline_copy_from_user(void *to, const void __user *from, unsigned long n) memset(to + (n - res), 0, res); return res; } -#ifndef INLINE_COPY_USER -extern __must_check unsigned long -_copy_from_user(void *, const void __user *, unsigned long); -#endif static inline __must_check unsigned long _inline_copy_to_user(void __user *to, const void *from, unsigned long n) @@ -207,7 +203,13 @@ _inline_copy_to_user(void __user *to, const void *from, unsigned long n) } return n; } -#ifndef INLINE_COPY_USER +#ifdef INLINE_COPY_USER +# define _copy_to_user _inline_copy_to_user +# define _copy_from_user _inline_copy_from_user +#else +extern __must_check unsigned long +_copy_from_user(void *, const void __user *, unsigned long); + extern __must_check unsigned long _copy_to_user(void __user *, const void *, unsigned long); #endif @@ -217,11 +219,7 @@ copy_from_user(void *to, const void __user *from, unsigned long n) { if (!check_copy_size(to, n, false)) return n; -#ifdef INLINE_COPY_USER - return _inline_copy_from_user(to, from, n); -#else return _copy_from_user(to, from, n); -#endif } static __always_inline unsigned long __must_check @@ -229,12 +227,7 @@ copy_to_user(void __user *to, const void *from, unsigned long n) { if (!check_copy_size(from, n, true)) return n; - -#ifdef INLINE_COPY_USER - return _inline_copy_to_user(to, from, n); -#else return _copy_to_user(to, from, n); -#endif } #ifndef copy_mc_to_kernel From 5a13e296a3e32a70db2a520d8611a53d7bde10e8 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Thu, 30 Apr 2026 16:15:33 +0200 Subject: [PATCH 027/108] kcov: refactor common handle ID into kcov_common_handle_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Store common handle IDs in "struct kcov_common_handle_id", which consumes no space in non-KCOV builds. This cleanup removes #ifdef boilerplate code from subsystems that integrate with KCOV (in particular in usbip_common.h and skbuff.h, see the diffstat). This should also make it easier to add KCOV remote coverage to more subsystems in the future. Link: https://lore.kernel.org/20260430-kcov-refactor-common-handle-v1-1-23a0c7a0ba38@google.com Signed-off-by: Jann Horn Acked-by: Greg Kroah-Hartman Reviewed-by: Dmitry Vyukov Acked-by: Jakub Kicinski Cc: Alexander Potapenko Cc: Andrey Konovalov Cc: Eugenio Pérez Cc: Hongren (Zenithal) Zheng Cc: Jann Horn Cc: Jason Wang Cc: "Michael S. Tsirkin" Cc: Shuah Khan Cc: Valentina Manea Signed-off-by: Andrew Morton --- drivers/usb/usbip/usbip_common.h | 29 +---------------------------- drivers/usb/usbip/vhci_rx.c | 4 ++-- drivers/usb/usbip/vhci_sysfs.c | 2 +- drivers/vhost/vhost.h | 2 +- include/linux/kcov.h | 12 ++++++------ include/linux/skbuff.h | 14 +++----------- include/linux/types.h | 6 ++++++ kernel/kcov.c | 6 +++--- 8 files changed, 23 insertions(+), 52 deletions(-) diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h index 282efca64a01..be4c5e65a7f8 100644 --- a/drivers/usb/usbip/usbip_common.h +++ b/drivers/usb/usbip/usbip_common.h @@ -282,9 +282,7 @@ struct usbip_device { void (*unusable)(struct usbip_device *); } eh_ops; -#ifdef CONFIG_KCOV - u64 kcov_handle; -#endif + struct kcov_common_handle_id kcov_handle; }; #define kthread_get_run(threadfn, data, namefmt, ...) \ @@ -339,29 +337,4 @@ static inline int interface_to_devnum(struct usb_interface *interface) return udev->devnum; } -#ifdef CONFIG_KCOV - -static inline void usbip_kcov_handle_init(struct usbip_device *ud) -{ - ud->kcov_handle = kcov_common_handle(); -} - -static inline void usbip_kcov_remote_start(struct usbip_device *ud) -{ - kcov_remote_start_common(ud->kcov_handle); -} - -static inline void usbip_kcov_remote_stop(void) -{ - kcov_remote_stop(); -} - -#else /* CONFIG_KCOV */ - -static inline void usbip_kcov_handle_init(struct usbip_device *ud) { } -static inline void usbip_kcov_remote_start(struct usbip_device *ud) { } -static inline void usbip_kcov_remote_stop(void) { } - -#endif /* CONFIG_KCOV */ - #endif /* __USBIP_COMMON_H */ diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c index a75f4a898a41..a678e7c89837 100644 --- a/drivers/usb/usbip/vhci_rx.c +++ b/drivers/usb/usbip/vhci_rx.c @@ -261,9 +261,9 @@ int vhci_rx_loop(void *data) if (usbip_event_happened(ud)) break; - usbip_kcov_remote_start(ud); + kcov_remote_start_common(ud->kcov_handle); vhci_rx_pdu(ud); - usbip_kcov_remote_stop(); + kcov_remote_stop(); } return 0; diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c index 5bc8c47788d4..b98d14c43d13 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -425,7 +425,7 @@ static ssize_t attach_store(struct device *dev, struct device_attribute *attr, vdev->ud.tcp_rx = tcp_rx; vdev->ud.tcp_tx = tcp_tx; vdev->ud.status = VDEV_ST_NOTASSIGNED; - usbip_kcov_handle_init(&vdev->ud); + vdev->ud.kcov_handle = kcov_common_handle(); spin_unlock(&vdev->ud.lock); spin_unlock_irqrestore(&vhci->lock, flags); diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index 4fe99765c5c7..0192ade6e749 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -44,7 +44,7 @@ struct vhost_worker { /* Used to serialize device wide flushing with worker swapping. */ struct mutex mutex; struct llist_head work_list; - u64 kcov_handle; + struct kcov_common_handle_id kcov_handle; u32 id; int attachment_cnt; bool killed; diff --git a/include/linux/kcov.h b/include/linux/kcov.h index 0143358874b0..cdb72b3859d8 100644 --- a/include/linux/kcov.h +++ b/include/linux/kcov.h @@ -43,11 +43,11 @@ do { \ /* See Documentation/dev-tools/kcov.rst for usage details. */ void kcov_remote_start(u64 handle); void kcov_remote_stop(void); -u64 kcov_common_handle(void); +struct kcov_common_handle_id kcov_common_handle(void); -static inline void kcov_remote_start_common(u64 id) +static inline void kcov_remote_start_common(struct kcov_common_handle_id id) { - kcov_remote_start(kcov_remote_handle(KCOV_SUBSYSTEM_COMMON, id)); + kcov_remote_start(kcov_remote_handle(KCOV_SUBSYSTEM_COMMON, id.val)); } static inline void kcov_remote_start_usb(u64 id) @@ -99,11 +99,11 @@ static inline void kcov_prepare_switch(struct task_struct *t) {} static inline void kcov_finish_switch(struct task_struct *t) {} static inline void kcov_remote_start(u64 handle) {} static inline void kcov_remote_stop(void) {} -static inline u64 kcov_common_handle(void) +static inline struct kcov_common_handle_id kcov_common_handle(void) { - return 0; + return (struct kcov_common_handle_id){}; } -static inline void kcov_remote_start_common(u64 id) {} +static inline void kcov_remote_start_common(struct kcov_common_handle_id id) {} static inline void kcov_remote_start_usb(u64 id) {} static inline void kcov_remote_start_usb_softirq(u64 id) {} static inline void kcov_remote_stop_softirq(void) {} diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 2bcf78a4de7b..a3fe418f7ced 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1082,9 +1082,7 @@ struct sk_buff { __u16 network_header; __u16 mac_header; -#ifdef CONFIG_KCOV - u64 kcov_handle; -#endif + struct kcov_common_handle_id kcov_handle; ); /* end headers group */ @@ -5437,20 +5435,14 @@ static inline void skb_reset_csum_not_inet(struct sk_buff *skb) } static inline void skb_set_kcov_handle(struct sk_buff *skb, - const u64 kcov_handle) + struct kcov_common_handle_id kcov_handle) { -#ifdef CONFIG_KCOV skb->kcov_handle = kcov_handle; -#endif } -static inline u64 skb_get_kcov_handle(struct sk_buff *skb) +static inline struct kcov_common_handle_id skb_get_kcov_handle(struct sk_buff *skb) { -#ifdef CONFIG_KCOV return skb->kcov_handle; -#else - return 0; -#endif } static inline void skb_mark_for_recycle(struct sk_buff *skb) diff --git a/include/linux/types.h b/include/linux/types.h index 608050dbca6a..93166b0b0617 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -224,6 +224,12 @@ struct ustat { char f_fpack[6]; }; +struct kcov_common_handle_id { +#ifdef CONFIG_KCOV + u64 val; +#endif +}; + /** * struct callback_head - callback structure for use with RCU and task_work * @next: next update requests in a list diff --git a/kernel/kcov.c b/kernel/kcov.c index 0b369e88c7c9..a43e33a28adb 100644 --- a/kernel/kcov.c +++ b/kernel/kcov.c @@ -1083,11 +1083,11 @@ void kcov_remote_stop(void) EXPORT_SYMBOL(kcov_remote_stop); /* See the comment before kcov_remote_start() for usage details. */ -u64 kcov_common_handle(void) +struct kcov_common_handle_id kcov_common_handle(void) { if (!in_task()) - return 0; - return current->kcov_handle; + return (struct kcov_common_handle_id){ .val = 0 }; + return (struct kcov_common_handle_id){ .val = current->kcov_handle }; } EXPORT_SYMBOL(kcov_common_handle); From fc15e3a30ddd950f009c76765331783b9af94a87 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 8 May 2026 10:51:56 +0300 Subject: [PATCH 028/108] rapidio/tsi721: prevent a bad dereference in tsi721_db_dpc() With a list_for_each() loop, if we don't find the item we are looking for in the list, then the loop exits with the iterator, which is "dbell" in this loop, pointing to invalid memory. This code uses the "found" variable to determine if we have found the doorbell we are looking for or not. However, the problem that the "found" variable needs to be set to false at the start of each iteration, otherwise after the first correct doorbell, then everything is marked as found. Reset the "found" to false at the start of the iteration and move the variable inside the loop. Link: https://lore.kernel.org/af2WHMZiqMwdYveO@stanley.mountain Fixes: 48618fb4e522 ("RapidIO: add mport driver for Tsi721 bridge") Signed-off-by: Dan Carpenter Cc: Alexandre Bounine Cc: Chul Kim Cc: Matt Porter Signed-off-by: Andrew Morton --- drivers/rapidio/devices/tsi721.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c index 66331e67cf4e..71b87bf8c31d 100644 --- a/drivers/rapidio/devices/tsi721.c +++ b/drivers/rapidio/devices/tsi721.c @@ -394,7 +394,6 @@ static void tsi721_db_dpc(struct work_struct *work) idb_work); struct rio_mport *mport; struct rio_dbell *dbell; - int found = 0; u32 wr_ptr, rd_ptr; u64 *idb_entry; u32 regval; @@ -412,6 +411,8 @@ static void tsi721_db_dpc(struct work_struct *work) rd_ptr = ioread32(priv->regs + TSI721_IDQ_RP(IDB_QUEUE)) % IDB_QSIZE; while (wr_ptr != rd_ptr) { + int found = 0; + idb_entry = (u64 *)(priv->idb_base + (TSI721_IDB_ENTRY_SIZE * rd_ptr)); rd_ptr++; From 83544c68f71da543f59fdc9b30c1f5ff66de1a25 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 7 May 2026 10:41:46 -0700 Subject: [PATCH 029/108] MAINTAINERS/CREDITS: remove inactive checkpatch reviewers Dwaipayan Ray and Lukas Bulwahn have not commented on checkpatch in several years. Lukas is still active on MAINTAINERS. Create an entry in CREDITS for Dwaipayan. Link: https://lore.kernel.org/64f057d1d7f247583eb616337b89b3ff7bcc627f.camel@perches.com Signed-off-by: Joe Perches Cc: Dwaipayan Ray Cc: Lukas Bulwahn Signed-off-by: Andrew Morton --- CREDITS | 4 ++++ MAINTAINERS | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CREDITS b/CREDITS index 17962bdd6dbd..102a20dbd03f 100644 --- a/CREDITS +++ b/CREDITS @@ -3368,6 +3368,10 @@ N: Anil Ravindranath E: anil_ravindranath@pmc-sierra.com D: PMC-Sierra MaxRAID driver +N: Dwaipayan Ray +E: dwaipayanray1@gmail.com +D: checkpatch improvements + N: Eric S. Raymond E: esr@thyrsus.com W: http://www.tuxedo.org/~esr/ diff --git a/MAINTAINERS b/MAINTAINERS index 461a3eed6129..236b0785bf3d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5969,8 +5969,6 @@ F: drivers/input/keyboard/charlieplex_keypad.c CHECKPATCH M: Andy Whitcroft M: Joe Perches -R: Dwaipayan Ray -R: Lukas Bulwahn S: Maintained F: scripts/checkpatch.pl From e4f5f6f3c199ae7fbe142da6b79a97a504ac7e55 Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Thu, 7 May 2026 11:52:37 +0200 Subject: [PATCH 030/108] kfence: fix KASAN HW tags bypass via runtime sample_interval change If a user writes a non-zero value to the sample_interval module parameter at runtime, the missing KASAN HW tags check in the late init path allows KFENCE to be enabled alongside KASAN HW tags, bypassing the boot restriction. This patch adds the missing check to param_set_sample_interval() to reject the parameter change if KASAN HW tags are enabled. Link: https://lore.kernel.org/20260507095237.741017-1-glider@google.com Fixes: 09833d99db36 ("mm/kfence: disable KFENCE upon KASAN HW tags enablement") Signed-off-by: Alexander Potapenko Cc: Marco Elver Cc: Greg Thelen Cc: Roman Gushchin Cc: Pimyn Girgis Signed-off-by: Andrew Morton --- mm/kfence/core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mm/kfence/core.c b/mm/kfence/core.c index 655dc5ce3240..ee6ae01de5ae 100644 --- a/mm/kfence/core.c +++ b/mm/kfence/core.c @@ -77,6 +77,11 @@ static int param_set_sample_interval(const char *val, const struct kernel_param WRITE_ONCE(kfence_enabled, false); } + if (num && kasan_hw_tags_enabled()) { + pr_info("disabled as KASAN HW tags are enabled\n"); + return -EINVAL; + } + *((unsigned long *)kp->arg) = num; if (num && !READ_ONCE(kfence_enabled) && system_state != SYSTEM_BOOTING) From 25c786a9f7f8b5e882367f569020f1f37ac9fea7 Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Thu, 7 May 2026 11:49:19 +0200 Subject: [PATCH 031/108] llist: make locking comments consistent llist's locking requirement table has a legend which claims that all operations not needing a lock a marked with '-', whereas in truth for some table entries just a whitespace is used. Add the '-' to all appropriate places. Link: https://lore.kernel.org/20260507094918.23910-2-phasta@kernel.org Signed-off-by: Philipp Stanner Cc: Jens Axboe Cc: "Paul E . McKenney" Cc: Shakeel Butt Cc: Tejun Heo Signed-off-by: Andrew Morton --- include/linux/llist.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/llist.h b/include/linux/llist.h index 607b2360c938..8846b7709669 100644 --- a/include/linux/llist.h +++ b/include/linux/llist.h @@ -26,8 +26,8 @@ * * | add | del_first | del_all * add | - | - | - - * del_first | | L | L - * del_all | | | - + * del_first | - | L | L + * del_all | - | - | - * * Where, a particular row's operation can happen concurrently with a column's * operation, with "-" being no lock needed, while "L" being lock is needed. From 56cb9b7d96b28a1173a510ab25354b6599ad3a33 Mon Sep 17 00:00:00 2001 From: Konstantin Khorenko Date: Mon, 11 May 2026 12:50:52 +0200 Subject: [PATCH 032/108] gcov: use atomic counter updates to fix concurrent access crashes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GCC's GCOV instrumentation can merge global branch counters with loop induction variables as an optimization. In inflate_fast(), the inner copy loops get transformed so that the GCOV counter value is loaded multiple times to compute the loop base address, start index, and end bound. Since GCOV counters are global (not per-CPU), concurrent execution on different CPUs causes the counter to change between loads, producing inconsistent values and out-of-bounds memory writes. The crash manifests during IPComp (IP Payload Compression) processing when inflate_fast() runs concurrently on multiple CPUs: BUG: unable to handle page fault for address: ffffd0a3c0902ffa RIP: inflate_fast+1431 Call Trace: zlib_inflate __deflate_decompress crypto_comp_decompress ipcomp_decompress [xfrm_ipcomp] ipcomp_input [xfrm_ipcomp] xfrm_input At the crash point, the compiler generated three loads from the same global GCOV counter (__gcov0.inflate_fast+216) to compute base, start, and end for an indexed loop. Another CPU modified the counter between loads, making the values inconsistent - the write went 3.4 MB past a 65 KB buffer. Add -fprofile-update=prefer-atomic to CFLAGS_GCOV at the global level in the top-level Makefile, guarded by a try-run compile test. The test compiles a minimal program with and without -fprofile-update=prefer-atomic using the full KBUILD_CFLAGS, then compares undefined symbols in the resulting object files. If prefer-atomic introduces new undefined references (such as __atomic_fetch_add_8 on i386 or __aarch64_ldadd8_relax on arm64 with outline-atomics), the flag is not added -- the kernel does not link against libatomic. On architectures where GCC inlines 64-bit atomic counter updates (x86_64, s390, ...) the test passes and the flag is enabled, preventing the compiler from merging counters with loop induction variables and fixing the observed concurrent-access crash. On architectures where the flag would introduce libatomic dependencies, it is silently omitted and behaviour is no worse than before this patch. Move the CFLAGS_GCOV block from its original position (before the arch Makefile include) to after the core KBUILD_CFLAGS assignments but before the scripts/Makefile.gcc-plugins include. This placement ensures the try-run test sees arch-specific flags (-m32, -march=, -mno-outline-atomics) while avoiding GCC plugin flags (-fplugin=) that would break the test on clean builds when plugin shared objects do not yet exist. Link: https://lore.kernel.org/20260511105052.417187-2-khorenko@virtuozzo.com Signed-off-by: Konstantin Khorenko Tested-by: Arnd Bergmann Tested-by: Peter Oberparleiter Reviewed-by: Peter Oberparleiter Cc: Masahiro Yamada Cc: Miguel Ojeda Cc: Mikhail Zaslonko Cc: Nathan Chancellor Cc: Pavel Tikhomirov Cc: Thomas Weißschuh Cc: Signed-off-by: Andrew Morton --- Makefile | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index f056c921ea9c..8813f24b1ff4 100644 --- a/Makefile +++ b/Makefile @@ -826,12 +826,6 @@ endif # KBUILD_EXTMOD # Defaults to vmlinux, but the arch makefile usually adds further targets all: vmlinux -CFLAGS_GCOV := -fprofile-arcs -ftest-coverage -ifdef CONFIG_CC_IS_GCC -CFLAGS_GCOV += -fno-tree-loop-im -endif -export CFLAGS_GCOV - # The arch Makefiles can override CC_FLAGS_FTRACE. We may also append it later. ifdef CONFIG_FUNCTION_TRACER CC_FLAGS_FTRACE := -pg @@ -1149,6 +1143,27 @@ endif # Ensure compilers do not transform certain loops into calls to wcslen() KBUILD_CFLAGS += -fno-builtin-wcslen +CFLAGS_GCOV := -fprofile-arcs -ftest-coverage +ifdef CONFIG_CC_IS_GCC +CFLAGS_GCOV += -fno-tree-loop-im +# Use atomic counter updates to avoid concurrent-access crashes in GCOV. +# Only enable if -fprofile-update=prefer-atomic does not introduce new +# undefined symbols (e.g. libatomic calls that the kernel cannot link). +CFLAGS_GCOV += $(call try-run,\ + echo 'long long x; void f(void){x++;}' | \ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \ + -ftest-coverage -x c - -c -o "$$TMP.base" && \ + echo 'long long x; void f(void){x++;}' | \ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -w -fprofile-arcs \ + -ftest-coverage -fprofile-update=prefer-atomic \ + -x c - -c -o "$$TMP" && \ + $(NM) "$$TMP.base" | grep ' U ' > "$$TMP.ubase" || true ; \ + $(NM) "$$TMP" | grep ' U ' > "$$TMP.utest" || true ; \ + cmp -s "$$TMP.ubase" "$$TMP.utest",\ + -fprofile-update=prefer-atomic) +endif +export CFLAGS_GCOV + # change __FILE__ to the relative path to the source directory ifdef building_out_of_srctree KBUILD_CPPFLAGS += -fmacro-prefix-map=$(srcroot)/= From 738ce4979c802ba0f1f8249a893aa7db1fbb2cf8 Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Tue, 12 May 2026 10:16:00 +0800 Subject: [PATCH 033/108] ocfs2: reject inconsistent inode size before truncate [BUG] openat(..., O_WRONLY|O_CREAT|O_TRUNC) can hit: kernel BUG at fs/ocfs2/file.c:454! Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI RIP: 0010:ocfs2_truncate_file+0x1204/0x13c0 fs/ocfs2/file.c:454 Call Trace: ocfs2_setattr+0xa6d/0x1fd0 fs/ocfs2/file.c:1212 notify_change+0x4b5/0x1030 fs/attr.c:546 do_truncate+0x1d2/0x230 fs/open.c:68 handle_truncate fs/namei.c:3596 [inline] do_open fs/namei.c:3979 [inline] path_openat+0x260f/0x2ce0 fs/namei.c:4134 do_filp_open+0x1f6/0x430 fs/namei.c:4161 do_sys_openat2+0x117/0x1c0 fs/open.c:1437 do_sys_open fs/open.c:1452 [inline] __do_sys_openat fs/open.c:1468 [inline] __se_sys_openat fs/open.c:1463 [inline] __x64_sys_openat+0x15b/0x220 fs/open.c:1463 ... [CAUSE] ocfs2_truncate_file() treats di_bh->i_size matching inode->i_size as an internal code invariant and BUGs if it is broken. That assumption is too strong for corrupted metadata. The dinode block can still be structurally valid enough to pass ocfs2_read_inode_block() while no longer matching an already-instantiated VFS inode. On local mounts, ocfs2_inode_lock_update() skips refresh entirely, so truncate can observe the mismatch directly and crash instead of rejecting the corruption. [FIX] Turn the BUG_ON into normal OCFS2 corruption handling. If truncate sees di_bh->i_size disagree with inode->i_size, report it with ocfs2_error() and abort before touching truncate state. This keeps the fix at the first boundary that actually requires the sizes to match and avoids widening checks into hotter generic inode-lock paths Link: https://lore.kernel.org/20260512021601.3936417-1-gality369@gmail.com Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/file.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 7df9921c1a38..d6e977ba6565 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -444,21 +444,26 @@ int ocfs2_truncate_file(struct inode *inode, struct ocfs2_dinode *fe = NULL; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); - /* We trust di_bh because it comes from ocfs2_inode_lock(), which - * already validated it */ + /* + * On local mounts ocfs2_inode_lock_update() skips the inode + * refresh path, so truncation still needs to reject an inode + * state that no longer matches di_bh. + */ fe = (struct ocfs2_dinode *) di_bh->b_data; trace_ocfs2_truncate_file((unsigned long long)OCFS2_I(inode)->ip_blkno, (unsigned long long)le64_to_cpu(fe->i_size), (unsigned long long)new_i_size); - mlog_bug_on_msg(le64_to_cpu(fe->i_size) != i_size_read(inode), - "Inode %llu, inode i_size = %lld != di " - "i_size = %llu, i_flags = 0x%x\n", - (unsigned long long)OCFS2_I(inode)->ip_blkno, - i_size_read(inode), - (unsigned long long)le64_to_cpu(fe->i_size), - le32_to_cpu(fe->i_flags)); + if (unlikely(le64_to_cpu(fe->i_size) != i_size_read(inode))) { + status = ocfs2_error(inode->i_sb, + "Inode %llu has inconsistent i_size: inode = %lld, dinode = %llu, i_flags = 0x%x\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + i_size_read(inode), + (unsigned long long)le64_to_cpu(fe->i_size), + le32_to_cpu(fe->i_flags)); + goto bail; + } if (new_i_size > le64_to_cpu(fe->i_size)) { trace_ocfs2_truncate_file_error( From c0438198c28b1d22c272751af5e717c11d9fa8dd Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Tue, 12 May 2026 10:41:15 +0800 Subject: [PATCH 034/108] ocfs2: don't BUG_ON an invalid journal dinode [BUG] A fuzzed OCFS2 image can corrupt the current slot journal dinode while mount is still in progress. The mount path first reports the invalid journal block and then crashes in shutdown: kernel BUG at fs/ocfs2/journal.c:1034! Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI RIP: 0010:ocfs2_journal_toggle_dirty+0x2d6/0x340 fs/ocfs2/journal.c:1034 Call Trace: ocfs2_journal_shutdown+0x414/0xc30 fs/ocfs2/journal.c:1116 ocfs2_mount_volume fs/ocfs2/super.c:1785 [inline] ocfs2_fill_super+0x30a9/0x3cd0 fs/ocfs2/super.c:1083 get_tree_bdev_flags+0x38b/0x640 fs/super.c:1698 get_tree_bdev+0x24/0x40 fs/super.c:1721 ocfs2_get_tree+0x21/0x30 fs/ocfs2/super.c:1184 vfs_get_tree+0x9a/0x370 fs/super.c:1758 fc_mount fs/namespace.c:1199 [inline] do_new_mount_fc fs/namespace.c:3642 [inline] do_new_mount fs/namespace.c:3718 [inline] path_mount+0x5b8/0x1ea0 fs/namespace.c:4028 do_mount fs/namespace.c:4041 [inline] __do_sys_mount fs/namespace.c:4229 [inline] __se_sys_mount fs/namespace.c:4206 [inline] __x64_sys_mount+0x282/0x320 fs/namespace.c:4206 ... [CAUSE] ocfs2_journal_toggle_dirty() used to return -EIO when journal->j_bh no longer contained a valid dinode, because the startup and shutdown paths already handled that failure. Commit 10995aa2451a ("ocfs2: Morph the haphazard OCFS2_IS_VALID_DINODE() checks.") changed the check to a BUG_ON() under the assumption that the journal dinode had already been validated. That turns an unexpected invalid journal dinode during mount teardown into a kernel crash instead of a normal mount failure. [FIX] Replace the BUG_ON() with WARN_ON() and return -EIO. This keeps the invariant warning for debugging, but restores the original behavior of failing startup or shutdown cleanly instead of panicking the kernel. Link: https://lore.kernel.org/20260512024115.4036371-1-gality369@gmail.com Fixes: 10995aa2451a ("ocfs2: Morph the haphazard OCFS2_IS_VALID_DINODE() checks.") Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/journal.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index f9bf3bac085d..fc54cc798ce3 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -1022,11 +1022,8 @@ static int ocfs2_journal_toggle_dirty(struct ocfs2_super *osb, struct ocfs2_dinode *fe; fe = (struct ocfs2_dinode *)bh->b_data; - - /* The journal bh on the osb always comes from ocfs2_journal_init() - * and was validated there inside ocfs2_inode_lock_full(). It's a - * code bug if we mess it up. */ - BUG_ON(!OCFS2_IS_VALID_DINODE(fe)); + if (WARN_ON(!OCFS2_IS_VALID_DINODE(fe))) + return -EIO; flags = le32_to_cpu(fe->id1.journal1.ij_flags); if (dirty) From bef1006da49c91e8e154223d3005829a394f8f78 Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Fri, 8 May 2026 16:59:10 +0800 Subject: [PATCH 035/108] ocfs2: validate inline xattr header before ibody lookups Patch series "ocfs2: validate inline xattr header consumers". Corrupt i_xattr_inline_size can move the computed inode-body xattr header outside the dinode block. Several OCFS2 paths then trust xh_count or xattr entry geometry from that unchecked header. The reported KASAN splat hits the ibody lookup path: BUG: KASAN: use-after-free in ocfs2_xattr_find_entry+0x37b/0x3a0 ocfs2_xattr_ibody_get() ocfs2_xattr_get_nolock() ocfs2_calc_xattr_init() The same unchecked header derivation also exists in the outside-value probe, ibody remove, inline refcount attach, and inline reflink paths. This series factors the existing ibody list validation into a shared helper and then converts the remaining inline-header consumers one at a time. Patch layout: 1. validate ibody get/find and reuse the helper in ibody list 2. validate the outside-value probe 3. validate ibody remove 4. validate inline refcount attach 5. validate inline reflink This patch (of 5): [BUG] mknodat() can read past the end of a dinode block when ACL inheritance walks a corrupted inode-body xattr header. Another report shows the same unchecked lookup later faulting in the VFS open path after create returns a garbage status. KASAN: use-after-free in ocfs2_xattr_find_entry+0x37b/0x3a0 fs/ocfs2/xattr.c:1078 Read of size 2 at addr ffff88801c520300 by task syz.0.10/360 Trace: ... ocfs2_xattr_find_entry+0x37b/0x3a0 fs/ocfs2/xattr.c:1078 ocfs2_xattr_ibody_get fs/ocfs2/xattr.c:1178 [inline] ocfs2_xattr_get_nolock+0x2ee/0x1110 fs/ocfs2/xattr.c:1309 ocfs2_calc_xattr_init+0x716/0xac0 fs/ocfs2/xattr.c:628 ocfs2_mknod+0x935/0x2400 fs/ocfs2/namei.c:333 ocfs2_create+0x158/0x390 fs/ocfs2/namei.c:676 vfs_create fs/namei.c:3493 [inline] vfs_create+0x445/0x6f0 fs/namei.c:3477 do_mknodat+0x2d8/0x5e0 fs/namei.c:4372 __do_sys_mknodat fs/namei.c:4400 [inline] __se_sys_mknodat fs/namei.c:4397 [inline] __x64_sys_mknodat+0xb6/0xf0 fs/namei.c:4397 ... Another report: BUG: unable to handle page fault for address: fffffbfff3e40ec0 RIP: 0010:__d_entry_type include/linux/dcache.h:414 [inline] RIP: 0010:d_can_lookup include/linux/dcache.h:429 [inline] RIP: 0010:d_is_dir include/linux/dcache.h:439 [inline] RIP: 0010:path_openat+0xe2f/0x2ce0 fs/namei.c:4134 Trace: ... do_filp_open+0x1f6/0x430 fs/namei.c:4161 do_sys_openat2+0x117/0x1c0 fs/open.c:1437 __x64_sys_openat+0x15b/0x220 fs/open.c:1463 ... [CAUSE] ocfs2_xattr_ibody_list() already validates the inline xattr size and entry count, but ocfs2_xattr_ibody_get() and ocfs2_xattr_ibody_find() still derive the inline header directly from di->i_xattr_inline_size and then trust xh_count. A corrupted inline size or entry count can therefore move the computed header outside the dinode block before get/find start walking it. That can either make ocfs2_xattr_find_entry() dereference xs->header->xh_count outside the block or make ocfs2_xattr_get_nolock() bubble a garbage status back through ocfs2_calc_xattr_init() into the create/open path. [FIX] Factor the existing ibody header geometry checks into a shared helper. Use it in ocfs2_xattr_ibody_get() and ocfs2_xattr_ibody_find(), and have ocfs2_xattr_ibody_list() reuse the same helper instead of open-coding the validation. Reject corrupt ibody metadata with -EFSCORRUPTED before the lookup path can walk bogus xattr geometry or return a garbage status. Link: https://lore.kernel.org/20260508085914.61647-1-gality369@gmail.com Link: https://lore.kernel.org/20260508085914.61647-2-gality369@gmail.com Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Jia-Ju Bai Cc: Zixuan Fu Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/xattr.c | 82 +++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 86cfd4c2adf9..3a5a17cdcf7e 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -950,6 +950,41 @@ static int ocfs2_xattr_list_entries(struct inode *inode, return result; } +static int ocfs2_xattr_ibody_lookup_header(struct inode *inode, + struct ocfs2_dinode *di, + struct ocfs2_xattr_header **header) +{ + u16 xattr_count; + size_t max_entries; + u16 inline_size = le16_to_cpu(di->i_xattr_inline_size); + + if (inline_size > inode->i_sb->s_blocksize || + inline_size < sizeof(struct ocfs2_xattr_header)) { + ocfs2_error(inode->i_sb, + "Invalid xattr inline size %u in inode %llu\n", + inline_size, + (unsigned long long)OCFS2_I(inode)->ip_blkno); + return -EFSCORRUPTED; + } + + *header = (struct ocfs2_xattr_header *) + ((void *)di + inode->i_sb->s_blocksize - inline_size); + + xattr_count = le16_to_cpu((*header)->xh_count); + max_entries = (inline_size - sizeof(struct ocfs2_xattr_header)) / + sizeof(struct ocfs2_xattr_entry); + + if (xattr_count > max_entries) { + ocfs2_error(inode->i_sb, + "xattr entry count %u exceeds maximum %zu in inode %llu\n", + xattr_count, max_entries, + (unsigned long long)OCFS2_I(inode)->ip_blkno); + return -EFSCORRUPTED; + } + + return 0; +} + int ocfs2_has_inline_xattr_value_outside(struct inode *inode, struct ocfs2_dinode *di) { @@ -975,39 +1010,13 @@ static int ocfs2_xattr_ibody_list(struct inode *inode, struct ocfs2_xattr_header *header = NULL; struct ocfs2_inode_info *oi = OCFS2_I(inode); int ret = 0; - u16 xattr_count; - size_t max_entries; - u16 inline_size; if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) return ret; - inline_size = le16_to_cpu(di->i_xattr_inline_size); - - /* Validate inline size is reasonable */ - if (inline_size > inode->i_sb->s_blocksize || - inline_size < sizeof(struct ocfs2_xattr_header)) { - ocfs2_error(inode->i_sb, - "Invalid xattr inline size %u in inode %llu\n", - inline_size, - (unsigned long long)OCFS2_I(inode)->ip_blkno); - return -EFSCORRUPTED; - } - - header = (struct ocfs2_xattr_header *) - ((void *)di + inode->i_sb->s_blocksize - inline_size); - - xattr_count = le16_to_cpu(header->xh_count); - max_entries = (inline_size - sizeof(struct ocfs2_xattr_header)) / - sizeof(struct ocfs2_xattr_entry); - - if (xattr_count > max_entries) { - ocfs2_error(inode->i_sb, - "xattr entry count %u exceeds maximum %zu in inode %llu\n", - xattr_count, max_entries, - (unsigned long long)OCFS2_I(inode)->ip_blkno); - return -EFSCORRUPTED; - } + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &header); + if (ret) + return ret; ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size); @@ -1200,8 +1209,9 @@ static int ocfs2_xattr_ibody_get(struct inode *inode, return -ENODATA; xs->end = (void *)di + inode->i_sb->s_blocksize; - xs->header = (struct ocfs2_xattr_header *) - (xs->end - le16_to_cpu(di->i_xattr_inline_size)); + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &xs->header); + if (ret) + return ret; xs->base = (void *)xs->header; xs->here = xs->header->xh_entries; @@ -2726,12 +2736,14 @@ static int ocfs2_xattr_ibody_find(struct inode *inode, xs->xattr_bh = xs->inode_bh; xs->end = (void *)di + inode->i_sb->s_blocksize; - if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) - xs->header = (struct ocfs2_xattr_header *) - (xs->end - le16_to_cpu(di->i_xattr_inline_size)); - else + if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) { + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &xs->header); + if (ret) + return ret; + } else { xs->header = (struct ocfs2_xattr_header *) (xs->end - OCFS2_SB(inode->i_sb)->s_xattr_inline_size); + } xs->base = (void *)xs->header; xs->here = xs->header->xh_entries; From a61b83dd83ed44e937de7aead2b4ddd3ad32e3f8 Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Fri, 8 May 2026 16:59:11 +0800 Subject: [PATCH 036/108] ocfs2: validate inline xattr header before checking outside values [BUG] A corrupt inline xattr header can make ocfs2_has_inline_xattr_value_outside() walk xh_count from an unchecked header while refcount-tree teardown decides whether inline xattrs still point outside the inode body. [CAUSE] ocfs2_has_inline_xattr_value_outside() still computed the inline header directly from di->i_xattr_inline_size and immediately iterated xh_count. That is the same unchecked metadata boundary as the ibody lookup bug. [FIX] Reuse the shared inline-header helper before iterating xh_count. Because this helper returns a boolean-style answer to its caller, treat a corrupt header conservatively as "has outside values" instead of walking it. Link: https://lore.kernel.org/20260508085914.61647-3-gality369@gmail.com Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Jia-Ju Bai Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Zixuan Fu Signed-off-by: Andrew Morton --- fs/ocfs2/xattr.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 3a5a17cdcf7e..05f6f0a886cf 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -989,11 +989,12 @@ int ocfs2_has_inline_xattr_value_outside(struct inode *inode, struct ocfs2_dinode *di) { struct ocfs2_xattr_header *xh; + int ret; int i; - xh = (struct ocfs2_xattr_header *) - ((void *)di + inode->i_sb->s_blocksize - - le16_to_cpu(di->i_xattr_inline_size)); + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &xh); + if (ret) + return 1; for (i = 0; i < le16_to_cpu(xh->xh_count); i++) if (!ocfs2_xattr_is_local(&xh->xh_entries[i])) From b8ba8bbe69ad8a37e2f9bc2792c1b825f1964c91 Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Fri, 8 May 2026 16:59:12 +0800 Subject: [PATCH 037/108] ocfs2: validate inline xattr header before ibody remove [BUG] A corrupt inline xattr header can make ocfs2_xattr_ibody_remove() pass an unchecked header into ocfs2_remove_value_outside() during inode xattr teardown. [CAUSE] ocfs2_xattr_ibody_remove() still rebuilt the ibody xattr header directly from di->i_xattr_inline_size and then handed it to code that iterates xh_count and entry geometry. [FIX] Validate the inline xattr header with the shared helper before handing it to the outside-value removal path, and propagate -EFSCORRUPTED on bad metadata instead of traversing the unchecked header. Link: https://lore.kernel.org/20260508085914.61647-4-gality369@gmail.com Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Jia-Ju Bai Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Zixuan Fu Signed-off-by: Andrew Morton --- fs/ocfs2/xattr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 05f6f0a886cf..bbb25a01b097 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -2476,9 +2476,9 @@ static int ocfs2_xattr_ibody_remove(struct inode *inode, .vb_access = ocfs2_journal_access_di, }; - header = (struct ocfs2_xattr_header *) - ((void *)di + inode->i_sb->s_blocksize - - le16_to_cpu(di->i_xattr_inline_size)); + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &header); + if (ret) + return ret; ret = ocfs2_remove_value_outside(inode, &vb, header, ref_ci, ref_root_bh); From 4523ba0ee2e9ab6ee9c4b20b2867c3e4aa01f503 Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Fri, 8 May 2026 16:59:13 +0800 Subject: [PATCH 038/108] ocfs2: validate inline xattr header before inline refcount attach [BUG] A corrupt inline xattr header can make ocfs2_xattr_inline_attach_refcount() feed an unchecked header into the refcount-attachment walk for inline xattr values. [CAUSE] The inline refcount-attach path still derived the header directly from di->i_xattr_inline_size and then passed it to code that iterates xh_count and xattr entries. [FIX] Use the shared ibody header helper before attaching refcounts to inline xattr values so corrupt header geometry is rejected with -EFSCORRUPTED instead of being traversed. Link: https://lore.kernel.org/20260508085914.61647-5-gality369@gmail.com Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Jia-Ju Bai Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Zixuan Fu Signed-off-by: Andrew Morton --- fs/ocfs2/xattr.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index bbb25a01b097..4877406a83ce 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -6016,14 +6016,17 @@ static int ocfs2_xattr_inline_attach_refcount(struct inode *inode, struct ocfs2_cached_dealloc_ctxt *dealloc) { struct ocfs2_dinode *di = (struct ocfs2_dinode *)fe_bh->b_data; - struct ocfs2_xattr_header *header = (struct ocfs2_xattr_header *) - (fe_bh->b_data + inode->i_sb->s_blocksize - - le16_to_cpu(di->i_xattr_inline_size)); + struct ocfs2_xattr_header *header; + int ret; struct ocfs2_xattr_value_buf vb = { .vb_bh = fe_bh, .vb_access = ocfs2_journal_access_di, }; + ret = ocfs2_xattr_ibody_lookup_header(inode, di, &header); + if (ret) + return ret; + return ocfs2_xattr_attach_refcount_normal(inode, &vb, header, ref_ci, ref_root_bh, dealloc); } From bda614e6b4f27d3535ee86a96a6bab3b9b4f5e87 Mon Sep 17 00:00:00 2001 From: ZhengYuan Huang Date: Fri, 8 May 2026 16:59:14 +0800 Subject: [PATCH 039/108] ocfs2: validate inline xattr header before reflinking inline xattrs [BUG] A corrupt inline xattr header can make ocfs2_reflink_xattr_inline() lock, copy, and reflink xattr state from an unchecked ibody xattr header. [CAUSE] The inline reflink path still trusted di->i_xattr_inline_size to compute header_off, xh, and new_xh before handing the source header to the reflink allocator and copy logic. [FIX] Validate the source inode's inline xattr header with the shared helper first, then derive the reflink copy offsets from the validated inline size/header. This keeps the reflink path from traversing corrupt ibody xattr geometry. Link: https://lore.kernel.org/20260508085914.61647-6-gality369@gmail.com Signed-off-by: ZhengYuan Huang Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Jia-Ju Bai Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Zixuan Fu Signed-off-by: Andrew Morton --- fs/ocfs2/xattr.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 4877406a83ce..fcddd3c13acd 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -6511,12 +6511,10 @@ static int ocfs2_reflink_xattr_inline(struct ocfs2_xattr_reflink *args) handle_t *handle; struct ocfs2_super *osb = OCFS2_SB(args->old_inode->i_sb); struct ocfs2_dinode *di = (struct ocfs2_dinode *)args->old_bh->b_data; - int inline_size = le16_to_cpu(di->i_xattr_inline_size); - int header_off = osb->sb->s_blocksize - inline_size; - struct ocfs2_xattr_header *xh = (struct ocfs2_xattr_header *) - (args->old_bh->b_data + header_off); - struct ocfs2_xattr_header *new_xh = (struct ocfs2_xattr_header *) - (args->new_bh->b_data + header_off); + int inline_size; + int header_off; + struct ocfs2_xattr_header *xh; + struct ocfs2_xattr_header *new_xh; struct ocfs2_alloc_context *meta_ac = NULL; struct ocfs2_inode_info *new_oi; struct ocfs2_dinode *new_di; @@ -6525,6 +6523,15 @@ static int ocfs2_reflink_xattr_inline(struct ocfs2_xattr_reflink *args) .vb_access = ocfs2_journal_access_di, }; + ret = ocfs2_xattr_ibody_lookup_header(args->old_inode, di, &xh); + if (ret) + goto out; + + inline_size = le16_to_cpu(di->i_xattr_inline_size); + header_off = osb->sb->s_blocksize - inline_size; + new_xh = (struct ocfs2_xattr_header *) + (args->new_bh->b_data + header_off); + ret = ocfs2_reflink_lock_xattr_allocators(osb, xh, args->ref_root_bh, &credits, &meta_ac); if (ret) { From c210dfaa2720fcad9cbc8ec30fb45ddbabee4bf8 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Mon, 4 May 2026 16:36:05 -0400 Subject: [PATCH 040/108] scripts/bloat-o-meter: ignore _sdata _sdata is a linker symbol, but bloat-o-meter may consider it as a real variable: $ scripts/bloat-o-meter vmlinux.orig vmlinux add/remove: 7/1 grow/shrink: 0/0 up/down: 3437/-4096 (-659) Function old new delta crc32table_le - 1024 +1024 crc32table_be - 1024 +1024 crc32ctable_le - 1024 +1024 byte_rev_table - 256 +256 crc32_be - 39 +39 crc32c - 35 +35 crc32_le - 35 +35 _sdata 4096 - -4096 Total: Before=8592564398, After=8592563739, chg -0.00% With the patch: $ scripts/bloat-o-meter vmlinux.orig vmlinux add/remove: 7/0 grow/shrink: 0/0 up/down: 3437/0 (3437) Function old new delta crc32table_le - 1024 +1024 crc32table_be - 1024 +1024 crc32ctable_le - 1024 +1024 byte_rev_table - 256 +256 crc32_be - 39 +39 crc32c - 35 +35 crc32_le - 35 +35 Total: Before=8592560302, After=8592563739, chg +0.00% Link: https://lore.kernel.org/20260504203606.427972-1-ynorov@nvidia.com Signed-off-by: Yury Norov Cc: Valtteri Koskivuori Cc: Eric Dumazet Signed-off-by: Andrew Morton --- scripts/bloat-o-meter | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/bloat-o-meter b/scripts/bloat-o-meter index 9b4fb996d95b..5868a8b11b0f 100755 --- a/scripts/bloat-o-meter +++ b/scripts/bloat-o-meter @@ -43,6 +43,7 @@ def getsizes(file, format): if name.startswith("__se_compat_sys"): continue if name.startswith("__addressable_"): continue if name.startswith("__noinstr_text_start"): continue + if name.startswith("_sdata"): continue if name == "linux_banner": continue if name == "vermagic": continue # statics and some other optimizations adds random .NUMBER From 1c56b9cb489e7aa820dd61860e3dd892bdebe80c Mon Sep 17 00:00:00 2001 From: Lucas Poupeau Date: Mon, 4 May 2026 22:16:07 +0200 Subject: [PATCH 041/108] lib/bug: cleanup comment style, types and modernize logging Improve the overall code quality of lib/bug.c by: - Reformatting the main documentation block to follow the standard kernel multi-line comment style. - Replacing 'unsigned' with the preferred 'unsigned int'. - Converting legacy printk() calls to modern pr_warn() and pr_info() macros to include proper facility levels and satisfy checkpatch. Link: https://lore.kernel.org/20260504201607.56932-1-lucasp.linux@gmail.com Signed-off-by: Lucas Poupeau Signed-off-by: Andrew Morton --- lib/bug.c | 80 +++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/lib/bug.c b/lib/bug.c index 224f4cfa4aa3..f4a4df3991b0 100644 --- a/lib/bug.c +++ b/lib/bug.c @@ -1,41 +1,41 @@ // SPDX-License-Identifier: GPL-2.0 /* - Generic support for BUG() - - This respects the following config options: - - CONFIG_BUG - emit BUG traps. Nothing happens without this. - CONFIG_GENERIC_BUG - enable this code. - CONFIG_GENERIC_BUG_RELATIVE_POINTERS - use 32-bit relative pointers for bug_addr and file - CONFIG_DEBUG_BUGVERBOSE - emit full file+line information for each BUG - - CONFIG_BUG and CONFIG_DEBUG_BUGVERBOSE are potentially user-settable - (though they're generally always on). - - CONFIG_GENERIC_BUG is set by each architecture using this code. - - To use this, your architecture must: - - 1. Set up the config options: - - Enable CONFIG_GENERIC_BUG if CONFIG_BUG - - 2. Implement BUG (and optionally BUG_ON, WARN, WARN_ON) - - Define HAVE_ARCH_BUG - - Implement BUG() to generate a faulting instruction - - NOTE: struct bug_entry does not have "file" or "line" entries - when CONFIG_DEBUG_BUGVERBOSE is not enabled, so you must generate - the values accordingly. - - 3. Implement the trap - - In the illegal instruction trap handler (typically), verify - that the fault was in kernel mode, and call report_bug() - - report_bug() will return whether it was a false alarm, a warning, - or an actual bug. - - You must implement the is_valid_bugaddr(bugaddr) callback which - returns true if the eip is a real kernel address, and it points - to the expected BUG trap instruction. - - Jeremy Fitzhardinge 2006 + * Generic support for BUG() + * + * This respects the following config options: + * + * CONFIG_BUG - emit BUG traps. Nothing happens without this. + * CONFIG_GENERIC_BUG - enable this code. + * CONFIG_GENERIC_BUG_RELATIVE_POINTERS - use 32-bit relative pointers for bug_addr and file + * CONFIG_DEBUG_BUGVERBOSE - emit full file+line information for each BUG + * + * CONFIG_BUG and CONFIG_DEBUG_BUGVERBOSE are potentially user-settable + * (though they're generally always on). + * + * CONFIG_GENERIC_BUG is set by each architecture using this code. + * + * To use this, your architecture must: + * + * 1. Set up the config options: + * - Enable CONFIG_GENERIC_BUG if CONFIG_BUG + * + * 2. Implement BUG (and optionally BUG_ON, WARN, WARN_ON) + * - Define HAVE_ARCH_BUG + * - Implement BUG() to generate a faulting instruction + * - NOTE: struct bug_entry does not have "file" or "line" entries + * when CONFIG_DEBUG_BUGVERBOSE is not enabled, so you must generate + * the values accordingly. + * + * 3. Implement the trap + * - In the illegal instruction trap handler (typically), verify + * that the fault was in kernel mode, and call report_bug() + * - report_bug() will return whether it was a false alarm, a warning, + * or an actual bug. + * - You must implement the is_valid_bugaddr(bugaddr) callback which + * returns true if the eip is a real kernel address, and it points + * to the expected BUG trap instruction. + * + * Jeremy Fitzhardinge 2006 */ #define pr_fmt(fmt) fmt @@ -71,7 +71,7 @@ static struct bug_entry *module_find_bug(unsigned long bugaddr) guard(rcu)(); list_for_each_entry_rcu(mod, &module_bug_list, bug_list) { - unsigned i; + unsigned int i; bug = mod->bug_table; for (i = 0; i < mod->num_bugs; ++i, ++bug) @@ -191,14 +191,14 @@ void __warn_printf(const char *fmt, struct pt_regs *regs) } #endif - printk("%s", fmt); + pr_warn("%s", fmt); } static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long bugaddr, struct pt_regs *regs) { bool warning, once, done, no_cut, has_args; const char *file, *fmt; - unsigned line; + unsigned int line; if (!bug) { if (!is_valid_bugaddr(bugaddr)) @@ -237,7 +237,7 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga * extra debugging message it writes before triggering the handler. */ if (!no_cut) { - printk(KERN_DEFAULT CUT_HERE); + pr_info(CUT_HERE); __warn_printf(fmt, has_args ? regs : NULL); } From 2f5026b8d4fe34601d692ae7f7cd27367e002266 Mon Sep 17 00:00:00 2001 From: Hongfu Li Date: Wed, 13 May 2026 10:58:38 +0800 Subject: [PATCH 042/108] selftests/perf_events: fix mmap() error check in sigtrap_threads In sigtrap_threads(), the return value of mmap() is checked against NULL. mmap() returns MAP_FAILED, which is (void *)-1, not NULL, when it fails. Since MAP_FAILED is non-zero and non-NULL, the condition "p == NULL" will never be true on failure, causing the program to proceed with an invalid pointer and segfault if mmap() actually fails under memory pressure. Link: https://lore.kernel.org/20260513025838.594945-1-lihongfu@kylinos.cn Signed-off-by: Hongfu Li Reviewed-by: Andrew Morton Cc: Mickael Salaun Cc: SeongJae Park Cc: Shuah Khan Cc: Wei Yang Cc: Kyle Huey Cc: Ingo Molnar Signed-off-by: Andrew Morton --- tools/testing/selftests/perf_events/watermark_signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/perf_events/watermark_signal.c b/tools/testing/selftests/perf_events/watermark_signal.c index 0f64b9b17081..a84709cabd8b 100644 --- a/tools/testing/selftests/perf_events/watermark_signal.c +++ b/tools/testing/selftests/perf_events/watermark_signal.c @@ -102,7 +102,7 @@ TEST(watermark_signal) } p = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (p == NULL) { + if (p == MAP_FAILED) { perror("mmap"); goto cleanup; } From dcc8a57815534e3844f342b28ecfb6e626a816a0 Mon Sep 17 00:00:00 2001 From: Shuvam Pandey Date: Mon, 16 Mar 2026 15:57:27 +0545 Subject: [PATCH 043/108] lib/tests: extend cmdline KUnit with next_arg() tests The cmdline KUnit suite covers get_option() and get_options(), but it does not exercise next_arg(). Extend the suite with one test for a quoted value containing spaces and one regression test for a bare quote token after a normal parameter. The regression test covers the bare quote token path fixed by commit 9847f21225c4 ("lib/cmdline: avoid page fault in next_arg"). [shuvampandey1@gmail.com: extend cmdline next_arg() coverage with mixed tokens] Link: https://lore.kernel.org/20260316211249.88601-1-shuvampandey1@gmail.com Link: https://lore.kernel.org/20260316101227.15807-1-shuvampandey1@gmail.com Signed-off-by: Shuvam Pandey Reviewed-by: Andy Shevchenko Cc: Neel Natu Cc: Dmitry Antipov Cc: "Darrick J. Wong" Cc: Kees Cook Signed-off-by: Andrew Morton --- lib/tests/cmdline_kunit.c | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/lib/tests/cmdline_kunit.c b/lib/tests/cmdline_kunit.c index c1602f797637..af44ae9e92c3 100644 --- a/lib/tests/cmdline_kunit.c +++ b/lib/tests/cmdline_kunit.c @@ -139,11 +139,73 @@ static void cmdline_test_range(struct kunit *test) } while (++i < ARRAY_SIZE(cmdline_test_range_strings)); } +static void cmdline_test_next_arg_quoted_value(struct kunit *test) +{ + char in[] = "foo=\"bar baz\" qux=1"; + char *next, *param, *val; + + next = next_arg(in, ¶m, &val); + KUNIT_EXPECT_STREQ(test, param, "foo"); + KUNIT_ASSERT_NOT_NULL(test, val); + KUNIT_EXPECT_STREQ(test, val, "bar baz"); + KUNIT_EXPECT_STREQ(test, next, "qux=1"); + + next = next_arg(next, ¶m, &val); + KUNIT_EXPECT_STREQ(test, param, "qux"); + KUNIT_ASSERT_NOT_NULL(test, val); + KUNIT_EXPECT_STREQ(test, val, "1"); + KUNIT_EXPECT_STREQ(test, next, ""); +} + +static void cmdline_test_next_arg_bare_quote_regression(struct kunit *test) +{ + char in[] = "foo=bar \""; + char *next, *param, *val; + + next = next_arg(in, ¶m, &val); + KUNIT_EXPECT_STREQ(test, param, "foo"); + KUNIT_ASSERT_NOT_NULL(test, val); + KUNIT_EXPECT_STREQ(test, val, "bar"); + KUNIT_EXPECT_STREQ(test, next, "\""); + + /* This hits the i == 0 quoted-token case fixed by 9847f21225c4. */ + next = next_arg(next, ¶m, &val); + KUNIT_EXPECT_STREQ(test, param, ""); + KUNIT_EXPECT_PTR_EQ(test, val, NULL); + KUNIT_EXPECT_STREQ(test, next, ""); +} + +static void cmdline_test_next_arg_mixed_tokens(struct kunit *test) +{ + char in[] = "bbb= jjj kkk=\"a=b\""; + char *next, *param, *val; + + next = next_arg(in, ¶m, &val); + KUNIT_EXPECT_STREQ(test, param, "bbb"); + KUNIT_ASSERT_NOT_NULL(test, val); + KUNIT_EXPECT_STREQ(test, val, ""); + KUNIT_EXPECT_STREQ(test, next, "jjj kkk=\"a=b\""); + + next = next_arg(next, ¶m, &val); + KUNIT_EXPECT_STREQ(test, param, "jjj"); + KUNIT_EXPECT_NULL(test, val); + KUNIT_EXPECT_STREQ(test, next, "kkk=\"a=b\""); + + next = next_arg(next, ¶m, &val); + KUNIT_EXPECT_STREQ(test, param, "kkk"); + KUNIT_ASSERT_NOT_NULL(test, val); + KUNIT_EXPECT_STREQ(test, val, "a=b"); + KUNIT_EXPECT_STREQ(test, next, ""); +} + static struct kunit_case cmdline_test_cases[] = { KUNIT_CASE(cmdline_test_noint), KUNIT_CASE(cmdline_test_lead_int), KUNIT_CASE(cmdline_test_tail_int), KUNIT_CASE(cmdline_test_range), + KUNIT_CASE(cmdline_test_next_arg_quoted_value), + KUNIT_CASE(cmdline_test_next_arg_bare_quote_regression), + KUNIT_CASE(cmdline_test_next_arg_mixed_tokens), {} }; From 6e30111dbb4075e3c26c230417b32bc4a1c66831 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:52 +0300 Subject: [PATCH 044/108] lib: fix _parse_integer_limit() to handle overflow Patch series "lib and lib/cmdline enhancements", v11. This series is a merge of the recently posted [1] and [2]. The first one is intended to adjust '_parse_integer_limit()' and 'memparse()' to not ignore overflows, extend string to 64-bit integer conversion tests, add KUnit-based test for 'memparse()' and fix kernel-doc glitches found in lib/cmdline.c. The second one was originated from RISCV-specific build fixes needed to integrate the former and now aims to provide platform-specific double-word shifts and corresponding KUnit test. Getting feedback from RISCV core maintainers would be very helpful. Special thanks to Andy Shevchenko, Charlie Jenkins, and Andrew Morton. This patch (of 8): In '_parse_integer_limit()', adjust native integer arithmetic with near-to-overflow branch where 'check_mul_overflow()' and 'check_add_overflow()' are used to check whether an intermediate result goes out of range, and denote such a case with ULLONG_MAX, thus making the function more similar to standard C library's 'strtoull()'. Adjust comment to kernel-doc style as well. Link: https://lore.kernel.org/20260519172259.908980-1-dmantipov@yandex.ru Link: https://lore.kernel.org/20260519172259.908980-2-dmantipov@yandex.ru Link: https://lore.kernel.org/linux-riscv/20260403103338.1122415-1-dmantipov@yandex.ru [1] Link: https://lore.kernel.org/linux-riscv/20260427090105.705529-1-dmantipov@yandex.ru [2] Signed-off-by: Dmitry Antipov Reviewed-by: Andy Shevchenko Cc: Albert Ou Cc: Alexandre Ghiti Cc: Andriy Shevchenko Cc: Ard Biesheuvel Cc: Palmer Dabbelt Cc: Charlie Jenkins Signed-off-by: Andrew Morton --- lib/kstrtox.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/lib/kstrtox.c b/lib/kstrtox.c index 97be2a39f537..edc4eb7c1bca 100644 --- a/lib/kstrtox.c +++ b/lib/kstrtox.c @@ -39,25 +39,30 @@ const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) return s; } -/* - * Convert non-negative integer string representation in explicitly given radix - * to an integer. A maximum of max_chars characters will be converted. +/** + * _parse_integer_limit - Convert integer string representation to an integer + * @s: Integer string representation + * @base: Radix + * @p: Where to store result + * @max_chars: Maximum amount of characters to convert * - * Return number of characters consumed maybe or-ed with overflow bit. - * If overflow occurs, result integer (incorrect) is still returned. + * Convert non-negative integer string representation in explicitly given + * radix to an integer. If overflow occurs, value at @p is set to ULLONG_MAX. * - * Don't you dare use this function. + * This function is the workhorse of other string conversion functions and it + * is discouraged to use it explicitly. Consider kstrto*() family instead. + * + * Return: Number of characters consumed, maybe ORed with overflow bit */ noinline unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned long long *p, size_t max_chars) { + unsigned int rv, overflow = 0; unsigned long long res; - unsigned int rv; res = 0; - rv = 0; - while (max_chars--) { + for (rv = 0; rv < max_chars; rv++, s++) { unsigned int c = *s; unsigned int lc = _tolower(c); unsigned int val; @@ -76,15 +81,17 @@ unsigned int _parse_integer_limit(const char *s, unsigned int base, unsigned lon * it in the max base we support (16) */ if (unlikely(res & (~0ull << 60))) { - if (res > div_u64(ULLONG_MAX - val, base)) - rv |= KSTRTOX_OVERFLOW; + if (check_mul_overflow(res, base, &res) || + check_add_overflow(res, val, &res)) { + res = ULLONG_MAX; + overflow = KSTRTOX_OVERFLOW; + } + } else { + res = res * base + val; } - res = res * base + val; - rv++; - s++; } *p = res; - return rv; + return rv | overflow; } noinline From 9a4580db6e9f83428813f671a79486469684069f Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:53 +0300 Subject: [PATCH 045/108] lib: fix memparse() to handle overflow Since '_parse_integer_limit()' (and so 'simple_strtoull()') is now capable to handle overflow, adjust 'memparse()' to handle overflow (denoted by ULLONG_MAX) returned from 'simple_strtoull()'. Also use 'check_shl_overflow()' to catch an overflow possibly caused by processing size suffix and denote it with ULLONG_MAX as well. Link: https://lore.kernel.org/20260519172259.908980-3-dmantipov@yandex.ru Signed-off-by: Dmitry Antipov Reviewed-by: Andy Shevchenko Cc: Albert Ou Cc: Alexandre Ghiti Cc: Ard Biesheuvel Cc: Charlie Jenkins Cc: Palmer Dabbelt Signed-off-by: Andrew Morton --- lib/cmdline.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/cmdline.c b/lib/cmdline.c index 90ed997d9570..f6e4b113ca9f 100644 --- a/lib/cmdline.c +++ b/lib/cmdline.c @@ -150,39 +150,46 @@ EXPORT_SYMBOL(get_options); unsigned long long memparse(const char *ptr, char **retptr) { char *endptr; /* local pointer to end of parsed string */ - unsigned long long ret = simple_strtoull(ptr, &endptr, 0); + unsigned int shl = 0; + /* Consume valid suffix even in case of overflow. */ switch (*endptr) { case 'E': case 'e': - ret <<= 10; + shl += 10; fallthrough; case 'P': case 'p': - ret <<= 10; + shl += 10; fallthrough; case 'T': case 't': - ret <<= 10; + shl += 10; fallthrough; case 'G': case 'g': - ret <<= 10; + shl += 10; fallthrough; case 'M': case 'm': - ret <<= 10; + shl += 10; fallthrough; case 'K': case 'k': - ret <<= 10; - endptr++; + shl += 10; fallthrough; default: break; } + if (shl && likely(ptr != endptr)) { + /* Have valid suffix with preceding number. */ + if (unlikely(check_shl_overflow(ret, shl, &ret))) + ret = ULLONG_MAX; + endptr++; + } + if (retptr) *retptr = endptr; From ab90fc1007245380988c500ddf7eff6da1b10056 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:54 +0300 Subject: [PATCH 046/108] lib: add more string to 64-bit integer conversion overflow tests Add a few more string to 64-bit integer conversion tests to check whether 'kstrtoull()', 'kstrtoll()', 'kstrtou64()' and 'kstrtos64()' can handle overflows reported by '_parse_integer_limit()'. Link: https://lore.kernel.org/20260519172259.908980-4-dmantipov@yandex.ru Signed-off-by: Dmitry Antipov Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Cc: Albert Ou Cc: Alexandre Ghiti Cc: Ard Biesheuvel Cc: Charlie Jenkins Cc: Palmer Dabbelt Signed-off-by: Andrew Morton --- lib/test-kstrtox.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/test-kstrtox.c b/lib/test-kstrtox.c index ee87fef66cb5..811128d0df16 100644 --- a/lib/test-kstrtox.c +++ b/lib/test-kstrtox.c @@ -198,6 +198,7 @@ static void __init test_kstrtoull_fail(void) {"10000000000000000000000000000000000000000000000000000000000000000", 2}, {"2000000000000000000000", 8}, {"18446744073709551616", 10}, + {"569202370375329612767", 10}, {"10000000000000000", 16}, /* negative */ {"-0", 0}, @@ -275,9 +276,11 @@ static void __init test_kstrtoll_fail(void) {"9223372036854775809", 10}, {"18446744073709551614", 10}, {"18446744073709551615", 10}, + {"569202370375329612767", 10}, {"-9223372036854775809", 10}, {"-18446744073709551614", 10}, {"-18446744073709551615", 10}, + {"-569202370375329612767", 10}, /* sign is first character if any */ {"-+1", 0}, {"-+1", 8}, @@ -334,6 +337,7 @@ static void __init test_kstrtou64_fail(void) {"-1", 10}, {"18446744073709551616", 10}, {"18446744073709551617", 10}, + {"569202370375329612767", 10}, }; TEST_FAIL(kstrtou64, u64, "%llu", test_u64_fail); } @@ -386,6 +390,8 @@ static void __init test_kstrtos64_fail(void) {"18446744073709551615", 10}, {"18446744073709551616", 10}, {"18446744073709551617", 10}, + {"569202370375329612767", 10}, + {"-569202370375329612767", 10}, }; TEST_FAIL(kstrtos64, s64, "%lld", test_s64_fail); } From a535853b664988aba11771deed44673765df581e Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:55 +0300 Subject: [PATCH 047/108] lib/cmdline_kunit: add test case for memparse() Better late than never, now there is a long-awaited basic test for 'memparse()' which is provided by cmdline.c. Link: https://lore.kernel.org/20260519172259.908980-5-dmantipov@yandex.ru Signed-off-by: Dmitry Antipov Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Cc: Albert Ou Cc: Alexandre Ghiti Cc: Ard Biesheuvel Cc: Charlie Jenkins Cc: Palmer Dabbelt Signed-off-by: Andrew Morton --- lib/tests/cmdline_kunit.c | 56 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/lib/tests/cmdline_kunit.c b/lib/tests/cmdline_kunit.c index af44ae9e92c3..3f61ff8d3178 100644 --- a/lib/tests/cmdline_kunit.c +++ b/lib/tests/cmdline_kunit.c @@ -6,6 +6,7 @@ #include #include #include +#include #include static const char *cmdline_test_strings[] = { @@ -198,6 +199,60 @@ static void cmdline_test_next_arg_mixed_tokens(struct kunit *test) KUNIT_EXPECT_STREQ(test, next, ""); } +struct cmdline_test_memparse_entry { + const char *input; + const char *unrecognized; + unsigned long long result; +}; + +static const struct cmdline_test_memparse_entry testdata[] = { + { "0", "", 0ULL }, + { "1", "", 1ULL }, + { "a", "a", 0ULL }, + { "k", "k", 0ULL }, + { "E", "E", 0ULL }, + { "0xb", "", 11ULL }, + { "0xz", "x", 0ULL }, + { "1234", "", 1234ULL }, + { "04567", "", 2423ULL }, + { "0x9876", "", 39030LL }, + { "05678", "8", 375ULL }, + { "0xabcdefz", "z", 11259375ULL }, + { "0cdba", "c", 0ULL }, + { "4K", "", SZ_4K }, + { "0x10k@0xaaaabbbb", "@", SZ_16K }, + { "32M", "", SZ_32M }, + { "067m:foo", ":", 55 * SZ_1M }, + { "2G;bar=baz", ";", SZ_2G }, + { "07gz", "z", 7ULL * SZ_1G }, + { "3T+data", "+", 3 * SZ_1T }, + { "04t,ro", ",", SZ_4T }, + { "012p", "", 11258999068426240ULL }, + { "7P,sync", ",", 7881299347898368ULL }, + { "0x2e", "", 46ULL }, + { "2E and more", " ", 2305843009213693952ULL }, + { "18446744073709551615", "", ULLONG_MAX }, + { "0xffffffffffffffff0", "", ULLONG_MAX }, + { "1111111111111111111T", "", ULLONG_MAX }, + { "222222222222222222222G", "", ULLONG_MAX }, + { "3333333333333333333333M", "", ULLONG_MAX }, +}; + +static void cmdline_test_memparse(struct kunit *test) +{ + const struct cmdline_test_memparse_entry *e; + unsigned long long ret; + char *retptr; + + for (e = testdata; e < testdata + ARRAY_SIZE(testdata); e++) { + ret = memparse(e->input, &retptr); + KUNIT_EXPECT_EQ_MSG(test, ret, e->result, + " when parsing '%s'", e->input); + KUNIT_EXPECT_EQ_MSG(test, *retptr, *e->unrecognized, + " when parsing '%s'", e->input); + } +} + static struct kunit_case cmdline_test_cases[] = { KUNIT_CASE(cmdline_test_noint), KUNIT_CASE(cmdline_test_lead_int), @@ -206,6 +261,7 @@ static struct kunit_case cmdline_test_cases[] = { KUNIT_CASE(cmdline_test_next_arg_quoted_value), KUNIT_CASE(cmdline_test_next_arg_bare_quote_regression), KUNIT_CASE(cmdline_test_next_arg_mixed_tokens), + KUNIT_CASE(cmdline_test_memparse), {} }; From 117d2bfa0ab1bec88d600c50884000334f034338 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:56 +0300 Subject: [PATCH 048/108] lib/cmdline: adjust a few comments to fix kernel-doc -Wreturn warnings Fix 'get_option()', 'memparse()' and 'parse_option_str()' comments to match the commonly used style as suggested by kernel-doc -Wreturn. Link: https://lore.kernel.org/20260519172259.908980-6-dmantipov@yandex.ru Signed-off-by: Dmitry Antipov Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Cc: Albert Ou Cc: Alexandre Ghiti Cc: Ard Biesheuvel Cc: Charlie Jenkins Cc: Palmer Dabbelt Signed-off-by: Andrew Morton --- lib/cmdline.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/cmdline.c b/lib/cmdline.c index f6e4b113ca9f..16cce6621cec 100644 --- a/lib/cmdline.c +++ b/lib/cmdline.c @@ -43,7 +43,7 @@ static int get_range(char **str, int *pint, int n) * When @pint is NULL the function can be used as a validator of * the current option in the string. * - * Return values: + * Return: * 0 - no int in string * 1 - int found, no subsequent comma * 2 - int found including a subsequent comma @@ -145,6 +145,9 @@ EXPORT_SYMBOL(get_options); * * Parses a string into a number. The number stored at @ptr is * potentially suffixed with K, M, G, T, P, E. + * + * Return: The value as recognized by simple_strtoull() multiplied + * by the value as specified by suffix, if any. */ unsigned long long memparse(const char *ptr, char **retptr) @@ -205,7 +208,7 @@ EXPORT_SYMBOL(memparse); * This function parses a string containing a comma-separated list of * strings like a=b,c. * - * Return true if there's such option in the string, or return false. + * Return: True if there's such option in the string or false otherwise. */ bool parse_option_str(const char *str, const char *option) { From a354b8de9ad607c0a11419a402df46b46503f921 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:57 +0300 Subject: [PATCH 049/108] riscv: add platform-specific double word shifts for riscv32 Add riscv32-specific '__ashldi3()', '__ashrdi3()', and '__lshrdi3()'. Initially it was intended to fix the following link error observed when building EFI-enabled kernel with CONFIG_EFI_STUB=y and CONFIG_EFI_GENERIC_STUB=y: riscv32-linux-gnu-ld: ./drivers/firmware/efi/libstub/lib-cmdline.stub.o: in function `__efistub_.L49': __efistub_cmdline.c:(.init.text+0x1f2): undefined reference to `__efistub___ashldi3' riscv32-linux-gnu-ld: __efistub_cmdline.c:(.init.text+0x202): undefined reference to `__efistub___lshrdi3' Reported at [1] trying to build https://patchew.org/linux/20260212164413.889625-1-dmantipov@yandex.ru, tested with 'qemu-system-riscv32 -M virt' only. Link: https://lore.kernel.org/20260519172259.908980-7-dmantipov@yandex.ru Signed-off-by: Dmitry Antipov Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603041925.KLKqpK6N-lkp@intel.com [1] Suggested-by: Ard Biesheuvel Tested-by: Charlie Jenkins Assisted-by: Gemini:gemini-3.1-pro-preview sashiko Cc: Albert Ou Cc: Alexandre Ghiti Cc: Andriy Shevchenko Cc: Palmer Dabbelt Signed-off-by: Andrew Morton --- arch/riscv/Kconfig | 3 --- arch/riscv/include/asm/asm-prototypes.h | 4 +++ arch/riscv/kernel/image-vars.h | 9 +++++++ arch/riscv/lib/Makefile | 1 + arch/riscv/lib/ashldi3.S | 36 +++++++++++++++++++++++++ arch/riscv/lib/ashrdi3.S | 36 +++++++++++++++++++++++++ arch/riscv/lib/lshrdi3.S | 36 +++++++++++++++++++++++++ 7 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 arch/riscv/lib/ashldi3.S create mode 100644 arch/riscv/lib/ashrdi3.S create mode 100644 arch/riscv/lib/lshrdi3.S diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index c5754942cf85..0d10b299bad8 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -404,9 +404,6 @@ config ARCH_RV32I bool "RV32I" depends on NONPORTABLE select 32BIT - select GENERIC_LIB_ASHLDI3 - select GENERIC_LIB_ASHRDI3 - select GENERIC_LIB_LSHRDI3 select GENERIC_LIB_UCMPDI2 config ARCH_RV64I diff --git a/arch/riscv/include/asm/asm-prototypes.h b/arch/riscv/include/asm/asm-prototypes.h index 5b90ba5314ee..a0ca9efff267 100644 --- a/arch/riscv/include/asm/asm-prototypes.h +++ b/arch/riscv/include/asm/asm-prototypes.h @@ -5,6 +5,10 @@ #include #include +long long __lshrdi3(long long a, int b); +long long __ashrdi3(long long a, int b); +long long __ashldi3(long long a, int b); + long long __lshrti3(long long a, int b); long long __ashrti3(long long a, int b); long long __ashlti3(long long a, int b); diff --git a/arch/riscv/kernel/image-vars.h b/arch/riscv/kernel/image-vars.h index 3bd9d06a8b8f..7b44b94f1283 100644 --- a/arch/riscv/kernel/image-vars.h +++ b/arch/riscv/kernel/image-vars.h @@ -32,6 +32,15 @@ __efistub___init_text_end = __init_text_end; __efistub_sysfb_primary_display = sysfb_primary_display; #endif +/* + * These double-word integer shifts are used by the library code, and + * the first two of them are required to link EFI stub. Note __ashrdi3() + * is not actually used by the stub but this may change in the future. + */ +PROVIDE(__efistub___lshrdi3 = __lshrdi3); +PROVIDE(__efistub___ashldi3 = __ashldi3); +PROVIDE(__efistub___ashrdi3 = __ashrdi3); + #endif #endif /* __RISCV_KERNEL_IMAGE_VARS_H */ diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index 6f767b2a349d..f668b98970bd 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -16,6 +16,7 @@ ifeq ($(CONFIG_MMU), y) lib-$(CONFIG_RISCV_ISA_V) += uaccess_vector.o endif lib-$(CONFIG_MMU) += uaccess.o +lib-$(CONFIG_32BIT) += ashldi3.o ashrdi3.o lshrdi3.o lib-$(CONFIG_64BIT) += tishift.o lib-$(CONFIG_RISCV_ISA_ZICBOZ) += clear_page.o obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o diff --git a/arch/riscv/lib/ashldi3.S b/arch/riscv/lib/ashldi3.S new file mode 100644 index 000000000000..c3408862e2f6 --- /dev/null +++ b/arch/riscv/lib/ashldi3.S @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/** + * Adopted for the Linux kernel from IPXE project, see + * https://github.com/ipxe/ipxe/blob/master/src/arch/riscv32/libgcc/llshift.S + */ + +#include +#include + +/** + * Shift left + * + * @v a1:a0 Value to shift + * @v a2 Shift amount + * @ret a1:a0 Shifted value + */ + +SYM_FUNC_START(__ashldi3) + + /* Perform shift by 32 bits, if applicable */ + li t0, 32 + sub t1, t0, a2 + bgtz t1, 1f + mv a1, a0 + mv a0, zero +1: /* Perform shift by modulo-32 bits, if applicable */ + andi a2, a2, 0x1f + beqz a2, 2f + srl t2, a0, t1 + sll a0, a0, a2 + sll a1, a1, a2 + or a1, a1, t2 +2: ret + +SYM_FUNC_END(__ashldi3) +EXPORT_SYMBOL(__ashldi3) diff --git a/arch/riscv/lib/ashrdi3.S b/arch/riscv/lib/ashrdi3.S new file mode 100644 index 000000000000..426de0946606 --- /dev/null +++ b/arch/riscv/lib/ashrdi3.S @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/** + * Adopted for the Linux kernel from IPXE project, see + * https://github.com/ipxe/ipxe/blob/master/src/arch/riscv32/libgcc/llshift.S + */ + +#include +#include + +/** + * Arithmetic shift right + * + * @v a1:a0 Value to shift + * @v a2 Shift amount + * @ret a1:a0 Shifted value + */ + +SYM_FUNC_START(__ashrdi3) + + /* Perform shift by 32 bits, if applicable */ + li t0, 32 + sub t1, t0, a2 + bgtz t1, 1f + mv a0, a1 + srai a1, a1, 31 +1: /* Perform shift by modulo-32 bits, if applicable */ + andi a2, a2, 0x1f + beqz a2, 2f + sll t2, a1, t1 + sra a1, a1, a2 + srl a0, a0, a2 + or a0, a0, t2 +2: ret + +SYM_FUNC_END(__ashrdi3) +EXPORT_SYMBOL(__ashrdi3) diff --git a/arch/riscv/lib/lshrdi3.S b/arch/riscv/lib/lshrdi3.S new file mode 100644 index 000000000000..1af03985ccb7 --- /dev/null +++ b/arch/riscv/lib/lshrdi3.S @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/** + * Adopted for the Linux kernel from IPXE project, see + * https://github.com/ipxe/ipxe/blob/master/src/arch/riscv32/libgcc/llshift.S + */ + +#include +#include + +/** + * Logical shift right + * + * @v a1:a0 Value to shift + * @v a2 Shift amount + * @ret a1:a0 Shifted value + */ + +SYM_FUNC_START(__lshrdi3) + + /* Perform shift by 32 bits, if applicable */ + li t0, 32 + sub t1, t0, a2 + bgtz t1, 1f + mv a0, a1 + mv a1, zero +1: /* Perform shift by modulo-32 bits, if applicable */ + andi a2, a2, 0x1f + beqz a2, 2f + sll t2, a1, t1 + srl a1, a1, a2 + srl a0, a0, a2 + or a0, a0, t2 +2: ret + +SYM_FUNC_END(__lshrdi3) +EXPORT_SYMBOL(__lshrdi3) From 6bba2ae43e8f379d3bed4c3eb258ea6d81baae80 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:58 +0300 Subject: [PATCH 050/108] lib: kunit: add tests for __ashldi3(), __ashrdi3(), and __lshrdi3() Add KUnit tests for '__ashldi3()', '__ashrdi3()', and '__lshrdi3()' helper functions used to implement 64-bit arithmetic shift left, arithmetic shift right and logical shift right, respectively, on a 32-bit CPUs. Tested with 'qemu-system-riscv32 -M virt' and 'qemu-system-arm -M virt'. Link: https://lore.kernel.org/20260519172259.908980-8-dmantipov@yandex.ru Signed-off-by: Dmitry Antipov Reviewed-by: Andy Shevchenko Tested-by: Charlie Jenkins Assisted-by: Gemini:gemini-3.1-pro-preview sashiko Cc: Albert Ou Cc: Alexandre Ghiti Cc: Ard Biesheuvel Cc: Palmer Dabbelt Signed-off-by: Andrew Morton --- lib/Kconfig.debug | 10 +++ lib/tests/Makefile | 1 + lib/tests/shdi3_kunit.c | 175 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 lib/tests/shdi3_kunit.c diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 8ff5adcfe1e0..7ca468c7d81d 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2971,6 +2971,16 @@ config BITS_TEST If unsure, say N. +config SHDI3_KUNIT_TEST + tristate "KUnit test for __ashldi3(), __ashrdi3(), and __lshrdi3()" + depends on KUNIT + depends on ARM || XTENSA || MICROBLAZE || ((RISCV || SPARC) && !64BIT) + help + This builds the unit test for __ashldi3(), __ashrdi3(), and + __lshrdi3() helper functions used to implement 64-bit arithmetic + shift left, arithmetic shift right and logical shift right, + respectively, on a 32-bit CPUs. + config SLUB_KUNIT_TEST tristate "KUnit test for SLUB cache error detection" if !KUNIT_ALL_TESTS depends on SLUB_DEBUG && KUNIT diff --git a/lib/tests/Makefile b/lib/tests/Makefile index 7e9c2fa52e35..4ead57602eac 100644 --- a/lib/tests/Makefile +++ b/lib/tests/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_BASE64_KUNIT) += base64_kunit.o obj-$(CONFIG_BITOPS_KUNIT) += bitops_kunit.o obj-$(CONFIG_BITFIELD_KUNIT) += bitfield_kunit.o obj-$(CONFIG_BITS_TEST) += test_bits.o +obj-$(CONFIG_SHDI3_KUNIT_TEST) += shdi3_kunit.o obj-$(CONFIG_BLACKHOLE_DEV_KUNIT_TEST) += blackhole_dev_kunit.o obj-$(CONFIG_CHECKSUM_KUNIT) += checksum_kunit.o obj-$(CONFIG_CMDLINE_KUNIT_TEST) += cmdline_kunit.o diff --git a/lib/tests/shdi3_kunit.c b/lib/tests/shdi3_kunit.c new file mode 100644 index 000000000000..44f65e66512b --- /dev/null +++ b/lib/tests/shdi3_kunit.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR Apache-2.0 +/* + * Test cases for __ashldi3(), __ashrdi3(), and __lshrdi3(). + */ + +#include +#include +#include +#include + +struct shdi3_test_entry { + long long input; + int shift; + long long result; +}; + +static const struct shdi3_test_entry ashldi3_testdata[] = { + /* https://github.com/llvm/llvm-project/compiler-rt/test/builtins/Unit/ashldi3_test.c */ + { 0x0123456789ABCDEFLL, 0, 0x123456789ABCDEFLL }, + { 0x0123456789ABCDEFLL, 1, 0x2468ACF13579BDELL }, + { 0x0123456789ABCDEFLL, 2, 0x48D159E26AF37BCLL }, + { 0x0123456789ABCDEFLL, 3, 0x91A2B3C4D5E6F78LL }, + { 0x0123456789ABCDEFLL, 4, 0x123456789ABCDEF0LL }, + { 0x0123456789ABCDEFLL, 28, 0x789ABCDEF0000000LL }, + { 0x0123456789ABCDEFLL, 29, 0xF13579BDE0000000LL }, + { 0x0123456789ABCDEFLL, 30, 0xE26AF37BC0000000LL }, + { 0x0123456789ABCDEFLL, 31, 0xC4D5E6F780000000LL }, + { 0x0123456789ABCDEFLL, 32, 0x89ABCDEF00000000LL }, + { 0x0123456789ABCDEFLL, 33, 0x13579BDE00000000LL }, + { 0x0123456789ABCDEFLL, 34, 0x26AF37BC00000000LL }, + { 0x0123456789ABCDEFLL, 35, 0x4D5E6F7800000000LL }, + { 0x0123456789ABCDEFLL, 36, 0x9ABCDEF000000000LL }, + { 0x0123456789ABCDEFLL, 60, 0xF000000000000000LL }, + { 0x0123456789ABCDEFLL, 61, 0xE000000000000000LL }, + { 0x0123456789ABCDEFLL, 62, 0xC000000000000000LL }, + { 0x0123456789ABCDEFLL, 63, 0x8000000000000000LL }, +}; + +static void shdi3_test_ashldi3(struct kunit *test) +{ + const struct shdi3_test_entry *e; + long long ret; + + for (e = ashldi3_testdata; + e < ashldi3_testdata + ARRAY_SIZE(ashldi3_testdata); e++) { + ret = __ashldi3(e->input, e->shift); + KUNIT_EXPECT_EQ_MSG(test, ret, e->result, + " when evaluating __ashldi3(%lld, %d)", + e->input, e->shift); + } +} + +static const struct shdi3_test_entry ashrdi3_testdata[] = { + /* https://github.com/llvm/llvm-project/compiler-rt/test/builtins/Unit/ashrdi3_test.c */ + { 0x0123456789ABCDEFLL, 0, 0x123456789ABCDEFLL }, + { 0x0123456789ABCDEFLL, 1, 0x91A2B3C4D5E6F7LL }, + { 0x0123456789ABCDEFLL, 2, 0x48D159E26AF37BLL }, + { 0x0123456789ABCDEFLL, 3, 0x2468ACF13579BDLL }, + { 0x0123456789ABCDEFLL, 4, 0x123456789ABCDELL }, + { 0x0123456789ABCDEFLL, 28, 0x12345678LL }, + { 0x0123456789ABCDEFLL, 29, 0x91A2B3CLL }, + { 0x0123456789ABCDEFLL, 30, 0x48D159ELL }, + { 0x0123456789ABCDEFLL, 31, 0x2468ACFLL }, + { 0x0123456789ABCDEFLL, 32, 0x1234567LL }, + { 0x0123456789ABCDEFLL, 33, 0x91A2B3LL }, + { 0x0123456789ABCDEFLL, 34, 0x48D159LL }, + { 0x0123456789ABCDEFLL, 35, 0x2468ACLL }, + { 0x0123456789ABCDEFLL, 36, 0x123456LL }, + { 0x0123456789ABCDEFLL, 60, 0 }, + { 0x0123456789ABCDEFLL, 61, 0 }, + { 0x0123456789ABCDEFLL, 62, 0 }, + { 0x0123456789ABCDEFLL, 63, 0 }, + { 0xFEDCBA9876543210LL, 0, 0xFEDCBA9876543210LL }, + { 0xFEDCBA9876543210LL, 1, 0xFF6E5D4C3B2A1908LL }, + { 0xFEDCBA9876543210LL, 2, 0xFFB72EA61D950C84LL }, + { 0xFEDCBA9876543210LL, 3, 0xFFDB97530ECA8642LL }, + { 0xFEDCBA9876543210LL, 4, 0xFFEDCBA987654321LL }, + { 0xFEDCBA9876543210LL, 28, 0xFFFFFFFFEDCBA987LL }, + { 0xFEDCBA9876543210LL, 29, 0xFFFFFFFFF6E5D4C3LL }, + { 0xFEDCBA9876543210LL, 30, 0xFFFFFFFFFB72EA61LL }, + { 0xFEDCBA9876543210LL, 31, 0xFFFFFFFFFDB97530LL }, + { 0xFEDCBA9876543210LL, 32, 0xFFFFFFFFFEDCBA98LL }, + { 0xFEDCBA9876543210LL, 33, 0xFFFFFFFFFF6E5D4CLL }, + { 0xFEDCBA9876543210LL, 34, 0xFFFFFFFFFFB72EA6LL }, + { 0xFEDCBA9876543210LL, 35, 0xFFFFFFFFFFDB9753LL }, + { 0xFEDCBA9876543210LL, 36, 0xFFFFFFFFFFEDCBA9LL }, + { 0xAEDCBA9876543210LL, 60, 0xFFFFFFFFFFFFFFFALL }, + { 0xAEDCBA9876543210LL, 61, 0xFFFFFFFFFFFFFFFDLL }, + { 0xAEDCBA9876543210LL, 62, 0xFFFFFFFFFFFFFFFELL }, + { 0xAEDCBA9876543210LL, 63, 0xFFFFFFFFFFFFFFFFLL }, +}; + +static void shdi3_test_ashrdi3(struct kunit *test) +{ + const struct shdi3_test_entry *e; + long long ret; + + for (e = ashrdi3_testdata; + e < ashrdi3_testdata + ARRAY_SIZE(ashrdi3_testdata); e++) { + ret = __ashrdi3(e->input, e->shift); + KUNIT_EXPECT_EQ_MSG(test, ret, e->result, + " when evaluating __ashrdi3(%lld, %d)", + e->input, e->shift); + } +} + +static const struct shdi3_test_entry lshrdi3_testdata[] = { + /* https://github.com/llvm/llvm-project/compiler-rt/test/builtins/Unit/lshrdi3_test.c */ + { 0x0123456789ABCDEFLL, 0, 0x123456789ABCDEFLL }, + { 0x0123456789ABCDEFLL, 1, 0x91A2B3C4D5E6F7LL }, + { 0x0123456789ABCDEFLL, 2, 0x48D159E26AF37BLL }, + { 0x0123456789ABCDEFLL, 3, 0x2468ACF13579BDLL }, + { 0x0123456789ABCDEFLL, 4, 0x123456789ABCDELL }, + { 0x0123456789ABCDEFLL, 28, 0x12345678LL }, + { 0x0123456789ABCDEFLL, 29, 0x91A2B3CLL }, + { 0x0123456789ABCDEFLL, 30, 0x48D159ELL }, + { 0x0123456789ABCDEFLL, 31, 0x2468ACFLL }, + { 0x0123456789ABCDEFLL, 32, 0x1234567LL }, + { 0x0123456789ABCDEFLL, 33, 0x91A2B3LL }, + { 0x0123456789ABCDEFLL, 34, 0x48D159LL }, + { 0x0123456789ABCDEFLL, 35, 0x2468ACLL }, + { 0x0123456789ABCDEFLL, 36, 0x123456LL }, + { 0x0123456789ABCDEFLL, 60, 0 }, + { 0x0123456789ABCDEFLL, 61, 0 }, + { 0x0123456789ABCDEFLL, 62, 0 }, + { 0x0123456789ABCDEFLL, 63, 0 }, + { 0xFEDCBA9876543210LL, 0, 0xFEDCBA9876543210LL }, + { 0xFEDCBA9876543210LL, 1, 0x7F6E5D4C3B2A1908LL }, + { 0xFEDCBA9876543210LL, 2, 0x3FB72EA61D950C84LL }, + { 0xFEDCBA9876543210LL, 3, 0x1FDB97530ECA8642LL }, + { 0xFEDCBA9876543210LL, 4, 0xFEDCBA987654321LL }, + { 0xFEDCBA9876543210LL, 28, 0xFEDCBA987LL }, + { 0xFEDCBA9876543210LL, 29, 0x7F6E5D4C3LL }, + { 0xFEDCBA9876543210LL, 30, 0x3FB72EA61LL }, + { 0xFEDCBA9876543210LL, 31, 0x1FDB97530LL }, + { 0xFEDCBA9876543210LL, 32, 0xFEDCBA98LL }, + { 0xFEDCBA9876543210LL, 33, 0x7F6E5D4CLL }, + { 0xFEDCBA9876543210LL, 34, 0x3FB72EA6LL }, + { 0xFEDCBA9876543210LL, 35, 0x1FDB9753LL }, + { 0xFEDCBA9876543210LL, 36, 0xFEDCBA9LL }, + { 0xAEDCBA9876543210LL, 60, 0xALL }, + { 0xAEDCBA9876543210LL, 61, 0x5LL }, + { 0xAEDCBA9876543210LL, 62, 0x2LL }, + { 0xAEDCBA9876543210LL, 63, 0x1LL }, +}; + +static void shdi3_test_lshrdi3(struct kunit *test) +{ + const struct shdi3_test_entry *e; + long long ret; + + for (e = lshrdi3_testdata; + e < lshrdi3_testdata + ARRAY_SIZE(lshrdi3_testdata); e++) { + ret = __lshrdi3(e->input, e->shift); + KUNIT_EXPECT_EQ_MSG(test, ret, e->result, + " when evaluating __lshrdi3(%lld, %d)", + e->input, e->shift); + } +} + +static struct kunit_case shdi3_test_cases[] = { + KUNIT_CASE(shdi3_test_ashldi3), + KUNIT_CASE(shdi3_test_ashrdi3), + KUNIT_CASE(shdi3_test_lshrdi3), + {} +}; + +static struct kunit_suite shdi3_test_suite = { + .name = "shdi3", + .test_cases = shdi3_test_cases, +}; +kunit_test_suite(shdi3_test_suite); + +MODULE_DESCRIPTION("Test cases for __ashldi3(), __ashrdi3(), and __lshrdi3()"); +MODULE_LICENSE("GPL"); From 442082297e3d38f3a59754ff56fc07d72a93fdbd Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Tue, 19 May 2026 20:22:59 +0300 Subject: [PATCH 051/108] riscv: fix building compressed EFI image When building vmlinuz.efi with CONFIG_EFI_ZBOOT enabled, '__lshrdi3()' is also needed to fix yet another link error observed when building riscv32 and loongarch32 images: riscv32-linux-gnu-ld: drivers/firmware/efi/libstub/lib-cmdline.stub.o: in function `__efistub_.L49': __efistub_cmdline.c:(.init.text+0x202): undefined reference to `__efistub___lshrdi3' /usr/bin/loongarch32-linux-gnu-ld: ./drivers/firmware/efi/libstub/lib-cmdline.stub.o: in function `__efistub_.L47': __efistub_cmdline.c:(.init.text+0x26c): undefined reference to `__efistub___lshrdi3' And since both riscv64 and loongarch64 can have CONFIG_EFI_ZBOOT but doesn't need these library routines, rely on CONFIG_32BIT to manage linking of lib-ashldi3.o and lib-lshrdi3.o on 32-bit variants only. [dmantipov@yandex.ru: fix loongarch32] Link: https://lore.kernel.org/8095016e47aceab4830c2523ce78af968ec0497e.camel@yandex.ru Link: https://lore.kernel.org/20260519172259.908980-9-dmantipov@yandex.ru Signed-off-by: Dmitry Antipov Reported-by: Charlie Jenkins Closes: https://lore.kernel.org/linux-riscv/20260409050018.GA371560@inky.localdomain Tested-by: Charlie Jenkins Suggested-by: Ard Biesheuvel Assisted-by: Gemini:gemini-3.1-pro-preview sashiko Tested-by: Charlie Jenkins Cc: Albert Ou Cc: Alexandre Ghiti Cc: Andriy Shevchenko Cc: Palmer Dabbelt Signed-off-by: Andrew Morton --- drivers/firmware/efi/libstub/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index cfedb3025c26..77a2b2d74f3f 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -95,8 +95,10 @@ CFLAGS_zboot-decompress-gzip.o += -I$(srctree)/lib/zlib_inflate zboot-obj-$(CONFIG_KERNEL_ZSTD) := zboot-decompress-zstd.o lib-xxhash.o CFLAGS_zboot-decompress-zstd.o += -I$(srctree)/lib/zstd -zboot-obj-$(CONFIG_RISCV) += lib-clz_ctz.o lib-ashldi3.o -zboot-obj-$(CONFIG_LOONGARCH) += lib-clz_ctz.o lib-ashldi3.o +zboot-riscv-obj-$(CONFIG_32BIT) := lib-ashldi3.o lib-lshrdi3.o +zboot-obj-$(CONFIG_RISCV) += lib-clz_ctz.o $(zboot-riscv-obj-y) +zboot-loongarch-obj-$(CONFIG_32BIT) := lib-ashldi3.o lib-lshrdi3.o +zboot-obj-$(CONFIG_LOONGARCH) += lib-clz_ctz.o $(zboot-loongarch-obj-y) lib-$(CONFIG_EFI_ZBOOT) += zboot.o $(zboot-obj-y) lib-$(CONFIG_UNACCEPTED_MEMORY) += unaccepted_memory.o bitmap.o find.o From 1d9c9493692eccd16b47610f1d21cc7a100199e9 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Tue, 5 May 2026 11:00:46 +0200 Subject: [PATCH 052/108] kcov: allow simultaneous KCOV_ENABLE/KCOV_REMOTE_ENABLE Allow the same userspace thread to simultaneously collect normal coverage in syscall context (KCOV_ENABLE) and remote coverage of asynchronous work created by the thread (KCOV_REMOTE_ENABLE). With this, remote KCOV coverage becomes useful for generic fuzzing and not just fuzzing of specific data injection interfaces. This requires that the task_struct::kcov_* fields are separated into ones that are used by the task that generates coverage, and ones that are used by the task that requested remote coverage. To split this up: - Split task_struct::kcov into kcov and kcov_remote. kcov_task_exit() now has to clean up both separately. - Only use task_struct::kcov_mode on the task that generates coverage. - Only reset task_struct::kcov_handle on the task that requested remote coverage. After this change, fields used by the task that generates coverage are: - kcov_mode - kcov_size - kcov_area - kcov - kcov_sequence - kcov_softirq Fields used by the task that requested remote coverage are: - kcov_remote - kcov_handle [jannh@google.com: remove unused constant KCOV_MODE_REMOTE, per Dmitry] Link: https://lore.kernel.org/20260515-kcov-simultaneous-remote-v2-1-56fde1cfa509@google.com [jannh@google.com: update documentation on remote coverage collection] Link: https://lore.kernel.org/20260519-kcov-docs-v1-1-5bb22f4cb20c@google.com [jannh@google.com: move and reword sentence on simultaneous normal/remote collection Link: https://lore.kernel.org/20260520-kcov-docs-v2-1-819f78778763@google.com Link: https://lore.kernel.org/20260505-kcov-simultaneous-remote-v1-1-a670ba7cefd2@google.com Signed-off-by: Jann Horn Reviewed-by: Dmitry Vyukov Cc: Alexander Potapenko Cc: Andrey Konovalov Cc: Marco Elver Signed-off-by: Andrew Morton --- Documentation/dev-tools/kcov.rst | 6 ++ include/linux/kcov.h | 2 - include/linux/sched.h | 3 + kernel/kcov.c | 94 ++++++++++++++++++-------------- 4 files changed, 61 insertions(+), 44 deletions(-) diff --git a/Documentation/dev-tools/kcov.rst b/Documentation/dev-tools/kcov.rst index 8127849d40f5..1a739290c8ec 100644 --- a/Documentation/dev-tools/kcov.rst +++ b/Documentation/dev-tools/kcov.rst @@ -237,6 +237,9 @@ Both ``kcov_remote_start`` and ``kcov_remote_stop`` annotations and the collection sections. The way a handle is used depends on the context where the matching code section executes. +A thread can use two separate KCOV instances to collect remote coverage and +normal coverage at the same time. + KCOV supports collecting remote coverage from the following contexts: 1. Global kernel background tasks. These are the tasks that are spawned during @@ -262,6 +265,9 @@ gets saved to the ``kcov_handle`` field in the current ``task_struct`` and needs to be passed to the newly spawned local tasks via custom kernel code modifications. Those tasks should in turn use the passed handle in their ``kcov_remote_start`` and ``kcov_remote_stop`` annotations. +In the kernel, common handles are wrapped in a ``kcov_common_handle_id``, which +consumes no space in builds without ``CONFIG_KCOV``; subsystems that integrate +with this mechanism should not need to use any ``#ifdef CONFIG_KCOV`` or such. KCOV follows a predefined format for both global and common handles. Each handle is a ``u64`` integer. Currently, only the one top and the lower 4 bytes diff --git a/include/linux/kcov.h b/include/linux/kcov.h index cdb72b3859d8..895b761b2db1 100644 --- a/include/linux/kcov.h +++ b/include/linux/kcov.h @@ -21,8 +21,6 @@ enum kcov_mode { KCOV_MODE_TRACE_PC = 2, /* Collecting comparison operands mode. */ KCOV_MODE_TRACE_CMP = 3, - /* The process owns a KCOV remote reference. */ - KCOV_MODE_REMOTE = 4, }; #define KCOV_IN_CTXSW (1 << 30) diff --git a/include/linux/sched.h b/include/linux/sched.h index ee06cba5c6f5..d71cec884a5d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1517,6 +1517,9 @@ struct task_struct { /* KCOV descriptor wired with this task or NULL: */ struct kcov *kcov; + /* KCOV descriptor for remote coverage collection from other tasks: */ + struct kcov *kcov_remote; + /* KCOV common handle for remote coverage collection: */ u64 kcov_handle; diff --git a/kernel/kcov.c b/kernel/kcov.c index a43e33a28adb..fd2503030729 100644 --- a/kernel/kcov.c +++ b/kernel/kcov.c @@ -368,6 +368,7 @@ static void kcov_start(struct task_struct *t, struct kcov *kcov, WRITE_ONCE(t->kcov_mode, mode); } +/* operates on coverage-generator-owned fields */ static void kcov_stop(struct task_struct *t) { WRITE_ONCE(t->kcov_mode, KCOV_MODE_DISABLED); @@ -377,16 +378,17 @@ static void kcov_stop(struct task_struct *t) t->kcov_area = NULL; } +/* operates on coverage-generator-owned fields */ static void kcov_task_reset(struct task_struct *t) { kcov_stop(t); t->kcov_sequence = 0; - t->kcov_handle = 0; } void kcov_task_init(struct task_struct *t) { kcov_task_reset(t); + t->kcov_remote = NULL; t->kcov_handle = current->kcov_handle; } @@ -423,11 +425,14 @@ static void kcov_remote_reset(struct kcov *kcov) static void kcov_disable(struct task_struct *t, struct kcov *kcov) __must_hold(&kcov->lock) { - kcov_task_reset(t); - if (kcov->remote) + if (kcov->remote) { + t->kcov_handle = 0; + t->kcov_remote = NULL; kcov_remote_reset(kcov); - else + } else { + kcov_task_reset(t); kcov_reset(kcov); + } } static void kcov_get(struct kcov *kcov) @@ -453,41 +458,47 @@ void kcov_task_exit(struct task_struct *t) unsigned long flags; kcov = t->kcov; - if (kcov == NULL) - return; - - spin_lock_irqsave(&kcov->lock, flags); - kcov_debug("t = %px, kcov->t = %px\n", t, kcov->t); - /* - * For KCOV_ENABLE devices we want to make sure that t->kcov->t == t, - * which comes down to: - * WARN_ON(!kcov->remote && kcov->t != t); - * - * For KCOV_REMOTE_ENABLE devices, the exiting task is either: - * - * 1. A remote task between kcov_remote_start() and kcov_remote_stop(). - * In this case we should print a warning right away, since a task - * shouldn't be exiting when it's in a kcov coverage collection - * section. Here t points to the task that is collecting remote - * coverage, and t->kcov->t points to the thread that created the - * kcov device. Which means that to detect this case we need to - * check that t != t->kcov->t, and this gives us the following: - * WARN_ON(kcov->remote && kcov->t != t); - * - * 2. The task that created kcov exiting without calling KCOV_DISABLE, - * and then again we make sure that t->kcov->t == t: - * WARN_ON(kcov->remote && kcov->t != t); - * - * By combining all three checks into one we get: - */ - if (WARN_ON(kcov->t != t)) { + if (kcov) { + spin_lock_irqsave(&kcov->lock, flags); + kcov_debug("t = %px, kcov->t = %px\n", t, kcov->t); + /* + * This could be a remote task between kcov_remote_start() and + * kcov_remote_stop(). + * In this case we should print a warning right away, since a + * task shouldn't be exiting when it's in a kcov coverage + * collection section. + * + * Otherwise, this should be a task that created a local + * kcov instance and hasn't called KCOV_DISABLE. + * Make sure that t->kcov->t is consistent. + */ + if (WARN_ON(kcov->remote) || WARN_ON(kcov->t != t)) { + spin_unlock_irqrestore(&kcov->lock, flags); + return; + } + /* Just to not leave dangling references behind. */ + kcov_disable(t, kcov); spin_unlock_irqrestore(&kcov->lock, flags); - return; + kcov_put(kcov); + } + kcov = t->kcov_remote; + if (kcov) { + spin_lock_irqsave(&kcov->lock, flags); + kcov_debug("t = %px, kcov->t = %px\n", t, kcov->t); + /* + * This is a KCOV_REMOTE_ENABLE device, and the task is the + * user task which has requested remote coverage collection. + * Make sure that t->kcov->t is consistent. + */ + if (WARN_ON(!kcov->remote) || WARN_ON(kcov->t != t)) { + spin_unlock_irqrestore(&kcov->lock, flags); + return; + } + /* Just to not leave dangling references behind. */ + kcov_disable(t, kcov); + spin_unlock_irqrestore(&kcov->lock, flags); + kcov_put(kcov); } - /* Just to not leave dangling references behind. */ - kcov_disable(t, kcov); - spin_unlock_irqrestore(&kcov->lock, flags); - kcov_put(kcov); } static int kcov_mmap(struct file *filep, struct vm_area_struct *vma) @@ -629,9 +640,9 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd, case KCOV_DISABLE: /* Disable coverage for the current task. */ unused = arg; - if (unused != 0 || current->kcov != kcov) - return -EINVAL; t = current; + if (unused != 0 || (kcov != t->kcov && kcov != t->kcov_remote)) + return -EINVAL; if (WARN_ON(kcov->t != t)) return -EINVAL; kcov_disable(t, kcov); @@ -641,7 +652,7 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd, if (kcov->mode != KCOV_MODE_INIT || !kcov->area) return -EINVAL; t = current; - if (kcov->t != NULL || t->kcov != NULL) + if (kcov->t != NULL || t->kcov_remote != NULL) return -EBUSY; remote_arg = (struct kcov_remote_arg *)arg; mode = kcov_get_mode(remote_arg->trace_mode); @@ -651,8 +662,7 @@ static int kcov_ioctl_locked(struct kcov *kcov, unsigned int cmd, LONG_MAX / sizeof(unsigned long)) return -EINVAL; kcov->mode = mode; - t->kcov = kcov; - t->kcov_mode = KCOV_MODE_REMOTE; + t->kcov_remote = kcov; kcov->t = t; kcov->remote = true; kcov->remote_size = remote_arg->area_size; From 91e11432d7db690dfa3e6d2e6f89f5cd865123f5 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Mon, 18 May 2026 11:10:05 +0200 Subject: [PATCH 053/108] mailmap: update Jorge Ramirez-Ortiz email address Map old Linaro address to the new Qualcomm OSS address. Link: https://lore.kernel.org/20260518091019.3926371-1-jorge.ramirez@oss.qualcomm.com Signed-off-by: Jorge Ramirez-Ortiz Cc: Jakub Kacinski Cc: Konrad Dybcio Cc: Martin Kepplinger Cc: Shannon Nelson Signed-off-by: Andrew Morton --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index a009f73d7ea5..cf67173dcd51 100644 --- a/.mailmap +++ b/.mailmap @@ -435,6 +435,7 @@ John Stultz Jonas Gorski Jonathan Cameron Jordan Crouse +Jorge Ramirez-Ortiz From c4697486fc232e821a33191d809d0ac3bb600027 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:44 +0200 Subject: [PATCH 054/108] raid6: turn the userspace test harness into a kunit test Patch series "cleanup the RAID6 P/Q library", v3. This series cleans up the RAID6 P/Q library to match the recent updates to the RAID 5 XOR library and other CRC/crypto libraries. This includes providing properly documented external interfaces, hiding the internals, using static_call instead of indirect calls and turning the user space test suite into an in-kernel kunit test which is also extended to improve coverage. Note that this changes registration so that non-priority algorithms are not registered, which greatly helps with the benchmark time at boot time. I'd like to encourage all architecture maintainers to see if they can further optimized this by registering as few as possible algorithms when there is a clear benefit in optimized or more unrolled implementations. This patch (of 18): Currently the raid6 code can be compiled as userspace code to run the test suite. Convert that to be a kunit case with minimal changes to avoid mutating global state so that we can drop this requirement. Note that this is not a good kunit test case yet and will need a lot more work, but that is deferred until the raid6 code is moved to it's new place, which is easier if the userspace makefile doesn't need adjustments for the new location first. Link: https://lore.kernel.org/20260518051804.462141-1-hch@lst.de Link: https://lore.kernel.org/20260518051804.462141-2-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- include/linux/raid/pq.h | 3 - lib/Kconfig | 11 +++ lib/raid6/Makefile | 2 +- lib/raid6/algos.c | 5 +- lib/raid6/recov.c | 34 --------- lib/raid6/test/Makefile | 155 +-------------------------------------- lib/raid6/test/test.c | 156 +++++++++++++++++++++------------------- 7 files changed, 100 insertions(+), 266 deletions(-) diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index 2467b3be15c9..08c5995ea980 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -144,7 +144,6 @@ extern const struct raid6_calls raid6_neonx8; /* Algorithm list */ extern const struct raid6_calls * const raid6_algos[]; extern const struct raid6_recov_calls *const raid6_recov_algos[]; -int raid6_select_algo(void); /* Return values from chk_syndrome */ #define RAID6_OK 0 @@ -165,8 +164,6 @@ extern void (*raid6_2data_recov)(int disks, size_t bytes, int faila, int failb, void **ptrs); extern void (*raid6_datap_recov)(int disks, size_t bytes, int faila, void **ptrs); -void raid6_dual_recov(int disks, size_t bytes, int faila, int failb, - void **ptrs); /* Some definitions to allow code to be compiled for testing in userspace */ #ifndef __KERNEL__ diff --git a/lib/Kconfig b/lib/Kconfig index ed31919a5a0b..f882f7f0a68f 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -11,6 +11,17 @@ menu "Library routines" config RAID6_PQ tristate +config RAID6_PQ_KUNIT_TEST + tristate "KUnit tests for RAID6 PQ functions" if !KUNIT_ALL_TESTS + depends on KUNIT + depends on RAID6_PQ + default KUNIT_ALL_TESTS + help + Unit tests for the RAID6 PQ library functions. + + This is intended to help people writing architecture-specific + optimized versions. If unsure, say N. + config RAID6_PQ_BENCHMARK bool "Automatically choose fastest RAID6 PQ functions" depends on RAID6_PQ diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile index 5be0a4e60ab1..6fd048c127b6 100644 --- a/lib/raid6/Makefile +++ b/lib/raid6/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_RAID6_PQ) += raid6_pq.o +obj-$(CONFIG_RAID6_PQ) += raid6_pq.o test/ raid6_pq-y += algos.o recov.o tables.o int1.o int2.o int4.o \ int8.o diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c index 799e0e5eac26..5a9f4882e18d 100644 --- a/lib/raid6/algos.c +++ b/lib/raid6/algos.c @@ -19,6 +19,7 @@ #include #include #endif +#include struct raid6_calls raid6_call; EXPORT_SYMBOL_GPL(raid6_call); @@ -86,6 +87,7 @@ const struct raid6_calls * const raid6_algos[] = { &raid6_intx1, NULL }; +EXPORT_SYMBOL_IF_KUNIT(raid6_algos); void (*raid6_2data_recov)(int, size_t, int, int, void **); EXPORT_SYMBOL_GPL(raid6_2data_recov); @@ -119,6 +121,7 @@ const struct raid6_recov_calls *const raid6_recov_algos[] = { &raid6_recov_intx1, NULL }; +EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algos); #ifdef __KERNEL__ #define RAID6_TIME_JIFFIES_LG2 4 @@ -239,7 +242,7 @@ static inline const struct raid6_calls *raid6_choose_gen( /* Try to pick the best algorithm */ /* This code uses the gfmul table as convenient data set to abuse */ -int __init raid6_select_algo(void) +static int __init raid6_select_algo(void) { const int disks = RAID6_TEST_DISKS; diff --git a/lib/raid6/recov.c b/lib/raid6/recov.c index b5e47c008b41..8d113196632e 100644 --- a/lib/raid6/recov.c +++ b/lib/raid6/recov.c @@ -99,37 +99,3 @@ const struct raid6_recov_calls raid6_recov_intx1 = { .name = "intx1", .priority = 0, }; - -#ifndef __KERNEL__ -/* Testing only */ - -/* Recover two failed blocks. */ -void raid6_dual_recov(int disks, size_t bytes, int faila, int failb, void **ptrs) -{ - if ( faila > failb ) { - int tmp = faila; - faila = failb; - failb = tmp; - } - - if ( failb == disks-1 ) { - if ( faila == disks-2 ) { - /* P+Q failure. Just rebuild the syndrome. */ - raid6_call.gen_syndrome(disks, bytes, ptrs); - } else { - /* data+Q failure. Reconstruct data from P, - then rebuild syndrome. */ - /* NOT IMPLEMENTED - equivalent to RAID-5 */ - } - } else { - if ( failb == disks-2 ) { - /* data+P failure. */ - raid6_datap_recov(disks, bytes, faila, ptrs); - } else { - /* data+data failure. */ - raid6_2data_recov(disks, bytes, faila, failb, ptrs); - } - } -} - -#endif diff --git a/lib/raid6/test/Makefile b/lib/raid6/test/Makefile index 09bbe2b14cce..520381ea71d7 100644 --- a/lib/raid6/test/Makefile +++ b/lib/raid6/test/Makefile @@ -1,156 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -# -# This is a simple Makefile to test some of the RAID-6 code -# from userspace. -# -pound := \# +obj-$(CONFIG_RAID6_PQ_KUNIT_TEST) += raid6_kunit.o -# Adjust as desired -CC = gcc -OPTFLAGS = -O2 -CFLAGS = -I.. -I ../../../include -g $(OPTFLAGS) -LD = ld -AWK = awk -f -AR = ar -RANLIB = ranlib -OBJS = int1.o int2.o int4.o int8.o int16.o int32.o recov.o algos.o tables.o - -ARCH := $(shell uname -m 2>/dev/null | sed -e /s/i.86/i386/) -ifeq ($(ARCH),i386) - CFLAGS += -DCONFIG_X86_32 - IS_X86 = yes -endif -ifeq ($(ARCH),x86_64) - CFLAGS += -DCONFIG_X86_64 - IS_X86 = yes -endif - -ifeq ($(ARCH),arm) - CFLAGS += -I../../../arch/arm/include -mfpu=neon - HAS_NEON = yes -endif -ifeq ($(ARCH),aarch64) - CFLAGS += -I../../../arch/arm64/include - HAS_NEON = yes -endif - -ifeq ($(findstring riscv,$(ARCH)),riscv) - CFLAGS += -I../../../arch/riscv/include -DCONFIG_RISCV=1 - HAS_RVV = yes -endif - -ifeq ($(findstring ppc,$(ARCH)),ppc) - CFLAGS += -I../../../arch/powerpc/include - HAS_ALTIVEC := $(shell printf '$(pound)include \nvector int a;\n' |\ - gcc -c -x c - >/dev/null && rm ./-.o && echo yes) -endif - -ifeq ($(ARCH),loongarch64) - CFLAGS += -I../../../arch/loongarch/include -DCONFIG_LOONGARCH=1 - CFLAGS += $(shell echo 'vld $$vr0, $$zero, 0' | \ - gcc -c -x assembler - >/dev/null 2>&1 && \ - rm ./-.o && echo -DCONFIG_CPU_HAS_LSX=1) - CFLAGS += $(shell echo 'xvld $$xr0, $$zero, 0' | \ - gcc -c -x assembler - >/dev/null 2>&1 && \ - rm ./-.o && echo -DCONFIG_CPU_HAS_LASX=1) -endif - -ifeq ($(IS_X86),yes) - OBJS += mmx.o sse1.o sse2.o avx2.o recov_ssse3.o recov_avx2.o avx512.o recov_avx512.o - CFLAGS += -DCONFIG_X86 -else ifeq ($(HAS_NEON),yes) - OBJS += neon.o neon1.o neon2.o neon4.o neon8.o recov_neon.o recov_neon_inner.o - CFLAGS += -DCONFIG_KERNEL_MODE_NEON=1 -else ifeq ($(HAS_ALTIVEC),yes) - CFLAGS += -DCONFIG_ALTIVEC - OBJS += altivec1.o altivec2.o altivec4.o altivec8.o \ - vpermxor1.o vpermxor2.o vpermxor4.o vpermxor8.o -else ifeq ($(ARCH),loongarch64) - OBJS += loongarch_simd.o recov_loongarch_simd.o -else ifeq ($(HAS_RVV),yes) - OBJS += rvv.o recov_rvv.o - CFLAGS += -DCONFIG_RISCV_ISA_V=1 -endif - -.c.o: - $(CC) $(CFLAGS) -c -o $@ $< - -%.c: ../%.c - cp -f $< $@ - -%.uc: ../%.uc - cp -f $< $@ - -all: raid6.a raid6test - -raid6.a: $(OBJS) - rm -f $@ - $(AR) cq $@ $^ - $(RANLIB) $@ - -raid6test: test.c raid6.a - $(CC) $(CFLAGS) -o raid6test $^ - -neon1.c: neon.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=1 < neon.uc > $@ - -neon2.c: neon.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=2 < neon.uc > $@ - -neon4.c: neon.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=4 < neon.uc > $@ - -neon8.c: neon.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=8 < neon.uc > $@ - -altivec1.c: altivec.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=1 < altivec.uc > $@ - -altivec2.c: altivec.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=2 < altivec.uc > $@ - -altivec4.c: altivec.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=4 < altivec.uc > $@ - -altivec8.c: altivec.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=8 < altivec.uc > $@ - -vpermxor1.c: vpermxor.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=1 < vpermxor.uc > $@ - -vpermxor2.c: vpermxor.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=2 < vpermxor.uc > $@ - -vpermxor4.c: vpermxor.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=4 < vpermxor.uc > $@ - -vpermxor8.c: vpermxor.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=8 < vpermxor.uc > $@ - -int1.c: int.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=1 < int.uc > $@ - -int2.c: int.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=2 < int.uc > $@ - -int4.c: int.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=4 < int.uc > $@ - -int8.c: int.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=8 < int.uc > $@ - -int16.c: int.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=16 < int.uc > $@ - -int32.c: int.uc ../unroll.awk - $(AWK) ../unroll.awk -vN=32 < int.uc > $@ - -tables.c: mktables - ./mktables > tables.c - -clean: - rm -f *.o *.a mktables mktables.c *.uc int*.c altivec*.c vpermxor*.c neon*.c tables.c raid6test - -spotless: clean - rm -f *~ +raid6_kunit-y += test.o diff --git a/lib/raid6/test/test.c b/lib/raid6/test/test.c index 841a55242aba..9db287b4a48f 100644 --- a/lib/raid6/test/test.c +++ b/lib/raid6/test/test.c @@ -1,43 +1,37 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * raid6test.c + * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved * - * Test RAID-6 recovery with various algorithms + * Test RAID-6 recovery algorithms. */ -#include -#include -#include +#include +#include #include +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); + +#define RAID6_KUNIT_SEED 42 + #define NDISKS 16 /* Including P and Q */ -const char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); - -char *dataptrs[NDISKS]; -char data[NDISKS][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); -char recovi[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); -char recovj[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +static struct rnd_state rng; +static void *dataptrs[NDISKS]; +static char data[NDISKS][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +static char recovi[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +static char recovj[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); static void makedata(int start, int stop) { - int i, j; + int i; for (i = start; i <= stop; i++) { - for (j = 0; j < PAGE_SIZE; j++) - data[i][j] = rand(); - + prandom_bytes_state(&rng, data[i], PAGE_SIZE); dataptrs[i] = data[i]; } } -static char disk_type(int d) +static char member_type(int d) { switch (d) { case NDISKS-2: @@ -49,104 +43,118 @@ static char disk_type(int d) } } -static int test_disks(int i, int j) +static void test_disks(struct kunit *test, const struct raid6_calls *calls, + const struct raid6_recov_calls *ra, int faila, int failb) { - int erra, errb; - memset(recovi, 0xf0, PAGE_SIZE); memset(recovj, 0xba, PAGE_SIZE); - dataptrs[i] = recovi; - dataptrs[j] = recovj; + dataptrs[faila] = recovi; + dataptrs[failb] = recovj; - raid6_dual_recov(NDISKS, PAGE_SIZE, i, j, (void **)&dataptrs); + if (failb == NDISKS - 1) { + /* + * We don't implement the data+Q failure scenario, since it + * is equivalent to a RAID-5 failure (XOR, then recompute Q). + */ + if (faila != NDISKS - 2) + goto skip; - erra = memcmp(data[i], recovi, PAGE_SIZE); - errb = memcmp(data[j], recovj, PAGE_SIZE); - - if (i < NDISKS-2 && j == NDISKS-1) { - /* We don't implement the DQ failure scenario, since it's - equivalent to a RAID-5 failure (XOR, then recompute Q) */ - erra = errb = 0; + /* P+Q failure. Just rebuild the syndrome. */ + calls->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs); + } else if (failb == NDISKS - 2) { + /* data+P failure. */ + ra->datap(NDISKS, PAGE_SIZE, faila, dataptrs); } else { - printf("algo=%-8s faila=%3d(%c) failb=%3d(%c) %s\n", - raid6_call.name, - i, disk_type(i), - j, disk_type(j), - (!erra && !errb) ? "OK" : - !erra ? "ERRB" : - !errb ? "ERRA" : "ERRAB"); + /* data+data failure. */ + ra->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs); } - dataptrs[i] = data[i]; - dataptrs[j] = data[j]; + KUNIT_EXPECT_MEMEQ_MSG(test, data[faila], recovi, PAGE_SIZE, + "algo=%-8s/%-8s faila miscompared: %3d[%c] (failb=%3d[%c])\n", + calls->name, ra->name, + faila, member_type(faila), + failb, member_type(failb)); + KUNIT_EXPECT_MEMEQ_MSG(test, data[failb], recovj, PAGE_SIZE, + "algo=%-8s/%-8s failb miscompared: %3d[%c] (faila=%3d[%c])\n", + calls->name, ra->name, + failb, member_type(failb), + faila, member_type(faila)); - return erra || errb; +skip: + dataptrs[faila] = data[faila]; + dataptrs[failb] = data[failb]; } -int main(int argc, char *argv[]) +static void raid6_test(struct kunit *test) { const struct raid6_calls *const *algo; const struct raid6_recov_calls *const *ra; int i, j, p1, p2; - int err = 0; - - makedata(0, NDISKS-1); for (ra = raid6_recov_algos; *ra; ra++) { if ((*ra)->valid && !(*ra)->valid()) continue; - raid6_2data_recov = (*ra)->data2; - raid6_datap_recov = (*ra)->datap; - - printf("using recovery %s\n", (*ra)->name); - for (algo = raid6_algos; *algo; algo++) { - if ((*algo)->valid && !(*algo)->valid()) + const struct raid6_calls *calls = *algo; + + if (calls->valid && !calls->valid()) continue; - raid6_call = **algo; - /* Nuke syndromes */ - memset(data[NDISKS-2], 0xee, 2*PAGE_SIZE); + memset(data[NDISKS - 2], 0xee, PAGE_SIZE); + memset(data[NDISKS - 1], 0xee, PAGE_SIZE); /* Generate assumed good syndrome */ - raid6_call.gen_syndrome(NDISKS, PAGE_SIZE, + calls->gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs); for (i = 0; i < NDISKS-1; i++) for (j = i+1; j < NDISKS; j++) - err += test_disks(i, j); + test_disks(test, calls, *ra, i, j); - if (!raid6_call.xor_syndrome) + if (!calls->xor_syndrome) continue; for (p1 = 0; p1 < NDISKS-2; p1++) for (p2 = p1; p2 < NDISKS-2; p2++) { /* Simulate rmw run */ - raid6_call.xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, + calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, (void **)&dataptrs); makedata(p1, p2); - raid6_call.xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, + calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, (void **)&dataptrs); for (i = 0; i < NDISKS-1; i++) for (j = i+1; j < NDISKS; j++) - err += test_disks(i, j); + test_disks(test, calls, + *ra, i, j); } } - printf("\n"); } - - printf("\n"); - /* Pick the best algorithm test */ - raid6_select_algo(); - - if (err) - printf("\n*** ERRORS FOUND ***\n"); - - return err; } + +static struct kunit_case raid6_test_cases[] = { + KUNIT_CASE(raid6_test), + {}, +}; + +static int raid6_suite_init(struct kunit_suite *suite) +{ + prandom_seed_state(&rng, RAID6_KUNIT_SEED); + makedata(0, NDISKS - 1); + return 0; +} + +static struct kunit_suite raid6_test_suite = { + .name = "raid6", + .test_cases = raid6_test_cases, + .suite_init = raid6_suite_init, +}; +kunit_test_suite(raid6_test_suite); + +MODULE_DESCRIPTION("Unit test for the RAID P/Q library functions"); +MODULE_LICENSE("GPL"); From 3d6beb659ddf0664612bafacb0bd9030ba6ec7e6 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:45 +0200 Subject: [PATCH 055/108] raid6: remove __KERNEL__ ifdefs With the test code ported to kernel space, none of this is required. Link: https://lore.kernel.org/20260518051804.462141-3-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- include/linux/raid/pq.h | 90 -------------------------------- lib/raid6/algos.c | 12 ----- lib/raid6/altivec.uc | 10 +--- lib/raid6/avx2.c | 2 +- lib/raid6/avx512.c | 2 +- lib/raid6/loongarch.h | 38 -------------- lib/raid6/loongarch_simd.c | 3 +- lib/raid6/mktables.c | 14 ----- lib/raid6/mmx.c | 2 +- lib/raid6/neon.c | 6 --- lib/raid6/recov_avx2.c | 2 +- lib/raid6/recov_avx512.c | 2 +- lib/raid6/recov_loongarch_simd.c | 3 +- lib/raid6/recov_neon.c | 6 --- lib/raid6/recov_ssse3.c | 2 +- lib/raid6/rvv.h | 11 +--- lib/raid6/sse1.c | 2 +- lib/raid6/sse2.c | 2 +- lib/raid6/vpermxor.uc | 7 --- lib/raid6/x86.h | 75 -------------------------- 20 files changed, 15 insertions(+), 276 deletions(-) delete mode 100644 lib/raid6/loongarch.h delete mode 100644 lib/raid6/x86.h diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index 08c5995ea980..d26788fada58 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -8,8 +8,6 @@ #ifndef LINUX_RAID_RAID6_H #define LINUX_RAID_RAID6_H -#ifdef __KERNEL__ - #include #include @@ -19,59 +17,6 @@ static inline void *raid6_get_zero_page(void) return page_address(ZERO_PAGE(0)); } -#else /* ! __KERNEL__ */ -/* Used for testing in user space */ - -#include -#include -#include -#include -#include -#include -#include - -/* Not standard, but glibc defines it */ -#define BITS_PER_LONG __WORDSIZE - -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; - -#ifndef PAGE_SIZE -# define PAGE_SIZE 4096 -#endif -#ifndef PAGE_SHIFT -# define PAGE_SHIFT 12 -#endif -extern const char raid6_empty_zero_page[PAGE_SIZE]; - -#define __init -#define __exit -#ifndef __attribute_const__ -# define __attribute_const__ __attribute__((const)) -#endif -#define noinline __attribute__((noinline)) - -#define preempt_enable() -#define preempt_disable() -#define cpu_has_feature(x) 1 -#define enable_kernel_altivec() -#define disable_kernel_altivec() - -#undef EXPORT_SYMBOL -#define EXPORT_SYMBOL(sym) -#undef EXPORT_SYMBOL_GPL -#define EXPORT_SYMBOL_GPL(sym) -#define MODULE_LICENSE(licence) -#define MODULE_DESCRIPTION(desc) -#define subsys_initcall(x) -#define module_exit(x) - -#define IS_ENABLED(x) (x) -#define CONFIG_RAID6_PQ_BENCHMARK 1 -#endif /* __KERNEL__ */ - /* Routine choices */ struct raid6_calls { void (*gen_syndrome)(int, size_t, void **); @@ -165,39 +110,4 @@ extern void (*raid6_2data_recov)(int disks, size_t bytes, int faila, int failb, extern void (*raid6_datap_recov)(int disks, size_t bytes, int faila, void **ptrs); -/* Some definitions to allow code to be compiled for testing in userspace */ -#ifndef __KERNEL__ - -# define jiffies raid6_jiffies() -# define printk printf -# define pr_err(format, ...) fprintf(stderr, format, ## __VA_ARGS__) -# define pr_info(format, ...) fprintf(stdout, format, ## __VA_ARGS__) -# define GFP_KERNEL 0 -# define __get_free_pages(x, y) ((unsigned long)mmap(NULL, PAGE_SIZE << (y), \ - PROT_READ|PROT_WRITE, \ - MAP_PRIVATE|MAP_ANONYMOUS,\ - 0, 0)) -# define free_pages(x, y) munmap((void *)(x), PAGE_SIZE << (y)) - -static inline void cpu_relax(void) -{ - /* Nothing */ -} - -#undef HZ -#define HZ 1000 -static inline uint32_t raid6_jiffies(void) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return tv.tv_sec*1000 + tv.tv_usec/1000; -} - -static inline void *raid6_get_zero_page(void) -{ - return raid6_empty_zero_page; -} - -#endif /* ! __KERNEL__ */ - #endif /* LINUX_RAID_RAID6_H */ diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c index 5a9f4882e18d..985c60bb00a4 100644 --- a/lib/raid6/algos.c +++ b/lib/raid6/algos.c @@ -12,13 +12,8 @@ */ #include -#ifndef __KERNEL__ -#include -#include -#else #include #include -#endif #include struct raid6_calls raid6_call; @@ -123,14 +118,7 @@ const struct raid6_recov_calls *const raid6_recov_algos[] = { }; EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algos); -#ifdef __KERNEL__ #define RAID6_TIME_JIFFIES_LG2 4 -#else -/* Need more time to be stable in userspace */ -#define RAID6_TIME_JIFFIES_LG2 9 -#define time_before(x, y) ((x) < (y)) -#endif - #define RAID6_TEST_DISKS 8 #define RAID6_TEST_DISKS_ORDER 3 diff --git a/lib/raid6/altivec.uc b/lib/raid6/altivec.uc index d20ed0d11411..2c59963e58f9 100644 --- a/lib/raid6/altivec.uc +++ b/lib/raid6/altivec.uc @@ -27,10 +27,8 @@ #ifdef CONFIG_ALTIVEC #include -#ifdef __KERNEL__ -# include -# include -#endif /* __KERNEL__ */ +#include +#include /* * This is the C data type to use. We use a vector of @@ -113,11 +111,7 @@ int raid6_have_altivec(void); int raid6_have_altivec(void) { /* This assumes either all CPUs have Altivec or none does */ -# ifdef __KERNEL__ return cpu_has_feature(CPU_FTR_ALTIVEC); -# else - return 1; -# endif } #endif diff --git a/lib/raid6/avx2.c b/lib/raid6/avx2.c index 059024234dce..a1a5213918af 100644 --- a/lib/raid6/avx2.c +++ b/lib/raid6/avx2.c @@ -14,7 +14,7 @@ */ #include -#include "x86.h" +#include static const struct raid6_avx2_constants { u64 x1d[4]; diff --git a/lib/raid6/avx512.c b/lib/raid6/avx512.c index 009bd0adeebf..874998bcd7d7 100644 --- a/lib/raid6/avx512.c +++ b/lib/raid6/avx512.c @@ -18,7 +18,7 @@ */ #include -#include "x86.h" +#include static const struct raid6_avx512_constants { u64 x1d[8]; diff --git a/lib/raid6/loongarch.h b/lib/raid6/loongarch.h deleted file mode 100644 index acfc33ce7056..000000000000 --- a/lib/raid6/loongarch.h +++ /dev/null @@ -1,38 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2023 WANG Xuerui - * - * raid6/loongarch.h - * - * Definitions common to LoongArch RAID-6 code only - */ - -#ifndef _LIB_RAID6_LOONGARCH_H -#define _LIB_RAID6_LOONGARCH_H - -#ifdef __KERNEL__ - -#include -#include - -#else /* for user-space testing */ - -#include - -/* have to supply these defines for glibc 2.37- and musl */ -#ifndef HWCAP_LOONGARCH_LSX -#define HWCAP_LOONGARCH_LSX (1 << 4) -#endif -#ifndef HWCAP_LOONGARCH_LASX -#define HWCAP_LOONGARCH_LASX (1 << 5) -#endif - -#define kernel_fpu_begin() -#define kernel_fpu_end() - -#define cpu_has_lsx (getauxval(AT_HWCAP) & HWCAP_LOONGARCH_LSX) -#define cpu_has_lasx (getauxval(AT_HWCAP) & HWCAP_LOONGARCH_LASX) - -#endif /* __KERNEL__ */ - -#endif /* _LIB_RAID6_LOONGARCH_H */ diff --git a/lib/raid6/loongarch_simd.c b/lib/raid6/loongarch_simd.c index aa5d9f924ca3..72f4d92d4876 100644 --- a/lib/raid6/loongarch_simd.c +++ b/lib/raid6/loongarch_simd.c @@ -10,7 +10,8 @@ */ #include -#include "loongarch.h" +#include +#include /* * The vector algorithms are currently priority 0, which means the generic diff --git a/lib/raid6/mktables.c b/lib/raid6/mktables.c index 3be03793237c..3de1dbf6846c 100644 --- a/lib/raid6/mktables.c +++ b/lib/raid6/mktables.c @@ -56,9 +56,7 @@ int main(int argc, char *argv[]) uint8_t v; uint8_t exptbl[256], invtbl[256]; - printf("#ifdef __KERNEL__\n"); printf("#include \n"); - printf("#endif\n"); printf("#include \n"); /* Compute multiplication table */ @@ -76,9 +74,7 @@ int main(int argc, char *argv[]) printf("\t},\n"); } printf("};\n"); - printf("#ifdef __KERNEL__\n"); printf("EXPORT_SYMBOL(raid6_gfmul);\n"); - printf("#endif\n"); /* Compute vector multiplication table */ printf("\nconst u8 __attribute__((aligned(256)))\n" @@ -101,9 +97,7 @@ int main(int argc, char *argv[]) printf("\t},\n"); } printf("};\n"); - printf("#ifdef __KERNEL__\n"); printf("EXPORT_SYMBOL(raid6_vgfmul);\n"); - printf("#endif\n"); /* Compute power-of-2 table (exponent) */ v = 1; @@ -120,9 +114,7 @@ int main(int argc, char *argv[]) } } printf("};\n"); - printf("#ifdef __KERNEL__\n"); printf("EXPORT_SYMBOL(raid6_gfexp);\n"); - printf("#endif\n"); /* Compute log-of-2 table */ printf("\nconst u8 __attribute__((aligned(256)))\n" @@ -140,9 +132,7 @@ int main(int argc, char *argv[]) } } printf("};\n"); - printf("#ifdef __KERNEL__\n"); printf("EXPORT_SYMBOL(raid6_gflog);\n"); - printf("#endif\n"); /* Compute inverse table x^-1 == x^254 */ printf("\nconst u8 __attribute__((aligned(256)))\n" @@ -155,9 +145,7 @@ int main(int argc, char *argv[]) } } printf("};\n"); - printf("#ifdef __KERNEL__\n"); printf("EXPORT_SYMBOL(raid6_gfinv);\n"); - printf("#endif\n"); /* Compute inv(2^x + 1) (exponent-xor-inverse) table */ printf("\nconst u8 __attribute__((aligned(256)))\n" @@ -169,9 +157,7 @@ int main(int argc, char *argv[]) (j == 7) ? '\n' : ' '); } printf("};\n"); - printf("#ifdef __KERNEL__\n"); printf("EXPORT_SYMBOL(raid6_gfexi);\n"); - printf("#endif\n"); return 0; } diff --git a/lib/raid6/mmx.c b/lib/raid6/mmx.c index 3a5bf53a297b..e411f0cfbd95 100644 --- a/lib/raid6/mmx.c +++ b/lib/raid6/mmx.c @@ -14,7 +14,7 @@ #ifdef CONFIG_X86_32 #include -#include "x86.h" +#include /* Shared with raid6/sse1.c */ const struct raid6_mmx_constants { diff --git a/lib/raid6/neon.c b/lib/raid6/neon.c index 6d9474ce6da9..47b8bb0afc65 100644 --- a/lib/raid6/neon.c +++ b/lib/raid6/neon.c @@ -6,13 +6,7 @@ */ #include - -#ifdef __KERNEL__ #include -#else -#define scoped_ksimd() -#define cpu_has_neon() (1) -#endif /* * There are 2 reasons these wrappers are kept in a separate compilation unit diff --git a/lib/raid6/recov_avx2.c b/lib/raid6/recov_avx2.c index 97d598d2535c..19fbd9c4dce6 100644 --- a/lib/raid6/recov_avx2.c +++ b/lib/raid6/recov_avx2.c @@ -5,7 +5,7 @@ */ #include -#include "x86.h" +#include static int raid6_has_avx2(void) { diff --git a/lib/raid6/recov_avx512.c b/lib/raid6/recov_avx512.c index 7986120ca444..143f4976b2ad 100644 --- a/lib/raid6/recov_avx512.c +++ b/lib/raid6/recov_avx512.c @@ -7,7 +7,7 @@ */ #include -#include "x86.h" +#include static int raid6_has_avx512(void) { diff --git a/lib/raid6/recov_loongarch_simd.c b/lib/raid6/recov_loongarch_simd.c index 93dc515997a1..eb3a1e79f01f 100644 --- a/lib/raid6/recov_loongarch_simd.c +++ b/lib/raid6/recov_loongarch_simd.c @@ -11,7 +11,8 @@ */ #include -#include "loongarch.h" +#include +#include /* * Unlike with the syndrome calculation algorithms, there's no boot-time diff --git a/lib/raid6/recov_neon.c b/lib/raid6/recov_neon.c index 9d99aeabd31a..13d5df718c15 100644 --- a/lib/raid6/recov_neon.c +++ b/lib/raid6/recov_neon.c @@ -5,14 +5,8 @@ */ #include - -#ifdef __KERNEL__ #include #include "neon.h" -#else -#define scoped_ksimd() -#define cpu_has_neon() (1) -#endif static int raid6_has_neon(void) { diff --git a/lib/raid6/recov_ssse3.c b/lib/raid6/recov_ssse3.c index 2e849185c32b..146cdbf465bd 100644 --- a/lib/raid6/recov_ssse3.c +++ b/lib/raid6/recov_ssse3.c @@ -4,7 +4,7 @@ */ #include -#include "x86.h" +#include static int raid6_has_ssse3(void) { diff --git a/lib/raid6/rvv.h b/lib/raid6/rvv.h index 6d0708a2c8a4..b0a71b375962 100644 --- a/lib/raid6/rvv.h +++ b/lib/raid6/rvv.h @@ -7,17 +7,8 @@ * Definitions for RISC-V RAID-6 code */ -#ifdef __KERNEL__ -#include -#else -#define kernel_vector_begin() -#define kernel_vector_end() -#include -#include -#define has_vector() (getauxval(AT_HWCAP) & COMPAT_HWCAP_ISA_V) -#endif - #include +#include static int rvv_has_vector(void) { diff --git a/lib/raid6/sse1.c b/lib/raid6/sse1.c index 692fa3a93bf0..794d5cfa0306 100644 --- a/lib/raid6/sse1.c +++ b/lib/raid6/sse1.c @@ -19,7 +19,7 @@ #ifdef CONFIG_X86_32 #include -#include "x86.h" +#include /* Defined in raid6/mmx.c */ extern const struct raid6_mmx_constants { diff --git a/lib/raid6/sse2.c b/lib/raid6/sse2.c index 2930220249c9..f9edf8a8d1c4 100644 --- a/lib/raid6/sse2.c +++ b/lib/raid6/sse2.c @@ -13,7 +13,7 @@ */ #include -#include "x86.h" +#include static const struct raid6_sse_constants { u64 x1d[2]; diff --git a/lib/raid6/vpermxor.uc b/lib/raid6/vpermxor.uc index 1bfb127fbfe8..a8e76b1c956e 100644 --- a/lib/raid6/vpermxor.uc +++ b/lib/raid6/vpermxor.uc @@ -25,10 +25,8 @@ #include #include -#ifdef __KERNEL__ #include #include -#endif typedef vector unsigned char unative_t; #define NSIZE sizeof(unative_t) @@ -85,13 +83,8 @@ int raid6_have_altivec_vpermxor(void); int raid6_have_altivec_vpermxor(void) { /* Check if arch has both altivec and the vpermxor instructions */ -# ifdef __KERNEL__ return (cpu_has_feature(CPU_FTR_ALTIVEC_COMP) && cpu_has_feature(CPU_FTR_ARCH_207S)); -# else - return 1; -#endif - } #endif diff --git a/lib/raid6/x86.h b/lib/raid6/x86.h deleted file mode 100644 index 9a6ff37115e7..000000000000 --- a/lib/raid6/x86.h +++ /dev/null @@ -1,75 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* ----------------------------------------------------------------------- * - * - * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - -/* - * raid6/x86.h - * - * Definitions common to x86 and x86-64 RAID-6 code only - */ - -#ifndef LINUX_RAID_RAID6X86_H -#define LINUX_RAID_RAID6X86_H - -#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arch_um__) - -#ifdef __KERNEL__ /* Real code */ - -#include - -#else /* Dummy code for user space testing */ - -static inline void kernel_fpu_begin(void) -{ -} - -static inline void kernel_fpu_end(void) -{ -} - -#define __aligned(x) __attribute__((aligned(x))) - -#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */ -#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions - * (fast save and restore) */ -#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */ -#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */ -#define X86_FEATURE_XMM3 (4*32+ 0) /* "pni" SSE-3 */ -#define X86_FEATURE_SSSE3 (4*32+ 9) /* Supplemental SSE-3 */ -#define X86_FEATURE_AVX (4*32+28) /* Advanced Vector Extensions */ -#define X86_FEATURE_AVX2 (9*32+ 5) /* AVX2 instructions */ -#define X86_FEATURE_AVX512F (9*32+16) /* AVX-512 Foundation */ -#define X86_FEATURE_AVX512DQ (9*32+17) /* AVX-512 DQ (Double/Quad granular) - * Instructions - */ -#define X86_FEATURE_AVX512BW (9*32+30) /* AVX-512 BW (Byte/Word granular) - * Instructions - */ -#define X86_FEATURE_AVX512VL (9*32+31) /* AVX-512 VL (128/256 Vector Length) - * Extensions - */ -#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */ - -/* Should work well enough on modern CPUs for testing */ -static inline int boot_cpu_has(int flag) -{ - u32 eax, ebx, ecx, edx; - - eax = (flag & 0x100) ? 7 : - (flag & 0x20) ? 0x80000001 : 1; - ecx = 0; - - asm volatile("cpuid" - : "+a" (eax), "=b" (ebx), "=d" (edx), "+c" (ecx)); - - return ((flag & 0x100 ? ebx : - (flag & 0x80) ? ecx : edx) >> (flag & 31)) & 1; -} - -#endif /* ndef __KERNEL__ */ - -#endif -#endif From 3626738bc7147d52cb49f3994a9846aa2d34810a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:46 +0200 Subject: [PATCH 056/108] raid6: move to lib/raid/ Move the raid6 code to live in lib/raid/ with the XOR code, and change the internal organization so that each architecture has a subdirectory similar to the CRC, crypto and XOR libraries, and fix up the Makefile to only build files actually needed. Also move the kunit test case from the history test/ subdirectory to tests/ and use the normal naming scheme for it. Link: https://lore.kernel.org/20260518051804.462141-4-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- MAINTAINERS | 2 +- lib/Kconfig | 22 ---- lib/Makefile | 1 - lib/raid/Kconfig | 22 ++++ lib/raid/Makefile | 2 +- lib/{ => raid}/raid6/.gitignore | 0 lib/raid/raid6/Makefile | 122 ++++++++++++++++++ lib/{ => raid}/raid6/algos.c | 0 lib/{raid6 => raid/raid6/arm}/neon.c | 0 lib/{raid6 => raid/raid6/arm}/neon.h | 0 lib/{raid6 => raid/raid6/arm}/neon.uc | 2 +- lib/{raid6 => raid/raid6/arm}/recov_neon.c | 2 +- .../raid6/arm}/recov_neon_inner.c | 2 +- lib/{ => raid}/raid6/int.uc | 0 .../raid6/loongarch}/loongarch_simd.c | 0 .../raid6/loongarch}/recov_loongarch_simd.c | 0 lib/{ => raid}/raid6/mktables.c | 0 lib/{raid6 => raid/raid6/powerpc}/altivec.uc | 4 - lib/{raid6 => raid/raid6/powerpc}/vpermxor.uc | 3 - lib/{ => raid}/raid6/recov.c | 0 lib/{raid6 => raid/raid6/riscv}/recov_rvv.c | 0 lib/{raid6 => raid/raid6/riscv}/rvv.c | 0 lib/{raid6 => raid/raid6/riscv}/rvv.h | 0 lib/{raid6 => raid/raid6/s390}/recov_s390xc.c | 0 lib/{raid6 => raid/raid6/s390}/s390vx.uc | 0 lib/{raid6/test => raid/raid6/tests}/Makefile | 2 - .../test.c => raid/raid6/tests/raid6_kunit.c} | 0 lib/{ => raid}/raid6/unroll.awk | 0 lib/{raid6 => raid/raid6/x86}/avx2.c | 0 lib/{raid6 => raid/raid6/x86}/avx512.c | 0 lib/{raid6 => raid/raid6/x86}/mmx.c | 4 - lib/{raid6 => raid/raid6/x86}/recov_avx2.c | 0 lib/{raid6 => raid/raid6/x86}/recov_avx512.c | 0 lib/{raid6 => raid/raid6/x86}/recov_ssse3.c | 0 lib/{raid6 => raid/raid6/x86}/sse1.c | 4 - lib/{raid6 => raid/raid6/x86}/sse2.c | 0 lib/raid6/Makefile | 83 ------------ lib/raid6/test/.gitignore | 3 - 38 files changed, 149 insertions(+), 131 deletions(-) rename lib/{ => raid}/raid6/.gitignore (100%) create mode 100644 lib/raid/raid6/Makefile rename lib/{ => raid}/raid6/algos.c (100%) rename lib/{raid6 => raid/raid6/arm}/neon.c (100%) rename lib/{raid6 => raid/raid6/arm}/neon.h (100%) rename lib/{raid6 => raid/raid6/arm}/neon.uc (99%) rename lib/{raid6 => raid/raid6/arm}/recov_neon.c (99%) rename lib/{raid6 => raid/raid6/arm}/recov_neon_inner.c (99%) rename lib/{ => raid}/raid6/int.uc (100%) rename lib/{raid6 => raid/raid6/loongarch}/loongarch_simd.c (100%) rename lib/{raid6 => raid/raid6/loongarch}/recov_loongarch_simd.c (100%) rename lib/{ => raid}/raid6/mktables.c (100%) rename lib/{raid6 => raid/raid6/powerpc}/altivec.uc (98%) rename lib/{raid6 => raid/raid6/powerpc}/vpermxor.uc (98%) rename lib/{ => raid}/raid6/recov.c (100%) rename lib/{raid6 => raid/raid6/riscv}/recov_rvv.c (100%) rename lib/{raid6 => raid/raid6/riscv}/rvv.c (100%) rename lib/{raid6 => raid/raid6/riscv}/rvv.h (100%) rename lib/{raid6 => raid/raid6/s390}/recov_s390xc.c (100%) rename lib/{raid6 => raid/raid6/s390}/s390vx.uc (100%) rename lib/{raid6/test => raid/raid6/tests}/Makefile (77%) rename lib/{raid6/test/test.c => raid/raid6/tests/raid6_kunit.c} (100%) rename lib/{ => raid}/raid6/unroll.awk (100%) rename lib/{raid6 => raid/raid6/x86}/avx2.c (100%) rename lib/{raid6 => raid/raid6/x86}/avx512.c (100%) rename lib/{raid6 => raid/raid6/x86}/mmx.c (99%) rename lib/{raid6 => raid/raid6/x86}/recov_avx2.c (100%) rename lib/{raid6 => raid/raid6/x86}/recov_avx512.c (100%) rename lib/{raid6 => raid/raid6/x86}/recov_ssse3.c (100%) rename lib/{raid6 => raid/raid6/x86}/sse1.c (99%) rename lib/{raid6 => raid/raid6/x86}/sse2.c (100%) delete mode 100644 lib/raid6/Makefile delete mode 100644 lib/raid6/test/.gitignore diff --git a/MAINTAINERS b/MAINTAINERS index 236b0785bf3d..49a264b999e2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24823,7 +24823,7 @@ F: drivers/md/md* F: drivers/md/raid* F: include/linux/raid/ F: include/uapi/linux/raid/ -F: lib/raid6/ +F: lib/raid/raid6/ SOLIDRUN CLEARFOG SUPPORT M: Russell King diff --git a/lib/Kconfig b/lib/Kconfig index f882f7f0a68f..f01a33e521e1 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -8,28 +8,6 @@ config BINARY_PRINTF menu "Library routines" -config RAID6_PQ - tristate - -config RAID6_PQ_KUNIT_TEST - tristate "KUnit tests for RAID6 PQ functions" if !KUNIT_ALL_TESTS - depends on KUNIT - depends on RAID6_PQ - default KUNIT_ALL_TESTS - help - Unit tests for the RAID6 PQ library functions. - - This is intended to help people writing architecture-specific - optimized versions. If unsure, say N. - -config RAID6_PQ_BENCHMARK - bool "Automatically choose fastest RAID6 PQ functions" - depends on RAID6_PQ - default y - help - Benchmark all available RAID6 PQ functions on init and choose the - fastest one. - config LINEAR_RANGES tristate diff --git a/lib/Makefile b/lib/Makefile index f33a24bf1c19..6e72d2c1cce7 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -167,7 +167,6 @@ obj-$(CONFIG_LZ4_DECOMPRESS) += lz4/ obj-$(CONFIG_ZSTD_COMPRESS) += zstd/ obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd/ obj-$(CONFIG_XZ_DEC) += xz/ -obj-$(CONFIG_RAID6_PQ) += raid6/ lib-$(CONFIG_DECOMPRESS_GZIP) += decompress_inflate.o lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o diff --git a/lib/raid/Kconfig b/lib/raid/Kconfig index 5ab2b0a7be4c..e39f6d667792 100644 --- a/lib/raid/Kconfig +++ b/lib/raid/Kconfig @@ -28,3 +28,25 @@ config XOR_KUNIT_TEST This is intended to help people writing architecture-specific optimized versions. If unsure, say N. + +config RAID6_PQ + tristate + +config RAID6_PQ_KUNIT_TEST + tristate "KUnit tests for RAID6 PQ functions" if !KUNIT_ALL_TESTS + depends on KUNIT + depends on RAID6_PQ + default KUNIT_ALL_TESTS + help + Unit tests for the RAID6 PQ library functions. + + This is intended to help people writing architecture-specific + optimized versions. If unsure, say N. + +config RAID6_PQ_BENCHMARK + bool "Automatically choose fastest RAID6 PQ functions" + depends on RAID6_PQ + default y + help + Benchmark all available RAID6 PQ functions on init and choose the + fastest one. diff --git a/lib/raid/Makefile b/lib/raid/Makefile index 3540fe846dc4..6fc5eeb53df0 100644 --- a/lib/raid/Makefile +++ b/lib/raid/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -obj-y += xor/ +obj-y += xor/ raid6/ diff --git a/lib/raid6/.gitignore b/lib/raid/raid6/.gitignore similarity index 100% rename from lib/raid6/.gitignore rename to lib/raid/raid6/.gitignore diff --git a/lib/raid/raid6/Makefile b/lib/raid/raid6/Makefile new file mode 100644 index 000000000000..7cb31b8a5c17 --- /dev/null +++ b/lib/raid/raid6/Makefile @@ -0,0 +1,122 @@ +# SPDX-License-Identifier: GPL-2.0 + +ccflags-y += -I $(src) + +obj-$(CONFIG_RAID6_PQ) += raid6_pq.o tests/ + +raid6_pq-y += algos.o tables.o + +# generic integer generation and recovery implementation +raid6_pq-y += int1.o int2.o int4.o int8.o +raid6_pq-y += recov.o + +# architecture-specific generation and recovery implementations: +raid6_pq-$(CONFIG_KERNEL_MODE_NEON) += arm/neon.o \ + arm/neon1.o \ + arm/neon2.o \ + arm/neon4.o \ + arm/neon8.o \ + arm/recov_neon.o \ + arm/recov_neon_inner.o +raid6_pq-$(CONFIG_LOONGARCH) += loongarch/loongarch_simd.o \ + loongarch/recov_loongarch_simd.o +raid6_pq-$(CONFIG_ALTIVEC) += powerpc/altivec1.o \ + powerpc/altivec2.o \ + powerpc/altivec4.o \ + powerpc/altivec8.o \ + powerpc/vpermxor1.o \ + powerpc/vpermxor2.o \ + powerpc/vpermxor4.o \ + powerpc/vpermxor8.o +raid6_pq-$(CONFIG_RISCV_ISA_V) += riscv/rvv.o \ + riscv/recov_rvv.o +raid6_pq-$(CONFIG_S390) += s390/s390vx8.o \ + s390/recov_s390xc.o +ifeq ($(CONFIG_X86),y) +raid6_pq-$(CONFIG_X86_32) += x86/mmx.o \ + x86/sse1.o +endif +raid6_pq-$(CONFIG_X86) += x86/sse2.o \ + x86/avx2.o \ + x86/avx512.o \ + x86/recov_ssse3.o \ + x86/recov_avx2.o \ + x86/recov_avx512.o + +hostprogs += mktables + +CFLAGS_arm/neon1.o += $(CC_FLAGS_FPU) +CFLAGS_arm/neon2.o += $(CC_FLAGS_FPU) +CFLAGS_arm/neon4.o += $(CC_FLAGS_FPU) +CFLAGS_arm/neon8.o += $(CC_FLAGS_FPU) +CFLAGS_arm/recov_neon_inner.o += $(CC_FLAGS_FPU) +CFLAGS_REMOVE_arm/neon1.o += $(CC_FLAGS_NO_FPU) +CFLAGS_REMOVE_arm/neon2.o += $(CC_FLAGS_NO_FPU) +CFLAGS_REMOVE_arm/neon4.o += $(CC_FLAGS_NO_FPU) +CFLAGS_REMOVE_arm/neon8.o += $(CC_FLAGS_NO_FPU) +CFLAGS_REMOVE_arm/recov_neon_inner.o += $(CC_FLAGS_NO_FPU) + +ifeq ($(CONFIG_ALTIVEC),y) +altivec_flags := -maltivec $(call cc-option,-mabi=altivec) +# Enable +altivec_flags += -isystem $(shell $(CC) -print-file-name=include) + +CFLAGS_powerpc/altivec1.o += $(altivec_flags) +CFLAGS_powerpc/altivec2.o += $(altivec_flags) +CFLAGS_powerpc/altivec4.o += $(altivec_flags) +CFLAGS_powerpc/altivec8.o += $(altivec_flags) +CFLAGS_powerpc/vpermxor1.o += $(altivec_flags) +CFLAGS_powerpc/vpermxor2.o += $(altivec_flags) +CFLAGS_powerpc/vpermxor4.o += $(altivec_flags) +CFLAGS_powerpc/vpermxor8.o += $(altivec_flags) + +ifdef CONFIG_CC_IS_CLANG +# clang ppc port does not yet support -maltivec when -msoft-float is +# enabled. A future release of clang will resolve this +# https://llvm.org/pr31177 +CFLAGS_REMOVE_powerpc/altivec1.o += -msoft-float +CFLAGS_REMOVE_powerpc/altivec2.o += -msoft-float +CFLAGS_REMOVE_powerpc/altivec4.o += -msoft-float +CFLAGS_REMOVE_powerpc/altivec8.o += -msoft-float +CFLAGS_REMOVE_powerpc/vpermxor1.o += -msoft-float +CFLAGS_REMOVE_powerpc/vpermxor2.o += -msoft-float +CFLAGS_REMOVE_powerpc/vpermxor4.o += -msoft-float +CFLAGS_REMOVE_powerpc/vpermxor8.o += -msoft-float +endif # CONFIG_CC_IS_CLANG +endif # CONFIG_ALTIVEC + +quiet_cmd_mktable = TABLE $@ + cmd_mktable = $(obj)/mktables > $@ + +targets += tables.c +$(obj)/tables.c: $(obj)/mktables FORCE + $(call if_changed,mktable) + +quiet_cmd_unroll = UNROLL $@ + cmd_unroll = $(AWK) -v N=$* -f $(src)/unroll.awk < $< > $@ + +targets += int1.c int2.c int4.c int8.c +$(obj)/int%.c: $(src)/int.uc $(src)/unroll.awk FORCE + $(call if_changed,unroll) + +targets += arm/neon1.c arm/neon2.c arm/neon4.c arm/neon8.c +$(obj)/arm/neon%.c: $(src)/arm/neon.uc $(src)/unroll.awk FORCE + $(call if_changed,unroll) + +targets += powerpc/altivec1.c \ + powerpc/altivec2.c \ + powerpc/altivec4.c \ + powerpc/altivec8.c +$(obj)/powerpc/altivec%.c: $(src)/powerpc/altivec.uc $(src)/unroll.awk FORCE + $(call if_changed,unroll) + +targets += powerpc/vpermxor1.c \ + powerpc/vpermxor2.c \ + powerpc/vpermxor4.c \ + powerpc/vpermxor8.c +$(obj)/powerpc/vpermxor%.c: $(src)/powerpc/vpermxor.uc $(src)/unroll.awk FORCE + $(call if_changed,unroll) + +targets += s390/s390vx8.c +$(obj)/s390/s390vx%.c: $(src)/s390/s390vx.uc $(src)/unroll.awk FORCE + $(call if_changed,unroll) diff --git a/lib/raid6/algos.c b/lib/raid/raid6/algos.c similarity index 100% rename from lib/raid6/algos.c rename to lib/raid/raid6/algos.c diff --git a/lib/raid6/neon.c b/lib/raid/raid6/arm/neon.c similarity index 100% rename from lib/raid6/neon.c rename to lib/raid/raid6/arm/neon.c diff --git a/lib/raid6/neon.h b/lib/raid/raid6/arm/neon.h similarity index 100% rename from lib/raid6/neon.h rename to lib/raid/raid6/arm/neon.h diff --git a/lib/raid6/neon.uc b/lib/raid/raid6/arm/neon.uc similarity index 99% rename from lib/raid6/neon.uc rename to lib/raid/raid6/arm/neon.uc index 355270af0cd6..14a9fc2c60fa 100644 --- a/lib/raid6/neon.uc +++ b/lib/raid/raid6/arm/neon.uc @@ -25,7 +25,7 @@ */ #include -#include "neon.h" +#include "arm/neon.h" typedef uint8x16_t unative_t; diff --git a/lib/raid6/recov_neon.c b/lib/raid/raid6/arm/recov_neon.c similarity index 99% rename from lib/raid6/recov_neon.c rename to lib/raid/raid6/arm/recov_neon.c index 13d5df718c15..5a48fcc762e8 100644 --- a/lib/raid6/recov_neon.c +++ b/lib/raid/raid6/arm/recov_neon.c @@ -6,7 +6,7 @@ #include #include -#include "neon.h" +#include "arm/neon.h" static int raid6_has_neon(void) { diff --git a/lib/raid6/recov_neon_inner.c b/lib/raid/raid6/arm/recov_neon_inner.c similarity index 99% rename from lib/raid6/recov_neon_inner.c rename to lib/raid/raid6/arm/recov_neon_inner.c index f9e7e8f5a151..53c355efa7ff 100644 --- a/lib/raid6/recov_neon_inner.c +++ b/lib/raid/raid6/arm/recov_neon_inner.c @@ -5,7 +5,7 @@ */ #include -#include "neon.h" +#include "arm/neon.h" #ifdef CONFIG_ARM /* diff --git a/lib/raid6/int.uc b/lib/raid/raid6/int.uc similarity index 100% rename from lib/raid6/int.uc rename to lib/raid/raid6/int.uc diff --git a/lib/raid6/loongarch_simd.c b/lib/raid/raid6/loongarch/loongarch_simd.c similarity index 100% rename from lib/raid6/loongarch_simd.c rename to lib/raid/raid6/loongarch/loongarch_simd.c diff --git a/lib/raid6/recov_loongarch_simd.c b/lib/raid/raid6/loongarch/recov_loongarch_simd.c similarity index 100% rename from lib/raid6/recov_loongarch_simd.c rename to lib/raid/raid6/loongarch/recov_loongarch_simd.c diff --git a/lib/raid6/mktables.c b/lib/raid/raid6/mktables.c similarity index 100% rename from lib/raid6/mktables.c rename to lib/raid/raid6/mktables.c diff --git a/lib/raid6/altivec.uc b/lib/raid/raid6/powerpc/altivec.uc similarity index 98% rename from lib/raid6/altivec.uc rename to lib/raid/raid6/powerpc/altivec.uc index 2c59963e58f9..130d3d3dd42c 100644 --- a/lib/raid6/altivec.uc +++ b/lib/raid/raid6/powerpc/altivec.uc @@ -24,8 +24,6 @@ #include -#ifdef CONFIG_ALTIVEC - #include #include #include @@ -122,5 +120,3 @@ const struct raid6_calls raid6_altivec$# = { "altivecx$#", 0 }; - -#endif /* CONFIG_ALTIVEC */ diff --git a/lib/raid6/vpermxor.uc b/lib/raid/raid6/powerpc/vpermxor.uc similarity index 98% rename from lib/raid6/vpermxor.uc rename to lib/raid/raid6/powerpc/vpermxor.uc index a8e76b1c956e..595f20aaf4cf 100644 --- a/lib/raid6/vpermxor.uc +++ b/lib/raid/raid6/powerpc/vpermxor.uc @@ -21,8 +21,6 @@ */ #include -#ifdef CONFIG_ALTIVEC - #include #include #include @@ -95,4 +93,3 @@ const struct raid6_calls raid6_vpermxor$# = { "vpermxor$#", 0 }; -#endif diff --git a/lib/raid6/recov.c b/lib/raid/raid6/recov.c similarity index 100% rename from lib/raid6/recov.c rename to lib/raid/raid6/recov.c diff --git a/lib/raid6/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c similarity index 100% rename from lib/raid6/recov_rvv.c rename to lib/raid/raid6/riscv/recov_rvv.c diff --git a/lib/raid6/rvv.c b/lib/raid/raid6/riscv/rvv.c similarity index 100% rename from lib/raid6/rvv.c rename to lib/raid/raid6/riscv/rvv.c diff --git a/lib/raid6/rvv.h b/lib/raid/raid6/riscv/rvv.h similarity index 100% rename from lib/raid6/rvv.h rename to lib/raid/raid6/riscv/rvv.h diff --git a/lib/raid6/recov_s390xc.c b/lib/raid/raid6/s390/recov_s390xc.c similarity index 100% rename from lib/raid6/recov_s390xc.c rename to lib/raid/raid6/s390/recov_s390xc.c diff --git a/lib/raid6/s390vx.uc b/lib/raid/raid6/s390/s390vx.uc similarity index 100% rename from lib/raid6/s390vx.uc rename to lib/raid/raid6/s390/s390vx.uc diff --git a/lib/raid6/test/Makefile b/lib/raid/raid6/tests/Makefile similarity index 77% rename from lib/raid6/test/Makefile rename to lib/raid/raid6/tests/Makefile index 520381ea71d7..87a001b22847 100644 --- a/lib/raid6/test/Makefile +++ b/lib/raid/raid6/tests/Makefile @@ -1,5 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_RAID6_PQ_KUNIT_TEST) += raid6_kunit.o - -raid6_kunit-y += test.o diff --git a/lib/raid6/test/test.c b/lib/raid/raid6/tests/raid6_kunit.c similarity index 100% rename from lib/raid6/test/test.c rename to lib/raid/raid6/tests/raid6_kunit.c diff --git a/lib/raid6/unroll.awk b/lib/raid/raid6/unroll.awk similarity index 100% rename from lib/raid6/unroll.awk rename to lib/raid/raid6/unroll.awk diff --git a/lib/raid6/avx2.c b/lib/raid/raid6/x86/avx2.c similarity index 100% rename from lib/raid6/avx2.c rename to lib/raid/raid6/x86/avx2.c diff --git a/lib/raid6/avx512.c b/lib/raid/raid6/x86/avx512.c similarity index 100% rename from lib/raid6/avx512.c rename to lib/raid/raid6/x86/avx512.c diff --git a/lib/raid6/mmx.c b/lib/raid/raid6/x86/mmx.c similarity index 99% rename from lib/raid6/mmx.c rename to lib/raid/raid6/x86/mmx.c index e411f0cfbd95..7e9810669347 100644 --- a/lib/raid6/mmx.c +++ b/lib/raid/raid6/x86/mmx.c @@ -11,8 +11,6 @@ * MMX implementation of RAID-6 syndrome functions */ -#ifdef CONFIG_X86_32 - #include #include @@ -135,5 +133,3 @@ const struct raid6_calls raid6_mmxx2 = { "mmxx2", 0 }; - -#endif diff --git a/lib/raid6/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c similarity index 100% rename from lib/raid6/recov_avx2.c rename to lib/raid/raid6/x86/recov_avx2.c diff --git a/lib/raid6/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c similarity index 100% rename from lib/raid6/recov_avx512.c rename to lib/raid/raid6/x86/recov_avx512.c diff --git a/lib/raid6/recov_ssse3.c b/lib/raid/raid6/x86/recov_ssse3.c similarity index 100% rename from lib/raid6/recov_ssse3.c rename to lib/raid/raid6/x86/recov_ssse3.c diff --git a/lib/raid6/sse1.c b/lib/raid/raid6/x86/sse1.c similarity index 99% rename from lib/raid6/sse1.c rename to lib/raid/raid6/x86/sse1.c index 794d5cfa0306..deecdd72ceec 100644 --- a/lib/raid6/sse1.c +++ b/lib/raid/raid6/x86/sse1.c @@ -16,8 +16,6 @@ * worthwhile as a separate implementation. */ -#ifdef CONFIG_X86_32 - #include #include @@ -155,5 +153,3 @@ const struct raid6_calls raid6_sse1x2 = { "sse1x2", 1 /* Has cache hints */ }; - -#endif diff --git a/lib/raid6/sse2.c b/lib/raid/raid6/x86/sse2.c similarity index 100% rename from lib/raid6/sse2.c rename to lib/raid/raid6/x86/sse2.c diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile deleted file mode 100644 index 6fd048c127b6..000000000000 --- a/lib/raid6/Makefile +++ /dev/null @@ -1,83 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_RAID6_PQ) += raid6_pq.o test/ - -raid6_pq-y += algos.o recov.o tables.o int1.o int2.o int4.o \ - int8.o - -raid6_pq-$(CONFIG_X86) += recov_ssse3.o recov_avx2.o mmx.o sse1.o sse2.o avx2.o avx512.o recov_avx512.o -raid6_pq-$(CONFIG_ALTIVEC) += altivec1.o altivec2.o altivec4.o altivec8.o \ - vpermxor1.o vpermxor2.o vpermxor4.o vpermxor8.o -raid6_pq-$(CONFIG_KERNEL_MODE_NEON) += neon.o neon1.o neon2.o neon4.o neon8.o recov_neon.o recov_neon_inner.o -raid6_pq-$(CONFIG_S390) += s390vx8.o recov_s390xc.o -raid6_pq-$(CONFIG_LOONGARCH) += loongarch_simd.o recov_loongarch_simd.o -raid6_pq-$(CONFIG_RISCV_ISA_V) += rvv.o recov_rvv.o - -hostprogs += mktables - -ifeq ($(CONFIG_ALTIVEC),y) -altivec_flags := -maltivec $(call cc-option,-mabi=altivec) -# Enable -altivec_flags += -isystem $(shell $(CC) -print-file-name=include) - -ifdef CONFIG_CC_IS_CLANG -# clang ppc port does not yet support -maltivec when -msoft-float is -# enabled. A future release of clang will resolve this -# https://llvm.org/pr31177 -CFLAGS_REMOVE_altivec1.o += -msoft-float -CFLAGS_REMOVE_altivec2.o += -msoft-float -CFLAGS_REMOVE_altivec4.o += -msoft-float -CFLAGS_REMOVE_altivec8.o += -msoft-float -CFLAGS_REMOVE_vpermxor1.o += -msoft-float -CFLAGS_REMOVE_vpermxor2.o += -msoft-float -CFLAGS_REMOVE_vpermxor4.o += -msoft-float -CFLAGS_REMOVE_vpermxor8.o += -msoft-float -endif -endif - -quiet_cmd_unroll = UNROLL $@ - cmd_unroll = $(AWK) -v N=$* -f $(src)/unroll.awk < $< > $@ - -targets += int1.c int2.c int4.c int8.c -$(obj)/int%.c: $(src)/int.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - -CFLAGS_altivec1.o += $(altivec_flags) -CFLAGS_altivec2.o += $(altivec_flags) -CFLAGS_altivec4.o += $(altivec_flags) -CFLAGS_altivec8.o += $(altivec_flags) -targets += altivec1.c altivec2.c altivec4.c altivec8.c -$(obj)/altivec%.c: $(src)/altivec.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - -CFLAGS_vpermxor1.o += $(altivec_flags) -CFLAGS_vpermxor2.o += $(altivec_flags) -CFLAGS_vpermxor4.o += $(altivec_flags) -CFLAGS_vpermxor8.o += $(altivec_flags) -targets += vpermxor1.c vpermxor2.c vpermxor4.c vpermxor8.c -$(obj)/vpermxor%.c: $(src)/vpermxor.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - -CFLAGS_neon1.o += $(CC_FLAGS_FPU) -CFLAGS_neon2.o += $(CC_FLAGS_FPU) -CFLAGS_neon4.o += $(CC_FLAGS_FPU) -CFLAGS_neon8.o += $(CC_FLAGS_FPU) -CFLAGS_recov_neon_inner.o += $(CC_FLAGS_FPU) -CFLAGS_REMOVE_neon1.o += $(CC_FLAGS_NO_FPU) -CFLAGS_REMOVE_neon2.o += $(CC_FLAGS_NO_FPU) -CFLAGS_REMOVE_neon4.o += $(CC_FLAGS_NO_FPU) -CFLAGS_REMOVE_neon8.o += $(CC_FLAGS_NO_FPU) -CFLAGS_REMOVE_recov_neon_inner.o += $(CC_FLAGS_NO_FPU) -targets += neon1.c neon2.c neon4.c neon8.c -$(obj)/neon%.c: $(src)/neon.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - -targets += s390vx8.c -$(obj)/s390vx%.c: $(src)/s390vx.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - -quiet_cmd_mktable = TABLE $@ - cmd_mktable = $(obj)/mktables > $@ - -targets += tables.c -$(obj)/tables.c: $(obj)/mktables FORCE - $(call if_changed,mktable) diff --git a/lib/raid6/test/.gitignore b/lib/raid6/test/.gitignore deleted file mode 100644 index 1b68a77f348f..000000000000 --- a/lib/raid6/test/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/int.uc -/neon.uc -/raid6test From 06d2a66fb7c0b33ce893edb2f695add88291bff2 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:47 +0200 Subject: [PATCH 057/108] raid6: remove unused defines in pq.h These are not used anywhere in the kernel. Link: https://lore.kernel.org/20260518051804.462141-5-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- include/linux/raid/pq.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index d26788fada58..5e7e743b83f5 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -90,12 +90,6 @@ extern const struct raid6_calls raid6_neonx8; extern const struct raid6_calls * const raid6_algos[]; extern const struct raid6_recov_calls *const raid6_recov_algos[]; -/* Return values from chk_syndrome */ -#define RAID6_OK 0 -#define RAID6_P_BAD 1 -#define RAID6_Q_BAD 2 -#define RAID6_PQ_BAD 3 - /* Galois field tables */ extern const u8 raid6_gfmul[256][256] __attribute__((aligned(256))); extern const u8 raid6_vgfmul[256][32] __attribute__((aligned(256))); From 885d314231837a58258edc2757def1ee7fa0138c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:48 +0200 Subject: [PATCH 058/108] raid6: remove raid6_get_zero_page Just open code it as in other places in the kernel. Link: https://lore.kernel.org/20260518051804.462141-6-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- crypto/async_tx/async_pq.c | 2 +- crypto/async_tx/async_raid6_recov.c | 4 ++-- include/linux/raid/pq.h | 6 ------ lib/raid/raid6/arm/recov_neon.c | 6 +++--- lib/raid/raid6/loongarch/recov_loongarch_simd.c | 12 ++++++------ lib/raid/raid6/recov.c | 6 +++--- lib/raid/raid6/riscv/recov_rvv.c | 6 +++--- lib/raid/raid6/s390/recov_s390xc.c | 6 +++--- lib/raid/raid6/x86/recov_avx2.c | 6 +++--- lib/raid/raid6/x86/recov_avx512.c | 6 +++--- lib/raid/raid6/x86/recov_ssse3.c | 6 +++--- 11 files changed, 30 insertions(+), 36 deletions(-) diff --git a/crypto/async_tx/async_pq.c b/crypto/async_tx/async_pq.c index 9e4bb7fbde25..0ce6f07b4e0d 100644 --- a/crypto/async_tx/async_pq.c +++ b/crypto/async_tx/async_pq.c @@ -119,7 +119,7 @@ do_sync_gen_syndrome(struct page **blocks, unsigned int *offsets, int disks, for (i = 0; i < disks; i++) { if (blocks[i] == NULL) { BUG_ON(i > disks - 3); /* P or Q can't be zero */ - srcs[i] = raid6_get_zero_page(); + srcs[i] = page_address(ZERO_PAGE(0)); } else { srcs[i] = page_address(blocks[i]) + offsets[i]; diff --git a/crypto/async_tx/async_raid6_recov.c b/crypto/async_tx/async_raid6_recov.c index 539ea5b378dc..f2dc6af6e6a7 100644 --- a/crypto/async_tx/async_raid6_recov.c +++ b/crypto/async_tx/async_raid6_recov.c @@ -414,7 +414,7 @@ async_raid6_2data_recov(int disks, size_t bytes, int faila, int failb, async_tx_quiesce(&submit->depend_tx); for (i = 0; i < disks; i++) if (blocks[i] == NULL) - ptrs[i] = raid6_get_zero_page(); + ptrs[i] = page_address(ZERO_PAGE(0)); else ptrs[i] = page_address(blocks[i]) + offs[i]; @@ -497,7 +497,7 @@ async_raid6_datap_recov(int disks, size_t bytes, int faila, async_tx_quiesce(&submit->depend_tx); for (i = 0; i < disks; i++) if (blocks[i] == NULL) - ptrs[i] = raid6_get_zero_page(); + ptrs[i] = page_address(ZERO_PAGE(0)); else ptrs[i] = page_address(blocks[i]) + offs[i]; diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index 5e7e743b83f5..f27a866c287f 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -11,12 +11,6 @@ #include #include -/* This should be const but the raid6 code is too convoluted for that. */ -static inline void *raid6_get_zero_page(void) -{ - return page_address(ZERO_PAGE(0)); -} - /* Routine choices */ struct raid6_calls { void (*gen_syndrome)(int, size_t, void **); diff --git a/lib/raid/raid6/arm/recov_neon.c b/lib/raid/raid6/arm/recov_neon.c index 5a48fcc762e8..9993bda5d3a6 100644 --- a/lib/raid/raid6/arm/recov_neon.c +++ b/lib/raid/raid6/arm/recov_neon.c @@ -29,10 +29,10 @@ static void raid6_2data_recov_neon(int disks, size_t bytes, int faila, * delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -66,7 +66,7 @@ static void raid6_datap_recov_neon(int disks, size_t bytes, int faila, * Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); diff --git a/lib/raid/raid6/loongarch/recov_loongarch_simd.c b/lib/raid/raid6/loongarch/recov_loongarch_simd.c index eb3a1e79f01f..4d4563209647 100644 --- a/lib/raid/raid6/loongarch/recov_loongarch_simd.c +++ b/lib/raid/raid6/loongarch/recov_loongarch_simd.c @@ -43,10 +43,10 @@ static void raid6_2data_recov_lsx(int disks, size_t bytes, int faila, * delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -198,7 +198,7 @@ static void raid6_datap_recov_lsx(int disks, size_t bytes, int faila, * Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -317,10 +317,10 @@ static void raid6_2data_recov_lasx(int disks, size_t bytes, int faila, * delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -437,7 +437,7 @@ static void raid6_datap_recov_lasx(int disks, size_t bytes, int faila, * Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); diff --git a/lib/raid/raid6/recov.c b/lib/raid/raid6/recov.c index 8d113196632e..211e1df28963 100644 --- a/lib/raid/raid6/recov.c +++ b/lib/raid/raid6/recov.c @@ -31,10 +31,10 @@ static void raid6_2data_recov_intx1(int disks, size_t bytes, int faila, Use the dead data pages as temporary storage for delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -72,7 +72,7 @@ static void raid6_datap_recov_intx1(int disks, size_t bytes, int faila, /* Compute syndrome with zero for the missing data page Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); diff --git a/lib/raid/raid6/riscv/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c index 40c393206b6a..f77d9c430687 100644 --- a/lib/raid/raid6/riscv/recov_rvv.c +++ b/lib/raid/raid6/riscv/recov_rvv.c @@ -158,10 +158,10 @@ static void raid6_2data_recov_rvv(int disks, size_t bytes, int faila, * delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -196,7 +196,7 @@ static void raid6_datap_recov_rvv(int disks, size_t bytes, int faila, * Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); diff --git a/lib/raid/raid6/s390/recov_s390xc.c b/lib/raid/raid6/s390/recov_s390xc.c index 487018f81192..0f32217b7123 100644 --- a/lib/raid/raid6/s390/recov_s390xc.c +++ b/lib/raid/raid6/s390/recov_s390xc.c @@ -34,10 +34,10 @@ static void raid6_2data_recov_s390xc(int disks, size_t bytes, int faila, Use the dead data pages as temporary storage for delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -81,7 +81,7 @@ static void raid6_datap_recov_s390xc(int disks, size_t bytes, int faila, /* Compute syndrome with zero for the missing data page Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); diff --git a/lib/raid/raid6/x86/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c index 19fbd9c4dce6..325310c81e1c 100644 --- a/lib/raid/raid6/x86/recov_avx2.c +++ b/lib/raid/raid6/x86/recov_avx2.c @@ -28,10 +28,10 @@ static void raid6_2data_recov_avx2(int disks, size_t bytes, int faila, Use the dead data pages as temporary storage for delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -196,7 +196,7 @@ static void raid6_datap_recov_avx2(int disks, size_t bytes, int faila, /* Compute syndrome with zero for the missing data page Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); diff --git a/lib/raid/raid6/x86/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c index 143f4976b2ad..08de77fcb8bd 100644 --- a/lib/raid/raid6/x86/recov_avx512.c +++ b/lib/raid/raid6/x86/recov_avx512.c @@ -37,10 +37,10 @@ static void raid6_2data_recov_avx512(int disks, size_t bytes, int faila, */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -238,7 +238,7 @@ static void raid6_datap_recov_avx512(int disks, size_t bytes, int faila, */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); diff --git a/lib/raid/raid6/x86/recov_ssse3.c b/lib/raid/raid6/x86/recov_ssse3.c index 146cdbf465bd..002bef1e0847 100644 --- a/lib/raid/raid6/x86/recov_ssse3.c +++ b/lib/raid/raid6/x86/recov_ssse3.c @@ -30,10 +30,10 @@ static void raid6_2data_recov_ssse3(int disks, size_t bytes, int faila, Use the dead data pages as temporary storage for delta p and delta q */ dp = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-2] = dp; dq = (u8 *)ptrs[failb]; - ptrs[failb] = raid6_get_zero_page(); + ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); @@ -203,7 +203,7 @@ static void raid6_datap_recov_ssse3(int disks, size_t bytes, int faila, /* Compute syndrome with zero for the missing data page Use the dead data page as temporary storage for delta q */ dq = (u8 *)ptrs[faila]; - ptrs[faila] = raid6_get_zero_page(); + ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; raid6_call.gen_syndrome(disks, bytes, ptrs); From 7e91f76a96686b3341c96e1e7f3e86c0f51e2cff Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:49 +0200 Subject: [PATCH 059/108] raid6: use named initializers for struct raid6_calls Link: https://lore.kernel.org/20260518051804.462141-7-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/arm/neon.c | 9 +++---- lib/raid/raid6/int.uc | 8 +++--- lib/raid/raid6/loongarch/loongarch_simd.c | 18 ++++++------- lib/raid/raid6/powerpc/altivec.uc | 8 +++--- lib/raid/raid6/powerpc/vpermxor.uc | 8 +++--- lib/raid/raid6/riscv/rvv.h | 9 +++---- lib/raid/raid6/s390/s390vx.uc | 10 +++---- lib/raid/raid6/x86/avx2.c | 33 ++++++++++++----------- lib/raid/raid6/x86/avx512.c | 33 ++++++++++++----------- lib/raid/raid6/x86/mmx.c | 16 +++++------ lib/raid/raid6/x86/sse1.c | 18 ++++++------- lib/raid/raid6/x86/sse2.c | 30 ++++++++++----------- 12 files changed, 95 insertions(+), 105 deletions(-) diff --git a/lib/raid/raid6/arm/neon.c b/lib/raid/raid6/arm/neon.c index 47b8bb0afc65..c21da59ab48f 100644 --- a/lib/raid/raid6/arm/neon.c +++ b/lib/raid/raid6/arm/neon.c @@ -40,11 +40,10 @@ start, stop, (unsigned long)bytes, ptrs);\ } \ struct raid6_calls const raid6_neonx ## _n = { \ - raid6_neon ## _n ## _gen_syndrome, \ - raid6_neon ## _n ## _xor_syndrome, \ - raid6_have_neon, \ - "neonx" #_n, \ - 0 \ + .gen_syndrome = raid6_neon ## _n ## _gen_syndrome, \ + .xor_syndrome = raid6_neon ## _n ## _xor_syndrome, \ + .valid = raid6_have_neon, \ + .name = "neonx" #_n, \ } static int raid6_have_neon(void) diff --git a/lib/raid/raid6/int.uc b/lib/raid/raid6/int.uc index 1ba56c3fa482..4f5f2869e21e 100644 --- a/lib/raid/raid6/int.uc +++ b/lib/raid/raid6/int.uc @@ -139,9 +139,7 @@ static void raid6_int$#_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_intx$# = { - raid6_int$#_gen_syndrome, - raid6_int$#_xor_syndrome, - NULL, /* always valid */ - "int" NSTRING "x$#", - 0 + .gen_syndrome = raid6_int$#_gen_syndrome, + .xor_syndrome = raid6_int$#_xor_syndrome, + .name = "int" NSTRING "x$#", }; diff --git a/lib/raid/raid6/loongarch/loongarch_simd.c b/lib/raid/raid6/loongarch/loongarch_simd.c index 72f4d92d4876..1b4cd1512d05 100644 --- a/lib/raid/raid6/loongarch/loongarch_simd.c +++ b/lib/raid/raid6/loongarch/loongarch_simd.c @@ -244,11 +244,10 @@ static void raid6_lsx_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_lsx = { - raid6_lsx_gen_syndrome, - raid6_lsx_xor_syndrome, - raid6_has_lsx, - "lsx", - .priority = 0 /* see the comment near the top of the file for reason */ + .gen_syndrome = raid6_lsx_gen_syndrome, + .xor_syndrome = raid6_lsx_xor_syndrome, + .valid = raid6_has_lsx, + .name = "lsx", }; #undef NSIZE @@ -413,11 +412,10 @@ static void raid6_lasx_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_lasx = { - raid6_lasx_gen_syndrome, - raid6_lasx_xor_syndrome, - raid6_has_lasx, - "lasx", - .priority = 0 /* see the comment near the top of the file for reason */ + .gen_syndrome = raid6_lasx_gen_syndrome, + .xor_syndrome = raid6_lasx_xor_syndrome, + .valid = raid6_has_lasx, + .name = "lasx", }; #undef NSIZE #endif /* CONFIG_CPU_HAS_LASX */ diff --git a/lib/raid/raid6/powerpc/altivec.uc b/lib/raid/raid6/powerpc/altivec.uc index 130d3d3dd42c..084ead768ddb 100644 --- a/lib/raid/raid6/powerpc/altivec.uc +++ b/lib/raid/raid6/powerpc/altivec.uc @@ -114,9 +114,7 @@ int raid6_have_altivec(void) #endif const struct raid6_calls raid6_altivec$# = { - raid6_altivec$#_gen_syndrome, - NULL, /* XOR not yet implemented */ - raid6_have_altivec, - "altivecx$#", - 0 + .gen_syndrome = raid6_altivec$#_gen_syndrome, + .valid = raid6_have_altivec, + .name = "altivecx$#", }; diff --git a/lib/raid/raid6/powerpc/vpermxor.uc b/lib/raid/raid6/powerpc/vpermxor.uc index 595f20aaf4cf..bb2c3a316ae8 100644 --- a/lib/raid/raid6/powerpc/vpermxor.uc +++ b/lib/raid/raid6/powerpc/vpermxor.uc @@ -87,9 +87,7 @@ int raid6_have_altivec_vpermxor(void) #endif const struct raid6_calls raid6_vpermxor$# = { - raid6_vpermxor$#_gen_syndrome, - NULL, - raid6_have_altivec_vpermxor, - "vpermxor$#", - 0 + .gen_syndrome = raid6_vpermxor$#_gen_syndrome, + .valid = raid6_have_altivec_vpermxor, + .name = "vpermxor$#", }; diff --git a/lib/raid/raid6/riscv/rvv.h b/lib/raid/raid6/riscv/rvv.h index b0a71b375962..0d430a4c5f08 100644 --- a/lib/raid/raid6/riscv/rvv.h +++ b/lib/raid/raid6/riscv/rvv.h @@ -39,9 +39,8 @@ static int rvv_has_vector(void) kernel_vector_end(); \ } \ struct raid6_calls const raid6_rvvx ## _n = { \ - raid6_rvv ## _n ## _gen_syndrome, \ - raid6_rvv ## _n ## _xor_syndrome, \ - rvv_has_vector, \ - "rvvx" #_n, \ - 0 \ + .gen_syndrome = raid6_rvv ## _n ## _gen_syndrome, \ + .xor_syndrome = raid6_rvv ## _n ## _xor_syndrome, \ + .valid = rvv_has_vector, \ + .name = "rvvx" #_n, \ } diff --git a/lib/raid/raid6/s390/s390vx.uc b/lib/raid/raid6/s390/s390vx.uc index 8aa53eb2f395..97c5d5d9dcf9 100644 --- a/lib/raid/raid6/s390/s390vx.uc +++ b/lib/raid/raid6/s390/s390vx.uc @@ -127,9 +127,9 @@ static int raid6_s390vx$#_valid(void) } const struct raid6_calls raid6_s390vx$# = { - raid6_s390vx$#_gen_syndrome, - raid6_s390vx$#_xor_syndrome, - raid6_s390vx$#_valid, - "vx128x$#", - 1 + .gen_syndrome = raid6_s390vx$#_gen_syndrome, + .xor_syndrome = raid6_s390vx$#_xor_syndrome, + .valid = raid6_s390vx$#_valid, + .name = "vx128x$#", + .priority = 1, }; diff --git a/lib/raid/raid6/x86/avx2.c b/lib/raid/raid6/x86/avx2.c index a1a5213918af..aab8b624c635 100644 --- a/lib/raid/raid6/x86/avx2.c +++ b/lib/raid/raid6/x86/avx2.c @@ -128,11 +128,12 @@ static void raid6_avx21_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_avx2x1 = { - raid6_avx21_gen_syndrome, - raid6_avx21_xor_syndrome, - raid6_have_avx2, - "avx2x1", - .priority = 2 /* Prefer AVX2 over priority 1 (SSE2 and others) */ + .gen_syndrome = raid6_avx21_gen_syndrome, + .xor_syndrome = raid6_avx21_xor_syndrome, + .valid = raid6_have_avx2, + .name = "avx2x1", + /* Prefer AVX2 over priority 1 (SSE2 and others) */ + .priority = 2, }; /* @@ -258,11 +259,12 @@ static void raid6_avx22_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_avx2x2 = { - raid6_avx22_gen_syndrome, - raid6_avx22_xor_syndrome, - raid6_have_avx2, - "avx2x2", - .priority = 2 /* Prefer AVX2 over priority 1 (SSE2 and others) */ + .gen_syndrome = raid6_avx22_gen_syndrome, + .xor_syndrome = raid6_avx22_xor_syndrome, + .valid = raid6_have_avx2, + .name = "avx2x2", + /* Prefer AVX2 over priority 1 (SSE2 and others) */ + .priority = 2, }; #ifdef CONFIG_X86_64 @@ -461,10 +463,11 @@ static void raid6_avx24_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_avx2x4 = { - raid6_avx24_gen_syndrome, - raid6_avx24_xor_syndrome, - raid6_have_avx2, - "avx2x4", - .priority = 2 /* Prefer AVX2 over priority 1 (SSE2 and others) */ + .gen_syndrome = raid6_avx24_gen_syndrome, + .xor_syndrome = raid6_avx24_xor_syndrome, + .valid = raid6_have_avx2, + .name = "avx2x4", + /* Prefer AVX2 over priority 1 (SSE2 and others) */ + .priority = 2, }; #endif /* CONFIG_X86_64 */ diff --git a/lib/raid/raid6/x86/avx512.c b/lib/raid/raid6/x86/avx512.c index 874998bcd7d7..47636b16632f 100644 --- a/lib/raid/raid6/x86/avx512.c +++ b/lib/raid/raid6/x86/avx512.c @@ -156,11 +156,12 @@ static void raid6_avx5121_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_avx512x1 = { - raid6_avx5121_gen_syndrome, - raid6_avx5121_xor_syndrome, - raid6_have_avx512, - "avx512x1", - .priority = 2 /* Prefer AVX512 over priority 1 (SSE2 and others) */ + .gen_syndrome = raid6_avx5121_gen_syndrome, + .xor_syndrome = raid6_avx5121_xor_syndrome, + .valid = raid6_have_avx512, + .name = "avx512x1", + /* Prefer AVX512 over priority 1 (SSE2 and others) */ + .priority = 2, }; /* @@ -313,11 +314,12 @@ static void raid6_avx5122_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_avx512x2 = { - raid6_avx5122_gen_syndrome, - raid6_avx5122_xor_syndrome, - raid6_have_avx512, - "avx512x2", - .priority = 2 /* Prefer AVX512 over priority 1 (SSE2 and others) */ + .gen_syndrome = raid6_avx5122_gen_syndrome, + .xor_syndrome = raid6_avx5122_xor_syndrome, + .valid = raid6_have_avx512, + .name = "avx512x2", + /* Prefer AVX512 over priority 1 (SSE2 and others) */ + .priority = 2, }; #ifdef CONFIG_X86_64 @@ -551,10 +553,11 @@ static void raid6_avx5124_xor_syndrome(int disks, int start, int stop, kernel_fpu_end(); } const struct raid6_calls raid6_avx512x4 = { - raid6_avx5124_gen_syndrome, - raid6_avx5124_xor_syndrome, - raid6_have_avx512, - "avx512x4", - .priority = 2 /* Prefer AVX512 over priority 1 (SSE2 and others) */ + .gen_syndrome = raid6_avx5124_gen_syndrome, + .xor_syndrome = raid6_avx5124_xor_syndrome, + .valid = raid6_have_avx512, + .name = "avx512x4", + /* Prefer AVX512 over priority 1 (SSE2 and others) */ + .priority = 2, }; #endif diff --git a/lib/raid/raid6/x86/mmx.c b/lib/raid/raid6/x86/mmx.c index 7e9810669347..22b9fdaa705f 100644 --- a/lib/raid/raid6/x86/mmx.c +++ b/lib/raid/raid6/x86/mmx.c @@ -68,11 +68,9 @@ static void raid6_mmx1_gen_syndrome(int disks, size_t bytes, void **ptrs) } const struct raid6_calls raid6_mmxx1 = { - raid6_mmx1_gen_syndrome, - NULL, /* XOR not yet implemented */ - raid6_have_mmx, - "mmxx1", - 0 + .gen_syndrome = raid6_mmx1_gen_syndrome, + .valid = raid6_have_mmx, + .name = "mmxx1", }; /* @@ -127,9 +125,7 @@ static void raid6_mmx2_gen_syndrome(int disks, size_t bytes, void **ptrs) } const struct raid6_calls raid6_mmxx2 = { - raid6_mmx2_gen_syndrome, - NULL, /* XOR not yet implemented */ - raid6_have_mmx, - "mmxx2", - 0 + .gen_syndrome = raid6_mmx2_gen_syndrome, + .valid = raid6_have_mmx, + .name = "mmxx2", }; diff --git a/lib/raid/raid6/x86/sse1.c b/lib/raid/raid6/x86/sse1.c index deecdd72ceec..fad214a430d8 100644 --- a/lib/raid/raid6/x86/sse1.c +++ b/lib/raid/raid6/x86/sse1.c @@ -84,11 +84,10 @@ static void raid6_sse11_gen_syndrome(int disks, size_t bytes, void **ptrs) } const struct raid6_calls raid6_sse1x1 = { - raid6_sse11_gen_syndrome, - NULL, /* XOR not yet implemented */ - raid6_have_sse1_or_mmxext, - "sse1x1", - 1 /* Has cache hints */ + .gen_syndrome = raid6_sse11_gen_syndrome, + .valid = raid6_have_sse1_or_mmxext, + .name = "sse1x1", + .priority = 1, /* Has cache hints */ }; /* @@ -147,9 +146,8 @@ static void raid6_sse12_gen_syndrome(int disks, size_t bytes, void **ptrs) } const struct raid6_calls raid6_sse1x2 = { - raid6_sse12_gen_syndrome, - NULL, /* XOR not yet implemented */ - raid6_have_sse1_or_mmxext, - "sse1x2", - 1 /* Has cache hints */ + .gen_syndrome = raid6_sse12_gen_syndrome, + .valid = raid6_have_sse1_or_mmxext, + .name = "sse1x2", + .priority = 1, /* Has cache hints */ }; diff --git a/lib/raid/raid6/x86/sse2.c b/lib/raid/raid6/x86/sse2.c index f9edf8a8d1c4..1b28e858a1d4 100644 --- a/lib/raid/raid6/x86/sse2.c +++ b/lib/raid/raid6/x86/sse2.c @@ -133,11 +133,11 @@ static void raid6_sse21_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_sse2x1 = { - raid6_sse21_gen_syndrome, - raid6_sse21_xor_syndrome, - raid6_have_sse2, - "sse2x1", - 1 /* Has cache hints */ + .gen_syndrome = raid6_sse21_gen_syndrome, + .xor_syndrome = raid6_sse21_xor_syndrome, + .valid = raid6_have_sse2, + .name = "sse2x1", + .priority = 1, /* Has cache hints */ }; /* @@ -263,11 +263,11 @@ static void raid6_sse22_xor_syndrome(int disks, int start, int stop, } const struct raid6_calls raid6_sse2x2 = { - raid6_sse22_gen_syndrome, - raid6_sse22_xor_syndrome, - raid6_have_sse2, - "sse2x2", - 1 /* Has cache hints */ + .gen_syndrome = raid6_sse22_gen_syndrome, + .xor_syndrome = raid6_sse22_xor_syndrome, + .valid = raid6_have_sse2, + .name = "sse2x2", + .priority = 1, /* Has cache hints */ }; #ifdef CONFIG_X86_64 @@ -470,11 +470,11 @@ static void raid6_sse24_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_sse2x4 = { - raid6_sse24_gen_syndrome, - raid6_sse24_xor_syndrome, - raid6_have_sse2, - "sse2x4", - 1 /* Has cache hints */ + .gen_syndrome = raid6_sse24_gen_syndrome, + .xor_syndrome = raid6_sse24_xor_syndrome, + .valid = raid6_have_sse2, + .name = "sse2x4", + .priority = 1, /* Has cache hints */ }; #endif /* CONFIG_X86_64 */ From 35472bc6f31b64e58d1fe560340aa4f1684b8d6d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:50 +0200 Subject: [PATCH 060/108] raid6: improve the public interface Stop directly calling into function pointers from users of the RAID6 PQ API, and provide exported functions with proper documentation and API guarantees asserts where applicable instead. Link: https://lore.kernel.org/20260518051804.462141-8-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- Documentation/crypto/async-tx-api.rst | 4 +- crypto/async_tx/async_pq.c | 6 +- crypto/async_tx/async_raid6_recov.c | 4 +- drivers/md/raid5.c | 4 +- fs/btrfs/raid56.c | 8 +- include/linux/raid/pq.h | 19 +-- lib/raid/raid6/algos.c | 137 +++++++++++++++++- lib/raid/raid6/arm/recov_neon.c | 4 +- .../raid6/loongarch/recov_loongarch_simd.c | 8 +- lib/raid/raid6/recov.c | 4 +- lib/raid/raid6/riscv/recov_rvv.c | 4 +- lib/raid/raid6/s390/recov_s390xc.c | 4 +- lib/raid/raid6/x86/recov_avx2.c | 4 +- lib/raid/raid6/x86/recov_avx512.c | 4 +- lib/raid/raid6/x86/recov_ssse3.c | 4 +- 15 files changed, 170 insertions(+), 48 deletions(-) diff --git a/Documentation/crypto/async-tx-api.rst b/Documentation/crypto/async-tx-api.rst index f88a7809385e..49fcfc66314a 100644 --- a/Documentation/crypto/async-tx-api.rst +++ b/Documentation/crypto/async-tx-api.rst @@ -82,9 +82,9 @@ xor_val xor a series of source buffers and set a flag if the pq generate the p+q (raid6 syndrome) from a series of source buffers pq_val validate that a p and or q buffer are in sync with a given series of sources -datap (raid6_datap_recov) recover a raid6 data block and the p block +datap (raid6_recov_datap) recover a raid6 data block and the p block from the given sources -2data (raid6_2data_recov) recover 2 raid6 data blocks from the given +2data (raid6_recov_2data) recover 2 raid6 data blocks from the given sources ======== ==================================================================== diff --git a/crypto/async_tx/async_pq.c b/crypto/async_tx/async_pq.c index 0ce6f07b4e0d..f3574f80d1df 100644 --- a/crypto/async_tx/async_pq.c +++ b/crypto/async_tx/async_pq.c @@ -131,11 +131,11 @@ do_sync_gen_syndrome(struct page **blocks, unsigned int *offsets, int disks, } } if (submit->flags & ASYNC_TX_PQ_XOR_DST) { - BUG_ON(!raid6_call.xor_syndrome); + BUG_ON(!raid6_can_xor_syndrome()); if (start >= 0) - raid6_call.xor_syndrome(disks, start, stop, len, srcs); + raid6_xor_syndrome(disks, start, stop, len, srcs); } else - raid6_call.gen_syndrome(disks, len, srcs); + raid6_gen_syndrome(disks, len, srcs); async_tx_sync_epilog(submit); } diff --git a/crypto/async_tx/async_raid6_recov.c b/crypto/async_tx/async_raid6_recov.c index f2dc6af6e6a7..305ea1421a3e 100644 --- a/crypto/async_tx/async_raid6_recov.c +++ b/crypto/async_tx/async_raid6_recov.c @@ -418,7 +418,7 @@ async_raid6_2data_recov(int disks, size_t bytes, int faila, int failb, else ptrs[i] = page_address(blocks[i]) + offs[i]; - raid6_2data_recov(disks, bytes, faila, failb, ptrs); + raid6_recov_2data(disks, bytes, faila, failb, ptrs); async_tx_sync_epilog(submit); @@ -501,7 +501,7 @@ async_raid6_datap_recov(int disks, size_t bytes, int faila, else ptrs[i] = page_address(blocks[i]) + offs[i]; - raid6_datap_recov(disks, bytes, faila, ptrs); + raid6_recov_datap(disks, bytes, faila, ptrs); async_tx_sync_epilog(submit); diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 0d76e82f4506..ebcb19317670 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -6955,7 +6955,7 @@ raid5_store_rmw_level(struct mddev *mddev, const char *page, size_t len) if (kstrtoul(page, 10, &new)) return -EINVAL; - if (new != PARITY_DISABLE_RMW && !raid6_call.xor_syndrome) + if (new != PARITY_DISABLE_RMW && !raid6_can_xor_syndrome()) return -EINVAL; if (new != PARITY_DISABLE_RMW && @@ -7646,7 +7646,7 @@ static struct r5conf *setup_conf(struct mddev *mddev) conf->level = mddev->new_level; if (conf->level == 6) { conf->max_degraded = 2; - if (raid6_call.xor_syndrome) + if (raid6_can_xor_syndrome()) conf->rmw_level = PARITY_ENABLE_RMW; else conf->rmw_level = PARITY_DISABLE_RMW; diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index 08ee8f316d96..dabc9522e881 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -1410,7 +1410,7 @@ static void generate_pq_vertical_step(struct btrfs_raid_bio *rbio, unsigned int rbio_qstripe_paddr(rbio, sector_nr, step_nr)); assert_rbio(rbio); - raid6_call.gen_syndrome(rbio->real_stripes, step, pointers); + raid6_gen_syndrome(rbio->real_stripes, step, pointers); } else { /* raid5 */ memcpy(pointers[rbio->nr_data], pointers[0], step); @@ -1987,10 +1987,10 @@ static void recover_vertical_step(struct btrfs_raid_bio *rbio, } if (failb == rbio->real_stripes - 2) { - raid6_datap_recov(rbio->real_stripes, step, + raid6_recov_datap(rbio->real_stripes, step, faila, pointers); } else { - raid6_2data_recov(rbio->real_stripes, step, + raid6_recov_2data(rbio->real_stripes, step, faila, failb, pointers); } } else { @@ -2644,7 +2644,7 @@ static bool verify_one_parity_step(struct btrfs_raid_bio *rbio, if (has_qstripe) { assert_rbio(rbio); /* RAID6, call the library function to fill in our P/Q. */ - raid6_call.gen_syndrome(rbio->real_stripes, step, pointers); + raid6_gen_syndrome(rbio->real_stripes, step, pointers); } else { /* RAID5. */ memcpy(pointers[nr_data], pointers[0], step); diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index f27a866c287f..425a227591c0 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -11,6 +11,16 @@ #include #include +void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs); +void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes, + void **ptrs); +bool raid6_can_xor_syndrome(void); + +void raid6_recov_2data(int disks, size_t bytes, int faila, int failb, + void **ptrs); +void raid6_recov_datap(int disks, size_t bytes, int faila, + void **ptrs); + /* Routine choices */ struct raid6_calls { void (*gen_syndrome)(int, size_t, void **); @@ -20,9 +30,6 @@ struct raid6_calls { int priority; /* Relative priority ranking if non-zero */ }; -/* Selected algorithm */ -extern struct raid6_calls raid6_call; - /* Various routine sets */ extern const struct raid6_calls raid6_intx1; extern const struct raid6_calls raid6_intx2; @@ -92,10 +99,4 @@ extern const u8 raid6_gflog[256] __attribute__((aligned(256))); extern const u8 raid6_gfinv[256] __attribute__((aligned(256))); extern const u8 raid6_gfexi[256] __attribute__((aligned(256))); -/* Recovery routines */ -extern void (*raid6_2data_recov)(int disks, size_t bytes, int faila, int failb, - void **ptrs); -extern void (*raid6_datap_recov)(int disks, size_t bytes, int faila, - void **ptrs); - #endif /* LINUX_RAID_RAID6_H */ diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index 985c60bb00a4..b0ba31f6d48e 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -16,8 +16,83 @@ #include #include -struct raid6_calls raid6_call; -EXPORT_SYMBOL_GPL(raid6_call); +static const struct raid6_recov_calls *raid6_recov_algo; + +/* Selected algorithm */ +static struct raid6_calls raid6_call; + +/** + * raid6_gen_syndrome - generate RAID6 P/Q parity + * @disks: number of "disks" to operate on including parity + * @bytes: length in bytes of each vector + * @ptrs: @disks size array of memory pointers + * + * Generate @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and + * @ptrs[@disks - 1] respectively from the memory pointed to by @ptrs[0] to + * @ptrs[@disks - 3]. + * + * @disks must be at least 4, and the memory pointed to by each member of @ptrs + * must be at least 64-byte aligned. @bytes must be non-zero and a multiple of + * 512. + * + * See https://kernel.org/pub/linux/kernel/people/hpa/raid6.pdf for underlying + * algorithm. + */ +void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs) +{ + WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); + WARN_ON_ONCE(bytes & 511); + + raid6_call.gen_syndrome(disks, bytes, ptrs); +} +EXPORT_SYMBOL_GPL(raid6_gen_syndrome); + +/** + * raid6_xor_syndrome - update RAID6 P/Q parity + * @disks: number of "disks" to operate on including parity + * @start: first index into @disk to update + * @stop: last index into @disk to update + * @bytes: length in bytes of each vector + * @ptrs: @disks size array of memory pointers + * + * Update @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and + * @ptrs[@disks - 1] respectively for the memory pointed to by + * @ptrs[@start..@stop]. + * + * This is used to update parity in place using the following sequence: + * + * 1) call raid6_xor_syndrome(disk, start, stop, ...) for the existing data. + * 2) update the the data in @ptrs[@start..@stop]. + * 3) call raid6_xor_syndrome(disk, start, stop, ...) for the new data. + * + * Data between @start and @stop that is not changed should be filled + * with a pointer to the kernel zero page. + * + * @disks must be at least 4, and the memory pointed to by each member of @ptrs + * must be at least 64-byte aligned. @bytes must be non-zero and a multiple of + * 512. @stop must be larger or equal to @start. + */ +void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes, + void **ptrs) +{ + WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); + WARN_ON_ONCE(bytes & 511); + WARN_ON_ONCE(stop < start); + + raid6_call.xor_syndrome(disks, start, stop, bytes, ptrs); +} +EXPORT_SYMBOL_GPL(raid6_xor_syndrome); + +/* + * raid6_can_xor_syndrome - check if raid6_xor_syndrome() can be used + * + * Returns %true if raid6_can_xor_syndrome() can be used, else %false. + */ +bool raid6_can_xor_syndrome(void) +{ + return !!raid6_call.xor_syndrome; +} +EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome); const struct raid6_calls * const raid6_algos[] = { #if defined(__i386__) && !defined(__arch_um__) @@ -84,11 +159,58 @@ const struct raid6_calls * const raid6_algos[] = { }; EXPORT_SYMBOL_IF_KUNIT(raid6_algos); -void (*raid6_2data_recov)(int, size_t, int, int, void **); -EXPORT_SYMBOL_GPL(raid6_2data_recov); +/** + * raid6_recov_2data - recover two missing data disks + * @disks: number of "disks" to operate on including parity + * @bytes: length in bytes of each vector + * @faila: first failed data disk index + * @failb: second failed data disk index + * @ptrs: @disks size array of memory pointers + * + * Rebuild @bytes of missing data in @ptrs[@faila] and @ptrs[@failb] from the + * data in the remaining disks and the two parities pointed to by the other + * indices between 0 and @disks - 1 in @ptrs. @disks includes the data disks + * and the two parities. @faila must be smaller than @failb. + * + * Memory pointed to by each pointer in @ptrs must be page aligned and is + * limited to %PAGE_SIZE. + */ +void raid6_recov_2data(int disks, size_t bytes, int faila, int failb, + void **ptrs) +{ + WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); + WARN_ON_ONCE(bytes & 511); + WARN_ON_ONCE(bytes > PAGE_SIZE); + WARN_ON_ONCE(failb <= faila); -void (*raid6_datap_recov)(int, size_t, int, void **); -EXPORT_SYMBOL_GPL(raid6_datap_recov); + raid6_recov_algo->data2(disks, bytes, faila, failb, ptrs); +} +EXPORT_SYMBOL_GPL(raid6_recov_2data); + +/** + * raid6_recov_datap - recover a missing data disk and missing P-parity + * @disks: number of "disks" to operate on including parity + * @bytes: length in bytes of each vector + * @faila: failed data disk index + * @ptrs: @disks size array of memory pointers + * + * Rebuild @bytes of missing data in @ptrs[@faila] and the missing P-parity in + * @ptrs[@disks - 2] from the data in the remaining disks and the Q-parity + * pointed to by the other indices between 0 and @disks - 1 in @ptrs. @disks + * includes the data disks and the two parities. + * + * Memory pointed to by each pointer in @ptrs must be page aligned and is + * limited to %PAGE_SIZE. + */ +void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs) +{ + WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); + WARN_ON_ONCE(bytes & 511); + WARN_ON_ONCE(bytes > PAGE_SIZE); + + raid6_recov_algo->datap(disks, bytes, faila, ptrs); +} +EXPORT_SYMBOL_GPL(raid6_recov_datap); const struct raid6_recov_calls *const raid6_recov_algos[] = { #ifdef CONFIG_X86 @@ -133,8 +255,7 @@ static inline const struct raid6_recov_calls *raid6_choose_recov(void) best = *algo; if (best) { - raid6_2data_recov = best->data2; - raid6_datap_recov = best->datap; + raid6_recov_algo = best; pr_info("raid6: using %s recovery algorithm\n", best->name); } else diff --git a/lib/raid/raid6/arm/recov_neon.c b/lib/raid/raid6/arm/recov_neon.c index 9993bda5d3a6..4eb0efb44750 100644 --- a/lib/raid/raid6/arm/recov_neon.c +++ b/lib/raid/raid6/arm/recov_neon.c @@ -35,7 +35,7 @@ static void raid6_2data_recov_neon(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -69,7 +69,7 @@ static void raid6_datap_recov_neon(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; diff --git a/lib/raid/raid6/loongarch/recov_loongarch_simd.c b/lib/raid/raid6/loongarch/recov_loongarch_simd.c index 4d4563209647..7d4d349322b3 100644 --- a/lib/raid/raid6/loongarch/recov_loongarch_simd.c +++ b/lib/raid/raid6/loongarch/recov_loongarch_simd.c @@ -49,7 +49,7 @@ static void raid6_2data_recov_lsx(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -201,7 +201,7 @@ static void raid6_datap_recov_lsx(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; @@ -323,7 +323,7 @@ static void raid6_2data_recov_lasx(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -440,7 +440,7 @@ static void raid6_datap_recov_lasx(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; diff --git a/lib/raid/raid6/recov.c b/lib/raid/raid6/recov.c index 211e1df28963..cc7e4dc1eaa6 100644 --- a/lib/raid/raid6/recov.c +++ b/lib/raid/raid6/recov.c @@ -37,7 +37,7 @@ static void raid6_2data_recov_intx1(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -75,7 +75,7 @@ static void raid6_datap_recov_intx1(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; diff --git a/lib/raid/raid6/riscv/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c index f77d9c430687..3ff39826e33f 100644 --- a/lib/raid/raid6/riscv/recov_rvv.c +++ b/lib/raid/raid6/riscv/recov_rvv.c @@ -164,7 +164,7 @@ static void raid6_2data_recov_rvv(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -199,7 +199,7 @@ static void raid6_datap_recov_rvv(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks - 1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; diff --git a/lib/raid/raid6/s390/recov_s390xc.c b/lib/raid/raid6/s390/recov_s390xc.c index 0f32217b7123..2bc4c85174de 100644 --- a/lib/raid/raid6/s390/recov_s390xc.c +++ b/lib/raid/raid6/s390/recov_s390xc.c @@ -40,7 +40,7 @@ static void raid6_2data_recov_s390xc(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -84,7 +84,7 @@ static void raid6_datap_recov_s390xc(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; diff --git a/lib/raid/raid6/x86/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c index 325310c81e1c..bef82a38d8eb 100644 --- a/lib/raid/raid6/x86/recov_avx2.c +++ b/lib/raid/raid6/x86/recov_avx2.c @@ -34,7 +34,7 @@ static void raid6_2data_recov_avx2(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -199,7 +199,7 @@ static void raid6_datap_recov_avx2(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; diff --git a/lib/raid/raid6/x86/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c index 08de77fcb8bd..06c70e771eaa 100644 --- a/lib/raid/raid6/x86/recov_avx512.c +++ b/lib/raid/raid6/x86/recov_avx512.c @@ -43,7 +43,7 @@ static void raid6_2data_recov_avx512(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -241,7 +241,7 @@ static void raid6_datap_recov_avx512(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; diff --git a/lib/raid/raid6/x86/recov_ssse3.c b/lib/raid/raid6/x86/recov_ssse3.c index 002bef1e0847..5ca7d56f23d8 100644 --- a/lib/raid/raid6/x86/recov_ssse3.c +++ b/lib/raid/raid6/x86/recov_ssse3.c @@ -36,7 +36,7 @@ static void raid6_2data_recov_ssse3(int disks, size_t bytes, int faila, ptrs[failb] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dp; @@ -206,7 +206,7 @@ static void raid6_datap_recov_ssse3(int disks, size_t bytes, int faila, ptrs[faila] = page_address(ZERO_PAGE(0)); ptrs[disks-1] = dq; - raid6_call.gen_syndrome(disks, bytes, ptrs); + raid6_gen_syndrome(disks, bytes, ptrs); /* Restore pointer table */ ptrs[faila] = dq; From 2790045a62eb52a5052eed06ddcc10b03b007a39 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:51 +0200 Subject: [PATCH 061/108] raid6: warn when using less than four devices Quoting H. Peter Anvin who came up with the RAID6 P/Q algorithm, and who wrote the initial implementation, then still part of the md driver: The RAID-6 code has *never* supported only 3 units, and if it ever worked for *any* of the implementations it was purely by accident. Speaking as the original author I should know; this was deliberate as in some cases the degenerate case (3) would have required extra trays in the code to no user benefit. While md never allowed less than 4 devices, btrfs does. This new warning will trigger for such file systems, but given how it already causes havoc that is a good thing. If btrfs wants to fix third, it should switch to transparently use three-way mirroring underneath, which will work as P and Q are copies of the single data device by the definition of the Linux RAID 6 P/Q algorithm. Link: https://lore.kernel.org/20260518051804.462141-9-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- include/linux/raid/pq.h | 2 ++ lib/raid/raid6/algos.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index 425a227591c0..87e3cb55bf07 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -11,6 +11,8 @@ #include #include +#define RAID6_MIN_DISKS 4 + void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs); void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes, void **ptrs); diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index b0ba31f6d48e..63d1945ba63c 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -42,6 +42,7 @@ void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs) { WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); WARN_ON_ONCE(bytes & 511); + WARN_ON_ONCE(disks < RAID6_MIN_DISKS); raid6_call.gen_syndrome(disks, bytes, ptrs); } @@ -77,6 +78,7 @@ void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes, { WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count()); WARN_ON_ONCE(bytes & 511); + WARN_ON_ONCE(disks < RAID6_MIN_DISKS); WARN_ON_ONCE(stop < start); raid6_call.xor_syndrome(disks, start, stop, bytes, ptrs); From 769d603fc44f896e7f61de7f0cdb8b78d46bc8c8 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:52 +0200 Subject: [PATCH 062/108] raid6: hide internals Split out two new headers from the public pq.h: - lib/raid/raid6/algos.h contains the algorithm lists private to lib/raid/raid6 - include/linux/raid/pq_tables.h contains the tables also used by async_tx providers. The public include/linux/pq.h is now limited to the public interface for the consumers of the RAID6 PQ API. [hch@lst.de: remove duplicate ccflags-y line] Link: https://lore.kernel.org/20260527074539.2292913-2-hch@lst.de Link: https://lore.kernel.org/20260518051804.462141-10-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- crypto/async_tx/async_pq.c | 1 + crypto/async_tx/async_raid6_recov.c | 1 + drivers/dma/bcm-sba-raid.c | 1 + include/linux/raid/pq.h | 96 ++----------------- include/linux/raid/pq_tables.h | 19 ++++ lib/raid/raid6/algos.c | 3 +- lib/raid/raid6/algos.h | 82 ++++++++++++++++ lib/raid/raid6/arm/neon.c | 2 +- lib/raid/raid6/arm/recov_neon.c | 2 + lib/raid/raid6/int.uc | 2 +- lib/raid/raid6/loongarch/loongarch_simd.c | 2 +- .../raid6/loongarch/recov_loongarch_simd.c | 2 + lib/raid/raid6/mktables.c | 2 +- lib/raid/raid6/powerpc/altivec.uc | 2 +- lib/raid/raid6/powerpc/vpermxor.uc | 2 +- lib/raid/raid6/recov.c | 2 + lib/raid/raid6/riscv/recov_rvv.c | 2 + lib/raid/raid6/riscv/rvv.h | 2 +- lib/raid/raid6/s390/recov_s390xc.c | 2 + lib/raid/raid6/s390/s390vx.uc | 2 +- lib/raid/raid6/tests/raid6_kunit.c | 2 +- lib/raid/raid6/x86/avx2.c | 3 +- lib/raid/raid6/x86/avx512.c | 3 +- lib/raid/raid6/x86/mmx.c | 3 +- lib/raid/raid6/x86/recov_avx2.c | 2 + lib/raid/raid6/x86/recov_avx512.c | 2 + lib/raid/raid6/x86/recov_ssse3.c | 2 + lib/raid/raid6/x86/sse1.c | 3 +- lib/raid/raid6/x86/sse2.c | 3 +- 29 files changed, 149 insertions(+), 103 deletions(-) create mode 100644 include/linux/raid/pq_tables.h create mode 100644 lib/raid/raid6/algos.h diff --git a/crypto/async_tx/async_pq.c b/crypto/async_tx/async_pq.c index f3574f80d1df..27f99349e310 100644 --- a/crypto/async_tx/async_pq.c +++ b/crypto/async_tx/async_pq.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include diff --git a/crypto/async_tx/async_raid6_recov.c b/crypto/async_tx/async_raid6_recov.c index 305ea1421a3e..e53870d84bc5 100644 --- a/crypto/async_tx/async_raid6_recov.c +++ b/crypto/async_tx/async_raid6_recov.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/drivers/dma/bcm-sba-raid.c b/drivers/dma/bcm-sba-raid.c index ed037fa883f6..0de03611252e 100644 --- a/drivers/dma/bcm-sba-raid.c +++ b/drivers/dma/bcm-sba-raid.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "dmaengine.h" diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index 87e3cb55bf07..b3bf9339cd86 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -1,15 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -/* -*- linux-c -*- ------------------------------------------------------- * +/* + * Copyright 2003 H. Peter Anvin - All Rights Reserved * - * Copyright 2003 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ + * Public interface to the RAID6 P/Q calculation and recovery library. + */ +#ifndef LINUX_RAID_PQ_H +#define LINUX_RAID_PQ_H -#ifndef LINUX_RAID_RAID6_H -#define LINUX_RAID_RAID6_H - -#include -#include +#include #define RAID6_MIN_DISKS 4 @@ -23,82 +21,4 @@ void raid6_recov_2data(int disks, size_t bytes, int faila, int failb, void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs); -/* Routine choices */ -struct raid6_calls { - void (*gen_syndrome)(int, size_t, void **); - void (*xor_syndrome)(int, int, int, size_t, void **); - int (*valid)(void); /* Returns 1 if this routine set is usable */ - const char *name; /* Name of this routine set */ - int priority; /* Relative priority ranking if non-zero */ -}; - -/* Various routine sets */ -extern const struct raid6_calls raid6_intx1; -extern const struct raid6_calls raid6_intx2; -extern const struct raid6_calls raid6_intx4; -extern const struct raid6_calls raid6_intx8; -extern const struct raid6_calls raid6_mmxx1; -extern const struct raid6_calls raid6_mmxx2; -extern const struct raid6_calls raid6_sse1x1; -extern const struct raid6_calls raid6_sse1x2; -extern const struct raid6_calls raid6_sse2x1; -extern const struct raid6_calls raid6_sse2x2; -extern const struct raid6_calls raid6_sse2x4; -extern const struct raid6_calls raid6_altivec1; -extern const struct raid6_calls raid6_altivec2; -extern const struct raid6_calls raid6_altivec4; -extern const struct raid6_calls raid6_altivec8; -extern const struct raid6_calls raid6_avx2x1; -extern const struct raid6_calls raid6_avx2x2; -extern const struct raid6_calls raid6_avx2x4; -extern const struct raid6_calls raid6_avx512x1; -extern const struct raid6_calls raid6_avx512x2; -extern const struct raid6_calls raid6_avx512x4; -extern const struct raid6_calls raid6_s390vx8; -extern const struct raid6_calls raid6_vpermxor1; -extern const struct raid6_calls raid6_vpermxor2; -extern const struct raid6_calls raid6_vpermxor4; -extern const struct raid6_calls raid6_vpermxor8; -extern const struct raid6_calls raid6_lsx; -extern const struct raid6_calls raid6_lasx; -extern const struct raid6_calls raid6_rvvx1; -extern const struct raid6_calls raid6_rvvx2; -extern const struct raid6_calls raid6_rvvx4; -extern const struct raid6_calls raid6_rvvx8; - -struct raid6_recov_calls { - void (*data2)(int, size_t, int, int, void **); - void (*datap)(int, size_t, int, void **); - int (*valid)(void); - const char *name; - int priority; -}; - -extern const struct raid6_recov_calls raid6_recov_intx1; -extern const struct raid6_recov_calls raid6_recov_ssse3; -extern const struct raid6_recov_calls raid6_recov_avx2; -extern const struct raid6_recov_calls raid6_recov_avx512; -extern const struct raid6_recov_calls raid6_recov_s390xc; -extern const struct raid6_recov_calls raid6_recov_neon; -extern const struct raid6_recov_calls raid6_recov_lsx; -extern const struct raid6_recov_calls raid6_recov_lasx; -extern const struct raid6_recov_calls raid6_recov_rvv; - -extern const struct raid6_calls raid6_neonx1; -extern const struct raid6_calls raid6_neonx2; -extern const struct raid6_calls raid6_neonx4; -extern const struct raid6_calls raid6_neonx8; - -/* Algorithm list */ -extern const struct raid6_calls * const raid6_algos[]; -extern const struct raid6_recov_calls *const raid6_recov_algos[]; - -/* Galois field tables */ -extern const u8 raid6_gfmul[256][256] __attribute__((aligned(256))); -extern const u8 raid6_vgfmul[256][32] __attribute__((aligned(256))); -extern const u8 raid6_gfexp[256] __attribute__((aligned(256))); -extern const u8 raid6_gflog[256] __attribute__((aligned(256))); -extern const u8 raid6_gfinv[256] __attribute__((aligned(256))); -extern const u8 raid6_gfexi[256] __attribute__((aligned(256))); - -#endif /* LINUX_RAID_RAID6_H */ +#endif /* LINUX_RAID_PQ_H */ diff --git a/include/linux/raid/pq_tables.h b/include/linux/raid/pq_tables.h new file mode 100644 index 000000000000..7b1ebe675677 --- /dev/null +++ b/include/linux/raid/pq_tables.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright 2003 H. Peter Anvin - All Rights Reserved + * + * Galois field tables for the Linux RAID6 P/Q parity algorithm. + */ +#ifndef _LINUX_RAID_PQ_TABLES_H +#define _LINUX_RAID_PQ_TABLES_H + +#include + +extern const u8 raid6_gfmul[256][256] __attribute__((aligned(256))); +extern const u8 raid6_vgfmul[256][32] __attribute__((aligned(256))); +extern const u8 raid6_gfexp[256] __attribute__((aligned(256))); +extern const u8 raid6_gflog[256] __attribute__((aligned(256))); +extern const u8 raid6_gfinv[256] __attribute__((aligned(256))); +extern const u8 raid6_gfexi[256] __attribute__((aligned(256))); + +#endif /* _LINUX_RAID_PQ_TABLES_H */ diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index 63d1945ba63c..d83ca4dac864 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -11,10 +11,11 @@ * Algorithm list and algorithm selection for RAID-6 */ -#include #include #include +#include #include +#include "algos.h" static const struct raid6_recov_calls *raid6_recov_algo; diff --git a/lib/raid/raid6/algos.h b/lib/raid/raid6/algos.h new file mode 100644 index 000000000000..e5f1098d2179 --- /dev/null +++ b/lib/raid/raid6/algos.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright 2003 H. Peter Anvin - All Rights Reserved + */ +#ifndef _PQ_IMPL_H +#define _PQ_IMPL_H + +#include + +/* Routine choices */ +struct raid6_calls { + const char *name; + void (*gen_syndrome)(int disks, size_t bytes, void **ptrs); + void (*xor_syndrome)(int disks, int start, int stop, size_t bytes, + void **ptrs); + int (*valid)(void); /* Returns 1 if this routine set is usable */ + int priority; /* Relative priority ranking if non-zero */ +}; + +/* Various routine sets */ +extern const struct raid6_calls raid6_intx1; +extern const struct raid6_calls raid6_intx2; +extern const struct raid6_calls raid6_intx4; +extern const struct raid6_calls raid6_intx8; +extern const struct raid6_calls raid6_mmxx1; +extern const struct raid6_calls raid6_mmxx2; +extern const struct raid6_calls raid6_sse1x1; +extern const struct raid6_calls raid6_sse1x2; +extern const struct raid6_calls raid6_sse2x1; +extern const struct raid6_calls raid6_sse2x2; +extern const struct raid6_calls raid6_sse2x4; +extern const struct raid6_calls raid6_altivec1; +extern const struct raid6_calls raid6_altivec2; +extern const struct raid6_calls raid6_altivec4; +extern const struct raid6_calls raid6_altivec8; +extern const struct raid6_calls raid6_avx2x1; +extern const struct raid6_calls raid6_avx2x2; +extern const struct raid6_calls raid6_avx2x4; +extern const struct raid6_calls raid6_avx512x1; +extern const struct raid6_calls raid6_avx512x2; +extern const struct raid6_calls raid6_avx512x4; +extern const struct raid6_calls raid6_s390vx8; +extern const struct raid6_calls raid6_vpermxor1; +extern const struct raid6_calls raid6_vpermxor2; +extern const struct raid6_calls raid6_vpermxor4; +extern const struct raid6_calls raid6_vpermxor8; +extern const struct raid6_calls raid6_lsx; +extern const struct raid6_calls raid6_lasx; +extern const struct raid6_calls raid6_rvvx1; +extern const struct raid6_calls raid6_rvvx2; +extern const struct raid6_calls raid6_rvvx4; +extern const struct raid6_calls raid6_rvvx8; + +struct raid6_recov_calls { + const char *name; + void (*data2)(int disks, size_t bytes, int faila, int failb, + void **ptrs); + void (*datap)(int disks, size_t bytes, int faila, void **ptrs); + int (*valid)(void); + int priority; +}; + +extern const struct raid6_recov_calls raid6_recov_intx1; +extern const struct raid6_recov_calls raid6_recov_ssse3; +extern const struct raid6_recov_calls raid6_recov_avx2; +extern const struct raid6_recov_calls raid6_recov_avx512; +extern const struct raid6_recov_calls raid6_recov_s390xc; +extern const struct raid6_recov_calls raid6_recov_neon; +extern const struct raid6_recov_calls raid6_recov_lsx; +extern const struct raid6_recov_calls raid6_recov_lasx; +extern const struct raid6_recov_calls raid6_recov_rvv; + +extern const struct raid6_calls raid6_neonx1; +extern const struct raid6_calls raid6_neonx2; +extern const struct raid6_calls raid6_neonx4; +extern const struct raid6_calls raid6_neonx8; + +/* Algorithm list */ +extern const struct raid6_calls * const raid6_algos[]; +extern const struct raid6_recov_calls *const raid6_recov_algos[]; + +#endif /* _PQ_IMPL_H */ diff --git a/lib/raid/raid6/arm/neon.c b/lib/raid/raid6/arm/neon.c index c21da59ab48f..bd4ec4c86ee8 100644 --- a/lib/raid/raid6/arm/neon.c +++ b/lib/raid/raid6/arm/neon.c @@ -5,8 +5,8 @@ * Copyright (C) 2013 Linaro Ltd */ -#include #include +#include "algos.h" /* * There are 2 reasons these wrappers are kept in a separate compilation unit diff --git a/lib/raid/raid6/arm/recov_neon.c b/lib/raid/raid6/arm/recov_neon.c index 4eb0efb44750..e1d1d19fc9a8 100644 --- a/lib/raid/raid6/arm/recov_neon.c +++ b/lib/raid/raid6/arm/recov_neon.c @@ -4,8 +4,10 @@ * Copyright (C) 2017 Linaro Ltd. */ +#include #include #include +#include "algos.h" #include "arm/neon.h" static int raid6_has_neon(void) diff --git a/lib/raid/raid6/int.uc b/lib/raid/raid6/int.uc index 4f5f2869e21e..e63bd5a9c2ed 100644 --- a/lib/raid/raid6/int.uc +++ b/lib/raid/raid6/int.uc @@ -18,7 +18,7 @@ * This file is postprocessed using unroll.awk */ -#include +#include "algos.h" /* * This is the C data type to use diff --git a/lib/raid/raid6/loongarch/loongarch_simd.c b/lib/raid/raid6/loongarch/loongarch_simd.c index 1b4cd1512d05..f77d11ce676e 100644 --- a/lib/raid/raid6/loongarch/loongarch_simd.c +++ b/lib/raid/raid6/loongarch/loongarch_simd.c @@ -9,9 +9,9 @@ * Copyright 2002-2004 H. Peter Anvin */ -#include #include #include +#include "algos.h" /* * The vector algorithms are currently priority 0, which means the generic diff --git a/lib/raid/raid6/loongarch/recov_loongarch_simd.c b/lib/raid/raid6/loongarch/recov_loongarch_simd.c index 7d4d349322b3..0bbdc8b5c2e7 100644 --- a/lib/raid/raid6/loongarch/recov_loongarch_simd.c +++ b/lib/raid/raid6/loongarch/recov_loongarch_simd.c @@ -10,9 +10,11 @@ * Author: Jim Kukunas */ +#include #include #include #include +#include "algos.h" /* * Unlike with the syndrome calculation algorithms, there's no boot-time diff --git a/lib/raid/raid6/mktables.c b/lib/raid/raid6/mktables.c index 3de1dbf6846c..97a17493bbd8 100644 --- a/lib/raid/raid6/mktables.c +++ b/lib/raid/raid6/mktables.c @@ -57,7 +57,7 @@ int main(int argc, char *argv[]) uint8_t exptbl[256], invtbl[256]; printf("#include \n"); - printf("#include \n"); + printf("#include \"algos.h\"\n"); /* Compute multiplication table */ printf("\nconst u8 __attribute__((aligned(256)))\n" diff --git a/lib/raid/raid6/powerpc/altivec.uc b/lib/raid/raid6/powerpc/altivec.uc index 084ead768ddb..eb4a448cc88e 100644 --- a/lib/raid/raid6/powerpc/altivec.uc +++ b/lib/raid/raid6/powerpc/altivec.uc @@ -22,7 +22,7 @@ * bracked this with preempt_disable/enable or in a lock) */ -#include +#include "algos.h" #include #include diff --git a/lib/raid/raid6/powerpc/vpermxor.uc b/lib/raid/raid6/powerpc/vpermxor.uc index bb2c3a316ae8..ec61f30bec11 100644 --- a/lib/raid/raid6/powerpc/vpermxor.uc +++ b/lib/raid/raid6/powerpc/vpermxor.uc @@ -20,11 +20,11 @@ * This instruction was introduced in POWER8 - ISA v2.07. */ -#include #include #include #include #include +#include "algos.h" typedef vector unsigned char unative_t; #define NSIZE sizeof(unative_t) diff --git a/lib/raid/raid6/recov.c b/lib/raid/raid6/recov.c index cc7e4dc1eaa6..735ab4013771 100644 --- a/lib/raid/raid6/recov.c +++ b/lib/raid/raid6/recov.c @@ -13,7 +13,9 @@ * the syndrome.) */ +#include #include +#include "algos.h" /* Recover two failed data blocks. */ static void raid6_2data_recov_intx1(int disks, size_t bytes, int faila, diff --git a/lib/raid/raid6/riscv/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c index 3ff39826e33f..02120d245e22 100644 --- a/lib/raid/raid6/riscv/recov_rvv.c +++ b/lib/raid/raid6/riscv/recov_rvv.c @@ -4,7 +4,9 @@ * Author: Chunyan Zhang */ +#include #include +#include "algos.h" #include "rvv.h" static void __raid6_2data_recov_rvv(int bytes, u8 *p, u8 *q, u8 *dp, diff --git a/lib/raid/raid6/riscv/rvv.h b/lib/raid/raid6/riscv/rvv.h index 0d430a4c5f08..c293130d798b 100644 --- a/lib/raid/raid6/riscv/rvv.h +++ b/lib/raid/raid6/riscv/rvv.h @@ -7,8 +7,8 @@ * Definitions for RISC-V RAID-6 code */ -#include #include +#include "algos.h" static int rvv_has_vector(void) { diff --git a/lib/raid/raid6/s390/recov_s390xc.c b/lib/raid/raid6/s390/recov_s390xc.c index 2bc4c85174de..e7b3409f21e2 100644 --- a/lib/raid/raid6/s390/recov_s390xc.c +++ b/lib/raid/raid6/s390/recov_s390xc.c @@ -6,7 +6,9 @@ * Author(s): Martin Schwidefsky */ +#include #include +#include "algos.h" static inline void xor_block(u8 *p1, u8 *p2) { diff --git a/lib/raid/raid6/s390/s390vx.uc b/lib/raid/raid6/s390/s390vx.uc index 97c5d5d9dcf9..aba3515eacac 100644 --- a/lib/raid/raid6/s390/s390vx.uc +++ b/lib/raid/raid6/s390/s390vx.uc @@ -12,8 +12,8 @@ */ #include -#include #include +#include "algos.h" #define NSIZE 16 diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c index 9db287b4a48f..de6a866953c5 100644 --- a/lib/raid/raid6/tests/raid6_kunit.c +++ b/lib/raid/raid6/tests/raid6_kunit.c @@ -7,7 +7,7 @@ #include #include -#include +#include "../algos.h" MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); diff --git a/lib/raid/raid6/x86/avx2.c b/lib/raid/raid6/x86/avx2.c index aab8b624c635..0bf831799082 100644 --- a/lib/raid/raid6/x86/avx2.c +++ b/lib/raid/raid6/x86/avx2.c @@ -13,8 +13,9 @@ * */ -#include +#include #include +#include "algos.h" static const struct raid6_avx2_constants { u64 x1d[4]; diff --git a/lib/raid/raid6/x86/avx512.c b/lib/raid/raid6/x86/avx512.c index 47636b16632f..98ed42fb0a46 100644 --- a/lib/raid/raid6/x86/avx512.c +++ b/lib/raid/raid6/x86/avx512.c @@ -17,8 +17,9 @@ * */ -#include +#include #include +#include "algos.h" static const struct raid6_avx512_constants { u64 x1d[8]; diff --git a/lib/raid/raid6/x86/mmx.c b/lib/raid/raid6/x86/mmx.c index 22b9fdaa705f..052d9f010bfe 100644 --- a/lib/raid/raid6/x86/mmx.c +++ b/lib/raid/raid6/x86/mmx.c @@ -11,8 +11,9 @@ * MMX implementation of RAID-6 syndrome functions */ -#include +#include #include +#include "algos.h" /* Shared with raid6/sse1.c */ const struct raid6_mmx_constants { diff --git a/lib/raid/raid6/x86/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c index bef82a38d8eb..06c6e05763bc 100644 --- a/lib/raid/raid6/x86/recov_avx2.c +++ b/lib/raid/raid6/x86/recov_avx2.c @@ -4,8 +4,10 @@ * Author: Jim Kukunas */ +#include #include #include +#include "algos.h" static int raid6_has_avx2(void) { diff --git a/lib/raid/raid6/x86/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c index 06c70e771eaa..850bb962b514 100644 --- a/lib/raid/raid6/x86/recov_avx512.c +++ b/lib/raid/raid6/x86/recov_avx512.c @@ -6,8 +6,10 @@ * Author: Megha Dey */ +#include #include #include +#include "algos.h" static int raid6_has_avx512(void) { diff --git a/lib/raid/raid6/x86/recov_ssse3.c b/lib/raid/raid6/x86/recov_ssse3.c index 5ca7d56f23d8..95589c33003a 100644 --- a/lib/raid/raid6/x86/recov_ssse3.c +++ b/lib/raid/raid6/x86/recov_ssse3.c @@ -3,8 +3,10 @@ * Copyright (C) 2012 Intel Corporation */ +#include #include #include +#include "algos.h" static int raid6_has_ssse3(void) { diff --git a/lib/raid/raid6/x86/sse1.c b/lib/raid/raid6/x86/sse1.c index fad214a430d8..7004255a0bb1 100644 --- a/lib/raid/raid6/x86/sse1.c +++ b/lib/raid/raid6/x86/sse1.c @@ -16,8 +16,9 @@ * worthwhile as a separate implementation. */ -#include +#include #include +#include "algos.h" /* Defined in raid6/mmx.c */ extern const struct raid6_mmx_constants { diff --git a/lib/raid/raid6/x86/sse2.c b/lib/raid/raid6/x86/sse2.c index 1b28e858a1d4..f30be4ee14d0 100644 --- a/lib/raid/raid6/x86/sse2.c +++ b/lib/raid/raid6/x86/sse2.c @@ -12,8 +12,9 @@ * */ -#include +#include #include +#include "algos.h" static const struct raid6_sse_constants { u64 x1d[2]; From adfcf6e89bc1322c4a6cc37ad32e411cf0500622 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:53 +0200 Subject: [PATCH 063/108] raid6: rework registration of optimized algorithms Replace the static array of algorithms with a call to an architecture helper to register algorithms. This serves two purposes: it avoid having to register all algorithms in a single central place, and it removes the need for the priority field by just registering the algorithms that the architecture considers suitable for the currently running CPUs. [hch@lst.de: register avx512 after avx2] Link: https://lore.kernel.org/20260527074539.2292913-3-hch@lst.de Link: https://lore.kernel.org/20260518051804.462141-11-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/Kconfig | 11 + lib/raid/raid6/Makefile | 4 + lib/raid/raid6/algos.c | 305 ++++++++---------- lib/raid/raid6/algos.h | 69 +--- lib/raid/raid6/arm/neon.c | 6 - lib/raid/raid6/arm/pq_arch.h | 21 ++ lib/raid/raid6/arm/recov_neon.c | 7 - lib/raid/raid6/arm64/pq_arch.h | 1 + lib/raid/raid6/loongarch/loongarch_simd.c | 12 - lib/raid/raid6/loongarch/pq_arch.h | 23 ++ .../raid6/loongarch/recov_loongarch_simd.c | 14 - lib/raid/raid6/powerpc/altivec.uc | 10 - lib/raid/raid6/powerpc/pq_arch.h | 32 ++ lib/raid/raid6/powerpc/vpermxor.uc | 11 - lib/raid/raid6/recov.c | 2 - lib/raid/raid6/riscv/pq_arch.h | 21 ++ lib/raid/raid6/riscv/recov_rvv.c | 2 - lib/raid/raid6/riscv/rvv.h | 6 - lib/raid/raid6/s390/pq_arch.h | 15 + lib/raid/raid6/s390/recov_s390xc.c | 2 - lib/raid/raid6/s390/s390vx.uc | 7 - lib/raid/raid6/tests/raid6_kunit.c | 23 +- lib/raid/raid6/x86/avx2.c | 14 - lib/raid/raid6/x86/avx512.c | 19 -- lib/raid/raid6/x86/mmx.c | 8 - lib/raid/raid6/x86/pq_arch.h | 96 ++++++ lib/raid/raid6/x86/recov_avx2.c | 8 - lib/raid/raid6/x86/recov_avx512.c | 12 - lib/raid/raid6/x86/recov_ssse3.c | 9 - lib/raid/raid6/x86/sse1.c | 12 - lib/raid/raid6/x86/sse2.c | 15 - 31 files changed, 386 insertions(+), 411 deletions(-) create mode 100644 lib/raid/raid6/arm/pq_arch.h create mode 100644 lib/raid/raid6/arm64/pq_arch.h create mode 100644 lib/raid/raid6/loongarch/pq_arch.h create mode 100644 lib/raid/raid6/powerpc/pq_arch.h create mode 100644 lib/raid/raid6/riscv/pq_arch.h create mode 100644 lib/raid/raid6/s390/pq_arch.h create mode 100644 lib/raid/raid6/x86/pq_arch.h diff --git a/lib/raid/Kconfig b/lib/raid/Kconfig index e39f6d667792..978cd6ba08ac 100644 --- a/lib/raid/Kconfig +++ b/lib/raid/Kconfig @@ -32,6 +32,17 @@ config XOR_KUNIT_TEST config RAID6_PQ tristate +# selected by architectures that provide an optimized PQ implementation +config RAID6_PQ_ARCH + depends on RAID6_PQ + default y if KERNEL_MODE_NEON # arm32/arm64 + default y if LOONGARCH + default y if ALTIVEC # powerpc + default y if RISCV_ISA_V + default y if S390 + default y if X86 + bool + config RAID6_PQ_KUNIT_TEST tristate "KUnit tests for RAID6 PQ functions" if !KUNIT_ALL_TESTS depends on KUNIT diff --git a/lib/raid/raid6/Makefile b/lib/raid/raid6/Makefile index 7cb31b8a5c17..038d6c74d1ba 100644 --- a/lib/raid/raid6/Makefile +++ b/lib/raid/raid6/Makefile @@ -2,6 +2,10 @@ ccflags-y += -I $(src) +ifeq ($(CONFIG_RAID6_PQ_ARCH),y) +CFLAGS_algos.o += -I$(src)/$(SRCARCH) +endif + obj-$(CONFIG_RAID6_PQ) += raid6_pq.o tests/ raid6_pq-y += algos.o tables.o diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index d83ca4dac864..30d7cb34874f 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -17,6 +17,9 @@ #include #include "algos.h" +#define RAID6_MAX_ALGOS 16 +static const struct raid6_calls *raid6_algos[RAID6_MAX_ALGOS]; +static unsigned int raid6_nr_algos; static const struct raid6_recov_calls *raid6_recov_algo; /* Selected algorithm */ @@ -97,71 +100,6 @@ bool raid6_can_xor_syndrome(void) } EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome); -const struct raid6_calls * const raid6_algos[] = { -#if defined(__i386__) && !defined(__arch_um__) - &raid6_avx512x2, - &raid6_avx512x1, - &raid6_avx2x2, - &raid6_avx2x1, - &raid6_sse2x2, - &raid6_sse2x1, - &raid6_sse1x2, - &raid6_sse1x1, - &raid6_mmxx2, - &raid6_mmxx1, -#endif -#if defined(__x86_64__) && !defined(__arch_um__) - &raid6_avx512x4, - &raid6_avx512x2, - &raid6_avx512x1, - &raid6_avx2x4, - &raid6_avx2x2, - &raid6_avx2x1, - &raid6_sse2x4, - &raid6_sse2x2, - &raid6_sse2x1, -#endif -#ifdef CONFIG_ALTIVEC - &raid6_vpermxor8, - &raid6_vpermxor4, - &raid6_vpermxor2, - &raid6_vpermxor1, - &raid6_altivec8, - &raid6_altivec4, - &raid6_altivec2, - &raid6_altivec1, -#endif -#if defined(CONFIG_S390) - &raid6_s390vx8, -#endif -#ifdef CONFIG_KERNEL_MODE_NEON - &raid6_neonx8, - &raid6_neonx4, - &raid6_neonx2, - &raid6_neonx1, -#endif -#ifdef CONFIG_LOONGARCH -#ifdef CONFIG_CPU_HAS_LASX - &raid6_lasx, -#endif -#ifdef CONFIG_CPU_HAS_LSX - &raid6_lsx, -#endif -#endif -#ifdef CONFIG_RISCV_ISA_V - &raid6_rvvx1, - &raid6_rvvx2, - &raid6_rvvx4, - &raid6_rvvx8, -#endif - &raid6_intx8, - &raid6_intx4, - &raid6_intx2, - &raid6_intx1, - NULL -}; -EXPORT_SYMBOL_IF_KUNIT(raid6_algos); - /** * raid6_recov_2data - recover two missing data disks * @disks: number of "disks" to operate on including parity @@ -215,119 +153,57 @@ void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs) } EXPORT_SYMBOL_GPL(raid6_recov_datap); -const struct raid6_recov_calls *const raid6_recov_algos[] = { -#ifdef CONFIG_X86 - &raid6_recov_avx512, - &raid6_recov_avx2, - &raid6_recov_ssse3, -#endif -#ifdef CONFIG_S390 - &raid6_recov_s390xc, -#endif -#if defined(CONFIG_KERNEL_MODE_NEON) - &raid6_recov_neon, -#endif -#ifdef CONFIG_LOONGARCH -#ifdef CONFIG_CPU_HAS_LASX - &raid6_recov_lasx, -#endif -#ifdef CONFIG_CPU_HAS_LSX - &raid6_recov_lsx, -#endif -#endif -#ifdef CONFIG_RISCV_ISA_V - &raid6_recov_rvv, -#endif - &raid6_recov_intx1, - NULL -}; -EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algos); - #define RAID6_TIME_JIFFIES_LG2 4 #define RAID6_TEST_DISKS 8 #define RAID6_TEST_DISKS_ORDER 3 -static inline const struct raid6_recov_calls *raid6_choose_recov(void) +static int raid6_choose_gen(void *(*const dptrs)[RAID6_TEST_DISKS], + const int disks) { - const struct raid6_recov_calls *const *algo; - const struct raid6_recov_calls *best; + /* work on the second half of the disks */ + int start = (disks >> 1) - 1, stop = disks - 3; + const struct raid6_calls *best = NULL; + unsigned long bestgenperf = 0; + unsigned int i; - for (best = NULL, algo = raid6_recov_algos; *algo; algo++) - if (!best || (*algo)->priority > best->priority) - if (!(*algo)->valid || (*algo)->valid()) - best = *algo; + for (i = 0; i < raid6_nr_algos; i++) { + const struct raid6_calls *algo = raid6_algos[i]; + unsigned long perf = 0, j0, j1; - if (best) { - raid6_recov_algo = best; - - pr_info("raid6: using %s recovery algorithm\n", best->name); - } else - pr_err("raid6: Yikes! No recovery algorithm found!\n"); - - return best; -} - -static inline const struct raid6_calls *raid6_choose_gen( - void *(*const dptrs)[RAID6_TEST_DISKS], const int disks) -{ - unsigned long perf, bestgenperf, j0, j1; - int start = (disks>>1)-1, stop = disks-3; /* work on the second half of the disks */ - const struct raid6_calls *const *algo; - const struct raid6_calls *best; - - for (bestgenperf = 0, best = NULL, algo = raid6_algos; *algo; algo++) { - if (!best || (*algo)->priority >= best->priority) { - if ((*algo)->valid && !(*algo)->valid()) - continue; - - if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) { - best = *algo; - break; - } - - perf = 0; - - preempt_disable(); - j0 = jiffies; - while ((j1 = jiffies) == j0) - cpu_relax(); - while (time_before(jiffies, - j1 + (1<gen_syndrome(disks, PAGE_SIZE, *dptrs); - perf++; - } - preempt_enable(); - - if (perf > bestgenperf) { - bestgenperf = perf; - best = *algo; - } - pr_info("raid6: %-8s gen() %5ld MB/s\n", (*algo)->name, - (perf * HZ * (disks-2)) >> - (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2)); + preempt_disable(); + j0 = jiffies; + while ((j1 = jiffies) == j0) + cpu_relax(); + while (time_before(jiffies, + j1 + (1<gen_syndrome(disks, PAGE_SIZE, *dptrs); + perf++; } + preempt_enable(); + + if (perf > bestgenperf) { + bestgenperf = perf; + best = algo; + } + pr_info("raid6: %-8s gen() %5ld MB/s\n", algo->name, + (perf * HZ * (disks-2)) >> + (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2)); } if (!best) { pr_err("raid6: Yikes! No algorithm found!\n"); - goto out; + return -EINVAL; } raid6_call = *best; - if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) { - pr_info("raid6: skipped pq benchmark and selected %s\n", - best->name); - goto out; - } - pr_info("raid6: using algorithm %s gen() %ld MB/s\n", best->name, (bestgenperf * HZ * (disks - 2)) >> (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2)); if (best->xor_syndrome) { - perf = 0; + unsigned long perf = 0, j0, j1; preempt_disable(); j0 = jiffies; @@ -346,8 +222,7 @@ static inline const struct raid6_calls *raid6_choose_gen( (20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1)); } -out: - return best; + return 0; } @@ -357,12 +232,17 @@ static inline const struct raid6_calls *raid6_choose_gen( static int __init raid6_select_algo(void) { const int disks = RAID6_TEST_DISKS; - - const struct raid6_calls *gen_best; - const struct raid6_recov_calls *rec_best; char *disk_ptr, *p; void *dptrs[RAID6_TEST_DISKS]; int i, cycle; + int error; + + if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK) || raid6_nr_algos == 1) { + pr_info("raid6: skipped pq benchmark and selected %s\n", + raid6_algos[raid6_nr_algos - 1]->name); + raid6_call = *raid6_algos[raid6_nr_algos - 1]; + return 0; + } /* prepare the buffer and fill it circularly with gfmul table */ disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER); @@ -385,22 +265,109 @@ static int __init raid6_select_algo(void) memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536); /* select raid gen_syndrome function */ - gen_best = raid6_choose_gen(&dptrs, disks); - - /* select raid recover functions */ - rec_best = raid6_choose_recov(); + error = raid6_choose_gen(&dptrs, disks); free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER); - return gen_best && rec_best ? 0 : -EINVAL; + return error; } -static void raid6_exit(void) +/* + * Register a RAID6 P/Q generation algorithm. The most optimized/unrolled + * implementation should be registered last so it will be selected when the + * boot-time benchmark is disabled. + */ +void __init raid6_algo_add(const struct raid6_calls *algo) { - do { } while (0); + if (WARN_ON_ONCE(raid6_nr_algos == RAID6_MAX_ALGOS)) + return; + raid6_algos[raid6_nr_algos++] = algo; } -subsys_initcall(raid6_select_algo); +void __init raid6_algo_add_default(void) +{ + raid6_algo_add(&raid6_intx1); + raid6_algo_add(&raid6_intx2); + raid6_algo_add(&raid6_intx4); + raid6_algo_add(&raid6_intx8); +} + +void __init raid6_recov_algo_add(const struct raid6_recov_calls *algo) +{ + if (WARN_ON_ONCE(raid6_recov_algo)) + return; + raid6_recov_algo = algo; +} + +#ifdef CONFIG_RAID6_PQ_ARCH +#include "pq_arch.h" +#else +static inline void arch_raid6_init(void) +{ + raid6_algo_add_default(); +} +#endif /* CONFIG_RAID6_PQ_ARCH */ + +static int __init raid6_init(void) +{ + /* + * Architectures providing arch_raid6_init must add all PQ generation + * algorithms they want to consider in arch_raid6_init(), including + * the generic ones using raid6_algo_add_default() if wanted. + */ + arch_raid6_init(); + + /* + * Architectures don't have to set a recovery algorithm, we'll just pick + * the generic integer one if none was set. + */ + if (!raid6_recov_algo) + raid6_recov_algo = &raid6_recov_intx1; + pr_info("raid6: using %s recovery algorithm\n", raid6_recov_algo->name); + + return raid6_select_algo(); +} + +static void __exit raid6_exit(void) +{ +} + +subsys_initcall(raid6_init); module_exit(raid6_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("RAID6 Q-syndrome calculations"); + +#if IS_ENABLED(CONFIG_RAID6_PQ_KUNIT_TEST) +const struct raid6_calls *raid6_algo_find(unsigned int idx) +{ + if (idx >= raid6_nr_algos) { + /* + * Always include the simplest generic integer implementation in + * the unit tests as a baseline. + */ + if (idx == raid6_nr_algos && + raid6_algos[0] != &raid6_intx1) + return &raid6_intx1; + return NULL; + } + return raid6_algos[idx]; +} +EXPORT_SYMBOL_IF_KUNIT(raid6_algo_find); + +const struct raid6_recov_calls *raid6_recov_algo_find(unsigned int idx) +{ + switch (idx) { + case 0: + /* always test the generic integer implementation */ + return &raid6_recov_intx1; + case 1: + /* test the optimized implementation if there is one */ + if (raid6_recov_algo != &raid6_recov_intx1) + return raid6_recov_algo; + return NULL; + default: + return NULL; + } +} +EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algo_find); +#endif /* CONFIG_RAID6_PQ_KUNIT_TEST */ diff --git a/lib/raid/raid6/algos.h b/lib/raid/raid6/algos.h index e5f1098d2179..43f636be183f 100644 --- a/lib/raid/raid6/algos.h +++ b/lib/raid/raid6/algos.h @@ -5,6 +5,7 @@ #ifndef _PQ_IMPL_H #define _PQ_IMPL_H +#include #include /* Routine choices */ @@ -13,70 +14,28 @@ struct raid6_calls { void (*gen_syndrome)(int disks, size_t bytes, void **ptrs); void (*xor_syndrome)(int disks, int start, int stop, size_t bytes, void **ptrs); - int (*valid)(void); /* Returns 1 if this routine set is usable */ - int priority; /* Relative priority ranking if non-zero */ }; -/* Various routine sets */ -extern const struct raid6_calls raid6_intx1; -extern const struct raid6_calls raid6_intx2; -extern const struct raid6_calls raid6_intx4; -extern const struct raid6_calls raid6_intx8; -extern const struct raid6_calls raid6_mmxx1; -extern const struct raid6_calls raid6_mmxx2; -extern const struct raid6_calls raid6_sse1x1; -extern const struct raid6_calls raid6_sse1x2; -extern const struct raid6_calls raid6_sse2x1; -extern const struct raid6_calls raid6_sse2x2; -extern const struct raid6_calls raid6_sse2x4; -extern const struct raid6_calls raid6_altivec1; -extern const struct raid6_calls raid6_altivec2; -extern const struct raid6_calls raid6_altivec4; -extern const struct raid6_calls raid6_altivec8; -extern const struct raid6_calls raid6_avx2x1; -extern const struct raid6_calls raid6_avx2x2; -extern const struct raid6_calls raid6_avx2x4; -extern const struct raid6_calls raid6_avx512x1; -extern const struct raid6_calls raid6_avx512x2; -extern const struct raid6_calls raid6_avx512x4; -extern const struct raid6_calls raid6_s390vx8; -extern const struct raid6_calls raid6_vpermxor1; -extern const struct raid6_calls raid6_vpermxor2; -extern const struct raid6_calls raid6_vpermxor4; -extern const struct raid6_calls raid6_vpermxor8; -extern const struct raid6_calls raid6_lsx; -extern const struct raid6_calls raid6_lasx; -extern const struct raid6_calls raid6_rvvx1; -extern const struct raid6_calls raid6_rvvx2; -extern const struct raid6_calls raid6_rvvx4; -extern const struct raid6_calls raid6_rvvx8; - struct raid6_recov_calls { const char *name; void (*data2)(int disks, size_t bytes, int faila, int failb, void **ptrs); void (*datap)(int disks, size_t bytes, int faila, void **ptrs); - int (*valid)(void); - int priority; }; +void __init raid6_algo_add(const struct raid6_calls *algo); +void __init raid6_algo_add_default(void); +void __init raid6_recov_algo_add(const struct raid6_recov_calls *algo); + +/* for the kunit test */ +const struct raid6_calls *raid6_algo_find(unsigned int idx); +const struct raid6_recov_calls *raid6_recov_algo_find(unsigned int idx); + +/* generic implementations */ +extern const struct raid6_calls raid6_intx1; +extern const struct raid6_calls raid6_intx2; +extern const struct raid6_calls raid6_intx4; +extern const struct raid6_calls raid6_intx8; extern const struct raid6_recov_calls raid6_recov_intx1; -extern const struct raid6_recov_calls raid6_recov_ssse3; -extern const struct raid6_recov_calls raid6_recov_avx2; -extern const struct raid6_recov_calls raid6_recov_avx512; -extern const struct raid6_recov_calls raid6_recov_s390xc; -extern const struct raid6_recov_calls raid6_recov_neon; -extern const struct raid6_recov_calls raid6_recov_lsx; -extern const struct raid6_recov_calls raid6_recov_lasx; -extern const struct raid6_recov_calls raid6_recov_rvv; - -extern const struct raid6_calls raid6_neonx1; -extern const struct raid6_calls raid6_neonx2; -extern const struct raid6_calls raid6_neonx4; -extern const struct raid6_calls raid6_neonx8; - -/* Algorithm list */ -extern const struct raid6_calls * const raid6_algos[]; -extern const struct raid6_recov_calls *const raid6_recov_algos[]; #endif /* _PQ_IMPL_H */ diff --git a/lib/raid/raid6/arm/neon.c b/lib/raid/raid6/arm/neon.c index bd4ec4c86ee8..341c61af675e 100644 --- a/lib/raid/raid6/arm/neon.c +++ b/lib/raid/raid6/arm/neon.c @@ -42,15 +42,9 @@ struct raid6_calls const raid6_neonx ## _n = { \ .gen_syndrome = raid6_neon ## _n ## _gen_syndrome, \ .xor_syndrome = raid6_neon ## _n ## _xor_syndrome, \ - .valid = raid6_have_neon, \ .name = "neonx" #_n, \ } -static int raid6_have_neon(void) -{ - return cpu_has_neon(); -} - RAID6_NEON_WRAPPER(1); RAID6_NEON_WRAPPER(2); RAID6_NEON_WRAPPER(4); diff --git a/lib/raid/raid6/arm/pq_arch.h b/lib/raid/raid6/arm/pq_arch.h new file mode 100644 index 000000000000..3f876ea6749c --- /dev/null +++ b/lib/raid/raid6/arm/pq_arch.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +extern const struct raid6_calls raid6_neonx1; +extern const struct raid6_calls raid6_neonx2; +extern const struct raid6_calls raid6_neonx4; +extern const struct raid6_calls raid6_neonx8; +extern const struct raid6_recov_calls raid6_recov_neon; + +static __always_inline void __init arch_raid6_init(void) +{ + raid6_algo_add_default(); + if (cpu_has_neon()) { + raid6_algo_add(&raid6_neonx1); + raid6_algo_add(&raid6_neonx2); + raid6_algo_add(&raid6_neonx4); + raid6_algo_add(&raid6_neonx8); + raid6_recov_algo_add(&raid6_recov_neon); + } +} diff --git a/lib/raid/raid6/arm/recov_neon.c b/lib/raid/raid6/arm/recov_neon.c index e1d1d19fc9a8..1524050d09b7 100644 --- a/lib/raid/raid6/arm/recov_neon.c +++ b/lib/raid/raid6/arm/recov_neon.c @@ -10,11 +10,6 @@ #include "algos.h" #include "arm/neon.h" -static int raid6_has_neon(void) -{ - return cpu_has_neon(); -} - static void raid6_2data_recov_neon(int disks, size_t bytes, int faila, int failb, void **ptrs) { @@ -87,7 +82,5 @@ static void raid6_datap_recov_neon(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_neon = { .data2 = raid6_2data_recov_neon, .datap = raid6_datap_recov_neon, - .valid = raid6_has_neon, .name = "neon", - .priority = 10, }; diff --git a/lib/raid/raid6/arm64/pq_arch.h b/lib/raid/raid6/arm64/pq_arch.h new file mode 100644 index 000000000000..27ff564d7594 --- /dev/null +++ b/lib/raid/raid6/arm64/pq_arch.h @@ -0,0 +1 @@ +#include "arm/pq_arch.h" diff --git a/lib/raid/raid6/loongarch/loongarch_simd.c b/lib/raid/raid6/loongarch/loongarch_simd.c index f77d11ce676e..c1eb53fafd27 100644 --- a/lib/raid/raid6/loongarch/loongarch_simd.c +++ b/lib/raid/raid6/loongarch/loongarch_simd.c @@ -26,11 +26,6 @@ #ifdef CONFIG_CPU_HAS_LSX #define NSIZE 16 -static int raid6_has_lsx(void) -{ - return cpu_has_lsx; -} - static void raid6_lsx_gen_syndrome(int disks, size_t bytes, void **ptrs) { u8 **dptr = (u8 **)ptrs; @@ -246,7 +241,6 @@ static void raid6_lsx_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_lsx = { .gen_syndrome = raid6_lsx_gen_syndrome, .xor_syndrome = raid6_lsx_xor_syndrome, - .valid = raid6_has_lsx, .name = "lsx", }; @@ -256,11 +250,6 @@ const struct raid6_calls raid6_lsx = { #ifdef CONFIG_CPU_HAS_LASX #define NSIZE 32 -static int raid6_has_lasx(void) -{ - return cpu_has_lasx; -} - static void raid6_lasx_gen_syndrome(int disks, size_t bytes, void **ptrs) { u8 **dptr = (u8 **)ptrs; @@ -414,7 +403,6 @@ static void raid6_lasx_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_lasx = { .gen_syndrome = raid6_lasx_gen_syndrome, .xor_syndrome = raid6_lasx_xor_syndrome, - .valid = raid6_has_lasx, .name = "lasx", }; #undef NSIZE diff --git a/lib/raid/raid6/loongarch/pq_arch.h b/lib/raid/raid6/loongarch/pq_arch.h new file mode 100644 index 000000000000..ae443a4d7b69 --- /dev/null +++ b/lib/raid/raid6/loongarch/pq_arch.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +extern const struct raid6_calls raid6_lsx; +extern const struct raid6_calls raid6_lasx; + +extern const struct raid6_recov_calls raid6_recov_lsx; +extern const struct raid6_recov_calls raid6_recov_lasx; + +static __always_inline void __init arch_raid6_init(void) +{ + raid6_algo_add_default(); + if (IS_ENABLED(CONFIG_CPU_HAS_LSX) && cpu_has_lsx) + raid6_algo_add(&raid6_lsx); + if (IS_ENABLED(CONFIG_CPU_HAS_LASX) && cpu_has_lasx) + raid6_algo_add(&raid6_lasx); + + if (IS_ENABLED(CONFIG_CPU_HAS_LASX) && cpu_has_lasx) + raid6_recov_algo_add(&raid6_recov_lasx); + else if (IS_ENABLED(CONFIG_CPU_HAS_LSX) && cpu_has_lsx) + raid6_recov_algo_add(&raid6_recov_lsx); +} diff --git a/lib/raid/raid6/loongarch/recov_loongarch_simd.c b/lib/raid/raid6/loongarch/recov_loongarch_simd.c index 0bbdc8b5c2e7..87a2313bbb4f 100644 --- a/lib/raid/raid6/loongarch/recov_loongarch_simd.c +++ b/lib/raid/raid6/loongarch/recov_loongarch_simd.c @@ -24,11 +24,6 @@ */ #ifdef CONFIG_CPU_HAS_LSX -static int raid6_has_lsx(void) -{ - return cpu_has_lsx; -} - static void raid6_2data_recov_lsx(int disks, size_t bytes, int faila, int failb, void **ptrs) { @@ -291,18 +286,11 @@ static void raid6_datap_recov_lsx(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_lsx = { .data2 = raid6_2data_recov_lsx, .datap = raid6_datap_recov_lsx, - .valid = raid6_has_lsx, .name = "lsx", - .priority = 1, }; #endif /* CONFIG_CPU_HAS_LSX */ #ifdef CONFIG_CPU_HAS_LASX -static int raid6_has_lasx(void) -{ - return cpu_has_lasx; -} - static void raid6_2data_recov_lasx(int disks, size_t bytes, int faila, int failb, void **ptrs) { @@ -509,8 +497,6 @@ static void raid6_datap_recov_lasx(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_lasx = { .data2 = raid6_2data_recov_lasx, .datap = raid6_datap_recov_lasx, - .valid = raid6_has_lasx, .name = "lasx", - .priority = 2, }; #endif /* CONFIG_CPU_HAS_LASX */ diff --git a/lib/raid/raid6/powerpc/altivec.uc b/lib/raid/raid6/powerpc/altivec.uc index eb4a448cc88e..c5429fb71dd6 100644 --- a/lib/raid/raid6/powerpc/altivec.uc +++ b/lib/raid/raid6/powerpc/altivec.uc @@ -104,17 +104,7 @@ static void raid6_altivec$#_gen_syndrome(int disks, size_t bytes, void **ptrs) preempt_enable(); } -int raid6_have_altivec(void); -#if $# == 1 -int raid6_have_altivec(void) -{ - /* This assumes either all CPUs have Altivec or none does */ - return cpu_has_feature(CPU_FTR_ALTIVEC); -} -#endif - const struct raid6_calls raid6_altivec$# = { .gen_syndrome = raid6_altivec$#_gen_syndrome, - .valid = raid6_have_altivec, .name = "altivecx$#", }; diff --git a/lib/raid/raid6/powerpc/pq_arch.h b/lib/raid/raid6/powerpc/pq_arch.h new file mode 100644 index 000000000000..ea1878777ff2 --- /dev/null +++ b/lib/raid/raid6/powerpc/pq_arch.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +extern const struct raid6_calls raid6_altivec1; +extern const struct raid6_calls raid6_altivec2; +extern const struct raid6_calls raid6_altivec4; +extern const struct raid6_calls raid6_altivec8; +extern const struct raid6_calls raid6_vpermxor1; +extern const struct raid6_calls raid6_vpermxor2; +extern const struct raid6_calls raid6_vpermxor4; +extern const struct raid6_calls raid6_vpermxor8; + +static __always_inline void __init arch_raid6_init(void) +{ + raid6_algo_add_default(); + + /* This assumes either all CPUs have Altivec or none does */ + if (cpu_has_feature(CPU_FTR_ALTIVEC)) { + raid6_algo_add(&raid6_altivec1); + raid6_algo_add(&raid6_altivec2); + raid6_algo_add(&raid6_altivec4); + raid6_algo_add(&raid6_altivec8); + } + if (cpu_has_feature(CPU_FTR_ALTIVEC_COMP) && + cpu_has_feature(CPU_FTR_ARCH_207S)) { + raid6_algo_add(&raid6_vpermxor1); + raid6_algo_add(&raid6_vpermxor2); + raid6_algo_add(&raid6_vpermxor4); + raid6_algo_add(&raid6_vpermxor8); + } +} diff --git a/lib/raid/raid6/powerpc/vpermxor.uc b/lib/raid/raid6/powerpc/vpermxor.uc index ec61f30bec11..e8964361aaef 100644 --- a/lib/raid/raid6/powerpc/vpermxor.uc +++ b/lib/raid/raid6/powerpc/vpermxor.uc @@ -76,18 +76,7 @@ static void raid6_vpermxor$#_gen_syndrome(int disks, size_t bytes, void **ptrs) preempt_enable(); } -int raid6_have_altivec_vpermxor(void); -#if $# == 1 -int raid6_have_altivec_vpermxor(void) -{ - /* Check if arch has both altivec and the vpermxor instructions */ - return (cpu_has_feature(CPU_FTR_ALTIVEC_COMP) && - cpu_has_feature(CPU_FTR_ARCH_207S)); -} -#endif - const struct raid6_calls raid6_vpermxor$# = { .gen_syndrome = raid6_vpermxor$#_gen_syndrome, - .valid = raid6_have_altivec_vpermxor, .name = "vpermxor$#", }; diff --git a/lib/raid/raid6/recov.c b/lib/raid/raid6/recov.c index 735ab4013771..76eb2aef3667 100644 --- a/lib/raid/raid6/recov.c +++ b/lib/raid/raid6/recov.c @@ -97,7 +97,5 @@ static void raid6_datap_recov_intx1(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_intx1 = { .data2 = raid6_2data_recov_intx1, .datap = raid6_datap_recov_intx1, - .valid = NULL, .name = "intx1", - .priority = 0, }; diff --git a/lib/raid/raid6/riscv/pq_arch.h b/lib/raid/raid6/riscv/pq_arch.h new file mode 100644 index 000000000000..82f1a188f8c4 --- /dev/null +++ b/lib/raid/raid6/riscv/pq_arch.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +extern const struct raid6_calls raid6_rvvx1; +extern const struct raid6_calls raid6_rvvx2; +extern const struct raid6_calls raid6_rvvx4; +extern const struct raid6_calls raid6_rvvx8; +extern const struct raid6_recov_calls raid6_recov_rvv; + +static __always_inline void __init arch_raid6_init(void) +{ + raid6_algo_add_default(); + if (has_vector()) { + raid6_algo_add(&raid6_rvvx1); + raid6_algo_add(&raid6_rvvx2); + raid6_algo_add(&raid6_rvvx4); + raid6_algo_add(&raid6_rvvx8); + raid6_recov_algo_add(&raid6_recov_rvv); + } +} diff --git a/lib/raid/raid6/riscv/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c index 02120d245e22..2305940276dd 100644 --- a/lib/raid/raid6/riscv/recov_rvv.c +++ b/lib/raid/raid6/riscv/recov_rvv.c @@ -218,7 +218,5 @@ static void raid6_datap_recov_rvv(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_rvv = { .data2 = raid6_2data_recov_rvv, .datap = raid6_datap_recov_rvv, - .valid = rvv_has_vector, .name = "rvv", - .priority = 1, }; diff --git a/lib/raid/raid6/riscv/rvv.h b/lib/raid/raid6/riscv/rvv.h index c293130d798b..3a7c2468b1ea 100644 --- a/lib/raid/raid6/riscv/rvv.h +++ b/lib/raid/raid6/riscv/rvv.h @@ -10,11 +10,6 @@ #include #include "algos.h" -static int rvv_has_vector(void) -{ - return has_vector(); -} - #define RAID6_RVV_WRAPPER(_n) \ static void raid6_rvv ## _n ## _gen_syndrome(int disks, \ size_t bytes, void **ptrs) \ @@ -41,6 +36,5 @@ static int rvv_has_vector(void) struct raid6_calls const raid6_rvvx ## _n = { \ .gen_syndrome = raid6_rvv ## _n ## _gen_syndrome, \ .xor_syndrome = raid6_rvv ## _n ## _xor_syndrome, \ - .valid = rvv_has_vector, \ .name = "rvvx" #_n, \ } diff --git a/lib/raid/raid6/s390/pq_arch.h b/lib/raid/raid6/s390/pq_arch.h new file mode 100644 index 000000000000..95d14c342306 --- /dev/null +++ b/lib/raid/raid6/s390/pq_arch.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +extern const struct raid6_calls raid6_s390vx8; +extern const struct raid6_recov_calls raid6_recov_s390xc; + +static __always_inline void __init arch_raid6_init(void) +{ + if (cpu_has_vx()) + raid6_algo_add(&raid6_s390vx8); + else + raid6_algo_add_default(); + raid6_recov_algo_add(&raid6_recov_s390xc); +} diff --git a/lib/raid/raid6/s390/recov_s390xc.c b/lib/raid/raid6/s390/recov_s390xc.c index e7b3409f21e2..08d56896e5ea 100644 --- a/lib/raid/raid6/s390/recov_s390xc.c +++ b/lib/raid/raid6/s390/recov_s390xc.c @@ -112,7 +112,5 @@ static void raid6_datap_recov_s390xc(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_s390xc = { .data2 = raid6_2data_recov_s390xc, .datap = raid6_datap_recov_s390xc, - .valid = NULL, .name = "s390xc", - .priority = 1, }; diff --git a/lib/raid/raid6/s390/s390vx.uc b/lib/raid/raid6/s390/s390vx.uc index aba3515eacac..e5cf9054be2a 100644 --- a/lib/raid/raid6/s390/s390vx.uc +++ b/lib/raid/raid6/s390/s390vx.uc @@ -121,15 +121,8 @@ static void raid6_s390vx$#_xor_syndrome(int disks, int start, int stop, kernel_fpu_end(&vxstate, KERNEL_VXR); } -static int raid6_s390vx$#_valid(void) -{ - return cpu_has_vx(); -} - const struct raid6_calls raid6_s390vx$# = { .gen_syndrome = raid6_s390vx$#_gen_syndrome, .xor_syndrome = raid6_s390vx$#_xor_syndrome, - .valid = raid6_s390vx$#_valid, .name = "vx128x$#", - .priority = 1, }; diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c index de6a866953c5..4dea1c5acc96 100644 --- a/lib/raid/raid6/tests/raid6_kunit.c +++ b/lib/raid/raid6/tests/raid6_kunit.c @@ -88,19 +88,20 @@ static void test_disks(struct kunit *test, const struct raid6_calls *calls, static void raid6_test(struct kunit *test) { - const struct raid6_calls *const *algo; - const struct raid6_recov_calls *const *ra; int i, j, p1, p2; + unsigned int r, g; - for (ra = raid6_recov_algos; *ra; ra++) { - if ((*ra)->valid && !(*ra)->valid()) - continue; + for (r = 0; ; r++) { + const struct raid6_recov_calls *ra = raid6_recov_algo_find(r); - for (algo = raid6_algos; *algo; algo++) { - const struct raid6_calls *calls = *algo; + if (!ra) + break; - if (calls->valid && !calls->valid()) - continue; + for (g = 0; ; g++) { + const struct raid6_calls *calls = raid6_algo_find(g); + + if (!calls) + break; /* Nuke syndromes */ memset(data[NDISKS - 2], 0xee, PAGE_SIZE); @@ -112,7 +113,7 @@ static void raid6_test(struct kunit *test) for (i = 0; i < NDISKS-1; i++) for (j = i+1; j < NDISKS; j++) - test_disks(test, calls, *ra, i, j); + test_disks(test, calls, ra, i, j); if (!calls->xor_syndrome) continue; @@ -130,7 +131,7 @@ static void raid6_test(struct kunit *test) for (i = 0; i < NDISKS-1; i++) for (j = i+1; j < NDISKS; j++) test_disks(test, calls, - *ra, i, j); + ra, i, j); } } diff --git a/lib/raid/raid6/x86/avx2.c b/lib/raid/raid6/x86/avx2.c index 0bf831799082..7efd94e6a87a 100644 --- a/lib/raid/raid6/x86/avx2.c +++ b/lib/raid/raid6/x86/avx2.c @@ -24,11 +24,6 @@ static const struct raid6_avx2_constants { 0x1d1d1d1d1d1d1d1dULL, 0x1d1d1d1d1d1d1d1dULL,}, }; -static int raid6_have_avx2(void) -{ - return boot_cpu_has(X86_FEATURE_AVX2) && boot_cpu_has(X86_FEATURE_AVX); -} - /* * Plain AVX2 implementation */ @@ -131,10 +126,7 @@ static void raid6_avx21_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_avx2x1 = { .gen_syndrome = raid6_avx21_gen_syndrome, .xor_syndrome = raid6_avx21_xor_syndrome, - .valid = raid6_have_avx2, .name = "avx2x1", - /* Prefer AVX2 over priority 1 (SSE2 and others) */ - .priority = 2, }; /* @@ -262,10 +254,7 @@ static void raid6_avx22_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_avx2x2 = { .gen_syndrome = raid6_avx22_gen_syndrome, .xor_syndrome = raid6_avx22_xor_syndrome, - .valid = raid6_have_avx2, .name = "avx2x2", - /* Prefer AVX2 over priority 1 (SSE2 and others) */ - .priority = 2, }; #ifdef CONFIG_X86_64 @@ -466,9 +455,6 @@ static void raid6_avx24_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_avx2x4 = { .gen_syndrome = raid6_avx24_gen_syndrome, .xor_syndrome = raid6_avx24_xor_syndrome, - .valid = raid6_have_avx2, .name = "avx2x4", - /* Prefer AVX2 over priority 1 (SSE2 and others) */ - .priority = 2, }; #endif /* CONFIG_X86_64 */ diff --git a/lib/raid/raid6/x86/avx512.c b/lib/raid/raid6/x86/avx512.c index 98ed42fb0a46..0772e798b742 100644 --- a/lib/raid/raid6/x86/avx512.c +++ b/lib/raid/raid6/x86/avx512.c @@ -30,16 +30,6 @@ static const struct raid6_avx512_constants { 0x1d1d1d1d1d1d1d1dULL, 0x1d1d1d1d1d1d1d1dULL,}, }; -static int raid6_have_avx512(void) -{ - return boot_cpu_has(X86_FEATURE_AVX2) && - boot_cpu_has(X86_FEATURE_AVX) && - boot_cpu_has(X86_FEATURE_AVX512F) && - boot_cpu_has(X86_FEATURE_AVX512BW) && - boot_cpu_has(X86_FEATURE_AVX512VL) && - boot_cpu_has(X86_FEATURE_AVX512DQ); -} - static void raid6_avx5121_gen_syndrome(int disks, size_t bytes, void **ptrs) { u8 **dptr = (u8 **)ptrs; @@ -159,10 +149,7 @@ static void raid6_avx5121_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_avx512x1 = { .gen_syndrome = raid6_avx5121_gen_syndrome, .xor_syndrome = raid6_avx5121_xor_syndrome, - .valid = raid6_have_avx512, .name = "avx512x1", - /* Prefer AVX512 over priority 1 (SSE2 and others) */ - .priority = 2, }; /* @@ -317,10 +304,7 @@ static void raid6_avx5122_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_avx512x2 = { .gen_syndrome = raid6_avx5122_gen_syndrome, .xor_syndrome = raid6_avx5122_xor_syndrome, - .valid = raid6_have_avx512, .name = "avx512x2", - /* Prefer AVX512 over priority 1 (SSE2 and others) */ - .priority = 2, }; #ifdef CONFIG_X86_64 @@ -556,9 +540,6 @@ static void raid6_avx5124_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_avx512x4 = { .gen_syndrome = raid6_avx5124_gen_syndrome, .xor_syndrome = raid6_avx5124_xor_syndrome, - .valid = raid6_have_avx512, .name = "avx512x4", - /* Prefer AVX512 over priority 1 (SSE2 and others) */ - .priority = 2, }; #endif diff --git a/lib/raid/raid6/x86/mmx.c b/lib/raid/raid6/x86/mmx.c index 052d9f010bfe..3228c335965a 100644 --- a/lib/raid/raid6/x86/mmx.c +++ b/lib/raid/raid6/x86/mmx.c @@ -22,12 +22,6 @@ const struct raid6_mmx_constants { 0x1d1d1d1d1d1d1d1dULL, }; -static int raid6_have_mmx(void) -{ - /* Not really "boot_cpu" but "all_cpus" */ - return boot_cpu_has(X86_FEATURE_MMX); -} - /* * Plain MMX implementation */ @@ -70,7 +64,6 @@ static void raid6_mmx1_gen_syndrome(int disks, size_t bytes, void **ptrs) const struct raid6_calls raid6_mmxx1 = { .gen_syndrome = raid6_mmx1_gen_syndrome, - .valid = raid6_have_mmx, .name = "mmxx1", }; @@ -127,6 +120,5 @@ static void raid6_mmx2_gen_syndrome(int disks, size_t bytes, void **ptrs) const struct raid6_calls raid6_mmxx2 = { .gen_syndrome = raid6_mmx2_gen_syndrome, - .valid = raid6_have_mmx, .name = "mmxx2", }; diff --git a/lib/raid/raid6/x86/pq_arch.h b/lib/raid/raid6/x86/pq_arch.h new file mode 100644 index 000000000000..02f8843b0537 --- /dev/null +++ b/lib/raid/raid6/x86/pq_arch.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +extern const struct raid6_calls raid6_mmxx1; +extern const struct raid6_calls raid6_mmxx2; +extern const struct raid6_calls raid6_sse1x1; +extern const struct raid6_calls raid6_sse1x2; +extern const struct raid6_calls raid6_sse2x1; +extern const struct raid6_calls raid6_sse2x2; +extern const struct raid6_calls raid6_sse2x4; +extern const struct raid6_calls raid6_avx2x1; +extern const struct raid6_calls raid6_avx2x2; +extern const struct raid6_calls raid6_avx2x4; +extern const struct raid6_calls raid6_avx512x1; +extern const struct raid6_calls raid6_avx512x2; +extern const struct raid6_calls raid6_avx512x4; + +extern const struct raid6_recov_calls raid6_recov_ssse3; +extern const struct raid6_recov_calls raid6_recov_avx2; +extern const struct raid6_recov_calls raid6_recov_avx512; + +static inline int raid6_has_avx512(void) +{ + return boot_cpu_has(X86_FEATURE_AVX2) && + boot_cpu_has(X86_FEATURE_AVX) && + boot_cpu_has(X86_FEATURE_AVX512F) && + boot_cpu_has(X86_FEATURE_AVX512BW) && + boot_cpu_has(X86_FEATURE_AVX512VL) && + boot_cpu_has(X86_FEATURE_AVX512DQ); +} + +static inline bool raid6_has_avx2(void) +{ + return boot_cpu_has(X86_FEATURE_AVX2) && boot_cpu_has(X86_FEATURE_AVX); +} + +static inline bool raid6_has_ssse3(void) +{ + return boot_cpu_has(X86_FEATURE_XMM) && + boot_cpu_has(X86_FEATURE_XMM2) && + boot_cpu_has(X86_FEATURE_SSSE3); +} + +static inline bool raid6_has_sse2(void) +{ + return boot_cpu_has(X86_FEATURE_MMX) && + boot_cpu_has(X86_FEATURE_FXSR) && + boot_cpu_has(X86_FEATURE_XMM) && + boot_cpu_has(X86_FEATURE_XMM2); +} + +static inline bool raid6_has_sse1_or_mmxext(void) +{ + return boot_cpu_has(X86_FEATURE_MMX) && + (boot_cpu_has(X86_FEATURE_XMM) || + boot_cpu_has(X86_FEATURE_MMXEXT)); +} + +static __always_inline void __init arch_raid6_init(void) +{ + if (raid6_has_avx2()) { + raid6_algo_add(&raid6_avx2x1); + raid6_algo_add(&raid6_avx2x2); + if (IS_ENABLED(CONFIG_X86_64)) + raid6_algo_add(&raid6_avx2x4); + if (raid6_has_avx512()) { + raid6_algo_add(&raid6_avx512x1); + raid6_algo_add(&raid6_avx512x2); + if (IS_ENABLED(CONFIG_X86_64)) + raid6_algo_add(&raid6_avx512x4); + } + } else if (IS_ENABLED(CONFIG_X86_64) || raid6_has_sse2()) { + /* x86_64 can assume SSE2 as baseline */ + raid6_algo_add(&raid6_sse2x1); + raid6_algo_add(&raid6_sse2x2); + if (IS_ENABLED(CONFIG_X86_64)) + raid6_algo_add(&raid6_sse2x4); + } else { + raid6_algo_add_default(); + if (raid6_has_sse1_or_mmxext()) { + raid6_algo_add(&raid6_sse1x1); + raid6_algo_add(&raid6_sse1x2); + } else if (boot_cpu_has(X86_FEATURE_MMX)) { + raid6_algo_add(&raid6_mmxx1); + raid6_algo_add(&raid6_mmxx2); + } + } + + if (raid6_has_avx512()) + raid6_recov_algo_add(&raid6_recov_avx512); + else if (raid6_has_avx2()) + raid6_recov_algo_add(&raid6_recov_avx2); + else if (raid6_has_ssse3()) + raid6_recov_algo_add(&raid6_recov_ssse3); +} diff --git a/lib/raid/raid6/x86/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c index 06c6e05763bc..a714a780a2d8 100644 --- a/lib/raid/raid6/x86/recov_avx2.c +++ b/lib/raid/raid6/x86/recov_avx2.c @@ -9,12 +9,6 @@ #include #include "algos.h" -static int raid6_has_avx2(void) -{ - return boot_cpu_has(X86_FEATURE_AVX2) && - boot_cpu_has(X86_FEATURE_AVX); -} - static void raid6_2data_recov_avx2(int disks, size_t bytes, int faila, int failb, void **ptrs) { @@ -305,11 +299,9 @@ static void raid6_datap_recov_avx2(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_avx2 = { .data2 = raid6_2data_recov_avx2, .datap = raid6_datap_recov_avx2, - .valid = raid6_has_avx2, #ifdef CONFIG_X86_64 .name = "avx2x2", #else .name = "avx2x1", #endif - .priority = 2, }; diff --git a/lib/raid/raid6/x86/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c index 850bb962b514..ec72d5a30c01 100644 --- a/lib/raid/raid6/x86/recov_avx512.c +++ b/lib/raid/raid6/x86/recov_avx512.c @@ -11,16 +11,6 @@ #include #include "algos.h" -static int raid6_has_avx512(void) -{ - return boot_cpu_has(X86_FEATURE_AVX2) && - boot_cpu_has(X86_FEATURE_AVX) && - boot_cpu_has(X86_FEATURE_AVX512F) && - boot_cpu_has(X86_FEATURE_AVX512BW) && - boot_cpu_has(X86_FEATURE_AVX512VL) && - boot_cpu_has(X86_FEATURE_AVX512DQ); -} - static void raid6_2data_recov_avx512(int disks, size_t bytes, int faila, int failb, void **ptrs) { @@ -369,11 +359,9 @@ static void raid6_datap_recov_avx512(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_avx512 = { .data2 = raid6_2data_recov_avx512, .datap = raid6_datap_recov_avx512, - .valid = raid6_has_avx512, #ifdef CONFIG_X86_64 .name = "avx512x2", #else .name = "avx512x1", #endif - .priority = 3, }; diff --git a/lib/raid/raid6/x86/recov_ssse3.c b/lib/raid/raid6/x86/recov_ssse3.c index 95589c33003a..700bd2c865ec 100644 --- a/lib/raid/raid6/x86/recov_ssse3.c +++ b/lib/raid/raid6/x86/recov_ssse3.c @@ -8,13 +8,6 @@ #include #include "algos.h" -static int raid6_has_ssse3(void) -{ - return boot_cpu_has(X86_FEATURE_XMM) && - boot_cpu_has(X86_FEATURE_XMM2) && - boot_cpu_has(X86_FEATURE_SSSE3); -} - static void raid6_2data_recov_ssse3(int disks, size_t bytes, int faila, int failb, void **ptrs) { @@ -320,11 +313,9 @@ static void raid6_datap_recov_ssse3(int disks, size_t bytes, int faila, const struct raid6_recov_calls raid6_recov_ssse3 = { .data2 = raid6_2data_recov_ssse3, .datap = raid6_datap_recov_ssse3, - .valid = raid6_has_ssse3, #ifdef CONFIG_X86_64 .name = "ssse3x2", #else .name = "ssse3x1", #endif - .priority = 1, }; diff --git a/lib/raid/raid6/x86/sse1.c b/lib/raid/raid6/x86/sse1.c index 7004255a0bb1..6ebdcf824e00 100644 --- a/lib/raid/raid6/x86/sse1.c +++ b/lib/raid/raid6/x86/sse1.c @@ -25,14 +25,6 @@ extern const struct raid6_mmx_constants { u64 x1d; } raid6_mmx_constants; -static int raid6_have_sse1_or_mmxext(void) -{ - /* Not really boot_cpu but "all_cpus" */ - return boot_cpu_has(X86_FEATURE_MMX) && - (boot_cpu_has(X86_FEATURE_XMM) || - boot_cpu_has(X86_FEATURE_MMXEXT)); -} - /* * Plain SSE1 implementation */ @@ -86,9 +78,7 @@ static void raid6_sse11_gen_syndrome(int disks, size_t bytes, void **ptrs) const struct raid6_calls raid6_sse1x1 = { .gen_syndrome = raid6_sse11_gen_syndrome, - .valid = raid6_have_sse1_or_mmxext, .name = "sse1x1", - .priority = 1, /* Has cache hints */ }; /* @@ -148,7 +138,5 @@ static void raid6_sse12_gen_syndrome(int disks, size_t bytes, void **ptrs) const struct raid6_calls raid6_sse1x2 = { .gen_syndrome = raid6_sse12_gen_syndrome, - .valid = raid6_have_sse1_or_mmxext, .name = "sse1x2", - .priority = 1, /* Has cache hints */ }; diff --git a/lib/raid/raid6/x86/sse2.c b/lib/raid/raid6/x86/sse2.c index f30be4ee14d0..7049c8512f35 100644 --- a/lib/raid/raid6/x86/sse2.c +++ b/lib/raid/raid6/x86/sse2.c @@ -22,15 +22,6 @@ static const struct raid6_sse_constants { { 0x1d1d1d1d1d1d1d1dULL, 0x1d1d1d1d1d1d1d1dULL }, }; -static int raid6_have_sse2(void) -{ - /* Not really boot_cpu but "all_cpus" */ - return boot_cpu_has(X86_FEATURE_MMX) && - boot_cpu_has(X86_FEATURE_FXSR) && - boot_cpu_has(X86_FEATURE_XMM) && - boot_cpu_has(X86_FEATURE_XMM2); -} - /* * Plain SSE2 implementation */ @@ -136,9 +127,7 @@ static void raid6_sse21_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_sse2x1 = { .gen_syndrome = raid6_sse21_gen_syndrome, .xor_syndrome = raid6_sse21_xor_syndrome, - .valid = raid6_have_sse2, .name = "sse2x1", - .priority = 1, /* Has cache hints */ }; /* @@ -266,9 +255,7 @@ static void raid6_sse22_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_sse2x2 = { .gen_syndrome = raid6_sse22_gen_syndrome, .xor_syndrome = raid6_sse22_xor_syndrome, - .valid = raid6_have_sse2, .name = "sse2x2", - .priority = 1, /* Has cache hints */ }; #ifdef CONFIG_X86_64 @@ -473,9 +460,7 @@ static void raid6_sse24_xor_syndrome(int disks, int start, int stop, const struct raid6_calls raid6_sse2x4 = { .gen_syndrome = raid6_sse24_gen_syndrome, .xor_syndrome = raid6_sse24_xor_syndrome, - .valid = raid6_have_sse2, .name = "sse2x4", - .priority = 1, /* Has cache hints */ }; #endif /* CONFIG_X86_64 */ From 10f4b8e2a16452a7ead747c91a27ba3fabfe948d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:54 +0200 Subject: [PATCH 064/108] raid6: use static_call for gen_syndrom and xor_syndrom Avoid indirect calls for P/Q parity generation. Link: https://lore.kernel.org/20260518051804.462141-12-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/algos.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index 30d7cb34874f..ff99202cad46 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "algos.h" @@ -23,7 +24,8 @@ static unsigned int raid6_nr_algos; static const struct raid6_recov_calls *raid6_recov_algo; /* Selected algorithm */ -static struct raid6_calls raid6_call; +DEFINE_STATIC_CALL_NULL(raid6_gen_syndrome_impl, *raid6_intx1.gen_syndrome); +DEFINE_STATIC_CALL_NULL(raid6_xor_syndrome_impl, *raid6_intx1.xor_syndrome); /** * raid6_gen_syndrome - generate RAID6 P/Q parity @@ -48,7 +50,7 @@ void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs) WARN_ON_ONCE(bytes & 511); WARN_ON_ONCE(disks < RAID6_MIN_DISKS); - raid6_call.gen_syndrome(disks, bytes, ptrs); + static_call(raid6_gen_syndrome_impl)(disks, bytes, ptrs); } EXPORT_SYMBOL_GPL(raid6_gen_syndrome); @@ -85,7 +87,7 @@ void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes, WARN_ON_ONCE(disks < RAID6_MIN_DISKS); WARN_ON_ONCE(stop < start); - raid6_call.xor_syndrome(disks, start, stop, bytes, ptrs); + static_call(raid6_xor_syndrome_impl)(disks, start, stop, bytes, ptrs); } EXPORT_SYMBOL_GPL(raid6_xor_syndrome); @@ -96,7 +98,7 @@ EXPORT_SYMBOL_GPL(raid6_xor_syndrome); */ bool raid6_can_xor_syndrome(void) { - return !!raid6_call.xor_syndrome; + return !!static_call_query(raid6_xor_syndrome_impl); } EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome); @@ -195,7 +197,8 @@ static int raid6_choose_gen(void *(*const dptrs)[RAID6_TEST_DISKS], return -EINVAL; } - raid6_call = *best; + static_call_update(raid6_gen_syndrome_impl, best->gen_syndrome); + static_call_update(raid6_xor_syndrome_impl, best->xor_syndrome); pr_info("raid6: using algorithm %s gen() %ld MB/s\n", best->name, @@ -240,7 +243,10 @@ static int __init raid6_select_algo(void) if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK) || raid6_nr_algos == 1) { pr_info("raid6: skipped pq benchmark and selected %s\n", raid6_algos[raid6_nr_algos - 1]->name); - raid6_call = *raid6_algos[raid6_nr_algos - 1]; + static_call_update(raid6_gen_syndrome_impl, + raid6_algos[raid6_nr_algos - 1]->gen_syndrome); + static_call_update(raid6_xor_syndrome_impl, + raid6_algos[raid6_nr_algos - 1]->xor_syndrome); return 0; } From dd83de0341da9979d6e584cd10e44361a183c938 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:55 +0200 Subject: [PATCH 065/108] raid6: use static_call for raid6_recov_2data and raid6_recov_datap Avoid expensive indirect calls for the recovery routines as well. Link: https://lore.kernel.org/20260518051804.462141-13-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/algos.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index ff99202cad46..43e3309b5306 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -26,6 +26,8 @@ static const struct raid6_recov_calls *raid6_recov_algo; /* Selected algorithm */ DEFINE_STATIC_CALL_NULL(raid6_gen_syndrome_impl, *raid6_intx1.gen_syndrome); DEFINE_STATIC_CALL_NULL(raid6_xor_syndrome_impl, *raid6_intx1.xor_syndrome); +DEFINE_STATIC_CALL_NULL(raid6_recov_2data_impl, *raid6_recov_intx1.data2); +DEFINE_STATIC_CALL_NULL(raid6_recov_datap_impl, *raid6_recov_intx1.datap); /** * raid6_gen_syndrome - generate RAID6 P/Q parity @@ -126,7 +128,7 @@ void raid6_recov_2data(int disks, size_t bytes, int faila, int failb, WARN_ON_ONCE(bytes > PAGE_SIZE); WARN_ON_ONCE(failb <= faila); - raid6_recov_algo->data2(disks, bytes, faila, failb, ptrs); + static_call(raid6_recov_2data_impl)(disks, bytes, faila, failb, ptrs); } EXPORT_SYMBOL_GPL(raid6_recov_2data); @@ -151,7 +153,7 @@ void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs) WARN_ON_ONCE(bytes & 511); WARN_ON_ONCE(bytes > PAGE_SIZE); - raid6_recov_algo->datap(disks, bytes, faila, ptrs); + static_call(raid6_recov_datap_impl)(disks, bytes, faila, ptrs); } EXPORT_SYMBOL_GPL(raid6_recov_datap); @@ -329,6 +331,8 @@ static int __init raid6_init(void) */ if (!raid6_recov_algo) raid6_recov_algo = &raid6_recov_intx1; + static_call_update(raid6_recov_2data_impl, raid6_recov_algo->data2); + static_call_update(raid6_recov_datap_impl, raid6_recov_algo->datap); pr_info("raid6: using %s recovery algorithm\n", raid6_recov_algo->name); return raid6_select_algo(); From 30bf04bd13a58cd9b877589569aa0abd06f04e52 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:56 +0200 Subject: [PATCH 066/108] raid6: update top of file comments Drop the pointless mention of the file name, and use standard formatting for the top of file comments. Link: https://lore.kernel.org/20260518051804.462141-14-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/algos.c | 8 +------- lib/raid/raid6/arm/neon.c | 2 +- lib/raid/raid6/mktables.c | 12 +++--------- lib/raid/raid6/recov.c | 14 ++++---------- lib/raid/raid6/riscv/rvv.h | 2 -- lib/raid/raid6/x86/avx2.c | 15 +++++---------- lib/raid/raid6/x86/avx512.c | 22 ++++++++-------------- lib/raid/raid6/x86/mmx.c | 10 ++-------- lib/raid/raid6/x86/sse1.c | 18 ++++++------------ lib/raid/raid6/x86/sse2.c | 9 +-------- 10 files changed, 31 insertions(+), 81 deletions(-) diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index 43e3309b5306..a600d5853672 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -1,12 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright 2002 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * raid6/algos.c + * Copyright 2002 H. Peter Anvin - All Rights Reserved * * Algorithm list and algorithm selection for RAID-6 */ diff --git a/lib/raid/raid6/arm/neon.c b/lib/raid/raid6/arm/neon.c index 341c61af675e..af90869aaffc 100644 --- a/lib/raid/raid6/arm/neon.c +++ b/lib/raid/raid6/arm/neon.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * linux/lib/raid6/neon.c - RAID6 syndrome calculation using ARM NEON intrinsics + * RAID6 syndrome calculation using ARM NEON intrinsics * * Copyright (C) 2013 Linaro Ltd */ diff --git a/lib/raid/raid6/mktables.c b/lib/raid/raid6/mktables.c index 97a17493bbd8..b6327b562fdb 100644 --- a/lib/raid/raid6/mktables.c +++ b/lib/raid/raid6/mktables.c @@ -1,15 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * mktables.c + * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved * - * Make RAID-6 tables. This is a host user space program to be run at - * compile time. + * Make RAID-6 tables. This is a host user space program to be run at compile + * time. */ #include diff --git a/lib/raid/raid6/recov.c b/lib/raid/raid6/recov.c index 76eb2aef3667..3fa53bc3fde4 100644 --- a/lib/raid/raid6/recov.c +++ b/lib/raid/raid6/recov.c @@ -1,16 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright 2002 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * raid6/recov.c + * Copyright 2002 H. Peter Anvin - All Rights Reserved * - * RAID-6 data recovery in dual failure mode. In single failure mode, - * use the RAID-5 algorithm (or, in the case of Q failure, just reconstruct - * the syndrome.) + * RAID-6 data recovery in dual failure mode. In single failure mode, use the + * RAID-5 algorithm (or, in the case of Q failure, just reconstruct the + * syndrome.) */ #include diff --git a/lib/raid/raid6/riscv/rvv.h b/lib/raid/raid6/riscv/rvv.h index 3a7c2468b1ea..df0e3637cae8 100644 --- a/lib/raid/raid6/riscv/rvv.h +++ b/lib/raid/raid6/riscv/rvv.h @@ -2,8 +2,6 @@ /* * Copyright 2024 Institute of Software, CAS. * - * raid6/rvv.h - * * Definitions for RISC-V RAID-6 code */ diff --git a/lib/raid/raid6/x86/avx2.c b/lib/raid/raid6/x86/avx2.c index 7efd94e6a87a..7d829c669ea7 100644 --- a/lib/raid/raid6/x86/avx2.c +++ b/lib/raid/raid6/x86/avx2.c @@ -1,16 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright (C) 2012 Intel Corporation - * Author: Yuanhan Liu - * - * Based on sse2.c: Copyright 2002 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * AVX2 implementation of RAID-6 syndrome functions + * Copyright (C) 2012 Intel Corporation + * Author: Yuanhan Liu * + * Based on sse2.c: Copyright 2002 H. Peter Anvin - All Rights Reserved + * + * AVX2 implementation of RAID-6 syndrome functions */ #include diff --git a/lib/raid/raid6/x86/avx512.c b/lib/raid/raid6/x86/avx512.c index 0772e798b742..e671eb5bde63 100644 --- a/lib/raid/raid6/x86/avx512.c +++ b/lib/raid/raid6/x86/avx512.c @@ -1,20 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- -------------------------------------------------------- - * - * Copyright (C) 2016 Intel Corporation - * - * Author: Gayatri Kammela - * Author: Megha Dey - * - * Based on avx2.c: Copyright 2012 Yuanhan Liu All Rights Reserved - * Based on sse2.c: Copyright 2002 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- - */ - /* - * AVX512 implementation of RAID-6 syndrome functions + * Copyright (C) 2016 Intel Corporation * + * Author: Gayatri Kammela + * Author: Megha Dey + * + * Based on avx2.c: Copyright 2012 Yuanhan Liu All Rights Reserved + * Based on sse2.c: Copyright 2002 H. Peter Anvin - All Rights Reserved + * + * AVX512 implementation of RAID-6 syndrome functions */ #include diff --git a/lib/raid/raid6/x86/mmx.c b/lib/raid/raid6/x86/mmx.c index 3228c335965a..afa82536142d 100644 --- a/lib/raid/raid6/x86/mmx.c +++ b/lib/raid/raid6/x86/mmx.c @@ -1,14 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright 2002 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * raid6/mmx.c + * Copyright 2002 H. Peter Anvin - All Rights Reserved * - * MMX implementation of RAID-6 syndrome functions + * MMX implementation of RAID-6 syndrome functions. */ #include diff --git a/lib/raid/raid6/x86/sse1.c b/lib/raid/raid6/x86/sse1.c index 6ebdcf824e00..f4b260df522a 100644 --- a/lib/raid/raid6/x86/sse1.c +++ b/lib/raid/raid6/x86/sse1.c @@ -1,19 +1,13 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright 2002 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * raid6/sse1.c + * Copyright 2002 H. Peter Anvin - All Rights Reserved * - * SSE-1/MMXEXT implementation of RAID-6 syndrome functions + * SSE-1/MMXEXT implementation of RAID-6 syndrome functions. * - * This is really an MMX implementation, but it requires SSE-1 or - * AMD MMXEXT for prefetch support and a few other features. The - * support for nontemporal memory accesses is enough to make this - * worthwhile as a separate implementation. + * This is really an MMX implementation, but it requires SSE-1 or AMD MMXEXT for + * prefetch support and a few other features. The support for nontemporal + * memory accesses is enough to make this worthwhile as a separate + * implementation. */ #include diff --git a/lib/raid/raid6/x86/sse2.c b/lib/raid/raid6/x86/sse2.c index 7049c8512f35..43b09ce58270 100644 --- a/lib/raid/raid6/x86/sse2.c +++ b/lib/raid/raid6/x86/sse2.c @@ -1,15 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* -*- linux-c -*- ------------------------------------------------------- * - * - * Copyright 2002 H. Peter Anvin - All Rights Reserved - * - * ----------------------------------------------------------------------- */ - /* - * raid6/sse2.c + * Copyright 2002 H. Peter Anvin - All Rights Reserved * * SSE-2 implementation of RAID-6 syndrome functions - * */ #include From 2175395f76c3eb3b19f1ce9be54d9dc9b0e4e61e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:57 +0200 Subject: [PATCH 067/108] raid6_kunit: use KUNIT_CASE_PARAM The raid6 test combines various generation and recovery algorithms. Use KUNIT_CASE_PARAM and provide a generator that iterates over the possible combinations instead of looping inside a single test instance. Link: https://lore.kernel.org/20260518051804.462141-15-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/tests/raid6_kunit.c | 122 ++++++++++++++++------------- 1 file changed, 69 insertions(+), 53 deletions(-) diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c index 4dea1c5acc96..9d06ed9b90e4 100644 --- a/lib/raid/raid6/tests/raid6_kunit.c +++ b/lib/raid/raid6/tests/raid6_kunit.c @@ -21,6 +21,15 @@ static char data[NDISKS][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); static char recovi[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); static char recovj[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +struct test_args { + unsigned int recov_idx; + const struct raid6_recov_calls *recov; + unsigned int gen_idx; + const struct raid6_calls *gen; +}; + +static struct test_args args; + static void makedata(int start, int stop) { int i; @@ -43,9 +52,10 @@ static char member_type(int d) } } -static void test_disks(struct kunit *test, const struct raid6_calls *calls, - const struct raid6_recov_calls *ra, int faila, int failb) +static void test_recover(struct kunit *test, int faila, int failb) { + const struct test_args *ta = test->param_value; + memset(recovi, 0xf0, PAGE_SIZE); memset(recovj, 0xba, PAGE_SIZE); @@ -61,25 +71,23 @@ static void test_disks(struct kunit *test, const struct raid6_calls *calls, goto skip; /* P+Q failure. Just rebuild the syndrome. */ - calls->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs); + ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs); } else if (failb == NDISKS - 2) { /* data+P failure. */ - ra->datap(NDISKS, PAGE_SIZE, faila, dataptrs); + ta->recov->datap(NDISKS, PAGE_SIZE, faila, dataptrs); } else { /* data+data failure. */ - ra->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs); + ta->recov->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs); } KUNIT_EXPECT_MEMEQ_MSG(test, data[faila], recovi, PAGE_SIZE, - "algo=%-8s/%-8s faila miscompared: %3d[%c] (failb=%3d[%c])\n", - calls->name, ra->name, - faila, member_type(faila), - failb, member_type(failb)); + "faila miscompared: %3d[%c] (failb=%3d[%c])\n", + faila, member_type(faila), + failb, member_type(failb)); KUNIT_EXPECT_MEMEQ_MSG(test, data[failb], recovj, PAGE_SIZE, - "algo=%-8s/%-8s failb miscompared: %3d[%c] (faila=%3d[%c])\n", - calls->name, ra->name, - failb, member_type(failb), - faila, member_type(faila)); + "failb miscompared: %3d[%c] (faila=%3d[%c])\n", + failb, member_type(failb), + faila, member_type(faila)); skip: dataptrs[faila] = data[faila]; @@ -88,58 +96,66 @@ static void test_disks(struct kunit *test, const struct raid6_calls *calls, static void raid6_test(struct kunit *test) { + const struct test_args *ta = test->param_value; int i, j, p1, p2; - unsigned int r, g; - for (r = 0; ; r++) { - const struct raid6_recov_calls *ra = raid6_recov_algo_find(r); + /* Nuke syndromes */ + memset(data[NDISKS - 2], 0xee, PAGE_SIZE); + memset(data[NDISKS - 1], 0xee, PAGE_SIZE); - if (!ra) - break; + /* Generate assumed good syndrome */ + ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs); - for (g = 0; ; g++) { - const struct raid6_calls *calls = raid6_algo_find(g); + for (i = 0; i < NDISKS - 1; i++) + for (j = i + 1; j < NDISKS; j++) + test_recover(test, i, j); - if (!calls) - break; + if (!ta->gen->xor_syndrome) + return; - /* Nuke syndromes */ - memset(data[NDISKS - 2], 0xee, PAGE_SIZE); - memset(data[NDISKS - 1], 0xee, PAGE_SIZE); - - /* Generate assumed good syndrome */ - calls->gen_syndrome(NDISKS, PAGE_SIZE, - (void **)&dataptrs); - - for (i = 0; i < NDISKS-1; i++) - for (j = i+1; j < NDISKS; j++) - test_disks(test, calls, ra, i, j); - - if (!calls->xor_syndrome) - continue; - - for (p1 = 0; p1 < NDISKS-2; p1++) - for (p2 = p1; p2 < NDISKS-2; p2++) { - - /* Simulate rmw run */ - calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, - (void **)&dataptrs); - makedata(p1, p2); - calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, - (void **)&dataptrs); - - for (i = 0; i < NDISKS-1; i++) - for (j = i+1; j < NDISKS; j++) - test_disks(test, calls, - ra, i, j); - } + for (p1 = 0; p1 < NDISKS - 2; p1++) { + for (p2 = p1; p2 < NDISKS - 2; p2++) { + /* Simulate rmw run */ + ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, + (void **)&dataptrs); + makedata(p1, p2); + ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, + (void **)&dataptrs); + for (i = 0; i < NDISKS - 1; i++) + for (j = i + 1; j < NDISKS; j++) + test_recover(test, i, j); } } } +static const void *raid6_gen_params(struct kunit *test, const void *prev, + char *desc) +{ + if (!prev) { + memset(&args, 0, sizeof(args)); +next_algo: + args.recov_idx = 0; + args.gen = raid6_algo_find(args.gen_idx); + if (!args.gen) + return NULL; + } + + if (args.recov) + args.recov_idx++; + args.recov = raid6_recov_algo_find(args.recov_idx); + if (!args.recov) { + args.gen_idx++; + goto next_algo; + } + + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gen=%s recov=%s", + args.gen->name, args.recov->name); + return &args; +} + static struct kunit_case raid6_test_cases[] = { - KUNIT_CASE(raid6_test), + KUNIT_CASE_PARAM(raid6_test, raid6_gen_params), {}, }; From d67c25712fe3c5d78fea03827771c49debb89c80 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:58 +0200 Subject: [PATCH 068/108] raid6_kunit: dynamically allocate data buffers using vmalloc Use vmalloc for the data buffers instead of using static .data allocations. This provides for better out of bounds checking and avoids wasting kernel memory after the test has run. vmalloc is used instead of kmalloc to provide for better out of bounds access checking as in other kunit tests. Link: https://lore.kernel.org/20260518051804.462141-16-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/tests/raid6_kunit.c | 77 ++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c index 9d06ed9b90e4..72c78834df7f 100644 --- a/lib/raid/raid6/tests/raid6_kunit.c +++ b/lib/raid/raid6/tests/raid6_kunit.c @@ -7,19 +7,20 @@ #include #include +#include #include "../algos.h" MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); #define RAID6_KUNIT_SEED 42 +#define RAID6_KUNIT_MAX_FAILURES 2 #define NDISKS 16 /* Including P and Q */ static struct rnd_state rng; static void *dataptrs[NDISKS]; -static char data[NDISKS][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); -static char recovi[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); -static char recovj[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +static void *test_buffers[NDISKS]; +static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES]; struct test_args { unsigned int recov_idx; @@ -35,8 +36,8 @@ static void makedata(int start, int stop) int i; for (i = start; i <= stop; i++) { - prandom_bytes_state(&rng, data[i], PAGE_SIZE); - dataptrs[i] = data[i]; + prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE); + dataptrs[i] = test_buffers[i]; } } @@ -55,12 +56,13 @@ static char member_type(int d) static void test_recover(struct kunit *test, int faila, int failb) { const struct test_args *ta = test->param_value; + int i; - memset(recovi, 0xf0, PAGE_SIZE); - memset(recovj, 0xba, PAGE_SIZE); + for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) + memset(test_recov_buffers[i], 0xf0, PAGE_SIZE); - dataptrs[faila] = recovi; - dataptrs[failb] = recovj; + dataptrs[faila] = test_recov_buffers[0]; + dataptrs[failb] = test_recov_buffers[1]; if (failb == NDISKS - 1) { /* @@ -80,18 +82,20 @@ static void test_recover(struct kunit *test, int faila, int failb) ta->recov->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs); } - KUNIT_EXPECT_MEMEQ_MSG(test, data[faila], recovi, PAGE_SIZE, + KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0], + PAGE_SIZE, "faila miscompared: %3d[%c] (failb=%3d[%c])\n", faila, member_type(faila), failb, member_type(failb)); - KUNIT_EXPECT_MEMEQ_MSG(test, data[failb], recovj, PAGE_SIZE, + KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1], + PAGE_SIZE, "failb miscompared: %3d[%c] (faila=%3d[%c])\n", failb, member_type(failb), faila, member_type(faila)); skip: - dataptrs[faila] = data[faila]; - dataptrs[failb] = data[failb]; + dataptrs[faila] = test_buffers[faila]; + dataptrs[failb] = test_buffers[failb]; } static void raid6_test(struct kunit *test) @@ -100,8 +104,8 @@ static void raid6_test(struct kunit *test) int i, j, p1, p2; /* Nuke syndromes */ - memset(data[NDISKS - 2], 0xee, PAGE_SIZE); - memset(data[NDISKS - 1], 0xee, PAGE_SIZE); + memset(test_buffers[NDISKS - 2], 0xee, PAGE_SIZE); + memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE); /* Generate assumed good syndrome */ ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs); @@ -161,15 +165,58 @@ static struct kunit_case raid6_test_cases[] = { static int raid6_suite_init(struct kunit_suite *suite) { + int i; + prandom_seed_state(&rng, RAID6_KUNIT_SEED); + + /* + * Allocate the test buffer using vmalloc() with a page-aligned length + * so that it is immediately followed by a guard page. This allows + * buffer overreads to be detected, even in assembly code. + */ + for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) { + test_recov_buffers[i] = vmalloc(PAGE_SIZE); + if (!test_recov_buffers[i]) + goto out_free_recov_buffers; + } + for (i = 0; i < NDISKS; i++) { + test_buffers[i] = vmalloc(PAGE_SIZE); + if (!test_buffers[i]) + goto out_free_buffers; + } + makedata(0, NDISKS - 1); + return 0; + +out_free_buffers: + for (i = 0; i < NDISKS; i++) + vfree(test_buffers[i]); + memset(test_buffers, 0, sizeof(test_buffers)); +out_free_recov_buffers: + for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) + vfree(test_recov_buffers[i]); + memset(test_recov_buffers, 0, sizeof(test_recov_buffers)); + return -ENOMEM; +} + +static void raid6_suite_exit(struct kunit_suite *suite) +{ + int i; + + for (i = 0; i < NDISKS; i++) + vfree(test_buffers[i]); + memset(test_buffers, 0, sizeof(test_buffers)); + for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) + vfree(test_recov_buffers[i]); + memset(test_recov_buffers, 0, sizeof(test_recov_buffers)); } static struct kunit_suite raid6_test_suite = { .name = "raid6", .test_cases = raid6_test_cases, .suite_init = raid6_suite_init, + .suite_exit = raid6_suite_exit, }; kunit_test_suite(raid6_test_suite); From 562bcbfcb99b2eb4fd17d6551d760450e128afe1 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:17:59 +0200 Subject: [PATCH 069/108] raid6_kunit: cleanup dataptr handling Move the global dataptr array into test_recover() as all sites that fill data or parity can use test_buffers directly, and this localized the override for the failed slots to the recovery testing routine. Link: https://lore.kernel.org/20260518051804.462141-17-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/tests/raid6_kunit.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c index 72c78834df7f..152d5d1c3a88 100644 --- a/lib/raid/raid6/tests/raid6_kunit.c +++ b/lib/raid/raid6/tests/raid6_kunit.c @@ -18,7 +18,6 @@ MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); #define NDISKS 16 /* Including P and Q */ static struct rnd_state rng; -static void *dataptrs[NDISKS]; static void *test_buffers[NDISKS]; static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES]; @@ -35,10 +34,8 @@ static void makedata(int start, int stop) { int i; - for (i = start; i <= stop; i++) { + for (i = start; i <= stop; i++) prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE); - dataptrs[i] = test_buffers[i]; - } } static char member_type(int d) @@ -56,11 +53,13 @@ static char member_type(int d) static void test_recover(struct kunit *test, int faila, int failb) { const struct test_args *ta = test->param_value; + void *dataptrs[NDISKS]; int i; for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) memset(test_recov_buffers[i], 0xf0, PAGE_SIZE); + memcpy(dataptrs, test_buffers, sizeof(dataptrs)); dataptrs[faila] = test_recov_buffers[0]; dataptrs[failb] = test_recov_buffers[1]; @@ -70,7 +69,7 @@ static void test_recover(struct kunit *test, int faila, int failb) * is equivalent to a RAID-5 failure (XOR, then recompute Q). */ if (faila != NDISKS - 2) - goto skip; + return; /* P+Q failure. Just rebuild the syndrome. */ ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs); @@ -92,10 +91,6 @@ static void test_recover(struct kunit *test, int faila, int failb) "failb miscompared: %3d[%c] (faila=%3d[%c])\n", failb, member_type(failb), faila, member_type(faila)); - -skip: - dataptrs[faila] = test_buffers[faila]; - dataptrs[failb] = test_buffers[failb]; } static void raid6_test(struct kunit *test) @@ -108,7 +103,7 @@ static void raid6_test(struct kunit *test) memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE); /* Generate assumed good syndrome */ - ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs); + ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, test_buffers); for (i = 0; i < NDISKS - 1; i++) for (j = i + 1; j < NDISKS; j++) @@ -121,10 +116,10 @@ static void raid6_test(struct kunit *test) for (p2 = p1; p2 < NDISKS - 2; p2++) { /* Simulate rmw run */ ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, - (void **)&dataptrs); + test_buffers); makedata(p1, p2); ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, - (void **)&dataptrs); + test_buffers); for (i = 0; i < NDISKS - 1; i++) for (j = i + 1; j < NDISKS; j++) From fa0c812c0aa58d4375317d804dd54e44b8bcf5e3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:18:00 +0200 Subject: [PATCH 070/108] raid6_kunit: randomize parameters and increase limits The current test has double-quadratic behavior in the selection for the updated ("XORed") disks, and in the selection of updated pointers, which makes scaling it to more tests difficult. At the same time it only ever tests with the maximum number of disks, which leaves a coverage hole for smaller ones. Fix this by randomizing the total number, failed disks and regions to update, and increasing the upper number of tests disks. Link: https://lore.kernel.org/20260518051804.462141-18-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/tests/raid6_kunit.c | 201 ++++++++++++++++++++--------- 1 file changed, 137 insertions(+), 64 deletions(-) diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c index 152d5d1c3a88..71adf8932e93 100644 --- a/lib/raid/raid6/tests/raid6_kunit.c +++ b/lib/raid/raid6/tests/raid6_kunit.c @@ -8,18 +8,21 @@ #include #include #include +#include #include "../algos.h" MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); #define RAID6_KUNIT_SEED 42 +#define RAID6_KUNIT_NUM_TEST_ITERS 10 +#define RAID6_KUNIT_MAX_BUFFERS 64 /* Including P and Q */ #define RAID6_KUNIT_MAX_FAILURES 2 - -#define NDISKS 16 /* Including P and Q */ +#define RAID6_KUNIT_MAX_BYTES PAGE_SIZE static struct rnd_state rng; -static void *test_buffers[NDISKS]; +static void *test_buffers[RAID6_KUNIT_MAX_BUFFERS]; static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES]; +static size_t test_buflen; struct test_args { unsigned int recov_idx; @@ -30,102 +33,171 @@ struct test_args { static struct test_args args; +static u32 rand32(void) +{ + return prandom_u32_state(&rng); +} + +/* Generate a random length that is a multiple of 512. */ +static unsigned int random_length(unsigned int max_length) +{ + return round_up((rand32() % max_length) + 1, 512); +} + +static unsigned int random_nr_buffers(void) +{ + return (rand32() % (RAID6_KUNIT_MAX_BUFFERS - (RAID6_MIN_DISKS - 1))) + + RAID6_MIN_DISKS; +} + static void makedata(int start, int stop) { int i; for (i = start; i <= stop; i++) - prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE); + prandom_bytes_state(&rng, test_buffers[i], test_buflen); } -static char member_type(int d) +static char member_type(unsigned int nr_buffers, int d) { - switch (d) { - case NDISKS-2: + if (d == nr_buffers - 2) return 'P'; - case NDISKS-1: + if (d == nr_buffers - 1) return 'Q'; - default: - return 'D'; - } + return 'D'; } -static void test_recover(struct kunit *test, int faila, int failb) +static void test_recover_one(struct kunit *test, unsigned int nr_buffers, + unsigned int len, int faila, int failb) { const struct test_args *ta = test->param_value; - void *dataptrs[NDISKS]; + void *dataptrs[RAID6_KUNIT_MAX_BUFFERS]; int i; + if (faila > failb) + swap(faila, failb); + for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) - memset(test_recov_buffers[i], 0xf0, PAGE_SIZE); + memset(test_recov_buffers[i], 0xf0, test_buflen); memcpy(dataptrs, test_buffers, sizeof(dataptrs)); dataptrs[faila] = test_recov_buffers[0]; dataptrs[failb] = test_recov_buffers[1]; - if (failb == NDISKS - 1) { + if (failb == nr_buffers - 1) { /* * We don't implement the data+Q failure scenario, since it * is equivalent to a RAID-5 failure (XOR, then recompute Q). */ - if (faila != NDISKS - 2) + if (WARN_ON_ONCE(faila != nr_buffers - 2)) return; /* P+Q failure. Just rebuild the syndrome. */ - ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs); - } else if (failb == NDISKS - 2) { + ta->gen->gen_syndrome(nr_buffers, len, dataptrs); + } else if (failb == nr_buffers - 2) { /* data+P failure. */ - ta->recov->datap(NDISKS, PAGE_SIZE, faila, dataptrs); + ta->recov->datap(nr_buffers, len, faila, dataptrs); } else { /* data+data failure. */ - ta->recov->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs); + ta->recov->data2(nr_buffers, len, faila, failb, dataptrs); } KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0], - PAGE_SIZE, - "faila miscompared: %3d[%c] (failb=%3d[%c])\n", - faila, member_type(faila), - failb, member_type(failb)); + len, + "faila miscompared: %3d[%c] buffers %u len %u (failb=%3d[%c])\n", + faila, member_type(nr_buffers, faila), + nr_buffers, len, + failb, member_type(nr_buffers, failb)); KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1], - PAGE_SIZE, - "failb miscompared: %3d[%c] (faila=%3d[%c])\n", - failb, member_type(failb), - faila, member_type(faila)); + len, + "failb miscompared: %3d[%c] buffers %u len %u (faila=%3d[%c])\n", + failb, member_type(nr_buffers, failb), + nr_buffers, len, + faila, member_type(nr_buffers, faila)); +} + +static void test_recover(struct kunit *test, unsigned int nr_buffers, + unsigned int len) +{ + unsigned int nr_data = nr_buffers - 2; + int iterations, i; + + /* Test P+Q recovery */ + test_recover_one(test, nr_buffers, len, nr_data, nr_buffers - 1); + + /* Test data+P recovery */ + for (i = 0; i < nr_buffers - 2; i++) + test_recover_one(test, nr_buffers, len, i, nr_data); + + /* Double data failure is impossible with a single data disk */ + if (nr_data == 1) + return; + + /* Test data+data recovery using random sampling */ + iterations = nr_buffers * 2; /* should provide good enough coverage */ + for (i = 0; i < iterations; i++) { + int faila = rand32() % nr_data, failb; + + do { + failb = rand32() % nr_data; + } while (failb == faila); + + test_recover_one(test, nr_buffers, len, faila, failb); + } +} + +/* Simulate rmw run */ +static void test_rmw_one(struct kunit *test, unsigned int nr_buffers, + unsigned int len, int p1, int p2) +{ + const struct test_args *ta = test->param_value; + + ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers); + makedata(p1, p2); + ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers); + test_recover(test, nr_buffers, len); +} + +static void test_rmw(struct kunit *test, unsigned int nr_buffers, + unsigned int len) +{ + int iterations = nr_buffers / 2, i; + + for (i = 0; i < iterations; i++) { + int p1 = rand32() % (nr_buffers - 2); + int p2 = rand32() % (nr_buffers - 2); + + if (p2 < p1) + swap(p1, p2); + test_rmw_one(test, nr_buffers, len, p1, p2); + } +} + +static void raid6_test_one(struct kunit *test) +{ + const struct test_args *ta = test->param_value; + unsigned int nr_buffers = random_nr_buffers(); + unsigned int len = random_length(RAID6_KUNIT_MAX_BYTES); + + /* Nuke syndromes */ + memset(test_buffers[nr_buffers - 2], 0xee, test_buflen); + memset(test_buffers[nr_buffers - 1], 0xee, test_buflen); + + /* Generate assumed good syndrome */ + ta->gen->gen_syndrome(nr_buffers, len, test_buffers); + + test_recover(test, nr_buffers, len); + + if (ta->gen->xor_syndrome) + test_rmw(test, nr_buffers, len); } static void raid6_test(struct kunit *test) { - const struct test_args *ta = test->param_value; - int i, j, p1, p2; + int i; - /* Nuke syndromes */ - memset(test_buffers[NDISKS - 2], 0xee, PAGE_SIZE); - memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE); - - /* Generate assumed good syndrome */ - ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, test_buffers); - - for (i = 0; i < NDISKS - 1; i++) - for (j = i + 1; j < NDISKS; j++) - test_recover(test, i, j); - - if (!ta->gen->xor_syndrome) - return; - - for (p1 = 0; p1 < NDISKS - 2; p1++) { - for (p2 = p1; p2 < NDISKS - 2; p2++) { - /* Simulate rmw run */ - ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, - test_buffers); - makedata(p1, p2); - ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, - test_buffers); - - for (i = 0; i < NDISKS - 1; i++) - for (j = i + 1; j < NDISKS; j++) - test_recover(test, i, j); - } - } + for (i = 0; i < RAID6_KUNIT_NUM_TEST_ITERS; i++) + raid6_test_one(test); } static const void *raid6_gen_params(struct kunit *test, const void *prev, @@ -169,23 +241,24 @@ static int raid6_suite_init(struct kunit_suite *suite) * so that it is immediately followed by a guard page. This allows * buffer overreads to be detected, even in assembly code. */ + test_buflen = round_up(RAID6_KUNIT_MAX_BYTES, PAGE_SIZE); for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) { - test_recov_buffers[i] = vmalloc(PAGE_SIZE); + test_recov_buffers[i] = vmalloc(test_buflen); if (!test_recov_buffers[i]) goto out_free_recov_buffers; } - for (i = 0; i < NDISKS; i++) { - test_buffers[i] = vmalloc(PAGE_SIZE); + for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) { + test_buffers[i] = vmalloc(test_buflen); if (!test_buffers[i]) goto out_free_buffers; } - makedata(0, NDISKS - 1); + makedata(0, RAID6_KUNIT_MAX_BUFFERS - 1); return 0; out_free_buffers: - for (i = 0; i < NDISKS; i++) + for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) vfree(test_buffers[i]); memset(test_buffers, 0, sizeof(test_buffers)); out_free_recov_buffers: @@ -199,7 +272,7 @@ static void raid6_suite_exit(struct kunit_suite *suite) { int i; - for (i = 0; i < NDISKS; i++) + for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) vfree(test_buffers[i]); memset(test_buffers, 0, sizeof(test_buffers)); for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) From 8cf0a6c4bb9e09a2d280376dba723b218c139e58 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 18 May 2026 07:18:01 +0200 Subject: [PATCH 071/108] raid6_kunit: randomize buffer alignment Add code to add random alignment to the buffers to test the case where they are not page aligned, and to move the buffers to the end of the allocation so that they are next to the vmalloc guard page. This does not include the recovery buffers as the recovery requires page alignment. Link: https://lore.kernel.org/20260518051804.462141-19-hch@lst.de Signed-off-by: Christoph Hellwig Acked-by: Ard Biesheuvel Tested-by: Ard Biesheuvel # kunit only on arm64 Cc: Albert Ou Cc: Alexander Gordeev Cc: Alexandre Ghiti Cc: Arnd Bergmann Cc: "Borislav Petkov (AMD)" Cc: Catalin Marinas Cc: Chris Mason Cc: Christian Borntraeger Cc: Dan Williams Cc: David Sterba Cc: Heiko Carstens Cc: Herbert Xu Cc: "H. Peter Anvin" Cc: Huacai Chen Cc: Ingo Molnar Cc: Li Nan Cc: Madhavan Srinivasan Cc: Michael Ellerman Cc: Nicholas Piggin Cc: Palmer Dabbelt Cc: Song Liu Cc: Sven Schnelle Cc: Vasily Gorbik Cc: WANG Xuerui Cc: Will Deacon Signed-off-by: Andrew Morton --- lib/raid/raid6/tests/raid6_kunit.c | 41 +++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c index 71adf8932e93..9f3e671a1224 100644 --- a/lib/raid/raid6/tests/raid6_kunit.c +++ b/lib/raid/raid6/tests/raid6_kunit.c @@ -21,6 +21,7 @@ MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); static struct rnd_state rng; static void *test_buffers[RAID6_KUNIT_MAX_BUFFERS]; +static void *aligned_buffers[RAID6_KUNIT_MAX_BUFFERS]; static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES]; static size_t test_buflen; @@ -50,6 +51,14 @@ static unsigned int random_nr_buffers(void) RAID6_MIN_DISKS; } +/* Generate a random alignment that is a multiple of 64. */ +static unsigned int random_alignment(unsigned int max_alignment) +{ + if (max_alignment == 0) + return 0; + return (rand32() % (max_alignment + 1)) & ~63; +} + static void makedata(int start, int stop) { int i; @@ -80,7 +89,7 @@ static void test_recover_one(struct kunit *test, unsigned int nr_buffers, for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) memset(test_recov_buffers[i], 0xf0, test_buflen); - memcpy(dataptrs, test_buffers, sizeof(dataptrs)); + memcpy(dataptrs, aligned_buffers, sizeof(dataptrs)); dataptrs[faila] = test_recov_buffers[0]; dataptrs[failb] = test_recov_buffers[1]; @@ -102,13 +111,13 @@ static void test_recover_one(struct kunit *test, unsigned int nr_buffers, ta->recov->data2(nr_buffers, len, faila, failb, dataptrs); } - KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0], + KUNIT_EXPECT_MEMEQ_MSG(test, aligned_buffers[faila], dataptrs[faila], len, "faila miscompared: %3d[%c] buffers %u len %u (failb=%3d[%c])\n", faila, member_type(nr_buffers, faila), nr_buffers, len, failb, member_type(nr_buffers, failb)); - KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1], + KUNIT_EXPECT_MEMEQ_MSG(test, aligned_buffers[failb], dataptrs[failb], len, "failb miscompared: %3d[%c] buffers %u len %u (faila=%3d[%c])\n", failb, member_type(nr_buffers, failb), @@ -152,9 +161,9 @@ static void test_rmw_one(struct kunit *test, unsigned int nr_buffers, { const struct test_args *ta = test->param_value; - ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers); + ta->gen->xor_syndrome(nr_buffers, p1, p2, len, aligned_buffers); makedata(p1, p2); - ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers); + ta->gen->xor_syndrome(nr_buffers, p1, p2, len, aligned_buffers); test_recover(test, nr_buffers, len); } @@ -178,13 +187,33 @@ static void raid6_test_one(struct kunit *test) const struct test_args *ta = test->param_value; unsigned int nr_buffers = random_nr_buffers(); unsigned int len = random_length(RAID6_KUNIT_MAX_BYTES); + unsigned int max_alignment; + int i; /* Nuke syndromes */ memset(test_buffers[nr_buffers - 2], 0xee, test_buflen); memset(test_buffers[nr_buffers - 1], 0xee, test_buflen); + /* + * If we're not using the entire buffer size, inject randomize alignment + * into the buffer. + */ + max_alignment = RAID6_KUNIT_MAX_BYTES - len; + if (rand32() % 2 == 0) { + /* Use random alignments mod 64 */ + for (i = 0; i < nr_buffers; i++) + aligned_buffers[i] = test_buffers[i] + + random_alignment(max_alignment); + } else { + /* Go up to the guard page, to catch buffer overreads */ + unsigned int align = test_buflen - len; + + for (i = 0; i < nr_buffers; i++) + aligned_buffers[i] = test_buffers[i] + align; + } + /* Generate assumed good syndrome */ - ta->gen->gen_syndrome(nr_buffers, len, test_buffers); + ta->gen->gen_syndrome(nr_buffers, len, aligned_buffers); test_recover(test, nr_buffers, len); From f7ea0d13735ef812f7c777d5c0a79ff6a4b369c3 Mon Sep 17 00:00:00 2001 From: Costa Shulyupin Date: Fri, 15 May 2026 21:34:24 +0300 Subject: [PATCH 072/108] include: remove unused cnt32_to_63.h All users have been removed over time as ARM and other architectures switched to generic sched_clock. The last user was microblaze, removed in commit 839396ab88e4 ("microblaze: timer: Use generic sched_clock implementation"). Assisted-by: Claude:claude-opus-4-6 Link: https://lore.kernel.org/20260515183429.1503740-1-costa.shul@redhat.com Signed-off-by: Costa Shulyupin Reviewed-by: Andrew Morton Cc: Nicolas Pitre Cc: Nicolas Pitre Signed-off-by: Andrew Morton --- include/linux/cnt32_to_63.h | 104 ------------------------------------ 1 file changed, 104 deletions(-) delete mode 100644 include/linux/cnt32_to_63.h diff --git a/include/linux/cnt32_to_63.h b/include/linux/cnt32_to_63.h deleted file mode 100644 index 064428479f2d..000000000000 --- a/include/linux/cnt32_to_63.h +++ /dev/null @@ -1,104 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Extend a 32-bit counter to 63 bits - * - * Author: Nicolas Pitre - * Created: December 3, 2006 - * Copyright: MontaVista Software, Inc. - */ - -#ifndef __LINUX_CNT32_TO_63_H__ -#define __LINUX_CNT32_TO_63_H__ - -#include -#include -#include - -/* this is used only to give gcc a clue about good code generation */ -union cnt32_to_63 { - struct { -#if defined(__LITTLE_ENDIAN) - u32 lo, hi; -#elif defined(__BIG_ENDIAN) - u32 hi, lo; -#endif - }; - u64 val; -}; - - -/** - * cnt32_to_63 - Expand a 32-bit counter to a 63-bit counter - * @cnt_lo: The low part of the counter - * - * Many hardware clock counters are only 32 bits wide and therefore have - * a relatively short period making wrap-arounds rather frequent. This - * is a problem when implementing sched_clock() for example, where a 64-bit - * non-wrapping monotonic value is expected to be returned. - * - * To overcome that limitation, let's extend a 32-bit counter to 63 bits - * in a completely lock free fashion. Bits 0 to 31 of the clock are provided - * by the hardware while bits 32 to 62 are stored in memory. The top bit in - * memory is used to synchronize with the hardware clock half-period. When - * the top bit of both counters (hardware and in memory) differ then the - * memory is updated with a new value, incrementing it when the hardware - * counter wraps around. - * - * Because a word store in memory is atomic then the incremented value will - * always be in synch with the top bit indicating to any potential concurrent - * reader if the value in memory is up to date or not with regards to the - * needed increment. And any race in updating the value in memory is harmless - * as the same value would simply be stored more than once. - * - * The restrictions for the algorithm to work properly are: - * - * 1) this code must be called at least once per each half period of the - * 32-bit counter; - * - * 2) this code must not be preempted for a duration longer than the - * 32-bit counter half period minus the longest period between two - * calls to this code; - * - * Those requirements ensure proper update to the state bit in memory. - * This is usually not a problem in practice, but if it is then a kernel - * timer should be scheduled to manage for this code to be executed often - * enough. - * - * And finally: - * - * 3) the cnt_lo argument must be seen as a globally incrementing value, - * meaning that it should be a direct reference to the counter data which - * can be evaluated according to a specific ordering within the macro, - * and not the result of a previous evaluation stored in a variable. - * - * For example, this is wrong: - * - * u32 partial = get_hw_count(); - * u64 full = cnt32_to_63(partial); - * return full; - * - * This is fine: - * - * u64 full = cnt32_to_63(get_hw_count()); - * return full; - * - * Note that the top bit (bit 63) in the returned value should be considered - * as garbage. It is not cleared here because callers are likely to use a - * multiplier on the returned value which can get rid of the top bit - * implicitly by making the multiplier even, therefore saving on a runtime - * clear-bit instruction. Otherwise caller must remember to clear the top - * bit explicitly. - */ -#define cnt32_to_63(cnt_lo) \ -({ \ - static u32 __m_cnt_hi; \ - union cnt32_to_63 __x; \ - __x.hi = __m_cnt_hi; \ - smp_rmb(); \ - __x.lo = (cnt_lo); \ - if (unlikely((s32)(__x.hi ^ __x.lo) < 0)) \ - __m_cnt_hi = __x.hi = (__x.hi ^ 0x80000000) + (__x.hi >> 31); \ - __x.val; \ -}) - -#endif From f5b1910e23f1233c8d4185268b2e659df2bc5dbf Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 18 May 2026 13:23:40 +0900 Subject: [PATCH 073/108] ocfs2: kill osb->system_file_mutex lock Commit 43b10a20372d ("ocfs2: avoid system inode ref confusion by adding mutex lock") tried to avoid a refcount leak caused by allowing multiple threads to call igrab(inode). But addition of osb->system_file_mutex made locking dependency complicated and is causing lockdep to warn about possibility of AB-BA deadlock. Since _ocfs2_get_system_file_inode() returns the same inode for the same input arguments, we don't need to serialize _ocfs2_get_system_file_inode(). What we need to make sure is that igrab(inode) is called for only once(). Therefore, replace osb->system_file_mutex with cmpxchg()-based locking. Link: https://lore.kernel.org/fea8d1fd-afb0-4302-a560-c202e2ef7afd@I-love.SAKURA.ne.jp Fixes: 43b10a20372d ("ocfs2: avoid system inode ref confusion by adding mutex lock") Signed-off-by: Tetsuo Handa Reviewed-by: Heming Zhao Acked-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Signed-off-by: Andrew Morton --- fs/ocfs2/ocfs2.h | 2 -- fs/ocfs2/super.c | 2 -- fs/ocfs2/sysfile.c | 9 +++------ 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index 7b50e03dfa66..62cad6522c7a 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -494,8 +494,6 @@ struct ocfs2_super struct rb_root osb_rf_lock_tree; struct ocfs2_refcount_tree *osb_ref_tree_lru; - struct mutex system_file_mutex; - /* * OCFS2 needs to schedule several different types of work which * require cluster locking, disk I/O, recovery waits, etc. Since these diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index b875f01c9756..6dd45c2153f8 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -1997,8 +1997,6 @@ static int ocfs2_initialize_super(struct super_block *sb, spin_lock_init(&osb->osb_xattr_lock); ocfs2_init_steal_slots(osb); - mutex_init(&osb->system_file_mutex); - atomic_set(&osb->alloc_stats.moves, 0); atomic_set(&osb->alloc_stats.local_data, 0); atomic_set(&osb->alloc_stats.bitmap_data, 0); diff --git a/fs/ocfs2/sysfile.c b/fs/ocfs2/sysfile.c index d53a6cc866be..67e492f4b828 100644 --- a/fs/ocfs2/sysfile.c +++ b/fs/ocfs2/sysfile.c @@ -98,11 +98,9 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb, } else arr = get_local_system_inode(osb, type, slot); - mutex_lock(&osb->system_file_mutex); if (arr && ((inode = *arr) != NULL)) { /* get a ref in addition to the array ref */ inode = igrab(inode); - mutex_unlock(&osb->system_file_mutex); BUG_ON(!inode); return inode; @@ -112,11 +110,10 @@ struct inode *ocfs2_get_system_file_inode(struct ocfs2_super *osb, inode = _ocfs2_get_system_file_inode(osb, type, slot); /* add one more if putting into array for first time */ - if (arr && inode) { - *arr = igrab(inode); - BUG_ON(!*arr); + if (inode && arr && !*arr && !cmpxchg(&(*arr), NULL, inode)) { + inode = igrab(inode); + BUG_ON(!inode); } - mutex_unlock(&osb->system_file_mutex); return inode; } From 4d80db59de9c7b52f75d8f32005dafc8d64658b0 Mon Sep 17 00:00:00 2001 From: Ingyu Jang Date: Fri, 15 May 2026 04:32:14 +0900 Subject: [PATCH 074/108] error-inject: use IS_ERR() check for debugfs_create_file() debugfs_create_file() returns an error pointer on failure, never NULL, so the !file check in ei_debugfs_init() never triggers and the debugfs_remove() cleanup cannot run. Use IS_ERR() and propagate the actual error via PTR_ERR(). Link: https://lore.kernel.org/20260514193214.2432769-1-ingyujang25@korea.ac.kr Signed-off-by: Ingyu Jang Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton --- lib/error-inject.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/error-inject.c b/lib/error-inject.c index f3d1b70be605..32f3d1ca9ea2 100644 --- a/lib/error-inject.c +++ b/lib/error-inject.c @@ -219,9 +219,9 @@ static int __init ei_debugfs_init(void) dir = debugfs_create_dir("error_injection", NULL); file = debugfs_create_file("list", 0444, dir, NULL, &ei_fops); - if (!file) { + if (IS_ERR(file)) { debugfs_remove(dir); - return -ENOMEM; + return PTR_ERR(file); } return 0; From 5366a017099c6a3c443be908a05f26fd72af12a1 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 07:04:02 -0400 Subject: [PATCH 075/108] ocfs2: reject dinodes with non-canonical i_mode type Patch series "ocfs2: harden inode validators against forged metadata", v2. This series adds three structural checks to OCFS2 dinode validation so malformed on-disk fields are rejected before ocfs2_populate_inode() copies them into the in-core inode. The checks cover: - i_mode values whose type bits do not name a canonical POSIX file type; - non-device dinodes whose id1.dev1.i_rdev field is non-zero; and - non-inline dinodes that claim non-zero i_size while i_clusters is zero, covering directories unconditionally and regular files on non-sparse volumes. The normal read path reports these through ocfs2_error(), matching the existing suballoc-slot, inline-data, chain-list, and refcount checks. The online filecheck path uses the same structural predicates but keeps its own reporting contract, returning OCFS2_FILECHECK_ERR_INVALIDINO instead of calling ocfs2_error(). This patch (of 3): ocfs2_validate_inode_block() currently accepts any non-zero i_mode value. ocfs2_populate_inode() then copies that mode verbatim into inode->i_mode and dispatches on i_mode & S_IFMT to the file/dir/symlink/special_file iops; an unrecognised type falls through to ocfs2_special_file_iops and init_special_inode(). Reject dinodes whose type bits do not name one of the seven canonical POSIX file types. Use fs_umode_to_ftype(), the same generic file-type conversion helper OCFS2 already uses for directory entries, so the accepted inode type set matches the kernel file-type vocabulary instead of open-coding a local switch. Apply the same structural check to the online filecheck read path. filecheck keeps its own error namespace, so it reports malformed i_mode through the filecheck logger and OCFS2_FILECHECK_ERR_INVALIDINO instead of calling ocfs2_error(), but it must not allow a malformed dinode to proceed into ocfs2_populate_inode(). Link: https://lore.kernel.org/20260519110404.1803902-1-michael.bommarito@gmail.com Link: https://lore.kernel.org/20260519110404.1803902-2-michael.bommarito@gmail.com Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function.") Signed-off-by: Michael Bommarito Link: https://sashiko.dev/#/patchset/20260517111015.3187935-1-michael.bommarito%40gmail.com Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/inode.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index a510a0eb1adc..e149ccbdc03c 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -64,7 +65,12 @@ static int ocfs2_filecheck_read_inode_block_full(struct inode *inode, static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, struct buffer_head *bh); static int ocfs2_filecheck_repair_inode_block(struct super_block *sb, - struct buffer_head *bh); + struct buffer_head *bh); + +static bool ocfs2_valid_inode_mode(umode_t mode) +{ + return fs_umode_to_ftype(mode) != FT_UNKNOWN; +} void ocfs2_set_inode_flags(struct inode *inode) { @@ -1494,6 +1500,24 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + /* + * Reject dinodes whose i_mode does not name one of the seven + * canonical POSIX file types. ocfs2_populate_inode() copies + * i_mode verbatim into inode->i_mode and then dispatches via + * switch (mode & S_IFMT) to file/dir/symlink/special_file iops; + * an unrecognised type falls into ocfs2_special_file_iops with + * init_special_inode(), which interprets i_rdev. Constrain the + * type here so the dispatch only ever sees a value mkfs.ocfs2 / + * VFS can produce. + */ + if (!ocfs2_valid_inode_mode(le16_to_cpu(di->i_mode))) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: mode 0%o has unknown file type\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode)); + goto bail; + } + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *data = &di->id2.i_data; @@ -1624,6 +1648,15 @@ static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, (unsigned long long)bh->b_blocknr, le32_to_cpu(di->i_fs_generation)); rc = -OCFS2_FILECHECK_ERR_GENERATION; + goto bail; + } + + if (!ocfs2_valid_inode_mode(le16_to_cpu(di->i_mode))) { + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: mode 0%o has unknown file type\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode)); + rc = -OCFS2_FILECHECK_ERR_INVALIDINO; } bail: @@ -1812,4 +1845,3 @@ const struct ocfs2_caching_operations ocfs2_inode_caching_ops = { .co_io_lock = ocfs2_inode_cache_io_lock, .co_io_unlock = ocfs2_inode_cache_io_unlock, }; - From 51407c2d249987a466416890a5c931a2354a46aa Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 07:04:03 -0400 Subject: [PATCH 076/108] ocfs2: reject dinodes whose i_rdev disagrees with the file type id1.dev1.i_rdev is the device-number arm of the ocfs2_dinode id1 union. It is only meaningful for character and block device inodes. For any other user-visible file type the on-disk value must be zero. ocfs2_populate_inode() currently copies id1.dev1.i_rdev into inode->i_rdev before the S_IFMT switch decides whether the inode is a special file. A non-device inode with a non-zero i_rdev can therefore publish stale or attacker-controlled device state into the in-core inode. System inodes legitimately use other arms of the same union, so keep the cross-check restricted to non-system inodes. Factor that predicate into a helper and use it in both the normal validator and online filecheck path; filecheck reports the malformed dinode through OCFS2_FILECHECK_ERR_INVALIDINO instead of ocfs2_error(). Link: https://lore.kernel.org/20260519110404.1803902-3-michael.bommarito@gmail.com Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function.") Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Joseph Qi Cc: Changwei Ge Cc: Heming Zhao Cc: Joel Becker Cc: Jun Piao Cc: Junxiao Bi Cc: Mark Fasheh Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/inode.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index e149ccbdc03c..992980ea9804 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -72,6 +72,16 @@ static bool ocfs2_valid_inode_mode(umode_t mode) return fs_umode_to_ftype(mode) != FT_UNKNOWN; } +static bool ocfs2_dinode_has_unexpected_rdev(struct ocfs2_dinode *di) +{ + umode_t mode = le16_to_cpu(di->i_mode); + + if (le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL) + return false; + + return !S_ISCHR(mode) && !S_ISBLK(mode) && di->id1.dev1.i_rdev != 0; +} + void ocfs2_set_inode_flags(struct inode *inode) { unsigned int flags = OCFS2_I(inode)->ip_attr; @@ -1518,6 +1528,41 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + /* + * id1.dev1.i_rdev is the device-number arm of the id1 union and + * is only meaningful for character and block device inodes. For + * any other regular user-visible file type the on-disk value + * must be zero. ocfs2_populate_inode() currently runs + * + * inode->i_rdev = huge_decode_dev(le64_to_cpu(fe->id1.dev1.i_rdev)); + * + * unconditionally, before the S_IFMT switch decides whether the + * inode is a special file. As a result, an i_rdev value present + * on a non-device inode is silently published into the in-core + * inode; a subsequent forced re-read or in-core mode mutation + * (cluster peer with raw write access to the shared LUN, + * on-disk corruption, or a separately forged dinode) can then + * expose the attacker-controlled device number to + * init_special_inode() without ever showing an unusual i_mode + * at validation time. + * + * System inodes (OCFS2_SYSTEM_FL) legitimately use the bitmap1 + * and journal1 arms of the same union (allocator i_used / + * i_total counters and the journal ij_flags / + * ij_recovery_generation pair); those bytes are not an i_rdev + * and must not be checked here. Restrict the cross-check to + * non-system inodes, which is the full attacker-controllable + * surface. + */ + if (ocfs2_dinode_has_unexpected_rdev(di)) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: non-device mode 0%o with i_rdev %llu\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode), + (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev)); + goto bail; + } + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *data = &di->id2.i_data; @@ -1657,6 +1702,16 @@ static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, (unsigned long long)bh->b_blocknr, le16_to_cpu(di->i_mode)); rc = -OCFS2_FILECHECK_ERR_INVALIDINO; + goto bail; + } + + if (ocfs2_dinode_has_unexpected_rdev(di)) { + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: non-device mode 0%o with i_rdev %llu\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(di->i_mode), + (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev)); + rc = -OCFS2_FILECHECK_ERR_INVALIDINO; } bail: From 7ebc672fab7a76e1e47e0f2fc1ee48118d27fde4 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 07:04:04 -0400 Subject: [PATCH 077/108] ocfs2: reject non-inline dinodes with i_size and zero i_clusters On a volume mounted without OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC, a non-inline regular file with non-zero i_size and zero i_clusters is structurally malformed: the extent map declares no allocated clusters yet the size header claims content exists. Keep rejecting that shape, but express it through a shared predicate so the same invariant is available to normal inode reads and online filecheck. The same zero-cluster shape is also malformed for non-inline directories. ocfs2 directory growth allocates backing storage before advancing i_size, and ocfs2_dir_foreach_blk_el() later walks until ctx->pos reaches i_size_read(inode). A forged directory dinode with a huge i_size and no clusters would repeatedly fail on holes while advancing through the claimed size. Sparse regular files remain exempt: on sparse-alloc volumes, truncate can legitimately grow i_size without allocating clusters. System inodes and inline-data dinodes also retain their separate storage rules. Mirror the check in ocfs2_filecheck_validate_inode_block() as well. filecheck reports through its own error namespace, so malformed size/cluster state is logged as a filecheck invalid-inode result rather than via ocfs2_error(), but it must not proceed into ocfs2_populate_inode(). Link: https://lore.kernel.org/20260519110404.1803902-4-michael.bommarito@gmail.com Fixes: b657c95c1108 ("ocfs2: Wrap inode block reads in a dedicated function.") Signed-off-by: Michael Bommarito Link: https://sashiko.dev/#/patchset/20260517111015.3187935-1-michael.bommarito%40gmail.com Assisted-by: Claude:claude-opus-4-7 Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/inode.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 992980ea9804..432eac01c176 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -82,6 +82,24 @@ static bool ocfs2_dinode_has_unexpected_rdev(struct ocfs2_dinode *di) return !S_ISCHR(mode) && !S_ISBLK(mode) && di->id1.dev1.i_rdev != 0; } +static bool ocfs2_dinode_has_size_without_clusters(struct super_block *sb, + struct ocfs2_dinode *di) +{ + umode_t mode = le16_to_cpu(di->i_mode); + + if (le32_to_cpu(di->i_flags) & OCFS2_SYSTEM_FL) + return false; + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) + return false; + if (!le64_to_cpu(di->i_size) || le32_to_cpu(di->i_clusters)) + return false; + + if (S_ISDIR(mode)) + return true; + + return !ocfs2_sparse_alloc(OCFS2_SB(sb)) && S_ISREG(mode); +} + void ocfs2_set_inode_flags(struct inode *inode) { unsigned int flags = OCFS2_I(inode)->ip_attr; @@ -1563,6 +1581,33 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + /* + * Non-inline directories must not have i_size without allocated + * clusters: directory growth adds storage before advancing i_size, + * and readdir walks i_size block-by-block. A forged directory + * with zero clusters and a huge i_size would repeatedly fault on + * holes while advancing through the claimed size. + * + * Non-inline regular files have the same invariant on non-sparse + * volumes. Sparse regular files are different: truncate can + * legitimately grow i_size without allocating clusters, so keep + * the sparse-alloc carveout for S_IFREG only. System inodes and + * inline-data dinodes have their own storage rules. + */ + if (ocfs2_dinode_has_size_without_clusters(sb, di)) { + if (S_ISDIR(le16_to_cpu(di->i_mode))) + rc = ocfs2_error(sb, + "Invalid dinode #%llu: directory i_size %llu with i_clusters 0 and no inline-data flag\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + else + rc = ocfs2_error(sb, + "Invalid dinode #%llu: regular file i_size %llu with i_clusters 0 and no inline-data flag on non-sparse volume\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + goto bail; + } + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_DATA_FL) { struct ocfs2_inline_data *data = &di->id2.i_data; @@ -1712,6 +1757,21 @@ static int ocfs2_filecheck_validate_inode_block(struct super_block *sb, le16_to_cpu(di->i_mode), (unsigned long long)le64_to_cpu(di->id1.dev1.i_rdev)); rc = -OCFS2_FILECHECK_ERR_INVALIDINO; + goto bail; + } + + if (ocfs2_dinode_has_size_without_clusters(sb, di)) { + if (S_ISDIR(le16_to_cpu(di->i_mode))) + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: directory i_size %llu with i_clusters 0 and no inline-data flag\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + else + mlog(ML_ERROR, + "Filecheck: invalid dinode #%llu: regular file i_size %llu with i_clusters 0 and no inline-data flag on non-sparse volume\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)le64_to_cpu(di->i_size)); + rc = -OCFS2_FILECHECK_ERR_INVALIDINO; } bail: From 2e73ea35a5dbe7e258b7fd396bcb100fa154f029 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Sat, 16 May 2026 17:09:15 +0500 Subject: [PATCH 078/108] lib/uuid_kunit: add tests for the four random UUID/GUID generators uuid_kunit currently exercises only guid_parse() and uuid_parse() (plus their invalid-input paths). The four random generators exported from lib/uuid.c -- generate_random_uuid(), generate_random_guid(), uuid_gen() and guid_gen() -- have no direct kunit coverage. Random output cannot be compared against a fixed expected value, but RFC 4122 section 4.4 specifies two invariants that any version-4 random UUID/GUID must satisfy: - version 4 in the high nibble of the version byte (byte 6 in the wire uuid_t layout, byte 7 in the byte-swapped guid_t layout); - variant DCE 1.1 (binary 10x) in the high bits of byte 8. Add four test cases that invoke each generator several times and verify these bit patterns hold. The same checks catch a regression in either the mask/OR sequence in the generators or the layout constants. Run the loop a handful of times to cover the small but non-zero chance that an unmasked random byte happens to satisfy the version/variant pattern by accident on a single call. Link: https://lore.kernel.org/20260516120915.40544-1-sozdayvek@gmail.com Signed-off-by: Stepan Ionichev Acked-by: Andy Shevchenko Cc: Brendan Higgins Cc: David Gow Cc: Greg Kroah-Hartman Signed-off-by: Andrew Morton --- lib/tests/uuid_kunit.c | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/lib/tests/uuid_kunit.c b/lib/tests/uuid_kunit.c index de71b2649dac..2ef64fbe67d6 100644 --- a/lib/tests/uuid_kunit.c +++ b/lib/tests/uuid_kunit.c @@ -86,11 +86,67 @@ static void uuid_test_uuid_invalid(struct kunit *test) } } +/* + * RFC 4122 section 4.4 says random UUIDs/GUIDs (version 4) must have: + * - version 4 in the high nibble of the version byte, + * - variant DCE 1.1 (binary 10x) in the high bits of byte 8. + * + * The version byte is byte 6 in the "wire" uuid_t layout and byte 7 in + * the byte-swapped guid_t layout. + */ +static void uuid_test_uuid_gen(struct kunit *test) +{ + uuid_t u; + + for (unsigned int i = 0; i < 8; i++) { + uuid_gen(&u); + KUNIT_EXPECT_EQ(test, u.b[6] & 0xf0, 0x40); + KUNIT_EXPECT_EQ(test, u.b[8] & 0xc0, 0x80); + } +} + +static void uuid_test_guid_gen(struct kunit *test) +{ + guid_t g; + + for (unsigned int i = 0; i < 8; i++) { + guid_gen(&g); + KUNIT_EXPECT_EQ(test, g.b[7] & 0xf0, 0x40); + KUNIT_EXPECT_EQ(test, g.b[8] & 0xc0, 0x80); + } +} + +static void uuid_test_generate_random_uuid(struct kunit *test) +{ + unsigned char buf[16]; + + for (unsigned int i = 0; i < 8; i++) { + generate_random_uuid(buf); + KUNIT_EXPECT_EQ(test, buf[6] & 0xf0, 0x40); + KUNIT_EXPECT_EQ(test, buf[8] & 0xc0, 0x80); + } +} + +static void uuid_test_generate_random_guid(struct kunit *test) +{ + unsigned char buf[16]; + + for (unsigned int i = 0; i < 8; i++) { + generate_random_guid(buf); + KUNIT_EXPECT_EQ(test, buf[7] & 0xf0, 0x40); + KUNIT_EXPECT_EQ(test, buf[8] & 0xc0, 0x80); + } +} + static struct kunit_case uuid_test_cases[] = { KUNIT_CASE(uuid_test_guid_valid), KUNIT_CASE(uuid_test_uuid_valid), KUNIT_CASE(uuid_test_guid_invalid), KUNIT_CASE(uuid_test_uuid_invalid), + KUNIT_CASE(uuid_test_uuid_gen), + KUNIT_CASE(uuid_test_guid_gen), + KUNIT_CASE(uuid_test_generate_random_uuid), + KUNIT_CASE(uuid_test_generate_random_guid), {}, }; From 685568777c5a18fbc40bd0b64527fd9444c255be Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 14 May 2026 18:56:03 +0200 Subject: [PATCH 079/108] string: use min in sized_strscpy Use min() and drop the limit variable to simplify sized_strscpy(). Link: https://lore.kernel.org/20260514165601.527883-3-thorsten.blum@linux.dev Signed-off-by: Thorsten Blum Cc: Andy Shevchenko Cc: Kees Cook Signed-off-by: Andrew Morton --- lib/string.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/string.c b/lib/string.c index b632c71df1a5..1f9297e9776a 100644 --- a/lib/string.c +++ b/lib/string.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -125,11 +126,8 @@ ssize_t sized_strscpy(char *dest, const char *src, size_t count) * If src is unaligned, don't cross a page boundary, * since we don't know if the next page is mapped. */ - if ((long)src & (sizeof(long) - 1)) { - size_t limit = PAGE_SIZE - ((long)src & (PAGE_SIZE - 1)); - if (limit < max) - max = limit; - } + if ((long)src & (sizeof(long) - 1)) + max = min(PAGE_SIZE - ((long)src & (PAGE_SIZE - 1)), max); #else /* If src or dest is unaligned, don't do word-at-a-time. */ if (((long) dest | (long) src) & (sizeof(long) - 1)) From c60ffec33ddf24577f6f4da18fe825b2058c5f78 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Thu, 21 May 2026 11:03:36 +0800 Subject: [PATCH 080/108] lib/nmi_backtrace: print out the CPUs which fail to respond to NMI When debugging RCU stall cases, usually all CPUs will respond to the NMI and print out the backtrace. But in some nasty or hardware related cases, some CPUs may fail to respond in 10 seconds, and very likely this is sign of severe issues. Paul McKenney has implemented the NMI backtrace stall check for x86, and for other architectures, it should be also helpful to at least print out those CPUs which failed to repond to the NMI, so that users can get an early heads-up for possible CPU hard stall. [feng.tang@linux.alibaba.com: avoid hard-coding "10" in two places and in a comment] Link: https://lore.kernel.org/ag-1ciG0FSomBf7q@U-2FWC9VHC-2323.local [akpm@linux-foundation.org: use __stringify()] Link: https://lore.kernel.org/20260521030336.92172-1-feng.tang@linux.alibaba.com Signed-off-by: Feng Tang Reviewed-by: Petr Mladek Cc: "Paul E . McKenney" Signed-off-by: Andrew Morton --- lib/nmi_backtrace.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/nmi_backtrace.c b/lib/nmi_backtrace.c index 33c154264bfe..a3bfa9360b23 100644 --- a/lib/nmi_backtrace.c +++ b/lib/nmi_backtrace.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,8 @@ static DECLARE_BITMAP(backtrace_mask, NR_CPUS) __read_mostly; /* "in progress" flag of arch_trigger_cpumask_backtrace */ static unsigned long backtrace_flag; +#define NMI_BT_TIMEOUT_SEC 10 + /* * When raise() is called it will be passed a pointer to the * backtrace_mask. Architectures that call nmi_cpu_backtrace() @@ -68,14 +71,20 @@ void nmi_trigger_cpumask_backtrace(const cpumask_t *mask, raise(to_cpumask(backtrace_mask)); } - /* Wait for up to 10 seconds for all CPUs to do the backtrace */ - for (i = 0; i < 10 * 1000; i++) { + /* Wait for up to NMI_BT_TIMEOUT_SEC seconds for all CPUs to do the backtrace */ + for (i = 0; i < NMI_BT_TIMEOUT_SEC * 1000; i++) { if (cpumask_empty(to_cpumask(backtrace_mask))) break; mdelay(1); touch_softlockup_watchdog(); } - nmi_backtrace_stall_check(to_cpumask(backtrace_mask)); + + if (!cpumask_empty(to_cpumask(backtrace_mask))) { + pr_warn("After " __stringify(NMI_BT_TIMEOUT_SEC) " seconds, these CPUS still haven't responded to the NMI: %*pbl\n", + cpumask_pr_args(to_cpumask(backtrace_mask))); + + nmi_backtrace_stall_check(to_cpumask(backtrace_mask)); + } /* * Force flush any remote buffers that might be stuck in IRQ context From 93612d48fa42b3d1a637eb9279e15281c611c000 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Mon, 25 May 2026 12:17:26 +0800 Subject: [PATCH 081/108] ocfs2: rebase copied fsdlm LVB pointers in locking_state The locking_state debugfs iterator snapshots struct ocfs2_lock_res by value under ocfs2_dlm_tracking_lock and later formats that copy in ocfs2_dlm_seq_show(). That is fine for the inline fields, but the userspace fsdlm stack stores the LVB through lksb_fsdlm.sb_lvbptr. Once the iterator drops the tracking lock, a copied non-NULL sb_lvbptr still points into the original lockres owner, so teardown can free that container before the debugfs dump walks the raw LVB bytes. Rebase the copied sb_lvbptr to the copied l_lksb before dumping the raw LVB. The seq snapshot already carries the inline LVB storage reserved in struct ocfs2_dlm_lksb, so the debugfs reader can dump the copied bytes without borrowing the original lockres lifetime. The buggy scenario involves two paths, with each column showing the order within that path: locking_state reader: lockres teardown: 1. ocfs2_dlm_seq_start()/next() 1. file release or another owner copies struct ocfs2_lock_res teardown reaches 2. ocfs2_dlm_seq_show() formats ocfs2_lock_res_free() the copied row 2. the lockres is removed from the 3. ocfs2_dlm_lvb() follows the tracking list copied sb_lvbptr 3. the owner frees the original lockres container Validation reproduced this kernel report: KASAN slab-use-after-free in ocfs2_dlm_seq_show+0x1bd/0x430 RIP: 0033:0x7f8ec4b1e29d The buggy address belongs to the object at ffff88810a1e0800 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 368 bytes inside of freed 1024-byte region [ffff88810a1e0800, ffff88810a1e0c00) Read of size 1 Call trace: dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ocfs2_dlm_seq_show+0x1bd/0x430 (fs/ocfs2/dlmglue.c:3137) srso_alias_return_thunk+0x5/0xfbef5 __virt_addr_valid+0x19f/0x330 kasan_report+0xe0/0x110 seq_read_iter+0x29d/0x790 seq_read+0x20a/0x280 find_held_lock+0x2b/0x80 rcu_read_unlock+0x18/0x70 full_proxy_read+0x9e/0xd0 vfs_read+0x12c/0x590 ksys_read+0xd2/0x170 do_user_addr_fault+0x65a/0x890 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 ocfs2_file_open+0x13e/0x300 do_dentry_open+0x233/0x7f0 vfs_open+0x5a/0x1b0 path_openat+0x66d/0x1540 do_file_open+0x186/0x2b0 do_sys_openat2+0xce/0x150 __x64_sys_openat+0xd0/0x140 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task stack: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x313/0x590 ocfs2_file_release+0x138/0x260 __fput+0x1df/0x4b0 fput_close_sync+0xd2/0x170 __x64_sys_close+0x55/0x90 do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f Link: https://lore.kernel.org/20260525041726.4112882-1-rollkingzzc@gmail.com Fixes: cf4d8d75d8ab ("ocfs2: add fsdlm to stackglue") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/dlmglue.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 7283bb2c5a31..a23dd8f86c89 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -3134,6 +3134,22 @@ static void *ocfs2_dlm_seq_next(struct seq_file *m, void *v, loff_t *pos) * - Add last pr/ex unlock times and first lock wait time in usecs */ #define OCFS2_DLM_DEBUG_STR_VERSION 4 + +/* + * The debug iterator snapshots lockres by value, so a userspace-stack LVB + * pointer copied from the original lockres must be rebased to the copied + * lksb before the dump walks the raw bytes. + */ +static void ocfs2_dlm_seq_rebase_lvb(struct ocfs2_lock_res *lockres) +{ + if (!ocfs2_stack_supports_plocks()) + return; + + if (lockres->l_lksb.lksb_fsdlm.sb_lvbptr) + lockres->l_lksb.lksb_fsdlm.sb_lvbptr = + (char *)&lockres->l_lksb + sizeof(struct dlm_lksb); +} + static int ocfs2_dlm_seq_show(struct seq_file *m, void *v) { int i; @@ -3191,6 +3207,7 @@ static int ocfs2_dlm_seq_show(struct seq_file *m, void *v) lockres->l_blocking); /* Dump the raw LVB */ + ocfs2_dlm_seq_rebase_lvb(lockres); lvb = ocfs2_dlm_lvb(&lockres->l_lksb); for(i = 0; i < DLM_LVB_LEN; i++) seq_printf(m, "0x%x\t", lvb[i]); From 9a79524d1420e6b79a6868208c264f4518d1318e Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Tue, 26 May 2026 13:47:15 +0200 Subject: [PATCH 082/108] kcov: use WRITE_ONCE() for selftest mode stores The KCOV selftest enables coverage by setting current->kcov_mode to KCOV_MODE_TRACE_PC without installing a coverage area. If an interrupt records coverage in that window, the access should fault and expose the bug. When building for QEMU raspi0 (Raspberry Pi Zero, ARMv6, CONFIG_CPU_V6K=y, CONFIG_CURRENT_POINTER_IN_TPIDRURO=y) with GCC 13.3.0, the store that enables the mode is removed. The generated kcov_init() code only stores zero after the wait loop: mrc 15, 0, r3, cr13, cr0, {3} str r4, [r3, #2028] where r4 is zero. There is no store of KCOV_MODE_TRACE_PC before the loop, so the selftest reports success without exercising coverage. Use WRITE_ONCE() for the temporary mode stores. With the same compiler and config, kcov_init() contains the intended mode store: mov r3, #2 mrc 15, 0, r2, cr13, cr0, {3} str r3, [r2, #2028] Now that the KCOV selftest is actually executed, it may expose KCOV instrumentation issues depending on the kernel config. That is expected for a selftest that was intended to catch coverage from interrupt paths. Link: https://lore.kernel.org/20260526114715.38280-1-kmehltretter@gmail.com Fixes: 6cd0dd934b03 ("kcov: Add interrupt handling self test") Assisted-by: Codex:gpt-5 Signed-off-by: Karl Mehltretter Reviewed-by: Alexander Potapenko Cc: Andrey Konovalov Cc: Dmitry Vyukov Cc: Kees Cook Cc: Marco Elver Cc: Peter Zijlstra Cc: Signed-off-by: Andrew Morton --- kernel/kcov.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/kcov.c b/kernel/kcov.c index fd2503030729..1df373fb562b 100644 --- a/kernel/kcov.c +++ b/kernel/kcov.c @@ -1119,10 +1119,10 @@ static void __init selftest(void) * potentially traced functions in this region. */ start = jiffies; - current->kcov_mode = KCOV_MODE_TRACE_PC; + WRITE_ONCE(current->kcov_mode, KCOV_MODE_TRACE_PC); while ((jiffies - start) * MSEC_PER_SEC / HZ < 300) ; - current->kcov_mode = 0; + WRITE_ONCE(current->kcov_mode, 0); pr_err("done running self test\n"); } #endif From 94bfc7f3b0c7c33331ba4ff6cc64ff309dfcbce8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 26 May 2026 12:18:41 +0200 Subject: [PATCH 083/108] err.h: use __always_inline on all error pointer helpers While testing randconfig builds on s390, I came across a link failure with CONFIG_DMA_SHARED_BUFFER disabled: ERROR: modpost: "dma_buf_put" [drivers/iommu/iommufd/iommufd.ko] undefined! The problem here is that IS_ERR() is not inlined and dead code elimination fails as a consequence. The err.h helpers all turn into a trivial assignment of a bit mask and should never result in a function call, so force them to always be inline. This should generally result in better object code aside from avoiding the link failure above. Link: https://lore.kernel.org/20260526101851.2495110-1-arnd@kernel.org Signed-off-by: Arnd Bergmann Reviewed-by: Alexander Lobakin Reviewed-by: Nathan Chancellor Tested-by: Tamir Duberstein Cc: Alexander Gordeev Cc: Andriy Shevchenko Cc: Ansuel Smith Cc: Bjorn Andersson Cc: Heiko Carstens Cc: Vasily Gorbik Cc: Signed-off-by: Andrew Morton --- include/linux/err.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/linux/err.h b/include/linux/err.h index 8c37be0620ab..d3e38d5b3a98 100644 --- a/include/linux/err.h +++ b/include/linux/err.h @@ -36,7 +36,7 @@ * * Return: A pointer with @error encoded within its value. */ -static inline void * __must_check ERR_PTR(long error) +static __always_inline void * __must_check ERR_PTR(long error) { return (void *) error; } @@ -60,7 +60,7 @@ static inline void * __must_check ERR_PTR(long error) * @ptr: An error pointer. * Return: The error code within @ptr. */ -static inline long __must_check PTR_ERR(__force const void *ptr) +static __always_inline long __must_check PTR_ERR(__force const void *ptr) { return (long) ptr; } @@ -73,7 +73,7 @@ static inline long __must_check PTR_ERR(__force const void *ptr) * @ptr: The pointer to check. * Return: true if @ptr is an error pointer, false otherwise. */ -static inline bool __must_check IS_ERR(__force const void *ptr) +static __always_inline bool __must_check IS_ERR(__force const void *ptr) { return IS_ERR_VALUE((unsigned long)ptr); } @@ -87,7 +87,7 @@ static inline bool __must_check IS_ERR(__force const void *ptr) * * Like IS_ERR(), but also returns true for a null pointer. */ -static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr) +static __always_inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr) { return unlikely(!ptr) || IS_ERR_VALUE((unsigned long)ptr); } @@ -99,7 +99,7 @@ static inline bool __must_check IS_ERR_OR_NULL(__force const void *ptr) * Explicitly cast an error-valued pointer to another pointer type in such a * way as to make it clear that's what's going on. */ -static inline void * __must_check ERR_CAST(__force const void *ptr) +static __always_inline void * __must_check ERR_CAST(__force const void *ptr) { /* cast away the const */ return (void *) ptr; @@ -122,7 +122,7 @@ static inline void * __must_check ERR_CAST(__force const void *ptr) * * Return: The error code within @ptr if it is an error pointer; 0 otherwise. */ -static inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr) +static __always_inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr) { if (IS_ERR(ptr)) return PTR_ERR(ptr); From 9ac9a08e4ac4bc063e56e1aff266c5e8aa5c6c03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Tue, 26 May 2026 18:43:40 +0200 Subject: [PATCH 084/108] lib: kunit_iov_iter: repeatedly call alloc_pages_bulk() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit alloc_pages_bulk() is not guaranteed to return all requested pages in a single call. Call it repeatedly until all pages have been allocated or no more progress is being made. Link: https://lore.kernel.org/20260526-kunit_iov_iter-alloc_bulk-v2-1-24fbcd995c61@weissschuh.net Fixes: 2d71340ff1d4 ("iov_iter: Kunit tests for copying to/from an iterator") Signed-off-by: Thomas Weißschuh Cc: "Christian A. Ehrhardt" Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton --- lib/tests/kunit_iov_iter.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/tests/kunit_iov_iter.c b/lib/tests/kunit_iov_iter.c index f02f7b7aa796..1e6fce9cb255 100644 --- a/lib/tests/kunit_iov_iter.c +++ b/lib/tests/kunit_iov_iter.c @@ -53,7 +53,7 @@ static void *__init iov_kunit_create_buffer(struct kunit *test, size_t npages) { struct page **pages; - unsigned long got; + unsigned long got, last; void *buffer; unsigned int i; @@ -61,7 +61,15 @@ static void *__init iov_kunit_create_buffer(struct kunit *test, KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pages); *ppages = pages; - got = alloc_pages_bulk(GFP_KERNEL, npages, pages); + got = 0; + while (true) { + last = got; + got = alloc_pages_bulk(GFP_KERNEL, npages, pages); + + if (last == got || got == npages) + break; + } + if (got != npages) { release_pages(pages, got); kvfree(pages); From 9bd541e09dffff27e5bec0f9f45b0228173a5375 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Sun, 24 May 2026 19:12:48 +0800 Subject: [PATCH 085/108] ocfs2: reject oversized group bitmap descriptors ocfs2_validate_gd_parent() only bounds bg_bits against the parent allocator's chain geometry. A malicious descriptor can still claim a bg_size/bg_bits pair that exceeds the bitmap bytes that physically fit in the group descriptor block, so later bitmap scans and bit updates can run past bg_bitmap. Add a physical-cap check based on ocfs2_group_bitmap_size() for the parent allocator type and reject descriptors whose bg_size or bg_bits exceed that capacity. Keep the existing chain geometry check so both the on-disk bitmap layout and the allocator metadata must agree before the descriptor is used. Validation reproduced this kernel report: KASAN use-after-free in _find_next_bit+0x7f/0xc0 Read of size 8 Call trace: dump_stack_lvl+0x66/0xa0 (?:?) print_report+0xd0/0x630 (?:?) _find_next_bit+0x7f/0xc0 (?:?) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x188/0x2f0 (?:?) kasan_report+0xe4/0x120 (?:?) ocfs2_find_max_contig_free_bits+0x35/0x70 (fs/ocfs2/suballoc.c:1375) ocfs2_block_group_set_bits+0x472/0x4b0 (fs/ocfs2/suballoc.c:1457) ocfs2_cluster_group_search+0x16b/0x440 (fs/ocfs2/suballoc.c:86) ocfs2_bg_discontig_fix_result+0x1ef/0x230 (fs/ocfs2/suballoc.c:1786) ocfs2_search_chain+0x8f8/0x10a0 (fs/ocfs2/suballoc.c:1886) get_page_from_freelist+0x70e/0x2370 (?:?) lock_release+0xc6/0x290 (?:?) do_raw_spin_unlock+0x9a/0x100 (?:?) kasan_unpoison+0x27/0x60 (?:?) __bfs+0x147/0x240 (?:?) get_page_from_freelist+0x83d/0x2370 (?:?) ocfs2_claim_suballoc_bits+0x38c/0xe70 (fs/ocfs2/suballoc.c:96) sched_domains_numa_masks_clear+0x70/0xd0 (?:?) check_irq_usage+0xe8/0xb70 (?:?) __ocfs2_claim_clusters+0x18d/0x4c0 (fs/ocfs2/suballoc.c:2497) check_path+0x24/0x50 (?:?) rcu_is_watching+0x20/0x50 (?:?) check_prev_add+0xfd/0xd00 (?:?) ocfs2_add_clusters_in_btree+0x17d/0x810 (fs/ocfs2/suballoc.c:?) __folio_batch_add_and_move+0x1f5/0x3d0 (?:?) ocfs2_add_inode_data+0xd9/0x120 (fs/ocfs2/suballoc.c:?) filemap_add_folio+0x105/0x1f0 (?:?) ocfs2_write_begin_nolock+0x29f7/0x2f80 (fs/ocfs2/suballoc.c:3043) ocfs2_read_inode_block+0xb5/0x110 (fs/ocfs2/suballoc.c:?) down_write+0xf5/0x180 (?:?) ocfs2_write_begin+0x180/0x240 (fs/ocfs2/suballoc.c:?) __mark_inode_dirty+0x758/0x9a0 (?:?) inode_to_bdi+0x41/0x90 (?:?) balance_dirty_pages_ratelimited_flags+0xf8/0x1d0 (?:?) generic_perform_write+0x252/0x440 (?:?) mnt_put_write_access_file+0x16/0x70 (?:?) file_update_time_flags+0xe4/0x200 (?:?) ocfs2_file_write_iter+0x80a/0x1320 (fs/ocfs2/suballoc.c:?) lock_acquire+0x184/0x2f0 (?:?) ksys_write+0xd2/0x170 (?:?) apparmor_file_permission+0xf5/0x310 (?:?) read_zero+0x8d/0x140 (?:?) lock_is_held_type+0x8f/0x100 (?:?) Link: https://lore.kernel.org/20260524111248.1429884-1-rollkingzzc@gmail.com Fixes: ccd979bdbce9 ("[PATCH] OCFS2: The Second Oracle Cluster Filesystem") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/suballoc.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index d284e0e37252..a4a2b87a45fe 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -231,8 +231,16 @@ static int ocfs2_validate_gd_parent(struct super_block *sb, int resize) { unsigned int max_bits; + unsigned int max_bitmap_bits; + unsigned int max_bitmap_size; + int suballocator; struct ocfs2_group_desc *gd = (struct ocfs2_group_desc *)bh->b_data; + suballocator = le64_to_cpu(di->i_blkno) != OCFS2_SB(sb)->bitmap_blkno; + max_bitmap_size = ocfs2_group_bitmap_size(sb, suballocator, + OCFS2_SB(sb)->s_feature_incompat); + max_bitmap_bits = max_bitmap_size * 8; + if (di->i_blkno != gd->bg_parent_dinode) { do_error("Group descriptor #%llu has bad parent pointer (%llu, expected %llu)\n", (unsigned long long)bh->b_blocknr, @@ -240,6 +248,20 @@ static int ocfs2_validate_gd_parent(struct super_block *sb, (unsigned long long)le64_to_cpu(di->i_blkno)); } + if (le16_to_cpu(gd->bg_size) > max_bitmap_size) { + do_error("Group descriptor #%llu has bitmap size %u but physical max of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(gd->bg_size), + max_bitmap_size); + } + + if (le16_to_cpu(gd->bg_bits) > max_bitmap_bits) { + do_error("Group descriptor #%llu has bit count %u but physical max of %u\n", + (unsigned long long)bh->b_blocknr, + le16_to_cpu(gd->bg_bits), + max_bitmap_bits); + } + max_bits = le16_to_cpu(di->id2.i_chain.cl_cpg) * le16_to_cpu(di->id2.i_chain.cl_bpc); if (le16_to_cpu(gd->bg_bits) > max_bits) { do_error("Group descriptor #%llu has bit count of %u\n", From d280a26a983f62bfeff3f134f9d5ba4035356b43 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Thu, 28 May 2026 12:53:00 +0300 Subject: [PATCH 086/108] xor: use kmalloc() in calibrate_xor_blocks() Patch series "lib/raid: replace __get_free_pages() call with kmalloc()", v4. The xor benchmark allocates 4 pages for a scratch buffer that is used purely as a CPU-only XOR working area. This buffer can be allocated with kmalloc() as there's nothing special about it to go directly to the page allocator. kmalloc() provides a better API than ancient __get_free_pages(). kmalloc() does not require ugly casts and kfree() does not need to know the size of the freed object. There is no performance difference because kmalloc() redirects allocations of such size to the page allocator. Replace __get_free_pages() call with kmalloc(). This patch (of 2): The xor benchmark allocates 4 pages for a scratch buffer that is used purely as a CPU-only XOR working area. This buffer can be allocated with kmalloc() as there's nothing special about it to go directly to the page allocator. kmalloc() provides a better API than ancient __get_free_pages(). kmalloc() does not require ugly casts and kfree() does not need to know the size of the freed object. There is no performance difference because kmalloc() redirects allocations of such size to the page allocator. Replace __get_free_pages() call with kmalloc(). Link: https://lore.kernel.org/20260528-lib-v4-0-4e3ad1277279@kernel.org Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Link: https://lore.kernel.org/20260528-lib-v4-1-4e3ad1277279@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Hannes Reinecke Reviewed-by: Christoph Hellwig Cc: Christoph Hellwig Cc: Li Nan Cc: Song Liu Signed-off-by: Andrew Morton --- lib/raid/xor/xor-core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/raid/xor/xor-core.c b/lib/raid/xor/xor-core.c index bd4e6e434418..50931fbf0324 100644 --- a/lib/raid/xor/xor-core.c +++ b/lib/raid/xor/xor-core.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -114,7 +115,7 @@ static int __init calibrate_xor_blocks(void) if (forced_template) return 0; - b1 = (void *) __get_free_pages(GFP_KERNEL, 2); + b1 = kmalloc(PAGE_SIZE * 4, GFP_KERNEL); if (!b1) { pr_warn("xor: Yikes! No memory available.\n"); return -ENOMEM; @@ -132,7 +133,7 @@ static int __init calibrate_xor_blocks(void) pr_info("xor: using function: %s (%d MB/sec)\n", fastest->name, fastest->speed); - free_pages((unsigned long)b1, 2); + kfree(b1); return 0; } From 19c000ba93d609e15e95fed76a9e3b8055833098 Mon Sep 17 00:00:00 2001 From: "Mike Rapoport (Microsoft)" Date: Thu, 28 May 2026 12:53:01 +0300 Subject: [PATCH 087/108] raid6: use kmalloc() in raid6_select_algo() raid6_select_algo() allocates 8 pages for buffer that is used as a scratch area for selection of the best algorithm. This buffer can be allocated with kmalloc() as there's nothing special about it to go directly to the page allocator. kmalloc() provides a better API than ancient __get_free_pages(). kmalloc() does not require ugly casts and kfree() does not need to know the size of the freed object. There is no performance difference because kmalloc() redirects allocations of such size to the page allocator. Replace __get_free_pages() call with kmalloc(). Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com Link: https://lore.kernel.org/20260528-lib-v4-2-4e3ad1277279@kernel.org Signed-off-by: Mike Rapoport (Microsoft) Reviewed-by: Christoph Hellwig Cc: Christoph Hellwig Cc: Hannes Reinecke Cc: Li Nan Cc: Song Liu Signed-off-by: Andrew Morton --- lib/raid/raid6/algos.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c index a600d5853672..6f5c89ab2b17 100644 --- a/lib/raid/raid6/algos.c +++ b/lib/raid/raid6/algos.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include "algos.h" @@ -153,7 +154,6 @@ EXPORT_SYMBOL_GPL(raid6_recov_datap); #define RAID6_TIME_JIFFIES_LG2 4 #define RAID6_TEST_DISKS 8 -#define RAID6_TEST_DISKS_ORDER 3 static int raid6_choose_gen(void *(*const dptrs)[RAID6_TEST_DISKS], const int disks) @@ -247,7 +247,7 @@ static int __init raid6_select_algo(void) } /* prepare the buffer and fill it circularly with gfmul table */ - disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER); + disk_ptr = kmalloc(PAGE_SIZE * RAID6_TEST_DISKS, GFP_KERNEL); if (!disk_ptr) { pr_err("raid6: Yikes! No memory available.\n"); return -ENOMEM; @@ -269,7 +269,7 @@ static int __init raid6_select_algo(void) /* select raid gen_syndrome function */ error = raid6_choose_gen(&dptrs, disks); - free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER); + kfree(disk_ptr); return error; } From 6371a07148ee979af22a9d6f4c277462953a9a4a Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Fri, 29 May 2026 12:41:28 +0300 Subject: [PATCH 088/108] ocfs2: fix buffer head management in ocfs2_read_blocks() In ocfs2_read_blocks(), caller should't assume that buffer head returned by 'sb_getblk()' is exclusively owned and so 'put_bh()' always drops b_count from 1 to 0. If it is not so, buffer head remains on hold and likely to be returned by the next call to 'sb_getblk()' unchanged - that is, with BH_Uptodate bit set even if it has failed validation previously, thus allowing to insert that buffer head into OCFS2 metadata cache and submit it to upper layers. To avoid such a scenario, BH_Uptodate should be cleared immediately after 'validate()' callback has detected some data inconsistency. Link: https://lore.kernel.org/20260529094128.494293-1-dmantipov@yandex.ru Fixes: cf76c78595ca ("ocfs2: don't put and assigning null to bh allocated outside") Signed-off-by: Dmitry Antipov Reported-by: syzbot+caacd220635a9cc3bac9@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=caacd220635a9cc3bac9 Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/buffer_head_io.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/ocfs2/buffer_head_io.c b/fs/ocfs2/buffer_head_io.c index 701d27d908d4..6114299b121e 100644 --- a/fs/ocfs2/buffer_head_io.c +++ b/fs/ocfs2/buffer_head_io.c @@ -350,8 +350,6 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr, wait_on_buffer(bh); put_bh(bh); bhs[i] = NULL; - } else if (bh && buffer_uptodate(bh)) { - clear_buffer_uptodate(bh); } continue; } @@ -380,8 +378,11 @@ int ocfs2_read_blocks(struct ocfs2_caching_info *ci, u64 block, int nr, BUG_ON(buffer_jbd(bh)); clear_buffer_needs_validate(bh); status = validate(sb, bh); - if (status) + if (status) { + if (buffer_uptodate(bh)) + clear_buffer_uptodate(bh); goto read_failure; + } } } From a291c77c034b7a81849ce9b71cc9ecda9e587d89 Mon Sep 17 00:00:00 2001 From: Joseph Qi Date: Sun, 31 May 2026 21:16:45 +0800 Subject: [PATCH 089/108] ocfs2: add journal NULL check in ocfs2_checkpoint_inode() During unmount, ocfs2_journal_shutdown() frees the journal and sets osb->journal to NULL. Later, when VFS evicts remaining cached inodes, ocfs2_evict_inode() -> ocfs2_clear_inode() -> ocfs2_checkpoint_inode() -> ocfs2_ci_fully_checkpointed() dereferences osb->journal, causing a NULL pointer dereference. Fix this by adding a NULL check for osb->journal in ocfs2_checkpoint_inode(). If the journal is NULL, it has already been fully flushed and destroyed during shutdown, so there is nothing to checkpoint. Link: https://lore.kernel.org/20260531131645.3650299-1-joseph.qi@linux.alibaba.com Reported-by: Farhad Alemi Fixes: da5e7c87827e ("ocfs2: cleanup journal init and shutdown") Signed-off-by: Joseph Qi Tested-by: Farhad Alemi Reviewed-by: Heming Zhao Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/journal.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 6397170f302f..f8b3b2a3d630 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -196,6 +196,9 @@ static inline void ocfs2_checkpoint_inode(struct inode *inode) if (ocfs2_mount_local(osb)) return; + if (!osb->journal) + return; + if (!ocfs2_ci_fully_checkpointed(INODE_CACHE(inode))) { /* WARNING: This only kicks off a single * checkpoint. If someone races you and adds more From e234973f286ed2e8961a24561ec91594ec3e3ff8 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Thu, 28 May 2026 23:12:30 +0800 Subject: [PATCH 090/108] ocfs2: validate fast symlink target during inode read ocfs2_validate_inode_block() already rejects several inconsistent self-contained dinodes before they are exposed to the rest of the filesystem. Fast symlinks need the same treatment. A zero-cluster symlink is treated as a fast symlink and later read through page_get_link() and ocfs2_fast_symlink_read_folio(). That path uses strnlen() on the inline payload and then copies len + 1 bytes into the folio. If a corrupt dinode stores an i_size that does not fit the inline area or omits the terminating NUL at i_size, that copy reads past the end of the inode block buffer. Reject zero-cluster symlink dinodes whose i_size exceeds the inline fast-symlink capacity or whose inline payload is not NUL-terminated exactly at i_size when the inode block is validated. This keeps malformed fast symlinks from reaching the read path. Validation reproduced this kernel report: KASAN use-after-free in ocfs2_fast_symlink_read_folio+0x12c/0x1f0 RIP: 0033:0x7f5c6d859aa7 Read of size 3905 Call trace: dump_stack_lvl+0x66/0xa0 (?:?) print_report+0xce/0x630 (?:?) ocfs2_fast_symlink_read_folio+0x12c/0x1f0 (fs/ocfs2/inode.c:?) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x19f/0x330 (?:?) kasan_report+0xe0/0x110 (?:?) kasan_check_range+0x105/0x1b0 (?:?) __asan_memcpy+0x23/0x60 (?:?) filemap_read_folio+0x27/0xe0 (?:?) filemap_read_folio+0x35/0xe0 (?:?) do_read_cache_folio+0x138/0x230 (?:?) __page_get_link+0x26/0x110 (?:?) page_get_link+0x2e/0x70 (?:?) vfs_readlink+0x15e/0x250 (?:?) touch_atime+0x4d/0x370 (?:?) do_readlinkat+0x186/0x200 (?:?) do_user_addr_fault+0x65a/0x890 (?:?) __x64_sys_readlink+0x46/0x60 (?:?) do_syscall_64+0x115/0x6a0 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Link: https://lore.kernel.org/20260528151230.361127-1-rollkingzzc@gmail.com Fixes: ea022dfb3c2a ("ocfs: simplify symlink handling") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Gui-Dong Han <2045gemini@gmail.com> Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/inode.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 432eac01c176..6fc29920ecb2 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -1639,6 +1639,29 @@ int ocfs2_validate_inode_block(struct super_block *sb, } } + if (S_ISLNK(le16_to_cpu(di->i_mode)) && + !le32_to_cpu(di->i_clusters)) { + int max_inline = ocfs2_fast_symlink_chars(sb); + u64 i_size = le64_to_cpu(di->i_size); + + if (i_size >= max_inline) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: fast symlink i_size %llu exceeds max %d\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)i_size, + max_inline - 1); + goto bail; + } + + if (strnlen((char *)di->id2.i_symlink, i_size + 1) != i_size) { + rc = ocfs2_error(sb, + "Invalid dinode #%llu: fast symlink is not NUL-terminated at i_size %llu\n", + (unsigned long long)bh->b_blocknr, + (unsigned long long)i_size); + goto bail; + } + } + if (le32_to_cpu(di->i_flags) & OCFS2_CHAIN_FL) { struct ocfs2_chain_list *cl = &di->id2.i_chain; u16 bpc = 1 << (OCFS2_SB(sb)->s_clustersize_bits - From ca1afd88f5eaaff9168e1466e5401385edf59543 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Thu, 28 May 2026 23:12:47 +0800 Subject: [PATCH 091/108] ocfs2: reject FITRIM ranges shorter than a cluster ocfs2_trim_mainbm() trims the global bitmap in cluster units, but its too-short range validation only checks sb->s_blocksize. On filesystems with a cluster size larger than the block size, a FITRIM range that is at least one block but shorter than one cluster is accepted and shifted down to len == 0. The later start + len - 1 and len -= ... arithmetic then underflows and can drive trimming past the requested range. Reject ranges shorter than s_clustersize instead. That preserves the existing -EINVAL behavior for requests that cannot discard even one allocation unit and keeps zero-cluster trims out of the group walk. Link: https://lore.kernel.org/20260528151247.361854-1-rollkingzzc@gmail.com Fixes: aa89762c5480 ("ocfs2: return EINVAL if the given range to discard is less than block size") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index 6e5fd3f12a84..be09e766ac1f 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -7576,7 +7576,7 @@ int ocfs2_trim_mainbm(struct super_block *sb, struct fstrim_range *range) len = range->len >> osb->s_clustersize_bits; minlen = range->minlen >> osb->s_clustersize_bits; - if (minlen >= osb->bitmap_cpg || range->len < sb->s_blocksize) + if (minlen >= osb->bitmap_cpg || range->len < osb->s_clustersize) return -EINVAL; trace_ocfs2_trim_mainbm(start, len, minlen); From 03ad858ce8064861ea580021976dc19b7aabb549 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Sun, 31 May 2026 12:47:14 +0800 Subject: [PATCH 092/108] ocfs2/dlm: require a ref for locking_state debugfs open debug_lockres_open() copies inode->i_private into struct debug_lockres and debug_lockres_release() later drops that pointer with dlm_put(). That only works if open successfully pins the struct dlm_ctxt. Today open calls dlm_grab(dlm) but ignores its return value. Once the last domain unregister has removed the context from dlm_domains, dlm_grab() returns NULL, yet open still stores the raw pointer and returns success. The later release path is outside the debugfs removal barrier, so it can call dlm_put() after dlm_free_ctxt_mem() has freed the context. KASAN reports this as a slab-use-after-free in dlm_put() called from debug_lockres_release(). Fail the open when dlm_grab() cannot acquire the reference and unwind the seq_file private state before returning. That keeps locking_state from handing out a file descriptor whose release path does not own the dlm_ctxt. The buggy scenario involves two paths, with each column showing the order within that path: locking_state debugfs open: last domain unregister: 1. debug_lockres_open() reads 1. dlm_unregister_domain() calls inode->i_private. dlm_complete_dlm_shutdown(). 2. debug_lockres_open() calls 2. shutdown removes the dlm_ctxt from dlm_grab(dlm) and gets NULL. dlm_domains. 3. open still stores the raw dlm 3. final teardown reaches pointer in dl->dl_ctxt and dlm_free_ctxt_mem() and frees it. returns success. 4. debug_lockres_release() later calls dlm_put(dl->dl_ctxt). Validation reproduced this kernel report: KASAN slab-use-after-free in dlm_put+0x82/0x200 RIP: 0033:0x7f4d349bc9e0 The buggy address belongs to the object at ffff888103a3c000 which belongs to the cache kmalloc-2k of size 2048 The buggy address is located 816 bytes inside of freed 2048-byte region [ffff888103a3c000, ffff888103a3c800) Write of size 4 Call trace: dump_stack_lvl+0x66/0xa0 (?:?) print_report+0xd0/0x630 (?:?) dlm_put+0x82/0x200 (?:?) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x188/0x2f0 (?:?) kasan_report+0xe4/0x120 (?:?) kasan_check_range+0x105/0x1b0 (?:?) debug_lockres_release+0x53/0x80 (fs/ocfs2/dlm/dlmdebug.c:587) dlm_put+0x9/0x200 (?:?) debug_lockres_release+0x5c/0x80 (fs/ocfs2/dlm/dlmdebug.c:587) full_proxy_release+0x67/0x90 (?:?) __fput+0x1df/0x4b0 (?:?) do_raw_spin_lock+0x10f/0x1b0 (?:?) fput_close_sync+0xd2/0x170 (?:?) __x64_sys_close+0x55/0x90 (?:?) do_syscall_64+0x10c/0x640 (arch/x86/entry/syscall_64.c:87) irqentry_exit+0xac/0x6e0 (?:?) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Freed by task stack: kasan_save_stack+0x33/0x60 (?:?) kasan_save_track+0x14/0x30 (?:?) kasan_save_free_info+0x3b/0x60 (?:?) __kasan_slab_free+0x5f/0x80 (?:?) kfree+0x30f/0x580 (?:?) dlm_put+0x1ce/0x200 (?:?) dlm_unregister_domain+0xf6/0xb30 (?:?) o2cb_cluster_disconnect+0x6b/0x90 (?:?) ocfs2_cluster_disconnect+0x41/0x70 (?:?) ocfs2_dlm_shutdown+0x1c4/0x220 (?:?) ocfs2_dismount_volume+0x38a/0x550 (?:?) generic_shutdown_super+0xc3/0x220 (?:?) kill_block_super+0x29/0x60 (?:?) deactivate_locked_super+0x66/0xe0 (?:?) cleanup_mnt+0x13d/0x210 (?:?) task_work_run+0xfa/0x170 (?:?) exit_to_user_mode_loop+0xd6/0x430 (?:?) do_syscall_64+0x3cb/0x640 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Link: https://lore.kernel.org/20260531044714.1640172-1-rollkingzzc@gmail.com Fixes: 4e3d24ed1a12 ("ocfs2/dlm: Dumps the lockres' into a debugfs file") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/dlm/dlmdebug.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c index fe4fdd09bae3..564567358620 100644 --- a/fs/ocfs2/dlm/dlmdebug.c +++ b/fs/ocfs2/dlm/dlmdebug.c @@ -560,6 +560,7 @@ static int debug_lockres_open(struct inode *inode, struct file *file) struct dlm_ctxt *dlm = inode->i_private; struct debug_lockres *dl; void *buf; + int status = -ENOMEM; buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) @@ -572,16 +573,23 @@ static int debug_lockres_open(struct inode *inode, struct file *file) dl->dl_len = PAGE_SIZE; dl->dl_buf = buf; - dlm_grab(dlm); - dl->dl_ctxt = dlm; + /* ->release uses dl_ctxt after open, so it needs a real pin. */ + dl->dl_ctxt = dlm_grab(dlm); + if (!dl->dl_ctxt) { + status = -ENOENT; + goto bailseq; + } return 0; +bailseq: + seq_release_private(inode, file); bailfree: kfree(buf); bail: - mlog_errno(-ENOMEM); - return -ENOMEM; + if (status != -ENOENT) + mlog_errno(status); + return status; } static int debug_lockres_release(struct inode *inode, struct file *file) From 57dcfd9049d497c31151787a0696d59f0a98f8e6 Mon Sep 17 00:00:00 2001 From: Joseph Qi Date: Mon, 1 Jun 2026 20:16:18 +0800 Subject: [PATCH 093/108] ocfs2: fix race between ocfs2_control_install_private() and ocfs2_control_release() Move atomic_inc(&ocfs2_control_opened) and the handshake state update inside ocfs2_control_lock to close a race window where ocfs2_control_release() can observe ocfs2_control_opened dropping to zero (resetting ocfs2_control_this_node and running_proto) while ocfs2_control_install_private() is about to bump the counter and mark the connection valid. Link: https://lore.kernel.org/20260601121618.1263346-1-joseph.qi@linux.alibaba.com Fixes: 3cfd4ab6b6b4 ("ocfs2: Add the local node id to the handshake.") Signed-off-by: Joseph Qi Reported-by: Ginger Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/stack_user.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/fs/ocfs2/stack_user.c b/fs/ocfs2/stack_user.c index 5803f1dee679..91e19d33847c 100644 --- a/fs/ocfs2/stack_user.c +++ b/fs/ocfs2/stack_user.c @@ -327,18 +327,14 @@ static int ocfs2_control_install_private(struct file *file) ocfs2_control_this_node = p->op_this_node; running_proto.pv_major = p->op_proto.pv_major; running_proto.pv_minor = p->op_proto.pv_minor; - } - -out_unlock: - mutex_unlock(&ocfs2_control_lock); - - if (!rc && set_p) { - /* We set the global values successfully */ atomic_inc(&ocfs2_control_opened); ocfs2_control_set_handshake_state(file, OCFS2_CONTROL_HANDSHAKE_VALID); } +out_unlock: + mutex_unlock(&ocfs2_control_lock); + return rc; } From 1ec3cca2d8b6b9ff6584ca626d4c8918bbf48d44 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Mon, 1 Jun 2026 13:44:33 -0500 Subject: [PATCH 094/108] ocfs2: fix out-of-bounds write in ocfs2_remove_refcount_extent [BUG] Unlinking a refcounted file whose refcount tree has leaf blocks triggers a fortify panic due to an out-of-bounds write. [CAUSE] When the last leaf block is removed from a refcount tree, ocfs2_remove_refcount_extent() converts the root back to leaf mode with a bulk memset on &rb->rf_records. rf_records sits in an anonymous union with rf_list. rf_list.l_tree_depth aliases rf_records.rl_count, and is 0 for a single-level tree. With rl_count equal to 0, the memset writes past the 16-byte declared size of rf_records, which the fortify checker catches. [FIX] Replace the bulk memset on &rb->rf_records with a correctly-bounded memset on rl_recs[] alone, after setting rl_count to the correct value. Link: https://lore.kernel.org/ah3TESOsEO9j_JLU@dev Fixes: 2f26f58df041 ("ocfs2: annotate flexible array members with __counted_by_le()") Signed-off-by: Ian Bridges Reported-by: syzbot+3ef989aae096b30f1663@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=3ef989aae096b30f1663 Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Signed-off-by: Andrew Morton --- fs/ocfs2/refcounttree.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 8eee5be4d1ed..7323bde70caa 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -2131,10 +2131,15 @@ static int ocfs2_remove_refcount_extent(handle_t *handle, rb->rf_flags = 0; rb->rf_parent = 0; rb->rf_cpos = 0; - memset(&rb->rf_records, 0, sb->s_blocksize - - offsetof(struct ocfs2_refcount_block, rf_records)); + rb->rf_records.rl_used = 0; + rb->rf_records.rl_reserved2 = 0; + rb->rf_records.rl_reserved1 = 0; + /* rl_count determines the memset size and fortify object size. */ rb->rf_records.rl_count = cpu_to_le16(ocfs2_refcount_recs_per_rb(sb)); + memset(rb->rf_records.rl_recs, 0, + le16_to_cpu(rb->rf_records.rl_count) * + sizeof(*rb->rf_records.rl_recs)); } ocfs2_journal_dirty(handle, ref_root_bh); From aae636b5463d9957fadba71e83e7cfc157416974 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Wed, 3 Jun 2026 09:52:21 -0500 Subject: [PATCH 095/108] fs: fat: inode: replace sprintf() with scnprintf() The kernel documentation notes that sprintf() is deprecated and unsafe. Replace it with the more preferred scnprintf() to help with hardening. Link: https://lore.kernel.org/20260603145222.59012-1-m32285159@gmail.com Signed-off-by: Maxwell Doose Acked-by: OGAWA Hirofumi Signed-off-by: Andrew Morton --- fs/fat/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 28f78df086ef..b032bbc6855c 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -1786,7 +1786,7 @@ int fat_fill_super(struct super_block *sb, struct fs_context *fc, */ error = -EINVAL; - sprintf(buf, "cp%d", sbi->options.codepage); + scnprintf(buf, sizeof(buf), "cp%d", sbi->options.codepage); sbi->nls_disk = load_nls(buf); if (!sbi->nls_disk) { fat_msg(sb, KERN_ERR, "codepage %s not found", buf); From 0f1f777b309bf9ac90f9ad2a93ae6ab68a1376a1 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Thu, 4 Jun 2026 00:58:40 +0200 Subject: [PATCH 096/108] mailmap: update Alexander Sverdlin's Email addresses - add kernel.org address - add sverdlin.org address (as primary address) - drop siemens.com address (for accounting purposes only) Link: https://lore.kernel.org/20260603225847.1849399-1-asv@kernel.org Signed-off-by: Alexander Sverdlin Signed-off-by: Andrew Morton --- .mailmap | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.mailmap b/.mailmap index cf67173dcd51..ba94d4a1fe55 100644 --- a/.mailmap +++ b/.mailmap @@ -36,13 +36,14 @@ Alexander Lobakin Alexander Mikhalitsyn Alexander Mikhalitsyn Alexander Mikhalitsyn -Alexander Sverdlin -Alexander Sverdlin -Alexander Sverdlin -Alexander Sverdlin -Alexander Sverdlin -Alexander Sverdlin -Alexander Sverdlin +Alexander Sverdlin +Alexander Sverdlin +Alexander Sverdlin +Alexander Sverdlin +Alexander Sverdlin +Alexander Sverdlin +Alexander Sverdlin +Alexander Sverdlin Alexandre Belloni Alexandre Ghiti Alexei Avshalom Lazar From 5ef8a1c7aa7f095b0913aed643df4a6da0d8b275 Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Fri, 5 Jun 2026 10:54:23 +0200 Subject: [PATCH 097/108] MAINTAINERS: add Alexander as a kcov reviewer Also move Dmitry Vyukov to the bottom of the list. Link: https://lore.kernel.org/20260605085423.1962700-1-glider@google.com Signed-off-by: Alexander Potapenko Reviewed-by: Dmitry Vyukov Cc: Alexander Potapenko Cc: Marco Elver Signed-off-by: Andrew Morton --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 49a264b999e2..7e1856a3f13c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13838,8 +13838,9 @@ F: fs/proc/kcore.c F: include/linux/kcore.h KCOV -R: Dmitry Vyukov R: Andrey Konovalov +R: Alexander Potapenko +R: Dmitry Vyukov L: kasan-dev@googlegroups.com S: Maintained B: https://bugzilla.kernel.org/buglist.cgi?component=Sanitizers&product=Memory%20Management From 66cbd504306bf354231b1fbe43585b9aaa242d28 Mon Sep 17 00:00:00 2001 From: Cryolitia PukNgae Date: Fri, 5 Jun 2026 14:57:04 +0800 Subject: [PATCH 098/108] checkpatch: cuppress warnings when Reported-by: is followed by Link: > The tag should be followed by a Closes: tag pointing to the report, > unless the report is not available on the web. The Link: tag can be > used instead of Closes: if the patch fixes a part of the issue(s) > being reported. According to Documentation/process/submitting-patches.rst, Link: is also acceptable to follow a Reported-by:, if the patch fixes a part of the issue(s) being reported. Link: https://lore.kernel.org/20260605-checkpatch-v1-1-8c68ae618513@linux.dev Signed-off-by: Cryolitia PukNgae Reviewed-by: Petr Vorel Cc: Andy Whitcroft Cc: Cheng Nie Cc: Dwaipayan Ray Cc: Joe Perches Cc: Lukas Bulwahn Signed-off-by: Andrew Morton --- scripts/checkpatch.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 3727156e4cca..d9af266c63df 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3253,10 +3253,10 @@ sub process { if ($sign_off =~ /^reported(?:|-and-tested)-by:$/i) { if (!defined $lines[$linenr]) { WARN("BAD_REPORTED_BY_LINK", - "Reported-by: should be immediately followed by Closes: with a URL to the report\n" . $herecurr . "\n"); - } elsif ($rawlines[$linenr] !~ /^closes:\s*/i) { + "Reported-by: should be immediately followed by Closes: or Link: with a URL to the report\n" . $herecurr . "\n"); + } elsif ($rawlines[$linenr] !~ /^(closes|link):\s*/i) { WARN("BAD_REPORTED_BY_LINK", - "Reported-by: should be immediately followed by Closes: with a URL to the report\n" . $herecurr . $rawlines[$linenr] . "\n"); + "Reported-by: should be immediately followed by Closes: or Link: with a URL to the report\n" . $herecurr . $rawlines[$linenr] . "\n"); } } } From 89009392c80da5da00876c8334ff20028e6e3eb6 Mon Sep 17 00:00:00 2001 From: Maxwell Doose Date: Thu, 4 Jun 2026 22:52:51 -0500 Subject: [PATCH 099/108] fs: efs: remove unneeded debug prints The current code uses debug prints conditionally compiled with #ifdef DEBUG. However, that code, when compiled, causes compiler errors due to incompatible formatters and undefined variables, notably: fs/efs/file.c: In function `efs_get_block': fs/efs/file.c:26:35: error: `block' undeclared (first use in this function); did you mean `iblock'? 26 | __func__, block, inode->i_blocks, inode->i_size); | ^~~~~ and: fs/efs/file.c: In function `efs_bmap': ./include/linux/kern_levels.h:5:25: error: format `%ld' expects argument of type `long int', but argument 4 has type `blkcnt_t' {aka `long long unsigned int'} [-Werror=format=] 5 | #define KERN_SOH "\001" /* ASCII Start Of Header */ | ^~~~~~ which also extends to the other formatters. As this part of the code has been dead for just about 14 years now, it has not been modernized to stay compatible with the most recent gcc compilers. Fix these issues by removing the debug prints. Link: https://lore.kernel.org/20260605035251.89305-2-m32285159@gmail.com Fixes: f403d1dbac6d ("fs/efs: add pr_fmt / use __func__") Signed-off-by: Maxwell Doose Suggested-by: Andrew Morton Cc: Fabian Frederick Cc: Christian Brauner Signed-off-by: Andrew Morton --- fs/efs/file.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/fs/efs/file.c b/fs/efs/file.c index 9e641da6fab2..9153dfe79bbc 100644 --- a/fs/efs/file.c +++ b/fs/efs/file.c @@ -18,16 +18,9 @@ int efs_get_block(struct inode *inode, sector_t iblock, if (create) return error; - if (iblock >= inode->i_blocks) { -#ifdef DEBUG - /* - * i have no idea why this happens as often as it does - */ - pr_warn("%s(): block %d >= %ld (filesize %ld)\n", - __func__, block, inode->i_blocks, inode->i_size); -#endif + if (iblock >= inode->i_blocks) return 0; - } + phys = efs_map_block(inode, iblock); if (phys) map_bh(bh_result, inode->i_sb, phys); @@ -42,16 +35,8 @@ int efs_bmap(struct inode *inode, efs_block_t block) { } /* are we about to read past the end of a file ? */ - if (!(block < inode->i_blocks)) { -#ifdef DEBUG - /* - * i have no idea why this happens as often as it does - */ - pr_warn("%s(): block %d >= %ld (filesize %ld)\n", - __func__, block, inode->i_blocks, inode->i_size); -#endif + if (!(block < inode->i_blocks)) return 0; - } return efs_map_block(inode, block); } From f2737dc40d2ef3e9f3f9395d61f53f6668306a71 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 5 Jun 2026 00:30:37 +0000 Subject: [PATCH 100/108] lib/test_firmware: allocate the configured into_buf size The batched into_buf test path allocates TEST_FIRMWARE_BUF_SIZE bytes unconditionally, but then passes test_fw_config->buf_size to request_firmware_into_buf() or request_partial_firmware_into_buf(). Userspace can set config_buf_size above TEST_FIRMWARE_BUF_SIZE before triggering a batched request. If the firmware file is large enough, the firmware loader writes past the end of the 1 KiB test buffer. Allocate the buffer with the same size that the test passes to the firmware API so config_buf_size remains the actual buffer size under test. Assisted-by: Codex:gpt-5.5-cyber-preview Link: https://lore.kernel.org/20260605003038.2005840-1-sam.moelius@trailofbits.com Signed-off-by: Samuel Moelius Reviewed-by: Andrew Morton Cc: Kees Cook Cc: Luis R. Rodriguez Cc: Scott Branden Signed-off-by: Andrew Morton --- lib/test_firmware.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/test_firmware.c b/lib/test_firmware.c index b471d720879a..7459bba65444 100644 --- a/lib/test_firmware.c +++ b/lib/test_firmware.c @@ -867,7 +867,7 @@ static int test_fw_run_batch_request(void *data) if (test_fw_config->into_buf) { void *test_buf; - test_buf = kzalloc(TEST_FIRMWARE_BUF_SIZE, GFP_KERNEL); + test_buf = kzalloc(test_fw_config->buf_size, GFP_KERNEL); if (!test_buf) return -ENOMEM; From c7fdbc2c2f26b9c397eb3aad2fdc54dbd85f68e1 Mon Sep 17 00:00:00 2001 From: Guixin Liu Date: Mon, 8 Jun 2026 13:39:34 +0800 Subject: [PATCH 101/108] selftests/uevent: increase __UEVENT_BUFFER_SIZE to avoid ENOBUFS on busy systems The kselftests case uevent.uevent_filtering fails reproducibly on busy systems (e.g. Intel EMR / AMD servers) with: No buffer space available - Failed to receive uevent The listener binds the NETLINK_KOBJECT_UEVENT socket to all 32 multicast groups (nl_groups = -1) but only sets SO_RCVBUF to 4 KiB (__UEVENT_BUFFER_SIZE = 2048 * 2). On hosts with many devices, the kernel and userspace daemons (udev/systemd) constantly emit uevents on multiple groups, plus the test itself triggers 10 add events in a row. The 4 KiB receive buffer overflows before the listener can drain it, recvmsg() returns -ENOBUFS, and the test bails out as failure. Increase __UEVENT_BUFFER_SIZE to 1 MiB so the receive buffer is large enough to absorb the burst of uevents on busy systems. After this change the test passes consistently across dozens of runs on Intel EMR and AMD platforms. Link: https://lore.kernel.org/20260608053934.4059533-1-kanie@linux.alibaba.com Signed-off-by: Guixin Liu Cc: Christian Brauner Cc: Shuah Khan Cc: Wei Yang Signed-off-by: Andrew Morton --- tools/testing/selftests/uevent/uevent_filtering.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/uevent/uevent_filtering.c b/tools/testing/selftests/uevent/uevent_filtering.c index 974b076f9235..33a09f66d7e2 100644 --- a/tools/testing/selftests/uevent/uevent_filtering.c +++ b/tools/testing/selftests/uevent/uevent_filtering.c @@ -22,7 +22,7 @@ #include "kselftest_harness.h" #define __DEV_FULL "/sys/devices/virtual/mem/full/uevent" -#define __UEVENT_BUFFER_SIZE (2048 * 2) +#define __UEVENT_BUFFER_SIZE (1024 * 1024) #define __UEVENT_HEADER "add@/devices/virtual/mem/full" #define __UEVENT_HEADER_LEN sizeof("add@/devices/virtual/mem/full") #define __UEVENT_LISTEN_ALL -1 From 5108f4765637bd0ac5ea2897dc7d537486a09885 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Fri, 5 Jun 2026 15:52:15 +0000 Subject: [PATCH 102/108] fat: reject BPB volumes whose data area starts beyond total sectors fat_fill_super() subtracts sbi->data_start from the BPB total sector count before computing the number of clusters. A malformed image can declare a total sector count smaller than data_start, causing the subtraction to underflow and the mount code to derive a plausible cluster count from the FAT length instead. Reject such images before the subtraction. In QEMU, a crafted FAT image with total_sectors=2 and data_start=3 mounted successfully before the fix and reading a file returned bytes stored past the BPB-declared end of the volume. With this change, the same image is rejected during mount. Assisted-by: Codex:gpt-5.5-cyber-preview Link: https://lore.kernel.org/20260605155216.2126545-1-sam.moelius@trailofbits.com Signed-off-by: Samuel Moelius Acked-by: OGAWA Hirofumi Cc: Christian Brauner Signed-off-by: Andrew Morton --- fs/fat/inode.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/fat/inode.c b/fs/fat/inode.c index b032bbc6855c..3aa52481ad5c 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -1738,6 +1738,14 @@ int fat_fill_super(struct super_block *sb, struct fs_context *fc, if (total_sectors == 0) total_sectors = bpb.fat_total_sect; + if (total_sectors < sbi->data_start) { + if (!silent) + fat_msg(sb, KERN_ERR, + "data area starts beyond volume (%lu > %u)", + sbi->data_start, total_sectors); + goto out_invalid; + } + total_clusters = (total_sectors - sbi->data_start) / sbi->sec_per_clus; if (!is_fat32(sbi)) From 452a8467be8143747292218212671deeb186d2ae Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Wed, 10 Jun 2026 19:23:11 -0500 Subject: [PATCH 103/108] ocfs2: fix UBSAN array-index-out-of-bounds in ocfs2_sum_rightmost_rec [BUG] On-disk corruption setting l_next_free_rec to 0 in an inode's embedded extent list triggers a UBSAN panic on the next write to that file. [CAUSE] ocfs2_sum_rightmost_rec() computes i = le16_to_cpu(el->l_next_free_rec) - 1 and accesses el->l_recs[i] without validating i. When l_next_free_rec is 0, i becomes -1; when l_next_free_rec exceeds l_count, i falls past the end of the array. Either case violates the __counted_by_le(l_count) annotation on l_recs[] and triggers UBSAN. [FIX] Validate the inode's embedded extent list when the inode is read, in ocfs2_validate_inode_block(): l_count must be non-zero and no larger than the inode block can hold, and l_next_free_rec must not exceed l_count. A corrupt list is rejected at read time, before the b-tree code can index l_recs[] out of bounds. Link: https://lore.kernel.org/ain_780qc0P4ypNd@dev Signed-off-by: Ian Bridges Reported-by: syzbot+be16e33db01e6644db7a@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=be16e33db01e6644db7a Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/inode.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 6fc29920ecb2..662dbc845b8b 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -1696,6 +1696,38 @@ int ocfs2_validate_inode_block(struct super_block *sb, goto bail; } + if (ocfs2_dinode_has_extents(di)) { + struct ocfs2_extent_list *el = &di->id2.i_list; + u16 count = le16_to_cpu(el->l_count); + u16 next_free = le16_to_cpu(el->l_next_free_rec); + + if (count == 0) { + rc = ocfs2_error(sb, + "Invalid dinode %llu: extent list l_count is zero\n", + (unsigned long long)bh->b_blocknr); + goto bail; + } + /* + * The exact capacity depends on i_xattr_inline_size, another + * unvalidated on-disk field. Inline xattrs only shrink the + * list, so the no-xattr maximum is a safe upper bound that a + * valid l_count never exceeds. + */ + if (count > ocfs2_extent_recs_per_inode(sb)) { + rc = ocfs2_error(sb, + "Invalid dinode %llu: extent list l_count %u exceeds max %u\n", + (unsigned long long)bh->b_blocknr, count, + ocfs2_extent_recs_per_inode(sb)); + goto bail; + } + if (next_free > count) { + rc = ocfs2_error(sb, + "Invalid dinode %llu: extent list l_next_free_rec %u exceeds l_count %u\n", + (unsigned long long)bh->b_blocknr, next_free, count); + goto bail; + } + } + rc = 0; bail: From 07669b0abe4ce76c716e8437e198e1337cf43d1f Mon Sep 17 00:00:00 2001 From: Shardul Deshpande Date: Fri, 12 Jun 2026 23:46:32 +0530 Subject: [PATCH 104/108] treewide: fix transposed "sign" typos and update spelling.txt Several comments transpose the letters in "assigned" and "unsigned", spelling them with "sing" instead of "sign". Correct all of them. Of these, the misspelling of "assigned" is not yet flagged by checkpatch, so also add it to scripts/spelling.txt. The remaining matches of `grep -ri singed` are RISINGEDGE register and enum names, not typos. Link: https://lore.kernel.org/20260612181633.734458-1-iamsharduld@gmail.com Signed-off-by: Shardul Deshpande Suggested-by: Andrew Morton Reviewed-by: SeongJae Park Cc: Joe Perches Signed-off-by: Andrew Morton --- arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi | 2 +- drivers/gpu/drm/drm_color_mgmt.c | 2 +- drivers/scsi/elx/efct/efct_hw.h | 2 +- drivers/scsi/isci/host.c | 2 +- drivers/scsi/isci/port.c | 2 +- drivers/scsi/isci/port_config.c | 2 +- kernel/bpf/helpers.c | 2 +- scripts/spelling.txt | 1 + 8 files changed, 8 insertions(+), 7 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi b/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi index 469a5d103e3d..9ae9af40f4d2 100644 --- a/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280-qcard.dtsi @@ -562,7 +562,7 @@ &sdc1_rclk { * * This has entries that are defined by Qcard even if they go to the main * board. In cases where the pulls may be board dependent we defer those - * settings to the board device tree. Drive strengths tend to be assinged here + * settings to the board device tree. Drive strengths tend to be assigned here * but could conceivably be overwridden by board device trees. */ diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c index e7db4e4ea700..cb7f51ff83bf 100644 --- a/drivers/gpu/drm/drm_color_mgmt.c +++ b/drivers/gpu/drm/drm_color_mgmt.c @@ -54,7 +54,7 @@ * &drm_crtc_state.degamma_lut. * * “DEGAMMA_LUT_SIZE”: - * Unsinged range property to give the size of the lookup table to be set + * Unsigned range property to give the size of the lookup table to be set * on the DEGAMMA_LUT property (the size depends on the underlying * hardware). If drivers support multiple LUT sizes then they should * publish the largest size, and sub-sample smaller sized LUTs (e.g. for diff --git a/drivers/scsi/elx/efct/efct_hw.h b/drivers/scsi/elx/efct/efct_hw.h index f3f4aa78dce9..20aae04021aa 100644 --- a/drivers/scsi/elx/efct/efct_hw.h +++ b/drivers/scsi/elx/efct/efct_hw.h @@ -59,7 +59,7 @@ #define EFCT_HW_EQ_DEPTH 1024 /* - * A CQ will be assinged to each WQ + * A CQ will be assigned to each WQ * (CQ must have 2X entries of the WQ for abort * processing), plus a separate one for each RQ PAIR and one for MQ */ diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index ff199bab5d1a..1592fc552e6c 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -2488,7 +2488,7 @@ struct isci_request *sci_request_by_tag(struct isci_host *ihost, u16 io_tag) * free remote node ids * @idev: This is the device object which is requesting the a remote node * id - * @node_id: This is the remote node id that is assinged to the device if one + * @node_id: This is the remote node id that is assigned to the device if one * is available * * enum sci_status SCI_FAILURE_OUT_OF_RESOURCES if there are no available remote diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c index 10bd2aac2cb4..0dea0abb9927 100644 --- a/drivers/scsi/isci/port.c +++ b/drivers/scsi/isci/port.c @@ -464,7 +464,7 @@ static enum sci_status sci_port_set_phy(struct isci_port *iport, struct isci_phy { /* Check to see if we can add this phy to a port * that means that the phy is not part of a port and that the port does - * not already have a phy assinged to the phy index. + * not already have a phy assigned to the phy index. */ if (!iport->phy_table[iphy->phy_index] && !phy_get_non_dummy_port(iphy) && diff --git a/drivers/scsi/isci/port_config.c b/drivers/scsi/isci/port_config.c index 3b4820defe63..d709c9df823c 100644 --- a/drivers/scsi/isci/port_config.c +++ b/drivers/scsi/isci/port_config.c @@ -259,7 +259,7 @@ sci_mpc_agent_validate_phy_configuration(struct isci_host *ihost, if (!phy_mask) continue; /* - * Make sure that one or more of the phys were not already assinged to + * Make sure that one or more of the phys were not already assigned to * a different port. */ if ((phy_mask & ~assigned_phy_mask) == 0) { return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index b5314c9fed3c..6f56ba88fb61 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -3601,7 +3601,7 @@ __bpf_kfunc int bpf_copy_from_user_task_str(void *dst, u32 dst__sz, return ret + 1; } -/* Keep unsinged long in prototype so that kfunc is usable when emitted to +/* Keep unsigned long in prototype so that kfunc is usable when emitted to * vmlinux.h in BPF programs directly, but note that while in BPF prog, the * unsigned long always points to 8-byte region on stack, the kernel may only * read and write the 4-bytes on 32-bit. diff --git a/scripts/spelling.txt b/scripts/spelling.txt index 2f2e81dbda03..3372873cd7bb 100644 --- a/scripts/spelling.txt +++ b/scripts/spelling.txt @@ -176,6 +176,7 @@ assgined||assigned assiged||assigned assigment||assignment assigments||assignments +assinged||assigned assistent||assistant assocaited||associated assocated||associated From 22920541c35a9f23f219038ba5874c843a7c4419 Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Thu, 11 Jun 2026 14:35:10 -0700 Subject: [PATCH 105/108] ocfs2: avoid moving extents to occupied clusters For non-auto OCFS2_IOC_MOVE_EXT operations, userspace supplies a physical me_goal. ocfs2_move_extent() initializes new_phys_cpos from that goal and expects ocfs2_probe_alloc_group() to replace it with a free run in the target block group. The probe currently leaves *phys_cpos unchanged if the scan reaches the end of the group without finding a free run. An occupied goal at the last bit can therefore survive the probe and be passed to __ocfs2_move_extent(), which copies file data into a cluster still owned by another inode before the bitmap is updated. When the probe does find a free run, it also subtracts move_len from the ending bit. The start of an N-bit run ending at i is i - N + 1, so the current calculation can report the bit immediately before the free run. Clear *phys_cpos before scanning and use the correct free-run start. Callers already treat a zero result as -ENOSPC, so failed probes no longer continue with an occupied caller-controlled goal. Link: https://lore.kernel.org/20260611213510.16956-1-kylebot@openai.com Fixes: e6b5859cccfa ("Ocfs2/move_extents: helper to probe a proper region to move in an alloc group.") Fixes: 236b9254f8d1 ("ocfs2: fix non-auto defrag path not working issue") Assisted-by: Codex:gpt-5.5 Signed-off-by: Kyle Zeng Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/move_extents.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c index c53de4439d93..ad1678ee7cc4 100644 --- a/fs/ocfs2/move_extents.c +++ b/fs/ocfs2/move_extents.c @@ -534,6 +534,8 @@ static void ocfs2_probe_alloc_group(struct inode *inode, struct buffer_head *bh, u32 base_cpos = ocfs2_blocks_to_clusters(inode->i_sb, le64_to_cpu(gd->bg_blkno)); + *phys_cpos = 0; + for (i = base_bit; i < le16_to_cpu(gd->bg_bits); i++) { used = ocfs2_test_bit(i, (unsigned long *)gd->bg_bitmap); @@ -555,7 +557,7 @@ static void ocfs2_probe_alloc_group(struct inode *inode, struct buffer_head *bh, last_free_bits++; if (last_free_bits == move_len) { - i -= move_len; + i = i - move_len + 1; *goal_bit = i; *phys_cpos = base_cpos + i; break; From c1fff9794a165b6b64ae4ad9b54c00bc94e7daed Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Tue, 9 Jun 2026 00:54:47 +0000 Subject: [PATCH 106/108] lib: interval_tree_test: validate benchmark parameters The interval tree runtime test accepts module parameters that are later used as divisors while generating randomized intervals and while reporting average timings. For example, max_endpoint=1 makes the generated interval end value zero and the next modulo operation divides by that zero value. Reject non-positive counts and require max_endpoint to provide at least one non-zero generated endpoint before the test allocates state or starts the benchmark. [akpm@linux-foundation.org: include printk.h for pr_warn()] Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius Link: https://lore.kernel.org/20260609005446.1241288.1525a5964698.interval-tree-test-small-max-endpoint-div0@trailofbits.com Reviewed-by: Andrew Morton Signed-off-by: Andrew Morton --- lib/interval_tree_test.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/interval_tree_test.c b/lib/interval_tree_test.c index 16200feacbf3..eba2d3e28980 100644 --- a/lib/interval_tree_test.c +++ b/lib/interval_tree_test.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -311,6 +312,27 @@ static inline int span_iteration_check(void) {return 0; } static int interval_tree_test_init(void) { + if (nnodes <= 0) { + pr_warn("nnodes must be positive\n"); + return -EINVAL; + } + if (nsearches <= 0) { + pr_warn("nsearches must be positive\n"); + return -EINVAL; + } + if (perf_loops <= 0) { + pr_warn("perf_loops must be positive\n"); + return -EINVAL; + } + if (search_loops <= 0) { + pr_warn("search_loops must be positive\n"); + return -EINVAL; + } + if (max_endpoint < 2) { + pr_warn("max_endpoint must be at least 2\n"); + return -EINVAL; + } + nodes = kmalloc_objs(struct interval_tree_node, nnodes); if (!nodes) return -ENOMEM; From f9ab30c96b0f00c20c6dac93681bdae3a033d229 Mon Sep 17 00:00:00 2001 From: Ian Bridges Date: Thu, 11 Jun 2026 09:46:38 -0500 Subject: [PATCH 107/108] ocfs2: fix NULL h_transaction deref in ocfs2_assure_trans_credits [BUG] A direct write over unwritten extents can panic the kernel in ocfs2_assure_trans_credits() when the journal aborts during DIO completion. The crash is a general protection fault from a NULL pointer dereference. [CAUSE] ocfs2_dio_end_io_write() loops over a direct write's unwritten extents, marking each written under a single journal handle. If the journal aborts (for example after an I/O error) while the extent tree is being updated, the handle is left aborted with its transaction pointer cleared. The extent merge treats that failure as not critical and reports success, so the loop keeps using the handle. ocfs2_assure_trans_credits() reads the handle's remaining credits without first checking whether the handle is aborted, and that read dereferences the cleared transaction pointer. [FIX] A journal abort is recorded in the handle itself, so callers are expected to test the handle rather than rely on a returned error. Make ocfs2_assure_trans_credits() do that, as the other ocfs2 journal helpers already do, and return -EROFS when the handle is aborted. Link: https://lore.kernel.org/airKTsM1fRVN-Wj7@dev Fixes: be346c1a6eeb ("ocfs2: fix DIO failure due to insufficient transaction credits") Signed-off-by: Ian Bridges Reported-by: syzbot+e9c15ff790cea6a0cfae@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e9c15ff790cea6a0cfae Reviewed-by: Joseph Qi Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Changwei Ge Cc: Jun Piao Cc: Heming Zhao Cc: Signed-off-by: Andrew Morton --- fs/ocfs2/journal.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index fc54cc798ce3..d8afbc1a76bb 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -473,8 +473,12 @@ int ocfs2_extend_trans(handle_t *handle, int nblocks) */ int ocfs2_assure_trans_credits(handle_t *handle, int nblocks) { - int old_nblks = jbd2_handle_buffer_credits(handle); + int old_nblks; + if (is_handle_aborted(handle)) + return -EROFS; + + old_nblks = jbd2_handle_buffer_credits(handle); trace_ocfs2_assure_trans_credits(old_nblks); if (old_nblks >= nblocks) return 0; From ff6f26c58421614b02694ac9d219ac61d924bc68 Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Fri, 12 Jun 2026 11:50:20 +0000 Subject: [PATCH 108/108] ocfs2: fix circular locking dependency in ocfs2_dio_end_io_write A circular locking dependency involves INODE_ALLOC_SYSTEM_INODE, EXTENT_ALLOC_SYSTEM_INODE, and ORPHAN_DIR_SYSTEM_INODE. 1. ocfs2_mknod() acquires INODE_ALLOC then EXTENT_ALLOC. 2. ocfs2_dio_end_io_write() acquires EXTENT_ALLOC for unwritten extents, then ORPHAN_DIR via ocfs2_del_inode_from_orphan() while still holding EXTENT_ALLOC. 3. ocfs2_wipe_inode() acquires ORPHAN_DIR then INODE_ALLOC via ocfs2_remove_inode. Break the cycle in ocfs2_dio_end_io_write() by freeing the allocation contexts (releasing EXTENT_ALLOC) before acquiring ORPHAN_DIR. WARNING: possible circular locking dependency detected ------------------------------------------------------ is trying to acquire lock: ffff8881e78b33a0 (&ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE]){+.+.}-{4:4}, at: ocfs2_evict_inode+0x1539/0x43b0 fs/ocfs2/inode.c:1299 but task is already holding lock: ffff8881e78b4fa0 (&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]){+.+.}-{4:4}, at: ocfs2_evict_inode+0xe97/0x43b0 fs/ocfs2/inode.c:1299 the existing dependency chain (in reverse order) is: -> #2 (&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]){+.+.}-{4:4}: inode_lock include/linux/fs.h:1029 [inline] ocfs2_del_inode_from_orphan+0x12e/0x7a0 fs/ocfs2/namei.c:2728 ocfs2_dio_end_io+0xf9c/0x1370 fs/ocfs2/aops.c:2418 dio_complete+0x25b/0x790 fs/direct-io.c:281 -> #1 (&ocfs2_sysfile_lock_key[EXTENT_ALLOC_SYSTEM_INODE]){+.+.}-{4:4}: inode_lock include/linux/fs.h:1029 [inline] ocfs2_reserve_suballoc_bits+0x16d/0x4840 fs/ocfs2/suballoc.c:882 ocfs2_reserve_new_metadata_blocks+0x415/0x9a0 fs/ocfs2/suballoc.c:1078 ocfs2_mknod+0x10f3/0x2260 fs/ocfs2/namei.c:351 -> #0 (&ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE]){+.+.}-{4:4}: __lock_acquire+0x15a5/0x2cf0 kernel/locking/lockdep.c:5237 lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5868 down_write+0x96/0x200 kernel/locking/rwsem.c:1625 inode_lock include/linux/fs.h:1029 [inline] ocfs2_remove_inode fs/ocfs2/inode.c:733 [inline] ocfs2_wipe_inode fs/ocfs2/inode.c:896 [inline] ocfs2_delete_inode fs/ocfs2/inode.c:1157 [inline] ocfs2_evict_inode+0x1539/0x43b0 fs/ocfs2/inode.c:1299 Chain exists of: &ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE] --> &ocfs2_sysfile_lock_key[EXTENT_ALLOC_SYSTEM_INODE] --> &ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE] Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]); lock(&ocfs2_sysfile_lock_key[EXTENT_ALLOC_SYSTEM_INODE]); lock(&ocfs2_sysfile_lock_key[ORPHAN_DIR_SYSTEM_INODE]); lock(&ocfs2_sysfile_lock_key[INODE_ALLOC_SYSTEM_INODE]); *** DEADLOCK *** Link: https://lore.kernel.org/97c902a6-3bcf-43ea-9b70-f1f136a6c3f2@mail.kernel.org Fixes: d647c5b2fbf8 ("ocfs2: split transactions in dio completion to avoid credit exhaustion") Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview syzbot Reported-by: syzbot+b225d4dfce6219600c42@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=b225d4dfce6219600c42 Link: https://syzkaller.appspot.com/ai_job?id=0b53ce1e-2972-4192-aa85-8097a702762c Signed-off-by: Aleksandr Nogikh Reviewed-by: Heming Zhao Cc: Mark Fasheh Cc: Joel Becker Cc: Junxiao Bi Cc: Joseph Qi Cc: Changwei Ge Cc: Jun Piao Signed-off-by: Andrew Morton --- fs/ocfs2/aops.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 6ec198bdab12..4acdbb70882c 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -2372,6 +2372,15 @@ static int ocfs2_dio_end_io_write(struct inode *inode, unlock: up_write(&oi->ip_alloc_sem); + if (data_ac) { + ocfs2_free_alloc_context(data_ac); + data_ac = NULL; + } + if (meta_ac) { + ocfs2_free_alloc_context(meta_ac); + meta_ac = NULL; + } + /* everything looks good, let's start the cleanup */ if (!ret && dwc->dw_orphaned) { BUG_ON(dwc->dw_writer_pid != task_pid_nr(current)); @@ -2383,10 +2392,6 @@ static int ocfs2_dio_end_io_write(struct inode *inode, ocfs2_inode_unlock(inode, 1); brelse(di_bh); out: - if (data_ac) - ocfs2_free_alloc_context(data_ac); - if (meta_ac) - ocfs2_free_alloc_context(meta_ac); ocfs2_run_deallocs(osb, &dealloc); ocfs2_dio_free_write_ctx(inode, dwc);