mirror of
https://github.com/hardkernel/kernel_common_drivers.git
synced 2026-06-25 12:03:48 +09:00
ba1bb225eb
PD#SWPL-179411 Problem: BUG: sleeping function called from invalid context at kernel/sched/completion.c in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 1, name: swapper/0 Preemption disabled at: [<c0428468>] queue_stop_cpus_work+0x60/0x1bc [<c030dc24>] show_stack+0x28/0x2c [<c105e624>] dump_stack_lvl+0x48/0x54 [<c0371660>] ___might_sleep+0x1c4/0x1e8 [<c10621b8>] wait_for_completion+0x28/0x58 [<c0428180>] stop_machine_cpuslocked+0x10c/0x208 [<c106ac28>] patch_text+0x20/0x28 [<c0501254>] __jump_label_update+0x7c/0xec [<c05002b8>] static_key_enable_cpuslocked+0x8c/0x100 [<c050034c>] static_key_enable+0x20/0x28 [<c0441acc>] tracepoint_add_func+0x38c/0x400 [<c0441c00>] tracepoint_probe_register+0x54/0x6c [<c0db6974>] ftrace_ramoops_init+0x288/0x2fc [<c163cb30>] aml_iotrace_init+0x46c/0x550 [<c03020a0>] do_one_initcall+0x104/0x364 [<c1601f90>] do_initcall_level+0xa0/0x15c [<c1601ec0>] do_initcalls+0x58/0x88 [<c1601d04>] kernel_init_freeable+0x114/0x160 [<c105fe24>] kernel_init+0x20/0x1c4 [<c0300134>] ret_from_fork+0x14/0x40 Solution: add preempt protections at hooks Verify: t5w Change-Id: Ib6a13e104aeb697e84e331152f6ea41ef6d11b35 Signed-off-by: song.han <song.han@amlogic.com>
192 lines
5.9 KiB
C
192 lines
5.9 KiB
C
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#ifndef __AML_IOTRACE_H
|
|
#define __AML_IOTRACE_H
|
|
|
|
#include <linux/preempt.h>
|
|
|
|
/*
|
|
* We resue the preemption counter unsed bits for iotrace counter.
|
|
*
|
|
* Now preemption counter bitmask meaning:
|
|
* - bit 0-7 preemption count
|
|
* - bit 8-15 softirq count
|
|
* - bit 16-19 hardirq count
|
|
* - bit 20-23 nmi count
|
|
*
|
|
* Use 4 bits for iotrace counter:
|
|
* - bit 24-27 iotrace count
|
|
*/
|
|
#define PREEMPT_IOTRACE_BITS 4
|
|
#define PREEMPT_IOTRACE_SHIFT (NMI_SHIFT + NMI_BITS)
|
|
#define PREEMPT_IOTRACE_MASK (__IRQ_MASK(PREEMPT_IOTRACE_BITS) << PREEMPT_IOTRACE_SHIFT)
|
|
#define PREEMPT_IOTRACE_OFFSET (1UL << PREEMPT_IOTRACE_SHIFT)
|
|
|
|
#define iotrace_count() (preempt_count() & PREEMPT_IOTRACE_MASK)
|
|
|
|
/*
|
|
* We calls preempt_iotrace_enter() at the start of iotrace begin hook,
|
|
* and calls preempt_iotrace_exit() at the end of iotrace post hook.
|
|
*
|
|
* At the beginning of iotrace function initialization:
|
|
*
|
|
* - Begin hook cannot do preempt_count operation before post hook have registered.
|
|
* Or we may got a bad preempt_count and caused kernel exception.
|
|
*
|
|
* - Attention, we must be very careful in post hook, and we must have a very
|
|
* clear understanding of what have done in begin hook. Whether preempt_iotrace_enter()
|
|
* executed or not, whether pstore_io_save() executed and percpu irqflag have
|
|
* valid data.
|
|
*
|
|
* To archive this goal:
|
|
* - We use <ramoops_ftrace_en> to ensure begin/post hooks called in pairs.
|
|
* We register begin/post hooks first then set <ramoops_ftrace_en> to 1,
|
|
* and in hooks must return directly before <ramoops_ftrace_en> becomes to 1.
|
|
*
|
|
* - Set <ramoops_ftrace_en> to 1 may happened between a register begin/post
|
|
* hooks, this means begin hooks may saw ramoops_ftrace_en=0 but post hook was 1.
|
|
* So in post hook, we must check iotrace_count() first to decide whether
|
|
* begin hook exceuted or not.
|
|
*
|
|
* - In post hook, pstore_io_save() will restore percpu irqflag where saved in
|
|
* begin hook, we must ensure this percpu irqflag is valid, or will caused
|
|
* bad CPU status and then undefined instruction exception. We also use <ramoops_ftrace_en>
|
|
* to ensure this data valid.
|
|
*
|
|
* - If only register post hook first and then begin hook without ramoops_ftrace_en
|
|
* restriction, we can't enusre percpu irqflag is valid. Because in begin hook
|
|
* pstore_io_save() may haven't saved irqflag when saw ramoops_ftrace_en=0, but
|
|
* in post hook pstore_io_save() may tried to rstore irqflag when saw ramoops_ftrace_en=1.
|
|
*/
|
|
#define preempt_iotrace_enter() preempt_count_add(PREEMPT_IOTRACE_OFFSET)
|
|
#define preempt_iotrace_exit() preempt_count_sub(PREEMPT_IOTRACE_OFFSET)
|
|
|
|
extern int ramoops_io_stack;
|
|
extern int ramoops_io_skip;
|
|
extern int ramoops_ftrace_en;
|
|
extern int ramoops_trace_mask;
|
|
DECLARE_PER_CPU(bool, vsync_iotrace_cut);
|
|
DECLARE_PER_CPU(bool, frc_iotrace_cut);
|
|
DECLARE_PER_CPU(bool, amvecm_iotrace_cut);
|
|
DECLARE_PER_CPU(bool, usb_iotrace_cut);
|
|
|
|
#define TRACE_MASK_IO 0x1
|
|
#define TRACE_MASK_SCHED 0x2
|
|
#define TRACE_MASK_IRQ 0x4
|
|
#define TRACE_MASK_SMC 0x8
|
|
#define TRACE_MASK_MISC 0x10
|
|
|
|
void notrace __nocfi pstore_io_save(unsigned long reg, unsigned long val, unsigned int flag,
|
|
unsigned long *irq_flags);
|
|
|
|
#ifdef CONFIG_SHADOW_CALL_STACK
|
|
unsigned long get_prev_lr_val(unsigned long lr, unsigned long offset);
|
|
#endif
|
|
|
|
enum iotrace_record_type_id {
|
|
RECORD_TYPE_IO_R = 0x0,
|
|
RECORD_TYPE_IO_W = 0x1,
|
|
RECORD_TYPE_IO_R_END = 0x2,
|
|
RECORD_TYPE_IO_W_END = 0x3,
|
|
RECORD_TYPE_SCHED_SWITCH = 0x4,
|
|
RECORD_TYPE_ISR_IN = 0x5,
|
|
RECORD_TYPE_ISR_OUT = 0x6,
|
|
RECORD_TYPE_SMC_IN = 0x7,
|
|
RECORD_TYPE_SMC_OUT = 0x8,
|
|
RECORD_TYPE_SMC_NORET_IN = 0x9,
|
|
RECORD_TYPE_USB_IN = 0xa,
|
|
RECORD_TYPE_USB_OUT = 0xb,
|
|
RECORD_TYPE_FRC_INPUT_IN = 0xc,
|
|
RECORD_TYPE_FRC_INPUT_OUT = 0xd,
|
|
RECORD_TYPE_FRC_OUTPUT_IN = 0xe,
|
|
RECORD_TYPE_FRC_OUTPUT_OUT = 0xf,
|
|
RECORD_TYPE_VSYNC_IN = 0x10,
|
|
RECORD_TYPE_VSYNC_OUT = 0x11,
|
|
RECORD_TYPE_AMVECM_IN = 0x12,
|
|
RECORD_TYPE_AMVECM_OUT = 0x13,
|
|
RECORD_TYPE_MASK = 0xff
|
|
};
|
|
|
|
#if IS_ENABLED(CONFIG_AMLOGIC_DEBUG_IOTRACE)
|
|
#define pstore_ftrace_io_wr(reg, val, irqflg) \
|
|
pstore_io_save(reg, val, RECORD_TYPE_IO_W, &(irqflg))
|
|
|
|
#define pstore_ftrace_io_wr_end(reg, val, irqflg) \
|
|
pstore_io_save(reg, val, RECORD_TYPE_IO_W_END, &(irqflg))
|
|
|
|
#define pstore_ftrace_io_rd(reg, irqflg) \
|
|
pstore_io_save(reg, 0, RECORD_TYPE_IO_R, &(irqflg))
|
|
|
|
#define pstore_ftrace_io_rd_end(reg, val, irqflg) \
|
|
pstore_io_save(reg, val, RECORD_TYPE_IO_R_END, &(irqflg))
|
|
|
|
#else
|
|
#define pstore_ftrace_io_wr(reg, val, irqflg) do { } while (0)
|
|
#define pstore_ftrace_io_rd(reg, irqflg) do { } while (0)
|
|
#define pstore_ftrace_io_wr_end(reg, val, irqflg) do { } while (0)
|
|
#define pstore_ftrace_io_rd_end(reg, val, irqflg) do { } while (0)
|
|
|
|
#endif /* CONFIG_AMLOGIC_DEBUG_IOTRACE */
|
|
|
|
enum aml_pstore_type_id {
|
|
AML_PSTORE_TYPE_IO = 0,
|
|
AML_PSTORE_TYPE_SCHED = 1,
|
|
AML_PSTORE_TYPE_IRQ = 2,
|
|
AML_PSTORE_TYPE_SMC = 3,
|
|
AML_PSTORE_TYPE_MISC = 4,
|
|
AML_PSTORE_TYPE_MAX
|
|
};
|
|
|
|
struct iotrace_record {
|
|
u16 magic; /* 0xabcd */
|
|
u16 pid;
|
|
u8 cpu;
|
|
struct {
|
|
u8 in_irq:1;
|
|
u8 in_softirq:1;
|
|
u8 irq_disabled:1;
|
|
u8 reserved:5;
|
|
} flag;
|
|
u16 type;
|
|
u64 time;
|
|
union {
|
|
struct {
|
|
unsigned int reg;
|
|
unsigned int reg_val;
|
|
unsigned long ip;
|
|
unsigned long parent_ip;
|
|
}; //io
|
|
struct {
|
|
char curr_comm[10];
|
|
char next_comm[10];
|
|
u16 curr_pid;
|
|
u16 next_pid;
|
|
}; //sched
|
|
struct {
|
|
unsigned int irq;
|
|
}; //irq
|
|
struct {
|
|
unsigned long smcid;
|
|
unsigned long val;
|
|
}; //smc
|
|
struct {
|
|
unsigned long misc_data_1;
|
|
unsigned long misc_data_2;
|
|
unsigned long misc_data_3;
|
|
}; //misc
|
|
};
|
|
};
|
|
|
|
void aml_pstore_write(enum aml_pstore_type_id type, struct iotrace_record *rec,
|
|
unsigned int dis_irq, unsigned int io_flag);
|
|
|
|
void iotrace_misc_record_write(enum iotrace_record_type_id type, unsigned long misc_data_1,
|
|
unsigned long misc_data_2, unsigned long misc_data_3);
|
|
|
|
int ftrace_ramoops_init(void);
|
|
#endif
|
|
|