blob: 1569668925be340302a9847a57c009a740fbc433 [file] [log] [blame]
/*
* perfmon_activate.c: perfmon2 start/stop functions
*
* 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/kernel.h>
#include <linux/perfmon_kern.h>
#include "perfmon_priv.h"
/**
* __pfm_start - activate monitoring
* @ctx: context to operate on
* @start: pfarg_start as passed by user
*
* When operating in per-thread mode and not self-monitoring, the monitored
* thread must be stopped. Activation will be effective next time the thread
* is context switched in.
*
* The pfarg_start argument is optional and may be used to designate
* the initial event set to activate. When not provided, the last active
* set is used. For the first activation, set0 is used when start is NULL.
*
* On some architectures, e.g., IA-64, it may be possible to start monitoring
* without calling this function under certain conditions (per-thread and self
* monitoring). In this case, either set0 or the last active set is used.
*
* the context is locked and interrupts are disabled.
*/
int __pfm_start(struct pfm_context *ctx, struct pfarg_start *start)
{
struct task_struct *task, *owner_task;
struct pfm_smpl_fmt *fmt;
struct pfm_event_set *new_set, *old_set;
int is_self;
task = ctx->task;
/*
* UNLOADED: error
* LOADED : normal start, nop if started unless set is different
* MASKED : nop or change set when unmasking
* ZOMBIE : cannot happen
*/
if (ctx->state == PFM_CTX_UNLOADED)
return -EINVAL;
old_set = new_set = ctx->active_set;
fmt = ctx->smpl_fmt;
/*
* always the case for system-wide
*/
if (task == NULL)
task = current;
is_self = task == current;
/*
* argument is provided?
*/
if (start) {
/*
* find the set to load first
*/
new_set = pfm_find_set(ctx, start->start_set, 0);
if (new_set == NULL) {
PFM_DBG("event set%u does not exist",
start->start_set);
return -EINVAL;
}
}
PFM_DBG("cur_set=%u req_set=%u", old_set->id, new_set->id);
/*
* if we need to change the active set we need
* to check if we can access the PMU
*/
if (new_set != old_set) {
owner_task = __get_cpu_var(pmu_owner);
/*
* system-wide: must run on the right CPU
* per-thread : must be the owner of the PMU context
*
* pfm_switch_sets() returns with monitoring stopped
*/
if (is_self) {
pfm_switch_sets(ctx, new_set, PFM_PMD_RESET_LONG, 1);
} else {
/*
* In a UP kernel, the PMU may contain the state
* of the task we want to operate on, yet the task
* may be switched out (lazy save). We need to save
* current state (old_set), switch active_set and
* mark it for reload.
*/
if (owner_task == task)
pfm_save_pmds(ctx, old_set);
ctx->active_set = new_set;
new_set->priv_flags |= PFM_SETFL_PRIV_MOD_BOTH;
}
}
/*
* mark as started
* must be done before calling pfm_arch_start()
*/
ctx->flags.started = 1;
pfm_arch_start(task, ctx);
if (fmt && fmt->fmt_start)
(*fmt->fmt_start)(ctx);
/*
* we check whether we had a pending ovfl before restarting.
* If so we need to regenerate the interrupt to make sure we
* keep recorded samples. For non-self monitoring this check
* is done in the pfm_ctxswin_thread() routine.
*
* we check new_set/old_set because pfm_switch_sets() already
* takes care of replaying the pending interrupts
*/
if (is_self && new_set != old_set && new_set->npend_ovfls) {
pfm_arch_resend_irq(ctx);
pfm_stats_inc(ovfl_intr_replay_count);
}
/*
* always start with full timeout
*/
new_set->hrtimer_rem = new_set->hrtimer_exp;
/*
* activate timeout for system-wide, self-montoring
* Always start with full timeout
* Timeout is at least one tick away, so no risk of
* having hrtimer_start() trying to wakeup softirqd
* and thus causing troubles. This cannot happen anmyway
* because cb_mode = HRTIMER_CB_IRQSAFE_NO_SOFTIRQ
*/
if (is_self && new_set->flags & PFM_SETFL_TIME_SWITCH) {
hrtimer_start(&__get_cpu_var(pfm_hrtimer),
new_set->hrtimer_rem,
HRTIMER_MODE_REL);
PFM_DBG("set%u started timeout=%lld",
new_set->id,
(unsigned long long)new_set->hrtimer_rem.tv64);
}
/*
* we restart total duration even if context was
* already started. In that case, counts are simply
* reset.
*
* For per-thread, if not self-monitoring, the statement
* below will have no effect because thread is stopped.
* The field is reset of ctxsw in.
*/
new_set->duration_start = sched_clock();
return 0;
}
/**
* __pfm_stop - stop monitoring
* @ctx: context to operate on
* @release_info: infos for caller (see below)
*
* When operating in per-thread* mode and when not self-monitoring,
* the monitored thread must be stopped.
*
* the context is locked and interrupts are disabled.
*
* release_info value upon return:
* - bit 0 : unused
* - bit 1 : when set, must cancel hrtimer
*/
int __pfm_stop(struct pfm_context *ctx, int *release_info)
{
struct pfm_event_set *set;
struct task_struct *task;
struct pfm_smpl_fmt *fmt;
u64 now;
int state;
*release_info = 0;
now = sched_clock();
state = ctx->state;
set = ctx->active_set;
/*
* context must be attached (zombie cannot happen)
*/
if (state == PFM_CTX_UNLOADED)
return -EINVAL;
task = ctx->task;
fmt = ctx->smpl_fmt;
PFM_DBG("ctx_task=[%d] ctx_state=%d is_system=%d",
task ? task->pid : -1,
state,
!task);
/*
* this happens for system-wide context
*/
if (task == NULL)
task = current;
/*
* compute elapsed time
*
* unless masked, compute elapsed duration, stop timeout
*/
if (task == current && state == PFM_CTX_LOADED) {
/*
* timeout cancel must be deferred until context is
* unlocked to avoid race with pfm_handle_switch_timeout()
*/
if (set->flags & PFM_SETFL_TIME_SWITCH)
*release_info |= 0x2;
set->duration += now - set->duration_start;
}
pfm_arch_stop(task, ctx);
ctx->flags.started = 0;
if (fmt && fmt->fmt_stop)
(*fmt->fmt_stop)(ctx);
/*
* starting now, in-flight PMU interrupt for this context
* are treated as spurious
*/
return 0;
}