Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d2852b93 authored by Robert Richter's avatar Robert Richter
Browse files

Merge branch 'oprofile/ring_buffer' into oprofile/oprofile-for-tip

parents 4a6908a3 14f0ca8e
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -6,6 +6,8 @@ config OPROFILE
	tristate "OProfile system profiling (EXPERIMENTAL)"
	depends on PROFILING
	depends on HAVE_OPROFILE
	select TRACING
	select RING_BUFFER
	help
	  OProfile is a profiling system capable of profiling the
	  whole system, include the kernel, kernel modules, libraries,
+91 −133
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@
 * @file op_model_amd.c
 * athlon / K7 / K8 / Family 10h model-specific MSR operations
 *
 * @remark Copyright 2002-2008 OProfile authors
 * @remark Copyright 2002-2009 OProfile authors
 * @remark Read the file COPYING
 *
 * @author John Levon
@@ -60,56 +60,10 @@ static unsigned long reset_value[NUM_COUNTERS];
#define IBS_OP_LOW_VALID_BIT		(1ULL<<18)	/* bit 18 */
#define IBS_OP_LOW_ENABLE		(1ULL<<17)	/* bit 17 */

/* Codes used in cpu_buffer.c */
/* This produces duplicate code, need to be fixed */
#define IBS_FETCH_BEGIN 3
#define IBS_OP_BEGIN    4

/* The function interface needs to be fixed, something like add
   data. Should then be added to linux/oprofile.h. */
extern void
oprofile_add_ibs_sample(struct pt_regs *const regs,
			unsigned int *const ibs_sample, int ibs_code);

struct ibs_fetch_sample {
	/* MSRC001_1031 IBS Fetch Linear Address Register */
	unsigned int ibs_fetch_lin_addr_low;
	unsigned int ibs_fetch_lin_addr_high;
	/* MSRC001_1030 IBS Fetch Control Register */
	unsigned int ibs_fetch_ctl_low;
	unsigned int ibs_fetch_ctl_high;
	/* MSRC001_1032 IBS Fetch Physical Address Register */
	unsigned int ibs_fetch_phys_addr_low;
	unsigned int ibs_fetch_phys_addr_high;
};
#define IBS_FETCH_SIZE	6
#define IBS_OP_SIZE	12

struct ibs_op_sample {
	/* MSRC001_1034 IBS Op Logical Address Register (IbsRIP) */
	unsigned int ibs_op_rip_low;
	unsigned int ibs_op_rip_high;
	/* MSRC001_1035 IBS Op Data Register */
	unsigned int ibs_op_data1_low;
	unsigned int ibs_op_data1_high;
	/* MSRC001_1036 IBS Op Data 2 Register */
	unsigned int ibs_op_data2_low;
	unsigned int ibs_op_data2_high;
	/* MSRC001_1037 IBS Op Data 3 Register */
	unsigned int ibs_op_data3_low;
	unsigned int ibs_op_data3_high;
	/* MSRC001_1038 IBS DC Linear Address Register (IbsDcLinAd) */
	unsigned int ibs_dc_linear_low;
	unsigned int ibs_dc_linear_high;
	/* MSRC001_1039 IBS DC Physical Address Register (IbsDcPhysAd) */
	unsigned int ibs_dc_phys_low;
	unsigned int ibs_dc_phys_high;
};

/*
 * unitialize the APIC for the IBS interrupts if needed on AMD Family10h+
*/
static void clear_ibs_nmi(void);

static int ibs_allowed;	/* AMD Family10h and later */
static int has_ibs;	/* AMD Family10h and later */

struct op_ibs_config {
	unsigned long op_enabled;
@@ -200,31 +154,29 @@ static inline int
op_amd_handle_ibs(struct pt_regs * const regs,
		  struct op_msrs const * const msrs)
{
	unsigned int low, high;
	struct ibs_fetch_sample ibs_fetch;
	struct ibs_op_sample ibs_op;
	u32 low, high;
	u64 msr;
	struct op_entry entry;

