From: eranian@googlemail.com
To: linux-kernel@vger.kernel.org
Subject: [patch 09/19] perfmon2 minimal v2: PMU mapping
Date: Tue, 17 Jun 2008 15:02:43 -0700 (PDT) [thread overview]
Message-ID: <48583483.073e400a.7c5d.71c2@mx.google.com> (raw)
This patch adds PMU resource description management functions.
Perfmon2 exports a logical view of the PMU with uniform register
names across all architectures. Each logical register is then
map onto an actual PMU register via a mapping table. Such table
exists for each PMU model.
PMU description tables are hardcoded into the kernel and one
is selected during boot.
Signed-off-by: Stephane Eranian <eranian@gmail.com>
--
Index: o/perfmon/perfmon_pmu.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ o/perfmon/perfmon_pmu.c 2008-06-04 16:18:33.000000000 +0200
@@ -0,0 +1,255 @@
+/*
+ * perfmon_pmu.c: perfmon2 PMU configuration management
+ *
+ * This file implements the perfmon2 interface which
+ * provides access to the hardware performance counters
+ * of the host processor.
+ *
+ * The initial version of perfmon.c was written by
+ * Ganesh Venkitachalam, IBM Corp.
+ *
+ * Then it was modified for perfmon-1.x by Stephane Eranian and
+ * David Mosberger, Hewlett Packard Co.
+ *
+ * Version Perfmon-2.x is a complete rewrite of perfmon-1.x
+ * by Stephane Eranian, Hewlett Packard Co.
+ *
+ * Copyright (c) 1999-2006 Hewlett-Packard Development Company, L.P.
+ * Contributed by Stephane Eranian <eranian@hpl.hp.com>
+ * David Mosberger-Tang <davidm@hpl.hp.com>
+ *
+ * More information about perfmon available at:
+ * http://perfmon2.sf.net
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+#include <linux/module.h>
+#include <linux/perfmon_kern.h>
+#include "perfmon_priv.h"
+
+#ifndef CONFIG_MODULE_UNLOAD
+#define module_refcount(n) 1
+#endif
+
+static __cacheline_aligned_in_smp DEFINE_SPINLOCK(pfm_pmu_conf_lock);
+
+static __cacheline_aligned_in_smp DEFINE_SPINLOCK(pfm_pmu_acq_lock);
+static u32 pfm_pmu_acquired;
+
+/*
+ * perfmon core must acces PMU information ONLY through pfm_pmu_conf
+ * if pfm_pmu_conf is NULL, then no description is registered
+ */
+struct pfm_pmu_config *pfm_pmu_conf;
+EXPORT_SYMBOL(pfm_pmu_conf);
+
+static int pfm_pmu_regdesc_init(u64 *unavail_pmcs, u64 *unavail_pmds)
+{
+ struct pfm_regmap_desc *d;
+ u16 n, n2, n_counters, i;
+ int max1, max2, max3;
+
+ /*
+ * compute the number of implemented PMC from the
+ * description table
+ */
+ n = 0;
+ max1 = max2 = -1;
+ d = pfm_pmu_conf->pmc_desc;
+ for (i = 0; i < pfm_pmu_conf->num_pmc_entries; i++, d++) {
+ if (!(d->type & PFM_REG_I))
+ continue;
+
+ if (test_bit(i, cast_ulp(unavail_pmcs)))
+ continue;
+
+ __set_bit(i, cast_ulp(pfm_pmu_conf->regs.pmcs));
+
+ max1 = i;
+ n++;
+ }
+
+ if (!n) {
+ PFM_INFO("%s PMU description has no PMC registers",
+ pfm_pmu_conf->pmu_name);
+ return -EINVAL;
+ }
+
+ pfm_pmu_conf->regs.max_pmc = max1 + 1;
+ pfm_pmu_conf->regs.num_pmcs = n;
+
+ n = n_counters = n2 = 0;
+ max1 = max2 = max3 = -1;
+ d = pfm_pmu_conf->pmd_desc;
+ for (i = 0; i < pfm_pmu_conf->num_pmd_entries; i++, d++) {
+ if (!(d->type & PFM_REG_I))
+ continue;
+
+ if (test_bit(i, cast_ulp(unavail_pmds)))
+ continue;
+
+ __set_bit(i, cast_ulp(pfm_pmu_conf->regs.pmds));
+ max1 = i;
+ n++;
+
+ /*
+ * read-write registers
+ */
+ if (!(d->type & PFM_REG_RO)) {
+ __set_bit(i, cast_ulp(pfm_pmu_conf->regs.rw_pmds));
+ max3 = i;
+ n2++;
+ }
+
+ /*
+ * counter registers
+ */
+ if (d->type & PFM_REG_C64) {
+ __set_bit(i, cast_ulp(pfm_pmu_conf->regs.cnt_pmds));
+ n_counters++;
+ }
+
+ /*
+ * PMD with intr capabilities
+ */
+ if (d->type & PFM_REG_INTR) {
+ __set_bit(i, cast_ulp(pfm_pmu_conf->regs.intr_pmds));
+ max2 = i;
+ }
+ }
+
+ if (!n) {
+ PFM_INFO("%s PMU description has no PMD registers",
+ pfm_pmu_conf->pmu_name);
+ return -EINVAL;
+ }
+
+ pfm_pmu_conf->regs.max_pmd = max1 + 1;
+ pfm_pmu_conf->regs.max_intr_pmd = max2 + 1;
+
+ pfm_pmu_conf->regs.num_counters = n_counters;
+ pfm_pmu_conf->regs.num_pmds = n;
+ pfm_pmu_conf->regs.max_rw_pmd = max3 + 1;
+ pfm_pmu_conf->regs.num_rw_pmd = n2;
+
+ return 0;
+}
+
+int pfm_pmu_register(struct pfm_pmu_config *cfg)
+{
+ int ret = -EBUSY;
+
+ if (perfmon_disabled) {
+ PFM_INFO("perfmon disabled, cannot add PMU description");
+ return -ENOSYS;
+ }
+
+ spin_lock(&pfm_pmu_conf_lock);
+
+ if (pfm_pmu_conf)
+ goto unlock;
+
+ pfm_pmu_conf = cfg;
+ pfm_pmu_conf->ovfl_mask = (1ULL << cfg->counter_width) - 1;
+
+unlock:
+ spin_unlock(&pfm_pmu_conf_lock);
+
+ if (ret)
+ PFM_INFO("register %s PMU error %d", cfg->pmu_name, ret);
+ else
+ PFM_INFO("%s PMU installed", cfg->pmu_name);
+ return ret;
+}
+
+/*
+ * acquire PMU resource from lower-level PMU register allocator
+ * (currently perfctr-watchdog.c)
+ *
+ * acquisition is done when the first context is created (and not
+ * when it is loaded). We grab all that is defined in the description
+ * module and then we make adjustments at the arch-specific level.
+ *
+ * The PMU resource is released when the last perfmon context is
+ * destroyed.
+ *
+ * interrupts are not masked
+ */
+int pfm_pmu_acquire(void)
+{
+ u64 unavail_pmcs[PFM_PMC_BV];
+ u64 unavail_pmds[PFM_PMD_BV];
+ int ret = 0;
+
+ spin_lock(&pfm_pmu_acq_lock);
+
+ PFM_DBG("pmu_acquired=%d", pfm_pmu_acquired);
+
+ pfm_pmu_acquired++;
+
+ if (pfm_pmu_acquired == 1) {
+
+ memset(unavail_pmcs, 0, sizeof(unavail_pmcs));
+ memset(unavail_pmds, 0, sizeof(unavail_pmds));
+
+ ret = pfm_arch_pmu_acquire(unavail_pmcs, unavail_pmds);
+ if (ret) {
+ pfm_pmu_acquired--;
+ } else {
+ pfm_pmu_regdesc_init(unavail_pmcs, unavail_pmds);
+
+ /* available PMU ressources */
+ PFM_DBG("PMU acquired: %u PMCs, %u PMDs, %u counters",
+ pfm_pmu_conf->regs.num_pmcs,
+ pfm_pmu_conf->regs.num_pmds,
+ pfm_pmu_conf->regs.num_counters);
+ }
+ }
+ spin_unlock(&pfm_pmu_acq_lock);
+
+ return ret;
+}
+
+/*
+ * release the PMU resource
+ *
+ * actual release happens when last context is destroyed
+ *
+ * interrupts are not masked
+ */
+void pfm_pmu_release(void)
+{
+ BUG_ON(irqs_disabled());
+
+ /*
+ * we need to use a spinlock because release takes some time
+ * and we may have a race with pfm_pmu_acquire()
+ */
+ spin_lock(&pfm_pmu_acq_lock);
+
+ PFM_DBG("pmu_acquired=%d", pfm_pmu_acquired);
+
+ /*
+ * we decouple test and decrement because if we had errors
+ * in pfm_pmu_acquire(), we still come here on pfm_context_free()
+ * but with pfm_pmu_acquire=0
+ */
+ if (pfm_pmu_acquired > 0 && --pfm_pmu_acquired == 0) {
+ pfm_arch_pmu_release();
+ memset(&pfm_pmu_conf->regs, 0, sizeof(pfm_pmu_conf->regs));
+ PFM_DBG("PMU released");
+ }
+ spin_unlock(&pfm_pmu_acq_lock);
+}
Index: o/perfmon/Makefile
===================================================================
--- o.orig/perfmon/Makefile 2008-06-04 16:18:22.000000000 +0200
+++ o/perfmon/Makefile 2008-06-04 16:18:33.000000000 +0200
@@ -5,4 +5,4 @@
obj-$(CONFIG_PERFMON) = perfmon_ctx.o perfmon_ctxsw.o \
perfmon_file.o perfmon_attach.o \
perfmon_res.o perfmon_init.o \
- perfmon_intr.o
+ perfmon_intr.o perfmon_pmu.o
Index: o/perfmon/perfmon_priv.h
===================================================================
--- o.orig/perfmon/perfmon_priv.h 2008-06-04 16:18:42.000000000 +0200
+++ o/perfmon/perfmon_priv.h 2008-06-04 16:18:47.000000000 +0200
@@ -52,6 +52,9 @@
void pfm_free_context(struct pfm_context *ctx);
+int pfm_pmu_acquire(void);
+void pfm_pmu_release(void);
+
void pfm_save_pmds(struct pfm_context *ctx);
/*
--
reply other threads:[~2008-06-17 22:05 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=48583483.073e400a.7c5d.71c2@mx.google.com \
--to=eranian@googlemail.com \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.