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

Commit 8df341cf authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge branch 'core' of git://git.kernel.org/pub/scm/linux/kernel/git/rric/oprofile into perf/urgent

parents eb4316dd ec6b426c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -6,4 +6,4 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
		oprofilefs.o oprofile_stats.o  \
		timer_int.o )

oprofile-y				:= $(DRIVER_OBJS) init.o backtrace.o
oprofile-y :=	$(DRIVER_OBJS) init.o backtrace.o hwsampler.o
+1256 −0

File added.

Preview size limit exceeded, changes collapsed.

+113 −0
Original line number Diff line number Diff line
/*
 * CPUMF HW sampler functions and internal structures
 *
 *    Copyright IBM Corp. 2010
 *    Author(s): Heinz Graalfs <graalfs@de.ibm.com>
 */

#ifndef HWSAMPLER_H_
#define HWSAMPLER_H_

#include <linux/workqueue.h>

struct hws_qsi_info_block          /* QUERY SAMPLING information block  */
{ /* Bit(s) */
	unsigned int b0_13:14;      /* 0-13: zeros                       */
	unsigned int as:1;          /* 14: sampling authorisation control*/
	unsigned int b15_21:7;      /* 15-21: zeros                      */
	unsigned int es:1;          /* 22: sampling enable control       */
	unsigned int b23_29:7;      /* 23-29: zeros                      */
	unsigned int cs:1;          /* 30: sampling activation control   */
	unsigned int:1;             /* 31: reserved                      */
	unsigned int bsdes:16;      /* 4-5: size of sampling entry       */
	unsigned int:16;            /* 6-7: reserved                     */
	unsigned long min_sampl_rate; /* 8-15: minimum sampling interval */
	unsigned long max_sampl_rate; /* 16-23: maximum sampling interval*/
	unsigned long tear;         /* 24-31: TEAR contents              */
	unsigned long dear;         /* 32-39: DEAR contents              */
	unsigned int rsvrd0;        /* 40-43: reserved                   */
	unsigned int cpu_speed;     /* 44-47: CPU speed                  */
	unsigned long long rsvrd1;  /* 48-55: reserved                   */
	unsigned long long rsvrd2;  /* 56-63: reserved                   */
};

struct hws_ssctl_request_block     /* SET SAMPLING CONTROLS req block   */
{ /* bytes 0 - 7  Bit(s) */
	unsigned int s:1;           /* 0: maximum buffer indicator       */
	unsigned int h:1;           /* 1: part. level reserved for VM use*/
	unsigned long b2_53:52;     /* 2-53: zeros                       */
	unsigned int es:1;          /* 54: sampling enable control       */
	unsigned int b55_61:7;      /* 55-61: - zeros                    */
	unsigned int cs:1;          /* 62: sampling activation control   */
	unsigned int b63:1;         /* 63: zero                          */
	unsigned long interval;     /* 8-15: sampling interval           */
	unsigned long tear;         /* 16-23: TEAR contents              */
	unsigned long dear;         /* 24-31: DEAR contents              */
	/* 32-63:                                                        */
	unsigned long rsvrd1;       /* reserved                          */
	unsigned long rsvrd2;       /* reserved                          */
	unsigned long rsvrd3;       /* reserved                          */
	unsigned long rsvrd4;       /* reserved                          */
};

struct hws_cpu_buffer {
	unsigned long first_sdbt;       /* @ of 1st SDB-Table for this CP*/
	unsigned long worker_entry;
	unsigned long sample_overflow;  /* taken from SDB ...            */
	struct hws_qsi_info_block qsi;
	struct hws_ssctl_request_block ssctl;
	struct work_struct worker;
	atomic_t ext_params;
	unsigned long req_alert;
	unsigned long loss_of_sample_data;
	unsigned long invalid_entry_address;
	unsigned long incorrect_sdbt_entry;
	unsigned long sample_auth_change_alert;
	unsigned int finish:1;
	unsigned int oom:1;
	unsigned int stop_mode:1;
};

struct hws_data_entry {
	unsigned int def:16;        /* 0-15  Data Entry Format           */
	unsigned int R:4;           /* 16-19 reserved                    */
	unsigned int U:4;           /* 20-23 Number of unique instruct.  */
	unsigned int z:2;           /* zeros                             */
	unsigned int T:1;           /* 26 PSW DAT mode                   */
	unsigned int W:1;           /* 27 PSW wait state                 */
	unsigned int P:1;           /* 28 PSW Problem state              */
	unsigned int AS:2;          /* 29-30 PSW address-space control   */
	unsigned int I:1;           /* 31 entry valid or invalid         */
	unsigned int:16;
	unsigned int prim_asn:16;   /* primary ASN                       */
	unsigned long long ia;      /* Instruction Address               */
	unsigned long long lpp;     /* Logical-Partition Program Param.  */
	unsigned long long vpp;     /* Virtual-Machine Program Param.    */
};

