|
Packit Service |
a1973e |
/* $Id: virtual.c,v 1.88.2.28 2009/06/11 08:09:12 mikpe Exp $
|
|
Packit Service |
a1973e |
* Virtual per-process performance counters.
|
|
Packit Service |
a1973e |
*
|
|
Packit Service |
a1973e |
* Copyright (C) 1999-2009 Mikael Pettersson
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
#include <linux/version.h>
|
|
Packit Service |
a1973e |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
|
|
Packit Service |
a1973e |
#include <linux/config.h>
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
#define __NO_VERSION__
|
|
Packit Service |
a1973e |
#include <linux/module.h>
|
|
Packit Service |
a1973e |
#include <linux/init.h>
|
|
Packit Service |
a1973e |
#include <linux/compiler.h>
|
|
Packit Service |
a1973e |
#include <linux/kernel.h>
|
|
Packit Service |
a1973e |
#include <linux/mm.h>
|
|
Packit Service |
a1973e |
#include <linux/ptrace.h>
|
|
Packit Service |
a1973e |
#include <linux/fs.h>
|
|
Packit Service |
a1973e |
#include <linux/file.h>
|
|
Packit Service |
a1973e |
#include <linux/perfctr.h>
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#include <asm/io.h>
|
|
Packit Service |
a1973e |
#include <asm/uaccess.h>
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#include "compat.h"
|
|
Packit Service |
a1973e |
#include "virtual.h"
|
|
Packit Service |
a1973e |
#include "marshal.h"
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/****************************************************************
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
* Data types and macros. *
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
****************************************************************/
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
struct vperfctr {
|
|
Packit Service |
a1973e |
/* User-visible fields: (must be first for mmap()) */
|
|
Packit Service |
a1973e |
struct perfctr_cpu_state cpu_state;
|
|
Packit Service |
a1973e |
/* Kernel-private fields: */
|
|
Packit Service |
a1973e |
int si_signo;
|
|
Packit Service |
a1973e |
atomic_t count;
|
|
Packit Service |
a1973e |
spinlock_t owner_lock;
|
|
Packit Service |
a1973e |
struct task_struct *owner;
|
|
Packit Service |
a1973e |
/* sampling_timer and bad_cpus_allowed are frequently
|
|
Packit Service |
a1973e |
accessed, so they get to share a cache line */
|
|
Packit Service |
a1973e |
unsigned int sampling_timer ____cacheline_aligned;
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK
|
|
Packit Service |
a1973e |
atomic_t bad_cpus_allowed;
|
|
Packit Service |
a1973e |
cpumask_t cpumask;
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
pid_t updater_tgid; /* to detect self vs remote vperfctr_control races */
|
|
Packit Service |
a1973e |
#if 0 && defined(CONFIG_PERFCTR_DEBUG)
|
|
Packit Service |
a1973e |
unsigned start_smp_id;
|
|
Packit Service |
a1973e |
unsigned suspended;
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit Service |
a1973e |
unsigned int iresume_cstatus;
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
unsigned int flags;
|
|
Packit Service |
a1973e |
};
|
|
Packit Service |
a1973e |
#define IS_RUNNING(perfctr) perfctr_cstatus_enabled((perfctr)->cpu_state.cstatus)
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* XXX: disabled: called from switch_to() where printk() is disallowed */
|
|
Packit Service |
a1973e |
#if 0 && defined(CONFIG_PERFCTR_DEBUG)
|
|
Packit Service |
a1973e |
#define debug_free(perfctr) \
|
|
Packit Service |
a1973e |
do { \
|
|
Packit Service |
a1973e |
int i; \
|
|
Packit Service |
a1973e |
for(i = 0; i < PAGE_SIZE/sizeof(int); ++i) \
|
|
Packit Service |
a1973e |
((int*)(perfctr))[i] = 0xfedac0ed; \
|
|
Packit Service |
a1973e |
} while(0)
|
|
Packit Service |
a1973e |
#define debug_init(perfctr) do { (perfctr)->suspended = 1; } while(0)
|
|
Packit Service |
a1973e |
#define debug_suspend(perfctr) \
|
|
Packit Service |
a1973e |
do { \
|
|
Packit Service |
a1973e |
if ((perfctr)->suspended) \
|
|
Packit Service |
a1973e |
printk(KERN_ERR "%s: BUG! suspending non-running perfctr (pid %d, comm %s)\n", \
|
|
Packit Service |
a1973e |
__FUNCTION__, current->pid, current->comm); \
|
|
Packit Service |
a1973e |
(perfctr)->suspended = 1; \
|
|
Packit Service |
a1973e |
} while(0)
|
|
Packit Service |
a1973e |
#define debug_resume(perfctr) \
|
|
Packit Service |
a1973e |
do { \
|
|
Packit Service |
a1973e |
if (!(perfctr)->suspended) \
|
|
Packit Service |
a1973e |
printk(KERN_ERR "%s: BUG! resuming non-suspended perfctr (pid %d, comm %s)\n", \
|
|
Packit Service |
a1973e |
__FUNCTION__, current->pid, current->comm); \
|
|
Packit Service |
a1973e |
(perfctr)->suspended = 0; \
|
|
Packit Service |
a1973e |
} while(0)
|
|
Packit Service |
a1973e |
#define debug_check_smp_id(perfctr) \
|
|
Packit Service |
a1973e |
do { \
|
|
Packit Service |
a1973e |
if ((perfctr)->start_smp_id != smp_processor_id()) { \
|
|
Packit Service |
a1973e |
printk(KERN_ERR "%s: BUG! current cpu %u differs from start cpu %u (pid %d, comm %s)\n", \
|
|
Packit Service |
a1973e |
__FUNCTION__, smp_processor_id(), (perfctr)->start_smp_id, \
|
|
Packit Service |
a1973e |
current->pid, current->comm); \
|
|
Packit Service |
a1973e |
return; \
|
|
Packit Service |
a1973e |
} \
|
|
Packit Service |
a1973e |
} while(0)
|
|
Packit Service |
a1973e |
#define debug_set_smp_id(perfctr) \
|
|
Packit Service |
a1973e |
do { (perfctr)->start_smp_id = smp_processor_id(); } while(0)
|
|
Packit Service |
a1973e |
#else /* CONFIG_PERFCTR_DEBUG */
|
|
Packit Service |
a1973e |
#define debug_free(perfctr) do{}while(0)
|
|
Packit Service |
a1973e |
#define debug_init(perfctr) do{}while(0)
|
|
Packit Service |
a1973e |
#define debug_suspend(perfctr) do{}while(0)
|
|
Packit Service |
a1973e |
#define debug_resume(perfctr) do{}while(0)
|
|
Packit Service |
a1973e |
#define debug_check_smp_id(perfctr) do{}while(0)
|
|
Packit Service |
a1973e |
#define debug_set_smp_id(perfctr) do{}while(0)
|
|
Packit Service |
a1973e |
#endif /* CONFIG_PERFCTR_DEBUG */
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void vperfctr_ihandler(unsigned long pc);
|
|
Packit Service |
a1973e |
static void vperfctr_handle_overflow(struct task_struct*, struct vperfctr*);
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void vperfctr_set_ihandler(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
perfctr_cpu_set_ihandler(vperfctr_ihandler);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void vperfctr_clear_iresume_cstatus(struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
perfctr->iresume_cstatus = 0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#else
|
|
Packit Service |
a1973e |
static inline void vperfctr_set_ihandler(void) { }
|
|
Packit Service |
a1973e |
static inline void vperfctr_clear_iresume_cstatus(struct vperfctr *perfctr) { }
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void vperfctr_init_bad_cpus_allowed(struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
atomic_set(&perfctr->bad_cpus_allowed, 0);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void vperfctr_init_cpumask(struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
cpus_setall(perfctr->cpumask);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* Concurrent set_cpus_allowed() is possible. The only lock it
|
|
Packit Service |
a1973e |
can take is the task lock, so we have to take it as well.
|
|
Packit Service |
a1973e |
task_lock/unlock also disables/enables preemption. */
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void vperfctr_task_lock(struct task_struct *p)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
task_lock(p);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void vperfctr_task_unlock(struct task_struct *p)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
task_unlock(p);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#else /* !CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK */
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void vperfctr_init_bad_cpus_allowed(struct vperfctr *perfctr) { }
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void vperfctr_init_cpumask(struct vperfctr *perfctr) { }
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* Concurrent set_cpus_allowed() is impossible or irrelevant.
|
|
Packit Service |
a1973e |
Disabling and enabling preemption suffices for an atomic region. */
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void vperfctr_task_lock(struct task_struct *p)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
preempt_disable();
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void vperfctr_task_unlock(struct task_struct *p)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
preempt_enable();
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#endif /* !CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK */
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* How to lock around find_task_by_vpid(). The tasklist_lock always
|
|
Packit Service |
a1973e |
works, but it's no longer exported starting with kernel 2.6.18.
|
|
Packit Service |
a1973e |
For kernels 2.6.18 and newer use rcu_read_{lock,unlock}(). */
|
|
Packit Service |
a1973e |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
|
|
Packit Service |
a1973e |
static inline void vperfctr_lock_find_task_by_vpid(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
rcu_read_lock();
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void vperfctr_unlock_find_task_by_vpid(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
rcu_read_unlock();
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
#else /* < 2.6.18 */
|
|
Packit Service |
a1973e |
static inline void vperfctr_lock_find_task_by_vpid(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
read_lock(&tasklist_lock);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void vperfctr_unlock_find_task_by_vpid(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
read_unlock(&tasklist_lock);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
#endif /* < 2.6.18 */
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/****************************************************************
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
* Resource management. *
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
****************************************************************/
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* XXX: perhaps relax this to number of _live_ perfctrs */
|
|
Packit Service |
a1973e |
static DEFINE_MUTEX(nrctrs_mutex);
|
|
Packit Service |
a1973e |
static int nrctrs;
|
|
Packit Service |
a1973e |
static const char this_service[] = __FILE__;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static int inc_nrctrs(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
const char *other;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
other = NULL;
|
|
Packit Service |
a1973e |
mutex_lock(&nrctrs_mutex);
|
|
Packit Service |
a1973e |
if (++nrctrs == 1) {
|
|
Packit Service |
a1973e |
other = perfctr_cpu_reserve(this_service);
|
|
Packit Service |
a1973e |
if (other)
|
|
Packit Service |
a1973e |
nrctrs = 0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
mutex_unlock(&nrctrs_mutex);
|
|
Packit Service |
a1973e |
if (other) {
|
|
Packit Service |
a1973e |
printk(KERN_ERR __FILE__
|
|
Packit Service |
a1973e |
": cannot operate, perfctr hardware taken by '%s'\n",
|
|
Packit Service |
a1973e |
other);
|
|
Packit Service |
a1973e |
return -EBUSY;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
vperfctr_set_ihandler();
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void dec_nrctrs(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
mutex_lock(&nrctrs_mutex);
|
|
Packit Service |
a1973e |
if (--nrctrs == 0)
|
|
Packit Service |
a1973e |
perfctr_cpu_release(this_service);
|
|
Packit Service |
a1973e |
mutex_unlock(&nrctrs_mutex);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static struct vperfctr *vperfctr_alloc(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
unsigned long page;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
if (inc_nrctrs() != 0)
|
|
Packit Service |
a1973e |
return ERR_PTR(-EBUSY);
|
|
Packit Service |
a1973e |
page = get_zeroed_page(GFP_KERNEL);
|
|
Packit Service |
a1973e |
if (!page) {
|
|
Packit Service |
a1973e |
dec_nrctrs();
|
|
Packit Service |
a1973e |
return ERR_PTR(-ENOMEM);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
SetPageReserved(virt_to_page(page));
|
|
Packit Service |
a1973e |
return (struct vperfctr*) page;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void vperfctr_free(struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
debug_free(perfctr);
|
|
Packit Service |
a1973e |
ClearPageReserved(virt_to_page(perfctr));
|
|
Packit Service |
a1973e |
free_page((unsigned long)perfctr);
|
|
Packit Service |
a1973e |
dec_nrctrs();
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static struct vperfctr *get_empty_vperfctr(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct vperfctr *perfctr = vperfctr_alloc();
|
|
Packit Service |
a1973e |
if (!IS_ERR(perfctr)) {
|
|
Packit Service |
a1973e |
atomic_set(&perfctr->count, 1);
|
|
Packit Service |
a1973e |
vperfctr_init_bad_cpus_allowed(perfctr);
|
|
Packit Service |
a1973e |
vperfctr_init_cpumask(perfctr);
|
|
Packit Service |
a1973e |
spin_lock_init(&perfctr->owner_lock);
|
|
Packit Service |
a1973e |
debug_init(perfctr);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
return perfctr;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void put_vperfctr(struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
if (atomic_dec_and_test(&perfctr->count))
|
|
Packit Service |
a1973e |
vperfctr_free(perfctr);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/****************************************************************
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
* Basic counter operations. *
|
|
Packit Service |
a1973e |
* These must all be called by the owner process only. *
|
|
Packit Service |
a1973e |
* These must all be called with preemption disabled. *
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
****************************************************************/
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* PRE: IS_RUNNING(perfctr)
|
|
Packit Service |
a1973e |
* Suspend the counters.
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
static inline void vperfctr_suspend(struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
debug_suspend(perfctr);
|
|
Packit Service |
a1973e |
debug_check_smp_id(perfctr);
|
|
Packit Service |
a1973e |
perfctr_cpu_suspend(&perfctr->cpu_state);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void vperfctr_reset_sampling_timer(struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
/* XXX: base the value on perfctr_info.cpu_khz instead! */
|
|
Packit Service |
a1973e |
perfctr->sampling_timer = HZ/2;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* PRE: perfctr == current->thread.perfctr && IS_RUNNING(perfctr)
|
|
Packit Service |
a1973e |
* Restart the counters.
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
static inline void vperfctr_resume(struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
debug_resume(perfctr);
|
|
Packit Service |
a1973e |
perfctr_cpu_resume(&perfctr->cpu_state);
|
|
Packit Service |
a1973e |
vperfctr_reset_sampling_timer(perfctr);
|
|
Packit Service |
a1973e |
debug_set_smp_id(perfctr);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static inline void vperfctr_resume_with_overflow_check(struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit Service |
a1973e |
if (perfctr_cpu_has_pending_interrupt(&perfctr->cpu_state)) {
|
|
Packit Service |
a1973e |
vperfctr_handle_overflow(current, perfctr);
|
|
Packit Service |
a1973e |
return;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
vperfctr_resume(perfctr);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* Sample the counters but do not suspend them. */
|
|
Packit Service |
a1973e |
static void vperfctr_sample(struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
if (IS_RUNNING(perfctr)) {
|
|
Packit Service |
a1973e |
debug_check_smp_id(perfctr);
|
|
Packit Service |
a1973e |
perfctr_cpu_sample(&perfctr->cpu_state);
|
|
Packit Service |
a1973e |
vperfctr_reset_sampling_timer(perfctr);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit Service |
a1973e |
/* vperfctr interrupt handler (XXX: add buffering support) */
|
|
Packit Service |
a1973e |
/* PREEMPT note: called in IRQ context with preemption disabled. */
|
|
Packit Service |
a1973e |
static void vperfctr_ihandler(unsigned long pc)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct task_struct *tsk = current;
|
|
Packit Service |
a1973e |
struct vperfctr *perfctr;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
perfctr = tsk->thread.perfctr;
|
|
Packit Service |
a1973e |
if (!perfctr) {
|
|
Packit Service |
a1973e |
printk(KERN_ERR "%s: BUG! pid %d has no vperfctr\n",
|
|
Packit Service |
a1973e |
__FUNCTION__, tsk->pid);
|
|
Packit Service |
a1973e |
return;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
if (!perfctr_cstatus_has_ictrs(perfctr->cpu_state.cstatus)) {
|
|
Packit Service |
a1973e |
printk(KERN_ERR "%s: BUG! vperfctr has cstatus %#x (pid %d, comm %s)\n",
|
|
Packit Service |
a1973e |
__FUNCTION__, perfctr->cpu_state.cstatus, tsk->pid, tsk->comm);
|
|
Packit Service |
a1973e |
return;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
vperfctr_suspend(perfctr);
|
|
Packit Service |
a1973e |
vperfctr_handle_overflow(tsk, perfctr);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void vperfctr_handle_overflow(struct task_struct *tsk,
|
|
Packit Service |
a1973e |
struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
unsigned int pmc_mask;
|
|
Packit Service |
a1973e |
siginfo_t si;
|
|
Packit Service |
a1973e |
sigset_t old_blocked;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
pmc_mask = perfctr_cpu_identify_overflow(&perfctr->cpu_state);
|
|
Packit Service |
a1973e |
if (!pmc_mask) {
|
|
Packit Service |
a1973e |
printk(KERN_ERR "%s: BUG! pid %d has unidentifiable overflow source\n",
|
|
Packit Service |
a1973e |
__FUNCTION__, tsk->pid);
|
|
Packit Service |
a1973e |
return;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
/* suspend a-mode and i-mode PMCs, leaving only TSC on */
|
|
Packit Service |
a1973e |
/* XXX: some people also want to suspend the TSC */
|
|
Packit Service |
a1973e |
perfctr->iresume_cstatus = perfctr->cpu_state.cstatus;
|
|
Packit Service |
a1973e |
if (perfctr_cstatus_has_tsc(perfctr->iresume_cstatus)) {
|
|
Packit Service |
a1973e |
perfctr->cpu_state.cstatus = perfctr_mk_cstatus(1, 0, 0);
|
|
Packit Service |
a1973e |
vperfctr_resume(perfctr);
|
|
Packit Service |
a1973e |
} else
|
|
Packit Service |
a1973e |
perfctr->cpu_state.cstatus = 0;
|
|
Packit Service |
a1973e |
si.si_signo = perfctr->si_signo;
|
|
Packit Service |
a1973e |
si.si_errno = 0;
|
|
Packit Service |
a1973e |
si.si_code = SI_PMC_OVF;
|
|
Packit Service |
a1973e |
si.si_pmc_ovf_mask = pmc_mask;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* deliver signal without waking up the receiver */
|
|
Packit Service |
a1973e |
spin_lock_irq(&task_siglock(tsk));
|
|
Packit Service |
a1973e |
old_blocked = tsk->blocked;
|
|
Packit Service |
a1973e |
sigaddset(&tsk->blocked, si.si_signo);
|
|
Packit Service |
a1973e |
spin_unlock_irq(&task_siglock(tsk));
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
if (!send_sig_info(si.si_signo, &si, tsk))
|
|
Packit Service |
a1973e |
send_sig(si.si_signo, tsk, 1);
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
spin_lock_irq(&task_siglock(tsk));
|
|
Packit Service |
a1973e |
tsk->blocked = old_blocked;
|
|
Packit Service |
a1973e |
recalc_sigpending();
|
|
Packit Service |
a1973e |
spin_unlock_irq(&task_siglock(tsk));
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/****************************************************************
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
* Process management operations. *
|
|
Packit Service |
a1973e |
* These must all, with the exception of vperfctr_unlink() *
|
|
Packit Service |
a1973e |
* and __vperfctr_set_cpus_allowed(), be called by the owner *
|
|
Packit Service |
a1973e |
* process only. *
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
****************************************************************/
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* Called from exit_thread() or sys_vperfctr_unlink().
|
|
Packit Service |
a1973e |
* If the counters are running, stop them and sample their final values.
|
|
Packit Service |
a1973e |
* Detach the vperfctr object from its owner task.
|
|
Packit Service |
a1973e |
* PREEMPT note: exit_thread() does not run with preemption disabled.
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
static void vperfctr_unlink(struct task_struct *owner, struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
/* this synchronises with vperfctr_ioctl() */
|
|
Packit Service |
a1973e |
spin_lock(&perfctr->owner_lock);
|
|
Packit Service |
a1973e |
perfctr->owner = NULL;
|
|
Packit Service |
a1973e |
spin_unlock(&perfctr->owner_lock);
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* perfctr suspend+detach must be atomic wrt process suspend */
|
|
Packit Service |
a1973e |
/* this also synchronises with perfctr_set_cpus_allowed() */
|
|
Packit Service |
a1973e |
vperfctr_task_lock(owner);
|
|
Packit Service |
a1973e |
if (IS_RUNNING(perfctr) && owner == current)
|
|
Packit Service |
a1973e |
vperfctr_suspend(perfctr);
|
|
Packit Service |
a1973e |
owner->thread.perfctr = NULL;
|
|
Packit Service |
a1973e |
vperfctr_task_unlock(owner);
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
perfctr->cpu_state.cstatus = 0;
|
|
Packit Service |
a1973e |
vperfctr_clear_iresume_cstatus(perfctr);
|
|
Packit Service |
a1973e |
put_vperfctr(perfctr);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
void __vperfctr_exit(struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
vperfctr_unlink(current, perfctr);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* sys_execve() -> .. -> flush_old_exec() -> .. -> __vperfctr_flush().
|
|
Packit Service |
a1973e |
* Unlink the thread's perfctr state, if the CLOEXEC control flag is set.
|
|
Packit Service |
a1973e |
* PREEMPT note: flush_old_exec() does not run with preemption disabled.
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
void __vperfctr_flush(struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
if (perfctr->flags & VPERFCTR_CONTROL_CLOEXEC)
|
|
Packit Service |
a1973e |
__vperfctr_exit(perfctr);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* schedule() --> switch_to() --> .. --> __vperfctr_suspend().
|
|
Packit Service |
a1973e |
* If the counters are running, suspend them.
|
|
Packit Service |
a1973e |
* PREEMPT note: switch_to() runs with preemption disabled.
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
void __vperfctr_suspend(struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
if (IS_RUNNING(perfctr))
|
|
Packit Service |
a1973e |
vperfctr_suspend(perfctr);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* schedule() --> switch_to() --> .. --> __vperfctr_resume().
|
|
Packit Service |
a1973e |
* PRE: perfctr == current->thread.perfctr
|
|
Packit Service |
a1973e |
* If the counters are runnable, resume them.
|
|
Packit Service |
a1973e |
* PREEMPT note: switch_to() runs with preemption disabled.
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
void __vperfctr_resume(struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
if (IS_RUNNING(perfctr)) {
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK
|
|
Packit Service |
a1973e |
if (unlikely(atomic_read(&perfctr->bad_cpus_allowed)) &&
|
|
Packit Service |
a1973e |
perfctr_cstatus_nrctrs(perfctr->cpu_state.cstatus)) {
|
|
Packit Service |
a1973e |
perfctr->cpu_state.cstatus = 0;
|
|
Packit Service |
a1973e |
vperfctr_clear_iresume_cstatus(perfctr);
|
|
Packit Service |
a1973e |
BUG_ON(current->state != TASK_RUNNING);
|
|
Packit Service |
a1973e |
send_sig(SIGILL, current, 1);
|
|
Packit Service |
a1973e |
return;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
vperfctr_resume_with_overflow_check(perfctr);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* Called from update_one_process() [triggered by timer interrupt].
|
|
Packit Service |
a1973e |
* PRE: perfctr == current->thread.perfctr.
|
|
Packit Service |
a1973e |
* Sample the counters but do not suspend them.
|
|
Packit Service |
a1973e |
* Needed to avoid precision loss due to multiple counter
|
|
Packit Service |
a1973e |
* wraparounds between resume/suspend for CPU-bound processes.
|
|
Packit Service |
a1973e |
* PREEMPT note: called in IRQ context with preemption disabled.
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
void __vperfctr_sample(struct vperfctr *perfctr)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
if (--perfctr->sampling_timer == 0)
|
|
Packit Service |
a1973e |
vperfctr_sample(perfctr);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK
|
|
Packit Service |
a1973e |
/* Called from set_cpus_allowed().
|
|
Packit Service |
a1973e |
* PRE: current holds task_lock(owner)
|
|
Packit Service |
a1973e |
* PRE: owner->thread.perfctr == perfctr
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
void __vperfctr_set_cpus_allowed(struct task_struct *owner,
|
|
Packit Service |
a1973e |
struct vperfctr *perfctr,
|
|
Packit Service |
a1973e |
cpumask_t new_mask)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
if (!cpus_subset(new_mask, perfctr->cpumask)) {
|
|
Packit Service |
a1973e |
atomic_set(&perfctr->bad_cpus_allowed, 1);
|
|
Packit Service |
a1973e |
printk(KERN_WARNING "perfctr: process %d (comm %s) issued unsafe"
|
|
Packit Service |
a1973e |
" set_cpus_allowed() on process %d (comm %s)\n",
|
|
Packit Service |
a1973e |
current->pid, current->comm, owner->pid, owner->comm);
|
|
Packit Service |
a1973e |
} else
|
|
Packit Service |
a1973e |
atomic_set(&perfctr->bad_cpus_allowed, 0);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/****************************************************************
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
* Virtual perfctr "system calls". *
|
|
Packit Service |
a1973e |
* These can be called by the owner process (tsk == current), *
|
|
Packit Service |
a1973e |
* a monitor process which has the owner under ptrace ATTACH *
|
|
Packit Service |
a1973e |
* control (tsk && tsk != current), or anyone with a handle to *
|
|
Packit Service |
a1973e |
* an unlinked perfctr (!tsk). *
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
****************************************************************/
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static int sys_vperfctr_control(struct vperfctr *perfctr,
|
|
Packit Service |
a1973e |
struct perfctr_struct_buf *argp,
|
|
Packit Service |
a1973e |
struct task_struct *tsk)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct vperfctr_control control;
|
|
Packit Service |
a1973e |
int err;
|
|
Packit Service |
a1973e |
unsigned int next_cstatus;
|
|
Packit Service |
a1973e |
unsigned int nrctrs, i;
|
|
Packit Service |
a1973e |
cpumask_t cpumask;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
if (!tsk)
|
|
Packit Service |
a1973e |
return -ESRCH; /* attempt to update unlinked perfctr */
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
err = perfctr_copy_from_user(&control, argp, &vperfctr_control_sdesc);
|
|
Packit Service |
a1973e |
if (err)
|
|
Packit Service |
a1973e |
return err;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* Step 1: Update the control but keep the counters disabled.
|
|
Packit Service |
a1973e |
PREEMPT note: Preemption is disabled since we're updating
|
|
Packit Service |
a1973e |
an active perfctr. */
|
|
Packit Service |
a1973e |
preempt_disable();
|
|
Packit Service |
a1973e |
if (IS_RUNNING(perfctr)) {
|
|
Packit Service |
a1973e |
if (tsk == current)
|
|
Packit Service |
a1973e |
vperfctr_suspend(perfctr);
|
|
Packit Service |
a1973e |
perfctr->cpu_state.cstatus = 0;
|
|
Packit Service |
a1973e |
vperfctr_clear_iresume_cstatus(perfctr);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
perfctr->cpu_state.control = control.cpu_control;
|
|
Packit Service |
a1973e |
/* remote access note: perfctr_cpu_update_control() is ok */
|
|
Packit Service |
a1973e |
cpus_setall(cpumask);
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK
|
|
Packit Service |
a1973e |
/* make a stopped vperfctr have an unconstrained cpumask */
|
|
Packit Service |
a1973e |
perfctr->cpumask = cpumask;
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
err = perfctr_cpu_update_control(&perfctr->cpu_state, &cpumask);
|
|
Packit Service |
a1973e |
if (err < 0) {
|
|
Packit Service |
a1973e |
next_cstatus = 0;
|
|
Packit Service |
a1973e |
} else {
|
|
Packit Service |
a1973e |
next_cstatus = perfctr->cpu_state.cstatus;
|
|
Packit Service |
a1973e |
perfctr->cpu_state.cstatus = 0;
|
|
Packit Service |
a1973e |
perfctr->updater_tgid = current->tgid;
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK
|
|
Packit Service |
a1973e |
perfctr->cpumask = cpumask;
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
preempt_enable_no_resched();
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
if (!perfctr_cstatus_enabled(next_cstatus))
|
|
Packit Service |
a1973e |
return err;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK
|
|
Packit Service |
a1973e |
/* Step 2: Update the task's CPU affinity mask.
|
|
Packit Service |
a1973e |
PREEMPT note: Preemption must be enabled for set_cpus_allowed(). */
|
|
Packit Service |
a1973e |
if (control.cpu_control.nractrs || control.cpu_control.nrictrs) {
|
|
Packit Service |
a1973e |
cpumask_t old_mask, new_mask;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
old_mask = tsk->cpus_allowed;
|
|
Packit Service |
a1973e |
cpus_and(new_mask, old_mask, cpumask);
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
if (cpus_empty(new_mask))
|
|
Packit Service |
a1973e |
return -EINVAL;
|
|
Packit Service |
a1973e |
if (!cpus_equal(new_mask, old_mask))
|
|
Packit Service |
a1973e |
set_cpus_allowed(tsk, new_mask);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* Step 3: Enable the counters with the new control and affinity.
|
|
Packit Service |
a1973e |
PREEMPT note: Preemption is disabled since we're updating
|
|
Packit Service |
a1973e |
an active perfctr. */
|
|
Packit Service |
a1973e |
preempt_disable();
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* We had to enable preemption above for set_cpus_allowed() so we may
|
|
Packit Service |
a1973e |
have lost a race with a concurrent update via the remote control
|
|
Packit Service |
a1973e |
interface. If so then we must abort our update of this perfctr. */
|
|
Packit Service |
a1973e |
if (perfctr->updater_tgid != current->tgid) {
|
|
Packit Service |
a1973e |
printk(KERN_WARNING "perfctr: control update by task %d"
|
|
Packit Service |
a1973e |
" was lost due to race with update by task %d\n",
|
|
Packit Service |
a1973e |
current->tgid, perfctr->updater_tgid);
|
|
Packit Service |
a1973e |
err = -EBUSY;
|
|
Packit Service |
a1973e |
} else {
|
|
Packit Service |
a1973e |
/* XXX: validate si_signo? */
|
|
Packit Service |
a1973e |
perfctr->si_signo = control.si_signo;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
perfctr->cpu_state.cstatus = next_cstatus;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
if (!perfctr_cstatus_has_tsc(next_cstatus))
|
|
Packit Service |
a1973e |
perfctr->cpu_state.tsc_sum = 0;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
nrctrs = perfctr_cstatus_nrctrs(next_cstatus);
|
|
Packit Service |
a1973e |
for(i = 0; i < nrctrs; ++i)
|
|
Packit Service |
a1973e |
if (!(control.preserve & (1<
|
|
Packit Service |
a1973e |
perfctr->cpu_state.pmc[i].sum = 0;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
perfctr->flags = control.flags;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
if (tsk == current)
|
|
Packit Service |
a1973e |
vperfctr_resume(perfctr);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
preempt_enable();
|
|
Packit Service |
a1973e |
return err;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static int sys_vperfctr_iresume(struct vperfctr *perfctr, const struct task_struct *tsk)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT
|
|
Packit Service |
a1973e |
unsigned int iresume_cstatus;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
if (!tsk)
|
|
Packit Service |
a1973e |
return -ESRCH; /* attempt to update unlinked perfctr */
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
iresume_cstatus = perfctr->iresume_cstatus;
|
|
Packit Service |
a1973e |
if (!perfctr_cstatus_has_ictrs(iresume_cstatus))
|
|
Packit Service |
a1973e |
return -EPERM;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* PREEMPT note: preemption is disabled over the entire
|
|
Packit Service |
a1973e |
region because we're updating an active perfctr. */
|
|
Packit Service |
a1973e |
preempt_disable();
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
if (IS_RUNNING(perfctr) && tsk == current)
|
|
Packit Service |
a1973e |
vperfctr_suspend(perfctr);
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
perfctr->cpu_state.cstatus = iresume_cstatus;
|
|
Packit Service |
a1973e |
perfctr->iresume_cstatus = 0;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* remote access note: perfctr_cpu_ireload() is ok */
|
|
Packit Service |
a1973e |
perfctr_cpu_ireload(&perfctr->cpu_state);
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
if (tsk == current)
|
|
Packit Service |
a1973e |
vperfctr_resume(perfctr);
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
preempt_enable();
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
#else
|
|
Packit Service |
a1973e |
return -ENOSYS;
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static int sys_vperfctr_unlink(struct vperfctr *perfctr, struct task_struct *tsk)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
if (tsk)
|
|
Packit Service |
a1973e |
vperfctr_unlink(tsk, perfctr);
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static int sys_vperfctr_read_sum(struct vperfctr *perfctr,
|
|
Packit Service |
a1973e |
struct perfctr_struct_buf *argp,
|
|
Packit Service |
a1973e |
const struct task_struct *tsk)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct perfctr_sum_ctrs sum;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
if (tsk == current) {
|
|
Packit Service |
a1973e |
preempt_disable();
|
|
Packit Service |
a1973e |
vperfctr_sample(perfctr);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
//sum = perfctr->cpu_state.sum;
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
int j;
|
|
Packit Service |
a1973e |
sum.tsc = perfctr->cpu_state.tsc_sum;
|
|
Packit Service |
a1973e |
for(j = 0; j < ARRAY_SIZE(sum.pmc); ++j)
|
|
Packit Service |
a1973e |
sum.pmc[j] = perfctr->cpu_state.pmc[j].sum;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
if (tsk == current)
|
|
Packit Service |
a1973e |
preempt_enable();
|
|
Packit Service |
a1973e |
return perfctr_copy_to_user(argp, &sum, &perfctr_sum_ctrs_sdesc);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static int sys_vperfctr_read_control(struct vperfctr *perfctr,
|
|
Packit Service |
a1973e |
struct perfctr_struct_buf *argp,
|
|
Packit Service |
a1973e |
const struct task_struct *tsk)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct vperfctr_control control;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* PREEMPT note: While we're reading our own control, another
|
|
Packit Service |
a1973e |
process may ptrace ATTACH to us and update our control.
|
|
Packit Service |
a1973e |
Disable preemption to ensure we get a consistent copy.
|
|
Packit Service |
a1973e |
Not needed for other cases since the perfctr is either
|
|
Packit Service |
a1973e |
unlinked or its owner is ptrace ATTACH suspended by us. */
|
|
Packit Service |
a1973e |
if (tsk == current)
|
|
Packit Service |
a1973e |
preempt_disable();
|
|
Packit Service |
a1973e |
control.si_signo = perfctr->si_signo;
|
|
Packit Service |
a1973e |
control.cpu_control = perfctr->cpu_state.control;
|
|
Packit Service |
a1973e |
control.flags = perfctr->flags;
|
|
Packit Service |
a1973e |
if (tsk == current)
|
|
Packit Service |
a1973e |
preempt_enable();
|
|
Packit Service |
a1973e |
control.preserve = 0;
|
|
Packit Service |
a1973e |
return perfctr_copy_to_user(argp, &control, &vperfctr_control_sdesc);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/****************************************************************
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
* Virtual perfctr file operations. *
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
****************************************************************/
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static int vperfctr_mmap(struct file *filp, struct vm_area_struct *vma)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct vperfctr *perfctr;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#ifdef CONFIG_ARM
|
|
Packit Service |
a1973e |
#define _PAGE_RW L_PTE_WRITE
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
/* Only allow read-only mapping of first page. */
|
|
Packit Service |
a1973e |
if ((vma->vm_end - vma->vm_start) != PAGE_SIZE ||
|
|
Packit Service |
a1973e |
vma->vm_pgoff != 0 ||
|
|
Packit Service |
a1973e |
(pgprot_val(vma->vm_page_prot) & _PAGE_RW) ||
|
|
Packit Service |
a1973e |
(vma->vm_flags & (VM_WRITE | VM_MAYWRITE)))
|
|
Packit Service |
a1973e |
return -EPERM;
|
|
Packit Service |
a1973e |
perfctr = filp->private_data;
|
|
Packit Service |
a1973e |
if (!perfctr)
|
|
Packit Service |
a1973e |
return -EPERM;
|
|
Packit Service |
a1973e |
/* 2.6.29-rc1 changed arch/x86/mm/pat.c to WARN_ON when
|
|
Packit Service |
a1973e |
remap_pfn_range() is applied to plain RAM pages.
|
|
Packit Service |
a1973e |
Comments there indicate that one should set_memory_wc()
|
|
Packit Service |
a1973e |
before the remap, but that doesn't silence the WARN_ON.
|
|
Packit Service |
a1973e |
Luckily vm_insert_page() works without complaints. */
|
|
Packit Service |
a1973e |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
|
|
Packit Service |
a1973e |
return vm_insert_page(vma, vma->vm_start, virt_to_page((unsigned long)perfctr));
|
|
Packit Service |
a1973e |
#else
|
|
Packit Service |
a1973e |
return remap_pfn_range(vma, vma->vm_start,
|
|
Packit Service |
a1973e |
virt_to_phys(perfctr) >> PAGE_SHIFT,
|
|
Packit Service |
a1973e |
PAGE_SIZE, vma->vm_page_prot);
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static int vperfctr_release(struct inode *inode, struct file *filp)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct vperfctr *perfctr = filp->private_data;
|
|
Packit Service |
a1973e |
filp->private_data = NULL;
|
|
Packit Service |
a1973e |
if (perfctr)
|
|
Packit Service |
a1973e |
put_vperfctr(perfctr);
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static long vperfctr_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct vperfctr *perfctr;
|
|
Packit Service |
a1973e |
struct task_struct *tsk;
|
|
Packit Service |
a1973e |
int ret;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
switch (cmd) {
|
|
Packit Service |
a1973e |
case PERFCTR_ABI:
|
|
Packit Service |
a1973e |
return sys_perfctr_abi((unsigned int*)arg);
|
|
Packit Service |
a1973e |
case PERFCTR_INFO:
|
|
Packit Service |
a1973e |
return sys_perfctr_info((struct perfctr_struct_buf*)arg);
|
|
Packit Service |
a1973e |
case PERFCTR_CPUS:
|
|
Packit Service |
a1973e |
return sys_perfctr_cpus((struct perfctr_cpu_mask*)arg);
|
|
Packit Service |
a1973e |
case PERFCTR_CPUS_FORBIDDEN:
|
|
Packit Service |
a1973e |
return sys_perfctr_cpus_forbidden((struct perfctr_cpu_mask*)arg);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
perfctr = filp->private_data;
|
|
Packit Service |
a1973e |
if (!perfctr)
|
|
Packit Service |
a1973e |
return -EINVAL;
|
|
Packit Service |
a1973e |
tsk = current;
|
|
Packit Service |
a1973e |
if (perfctr != current->thread.perfctr) {
|
|
Packit Service |
a1973e |
/* this synchronises with vperfctr_unlink() and itself */
|
|
Packit Service |
a1973e |
spin_lock(&perfctr->owner_lock);
|
|
Packit Service |
a1973e |
tsk = perfctr->owner;
|
|
Packit Service |
a1973e |
if (tsk)
|
|
Packit Service |
a1973e |
get_task_struct(tsk);
|
|
Packit Service |
a1973e |
spin_unlock(&perfctr->owner_lock);
|
|
Packit Service |
a1973e |
if (tsk) {
|
|
Packit Service |
a1973e |
ret = ptrace_check_attach(tsk, 0);
|
|
Packit Service |
a1973e |
if (ret < 0)
|
|
Packit Service |
a1973e |
goto out;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
switch (cmd) {
|
|
Packit Service |
a1973e |
case VPERFCTR_CONTROL:
|
|
Packit Service |
a1973e |
ret = sys_vperfctr_control(perfctr, (struct perfctr_struct_buf*)arg, tsk);
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case VPERFCTR_UNLINK:
|
|
Packit Service |
a1973e |
ret = sys_vperfctr_unlink(perfctr, tsk);
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case VPERFCTR_READ_SUM:
|
|
Packit Service |
a1973e |
ret = sys_vperfctr_read_sum(perfctr, (struct perfctr_struct_buf*)arg, tsk);
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case VPERFCTR_IRESUME:
|
|
Packit Service |
a1973e |
ret = sys_vperfctr_iresume(perfctr, tsk);
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
case VPERFCTR_READ_CONTROL:
|
|
Packit Service |
a1973e |
ret = sys_vperfctr_read_control(perfctr, (struct perfctr_struct_buf*)arg, tsk);
|
|
Packit Service |
a1973e |
break;
|
|
Packit Service |
a1973e |
default:
|
|
Packit Service |
a1973e |
ret = -EINVAL;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
out:
|
|
Packit Service |
a1973e |
if (tsk && tsk != current)
|
|
Packit Service |
a1973e |
put_task_struct(tsk);
|
|
Packit Service |
a1973e |
return ret;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#if !HAVE_UNLOCKED_IOCTL
|
|
Packit Service |
a1973e |
static int vperfctr_ioctl_oldstyle(struct inode *inode, struct file *filp,
|
|
Packit Service |
a1973e |
unsigned int cmd, unsigned long arg)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
return vperfctr_ioctl(filp, cmd, arg);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static
|
|
Packit Service |
a1973e |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
Packit Service |
a1973e |
const
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
struct file_operations vperfctr_file_ops = {
|
|
Packit Service |
a1973e |
.owner = THIS_MODULE,
|
|
Packit Service |
a1973e |
.mmap = vperfctr_mmap,
|
|
Packit Service |
a1973e |
.release = vperfctr_release,
|
|
Packit Service |
a1973e |
/* 2.6.11-rc2 introduced HAVE_UNLOCKED_IOCTL and HAVE_COMPAT_IOCTL */
|
|
Packit Service |
a1973e |
#if HAVE_UNLOCKED_IOCTL
|
|
Packit Service |
a1973e |
.unlocked_ioctl = vperfctr_ioctl,
|
|
Packit Service |
a1973e |
#else
|
|
Packit Service |
a1973e |
.ioctl = vperfctr_ioctl_oldstyle,
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
#if defined(CONFIG_IA32_EMULATION) && HAVE_COMPAT_IOCTL
|
|
Packit Service |
a1973e |
.compat_ioctl = vperfctr_ioctl,
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
};
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/****************************************************************
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
* File system for virtual perfctrs. Based on pipefs. *
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
****************************************************************/
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#define VPERFCTRFS_MAGIC (('V'<<24)|('P'<<16)|('M'<<8)|('C'))
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#include <linux/mount.h>
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* 2.6 kernels prior to 2.6.11-rc1 don't EXPORT_SYMBOL() get_sb_pseudo().
|
|
Packit Service |
a1973e |
This is a verbatim copy, only renamed. */
|
|
Packit Service |
a1973e |
#if defined(MODULE) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
|
|
Packit Service |
a1973e |
static
|
|
Packit Service |
a1973e |
struct super_block *
|
|
Packit Service |
a1973e |
perfctr_get_sb_pseudo(struct file_system_type *fs_type, char *name,
|
|
Packit Service |
a1973e |
struct super_operations *ops, unsigned long magic)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL);
|
|
Packit Service |
a1973e |
static struct super_operations default_ops = {.statfs = simple_statfs};
|
|
Packit Service |
a1973e |
struct dentry *dentry;
|
|
Packit Service |
a1973e |
struct inode *root;
|
|
Packit Service |
a1973e |
struct qstr d_name = {.name = name, .len = strlen(name)};
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
if (IS_ERR(s))
|
|
Packit Service |
a1973e |
return s;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
s->s_flags = MS_NOUSER;
|
|
Packit Service |
a1973e |
s->s_maxbytes = ~0ULL;
|
|
Packit Service |
a1973e |
s->s_blocksize = 1024;
|
|
Packit Service |
a1973e |
s->s_blocksize_bits = 10;
|
|
Packit Service |
a1973e |
s->s_magic = magic;
|
|
Packit Service |
a1973e |
s->s_op = ops ? ops : &default_ops;
|
|
Packit Service |
a1973e |
root = new_inode(s);
|
|
Packit Service |
a1973e |
if (!root)
|
|
Packit Service |
a1973e |
goto Enomem;
|
|
Packit Service |
a1973e |
root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR;
|
|
Packit Service |
a1973e |
root->i_uid = root->i_gid = 0;
|
|
Packit Service |
a1973e |
root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME;
|
|
Packit Service |
a1973e |
dentry = d_alloc(NULL, &d_name);
|
|
Packit Service |
a1973e |
if (!dentry) {
|
|
Packit Service |
a1973e |
iput(root);
|
|
Packit Service |
a1973e |
goto Enomem;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
dentry->d_sb = s;
|
|
Packit Service |
a1973e |
dentry->d_parent = dentry;
|
|
Packit Service |
a1973e |
d_instantiate(dentry, root);
|
|
Packit Service |
a1973e |
s->s_root = dentry;
|
|
Packit Service |
a1973e |
s->s_flags |= MS_ACTIVE;
|
|
Packit Service |
a1973e |
return s;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
Enomem:
|
|
Packit Service |
a1973e |
up_write(&s->s_umount);
|
|
Packit Service |
a1973e |
deactivate_super(s);
|
|
Packit Service |
a1973e |
return ERR_PTR(-ENOMEM);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
#undef get_sb_pseudo
|
|
Packit Service |
a1973e |
#define get_sb_pseudo perfctr_get_sb_pseudo
|
|
Packit Service |
a1973e |
#endif /* MODULE && VERSION < 2.6.11 */
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
|
|
Packit Service |
a1973e |
static int
|
|
Packit Service |
a1973e |
vperfctrfs_get_sb(struct file_system_type *fs_type,
|
|
Packit Service |
a1973e |
int flags, const char *dev_name, void *data,
|
|
Packit Service |
a1973e |
struct vfsmount *mnt)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
return get_sb_pseudo(fs_type, "vperfctr:", NULL, VPERFCTRFS_MAGIC, mnt);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
#else
|
|
Packit Service |
a1973e |
static struct super_block *
|
|
Packit Service |
a1973e |
vperfctrfs_get_sb(struct file_system_type *fs_type,
|
|
Packit Service |
a1973e |
int flags, const char *dev_name, void *data)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
return get_sb_pseudo(fs_type, "vperfctr:", NULL, VPERFCTRFS_MAGIC);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static struct file_system_type vperfctrfs_type = {
|
|
Packit Service |
a1973e |
.name = "vperfctrfs",
|
|
Packit Service |
a1973e |
.get_sb = vperfctrfs_get_sb,
|
|
Packit Service |
a1973e |
.kill_sb = kill_anon_super,
|
|
Packit Service |
a1973e |
};
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* XXX: check if s/vperfctr_mnt/vperfctrfs_type.kern_mnt/ would work */
|
|
Packit Service |
a1973e |
static struct vfsmount *vperfctr_mnt;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static int __init vperfctrfs_init(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
int err = register_filesystem(&vperfctrfs_type);
|
|
Packit Service |
a1973e |
if (!err) {
|
|
Packit Service |
a1973e |
vperfctr_mnt = kern_mount(&vperfctrfs_type);
|
|
Packit Service |
a1973e |
if (!IS_ERR(vperfctr_mnt))
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
err = PTR_ERR(vperfctr_mnt);
|
|
Packit Service |
a1973e |
unregister_filesystem(&vperfctrfs_type);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
return err;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void __exit vperfctrfs_exit(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
unregister_filesystem(&vperfctrfs_type);
|
|
Packit Service |
a1973e |
mntput(vperfctr_mnt);
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static struct inode *vperfctr_get_inode(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct inode *inode;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
inode = new_inode(vperfctr_mnt->mnt_sb);
|
|
Packit Service |
a1973e |
if (!inode)
|
|
Packit Service |
a1973e |
return NULL;
|
|
Packit Service |
a1973e |
inode->i_fop = &vperfctr_file_ops;
|
|
Packit Service |
a1973e |
inode->i_state = I_DIRTY;
|
|
Packit Service |
a1973e |
inode->i_mode = S_IFCHR | S_IRUSR | S_IWUSR;
|
|
Packit Service |
a1973e |
inode->i_uid = current_fsuid();
|
|
Packit Service |
a1973e |
inode->i_gid = current_fsgid();
|
|
Packit Service |
a1973e |
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
|
|
Packit Service |
a1973e |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) && !defined(DONT_HAVE_i_blksize)
|
|
Packit Service |
a1973e |
inode->i_blksize = 0;
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
return inode;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static int vperfctrfs_delete_dentry(struct dentry *dentry)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
Packit Service |
a1973e |
/*
|
|
Packit Service |
a1973e |
* At creation time, we pretended this dentry was hashed
|
|
Packit Service |
a1973e |
* (by clearing DCACHE_UNHASHED bit in d_flags)
|
|
Packit Service |
a1973e |
* At delete time, we restore the truth : not hashed.
|
|
Packit Service |
a1973e |
* (so that dput() can proceed correctly)
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
dentry->d_flags |= DCACHE_UNHASHED;
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
#else
|
|
Packit Service |
a1973e |
return 1;
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static
|
|
Packit Service |
a1973e |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
|
|
Packit Service |
a1973e |
const
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
struct dentry_operations vperfctrfs_dentry_operations = {
|
|
Packit Service |
a1973e |
.d_delete = vperfctrfs_delete_dentry,
|
|
Packit Service |
a1973e |
};
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static struct dentry *vperfctr_d_alloc_root(struct inode *inode)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct qstr this;
|
|
Packit Service |
a1973e |
char name[32];
|
|
Packit Service |
a1973e |
struct dentry *dentry;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
sprintf(name, "[%lu]", inode->i_ino);
|
|
Packit Service |
a1973e |
this.name = name;
|
|
Packit Service |
a1973e |
this.len = strlen(name);
|
|
Packit Service |
a1973e |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
Packit Service |
a1973e |
this.hash = 0;
|
|
Packit Service |
a1973e |
#else
|
|
Packit Service |
a1973e |
this.hash = inode->i_ino; /* will go */
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
dentry = d_alloc(vperfctr_mnt->mnt_sb->s_root, &this;;
|
|
Packit Service |
a1973e |
if (dentry) {
|
|
Packit Service |
a1973e |
dentry->d_op = &vperfctrfs_dentry_operations;
|
|
Packit Service |
a1973e |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
|
|
Packit Service |
a1973e |
/*
|
|
Packit Service |
a1973e |
* We dont want to publish this dentry into global dentry hash table.
|
|
Packit Service |
a1973e |
* We pretend dentry is already hashed, by unsetting DCACHE_UNHASHED
|
|
Packit Service |
a1973e |
* This permits a working /proc/$pid/fd/XXX on vperfctrs
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
dentry->d_flags &= ~DCACHE_UNHASHED;
|
|
Packit Service |
a1973e |
d_instantiate(dentry, inode);
|
|
Packit Service |
a1973e |
#else
|
|
Packit Service |
a1973e |
d_add(dentry, inode);
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
return dentry;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static struct file *vperfctr_get_filp(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct file *filp;
|
|
Packit Service |
a1973e |
struct inode *inode;
|
|
Packit Service |
a1973e |
struct dentry *dentry;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
inode = vperfctr_get_inode();
|
|
Packit Service |
a1973e |
if (!inode)
|
|
Packit Service |
a1973e |
goto out;
|
|
Packit Service |
a1973e |
dentry = vperfctr_d_alloc_root(inode);
|
|
Packit Service |
a1973e |
if (!dentry)
|
|
Packit Service |
a1973e |
goto out_inode;
|
|
Packit Service |
a1973e |
/*
|
|
Packit Service |
a1973e |
* Create the filp _after_ the inode and dentry, to avoid
|
|
Packit Service |
a1973e |
* needing access to put_filp(), which is no longer exported
|
|
Packit Service |
a1973e |
* starting with kernel 2.6.10-rc1. fput() is available but
|
|
Packit Service |
a1973e |
* doesn't work on incomplete files. We now need access to
|
|
Packit Service |
a1973e |
* dput() instead, but that's Ok.
|
|
Packit Service |
a1973e |
*/
|
|
Packit Service |
a1973e |
filp = get_empty_filp();
|
|
Packit Service |
a1973e |
if (!filp)
|
|
Packit Service |
a1973e |
goto out_dentry;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
filp_vfsmnt(filp) = mntget(vperfctr_mnt);
|
|
Packit Service |
a1973e |
filp_dentry(filp) = dentry;
|
|
Packit Service |
a1973e |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,2)
|
|
Packit Service |
a1973e |
filp->f_mapping = dentry->d_inode->i_mapping;
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
filp->f_pos = 0;
|
|
Packit Service |
a1973e |
filp->f_flags = 0;
|
|
Packit Service |
a1973e |
filp->f_op = fops_get(&vperfctr_file_ops); /* fops_get() for MODULE */
|
|
Packit Service |
a1973e |
filp->f_mode = FMODE_READ;
|
|
Packit Service |
a1973e |
filp->f_version = 0;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
return filp;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
out_dentry:
|
|
Packit Service |
a1973e |
dput(dentry);
|
|
Packit Service |
a1973e |
goto out; /* dput() also does iput() */
|
|
Packit Service |
a1973e |
out_inode:
|
|
Packit Service |
a1973e |
iput(inode);
|
|
Packit Service |
a1973e |
out:
|
|
Packit Service |
a1973e |
return NULL;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/* tid is the actual task/thread id (née pid, stored as ->pid),
|
|
Packit Service |
a1973e |
pid/tgid is that 2.6 thread group id crap (stored as ->tgid) */
|
|
Packit Service |
a1973e |
int vperfctr_attach(int tid, int creat)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
struct file *filp;
|
|
Packit Service |
a1973e |
struct task_struct *tsk;
|
|
Packit Service |
a1973e |
struct vperfctr *perfctr;
|
|
Packit Service |
a1973e |
int err;
|
|
Packit Service |
a1973e |
int fd;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
filp = vperfctr_get_filp();
|
|
Packit Service |
a1973e |
if (!filp)
|
|
Packit Service |
a1973e |
return -ENOMEM;
|
|
Packit Service |
a1973e |
err = fd = get_unused_fd();
|
|
Packit Service |
a1973e |
if (err < 0)
|
|
Packit Service |
a1973e |
goto err_filp;
|
|
Packit Service |
a1973e |
perfctr = NULL;
|
|
Packit Service |
a1973e |
if (creat) {
|
|
Packit Service |
a1973e |
perfctr = get_empty_vperfctr(); /* may sleep */
|
|
Packit Service |
a1973e |
if (IS_ERR(perfctr)) {
|
|
Packit Service |
a1973e |
err = PTR_ERR(perfctr);
|
|
Packit Service |
a1973e |
goto err_fd;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
tsk = current;
|
|
Packit Service |
a1973e |
if (tid != 0 && tid != task_pid_vnr(tsk)) { /* remote? */
|
|
Packit Service |
a1973e |
vperfctr_lock_find_task_by_vpid();
|
|
Packit Service |
a1973e |
tsk = find_task_by_vpid(tid);
|
|
Packit Service |
a1973e |
if (tsk)
|
|
Packit Service |
a1973e |
get_task_struct(tsk);
|
|
Packit Service |
a1973e |
vperfctr_unlock_find_task_by_vpid();
|
|
Packit Service |
a1973e |
err = -ESRCH;
|
|
Packit Service |
a1973e |
if (!tsk)
|
|
Packit Service |
a1973e |
goto err_perfctr;
|
|
Packit Service |
a1973e |
err = ptrace_check_attach(tsk, 0);
|
|
Packit Service |
a1973e |
if (err < 0)
|
|
Packit Service |
a1973e |
goto err_tsk;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
if (creat) {
|
|
Packit Service |
a1973e |
/* check+install must be atomic to prevent remote-control races */
|
|
Packit Service |
a1973e |
vperfctr_task_lock(tsk);
|
|
Packit Service |
a1973e |
if (!tsk->thread.perfctr) {
|
|
Packit Service |
a1973e |
perfctr->owner = tsk;
|
|
Packit Service |
a1973e |
tsk->thread.perfctr = perfctr;
|
|
Packit Service |
a1973e |
err = 0;
|
|
Packit Service |
a1973e |
} else
|
|
Packit Service |
a1973e |
err = -EEXIST;
|
|
Packit Service |
a1973e |
vperfctr_task_unlock(tsk);
|
|
Packit Service |
a1973e |
if (err)
|
|
Packit Service |
a1973e |
goto err_tsk;
|
|
Packit Service |
a1973e |
} else {
|
|
Packit Service |
a1973e |
perfctr = tsk->thread.perfctr;
|
|
Packit Service |
a1973e |
/* PERFCTR_ABI and PERFCTR_INFO don't need the perfctr.
|
|
Packit Service |
a1973e |
Hence no non-NULL check here. */
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
filp->private_data = perfctr;
|
|
Packit Service |
a1973e |
if (perfctr)
|
|
Packit Service |
a1973e |
atomic_inc(&perfctr->count);
|
|
Packit Service |
a1973e |
if (tsk != current)
|
|
Packit Service |
a1973e |
put_task_struct(tsk);
|
|
Packit Service |
a1973e |
fd_install(fd, filp);
|
|
Packit Service |
a1973e |
return fd;
|
|
Packit Service |
a1973e |
err_tsk:
|
|
Packit Service |
a1973e |
if (tsk != current)
|
|
Packit Service |
a1973e |
put_task_struct(tsk);
|
|
Packit Service |
a1973e |
err_perfctr:
|
|
Packit Service |
a1973e |
if (perfctr) /* can only occur if creat != 0 */
|
|
Packit Service |
a1973e |
put_vperfctr(perfctr);
|
|
Packit Service |
a1973e |
err_fd:
|
|
Packit Service |
a1973e |
put_unused_fd(fd);
|
|
Packit Service |
a1973e |
err_filp:
|
|
Packit Service |
a1973e |
fput(filp);
|
|
Packit Service |
a1973e |
return err;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
/****************************************************************
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
* module_init/exit *
|
|
Packit Service |
a1973e |
* *
|
|
Packit Service |
a1973e |
****************************************************************/
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
#ifdef MODULE
|
|
Packit Service |
a1973e |
static struct vperfctr_stub off;
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void vperfctr_stub_init(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
off = vperfctr_stub;
|
|
Packit Service |
a1973e |
vperfctr_stub.owner = THIS_MODULE;
|
|
Packit Service |
a1973e |
vperfctr_stub.exit = __vperfctr_exit;
|
|
Packit Service |
a1973e |
vperfctr_stub.flush = __vperfctr_flush;
|
|
Packit Service |
a1973e |
vperfctr_stub.suspend = __vperfctr_suspend;
|
|
Packit Service |
a1973e |
vperfctr_stub.resume = __vperfctr_resume;
|
|
Packit Service |
a1973e |
vperfctr_stub.sample = __vperfctr_sample;
|
|
Packit Service |
a1973e |
#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK
|
|
Packit Service |
a1973e |
vperfctr_stub.set_cpus_allowed = __vperfctr_set_cpus_allowed;
|
|
Packit Service |
a1973e |
#endif
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
static void vperfctr_stub_exit(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
vperfctr_stub = off;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
#else
|
|
Packit Service |
a1973e |
static inline void vperfctr_stub_init(void) { }
|
|
Packit Service |
a1973e |
static inline void vperfctr_stub_exit(void) { }
|
|
Packit Service |
a1973e |
#endif /* MODULE */
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
int __init vperfctr_init(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
int err = vperfctrfs_init();
|
|
Packit Service |
a1973e |
if (err)
|
|
Packit Service |
a1973e |
return err;
|
|
Packit Service |
a1973e |
vperfctr_stub_init();
|
|
Packit Service |
a1973e |
return 0;
|
|
Packit Service |
a1973e |
}
|
|
Packit Service |
a1973e |
|
|
Packit Service |
a1973e |
void __exit vperfctr_exit(void)
|
|
Packit Service |
a1973e |
{
|
|
Packit Service |
a1973e |
vperfctrfs_exit();
|
|
Packit Service |
a1973e |
vperfctr_stub_exit();
|
|
Packit Service |
a1973e |
}
|