Blame src/perfctr-2.7.x/linux/drivers/perfctr/virtual.c

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
}