|
Packit |
577717 |
/* $Id: virtual.c,v 1.117 2007/10/06 13:02:07 mikpe Exp $
|
|
Packit |
577717 |
* Virtual per-process performance counters.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Copyright (C) 1999-2007 Mikael Pettersson
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
#include <linux/version.h>
|
|
Packit |
577717 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
|
|
Packit |
577717 |
#include <linux/config.h>
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
#include <linux/init.h>
|
|
Packit |
577717 |
#include <linux/compiler.h> /* for unlikely() in 2.4.18 and older */
|
|
Packit |
577717 |
#include <linux/kernel.h>
|
|
Packit |
577717 |
#include <linux/mm.h>
|
|
Packit |
577717 |
#include <linux/ptrace.h>
|
|
Packit |
577717 |
#include <linux/fs.h>
|
|
Packit |
577717 |
#include <linux/file.h>
|
|
Packit |
577717 |
#include <linux/perfctr.h>
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#include <asm/io.h>
|
|
Packit |
577717 |
#include <asm/uaccess.h>
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#include "cpumask.h"
|
|
Packit |
577717 |
#include "virtual.h"
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* Data types and macros. *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
struct vperfctr {
|
|
Packit |
577717 |
/* User-visible fields: (must be first for mmap()) */
|
|
Packit |
577717 |
struct perfctr_cpu_state cpu_state;
|
|
Packit |
577717 |
/* Kernel-private fields: */
|
|
Packit |
577717 |
int si_signo;
|
|
Packit |
577717 |
atomic_t count;
|
|
Packit |
577717 |
spinlock_t owner_lock;
|
|
Packit |
577717 |
struct task_struct *owner;
|
|
Packit |
577717 |
/* sampling_timer and bad_cpus_allowed are frequently
|
|
Packit |
577717 |
accessed, so they get to share a cache line */
|
|
Packit |
577717 |
unsigned int sampling_timer ____cacheline_aligned;
|
|
Packit |
577717 |
#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK
|
|
Packit |
577717 |
atomic_t bad_cpus_allowed;
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
unsigned int preserve;
|
|
Packit |
577717 |
unsigned int resume_cstatus;
|
|
Packit |
577717 |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit |
577717 |
unsigned int ireload_needed; /* only valid if resume_cstatus != 0 */
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
/* children_lock protects inheritance_id and children,
|
|
Packit |
577717 |
when parent is not the one doing release_task() */
|
|
Packit |
577717 |
spinlock_t children_lock;
|
|
Packit |
577717 |
unsigned long long inheritance_id;
|
|
Packit |
577717 |
struct perfctr_sum_ctrs children;
|
|
Packit |
577717 |
/* schedule_work() data for when an operation cannot be
|
|
Packit |
577717 |
done in the current context due to locking rules */
|
|
Packit |
577717 |
struct work_struct work;
|
|
Packit |
577717 |
struct task_struct *parent_tsk;
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
#define IS_RUNNING(perfctr) perfctr_cstatus_enabled((perfctr)->cpu_state.user.cstatus)
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void vperfctr_ihandler(unsigned long pc);
|
|
Packit |
577717 |
static void vperfctr_handle_overflow(struct task_struct*, struct vperfctr*);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline void vperfctr_set_ihandler(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
perfctr_cpu_set_ihandler(vperfctr_ihandler);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
static inline void vperfctr_set_ihandler(void) { }
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline void vperfctr_init_bad_cpus_allowed(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
atomic_set(&perfctr->bad_cpus_allowed, 0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#else /* !CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK */
|
|
Packit |
577717 |
static inline void vperfctr_init_bad_cpus_allowed(struct vperfctr *perfctr) { }
|
|
Packit |
577717 |
#endif /* !CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* Resource management. *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* XXX: perhaps relax this to number of _live_ perfctrs */
|
|
Packit |
577717 |
static DEFINE_MUTEX(nrctrs_mutex);
|
|
Packit |
577717 |
static int nrctrs;
|
|
Packit |
577717 |
static const char this_service[] = __FILE__;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int inc_nrctrs(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
const char *other;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
other = NULL;
|
|
Packit |
577717 |
mutex_lock(&nrctrs_mutex);
|
|
Packit |
577717 |
if (++nrctrs == 1) {
|
|
Packit |
577717 |
other = perfctr_cpu_reserve(this_service);
|
|
Packit |
577717 |
if (other)
|
|
Packit |
577717 |
nrctrs = 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
mutex_unlock(&nrctrs_mutex);
|
|
Packit |
577717 |
if (other) {
|
|
Packit |
577717 |
printk(KERN_ERR __FILE__
|
|
Packit |
577717 |
": cannot operate, perfctr hardware taken by '%s'\n",
|
|
Packit |
577717 |
other);
|
|
Packit |
577717 |
return -EBUSY;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
vperfctr_set_ihandler();
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void dec_nrctrs(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
mutex_lock(&nrctrs_mutex);
|
|
Packit |
577717 |
if (--nrctrs == 0)
|
|
Packit |
577717 |
perfctr_cpu_release(this_service);
|
|
Packit |
577717 |
mutex_unlock(&nrctrs_mutex);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Allocate a `struct vperfctr'. Claim and reserve
|
|
Packit |
577717 |
an entire page so that it can be mmap():ed. */
|
|
Packit |
577717 |
static struct vperfctr *vperfctr_alloc(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned long page;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (inc_nrctrs() != 0)
|
|
Packit |
577717 |
return ERR_PTR(-EBUSY);
|
|
Packit |
577717 |
page = get_zeroed_page(GFP_KERNEL);
|
|
Packit |
577717 |
if (!page) {
|
|
Packit |
577717 |
dec_nrctrs();
|
|
Packit |
577717 |
return ERR_PTR(-ENOMEM);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
SetPageReserved(virt_to_page(page));
|
|
Packit |
577717 |
return (struct vperfctr*) page;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void vperfctr_free(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
ClearPageReserved(virt_to_page(perfctr));
|
|
Packit |
577717 |
free_page((unsigned long)perfctr);
|
|
Packit |
577717 |
dec_nrctrs();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static struct vperfctr *get_empty_vperfctr(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct vperfctr *perfctr = vperfctr_alloc();
|
|
Packit |
577717 |
if (!IS_ERR(perfctr)) {
|
|
Packit |
577717 |
atomic_set(&perfctr->count, 1);
|
|
Packit |
577717 |
vperfctr_init_bad_cpus_allowed(perfctr);
|
|
Packit |
577717 |
spin_lock_init(&perfctr->owner_lock);
|
|
Packit |
577717 |
spin_lock_init(&perfctr->children_lock);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return perfctr;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void put_vperfctr(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (atomic_dec_and_test(&perfctr->count))
|
|
Packit |
577717 |
vperfctr_free(perfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
Packit |
577717 |
static void scheduled_vperfctr_free(struct work_struct *work)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct vperfctr *perfctr = container_of(work, struct vperfctr, work);
|
|
Packit |
577717 |
vperfctr_free(perfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
static void scheduled_vperfctr_free(void *data)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
vperfctr_free((struct vperfctr*)data);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void schedule_put_vperfctr(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (!atomic_dec_and_test(&perfctr->count))
|
|
Packit |
577717 |
return;
|
|
Packit |
577717 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
Packit |
577717 |
INIT_WORK(&perfctr->work, scheduled_vperfctr_free);
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
INIT_WORK(&perfctr->work, scheduled_vperfctr_free, perfctr);
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
schedule_work(&perfctr->work);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static unsigned long long new_inheritance_id(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
static spinlock_t lock = SPIN_LOCK_UNLOCKED;
|
|
Packit |
577717 |
static unsigned long long counter;
|
|
Packit |
577717 |
unsigned long long id;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
spin_lock(&lock);
|
|
Packit |
577717 |
id = ++counter;
|
|
Packit |
577717 |
spin_unlock(&lock);
|
|
Packit |
577717 |
return id;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* Basic counter operations. *
|
|
Packit |
577717 |
* These must all be called by the owner process only. *
|
|
Packit |
577717 |
* These must all be called with preemption disabled. *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* PRE: IS_RUNNING(perfctr)
|
|
Packit |
577717 |
* Suspend the counters.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
static inline void vperfctr_suspend(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
perfctr_cpu_suspend(&perfctr->cpu_state);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline void vperfctr_reset_sampling_timer(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
/* XXX: base the value on perfctr_info.cpu_khz instead! */
|
|
Packit |
577717 |
perfctr->sampling_timer = HZ/2;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* PRE: perfctr == current->thread.perfctr && IS_RUNNING(perfctr)
|
|
Packit |
577717 |
* Restart the counters.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
static inline void vperfctr_resume(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
perfctr_cpu_resume(&perfctr->cpu_state);
|
|
Packit |
577717 |
vperfctr_reset_sampling_timer(perfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline void vperfctr_resume_with_overflow_check(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit |
577717 |
if (perfctr_cpu_has_pending_interrupt(&perfctr->cpu_state)) {
|
|
Packit |
577717 |
vperfctr_handle_overflow(current, perfctr);
|
|
Packit |
577717 |
return;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
vperfctr_resume(perfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Sample the counters but do not suspend them. */
|
|
Packit |
577717 |
static void vperfctr_sample(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (IS_RUNNING(perfctr)) {
|
|
Packit |
577717 |
perfctr_cpu_sample(&perfctr->cpu_state);
|
|
Packit |
577717 |
vperfctr_reset_sampling_timer(perfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit |
577717 |
/* vperfctr interrupt handler (XXX: add buffering support) */
|
|
Packit |
577717 |
/* PREEMPT note: called in IRQ context with preemption disabled. */
|
|
Packit |
577717 |
static void vperfctr_ihandler(unsigned long pc)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct task_struct *tsk = current;
|
|
Packit |
577717 |
struct vperfctr *perfctr;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr = tsk->thread.perfctr;
|
|
Packit |
577717 |
if (!perfctr) {
|
|
Packit |
577717 |
printk(KERN_ERR "%s: BUG! pid %d has no vperfctr\n",
|
|
Packit |
577717 |
__FUNCTION__, tsk->pid);
|
|
Packit |
577717 |
return;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if (!perfctr_cstatus_has_ictrs(perfctr->cpu_state.user.cstatus)) {
|
|
Packit |
577717 |
printk(KERN_ERR "%s: BUG! vperfctr has cstatus %#x (pid %d, comm %s)\n",
|
|
Packit |
577717 |
__FUNCTION__, perfctr->cpu_state.user.cstatus, tsk->pid, tsk->comm);
|
|
Packit |
577717 |
return;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
vperfctr_suspend(perfctr);
|
|
Packit |
577717 |
vperfctr_handle_overflow(tsk, perfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void vperfctr_handle_overflow(struct task_struct *tsk,
|
|
Packit |
577717 |
struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int pmc_mask;
|
|
Packit |
577717 |
siginfo_t si;
|
|
Packit |
577717 |
sigset_t old_blocked;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
pmc_mask = perfctr_cpu_identify_overflow(&perfctr->cpu_state);
|
|
Packit |
577717 |
if (!pmc_mask) {
|
|
Packit |
577717 |
#ifdef CONFIG_PPC64
|
|
Packit |
577717 |
/* On some hardware (ppc64, in particular) it's
|
|
Packit |
577717 |
* impossible to control interrupts finely enough to
|
|
Packit |
577717 |
* eliminate overflows on counters we don't care
|
|
Packit |
577717 |
* about. So in this case just restart the counters
|
|
Packit |
577717 |
* and keep going. */
|
|
Packit |
577717 |
vperfctr_resume(perfctr);
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
printk(KERN_ERR "%s: BUG! pid %d has unidentifiable overflow source\n",
|
|
Packit |
577717 |
__FUNCTION__, tsk->pid);
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
return;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
perfctr->ireload_needed = 1;
|
|
Packit |
577717 |
/* suspend a-mode and i-mode PMCs, leaving only TSC on */
|
|
Packit |
577717 |
/* XXX: some people also want to suspend the TSC */
|
|
Packit |
577717 |
perfctr->resume_cstatus = perfctr->cpu_state.user.cstatus;
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(perfctr->resume_cstatus)) {
|
|
Packit |
577717 |
perfctr->cpu_state.user.cstatus = perfctr_mk_cstatus(1, 0, 0);
|
|
Packit |
577717 |
vperfctr_resume(perfctr);
|
|
Packit |
577717 |
} else
|
|
Packit |
577717 |
perfctr->cpu_state.user.cstatus = 0;
|
|
Packit |
577717 |
si.si_signo = perfctr->si_signo;
|
|
Packit |
577717 |
si.si_errno = 0;
|
|
Packit |
577717 |
si.si_code = SI_PMC_OVF;
|
|
Packit |
577717 |
si.si_pmc_ovf_mask = pmc_mask;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* deliver signal without waking up the receiver */
|
|
Packit |
577717 |
spin_lock_irq(&tsk->sighand->siglock);
|
|
Packit |
577717 |
old_blocked = tsk->blocked;
|
|
Packit |
577717 |
sigaddset(&tsk->blocked, si.si_signo);
|
|
Packit |
577717 |
spin_unlock_irq(&tsk->sighand->siglock);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (!send_sig_info(si.si_signo, &si, tsk))
|
|
Packit |
577717 |
send_sig(si.si_signo, tsk, 1);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
spin_lock_irq(&tsk->sighand->siglock);
|
|
Packit |
577717 |
tsk->blocked = old_blocked;
|
|
Packit |
577717 |
recalc_sigpending();
|
|
Packit |
577717 |
spin_unlock_irq(&tsk->sighand->siglock);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* Process management operations. *
|
|
Packit |
577717 |
* These must all, with the exception of vperfctr_unlink() *
|
|
Packit |
577717 |
* and __vperfctr_set_cpus_allowed(), be called by the owner *
|
|
Packit |
577717 |
* process only. *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* do_fork() -> copy_process() -> copy_thread() -> __vperfctr_copy().
|
|
Packit |
577717 |
* Inherit the parent's perfctr settings to the child.
|
|
Packit |
577717 |
* PREEMPT note: do_fork() etc do not run with preemption disabled.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
void __vperfctr_copy(struct task_struct *child_tsk, struct pt_regs *regs)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct vperfctr *parent_perfctr;
|
|
Packit |
577717 |
struct vperfctr *child_perfctr;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Do not inherit perfctr settings to kernel-generated
|
|
Packit |
577717 |
threads, like those created by kmod. */
|
|
Packit |
577717 |
child_perfctr = NULL;
|
|
Packit |
577717 |
if (!user_mode(regs))
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Allocation may sleep. Do it before the critical region. */
|
|
Packit |
577717 |
child_perfctr = get_empty_vperfctr();
|
|
Packit |
577717 |
if (IS_ERR(child_perfctr)) {
|
|
Packit |
577717 |
child_perfctr = NULL;
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Although we're executing in the parent, if it is scheduled
|
|
Packit |
577717 |
then a remote monitor may attach and change the perfctr
|
|
Packit |
577717 |
pointer or the object it points to. This may already have
|
|
Packit |
577717 |
occurred when we get here, so the old copy of the pointer
|
|
Packit |
577717 |
in the child cannot be trusted. */
|
|
Packit |
577717 |
preempt_disable();
|
|
Packit |
577717 |
parent_perfctr = current->thread.perfctr;
|
|
Packit |
577717 |
if (parent_perfctr) {
|
|
Packit |
577717 |
child_perfctr->cpu_state.control = parent_perfctr->cpu_state.control;
|
|
Packit |
577717 |
child_perfctr->si_signo = parent_perfctr->si_signo;
|
|
Packit |
577717 |
child_perfctr->inheritance_id = parent_perfctr->inheritance_id;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
preempt_enable();
|
|
Packit |
577717 |
if (!parent_perfctr) {
|
|
Packit |
577717 |
put_vperfctr(child_perfctr);
|
|
Packit |
577717 |
child_perfctr = NULL;
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
(void)perfctr_cpu_update_control(&child_perfctr->cpu_state, 0);
|
|
Packit |
577717 |
child_perfctr->owner = child_tsk;
|
|
Packit |
577717 |
out:
|
|
Packit |
577717 |
child_tsk->thread.perfctr = child_perfctr;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Called from exit_thread() or do_vperfctr_unlink().
|
|
Packit |
577717 |
* If the counters are running, stop them and sample their final values.
|
|
Packit |
577717 |
* Mark the vperfctr object as dead.
|
|
Packit |
577717 |
* Optionally detach the vperfctr object from its owner task.
|
|
Packit |
577717 |
* PREEMPT note: exit_thread() does not run with preemption disabled.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
static void vperfctr_unlink(struct task_struct *owner, struct vperfctr *perfctr, int do_unlink)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
/* this synchronises with sys_vperfctr() */
|
|
Packit |
577717 |
spin_lock(&perfctr->owner_lock);
|
|
Packit |
577717 |
perfctr->owner = NULL;
|
|
Packit |
577717 |
spin_unlock(&perfctr->owner_lock);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* perfctr suspend+detach must be atomic wrt process suspend */
|
|
Packit |
577717 |
/* this also synchronises with perfctr_set_cpus_allowed() */
|
|
Packit |
577717 |
task_lock(owner);
|
|
Packit |
577717 |
if (IS_RUNNING(perfctr) && owner == current)
|
|
Packit |
577717 |
vperfctr_suspend(perfctr);
|
|
Packit |
577717 |
if (do_unlink)
|
|
Packit |
577717 |
owner->thread.perfctr = NULL;
|
|
Packit |
577717 |
task_unlock(owner);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr->cpu_state.user.cstatus = 0;
|
|
Packit |
577717 |
perfctr->resume_cstatus = 0;
|
|
Packit |
577717 |
if (do_unlink)
|
|
Packit |
577717 |
put_vperfctr(perfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void __vperfctr_exit(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
vperfctr_unlink(current, perfctr, 0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* release_task() -> perfctr_release_task() -> __vperfctr_release().
|
|
Packit |
577717 |
* A task is being released. If it inherited its perfctr settings
|
|
Packit |
577717 |
* from its parent, then merge its final counts back into the parent.
|
|
Packit |
577717 |
* Then unlink the child's perfctr.
|
|
Packit |
577717 |
* PRE: caller has write_lock_irq(&tasklist_lock).
|
|
Packit |
577717 |
* PREEMPT note: preemption is disabled due to tasklist_lock.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* When current == parent_tsk, the child's counts can be merged
|
|
Packit |
577717 |
* into the parent's immediately. This is the common case.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* When current != parent_tsk, the parent must be task_lock()ed
|
|
Packit |
577717 |
* before its perfctr state can be accessed. task_lock() is illegal
|
|
Packit |
577717 |
* here due to the write_lock_irq(&tasklist_lock) in release_task(),
|
|
Packit |
577717 |
* so the operation is done via schedule_work().
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
static void do_vperfctr_release(struct vperfctr *child_perfctr, struct task_struct *parent_tsk)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct vperfctr *parent_perfctr;
|
|
Packit |
577717 |
unsigned int cstatus, nrctrs, i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
parent_perfctr = parent_tsk->thread.perfctr;
|
|
Packit |
577717 |
if (parent_perfctr && child_perfctr) {
|
|
Packit |
577717 |
spin_lock(&parent_perfctr->children_lock);
|
|
Packit |
577717 |
if (parent_perfctr->inheritance_id == child_perfctr->inheritance_id) {
|
|
Packit |
577717 |
cstatus = parent_perfctr->cpu_state.user.cstatus;
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(cstatus))
|
|
Packit |
577717 |
parent_perfctr->children.tsc +=
|
|
Packit |
577717 |
child_perfctr->cpu_state.user.tsc_sum +
|
|
Packit |
577717 |
child_perfctr->children.tsc;
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nrctrs(cstatus);
|
|
Packit |
577717 |
for(i = 0; i < nrctrs; ++i)
|
|
Packit |
577717 |
parent_perfctr->children.pmc[i] +=
|
|
Packit |
577717 |
child_perfctr->cpu_state.user.pmc[i].sum +
|
|
Packit |
577717 |
child_perfctr->children.pmc[i];
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
spin_unlock(&parent_perfctr->children_lock);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
schedule_put_vperfctr(child_perfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void do_scheduled_release(struct vperfctr *child_perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct task_struct *parent_tsk = child_perfctr->parent_tsk;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
task_lock(parent_tsk);
|
|
Packit |
577717 |
do_vperfctr_release(child_perfctr, parent_tsk);
|
|
Packit |
577717 |
task_unlock(parent_tsk);
|
|
Packit |
577717 |
put_task_struct(parent_tsk);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
Packit |
577717 |
static void scheduled_release(struct work_struct *work)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct vperfctr *perfctr = container_of(work, struct vperfctr, work);
|
|
Packit |
577717 |
do_scheduled_release(perfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
static void scheduled_release(void *data)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
do_scheduled_release((struct vperfctr*)data);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void __vperfctr_release(struct task_struct *child_tsk)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct task_struct *parent_tsk = child_tsk->parent;
|
|
Packit |
577717 |
struct vperfctr *child_perfctr = child_tsk->thread.perfctr;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
child_tsk->thread.perfctr = NULL;
|
|
Packit |
577717 |
if (parent_tsk == current)
|
|
Packit |
577717 |
do_vperfctr_release(child_perfctr, parent_tsk);
|
|
Packit |
577717 |
else {
|
|
Packit |
577717 |
get_task_struct(parent_tsk);
|
|
Packit |
577717 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
Packit |
577717 |
INIT_WORK(&child_perfctr->work, scheduled_release);
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
INIT_WORK(&child_perfctr->work, scheduled_release, child_perfctr);
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
child_perfctr->parent_tsk = parent_tsk;
|
|
Packit |
577717 |
schedule_work(&child_perfctr->work);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* schedule() --> switch_to() --> .. --> __vperfctr_suspend().
|
|
Packit |
577717 |
* If the counters are running, suspend them.
|
|
Packit |
577717 |
* PREEMPT note: switch_to() runs with preemption disabled.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
void __vperfctr_suspend(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (IS_RUNNING(perfctr))
|
|
Packit |
577717 |
vperfctr_suspend(perfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* schedule() --> switch_to() --> .. --> __vperfctr_resume().
|
|
Packit |
577717 |
* PRE: perfctr == current->thread.perfctr
|
|
Packit |
577717 |
* If the counters are runnable, resume them.
|
|
Packit |
577717 |
* PREEMPT note: switch_to() runs with preemption disabled.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
void __vperfctr_resume(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (IS_RUNNING(perfctr)) {
|
|
Packit |
577717 |
#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK
|
|
Packit |
577717 |
if (unlikely(atomic_read(&perfctr->bad_cpus_allowed)) &&
|
|
Packit |
577717 |
perfctr_cstatus_nrctrs(perfctr->cpu_state.user.cstatus)) {
|
|
Packit |
577717 |
perfctr->cpu_state.user.cstatus = 0;
|
|
Packit |
577717 |
perfctr->resume_cstatus = 0;
|
|
Packit |
577717 |
BUG_ON(current->state != TASK_RUNNING);
|
|
Packit |
577717 |
send_sig(SIGILL, current, 1);
|
|
Packit |
577717 |
return;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
vperfctr_resume_with_overflow_check(perfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Called from update_one_process() [triggered by timer interrupt].
|
|
Packit |
577717 |
* PRE: perfctr == current->thread.perfctr.
|
|
Packit |
577717 |
* Sample the counters but do not suspend them.
|
|
Packit |
577717 |
* Needed to avoid precision loss due to multiple counter
|
|
Packit |
577717 |
* wraparounds between resume/suspend for CPU-bound processes.
|
|
Packit |
577717 |
* PREEMPT note: called in IRQ context with preemption disabled.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
void __vperfctr_sample(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (--perfctr->sampling_timer == 0)
|
|
Packit |
577717 |
vperfctr_sample(perfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK
|
|
Packit |
577717 |
/* Called from set_cpus_allowed().
|
|
Packit |
577717 |
* PRE: current holds task_lock(owner)
|
|
Packit |
577717 |
* PRE: owner->thread.perfctr == perfctr
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
void __vperfctr_set_cpus_allowed(struct task_struct *owner,
|
|
Packit |
577717 |
struct vperfctr *perfctr,
|
|
Packit |
577717 |
cpumask_t new_mask)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (cpus_intersects(new_mask, perfctr_cpus_forbidden_mask)) {
|
|
Packit |
577717 |
atomic_set(&perfctr->bad_cpus_allowed, 1);
|
|
Packit |
577717 |
if (printk_ratelimit())
|
|
Packit |
577717 |
printk(KERN_WARNING "perfctr: process %d (comm %s) issued unsafe"
|
|
Packit |
577717 |
" set_cpus_allowed() on process %d (comm %s)\n",
|
|
Packit |
577717 |
current->pid, current->comm, owner->pid, owner->comm);
|
|
Packit |
577717 |
} else
|
|
Packit |
577717 |
atomic_set(&perfctr->bad_cpus_allowed, 0);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* Virtual perfctr system calls implementation. *
|
|
Packit |
577717 |
* These can be called by the owner process (tsk == current), *
|
|
Packit |
577717 |
* a monitor process which has the owner under ptrace ATTACH *
|
|
Packit |
577717 |
* control (tsk && tsk != current), or anyone with a handle to *
|
|
Packit |
577717 |
* an unlinked perfctr (!tsk). *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int do_vperfctr_write(struct vperfctr *perfctr,
|
|
Packit |
577717 |
unsigned int domain,
|
|
Packit |
577717 |
const void __user *srcp,
|
|
Packit |
577717 |
unsigned int srcbytes,
|
|
Packit |
577717 |
struct task_struct *tsk)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
void *tmp;
|
|
Packit |
577717 |
int err;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (!tsk)
|
|
Packit |
577717 |
return -ESRCH; /* attempt to update unlinked perfctr */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (srcbytes > PAGE_SIZE) /* primitive sanity check */
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
tmp = kmalloc(srcbytes, GFP_USER);
|
|
Packit |
577717 |
if (!tmp)
|
|
Packit |
577717 |
return -ENOMEM;
|
|
Packit |
577717 |
err = -EFAULT;
|
|
Packit |
577717 |
if (copy_from_user(tmp, srcp, srcbytes))
|
|
Packit |
577717 |
goto out_kfree;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* PREEMPT note: preemption is disabled over the entire
|
|
Packit |
577717 |
region since we're updating an active perfctr. */
|
|
Packit |
577717 |
preempt_disable();
|
|
Packit |
577717 |
if (IS_RUNNING(perfctr)) {
|
|
Packit |
577717 |
if (tsk == current)
|
|
Packit |
577717 |
vperfctr_suspend(perfctr);
|
|
Packit |
577717 |
perfctr->cpu_state.user.cstatus = 0;
|
|
Packit |
577717 |
perfctr->resume_cstatus = 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
switch (domain) {
|
|
Packit |
577717 |
case VPERFCTR_DOMAIN_CONTROL: {
|
|
Packit |
577717 |
struct vperfctr_control control;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
err = -EINVAL;
|
|
Packit |
577717 |
if (srcbytes > sizeof(control))
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
control.si_signo = perfctr->si_signo;
|
|
Packit |
577717 |
control.preserve = perfctr->preserve;
|
|
Packit |
577717 |
memcpy(&control, tmp, srcbytes);
|
|
Packit |
577717 |
/* XXX: validate si_signo? */
|
|
Packit |
577717 |
perfctr->si_signo = control.si_signo;
|
|
Packit |
577717 |
perfctr->preserve = control.preserve;
|
|
Packit |
577717 |
err = 0;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
case PERFCTR_DOMAIN_CPU_CONTROL:
|
|
Packit |
577717 |
err = -EINVAL;
|
|
Packit |
577717 |
if (srcbytes > sizeof(perfctr->cpu_state.control.header))
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
memcpy(&perfctr->cpu_state.control.header, tmp, srcbytes);
|
|
Packit |
577717 |
err = 0;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case PERFCTR_DOMAIN_CPU_MAP:
|
|
Packit |
577717 |
err = -EINVAL;
|
|
Packit |
577717 |
if (srcbytes > sizeof(perfctr->cpu_state.control.pmc_map))
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
memcpy(perfctr->cpu_state.control.pmc_map, tmp, srcbytes);
|
|
Packit |
577717 |
err = 0;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
err = perfctr_cpu_control_write(&perfctr->cpu_state.control,
|
|
Packit |
577717 |
domain, tmp, srcbytes);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
preempt_enable();
|
|
Packit |
577717 |
out_kfree:
|
|
Packit |
577717 |
kfree(tmp);
|
|
Packit |
577717 |
return err;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int vperfctr_enable_control(struct vperfctr *perfctr, struct task_struct *tsk)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int err;
|
|
Packit |
577717 |
unsigned int next_cstatus;
|
|
Packit |
577717 |
unsigned int nrctrs, i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (perfctr->cpu_state.control.header.nractrs ||
|
|
Packit |
577717 |
perfctr->cpu_state.control.header.nrictrs) {
|
|
Packit |
577717 |
cpumask_t old_mask, new_mask;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
old_mask = tsk->cpus_allowed;
|
|
Packit |
577717 |
cpus_andnot(new_mask, old_mask, perfctr_cpus_forbidden_mask);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (cpus_empty(new_mask))
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
if (!cpus_equal(new_mask, old_mask))
|
|
Packit |
577717 |
set_cpus_allowed(tsk, new_mask);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr->cpu_state.user.cstatus = 0;
|
|
Packit |
577717 |
perfctr->resume_cstatus = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* remote access note: perfctr_cpu_update_control() is ok */
|
|
Packit |
577717 |
err = perfctr_cpu_update_control(&perfctr->cpu_state, 0);
|
|
Packit |
577717 |
if (err < 0)
|
|
Packit |
577717 |
return err;
|
|
Packit |
577717 |
next_cstatus = perfctr->cpu_state.user.cstatus;
|
|
Packit |
577717 |
if (!perfctr_cstatus_enabled(next_cstatus))
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (!perfctr_cstatus_has_tsc(next_cstatus))
|
|
Packit |
577717 |
perfctr->cpu_state.user.tsc_sum = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nrctrs(next_cstatus);
|
|
Packit |
577717 |
for(i = 0; i < nrctrs; ++i)
|
|
Packit |
577717 |
if (!(perfctr->preserve & (1<
|
|
Packit |
577717 |
perfctr->cpu_state.user.pmc[i].sum = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
spin_lock(&perfctr->children_lock);
|
|
Packit |
577717 |
perfctr->inheritance_id = new_inheritance_id();
|
|
Packit |
577717 |
memset(&perfctr->children, 0, sizeof perfctr->children);
|
|
Packit |
577717 |
spin_unlock(&perfctr->children_lock);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static inline void vperfctr_ireload(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit |
577717 |
if (perfctr->ireload_needed) {
|
|
Packit |
577717 |
perfctr->ireload_needed = 0;
|
|
Packit |
577717 |
/* remote access note: perfctr_cpu_ireload() is ok */
|
|
Packit |
577717 |
perfctr_cpu_ireload(&perfctr->cpu_state);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int do_vperfctr_resume(struct vperfctr *perfctr, struct task_struct *tsk)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int resume_cstatus;
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (!tsk)
|
|
Packit |
577717 |
return -ESRCH; /* attempt to update unlinked perfctr */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* PREEMPT note: preemption is disabled over the entire
|
|
Packit |
577717 |
region because we're updating an active perfctr. */
|
|
Packit |
577717 |
preempt_disable();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (IS_RUNNING(perfctr) && tsk == current)
|
|
Packit |
577717 |
vperfctr_suspend(perfctr);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
resume_cstatus = perfctr->resume_cstatus;
|
|
Packit |
577717 |
if (perfctr_cstatus_enabled(resume_cstatus)) {
|
|
Packit |
577717 |
perfctr->cpu_state.user.cstatus = resume_cstatus;
|
|
Packit |
577717 |
perfctr->resume_cstatus = 0;
|
|
Packit |
577717 |
vperfctr_ireload(perfctr);
|
|
Packit |
577717 |
ret = 0;
|
|
Packit |
577717 |
} else {
|
|
Packit |
577717 |
ret = vperfctr_enable_control(perfctr, tsk);
|
|
Packit |
577717 |
resume_cstatus = perfctr->cpu_state.user.cstatus;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (ret >= 0 && perfctr_cstatus_enabled(resume_cstatus) && tsk == current)
|
|
Packit |
577717 |
vperfctr_resume(perfctr);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
preempt_enable();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return ret;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int do_vperfctr_suspend(struct vperfctr *perfctr, struct task_struct *tsk)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (!tsk)
|
|
Packit |
577717 |
return -ESRCH; /* attempt to update unlinked perfctr */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* PREEMPT note: preemption is disabled over the entire
|
|
Packit |
577717 |
region since we're updating an active perfctr. */
|
|
Packit |
577717 |
preempt_disable();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (IS_RUNNING(perfctr)) {
|
|
Packit |
577717 |
if (tsk == current)
|
|
Packit |
577717 |
vperfctr_suspend(perfctr);
|
|
Packit |
577717 |
perfctr->resume_cstatus = perfctr->cpu_state.user.cstatus;
|
|
Packit |
577717 |
perfctr->cpu_state.user.cstatus = 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
preempt_enable();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int do_vperfctr_unlink(struct vperfctr *perfctr, struct task_struct *tsk)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (tsk)
|
|
Packit |
577717 |
vperfctr_unlink(tsk, perfctr, 1);
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int do_vperfctr_clear(struct vperfctr *perfctr, struct task_struct *tsk)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (!tsk)
|
|
Packit |
577717 |
return -ESRCH; /* attempt to update unlinked perfctr */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* PREEMPT note: preemption is disabled over the entire
|
|
Packit |
577717 |
region because we're updating an active perfctr. */
|
|
Packit |
577717 |
preempt_disable();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (IS_RUNNING(perfctr) && tsk == current)
|
|
Packit |
577717 |
vperfctr_suspend(perfctr);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
memset(&perfctr->cpu_state, 0, sizeof perfctr->cpu_state);
|
|
Packit |
577717 |
perfctr->resume_cstatus = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
spin_lock(&perfctr->children_lock);
|
|
Packit |
577717 |
perfctr->inheritance_id = 0;
|
|
Packit |
577717 |
memset(&perfctr->children, 0, sizeof perfctr->children);
|
|
Packit |
577717 |
spin_unlock(&perfctr->children_lock);
|
|
Packit |
577717 |
|
|
Packit |
577717 |
preempt_enable();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int do_vperfctr_control(struct vperfctr *perfctr,
|
|
Packit |
577717 |
unsigned int cmd,
|
|
Packit |
577717 |
struct task_struct *tsk)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
switch (cmd) {
|
|
Packit |
577717 |
case VPERFCTR_CONTROL_UNLINK:
|
|
Packit |
577717 |
return do_vperfctr_unlink(perfctr, tsk);
|
|
Packit |
577717 |
case VPERFCTR_CONTROL_SUSPEND:
|
|
Packit |
577717 |
return do_vperfctr_suspend(perfctr, tsk);
|
|
Packit |
577717 |
case VPERFCTR_CONTROL_RESUME:
|
|
Packit |
577717 |
return do_vperfctr_resume(perfctr, tsk);
|
|
Packit |
577717 |
case VPERFCTR_CONTROL_CLEAR:
|
|
Packit |
577717 |
return do_vperfctr_clear(perfctr, tsk);
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int do_vperfctr_read(struct vperfctr *perfctr,
|
|
Packit |
577717 |
unsigned int domain,
|
|
Packit |
577717 |
void __user *dstp,
|
|
Packit |
577717 |
unsigned int dstbytes,
|
|
Packit |
577717 |
struct task_struct *tsk)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
union {
|
|
Packit |
577717 |
struct perfctr_sum_ctrs sum;
|
|
Packit |
577717 |
struct vperfctr_control control;
|
|
Packit |
577717 |
struct perfctr_sum_ctrs children;
|
|
Packit |
577717 |
} *tmp;
|
|
Packit |
577717 |
unsigned int tmpbytes;
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
tmpbytes = dstbytes;
|
|
Packit |
577717 |
if (tmpbytes > PAGE_SIZE) /* primitive sanity check */
|
|
Packit |
577717 |
return -EINVAL;
|
|
Packit |
577717 |
if (tmpbytes < sizeof(*tmp))
|
|
Packit |
577717 |
tmpbytes = sizeof(*tmp);
|
|
Packit |
577717 |
tmp = kmalloc(tmpbytes, GFP_USER);
|
|
Packit |
577717 |
if (!tmp)
|
|
Packit |
577717 |
return -ENOMEM;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* PREEMPT note: While we're reading our own control, another
|
|
Packit |
577717 |
process may ptrace ATTACH to us and update our control.
|
|
Packit |
577717 |
Disable preemption to ensure we get a consistent copy.
|
|
Packit |
577717 |
Not needed for other cases since the perfctr is either
|
|
Packit |
577717 |
unlinked or its owner is ptrace ATTACH suspended by us. */
|
|
Packit |
577717 |
if (tsk == current)
|
|
Packit |
577717 |
preempt_disable();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
switch (domain) {
|
|
Packit |
577717 |
case VPERFCTR_DOMAIN_SUM: {
|
|
Packit |
577717 |
int j;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
vperfctr_sample(perfctr);
|
|
Packit |
577717 |
tmp->sum.tsc = perfctr->cpu_state.user.tsc_sum;
|
|
Packit |
577717 |
for(j = 0; j < ARRAY_SIZE(tmp->sum.pmc); ++j)
|
|
Packit |
577717 |
tmp->sum.pmc[j] = perfctr->cpu_state.user.pmc[j].sum;
|
|
Packit |
577717 |
ret = sizeof(tmp->sum);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
case VPERFCTR_DOMAIN_CONTROL:
|
|
Packit |
577717 |
tmp->control.si_signo = perfctr->si_signo;
|
|
Packit |
577717 |
tmp->control.preserve = perfctr->preserve;
|
|
Packit |
577717 |
ret = sizeof(tmp->control);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case VPERFCTR_DOMAIN_CHILDREN:
|
|
Packit |
577717 |
if (tsk)
|
|
Packit |
577717 |
spin_lock(&perfctr->children_lock);
|
|
Packit |
577717 |
tmp->children = perfctr->children;
|
|
Packit |
577717 |
if (tsk)
|
|
Packit |
577717 |
spin_unlock(&perfctr->children_lock);
|
|
Packit |
577717 |
ret = sizeof(tmp->children);
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case PERFCTR_DOMAIN_CPU_CONTROL:
|
|
Packit |
577717 |
if (tmpbytes > sizeof(perfctr->cpu_state.control.header))
|
|
Packit |
577717 |
tmpbytes = sizeof(perfctr->cpu_state.control.header);
|
|
Packit |
577717 |
memcpy(tmp, &perfctr->cpu_state.control.header, tmpbytes);
|
|
Packit |
577717 |
ret = tmpbytes;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
case PERFCTR_DOMAIN_CPU_MAP:
|
|
Packit |
577717 |
if (tmpbytes > sizeof(perfctr->cpu_state.control.pmc_map))
|
|
Packit |
577717 |
tmpbytes = sizeof(perfctr->cpu_state.control.pmc_map);
|
|
Packit |
577717 |
memcpy(tmp, perfctr->cpu_state.control.pmc_map, tmpbytes);
|
|
Packit |
577717 |
ret = tmpbytes;
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
ret = -EFAULT;
|
|
Packit |
577717 |
if (copy_from_user(tmp, dstp, dstbytes) == 0)
|
|
Packit |
577717 |
ret = perfctr_cpu_control_read(&perfctr->cpu_state.control,
|
|
Packit |
577717 |
domain, tmp, dstbytes);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (tsk == current)
|
|
Packit |
577717 |
preempt_enable();
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (ret > 0) {
|
|
Packit |
577717 |
if (ret > dstbytes)
|
|
Packit |
577717 |
ret = dstbytes;
|
|
Packit |
577717 |
if (ret > 0 && copy_to_user(dstp, tmp, ret))
|
|
Packit |
577717 |
ret = -EFAULT;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
kfree(tmp);
|
|
Packit |
577717 |
return ret;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* Virtual perfctr file operations. *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int vperfctr_mmap(struct file *filp, struct vm_area_struct *vma)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct vperfctr *perfctr;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Only allow read-only mapping of first page. */
|
|
Packit |
577717 |
if ((vma->vm_end - vma->vm_start) != PAGE_SIZE ||
|
|
Packit |
577717 |
vma->vm_pgoff != 0 ||
|
|
Packit |
577717 |
(pgprot_val(vma->vm_page_prot) & _PAGE_RW) ||
|
|
Packit |
577717 |
(vma->vm_flags & (VM_WRITE | VM_MAYWRITE)))
|
|
Packit |
577717 |
return -EPERM;
|
|
Packit |
577717 |
perfctr = filp->private_data;
|
|
Packit |
577717 |
if (!perfctr)
|
|
Packit |
577717 |
return -EPERM;
|
|
Packit |
577717 |
return remap_pfn_range(vma, vma->vm_start,
|
|
Packit |
577717 |
virt_to_phys(perfctr) >> PAGE_SHIFT,
|
|
Packit |
577717 |
PAGE_SIZE, vma->vm_page_prot);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int vperfctr_release(struct inode *inode, struct file *filp)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct vperfctr *perfctr = filp->private_data;
|
|
Packit |
577717 |
filp->private_data = NULL;
|
|
Packit |
577717 |
if (perfctr)
|
|
Packit |
577717 |
put_vperfctr(perfctr);
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static struct file_operations vperfctr_file_ops = {
|
|
Packit |
577717 |
.mmap = vperfctr_mmap,
|
|
Packit |
577717 |
.release = vperfctr_release,
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* File system for virtual perfctrs. Based on pipefs. *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define VPERFCTRFS_MAGIC (('V'<<24)|('P'<<16)|('M'<<8)|('C'))
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* The code to set up a `struct file_system_type' for a pseudo fs
|
|
Packit |
577717 |
is unfortunately not the same in 2.4 and 2.6. */
|
|
Packit |
577717 |
#include <linux/mount.h> /* needed for 2.6, included by fs.h in 2.4 */
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
|
|
Packit |
577717 |
static int
|
|
Packit |
577717 |
vperfctrfs_get_sb(struct file_system_type *fs_type,
|
|
Packit |
577717 |
int flags, const char *dev_name, void *data,
|
|
Packit |
577717 |
struct vfsmount *mnt)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return get_sb_pseudo(fs_type, "vperfctr:", NULL, VPERFCTRFS_MAGIC, mnt);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
static struct super_block *
|
|
Packit |
577717 |
vperfctrfs_get_sb(struct file_system_type *fs_type,
|
|
Packit |
577717 |
int flags, const char *dev_name, void *data)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return get_sb_pseudo(fs_type, "vperfctr:", NULL, VPERFCTRFS_MAGIC);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static struct file_system_type vperfctrfs_type = {
|
|
Packit |
577717 |
.name = "vperfctrfs",
|
|
Packit |
577717 |
.get_sb = vperfctrfs_get_sb,
|
|
Packit |
577717 |
.kill_sb = kill_anon_super,
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* XXX: check if s/vperfctr_mnt/vperfctrfs_type.kern_mnt/ would work */
|
|
Packit |
577717 |
static struct vfsmount *vperfctr_mnt;
|
|
Packit |
577717 |
#define vperfctr_fs_init_done() (vperfctr_mnt != NULL)
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int __init vperfctrfs_init(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int err = register_filesystem(&vperfctrfs_type);
|
|
Packit |
577717 |
if (!err) {
|
|
Packit |
577717 |
vperfctr_mnt = kern_mount(&vperfctrfs_type);
|
|
Packit |
577717 |
if (!IS_ERR(vperfctr_mnt))
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
err = PTR_ERR(vperfctr_mnt);
|
|
Packit |
577717 |
unregister_filesystem(&vperfctrfs_type);
|
|
Packit |
577717 |
vperfctr_mnt = NULL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return err;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void __exit vperfctrfs_exit(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unregister_filesystem(&vperfctrfs_type);
|
|
Packit |
577717 |
mntput(vperfctr_mnt);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static struct inode *vperfctr_get_inode(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct inode *inode;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
inode = new_inode(vperfctr_mnt->mnt_sb);
|
|
Packit |
577717 |
if (!inode)
|
|
Packit |
577717 |
return NULL;
|
|
Packit |
577717 |
inode->i_fop = &vperfctr_file_ops;
|
|
Packit |
577717 |
inode->i_state = I_DIRTY;
|
|
Packit |
577717 |
inode->i_mode = S_IFCHR | S_IRUSR | S_IWUSR;
|
|
Packit |
577717 |
inode->i_uid = current->fsuid;
|
|
Packit |
577717 |
inode->i_gid = current->fsgid;
|
|
Packit |
577717 |
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
|
Packit |
577717 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) && !DONT_HAVE_i_blksize
|
|
Packit |
577717 |
inode->i_blksize = 0;
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
return inode;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int vperfctrfs_delete_dentry(struct dentry *dentry)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return 1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static struct dentry_operations vperfctrfs_dentry_operations = {
|
|
Packit |
577717 |
.d_delete = vperfctrfs_delete_dentry,
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static struct dentry *vperfctr_d_alloc_root(struct inode *inode)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct qstr this;
|
|
Packit |
577717 |
char name[32];
|
|
Packit |
577717 |
struct dentry *dentry;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
sprintf(name, "[%lu]", inode->i_ino);
|
|
Packit |
577717 |
this.name = name;
|
|
Packit |
577717 |
this.len = strlen(name);
|
|
Packit |
577717 |
this.hash = inode->i_ino; /* will go */
|
|
Packit |
577717 |
dentry = d_alloc(vperfctr_mnt->mnt_sb->s_root, &this;;
|
|
Packit |
577717 |
if (dentry) {
|
|
Packit |
577717 |
dentry->d_op = &vperfctrfs_dentry_operations;
|
|
Packit |
577717 |
d_add(dentry, inode);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return dentry;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static struct file *vperfctr_get_filp(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct file *filp;
|
|
Packit |
577717 |
struct inode *inode;
|
|
Packit |
577717 |
struct dentry *dentry;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
filp = get_empty_filp();
|
|
Packit |
577717 |
if (!filp)
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
inode = vperfctr_get_inode();
|
|
Packit |
577717 |
if (!inode)
|
|
Packit |
577717 |
goto out_filp;
|
|
Packit |
577717 |
dentry = vperfctr_d_alloc_root(inode);
|
|
Packit |
577717 |
if (!dentry)
|
|
Packit |
577717 |
goto out_inode;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
filp->f_vfsmnt = mntget(vperfctr_mnt);
|
|
Packit |
577717 |
filp->f_dentry = dentry;
|
|
Packit |
577717 |
filp->f_mapping = dentry->d_inode->i_mapping;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
filp->f_pos = 0;
|
|
Packit |
577717 |
filp->f_flags = 0;
|
|
Packit |
577717 |
filp->f_op = &vperfctr_file_ops; /* fops_get() if MODULE */
|
|
Packit |
577717 |
filp->f_mode = FMODE_READ;
|
|
Packit |
577717 |
filp->f_version = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
return filp;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
out_inode:
|
|
Packit |
577717 |
iput(inode);
|
|
Packit |
577717 |
out_filp:
|
|
Packit |
577717 |
put_filp(filp); /* doesn't run ->release() like fput() does */
|
|
Packit |
577717 |
out:
|
|
Packit |
577717 |
return NULL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* Virtual perfctr actual system calls. *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* tid is the actual task/thread id (née pid, stored as ->pid),
|
|
Packit |
577717 |
pid/tgid is that 2.6 thread group id crap (stored as ->tgid) */
|
|
Packit |
577717 |
asmlinkage long sys_vperfctr_open(int tid, int creat)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct file *filp;
|
|
Packit |
577717 |
struct task_struct *tsk;
|
|
Packit |
577717 |
struct vperfctr *perfctr;
|
|
Packit |
577717 |
int err;
|
|
Packit |
577717 |
int fd;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (!vperfctr_fs_init_done())
|
|
Packit |
577717 |
return -ENODEV;
|
|
Packit |
577717 |
filp = vperfctr_get_filp();
|
|
Packit |
577717 |
if (!filp)
|
|
Packit |
577717 |
return -ENOMEM;
|
|
Packit |
577717 |
err = fd = get_unused_fd();
|
|
Packit |
577717 |
if (err < 0)
|
|
Packit |
577717 |
goto err_filp;
|
|
Packit |
577717 |
perfctr = NULL;
|
|
Packit |
577717 |
if (creat) {
|
|
Packit |
577717 |
perfctr = get_empty_vperfctr(); /* may sleep */
|
|
Packit |
577717 |
if (IS_ERR(perfctr)) {
|
|
Packit |
577717 |
err = PTR_ERR(perfctr);
|
|
Packit |
577717 |
goto err_fd;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
tsk = current;
|
|
Packit |
577717 |
if (tid != 0 && tid != tsk->pid) { /* remote? */
|
|
Packit |
577717 |
read_lock(&tasklist_lock);
|
|
Packit |
577717 |
tsk = find_task_by_pid(tid);
|
|
Packit |
577717 |
if (tsk)
|
|
Packit |
577717 |
get_task_struct(tsk);
|
|
Packit |
577717 |
read_unlock(&tasklist_lock);
|
|
Packit |
577717 |
err = -ESRCH;
|
|
Packit |
577717 |
if (!tsk)
|
|
Packit |
577717 |
goto err_perfctr;
|
|
Packit |
577717 |
err = ptrace_check_attach(tsk, 0);
|
|
Packit |
577717 |
if (err < 0)
|
|
Packit |
577717 |
goto err_tsk;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if (creat) {
|
|
Packit |
577717 |
/* check+install must be atomic to prevent remote-control races */
|
|
Packit |
577717 |
task_lock(tsk);
|
|
Packit |
577717 |
if (!tsk->thread.perfctr) {
|
|
Packit |
577717 |
perfctr->owner = tsk;
|
|
Packit |
577717 |
tsk->thread.perfctr = perfctr;
|
|
Packit |
577717 |
err = 0;
|
|
Packit |
577717 |
} else
|
|
Packit |
577717 |
err = -EEXIST;
|
|
Packit |
577717 |
task_unlock(tsk);
|
|
Packit |
577717 |
if (err)
|
|
Packit |
577717 |
goto err_tsk;
|
|
Packit |
577717 |
} else {
|
|
Packit |
577717 |
perfctr = tsk->thread.perfctr;
|
|
Packit |
577717 |
/* XXX: Old API needed to allow NULL perfctr here.
|
|
Packit |
577717 |
Do we want to keep or change that rule? */
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
filp->private_data = perfctr;
|
|
Packit |
577717 |
if (perfctr)
|
|
Packit |
577717 |
atomic_inc(&perfctr->count);
|
|
Packit |
577717 |
if (tsk != current)
|
|
Packit |
577717 |
put_task_struct(tsk);
|
|
Packit |
577717 |
fd_install(fd, filp);
|
|
Packit |
577717 |
return fd;
|
|
Packit |
577717 |
err_tsk:
|
|
Packit |
577717 |
if (tsk != current)
|
|
Packit |
577717 |
put_task_struct(tsk);
|
|
Packit |
577717 |
err_perfctr:
|
|
Packit |
577717 |
if (perfctr) /* can only occur if creat != 0 */
|
|
Packit |
577717 |
put_vperfctr(perfctr);
|
|
Packit |
577717 |
err_fd:
|
|
Packit |
577717 |
put_unused_fd(fd);
|
|
Packit |
577717 |
err_filp:
|
|
Packit |
577717 |
fput(filp);
|
|
Packit |
577717 |
return err;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static struct vperfctr *fd_get_vperfctr(int fd)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct vperfctr *perfctr;
|
|
Packit |
577717 |
struct file *filp;
|
|
Packit |
577717 |
int err;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
err = -EBADF;
|
|
Packit |
577717 |
filp = fget(fd);
|
|
Packit |
577717 |
if (!filp)
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
err = -EINVAL;
|
|
Packit |
577717 |
if (filp->f_op != &vperfctr_file_ops)
|
|
Packit |
577717 |
goto out_filp;
|
|
Packit |
577717 |
perfctr = filp->private_data;
|
|
Packit |
577717 |
if (!perfctr)
|
|
Packit |
577717 |
goto out_filp;
|
|
Packit |
577717 |
atomic_inc(&perfctr->count);
|
|
Packit |
577717 |
fput(filp);
|
|
Packit |
577717 |
return perfctr;
|
|
Packit |
577717 |
out_filp:
|
|
Packit |
577717 |
fput(filp);
|
|
Packit |
577717 |
out:
|
|
Packit |
577717 |
return ERR_PTR(err);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static struct task_struct *vperfctr_get_tsk(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct task_struct *tsk;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
tsk = current;
|
|
Packit |
577717 |
if (perfctr != current->thread.perfctr) {
|
|
Packit |
577717 |
/* this synchronises with vperfctr_unlink() and itself */
|
|
Packit |
577717 |
spin_lock(&perfctr->owner_lock);
|
|
Packit |
577717 |
tsk = perfctr->owner;
|
|
Packit |
577717 |
if (tsk)
|
|
Packit |
577717 |
get_task_struct(tsk);
|
|
Packit |
577717 |
spin_unlock(&perfctr->owner_lock);
|
|
Packit |
577717 |
if (tsk) {
|
|
Packit |
577717 |
int ret = ptrace_check_attach(tsk, 0);
|
|
Packit |
577717 |
if (ret < 0) {
|
|
Packit |
577717 |
put_task_struct(tsk);
|
|
Packit |
577717 |
return ERR_PTR(ret);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return tsk;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void vperfctr_put_tsk(struct task_struct *tsk)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (tsk && tsk != current)
|
|
Packit |
577717 |
put_task_struct(tsk);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
asmlinkage long sys_vperfctr_write(int fd, unsigned int domain,
|
|
Packit |
577717 |
const void __user *argp,
|
|
Packit |
577717 |
unsigned int argbytes)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct vperfctr *perfctr;
|
|
Packit |
577717 |
struct task_struct *tsk;
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr = fd_get_vperfctr(fd);
|
|
Packit |
577717 |
if (IS_ERR(perfctr))
|
|
Packit |
577717 |
return PTR_ERR(perfctr);
|
|
Packit |
577717 |
tsk = vperfctr_get_tsk(perfctr);
|
|
Packit |
577717 |
if (IS_ERR(tsk)) {
|
|
Packit |
577717 |
ret = PTR_ERR(tsk);
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
ret = do_vperfctr_write(perfctr, domain, argp, argbytes, tsk);
|
|
Packit |
577717 |
vperfctr_put_tsk(tsk);
|
|
Packit |
577717 |
out:
|
|
Packit |
577717 |
put_vperfctr(perfctr);
|
|
Packit |
577717 |
return ret;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
asmlinkage long sys_vperfctr_control(int fd, unsigned int cmd)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct vperfctr *perfctr;
|
|
Packit |
577717 |
struct task_struct *tsk;
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr = fd_get_vperfctr(fd);
|
|
Packit |
577717 |
if (IS_ERR(perfctr))
|
|
Packit |
577717 |
return PTR_ERR(perfctr);
|
|
Packit |
577717 |
tsk = vperfctr_get_tsk(perfctr);
|
|
Packit |
577717 |
if (IS_ERR(tsk)) {
|
|
Packit |
577717 |
ret = PTR_ERR(tsk);
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
ret = do_vperfctr_control(perfctr, cmd, tsk);
|
|
Packit |
577717 |
vperfctr_put_tsk(tsk);
|
|
Packit |
577717 |
out:
|
|
Packit |
577717 |
put_vperfctr(perfctr);
|
|
Packit |
577717 |
return ret;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
asmlinkage long sys_vperfctr_read(int fd, unsigned int domain,
|
|
Packit |
577717 |
void __user *argp, unsigned int argbytes)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct vperfctr *perfctr;
|
|
Packit |
577717 |
struct task_struct *tsk;
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr = fd_get_vperfctr(fd);
|
|
Packit |
577717 |
if (IS_ERR(perfctr))
|
|
Packit |
577717 |
return PTR_ERR(perfctr);
|
|
Packit |
577717 |
tsk = vperfctr_get_tsk(perfctr);
|
|
Packit |
577717 |
if (IS_ERR(tsk)) {
|
|
Packit |
577717 |
ret = PTR_ERR(tsk);
|
|
Packit |
577717 |
goto out;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
ret = do_vperfctr_read(perfctr, domain, argp, argbytes, tsk);
|
|
Packit |
577717 |
vperfctr_put_tsk(tsk);
|
|
Packit |
577717 |
out:
|
|
Packit |
577717 |
put_vperfctr(perfctr);
|
|
Packit |
577717 |
return ret;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/****************************************************************
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
* module_init/exit *
|
|
Packit |
577717 |
* *
|
|
Packit |
577717 |
****************************************************************/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int __init vperfctr_init(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return vperfctrfs_init();
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void __exit vperfctr_exit(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
vperfctrfs_exit();
|
|
Packit |
577717 |
}
|