summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/.gitignore2
-rw-r--r--lib/842/842.h2
-rw-r--r--lib/Kconfig9
-rw-r--r--lib/Kconfig.debug124
-rw-r--r--lib/Kconfig.kasan7
-rw-r--r--lib/Kconfig.kgdb1
-rw-r--r--lib/Kconfig.ubsan5
-rw-r--r--lib/Makefile70
-rw-r--r--lib/alloc_tag.c272
-rw-r--r--lib/asn1_encoder.c1
-rw-r--r--lib/atomic64_test.c1
-rw-r--r--lib/bcd.c4
-rw-r--r--lib/bch.c20
-rw-r--r--lib/bitfield_kunit.c1
-rw-r--r--lib/bootconfig.c20
-rw-r--r--lib/btree.c1
-rwxr-xr-xlib/build_OID_registry7
-rw-r--r--lib/buildid.c388
-rw-r--r--lib/checksum_kunit.c10
-rw-r--r--lib/closure.c96
-rw-r--r--lib/cmdline_kunit.c1
-rw-r--r--lib/codetag.c291
-rw-r--r--lib/cpumask_kunit.c1
-rw-r--r--lib/crypto/aes.c2
-rw-r--r--lib/crypto/arc4.c1
-rw-r--r--lib/crypto/blake2s-generic.c2
-rw-r--r--lib/crypto/chacha.c2
-rw-r--r--lib/crypto/chacha20poly1305-selftest.c2
-rw-r--r--lib/crypto/chacha20poly1305.c2
-rw-r--r--lib/crypto/curve25519-fiat32.c2
-rw-r--r--lib/crypto/curve25519-hacl64.c2
-rw-r--r--lib/crypto/des.c3
-rw-r--r--lib/crypto/libchacha.c1
-rw-r--r--lib/crypto/memneq.c2
-rw-r--r--lib/crypto/mpi/Makefile2
-rw-r--r--lib/crypto/mpi/ec.c1509
-rw-r--r--lib/crypto/mpi/mpi-add.c89
-rw-r--r--lib/crypto/mpi/mpi-bit.c178
-rw-r--r--lib/crypto/mpi/mpi-cmp.c46
-rw-r--r--lib/crypto/mpi/mpi-div.c82
-rw-r--r--lib/crypto/mpi/mpi-internal.h21
-rw-r--r--lib/crypto/mpi/mpi-inv.c143
-rw-r--r--lib/crypto/mpi/mpi-mod.c148
-rw-r--r--lib/crypto/mpi/mpi-mul.c29
-rw-r--r--lib/crypto/mpi/mpi-pow.c9
-rw-r--r--lib/crypto/mpi/mpicoder.c336
-rw-r--r--lib/crypto/mpi/mpih-mul.c25
-rw-r--r--lib/crypto/mpi/mpiutil.c184
-rw-r--r--lib/crypto/poly1305-donna32.c2
-rw-r--r--lib/crypto/poly1305-donna64.c2
-rw-r--r--lib/crypto/poly1305.c3
-rw-r--r--lib/crypto/sha1.c3
-rw-r--r--lib/crypto/sha256.c3
-rw-r--r--lib/crypto/utils.c3
-rw-r--r--lib/debugobjects.c44
-rw-r--r--lib/decompress_bunzip2.c3
-rw-r--r--lib/decompress_unlz4.c2
-rw-r--r--lib/decompress_unlzma.c2
-rw-r--r--lib/decompress_unlzo.c2
-rw-r--r--lib/decompress_unxz.c40
-rw-r--r--lib/devres.c26
-rw-r--r--lib/dhry_run.c1
-rw-r--r--lib/dim/Makefile2
-rw-r--r--lib/dim/net_dim.c144
-rw-r--r--lib/dump_stack.c10
-rw-r--r--lib/dynamic_debug.c4
-rw-r--r--lib/fault-inject.c1
-rw-r--r--lib/find_bit.c2
-rw-r--r--lib/find_bit_benchmark.c1
-rw-r--r--lib/fonts/Kconfig22
-rw-r--r--lib/fortify_kunit.c16
-rw-r--r--lib/generic-radix-tree.c82
-rw-r--r--lib/glob.c2
-rw-r--r--lib/hashtable_test.c1
-rw-r--r--lib/hexdump.c2
-rw-r--r--lib/iomap_copy.c13
-rw-r--r--lib/iov_iter.c240
-rw-r--r--lib/irq_poll.c2
-rw-r--r--lib/is_signed_type_kunit.c1
-rw-r--r--lib/kfifo.c115
-rw-r--r--lib/kobject_uevent.c17
-rw-r--r--lib/kunit/Makefile6
-rw-r--r--lib/kunit/assert.c19
-rw-r--r--lib/kunit/assert_test.c388
-rw-r--r--lib/kunit/device.c7
-rw-r--r--lib/kunit/executor.c12
-rw-r--r--lib/kunit/executor_test.c2
-rw-r--r--lib/kunit/kunit-example-test.c1
-rw-r--r--lib/kunit/kunit-test.c1
-rw-r--r--lib/kunit/platform-test.c224
-rw-r--r--lib/kunit/platform.c302
-rw-r--r--lib/kunit/test.c20
-rw-r--r--lib/kunit/user_alloc.c117
-rw-r--r--lib/kunit_iov_iter.c259
-rw-r--r--lib/list-test.c11
-rw-r--r--lib/lru_cache.c10
-rw-r--r--lib/lz4/lz4_compress.c2
-rw-r--r--lib/lz4/lz4_decompress.c2
-rw-r--r--lib/lz4/lz4defs.h2
-rw-r--r--lib/lz4/lz4hc_compress.c1
-rw-r--r--lib/lzo/lzo1x_compress.c2
-rw-r--r--lib/lzo/lzo1x_decompress_safe.c2
-rw-r--r--lib/maple_tree.c825
-rw-r--r--lib/math/Makefile2
-rw-r--r--lib/math/div64.c115
-rw-r--r--lib/math/prime_numbers.c1
-rw-r--r--lib/math/rational-test.c1
-rw-r--r--lib/math/rational.c1
-rw-r--r--lib/math/test_mul_u64_u64_div_u64.c99
-rw-r--r--lib/math/tests/Makefile3
-rw-r--r--lib/math/tests/int_pow_kunit.c52
-rw-r--r--lib/memcpy_kunit.c1
-rw-r--r--lib/objagg.c20
-rw-r--r--lib/objpool.c114
-rw-r--r--lib/overflow_kunit.c24
-rw-r--r--lib/percpu_counter.c46
-rw-r--r--lib/pldmfw/pldmfw.c2
-rw-r--r--lib/plist.c42
-rw-r--r--lib/raid6/Makefile35
-rw-r--r--lib/random32.c2
-rw-r--r--lib/rbtree.c8
-rw-r--r--lib/rhashtable.c24
-rw-r--r--lib/sbitmap.c36
-rw-r--r--lib/scatterlist.c69
-rw-r--r--lib/siphash.c2
-rw-r--r--lib/siphash_kunit.c1
-rw-r--r--lib/slub_kunit.c39
-rw-r--r--lib/sort.c14
-rw-r--r--lib/stackdepot.c11
-rw-r--r--lib/stackinit_kunit.c1
-rw-r--r--lib/string.c2
-rw-r--r--lib/string_helpers.c3
-rw-r--r--lib/string_helpers_kunit.c1
-rw-r--r--lib/string_kunit.c1
-rw-r--r--lib/strncpy_from_user.c9
-rw-r--r--lib/strnlen_user.c9
-rw-r--r--lib/test-kstrtox.c1
-rw-r--r--lib/test_bitmap.c5
-rw-r--r--lib/test_bitops.c28
-rw-r--r--lib/test_bits.c35
-rw-r--r--lib/test_blackhole_dev.c1
-rw-r--r--lib/test_bpf.c18
-rw-r--r--lib/test_dynamic_debug.c1
-rw-r--r--lib/test_firmware.c1
-rw-r--r--lib/test_fortify/.gitignore2
-rw-r--r--lib/test_fortify/Makefile28
-rw-r--r--lib/test_fortify/test_fortify.sh66
-rw-r--r--lib/test_fpu.h8
-rw-r--r--lib/test_fpu_glue.c (renamed from lib/test_fpu.c)40
-rw-r--r--lib/test_fpu_impl.c37
-rw-r--r--lib/test_free_pages.c1
-rw-r--r--lib/test_hash.c1
-rw-r--r--lib/test_hexdump.c3
-rw-r--r--lib/test_hmm.c14
-rw-r--r--lib/test_ida.c1
-rw-r--r--lib/test_kmod.c1
-rw-r--r--lib/test_kprobes.c3
-rw-r--r--lib/test_linear_ranges.c1
-rw-r--r--lib/test_list_sort.c1
-rw-r--r--lib/test_maple_tree.c1
-rw-r--r--lib/test_memcat_p.c1
-rw-r--r--lib/test_meminit.c1
-rw-r--r--lib/test_min_heap.c76
-rw-r--r--lib/test_module.c1
-rw-r--r--lib/test_objagg.c2
-rw-r--r--lib/test_objpool.c3
-rw-r--r--lib/test_printf.c27
-rw-r--r--lib/test_ref_tracker.c3
-rw-r--r--lib/test_rhashtable.c1
-rw-r--r--lib/test_scanf.c1
-rw-r--r--lib/test_sort.c15
-rw-r--r--lib/test_static_key_base.c1
-rw-r--r--lib/test_static_keys.c1
-rw-r--r--lib/test_sysctl.c1
-rw-r--r--lib/test_ubsan.c1
-rw-r--r--lib/test_user_copy.c331
-rw-r--r--lib/test_uuid.c1
-rw-r--r--lib/test_xarray.c94
-rw-r--r--lib/ts_bm.c1
-rw-r--r--lib/ts_fsm.c1
-rw-r--r--lib/ts_kmp.c1
-rw-r--r--lib/union_find.c49
-rw-r--r--lib/usercopy.c39
-rw-r--r--lib/usercopy_kunit.c335
-rw-r--r--lib/vdso/Kconfig5
-rw-r--r--lib/vdso/Makefile1
-rw-r--r--lib/vdso/getrandom.c260
-rw-r--r--lib/vdso/gettimeofday.c20
-rw-r--r--lib/vsprintf.c29
-rw-r--r--lib/xarray.c52
-rw-r--r--lib/xxhash.c2
-rw-r--r--lib/xz/Kconfig13
-rw-r--r--lib/xz/xz_crc32.c11
-rw-r--r--lib/xz/xz_dec_bcj.c191
-rw-r--r--lib/xz/xz_dec_lzma2.c15
-rw-r--r--lib/xz/xz_dec_stream.c13
-rw-r--r--lib/xz/xz_dec_syms.c14
-rw-r--r--lib/xz/xz_dec_test.c12
-rw-r--r--lib/xz/xz_lzma2.h5
-rw-r--r--lib/xz/xz_private.h42
-rw-r--r--lib/xz/xz_stream.h5
-rw-r--r--lib/zlib_deflate/deflate_syms.c1
-rw-r--r--lib/zlib_dfltcc/dfltcc.h1
-rw-r--r--lib/zlib_dfltcc/dfltcc_util.h28
-rw-r--r--lib/zstd/common/mem.h2
-rw-r--r--lib/zstd/compress/zstd_compress.c2
-rw-r--r--lib/zstd/zstd_compress_module.c49
-rw-r--r--lib/zstd/zstd_decompress_module.c36
208 files changed, 5658 insertions, 4410 deletions
diff --git a/lib/.gitignore b/lib/.gitignore
index 54596b634ecb..101a4aa92fb5 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -5,5 +5,3 @@
/gen_crc32table
/gen_crc64table
/oid_registry_data.c
-/test_fortify.log
-/test_fortify/*.log
diff --git a/lib/842/842.h b/lib/842/842.h
index 7b1f581a2907..f9e8a5dd790f 100644
--- a/lib/842/842.h
+++ b/lib/842/842.h
@@ -78,7 +78,7 @@
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/crc32.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/sw842.h>
diff --git a/lib/Kconfig b/lib/Kconfig
index d33a268bc256..b38849af6f13 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -539,13 +539,7 @@ config CPUMASK_OFFSTACK
stack overflow.
config FORCE_NR_CPUS
- bool "Set number of CPUs at compile time"
- depends on SMP && EXPERT && !COMPILE_TEST
- help
- Say Yes if you have NR_CPUS set to an actual number of possible
- CPUs in your system, not to a default value. This forces the core
- code to rely on compile-time value and optimize kernel routines
- better.
+ def_bool !SMP
config CPU_RMAP
bool
@@ -629,6 +623,7 @@ config SIGNATURE
config DIMLIB
tristate
+ depends on NET
help
Dynamic Interrupt Moderation library.
Implements an algorithm for dynamically changing CQ moderation values
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 8bba448c819b..7312ae7c3cc5 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -97,7 +97,7 @@ config BOOT_PRINTK_DELAY
using "boot_delay=N".
It is likely that you would also need to use "lpj=M" to preset
- the "loops per jiffie" value.
+ the "loops per jiffy" value.
See a previous boot log for the "lpj" value to use for your
system, and then set "lpj=M" before setting "boot_delay=N".
NOTE: Using this option may adversely affect SMP systems.
@@ -379,13 +379,15 @@ config DEBUG_INFO_BTF
depends on !DEBUG_INFO_SPLIT && !DEBUG_INFO_REDUCED
depends on !GCC_PLUGIN_RANDSTRUCT || COMPILE_TEST
depends on BPF_SYSCALL
- depends on !DEBUG_INFO_DWARF5 || PAHOLE_VERSION >= 121
+ depends on PAHOLE_VERSION >= 116
+ depends on DEBUG_INFO_DWARF4 || PAHOLE_VERSION >= 121
# pahole uses elfutils, which does not have support for Hexagon relocations
depends on !HEXAGON
help
Generate deduplicated BTF type information from DWARF debug info.
- Turning this on expects presence of pahole tool, which will convert
- DWARF type info into equivalent deduplicated BTF type info.
+ Turning this on requires pahole v1.16 or later (v1.21 or later to
+ support DWARF 5), which will convert DWARF type info into equivalent
+ deduplicated BTF type info.
config PAHOLE_HAS_SPLIT_BTF
def_bool PAHOLE_VERSION >= 119
@@ -571,6 +573,21 @@ config VMLINUX_MAP
pieces of code get eliminated with
CONFIG_LD_DEAD_CODE_DATA_ELIMINATION.
+config BUILTIN_MODULE_RANGES
+ bool "Generate address range information for builtin modules"
+ depends on !LTO
+ depends on VMLINUX_MAP
+ help
+ When modules are built into the kernel, there will be no module name
+ associated with its symbols in /proc/kallsyms. Tracers may want to
+ identify symbols by module name and symbol name regardless of whether
+ the module is configured as loadable or not.
+
+ This option generates modules.builtin.ranges in the build tree with
+ offset ranges (per ELF section) for the module(s) they belong to.
+ It also records an anchor symbol to determine the load address of the
+ section.
+
config DEBUG_FORCE_WEAK_PER_CPU
bool "Force weak per-cpu definitions"
depends on DEBUG_KERNEL
@@ -969,6 +986,37 @@ config DEBUG_STACKOVERFLOW
If in doubt, say "N".
+config CODE_TAGGING
+ bool
+ select KALLSYMS
+
+config MEM_ALLOC_PROFILING
+ bool "Enable memory allocation profiling"
+ default n
+ depends on PROC_FS
+ depends on !DEBUG_FORCE_WEAK_PER_CPU
+ select CODE_TAGGING
+ select PAGE_EXTENSION
+ select SLAB_OBJ_EXT
+ help
+ Track allocation source code and record total allocation size
+ initiated at that code location. The mechanism can be used to track
+ memory leaks with a low performance and memory impact.
+
+config MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT
+ bool "Enable memory allocation profiling by default"
+ default y
+ depends on MEM_ALLOC_PROFILING
+
+config MEM_ALLOC_PROFILING_DEBUG
+ bool "Memory allocation profiler debugging"
+ default n
+ depends on MEM_ALLOC_PROFILING
+ select MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT
+ help
+ Adds warnings with helpful error messages for memory allocation
+ profiling.
+
source "lib/Kconfig.kasan"
source "lib/Kconfig.kfence"
source "lib/Kconfig.kmsan"
@@ -1012,7 +1060,9 @@ config PANIC_TIMEOUT
Set the timeout value (in seconds) until a reboot occurs when
the kernel panics. If n = 0, then we wait forever. A timeout
value n > 0 will wait n seconds before rebooting, while a timeout
- value n < 0 will reboot immediately.
+ value n < 0 will reboot immediately. This setting can be overridden
+ with the kernel command line option panic=, and from userspace via
+ /proc/sys/kernel/panic.
config LOCKUP_DETECTOR
bool
@@ -1482,7 +1532,7 @@ config LOCKDEP_BITS
config LOCKDEP_CHAINS_BITS
int "Bitsize for MAX_LOCKDEP_CHAINS"
depends on LOCKDEP && !LOCKDEP_SMALL
- range 10 30
+ range 10 21
default 16
help
Try increasing this value if you hit "BUG: MAX_LOCKDEP_CHAINS too low!" message.
@@ -1581,6 +1631,7 @@ config SCF_TORTURE_TEST
config CSD_LOCK_WAIT_DEBUG
bool "Debugging for csd_lock_wait(), called from smp_call_function*()"
depends on DEBUG_KERNEL
+ depends on SMP
depends on 64BIT
default n
help
@@ -2140,6 +2191,14 @@ config KCOV_IRQ_AREA_SIZE
soft interrupts. This specifies the size of those areas in the
number of unsigned long words.
+config KCOV_SELFTEST
+ bool "Perform short selftests on boot"
+ depends on KCOV
+ help
+ Run short KCOV coverage collection selftests on boot.
+ On test failure, causes the kernel to panic. Recommended to be
+ enabled, ensuring critical functionality works as intended.
+
menuconfig RUNTIME_TESTING_MENU
bool "Runtime Testing"
default y
@@ -2247,6 +2306,16 @@ config TEST_DIV64
If unsure, say N.
+config TEST_MULDIV64
+ tristate "mul_u64_u64_div_u64() test"
+ depends on DEBUG_KERNEL || m
+ help
+ Enable this to turn on 'mul_u64_u64_div_u64()' function test.
+ This test is executed only once during system boot (so affects
+ only boot time), or at module load time.
+
+ If unsure, say N.
+
config TEST_IOV_ITER
tristate "Test iov_iter operation" if !KUNIT_ALL_TESTS
depends on KUNIT
@@ -2451,7 +2520,6 @@ config TEST_LKM
config TEST_BITOPS
tristate "Test module for compilation of bitops operations"
- depends on m
help
This builds the "test_bitops" module that is much like the
TEST_LKM module except that it does a basic exercise of the
@@ -2475,18 +2543,6 @@ config TEST_VMALLOC
If unsure, say N.
-config TEST_USER_COPY
- tristate "Test user/kernel boundary protections"
- depends on m
- help
- This builds the "test_user_copy" module that runs sanity checks
- on the copy_to/from_user infrastructure, making sure basic
- user/kernel boundary testing is working. If it fails to load,
- a regression has been detected in the user/kernel memory boundary
- protections.
-
- If unsure, say N.
-
config TEST_BPF
tristate "Test BPF filter functionality"
depends on m && NET
@@ -2596,6 +2652,7 @@ config RESOURCE_KUNIT_TEST
tristate "KUnit test for resource API" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
+ select GET_FREE_REGION
help
This builds the resource API unit test.
Tests the logic of API provided by resource.c and ioport.h.
@@ -2784,6 +2841,15 @@ config SIPHASH_KUNIT_TEST
This is intended to help people writing architecture-specific
optimized versions. If unsure, say N.
+config USERCOPY_KUNIT_TEST
+ tristate "KUnit Test for user/kernel boundary protections"
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds the "usercopy_kunit" module that runs sanity checks
+ on the copy_to/from_user infrastructure, making sure basic
+ user/kernel boundary testing is working.
+
config TEST_UDELAY
tristate "udelay test driver"
help
@@ -2895,7 +2961,7 @@ config TEST_FREE_PAGES
config TEST_FPU
tristate "Test floating point operations in kernel space"
- depends on X86 && !KCOV_INSTRUMENT_ALL
+ depends on ARCH_HAS_KERNEL_FPU_SUPPORT && !KCOV_INSTRUMENT_ALL
help
Enable this option to add /sys/kernel/debug/selftest_helpers/test_fpu
which will trigger a sequence of floating point operations. This is used
@@ -2994,7 +3060,7 @@ config RUST_BUILD_ASSERT_ALLOW
bool "Allow unoptimized build-time assertions"
depends on RUST
help
- Controls how are `build_error!` and `build_assert!` handled during build.
+ Controls how `build_error!` and `build_assert!` are handled during the build.
If calls to them exist in the binary, it may indicate a violated invariant
or that the optimizer failed to verify the invariant during compilation.
@@ -3022,3 +3088,19 @@ config RUST_KERNEL_DOCTESTS
endmenu # "Rust"
endmenu # Kernel hacking
+
+config INT_POW_TEST
+ tristate "Integer exponentiation (int_pow) test" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This option enables the KUnit test suite for the int_pow function,
+ which performs integer exponentiation. The test suite is designed to
+ verify that the implementation of int_pow correctly computes the power
+ of a given base raised to a given exponent.
+
+ Enabling this option will include tests that check various scenarios
+ and edge cases to ensure the accuracy and reliability of the exponentiation
+ function.
+
+ If unsure, say N
diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan
index 98016e137b7f..233ab2096924 100644
--- a/lib/Kconfig.kasan
+++ b/lib/Kconfig.kasan
@@ -22,8 +22,11 @@ config ARCH_DISABLE_KASAN_INLINE
config CC_HAS_KASAN_GENERIC
def_bool $(cc-option, -fsanitize=kernel-address)
+# GCC appears to ignore no_sanitize_address when -fsanitize=kernel-hwaddress
+# is passed. See https://bugzilla.kernel.org/show_bug.cgi?id=218854 (and
+# the linked LKML thread) for more details.
config CC_HAS_KASAN_SW_TAGS
- def_bool $(cc-option, -fsanitize=kernel-hwaddress)
+ def_bool !CC_IS_GCC && $(cc-option, -fsanitize=kernel-hwaddress)
# This option is only required for software KASAN modes.
# Old GCC versions do not have proper support for no_sanitize_address.
@@ -98,7 +101,7 @@ config KASAN_SW_TAGS
help
Enables Software Tag-Based KASAN.
- Requires GCC 11+ or Clang.
+ Requires Clang.
Supported only on arm64 CPUs and relies on Top Byte Ignore.
diff --git a/lib/Kconfig.kgdb b/lib/Kconfig.kgdb
index b5c0e6576749..537e1b3f5734 100644
--- a/lib/Kconfig.kgdb
+++ b/lib/Kconfig.kgdb
@@ -122,6 +122,7 @@ config KDB_DEFAULT_ENABLE
config KDB_KEYBOARD
bool "KGDB_KDB: keyboard as input device"
depends on VT && KGDB_KDB && !PARISC
+ depends on HAS_IOPORT
default n
help
KDB can use a PS/2 type keyboard for an input device
diff --git a/lib/Kconfig.ubsan b/lib/Kconfig.ubsan
index e81e1ac4a919..1d4aa7a83b3a 100644
--- a/lib/Kconfig.ubsan
+++ b/lib/Kconfig.ubsan
@@ -4,6 +4,7 @@ config ARCH_HAS_UBSAN
menuconfig UBSAN
bool "Undefined behaviour sanity checker"
+ depends on ARCH_HAS_UBSAN
help
This option enables the Undefined Behaviour sanity checker.
Compile-time instrumentation is used to detect various undefined
@@ -28,8 +29,8 @@ config UBSAN_TRAP
Also note that selecting Y will cause your kernel to Oops
with an "illegal instruction" error with no further details
- when a UBSAN violation occurs. (Except on arm64, which will
- report which Sanitizer failed.) This may make it hard to
+ when a UBSAN violation occurs. (Except on arm64 and x86, which
+ will report which Sanitizer failed.) This may make it hard to
determine whether an Oops was caused by UBSAN or to figure
out the details of a UBSAN violation. It makes the kernel log
output less useful for bug reports.
diff --git a/lib/Makefile b/lib/Makefile
index ed8dbf4436dd..773adf88af41 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -14,6 +14,7 @@ KCOV_INSTRUMENT_list_debug.o := n
KCOV_INSTRUMENT_debugobjects.o := n
KCOV_INSTRUMENT_dynamic_debug.o := n
KCOV_INSTRUMENT_fault-inject.o := n
+KCOV_INSTRUMENT_find_bit.o := n
# string.o implements standard library functions like memset/memcpy etc.
# Use -ffreestanding to ensure that the compiler does not try to "optimize"
@@ -34,7 +35,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
is_single_threaded.o plist.o decompress.o kobject_uevent.o \
earlycpio.o seq_buf.o siphash.o dec_and_lock.o \
nmi_backtrace.o win_minmax.o memcat_p.o \
- buildid.o objpool.o
+ buildid.o objpool.o union_find.o
lib-$(CONFIG_PRINTK) += dump_stack.o
lib-$(CONFIG_SMP) += cpumask.o
@@ -78,7 +79,6 @@ obj-$(CONFIG_TEST_LKM) += test_module.o
obj-$(CONFIG_TEST_VMALLOC) += test_vmalloc.o
obj-$(CONFIG_TEST_RHASHTABLE) += test_rhashtable.o
obj-$(CONFIG_TEST_SORT) += test_sort.o
-obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o
obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_keys.o
obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_key_base.o
obj-$(CONFIG_TEST_DYNAMIC_DEBUG) += test_dynamic_debug.o
@@ -110,30 +110,10 @@ CFLAGS_test_fprobe.o += $(CC_FLAGS_FTRACE)
obj-$(CONFIG_FPROBE_SANITY_TEST) += test_fprobe.o
obj-$(CONFIG_TEST_OBJPOOL) += test_objpool.o
-#
-# CFLAGS for compiling floating point code inside the kernel. x86/Makefile turns
-# off the generation of FPU/SSE* instructions for kernel proper but FPU_FLAGS
-# get appended last to CFLAGS and thus override those previous compiler options.
-#
-FPU_CFLAGS := -msse -msse2
-ifdef CONFIG_CC_IS_GCC
-# Stack alignment mismatch, proceed with caution.
-# GCC < 7.1 cannot compile code using `double` and -mpreferred-stack-boundary=3
-# (8B stack alignment).
-# See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53383
-#
-# The "-msse" in the first argument is there so that the
-# -mpreferred-stack-boundary=3 build error:
-#
-# -mpreferred-stack-boundary=3 is not between 4 and 12
-#
-# can be triggered. Otherwise gcc doesn't complain.
-FPU_CFLAGS += -mhard-float
-FPU_CFLAGS += $(call cc-option,-msse -mpreferred-stack-boundary=3,-mpreferred-stack-boundary=4)
-endif
-
obj-$(CONFIG_TEST_FPU) += test_fpu.o
-CFLAGS_test_fpu.o += $(FPU_CFLAGS)
+test_fpu-y := test_fpu_glue.o test_fpu_impl.o
+CFLAGS_test_fpu_impl.o += $(CC_FLAGS_FPU)
+CFLAGS_REMOVE_test_fpu_impl.o += $(CC_FLAGS_NO_FPU)
# Some KUnit files (hooks.o) need to be built-in even when KUnit is a module,
# so we can't just use obj-$(CONFIG_KUNIT).
@@ -233,6 +213,9 @@ obj-$(CONFIG_OF_RECONFIG_NOTIFIER_ERROR_INJECT) += \
of-reconfig-notifier-error-inject.o
obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o
+obj-$(CONFIG_CODE_TAGGING) += codetag.o
+obj-$(CONFIG_MEM_ALLOC_PROFILING) += alloc_tag.o
+
lib-$(CONFIG_GENERIC_BUG) += bug.o
obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o
@@ -353,7 +336,7 @@ $(obj)/oid_registry_data.c: $(srctree)/include/linux/oid_registry.h \
$(call cmd,build_OID_registry)
quiet_cmd_build_OID_registry = GEN $@
- cmd_build_OID_registry = perl $(srctree)/$(src)/build_OID_registry $< $@
+ cmd_build_OID_registry = perl $(src)/build_OID_registry $< $@
clean-files += oid_registry_data.c
@@ -405,41 +388,10 @@ CFLAGS_fortify_kunit.o += $(call cc-disable-warning, stringop-truncation)
CFLAGS_fortify_kunit.o += $(DISABLE_STRUCTLEAK_PLUGIN)
obj-$(CONFIG_FORTIFY_KUNIT_TEST) += fortify_kunit.o
obj-$(CONFIG_SIPHASH_KUNIT_TEST) += siphash_kunit.o
+obj-$(CONFIG_USERCOPY_KUNIT_TEST) += usercopy_kunit.o
obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o
obj-$(CONFIG_FIRMWARE_TABLE) += fw_table.o
-# FORTIFY_SOURCE compile-time behavior tests
-TEST_FORTIFY_SRCS = $(wildcard $(srctree)/$(src)/test_fortify/*-*.c)
-TEST_FORTIFY_LOGS = $(patsubst $(srctree)/$(src)/%.c, %.log, $(TEST_FORTIFY_SRCS))
-TEST_FORTIFY_LOG = test_fortify.log
-
-quiet_cmd_test_fortify = TEST $@
- cmd_test_fortify = $(CONFIG_SHELL) $(srctree)/scripts/test_fortify.sh \
- $< $@ "$(NM)" $(CC) $(c_flags) \
- $(call cc-disable-warning,fortify-source) \
- -DKBUILD_EXTRA_WARN1
-
-targets += $(TEST_FORTIFY_LOGS)
-clean-files += $(TEST_FORTIFY_LOGS)
-clean-files += $(addsuffix .o, $(TEST_FORTIFY_LOGS))
-$(obj)/test_fortify/%.log: $(src)/test_fortify/%.c \
- $(src)/test_fortify/test_fortify.h \
- $(srctree)/include/linux/fortify-string.h \
- $(srctree)/scripts/test_fortify.sh \
- FORCE
- $(call if_changed,test_fortify)
-
-quiet_cmd_gen_fortify_log = GEN $@
- cmd_gen_fortify_log = cat </dev/null $(filter-out FORCE,$^) 2>/dev/null > $@ || true
-
-targets += $(TEST_FORTIFY_LOG)
-clean-files += $(TEST_FORTIFY_LOG)
-$(obj)/$(TEST_FORTIFY_LOG): $(addprefix $(obj)/, $(TEST_FORTIFY_LOGS)) FORCE
- $(call if_changed,gen_fortify_log)
-
-# Fake dependency to trigger the fortify tests.
-ifeq ($(CONFIG_FORTIFY_SOURCE),y)
-$(obj)/string.o: $(obj)/$(TEST_FORTIFY_LOG)
-endif
+subdir-$(CONFIG_FORTIFY_SOURCE) += test_fortify
diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c
new file mode 100644
index 000000000000..81e5f9a70f22
--- /dev/null
+++ b/lib/alloc_tag.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/alloc_tag.h>
+#include <linux/fs.h>
+#include <linux/gfp.h>
+#include <linux/module.h>
+#include <linux/page_ext.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_buf.h>
+#include <linux/seq_file.h>
+
+static struct codetag_type *alloc_tag_cttype;
+
+DEFINE_PER_CPU(struct alloc_tag_counters, _shared_alloc_tag);
+EXPORT_SYMBOL(_shared_alloc_tag);
+
+DEFINE_STATIC_KEY_MAYBE(CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT,
+ mem_alloc_profiling_key);
+
+struct allocinfo_private {
+ struct codetag_iterator iter;
+ bool print_header;
+};
+
+static void *allocinfo_start(struct seq_file *m, loff_t *pos)
+{
+ struct allocinfo_private *priv;
+ struct codetag *ct;
+ loff_t node = *pos;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ m->private = priv;
+ if (!priv)
+ return NULL;
+
+ priv->print_header = (node == 0);
+ codetag_lock_module_list(alloc_tag_cttype, true);
+ priv->iter = codetag_get_ct_iter(alloc_tag_cttype);
+ while ((ct = codetag_next_ct(&priv->iter)) != NULL && node)
+ node--;
+
+ return ct ? priv : NULL;
+}
+
+static void *allocinfo_next(struct seq_file *m, void *arg, loff_t *pos)
+{
+ struct allocinfo_private *priv = (struct allocinfo_private *)arg;
+ struct codetag *ct = codetag_next_ct(&priv->iter);
+
+ (*pos)++;
+ if (!ct)
+ return NULL;
+
+ return priv;
+}
+
+static void allocinfo_stop(struct seq_file *m, void *arg)
+{
+ struct allocinfo_private *priv = (struct allocinfo_private *)m->private;
+
+ if (priv) {
+ codetag_lock_module_list(alloc_tag_cttype, false);
+ kfree(priv);
+ }
+}
+
+static void print_allocinfo_header(struct seq_buf *buf)
+{
+ /* Output format version, so we can change it. */
+ seq_buf_printf(buf, "allocinfo - version: 1.0\n");
+ seq_buf_printf(buf, "# <size> <calls> <tag info>\n");
+}
+
+static void alloc_tag_to_text(struct seq_buf *out, struct codetag *ct)
+{
+ struct alloc_tag *tag = ct_to_alloc_tag(ct);
+ struct alloc_tag_counters counter = alloc_tag_read(tag);
+ s64 bytes = counter.bytes;
+
+ seq_buf_printf(out, "%12lli %8llu ", bytes, counter.calls);
+ codetag_to_text(out, ct);
+ seq_buf_putc(out, ' ');
+ seq_buf_putc(out, '\n');
+}
+
+static int allocinfo_show(struct seq_file *m, void *arg)
+{
+ struct allocinfo_private *priv = (struct allocinfo_private *)arg;
+ char *bufp;
+ size_t n = seq_get_buf(m, &bufp);
+ struct seq_buf buf;
+
+ seq_buf_init(&buf, bufp, n);
+ if (priv->print_header) {
+ print_allocinfo_header(&buf);
+ priv->print_header = false;
+ }
+ alloc_tag_to_text(&buf, priv->iter.ct);
+ seq_commit(m, seq_buf_used(&buf));
+ return 0;
+}
+
+static const struct seq_operations allocinfo_seq_op = {
+ .start = allocinfo_start,
+ .next = allocinfo_next,
+ .stop = allocinfo_stop,
+ .show = allocinfo_show,
+};
+
+size_t alloc_tag_top_users(struct codetag_bytes *tags, size_t count, bool can_sleep)
+{
+ struct codetag_iterator iter;
+ struct codetag *ct;
+ struct codetag_bytes n;
+ unsigned int i, nr = 0;
+
+ if (can_sleep)
+ codetag_lock_module_list(alloc_tag_cttype, true);
+ else if (!codetag_trylock_module_list(alloc_tag_cttype))
+ return 0;
+
+ iter = codetag_get_ct_iter(alloc_tag_cttype);
+ while ((ct = codetag_next_ct(&iter))) {
+ struct alloc_tag_counters counter = alloc_tag_read(ct_to_alloc_tag(ct));
+
+ n.ct = ct;
+ n.bytes = counter.bytes;
+
+ for (i = 0; i < nr; i++)
+ if (n.bytes > tags[i].bytes)
+ break;
+
+ if (i < count) {
+ nr -= nr == count;
+ memmove(&tags[i + 1],
+ &tags[i],
+ sizeof(tags[0]) * (nr - i));
+ nr++;
+ tags[i] = n;
+ }
+ }
+
+ codetag_lock_module_list(alloc_tag_cttype, false);
+
+ return nr;
+}
+
+static void __init procfs_init(void)
+{
+ proc_create_seq("allocinfo", 0400, NULL, &allocinfo_seq_op);
+}
+
+static bool alloc_tag_module_unload(struct codetag_type *cttype,
+ struct codetag_module *cmod)
+{
+ struct codetag_iterator iter = codetag_get_ct_iter(cttype);
+ struct alloc_tag_counters counter;
+ bool module_unused = true;
+ struct alloc_tag *tag;
+ struct codetag *ct;
+
+ for (ct = codetag_next_ct(&iter); ct; ct = codetag_next_ct(&iter)) {
+ if (iter.cmod != cmod)
+ continue;
+
+ tag = ct_to_alloc_tag(ct);
+ counter = alloc_tag_read(tag);
+
+ if (WARN(counter.bytes,
+ "%s:%u module %s func:%s has %llu allocated at module unload",
+ ct->filename, ct->lineno, ct->modname, ct->function, counter.bytes))
+ module_unused = false;
+ }
+
+ return module_unused;
+}
+
+#ifdef CONFIG_MEM_ALLOC_PROFILING_ENABLED_BY_DEFAULT
+static bool mem_profiling_support __meminitdata = true;
+#else
+static bool mem_profiling_support __meminitdata;
+#endif
+
+static int __init setup_early_mem_profiling(char *str)
+{
+ bool enable;
+
+ if (!str || !str[0])
+ return -EINVAL;
+
+ if (!strncmp(str, "never", 5)) {
+ enable = false;
+ mem_profiling_support = false;
+ } else {
+ int res;
+
+ res = kstrtobool(str, &enable);
+ if (res)
+ return res;
+
+ mem_profiling_support = true;
+ }
+
+ if (enable != static_key_enabled(&mem_alloc_profiling_key)) {
+ if (enable)
+ static_branch_enable(&mem_alloc_profiling_key);
+ else
+ static_branch_disable(&mem_alloc_profiling_key);
+ }
+
+ return 0;
+}
+early_param("sysctl.vm.mem_profiling", setup_early_mem_profiling);
+
+static __init bool need_page_alloc_tagging(void)
+{
+ return mem_profiling_support;
+}
+
+static __init void init_page_alloc_tagging(void)
+{
+}
+
+struct page_ext_operations page_alloc_tagging_ops = {
+ .size = sizeof(union codetag_ref),
+ .need = need_page_alloc_tagging,
+ .init = init_page_alloc_tagging,
+};
+EXPORT_SYMBOL(page_alloc_tagging_ops);
+
+#ifdef CONFIG_SYSCTL
+static struct ctl_table memory_allocation_profiling_sysctls[] = {
+ {
+ .procname = "mem_profiling",
+ .data = &mem_alloc_profiling_key,
+#ifdef CONFIG_MEM_ALLOC_PROFILING_DEBUG
+ .mode = 0444,
+#else
+ .mode = 0644,
+#endif
+ .proc_handler = proc_do_static_key,
+ },
+};
+
+static void __init sysctl_init(void)
+{
+ if (!mem_profiling_support)
+ memory_allocation_profiling_sysctls[0].mode = 0444;
+
+ register_sysctl_init("vm", memory_allocation_profiling_sysctls);
+}
+#else /* CONFIG_SYSCTL */
+static inline void sysctl_init(void) {}
+#endif /* CONFIG_SYSCTL */
+
+static int __init alloc_tag_init(void)
+{
+ const struct codetag_type_desc desc = {
+ .section = "alloc_tags",
+ .tag_size = sizeof(struct alloc_tag),
+ .module_unload = alloc_tag_module_unload,
+ };
+
+ alloc_tag_cttype = codetag_register_type(&desc);
+ if (IS_ERR(alloc_tag_cttype))
+ return PTR_ERR(alloc_tag_cttype);
+
+ sysctl_init();
+ procfs_init();
+
+ return 0;
+}
+module_init(alloc_tag_init);
diff --git a/lib/asn1_encoder.c b/lib/asn1_encoder.c
index 0fd3c454a468..92f35aae13b1 100644
--- a/lib/asn1_encoder.c
+++ b/lib/asn1_encoder.c
@@ -449,4 +449,5 @@ asn1_encode_boolean(unsigned char *data, const unsigned char *end_data,
}
EXPORT_SYMBOL_GPL(asn1_encode_boolean);
+MODULE_DESCRIPTION("Simple encoder primitives for ASN.1 BER/DER/CER");
MODULE_LICENSE("GPL");
diff --git a/lib/atomic64_test.c b/lib/atomic64_test.c
index d9d170238165..759ea1783cc5 100644
--- a/lib/atomic64_test.c
+++ b/lib/atomic64_test.c
@@ -273,4 +273,5 @@ static __exit void test_atomics_exit(void) {}
module_init(test_atomics_init);
module_exit(test_atomics_exit);
+MODULE_DESCRIPTION("Testsuite for atomic64_t functions");
MODULE_LICENSE("GPL");
diff --git a/lib/bcd.c b/lib/bcd.c
index 7e4750b6e801..c5e79ba9cd7b 100644
--- a/lib/bcd.c
+++ b/lib/bcd.c
@@ -10,6 +10,8 @@ EXPORT_SYMBOL(_bcd2bin);
unsigned char _bin2bcd(unsigned val)
{
- return ((val / 10) << 4) + val % 10;
+ const unsigned int t = (val * 103) >> 10;
+
+ return (t << 4) | (val - t * 10);
}
EXPORT_SYMBOL(_bin2bcd);
diff --git a/lib/bch.c b/lib/bch.c
index 5f71fd76eca8..1c0cb07cdfeb 100644
--- a/lib/bch.c
+++ b/lib/bch.c
@@ -479,11 +479,8 @@ static int solve_linear_system(struct bch_control *bch, unsigned int *rows,
/* find suitable row for elimination */
for (r = p; r < m; r++) {
if (rows[r] & mask) {
- if (r != p) {
- tmp = rows[r];
- rows[r] = rows[p];
- rows[p] = tmp;
- }
+ if (r != p)
+ swap(rows[r], rows[p]);
rem = r+1;
break;
}
@@ -799,21 +796,14 @@ static void gf_poly_div(struct bch_control *bch, struct gf_poly *a,
static struct gf_poly *gf_poly_gcd(struct bch_control *bch, struct gf_poly *a,
struct gf_poly *b)
{
- struct gf_poly *tmp;
-
dbg("gcd(%s,%s)=", gf_poly_str(a), gf_poly_str(b));
- if (a->deg < b->deg) {
- tmp = b;
- b = a;
- a = tmp;
- }
+ if (a->deg < b->deg)
+ swap(a, b);
while (b->deg > 0) {
gf_poly_mod(bch, a, b, NULL);
- tmp = b;
- b = a;
- a = tmp;
+ swap(a, b);
}
dbg("%s\n", gf_poly_str(a));
diff --git a/lib/bitfield_kunit.c b/lib/bitfield_kunit.c
index 1473d8b4bf0f..5ccd86f61896 100644
--- a/lib/bitfield_kunit.c
+++ b/lib/bitfield_kunit.c
@@ -151,4 +151,5 @@ static struct kunit_suite bitfields_test_suite = {
kunit_test_suites(&bitfields_test_suite);
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_DESCRIPTION("Test cases for bitfield helpers");
MODULE_LICENSE("GPL");
diff --git a/lib/bootconfig.c b/lib/bootconfig.c
index 97f8911ea339..81f29c29f47b 100644
--- a/lib/bootconfig.c
+++ b/lib/bootconfig.c
@@ -4,8 +4,16 @@
* Masami Hiramatsu <mhiramat@kernel.org>
*/
-#ifdef __KERNEL__
+/*
+ * NOTE: This is only for tools/bootconfig, because tools/bootconfig will
+ * run the parser sanity test.
+ * This does NOT mean lib/bootconfig.c is available in the user space.
+ * However, if you change this file, please make sure the tools/bootconfig
+ * has no issue on building and running.
+ */
#include <linux/bootconfig.h>
+
+#ifdef __KERNEL__
#include <linux/bug.h>
#include <linux/ctype.h>
#include <linux/errno.h>
@@ -24,16 +32,6 @@ const char * __init xbc_get_embedded_bootconfig(size_t *size)
return (*size) ? embedded_bootconfig_data : NULL;
}
#endif
-
-#else /* !__KERNEL__ */
-/*
- * NOTE: This is only for tools/bootconfig, because tools/bootconfig will
- * run the parser sanity test.
- * This does NOT mean lib/bootconfig.c is available in the user space.
- * However, if you change this file, please make sure the tools/bootconfig
- * has no issue on building and running.
- */
-#include <linux/bootconfig.h>
#endif
/*
diff --git a/lib/btree.c b/lib/btree.c
index 49420cae3a83..bb81d3393ac5 100644
--- a/lib/btree.c
+++ b/lib/btree.c
@@ -43,7 +43,6 @@
#include <linux/slab.h>
#include <linux/module.h>
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define NODESIZE MAX(L1_CACHE_BYTES, 128)
struct btree_geo {
diff --git a/lib/build_OID_registry b/lib/build_OID_registry
index d7fc32ea8ac2..8267e8d71338 100755
--- a/lib/build_OID_registry
+++ b/lib/build_OID_registry
@@ -8,6 +8,7 @@
#
use strict;
+use Cwd qw(abs_path);
my @names = ();
my @oids = ();
@@ -17,6 +18,8 @@ if ($#ARGV != 1) {
exit(2);
}
+my $abs_srctree = abs_path($ENV{'srctree'});
+
#
# Open the file to read from
#
@@ -35,7 +38,9 @@ close IN_FILE || die;
#
open C_FILE, ">$ARGV[1]" or die;
print C_FILE "/*\n";
-print C_FILE " * Automatically generated by ", $0, ". Do not edit\n";
+my $scriptname = $0;
+$scriptname =~ s#^\Q$abs_srctree/\E##;
+print C_FILE " * Automatically generated by ", $scriptname, ". Do not edit\n";
print C_FILE " */\n";
#
diff --git a/lib/buildid.c b/lib/buildid.c
index 898301b49eb6..c4b0f376fb34 100644
--- a/lib/buildid.c
+++ b/lib/buildid.c
@@ -5,143 +5,310 @@
#include <linux/elf.h>
#include <linux/kernel.h>
#include <linux/pagemap.h>
+#include <linux/secretmem.h>
#define BUILD_ID 3
+#define MAX_PHDR_CNT 256
+
+struct freader {
+ void *buf;
+ u32 buf_sz;
+ int err;
+ union {
+ struct {
+ struct file *file;
+ struct folio *folio;
+ void *addr;
+ loff_t folio_off;
+ bool may_fault;
+ };
+ struct {
+ const char *data;
+ u64 data_sz;
+ };
+ };
+};
+
+static void freader_init_from_file(struct freader *r, void *buf, u32 buf_sz,
+ struct file *file, bool may_fault)
+{
+ memset(r, 0, sizeof(*r));
+ r->buf = buf;
+ r->buf_sz = buf_sz;
+ r->file = file;
+ r->may_fault = may_fault;
+}
+
+static void freader_init_from_mem(struct freader *r, const char *data, u64 data_sz)
+{
+ memset(r, 0, sizeof(*r));
+ r->data = data;
+ r->data_sz = data_sz;
+}
+
+static void freader_put_folio(struct freader *r)
+{
+ if (!r->folio)
+ return;
+ kunmap_local(r->addr);
+ folio_put(r->folio);
+ r->folio = NULL;
+}
+
+static int freader_get_folio(struct freader *r, loff_t file_off)
+{
+ /* check if we can just reuse current folio */
+ if (r->folio && file_off >= r->folio_off &&
+ file_off < r->folio_off + folio_size(r->folio))
+ return 0;
+
+ freader_put_folio(r);
+
+ /* reject secretmem folios created with memfd_secret() */
+ if (secretmem_mapping(r->file->f_mapping))
+ return -EFAULT;
+
+ r->folio = filemap_get_folio(r->file->f_mapping, file_off >> PAGE_SHIFT);
+
+ /* if sleeping is allowed, wait for the page, if necessary */
+ if (r->may_fault && (IS_ERR(r->folio) || !folio_test_uptodate(r->folio))) {
+ filemap_invalidate_lock_shared(r->file->f_mapping);
+ r->folio = read_cache_folio(r->file->f_mapping, file_off >> PAGE_SHIFT,
+ NULL, r->file);
+ filemap_invalidate_unlock_shared(r->file->f_mapping);
+ }
+
+ if (IS_ERR(r->folio) || !folio_test_uptodate(r->folio)) {
+ if (!IS_ERR(r->folio))
+ folio_put(r->folio);
+ r->folio = NULL;
+ return -EFAULT;
+ }
+
+ r->folio_off = folio_pos(r->folio);
+ r->addr = kmap_local_folio(r->folio, 0);
+
+ return 0;
+}
+
+static const void *freader_fetch(struct freader *r, loff_t file_off, size_t sz)
+{
+ size_t folio_sz;
+
+ /* provided internal temporary buffer should be sized correctly */
+ if (WARN_ON(r->buf && sz > r->buf_sz)) {
+ r->err = -E2BIG;
+ return NULL;
+ }
+
+ if (unlikely(file_off + sz < file_off)) {
+ r->err = -EOVERFLOW;
+ return NULL;
+ }
+
+ /* working with memory buffer is much more straightforward */
+ if (!r->buf) {
+ if (file_off + sz > r->data_sz) {
+ r->err = -ERANGE;
+ return NULL;
+ }
+ return r->data + file_off;
+ }
+
+ /* fetch or reuse folio for given file offset */
+ r->err = freader_get_folio(r, file_off);
+ if (r->err)
+ return NULL;
+
+ /* if requested data is crossing folio boundaries, we have to copy
+ * everything into our local buffer to keep a simple linear memory
+ * access interface
+ */
+ folio_sz = folio_size(r->folio);
+ if (file_off + sz > r->folio_off + folio_sz) {
+ int part_sz = r->folio_off + folio_sz - file_off;
+
+ /* copy the part that resides in the current folio */
+ memcpy(r->buf, r->addr + (file_off - r->folio_off), part_sz);
+
+ /* fetch next folio */
+ r->err = freader_get_folio(r, r->folio_off + folio_sz);
+ if (r->err)
+ return NULL;
+
+ /* copy the rest of requested data */
+ memcpy(r->buf + part_sz, r->addr, sz - part_sz);
+
+ return r->buf;
+ }
+
+ /* if data fits in a single folio, just return direct pointer */
+ return r->addr + (file_off - r->folio_off);
+}
+
+static void freader_cleanup(struct freader *r)
+{
+ if (!r->buf)
+ return; /* non-file-backed mode */
+
+ freader_put_folio(r);
+}
+
/*
* Parse build id from the note segment. This logic can be shared between
* 32-bit and 64-bit system, because Elf32_Nhdr and Elf64_Nhdr are
* identical.
*/
-static int parse_build_id_buf(unsigned char *build_id,
- __u32 *size,
- const void *note_start,
- Elf32_Word note_size)
+static int parse_build_id(struct freader *r, unsigned char *build_id, __u32 *size,
+ loff_t note_off, Elf32_Word note_size)
{
- Elf32_Word note_offs = 0, new_offs;
+ const char note_name[] = "GNU";
+ const size_t note_name_sz = sizeof(note_name);
+ u32 build_id_off, new_off, note_end, name_sz, desc_sz;
+ const Elf32_Nhdr *nhdr;
+ const char *data;
+
+ if (check_add_overflow(note_off, note_size, &note_end))
+ return -EINVAL;
- while (note_offs + sizeof(Elf32_Nhdr) < note_size) {
- Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs);
+ while (note_end - note_off > sizeof(Elf32_Nhdr) + note_name_sz) {
+ nhdr = freader_fetch(r, note_off, sizeof(Elf32_Nhdr) + note_name_sz);
+ if (!nhdr)
+ return r->err;
+
+ name_sz = READ_ONCE(nhdr->n_namesz);
+ desc_sz = READ_ONCE(nhdr->n_descsz);
+
+ new_off = note_off + sizeof(Elf32_Nhdr);
+ if (check_add_overflow(new_off, ALIGN(name_sz, 4), &new_off) ||
+ check_add_overflow(new_off, ALIGN(desc_sz, 4), &new_off) ||
+ new_off > note_end)
+ break;
if (nhdr->n_type == BUILD_ID &&
- nhdr->n_namesz == sizeof("GNU") &&
- !strcmp((char *)(nhdr + 1), "GNU") &&
- nhdr->n_descsz > 0 &&
- nhdr->n_descsz <= BUILD_ID_SIZE_MAX) {
- memcpy(build_id,
- note_start + note_offs +
- ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr),
- nhdr->n_descsz);
- memset(build_id + nhdr->n_descsz, 0,
- BUILD_ID_SIZE_MAX - nhdr->n_descsz);
+ name_sz == note_name_sz &&
+ memcmp(nhdr + 1, note_name, note_name_sz) == 0 &&
+ desc_sz > 0 && desc_sz <= BUILD_ID_SIZE_MAX) {
+ build_id_off = note_off + sizeof(Elf32_Nhdr) + ALIGN(note_name_sz, 4);
+
+ /* freader_fetch() will invalidate nhdr pointer */
+ data = freader_fetch(r, build_id_off, desc_sz);
+ if (!data)
+ return r->err;
+
+ memcpy(build_id, data, desc_sz);
+ memset(build_id + desc_sz, 0, BUILD_ID_SIZE_MAX - desc_sz);
if (size)
- *size = nhdr->n_descsz;
+ *size = desc_sz;
return 0;
}
- new_offs = note_offs + sizeof(Elf32_Nhdr) +
- ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4);
- if (new_offs <= note_offs) /* overflow */
- break;
- note_offs = new_offs;
+
+ note_off = new_off;
}
return -EINVAL;
}
-static inline int parse_build_id(const void *page_addr,
- unsigned char *build_id,
- __u32 *size,
- const void *note_start,
- Elf32_Word note_size)
+/* Parse build ID from 32-bit ELF */
+static int get_build_id_32(struct freader *r, unsigned char *build_id, __u32 *size)
{
- /* check for overflow */
- if (note_start < page_addr || note_start + note_size < note_start)
- return -EINVAL;
+ const Elf32_Ehdr *ehdr;
+ const Elf32_Phdr *phdr;
+ __u32 phnum, phoff, i;
- /* only supports note that fits in the first page */
- if (note_start + note_size > page_addr + PAGE_SIZE)
- return -EINVAL;
+ ehdr = freader_fetch(r, 0, sizeof(Elf32_Ehdr));
+ if (!ehdr)
+ return r->err;
- return parse_build_id_buf(build_id, size, note_start, note_size);
-}
+ /* subsequent freader_fetch() calls invalidate pointers, so remember locally */
+ phnum = READ_ONCE(ehdr->e_phnum);
+ phoff = READ_ONCE(ehdr->e_phoff);
-/* Parse build ID from 32-bit ELF */
-static int get_build_id_32(const void *page_addr, unsigned char *build_id,
- __u32 *size)
-{
- Elf32_Ehdr *ehdr = (Elf32_Ehdr *)page_addr;
- Elf32_Phdr *phdr;
- int i;
+ /* set upper bound on amount of segments (phdrs) we iterate */
+ if (phnum > MAX_PHDR_CNT)
+ phnum = MAX_PHDR_CNT;
- /* only supports phdr that fits in one page */
- if (ehdr->e_phnum >
- (PAGE_SIZE - sizeof(Elf32_Ehdr)) / sizeof(Elf32_Phdr))
+ /* check that phoff is not large enough to cause an overflow */
+ if (phoff + phnum * sizeof(Elf32_Phdr) < phoff)
return -EINVAL;
- phdr = (Elf32_Phdr *)(page_addr + sizeof(Elf32_Ehdr));
+ for (i = 0; i < phnum; ++i) {
+ phdr = freader_fetch(r, phoff + i * sizeof(Elf32_Phdr), sizeof(Elf32_Phdr));
+ if (!phdr)
+ return r->err;
- for (i = 0; i < ehdr->e_phnum; ++i) {
- if (phdr[i].p_type == PT_NOTE &&
- !parse_build_id(page_addr, build_id, size,
- page_addr + phdr[i].p_offset,
- phdr[i].p_filesz))
+ if (phdr->p_type == PT_NOTE &&
+ !parse_build_id(r, build_id, size, READ_ONCE(phdr->p_offset),
+ READ_ONCE(phdr->p_filesz)))
return 0;
}
return -EINVAL;
}
/* Parse build ID from 64-bit ELF */
-static int get_build_id_64(const void *page_addr, unsigned char *build_id,
- __u32 *size)
+static int get_build_id_64(struct freader *r, unsigned char *build_id, __u32 *size)
{
- Elf64_Ehdr *ehdr = (Elf64_Ehdr *)page_addr;
- Elf64_Phdr *phdr;
- int i;
+ const Elf64_Ehdr *ehdr;
+ const Elf64_Phdr *phdr;
+ __u32 phnum, i;
+ __u64 phoff;
+
+ ehdr = freader_fetch(r, 0, sizeof(Elf64_Ehdr));
+ if (!ehdr)
+ return r->err;
+
+ /* subsequent freader_fetch() calls invalidate pointers, so remember locally */
+ phnum = READ_ONCE(ehdr->e_phnum);
+ phoff = READ_ONCE(ehdr->e_phoff);
+
+ /* set upper bound on amount of segments (phdrs) we iterate */
+ if (phnum > MAX_PHDR_CNT)
+ phnum = MAX_PHDR_CNT;
- /* only supports phdr that fits in one page */
- if (ehdr->e_phnum >
- (PAGE_SIZE - sizeof(Elf64_Ehdr)) / sizeof(Elf64_Phdr))
+ /* check that phoff is not large enough to cause an overflow */
+ if (phoff + phnum * sizeof(Elf64_Phdr) < phoff)
return -EINVAL;
- phdr = (Elf64_Phdr *)(page_addr + sizeof(Elf64_Ehdr));
+ for (i = 0; i < phnum; ++i) {
+ phdr = freader_fetch(r, phoff + i * sizeof(Elf64_Phdr), sizeof(Elf64_Phdr));
+ if (!phdr)
+ return r->err;
- for (i = 0; i < ehdr->e_phnum; ++i) {
- if (phdr[i].p_type == PT_NOTE &&
- !parse_build_id(page_addr, build_id, size,
- page_addr + phdr[i].p_offset,
- phdr[i].p_filesz))
+ if (phdr->p_type == PT_NOTE &&
+ !parse_build_id(r, build_id, size, READ_ONCE(phdr->p_offset),
+ READ_ONCE(phdr->p_filesz)))
return 0;
}
+
return -EINVAL;
}
-/*
- * Parse build ID of ELF file mapped to vma
- * @vma: vma object
- * @build_id: buffer to store build id, at least BUILD_ID_SIZE long
- * @size: returns actual build id size in case of success
- *
- * Return: 0 on success, -EINVAL otherwise
- */
-int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,
- __u32 *size)
+/* enough for Elf64_Ehdr, Elf64_Phdr, and all the smaller requests */
+#define MAX_FREADER_BUF_SZ 64
+
+static int __build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,
+ __u32 *size, bool may_fault)
{
- Elf32_Ehdr *ehdr;
- struct page *page;
- void *page_addr;
+ const Elf32_Ehdr *ehdr;
+ struct freader r;
+ char buf[MAX_FREADER_BUF_SZ];
int ret;
/* only works for page backed storage */
if (!vma->vm_file)
return -EINVAL;
- page = find_get_page(vma->vm_file->f_mapping, 0);
- if (!page)
- return -EFAULT; /* page not mapped */
+ freader_init_from_file(&r, buf, sizeof(buf), vma->vm_file, may_fault);
+
+ /* fetch first 18 bytes of ELF header for checks */
+ ehdr = freader_fetch(&r, 0, offsetofend(Elf32_Ehdr, e_type));
+ if (!ehdr) {
+ ret = r.err;
+ goto out;
+ }
ret = -EINVAL;
- page_addr = kmap_local_page(page);
- ehdr = (Elf32_Ehdr *)page_addr;
/* compare magic x7f "ELF" */
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
@@ -152,15 +319,46 @@ int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,
goto out;
if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)
- ret = get_build_id_32(page_addr, build_id, size);
+ ret = get_build_id_32(&r, build_id, size);
else if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)
- ret = get_build_id_64(page_addr, build_id, size);
+ ret = get_build_id_64(&r, build_id, size);
out:
- kunmap_local(page_addr);
- put_page(page);
+ freader_cleanup(&r);
return ret;
}
+/*
+ * Parse build ID of ELF file mapped to vma
+ * @vma: vma object
+ * @build_id: buffer to store build id, at least BUILD_ID_SIZE long
+ * @size: returns actual build id size in case of success
+ *
+ * Assumes no page fault can be taken, so if relevant portions of ELF file are
+ * not already paged in, fetching of build ID fails.
+ *
+ * Return: 0 on success; negative error, otherwise
+ */
+int build_id_parse_nofault(struct vm_area_struct *vma, unsigned char *build_id, __u32 *size)
+{
+ return __build_id_parse(vma, build_id, size, false /* !may_fault */);
+}
+
+/*
+ * Parse build ID of ELF file mapped to VMA
+ * @vma: vma object
+ * @build_id: buffer to store build id, at least BUILD_ID_SIZE long
+ * @size: returns actual build id size in case of success
+ *
+ * Assumes faultable context and can cause page faults to bring in file data
+ * into page cache.
+ *
+ * Return: 0 on success; negative error, otherwise
+ */
+int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id, __u32 *size)
+{
+ return __build_id_parse(vma, build_id, size, true /* may_fault */);
+}
+
/**
* build_id_parse_buf - Get build ID from a buffer
* @buf: ELF note section(s) to parse
@@ -171,7 +369,15 @@ out:
*/
int build_id_parse_buf(const void *buf, unsigned char *build_id, u32 buf_size)
{
- return parse_build_id_buf(build_id, NULL, buf, buf_size);
+ struct freader r;
+ int err;
+
+ freader_init_from_mem(&r, buf, buf_size);
+
+ err = parse_build_id(&r, build_id, NULL, 0, buf_size);
+
+ freader_cleanup(&r);
+ return err;
}
#if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) || IS_ENABLED(CONFIG_VMCORE_INFO)
@@ -182,8 +388,8 @@ unsigned char vmlinux_build_id[BUILD_ID_SIZE_MAX] __ro_after_init;
*/
void __init init_vmlinux_build_id(void)
{
- extern const void __start_notes __weak;
- extern const void __stop_notes __weak;
+ extern const void __start_notes;
+ extern const void __stop_notes;
unsigned int size = &__stop_notes - &__start_notes;
build_id_parse_buf(&__start_notes, vmlinux_build_id, size);
diff --git a/lib/checksum_kunit.c b/lib/checksum_kunit.c
index 404dba36bae3..be04aa42125c 100644
--- a/lib/checksum_kunit.c
+++ b/lib/checksum_kunit.c
@@ -468,12 +468,9 @@ static __wsum to_wsum(u32 x)
static void assert_setup_correct(struct kunit *test)
{
- CHECK_EQ(sizeof(random_buf) / sizeof(random_buf[0]), MAX_LEN);
- CHECK_EQ(sizeof(expected_results) / sizeof(expected_results[0]),
- MAX_LEN);
- CHECK_EQ(sizeof(init_sums_no_overflow) /
- sizeof(init_sums_no_overflow[0]),
- MAX_LEN);
+ CHECK_EQ(ARRAY_SIZE(random_buf), MAX_LEN);
+ CHECK_EQ(ARRAY_SIZE(expected_results), MAX_LEN);
+ CHECK_EQ(ARRAY_SIZE(init_sums_no_overflow), MAX_LEN);
}
/*
@@ -639,4 +636,5 @@ static struct kunit_suite checksum_test_suite = {
kunit_test_suites(&checksum_test_suite);
MODULE_AUTHOR("Noah Goldstein <goldstein.w.n@gmail.com>");
+MODULE_DESCRIPTION("Test cases csum_* APIs");
MODULE_LICENSE("GPL");
diff --git a/lib/closure.c b/lib/closure.c
index c16540552d61..2bfe7d2a0048 100644
--- a/lib/closure.c
+++ b/lib/closure.c
@@ -13,14 +13,25 @@
#include <linux/seq_file.h>
#include <linux/sched/debug.h>
-static inline void closure_put_after_sub(struct closure *cl, int flags)
+static inline void closure_put_after_sub_checks(int flags)
{
int r = flags & CLOSURE_REMAINING_MASK;
- BUG_ON(flags & CLOSURE_GUARD_MASK);
- BUG_ON(!r && (flags & ~CLOSURE_DESTRUCTOR));
+ if (WARN(flags & CLOSURE_GUARD_MASK,
+ "closure has guard bits set: %x (%u)",
+ flags & CLOSURE_GUARD_MASK, (unsigned) __fls(r)))
+ r &= ~CLOSURE_GUARD_MASK;
+
+ WARN(!r && (flags & ~CLOSURE_DESTRUCTOR),
+ "closure ref hit 0 with incorrect flags set: %x (%u)",
+ flags & ~CLOSURE_DESTRUCTOR, (unsigned) __fls(flags));
+}
+
+static inline void closure_put_after_sub(struct closure *cl, int flags)
+{
+ closure_put_after_sub_checks(flags);
- if (!r) {
+ if (!(flags & CLOSURE_REMAINING_MASK)) {
smp_acquire__after_ctrl_dep();
cl->closure_get_happened = false;
@@ -139,6 +150,78 @@ void __sched __closure_sync(struct closure *cl)
}
EXPORT_SYMBOL(__closure_sync);
+/*
+ * closure_return_sync - finish running a closure, synchronously (i.e. waiting
+ * for outstanding get()s to finish) and returning once closure refcount is 0.
+ *
+ * Unlike closure_sync() this doesn't reinit the ref to 1; subsequent
+ * closure_get_not_zero() calls waill fail.
+ */
+void __sched closure_return_sync(struct closure *cl)
+{
+ struct closure_syncer s = { .task = current };
+
+ cl->s = &s;
+ set_closure_fn(cl, closure_sync_fn, NULL);
+
+ unsigned flags = atomic_sub_return_release(1 + CLOSURE_RUNNING - CLOSURE_DESTRUCTOR,
+ &cl->remaining);
+
+ closure_put_after_sub_checks(flags);
+
+ if (unlikely(flags & CLOSURE_REMAINING_MASK)) {
+ while (1) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (s.done)
+ break;
+ schedule();
+ }
+
+ __set_current_state(TASK_RUNNING);
+ }
+
+ if (cl->parent)
+ closure_put(cl->parent);
+}
+EXPORT_SYMBOL(closure_return_sync);
+
+int __sched __closure_sync_timeout(struct closure *cl, unsigned long timeout)
+{
+ struct closure_syncer s = { .task = current };
+ int ret = 0;
+
+ cl->s = &s;
+ continue_at(cl, closure_sync_fn, NULL);
+
+ while (1) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (s.done)
+ break;
+ if (!timeout) {
+ /*
+ * Carefully undo the continue_at() - but only if it
+ * hasn't completed, i.e. the final closure_put() hasn't
+ * happened yet:
+ */
+ unsigned old, new, v = atomic_read(&cl->remaining);
+ do {
+ old = v;
+ if (!old || (old & CLOSURE_RUNNING))
+ goto success;
+
+ new = old + CLOSURE_REMAINING_INITIALIZER;
+ } while ((v = atomic_cmpxchg(&cl->remaining, old, new)) != old);
+ ret = -ETIME;
+ }
+
+ timeout = schedule_timeout(timeout);
+ }
+success:
+ __set_current_state(TASK_RUNNING);
+ return ret;
+}
+EXPORT_SYMBOL(__closure_sync_timeout);
+
#ifdef CONFIG_DEBUG_CLOSURES
static LIST_HEAD(closure_list);
@@ -161,6 +244,9 @@ void closure_debug_destroy(struct closure *cl)
{
unsigned long flags;
+ if (cl->magic == CLOSURE_MAGIC_STACK)
+ return;
+
BUG_ON(cl->magic != CLOSURE_MAGIC_ALIVE);
cl->magic = CLOSURE_MAGIC_DEAD;
@@ -192,7 +278,7 @@ static int debug_show(struct seq_file *f, void *data)
seq_printf(f, " W %pS\n",
(void *) cl->waiting_on);
- seq_puts(f, "\n");
+ seq_putc(f, '\n');
}
spin_unlock_irq(&closure_list_lock);
diff --git a/lib/cmdline_kunit.c b/lib/cmdline_kunit.c
index 705b82736be0..c1602f797637 100644
--- a/lib/cmdline_kunit.c
+++ b/lib/cmdline_kunit.c
@@ -153,4 +153,5 @@ static struct kunit_suite cmdline_test_suite = {
};
kunit_test_suite(cmdline_test_suite);
+MODULE_DESCRIPTION("Test cases for API provided by cmdline.c");
MODULE_LICENSE("GPL");
diff --git a/lib/codetag.c b/lib/codetag.c
new file mode 100644
index 000000000000..d1fbbb7c2ec3
--- /dev/null
+++ b/lib/codetag.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/codetag.h>
+#include <linux/idr.h>
+#include <linux/kallsyms.h>
+#include <linux/module.h>
+#include <linux/seq_buf.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+struct codetag_type {
+ struct list_head link;
+ unsigned int count;
+ struct idr mod_idr;
+ struct rw_semaphore mod_lock; /* protects mod_idr */
+ struct codetag_type_desc desc;
+};
+
+struct codetag_range {
+ struct codetag *start;
+ struct codetag *stop;
+};
+
+struct codetag_module {
+ struct module *mod;
+ struct codetag_range range;
+};
+
+static DEFINE_MUTEX(codetag_lock);
+static LIST_HEAD(codetag_types);
+
+void codetag_lock_module_list(struct codetag_type *cttype, bool lock)
+{
+ if (lock)
+ down_read(&cttype->mod_lock);
+ else
+ up_read(&cttype->mod_lock);
+}
+
+bool codetag_trylock_module_list(struct codetag_type *cttype)
+{
+ return down_read_trylock(&cttype->mod_lock) != 0;
+}
+
+struct codetag_iterator codetag_get_ct_iter(struct codetag_type *cttype)
+{
+ struct codetag_iterator iter = {
+ .cttype = cttype,
+ .cmod = NULL,
+ .mod_id = 0,
+ .ct = NULL,
+ };
+
+ return iter;
+}
+
+static inline struct codetag *get_first_module_ct(struct codetag_module *cmod)
+{
+ return cmod->range.start < cmod->range.stop ? cmod->range.start : NULL;
+}
+
+static inline
+struct codetag *get_next_module_ct(struct codetag_iterator *iter)
+{
+ struct codetag *res = (struct codetag *)
+ ((char *)iter->ct + iter->cttype->desc.tag_size);
+
+ return res < iter->cmod->range.stop ? res : NULL;
+}
+
+struct codetag *codetag_next_ct(struct codetag_iterator *iter)
+{
+ struct codetag_type *cttype = iter->cttype;
+ struct codetag_module *cmod;
+ struct codetag *ct;
+
+ lockdep_assert_held(&cttype->mod_lock);
+
+ if (unlikely(idr_is_empty(&cttype->mod_idr)))
+ return NULL;
+
+ ct = NULL;
+ while (true) {
+ cmod = idr_find(&cttype->mod_idr, iter->mod_id);
+
+ /* If module was removed move to the next one */
+ if (!cmod)
+ cmod = idr_get_next_ul(&cttype->mod_idr,
+ &iter->mod_id);
+
+ /* Exit if no more modules */
+ if (!cmod)
+ break;
+
+ if (cmod != iter->cmod) {
+ iter->cmod = cmod;
+ ct = get_first_module_ct(cmod);
+ } else
+ ct = get_next_module_ct(iter);
+
+ if (ct)
+ break;
+
+ iter->mod_id++;
+ }
+
+ iter->ct = ct;
+ return ct;
+}
+
+void codetag_to_text(struct seq_buf *out, struct codetag *ct)
+{
+ if (ct->modname)
+ seq_buf_printf(out, "%s:%u [%s] func:%s",
+ ct->filename, ct->lineno,
+ ct->modname, ct->function);
+ else
+ seq_buf_printf(out, "%s:%u func:%s",
+ ct->filename, ct->lineno, ct->function);
+}
+
+static inline size_t range_size(const struct codetag_type *cttype,
+ const struct codetag_range *range)
+{
+ return ((char *)range->stop - (char *)range->start) /
+ cttype->desc.tag_size;
+}
+
+static void *get_symbol(struct module *mod, const char *prefix, const char *name)
+{
+ DECLARE_SEQ_BUF(sb, KSYM_NAME_LEN);
+ const char *buf;
+ void *ret;
+
+ seq_buf_printf(&sb, "%s%s", prefix, name);
+ if (seq_buf_has_overflowed(&sb))
+ return NULL;
+
+ buf = seq_buf_str(&sb);
+ preempt_disable();
+ ret = mod ?
+ (void *)find_kallsyms_symbol_value(mod, buf) :
+ (void *)kallsyms_lookup_name(buf);
+ preempt_enable();
+
+ return ret;
+}
+
+static struct codetag_range get_section_range(struct module *mod,
+ const char *section)
+{
+ return (struct codetag_range) {
+ get_symbol(mod, "__start_", section),
+ get_symbol(mod, "__stop_", section),
+ };
+}
+
+static const char *get_mod_name(__maybe_unused struct module *mod)
+{
+#ifdef CONFIG_MODULES
+ if (mod)
+ return mod->name;
+#endif
+ return "(built-in)";
+}
+
+static int codetag_module_init(struct codetag_type *cttype, struct module *mod)
+{
+ struct codetag_range range;
+ struct codetag_module *cmod;
+ int err;
+
+ range = get_section_range(mod, cttype->desc.section);
+ if (!range.start || !range.stop) {
+ pr_warn("Failed to load code tags of type %s from the module %s\n",
+ cttype->desc.section, get_mod_name(mod));
+ return -EINVAL;
+ }
+
+ /* Ignore empty ranges */
+ if (range.start == range.stop)
+ return 0;
+
+ BUG_ON(range.start > range.stop);
+
+ cmod = kmalloc(sizeof(*cmod), GFP_KERNEL);
+ if (unlikely(!cmod))
+ return -ENOMEM;
+
+ cmod->mod = mod;
+ cmod->range = range;
+
+ down_write(&cttype->mod_lock);
+ err = idr_alloc(&cttype->mod_idr, cmod, 0, 0, GFP_KERNEL);
+ if (err >= 0) {
+ cttype->count += range_size(cttype, &range);
+ if (cttype->desc.module_load)
+ cttype->desc.module_load(cttype, cmod);
+ }
+ up_write(&cttype->mod_lock);
+
+ if (err < 0) {
+ kfree(cmod);
+ return err;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_MODULES
+void codetag_load_module(struct module *mod)
+{
+ struct codetag_type *cttype;
+
+ if (!mod)
+ return;
+
+ mutex_lock(&codetag_lock);
+ list_for_each_entry(cttype, &codetag_types, link)
+ codetag_module_init(cttype, mod);
+ mutex_unlock(&codetag_lock);
+}
+
+bool codetag_unload_module(struct module *mod)
+{
+ struct codetag_type *cttype;
+ bool unload_ok = true;
+
+ if (!mod)
+ return true;
+
+ /* await any module's kfree_rcu() operations to complete */
+ kvfree_rcu_barrier();
+
+ mutex_lock(&codetag_lock);
+ list_for_each_entry(cttype, &codetag_types, link) {
+ struct codetag_module *found = NULL;
+ struct codetag_module *cmod;
+ unsigned long mod_id, tmp;
+
+ down_write(&cttype->mod_lock);
+ idr_for_each_entry_ul(&cttype->mod_idr, cmod, tmp, mod_id) {
+ if (cmod->mod && cmod->mod == mod) {
+ found = cmod;
+ break;
+ }
+ }
+ if (found) {
+ if (cttype->desc.module_unload)
+ if (!cttype->desc.module_unload(cttype, cmod))
+ unload_ok = false;
+
+ cttype->count -= range_size(cttype, &cmod->range);
+ idr_remove(&cttype->mod_idr, mod_id);
+ kfree(cmod);
+ }
+ up_write(&cttype->mod_lock);
+ }
+ mutex_unlock(&codetag_lock);
+
+ return unload_ok;
+}
+#endif /* CONFIG_MODULES */
+
+struct codetag_type *
+codetag_register_type(const struct codetag_type_desc *desc)
+{
+ struct codetag_type *cttype;
+ int err;
+
+ BUG_ON(desc->tag_size <= 0);
+
+ cttype = kzalloc(sizeof(*cttype), GFP_KERNEL);
+ if (unlikely(!cttype))
+ return ERR_PTR(-ENOMEM);
+
+ cttype->desc = *desc;
+ idr_init(&cttype->mod_idr);
+ init_rwsem(&cttype->mod_lock);
+
+ err = codetag_module_init(cttype, NULL);
+ if (unlikely(err)) {
+ kfree(cttype);
+ return ERR_PTR(err);
+ }
+
+ mutex_lock(&codetag_lock);
+ list_add_tail(&cttype->link, &codetag_types);
+ mutex_unlock(&codetag_lock);
+
+ return cttype;
+}
diff --git a/lib/cpumask_kunit.c b/lib/cpumask_kunit.c
index a105e6369efc..6b62a6bdd50e 100644
--- a/lib/cpumask_kunit.c
+++ b/lib/cpumask_kunit.c
@@ -152,4 +152,5 @@ static struct kunit_suite test_cpumask_suite = {
};
kunit_test_suite(test_cpumask_suite);
+MODULE_DESCRIPTION("KUnit tests for cpumask");
MODULE_LICENSE("GPL");
diff --git a/lib/crypto/aes.c b/lib/crypto/aes.c
index 827fe89922ff..eafe14d021f5 100644
--- a/lib/crypto/aes.c
+++ b/lib/crypto/aes.c
@@ -6,7 +6,7 @@
#include <crypto/aes.h>
#include <linux/crypto.h>
#include <linux/module.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
/*
* Emit the sbox as volatile const to prevent the compiler from doing
diff --git a/lib/crypto/arc4.c b/lib/crypto/arc4.c
index c2020f19c652..838812d18216 100644
--- a/lib/crypto/arc4.c
+++ b/lib/crypto/arc4.c
@@ -71,4 +71,5 @@ void arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len)
}
EXPORT_SYMBOL(arc4_crypt);
+MODULE_DESCRIPTION("ARC4 Cipher Algorithm");
MODULE_LICENSE("GPL");
diff --git a/lib/crypto/blake2s-generic.c b/lib/crypto/blake2s-generic.c
index 3b6dcfdd9628..09682136b57c 100644
--- a/lib/crypto/blake2s-generic.c
+++ b/lib/crypto/blake2s-generic.c
@@ -14,7 +14,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/bug.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
static const u8 blake2s_sigma[10][16] = {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
diff --git a/lib/crypto/chacha.c b/lib/crypto/chacha.c
index b748fd3d256e..3cdda3b5ee06 100644
--- a/lib/crypto/chacha.c
+++ b/lib/crypto/chacha.c
@@ -10,7 +10,7 @@
#include <linux/export.h>
#include <linux/bitops.h>
#include <linux/string.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <crypto/chacha.h>
static void chacha_permute(u32 *x, int nrounds)
diff --git a/lib/crypto/chacha20poly1305-selftest.c b/lib/crypto/chacha20poly1305-selftest.c
index fa43deda2660..2ea61c28be4f 100644
--- a/lib/crypto/chacha20poly1305-selftest.c
+++ b/lib/crypto/chacha20poly1305-selftest.c
@@ -7,7 +7,7 @@
#include <crypto/chacha.h>
#include <crypto/poly1305.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/bug.h>
#include <linux/init.h>
#include <linux/mm.h>
diff --git a/lib/crypto/chacha20poly1305.c b/lib/crypto/chacha20poly1305.c
index fa6a9440fc95..a839c0ac60b2 100644
--- a/lib/crypto/chacha20poly1305.c
+++ b/lib/crypto/chacha20poly1305.c
@@ -13,7 +13,7 @@
#include <crypto/poly1305.h>
#include <crypto/scatterwalk.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/mm.h>
diff --git a/lib/crypto/curve25519-fiat32.c b/lib/crypto/curve25519-fiat32.c
index 2fde0ec33dbd..2e0ba634e299 100644
--- a/lib/crypto/curve25519-fiat32.c
+++ b/lib/crypto/curve25519-fiat32.c
@@ -10,7 +10,7 @@
* with 128-bit integer types.
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <crypto/curve25519.h>
#include <linux/string.h>
diff --git a/lib/crypto/curve25519-hacl64.c b/lib/crypto/curve25519-hacl64.c
index c40e5d913234..c4204133afb7 100644
--- a/lib/crypto/curve25519-hacl64.c
+++ b/lib/crypto/curve25519-hacl64.c
@@ -10,7 +10,7 @@
* integer types.
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <crypto/curve25519.h>
#include <linux/string.h>
diff --git a/lib/crypto/des.c b/lib/crypto/des.c
index ef5bb8822aba..d3423b34a8e9 100644
--- a/lib/crypto/des.c
+++ b/lib/crypto/des.c
@@ -17,7 +17,7 @@
#include <linux/string.h>
#include <linux/types.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <crypto/des.h>
#include <crypto/internal/des.h>
@@ -899,4 +899,5 @@ void des3_ede_decrypt(const struct des3_ede_ctx *dctx, u8 *dst, const u8 *src)
}
EXPORT_SYMBOL_GPL(des3_ede_decrypt);
+MODULE_DESCRIPTION("DES & Triple DES EDE Cipher Algorithms");
MODULE_LICENSE("GPL");
diff --git a/lib/crypto/libchacha.c b/lib/crypto/libchacha.c
index dabc3accae05..cc1be0496eb9 100644
--- a/lib/crypto/libchacha.c
+++ b/lib/crypto/libchacha.c
@@ -32,4 +32,5 @@ void chacha_crypt_generic(u32 *state, u8 *dst, const u8 *src,
}
EXPORT_SYMBOL(chacha_crypt_generic);
+MODULE_DESCRIPTION("ChaCha stream cipher (RFC7539)");
MODULE_LICENSE("GPL");
diff --git a/lib/crypto/memneq.c b/lib/crypto/memneq.c
index 243d8677cc51..a2afd10349c9 100644
--- a/lib/crypto/memneq.c
+++ b/lib/crypto/memneq.c
@@ -59,7 +59,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <crypto/algapi.h>
#include <linux/module.h>
diff --git a/lib/crypto/mpi/Makefile b/lib/crypto/mpi/Makefile
index 6e6ef9a34fe1..9ad84079025a 100644
--- a/lib/crypto/mpi/Makefile
+++ b/lib/crypto/mpi/Makefile
@@ -13,14 +13,12 @@ mpi-y = \
generic_mpih-rshift.o \
generic_mpih-sub1.o \
generic_mpih-add1.o \
- ec.o \
mpicoder.o \
mpi-add.o \
mpi-bit.o \
mpi-cmp.o \
mpi-sub-ui.o \
mpi-div.o \
- mpi-inv.o \
mpi-mod.o \
mpi-mul.o \
mpih-cmp.o \
diff --git a/lib/crypto/mpi/ec.c b/lib/crypto/mpi/ec.c
deleted file mode 100644
index e16dca1e23d5..000000000000
--- a/lib/crypto/mpi/ec.c
+++ /dev/null
@@ -1,1509 +0,0 @@
-/* ec.c - Elliptic Curve functions
- * Copyright (C) 2007 Free Software Foundation, Inc.
- * Copyright (C) 2013 g10 Code GmbH
- *
- * This file is part of Libgcrypt.
- *
- * Libgcrypt is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * Libgcrypt is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "mpi-internal.h"
-#include "longlong.h"
-
-#define point_init(a) mpi_point_init((a))
-#define point_free(a) mpi_point_free_parts((a))
-
-#define log_error(fmt, ...) pr_err(fmt, ##__VA_ARGS__)
-#define log_fatal(fmt, ...) pr_err(fmt, ##__VA_ARGS__)
-
-#define DIM(v) (sizeof(v)/sizeof((v)[0]))
-
-
-/* Create a new point option. NBITS gives the size in bits of one
- * coordinate; it is only used to pre-allocate some resources and
- * might also be passed as 0 to use a default value.
- */
-MPI_POINT mpi_point_new(unsigned int nbits)
-{
- MPI_POINT p;
-
- (void)nbits; /* Currently not used. */
-
- p = kmalloc(sizeof(*p), GFP_KERNEL);
- if (p)
- mpi_point_init(p);
- return p;
-}
-EXPORT_SYMBOL_GPL(mpi_point_new);
-
-/* Release the point object P. P may be NULL. */
-void mpi_point_release(MPI_POINT p)
-{
- if (p) {
- mpi_point_free_parts(p);
- kfree(p);
- }
-}
-EXPORT_SYMBOL_GPL(mpi_point_release);
-
-/* Initialize the fields of a point object. gcry_mpi_point_free_parts
- * may be used to release the fields.
- */
-void mpi_point_init(MPI_POINT p)
-{
- p->x = mpi_new(0);
- p->y = mpi_new(0);
- p->z = mpi_new(0);
-}
-EXPORT_SYMBOL_GPL(mpi_point_init);
-
-/* Release the parts of a point object. */
-void mpi_point_free_parts(MPI_POINT p)
-{
- mpi_free(p->x); p->x = NULL;
- mpi_free(p->y); p->y = NULL;
- mpi_free(p->z); p->z = NULL;
-}
-EXPORT_SYMBOL_GPL(mpi_point_free_parts);
-
-/* Set the value from S into D. */
-static void point_set(MPI_POINT d, MPI_POINT s)
-{
- mpi_set(d->x, s->x);
- mpi_set(d->y, s->y);
- mpi_set(d->z, s->z);
-}
-
-static void point_resize(MPI_POINT p, struct mpi_ec_ctx *ctx)
-{
- size_t nlimbs = ctx->p->nlimbs;
-
- mpi_resize(p->x, nlimbs);
- p->x->nlimbs = nlimbs;
- mpi_resize(p->z, nlimbs);
- p->z->nlimbs = nlimbs;
-
- if (ctx->model != MPI_EC_MONTGOMERY) {
- mpi_resize(p->y, nlimbs);
- p->y->nlimbs = nlimbs;
- }
-}
-
-static void point_swap_cond(MPI_POINT d, MPI_POINT s, unsigned long swap,
- struct mpi_ec_ctx *ctx)
-{
- mpi_swap_cond(d->x, s->x, swap);
- if (ctx->model != MPI_EC_MONTGOMERY)
- mpi_swap_cond(d->y, s->y, swap);
- mpi_swap_cond(d->z, s->z, swap);
-}
-
-
-/* W = W mod P. */
-static void ec_mod(MPI w, struct mpi_ec_ctx *ec)
-{
- if (ec->t.p_barrett)
- mpi_mod_barrett(w, w, ec->t.p_barrett);
- else
- mpi_mod(w, w, ec->p);
-}
-
-static void ec_addm(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
-{
- mpi_add(w, u, v);
- ec_mod(w, ctx);
-}
-
-static void ec_subm(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ec)
-{
- mpi_sub(w, u, v);
- while (w->sign)
- mpi_add(w, w, ec->p);
- /*ec_mod(w, ec);*/
-}
-
-static void ec_mulm(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
-{
- mpi_mul(w, u, v);
- ec_mod(w, ctx);
-}
-
-/* W = 2 * U mod P. */
-static void ec_mul2(MPI w, MPI u, struct mpi_ec_ctx *ctx)
-{
- mpi_lshift(w, u, 1);
- ec_mod(w, ctx);
-}
-
-static void ec_powm(MPI w, const MPI b, const MPI e,
- struct mpi_ec_ctx *ctx)
-{
- mpi_powm(w, b, e, ctx->p);
- /* mpi_abs(w); */
-}
-
-/* Shortcut for
- * ec_powm(B, B, mpi_const(MPI_C_TWO), ctx);
- * for easier optimization.
- */
-static void ec_pow2(MPI w, const MPI b, struct mpi_ec_ctx *ctx)
-{
- /* Using mpi_mul is slightly faster (at least on amd64). */
- /* mpi_powm(w, b, mpi_const(MPI_C_TWO), ctx->p); */
- ec_mulm(w, b, b, ctx);
-}
-
-/* Shortcut for
- * ec_powm(B, B, mpi_const(MPI_C_THREE), ctx);
- * for easier optimization.
- */
-static void ec_pow3(MPI w, const MPI b, struct mpi_ec_ctx *ctx)
-{
- mpi_powm(w, b, mpi_const(MPI_C_THREE), ctx->p);
-}
-
-static void ec_invm(MPI x, MPI a, struct mpi_ec_ctx *ctx)
-{
- if (!mpi_invm(x, a, ctx->p))
- log_error("ec_invm: inverse does not exist:\n");
-}
-
-static void mpih_set_cond(mpi_ptr_t wp, mpi_ptr_t up,
- mpi_size_t usize, unsigned long set)
-{
- mpi_size_t i;
- mpi_limb_t mask = ((mpi_limb_t)0) - set;
- mpi_limb_t x;
-
- for (i = 0; i < usize; i++) {
- x = mask & (wp[i] ^ up[i]);
- wp[i] = wp[i] ^ x;
- }
-}
-
-/* Routines for 2^255 - 19. */
-
-#define LIMB_SIZE_25519 ((256+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB)
-
-static void ec_addm_25519(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
-{
- mpi_ptr_t wp, up, vp;
- mpi_size_t wsize = LIMB_SIZE_25519;
- mpi_limb_t n[LIMB_SIZE_25519];
- mpi_limb_t borrow;
-
- if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
- log_bug("addm_25519: different sizes\n");
-
- memset(n, 0, sizeof(n));
- up = u->d;
- vp = v->d;
- wp = w->d;
-
- mpihelp_add_n(wp, up, vp, wsize);
- borrow = mpihelp_sub_n(wp, wp, ctx->p->d, wsize);
- mpih_set_cond(n, ctx->p->d, wsize, (borrow != 0UL));
- mpihelp_add_n(wp, wp, n, wsize);
- wp[LIMB_SIZE_25519-1] &= ~((mpi_limb_t)1 << (255 % BITS_PER_MPI_LIMB));
-}
-
-static void ec_subm_25519(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
-{
- mpi_ptr_t wp, up, vp;
- mpi_size_t wsize = LIMB_SIZE_25519;
- mpi_limb_t n[LIMB_SIZE_25519];
- mpi_limb_t borrow;
-
- if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
- log_bug("subm_25519: different sizes\n");
-
- memset(n, 0, sizeof(n));
- up = u->d;
- vp = v->d;
- wp = w->d;
-
- borrow = mpihelp_sub_n(wp, up, vp, wsize);
- mpih_set_cond(n, ctx->p->d, wsize, (borrow != 0UL));
- mpihelp_add_n(wp, wp, n, wsize);
- wp[LIMB_SIZE_25519-1] &= ~((mpi_limb_t)1 << (255 % BITS_PER_MPI_LIMB));
-}
-
-static void ec_mulm_25519(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
-{
- mpi_ptr_t wp, up, vp;
- mpi_size_t wsize = LIMB_SIZE_25519;
- mpi_limb_t n[LIMB_SIZE_25519*2];
- mpi_limb_t m[LIMB_SIZE_25519+1];
- mpi_limb_t cy;
- int msb;
-
- (void)ctx;
- if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
- log_bug("mulm_25519: different sizes\n");
-
- up = u->d;
- vp = v->d;
- wp = w->d;
-
- mpihelp_mul_n(n, up, vp, wsize);
- memcpy(wp, n, wsize * BYTES_PER_MPI_LIMB);
- wp[LIMB_SIZE_25519-1] &= ~((mpi_limb_t)1 << (255 % BITS_PER_MPI_LIMB));
-
- memcpy(m, n+LIMB_SIZE_25519-1, (wsize+1) * BYTES_PER_MPI_LIMB);
- mpihelp_rshift(m, m, LIMB_SIZE_25519+1, (255 % BITS_PER_MPI_LIMB));
-
- memcpy(n, m, wsize * BYTES_PER_MPI_LIMB);
- cy = mpihelp_lshift(m, m, LIMB_SIZE_25519, 4);
- m[LIMB_SIZE_25519] = cy;
- cy = mpihelp_add_n(m, m, n, wsize);
- m[LIMB_SIZE_25519] += cy;
- cy = mpihelp_add_n(m, m, n, wsize);
- m[LIMB_SIZE_25519] += cy;
- cy = mpihelp_add_n(m, m, n, wsize);
- m[LIMB_SIZE_25519] += cy;
-
- cy = mpihelp_add_n(wp, wp, m, wsize);
- m[LIMB_SIZE_25519] += cy;
-
- memset(m, 0, wsize * BYTES_PER_MPI_LIMB);
- msb = (wp[LIMB_SIZE_25519-1] >> (255 % BITS_PER_MPI_LIMB));
- m[0] = (m[LIMB_SIZE_25519] * 2 + msb) * 19;
- wp[LIMB_SIZE_25519-1] &= ~((mpi_limb_t)1 << (255 % BITS_PER_MPI_LIMB));
- mpihelp_add_n(wp, wp, m, wsize);
-
- m[0] = 0;
- cy = mpihelp_sub_n(wp, wp, ctx->p->d, wsize);
- mpih_set_cond(m, ctx->p->d, wsize, (cy != 0UL));
- mpihelp_add_n(wp, wp, m, wsize);
-}
-
-static void ec_mul2_25519(MPI w, MPI u, struct mpi_ec_ctx *ctx)
-{
- ec_addm_25519(w, u, u, ctx);
-}
-
-static void ec_pow2_25519(MPI w, const MPI b, struct mpi_ec_ctx *ctx)
-{
- ec_mulm_25519(w, b, b, ctx);
-}
-
-/* Routines for 2^448 - 2^224 - 1. */
-
-#define LIMB_SIZE_448 ((448+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB)
-#define LIMB_SIZE_HALF_448 ((LIMB_SIZE_448+1)/2)
-
-static void ec_addm_448(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
-{
- mpi_ptr_t wp, up, vp;
- mpi_size_t wsize = LIMB_SIZE_448;
- mpi_limb_t n[LIMB_SIZE_448];
- mpi_limb_t cy;
-
- if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
- log_bug("addm_448: different sizes\n");
-
- memset(n, 0, sizeof(n));
- up = u->d;
- vp = v->d;
- wp = w->d;
-
- cy = mpihelp_add_n(wp, up, vp, wsize);
- mpih_set_cond(n, ctx->p->d, wsize, (cy != 0UL));
- mpihelp_sub_n(wp, wp, n, wsize);
-}
-
-static void ec_subm_448(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
-{
- mpi_ptr_t wp, up, vp;
- mpi_size_t wsize = LIMB_SIZE_448;
- mpi_limb_t n[LIMB_SIZE_448];
- mpi_limb_t borrow;
-
- if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
- log_bug("subm_448: different sizes\n");
-
- memset(n, 0, sizeof(n));
- up = u->d;
- vp = v->d;
- wp = w->d;
-
- borrow = mpihelp_sub_n(wp, up, vp, wsize);
- mpih_set_cond(n, ctx->p->d, wsize, (borrow != 0UL));
- mpihelp_add_n(wp, wp, n, wsize);
-}
-
-static void ec_mulm_448(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
-{
- mpi_ptr_t wp, up, vp;
- mpi_size_t wsize = LIMB_SIZE_448;
- mpi_limb_t n[LIMB_SIZE_448*2];
- mpi_limb_t a2[LIMB_SIZE_HALF_448];
- mpi_limb_t a3[LIMB_SIZE_HALF_448];
- mpi_limb_t b0[LIMB_SIZE_HALF_448];
- mpi_limb_t b1[LIMB_SIZE_HALF_448];
- mpi_limb_t cy;
- int i;
-#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
- mpi_limb_t b1_rest, a3_rest;
-#endif
-
- if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
- log_bug("mulm_448: different sizes\n");
-
- up = u->d;
- vp = v->d;
- wp = w->d;
-
- mpihelp_mul_n(n, up, vp, wsize);
-
- for (i = 0; i < (wsize + 1) / 2; i++) {
- b0[i] = n[i];
- b1[i] = n[i+wsize/2];
- a2[i] = n[i+wsize];
- a3[i] = n[i+wsize+wsize/2];
- }
-
-#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
- b0[LIMB_SIZE_HALF_448-1] &= ((mpi_limb_t)1UL << 32)-1;
- a2[LIMB_SIZE_HALF_448-1] &= ((mpi_limb_t)1UL << 32)-1;
-
- b1_rest = 0;
- a3_rest = 0;
-
- for (i = (wsize + 1) / 2 - 1; i >= 0; i--) {
- mpi_limb_t b1v, a3v;
- b1v = b1[i];
- a3v = a3[i];
- b1[i] = (b1_rest << 32) | (b1v >> 32);
- a3[i] = (a3_rest << 32) | (a3v >> 32);
- b1_rest = b1v & (((mpi_limb_t)1UL << 32)-1);
- a3_rest = a3v & (((mpi_limb_t)1UL << 32)-1);
- }
-#endif
-
- cy = mpihelp_add_n(b0, b0, a2, LIMB_SIZE_HALF_448);
- cy += mpihelp_add_n(b0, b0, a3, LIMB_SIZE_HALF_448);
- for (i = 0; i < (wsize + 1) / 2; i++)
- wp[i] = b0[i];
-#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
- wp[LIMB_SIZE_HALF_448-1] &= (((mpi_limb_t)1UL << 32)-1);
-#endif
-
-#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
- cy = b0[LIMB_SIZE_HALF_448-1] >> 32;
-#endif
-
- cy = mpihelp_add_1(b1, b1, LIMB_SIZE_HALF_448, cy);
- cy += mpihelp_add_n(b1, b1, a2, LIMB_SIZE_HALF_448);
- cy += mpihelp_add_n(b1, b1, a3, LIMB_SIZE_HALF_448);
- cy += mpihelp_add_n(b1, b1, a3, LIMB_SIZE_HALF_448);
-#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
- b1_rest = 0;
- for (i = (wsize + 1) / 2 - 1; i >= 0; i--) {
- mpi_limb_t b1v = b1[i];
- b1[i] = (b1_rest << 32) | (b1v >> 32);
- b1_rest = b1v & (((mpi_limb_t)1UL << 32)-1);
- }
- wp[LIMB_SIZE_HALF_448-1] |= (b1_rest << 32);
-#endif
- for (i = 0; i < wsize / 2; i++)
- wp[i+(wsize + 1) / 2] = b1[i];
-
-#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
- cy = b1[LIMB_SIZE_HALF_448-1];
-#endif
-
- memset(n, 0, wsize * BYTES_PER_MPI_LIMB);
-
-#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
- n[LIMB_SIZE_HALF_448-1] = cy << 32;
-#else
- n[LIMB_SIZE_HALF_448] = cy;
-#endif
- n[0] = cy;
- mpihelp_add_n(wp, wp, n, wsize);
-
- memset(n, 0, wsize * BYTES_PER_MPI_LIMB);
- cy = mpihelp_sub_n(wp, wp, ctx->p->d, wsize);
- mpih_set_cond(n, ctx->p->d, wsize, (cy != 0UL));
- mpihelp_add_n(wp, wp, n, wsize);
-}
-
-static void ec_mul2_448(MPI w, MPI u, struct mpi_ec_ctx *ctx)
-{
- ec_addm_448(w, u, u, ctx);
-}
-
-static void ec_pow2_448(MPI w, const MPI b, struct mpi_ec_ctx *ctx)
-{
- ec_mulm_448(w, b, b, ctx);
-}
-
-struct field_table {
- const char *p;
-
- /* computation routines for the field. */
- void (*addm)(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx);
- void (*subm)(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx);
- void (*mulm)(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx);
- void (*mul2)(MPI w, MPI u, struct mpi_ec_ctx *ctx);
- void (*pow2)(MPI w, const MPI b, struct mpi_ec_ctx *ctx);
-};
-
-static const struct field_table field_table[] = {
- {
- "0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED",
- ec_addm_25519,
- ec_subm_25519,
- ec_mulm_25519,
- ec_mul2_25519,
- ec_pow2_25519
- },
- {
- "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"
- "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
- ec_addm_448,
- ec_subm_448,
- ec_mulm_448,
- ec_mul2_448,
- ec_pow2_448
- },
- { NULL, NULL, NULL, NULL, NULL, NULL },
-};
-
-/* Force recomputation of all helper variables. */
-static void mpi_ec_get_reset(struct mpi_ec_ctx *ec)
-{
- ec->t.valid.a_is_pminus3 = 0;
- ec->t.valid.two_inv_p = 0;
-}
-
-/* Accessor for helper variable. */
-static int ec_get_a_is_pminus3(struct mpi_ec_ctx *ec)
-{
- MPI tmp;
-
- if (!ec->t.valid.a_is_pminus3) {
- ec->t.valid.a_is_pminus3 = 1;
- tmp = mpi_alloc_like(ec->p);
- mpi_sub_ui(tmp, ec->p, 3);
- ec->t.a_is_pminus3 = !mpi_cmp(ec->a, tmp);
- mpi_free(tmp);
- }
-
- return ec->t.a_is_pminus3;
-}
-
-/* Accessor for helper variable. */
-static MPI ec_get_two_inv_p(struct mpi_ec_ctx *ec)
-{
- if (!ec->t.valid.two_inv_p) {
- ec->t.valid.two_inv_p = 1;
- if (!ec->t.two_inv_p)
- ec->t.two_inv_p = mpi_alloc(0);
- ec_invm(ec->t.two_inv_p, mpi_const(MPI_C_TWO), ec);
- }
- return ec->t.two_inv_p;
-}
-
-static const char *const curve25519_bad_points[] = {
- "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
- "0x0000000000000000000000000000000000000000000000000000000000000000",
- "0x0000000000000000000000000000000000000000000000000000000000000001",
- "0x00b8495f16056286fdb1329ceb8d09da6ac49ff1fae35616aeb8413b7c7aebe0",
- "0x57119fd0dd4e22d8868e1c58c45c44045bef839c55b1d0b1248c50a3bc959c5f",
- "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec",
- "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee",
- NULL
-};
-
-static const char *const curve448_bad_points[] = {
- "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
- "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
- "0x00000000000000000000000000000000000000000000000000000000"
- "00000000000000000000000000000000000000000000000000000000",
- "0x00000000000000000000000000000000000000000000000000000000"
- "00000000000000000000000000000000000000000000000000000001",
- "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
- "fffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
- "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
- "00000000000000000000000000000000000000000000000000000000",
- NULL
-};
-
-static const char *const *bad_points_table[] = {
- curve25519_bad_points,
- curve448_bad_points,
-};
-
-static void mpi_ec_coefficient_normalize(MPI a, MPI p)
-{
- if (a->sign) {
- mpi_resize(a, p->nlimbs);
- mpihelp_sub_n(a->d, p->d, a->d, p->nlimbs);
- a->nlimbs = p->nlimbs;
- a->sign = 0;
- }
-}
-
-/* This function initialized a context for elliptic curve based on the
- * field GF(p). P is the prime specifying this field, A is the first
- * coefficient. CTX is expected to be zeroized.
- */
-void mpi_ec_init(struct mpi_ec_ctx *ctx, enum gcry_mpi_ec_models model,
- enum ecc_dialects dialect,
- int flags, MPI p, MPI a, MPI b)
-{
- int i;
- static int use_barrett = -1 /* TODO: 1 or -1 */;
-
- mpi_ec_coefficient_normalize(a, p);
- mpi_ec_coefficient_normalize(b, p);
-
- /* Fixme: Do we want to check some constraints? e.g. a < p */
-
- ctx->model = model;
- ctx->dialect = dialect;
- ctx->flags = flags;
- if (dialect == ECC_DIALECT_ED25519)
- ctx->nbits = 256;
- else
- ctx->nbits = mpi_get_nbits(p);
- ctx->p = mpi_copy(p);
- ctx->a = mpi_copy(a);
- ctx->b = mpi_copy(b);
-
- ctx->d = NULL;
- ctx->t.two_inv_p = NULL;
-
- ctx->t.p_barrett = use_barrett > 0 ? mpi_barrett_init(ctx->p, 0) : NULL;
-
- mpi_ec_get_reset(ctx);
-
- if (model == MPI_EC_MONTGOMERY) {
- for (i = 0; i < DIM(bad_points_table); i++) {
- MPI p_candidate = mpi_scanval(bad_points_table[i][0]);
- int match_p = !mpi_cmp(ctx->p, p_candidate);
- int j;
-
- mpi_free(p_candidate);
- if (!match_p)
- continue;
-
- for (j = 0; i < DIM(ctx->t.scratch) && bad_points_table[i][j]; j++)
- ctx->t.scratch[j] = mpi_scanval(bad_points_table[i][j]);
- }
- } else {
- /* Allocate scratch variables. */
- for (i = 0; i < DIM(ctx->t.scratch); i++)
- ctx->t.scratch[i] = mpi_alloc_like(ctx->p);
- }
-
- ctx->addm = ec_addm;
- ctx->subm = ec_subm;
- ctx->mulm = ec_mulm;
- ctx->mul2 = ec_mul2;
- ctx->pow2 = ec_pow2;
-
- for (i = 0; field_table[i].p; i++) {
- MPI f_p;
-
- f_p = mpi_scanval(field_table[i].p);
- if (!f_p)
- break;
-
- if (!mpi_cmp(p, f_p)) {
- ctx->addm = field_table[i].addm;
- ctx->subm = field_table[i].subm;
- ctx->mulm = field_table[i].mulm;
- ctx->mul2 = field_table[i].mul2;
- ctx->pow2 = field_table[i].pow2;
- mpi_free(f_p);
-
- mpi_resize(ctx->a, ctx->p->nlimbs);
- ctx->a->nlimbs = ctx->p->nlimbs;
-
- mpi_resize(ctx->b, ctx->p->nlimbs);
- ctx->b->nlimbs = ctx->p->nlimbs;
-
- for (i = 0; i < DIM(ctx->t.scratch) && ctx->t.scratch[i]; i++)
- ctx->t.scratch[i]->nlimbs = ctx->p->nlimbs;
-
- break;
- }
-
- mpi_free(f_p);
- }
-}
-EXPORT_SYMBOL_GPL(mpi_ec_init);
-
-void mpi_ec_deinit(struct mpi_ec_ctx *ctx)
-{
- int i;
-
- mpi_barrett_free(ctx->t.p_barrett);
-
- /* Domain parameter. */
- mpi_free(ctx->p);
- mpi_free(ctx->a);
- mpi_free(ctx->b);
- mpi_point_release(ctx->G);
- mpi_free(ctx->n);
-
- /* The key. */
- mpi_point_release(ctx->Q);
- mpi_free(ctx->d);
-
- /* Private data of ec.c. */
- mpi_free(ctx->t.two_inv_p);
-
- for (i = 0; i < DIM(ctx->t.scratch); i++)
- mpi_free(ctx->t.scratch[i]);
-}
-EXPORT_SYMBOL_GPL(mpi_ec_deinit);
-
-/* Compute the affine coordinates from the projective coordinates in
- * POINT. Set them into X and Y. If one coordinate is not required,
- * X or Y may be passed as NULL. CTX is the usual context. Returns: 0
- * on success or !0 if POINT is at infinity.
- */
-int mpi_ec_get_affine(MPI x, MPI y, MPI_POINT point, struct mpi_ec_ctx *ctx)
-{
- if (!mpi_cmp_ui(point->z, 0))
- return -1;
-
- switch (ctx->model) {
- case MPI_EC_WEIERSTRASS: /* Using Jacobian coordinates. */
- {
- MPI z1, z2, z3;
-
- z1 = mpi_new(0);
- z2 = mpi_new(0);
- ec_invm(z1, point->z, ctx); /* z1 = z^(-1) mod p */
- ec_mulm(z2, z1, z1, ctx); /* z2 = z^(-2) mod p */
-
- if (x)
- ec_mulm(x, point->x, z2, ctx);
-
- if (y) {
- z3 = mpi_new(0);
- ec_mulm(z3, z2, z1, ctx); /* z3 = z^(-3) mod p */
- ec_mulm(y, point->y, z3, ctx);
- mpi_free(z3);
- }
-
- mpi_free(z2);
- mpi_free(z1);
- }
- return 0;
-
- case MPI_EC_MONTGOMERY:
- {
- if (x)
- mpi_set(x, point->x);
-
- if (y) {
- log_fatal("%s: Getting Y-coordinate on %s is not supported\n",
- "mpi_ec_get_affine", "Montgomery");
- return -1;
- }
- }
- return 0;
-
- case MPI_EC_EDWARDS:
- {
- MPI z;
-
- z = mpi_new(0);
- ec_invm(z, point->z, ctx);
-
- mpi_resize(z, ctx->p->nlimbs);
- z->nlimbs = ctx->p->nlimbs;
-
- if (x) {
- mpi_resize(x, ctx->p->nlimbs);
- x->nlimbs = ctx->p->nlimbs;
- ctx->mulm(x, point->x, z, ctx);
- }
- if (y) {
- mpi_resize(y, ctx->p->nlimbs);
- y->nlimbs = ctx->p->nlimbs;
- ctx->mulm(y, point->y, z, ctx);
- }
-
- mpi_free(z);
- }
- return 0;
-
- default:
- return -1;
- }
-}
-EXPORT_SYMBOL_GPL(mpi_ec_get_affine);
-
-/* RESULT = 2 * POINT (Weierstrass version). */
-static void dup_point_weierstrass(MPI_POINT result,
- MPI_POINT point, struct mpi_ec_ctx *ctx)
-{
-#define x3 (result->x)
-#define y3 (result->y)
-#define z3 (result->z)
-#define t1 (ctx->t.scratch[0])
-#define t2 (ctx->t.scratch[1])
-#define t3 (ctx->t.scratch[2])
-#define l1 (ctx->t.scratch[3])
-#define l2 (ctx->t.scratch[4])
-#define l3 (ctx->t.scratch[5])
-
- if (!mpi_cmp_ui(point->y, 0) || !mpi_cmp_ui(point->z, 0)) {
- /* P_y == 0 || P_z == 0 => [1:1:0] */
- mpi_set_ui(x3, 1);
- mpi_set_ui(y3, 1);
- mpi_set_ui(z3, 0);
- } else {
- if (ec_get_a_is_pminus3(ctx)) {
- /* Use the faster case. */
- /* L1 = 3(X - Z^2)(X + Z^2) */
- /* T1: used for Z^2. */
- /* T2: used for the right term. */
- ec_pow2(t1, point->z, ctx);
- ec_subm(l1, point->x, t1, ctx);
- ec_mulm(l1, l1, mpi_const(MPI_C_THREE), ctx);
- ec_addm(t2, point->x, t1, ctx);
- ec_mulm(l1, l1, t2, ctx);
- } else {
- /* Standard case. */
- /* L1 = 3X^2 + aZ^4 */
- /* T1: used for aZ^4. */
- ec_pow2(l1, point->x, ctx);
- ec_mulm(l1, l1, mpi_const(MPI_C_THREE), ctx);
- ec_powm(t1, point->z, mpi_const(MPI_C_FOUR), ctx);
- ec_mulm(t1, t1, ctx->a, ctx);
- ec_addm(l1, l1, t1, ctx);
- }
- /* Z3 = 2YZ */
- ec_mulm(z3, point->y, point->z, ctx);
- ec_mul2(z3, z3, ctx);
-
- /* L2 = 4XY^2 */
- /* T2: used for Y2; required later. */
- ec_pow2(t2, point->y, ctx);
- ec_mulm(l2, t2, point->x, ctx);
- ec_mulm(l2, l2, mpi_const(MPI_C_FOUR), ctx);
-
- /* X3 = L1^2 - 2L2 */
- /* T1: used for L2^2. */
- ec_pow2(x3, l1, ctx);
- ec_mul2(t1, l2, ctx);
- ec_subm(x3, x3, t1, ctx);
-
- /* L3 = 8Y^4 */
- /* T2: taken from above. */
- ec_pow2(t2, t2, ctx);
- ec_mulm(l3, t2, mpi_const(MPI_C_EIGHT), ctx);
-
- /* Y3 = L1(L2 - X3) - L3 */
- ec_subm(y3, l2, x3, ctx);
- ec_mulm(y3, y3, l1, ctx);
- ec_subm(y3, y3, l3, ctx);
- }
-
-#undef x3
-#undef y3
-#undef z3
-#undef t1
-#undef t2
-#undef t3
-#undef l1
-#undef l2
-#undef l3
-}
-
-/* RESULT = 2 * POINT (Montgomery version). */
-static void dup_point_montgomery(MPI_POINT result,
- MPI_POINT point, struct mpi_ec_ctx *ctx)
-{
- (void)result;
- (void)point;
- (void)ctx;
- log_fatal("%s: %s not yet supported\n",
- "mpi_ec_dup_point", "Montgomery");
-}
-
-/* RESULT = 2 * POINT (Twisted Edwards version). */
-static void dup_point_edwards(MPI_POINT result,
- MPI_POINT point, struct mpi_ec_ctx *ctx)
-{
-#define X1 (point->x)
-#define Y1 (point->y)
-#define Z1 (point->z)
-#define X3 (result->x)
-#define Y3 (result->y)
-#define Z3 (result->z)
-#define B (ctx->t.scratch[0])
-#define C (ctx->t.scratch[1])
-#define D (ctx->t.scratch[2])
-#define E (ctx->t.scratch[3])
-#define F (ctx->t.scratch[4])
-#define H (ctx->t.scratch[5])
-#define J (ctx->t.scratch[6])
-
- /* Compute: (X_3 : Y_3 : Z_3) = 2( X_1 : Y_1 : Z_1 ) */
-
- /* B = (X_1 + Y_1)^2 */
- ctx->addm(B, X1, Y1, ctx);
- ctx->pow2(B, B, ctx);
-
- /* C = X_1^2 */
- /* D = Y_1^2 */
- ctx->pow2(C, X1, ctx);
- ctx->pow2(D, Y1, ctx);
-
- /* E = aC */
- if (ctx->dialect == ECC_DIALECT_ED25519)
- ctx->subm(E, ctx->p, C, ctx);
- else
- ctx->mulm(E, ctx->a, C, ctx);
-
- /* F = E + D */
- ctx->addm(F, E, D, ctx);
-
- /* H = Z_1^2 */
- ctx->pow2(H, Z1, ctx);
-
- /* J = F - 2H */
- ctx->mul2(J, H, ctx);
- ctx->subm(J, F, J, ctx);
-
- /* X_3 = (B - C - D) · J */
- ctx->subm(X3, B, C, ctx);
- ctx->subm(X3, X3, D, ctx);
- ctx->mulm(X3, X3, J, ctx);
-
- /* Y_3 = F · (E - D) */
- ctx->subm(Y3, E, D, ctx);
- ctx->mulm(Y3, Y3, F, ctx);
-
- /* Z_3 = F · J */
- ctx->mulm(Z3, F, J, ctx);
-
-#undef X1
-#undef Y1
-#undef Z1
-#undef X3
-#undef Y3
-#undef Z3
-#undef B
-#undef C
-#undef D
-#undef E
-#undef F
-#undef H
-#undef J
-}
-
-/* RESULT = 2 * POINT */
-static void
-mpi_ec_dup_point(MPI_POINT result, MPI_POINT point, struct mpi_ec_ctx *ctx)
-{
- switch (ctx->model) {
- case MPI_EC_WEIERSTRASS:
- dup_point_weierstrass(result, point, ctx);
- break;
- case MPI_EC_MONTGOMERY:
- dup_point_montgomery(result, point, ctx);
- break;
- case MPI_EC_EDWARDS:
- dup_point_edwards(result, point, ctx);
- break;
- }
-}
-
-/* RESULT = P1 + P2 (Weierstrass version).*/
-static void add_points_weierstrass(MPI_POINT result,
- MPI_POINT p1, MPI_POINT p2,
- struct mpi_ec_ctx *ctx)
-{
-#define x1 (p1->x)
-#define y1 (p1->y)
-#define z1 (p1->z)
-#define x2 (p2->x)
-#define y2 (p2->y)
-#define z2 (p2->z)
-#define x3 (result->x)
-#define y3 (result->y)
-#define z3 (result->z)
-#define l1 (ctx->t.scratch[0])
-#define l2 (ctx->t.scratch[1])
-#define l3 (ctx->t.scratch[2])
-#define l4 (ctx->t.scratch[3])
-#define l5 (ctx->t.scratch[4])
-#define l6 (ctx->t.scratch[5])
-#define l7 (ctx->t.scratch[6])
-#define l8 (ctx->t.scratch[7])
-#define l9 (ctx->t.scratch[8])
-#define t1 (ctx->t.scratch[9])
-#define t2 (ctx->t.scratch[10])
-
- if ((!mpi_cmp(x1, x2)) && (!mpi_cmp(y1, y2)) && (!mpi_cmp(z1, z2))) {
- /* Same point; need to call the duplicate function. */
- mpi_ec_dup_point(result, p1, ctx);
- } else if (!mpi_cmp_ui(z1, 0)) {
- /* P1 is at infinity. */
- mpi_set(x3, p2->x);
- mpi_set(y3, p2->y);
- mpi_set(z3, p2->z);
- } else if (!mpi_cmp_ui(z2, 0)) {
- /* P2 is at infinity. */
- mpi_set(x3, p1->x);
- mpi_set(y3, p1->y);
- mpi_set(z3, p1->z);
- } else {
- int z1_is_one = !mpi_cmp_ui(z1, 1);
- int z2_is_one = !mpi_cmp_ui(z2, 1);
-
- /* l1 = x1 z2^2 */
- /* l2 = x2 z1^2 */
- if (z2_is_one)
- mpi_set(l1, x1);
- else {
- ec_pow2(l1, z2, ctx);
- ec_mulm(l1, l1, x1, ctx);
- }
- if (z1_is_one)
- mpi_set(l2, x2);
- else {
- ec_pow2(l2, z1, ctx);
- ec_mulm(l2, l2, x2, ctx);
- }
- /* l3 = l1 - l2 */
- ec_subm(l3, l1, l2, ctx);
- /* l4 = y1 z2^3 */
- ec_powm(l4, z2, mpi_const(MPI_C_THREE), ctx);
- ec_mulm(l4, l4, y1, ctx);
- /* l5 = y2 z1^3 */
- ec_powm(l5, z1, mpi_const(MPI_C_THREE), ctx);
- ec_mulm(l5, l5, y2, ctx);
- /* l6 = l4 - l5 */
- ec_subm(l6, l4, l5, ctx);
-
- if (!mpi_cmp_ui(l3, 0)) {
- if (!mpi_cmp_ui(l6, 0)) {
- /* P1 and P2 are the same - use duplicate function. */
- mpi_ec_dup_point(result, p1, ctx);
- } else {
- /* P1 is the inverse of P2. */
- mpi_set_ui(x3, 1);
- mpi_set_ui(y3, 1);
- mpi_set_ui(z3, 0);
- }
- } else {
- /* l7 = l1 + l2 */
- ec_addm(l7, l1, l2, ctx);
- /* l8 = l4 + l5 */
- ec_addm(l8, l4, l5, ctx);
- /* z3 = z1 z2 l3 */
- ec_mulm(z3, z1, z2, ctx);
- ec_mulm(z3, z3, l3, ctx);
- /* x3 = l6^2 - l7 l3^2 */
- ec_pow2(t1, l6, ctx);
- ec_pow2(t2, l3, ctx);
- ec_mulm(t2, t2, l7, ctx);
- ec_subm(x3, t1, t2, ctx);
- /* l9 = l7 l3^2 - 2 x3 */
- ec_mul2(t1, x3, ctx);
- ec_subm(l9, t2, t1, ctx);
- /* y3 = (l9 l6 - l8 l3^3)/2 */
- ec_mulm(l9, l9, l6, ctx);
- ec_powm(t1, l3, mpi_const(MPI_C_THREE), ctx); /* fixme: Use saved value*/
- ec_mulm(t1, t1, l8, ctx);
- ec_subm(y3, l9, t1, ctx);
- ec_mulm(y3, y3, ec_get_two_inv_p(ctx), ctx);
- }
- }
-
-#undef x1
-#undef y1
-#undef z1
-#undef x2
-#undef y2
-#undef z2
-#undef x3
-#undef y3
-#undef z3
-#undef l1
-#undef l2
-#undef l3
-#undef l4
-#undef l5
-#undef l6
-#undef l7
-#undef l8
-#undef l9
-#undef t1
-#undef t2
-}
-
-/* RESULT = P1 + P2 (Montgomery version).*/
-static void add_points_montgomery(MPI_POINT result,
- MPI_POINT p1, MPI_POINT p2,
- struct mpi_ec_ctx *ctx)
-{
- (void)result;
- (void)p1;
- (void)p2;
- (void)ctx;
- log_fatal("%s: %s not yet supported\n",
- "mpi_ec_add_points", "Montgomery");
-}
-
-/* RESULT = P1 + P2 (Twisted Edwards version).*/
-static void add_points_edwards(MPI_POINT result,
- MPI_POINT p1, MPI_POINT p2,
- struct mpi_ec_ctx *ctx)
-{
-#define X1 (p1->x)
-#define Y1 (p1->y)
-#define Z1 (p1->z)
-#define X2 (p2->x)
-#define Y2 (p2->y)
-#define Z2 (p2->z)
-#define X3 (result->x)
-#define Y3 (result->y)
-#define Z3 (result->z)
-#define A (ctx->t.scratch[0])
-#define B (ctx->t.scratch[1])
-#define C (ctx->t.scratch[2])
-#define D (ctx->t.scratch[3])
-#define E (ctx->t.scratch[4])
-#define F (ctx->t.scratch[5])
-#define G (ctx->t.scratch[6])
-#define tmp (ctx->t.scratch[7])
-
- point_resize(result, ctx);
-
- /* Compute: (X_3 : Y_3 : Z_3) = (X_1 : Y_1 : Z_1) + (X_2 : Y_2 : Z_3) */
-
- /* A = Z1 · Z2 */
- ctx->mulm(A, Z1, Z2, ctx);
-
- /* B = A^2 */
- ctx->pow2(B, A, ctx);
-
- /* C = X1 · X2 */
- ctx->mulm(C, X1, X2, ctx);
-
- /* D = Y1 · Y2 */
- ctx->mulm(D, Y1, Y2, ctx);
-
- /* E = d · C · D */
- ctx->mulm(E, ctx->b, C, ctx);
- ctx->mulm(E, E, D, ctx);
-
- /* F = B - E */
- ctx->subm(F, B, E, ctx);
-
- /* G = B + E */
- ctx->addm(G, B, E, ctx);
-
- /* X_3 = A · F · ((X_1 + Y_1) · (X_2 + Y_2) - C - D) */
- ctx->addm(tmp, X1, Y1, ctx);
- ctx->addm(X3, X2, Y2, ctx);
- ctx->mulm(X3, X3, tmp, ctx);
- ctx->subm(X3, X3, C, ctx);
- ctx->subm(X3, X3, D, ctx);
- ctx->mulm(X3, X3, F, ctx);
- ctx->mulm(X3, X3, A, ctx);
-
- /* Y_3 = A · G · (D - aC) */
- if (ctx->dialect == ECC_DIALECT_ED25519) {
- ctx->addm(Y3, D, C, ctx);
- } else {
- ctx->mulm(Y3, ctx->a, C, ctx);
- ctx->subm(Y3, D, Y3, ctx);
- }
- ctx->mulm(Y3, Y3, G, ctx);
- ctx->mulm(Y3, Y3, A, ctx);
-
- /* Z_3 = F · G */
- ctx->mulm(Z3, F, G, ctx);
-
-
-#undef X1
-#undef Y1
-#undef Z1
-#undef X2
-#undef Y2
-#undef Z2
-#undef X3
-#undef Y3
-#undef Z3
-#undef A
-#undef B
-#undef C
-#undef D
-#undef E
-#undef F
-#undef G
-#undef tmp
-}
-
-/* Compute a step of Montgomery Ladder (only use X and Z in the point).
- * Inputs: P1, P2, and x-coordinate of DIF = P1 - P1.
- * Outputs: PRD = 2 * P1 and SUM = P1 + P2.
- */
-static void montgomery_ladder(MPI_POINT prd, MPI_POINT sum,
- MPI_POINT p1, MPI_POINT p2, MPI dif_x,
- struct mpi_ec_ctx *ctx)
-{
- ctx->addm(sum->x, p2->x, p2->z, ctx);
- ctx->subm(p2->z, p2->x, p2->z, ctx);
- ctx->addm(prd->x, p1->x, p1->z, ctx);
- ctx->subm(p1->z, p1->x, p1->z, ctx);
- ctx->mulm(p2->x, p1->z, sum->x, ctx);
- ctx->mulm(p2->z, prd->x, p2->z, ctx);
- ctx->pow2(p1->x, prd->x, ctx);
- ctx->pow2(p1->z, p1->z, ctx);
- ctx->addm(sum->x, p2->x, p2->z, ctx);
- ctx->subm(p2->z, p2->x, p2->z, ctx);
- ctx->mulm(prd->x, p1->x, p1->z, ctx);
- ctx->subm(p1->z, p1->x, p1->z, ctx);
- ctx->pow2(sum->x, sum->x, ctx);
- ctx->pow2(sum->z, p2->z, ctx);
- ctx->mulm(prd->z, p1->z, ctx->a, ctx); /* CTX->A: (a-2)/4 */
- ctx->mulm(sum->z, sum->z, dif_x, ctx);
- ctx->addm(prd->z, p1->x, prd->z, ctx);
- ctx->mulm(prd->z, prd->z, p1->z, ctx);
-}
-
-/* RESULT = P1 + P2 */
-void mpi_ec_add_points(MPI_POINT result,
- MPI_POINT p1, MPI_POINT p2,
- struct mpi_ec_ctx *ctx)
-{
- switch (ctx->model) {
- case MPI_EC_WEIERSTRASS:
- add_points_weierstrass(result, p1, p2, ctx);
- break;
- case MPI_EC_MONTGOMERY:
- add_points_montgomery(result, p1, p2, ctx);
- break;
- case MPI_EC_EDWARDS:
- add_points_edwards(result, p1, p2, ctx);
- break;
- }
-}
-EXPORT_SYMBOL_GPL(mpi_ec_add_points);
-
-/* Scalar point multiplication - the main function for ECC. If takes
- * an integer SCALAR and a POINT as well as the usual context CTX.
- * RESULT will be set to the resulting point.
- */
-void mpi_ec_mul_point(MPI_POINT result,
- MPI scalar, MPI_POINT point,
- struct mpi_ec_ctx *ctx)
-{
- MPI x1, y1, z1, k, h, yy;
- unsigned int i, loops;
- struct gcry_mpi_point p1, p2, p1inv;
-
- if (ctx->model == MPI_EC_EDWARDS) {
- /* Simple left to right binary method. Algorithm 3.27 from
- * {author={Hankerson, Darrel and Menezes, Alfred J. and Vanstone, Scott},
- * title = {Guide to Elliptic Curve Cryptography},
- * year = {2003}, isbn = {038795273X},
- * url = {http://www.cacr.math.uwaterloo.ca/ecc/},
- * publisher = {Springer-Verlag New York, Inc.}}
- */
- unsigned int nbits;
- int j;
-
- if (mpi_cmp(scalar, ctx->p) >= 0)
- nbits = mpi_get_nbits(scalar);
- else
- nbits = mpi_get_nbits(ctx->p);
-
- mpi_set_ui(result->x, 0);
- mpi_set_ui(result->y, 1);
- mpi_set_ui(result->z, 1);
- point_resize(point, ctx);
-
- point_resize(result, ctx);
- point_resize(point, ctx);
-
- for (j = nbits-1; j >= 0; j--) {
- mpi_ec_dup_point(result, result, ctx);
- if (mpi_test_bit(scalar, j))
- mpi_ec_add_points(result, result, point, ctx);
- }
- return;
- } else if (ctx->model == MPI_EC_MONTGOMERY) {
- unsigned int nbits;
- int j;
- struct gcry_mpi_point p1_, p2_;
- MPI_POINT q1, q2, prd, sum;
- unsigned long sw;
- mpi_size_t rsize;
-
- /* Compute scalar point multiplication with Montgomery Ladder.
- * Note that we don't use Y-coordinate in the points at all.
- * RESULT->Y will be filled by zero.
- */
-
- nbits = mpi_get_nbits(scalar);
- point_init(&p1);
- point_init(&p2);
- point_init(&p1_);
- point_init(&p2_);
- mpi_set_ui(p1.x, 1);
- mpi_free(p2.x);
- p2.x = mpi_copy(point->x);
- mpi_set_ui(p2.z, 1);
-
- point_resize(&p1, ctx);
- point_resize(&p2, ctx);
- point_resize(&p1_, ctx);
- point_resize(&p2_, ctx);
-
- mpi_resize(point->x, ctx->p->nlimbs);
- point->x->nlimbs = ctx->p->nlimbs;
-
- q1 = &p1;
- q2 = &p2;
- prd = &p1_;
- sum = &p2_;
-
- for (j = nbits-1; j >= 0; j--) {
- MPI_POINT t;
-
- sw = mpi_test_bit(scalar, j);
- point_swap_cond(q1, q2, sw, ctx);
- montgomery_ladder(prd, sum, q1, q2, point->x, ctx);
- point_swap_cond(prd, sum, sw, ctx);
- t = q1; q1 = prd; prd = t;
- t = q2; q2 = sum; sum = t;
- }
-
- mpi_clear(result->y);
- sw = (nbits & 1);
- point_swap_cond(&p1, &p1_, sw, ctx);
-
- rsize = p1.z->nlimbs;
- MPN_NORMALIZE(p1.z->d, rsize);
- if (rsize == 0) {
- mpi_set_ui(result->x, 1);
- mpi_set_ui(result->z, 0);
- } else {
- z1 = mpi_new(0);
- ec_invm(z1, p1.z, ctx);
- ec_mulm(result->x, p1.x, z1, ctx);
- mpi_set_ui(result->z, 1);
- mpi_free(z1);
- }
-
- point_free(&p1);
- point_free(&p2);
- point_free(&p1_);
- point_free(&p2_);
- return;
- }
-
- x1 = mpi_alloc_like(ctx->p);
- y1 = mpi_alloc_like(ctx->p);
- h = mpi_alloc_like(ctx->p);
- k = mpi_copy(scalar);
- yy = mpi_copy(point->y);
-
- if (mpi_has_sign(k)) {
- k->sign = 0;
- ec_invm(yy, yy, ctx);
- }
-
- if (!mpi_cmp_ui(point->z, 1)) {
- mpi_set(x1, point->x);
- mpi_set(y1, yy);
- } else {
- MPI z2, z3;
-
- z2 = mpi_alloc_like(ctx->p);
- z3 = mpi_alloc_like(ctx->p);
- ec_mulm(z2, point->z, point->z, ctx);
- ec_mulm(z3, point->z, z2, ctx);
- ec_invm(z2, z2, ctx);
- ec_mulm(x1, point->x, z2, ctx);
- ec_invm(z3, z3, ctx);
- ec_mulm(y1, yy, z3, ctx);
- mpi_free(z2);
- mpi_free(z3);
- }
- z1 = mpi_copy(mpi_const(MPI_C_ONE));
-
- mpi_mul(h, k, mpi_const(MPI_C_THREE)); /* h = 3k */
- loops = mpi_get_nbits(h);
- if (loops < 2) {
- /* If SCALAR is zero, the above mpi_mul sets H to zero and thus
- * LOOPs will be zero. To avoid an underflow of I in the main
- * loop we set LOOP to 2 and the result to (0,0,0).
- */
- loops = 2;
- mpi_clear(result->x);
- mpi_clear(result->y);
- mpi_clear(result->z);
- } else {
- mpi_set(result->x, point->x);
- mpi_set(result->y, yy);
- mpi_set(result->z, point->z);
- }
- mpi_free(yy); yy = NULL;
-
- p1.x = x1; x1 = NULL;
- p1.y = y1; y1 = NULL;
- p1.z = z1; z1 = NULL;
- point_init(&p2);
- point_init(&p1inv);
-
- /* Invert point: y = p - y mod p */
- point_set(&p1inv, &p1);
- ec_subm(p1inv.y, ctx->p, p1inv.y, ctx);
-
- for (i = loops-2; i > 0; i--) {
- mpi_ec_dup_point(result, result, ctx);
- if (mpi_test_bit(h, i) == 1 && mpi_test_bit(k, i) == 0) {
- point_set(&p2, result);
- mpi_ec_add_points(result, &p2, &p1, ctx);
- }
- if (mpi_test_bit(h, i) == 0 && mpi_test_bit(k, i) == 1) {
- point_set(&p2, result);
- mpi_ec_add_points(result, &p2, &p1inv, ctx);
- }
- }
-
- point_free(&p1);
- point_free(&p2);
- point_free(&p1inv);
- mpi_free(h);
- mpi_free(k);
-}
-EXPORT_SYMBOL_GPL(mpi_ec_mul_point);
-
-/* Return true if POINT is on the curve described by CTX. */
-int mpi_ec_curve_point(MPI_POINT point, struct mpi_ec_ctx *ctx)
-{
- int res = 0;
- MPI x, y, w;
-
- x = mpi_new(0);
- y = mpi_new(0);
- w = mpi_new(0);
-
- /* Check that the point is in range. This needs to be done here and
- * not after conversion to affine coordinates.
- */
- if (mpi_cmpabs(point->x, ctx->p) >= 0)
- goto leave;
- if (mpi_cmpabs(point->y, ctx->p) >= 0)
- goto leave;
- if (mpi_cmpabs(point->z, ctx->p) >= 0)
- goto leave;
-
- switch (ctx->model) {
- case MPI_EC_WEIERSTRASS:
- {
- MPI xxx;
-
- if (mpi_ec_get_affine(x, y, point, ctx))
- goto leave;
-
- xxx = mpi_new(0);
-
- /* y^2 == x^3 + a·x + b */
- ec_pow2(y, y, ctx);
-
- ec_pow3(xxx, x, ctx);
- ec_mulm(w, ctx->a, x, ctx);
- ec_addm(w, w, ctx->b, ctx);
- ec_addm(w, w, xxx, ctx);
-
- if (!mpi_cmp(y, w))
- res = 1;
-
- mpi_free(xxx);
- }
- break;
-
- case MPI_EC_MONTGOMERY:
- {
-#define xx y
- /* With Montgomery curve, only X-coordinate is valid. */
- if (mpi_ec_get_affine(x, NULL, point, ctx))
- goto leave;
-
- /* The equation is: b * y^2 == x^3 + a · x^2 + x */
- /* We check if right hand is quadratic residue or not by
- * Euler's criterion.
- */
- /* CTX->A has (a-2)/4 and CTX->B has b^-1 */
- ec_mulm(w, ctx->a, mpi_const(MPI_C_FOUR), ctx);
- ec_addm(w, w, mpi_const(MPI_C_TWO), ctx);
- ec_mulm(w, w, x, ctx);
- ec_pow2(xx, x, ctx);
- ec_addm(w, w, xx, ctx);
- ec_addm(w, w, mpi_const(MPI_C_ONE), ctx);
- ec_mulm(w, w, x, ctx);
- ec_mulm(w, w, ctx->b, ctx);
-#undef xx
- /* Compute Euler's criterion: w^(p-1)/2 */
-#define p_minus1 y
- ec_subm(p_minus1, ctx->p, mpi_const(MPI_C_ONE), ctx);
- mpi_rshift(p_minus1, p_minus1, 1);
- ec_powm(w, w, p_minus1, ctx);
-
- res = !mpi_cmp_ui(w, 1);
-#undef p_minus1
- }
- break;
-
- case MPI_EC_EDWARDS:
- {
- if (mpi_ec_get_affine(x, y, point, ctx))
- goto leave;
-
- mpi_resize(w, ctx->p->nlimbs);
- w->nlimbs = ctx->p->nlimbs;
-
- /* a · x^2 + y^2 - 1 - b · x^2 · y^2 == 0 */
- ctx->pow2(x, x, ctx);
- ctx->pow2(y, y, ctx);
- if (ctx->dialect == ECC_DIALECT_ED25519)
- ctx->subm(w, ctx->p, x, ctx);
- else
- ctx->mulm(w, ctx->a, x, ctx);
- ctx->addm(w, w, y, ctx);
- ctx->mulm(x, x, y, ctx);
- ctx->mulm(x, x, ctx->b, ctx);
- ctx->subm(w, w, x, ctx);
- if (!mpi_cmp_ui(w, 1))
- res = 1;
- }
- break;
- }
-
-leave:
- mpi_free(w);
- mpi_free(x);
- mpi_free(y);
-
- return res;
-}
-EXPORT_SYMBOL_GPL(mpi_ec_curve_point);
diff --git a/lib/crypto/mpi/mpi-add.c b/lib/crypto/mpi/mpi-add.c
index 9056fc5167fc..3015140d4860 100644
--- a/lib/crypto/mpi/mpi-add.c
+++ b/lib/crypto/mpi/mpi-add.c
@@ -13,62 +13,12 @@
#include "mpi-internal.h"
-/****************
- * Add the unsigned integer V to the mpi-integer U and store the
- * result in W. U and V may be the same.
- */
-void mpi_add_ui(MPI w, MPI u, unsigned long v)
-{
- mpi_ptr_t wp, up;
- mpi_size_t usize, wsize;
- int usign, wsign;
-
- usize = u->nlimbs;
- usign = u->sign;
- wsign = 0;
-
- /* If not space for W (and possible carry), increase space. */
- wsize = usize + 1;
- if (w->alloced < wsize)
- mpi_resize(w, wsize);
-
- /* These must be after realloc (U may be the same as W). */
- up = u->d;
- wp = w->d;
-
- if (!usize) { /* simple */
- wp[0] = v;
- wsize = v ? 1:0;
- } else if (!usign) { /* mpi is not negative */
- mpi_limb_t cy;
- cy = mpihelp_add_1(wp, up, usize, v);
- wp[usize] = cy;
- wsize = usize + cy;
- } else {
- /* The signs are different. Need exact comparison to determine
- * which operand to subtract from which.
- */
- if (usize == 1 && up[0] < v) {
- wp[0] = v - up[0];
- wsize = 1;
- } else {
- mpihelp_sub_1(wp, up, usize, v);
- /* Size can decrease with at most one limb. */
- wsize = usize - (wp[usize-1] == 0);
- wsign = 1;
- }
- }
-
- w->nlimbs = wsize;
- w->sign = wsign;
-}
-
-
-void mpi_add(MPI w, MPI u, MPI v)
+int mpi_add(MPI w, MPI u, MPI v)
{
mpi_ptr_t wp, up, vp;
mpi_size_t usize, vsize, wsize;
int usign, vsign, wsign;
+ int err;
if (u->nlimbs < v->nlimbs) { /* Swap U and V. */
usize = v->nlimbs;
@@ -76,7 +26,9 @@ void mpi_add(MPI w, MPI u, MPI v)
vsize = u->nlimbs;
vsign = u->sign;
wsize = usize + 1;
- RESIZE_IF_NEEDED(w, wsize);
+ err = RESIZE_IF_NEEDED(w, wsize);
+ if (err)
+ return err;
/* These must be after realloc (u or v may be the same as w). */
up = v->d;
vp = u->d;
@@ -86,7 +38,9 @@ void mpi_add(MPI w, MPI u, MPI v)
vsize = v->nlimbs;
vsign = v->sign;
wsize = usize + 1;
- RESIZE_IF_NEEDED(w, wsize);
+ err = RESIZE_IF_NEEDED(w, wsize);
+ if (err)
+ return err;
/* These must be after realloc (u or v may be the same as w). */
up = u->d;
vp = v->d;
@@ -128,28 +82,37 @@ void mpi_add(MPI w, MPI u, MPI v)
w->nlimbs = wsize;
w->sign = wsign;
+ return 0;
}
EXPORT_SYMBOL_GPL(mpi_add);
-void mpi_sub(MPI w, MPI u, MPI v)
+int mpi_sub(MPI w, MPI u, MPI v)
{
- MPI vv = mpi_copy(v);
+ int err;
+ MPI vv;
+
+ vv = mpi_copy(v);
+ if (!vv)
+ return -ENOMEM;
+
vv->sign = !vv->sign;
- mpi_add(w, u, vv);
+ err = mpi_add(w, u, vv);
mpi_free(vv);
+
+ return err;
}
EXPORT_SYMBOL_GPL(mpi_sub);
-void mpi_addm(MPI w, MPI u, MPI v, MPI m)
+int mpi_addm(MPI w, MPI u, MPI v, MPI m)
{
- mpi_add(w, u, v);
- mpi_mod(w, w, m);
+ return mpi_add(w, u, v) ?:
+ mpi_mod(w, w, m);
}
EXPORT_SYMBOL_GPL(mpi_addm);
-void mpi_subm(MPI w, MPI u, MPI v, MPI m)
+int mpi_subm(MPI w, MPI u, MPI v, MPI m)
{
- mpi_sub(w, u, v);
- mpi_mod(w, w, m);
+ return mpi_sub(w, u, v) ?:
+ mpi_mod(w, w, m);
}
EXPORT_SYMBOL_GPL(mpi_subm);
diff --git a/lib/crypto/mpi/mpi-bit.c b/lib/crypto/mpi/mpi-bit.c
index 070ba784c9f1..835a2f0622a0 100644
--- a/lib/crypto/mpi/mpi-bit.c
+++ b/lib/crypto/mpi/mpi-bit.c
@@ -32,7 +32,6 @@ void mpi_normalize(MPI a)
for (; a->nlimbs && !a->d[a->nlimbs - 1]; a->nlimbs--)
;
}
-EXPORT_SYMBOL_GPL(mpi_normalize);
/****************
* Return the number of bits in A.
@@ -77,9 +76,10 @@ EXPORT_SYMBOL_GPL(mpi_test_bit);
/****************
* Set bit N of A.
*/
-void mpi_set_bit(MPI a, unsigned int n)
+int mpi_set_bit(MPI a, unsigned int n)
{
unsigned int i, limbno, bitno;
+ int err;
limbno = n / BITS_PER_MPI_LIMB;
bitno = n % BITS_PER_MPI_LIMB;
@@ -87,106 +87,31 @@ void mpi_set_bit(MPI a, unsigned int n)
if (limbno >= a->nlimbs) {
for (i = a->nlimbs; i < a->alloced; i++)
a->d[i] = 0;
- mpi_resize(a, limbno+1);
+ err = mpi_resize(a, limbno+1);
+ if (err)
+ return err;
a->nlimbs = limbno+1;
}
a->d[limbno] |= (A_LIMB_1<<bitno);
-}
-
-/****************
- * Set bit N of A. and clear all bits above
- */
-void mpi_set_highbit(MPI a, unsigned int n)
-{
- unsigned int i, limbno, bitno;
-
- limbno = n / BITS_PER_MPI_LIMB;
- bitno = n % BITS_PER_MPI_LIMB;
-
- if (limbno >= a->nlimbs) {
- for (i = a->nlimbs; i < a->alloced; i++)
- a->d[i] = 0;
- mpi_resize(a, limbno+1);
- a->nlimbs = limbno+1;
- }
- a->d[limbno] |= (A_LIMB_1<<bitno);
- for (bitno++; bitno < BITS_PER_MPI_LIMB; bitno++)
- a->d[limbno] &= ~(A_LIMB_1 << bitno);
- a->nlimbs = limbno+1;
-}
-EXPORT_SYMBOL_GPL(mpi_set_highbit);
-
-/****************
- * clear bit N of A and all bits above
- */
-void mpi_clear_highbit(MPI a, unsigned int n)
-{
- unsigned int limbno, bitno;
-
- limbno = n / BITS_PER_MPI_LIMB;
- bitno = n % BITS_PER_MPI_LIMB;
-
- if (limbno >= a->nlimbs)
- return; /* not allocated, therefore no need to clear bits :-) */
-
- for ( ; bitno < BITS_PER_MPI_LIMB; bitno++)
- a->d[limbno] &= ~(A_LIMB_1 << bitno);
- a->nlimbs = limbno+1;
-}
-
-/****************
- * Clear bit N of A.
- */
-void mpi_clear_bit(MPI a, unsigned int n)
-{
- unsigned int limbno, bitno;
-
- limbno = n / BITS_PER_MPI_LIMB;
- bitno = n % BITS_PER_MPI_LIMB;
-
- if (limbno >= a->nlimbs)
- return; /* Don't need to clear this bit, it's far too left. */
- a->d[limbno] &= ~(A_LIMB_1 << bitno);
-}
-EXPORT_SYMBOL_GPL(mpi_clear_bit);
-
-
-/****************
- * Shift A by COUNT limbs to the right
- * This is used only within the MPI library
- */
-void mpi_rshift_limbs(MPI a, unsigned int count)
-{
- mpi_ptr_t ap = a->d;
- mpi_size_t n = a->nlimbs;
- unsigned int i;
-
- if (count >= n) {
- a->nlimbs = 0;
- return;
- }
-
- for (i = 0; i < n - count; i++)
- ap[i] = ap[i+count];
- ap[i] = 0;
- a->nlimbs -= count;
+ return 0;
}
/*
* Shift A by N bits to the right.
*/
-void mpi_rshift(MPI x, MPI a, unsigned int n)
+int mpi_rshift(MPI x, MPI a, unsigned int n)
{
mpi_size_t xsize;
unsigned int i;
unsigned int nlimbs = (n/BITS_PER_MPI_LIMB);
unsigned int nbits = (n%BITS_PER_MPI_LIMB);
+ int err;
if (x == a) {
/* In-place operation. */
if (nlimbs >= x->nlimbs) {
x->nlimbs = 0;
- return;
+ return 0;
}
if (nlimbs) {
@@ -201,7 +126,9 @@ void mpi_rshift(MPI x, MPI a, unsigned int n)
/* Copy and shift by more or equal bits than in a limb. */
xsize = a->nlimbs;
x->sign = a->sign;
- RESIZE_IF_NEEDED(x, xsize);
+ err = RESIZE_IF_NEEDED(x, xsize);
+ if (err)
+ return err;
x->nlimbs = xsize;
for (i = 0; i < a->nlimbs; i++)
x->d[i] = a->d[i];
@@ -209,15 +136,13 @@ void mpi_rshift(MPI x, MPI a, unsigned int n)
if (nlimbs >= x->nlimbs) {
x->nlimbs = 0;
- return;
+ return 0;
}
- if (nlimbs) {
- for (i = 0; i < x->nlimbs - nlimbs; i++)
- x->d[i] = x->d[i+nlimbs];
- x->d[i] = 0;
- x->nlimbs -= nlimbs;
- }
+ for (i = 0; i < x->nlimbs - nlimbs; i++)
+ x->d[i] = x->d[i+nlimbs];
+ x->d[i] = 0;
+ x->nlimbs -= nlimbs;
if (x->nlimbs && nbits)
mpihelp_rshift(x->d, x->d, x->nlimbs, nbits);
@@ -225,7 +150,9 @@ void mpi_rshift(MPI x, MPI a, unsigned int n)
/* Copy and shift by less than bits in a limb. */
xsize = a->nlimbs;
x->sign = a->sign;
- RESIZE_IF_NEEDED(x, xsize);
+ err = RESIZE_IF_NEEDED(x, xsize);
+ if (err)
+ return err;
x->nlimbs = xsize;
if (xsize) {
@@ -241,68 +168,7 @@ void mpi_rshift(MPI x, MPI a, unsigned int n)
}
}
MPN_NORMALIZE(x->d, x->nlimbs);
-}
-EXPORT_SYMBOL_GPL(mpi_rshift);
-/****************
- * Shift A by COUNT limbs to the left
- * This is used only within the MPI library
- */
-void mpi_lshift_limbs(MPI a, unsigned int count)
-{
- mpi_ptr_t ap;
- int n = a->nlimbs;
- int i;
-
- if (!count || !n)
- return;
-
- RESIZE_IF_NEEDED(a, n+count);
-
- ap = a->d;
- for (i = n-1; i >= 0; i--)
- ap[i+count] = ap[i];
- for (i = 0; i < count; i++)
- ap[i] = 0;
- a->nlimbs += count;
-}
-
-/*
- * Shift A by N bits to the left.
- */
-void mpi_lshift(MPI x, MPI a, unsigned int n)
-{
- unsigned int nlimbs = (n/BITS_PER_MPI_LIMB);
- unsigned int nbits = (n%BITS_PER_MPI_LIMB);
-
- if (x == a && !n)
- return; /* In-place shift with an amount of zero. */
-
- if (x != a) {
- /* Copy A to X. */
- unsigned int alimbs = a->nlimbs;
- int asign = a->sign;
- mpi_ptr_t xp, ap;
-
- RESIZE_IF_NEEDED(x, alimbs+nlimbs+1);
- xp = x->d;
- ap = a->d;
- MPN_COPY(xp, ap, alimbs);
- x->nlimbs = alimbs;
- x->flags = a->flags;
- x->sign = asign;
- }
-
- if (nlimbs && !nbits) {
- /* Shift a full number of limbs. */
- mpi_lshift_limbs(x, nlimbs);
- } else if (n) {
- /* We use a very dump approach: Shift left by the number of
- * limbs plus one and than fix it up by an rshift.
- */
- mpi_lshift_limbs(x, nlimbs+1);
- mpi_rshift(x, x, BITS_PER_MPI_LIMB - nbits);
- }
-
- MPN_NORMALIZE(x->d, x->nlimbs);
+ return 0;
}
+EXPORT_SYMBOL_GPL(mpi_rshift);
diff --git a/lib/crypto/mpi/mpi-cmp.c b/lib/crypto/mpi/mpi-cmp.c
index 0835b6213235..ceaebe181cd7 100644
--- a/lib/crypto/mpi/mpi-cmp.c
+++ b/lib/crypto/mpi/mpi-cmp.c
@@ -45,54 +45,28 @@ int mpi_cmp_ui(MPI u, unsigned long v)
}
EXPORT_SYMBOL_GPL(mpi_cmp_ui);
-static int do_mpi_cmp(MPI u, MPI v, int absmode)
+int mpi_cmp(MPI u, MPI v)
{
- mpi_size_t usize;
- mpi_size_t vsize;
- int usign;
- int vsign;
+ mpi_size_t usize, vsize;
int cmp;
mpi_normalize(u);
mpi_normalize(v);
-
usize = u->nlimbs;
vsize = v->nlimbs;
- usign = absmode ? 0 : u->sign;
- vsign = absmode ? 0 : v->sign;
-
- /* Compare sign bits. */
-
- if (!usign && vsign)
+ if (!u->sign && v->sign)
return 1;
- if (usign && !vsign)
+ if (u->sign && !v->sign)
return -1;
-
- /* U and V are either both positive or both negative. */
-
- if (usize != vsize && !usign && !vsign)
+ if (usize != vsize && !u->sign && !v->sign)
return usize - vsize;
- if (usize != vsize && usign && vsign)
- return vsize + usize;
+ if (usize != vsize && u->sign && v->sign)
+ return vsize - usize;
if (!usize)
return 0;
cmp = mpihelp_cmp(u->d, v->d, usize);
- if (!cmp)
- return 0;
- if ((cmp < 0?1:0) == (usign?1:0))
- return 1;
-
- return -1;
-}
-
-int mpi_cmp(MPI u, MPI v)
-{
- return do_mpi_cmp(u, v, 0);
+ if (u->sign)
+ return -cmp;
+ return cmp;
}
EXPORT_SYMBOL_GPL(mpi_cmp);
-
-int mpi_cmpabs(MPI u, MPI v)
-{
- return do_mpi_cmp(u, v, 1);
-}
-EXPORT_SYMBOL_GPL(mpi_cmpabs);
diff --git a/lib/crypto/mpi/mpi-div.c b/lib/crypto/mpi/mpi-div.c
index 45beab8b9e9e..6e5044e72595 100644
--- a/lib/crypto/mpi/mpi-div.c
+++ b/lib/crypto/mpi/mpi-div.c
@@ -14,13 +14,13 @@
#include "mpi-internal.h"
#include "longlong.h"
-void mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den);
-void mpi_fdiv_qr(MPI quot, MPI rem, MPI dividend, MPI divisor);
+int mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den);
-void mpi_fdiv_r(MPI rem, MPI dividend, MPI divisor)
+int mpi_fdiv_r(MPI rem, MPI dividend, MPI divisor)
{
int divisor_sign = divisor->sign;
MPI temp_divisor = NULL;
+ int err;
/* We need the original value of the divisor after the remainder has been
* preliminary calculated. We have to copy it to temporary space if it's
@@ -28,44 +28,22 @@ void mpi_fdiv_r(MPI rem, MPI dividend, MPI divisor)
*/
if (rem == divisor) {
temp_divisor = mpi_copy(divisor);
+ if (!temp_divisor)
+ return -ENOMEM;
divisor = temp_divisor;
}
- mpi_tdiv_r(rem, dividend, divisor);
+ err = mpi_tdiv_r(rem, dividend, divisor);
+ if (err)
+ goto free_temp_divisor;
if (((divisor_sign?1:0) ^ (dividend->sign?1:0)) && rem->nlimbs)
- mpi_add(rem, rem, divisor);
+ err = mpi_add(rem, rem, divisor);
- if (temp_divisor)
- mpi_free(temp_divisor);
-}
+free_temp_divisor:
+ mpi_free(temp_divisor);
-void mpi_fdiv_q(MPI quot, MPI dividend, MPI divisor)
-{
- MPI tmp = mpi_alloc(mpi_get_nlimbs(quot));
- mpi_fdiv_qr(quot, tmp, dividend, divisor);
- mpi_free(tmp);
-}
-
-void mpi_fdiv_qr(MPI quot, MPI rem, MPI dividend, MPI divisor)
-{
- int divisor_sign = divisor->sign;
- MPI temp_divisor = NULL;
-
- if (quot == divisor || rem == divisor) {
- temp_divisor = mpi_copy(divisor);
- divisor = temp_divisor;
- }
-
- mpi_tdiv_qr(quot, rem, dividend, divisor);
-
- if ((divisor_sign ^ dividend->sign) && rem->nlimbs) {
- mpi_sub_ui(quot, quot, 1);
- mpi_add(rem, rem, divisor);
- }
-
- if (temp_divisor)
- mpi_free(temp_divisor);
+ return err;
}
/* If den == quot, den needs temporary storage.
@@ -75,12 +53,12 @@ void mpi_fdiv_qr(MPI quot, MPI rem, MPI dividend, MPI divisor)
* i.e no extra storage should be allocated.
*/
-void mpi_tdiv_r(MPI rem, MPI num, MPI den)
+int mpi_tdiv_r(MPI rem, MPI num, MPI den)
{
- mpi_tdiv_qr(NULL, rem, num, den);
+ return mpi_tdiv_qr(NULL, rem, num, den);
}
-void mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den)
+int mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den)
{
mpi_ptr_t np, dp;
mpi_ptr_t qp, rp;
@@ -93,13 +71,16 @@ void mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den)
mpi_limb_t q_limb;
mpi_ptr_t marker[5];
int markidx = 0;
+ int err;
/* Ensure space is enough for quotient and remainder.
* We need space for an extra limb in the remainder, because it's
* up-shifted (normalized) below.
*/
rsize = nsize + 1;
- mpi_resize(rem, rsize);
+ err = mpi_resize(rem, rsize);
+ if (err)
+ return err;
qsize = rsize - dsize; /* qsize cannot be bigger than this. */
if (qsize <= 0) {
@@ -115,11 +96,14 @@ void mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den)
quot->nlimbs = 0;
quot->sign = 0;
}
- return;
+ return 0;
}
- if (quot)
- mpi_resize(quot, qsize);
+ if (quot) {
+ err = mpi_resize(quot, qsize);
+ if (err)
+ return err;
+ }
/* Read pointers here, when reallocation is finished. */
np = num->d;
@@ -141,10 +125,10 @@ void mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den)
rsize = rlimb != 0?1:0;
rem->nlimbs = rsize;
rem->sign = sign_remainder;
- return;
+ return 0;
}
-
+ err = -ENOMEM;
if (quot) {
qp = quot->d;
/* Make sure QP and NP point to different objects. Otherwise the
@@ -152,6 +136,8 @@ void mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den)
*/
if (qp == np) { /* Copy NP object to temporary space. */
np = marker[markidx++] = mpi_alloc_limb_space(nsize);
+ if (!np)
+ goto out_free_marker;
MPN_COPY(np, qp, nsize);
}
} else /* Put quotient at top of remainder. */
@@ -172,6 +158,8 @@ void mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den)
* the original contents of the denominator.
*/
tp = marker[markidx++] = mpi_alloc_limb_space(dsize);
+ if (!tp)
+ goto out_free_marker;
mpihelp_lshift(tp, dp, dsize, normalization_steps);
dp = tp;
@@ -193,6 +181,8 @@ void mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den)
mpi_ptr_t tp;
tp = marker[markidx++] = mpi_alloc_limb_space(dsize);
+ if (!tp)
+ goto out_free_marker;
MPN_COPY(tp, dp, dsize);
dp = tp;
}
@@ -227,8 +217,14 @@ void mpi_tdiv_qr(MPI quot, MPI rem, MPI num, MPI den)
rem->nlimbs = rsize;
rem->sign = sign_remainder;
+
+ err = 0;
+
+out_free_marker:
while (markidx) {
markidx--;
mpi_free_limb_space(marker[markidx]);
}
+
+ return err;
}
diff --git a/lib/crypto/mpi/mpi-internal.h b/lib/crypto/mpi/mpi-internal.h
index 554002182db1..8a4f49e3043c 100644
--- a/lib/crypto/mpi/mpi-internal.h
+++ b/lib/crypto/mpi/mpi-internal.h
@@ -52,11 +52,12 @@
typedef mpi_limb_t *mpi_ptr_t; /* pointer to a limb */
typedef int mpi_size_t; /* (must be a signed type) */
-#define RESIZE_IF_NEEDED(a, b) \
- do { \
- if ((a)->alloced < (b)) \
- mpi_resize((a), (b)); \
- } while (0)
+static inline int RESIZE_IF_NEEDED(MPI a, unsigned b)
+{
+ if (a->alloced < b)
+ return mpi_resize(a, b);
+ return 0;
+}
/* Copy N limbs from S to D. */
#define MPN_COPY(d, s, n) \
@@ -66,14 +67,6 @@ typedef int mpi_size_t; /* (must be a signed type) */
(d)[_i] = (s)[_i]; \
} while (0)
-#define MPN_COPY_INCR(d, s, n) \
- do { \
- mpi_size_t _i; \
- for (_i = 0; _i < (n); _i++) \
- (d)[_i] = (s)[_i]; \
- } while (0)
-
-
#define MPN_COPY_DECR(d, s, n) \
do { \
mpi_size_t _i; \
@@ -181,8 +174,6 @@ int mpihelp_mul(mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t usize,
void mpih_sqr_n_basecase(mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size);
void mpih_sqr_n(mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size,
mpi_ptr_t tspace);
-void mpihelp_mul_n(mpi_ptr_t prodp,
- mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t size);
int mpihelp_mul_karatsuba_case(mpi_ptr_t prodp,
mpi_ptr_t up, mpi_size_t usize,
diff --git a/lib/crypto/mpi/mpi-inv.c b/lib/crypto/mpi/mpi-inv.c
deleted file mode 100644
index 61e37d18f793..000000000000
--- a/lib/crypto/mpi/mpi-inv.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/* mpi-inv.c - MPI functions
- * Copyright (C) 1998, 2001, 2002, 2003 Free Software Foundation, Inc.
- *
- * This file is part of Libgcrypt.
- *
- * Libgcrypt is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * Libgcrypt is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "mpi-internal.h"
-
-/****************
- * Calculate the multiplicative inverse X of A mod N
- * That is: Find the solution x for
- * 1 = (a*x) mod n
- */
-int mpi_invm(MPI x, MPI a, MPI n)
-{
- /* Extended Euclid's algorithm (See TAOCP Vol II, 4.5.2, Alg X)
- * modified according to Michael Penk's solution for Exercise 35
- * with further enhancement
- */
- MPI u, v, u1, u2 = NULL, u3, v1, v2 = NULL, v3, t1, t2 = NULL, t3;
- unsigned int k;
- int sign;
- int odd;
-
- if (!mpi_cmp_ui(a, 0))
- return 0; /* Inverse does not exists. */
- if (!mpi_cmp_ui(n, 1))
- return 0; /* Inverse does not exists. */
-
- u = mpi_copy(a);
- v = mpi_copy(n);
-
- for (k = 0; !mpi_test_bit(u, 0) && !mpi_test_bit(v, 0); k++) {
- mpi_rshift(u, u, 1);
- mpi_rshift(v, v, 1);
- }
- odd = mpi_test_bit(v, 0);
-
- u1 = mpi_alloc_set_ui(1);
- if (!odd)
- u2 = mpi_alloc_set_ui(0);
- u3 = mpi_copy(u);
- v1 = mpi_copy(v);
- if (!odd) {
- v2 = mpi_alloc(mpi_get_nlimbs(u));
- mpi_sub(v2, u1, u); /* U is used as const 1 */
- }
- v3 = mpi_copy(v);
- if (mpi_test_bit(u, 0)) { /* u is odd */
- t1 = mpi_alloc_set_ui(0);
- if (!odd) {
- t2 = mpi_alloc_set_ui(1);
- t2->sign = 1;
- }
- t3 = mpi_copy(v);
- t3->sign = !t3->sign;
- goto Y4;
- } else {
- t1 = mpi_alloc_set_ui(1);
- if (!odd)
- t2 = mpi_alloc_set_ui(0);
- t3 = mpi_copy(u);
- }
-
- do {
- do {
- if (!odd) {
- if (mpi_test_bit(t1, 0) || mpi_test_bit(t2, 0)) {
- /* one is odd */
- mpi_add(t1, t1, v);
- mpi_sub(t2, t2, u);
- }
- mpi_rshift(t1, t1, 1);
- mpi_rshift(t2, t2, 1);
- mpi_rshift(t3, t3, 1);
- } else {
- if (mpi_test_bit(t1, 0))
- mpi_add(t1, t1, v);
- mpi_rshift(t1, t1, 1);
- mpi_rshift(t3, t3, 1);
- }
-Y4:
- ;
- } while (!mpi_test_bit(t3, 0)); /* while t3 is even */
-
- if (!t3->sign) {
- mpi_set(u1, t1);
- if (!odd)
- mpi_set(u2, t2);
- mpi_set(u3, t3);
- } else {
- mpi_sub(v1, v, t1);
- sign = u->sign; u->sign = !u->sign;
- if (!odd)
- mpi_sub(v2, u, t2);
- u->sign = sign;
- sign = t3->sign; t3->sign = !t3->sign;
- mpi_set(v3, t3);
- t3->sign = sign;
- }
- mpi_sub(t1, u1, v1);
- if (!odd)
- mpi_sub(t2, u2, v2);
- mpi_sub(t3, u3, v3);
- if (t1->sign) {
- mpi_add(t1, t1, v);
- if (!odd)
- mpi_sub(t2, t2, u);
- }
- } while (mpi_cmp_ui(t3, 0)); /* while t3 != 0 */
- /* mpi_lshift( u3, k ); */
- mpi_set(x, u1);
-
- mpi_free(u1);
- mpi_free(v1);
- mpi_free(t1);
- if (!odd) {
- mpi_free(u2);
- mpi_free(v2);
- mpi_free(t2);
- }
- mpi_free(u3);
- mpi_free(v3);
- mpi_free(t3);
-
- mpi_free(u);
- mpi_free(v);
- return 1;
-}
-EXPORT_SYMBOL_GPL(mpi_invm);
diff --git a/lib/crypto/mpi/mpi-mod.c b/lib/crypto/mpi/mpi-mod.c
index 54fcc01564d9..d5fdaec3d0b6 100644
--- a/lib/crypto/mpi/mpi-mod.c
+++ b/lib/crypto/mpi/mpi-mod.c
@@ -5,153 +5,9 @@
* This file is part of Libgcrypt.
*/
-
#include "mpi-internal.h"
-#include "longlong.h"
-
-/* Context used with Barrett reduction. */
-struct barrett_ctx_s {
- MPI m; /* The modulus - may not be modified. */
- int m_copied; /* If true, M needs to be released. */
- int k;
- MPI y;
- MPI r1; /* Helper MPI. */
- MPI r2; /* Helper MPI. */
- MPI r3; /* Helper MPI allocated on demand. */
-};
-
-
-
-void mpi_mod(MPI rem, MPI dividend, MPI divisor)
-{
- mpi_fdiv_r(rem, dividend, divisor);
-}
-
-/* This function returns a new context for Barrett based operations on
- * the modulus M. This context needs to be released using
- * _gcry_mpi_barrett_free. If COPY is true M will be transferred to
- * the context and the user may change M. If COPY is false, M may not
- * be changed until gcry_mpi_barrett_free has been called.
- */
-mpi_barrett_t mpi_barrett_init(MPI m, int copy)
-{
- mpi_barrett_t ctx;
- MPI tmp;
-
- mpi_normalize(m);
- ctx = kcalloc(1, sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
- return NULL;
-
- if (copy) {
- ctx->m = mpi_copy(m);
- ctx->m_copied = 1;
- } else
- ctx->m = m;
-
- ctx->k = mpi_get_nlimbs(m);
- tmp = mpi_alloc(ctx->k + 1);
-
- /* Barrett precalculation: y = floor(b^(2k) / m). */
- mpi_set_ui(tmp, 1);
- mpi_lshift_limbs(tmp, 2 * ctx->k);
- mpi_fdiv_q(tmp, tmp, m);
-
- ctx->y = tmp;
- ctx->r1 = mpi_alloc(2 * ctx->k + 1);
- ctx->r2 = mpi_alloc(2 * ctx->k + 1);
-
- return ctx;
-}
-
-void mpi_barrett_free(mpi_barrett_t ctx)
-{
- if (ctx) {
- mpi_free(ctx->y);
- mpi_free(ctx->r1);
- mpi_free(ctx->r2);
- if (ctx->r3)
- mpi_free(ctx->r3);
- if (ctx->m_copied)
- mpi_free(ctx->m);
- kfree(ctx);
- }
-}
-
-
-/* R = X mod M
- *
- * Using Barrett reduction. Before using this function
- * _gcry_mpi_barrett_init must have been called to do the
- * precalculations. CTX is the context created by this precalculation
- * and also conveys M. If the Barret reduction could no be done a
- * straightforward reduction method is used.
- *
- * We assume that these conditions are met:
- * Input: x =(x_2k-1 ...x_0)_b
- * m =(m_k-1 ....m_0)_b with m_k-1 != 0
- * Output: r = x mod m
- */
-void mpi_mod_barrett(MPI r, MPI x, mpi_barrett_t ctx)
-{
- MPI m = ctx->m;
- int k = ctx->k;
- MPI y = ctx->y;
- MPI r1 = ctx->r1;
- MPI r2 = ctx->r2;
- int sign;
-
- mpi_normalize(x);
- if (mpi_get_nlimbs(x) > 2*k) {
- mpi_mod(r, x, m);
- return;
- }
-
- sign = x->sign;
- x->sign = 0;
-
- /* 1. q1 = floor( x / b^k-1)
- * q2 = q1 * y
- * q3 = floor( q2 / b^k+1 )
- * Actually, we don't need qx, we can work direct on r2
- */
- mpi_set(r2, x);
- mpi_rshift_limbs(r2, k-1);
- mpi_mul(r2, r2, y);
- mpi_rshift_limbs(r2, k+1);
-
- /* 2. r1 = x mod b^k+1
- * r2 = q3 * m mod b^k+1
- * r = r1 - r2
- * 3. if r < 0 then r = r + b^k+1
- */
- mpi_set(r1, x);
- if (r1->nlimbs > k+1) /* Quick modulo operation. */
- r1->nlimbs = k+1;
- mpi_mul(r2, r2, m);
- if (r2->nlimbs > k+1) /* Quick modulo operation. */
- r2->nlimbs = k+1;
- mpi_sub(r, r1, r2);
-
- if (mpi_has_sign(r)) {
- if (!ctx->r3) {
- ctx->r3 = mpi_alloc(k + 2);
- mpi_set_ui(ctx->r3, 1);
- mpi_lshift_limbs(ctx->r3, k + 1);
- }
- mpi_add(r, r, ctx->r3);
- }
-
- /* 4. while r >= m do r = r - m */
- while (mpi_cmp(r, m) >= 0)
- mpi_sub(r, r, m);
-
- x->sign = sign;
-}
-
-void mpi_mul_barrett(MPI w, MPI u, MPI v, mpi_barrett_t ctx)
+int mpi_mod(MPI rem, MPI dividend, MPI divisor)
{
- mpi_mul(w, u, v);
- mpi_mod_barrett(w, w, ctx);
+ return mpi_fdiv_r(rem, dividend, divisor);
}
diff --git a/lib/crypto/mpi/mpi-mul.c b/lib/crypto/mpi/mpi-mul.c
index 7f4eda8560dc..7e6ff1ce3e9b 100644
--- a/lib/crypto/mpi/mpi-mul.c
+++ b/lib/crypto/mpi/mpi-mul.c
@@ -13,7 +13,7 @@
#include "mpi-internal.h"
-void mpi_mul(MPI w, MPI u, MPI v)
+int mpi_mul(MPI w, MPI u, MPI v)
{
mpi_size_t usize, vsize, wsize;
mpi_ptr_t up, vp, wp;
@@ -21,6 +21,7 @@ void mpi_mul(MPI w, MPI u, MPI v)
int usign, vsign, sign_product;
int assign_wp = 0;
mpi_ptr_t tmp_limb = NULL;
+ int err = 0;
if (u->nlimbs < v->nlimbs) {
/* Swap U and V. */
@@ -46,15 +47,21 @@ void mpi_mul(MPI w, MPI u, MPI v)
if (w->alloced < wsize) {
if (wp == up || wp == vp) {
wp = mpi_alloc_limb_space(wsize);
+ if (!wp)
+ return -ENOMEM;
assign_wp = 1;
} else {
- mpi_resize(w, wsize);
+ err = mpi_resize(w, wsize);
+ if (err)
+ return err;
wp = w->d;
}
} else { /* Make U and V not overlap with W. */
if (wp == up) {
/* W and U are identical. Allocate temporary space for U. */
up = tmp_limb = mpi_alloc_limb_space(usize);
+ if (!up)
+ return -ENOMEM;
/* Is V identical too? Keep it identical with U. */
if (wp == vp)
vp = up;
@@ -63,6 +70,8 @@ void mpi_mul(MPI w, MPI u, MPI v)
} else if (wp == vp) {
/* W and V are identical. Allocate temporary space for V. */
vp = tmp_limb = mpi_alloc_limb_space(vsize);
+ if (!vp)
+ return -ENOMEM;
/* Copy to the temporary space. */
MPN_COPY(vp, wp, vsize);
}
@@ -71,7 +80,12 @@ void mpi_mul(MPI w, MPI u, MPI v)
if (!vsize)
wsize = 0;
else {
- mpihelp_mul(wp, up, usize, vp, vsize, &cy);
+ err = mpihelp_mul(wp, up, usize, vp, vsize, &cy);
+ if (err) {
+ if (assign_wp)
+ mpi_free_limb_space(wp);
+ goto free_tmp_limb;
+ }
wsize -= cy ? 0:1;
}
@@ -79,14 +93,17 @@ void mpi_mul(MPI w, MPI u, MPI v)
mpi_assign_limb_space(w, wp, wsize);
w->nlimbs = wsize;
w->sign = sign_product;
+
+free_tmp_limb:
if (tmp_limb)
mpi_free_limb_space(tmp_limb);
+ return err;
}
EXPORT_SYMBOL_GPL(mpi_mul);
-void mpi_mulm(MPI w, MPI u, MPI v, MPI m)
+int mpi_mulm(MPI w, MPI u, MPI v, MPI m)
{
- mpi_mul(w, u, v);
- mpi_tdiv_r(w, w, m);
+ return mpi_mul(w, u, v) ?:
+ mpi_tdiv_r(w, w, m);
}
EXPORT_SYMBOL_GPL(mpi_mulm);
diff --git a/lib/crypto/mpi/mpi-pow.c b/lib/crypto/mpi/mpi-pow.c
index 2fd7a46d55ec..67fbd4c2503d 100644
--- a/lib/crypto/mpi/mpi-pow.c
+++ b/lib/crypto/mpi/mpi-pow.c
@@ -176,7 +176,6 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod)
for (;;) {
while (c) {
- mpi_ptr_t tp;
mpi_size_t xsize;
/*if (mpihelp_mul_n(xp, rp, rp, rsize) < 0) goto enomem */
@@ -207,9 +206,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod)
xsize = msize;
}
- tp = rp;
- rp = xp;
- xp = tp;
+ swap(rp, xp);
rsize = xsize;
if ((mpi_limb_signed_t) e < 0) {
@@ -235,9 +232,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod)
xsize = msize;
}
- tp = rp;
- rp = xp;
- xp = tp;
+ swap(rp, xp);
rsize = xsize;
}
e <<= 1;
diff --git a/lib/crypto/mpi/mpicoder.c b/lib/crypto/mpi/mpicoder.c
index 3cb6bd148fa9..dde01030807d 100644
--- a/lib/crypto/mpi/mpicoder.c
+++ b/lib/crypto/mpi/mpicoder.c
@@ -25,7 +25,6 @@
#include <linux/string.h>
#include "mpi-internal.h"
-#define MAX_EXTERN_SCAN_BYTES (16*1024*1024)
#define MAX_EXTERN_MPI_BITS 16384
/**
@@ -110,112 +109,6 @@ MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread)
}
EXPORT_SYMBOL_GPL(mpi_read_from_buffer);
-/****************
- * Fill the mpi VAL from the hex string in STR.
- */
-int mpi_fromstr(MPI val, const char *str)
-{
- int sign = 0;
- int prepend_zero = 0;
- int i, j, c, c1, c2;
- unsigned int nbits, nbytes, nlimbs;
- mpi_limb_t a;
-
- if (*str == '-') {
- sign = 1;
- str++;
- }
-
- /* Skip optional hex prefix. */
- if (*str == '0' && str[1] == 'x')
- str += 2;
-
- nbits = strlen(str);
- if (nbits > MAX_EXTERN_SCAN_BYTES) {
- mpi_clear(val);
- return -EINVAL;
- }
- nbits *= 4;
- if ((nbits % 8))
- prepend_zero = 1;
-
- nbytes = (nbits+7) / 8;
- nlimbs = (nbytes+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB;
-
- if (val->alloced < nlimbs)
- mpi_resize(val, nlimbs);
-
- i = BYTES_PER_MPI_LIMB - (nbytes % BYTES_PER_MPI_LIMB);
- i %= BYTES_PER_MPI_LIMB;
- j = val->nlimbs = nlimbs;
- val->sign = sign;
- for (; j > 0; j--) {
- a = 0;
- for (; i < BYTES_PER_MPI_LIMB; i++) {
- if (prepend_zero) {
- c1 = '0';
- prepend_zero = 0;
- } else
- c1 = *str++;
-
- if (!c1) {
- mpi_clear(val);
- return -EINVAL;
- }
- c2 = *str++;
- if (!c2) {
- mpi_clear(val);
- return -EINVAL;
- }
- if (c1 >= '0' && c1 <= '9')
- c = c1 - '0';
- else if (c1 >= 'a' && c1 <= 'f')
- c = c1 - 'a' + 10;
- else if (c1 >= 'A' && c1 <= 'F')
- c = c1 - 'A' + 10;
- else {
- mpi_clear(val);
- return -EINVAL;
- }
- c <<= 4;
- if (c2 >= '0' && c2 <= '9')
- c |= c2 - '0';
- else if (c2 >= 'a' && c2 <= 'f')
- c |= c2 - 'a' + 10;
- else if (c2 >= 'A' && c2 <= 'F')
- c |= c2 - 'A' + 10;
- else {
- mpi_clear(val);
- return -EINVAL;
- }
- a <<= 8;
- a |= c;
- }
- i = 0;
- val->d[j-1] = a;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(mpi_fromstr);
-
-MPI mpi_scanval(const char *string)
-{
- MPI a;
-
- a = mpi_alloc(0);
- if (!a)
- return NULL;
-
- if (mpi_fromstr(a, string)) {
- mpi_free(a);
- return NULL;
- }
- mpi_normalize(a);
- return a;
-}
-EXPORT_SYMBOL_GPL(mpi_scanval);
-
static int count_lzeros(MPI a)
{
mpi_limb_t alimb;
@@ -521,232 +414,3 @@ MPI mpi_read_raw_from_sgl(struct scatterlist *sgl, unsigned int nbytes)
return val;
}
EXPORT_SYMBOL_GPL(mpi_read_raw_from_sgl);
-
-/* Perform a two's complement operation on buffer P of size N bytes. */
-static void twocompl(unsigned char *p, unsigned int n)
-{
- int i;
-
- for (i = n-1; i >= 0 && !p[i]; i--)
- ;
- if (i >= 0) {
- if ((p[i] & 0x01))
- p[i] = (((p[i] ^ 0xfe) | 0x01) & 0xff);
- else if ((p[i] & 0x02))
- p[i] = (((p[i] ^ 0xfc) | 0x02) & 0xfe);
- else if ((p[i] & 0x04))
- p[i] = (((p[i] ^ 0xf8) | 0x04) & 0xfc);
- else if ((p[i] & 0x08))
- p[i] = (((p[i] ^ 0xf0) | 0x08) & 0xf8);
- else if ((p[i] & 0x10))
- p[i] = (((p[i] ^ 0xe0) | 0x10) & 0xf0);
- else if ((p[i] & 0x20))
- p[i] = (((p[i] ^ 0xc0) | 0x20) & 0xe0);
- else if ((p[i] & 0x40))
- p[i] = (((p[i] ^ 0x80) | 0x40) & 0xc0);
- else
- p[i] = 0x80;
-
- for (i--; i >= 0; i--)
- p[i] ^= 0xff;
- }
-}
-
-int mpi_print(enum gcry_mpi_format format, unsigned char *buffer,
- size_t buflen, size_t *nwritten, MPI a)
-{
- unsigned int nbits = mpi_get_nbits(a);
- size_t len;
- size_t dummy_nwritten;
- int negative;
-
- if (!nwritten)
- nwritten = &dummy_nwritten;
-
- /* Libgcrypt does no always care to set clear the sign if the value
- * is 0. For printing this is a bit of a surprise, in particular
- * because if some of the formats don't support negative numbers but
- * should be able to print a zero. Thus we need this extra test
- * for a negative number.
- */
- if (a->sign && mpi_cmp_ui(a, 0))
- negative = 1;
- else
- negative = 0;
-
- len = buflen;
- *nwritten = 0;
- if (format == GCRYMPI_FMT_STD) {
- unsigned char *tmp;
- int extra = 0;
- unsigned int n;
-
- tmp = mpi_get_buffer(a, &n, NULL);
- if (!tmp)
- return -EINVAL;
-
- if (negative) {
- twocompl(tmp, n);
- if (!(*tmp & 0x80)) {
- /* Need to extend the sign. */
- n++;
- extra = 2;
- }
- } else if (n && (*tmp & 0x80)) {
- /* Positive but the high bit of the returned buffer is set.
- * Thus we need to print an extra leading 0x00 so that the
- * output is interpreted as a positive number.
- */
- n++;
- extra = 1;
- }
-
- if (buffer && n > len) {
- /* The provided buffer is too short. */
- kfree(tmp);
- return -E2BIG;
- }
- if (buffer) {
- unsigned char *s = buffer;
-
- if (extra == 1)
- *s++ = 0;
- else if (extra)
- *s++ = 0xff;
- memcpy(s, tmp, n-!!extra);
- }
- kfree(tmp);
- *nwritten = n;
- return 0;
- } else if (format == GCRYMPI_FMT_USG) {
- unsigned int n = (nbits + 7)/8;
-
- /* Note: We ignore the sign for this format. */
- /* FIXME: for performance reasons we should put this into
- * mpi_aprint because we can then use the buffer directly.
- */
-
- if (buffer && n > len)
- return -E2BIG;
- if (buffer) {
- unsigned char *tmp;
-
- tmp = mpi_get_buffer(a, &n, NULL);
- if (!tmp)
- return -EINVAL;
- memcpy(buffer, tmp, n);
- kfree(tmp);
- }
- *nwritten = n;
- return 0;
- } else if (format == GCRYMPI_FMT_PGP) {
- unsigned int n = (nbits + 7)/8;
-
- /* The PGP format can only handle unsigned integers. */
- if (negative)
- return -EINVAL;
-
- if (buffer && n+2 > len)
- return -E2BIG;
-
- if (buffer) {
- unsigned char *tmp;
- unsigned char *s = buffer;
-
- s[0] = nbits >> 8;
- s[1] = nbits;
-
- tmp = mpi_get_buffer(a, &n, NULL);
- if (!tmp)
- return -EINVAL;
- memcpy(s+2, tmp, n);
- kfree(tmp);
- }
- *nwritten = n+2;
- return 0;
- } else if (format == GCRYMPI_FMT_SSH) {
- unsigned char *tmp;
- int extra = 0;
- unsigned int n;
-
- tmp = mpi_get_buffer(a, &n, NULL);
- if (!tmp)
- return -EINVAL;
-
- if (negative) {
- twocompl(tmp, n);
- if (!(*tmp & 0x80)) {
- /* Need to extend the sign. */
- n++;
- extra = 2;
- }
- } else if (n && (*tmp & 0x80)) {
- n++;
- extra = 1;
- }
-
- if (buffer && n+4 > len) {
- kfree(tmp);
- return -E2BIG;
- }
-
- if (buffer) {
- unsigned char *s = buffer;
-
- *s++ = n >> 24;
- *s++ = n >> 16;
- *s++ = n >> 8;
- *s++ = n;
- if (extra == 1)
- *s++ = 0;
- else if (extra)
- *s++ = 0xff;
- memcpy(s, tmp, n-!!extra);
- }
- kfree(tmp);
- *nwritten = 4+n;
- return 0;
- } else if (format == GCRYMPI_FMT_HEX) {
- unsigned char *tmp;
- int i;
- int extra = 0;
- unsigned int n = 0;
-
- tmp = mpi_get_buffer(a, &n, NULL);
- if (!tmp)
- return -EINVAL;
- if (!n || (*tmp & 0x80))
- extra = 2;
-
- if (buffer && 2*n + extra + negative + 1 > len) {
- kfree(tmp);
- return -E2BIG;
- }
- if (buffer) {
- unsigned char *s = buffer;
-
- if (negative)
- *s++ = '-';
- if (extra) {
- *s++ = '0';
- *s++ = '0';
- }
-
- for (i = 0; i < n; i++) {
- unsigned int c = tmp[i];
-
- *s++ = (c >> 4) < 10 ? '0'+(c>>4) : 'A'+(c>>4)-10;
- c &= 15;
- *s++ = c < 10 ? '0'+c : 'A'+c-10;
- }
- *s++ = 0;
- *nwritten = s - buffer;
- } else {
- *nwritten = 2*n + extra + negative + 1;
- }
- kfree(tmp);
- return 0;
- } else
- return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(mpi_print);
diff --git a/lib/crypto/mpi/mpih-mul.c b/lib/crypto/mpi/mpih-mul.c
index e5f1c84e3c48..a93647564054 100644
--- a/lib/crypto/mpi/mpih-mul.c
+++ b/lib/crypto/mpi/mpih-mul.c
@@ -317,31 +317,6 @@ mpih_sqr_n(mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size, mpi_ptr_t tspace)
}
}
-
-void mpihelp_mul_n(mpi_ptr_t prodp,
- mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t size)
-{
- if (up == vp) {
- if (size < KARATSUBA_THRESHOLD)
- mpih_sqr_n_basecase(prodp, up, size);
- else {
- mpi_ptr_t tspace;
- tspace = mpi_alloc_limb_space(2 * size);
- mpih_sqr_n(prodp, up, size, tspace);
- mpi_free_limb_space(tspace);
- }
- } else {
- if (size < KARATSUBA_THRESHOLD)
- mul_n_basecase(prodp, up, vp, size);
- else {
- mpi_ptr_t tspace;
- tspace = mpi_alloc_limb_space(2 * size);
- mul_n(prodp, up, vp, size, tspace);
- mpi_free_limb_space(tspace);
- }
- }
-}
-
int
mpihelp_mul_karatsuba_case(mpi_ptr_t prodp,
mpi_ptr_t up, mpi_size_t usize,
diff --git a/lib/crypto/mpi/mpiutil.c b/lib/crypto/mpi/mpiutil.c
index aa8c46544af8..979ece5a81d2 100644
--- a/lib/crypto/mpi/mpiutil.c
+++ b/lib/crypto/mpi/mpiutil.c
@@ -20,63 +20,6 @@
#include "mpi-internal.h"
-/* Constants allocated right away at startup. */
-static MPI constants[MPI_NUMBER_OF_CONSTANTS];
-
-/* Initialize the MPI subsystem. This is called early and allows to
- * do some initialization without taking care of threading issues.
- */
-static int __init mpi_init(void)
-{
- int idx;
- unsigned long value;
-
- for (idx = 0; idx < MPI_NUMBER_OF_CONSTANTS; idx++) {
- switch (idx) {
- case MPI_C_ZERO:
- value = 0;
- break;
- case MPI_C_ONE:
- value = 1;
- break;
- case MPI_C_TWO:
- value = 2;
- break;
- case MPI_C_THREE:
- value = 3;
- break;
- case MPI_C_FOUR:
- value = 4;
- break;
- case MPI_C_EIGHT:
- value = 8;
- break;
- default:
- pr_err("MPI: invalid mpi_const selector %d\n", idx);
- return -EFAULT;
- }
- constants[idx] = mpi_alloc_set_ui(value);
- constants[idx]->flags = (16|32);
- }
-
- return 0;
-}
-postcore_initcall(mpi_init);
-
-/* Return a constant MPI descripbed by NO which is one of the
- * MPI_C_xxx macros. There is no need to copy this returned value; it
- * may be used directly.
- */
-MPI mpi_const(enum gcry_mpi_constants no)
-{
- if ((int)no < 0 || no > MPI_NUMBER_OF_CONSTANTS)
- pr_err("MPI: invalid mpi_const selector %d\n", no);
- if (!constants[no])
- pr_err("MPI: MPI subsystem not initialized\n");
- return constants[no];
-}
-EXPORT_SYMBOL_GPL(mpi_const);
-
/****************
* Note: It was a bad idea to use the number of limbs to allocate
* because on a alpha the limbs are large but we normally need
@@ -163,15 +106,6 @@ int mpi_resize(MPI a, unsigned nlimbs)
return 0;
}
-void mpi_clear(MPI a)
-{
- if (!a)
- return;
- a->nlimbs = 0;
- a->flags = 0;
-}
-EXPORT_SYMBOL_GPL(mpi_clear);
-
void mpi_free(MPI a)
{
if (!a)
@@ -199,6 +133,8 @@ MPI mpi_copy(MPI a)
if (a) {
b = mpi_alloc(a->nlimbs);
+ if (!b)
+ return NULL;
b->nlimbs = a->nlimbs;
b->sign = a->sign;
b->flags = a->flags;
@@ -210,121 +146,5 @@ MPI mpi_copy(MPI a)
return b;
}
-/****************
- * This function allocates an MPI which is optimized to hold
- * a value as large as the one given in the argument and allocates it
- * with the same flags as A.
- */
-MPI mpi_alloc_like(MPI a)
-{
- MPI b;
-
- if (a) {
- b = mpi_alloc(a->nlimbs);
- b->nlimbs = 0;
- b->sign = 0;
- b->flags = a->flags;
- } else
- b = NULL;
-
- return b;
-}
-
-
-/* Set U into W and release U. If W is NULL only U will be released. */
-void mpi_snatch(MPI w, MPI u)
-{
- if (w) {
- mpi_assign_limb_space(w, u->d, u->alloced);
- w->nlimbs = u->nlimbs;
- w->sign = u->sign;
- w->flags = u->flags;
- u->alloced = 0;
- u->nlimbs = 0;
- u->d = NULL;
- }
- mpi_free(u);
-}
-
-
-MPI mpi_set(MPI w, MPI u)
-{
- mpi_ptr_t wp, up;
- mpi_size_t usize = u->nlimbs;
- int usign = u->sign;
-
- if (!w)
- w = mpi_alloc(mpi_get_nlimbs(u));
- RESIZE_IF_NEEDED(w, usize);
- wp = w->d;
- up = u->d;
- MPN_COPY(wp, up, usize);
- w->nlimbs = usize;
- w->flags = u->flags;
- w->flags &= ~(16|32); /* Reset the immutable and constant flags. */
- w->sign = usign;
- return w;
-}
-EXPORT_SYMBOL_GPL(mpi_set);
-
-MPI mpi_set_ui(MPI w, unsigned long u)
-{
- if (!w)
- w = mpi_alloc(1);
- /* FIXME: If U is 0 we have no need to resize and thus possible
- * allocating the limbs.
- */
- RESIZE_IF_NEEDED(w, 1);
- w->d[0] = u;
- w->nlimbs = u ? 1 : 0;
- w->sign = 0;
- w->flags = 0;
- return w;
-}
-EXPORT_SYMBOL_GPL(mpi_set_ui);
-
-MPI mpi_alloc_set_ui(unsigned long u)
-{
- MPI w = mpi_alloc(1);
- w->d[0] = u;
- w->nlimbs = u ? 1 : 0;
- w->sign = 0;
- return w;
-}
-
-/****************
- * Swap the value of A and B, when SWAP is 1.
- * Leave the value when SWAP is 0.
- * This implementation should be constant-time regardless of SWAP.
- */
-void mpi_swap_cond(MPI a, MPI b, unsigned long swap)
-{
- mpi_size_t i;
- mpi_size_t nlimbs;
- mpi_limb_t mask = ((mpi_limb_t)0) - swap;
- mpi_limb_t x;
-
- if (a->alloced > b->alloced)
- nlimbs = b->alloced;
- else
- nlimbs = a->alloced;
- if (a->nlimbs > nlimbs || b->nlimbs > nlimbs)
- return;
-
- for (i = 0; i < nlimbs; i++) {
- x = mask & (a->d[i] ^ b->d[i]);
- a->d[i] = a->d[i] ^ x;
- b->d[i] = b->d[i] ^ x;
- }
-
- x = mask & (a->nlimbs ^ b->nlimbs);
- a->nlimbs = a->nlimbs ^ x;
- b->nlimbs = b->nlimbs ^ x;
-
- x = mask & (a->sign ^ b->sign);
- a->sign = a->sign ^ x;
- b->sign = b->sign ^ x;
-}
-
MODULE_DESCRIPTION("Multiprecision maths library");
MODULE_LICENSE("GPL");
diff --git a/lib/crypto/poly1305-donna32.c b/lib/crypto/poly1305-donna32.c
index 7fb71845cc84..0a4a2d99e365 100644
--- a/lib/crypto/poly1305-donna32.c
+++ b/lib/crypto/poly1305-donna32.c
@@ -7,7 +7,7 @@
*/
#include <linux/kernel.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <crypto/internal/poly1305.h>
void poly1305_core_setkey(struct poly1305_core_key *key,
diff --git a/lib/crypto/poly1305-donna64.c b/lib/crypto/poly1305-donna64.c
index 988702c9b3b2..530287531b2e 100644
--- a/lib/crypto/poly1305-donna64.c
+++ b/lib/crypto/poly1305-donna64.c
@@ -7,7 +7,7 @@
*/
#include <linux/kernel.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <crypto/internal/poly1305.h>
void poly1305_core_setkey(struct poly1305_core_key *key,
diff --git a/lib/crypto/poly1305.c b/lib/crypto/poly1305.c
index 26d87fc3823e..6e80214ebad8 100644
--- a/lib/crypto/poly1305.c
+++ b/lib/crypto/poly1305.c
@@ -10,7 +10,7 @@
#include <crypto/internal/poly1305.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
void poly1305_init_generic(struct poly1305_desc_ctx *desc,
const u8 key[POLY1305_KEY_SIZE])
@@ -76,3 +76,4 @@ EXPORT_SYMBOL_GPL(poly1305_final_generic);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Martin Willi <martin@strongswan.org>");
+MODULE_DESCRIPTION("Poly1305 authenticator algorithm, RFC7539");
diff --git a/lib/crypto/sha1.c b/lib/crypto/sha1.c
index 1aebe7be9401..ebb60519ae93 100644
--- a/lib/crypto/sha1.c
+++ b/lib/crypto/sha1.c
@@ -12,7 +12,7 @@
#include <linux/bitops.h>
#include <linux/string.h>
#include <crypto/sha1.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
/*
* If you have 32 registers or more, the compiler can (and should)
@@ -137,4 +137,5 @@ void sha1_init(__u32 *buf)
}
EXPORT_SYMBOL(sha1_init);
+MODULE_DESCRIPTION("SHA-1 Algorithm");
MODULE_LICENSE("GPL");
diff --git a/lib/crypto/sha256.c b/lib/crypto/sha256.c
index 3ac1ef8677db..04c1f2557e6c 100644
--- a/lib/crypto/sha256.c
+++ b/lib/crypto/sha256.c
@@ -11,7 +11,7 @@
* Copyright (c) 2014 Red Hat Inc.
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <crypto/sha256_base.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -165,4 +165,5 @@ void sha256(const u8 *data, unsigned int len, u8 *out)
}
EXPORT_SYMBOL(sha256);
+MODULE_DESCRIPTION("SHA-256 Algorithm");
MODULE_LICENSE("GPL");
diff --git a/lib/crypto/utils.c b/lib/crypto/utils.c
index c852c7151b0a..87da2a6dd161 100644
--- a/lib/crypto/utils.c
+++ b/lib/crypto/utils.c
@@ -5,7 +5,7 @@
* Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au>
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <crypto/utils.h>
#include <linux/module.h>
@@ -85,4 +85,5 @@ void __crypto_xor(u8 *dst, const u8 *src1, const u8 *src2, unsigned int len)
}
EXPORT_SYMBOL_GPL(__crypto_xor);
+MODULE_DESCRIPTION("Crypto library utility functions");
MODULE_LICENSE("GPL");
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index fb12a9bacd2f..5ce473ad499b 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -70,32 +70,33 @@ static HLIST_HEAD(obj_to_free);
* made at debug_stats_show(). Both obj_pool_min_free and obj_pool_max_used
* can be off.
*/
-static int obj_pool_min_free = ODEBUG_POOL_SIZE;
-static int obj_pool_free = ODEBUG_POOL_SIZE;
+static int __data_racy obj_pool_min_free = ODEBUG_POOL_SIZE;
+static int __data_racy obj_pool_free = ODEBUG_POOL_SIZE;
static int obj_pool_used;
-static int obj_pool_max_used;
+static int __data_racy obj_pool_max_used;
static bool obj_freeing;
/* The number of objs on the global free list */
static int obj_nr_tofree;
-static int debug_objects_maxchain __read_mostly;
-static int __maybe_unused debug_objects_maxchecked __read_mostly;
-static int debug_objects_fixups __read_mostly;
-static int debug_objects_warnings __read_mostly;
-static int debug_objects_enabled __read_mostly
- = CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT;
-static int debug_objects_pool_size __read_mostly
- = ODEBUG_POOL_SIZE;
-static int debug_objects_pool_min_level __read_mostly
- = ODEBUG_POOL_MIN_LEVEL;
+static int __data_racy debug_objects_maxchain __read_mostly;
+static int __data_racy __maybe_unused debug_objects_maxchecked __read_mostly;
+static int __data_racy debug_objects_fixups __read_mostly;
+static int __data_racy debug_objects_warnings __read_mostly;
+static int __data_racy debug_objects_enabled __read_mostly
+ = CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT;
+static int debug_objects_pool_size __ro_after_init
+ = ODEBUG_POOL_SIZE;
+static int debug_objects_pool_min_level __ro_after_init
+ = ODEBUG_POOL_MIN_LEVEL;
+
static const struct debug_obj_descr *descr_test __read_mostly;
static struct kmem_cache *obj_cache __ro_after_init;
/*
* Track numbers of kmem_cache_alloc()/free() calls done.
*/
-static int debug_objects_allocated;
-static int debug_objects_freed;
+static int __data_racy debug_objects_allocated;
+static int __data_racy debug_objects_freed;
static void free_obj_work(struct work_struct *work);
static DECLARE_DELAYED_WORK(debug_obj_work, free_obj_work);
@@ -134,20 +135,19 @@ static void fill_pool(void)
return;
/*
- * Reuse objs from the global free list; they will be reinitialized
- * when allocating.
+ * Reuse objs from the global obj_to_free list; they will be
+ * reinitialized when allocating.
*
- * Both obj_nr_tofree and obj_pool_free are checked locklessly; the
- * READ_ONCE()s pair with the WRITE_ONCE()s in pool_lock critical
- * sections.
+ * obj_nr_tofree is checked locklessly; the READ_ONCE() pairs with
+ * the WRITE_ONCE() in pool_lock critical sections.
*/
- while (READ_ONCE(obj_nr_tofree) && (READ_ONCE(obj_pool_free) < obj_pool_min_free)) {
+ if (READ_ONCE(obj_nr_tofree)) {
raw_spin_lock_irqsave(&pool_lock, flags);
/*
* Recheck with the lock held as the worker thread might have
* won the race and freed the global free list already.
*/
- while (obj_nr_tofree && (obj_pool_free < obj_pool_min_free)) {
+ while (obj_nr_tofree && (obj_pool_free < debug_objects_pool_min_level)) {
obj = hlist_entry(obj_to_free.first, typeof(*obj), node);
hlist_del(&obj->node);
WRITE_ONCE(obj_nr_tofree, obj_nr_tofree - 1);
diff --git a/lib/decompress_bunzip2.c b/lib/decompress_bunzip2.c
index 3518e7394eca..ca736166f100 100644
--- a/lib/decompress_bunzip2.c
+++ b/lib/decompress_bunzip2.c
@@ -232,7 +232,8 @@ static int INIT get_next_block(struct bunzip_data *bd)
RUNB) */
symCount = symTotal+2;
for (j = 0; j < groupCount; j++) {
- unsigned char length[MAX_SYMBOLS], temp[MAX_HUFCODE_BITS+1];
+ unsigned char length[MAX_SYMBOLS];
+ unsigned short temp[MAX_HUFCODE_BITS+1];
int minLen, maxLen, pp;
/* Read Huffman code lengths for each symbol. They're
stored in a way similar to mtf; record a starting
diff --git a/lib/decompress_unlz4.c b/lib/decompress_unlz4.c
index e6327391b6b6..c0dbb3cea915 100644
--- a/lib/decompress_unlz4.c
+++ b/lib/decompress_unlz4.c
@@ -16,7 +16,7 @@
#include <linux/decompress/mm.h>
#include <linux/compiler.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
/*
* Note: Uncompressed chunk size is used in the compressor side
diff --git a/lib/decompress_unlzma.c b/lib/decompress_unlzma.c
index 20a858031f12..9d34d35908da 100644
--- a/lib/decompress_unlzma.c
+++ b/lib/decompress_unlzma.c
@@ -37,7 +37,9 @@
#include <linux/decompress/mm.h>
+#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
static long long INIT read_int(unsigned char *ptr, int size)
{
diff --git a/lib/decompress_unlzo.c b/lib/decompress_unlzo.c
index 64c1358500ce..57a9e93743e1 100644
--- a/lib/decompress_unlzo.c
+++ b/lib/decompress_unlzo.c
@@ -28,7 +28,7 @@
#include <linux/decompress/mm.h>
#include <linux/compiler.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
static const unsigned char lzop_magic[] = {
0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a };
diff --git a/lib/decompress_unxz.c b/lib/decompress_unxz.c
index 842894158944..32138bb8ef77 100644
--- a/lib/decompress_unxz.c
+++ b/lib/decompress_unxz.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: 0BSD
+
/*
* Wrapper for decompressing XZ-compressed kernel, initramfs, and initrd
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
- *
- * This file has been put into the public domain.
- * You can do whatever you want with this file.
*/
/*
@@ -103,12 +102,11 @@
#ifdef STATIC
# define XZ_PREBOOT
#else
-#include <linux/decompress/unxz.h>
+# include <linux/decompress/unxz.h>
#endif
#ifdef __KERNEL__
# include <linux/decompress/mm.h>
#endif
-#define XZ_EXTERN STATIC
#ifndef XZ_PREBOOT
# include <linux/slab.h>
@@ -127,11 +125,21 @@
#ifdef CONFIG_X86
# define XZ_DEC_X86
#endif
-#ifdef CONFIG_PPC
+#if defined(CONFIG_PPC) && defined(CONFIG_CPU_BIG_ENDIAN)
# define XZ_DEC_POWERPC
#endif
#ifdef CONFIG_ARM
-# define XZ_DEC_ARM
+# ifdef CONFIG_THUMB2_KERNEL
+# define XZ_DEC_ARMTHUMB
+# else
+# define XZ_DEC_ARM
+# endif
+#endif
+#ifdef CONFIG_ARM64
+# define XZ_DEC_ARM64
+#endif
+#ifdef CONFIG_RISCV
+# define XZ_DEC_RISCV
#endif
#ifdef CONFIG_SPARC
# define XZ_DEC_SPARC
@@ -220,7 +228,7 @@ void *memmove(void *dest, const void *src, size_t size)
#endif
/*
- * Since we need memmove anyway, would use it as memcpy too.
+ * Since we need memmove anyway, we could use it as memcpy too.
* Commented out for now to avoid breaking things.
*/
/*
@@ -390,17 +398,17 @@ error_alloc_state:
}
/*
- * This macro is used by architecture-specific files to decompress
+ * This function is used by architecture-specific files to decompress
* the kernel image.
*/
#ifdef XZ_PREBOOT
-STATIC int INIT __decompress(unsigned char *buf, long len,
- long (*fill)(void*, unsigned long),
- long (*flush)(void*, unsigned long),
- unsigned char *out_buf, long olen,
- long *pos,
- void (*error)(char *x))
+STATIC int INIT __decompress(unsigned char *in, long in_size,
+ long (*fill)(void *dest, unsigned long size),
+ long (*flush)(void *src, unsigned long size),
+ unsigned char *out, long out_size,
+ long *in_used,
+ void (*error)(char *x))
{
- return unxz(buf, len, fill, flush, out_buf, pos, error);
+ return unxz(in, in_size, fill, flush, out, in_used, error);
}
#endif
diff --git a/lib/devres.c b/lib/devres.c
index fe0c63caeb68..4fc152de6d8b 100644
--- a/lib/devres.c
+++ b/lib/devres.c
@@ -1,10 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
+#include <linux/bug.h>
#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/gfp.h>
+#include <linux/errno.h>
#include <linux/export.h>
+#include <linux/gfp_types.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
#include <linux/of_address.h>
+#include <linux/types.h>
enum devm_ioremap_type {
DEVM_IOREMAP = 0,
@@ -125,12 +128,13 @@ __devm_ioremap_resource(struct device *dev, const struct resource *res,
resource_size_t size;
void __iomem *dest_ptr;
char *pretty_name;
+ int ret;
BUG_ON(!dev);
if (!res || resource_type(res) != IORESOURCE_MEM) {
- dev_err(dev, "invalid resource %pR\n", res);
- return IOMEM_ERR_PTR(-EINVAL);
+ ret = dev_err_probe(dev, -EINVAL, "invalid resource %pR\n", res);
+ return IOMEM_ERR_PTR(ret);
}
if (type == DEVM_IOREMAP && res->flags & IORESOURCE_MEM_NONPOSTED)
@@ -144,20 +148,20 @@ __devm_ioremap_resource(struct device *dev, const struct resource *res,
else
pretty_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
if (!pretty_name) {
- dev_err(dev, "can't generate pretty name for resource %pR\n", res);
- return IOMEM_ERR_PTR(-ENOMEM);
+ ret = dev_err_probe(dev, -ENOMEM, "can't generate pretty name for resource %pR\n", res);
+ return IOMEM_ERR_PTR(ret);
}
if (!devm_request_mem_region(dev, res->start, size, pretty_name)) {
- dev_err(dev, "can't request region for resource %pR\n", res);
- return IOMEM_ERR_PTR(-EBUSY);
+ ret = dev_err_probe(dev, -EBUSY, "can't request region for resource %pR\n", res);
+ return IOMEM_ERR_PTR(ret);
}
dest_ptr = __devm_ioremap(dev, res->start, size, type);
if (!dest_ptr) {
- dev_err(dev, "ioremap failed for resource %pR\n", res);
devm_release_mem_region(dev, res->start, size);
- dest_ptr = IOMEM_ERR_PTR(-ENOMEM);
+ ret = dev_err_probe(dev, -ENOMEM, "ioremap failed for resource %pR\n", res);
+ return IOMEM_ERR_PTR(ret);
}
return dest_ptr;
diff --git a/lib/dhry_run.c b/lib/dhry_run.c
index e6a279dabf84..4a6d05ce4361 100644
--- a/lib/dhry_run.c
+++ b/lib/dhry_run.c
@@ -83,4 +83,5 @@ static int __init dhry_init(void)
module_init(dhry_init);
MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
+MODULE_DESCRIPTION("Dhrystone benchmark test module");
MODULE_LICENSE("GPL");
diff --git a/lib/dim/Makefile b/lib/dim/Makefile
index c4cc4026c451..5b9bfaac7ac1 100644
--- a/lib/dim/Makefile
+++ b/lib/dim/Makefile
@@ -4,4 +4,4 @@
obj-$(CONFIG_DIMLIB) += dimlib.o
-dimlib-objs := dim.o net_dim.o rdma_dim.o
+dimlib-y := dim.o net_dim.o rdma_dim.o
diff --git a/lib/dim/net_dim.c b/lib/dim/net_dim.c
index 4e32f7aaac86..d7e7028e9b19 100644
--- a/lib/dim/net_dim.c
+++ b/lib/dim/net_dim.c
@@ -4,6 +4,7 @@
*/
#include <linux/dim.h>
+#include <linux/rtnetlink.h>
/*
* Net DIM profiles:
@@ -11,12 +12,6 @@
* There are different set of profiles for RX/TX CQs.
* Each profile size must be of NET_DIM_PARAMS_NUM_PROFILES
*/
-#define NET_DIM_PARAMS_NUM_PROFILES 5
-#define NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE 256
-#define NET_DIM_DEFAULT_TX_CQ_PKTS_FROM_EQE 128
-#define NET_DIM_DEF_PROFILE_CQE 1
-#define NET_DIM_DEF_PROFILE_EQE 1
-
#define NET_DIM_RX_EQE_PROFILES { \
{.usec = 1, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
{.usec = 8, .pkts = NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE,}, \
@@ -101,6 +96,143 @@ net_dim_get_def_tx_moderation(u8 cq_period_mode)
}
EXPORT_SYMBOL(net_dim_get_def_tx_moderation);
+int net_dim_init_irq_moder(struct net_device *dev, u8 profile_flags,
+ u8 coal_flags, u8 rx_mode, u8 tx_mode,
+ void (*rx_dim_work)(struct work_struct *work),
+ void (*tx_dim_work)(struct work_struct *work))
+{
+ struct dim_cq_moder *rxp = NULL, *txp;
+ struct dim_irq_moder *moder;
+ int len;
+
+ dev->irq_moder = kzalloc(sizeof(*dev->irq_moder), GFP_KERNEL);
+ if (!dev->irq_moder)
+ return -ENOMEM;
+
+ moder = dev->irq_moder;
+ len = NET_DIM_PARAMS_NUM_PROFILES * sizeof(*moder->rx_profile);
+
+ moder->coal_flags = coal_flags;
+ moder->profile_flags = profile_flags;
+
+ if (profile_flags & DIM_PROFILE_RX) {
+ moder->rx_dim_work = rx_dim_work;
+ moder->dim_rx_mode = rx_mode;
+ rxp = kmemdup(rx_profile[rx_mode], len, GFP_KERNEL);
+ if (!rxp)
+ goto free_moder;
+
+ rcu_assign_pointer(moder->rx_profile, rxp);
+ }
+
+ if (profile_flags & DIM_PROFILE_TX) {
+ moder->tx_dim_work = tx_dim_work;
+ moder->dim_tx_mode = tx_mode;
+ txp = kmemdup(tx_profile[tx_mode], len, GFP_KERNEL);
+ if (!txp)
+ goto free_rxp;
+
+ rcu_assign_pointer(moder->tx_profile, txp);
+ }
+
+ return 0;
+
+free_rxp:
+ kfree(rxp);
+free_moder:
+ kfree(moder);
+ return -ENOMEM;
+}
+EXPORT_SYMBOL(net_dim_init_irq_moder);
+
+/* RTNL lock is held. */
+void net_dim_free_irq_moder(struct net_device *dev)
+{
+ struct dim_cq_moder *rxp, *txp;
+
+ if (!dev->irq_moder)
+ return;
+
+ rxp = rtnl_dereference(dev->irq_moder->rx_profile);
+ txp = rtnl_dereference(dev->irq_moder->tx_profile);
+
+ rcu_assign_pointer(dev->irq_moder->rx_profile, NULL);
+ rcu_assign_pointer(dev->irq_moder->tx_profile, NULL);
+
+ kfree_rcu(rxp, rcu);
+ kfree_rcu(txp, rcu);
+ kfree(dev->irq_moder);
+}
+EXPORT_SYMBOL(net_dim_free_irq_moder);
+
+void net_dim_setting(struct net_device *dev, struct dim *dim, bool is_tx)
+{
+ struct dim_irq_moder *irq_moder = dev->irq_moder;
+
+ if (!irq_moder)
+ return;
+
+ if (is_tx) {
+ INIT_WORK(&dim->work, irq_moder->tx_dim_work);
+ dim->mode = READ_ONCE(irq_moder->dim_tx_mode);
+ return;
+ }
+
+ INIT_WORK(&dim->work, irq_moder->rx_dim_work);
+ dim->mode = READ_ONCE(irq_moder->dim_rx_mode);
+}
+EXPORT_SYMBOL(net_dim_setting);
+
+void net_dim_work_cancel(struct dim *dim)
+{
+ cancel_work_sync(&dim->work);
+}
+EXPORT_SYMBOL(net_dim_work_cancel);
+
+struct dim_cq_moder net_dim_get_rx_irq_moder(struct net_device *dev,
+ struct dim *dim)
+{
+ struct dim_cq_moder res, *profile;
+
+ rcu_read_lock();
+ profile = rcu_dereference(dev->irq_moder->rx_profile);
+ res = profile[dim->profile_ix];
+ rcu_read_unlock();
+
+ res.cq_period_mode = dim->mode;
+
+ return res;
+}
+EXPORT_SYMBOL(net_dim_get_rx_irq_moder);
+
+struct dim_cq_moder net_dim_get_tx_irq_moder(struct net_device *dev,
+ struct dim *dim)
+{
+ struct dim_cq_moder res, *profile;
+
+ rcu_read_lock();
+ profile = rcu_dereference(dev->irq_moder->tx_profile);
+ res = profile[dim->profile_ix];
+ rcu_read_unlock();
+
+ res.cq_period_mode = dim->mode;
+
+ return res;
+}
+EXPORT_SYMBOL(net_dim_get_tx_irq_moder);
+
+void net_dim_set_rx_mode(struct net_device *dev, u8 rx_mode)
+{
+ WRITE_ONCE(dev->irq_moder->dim_rx_mode, rx_mode);
+}
+EXPORT_SYMBOL(net_dim_set_rx_mode);
+
+void net_dim_set_tx_mode(struct net_device *dev, u8 tx_mode)
+{
+ WRITE_ONCE(dev->irq_moder->dim_tx_mode, tx_mode);
+}
+EXPORT_SYMBOL(net_dim_set_tx_mode);
+
static int net_dim_step(struct dim *dim)
{
if (dim->tired == (NET_DIM_PARAMS_NUM_PROFILES * 2))
diff --git a/lib/dump_stack.c b/lib/dump_stack.c
index 222c6d6c8281..388da1aea14a 100644
--- a/lib/dump_stack.c
+++ b/lib/dump_stack.c
@@ -54,20 +54,26 @@ void __init dump_stack_set_arch_desc(const char *fmt, ...)
*/
void dump_stack_print_info(const char *log_lvl)
{
- printk("%sCPU: %d PID: %d Comm: %.20s %s%s %s %.*s" BUILD_ID_FMT "\n",
- log_lvl, raw_smp_processor_id(), current->pid, current->comm,
+ printk("%sCPU: %d UID: %u PID: %d Comm: %.20s %s%s %s %.*s" BUILD_ID_FMT "\n",
+ log_lvl, raw_smp_processor_id(),
+ __kuid_val(current_real_cred()->euid),
+ current->pid, current->comm,
kexec_crash_loaded() ? "Kdump: loaded " : "",
print_tainted(),
init_utsname()->release,
(int)strcspn(init_utsname()->version, " "),
init_utsname()->version, BUILD_ID_VAL);
+ if (get_taint())
+ printk("%s%s\n", log_lvl, print_tainted_verbose());
+
if (dump_stack_arch_desc_str[0] != '\0')
printk("%sHardware name: %s\n",
log_lvl, dump_stack_arch_desc_str);
print_worker_info(log_lvl, current);
print_stop_info(log_lvl, current);
+ print_scx_info(log_lvl, current);
}
/**
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index f2c5e7910bb1..5a007952f7f2 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -1147,7 +1147,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
iter->table->mod_name, dp->function,
ddebug_describe_flags(dp->flags, &flags));
seq_escape_str(m, dp->format, ESCAPE_SPACE, "\t\r\n\"");
- seq_puts(m, "\"");
+ seq_putc(m, '"');
if (dp->class_id != _DPRINTK_CLASS_DFLT) {
class = ddebug_class_name(iter, dp);
@@ -1156,7 +1156,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p)
else
seq_printf(m, " class unknown, _id:%d", dp->class_id);
}
- seq_puts(m, "\n");
+ seq_putc(m, '\n');
return 0;
}
diff --git a/lib/fault-inject.c b/lib/fault-inject.c
index d608f9b48c10..52eb6ba29698 100644
--- a/lib/fault-inject.c
+++ b/lib/fault-inject.c
@@ -2,6 +2,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/random.h>
+#include <linux/debugfs.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/types.h>
diff --git a/lib/find_bit.c b/lib/find_bit.c
index dacadd904250..0836bb3d76c5 100644
--- a/lib/find_bit.c
+++ b/lib/find_bit.c
@@ -87,7 +87,7 @@ out: \
if (sz % BITS_PER_LONG) \
tmp = (FETCH) & BITMAP_LAST_WORD_MASK(sz); \
found: \
- sz = min(idx * BITS_PER_LONG + fns(tmp, nr), sz); \
+ sz = idx * BITS_PER_LONG + fns(tmp, nr); \
out: \
sz; \
})
diff --git a/lib/find_bit_benchmark.c b/lib/find_bit_benchmark.c
index d3fb09e6eff1..402e160e7186 100644
--- a/lib/find_bit_benchmark.c
+++ b/lib/find_bit_benchmark.c
@@ -194,4 +194,5 @@ static int __init find_bit_test(void)
}
module_init(find_bit_test);
+MODULE_DESCRIPTION("Test for find_*_bit functions");
MODULE_LICENSE("GPL");
diff --git a/lib/fonts/Kconfig b/lib/fonts/Kconfig
index 7e945fdcbf11..3ac26bdbc3ff 100644
--- a/lib/fonts/Kconfig
+++ b/lib/fonts/Kconfig
@@ -10,7 +10,7 @@ if FONT_SUPPORT
config FONTS
bool "Select compiled-in fonts"
- depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
+ depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE || DRM_PANIC
help
Say Y here if you would like to use fonts other than the default
your frame buffer console usually use.
@@ -23,7 +23,7 @@ config FONTS
config FONT_8x8
bool "VGA 8x8 font" if FONTS
- depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
+ depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE || DRM_PANIC
default y if !SPARC && !FONTS
help
This is the "high resolution" font for the VGA frame buffer (the one
@@ -46,7 +46,7 @@ config FONT_8x16
config FONT_6x11
bool "Mac console 6x11 font (not supported by all drivers)" if FONTS
- depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
+ depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE || DRM_PANIC
default y if !SPARC && !FONTS && MAC
help
Small console font with Macintosh-style high-half glyphs. Some Mac
@@ -54,7 +54,7 @@ config FONT_6x11
config FONT_7x14
bool "console 7x14 font (not supported by all drivers)" if FONTS
- depends on FRAMEBUFFER_CONSOLE
+ depends on FRAMEBUFFER_CONSOLE || DRM_PANIC
help
Console font with characters just a bit smaller than the default.
If the standard 8x16 font is a little too big for you, say Y.
@@ -62,7 +62,7 @@ config FONT_7x14
config FONT_PEARL_8x8
bool "Pearl (old m68k) console 8x8 font" if FONTS
- depends on FRAMEBUFFER_CONSOLE
+ depends on FRAMEBUFFER_CONSOLE || DRM_PANIC
default y if !SPARC && !FONTS && AMIGA
help
Small console font with PC-style control-character and high-half
@@ -70,7 +70,7 @@ config FONT_PEARL_8x8
config FONT_ACORN_8x8
bool "Acorn console 8x8 font" if FONTS
- depends on FRAMEBUFFER_CONSOLE
+ depends on FRAMEBUFFER_CONSOLE || DRM_PANIC
default y if !SPARC && !FONTS && ARM && ARCH_ACORN
help
Small console font with PC-style control characters and high-half
@@ -90,7 +90,7 @@ config FONT_6x10
config FONT_10x18
bool "console 10x18 font (not supported by all drivers)" if FONTS
- depends on FRAMEBUFFER_CONSOLE
+ depends on FRAMEBUFFER_CONSOLE || DRM_PANIC
help
This is a high resolution console font for machines with very
big letters. It fits between the sun 12x22 and the normal 8x16 font.
@@ -105,7 +105,8 @@ config FONT_SUN8x16
config FONT_SUN12x22
bool "Sparc console 12x22 font (not supported by all drivers)"
- depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC)
+ depends on FRAMEBUFFER_CONSOLE || DRM_PANIC
+ depends on !SPARC && FONTS
help
This is the high resolution console font for Sun machines with very
big letters (like the letters used in the SPARC PROM). If the
@@ -113,7 +114,8 @@ config FONT_SUN12x22
config FONT_TER16x32
bool "Terminus 16x32 font (not supported by all drivers)"
- depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC)
+ depends on FRAMEBUFFER_CONSOLE || DRM_PANIC
+ depends on !SPARC && FONTS || SPARC
help
Terminus Font is a clean, fixed width bitmap font, designed
for long (8 and more hours per day) work with computers.
@@ -122,7 +124,7 @@ config FONT_TER16x32
config FONT_6x8
bool "OLED 6x8 font" if FONTS
- depends on FRAMEBUFFER_CONSOLE
+ depends on FRAMEBUFFER_CONSOLE || DRM_PANIC
help
This font is useful for small displays (OLED).
diff --git a/lib/fortify_kunit.c b/lib/fortify_kunit.c
index d2377e00caab..ecb638d4cde1 100644
--- a/lib/fortify_kunit.c
+++ b/lib/fortify_kunit.c
@@ -234,11 +234,6 @@ static void fortify_test_alloc_size_##allocator##_dynamic(struct kunit *test) \
checker(expected_size, \
kmalloc_array_node(alloc_size, 1, gfp, NUMA_NO_NODE), \
kfree(p)); \
- checker(expected_size, __kmalloc(alloc_size, gfp), \
- kfree(p)); \
- checker(expected_size, \
- __kmalloc_node(alloc_size, gfp, NUMA_NO_NODE), \
- kfree(p)); \
\
orig = kmalloc(alloc_size, gfp); \
KUNIT_EXPECT_TRUE(test, orig != NULL); \
@@ -311,8 +306,7 @@ DEFINE_ALLOC_SIZE_TEST_PAIR(vmalloc)
orig = kvmalloc(prev_size, gfp); \
KUNIT_EXPECT_TRUE(test, orig != NULL); \
checker(((expected_pages) * PAGE_SIZE) * 2, \
- kvrealloc(orig, prev_size, \
- ((alloc_pages) * PAGE_SIZE) * 2, gfp), \
+ kvrealloc(orig, ((alloc_pages) * PAGE_SIZE) * 2, gfp), \
kvfree(p)); \
} while (0)
DEFINE_ALLOC_SIZE_TEST_PAIR(kvmalloc)
@@ -377,7 +371,7 @@ static const char * const test_strs[] = {
for (i = 0; i < ARRAY_SIZE(test_strs); i++) { \
len = strlen(test_strs[i]); \
KUNIT_EXPECT_EQ(test, __builtin_constant_p(len), 0); \
- checker(len, kmemdup_array(test_strs[i], len, 1, gfp), \
+ checker(len, kmemdup_array(test_strs[i], 1, len, gfp), \
kfree(p)); \
checker(len, kmemdup(test_strs[i], len, gfp), \
kfree(p)); \
@@ -913,10 +907,9 @@ static void fortify_test_##memfunc(struct kunit *test) \
memfunc(zero.buf, srcB, 0 + unconst); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); \
- /* We currently explicitly ignore zero-sized dests. */ \
memfunc(zero.buf, srcB, 1 + unconst); \
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0); \
- KUNIT_EXPECT_EQ(test, fortify_write_overflows, 0); \
+ KUNIT_EXPECT_EQ(test, fortify_write_overflows, 1); \
}
__fortify_test(memcpy)
__fortify_test(memmove)
@@ -990,7 +983,7 @@ static void fortify_test_memcmp(struct kunit *test)
KUNIT_ASSERT_EQ(test, memcmp(one, two, one_len), 0);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
/* Still in bounds, but no longer matching. */
- KUNIT_ASSERT_EQ(test, memcmp(one, two, one_len + 1), -32);
+ KUNIT_ASSERT_LT(test, memcmp(one, two, one_len + 1), 0);
KUNIT_EXPECT_EQ(test, fortify_read_overflows, 0);
/* Catch too-large ranges. */
@@ -1099,4 +1092,5 @@ static struct kunit_suite fortify_test_suite = {
kunit_test_suite(fortify_test_suite);
+MODULE_DESCRIPTION("Runtime test cases for CONFIG_FORTIFY_SOURCE");
MODULE_LICENSE("GPL");
diff --git a/lib/generic-radix-tree.c b/lib/generic-radix-tree.c
index aaefb9b678c8..79e067b51488 100644
--- a/lib/generic-radix-tree.c
+++ b/lib/generic-radix-tree.c
@@ -5,99 +5,31 @@
#include <linux/gfp.h>
#include <linux/kmemleak.h>
-#define GENRADIX_ARY (GENRADIX_NODE_SIZE / sizeof(struct genradix_node *))
-#define GENRADIX_ARY_SHIFT ilog2(GENRADIX_ARY)
-
-struct genradix_node {
- union {
- /* Interior node: */
- struct genradix_node *children[GENRADIX_ARY];
-
- /* Leaf: */
- u8 data[GENRADIX_NODE_SIZE];
- };
-};
-
-static inline int genradix_depth_shift(unsigned depth)
-{
- return GENRADIX_NODE_SHIFT + GENRADIX_ARY_SHIFT * depth;
-}
-
-/*
- * Returns size (of data, in bytes) that a tree of a given depth holds:
- */
-static inline size_t genradix_depth_size(unsigned depth)
-{
- return 1UL << genradix_depth_shift(depth);
-}
-
-/* depth that's needed for a genradix that can address up to ULONG_MAX: */
-#define GENRADIX_MAX_DEPTH \
- DIV_ROUND_UP(BITS_PER_LONG - GENRADIX_NODE_SHIFT, GENRADIX_ARY_SHIFT)
-
-#define GENRADIX_DEPTH_MASK \
- ((unsigned long) (roundup_pow_of_two(GENRADIX_MAX_DEPTH + 1) - 1))
-
-static inline unsigned genradix_root_to_depth(struct genradix_root *r)
-{
- return (unsigned long) r & GENRADIX_DEPTH_MASK;
-}
-
-static inline struct genradix_node *genradix_root_to_node(struct genradix_root *r)
-{
- return (void *) ((unsigned long) r & ~GENRADIX_DEPTH_MASK);
-}
-
/*
* Returns pointer to the specified byte @offset within @radix, or NULL if not
* allocated
*/
void *__genradix_ptr(struct __genradix *radix, size_t offset)
{
- struct genradix_root *r = READ_ONCE(radix->root);
- struct genradix_node *n = genradix_root_to_node(r);
- unsigned level = genradix_root_to_depth(r);
-
- if (ilog2(offset) >= genradix_depth_shift(level))
- return NULL;
-
- while (1) {
- if (!n)
- return NULL;
- if (!level)
- break;
-
- level--;
-
- n = n->children[offset >> genradix_depth_shift(level)];
- offset &= genradix_depth_size(level) - 1;
- }
-
- return &n->data[offset];
+ return __genradix_ptr_inlined(radix, offset);
}
EXPORT_SYMBOL(__genradix_ptr);
-static inline struct genradix_node *genradix_alloc_node(gfp_t gfp_mask)
-{
- return kzalloc(GENRADIX_NODE_SIZE, gfp_mask);
-}
-
-static inline void genradix_free_node(struct genradix_node *node)
-{
- kfree(node);
-}
-
/*
* Returns pointer to the specified byte @offset within @radix, allocating it if
* necessary - newly allocated slots are always zeroed out:
*/
void *__genradix_ptr_alloc(struct __genradix *radix, size_t offset,
+ struct genradix_node **preallocated,
gfp_t gfp_mask)
{
struct genradix_root *v = READ_ONCE(radix->root);
struct genradix_node *n, *new_node = NULL;
unsigned level;
+ if (preallocated)
+ swap(new_node, *preallocated);
+
/* Increase tree depth if necessary: */
while (1) {
struct genradix_root *r = v, *new_root;
@@ -121,6 +53,8 @@ void *__genradix_ptr_alloc(struct __genradix *radix, size_t offset,
if ((v = cmpxchg_release(&radix->root, r, new_root)) == r) {
v = new_root;
new_node = NULL;
+ } else {
+ new_node->children[0] = NULL;
}
}
@@ -279,7 +213,7 @@ int __genradix_prealloc(struct __genradix *radix, size_t size,
size_t offset;
for (offset = 0; offset < size; offset += GENRADIX_NODE_SIZE)
- if (!__genradix_ptr_alloc(radix, offset, gfp_mask))
+ if (!__genradix_ptr_alloc(radix, offset, NULL, gfp_mask))
return -ENOMEM;
return 0;
diff --git a/lib/glob.c b/lib/glob.c
index 15b73f490720..aa57900d2062 100644
--- a/lib/glob.c
+++ b/lib/glob.c
@@ -68,6 +68,8 @@ bool __pure glob_match(char const *pat, char const *str)
back_str = --str; /* Allow zero-length match */
break;
case '[': { /* Character class */
+ if (c == '\0') /* No possible match */
+ return false;
bool match = false, inverted = (*pat == '!');
char const *class = pat + inverted;
unsigned char a = *class++;
diff --git a/lib/hashtable_test.c b/lib/hashtable_test.c
index 1d1b3288dee2..3521de6bad15 100644
--- a/lib/hashtable_test.c
+++ b/lib/hashtable_test.c
@@ -314,4 +314,5 @@ static struct kunit_suite hashtable_test_module = {
kunit_test_suites(&hashtable_test_module);
+MODULE_DESCRIPTION("KUnit test for the Kernel Hashtable structures");
MODULE_LICENSE("GPL");
diff --git a/lib/hexdump.c b/lib/hexdump.c
index 06833d404398..c3db7c3a7643 100644
--- a/lib/hexdump.c
+++ b/lib/hexdump.c
@@ -9,7 +9,7 @@
#include <linux/kernel.h>
#include <linux/minmax.h>
#include <linux/export.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
const char hex_asc[] = "0123456789abcdef";
EXPORT_SYMBOL(hex_asc);
diff --git a/lib/iomap_copy.c b/lib/iomap_copy.c
index 5de7c04e05ef..2fd5712fb7c0 100644
--- a/lib/iomap_copy.c
+++ b/lib/iomap_copy.c
@@ -16,9 +16,8 @@
* time. Order of access is not guaranteed, nor is a memory barrier
* performed afterwards.
*/
-void __attribute__((weak)) __iowrite32_copy(void __iomem *to,
- const void *from,
- size_t count)
+#ifndef __iowrite32_copy
+void __iowrite32_copy(void __iomem *to, const void *from, size_t count)
{
u32 __iomem *dst = to;
const u32 *src = from;
@@ -28,6 +27,7 @@ void __attribute__((weak)) __iowrite32_copy(void __iomem *to,
__raw_writel(*src++, dst++);
}
EXPORT_SYMBOL_GPL(__iowrite32_copy);
+#endif
/**
* __ioread32_copy - copy data from MMIO space, in 32-bit units
@@ -60,9 +60,8 @@ EXPORT_SYMBOL_GPL(__ioread32_copy);
* time. Order of access is not guaranteed, nor is a memory barrier
* performed afterwards.
*/
-void __attribute__((weak)) __iowrite64_copy(void __iomem *to,
- const void *from,
- size_t count)
+#ifndef __iowrite64_copy
+void __iowrite64_copy(void __iomem *to, const void *from, size_t count)
{
#ifdef CONFIG_64BIT
u64 __iomem *dst = to;
@@ -75,5 +74,5 @@ void __attribute__((weak)) __iowrite64_copy(void __iomem *to,
__iowrite32_copy(to, from, count * 2);
#endif
}
-
EXPORT_SYMBOL_GPL(__iowrite64_copy);
+#endif
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index 4a6a9f419bd7..1abb32c0da50 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -527,6 +527,39 @@ static void iov_iter_iovec_advance(struct iov_iter *i, size_t size)
i->__iov = iov;
}
+static void iov_iter_folioq_advance(struct iov_iter *i, size_t size)
+{
+ const struct folio_queue *folioq = i->folioq;
+ unsigned int slot = i->folioq_slot;
+
+ if (!i->count)
+ return;
+ i->count -= size;
+
+ if (slot >= folioq_nr_slots(folioq)) {
+ folioq = folioq->next;
+ slot = 0;
+ }
+
+ size += i->iov_offset; /* From beginning of current segment. */
+ do {
+ size_t fsize = folioq_folio_size(folioq, slot);
+
+ if (likely(size < fsize))
+ break;
+ size -= fsize;
+ slot++;
+ if (slot >= folioq_nr_slots(folioq) && folioq->next) {
+ folioq = folioq->next;
+ slot = 0;
+ }
+ } while (size);
+
+ i->iov_offset = size;
+ i->folioq_slot = slot;
+ i->folioq = folioq;
+}
+
void iov_iter_advance(struct iov_iter *i, size_t size)
{
if (unlikely(i->count < size))
@@ -539,12 +572,40 @@ void iov_iter_advance(struct iov_iter *i, size_t size)
iov_iter_iovec_advance(i, size);
} else if (iov_iter_is_bvec(i)) {
iov_iter_bvec_advance(i, size);
+ } else if (iov_iter_is_folioq(i)) {
+ iov_iter_folioq_advance(i, size);
} else if (iov_iter_is_discard(i)) {
i->count -= size;
}
}
EXPORT_SYMBOL(iov_iter_advance);
+static void iov_iter_folioq_revert(struct iov_iter *i, size_t unroll)
+{
+ const struct folio_queue *folioq = i->folioq;
+ unsigned int slot = i->folioq_slot;
+
+ for (;;) {
+ size_t fsize;
+
+ if (slot == 0) {
+ folioq = folioq->prev;
+ slot = folioq_nr_slots(folioq);
+ }
+ slot--;
+
+ fsize = folioq_folio_size(folioq, slot);
+ if (unroll <= fsize) {
+ i->iov_offset = fsize - unroll;
+ break;
+ }
+ unroll -= fsize;
+ }
+
+ i->folioq_slot = slot;
+ i->folioq = folioq;
+}
+
void iov_iter_revert(struct iov_iter *i, size_t unroll)
{
if (!unroll)
@@ -576,6 +637,9 @@ void iov_iter_revert(struct iov_iter *i, size_t unroll)
}
unroll -= n;
}
+ } else if (iov_iter_is_folioq(i)) {
+ i->iov_offset = 0;
+ iov_iter_folioq_revert(i, unroll);
} else { /* same logics for iovec and kvec */
const struct iovec *iov = iter_iov(i);
while (1) {
@@ -603,6 +667,9 @@ size_t iov_iter_single_seg_count(const struct iov_iter *i)
if (iov_iter_is_bvec(i))
return min(i->count, i->bvec->bv_len - i->iov_offset);
}
+ if (unlikely(iov_iter_is_folioq(i)))
+ return !i->count ? 0 :
+ umin(folioq_folio_size(i->folioq, i->folioq_slot), i->count);
return i->count;
}
EXPORT_SYMBOL(iov_iter_single_seg_count);
@@ -640,6 +707,36 @@ void iov_iter_bvec(struct iov_iter *i, unsigned int direction,
EXPORT_SYMBOL(iov_iter_bvec);
/**
+ * iov_iter_folio_queue - Initialise an I/O iterator to use the folios in a folio queue
+ * @i: The iterator to initialise.
+ * @direction: The direction of the transfer.
+ * @folioq: The starting point in the folio queue.
+ * @first_slot: The first slot in the folio queue to use
+ * @offset: The offset into the folio in the first slot to start at
+ * @count: The size of the I/O buffer in bytes.
+ *
+ * Set up an I/O iterator to either draw data out of the pages attached to an
+ * inode or to inject data into those pages. The pages *must* be prevented
+ * from evaporation, either by taking a ref on them or locking them by the
+ * caller.
+ */
+void iov_iter_folio_queue(struct iov_iter *i, unsigned int direction,
+ const struct folio_queue *folioq, unsigned int first_slot,
+ unsigned int offset, size_t count)
+{
+ BUG_ON(direction & ~1);
+ *i = (struct iov_iter) {
+ .iter_type = ITER_FOLIOQ,
+ .data_source = direction,
+ .folioq = folioq,
+ .folioq_slot = first_slot,
+ .count = count,
+ .iov_offset = offset,
+ };
+}
+EXPORT_SYMBOL(iov_iter_folio_queue);
+
+/**
* iov_iter_xarray - Initialise an I/O iterator to use the pages in an xarray
* @i: The iterator to initialise.
* @direction: The direction of the transfer.
@@ -765,12 +862,19 @@ bool iov_iter_is_aligned(const struct iov_iter *i, unsigned addr_mask,
if (iov_iter_is_bvec(i))
return iov_iter_aligned_bvec(i, addr_mask, len_mask);
+ /* With both xarray and folioq types, we're dealing with whole folios. */
if (iov_iter_is_xarray(i)) {
if (i->count & len_mask)
return false;
if ((i->xarray_start + i->iov_offset) & addr_mask)
return false;
}
+ if (iov_iter_is_folioq(i)) {
+ if (i->count & len_mask)
+ return false;
+ if (i->iov_offset & addr_mask)
+ return false;
+ }
return true;
}
@@ -835,6 +939,9 @@ unsigned long iov_iter_alignment(const struct iov_iter *i)
if (iov_iter_is_bvec(i))
return iov_iter_alignment_bvec(i);
+ /* With both xarray and folioq types, we're dealing with whole folios. */
+ if (iov_iter_is_folioq(i))
+ return i->iov_offset | i->count;
if (iov_iter_is_xarray(i))
return (i->xarray_start + i->iov_offset) | i->count;
@@ -887,6 +994,62 @@ static int want_pages_array(struct page ***res, size_t size,
return count;
}
+static ssize_t iter_folioq_get_pages(struct iov_iter *iter,
+ struct page ***ppages, size_t maxsize,
+ unsigned maxpages, size_t *_start_offset)
+{
+ const struct folio_queue *folioq = iter->folioq;
+ struct page **pages;
+ unsigned int slot = iter->folioq_slot;
+ size_t extracted = 0, count = iter->count, iov_offset = iter->iov_offset;
+
+ if (slot >= folioq_nr_slots(folioq)) {
+ folioq = folioq->next;
+ slot = 0;
+ if (WARN_ON(iov_offset != 0))
+ return -EIO;
+ }
+
+ maxpages = want_pages_array(ppages, maxsize, iov_offset & ~PAGE_MASK, maxpages);
+ if (!maxpages)
+ return -ENOMEM;
+ *_start_offset = iov_offset & ~PAGE_MASK;
+ pages = *ppages;
+
+ for (;;) {
+ struct folio *folio = folioq_folio(folioq, slot);
+ size_t offset = iov_offset, fsize = folioq_folio_size(folioq, slot);
+ size_t part = PAGE_SIZE - offset % PAGE_SIZE;
+
+ part = umin(part, umin(maxsize - extracted, fsize - offset));
+ count -= part;
+ iov_offset += part;
+ extracted += part;
+
+ *pages = folio_page(folio, offset / PAGE_SIZE);
+ get_page(*pages);
+ pages++;
+ maxpages--;
+ if (maxpages == 0 || extracted >= maxsize)
+ break;
+
+ if (iov_offset >= fsize) {
+ iov_offset = 0;
+ slot++;
+ if (slot == folioq_nr_slots(folioq) && folioq->next) {
+ folioq = folioq->next;
+ slot = 0;
+ }
+ }
+ }
+
+ iter->count = count;
+ iter->iov_offset = iov_offset;
+ iter->folioq = folioq;
+ iter->folioq_slot = slot;
+ return extracted;
+}
+
static ssize_t iter_xarray_populate_pages(struct page **pages, struct xarray *xa,
pgoff_t index, unsigned int nr_pages)
{
@@ -1034,6 +1197,8 @@ static ssize_t __iov_iter_get_pages_alloc(struct iov_iter *i,
}
return maxsize;
}
+ if (iov_iter_is_folioq(i))
+ return iter_folioq_get_pages(i, pages, maxsize, maxpages, start);
if (iov_iter_is_xarray(i))
return iter_xarray_get_pages(i, pages, maxsize, maxpages, start);
return -EFAULT;
@@ -1118,6 +1283,11 @@ int iov_iter_npages(const struct iov_iter *i, int maxpages)
return iov_npages(i, maxpages);
if (iov_iter_is_bvec(i))
return bvec_npages(i, maxpages);
+ if (iov_iter_is_folioq(i)) {
+ unsigned offset = i->iov_offset % PAGE_SIZE;
+ int npages = DIV_ROUND_UP(offset + i->count, PAGE_SIZE);
+ return min(npages, maxpages);
+ }
if (iov_iter_is_xarray(i)) {
unsigned offset = (i->xarray_start + i->iov_offset) % PAGE_SIZE;
int npages = DIV_ROUND_UP(offset + i->count, PAGE_SIZE);
@@ -1399,6 +1569,68 @@ void iov_iter_restore(struct iov_iter *i, struct iov_iter_state *state)
}
/*
+ * Extract a list of contiguous pages from an ITER_FOLIOQ iterator. This does
+ * not get references on the pages, nor does it get a pin on them.
+ */
+static ssize_t iov_iter_extract_folioq_pages(struct iov_iter *i,
+ struct page ***pages, size_t maxsize,
+ unsigned int maxpages,
+ iov_iter_extraction_t extraction_flags,
+ size_t *offset0)
+{
+ const struct folio_queue *folioq = i->folioq;
+ struct page **p;
+ unsigned int nr = 0;
+ size_t extracted = 0, offset, slot = i->folioq_slot;
+
+ if (slot >= folioq_nr_slots(folioq)) {
+ folioq = folioq->next;
+ slot = 0;
+ if (WARN_ON(i->iov_offset != 0))
+ return -EIO;
+ }
+
+ offset = i->iov_offset & ~PAGE_MASK;
+ *offset0 = offset;
+
+ maxpages = want_pages_array(pages, maxsize, offset, maxpages);
+ if (!maxpages)
+ return -ENOMEM;
+ p = *pages;
+
+ for (;;) {
+ struct folio *folio = folioq_folio(folioq, slot);
+ size_t offset = i->iov_offset, fsize = folioq_folio_size(folioq, slot);
+ size_t part = PAGE_SIZE - offset % PAGE_SIZE;
+
+ if (offset < fsize) {
+ part = umin(part, umin(maxsize - extracted, fsize - offset));
+ i->count -= part;
+ i->iov_offset += part;
+ extracted += part;
+
+ p[nr++] = folio_page(folio, offset / PAGE_SIZE);
+ }
+
+ if (nr >= maxpages || extracted >= maxsize)
+ break;
+
+ if (i->iov_offset >= fsize) {
+ i->iov_offset = 0;
+ slot++;
+ if (slot == folioq_nr_slots(folioq) && folioq->next) {
+ folioq = folioq->next;
+ slot = 0;
+ }
+ }
+ }
+
+ i->folioq = folioq;
+ i->folioq_slot = slot;
+ return extracted;
+}
+
+/*
* Extract a list of contiguous pages from an ITER_XARRAY iterator. This does not
* get references on the pages, nor does it get a pin on them.
*/
@@ -1618,8 +1850,8 @@ static ssize_t iov_iter_extract_user_pages(struct iov_iter *i,
* added to the pages, but refs will not be taken.
* iov_iter_extract_will_pin() will return true.
*
- * (*) If the iterator is ITER_KVEC, ITER_BVEC or ITER_XARRAY, the pages are
- * merely listed; no extra refs or pins are obtained.
+ * (*) If the iterator is ITER_KVEC, ITER_BVEC, ITER_FOLIOQ or ITER_XARRAY, the
+ * pages are merely listed; no extra refs or pins are obtained.
* iov_iter_extract_will_pin() will return 0.
*
* Note also:
@@ -1654,6 +1886,10 @@ ssize_t iov_iter_extract_pages(struct iov_iter *i,
return iov_iter_extract_bvec_pages(i, pages, maxsize,
maxpages, extraction_flags,
offset0);
+ if (iov_iter_is_folioq(i))
+ return iov_iter_extract_folioq_pages(i, pages, maxsize,
+ maxpages, extraction_flags,
+ offset0);
if (iov_iter_is_xarray(i))
return iov_iter_extract_xarray_pages(i, pages, maxsize,
maxpages, extraction_flags,
diff --git a/lib/irq_poll.c b/lib/irq_poll.c
index 2d5329a42105..08b242bbdbdf 100644
--- a/lib/irq_poll.c
+++ b/lib/irq_poll.c
@@ -75,7 +75,7 @@ void irq_poll_complete(struct irq_poll *iop)
}
EXPORT_SYMBOL(irq_poll_complete);
-static void __latent_entropy irq_poll_softirq(struct softirq_action *h)
+static void __latent_entropy irq_poll_softirq(void)
{
struct list_head *list = this_cpu_ptr(&blk_cpu_iopoll);
int rearm = 0, budget = irq_poll_budget;
diff --git a/lib/is_signed_type_kunit.c b/lib/is_signed_type_kunit.c
index 0a7f6ae62839..88adbe813f3a 100644
--- a/lib/is_signed_type_kunit.c
+++ b/lib/is_signed_type_kunit.c
@@ -46,4 +46,5 @@ static struct kunit_suite is_signed_type_test_suite = {
kunit_test_suite(is_signed_type_test_suite);
+MODULE_DESCRIPTION("is_signed_type() KUnit test suite");
MODULE_LICENSE("Dual MIT/GPL");
diff --git a/lib/kfifo.c b/lib/kfifo.c
index 12f5a347aa13..a8b2eed90599 100644
--- a/lib/kfifo.c
+++ b/lib/kfifo.c
@@ -5,13 +5,14 @@
* Copyright (C) 2009/2010 Stefani Seibold <stefani@seibold.net>
*/
-#include <linux/kernel.h>
-#include <linux/export.h>
-#include <linux/slab.h>
+#include <linux/dma-mapping.h>
#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/kfifo.h>
#include <linux/log2.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <linux/uaccess.h>
-#include <linux/kfifo.h>
/*
* internal helper to calculate the unused elements in a fifo
@@ -163,6 +164,19 @@ unsigned int __kfifo_out_peek(struct __kfifo *fifo,
}
EXPORT_SYMBOL(__kfifo_out_peek);
+unsigned int __kfifo_out_linear(struct __kfifo *fifo,
+ unsigned int *tail, unsigned int n)
+{
+ unsigned int size = fifo->mask + 1;
+ unsigned int off = fifo->out & fifo->mask;
+
+ if (tail)
+ *tail = off;
+
+ return min3(n, fifo->in - fifo->out, size - off);
+}
+EXPORT_SYMBOL(__kfifo_out_linear);
+
unsigned int __kfifo_out(struct __kfifo *fifo,
void *buf, unsigned int len)
{
@@ -292,51 +306,31 @@ int __kfifo_to_user(struct __kfifo *fifo, void __user *to,
}
EXPORT_SYMBOL(__kfifo_to_user);
-static int setup_sgl_buf(struct scatterlist *sgl, void *buf,
- int nents, unsigned int len)
+static unsigned int setup_sgl_buf(struct __kfifo *fifo, struct scatterlist *sgl,
+ unsigned int data_offset, int nents,
+ unsigned int len, dma_addr_t dma)
{
- int n;
- unsigned int l;
- unsigned int off;
- struct page *page;
+ const void *buf = fifo->data + data_offset;
- if (!nents)
+ if (!nents || !len)
return 0;
- if (!len)
- return 0;
+ sg_set_buf(sgl, buf, len);
- n = 0;
- page = virt_to_page(buf);
- off = offset_in_page(buf);
- l = 0;
-
- while (len >= l + PAGE_SIZE - off) {
- struct page *npage;
-
- l += PAGE_SIZE;
- buf += PAGE_SIZE;
- npage = virt_to_page(buf);
- if (page_to_phys(page) != page_to_phys(npage) - l) {
- sg_set_page(sgl, page, l - off, off);
- sgl = sg_next(sgl);
- if (++n == nents || sgl == NULL)
- return n;
- page = npage;
- len -= l - off;
- l = off = 0;
- }
+ if (dma != DMA_MAPPING_ERROR) {
+ sg_dma_address(sgl) = dma + data_offset;
+ sg_dma_len(sgl) = len;
}
- sg_set_page(sgl, page, len, off);
- return n + 1;
+
+ return 1;
}
static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl,
- int nents, unsigned int len, unsigned int off)
+ int nents, unsigned int len, unsigned int off, dma_addr_t dma)
{
unsigned int size = fifo->mask + 1;
unsigned int esize = fifo->esize;
- unsigned int l;
+ unsigned int len_to_end;
unsigned int n;
off &= fifo->mask;
@@ -345,16 +339,17 @@ static unsigned int setup_sgl(struct __kfifo *fifo, struct scatterlist *sgl,
size *= esize;
len *= esize;
}
- l = min(len, size - off);
+ len_to_end = min(len, size - off);
- n = setup_sgl_buf(sgl, fifo->data + off, nents, l);
- n += setup_sgl_buf(sgl + n, fifo->data, nents - n, len - l);
+ n = setup_sgl_buf(fifo, sgl, off, nents, len_to_end, dma);
+ n += setup_sgl_buf(fifo, sgl + n, 0, nents - n, len - len_to_end, dma);
return n;
}
unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo,
- struct scatterlist *sgl, int nents, unsigned int len)
+ struct scatterlist *sgl, int nents, unsigned int len,
+ dma_addr_t dma)
{
unsigned int l;
@@ -362,12 +357,13 @@ unsigned int __kfifo_dma_in_prepare(struct __kfifo *fifo,
if (len > l)
len = l;
- return setup_sgl(fifo, sgl, nents, len, fifo->in);
+ return setup_sgl(fifo, sgl, nents, len, fifo->in, dma);
}
EXPORT_SYMBOL(__kfifo_dma_in_prepare);
unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo,
- struct scatterlist *sgl, int nents, unsigned int len)
+ struct scatterlist *sgl, int nents, unsigned int len,
+ dma_addr_t dma)
{
unsigned int l;
@@ -375,7 +371,7 @@ unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo,
if (len > l)
len = l;
- return setup_sgl(fifo, sgl, nents, len, fifo->out);
+ return setup_sgl(fifo, sgl, nents, len, fifo->out, dma);
}
EXPORT_SYMBOL(__kfifo_dma_out_prepare);
@@ -473,6 +469,19 @@ unsigned int __kfifo_out_peek_r(struct __kfifo *fifo, void *buf,
}
EXPORT_SYMBOL(__kfifo_out_peek_r);
+unsigned int __kfifo_out_linear_r(struct __kfifo *fifo,
+ unsigned int *tail, unsigned int n, size_t recsize)
+{
+ if (fifo->in == fifo->out)
+ return 0;
+
+ if (tail)
+ *tail = fifo->out + recsize;
+
+ return min(n, __kfifo_peek_n(fifo, recsize));
+}
+EXPORT_SYMBOL(__kfifo_out_linear_r);
+
unsigned int __kfifo_out_r(struct __kfifo *fifo, void *buf,
unsigned int len, size_t recsize)
{
@@ -546,7 +555,8 @@ int __kfifo_to_user_r(struct __kfifo *fifo, void __user *to,
EXPORT_SYMBOL(__kfifo_to_user_r);
unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo,
- struct scatterlist *sgl, int nents, unsigned int len, size_t recsize)
+ struct scatterlist *sgl, int nents, unsigned int len, size_t recsize,
+ dma_addr_t dma)
{
BUG_ON(!nents);
@@ -555,7 +565,7 @@ unsigned int __kfifo_dma_in_prepare_r(struct __kfifo *fifo,
if (len + recsize > kfifo_unused(fifo))
return 0;
- return setup_sgl(fifo, sgl, nents, len, fifo->in + recsize);
+ return setup_sgl(fifo, sgl, nents, len, fifo->in + recsize, dma);
}
EXPORT_SYMBOL(__kfifo_dma_in_prepare_r);
@@ -569,7 +579,8 @@ void __kfifo_dma_in_finish_r(struct __kfifo *fifo,
EXPORT_SYMBOL(__kfifo_dma_in_finish_r);
unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo,
- struct scatterlist *sgl, int nents, unsigned int len, size_t recsize)
+ struct scatterlist *sgl, int nents, unsigned int len, size_t recsize,
+ dma_addr_t dma)
{
BUG_ON(!nents);
@@ -578,15 +589,7 @@ unsigned int __kfifo_dma_out_prepare_r(struct __kfifo *fifo,
if (len + recsize > fifo->in - fifo->out)
return 0;
- return setup_sgl(fifo, sgl, nents, len, fifo->out + recsize);
+ return setup_sgl(fifo, sgl, nents, len, fifo->out + recsize, dma);
}
EXPORT_SYMBOL(__kfifo_dma_out_prepare_r);
-void __kfifo_dma_out_finish_r(struct __kfifo *fifo, size_t recsize)
-{
- unsigned int len;
-
- len = __kfifo_peek_n(fifo, recsize);
- fifo->out += len + recsize;
-}
-EXPORT_SYMBOL(__kfifo_dma_out_finish_r);
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
index 03b427e2707e..b7f2fa08d9c8 100644
--- a/lib/kobject_uevent.c
+++ b/lib/kobject_uevent.c
@@ -433,8 +433,23 @@ static void zap_modalias_env(struct kobj_uevent_env *env)
len = strlen(env->envp[i]) + 1;
if (i != env->envp_idx - 1) {
+ /* @env->envp[] contains pointers to @env->buf[]
+ * with @env->buflen chars, and we are removing
+ * variable MODALIAS here pointed by @env->envp[i]
+ * with length @len as shown below:
+ *
+ * 0 @env->buf[] @env->buflen
+ * ---------------------------------------------
+ * ^ ^ ^ ^
+ * | |-> @len <-| target block |
+ * @env->envp[0] @env->envp[i] @env->envp[i + 1]
+ *
+ * so the "target block" indicated above is moved
+ * backward by @len, and its right size is
+ * @env->buflen - (@env->envp[i + 1] - @env->envp[0]).
+ */
memmove(env->envp[i], env->envp[i + 1],
- env->buflen - len);
+ env->buflen - (env->envp[i + 1] - env->envp[0]));
for (j = i; j < env->envp_idx - 1; j++)
env->envp[j] = env->envp[j + 1] - len;
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 309659a32a78..5aa51978e456 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -2,13 +2,15 @@ obj-$(CONFIG_KUNIT) += kunit.o
kunit-objs += test.o \
resource.o \
+ user_alloc.o \
static_stub.o \
string-stream.o \
assert.o \
try-catch.o \
executor.o \
attributes.o \
- device.o
+ device.o \
+ platform.o
ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
kunit-objs += debugfs.o
@@ -18,10 +20,12 @@ endif
obj-y += hooks.o
obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
+obj-$(CONFIG_KUNIT_TEST) += platform-test.o
# string-stream-test compiles built-in only.
ifeq ($(CONFIG_KUNIT_TEST),y)
obj-$(CONFIG_KUNIT_TEST) += string-stream-test.o
+obj-$(CONFIG_KUNIT_TEST) += assert_test.o
endif
obj-$(CONFIG_KUNIT_EXAMPLE_TEST) += kunit-example-test.o
diff --git a/lib/kunit/assert.c b/lib/kunit/assert.c
index dd1d633d0fe2..867aa5c4bccf 100644
--- a/lib/kunit/assert.c
+++ b/lib/kunit/assert.c
@@ -7,6 +7,7 @@
*/
#include <kunit/assert.h>
#include <kunit/test.h>
+#include <kunit/visibility.h>
#include "string-stream.h"
@@ -30,8 +31,9 @@ void kunit_assert_prologue(const struct kunit_loc *loc,
}
EXPORT_SYMBOL_GPL(kunit_assert_prologue);
-static void kunit_assert_print_msg(const struct va_format *message,
- struct string_stream *stream)
+VISIBLE_IF_KUNIT
+void kunit_assert_print_msg(const struct va_format *message,
+ struct string_stream *stream)
{
if (message->fmt)
string_stream_add(stream, "\n%pV", message);
@@ -89,7 +91,7 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert,
EXPORT_SYMBOL_GPL(kunit_ptr_not_err_assert_format);
/* Checks if `text` is a literal representing `value`, e.g. "5" and 5 */
-static bool is_literal(const char *text, long long value)
+VISIBLE_IF_KUNIT bool is_literal(const char *text, long long value)
{
char *buffer;
int len;
@@ -166,7 +168,7 @@ EXPORT_SYMBOL_GPL(kunit_binary_ptr_assert_format);
/* Checks if KUNIT_EXPECT_STREQ() args were string literals.
* Note: `text` will have ""s where as `value` will not.
*/
-static bool is_str_literal(const char *text, const char *value)
+VISIBLE_IF_KUNIT bool is_str_literal(const char *text, const char *value)
{
int len;
@@ -208,10 +210,11 @@ EXPORT_SYMBOL_GPL(kunit_binary_str_assert_format);
/* Adds a hexdump of a buffer to a string_stream comparing it with
* a second buffer. The different bytes are marked with <>.
*/
-static void kunit_assert_hexdump(struct string_stream *stream,
- const void *buf,
- const void *compared_buf,
- const size_t len)
+VISIBLE_IF_KUNIT
+void kunit_assert_hexdump(struct string_stream *stream,
+ const void *buf,
+ const void *compared_buf,
+ const size_t len)
{
size_t i;
const u8 *buf1 = buf;
diff --git a/lib/kunit/assert_test.c b/lib/kunit/assert_test.c
new file mode 100644
index 000000000000..4a5967712186
--- /dev/null
+++ b/lib/kunit/assert_test.c
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * KUnit test for the assertion formatting functions.
+ * Author: Ivan Orlov <ivan.orlov0322@gmail.com>
+ */
+#include <kunit/test.h>
+#include "string-stream.h"
+
+#define TEST_PTR_EXPECTED_BUF_SIZE 32
+#define HEXDUMP_TEST_BUF_LEN 5
+#define ASSERT_TEST_EXPECT_CONTAIN(test, str, substr) KUNIT_EXPECT_TRUE(test, strstr(str, substr))
+#define ASSERT_TEST_EXPECT_NCONTAIN(test, str, substr) KUNIT_EXPECT_FALSE(test, strstr(str, substr))
+
+static void kunit_test_is_literal(struct kunit *test)
+{
+ KUNIT_EXPECT_TRUE(test, is_literal("5", 5));
+ KUNIT_EXPECT_TRUE(test, is_literal("0", 0));
+ KUNIT_EXPECT_TRUE(test, is_literal("1234567890", 1234567890));
+ KUNIT_EXPECT_TRUE(test, is_literal("-1234567890", -1234567890));
+ KUNIT_EXPECT_FALSE(test, is_literal("05", 5));
+ KUNIT_EXPECT_FALSE(test, is_literal("", 0));
+ KUNIT_EXPECT_FALSE(test, is_literal("-0", 0));
+ KUNIT_EXPECT_FALSE(test, is_literal("12#45", 1245));
+}
+
+static void kunit_test_is_str_literal(struct kunit *test)
+{
+ KUNIT_EXPECT_TRUE(test, is_str_literal("\"Hello, World!\"", "Hello, World!"));
+ KUNIT_EXPECT_TRUE(test, is_str_literal("\"\"", ""));
+ KUNIT_EXPECT_TRUE(test, is_str_literal("\"\"\"", "\""));
+ KUNIT_EXPECT_FALSE(test, is_str_literal("", ""));
+ KUNIT_EXPECT_FALSE(test, is_str_literal("\"", "\""));
+ KUNIT_EXPECT_FALSE(test, is_str_literal("\"Abacaba", "Abacaba"));
+ KUNIT_EXPECT_FALSE(test, is_str_literal("Abacaba\"", "Abacaba"));
+ KUNIT_EXPECT_FALSE(test, is_str_literal("\"Abacaba\"", "\"Abacaba\""));
+}
+
+KUNIT_DEFINE_ACTION_WRAPPER(kfree_wrapper, kfree, const void *);
+
+/* this function is used to get a "char *" string from the string stream and defer its cleanup */
+static char *get_str_from_stream(struct kunit *test, struct string_stream *stream)
+{
+ char *str = string_stream_get_string(stream);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, str);
+ kunit_add_action(test, kfree_wrapper, (void *)str);
+
+ return str;
+}
+
+static void kunit_test_assert_prologue(struct kunit *test)
+{
+ struct string_stream *stream;
+ char *str;
+ const struct kunit_loc location = {
+ .file = "testfile.c",
+ .line = 1337,
+ };
+
+ stream = kunit_alloc_string_stream(test, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+
+ /* Test an expectation fail prologue */
+ kunit_assert_prologue(&location, KUNIT_EXPECTATION, stream);
+ str = get_str_from_stream(test, stream);
+ ASSERT_TEST_EXPECT_CONTAIN(test, str, "EXPECTATION");
+ ASSERT_TEST_EXPECT_CONTAIN(test, str, "testfile.c");
+ ASSERT_TEST_EXPECT_CONTAIN(test, str, "1337");
+
+ /* Test an assertion fail prologue */
+ string_stream_clear(stream);
+ kunit_assert_prologue(&location, KUNIT_ASSERTION, stream);
+ str = get_str_from_stream(test, stream);
+ ASSERT_TEST_EXPECT_CONTAIN(test, str, "ASSERTION");
+ ASSERT_TEST_EXPECT_CONTAIN(test, str, "testfile.c");
+ ASSERT_TEST_EXPECT_CONTAIN(test, str, "1337");
+}
+
+/*
+ * This function accepts an arbitrary count of parameters and generates a va_format struct,
+ * which can be used to validate kunit_assert_print_msg function
+ */
+static void verify_assert_print_msg(struct kunit *test,
+ struct string_stream *stream,
+ char *expected, const char *format, ...)
+{
+ va_list list;
+ const struct va_format vformat = {
+ .fmt = format,
+ .va = &list,
+ };
+
+ va_start(list, format);
+ string_stream_clear(stream);
+ kunit_assert_print_msg(&vformat, stream);
+ KUNIT_EXPECT_STREQ(test, get_str_from_stream(test, stream), expected);
+}
+
+static void kunit_test_assert_print_msg(struct kunit *test)
+{
+ struct string_stream *stream;
+
+ stream = kunit_alloc_string_stream(test, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+
+ verify_assert_print_msg(test, stream, "\nTest", "Test");
+ verify_assert_print_msg(test, stream, "\nAbacaba -123 234", "%s %d %u",
+ "Abacaba", -123, 234U);
+ verify_assert_print_msg(test, stream, "", NULL);
+}
+
+/*
+ * Further code contains the tests for different assert format functions.
+ * This helper function accepts the assert format function, executes it and
+ * validates the result string from the stream by checking that all of the
+ * substrings exist in the output.
+ */
+static void validate_assert(assert_format_t format_func, struct kunit *test,
+ const struct kunit_assert *assert,
+ struct string_stream *stream, int num_checks, ...)
+{
+ size_t i;
+ va_list checks;
+ char *cur_substr_exp;
+ struct va_format message = { NULL, NULL };
+
+ va_start(checks, num_checks);
+ string_stream_clear(stream);
+ format_func(assert, &message, stream);
+
+ for (i = 0; i < num_checks; i++) {
+ cur_substr_exp = va_arg(checks, char *);
+ ASSERT_TEST_EXPECT_CONTAIN(test, get_str_from_stream(test, stream), cur_substr_exp);
+ }
+}
+
+static void kunit_test_unary_assert_format(struct kunit *test)
+{
+ struct string_stream *stream;
+ struct kunit_assert assert = {};
+ struct kunit_unary_assert un_assert = {
+ .assert = assert,
+ .condition = "expr",
+ .expected_true = true,
+ };
+
+ stream = kunit_alloc_string_stream(test, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+
+ validate_assert(kunit_unary_assert_format, test, &un_assert.assert,
+ stream, 2, "true", "is false");
+
+ un_assert.expected_true = false;
+ validate_assert(kunit_unary_assert_format, test, &un_assert.assert,
+ stream, 2, "false", "is true");
+}
+
+static void kunit_test_ptr_not_err_assert_format(struct kunit *test)
+{
+ struct string_stream *stream;
+ struct kunit_assert assert = {};
+ struct kunit_ptr_not_err_assert not_err_assert = {
+ .assert = assert,
+ .text = "expr",
+ .value = NULL,
+ };
+
+ stream = kunit_alloc_string_stream(test, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+
+ /* Value is NULL. The corresponding message should be printed out */
+ validate_assert(kunit_ptr_not_err_assert_format, test,
+ &not_err_assert.assert,
+ stream, 1, "null");
+
+ /* Value is not NULL, but looks like an error pointer. Error should be printed out */
+ not_err_assert.value = (void *)-12;
+ validate_assert(kunit_ptr_not_err_assert_format, test,
+ &not_err_assert.assert, stream, 2,
+ "error", "-12");
+}
+
+static void kunit_test_binary_assert_format(struct kunit *test)
+{
+ struct string_stream *stream;
+ struct kunit_assert assert = {};
+ struct kunit_binary_assert_text text = {
+ .left_text = "1 + 2",
+ .operation = "==",
+ .right_text = "2",
+ };
+ const struct kunit_binary_assert binary_assert = {
+ .assert = assert,
+ .text = &text,
+ .left_value = 3,
+ .right_value = 2,
+ };
+
+ stream = kunit_alloc_string_stream(test, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+
+ /*
+ * Printed values should depend on the input we provide: the left text, right text, left
+ * value and the right value.
+ */
+ validate_assert(kunit_binary_assert_format, test, &binary_assert.assert,
+ stream, 4, "1 + 2", "2", "3", "==");
+
+ text.right_text = "4 - 2";
+ validate_assert(kunit_binary_assert_format, test, &binary_assert.assert,
+ stream, 3, "==", "1 + 2", "4 - 2");
+
+ text.left_text = "3";
+ validate_assert(kunit_binary_assert_format, test, &binary_assert.assert,
+ stream, 4, "3", "4 - 2", "2", "==");
+
+ text.right_text = "2";
+ validate_assert(kunit_binary_assert_format, test, &binary_assert.assert,
+ stream, 3, "3", "2", "==");
+}
+
+static void kunit_test_binary_ptr_assert_format(struct kunit *test)
+{
+ struct string_stream *stream;
+ struct kunit_assert assert = {};
+ char *addr_var_a, *addr_var_b;
+ static const void *var_a = (void *)0xDEADBEEF;
+ static const void *var_b = (void *)0xBADDCAFE;
+ struct kunit_binary_assert_text text = {
+ .left_text = "var_a",
+ .operation = "==",
+ .right_text = "var_b",
+ };
+ struct kunit_binary_ptr_assert binary_ptr_assert = {
+ .assert = assert,
+ .text = &text,
+ .left_value = var_a,
+ .right_value = var_b,
+ };
+
+ addr_var_a = kunit_kzalloc(test, TEST_PTR_EXPECTED_BUF_SIZE, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, addr_var_a);
+ addr_var_b = kunit_kzalloc(test, TEST_PTR_EXPECTED_BUF_SIZE, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, addr_var_b);
+ /*
+ * Print the addresses to the buffers first.
+ * This is necessary as we may have different count of leading zeros in the pointer
+ * on different architectures.
+ */
+ snprintf(addr_var_a, TEST_PTR_EXPECTED_BUF_SIZE, "%px", var_a);
+ snprintf(addr_var_b, TEST_PTR_EXPECTED_BUF_SIZE, "%px", var_b);
+
+ stream = kunit_alloc_string_stream(test, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+ validate_assert(kunit_binary_ptr_assert_format, test, &binary_ptr_assert.assert,
+ stream, 3, addr_var_a, addr_var_b, "==");
+}
+
+static void kunit_test_binary_str_assert_format(struct kunit *test)
+{
+ struct string_stream *stream;
+ struct kunit_assert assert = {};
+ static const char *var_a = "abacaba";
+ static const char *var_b = "kernel";
+ struct kunit_binary_assert_text text = {
+ .left_text = "var_a",
+ .operation = "==",
+ .right_text = "var_b",
+ };
+ struct kunit_binary_str_assert binary_str_assert = {
+ .assert = assert,
+ .text = &text,
+ .left_value = var_a,
+ .right_value = var_b,
+ };
+
+ stream = kunit_alloc_string_stream(test, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+
+ validate_assert(kunit_binary_str_assert_format, test,
+ &binary_str_assert.assert,
+ stream, 5, "var_a", "var_b", "\"abacaba\"",
+ "\"kernel\"", "==");
+
+ text.left_text = "\"abacaba\"";
+ validate_assert(kunit_binary_str_assert_format, test, &binary_str_assert.assert,
+ stream, 4, "\"abacaba\"", "var_b", "\"kernel\"", "==");
+
+ text.right_text = "\"kernel\"";
+ validate_assert(kunit_binary_str_assert_format, test, &binary_str_assert.assert,
+ stream, 3, "\"abacaba\"", "\"kernel\"", "==");
+}
+
+static const u8 hex_testbuf1[] = { 0x26, 0x74, 0x6b, 0x9c, 0x55,
+ 0x45, 0x9d, 0x47, 0xd6, 0x47,
+ 0x2, 0x89, 0x8c, 0x81, 0x94,
+ 0x12, 0xfe, 0x01 };
+static const u8 hex_testbuf2[] = { 0x26, 0x74, 0x6b, 0x9c, 0x55,
+ 0x45, 0x9d, 0x47, 0x21, 0x47,
+ 0xcd, 0x89, 0x24, 0x50, 0x94,
+ 0x12, 0xba, 0x01 };
+static void kunit_test_assert_hexdump(struct kunit *test)
+{
+ struct string_stream *stream;
+ char *str;
+ size_t i;
+ char buf[HEXDUMP_TEST_BUF_LEN];
+
+ stream = kunit_alloc_string_stream(test, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+ /* Check that we are getting output like <xx> for non-matching numbers. */
+ kunit_assert_hexdump(stream, hex_testbuf1, hex_testbuf2, sizeof(hex_testbuf1));
+ str = get_str_from_stream(test, stream);
+ for (i = 0; i < sizeof(hex_testbuf1); i++) {
+ snprintf(buf, HEXDUMP_TEST_BUF_LEN, "<%02x>", hex_testbuf1[i]);
+ if (hex_testbuf1[i] != hex_testbuf2[i])
+ ASSERT_TEST_EXPECT_CONTAIN(test, str, buf);
+ }
+ /* We shouldn't get any <xx> numbers when comparing the buffer with itself. */
+ string_stream_clear(stream);
+ kunit_assert_hexdump(stream, hex_testbuf1, hex_testbuf1, sizeof(hex_testbuf1));
+ str = get_str_from_stream(test, stream);
+ ASSERT_TEST_EXPECT_NCONTAIN(test, str, "<");
+ ASSERT_TEST_EXPECT_NCONTAIN(test, str, ">");
+}
+
+static void kunit_test_mem_assert_format(struct kunit *test)
+{
+ struct string_stream *stream;
+ struct string_stream *expected_stream;
+ struct kunit_assert assert = {};
+ static const struct kunit_binary_assert_text text = {
+ .left_text = "hex_testbuf1",
+ .operation = "==",
+ .right_text = "hex_testbuf2",
+ };
+ struct kunit_mem_assert mem_assert = {
+ .assert = assert,
+ .text = &text,
+ .left_value = NULL,
+ .right_value = hex_testbuf2,
+ .size = sizeof(hex_testbuf1),
+ };
+
+ expected_stream = kunit_alloc_string_stream(test, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, expected_stream);
+ stream = kunit_alloc_string_stream(test, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+
+ /* The left value is NULL */
+ validate_assert(kunit_mem_assert_format, test, &mem_assert.assert,
+ stream, 2, "hex_testbuf1", "is not null");
+
+ /* The right value is NULL, the left value is not NULL */
+ mem_assert.left_value = hex_testbuf1;
+ mem_assert.right_value = NULL;
+ validate_assert(kunit_mem_assert_format, test, &mem_assert.assert,
+ stream, 2, "hex_testbuf2", "is not null");
+
+ /* Both arguments are not null */
+ mem_assert.left_value = hex_testbuf1;
+ mem_assert.right_value = hex_testbuf2;
+
+ validate_assert(kunit_mem_assert_format, test, &mem_assert.assert,
+ stream, 3, "hex_testbuf1", "hex_testbuf2", "==");
+}
+
+static struct kunit_case assert_test_cases[] = {
+ KUNIT_CASE(kunit_test_is_literal),
+ KUNIT_CASE(kunit_test_is_str_literal),
+ KUNIT_CASE(kunit_test_assert_prologue),
+ KUNIT_CASE(kunit_test_assert_print_msg),
+ KUNIT_CASE(kunit_test_unary_assert_format),
+ KUNIT_CASE(kunit_test_ptr_not_err_assert_format),
+ KUNIT_CASE(kunit_test_binary_assert_format),
+ KUNIT_CASE(kunit_test_binary_ptr_assert_format),
+ KUNIT_CASE(kunit_test_binary_str_assert_format),
+ KUNIT_CASE(kunit_test_assert_hexdump),
+ KUNIT_CASE(kunit_test_mem_assert_format),
+ {}
+};
+
+static struct kunit_suite assert_test_suite = {
+ .name = "kunit-assert",
+ .test_cases = assert_test_cases,
+};
+
+kunit_test_suites(&assert_test_suite);
diff --git a/lib/kunit/device.c b/lib/kunit/device.c
index 25c81ed465fb..520c1fccee8a 100644
--- a/lib/kunit/device.c
+++ b/lib/kunit/device.c
@@ -89,7 +89,7 @@ struct device_driver *kunit_driver_create(struct kunit *test, const char *name)
if (!driver)
return ERR_PTR(err);
- driver->name = name;
+ driver->name = kunit_kstrdup_const(test, name, GFP_KERNEL);
driver->bus = &kunit_bus_type;
driver->owner = THIS_MODULE;
@@ -192,8 +192,11 @@ void kunit_device_unregister(struct kunit *test, struct device *dev)
const struct device_driver *driver = to_kunit_device(dev)->driver;
kunit_release_action(test, device_unregister_wrapper, dev);
- if (driver)
+ if (driver) {
+ const char *driver_name = driver->name;
kunit_release_action(test, driver_unregister_wrapper, (void *)driver);
+ kunit_kfree_const(test, driver_name);
+ }
}
EXPORT_SYMBOL_GPL(kunit_device_unregister);
diff --git a/lib/kunit/executor.c b/lib/kunit/executor.c
index 70b9a43cd257..34b7b6833df3 100644
--- a/lib/kunit/executor.c
+++ b/lib/kunit/executor.c
@@ -70,32 +70,26 @@ struct kunit_glob_filter {
static int kunit_parse_glob_filter(struct kunit_glob_filter *parsed,
const char *filter_glob)
{
- const int len = strlen(filter_glob);
const char *period = strchr(filter_glob, '.');
if (!period) {
- parsed->suite_glob = kzalloc(len + 1, GFP_KERNEL);
+ parsed->suite_glob = kstrdup(filter_glob, GFP_KERNEL);
if (!parsed->suite_glob)
return -ENOMEM;
-
parsed->test_glob = NULL;
- strcpy(parsed->suite_glob, filter_glob);
return 0;
}
- parsed->suite_glob = kzalloc(period - filter_glob + 1, GFP_KERNEL);
+ parsed->suite_glob = kstrndup(filter_glob, period - filter_glob, GFP_KERNEL);
if (!parsed->suite_glob)
return -ENOMEM;
- parsed->test_glob = kzalloc(len - (period - filter_glob) + 1, GFP_KERNEL);
+ parsed->test_glob = kstrdup(period + 1, GFP_KERNEL);
if (!parsed->test_glob) {
kfree(parsed->suite_glob);
return -ENOMEM;
}
- strncpy(parsed->suite_glob, filter_glob, period - filter_glob);
- strncpy(parsed->test_glob, period + 1, len - (period - filter_glob));
-
return 0;
}
diff --git a/lib/kunit/executor_test.c b/lib/kunit/executor_test.c
index 3f7f967e3688..f0090c2729cd 100644
--- a/lib/kunit/executor_test.c
+++ b/lib/kunit/executor_test.c
@@ -286,7 +286,7 @@ static struct kunit_suite *alloc_fake_suite(struct kunit *test,
/* We normally never expect to allocate suites, hence the non-const cast. */
suite = kunit_kzalloc(test, sizeof(*suite), GFP_KERNEL);
- strncpy((char *)suite->name, suite_name, sizeof(suite->name) - 1);
+ strscpy((char *)suite->name, suite_name, sizeof(suite->name));
suite->test_cases = test_cases;
return suite;
diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c
index 798924f7cc86..3056d6bc705d 100644
--- a/lib/kunit/kunit-example-test.c
+++ b/lib/kunit/kunit-example-test.c
@@ -374,4 +374,5 @@ static struct kunit_suite example_init_test_suite = {
*/
kunit_test_init_section_suites(&example_init_test_suite);
+MODULE_DESCRIPTION("Example KUnit test suite");
MODULE_LICENSE("GPL v2");
diff --git a/lib/kunit/kunit-test.c b/lib/kunit/kunit-test.c
index e3412e0ca399..37e02be1e710 100644
--- a/lib/kunit/kunit-test.c
+++ b/lib/kunit/kunit-test.c
@@ -871,4 +871,5 @@ kunit_test_suites(&kunit_try_catch_test_suite, &kunit_resource_test_suite,
&kunit_current_test_suite, &kunit_device_test_suite,
&kunit_fault_test_suite);
+MODULE_DESCRIPTION("KUnit test for core test infrastructure");
MODULE_LICENSE("GPL v2");
diff --git a/lib/kunit/platform-test.c b/lib/kunit/platform-test.c
new file mode 100644
index 000000000000..e3debb8fbcef
--- /dev/null
+++ b/lib/kunit/platform-test.c
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for KUnit platform driver infrastructure.
+ */
+
+#include <linux/platform_device.h>
+
+#include <kunit/platform_device.h>
+#include <kunit/test.h>
+
+/*
+ * Test that kunit_platform_device_alloc() creates a platform device.
+ */
+static void kunit_platform_device_alloc_test(struct kunit *test)
+{
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test,
+ kunit_platform_device_alloc(test, "kunit-platform", 1));
+}
+
+/*
+ * Test that kunit_platform_device_add() registers a platform device on the
+ * platform bus with the proper name and id.
+ */
+static void kunit_platform_device_add_test(struct kunit *test)
+{
+ struct platform_device *pdev;
+ const char *name = "kunit-platform-add";
+ const int id = -1;
+
+ pdev = kunit_platform_device_alloc(test, name, id);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+
+ KUNIT_EXPECT_EQ(test, 0, kunit_platform_device_add(test, pdev));
+ KUNIT_EXPECT_TRUE(test, dev_is_platform(&pdev->dev));
+ KUNIT_EXPECT_STREQ(test, pdev->name, name);
+ KUNIT_EXPECT_EQ(test, pdev->id, id);
+}
+
+/*
+ * Test that kunit_platform_device_add() called twice with the same device name
+ * and id fails the second time and properly cleans up.
+ */
+static void kunit_platform_device_add_twice_fails_test(struct kunit *test)
+{
+ struct platform_device *pdev;
+ const char *name = "kunit-platform-add-2";
+ const int id = -1;
+
+ pdev = kunit_platform_device_alloc(test, name, id);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+ KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, pdev));
+
+ pdev = kunit_platform_device_alloc(test, name, id);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+
+ KUNIT_EXPECT_NE(test, 0, kunit_platform_device_add(test, pdev));
+}
+
+static int kunit_platform_device_find_by_name(struct device *dev, const void *data)
+{
+ return strcmp(dev_name(dev), data) == 0;
+}
+
+/*
+ * Test that kunit_platform_device_add() cleans up by removing the platform
+ * device when the test finishes. */
+static void kunit_platform_device_add_cleans_up(struct kunit *test)
+{
+ struct platform_device *pdev;
+ const char *name = "kunit-platform-clean";
+ const int id = -1;
+ struct kunit fake;
+ struct device *dev;
+
+ kunit_init_test(&fake, "kunit_platform_device_add_fake_test", NULL);
+ KUNIT_ASSERT_EQ(test, fake.status, KUNIT_SUCCESS);
+
+ pdev = kunit_platform_device_alloc(&fake, name, id);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+ KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(&fake, pdev));
+ dev = bus_find_device(&platform_bus_type, NULL, name,
+ kunit_platform_device_find_by_name);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+ put_device(dev);
+
+ /* Remove pdev */
+ kunit_cleanup(&fake);
+
+ /*
+ * Failing to migrate the kunit_resource would lead to an extra
+ * put_device() call on the platform device. The best we can do here is
+ * make sure the device no longer exists on the bus, but if something
+ * is wrong we'll see a refcount underflow here. We can't test for a
+ * refcount underflow because the kref matches the lifetime of the
+ * device which should already be freed and could be used by something
+ * else.
+ */
+ dev = bus_find_device(&platform_bus_type, NULL, name,
+ kunit_platform_device_find_by_name);
+ KUNIT_EXPECT_PTR_EQ(test, NULL, dev);
+ put_device(dev);
+}
+
+/*
+ * Test suite for struct platform_device kunit APIs
+ */
+static struct kunit_case kunit_platform_device_test_cases[] = {
+ KUNIT_CASE(kunit_platform_device_alloc_test),
+ KUNIT_CASE(kunit_platform_device_add_test),
+ KUNIT_CASE(kunit_platform_device_add_twice_fails_test),
+ KUNIT_CASE(kunit_platform_device_add_cleans_up),
+ {}
+};
+
+static struct kunit_suite kunit_platform_device_suite = {
+ .name = "kunit_platform_device",
+ .test_cases = kunit_platform_device_test_cases,
+};
+
+struct kunit_platform_driver_test_context {
+ struct platform_driver pdrv;
+ const char *data;
+};
+
+static const char * const test_data = "test data";
+
+static inline struct kunit_platform_driver_test_context *
+to_test_context(struct platform_device *pdev)
+{
+ return container_of(to_platform_driver(pdev->dev.driver),
+ struct kunit_platform_driver_test_context,
+ pdrv);
+}
+
+static int kunit_platform_driver_probe(struct platform_device *pdev)
+{
+ struct kunit_platform_driver_test_context *ctx;
+
+ ctx = to_test_context(pdev);
+ ctx->data = test_data;
+
+ return 0;
+}
+
+/* Test that kunit_platform_driver_register() registers a driver that probes. */
+static void kunit_platform_driver_register_test(struct kunit *test)
+{
+ struct platform_device *pdev;
+ struct kunit_platform_driver_test_context *ctx;
+ DECLARE_COMPLETION_ONSTACK(comp);
+ const char *name = "kunit-platform-register";
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ pdev = kunit_platform_device_alloc(test, name, -1);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+ KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, pdev));
+
+ ctx->pdrv.probe = kunit_platform_driver_probe;
+ ctx->pdrv.driver.name = name;
+ ctx->pdrv.driver.owner = THIS_MODULE;
+
+ KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_prepare_wait_for_probe(test, pdev, &comp));
+
+ KUNIT_EXPECT_EQ(test, 0, kunit_platform_driver_register(test, &ctx->pdrv));
+ KUNIT_EXPECT_NE(test, 0, wait_for_completion_timeout(&comp, 3 * HZ));
+ KUNIT_EXPECT_STREQ(test, ctx->data, test_data);
+}
+
+/*
+ * Test that kunit_platform_device_prepare_wait_for_probe() completes the completion
+ * when the device is already probed.
+ */
+static void kunit_platform_device_prepare_wait_for_probe_completes_when_already_probed(struct kunit *test)
+{
+ struct platform_device *pdev;
+ struct kunit_platform_driver_test_context *ctx;
+ DECLARE_COMPLETION_ONSTACK(comp);
+ const char *name = "kunit-platform-wait";
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ pdev = kunit_platform_device_alloc(test, name, -1);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+ KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, pdev));
+
+ ctx->pdrv.probe = kunit_platform_driver_probe;
+ ctx->pdrv.driver.name = name;
+ ctx->pdrv.driver.owner = THIS_MODULE;
+
+ /* Make sure driver has actually probed */
+ KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_prepare_wait_for_probe(test, pdev, &comp));
+ KUNIT_ASSERT_EQ(test, 0, kunit_platform_driver_register(test, &ctx->pdrv));
+ KUNIT_ASSERT_NE(test, 0, wait_for_completion_timeout(&comp, 3 * HZ));
+
+ reinit_completion(&comp);
+ KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_prepare_wait_for_probe(test, pdev, &comp));
+
+ KUNIT_EXPECT_NE(test, 0, wait_for_completion_timeout(&comp, HZ));
+}
+
+static struct kunit_case kunit_platform_driver_test_cases[] = {
+ KUNIT_CASE(kunit_platform_driver_register_test),
+ KUNIT_CASE(kunit_platform_device_prepare_wait_for_probe_completes_when_already_probed),
+ {}
+};
+
+/*
+ * Test suite for struct platform_driver kunit APIs
+ */
+static struct kunit_suite kunit_platform_driver_suite = {
+ .name = "kunit_platform_driver",
+ .test_cases = kunit_platform_driver_test_cases,
+};
+
+kunit_test_suites(
+ &kunit_platform_device_suite,
+ &kunit_platform_driver_suite,
+);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit test for KUnit platform driver infrastructure");
diff --git a/lib/kunit/platform.c b/lib/kunit/platform.c
new file mode 100644
index 000000000000..0b518de26065
--- /dev/null
+++ b/lib/kunit/platform.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test managed platform driver
+ */
+
+#include <linux/completion.h>
+#include <linux/device/bus.h>
+#include <linux/device/driver.h>
+#include <linux/platform_device.h>
+
+#include <kunit/platform_device.h>
+#include <kunit/resource.h>
+
+struct kunit_platform_device_alloc_params {
+ const char *name;
+ int id;
+};
+
+static int kunit_platform_device_alloc_init(struct kunit_resource *res, void *context)
+{
+ struct kunit_platform_device_alloc_params *params = context;
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc(params->name, params->id);
+ if (!pdev)
+ return -ENOMEM;
+
+ res->data = pdev;
+
+ return 0;
+}
+
+static void kunit_platform_device_alloc_exit(struct kunit_resource *res)
+{
+ struct platform_device *pdev = res->data;
+
+ platform_device_put(pdev);
+}
+
+/**
+ * kunit_platform_device_alloc() - Allocate a KUnit test managed platform device
+ * @test: test context
+ * @name: device name of platform device to alloc
+ * @id: identifier of platform device to alloc.
+ *
+ * Allocate a test managed platform device. The device is put when the test completes.
+ *
+ * Return: Allocated platform device on success, NULL on failure.
+ */
+struct platform_device *
+kunit_platform_device_alloc(struct kunit *test, const char *name, int id)
+{
+ struct kunit_platform_device_alloc_params params = {
+ .name = name,
+ .id = id,
+ };
+
+ return kunit_alloc_resource(test,
+ kunit_platform_device_alloc_init,
+ kunit_platform_device_alloc_exit,
+ GFP_KERNEL, &params);
+}
+EXPORT_SYMBOL_GPL(kunit_platform_device_alloc);
+
+static void kunit_platform_device_add_exit(struct kunit_resource *res)
+{
+ struct platform_device *pdev = res->data;
+
+ platform_device_unregister(pdev);
+}
+
+static bool
+kunit_platform_device_alloc_match(struct kunit *test,
+ struct kunit_resource *res, void *match_data)
+{
+ struct platform_device *pdev = match_data;
+
+ return res->data == pdev && res->free == kunit_platform_device_alloc_exit;
+}
+
+KUNIT_DEFINE_ACTION_WRAPPER(platform_device_unregister_wrapper,
+ platform_device_unregister, struct platform_device *);
+/**
+ * kunit_platform_device_add() - Register a KUnit test managed platform device
+ * @test: test context
+ * @pdev: platform device to add
+ *
+ * Register a test managed platform device. The device is unregistered when the
+ * test completes.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int kunit_platform_device_add(struct kunit *test, struct platform_device *pdev)
+{
+ struct kunit_resource *res;
+ int ret;
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ return ret;
+
+ res = kunit_find_resource(test, kunit_platform_device_alloc_match, pdev);
+ if (res) {
+ /*
+ * Transfer the reference count of the platform device if it
+ * was allocated with kunit_platform_device_alloc(). In this
+ * case, calling platform_device_put() when the test exits from
+ * kunit_platform_device_alloc_exit() would lead to reference
+ * count underflow because platform_device_unregister_wrapper()
+ * calls platform_device_unregister() which also calls
+ * platform_device_put().
+ *
+ * Usually callers transfer the refcount initialized in
+ * platform_device_alloc() to platform_device_add() by calling
+ * platform_device_unregister() when platform_device_add()
+ * succeeds or platform_device_put() when it fails. KUnit has to
+ * keep this straight by redirecting the free routine for the
+ * resource to the right function. Luckily this only has to
+ * account for the success scenario.
+ */
+ res->free = kunit_platform_device_add_exit;
+ kunit_put_resource(res);
+ } else {
+ ret = kunit_add_action_or_reset(test, platform_device_unregister_wrapper, pdev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kunit_platform_device_add);
+
+struct kunit_platform_device_probe_nb {
+ struct completion *x;
+ struct device *dev;
+ struct notifier_block nb;
+};
+
+static int kunit_platform_device_probe_notify(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct kunit_platform_device_probe_nb *knb;
+ struct device *dev = data;
+
+ knb = container_of(nb, struct kunit_platform_device_probe_nb, nb);
+ if (event != BUS_NOTIFY_BOUND_DRIVER || knb->dev != dev)
+ return NOTIFY_DONE;
+
+ complete(knb->x);
+
+ return NOTIFY_OK;
+}
+
+static void kunit_platform_device_probe_nb_remove(void *nb)
+{
+ bus_unregister_notifier(&platform_bus_type, nb);
+}
+
+/**
+ * kunit_platform_device_prepare_wait_for_probe() - Prepare a completion
+ * variable to wait for a platform device to probe
+ * @test: test context
+ * @pdev: platform device to prepare to wait for probe of
+ * @x: completion variable completed when @dev has probed
+ *
+ * Prepare a completion variable @x to wait for @pdev to probe. Waiting on the
+ * completion forces a preemption, allowing the platform driver to probe.
+ *
+ * Example
+ *
+ * .. code-block:: c
+ *
+ * static int kunit_platform_driver_probe(struct platform_device *pdev)
+ * {
+ * return 0;
+ * }
+ *
+ * static void kunit_platform_driver_test(struct kunit *test)
+ * {
+ * struct platform_device *pdev;
+ * struct platform_driver *pdrv;
+ * DECLARE_COMPLETION_ONSTACK(comp);
+ *
+ * pdev = kunit_platform_device_alloc(test, "kunit-platform", -1);
+ * KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
+ * KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, pdev));
+ *
+ * pdrv = kunit_kzalloc(test, sizeof(*pdrv), GFP_KERNEL);
+ * KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdrv);
+ *
+ * pdrv->probe = kunit_platform_driver_probe;
+ * pdrv->driver.name = "kunit-platform";
+ * pdrv->driver.owner = THIS_MODULE;
+ *
+ * KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_prepare_wait_for_probe(test, pdev, &comp));
+ * KUNIT_ASSERT_EQ(test, 0, kunit_platform_driver_register(test, pdrv));
+ *
+ * KUNIT_EXPECT_NE(test, 0, wait_for_completion_timeout(&comp, 3 * HZ));
+ * }
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int kunit_platform_device_prepare_wait_for_probe(struct kunit *test,
+ struct platform_device *pdev,
+ struct completion *x)
+{
+ struct device *dev = &pdev->dev;
+ struct kunit_platform_device_probe_nb *knb;
+ bool bound;
+
+ knb = kunit_kzalloc(test, sizeof(*knb), GFP_KERNEL);
+ if (!knb)
+ return -ENOMEM;
+
+ knb->nb.notifier_call = kunit_platform_device_probe_notify;
+ knb->dev = dev;
+ knb->x = x;
+
+ device_lock(dev);
+ bound = device_is_bound(dev);
+ if (bound) {
+ device_unlock(dev);
+ complete(x);
+ kunit_kfree(test, knb);
+ return 0;
+ }
+
+ bus_register_notifier(&platform_bus_type, &knb->nb);
+ device_unlock(&pdev->dev);
+
+ return kunit_add_action_or_reset(test, kunit_platform_device_probe_nb_remove, &knb->nb);
+}
+EXPORT_SYMBOL_GPL(kunit_platform_device_prepare_wait_for_probe);
+
+KUNIT_DEFINE_ACTION_WRAPPER(platform_driver_unregister_wrapper,
+ platform_driver_unregister, struct platform_driver *);
+/**
+ * kunit_platform_driver_register() - Register a KUnit test managed platform driver
+ * @test: test context
+ * @drv: platform driver to register
+ *
+ * Register a test managed platform driver. This allows callers to embed the
+ * @drv in a container structure and use container_of() in the probe function
+ * to pass information to KUnit tests.
+ *
+ * Example
+ *
+ * .. code-block:: c
+ *
+ * struct kunit_test_context {
+ * struct platform_driver pdrv;
+ * const char *data;
+ * };
+ *
+ * static inline struct kunit_test_context *
+ * to_test_context(struct platform_device *pdev)
+ * {
+ * return container_of(to_platform_driver(pdev->dev.driver),
+ * struct kunit_test_context,
+ * pdrv);
+ * }
+ *
+ * static int kunit_platform_driver_probe(struct platform_device *pdev)
+ * {
+ * struct kunit_test_context *ctx;
+ *
+ * ctx = to_test_context(pdev);
+ * ctx->data = "test data";
+ *
+ * return 0;
+ * }
+ *
+ * static void kunit_platform_driver_test(struct kunit *test)
+ * {
+ * struct kunit_test_context *ctx;
+ *
+ * ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ * KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+ *
+ * ctx->pdrv.probe = kunit_platform_driver_probe;
+ * ctx->pdrv.driver.name = "kunit-platform";
+ * ctx->pdrv.driver.owner = THIS_MODULE;
+ *
+ * KUNIT_EXPECT_EQ(test, 0, kunit_platform_driver_register(test, &ctx->pdrv));
+ * <... wait for driver to probe ...>
+ * KUNIT_EXPECT_STREQ(test, ctx->data, "test data");
+ * }
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int kunit_platform_driver_register(struct kunit *test,
+ struct platform_driver *drv)
+{
+ int ret;
+
+ ret = platform_driver_register(drv);
+ if (ret)
+ return ret;
+
+ return kunit_add_action_or_reset(test, platform_driver_unregister_wrapper, drv);
+}
+EXPORT_SYMBOL_GPL(kunit_platform_driver_register);
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index b8514dbb337c..089c832e3cdb 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -874,6 +874,25 @@ void kunit_kfree(struct kunit *test, const void *ptr)
}
EXPORT_SYMBOL_GPL(kunit_kfree);
+void kunit_kfree_const(struct kunit *test, const void *x)
+{
+#if !IS_MODULE(CONFIG_KUNIT)
+ if (!is_kernel_rodata((unsigned long)x))
+#endif
+ kunit_kfree(test, x);
+}
+EXPORT_SYMBOL_GPL(kunit_kfree_const);
+
+const char *kunit_kstrdup_const(struct kunit *test, const char *str, gfp_t gfp)
+{
+#if !IS_MODULE(CONFIG_KUNIT)
+ if (is_kernel_rodata((unsigned long)str))
+ return str;
+#endif
+ return kunit_kstrdup(test, str, gfp);
+}
+EXPORT_SYMBOL_GPL(kunit_kstrdup_const);
+
void kunit_cleanup(struct kunit *test)
{
struct kunit_resource *res;
@@ -938,4 +957,5 @@ static void __exit kunit_exit(void)
}
module_exit(kunit_exit);
+MODULE_DESCRIPTION("Base unit test (KUnit) API");
MODULE_LICENSE("GPL v2");
diff --git a/lib/kunit/user_alloc.c b/lib/kunit/user_alloc.c
new file mode 100644
index 000000000000..ae935df09a5e
--- /dev/null
+++ b/lib/kunit/user_alloc.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit userspace memory allocation resource management.
+ */
+#include <kunit/resource.h>
+#include <kunit/test.h>
+#include <linux/kthread.h>
+#include <linux/mm.h>
+
+struct kunit_vm_mmap_resource {
+ unsigned long addr;
+ size_t size;
+};
+
+/* vm_mmap() arguments */
+struct kunit_vm_mmap_params {
+ struct file *file;
+ unsigned long addr;
+ unsigned long len;
+ unsigned long prot;
+ unsigned long flag;
+ unsigned long offset;
+};
+
+/* Create and attach a new mm if it doesn't already exist. */
+static int kunit_attach_mm(void)
+{
+ struct mm_struct *mm;
+
+ if (current->mm)
+ return 0;
+
+ /* arch_pick_mmap_layout() is only sane with MMU systems. */
+ if (!IS_ENABLED(CONFIG_MMU))
+ return -EINVAL;
+
+ mm = mm_alloc();
+ if (!mm)
+ return -ENOMEM;
+
+ /* Define the task size. */
+ mm->task_size = TASK_SIZE;
+
+ /* Make sure we can allocate new VMAs. */
+ arch_pick_mmap_layout(mm, &current->signal->rlim[RLIMIT_STACK]);
+
+ /* Attach the mm. It will be cleaned up when the process dies. */
+ kthread_use_mm(mm);
+
+ return 0;
+}
+
+static int kunit_vm_mmap_init(struct kunit_resource *res, void *context)
+{
+ struct kunit_vm_mmap_params *p = context;
+ struct kunit_vm_mmap_resource vres;
+ int ret;
+
+ ret = kunit_attach_mm();
+ if (ret)
+ return ret;
+
+ vres.size = p->len;
+ vres.addr = vm_mmap(p->file, p->addr, p->len, p->prot, p->flag, p->offset);
+ if (!vres.addr)
+ return -ENOMEM;
+ res->data = kmemdup(&vres, sizeof(vres), GFP_KERNEL);
+ if (!res->data) {
+ vm_munmap(vres.addr, vres.size);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void kunit_vm_mmap_free(struct kunit_resource *res)
+{
+ struct kunit_vm_mmap_resource *vres = res->data;
+
+ /*
+ * Since this is executed from the test monitoring process,
+ * the test's mm has already been torn down. We don't need
+ * to run vm_munmap(vres->addr, vres->size), only clean up
+ * the vres.
+ */
+
+ kfree(vres);
+ res->data = NULL;
+}
+
+unsigned long kunit_vm_mmap(struct kunit *test, struct file *file,
+ unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flag,
+ unsigned long offset)
+{
+ struct kunit_vm_mmap_params params = {
+ .file = file,
+ .addr = addr,
+ .len = len,
+ .prot = prot,
+ .flag = flag,
+ .offset = offset,
+ };
+ struct kunit_vm_mmap_resource *vres;
+
+ vres = kunit_alloc_resource(test,
+ kunit_vm_mmap_init,
+ kunit_vm_mmap_free,
+ GFP_KERNEL,
+ &params);
+ if (vres)
+ return vres->addr;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kunit_vm_mmap);
+
+MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING);
diff --git a/lib/kunit_iov_iter.c b/lib/kunit_iov_iter.c
index 27e0c8ee71d8..13e15687675a 100644
--- a/lib/kunit_iov_iter.c
+++ b/lib/kunit_iov_iter.c
@@ -12,6 +12,7 @@
#include <linux/mm.h>
#include <linux/uio.h>
#include <linux/bvec.h>
+#include <linux/folio_queue.h>
#include <kunit/test.h>
MODULE_DESCRIPTION("iov_iter testing");
@@ -62,6 +63,9 @@ static void *__init iov_kunit_create_buffer(struct kunit *test,
KUNIT_ASSERT_EQ(test, got, npages);
}
+ for (int i = 0; i < npages; i++)
+ pages[i]->index = i;
+
buffer = vmap(pages, npages, VM_MAP | VM_MAP_PUT_PAGES, PAGE_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buffer);
@@ -362,6 +366,179 @@ stop:
KUNIT_SUCCEED(test);
}
+static void iov_kunit_destroy_folioq(void *data)
+{
+ struct folio_queue *folioq, *next;
+
+ for (folioq = data; folioq; folioq = next) {
+ next = folioq->next;
+ for (int i = 0; i < folioq_nr_slots(folioq); i++)
+ if (folioq_folio(folioq, i))
+ folio_put(folioq_folio(folioq, i));
+ kfree(folioq);
+ }
+}
+
+static void __init iov_kunit_load_folioq(struct kunit *test,
+ struct iov_iter *iter, int dir,
+ struct folio_queue *folioq,
+ struct page **pages, size_t npages)
+{
+ struct folio_queue *p = folioq;
+ size_t size = 0;
+ int i;
+
+ for (i = 0; i < npages; i++) {
+ if (folioq_full(p)) {
+ p->next = kzalloc(sizeof(struct folio_queue), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, p->next);
+ folioq_init(p->next);
+ p->next->prev = p;
+ p = p->next;
+ }
+ folioq_append(p, page_folio(pages[i]));
+ size += PAGE_SIZE;
+ }
+ iov_iter_folio_queue(iter, dir, folioq, 0, 0, size);
+}
+
+static struct folio_queue *iov_kunit_create_folioq(struct kunit *test)
+{
+ struct folio_queue *folioq;
+
+ folioq = kzalloc(sizeof(struct folio_queue), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, folioq);
+ kunit_add_action_or_reset(test, iov_kunit_destroy_folioq, folioq);
+ folioq_init(folioq);
+ return folioq;
+}
+
+/*
+ * Test copying to a ITER_FOLIOQ-type iterator.
+ */
+static void __init iov_kunit_copy_to_folioq(struct kunit *test)
+{
+ const struct kvec_test_range *pr;
+ struct iov_iter iter;
+ struct folio_queue *folioq;
+ struct page **spages, **bpages;
+ u8 *scratch, *buffer;
+ size_t bufsize, npages, size, copied;
+ int i, patt;
+
+ bufsize = 0x100000;
+ npages = bufsize / PAGE_SIZE;
+
+ folioq = iov_kunit_create_folioq(test);
+
+ scratch = iov_kunit_create_buffer(test, &spages, npages);
+ for (i = 0; i < bufsize; i++)
+ scratch[i] = pattern(i);
+
+ buffer = iov_kunit_create_buffer(test, &bpages, npages);
+ memset(buffer, 0, bufsize);
+
+ iov_kunit_load_folioq(test, &iter, READ, folioq, bpages, npages);
+
+ i = 0;
+ for (pr = kvec_test_ranges; pr->from >= 0; pr++) {
+ size = pr->to - pr->from;
+ KUNIT_ASSERT_LE(test, pr->to, bufsize);
+
+ iov_iter_folio_queue(&iter, READ, folioq, 0, 0, pr->to);
+ iov_iter_advance(&iter, pr->from);
+ copied = copy_to_iter(scratch + i, size, &iter);
+
+ KUNIT_EXPECT_EQ(test, copied, size);
+ KUNIT_EXPECT_EQ(test, iter.count, 0);
+ KUNIT_EXPECT_EQ(test, iter.iov_offset, pr->to % PAGE_SIZE);
+ i += size;
+ if (test->status == KUNIT_FAILURE)
+ goto stop;
+ }
+
+ /* Build the expected image in the scratch buffer. */
+ patt = 0;
+ memset(scratch, 0, bufsize);
+ for (pr = kvec_test_ranges; pr->from >= 0; pr++)
+ for (i = pr->from; i < pr->to; i++)
+ scratch[i] = pattern(patt++);
+
+ /* Compare the images */
+ for (i = 0; i < bufsize; i++) {
+ KUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], "at i=%x", i);
+ if (buffer[i] != scratch[i])
+ return;
+ }
+
+stop:
+ KUNIT_SUCCEED(test);
+}
+
+/*
+ * Test copying from a ITER_FOLIOQ-type iterator.
+ */
+static void __init iov_kunit_copy_from_folioq(struct kunit *test)
+{
+ const struct kvec_test_range *pr;
+ struct iov_iter iter;
+ struct folio_queue *folioq;
+ struct page **spages, **bpages;
+ u8 *scratch, *buffer;
+ size_t bufsize, npages, size, copied;
+ int i, j;
+
+ bufsize = 0x100000;
+ npages = bufsize / PAGE_SIZE;
+
+ folioq = iov_kunit_create_folioq(test);
+
+ buffer = iov_kunit_create_buffer(test, &bpages, npages);
+ for (i = 0; i < bufsize; i++)
+ buffer[i] = pattern(i);
+
+ scratch = iov_kunit_create_buffer(test, &spages, npages);
+ memset(scratch, 0, bufsize);
+
+ iov_kunit_load_folioq(test, &iter, READ, folioq, bpages, npages);
+
+ i = 0;
+ for (pr = kvec_test_ranges; pr->from >= 0; pr++) {
+ size = pr->to - pr->from;
+ KUNIT_ASSERT_LE(test, pr->to, bufsize);
+
+ iov_iter_folio_queue(&iter, WRITE, folioq, 0, 0, pr->to);
+ iov_iter_advance(&iter, pr->from);
+ copied = copy_from_iter(scratch + i, size, &iter);
+
+ KUNIT_EXPECT_EQ(test, copied, size);
+ KUNIT_EXPECT_EQ(test, iter.count, 0);
+ KUNIT_EXPECT_EQ(test, iter.iov_offset, pr->to % PAGE_SIZE);
+ i += size;
+ }
+
+ /* Build the expected image in the main buffer. */
+ i = 0;
+ memset(buffer, 0, bufsize);
+ for (pr = kvec_test_ranges; pr->from >= 0; pr++) {
+ for (j = pr->from; j < pr->to; j++) {
+ buffer[i++] = pattern(j);
+ if (i >= bufsize)
+ goto stop;
+ }
+ }
+stop:
+
+ /* Compare the images */
+ for (i = 0; i < bufsize; i++) {
+ KUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], "at i=%x", i);
+ if (scratch[i] != buffer[i])
+ return;
+ }
+
+ KUNIT_SUCCEED(test);
+}
+
static void iov_kunit_destroy_xarray(void *data)
{
struct xarray *xarray = data;
@@ -678,6 +855,85 @@ stop:
}
/*
+ * Test the extraction of ITER_FOLIOQ-type iterators.
+ */
+static void __init iov_kunit_extract_pages_folioq(struct kunit *test)
+{
+ const struct kvec_test_range *pr;
+ struct folio_queue *folioq;
+ struct iov_iter iter;
+ struct page **bpages, *pagelist[8], **pages = pagelist;
+ ssize_t len;
+ size_t bufsize, size = 0, npages;
+ int i, from;
+
+ bufsize = 0x100000;
+ npages = bufsize / PAGE_SIZE;
+
+ folioq = iov_kunit_create_folioq(test);
+
+ iov_kunit_create_buffer(test, &bpages, npages);
+ iov_kunit_load_folioq(test, &iter, READ, folioq, bpages, npages);
+
+ for (pr = kvec_test_ranges; pr->from >= 0; pr++) {
+ from = pr->from;
+ size = pr->to - from;
+ KUNIT_ASSERT_LE(test, pr->to, bufsize);
+
+ iov_iter_folio_queue(&iter, WRITE, folioq, 0, 0, pr->to);
+ iov_iter_advance(&iter, from);
+
+ do {
+ size_t offset0 = LONG_MAX;
+
+ for (i = 0; i < ARRAY_SIZE(pagelist); i++)
+ pagelist[i] = (void *)(unsigned long)0xaa55aa55aa55aa55ULL;
+
+ len = iov_iter_extract_pages(&iter, &pages, 100 * 1024,
+ ARRAY_SIZE(pagelist), 0, &offset0);
+ KUNIT_EXPECT_GE(test, len, 0);
+ if (len < 0)
+ break;
+ KUNIT_EXPECT_LE(test, len, size);
+ KUNIT_EXPECT_EQ(test, iter.count, size - len);
+ if (len == 0)
+ break;
+ size -= len;
+ KUNIT_EXPECT_GE(test, (ssize_t)offset0, 0);
+ KUNIT_EXPECT_LT(test, offset0, PAGE_SIZE);
+
+ for (i = 0; i < ARRAY_SIZE(pagelist); i++) {
+ struct page *p;
+ ssize_t part = min_t(ssize_t, len, PAGE_SIZE - offset0);
+ int ix;
+
+ KUNIT_ASSERT_GE(test, part, 0);
+ ix = from / PAGE_SIZE;
+ KUNIT_ASSERT_LT(test, ix, npages);
+ p = bpages[ix];
+ KUNIT_EXPECT_PTR_EQ(test, pagelist[i], p);
+ KUNIT_EXPECT_EQ(test, offset0, from % PAGE_SIZE);
+ from += part;
+ len -= part;
+ KUNIT_ASSERT_GE(test, len, 0);
+ if (len == 0)
+ break;
+ offset0 = 0;
+ }
+
+ if (test->status == KUNIT_FAILURE)
+ goto stop;
+ } while (iov_iter_count(&iter) > 0);
+
+ KUNIT_EXPECT_EQ(test, size, 0);
+ KUNIT_EXPECT_EQ(test, iter.count, 0);
+ }
+
+stop:
+ KUNIT_SUCCEED(test);
+}
+
+/*
* Test the extraction of ITER_XARRAY-type iterators.
*/
static void __init iov_kunit_extract_pages_xarray(struct kunit *test)
@@ -761,10 +1017,13 @@ static struct kunit_case __refdata iov_kunit_cases[] = {
KUNIT_CASE(iov_kunit_copy_from_kvec),
KUNIT_CASE(iov_kunit_copy_to_bvec),
KUNIT_CASE(iov_kunit_copy_from_bvec),
+ KUNIT_CASE(iov_kunit_copy_to_folioq),
+ KUNIT_CASE(iov_kunit_copy_from_folioq),
KUNIT_CASE(iov_kunit_copy_to_xarray),
KUNIT_CASE(iov_kunit_copy_from_xarray),
KUNIT_CASE(iov_kunit_extract_pages_kvec),
KUNIT_CASE(iov_kunit_extract_pages_bvec),
+ KUNIT_CASE(iov_kunit_extract_pages_folioq),
KUNIT_CASE(iov_kunit_extract_pages_xarray),
{}
};
diff --git a/lib/list-test.c b/lib/list-test.c
index 0cc27de9cec8..e207c4c98d70 100644
--- a/lib/list-test.c
+++ b/lib/list-test.c
@@ -102,6 +102,8 @@ static void list_test_list_replace(struct kunit *test)
/* now: [list] -> a_new -> b */
KUNIT_EXPECT_PTR_EQ(test, list.next, &a_new);
KUNIT_EXPECT_PTR_EQ(test, b.prev, &a_new);
+ KUNIT_EXPECT_PTR_EQ(test, a_new.next, &b);
+ KUNIT_EXPECT_PTR_EQ(test, a_new.prev, &list);
}
static void list_test_list_replace_init(struct kunit *test)
@@ -118,6 +120,8 @@ static void list_test_list_replace_init(struct kunit *test)
/* now: [list] -> a_new -> b */
KUNIT_EXPECT_PTR_EQ(test, list.next, &a_new);
KUNIT_EXPECT_PTR_EQ(test, b.prev, &a_new);
+ KUNIT_EXPECT_PTR_EQ(test, a_new.next, &b);
+ KUNIT_EXPECT_PTR_EQ(test, a_new.prev, &list);
/* check a_old is empty (initialized) */
KUNIT_EXPECT_TRUE(test, list_empty_careful(&a_old));
@@ -1201,12 +1205,6 @@ static struct kunit_suite hlist_test_module = {
};
-struct klist_test_struct {
- int data;
- struct klist klist;
- struct klist_node klist_node;
-};
-
static int node_count;
static struct klist_node *last_node;
@@ -1499,4 +1497,5 @@ static struct kunit_suite klist_test_module = {
kunit_test_suites(&list_test_module, &hlist_test_module, &klist_test_module);
+MODULE_DESCRIPTION("KUnit test for the Kernel Linked-list structures");
MODULE_LICENSE("GPL v2");
diff --git a/lib/lru_cache.c b/lib/lru_cache.c
index b3d9187611de..9e0d469c7658 100644
--- a/lib/lru_cache.c
+++ b/lib/lru_cache.c
@@ -243,7 +243,7 @@ static struct lc_element *__lc_find(struct lru_cache *lc, unsigned int enr,
BUG_ON(!lc);
BUG_ON(!lc->nr_elements);
- hlist_for_each_entry(e, lc_hash_slot(lc, enr), colision) {
+ hlist_for_each_entry(e, lc_hash_slot(lc, enr), collision) {
/* "about to be changed" elements, pending transaction commit,
* are hashed by their "new number". "Normal" elements have
* lc_number == lc_new_number. */
@@ -303,7 +303,7 @@ void lc_del(struct lru_cache *lc, struct lc_element *e)
BUG_ON(e->refcnt);
e->lc_number = e->lc_new_number = LC_FREE;
- hlist_del_init(&e->colision);
+ hlist_del_init(&e->collision);
list_move(&e->list, &lc->free);
RETURN();
}
@@ -324,9 +324,9 @@ static struct lc_element *lc_prepare_for_change(struct lru_cache *lc, unsigned n
PARANOIA_LC_ELEMENT(lc, e);
e->lc_new_number = new_number;
- if (!hlist_unhashed(&e->colision))
- __hlist_del(&e->colision);
- hlist_add_head(&e->colision, lc_hash_slot(lc, new_number));
+ if (!hlist_unhashed(&e->collision))
+ __hlist_del(&e->collision);
+ hlist_add_head(&e->collision, lc_hash_slot(lc, new_number));
list_move(&e->list, &lc->to_be_changed);
return e;
diff --git a/lib/lz4/lz4_compress.c b/lib/lz4/lz4_compress.c
index 90bb67994688..b0bbeeb74b9e 100644
--- a/lib/lz4/lz4_compress.c
+++ b/lib/lz4/lz4_compress.c
@@ -37,7 +37,7 @@
#include "lz4defs.h"
#include <linux/module.h>
#include <linux/kernel.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
static const int LZ4_minLength = (MFLIMIT + 1);
static const int LZ4_64Klimit = ((64 * KB) + (MFLIMIT - 1));
diff --git a/lib/lz4/lz4_decompress.c b/lib/lz4/lz4_decompress.c
index 59fe69a63800..0e31e6da5ce7 100644
--- a/lib/lz4/lz4_decompress.c
+++ b/lib/lz4/lz4_decompress.c
@@ -38,7 +38,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
/*-*****************************
* Decompression functions
diff --git a/lib/lz4/lz4defs.h b/lib/lz4/lz4defs.h
index 330aa539b46e..cb358d6bde5a 100644
--- a/lib/lz4/lz4defs.h
+++ b/lib/lz4/lz4defs.h
@@ -35,7 +35,7 @@
* Sven Schmidt <4sschmid@informatik.uni-hamburg.de>
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/bitops.h>
#include <linux/string.h> /* memset, memcpy */
diff --git a/lib/lz4/lz4hc_compress.c b/lib/lz4/lz4hc_compress.c
index e7ac8694b797..bc45594ad2a8 100644
--- a/lib/lz4/lz4hc_compress.c
+++ b/lib/lz4/lz4hc_compress.c
@@ -621,6 +621,7 @@ void LZ4_resetStreamHC(LZ4_streamHC_t *LZ4_streamHCPtr, int compressionLevel)
LZ4_streamHCPtr->internal_donotuse.base = NULL;
LZ4_streamHCPtr->internal_donotuse.compressionLevel = (unsigned int)compressionLevel;
}
+EXPORT_SYMBOL(LZ4_resetStreamHC);
int LZ4_loadDictHC(LZ4_streamHC_t *LZ4_streamHCPtr,
const char *dictionary,
diff --git a/lib/lzo/lzo1x_compress.c b/lib/lzo/lzo1x_compress.c
index 9d31e7126606..47d6d43ea957 100644
--- a/lib/lzo/lzo1x_compress.c
+++ b/lib/lzo/lzo1x_compress.c
@@ -14,7 +14,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/lzo.h>
#include "lzodefs.h"
diff --git a/lib/lzo/lzo1x_decompress_safe.c b/lib/lzo/lzo1x_decompress_safe.c
index 7892a40cf765..c94f4928e188 100644
--- a/lib/lzo/lzo1x_decompress_safe.c
+++ b/lib/lzo/lzo1x_decompress_safe.c
@@ -16,7 +16,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#endif
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/lzo.h>
#include "lzodefs.h"
diff --git a/lib/maple_tree.c b/lib/maple_tree.c
index 2d7d27e6ae3c..3619301dda2e 100644
--- a/lib/maple_tree.c
+++ b/lib/maple_tree.c
@@ -348,17 +348,17 @@ static inline void *mte_safe_root(const struct maple_enode *node)
return (void *)((unsigned long)node & ~MAPLE_ROOT_NODE);
}
-static inline void *mte_set_full(const struct maple_enode *node)
+static inline void __maybe_unused *mte_set_full(const struct maple_enode *node)
{
return (void *)((unsigned long)node & ~MAPLE_ENODE_NULL);
}
-static inline void *mte_clear_full(const struct maple_enode *node)
+static inline void __maybe_unused *mte_clear_full(const struct maple_enode *node)
{
return (void *)((unsigned long)node | MAPLE_ENODE_NULL);
}
-static inline bool mte_has_null(const struct maple_enode *node)
+static inline bool __maybe_unused mte_has_null(const struct maple_enode *node)
{
return (unsigned long)node & MAPLE_ENODE_NULL;
}
@@ -474,6 +474,7 @@ enum maple_type mas_parent_type(struct ma_state *mas, struct maple_enode *enode)
/*
* mas_set_parent() - Set the parent node and encode the slot
+ * @mas: The maple state
* @enode: The encoded maple node.
* @parent: The encoded maple node that is the parent of @enode.
* @slot: The slot that @enode resides in @parent.
@@ -534,7 +535,7 @@ unsigned int mte_parent_slot(const struct maple_enode *enode)
/*
* mte_parent() - Get the parent of @node.
- * @node: The encoded maple node.
+ * @enode: The encoded maple node.
*
* Return: The parent maple node.
*/
@@ -641,8 +642,8 @@ static inline unsigned int mas_alloc_req(const struct ma_state *mas)
/*
* ma_pivots() - Get a pointer to the maple node pivots.
- * @node - the maple node
- * @type - the node type
+ * @node: the maple node
+ * @type: the node type
*
* In the event of a dead node, this array may be %NULL
*
@@ -665,8 +666,8 @@ static inline unsigned long *ma_pivots(struct maple_node *node,
/*
* ma_gaps() - Get a pointer to the maple node gaps.
- * @node - the maple node
- * @type - the node type
+ * @node: the maple node
+ * @type: the node type
*
* Return: A pointer to the maple node gaps
*/
@@ -880,8 +881,6 @@ static inline void ma_set_meta(struct maple_node *mn, enum maple_type mt,
* @mt: The maple tree
* @mn: The maple node
* @type: The maple node type
- * @offset: The offset of the highest sub-gap in this node.
- * @end: The end of the data in this node.
*/
static inline void mt_clear_meta(struct maple_tree *mt, struct maple_node *mn,
enum maple_type type)
@@ -939,7 +938,7 @@ static inline unsigned char ma_meta_gap(struct maple_node *mn)
/*
* ma_set_meta_gap() - Set the largest gap location in a nodes metadata
* @mn: The maple node
- * @mn: The maple node type
+ * @mt: The maple node type
* @offset: The location of the largest gap.
*/
static inline void ma_set_meta_gap(struct maple_node *mn, enum maple_type mt,
@@ -953,8 +952,8 @@ static inline void ma_set_meta_gap(struct maple_node *mn, enum maple_type mt,
/*
* mat_add() - Add a @dead_enode to the ma_topiary of a list of dead nodes.
- * @mat - the ma_topiary, a linked list of dead nodes.
- * @dead_enode - the node to be marked as dead and added to the tail of the list
+ * @mat: the ma_topiary, a linked list of dead nodes.
+ * @dead_enode: the node to be marked as dead and added to the tail of the list
*
* Add the @dead_enode to the linked list in @mat.
*/
@@ -977,8 +976,8 @@ static void mt_destroy_walk(struct maple_enode *enode, struct maple_tree *mt,
bool free);
/*
* mas_mat_destroy() - Free all nodes and subtrees in a dead list.
- * @mas - the maple state
- * @mat - the ma_topiary linked list of dead nodes to free.
+ * @mas: the maple state
+ * @mat: the ma_topiary linked list of dead nodes to free.
*
* Destroy walk a dead list.
*/
@@ -999,7 +998,7 @@ static void mas_mat_destroy(struct ma_state *mas, struct ma_topiary *mat)
}
/*
* mas_descend() - Descend into the slot stored in the ma_state.
- * @mas - the maple state.
+ * @mas: the maple state.
*
* Note: Not RCU safe, only use in write side or debug code.
*/
@@ -1346,8 +1345,8 @@ static void mas_node_count(struct ma_state *mas, int count)
* Return:
* - If mas->node is an error or not mas_start, return NULL.
* - If it's an empty tree: NULL & mas->status == ma_none
- * - If it's a single entry: The entry & mas->status == mas_root
- * - If it's a tree: NULL & mas->status == safe root node.
+ * - If it's a single entry: The entry & mas->status == ma_root
+ * - If it's a tree: NULL & mas->status == ma_active
*/
static inline struct maple_enode *mas_start(struct ma_state *mas)
{
@@ -1372,9 +1371,9 @@ retry:
return NULL;
}
+ mas->node = NULL;
/* empty tree */
if (unlikely(!root)) {
- mas->node = NULL;
mas->status = ma_none;
mas->offset = MAPLE_NODE_SLOTS;
return NULL;
@@ -1462,7 +1461,7 @@ static inline unsigned char mas_data_end(struct ma_state *mas)
/*
* mas_leaf_max_gap() - Returns the largest gap in a leaf node
- * @mas - the maple state
+ * @mas: the maple state
*
* Return: The maximum gap in the leaf.
*/
@@ -1544,7 +1543,7 @@ static unsigned long mas_leaf_max_gap(struct ma_state *mas)
* @node: The maple node
* @gaps: The pointer to the gaps
* @mt: The maple node type
- * @*off: Pointer to store the offset location of the gap.
+ * @off: Pointer to store the offset location of the gap.
*
* Uses the metadata data end to scan backwards across set gaps.
*
@@ -1651,7 +1650,7 @@ ascend:
/*
* mas_update_gap() - Update a nodes gaps and propagate up if necessary.
- * @mas - the maple state.
+ * @mas: the maple state.
*/
static inline void mas_update_gap(struct ma_state *mas)
{
@@ -1678,8 +1677,8 @@ static inline void mas_update_gap(struct ma_state *mas)
/*
* mas_adopt_children() - Set the parent pointer of all nodes in @parent to
* @parent with the slot encoded.
- * @mas - the maple state (for the tree)
- * @parent - the maple encoded node containing the children.
+ * @mas: the maple state (for the tree)
+ * @parent: the maple encoded node containing the children.
*/
static inline void mas_adopt_children(struct ma_state *mas,
struct maple_enode *parent)
@@ -1701,8 +1700,8 @@ static inline void mas_adopt_children(struct ma_state *mas,
/*
* mas_put_in_tree() - Put a new node in the tree, smp_wmb(), and mark the old
* node as dead.
- * @mas - the maple state with the new node
- * @old_enode - The old maple encoded node to replace.
+ * @mas: the maple state with the new node
+ * @old_enode: The old maple encoded node to replace.
*/
static inline void mas_put_in_tree(struct ma_state *mas,
struct maple_enode *old_enode)
@@ -1730,8 +1729,8 @@ static inline void mas_put_in_tree(struct ma_state *mas,
* mas_replace_node() - Replace a node by putting it in the tree, marking it
* dead, and freeing it.
* the parent encoding to locate the maple node in the tree.
- * @mas - the ma_state with @mas->node pointing to the new node.
- * @old_enode - The old maple encoded node.
+ * @mas: the ma_state with @mas->node pointing to the new node.
+ * @old_enode: The old maple encoded node.
*/
static inline void mas_replace_node(struct ma_state *mas,
struct maple_enode *old_enode)
@@ -1796,7 +1795,6 @@ static inline void mab_shift_right(struct maple_big_node *b_node,
/*
* mab_middle_node() - Check if a middle node is needed (unlikely)
* @b_node: the maple_big_node that contains the data.
- * @size: the amount of data in the b_node
* @split: the potential split location
* @slot_count: the size that can be stored in a single node being considered.
*
@@ -1844,6 +1842,7 @@ static inline int mab_no_null_split(struct maple_big_node *b_node,
/*
* mab_calc_split() - Calculate the split location and if there needs to be two
* splits.
+ * @mas: The maple state
* @bn: The maple_big_node with the data
* @mid_split: The second split, if required. 0 otherwise.
*
@@ -2177,7 +2176,8 @@ static inline bool mas_next_sibling(struct ma_state *mas)
}
/*
- * mte_node_or_none() - Set the enode and state.
+ * mas_node_or_none() - Set the enode and state.
+ * @mas: the maple state
* @enode: The encoded maple node.
*
* Set the node to the enode and the status.
@@ -2196,6 +2196,8 @@ static inline void mas_node_or_none(struct ma_state *mas,
/*
* mas_wr_node_walk() - Find the correct offset for the index in the @mas.
+ * If @mas->index cannot be found within the containing
+ * node, we traverse to the last entry in the node.
* @wr_mas: The maple write state
*
* Uses mas_slot_locked() and does not need to worry about dead nodes.
@@ -2228,7 +2230,6 @@ static inline void mas_wr_node_walk(struct ma_wr_state *wr_mas)
/*
* mast_rebalance_next() - Rebalance against the next node
* @mast: The maple subtree state
- * @old_r: The encoded maple node to the right (next node).
*/
static inline void mast_rebalance_next(struct maple_subtree_state *mast)
{
@@ -2242,7 +2243,6 @@ static inline void mast_rebalance_next(struct maple_subtree_state *mast)
/*
* mast_rebalance_prev() - Rebalance against the previous node
* @mast: The maple subtree state
- * @old_l: The encoded maple node to the left (previous node)
*/
static inline void mast_rebalance_prev(struct maple_subtree_state *mast)
{
@@ -2393,9 +2393,9 @@ static inline unsigned char mas_mab_to_node(struct ma_state *mas,
/*
* mab_set_b_end() - Add entry to b_node at b_node->b_end and increment the end
* pointer.
- * @b_node - the big node to add the entry
- * @mas - the maple state to get the pivot (mas->max)
- * @entry - the entry to add, if NULL nothing happens.
+ * @b_node: the big node to add the entry
+ * @mas: the maple state to get the pivot (mas->max)
+ * @entry: the entry to add, if NULL nothing happens.
*/
static inline void mab_set_b_end(struct maple_big_node *b_node,
struct ma_state *mas,
@@ -2414,11 +2414,11 @@ static inline void mab_set_b_end(struct maple_big_node *b_node,
* mas_set_split_parent() - combine_then_separate helper function. Sets the parent
* of @mas->node to either @left or @right, depending on @slot and @split
*
- * @mas - the maple state with the node that needs a parent
- * @left - possible parent 1
- * @right - possible parent 2
- * @slot - the slot the mas->node was placed
- * @split - the split location between @left and @right
+ * @mas: the maple state with the node that needs a parent
+ * @left: possible parent 1
+ * @right: possible parent 2
+ * @slot: the slot the mas->node was placed
+ * @split: the split location between @left and @right
*/
static inline void mas_set_split_parent(struct ma_state *mas,
struct maple_enode *left,
@@ -2438,11 +2438,11 @@ static inline void mas_set_split_parent(struct ma_state *mas,
/*
* mte_mid_split_check() - Check if the next node passes the mid-split
- * @**l: Pointer to left encoded maple node.
- * @**m: Pointer to middle encoded maple node.
- * @**r: Pointer to right encoded maple node.
+ * @l: Pointer to left encoded maple node.
+ * @m: Pointer to middle encoded maple node.
+ * @r: Pointer to right encoded maple node.
* @slot: The offset
- * @*split: The split location.
+ * @split: The split location.
* @mid_split: The middle split.
*/
static inline void mte_mid_split_check(struct maple_enode **l,
@@ -2466,10 +2466,10 @@ static inline void mte_mid_split_check(struct maple_enode **l,
/*
* mast_set_split_parents() - Helper function to set three nodes parents. Slot
* is taken from @mast->l.
- * @mast - the maple subtree state
- * @left - the left node
- * @right - the right node
- * @split - the split location.
+ * @mast: the maple subtree state
+ * @left: the left node
+ * @right: the right node
+ * @split: the split location.
*/
static inline void mast_set_split_parents(struct maple_subtree_state *mast,
struct maple_enode *left,
@@ -2503,7 +2503,6 @@ static inline void mast_set_split_parents(struct maple_subtree_state *mast,
/*
* mas_topiary_node() - Dispose of a single node
* @mas: The maple state for pushing nodes
- * @enode: The encoded maple node
* @in_rcu: If the tree is in rcu mode
*
* The node will either be RCU freed or pushed back on the maple state.
@@ -2635,7 +2634,7 @@ static inline void mas_topiary_replace(struct ma_state *mas,
/*
* mas_wmb_replace() - Write memory barrier and replace
* @mas: The maple state
- * @old: The old maple encoded node that is being replaced.
+ * @old_enode: The old maple encoded node that is being replaced.
*
* Updates gap as necessary.
*/
@@ -2823,10 +2822,8 @@ dead_node:
* orig_l_mas->last is used in mas_consume to find the slots that will need to
* be either freed or destroyed. orig_l_mas->depth keeps track of the height of
* the new sub-tree in case the sub-tree becomes the full tree.
- *
- * Return: the number of elements in b_node during the last loop.
*/
-static int mas_spanning_rebalance(struct ma_state *mas,
+static void mas_spanning_rebalance(struct ma_state *mas,
struct maple_subtree_state *mast, unsigned char count)
{
unsigned char split, mid_split;
@@ -2942,7 +2939,7 @@ new_root:
mas->offset = l_mas.offset;
mas_wmb_replace(mas, old_enode);
mtree_range_walk(mas);
- return mast->bn->b_end;
+ return;
}
/*
@@ -2952,10 +2949,8 @@ new_root:
*
* Rebalance two nodes into a single node or two new nodes that are sufficient.
* Continue upwards until tree is sufficient.
- *
- * Return: the number of elements in b_node during the last loop.
*/
-static inline int mas_rebalance(struct ma_state *mas,
+static inline void mas_rebalance(struct ma_state *mas,
struct maple_big_node *b_node)
{
char empty_count = mas_mt_height(mas);
@@ -2976,9 +2971,6 @@ static inline int mas_rebalance(struct ma_state *mas,
* tries to combine the data in the same way. If one node contains the
* entire range of the tree, then that node is used as a new root node.
*/
- mas_node_count(mas, empty_count * 2 - 1);
- if (mas_is_err(mas))
- return 0;
mast.orig_l = &l_mas;
mast.orig_r = &r_mas;
@@ -3029,11 +3021,6 @@ static inline void mas_destroy_rebalance(struct ma_state *mas, unsigned char end
/* set up node. */
if (in_rcu) {
- /* Allocate for both left and right as well as parent. */
- mas_node_count(mas, 3);
- if (mas_is_err(mas))
- return;
-
newnode = mas_pop_node(mas);
} else {
newnode = &reuse;
@@ -3308,9 +3295,8 @@ static inline bool mas_push_data(struct ma_state *mas, int height,
* mas_split() - Split data that is too big for one node into two.
* @mas: The maple state
* @b_node: The maple big node
- * Return: 1 on success, 0 on failure.
*/
-static int mas_split(struct ma_state *mas, struct maple_big_node *b_node)
+static void mas_split(struct ma_state *mas, struct maple_big_node *b_node)
{
struct maple_subtree_state mast;
int height = 0;
@@ -3341,10 +3327,6 @@ static int mas_split(struct ma_state *mas, struct maple_big_node *b_node)
trace_ma_op(__func__, mas);
mas->depth = mas_mt_height(mas);
- /* Allocation failures will happen early. */
- mas_node_count(mas, 1 + mas->depth * 2);
- if (mas_is_err(mas))
- return 0;
mast.l = &l_mas;
mast.r = &r_mas;
@@ -3392,75 +3374,25 @@ static int mas_split(struct ma_state *mas, struct maple_big_node *b_node)
mas->node = l_mas.node;
mas_wmb_replace(mas, old);
mtree_range_walk(mas);
- return 1;
-}
-
-/*
- * mas_reuse_node() - Reuse the node to store the data.
- * @wr_mas: The maple write state
- * @bn: The maple big node
- * @end: The end of the data.
- *
- * Will always return false in RCU mode.
- *
- * Return: True if node was reused, false otherwise.
- */
-static inline bool mas_reuse_node(struct ma_wr_state *wr_mas,
- struct maple_big_node *bn, unsigned char end)
-{
- /* Need to be rcu safe. */
- if (mt_in_rcu(wr_mas->mas->tree))
- return false;
-
- if (end > bn->b_end) {
- int clear = mt_slots[wr_mas->type] - bn->b_end;
-
- memset(wr_mas->slots + bn->b_end, 0, sizeof(void *) * clear--);
- memset(wr_mas->pivots + bn->b_end, 0, sizeof(void *) * clear);
- }
- mab_mas_cp(bn, 0, bn->b_end, wr_mas->mas, false);
- return true;
+ return;
}
/*
* mas_commit_b_node() - Commit the big node into the tree.
* @wr_mas: The maple write state
* @b_node: The maple big node
- * @end: The end of the data.
*/
-static noinline_for_kasan int mas_commit_b_node(struct ma_wr_state *wr_mas,
- struct maple_big_node *b_node, unsigned char end)
+static noinline_for_kasan void mas_commit_b_node(struct ma_wr_state *wr_mas,
+ struct maple_big_node *b_node)
{
- struct maple_node *node;
- struct maple_enode *old_enode;
- unsigned char b_end = b_node->b_end;
- enum maple_type b_type = b_node->type;
-
- old_enode = wr_mas->mas->node;
- if ((b_end < mt_min_slots[b_type]) &&
- (!mte_is_root(old_enode)) &&
- (mas_mt_height(wr_mas->mas) > 1))
- return mas_rebalance(wr_mas->mas, b_node);
-
- if (b_end >= mt_slots[b_type])
- return mas_split(wr_mas->mas, b_node);
+ enum store_type type = wr_mas->mas->store_type;
- if (mas_reuse_node(wr_mas, b_node, end))
- goto reuse_node;
+ WARN_ON_ONCE(type != wr_rebalance && type != wr_split_store);
- mas_node_count(wr_mas->mas, 1);
- if (mas_is_err(wr_mas->mas))
- return 0;
+ if (type == wr_rebalance)
+ return mas_rebalance(wr_mas->mas, b_node);
- node = mas_pop_node(wr_mas->mas);
- node->parent = mas_mn(wr_mas->mas)->parent;
- wr_mas->mas->node = mt_mk_node(node, b_type);
- mab_mas_cp(b_node, 0, b_end, wr_mas->mas, false);
- mas_replace_node(wr_mas->mas, old_enode);
-reuse_node:
- mas_update_gap(wr_mas->mas);
- wr_mas->mas->end = b_end;
- return 1;
+ return mas_split(wr_mas->mas, b_node);
}
/*
@@ -3477,10 +3409,6 @@ static inline int mas_root_expand(struct ma_state *mas, void *entry)
unsigned long *pivots;
int slot = 0;
- mas_node_count(mas, 1);
- if (unlikely(mas_is_err(mas)))
- return 0;
-
node = mas_pop_node(mas);
pivots = ma_pivots(node, type);
slots = ma_slots(node, type);
@@ -3526,10 +3454,7 @@ static inline void mas_store_root(struct ma_state *mas, void *entry)
/*
* mas_is_span_wr() - Check if the write needs to be treated as a write that
* spans the node.
- * @mas: The maple state
- * @piv: The pivot value being written
- * @type: The maple node type
- * @entry: The data to write
+ * @wr_mas: The maple write state
*
* Spanning writes are writes that start in one node and end in another OR if
* the write of a %NULL will cause the node to end with a %NULL.
@@ -3609,7 +3534,7 @@ static bool mas_wr_walk(struct ma_wr_state *wr_mas)
return true;
}
-static bool mas_wr_walk_index(struct ma_wr_state *wr_mas)
+static void mas_wr_walk_index(struct ma_wr_state *wr_mas)
{
struct ma_state *mas = wr_mas->mas;
@@ -3618,11 +3543,9 @@ static bool mas_wr_walk_index(struct ma_wr_state *wr_mas)
wr_mas->content = mas_slot_locked(mas, wr_mas->slots,
mas->offset);
if (ma_is_leaf(wr_mas->type))
- return true;
+ return;
mas_wr_walk_traverse(wr_mas);
-
}
- return true;
}
/*
* mas_extend_spanning_null() - Extend a store of a %NULL to include surrounding %NULLs.
@@ -3730,10 +3653,8 @@ static void mte_destroy_walk(struct maple_enode *, struct maple_tree *);
* @entry: The entry to store.
*
* Only valid when the index == 0 and the last == ULONG_MAX
- *
- * Return 0 on error, 1 on success.
*/
-static inline int mas_new_root(struct ma_state *mas, void *entry)
+static inline void mas_new_root(struct ma_state *mas, void *entry)
{
struct maple_enode *root = mas_root_locked(mas);
enum maple_type type = maple_leaf_64;
@@ -3749,10 +3670,6 @@ static inline int mas_new_root(struct ma_state *mas, void *entry)
goto done;
}
- mas_node_count(mas, 1);
- if (mas_is_err(mas))
- return 0;
-
node = mas_pop_node(mas);
pivots = ma_pivots(node, type);
slots = ma_slots(node, type);
@@ -3769,7 +3686,7 @@ done:
if (xa_is_node(root))
mte_destroy_walk(root, mas->tree);
- return 1;
+ return;
}
/*
* mas_wr_spanning_store() - Create a subtree with the store operation completed
@@ -3777,10 +3694,8 @@ done:
* Note that mas is expected to point to the node which caused the store to
* span.
* @wr_mas: The maple write state
- *
- * Return: 0 on error, positive on success.
*/
-static inline int mas_wr_spanning_store(struct ma_wr_state *wr_mas)
+static noinline void mas_wr_spanning_store(struct ma_wr_state *wr_mas)
{
struct maple_subtree_state mast;
struct maple_big_node b_node;
@@ -3815,9 +3730,6 @@ static inline int mas_wr_spanning_store(struct ma_wr_state *wr_mas)
* entries per level plus a new root.
*/
height = mas_mt_height(mas);
- mas_node_count(mas, 1 + height * 3);
- if (mas_is_err(mas))
- return 0;
/*
* Set up right side. Need to get to the next offset after the spanning
@@ -3853,8 +3765,8 @@ static inline int mas_wr_spanning_store(struct ma_wr_state *wr_mas)
memset(&b_node, 0, sizeof(struct maple_big_node));
/* Copy l_mas and store the value in b_node. */
mas_store_b_node(&l_wr_mas, &b_node, l_mas.end);
- /* Copy r_mas into b_node. */
- if (r_mas.offset <= r_mas.end)
+ /* Copy r_mas into b_node if there is anything to copy. */
+ if (r_mas.max > r_mas.last)
mas_mab_cp(&r_mas, r_mas.offset, r_mas.end,
&b_node, b_node.b_end + 1);
else
@@ -3875,10 +3787,8 @@ static inline int mas_wr_spanning_store(struct ma_wr_state *wr_mas)
* @wr_mas: The maple write state
*
* Attempts to reuse the node, but may allocate.
- *
- * Return: True if stored, false otherwise
*/
-static inline bool mas_wr_node_store(struct ma_wr_state *wr_mas,
+static inline void mas_wr_node_store(struct ma_wr_state *wr_mas,
unsigned char new_end)
{
struct ma_state *mas = wr_mas->mas;
@@ -3889,11 +3799,6 @@ static inline bool mas_wr_node_store(struct ma_wr_state *wr_mas,
unsigned char copy_size, node_pivots = mt_pivots[wr_mas->type];
bool in_rcu = mt_in_rcu(mas->tree);
- /* Check if there is enough data. The room is enough. */
- if (!mte_is_root(mas->node) && (new_end <= mt_min_slots[wr_mas->type]) &&
- !(mas->mas_flags & MA_STATE_BULK))
- return false;
-
if (mas->last == wr_mas->end_piv)
offset_end++; /* don't copy this offset */
else if (unlikely(wr_mas->r_max == ULONG_MAX))
@@ -3901,10 +3806,6 @@ static inline bool mas_wr_node_store(struct ma_wr_state *wr_mas,
/* set up node. */
if (in_rcu) {
- mas_node_count(mas, 1);
- if (mas_is_err(mas))
- return false;
-
newnode = mas_pop_node(mas);
} else {
memset(&reuse, 0, sizeof(struct maple_node));
@@ -3960,16 +3861,14 @@ done:
trace_ma_write(__func__, mas, 0, wr_mas->entry);
mas_update_gap(mas);
mas->end = new_end;
- return true;
+ return;
}
/*
* mas_wr_slot_store: Attempt to store a value in a slot.
* @wr_mas: the maple write state
- *
- * Return: True if stored, false otherwise
*/
-static inline bool mas_wr_slot_store(struct ma_wr_state *wr_mas)
+static inline void mas_wr_slot_store(struct ma_wr_state *wr_mas)
{
struct ma_state *mas = wr_mas->mas;
unsigned char offset = mas->offset;
@@ -4001,7 +3900,7 @@ static inline bool mas_wr_slot_store(struct ma_wr_state *wr_mas)
wr_mas->pivots[offset + 1] = mas->last;
mas->offset++; /* Keep mas accurate. */
} else {
- return false;
+ return;
}
trace_ma_write(__func__, mas, 0, wr_mas->entry);
@@ -4012,7 +3911,7 @@ static inline bool mas_wr_slot_store(struct ma_wr_state *wr_mas)
if (!wr_mas->entry || gap)
mas_update_gap(mas);
- return true;
+ return;
}
static inline void mas_wr_extend_null(struct ma_wr_state *wr_mas)
@@ -4061,9 +3960,6 @@ static inline void mas_wr_end_piv(struct ma_wr_state *wr_mas)
wr_mas->end_piv = wr_mas->pivots[wr_mas->offset_end];
else
wr_mas->end_piv = wr_mas->mas->max;
-
- if (!wr_mas->entry)
- mas_wr_extend_null(wr_mas);
}
static inline unsigned char mas_wr_new_end(struct ma_wr_state *wr_mas)
@@ -4089,23 +3985,13 @@ static inline unsigned char mas_wr_new_end(struct ma_wr_state *wr_mas)
* This is currently unsafe in rcu mode since the end of the node may be cached
* by readers while the node contents may be updated which could result in
* inaccurate information.
- *
- * Return: True if appended, false otherwise
*/
-static inline bool mas_wr_append(struct ma_wr_state *wr_mas,
+static inline void mas_wr_append(struct ma_wr_state *wr_mas,
unsigned char new_end)
{
- struct ma_state *mas;
+ struct ma_state *mas = wr_mas->mas;
void __rcu **slots;
- unsigned char end;
-
- mas = wr_mas->mas;
- if (mt_in_rcu(mas->tree))
- return false;
-
- end = mas->end;
- if (mas->offset != end)
- return false;
+ unsigned char end = mas->end;
if (new_end < mt_pivots[wr_mas->type]) {
wr_mas->pivots[new_end] = wr_mas->pivots[end];
@@ -4139,7 +4025,7 @@ static inline bool mas_wr_append(struct ma_wr_state *wr_mas,
mas->end = new_end;
trace_ma_write(__func__, mas, new_end, wr_mas->entry);
- return true;
+ return;
}
/*
@@ -4155,79 +4041,235 @@ static void mas_wr_bnode(struct ma_wr_state *wr_mas)
trace_ma_write(__func__, wr_mas->mas, 0, wr_mas->entry);
memset(&b_node, 0, sizeof(struct maple_big_node));
mas_store_b_node(wr_mas, &b_node, wr_mas->offset_end);
- mas_commit_b_node(wr_mas, &b_node, wr_mas->mas->end);
+ mas_commit_b_node(wr_mas, &b_node);
}
-static inline void mas_wr_modify(struct ma_wr_state *wr_mas)
+/*
+ * mas_wr_store_entry() - Internal call to store a value
+ * @wr_mas: The maple write state
+ */
+static inline void mas_wr_store_entry(struct ma_wr_state *wr_mas)
{
struct ma_state *mas = wr_mas->mas;
- unsigned char new_end;
+ unsigned char new_end = mas_wr_new_end(wr_mas);
- /* Direct replacement */
- if (wr_mas->r_min == mas->index && wr_mas->r_max == mas->last) {
+ switch (mas->store_type) {
+ case wr_invalid:
+ MT_BUG_ON(mas->tree, 1);
+ return;
+ case wr_new_root:
+ mas_new_root(mas, wr_mas->entry);
+ break;
+ case wr_store_root:
+ mas_store_root(mas, wr_mas->entry);
+ break;
+ case wr_exact_fit:
rcu_assign_pointer(wr_mas->slots[mas->offset], wr_mas->entry);
if (!!wr_mas->entry ^ !!wr_mas->content)
mas_update_gap(mas);
- return;
+ break;
+ case wr_append:
+ mas_wr_append(wr_mas, new_end);
+ break;
+ case wr_slot_store:
+ mas_wr_slot_store(wr_mas);
+ break;
+ case wr_node_store:
+ mas_wr_node_store(wr_mas, new_end);
+ break;
+ case wr_spanning_store:
+ mas_wr_spanning_store(wr_mas);
+ break;
+ case wr_split_store:
+ case wr_rebalance:
+ mas_wr_bnode(wr_mas);
+ break;
+ }
+
+ return;
+}
+
+static inline void mas_wr_prealloc_setup(struct ma_wr_state *wr_mas)
+{
+ struct ma_state *mas = wr_mas->mas;
+
+ if (!mas_is_active(mas)) {
+ if (mas_is_start(mas))
+ goto set_content;
+
+ if (unlikely(mas_is_paused(mas)))
+ goto reset;
+
+ if (unlikely(mas_is_none(mas)))
+ goto reset;
+
+ if (unlikely(mas_is_overflow(mas)))
+ goto reset;
+
+ if (unlikely(mas_is_underflow(mas)))
+ goto reset;
}
/*
- * new_end exceeds the size of the maple node and cannot enter the fast
- * path.
+ * A less strict version of mas_is_span_wr() where we allow spanning
+ * writes within this node. This is to stop partial walks in
+ * mas_prealloc() from being reset.
*/
- new_end = mas_wr_new_end(wr_mas);
- if (new_end >= mt_slots[wr_mas->type])
- goto slow_path;
-
- /* Attempt to append */
- if (mas_wr_append(wr_mas, new_end))
- return;
+ if (mas->last > mas->max)
+ goto reset;
- if (new_end == mas->end && mas_wr_slot_store(wr_mas))
- return;
+ if (wr_mas->entry)
+ goto set_content;
- if (mas_wr_node_store(wr_mas, new_end))
- return;
+ if (mte_is_leaf(mas->node) && mas->last == mas->max)
+ goto reset;
- if (mas_is_err(mas))
- return;
+ goto set_content;
-slow_path:
- mas_wr_bnode(wr_mas);
+reset:
+ mas_reset(mas);
+set_content:
+ wr_mas->content = mas_start(mas);
}
-/*
- * mas_wr_store_entry() - Internal call to store a value
+/**
+ * mas_prealloc_calc() - Calculate number of nodes needed for a
+ * given store oepration
* @mas: The maple state
- * @entry: The entry to store.
+ * @entry: The entry to store into the tree
*
- * Return: The contents that was stored at the index.
+ * Return: Number of nodes required for preallocation.
*/
-static inline void *mas_wr_store_entry(struct ma_wr_state *wr_mas)
+static inline int mas_prealloc_calc(struct ma_state *mas, void *entry)
+{
+ int ret = mas_mt_height(mas) * 3 + 1;
+
+ switch (mas->store_type) {
+ case wr_invalid:
+ WARN_ON_ONCE(1);
+ break;
+ case wr_new_root:
+ ret = 1;
+ break;
+ case wr_store_root:
+ if (likely((mas->last != 0) || (mas->index != 0)))
+ ret = 1;
+ else if (((unsigned long) (entry) & 3) == 2)
+ ret = 1;
+ else
+ ret = 0;
+ break;
+ case wr_spanning_store:
+ ret = mas_mt_height(mas) * 3 + 1;
+ break;
+ case wr_split_store:
+ ret = mas_mt_height(mas) * 2 + 1;
+ break;
+ case wr_rebalance:
+ ret = mas_mt_height(mas) * 2 - 1;
+ break;
+ case wr_node_store:
+ ret = mt_in_rcu(mas->tree) ? 1 : 0;
+ break;
+ case wr_append:
+ case wr_exact_fit:
+ case wr_slot_store:
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/*
+ * mas_wr_store_type() - Set the store type for a given
+ * store operation.
+ * @wr_mas: The maple write state
+ */
+static inline void mas_wr_store_type(struct ma_wr_state *wr_mas)
{
struct ma_state *mas = wr_mas->mas;
+ unsigned char new_end;
- wr_mas->content = mas_start(mas);
- if (mas_is_none(mas) || mas_is_ptr(mas)) {
- mas_store_root(mas, wr_mas->entry);
- return wr_mas->content;
+ if (unlikely(mas_is_none(mas) || mas_is_ptr(mas))) {
+ mas->store_type = wr_store_root;
+ return;
}
if (unlikely(!mas_wr_walk(wr_mas))) {
- mas_wr_spanning_store(wr_mas);
- return wr_mas->content;
+ mas->store_type = wr_spanning_store;
+ return;
}
/* At this point, we are at the leaf node that needs to be altered. */
mas_wr_end_piv(wr_mas);
- /* New root for a single pointer */
+ if (!wr_mas->entry)
+ mas_wr_extend_null(wr_mas);
+
+ new_end = mas_wr_new_end(wr_mas);
+ if ((wr_mas->r_min == mas->index) && (wr_mas->r_max == mas->last)) {
+ mas->store_type = wr_exact_fit;
+ return;
+ }
+
if (unlikely(!mas->index && mas->last == ULONG_MAX)) {
- mas_new_root(mas, wr_mas->entry);
- return wr_mas->content;
+ mas->store_type = wr_new_root;
+ return;
+ }
+
+ /* Potential spanning rebalance collapsing a node */
+ if (new_end < mt_min_slots[wr_mas->type]) {
+ if (!mte_is_root(mas->node) && !(mas->mas_flags & MA_STATE_BULK)) {
+ mas->store_type = wr_rebalance;
+ return;
+ }
+ mas->store_type = wr_node_store;
+ return;
+ }
+
+ if (new_end >= mt_slots[wr_mas->type]) {
+ mas->store_type = wr_split_store;
+ return;
+ }
+
+ if (!mt_in_rcu(mas->tree) && (mas->offset == mas->end)) {
+ mas->store_type = wr_append;
+ return;
+ }
+
+ if ((new_end == mas->end) && (!mt_in_rcu(mas->tree) ||
+ (wr_mas->offset_end - mas->offset == 1))) {
+ mas->store_type = wr_slot_store;
+ return;
}
- mas_wr_modify(wr_mas);
- return wr_mas->content;
+ if (mte_is_root(mas->node) || (new_end >= mt_min_slots[wr_mas->type]) ||
+ (mas->mas_flags & MA_STATE_BULK)) {
+ mas->store_type = wr_node_store;
+ return;
+ }
+
+ mas->store_type = wr_invalid;
+ MAS_WARN_ON(mas, 1);
+}
+
+/**
+ * mas_wr_preallocate() - Preallocate enough nodes for a store operation
+ * @wr_mas: The maple write state
+ * @entry: The entry that will be stored
+ *
+ */
+static inline void mas_wr_preallocate(struct ma_wr_state *wr_mas, void *entry)
+{
+ struct ma_state *mas = wr_mas->mas;
+ int request;
+
+ mas_wr_prealloc_setup(wr_mas);
+ mas_wr_store_type(wr_mas);
+ request = mas_prealloc_calc(mas, entry);
+ if (!request)
+ return;
+
+ mas_node_count(mas, request);
}
/**
@@ -4260,26 +4302,24 @@ static inline void *mas_insert(struct ma_state *mas, void *entry)
if (wr_mas.content)
goto exists;
- if (mas_is_none(mas) || mas_is_ptr(mas)) {
- mas_store_root(mas, entry);
+ mas_wr_preallocate(&wr_mas, entry);
+ if (mas_is_err(mas))
return NULL;
- }
/* spanning writes always overwrite something */
- if (!mas_wr_walk(&wr_mas))
+ if (mas->store_type == wr_spanning_store)
goto exists;
/* At this point, we are at the leaf node that needs to be altered. */
- wr_mas.offset_end = mas->offset;
- wr_mas.end_piv = wr_mas.r_max;
-
- if (wr_mas.content || (mas->last > wr_mas.r_max))
- goto exists;
+ if (mas->store_type != wr_new_root && mas->store_type != wr_store_root) {
+ wr_mas.offset_end = mas->offset;
+ wr_mas.end_piv = wr_mas.r_max;
- if (!entry)
- return NULL;
+ if (wr_mas.content || (mas->last > wr_mas.r_max))
+ goto exists;
+ }
- mas_wr_modify(&wr_mas);
+ mas_wr_store_entry(&wr_mas);
return wr_mas.content;
exists:
@@ -4334,6 +4374,7 @@ int mas_alloc_cyclic(struct ma_state *mas, unsigned long *startp,
if (*next == 0)
mas->tree->ma_flags |= MT_FLAGS_ALLOC_WRAPPED;
+ mas_destroy(mas);
return ret;
}
EXPORT_SYMBOL(mas_alloc_cyclic);
@@ -4443,9 +4484,8 @@ no_entry:
* mas_prev_slot() - Get the entry in the previous slot
*
* @mas: The maple state
- * @max: The minimum starting range
+ * @min: The minimum starting range
* @empty: Can be empty
- * @set_underflow: Set the @mas->node to underflow state on limit.
*
* Return: The entry in the previous slot which is possibly NULL
*/
@@ -4528,6 +4568,7 @@ underflow:
/*
* mas_next_node() - Get the next node at the same level in the tree.
* @mas: The maple state
+ * @node: The maple node
* @max: The maximum pivot value to check.
*
* The next value will be mas->node[mas->offset] or the status will have
@@ -4618,8 +4659,6 @@ overflow:
* @mas: The maple state
* @max: The maximum starting range
* @empty: Can be empty
- * @set_overflow: Should @mas->node be set to overflow when the limit is
- * reached.
*
* Return: The entry in the next slot which is possibly NULL
*/
@@ -5153,9 +5192,9 @@ EXPORT_SYMBOL_GPL(mas_empty_area_rev);
/*
* mte_dead_leaves() - Mark all leaves of a node as dead.
- * @mas: The maple state
+ * @enode: the encoded node
+ * @mt: the maple tree
* @slots: Pointer to the slot array
- * @type: The maple node type
*
* Must hold the write lock.
*
@@ -5361,47 +5400,6 @@ static inline void mte_destroy_walk(struct maple_enode *enode,
mt_destroy_walk(enode, mt, true);
}
}
-
-static void mas_wr_store_setup(struct ma_wr_state *wr_mas)
-{
- if (!mas_is_active(wr_mas->mas)) {
- if (mas_is_start(wr_mas->mas))
- return;
-
- if (unlikely(mas_is_paused(wr_mas->mas)))
- goto reset;
-
- if (unlikely(mas_is_none(wr_mas->mas)))
- goto reset;
-
- if (unlikely(mas_is_overflow(wr_mas->mas)))
- goto reset;
-
- if (unlikely(mas_is_underflow(wr_mas->mas)))
- goto reset;
- }
-
- /*
- * A less strict version of mas_is_span_wr() where we allow spanning
- * writes within this node. This is to stop partial walks in
- * mas_prealloc() from being reset.
- */
- if (wr_mas->mas->last > wr_mas->mas->max)
- goto reset;
-
- if (wr_mas->entry)
- return;
-
- if (mte_is_leaf(wr_mas->mas->node) &&
- wr_mas->mas->last == wr_mas->mas->max)
- goto reset;
-
- return;
-
-reset:
- mas_reset(wr_mas->mas);
-}
-
/* Interface */
/**
@@ -5410,13 +5408,12 @@ reset:
* @entry: The entry to store.
*
* The @mas->index and @mas->last is used to set the range for the @entry.
- * Note: The @mas should have pre-allocated entries to ensure there is memory to
- * store the entry. Please see mas_expected_entries()/mas_destroy() for more details.
*
* Return: the first entry between mas->index and mas->last or %NULL.
*/
void *mas_store(struct ma_state *mas, void *entry)
{
+ int request;
MA_WR_STATE(wr_mas, mas, entry);
trace_ma_write(__func__, mas, 0, entry);
@@ -5437,8 +5434,25 @@ void *mas_store(struct ma_state *mas, void *entry)
* want to examine what happens if a single store operation was to
* overwrite multiple entries within a self-balancing B-Tree.
*/
- mas_wr_store_setup(&wr_mas);
+ mas_wr_prealloc_setup(&wr_mas);
+ mas_wr_store_type(&wr_mas);
+ if (mas->mas_flags & MA_STATE_PREALLOC) {
+ mas_wr_store_entry(&wr_mas);
+ MAS_WR_BUG_ON(&wr_mas, mas_is_err(mas));
+ return wr_mas.content;
+ }
+
+ request = mas_prealloc_calc(mas, entry);
+ if (!request)
+ goto store;
+
+ mas_node_count(mas, request);
+ if (mas_is_err(mas))
+ return NULL;
+
+store:
mas_wr_store_entry(&wr_mas);
+ mas_destroy(mas);
return wr_mas.content;
}
EXPORT_SYMBOL_GPL(mas_store);
@@ -5454,19 +5468,28 @@ EXPORT_SYMBOL_GPL(mas_store);
*/
int mas_store_gfp(struct ma_state *mas, void *entry, gfp_t gfp)
{
+ unsigned long index = mas->index;
+ unsigned long last = mas->last;
MA_WR_STATE(wr_mas, mas, entry);
+ int ret = 0;
- mas_wr_store_setup(&wr_mas);
- trace_ma_write(__func__, mas, 0, entry);
retry:
- mas_wr_store_entry(&wr_mas);
- if (unlikely(mas_nomem(mas, gfp)))
+ mas_wr_preallocate(&wr_mas, entry);
+ if (unlikely(mas_nomem(mas, gfp))) {
+ if (!entry)
+ __mas_set_range(mas, index, last);
goto retry;
+ }
- if (unlikely(mas_is_err(mas)))
- return xa_err(mas->node);
+ if (mas_is_err(mas)) {
+ ret = xa_err(mas->node);
+ goto out;
+ }
- return 0;
+ mas_wr_store_entry(&wr_mas);
+out:
+ mas_destroy(mas);
+ return ret;
}
EXPORT_SYMBOL_GPL(mas_store_gfp);
@@ -5480,7 +5503,19 @@ void mas_store_prealloc(struct ma_state *mas, void *entry)
{
MA_WR_STATE(wr_mas, mas, entry);
- mas_wr_store_setup(&wr_mas);
+ if (mas->store_type == wr_store_root) {
+ mas_wr_prealloc_setup(&wr_mas);
+ goto store;
+ }
+
+ mas_wr_walk_descend(&wr_mas);
+ if (mas->store_type != wr_spanning_store) {
+ /* set wr_mas->content to current slot */
+ wr_mas.content = mas_slot_locked(mas, wr_mas.slots, mas->offset);
+ mas_wr_end_piv(&wr_mas);
+ }
+
+store:
trace_ma_write(__func__, mas, 0, entry);
mas_wr_store_entry(&wr_mas);
MAS_WR_BUG_ON(&wr_mas, mas_is_err(mas));
@@ -5499,70 +5534,25 @@ EXPORT_SYMBOL_GPL(mas_store_prealloc);
int mas_preallocate(struct ma_state *mas, void *entry, gfp_t gfp)
{
MA_WR_STATE(wr_mas, mas, entry);
- unsigned char node_size;
- int request = 1;
- int ret;
-
-
- if (unlikely(!mas->index && mas->last == ULONG_MAX))
- goto ask_now;
-
- mas_wr_store_setup(&wr_mas);
- wr_mas.content = mas_start(mas);
- /* Root expand */
- if (unlikely(mas_is_none(mas) || mas_is_ptr(mas)))
- goto ask_now;
-
- if (unlikely(!mas_wr_walk(&wr_mas))) {
- /* Spanning store, use worst case for now */
- request = 1 + mas_mt_height(mas) * 3;
- goto ask_now;
- }
-
- /* At this point, we are at the leaf node that needs to be altered. */
- /* Exact fit, no nodes needed. */
- if (wr_mas.r_min == mas->index && wr_mas.r_max == mas->last)
- return 0;
-
- mas_wr_end_piv(&wr_mas);
- node_size = mas_wr_new_end(&wr_mas);
+ int ret = 0;
+ int request;
- /* Slot store, does not require additional nodes */
- if (node_size == mas->end) {
- /* reuse node */
- if (!mt_in_rcu(mas->tree))
- return 0;
- /* shifting boundary */
- if (wr_mas.offset_end - mas->offset == 1)
- return 0;
- }
+ mas_wr_prealloc_setup(&wr_mas);
+ mas_wr_store_type(&wr_mas);
+ request = mas_prealloc_calc(mas, entry);
+ if (!request)
+ return ret;
- if (node_size >= mt_slots[wr_mas.type]) {
- /* Split, worst case for now. */
- request = 1 + mas_mt_height(mas) * 2;
- goto ask_now;
+ mas_node_count_gfp(mas, request, gfp);
+ if (mas_is_err(mas)) {
+ mas_set_alloc_req(mas, 0);
+ ret = xa_err(mas->node);
+ mas_destroy(mas);
+ mas_reset(mas);
+ return ret;
}
- /* New root needs a single node */
- if (unlikely(mte_is_root(mas->node)))
- goto ask_now;
-
- /* Potential spanning rebalance collapsing a node, use worst-case */
- if (node_size - 1 <= mt_min_slots[wr_mas.type])
- request = mas_mt_height(mas) * 2 - 1;
-
- /* node store, slot store needs one node */
-ask_now:
- mas_node_count_gfp(mas, request, gfp);
mas->mas_flags |= MA_STATE_PREALLOC;
- if (likely(!mas_is_err(mas)))
- return 0;
-
- mas_set_alloc_req(mas, 0);
- ret = xa_err(mas->node);
- mas_reset(mas);
- mas_destroy(mas);
- mas_reset(mas);
return ret;
}
EXPORT_SYMBOL_GPL(mas_preallocate);
@@ -5588,7 +5578,8 @@ void mas_destroy(struct ma_state *mas)
*/
if (mas->mas_flags & MA_STATE_REBALANCE) {
unsigned char end;
-
+ if (mas_is_err(mas))
+ mas_reset(mas);
mas_start(mas);
mtree_range_walk(mas);
end = mas->end + 1;
@@ -6248,24 +6239,32 @@ EXPORT_SYMBOL_GPL(mas_find_range_rev);
void *mas_erase(struct ma_state *mas)
{
void *entry;
+ unsigned long index = mas->index;
MA_WR_STATE(wr_mas, mas, NULL);
if (!mas_is_active(mas) || !mas_is_start(mas))
mas->status = ma_start;
- /* Retry unnecessary when holding the write lock. */
+write_retry:
entry = mas_state_walk(mas);
if (!entry)
return NULL;
-write_retry:
/* Must reset to ensure spanning writes of last slot are detected */
mas_reset(mas);
- mas_wr_store_setup(&wr_mas);
- mas_wr_store_entry(&wr_mas);
- if (mas_nomem(mas, GFP_KERNEL))
+ mas_wr_preallocate(&wr_mas, NULL);
+ if (mas_nomem(mas, GFP_KERNEL)) {
+ /* in case the range of entry changed when unlocked */
+ mas->index = mas->last = index;
goto write_retry;
+ }
+ if (mas_is_err(mas))
+ goto out;
+
+ mas_wr_store_entry(&wr_mas);
+out:
+ mas_destroy(mas);
return entry;
}
EXPORT_SYMBOL_GPL(mas_erase);
@@ -6280,10 +6279,8 @@ EXPORT_SYMBOL_GPL(mas_erase);
bool mas_nomem(struct ma_state *mas, gfp_t gfp)
__must_hold(mas->tree->ma_lock)
{
- if (likely(mas->node != MA_ERROR(-ENOMEM))) {
- mas_destroy(mas);
+ if (likely(mas->node != MA_ERROR(-ENOMEM)))
return false;
- }
if (gfpflags_allow_blocking(gfp) && !mt_external_lock(mas->tree)) {
mtree_unlock(mas->tree);
@@ -6360,7 +6357,7 @@ int mtree_store_range(struct maple_tree *mt, unsigned long index,
unsigned long last, void *entry, gfp_t gfp)
{
MA_STATE(mas, mt, index, last);
- MA_WR_STATE(wr_mas, &mas, entry);
+ int ret = 0;
trace_ma_write(__func__, &mas, 0, entry);
if (WARN_ON_ONCE(xa_is_advanced(entry)))
@@ -6370,16 +6367,10 @@ int mtree_store_range(struct maple_tree *mt, unsigned long index,
return -EINVAL;
mtree_lock(mt);
-retry:
- mas_wr_store_entry(&wr_mas);
- if (mas_nomem(&mas, gfp))
- goto retry;
-
+ ret = mas_store_gfp(&mas, entry, gfp);
mtree_unlock(mt);
- if (mas_is_err(&mas))
- return xa_err(mas.node);
- return 0;
+ return ret;
}
EXPORT_SYMBOL(mtree_store_range);
@@ -6415,6 +6406,7 @@ int mtree_insert_range(struct maple_tree *mt, unsigned long first,
unsigned long last, void *entry, gfp_t gfp)
{
MA_STATE(ms, mt, first, last);
+ int ret = 0;
if (WARN_ON_ONCE(xa_is_advanced(entry)))
return -EINVAL;
@@ -6430,9 +6422,10 @@ retry:
mtree_unlock(mt);
if (mas_is_err(&ms))
- return xa_err(ms.node);
+ ret = xa_err(ms.node);
- return 0;
+ mas_destroy(&ms);
+ return ret;
}
EXPORT_SYMBOL(mtree_insert_range);
@@ -6487,6 +6480,7 @@ retry:
unlock:
mtree_unlock(mt);
+ mas_destroy(&mas);
return ret;
}
EXPORT_SYMBOL(mtree_alloc_range);
@@ -6568,6 +6562,7 @@ retry:
unlock:
mtree_unlock(mt);
+ mas_destroy(&mas);
return ret;
}
EXPORT_SYMBOL(mtree_alloc_rrange);
@@ -7000,6 +6995,19 @@ void mt_set_non_kernel(unsigned int val)
kmem_cache_set_non_kernel(maple_node_cache, val);
}
+extern void kmem_cache_set_callback(struct kmem_cache *cachep,
+ void (*callback)(void *));
+void mt_set_callback(void (*callback)(void *))
+{
+ kmem_cache_set_callback(maple_node_cache, callback);
+}
+
+extern void kmem_cache_set_private(struct kmem_cache *cachep, void *private);
+void mt_set_private(void *private)
+{
+ kmem_cache_set_private(maple_node_cache, private);
+}
+
extern unsigned long kmem_cache_get_alloc(struct kmem_cache *);
unsigned long mt_get_alloc_size(void)
{
@@ -7184,7 +7192,6 @@ static void mt_dump_arange64(const struct maple_tree *mt, void *entry,
enum mt_dump_format format)
{
struct maple_arange_64 *node = &mte_to_node(entry)->ma64;
- bool leaf = mte_is_leaf(entry);
unsigned long first = min;
int i;
@@ -7218,19 +7225,22 @@ static void mt_dump_arange64(const struct maple_tree *mt, void *entry,
break;
if (last == 0 && i > 0)
break;
- if (leaf)
- mt_dump_entry(mt_slot(mt, node->slot, i),
- first, last, depth + 1, format);
- else if (node->slot[i])
+ if (node->slot[i])
mt_dump_node(mt, mt_slot(mt, node->slot, i),
first, last, depth + 1, format);
if (last == max)
break;
if (last > max) {
- pr_err("node %p last (%lu) > max (%lu) at pivot %d!\n",
+ switch(format) {
+ case mt_dump_hex:
+ pr_err("node %p last (%lx) > max (%lx) at pivot %d!\n",
node, last, max, i);
- break;
+ break;
+ case mt_dump_dec:
+ pr_err("node %p last (%lu) > max (%lu) at pivot %d!\n",
+ node, last, max, i);
+ }
}
first = last + 1;
}
@@ -7569,14 +7579,14 @@ static void mt_validate_nulls(struct maple_tree *mt)
* 2. The gap is correctly set in the parents
*/
void mt_validate(struct maple_tree *mt)
+ __must_hold(mas->tree->ma_lock)
{
unsigned char end;
MA_STATE(mas, mt, 0, 0);
- rcu_read_lock();
mas_start(&mas);
if (!mas_is_active(&mas))
- goto done;
+ return;
while (!mte_is_leaf(mas.node))
mas_descend(&mas);
@@ -7597,9 +7607,6 @@ void mt_validate(struct maple_tree *mt)
mas_dfs_postorder(&mas, ULONG_MAX);
}
mt_validate_nulls(mt);
-done:
- rcu_read_unlock();
-
}
EXPORT_SYMBOL_GPL(mt_validate);
@@ -7633,6 +7640,40 @@ void mas_dump(const struct ma_state *mas)
break;
}
+ pr_err("Store Type: ");
+ switch (mas->store_type) {
+ case wr_invalid:
+ pr_err("invalid store type\n");
+ break;
+ case wr_new_root:
+ pr_err("new_root\n");
+ break;
+ case wr_store_root:
+ pr_err("store_root\n");
+ break;
+ case wr_exact_fit:
+ pr_err("exact_fit\n");
+ break;
+ case wr_split_store:
+ pr_err("split_store\n");
+ break;
+ case wr_slot_store:
+ pr_err("slot_store\n");
+ break;
+ case wr_append:
+ pr_err("append\n");
+ break;
+ case wr_node_store:
+ pr_err("node_store\n");
+ break;
+ case wr_spanning_store:
+ pr_err("spanning_store\n");
+ break;
+ case wr_rebalance:
+ pr_err("rebalance\n");
+ break;
+ }
+
pr_err("[%u/%u] index=%lx last=%lx\n", mas->offset, mas->end,
mas->index, mas->last);
pr_err(" min=%lx max=%lx alloc=%p, depth=%u, flags=%x\n",
diff --git a/lib/math/Makefile b/lib/math/Makefile
index 91fcdb0c9efe..3ef11305f8d2 100644
--- a/lib/math/Makefile
+++ b/lib/math/Makefile
@@ -5,5 +5,7 @@ obj-$(CONFIG_CORDIC) += cordic.o
obj-$(CONFIG_PRIME_NUMBERS) += prime_numbers.o
obj-$(CONFIG_RATIONAL) += rational.o
+obj-$(CONFIG_INT_POW_TEST) += tests/int_pow_kunit.o
obj-$(CONFIG_TEST_DIV64) += test_div64.o
+obj-$(CONFIG_TEST_MULDIV64) += test_mul_u64_u64_div_u64.o
obj-$(CONFIG_RATIONAL_KUNIT_TEST) += rational-test.o
diff --git a/lib/math/div64.c b/lib/math/div64.c
index 191761b1b623..5faa29208bdb 100644
--- a/lib/math/div64.c
+++ b/lib/math/div64.c
@@ -186,55 +186,84 @@ EXPORT_SYMBOL(iter_div_u64_rem);
#ifndef mul_u64_u64_div_u64
u64 mul_u64_u64_div_u64(u64 a, u64 b, u64 c)
{
- u64 res = 0, div, rem;
- int shift;
+ if (ilog2(a) + ilog2(b) <= 62)
+ return div64_u64(a * b, c);
- /* can a * b overflow ? */
- if (ilog2(a) + ilog2(b) > 62) {
- /*
- * Note that the algorithm after the if block below might lose
- * some precision and the result is more exact for b > a. So
- * exchange a and b if a is bigger than b.
- *
- * For example with a = 43980465100800, b = 100000000, c = 1000000000
- * the below calculation doesn't modify b at all because div == 0
- * and then shift becomes 45 + 26 - 62 = 9 and so the result
- * becomes 4398035251080. However with a and b swapped the exact
- * result is calculated (i.e. 4398046510080).
- */
- if (a > b)
- swap(a, b);
+#if defined(__SIZEOF_INT128__)
+
+ /* native 64x64=128 bits multiplication */
+ u128 prod = (u128)a * b;
+ u64 n_lo = prod, n_hi = prod >> 64;
+
+#else
+
+ /* perform a 64x64=128 bits multiplication manually */
+ u32 a_lo = a, a_hi = a >> 32, b_lo = b, b_hi = b >> 32;
+ u64 x, y, z;
+
+ x = (u64)a_lo * b_lo;
+ y = (u64)a_lo * b_hi + (u32)(x >> 32);
+ z = (u64)a_hi * b_hi + (u32)(y >> 32);
+ y = (u64)a_hi * b_lo + (u32)y;
+ z += (u32)(y >> 32);
+ x = (y << 32) + (u32)x;
+
+ u64 n_lo = x, n_hi = z;
+
+#endif
+
+ /* make sure c is not zero, trigger exception otherwise */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdiv-by-zero"
+ if (unlikely(c == 0))
+ return 1/0;
+#pragma GCC diagnostic pop
+
+ int shift = __builtin_ctzll(c);
+ /* try reducing the fraction in case the dividend becomes <= 64 bits */
+ if ((n_hi >> shift) == 0) {
+ u64 n = shift ? (n_lo >> shift) | (n_hi << (64 - shift)) : n_lo;
+
+ return div64_u64(n, c >> shift);
/*
- * (b * a) / c is equal to
- *
- * (b / c) * a +
- * (b % c) * a / c
- *
- * if nothing overflows. Can the 1st multiplication
- * overflow? Yes, but we do not care: this can only
- * happen if the end result can't fit in u64 anyway.
- *
- * So the code below does
- *
- * res = (b / c) * a;
- * b = b % c;
+ * The remainder value if needed would be:
+ * res = div64_u64_rem(n, c >> shift, &rem);
+ * rem = (rem << shift) + (n_lo - (n << shift));
*/
- div = div64_u64_rem(b, c, &rem);
- res = div * a;
- b = rem;
-
- shift = ilog2(a) + ilog2(b) - 62;
- if (shift > 0) {
- /* drop precision */
- b >>= shift;
- c >>= shift;
- if (!c)
- return res;
- }
}
- return res + div64_u64(a * b, c);
+ if (n_hi >= c) {
+ /* overflow: result is unrepresentable in a u64 */
+ return -1;
+ }
+
+ /* Do the full 128 by 64 bits division */
+
+ shift = __builtin_clzll(c);
+ c <<= shift;
+
+ int p = 64 + shift;
+ u64 res = 0;
+ bool carry;
+
+ do {
+ carry = n_hi >> 63;
+ shift = carry ? 1 : __builtin_clzll(n_hi);
+ if (p < shift)
+ break;
+ p -= shift;
+ n_hi <<= shift;
+ n_hi |= n_lo >> (64 - shift);
+ n_lo <<= shift;
+ if (carry || (n_hi >= c)) {
+ n_hi -= c;
+ res |= 1ULL << p;
+ }
+ } while (n_hi);
+ /* The remainder value if needed would be n_hi << p */
+
+ return res;
}
EXPORT_SYMBOL(mul_u64_u64_div_u64);
#endif
diff --git a/lib/math/prime_numbers.c b/lib/math/prime_numbers.c
index d3b64b10da1c..9a17ee9af93a 100644
--- a/lib/math/prime_numbers.c
+++ b/lib/math/prime_numbers.c
@@ -311,4 +311,5 @@ module_exit(primes_exit);
module_param_named(selftest, selftest_max, ulong, 0400);
MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("Prime number library");
MODULE_LICENSE("GPL");
diff --git a/lib/math/rational-test.c b/lib/math/rational-test.c
index 01611ddff420..47486a95f088 100644
--- a/lib/math/rational-test.c
+++ b/lib/math/rational-test.c
@@ -53,4 +53,5 @@ static struct kunit_suite rational_test_suite = {
kunit_test_suites(&rational_test_suite);
+MODULE_DESCRIPTION("Rational fractions unit test");
MODULE_LICENSE("GPL v2");
diff --git a/lib/math/rational.c b/lib/math/rational.c
index ec59d426ea63..d2c34e629ee1 100644
--- a/lib/math/rational.c
+++ b/lib/math/rational.c
@@ -108,4 +108,5 @@ void rational_best_approximation(
EXPORT_SYMBOL(rational_best_approximation);
+MODULE_DESCRIPTION("Rational fraction support library");
MODULE_LICENSE("GPL v2");
diff --git a/lib/math/test_mul_u64_u64_div_u64.c b/lib/math/test_mul_u64_u64_div_u64.c
new file mode 100644
index 000000000000..58d058de4e73
--- /dev/null
+++ b/lib/math/test_mul_u64_u64_div_u64.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 BayLibre SAS
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/math64.h>
+
+typedef struct { u64 a; u64 b; u64 c; u64 result; } test_params;
+
+static test_params test_values[] = {
+/* this contains many edge values followed by a couple random values */
+{ 0xb, 0x7, 0x3, 0x19 },
+{ 0xffff0000, 0xffff0000, 0xf, 0x1110eeef00000000 },
+{ 0xffffffff, 0xffffffff, 0x1, 0xfffffffe00000001 },
+{ 0xffffffff, 0xffffffff, 0x2, 0x7fffffff00000000 },
+{ 0x1ffffffff, 0xffffffff, 0x2, 0xfffffffe80000000 },
+{ 0x1ffffffff, 0xffffffff, 0x3, 0xaaaaaaa9aaaaaaab },
+{ 0x1ffffffff, 0x1ffffffff, 0x4, 0xffffffff00000000 },
+{ 0xffff000000000000, 0xffff000000000000, 0xffff000000000001, 0xfffeffffffffffff },
+{ 0x3333333333333333, 0x3333333333333333, 0x5555555555555555, 0x1eb851eb851eb851 },
+{ 0x7fffffffffffffff, 0x2, 0x3, 0x5555555555555554 },
+{ 0xffffffffffffffff, 0x2, 0x8000000000000000, 0x3 },
+{ 0xffffffffffffffff, 0x2, 0xc000000000000000, 0x2 },
+{ 0xffffffffffffffff, 0x4000000000000004, 0x8000000000000000, 0x8000000000000007 },
+{ 0xffffffffffffffff, 0x4000000000000001, 0x8000000000000000, 0x8000000000000001 },
+{ 0xffffffffffffffff, 0x8000000000000001, 0xffffffffffffffff, 0x8000000000000001 },
+{ 0xfffffffffffffffe, 0x8000000000000001, 0xffffffffffffffff, 0x8000000000000000 },
+{ 0xffffffffffffffff, 0x8000000000000001, 0xfffffffffffffffe, 0x8000000000000001 },
+{ 0xffffffffffffffff, 0x8000000000000001, 0xfffffffffffffffd, 0x8000000000000002 },
+{ 0x7fffffffffffffff, 0xffffffffffffffff, 0xc000000000000000, 0xaaaaaaaaaaaaaaa8 },
+{ 0xffffffffffffffff, 0x7fffffffffffffff, 0xa000000000000000, 0xccccccccccccccca },
+{ 0xffffffffffffffff, 0x7fffffffffffffff, 0x9000000000000000, 0xe38e38e38e38e38b },
+{ 0x7fffffffffffffff, 0x7fffffffffffffff, 0x5000000000000000, 0xccccccccccccccc9 },
+{ 0xffffffffffffffff, 0xfffffffffffffffe, 0xffffffffffffffff, 0xfffffffffffffffe },
+{ 0xe6102d256d7ea3ae, 0x70a77d0be4c31201, 0xd63ec35ab3220357, 0x78f8bf8cc86c6e18 },
+{ 0xf53bae05cb86c6e1, 0x3847b32d2f8d32e0, 0xcfd4f55a647f403c, 0x42687f79d8998d35 },
+{ 0x9951c5498f941092, 0x1f8c8bfdf287a251, 0xa3c8dc5f81ea3fe2, 0x1d887cb25900091f },
+{ 0x374fee9daa1bb2bb, 0x0d0bfbff7b8ae3ef, 0xc169337bd42d5179, 0x03bb2dbaffcbb961 },
+{ 0xeac0d03ac10eeaf0, 0x89be05dfa162ed9b, 0x92bb1679a41f0e4b, 0xdc5f5cc9e270d216 },
+};
+
+/*
+ * The above table can be verified with the following shell script:
+ *
+ * #!/bin/sh
+ * sed -ne 's/^{ \+\(.*\), \+\(.*\), \+\(.*\), \+\(.*\) },$/\1 \2 \3 \4/p' \
+ * lib/math/test_mul_u64_u64_div_u64.c |
+ * while read a b c r; do
+ * expected=$( printf "obase=16; ibase=16; %X * %X / %X\n" $a $b $c | bc )
+ * given=$( printf "%X\n" $r )
+ * if [ "$expected" = "$given" ]; then
+ * echo "$a * $b / $c = $r OK"
+ * else
+ * echo "$a * $b / $c = $r is wrong" >&2
+ * echo "should be equivalent to 0x$expected" >&2
+ * exit 1
+ * fi
+ * done
+ */
+
+static int __init test_init(void)
+{
+ int i;
+
+ pr_info("Starting mul_u64_u64_div_u64() test\n");
+
+ for (i = 0; i < ARRAY_SIZE(test_values); i++) {
+ u64 a = test_values[i].a;
+ u64 b = test_values[i].b;
+ u64 c = test_values[i].c;
+ u64 expected_result = test_values[i].result;
+ u64 result = mul_u64_u64_div_u64(a, b, c);
+
+ if (result != expected_result) {
+ pr_err("ERROR: 0x%016llx * 0x%016llx / 0x%016llx\n", a, b, c);
+ pr_err("ERROR: expected result: %016llx\n", expected_result);
+ pr_err("ERROR: obtained result: %016llx\n", result);
+ }
+ }
+
+ pr_info("Completed mul_u64_u64_div_u64() test\n");
+ return 0;
+}
+
+static void __exit test_exit(void)
+{
+}
+
+module_init(test_init);
+module_exit(test_exit);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("mul_u64_u64_div_u64() test module");
diff --git a/lib/math/tests/Makefile b/lib/math/tests/Makefile
new file mode 100644
index 000000000000..6a169123320a
--- /dev/null
+++ b/lib/math/tests/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_INT_POW_TEST) += int_pow_kunit.o
diff --git a/lib/math/tests/int_pow_kunit.c b/lib/math/tests/int_pow_kunit.c
new file mode 100644
index 000000000000..34b33677d458
--- /dev/null
+++ b/lib/math/tests/int_pow_kunit.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <kunit/test.h>
+#include <linux/math.h>
+
+struct test_case_params {
+ u64 base;
+ unsigned int exponent;
+ u64 expected_result;
+ const char *name;
+};
+
+static const struct test_case_params params[] = {
+ { 64, 0, 1, "Power of zero" },
+ { 64, 1, 64, "Power of one"},
+ { 0, 5, 0, "Base zero" },
+ { 1, 64, 1, "Base one" },
+ { 2, 2, 4, "Two squared"},
+ { 2, 3, 8, "Two cubed"},
+ { 5, 5, 3125, "Five raised to the fifth power" },
+ { U64_MAX, 1, U64_MAX, "Max base" },
+ { 2, 63, 9223372036854775808ULL, "Large result"},
+};
+
+static void get_desc(const struct test_case_params *tc, char *desc)
+{
+ strscpy(desc, tc->name, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(int_pow, params, get_desc);
+
+static void int_pow_test(struct kunit *test)
+{
+ const struct test_case_params *tc = (const struct test_case_params *)test->param_value;
+
+ KUNIT_EXPECT_EQ(test, tc->expected_result, int_pow(tc->base, tc->exponent));
+}
+
+static struct kunit_case math_int_pow_test_cases[] = {
+ KUNIT_CASE_PARAM(int_pow_test, int_pow_gen_params),
+ {}
+};
+
+static struct kunit_suite int_pow_test_suite = {
+ .name = "math-int_pow",
+ .test_cases = math_int_pow_test_cases,
+};
+
+kunit_test_suites(&int_pow_test_suite);
+
+MODULE_DESCRIPTION("math.int_pow KUnit test suite");
+MODULE_LICENSE("GPL");
diff --git a/lib/memcpy_kunit.c b/lib/memcpy_kunit.c
index 20ea9038c3ff..d36933554e46 100644
--- a/lib/memcpy_kunit.c
+++ b/lib/memcpy_kunit.c
@@ -510,4 +510,5 @@ static struct kunit_suite memcpy_test_suite = {
kunit_test_suite(memcpy_test_suite);
+MODULE_DESCRIPTION("test cases for memcpy(), memmove(), and memset()");
MODULE_LICENSE("GPL");
diff --git a/lib/objagg.c b/lib/objagg.c
index 1e248629ed64..363e43e849ac 100644
--- a/lib/objagg.c
+++ b/lib/objagg.c
@@ -167,6 +167,9 @@ static int objagg_obj_parent_assign(struct objagg *objagg,
{
void *delta_priv;
+ if (WARN_ON(!objagg_obj_is_root(parent)))
+ return -EINVAL;
+
delta_priv = objagg->ops->delta_create(objagg->priv, parent->obj,
objagg_obj->obj);
if (IS_ERR(delta_priv))
@@ -421,7 +424,7 @@ static struct objagg_obj *__objagg_obj_get(struct objagg *objagg, void *obj)
*
* There are 3 main options this function wraps:
* 1) The object according to "obj" already exist. In that case
- * the reference counter is incrementes and the object is returned.
+ * the reference counter is incremented and the object is returned.
* 2) The object does not exist, but it can be aggregated within
* another object. In that case, user ops->delta_create() is called
* to obtain delta data and a new object is created with returned
@@ -903,20 +906,6 @@ static const struct objagg_opt_algo *objagg_opt_algos[] = {
[OBJAGG_OPT_ALGO_SIMPLE_GREEDY] = &objagg_opt_simple_greedy,
};
-static int objagg_hints_obj_cmp(struct rhashtable_compare_arg *arg,
- const void *obj)
-{
- struct rhashtable *ht = arg->ht;
- struct objagg_hints *objagg_hints =
- container_of(ht, struct objagg_hints, node_ht);
- const struct objagg_ops *ops = objagg_hints->ops;
- const char *ptr = obj;
-
- ptr += ht->p.key_offset;
- return ops->hints_obj_cmp ? ops->hints_obj_cmp(ptr, arg->key) :
- memcmp(ptr, arg->key, ht->p.key_len);
-}
-
/**
* objagg_hints_get - obtains hints instance
* @objagg: objagg instance
@@ -955,7 +944,6 @@ struct objagg_hints *objagg_hints_get(struct objagg *objagg,
offsetof(struct objagg_hints_node, obj);
objagg_hints->ht_params.head_offset =
offsetof(struct objagg_hints_node, ht_node);
- objagg_hints->ht_params.obj_cmpfn = objagg_hints_obj_cmp;
err = rhashtable_init(&objagg_hints->node_ht, &objagg_hints->ht_params);
if (err)
diff --git a/lib/objpool.c b/lib/objpool.c
index cfdc02420884..fd108fe0d095 100644
--- a/lib/objpool.c
+++ b/lib/objpool.c
@@ -50,7 +50,7 @@ objpool_init_percpu_slots(struct objpool_head *pool, int nr_objs,
{
int i, cpu_count = 0;
- for (i = 0; i < pool->nr_cpus; i++) {
+ for (i = 0; i < nr_cpu_ids; i++) {
struct objpool_slot *slot;
int nodes, size, rc;
@@ -60,8 +60,8 @@ objpool_init_percpu_slots(struct objpool_head *pool, int nr_objs,
continue;
/* compute how many objects to be allocated with this slot */
- nodes = nr_objs / num_possible_cpus();
- if (cpu_count < (nr_objs % num_possible_cpus()))
+ nodes = nr_objs / pool->nr_possible_cpus;
+ if (cpu_count < (nr_objs % pool->nr_possible_cpus))
nodes++;
cpu_count++;
@@ -76,7 +76,7 @@ objpool_init_percpu_slots(struct objpool_head *pool, int nr_objs,
* mimimal size of vmalloc is one page since vmalloc would
* always align the requested size to page size
*/
- if (pool->gfp & GFP_ATOMIC)
+ if ((pool->gfp & GFP_ATOMIC) == GFP_ATOMIC)
slot = kmalloc_node(size, pool->gfp, cpu_to_node(i));
else
slot = __vmalloc_node(size, sizeof(void *), pool->gfp,
@@ -103,7 +103,7 @@ static void objpool_fini_percpu_slots(struct objpool_head *pool)
if (!pool->cpu_slots)
return;
- for (i = 0; i < pool->nr_cpus; i++)
+ for (i = 0; i < nr_cpu_ids; i++)
kvfree(pool->cpu_slots[i]);
kfree(pool->cpu_slots);
}
@@ -130,13 +130,13 @@ int objpool_init(struct objpool_head *pool, int nr_objs, int object_size,
/* initialize objpool pool */
memset(pool, 0, sizeof(struct objpool_head));
- pool->nr_cpus = nr_cpu_ids;
+ pool->nr_possible_cpus = num_possible_cpus();
pool->obj_size = object_size;
pool->capacity = capacity;
pool->gfp = gfp & ~__GFP_ZERO;
pool->context = context;
pool->release = release;
- slot_size = pool->nr_cpus * sizeof(struct objpool_slot);
+ slot_size = nr_cpu_ids * sizeof(struct objpool_slot);
pool->cpu_slots = kzalloc(slot_size, pool->gfp);
if (!pool->cpu_slots)
return -ENOMEM;
@@ -152,106 +152,6 @@ int objpool_init(struct objpool_head *pool, int nr_objs, int object_size,
}
EXPORT_SYMBOL_GPL(objpool_init);
-/* adding object to slot, abort if the slot was already full */
-static inline int
-objpool_try_add_slot(void *obj, struct objpool_head *pool, int cpu)
-{
- struct objpool_slot *slot = pool->cpu_slots[cpu];
- uint32_t head, tail;
-
- /* loading tail and head as a local snapshot, tail first */
- tail = READ_ONCE(slot->tail);
-
- do {
- head = READ_ONCE(slot->head);
- /* fault caught: something must be wrong */
- WARN_ON_ONCE(tail - head > pool->nr_objs);
- } while (!try_cmpxchg_acquire(&slot->tail, &tail, tail + 1));
-
- /* now the tail position is reserved for the given obj */
- WRITE_ONCE(slot->entries[tail & slot->mask], obj);
- /* update sequence to make this obj available for pop() */
- smp_store_release(&slot->last, tail + 1);
-
- return 0;
-}
-
-/* reclaim an object to object pool */
-int objpool_push(void *obj, struct objpool_head *pool)
-{
- unsigned long flags;
- int rc;
-
- /* disable local irq to avoid preemption & interruption */
- raw_local_irq_save(flags);
- rc = objpool_try_add_slot(obj, pool, raw_smp_processor_id());
- raw_local_irq_restore(flags);
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(objpool_push);
-
-/* try to retrieve object from slot */
-static inline void *objpool_try_get_slot(struct objpool_head *pool, int cpu)
-{
- struct objpool_slot *slot = pool->cpu_slots[cpu];
- /* load head snapshot, other cpus may change it */
- uint32_t head = smp_load_acquire(&slot->head);
-
- while (head != READ_ONCE(slot->last)) {
- void *obj;
-
- /*
- * data visibility of 'last' and 'head' could be out of
- * order since memory updating of 'last' and 'head' are
- * performed in push() and pop() independently
- *
- * before any retrieving attempts, pop() must guarantee
- * 'last' is behind 'head', that is to say, there must
- * be available objects in slot, which could be ensured
- * by condition 'last != head && last - head <= nr_objs'
- * that is equivalent to 'last - head - 1 < nr_objs' as
- * 'last' and 'head' are both unsigned int32
- */
- if (READ_ONCE(slot->last) - head - 1 >= pool->nr_objs) {
- head = READ_ONCE(slot->head);
- continue;
- }
-
- /* obj must be retrieved before moving forward head */
- obj = READ_ONCE(slot->entries[head & slot->mask]);
-
- /* move head forward to mark it's consumption */
- if (try_cmpxchg_release(&slot->head, &head, head + 1))
- return obj;
- }
-
- return NULL;
-}
-
-/* allocate an object from object pool */
-void *objpool_pop(struct objpool_head *pool)
-{
- void *obj = NULL;
- unsigned long flags;
- int i, cpu;
-
- /* disable local irq to avoid preemption & interruption */
- raw_local_irq_save(flags);
-
- cpu = raw_smp_processor_id();
- for (i = 0; i < num_possible_cpus(); i++) {
- obj = objpool_try_get_slot(pool, cpu);
- if (obj)
- break;
- cpu = cpumask_next_wrap(cpu, cpu_possible_mask, -1, 1);
- }
- raw_local_irq_restore(flags);
-
- return obj;
-}
-EXPORT_SYMBOL_GPL(objpool_pop);
-
/* release whole objpool forcely */
void objpool_free(struct objpool_head *pool)
{
diff --git a/lib/overflow_kunit.c b/lib/overflow_kunit.c
index 4ef31b0bb74d..2abc78367dd1 100644
--- a/lib/overflow_kunit.c
+++ b/lib/overflow_kunit.c
@@ -668,7 +668,6 @@ DEFINE_TEST_ALLOC(devm_kzalloc, devm_kfree, 1, 1, 0);
static void overflow_allocation_test(struct kunit *test)
{
- const char device_name[] = "overflow-test";
struct device *dev;
int count = 0;
@@ -678,7 +677,7 @@ static void overflow_allocation_test(struct kunit *test)
} while (0)
/* Create dummy device for devm_kmalloc()-family tests. */
- dev = kunit_device_register(test, device_name);
+ dev = kunit_device_register(test, "overflow-test");
KUNIT_ASSERT_FALSE_MSG(test, IS_ERR(dev),
"Cannot register test device\n");
@@ -1178,14 +1177,28 @@ struct foo {
s16 array[] __counted_by(counter);
};
+struct bar {
+ int a;
+ u32 counter;
+ s16 array[];
+};
+
static void DEFINE_FLEX_test(struct kunit *test)
{
- DEFINE_RAW_FLEX(struct foo, two, array, 2);
+ /* Using _RAW_ on a __counted_by struct will initialize "counter" to zero */
+ DEFINE_RAW_FLEX(struct foo, two_but_zero, array, 2);
+#if __has_attribute(__counted_by__)
+ int expected_raw_size = sizeof(struct foo);
+#else
+ int expected_raw_size = sizeof(struct foo) + 2 * sizeof(s16);
+#endif
+ /* Without annotation, it will always be on-stack size. */
+ DEFINE_RAW_FLEX(struct bar, two, array, 2);
DEFINE_FLEX(struct foo, eight, array, counter, 8);
DEFINE_FLEX(struct foo, empty, array, counter, 0);
- KUNIT_EXPECT_EQ(test, __struct_size(two),
- sizeof(struct foo) + sizeof(s16) + sizeof(s16));
+ KUNIT_EXPECT_EQ(test, __struct_size(two_but_zero), expected_raw_size);
+ KUNIT_EXPECT_EQ(test, __struct_size(two), sizeof(struct bar) + 2 * sizeof(s16));
KUNIT_EXPECT_EQ(test, __struct_size(eight), 24);
KUNIT_EXPECT_EQ(test, __struct_size(empty), sizeof(struct foo));
}
@@ -1223,4 +1236,5 @@ static struct kunit_suite overflow_test_suite = {
kunit_test_suite(overflow_test_suite);
+MODULE_DESCRIPTION("Test cases for arithmetic overflow checks");
MODULE_LICENSE("Dual MIT/GPL");
diff --git a/lib/percpu_counter.c b/lib/percpu_counter.c
index 44dd133594d4..2891f94a11c6 100644
--- a/lib/percpu_counter.c
+++ b/lib/percpu_counter.c
@@ -73,17 +73,50 @@ void percpu_counter_set(struct percpu_counter *fbc, s64 amount)
EXPORT_SYMBOL(percpu_counter_set);
/*
- * local_irq_save() is needed to make the function irq safe:
- * - The slow path would be ok as protected by an irq-safe spinlock.
- * - this_cpu_add would be ok as it is irq-safe by definition.
- * But:
- * The decision slow path/fast path and the actual update must be atomic, too.
+ * Add to a counter while respecting batch size.
+ *
+ * There are 2 implementations, both dealing with the following problem:
+ *
+ * The decision slow path/fast path and the actual update must be atomic.
* Otherwise a call in process context could check the current values and
* decide that the fast path can be used. If now an interrupt occurs before
* the this_cpu_add(), and the interrupt updates this_cpu(*fbc->counters),
* then the this_cpu_add() that is executed after the interrupt has completed
* can produce values larger than "batch" or even overflows.
*/
+#ifdef CONFIG_HAVE_CMPXCHG_LOCAL
+/*
+ * Safety against interrupts is achieved in 2 ways:
+ * 1. the fast path uses local cmpxchg (note: no lock prefix)
+ * 2. the slow path operates with interrupts disabled
+ */
+void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch)
+{
+ s64 count;
+ unsigned long flags;
+
+ count = this_cpu_read(*fbc->counters);
+ do {
+ if (unlikely(abs(count + amount) >= batch)) {
+ raw_spin_lock_irqsave(&fbc->lock, flags);
+ /*
+ * Note: by now we might have migrated to another CPU
+ * or the value might have changed.
+ */
+ count = __this_cpu_read(*fbc->counters);
+ fbc->count += count + amount;
+ __this_cpu_sub(*fbc->counters, count);
+ raw_spin_unlock_irqrestore(&fbc->lock, flags);
+ return;
+ }
+ } while (!this_cpu_try_cmpxchg(*fbc->counters, &count, count + amount));
+}
+#else
+/*
+ * local_irq_save() is used to make the function irq safe:
+ * - The slow path would be ok as protected by an irq-safe spinlock.
+ * - this_cpu_add would be ok as it is irq-safe by definition.
+ */
void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch)
{
s64 count;
@@ -101,6 +134,7 @@ void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch)
}
local_irq_restore(flags);
}
+#endif
EXPORT_SYMBOL(percpu_counter_add_batch);
/*
@@ -175,7 +209,7 @@ int __percpu_counter_init_many(struct percpu_counter *fbc, s64 amount,
INIT_LIST_HEAD(&fbc[i].list);
#endif
fbc[i].count = amount;
- fbc[i].counters = (void *)counters + (i * counter_size);
+ fbc[i].counters = (void __percpu *)counters + i * counter_size;
debug_percpu_counter_activate(&fbc[i]);
}
diff --git a/lib/pldmfw/pldmfw.c b/lib/pldmfw/pldmfw.c
index 54e1809a38fd..6e1581b9a616 100644
--- a/lib/pldmfw/pldmfw.c
+++ b/lib/pldmfw/pldmfw.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2018-2019, Intel Corporation. */
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/crc32.h>
#include <linux/device.h>
#include <linux/firmware.h>
diff --git a/lib/plist.c b/lib/plist.c
index 0d86ed7a76ac..c6bce1226874 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -47,8 +47,8 @@ static void plist_check_list(struct list_head *top)
plist_check_prev_next(top, prev, next);
while (next != top) {
- prev = next;
- next = prev->next;
+ WRITE_ONCE(prev, next);
+ WRITE_ONCE(next, prev->next);
plist_check_prev_next(top, prev, next);
}
}
@@ -72,7 +72,7 @@ static void plist_check_head(struct plist_head *head)
*/
void plist_add(struct plist_node *node, struct plist_head *head)
{
- struct plist_node *first, *iter, *prev = NULL;
+ struct plist_node *first, *iter, *prev = NULL, *last, *reverse_iter;
struct list_head *node_next = &head->node_list;
plist_check_head(head);
@@ -83,16 +83,26 @@ void plist_add(struct plist_node *node, struct plist_head *head)
goto ins_node;
first = iter = plist_first(head);
+ last = reverse_iter = list_entry(first->prio_list.prev, struct plist_node, prio_list);
do {
if (node->prio < iter->prio) {
node_next = &iter->node_list;
break;
+ } else if (node->prio >= reverse_iter->prio) {
+ prev = reverse_iter;
+ iter = list_entry(reverse_iter->prio_list.next,
+ struct plist_node, prio_list);
+ if (likely(reverse_iter != last))
+ node_next = &iter->node_list;
+ break;
}
prev = iter;
iter = list_entry(iter->prio_list.next,
struct plist_node, prio_list);
+ reverse_iter = list_entry(reverse_iter->prio_list.prev,
+ struct plist_node, prio_list);
} while (iter != first);
if (!prev || prev->prio != node->prio)
@@ -255,6 +265,32 @@ static int __init plist_test(void)
}
printk(KERN_DEBUG "end plist test\n");
+
+ /* Worst case test for plist_add() */
+ unsigned int test_data[241];
+
+ for (i = 0; i < ARRAY_SIZE(test_data); i++)
+ test_data[i] = i;
+
+ ktime_t start, end, time_elapsed = 0;
+
+ plist_head_init(&test_head);
+
+ for (i = 0; i < ARRAY_SIZE(test_node); i++) {
+ plist_node_init(test_node + i, 0);
+ test_node[i].prio = test_data[i];
+ }
+
+ for (i = 0; i < ARRAY_SIZE(test_node); i++) {
+ if (plist_node_empty(test_node + i)) {
+ start = ktime_get();
+ plist_add(test_node + i, &test_head);
+ end = ktime_get();
+ time_elapsed += (end - start);
+ }
+ }
+
+ pr_debug("plist_add worst case test time elapsed %lld\n", time_elapsed);
return 0;
}
diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile
index 385a94aa0b99..29127dd05d63 100644
--- a/lib/raid6/Makefile
+++ b/lib/raid6/Makefile
@@ -33,27 +33,8 @@ CFLAGS_REMOVE_vpermxor8.o += -msoft-float
endif
endif
-# The GCC option -ffreestanding is required in order to compile code containing
-# ARM/NEON intrinsics in a non C99-compliant environment (such as the kernel)
-ifeq ($(CONFIG_KERNEL_MODE_NEON),y)
-NEON_FLAGS := -ffreestanding
-# Enable <arm_neon.h>
-NEON_FLAGS += -isystem $(shell $(CC) -print-file-name=include)
-ifeq ($(ARCH),arm)
-NEON_FLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=neon
-endif
-CFLAGS_recov_neon_inner.o += $(NEON_FLAGS)
-ifeq ($(ARCH),arm64)
-CFLAGS_REMOVE_recov_neon_inner.o += -mgeneral-regs-only
-CFLAGS_REMOVE_neon1.o += -mgeneral-regs-only
-CFLAGS_REMOVE_neon2.o += -mgeneral-regs-only
-CFLAGS_REMOVE_neon4.o += -mgeneral-regs-only
-CFLAGS_REMOVE_neon8.o += -mgeneral-regs-only
-endif
-endif
-
quiet_cmd_unroll = UNROLL $@
- cmd_unroll = $(AWK) -v N=$* -f $(srctree)/$(src)/unroll.awk < $< > $@
+ 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
@@ -75,10 +56,16 @@ 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 += $(NEON_FLAGS)
-CFLAGS_neon2.o += $(NEON_FLAGS)
-CFLAGS_neon4.o += $(NEON_FLAGS)
-CFLAGS_neon8.o += $(NEON_FLAGS)
+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)
diff --git a/lib/random32.c b/lib/random32.c
index 32060b852668..0a5a0e3600c8 100644
--- a/lib/random32.c
+++ b/lib/random32.c
@@ -40,7 +40,7 @@
#include <linux/sched.h>
#include <linux/bitops.h>
#include <linux/slab.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
/**
* prandom_u32_state - seeded pseudo-random number generator.
diff --git a/lib/rbtree.c b/lib/rbtree.c
index 5114eda6309c..989c2d615f92 100644
--- a/lib/rbtree.c
+++ b/lib/rbtree.c
@@ -297,9 +297,9 @@ ____rb_erase_color(struct rb_node *parent, struct rb_root *root,
* / \ / \
* N S --> N sl
* / \ \
- * sl Sr S
+ * sl sr S
* \
- * Sr
+ * sr
*
* Note: p might be red, and then both
* p and sl are red after rotation(which
@@ -312,9 +312,9 @@ ____rb_erase_color(struct rb_node *parent, struct rb_root *root,
* / \ / \
* N sl --> P S
* \ / \
- * S N Sr
+ * S N sr
* \
- * Sr
+ * sr
*/
tmp1 = tmp2->rb_right;
WRITE_ONCE(sibling->rb_left, tmp1);
diff --git a/lib/rhashtable.c b/lib/rhashtable.c
index 6ae2ba8e06a2..6c902639728b 100644
--- a/lib/rhashtable.c
+++ b/lib/rhashtable.c
@@ -130,7 +130,8 @@ static union nested_table *nested_table_alloc(struct rhashtable *ht,
if (ntbl)
return ntbl;
- ntbl = kzalloc(PAGE_SIZE, GFP_ATOMIC);
+ ntbl = alloc_hooks_tag(ht->alloc_tag,
+ kmalloc_noprof(PAGE_SIZE, GFP_ATOMIC|__GFP_ZERO));
if (ntbl && leaf) {
for (i = 0; i < PAGE_SIZE / sizeof(ntbl[0]); i++)
@@ -157,7 +158,8 @@ static struct bucket_table *nested_bucket_table_alloc(struct rhashtable *ht,
size = sizeof(*tbl) + sizeof(tbl->buckets[0]);
- tbl = kzalloc(size, gfp);
+ tbl = alloc_hooks_tag(ht->alloc_tag,
+ kmalloc_noprof(size, gfp|__GFP_ZERO));
if (!tbl)
return NULL;
@@ -181,11 +183,13 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
int i;
static struct lock_class_key __key;
- tbl = kvzalloc(struct_size(tbl, buckets, nbuckets), gfp);
+ tbl = alloc_hooks_tag(ht->alloc_tag,
+ kvmalloc_node_noprof(struct_size(tbl, buckets, nbuckets),
+ gfp|__GFP_ZERO, NUMA_NO_NODE));
size = nbuckets;
- if (tbl == NULL && (gfp & ~__GFP_NOFAIL) != GFP_KERNEL) {
+ if (tbl == NULL && !gfpflags_allow_blocking(gfp)) {
tbl = nested_bucket_table_alloc(ht, nbuckets, gfp);
nbuckets = 0;
}
@@ -1016,7 +1020,7 @@ static u32 rhashtable_jhash2(const void *key, u32 length, u32 seed)
* .obj_hashfn = my_hash_fn,
* };
*/
-int rhashtable_init(struct rhashtable *ht,
+int rhashtable_init_noprof(struct rhashtable *ht,
const struct rhashtable_params *params)
{
struct bucket_table *tbl;
@@ -1031,6 +1035,8 @@ int rhashtable_init(struct rhashtable *ht,
spin_lock_init(&ht->lock);
memcpy(&ht->p, params, sizeof(*params));
+ alloc_tag_record(ht->alloc_tag);
+
if (params->min_size)
ht->p.min_size = roundup_pow_of_two(params->min_size);
@@ -1076,7 +1082,7 @@ int rhashtable_init(struct rhashtable *ht,
return 0;
}
-EXPORT_SYMBOL_GPL(rhashtable_init);
+EXPORT_SYMBOL_GPL(rhashtable_init_noprof);
/**
* rhltable_init - initialize a new hash list table
@@ -1087,15 +1093,15 @@ EXPORT_SYMBOL_GPL(rhashtable_init);
*
* See documentation for rhashtable_init.
*/
-int rhltable_init(struct rhltable *hlt, const struct rhashtable_params *params)
+int rhltable_init_noprof(struct rhltable *hlt, const struct rhashtable_params *params)
{
int err;
- err = rhashtable_init(&hlt->ht, params);
+ err = rhashtable_init_noprof(&hlt->ht, params);
hlt->ht.rhlist = true;
return err;
}
-EXPORT_SYMBOL_GPL(rhltable_init);
+EXPORT_SYMBOL_GPL(rhltable_init_noprof);
static void rhashtable_free_one(struct rhashtable *ht, struct rhash_head *obj,
void (*free_fn)(void *ptr, void *arg),
diff --git a/lib/sbitmap.c b/lib/sbitmap.c
index 1e453f825c05..d3412984170c 100644
--- a/lib/sbitmap.c
+++ b/lib/sbitmap.c
@@ -60,12 +60,30 @@ static inline void update_alloc_hint_after_get(struct sbitmap *sb,
/*
* See if we have deferred clears that we can batch move
*/
-static inline bool sbitmap_deferred_clear(struct sbitmap_word *map)
+static inline bool sbitmap_deferred_clear(struct sbitmap_word *map,
+ unsigned int depth, unsigned int alloc_hint, bool wrap)
{
- unsigned long mask;
+ unsigned long mask, word_mask;
- if (!READ_ONCE(map->cleared))
- return false;
+ guard(raw_spinlock_irqsave)(&map->swap_lock);
+
+ if (!map->cleared) {
+ if (depth == 0)
+ return false;
+
+ word_mask = (~0UL) >> (BITS_PER_LONG - depth);
+ /*
+ * The current behavior is to always retry after moving
+ * ->cleared to word, and we change it to retry in case
+ * of any free bits. To avoid an infinite loop, we need
+ * to take wrap & alloc_hint into account, otherwise a
+ * soft lockup may occur.
+ */
+ if (!wrap && alloc_hint)
+ word_mask &= ~((1UL << alloc_hint) - 1);
+
+ return (READ_ONCE(map->word) & word_mask) != word_mask;
+ }
/*
* First get a stable cleared mask, setting the old mask to 0.
@@ -85,6 +103,7 @@ int sbitmap_init_node(struct sbitmap *sb, unsigned int depth, int shift,
bool alloc_hint)
{
unsigned int bits_per_word;
+ int i;
if (shift < 0)
shift = sbitmap_calculate_shift(depth);
@@ -116,6 +135,9 @@ int sbitmap_init_node(struct sbitmap *sb, unsigned int depth, int shift,
return -ENOMEM;
}
+ for (i = 0; i < sb->map_nr; i++)
+ raw_spin_lock_init(&sb->map[i].swap_lock);
+
return 0;
}
EXPORT_SYMBOL_GPL(sbitmap_init_node);
@@ -126,7 +148,7 @@ void sbitmap_resize(struct sbitmap *sb, unsigned int depth)
unsigned int i;
for (i = 0; i < sb->map_nr; i++)
- sbitmap_deferred_clear(&sb->map[i]);
+ sbitmap_deferred_clear(&sb->map[i], 0, 0, 0);
sb->depth = depth;
sb->map_nr = DIV_ROUND_UP(sb->depth, bits_per_word);
@@ -179,7 +201,7 @@ static int sbitmap_find_bit_in_word(struct sbitmap_word *map,
alloc_hint, wrap);
if (nr != -1)
break;
- if (!sbitmap_deferred_clear(map))
+ if (!sbitmap_deferred_clear(map, depth, alloc_hint, wrap))
break;
} while (1);
@@ -496,7 +518,7 @@ unsigned long __sbitmap_queue_get_batch(struct sbitmap_queue *sbq, int nr_tags,
unsigned int map_depth = __map_depth(sb, index);
unsigned long val;
- sbitmap_deferred_clear(map);
+ sbitmap_deferred_clear(map, 0, 0, 0);
val = READ_ONCE(map->word);
if (val == (1UL << (map_depth - 1)) - 1)
goto next;
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index 7bc2220fea80..473b2646f71c 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -11,6 +11,7 @@
#include <linux/kmemleak.h>
#include <linux/bvec.h>
#include <linux/uio.h>
+#include <linux/folio_queue.h>
/**
* sg_next - return the next scatterlist entry in a list
@@ -1262,6 +1263,67 @@ static ssize_t extract_kvec_to_sg(struct iov_iter *iter,
}
/*
+ * Extract up to sg_max folios from an FOLIOQ-type iterator and add them to
+ * the scatterlist. The pages are not pinned.
+ */
+static ssize_t extract_folioq_to_sg(struct iov_iter *iter,
+ ssize_t maxsize,
+ struct sg_table *sgtable,
+ unsigned int sg_max,
+ iov_iter_extraction_t extraction_flags)
+{
+ const struct folio_queue *folioq = iter->folioq;
+ struct scatterlist *sg = sgtable->sgl + sgtable->nents;
+ unsigned int slot = iter->folioq_slot;
+ ssize_t ret = 0;
+ size_t offset = iter->iov_offset;
+
+ BUG_ON(!folioq);
+
+ if (slot >= folioq_nr_slots(folioq)) {
+ folioq = folioq->next;
+ if (WARN_ON_ONCE(!folioq))
+ return 0;
+ slot = 0;
+ }
+
+ do {
+ struct folio *folio = folioq_folio(folioq, slot);
+ size_t fsize = folioq_folio_size(folioq, slot);
+
+ if (offset < fsize) {
+ size_t part = umin(maxsize - ret, fsize - offset);
+
+ sg_set_page(sg, folio_page(folio, 0), part, offset);
+ sgtable->nents++;
+ sg++;
+ sg_max--;
+ offset += part;
+ ret += part;
+ }
+
+ if (offset >= fsize) {
+ offset = 0;
+ slot++;
+ if (slot >= folioq_nr_slots(folioq)) {
+ if (!folioq->next) {
+ WARN_ON_ONCE(ret < iter->count);
+ break;
+ }
+ folioq = folioq->next;
+ slot = 0;
+ }
+ }
+ } while (sg_max > 0 && ret < maxsize);
+
+ iter->folioq = folioq;
+ iter->folioq_slot = slot;
+ iter->iov_offset = offset;
+ iter->count -= ret;
+ return ret;
+}
+
+/*
* Extract up to sg_max folios from an XARRAY-type iterator and add them to
* the scatterlist. The pages are not pinned.
*/
@@ -1323,8 +1385,8 @@ static ssize_t extract_xarray_to_sg(struct iov_iter *iter,
* addition of @sg_max elements.
*
* The pages referred to by UBUF- and IOVEC-type iterators are extracted and
- * pinned; BVEC-, KVEC- and XARRAY-type are extracted but aren't pinned; PIPE-
- * and DISCARD-type are not supported.
+ * pinned; BVEC-, KVEC-, FOLIOQ- and XARRAY-type are extracted but aren't
+ * pinned; DISCARD-type is not supported.
*
* No end mark is placed on the scatterlist; that's left to the caller.
*
@@ -1356,6 +1418,9 @@ ssize_t extract_iter_to_sg(struct iov_iter *iter, size_t maxsize,
case ITER_KVEC:
return extract_kvec_to_sg(iter, maxsize, sgtable, sg_max,
extraction_flags);
+ case ITER_FOLIOQ:
+ return extract_folioq_to_sg(iter, maxsize, sgtable, sg_max,
+ extraction_flags);
case ITER_XARRAY:
return extract_xarray_to_sg(iter, maxsize, sgtable, sg_max,
extraction_flags);
diff --git a/lib/siphash.c b/lib/siphash.c
index 15bc5b6f368c..9e4e88752d2e 100644
--- a/lib/siphash.c
+++ b/lib/siphash.c
@@ -10,7 +10,7 @@
*/
#include <linux/siphash.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#if defined(CONFIG_DCACHE_WORD_ACCESS) && BITS_PER_LONG == 64
#include <linux/dcache.h>
diff --git a/lib/siphash_kunit.c b/lib/siphash_kunit.c
index a3c697e8be35..26bd4e8dc03e 100644
--- a/lib/siphash_kunit.c
+++ b/lib/siphash_kunit.c
@@ -194,4 +194,5 @@ static struct kunit_suite siphash_test_suite = {
kunit_test_suite(siphash_test_suite);
MODULE_AUTHOR("Jason A. Donenfeld <Jason@zx2c4.com>");
+MODULE_DESCRIPTION("Test cases for siphash.c");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/lib/slub_kunit.c b/lib/slub_kunit.c
index 4ce960438806..33564f965958 100644
--- a/lib/slub_kunit.c
+++ b/lib/slub_kunit.c
@@ -5,6 +5,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/rcupdate.h>
#include "../mm/slab.h"
static struct kunit_resource resource;
@@ -140,7 +141,7 @@ static void test_kmalloc_redzone_access(struct kunit *test)
{
struct kmem_cache *s = test_kmem_cache_create("TestSlub_RZ_kmalloc", 32,
SLAB_KMALLOC|SLAB_STORE_USER|SLAB_RED_ZONE);
- u8 *p = kmalloc_trace(s, GFP_KERNEL, 18);
+ u8 *p = alloc_hooks(__kmalloc_cache_noprof(s, GFP_KERNEL, 18));
kasan_disable_current();
@@ -157,6 +158,40 @@ static void test_kmalloc_redzone_access(struct kunit *test)
kmem_cache_destroy(s);
}
+struct test_kfree_rcu_struct {
+ struct rcu_head rcu;
+};
+
+static void test_kfree_rcu(struct kunit *test)
+{
+ struct kmem_cache *s;
+ struct test_kfree_rcu_struct *p;
+
+ if (IS_BUILTIN(CONFIG_SLUB_KUNIT_TEST))
+ kunit_skip(test, "can't do kfree_rcu() when test is built-in");
+
+ s = test_kmem_cache_create("TestSlub_kfree_rcu",
+ sizeof(struct test_kfree_rcu_struct),
+ SLAB_NO_MERGE);
+ p = kmem_cache_alloc(s, GFP_KERNEL);
+
+ kfree_rcu(p, rcu);
+ kmem_cache_destroy(s);
+
+ KUNIT_EXPECT_EQ(test, 0, slab_errors);
+}
+
+static void test_leak_destroy(struct kunit *test)
+{
+ struct kmem_cache *s = test_kmem_cache_create("TestSlub_leak_destroy",
+ 64, SLAB_NO_MERGE);
+ kmem_cache_alloc(s, GFP_KERNEL);
+
+ kmem_cache_destroy(s);
+
+ KUNIT_EXPECT_EQ(test, 2, slab_errors);
+}
+
static int test_init(struct kunit *test)
{
slab_errors = 0;
@@ -177,6 +212,8 @@ static struct kunit_case test_cases[] = {
KUNIT_CASE(test_clobber_redzone_free),
KUNIT_CASE(test_kmalloc_redzone_access),
+ KUNIT_CASE(test_kfree_rcu),
+ KUNIT_CASE(test_leak_destroy),
{}
};
diff --git a/lib/sort.c b/lib/sort.c
index a0509088f82a..048b7a6ef967 100644
--- a/lib/sort.c
+++ b/lib/sort.c
@@ -5,13 +5,11 @@
* This performs n*log2(n) + 0.37*n + o(n) comparisons on average,
* and 1.5*n*log2(n) + O(n) in the (very contrived) worst case.
*
- * Glibc qsort() manages n*log2(n) - 1.26*n for random inputs (1.63*n
+ * Quicksort manages n*log2(n) - 1.26*n for random inputs (1.63*n
* better) at the expense of stack usage and much larger code to avoid
* quicksort's O(n^2) worst case.
*/
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
#include <linux/types.h>
#include <linux/export.h>
#include <linux/sort.h>
@@ -252,10 +250,7 @@ void sort_r(void *base, size_t num, size_t size,
a = size << shift;
n -= size;
do_swap(base + a, base + n, size, swap_func, priv);
- } else if (n > size) { /* Sorting: Extract root */
- n -= size;
- do_swap(base, base + n, size, swap_func, priv);
- } else { /* Sort complete */
+ } else { /* Sort complete */
break;
}
@@ -285,6 +280,11 @@ void sort_r(void *base, size_t num, size_t size,
do_swap(base + b, base + c, size, swap_func, priv);
}
}
+
+ n -= size;
+ do_swap(base, base + n, size, swap_func, priv);
+ if (n == size * 2 && do_cmp(base, base + size, cmp_func, priv) > 0)
+ do_swap(base, base + size, size, swap_func, priv);
}
EXPORT_SYMBOL(sort_r);
diff --git a/lib/stackdepot.c b/lib/stackdepot.c
index cd8f23455285..5ed34cc963fc 100644
--- a/lib/stackdepot.c
+++ b/lib/stackdepot.c
@@ -624,15 +624,8 @@ depot_stack_handle_t stack_depot_save_flags(unsigned long *entries,
* we won't be able to do that under the lock.
*/
if (unlikely(can_alloc && !READ_ONCE(new_pool))) {
- /*
- * Zero out zone modifiers, as we don't have specific zone
- * requirements. Keep the flags related to allocation in atomic
- * contexts, I/O, nolockdep.
- */
- alloc_flags &= ~GFP_ZONEMASK;
- alloc_flags &= (GFP_ATOMIC | GFP_KERNEL | __GFP_NOLOCKDEP);
- alloc_flags |= __GFP_NOWARN;
- page = alloc_pages(alloc_flags, DEPOT_POOL_ORDER);
+ page = alloc_pages(gfp_nested_mask(alloc_flags),
+ DEPOT_POOL_ORDER);
if (page)
prealloc = page_address(page);
}
diff --git a/lib/stackinit_kunit.c b/lib/stackinit_kunit.c
index 3bc14d1ee816..c14c6f8e6308 100644
--- a/lib/stackinit_kunit.c
+++ b/lib/stackinit_kunit.c
@@ -471,4 +471,5 @@ static struct kunit_suite stackinit_test_suite = {
kunit_test_suites(&stackinit_test_suite);
+MODULE_DESCRIPTION("Test cases for compiler-based stack variable zeroing");
MODULE_LICENSE("GPL");
diff --git a/lib/string.c b/lib/string.c
index 966da44bfc86..76327b51e36f 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -27,7 +27,7 @@
#include <asm/page.h>
#include <asm/rwonce.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <asm/word-at-a-time.h>
#ifndef __HAVE_ARCH_STRNCASECMP
diff --git a/lib/string_helpers.c b/lib/string_helpers.c
index 69ba49b853c7..4f887aa62fa0 100644
--- a/lib/string_helpers.c
+++ b/lib/string_helpers.c
@@ -321,6 +321,9 @@ int string_unescape(char *src, char *dst, size_t size, unsigned int flags)
{
char *out = dst;
+ if (!size)
+ size = SIZE_MAX;
+
while (*src && --size) {
if (src[0] == '\\' && src[1] != '\0' && size > 1) {
src++;
diff --git a/lib/string_helpers_kunit.c b/lib/string_helpers_kunit.c
index f88e39fd68d6..c853046183d2 100644
--- a/lib/string_helpers_kunit.c
+++ b/lib/string_helpers_kunit.c
@@ -625,4 +625,5 @@ static struct kunit_suite string_helpers_test_suite = {
kunit_test_suites(&string_helpers_test_suite);
+MODULE_DESCRIPTION("Test cases for string helpers module");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/lib/string_kunit.c b/lib/string_kunit.c
index 2a812decf14b..c919e3293da6 100644
--- a/lib/string_kunit.c
+++ b/lib/string_kunit.c
@@ -633,4 +633,5 @@ static struct kunit_suite string_test_suite = {
kunit_test_suites(&string_test_suite);
+MODULE_DESCRIPTION("Test cases for string functions");
MODULE_LICENSE("GPL v2");
diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c
index 6432b8c3e431..989a12a67872 100644
--- a/lib/strncpy_from_user.c
+++ b/lib/strncpy_from_user.c
@@ -120,6 +120,15 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
if (unlikely(count <= 0))
return 0;
+ if (can_do_masked_user_access()) {
+ long retval;
+
+ src = masked_user_access_begin(src);
+ retval = do_strncpy_from_user(dst, src, count, count);
+ user_read_access_end();
+ return retval;
+ }
+
max_addr = TASK_SIZE_MAX;
src_addr = (unsigned long)untagged_addr(src);
if (likely(src_addr < max_addr)) {
diff --git a/lib/strnlen_user.c b/lib/strnlen_user.c
index feeb935a2299..6e489f9e90f1 100644
--- a/lib/strnlen_user.c
+++ b/lib/strnlen_user.c
@@ -96,6 +96,15 @@ long strnlen_user(const char __user *str, long count)
if (unlikely(count <= 0))
return 0;
+ if (can_do_masked_user_access()) {
+ long retval;
+
+ str = masked_user_access_begin(str);
+ retval = do_strnlen_user(str, count, count);
+ user_read_access_end();
+ return retval;
+ }
+
max_addr = TASK_SIZE_MAX;
src_addr = (unsigned long)untagged_addr(str);
if (likely(src_addr < max_addr)) {
diff --git a/lib/test-kstrtox.c b/lib/test-kstrtox.c
index f355f67169b6..ee87fef66cb5 100644
--- a/lib/test-kstrtox.c
+++ b/lib/test-kstrtox.c
@@ -732,4 +732,5 @@ static int __init test_kstrtox_init(void)
return -EINVAL;
}
module_init(test_kstrtox_init);
+MODULE_DESCRIPTION("Module test for kstrto*() APIs");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/lib/test_bitmap.c b/lib/test_bitmap.c
index 83019beabce4..65a75d58ed9e 100644
--- a/lib/test_bitmap.c
+++ b/lib/test_bitmap.c
@@ -244,7 +244,7 @@ static void __init test_find_nth_bit(void)
expect_eq_uint(60, find_nth_bit(bmap, 64 * 3, 5));
expect_eq_uint(80, find_nth_bit(bmap, 64 * 3, 6));
expect_eq_uint(123, find_nth_bit(bmap, 64 * 3, 7));
- expect_eq_uint(64 * 3, find_nth_bit(bmap, 64 * 3, 8));
+ expect_eq_uint(0, !!(find_nth_bit(bmap, 64 * 3, 8) < 64 * 3));
expect_eq_uint(10, find_nth_bit(bmap, 64 * 3 - 1, 0));
expect_eq_uint(20, find_nth_bit(bmap, 64 * 3 - 1, 1));
@@ -254,7 +254,7 @@ static void __init test_find_nth_bit(void)
expect_eq_uint(60, find_nth_bit(bmap, 64 * 3 - 1, 5));
expect_eq_uint(80, find_nth_bit(bmap, 64 * 3 - 1, 6));
expect_eq_uint(123, find_nth_bit(bmap, 64 * 3 - 1, 7));
- expect_eq_uint(64 * 3 - 1, find_nth_bit(bmap, 64 * 3 - 1, 8));
+ expect_eq_uint(0, !!(find_nth_bit(bmap, 64 * 3 - 1, 8) < 64 * 3 - 1));
for_each_set_bit(bit, exp1, EXP1_IN_BITS) {
b = find_nth_bit(exp1, EXP1_IN_BITS, cnt++);
@@ -1486,4 +1486,5 @@ static void __init selftest(void)
KSTM_MODULE_LOADERS(test_bitmap);
MODULE_AUTHOR("david decotigny <david.decotigny@googlers.com>");
+MODULE_DESCRIPTION("Test cases for bitmap API");
MODULE_LICENSE("GPL");
diff --git a/lib/test_bitops.c b/lib/test_bitops.c
index 3b7bcbee84db..55669624bb28 100644
--- a/lib/test_bitops.c
+++ b/lib/test_bitops.c
@@ -5,9 +5,11 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/cleanup.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
+#include <linux/slab.h>
/* a tiny module only meant to test
*
@@ -50,6 +52,30 @@ static unsigned long order_comb_long[][2] = {
};
#endif
+static int __init test_fns(void)
+{
+ static volatile __always_used unsigned long tmp __initdata;
+ unsigned long *buf __free(kfree) = NULL;
+ unsigned int i, n;
+ ktime_t time;
+
+ buf = kmalloc_array(10000, sizeof(unsigned long), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ get_random_bytes(buf, 10000 * sizeof(unsigned long));
+ time = ktime_get();
+
+ for (n = 0; n < BITS_PER_LONG; n++)
+ for (i = 0; i < 10000; i++)
+ tmp = fns(buf[i], n);
+
+ time = ktime_get() - time;
+ pr_err("fns: %18llu ns\n", time);
+
+ return 0;
+}
+
static int __init test_bitops_startup(void)
{
int i, bit_set;
@@ -94,6 +120,8 @@ static int __init test_bitops_startup(void)
if (bit_set != BITOPS_LAST)
pr_err("ERROR: FOUND SET BIT %d\n", bit_set);
+ test_fns();
+
pr_info("Completed bitops test\n");
return 0;
diff --git a/lib/test_bits.c b/lib/test_bits.c
index c9368a2314e7..c7b38d91e1f1 100644
--- a/lib/test_bits.c
+++ b/lib/test_bits.c
@@ -39,6 +39,36 @@ static void genmask_ull_test(struct kunit *test)
#endif
}
+static void genmask_u128_test(struct kunit *test)
+{
+#ifdef CONFIG_ARCH_SUPPORTS_INT128
+ /* Below 64 bit masks */
+ KUNIT_EXPECT_EQ(test, 0x0000000000000001ull, GENMASK_U128(0, 0));
+ KUNIT_EXPECT_EQ(test, 0x0000000000000003ull, GENMASK_U128(1, 0));
+ KUNIT_EXPECT_EQ(test, 0x0000000000000006ull, GENMASK_U128(2, 1));
+ KUNIT_EXPECT_EQ(test, 0x00000000ffffffffull, GENMASK_U128(31, 0));
+ KUNIT_EXPECT_EQ(test, 0x000000ffffe00000ull, GENMASK_U128(39, 21));
+ KUNIT_EXPECT_EQ(test, 0xffffffffffffffffull, GENMASK_U128(63, 0));
+
+ /* Above 64 bit masks - only 64 bit portion can be validated once */
+ KUNIT_EXPECT_EQ(test, 0xffffffffffffffffull, GENMASK_U128(64, 0) >> 1);
+ KUNIT_EXPECT_EQ(test, 0x00000000ffffffffull, GENMASK_U128(81, 50) >> 50);
+ KUNIT_EXPECT_EQ(test, 0x0000000000ffffffull, GENMASK_U128(87, 64) >> 64);
+ KUNIT_EXPECT_EQ(test, 0x0000000000ff0000ull, GENMASK_U128(87, 80) >> 64);
+
+ KUNIT_EXPECT_EQ(test, 0xffffffffffffffffull, GENMASK_U128(127, 0) >> 64);
+ KUNIT_EXPECT_EQ(test, 0xffffffffffffffffull, (u64)GENMASK_U128(127, 0));
+ KUNIT_EXPECT_EQ(test, 0x0000000000000003ull, GENMASK_U128(127, 126) >> 126);
+ KUNIT_EXPECT_EQ(test, 0x0000000000000001ull, GENMASK_U128(127, 127) >> 127);
+#ifdef TEST_GENMASK_FAILURES
+ /* these should fail compilation */
+ GENMASK_U128(0, 1);
+ GENMASK_U128(0, 10);
+ GENMASK_U128(9, 10);
+#endif /* TEST_GENMASK_FAILURES */
+#endif /* CONFIG_ARCH_SUPPORTS_INT128 */
+}
+
static void genmask_input_check_test(struct kunit *test)
{
unsigned int x, y;
@@ -56,12 +86,16 @@ static void genmask_input_check_test(struct kunit *test)
/* Valid input */
KUNIT_EXPECT_EQ(test, 0, GENMASK_INPUT_CHECK(1, 1));
KUNIT_EXPECT_EQ(test, 0, GENMASK_INPUT_CHECK(39, 21));
+ KUNIT_EXPECT_EQ(test, 0, GENMASK_INPUT_CHECK(100, 80));
+ KUNIT_EXPECT_EQ(test, 0, GENMASK_INPUT_CHECK(110, 65));
+ KUNIT_EXPECT_EQ(test, 0, GENMASK_INPUT_CHECK(127, 0));
}
static struct kunit_case bits_test_cases[] = {
KUNIT_CASE(genmask_test),
KUNIT_CASE(genmask_ull_test),
+ KUNIT_CASE(genmask_u128_test),
KUNIT_CASE(genmask_input_check_test),
{}
};
@@ -72,4 +106,5 @@ static struct kunit_suite bits_test_suite = {
};
kunit_test_suite(bits_test_suite);
+MODULE_DESCRIPTION("Test cases for functions and macros in bits.h");
MODULE_LICENSE("GPL");
diff --git a/lib/test_blackhole_dev.c b/lib/test_blackhole_dev.c
index f247089d63c0..ec290ac2a0d9 100644
--- a/lib/test_blackhole_dev.c
+++ b/lib/test_blackhole_dev.c
@@ -96,4 +96,5 @@ module_init(test_blackholedev_init);
module_exit(test_blackholedev_exit);
MODULE_AUTHOR("Mahesh Bandewar <maheshb@google.com>");
+MODULE_DESCRIPTION("module test of the blackhole_dev");
MODULE_LICENSE("GPL");
diff --git a/lib/test_bpf.c b/lib/test_bpf.c
index 207ff87194db..fa5edd6ef7f7 100644
--- a/lib/test_bpf.c
+++ b/lib/test_bpf.c
@@ -1740,7 +1740,7 @@ static int __bpf_emit_cmpxchg32(struct bpf_test *self, void *arg,
/* Result unsuccessful */
insns[i++] = BPF_STX_MEM(BPF_W, R10, R1, -4);
insns[i++] = BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, R10, R2, -4);
- insns[i++] = BPF_ZEXT_REG(R0), /* Zext always inserted by verifier */
+ insns[i++] = BPF_ZEXT_REG(R0); /* Zext always inserted by verifier */
insns[i++] = BPF_LDX_MEM(BPF_W, R3, R10, -4);
insns[i++] = BPF_JMP32_REG(BPF_JEQ, R1, R3, 2);
@@ -1754,7 +1754,7 @@ static int __bpf_emit_cmpxchg32(struct bpf_test *self, void *arg,
/* Result successful */
i += __bpf_ld_imm64(&insns[i], R0, dst);
insns[i++] = BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, R10, R2, -4);
- insns[i++] = BPF_ZEXT_REG(R0), /* Zext always inserted by verifier */
+ insns[i++] = BPF_ZEXT_REG(R0); /* Zext always inserted by verifier */
insns[i++] = BPF_LDX_MEM(BPF_W, R3, R10, -4);
insns[i++] = BPF_JMP32_REG(BPF_JEQ, R2, R3, 2);
@@ -15077,8 +15077,7 @@ static struct skb_segment_test skb_segment_tests[] __initconst = {
.build_skb = build_test_skb_linear_no_head_frag,
.features = NETIF_F_SG | NETIF_F_FRAGLIST |
NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_GSO |
- NETIF_F_LLTX | NETIF_F_GRO |
- NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |
+ NETIF_F_GRO | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |
NETIF_F_HW_VLAN_STAG_TX
}
};
@@ -15198,6 +15197,7 @@ struct tail_call_test {
int flags;
int result;
int stack_depth;
+ bool has_tail_call;
};
/* Flags that can be passed to tail call test cases */
@@ -15273,6 +15273,7 @@ static struct tail_call_test tail_call_tests[] = {
BPF_EXIT_INSN(),
},
.result = 3,
+ .has_tail_call = true,
},
{
"Tail call 3",
@@ -15283,6 +15284,7 @@ static struct tail_call_test tail_call_tests[] = {
BPF_EXIT_INSN(),
},
.result = 6,
+ .has_tail_call = true,
},
{
"Tail call 4",
@@ -15293,6 +15295,7 @@ static struct tail_call_test tail_call_tests[] = {
BPF_EXIT_INSN(),
},
.result = 10,
+ .has_tail_call = true,
},
{
"Tail call load/store leaf",
@@ -15323,6 +15326,7 @@ static struct tail_call_test tail_call_tests[] = {
},
.result = 0,
.stack_depth = 16,
+ .has_tail_call = true,
},
{
"Tail call error path, max count reached",
@@ -15335,6 +15339,7 @@ static struct tail_call_test tail_call_tests[] = {
},
.flags = FLAG_NEED_STATE | FLAG_RESULT_IN_STATE,
.result = (MAX_TAIL_CALL_CNT + 1) * MAX_TESTRUNS,
+ .has_tail_call = true,
},
{
"Tail call count preserved across function calls",
@@ -15357,6 +15362,7 @@ static struct tail_call_test tail_call_tests[] = {
.stack_depth = 8,
.flags = FLAG_NEED_STATE | FLAG_RESULT_IN_STATE,
.result = (MAX_TAIL_CALL_CNT + 1) * MAX_TESTRUNS,
+ .has_tail_call = true,
},
{
"Tail call error path, NULL target",
@@ -15369,6 +15375,7 @@ static struct tail_call_test tail_call_tests[] = {
},
.flags = FLAG_NEED_STATE | FLAG_RESULT_IN_STATE,
.result = MAX_TESTRUNS,
+ .has_tail_call = true,
},
{
"Tail call error path, index out of range",
@@ -15381,6 +15388,7 @@ static struct tail_call_test tail_call_tests[] = {
},
.flags = FLAG_NEED_STATE | FLAG_RESULT_IN_STATE,
.result = MAX_TESTRUNS,
+ .has_tail_call = true,
},
};
@@ -15430,6 +15438,7 @@ static __init int prepare_tail_call_tests(struct bpf_array **pprogs)
fp->len = len;
fp->type = BPF_PROG_TYPE_SOCKET_FILTER;
fp->aux->stack_depth = test->stack_depth;
+ fp->aux->tail_call_reachable = test->has_tail_call;
memcpy(fp->insnsi, test->insns, len * sizeof(struct bpf_insn));
/* Relocate runtime tail call offsets and addresses */
@@ -15706,4 +15715,5 @@ static void __exit test_bpf_exit(void)
module_init(test_bpf_init);
module_exit(test_bpf_exit);
+MODULE_DESCRIPTION("Testsuite for BPF interpreter and BPF JIT compiler");
MODULE_LICENSE("GPL");
diff --git a/lib/test_dynamic_debug.c b/lib/test_dynamic_debug.c
index 8dd250ad022b..77c2a669b6af 100644
--- a/lib/test_dynamic_debug.c
+++ b/lib/test_dynamic_debug.c
@@ -162,4 +162,5 @@ module_init(test_dynamic_debug_init);
module_exit(test_dynamic_debug_exit);
MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
+MODULE_DESCRIPTION("Kernel module for testing dynamic_debug");
MODULE_LICENSE("GPL");
diff --git a/lib/test_firmware.c b/lib/test_firmware.c
index 9cfdcd6d21db..bcb32cbff188 100644
--- a/lib/test_firmware.c
+++ b/lib/test_firmware.c
@@ -1567,4 +1567,5 @@ static void __exit test_firmware_exit(void)
module_exit(test_firmware_exit);
MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
+MODULE_DESCRIPTION("interface to trigger and test firmware loading");
MODULE_LICENSE("GPL");
diff --git a/lib/test_fortify/.gitignore b/lib/test_fortify/.gitignore
new file mode 100644
index 000000000000..c1ba37d14b50
--- /dev/null
+++ b/lib/test_fortify/.gitignore
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+/*.log
diff --git a/lib/test_fortify/Makefile b/lib/test_fortify/Makefile
new file mode 100644
index 000000000000..1c3f82ad8bb2
--- /dev/null
+++ b/lib/test_fortify/Makefile
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0
+
+ccflags-y := $(call cc-disable-warning,fortify-source)
+
+quiet_cmd_test_fortify = TEST $@
+ cmd_test_fortify = $(CONFIG_SHELL) $(src)/test_fortify.sh \
+ $< $@ "$(NM)" $(CC) $(c_flags) -DKBUILD_EXTRA_WARN1
+
+$(obj)/%.log: $(src)/%.c $(src)/test_fortify.sh FORCE
+ $(call if_changed_dep,test_fortify)
+
+logs = $(patsubst $(src)/%.c, %.log, $(wildcard $(src)/*-*.c))
+targets += $(logs)
+
+quiet_cmd_gen_fortify_log = CAT $@
+ cmd_gen_fortify_log = cat $(or $(real-prereqs),/dev/null) > $@
+
+$(obj)/test_fortify.log: $(addprefix $(obj)/, $(logs)) FORCE
+ $(call if_changed,gen_fortify_log)
+
+# GCC<=7 does not always produce *.d files.
+# Run the tests only for GCC>=8 or Clang.
+always-$(call gcc-min-version, 80000) += test_fortify.log
+always-$(CONFIG_CC_IS_CLANG) += test_fortify.log
+
+# Some architectures define __NO_FORTIFY if __SANITIZE_ADDRESS__ is undefined.
+# Pass CFLAGS_KASAN to avoid warnings.
+KASAN_SANITIZE := y
diff --git a/lib/test_fortify/test_fortify.sh b/lib/test_fortify/test_fortify.sh
new file mode 100644
index 000000000000..c2688ab8281d
--- /dev/null
+++ b/lib/test_fortify/test_fortify.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
+set -e
+
+# Argument 1: Source file to build.
+IN="$1"
+shift
+# Extract just the filename for error messages below.
+FILE="${IN##*/}"
+# Extract the function name for error messages below.
+FUNC="${FILE#*-}"
+FUNC="${FUNC%%-*}"
+FUNC="${FUNC%%.*}"
+# Extract the symbol to test for in build/symbol test below.
+WANT="__${FILE%%-*}"
+
+# Argument 2: Where to write the build log.
+OUT="$1"
+shift
+TMP="${OUT}.tmp"
+
+# Argument 3: Path to "nm" tool.
+NM="$1"
+shift
+
+# Remaining arguments are: $(CC) $(c_flags)
+
+# Clean up temporary file at exit.
+__cleanup() {
+ rm -f "$TMP"
+}
+trap __cleanup EXIT
+
+# Function names in warnings are wrapped in backticks under UTF-8 locales.
+# Run the commands with LANG=C so that grep output will not change.
+export LANG=C
+
+status=
+# Attempt to build a source that is expected to fail with a specific warning.
+if "$@" -Werror -c "$IN" -o "$OUT".o 2> "$TMP" ; then
+ # If the build succeeds, either the test has failed or the
+ # warning may only happen at link time (Clang). In that case,
+ # make sure the expected symbol is unresolved in the symbol list.
+ # If so, FORTIFY is working for this case.
+ if ! $NM -A "$OUT".o | grep -m1 "\bU ${WANT}$" >>"$TMP" ; then
+ status="warning: unsafe ${FUNC}() usage lacked '$WANT' symbol in $IN"
+ fi
+else
+ # If the build failed, check for the warning in the stderr.
+ # GCC:
+ # ./include/linux/fortify-string.h:316:25: error: call to '__write_overflow_field' declared with attribute warning: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Werror=attribute-warning]
+ # Clang 14:
+ # ./include/linux/fortify-string.h:316:4: error: call to __write_overflow_field declared with 'warning' attribute: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Werror,-Wattribute-warning]
+ if ! grep -Eq -m1 "error: call to .?\b${WANT}\b.?" "$TMP" ; then
+ status="warning: unsafe ${FUNC}() usage lacked '$WANT' warning in $IN"
+ fi
+fi
+
+if [ -n "$status" ]; then
+ # Report on failure results, including compilation warnings.
+ echo "$status" | tee "$OUT" >&2
+else
+ # Report on good results, and save any compilation output to log.
+ echo "ok: unsafe ${FUNC}() usage correctly detected with '$WANT' in $IN" >"$OUT"
+fi
+cat "$TMP" >>"$OUT"
diff --git a/lib/test_fpu.h b/lib/test_fpu.h
new file mode 100644
index 000000000000..4459807084bc
--- /dev/null
+++ b/lib/test_fpu.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _LIB_TEST_FPU_H
+#define _LIB_TEST_FPU_H
+
+int test_fpu(void);
+
+#endif
diff --git a/lib/test_fpu.c b/lib/test_fpu_glue.c
index e82db19fed84..c0596426370a 100644
--- a/lib/test_fpu.c
+++ b/lib/test_fpu_glue.c
@@ -17,39 +17,9 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>
-#include <asm/fpu/api.h>
+#include <linux/fpu.h>
-static int test_fpu(void)
-{
- /*
- * This sequence of operations tests that rounding mode is
- * to nearest and that denormal numbers are supported.
- * Volatile variables are used to avoid compiler optimizing
- * the calculations away.
- */
- volatile double a, b, c, d, e, f, g;
-
- a = 4.0;
- b = 1e-15;
- c = 1e-310;
-
- /* Sets precision flag */
- d = a + b;
-
- /* Result depends on rounding mode */
- e = a + b / 2;
-
- /* Denormal and very large values */
- f = b / c;
-
- /* Depends on denormal support */
- g = a + c * f;
-
- if (d > a && e > a && g > a)
- return 0;
- else
- return -EINVAL;
-}
+#include "test_fpu.h"
static int test_fpu_get(void *data, u64 *val)
{
@@ -68,8 +38,11 @@ static struct dentry *selftest_dir;
static int __init test_fpu_init(void)
{
+ if (!kernel_fpu_available())
+ return -EINVAL;
+
selftest_dir = debugfs_create_dir("selftest_helpers", NULL);
- if (!selftest_dir)
+ if (IS_ERR(selftest_dir))
return -ENOMEM;
debugfs_create_file_unsafe("test_fpu", 0444, selftest_dir, NULL,
@@ -86,4 +59,5 @@ static void __exit test_fpu_exit(void)
module_init(test_fpu_init);
module_exit(test_fpu_exit);
+MODULE_DESCRIPTION("Test cases for floating point operations");
MODULE_LICENSE("GPL");
diff --git a/lib/test_fpu_impl.c b/lib/test_fpu_impl.c
new file mode 100644
index 000000000000..777894dbbe86
--- /dev/null
+++ b/lib/test_fpu_impl.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/errno.h>
+
+#include "test_fpu.h"
+
+int test_fpu(void)
+{
+ /*
+ * This sequence of operations tests that rounding mode is
+ * to nearest and that denormal numbers are supported.
+ * Volatile variables are used to avoid compiler optimizing
+ * the calculations away.
+ */
+ volatile double a, b, c, d, e, f, g;
+
+ a = 4.0;
+ b = 1e-15;
+ c = 1e-310;
+
+ /* Sets precision flag */
+ d = a + b;
+
+ /* Result depends on rounding mode */
+ e = a + b / 2;
+
+ /* Denormal and very large values */
+ f = b / c;
+
+ /* Depends on denormal support */
+ g = a + c * f;
+
+ if (d > a && e > a && g > a)
+ return 0;
+ else
+ return -EINVAL;
+}
diff --git a/lib/test_free_pages.c b/lib/test_free_pages.c
index 9ebf6f5549f3..48952364c540 100644
--- a/lib/test_free_pages.c
+++ b/lib/test_free_pages.c
@@ -44,4 +44,5 @@ static void m_ex(void)
module_init(m_in);
module_exit(m_ex);
MODULE_AUTHOR("Matthew Wilcox <willy@infradead.org>");
+MODULE_DESCRIPTION("Check that free_pages() doesn't leak memory");
MODULE_LICENSE("GPL");
diff --git a/lib/test_hash.c b/lib/test_hash.c
index bb25fda34794..a7af39662a0a 100644
--- a/lib/test_hash.c
+++ b/lib/test_hash.c
@@ -235,4 +235,5 @@ static struct kunit_suite hash_test_suite = {
kunit_test_suite(hash_test_suite);
+MODULE_DESCRIPTION("Test cases for <linux/hash.h> and <linux/stringhash.h>");
MODULE_LICENSE("GPL");
diff --git a/lib/test_hexdump.c b/lib/test_hexdump.c
index b916801f23a8..751645645988 100644
--- a/lib/test_hexdump.c
+++ b/lib/test_hexdump.c
@@ -113,7 +113,7 @@ static void __init test_hexdump_prepare_test(size_t len, int rowsize,
*p++ = ' ';
} while (p < test + rs * 2 + rs / gs + 1);
- strncpy(p, data_a, l);
+ memcpy(p, data_a, l);
p += l;
}
@@ -253,4 +253,5 @@ static void __exit test_hexdump_exit(void)
module_exit(test_hexdump_exit);
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
+MODULE_DESCRIPTION("Test cases for lib/hexdump.c module");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/lib/test_hmm.c b/lib/test_hmm.c
index 717dcb830127..056f2e411d7b 100644
--- a/lib/test_hmm.c
+++ b/lib/test_hmm.c
@@ -799,10 +799,7 @@ static int dmirror_exclusive(struct dmirror *dmirror,
unsigned long mapped = 0;
int i;
- if (end < addr + (ARRAY_SIZE(pages) << PAGE_SHIFT))
- next = end;
- else
- next = addr + (ARRAY_SIZE(pages) << PAGE_SHIFT);
+ next = min(end, addr + (ARRAY_SIZE(pages) << PAGE_SHIFT));
ret = make_device_exclusive_range(mm, addr, next, pages, NULL);
/*
@@ -1226,8 +1223,8 @@ static void dmirror_device_evict_chunk(struct dmirror_chunk *chunk)
unsigned long *src_pfns;
unsigned long *dst_pfns;
- src_pfns = kcalloc(npages, sizeof(*src_pfns), GFP_KERNEL);
- dst_pfns = kcalloc(npages, sizeof(*dst_pfns), GFP_KERNEL);
+ src_pfns = kvcalloc(npages, sizeof(*src_pfns), GFP_KERNEL | __GFP_NOFAIL);
+ dst_pfns = kvcalloc(npages, sizeof(*dst_pfns), GFP_KERNEL | __GFP_NOFAIL);
migrate_device_range(src_pfns, start_pfn, npages);
for (i = 0; i < npages; i++) {
@@ -1250,8 +1247,8 @@ static void dmirror_device_evict_chunk(struct dmirror_chunk *chunk)
}
migrate_device_pages(src_pfns, dst_pfns, npages);
migrate_device_finalize(src_pfns, dst_pfns, npages);
- kfree(src_pfns);
- kfree(dst_pfns);
+ kvfree(src_pfns);
+ kvfree(dst_pfns);
}
/* Removes free pages from the free list so they can't be re-allocated */
@@ -1550,4 +1547,5 @@ static void __exit hmm_dmirror_exit(void)
module_init(hmm_dmirror_init);
module_exit(hmm_dmirror_exit);
+MODULE_DESCRIPTION("HMM (Heterogeneous Memory Management) test module");
MODULE_LICENSE("GPL");
diff --git a/lib/test_ida.c b/lib/test_ida.c
index 072a49897e71..c80155a1956d 100644
--- a/lib/test_ida.c
+++ b/lib/test_ida.c
@@ -214,4 +214,5 @@ static void ida_exit(void)
module_init(ida_checks);
module_exit(ida_exit);
MODULE_AUTHOR("Matthew Wilcox <willy@infradead.org>");
+MODULE_DESCRIPTION("Test the IDA API");
MODULE_LICENSE("GPL");
diff --git a/lib/test_kmod.c b/lib/test_kmod.c
index 1eec3b7ac67c..064ed0fce75a 100644
--- a/lib/test_kmod.c
+++ b/lib/test_kmod.c
@@ -1223,4 +1223,5 @@ static void __exit test_kmod_exit(void)
module_exit(test_kmod_exit);
MODULE_AUTHOR("Luis R. Rodriguez <mcgrof@kernel.org>");
+MODULE_DESCRIPTION("kmod stress test driver");
MODULE_LICENSE("GPL");
diff --git a/lib/test_kprobes.c b/lib/test_kprobes.c
index 0648f7154f5c..b7582010125c 100644
--- a/lib/test_kprobes.c
+++ b/lib/test_kprobes.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * test_kprobes.c - simple sanity test for *probes
+ * test_kprobes.c - simple sanity test for k*probes
*
* Copyright IBM Corp. 2008
*/
@@ -400,4 +400,5 @@ static struct kunit_suite kprobes_test_suite = {
kunit_test_suites(&kprobes_test_suite);
+MODULE_DESCRIPTION("simple sanity test for k*probes");
MODULE_LICENSE("GPL");
diff --git a/lib/test_linear_ranges.c b/lib/test_linear_ranges.c
index c18f9c0f1f25..f482be00f1bc 100644
--- a/lib/test_linear_ranges.c
+++ b/lib/test_linear_ranges.c
@@ -216,4 +216,5 @@ static struct kunit_suite range_test_module = {
kunit_test_suites(&range_test_module);
+MODULE_DESCRIPTION("KUnit test for the linear_ranges helper");
MODULE_LICENSE("GPL");
diff --git a/lib/test_list_sort.c b/lib/test_list_sort.c
index cc5f335f29b5..30879abc8a42 100644
--- a/lib/test_list_sort.c
+++ b/lib/test_list_sort.c
@@ -119,4 +119,5 @@ static struct kunit_suite list_sort_suite = {
kunit_test_suites(&list_sort_suite);
+MODULE_DESCRIPTION("list_sort() KUnit test suite");
MODULE_LICENSE("GPL");
diff --git a/lib/test_maple_tree.c b/lib/test_maple_tree.c
index 399380db449c..31561e0e1a0d 100644
--- a/lib/test_maple_tree.c
+++ b/lib/test_maple_tree.c
@@ -3946,4 +3946,5 @@ static void __exit maple_tree_harvest(void)
module_init(maple_tree_seed);
module_exit(maple_tree_harvest);
MODULE_AUTHOR("Liam R. Howlett <Liam.Howlett@Oracle.com>");
+MODULE_DESCRIPTION("maple tree API test module");
MODULE_LICENSE("GPL");
diff --git a/lib/test_memcat_p.c b/lib/test_memcat_p.c
index 849c477d49d0..7e0797a6bebf 100644
--- a/lib/test_memcat_p.c
+++ b/lib/test_memcat_p.c
@@ -112,4 +112,5 @@ static void __exit test_memcat_p_exit(void)
module_init(test_memcat_p_init);
module_exit(test_memcat_p_exit);
+MODULE_DESCRIPTION("Test cases for memcat_p() in lib/memcat_p.c");
MODULE_LICENSE("GPL");
diff --git a/lib/test_meminit.c b/lib/test_meminit.c
index 0dc173849a54..6298f66c964b 100644
--- a/lib/test_meminit.c
+++ b/lib/test_meminit.c
@@ -436,4 +436,5 @@ static int __init test_meminit_init(void)
}
module_init(test_meminit_init);
+MODULE_DESCRIPTION("Test cases for SL[AOU]B/page initialization at alloc/free time");
MODULE_LICENSE("GPL");
diff --git a/lib/test_min_heap.c b/lib/test_min_heap.c
index 7b01b4387cfb..64c877e73b64 100644
--- a/lib/test_min_heap.c
+++ b/lib/test_min_heap.c
@@ -11,17 +11,19 @@
#include <linux/printk.h>
#include <linux/random.h>
-static __init bool less_than(const void *lhs, const void *rhs)
+DEFINE_MIN_HEAP(int, min_heap_test);
+
+static __init bool less_than(const void *lhs, const void *rhs, void __always_unused *args)
{
return *(int *)lhs < *(int *)rhs;
}
-static __init bool greater_than(const void *lhs, const void *rhs)
+static __init bool greater_than(const void *lhs, const void *rhs, void __always_unused *args)
{
return *(int *)lhs > *(int *)rhs;
}
-static __init void swap_ints(void *lhs, void *rhs)
+static __init void swap_ints(void *lhs, void *rhs, void __always_unused *args)
{
int temp = *(int *)lhs;
@@ -30,7 +32,7 @@ static __init void swap_ints(void *lhs, void *rhs)
}
static __init int pop_verify_heap(bool min_heap,
- struct min_heap *heap,
+ struct min_heap_test *heap,
const struct min_heap_callbacks *funcs)
{
int *values = heap->data;
@@ -38,7 +40,7 @@ static __init int pop_verify_heap(bool min_heap,
int last;
last = values[0];
- min_heap_pop(heap, funcs);
+ min_heap_pop(heap, funcs, NULL);
while (heap->nr > 0) {
if (min_heap) {
if (last > values[0]) {
@@ -54,7 +56,7 @@ static __init int pop_verify_heap(bool min_heap,
}
}
last = values[0];
- min_heap_pop(heap, funcs);
+ min_heap_pop(heap, funcs, NULL);
}
return err;
}
@@ -63,20 +65,19 @@ static __init int test_heapify_all(bool min_heap)
{
int values[] = { 3, 1, 2, 4, 0x8000000, 0x7FFFFFF, 0,
-3, -1, -2, -4, 0x8000000, 0x7FFFFFF };
- struct min_heap heap = {
+ struct min_heap_test heap = {
.data = values,
.nr = ARRAY_SIZE(values),
.size = ARRAY_SIZE(values),
};
struct min_heap_callbacks funcs = {
- .elem_size = sizeof(int),
.less = min_heap ? less_than : greater_than,
.swp = swap_ints,
};
int i, err;
/* Test with known set of values. */
- min_heapify_all(&heap, &funcs);
+ min_heapify_all(&heap, &funcs, NULL);
err = pop_verify_heap(min_heap, &heap, &funcs);
@@ -85,7 +86,7 @@ static __init int test_heapify_all(bool min_heap)
for (i = 0; i < heap.nr; i++)
values[i] = get_random_u32();
- min_heapify_all(&heap, &funcs);
+ min_heapify_all(&heap, &funcs, NULL);
err += pop_verify_heap(min_heap, &heap, &funcs);
return err;
@@ -96,13 +97,12 @@ static __init int test_heap_push(bool min_heap)
const int data[] = { 3, 1, 2, 4, 0x80000000, 0x7FFFFFFF, 0,
-3, -1, -2, -4, 0x80000000, 0x7FFFFFFF };
int values[ARRAY_SIZE(data)];
- struct min_heap heap = {
+ struct min_heap_test heap = {
.data = values,
.nr = 0,
.size = ARRAY_SIZE(values),
};
struct min_heap_callbacks funcs = {
- .elem_size = sizeof(int),
.less = min_heap ? less_than : greater_than,
.swp = swap_ints,
};
@@ -110,14 +110,14 @@ static __init int test_heap_push(bool min_heap)
/* Test with known set of values copied from data. */
for (i = 0; i < ARRAY_SIZE(data); i++)
- min_heap_push(&heap, &data[i], &funcs);
+ min_heap_push(&heap, &data[i], &funcs, NULL);
err = pop_verify_heap(min_heap, &heap, &funcs);
/* Test with randomly generated values. */
while (heap.nr < heap.size) {
temp = get_random_u32();
- min_heap_push(&heap, &temp, &funcs);
+ min_heap_push(&heap, &temp, &funcs, NULL);
}
err += pop_verify_heap(min_heap, &heap, &funcs);
@@ -129,13 +129,12 @@ static __init int test_heap_pop_push(bool min_heap)
const int data[] = { 3, 1, 2, 4, 0x80000000, 0x7FFFFFFF, 0,
-3, -1, -2, -4, 0x80000000, 0x7FFFFFFF };
int values[ARRAY_SIZE(data)];
- struct min_heap heap = {
+ struct min_heap_test heap = {
.data = values,
.nr = 0,
.size = ARRAY_SIZE(values),
};
struct min_heap_callbacks funcs = {
- .elem_size = sizeof(int),
.less = min_heap ? less_than : greater_than,
.swp = swap_ints,
};
@@ -144,28 +143,62 @@ static __init int test_heap_pop_push(bool min_heap)
/* Fill values with data to pop and replace. */
temp = min_heap ? 0x80000000 : 0x7FFFFFFF;
for (i = 0; i < ARRAY_SIZE(data); i++)
- min_heap_push(&heap, &temp, &funcs);
+ min_heap_push(&heap, &temp, &funcs, NULL);
/* Test with known set of values copied from data. */
for (i = 0; i < ARRAY_SIZE(data); i++)
- min_heap_pop_push(&heap, &data[i], &funcs);
+ min_heap_pop_push(&heap, &data[i], &funcs, NULL);
err = pop_verify_heap(min_heap, &heap, &funcs);
heap.nr = 0;
for (i = 0; i < ARRAY_SIZE(data); i++)
- min_heap_push(&heap, &temp, &funcs);
+ min_heap_push(&heap, &temp, &funcs, NULL);
/* Test with randomly generated values. */
for (i = 0; i < ARRAY_SIZE(data); i++) {
temp = get_random_u32();
- min_heap_pop_push(&heap, &temp, &funcs);
+ min_heap_pop_push(&heap, &temp, &funcs, NULL);
}
err += pop_verify_heap(min_heap, &heap, &funcs);
return err;
}
+static __init int test_heap_del(bool min_heap)
+{
+ int values[] = { 3, 1, 2, 4, 0x8000000, 0x7FFFFFF, 0,
+ -3, -1, -2, -4, 0x8000000, 0x7FFFFFF };
+ struct min_heap_test heap;
+
+ min_heap_init(&heap, values, ARRAY_SIZE(values));
+ heap.nr = ARRAY_SIZE(values);
+ struct min_heap_callbacks funcs = {
+ .less = min_heap ? less_than : greater_than,
+ .swp = swap_ints,
+ };
+ int i, err;
+
+ /* Test with known set of values. */
+ min_heapify_all(&heap, &funcs, NULL);
+ for (i = 0; i < ARRAY_SIZE(values) / 2; i++)
+ min_heap_del(&heap, get_random_u32() % heap.nr, &funcs, NULL);
+ err = pop_verify_heap(min_heap, &heap, &funcs);
+
+
+ /* Test with randomly generated values. */
+ heap.nr = ARRAY_SIZE(values);
+ for (i = 0; i < heap.nr; i++)
+ values[i] = get_random_u32();
+ min_heapify_all(&heap, &funcs, NULL);
+
+ for (i = 0; i < ARRAY_SIZE(values) / 2; i++)
+ min_heap_del(&heap, get_random_u32() % heap.nr, &funcs, NULL);
+ err += pop_verify_heap(min_heap, &heap, &funcs);
+
+ return err;
+}
+
static int __init test_min_heap_init(void)
{
int err = 0;
@@ -176,6 +209,8 @@ static int __init test_min_heap_init(void)
err += test_heap_push(false);
err += test_heap_pop_push(true);
err += test_heap_pop_push(false);
+ err += test_heap_del(true);
+ err += test_heap_del(false);
if (err) {
pr_err("test failed with %d errors\n", err);
return -EINVAL;
@@ -191,4 +226,5 @@ static void __exit test_min_heap_exit(void)
}
module_exit(test_min_heap_exit);
+MODULE_DESCRIPTION("Test cases for the min max heap");
MODULE_LICENSE("GPL");
diff --git a/lib/test_module.c b/lib/test_module.c
index debd19e35198..3d1b29b74807 100644
--- a/lib/test_module.c
+++ b/lib/test_module.c
@@ -31,4 +31,5 @@ static void __exit test_module_exit(void)
module_exit(test_module_exit);
MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
+MODULE_DESCRIPTION("module loading subsystem test module");
MODULE_LICENSE("GPL");
diff --git a/lib/test_objagg.c b/lib/test_objagg.c
index c0c957c50635..d34df4306b87 100644
--- a/lib/test_objagg.c
+++ b/lib/test_objagg.c
@@ -60,7 +60,7 @@ static struct objagg_obj *world_obj_get(struct world *world,
if (!world->key_refs[key_id_index(key_id)]) {
world->objagg_objs[key_id_index(key_id)] = objagg_obj;
} else if (world->objagg_objs[key_id_index(key_id)] != objagg_obj) {
- pr_err("Key %u: God another object for the same key.\n",
+ pr_err("Key %u: Got another object for the same key.\n",
key_id);
err = -EINVAL;
goto err_key_id_check;
diff --git a/lib/test_objpool.c b/lib/test_objpool.c
index bfdb81599832..5a3f6961a70f 100644
--- a/lib/test_objpool.c
+++ b/lib/test_objpool.c
@@ -687,4 +687,5 @@ static void __exit ot_mod_exit(void)
module_init(ot_mod_init);
module_exit(ot_mod_exit);
-MODULE_LICENSE("GPL"); \ No newline at end of file
+MODULE_DESCRIPTION("Test module for lockless object pool");
+MODULE_LICENSE("GPL");
diff --git a/lib/test_printf.c b/lib/test_printf.c
index 69b6a5e177f2..8448b6d02bd9 100644
--- a/lib/test_printf.c
+++ b/lib/test_printf.c
@@ -641,26 +641,12 @@ page_flags_test(int section, int node, int zone, int last_cpupid,
test(cmp_buf, "%pGp", &flags);
}
-static void __init page_type_test(unsigned int page_type, const char *name,
- char *cmp_buf)
-{
- unsigned long size;
-
- size = scnprintf(cmp_buf, BUF_SIZE, "%#x(", page_type);
- if (page_type_has_type(page_type))
- size += scnprintf(cmp_buf + size, BUF_SIZE - size, "%s", name);
-
- snprintf(cmp_buf + size, BUF_SIZE - size, ")");
- test(cmp_buf, "%pGt", &page_type);
-}
-
static void __init
flags(void)
{
unsigned long flags;
char *cmp_buffer;
gfp_t gfp;
- unsigned int page_type;
cmp_buffer = kmalloc(BUF_SIZE, GFP_KERNEL);
if (!cmp_buffer)
@@ -700,18 +686,6 @@ flags(void)
gfp |= __GFP_HIGH;
test(cmp_buffer, "%pGg", &gfp);
- page_type = ~0;
- page_type_test(page_type, "", cmp_buffer);
-
- page_type = 10;
- page_type_test(page_type, "", cmp_buffer);
-
- page_type = ~PG_buddy;
- page_type_test(page_type, "buddy", cmp_buffer);
-
- page_type = ~(PG_table | PG_buddy);
- page_type_test(page_type, "table|buddy", cmp_buffer);
-
kfree(cmp_buffer);
}
@@ -824,4 +798,5 @@ static void __init selftest(void)
KSTM_MODULE_LOADERS(test_printf);
MODULE_AUTHOR("Rasmus Villemoes <linux@rasmusvillemoes.dk>");
+MODULE_DESCRIPTION("Test cases for printf facility");
MODULE_LICENSE("GPL");
diff --git a/lib/test_ref_tracker.c b/lib/test_ref_tracker.c
index 49970a7c96f3..b983ceb12afc 100644
--- a/lib/test_ref_tracker.c
+++ b/lib/test_ref_tracker.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Referrence tracker self test.
+ * Reference tracker self test.
*
* Copyright (c) 2021 Eric Dumazet <edumazet@google.com>
*/
@@ -112,4 +112,5 @@ static void __exit test_ref_tracker_exit(void)
module_init(test_ref_tracker_init);
module_exit(test_ref_tracker_exit);
+MODULE_DESCRIPTION("Reference tracker self test");
MODULE_LICENSE("GPL v2");
diff --git a/lib/test_rhashtable.c b/lib/test_rhashtable.c
index 42b585208249..c63db03ebb9d 100644
--- a/lib/test_rhashtable.c
+++ b/lib/test_rhashtable.c
@@ -811,4 +811,5 @@ static void __exit test_rht_exit(void)
module_init(test_rht_init);
module_exit(test_rht_exit);
+MODULE_DESCRIPTION("Resizable, Scalable, Concurrent Hash Table test module");
MODULE_LICENSE("GPL v2");
diff --git a/lib/test_scanf.c b/lib/test_scanf.c
index a2707af2951a..7257b1768545 100644
--- a/lib/test_scanf.c
+++ b/lib/test_scanf.c
@@ -810,4 +810,5 @@ static void __init selftest(void)
KSTM_MODULE_LOADERS(test_scanf);
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_DESCRIPTION("Test cases for sscanf facility");
MODULE_LICENSE("GPL v2");
diff --git a/lib/test_sort.c b/lib/test_sort.c
index be02e3a098cf..cd4a338d1153 100644
--- a/lib/test_sort.c
+++ b/lib/test_sort.c
@@ -29,7 +29,19 @@ static void test_sort(struct kunit *test)
sort(a, TEST_LEN, sizeof(*a), cmpint, NULL);
- for (i = 0; i < TEST_LEN-1; i++)
+ for (i = 0; i < TEST_LEN - 1; i++)
+ KUNIT_ASSERT_LE(test, a[i], a[i + 1]);
+
+ r = 48;
+
+ for (i = 0; i < TEST_LEN - 1; i++) {
+ r = (r * 725861) % 6599;
+ a[i] = r;
+ }
+
+ sort(a, TEST_LEN - 1, sizeof(*a), cmpint, NULL);
+
+ for (i = 0; i < TEST_LEN - 2; i++)
KUNIT_ASSERT_LE(test, a[i], a[i + 1]);
}
@@ -45,4 +57,5 @@ static struct kunit_suite sort_test_suite = {
kunit_test_suites(&sort_test_suite);
+MODULE_DESCRIPTION("sort() KUnit test suite");
MODULE_LICENSE("GPL");
diff --git a/lib/test_static_key_base.c b/lib/test_static_key_base.c
index 5089a2e2bdd8..9f507672afa5 100644
--- a/lib/test_static_key_base.c
+++ b/lib/test_static_key_base.c
@@ -57,4 +57,5 @@ module_init(test_static_key_base_init);
module_exit(test_static_key_base_exit);
MODULE_AUTHOR("Jason Baron <jbaron@akamai.com>");
+MODULE_DESCRIPTION("Kernel module to support testing static keys");
MODULE_LICENSE("GPL");
diff --git a/lib/test_static_keys.c b/lib/test_static_keys.c
index 42daa74be029..00c715f30df9 100644
--- a/lib/test_static_keys.c
+++ b/lib/test_static_keys.c
@@ -236,4 +236,5 @@ module_init(test_static_key_init);
module_exit(test_static_key_exit);
MODULE_AUTHOR("Jason Baron <jbaron@akamai.com>");
+MODULE_DESCRIPTION("Kernel module for testing static keys");
MODULE_LICENSE("GPL");
diff --git a/lib/test_sysctl.c b/lib/test_sysctl.c
index 9321d850931f..b6696fa1d426 100644
--- a/lib/test_sysctl.c
+++ b/lib/test_sysctl.c
@@ -280,4 +280,5 @@ static void __exit test_sysctl_exit(void)
module_exit(test_sysctl_exit);
MODULE_AUTHOR("Luis R. Rodriguez <mcgrof@kernel.org>");
+MODULE_DESCRIPTION("proc sysctl test driver");
MODULE_LICENSE("GPL");
diff --git a/lib/test_ubsan.c b/lib/test_ubsan.c
index c288df9372ed..5d7b10e98610 100644
--- a/lib/test_ubsan.c
+++ b/lib/test_ubsan.c
@@ -156,4 +156,5 @@ static void __exit test_ubsan_exit(void)
module_exit(test_ubsan_exit);
MODULE_AUTHOR("Jinbum Park <jinb.park7@gmail.com>");
+MODULE_DESCRIPTION("UBSAN unit test");
MODULE_LICENSE("GPL v2");
diff --git a/lib/test_user_copy.c b/lib/test_user_copy.c
deleted file mode 100644
index 5ff04d8fe971..000000000000
--- a/lib/test_user_copy.c
+++ /dev/null
@@ -1,331 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Kernel module for testing copy_to/from_user infrastructure.
- *
- * Copyright 2013 Google Inc. All Rights Reserved
- *
- * Authors:
- * Kees Cook <keescook@chromium.org>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/mman.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/vmalloc.h>
-
-/*
- * Several 32-bit architectures support 64-bit {get,put}_user() calls.
- * As there doesn't appear to be anything that can safely determine
- * their capability at compile-time, we just have to opt-out certain archs.
- */
-#if BITS_PER_LONG == 64 || (!(defined(CONFIG_ARM) && !defined(MMU)) && \
- !defined(CONFIG_M68K) && \
- !defined(CONFIG_MICROBLAZE) && \
- !defined(CONFIG_NIOS2) && \
- !defined(CONFIG_PPC32) && \
- !defined(CONFIG_SUPERH))
-# define TEST_U64
-#endif
-
-#define test(condition, msg, ...) \
-({ \
- int cond = (condition); \
- if (cond) \
- pr_warn("[%d] " msg "\n", __LINE__, ##__VA_ARGS__); \
- cond; \
-})
-
-static bool is_zeroed(void *from, size_t size)
-{
- return memchr_inv(from, 0x0, size) == NULL;
-}
-
-static int test_check_nonzero_user(char *kmem, char __user *umem, size_t size)
-{
- int ret = 0;
- size_t start, end, i, zero_start, zero_end;
-
- if (test(size < 2 * PAGE_SIZE, "buffer too small"))
- return -EINVAL;
-
- /*
- * We want to cross a page boundary to exercise the code more
- * effectively. We also don't want to make the size we scan too large,
- * otherwise the test can take a long time and cause soft lockups. So
- * scan a 1024 byte region across the page boundary.
- */
- size = 1024;
- start = PAGE_SIZE - (size / 2);
-
- kmem += start;
- umem += start;
-
- zero_start = size / 4;
- zero_end = size - zero_start;
-
- /*
- * We conduct a series of check_nonzero_user() tests on a block of
- * memory with the following byte-pattern (trying every possible
- * [start,end] pair):
- *
- * [ 00 ff 00 ff ... 00 00 00 00 ... ff 00 ff 00 ]
- *
- * And we verify that check_nonzero_user() acts identically to
- * memchr_inv().
- */
-
- memset(kmem, 0x0, size);
- for (i = 1; i < zero_start; i += 2)
- kmem[i] = 0xff;
- for (i = zero_end; i < size; i += 2)
- kmem[i] = 0xff;
-
- ret |= test(copy_to_user(umem, kmem, size),
- "legitimate copy_to_user failed");
-
- for (start = 0; start <= size; start++) {
- for (end = start; end <= size; end++) {
- size_t len = end - start;
- int retval = check_zeroed_user(umem + start, len);
- int expected = is_zeroed(kmem + start, len);
-
- ret |= test(retval != expected,
- "check_nonzero_user(=%d) != memchr_inv(=%d) mismatch (start=%zu, end=%zu)",
- retval, expected, start, end);
- }
- }
-
- return ret;
-}
-
-static int test_copy_struct_from_user(char *kmem, char __user *umem,
- size_t size)
-{
- int ret = 0;
- char *umem_src = NULL, *expected = NULL;
- size_t ksize, usize;
-
- umem_src = kmalloc(size, GFP_KERNEL);
- ret = test(umem_src == NULL, "kmalloc failed");
- if (ret)
- goto out_free;
-
- expected = kmalloc(size, GFP_KERNEL);
- ret = test(expected == NULL, "kmalloc failed");
- if (ret)
- goto out_free;
-
- /* Fill umem with a fixed byte pattern. */
- memset(umem_src, 0x3e, size);
- ret |= test(copy_to_user(umem, umem_src, size),
- "legitimate copy_to_user failed");
-
- /* Check basic case -- (usize == ksize). */
- ksize = size;
- usize = size;
-
- memcpy(expected, umem_src, ksize);
-
- memset(kmem, 0x0, size);
- ret |= test(copy_struct_from_user(kmem, ksize, umem, usize),
- "copy_struct_from_user(usize == ksize) failed");
- ret |= test(memcmp(kmem, expected, ksize),
- "copy_struct_from_user(usize == ksize) gives unexpected copy");
-
- /* Old userspace case -- (usize < ksize). */
- ksize = size;
- usize = size / 2;
-
- memcpy(expected, umem_src, usize);
- memset(expected + usize, 0x0, ksize - usize);
-
- memset(kmem, 0x0, size);
- ret |= test(copy_struct_from_user(kmem, ksize, umem, usize),
- "copy_struct_from_user(usize < ksize) failed");
- ret |= test(memcmp(kmem, expected, ksize),
- "copy_struct_from_user(usize < ksize) gives unexpected copy");
-
- /* New userspace (-E2BIG) case -- (usize > ksize). */
- ksize = size / 2;
- usize = size;
-
- memset(kmem, 0x0, size);
- ret |= test(copy_struct_from_user(kmem, ksize, umem, usize) != -E2BIG,
- "copy_struct_from_user(usize > ksize) didn't give E2BIG");
-
- /* New userspace (success) case -- (usize > ksize). */
- ksize = size / 2;
- usize = size;
-
- memcpy(expected, umem_src, ksize);
- ret |= test(clear_user(umem + ksize, usize - ksize),
- "legitimate clear_user failed");
-
- memset(kmem, 0x0, size);
- ret |= test(copy_struct_from_user(kmem, ksize, umem, usize),
- "copy_struct_from_user(usize > ksize) failed");
- ret |= test(memcmp(kmem, expected, ksize),
- "copy_struct_from_user(usize > ksize) gives unexpected copy");
-
-out_free:
- kfree(expected);
- kfree(umem_src);
- return ret;
-}
-
-static int __init test_user_copy_init(void)
-{
- int ret = 0;
- char *kmem;
- char __user *usermem;
- char *bad_usermem;
- unsigned long user_addr;
- u8 val_u8;
- u16 val_u16;
- u32 val_u32;
-#ifdef TEST_U64
- u64 val_u64;
-#endif
-
- kmem = kmalloc(PAGE_SIZE * 2, GFP_KERNEL);
- if (!kmem)
- return -ENOMEM;
-
- user_addr = vm_mmap(NULL, 0, PAGE_SIZE * 2,
- PROT_READ | PROT_WRITE | PROT_EXEC,
- MAP_ANONYMOUS | MAP_PRIVATE, 0);
- if (user_addr >= (unsigned long)(TASK_SIZE)) {
- pr_warn("Failed to allocate user memory\n");
- kfree(kmem);
- return -ENOMEM;
- }
-
- usermem = (char __user *)user_addr;
- bad_usermem = (char *)user_addr;
-
- /*
- * Legitimate usage: none of these copies should fail.
- */
- memset(kmem, 0x3a, PAGE_SIZE * 2);
- ret |= test(copy_to_user(usermem, kmem, PAGE_SIZE),
- "legitimate copy_to_user failed");
- memset(kmem, 0x0, PAGE_SIZE);
- ret |= test(copy_from_user(kmem, usermem, PAGE_SIZE),
- "legitimate copy_from_user failed");
- ret |= test(memcmp(kmem, kmem + PAGE_SIZE, PAGE_SIZE),
- "legitimate usercopy failed to copy data");
-
-#define test_legit(size, check) \
- do { \
- val_##size = check; \
- ret |= test(put_user(val_##size, (size __user *)usermem), \
- "legitimate put_user (" #size ") failed"); \
- val_##size = 0; \
- ret |= test(get_user(val_##size, (size __user *)usermem), \
- "legitimate get_user (" #size ") failed"); \
- ret |= test(val_##size != check, \
- "legitimate get_user (" #size ") failed to do copy"); \
- if (val_##size != check) { \
- pr_info("0x%llx != 0x%llx\n", \
- (unsigned long long)val_##size, \
- (unsigned long long)check); \
- } \
- } while (0)
-
- test_legit(u8, 0x5a);
- test_legit(u16, 0x5a5b);
- test_legit(u32, 0x5a5b5c5d);
-#ifdef TEST_U64
- test_legit(u64, 0x5a5b5c5d6a6b6c6d);
-#endif
-#undef test_legit
-
- /* Test usage of check_nonzero_user(). */
- ret |= test_check_nonzero_user(kmem, usermem, 2 * PAGE_SIZE);
- /* Test usage of copy_struct_from_user(). */
- ret |= test_copy_struct_from_user(kmem, usermem, 2 * PAGE_SIZE);
-
- /*
- * Invalid usage: none of these copies should succeed.
- */
-
- /* Prepare kernel memory with check values. */
- memset(kmem, 0x5a, PAGE_SIZE);
- memset(kmem + PAGE_SIZE, 0, PAGE_SIZE);
-
- /* Reject kernel-to-kernel copies through copy_from_user(). */
- ret |= test(!copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE),
- PAGE_SIZE),
- "illegal all-kernel copy_from_user passed");
-
- /* Destination half of buffer should have been zeroed. */
- ret |= test(memcmp(kmem + PAGE_SIZE, kmem, PAGE_SIZE),
- "zeroing failure for illegal all-kernel copy_from_user");
-
-#if 0
- /*
- * When running with SMAP/PAN/etc, this will Oops the kernel
- * due to the zeroing of userspace memory on failure. This needs
- * to be tested in LKDTM instead, since this test module does not
- * expect to explode.
- */
- ret |= test(!copy_from_user(bad_usermem, (char __user *)kmem,
- PAGE_SIZE),
- "illegal reversed copy_from_user passed");
-#endif
- ret |= test(!copy_to_user((char __user *)kmem, kmem + PAGE_SIZE,
- PAGE_SIZE),
- "illegal all-kernel copy_to_user passed");
- ret |= test(!copy_to_user((char __user *)kmem, bad_usermem,
- PAGE_SIZE),
- "illegal reversed copy_to_user passed");
-
-#define test_illegal(size, check) \
- do { \
- val_##size = (check); \
- ret |= test(!get_user(val_##size, (size __user *)kmem), \
- "illegal get_user (" #size ") passed"); \
- ret |= test(val_##size != (size)0, \
- "zeroing failure for illegal get_user (" #size ")"); \
- if (val_##size != (size)0) { \
- pr_info("0x%llx != 0\n", \
- (unsigned long long)val_##size); \
- } \
- ret |= test(!put_user(val_##size, (size __user *)kmem), \
- "illegal put_user (" #size ") passed"); \
- } while (0)
-
- test_illegal(u8, 0x5a);
- test_illegal(u16, 0x5a5b);
- test_illegal(u32, 0x5a5b5c5d);
-#ifdef TEST_U64
- test_illegal(u64, 0x5a5b5c5d6a6b6c6d);
-#endif
-#undef test_illegal
-
- vm_munmap(user_addr, PAGE_SIZE * 2);
- kfree(kmem);
-
- if (ret == 0) {
- pr_info("tests passed.\n");
- return 0;
- }
-
- return -EINVAL;
-}
-
-module_init(test_user_copy_init);
-
-static void __exit test_user_copy_exit(void)
-{
- pr_info("unloaded.\n");
-}
-
-module_exit(test_user_copy_exit);
-
-MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
-MODULE_LICENSE("GPL");
diff --git a/lib/test_uuid.c b/lib/test_uuid.c
index cd819c397dc7..0124fad5d72c 100644
--- a/lib/test_uuid.c
+++ b/lib/test_uuid.c
@@ -130,4 +130,5 @@ static void __exit test_uuid_exit(void)
module_exit(test_uuid_exit);
MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
+MODULE_DESCRIPTION("Test cases for lib/uuid.c module");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/lib/test_xarray.c b/lib/test_xarray.c
index 928fc20337e6..d5c5cbba33ed 100644
--- a/lib/test_xarray.c
+++ b/lib/test_xarray.c
@@ -2001,6 +2001,97 @@ static noinline void check_get_order(struct xarray *xa)
}
}
+static noinline void check_xas_get_order(struct xarray *xa)
+{
+ XA_STATE(xas, xa, 0);
+
+ unsigned int max_order = IS_ENABLED(CONFIG_XARRAY_MULTI) ? 20 : 1;
+ unsigned int order;
+ unsigned long i, j;
+
+ for (order = 0; order < max_order; order++) {
+ for (i = 0; i < 10; i++) {
+ xas_set_order(&xas, i << order, order);
+ do {
+ xas_lock(&xas);
+ xas_store(&xas, xa_mk_value(i));
+ xas_unlock(&xas);
+ } while (xas_nomem(&xas, GFP_KERNEL));
+
+ for (j = i << order; j < (i + 1) << order; j++) {
+ xas_set_order(&xas, j, 0);
+ rcu_read_lock();
+ xas_load(&xas);
+ XA_BUG_ON(xa, xas_get_order(&xas) != order);
+ rcu_read_unlock();
+ }
+
+ xas_lock(&xas);
+ xas_set_order(&xas, i << order, order);
+ xas_store(&xas, NULL);
+ xas_unlock(&xas);
+ }
+ }
+}
+
+static noinline void check_xas_conflict_get_order(struct xarray *xa)
+{
+ XA_STATE(xas, xa, 0);
+
+ void *entry;
+ int only_once;
+ unsigned int max_order = IS_ENABLED(CONFIG_XARRAY_MULTI) ? 20 : 1;
+ unsigned int order;
+ unsigned long i, j, k;
+
+ for (order = 0; order < max_order; order++) {
+ for (i = 0; i < 10; i++) {
+ xas_set_order(&xas, i << order, order);
+ do {
+ xas_lock(&xas);
+ xas_store(&xas, xa_mk_value(i));
+ xas_unlock(&xas);
+ } while (xas_nomem(&xas, GFP_KERNEL));
+
+ /*
+ * Ensure xas_get_order works with xas_for_each_conflict.
+ */
+ j = i << order;
+ for (k = 0; k < order; k++) {
+ only_once = 0;
+ xas_set_order(&xas, j + (1 << k), k);
+ xas_lock(&xas);
+ xas_for_each_conflict(&xas, entry) {
+ XA_BUG_ON(xa, entry != xa_mk_value(i));
+ XA_BUG_ON(xa, xas_get_order(&xas) != order);
+ only_once++;
+ }
+ XA_BUG_ON(xa, only_once != 1);
+ xas_unlock(&xas);
+ }
+
+ if (order < max_order - 1) {
+ only_once = 0;
+ xas_set_order(&xas, (i & ~1UL) << order, order + 1);
+ xas_lock(&xas);
+ xas_for_each_conflict(&xas, entry) {
+ XA_BUG_ON(xa, entry != xa_mk_value(i));
+ XA_BUG_ON(xa, xas_get_order(&xas) != order);
+ only_once++;
+ }
+ XA_BUG_ON(xa, only_once != 1);
+ xas_unlock(&xas);
+ }
+
+ xas_set_order(&xas, i << order, order);
+ xas_lock(&xas);
+ xas_store(&xas, NULL);
+ xas_unlock(&xas);
+ }
+ }
+}
+
+
static noinline void check_destroy(struct xarray *xa)
{
unsigned long index;
@@ -2052,6 +2143,8 @@ static int xarray_checks(void)
check_multi_store(&array);
check_multi_store_advanced(&array);
check_get_order(&array);
+ check_xas_get_order(&array);
+ check_xas_conflict_get_order(&array);
check_xa_alloc();
check_find(&array);
check_find_entry(&array);
@@ -2080,4 +2173,5 @@ static void xarray_exit(void)
module_init(xarray_checks);
module_exit(xarray_exit);
MODULE_AUTHOR("Matthew Wilcox <willy@infradead.org>");
+MODULE_DESCRIPTION("XArray API test module");
MODULE_LICENSE("GPL");
diff --git a/lib/ts_bm.c b/lib/ts_bm.c
index e5f30f9177df..eed5967238c5 100644
--- a/lib/ts_bm.c
+++ b/lib/ts_bm.c
@@ -216,6 +216,7 @@ static void __exit exit_bm(void)
textsearch_unregister(&bm_ops);
}
+MODULE_DESCRIPTION("Boyer-Moore text search implementation");
MODULE_LICENSE("GPL");
module_init(init_bm);
diff --git a/lib/ts_fsm.c b/lib/ts_fsm.c
index 64fd9015ad80..053615f4fcd7 100644
--- a/lib/ts_fsm.c
+++ b/lib/ts_fsm.c
@@ -331,6 +331,7 @@ static void __exit exit_fsm(void)
textsearch_unregister(&fsm_ops);
}
+MODULE_DESCRIPTION("naive finite state machine text search");
MODULE_LICENSE("GPL");
module_init(init_fsm);
diff --git a/lib/ts_kmp.c b/lib/ts_kmp.c
index c77a3d537f24..5520dc28255a 100644
--- a/lib/ts_kmp.c
+++ b/lib/ts_kmp.c
@@ -147,6 +147,7 @@ static void __exit exit_kmp(void)
textsearch_unregister(&kmp_ops);
}
+MODULE_DESCRIPTION("Knuth-Morris-Pratt text search implementation");
MODULE_LICENSE("GPL");
module_init(init_kmp);
diff --git a/lib/union_find.c b/lib/union_find.c
new file mode 100644
index 000000000000..413b0f8adf7a
--- /dev/null
+++ b/lib/union_find.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/union_find.h>
+
+/**
+ * uf_find - Find the root of a node and perform path compression
+ * @node: the node to find the root of
+ *
+ * This function returns the root of the node by following the parent
+ * pointers. It also performs path compression, making the tree shallower.
+ *
+ * Returns the root node of the set containing node.
+ */
+struct uf_node *uf_find(struct uf_node *node)
+{
+ struct uf_node *parent;
+
+ while (node->parent != node) {
+ parent = node->parent;
+ node->parent = parent->parent;
+ node = parent;
+ }
+ return node;
+}
+
+/**
+ * uf_union - Merge two sets, using union by rank
+ * @node1: the first node
+ * @node2: the second node
+ *
+ * This function merges the sets containing node1 and node2, by comparing
+ * the ranks to keep the tree balanced.
+ */
+void uf_union(struct uf_node *node1, struct uf_node *node2)
+{
+ struct uf_node *root1 = uf_find(node1);
+ struct uf_node *root2 = uf_find(node2);
+
+ if (root1 == root2)
+ return;
+
+ if (root1->rank < root2->rank) {
+ root1->parent = root2;
+ } else if (root1->rank > root2->rank) {
+ root2->parent = root1;
+ } else {
+ root2->parent = root1;
+ root1->rank++;
+ }
+}
diff --git a/lib/usercopy.c b/lib/usercopy.c
index d29fe29c6849..7b17b83c8042 100644
--- a/lib/usercopy.c
+++ b/lib/usercopy.c
@@ -1,46 +1,29 @@
// SPDX-License-Identifier: GPL-2.0
-#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/export.h>
#include <linux/fault-inject-usercopy.h>
#include <linux/instrumented.h>
-#include <linux/uaccess.h>
+#include <linux/kernel.h>
#include <linux/nospec.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/wordpart.h>
/* out-of-line parts */
-#ifndef INLINE_COPY_FROM_USER
+#if !defined(INLINE_COPY_FROM_USER) || defined(CONFIG_RUST)
unsigned long _copy_from_user(void *to, const void __user *from, unsigned long n)
{
- unsigned long res = n;
- might_fault();
- if (!should_fail_usercopy() && likely(access_ok(from, n))) {
- /*
- * Ensure that bad access_ok() speculation will not
- * lead to nasty side effects *after* the copy is
- * finished:
- */
- barrier_nospec();
- instrument_copy_from_user_before(to, from, n);
- res = raw_copy_from_user(to, from, n);
- instrument_copy_from_user_after(to, from, n, res);
- }
- if (unlikely(res))
- memset(to + (n - res), 0, res);
- return res;
+ return _inline_copy_from_user(to, from, n);
}
EXPORT_SYMBOL(_copy_from_user);
#endif
-#ifndef INLINE_COPY_TO_USER
+#if !defined(INLINE_COPY_TO_USER) || defined(CONFIG_RUST)
unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n)
{
- might_fault();
- if (should_fail_usercopy())
- return n;
- if (likely(access_ok(to, n))) {
- instrument_copy_to_user(to, from, n);
- n = raw_copy_to_user(to, from, n);
- }
- return n;
+ return _inline_copy_to_user(to, from, n);
}
EXPORT_SYMBOL(_copy_to_user);
#endif
diff --git a/lib/usercopy_kunit.c b/lib/usercopy_kunit.c
new file mode 100644
index 000000000000..77fa00a13df7
--- /dev/null
+++ b/lib/usercopy_kunit.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Kernel module for testing copy_to/from_user infrastructure.
+ *
+ * Copyright 2013 Google Inc. All Rights Reserved
+ *
+ * Authors:
+ * Kees Cook <keescook@chromium.org>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/mman.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <kunit/test.h>
+
+/*
+ * Several 32-bit architectures support 64-bit {get,put}_user() calls.
+ * As there doesn't appear to be anything that can safely determine
+ * their capability at compile-time, we just have to opt-out certain archs.
+ */
+#if BITS_PER_LONG == 64 || (!(defined(CONFIG_ARM) && !defined(MMU)) && \
+ !defined(CONFIG_M68K) && \
+ !defined(CONFIG_MICROBLAZE) && \
+ !defined(CONFIG_NIOS2) && \
+ !defined(CONFIG_PPC32) && \
+ !defined(CONFIG_SUPERH))
+# define TEST_U64
+#endif
+
+struct usercopy_test_priv {
+ char *kmem;
+ char __user *umem;
+ size_t size;
+};
+
+static bool is_zeroed(void *from, size_t size)
+{
+ return memchr_inv(from, 0x0, size) == NULL;
+}
+
+/* Test usage of check_nonzero_user(). */
+static void usercopy_test_check_nonzero_user(struct kunit *test)
+{
+ size_t start, end, i, zero_start, zero_end;
+ struct usercopy_test_priv *priv = test->priv;
+ char __user *umem = priv->umem;
+ char *kmem = priv->kmem;
+ size_t size = priv->size;
+
+ KUNIT_ASSERT_GE_MSG(test, size, 2 * PAGE_SIZE, "buffer too small");
+
+ /*
+ * We want to cross a page boundary to exercise the code more
+ * effectively. We also don't want to make the size we scan too large,
+ * otherwise the test can take a long time and cause soft lockups. So
+ * scan a 1024 byte region across the page boundary.
+ */
+ size = 1024;
+ start = PAGE_SIZE - (size / 2);
+
+ kmem += start;
+ umem += start;
+
+ zero_start = size / 4;
+ zero_end = size - zero_start;
+
+ /*
+ * We conduct a series of check_nonzero_user() tests on a block of
+ * memory with the following byte-pattern (trying every possible
+ * [start,end] pair):
+ *
+ * [ 00 ff 00 ff ... 00 00 00 00 ... ff 00 ff 00 ]
+ *
+ * And we verify that check_nonzero_user() acts identically to
+ * memchr_inv().
+ */
+
+ memset(kmem, 0x0, size);
+ for (i = 1; i < zero_start; i += 2)
+ kmem[i] = 0xff;
+ for (i = zero_end; i < size; i += 2)
+ kmem[i] = 0xff;
+
+ KUNIT_EXPECT_EQ_MSG(test, copy_to_user(umem, kmem, size), 0,
+ "legitimate copy_to_user failed");
+
+ for (start = 0; start <= size; start++) {
+ for (end = start; end <= size; end++) {
+ size_t len = end - start;
+ int retval = check_zeroed_user(umem + start, len);
+ int expected = is_zeroed(kmem + start, len);
+
+ KUNIT_ASSERT_EQ_MSG(test, retval, expected,
+ "check_nonzero_user(=%d) != memchr_inv(=%d) mismatch (start=%zu, end=%zu)",
+ retval, expected, start, end);
+ }
+ }
+}
+
+/* Test usage of copy_struct_from_user(). */
+static void usercopy_test_copy_struct_from_user(struct kunit *test)
+{
+ char *umem_src = NULL, *expected = NULL;
+ struct usercopy_test_priv *priv = test->priv;
+ char __user *umem = priv->umem;
+ char *kmem = priv->kmem;
+ size_t size = priv->size;
+ size_t ksize, usize;
+
+ umem_src = kunit_kmalloc(test, size, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, umem_src);
+
+ expected = kunit_kmalloc(test, size, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, expected);
+
+ /* Fill umem with a fixed byte pattern. */
+ memset(umem_src, 0x3e, size);
+ KUNIT_ASSERT_EQ_MSG(test, copy_to_user(umem, umem_src, size), 0,
+ "legitimate copy_to_user failed");
+
+ /* Check basic case -- (usize == ksize). */
+ ksize = size;
+ usize = size;
+
+ memcpy(expected, umem_src, ksize);
+
+ memset(kmem, 0x0, size);
+ KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), 0,
+ "copy_struct_from_user(usize == ksize) failed");
+ KUNIT_EXPECT_MEMEQ_MSG(test, kmem, expected, ksize,
+ "copy_struct_from_user(usize == ksize) gives unexpected copy");
+
+ /* Old userspace case -- (usize < ksize). */
+ ksize = size;
+ usize = size / 2;
+
+ memcpy(expected, umem_src, usize);
+ memset(expected + usize, 0x0, ksize - usize);
+
+ memset(kmem, 0x0, size);
+ KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), 0,
+ "copy_struct_from_user(usize < ksize) failed");
+ KUNIT_EXPECT_MEMEQ_MSG(test, kmem, expected, ksize,
+ "copy_struct_from_user(usize < ksize) gives unexpected copy");
+
+ /* New userspace (-E2BIG) case -- (usize > ksize). */
+ ksize = size / 2;
+ usize = size;
+
+ memset(kmem, 0x0, size);
+ KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), -E2BIG,
+ "copy_struct_from_user(usize > ksize) didn't give E2BIG");
+
+ /* New userspace (success) case -- (usize > ksize). */
+ ksize = size / 2;
+ usize = size;
+
+ memcpy(expected, umem_src, ksize);
+ KUNIT_EXPECT_EQ_MSG(test, clear_user(umem + ksize, usize - ksize), 0,
+ "legitimate clear_user failed");
+
+ memset(kmem, 0x0, size);
+ KUNIT_EXPECT_EQ_MSG(test, copy_struct_from_user(kmem, ksize, umem, usize), 0,
+ "copy_struct_from_user(usize > ksize) failed");
+ KUNIT_EXPECT_MEMEQ_MSG(test, kmem, expected, ksize,
+ "copy_struct_from_user(usize > ksize) gives unexpected copy");
+}
+
+/*
+ * Legitimate usage: none of these copies should fail.
+ */
+static void usercopy_test_valid(struct kunit *test)
+{
+ struct usercopy_test_priv *priv = test->priv;
+ char __user *usermem = priv->umem;
+ char *kmem = priv->kmem;
+
+ memset(kmem, 0x3a, PAGE_SIZE * 2);
+ KUNIT_EXPECT_EQ_MSG(test, 0, copy_to_user(usermem, kmem, PAGE_SIZE),
+ "legitimate copy_to_user failed");
+ memset(kmem, 0x0, PAGE_SIZE);
+ KUNIT_EXPECT_EQ_MSG(test, 0, copy_from_user(kmem, usermem, PAGE_SIZE),
+ "legitimate copy_from_user failed");
+ KUNIT_EXPECT_MEMEQ_MSG(test, kmem, kmem + PAGE_SIZE, PAGE_SIZE,
+ "legitimate usercopy failed to copy data");
+
+#define test_legit(size, check) \
+ do { \
+ size val_##size = (check); \
+ KUNIT_EXPECT_EQ_MSG(test, 0, \
+ put_user(val_##size, (size __user *)usermem), \
+ "legitimate put_user (" #size ") failed"); \
+ val_##size = 0; \
+ KUNIT_EXPECT_EQ_MSG(test, 0, \
+ get_user(val_##size, (size __user *)usermem), \
+ "legitimate get_user (" #size ") failed"); \
+ KUNIT_EXPECT_EQ_MSG(test, val_##size, check, \
+ "legitimate get_user (" #size ") failed to do copy"); \
+ } while (0)
+
+ test_legit(u8, 0x5a);
+ test_legit(u16, 0x5a5b);
+ test_legit(u32, 0x5a5b5c5d);
+#ifdef TEST_U64
+ test_legit(u64, 0x5a5b5c5d6a6b6c6d);
+#endif
+#undef test_legit
+}
+
+/*
+ * Invalid usage: none of these copies should succeed.
+ */
+static void usercopy_test_invalid(struct kunit *test)
+{
+ struct usercopy_test_priv *priv = test->priv;
+ char __user *usermem = priv->umem;
+ char *bad_usermem = (char *)usermem;
+ char *kmem = priv->kmem;
+ u64 *kmem_u64 = (u64 *)kmem;
+
+ if (IS_ENABLED(CONFIG_ALTERNATE_USER_ADDRESS_SPACE) ||
+ !IS_ENABLED(CONFIG_MMU)) {
+ kunit_skip(test, "Testing for kernel/userspace address confusion is only sensible on architectures with a shared address space");
+ return;
+ }
+
+ /* Prepare kernel memory with check values. */
+ memset(kmem, 0x5a, PAGE_SIZE);
+ memset(kmem + PAGE_SIZE, 0, PAGE_SIZE);
+
+ /* Reject kernel-to-kernel copies through copy_from_user(). */
+ KUNIT_EXPECT_NE_MSG(test, copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE),
+ PAGE_SIZE), 0,
+ "illegal all-kernel copy_from_user passed");
+
+ /* Destination half of buffer should have been zeroed. */
+ KUNIT_EXPECT_MEMEQ_MSG(test, kmem + PAGE_SIZE, kmem, PAGE_SIZE,
+ "zeroing failure for illegal all-kernel copy_from_user");
+
+#if 0
+ /*
+ * When running with SMAP/PAN/etc, this will Oops the kernel
+ * due to the zeroing of userspace memory on failure. This needs
+ * to be tested in LKDTM instead, since this test module does not
+ * expect to explode.
+ */
+ KUNIT_EXPECT_NE_MSG(test, copy_from_user(bad_usermem, (char __user *)kmem,
+ PAGE_SIZE), 0,
+ "illegal reversed copy_from_user passed");
+#endif
+ KUNIT_EXPECT_NE_MSG(test, copy_to_user((char __user *)kmem, kmem + PAGE_SIZE,
+ PAGE_SIZE), 0,
+ "illegal all-kernel copy_to_user passed");
+
+ KUNIT_EXPECT_NE_MSG(test, copy_to_user((char __user *)kmem, bad_usermem,
+ PAGE_SIZE), 0,
+ "illegal reversed copy_to_user passed");
+
+#define test_illegal(size, check) \
+ do { \
+ size val_##size = (check); \
+ /* get_user() */ \
+ KUNIT_EXPECT_NE_MSG(test, get_user(val_##size, (size __user *)kmem), 0, \
+ "illegal get_user (" #size ") passed"); \
+ KUNIT_EXPECT_EQ_MSG(test, val_##size, 0, \
+ "zeroing failure for illegal get_user (" #size ")"); \
+ /* put_user() */ \
+ *kmem_u64 = 0xF09FA4AFF09FA4AF; \
+ KUNIT_EXPECT_NE_MSG(test, put_user(val_##size, (size __user *)kmem), 0, \
+ "illegal put_user (" #size ") passed"); \
+ KUNIT_EXPECT_EQ_MSG(test, *kmem_u64, 0xF09FA4AFF09FA4AF, \
+ "illegal put_user (" #size ") wrote to kernel memory!"); \
+ } while (0)
+
+ test_illegal(u8, 0x5a);
+ test_illegal(u16, 0x5a5b);
+ test_illegal(u32, 0x5a5b5c5d);
+#ifdef TEST_U64
+ test_illegal(u64, 0x5a5b5c5d6a6b6c6d);
+#endif
+#undef test_illegal
+}
+
+static int usercopy_test_init(struct kunit *test)
+{
+ struct usercopy_test_priv *priv;
+ unsigned long user_addr;
+
+ if (!IS_ENABLED(CONFIG_MMU)) {
+ kunit_skip(test, "Userspace allocation testing not available on non-MMU systems");
+ return 0;
+ }
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+ test->priv = priv;
+ priv->size = PAGE_SIZE * 2;
+
+ priv->kmem = kunit_kmalloc(test, priv->size, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->kmem);
+
+ user_addr = kunit_vm_mmap(test, NULL, 0, priv->size,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0);
+ KUNIT_ASSERT_NE_MSG(test, user_addr, 0,
+ "Could not create userspace mm");
+ KUNIT_ASSERT_LT_MSG(test, user_addr, (unsigned long)TASK_SIZE,
+ "Failed to allocate user memory");
+ priv->umem = (char __user *)user_addr;
+
+ return 0;
+}
+
+static struct kunit_case usercopy_test_cases[] = {
+ KUNIT_CASE(usercopy_test_valid),
+ KUNIT_CASE(usercopy_test_invalid),
+ KUNIT_CASE(usercopy_test_check_nonzero_user),
+ KUNIT_CASE(usercopy_test_copy_struct_from_user),
+ {}
+};
+
+static struct kunit_suite usercopy_test_suite = {
+ .name = "usercopy",
+ .init = usercopy_test_init,
+ .test_cases = usercopy_test_cases,
+};
+
+kunit_test_suites(&usercopy_test_suite);
+MODULE_AUTHOR("Kees Cook <kees@kernel.org>");
+MODULE_DESCRIPTION("Kernel module for testing copy_to/from_user infrastructure");
+MODULE_LICENSE("GPL");
diff --git a/lib/vdso/Kconfig b/lib/vdso/Kconfig
index c46c2300517c..82fe827af542 100644
--- a/lib/vdso/Kconfig
+++ b/lib/vdso/Kconfig
@@ -38,3 +38,8 @@ config GENERIC_VDSO_OVERFLOW_PROTECT
in the hotpath.
endif
+
+config VDSO_GETRANDOM
+ bool
+ help
+ Selected by architectures that support vDSO getrandom().
diff --git a/lib/vdso/Makefile b/lib/vdso/Makefile
index 9f031eafc465..cedbf15f8087 100644
--- a/lib/vdso/Makefile
+++ b/lib/vdso/Makefile
@@ -4,6 +4,7 @@ GENERIC_VDSO_MK_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
GENERIC_VDSO_DIR := $(dir $(GENERIC_VDSO_MK_PATH))
c-gettimeofday-$(CONFIG_GENERIC_GETTIMEOFDAY) := $(addprefix $(GENERIC_VDSO_DIR), gettimeofday.c)
+c-getrandom-$(CONFIG_VDSO_GETRANDOM) := $(addprefix $(GENERIC_VDSO_DIR), getrandom.c)
# This cmd checks that the vdso library does not contain dynamic relocations.
# It has to be called after the linking of the vdso library and requires it
diff --git a/lib/vdso/getrandom.c b/lib/vdso/getrandom.c
new file mode 100644
index 000000000000..938ca539aaa6
--- /dev/null
+++ b/lib/vdso/getrandom.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022-2024 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include <linux/array_size.h>
+#include <linux/minmax.h>
+#include <vdso/datapage.h>
+#include <vdso/getrandom.h>
+#include <vdso/unaligned.h>
+#include <asm/vdso/getrandom.h>
+#include <uapi/linux/mman.h>
+#include <uapi/linux/random.h>
+
+#undef PAGE_SIZE
+#undef PAGE_MASK
+#define PAGE_SIZE (1UL << CONFIG_PAGE_SHIFT)
+#define PAGE_MASK (~(PAGE_SIZE - 1))
+
+#define MEMCPY_AND_ZERO_SRC(type, dst, src, len) do { \
+ while (len >= sizeof(type)) { \
+ __put_unaligned_t(type, __get_unaligned_t(type, src), dst); \
+ __put_unaligned_t(type, 0, src); \
+ dst += sizeof(type); \
+ src += sizeof(type); \
+ len -= sizeof(type); \
+ } \
+} while (0)
+
+static void memcpy_and_zero_src(void *dst, void *src, size_t len)
+{
+ if (IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) {
+ if (IS_ENABLED(CONFIG_64BIT))
+ MEMCPY_AND_ZERO_SRC(u64, dst, src, len);
+ MEMCPY_AND_ZERO_SRC(u32, dst, src, len);
+ MEMCPY_AND_ZERO_SRC(u16, dst, src, len);
+ }
+ MEMCPY_AND_ZERO_SRC(u8, dst, src, len);
+}
+
+/**
+ * __cvdso_getrandom_data - Generic vDSO implementation of getrandom() syscall.
+ * @rng_info: Describes state of kernel RNG, memory shared with kernel.
+ * @buffer: Destination buffer to fill with random bytes.
+ * @len: Size of @buffer in bytes.
+ * @flags: Zero or more GRND_* flags.
+ * @opaque_state: Pointer to an opaque state area.
+ * @opaque_len: Length of opaque state area.
+ *
+ * This implements a "fast key erasure" RNG using ChaCha20, in the same way that the kernel's
+ * getrandom() syscall does. It periodically reseeds its key from the kernel's RNG, at the same
+ * schedule that the kernel's RNG is reseeded. If the kernel's RNG is not ready, then this always
+ * calls into the syscall.
+ *
+ * If @buffer, @len, and @flags are 0, and @opaque_len is ~0UL, then @opaque_state is populated
+ * with a struct vgetrandom_opaque_params and the function returns 0; if it does not return 0,
+ * this function should not be used.
+ *
+ * @opaque_state *must* be allocated by calling mmap(2) using the mmap_prot and mmap_flags fields
+ * from the struct vgetrandom_opaque_params, and states must not straddle pages. Unless external
+ * locking is used, one state must be allocated per thread, as it is not safe to call this function
+ * concurrently with the same @opaque_state. However, it is safe to call this using the same
+ * @opaque_state that is shared between main code and signal handling code, within the same thread.
+ *
+ * Returns: The number of random bytes written to @buffer, or a negative value indicating an error.
+ */
+static __always_inline ssize_t
+__cvdso_getrandom_data(const struct vdso_rng_data *rng_info, void *buffer, size_t len,
+ unsigned int flags, void *opaque_state, size_t opaque_len)
+{
+ ssize_t ret = min_t(size_t, INT_MAX & PAGE_MASK /* = MAX_RW_COUNT */, len);
+ struct vgetrandom_state *state = opaque_state;
+ size_t batch_len, nblocks, orig_len = len;
+ bool in_use, have_retried = false;
+ void *orig_buffer = buffer;
+ u64 current_generation;
+ u32 counter[2] = { 0 };
+
+ if (unlikely(opaque_len == ~0UL && !buffer && !len && !flags)) {
+ struct vgetrandom_opaque_params *params = opaque_state;
+ params->size_of_opaque_state = sizeof(*state);
+ params->mmap_prot = PROT_READ | PROT_WRITE;
+ params->mmap_flags = MAP_DROPPABLE | MAP_ANONYMOUS;
+ for (size_t i = 0; i < ARRAY_SIZE(params->reserved); ++i)
+ params->reserved[i] = 0;
+ return 0;
+ }
+
+ /* The state must not straddle a page, since pages can be zeroed at any time. */
+ if (unlikely(((unsigned long)opaque_state & ~PAGE_MASK) + sizeof(*state) > PAGE_SIZE))
+ return -EFAULT;
+
+ /* Handle unexpected flags by falling back to the kernel. */
+ if (unlikely(flags & ~(GRND_NONBLOCK | GRND_RANDOM | GRND_INSECURE)))
+ goto fallback_syscall;
+
+ /* If the caller passes the wrong size, which might happen due to CRIU, fallback. */
+ if (unlikely(opaque_len != sizeof(*state)))
+ goto fallback_syscall;
+
+ /*
+ * If the kernel's RNG is not yet ready, then it's not possible to provide random bytes from
+ * userspace, because A) the various @flags require this to block, or not, depending on
+ * various factors unavailable to userspace, and B) the kernel's behavior before the RNG is
+ * ready is to reseed from the entropy pool at every invocation.
+ */
+ if (unlikely(!READ_ONCE(rng_info->is_ready)))
+ goto fallback_syscall;
+
+ /*
+ * This condition is checked after @rng_info->is_ready, because before the kernel's RNG is
+ * initialized, the @flags parameter may require this to block or return an error, even when
+ * len is zero.
+ */
+ if (unlikely(!len))
+ return 0;
+
+ /*
+ * @state->in_use is basic reentrancy protection against this running in a signal handler
+ * with the same @opaque_state, but obviously not atomic wrt multiple CPUs or more than one
+ * level of reentrancy. If a signal interrupts this after reading @state->in_use, but before
+ * writing @state->in_use, there is still no race, because the signal handler will run to
+ * its completion before returning execution.
+ */
+ in_use = READ_ONCE(state->in_use);
+ if (unlikely(in_use))
+ /* The syscall simply fills the buffer and does not touch @state, so fallback. */
+ goto fallback_syscall;
+ WRITE_ONCE(state->in_use, true);
+
+retry_generation:
+ /*
+ * @rng_info->generation must always be read here, as it serializes @state->key with the
+ * kernel's RNG reseeding schedule.
+ */
+ current_generation = READ_ONCE(rng_info->generation);
+
+ /*
+ * If @state->generation doesn't match the kernel RNG's generation, then it means the
+ * kernel's RNG has reseeded, and so @state->key is reseeded as well.
+ */
+ if (unlikely(state->generation != current_generation)) {
+ /*
+ * Write the generation before filling the key, in case of fork. If there is a fork
+ * just after this line, the parent and child will get different random bytes from
+ * the syscall, which is good. However, were this line to occur after the getrandom
+ * syscall, then both child and parent could have the same bytes and the same
+ * generation counter, so the fork would not be detected. Therefore, write
+ * @state->generation before the call to the getrandom syscall.
+ */
+ WRITE_ONCE(state->generation, current_generation);
+
+ /*
+ * Prevent the syscall from being reordered wrt current_generation. Pairs with the
+ * smp_store_release(&_vdso_rng_data.generation) in random.c.
+ */
+ smp_rmb();
+
+ /* Reseed @state->key using fresh bytes from the kernel. */
+ if (getrandom_syscall(state->key, sizeof(state->key), 0) != sizeof(state->key)) {
+ /*
+ * If the syscall failed to refresh the key, then @state->key is now
+ * invalid, so invalidate the generation so that it is not used again, and
+ * fallback to using the syscall entirely.
+ */
+ WRITE_ONCE(state->generation, 0);
+
+ /*
+ * Set @state->in_use to false only after the last write to @state in the
+ * line above.
+ */
+ WRITE_ONCE(state->in_use, false);
+
+ goto fallback_syscall;
+ }
+
+ /*
+ * Set @state->pos to beyond the end of the batch, so that the batch is refilled
+ * using the new key.
+ */
+ state->pos = sizeof(state->batch);
+ }
+
+ /* Set len to the total amount of bytes that this function is allowed to read, ret. */
+ len = ret;
+more_batch:
+ /*
+ * First use bytes out of @state->batch, which may have been filled by the last call to this
+ * function.
+ */
+ batch_len = min_t(size_t, sizeof(state->batch) - state->pos, len);
+ if (batch_len) {
+ /* Zeroing at the same time as memcpying helps preserve forward secrecy. */
+ memcpy_and_zero_src(buffer, state->batch + state->pos, batch_len);
+ state->pos += batch_len;
+ buffer += batch_len;
+ len -= batch_len;
+ }
+
+ if (!len) {
+ /* Prevent the loop from being reordered wrt ->generation. */
+ barrier();
+
+ /*
+ * Since @rng_info->generation will never be 0, re-read @state->generation, rather
+ * than using the local current_generation variable, to learn whether a fork
+ * occurred or if @state was zeroed due to memory pressure. Primarily, though, this
+ * indicates whether the kernel's RNG has reseeded, in which case generate a new key
+ * and start over.
+ */
+ if (unlikely(READ_ONCE(state->generation) != READ_ONCE(rng_info->generation))) {
+ /*
+ * Prevent this from looping forever in case of low memory or racing with a
+ * user force-reseeding the kernel's RNG using the ioctl.
+ */
+ if (have_retried) {
+ WRITE_ONCE(state->in_use, false);
+ goto fallback_syscall;
+ }
+
+ have_retried = true;
+ buffer = orig_buffer;
+ goto retry_generation;
+ }
+
+ /*
+ * Set @state->in_use to false only when there will be no more reads or writes of
+ * @state.
+ */
+ WRITE_ONCE(state->in_use, false);
+ return ret;
+ }
+
+ /* Generate blocks of RNG output directly into @buffer while there's enough room left. */
+ nblocks = len / CHACHA_BLOCK_SIZE;
+ if (nblocks) {
+ __arch_chacha20_blocks_nostack(buffer, state->key, counter, nblocks);
+ buffer += nblocks * CHACHA_BLOCK_SIZE;
+ len -= nblocks * CHACHA_BLOCK_SIZE;
+ }
+
+ BUILD_BUG_ON(sizeof(state->batch_key) % CHACHA_BLOCK_SIZE != 0);
+
+ /* Refill the batch and overwrite the key, in order to preserve forward secrecy. */
+ __arch_chacha20_blocks_nostack(state->batch_key, state->key, counter,
+ sizeof(state->batch_key) / CHACHA_BLOCK_SIZE);
+
+ /* Since the batch was just refilled, set the position back to 0 to indicate a full batch. */
+ state->pos = 0;
+ goto more_batch;
+
+fallback_syscall:
+ return getrandom_syscall(orig_buffer, orig_len, flags);
+}
+
+static __always_inline ssize_t
+__cvdso_getrandom(void *buffer, size_t len, unsigned int flags, void *opaque_state, size_t opaque_len)
+{
+ return __cvdso_getrandom_data(__arch_get_vdso_rng_data(), buffer, len, flags, opaque_state, opaque_len);
+}
diff --git a/lib/vdso/gettimeofday.c b/lib/vdso/gettimeofday.c
index 899850bd6f0b..c01eaafd8041 100644
--- a/lib/vdso/gettimeofday.c
+++ b/lib/vdso/gettimeofday.c
@@ -140,14 +140,14 @@ static __always_inline int do_hres(const struct vdso_data *vd, clockid_t clk,
do {
/*
- * Open coded to handle VDSO_CLOCKMODE_TIMENS. Time namespace
- * enabled tasks have a special VVAR page installed which
- * has vd->seq set to 1 and vd->clock_mode set to
- * VDSO_CLOCKMODE_TIMENS. For non time namespace affected tasks
- * this does not affect performance because if vd->seq is
- * odd, i.e. a concurrent update is in progress the extra
- * check for vd->clock_mode is just a few extra
- * instructions while spin waiting for vd->seq to become
+ * Open coded function vdso_read_begin() to handle
+ * VDSO_CLOCKMODE_TIMENS. Time namespace enabled tasks have a
+ * special VVAR page installed which has vd->seq set to 1 and
+ * vd->clock_mode set to VDSO_CLOCKMODE_TIMENS. For non time
+ * namespace affected tasks this does not affect performance
+ * because if vd->seq is odd, i.e. a concurrent update is in
+ * progress the extra check for vd->clock_mode is just a few
+ * extra instructions while spin waiting for vd->seq to become
* even again.
*/
while (unlikely((seq = READ_ONCE(vd->seq)) & 1)) {
@@ -223,8 +223,8 @@ static __always_inline int do_coarse(const struct vdso_data *vd, clockid_t clk,
do {
/*
- * Open coded to handle VDSO_CLOCK_TIMENS. See comment in
- * do_hres().
+ * Open coded function vdso_read_begin() to handle
+ * VDSO_CLOCK_TIMENS. See comment in do_hres().
*/
while ((seq = READ_ONCE(vd->seq)) & 1) {
if (IS_ENABLED(CONFIG_TIME_NS) &&
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 552738f14275..c5e2ec9303c5 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -51,7 +51,7 @@
#include <asm/page.h> /* for PAGE_SIZE */
#include <asm/byteorder.h> /* cpu_to_le16 */
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/string_helpers.h>
#include "kstrtox.h"
@@ -966,13 +966,13 @@ char *bdev_name(char *buf, char *end, struct block_device *bdev,
hd = bdev->bd_disk;
buf = string(buf, end, hd->disk_name, spec);
- if (bdev->bd_partno) {
+ if (bdev_is_partition(bdev)) {
if (isdigit(hd->disk_name[strlen(hd->disk_name)-1])) {
if (buf < end)
*buf = 'p';
buf++;
}
- buf = number(buf, end, bdev->bd_partno, spec);
+ buf = number(buf, end, bdev_partno(bdev), spec);
}
return buf;
}
@@ -1080,7 +1080,7 @@ char *resource_string(char *buf, char *end, struct resource *res,
#define FLAG_BUF_SIZE (2 * sizeof(res->flags))
#define DECODED_BUF_SIZE sizeof("[mem - 64bit pref window disabled]")
#define RAW_BUF_SIZE sizeof("[mem - flags 0x]")
- char sym[max(2*RSRC_BUF_SIZE + DECODED_BUF_SIZE,
+ char sym[MAX(2*RSRC_BUF_SIZE + DECODED_BUF_SIZE,
2*RSRC_BUF_SIZE + FLAG_BUF_SIZE + RAW_BUF_SIZE)];
char *p = sym, *pend = sym + sizeof(sym);
@@ -2054,25 +2054,6 @@ char *format_page_flags(char *buf, char *end, unsigned long flags)
return buf;
}
-static
-char *format_page_type(char *buf, char *end, unsigned int page_type)
-{
- buf = number(buf, end, page_type, default_flag_spec);
-
- if (buf < end)
- *buf = '(';
- buf++;
-
- if (page_type_has_type(page_type))
- buf = format_flags(buf, end, ~page_type, pagetype_names);
-
- if (buf < end)
- *buf = ')';
- buf++;
-
- return buf;
-}
-
static noinline_for_stack
char *flags_string(char *buf, char *end, void *flags_ptr,
struct printf_spec spec, const char *fmt)
@@ -2086,8 +2067,6 @@ char *flags_string(char *buf, char *end, void *flags_ptr,
switch (fmt[1]) {
case 'p':
return format_page_flags(buf, end, *(unsigned long *)flags_ptr);
- case 't':
- return format_page_type(buf, end, *(unsigned int *)flags_ptr);
case 'v':
flags = *(unsigned long *)flags_ptr;
names = vmaflag_names;
diff --git a/lib/xarray.c b/lib/xarray.c
index 5e7d6334d70d..32d4bac8c94c 100644
--- a/lib/xarray.c
+++ b/lib/xarray.c
@@ -200,7 +200,8 @@ static void *xas_start(struct xa_state *xas)
return entry;
}
-static void *xas_descend(struct xa_state *xas, struct xa_node *node)
+static __always_inline void *xas_descend(struct xa_state *xas,
+ struct xa_node *node)
{
unsigned int offset = get_offset(xas->xa_index, node);
void *entry = xa_entry(xas->xa, node, offset);
@@ -1765,39 +1766,52 @@ unlock:
EXPORT_SYMBOL(xa_store_range);
/**
- * xa_get_order() - Get the order of an entry.
- * @xa: XArray.
- * @index: Index of the entry.
+ * xas_get_order() - Get the order of an entry.
+ * @xas: XArray operation state.
+ *
+ * Called after xas_load, the xas should not be in an error state.
*
* Return: A number between 0 and 63 indicating the order of the entry.
*/
-int xa_get_order(struct xarray *xa, unsigned long index)
+int xas_get_order(struct xa_state *xas)
{
- XA_STATE(xas, xa, index);
- void *entry;
int order = 0;
- rcu_read_lock();
- entry = xas_load(&xas);
-
- if (!entry)
- goto unlock;
-
- if (!xas.xa_node)
- goto unlock;
+ if (!xas->xa_node)
+ return 0;
for (;;) {
- unsigned int slot = xas.xa_offset + (1 << order);
+ unsigned int slot = xas->xa_offset + (1 << order);
if (slot >= XA_CHUNK_SIZE)
break;
- if (!xa_is_sibling(xas.xa_node->slots[slot]))
+ if (!xa_is_sibling(xa_entry(xas->xa, xas->xa_node, slot)))
break;
order++;
}
- order += xas.xa_node->shift;
-unlock:
+ order += xas->xa_node->shift;
+ return order;
+}
+EXPORT_SYMBOL_GPL(xas_get_order);
+
+/**
+ * xa_get_order() - Get the order of an entry.
+ * @xa: XArray.
+ * @index: Index of the entry.
+ *
+ * Return: A number between 0 and 63 indicating the order of the entry.
+ */
+int xa_get_order(struct xarray *xa, unsigned long index)
+{
+ XA_STATE(xas, xa, index);
+ int order = 0;
+ void *entry;
+
+ rcu_read_lock();
+ entry = xas_load(&xas);
+ if (entry)
+ order = xas_get_order(&xas);
rcu_read_unlock();
return order;
diff --git a/lib/xxhash.c b/lib/xxhash.c
index d5bb9ff10607..b5bd567aa6b3 100644
--- a/lib/xxhash.c
+++ b/lib/xxhash.c
@@ -38,7 +38,7 @@
* - xxHash source repository: https://github.com/Cyan4973/xxHash
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/errno.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
diff --git a/lib/xz/Kconfig b/lib/xz/Kconfig
index aef086a6bf2f..20aa459bfb3e 100644
--- a/lib/xz/Kconfig
+++ b/lib/xz/Kconfig
@@ -5,7 +5,8 @@ config XZ_DEC
help
LZMA2 compression algorithm and BCJ filters are supported using
the .xz file format as the container. For integrity checking,
- CRC32 is supported. See Documentation/staging/xz.rst for more information.
+ CRC32 is supported. See Documentation/staging/xz.rst for more
+ information.
if XZ_DEC
@@ -29,11 +30,21 @@ config XZ_DEC_ARMTHUMB
default y
select XZ_DEC_BCJ
+config XZ_DEC_ARM64
+ bool "ARM64 BCJ filter decoder" if EXPERT
+ default y
+ select XZ_DEC_BCJ
+
config XZ_DEC_SPARC
bool "SPARC BCJ filter decoder" if EXPERT
default y
select XZ_DEC_BCJ
+config XZ_DEC_RISCV
+ bool "RISC-V BCJ filter decoder" if EXPERT
+ default y
+ select XZ_DEC_BCJ
+
config XZ_DEC_MICROLZMA
bool "MicroLZMA decoder"
default n
diff --git a/lib/xz/xz_crc32.c b/lib/xz/xz_crc32.c
index 88a2c35e1b59..6a7906a328ba 100644
--- a/lib/xz/xz_crc32.c
+++ b/lib/xz/xz_crc32.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: 0BSD
+
/*
* CRC32 using the polynomial from IEEE-802.3
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <https://7-zip.org/>
- *
- * This file has been put into the public domain.
- * You can do whatever you want with this file.
*/
/*
@@ -27,9 +26,9 @@
STATIC_RW_DATA uint32_t xz_crc32_table[256];
-XZ_EXTERN void xz_crc32_init(void)
+void xz_crc32_init(void)
{
- const uint32_t poly = CRC32_POLY_LE;
+ const uint32_t poly = 0xEDB88320;
uint32_t i;
uint32_t j;
@@ -46,7 +45,7 @@ XZ_EXTERN void xz_crc32_init(void)
return;
}
-XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
+uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
{
crc = ~crc;
diff --git a/lib/xz/xz_dec_bcj.c b/lib/xz/xz_dec_bcj.c
index ef449e97d1a1..8237db17eee3 100644
--- a/lib/xz/xz_dec_bcj.c
+++ b/lib/xz/xz_dec_bcj.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: 0BSD
+
/*
* Branch/Call/Jump (BCJ) filter decoders
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <https://7-zip.org/>
- *
- * This file has been put into the public domain.
- * You can do whatever you want with this file.
*/
#include "xz_private.h"
@@ -24,7 +23,9 @@ struct xz_dec_bcj {
BCJ_IA64 = 6, /* Big or little endian */
BCJ_ARM = 7, /* Little endian only */
BCJ_ARMTHUMB = 8, /* Little endian only */
- BCJ_SPARC = 9 /* Big or little endian */
+ BCJ_SPARC = 9, /* Big or little endian */
+ BCJ_ARM64 = 10, /* AArch64 */
+ BCJ_RISCV = 11 /* RV32GQC_Zfh, RV64GQC_Zfh */
} type;
/*
@@ -162,7 +163,9 @@ static size_t bcj_powerpc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
size_t i;
uint32_t instr;
- for (i = 0; i + 4 <= size; i += 4) {
+ size &= ~(size_t)3;
+
+ for (i = 0; i < size; i += 4) {
instr = get_unaligned_be32(buf + i);
if ((instr & 0xFC000003) == 0x48000001) {
instr &= 0x03FFFFFC;
@@ -219,7 +222,9 @@ static size_t bcj_ia64(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
/* Instruction normalized with bit_res for easier manipulation */
uint64_t norm;
- for (i = 0; i + 16 <= size; i += 16) {
+ size &= ~(size_t)15;
+
+ for (i = 0; i < size; i += 16) {
mask = branch_table[buf[i] & 0x1F];
for (slot = 0, bit_pos = 5; slot < 3; ++slot, bit_pos += 41) {
if (((mask >> slot) & 1) == 0)
@@ -267,7 +272,9 @@ static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
size_t i;
uint32_t addr;
- for (i = 0; i + 4 <= size; i += 4) {
+ size &= ~(size_t)3;
+
+ for (i = 0; i < size; i += 4) {
if (buf[i + 3] == 0xEB) {
addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8)
| ((uint32_t)buf[i + 2] << 16);
@@ -290,7 +297,12 @@ static size_t bcj_armthumb(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
size_t i;
uint32_t addr;
- for (i = 0; i + 4 <= size; i += 2) {
+ if (size < 4)
+ return 0;
+
+ size -= 4;
+
+ for (i = 0; i <= size; i += 2) {
if ((buf[i + 1] & 0xF8) == 0xF0
&& (buf[i + 3] & 0xF8) == 0xF8) {
addr = (((uint32_t)buf[i + 1] & 0x07) << 19)
@@ -318,7 +330,9 @@ static size_t bcj_sparc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
size_t i;
uint32_t instr;
- for (i = 0; i + 4 <= size; i += 4) {
+ size &= ~(size_t)3;
+
+ for (i = 0; i < size; i += 4) {
instr = get_unaligned_be32(buf + i);
if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) {
instr <<= 2;
@@ -334,6 +348,140 @@ static size_t bcj_sparc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
}
#endif
+#ifdef XZ_DEC_ARM64
+static size_t bcj_arm64(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t instr;
+ uint32_t addr;
+
+ size &= ~(size_t)3;
+
+ for (i = 0; i < size; i += 4) {
+ instr = get_unaligned_le32(buf + i);
+
+ if ((instr >> 26) == 0x25) {
+ /* BL instruction */
+ addr = instr - ((s->pos + (uint32_t)i) >> 2);
+ instr = 0x94000000 | (addr & 0x03FFFFFF);
+ put_unaligned_le32(instr, buf + i);
+
+ } else if ((instr & 0x9F000000) == 0x90000000) {
+ /* ADRP instruction */
+ addr = ((instr >> 29) & 3) | ((instr >> 3) & 0x1FFFFC);
+
+ /* Only convert values in the range +/-512 MiB. */
+ if ((addr + 0x020000) & 0x1C0000)
+ continue;
+
+ addr -= (s->pos + (uint32_t)i) >> 12;
+
+ instr &= 0x9000001F;
+ instr |= (addr & 3) << 29;
+ instr |= (addr & 0x03FFFC) << 3;
+ instr |= (0U - (addr & 0x020000)) & 0xE00000;
+
+ put_unaligned_le32(instr, buf + i);
+ }
+ }
+
+ return i;
+}
+#endif
+
+#ifdef XZ_DEC_RISCV
+static size_t bcj_riscv(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t b1;
+ uint32_t b2;
+ uint32_t b3;
+ uint32_t instr;
+ uint32_t instr2;
+ uint32_t instr2_rs1;
+ uint32_t addr;
+
+ if (size < 8)
+ return 0;
+
+ size -= 8;
+
+ for (i = 0; i <= size; i += 2) {
+ instr = buf[i];
+
+ if (instr == 0xEF) {
+ /* JAL */
+ b1 = buf[i + 1];
+ if ((b1 & 0x0D) != 0)
+ continue;
+
+ b2 = buf[i + 2];
+ b3 = buf[i + 3];
+
+ addr = ((b1 & 0xF0) << 13) | (b2 << 9) | (b3 << 1);
+ addr -= s->pos + (uint32_t)i;
+
+ buf[i + 1] = (uint8_t)((b1 & 0x0F)
+ | ((addr >> 8) & 0xF0));
+
+ buf[i + 2] = (uint8_t)(((addr >> 16) & 0x0F)
+ | ((addr >> 7) & 0x10)
+ | ((addr << 4) & 0xE0));
+
+ buf[i + 3] = (uint8_t)(((addr >> 4) & 0x7F)
+ | ((addr >> 13) & 0x80));
+
+ i += 4 - 2;
+
+ } else if ((instr & 0x7F) == 0x17) {
+ /* AUIPC */
+ instr |= (uint32_t)buf[i + 1] << 8;
+ instr |= (uint32_t)buf[i + 2] << 16;
+ instr |= (uint32_t)buf[i + 3] << 24;
+
+ if (instr & 0xE80) {
+ /* AUIPC's rd doesn't equal x0 or x2. */
+ instr2 = get_unaligned_le32(buf + i + 4);
+
+ if (((instr << 8) ^ (instr2 - 3)) & 0xF8003) {
+ i += 6 - 2;
+ continue;
+ }
+
+ addr = (instr & 0xFFFFF000) + (instr2 >> 20);
+
+ instr = 0x17 | (2 << 7) | (instr2 << 12);
+ instr2 = addr;
+ } else {
+ /* AUIPC's rd equals x0 or x2. */
+ instr2_rs1 = instr >> 27;
+
+ if ((uint32_t)((instr - 0x3117) << 18)
+ >= (instr2_rs1 & 0x1D)) {
+ i += 4 - 2;
+ continue;
+ }
+
+ addr = get_unaligned_be32(buf + i + 4);
+ addr -= s->pos + (uint32_t)i;
+
+ instr2 = (instr >> 12) | (addr << 20);
+
+ instr = 0x17 | (instr2_rs1 << 7)
+ | ((addr + 0x800) & 0xFFFFF000);
+ }
+
+ put_unaligned_le32(instr, buf + i);
+ put_unaligned_le32(instr2, buf + i + 4);
+
+ i += 8 - 2;
+ }
+ }
+
+ return i;
+}
+#endif
+
/*
* Apply the selected BCJ filter. Update *pos and s->pos to match the amount
* of data that got filtered.
@@ -381,6 +529,16 @@ static void bcj_apply(struct xz_dec_bcj *s,
filtered = bcj_sparc(s, buf, size);
break;
#endif
+#ifdef XZ_DEC_ARM64
+ case BCJ_ARM64:
+ filtered = bcj_arm64(s, buf, size);
+ break;
+#endif
+#ifdef XZ_DEC_RISCV
+ case BCJ_RISCV:
+ filtered = bcj_riscv(s, buf, size);
+ break;
+#endif
default:
/* Never reached but silence compiler warnings. */
filtered = 0;
@@ -414,9 +572,8 @@ static void bcj_flush(struct xz_dec_bcj *s, struct xz_buf *b)
* data in chunks of 1-16 bytes. To hide this issue, this function does
* some buffering.
*/
-XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
- struct xz_dec_lzma2 *lzma2,
- struct xz_buf *b)
+enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s, struct xz_dec_lzma2 *lzma2,
+ struct xz_buf *b)
{
size_t out_start;
@@ -524,7 +681,7 @@ XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
return s->ret;
}
-XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call)
+struct xz_dec_bcj *xz_dec_bcj_create(bool single_call)
{
struct xz_dec_bcj *s = kmalloc(sizeof(*s), GFP_KERNEL);
if (s != NULL)
@@ -533,7 +690,7 @@ XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call)
return s;
}
-XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id)
+enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id)
{
switch (id) {
#ifdef XZ_DEC_X86
@@ -554,6 +711,12 @@ XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id)
#ifdef XZ_DEC_SPARC
case BCJ_SPARC:
#endif
+#ifdef XZ_DEC_ARM64
+ case BCJ_ARM64:
+#endif
+#ifdef XZ_DEC_RISCV
+ case BCJ_RISCV:
+#endif
break;
default:
diff --git a/lib/xz/xz_dec_lzma2.c b/lib/xz/xz_dec_lzma2.c
index 27ce34520e78..83bb66b6016d 100644
--- a/lib/xz/xz_dec_lzma2.c
+++ b/lib/xz/xz_dec_lzma2.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: 0BSD
+
/*
* LZMA2 decoder
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <https://7-zip.org/>
- *
- * This file has been put into the public domain.
- * You can do whatever you want with this file.
*/
#include "xz_private.h"
@@ -961,8 +960,7 @@ static bool lzma2_lzma(struct xz_dec_lzma2 *s, struct xz_buf *b)
* Take care of the LZMA2 control layer, and forward the job of actual LZMA
* decoding or copying of uncompressed chunks to other functions.
*/
-XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
- struct xz_buf *b)
+enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, struct xz_buf *b)
{
uint32_t tmp;
@@ -1138,8 +1136,7 @@ XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
return XZ_OK;
}
-XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
- uint32_t dict_max)
+struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode, uint32_t dict_max)
{
struct xz_dec_lzma2 *s = kmalloc(sizeof(*s), GFP_KERNEL);
if (s == NULL)
@@ -1162,7 +1159,7 @@ XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
return s;
}
-XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props)
+enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props)
{
/* This limits dictionary size to 3 GiB to keep parsing simpler. */
if (props > 39)
@@ -1198,7 +1195,7 @@ XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props)
return XZ_OK;
}
-XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s)
+void xz_dec_lzma2_end(struct xz_dec_lzma2 *s)
{
if (DEC_IS_MULTI(s->dict.mode))
vfree(s->dict.buf);
diff --git a/lib/xz/xz_dec_stream.c b/lib/xz/xz_dec_stream.c
index 683570b93a8c..f9d003684d56 100644
--- a/lib/xz/xz_dec_stream.c
+++ b/lib/xz/xz_dec_stream.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: 0BSD
+
/*
* .xz Stream decoder
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
- *
- * This file has been put into the public domain.
- * You can do whatever you want with this file.
*/
#include "xz_private.h"
@@ -747,7 +746,7 @@ static enum xz_ret dec_main(struct xz_dec *s, struct xz_buf *b)
* actually succeeds (that's the price to pay of using the output buffer as
* the workspace).
*/
-XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b)
+enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b)
{
size_t in_start;
size_t out_start;
@@ -783,7 +782,7 @@ XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b)
return ret;
}
-XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max)
+struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max)
{
struct xz_dec *s = kmalloc(sizeof(*s), GFP_KERNEL);
if (s == NULL)
@@ -813,7 +812,7 @@ error_bcj:
return NULL;
}
-XZ_EXTERN void xz_dec_reset(struct xz_dec *s)
+void xz_dec_reset(struct xz_dec *s)
{
s->sequence = SEQ_STREAM_HEADER;
s->allow_buf_error = false;
@@ -825,7 +824,7 @@ XZ_EXTERN void xz_dec_reset(struct xz_dec *s)
s->temp.size = STREAM_HEADER_SIZE;
}
-XZ_EXTERN void xz_dec_end(struct xz_dec *s)
+void xz_dec_end(struct xz_dec *s)
{
if (s != NULL) {
xz_dec_lzma2_end(s->lzma2);
diff --git a/lib/xz/xz_dec_syms.c b/lib/xz/xz_dec_syms.c
index 61098c67a413..f40817d65897 100644
--- a/lib/xz/xz_dec_syms.c
+++ b/lib/xz/xz_dec_syms.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: 0BSD
+
/*
* XZ decoder module information
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
- *
- * This file has been put into the public domain.
- * You can do whatever you want with this file.
*/
#include <linux/module.h>
@@ -23,11 +22,6 @@ EXPORT_SYMBOL(xz_dec_microlzma_end);
#endif
MODULE_DESCRIPTION("XZ decompressor");
-MODULE_VERSION("1.1");
+MODULE_VERSION("1.2");
MODULE_AUTHOR("Lasse Collin <lasse.collin@tukaani.org> and Igor Pavlov");
-
-/*
- * This code is in the public domain, but in Linux it's simplest to just
- * say it's GPL and consider the authors as the copyright holders.
- */
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/lib/xz/xz_dec_test.c b/lib/xz/xz_dec_test.c
index da28a19d6c98..53d3600f2ddb 100644
--- a/lib/xz/xz_dec_test.c
+++ b/lib/xz/xz_dec_test.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: 0BSD
+
/*
* XZ decoder tester
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
- *
- * This file has been put into the public domain.
- * You can do whatever you want with this file.
*/
#include <linux/kernel.h>
@@ -212,9 +211,4 @@ module_exit(xz_dec_test_exit);
MODULE_DESCRIPTION("XZ decompressor tester");
MODULE_VERSION("1.0");
MODULE_AUTHOR("Lasse Collin <lasse.collin@tukaani.org>");
-
-/*
- * This code is in the public domain, but in Linux it's simplest to just
- * say it's GPL and consider the authors as the copyright holders.
- */
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/lib/xz/xz_lzma2.h b/lib/xz/xz_lzma2.h
index 92d852d4f87a..d2632b7dfb9c 100644
--- a/lib/xz/xz_lzma2.h
+++ b/lib/xz/xz_lzma2.h
@@ -1,11 +1,10 @@
+/* SPDX-License-Identifier: 0BSD */
+
/*
* LZMA2 definitions
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
* Igor Pavlov <https://7-zip.org/>
- *
- * This file has been put into the public domain.
- * You can do whatever you want with this file.
*/
#ifndef XZ_LZMA2_H
diff --git a/lib/xz/xz_private.h b/lib/xz/xz_private.h
index bf1e94ec7873..8409784b1639 100644
--- a/lib/xz/xz_private.h
+++ b/lib/xz/xz_private.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: 0BSD */
+
/*
* Private includes and definitions
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
- *
- * This file has been put into the public domain.
- * You can do whatever you want with this file.
*/
#ifndef XZ_PRIVATE_H
@@ -13,7 +12,7 @@
#ifdef __KERNEL__
# include <linux/xz.h>
# include <linux/kernel.h>
-# include <asm/unaligned.h>
+# include <linux/unaligned.h>
/* XZ_PREBOOT may be defined only via decompress_unxz.c. */
# ifndef XZ_PREBOOT
# include <linux/slab.h>
@@ -37,6 +36,12 @@
# ifdef CONFIG_XZ_DEC_SPARC
# define XZ_DEC_SPARC
# endif
+# ifdef CONFIG_XZ_DEC_ARM64
+# define XZ_DEC_ARM64
+# endif
+# ifdef CONFIG_XZ_DEC_RISCV
+# define XZ_DEC_RISCV
+# endif
# ifdef CONFIG_XZ_DEC_MICROLZMA
# define XZ_DEC_MICROLZMA
# endif
@@ -98,23 +103,19 @@
*/
#ifndef XZ_DEC_BCJ
# if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \
- || defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \
+ || defined(XZ_DEC_IA64) \
|| defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \
- || defined(XZ_DEC_SPARC)
+ || defined(XZ_DEC_SPARC) || defined(XZ_DEC_ARM64) \
+ || defined(XZ_DEC_RISCV)
# define XZ_DEC_BCJ
# endif
#endif
-#ifndef CRC32_POLY_LE
-#define CRC32_POLY_LE 0xedb88320
-#endif
-
/*
* Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used
* before calling xz_dec_lzma2_run().
*/
-XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
- uint32_t dict_max);
+struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode, uint32_t dict_max);
/*
* Decode the LZMA2 properties (one byte) and reset the decoder. Return
@@ -122,22 +123,20 @@ XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
* big enough, and XZ_OPTIONS_ERROR if props indicates something that this
* decoder doesn't support.
*/
-XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s,
- uint8_t props);
+enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props);
/* Decode raw LZMA2 stream from b->in to b->out. */
-XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
- struct xz_buf *b);
+enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, struct xz_buf *b);
/* Free the memory allocated for the LZMA2 decoder. */
-XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s);
+void xz_dec_lzma2_end(struct xz_dec_lzma2 *s);
#ifdef XZ_DEC_BCJ
/*
* Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before
* calling xz_dec_bcj_run().
*/
-XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call);
+struct xz_dec_bcj *xz_dec_bcj_create(bool single_call);
/*
* Decode the Filter ID of a BCJ filter. This implementation doesn't
@@ -145,16 +144,15 @@ XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call);
* is needed. Returns XZ_OK if the given Filter ID is supported.
* Otherwise XZ_OPTIONS_ERROR is returned.
*/
-XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id);
+enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id);
/*
* Decode raw BCJ + LZMA2 stream. This must be used only if there actually is
* a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run()
* must be called directly.
*/
-XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
- struct xz_dec_lzma2 *lzma2,
- struct xz_buf *b);
+enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s, struct xz_dec_lzma2 *lzma2,
+ struct xz_buf *b);
/* Free the memory allocated for the BCJ filters. */
#define xz_dec_bcj_end(s) kfree(s)
diff --git a/lib/xz/xz_stream.h b/lib/xz/xz_stream.h
index 430bb3a0d195..55f9f6f94b78 100644
--- a/lib/xz/xz_stream.h
+++ b/lib/xz/xz_stream.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: 0BSD */
+
/*
* Definitions for handling the .xz file format
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
- *
- * This file has been put into the public domain.
- * You can do whatever you want with this file.
*/
#ifndef XZ_STREAM_H
diff --git a/lib/zlib_deflate/deflate_syms.c b/lib/zlib_deflate/deflate_syms.c
index 24b740b99678..68941a2350ea 100644
--- a/lib/zlib_deflate/deflate_syms.c
+++ b/lib/zlib_deflate/deflate_syms.c
@@ -17,4 +17,5 @@ EXPORT_SYMBOL(zlib_deflate);
EXPORT_SYMBOL(zlib_deflateInit2);
EXPORT_SYMBOL(zlib_deflateEnd);
EXPORT_SYMBOL(zlib_deflateReset);
+MODULE_DESCRIPTION("Data compression using the deflation algorithm");
MODULE_LICENSE("GPL");
diff --git a/lib/zlib_dfltcc/dfltcc.h b/lib/zlib_dfltcc/dfltcc.h
index b96232bdd44d..0f2a16d7a48a 100644
--- a/lib/zlib_dfltcc/dfltcc.h
+++ b/lib/zlib_dfltcc/dfltcc.h
@@ -80,6 +80,7 @@ struct dfltcc_param_v0 {
uint8_t csb[1152];
};
+static_assert(offsetof(struct dfltcc_param_v0, csb) == 384);
static_assert(sizeof(struct dfltcc_param_v0) == 1536);
#define CVT_CRC32 0
diff --git a/lib/zlib_dfltcc/dfltcc_util.h b/lib/zlib_dfltcc/dfltcc_util.h
index 4a46b5009f0d..10509270d822 100644
--- a/lib/zlib_dfltcc/dfltcc_util.h
+++ b/lib/zlib_dfltcc/dfltcc_util.h
@@ -2,6 +2,8 @@
#ifndef DFLTCC_UTIL_H
#define DFLTCC_UTIL_H
+#include "dfltcc.h"
+#include <linux/kmsan-checks.h>
#include <linux/zutil.h>
/*
@@ -20,6 +22,7 @@ typedef enum {
#define DFLTCC_CMPR 2
#define DFLTCC_XPND 4
#define HBT_CIRCULAR (1 << 7)
+#define DFLTCC_FN_MASK ((1 << 7) - 1)
#define HB_BITS 15
#define HB_SIZE (1 << HB_BITS)
@@ -34,6 +37,7 @@ static inline dfltcc_cc dfltcc(
)
{
Byte *t2 = op1 ? *op1 : NULL;
+ unsigned char *orig_t2 = t2;
size_t t3 = len1 ? *len1 : 0;
const Byte *t4 = op2 ? *op2 : NULL;
size_t t5 = len2 ? *len2 : 0;
@@ -59,6 +63,30 @@ static inline dfltcc_cc dfltcc(
: "cc", "memory");
t2 = r2; t3 = r3; t4 = r4; t5 = r5;
+ /*
+ * Unpoison the parameter block and the output buffer.
+ * This is a no-op in non-KMSAN builds.
+ */
+ switch (fn & DFLTCC_FN_MASK) {
+ case DFLTCC_QAF:
+ kmsan_unpoison_memory(param, sizeof(struct dfltcc_qaf_param));
+ break;
+ case DFLTCC_GDHT:
+ kmsan_unpoison_memory(param, offsetof(struct dfltcc_param_v0, csb));
+ break;
+ case DFLTCC_CMPR:
+ kmsan_unpoison_memory(param, sizeof(struct dfltcc_param_v0));
+ kmsan_unpoison_memory(
+ orig_t2,
+ t2 - orig_t2 +
+ (((struct dfltcc_param_v0 *)param)->sbb == 0 ? 0 : 1));
+ break;
+ case DFLTCC_XPND:
+ kmsan_unpoison_memory(param, sizeof(struct dfltcc_param_v0));
+ kmsan_unpoison_memory(orig_t2, t2 - orig_t2);
+ break;
+ }
+
if (op1)
*op1 = t2;
if (len1)
diff --git a/lib/zstd/common/mem.h b/lib/zstd/common/mem.h
index 1d9cc03924ca..c22a2e69bf46 100644
--- a/lib/zstd/common/mem.h
+++ b/lib/zstd/common/mem.h
@@ -15,7 +15,7 @@
/*-****************************************
* Dependencies
******************************************/
-#include <asm/unaligned.h> /* get_unaligned, put_unaligned* */
+#include <linux/unaligned.h> /* get_unaligned, put_unaligned* */
#include <linux/compiler.h> /* inline */
#include <linux/swab.h> /* swab32, swab64 */
#include <linux/types.h> /* size_t, ptrdiff_t */
diff --git a/lib/zstd/compress/zstd_compress.c b/lib/zstd/compress/zstd_compress.c
index f620cafca633..16bb995bc6c4 100644
--- a/lib/zstd/compress/zstd_compress.c
+++ b/lib/zstd/compress/zstd_compress.c
@@ -4810,6 +4810,8 @@ ZSTD_CDict* ZSTD_createCDict_advanced2(
dictLoadMethod, cctxParams.cParams,
cctxParams.useRowMatchFinder, cctxParams.enableDedicatedDictSearch,
customMem);
+ if (!cdict)
+ return NULL;
if (ZSTD_isError( ZSTD_initCDict_internal(cdict,
dict, dictSize,
diff --git a/lib/zstd/zstd_compress_module.c b/lib/zstd/zstd_compress_module.c
index 04e1b5c01d9b..bd8784449b31 100644
--- a/lib/zstd/zstd_compress_module.c
+++ b/lib/zstd/zstd_compress_module.c
@@ -66,6 +66,12 @@ int zstd_max_clevel(void)
}
EXPORT_SYMBOL(zstd_max_clevel);
+int zstd_default_clevel(void)
+{
+ return ZSTD_defaultCLevel();
+}
+EXPORT_SYMBOL(zstd_default_clevel);
+
size_t zstd_compress_bound(size_t src_size)
{
return ZSTD_compressBound(src_size);
@@ -79,6 +85,13 @@ zstd_parameters zstd_get_params(int level,
}
EXPORT_SYMBOL(zstd_get_params);
+zstd_compression_parameters zstd_get_cparams(int level,
+ unsigned long long estimated_src_size, size_t dict_size)
+{
+ return ZSTD_getCParams(level, estimated_src_size, dict_size);
+}
+EXPORT_SYMBOL(zstd_get_cparams);
+
size_t zstd_cctx_workspace_bound(const zstd_compression_parameters *cparams)
{
return ZSTD_estimateCCtxSize_usingCParams(*cparams);
@@ -93,6 +106,33 @@ zstd_cctx *zstd_init_cctx(void *workspace, size_t workspace_size)
}
EXPORT_SYMBOL(zstd_init_cctx);
+zstd_cctx *zstd_create_cctx_advanced(zstd_custom_mem custom_mem)
+{
+ return ZSTD_createCCtx_advanced(custom_mem);
+}
+EXPORT_SYMBOL(zstd_create_cctx_advanced);
+
+size_t zstd_free_cctx(zstd_cctx *cctx)
+{
+ return ZSTD_freeCCtx(cctx);
+}
+EXPORT_SYMBOL(zstd_free_cctx);
+
+zstd_cdict *zstd_create_cdict_byreference(const void *dict, size_t dict_size,
+ zstd_compression_parameters cparams,
+ zstd_custom_mem custom_mem)
+{
+ return ZSTD_createCDict_advanced(dict, dict_size, ZSTD_dlm_byRef,
+ ZSTD_dct_auto, cparams, custom_mem);
+}
+EXPORT_SYMBOL(zstd_create_cdict_byreference);
+
+size_t zstd_free_cdict(zstd_cdict *cdict)
+{
+ return ZSTD_freeCDict(cdict);
+}
+EXPORT_SYMBOL(zstd_free_cdict);
+
size_t zstd_compress_cctx(zstd_cctx *cctx, void *dst, size_t dst_capacity,
const void *src, size_t src_size, const zstd_parameters *parameters)
{
@@ -101,6 +141,15 @@ size_t zstd_compress_cctx(zstd_cctx *cctx, void *dst, size_t dst_capacity,
}
EXPORT_SYMBOL(zstd_compress_cctx);
+size_t zstd_compress_using_cdict(zstd_cctx *cctx, void *dst,
+ size_t dst_capacity, const void *src, size_t src_size,
+ const ZSTD_CDict *cdict)
+{
+ return ZSTD_compress_usingCDict(cctx, dst, dst_capacity,
+ src, src_size, cdict);
+}
+EXPORT_SYMBOL(zstd_compress_using_cdict);
+
size_t zstd_cstream_workspace_bound(const zstd_compression_parameters *cparams)
{
return ZSTD_estimateCStreamSize_usingCParams(*cparams);
diff --git a/lib/zstd/zstd_decompress_module.c b/lib/zstd/zstd_decompress_module.c
index f4ed952ed485..469fc3059be0 100644
--- a/lib/zstd/zstd_decompress_module.c
+++ b/lib/zstd/zstd_decompress_module.c
@@ -44,6 +44,33 @@ size_t zstd_dctx_workspace_bound(void)
}
EXPORT_SYMBOL(zstd_dctx_workspace_bound);
+zstd_dctx *zstd_create_dctx_advanced(zstd_custom_mem custom_mem)
+{
+ return ZSTD_createDCtx_advanced(custom_mem);
+}
+EXPORT_SYMBOL(zstd_create_dctx_advanced);
+
+size_t zstd_free_dctx(zstd_dctx *dctx)
+{
+ return ZSTD_freeDCtx(dctx);
+}
+EXPORT_SYMBOL(zstd_free_dctx);
+
+zstd_ddict *zstd_create_ddict_byreference(const void *dict, size_t dict_size,
+ zstd_custom_mem custom_mem)
+{
+ return ZSTD_createDDict_advanced(dict, dict_size, ZSTD_dlm_byRef,
+ ZSTD_dct_auto, custom_mem);
+
+}
+EXPORT_SYMBOL(zstd_create_ddict_byreference);
+
+size_t zstd_free_ddict(zstd_ddict *ddict)
+{
+ return ZSTD_freeDDict(ddict);
+}
+EXPORT_SYMBOL(zstd_free_ddict);
+
zstd_dctx *zstd_init_dctx(void *workspace, size_t workspace_size)
{
if (workspace == NULL)
@@ -59,6 +86,15 @@ size_t zstd_decompress_dctx(zstd_dctx *dctx, void *dst, size_t dst_capacity,
}
EXPORT_SYMBOL(zstd_decompress_dctx);
+size_t zstd_decompress_using_ddict(zstd_dctx *dctx,
+ void *dst, size_t dst_capacity, const void* src, size_t src_size,
+ const zstd_ddict* ddict)
+{
+ return ZSTD_decompress_usingDDict(dctx, dst, dst_capacity, src,
+ src_size, ddict);
+}
+EXPORT_SYMBOL(zstd_decompress_using_ddict);
+
size_t zstd_dstream_workspace_bound(size_t max_window_size)
{
return ZSTD_estimateDStreamSize(max_window_size);