18c2ecf20Sopenharmony_ci/**
28c2ecf20Sopenharmony_ci * @file buffer_sync.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * @remark Copyright 2002-2009 OProfile authors
58c2ecf20Sopenharmony_ci * @remark Read the file COPYING
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * @author John Levon <levon@movementarian.org>
88c2ecf20Sopenharmony_ci * @author Barry Kasindorf
98c2ecf20Sopenharmony_ci * @author Robert Richter <robert.richter@amd.com>
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * This is the core of the buffer management. Each
128c2ecf20Sopenharmony_ci * CPU buffer is processed and entered into the
138c2ecf20Sopenharmony_ci * global event buffer. Such processing is necessary
148c2ecf20Sopenharmony_ci * in several circumstances, mentioned below.
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * The processing does the job of converting the
178c2ecf20Sopenharmony_ci * transitory EIP value into a persistent dentry/offset
188c2ecf20Sopenharmony_ci * value that the profiler can record at its leisure.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * See fs/dcookies.c for a description of the dentry/offset
218c2ecf20Sopenharmony_ci * objects.
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <linux/file.h>
258c2ecf20Sopenharmony_ci#include <linux/mm.h>
268c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
278c2ecf20Sopenharmony_ci#include <linux/notifier.h>
288c2ecf20Sopenharmony_ci#include <linux/dcookies.h>
298c2ecf20Sopenharmony_ci#include <linux/profile.h>
308c2ecf20Sopenharmony_ci#include <linux/module.h>
318c2ecf20Sopenharmony_ci#include <linux/fs.h>
328c2ecf20Sopenharmony_ci#include <linux/oprofile.h>
338c2ecf20Sopenharmony_ci#include <linux/sched.h>
348c2ecf20Sopenharmony_ci#include <linux/sched/mm.h>
358c2ecf20Sopenharmony_ci#include <linux/sched/task.h>
368c2ecf20Sopenharmony_ci#include <linux/gfp.h>
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#include "oprofile_stats.h"
398c2ecf20Sopenharmony_ci#include "event_buffer.h"
408c2ecf20Sopenharmony_ci#include "cpu_buffer.h"
418c2ecf20Sopenharmony_ci#include "buffer_sync.h"
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic LIST_HEAD(dying_tasks);
448c2ecf20Sopenharmony_cistatic LIST_HEAD(dead_tasks);
458c2ecf20Sopenharmony_cistatic cpumask_var_t marked_cpus;
468c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(task_mortuary);
478c2ecf20Sopenharmony_cistatic void process_task_mortuary(void);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* Take ownership of the task struct and place it on the
508c2ecf20Sopenharmony_ci * list for processing. Only after two full buffer syncs
518c2ecf20Sopenharmony_ci * does the task eventually get freed, because by then
528c2ecf20Sopenharmony_ci * we are sure we will not reference it again.
538c2ecf20Sopenharmony_ci * Can be invoked from softirq via RCU callback due to
548c2ecf20Sopenharmony_ci * call_rcu() of the task struct, hence the _irqsave.
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_cistatic int
578c2ecf20Sopenharmony_citask_free_notify(struct notifier_block *self, unsigned long val, void *data)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	unsigned long flags;
608c2ecf20Sopenharmony_ci	struct task_struct *task = data;
618c2ecf20Sopenharmony_ci	spin_lock_irqsave(&task_mortuary, flags);
628c2ecf20Sopenharmony_ci	list_add(&task->tasks, &dying_tasks);
638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&task_mortuary, flags);
648c2ecf20Sopenharmony_ci	return NOTIFY_OK;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* The task is on its way out. A sync of the buffer means we can catch
698c2ecf20Sopenharmony_ci * any remaining samples for this task.
708c2ecf20Sopenharmony_ci */
718c2ecf20Sopenharmony_cistatic int
728c2ecf20Sopenharmony_citask_exit_notify(struct notifier_block *self, unsigned long val, void *data)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	/* To avoid latency problems, we only process the current CPU,
758c2ecf20Sopenharmony_ci	 * hoping that most samples for the task are on this CPU
768c2ecf20Sopenharmony_ci	 */
778c2ecf20Sopenharmony_ci	sync_buffer(raw_smp_processor_id());
788c2ecf20Sopenharmony_ci	return 0;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/* The task is about to try a do_munmap(). We peek at what it's going to
838c2ecf20Sopenharmony_ci * do, and if it's an executable region, process the samples first, so
848c2ecf20Sopenharmony_ci * we don't lose any. This does not have to be exact, it's a QoI issue
858c2ecf20Sopenharmony_ci * only.
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_cistatic int
888c2ecf20Sopenharmony_cimunmap_notify(struct notifier_block *self, unsigned long val, void *data)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	unsigned long addr = (unsigned long)data;
918c2ecf20Sopenharmony_ci	struct mm_struct *mm = current->mm;
928c2ecf20Sopenharmony_ci	struct vm_area_struct *mpnt;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	mmap_read_lock(mm);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	mpnt = find_vma(mm, addr);
978c2ecf20Sopenharmony_ci	if (mpnt && mpnt->vm_file && (mpnt->vm_flags & VM_EXEC)) {
988c2ecf20Sopenharmony_ci		mmap_read_unlock(mm);
998c2ecf20Sopenharmony_ci		/* To avoid latency problems, we only process the current CPU,
1008c2ecf20Sopenharmony_ci		 * hoping that most samples for the task are on this CPU
1018c2ecf20Sopenharmony_ci		 */
1028c2ecf20Sopenharmony_ci		sync_buffer(raw_smp_processor_id());
1038c2ecf20Sopenharmony_ci		return 0;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	mmap_read_unlock(mm);
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/* We need to be told about new modules so we don't attribute to a previously
1128c2ecf20Sopenharmony_ci * loaded module, or drop the samples on the floor.
1138c2ecf20Sopenharmony_ci */
1148c2ecf20Sopenharmony_cistatic int
1158c2ecf20Sopenharmony_cimodule_load_notify(struct notifier_block *self, unsigned long val, void *data)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci#ifdef CONFIG_MODULES
1188c2ecf20Sopenharmony_ci	if (val != MODULE_STATE_COMING)
1198c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* FIXME: should we process all CPU buffers ? */
1228c2ecf20Sopenharmony_ci	mutex_lock(&buffer_mutex);
1238c2ecf20Sopenharmony_ci	add_event_entry(ESCAPE_CODE);
1248c2ecf20Sopenharmony_ci	add_event_entry(MODULE_LOADED_CODE);
1258c2ecf20Sopenharmony_ci	mutex_unlock(&buffer_mutex);
1268c2ecf20Sopenharmony_ci#endif
1278c2ecf20Sopenharmony_ci	return NOTIFY_OK;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic struct notifier_block task_free_nb = {
1328c2ecf20Sopenharmony_ci	.notifier_call	= task_free_notify,
1338c2ecf20Sopenharmony_ci};
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic struct notifier_block task_exit_nb = {
1368c2ecf20Sopenharmony_ci	.notifier_call	= task_exit_notify,
1378c2ecf20Sopenharmony_ci};
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic struct notifier_block munmap_nb = {
1408c2ecf20Sopenharmony_ci	.notifier_call	= munmap_notify,
1418c2ecf20Sopenharmony_ci};
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic struct notifier_block module_load_nb = {
1448c2ecf20Sopenharmony_ci	.notifier_call = module_load_notify,
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic void free_all_tasks(void)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	/* make sure we don't leak task structs */
1508c2ecf20Sopenharmony_ci	process_task_mortuary();
1518c2ecf20Sopenharmony_ci	process_task_mortuary();
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ciint sync_start(void)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	int err;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (!zalloc_cpumask_var(&marked_cpus, GFP_KERNEL))
1598c2ecf20Sopenharmony_ci		return -ENOMEM;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	err = task_handoff_register(&task_free_nb);
1628c2ecf20Sopenharmony_ci	if (err)
1638c2ecf20Sopenharmony_ci		goto out1;
1648c2ecf20Sopenharmony_ci	err = profile_event_register(PROFILE_TASK_EXIT, &task_exit_nb);
1658c2ecf20Sopenharmony_ci	if (err)
1668c2ecf20Sopenharmony_ci		goto out2;
1678c2ecf20Sopenharmony_ci	err = profile_event_register(PROFILE_MUNMAP, &munmap_nb);
1688c2ecf20Sopenharmony_ci	if (err)
1698c2ecf20Sopenharmony_ci		goto out3;
1708c2ecf20Sopenharmony_ci	err = register_module_notifier(&module_load_nb);
1718c2ecf20Sopenharmony_ci	if (err)
1728c2ecf20Sopenharmony_ci		goto out4;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	start_cpu_work();
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ciout:
1778c2ecf20Sopenharmony_ci	return err;
1788c2ecf20Sopenharmony_ciout4:
1798c2ecf20Sopenharmony_ci	profile_event_unregister(PROFILE_MUNMAP, &munmap_nb);
1808c2ecf20Sopenharmony_ciout3:
1818c2ecf20Sopenharmony_ci	profile_event_unregister(PROFILE_TASK_EXIT, &task_exit_nb);
1828c2ecf20Sopenharmony_ciout2:
1838c2ecf20Sopenharmony_ci	task_handoff_unregister(&task_free_nb);
1848c2ecf20Sopenharmony_ci	free_all_tasks();
1858c2ecf20Sopenharmony_ciout1:
1868c2ecf20Sopenharmony_ci	free_cpumask_var(marked_cpus);
1878c2ecf20Sopenharmony_ci	goto out;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_civoid sync_stop(void)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	end_cpu_work();
1948c2ecf20Sopenharmony_ci	unregister_module_notifier(&module_load_nb);
1958c2ecf20Sopenharmony_ci	profile_event_unregister(PROFILE_MUNMAP, &munmap_nb);
1968c2ecf20Sopenharmony_ci	profile_event_unregister(PROFILE_TASK_EXIT, &task_exit_nb);
1978c2ecf20Sopenharmony_ci	task_handoff_unregister(&task_free_nb);
1988c2ecf20Sopenharmony_ci	barrier();			/* do all of the above first */
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	flush_cpu_work();
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	free_all_tasks();
2038c2ecf20Sopenharmony_ci	free_cpumask_var(marked_cpus);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/* Optimisation. We can manage without taking the dcookie sem
2088c2ecf20Sopenharmony_ci * because we cannot reach this code without at least one
2098c2ecf20Sopenharmony_ci * dcookie user still being registered (namely, the reader
2108c2ecf20Sopenharmony_ci * of the event buffer). */
2118c2ecf20Sopenharmony_cistatic inline unsigned long fast_get_dcookie(const struct path *path)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	unsigned long cookie;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (path->dentry->d_flags & DCACHE_COOKIE)
2168c2ecf20Sopenharmony_ci		return (unsigned long)path->dentry;
2178c2ecf20Sopenharmony_ci	get_dcookie(path, &cookie);
2188c2ecf20Sopenharmony_ci	return cookie;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci/* Look up the dcookie for the task's mm->exe_file,
2238c2ecf20Sopenharmony_ci * which corresponds loosely to "application name". This is
2248c2ecf20Sopenharmony_ci * not strictly necessary but allows oprofile to associate
2258c2ecf20Sopenharmony_ci * shared-library samples with particular applications
2268c2ecf20Sopenharmony_ci */
2278c2ecf20Sopenharmony_cistatic unsigned long get_exec_dcookie(struct mm_struct *mm)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	unsigned long cookie = NO_COOKIE;
2308c2ecf20Sopenharmony_ci	struct file *exe_file;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (!mm)
2338c2ecf20Sopenharmony_ci		goto done;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	exe_file = get_mm_exe_file(mm);
2368c2ecf20Sopenharmony_ci	if (!exe_file)
2378c2ecf20Sopenharmony_ci		goto done;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	cookie = fast_get_dcookie(&exe_file->f_path);
2408c2ecf20Sopenharmony_ci	fput(exe_file);
2418c2ecf20Sopenharmony_cidone:
2428c2ecf20Sopenharmony_ci	return cookie;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci/* Convert the EIP value of a sample into a persistent dentry/offset
2478c2ecf20Sopenharmony_ci * pair that can then be added to the global event buffer. We make
2488c2ecf20Sopenharmony_ci * sure to do this lookup before a mm->mmap modification happens so
2498c2ecf20Sopenharmony_ci * we don't lose track.
2508c2ecf20Sopenharmony_ci *
2518c2ecf20Sopenharmony_ci * The caller must ensure the mm is not nil (ie: not a kernel thread).
2528c2ecf20Sopenharmony_ci */
2538c2ecf20Sopenharmony_cistatic unsigned long
2548c2ecf20Sopenharmony_cilookup_dcookie(struct mm_struct *mm, unsigned long addr, off_t *offset)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	unsigned long cookie = NO_COOKIE;
2578c2ecf20Sopenharmony_ci	struct vm_area_struct *vma;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	mmap_read_lock(mm);
2608c2ecf20Sopenharmony_ci	for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) {
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		if (addr < vma->vm_start || addr >= vma->vm_end)
2638c2ecf20Sopenharmony_ci			continue;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci		if (vma->vm_file) {
2668c2ecf20Sopenharmony_ci			cookie = fast_get_dcookie(&vma->vm_file->f_path);
2678c2ecf20Sopenharmony_ci			*offset = (vma->vm_pgoff << PAGE_SHIFT) + addr -
2688c2ecf20Sopenharmony_ci				vma->vm_start;
2698c2ecf20Sopenharmony_ci		} else {
2708c2ecf20Sopenharmony_ci			/* must be an anonymous map */
2718c2ecf20Sopenharmony_ci			*offset = addr;
2728c2ecf20Sopenharmony_ci		}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		break;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (!vma)
2788c2ecf20Sopenharmony_ci		cookie = INVALID_COOKIE;
2798c2ecf20Sopenharmony_ci	mmap_read_unlock(mm);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	return cookie;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic unsigned long last_cookie = INVALID_COOKIE;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic void add_cpu_switch(int i)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	add_event_entry(ESCAPE_CODE);
2898c2ecf20Sopenharmony_ci	add_event_entry(CPU_SWITCH_CODE);
2908c2ecf20Sopenharmony_ci	add_event_entry(i);
2918c2ecf20Sopenharmony_ci	last_cookie = INVALID_COOKIE;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic void add_kernel_ctx_switch(unsigned int in_kernel)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	add_event_entry(ESCAPE_CODE);
2978c2ecf20Sopenharmony_ci	if (in_kernel)
2988c2ecf20Sopenharmony_ci		add_event_entry(KERNEL_ENTER_SWITCH_CODE);
2998c2ecf20Sopenharmony_ci	else
3008c2ecf20Sopenharmony_ci		add_event_entry(KERNEL_EXIT_SWITCH_CODE);
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic void
3048c2ecf20Sopenharmony_ciadd_user_ctx_switch(struct task_struct const *task, unsigned long cookie)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	add_event_entry(ESCAPE_CODE);
3078c2ecf20Sopenharmony_ci	add_event_entry(CTX_SWITCH_CODE);
3088c2ecf20Sopenharmony_ci	add_event_entry(task->pid);
3098c2ecf20Sopenharmony_ci	add_event_entry(cookie);
3108c2ecf20Sopenharmony_ci	/* Another code for daemon back-compat */
3118c2ecf20Sopenharmony_ci	add_event_entry(ESCAPE_CODE);
3128c2ecf20Sopenharmony_ci	add_event_entry(CTX_TGID_CODE);
3138c2ecf20Sopenharmony_ci	add_event_entry(task->tgid);
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic void add_cookie_switch(unsigned long cookie)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	add_event_entry(ESCAPE_CODE);
3208c2ecf20Sopenharmony_ci	add_event_entry(COOKIE_SWITCH_CODE);
3218c2ecf20Sopenharmony_ci	add_event_entry(cookie);
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic void add_trace_begin(void)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	add_event_entry(ESCAPE_CODE);
3288c2ecf20Sopenharmony_ci	add_event_entry(TRACE_BEGIN_CODE);
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic void add_data(struct op_entry *entry, struct mm_struct *mm)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	unsigned long code, pc, val;
3348c2ecf20Sopenharmony_ci	unsigned long cookie;
3358c2ecf20Sopenharmony_ci	off_t offset;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (!op_cpu_buffer_get_data(entry, &code))
3388c2ecf20Sopenharmony_ci		return;
3398c2ecf20Sopenharmony_ci	if (!op_cpu_buffer_get_data(entry, &pc))
3408c2ecf20Sopenharmony_ci		return;
3418c2ecf20Sopenharmony_ci	if (!op_cpu_buffer_get_size(entry))
3428c2ecf20Sopenharmony_ci		return;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	if (mm) {
3458c2ecf20Sopenharmony_ci		cookie = lookup_dcookie(mm, pc, &offset);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		if (cookie == NO_COOKIE)
3488c2ecf20Sopenharmony_ci			offset = pc;
3498c2ecf20Sopenharmony_ci		if (cookie == INVALID_COOKIE) {
3508c2ecf20Sopenharmony_ci			atomic_inc(&oprofile_stats.sample_lost_no_mapping);
3518c2ecf20Sopenharmony_ci			offset = pc;
3528c2ecf20Sopenharmony_ci		}
3538c2ecf20Sopenharmony_ci		if (cookie != last_cookie) {
3548c2ecf20Sopenharmony_ci			add_cookie_switch(cookie);
3558c2ecf20Sopenharmony_ci			last_cookie = cookie;
3568c2ecf20Sopenharmony_ci		}
3578c2ecf20Sopenharmony_ci	} else
3588c2ecf20Sopenharmony_ci		offset = pc;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	add_event_entry(ESCAPE_CODE);
3618c2ecf20Sopenharmony_ci	add_event_entry(code);
3628c2ecf20Sopenharmony_ci	add_event_entry(offset);	/* Offset from Dcookie */
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	while (op_cpu_buffer_get_data(entry, &val))
3658c2ecf20Sopenharmony_ci		add_event_entry(val);
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic inline void add_sample_entry(unsigned long offset, unsigned long event)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	add_event_entry(offset);
3718c2ecf20Sopenharmony_ci	add_event_entry(event);
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci/*
3768c2ecf20Sopenharmony_ci * Add a sample to the global event buffer. If possible the
3778c2ecf20Sopenharmony_ci * sample is converted into a persistent dentry/offset pair
3788c2ecf20Sopenharmony_ci * for later lookup from userspace. Return 0 on failure.
3798c2ecf20Sopenharmony_ci */
3808c2ecf20Sopenharmony_cistatic int
3818c2ecf20Sopenharmony_ciadd_sample(struct mm_struct *mm, struct op_sample *s, int in_kernel)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	unsigned long cookie;
3848c2ecf20Sopenharmony_ci	off_t offset;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	if (in_kernel) {
3878c2ecf20Sopenharmony_ci		add_sample_entry(s->eip, s->event);
3888c2ecf20Sopenharmony_ci		return 1;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	/* add userspace sample */
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if (!mm) {
3948c2ecf20Sopenharmony_ci		atomic_inc(&oprofile_stats.sample_lost_no_mm);
3958c2ecf20Sopenharmony_ci		return 0;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	cookie = lookup_dcookie(mm, s->eip, &offset);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (cookie == INVALID_COOKIE) {
4018c2ecf20Sopenharmony_ci		atomic_inc(&oprofile_stats.sample_lost_no_mapping);
4028c2ecf20Sopenharmony_ci		return 0;
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (cookie != last_cookie) {
4068c2ecf20Sopenharmony_ci		add_cookie_switch(cookie);
4078c2ecf20Sopenharmony_ci		last_cookie = cookie;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	add_sample_entry(offset, s->event);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	return 1;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic void release_mm(struct mm_struct *mm)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	if (!mm)
4198c2ecf20Sopenharmony_ci		return;
4208c2ecf20Sopenharmony_ci	mmput(mm);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic inline int is_code(unsigned long val)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	return val == ESCAPE_CODE;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci/* Move tasks along towards death. Any tasks on dead_tasks
4308c2ecf20Sopenharmony_ci * will definitely have no remaining references in any
4318c2ecf20Sopenharmony_ci * CPU buffers at this point, because we use two lists,
4328c2ecf20Sopenharmony_ci * and to have reached the list, it must have gone through
4338c2ecf20Sopenharmony_ci * one full sync already.
4348c2ecf20Sopenharmony_ci */
4358c2ecf20Sopenharmony_cistatic void process_task_mortuary(void)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	unsigned long flags;
4388c2ecf20Sopenharmony_ci	LIST_HEAD(local_dead_tasks);
4398c2ecf20Sopenharmony_ci	struct task_struct *task;
4408c2ecf20Sopenharmony_ci	struct task_struct *ttask;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	spin_lock_irqsave(&task_mortuary, flags);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	list_splice_init(&dead_tasks, &local_dead_tasks);
4458c2ecf20Sopenharmony_ci	list_splice_init(&dying_tasks, &dead_tasks);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&task_mortuary, flags);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	list_for_each_entry_safe(task, ttask, &local_dead_tasks, tasks) {
4508c2ecf20Sopenharmony_ci		list_del(&task->tasks);
4518c2ecf20Sopenharmony_ci		free_task(task);
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic void mark_done(int cpu)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	int i;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	cpumask_set_cpu(cpu, marked_cpus);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	for_each_online_cpu(i) {
4638c2ecf20Sopenharmony_ci		if (!cpumask_test_cpu(i, marked_cpus))
4648c2ecf20Sopenharmony_ci			return;
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	/* All CPUs have been processed at least once,
4688c2ecf20Sopenharmony_ci	 * we can process the mortuary once
4698c2ecf20Sopenharmony_ci	 */
4708c2ecf20Sopenharmony_ci	process_task_mortuary();
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	cpumask_clear(marked_cpus);
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci/* FIXME: this is not sufficient if we implement syscall barrier backtrace
4778c2ecf20Sopenharmony_ci * traversal, the code switch to sb_sample_start at first kernel enter/exit
4788c2ecf20Sopenharmony_ci * switch so we need a fifth state and some special handling in sync_buffer()
4798c2ecf20Sopenharmony_ci */
4808c2ecf20Sopenharmony_citypedef enum {
4818c2ecf20Sopenharmony_ci	sb_bt_ignore = -2,
4828c2ecf20Sopenharmony_ci	sb_buffer_start,
4838c2ecf20Sopenharmony_ci	sb_bt_start,
4848c2ecf20Sopenharmony_ci	sb_sample_start,
4858c2ecf20Sopenharmony_ci} sync_buffer_state;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci/* Sync one of the CPU's buffers into the global event buffer.
4888c2ecf20Sopenharmony_ci * Here we need to go through each batch of samples punctuated
4898c2ecf20Sopenharmony_ci * by context switch notes, taking the task's mmap_lock and doing
4908c2ecf20Sopenharmony_ci * lookup in task->mm->mmap to convert EIP into dcookie/offset
4918c2ecf20Sopenharmony_ci * value.
4928c2ecf20Sopenharmony_ci */
4938c2ecf20Sopenharmony_civoid sync_buffer(int cpu)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	struct mm_struct *mm = NULL;
4968c2ecf20Sopenharmony_ci	struct mm_struct *oldmm;
4978c2ecf20Sopenharmony_ci	unsigned long val;
4988c2ecf20Sopenharmony_ci	struct task_struct *new;
4998c2ecf20Sopenharmony_ci	unsigned long cookie = 0;
5008c2ecf20Sopenharmony_ci	int in_kernel = 1;
5018c2ecf20Sopenharmony_ci	sync_buffer_state state = sb_buffer_start;
5028c2ecf20Sopenharmony_ci	unsigned int i;
5038c2ecf20Sopenharmony_ci	unsigned long available;
5048c2ecf20Sopenharmony_ci	unsigned long flags;
5058c2ecf20Sopenharmony_ci	struct op_entry entry;
5068c2ecf20Sopenharmony_ci	struct op_sample *sample;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	mutex_lock(&buffer_mutex);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	add_cpu_switch(cpu);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	op_cpu_buffer_reset(cpu);
5138c2ecf20Sopenharmony_ci	available = op_cpu_buffer_entries(cpu);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	for (i = 0; i < available; ++i) {
5168c2ecf20Sopenharmony_ci		sample = op_cpu_buffer_read_entry(&entry, cpu);
5178c2ecf20Sopenharmony_ci		if (!sample)
5188c2ecf20Sopenharmony_ci			break;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci		if (is_code(sample->eip)) {
5218c2ecf20Sopenharmony_ci			flags = sample->event;
5228c2ecf20Sopenharmony_ci			if (flags & TRACE_BEGIN) {
5238c2ecf20Sopenharmony_ci				state = sb_bt_start;
5248c2ecf20Sopenharmony_ci				add_trace_begin();
5258c2ecf20Sopenharmony_ci			}
5268c2ecf20Sopenharmony_ci			if (flags & KERNEL_CTX_SWITCH) {
5278c2ecf20Sopenharmony_ci				/* kernel/userspace switch */
5288c2ecf20Sopenharmony_ci				in_kernel = flags & IS_KERNEL;
5298c2ecf20Sopenharmony_ci				if (state == sb_buffer_start)
5308c2ecf20Sopenharmony_ci					state = sb_sample_start;
5318c2ecf20Sopenharmony_ci				add_kernel_ctx_switch(flags & IS_KERNEL);
5328c2ecf20Sopenharmony_ci			}
5338c2ecf20Sopenharmony_ci			if (flags & USER_CTX_SWITCH
5348c2ecf20Sopenharmony_ci			    && op_cpu_buffer_get_data(&entry, &val)) {
5358c2ecf20Sopenharmony_ci				/* userspace context switch */
5368c2ecf20Sopenharmony_ci				new = (struct task_struct *)val;
5378c2ecf20Sopenharmony_ci				oldmm = mm;
5388c2ecf20Sopenharmony_ci				release_mm(oldmm);
5398c2ecf20Sopenharmony_ci				mm = get_task_mm(new);
5408c2ecf20Sopenharmony_ci				if (mm != oldmm)
5418c2ecf20Sopenharmony_ci					cookie = get_exec_dcookie(mm);
5428c2ecf20Sopenharmony_ci				add_user_ctx_switch(new, cookie);
5438c2ecf20Sopenharmony_ci			}
5448c2ecf20Sopenharmony_ci			if (op_cpu_buffer_get_size(&entry))
5458c2ecf20Sopenharmony_ci				add_data(&entry, mm);
5468c2ecf20Sopenharmony_ci			continue;
5478c2ecf20Sopenharmony_ci		}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci		if (state < sb_bt_start)
5508c2ecf20Sopenharmony_ci			/* ignore sample */
5518c2ecf20Sopenharmony_ci			continue;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci		if (add_sample(mm, sample, in_kernel))
5548c2ecf20Sopenharmony_ci			continue;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci		/* ignore backtraces if failed to add a sample */
5578c2ecf20Sopenharmony_ci		if (state == sb_bt_start) {
5588c2ecf20Sopenharmony_ci			state = sb_bt_ignore;
5598c2ecf20Sopenharmony_ci			atomic_inc(&oprofile_stats.bt_lost_no_mapping);
5608c2ecf20Sopenharmony_ci		}
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci	release_mm(mm);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	mark_done(cpu);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	mutex_unlock(&buffer_mutex);
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci/* The function can be used to add a buffer worth of data directly to
5708c2ecf20Sopenharmony_ci * the kernel buffer. The buffer is assumed to be a circular buffer.
5718c2ecf20Sopenharmony_ci * Take the entries from index start and end at index end, wrapping
5728c2ecf20Sopenharmony_ci * at max_entries.
5738c2ecf20Sopenharmony_ci */
5748c2ecf20Sopenharmony_civoid oprofile_put_buff(unsigned long *buf, unsigned int start,
5758c2ecf20Sopenharmony_ci		       unsigned int stop, unsigned int max)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	int i;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	i = start;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	mutex_lock(&buffer_mutex);
5828c2ecf20Sopenharmony_ci	while (i != stop) {
5838c2ecf20Sopenharmony_ci		add_event_entry(buf[i++]);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci		if (i >= max)
5868c2ecf20Sopenharmony_ci			i = 0;
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	mutex_unlock(&buffer_mutex);
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
592