Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf
Cross merge bpf/master after 6.17-rc1. No conflict. Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
/config_data
|
||||
/kheaders.md5
|
||||
/kheaders-objlist
|
||||
/kheaders-srclist
|
||||
|
||||
+42
-5
@@ -159,11 +159,48 @@ filechk_cat = cat $<
|
||||
$(obj)/config_data: $(KCONFIG_CONFIG) FORCE
|
||||
$(call filechk,cat)
|
||||
|
||||
# kheaders_data.tar.xz
|
||||
$(obj)/kheaders.o: $(obj)/kheaders_data.tar.xz
|
||||
|
||||
quiet_cmd_genikh = CHK $(obj)/kheaders_data.tar.xz
|
||||
cmd_genikh = $(CONFIG_SHELL) $(srctree)/kernel/gen_kheaders.sh $@
|
||||
$(obj)/kheaders_data.tar.xz: FORCE
|
||||
$(call cmd,genikh)
|
||||
quiet_cmd_kheaders_data = GEN $@
|
||||
cmd_kheaders_data = "$<" "$@" "$(obj)/kheaders-srclist" "$(obj)/kheaders-objlist" "$(KBUILD_BUILD_TIMESTAMP)"
|
||||
cmd_kheaders_data_dep = cat $(depfile) >> $(dot-target).cmd; rm -f $(depfile)
|
||||
|
||||
clean-files := kheaders_data.tar.xz kheaders.md5
|
||||
define rule_kheaders_data
|
||||
$(call cmd_and_savecmd,kheaders_data)
|
||||
$(call cmd,kheaders_data_dep)
|
||||
endef
|
||||
|
||||
targets += kheaders_data.tar.xz
|
||||
$(obj)/kheaders_data.tar.xz: $(src)/gen_kheaders.sh $(obj)/kheaders-srclist $(obj)/kheaders-objlist $(obj)/kheaders.md5 FORCE
|
||||
$(call if_changed_rule,kheaders_data)
|
||||
|
||||
# generated headers in objtree
|
||||
#
|
||||
# include/generated/utsversion.h is ignored because it is generated
|
||||
# after gen_kheaders.sh is executed. (utsversion.h is unneeded for kheaders)
|
||||
filechk_kheaders_objlist = \
|
||||
for d in include "arch/$(SRCARCH)/include"; do \
|
||||
find "$${d}/generated" ! -path "include/generated/utsversion.h" -a -name "*.h" -print; \
|
||||
done
|
||||
|
||||
$(obj)/kheaders-objlist: FORCE
|
||||
$(call filechk,kheaders_objlist)
|
||||
|
||||
# non-generated headers in srctree
|
||||
filechk_kheaders_srclist = \
|
||||
for d in include "arch/$(SRCARCH)/include"; do \
|
||||
find "$(srctree)/$${d}" -path "$(srctree)/$${d}/generated" -prune -o -name "*.h" -print; \
|
||||
done
|
||||
|
||||
$(obj)/kheaders-srclist: FORCE
|
||||
$(call filechk,kheaders_srclist)
|
||||
|
||||
# Some files are symlinks. If symlinks are changed, kheaders_data.tar.xz should
|
||||
# be rebuilt.
|
||||
filechk_kheaders_md5sum = xargs -r -a $< stat -c %N | md5sum
|
||||
|
||||
$(obj)/kheaders.md5: $(obj)/kheaders-srclist FORCE
|
||||
$(call filechk,kheaders_md5sum)
|
||||
|
||||
clean-files := kheaders.md5 kheaders-srclist kheaders-objlist
|
||||
|
||||
@@ -23115,6 +23115,8 @@ static void free_states(struct bpf_verifier_env *env)
|
||||
|
||||
for (i = 0; i < env->scc_cnt; ++i) {
|
||||
info = env->scc_info[i];
|
||||
if (!info)
|
||||
continue;
|
||||
for (j = 0; j < info->num_visits; j++)
|
||||
free_backedges(&info->visits[j]);
|
||||
kvfree(info);
|
||||
@@ -24555,6 +24557,7 @@ dfs_continue:
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
env->scc_cnt = next_scc_id;
|
||||
exit:
|
||||
kvfree(stack);
|
||||
kvfree(pre);
|
||||
|
||||
@@ -1309,9 +1309,6 @@ static int takedown_cpu(unsigned int cpu)
|
||||
*/
|
||||
irq_lock_sparse();
|
||||
|
||||
/*
|
||||
* So now all preempt/rcu users must observe !cpu_active().
|
||||
*/
|
||||
err = stop_machine_cpuslocked(take_cpu_down, NULL, cpumask_of(cpu));
|
||||
if (err) {
|
||||
/* CPU refused to die */
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/sections.h>
|
||||
@@ -33,6 +34,11 @@
|
||||
/* Per cpu memory for storing cpu states in case of system crash. */
|
||||
note_buf_t __percpu *crash_notes;
|
||||
|
||||
/* time to wait for possible DMA to finish before starting the kdump kernel
|
||||
* when a CMA reservation is used
|
||||
*/
|
||||
#define CMA_DMA_TIMEOUT_SEC 10
|
||||
|
||||
#ifdef CONFIG_CRASH_DUMP
|
||||
|
||||
int kimage_crash_copy_vmcoreinfo(struct kimage *image)
|
||||
@@ -97,6 +103,14 @@ int kexec_crash_loaded(void)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kexec_crash_loaded);
|
||||
|
||||
static void crash_cma_clear_pending_dma(void)
|
||||
{
|
||||
if (!crashk_cma_cnt)
|
||||
return;
|
||||
|
||||
mdelay(CMA_DMA_TIMEOUT_SEC * 1000);
|
||||
}
|
||||
|
||||
/*
|
||||
* No panic_cpu check version of crash_kexec(). This function is called
|
||||
* only when panic_cpu holds the current CPU number; this is the only CPU
|
||||
@@ -119,6 +133,7 @@ void __noclone __crash_kexec(struct pt_regs *regs)
|
||||
crash_setup_regs(&fixed_regs, regs);
|
||||
crash_save_vmcoreinfo();
|
||||
machine_crash_shutdown(&fixed_regs);
|
||||
crash_cma_clear_pending_dma();
|
||||
machine_kexec(kexec_crash_image);
|
||||
}
|
||||
kexec_unlock();
|
||||
|
||||
+66
-2
@@ -14,6 +14,8 @@
|
||||
#include <linux/cpuhotplug.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/kmemleak.h>
|
||||
#include <linux/cma.h>
|
||||
#include <linux/crash_reserve.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/sections.h>
|
||||
@@ -172,17 +174,19 @@ static int __init parse_crashkernel_simple(char *cmdline,
|
||||
|
||||
#define SUFFIX_HIGH 0
|
||||
#define SUFFIX_LOW 1
|
||||
#define SUFFIX_NULL 2
|
||||
#define SUFFIX_CMA 2
|
||||
#define SUFFIX_NULL 3
|
||||
static __initdata char *suffix_tbl[] = {
|
||||
[SUFFIX_HIGH] = ",high",
|
||||
[SUFFIX_LOW] = ",low",
|
||||
[SUFFIX_CMA] = ",cma",
|
||||
[SUFFIX_NULL] = NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* That function parses "suffix" crashkernel command lines like
|
||||
*
|
||||
* crashkernel=size,[high|low]
|
||||
* crashkernel=size,[high|low|cma]
|
||||
*
|
||||
* It returns 0 on success and -EINVAL on failure.
|
||||
*/
|
||||
@@ -298,9 +302,11 @@ int __init parse_crashkernel(char *cmdline,
|
||||
unsigned long long *crash_size,
|
||||
unsigned long long *crash_base,
|
||||
unsigned long long *low_size,
|
||||
unsigned long long *cma_size,
|
||||
bool *high)
|
||||
{
|
||||
int ret;
|
||||
unsigned long long __always_unused cma_base;
|
||||
|
||||
/* crashkernel=X[@offset] */
|
||||
ret = __parse_crashkernel(cmdline, system_ram, crash_size,
|
||||
@@ -331,6 +337,14 @@ int __init parse_crashkernel(char *cmdline,
|
||||
|
||||
*high = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* optional CMA reservation
|
||||
* cma_base is ignored
|
||||
*/
|
||||
if (cma_size)
|
||||
__parse_crashkernel(cmdline, 0, cma_size,
|
||||
&cma_base, suffix_tbl[SUFFIX_CMA]);
|
||||
#endif
|
||||
if (!*crash_size)
|
||||
ret = -EINVAL;
|
||||
@@ -457,6 +471,56 @@ retry:
|
||||
#endif
|
||||
}
|
||||
|
||||
struct range crashk_cma_ranges[CRASHKERNEL_CMA_RANGES_MAX];
|
||||
#ifdef CRASHKERNEL_CMA
|
||||
int crashk_cma_cnt;
|
||||
void __init reserve_crashkernel_cma(unsigned long long cma_size)
|
||||
{
|
||||
unsigned long long request_size = roundup(cma_size, PAGE_SIZE);
|
||||
unsigned long long reserved_size = 0;
|
||||
|
||||
if (!cma_size)
|
||||
return;
|
||||
|
||||
while (cma_size > reserved_size &&
|
||||
crashk_cma_cnt < CRASHKERNEL_CMA_RANGES_MAX) {
|
||||
|
||||
struct cma *res;
|
||||
|
||||
if (cma_declare_contiguous(0, request_size, 0, 0, 0, false,
|
||||
"crashkernel", &res)) {
|
||||
/* reservation failed, try half-sized blocks */
|
||||
if (request_size <= PAGE_SIZE)
|
||||
break;
|
||||
|
||||
request_size = roundup(request_size / 2, PAGE_SIZE);
|
||||
continue;
|
||||
}
|
||||
|
||||
crashk_cma_ranges[crashk_cma_cnt].start = cma_get_base(res);
|
||||
crashk_cma_ranges[crashk_cma_cnt].end =
|
||||
crashk_cma_ranges[crashk_cma_cnt].start +
|
||||
cma_get_size(res) - 1;
|
||||
++crashk_cma_cnt;
|
||||
reserved_size += request_size;
|
||||
}
|
||||
|
||||
if (cma_size > reserved_size)
|
||||
pr_warn("crashkernel CMA reservation failed: %lld MB requested, %lld MB reserved in %d ranges\n",
|
||||
cma_size >> 20, reserved_size >> 20, crashk_cma_cnt);
|
||||
else
|
||||
pr_info("crashkernel CMA reserved: %lld MB in %d ranges\n",
|
||||
reserved_size >> 20, crashk_cma_cnt);
|
||||
}
|
||||
|
||||
#else /* CRASHKERNEL_CMA */
|
||||
void __init reserve_crashkernel_cma(unsigned long long cma_size)
|
||||
{
|
||||
if (cma_size)
|
||||
pr_warn("crashkernel CMA reservation not supported\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_ARCH_ADD_CRASH_RES_TO_IOMEM_EARLY
|
||||
static __init int insert_crashkernel_resources(void)
|
||||
{
|
||||
|
||||
+28
-8
@@ -6842,10 +6842,20 @@ static vm_fault_t perf_mmap_pfn_mkwrite(struct vm_fault *vmf)
|
||||
return vmf->pgoff == 0 ? 0 : VM_FAULT_SIGBUS;
|
||||
}
|
||||
|
||||
static int perf_mmap_may_split(struct vm_area_struct *vma, unsigned long addr)
|
||||
{
|
||||
/*
|
||||
* Forbid splitting perf mappings to prevent refcount leaks due to
|
||||
* the resulting non-matching offsets and sizes. See open()/close().
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct perf_mmap_vmops = {
|
||||
.open = perf_mmap_open,
|
||||
.close = perf_mmap_close, /* non mergeable */
|
||||
.pfn_mkwrite = perf_mmap_pfn_mkwrite,
|
||||
.may_split = perf_mmap_may_split,
|
||||
};
|
||||
|
||||
static int map_range(struct perf_buffer *rb, struct vm_area_struct *vma)
|
||||
@@ -7051,8 +7061,6 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
ret = 0;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
atomic_set(&rb->aux_mmap_count, 1);
|
||||
}
|
||||
|
||||
user_lock_limit = sysctl_perf_event_mlock >> (PAGE_SHIFT - 10);
|
||||
@@ -7115,15 +7123,16 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
perf_event_update_time(event);
|
||||
perf_event_init_userpage(event);
|
||||
perf_event_update_userpage(event);
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = rb_alloc_aux(rb, event, vma->vm_pgoff, nr_pages,
|
||||
event->attr.aux_watermark, flags);
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
atomic_set(&rb->aux_mmap_count, 1);
|
||||
rb->aux_mmap_locked = extra;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
unlock:
|
||||
if (!ret) {
|
||||
atomic_long_add(user_extra, &user->locked_vm);
|
||||
@@ -7131,6 +7140,7 @@ unlock:
|
||||
|
||||
atomic_inc(&event->mmap_count);
|
||||
} else if (rb) {
|
||||
/* AUX allocation failed */
|
||||
atomic_dec(&rb->mmap_count);
|
||||
}
|
||||
aux_unlock:
|
||||
@@ -7138,6 +7148,9 @@ aux_unlock:
|
||||
mutex_unlock(aux_mutex);
|
||||
mutex_unlock(&event->mmap_mutex);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Since pinned accounting is per vm we cannot allow fork() to copy our
|
||||
* vma.
|
||||
@@ -7145,13 +7158,20 @@ aux_unlock:
|
||||
vm_flags_set(vma, VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP);
|
||||
vma->vm_ops = &perf_mmap_vmops;
|
||||
|
||||
if (!ret)
|
||||
ret = map_range(rb, vma);
|
||||
|
||||
mapped = get_mapped(event, event_mapped);
|
||||
if (mapped)
|
||||
mapped(event, vma->vm_mm);
|
||||
|
||||
/*
|
||||
* Try to map it into the page table. On fail, invoke
|
||||
* perf_mmap_close() to undo the above, as the callsite expects
|
||||
* full cleanup in this case and therefore does not invoke
|
||||
* vmops::close().
|
||||
*/
|
||||
ret = map_range(rb, vma);
|
||||
if (ret)
|
||||
perf_mmap_close(vma);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -580,8 +580,8 @@ retry:
|
||||
|
||||
out:
|
||||
/* Revert back reference counter if instruction update failed. */
|
||||
if (ret < 0 && is_register && ref_ctr_updated)
|
||||
update_ref_ctr(uprobe, mm, -1);
|
||||
if (ret < 0 && ref_ctr_updated)
|
||||
update_ref_ctr(uprobe, mm, is_register ? -1 : 1);
|
||||
|
||||
/* try collapse pmd for compound page */
|
||||
if (ret > 0)
|
||||
|
||||
+1
-6
@@ -693,12 +693,7 @@ static void reparent_leader(struct task_struct *father, struct task_struct *p,
|
||||
}
|
||||
|
||||
/*
|
||||
* This does two things:
|
||||
*
|
||||
* A. Make init inherit all the child processes
|
||||
* B. Check to see if any process groups have become orphaned
|
||||
* as a result of our exiting, and if they have any stopped
|
||||
* jobs, send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2)
|
||||
* Make init inherit all the child processes
|
||||
*/
|
||||
static void forget_original_parent(struct task_struct *father,
|
||||
struct list_head *dead)
|
||||
|
||||
+54
-52
@@ -189,33 +189,33 @@ static inline void free_task_struct(struct task_struct *tsk)
|
||||
kmem_cache_free(task_struct_cachep, tsk);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate pages if THREAD_SIZE is >= PAGE_SIZE, otherwise use a
|
||||
* kmemcache based allocator.
|
||||
*/
|
||||
# if THREAD_SIZE >= PAGE_SIZE || defined(CONFIG_VMAP_STACK)
|
||||
|
||||
# ifdef CONFIG_VMAP_STACK
|
||||
#ifdef CONFIG_VMAP_STACK
|
||||
/*
|
||||
* vmalloc() is a bit slow, and calling vfree() enough times will force a TLB
|
||||
* flush. Try to minimize the number of calls by caching stacks.
|
||||
*/
|
||||
#define NR_CACHED_STACKS 2
|
||||
static DEFINE_PER_CPU(struct vm_struct *, cached_stacks[NR_CACHED_STACKS]);
|
||||
/*
|
||||
* Allocated stacks are cached and later reused by new threads, so memcg
|
||||
* accounting is performed by the code assigning/releasing stacks to tasks.
|
||||
* We need a zeroed memory without __GFP_ACCOUNT.
|
||||
*/
|
||||
#define GFP_VMAP_STACK (GFP_KERNEL | __GFP_ZERO)
|
||||
|
||||
struct vm_stack {
|
||||
struct rcu_head rcu;
|
||||
struct vm_struct *stack_vm_area;
|
||||
};
|
||||
|
||||
static bool try_release_thread_stack_to_cache(struct vm_struct *vm)
|
||||
static bool try_release_thread_stack_to_cache(struct vm_struct *vm_area)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < NR_CACHED_STACKS; i++) {
|
||||
struct vm_struct *tmp = NULL;
|
||||
|
||||
if (this_cpu_try_cmpxchg(cached_stacks[i], &tmp, vm))
|
||||
if (this_cpu_try_cmpxchg(cached_stacks[i], &tmp, vm_area))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -224,11 +224,12 @@ static bool try_release_thread_stack_to_cache(struct vm_struct *vm)
|
||||
static void thread_stack_free_rcu(struct rcu_head *rh)
|
||||
{
|
||||
struct vm_stack *vm_stack = container_of(rh, struct vm_stack, rcu);
|
||||
struct vm_struct *vm_area = vm_stack->stack_vm_area;
|
||||
|
||||
if (try_release_thread_stack_to_cache(vm_stack->stack_vm_area))
|
||||
return;
|
||||
|
||||
vfree(vm_stack);
|
||||
vfree(vm_area->addr);
|
||||
}
|
||||
|
||||
static void thread_stack_delayed_free(struct task_struct *tsk)
|
||||
@@ -241,32 +242,32 @@ static void thread_stack_delayed_free(struct task_struct *tsk)
|
||||
|
||||
static int free_vm_stack_cache(unsigned int cpu)
|
||||
{
|
||||
struct vm_struct **cached_vm_stacks = per_cpu_ptr(cached_stacks, cpu);
|
||||
struct vm_struct **cached_vm_stack_areas = per_cpu_ptr(cached_stacks, cpu);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NR_CACHED_STACKS; i++) {
|
||||
struct vm_struct *vm_stack = cached_vm_stacks[i];
|
||||
struct vm_struct *vm_area = cached_vm_stack_areas[i];
|
||||
|
||||
if (!vm_stack)
|
||||
if (!vm_area)
|
||||
continue;
|
||||
|
||||
vfree(vm_stack->addr);
|
||||
cached_vm_stacks[i] = NULL;
|
||||
vfree(vm_area->addr);
|
||||
cached_vm_stack_areas[i] = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int memcg_charge_kernel_stack(struct vm_struct *vm)
|
||||
static int memcg_charge_kernel_stack(struct vm_struct *vm_area)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
int nr_charged = 0;
|
||||
|
||||
BUG_ON(vm->nr_pages != THREAD_SIZE / PAGE_SIZE);
|
||||
BUG_ON(vm_area->nr_pages != THREAD_SIZE / PAGE_SIZE);
|
||||
|
||||
for (i = 0; i < THREAD_SIZE / PAGE_SIZE; i++) {
|
||||
ret = memcg_kmem_charge_page(vm->pages[i], GFP_KERNEL, 0);
|
||||
ret = memcg_kmem_charge_page(vm_area->pages[i], GFP_KERNEL, 0);
|
||||
if (ret)
|
||||
goto err;
|
||||
nr_charged++;
|
||||
@@ -274,55 +275,47 @@ static int memcg_charge_kernel_stack(struct vm_struct *vm)
|
||||
return 0;
|
||||
err:
|
||||
for (i = 0; i < nr_charged; i++)
|
||||
memcg_kmem_uncharge_page(vm->pages[i], 0);
|
||||
memcg_kmem_uncharge_page(vm_area->pages[i], 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int alloc_thread_stack_node(struct task_struct *tsk, int node)
|
||||
{
|
||||
struct vm_struct *vm;
|
||||
struct vm_struct *vm_area;
|
||||
void *stack;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NR_CACHED_STACKS; i++) {
|
||||
struct vm_struct *s;
|
||||
|
||||
s = this_cpu_xchg(cached_stacks[i], NULL);
|
||||
|
||||
if (!s)
|
||||
vm_area = this_cpu_xchg(cached_stacks[i], NULL);
|
||||
if (!vm_area)
|
||||
continue;
|
||||
|
||||
/* Reset stack metadata. */
|
||||
kasan_unpoison_range(s->addr, THREAD_SIZE);
|
||||
kasan_unpoison_range(vm_area->addr, THREAD_SIZE);
|
||||
|
||||
stack = kasan_reset_tag(s->addr);
|
||||
stack = kasan_reset_tag(vm_area->addr);
|
||||
|
||||
/* Clear stale pointers from reused stack. */
|
||||
memset(stack, 0, THREAD_SIZE);
|
||||
|
||||
if (memcg_charge_kernel_stack(s)) {
|
||||
vfree(s->addr);
|
||||
if (memcg_charge_kernel_stack(vm_area)) {
|
||||
vfree(vm_area->addr);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tsk->stack_vm_area = s;
|
||||
tsk->stack_vm_area = vm_area;
|
||||
tsk->stack = stack;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocated stacks are cached and later reused by new threads,
|
||||
* so memcg accounting is performed manually on assigning/releasing
|
||||
* stacks to tasks. Drop __GFP_ACCOUNT.
|
||||
*/
|
||||
stack = __vmalloc_node(THREAD_SIZE, THREAD_ALIGN,
|
||||
THREADINFO_GFP & ~__GFP_ACCOUNT,
|
||||
GFP_VMAP_STACK,
|
||||
node, __builtin_return_address(0));
|
||||
if (!stack)
|
||||
return -ENOMEM;
|
||||
|
||||
vm = find_vm_area(stack);
|
||||
if (memcg_charge_kernel_stack(vm)) {
|
||||
vm_area = find_vm_area(stack);
|
||||
if (memcg_charge_kernel_stack(vm_area)) {
|
||||
vfree(stack);
|
||||
return -ENOMEM;
|
||||
}
|
||||
@@ -331,7 +324,7 @@ static int alloc_thread_stack_node(struct task_struct *tsk, int node)
|
||||
* free_thread_stack() can be called in interrupt context,
|
||||
* so cache the vm_struct.
|
||||
*/
|
||||
tsk->stack_vm_area = vm;
|
||||
tsk->stack_vm_area = vm_area;
|
||||
stack = kasan_reset_tag(stack);
|
||||
tsk->stack = stack;
|
||||
return 0;
|
||||
@@ -346,7 +339,13 @@ static void free_thread_stack(struct task_struct *tsk)
|
||||
tsk->stack_vm_area = NULL;
|
||||
}
|
||||
|
||||
# else /* !CONFIG_VMAP_STACK */
|
||||
#else /* !CONFIG_VMAP_STACK */
|
||||
|
||||
/*
|
||||
* Allocate pages if THREAD_SIZE is >= PAGE_SIZE, otherwise use a
|
||||
* kmemcache based allocator.
|
||||
*/
|
||||
#if THREAD_SIZE >= PAGE_SIZE
|
||||
|
||||
static void thread_stack_free_rcu(struct rcu_head *rh)
|
||||
{
|
||||
@@ -378,8 +377,7 @@ static void free_thread_stack(struct task_struct *tsk)
|
||||
tsk->stack = NULL;
|
||||
}
|
||||
|
||||
# endif /* CONFIG_VMAP_STACK */
|
||||
# else /* !(THREAD_SIZE >= PAGE_SIZE || defined(CONFIG_VMAP_STACK)) */
|
||||
#else /* !(THREAD_SIZE >= PAGE_SIZE) */
|
||||
|
||||
static struct kmem_cache *thread_stack_cache;
|
||||
|
||||
@@ -418,7 +416,8 @@ void thread_stack_cache_init(void)
|
||||
BUG_ON(thread_stack_cache == NULL);
|
||||
}
|
||||
|
||||
# endif /* THREAD_SIZE >= PAGE_SIZE || defined(CONFIG_VMAP_STACK) */
|
||||
#endif /* THREAD_SIZE >= PAGE_SIZE */
|
||||
#endif /* CONFIG_VMAP_STACK */
|
||||
|
||||
/* SLAB cache for signal_struct structures (tsk->signal) */
|
||||
static struct kmem_cache *signal_cachep;
|
||||
@@ -438,11 +437,11 @@ static struct kmem_cache *mm_cachep;
|
||||
static void account_kernel_stack(struct task_struct *tsk, int account)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_VMAP_STACK)) {
|
||||
struct vm_struct *vm = task_stack_vm_area(tsk);
|
||||
struct vm_struct *vm_area = task_stack_vm_area(tsk);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < THREAD_SIZE / PAGE_SIZE; i++)
|
||||
mod_lruvec_page_state(vm->pages[i], NR_KERNEL_STACK_KB,
|
||||
mod_lruvec_page_state(vm_area->pages[i], NR_KERNEL_STACK_KB,
|
||||
account * (PAGE_SIZE / 1024));
|
||||
} else {
|
||||
void *stack = task_stack_page(tsk);
|
||||
@@ -458,12 +457,12 @@ void exit_task_stack_account(struct task_struct *tsk)
|
||||
account_kernel_stack(tsk, -1);
|
||||
|
||||
if (IS_ENABLED(CONFIG_VMAP_STACK)) {
|
||||
struct vm_struct *vm;
|
||||
struct vm_struct *vm_area;
|
||||
int i;
|
||||
|
||||
vm = task_stack_vm_area(tsk);
|
||||
vm_area = task_stack_vm_area(tsk);
|
||||
for (i = 0; i < THREAD_SIZE / PAGE_SIZE; i++)
|
||||
memcg_kmem_uncharge_page(vm->pages[i], 0);
|
||||
memcg_kmem_uncharge_page(vm_area->pages[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,9 +585,12 @@ static void check_mm(struct mm_struct *mm)
|
||||
for (i = 0; i < NR_MM_COUNTERS; i++) {
|
||||
long x = percpu_counter_sum(&mm->rss_stat[i]);
|
||||
|
||||
if (unlikely(x))
|
||||
pr_alert("BUG: Bad rss-counter state mm:%p type:%s val:%ld\n",
|
||||
mm, resident_page_types[i], x);
|
||||
if (unlikely(x)) {
|
||||
pr_alert("BUG: Bad rss-counter state mm:%p type:%s val:%ld Comm:%s Pid:%d\n",
|
||||
mm, resident_page_types[i], x,
|
||||
current->comm,
|
||||
task_pid_nr(current));
|
||||
}
|
||||
}
|
||||
|
||||
if (mm_pgtables_bytes(mm))
|
||||
@@ -687,6 +689,7 @@ void __mmdrop(struct mm_struct *mm)
|
||||
mm_pasid_drop(mm);
|
||||
mm_destroy_cid(mm);
|
||||
percpu_counter_destroy_many(mm->rss_stat, NR_MM_COUNTERS);
|
||||
futex_hash_free(mm);
|
||||
|
||||
free_mm(mm);
|
||||
}
|
||||
@@ -1135,7 +1138,6 @@ static inline void __mmput(struct mm_struct *mm)
|
||||
if (mm->binfmt)
|
||||
module_put(mm->binfmt->module);
|
||||
lru_gen_del_mm(mm);
|
||||
futex_hash_free(mm);
|
||||
mmdrop(mm);
|
||||
}
|
||||
|
||||
|
||||
+22
-71
@@ -4,79 +4,34 @@
|
||||
# This script generates an archive consisting of kernel headers
|
||||
# for CONFIG_IKHEADERS.
|
||||
set -e
|
||||
sfile="$(readlink -f "$0")"
|
||||
outdir="$(pwd)"
|
||||
tarfile=$1
|
||||
tmpdir=$outdir/${tarfile%/*}/.tmp_dir
|
||||
srclist=$2
|
||||
objlist=$3
|
||||
timestamp=$4
|
||||
|
||||
dir_list="
|
||||
include/
|
||||
arch/$SRCARCH/include/
|
||||
"
|
||||
dir=$(dirname "${tarfile}")
|
||||
tmpdir=${dir}/.tmp_dir
|
||||
depfile=${dir}/.$(basename "${tarfile}").d
|
||||
|
||||
# Support incremental builds by skipping archive generation
|
||||
# if timestamps of files being archived are not changed.
|
||||
# generate dependency list.
|
||||
{
|
||||
echo
|
||||
echo "deps_${tarfile} := \\"
|
||||
sed 's:\(.*\): \1 \\:' "${srclist}"
|
||||
sed -n '/^include\/generated\/autoconf\.h$/!s:\(.*\): \1 \\:p' "${objlist}"
|
||||
echo
|
||||
echo "${tarfile}: \$(deps_${tarfile})"
|
||||
echo
|
||||
echo "\$(deps_${tarfile}):"
|
||||
|
||||
# This block is useful for debugging the incremental builds.
|
||||
# Uncomment it for debugging.
|
||||
# if [ ! -f /tmp/iter ]; then iter=1; echo 1 > /tmp/iter;
|
||||
# else iter=$(($(cat /tmp/iter) + 1)); echo $iter > /tmp/iter; fi
|
||||
# find $all_dirs -name "*.h" | xargs ls -l > /tmp/ls-$iter
|
||||
|
||||
all_dirs=
|
||||
if [ "$building_out_of_srctree" ]; then
|
||||
for d in $dir_list; do
|
||||
all_dirs="$all_dirs $srctree/$d"
|
||||
done
|
||||
fi
|
||||
all_dirs="$all_dirs $dir_list"
|
||||
|
||||
# include/generated/utsversion.h is ignored because it is generated after this
|
||||
# script is executed. (utsversion.h is unneeded for kheaders)
|
||||
#
|
||||
# When Kconfig regenerates include/generated/autoconf.h, its timestamp is
|
||||
# updated, but the contents might be still the same. When any CONFIG option is
|
||||
# changed, Kconfig touches the corresponding timestamp file include/config/*.
|
||||
# Hence, the md5sum detects the configuration change anyway. We do not need to
|
||||
# check include/generated/autoconf.h explicitly.
|
||||
#
|
||||
# Ignore them for md5 calculation to avoid pointless regeneration.
|
||||
headers_md5="$(find $all_dirs -name "*.h" -a \
|
||||
! -path include/generated/utsversion.h -a \
|
||||
! -path include/generated/autoconf.h |
|
||||
xargs ls -l | md5sum | cut -d ' ' -f1)"
|
||||
|
||||
# Any changes to this script will also cause a rebuild of the archive.
|
||||
this_file_md5="$(ls -l $sfile | md5sum | cut -d ' ' -f1)"
|
||||
if [ -f $tarfile ]; then tarfile_md5="$(md5sum $tarfile | cut -d ' ' -f1)"; fi
|
||||
if [ -f kernel/kheaders.md5 ] &&
|
||||
[ "$(head -n 1 kernel/kheaders.md5)" = "$headers_md5" ] &&
|
||||
[ "$(head -n 2 kernel/kheaders.md5 | tail -n 1)" = "$this_file_md5" ] &&
|
||||
[ "$(tail -n 1 kernel/kheaders.md5)" = "$tarfile_md5" ]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
echo " GEN $tarfile"
|
||||
} > "${depfile}"
|
||||
|
||||
rm -rf "${tmpdir}"
|
||||
mkdir "${tmpdir}"
|
||||
|
||||
if [ "$building_out_of_srctree" ]; then
|
||||
(
|
||||
cd $srctree
|
||||
for f in $dir_list
|
||||
do find "$f" -name "*.h";
|
||||
done | tar -c -f - -T - | tar -xf - -C "${tmpdir}"
|
||||
)
|
||||
fi
|
||||
|
||||
for f in $dir_list;
|
||||
do find "$f" -name "*.h";
|
||||
done | tar -c -f - -T - | tar -xf - -C "${tmpdir}"
|
||||
|
||||
# Always exclude include/generated/utsversion.h
|
||||
# Otherwise, the contents of the tarball may vary depending on the build steps.
|
||||
rm -f "${tmpdir}/include/generated/utsversion.h"
|
||||
# shellcheck disable=SC2154 # srctree is passed as an env variable
|
||||
sed "s:^${srctree}/::" "${srclist}" | ${TAR} -c -f - -C "${srctree}" -T - | ${TAR} -xf - -C "${tmpdir}"
|
||||
${TAR} -c -f - -T "${objlist}" | ${TAR} -xf - -C "${tmpdir}"
|
||||
|
||||
# Remove comments except SDPX lines
|
||||
# Use a temporary file to store directory contents to prevent find/xargs from
|
||||
@@ -88,12 +43,8 @@ xargs -0 -P8 -n1 \
|
||||
rm -f "${tmpdir}.contents.txt"
|
||||
|
||||
# Create archive and try to normalize metadata for reproducibility.
|
||||
tar "${KBUILD_BUILD_TIMESTAMP:+--mtime=$KBUILD_BUILD_TIMESTAMP}" \
|
||||
${TAR} "${timestamp:+--mtime=$timestamp}" \
|
||||
--owner=0 --group=0 --sort=name --numeric-owner --mode=u=rw,go=r,a+X \
|
||||
-I $XZ -cf $tarfile -C "${tmpdir}/" . > /dev/null
|
||||
|
||||
echo $headers_md5 > kernel/kheaders.md5
|
||||
echo "$this_file_md5" >> kernel/kheaders.md5
|
||||
echo "$(md5sum $tarfile | cut -d ' ' -f1)" >> kernel/kheaders.md5
|
||||
-I "${XZ}" -cf "${tarfile}" -C "${tmpdir}/" . > /dev/null
|
||||
|
||||
rm -rf "${tmpdir}"
|
||||
|
||||
+25
-4
@@ -23,6 +23,7 @@
|
||||
#include <linux/sched/debug.h>
|
||||
#include <linux/sched/sysctl.h>
|
||||
#include <linux/hung_task.h>
|
||||
#include <linux/rwsem.h>
|
||||
|
||||
#include <trace/events/sched.h>
|
||||
|
||||
@@ -100,6 +101,7 @@ static void debug_show_blocker(struct task_struct *task)
|
||||
{
|
||||
struct task_struct *g, *t;
|
||||
unsigned long owner, blocker, blocker_type;
|
||||
const char *rwsem_blocked_by, *rwsem_blocked_as;
|
||||
|
||||
RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "No rcu lock held");
|
||||
|
||||
@@ -111,12 +113,20 @@ static void debug_show_blocker(struct task_struct *task)
|
||||
|
||||
switch (blocker_type) {
|
||||
case BLOCKER_TYPE_MUTEX:
|
||||
owner = mutex_get_owner(
|
||||
(struct mutex *)hung_task_blocker_to_lock(blocker));
|
||||
owner = mutex_get_owner(hung_task_blocker_to_lock(blocker));
|
||||
break;
|
||||
case BLOCKER_TYPE_SEM:
|
||||
owner = sem_last_holder(
|
||||
(struct semaphore *)hung_task_blocker_to_lock(blocker));
|
||||
owner = sem_last_holder(hung_task_blocker_to_lock(blocker));
|
||||
break;
|
||||
case BLOCKER_TYPE_RWSEM_READER:
|
||||
case BLOCKER_TYPE_RWSEM_WRITER:
|
||||
owner = (unsigned long)rwsem_owner(
|
||||
hung_task_blocker_to_lock(blocker));
|
||||
rwsem_blocked_as = (blocker_type == BLOCKER_TYPE_RWSEM_READER) ?
|
||||
"reader" : "writer";
|
||||
rwsem_blocked_by = is_rwsem_reader_owned(
|
||||
hung_task_blocker_to_lock(blocker)) ?
|
||||
"reader" : "writer";
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
@@ -134,6 +144,11 @@ static void debug_show_blocker(struct task_struct *task)
|
||||
pr_err("INFO: task %s:%d is blocked on a semaphore, but the last holder is not found.\n",
|
||||
task->comm, task->pid);
|
||||
break;
|
||||
case BLOCKER_TYPE_RWSEM_READER:
|
||||
case BLOCKER_TYPE_RWSEM_WRITER:
|
||||
pr_err("INFO: task %s:%d is blocked on an rw-semaphore, but the owner is not found.\n",
|
||||
task->comm, task->pid);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -152,6 +167,12 @@ static void debug_show_blocker(struct task_struct *task)
|
||||
pr_err("INFO: task %s:%d blocked on a semaphore likely last held by task %s:%d\n",
|
||||
task->comm, task->pid, t->comm, t->pid);
|
||||
break;
|
||||
case BLOCKER_TYPE_RWSEM_READER:
|
||||
case BLOCKER_TYPE_RWSEM_WRITER:
|
||||
pr_err("INFO: task %s:%d <%s> blocked on an rw-semaphore likely owned by task %s:%d <%s>\n",
|
||||
task->comm, task->pid, rwsem_blocked_as, t->comm,
|
||||
t->pid, rwsem_blocked_by);
|
||||
break;
|
||||
}
|
||||
sched_show_task(t);
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1+
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/interrupt.h>
|
||||
@@ -134,7 +135,8 @@ static void irq_shutdown_depth_test(struct kunit *test)
|
||||
disable_irq(virq);
|
||||
KUNIT_EXPECT_EQ(test, desc->depth, 1);
|
||||
|
||||
irq_shutdown_and_deactivate(desc);
|
||||
scoped_guard(raw_spinlock_irqsave, &desc->lock)
|
||||
irq_shutdown_and_deactivate(desc);
|
||||
|
||||
KUNIT_EXPECT_FALSE(test, irqd_is_activated(data));
|
||||
KUNIT_EXPECT_FALSE(test, irqd_is_started(data));
|
||||
|
||||
+1
-1
@@ -552,7 +552,7 @@ static int kcov_get_mode(unsigned long arg)
|
||||
|
||||
/*
|
||||
* Fault in a lazily-faulted vmalloc area before it can be used by
|
||||
* __santizer_cov_trace_pc(), to avoid recursion issues if any code on the
|
||||
* __sanitizer_cov_trace_pc(), to avoid recursion issues if any code on the
|
||||
* vmalloc fault handling path is instrumented.
|
||||
*/
|
||||
static void kcov_fault_in_area(struct kcov *kcov)
|
||||
|
||||
+1
-1
@@ -152,7 +152,7 @@ static int do_kexec_load(unsigned long entry, unsigned long nr_segments,
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < nr_segments; i++) {
|
||||
ret = kimage_load_segment(image, &image->segment[i]);
|
||||
ret = kimage_load_segment(image, i);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
+92
-8
@@ -40,6 +40,7 @@
|
||||
#include <linux/hugetlb.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/kmsg_dump.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/sections.h>
|
||||
@@ -553,6 +554,24 @@ static void kimage_free_entry(kimage_entry_t entry)
|
||||
kimage_free_pages(page);
|
||||
}
|
||||
|
||||
static void kimage_free_cma(struct kimage *image)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < image->nr_segments; i++) {
|
||||
struct page *cma = image->segment_cma[i];
|
||||
u32 nr_pages = image->segment[i].memsz >> PAGE_SHIFT;
|
||||
|
||||
if (!cma)
|
||||
continue;
|
||||
|
||||
arch_kexec_pre_free_pages(page_address(cma), nr_pages);
|
||||
dma_release_from_contiguous(NULL, cma, nr_pages);
|
||||
image->segment_cma[i] = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void kimage_free(struct kimage *image)
|
||||
{
|
||||
kimage_entry_t *ptr, entry;
|
||||
@@ -591,6 +610,9 @@ void kimage_free(struct kimage *image)
|
||||
/* Free the kexec control pages... */
|
||||
kimage_free_page_list(&image->control_pages);
|
||||
|
||||
/* Free CMA allocations */
|
||||
kimage_free_cma(image);
|
||||
|
||||
/*
|
||||
* Free up any temporary buffers allocated. This might hit if
|
||||
* error occurred much later after buffer allocation.
|
||||
@@ -716,9 +738,69 @@ static struct page *kimage_alloc_page(struct kimage *image,
|
||||
return page;
|
||||
}
|
||||
|
||||
static int kimage_load_normal_segment(struct kimage *image,
|
||||
struct kexec_segment *segment)
|
||||
static int kimage_load_cma_segment(struct kimage *image, int idx)
|
||||
{
|
||||
struct kexec_segment *segment = &image->segment[idx];
|
||||
struct page *cma = image->segment_cma[idx];
|
||||
char *ptr = page_address(cma);
|
||||
unsigned long maddr;
|
||||
size_t ubytes, mbytes;
|
||||
int result = 0;
|
||||
unsigned char __user *buf = NULL;
|
||||
unsigned char *kbuf = NULL;
|
||||
|
||||
if (image->file_mode)
|
||||
kbuf = segment->kbuf;
|
||||
else
|
||||
buf = segment->buf;
|
||||
ubytes = segment->bufsz;
|
||||
mbytes = segment->memsz;
|
||||
maddr = segment->mem;
|
||||
|
||||
/* Then copy from source buffer to the CMA one */
|
||||
while (mbytes) {
|
||||
size_t uchunk, mchunk;
|
||||
|
||||
ptr += maddr & ~PAGE_MASK;
|
||||
mchunk = min_t(size_t, mbytes,
|
||||
PAGE_SIZE - (maddr & ~PAGE_MASK));
|
||||
uchunk = min(ubytes, mchunk);
|
||||
|
||||
if (uchunk) {
|
||||
/* For file based kexec, source pages are in kernel memory */
|
||||
if (image->file_mode)
|
||||
memcpy(ptr, kbuf, uchunk);
|
||||
else
|
||||
result = copy_from_user(ptr, buf, uchunk);
|
||||
ubytes -= uchunk;
|
||||
if (image->file_mode)
|
||||
kbuf += uchunk;
|
||||
else
|
||||
buf += uchunk;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
result = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ptr += mchunk;
|
||||
maddr += mchunk;
|
||||
mbytes -= mchunk;
|
||||
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
/* Clear any remainder */
|
||||
memset(ptr, 0, mbytes);
|
||||
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int kimage_load_normal_segment(struct kimage *image, int idx)
|
||||
{
|
||||
struct kexec_segment *segment = &image->segment[idx];
|
||||
unsigned long maddr;
|
||||
size_t ubytes, mbytes;
|
||||
int result;
|
||||
@@ -733,6 +815,9 @@ static int kimage_load_normal_segment(struct kimage *image,
|
||||
mbytes = segment->memsz;
|
||||
maddr = segment->mem;
|
||||
|
||||
if (image->segment_cma[idx])
|
||||
return kimage_load_cma_segment(image, idx);
|
||||
|
||||
result = kimage_set_destination(image, maddr);
|
||||
if (result < 0)
|
||||
goto out;
|
||||
@@ -787,13 +872,13 @@ out:
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CRASH_DUMP
|
||||
static int kimage_load_crash_segment(struct kimage *image,
|
||||
struct kexec_segment *segment)
|
||||
static int kimage_load_crash_segment(struct kimage *image, int idx)
|
||||
{
|
||||
/* For crash dumps kernels we simply copy the data from
|
||||
* user space to it's destination.
|
||||
* We do things a page at a time for the sake of kmap.
|
||||
*/
|
||||
struct kexec_segment *segment = &image->segment[idx];
|
||||
unsigned long maddr;
|
||||
size_t ubytes, mbytes;
|
||||
int result;
|
||||
@@ -858,18 +943,17 @@ out:
|
||||
}
|
||||
#endif
|
||||
|
||||
int kimage_load_segment(struct kimage *image,
|
||||
struct kexec_segment *segment)
|
||||
int kimage_load_segment(struct kimage *image, int idx)
|
||||
{
|
||||
int result = -ENOMEM;
|
||||
|
||||
switch (image->type) {
|
||||
case KEXEC_TYPE_DEFAULT:
|
||||
result = kimage_load_normal_segment(image, segment);
|
||||
result = kimage_load_normal_segment(image, idx);
|
||||
break;
|
||||
#ifdef CONFIG_CRASH_DUMP
|
||||
case KEXEC_TYPE_CRASH:
|
||||
result = kimage_load_crash_segment(image, segment);
|
||||
result = kimage_load_crash_segment(image, idx);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
+50
-1
@@ -26,6 +26,7 @@
|
||||
#include <linux/kernel_read_file.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include "kexec_internal.h"
|
||||
|
||||
#ifdef CONFIG_KEXEC_SIG
|
||||
@@ -253,6 +254,8 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
image->no_cma = !!(flags & KEXEC_FILE_NO_CMA);
|
||||
|
||||
if (cmdline_len) {
|
||||
image->cmdline_buf = memdup_user(cmdline_ptr, cmdline_len);
|
||||
if (IS_ERR(image->cmdline_buf)) {
|
||||
@@ -434,7 +437,7 @@ SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd,
|
||||
i, ksegment->buf, ksegment->bufsz, ksegment->mem,
|
||||
ksegment->memsz);
|
||||
|
||||
ret = kimage_load_segment(image, &image->segment[i]);
|
||||
ret = kimage_load_segment(image, i);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
@@ -663,6 +666,43 @@ static int kexec_walk_resources(struct kexec_buf *kbuf,
|
||||
return walk_system_ram_res(0, ULONG_MAX, kbuf, func);
|
||||
}
|
||||
|
||||
static int kexec_alloc_contig(struct kexec_buf *kbuf)
|
||||
{
|
||||
size_t nr_pages = kbuf->memsz >> PAGE_SHIFT;
|
||||
unsigned long mem;
|
||||
struct page *p;
|
||||
|
||||
/* User space disabled CMA allocations, bail out. */
|
||||
if (kbuf->image->no_cma)
|
||||
return -EPERM;
|
||||
|
||||
/* Skip CMA logic for crash kernel */
|
||||
if (kbuf->image->type == KEXEC_TYPE_CRASH)
|
||||
return -EPERM;
|
||||
|
||||
p = dma_alloc_from_contiguous(NULL, nr_pages, get_order(kbuf->buf_align), true);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
pr_debug("allocated %zu DMA pages at 0x%lx", nr_pages, page_to_boot_pfn(p));
|
||||
|
||||
mem = page_to_boot_pfn(p) << PAGE_SHIFT;
|
||||
|
||||
if (kimage_is_destination_range(kbuf->image, mem, mem + kbuf->memsz)) {
|
||||
/* Our region is already in use by a statically defined one. Bail out. */
|
||||
pr_debug("CMA overlaps existing mem: 0x%lx+0x%lx\n", mem, kbuf->memsz);
|
||||
dma_release_from_contiguous(NULL, p, nr_pages);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
kbuf->mem = page_to_boot_pfn(p) << PAGE_SHIFT;
|
||||
kbuf->cma = p;
|
||||
|
||||
arch_kexec_post_alloc_pages(page_address(p), (int)nr_pages, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* kexec_locate_mem_hole - find free memory for the purgatory or the next kernel
|
||||
* @kbuf: Parameters for the memory search.
|
||||
@@ -687,6 +727,13 @@ int kexec_locate_mem_hole(struct kexec_buf *kbuf)
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Try to find a free physically contiguous block of memory first. With that, we
|
||||
* can avoid any copying at kexec time.
|
||||
*/
|
||||
if (!kexec_alloc_contig(kbuf))
|
||||
return 0;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK))
|
||||
ret = kexec_walk_resources(kbuf, locate_mem_hole_callback);
|
||||
else
|
||||
@@ -732,6 +779,7 @@ int kexec_add_buffer(struct kexec_buf *kbuf)
|
||||
/* Ensure minimum alignment needed for segments. */
|
||||
kbuf->memsz = ALIGN(kbuf->memsz, PAGE_SIZE);
|
||||
kbuf->buf_align = max(kbuf->buf_align, PAGE_SIZE);
|
||||
kbuf->cma = NULL;
|
||||
|
||||
/* Walk the RAM ranges and allocate a suitable range for the buffer */
|
||||
ret = arch_kexec_locate_mem_hole(kbuf);
|
||||
@@ -744,6 +792,7 @@ int kexec_add_buffer(struct kexec_buf *kbuf)
|
||||
ksegment->bufsz = kbuf->bufsz;
|
||||
ksegment->mem = kbuf->mem;
|
||||
ksegment->memsz = kbuf->memsz;
|
||||
kbuf->image->segment_cma[kbuf->image->nr_segments] = kbuf->cma;
|
||||
kbuf->image->nr_segments++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ struct kimage *do_kimage_alloc_init(void);
|
||||
int sanity_check_segment_list(struct kimage *image);
|
||||
void kimage_free_page_list(struct list_head *list);
|
||||
void kimage_free(struct kimage *image);
|
||||
int kimage_load_segment(struct kimage *image, struct kexec_segment *segment);
|
||||
int kimage_load_segment(struct kimage *image, int idx);
|
||||
void kimage_terminate(struct kimage *image);
|
||||
int kimage_is_destination_range(struct kimage *image,
|
||||
unsigned long start, unsigned long end);
|
||||
|
||||
+5
-6
@@ -88,13 +88,12 @@ static inline struct kthread *to_kthread(struct task_struct *k)
|
||||
/*
|
||||
* Variant of to_kthread() that doesn't assume @p is a kthread.
|
||||
*
|
||||
* Per construction; when:
|
||||
* When "(p->flags & PF_KTHREAD)" is set the task is a kthread and will
|
||||
* always remain a kthread. For kthreads p->worker_private always
|
||||
* points to a struct kthread. For tasks that are not kthreads
|
||||
* p->worker_private is used to point to other things.
|
||||
*
|
||||
* (p->flags & PF_KTHREAD) && p->worker_private
|
||||
*
|
||||
* the task is both a kthread and struct kthread is persistent. However
|
||||
* PF_KTHREAD on it's own is not, kernel_thread() can exec() (See umh.c and
|
||||
* begin_new_exec()).
|
||||
* Return NULL for any task that is not a kthread.
|
||||
*/
|
||||
static inline struct kthread *__to_kthread(struct task_struct *p)
|
||||
{
|
||||
|
||||
+23
-8
@@ -27,6 +27,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/hung_task.h>
|
||||
#include <trace/events/lock.h>
|
||||
|
||||
#ifndef CONFIG_PREEMPT_RT
|
||||
@@ -181,11 +182,11 @@ static inline void rwsem_set_reader_owned(struct rw_semaphore *sem)
|
||||
__rwsem_set_reader_owned(sem, current);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_RWSEMS
|
||||
#if defined(CONFIG_DEBUG_RWSEMS) || defined(CONFIG_DETECT_HUNG_TASK_BLOCKER)
|
||||
/*
|
||||
* Return just the real task structure pointer of the owner
|
||||
*/
|
||||
static inline struct task_struct *rwsem_owner(struct rw_semaphore *sem)
|
||||
struct task_struct *rwsem_owner(struct rw_semaphore *sem)
|
||||
{
|
||||
return (struct task_struct *)
|
||||
(atomic_long_read(&sem->owner) & ~RWSEM_OWNER_FLAGS_MASK);
|
||||
@@ -194,7 +195,7 @@ static inline struct task_struct *rwsem_owner(struct rw_semaphore *sem)
|
||||
/*
|
||||
* Return true if the rwsem is owned by a reader.
|
||||
*/
|
||||
static inline bool is_rwsem_reader_owned(struct rw_semaphore *sem)
|
||||
bool is_rwsem_reader_owned(struct rw_semaphore *sem)
|
||||
{
|
||||
/*
|
||||
* Check the count to see if it is write-locked.
|
||||
@@ -207,10 +208,10 @@ static inline bool is_rwsem_reader_owned(struct rw_semaphore *sem)
|
||||
}
|
||||
|
||||
/*
|
||||
* With CONFIG_DEBUG_RWSEMS configured, it will make sure that if there
|
||||
* is a task pointer in owner of a reader-owned rwsem, it will be the
|
||||
* real owner or one of the real owners. The only exception is when the
|
||||
* unlock is done by up_read_non_owner().
|
||||
* With CONFIG_DEBUG_RWSEMS or CONFIG_DETECT_HUNG_TASK_BLOCKER configured,
|
||||
* it will make sure that the owner field of a reader-owned rwsem either
|
||||
* points to a real reader-owner(s) or gets cleared. The only exception is
|
||||
* when the unlock is done by up_read_non_owner().
|
||||
*/
|
||||
static inline void rwsem_clear_reader_owned(struct rw_semaphore *sem)
|
||||
{
|
||||
@@ -1063,10 +1064,13 @@ queue:
|
||||
wake_up_q(&wake_q);
|
||||
|
||||
trace_contention_begin(sem, LCB_F_READ);
|
||||
set_current_state(state);
|
||||
|
||||
if (state == TASK_UNINTERRUPTIBLE)
|
||||
hung_task_set_blocker(sem, BLOCKER_TYPE_RWSEM_READER);
|
||||
|
||||
/* wait to be given the lock */
|
||||
for (;;) {
|
||||
set_current_state(state);
|
||||
if (!smp_load_acquire(&waiter.task)) {
|
||||
/* Matches rwsem_mark_wake()'s smp_store_release(). */
|
||||
break;
|
||||
@@ -1081,8 +1085,12 @@ queue:
|
||||
}
|
||||
schedule_preempt_disabled();
|
||||
lockevent_inc(rwsem_sleep_reader);
|
||||
set_current_state(state);
|
||||
}
|
||||
|
||||
if (state == TASK_UNINTERRUPTIBLE)
|
||||
hung_task_clear_blocker();
|
||||
|
||||
__set_current_state(TASK_RUNNING);
|
||||
lockevent_inc(rwsem_rlock);
|
||||
trace_contention_end(sem, 0);
|
||||
@@ -1144,6 +1152,9 @@ rwsem_down_write_slowpath(struct rw_semaphore *sem, int state)
|
||||
set_current_state(state);
|
||||
trace_contention_begin(sem, LCB_F_WRITE);
|
||||
|
||||
if (state == TASK_UNINTERRUPTIBLE)
|
||||
hung_task_set_blocker(sem, BLOCKER_TYPE_RWSEM_WRITER);
|
||||
|
||||
for (;;) {
|
||||
if (rwsem_try_write_lock(sem, &waiter)) {
|
||||
/* rwsem_try_write_lock() implies ACQUIRE on success */
|
||||
@@ -1177,6 +1188,10 @@ rwsem_down_write_slowpath(struct rw_semaphore *sem, int state)
|
||||
trylock_again:
|
||||
raw_spin_lock_irq(&sem->wait_lock);
|
||||
}
|
||||
|
||||
if (state == TASK_UNINTERRUPTIBLE)
|
||||
hung_task_clear_blocker();
|
||||
|
||||
__set_current_state(TASK_RUNNING);
|
||||
raw_spin_unlock_irq(&sem->wait_lock);
|
||||
lockevent_inc(rwsem_wlock);
|
||||
|
||||
@@ -112,6 +112,13 @@ struct find_symbol_arg {
|
||||
enum mod_license license;
|
||||
};
|
||||
|
||||
/* modules using other modules */
|
||||
struct module_use {
|
||||
struct list_head source_list;
|
||||
struct list_head target_list;
|
||||
struct module *source, *target;
|
||||
};
|
||||
|
||||
int mod_verify_sig(const void *mod, struct load_info *info);
|
||||
int try_to_force_load(struct module *mod, const char *reason);
|
||||
bool find_symbol(struct find_symbol_arg *fsa);
|
||||
|
||||
+9
-16
@@ -608,7 +608,7 @@ MODINFO_ATTR(version);
|
||||
MODINFO_ATTR(srcversion);
|
||||
|
||||
static struct {
|
||||
char name[MODULE_NAME_LEN + 1];
|
||||
char name[MODULE_NAME_LEN];
|
||||
char taints[MODULE_FLAGS_BUF_SIZE];
|
||||
} last_unloaded_module;
|
||||
|
||||
@@ -779,14 +779,16 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
|
||||
struct module *mod;
|
||||
char name[MODULE_NAME_LEN];
|
||||
char buf[MODULE_FLAGS_BUF_SIZE];
|
||||
int ret, forced = 0;
|
||||
int ret, len, forced = 0;
|
||||
|
||||
if (!capable(CAP_SYS_MODULE) || modules_disabled)
|
||||
return -EPERM;
|
||||
|
||||
if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
|
||||
return -EFAULT;
|
||||
name[MODULE_NAME_LEN-1] = '\0';
|
||||
len = strncpy_from_user(name, name_user, MODULE_NAME_LEN);
|
||||
if (len == 0 || len == MODULE_NAME_LEN)
|
||||
return -ENOENT;
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
audit_log_kern_module(name);
|
||||
|
||||
@@ -1320,20 +1322,11 @@ static int module_memory_alloc(struct module *mod, enum mod_mem_type type)
|
||||
else
|
||||
execmem_type = EXECMEM_MODULE_TEXT;
|
||||
|
||||
ptr = execmem_alloc(execmem_type, size);
|
||||
ptr = execmem_alloc_rw(execmem_type, size);
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
if (execmem_is_rox(execmem_type)) {
|
||||
int err = execmem_make_temp_rw(ptr, size);
|
||||
|
||||
if (err) {
|
||||
execmem_free(ptr);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mod->mem[type].is_rox = true;
|
||||
}
|
||||
mod->mem[type].is_rox = execmem_is_rox(execmem_type);
|
||||
|
||||
/*
|
||||
* The pointer to these blocks of memory are stored on the module
|
||||
|
||||
+32
-39
@@ -36,6 +36,7 @@
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/context_tracking.h>
|
||||
#include <linux/seq_buf.h>
|
||||
#include <linux/sys_info.h>
|
||||
#include <trace/events/error_report.h>
|
||||
#include <asm/sections.h>
|
||||
|
||||
@@ -63,20 +64,13 @@ int panic_on_warn __read_mostly;
|
||||
unsigned long panic_on_taint;
|
||||
bool panic_on_taint_nousertaint = false;
|
||||
static unsigned int warn_limit __read_mostly;
|
||||
static bool panic_console_replay;
|
||||
|
||||
bool panic_triggering_all_cpu_backtrace;
|
||||
|
||||
int panic_timeout = CONFIG_PANIC_TIMEOUT;
|
||||
EXPORT_SYMBOL_GPL(panic_timeout);
|
||||
|
||||
#define PANIC_PRINT_TASK_INFO 0x00000001
|
||||
#define PANIC_PRINT_MEM_INFO 0x00000002
|
||||
#define PANIC_PRINT_TIMER_INFO 0x00000004
|
||||
#define PANIC_PRINT_LOCK_INFO 0x00000008
|
||||
#define PANIC_PRINT_FTRACE_INFO 0x00000010
|
||||
#define PANIC_PRINT_ALL_PRINTK_MSG 0x00000020
|
||||
#define PANIC_PRINT_ALL_CPU_BT 0x00000040
|
||||
#define PANIC_PRINT_BLOCKED_TASKS 0x00000080
|
||||
unsigned long panic_print;
|
||||
|
||||
ATOMIC_NOTIFIER_HEAD(panic_notifier_list);
|
||||
@@ -128,6 +122,13 @@ static int proc_taint(const struct ctl_table *table, int write,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sysctl_panic_print_handler(const struct ctl_table *table, int write,
|
||||
void *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
pr_info_once("Kernel: 'panic_print' sysctl interface will be obsoleted by both 'panic_sys_info' and 'panic_console_replay'\n");
|
||||
return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
|
||||
}
|
||||
|
||||
static const struct ctl_table kern_panic_table[] = {
|
||||
#ifdef CONFIG_SMP
|
||||
{
|
||||
@@ -165,7 +166,7 @@ static const struct ctl_table kern_panic_table[] = {
|
||||
.data = &panic_print,
|
||||
.maxlen = sizeof(unsigned long),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_doulongvec_minmax,
|
||||
.proc_handler = sysctl_panic_print_handler,
|
||||
},
|
||||
{
|
||||
.procname = "panic_on_warn",
|
||||
@@ -193,6 +194,13 @@ static const struct ctl_table kern_panic_table[] = {
|
||||
.proc_handler = proc_dointvec,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.procname = "panic_sys_info",
|
||||
.data = &panic_print,
|
||||
.maxlen = sizeof(panic_print),
|
||||
.mode = 0644,
|
||||
.proc_handler = sysctl_sys_info_handler,
|
||||
},
|
||||
};
|
||||
|
||||
static __init int kernel_panic_sysctls_init(void)
|
||||
@@ -203,6 +211,15 @@ static __init int kernel_panic_sysctls_init(void)
|
||||
late_initcall(kernel_panic_sysctls_init);
|
||||
#endif
|
||||
|
||||
/* The format is "panic_sys_info=tasks,mem,locks,ftrace,..." */
|
||||
static int __init setup_panic_sys_info(char *buf)
|
||||
{
|
||||
/* There is no risk of race in kernel boot phase */
|
||||
panic_print = sys_info_parse_param(buf);
|
||||
return 1;
|
||||
}
|
||||
__setup("panic_sys_info=", setup_panic_sys_info);
|
||||
|
||||
static atomic_t warn_count = ATOMIC_INIT(0);
|
||||
|
||||
#ifdef CONFIG_SYSFS
|
||||
@@ -298,33 +315,6 @@ void nmi_panic(struct pt_regs *regs, const char *msg)
|
||||
}
|
||||
EXPORT_SYMBOL(nmi_panic);
|
||||
|
||||
static void panic_print_sys_info(bool console_flush)
|
||||
{
|
||||
if (console_flush) {
|
||||
if (panic_print & PANIC_PRINT_ALL_PRINTK_MSG)
|
||||
console_flush_on_panic(CONSOLE_REPLAY_ALL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (panic_print & PANIC_PRINT_TASK_INFO)
|
||||
show_state();
|
||||
|
||||
if (panic_print & PANIC_PRINT_MEM_INFO)
|
||||
show_mem();
|
||||
|
||||
if (panic_print & PANIC_PRINT_TIMER_INFO)
|
||||
sysrq_timer_list_show();
|
||||
|
||||
if (panic_print & PANIC_PRINT_LOCK_INFO)
|
||||
debug_show_all_locks();
|
||||
|
||||
if (panic_print & PANIC_PRINT_FTRACE_INFO)
|
||||
ftrace_dump(DUMP_ALL);
|
||||
|
||||
if (panic_print & PANIC_PRINT_BLOCKED_TASKS)
|
||||
show_state_filter(TASK_UNINTERRUPTIBLE);
|
||||
}
|
||||
|
||||
void check_panic_on_warn(const char *origin)
|
||||
{
|
||||
unsigned int limit;
|
||||
@@ -345,7 +335,7 @@ void check_panic_on_warn(const char *origin)
|
||||
*/
|
||||
static void panic_other_cpus_shutdown(bool crash_kexec)
|
||||
{
|
||||
if (panic_print & PANIC_PRINT_ALL_CPU_BT) {
|
||||
if (panic_print & SYS_INFO_ALL_CPU_BT) {
|
||||
/* Temporary allow non-panic CPUs to write their backtraces. */
|
||||
panic_triggering_all_cpu_backtrace = true;
|
||||
trigger_all_cpu_backtrace();
|
||||
@@ -468,7 +458,7 @@ void vpanic(const char *fmt, va_list args)
|
||||
*/
|
||||
atomic_notifier_call_chain(&panic_notifier_list, 0, buf);
|
||||
|
||||
panic_print_sys_info(false);
|
||||
sys_info(panic_print);
|
||||
|
||||
kmsg_dump_desc(KMSG_DUMP_PANIC, buf);
|
||||
|
||||
@@ -497,7 +487,9 @@ void vpanic(const char *fmt, va_list args)
|
||||
debug_locks_off();
|
||||
console_flush_on_panic(CONSOLE_FLUSH_PENDING);
|
||||
|
||||
panic_print_sys_info(true);
|
||||
if ((panic_print & SYS_INFO_PANIC_CONSOLE_REPLAY) ||
|
||||
panic_console_replay)
|
||||
console_flush_on_panic(CONSOLE_REPLAY_ALL);
|
||||
|
||||
if (!panic_blink)
|
||||
panic_blink = no_blink;
|
||||
@@ -949,6 +941,7 @@ core_param(panic_print, panic_print, ulong, 0644);
|
||||
core_param(pause_on_oops, pause_on_oops, int, 0644);
|
||||
core_param(panic_on_warn, panic_on_warn, int, 0644);
|
||||
core_param(crash_kexec_post_notifiers, crash_kexec_post_notifiers, bool, 0644);
|
||||
core_param(panic_console_replay, panic_console_replay, bool, 0644);
|
||||
|
||||
static int __init oops_setup(char *s)
|
||||
{
|
||||
|
||||
@@ -64,6 +64,7 @@ struct dev_printk_info;
|
||||
|
||||
extern struct printk_ringbuffer *prb;
|
||||
extern bool printk_kthreads_running;
|
||||
extern bool printk_kthreads_ready;
|
||||
extern bool debug_non_panic_cpus;
|
||||
|
||||
__printf(4, 0)
|
||||
@@ -179,6 +180,7 @@ static inline void nbcon_kthread_wake(struct console *con)
|
||||
#define PRINTKRB_RECORD_MAX 0
|
||||
|
||||
#define printk_kthreads_running (false)
|
||||
#define printk_kthreads_ready (false)
|
||||
|
||||
/*
|
||||
* In !PRINTK builds we still export console_sem
|
||||
|
||||
+65
-24
@@ -214,8 +214,9 @@ static void nbcon_seq_try_update(struct nbcon_context *ctxt, u64 new_seq)
|
||||
|
||||
/**
|
||||
* nbcon_context_try_acquire_direct - Try to acquire directly
|
||||
* @ctxt: The context of the caller
|
||||
* @cur: The current console state
|
||||
* @ctxt: The context of the caller
|
||||
* @cur: The current console state
|
||||
* @is_reacquire: This acquire is a reacquire
|
||||
*
|
||||
* Acquire the console when it is released. Also acquire the console when
|
||||
* the current owner has a lower priority and the console is in a safe state.
|
||||
@@ -225,17 +226,17 @@ static void nbcon_seq_try_update(struct nbcon_context *ctxt, u64 new_seq)
|
||||
*
|
||||
* Errors:
|
||||
*
|
||||
* -EPERM: A panic is in progress and this is not the panic CPU.
|
||||
* Or the current owner or waiter has the same or higher
|
||||
* priority. No acquire method can be successful in
|
||||
* this case.
|
||||
* -EPERM: A panic is in progress and this is neither the panic
|
||||
* CPU nor is this a reacquire. Or the current owner or
|
||||
* waiter has the same or higher priority. No acquire
|
||||
* method can be successful in these cases.
|
||||
*
|
||||
* -EBUSY: The current owner has a lower priority but the console
|
||||
* in an unsafe state. The caller should try using
|
||||
* the handover acquire method.
|
||||
*/
|
||||
static int nbcon_context_try_acquire_direct(struct nbcon_context *ctxt,
|
||||
struct nbcon_state *cur)
|
||||
struct nbcon_state *cur, bool is_reacquire)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
struct console *con = ctxt->console;
|
||||
@@ -243,14 +244,20 @@ static int nbcon_context_try_acquire_direct(struct nbcon_context *ctxt,
|
||||
|
||||
do {
|
||||
/*
|
||||
* Panic does not imply that the console is owned. However, it
|
||||
* is critical that non-panic CPUs during panic are unable to
|
||||
* acquire ownership in order to satisfy the assumptions of
|
||||
* nbcon_waiter_matches(). In particular, the assumption that
|
||||
* lower priorities are ignored during panic.
|
||||
* Panic does not imply that the console is owned. However,
|
||||
* since all non-panic CPUs are stopped during panic(), it
|
||||
* is safer to have them avoid gaining console ownership.
|
||||
*
|
||||
* If this acquire is a reacquire (and an unsafe takeover
|
||||
* has not previously occurred) then it is allowed to attempt
|
||||
* a direct acquire in panic. This gives console drivers an
|
||||
* opportunity to perform any necessary cleanup if they were
|
||||
* interrupted by the panic CPU while printing.
|
||||
*/
|
||||
if (other_cpu_in_panic())
|
||||
if (other_cpu_in_panic() &&
|
||||
(!is_reacquire || cur->unsafe_takeover)) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (ctxt->prio <= cur->prio || ctxt->prio <= cur->req_prio)
|
||||
return -EPERM;
|
||||
@@ -301,8 +308,9 @@ static bool nbcon_waiter_matches(struct nbcon_state *cur, int expected_prio)
|
||||
* Event #1 implies this context is EMERGENCY.
|
||||
* Event #2 implies the new context is PANIC.
|
||||
* Event #3 occurs when panic() has flushed the console.
|
||||
* Events #4 and #5 are not possible due to the other_cpu_in_panic()
|
||||
* check in nbcon_context_try_acquire_direct().
|
||||
* Event #4 occurs when a non-panic CPU reacquires.
|
||||
* Event #5 is not possible due to the other_cpu_in_panic() check
|
||||
* in nbcon_context_try_acquire_handover().
|
||||
*/
|
||||
|
||||
return (cur->req_prio == expected_prio);
|
||||
@@ -431,6 +439,16 @@ static int nbcon_context_try_acquire_handover(struct nbcon_context *ctxt,
|
||||
WARN_ON_ONCE(ctxt->prio <= cur->prio || ctxt->prio <= cur->req_prio);
|
||||
WARN_ON_ONCE(!cur->unsafe);
|
||||
|
||||
/*
|
||||
* Panic does not imply that the console is owned. However, it
|
||||
* is critical that non-panic CPUs during panic are unable to
|
||||
* wait for a handover in order to satisfy the assumptions of
|
||||
* nbcon_waiter_matches(). In particular, the assumption that
|
||||
* lower priorities are ignored during panic.
|
||||
*/
|
||||
if (other_cpu_in_panic())
|
||||
return -EPERM;
|
||||
|
||||
/* Handover is not possible on the same CPU. */
|
||||
if (cur->cpu == cpu)
|
||||
return -EBUSY;
|
||||
@@ -558,7 +576,8 @@ static struct printk_buffers panic_nbcon_pbufs;
|
||||
|
||||
/**
|
||||
* nbcon_context_try_acquire - Try to acquire nbcon console
|
||||
* @ctxt: The context of the caller
|
||||
* @ctxt: The context of the caller
|
||||
* @is_reacquire: This acquire is a reacquire
|
||||
*
|
||||
* Context: Under @ctxt->con->device_lock() or local_irq_save().
|
||||
* Return: True if the console was acquired. False otherwise.
|
||||
@@ -568,7 +587,7 @@ static struct printk_buffers panic_nbcon_pbufs;
|
||||
* in an unsafe state. Otherwise, on success the caller may assume
|
||||
* the console is not in an unsafe state.
|
||||
*/
|
||||
static bool nbcon_context_try_acquire(struct nbcon_context *ctxt)
|
||||
static bool nbcon_context_try_acquire(struct nbcon_context *ctxt, bool is_reacquire)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
struct console *con = ctxt->console;
|
||||
@@ -577,7 +596,7 @@ static bool nbcon_context_try_acquire(struct nbcon_context *ctxt)
|
||||
|
||||
nbcon_state_read(con, &cur);
|
||||
try_again:
|
||||
err = nbcon_context_try_acquire_direct(ctxt, &cur);
|
||||
err = nbcon_context_try_acquire_direct(ctxt, &cur, is_reacquire);
|
||||
if (err != -EBUSY)
|
||||
goto out;
|
||||
|
||||
@@ -913,7 +932,7 @@ void nbcon_reacquire_nobuf(struct nbcon_write_context *wctxt)
|
||||
{
|
||||
struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
|
||||
|
||||
while (!nbcon_context_try_acquire(ctxt))
|
||||
while (!nbcon_context_try_acquire(ctxt, true))
|
||||
cpu_relax();
|
||||
|
||||
nbcon_write_context_set_buf(wctxt, NULL, 0);
|
||||
@@ -1101,7 +1120,7 @@ static bool nbcon_emit_one(struct nbcon_write_context *wctxt, bool use_atomic)
|
||||
cant_migrate();
|
||||
}
|
||||
|
||||
if (!nbcon_context_try_acquire(ctxt))
|
||||
if (!nbcon_context_try_acquire(ctxt, false))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
@@ -1486,7 +1505,7 @@ static int __nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq,
|
||||
ctxt->prio = nbcon_get_default_prio();
|
||||
ctxt->allow_unsafe_takeover = allow_unsafe_takeover;
|
||||
|
||||
if (!nbcon_context_try_acquire(ctxt))
|
||||
if (!nbcon_context_try_acquire(ctxt, false))
|
||||
return -EPERM;
|
||||
|
||||
while (nbcon_seq_read(con) < stop_seq) {
|
||||
@@ -1671,6 +1690,9 @@ bool nbcon_alloc(struct console *con)
|
||||
{
|
||||
struct nbcon_state state = { };
|
||||
|
||||
/* Synchronize the kthread start. */
|
||||
lockdep_assert_console_list_lock_held();
|
||||
|
||||
/* The write_thread() callback is mandatory. */
|
||||
if (WARN_ON(!con->write_thread))
|
||||
return false;
|
||||
@@ -1701,12 +1723,15 @@ bool nbcon_alloc(struct console *con)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (printk_kthreads_running) {
|
||||
if (printk_kthreads_ready && !have_boot_console) {
|
||||
if (!nbcon_kthread_create(con)) {
|
||||
kfree(con->pbufs);
|
||||
con->pbufs = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Might be the first kthread. */
|
||||
printk_kthreads_running = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1716,14 +1741,30 @@ bool nbcon_alloc(struct console *con)
|
||||
/**
|
||||
* nbcon_free - Free and cleanup the nbcon console specific data
|
||||
* @con: Console to free/cleanup nbcon data
|
||||
*
|
||||
* Important: @have_nbcon_console must be updated before calling
|
||||
* this function. In particular, it can be set only when there
|
||||
* is still another nbcon console registered.
|
||||
*/
|
||||
void nbcon_free(struct console *con)
|
||||
{
|
||||
struct nbcon_state state = { };
|
||||
|
||||
if (printk_kthreads_running)
|
||||
/* Synchronize the kthread stop. */
|
||||
lockdep_assert_console_list_lock_held();
|
||||
|
||||
if (printk_kthreads_running) {
|
||||
nbcon_kthread_stop(con);
|
||||
|
||||
/* Might be the last nbcon console.
|
||||
*
|
||||
* Do not rely on printk_kthreads_check_locked(). It is not
|
||||
* called in some code paths, see nbcon_free() callers.
|
||||
*/
|
||||
if (!have_nbcon_console)
|
||||
printk_kthreads_running = false;
|
||||
}
|
||||
|
||||
nbcon_state_set(con, &state);
|
||||
|
||||
/* Boot consoles share global printk buffers. */
|
||||
@@ -1762,7 +1803,7 @@ bool nbcon_device_try_acquire(struct console *con)
|
||||
ctxt->console = con;
|
||||
ctxt->prio = NBCON_PRIO_NORMAL;
|
||||
|
||||
if (!nbcon_context_try_acquire(ctxt))
|
||||
if (!nbcon_context_try_acquire(ctxt, false))
|
||||
return false;
|
||||
|
||||
if (!nbcon_context_enter_unsafe(ctxt))
|
||||
|
||||
+11
-9
@@ -3574,7 +3574,7 @@ EXPORT_SYMBOL(console_resume);
|
||||
static int unregister_console_locked(struct console *console);
|
||||
|
||||
/* True when system boot is far enough to create printer threads. */
|
||||
static bool printk_kthreads_ready __ro_after_init;
|
||||
bool printk_kthreads_ready __ro_after_init;
|
||||
|
||||
static struct task_struct *printk_legacy_kthread;
|
||||
|
||||
@@ -3713,6 +3713,7 @@ static void printk_kthreads_check_locked(void)
|
||||
if (!printk_kthreads_ready)
|
||||
return;
|
||||
|
||||
/* Start or stop the legacy kthread when needed. */
|
||||
if (have_legacy_console || have_boot_console) {
|
||||
if (!printk_legacy_kthread &&
|
||||
force_legacy_kthread() &&
|
||||
@@ -4204,14 +4205,6 @@ static int unregister_console_locked(struct console *console)
|
||||
*/
|
||||
synchronize_srcu(&console_srcu);
|
||||
|
||||
if (console->flags & CON_NBCON)
|
||||
nbcon_free(console);
|
||||
|
||||
console_sysfs_notify();
|
||||
|
||||
if (console->exit)
|
||||
res = console->exit(console);
|
||||
|
||||
/*
|
||||
* With this console gone, the global flags tracking registered
|
||||
* console types may have changed. Update them.
|
||||
@@ -4232,6 +4225,15 @@ static int unregister_console_locked(struct console *console)
|
||||
if (!found_nbcon_con)
|
||||
have_nbcon_console = found_nbcon_con;
|
||||
|
||||
/* @have_nbcon_console must be updated before calling nbcon_free(). */
|
||||
if (console->flags & CON_NBCON)
|
||||
nbcon_free(console);
|
||||
|
||||
console_sysfs_notify();
|
||||
|
||||
if (console->exit)
|
||||
res = console->exit(console);
|
||||
|
||||
/* Changed console list, may require printer threads to start/stop. */
|
||||
printk_kthreads_check_locked();
|
||||
|
||||
|
||||
+54
-15
@@ -118,7 +118,7 @@ static void *relay_alloc_buf(struct rchan_buf *buf, size_t *size)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < n_pages; i++) {
|
||||
buf->page_array[i] = alloc_page(GFP_KERNEL);
|
||||
buf->page_array[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
|
||||
if (unlikely(!buf->page_array[i]))
|
||||
goto depopulate;
|
||||
set_page_private(buf->page_array[i], (unsigned long)buf);
|
||||
@@ -127,7 +127,6 @@ static void *relay_alloc_buf(struct rchan_buf *buf, size_t *size)
|
||||
if (!mem)
|
||||
goto depopulate;
|
||||
|
||||
memset(mem, 0, *size);
|
||||
buf->page_count = n_pages;
|
||||
return mem;
|
||||
|
||||
@@ -250,13 +249,18 @@ EXPORT_SYMBOL_GPL(relay_buf_full);
|
||||
*/
|
||||
|
||||
static int relay_subbuf_start(struct rchan_buf *buf, void *subbuf,
|
||||
void *prev_subbuf, size_t prev_padding)
|
||||
void *prev_subbuf)
|
||||
{
|
||||
int full = relay_buf_full(buf);
|
||||
|
||||
if (full)
|
||||
buf->stats.full_count++;
|
||||
|
||||
if (!buf->chan->cb->subbuf_start)
|
||||
return !relay_buf_full(buf);
|
||||
return !full;
|
||||
|
||||
return buf->chan->cb->subbuf_start(buf, subbuf,
|
||||
prev_subbuf, prev_padding);
|
||||
prev_subbuf);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,11 +302,13 @@ static void __relay_reset(struct rchan_buf *buf, unsigned int init)
|
||||
buf->finalized = 0;
|
||||
buf->data = buf->start;
|
||||
buf->offset = 0;
|
||||
buf->stats.full_count = 0;
|
||||
buf->stats.big_count = 0;
|
||||
|
||||
for (i = 0; i < buf->chan->n_subbufs; i++)
|
||||
buf->padding[i] = 0;
|
||||
|
||||
relay_subbuf_start(buf, buf->data, NULL, 0);
|
||||
relay_subbuf_start(buf, buf->data, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -555,9 +561,11 @@ size_t relay_switch_subbuf(struct rchan_buf *buf, size_t length)
|
||||
goto toobig;
|
||||
|
||||
if (buf->offset != buf->chan->subbuf_size + 1) {
|
||||
buf->prev_padding = buf->chan->subbuf_size - buf->offset;
|
||||
size_t prev_padding;
|
||||
|
||||
prev_padding = buf->chan->subbuf_size - buf->offset;
|
||||
old_subbuf = buf->subbufs_produced % buf->chan->n_subbufs;
|
||||
buf->padding[old_subbuf] = buf->prev_padding;
|
||||
buf->padding[old_subbuf] = prev_padding;
|
||||
buf->subbufs_produced++;
|
||||
if (buf->dentry)
|
||||
d_inode(buf->dentry)->i_size +=
|
||||
@@ -582,7 +590,7 @@ size_t relay_switch_subbuf(struct rchan_buf *buf, size_t length)
|
||||
new_subbuf = buf->subbufs_produced % buf->chan->n_subbufs;
|
||||
new = buf->start + new_subbuf * buf->chan->subbuf_size;
|
||||
buf->offset = 0;
|
||||
if (!relay_subbuf_start(buf, new, old, buf->prev_padding)) {
|
||||
if (!relay_subbuf_start(buf, new, old)) {
|
||||
buf->offset = buf->chan->subbuf_size + 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -595,7 +603,7 @@ size_t relay_switch_subbuf(struct rchan_buf *buf, size_t length)
|
||||
return length;
|
||||
|
||||
toobig:
|
||||
buf->chan->last_toobig = length;
|
||||
buf->stats.big_count++;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(relay_switch_subbuf);
|
||||
@@ -655,11 +663,6 @@ void relay_close(struct rchan *chan)
|
||||
if ((buf = *per_cpu_ptr(chan->buf, i)))
|
||||
relay_close_buf(buf);
|
||||
|
||||
if (chan->last_toobig)
|
||||
printk(KERN_WARNING "relay: one or more items not logged "
|
||||
"[item size (%zd) > sub-buffer size (%zd)]\n",
|
||||
chan->last_toobig, chan->subbuf_size);
|
||||
|
||||
list_del(&chan->list);
|
||||
kref_put(&chan->kref, relay_destroy_channel);
|
||||
mutex_unlock(&relay_channels_mutex);
|
||||
@@ -693,6 +696,42 @@ void relay_flush(struct rchan *chan)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(relay_flush);
|
||||
|
||||
/**
|
||||
* relay_stats - get channel buffer statistics
|
||||
* @chan: the channel
|
||||
* @flags: select particular information to get
|
||||
*
|
||||
* Returns the count of certain field that caller specifies.
|
||||
*/
|
||||
size_t relay_stats(struct rchan *chan, int flags)
|
||||
{
|
||||
unsigned int i, count = 0;
|
||||
struct rchan_buf *rbuf;
|
||||
|
||||
if (!chan || flags > RELAY_STATS_LAST)
|
||||
return 0;
|
||||
|
||||
if (chan->is_global) {
|
||||
rbuf = *per_cpu_ptr(chan->buf, 0);
|
||||
if (flags & RELAY_STATS_BUF_FULL)
|
||||
count = rbuf->stats.full_count;
|
||||
else if (flags & RELAY_STATS_WRT_BIG)
|
||||
count = rbuf->stats.big_count;
|
||||
} else {
|
||||
for_each_online_cpu(i) {
|
||||
rbuf = *per_cpu_ptr(chan->buf, i);
|
||||
if (rbuf) {
|
||||
if (flags & RELAY_STATS_BUF_FULL)
|
||||
count += rbuf->stats.full_count;
|
||||
else if (flags & RELAY_STATS_WRT_BIG)
|
||||
count += rbuf->stats.big_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* relay_file_open - open file op for relay files
|
||||
* @inode: the inode
|
||||
|
||||
+1
-5
@@ -176,7 +176,7 @@ struct psi_group psi_system = {
|
||||
.pcpu = &system_group_pcpu,
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(seqcount_t, psi_seq);
|
||||
static DEFINE_PER_CPU(seqcount_t, psi_seq) = SEQCNT_ZERO(psi_seq);
|
||||
|
||||
static inline void psi_write_begin(int cpu)
|
||||
{
|
||||
@@ -204,11 +204,7 @@ static void poll_timer_fn(struct timer_list *t);
|
||||
|
||||
static void group_init(struct psi_group *group)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
group->enabled = true;
|
||||
for_each_possible_cpu(cpu)
|
||||
seqcount_init(per_cpu_ptr(&psi_seq, cpu));
|
||||
group->avg_last_update = sched_clock();
|
||||
group->avg_next_update = group->avg_last_update + psi_period;
|
||||
mutex_init(&group->avgs_lock);
|
||||
|
||||
+1
-1
@@ -1018,7 +1018,7 @@ void __init smp_init(void)
|
||||
* @cond_func: A callback function that is passed a cpu id and
|
||||
* the info parameter. The function is called
|
||||
* with preemption disabled. The function should
|
||||
* return a blooean value indicating whether to IPI
|
||||
* return a boolean value indicating whether to IPI
|
||||
* the specified CPU.
|
||||
* @func: The function to run on all applicable CPUs.
|
||||
* This must be fast and non-blocking.
|
||||
|
||||
+2
-20
@@ -415,9 +415,10 @@ static ssize_t blk_dropped_read(struct file *filp, char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct blk_trace *bt = filp->private_data;
|
||||
size_t dropped = relay_stats(bt->rchan, RELAY_STATS_BUF_FULL);
|
||||
char buf[16];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%u\n", atomic_read(&bt->dropped));
|
||||
snprintf(buf, sizeof(buf), "%zu\n", dropped);
|
||||
|
||||
return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
|
||||
}
|
||||
@@ -456,23 +457,6 @@ static const struct file_operations blk_msg_fops = {
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
/*
|
||||
* Keep track of how many times we encountered a full subbuffer, to aid
|
||||
* the user space app in telling how many lost events there were.
|
||||
*/
|
||||
static int blk_subbuf_start_callback(struct rchan_buf *buf, void *subbuf,
|
||||
void *prev_subbuf, size_t prev_padding)
|
||||
{
|
||||
struct blk_trace *bt;
|
||||
|
||||
if (!relay_buf_full(buf))
|
||||
return 1;
|
||||
|
||||
bt = buf->chan->private_data;
|
||||
atomic_inc(&bt->dropped);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blk_remove_buf_file_callback(struct dentry *dentry)
|
||||
{
|
||||
debugfs_remove(dentry);
|
||||
@@ -491,7 +475,6 @@ static struct dentry *blk_create_buf_file_callback(const char *filename,
|
||||
}
|
||||
|
||||
static const struct rchan_callbacks blk_relay_callbacks = {
|
||||
.subbuf_start = blk_subbuf_start_callback,
|
||||
.create_buf_file = blk_create_buf_file_callback,
|
||||
.remove_buf_file = blk_remove_buf_file_callback,
|
||||
};
|
||||
@@ -580,7 +563,6 @@ static int do_blk_trace_setup(struct request_queue *q, char *name, dev_t dev,
|
||||
}
|
||||
|
||||
bt->dev = dev;
|
||||
atomic_set(&bt->dropped, 0);
|
||||
INIT_LIST_HEAD(&bt->running_list);
|
||||
|
||||
ret = -EIO;
|
||||
|
||||
@@ -4812,26 +4812,26 @@ int ring_buffer_write(struct trace_buffer *buffer,
|
||||
int ret = -EBUSY;
|
||||
int cpu;
|
||||
|
||||
preempt_disable_notrace();
|
||||
guard(preempt_notrace)();
|
||||
|
||||
if (atomic_read(&buffer->record_disabled))
|
||||
goto out;
|
||||
return -EBUSY;
|
||||
|
||||
cpu = raw_smp_processor_id();
|
||||
|
||||
if (!cpumask_test_cpu(cpu, buffer->cpumask))
|
||||
goto out;
|
||||
return -EBUSY;
|
||||
|
||||
cpu_buffer = buffer->buffers[cpu];
|
||||
|
||||
if (atomic_read(&cpu_buffer->record_disabled))
|
||||
goto out;
|
||||
return -EBUSY;
|
||||
|
||||
if (length > buffer->max_data_size)
|
||||
goto out;
|
||||
return -EBUSY;
|
||||
|
||||
if (unlikely(trace_recursive_lock(cpu_buffer)))
|
||||
goto out;
|
||||
return -EBUSY;
|
||||
|
||||
event = rb_reserve_next_event(buffer, cpu_buffer, length);
|
||||
if (!event)
|
||||
@@ -4849,10 +4849,6 @@ int ring_buffer_write(struct trace_buffer *buffer,
|
||||
|
||||
out_unlock:
|
||||
trace_recursive_unlock(cpu_buffer);
|
||||
|
||||
out:
|
||||
preempt_enable_notrace();
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ring_buffer_write);
|
||||
|
||||
+103
-186
@@ -432,15 +432,13 @@ static void ftrace_exports(struct ring_buffer_event *event, int flag)
|
||||
{
|
||||
struct trace_export *export;
|
||||
|
||||
preempt_disable_notrace();
|
||||
guard(preempt_notrace)();
|
||||
|
||||
export = rcu_dereference_raw_check(ftrace_exports_list);
|
||||
while (export) {
|
||||
trace_process_export(export, event, flag);
|
||||
export = rcu_dereference_raw_check(export->next);
|
||||
}
|
||||
|
||||
preempt_enable_notrace();
|
||||
}
|
||||
|
||||
static inline void
|
||||
@@ -497,27 +495,18 @@ int register_ftrace_export(struct trace_export *export)
|
||||
if (WARN_ON_ONCE(!export->write))
|
||||
return -1;
|
||||
|
||||
mutex_lock(&ftrace_export_lock);
|
||||
guard(mutex)(&ftrace_export_lock);
|
||||
|
||||
add_ftrace_export(&ftrace_exports_list, export);
|
||||
|
||||
mutex_unlock(&ftrace_export_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_ftrace_export);
|
||||
|
||||
int unregister_ftrace_export(struct trace_export *export)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ftrace_export_lock);
|
||||
|
||||
ret = rm_ftrace_export(&ftrace_exports_list, export);
|
||||
|
||||
mutex_unlock(&ftrace_export_lock);
|
||||
|
||||
return ret;
|
||||
guard(mutex)(&ftrace_export_lock);
|
||||
return rm_ftrace_export(&ftrace_exports_list, export);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_ftrace_export);
|
||||
|
||||
@@ -640,9 +629,8 @@ void trace_array_put(struct trace_array *this_tr)
|
||||
if (!this_tr)
|
||||
return;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
guard(mutex)(&trace_types_lock);
|
||||
__trace_array_put(this_tr);
|
||||
mutex_unlock(&trace_types_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_array_put);
|
||||
|
||||
@@ -1160,13 +1148,11 @@ int __trace_array_puts(struct trace_array *tr, unsigned long ip,
|
||||
|
||||
trace_ctx = tracing_gen_ctx();
|
||||
buffer = tr->array_buffer.buffer;
|
||||
ring_buffer_nest_start(buffer);
|
||||
guard(ring_buffer_nest)(buffer);
|
||||
event = __trace_buffer_lock_reserve(buffer, TRACE_PRINT, alloc,
|
||||
trace_ctx);
|
||||
if (!event) {
|
||||
size = 0;
|
||||
goto out;
|
||||
}
|
||||
if (!event)
|
||||
return 0;
|
||||
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->ip = ip;
|
||||
@@ -1182,8 +1168,6 @@ int __trace_array_puts(struct trace_array *tr, unsigned long ip,
|
||||
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
ftrace_trace_stack(tr, buffer, trace_ctx, 4, NULL);
|
||||
out:
|
||||
ring_buffer_nest_end(buffer);
|
||||
return size;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__trace_array_puts);
|
||||
@@ -1213,7 +1197,6 @@ int __trace_bputs(unsigned long ip, const char *str)
|
||||
struct bputs_entry *entry;
|
||||
unsigned int trace_ctx;
|
||||
int size = sizeof(struct bputs_entry);
|
||||
int ret = 0;
|
||||
|
||||
if (!printk_binsafe(tr))
|
||||
return __trace_puts(ip, str, strlen(str));
|
||||
@@ -1227,11 +1210,11 @@ int __trace_bputs(unsigned long ip, const char *str)
|
||||
trace_ctx = tracing_gen_ctx();
|
||||
buffer = tr->array_buffer.buffer;
|
||||
|
||||
ring_buffer_nest_start(buffer);
|
||||
guard(ring_buffer_nest)(buffer);
|
||||
event = __trace_buffer_lock_reserve(buffer, TRACE_BPUTS, size,
|
||||
trace_ctx);
|
||||
if (!event)
|
||||
goto out;
|
||||
return 0;
|
||||
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->ip = ip;
|
||||
@@ -1240,10 +1223,7 @@ int __trace_bputs(unsigned long ip, const char *str)
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
ftrace_trace_stack(tr, buffer, trace_ctx, 4, NULL);
|
||||
|
||||
ret = 1;
|
||||
out:
|
||||
ring_buffer_nest_end(buffer);
|
||||
return ret;
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__trace_bputs);
|
||||
|
||||
@@ -1432,13 +1412,8 @@ static int tracing_arm_snapshot_locked(struct trace_array *tr)
|
||||
|
||||
int tracing_arm_snapshot(struct trace_array *tr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
ret = tracing_arm_snapshot_locked(tr);
|
||||
mutex_unlock(&trace_types_lock);
|
||||
|
||||
return ret;
|
||||
guard(mutex)(&trace_types_lock);
|
||||
return tracing_arm_snapshot_locked(tr);
|
||||
}
|
||||
|
||||
void tracing_disarm_snapshot(struct trace_array *tr)
|
||||
@@ -1841,7 +1816,7 @@ int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
|
||||
|
||||
ret = get_user(ch, ubuf++);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
read++;
|
||||
cnt--;
|
||||
@@ -1855,7 +1830,7 @@ int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
|
||||
while (cnt && isspace(ch)) {
|
||||
ret = get_user(ch, ubuf++);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
read++;
|
||||
cnt--;
|
||||
}
|
||||
@@ -1865,8 +1840,7 @@ int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
|
||||
/* only spaces were written */
|
||||
if (isspace(ch) || !ch) {
|
||||
*ppos += read;
|
||||
ret = read;
|
||||
goto out;
|
||||
return read;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1874,13 +1848,12 @@ int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
|
||||
while (cnt && !isspace(ch) && ch) {
|
||||
if (parser->idx < parser->size - 1)
|
||||
parser->buffer[parser->idx++] = ch;
|
||||
else {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
ret = get_user(ch, ubuf++);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
read++;
|
||||
cnt--;
|
||||
}
|
||||
@@ -1895,15 +1868,11 @@ int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
|
||||
/* Make sure the parsed string always terminates with '\0'. */
|
||||
parser->buffer[parser->idx] = 0;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*ppos += read;
|
||||
ret = read;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
return read;
|
||||
}
|
||||
|
||||
/* TODO add a seq_buf_to_buffer() */
|
||||
@@ -2405,10 +2374,10 @@ int __init register_tracer(struct tracer *type)
|
||||
mutex_unlock(&trace_types_lock);
|
||||
|
||||
if (ret || !default_bootup_tracer)
|
||||
goto out_unlock;
|
||||
return ret;
|
||||
|
||||
if (strncmp(default_bootup_tracer, type->name, MAX_TRACER_SIZE))
|
||||
goto out_unlock;
|
||||
return 0;
|
||||
|
||||
printk(KERN_INFO "Starting tracer '%s'\n", type->name);
|
||||
/* Do we want this tracer to start on bootup? */
|
||||
@@ -2420,8 +2389,7 @@ int __init register_tracer(struct tracer *type)
|
||||
/* disable other selftests, since this will break it. */
|
||||
disable_tracing_selftest("running a tracer");
|
||||
|
||||
out_unlock:
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tracing_reset_cpu(struct array_buffer *buf, int cpu)
|
||||
@@ -2498,9 +2466,8 @@ void tracing_reset_all_online_cpus_unlocked(void)
|
||||
|
||||
void tracing_reset_all_online_cpus(void)
|
||||
{
|
||||
mutex_lock(&trace_types_lock);
|
||||
guard(mutex)(&trace_types_lock);
|
||||
tracing_reset_all_online_cpus_unlocked();
|
||||
mutex_unlock(&trace_types_lock);
|
||||
}
|
||||
|
||||
int is_tracing_stopped(void)
|
||||
@@ -2511,18 +2478,17 @@ int is_tracing_stopped(void)
|
||||
static void tracing_start_tr(struct trace_array *tr)
|
||||
{
|
||||
struct trace_buffer *buffer;
|
||||
unsigned long flags;
|
||||
|
||||
if (tracing_disabled)
|
||||
return;
|
||||
|
||||
raw_spin_lock_irqsave(&tr->start_lock, flags);
|
||||
guard(raw_spinlock_irqsave)(&tr->start_lock);
|
||||
if (--tr->stop_count) {
|
||||
if (WARN_ON_ONCE(tr->stop_count < 0)) {
|
||||
/* Someone screwed up their debugging */
|
||||
tr->stop_count = 0;
|
||||
}
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prevent the buffers from switching */
|
||||
@@ -2539,9 +2505,6 @@ static void tracing_start_tr(struct trace_array *tr)
|
||||
#endif
|
||||
|
||||
arch_spin_unlock(&tr->max_lock);
|
||||
|
||||
out:
|
||||
raw_spin_unlock_irqrestore(&tr->start_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2559,11 +2522,10 @@ void tracing_start(void)
|
||||
static void tracing_stop_tr(struct trace_array *tr)
|
||||
{
|
||||
struct trace_buffer *buffer;
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&tr->start_lock, flags);
|
||||
guard(raw_spinlock_irqsave)(&tr->start_lock);
|
||||
if (tr->stop_count++)
|
||||
goto out;
|
||||
return;
|
||||
|
||||
/* Prevent the buffers from switching */
|
||||
arch_spin_lock(&tr->max_lock);
|
||||
@@ -2579,9 +2541,6 @@ static void tracing_stop_tr(struct trace_array *tr)
|
||||
#endif
|
||||
|
||||
arch_spin_unlock(&tr->max_lock);
|
||||
|
||||
out:
|
||||
raw_spin_unlock_irqrestore(&tr->start_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2694,12 +2653,12 @@ void trace_buffered_event_enable(void)
|
||||
|
||||
per_cpu(trace_buffered_event, cpu) = event;
|
||||
|
||||
preempt_disable();
|
||||
if (cpu == smp_processor_id() &&
|
||||
__this_cpu_read(trace_buffered_event) !=
|
||||
per_cpu(trace_buffered_event, cpu))
|
||||
WARN_ON_ONCE(1);
|
||||
preempt_enable();
|
||||
scoped_guard(preempt,) {
|
||||
if (cpu == smp_processor_id() &&
|
||||
__this_cpu_read(trace_buffered_event) !=
|
||||
per_cpu(trace_buffered_event, cpu))
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3044,7 +3003,7 @@ static void __ftrace_trace_stack(struct trace_array *tr,
|
||||
skip++;
|
||||
#endif
|
||||
|
||||
preempt_disable_notrace();
|
||||
guard(preempt_notrace)();
|
||||
|
||||
stackidx = __this_cpu_inc_return(ftrace_stack_reserve) - 1;
|
||||
|
||||
@@ -3102,8 +3061,6 @@ static void __ftrace_trace_stack(struct trace_array *tr,
|
||||
/* Again, don't let gcc optimize things here */
|
||||
barrier();
|
||||
__this_cpu_dec(ftrace_stack_reserve);
|
||||
preempt_enable_notrace();
|
||||
|
||||
}
|
||||
|
||||
static inline void ftrace_trace_stack(struct trace_array *tr,
|
||||
@@ -3186,9 +3143,9 @@ ftrace_trace_userstack(struct trace_array *tr,
|
||||
* prevent recursion, since the user stack tracing may
|
||||
* trigger other kernel events.
|
||||
*/
|
||||
preempt_disable();
|
||||
guard(preempt)();
|
||||
if (__this_cpu_read(user_stack_count))
|
||||
goto out;
|
||||
return;
|
||||
|
||||
__this_cpu_inc(user_stack_count);
|
||||
|
||||
@@ -3206,8 +3163,6 @@ ftrace_trace_userstack(struct trace_array *tr,
|
||||
|
||||
out_drop_count:
|
||||
__this_cpu_dec(user_stack_count);
|
||||
out:
|
||||
preempt_enable();
|
||||
}
|
||||
#else /* CONFIG_USER_STACKTRACE_SUPPORT */
|
||||
static void ftrace_trace_userstack(struct trace_array *tr,
|
||||
@@ -3389,7 +3344,7 @@ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args)
|
||||
pause_graph_tracing();
|
||||
|
||||
trace_ctx = tracing_gen_ctx();
|
||||
preempt_disable_notrace();
|
||||
guard(preempt_notrace)();
|
||||
|
||||
tbuffer = get_trace_buf();
|
||||
if (!tbuffer) {
|
||||
@@ -3404,26 +3359,23 @@ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args)
|
||||
|
||||
size = sizeof(*entry) + sizeof(u32) * len;
|
||||
buffer = tr->array_buffer.buffer;
|
||||
ring_buffer_nest_start(buffer);
|
||||
event = __trace_buffer_lock_reserve(buffer, TRACE_BPRINT, size,
|
||||
trace_ctx);
|
||||
if (!event)
|
||||
goto out;
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->ip = ip;
|
||||
entry->fmt = fmt;
|
||||
scoped_guard(ring_buffer_nest, buffer) {
|
||||
event = __trace_buffer_lock_reserve(buffer, TRACE_BPRINT, size,
|
||||
trace_ctx);
|
||||
if (!event)
|
||||
goto out_put;
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->ip = ip;
|
||||
entry->fmt = fmt;
|
||||
|
||||
memcpy(entry->buf, tbuffer, sizeof(u32) * len);
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
ftrace_trace_stack(tr, buffer, trace_ctx, 6, NULL);
|
||||
|
||||
out:
|
||||
ring_buffer_nest_end(buffer);
|
||||
memcpy(entry->buf, tbuffer, sizeof(u32) * len);
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
ftrace_trace_stack(tr, buffer, trace_ctx, 6, NULL);
|
||||
}
|
||||
out_put:
|
||||
put_trace_buf();
|
||||
|
||||
out_nobuffer:
|
||||
preempt_enable_notrace();
|
||||
unpause_graph_tracing();
|
||||
|
||||
return len;
|
||||
@@ -3447,7 +3399,7 @@ int __trace_array_vprintk(struct trace_buffer *buffer,
|
||||
pause_graph_tracing();
|
||||
|
||||
trace_ctx = tracing_gen_ctx();
|
||||
preempt_disable_notrace();
|
||||
guard(preempt_notrace)();
|
||||
|
||||
|
||||
tbuffer = get_trace_buf();
|
||||
@@ -3459,24 +3411,22 @@ int __trace_array_vprintk(struct trace_buffer *buffer,
|
||||
len = vscnprintf(tbuffer, TRACE_BUF_SIZE, fmt, args);
|
||||
|
||||
size = sizeof(*entry) + len + 1;
|
||||
ring_buffer_nest_start(buffer);
|
||||
event = __trace_buffer_lock_reserve(buffer, TRACE_PRINT, size,
|
||||
trace_ctx);
|
||||
if (!event)
|
||||
goto out;
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->ip = ip;
|
||||
|
||||
memcpy(&entry->buf, tbuffer, len + 1);
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
ftrace_trace_stack(printk_trace, buffer, trace_ctx, 6, NULL);
|
||||
scoped_guard(ring_buffer_nest, buffer) {
|
||||
event = __trace_buffer_lock_reserve(buffer, TRACE_PRINT, size,
|
||||
trace_ctx);
|
||||
if (!event)
|
||||
goto out;
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->ip = ip;
|
||||
|
||||
memcpy(&entry->buf, tbuffer, len + 1);
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
ftrace_trace_stack(printk_trace, buffer, trace_ctx, 6, NULL);
|
||||
}
|
||||
out:
|
||||
ring_buffer_nest_end(buffer);
|
||||
put_trace_buf();
|
||||
|
||||
out_nobuffer:
|
||||
preempt_enable_notrace();
|
||||
unpause_graph_tracing();
|
||||
|
||||
return len;
|
||||
@@ -4800,20 +4750,16 @@ int tracing_open_file_tr(struct inode *inode, struct file *filp)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&event_mutex);
|
||||
guard(mutex)(&event_mutex);
|
||||
|
||||
/* Fail if the file is marked for removal */
|
||||
if (file->flags & EVENT_FILE_FL_FREED) {
|
||||
trace_array_put(file->tr);
|
||||
ret = -ENODEV;
|
||||
return -ENODEV;
|
||||
} else {
|
||||
event_file_get(file);
|
||||
}
|
||||
|
||||
mutex_unlock(&event_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
filp->private_data = inode->i_private;
|
||||
|
||||
return 0;
|
||||
@@ -5090,7 +5036,7 @@ tracing_cpumask_read(struct file *filp, char __user *ubuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct trace_array *tr = file_inode(filp)->i_private;
|
||||
char *mask_str;
|
||||
char *mask_str __free(kfree) = NULL;
|
||||
int len;
|
||||
|
||||
len = snprintf(NULL, 0, "%*pb\n",
|
||||
@@ -5101,16 +5047,10 @@ tracing_cpumask_read(struct file *filp, char __user *ubuf,
|
||||
|
||||
len = snprintf(mask_str, len, "%*pb\n",
|
||||
cpumask_pr_args(tr->tracing_cpumask));
|
||||
if (len >= count) {
|
||||
count = -EINVAL;
|
||||
goto out_err;
|
||||
}
|
||||
count = simple_read_from_buffer(ubuf, count, ppos, mask_str, len);
|
||||
if (len >= count)
|
||||
return -EINVAL;
|
||||
|
||||
out_err:
|
||||
kfree(mask_str);
|
||||
|
||||
return count;
|
||||
return simple_read_from_buffer(ubuf, count, ppos, mask_str, len);
|
||||
}
|
||||
|
||||
int tracing_set_cpumask(struct trace_array *tr,
|
||||
@@ -5957,9 +5897,9 @@ tracing_set_trace_read(struct file *filp, char __user *ubuf,
|
||||
char buf[MAX_TRACER_SIZE+2];
|
||||
int r;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
r = sprintf(buf, "%s\n", tr->current_trace->name);
|
||||
mutex_unlock(&trace_types_lock);
|
||||
scoped_guard(mutex, &trace_types_lock) {
|
||||
r = sprintf(buf, "%s\n", tr->current_trace->name);
|
||||
}
|
||||
|
||||
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
|
||||
}
|
||||
@@ -6261,15 +6201,13 @@ int tracing_update_buffers(struct trace_array *tr)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
guard(mutex)(&trace_types_lock);
|
||||
|
||||
update_last_data(tr);
|
||||
|
||||
if (!tr->ring_buffer_expanded)
|
||||
ret = __tracing_resize_ring_buffer(tr, trace_buf_size,
|
||||
RING_BUFFER_ALL_CPUS);
|
||||
mutex_unlock(&trace_types_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -6566,7 +6504,7 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
guard(mutex)(&trace_types_lock);
|
||||
cpu = tracing_get_cpu(inode);
|
||||
ret = open_pipe_on_cpu(tr, cpu);
|
||||
if (ret)
|
||||
@@ -6610,7 +6548,6 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp)
|
||||
|
||||
tr->trace_ref++;
|
||||
|
||||
mutex_unlock(&trace_types_lock);
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
@@ -6619,7 +6556,6 @@ fail_alloc_iter:
|
||||
close_pipe_on_cpu(tr, cpu);
|
||||
fail_pipe_on_cpu:
|
||||
__trace_array_put(tr);
|
||||
mutex_unlock(&trace_types_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -6628,14 +6564,13 @@ static int tracing_release_pipe(struct inode *inode, struct file *file)
|
||||
struct trace_iterator *iter = file->private_data;
|
||||
struct trace_array *tr = inode->i_private;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
scoped_guard(mutex, &trace_types_lock) {
|
||||
tr->trace_ref--;
|
||||
|
||||
tr->trace_ref--;
|
||||
|
||||
if (iter->trace->pipe_close)
|
||||
iter->trace->pipe_close(iter);
|
||||
close_pipe_on_cpu(tr, iter->cpu_file);
|
||||
mutex_unlock(&trace_types_lock);
|
||||
if (iter->trace->pipe_close)
|
||||
iter->trace->pipe_close(iter);
|
||||
close_pipe_on_cpu(tr, iter->cpu_file);
|
||||
}
|
||||
|
||||
free_trace_iter_content(iter);
|
||||
kfree(iter);
|
||||
@@ -7438,7 +7373,7 @@ int tracing_set_clock(struct trace_array *tr, const char *clockstr)
|
||||
if (i == ARRAY_SIZE(trace_clocks))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
guard(mutex)(&trace_types_lock);
|
||||
|
||||
tr->clock_id = i;
|
||||
|
||||
@@ -7462,8 +7397,6 @@ int tracing_set_clock(struct trace_array *tr, const char *clockstr)
|
||||
tscratch->clock_id = i;
|
||||
}
|
||||
|
||||
mutex_unlock(&trace_types_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -7515,15 +7448,13 @@ static int tracing_time_stamp_mode_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct trace_array *tr = m->private;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
guard(mutex)(&trace_types_lock);
|
||||
|
||||
if (ring_buffer_time_stamp_abs(tr->array_buffer.buffer))
|
||||
seq_puts(m, "delta [absolute]\n");
|
||||
else
|
||||
seq_puts(m, "[delta] absolute\n");
|
||||
|
||||
mutex_unlock(&trace_types_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -8111,14 +8042,14 @@ static void clear_tracing_err_log(struct trace_array *tr)
|
||||
{
|
||||
struct tracing_log_err *err, *next;
|
||||
|
||||
mutex_lock(&tracing_err_log_lock);
|
||||
guard(mutex)(&tracing_err_log_lock);
|
||||
|
||||
list_for_each_entry_safe(err, next, &tr->err_log, list) {
|
||||
list_del(&err->list);
|
||||
free_tracing_log_err(err);
|
||||
}
|
||||
|
||||
tr->n_err_log_entries = 0;
|
||||
mutex_unlock(&tracing_err_log_lock);
|
||||
}
|
||||
|
||||
static void *tracing_err_log_seq_start(struct seq_file *m, loff_t *pos)
|
||||
@@ -8389,7 +8320,7 @@ static int tracing_buffers_release(struct inode *inode, struct file *file)
|
||||
struct ftrace_buffer_info *info = file->private_data;
|
||||
struct trace_iterator *iter = &info->iter;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
guard(mutex)(&trace_types_lock);
|
||||
|
||||
iter->tr->trace_ref--;
|
||||
|
||||
@@ -8400,8 +8331,6 @@ static int tracing_buffers_release(struct inode *inode, struct file *file)
|
||||
info->spare_cpu, info->spare);
|
||||
kvfree(info);
|
||||
|
||||
mutex_unlock(&trace_types_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -8609,14 +8538,13 @@ static long tracing_buffers_ioctl(struct file *file, unsigned int cmd, unsigned
|
||||
* An ioctl call with cmd 0 to the ring buffer file will wake up all
|
||||
* waiters
|
||||
*/
|
||||
mutex_lock(&trace_types_lock);
|
||||
guard(mutex)(&trace_types_lock);
|
||||
|
||||
/* Make sure the waiters see the new wait_index */
|
||||
(void)atomic_fetch_inc_release(&iter->wait_index);
|
||||
|
||||
ring_buffer_wake_waiters(iter->array_buffer->buffer, iter->cpu_file);
|
||||
|
||||
mutex_unlock(&trace_types_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -8957,12 +8885,12 @@ ftrace_trace_snapshot_callback(struct trace_array *tr, struct ftrace_hash *hash,
|
||||
out_reg:
|
||||
ret = tracing_arm_snapshot(tr);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
ret = register_ftrace_function_probe(glob, tr, ops, count);
|
||||
if (ret < 0)
|
||||
tracing_disarm_snapshot(tr);
|
||||
out:
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
@@ -9106,10 +9034,9 @@ trace_options_write(struct file *filp, const char __user *ubuf, size_t cnt,
|
||||
return -EINVAL;
|
||||
|
||||
if (!!(topt->flags->val & topt->opt->bit) != val) {
|
||||
mutex_lock(&trace_types_lock);
|
||||
guard(mutex)(&trace_types_lock);
|
||||
ret = __set_tracer_option(topt->tr, topt->flags,
|
||||
topt->opt, !val);
|
||||
mutex_unlock(&trace_types_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@@ -9418,7 +9345,7 @@ rb_simple_write(struct file *filp, const char __user *ubuf,
|
||||
return ret;
|
||||
|
||||
if (buffer) {
|
||||
mutex_lock(&trace_types_lock);
|
||||
guard(mutex)(&trace_types_lock);
|
||||
if (!!val == tracer_tracing_is_on(tr)) {
|
||||
val = 0; /* do nothing */
|
||||
} else if (val) {
|
||||
@@ -9432,7 +9359,6 @@ rb_simple_write(struct file *filp, const char __user *ubuf,
|
||||
/* Wake up any waiters */
|
||||
ring_buffer_wake_waiters(buffer, RING_BUFFER_ALL_CPUS);
|
||||
}
|
||||
mutex_unlock(&trace_types_lock);
|
||||
}
|
||||
|
||||
(*ppos)++;
|
||||
@@ -9816,10 +9742,9 @@ static void __update_tracer_options(struct trace_array *tr)
|
||||
|
||||
static void update_tracer_options(struct trace_array *tr)
|
||||
{
|
||||
mutex_lock(&trace_types_lock);
|
||||
guard(mutex)(&trace_types_lock);
|
||||
tracer_options_updated = true;
|
||||
__update_tracer_options(tr);
|
||||
mutex_unlock(&trace_types_lock);
|
||||
}
|
||||
|
||||
/* Must have trace_types_lock held */
|
||||
@@ -9841,11 +9766,10 @@ struct trace_array *trace_array_find_get(const char *instance)
|
||||
{
|
||||
struct trace_array *tr;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
guard(mutex)(&trace_types_lock);
|
||||
tr = trace_array_find(instance);
|
||||
if (tr)
|
||||
tr->ref++;
|
||||
mutex_unlock(&trace_types_lock);
|
||||
|
||||
return tr;
|
||||
}
|
||||
@@ -10376,7 +10300,7 @@ bool module_exists(const char *module)
|
||||
{
|
||||
/* All modules have the symbol __this_module */
|
||||
static const char this_mod[] = "__this_module";
|
||||
char modname[MAX_PARAM_PREFIX_LEN + sizeof(this_mod) + 2];
|
||||
char modname[MODULE_NAME_LEN + sizeof(this_mod) + 2];
|
||||
unsigned long val;
|
||||
int n;
|
||||
|
||||
@@ -10803,7 +10727,8 @@ ssize_t trace_parse_run_command(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *ppos,
|
||||
int (*createfn)(const char *))
|
||||
{
|
||||
char *kbuf, *buf, *tmp;
|
||||
char *kbuf __free(kfree) = NULL;
|
||||
char *buf, *tmp;
|
||||
int ret = 0;
|
||||
size_t done = 0;
|
||||
size_t size;
|
||||
@@ -10818,10 +10743,9 @@ ssize_t trace_parse_run_command(struct file *file, const char __user *buffer,
|
||||
if (size >= WRITE_BUFSIZE)
|
||||
size = WRITE_BUFSIZE - 1;
|
||||
|
||||
if (copy_from_user(kbuf, buffer + done, size)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
if (copy_from_user(kbuf, buffer + done, size))
|
||||
return -EFAULT;
|
||||
|
||||
kbuf[size] = '\0';
|
||||
buf = kbuf;
|
||||
do {
|
||||
@@ -10837,8 +10761,7 @@ ssize_t trace_parse_run_command(struct file *file, const char __user *buffer,
|
||||
/* This can accept WRITE_BUFSIZE - 2 ('\n' + '\0') */
|
||||
pr_warn("Line length is too long: Should be less than %d\n",
|
||||
WRITE_BUFSIZE - 2);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
done += size;
|
||||
@@ -10851,17 +10774,12 @@ ssize_t trace_parse_run_command(struct file *file, const char __user *buffer,
|
||||
|
||||
ret = createfn(buf);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
buf += size;
|
||||
|
||||
} while (done < count);
|
||||
}
|
||||
ret = done;
|
||||
|
||||
out:
|
||||
kfree(kbuf);
|
||||
|
||||
return ret;
|
||||
return done;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TRACER_MAX_TRACE
|
||||
@@ -11064,7 +10982,7 @@ __init static int tracer_alloc_buffers(void)
|
||||
BUILD_BUG_ON(TRACE_ITER_LAST_BIT > TRACE_FLAGS_MAX_SIZE);
|
||||
|
||||
if (!alloc_cpumask_var(&tracing_buffer_mask, GFP_KERNEL))
|
||||
goto out;
|
||||
return -ENOMEM;
|
||||
|
||||
if (!alloc_cpumask_var(&global_trace.tracing_cpumask, GFP_KERNEL))
|
||||
goto out_free_buffer_mask;
|
||||
@@ -11182,7 +11100,6 @@ out_free_cpumask:
|
||||
free_cpumask_var(global_trace.tracing_cpumask);
|
||||
out_free_buffer_mask:
|
||||
free_cpumask_var(tracing_buffer_mask);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -536,12 +536,12 @@ static notrace void trace_event_raw_event_synth(void *__data,
|
||||
* is being performed within another event.
|
||||
*/
|
||||
buffer = trace_file->tr->array_buffer.buffer;
|
||||
ring_buffer_nest_start(buffer);
|
||||
guard(ring_buffer_nest)(buffer);
|
||||
|
||||
entry = trace_event_buffer_reserve(&fbuffer, trace_file,
|
||||
sizeof(*entry) + fields_size);
|
||||
if (!entry)
|
||||
goto out;
|
||||
return;
|
||||
|
||||
for (i = 0, n_u64 = 0; i < event->n_fields; i++) {
|
||||
val_idx = var_ref_idx[i];
|
||||
@@ -584,8 +584,6 @@ static notrace void trace_event_raw_event_synth(void *__data,
|
||||
}
|
||||
|
||||
trace_event_buffer_commit(&fbuffer);
|
||||
out:
|
||||
ring_buffer_nest_end(buffer);
|
||||
}
|
||||
|
||||
static void free_synth_event_print_fmt(struct trace_event_call *call)
|
||||
|
||||
@@ -701,6 +701,7 @@ void print_function_args(struct trace_seq *s, unsigned long *args,
|
||||
struct btf *btf;
|
||||
s32 tid, nr = 0;
|
||||
int a, p, x;
|
||||
u16 encode;
|
||||
|
||||
trace_seq_printf(s, "(");
|
||||
|
||||
@@ -744,7 +745,12 @@ void print_function_args(struct trace_seq *s, unsigned long *args,
|
||||
trace_seq_printf(s, "0x%lx", arg);
|
||||
break;
|
||||
case BTF_KIND_INT:
|
||||
trace_seq_printf(s, "%ld", arg);
|
||||
encode = btf_int_encoding(t);
|
||||
/* Print unsigned ints as hex */
|
||||
if (encode & BTF_INT_SIGNED)
|
||||
trace_seq_printf(s, "%ld", arg);
|
||||
else
|
||||
trace_seq_printf(s, "0x%lx", arg);
|
||||
break;
|
||||
case BTF_KIND_ENUM:
|
||||
trace_seq_printf(s, "%ld", arg);
|
||||
|
||||
+7
-9
@@ -199,18 +199,16 @@ void put_ucounts(struct ucounts *ucounts)
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool atomic_long_inc_below(atomic_long_t *v, int u)
|
||||
static inline bool atomic_long_inc_below(atomic_long_t *v, long u)
|
||||
{
|
||||
long c, old;
|
||||
c = atomic_long_read(v);
|
||||
for (;;) {
|
||||
long c = atomic_long_read(v);
|
||||
|
||||
do {
|
||||
if (unlikely(c >= u))
|
||||
return false;
|
||||
old = atomic_long_cmpxchg(v, c, c+1);
|
||||
if (likely(old == c))
|
||||
return true;
|
||||
c = old;
|
||||
}
|
||||
} while (!atomic_long_try_cmpxchg(v, &c, c+1));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct ucounts *inc_ucount(struct user_namespace *ns, kuid_t uid,
|
||||
|
||||
Reference in New Issue
Block a user