Add the Cell hardware-sampling module. The Cell PMU provides a hardware buffer that can store up to 1024 128-bit samples. There are several possible data formats for each sample, based on the type of sampling the user selects. These include several types of counter- based sampling modes, a few address tracing modes, and a thermal data mode.
All of these sampling modes are based on a hardware timer. However, this does not fit well into the general Perfmon design for the sampling-format modules, which is to collect samples based on counter overflows. Therefore, some of the sample collection code is "duplicated" in order to deal with the restrictions imposed by the Cell PMU. In order to do this, the pfm_mask_monitoring() and pfm_get_new_msg() routines in the Perfmon core code had to be exported so this module could call them. Signed-off-by: Kevin Corry <[EMAIL PROTECTED]> Signed-off-by: Carl Love <[EMAIL PROTECTED]> --- arch/powerpc/perfmon/Kconfig | 9 + arch/powerpc/perfmon/Makefile | 9 - arch/powerpc/perfmon/perfmon_cell.c | 51 +++- arch/powerpc/perfmon/perfmon_cell_hw_smpl.c | 392 +++++++++++++++++++++++++++ arch/powerpc/platforms/cell/pmu.c | 2 include/asm-powerpc/cell-pmu.h | 5 include/asm-powerpc/perfmon_cell_hw_smpl.h | 119 ++++++++ include/linux/perfmon.h | 3 perfmon/perfmon.c | 4 perfmon/perfmon_intr.c | 3 10 files changed, 588 insertions(+), 9 deletions(-) diff --git a/arch/powerpc/perfmon/Kconfig b/arch/powerpc/perfmon/Kconfig index ea8a81a..6ef5968 100644 --- a/arch/powerpc/perfmon/Kconfig +++ b/arch/powerpc/perfmon/Kconfig @@ -38,4 +38,13 @@ config PERFMON_CELL Enables support for the Cell hardware performance counters. If unsure, say M. +config PERFMON_CELL_HW_SMPL + tristate "Support for Cell hardware counter sampling" + depends on PERFMON_CELL + default n + help + Enables support for the Cell hardware counter sampling modes using + the PMU trace-buffer. + If unsure, say M. + endmenu diff --git a/arch/powerpc/perfmon/Makefile b/arch/powerpc/perfmon/Makefile index b1dfc51..ba75eaa 100644 --- a/arch/powerpc/perfmon/Makefile +++ b/arch/powerpc/perfmon/Makefile @@ -1,4 +1,5 @@ -obj-$(CONFIG_PERFMON) += perfmon.o -obj-$(CONFIG_PERFMON_POWER5) += perfmon_power5.o -obj-$(CONFIG_PERFMON_PPC32) += perfmon_ppc32.o -obj-$(CONFIG_PERFMON_CELL) += perfmon_cell.o +obj-$(CONFIG_PERFMON) += perfmon.o +obj-$(CONFIG_PERFMON_POWER5) += perfmon_power5.o +obj-$(CONFIG_PERFMON_PPC32) += perfmon_ppc32.o +obj-$(CONFIG_PERFMON_CELL) += perfmon_cell.o +obj-$(CONFIG_PERFMON_CELL_HW_SMPL) += perfmon_cell_hw_smpl.o diff --git a/arch/powerpc/perfmon/perfmon_cell.c b/arch/powerpc/perfmon/perfmon_cell.c index e0a3976..91c6f75 100644 --- a/arch/powerpc/perfmon/perfmon_cell.c +++ b/arch/powerpc/perfmon/perfmon_cell.c @@ -29,6 +29,7 @@ #include <asm/io.h> #include <asm/rtas.h> #include "../platforms/cell/cbe_regs.h" +#include <asm/perfmon_cell_hw_smpl.h> MODULE_AUTHOR("Kevin Corry <[EMAIL PROTECTED]>, " "Carl Love <[EMAIL PROTECTED]>"); @@ -76,7 +77,6 @@ static struct pfm_regmap_desc pfm_cell_pmc_desc[] = { }; #define PFM_PM_NUM_PMCS ARRAY_SIZE(pfm_cell_pmc_desc) -#define CELL_PMC_PM_STATUS 20 /* * Mapping from Perfmon logical data counters to Cell hardware counters. */ @@ -297,11 +297,26 @@ static void pfm_cell_enable_counters(struct pfm_context *ctx, * pfm_cell_disable_counters * * Just need to turn off the global disable bit in pm_control. + * + * Also, if we're using the hardware-sampling module, we need to empty the + * trace-buffer, since it cannot be restored to its current state when this + * event-set is enabled again. **/ static void pfm_cell_disable_counters(struct pfm_context *ctx, struct pfm_event_set *set) { + struct pfm_smpl_fmt *smpl_fmt = ctx->smpl_fmt; + cbe_disable_pm(smp_processor_id()); + + if (smpl_fmt && + ctx->state != PFM_CTX_MASKED && + !strcmp(smpl_fmt->fmt_name, PFM_CELL_HW_SMPL_NAME)) { + ctx->ovfl_arg.ovfl_pmd = PFM_CELL_HW_SMPL_OVFL_PMD_PARTIAL; + ctx->ovfl_arg.active_set = ctx->active_set->id; + smpl_fmt->fmt_handler(ctx->smpl_addr, + &ctx->ovfl_arg, 0, 0, NULL); + } } /** @@ -463,7 +478,32 @@ static void handle_trace_buffer_interrupts(unsigned long iip, struct pfm_context *ctx, u32 pm_status) { - /* FIX: Currently ignoring trace-buffer interrupts. */ + struct pfm_smpl_fmt *smpl_fmt; + + if (pm_status & CBE_PM_TRACE_BUFFER_FULL_INTR) { + /* The trace-buffer is full. Get the sampling-buffer address + * and call the handler. Need to lock the context here instead + * of in the handler, because the handler can also be called + * by the pfm_cell_disable_counters() routine, which is called + * with the context already locked. + */ + smpl_fmt = ctx->smpl_fmt; + + if (smpl_fmt && + !strcmp(smpl_fmt->fmt_name, PFM_CELL_HW_SMPL_NAME)) { + ctx->ovfl_arg.ovfl_pmd = PFM_CELL_HW_SMPL_OVFL_PMD; + ctx->ovfl_arg.active_set = ctx->active_set->id; + spin_lock(&ctx->lock); + smpl_fmt->fmt_handler(ctx->smpl_addr, &ctx->ovfl_arg, + iip, 0, regs); + spin_unlock(&ctx->lock); + } + } + + /* Currently the trace buffer underflow and interval timer + * interrupts are ignored. + */ + return; } @@ -522,6 +562,13 @@ static void pfm_cell_irq_handler(struct pt_regs *regs, struct pfm_context *ctx) handle_trace_buffer_interrupts(instruction_pointer(regs), regs, ctx, last_read_pm_status); + /* If the hardware-sampling module masked monitoring for this context, + * don't re-enable the PMU. + */ + if (ctx->state == PFM_CTX_MASKED) { + return; + } + /* The interrupt settings is the value written to the pm_status * register. It is saved in the context when the register is * written. diff --git a/arch/powerpc/perfmon/perfmon_cell_hw_smpl.c b/arch/powerpc/perfmon/perfmon_cell_hw_smpl.c new file mode 100644 index 0000000..7a47797 --- /dev/null +++ b/arch/powerpc/perfmon/perfmon_cell_hw_smpl.c @@ -0,0 +1,392 @@ +/* + * Copyright IBM Corp 2007 + * + * Contributed by Carl Love <[EMAIL PROTECTED]> + * and Kevin Corry <[EMAIL PROTECTED]> + * + * 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 + * + * + * This file implements the IBM Cell PMU hardware-sampling module. + */ +#include <linux/module.h> +#include <linux/perfmon.h> +#include <asm/cell-pmu.h> +#include <asm/perfmon_cell_hw_smpl.h> + +MODULE_AUTHOR("Carl Love <[EMAIL PROTECTED]>, " + "Kevin Corry <[EMAIL PROTECTED]>"); +MODULE_DESCRIPTION("Perfmon2 CELL hardware sampling format"); +MODULE_LICENSE("GPL"); + +/** + * pfm_cell_hw_smpl_validate + * + * Validate the arguments passed from user-space for creating the + * sampling-buffer. The buffer must be large enough to hold the + * sampling-buffer header and at least one copy of the trace-buffer. + **/ +static int pfm_cell_hw_smpl_validate(u32 flags, u16 npmds, void *user_arg) +{ + struct pfm_cell_hw_smpl_arg *smpl_arg = user_arg; + + if (!smpl_arg) { + PFM_ERR("No argument passed."); + return -EINVAL; + } + + if (smpl_arg->buf_size < PFM_CELL_HW_SMPL_MIN_BUF_SIZE) { + PFM_ERR("Specified buffer size (%lu) too small. " + "Min size is %lu bytes.", + smpl_arg->buf_size, PFM_CELL_HW_SMPL_MIN_BUF_SIZE); + return -EINVAL; + } + + return 0; +} + +/** + * pfm_cell_hw_smpl_get_size + * + * Tell the Perfmon2 core how large a buffer we need to have allocated, and + * it will do the allocation for us. The size of the buffer has already been + * validated. + **/ +static int pfm_cell_hw_smpl_get_size(unsigned int flags, + void *user_arg, size_t *size) +{ + struct pfm_cell_hw_smpl_arg *smpl_arg = user_arg; + *size = smpl_arg->buf_size; + return 0; +} + +/** + * pfm_cell_hw_smpl_init + * + * Initialize the start of the sampling-buffer with a header structure. + * The buffer has already been allocated by the Perfmon2 core. + **/ +static int pfm_cell_hw_smpl_init(struct pfm_context *ctx, void *smpl_buf, + u32 flags, u16 npmds, void *user_arg) +{ + struct pfm_cell_hw_smpl_hdr *smpl_hdr = smpl_buf; + struct pfm_cell_hw_smpl_arg *smpl_arg = user_arg; + + smpl_hdr->count = 0; + smpl_hdr->cur_offset = sizeof(*smpl_hdr); + smpl_hdr->overflows = 0; + smpl_hdr->buf_size = smpl_arg->buf_size; + smpl_hdr->version = PFM_CELL_HW_SMPL_VERSION; + smpl_hdr->buf_flags = smpl_arg->buf_flags; + + return 0; +} + +/** + * pfm_cell_hw_smpl_add_msg + * + * Add a "sampling-buffer-full" message to the context. A user-space process + * can then read() from the file-descriptor to get a copy of the message. + **/ +static int pfm_cell_hw_smpl_add_msg(struct pfm_context *ctx) +{ + union pfarg_msg *msg; + + if (ctx->flags.no_msg) { + /* User-space won't be reading any messages. */ + return 0; + } + + msg = pfm_get_new_msg(ctx); + if (msg == NULL) { + /* The message queue is full. The user must have called + * pfm_restart(), but didn't extract any messages. + */ + PFM_ERR("No notification messages available."); + return -EBUSY; + } + + msg->type = PFM_MSG_CELL_HW_SMPL_BUF_FULL; + + return 0; +} + +/** + * init_entry_header + **/ +static struct pfm_cell_hw_smpl_entry_hdr * +init_entry_header(struct pfm_cell_hw_smpl_hdr *buf_hdr, + struct pfm_event_set *set) +{ + struct pfm_cell_hw_smpl_entry_hdr *ent_hdr; + + ent_hdr = (void *)buf_hdr + buf_hdr->cur_offset; + ent_hdr->pid = current->pid; + ent_hdr->tgid = current->tgid; + ent_hdr->cpu = smp_processor_id(); + ent_hdr->set = set->id; + ent_hdr->num_samples = 0; + ent_hdr->entry_num = buf_hdr->count; + ent_hdr->pm_interval = 0; + + return ent_hdr; +} + +/** + * pmds_long_reset + * + * Reset all in-use PMDs to their "long" reset value. We write to the hardware + * counters, so this should be called before "saving" the counters to the + * event-set copy. + **/ +static inline void pmds_long_reset(struct pfm_event_set *set, int cpu) +{ + int i; + for (i = 0; i < NR_CTRS; i++) + if (test_bit(i, cast_ulp(&set->used_pmds))) + cbe_write_ctr(cpu, i, set->pmds[i].long_reset); +} + +/** + * read_trace_buffer + * + * Read at most 1024 samples from the trace-buffer. Note, samples could + * be added to the trace-buffer while it is being read. However, we only + * made sure we had space for up to 1024 samples. + **/ +static void read_trace_buffer(struct pfm_cell_hw_smpl_entry_hdr *ent_hdr, + u64 **trace_buffer_sample) +{ + int cpu = smp_processor_id(); + u32 trace_addr; + + *trace_buffer_sample = (u64 *)(ent_hdr + 1); + trace_addr = cbe_read_pm(cpu, trace_address); + while (!(trace_addr & CBE_PM_TRACE_BUF_EMPTY) && + ent_hdr->num_samples < CBE_PM_TRACE_BUF_MAX_COUNT) { + cbe_read_trace_buffer(cpu, *trace_buffer_sample); + *trace_buffer_sample += 2; + ent_hdr->num_samples++; + trace_addr = cbe_read_pm(cpu, trace_address); + } +} + +/** + * read_partial_sample + * + * We are disabling this event-set, and are in the middle of a sampling + * interval. When this event set gets re-enabled, the pm_interval will get + * reset to its initial value, so we'll lose the counter values from this + * incomplete sample. In "count" sampling-mode, we should be able to prevent + * this data loss by recording a "partial" sample from the actual counters. + * In occurrence and threshold sampling, however, the partial data is not + * available, so we'll have to settle for an "empty" sample. In any case, + * fill in the current pm_interval value so user-space will know that the last + * sample in this entry is partial or empty, and so they will be able to + * calculate the percentage-complete for that sample. + **/ +static void read_partial_sample(struct pfm_event_set *set, + struct pfm_cell_hw_smpl_entry_hdr *ent_hdr, + u64 **trace_buffer_sample) +{ + u32 pm_control = set->pmcs[CELL_PMC_PM_CONTROL]; + u32 *partial_sample = (u32 *)*trace_buffer_sample; + int i, cpu = smp_processor_id(); + + if (CBE_PM_TRACE_MODE_GET(pm_control) == CBE_PM_TRACE_MODE_COUNT) { + for (i = 0; i < NR_PHYS_CTRS; i++) { + partial_sample[i] = cbe_read_phys_ctr(cpu, i); + } + } else { + memset(partial_sample, 0, + NR_PHYS_CTRS * sizeof(*partial_sample)); + } + + ent_hdr->pm_interval = cbe_read_pm(cpu, pm_interval); + ent_hdr->num_samples++; + *trace_buffer_sample += 2; + + /* In all cases, reset the in-use PMDs to their "long" reset value + * since we've effectively invalidated the data in this interval. + */ + pmds_long_reset(set, cpu); +} + +/** + * handle_full_buffer + **/ +static int handle_full_buffer(struct pfm_cell_hw_smpl_hdr *buf_hdr, + struct pfm_context *ctx, + struct pfm_event_set *set) +{ + int cpu = smp_processor_id(); + + /* Increment the number of sampling-buffer overflows. This + * is important for detecting duplicate sets of samples. + */ + buf_hdr->overflows++; + + /* Reset the PMDs to their "long-reset" value. The hardware counters + * will be saved during the mask-monitoring call, and will be + * restored later when we are unmasked, so we need to be sure the + * counters are set to their correct initial values. + */ + pmds_long_reset(set, cpu); + + /* Reset the interval timer so we start a whole + * new interval when we get restarted. + */ + cbe_write_pm(cpu, pm_interval, set->pmcs[CELL_PMC_PM_INTERVAL]); + + /* Mask monitoring until a pfm_restart() occurs. */ + pfm_mask_monitoring(ctx, set); + ctx->state = PFM_CTX_MASKED; + ctx->flags.can_restart = 1; + + /* Add a message to the context's message queue. */ + pfm_cell_hw_smpl_add_msg(ctx); + + return -ENOBUFS; +} + +/** + * pfm_cell_hw_smpl_handler + * + * Create a new entry-header in the sampling-buffer and copy the current + * contents of the trace-buffer into the sampling-buffer. + **/ +static int pfm_cell_hw_smpl_handler(void *smpl_buf, + struct pfm_ovfl_arg *ovfl_arg, + unsigned long ip, + u64 tstamp, + void *data) +{ + struct pfm_cell_hw_smpl_hdr *buf_hdr = smpl_buf; + struct pfm_cell_hw_smpl_entry_hdr *ent_hdr; + struct pfm_event_set *set; + struct pfm_context *ctx; + u64 *trace_buffer_sample; + u64 free_bytes; + + /* If this handler was called due to an actual PMD overflowing, do + * nothing. Only store the contents of the trace-buffer if the trace- + * buffer overflowed or if we're disabling an event-set (during a + * process context-switch or an event-set switch). + */ + if (!(ovfl_arg->ovfl_pmd == PFM_CELL_HW_SMPL_OVFL_PMD || + ovfl_arg->ovfl_pmd == PFM_CELL_HW_SMPL_OVFL_PMD_PARTIAL)) + return 0; + + ctx = __get_cpu_var(pmu_ctx); + set = ctx->active_set; + + /* Check if the sampling-buffer is full. This should never happen, + * since we check for a full buffer after adding the new entry. + */ + free_bytes = buf_hdr->buf_size - buf_hdr->cur_offset; + if (free_bytes < PFM_CELL_HW_SMPL_MAX_ENTRY_SIZE) + return handle_full_buffer(buf_hdr, ctx, set); + + ent_hdr = init_entry_header(buf_hdr, set); + + read_trace_buffer(ent_hdr, &trace_buffer_sample); + + if (ovfl_arg->ovfl_pmd == PFM_CELL_HW_SMPL_OVFL_PMD_PARTIAL && + ent_hdr->num_samples < CBE_PM_TRACE_BUF_MAX_COUNT) { + read_partial_sample(set, ent_hdr, &trace_buffer_sample); + } + + /* Update the sampling-buffer header for the next entry. Since the + * hw_smpl_hdr and hw_smpl_entry_hdr structures are both padded to + * 128-bits, and each trace-buffer sample is 128-bits, we know that + * every buffer entry will start on a 128-bit boundary. + */ + if (ent_hdr->num_samples) { + buf_hdr->cur_offset = (void *)trace_buffer_sample - + (void *)buf_hdr; + buf_hdr->count++; + } + + /* Check the available size in the buffer again so we won't lose the + * next sample entry. + */ + free_bytes = buf_hdr->buf_size - buf_hdr->cur_offset; + if (free_bytes < PFM_CELL_HW_SMPL_MAX_ENTRY_SIZE) + return handle_full_buffer(buf_hdr, ctx, set); + + return 0; +} + +/** + * pfm_cell_hw_smpl_restart + * + * Reinitialize the sampling-buffer header, effectively deleting all entries + * previously stored in the sampling-buffer. + * + * FIX: What is the "is_active" argument for? It's not used by any of the + * other sampling modules. + **/ +static int pfm_cell_hw_smpl_restart(int is_active, + u32 *ovfl_ctrl, + void *smpl_buf) +{ + struct pfm_cell_hw_smpl_hdr *buf_hdr = smpl_buf; + + buf_hdr->count = 0; + buf_hdr->cur_offset = sizeof(*buf_hdr); + buf_hdr->overflows = 0; + + return 0; +} + +/** + * pfm_cell_hw_smpl_exit + **/ +static int pfm_cell_hw_smpl_exit(void *smpl_buf) +{ + return 0; +} + +/** + * cell_hw_smpl_fmt + * + * Structure to describe the Cell hardware-sampling module to the Perfmon2 core. + **/ +static struct pfm_smpl_fmt cell_hw_smpl_fmt = { + .fmt_name = PFM_CELL_HW_SMPL_NAME, + .fmt_arg_size = sizeof(struct pfm_cell_hw_smpl_arg), + .fmt_flags = PFM_FMT_BUILTIN_FLAG, + .fmt_version = PFM_CELL_HW_SMPL_VERSION, + .fmt_validate = pfm_cell_hw_smpl_validate, + .fmt_getsize = pfm_cell_hw_smpl_get_size, + .fmt_init = pfm_cell_hw_smpl_init, + .fmt_handler = pfm_cell_hw_smpl_handler, + .fmt_restart = pfm_cell_hw_smpl_restart, + .fmt_exit = pfm_cell_hw_smpl_exit, + .owner = THIS_MODULE, +}; + +static int __init pfm_cell_hw_smpl_init_module(void) +{ + return pfm_fmt_register(&cell_hw_smpl_fmt); +} + +static void __exit pfm_cell_hw_smpl_exit_module(void) +{ + pfm_fmt_unregister(&cell_hw_smpl_fmt); +} + +module_init(pfm_cell_hw_smpl_init_module); +module_exit(pfm_cell_hw_smpl_exit_module); diff --git a/arch/powerpc/platforms/cell/pmu.c b/arch/powerpc/platforms/cell/pmu.c index 66ca4b5..e518af7 100644 --- a/arch/powerpc/platforms/cell/pmu.c +++ b/arch/powerpc/platforms/cell/pmu.c @@ -213,7 +213,7 @@ u32 cbe_read_pm(u32 cpu, enum pm_reg_name reg) break; case pm_interval: - READ_SHADOW_REG(val, pm_interval); + READ_MMIO_UPPER32(val, pm_interval); break; case pm_start_stop: diff --git a/include/asm-powerpc/cell-pmu.h b/include/asm-powerpc/cell-pmu.h index 981db26..aa7416d 100644 --- a/include/asm-powerpc/cell-pmu.h +++ b/include/asm-powerpc/cell-pmu.h @@ -41,6 +41,11 @@ #define CBE_PM_FREEZE_ALL_CTRS 0x00100000 #define CBE_PM_ENABLE_EXT_TRACE 0x00008000 +#define CBE_PM_TRACE_MODE_NONE 0 +#define CBE_PM_TRACE_MODE_COUNT 1 +#define CBE_PM_TRACE_MODE_OCCURRENCE 2 +#define CBE_PM_TRACE_MODE_THRESHOLD 3 + /* Macros for the trace_address register. */ #define CBE_PM_TRACE_BUF_FULL 0x00000800 #define CBE_PM_TRACE_BUF_EMPTY 0x00000400 diff --git a/include/asm-powerpc/perfmon_cell_hw_smpl.h b/include/asm-powerpc/perfmon_cell_hw_smpl.h new file mode 100644 index 0000000..4927438 --- /dev/null +++ b/include/asm-powerpc/perfmon_cell_hw_smpl.h @@ -0,0 +1,119 @@ +/* + * Copyright IBM Corp 2007 + * + * Contributed by Carl Love <[EMAIL PROTECTED]> + * and Kevin Corry <[EMAIL PROTECTED]> + * + * 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 + */ +#ifndef __ASM_POWERPC_PERFMON_CELL_HW_SMPL_H__ +#define __ASM_POWERPC_PERFMON_CELL_HW_SMPL_H__ + +/** + * struct pfm_cell_hw_smpl_arg + * + * @buf_size: Desired size of full sampling-buffer (in bytes). + * @buf_flags: Sampling-buffer-specific flags. + * @reserved1: Pad to 128-bit boundary. + * + * Format specific parameters (passed at context creation). + **/ +struct pfm_cell_hw_smpl_arg { + __u64 buf_size; + __u32 buf_flags; + __u32 reserved1; +}; + +/** + * struct pfm_cell_hw_smpl_hdr + * + * @count: Number of valid pfm_cell_hw_smpl_entry_hdr's in the sampling-buffer. + * @cur_offset: Offset (in bytes) from the top of the sampling-buffer to the + * next available space for a sampling-buffer entry. + * @overflows: Number of times the sampling-buffer has filled up. + * @buf_size: Total bytes in the sampling-buffer. + * @version: Sampling module version. + * @buf_flags: Copy of buf_flags from pfm_cell_hw_smpl_arg. + * @reserved1: Pad to 128-bit boundary. + * + * This header is at the beginning of the sampling-buffer returned to the user. + * It is directly followed by the first entry header. + **/ +struct pfm_cell_hw_smpl_hdr { + __u64 count; + __u64 cur_offset; + __u64 overflows; + __u64 buf_size; + __u32 version; + __u32 buf_flags; + __u64 reserved1; +}; + +/** + * struct pfm_cell_hw_smpl_entry_hdr + * + * @pid: Thread ID. + * @tgid: Thread group ID. For NPTL, this is getpid(). + * @cpu: CPU on which the overflow occurred. + * @set: Event-set that was active when the overflow occurred. + * @num_samples: Number of 128-bit trace-buffer samples in this entry. + * @entry_num: Sequence number of sampling-buffer entries. + * @pm_interval: If the last sample in this entry is for a partial time + * interval, this is the value of pm_interval when the partial + * sample was recorded. If the value is zero, there is no partial + * sample at the end of the entry. + * @reserved1: Pad to 128-bit boundary. + * + * The header for each data entry in the sampling-buffer. The entry header + * is immediately followed by the contents of the trace-buffer. Each sample in + * the trace-buffer is 128 bits wide. The entry header specifies the number + * of 128-bit trace-buffer samples that follow the header. + **/ +struct pfm_cell_hw_smpl_entry_hdr { + __u32 pid; + __u32 tgid; + __u32 cpu; + __u32 set; + __u32 num_samples; + __u32 entry_num; + __u32 pm_interval; + __u32 reserved1; +}; + +/* The max size of each sampling-buffer entry is the size of the entry header + * plus the full size of the trace-buffer. + */ +#define PFM_CELL_HW_SMPL_MAX_ENTRY_SIZE \ + (sizeof(struct pfm_cell_hw_smpl_entry_hdr) + \ + 2 * sizeof(u64) * CBE_PM_TRACE_BUF_MAX_COUNT) + +/* The sampling-buffer must be at least as large as the sampling-buffer header + * and the max size of one sampling-buffer entry. + */ +#define PFM_CELL_HW_SMPL_MIN_BUF_SIZE (sizeof(struct pfm_cell_hw_smpl_hdr) + \ + PFM_CELL_HW_SMPL_MAX_ENTRY_SIZE) + +#define PFM_CELL_HW_SMPL_VERSION 1 +#define PFM_CELL_HW_SMPL_NAME "perfmon_cell_hw_smpl" +#define PFM_CELL_HW_SMPL_OVFL_PMD (PFM_MAX_PMDS + 1) +#define PFM_CELL_HW_SMPL_OVFL_PMD_PARTIAL (PFM_MAX_PMDS + 2) +#define PFM_MSG_CELL_HW_SMPL_BUF_FULL 99 + +/* Values are indexes into pfm_cell_pmc_desc[] array. */ +#define CELL_PMC_PM_STATUS 20 +#define CELL_PMC_PM_CONTROL 21 +#define CELL_PMC_PM_INTERVAL 22 + +#endif /* __ASM_POWERPC_PERFMON_CELL_HW_SMPL_H__ */ diff --git a/include/linux/perfmon.h b/include/linux/perfmon.h index 631cfca..66f0a44 100644 --- a/include/linux/perfmon.h +++ b/include/linux/perfmon.h @@ -593,10 +593,13 @@ void pfm_switch_sets(struct pfm_context *ctx, int reset_mode, int no_restart); +union pfarg_msg *pfm_get_new_msg(struct pfm_context *ctx); void pfm_save_pmds(struct pfm_context *ctx, struct pfm_event_set *set); int pfm_ovfl_notify_user(struct pfm_context *ctx, struct pfm_event_set *set, unsigned long ip); +void pfm_mask_monitoring(struct pfm_context *ctx, + struct pfm_event_set *set); int pfm_init_fs(void); diff --git a/perfmon/perfmon.c b/perfmon/perfmon.c index 6d1b56e..eb7648c 100644 --- a/perfmon/perfmon.c +++ b/perfmon/perfmon.c @@ -58,6 +58,7 @@ DEFINE_PER_CPU(struct task_struct *, pmu_owner); DEFINE_PER_CPU(struct pfm_context *, pmu_ctx); DEFINE_PER_CPU(u64, pmu_activation_number); DEFINE_PER_CPU(struct pfm_stats, pfm_stats); +EXPORT_PER_CPU_SYMBOL(pmu_ctx); #define PFM_INVALID_ACTIVATION ((u64)~0) @@ -74,7 +75,7 @@ int perfmon_disabled; /* >0 if perfmon is disabled */ * get a new message slot from the queue. If the queue is full NULL * is returned and monitoring stops. */ -static union pfarg_msg *pfm_get_new_msg(struct pfm_context *ctx) +union pfarg_msg *pfm_get_new_msg(struct pfm_context *ctx) { int next; @@ -95,6 +96,7 @@ static union pfarg_msg *pfm_get_new_msg(struct pfm_context *ctx) return ctx->msgq+next; } +EXPORT_SYMBOL(pfm_get_new_msg); void pfm_context_free(struct pfm_context *ctx) { diff --git a/perfmon/perfmon_intr.c b/perfmon/perfmon_intr.c index ba23be5..f9b7cf9 100644 --- a/perfmon/perfmon_intr.c +++ b/perfmon/perfmon_intr.c @@ -40,7 +40,7 @@ #include <linux/module.h> #include <linux/random.h> -static inline void pfm_mask_monitoring(struct pfm_context *ctx, +inline void pfm_mask_monitoring(struct pfm_context *ctx, struct pfm_event_set *set) { u64 now; @@ -63,6 +63,7 @@ static inline void pfm_mask_monitoring(struct pfm_context *ctx, */ set->duration += now - set->duration_start; } +EXPORT_SYMBOL(pfm_mask_monitoring); /* * main overflow processing routine. _______________________________________________ perfmon mailing list perfmon@linux.hpl.hp.com http://www.hpl.hp.com/hosted/linux/mail-archives/perfmon/