struct hws_trailer_entry {
	unsigned int f:1;           /* 0 - Block Full Indicator          */
	unsigned int a:1;           /* 1 - Alert request control         */
	unsigned long:62;           /* 2 - 63: Reserved                  */
	unsigned long overflow;     /* 64 - sample Overflow count        */
	unsigned long timestamp;    /* 16 - time-stamp                   */
	unsigned long timestamp1;   /*                                   */
	unsigned long reserved1;    /* 32 -Reserved                      */
	unsigned long reserved2;    /*                                   */
	unsigned long progusage1;   /* 48 - reserved for programming use */
	unsigned long progusage2;   /*                                   */
};

int hwsampler_setup(void);
int hwsampler_shutdown(void);
int hwsampler_allocate(unsigned long sdbt, unsigned long sdb);
int hwsampler_deallocate(void);
long hwsampler_query_min_interval(void);
long hwsampler_query_max_interval(void);
int hwsampler_start_all(unsigned long interval);
int hwsampler_stop_all(void);
int hwsampler_deactivate(unsigned int cpu);
int hwsampler_activate(unsigned int cpu);
unsigned long hwsampler_get_sample_overflow_count(unsigned int cpu);

#endif /*HWSAMPLER_H_*/
+162 −3
Original line number Diff line number Diff line
@@ -4,23 +4,182 @@
 * S390 Version
 *   Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
 *   Author(s): Thomas Spatzier (tspat@de.ibm.com)
 *   Author(s): Mahesh Salgaonkar (mahesh@linux.vnet.ibm.com)
 *   Author(s): Heinz Graalfs (graalfs@linux.vnet.ibm.com)
 *
 * @remark Copyright 2002 OProfile authors
 * @remark Copyright 2002-2011 OProfile authors
 */

#include <linux/oprofile.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/oprofile.h>
#include <linux/errno.h>
#include <linux/fs.h>

#include "../../../drivers/oprofile/oprof.h"
#include "hwsampler.h"

#define DEFAULT_INTERVAL	4096

#define DEFAULT_SDBT_BLOCKS	1
#define DEFAULT_SDB_BLOCKS	511

static unsigned long oprofile_hw_interval = DEFAULT_INTERVAL;
static unsigned long oprofile_min_interval;
static unsigned long oprofile_max_interval;

static unsigned long oprofile_sdbt_blocks = DEFAULT_SDBT_BLOCKS;
static unsigned long oprofile_sdb_blocks = DEFAULT_SDB_BLOCKS;

static int hwsampler_file;
static int hwsampler_running;	/* start_mutex must be held to change */

static struct oprofile_operations timer_ops;

extern void s390_backtrace(struct pt_regs * const regs, unsigned int depth);

static int oprofile_hwsampler_start(void)
{
	int retval;

	hwsampler_running = hwsampler_file;

	if (!hwsampler_running)
		return timer_ops.start();

	retval = hwsampler_allocate(oprofile_sdbt_blocks, oprofile_sdb_blocks);
	if (retval)
		return retval;

	retval = hwsampler_start_all(oprofile_hw_interval);
	if (retval)
		hwsampler_deallocate();

	return retval;
}

static void oprofile_hwsampler_stop(void)
{
	if (!hwsampler_running) {
		timer_ops.stop();
		return;
	}

	hwsampler_stop_all();
	hwsampler_deallocate();
	return;
}

static ssize_t hwsampler_read(struct file *file, char __user *buf,
		size_t count, loff_t *offset)
{
	return oprofilefs_ulong_to_user(hwsampler_file, buf, count, offset);
}

static ssize_t hwsampler_write(struct file *file, char const __user *buf,
		size_t count, loff_t *offset)
{
	unsigned long val;
	int retval;

	if (*offset)
		return -EINVAL;

	retval = oprofilefs_ulong_from_user(&val, buf, count);
	if (retval)
		return retval;

	if (oprofile_started)
		/*
		 * save to do without locking as we set
		 * hwsampler_running in start() when start_mutex is
		 * held
		 */
		return -EBUSY;

	hwsampler_file = val;

	return count;
}

static const struct file_operations hwsampler_fops = {
	.read		= hwsampler_read,
	.write		= hwsampler_write,
};

static int oprofile_create_hwsampling_files(struct super_block *sb,
						struct dentry *root)
{
	struct dentry *hw_dir;

	/* reinitialize default values */
	hwsampler_file = 1;

	hw_dir = oprofilefs_mkdir(sb, root, "hwsampling");
	if (!hw_dir)
		return -EINVAL;

	oprofilefs_create_file(sb, hw_dir, "hwsampler", &hwsampler_fops);
	oprofilefs_create_ulong(sb, hw_dir, "hw_interval",
				&oprofile_hw_interval);
	oprofilefs_create_ro_ulong(sb, hw_dir, "hw_min_interval",
				&oprofile_min_interval);
	oprofilefs_create_ro_ulong(sb, hw_dir, "hw_max_interval",
				&oprofile_max_interval);
	oprofilefs_create_ulong(sb, hw_dir, "hw_sdbt_blocks",
				&oprofile_sdbt_blocks);

	return 0;
}

static int oprofile_hwsampler_init(struct oprofile_operations *ops)
{
	if (hwsampler_setup())
		return -ENODEV;

	/*
	 * create hwsampler files only if hwsampler_setup() succeeds.
	 */
	oprofile_min_interval = hwsampler_query_min_interval();
	if (oprofile_min_interval < 0) {
		oprofile_min_interval = 0;
		return -ENODEV;
	}
	oprofile_max_interval = hwsampler_query_max_interval();
	if (oprofile_max_interval < 0) {
		oprofile_max_interval = 0;
		return -ENODEV;
	}

	if (oprofile_timer_init(ops))
		return -ENODEV;

	printk(KERN_INFO "oprofile: using hardware sampling\n");

	memcpy(&timer_ops, ops, sizeof(timer_ops));

	ops->start = oprofile_hwsampler_start;
	ops->stop = oprofile_hwsampler_stop;
	ops->create_files = oprofile_create_hwsampling_files;

	return 0;
}

static void oprofile_hwsampler_exit(void)
{
	oprofile_timer_exit();
	hwsampler_shutdown();
}

int __init oprofile_arch_init(struct oprofile_operations *ops)
{
	ops->backtrace = s390_backtrace;
	return -ENODEV;

	return oprofile_hwsampler_init(ops);
}

void oprofile_arch_exit(void)
{
	oprofile_hwsampler_exit();
}
+17 −7
Original line number Diff line number Diff line
@@ -258,8 +258,10 @@ op_add_sample(struct oprofile_cpu_buffer *cpu_buf,
 */
static int
log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc,
	   unsigned long backtrace, int is_kernel, unsigned long event)
	   unsigned long backtrace, int is_kernel, unsigned long event,
	   struct task_struct *task)
{
	struct task_struct *tsk = task ? task : current;
	cpu_buf->sample_received++;

	if (pc == ESCAPE_CODE) {
@@ -267,7 +269,7 @@ log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc,
		return 0;
	}

	if (op_add_code(cpu_buf, backtrace, is_kernel, current))
	if (op_add_code(cpu_buf, backtrace, is_kernel, tsk))
		goto fail;

	if (op_add_sample(cpu_buf, pc, event))
@@ -292,7 +294,8 @@ static inline void oprofile_end_trace(struct oprofile_cpu_buffer *cpu_buf)

static inline void
__oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs,
			  unsigned long event, int is_kernel)
			  unsigned long event, int is_kernel,
			  struct task_struct *task)
{
	struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(op_cpu_buffer);
	unsigned long backtrace = oprofile_backtrace_depth;
@@ -301,7 +304,7 @@ __oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs,
	 * if log_sample() fail we can't backtrace since we lost the
	 * source of this event
	 */
	if (!log_sample(cpu_buf, pc, backtrace, is_kernel, event))
	if (!log_sample(cpu_buf, pc, backtrace, is_kernel, event, task))
		/* failed */
		return;

@@ -313,10 +316,17 @@ __oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs,
	oprofile_end_trace(cpu_buf);
}

void oprofile_add_ext_hw_sample(unsigned long pc, struct pt_regs * const regs,
				unsigned long event, int is_kernel,
				struct task_struct *task)
{
	__oprofile_add_ext_sample(pc, regs, event, is_kernel, task);
}

void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs,
			     unsigned long event, int is_kernel)
{
	__oprofile_add_ext_sample(pc, regs, event, is_kernel);
	__oprofile_add_ext_sample(pc, regs, event, is_kernel, NULL);
}

void oprofile_add_sample(struct pt_regs * const regs, unsigned long event)
@@ -332,7 +342,7 @@ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event)
		pc = ESCAPE_CODE; /* as this causes an early return. */
	}

	__oprofile_add_ext_sample(pc, regs, event, is_kernel);
	__oprofile_add_ext_sample(pc, regs, event, is_kernel, NULL);
}

/*
@@ -403,7 +413,7 @@ int oprofile_write_commit(struct op_entry *entry)
void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event)
{
	struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(op_cpu_buffer);
	log_sample(cpu_buf, pc, 0, is_kernel, event);
	log_sample(cpu_buf, pc, 0, is_kernel, event, NULL);
}

void oprofile_add_trace(unsigned long pc)
Loading