diff options
author | 2019-07-28 22:22:40 +0200 | |
---|---|---|
committer | 2019-07-28 22:22:40 +0200 | |
commit | 7a30bdd99f37352b188575b27924c407c6ddff9e (patch) | |
tree | 10ea40ab1b5211e75c33eaddb3a6b393ad6ee7ad /lib | |
parent | x86/speculation/swapgs: Exclude ATOMs from speculation through SWAPGS (diff) | |
parent | Linux 5.3-rc2 (diff) | |
download | wireguard-linux-7a30bdd99f37352b188575b27924c407c6ddff9e.tar.xz wireguard-linux-7a30bdd99f37352b188575b27924c407c6ddff9e.zip |
Merge branch master from git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Pick up the spectre documentation so the Grand Schemozzle can be added.
Diffstat (limited to 'lib')
38 files changed, 1554 insertions, 531 deletions
diff --git a/lib/842/842_debugfs.h b/lib/842/842_debugfs.h index 277e403e8701..4469407c3e0d 100644 --- a/lib/842/842_debugfs.h +++ b/lib/842/842_debugfs.h @@ -22,8 +22,6 @@ static int __init sw842_debugfs_create(void) return -ENODEV; sw842_debugfs_root = debugfs_create_dir(MODULE_NAME, NULL); - if (IS_ERR(sw842_debugfs_root)) - return PTR_ERR(sw842_debugfs_root); for (i = 0; i < ARRAY_SIZE(template_count); i++) { char name[32]; @@ -46,8 +44,7 @@ static int __init sw842_debugfs_create(void) static void __exit sw842_debugfs_remove(void) { - if (sw842_debugfs_root && !IS_ERR(sw842_debugfs_root)) - debugfs_remove_recursive(sw842_debugfs_root); + debugfs_remove_recursive(sw842_debugfs_root); } #endif diff --git a/lib/Kconfig b/lib/Kconfig index 8c8eefc5e54c..f33d66fc0e86 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -531,14 +531,6 @@ config LRU_CACHE config CLZ_TAB bool -config DDR - bool "JEDEC DDR data" - help - Data from JEDEC specs for DDR SDRAM memories, - particularly the AC timing parameters and addressing - information. This data is useful for drivers handling - DDR SDRAM controllers. - config IRQ_POLL bool "IRQ polling library" help @@ -562,6 +554,14 @@ config SIGNATURE Digital signature verification. Currently only RSA is supported. Implementation is done using GnuPG MPI library +config DIMLIB + bool "DIM library" + default y + help + Dynamic Interrupt Moderation library. + Implements an algorithm for dynamically change CQ modertion values + according to run time performance. + # # libfdt files, only selected if needed. # diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 06d9c9d70385..5960e2980a8a 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -305,19 +305,26 @@ config DEBUG_FS If unsure, say N. -config HEADERS_CHECK - bool "Run 'make headers_check' when building vmlinux" +config HEADERS_INSTALL + bool "Install uapi headers to usr/include" depends on !UML help - This option will extract the user-visible kernel headers whenever - building the kernel, and will run basic sanity checks on them to - ensure that exported files do not attempt to include files which - were not exported, etc. + This option will install uapi headers (headers exported to user-space) + into the usr/include directory for use during the kernel build. + This is unneeded for building the kernel itself, but needed for some + user-space program samples. It is also needed by some features such + as uapi header sanity checks. + +config HEADERS_CHECK + bool "Run sanity checks on uapi headers when building 'all'" + depends on HEADERS_INSTALL + help + This option will run basic sanity checks on uapi headers when + building the 'all' target, for example, ensure that they do not + attempt to include files which were not exported, etc. If you're making modifications to header files which are - relevant for userspace, say 'Y', and check the headers - exported to $(INSTALL_HDR_PATH) (usually 'usr/include' in - your build tree), to make sure they're suitable. + relevant for userspace, say 'Y'. config OPTIMIZE_INLINING bool "Allow compiler to uninline functions marked 'inline'" @@ -346,23 +353,13 @@ config DEBUG_SECTION_MISMATCH which results in the code/data being placed in specific sections. The section mismatch analysis is always performed after a full kernel build, and enabling this option causes the following - additional steps to occur: + additional step to occur: - Add the option -fno-inline-functions-called-once to gcc commands. When inlining a function annotated with __init in a non-init function, we would lose the section information and thus the analysis would not catch the illegal reference. This option tells gcc to inline less (but it does result in a larger kernel). - - Run the section mismatch analysis for each module/built-in.a file. - When we run the section mismatch analysis on vmlinux.o, we - lose valuable information about where the mismatch was - introduced. - Running the analysis for each module/built-in.a file - tells where the mismatch happens much closer to the - source. The drawback is that the same mismatch is - reported at least twice. - - Enable verbose reporting from modpost in order to help resolve - the section mismatches that are reported. config SECTION_MISMATCH_WARN_ONLY bool "Make section mismatch errors non-fatal" @@ -1132,7 +1129,7 @@ config PROVE_LOCKING the proof of observed correctness is also maintained for an arbitrary combination of these separate locking variants. - For more details, see Documentation/locking/lockdep-design.txt. + For more details, see Documentation/locking/lockdep-design.rst. config LOCK_STAT bool "Lock usage statistics" @@ -1146,7 +1143,7 @@ config LOCK_STAT help This feature enables tracking lock contention points - For more details, see Documentation/locking/lockstat.txt + For more details, see Documentation/locking/lockstat.rst This also enables lock events required by "perf lock", subcommand of perf. @@ -1701,7 +1698,7 @@ config LKDTM called lkdtm. Documentation on how to use the module can be found in - Documentation/fault-injection/provoke-crashes.txt + Documentation/fault-injection/provoke-crashes.rst config TEST_LIST_SORT tristate "Linked list sorting test" @@ -1929,6 +1926,15 @@ config TEST_BPF If unsure, say N. +config TEST_BLACKHOLE_DEV + tristate "Test blackhole netdev functionality" + depends on m && NET + help + This builds the "test_blackhole_dev" module that validates the + data path through this blackhole netdev. + + If unsure, say N. + config FIND_BIT_BENCHMARK tristate "Test find_bit functions" help @@ -2060,6 +2066,14 @@ config TEST_STACKINIT If unsure, say N. +config TEST_MEMINIT + tristate "Test heap/page initialization" + help + Test if the kernel is zero-initializing heap and page allocations. + This can be useful to test init_on_alloc and init_on_free features. + + If unsure, say N. + endif # RUNTIME_TESTING_MENU config MEMTEST diff --git a/lib/Makefile b/lib/Makefile index fb7697031a79..095601ce371d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -91,6 +91,8 @@ obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o obj-$(CONFIG_TEST_STACKINIT) += test_stackinit.o +obj-$(CONFIG_TEST_BLACKHOLE_DEV) += test_blackhole_dev.o +obj-$(CONFIG_TEST_MEMINIT) += test_meminit.o obj-$(CONFIG_TEST_LIVEPATCH) += livepatch/ @@ -102,7 +104,7 @@ endif obj-$(CONFIG_DEBUG_INFO_REDUCED) += debug_info.o CFLAGS_debug_info.o += $(call cc-option, -femit-struct-debug-detailed=any) -obj-y += math/ +obj-y += math/ crypto/ obj-$(CONFIG_GENERIC_IOMAP) += iomap.o obj-$(CONFIG_GENERIC_PCI_IOMAP) += pci_iomap.o @@ -202,12 +204,11 @@ obj-$(CONFIG_GLOB) += glob.o obj-$(CONFIG_GLOB_SELFTEST) += globtest.o obj-$(CONFIG_MPILIB) += mpi/ +obj-$(CONFIG_DIMLIB) += dim/ obj-$(CONFIG_SIGNATURE) += digsig.o lib-$(CONFIG_CLZ_TAB) += clz_tab.o -obj-$(CONFIG_DDR) += jedec_ddr_data.o - obj-$(CONFIG_GENERIC_STRNCPY_FROM_USER) += strncpy_from_user.o obj-$(CONFIG_GENERIC_STRNLEN_USER) += strnlen_user.o diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile new file mode 100644 index 000000000000..88195c34932d --- /dev/null +++ b/lib/crypto/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_CRYPTO_LIB_ARC4) += libarc4.o +libarc4-y := arc4.o diff --git a/lib/crypto/arc4.c b/lib/crypto/arc4.c new file mode 100644 index 000000000000..c2020f19c652 --- /dev/null +++ b/lib/crypto/arc4.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Cryptographic API + * + * ARC4 Cipher Algorithm + * + * Jon Oberheide <jon@oberheide.org> + */ + +#include <crypto/arc4.h> +#include <linux/module.h> + +int arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len) +{ + int i, j = 0, k = 0; + + ctx->x = 1; + ctx->y = 0; + + for (i = 0; i < 256; i++) + ctx->S[i] = i; + + for (i = 0; i < 256; i++) { + u32 a = ctx->S[i]; + + j = (j + in_key[k] + a) & 0xff; + ctx->S[i] = ctx->S[j]; + ctx->S[j] = a; + if (++k >= key_len) + k = 0; + } + + return 0; +} +EXPORT_SYMBOL(arc4_setkey); + +void arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len) +{ + u32 *const S = ctx->S; + u32 x, y, a, b; + u32 ty, ta, tb; + + if (len == 0) + return; + + x = ctx->x; + y = ctx->y; + + a = S[x]; + y = (y + a) & 0xff; + b = S[y]; + + do { + S[y] = a; + a = (a + b) & 0xff; + S[x] = b; + x = (x + 1) & 0xff; + ta = S[x]; + ty = (y + ta) & 0xff; + tb = S[ty]; + *out++ = *in++ ^ S[a]; + if (--len == 0) + break; + y = ty; + a = ta; + b = tb; + } while (true); + + ctx->x = x; + ctx->y = y; +} +EXPORT_SYMBOL(arc4_crypt); + +MODULE_LICENSE("GPL"); diff --git a/lib/digsig.c b/lib/digsig.c index 3cf89c775ab2..e0627c3e53b2 100644 --- a/lib/digsig.c +++ b/lib/digsig.c @@ -218,7 +218,7 @@ int digsig_verify(struct key *keyring, const char *sig, int siglen, /* search in specific keyring */ key_ref_t kref; kref = keyring_search(make_key_ref(keyring, 1UL), - &key_type_user, name); + &key_type_user, name, true); if (IS_ERR(kref)) key = ERR_CAST(kref); else diff --git a/lib/dim/Makefile b/lib/dim/Makefile new file mode 100644 index 000000000000..1d6858a108cb --- /dev/null +++ b/lib/dim/Makefile @@ -0,0 +1,7 @@ +# +# DIM Dynamic Interrupt Moderation library +# + +obj-$(CONFIG_DIMLIB) += dim.o + +dim-y := dim.o net_dim.o rdma_dim.o diff --git a/lib/dim/dim.c b/lib/dim/dim.c new file mode 100644 index 000000000000..439d641ec796 --- /dev/null +++ b/lib/dim/dim.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * Copyright (c) 2019, Mellanox Technologies inc. All rights reserved. + */ + +#include <linux/dim.h> + +bool dim_on_top(struct dim *dim) +{ + switch (dim->tune_state) { + case DIM_PARKING_ON_TOP: + case DIM_PARKING_TIRED: + return true; + case DIM_GOING_RIGHT: + return (dim->steps_left > 1) && (dim->steps_right == 1); + default: /* DIM_GOING_LEFT */ + return (dim->steps_right > 1) && (dim->steps_left == 1); + } +} +EXPORT_SYMBOL(dim_on_top); + +void dim_turn(struct dim *dim) +{ + switch (dim->tune_state) { + case DIM_PARKING_ON_TOP: + case DIM_PARKING_TIRED: + break; + case DIM_GOING_RIGHT: + dim->tune_state = DIM_GOING_LEFT; + dim->steps_left = 0; + break; + case DIM_GOING_LEFT: + dim->tune_state = DIM_GOING_RIGHT; + dim->steps_right = 0; + break; + } +} +EXPORT_SYMBOL(dim_turn); + +void dim_park_on_top(struct dim *dim) +{ + dim->steps_right = 0; + dim->steps_left = 0; + dim->tired = 0; + dim->tune_state = DIM_PARKING_ON_TOP; +} +EXPORT_SYMBOL(dim_park_on_top); + +void dim_park_tired(struct dim *dim) +{ + dim->steps_right = 0; + dim->steps_left = 0; + dim->tune_state = DIM_PARKING_TIRED; +} +EXPORT_SYMBOL(dim_park_tired); + +void dim_calc_stats(struct dim_sample *start, struct dim_sample *end, + struct dim_stats *curr_stats) +{ + /* u32 holds up to 71 minutes, should be enough */ + u32 delta_us = ktime_us_delta(end->time, start->time); + u32 npkts = BIT_GAP(BITS_PER_TYPE(u32), end->pkt_ctr, start->pkt_ctr); + u32 nbytes = BIT_GAP(BITS_PER_TYPE(u32), end->byte_ctr, + start->byte_ctr); + u32 ncomps = BIT_GAP(BITS_PER_TYPE(u32), end->comp_ctr, + start->comp_ctr); + + if (!delta_us) + return; + + curr_stats->ppms = DIV_ROUND_UP(npkts * USEC_PER_MSEC, delta_us); + curr_stats->bpms = DIV_ROUND_UP(nbytes * USEC_PER_MSEC, delta_us); + curr_stats->epms = DIV_ROUND_UP(DIM_NEVENTS * USEC_PER_MSEC, + delta_us); + curr_stats->cpms = DIV_ROUND_UP(ncomps * USEC_PER_MSEC, delta_us); + if (curr_stats->epms != 0) + curr_stats->cpe_ratio = + (curr_stats->cpms * 100) / curr_stats->epms; + else + curr_stats->cpe_ratio = 0; + +} +EXPORT_SYMBOL(dim_calc_stats); diff --git a/lib/dim/net_dim.c b/lib/dim/net_dim.c new file mode 100644 index 000000000000..5bcc902c5388 --- /dev/null +++ b/lib/dim/net_dim.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * Copyright (c) 2018, Mellanox Technologies inc. All rights reserved. + */ + +#include <linux/dim.h> + +struct dim_cq_moder +net_dim_get_rx_moderation(u8 cq_period_mode, int ix) +{ + struct dim_cq_moder cq_moder = rx_profile[cq_period_mode][ix]; + + cq_moder.cq_period_mode = cq_period_mode; + return cq_moder; +} +EXPORT_SYMBOL(net_dim_get_rx_moderation); + +struct dim_cq_moder +net_dim_get_def_rx_moderation(u8 cq_period_mode) +{ + u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ? + NET_DIM_DEF_PROFILE_CQE : NET_DIM_DEF_PROFILE_EQE; + + return net_dim_get_rx_moderation(cq_period_mode, profile_ix); +} +EXPORT_SYMBOL(net_dim_get_def_rx_moderation); + +struct dim_cq_moder +net_dim_get_tx_moderation(u8 cq_period_mode, int ix) +{ + struct dim_cq_moder cq_moder = tx_profile[cq_period_mode][ix]; + + cq_moder.cq_period_mode = cq_period_mode; + return cq_moder; +} +EXPORT_SYMBOL(net_dim_get_tx_moderation); + +struct dim_cq_moder +net_dim_get_def_tx_moderation(u8 cq_period_mode) +{ + u8 profile_ix = cq_period_mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE ? + NET_DIM_DEF_PROFILE_CQE : NET_DIM_DEF_PROFILE_EQE; + + return net_dim_get_tx_moderation(cq_period_mode, profile_ix); +} +EXPORT_SYMBOL(net_dim_get_def_tx_moderation); + +static int net_dim_step(struct dim *dim) +{ + if (dim->tired == (NET_DIM_PARAMS_NUM_PROFILES * 2)) + return DIM_TOO_TIRED; + + switch (dim->tune_state) { + case DIM_PARKING_ON_TOP: + case DIM_PARKING_TIRED: + break; + case DIM_GOING_RIGHT: + if (dim->profile_ix == (NET_DIM_PARAMS_NUM_PROFILES - 1)) + return DIM_ON_EDGE; + dim->profile_ix++; + dim->steps_right++; + break; + case DIM_GOING_LEFT: + if (dim->profile_ix == 0) + return DIM_ON_EDGE; + dim->profile_ix--; + dim->steps_left++; + break; + } + + dim->tired++; + return DIM_STEPPED; +} + +static void net_dim_exit_parking(struct dim *dim) +{ + dim->tune_state = dim->profile_ix ? DIM_GOING_LEFT : DIM_GOING_RIGHT; + net_dim_step(dim); +} + +static int net_dim_stats_compare(struct dim_stats *curr, + struct dim_stats *prev) +{ + if (!prev->bpms) + return curr->bpms ? DIM_STATS_BETTER : DIM_STATS_SAME; + + if (IS_SIGNIFICANT_DIFF(curr->bpms, prev->bpms)) + return (curr->bpms > prev->bpms) ? DIM_STATS_BETTER : + DIM_STATS_WORSE; + + if (!prev->ppms) + return curr->ppms ? DIM_STATS_BETTER : + DIM_STATS_SAME; + + if (IS_SIGNIFICANT_DIFF(curr->ppms, prev->ppms)) + return (curr->ppms > prev->ppms) ? DIM_STATS_BETTER : + DIM_STATS_WORSE; + + if (!prev->epms) + return DIM_STATS_SAME; + + if (IS_SIGNIFICANT_DIFF(curr->epms, prev->epms)) + return (curr->epms < prev->epms) ? DIM_STATS_BETTER : + DIM_STATS_WORSE; + + return DIM_STATS_SAME; +} + +static bool net_dim_decision(struct dim_stats *curr_stats, struct dim *dim) +{ + int prev_state = dim->tune_state; + int prev_ix = dim->profile_ix; + int stats_res; + int step_res; + + switch (dim->tune_state) { + case DIM_PARKING_ON_TOP: + stats_res = net_dim_stats_compare(curr_stats, + &dim->prev_stats); + if (stats_res != DIM_STATS_SAME) + net_dim_exit_parking(dim); + break; + + case DIM_PARKING_TIRED: + dim->tired--; + if (!dim->tired) + net_dim_exit_parking(dim); + break; + + case DIM_GOING_RIGHT: + case DIM_GOING_LEFT: + stats_res = net_dim_stats_compare(curr_stats, + &dim->prev_stats); + if (stats_res != DIM_STATS_BETTER) + dim_turn(dim); + + if (dim_on_top(dim)) { + dim_park_on_top(dim); + break; + } + + step_res = net_dim_step(dim); + switch (step_res) { + case DIM_ON_EDGE: + dim_park_on_top(dim); + break; + case DIM_TOO_TIRED: + dim_park_tired(dim); + break; + } + + break; + } + + if (prev_state != DIM_PARKING_ON_TOP || + dim->tune_state != DIM_PARKING_ON_TOP) + dim->prev_stats = *curr_stats; + + return dim->profile_ix != prev_ix; +} + +void net_dim(struct dim *dim, struct dim_sample end_sample) +{ + struct dim_stats curr_stats; + u16 nevents; + + switch (dim->state) { + case DIM_MEASURE_IN_PROGRESS: + nevents = BIT_GAP(BITS_PER_TYPE(u16), + end_sample.event_ctr, + dim->start_sample.event_ctr); + if (nevents < DIM_NEVENTS) + break; + dim_calc_stats(&dim->start_sample, &end_sample, &curr_stats); + if (net_dim_decision(&curr_stats, dim)) { + dim->state = DIM_APPLY_NEW_PROFILE; + schedule_work(&dim->work); + break; + } + /* fall through */ + case DIM_START_MEASURE: + dim_update_sample(end_sample.event_ctr, end_sample.pkt_ctr, + end_sample.byte_ctr, &dim->start_sample); + dim->state = DIM_MEASURE_IN_PROGRESS; + break; + case DIM_APPLY_NEW_PROFILE: + break; + } +} +EXPORT_SYMBOL(net_dim); diff --git a/lib/dim/rdma_dim.c b/lib/dim/rdma_dim.c new file mode 100644 index 000000000000..f7e26c7b4749 --- /dev/null +++ b/lib/dim/rdma_dim.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * Copyright (c) 2019, Mellanox Technologies inc. All rights reserved. + */ + +#include <linux/dim.h> + +static int rdma_dim_step(struct dim *dim) +{ + if (dim->tune_state == DIM_GOING_RIGHT) { + if (dim->profile_ix == (RDMA_DIM_PARAMS_NUM_PROFILES - 1)) + return DIM_ON_EDGE; + dim->profile_ix++; + dim->steps_right++; + } + if (dim->tune_state == DIM_GOING_LEFT) { + if (dim->profile_ix == 0) + return DIM_ON_EDGE; + dim->profile_ix--; + dim->steps_left++; + } + + return DIM_STEPPED; +} + +static int rdma_dim_stats_compare(struct dim_stats *curr, + struct dim_stats *prev) +{ + /* first stat */ + if (!prev->cpms) + return DIM_STATS_SAME; + + if (IS_SIGNIFICANT_DIFF(curr->cpms, prev->cpms)) + return (curr->cpms > prev->cpms) ? DIM_STATS_BETTER : + DIM_STATS_WORSE; + + if (IS_SIGNIFICANT_DIFF(curr->cpe_ratio, prev->cpe_ratio)) + return (curr->cpe_ratio > prev->cpe_ratio) ? DIM_STATS_BETTER : + DIM_STATS_WORSE; + + return DIM_STATS_SAME; +} + +static bool rdma_dim_decision(struct dim_stats *curr_stats, struct dim *dim) +{ + int prev_ix = dim->profile_ix; + u8 state = dim->tune_state; + int stats_res; + int step_res; + + if (state != DIM_PARKING_ON_TOP && state != DIM_PARKING_TIRED) { + stats_res = rdma_dim_stats_compare(curr_stats, + &dim->prev_stats); + + switch (stats_res) { + case DIM_STATS_SAME: + if (curr_stats->cpe_ratio <= 50 * prev_ix) + dim->profile_ix = 0; + break; + case DIM_STATS_WORSE: + dim_turn(dim); + /* fall through */ + case DIM_STATS_BETTER: + step_res = rdma_dim_step(dim); + if (step_res == DIM_ON_EDGE) + dim_turn(dim); + break; + } + } + + dim->prev_stats = *curr_stats; + + return dim->profile_ix != prev_ix; +} + +void rdma_dim(struct dim *dim, u64 completions) +{ + struct dim_sample *curr_sample = &dim->measuring_sample; + struct dim_stats curr_stats; + u32 nevents; + + dim_update_sample_with_comps(curr_sample->event_ctr + 1, 0, 0, + curr_sample->comp_ctr + completions, + &dim->measuring_sample); + + switch (dim->state) { + case DIM_MEASURE_IN_PROGRESS: + nevents = curr_sample->event_ctr - dim->start_sample.event_ctr; + if (nevents < DIM_NEVENTS) + break; + dim_calc_stats(&dim->start_sample, curr_sample, &curr_stats); + if (rdma_dim_decision(&curr_stats, dim)) { + dim->state = DIM_APPLY_NEW_PROFILE; + schedule_work(&dim->work); + break; + } + /* fall through */ + case DIM_START_MEASURE: + dim->state = DIM_MEASURE_IN_PROGRESS; + dim_update_sample_with_comps(curr_sample->event_ctr, 0, 0, + curr_sample->comp_ctr, + &dim->start_sample); + break; + case DIM_APPLY_NEW_PROFILE: + break; + } +} +EXPORT_SYMBOL(rdma_dim); diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 8a16c2d498e9..c60409138e13 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -993,20 +993,14 @@ static __initdata int ddebug_init_success; static int __init dynamic_debug_init_debugfs(void) { - struct dentry *dir, *file; + struct dentry *dir; if (!ddebug_init_success) return -ENODEV; dir = debugfs_create_dir("dynamic_debug", NULL); - if (!dir) - return -ENOMEM; - file = debugfs_create_file("control", 0644, dir, NULL, - &ddebug_proc_fops); - if (!file) { - debugfs_remove(dir); - return -ENOMEM; - } + debugfs_create_file("control", 0644, dir, NULL, &ddebug_proc_fops); + return 0; } diff --git a/lib/fault-inject.c b/lib/fault-inject.c index 3cb21b2bf088..8186ca84910b 100644 --- a/lib/fault-inject.c +++ b/lib/fault-inject.c @@ -166,10 +166,10 @@ static int debugfs_ul_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(fops_ul, debugfs_ul_get, debugfs_ul_set, "%llu\n"); -static struct dentry *debugfs_create_ul(const char *name, umode_t mode, - struct dentry *parent, unsigned long *value) +static void debugfs_create_ul(const char *name, umode_t mode, + struct dentry *parent, unsigned long *value) { - return debugfs_create_file(name, mode, parent, value, &fops_ul); + debugfs_create_file(name, mode, parent, value, &fops_ul); } #ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER @@ -185,12 +185,11 @@ static int debugfs_stacktrace_depth_set(void *data, u64 val) DEFINE_SIMPLE_ATTRIBUTE(fops_stacktrace_depth, debugfs_ul_get, debugfs_stacktrace_depth_set, "%llu\n"); -static struct dentry *debugfs_create_stacktrace_depth( - const char *name, umode_t mode, - struct dentry *parent, unsigned long *value) +static void debugfs_create_stacktrace_depth(const char *name, umode_t mode, + struct dentry *parent, + unsigned long *value) { - return debugfs_create_file(name, mode, parent, value, - &fops_stacktrace_depth); + debugfs_create_file(name, mode, parent, value, &fops_stacktrace_depth); } #endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */ @@ -202,51 +201,31 @@ struct dentry *fault_create_debugfs_attr(const char *name, struct dentry *dir; dir = debugfs_create_dir(name, parent); - if (!dir) - return ERR_PTR(-ENOMEM); - - if (!debugfs_create_ul("probability", mode, dir, &attr->probability)) - goto fail; - if (!debugfs_create_ul("interval", mode, dir, &attr->interval)) - goto fail; - if (!debugfs_create_atomic_t("times", mode, dir, &attr->times)) - goto fail; - if (!debugfs_create_atomic_t("space", mode, dir, &attr->space)) - goto fail; - if (!debugfs_create_ul("verbose", mode, dir, &attr->verbose)) - goto fail; - if (!debugfs_create_u32("verbose_ratelimit_interval_ms", mode, dir, - &attr->ratelimit_state.interval)) - goto fail; - if (!debugfs_create_u32("verbose_ratelimit_burst", mode, dir, - &attr->ratelimit_state.burst)) - goto fail; - if (!debugfs_create_bool("task-filter", mode, dir, &attr->task_filter)) - goto fail; + if (IS_ERR(dir)) + return dir; + + debugfs_create_ul("probability", mode, dir, &attr->probability); + debugfs_create_ul("interval", mode, dir, &attr->interval); + debugfs_create_atomic_t("times", mode, dir, &attr->times); + debugfs_create_atomic_t("space", mode, dir, &attr->space); + debugfs_create_ul("verbose", mode, dir, &attr->verbose); + debugfs_create_u32("verbose_ratelimit_interval_ms", mode, dir, + &attr->ratelimit_state.interval); + debugfs_create_u32("verbose_ratelimit_burst", mode, dir, + &attr->ratelimit_state.burst); + debugfs_create_bool("task-filter", mode, dir, &attr->task_filter); #ifdef CONFIG_FAULT_INJECTION_STACKTRACE_FILTER - - if (!debugfs_create_stacktrace_depth("stacktrace-depth", mode, dir, - &attr->stacktrace_depth)) - goto fail; - if (!debugfs_create_ul("require-start", mode, dir, - &attr->require_start)) - goto fail; - if (!debugfs_create_ul("require-end", mode, dir, &attr->require_end)) - goto fail; - if (!debugfs_create_ul("reject-start", mode, dir, &attr->reject_start)) - goto fail; - if (!debugfs_create_ul("reject-end", mode, dir, &attr->reject_end)) - goto fail; - + debugfs_create_stacktrace_depth("stacktrace-depth", mode, dir, + &attr->stacktrace_depth); + debugfs_create_ul("require-start", mode, dir, &attr->require_start); + debugfs_create_ul("require-end", mode, dir, &attr->require_end); + debugfs_create_ul("reject-start", mode, dir, &attr->reject_start); + debugfs_create_ul("reject-end", mode, dir, &attr->reject_end); #endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */ attr->dname = dget(dir); return dir; -fail: - debugfs_remove_recursive(dir); - - return ERR_PTR(-ENOMEM); } EXPORT_SYMBOL_GPL(fault_create_debugfs_attr); diff --git a/lib/fonts/fonts.c b/lib/fonts/fonts.c index 9969358a7af5..e7258d8c252b 100644 --- a/lib/fonts/fonts.c +++ b/lib/fonts/fonts.c @@ -20,56 +20,42 @@ #endif #include <linux/font.h> -#define NO_FONTS - static const struct font_desc *fonts[] = { #ifdef CONFIG_FONT_8x8 -#undef NO_FONTS - &font_vga_8x8, + &font_vga_8x8, #endif #ifdef CONFIG_FONT_8x16 -#undef NO_FONTS - &font_vga_8x16, + &font_vga_8x16, #endif #ifdef CONFIG_FONT_6x11 -#undef NO_FONTS - &font_vga_6x11, + &font_vga_6x11, #endif #ifdef CONFIG_FONT_7x14 -#undef NO_FONTS - &font_7x14, + &font_7x14, #endif #ifdef CONFIG_FONT_SUN8x16 -#undef NO_FONTS - &font_sun_8x16, + &font_sun_8x16, #endif #ifdef CONFIG_FONT_SUN12x22 -#undef NO_FONTS - &font_sun_12x22, + &font_sun_12x22, #endif #ifdef CONFIG_FONT_10x18 -#undef NO_FONTS - &font_10x18, + &font_10x18, #endif #ifdef CONFIG_FONT_ACORN_8x8 -#undef NO_FONTS - &font_acorn_8x8, + &font_acorn_8x8, #endif #ifdef CONFIG_FONT_PEARL_8x8 -#undef NO_FONTS - &font_pearl_8x8, + &font_pearl_8x8, #endif #ifdef CONFIG_FONT_MINI_4x6 -#undef NO_FONTS - &font_mini_4x6, + &font_mini_4x6, #endif #ifdef CONFIG_FONT_6x10 -#undef NO_FONTS - &font_6x10, + &font_6x10, #endif #ifdef CONFIG_FONT_TER16x32 -#undef NO_FONTS - &font_ter_16x32, + &font_ter_16x32, #endif }; @@ -90,16 +76,17 @@ static const struct font_desc *fonts[] = { * specified font. * */ - const struct font_desc *find_font(const char *name) { - unsigned int i; + unsigned int i; - for (i = 0; i < num_fonts; i++) - if (!strcmp(fonts[i]->name, name)) - return fonts[i]; - return NULL; + BUILD_BUG_ON(!num_fonts); + for (i = 0; i < num_fonts; i++) + if (!strcmp(fonts[i]->name, name)) + return fonts[i]; + return NULL; } +EXPORT_SYMBOL(find_font); /** @@ -116,44 +103,46 @@ const struct font_desc *find_font(const char *name) * chosen font. * */ - const struct font_desc *get_default_font(int xres, int yres, u32 font_w, u32 font_h) { - int i, c, cc; - const struct font_desc *f, *g; - - g = NULL; - cc = -10000; - for(i=0; i<num_fonts; i++) { - f = fonts[i]; - c = f->pref; + int i, c, cc, res; + const struct font_desc *f, *g; + + g = NULL; + cc = -10000; + for (i = 0; i < num_fonts; i++) { + f = fonts[i]; + c = f->pref; #if defined(__mc68000__) #ifdef CONFIG_FONT_PEARL_8x8 - if (MACH_IS_AMIGA && f->idx == PEARL8x8_IDX) - c = 100; + if (MACH_IS_AMIGA && f->idx == PEARL8x8_IDX) + c = 100; #endif #ifdef CONFIG_FONT_6x11 - if (MACH_IS_MAC && xres < 640 && f->idx == VGA6x11_IDX) - c = 100; + if (MACH_IS_MAC && xres < 640 && f->idx == VGA6x11_IDX) + c = 100; #endif #endif - if ((yres < 400) == (f->height <= 8)) - c += 1000; + if ((yres < 400) == (f->height <= 8)) + c += 1000; + + /* prefer a bigger font for high resolution */ + res = (xres / f->width) * (yres / f->height) / 1000; + if (res > 20) + c += 20 - res; - if ((font_w & (1 << (f->width - 1))) && - (font_h & (1 << (f->height - 1)))) - c += 1000; + if ((font_w & (1 << (f->width - 1))) && + (font_h & (1 << (f->height - 1)))) + c += 1000; - if (c > cc) { - cc = c; - g = f; + if (c > cc) { + cc = c; + g = f; + } } - } - return g; + return g; } - -EXPORT_SYMBOL(find_font); EXPORT_SYMBOL(get_default_font); MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>"); diff --git a/lib/genalloc.c b/lib/genalloc.c index 5257f74fccf3..9fc31292cfa1 100644 --- a/lib/genalloc.c +++ b/lib/genalloc.c @@ -327,21 +327,45 @@ EXPORT_SYMBOL(gen_pool_alloc_algo_owner); * gen_pool_dma_alloc - allocate special memory from the pool for DMA usage * @pool: pool to allocate from * @size: number of bytes to allocate from the pool - * @dma: dma-view physical address return value. Use NULL if unneeded. + * @dma: dma-view physical address return value. Use %NULL if unneeded. * * Allocate the requested number of bytes from the specified pool. * Uses the pool allocation function (with first-fit algorithm by default). * Can not be used in NMI handler on architectures without * NMI-safe cmpxchg implementation. + * + * Return: virtual address of the allocated memory, or %NULL on failure */ void *gen_pool_dma_alloc(struct gen_pool *pool, size_t size, dma_addr_t *dma) { + return gen_pool_dma_alloc_algo(pool, size, dma, pool->algo, pool->data); +} +EXPORT_SYMBOL(gen_pool_dma_alloc); + +/** + * gen_pool_dma_alloc_algo - allocate special memory from the pool for DMA + * usage with the given pool algorithm + * @pool: pool to allocate from + * @size: number of bytes to allocate from the pool + * @dma: DMA-view physical address return value. Use %NULL if unneeded. + * @algo: algorithm passed from caller + * @data: data passed to algorithm + * + * Allocate the requested number of bytes from the specified pool. Uses the + * given pool allocation function. Can not be used in NMI handler on + * architectures without NMI-safe cmpxchg implementation. + * + * Return: virtual address of the allocated memory, or %NULL on failure + */ +void *gen_pool_dma_alloc_algo(struct gen_pool *pool, size_t size, + dma_addr_t *dma, genpool_algo_t algo, void *data) +{ unsigned long vaddr; if (!pool) return NULL; - vaddr = gen_pool_alloc(pool, size); + vaddr = gen_pool_alloc_algo(pool, size, algo, data); if (!vaddr) return NULL; @@ -350,7 +374,102 @@ void *gen_pool_dma_alloc(struct gen_pool *pool, size_t size, dma_addr_t *dma) return (void *)vaddr; } -EXPORT_SYMBOL(gen_pool_dma_alloc); +EXPORT_SYMBOL(gen_pool_dma_alloc_algo); + +/** + * gen_pool_dma_alloc_align - allocate special memory from the pool for DMA + * usage with the given alignment + * @pool: pool to allocate from + * @size: number of bytes to allocate from the pool + * @dma: DMA-view physical address return value. Use %NULL if unneeded. + * @align: alignment in bytes for starting address + * + * Allocate the requested number bytes from the specified pool, with the given + * alignment restriction. Can not be used in NMI handler on architectures + * without NMI-safe cmpxchg implementation. + * + * Return: virtual address of the allocated memory, or %NULL on failure + */ +void *gen_pool_dma_alloc_align(struct gen_pool *pool, size_t size, + dma_addr_t *dma, int align) +{ + struct genpool_data_align data = { .align = align }; + + return gen_pool_dma_alloc_algo(pool, size, dma, + gen_pool_first_fit_align, &data); +} +EXPORT_SYMBOL(gen_pool_dma_alloc_align); + +/** + * gen_pool_dma_zalloc - allocate special zeroed memory from the pool for + * DMA usage + * @pool: pool to allocate from + * @size: number of bytes to allocate from the pool + * @dma: dma-view physical address return value. Use %NULL if unneeded. + * + * Allocate the requested number of zeroed bytes from the specified pool. + * Uses the pool allocation function (with first-fit algorithm by default). + * Can not be used in NMI handler on architectures without + * NMI-safe cmpxchg implementation. + * + * Return: virtual address of the allocated zeroed memory, or %NULL on failure + */ +void *gen_pool_dma_zalloc(struct gen_pool *pool, size_t size, dma_addr_t *dma) +{ + return gen_pool_dma_zalloc_algo(pool, size, dma, pool->algo, pool->data); +} +EXPORT_SYMBOL(gen_pool_dma_zalloc); + +/** + * gen_pool_dma_zalloc_algo - allocate special zeroed memory from the pool for + * DMA usage with the given pool algorithm + * @pool: pool to allocate from + * @size: number of bytes to allocate from the pool + * @dma: DMA-view physical address return value. Use %NULL if unneeded. + * @algo: algorithm passed from caller + * @data: data passed to algorithm + * + * Allocate the requested number of zeroed bytes from the specified pool. Uses + * the given pool allocation function. Can not be used in NMI handler on + * architectures without NMI-safe cmpxchg implementation. + * + * Return: virtual address of the allocated zeroed memory, or %NULL on failure + */ +void *gen_pool_dma_zalloc_algo(struct gen_pool *pool, size_t size, + dma_addr_t *dma, genpool_algo_t algo, void *data) +{ + void *vaddr = gen_pool_dma_alloc_algo(pool, size, dma, algo, data); + + if (vaddr) + memset(vaddr, 0, size); + + return vaddr; +} +EXPORT_SYMBOL(gen_pool_dma_zalloc_algo); + +/** + * gen_pool_dma_zalloc_align - allocate special zeroed memory from the pool for + * DMA usage with the given alignment + * @pool: pool to allocate from + * @size: number of bytes to allocate from the pool + * @dma: DMA-view physical address return value. Use %NULL if unneeded. + * @align: alignment in bytes for starting address + * + * Allocate the requested number of zeroed bytes from the specified pool, + * with the given alignment restriction. Can not be used in NMI handler on + * architectures without NMI-safe cmpxchg implementation. + * + * Return: virtual address of the allocated zeroed memory, or %NULL on failure + */ +void *gen_pool_dma_zalloc_align(struct gen_pool *pool, size_t size, + dma_addr_t *dma, int align) +{ + struct genpool_data_align data = { .align = align }; + + return gen_pool_dma_zalloc_algo(pool, size, dma, + gen_pool_first_fit_align, &data); +} +EXPORT_SYMBOL(gen_pool_dma_zalloc_align); /** * gen_pool_free - free allocated special memory back to the pool diff --git a/lib/ioremap.c b/lib/ioremap.c index 063213685563..0a2ffadc6d71 100644 --- a/lib/ioremap.c +++ b/lib/ioremap.c @@ -30,6 +30,8 @@ early_param("nohugeiomap", set_nohugeiomap); void __init ioremap_huge_init(void) { if (!ioremap_huge_disabled) { + if (arch_ioremap_p4d_supported()) + ioremap_p4d_capable = 1; if (arch_ioremap_pud_supported()) ioremap_pud_capable = 1; if (arch_ioremap_pmd_supported()) @@ -86,6 +88,9 @@ static int ioremap_try_huge_pmd(pmd_t *pmd, unsigned long addr, if ((end - addr) != PMD_SIZE) return 0; + if (!IS_ALIGNED(addr, PMD_SIZE)) + return 0; + if (!IS_ALIGNED(phys_addr, PMD_SIZE)) return 0; @@ -126,6 +131,9 @@ static int ioremap_try_huge_pud(pud_t *pud, unsigned long addr, if ((end - addr) != PUD_SIZE) return 0; + if (!IS_ALIGNED(addr, PUD_SIZE)) + return 0; + if (!IS_ALIGNED(phys_addr, PUD_SIZE)) return 0; @@ -166,6 +174,9 @@ static int ioremap_try_huge_p4d(p4d_t *p4d, unsigned long addr, if ((end - addr) != P4D_SIZE) return 0; + if (!IS_ALIGNED(addr, P4D_SIZE)) + return 0; + if (!IS_ALIGNED(phys_addr, P4D_SIZE)) return 0; diff --git a/lib/iov_iter.c b/lib/iov_iter.c index f99c41d4eb54..f1e0569b4539 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -1634,9 +1634,9 @@ EXPORT_SYMBOL(dup_iter); * on-stack array was used or not (and regardless of whether this function * returns an error or not). * - * Return: 0 on success or negative error code on error. + * Return: Negative error code on error, bytes imported on success */ -int import_iovec(int type, const struct iovec __user * uvector, +ssize_t import_iovec(int type, const struct iovec __user * uvector, unsigned nr_segs, unsigned fast_segs, struct iovec **iov, struct iov_iter *i) { @@ -1652,16 +1652,17 @@ int import_iovec(int type, const struct iovec __user * uvector, } iov_iter_init(i, type, p, nr_segs, n); *iov = p == *iov ? NULL : p; - return 0; + return n; } EXPORT_SYMBOL(import_iovec); #ifdef CONFIG_COMPAT #include <linux/compat.h> -int compat_import_iovec(int type, const struct compat_iovec __user * uvector, - unsigned nr_segs, unsigned fast_segs, - struct iovec **iov, struct iov_iter *i) +ssize_t compat_import_iovec(int type, + const struct compat_iovec __user * uvector, + unsigned nr_segs, unsigned fast_segs, + struct iovec **iov, struct iov_iter *i) { ssize_t n; struct iovec *p; @@ -1675,7 +1676,7 @@ int compat_import_iovec(int type, const struct compat_iovec __user * uvector, } iov_iter_init(i, type, p, nr_segs, n); *iov = p == *iov ? NULL : p; - return 0; + return n; } #endif diff --git a/lib/jedec_ddr_data.c b/lib/jedec_ddr_data.c deleted file mode 100644 index d0b312e28d36..000000000000 --- a/lib/jedec_ddr_data.c +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * DDR addressing details and AC timing parameters from JEDEC specs - * - * Copyright (C) 2012 Texas Instruments, Inc. - * - * Aneesh V <aneesh@ti.com> - */ - -#include <memory/jedec_ddr.h> -#include <linux/module.h> - -/* LPDDR2 addressing details from JESD209-2 section 2.4 */ -const struct lpddr2_addressing - lpddr2_jedec_addressing_table[NUM_DDR_ADDR_TABLE_ENTRIES] = { - {B4, T_REFI_15_6, T_RFC_90}, /* 64M */ - {B4, T_REFI_15_6, T_RFC_90}, /* 128M */ - {B4, T_REFI_7_8, T_RFC_90}, /* 256M */ - {B4, T_REFI_7_8, T_RFC_90}, /* 512M */ - {B8, T_REFI_7_8, T_RFC_130}, /* 1GS4 */ - {B8, T_REFI_3_9, T_RFC_130}, /* 2GS4 */ - {B8, T_REFI_3_9, T_RFC_130}, /* 4G */ - {B8, T_REFI_3_9, T_RFC_210}, /* 8G */ - {B4, T_REFI_7_8, T_RFC_130}, /* 1GS2 */ - {B4, T_REFI_3_9, T_RFC_130}, /* 2GS2 */ -}; -EXPORT_SYMBOL_GPL(lpddr2_jedec_addressing_table); - -/* LPDDR2 AC timing parameters from JESD209-2 section 12 */ -const struct lpddr2_timings - lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES] = { - /* Speed bin 400(200 MHz) */ - [0] = { - .max_freq = 200000000, - .min_freq = 10000000, - .tRPab = 21000, - .tRCD = 18000, - .tWR = 15000, - .tRAS_min = 42000, - .tRRD = 10000, - .tWTR = 10000, - .tXP = 7500, - .tRTP = 7500, - .tCKESR = 15000, - .tDQSCK_max = 5500, - .tFAW = 50000, - .tZQCS = 90000, - .tZQCL = 360000, - .tZQinit = 1000000, - .tRAS_max_ns = 70000, - .tDQSCK_max_derated = 6000, - }, - /* Speed bin 533(266 MHz) */ - [1] = { - .max_freq = 266666666, - .min_freq = 10000000, - .tRPab = 21000, - .tRCD = 18000, - .tWR = 15000, - .tRAS_min = 42000, - .tRRD = 10000, - .tWTR = 7500, - .tXP = 7500, - .tRTP = 7500, - .tCKESR = 15000, - .tDQSCK_max = 5500, - .tFAW = 50000, - .tZQCS = 90000, - .tZQCL = 360000, - .tZQinit = 1000000, - .tRAS_max_ns = 70000, - .tDQSCK_max_derated = 6000, - }, - /* Speed bin 800(400 MHz) */ - [2] = { - .max_freq = 400000000, - .min_freq = 10000000, - .tRPab = 21000, - .tRCD = 18000, - .tWR = 15000, - .tRAS_min = 42000, - .tRRD = 10000, - .tWTR = 7500, - .tXP = 7500, - .tRTP = 7500, - .tCKESR = 15000, - .tDQSCK_max = 5500, - .tFAW = 50000, - .tZQCS = 90000, - .tZQCL = 360000, - .tZQinit = 1000000, - .tRAS_max_ns = 70000, - .tDQSCK_max_derated = 6000, - }, - /* Speed bin 1066(533 MHz) */ - [3] = { - .max_freq = 533333333, - .min_freq = 10000000, - .tRPab = 21000, - .tRCD = 18000, - .tWR = 15000, - .tRAS_min = 42000, - .tRRD = 10000, - .tWTR = 7500, - .tXP = 7500, - .tRTP = 7500, - .tCKESR = 15000, - .tDQSCK_max = 5500, - .tFAW = 50000, - .tZQCS = 90000, - .tZQCL = 360000, - .tZQinit = 1000000, - .tRAS_max_ns = 70000, - .tDQSCK_max_derated = 5620, - }, -}; -EXPORT_SYMBOL_GPL(lpddr2_jedec_timings); - -const struct lpddr2_min_tck lpddr2_jedec_min_tck = { - .tRPab = 3, - .tRCD = 3, - .tWR = 3, - .tRASmin = 3, - .tRRD = 2, - .tWTR = 2, - .tXP = 2, - .tRTP = 2, - .tCKE = 3, - .tCKESR = 3, - .tFAW = 8 -}; -EXPORT_SYMBOL_GPL(lpddr2_jedec_min_tck); diff --git a/lib/kobject.c b/lib/kobject.c index f2ccdbac8ed9..83198cb37d8d 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -498,8 +498,10 @@ int kobject_rename(struct kobject *kobj, const char *new_name) kobj = kobject_get(kobj); if (!kobj) return -EINVAL; - if (!kobj->parent) + if (!kobj->parent) { + kobject_put(kobj); return -EINVAL; + } devpath = kobject_get_path(kobj, GFP_KERNEL); if (!devpath) { diff --git a/lib/list_sort.c b/lib/list_sort.c index 712ed1f4eb64..52f0c258c895 100644 --- a/lib/list_sort.c +++ b/lib/list_sort.c @@ -157,9 +157,11 @@ static void merge_final(void *priv, cmp_func cmp, struct list_head *head, * * The number of pending lists of size 2^k is determined by the * state of bit k of "count" plus two extra pieces of information: + * * - The state of bit k-1 (when k == 0, consider bit -1 always set), and * - Whether the higher-order bits are zero or non-zero (i.e. * is count >= 2^(k+1)). + * * There are six states we distinguish. "x" represents some arbitrary * bits, and "y" represents some arbitrary non-zero bits: * 0: 00x: 0 pending of size 2^k; x pending of sizes < 2^k diff --git a/lib/mpi/longlong.h b/lib/mpi/longlong.h index 08c60d10747f..3bb6260d8f42 100644 --- a/lib/mpi/longlong.h +++ b/lib/mpi/longlong.h @@ -397,8 +397,8 @@ do { \ #define add_ssaaaa(sh, sl, ah, al, bh, bl) \ __asm__ ("addl %5,%1\n" \ "adcl %3,%0" \ - : "=r" ((USItype)(sh)), \ - "=&r" ((USItype)(sl)) \ + : "=r" (sh), \ + "=&r" (sl) \ : "%0" ((USItype)(ah)), \ "g" ((USItype)(bh)), \ "%1" ((USItype)(al)), \ @@ -406,22 +406,22 @@ do { \ #define sub_ddmmss(sh, sl, ah, al, bh, bl) \ __asm__ ("subl %5,%1\n" \ "sbbl %3,%0" \ - : "=r" ((USItype)(sh)), \ - "=&r" ((USItype)(sl)) \ + : "=r" (sh), \ + "=&r" (sl) \ : "0" ((USItype)(ah)), \ "g" ((USItype)(bh)), \ "1" ((USItype)(al)), \ "g" ((USItype)(bl))) #define umul_ppmm(w1, w0, u, v) \ __asm__ ("mull %3" \ - : "=a" ((USItype)(w0)), \ - "=d" ((USItype)(w1)) \ + : "=a" (w0), \ + "=d" (w1) \ : "%0" ((USItype)(u)), \ "rm" ((USItype)(v))) #define udiv_qrnnd(q, r, n1, n0, d) \ __asm__ ("divl %4" \ - : "=a" ((USItype)(q)), \ - "=d" ((USItype)(r)) \ + : "=a" (q), \ + "=d" (r) \ : "0" ((USItype)(n0)), \ "1" ((USItype)(n1)), \ "rm" ((USItype)(d))) diff --git a/lib/notifier-error-inject.c b/lib/notifier-error-inject.c index 3d2ba7cf83f4..21016b32d313 100644 --- a/lib/notifier-error-inject.c +++ b/lib/notifier-error-inject.c @@ -59,33 +59,22 @@ struct dentry *notifier_err_inject_init(const char *name, struct dentry *parent, err_inject->nb.priority = priority; dir = debugfs_create_dir(name, parent); - if (!dir) - return ERR_PTR(-ENOMEM); actions_dir = debugfs_create_dir("actions", dir); - if (!actions_dir) - goto fail; for (action = err_inject->actions; action->name; action++) { struct dentry *action_dir; action_dir = debugfs_create_dir(action->name, actions_dir); - if (!action_dir) - goto fail; /* * Create debugfs r/w file containing action->error. If * notifier call chain is called with action->val, it will * fail with the error code */ - if (!debugfs_create_errno("error", mode, action_dir, - &action->error)) - goto fail; + debugfs_create_errno("error", mode, action_dir, &action->error); } return dir; -fail: - debugfs_remove_recursive(dir); - return ERR_PTR(-ENOMEM); } EXPORT_SYMBOL_GPL(notifier_err_inject_init); diff --git a/lib/objagg.c b/lib/objagg.c index 576be22e86de..55621fb82e0a 100644 --- a/lib/objagg.c +++ b/lib/objagg.c @@ -605,12 +605,10 @@ const struct objagg_stats *objagg_stats_get(struct objagg *objagg) { struct objagg_stats *objagg_stats; struct objagg_obj *objagg_obj; - size_t alloc_size; int i; - alloc_size = sizeof(*objagg_stats) + - sizeof(objagg_stats->stats_info[0]) * objagg->obj_count; - objagg_stats = kzalloc(alloc_size, GFP_KERNEL); + objagg_stats = kzalloc(struct_size(objagg_stats, stats_info, + objagg->obj_count), GFP_KERNEL); if (!objagg_stats) return ERR_PTR(-ENOMEM); diff --git a/lib/percpu-refcount.c b/lib/percpu-refcount.c index 071a76c7bac0..4f6c6ebbbbde 100644 --- a/lib/percpu-refcount.c +++ b/lib/percpu-refcount.c @@ -70,11 +70,14 @@ int percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release, return -ENOMEM; ref->force_atomic = flags & PERCPU_REF_INIT_ATOMIC; + ref->allow_reinit = flags & PERCPU_REF_ALLOW_REINIT; - if (flags & (PERCPU_REF_INIT_ATOMIC | PERCPU_REF_INIT_DEAD)) + if (flags & (PERCPU_REF_INIT_ATOMIC | PERCPU_REF_INIT_DEAD)) { ref->percpu_count_ptr |= __PERCPU_REF_ATOMIC; - else + ref->allow_reinit = true; + } else { start_count += PERCPU_COUNT_BIAS; + } if (flags & PERCPU_REF_INIT_DEAD) ref->percpu_count_ptr |= __PERCPU_REF_DEAD; @@ -120,6 +123,9 @@ static void percpu_ref_call_confirm_rcu(struct rcu_head *rcu) ref->confirm_switch = NULL; wake_up_all(&percpu_ref_switch_waitq); + if (!ref->allow_reinit) + percpu_ref_exit(ref); + /* drop ref from percpu_ref_switch_to_atomic() */ percpu_ref_put(ref); } @@ -195,6 +201,9 @@ static void __percpu_ref_switch_to_percpu(struct percpu_ref *ref) if (!(ref->percpu_count_ptr & __PERCPU_REF_ATOMIC)) return; + if (WARN_ON_ONCE(!ref->allow_reinit)) + return; + atomic_long_add(PERCPU_COUNT_BIAS, &ref->count); /* diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile index e723eacf7868..42695bc8d451 100644 --- a/lib/raid6/Makefile +++ b/lib/raid6/Makefile @@ -12,9 +12,6 @@ raid6_pq-$(CONFIG_S390) += s390vx8.o recov_s390xc.o hostprogs-y += mktables -quiet_cmd_unroll = UNROLL $@ - cmd_unroll = $(AWK) -f$(srctree)/$(src)/unroll.awk -vN=$(UNROLL) < $< > $@ - ifeq ($(CONFIG_ALTIVEC),y) altivec_flags := -maltivec $(call cc-option,-mabi=altivec) @@ -26,7 +23,6 @@ CFLAGS_REMOVE_altivec1.o += -msoft-float CFLAGS_REMOVE_altivec2.o += -msoft-float CFLAGS_REMOVE_altivec4.o += -msoft-float CFLAGS_REMOVE_altivec8.o += -msoft-float -CFLAGS_REMOVE_altivec8.o += -msoft-float CFLAGS_REMOVE_vpermxor1.o += -msoft-float CFLAGS_REMOVE_vpermxor2.o += -msoft-float CFLAGS_REMOVE_vpermxor4.o += -msoft-float @@ -51,111 +47,39 @@ CFLAGS_REMOVE_neon8.o += -mgeneral-regs-only endif endif -targets += int1.c -$(obj)/int1.c: UNROLL := 1 -$(obj)/int1.c: $(src)/int.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - -targets += int2.c -$(obj)/int2.c: UNROLL := 2 -$(obj)/int2.c: $(src)/int.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - -targets += int4.c -$(obj)/int4.c: UNROLL := 4 -$(obj)/int4.c: $(src)/int.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - -targets += int8.c -$(obj)/int8.c: UNROLL := 8 -$(obj)/int8.c: $(src)/int.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - -targets += int16.c -$(obj)/int16.c: UNROLL := 16 -$(obj)/int16.c: $(src)/int.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) +quiet_cmd_unroll = UNROLL $@ + cmd_unroll = $(AWK) -f$(srctree)/$(src)/unroll.awk -vN=$* < $< > $@ -targets += int32.c -$(obj)/int32.c: UNROLL := 32 -$(obj)/int32.c: $(src)/int.uc $(src)/unroll.awk FORCE +targets += int1.c int2.c int4.c int8.c int16.c int32.c +$(obj)/int%.c: $(src)/int.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) CFLAGS_altivec1.o += $(altivec_flags) -targets += altivec1.c -$(obj)/altivec1.c: UNROLL := 1 -$(obj)/altivec1.c: $(src)/altivec.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - CFLAGS_altivec2.o += $(altivec_flags) -targets += altivec2.c -$(obj)/altivec2.c: UNROLL := 2 -$(obj)/altivec2.c: $(src)/altivec.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - CFLAGS_altivec4.o += $(altivec_flags) -targets += altivec4.c -$(obj)/altivec4.c: UNROLL := 4 -$(obj)/altivec4.c: $(src)/altivec.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - CFLAGS_altivec8.o += $(altivec_flags) -targets += altivec8.c -$(obj)/altivec8.c: UNROLL := 8 -$(obj)/altivec8.c: $(src)/altivec.uc $(src)/unroll.awk FORCE +targets += altivec1.c altivec2.c altivec4.c altivec8.c +$(obj)/altivec%.c: $(src)/altivec.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) CFLAGS_vpermxor1.o += $(altivec_flags) -targets += vpermxor1.c -$(obj)/vpermxor1.c: UNROLL := 1 -$(obj)/vpermxor1.c: $(src)/vpermxor.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - CFLAGS_vpermxor2.o += $(altivec_flags) -targets += vpermxor2.c -$(obj)/vpermxor2.c: UNROLL := 2 -$(obj)/vpermxor2.c: $(src)/vpermxor.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - CFLAGS_vpermxor4.o += $(altivec_flags) -targets += vpermxor4.c -$(obj)/vpermxor4.c: UNROLL := 4 -$(obj)/vpermxor4.c: $(src)/vpermxor.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - CFLAGS_vpermxor8.o += $(altivec_flags) -targets += vpermxor8.c -$(obj)/vpermxor8.c: UNROLL := 8 -$(obj)/vpermxor8.c: $(src)/vpermxor.uc $(src)/unroll.awk FORCE +targets += vpermxor1.o vpermxor2.o vpermxor4.o vpermxor8.o +$(obj)/vpermxor%.c: $(src)/vpermxor.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) CFLAGS_neon1.o += $(NEON_FLAGS) -targets += neon1.c -$(obj)/neon1.c: UNROLL := 1 -$(obj)/neon1.c: $(src)/neon.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - CFLAGS_neon2.o += $(NEON_FLAGS) -targets += neon2.c -$(obj)/neon2.c: UNROLL := 2 -$(obj)/neon2.c: $(src)/neon.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - CFLAGS_neon4.o += $(NEON_FLAGS) -targets += neon4.c -$(obj)/neon4.c: UNROLL := 4 -$(obj)/neon4.c: $(src)/neon.uc $(src)/unroll.awk FORCE - $(call if_changed,unroll) - CFLAGS_neon8.o += $(NEON_FLAGS) -targets += neon8.c -$(obj)/neon8.c: UNROLL := 8 -$(obj)/neon8.c: $(src)/neon.uc $(src)/unroll.awk FORCE +targets += neon1.c neon2.c neon4.c neon8.c +$(obj)/neon%.c: $(src)/neon.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) targets += s390vx8.c -$(obj)/s390vx8.c: UNROLL := 8 -$(obj)/s390vx8.c: $(src)/s390vx.uc $(src)/unroll.awk FORCE +$(obj)/s390vx%.c: $(src)/s390vx.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) quiet_cmd_mktable = TABLE $@ diff --git a/lib/rbtree.c b/lib/rbtree.c index 1ef6e25d031c..abc86c6a3177 100644 --- a/lib/rbtree.c +++ b/lib/rbtree.c @@ -83,14 +83,10 @@ __rb_rotate_set_parents(struct rb_node *old, struct rb_node *new, static __always_inline void __rb_insert(struct rb_node *node, struct rb_root *root, - bool newleft, struct rb_node **leftmost, void (*augment_rotate)(struct rb_node *old, struct rb_node *new)) { struct rb_node *parent = rb_red_parent(node), *gparent, *tmp; - if (newleft) - *leftmost = node; - while (true) { /* * Loop invariant: node is red. @@ -437,38 +433,19 @@ static const struct rb_augment_callbacks dummy_callbacks = { void rb_insert_color(struct rb_node *node, struct rb_root *root) { - __rb_insert(node, root, false, NULL, dummy_rotate); + __rb_insert(node, root, dummy_rotate); } EXPORT_SYMBOL(rb_insert_color); void rb_erase(struct rb_node *node, struct rb_root *root) { struct rb_node *rebalance; - rebalance = __rb_erase_augmented(node, root, - NULL, &dummy_callbacks); + rebalance = __rb_erase_augmented(node, root, &dummy_callbacks); if (rebalance) ____rb_erase_color(rebalance, root, dummy_rotate); } EXPORT_SYMBOL(rb_erase); -void rb_insert_color_cached(struct rb_node *node, - struct rb_root_cached *root, bool leftmost) -{ - __rb_insert(node, &root->rb_root, leftmost, - &root->rb_leftmost, dummy_rotate); -} -EXPORT_SYMBOL(rb_insert_color_cached); - -void rb_erase_cached(struct rb_node *node, struct rb_root_cached *root) -{ - struct rb_node *rebalance; - rebalance = __rb_erase_augmented(node, &root->rb_root, - &root->rb_leftmost, &dummy_callbacks); - if (rebalance) - ____rb_erase_color(rebalance, &root->rb_root, dummy_rotate); -} -EXPORT_SYMBOL(rb_erase_cached); - /* * Augmented rbtree manipulation functions. * @@ -477,10 +454,9 @@ EXPORT_SYMBOL(rb_erase_cached); */ void __rb_insert_augmented(struct rb_node *node, struct rb_root *root, - bool newleft, struct rb_node **leftmost, void (*augment_rotate)(struct rb_node *old, struct rb_node *new)) { - __rb_insert(node, root, newleft, leftmost, augment_rotate); + __rb_insert(node, root, augment_rotate); } EXPORT_SYMBOL(__rb_insert_augmented); @@ -591,16 +567,6 @@ void rb_replace_node(struct rb_node *victim, struct rb_node *new, } EXPORT_SYMBOL(rb_replace_node); -void rb_replace_node_cached(struct rb_node *victim, struct rb_node *new, - struct rb_root_cached *root) -{ - rb_replace_node(victim, new, &root->rb_root); - - if (root->rb_leftmost == victim) - root->rb_leftmost = new; -} -EXPORT_SYMBOL(rb_replace_node_cached); - void rb_replace_node_rcu(struct rb_node *victim, struct rb_node *new, struct rb_root *root) { diff --git a/lib/sbitmap.c b/lib/sbitmap.c index 54f57cd117c6..969e5400a615 100644 --- a/lib/sbitmap.c +++ b/lib/sbitmap.c @@ -26,9 +26,7 @@ static inline bool sbitmap_deferred_clear(struct sbitmap *sb, int index) /* * First get a stable cleared mask, setting the old mask to 0. */ - do { - mask = sb->map[index].cleared; - } while (cmpxchg(&sb->map[index].cleared, mask, 0) != mask); + mask = xchg(&sb->map[index].cleared, 0); /* * Now clear the masked bits in our free word @@ -516,10 +514,8 @@ static struct sbq_wait_state *sbq_wake_ptr(struct sbitmap_queue *sbq) struct sbq_wait_state *ws = &sbq->ws[wake_index]; if (waitqueue_active(&ws->wait)) { - int o = atomic_read(&sbq->wake_index); - - if (wake_index != o) - atomic_cmpxchg(&sbq->wake_index, o, wake_index); + if (wake_index != atomic_read(&sbq->wake_index)) + atomic_set(&sbq->wake_index, wake_index); return ws; } diff --git a/lib/scatterlist.c b/lib/scatterlist.c index 2882d9ba6607..c2cf2c311b7d 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -179,7 +179,8 @@ static void sg_kfree(struct scatterlist *sg, unsigned int nents) * __sg_free_table - Free a previously mapped sg table * @table: The sg table header to use * @max_ents: The maximum number of entries per single scatterlist - * @skip_first_chunk: don't free the (preallocated) first scatterlist chunk + * @nents_first_chunk: Number of entries int the (preallocated) first + * scatterlist chunk, 0 means no such preallocated first chunk * @free_fn: Free function * * Description: @@ -189,9 +190,10 @@ static void sg_kfree(struct scatterlist *sg, unsigned int nents) * **/ void __sg_free_table(struct sg_table *table, unsigned int max_ents, - bool skip_first_chunk, sg_free_fn *free_fn) + unsigned int nents_first_chunk, sg_free_fn *free_fn) { struct scatterlist *sgl, *next; + unsigned curr_max_ents = nents_first_chunk ?: max_ents; if (unlikely(!table->sgl)) return; @@ -207,9 +209,9 @@ void __sg_free_table(struct sg_table *table, unsigned int max_ents, * sg_size is then one less than alloc size, since the last * element is the chain pointer. */ - if (alloc_size > max_ents) { - next = sg_chain_ptr(&sgl[max_ents - 1]); - alloc_size = max_ents; + if (alloc_size > curr_max_ents) { + next = sg_chain_ptr(&sgl[curr_max_ents - 1]); + alloc_size = curr_max_ents; sg_size = alloc_size - 1; } else { sg_size = alloc_size; @@ -217,11 +219,12 @@ void __sg_free_table(struct sg_table *table, unsigned int max_ents, } table->orig_nents -= sg_size; - if (skip_first_chunk) - skip_first_chunk = false; + if (nents_first_chunk) + nents_first_chunk = 0; else free_fn(sgl, alloc_size); sgl = next; + curr_max_ents = max_ents; } table->sgl = NULL; @@ -244,6 +247,8 @@ EXPORT_SYMBOL(sg_free_table); * @table: The sg table header to use * @nents: Number of entries in sg list * @max_ents: The maximum number of entries the allocator returns per call + * @nents_first_chunk: Number of entries int the (preallocated) first + * scatterlist chunk, 0 means no such preallocated chunk provided by user * @gfp_mask: GFP allocation mask * @alloc_fn: Allocator to use * @@ -260,10 +265,13 @@ EXPORT_SYMBOL(sg_free_table); **/ int __sg_alloc_table(struct sg_table *table, unsigned int nents, unsigned int max_ents, struct scatterlist *first_chunk, - gfp_t gfp_mask, sg_alloc_fn *alloc_fn) + unsigned int nents_first_chunk, gfp_t gfp_mask, + sg_alloc_fn *alloc_fn) { struct scatterlist *sg, *prv; unsigned int left; + unsigned curr_max_ents = nents_first_chunk ?: max_ents; + unsigned prv_max_ents; memset(table, 0, sizeof(*table)); @@ -279,8 +287,8 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents, do { unsigned int sg_size, alloc_size = left; - if (alloc_size > max_ents) { - alloc_size = max_ents; + if (alloc_size > curr_max_ents) { + alloc_size = curr_max_ents; sg_size = alloc_size - 1; } else sg_size = alloc_size; @@ -314,7 +322,7 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents, * If this is not the first mapping, chain previous part. */ if (prv) - sg_chain(prv, max_ents, sg); + sg_chain(prv, prv_max_ents, sg); else table->sgl = sg; @@ -325,6 +333,8 @@ int __sg_alloc_table(struct sg_table *table, unsigned int nents, sg_mark_end(&sg[sg_size - 1]); prv = sg; + prv_max_ents = curr_max_ents; + curr_max_ents = max_ents; } while (left); return 0; @@ -347,9 +357,9 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask) int ret; ret = __sg_alloc_table(table, nents, SG_MAX_SINGLE_ALLOC, - NULL, gfp_mask, sg_kmalloc); + NULL, 0, gfp_mask, sg_kmalloc); if (unlikely(ret)) - __sg_free_table(table, SG_MAX_SINGLE_ALLOC, false, sg_kfree); + __sg_free_table(table, SG_MAX_SINGLE_ALLOC, 0, sg_kfree); return ret; } @@ -676,17 +686,18 @@ static bool sg_miter_get_next_page(struct sg_mapping_iter *miter) { if (!miter->__remaining) { struct scatterlist *sg; - unsigned long pgoffset; if (!__sg_page_iter_next(&miter->piter)) return false; sg = miter->piter.sg; - pgoffset = miter->piter.sg_pgoffset; - miter->__offset = pgoffset ? 0 : sg->offset; + miter->__offset = miter->piter.sg_pgoffset ? 0 : sg->offset; + miter->piter.sg_pgoffset += miter->__offset >> PAGE_SHIFT; + miter->__offset &= PAGE_SIZE - 1; miter->__remaining = sg->offset + sg->length - - (pgoffset << PAGE_SHIFT) - miter->__offset; + (miter->piter.sg_pgoffset << PAGE_SHIFT) - + miter->__offset; miter->__remaining = min_t(unsigned long, miter->__remaining, PAGE_SIZE - miter->__offset); } diff --git a/lib/sg_pool.c b/lib/sg_pool.c index cff20df2695e..db29e5c1f790 100644 --- a/lib/sg_pool.c +++ b/lib/sg_pool.c @@ -70,18 +70,27 @@ static struct scatterlist *sg_pool_alloc(unsigned int nents, gfp_t gfp_mask) /** * sg_free_table_chained - Free a previously mapped sg table * @table: The sg table header to use - * @first_chunk: was first_chunk not NULL in sg_alloc_table_chained? + * @nents_first_chunk: size of the first_chunk SGL passed to + * sg_alloc_table_chained * * Description: * Free an sg table previously allocated and setup with * sg_alloc_table_chained(). * + * @nents_first_chunk has to be same with that same parameter passed + * to sg_alloc_table_chained(). + * **/ -void sg_free_table_chained(struct sg_table *table, bool first_chunk) +void sg_free_table_chained(struct sg_table *table, + unsigned nents_first_chunk) { - if (first_chunk && table->orig_nents <= SG_CHUNK_SIZE) + if (table->orig_nents <= nents_first_chunk) return; - __sg_free_table(table, SG_CHUNK_SIZE, first_chunk, sg_pool_free); + + if (nents_first_chunk == 1) + nents_first_chunk = 0; + + __sg_free_table(table, SG_CHUNK_SIZE, nents_first_chunk, sg_pool_free); } EXPORT_SYMBOL_GPL(sg_free_table_chained); @@ -90,31 +99,41 @@ EXPORT_SYMBOL_GPL(sg_free_table_chained); * @table: The sg table header to use * @nents: Number of entries in sg list * @first_chunk: first SGL + * @nents_first_chunk: number of the SGL of @first_chunk * * Description: * Allocate and chain SGLs in an sg table. If @nents@ is larger than - * SG_CHUNK_SIZE a chained sg table will be setup. + * @nents_first_chunk a chained sg table will be setup. @first_chunk is + * ignored if nents_first_chunk <= 1 because user expects the SGL points + * non-chain SGL. * **/ int sg_alloc_table_chained(struct sg_table *table, int nents, - struct scatterlist *first_chunk) + struct scatterlist *first_chunk, unsigned nents_first_chunk) { int ret; BUG_ON(!nents); - if (first_chunk) { - if (nents <= SG_CHUNK_SIZE) { + if (first_chunk && nents_first_chunk) { + if (nents <= nents_first_chunk) { table->nents = table->orig_nents = nents; sg_init_table(table->sgl, nents); return 0; } } + /* User supposes that the 1st SGL includes real entry */ + if (nents_first_chunk <= 1) { + first_chunk = NULL; + nents_first_chunk = 0; + } + ret = __sg_alloc_table(table, nents, SG_CHUNK_SIZE, - first_chunk, GFP_ATOMIC, sg_pool_alloc); + first_chunk, nents_first_chunk, + GFP_ATOMIC, sg_pool_alloc); if (unlikely(ret)) - sg_free_table_chained(table, (bool)first_chunk); + sg_free_table_chained(table, nents_first_chunk); return ret; } EXPORT_SYMBOL_GPL(sg_alloc_table_chained); diff --git a/lib/string.c b/lib/string.c index 6016eb3ac73d..461fb620f85f 100644 --- a/lib/string.c +++ b/lib/string.c @@ -400,6 +400,9 @@ EXPORT_SYMBOL(strncmp); * strchr - Find the first occurrence of a character in a string * @s: The string to be searched * @c: The character to search for + * + * Note that the %NUL-terminator is considered part of the string, and can + * be searched for. */ char *strchr(const char *s, int c) { @@ -453,12 +456,18 @@ EXPORT_SYMBOL(strrchr); * @s: The string to be searched * @count: The number of characters to be searched * @c: The character to search for + * + * Note that the %NUL-terminator is considered part of the string, and can + * be searched for. */ char *strnchr(const char *s, size_t count, int c) { - for (; count-- && *s != '\0'; ++s) + while (count--) { if (*s == (char)c) return (char *)s; + if (*s++ == '\0') + break; + } return NULL; } EXPORT_SYMBOL(strnchr); diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 4403e1924f73..963050c0283e 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -231,35 +231,36 @@ static bool unescape_special(char **src, char **dst) * @src: source buffer (escaped) * @dst: destination buffer (unescaped) * @size: size of the destination buffer (0 to unlimit) - * @flags: combination of the flags (bitwise OR): - * %UNESCAPE_SPACE: + * @flags: combination of the flags. + * + * Description: + * The function unquotes characters in the given string. + * + * Because the size of the output will be the same as or less than the size of + * the input, the transformation may be performed in place. + * + * Caller must provide valid source and destination pointers. Be aware that + * destination buffer will always be NULL-terminated. Source string must be + * NULL-terminated as well. The supported flags are:: + * + * UNESCAPE_SPACE: * '\f' - form feed * '\n' - new line * '\r' - carriage return * '\t' - horizontal tab * '\v' - vertical tab - * %UNESCAPE_OCTAL: + * UNESCAPE_OCTAL: * '\NNN' - byte with octal value NNN (1 to 3 digits) - * %UNESCAPE_HEX: + * UNESCAPE_HEX: * '\xHH' - byte with hexadecimal value HH (1 to 2 digits) - * %UNESCAPE_SPECIAL: + * UNESCAPE_SPECIAL: * '\"' - double quote * '\\' - backslash * '\a' - alert (BEL) * '\e' - escape - * %UNESCAPE_ANY: + * UNESCAPE_ANY: * all previous together * - * Description: - * The function unquotes characters in the given string. - * - * Because the size of the output will be the same as or less than the size of - * the input, the transformation may be performed in place. - * - * Caller must provide valid source and destination pointers. Be aware that - * destination buffer will always be NULL-terminated. Source string must be - * NULL-terminated as well. - * * Return: * The amount of the characters processed to the destination buffer excluding * trailing '\0' is returned. @@ -441,7 +442,29 @@ static bool escape_hex(unsigned char c, char **dst, char *end) * @isz: source buffer size * @dst: destination buffer (escaped) * @osz: destination buffer size - * @flags: combination of the flags (bitwise OR): + * @flags: combination of the flags + * @only: NULL-terminated string containing characters used to limit + * the selected escape class. If characters are included in @only + * that would not normally be escaped by the classes selected + * in @flags, they will be copied to @dst unescaped. + * + * Description: + * The process of escaping byte buffer includes several parts. They are applied + * in the following sequence. + * + * 1. The character is matched to the printable class, if asked, and in + * case of match it passes through to the output. + * 2. The character is not matched to the one from @only string and thus + * must go as-is to the output. + * 3. The character is checked if it falls into the class given by @flags. + * %ESCAPE_OCTAL and %ESCAPE_HEX are going last since they cover any + * character. Note that they actually can't go together, otherwise + * %ESCAPE_HEX will be ignored. + * + * Caller must provide valid source and destination pointers. Be aware that + * destination buffer will not be NULL-terminated, thus caller have to append + * it if needs. The supported flags are:: + * * %ESCAPE_SPACE: (special white space, not space itself) * '\f' - form feed * '\n' - new line @@ -464,26 +487,6 @@ static bool escape_hex(unsigned char c, char **dst, char *end) * all previous together * %ESCAPE_HEX: * '\xHH' - byte with hexadecimal value HH (2 digits) - * @only: NULL-terminated string containing characters used to limit - * the selected escape class. If characters are included in @only - * that would not normally be escaped by the classes selected - * in @flags, they will be copied to @dst unescaped. - * - * Description: - * The process of escaping byte buffer includes several parts. They are applied - * in the following sequence. - * 1. The character is matched to the printable class, if asked, and in - * case of match it passes through to the output. - * 2. The character is not matched to the one from @only string and thus - * must go as-is to the output. - * 3. The character is checked if it falls into the class given by @flags. - * %ESCAPE_OCTAL and %ESCAPE_HEX are going last since they cover any - * character. Note that they actually can't go together, otherwise - * %ESCAPE_HEX will be ignored. - * - * Caller must provide valid source and destination pointers. Be aware that - * destination buffer will not be NULL-terminated, thus caller have to append - * it if needs. * * Return: * The total size of the escaped output that would be generated for @@ -540,6 +543,25 @@ int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz, } EXPORT_SYMBOL(string_escape_mem); +int string_escape_mem_ascii(const char *src, size_t isz, char *dst, + size_t osz) +{ + char *p = dst; + char *end = p + osz; + + while (isz--) { + unsigned char c = *src++; + + if (!isprint(c) || !isascii(c) || c == '"' || c == '\\') + escape_hex(c, &p, end); + else + escape_passthrough(c, &p, end); + } + + return p - dst; +} +EXPORT_SYMBOL(string_escape_mem_ascii); + /* * Return an allocated string that has been escaped of special characters * and double quotes, making it safe to log in quotes. diff --git a/lib/test_blackhole_dev.c b/lib/test_blackhole_dev.c new file mode 100644 index 000000000000..4c40580a99a3 --- /dev/null +++ b/lib/test_blackhole_dev.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This module tests the blackhole_dev that is created during the + * net subsystem initialization. The test this module performs is + * by injecting an skb into the stack with skb->dev as the + * blackhole_dev and expects kernel to behave in a sane manner + * (in other words, *not crash*)! + * + * Copyright (c) 2018, Mahesh Bandewar <maheshb@google.com> + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/printk.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/udp.h> +#include <linux/ipv6.h> + +#include <net/dst.h> + +#define SKB_SIZE 256 +#define HEAD_SIZE (14+40+8) /* Ether + IPv6 + UDP */ +#define TAIL_SIZE 32 /* random tail-room */ + +#define UDP_PORT 1234 + +static int __init test_blackholedev_init(void) +{ + struct ipv6hdr *ip6h; + struct sk_buff *skb; + struct ethhdr *ethh; + struct udphdr *uh; + int data_len; + int ret; + + skb = alloc_skb(SKB_SIZE, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + /* Reserve head-room for the headers */ + skb_reserve(skb, HEAD_SIZE); + + /* Add data to the skb */ + data_len = SKB_SIZE - (HEAD_SIZE + TAIL_SIZE); + memset(__skb_put(skb, data_len), 0xf, data_len); + + /* Add protocol data */ + /* (Transport) UDP */ + uh = (struct udphdr *)skb_push(skb, sizeof(struct udphdr)); + skb_set_transport_header(skb, 0); + uh->source = uh->dest = htons(UDP_PORT); + uh->len = htons(data_len); + uh->check = 0; + /* (Network) IPv6 */ + ip6h = (struct ipv6hdr *)skb_push(skb, sizeof(struct ipv6hdr)); + skb_set_network_header(skb, 0); + ip6h->hop_limit = 32; + ip6h->payload_len = data_len + sizeof(struct udphdr); + ip6h->nexthdr = IPPROTO_UDP; + ip6h->saddr = in6addr_loopback; + ip6h->daddr = in6addr_loopback; + /* Ether */ + ethh = (struct ethhdr *)skb_push(skb, sizeof(struct ethhdr)); + skb_set_mac_header(skb, 0); + + skb->protocol = htons(ETH_P_IPV6); + skb->pkt_type = PACKET_HOST; + skb->dev = blackhole_netdev; + + /* Now attempt to send the packet */ + ret = dev_queue_xmit(skb); + + switch (ret) { + case NET_XMIT_SUCCESS: + pr_warn("dev_queue_xmit() returned NET_XMIT_SUCCESS\n"); + break; + case NET_XMIT_DROP: + pr_warn("dev_queue_xmit() returned NET_XMIT_DROP\n"); + break; + case NET_XMIT_CN: + pr_warn("dev_queue_xmit() returned NET_XMIT_CN\n"); + break; + default: + pr_err("dev_queue_xmit() returned UNKNOWN(%d)\n", ret); + } + + return 0; +} + +static void __exit test_blackholedev_exit(void) +{ + pr_warn("test_blackholedev module terminating.\n"); +} + +module_init(test_blackholedev_init); +module_exit(test_blackholedev_exit); + +MODULE_AUTHOR("Mahesh Bandewar <maheshb@google.com>"); +MODULE_LICENSE("GPL"); diff --git a/lib/test_firmware.c b/lib/test_firmware.c index 83ea6c4e623c..6ca97a63b3d6 100644 --- a/lib/test_firmware.c +++ b/lib/test_firmware.c @@ -886,8 +886,11 @@ static int __init test_firmware_init(void) return -ENOMEM; rc = __test_firmware_config_init(); - if (rc) + if (rc) { + kfree(test_fw_config); + pr_err("could not init firmware test config: %d\n", rc); return rc; + } rc = misc_register(&test_fw_misc_device); if (rc) { diff --git a/lib/test_kasan.c b/lib/test_kasan.c index e3c593c38eff..b63b367a94e8 100644 --- a/lib/test_kasan.c +++ b/lib/test_kasan.c @@ -7,16 +7,17 @@ #define pr_fmt(fmt) "kasan test: %s " fmt, __func__ +#include <linux/bitops.h> #include <linux/delay.h> +#include <linux/kasan.h> #include <linux/kernel.h> -#include <linux/mman.h> #include <linux/mm.h> +#include <linux/mman.h> +#include <linux/module.h> #include <linux/printk.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/uaccess.h> -#include <linux/module.h> -#include <linux/kasan.h> /* * Note: test functions are marked noinline so that their names appear in @@ -619,6 +620,95 @@ static noinline void __init kasan_strings(void) strnlen(ptr, 1); } +static noinline void __init kasan_bitops(void) +{ + /* + * Allocate 1 more byte, which causes kzalloc to round up to 16-bytes; + * this way we do not actually corrupt other memory. + */ + long *bits = kzalloc(sizeof(*bits) + 1, GFP_KERNEL); + if (!bits) + return; + + /* + * Below calls try to access bit within allocated memory; however, the + * below accesses are still out-of-bounds, since bitops are defined to + * operate on the whole long the bit is in. + */ + pr_info("out-of-bounds in set_bit\n"); + set_bit(BITS_PER_LONG, bits); + + pr_info("out-of-bounds in __set_bit\n"); + __set_bit(BITS_PER_LONG, bits); + + pr_info("out-of-bounds in clear_bit\n"); + clear_bit(BITS_PER_LONG, bits); + + pr_info("out-of-bounds in __clear_bit\n"); + __clear_bit(BITS_PER_LONG, bits); + + pr_info("out-of-bounds in clear_bit_unlock\n"); + clear_bit_unlock(BITS_PER_LONG, bits); + + pr_info("out-of-bounds in __clear_bit_unlock\n"); + __clear_bit_unlock(BITS_PER_LONG, bits); + + pr_info("out-of-bounds in change_bit\n"); + change_bit(BITS_PER_LONG, bits); + + pr_info("out-of-bounds in __change_bit\n"); + __change_bit(BITS_PER_LONG, bits); + + /* + * Below calls try to access bit beyond allocated memory. + */ + pr_info("out-of-bounds in test_and_set_bit\n"); + test_and_set_bit(BITS_PER_LONG + BITS_PER_BYTE, bits); + + pr_info("out-of-bounds in __test_and_set_bit\n"); + __test_and_set_bit(BITS_PER_LONG + BITS_PER_BYTE, bits); + + pr_info("out-of-bounds in test_and_set_bit_lock\n"); + test_and_set_bit_lock(BITS_PER_LONG + BITS_PER_BYTE, bits); + + pr_info("out-of-bounds in test_and_clear_bit\n"); + test_and_clear_bit(BITS_PER_LONG + BITS_PER_BYTE, bits); + + pr_info("out-of-bounds in __test_and_clear_bit\n"); + __test_and_clear_bit(BITS_PER_LONG + BITS_PER_BYTE, bits); + + pr_info("out-of-bounds in test_and_change_bit\n"); + test_and_change_bit(BITS_PER_LONG + BITS_PER_BYTE, bits); + + pr_info("out-of-bounds in __test_and_change_bit\n"); + __test_and_change_bit(BITS_PER_LONG + BITS_PER_BYTE, bits); + + pr_info("out-of-bounds in test_bit\n"); + (void)test_bit(BITS_PER_LONG + BITS_PER_BYTE, bits); + +#if defined(clear_bit_unlock_is_negative_byte) + pr_info("out-of-bounds in clear_bit_unlock_is_negative_byte\n"); + clear_bit_unlock_is_negative_byte(BITS_PER_LONG + BITS_PER_BYTE, bits); +#endif + kfree(bits); +} + +static noinline void __init kmalloc_double_kzfree(void) +{ + char *ptr; + size_t size = 16; + + pr_info("double-free (kzfree)\n"); + ptr = kmalloc(size, GFP_KERNEL); + if (!ptr) { + pr_err("Allocation failed\n"); + return; + } + + kzfree(ptr); + kzfree(ptr); +} + static int __init kmalloc_tests_init(void) { /* @@ -660,6 +750,8 @@ static int __init kmalloc_tests_init(void) kasan_memchr(); kasan_memcmp(); kasan_strings(); + kasan_bitops(); + kmalloc_double_kzfree(); kasan_restore_multi_shot(multishot); diff --git a/lib/test_meminit.c b/lib/test_meminit.c new file mode 100644 index 000000000000..62d19f270cad --- /dev/null +++ b/lib/test_meminit.c @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test cases for SL[AOU]B/page initialization at alloc/free time. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/vmalloc.h> + +#define GARBAGE_INT (0x09A7BA9E) +#define GARBAGE_BYTE (0x9E) + +#define REPORT_FAILURES_IN_FN() \ + do { \ + if (failures) \ + pr_info("%s failed %d out of %d times\n", \ + __func__, failures, num_tests); \ + else \ + pr_info("all %d tests in %s passed\n", \ + num_tests, __func__); \ + } while (0) + +/* Calculate the number of uninitialized bytes in the buffer. */ +static int __init count_nonzero_bytes(void *ptr, size_t size) +{ + int i, ret = 0; + unsigned char *p = (unsigned char *)ptr; + + for (i = 0; i < size; i++) + if (p[i]) + ret++; + return ret; +} + +/* Fill a buffer with garbage, skipping |skip| first bytes. */ +static void __init fill_with_garbage_skip(void *ptr, int size, size_t skip) +{ + unsigned int *p = (unsigned int *)((char *)ptr + skip); + int i = 0; + + WARN_ON(skip > size); + size -= skip; + + while (size >= sizeof(*p)) { + p[i] = GARBAGE_INT; + i++; + size -= sizeof(*p); + } + if (size) + memset(&p[i], GARBAGE_BYTE, size); +} + +static void __init fill_with_garbage(void *ptr, size_t size) +{ + fill_with_garbage_skip(ptr, size, 0); +} + +static int __init do_alloc_pages_order(int order, int *total_failures) +{ + struct page *page; + void *buf; + size_t size = PAGE_SIZE << order; + + page = alloc_pages(GFP_KERNEL, order); + buf = page_address(page); + fill_with_garbage(buf, size); + __free_pages(page, order); + + page = alloc_pages(GFP_KERNEL, order); + buf = page_address(page); + if (count_nonzero_bytes(buf, size)) + (*total_failures)++; + fill_with_garbage(buf, size); + __free_pages(page, order); + return 1; +} + +/* Test the page allocator by calling alloc_pages with different orders. */ +static int __init test_pages(int *total_failures) +{ + int failures = 0, num_tests = 0; + int i; + + for (i = 0; i < 10; i++) + num_tests += do_alloc_pages_order(i, &failures); + + REPORT_FAILURES_IN_FN(); + *total_failures += failures; + return num_tests; +} + +/* Test kmalloc() with given parameters. */ +static int __init do_kmalloc_size(size_t size, int *total_failures) +{ + void *buf; + + buf = kmalloc(size, GFP_KERNEL); + fill_with_garbage(buf, size); + kfree(buf); + + buf = kmalloc(size, GFP_KERNEL); + if (count_nonzero_bytes(buf, size)) + (*total_failures)++; + fill_with_garbage(buf, size); + kfree(buf); + return 1; +} + +/* Test vmalloc() with given parameters. */ +static int __init do_vmalloc_size(size_t size, int *total_failures) +{ + void *buf; + + buf = vmalloc(size); + fill_with_garbage(buf, size); + vfree(buf); + + buf = vmalloc(size); + if (count_nonzero_bytes(buf, size)) + (*total_failures)++; + fill_with_garbage(buf, size); + vfree(buf); + return 1; +} + +/* Test kmalloc()/vmalloc() by allocating objects of different sizes. */ +static int __init test_kvmalloc(int *total_failures) +{ + int failures = 0, num_tests = 0; + int i, size; + + for (i = 0; i < 20; i++) { + size = 1 << i; + num_tests += do_kmalloc_size(size, &failures); + num_tests += do_vmalloc_size(size, &failures); + } + + REPORT_FAILURES_IN_FN(); + *total_failures += failures; + return num_tests; +} + +#define CTOR_BYTES (sizeof(unsigned int)) +#define CTOR_PATTERN (0x41414141) +/* Initialize the first 4 bytes of the object. */ +static void test_ctor(void *obj) +{ + *(unsigned int *)obj = CTOR_PATTERN; +} + +/* + * Check the invariants for the buffer allocated from a slab cache. + * If the cache has a test constructor, the first 4 bytes of the object must + * always remain equal to CTOR_PATTERN. + * If the cache isn't an RCU-typesafe one, or if the allocation is done with + * __GFP_ZERO, then the object contents must be zeroed after allocation. + * If the cache is an RCU-typesafe one, the object contents must never be + * zeroed after the first use. This is checked by memcmp() in + * do_kmem_cache_size(). + */ +static bool __init check_buf(void *buf, int size, bool want_ctor, + bool want_rcu, bool want_zero) +{ + int bytes; + bool fail = false; + + bytes = count_nonzero_bytes(buf, size); + WARN_ON(want_ctor && want_zero); + if (want_zero) + return bytes; + if (want_ctor) { + if (*(unsigned int *)buf != CTOR_PATTERN) + fail = 1; + } else { + if (bytes) + fail = !want_rcu; + } + return fail; +} + +/* + * Test kmem_cache with given parameters: + * want_ctor - use a constructor; + * want_rcu - use SLAB_TYPESAFE_BY_RCU; + * want_zero - use __GFP_ZERO. + */ +static int __init do_kmem_cache_size(size_t size, bool want_ctor, + bool want_rcu, bool want_zero, + int *total_failures) +{ + struct kmem_cache *c; + int iter; + bool fail = false; + gfp_t alloc_mask = GFP_KERNEL | (want_zero ? __GFP_ZERO : 0); + void *buf, *buf_copy; + + c = kmem_cache_create("test_cache", size, 1, + want_rcu ? SLAB_TYPESAFE_BY_RCU : 0, + want_ctor ? test_ctor : NULL); + for (iter = 0; iter < 10; iter++) { + buf = kmem_cache_alloc(c, alloc_mask); + /* Check that buf is zeroed, if it must be. */ + fail = check_buf(buf, size, want_ctor, want_rcu, want_zero); + fill_with_garbage_skip(buf, size, want_ctor ? CTOR_BYTES : 0); + + if (!want_rcu) { + kmem_cache_free(c, buf); + continue; + } + + /* + * If this is an RCU cache, use a critical section to ensure we + * can touch objects after they're freed. + */ + rcu_read_lock(); + /* + * Copy the buffer to check that it's not wiped on + * free(). + */ + buf_copy = kmalloc(size, GFP_KERNEL); + if (buf_copy) + memcpy(buf_copy, buf, size); + + kmem_cache_free(c, buf); + /* + * Check that |buf| is intact after kmem_cache_free(). + * |want_zero| is false, because we wrote garbage to + * the buffer already. + */ + fail |= check_buf(buf, size, want_ctor, want_rcu, + false); + if (buf_copy) { + fail |= (bool)memcmp(buf, buf_copy, size); + kfree(buf_copy); + } + rcu_read_unlock(); + } + kmem_cache_destroy(c); + + *total_failures += fail; + return 1; +} + +/* + * Check that the data written to an RCU-allocated object survives + * reallocation. + */ +static int __init do_kmem_cache_rcu_persistent(int size, int *total_failures) +{ + struct kmem_cache *c; + void *buf, *buf_contents, *saved_ptr; + void **used_objects; + int i, iter, maxiter = 1024; + bool fail = false; + + c = kmem_cache_create("test_cache", size, size, SLAB_TYPESAFE_BY_RCU, + NULL); + buf = kmem_cache_alloc(c, GFP_KERNEL); + saved_ptr = buf; + fill_with_garbage(buf, size); + buf_contents = kmalloc(size, GFP_KERNEL); + if (!buf_contents) + goto out; + used_objects = kmalloc_array(maxiter, sizeof(void *), GFP_KERNEL); + if (!used_objects) { + kfree(buf_contents); + goto out; + } + memcpy(buf_contents, buf, size); + kmem_cache_free(c, buf); + /* + * Run for a fixed number of iterations. If we never hit saved_ptr, + * assume the test passes. + */ + for (iter = 0; iter < maxiter; iter++) { + buf = kmem_cache_alloc(c, GFP_KERNEL); + used_objects[iter] = buf; + if (buf == saved_ptr) { + fail = memcmp(buf_contents, buf, size); + for (i = 0; i <= iter; i++) + kmem_cache_free(c, used_objects[i]); + goto free_out; + } + } + +free_out: + kmem_cache_destroy(c); + kfree(buf_contents); + kfree(used_objects); +out: + *total_failures += fail; + return 1; +} + +/* + * Test kmem_cache allocation by creating caches of different sizes, with and + * without constructors, with and without SLAB_TYPESAFE_BY_RCU. + */ +static int __init test_kmemcache(int *total_failures) +{ + int failures = 0, num_tests = 0; + int i, flags, size; + bool ctor, rcu, zero; + + for (i = 0; i < 10; i++) { + size = 8 << i; + for (flags = 0; flags < 8; flags++) { + ctor = flags & 1; + rcu = flags & 2; + zero = flags & 4; + if (ctor & zero) + continue; + num_tests += do_kmem_cache_size(size, ctor, rcu, zero, + &failures); + } + } + REPORT_FAILURES_IN_FN(); + *total_failures += failures; + return num_tests; +} + +/* Test the behavior of SLAB_TYPESAFE_BY_RCU caches of different sizes. */ +static int __init test_rcu_persistent(int *total_failures) +{ + int failures = 0, num_tests = 0; + int i, size; + + for (i = 0; i < 10; i++) { + size = 8 << i; + num_tests += do_kmem_cache_rcu_persistent(size, &failures); + } + REPORT_FAILURES_IN_FN(); + *total_failures += failures; + return num_tests; +} + +/* + * Run the tests. Each test function returns the number of executed tests and + * updates |failures| with the number of failed tests. + */ +static int __init test_meminit_init(void) +{ + int failures = 0, num_tests = 0; + + num_tests += test_pages(&failures); + num_tests += test_kvmalloc(&failures); + num_tests += test_kmemcache(&failures); + num_tests += test_rcu_persistent(&failures); + + if (failures == 0) + pr_info("all %d tests passed!\n", num_tests); + else + pr_info("failures: %d out of %d\n", failures, num_tests); + + return failures ? -EINVAL : 0; +} +module_init(test_meminit_init); + +MODULE_LICENSE("GPL"); diff --git a/lib/test_overflow.c b/lib/test_overflow.c index fc680562d8b6..7a4b6f6c5473 100644 --- a/lib/test_overflow.c +++ b/lib/test_overflow.c @@ -486,16 +486,17 @@ static int __init test_overflow_shift(void) * Deal with the various forms of allocator arguments. See comments above * the DEFINE_TEST_ALLOC() instances for mapping of the "bits". */ -#define alloc010(alloc, arg, sz) alloc(sz, GFP_KERNEL) -#define alloc011(alloc, arg, sz) alloc(sz, GFP_KERNEL, NUMA_NO_NODE) +#define alloc_GFP (GFP_KERNEL | __GFP_NOWARN) +#define alloc010(alloc, arg, sz) alloc(sz, alloc_GFP) +#define alloc011(alloc, arg, sz) alloc(sz, alloc_GFP, NUMA_NO_NODE) #define alloc000(alloc, arg, sz) alloc(sz) #define alloc001(alloc, arg, sz) alloc(sz, NUMA_NO_NODE) -#define alloc110(alloc, arg, sz) alloc(arg, sz, GFP_KERNEL) +#define alloc110(alloc, arg, sz) alloc(arg, sz, alloc_GFP) #define free0(free, arg, ptr) free(ptr) #define free1(free, arg, ptr) free(arg, ptr) -/* Wrap around to 8K */ -#define TEST_SIZE (9 << PAGE_SHIFT) +/* Wrap around to 16K */ +#define TEST_SIZE (5 * 4096) #define DEFINE_TEST_ALLOC(func, free_func, want_arg, want_gfp, want_node)\ static int __init test_ ## func (void *arg) \ diff --git a/lib/test_string.c b/lib/test_string.c index bf8def01ed20..7b31f4a505bf 100644 --- a/lib/test_string.c +++ b/lib/test_string.c @@ -36,7 +36,7 @@ static __init int memset16_selftest(void) fail: kfree(p); if (i < 256) - return (i << 24) | (j << 16) | k; + return (i << 24) | (j << 16) | k | 0x8000; return 0; } @@ -72,7 +72,7 @@ static __init int memset32_selftest(void) fail: kfree(p); if (i < 256) - return (i << 24) | (j << 16) | k; + return (i << 24) | (j << 16) | k | 0x8000; return 0; } @@ -108,7 +108,74 @@ static __init int memset64_selftest(void) fail: kfree(p); if (i < 256) - return (i << 24) | (j << 16) | k; + return (i << 24) | (j << 16) | k | 0x8000; + return 0; +} + +static __init int strchr_selftest(void) +{ + const char *test_string = "abcdefghijkl"; + const char *empty_string = ""; + char *result; + int i; + + for (i = 0; i < strlen(test_string) + 1; i++) { + result = strchr(test_string, test_string[i]); + if (result - test_string != i) + return i + 'a'; + } + + result = strchr(empty_string, '\0'); + if (result != empty_string) + return 0x101; + + result = strchr(empty_string, 'a'); + if (result) + return 0x102; + + result = strchr(test_string, 'z'); + if (result) + return 0x103; + + return 0; +} + +static __init int strnchr_selftest(void) +{ + const char *test_string = "abcdefghijkl"; + const char *empty_string = ""; + char *result; + int i, j; + + for (i = 0; i < strlen(test_string) + 1; i++) { + for (j = 0; j < strlen(test_string) + 2; j++) { + result = strnchr(test_string, j, test_string[i]); + if (j <= i) { + if (!result) + continue; + return ((i + 'a') << 8) | j; + } + if (result - test_string != i) + return ((i + 'a') << 8) | j; + } + } + + result = strnchr(empty_string, 0, '\0'); + if (result) + return 0x10001; + + result = strnchr(empty_string, 1, '\0'); + if (result != empty_string) + return 0x10002; + + result = strnchr(empty_string, 1, 'a'); + if (result) + return 0x10003; + + result = strnchr(NULL, 0, '\0'); + if (result) + return 0x10004; + return 0; } @@ -131,6 +198,16 @@ static __init int string_selftest_init(void) if (subtest) goto fail; + test = 4; + subtest = strchr_selftest(); + if (subtest) + goto fail; + + test = 5; + subtest = strnchr_selftest(); + if (subtest) + goto fail; + pr_info("String selftests succeeded\n"); return 0; fail: diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 63937044c57d..b0967cf17137 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -599,7 +599,7 @@ static char *string_nocheck(char *buf, char *end, const char *s, struct printf_spec spec) { int len = 0; - size_t lim = spec.precision; + int lim = spec.precision; while (lim--) { char c = *s++; @@ -1799,7 +1799,7 @@ char *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec, #ifdef CONFIG_COMMON_CLK return string(buf, end, __clk_get_name(clk), spec); #else - return error_string(buf, end, "(%pC?)", spec); + return ptr_to_id(buf, end, clk, spec); #endif } } |