Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
cdc-wdm: s/kill_urbs/poison_urbs/ to fix build Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
# BPF interpreter that, for example, classic socket filters depend on.
|
||||
config BPF
|
||||
bool
|
||||
|
||||
# Used by archs to tell that they support BPF JIT compiler plus which
|
||||
# flavour. Only one of the two can be selected for a specific arch since
|
||||
# eBPF JIT supersedes the cBPF JIT.
|
||||
|
||||
# Classic BPF JIT (cBPF)
|
||||
config HAVE_CBPF_JIT
|
||||
bool
|
||||
|
||||
# Extended BPF JIT (eBPF)
|
||||
config HAVE_EBPF_JIT
|
||||
bool
|
||||
|
||||
# Used by archs to tell that they want the BPF JIT compiler enabled by
|
||||
# default for kernels that were compiled with BPF JIT support.
|
||||
config ARCH_WANT_DEFAULT_BPF_JIT
|
||||
bool
|
||||
|
||||
menu "BPF subsystem"
|
||||
|
||||
config BPF_SYSCALL
|
||||
bool "Enable bpf() system call"
|
||||
select BPF
|
||||
select IRQ_WORK
|
||||
select TASKS_TRACE_RCU
|
||||
select BINARY_PRINTF
|
||||
select NET_SOCK_MSG if INET
|
||||
default n
|
||||
help
|
||||
Enable the bpf() system call that allows to manipulate BPF programs
|
||||
and maps via file descriptors.
|
||||
|
||||
config BPF_JIT
|
||||
bool "Enable BPF Just In Time compiler"
|
||||
depends on BPF
|
||||
depends on HAVE_CBPF_JIT || HAVE_EBPF_JIT
|
||||
depends on MODULES
|
||||
help
|
||||
BPF programs are normally handled by a BPF interpreter. This option
|
||||
allows the kernel to generate native code when a program is loaded
|
||||
into the kernel. This will significantly speed-up processing of BPF
|
||||
programs.
|
||||
|
||||
Note, an admin should enable this feature changing:
|
||||
/proc/sys/net/core/bpf_jit_enable
|
||||
/proc/sys/net/core/bpf_jit_harden (optional)
|
||||
/proc/sys/net/core/bpf_jit_kallsyms (optional)
|
||||
|
||||
config BPF_JIT_ALWAYS_ON
|
||||
bool "Permanently enable BPF JIT and remove BPF interpreter"
|
||||
depends on BPF_SYSCALL && HAVE_EBPF_JIT && BPF_JIT
|
||||
help
|
||||
Enables BPF JIT and removes BPF interpreter to avoid speculative
|
||||
execution of BPF instructions by the interpreter.
|
||||
|
||||
config BPF_JIT_DEFAULT_ON
|
||||
def_bool ARCH_WANT_DEFAULT_BPF_JIT || BPF_JIT_ALWAYS_ON
|
||||
depends on HAVE_EBPF_JIT && BPF_JIT
|
||||
|
||||
config BPF_UNPRIV_DEFAULT_OFF
|
||||
bool "Disable unprivileged BPF by default"
|
||||
depends on BPF_SYSCALL
|
||||
help
|
||||
Disables unprivileged BPF by default by setting the corresponding
|
||||
/proc/sys/kernel/unprivileged_bpf_disabled knob to 2. An admin can
|
||||
still reenable it by setting it to 0 later on, or permanently
|
||||
disable it by setting it to 1 (from which no other transition to
|
||||
0 is possible anymore).
|
||||
|
||||
source "kernel/bpf/preload/Kconfig"
|
||||
|
||||
config BPF_LSM
|
||||
bool "Enable BPF LSM Instrumentation"
|
||||
depends on BPF_EVENTS
|
||||
depends on BPF_SYSCALL
|
||||
depends on SECURITY
|
||||
depends on BPF_JIT
|
||||
help
|
||||
Enables instrumentation of the security hooks with BPF programs for
|
||||
implementing dynamic MAC and Audit Policies.
|
||||
|
||||
If you are unsure how to answer this question, answer N.
|
||||
|
||||
endmenu # "BPF subsystem"
|
||||
@@ -107,10 +107,12 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
return &bpf_inode_storage_get_proto;
|
||||
case BPF_FUNC_inode_storage_delete:
|
||||
return &bpf_inode_storage_delete_proto;
|
||||
#ifdef CONFIG_NET
|
||||
case BPF_FUNC_sk_storage_get:
|
||||
return &bpf_sk_storage_get_proto;
|
||||
case BPF_FUNC_sk_storage_delete:
|
||||
return &bpf_sk_storage_delete_proto;
|
||||
#endif /* CONFIG_NET */
|
||||
case BPF_FUNC_spin_lock:
|
||||
return &bpf_spin_lock_proto;
|
||||
case BPF_FUNC_spin_unlock:
|
||||
|
||||
@@ -5206,6 +5206,12 @@ int btf_distill_func_proto(struct bpf_verifier_log *log,
|
||||
m->ret_size = ret;
|
||||
|
||||
for (i = 0; i < nargs; i++) {
|
||||
if (i == nargs - 1 && args[i].type == 0) {
|
||||
bpf_log(log,
|
||||
"The function %s with variable args is unsupported.\n",
|
||||
tname);
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = __get_type_size(btf, args[i].type, &t);
|
||||
if (ret < 0) {
|
||||
bpf_log(log,
|
||||
@@ -5213,6 +5219,12 @@ int btf_distill_func_proto(struct bpf_verifier_log *log,
|
||||
tname, i, btf_kind_str[BTF_INFO_KIND(t->info)]);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (ret == 0) {
|
||||
bpf_log(log,
|
||||
"The function %s has malformed void argument.\n",
|
||||
tname);
|
||||
return -EINVAL;
|
||||
}
|
||||
m->arg_size[i] = ret;
|
||||
}
|
||||
m->nr_args = nargs;
|
||||
|
||||
+19
-16
@@ -692,38 +692,41 @@ static int bpf_trace_copy_string(char *buf, void *unsafe_ptr, char fmt_ptype,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Per-cpu temp buffers which can be used by printf-like helpers for %s or %p
|
||||
/* Per-cpu temp buffers used by printf-like helpers to store the bprintf binary
|
||||
* arguments representation.
|
||||
*/
|
||||
#define MAX_PRINTF_BUF_LEN 512
|
||||
#define MAX_BPRINTF_BUF_LEN 512
|
||||
|
||||
struct bpf_printf_buf {
|
||||
char tmp_buf[MAX_PRINTF_BUF_LEN];
|
||||
/* Support executing three nested bprintf helper calls on a given CPU */
|
||||
#define MAX_BPRINTF_NEST_LEVEL 3
|
||||
struct bpf_bprintf_buffers {
|
||||
char tmp_bufs[MAX_BPRINTF_NEST_LEVEL][MAX_BPRINTF_BUF_LEN];
|
||||
};
|
||||
static DEFINE_PER_CPU(struct bpf_printf_buf, bpf_printf_buf);
|
||||
static DEFINE_PER_CPU(int, bpf_printf_buf_used);
|
||||
static DEFINE_PER_CPU(struct bpf_bprintf_buffers, bpf_bprintf_bufs);
|
||||
static DEFINE_PER_CPU(int, bpf_bprintf_nest_level);
|
||||
|
||||
static int try_get_fmt_tmp_buf(char **tmp_buf)
|
||||
{
|
||||
struct bpf_printf_buf *bufs;
|
||||
int used;
|
||||
struct bpf_bprintf_buffers *bufs;
|
||||
int nest_level;
|
||||
|
||||
preempt_disable();
|
||||
used = this_cpu_inc_return(bpf_printf_buf_used);
|
||||
if (WARN_ON_ONCE(used > 1)) {
|
||||
this_cpu_dec(bpf_printf_buf_used);
|
||||
nest_level = this_cpu_inc_return(bpf_bprintf_nest_level);
|
||||
if (WARN_ON_ONCE(nest_level > MAX_BPRINTF_NEST_LEVEL)) {
|
||||
this_cpu_dec(bpf_bprintf_nest_level);
|
||||
preempt_enable();
|
||||
return -EBUSY;
|
||||
}
|
||||
bufs = this_cpu_ptr(&bpf_printf_buf);
|
||||
*tmp_buf = bufs->tmp_buf;
|
||||
bufs = this_cpu_ptr(&bpf_bprintf_bufs);
|
||||
*tmp_buf = bufs->tmp_bufs[nest_level - 1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bpf_bprintf_cleanup(void)
|
||||
{
|
||||
if (this_cpu_read(bpf_printf_buf_used)) {
|
||||
this_cpu_dec(bpf_printf_buf_used);
|
||||
if (this_cpu_read(bpf_bprintf_nest_level)) {
|
||||
this_cpu_dec(bpf_bprintf_nest_level);
|
||||
preempt_enable();
|
||||
}
|
||||
}
|
||||
@@ -760,7 +763,7 @@ int bpf_bprintf_prepare(char *fmt, u32 fmt_size, const u64 *raw_args,
|
||||
if (num_args && try_get_fmt_tmp_buf(&tmp_buf))
|
||||
return -EBUSY;
|
||||
|
||||
tmp_buf_end = tmp_buf + MAX_PRINTF_BUF_LEN;
|
||||
tmp_buf_end = tmp_buf + MAX_BPRINTF_BUF_LEN;
|
||||
*bin_args = (u32 *)tmp_buf;
|
||||
}
|
||||
|
||||
|
||||
+11
-13
@@ -221,25 +221,20 @@ static int ringbuf_map_get_next_key(struct bpf_map *map, void *key,
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static size_t bpf_ringbuf_mmap_page_cnt(const struct bpf_ringbuf *rb)
|
||||
{
|
||||
size_t data_pages = (rb->mask + 1) >> PAGE_SHIFT;
|
||||
|
||||
/* consumer page + producer page + 2 x data pages */
|
||||
return RINGBUF_POS_PAGES + 2 * data_pages;
|
||||
}
|
||||
|
||||
static int ringbuf_map_mmap(struct bpf_map *map, struct vm_area_struct *vma)
|
||||
{
|
||||
struct bpf_ringbuf_map *rb_map;
|
||||
size_t mmap_sz;
|
||||
|
||||
rb_map = container_of(map, struct bpf_ringbuf_map, map);
|
||||
mmap_sz = bpf_ringbuf_mmap_page_cnt(rb_map->rb) << PAGE_SHIFT;
|
||||
|
||||
if (vma->vm_pgoff * PAGE_SIZE + (vma->vm_end - vma->vm_start) > mmap_sz)
|
||||
return -EINVAL;
|
||||
|
||||
if (vma->vm_flags & VM_WRITE) {
|
||||
/* allow writable mapping for the consumer_pos only */
|
||||
if (vma->vm_pgoff != 0 || vma->vm_end - vma->vm_start != PAGE_SIZE)
|
||||
return -EPERM;
|
||||
} else {
|
||||
vma->vm_flags &= ~VM_MAYWRITE;
|
||||
}
|
||||
/* remap_vmalloc_range() checks size and offset constraints */
|
||||
return remap_vmalloc_range(vma, rb_map->rb,
|
||||
vma->vm_pgoff + RINGBUF_PGOFF);
|
||||
}
|
||||
@@ -315,6 +310,9 @@ static void *__bpf_ringbuf_reserve(struct bpf_ringbuf *rb, u64 size)
|
||||
return NULL;
|
||||
|
||||
len = round_up(size + BPF_RINGBUF_HDR_SZ, 8);
|
||||
if (len > rb->mask + 1)
|
||||
return NULL;
|
||||
|
||||
cons_pos = smp_load_acquire(&rb->consumer_pos);
|
||||
|
||||
if (in_nmi()) {
|
||||
|
||||
@@ -50,7 +50,8 @@ static DEFINE_SPINLOCK(map_idr_lock);
|
||||
static DEFINE_IDR(link_idr);
|
||||
static DEFINE_SPINLOCK(link_idr_lock);
|
||||
|
||||
int sysctl_unprivileged_bpf_disabled __read_mostly;
|
||||
int sysctl_unprivileged_bpf_disabled __read_mostly =
|
||||
IS_BUILTIN(CONFIG_BPF_UNPRIV_DEFAULT_OFF) ? 2 : 0;
|
||||
|
||||
static const struct bpf_map_ops * const bpf_map_types[] = {
|
||||
#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type)
|
||||
|
||||
+57
-37
@@ -6411,18 +6411,10 @@ enum {
|
||||
};
|
||||
|
||||
static int retrieve_ptr_limit(const struct bpf_reg_state *ptr_reg,
|
||||
const struct bpf_reg_state *off_reg,
|
||||
u32 *alu_limit, u8 opcode)
|
||||
u32 *alu_limit, bool mask_to_left)
|
||||
{
|
||||
bool off_is_neg = off_reg->smin_value < 0;
|
||||
bool mask_to_left = (opcode == BPF_ADD && off_is_neg) ||
|
||||
(opcode == BPF_SUB && !off_is_neg);
|
||||
u32 max = 0, ptr_limit = 0;
|
||||
|
||||
if (!tnum_is_const(off_reg->var_off) &&
|
||||
(off_reg->smin_value < 0) != (off_reg->smax_value < 0))
|
||||
return REASON_BOUNDS;
|
||||
|
||||
switch (ptr_reg->type) {
|
||||
case PTR_TO_STACK:
|
||||
/* Offset 0 is out-of-bounds, but acceptable start for the
|
||||
@@ -6488,15 +6480,20 @@ static bool sanitize_needed(u8 opcode)
|
||||
return opcode == BPF_ADD || opcode == BPF_SUB;
|
||||
}
|
||||
|
||||
struct bpf_sanitize_info {
|
||||
struct bpf_insn_aux_data aux;
|
||||
bool mask_to_left;
|
||||
};
|
||||
|
||||
static int sanitize_ptr_alu(struct bpf_verifier_env *env,
|
||||
struct bpf_insn *insn,
|
||||
const struct bpf_reg_state *ptr_reg,
|
||||
const struct bpf_reg_state *off_reg,
|
||||
struct bpf_reg_state *dst_reg,
|
||||
struct bpf_insn_aux_data *tmp_aux,
|
||||
struct bpf_sanitize_info *info,
|
||||
const bool commit_window)
|
||||
{
|
||||
struct bpf_insn_aux_data *aux = commit_window ? cur_aux(env) : tmp_aux;
|
||||
struct bpf_insn_aux_data *aux = commit_window ? cur_aux(env) : &info->aux;
|
||||
struct bpf_verifier_state *vstate = env->cur_state;
|
||||
bool off_is_imm = tnum_is_const(off_reg->var_off);
|
||||
bool off_is_neg = off_reg->smin_value < 0;
|
||||
@@ -6517,7 +6514,16 @@ static int sanitize_ptr_alu(struct bpf_verifier_env *env,
|
||||
if (vstate->speculative)
|
||||
goto do_sim;
|
||||
|
||||
err = retrieve_ptr_limit(ptr_reg, off_reg, &alu_limit, opcode);
|
||||
if (!commit_window) {
|
||||
if (!tnum_is_const(off_reg->var_off) &&
|
||||
(off_reg->smin_value < 0) != (off_reg->smax_value < 0))
|
||||
return REASON_BOUNDS;
|
||||
|
||||
info->mask_to_left = (opcode == BPF_ADD && off_is_neg) ||
|
||||
(opcode == BPF_SUB && !off_is_neg);
|
||||
}
|
||||
|
||||
err = retrieve_ptr_limit(ptr_reg, &alu_limit, info->mask_to_left);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@@ -6525,8 +6531,8 @@ static int sanitize_ptr_alu(struct bpf_verifier_env *env,
|
||||
/* In commit phase we narrow the masking window based on
|
||||
* the observed pointer move after the simulated operation.
|
||||
*/
|
||||
alu_state = tmp_aux->alu_state;
|
||||
alu_limit = abs(tmp_aux->alu_limit - alu_limit);
|
||||
alu_state = info->aux.alu_state;
|
||||
alu_limit = abs(info->aux.alu_limit - alu_limit);
|
||||
} else {
|
||||
alu_state = off_is_neg ? BPF_ALU_NEG_VALUE : 0;
|
||||
alu_state |= off_is_imm ? BPF_ALU_IMMEDIATE : 0;
|
||||
@@ -6541,8 +6547,12 @@ do_sim:
|
||||
/* If we're in commit phase, we're done here given we already
|
||||
* pushed the truncated dst_reg into the speculative verification
|
||||
* stack.
|
||||
*
|
||||
* Also, when register is a known constant, we rewrite register-based
|
||||
* operation to immediate-based, and thus do not need masking (and as
|
||||
* a consequence, do not need to simulate the zero-truncation either).
|
||||
*/
|
||||
if (commit_window)
|
||||
if (commit_window || off_is_imm)
|
||||
return 0;
|
||||
|
||||
/* Simulate and find potential out-of-bounds access under
|
||||
@@ -6687,7 +6697,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
|
||||
smin_ptr = ptr_reg->smin_value, smax_ptr = ptr_reg->smax_value;
|
||||
u64 umin_val = off_reg->umin_value, umax_val = off_reg->umax_value,
|
||||
umin_ptr = ptr_reg->umin_value, umax_ptr = ptr_reg->umax_value;
|
||||
struct bpf_insn_aux_data tmp_aux = {};
|
||||
struct bpf_sanitize_info info = {};
|
||||
u8 opcode = BPF_OP(insn->code);
|
||||
u32 dst = insn->dst_reg;
|
||||
int ret;
|
||||
@@ -6756,7 +6766,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
|
||||
|
||||
if (sanitize_needed(opcode)) {
|
||||
ret = sanitize_ptr_alu(env, insn, ptr_reg, off_reg, dst_reg,
|
||||
&tmp_aux, false);
|
||||
&info, false);
|
||||
if (ret < 0)
|
||||
return sanitize_err(env, insn, ret, off_reg, dst_reg);
|
||||
}
|
||||
@@ -6897,7 +6907,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
|
||||
return -EACCES;
|
||||
if (sanitize_needed(opcode)) {
|
||||
ret = sanitize_ptr_alu(env, insn, dst_reg, off_reg, dst_reg,
|
||||
&tmp_aux, true);
|
||||
&info, true);
|
||||
if (ret < 0)
|
||||
return sanitize_err(env, insn, ret, off_reg, dst_reg);
|
||||
}
|
||||
@@ -7086,11 +7096,10 @@ static void scalar32_min_max_and(struct bpf_reg_state *dst_reg,
|
||||
s32 smin_val = src_reg->s32_min_value;
|
||||
u32 umax_val = src_reg->u32_max_value;
|
||||
|
||||
/* Assuming scalar64_min_max_and will be called so its safe
|
||||
* to skip updating register for known 32-bit case.
|
||||
*/
|
||||
if (src_known && dst_known)
|
||||
if (src_known && dst_known) {
|
||||
__mark_reg32_known(dst_reg, var32_off.value);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We get our minimum from the var_off, since that's inherently
|
||||
* bitwise. Our maximum is the minimum of the operands' maxima.
|
||||
@@ -7110,7 +7119,6 @@ static void scalar32_min_max_and(struct bpf_reg_state *dst_reg,
|
||||
dst_reg->s32_min_value = dst_reg->u32_min_value;
|
||||
dst_reg->s32_max_value = dst_reg->u32_max_value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void scalar_min_max_and(struct bpf_reg_state *dst_reg,
|
||||
@@ -7157,11 +7165,10 @@ static void scalar32_min_max_or(struct bpf_reg_state *dst_reg,
|
||||
s32 smin_val = src_reg->s32_min_value;
|
||||
u32 umin_val = src_reg->u32_min_value;
|
||||
|
||||
/* Assuming scalar64_min_max_or will be called so it is safe
|
||||
* to skip updating register for known case.
|
||||
*/
|
||||
if (src_known && dst_known)
|
||||
if (src_known && dst_known) {
|
||||
__mark_reg32_known(dst_reg, var32_off.value);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We get our maximum from the var_off, and our minimum is the
|
||||
* maximum of the operands' minima
|
||||
@@ -7226,11 +7233,10 @@ static void scalar32_min_max_xor(struct bpf_reg_state *dst_reg,
|
||||
struct tnum var32_off = tnum_subreg(dst_reg->var_off);
|
||||
s32 smin_val = src_reg->s32_min_value;
|
||||
|
||||
/* Assuming scalar64_min_max_xor will be called so it is safe
|
||||
* to skip updating register for known case.
|
||||
*/
|
||||
if (src_known && dst_known)
|
||||
if (src_known && dst_known) {
|
||||
__mark_reg32_known(dst_reg, var32_off.value);
|
||||
return;
|
||||
}
|
||||
|
||||
/* We get both minimum and maximum from the var32_off. */
|
||||
dst_reg->u32_min_value = var32_off.value;
|
||||
@@ -13215,6 +13221,17 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
|
||||
return 0;
|
||||
}
|
||||
|
||||
BTF_SET_START(btf_id_deny)
|
||||
BTF_ID_UNUSED
|
||||
#ifdef CONFIG_SMP
|
||||
BTF_ID(func, migrate_disable)
|
||||
BTF_ID(func, migrate_enable)
|
||||
#endif
|
||||
#if !defined CONFIG_PREEMPT_RCU && !defined CONFIG_TINY_RCU
|
||||
BTF_ID(func, rcu_read_unlock_strict)
|
||||
#endif
|
||||
BTF_SET_END(btf_id_deny)
|
||||
|
||||
static int check_attach_btf_id(struct bpf_verifier_env *env)
|
||||
{
|
||||
struct bpf_prog *prog = env->prog;
|
||||
@@ -13282,6 +13299,9 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
|
||||
ret = bpf_lsm_verify_prog(&env->log, prog);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (prog->type == BPF_PROG_TYPE_TRACING &&
|
||||
btf_id_set_contains(&btf_id_deny, btf_id)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
key = bpf_trampoline_compute_key(tgt_prog, prog->aux->attach_btf, btf_id);
|
||||
@@ -13381,12 +13401,6 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr)
|
||||
if (is_priv)
|
||||
env->test_state_freq = attr->prog_flags & BPF_F_TEST_STATE_FREQ;
|
||||
|
||||
if (bpf_prog_is_dev_bound(env->prog->aux)) {
|
||||
ret = bpf_prog_offload_verifier_prep(env->prog);
|
||||
if (ret)
|
||||
goto skip_full_check;
|
||||
}
|
||||
|
||||
env->explored_states = kvcalloc(state_htab_size(env),
|
||||
sizeof(struct bpf_verifier_state_list *),
|
||||
GFP_USER);
|
||||
@@ -13414,6 +13428,12 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr)
|
||||
if (ret < 0)
|
||||
goto skip_full_check;
|
||||
|
||||
if (bpf_prog_is_dev_bound(env->prog->aux)) {
|
||||
ret = bpf_prog_offload_verifier_prep(env->prog);
|
||||
if (ret)
|
||||
goto skip_full_check;
|
||||
}
|
||||
|
||||
ret = check_cfg(env);
|
||||
if (ret < 0)
|
||||
goto skip_full_check;
|
||||
|
||||
@@ -1001,7 +1001,7 @@ static int check_cgroupfs_options(struct fs_context *fc)
|
||||
ctx->subsys_mask &= enabled;
|
||||
|
||||
/*
|
||||
* In absense of 'none', 'name=' or subsystem name options,
|
||||
* In absence of 'none', 'name=' and subsystem name options,
|
||||
* let's default to 'all'.
|
||||
*/
|
||||
if (!ctx->subsys_mask && !ctx->none && !ctx->name)
|
||||
|
||||
+9
-12
@@ -468,7 +468,7 @@ static struct cgroup_subsys_state *cgroup_css(struct cgroup *cgrp,
|
||||
* @cgrp: the cgroup of interest
|
||||
* @ss: the subsystem of interest
|
||||
*
|
||||
* Find and get @cgrp's css assocaited with @ss. If the css doesn't exist
|
||||
* Find and get @cgrp's css associated with @ss. If the css doesn't exist
|
||||
* or is offline, %NULL is returned.
|
||||
*/
|
||||
static struct cgroup_subsys_state *cgroup_tryget_css(struct cgroup *cgrp,
|
||||
@@ -1633,7 +1633,7 @@ static void cgroup_rm_file(struct cgroup *cgrp, const struct cftype *cft)
|
||||
|
||||
/**
|
||||
* css_clear_dir - remove subsys files in a cgroup directory
|
||||
* @css: taget css
|
||||
* @css: target css
|
||||
*/
|
||||
static void css_clear_dir(struct cgroup_subsys_state *css)
|
||||
{
|
||||
@@ -5350,7 +5350,7 @@ out_unlock:
|
||||
/*
|
||||
* This is called when the refcnt of a css is confirmed to be killed.
|
||||
* css_tryget_online() is now guaranteed to fail. Tell the subsystem to
|
||||
* initate destruction and put the css ref from kill_css().
|
||||
* initiate destruction and put the css ref from kill_css().
|
||||
*/
|
||||
static void css_killed_work_fn(struct work_struct *work)
|
||||
{
|
||||
@@ -5634,8 +5634,6 @@ int __init cgroup_init_early(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u16 cgroup_disable_mask __initdata;
|
||||
|
||||
/**
|
||||
* cgroup_init - cgroup initialization
|
||||
*
|
||||
@@ -5694,12 +5692,8 @@ int __init cgroup_init(void)
|
||||
* disabled flag and cftype registration needs kmalloc,
|
||||
* both of which aren't available during early_init.
|
||||
*/
|
||||
if (cgroup_disable_mask & (1 << ssid)) {
|
||||
static_branch_disable(cgroup_subsys_enabled_key[ssid]);
|
||||
printk(KERN_INFO "Disabling %s control group subsystem\n",
|
||||
ss->name);
|
||||
if (!cgroup_ssid_enabled(ssid))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cgroup1_ssid_disabled(ssid))
|
||||
printk(KERN_INFO "Disabling %s control group subsystem in v1 mounts\n",
|
||||
@@ -6058,7 +6052,7 @@ out_revert:
|
||||
* @kargs: the arguments passed to create the child process
|
||||
*
|
||||
* This calls the cancel_fork() callbacks if a fork failed *after*
|
||||
* cgroup_can_fork() succeded and cleans up references we took to
|
||||
* cgroup_can_fork() succeeded and cleans up references we took to
|
||||
* prepare a new css_set for the child process in cgroup_can_fork().
|
||||
*/
|
||||
void cgroup_cancel_fork(struct task_struct *child,
|
||||
@@ -6214,7 +6208,10 @@ static int __init cgroup_disable(char *str)
|
||||
if (strcmp(token, ss->name) &&
|
||||
strcmp(token, ss->legacy_name))
|
||||
continue;
|
||||
cgroup_disable_mask |= 1 << i;
|
||||
|
||||
static_branch_disable(cgroup_subsys_enabled_key[i]);
|
||||
pr_info("Disabling %s control group subsystem\n",
|
||||
ss->name);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
|
||||
@@ -3376,7 +3376,7 @@ nodemask_t cpuset_mems_allowed(struct task_struct *tsk)
|
||||
}
|
||||
|
||||
/**
|
||||
* cpuset_nodemask_valid_mems_allowed - check nodemask vs. curremt mems_allowed
|
||||
* cpuset_nodemask_valid_mems_allowed - check nodemask vs. current mems_allowed
|
||||
* @nodemask: the nodemask to be checked
|
||||
*
|
||||
* Are any of the nodes in the nodemask allowed in current->mems_allowed?
|
||||
|
||||
@@ -244,7 +244,7 @@ EXPORT_SYMBOL(rdmacg_uncharge);
|
||||
* This function follows charging resource in hierarchical way.
|
||||
* It will fail if the charge would cause the new value to exceed the
|
||||
* hierarchical limit.
|
||||
* Returns 0 if the charge succeded, otherwise -EAGAIN, -ENOMEM or -EINVAL.
|
||||
* Returns 0 if the charge succeeded, otherwise -EAGAIN, -ENOMEM or -EINVAL.
|
||||
* Returns pointer to rdmacg for this resource when charging is successful.
|
||||
*
|
||||
* Charger needs to account resources on two criteria.
|
||||
|
||||
@@ -75,7 +75,7 @@ void cgroup_rstat_updated(struct cgroup *cgrp, int cpu)
|
||||
* @root: root of the tree to traversal
|
||||
* @cpu: target cpu
|
||||
*
|
||||
* Walks the udpated rstat_cpu tree on @cpu from @root. %NULL @pos starts
|
||||
* Walks the updated rstat_cpu tree on @cpu from @root. %NULL @pos starts
|
||||
* the traversal and %NULL return indicates the end. During traversal,
|
||||
* each returned cgroup is unlinked from the tree. Must be called with the
|
||||
* matching cgroup_rstat_cpu_lock held.
|
||||
|
||||
@@ -6389,8 +6389,6 @@ void perf_event_wakeup(struct perf_event *event)
|
||||
|
||||
static void perf_sigtrap(struct perf_event *event)
|
||||
{
|
||||
struct kernel_siginfo info;
|
||||
|
||||
/*
|
||||
* We'd expect this to only occur if the irq_work is delayed and either
|
||||
* ctx->task or current has changed in the meantime. This can be the
|
||||
@@ -6405,13 +6403,8 @@ static void perf_sigtrap(struct perf_event *event)
|
||||
if (current->flags & PF_EXITING)
|
||||
return;
|
||||
|
||||
clear_siginfo(&info);
|
||||
info.si_signo = SIGTRAP;
|
||||
info.si_code = TRAP_PERF;
|
||||
info.si_errno = event->attr.type;
|
||||
info.si_perf = event->attr.sig_data;
|
||||
info.si_addr = (void __user *)event->pending_addr;
|
||||
force_sig_info(&info);
|
||||
force_sig_perf((void __user *)event->pending_addr,
|
||||
event->attr.type, event->attr.sig_data);
|
||||
}
|
||||
|
||||
static void perf_pending_event_disable(struct perf_event *event)
|
||||
|
||||
+40
-42
@@ -3710,8 +3710,7 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
|
||||
|
||||
if (op & FUTEX_CLOCK_REALTIME) {
|
||||
flags |= FLAGS_CLOCKRT;
|
||||
if (cmd != FUTEX_WAIT && cmd != FUTEX_WAIT_BITSET && \
|
||||
cmd != FUTEX_WAIT_REQUEUE_PI)
|
||||
if (cmd != FUTEX_WAIT_BITSET && cmd != FUTEX_WAIT_REQUEUE_PI)
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
@@ -3758,42 +3757,52 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static __always_inline bool futex_cmd_has_timeout(u32 cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case FUTEX_WAIT:
|
||||
case FUTEX_LOCK_PI:
|
||||
case FUTEX_WAIT_BITSET:
|
||||
case FUTEX_WAIT_REQUEUE_PI:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static __always_inline int
|
||||
futex_init_timeout(u32 cmd, u32 op, struct timespec64 *ts, ktime_t *t)
|
||||
{
|
||||
if (!timespec64_valid(ts))
|
||||
return -EINVAL;
|
||||
|
||||
*t = timespec64_to_ktime(*ts);
|
||||
if (cmd == FUTEX_WAIT)
|
||||
*t = ktime_add_safe(ktime_get(), *t);
|
||||
else if (cmd != FUTEX_LOCK_PI && !(op & FUTEX_CLOCK_REALTIME))
|
||||
*t = timens_ktime_to_host(CLOCK_MONOTONIC, *t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
|
||||
const struct __kernel_timespec __user *, utime,
|
||||
u32 __user *, uaddr2, u32, val3)
|
||||
{
|
||||
struct timespec64 ts;
|
||||
int ret, cmd = op & FUTEX_CMD_MASK;
|
||||
ktime_t t, *tp = NULL;
|
||||
u32 val2 = 0;
|
||||
int cmd = op & FUTEX_CMD_MASK;
|
||||
struct timespec64 ts;
|
||||
|
||||
if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
|
||||
cmd == FUTEX_WAIT_BITSET ||
|
||||
cmd == FUTEX_WAIT_REQUEUE_PI)) {
|
||||
if (utime && futex_cmd_has_timeout(cmd)) {
|
||||
if (unlikely(should_fail_futex(!(op & FUTEX_PRIVATE_FLAG))))
|
||||
return -EFAULT;
|
||||
if (get_timespec64(&ts, utime))
|
||||
return -EFAULT;
|
||||
if (!timespec64_valid(&ts))
|
||||
return -EINVAL;
|
||||
|
||||
t = timespec64_to_ktime(ts);
|
||||
if (cmd == FUTEX_WAIT)
|
||||
t = ktime_add_safe(ktime_get(), t);
|
||||
else if (!(op & FUTEX_CLOCK_REALTIME))
|
||||
t = timens_ktime_to_host(CLOCK_MONOTONIC, t);
|
||||
ret = futex_init_timeout(cmd, op, &ts, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
tp = &t;
|
||||
}
|
||||
/*
|
||||
* requeue parameter in 'utime' if cmd == FUTEX_*_REQUEUE_*.
|
||||
* number of waiters to wake in 'utime' if cmd == FUTEX_WAKE_OP.
|
||||
*/
|
||||
if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
|
||||
cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)
|
||||
val2 = (u32) (unsigned long) utime;
|
||||
|
||||
return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
|
||||
return do_futex(uaddr, op, val, tp, uaddr2, (unsigned long)utime, val3);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
@@ -3959,31 +3968,20 @@ SYSCALL_DEFINE6(futex_time32, u32 __user *, uaddr, int, op, u32, val,
|
||||
const struct old_timespec32 __user *, utime, u32 __user *, uaddr2,
|
||||
u32, val3)
|
||||
{
|
||||
struct timespec64 ts;
|
||||
int ret, cmd = op & FUTEX_CMD_MASK;
|
||||
ktime_t t, *tp = NULL;
|
||||
int val2 = 0;
|
||||
int cmd = op & FUTEX_CMD_MASK;
|
||||
struct timespec64 ts;
|
||||
|
||||
if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI ||
|
||||
cmd == FUTEX_WAIT_BITSET ||
|
||||
cmd == FUTEX_WAIT_REQUEUE_PI)) {
|
||||
if (utime && futex_cmd_has_timeout(cmd)) {
|
||||
if (get_old_timespec32(&ts, utime))
|
||||
return -EFAULT;
|
||||
if (!timespec64_valid(&ts))
|
||||
return -EINVAL;
|
||||
|
||||
t = timespec64_to_ktime(ts);
|
||||
if (cmd == FUTEX_WAIT)
|
||||
t = ktime_add_safe(ktime_get(), t);
|
||||
else if (!(op & FUTEX_CLOCK_REALTIME))
|
||||
t = timens_ktime_to_host(CLOCK_MONOTONIC, t);
|
||||
ret = futex_init_timeout(cmd, op, &ts, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
tp = &t;
|
||||
}
|
||||
if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
|
||||
cmd == FUTEX_CMP_REQUEUE_PI || cmd == FUTEX_WAKE_OP)
|
||||
val2 = (int) (unsigned long) utime;
|
||||
|
||||
return do_futex(uaddr, op, val, tp, uaddr2, val2, val3);
|
||||
return do_futex(uaddr, op, val, tp, uaddr2, (unsigned long)utime, val3);
|
||||
}
|
||||
#endif /* CONFIG_COMPAT_32BIT_TIME */
|
||||
|
||||
|
||||
@@ -266,9 +266,10 @@ static const struct file_operations debugfs_ops =
|
||||
.release = single_release
|
||||
};
|
||||
|
||||
static void __init kcsan_debugfs_init(void)
|
||||
static int __init kcsan_debugfs_init(void)
|
||||
{
|
||||
debugfs_create_file("kcsan", 0644, NULL, NULL, &debugfs_ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(kcsan_debugfs_init);
|
||||
|
||||
@@ -5736,7 +5736,7 @@ void lock_contended(struct lockdep_map *lock, unsigned long ip)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
trace_lock_acquired(lock, ip);
|
||||
trace_lock_contended(lock, ip);
|
||||
|
||||
if (unlikely(!lock_stat || !lockdep_enabled()))
|
||||
return;
|
||||
@@ -5754,7 +5754,7 @@ void lock_acquired(struct lockdep_map *lock, unsigned long ip)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
trace_lock_contended(lock, ip);
|
||||
trace_lock_acquired(lock, ip);
|
||||
|
||||
if (unlikely(!lock_stat || !lockdep_enabled()))
|
||||
return;
|
||||
|
||||
@@ -57,7 +57,7 @@ void debug_mutex_add_waiter(struct mutex *lock, struct mutex_waiter *waiter,
|
||||
task->blocked_on = waiter;
|
||||
}
|
||||
|
||||
void mutex_remove_waiter(struct mutex *lock, struct mutex_waiter *waiter,
|
||||
void debug_mutex_remove_waiter(struct mutex *lock, struct mutex_waiter *waiter,
|
||||
struct task_struct *task)
|
||||
{
|
||||
DEBUG_LOCKS_WARN_ON(list_empty(&waiter->list));
|
||||
@@ -65,7 +65,7 @@ void mutex_remove_waiter(struct mutex *lock, struct mutex_waiter *waiter,
|
||||
DEBUG_LOCKS_WARN_ON(task->blocked_on != waiter);
|
||||
task->blocked_on = NULL;
|
||||
|
||||
list_del_init(&waiter->list);
|
||||
INIT_LIST_HEAD(&waiter->list);
|
||||
waiter->task = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ extern void debug_mutex_free_waiter(struct mutex_waiter *waiter);
|
||||
extern void debug_mutex_add_waiter(struct mutex *lock,
|
||||
struct mutex_waiter *waiter,
|
||||
struct task_struct *task);
|
||||
extern void mutex_remove_waiter(struct mutex *lock, struct mutex_waiter *waiter,
|
||||
extern void debug_mutex_remove_waiter(struct mutex *lock, struct mutex_waiter *waiter,
|
||||
struct task_struct *task);
|
||||
extern void debug_mutex_unlock(struct mutex *lock);
|
||||
extern void debug_mutex_init(struct mutex *lock, const char *name,
|
||||
|
||||
+13
-5
@@ -194,7 +194,7 @@ static inline bool __mutex_waiter_is_first(struct mutex *lock, struct mutex_wait
|
||||
* Add @waiter to a given location in the lock wait_list and set the
|
||||
* FLAG_WAITERS flag if it's the first waiter.
|
||||
*/
|
||||
static void __sched
|
||||
static void
|
||||
__mutex_add_waiter(struct mutex *lock, struct mutex_waiter *waiter,
|
||||
struct list_head *list)
|
||||
{
|
||||
@@ -205,6 +205,16 @@ __mutex_add_waiter(struct mutex *lock, struct mutex_waiter *waiter,
|
||||
__mutex_set_flag(lock, MUTEX_FLAG_WAITERS);
|
||||
}
|
||||
|
||||
static void
|
||||
__mutex_remove_waiter(struct mutex *lock, struct mutex_waiter *waiter)
|
||||
{
|
||||
list_del(&waiter->list);
|
||||
if (likely(list_empty(&lock->wait_list)))
|
||||
__mutex_clear_flag(lock, MUTEX_FLAGS);
|
||||
|
||||
debug_mutex_remove_waiter(lock, waiter, current);
|
||||
}
|
||||
|
||||
/*
|
||||
* Give up ownership to a specific task, when @task = NULL, this is equivalent
|
||||
* to a regular unlock. Sets PICKUP on a handoff, clears HANDOFF, preserves
|
||||
@@ -1061,9 +1071,7 @@ acquired:
|
||||
__ww_mutex_check_waiters(lock, ww_ctx);
|
||||
}
|
||||
|
||||
mutex_remove_waiter(lock, &waiter, current);
|
||||
if (likely(list_empty(&lock->wait_list)))
|
||||
__mutex_clear_flag(lock, MUTEX_FLAGS);
|
||||
__mutex_remove_waiter(lock, &waiter);
|
||||
|
||||
debug_mutex_free_waiter(&waiter);
|
||||
|
||||
@@ -1080,7 +1088,7 @@ skip_wait:
|
||||
|
||||
err:
|
||||
__set_current_state(TASK_RUNNING);
|
||||
mutex_remove_waiter(lock, &waiter, current);
|
||||
__mutex_remove_waiter(lock, &waiter);
|
||||
err_early_kill:
|
||||
spin_unlock(&lock->wait_lock);
|
||||
debug_mutex_free_waiter(&waiter);
|
||||
|
||||
@@ -10,12 +10,10 @@
|
||||
* !CONFIG_DEBUG_MUTEXES case. Most of them are NOPs:
|
||||
*/
|
||||
|
||||
#define mutex_remove_waiter(lock, waiter, task) \
|
||||
__list_del((waiter)->list.prev, (waiter)->list.next)
|
||||
|
||||
#define debug_mutex_wake_waiter(lock, waiter) do { } while (0)
|
||||
#define debug_mutex_free_waiter(waiter) do { } while (0)
|
||||
#define debug_mutex_add_waiter(lock, waiter, ti) do { } while (0)
|
||||
#define debug_mutex_remove_waiter(lock, waiter, ti) do { } while (0)
|
||||
#define debug_mutex_unlock(lock) do { } while (0)
|
||||
#define debug_mutex_init(lock, name, key) do { } while (0)
|
||||
|
||||
|
||||
@@ -66,12 +66,12 @@ void queued_write_lock_slowpath(struct qrwlock *lock)
|
||||
arch_spin_lock(&lock->wait_lock);
|
||||
|
||||
/* Try to acquire the lock directly if no reader is present */
|
||||
if (!atomic_read(&lock->cnts) &&
|
||||
(atomic_cmpxchg_acquire(&lock->cnts, 0, _QW_LOCKED) == 0))
|
||||
if (!(cnts = atomic_read(&lock->cnts)) &&
|
||||
atomic_try_cmpxchg_acquire(&lock->cnts, &cnts, _QW_LOCKED))
|
||||
goto unlock;
|
||||
|
||||
/* Set the waiting flag to notify readers that a writer is pending */
|
||||
atomic_add(_QW_WAITING, &lock->cnts);
|
||||
atomic_or(_QW_WAITING, &lock->cnts);
|
||||
|
||||
/* When no more readers or writers, set the locked flag */
|
||||
do {
|
||||
|
||||
+11
-6
@@ -2401,6 +2401,15 @@ static long get_offset(struct module *mod, unsigned int *size,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool module_init_layout_section(const char *sname)
|
||||
{
|
||||
#ifndef CONFIG_MODULE_UNLOAD
|
||||
if (module_exit_section(sname))
|
||||
return true;
|
||||
#endif
|
||||
return module_init_section(sname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
|
||||
* might -- code, read-only data, read-write data, small data. Tally
|
||||
@@ -2435,7 +2444,7 @@ static void layout_sections(struct module *mod, struct load_info *info)
|
||||
if ((s->sh_flags & masks[m][0]) != masks[m][0]
|
||||
|| (s->sh_flags & masks[m][1])
|
||||
|| s->sh_entsize != ~0UL
|
||||
|| module_init_section(sname))
|
||||
|| module_init_layout_section(sname))
|
||||
continue;
|
||||
s->sh_entsize = get_offset(mod, &mod->core_layout.size, s, i);
|
||||
pr_debug("\t%s\n", sname);
|
||||
@@ -2468,7 +2477,7 @@ static void layout_sections(struct module *mod, struct load_info *info)
|
||||
if ((s->sh_flags & masks[m][0]) != masks[m][0]
|
||||
|| (s->sh_flags & masks[m][1])
|
||||
|| s->sh_entsize != ~0UL
|
||||
|| !module_init_section(sname))
|
||||
|| !module_init_layout_section(sname))
|
||||
continue;
|
||||
s->sh_entsize = (get_offset(mod, &mod->init_layout.size, s, i)
|
||||
| INIT_OFFSET_MASK);
|
||||
@@ -2807,11 +2816,7 @@ void * __weak module_alloc(unsigned long size)
|
||||
|
||||
bool __weak module_init_section(const char *name)
|
||||
{
|
||||
#ifndef CONFIG_MODULE_UNLOAD
|
||||
return strstarts(name, ".init") || module_exit_section(name);
|
||||
#else
|
||||
return strstarts(name, ".init");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool __weak module_exit_section(const char *name)
|
||||
|
||||
+17
-1
@@ -170,6 +170,21 @@ void __ptrace_unlink(struct task_struct *child)
|
||||
spin_unlock(&child->sighand->siglock);
|
||||
}
|
||||
|
||||
static bool looks_like_a_spurious_pid(struct task_struct *task)
|
||||
{
|
||||
if (task->exit_code != ((PTRACE_EVENT_EXEC << 8) | SIGTRAP))
|
||||
return false;
|
||||
|
||||
if (task_pid_vnr(task) == task->ptrace_message)
|
||||
return false;
|
||||
/*
|
||||
* The tracee changed its pid but the PTRACE_EVENT_EXEC event
|
||||
* was not wait()'ed, most probably debugger targets the old
|
||||
* leader which was destroyed in de_thread().
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Ensure that nothing can wake it up, even SIGKILL */
|
||||
static bool ptrace_freeze_traced(struct task_struct *task)
|
||||
{
|
||||
@@ -180,7 +195,8 @@ static bool ptrace_freeze_traced(struct task_struct *task)
|
||||
return ret;
|
||||
|
||||
spin_lock_irq(&task->sighand->siglock);
|
||||
if (task_is_traced(task) && !__fatal_signal_pending(task)) {
|
||||
if (task_is_traced(task) && !looks_like_a_spurious_pid(task) &&
|
||||
!__fatal_signal_pending(task)) {
|
||||
task->state = __TASK_TRACED;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1805,7 +1805,7 @@ static struct resource *__request_free_mem_region(struct device *dev,
|
||||
REGION_DISJOINT)
|
||||
continue;
|
||||
|
||||
if (!__request_region_locked(res, &iomem_resource, addr, size,
|
||||
if (__request_region_locked(res, &iomem_resource, addr, size,
|
||||
name, 0))
|
||||
break;
|
||||
|
||||
|
||||
+1
-1
@@ -938,7 +938,7 @@ DEFINE_STATIC_KEY_FALSE(sched_uclamp_used);
|
||||
|
||||
static inline unsigned int uclamp_bucket_id(unsigned int clamp_value)
|
||||
{
|
||||
return clamp_value / UCLAMP_BUCKET_DELTA;
|
||||
return min_t(unsigned int, clamp_value / UCLAMP_BUCKET_DELTA, UCLAMP_BUCKETS - 1);
|
||||
}
|
||||
|
||||
static inline unsigned int uclamp_none(enum uclamp_id clamp_id)
|
||||
|
||||
+10
-4
@@ -6217,7 +6217,7 @@ static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, bool
|
||||
}
|
||||
|
||||
if (has_idle_core)
|
||||
set_idle_cores(this, false);
|
||||
set_idle_cores(target, false);
|
||||
|
||||
if (sched_feat(SIS_PROP) && !has_idle_core) {
|
||||
time = cpu_clock(this) - time;
|
||||
@@ -10878,16 +10878,22 @@ static void propagate_entity_cfs_rq(struct sched_entity *se)
|
||||
{
|
||||
struct cfs_rq *cfs_rq;
|
||||
|
||||
list_add_leaf_cfs_rq(cfs_rq_of(se));
|
||||
|
||||
/* Start to propagate at parent */
|
||||
se = se->parent;
|
||||
|
||||
for_each_sched_entity(se) {
|
||||
cfs_rq = cfs_rq_of(se);
|
||||
|
||||
if (cfs_rq_throttled(cfs_rq))
|
||||
break;
|
||||
if (!cfs_rq_throttled(cfs_rq)){
|
||||
update_load_avg(cfs_rq, se, UPDATE_TG);
|
||||
list_add_leaf_cfs_rq(cfs_rq);
|
||||
continue;
|
||||
}
|
||||
|
||||
update_load_avg(cfs_rq, se, UPDATE_TG);
|
||||
if (list_add_leaf_cfs_rq(cfs_rq))
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
+26
-10
@@ -972,7 +972,7 @@ void psi_cgroup_free(struct cgroup *cgroup)
|
||||
*/
|
||||
void cgroup_move_task(struct task_struct *task, struct css_set *to)
|
||||
{
|
||||
unsigned int task_flags = 0;
|
||||
unsigned int task_flags;
|
||||
struct rq_flags rf;
|
||||
struct rq *rq;
|
||||
|
||||
@@ -987,15 +987,31 @@ void cgroup_move_task(struct task_struct *task, struct css_set *to)
|
||||
|
||||
rq = task_rq_lock(task, &rf);
|
||||
|
||||
if (task_on_rq_queued(task)) {
|
||||
task_flags = TSK_RUNNING;
|
||||
if (task_current(rq, task))
|
||||
task_flags |= TSK_ONCPU;
|
||||
} else if (task->in_iowait)
|
||||
task_flags = TSK_IOWAIT;
|
||||
|
||||
if (task->in_memstall)
|
||||
task_flags |= TSK_MEMSTALL;
|
||||
/*
|
||||
* We may race with schedule() dropping the rq lock between
|
||||
* deactivating prev and switching to next. Because the psi
|
||||
* updates from the deactivation are deferred to the switch
|
||||
* callback to save cgroup tree updates, the task's scheduling
|
||||
* state here is not coherent with its psi state:
|
||||
*
|
||||
* schedule() cgroup_move_task()
|
||||
* rq_lock()
|
||||
* deactivate_task()
|
||||
* p->on_rq = 0
|
||||
* psi_dequeue() // defers TSK_RUNNING & TSK_IOWAIT updates
|
||||
* pick_next_task()
|
||||
* rq_unlock()
|
||||
* rq_lock()
|
||||
* psi_task_change() // old cgroup
|
||||
* task->cgroups = to
|
||||
* psi_task_change() // new cgroup
|
||||
* rq_unlock()
|
||||
* rq_lock()
|
||||
* psi_sched_switch() // does deferred updates in new cgroup
|
||||
*
|
||||
* Don't rely on the scheduling state. Use psi_flags instead.
|
||||
*/
|
||||
task_flags = task->psi_flags;
|
||||
|
||||
if (task_flags)
|
||||
psi_task_change(task, task_flags, 0);
|
||||
|
||||
+34
-25
@@ -1236,6 +1236,7 @@ static inline bool has_si_pid_and_uid(struct kernel_siginfo *info)
|
||||
case SIL_TIMER:
|
||||
case SIL_POLL:
|
||||
case SIL_FAULT:
|
||||
case SIL_FAULT_TRAPNO:
|
||||
case SIL_FAULT_MCEERR:
|
||||
case SIL_FAULT_BNDERR:
|
||||
case SIL_FAULT_PKUERR:
|
||||
@@ -1804,6 +1805,21 @@ int force_sig_pkuerr(void __user *addr, u32 pkey)
|
||||
}
|
||||
#endif
|
||||
|
||||
int force_sig_perf(void __user *addr, u32 type, u64 sig_data)
|
||||
{
|
||||
struct kernel_siginfo info;
|
||||
|
||||
clear_siginfo(&info);
|
||||
info.si_signo = SIGTRAP;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TRAP_PERF;
|
||||
info.si_addr = addr;
|
||||
info.si_perf_data = sig_data;
|
||||
info.si_perf_type = type;
|
||||
|
||||
return force_sig_info(&info);
|
||||
}
|
||||
|
||||
/* For the crazy architectures that include trap information in
|
||||
* the errno field, instead of an actual errno value.
|
||||
*/
|
||||
@@ -2564,6 +2580,7 @@ static void hide_si_addr_tag_bits(struct ksignal *ksig)
|
||||
{
|
||||
switch (siginfo_layout(ksig->sig, ksig->info.si_code)) {
|
||||
case SIL_FAULT:
|
||||
case SIL_FAULT_TRAPNO:
|
||||
case SIL_FAULT_MCEERR:
|
||||
case SIL_FAULT_BNDERR:
|
||||
case SIL_FAULT_PKUERR:
|
||||
@@ -3251,6 +3268,10 @@ enum siginfo_layout siginfo_layout(unsigned sig, int si_code)
|
||||
#endif
|
||||
else if ((sig == SIGTRAP) && (si_code == TRAP_PERF))
|
||||
layout = SIL_PERF_EVENT;
|
||||
#ifdef __ARCH_SI_TRAPNO
|
||||
else if (layout == SIL_FAULT)
|
||||
layout = SIL_FAULT_TRAPNO;
|
||||
#endif
|
||||
}
|
||||
else if (si_code <= NSIGPOLL)
|
||||
layout = SIL_POLL;
|
||||
@@ -3354,35 +3375,28 @@ void copy_siginfo_to_external32(struct compat_siginfo *to,
|
||||
break;
|
||||
case SIL_FAULT:
|
||||
to->si_addr = ptr_to_compat(from->si_addr);
|
||||
#ifdef __ARCH_SI_TRAPNO
|
||||
break;
|
||||
case SIL_FAULT_TRAPNO:
|
||||
to->si_addr = ptr_to_compat(from->si_addr);
|
||||
to->si_trapno = from->si_trapno;
|
||||
#endif
|
||||
break;
|
||||
case SIL_FAULT_MCEERR:
|
||||
to->si_addr = ptr_to_compat(from->si_addr);
|
||||
#ifdef __ARCH_SI_TRAPNO
|
||||
to->si_trapno = from->si_trapno;
|
||||
#endif
|
||||
to->si_addr_lsb = from->si_addr_lsb;
|
||||
break;
|
||||
case SIL_FAULT_BNDERR:
|
||||
to->si_addr = ptr_to_compat(from->si_addr);
|
||||
#ifdef __ARCH_SI_TRAPNO
|
||||
to->si_trapno = from->si_trapno;
|
||||
#endif
|
||||
to->si_lower = ptr_to_compat(from->si_lower);
|
||||
to->si_upper = ptr_to_compat(from->si_upper);
|
||||
break;
|
||||
case SIL_FAULT_PKUERR:
|
||||
to->si_addr = ptr_to_compat(from->si_addr);
|
||||
#ifdef __ARCH_SI_TRAPNO
|
||||
to->si_trapno = from->si_trapno;
|
||||
#endif
|
||||
to->si_pkey = from->si_pkey;
|
||||
break;
|
||||
case SIL_PERF_EVENT:
|
||||
to->si_addr = ptr_to_compat(from->si_addr);
|
||||
to->si_perf = from->si_perf;
|
||||
to->si_perf_data = from->si_perf_data;
|
||||
to->si_perf_type = from->si_perf_type;
|
||||
break;
|
||||
case SIL_CHLD:
|
||||
to->si_pid = from->si_pid;
|
||||
@@ -3438,35 +3452,28 @@ static int post_copy_siginfo_from_user32(kernel_siginfo_t *to,
|
||||
break;
|
||||
case SIL_FAULT:
|
||||
to->si_addr = compat_ptr(from->si_addr);
|
||||
#ifdef __ARCH_SI_TRAPNO
|
||||
break;
|
||||
case SIL_FAULT_TRAPNO:
|
||||
to->si_addr = compat_ptr(from->si_addr);
|
||||
to->si_trapno = from->si_trapno;
|
||||
#endif
|
||||
break;
|
||||
case SIL_FAULT_MCEERR:
|
||||
to->si_addr = compat_ptr(from->si_addr);
|
||||
#ifdef __ARCH_SI_TRAPNO
|
||||
to->si_trapno = from->si_trapno;
|
||||
#endif
|
||||
to->si_addr_lsb = from->si_addr_lsb;
|
||||
break;
|
||||
case SIL_FAULT_BNDERR:
|
||||
to->si_addr = compat_ptr(from->si_addr);
|
||||
#ifdef __ARCH_SI_TRAPNO
|
||||
to->si_trapno = from->si_trapno;
|
||||
#endif
|
||||
to->si_lower = compat_ptr(from->si_lower);
|
||||
to->si_upper = compat_ptr(from->si_upper);
|
||||
break;
|
||||
case SIL_FAULT_PKUERR:
|
||||
to->si_addr = compat_ptr(from->si_addr);
|
||||
#ifdef __ARCH_SI_TRAPNO
|
||||
to->si_trapno = from->si_trapno;
|
||||
#endif
|
||||
to->si_pkey = from->si_pkey;
|
||||
break;
|
||||
case SIL_PERF_EVENT:
|
||||
to->si_addr = compat_ptr(from->si_addr);
|
||||
to->si_perf = from->si_perf;
|
||||
to->si_perf_data = from->si_perf_data;
|
||||
to->si_perf_type = from->si_perf_type;
|
||||
break;
|
||||
case SIL_CHLD:
|
||||
to->si_pid = from->si_pid;
|
||||
@@ -4644,11 +4651,13 @@ static inline void siginfo_buildtime_checks(void)
|
||||
|
||||
/* sigfault */
|
||||
CHECK_OFFSET(si_addr);
|
||||
CHECK_OFFSET(si_trapno);
|
||||
CHECK_OFFSET(si_addr_lsb);
|
||||
CHECK_OFFSET(si_lower);
|
||||
CHECK_OFFSET(si_upper);
|
||||
CHECK_OFFSET(si_pkey);
|
||||
CHECK_OFFSET(si_perf);
|
||||
CHECK_OFFSET(si_perf_data);
|
||||
CHECK_OFFSET(si_perf_type);
|
||||
|
||||
/* sigpoll */
|
||||
CHECK_OFFSET(si_band);
|
||||
|
||||
+13
-13
@@ -211,7 +211,7 @@ static u64 cfd_seq_inc(unsigned int src, unsigned int dst, unsigned int type)
|
||||
} while (0)
|
||||
|
||||
/* Record current CSD work for current CPU, NULL to erase. */
|
||||
static void __csd_lock_record(call_single_data_t *csd)
|
||||
static void __csd_lock_record(struct __call_single_data *csd)
|
||||
{
|
||||
if (!csd) {
|
||||
smp_mb(); /* NULL cur_csd after unlock. */
|
||||
@@ -226,13 +226,13 @@ static void __csd_lock_record(call_single_data_t *csd)
|
||||
/* Or before unlock, as the case may be. */
|
||||
}
|
||||
|
||||
static __always_inline void csd_lock_record(call_single_data_t *csd)
|
||||
static __always_inline void csd_lock_record(struct __call_single_data *csd)
|
||||
{
|
||||
if (static_branch_unlikely(&csdlock_debug_enabled))
|
||||
__csd_lock_record(csd);
|
||||
}
|
||||
|
||||
static int csd_lock_wait_getcpu(call_single_data_t *csd)
|
||||
static int csd_lock_wait_getcpu(struct __call_single_data *csd)
|
||||
{
|
||||
unsigned int csd_type;
|
||||
|
||||
@@ -282,7 +282,7 @@ static const char *csd_lock_get_type(unsigned int type)
|
||||
return (type >= ARRAY_SIZE(seq_type)) ? "?" : seq_type[type];
|
||||
}
|
||||
|
||||
static void csd_lock_print_extended(call_single_data_t *csd, int cpu)
|
||||
static void csd_lock_print_extended(struct __call_single_data *csd, int cpu)
|
||||
{
|
||||
struct cfd_seq_local *seq = &per_cpu(cfd_seq_local, cpu);
|
||||
unsigned int srccpu = csd->node.src;
|
||||
@@ -321,7 +321,7 @@ static void csd_lock_print_extended(call_single_data_t *csd, int cpu)
|
||||
* the CSD_TYPE_SYNC/ASYNC types provide the destination CPU,
|
||||
* so waiting on other types gets much less information.
|
||||
*/
|
||||
static bool csd_lock_wait_toolong(call_single_data_t *csd, u64 ts0, u64 *ts1, int *bug_id)
|
||||
static bool csd_lock_wait_toolong(struct __call_single_data *csd, u64 ts0, u64 *ts1, int *bug_id)
|
||||
{
|
||||
int cpu = -1;
|
||||
int cpux;
|
||||
@@ -387,7 +387,7 @@ static bool csd_lock_wait_toolong(call_single_data_t *csd, u64 ts0, u64 *ts1, in
|
||||
* previous function call. For multi-cpu calls its even more interesting
|
||||
* as we'll have to ensure no other cpu is observing our csd.
|
||||
*/
|
||||
static void __csd_lock_wait(call_single_data_t *csd)
|
||||
static void __csd_lock_wait(struct __call_single_data *csd)
|
||||
{
|
||||
int bug_id = 0;
|
||||
u64 ts0, ts1;
|
||||
@@ -401,7 +401,7 @@ static void __csd_lock_wait(call_single_data_t *csd)
|
||||
smp_acquire__after_ctrl_dep();
|
||||
}
|
||||
|
||||
static __always_inline void csd_lock_wait(call_single_data_t *csd)
|
||||
static __always_inline void csd_lock_wait(struct __call_single_data *csd)
|
||||
{
|
||||
if (static_branch_unlikely(&csdlock_debug_enabled)) {
|
||||
__csd_lock_wait(csd);
|
||||
@@ -431,17 +431,17 @@ static void __smp_call_single_queue_debug(int cpu, struct llist_node *node)
|
||||
#else
|
||||
#define cfd_seq_store(var, src, dst, type)
|
||||
|
||||
static void csd_lock_record(call_single_data_t *csd)
|
||||
static void csd_lock_record(struct __call_single_data *csd)
|
||||
{
|
||||
}
|
||||
|
||||
static __always_inline void csd_lock_wait(call_single_data_t *csd)
|
||||
static __always_inline void csd_lock_wait(struct __call_single_data *csd)
|
||||
{
|
||||
smp_cond_load_acquire(&csd->node.u_flags, !(VAL & CSD_FLAG_LOCK));
|
||||
}
|
||||
#endif
|
||||
|
||||
static __always_inline void csd_lock(call_single_data_t *csd)
|
||||
static __always_inline void csd_lock(struct __call_single_data *csd)
|
||||
{
|
||||
csd_lock_wait(csd);
|
||||
csd->node.u_flags |= CSD_FLAG_LOCK;
|
||||
@@ -454,7 +454,7 @@ static __always_inline void csd_lock(call_single_data_t *csd)
|
||||
smp_wmb();
|
||||
}
|
||||
|
||||
static __always_inline void csd_unlock(call_single_data_t *csd)
|
||||
static __always_inline void csd_unlock(struct __call_single_data *csd)
|
||||
{
|
||||
WARN_ON(!(csd->node.u_flags & CSD_FLAG_LOCK));
|
||||
|
||||
@@ -501,7 +501,7 @@ void __smp_call_single_queue(int cpu, struct llist_node *node)
|
||||
* for execution on the given CPU. data must already have
|
||||
* ->func, ->info, and ->flags set.
|
||||
*/
|
||||
static int generic_exec_single(int cpu, call_single_data_t *csd)
|
||||
static int generic_exec_single(int cpu, struct __call_single_data *csd)
|
||||
{
|
||||
if (cpu == smp_processor_id()) {
|
||||
smp_call_func_t func = csd->func;
|
||||
@@ -784,7 +784,7 @@ EXPORT_SYMBOL(smp_call_function_single);
|
||||
* NOTE: Be careful, there is unfortunately no current debugging facility to
|
||||
* validate the correctness of this serialization.
|
||||
*/
|
||||
int smp_call_function_single_async(int cpu, call_single_data_t *csd)
|
||||
int smp_call_function_single_async(int cpu, struct __call_single_data *csd)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
|
||||
+24
-5
@@ -225,7 +225,27 @@ static int bpf_stats_handler(struct ctl_table *table, int write,
|
||||
mutex_unlock(&bpf_stats_enabled_mutex);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int bpf_unpriv_handler(struct ctl_table *table, int write,
|
||||
void *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
int ret, unpriv_enable = *(int *)table->data;
|
||||
bool locked_state = unpriv_enable == 1;
|
||||
struct ctl_table tmp = *table;
|
||||
|
||||
if (write && !capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
tmp.data = &unpriv_enable;
|
||||
ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
|
||||
if (write && !ret) {
|
||||
if (locked_state && unpriv_enable != 1)
|
||||
return -EPERM;
|
||||
*(int *)table->data = unpriv_enable;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_BPF_SYSCALL && CONFIG_SYSCTL */
|
||||
|
||||
/*
|
||||
* /proc/sys support
|
||||
@@ -2600,10 +2620,9 @@ static struct ctl_table kern_table[] = {
|
||||
.data = &sysctl_unprivileged_bpf_disabled,
|
||||
.maxlen = sizeof(sysctl_unprivileged_bpf_disabled),
|
||||
.mode = 0644,
|
||||
/* only handle a transition from default "0" to "1" */
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = SYSCTL_ONE,
|
||||
.extra2 = SYSCTL_ONE,
|
||||
.proc_handler = bpf_unpriv_handler,
|
||||
.extra1 = SYSCTL_ZERO,
|
||||
.extra2 = &two,
|
||||
},
|
||||
{
|
||||
.procname = "bpf_stats_enabled",
|
||||
|
||||
@@ -92,7 +92,7 @@ static int alarmtimer_rtc_add_device(struct device *dev,
|
||||
if (rtcdev)
|
||||
return -EBUSY;
|
||||
|
||||
if (!rtc->ops->set_alarm)
|
||||
if (!test_bit(RTC_FEATURE_ALARM, rtc->features))
|
||||
return -1;
|
||||
if (!device_may_wakeup(rtc->dev.parent))
|
||||
return -1;
|
||||
|
||||
+27
-4
@@ -3704,6 +3704,9 @@ void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,
|
||||
goto print;
|
||||
|
||||
while (*p) {
|
||||
bool star = false;
|
||||
int len = 0;
|
||||
|
||||
j = 0;
|
||||
|
||||
/* We only care about %s and variants */
|
||||
@@ -3725,13 +3728,17 @@ void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,
|
||||
/* Need to test cases like %08.*s */
|
||||
for (j = 1; p[i+j]; j++) {
|
||||
if (isdigit(p[i+j]) ||
|
||||
p[i+j] == '*' ||
|
||||
p[i+j] == '.')
|
||||
continue;
|
||||
if (p[i+j] == '*') {
|
||||
star = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (p[i+j] == 's')
|
||||
break;
|
||||
star = false;
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
@@ -3744,6 +3751,9 @@ void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,
|
||||
iter->fmt[i] = '\0';
|
||||
trace_seq_vprintf(&iter->seq, iter->fmt, ap);
|
||||
|
||||
if (star)
|
||||
len = va_arg(ap, int);
|
||||
|
||||
/* The ap now points to the string data of the %s */
|
||||
str = va_arg(ap, const char *);
|
||||
|
||||
@@ -3762,8 +3772,18 @@ void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,
|
||||
int ret;
|
||||
|
||||
/* Try to safely read the string */
|
||||
ret = strncpy_from_kernel_nofault(iter->fmt, str,
|
||||
iter->fmt_size);
|
||||
if (star) {
|
||||
if (len + 1 > iter->fmt_size)
|
||||
len = iter->fmt_size - 1;
|
||||
if (len < 0)
|
||||
len = 0;
|
||||
ret = copy_from_kernel_nofault(iter->fmt, str, len);
|
||||
iter->fmt[len] = 0;
|
||||
star = false;
|
||||
} else {
|
||||
ret = strncpy_from_kernel_nofault(iter->fmt, str,
|
||||
iter->fmt_size);
|
||||
}
|
||||
if (ret < 0)
|
||||
trace_seq_printf(&iter->seq, "(0x%px)", str);
|
||||
else
|
||||
@@ -3775,7 +3795,10 @@ void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,
|
||||
strncpy(iter->fmt, p + i, j + 1);
|
||||
iter->fmt[j+1] = '\0';
|
||||
}
|
||||
trace_seq_printf(&iter->seq, iter->fmt, str);
|
||||
if (star)
|
||||
trace_seq_printf(&iter->seq, iter->fmt, len, str);
|
||||
else
|
||||
trace_seq_printf(&iter->seq, iter->fmt, str);
|
||||
|
||||
p += i + j + 1;
|
||||
}
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ int smp_call_function_single(int cpu, void (*func) (void *info), void *info,
|
||||
}
|
||||
EXPORT_SYMBOL(smp_call_function_single);
|
||||
|
||||
int smp_call_function_single_async(int cpu, call_single_data_t *csd)
|
||||
int smp_call_function_single_async(int cpu, struct __call_single_data *csd)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
|
||||
+20
-14
@@ -302,10 +302,10 @@ void touch_softlockup_watchdog_sync(void)
|
||||
__this_cpu_write(watchdog_report_ts, SOFTLOCKUP_DELAY_REPORT);
|
||||
}
|
||||
|
||||
static int is_softlockup(unsigned long touch_ts, unsigned long period_ts)
|
||||
static int is_softlockup(unsigned long touch_ts,
|
||||
unsigned long period_ts,
|
||||
unsigned long now)
|
||||
{
|
||||
unsigned long now = get_timestamp();
|
||||
|
||||
if ((watchdog_enabled & SOFT_WATCHDOG_ENABLED) && watchdog_thresh){
|
||||
/* Warn about unreasonable delays. */
|
||||
if (time_after(now, period_ts + get_softlockup_thresh()))
|
||||
@@ -353,8 +353,7 @@ static int softlockup_fn(void *data)
|
||||
/* watchdog kicker functions */
|
||||
static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
|
||||
{
|
||||
unsigned long touch_ts = __this_cpu_read(watchdog_touch_ts);
|
||||
unsigned long period_ts = __this_cpu_read(watchdog_report_ts);
|
||||
unsigned long touch_ts, period_ts, now;
|
||||
struct pt_regs *regs = get_irq_regs();
|
||||
int duration;
|
||||
int softlockup_all_cpu_backtrace = sysctl_softlockup_all_cpu_backtrace;
|
||||
@@ -376,12 +375,23 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
|
||||
/* .. and repeat */
|
||||
hrtimer_forward_now(hrtimer, ns_to_ktime(sample_period));
|
||||
|
||||
/*
|
||||
* Read the current timestamp first. It might become invalid anytime
|
||||
* when a virtual machine is stopped by the host or when the watchog
|
||||
* is touched from NMI.
|
||||
*/
|
||||
now = get_timestamp();
|
||||
/*
|
||||
* If a virtual machine is stopped by the host it can look to
|
||||
* the watchdog like a soft lockup. Check to see if the host
|
||||
* stopped the vm before we process the timestamps.
|
||||
* the watchdog like a soft lockup. This function touches the watchdog.
|
||||
*/
|
||||
kvm_check_and_clear_guest_paused();
|
||||
/*
|
||||
* The stored timestamp is comparable with @now only when not touched.
|
||||
* It might get touched anytime from NMI. Make sure that is_softlockup()
|
||||
* uses the same (valid) value.
|
||||
*/
|
||||
period_ts = READ_ONCE(*this_cpu_ptr(&watchdog_report_ts));
|
||||
|
||||
/* Reset the interval when touched by known problematic code. */
|
||||
if (period_ts == SOFTLOCKUP_DELAY_REPORT) {
|
||||
@@ -398,13 +408,9 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
/* check for a softlockup
|
||||
* This is done by making sure a high priority task is
|
||||
* being scheduled. The task touches the watchdog to
|
||||
* indicate it is getting cpu time. If it hasn't then
|
||||
* this is a good indication some task is hogging the cpu
|
||||
*/
|
||||
duration = is_softlockup(touch_ts, period_ts);
|
||||
/* Check for a softlockup. */
|
||||
touch_ts = __this_cpu_read(watchdog_touch_ts);
|
||||
duration = is_softlockup(touch_ts, period_ts, now);
|
||||
if (unlikely(duration)) {
|
||||
/*
|
||||
* Prevent multiple soft-lockup reports if one cpu is already
|
||||
|
||||
+10
-2
@@ -50,6 +50,7 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/sched/isolation.h>
|
||||
#include <linux/nmi.h>
|
||||
#include <linux/kvm_para.h>
|
||||
|
||||
#include "workqueue_internal.h"
|
||||
|
||||
@@ -5772,6 +5773,7 @@ static void wq_watchdog_timer_fn(struct timer_list *unused)
|
||||
{
|
||||
unsigned long thresh = READ_ONCE(wq_watchdog_thresh) * HZ;
|
||||
bool lockup_detected = false;
|
||||
unsigned long now = jiffies;
|
||||
struct worker_pool *pool;
|
||||
int pi;
|
||||
|
||||
@@ -5786,6 +5788,12 @@ static void wq_watchdog_timer_fn(struct timer_list *unused)
|
||||
if (list_empty(&pool->worklist))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If a virtual machine is stopped by the host it can look to
|
||||
* the watchdog like a stall.
|
||||
*/
|
||||
kvm_check_and_clear_guest_paused();
|
||||
|
||||
/* get the latest of pool and touched timestamps */
|
||||
if (pool->cpu >= 0)
|
||||
touched = READ_ONCE(per_cpu(wq_watchdog_touched_cpu, pool->cpu));
|
||||
@@ -5799,12 +5807,12 @@ static void wq_watchdog_timer_fn(struct timer_list *unused)
|
||||
ts = touched;
|
||||
|
||||
/* did we stall? */
|
||||
if (time_after(jiffies, ts + thresh)) {
|
||||
if (time_after(now, ts + thresh)) {
|
||||
lockup_detected = true;
|
||||
pr_emerg("BUG: workqueue lockup - pool");
|
||||
pr_cont_pool_info(pool);
|
||||
pr_cont(" stuck for %us!\n",
|
||||
jiffies_to_msecs(jiffies - pool_ts) / 1000);
|
||||
jiffies_to_msecs(now - pool_ts) / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user