Files
kernel_common_drivers/include/linux/amlogic/aml_iotrace.h
T
song.han ba1bb225eb iotrace: add preempt protections at hooks [1/1]
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>
2024-08-13 05:44:40 -07:00

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