mirror of
https://github.com/a52xq-dev/kernel.git
synced 2026-10-18 21:58:11 +07:00
Tracing updates for 4.15:
- Now allow module init functions to be traced
- Clean up some unused or not used by config events (saves space)
- Clean up of trace histogram code
- Add support for preempt and interrupt enabled/disable events
- Other various clean ups
-----BEGIN PGP SIGNATURE-----
iQHIBAABCgAyFiEEPm6V/WuN2kyArTUe1a05Y9njSUkFAloPGgkUHHJvc3RlZHRA
Z29vZG1pcy5vcmcACgkQ1a05Y9njSUmfaAwAjge5FWBCBQeby8tVuw4RGAorRgl5
IFuijFSygcKRMhQFP6B+haHsezeCbNaBBtIncXhoJGDC5XuhUhr9foYf1SChEmYp
tCOK2o71FgZ8yG539IYCVjG9cJZxPLM0OI7RQ8hcMETAr+eiXPXxHrmrm9kdBtYM
ZAQERvqI5yu2HWIb87KBc38H0rgYrOJKZt9Rx20as/aqAME7hFvYErFlcnxdmHo+
LmovJOQBCTicNJ4TXJc418JaUWi9cm/A3uhW3o5aLMoRAxCc/8FD+dq2rg4qlHDH
tOtK6pwIPHfqRZ3nMLXXWhaa+w+swsxBOnegkvgP2xCyibKjFgh9kzcpaj41w3x1
0FCfvS7flx9ob//fAB8kxLvJyY5p3Qp3xdvj0+gp2qa3Ga5lSqcMzS419TLY1Yfa
Jpi2oAagDqP94m0EjAGTkhZMOrsFIDr49g3h7nqz3T3Z54luyXniDoYoO11d+dUF
vCUiIJz/PsQIE3NVViZiaRtcLVXneLHISmnz
=h3F2
-----END PGP SIGNATURE-----
Merge tag 'trace-v4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace
Pull tracing updates from
- allow module init functions to be traced
- clean up some unused or not used by config events (saves space)
- clean up of trace histogram code
- add support for preempt and interrupt enabled/disable events
- other various clean ups
* tag 'trace-v4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace: (30 commits)
tracing, thermal: Hide cpu cooling trace events when not in use
tracing, thermal: Hide devfreq trace events when not in use
ftrace: Kill FTRACE_OPS_FL_PER_CPU
perf/ftrace: Small cleanup
perf/ftrace: Fix function trace events
perf/ftrace: Revert ("perf/ftrace: Fix double traces of perf on ftrace:function")
tracing, dma-buf: Remove unused trace event dma_fence_annotate_wait_on
tracing, memcg, vmscan: Hide trace events when not in use
tracing/xen: Hide events that are not used when X86_PAE is not defined
tracing: mark trace_test_buffer as __maybe_unused
printk: Remove superfluous memory barriers from printk_safe
ftrace: Clear hashes of stale ips of init memory
tracing: Add support for preempt and irq enable/disable events
tracing: Prepare to add preempt and irq trace events
ftrace/kallsyms: Have /proc/kallsyms show saved mod init functions
ftrace: Add freeing algorithm to free ftrace_mod_maps
ftrace: Save module init functions kallsyms symbols for tracing
ftrace: Allow module init functions to be traced
ftrace: Add a ftrace_free_mem() function for modules to use
tracing: Reimplement log2
...
This commit is contained in:
commit
2dcd9c71c1
32 changed files with 903 additions and 518 deletions
|
|
@ -27,7 +27,6 @@
|
|||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/dma_fence.h>
|
||||
|
||||
EXPORT_TRACEPOINT_SYMBOL(dma_fence_annotate_wait_on);
|
||||
EXPORT_TRACEPOINT_SYMBOL(dma_fence_emit);
|
||||
EXPORT_TRACEPOINT_SYMBOL(dma_fence_enable_signal);
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,30 @@ static inline void early_trace_init(void) { }
|
|||
struct module;
|
||||
struct ftrace_hash;
|
||||
|
||||
#if defined(CONFIG_FUNCTION_TRACER) && defined(CONFIG_MODULES) && \
|
||||
defined(CONFIG_DYNAMIC_FTRACE)
|
||||
const char *
|
||||
ftrace_mod_address_lookup(unsigned long addr, unsigned long *size,
|
||||
unsigned long *off, char **modname, char *sym);
|
||||
int ftrace_mod_get_kallsym(unsigned int symnum, unsigned long *value,
|
||||
char *type, char *name,
|
||||
char *module_name, int *exported);
|
||||
#else
|
||||
static inline const char *
|
||||
ftrace_mod_address_lookup(unsigned long addr, unsigned long *size,
|
||||
unsigned long *off, char **modname, char *sym)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline int ftrace_mod_get_kallsym(unsigned int symnum, unsigned long *value,
|
||||
char *type, char *name,
|
||||
char *module_name, int *exported)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef CONFIG_FUNCTION_TRACER
|
||||
|
||||
extern int ftrace_enabled;
|
||||
|
|
@ -79,10 +103,6 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops);
|
|||
* ENABLED - set/unset when ftrace_ops is registered/unregistered
|
||||
* DYNAMIC - set when ftrace_ops is registered to denote dynamically
|
||||
* allocated ftrace_ops which need special care
|
||||
* PER_CPU - set manualy by ftrace_ops user to denote the ftrace_ops
|
||||
* could be controlled by following calls:
|
||||
* ftrace_function_local_enable
|
||||
* ftrace_function_local_disable
|
||||
* SAVE_REGS - The ftrace_ops wants regs saved at each function called
|
||||
* and passed to the callback. If this flag is set, but the
|
||||
* architecture does not support passing regs
|
||||
|
|
@ -126,21 +146,20 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops);
|
|||
enum {
|
||||
FTRACE_OPS_FL_ENABLED = 1 << 0,
|
||||
FTRACE_OPS_FL_DYNAMIC = 1 << 1,
|
||||
FTRACE_OPS_FL_PER_CPU = 1 << 2,
|
||||
FTRACE_OPS_FL_SAVE_REGS = 1 << 3,
|
||||
FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED = 1 << 4,
|
||||
FTRACE_OPS_FL_RECURSION_SAFE = 1 << 5,
|
||||
FTRACE_OPS_FL_STUB = 1 << 6,
|
||||
FTRACE_OPS_FL_INITIALIZED = 1 << 7,
|
||||
FTRACE_OPS_FL_DELETED = 1 << 8,
|
||||
FTRACE_OPS_FL_ADDING = 1 << 9,
|
||||
FTRACE_OPS_FL_REMOVING = 1 << 10,
|
||||
FTRACE_OPS_FL_MODIFYING = 1 << 11,
|
||||
FTRACE_OPS_FL_ALLOC_TRAMP = 1 << 12,
|
||||
FTRACE_OPS_FL_IPMODIFY = 1 << 13,
|
||||
FTRACE_OPS_FL_PID = 1 << 14,
|
||||
FTRACE_OPS_FL_RCU = 1 << 15,
|
||||
FTRACE_OPS_FL_TRACE_ARRAY = 1 << 16,
|
||||
FTRACE_OPS_FL_SAVE_REGS = 1 << 2,
|
||||
FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED = 1 << 3,
|
||||
FTRACE_OPS_FL_RECURSION_SAFE = 1 << 4,
|
||||
FTRACE_OPS_FL_STUB = 1 << 5,
|
||||
FTRACE_OPS_FL_INITIALIZED = 1 << 6,
|
||||
FTRACE_OPS_FL_DELETED = 1 << 7,
|
||||
FTRACE_OPS_FL_ADDING = 1 << 8,
|
||||
FTRACE_OPS_FL_REMOVING = 1 << 9,
|
||||
FTRACE_OPS_FL_MODIFYING = 1 << 10,
|
||||
FTRACE_OPS_FL_ALLOC_TRAMP = 1 << 11,
|
||||
FTRACE_OPS_FL_IPMODIFY = 1 << 12,
|
||||
FTRACE_OPS_FL_PID = 1 << 13,
|
||||
FTRACE_OPS_FL_RCU = 1 << 14,
|
||||
FTRACE_OPS_FL_TRACE_ARRAY = 1 << 15,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||
|
|
@ -152,8 +171,10 @@ struct ftrace_ops_hash {
|
|||
};
|
||||
|
||||
void ftrace_free_init_mem(void);
|
||||
void ftrace_free_mem(struct module *mod, void *start, void *end);
|
||||
#else
|
||||
static inline void ftrace_free_init_mem(void) { }
|
||||
static inline void ftrace_free_mem(struct module *mod, void *start, void *end) { }
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
@ -173,7 +194,6 @@ struct ftrace_ops {
|
|||
unsigned long flags;
|
||||
void *private;
|
||||
ftrace_func_t saved_func;
|
||||
int __percpu *disabled;
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||
struct ftrace_ops_hash local_hash;
|
||||
struct ftrace_ops_hash *func_hash;
|
||||
|
|
@ -205,55 +225,6 @@ int register_ftrace_function(struct ftrace_ops *ops);
|
|||
int unregister_ftrace_function(struct ftrace_ops *ops);
|
||||
void clear_ftrace_function(void);
|
||||
|
||||
/**
|
||||
* ftrace_function_local_enable - enable ftrace_ops on current cpu
|
||||
*
|
||||
* This function enables tracing on current cpu by decreasing
|
||||
* the per cpu control variable.
|
||||
* It must be called with preemption disabled and only on ftrace_ops
|
||||
* registered with FTRACE_OPS_FL_PER_CPU. If called without preemption
|
||||
* disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled.
|
||||
*/
|
||||
static inline void ftrace_function_local_enable(struct ftrace_ops *ops)
|
||||
{
|
||||
if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU)))
|
||||
return;
|
||||
|
||||
(*this_cpu_ptr(ops->disabled))--;
|
||||
}
|
||||
|
||||
/**
|
||||
* ftrace_function_local_disable - disable ftrace_ops on current cpu
|
||||
*
|
||||
* This function disables tracing on current cpu by increasing
|
||||
* the per cpu control variable.
|
||||
* It must be called with preemption disabled and only on ftrace_ops
|
||||
* registered with FTRACE_OPS_FL_PER_CPU. If called without preemption
|
||||
* disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled.
|
||||
*/
|
||||
static inline void ftrace_function_local_disable(struct ftrace_ops *ops)
|
||||
{
|
||||
if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU)))
|
||||
return;
|
||||
|
||||
(*this_cpu_ptr(ops->disabled))++;
|
||||
}
|
||||
|
||||
/**
|
||||
* ftrace_function_local_disabled - returns ftrace_ops disabled value
|
||||
* on current cpu
|
||||
*
|
||||
* This function returns value of ftrace_ops::disabled on current cpu.
|
||||
* It must be called with preemption disabled and only on ftrace_ops
|
||||
* registered with FTRACE_OPS_FL_PER_CPU. If called without preemption
|
||||
* disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled.
|
||||
*/
|
||||
static inline int ftrace_function_local_disabled(struct ftrace_ops *ops)
|
||||
{
|
||||
WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU));
|
||||
return *this_cpu_ptr(ops->disabled);
|
||||
}
|
||||
|
||||
extern void ftrace_stub(unsigned long a0, unsigned long a1,
|
||||
struct ftrace_ops *op, struct pt_regs *regs);
|
||||
|
||||
|
|
@ -271,6 +242,7 @@ static inline int ftrace_nr_registered_ops(void)
|
|||
static inline void clear_ftrace_function(void) { }
|
||||
static inline void ftrace_kill(void) { }
|
||||
static inline void ftrace_free_init_mem(void) { }
|
||||
static inline void ftrace_free_mem(struct module *mod, void *start, void *end) { }
|
||||
#endif /* CONFIG_FUNCTION_TRACER */
|
||||
|
||||
#ifdef CONFIG_STACK_TRACER
|
||||
|
|
@ -743,7 +715,8 @@ static inline unsigned long get_lock_parent_ip(void)
|
|||
static inline void time_hardirqs_off(unsigned long a0, unsigned long a1) { }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PREEMPT_TRACER
|
||||
#if defined(CONFIG_PREEMPT_TRACER) || \
|
||||
(defined(CONFIG_DEBUG_PREEMPT) && defined(CONFIG_PREEMPTIRQ_EVENTS))
|
||||
extern void trace_preempt_on(unsigned long a0, unsigned long a1);
|
||||
extern void trace_preempt_off(unsigned long a0, unsigned long a1);
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
/* These are for everybody (although not all archs will actually
|
||||
discard it in modules) */
|
||||
#define __init __section(.init.text) __cold __inittrace __latent_entropy
|
||||
#define __init __section(.init.text) __cold __latent_entropy
|
||||
#define __initdata __section(.init.data)
|
||||
#define __initconst __section(.init.rodata)
|
||||
#define __exitdata __section(.exit.data)
|
||||
|
|
@ -69,10 +69,8 @@
|
|||
|
||||
#ifdef MODULE
|
||||
#define __exitused
|
||||
#define __inittrace notrace
|
||||
#else
|
||||
#define __exitused __used
|
||||
#define __inittrace
|
||||
#endif
|
||||
|
||||
#define __exit __section(.exit.text) __exitused __cold notrace
|
||||
|
|
|
|||
|
|
@ -1169,7 +1169,7 @@ extern void perf_event_init(void);
|
|||
extern void perf_tp_event(u16 event_type, u64 count, void *record,
|
||||
int entry_size, struct pt_regs *regs,
|
||||
struct hlist_head *head, int rctx,
|
||||
struct task_struct *task, struct perf_event *event);
|
||||
struct task_struct *task);
|
||||
extern void perf_bp_event(struct perf_event *event, void *data);
|
||||
|
||||
#ifndef perf_misc_flags
|
||||
|
|
|
|||
|
|
@ -174,6 +174,11 @@ enum trace_reg {
|
|||
TRACE_REG_PERF_UNREGISTER,
|
||||
TRACE_REG_PERF_OPEN,
|
||||
TRACE_REG_PERF_CLOSE,
|
||||
/*
|
||||
* These (ADD/DEL) use a 'boolean' return value, where 1 (true) means a
|
||||
* custom action was taken and the default action is not to be
|
||||
* performed.
|
||||
*/
|
||||
TRACE_REG_PERF_ADD,
|
||||
TRACE_REG_PERF_DEL,
|
||||
#endif
|
||||
|
|
@ -542,9 +547,9 @@ void perf_trace_run_bpf_submit(void *raw_data, int size, int rctx,
|
|||
static inline void
|
||||
perf_trace_buf_submit(void *raw_data, int size, int rctx, u16 type,
|
||||
u64 count, struct pt_regs *regs, void *head,
|
||||
struct task_struct *task, struct perf_event *event)
|
||||
struct task_struct *task)
|
||||
{
|
||||
perf_tp_event(type, count, raw_data, size, regs, head, rctx, task, event);
|
||||
perf_tp_event(type, count, raw_data, size, regs, head, rctx, task);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -9,46 +9,6 @@
|
|||
|
||||
struct dma_fence;
|
||||
|
||||
TRACE_EVENT(dma_fence_annotate_wait_on,
|
||||
|
||||
/* fence: the fence waiting on f1, f1: the fence to be waited on. */
|
||||
TP_PROTO(struct dma_fence *fence, struct dma_fence *f1),
|
||||
|
||||
TP_ARGS(fence, f1),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(driver, fence->ops->get_driver_name(fence))
|
||||
__string(timeline, fence->ops->get_timeline_name(fence))
|
||||
__field(unsigned int, context)
|
||||
__field(unsigned int, seqno)
|
||||
|
||||
__string(waiting_driver, f1->ops->get_driver_name(f1))
|
||||
__string(waiting_timeline, f1->ops->get_timeline_name(f1))
|
||||
__field(unsigned int, waiting_context)
|
||||
__field(unsigned int, waiting_seqno)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(driver, fence->ops->get_driver_name(fence))
|
||||
__assign_str(timeline, fence->ops->get_timeline_name(fence))
|
||||
__entry->context = fence->context;
|
||||
__entry->seqno = fence->seqno;
|
||||
|
||||
__assign_str(waiting_driver, f1->ops->get_driver_name(f1))
|
||||
__assign_str(waiting_timeline, f1->ops->get_timeline_name(f1))
|
||||
__entry->waiting_context = f1->context;
|
||||
__entry->waiting_seqno = f1->seqno;
|
||||
|
||||
),
|
||||
|
||||
TP_printk("driver=%s timeline=%s context=%u seqno=%u " \
|
||||
"waits on driver=%s timeline=%s context=%u seqno=%u",
|
||||
__get_str(driver), __get_str(timeline), __entry->context,
|
||||
__entry->seqno,
|
||||
__get_str(waiting_driver), __get_str(waiting_timeline),
|
||||
__entry->waiting_context, __entry->waiting_seqno)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dma_fence,
|
||||
|
||||
TP_PROTO(struct dma_fence *fence),
|
||||
|
|
|
|||
70
include/trace/events/preemptirq.h
Normal file
70
include/trace/events/preemptirq.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#ifdef CONFIG_PREEMPTIRQ_EVENTS
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM preemptirq
|
||||
|
||||
#if !defined(_TRACE_PREEMPTIRQ_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_PREEMPTIRQ_H
|
||||
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/tracepoint.h>
|
||||
#include <linux/string.h>
|
||||
#include <asm/sections.h>
|
||||
|
||||
DECLARE_EVENT_CLASS(preemptirq_template,
|
||||
|
||||
TP_PROTO(unsigned long ip, unsigned long parent_ip),
|
||||
|
||||
TP_ARGS(ip, parent_ip),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, caller_offs)
|
||||
__field(u32, parent_offs)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->caller_offs = (u32)(ip - (unsigned long)_stext);
|
||||
__entry->parent_offs = (u32)(parent_ip - (unsigned long)_stext);
|
||||
),
|
||||
|
||||
TP_printk("caller=%pF parent=%pF",
|
||||
(void *)((unsigned long)(_stext) + __entry->caller_offs),
|
||||
(void *)((unsigned long)(_stext) + __entry->parent_offs))
|
||||
);
|
||||
|
||||
#ifndef CONFIG_PROVE_LOCKING
|
||||
DEFINE_EVENT(preemptirq_template, irq_disable,
|
||||
TP_PROTO(unsigned long ip, unsigned long parent_ip),
|
||||
TP_ARGS(ip, parent_ip));
|
||||
|
||||
DEFINE_EVENT(preemptirq_template, irq_enable,
|
||||
TP_PROTO(unsigned long ip, unsigned long parent_ip),
|
||||
TP_ARGS(ip, parent_ip));
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_PREEMPT
|
||||
DEFINE_EVENT(preemptirq_template, preempt_disable,
|
||||
TP_PROTO(unsigned long ip, unsigned long parent_ip),
|
||||
TP_ARGS(ip, parent_ip));
|
||||
|
||||
DEFINE_EVENT(preemptirq_template, preempt_enable,
|
||||
TP_PROTO(unsigned long ip, unsigned long parent_ip),
|
||||
TP_ARGS(ip, parent_ip));
|
||||
#endif
|
||||
|
||||
#endif /* _TRACE_PREEMPTIRQ_H */
|
||||
|
||||
#include <trace/define_trace.h>
|
||||
|
||||
#else /* !CONFIG_PREEMPTIRQ_EVENTS */
|
||||
|
||||
#define trace_irq_enable(...)
|
||||
#define trace_irq_disable(...)
|
||||
#define trace_preempt_enable(...)
|
||||
#define trace_preempt_disable(...)
|
||||
#define trace_irq_enable_rcuidle(...)
|
||||
#define trace_irq_disable_rcuidle(...)
|
||||
#define trace_preempt_enable_rcuidle(...)
|
||||
#define trace_preempt_disable_rcuidle(...)
|
||||
|
||||
#endif
|
||||
|
|
@ -91,6 +91,7 @@ TRACE_EVENT(thermal_zone_trip,
|
|||
show_tzt_type(__entry->trip_type))
|
||||
);
|
||||
|
||||
#ifdef CONFIG_CPU_THERMAL
|
||||
TRACE_EVENT(thermal_power_cpu_get_power,
|
||||
TP_PROTO(const struct cpumask *cpus, unsigned long freq, u32 *load,
|
||||
size_t load_len, u32 dynamic_power, u32 static_power),
|
||||
|
|
@ -148,7 +149,9 @@ TRACE_EVENT(thermal_power_cpu_limit,
|
|||
__get_bitmask(cpumask), __entry->freq, __entry->cdev_state,
|
||||
__entry->power)
|
||||
);
|
||||
#endif /* CONFIG_CPU_THERMAL */
|
||||
|
||||
#ifdef CONFIG_DEVFREQ_THERMAL
|
||||
TRACE_EVENT(thermal_power_devfreq_get_power,
|
||||
TP_PROTO(struct thermal_cooling_device *cdev,
|
||||
struct devfreq_dev_status *status, unsigned long freq,
|
||||
|
|
@ -204,6 +207,7 @@ TRACE_EVENT(thermal_power_devfreq_limit,
|
|||
__get_str(type), __entry->freq, __entry->cdev_state,
|
||||
__entry->power)
|
||||
);
|
||||
#endif /* CONFIG_DEVFREQ_THERMAL */
|
||||
#endif /* _TRACE_THERMAL_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ DEFINE_EVENT(mm_vmscan_direct_reclaim_begin_template, mm_vmscan_direct_reclaim_b
|
|||
TP_ARGS(order, may_writepage, gfp_flags, classzone_idx)
|
||||
);
|
||||
|
||||
#ifdef CONFIG_MEMCG
|
||||
DEFINE_EVENT(mm_vmscan_direct_reclaim_begin_template, mm_vmscan_memcg_reclaim_begin,
|
||||
|
||||
TP_PROTO(int order, int may_writepage, gfp_t gfp_flags, int classzone_idx),
|
||||
|
|
@ -147,6 +148,7 @@ DEFINE_EVENT(mm_vmscan_direct_reclaim_begin_template, mm_vmscan_memcg_softlimit_
|
|||
|
||||
TP_ARGS(order, may_writepage, gfp_flags, classzone_idx)
|
||||
);
|
||||
#endif /* CONFIG_MEMCG */
|
||||
|
||||
DECLARE_EVENT_CLASS(mm_vmscan_direct_reclaim_end_template,
|
||||
|
||||
|
|
@ -172,6 +174,7 @@ DEFINE_EVENT(mm_vmscan_direct_reclaim_end_template, mm_vmscan_direct_reclaim_end
|
|||
TP_ARGS(nr_reclaimed)
|
||||
);
|
||||
|
||||
#ifdef CONFIG_MEMCG
|
||||
DEFINE_EVENT(mm_vmscan_direct_reclaim_end_template, mm_vmscan_memcg_reclaim_end,
|
||||
|
||||
TP_PROTO(unsigned long nr_reclaimed),
|
||||
|
|
@ -185,6 +188,7 @@ DEFINE_EVENT(mm_vmscan_direct_reclaim_end_template, mm_vmscan_memcg_softlimit_re
|
|||
|
||||
TP_ARGS(nr_reclaimed)
|
||||
);
|
||||
#endif /* CONFIG_MEMCG */
|
||||
|
||||
TRACE_EVENT(mm_shrink_slab_start,
|
||||
TP_PROTO(struct shrinker *shr, struct shrink_control *sc,
|
||||
|
|
|
|||
|
|
@ -148,7 +148,6 @@ DECLARE_EVENT_CLASS(xen_mmu__set_pte,
|
|||
TP_ARGS(ptep, pteval))
|
||||
|
||||
DEFINE_XEN_MMU_SET_PTE(xen_mmu_set_pte);
|
||||
DEFINE_XEN_MMU_SET_PTE(xen_mmu_set_pte_atomic);
|
||||
|
||||
TRACE_EVENT(xen_mmu_set_pte_at,
|
||||
TP_PROTO(struct mm_struct *mm, unsigned long addr,
|
||||
|
|
@ -170,21 +169,6 @@ TRACE_EVENT(xen_mmu_set_pte_at,
|
|||
(int)sizeof(pteval_t) * 2, (unsigned long long)__entry->pteval)
|
||||
);
|
||||
|
||||
TRACE_EVENT(xen_mmu_pte_clear,
|
||||
TP_PROTO(struct mm_struct *mm, unsigned long addr, pte_t *ptep),
|
||||
TP_ARGS(mm, addr, ptep),
|
||||
TP_STRUCT__entry(
|
||||
__field(struct mm_struct *, mm)
|
||||
__field(unsigned long, addr)
|
||||
__field(pte_t *, ptep)
|
||||
),
|
||||
TP_fast_assign(__entry->mm = mm;
|
||||
__entry->addr = addr;
|
||||
__entry->ptep = ptep),
|
||||
TP_printk("mm %p addr %lx ptep %p",
|
||||
__entry->mm, __entry->addr, __entry->ptep)
|
||||
);
|
||||
|
||||
TRACE_DEFINE_SIZEOF(pmdval_t);
|
||||
|
||||
TRACE_EVENT(xen_mmu_set_pmd,
|
||||
|
|
@ -202,6 +186,24 @@ TRACE_EVENT(xen_mmu_set_pmd,
|
|||
(int)sizeof(pmdval_t) * 2, (unsigned long long)__entry->pmdval)
|
||||
);
|
||||
|
||||
#ifdef CONFIG_X86_PAE
|
||||
DEFINE_XEN_MMU_SET_PTE(xen_mmu_set_pte_atomic);
|
||||
|
||||
TRACE_EVENT(xen_mmu_pte_clear,
|
||||
TP_PROTO(struct mm_struct *mm, unsigned long addr, pte_t *ptep),
|
||||
TP_ARGS(mm, addr, ptep),
|
||||
TP_STRUCT__entry(
|
||||
__field(struct mm_struct *, mm)
|
||||
__field(unsigned long, addr)
|
||||
__field(pte_t *, ptep)
|
||||
),
|
||||
TP_fast_assign(__entry->mm = mm;
|
||||
__entry->addr = addr;
|
||||
__entry->ptep = ptep),
|
||||
TP_printk("mm %p addr %lx ptep %p",
|
||||
__entry->mm, __entry->addr, __entry->ptep)
|
||||
);
|
||||
|
||||
TRACE_EVENT(xen_mmu_pmd_clear,
|
||||
TP_PROTO(pmd_t *pmdp),
|
||||
TP_ARGS(pmdp),
|
||||
|
|
@ -211,6 +213,7 @@ TRACE_EVENT(xen_mmu_pmd_clear,
|
|||
TP_fast_assign(__entry->pmdp = pmdp),
|
||||
TP_printk("pmdp %p", __entry->pmdp)
|
||||
);
|
||||
#endif
|
||||
|
||||
#if CONFIG_PGTABLE_LEVELS >= 4
|
||||
|
||||
|
|
|
|||
|
|
@ -7874,15 +7874,16 @@ void perf_trace_run_bpf_submit(void *raw_data, int size, int rctx,
|
|||
}
|
||||
}
|
||||
perf_tp_event(call->event.type, count, raw_data, size, regs, head,
|
||||
rctx, task, NULL);
|
||||
rctx, task);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(perf_trace_run_bpf_submit);
|
||||
|
||||
void perf_tp_event(u16 event_type, u64 count, void *record, int entry_size,
|
||||
struct pt_regs *regs, struct hlist_head *head, int rctx,
|
||||
struct task_struct *task, struct perf_event *event)
|
||||
struct task_struct *task)
|
||||
{
|
||||
struct perf_sample_data data;
|
||||
struct perf_event *event;
|
||||
|
||||
struct perf_raw_record raw = {
|
||||
.frag = {
|
||||
|
|
@ -7896,15 +7897,9 @@ void perf_tp_event(u16 event_type, u64 count, void *record, int entry_size,
|
|||
|
||||
perf_trace_buf_update(record, event_type);
|
||||
|
||||
/* Use the given event instead of the hlist */
|
||||
if (event) {
|
||||
hlist_for_each_entry_rcu(event, head, hlist_entry) {
|
||||
if (perf_tp_event_match(event, &data, regs))
|
||||
perf_swevent_event(event, count, &data, regs);
|
||||
} else {
|
||||
hlist_for_each_entry_rcu(event, head, hlist_entry) {
|
||||
if (perf_tp_event_match(event, &data, regs))
|
||||
perf_swevent_event(event, count, &data, regs);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/ctype.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
#include <asm/sections.h>
|
||||
|
|
@ -337,6 +338,10 @@ const char *kallsyms_lookup(unsigned long addr,
|
|||
if (!ret)
|
||||
ret = bpf_address_lookup(addr, symbolsize,
|
||||
offset, modname, namebuf);
|
||||
|
||||
if (!ret)
|
||||
ret = ftrace_mod_address_lookup(addr, symbolsize,
|
||||
offset, modname, namebuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -474,6 +479,7 @@ EXPORT_SYMBOL(__print_symbol);
|
|||
struct kallsym_iter {
|
||||
loff_t pos;
|
||||
loff_t pos_mod_end;
|
||||
loff_t pos_ftrace_mod_end;
|
||||
unsigned long value;
|
||||
unsigned int nameoff; /* If iterating in core kernel symbols. */
|
||||
char type;
|
||||
|
|
@ -497,11 +503,25 @@ static int get_ksymbol_mod(struct kallsym_iter *iter)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int get_ksymbol_ftrace_mod(struct kallsym_iter *iter)
|
||||
{
|
||||
int ret = ftrace_mod_get_kallsym(iter->pos - iter->pos_mod_end,
|
||||
&iter->value, &iter->type,
|
||||
iter->name, iter->module_name,
|
||||
&iter->exported);
|
||||
if (ret < 0) {
|
||||
iter->pos_ftrace_mod_end = iter->pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_ksymbol_bpf(struct kallsym_iter *iter)
|
||||
{
|
||||
iter->module_name[0] = '\0';
|
||||
iter->exported = 0;
|
||||
return bpf_get_kallsym(iter->pos - iter->pos_mod_end,
|
||||
return bpf_get_kallsym(iter->pos - iter->pos_ftrace_mod_end,
|
||||
&iter->value, &iter->type,
|
||||
iter->name) < 0 ? 0 : 1;
|
||||
}
|
||||
|
|
@ -526,20 +546,31 @@ static void reset_iter(struct kallsym_iter *iter, loff_t new_pos)
|
|||
iter->name[0] = '\0';
|
||||
iter->nameoff = get_symbol_offset(new_pos);
|
||||
iter->pos = new_pos;
|
||||
if (new_pos == 0)
|
||||
if (new_pos == 0) {
|
||||
iter->pos_mod_end = 0;
|
||||
iter->pos_ftrace_mod_end = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int update_iter_mod(struct kallsym_iter *iter, loff_t pos)
|
||||
{
|
||||
iter->pos = pos;
|
||||
|
||||
if (iter->pos_mod_end > 0 &&
|
||||
iter->pos_mod_end < iter->pos)
|
||||
if (iter->pos_ftrace_mod_end > 0 &&
|
||||
iter->pos_ftrace_mod_end < iter->pos)
|
||||
return get_ksymbol_bpf(iter);
|
||||
|
||||
if (!get_ksymbol_mod(iter))
|
||||
return get_ksymbol_bpf(iter);
|
||||
if (iter->pos_mod_end > 0 &&
|
||||
iter->pos_mod_end < iter->pos) {
|
||||
if (!get_ksymbol_ftrace_mod(iter))
|
||||
return get_ksymbol_bpf(iter);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!get_ksymbol_mod(iter)) {
|
||||
if (!get_ksymbol_ftrace_mod(iter))
|
||||
return get_ksymbol_bpf(iter);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3481,6 +3481,8 @@ static noinline int do_init_module(struct module *mod)
|
|||
if (!mod->async_probe_requested && (current->flags & PF_USED_ASYNC))
|
||||
async_synchronize_full();
|
||||
|
||||
ftrace_free_mem(mod, mod->init_layout.base, mod->init_layout.base +
|
||||
mod->init_layout.size);
|
||||
mutex_lock(&module_mutex);
|
||||
/* Drop initial reference. */
|
||||
module_put(mod);
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
* There are situations when we want to make sure that all buffers
|
||||
* were handled or when IRQs are blocked.
|
||||
*/
|
||||
static int printk_safe_irq_ready;
|
||||
static int printk_safe_irq_ready __read_mostly;
|
||||
|
||||
#define SAFE_LOG_BUF_LEN ((1 << CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT) - \
|
||||
sizeof(atomic_t) - \
|
||||
|
|
@ -63,11 +63,8 @@ static DEFINE_PER_CPU(struct printk_safe_seq_buf, nmi_print_seq);
|
|||
/* Get flushed in a more safe context. */
|
||||
static void queue_flush_work(struct printk_safe_seq_buf *s)
|
||||
{
|
||||
if (printk_safe_irq_ready) {
|
||||
/* Make sure that IRQ work is really initialized. */
|
||||
smp_rmb();
|
||||
if (printk_safe_irq_ready)
|
||||
irq_work_queue(&s->work);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -398,8 +395,12 @@ void __init printk_safe_init(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
/* Make sure that IRQ works are initialized before enabling. */
|
||||
smp_wmb();
|
||||
/*
|
||||
* In the highly unlikely event that a NMI were to trigger at
|
||||
* this moment. Make sure IRQ work is set up before this
|
||||
* variable is set.
|
||||
*/
|
||||
barrier();
|
||||
printk_safe_irq_ready = 1;
|
||||
|
||||
/* Flush pending messages that did not have scheduled IRQ works. */
|
||||
|
|
|
|||
|
|
@ -160,6 +160,17 @@ config FUNCTION_GRAPH_TRACER
|
|||
address on the current task structure into a stack of calls.
|
||||
|
||||
|
||||
config PREEMPTIRQ_EVENTS
|
||||
bool "Enable trace events for preempt and irq disable/enable"
|
||||
select TRACE_IRQFLAGS
|
||||
depends on DEBUG_PREEMPT || !PROVE_LOCKING
|
||||
default n
|
||||
help
|
||||
Enable tracing of disable and enable events for preemption and irqs.
|
||||
For tracing preempt disable/enable events, DEBUG_PREEMPT must be
|
||||
enabled. For tracing irq disable/enable events, PROVE_LOCKING must
|
||||
be disabled.
|
||||
|
||||
config IRQSOFF_TRACER
|
||||
bool "Interrupts-off Latency Tracer"
|
||||
default n
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ obj-$(CONFIG_TRACING) += trace_printk.o
|
|||
obj-$(CONFIG_TRACING_MAP) += tracing_map.o
|
||||
obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o
|
||||
obj-$(CONFIG_FUNCTION_TRACER) += trace_functions.o
|
||||
obj-$(CONFIG_PREEMPTIRQ_EVENTS) += trace_irqsoff.o
|
||||
obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o
|
||||
obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o
|
||||
obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o
|
||||
|
|
|
|||
|
|
@ -203,30 +203,6 @@ void clear_ftrace_function(void)
|
|||
ftrace_trace_function = ftrace_stub;
|
||||
}
|
||||
|
||||
static void per_cpu_ops_disable_all(struct ftrace_ops *ops)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
*per_cpu_ptr(ops->disabled, cpu) = 1;
|
||||
}
|
||||
|
||||
static int per_cpu_ops_alloc(struct ftrace_ops *ops)
|
||||
{
|
||||
int __percpu *disabled;
|
||||
|
||||
if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU)))
|
||||
return -EINVAL;
|
||||
|
||||
disabled = alloc_percpu(int);
|
||||
if (!disabled)
|
||||
return -ENOMEM;
|
||||
|
||||
ops->disabled = disabled;
|
||||
per_cpu_ops_disable_all(ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ftrace_sync(struct work_struct *work)
|
||||
{
|
||||
/*
|
||||
|
|
@ -262,8 +238,8 @@ static ftrace_func_t ftrace_ops_get_list_func(struct ftrace_ops *ops)
|
|||
* If this is a dynamic, RCU, or per CPU ops, or we force list func,
|
||||
* then it needs to call the list anyway.
|
||||
*/
|
||||
if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU |
|
||||
FTRACE_OPS_FL_RCU) || FTRACE_FORCE_LIST_FUNC)
|
||||
if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_RCU) ||
|
||||
FTRACE_FORCE_LIST_FUNC)
|
||||
return ftrace_ops_list_func;
|
||||
|
||||
return ftrace_ops_get_func(ops);
|
||||
|
|
@ -422,11 +398,6 @@ static int __register_ftrace_function(struct ftrace_ops *ops)
|
|||
if (!core_kernel_data((unsigned long)ops))
|
||||
ops->flags |= FTRACE_OPS_FL_DYNAMIC;
|
||||
|
||||
if (ops->flags & FTRACE_OPS_FL_PER_CPU) {
|
||||
if (per_cpu_ops_alloc(ops))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
add_ftrace_ops(&ftrace_ops_list, ops);
|
||||
|
||||
/* Always save the function, and reset at unregistering */
|
||||
|
|
@ -2727,11 +2698,6 @@ void __weak arch_ftrace_trampoline_free(struct ftrace_ops *ops)
|
|||
{
|
||||
}
|
||||
|
||||
static void per_cpu_ops_free(struct ftrace_ops *ops)
|
||||
{
|
||||
free_percpu(ops->disabled);
|
||||
}
|
||||
|
||||
static void ftrace_startup_enable(int command)
|
||||
{
|
||||
if (saved_ftrace_func != ftrace_trace_function) {
|
||||
|
|
@ -2833,7 +2799,7 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
|
|||
* not currently active, we can just free them
|
||||
* without synchronizing all CPUs.
|
||||
*/
|
||||
if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU))
|
||||
if (ops->flags & FTRACE_OPS_FL_DYNAMIC)
|
||||
goto free_ops;
|
||||
|
||||
return 0;
|
||||
|
|
@ -2880,7 +2846,7 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
|
|||
* The same goes for freeing the per_cpu data of the per_cpu
|
||||
* ops.
|
||||
*/
|
||||
if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU)) {
|
||||
if (ops->flags & FTRACE_OPS_FL_DYNAMIC) {
|
||||
/*
|
||||
* We need to do a hard force of sched synchronization.
|
||||
* This is because we use preempt_disable() to do RCU, but
|
||||
|
|
@ -2903,9 +2869,6 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command)
|
|||
|
||||
free_ops:
|
||||
arch_ftrace_trampoline_free(ops);
|
||||
|
||||
if (ops->flags & FTRACE_OPS_FL_PER_CPU)
|
||||
per_cpu_ops_free(ops);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -5672,10 +5635,29 @@ static int ftrace_process_locs(struct module *mod,
|
|||
return ret;
|
||||
}
|
||||
|
||||
struct ftrace_mod_func {
|
||||
struct list_head list;
|
||||
char *name;
|
||||
unsigned long ip;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
struct ftrace_mod_map {
|
||||
struct rcu_head rcu;
|
||||
struct list_head list;
|
||||
struct module *mod;
|
||||
unsigned long start_addr;
|
||||
unsigned long end_addr;
|
||||
struct list_head funcs;
|
||||
unsigned int num_funcs;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
|
||||
#define next_to_ftrace_page(p) container_of(p, struct ftrace_page, next)
|
||||
|
||||
static LIST_HEAD(ftrace_mod_maps);
|
||||
|
||||
static int referenced_filters(struct dyn_ftrace *rec)
|
||||
{
|
||||
struct ftrace_ops *ops;
|
||||
|
|
@ -5729,8 +5711,26 @@ static void clear_mod_from_hashes(struct ftrace_page *pg)
|
|||
mutex_unlock(&trace_types_lock);
|
||||
}
|
||||
|
||||
static void ftrace_free_mod_map(struct rcu_head *rcu)
|
||||
{
|
||||
struct ftrace_mod_map *mod_map = container_of(rcu, struct ftrace_mod_map, rcu);
|
||||
struct ftrace_mod_func *mod_func;
|
||||
struct ftrace_mod_func *n;
|
||||
|
||||
/* All the contents of mod_map are now not visible to readers */
|
||||
list_for_each_entry_safe(mod_func, n, &mod_map->funcs, list) {
|
||||
kfree(mod_func->name);
|
||||
list_del(&mod_func->list);
|
||||
kfree(mod_func);
|
||||
}
|
||||
|
||||
kfree(mod_map);
|
||||
}
|
||||
|
||||
void ftrace_release_mod(struct module *mod)
|
||||
{
|
||||
struct ftrace_mod_map *mod_map;
|
||||
struct ftrace_mod_map *n;
|
||||
struct dyn_ftrace *rec;
|
||||
struct ftrace_page **last_pg;
|
||||
struct ftrace_page *tmp_page = NULL;
|
||||
|
|
@ -5742,6 +5742,14 @@ void ftrace_release_mod(struct module *mod)
|
|||
if (ftrace_disabled)
|
||||
goto out_unlock;
|
||||
|
||||
list_for_each_entry_safe(mod_map, n, &ftrace_mod_maps, list) {
|
||||
if (mod_map->mod == mod) {
|
||||
list_del_rcu(&mod_map->list);
|
||||
call_rcu_sched(&mod_map->rcu, ftrace_free_mod_map);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Each module has its own ftrace_pages, remove
|
||||
* them from the list.
|
||||
|
|
@ -5749,7 +5757,8 @@ void ftrace_release_mod(struct module *mod)
|
|||
last_pg = &ftrace_pages_start;
|
||||
for (pg = ftrace_pages_start; pg; pg = *last_pg) {
|
||||
rec = &pg->records[0];
|
||||
if (within_module_core(rec->ip, mod)) {
|
||||
if (within_module_core(rec->ip, mod) ||
|
||||
within_module_init(rec->ip, mod)) {
|
||||
/*
|
||||
* As core pages are first, the first
|
||||
* page should never be a module page.
|
||||
|
|
@ -5818,7 +5827,8 @@ void ftrace_module_enable(struct module *mod)
|
|||
* not part of this module, then skip this pg,
|
||||
* which the "break" will do.
|
||||
*/
|
||||
if (!within_module_core(rec->ip, mod))
|
||||
if (!within_module_core(rec->ip, mod) &&
|
||||
!within_module_init(rec->ip, mod))
|
||||
break;
|
||||
|
||||
cnt = 0;
|
||||
|
|
@ -5863,23 +5873,245 @@ void ftrace_module_init(struct module *mod)
|
|||
ftrace_process_locs(mod, mod->ftrace_callsites,
|
||||
mod->ftrace_callsites + mod->num_ftrace_callsites);
|
||||
}
|
||||
|
||||
static void save_ftrace_mod_rec(struct ftrace_mod_map *mod_map,
|
||||
struct dyn_ftrace *rec)
|
||||
{
|
||||
struct ftrace_mod_func *mod_func;
|
||||
unsigned long symsize;
|
||||
unsigned long offset;
|
||||
char str[KSYM_SYMBOL_LEN];
|
||||
char *modname;
|
||||
const char *ret;
|
||||
|
||||
ret = kallsyms_lookup(rec->ip, &symsize, &offset, &modname, str);
|
||||
if (!ret)
|
||||
return;
|
||||
|
||||
mod_func = kmalloc(sizeof(*mod_func), GFP_KERNEL);
|
||||
if (!mod_func)
|
||||
return;
|
||||
|
||||
mod_func->name = kstrdup(str, GFP_KERNEL);
|
||||
if (!mod_func->name) {
|
||||
kfree(mod_func);
|
||||
return;
|
||||
}
|
||||
|
||||
mod_func->ip = rec->ip - offset;
|
||||
mod_func->size = symsize;
|
||||
|
||||
mod_map->num_funcs++;
|
||||
|
||||
list_add_rcu(&mod_func->list, &mod_map->funcs);
|
||||
}
|
||||
|
||||
static struct ftrace_mod_map *
|
||||
allocate_ftrace_mod_map(struct module *mod,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
struct ftrace_mod_map *mod_map;
|
||||
|
||||
mod_map = kmalloc(sizeof(*mod_map), GFP_KERNEL);
|
||||
if (!mod_map)
|
||||
return NULL;
|
||||
|
||||
mod_map->mod = mod;
|
||||
mod_map->start_addr = start;
|
||||
mod_map->end_addr = end;
|
||||
mod_map->num_funcs = 0;
|
||||
|
||||
INIT_LIST_HEAD_RCU(&mod_map->funcs);
|
||||
|
||||
list_add_rcu(&mod_map->list, &ftrace_mod_maps);
|
||||
|
||||
return mod_map;
|
||||
}
|
||||
|
||||
static const char *
|
||||
ftrace_func_address_lookup(struct ftrace_mod_map *mod_map,
|
||||
unsigned long addr, unsigned long *size,
|
||||
unsigned long *off, char *sym)
|
||||
{
|
||||
struct ftrace_mod_func *found_func = NULL;
|
||||
struct ftrace_mod_func *mod_func;
|
||||
|
||||
list_for_each_entry_rcu(mod_func, &mod_map->funcs, list) {
|
||||
if (addr >= mod_func->ip &&
|
||||
addr < mod_func->ip + mod_func->size) {
|
||||
found_func = mod_func;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_func) {
|
||||
if (size)
|
||||
*size = found_func->size;
|
||||
if (off)
|
||||
*off = addr - found_func->ip;
|
||||
if (sym)
|
||||
strlcpy(sym, found_func->name, KSYM_NAME_LEN);
|
||||
|
||||
return found_func->name;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
ftrace_mod_address_lookup(unsigned long addr, unsigned long *size,
|
||||
unsigned long *off, char **modname, char *sym)
|
||||
{
|
||||
struct ftrace_mod_map *mod_map;
|
||||
const char *ret = NULL;
|
||||
|
||||
/* mod_map is freed via call_rcu_sched() */
|
||||
preempt_disable();
|
||||
list_for_each_entry_rcu(mod_map, &ftrace_mod_maps, list) {
|
||||
ret = ftrace_func_address_lookup(mod_map, addr, size, off, sym);
|
||||
if (ret) {
|
||||
if (modname)
|
||||
*modname = mod_map->mod->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
preempt_enable();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ftrace_mod_get_kallsym(unsigned int symnum, unsigned long *value,
|
||||
char *type, char *name,
|
||||
char *module_name, int *exported)
|
||||
{
|
||||
struct ftrace_mod_map *mod_map;
|
||||
struct ftrace_mod_func *mod_func;
|
||||
|
||||
preempt_disable();
|
||||
list_for_each_entry_rcu(mod_map, &ftrace_mod_maps, list) {
|
||||
|
||||
if (symnum >= mod_map->num_funcs) {
|
||||
symnum -= mod_map->num_funcs;
|
||||
continue;
|
||||
}
|
||||
|
||||
list_for_each_entry_rcu(mod_func, &mod_map->funcs, list) {
|
||||
if (symnum > 1) {
|
||||
symnum--;
|
||||
continue;
|
||||
}
|
||||
|
||||
*value = mod_func->ip;
|
||||
*type = 'T';
|
||||
strlcpy(name, mod_func->name, KSYM_NAME_LEN);
|
||||
strlcpy(module_name, mod_map->mod->name, MODULE_NAME_LEN);
|
||||
*exported = 1;
|
||||
preempt_enable();
|
||||
return 0;
|
||||
}
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
preempt_enable();
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
#else
|
||||
static void save_ftrace_mod_rec(struct ftrace_mod_map *mod_map,
|
||||
struct dyn_ftrace *rec) { }
|
||||
static inline struct ftrace_mod_map *
|
||||
allocate_ftrace_mod_map(struct module *mod,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_MODULES */
|
||||
|
||||
void __init ftrace_free_init_mem(void)
|
||||
struct ftrace_init_func {
|
||||
struct list_head list;
|
||||
unsigned long ip;
|
||||
};
|
||||
|
||||
/* Clear any init ips from hashes */
|
||||
static void
|
||||
clear_func_from_hash(struct ftrace_init_func *func, struct ftrace_hash *hash)
|
||||
{
|
||||
unsigned long start = (unsigned long)(&__init_begin);
|
||||
unsigned long end = (unsigned long)(&__init_end);
|
||||
struct ftrace_func_entry *entry;
|
||||
|
||||
if (ftrace_hash_empty(hash))
|
||||
return;
|
||||
|
||||
entry = __ftrace_lookup_ip(hash, func->ip);
|
||||
|
||||
/*
|
||||
* Do not allow this rec to match again.
|
||||
* Yeah, it may waste some memory, but will be removed
|
||||
* if/when the hash is modified again.
|
||||
*/
|
||||
if (entry)
|
||||
entry->ip = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
clear_func_from_hashes(struct ftrace_init_func *func)
|
||||
{
|
||||
struct trace_array *tr;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
list_for_each_entry(tr, &ftrace_trace_arrays, list) {
|
||||
if (!tr->ops || !tr->ops->func_hash)
|
||||
continue;
|
||||
mutex_lock(&tr->ops->func_hash->regex_lock);
|
||||
clear_func_from_hash(func, tr->ops->func_hash->filter_hash);
|
||||
clear_func_from_hash(func, tr->ops->func_hash->notrace_hash);
|
||||
mutex_unlock(&tr->ops->func_hash->regex_lock);
|
||||
}
|
||||
mutex_unlock(&trace_types_lock);
|
||||
}
|
||||
|
||||
static void add_to_clear_hash_list(struct list_head *clear_list,
|
||||
struct dyn_ftrace *rec)
|
||||
{
|
||||
struct ftrace_init_func *func;
|
||||
|
||||
func = kmalloc(sizeof(*func), GFP_KERNEL);
|
||||
if (!func) {
|
||||
WARN_ONCE(1, "alloc failure, ftrace filter could be stale\n");
|
||||
return;
|
||||
}
|
||||
|
||||
func->ip = rec->ip;
|
||||
list_add(&func->list, clear_list);
|
||||
}
|
||||
|
||||
void ftrace_free_mem(struct module *mod, void *start_ptr, void *end_ptr)
|
||||
{
|
||||
unsigned long start = (unsigned long)(start_ptr);
|
||||
unsigned long end = (unsigned long)(end_ptr);
|
||||
struct ftrace_page **last_pg = &ftrace_pages_start;
|
||||
struct ftrace_page *pg;
|
||||
struct dyn_ftrace *rec;
|
||||
struct dyn_ftrace key;
|
||||
struct ftrace_mod_map *mod_map = NULL;
|
||||
struct ftrace_init_func *func, *func_next;
|
||||
struct list_head clear_hash;
|
||||
int order;
|
||||
|
||||
INIT_LIST_HEAD(&clear_hash);
|
||||
|
||||
key.ip = start;
|
||||
key.flags = end; /* overload flags, as it is unsigned long */
|
||||
|
||||
mutex_lock(&ftrace_lock);
|
||||
|
||||
/*
|
||||
* If we are freeing module init memory, then check if
|
||||
* any tracer is active. If so, we need to save a mapping of
|
||||
* the module functions being freed with the address.
|
||||
*/
|
||||
if (mod && ftrace_ops_list != &ftrace_list_end)
|
||||
mod_map = allocate_ftrace_mod_map(mod, start, end);
|
||||
|
||||
for (pg = ftrace_pages_start; pg; last_pg = &pg->next, pg = *last_pg) {
|
||||
if (end < pg->records[0].ip ||
|
||||
start >= (pg->records[pg->index - 1].ip + MCOUNT_INSN_SIZE))
|
||||
|
|
@ -5890,6 +6122,13 @@ void __init ftrace_free_init_mem(void)
|
|||
ftrace_cmp_recs);
|
||||
if (!rec)
|
||||
continue;
|
||||
|
||||
/* rec will be cleared from hashes after ftrace_lock unlock */
|
||||
add_to_clear_hash_list(&clear_hash, rec);
|
||||
|
||||
if (mod_map)
|
||||
save_ftrace_mod_rec(mod_map, rec);
|
||||
|
||||
pg->index--;
|
||||
ftrace_update_tot_cnt--;
|
||||
if (!pg->index) {
|
||||
|
|
@ -5908,6 +6147,19 @@ void __init ftrace_free_init_mem(void)
|
|||
goto again;
|
||||
}
|
||||
mutex_unlock(&ftrace_lock);
|
||||
|
||||
list_for_each_entry_safe(func, func_next, &clear_hash, list) {
|
||||
clear_func_from_hashes(func);
|
||||
kfree(func);
|
||||
}
|
||||
}
|
||||
|
||||
void __init ftrace_free_init_mem(void)
|
||||
{
|
||||
void *start = (void *)(&__init_begin);
|
||||
void *end = (void *)(&__init_end);
|
||||
|
||||
ftrace_free_mem(NULL, start, end);
|
||||
}
|
||||
|
||||
void __init ftrace_init(void)
|
||||
|
|
@ -6063,10 +6315,7 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
|
|||
* If any of the above fails then the op->func() is not executed.
|
||||
*/
|
||||
if ((!(op->flags & FTRACE_OPS_FL_RCU) || rcu_is_watching()) &&
|
||||
(!(op->flags & FTRACE_OPS_FL_PER_CPU) ||
|
||||
!ftrace_function_local_disabled(op)) &&
|
||||
ftrace_ops_test(op, ip, regs)) {
|
||||
|
||||
if (FTRACE_WARN_ON(!op->func)) {
|
||||
pr_warn("op=%p %pS\n", op, op);
|
||||
goto out;
|
||||
|
|
@ -6124,10 +6373,7 @@ static void ftrace_ops_assist_func(unsigned long ip, unsigned long parent_ip,
|
|||
|
||||
preempt_disable_notrace();
|
||||
|
||||
if (!(op->flags & FTRACE_OPS_FL_PER_CPU) ||
|
||||
!ftrace_function_local_disabled(op)) {
|
||||
op->func(ip, parent_ip, op, regs);
|
||||
}
|
||||
op->func(ip, parent_ip, op, regs);
|
||||
|
||||
preempt_enable_notrace();
|
||||
trace_clear_recursion(bit);
|
||||
|
|
@ -6151,7 +6397,7 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops)
|
|||
* or does per cpu logic, then we need to call the assist handler.
|
||||
*/
|
||||
if (!(ops->flags & FTRACE_OPS_FL_RECURSION_SAFE) ||
|
||||
ops->flags & (FTRACE_OPS_FL_RCU | FTRACE_OPS_FL_PER_CPU))
|
||||
ops->flags & FTRACE_OPS_FL_RCU)
|
||||
return ftrace_ops_assist_func;
|
||||
|
||||
return ops->func;
|
||||
|
|
|
|||
|
|
@ -2536,61 +2536,29 @@ rb_wakeups(struct ring_buffer *buffer, struct ring_buffer_per_cpu *cpu_buffer)
|
|||
* The lock and unlock are done within a preempt disable section.
|
||||
* The current_context per_cpu variable can only be modified
|
||||
* by the current task between lock and unlock. But it can
|
||||
* be modified more than once via an interrupt. To pass this
|
||||
* information from the lock to the unlock without having to
|
||||
* access the 'in_interrupt()' functions again (which do show
|
||||
* a bit of overhead in something as critical as function tracing,
|
||||
* we use a bitmask trick.
|
||||
* be modified more than once via an interrupt. There are four
|
||||
* different contexts that we need to consider.
|
||||
*
|
||||
* bit 0 = NMI context
|
||||
* bit 1 = IRQ context
|
||||
* bit 2 = SoftIRQ context
|
||||
* bit 3 = normal context.
|
||||
* Normal context.
|
||||
* SoftIRQ context
|
||||
* IRQ context
|
||||
* NMI context
|
||||
*
|
||||
* This works because this is the order of contexts that can
|
||||
* preempt other contexts. A SoftIRQ never preempts an IRQ
|
||||
* context.
|
||||
*
|
||||
* When the context is determined, the corresponding bit is
|
||||
* checked and set (if it was set, then a recursion of that context
|
||||
* happened).
|
||||
*
|
||||
* On unlock, we need to clear this bit. To do so, just subtract
|
||||
* 1 from the current_context and AND it to itself.
|
||||
*
|
||||
* (binary)
|
||||
* 101 - 1 = 100
|
||||
* 101 & 100 = 100 (clearing bit zero)
|
||||
*
|
||||
* 1010 - 1 = 1001
|
||||
* 1010 & 1001 = 1000 (clearing bit 1)
|
||||
*
|
||||
* The least significant bit can be cleared this way, and it
|
||||
* just so happens that it is the same bit corresponding to
|
||||
* the current context.
|
||||
* If for some reason the ring buffer starts to recurse, we
|
||||
* only allow that to happen at most 4 times (one for each
|
||||
* context). If it happens 5 times, then we consider this a
|
||||
* recusive loop and do not let it go further.
|
||||
*/
|
||||
|
||||
static __always_inline int
|
||||
trace_recursive_lock(struct ring_buffer_per_cpu *cpu_buffer)
|
||||
{
|
||||
unsigned int val = cpu_buffer->current_context;
|
||||
int bit;
|
||||
|
||||
if (in_interrupt()) {
|
||||
if (in_nmi())
|
||||
bit = RB_CTX_NMI;
|
||||
else if (in_irq())
|
||||
bit = RB_CTX_IRQ;
|
||||
else
|
||||
bit = RB_CTX_SOFTIRQ;
|
||||
} else
|
||||
bit = RB_CTX_NORMAL;
|
||||
|
||||
if (unlikely(val & (1 << bit)))
|
||||
if (cpu_buffer->current_context >= 4)
|
||||
return 1;
|
||||
|
||||
val |= (1 << bit);
|
||||
cpu_buffer->current_context = val;
|
||||
cpu_buffer->current_context++;
|
||||
/* Interrupts must see this update */
|
||||
barrier();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2598,7 +2566,9 @@ trace_recursive_lock(struct ring_buffer_per_cpu *cpu_buffer)
|
|||
static __always_inline void
|
||||
trace_recursive_unlock(struct ring_buffer_per_cpu *cpu_buffer)
|
||||
{
|
||||
cpu_buffer->current_context &= cpu_buffer->current_context - 1;
|
||||
/* Don't let the dec leak out */
|
||||
barrier();
|
||||
cpu_buffer->current_context--;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -7687,6 +7687,7 @@ static int instance_mkdir(const char *name)
|
|||
struct trace_array *tr;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&event_mutex);
|
||||
mutex_lock(&trace_types_lock);
|
||||
|
||||
ret = -EEXIST;
|
||||
|
|
@ -7742,6 +7743,7 @@ static int instance_mkdir(const char *name)
|
|||
list_add(&tr->list, &ftrace_trace_arrays);
|
||||
|
||||
mutex_unlock(&trace_types_lock);
|
||||
mutex_unlock(&event_mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
@ -7753,6 +7755,7 @@ static int instance_mkdir(const char *name)
|
|||
|
||||
out_unlock:
|
||||
mutex_unlock(&trace_types_lock);
|
||||
mutex_unlock(&event_mutex);
|
||||
|
||||
return ret;
|
||||
|
||||
|
|
@ -7765,6 +7768,7 @@ static int instance_rmdir(const char *name)
|
|||
int ret;
|
||||
int i;
|
||||
|
||||
mutex_lock(&event_mutex);
|
||||
mutex_lock(&trace_types_lock);
|
||||
|
||||
ret = -ENODEV;
|
||||
|
|
@ -7810,6 +7814,7 @@ static int instance_rmdir(const char *name)
|
|||
|
||||
out_unlock:
|
||||
mutex_unlock(&trace_types_lock);
|
||||
mutex_unlock(&event_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -8276,6 +8281,92 @@ void ftrace_dump(enum ftrace_dump_mode oops_dump_mode)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(ftrace_dump);
|
||||
|
||||
int trace_run_command(const char *buf, int (*createfn)(int, char **))
|
||||
{
|
||||
char **argv;
|
||||
int argc, ret;
|
||||
|
||||
argc = 0;
|
||||
ret = 0;
|
||||
argv = argv_split(GFP_KERNEL, buf, &argc);
|
||||
if (!argv)
|
||||
return -ENOMEM;
|
||||
|
||||
if (argc)
|
||||
ret = createfn(argc, argv);
|
||||
|
||||
argv_free(argv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define WRITE_BUFSIZE 4096
|
||||
|
||||
ssize_t trace_parse_run_command(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *ppos,
|
||||
int (*createfn)(int, char **))
|
||||
{
|
||||
char *kbuf, *buf, *tmp;
|
||||
int ret = 0;
|
||||
size_t done = 0;
|
||||
size_t size;
|
||||
|
||||
kbuf = kmalloc(WRITE_BUFSIZE, GFP_KERNEL);
|
||||
if (!kbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
while (done < count) {
|
||||
size = count - done;
|
||||
|
||||
if (size >= WRITE_BUFSIZE)
|
||||
size = WRITE_BUFSIZE - 1;
|
||||
|
||||
if (copy_from_user(kbuf, buffer + done, size)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
kbuf[size] = '\0';
|
||||
buf = kbuf;
|
||||
do {
|
||||
tmp = strchr(buf, '\n');
|
||||
if (tmp) {
|
||||
*tmp = '\0';
|
||||
size = tmp - buf + 1;
|
||||
} else {
|
||||
size = strlen(buf);
|
||||
if (done + size < count) {
|
||||
if (buf != kbuf)
|
||||
break;
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
done += size;
|
||||
|
||||
/* Remove comments */
|
||||
tmp = strchr(buf, '#');
|
||||
|
||||
if (tmp)
|
||||
*tmp = '\0';
|
||||
|
||||
ret = trace_run_command(buf, createfn);
|
||||
if (ret)
|
||||
goto out;
|
||||
buf += size;
|
||||
|
||||
} while (done < count);
|
||||
}
|
||||
ret = done;
|
||||
|
||||
out:
|
||||
kfree(kbuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
__init static int tracer_alloc_buffers(void)
|
||||
{
|
||||
int ring_buf_size;
|
||||
|
|
|
|||
|
|
@ -739,8 +739,6 @@ extern int trace_selftest_startup_wakeup(struct tracer *trace,
|
|||
struct trace_array *tr);
|
||||
extern int trace_selftest_startup_nop(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
extern int trace_selftest_startup_sched_switch(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
extern int trace_selftest_startup_branch(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
/*
|
||||
|
|
@ -1755,6 +1753,13 @@ void trace_printk_start_comm(void);
|
|||
int trace_keep_overwrite(struct tracer *tracer, u32 mask, int set);
|
||||
int set_tracer_flag(struct trace_array *tr, unsigned int mask, int enabled);
|
||||
|
||||
#define MAX_EVENT_NAME_LEN 64
|
||||
|
||||
extern int trace_run_command(const char *buf, int (*createfn)(int, char**));
|
||||
extern ssize_t trace_parse_run_command(struct file *file,
|
||||
const char __user *buffer, size_t count, loff_t *ppos,
|
||||
int (*createfn)(int, char**));
|
||||
|
||||
/*
|
||||
* Normal trace_printk() and friends allocates special buffers
|
||||
* to do the manipulation, as well as saves the print formats
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue