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

Commit 12c1c2fd authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge tag 'perf-core-for-mingo-4.12-20170503' of...

Merge tag 'perf-core-for-mingo-4.12-20170503' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux

 into perf/urgent

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

Fixes:

- Support setting probes in versioned user space symbols, such as
  pthread_create@@GLIBC_2.1, picking the default one, more work
  needed to make it possible to set it on the other versions, as
  the 'perf probe' syntax already uses @ for other purposes.
  (Paul Clarke)

- Do not special case address zero as an error for routines that
  return addresses (symbol lookup), instead use the return as the
  success/error indication and pass a pointer to return the address,
  fixing 'perf test vmlinux' (the one that compares address between
  vmlinux and kallsyms) on s/390, where the '_text' address is equal
  to zero (Arnaldo Carvalho de Melo)

Infrastructure changes:

- More header sanitization, moving stuff out of util.h into
  more appropriate headers and objects and sometimes creating
  new ones (Arnaldo Carvalho de Melo)

- Refactor a duplicated code for obtaining config file name (Taeung Song)

Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 33b88e70 4341ec6b
Loading
Loading
Loading
Loading
+3 −2
Original line number Original line Diff line number Diff line
@@ -8,8 +8,9 @@ Overview
--------
--------
These events are similar to tracepoint based events. Instead of Tracepoint,
These events are similar to tracepoint based events. Instead of Tracepoint,
this is based on kprobes (kprobe and kretprobe). So it can probe wherever
this is based on kprobes (kprobe and kretprobe). So it can probe wherever
kprobes can probe (this means, all functions body except for __kprobes
kprobes can probe (this means, all functions except those with
functions). Unlike the Tracepoint based event, this can be added and removed
__kprobes/nokprobe_inline annotation and those marked NOKPROBE_SYMBOL).
Unlike the Tracepoint based event, this can be added and removed
dynamically, on the fly.
dynamically, on the fly.


To enable this feature, build your kernel with CONFIG_KPROBE_EVENTS=y.
To enable this feature, build your kernel with CONFIG_KPROBE_EVENTS=y.
+2 −2
Original line number Original line Diff line number Diff line
@@ -302,8 +302,8 @@ extern int ignore_sigio_fd(int fd);
extern void maybe_sigio_broken(int fd, int read);
extern void maybe_sigio_broken(int fd, int read);
extern void sigio_broken(int fd, int read);
extern void sigio_broken(int fd, int read);


/* sys-x86_64/prctl.c */
/* prctl.c */
extern int os_arch_prctl(int pid, int code, unsigned long *addr);
extern int os_arch_prctl(int pid, int option, unsigned long *arg2);


/* tty.c */
/* tty.c */
extern int get_pty(void);
extern int get_pty(void);
+1 −0
Original line number Original line Diff line number Diff line
@@ -390,3 +390,4 @@
381	i386	pkey_alloc		sys_pkey_alloc
381	i386	pkey_alloc		sys_pkey_alloc
382	i386	pkey_free		sys_pkey_free
382	i386	pkey_free		sys_pkey_free
383	i386	statx			sys_statx
383	i386	statx			sys_statx
384	i386	arch_prctl		sys_arch_prctl			compat_sys_arch_prctl
+155 −170
Original line number Original line Diff line number Diff line
@@ -11,6 +11,8 @@
 * published by the Free Software Foundation.
 * published by the Free Software Foundation.
 */
 */


#define pr_fmt(fmt)	"perf/amd_iommu: " fmt

#include <linux/perf_event.h>
#include <linux/perf_event.h>
#include <linux/init.h>
#include <linux/init.h>
#include <linux/cpumask.h>
#include <linux/cpumask.h>
@@ -21,44 +23,42 @@


#define COUNTER_SHIFT		16
#define COUNTER_SHIFT		16


#define _GET_BANK(ev)       ((u8)(ev->hw.extra_reg.reg >> 8))
/* iommu pmu conf masks */
#define _GET_CNTR(ev)       ((u8)(ev->hw.extra_reg.reg))
#define GET_CSOURCE(x)     ((x)->conf & 0xFFULL)
#define GET_DEVID(x)       (((x)->conf >> 8)  & 0xFFFFULL)
#define GET_DOMID(x)       (((x)->conf >> 24) & 0xFFFFULL)
#define GET_PASID(x)       (((x)->conf >> 40) & 0xFFFFFULL)


/* iommu pmu config masks */
/* iommu pmu conf1 masks */
#define _GET_CSOURCE(ev)    ((ev->hw.config & 0xFFULL))
#define GET_DEVID_MASK(x)  ((x)->conf1  & 0xFFFFULL)
#define _GET_DEVID(ev)      ((ev->hw.config >> 8)  & 0xFFFFULL)
#define GET_DOMID_MASK(x)  (((x)->conf1 >> 16) & 0xFFFFULL)
#define _GET_PASID(ev)      ((ev->hw.config >> 24) & 0xFFFFULL)
#define GET_PASID_MASK(x)  (((x)->conf1 >> 32) & 0xFFFFFULL)
#define _GET_DOMID(ev)      ((ev->hw.config >> 40) & 0xFFFFULL)
#define _GET_DEVID_MASK(ev) ((ev->hw.extra_reg.config)  & 0xFFFFULL)
#define _GET_PASID_MASK(ev) ((ev->hw.extra_reg.config >> 16) & 0xFFFFULL)
#define _GET_DOMID_MASK(ev) ((ev->hw.extra_reg.config >> 32) & 0xFFFFULL)


static struct perf_amd_iommu __perf_iommu;
#define IOMMU_NAME_SIZE 16


struct perf_amd_iommu {
struct perf_amd_iommu {
	struct list_head list;
	struct pmu pmu;
	struct pmu pmu;
	struct amd_iommu *iommu;
	char name[IOMMU_NAME_SIZE];
	u8 max_banks;
	u8 max_banks;
	u8 max_counters;
	u8 max_counters;
	u64 cntr_assign_mask;
	u64 cntr_assign_mask;
	raw_spinlock_t lock;
	raw_spinlock_t lock;
	const struct attribute_group *attr_groups[4];
};
};


#define format_group	attr_groups[0]
static LIST_HEAD(perf_amd_iommu_list);
#define cpumask_group	attr_groups[1]
#define events_group	attr_groups[2]
#define null_group	attr_groups[3]


/*---------------------------------------------
/*---------------------------------------------
 * sysfs format attributes
 * sysfs format attributes
 *---------------------------------------------*/
 *---------------------------------------------*/
PMU_FORMAT_ATTR(csource,    "config:0-7");
PMU_FORMAT_ATTR(csource,    "config:0-7");
PMU_FORMAT_ATTR(devid,      "config:8-23");
PMU_FORMAT_ATTR(devid,      "config:8-23");
PMU_FORMAT_ATTR(pasid,      "config:24-39");
PMU_FORMAT_ATTR(domid,      "config:24-39");
PMU_FORMAT_ATTR(domid,      "config:40-55");
PMU_FORMAT_ATTR(pasid,      "config:40-59");
PMU_FORMAT_ATTR(devid_mask, "config1:0-15");
PMU_FORMAT_ATTR(devid_mask, "config1:0-15");
PMU_FORMAT_ATTR(pasid_mask, "config1:16-31");
PMU_FORMAT_ATTR(domid_mask, "config1:16-31");
PMU_FORMAT_ATTR(domid_mask, "config1:32-47");
PMU_FORMAT_ATTR(pasid_mask, "config1:32-51");


static struct attribute *iommu_format_attrs[] = {
static struct attribute *iommu_format_attrs[] = {
	&format_attr_csource.attr,
	&format_attr_csource.attr,
@@ -79,6 +79,10 @@ static struct attribute_group amd_iommu_format_group = {
/*---------------------------------------------
/*---------------------------------------------
 * sysfs events attributes
 * sysfs events attributes
 *---------------------------------------------*/
 *---------------------------------------------*/
static struct attribute_group amd_iommu_events_group = {
	.name = "events",
};

struct amd_iommu_event_desc {
struct amd_iommu_event_desc {
	struct kobj_attribute attr;
	struct kobj_attribute attr;
	const char *event;
	const char *event;
@@ -150,30 +154,34 @@ static struct attribute_group amd_iommu_cpumask_group = {


/*---------------------------------------------*/
/*---------------------------------------------*/


static int get_next_avail_iommu_bnk_cntr(struct perf_amd_iommu *perf_iommu)
static int get_next_avail_iommu_bnk_cntr(struct perf_event *event)
{
{
	struct perf_amd_iommu *piommu = container_of(event->pmu, struct perf_amd_iommu, pmu);
	int max_cntrs = piommu->max_counters;
	int max_banks = piommu->max_banks;
	u32 shift, bank, cntr;
	unsigned long flags;
	unsigned long flags;
	int shift, bank, cntr, retval;
	int retval;
	int max_banks = perf_iommu->max_banks;
	int max_cntrs = perf_iommu->max_counters;


	raw_spin_lock_irqsave(&perf_iommu->lock, flags);
	raw_spin_lock_irqsave(&piommu->lock, flags);


	for (bank = 0, shift = 0; bank < max_banks; bank++) {
	for (bank = 0, shift = 0; bank < max_banks; bank++) {
		for (cntr = 0; cntr < max_cntrs; cntr++) {
		for (cntr = 0; cntr < max_cntrs; cntr++) {
			shift = bank + (bank*3) + cntr;
			shift = bank + (bank*3) + cntr;
			if (perf_iommu->cntr_assign_mask & (1ULL<<shift)) {
			if (piommu->cntr_assign_mask & BIT_ULL(shift)) {
				continue;
				continue;
			} else {
			} else {
				perf_iommu->cntr_assign_mask |= (1ULL<<shift);
				piommu->cntr_assign_mask |= BIT_ULL(shift);
				retval = ((u16)((u16)bank<<8) | (u8)(cntr));
				event->hw.iommu_bank = bank;
				event->hw.iommu_cntr = cntr;
				retval = 0;
				goto out;
				goto out;
			}
			}
		}
		}
	}
	}
	retval = -ENOSPC;
	retval = -ENOSPC;
out:
out:
	raw_spin_unlock_irqrestore(&perf_iommu->lock, flags);
	raw_spin_unlock_irqrestore(&piommu->lock, flags);
	return retval;
	return retval;
}
}


@@ -202,8 +210,6 @@ static int clear_avail_iommu_bnk_cntr(struct perf_amd_iommu *perf_iommu,
static int perf_iommu_event_init(struct perf_event *event)
static int perf_iommu_event_init(struct perf_event *event)
{
{
	struct hw_perf_event *hwc = &event->hw;
	struct hw_perf_event *hwc = &event->hw;
	struct perf_amd_iommu *perf_iommu;
	u64 config, config1;


	/* test the event attr type check for PMU enumeration */
	/* test the event attr type check for PMU enumeration */
	if (event->attr.type != event->pmu->type)
	if (event->attr.type != event->pmu->type)
@@ -225,80 +231,62 @@ static int perf_iommu_event_init(struct perf_event *event)
	if (event->cpu < 0)
	if (event->cpu < 0)
		return -EINVAL;
		return -EINVAL;


	perf_iommu = &__perf_iommu;

	if (event->pmu != &perf_iommu->pmu)
		return -ENOENT;

	if (perf_iommu) {
		config = event->attr.config;
		config1 = event->attr.config1;
	} else {
		return -EINVAL;
	}

	/* integrate with iommu base devid (0000), assume one iommu */
	perf_iommu->max_banks =
		amd_iommu_pc_get_max_banks(IOMMU_BASE_DEVID);
	perf_iommu->max_counters =
		amd_iommu_pc_get_max_counters(IOMMU_BASE_DEVID);
	if ((perf_iommu->max_banks == 0) || (perf_iommu->max_counters == 0))
		return -EINVAL;

	/* update the hw_perf_event struct with the iommu config data */
	/* update the hw_perf_event struct with the iommu config data */
	hwc->config = config;
	hwc->conf  = event->attr.config;
	hwc->extra_reg.config = config1;
	hwc->conf1 = event->attr.config1;


	return 0;
	return 0;
}
}


static inline struct amd_iommu *perf_event_2_iommu(struct perf_event *ev)
{
	return (container_of(ev->pmu, struct perf_amd_iommu, pmu))->iommu;
}

static void perf_iommu_enable_event(struct perf_event *ev)
static void perf_iommu_enable_event(struct perf_event *ev)
{
{
	u8 csource = _GET_CSOURCE(ev);
	struct amd_iommu *iommu = perf_event_2_iommu(ev);
	u16 devid = _GET_DEVID(ev);
	struct hw_perf_event *hwc = &ev->hw;
	u8 bank = hwc->iommu_bank;
	u8 cntr = hwc->iommu_cntr;
	u64 reg = 0ULL;
	u64 reg = 0ULL;


	reg = csource;
	reg = GET_CSOURCE(hwc);
	amd_iommu_pc_get_set_reg_val(devid,
	amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_COUNTER_SRC_REG, &reg);
			_GET_BANK(ev), _GET_CNTR(ev) ,
			 IOMMU_PC_COUNTER_SRC_REG, &reg, true);


	reg = 0ULL | devid | (_GET_DEVID_MASK(ev) << 32);
	reg = GET_DEVID_MASK(hwc);
	reg = GET_DEVID(hwc) | (reg << 32);
	if (reg)
	if (reg)
		reg |= (1UL << 31);
		reg |= BIT(31);
	amd_iommu_pc_get_set_reg_val(devid,
	amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_DEVID_MATCH_REG, &reg);
			_GET_BANK(ev), _GET_CNTR(ev) ,
			 IOMMU_PC_DEVID_MATCH_REG, &reg, true);


	reg = 0ULL | _GET_PASID(ev) | (_GET_PASID_MASK(ev) << 32);
	reg = GET_PASID_MASK(hwc);
	reg = GET_PASID(hwc) | (reg << 32);
	if (reg)
	if (reg)
		reg |= (1UL << 31);
		reg |= BIT(31);
	amd_iommu_pc_get_set_reg_val(devid,
	amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_PASID_MATCH_REG, &reg);
			_GET_BANK(ev), _GET_CNTR(ev) ,
			 IOMMU_PC_PASID_MATCH_REG, &reg, true);


	reg = 0ULL | _GET_DOMID(ev) | (_GET_DOMID_MASK(ev) << 32);
	reg = GET_DOMID_MASK(hwc);
	reg = GET_DOMID(hwc) | (reg << 32);
	if (reg)
	if (reg)
		reg |= (1UL << 31);
		reg |= BIT(31);
	amd_iommu_pc_get_set_reg_val(devid,
	amd_iommu_pc_set_reg(iommu, bank, cntr, IOMMU_PC_DOMID_MATCH_REG, &reg);
			_GET_BANK(ev), _GET_CNTR(ev) ,
			 IOMMU_PC_DOMID_MATCH_REG, &reg, true);
}
}


static void perf_iommu_disable_event(struct perf_event *event)
static void perf_iommu_disable_event(struct perf_event *event)
{
{
	struct amd_iommu *iommu = perf_event_2_iommu(event);
	struct hw_perf_event *hwc = &event->hw;
	u64 reg = 0ULL;
	u64 reg = 0ULL;


	amd_iommu_pc_get_set_reg_val(_GET_DEVID(event),
	amd_iommu_pc_set_reg(iommu, hwc->iommu_bank, hwc->iommu_cntr,
			_GET_BANK(event), _GET_CNTR(event),
			     IOMMU_PC_COUNTER_SRC_REG, &reg);
			IOMMU_PC_COUNTER_SRC_REG, &reg, true);
}
}


static void perf_iommu_start(struct perf_event *event, int flags)
static void perf_iommu_start(struct perf_event *event, int flags)
{
{
	struct hw_perf_event *hwc = &event->hw;
	struct hw_perf_event *hwc = &event->hw;


	pr_debug("perf: amd_iommu:perf_iommu_start\n");
	if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
	if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
		return;
		return;


@@ -307,9 +295,10 @@ static void perf_iommu_start(struct perf_event *event, int flags)


	if (flags & PERF_EF_RELOAD) {
	if (flags & PERF_EF_RELOAD) {
		u64 prev_raw_count = local64_read(&hwc->prev_count);
		u64 prev_raw_count = local64_read(&hwc->prev_count);
		amd_iommu_pc_get_set_reg_val(_GET_DEVID(event),
		struct amd_iommu *iommu = perf_event_2_iommu(event);
				_GET_BANK(event), _GET_CNTR(event),

				IOMMU_PC_COUNTER_REG, &prev_raw_count, true);
		amd_iommu_pc_set_reg(iommu, hwc->iommu_bank, hwc->iommu_cntr,
				     IOMMU_PC_COUNTER_REG, &prev_raw_count);
	}
	}


	perf_iommu_enable_event(event);
	perf_iommu_enable_event(event);
@@ -319,37 +308,30 @@ static void perf_iommu_start(struct perf_event *event, int flags)


static void perf_iommu_read(struct perf_event *event)
static void perf_iommu_read(struct perf_event *event)
{
{
	u64 count = 0ULL;
	u64 count, prev, delta;
	u64 prev_raw_count = 0ULL;
	u64 delta = 0ULL;
	struct hw_perf_event *hwc = &event->hw;
	struct hw_perf_event *hwc = &event->hw;
	pr_debug("perf: amd_iommu:perf_iommu_read\n");
	struct amd_iommu *iommu = perf_event_2_iommu(event);


	amd_iommu_pc_get_set_reg_val(_GET_DEVID(event),
	if (amd_iommu_pc_get_reg(iommu, hwc->iommu_bank, hwc->iommu_cntr,
				_GET_BANK(event), _GET_CNTR(event),
				 IOMMU_PC_COUNTER_REG, &count))
				IOMMU_PC_COUNTER_REG, &count, false);
		return;


	/* IOMMU pc counter register is only 48 bits */
	/* IOMMU pc counter register is only 48 bits */
	count &= 0xFFFFFFFFFFFFULL;
	count &= GENMASK_ULL(47, 0);


	prev_raw_count =  local64_read(&hwc->prev_count);
	prev = local64_read(&hwc->prev_count);
	if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
	if (local64_cmpxchg(&hwc->prev_count, prev, count) != prev)
					count) != prev_raw_count)
		return;
		return;


	/* Handling 48-bit counter overflowing */
	/* Handle 48-bit counter overflow */
	delta = (count << COUNTER_SHIFT) - (prev_raw_count << COUNTER_SHIFT);
	delta = (count << COUNTER_SHIFT) - (prev << COUNTER_SHIFT);
	delta >>= COUNTER_SHIFT;
	delta >>= COUNTER_SHIFT;
	local64_add(delta, &event->count);
	local64_add(delta, &event->count);

}
}


static void perf_iommu_stop(struct perf_event *event, int flags)
static void perf_iommu_stop(struct perf_event *event, int flags)
{
{
	struct hw_perf_event *hwc = &event->hw;
	struct hw_perf_event *hwc = &event->hw;
	u64 config;

	pr_debug("perf: amd_iommu:perf_iommu_stop\n");


	if (hwc->state & PERF_HES_UPTODATE)
	if (hwc->state & PERF_HES_UPTODATE)
		return;
		return;
@@ -361,7 +343,6 @@ static void perf_iommu_stop(struct perf_event *event, int flags)
	if (hwc->state & PERF_HES_UPTODATE)
	if (hwc->state & PERF_HES_UPTODATE)
		return;
		return;


	config = hwc->config;
	perf_iommu_read(event);
	perf_iommu_read(event);
	hwc->state |= PERF_HES_UPTODATE;
	hwc->state |= PERF_HES_UPTODATE;
}
}
@@ -369,17 +350,12 @@ static void perf_iommu_stop(struct perf_event *event, int flags)
static int perf_iommu_add(struct perf_event *event, int flags)
static int perf_iommu_add(struct perf_event *event, int flags)
{
{
	int retval;
	int retval;
	struct perf_amd_iommu *perf_iommu =
			container_of(event->pmu, struct perf_amd_iommu, pmu);


	pr_debug("perf: amd_iommu:perf_iommu_add\n");
	event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
	event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED;


	/* request an iommu bank/counter */
	/* request an iommu bank/counter */
	retval = get_next_avail_iommu_bnk_cntr(perf_iommu);
	retval = get_next_avail_iommu_bnk_cntr(event);
	if (retval != -ENOSPC)
	if (retval)
		event->hw.extra_reg.reg = (u16)retval;
	else
		return retval;
		return retval;


	if (flags & PERF_EF_START)
	if (flags & PERF_EF_START)
@@ -390,115 +366,124 @@ static int perf_iommu_add(struct perf_event *event, int flags)


static void perf_iommu_del(struct perf_event *event, int flags)
static void perf_iommu_del(struct perf_event *event, int flags)
{
{
	struct hw_perf_event *hwc = &event->hw;
	struct perf_amd_iommu *perf_iommu =
	struct perf_amd_iommu *perf_iommu =
			container_of(event->pmu, struct perf_amd_iommu, pmu);
			container_of(event->pmu, struct perf_amd_iommu, pmu);


	pr_debug("perf: amd_iommu:perf_iommu_del\n");
	perf_iommu_stop(event, PERF_EF_UPDATE);
	perf_iommu_stop(event, PERF_EF_UPDATE);


	/* clear the assigned iommu bank/counter */
	/* clear the assigned iommu bank/counter */
	clear_avail_iommu_bnk_cntr(perf_iommu,
	clear_avail_iommu_bnk_cntr(perf_iommu,
				     _GET_BANK(event),
				   hwc->iommu_bank, hwc->iommu_cntr);
				     _GET_CNTR(event));


	perf_event_update_userpage(event);
	perf_event_update_userpage(event);
}
}


static __init int _init_events_attrs(struct perf_amd_iommu *perf_iommu)
static __init int _init_events_attrs(void)
{
{
	struct attribute **attrs;
	struct attribute_group *attr_group;
	int i = 0, j;
	int i = 0, j;
	struct attribute **attrs;


	while (amd_iommu_v2_event_descs[i].attr.attr.name)
	while (amd_iommu_v2_event_descs[i].attr.attr.name)
		i++;
		i++;


	attr_group = kzalloc(sizeof(struct attribute *)
	attrs = kzalloc(sizeof(struct attribute **) * (i + 1), GFP_KERNEL);
		* (i + 1) + sizeof(*attr_group), GFP_KERNEL);
	if (!attrs)
	if (!attr_group)
		return -ENOMEM;
		return -ENOMEM;


	attrs = (struct attribute **)(attr_group + 1);
	for (j = 0; j < i; j++)
	for (j = 0; j < i; j++)
		attrs[j] = &amd_iommu_v2_event_descs[j].attr.attr;
		attrs[j] = &amd_iommu_v2_event_descs[j].attr.attr;


	attr_group->name = "events";
	amd_iommu_events_group.attrs = attrs;
	attr_group->attrs = attrs;
	perf_iommu->events_group = attr_group;

	return 0;
	return 0;
}
}


static __init void amd_iommu_pc_exit(void)
const struct attribute_group *amd_iommu_attr_groups[] = {
{
	&amd_iommu_format_group,
	if (__perf_iommu.events_group != NULL) {
	&amd_iommu_cpumask_group,
		kfree(__perf_iommu.events_group);
	&amd_iommu_events_group,
		__perf_iommu.events_group = NULL;
	NULL,
	}
};
}

static struct pmu iommu_pmu = {
	.event_init	= perf_iommu_event_init,
	.add		= perf_iommu_add,
	.del		= perf_iommu_del,
	.start		= perf_iommu_start,
	.stop		= perf_iommu_stop,
	.read		= perf_iommu_read,
	.task_ctx_nr	= perf_invalid_context,
	.attr_groups	= amd_iommu_attr_groups,
};


static __init int _init_perf_amd_iommu(
static __init int init_one_iommu(unsigned int idx)
	struct perf_amd_iommu *perf_iommu, char *name)
{
{
	struct perf_amd_iommu *perf_iommu;
	int ret;
	int ret;


	raw_spin_lock_init(&perf_iommu->lock);
	perf_iommu = kzalloc(sizeof(struct perf_amd_iommu), GFP_KERNEL);
	if (!perf_iommu)
		return -ENOMEM;


	/* Init format attributes */
	raw_spin_lock_init(&perf_iommu->lock);
	perf_iommu->format_group = &amd_iommu_format_group;


	/* Init cpumask attributes to only core 0 */
	perf_iommu->pmu          = iommu_pmu;
	cpumask_set_cpu(0, &iommu_cpumask);
	perf_iommu->iommu        = get_amd_iommu(idx);
	perf_iommu->cpumask_group = &amd_iommu_cpumask_group;
	perf_iommu->max_banks    = amd_iommu_pc_get_max_banks(idx);
	perf_iommu->max_counters = amd_iommu_pc_get_max_counters(idx);


	/* Init events attributes */
	if (!perf_iommu->iommu ||
	if (_init_events_attrs(perf_iommu) != 0)
	    !perf_iommu->max_banks ||
		pr_err("perf: amd_iommu: Only support raw events.\n");
	    !perf_iommu->max_counters) {
		kfree(perf_iommu);
		return -EINVAL;
	}


	/* Init null attributes */
	snprintf(perf_iommu->name, IOMMU_NAME_SIZE, "amd_iommu_%u", idx);
	perf_iommu->null_group = NULL;
	perf_iommu->pmu.attr_groups = perf_iommu->attr_groups;


	ret = perf_pmu_register(&perf_iommu->pmu, name, -1);
	ret = perf_pmu_register(&perf_iommu->pmu, perf_iommu->name, -1);
	if (ret) {
	if (!ret) {
		pr_err("perf: amd_iommu: Failed to initialized.\n");
		pr_info("Detected AMD IOMMU #%d (%d banks, %d counters/bank).\n",
		amd_iommu_pc_exit();
			idx, perf_iommu->max_banks, perf_iommu->max_counters);
		list_add_tail(&perf_iommu->list, &perf_amd_iommu_list);
	} else {
	} else {
		pr_info("perf: amd_iommu: Detected. (%d banks, %d counters/bank)\n",
		pr_warn("Error initializing IOMMU %d.\n", idx);
			amd_iommu_pc_get_max_banks(IOMMU_BASE_DEVID),
		kfree(perf_iommu);
			amd_iommu_pc_get_max_counters(IOMMU_BASE_DEVID));
	}
	}

	return ret;
	return ret;
}
}


static struct perf_amd_iommu __perf_iommu = {
	.pmu = {
		.task_ctx_nr    = perf_invalid_context,
		.event_init	= perf_iommu_event_init,
		.add		= perf_iommu_add,
		.del		= perf_iommu_del,
		.start		= perf_iommu_start,
		.stop		= perf_iommu_stop,
		.read		= perf_iommu_read,
	},
	.max_banks		= 0x00,
	.max_counters		= 0x00,
	.cntr_assign_mask	= 0ULL,
	.format_group		= NULL,
	.cpumask_group		= NULL,
	.events_group		= NULL,
	.null_group		= NULL,
};

static __init int amd_iommu_pc_init(void)
static __init int amd_iommu_pc_init(void)
{
{
	unsigned int i, cnt = 0;
	int ret;

	/* Make sure the IOMMU PC resource is available */
	/* Make sure the IOMMU PC resource is available */
	if (!amd_iommu_pc_supported())
	if (!amd_iommu_pc_supported())
		return -ENODEV;
		return -ENODEV;


	_init_perf_amd_iommu(&__perf_iommu, "amd_iommu");
	ret = _init_events_attrs();
	if (ret)
		return ret;

	/*
	 * An IOMMU PMU is specific to an IOMMU, and can function independently.
	 * So we go through all IOMMUs and ignore the one that fails init
	 * unless all IOMMU are failing.
	 */
	for (i = 0; i < amd_iommu_get_num_iommus(); i++) {
		ret = init_one_iommu(i);
		if (!ret)
			cnt++;
	}


	if (!cnt) {
		kfree(amd_iommu_events_group.attrs);
		return -ENODEV;
	}

	/* Init cpumask attributes to only core 0 */
	cpumask_set_cpu(0, &iommu_cpumask);
	return 0;
	return 0;
}
}


+12 −6
Original line number Original line Diff line number Diff line
@@ -24,17 +24,23 @@
#define PC_MAX_SPEC_BNKS			64
#define PC_MAX_SPEC_BNKS			64
#define PC_MAX_SPEC_CNTRS			16
#define PC_MAX_SPEC_CNTRS			16


/* iommu pc reg masks*/
struct amd_iommu;
#define IOMMU_BASE_DEVID			0x0000


/* amd_iommu_init.c external support functions */
/* amd_iommu_init.c external support functions */
extern int amd_iommu_get_num_iommus(void);

extern bool amd_iommu_pc_supported(void);
extern bool amd_iommu_pc_supported(void);


extern u8 amd_iommu_pc_get_max_banks(u16 devid);
extern u8 amd_iommu_pc_get_max_banks(unsigned int idx);

extern u8 amd_iommu_pc_get_max_counters(unsigned int idx);

extern int amd_iommu_pc_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr,
				u8 fxn, u64 *value);


extern u8 amd_iommu_pc_get_max_counters(u16 devid);
extern int amd_iommu_pc_get_reg(struct amd_iommu *iommu, u8 bank, u8 cntr,
				u8 fxn, u64 *value);


extern int amd_iommu_pc_get_set_reg_val(u16 devid, u8 bank, u8 cntr,
extern struct amd_iommu *get_amd_iommu(int idx);
			u8 fxn, u64 *value, bool is_write);


#endif /*_PERF_EVENT_AMD_IOMMU_H_*/
#endif /*_PERF_EVENT_AMD_IOMMU_H_*/
Loading