	if (!ibs_allowed)
	if (!has_ibs)
		return 1;

	if (ibs_config.fetch_enabled) {
		rdmsr(MSR_AMD64_IBSFETCHCTL, low, high);
		if (high & IBS_FETCH_HIGH_VALID_BIT) {
			ibs_fetch.ibs_fetch_ctl_high = high;
			ibs_fetch.ibs_fetch_ctl_low = low;
			rdmsr(MSR_AMD64_IBSFETCHLINAD, low, high);
			ibs_fetch.ibs_fetch_lin_addr_high = high;
			ibs_fetch.ibs_fetch_lin_addr_low = low;
			rdmsr(MSR_AMD64_IBSFETCHPHYSAD, low, high);
			ibs_fetch.ibs_fetch_phys_addr_high = high;
			ibs_fetch.ibs_fetch_phys_addr_low = low;

			oprofile_add_ibs_sample(regs,
						(unsigned int *)&ibs_fetch,
						IBS_FETCH_BEGIN);
			rdmsrl(MSR_AMD64_IBSFETCHLINAD, msr);
			oprofile_write_reserve(&entry, regs, msr,
					       IBS_FETCH_CODE, IBS_FETCH_SIZE);
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
			oprofile_add_data(&entry, low);
			oprofile_add_data(&entry, high);
			rdmsrl(MSR_AMD64_IBSFETCHPHYSAD, msr);
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
			oprofile_write_commit(&entry);

			/* reenable the IRQ */
			rdmsr(MSR_AMD64_IBSFETCHCTL, low, high);
			high &= ~IBS_FETCH_HIGH_VALID_BIT;
			high |= IBS_FETCH_HIGH_ENABLE;
			low &= IBS_FETCH_LOW_MAX_CNT_MASK;
@@ -235,30 +187,29 @@ op_amd_handle_ibs(struct pt_regs * const regs,
	if (ibs_config.op_enabled) {
		rdmsr(MSR_AMD64_IBSOPCTL, low, high);
		if (low & IBS_OP_LOW_VALID_BIT) {
			rdmsr(MSR_AMD64_IBSOPRIP, low, high);
			ibs_op.ibs_op_rip_low = low;
			ibs_op.ibs_op_rip_high = high;
			rdmsr(MSR_AMD64_IBSOPDATA, low, high);
			ibs_op.ibs_op_data1_low = low;
			ibs_op.ibs_op_data1_high = high;
			rdmsr(MSR_AMD64_IBSOPDATA2, low, high);
			ibs_op.ibs_op_data2_low = low;
			ibs_op.ibs_op_data2_high = high;
			rdmsr(MSR_AMD64_IBSOPDATA3, low, high);
			ibs_op.ibs_op_data3_low = low;
			ibs_op.ibs_op_data3_high = high;
			rdmsr(MSR_AMD64_IBSDCLINAD, low, high);
			ibs_op.ibs_dc_linear_low = low;
			ibs_op.ibs_dc_linear_high = high;
			rdmsr(MSR_AMD64_IBSDCPHYSAD, low, high);
			ibs_op.ibs_dc_phys_low = low;
			ibs_op.ibs_dc_phys_high = high;
			rdmsrl(MSR_AMD64_IBSOPRIP, msr);
			oprofile_write_reserve(&entry, regs, msr,
					       IBS_OP_CODE, IBS_OP_SIZE);
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
			rdmsrl(MSR_AMD64_IBSOPDATA, msr);
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
			rdmsrl(MSR_AMD64_IBSOPDATA2, msr);
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
			rdmsrl(MSR_AMD64_IBSOPDATA3, msr);
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
			rdmsrl(MSR_AMD64_IBSDCLINAD, msr);
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
			rdmsrl(MSR_AMD64_IBSDCPHYSAD, msr);
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
			oprofile_write_commit(&entry);

			/* reenable the IRQ */
			oprofile_add_ibs_sample(regs,
						(unsigned int *)&ibs_op,
						IBS_OP_BEGIN);
			rdmsr(MSR_AMD64_IBSOPCTL, low, high);
			high = 0;
			low &= ~IBS_OP_LOW_VALID_BIT;
			low |= IBS_OP_LOW_ENABLE;
@@ -308,14 +259,14 @@ static void op_amd_start(struct op_msrs const * const msrs)
	}

#ifdef CONFIG_OPROFILE_IBS
	if (ibs_allowed && ibs_config.fetch_enabled) {
	if (has_ibs && ibs_config.fetch_enabled) {
		low = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF;
		high = ((ibs_config.rand_en & 0x1) << 25) /* bit 57 */
			+ IBS_FETCH_HIGH_ENABLE;
		wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
	}

	if (ibs_allowed && ibs_config.op_enabled) {
	if (has_ibs && ibs_config.op_enabled) {
		low = ((ibs_config.max_cnt_op >> 4) & 0xFFFF)
			+ ((ibs_config.dispatched_ops & 0x1) << 19) /* bit 19 */
			+ IBS_OP_LOW_ENABLE;
@@ -331,8 +282,10 @@ static void op_amd_stop(struct op_msrs const * const msrs)
	unsigned int low, high;
	int i;

	/* Subtle: stop on all counters to avoid race with
	 * setting our pm callback */
	/*
	 * Subtle: stop on all counters to avoid race with setting our
	 * pm callback
	 */
	for (i = 0 ; i < NUM_COUNTERS ; ++i) {
		if (!reset_value[i])
			continue;
@@ -342,14 +295,16 @@ static void op_amd_stop(struct op_msrs const * const msrs)
	}

#ifdef CONFIG_OPROFILE_IBS
	if (ibs_allowed && ibs_config.fetch_enabled) {
		low = 0;		/* clear max count and enable */
	if (has_ibs && ibs_config.fetch_enabled) {
		/* clear max count and enable */
		low = 0;
		high = 0;
		wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
	}

	if (ibs_allowed && ibs_config.op_enabled) {
		low = 0;		/* clear max count and enable */
	if (has_ibs && ibs_config.op_enabled) {
		/* clear max count and enable */
		low = 0;
		high = 0;
		wrmsr(MSR_AMD64_IBSOPCTL, low, high);
	}
@@ -370,18 +325,7 @@ static void op_amd_shutdown(struct op_msrs const * const msrs)
	}
}

#ifndef CONFIG_OPROFILE_IBS

/* no IBS support */

static int op_amd_init(struct oprofile_operations *ops)
{
	return 0;
}

static void op_amd_exit(void) {}

#else
#ifdef CONFIG_OPROFILE_IBS

static u8 ibs_eilvt_off;

@@ -395,7 +339,7 @@ static inline void apic_clear_ibs_nmi_per_cpu(void *arg)
	setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1);
}

static int pfm_amd64_setup_eilvt(void)
static int init_ibs_nmi(void)
{
#define IBSCTL_LVTOFFSETVAL		(1 << 8)
#define IBSCTL				0x1cc
@@ -419,6 +363,7 @@ static int pfm_amd64_setup_eilvt(void)
				       | IBSCTL_LVTOFFSETVAL);
		pci_read_config_dword(cpu_cfg, IBSCTL, &value);
		if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) {
			pci_dev_put(cpu_cfg);
			printk(KERN_DEBUG "Failed to setup IBS LVT offset, "
				"IBSCTL = 0x%08x", value);
			return 1;
@@ -443,33 +388,35 @@ static int pfm_amd64_setup_eilvt(void)
	return 0;
}

/*
 * initialize the APIC for the IBS interrupts
 * if available (AMD Family10h rev B0 and later)
 */
static void setup_ibs(void)
/* uninitialize the APIC for the IBS interrupts if needed */
static void clear_ibs_nmi(void)
{
	ibs_allowed = boot_cpu_has(X86_FEATURE_IBS);
	if (has_ibs)
		on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1);
}

	if (!ibs_allowed)
/* initialize the APIC for the IBS interrupts if available */
static void ibs_init(void)
{
	has_ibs = boot_cpu_has(X86_FEATURE_IBS);

	if (!has_ibs)
		return;

	if (pfm_amd64_setup_eilvt()) {
		ibs_allowed = 0;
	if (init_ibs_nmi()) {
		has_ibs = 0;
		return;
	}

	printk(KERN_INFO "oprofile: AMD IBS detected\n");
}


/*
 * unitialize the APIC for the IBS interrupts if needed on AMD Family10h
 * rev B0 and later */
static void clear_ibs_nmi(void)
static void ibs_exit(void)
{
	if (ibs_allowed)
		on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1);
	if (!has_ibs)
		return;

	clear_ibs_nmi();
}

static int (*create_arch_files)(struct super_block *sb, struct dentry *root);
@@ -486,7 +433,7 @@ static int setup_ibs_files(struct super_block *sb, struct dentry *root)
	if (ret)
		return ret;

	if (!ibs_allowed)
	if (!has_ibs)
		return ret;

	/* model specific files */
@@ -519,7 +466,7 @@ static int setup_ibs_files(struct super_block *sb, struct dentry *root)

static int op_amd_init(struct oprofile_operations *ops)
{
	setup_ibs();
	ibs_init();
	create_arch_files = ops->create_files;
	ops->create_files = setup_ibs_files;
	return 0;
@@ -527,10 +474,21 @@ static int op_amd_init(struct oprofile_operations *ops)

static void op_amd_exit(void)
{
	clear_ibs_nmi();
	ibs_exit();
}

#endif
#else

/* no IBS support */

static int op_amd_init(struct oprofile_operations *ops)
{
	return 0;
}

static void op_amd_exit(void) {}

#endif /* CONFIG_OPROFILE_IBS */

struct op_x86_model_spec const op_amd_spec = {
	.init			= op_amd_init,
+81 −148
Original line number Diff line number Diff line
/**
 * @file buffer_sync.c
 *
 * @remark Copyright 2002 OProfile authors
 * @remark Copyright 2002-2009 OProfile authors
 * @remark Read the file COPYING
 *
 * @author John Levon <levon@movementarian.org>
 * @author Barry Kasindorf
 * @author Robert Richter <robert.richter@amd.com>
 *
 * This is the core of the buffer management. Each
 * CPU buffer is processed and entered into the
@@ -268,18 +269,6 @@ lookup_dcookie(struct mm_struct *mm, unsigned long addr, off_t *offset)
	return cookie;
}

static void increment_tail(struct oprofile_cpu_buffer *b)
{
	unsigned long new_tail = b->tail_pos + 1;

	rmb();	/* be sure fifo pointers are synchromized */

	if (new_tail < b->buffer_size)
		b->tail_pos = new_tail;
	else
		b->tail_pos = 0;
}

static unsigned long last_cookie = INVALID_COOKIE;

static void add_cpu_switch(int i)
@@ -327,84 +316,73 @@ static void add_trace_begin(void)
	add_event_entry(TRACE_BEGIN_CODE);
}

#ifdef CONFIG_OPROFILE_IBS

#define IBS_FETCH_CODE_SIZE	2
#define IBS_OP_CODE_SIZE	5
#define IBS_EIP(offset)				\
	(((struct op_sample *)&cpu_buf->buffer[(offset)])->eip)
#define IBS_EVENT(offset)				\
	(((struct op_sample *)&cpu_buf->buffer[(offset)])->event)

/*
 * Add IBS fetch and op entries to event buffer
 */
static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code,
			  struct mm_struct *mm)
static void add_data(struct op_entry *entry, struct mm_struct *mm)
{
	unsigned long rip;
	int i, count;
	unsigned long ibs_cookie = 0;
	unsigned long code, pc, val;
	unsigned long cookie;
	off_t offset;

	increment_tail(cpu_buf);	/* move to RIP entry */

	rip = IBS_EIP(cpu_buf->tail_pos);

#ifdef __LP64__
	rip += IBS_EVENT(cpu_buf->tail_pos) << 32;
#endif
	if (!op_cpu_buffer_get_data(entry, &code))
		return;
	if (!op_cpu_buffer_get_data(entry, &pc))
		return;
	if (!op_cpu_buffer_get_size(entry))
		return;

	if (mm) {
		ibs_cookie = lookup_dcookie(mm, rip, &offset);
		cookie = lookup_dcookie(mm, pc, &offset);

		if (ibs_cookie == NO_COOKIE)
			offset = rip;
		if (ibs_cookie == INVALID_COOKIE) {
		if (cookie == NO_COOKIE)
			offset = pc;
		if (cookie == INVALID_COOKIE) {
			atomic_inc(&oprofile_stats.sample_lost_no_mapping);
			offset = rip;
			offset = pc;
		}
		if (ibs_cookie != last_cookie) {
			add_cookie_switch(ibs_cookie);
			last_cookie = ibs_cookie;
		if (cookie != last_cookie) {
			add_cookie_switch(cookie);
			last_cookie = cookie;
		}
	} else
		offset = rip;
		offset = pc;

	add_event_entry(ESCAPE_CODE);
	add_event_entry(code);
	add_event_entry(offset);	/* Offset from Dcookie */

	/* we send the Dcookie offset, but send the raw Linear Add also*/
	add_event_entry(IBS_EIP(cpu_buf->tail_pos));
	add_event_entry(IBS_EVENT(cpu_buf->tail_pos));

	if (code == IBS_FETCH_CODE)
		count = IBS_FETCH_CODE_SIZE;	/*IBS FETCH is 2 int64s*/
	else
		count = IBS_OP_CODE_SIZE;	/*IBS OP is 5 int64s*/

	for (i = 0; i < count; i++) {
		increment_tail(cpu_buf);
		add_event_entry(IBS_EIP(cpu_buf->tail_pos));
		add_event_entry(IBS_EVENT(cpu_buf->tail_pos));
	}
	while (op_cpu_buffer_get_data(entry, &val))
		add_event_entry(val);
}

#endif

static void add_sample_entry(unsigned long offset, unsigned long event)
static inline void add_sample_entry(unsigned long offset, unsigned long event)
{
	add_event_entry(offset);
	add_event_entry(event);
}


static int add_us_sample(struct mm_struct *mm, struct op_sample *s)
/*
 * Add a sample to the global event buffer. If possible the
 * sample is converted into a persistent dentry/offset pair
 * for later lookup from userspace. Return 0 on failure.
 */
static int
add_sample(struct mm_struct *mm, struct op_sample *s, int in_kernel)
{
	unsigned long cookie;
	off_t offset;

	if (in_kernel) {
		add_sample_entry(s->eip, s->event);
		return 1;
	}

	/* add userspace sample */

	if (!mm) {
		atomic_inc(&oprofile_stats.sample_lost_no_mm);
		return 0;
	}

	cookie = lookup_dcookie(mm, s->eip, &offset);

	if (cookie == INVALID_COOKIE) {
@@ -423,25 +401,6 @@ static int add_us_sample(struct mm_struct *mm, struct op_sample *s)
}


/* Add a sample to the global event buffer. If possible the
 * sample is converted into a persistent dentry/offset pair
 * for later lookup from userspace.
 */
static int
add_sample(struct mm_struct *mm, struct op_sample *s, int in_kernel)
{
	if (in_kernel) {
		add_sample_entry(s->eip, s->event);
		return 1;
	} else if (mm) {
		return add_us_sample(mm, s);
	} else {
		atomic_inc(&oprofile_stats.sample_lost_no_mm);
	}
	return 0;
}


static void release_mm(struct mm_struct *mm)
{
	if (!mm)
@@ -466,33 +425,6 @@ static inline int is_code(unsigned long val)
}


/* "acquire" as many cpu buffer slots as we can */
static unsigned long get_slots(struct oprofile_cpu_buffer *b)
{
	unsigned long head = b->head_pos;
	unsigned long tail = b->tail_pos;

	/*
	 * Subtle. This resets the persistent last_task
	 * and in_kernel values used for switching notes.
	 * BUT, there is a small window between reading
	 * head_pos, and this call, that means samples
	 * can appear at the new head position, but not
	 * be prefixed with the notes for switching
	 * kernel mode or a task switch. This small hole
	 * can lead to mis-attribution or samples where
	 * we don't know if it's in the kernel or not,
	 * at the start of an event buffer.
	 */
	cpu_buffer_reset(b);

	if (head >= tail)
		return head - tail;

	return head + (b->buffer_size - tail);
}


/* Move tasks along towards death. Any tasks on dead_tasks
 * will definitely have no remaining references in any
 * CPU buffers at this point, because we use two lists,
@@ -559,72 +491,73 @@ typedef enum {
 */
void sync_buffer(int cpu)
{
	struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu);
	struct mm_struct *mm = NULL;
	struct mm_struct *oldmm;
	unsigned long val;
	struct task_struct *new;
	unsigned long cookie = 0;
	int in_kernel = 1;
	sync_buffer_state state = sb_buffer_start;
#ifndef CONFIG_OPROFILE_IBS
	unsigned int i;
	unsigned long available;
#endif
	unsigned long flags;
	struct op_entry entry;
	struct op_sample *sample;

	mutex_lock(&buffer_mutex);

	add_cpu_switch(cpu);

	/* Remember, only we can modify tail_pos */

#ifndef CONFIG_OPROFILE_IBS
	available = get_slots(cpu_buf);
	op_cpu_buffer_reset(cpu);
	available = op_cpu_buffer_entries(cpu);

	for (i = 0; i < available; ++i) {
#else
	while (get_slots(cpu_buf)) {
#endif
		struct op_sample *s = &cpu_buf->buffer[cpu_buf->tail_pos];
		sample = op_cpu_buffer_read_entry(&entry, cpu);
		if (!sample)
			break;

		if (is_code(s->eip)) {
			if (s->event <= CPU_IS_KERNEL) {
		if (is_code(sample->eip)) {
			flags = sample->event;
			if (flags & TRACE_BEGIN) {
				state = sb_bt_start;
				add_trace_begin();
			}
			if (flags & KERNEL_CTX_SWITCH) {
				/* kernel/userspace switch */
				in_kernel = s->event;
				in_kernel = flags & IS_KERNEL;
				if (state == sb_buffer_start)
					state = sb_sample_start;
				add_kernel_ctx_switch(s->event);
			} else if (s->event == CPU_TRACE_BEGIN) {
				state = sb_bt_start;
				add_trace_begin();
#ifdef CONFIG_OPROFILE_IBS
			} else if (s->event == IBS_FETCH_BEGIN) {
				state = sb_bt_start;
				add_ibs_begin(cpu_buf, IBS_FETCH_CODE, mm);
			} else if (s->event == IBS_OP_BEGIN) {
				state = sb_bt_start;
				add_ibs_begin(cpu_buf, IBS_OP_CODE, mm);
#endif
			} else {
				struct mm_struct *oldmm = mm;

				add_kernel_ctx_switch(flags & IS_KERNEL);
			}
			if (flags & USER_CTX_SWITCH
			    && op_cpu_buffer_get_data(&entry, &val)) {
				/* userspace context switch */
				new = (struct task_struct *)s->event;

				new = (struct task_struct *)val;
				oldmm = mm;
				release_mm(oldmm);
				mm = take_tasks_mm(new);
				if (mm != oldmm)
					cookie = get_exec_dcookie(mm);
				add_user_ctx_switch(new, cookie);
			}
		} else if (state >= sb_bt_start &&
			   !add_sample(mm, s, in_kernel)) {
			if (op_cpu_buffer_get_size(&entry))
				add_data(&entry, mm);
			continue;
		}

		if (state < sb_bt_start)
			/* ignore sample */
			continue;

		if (add_sample(mm, sample, in_kernel))
			continue;

		/* ignore backtraces if failed to add a sample */
		if (state == sb_bt_start) {
			state = sb_bt_ignore;
			atomic_inc(&oprofile_stats.bt_lost_no_mapping);
		}
	}

		increment_tail(cpu_buf);
	}
	release_mm(mm);

	mark_done(cpu);
+240 −153

File changed.

Preview size limit exceeded, changes collapsed.

+62 −10
Original line number Diff line number Diff line
/**
 * @file cpu_buffer.h
 *
 * @remark Copyright 2002 OProfile authors
 * @remark Copyright 2002-2009 OProfile authors
 * @remark Read the file COPYING
 *
 * @author John Levon <levon@movementarian.org>
 * @author Robert Richter <robert.richter@amd.com>
 */

#ifndef OPROFILE_CPU_BUFFER_H
@@ -15,6 +16,7 @@
#include <linux/workqueue.h>
#include <linux/cache.h>
#include <linux/sched.h>
#include <linux/ring_buffer.h>

struct task_struct;

@@ -30,16 +32,16 @@ void end_cpu_work(void);
struct op_sample {
	unsigned long eip;
	unsigned long event;
	unsigned long data[0];
};

struct op_entry;

struct oprofile_cpu_buffer {
	volatile unsigned long head_pos;
	volatile unsigned long tail_pos;
	unsigned long buffer_size;
	struct task_struct *last_task;
	int last_is_kernel;
	int tracing;
	struct op_sample *buffer;
	unsigned long sample_received;
	unsigned long sample_lost_overflow;
	unsigned long backtrace_aborted;
@@ -50,12 +52,62 @@ struct oprofile_cpu_buffer {

DECLARE_PER_CPU(struct oprofile_cpu_buffer, cpu_buffer);

void cpu_buffer_reset(struct oprofile_cpu_buffer *cpu_buf);
/*
 * Resets the cpu buffer to a sane state.
 *
 * reset these to invalid values; the next sample collected will
 * populate the buffer with proper values to initialize the buffer
 */
static inline void op_cpu_buffer_reset(int cpu)
{
	struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu);

	cpu_buf->last_is_kernel = -1;
	cpu_buf->last_task = NULL;
}

struct op_sample
*op_cpu_buffer_write_reserve(struct op_entry *entry, unsigned long size);
int op_cpu_buffer_write_commit(struct op_entry *entry);
struct op_sample *op_cpu_buffer_read_entry(struct op_entry *entry, int cpu);
unsigned long op_cpu_buffer_entries(int cpu);

/* returns the remaining free size of data in the entry */
static inline
int op_cpu_buffer_add_data(struct op_entry *entry, unsigned long val)
{
	if (!entry->size)
		return 0;
	*entry->data = val;
	entry->size--;
	entry->data++;
	return entry->size;
}

/* returns the size of data in the entry */
static inline
int op_cpu_buffer_get_size(struct op_entry *entry)
{
	return entry->size;
}

/* returns 0 if empty or the size of data including the current value */
static inline
int op_cpu_buffer_get_data(struct op_entry *entry, unsigned long *val)
{
	int size = entry->size;
	if (!size)
		return 0;
	*val = *entry->data;
	entry->size--;
	entry->data++;
	return size;
}

/* transient events for the CPU buffer -> event buffer */
#define CPU_IS_KERNEL 1
#define CPU_TRACE_BEGIN 2
#define IBS_FETCH_BEGIN 3
#define IBS_OP_BEGIN    4
/* extra data flags */
#define KERNEL_CTX_SWITCH	(1UL << 0)
#define IS_KERNEL		(1UL << 1)
#define TRACE_BEGIN		(1UL << 2)
#define USER_CTX_SWITCH		(1UL << 3)

#endif /* OPROFILE_CPU_BUFFER_H */
Loading