18c2ecf20Sopenharmony_ci/**
28c2ecf20Sopenharmony_ci * @file cpu_buffer.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 <barry.kasindorf@amd.com>
98c2ecf20Sopenharmony_ci * @author Robert Richter <robert.richter@amd.com>
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Each CPU has a local buffer that stores PC value/event
128c2ecf20Sopenharmony_ci * pairs. We also log context switches when we notice them.
138c2ecf20Sopenharmony_ci * Eventually each CPU's buffer is processed into the global
148c2ecf20Sopenharmony_ci * event buffer by sync_buffer().
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * We use a local buffer for two reasons: an NMI or similar
178c2ecf20Sopenharmony_ci * interrupt cannot synchronise, and high sampling rates
188c2ecf20Sopenharmony_ci * would lead to catastrophic global synchronisation if
198c2ecf20Sopenharmony_ci * a global buffer was used.
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <linux/sched.h>
238c2ecf20Sopenharmony_ci#include <linux/oprofile.h>
248c2ecf20Sopenharmony_ci#include <linux/errno.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <asm/ptrace.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "event_buffer.h"
298c2ecf20Sopenharmony_ci#include "cpu_buffer.h"
308c2ecf20Sopenharmony_ci#include "buffer_sync.h"
318c2ecf20Sopenharmony_ci#include "oprof.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define OP_BUFFER_FLAGS	0
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic struct trace_buffer *op_ring_buffer;
368c2ecf20Sopenharmony_ciDEFINE_PER_CPU(struct oprofile_cpu_buffer, op_cpu_buffer);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void wq_sync_buffer(struct work_struct *work);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define DEFAULT_TIMER_EXPIRE (HZ / 10)
418c2ecf20Sopenharmony_cistatic int work_enabled;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ciunsigned long oprofile_get_cpu_buffer_size(void)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	return oprofile_cpu_buffer_size;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_civoid oprofile_cpu_buffer_inc_smpl_lost(void)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct oprofile_cpu_buffer *cpu_buf = this_cpu_ptr(&op_cpu_buffer);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	cpu_buf->sample_lost_overflow++;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_civoid free_cpu_buffers(void)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	if (op_ring_buffer)
588c2ecf20Sopenharmony_ci		ring_buffer_free(op_ring_buffer);
598c2ecf20Sopenharmony_ci	op_ring_buffer = NULL;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define RB_EVENT_HDR_SIZE 4
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ciint alloc_cpu_buffers(void)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	int i;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	unsigned long buffer_size = oprofile_cpu_buffer_size;
698c2ecf20Sopenharmony_ci	unsigned long byte_size = buffer_size * (sizeof(struct op_sample) +
708c2ecf20Sopenharmony_ci						 RB_EVENT_HDR_SIZE);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	op_ring_buffer = ring_buffer_alloc(byte_size, OP_BUFFER_FLAGS);
738c2ecf20Sopenharmony_ci	if (!op_ring_buffer)
748c2ecf20Sopenharmony_ci		goto fail;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	for_each_possible_cpu(i) {
778c2ecf20Sopenharmony_ci		struct oprofile_cpu_buffer *b = &per_cpu(op_cpu_buffer, i);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		b->last_task = NULL;
808c2ecf20Sopenharmony_ci		b->last_is_kernel = -1;
818c2ecf20Sopenharmony_ci		b->tracing = 0;
828c2ecf20Sopenharmony_ci		b->buffer_size = buffer_size;
838c2ecf20Sopenharmony_ci		b->sample_received = 0;
848c2ecf20Sopenharmony_ci		b->sample_lost_overflow = 0;
858c2ecf20Sopenharmony_ci		b->backtrace_aborted = 0;
868c2ecf20Sopenharmony_ci		b->sample_invalid_eip = 0;
878c2ecf20Sopenharmony_ci		b->cpu = i;
888c2ecf20Sopenharmony_ci		INIT_DELAYED_WORK(&b->work, wq_sync_buffer);
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci	return 0;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cifail:
938c2ecf20Sopenharmony_ci	free_cpu_buffers();
948c2ecf20Sopenharmony_ci	return -ENOMEM;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_civoid start_cpu_work(void)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	int i;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	work_enabled = 1;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	for_each_online_cpu(i) {
1048c2ecf20Sopenharmony_ci		struct oprofile_cpu_buffer *b = &per_cpu(op_cpu_buffer, i);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci		/*
1078c2ecf20Sopenharmony_ci		 * Spread the work by 1 jiffy per cpu so they dont all
1088c2ecf20Sopenharmony_ci		 * fire at once.
1098c2ecf20Sopenharmony_ci		 */
1108c2ecf20Sopenharmony_ci		schedule_delayed_work_on(i, &b->work, DEFAULT_TIMER_EXPIRE + i);
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_civoid end_cpu_work(void)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	work_enabled = 0;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_civoid flush_cpu_work(void)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	int i;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	for_each_online_cpu(i) {
1248c2ecf20Sopenharmony_ci		struct oprofile_cpu_buffer *b = &per_cpu(op_cpu_buffer, i);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		/* these works are per-cpu, no need for flush_sync */
1278c2ecf20Sopenharmony_ci		flush_delayed_work(&b->work);
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci/*
1328c2ecf20Sopenharmony_ci * This function prepares the cpu buffer to write a sample.
1338c2ecf20Sopenharmony_ci *
1348c2ecf20Sopenharmony_ci * Struct op_entry is used during operations on the ring buffer while
1358c2ecf20Sopenharmony_ci * struct op_sample contains the data that is stored in the ring
1368c2ecf20Sopenharmony_ci * buffer. Struct entry can be uninitialized. The function reserves a
1378c2ecf20Sopenharmony_ci * data array that is specified by size. Use
1388c2ecf20Sopenharmony_ci * op_cpu_buffer_write_commit() after preparing the sample. In case of
1398c2ecf20Sopenharmony_ci * errors a null pointer is returned, otherwise the pointer to the
1408c2ecf20Sopenharmony_ci * sample.
1418c2ecf20Sopenharmony_ci *
1428c2ecf20Sopenharmony_ci */
1438c2ecf20Sopenharmony_cistruct op_sample
1448c2ecf20Sopenharmony_ci*op_cpu_buffer_write_reserve(struct op_entry *entry, unsigned long size)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	entry->event = ring_buffer_lock_reserve
1478c2ecf20Sopenharmony_ci		(op_ring_buffer, sizeof(struct op_sample) +
1488c2ecf20Sopenharmony_ci		 size * sizeof(entry->sample->data[0]));
1498c2ecf20Sopenharmony_ci	if (!entry->event)
1508c2ecf20Sopenharmony_ci		return NULL;
1518c2ecf20Sopenharmony_ci	entry->sample = ring_buffer_event_data(entry->event);
1528c2ecf20Sopenharmony_ci	entry->size = size;
1538c2ecf20Sopenharmony_ci	entry->data = entry->sample->data;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	return entry->sample;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ciint op_cpu_buffer_write_commit(struct op_entry *entry)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	return ring_buffer_unlock_commit(op_ring_buffer, entry->event);
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistruct op_sample *op_cpu_buffer_read_entry(struct op_entry *entry, int cpu)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct ring_buffer_event *e;
1668c2ecf20Sopenharmony_ci	e = ring_buffer_consume(op_ring_buffer, cpu, NULL, NULL);
1678c2ecf20Sopenharmony_ci	if (!e)
1688c2ecf20Sopenharmony_ci		return NULL;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	entry->event = e;
1718c2ecf20Sopenharmony_ci	entry->sample = ring_buffer_event_data(e);
1728c2ecf20Sopenharmony_ci	entry->size = (ring_buffer_event_length(e) - sizeof(struct op_sample))
1738c2ecf20Sopenharmony_ci		/ sizeof(entry->sample->data[0]);
1748c2ecf20Sopenharmony_ci	entry->data = entry->sample->data;
1758c2ecf20Sopenharmony_ci	return entry->sample;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ciunsigned long op_cpu_buffer_entries(int cpu)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	return ring_buffer_entries_cpu(op_ring_buffer, cpu);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic int
1848c2ecf20Sopenharmony_ciop_add_code(struct oprofile_cpu_buffer *cpu_buf, unsigned long backtrace,
1858c2ecf20Sopenharmony_ci	    int is_kernel, struct task_struct *task)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct op_entry entry;
1888c2ecf20Sopenharmony_ci	struct op_sample *sample;
1898c2ecf20Sopenharmony_ci	unsigned long flags;
1908c2ecf20Sopenharmony_ci	int size;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	flags = 0;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (backtrace)
1958c2ecf20Sopenharmony_ci		flags |= TRACE_BEGIN;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/* notice a switch from user->kernel or vice versa */
1988c2ecf20Sopenharmony_ci	is_kernel = !!is_kernel;
1998c2ecf20Sopenharmony_ci	if (cpu_buf->last_is_kernel != is_kernel) {
2008c2ecf20Sopenharmony_ci		cpu_buf->last_is_kernel = is_kernel;
2018c2ecf20Sopenharmony_ci		flags |= KERNEL_CTX_SWITCH;
2028c2ecf20Sopenharmony_ci		if (is_kernel)
2038c2ecf20Sopenharmony_ci			flags |= IS_KERNEL;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* notice a task switch */
2078c2ecf20Sopenharmony_ci	if (cpu_buf->last_task != task) {
2088c2ecf20Sopenharmony_ci		cpu_buf->last_task = task;
2098c2ecf20Sopenharmony_ci		flags |= USER_CTX_SWITCH;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (!flags)
2138c2ecf20Sopenharmony_ci		/* nothing to do */
2148c2ecf20Sopenharmony_ci		return 0;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (flags & USER_CTX_SWITCH)
2178c2ecf20Sopenharmony_ci		size = 1;
2188c2ecf20Sopenharmony_ci	else
2198c2ecf20Sopenharmony_ci		size = 0;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	sample = op_cpu_buffer_write_reserve(&entry, size);
2228c2ecf20Sopenharmony_ci	if (!sample)
2238c2ecf20Sopenharmony_ci		return -ENOMEM;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	sample->eip = ESCAPE_CODE;
2268c2ecf20Sopenharmony_ci	sample->event = flags;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (size)
2298c2ecf20Sopenharmony_ci		op_cpu_buffer_add_data(&entry, (unsigned long)task);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	op_cpu_buffer_write_commit(&entry);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return 0;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic inline int
2378c2ecf20Sopenharmony_ciop_add_sample(struct oprofile_cpu_buffer *cpu_buf,
2388c2ecf20Sopenharmony_ci	      unsigned long pc, unsigned long event)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct op_entry entry;
2418c2ecf20Sopenharmony_ci	struct op_sample *sample;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	sample = op_cpu_buffer_write_reserve(&entry, 0);
2448c2ecf20Sopenharmony_ci	if (!sample)
2458c2ecf20Sopenharmony_ci		return -ENOMEM;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	sample->eip = pc;
2488c2ecf20Sopenharmony_ci	sample->event = event;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return op_cpu_buffer_write_commit(&entry);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci/*
2548c2ecf20Sopenharmony_ci * This must be safe from any context.
2558c2ecf20Sopenharmony_ci *
2568c2ecf20Sopenharmony_ci * is_kernel is needed because on some architectures you cannot
2578c2ecf20Sopenharmony_ci * tell if you are in kernel or user space simply by looking at
2588c2ecf20Sopenharmony_ci * pc. We tag this in the buffer by generating kernel enter/exit
2598c2ecf20Sopenharmony_ci * events whenever is_kernel changes
2608c2ecf20Sopenharmony_ci */
2618c2ecf20Sopenharmony_cistatic int
2628c2ecf20Sopenharmony_cilog_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc,
2638c2ecf20Sopenharmony_ci	   unsigned long backtrace, int is_kernel, unsigned long event,
2648c2ecf20Sopenharmony_ci	   struct task_struct *task)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	struct task_struct *tsk = task ? task : current;
2678c2ecf20Sopenharmony_ci	cpu_buf->sample_received++;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (pc == ESCAPE_CODE) {
2708c2ecf20Sopenharmony_ci		cpu_buf->sample_invalid_eip++;
2718c2ecf20Sopenharmony_ci		return 0;
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (op_add_code(cpu_buf, backtrace, is_kernel, tsk))
2758c2ecf20Sopenharmony_ci		goto fail;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (op_add_sample(cpu_buf, pc, event))
2788c2ecf20Sopenharmony_ci		goto fail;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return 1;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cifail:
2838c2ecf20Sopenharmony_ci	cpu_buf->sample_lost_overflow++;
2848c2ecf20Sopenharmony_ci	return 0;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic inline void oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	cpu_buf->tracing = 1;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic inline void oprofile_end_trace(struct oprofile_cpu_buffer *cpu_buf)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	cpu_buf->tracing = 0;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic inline void
2988c2ecf20Sopenharmony_ci__oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs,
2998c2ecf20Sopenharmony_ci			  unsigned long event, int is_kernel,
3008c2ecf20Sopenharmony_ci			  struct task_struct *task)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct oprofile_cpu_buffer *cpu_buf = this_cpu_ptr(&op_cpu_buffer);
3038c2ecf20Sopenharmony_ci	unsigned long backtrace = oprofile_backtrace_depth;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	/*
3068c2ecf20Sopenharmony_ci	 * if log_sample() fail we can't backtrace since we lost the
3078c2ecf20Sopenharmony_ci	 * source of this event
3088c2ecf20Sopenharmony_ci	 */
3098c2ecf20Sopenharmony_ci	if (!log_sample(cpu_buf, pc, backtrace, is_kernel, event, task))
3108c2ecf20Sopenharmony_ci		/* failed */
3118c2ecf20Sopenharmony_ci		return;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (!backtrace)
3148c2ecf20Sopenharmony_ci		return;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	oprofile_begin_trace(cpu_buf);
3178c2ecf20Sopenharmony_ci	oprofile_ops.backtrace(regs, backtrace);
3188c2ecf20Sopenharmony_ci	oprofile_end_trace(cpu_buf);
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_civoid oprofile_add_ext_hw_sample(unsigned long pc, struct pt_regs * const regs,
3228c2ecf20Sopenharmony_ci				unsigned long event, int is_kernel,
3238c2ecf20Sopenharmony_ci				struct task_struct *task)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	__oprofile_add_ext_sample(pc, regs, event, is_kernel, task);
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_civoid oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs,
3298c2ecf20Sopenharmony_ci			     unsigned long event, int is_kernel)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	__oprofile_add_ext_sample(pc, regs, event, is_kernel, NULL);
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_civoid oprofile_add_sample(struct pt_regs * const regs, unsigned long event)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	int is_kernel;
3378c2ecf20Sopenharmony_ci	unsigned long pc;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (likely(regs)) {
3408c2ecf20Sopenharmony_ci		is_kernel = !user_mode(regs);
3418c2ecf20Sopenharmony_ci		pc = profile_pc(regs);
3428c2ecf20Sopenharmony_ci	} else {
3438c2ecf20Sopenharmony_ci		is_kernel = 0;    /* This value will not be used */
3448c2ecf20Sopenharmony_ci		pc = ESCAPE_CODE; /* as this causes an early return. */
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	__oprofile_add_ext_sample(pc, regs, event, is_kernel, NULL);
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci/*
3518c2ecf20Sopenharmony_ci * Add samples with data to the ring buffer.
3528c2ecf20Sopenharmony_ci *
3538c2ecf20Sopenharmony_ci * Use oprofile_add_data(&entry, val) to add data and
3548c2ecf20Sopenharmony_ci * oprofile_write_commit(&entry) to commit the sample.
3558c2ecf20Sopenharmony_ci */
3568c2ecf20Sopenharmony_civoid
3578c2ecf20Sopenharmony_cioprofile_write_reserve(struct op_entry *entry, struct pt_regs * const regs,
3588c2ecf20Sopenharmony_ci		       unsigned long pc, int code, int size)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct op_sample *sample;
3618c2ecf20Sopenharmony_ci	int is_kernel = !user_mode(regs);
3628c2ecf20Sopenharmony_ci	struct oprofile_cpu_buffer *cpu_buf = this_cpu_ptr(&op_cpu_buffer);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	cpu_buf->sample_received++;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	/* no backtraces for samples with data */
3678c2ecf20Sopenharmony_ci	if (op_add_code(cpu_buf, 0, is_kernel, current))
3688c2ecf20Sopenharmony_ci		goto fail;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	sample = op_cpu_buffer_write_reserve(entry, size + 2);
3718c2ecf20Sopenharmony_ci	if (!sample)
3728c2ecf20Sopenharmony_ci		goto fail;
3738c2ecf20Sopenharmony_ci	sample->eip = ESCAPE_CODE;
3748c2ecf20Sopenharmony_ci	sample->event = 0;		/* no flags */
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	op_cpu_buffer_add_data(entry, code);
3778c2ecf20Sopenharmony_ci	op_cpu_buffer_add_data(entry, pc);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	return;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cifail:
3828c2ecf20Sopenharmony_ci	entry->event = NULL;
3838c2ecf20Sopenharmony_ci	cpu_buf->sample_lost_overflow++;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ciint oprofile_add_data(struct op_entry *entry, unsigned long val)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	if (!entry->event)
3898c2ecf20Sopenharmony_ci		return 0;
3908c2ecf20Sopenharmony_ci	return op_cpu_buffer_add_data(entry, val);
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ciint oprofile_add_data64(struct op_entry *entry, u64 val)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	if (!entry->event)
3968c2ecf20Sopenharmony_ci		return 0;
3978c2ecf20Sopenharmony_ci	if (op_cpu_buffer_get_size(entry) < 2)
3988c2ecf20Sopenharmony_ci		/*
3998c2ecf20Sopenharmony_ci		 * the function returns 0 to indicate a too small
4008c2ecf20Sopenharmony_ci		 * buffer, even if there is some space left
4018c2ecf20Sopenharmony_ci		 */
4028c2ecf20Sopenharmony_ci		return 0;
4038c2ecf20Sopenharmony_ci	if (!op_cpu_buffer_add_data(entry, (u32)val))
4048c2ecf20Sopenharmony_ci		return 0;
4058c2ecf20Sopenharmony_ci	return op_cpu_buffer_add_data(entry, (u32)(val >> 32));
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ciint oprofile_write_commit(struct op_entry *entry)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	if (!entry->event)
4118c2ecf20Sopenharmony_ci		return -EINVAL;
4128c2ecf20Sopenharmony_ci	return op_cpu_buffer_write_commit(entry);
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_civoid oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct oprofile_cpu_buffer *cpu_buf = this_cpu_ptr(&op_cpu_buffer);
4188c2ecf20Sopenharmony_ci	log_sample(cpu_buf, pc, 0, is_kernel, event, NULL);
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_civoid oprofile_add_trace(unsigned long pc)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct oprofile_cpu_buffer *cpu_buf = this_cpu_ptr(&op_cpu_buffer);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (!cpu_buf->tracing)
4268c2ecf20Sopenharmony_ci		return;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	/*
4298c2ecf20Sopenharmony_ci	 * broken frame can give an eip with the same value as an
4308c2ecf20Sopenharmony_ci	 * escape code, abort the trace if we get it
4318c2ecf20Sopenharmony_ci	 */
4328c2ecf20Sopenharmony_ci	if (pc == ESCAPE_CODE)
4338c2ecf20Sopenharmony_ci		goto fail;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (op_add_sample(cpu_buf, pc, 0))
4368c2ecf20Sopenharmony_ci		goto fail;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	return;
4398c2ecf20Sopenharmony_cifail:
4408c2ecf20Sopenharmony_ci	cpu_buf->tracing = 0;
4418c2ecf20Sopenharmony_ci	cpu_buf->backtrace_aborted++;
4428c2ecf20Sopenharmony_ci	return;
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci/*
4468c2ecf20Sopenharmony_ci * This serves to avoid cpu buffer overflow, and makes sure
4478c2ecf20Sopenharmony_ci * the task mortuary progresses
4488c2ecf20Sopenharmony_ci *
4498c2ecf20Sopenharmony_ci * By using schedule_delayed_work_on and then schedule_delayed_work
4508c2ecf20Sopenharmony_ci * we guarantee this will stay on the correct cpu
4518c2ecf20Sopenharmony_ci */
4528c2ecf20Sopenharmony_cistatic void wq_sync_buffer(struct work_struct *work)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	struct oprofile_cpu_buffer *b =
4558c2ecf20Sopenharmony_ci		container_of(work, struct oprofile_cpu_buffer, work.work);
4568c2ecf20Sopenharmony_ci	if (b->cpu != smp_processor_id() && !cpu_online(b->cpu)) {
4578c2ecf20Sopenharmony_ci		cancel_delayed_work(&b->work);
4588c2ecf20Sopenharmony_ci		return;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci	sync_buffer(b->cpu);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	/* don't re-add the work if we're shutting down */
4638c2ecf20Sopenharmony_ci	if (work_enabled)
4648c2ecf20Sopenharmony_ci		schedule_delayed_work(&b->work, DEFAULT_TIMER_EXPIRE);
4658c2ecf20Sopenharmony_ci}
466