665 lines · 14.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * trace context switch * * Copyright (C) 2007 Steven Rostedt <srostedt@redhat.com> * */#include <linux/module.h>#include <linux/kallsyms.h>#include <linux/uaccess.h>#include <linux/kmemleak.h>#include <linux/ftrace.h>#include <trace/events/sched.h> #include "trace.h" #define RECORD_CMDLINE	1#define RECORD_TGID	2 static int		sched_cmdline_ref;static int		sched_tgid_ref;static DEFINE_MUTEX(sched_register_mutex); static voidprobe_sched_switch(void *ignore, bool preempt,		   struct task_struct *prev, struct task_struct *next,		   unsigned int prev_state){	int flags; 	flags = (RECORD_TGID * !!sched_tgid_ref) +		(RECORD_CMDLINE * !!sched_cmdline_ref); 	if (!flags)		return;	tracing_record_taskinfo_sched_switch(prev, next, flags);} static voidprobe_sched_wakeup(void *ignore, struct task_struct *wakee){	int flags; 	flags = (RECORD_TGID * !!sched_tgid_ref) +		(RECORD_CMDLINE * !!sched_cmdline_ref); 	if (!flags)		return;	tracing_record_taskinfo_sched_switch(current, wakee, flags);} static int tracing_sched_register(void){	int ret; 	ret = register_trace_sched_wakeup(probe_sched_wakeup, NULL);	if (ret) {		pr_info("wakeup trace: Couldn't activate tracepoint"			" probe to kernel_sched_wakeup\n");		return ret;	} 	ret = register_trace_sched_wakeup_new(probe_sched_wakeup, NULL);	if (ret) {		pr_info("wakeup trace: Couldn't activate tracepoint"			" probe to kernel_sched_wakeup_new\n");		goto fail_deprobe;	} 	ret = register_trace_sched_switch(probe_sched_switch, NULL);	if (ret) {		pr_info("sched trace: Couldn't activate tracepoint"			" probe to kernel_sched_switch\n");		goto fail_deprobe_wake_new;	} 	return ret;fail_deprobe_wake_new:	unregister_trace_sched_wakeup_new(probe_sched_wakeup, NULL);fail_deprobe:	unregister_trace_sched_wakeup(probe_sched_wakeup, NULL);	return ret;} static void tracing_sched_unregister(void){	unregister_trace_sched_switch(probe_sched_switch, NULL);	unregister_trace_sched_wakeup_new(probe_sched_wakeup, NULL);	unregister_trace_sched_wakeup(probe_sched_wakeup, NULL);} static void tracing_start_sched_switch(int ops){	bool sched_register; 	mutex_lock(&sched_register_mutex);	sched_register = (!sched_cmdline_ref && !sched_tgid_ref); 	switch (ops) {	case RECORD_CMDLINE:		sched_cmdline_ref++;		break; 	case RECORD_TGID:		sched_tgid_ref++;		break;	} 	if (sched_register && (sched_cmdline_ref || sched_tgid_ref))		tracing_sched_register();	mutex_unlock(&sched_register_mutex);} static void tracing_stop_sched_switch(int ops){	mutex_lock(&sched_register_mutex); 	switch (ops) {	case RECORD_CMDLINE:		sched_cmdline_ref--;		break; 	case RECORD_TGID:		sched_tgid_ref--;		break;	} 	if (!sched_cmdline_ref && !sched_tgid_ref)		tracing_sched_unregister();	mutex_unlock(&sched_register_mutex);} void tracing_start_cmdline_record(void){	tracing_start_sched_switch(RECORD_CMDLINE);} void tracing_stop_cmdline_record(void){	tracing_stop_sched_switch(RECORD_CMDLINE);} void tracing_start_tgid_record(void){	tracing_start_sched_switch(RECORD_TGID);} void tracing_stop_tgid_record(void){	tracing_stop_sched_switch(RECORD_TGID);} /* * The tgid_map array maps from pid to tgid; i.e. the value stored at index i * is the tgid last observed corresponding to pid=i. */static int *tgid_map; /* The maximum valid index into tgid_map. */static size_t tgid_map_max; #define SAVED_CMDLINES_DEFAULT 128#define NO_CMDLINE_MAP UINT_MAX/* * Preemption must be disabled before acquiring trace_cmdline_lock. * The various trace_arrays' max_lock must be acquired in a context * where interrupt is disabled. */static arch_spinlock_t trace_cmdline_lock = __ARCH_SPIN_LOCK_UNLOCKED;struct saved_cmdlines_buffer {	unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1];	unsigned *map_cmdline_to_pid;	unsigned cmdline_num;	int cmdline_idx;	char saved_cmdlines[];};static struct saved_cmdlines_buffer *savedcmd; /* Holds the size of a cmdline and pid element */#define SAVED_CMDLINE_MAP_ELEMENT_SIZE(s)			\	(TASK_COMM_LEN + sizeof((s)->map_cmdline_to_pid[0])) static inline char *get_saved_cmdlines(int idx){	return &savedcmd->saved_cmdlines[idx * TASK_COMM_LEN];} static inline void set_cmdline(int idx, const char *cmdline){	strscpy(get_saved_cmdlines(idx), cmdline, TASK_COMM_LEN);} static void free_saved_cmdlines_buffer(struct saved_cmdlines_buffer *s){	int order = get_order(sizeof(*s) + s->cmdline_num * TASK_COMM_LEN); 	kmemleak_free(s);	free_pages((unsigned long)s, order);} static struct saved_cmdlines_buffer *allocate_cmdlines_buffer(unsigned int val){	struct saved_cmdlines_buffer *s;	struct page *page;	int orig_size, size;	int order; 	/* Figure out how much is needed to hold the given number of cmdlines */	orig_size = sizeof(*s) + val * SAVED_CMDLINE_MAP_ELEMENT_SIZE(s);	order = get_order(orig_size);	size = 1 << (order + PAGE_SHIFT);	page = alloc_pages(GFP_KERNEL, order);	if (!page)		return NULL; 	s = page_address(page);	kmemleak_alloc(s, size, 1, GFP_KERNEL);	memset(s, 0, sizeof(*s)); 	/* Round up to actual allocation */	val = (size - sizeof(*s)) / SAVED_CMDLINE_MAP_ELEMENT_SIZE(s);	s->cmdline_num = val; 	/* Place map_cmdline_to_pid array right after saved_cmdlines */	s->map_cmdline_to_pid = (unsigned *)&s->saved_cmdlines[val * TASK_COMM_LEN]; 	memset(&s->map_pid_to_cmdline, NO_CMDLINE_MAP,	       sizeof(s->map_pid_to_cmdline));	memset(s->map_cmdline_to_pid, NO_CMDLINE_MAP,	       val * sizeof(*s->map_cmdline_to_pid)); 	return s;} int trace_create_savedcmd(void){	savedcmd = allocate_cmdlines_buffer(SAVED_CMDLINES_DEFAULT); 	return savedcmd ? 0 : -ENOMEM;} int trace_save_cmdline(struct task_struct *tsk){	unsigned tpid, idx; 	/* treat recording of idle task as a success */	if (!tsk->pid)		return 1; 	BUILD_BUG_ON(!is_power_of_2(PID_MAX_DEFAULT)); 	tpid = tsk->pid & (PID_MAX_DEFAULT - 1); 	/*	 * It's not the end of the world if we don't get	 * the lock, but we also don't want to spin	 * nor do we want to disable interrupts,	 * so if we miss here, then better luck next time.	 *	 * This is called within the scheduler and wake up, so interrupts	 * had better been disabled and run queue lock been held.	 */	lockdep_assert_preemption_disabled();	if (!arch_spin_trylock(&trace_cmdline_lock))		return 0; 	idx = savedcmd->map_pid_to_cmdline[tpid];	if (idx == NO_CMDLINE_MAP) {		idx = (savedcmd->cmdline_idx + 1) % savedcmd->cmdline_num; 		savedcmd->map_pid_to_cmdline[tpid] = idx;		savedcmd->cmdline_idx = idx;	} 	savedcmd->map_cmdline_to_pid[idx] = tsk->pid;	set_cmdline(idx, tsk->comm); 	arch_spin_unlock(&trace_cmdline_lock); 	return 1;} static void __trace_find_cmdline(int pid, char comm[]){	unsigned map;	int tpid; 	if (!pid) {		strcpy(comm, "<idle>");		return;	} 	if (WARN_ON_ONCE(pid < 0)) {		strcpy(comm, "<XXX>");		return;	} 	tpid = pid & (PID_MAX_DEFAULT - 1);	map = savedcmd->map_pid_to_cmdline[tpid];	if (map != NO_CMDLINE_MAP) {		tpid = savedcmd->map_cmdline_to_pid[map];		if (tpid == pid) {			strscpy(comm, get_saved_cmdlines(map), TASK_COMM_LEN);			return;		}	}	strcpy(comm, "<...>");} void trace_find_cmdline(int pid, char comm[]){	preempt_disable();	arch_spin_lock(&trace_cmdline_lock); 	__trace_find_cmdline(pid, comm); 	arch_spin_unlock(&trace_cmdline_lock);	preempt_enable();} static int *trace_find_tgid_ptr(int pid){	/*	 * Pairs with the smp_store_release in set_tracer_flag() to ensure that	 * if we observe a non-NULL tgid_map then we also observe the correct	 * tgid_map_max.	 */	int *map = smp_load_acquire(&tgid_map); 	if (unlikely(!map || pid > tgid_map_max))		return NULL; 	return &map[pid];} int trace_find_tgid(int pid){	int *ptr = trace_find_tgid_ptr(pid); 	return ptr ? *ptr : 0;} static int trace_save_tgid(struct task_struct *tsk){	int *ptr; 	/* treat recording of idle task as a success */	if (!tsk->pid)		return 1; 	ptr = trace_find_tgid_ptr(tsk->pid);	if (!ptr)		return 0; 	*ptr = tsk->tgid;	return 1;} static bool tracing_record_taskinfo_skip(int flags){	if (unlikely(!(flags & (TRACE_RECORD_CMDLINE | TRACE_RECORD_TGID))))		return true;	if (!__this_cpu_read(trace_taskinfo_save))		return true;	return false;} /** * tracing_record_taskinfo - record the task info of a task * * @task:  task to record * @flags: TRACE_RECORD_CMDLINE for recording comm *         TRACE_RECORD_TGID for recording tgid */void tracing_record_taskinfo(struct task_struct *task, int flags){	bool done; 	if (tracing_record_taskinfo_skip(flags))		return; 	/*	 * Record as much task information as possible. If some fail, continue	 * to try to record the others.	 */	done = !(flags & TRACE_RECORD_CMDLINE) || trace_save_cmdline(task);	done &= !(flags & TRACE_RECORD_TGID) || trace_save_tgid(task); 	/* If recording any information failed, retry again soon. */	if (!done)		return; 	__this_cpu_write(trace_taskinfo_save, false);} /** * tracing_record_taskinfo_sched_switch - record task info for sched_switch * * @prev: previous task during sched_switch * @next: next task during sched_switch * @flags: TRACE_RECORD_CMDLINE for recording comm *         TRACE_RECORD_TGID for recording tgid */void tracing_record_taskinfo_sched_switch(struct task_struct *prev,					  struct task_struct *next, int flags){	bool done; 	if (tracing_record_taskinfo_skip(flags))		return; 	/*	 * Record as much task information as possible. If some fail, continue	 * to try to record the others.	 */	done  = !(flags & TRACE_RECORD_CMDLINE) || trace_save_cmdline(prev);	done &= !(flags & TRACE_RECORD_CMDLINE) || trace_save_cmdline(next);	done &= !(flags & TRACE_RECORD_TGID) || trace_save_tgid(prev);	done &= !(flags & TRACE_RECORD_TGID) || trace_save_tgid(next); 	/* If recording any information failed, retry again soon. */	if (!done)		return; 	__this_cpu_write(trace_taskinfo_save, false);} /* Helpers to record a specific task information */void tracing_record_cmdline(struct task_struct *task){	tracing_record_taskinfo(task, TRACE_RECORD_CMDLINE);} void tracing_record_tgid(struct task_struct *task){	tracing_record_taskinfo(task, TRACE_RECORD_TGID);} int trace_alloc_tgid_map(void){	int *map; 	if (tgid_map)		return 0; 	tgid_map_max = init_pid_ns.pid_max;	map = kvzalloc_objs(*tgid_map, tgid_map_max + 1);	if (!map)		return -ENOMEM; 	/*	 * Pairs with smp_load_acquire() in	 * trace_find_tgid_ptr() to ensure that if it observes	 * the tgid_map we just allocated then it also observes	 * the corresponding tgid_map_max value.	 */	smp_store_release(&tgid_map, map);	return 0;} static void *saved_tgids_next(struct seq_file *m, void *v, loff_t *pos){	int pid = ++(*pos); 	return trace_find_tgid_ptr(pid);} static void *saved_tgids_start(struct seq_file *m, loff_t *pos){	int pid = *pos; 	return trace_find_tgid_ptr(pid);} static void saved_tgids_stop(struct seq_file *m, void *v){} static int saved_tgids_show(struct seq_file *m, void *v){	int *entry = (int *)v;	int pid = entry - tgid_map;	int tgid = *entry; 	if (tgid == 0)		return SEQ_SKIP; 	seq_printf(m, "%d %d\n", pid, tgid);	return 0;} static const struct seq_operations tracing_saved_tgids_seq_ops = {	.start		= saved_tgids_start,	.stop		= saved_tgids_stop,	.next		= saved_tgids_next,	.show		= saved_tgids_show,}; static int tracing_saved_tgids_open(struct inode *inode, struct file *filp){

Lines 1–500 of 665.