* The initial version of perfmon.c was written by
* Ganesh Venkitachalam, IBM Corp.
*
- * Then it was modified for perfmon-1.x by Stephane Eranian and
+ * Then it was modified for perfmon-1.x by Stephane Eranian and
* David Mosberger, Hewlett Packard Co.
- *
+ *
* Version Perfmon-2.x is a rewrite of perfmon-1.x
- * by Stephane Eranian, Hewlett Packard Co.
+ * by Stephane Eranian, Hewlett Packard Co.
*
- * Copyright (C) 1999-2003 Hewlett Packard Co
+ * Copyright (C) 1999-2003, 2005 Hewlett Packard Co
* Stephane Eranian <eranian@hpl.hp.com>
* David Mosberger-Tang <davidm@hpl.hp.com>
*
#include <linux/pagemap.h>
#include <linux/mount.h>
#include <linux/version.h>
+#include <linux/bitops.h>
+#include <linux/vs_memory.h>
+#include <linux/vs_cvirt.h>
-#include <asm/bitops.h>
#include <asm/errno.h>
#include <asm/intrinsics.h>
#include <asm/page.h>
unsigned int ctx_cpu; /* cpu to which perfmon is applied (system wide) */
int ctx_fd; /* file descriptor used my this context */
+ pfm_ovfl_arg_t ctx_ovfl_arg; /* argument to custom buffer format handler */
pfm_buffer_fmt_t *ctx_buf_fmt; /* buffer format callbacks */
void *ctx_smpl_hdr; /* points to sampling buffer header kernel vaddr */
ClearPageReserved(vmalloc_to_page((void*)a));
}
-static inline int
-pfm_remap_page_range(struct vm_area_struct *vma, unsigned long from, unsigned long phys_addr, unsigned long size, pgprot_t prot)
-{
- return remap_page_range(vma, from, phys_addr, size, prot);
-}
-
static inline unsigned long
pfm_protect_ctx_ctxsw(pfm_context_t *x)
{
DPRINT(("ctx=%p msgq reset\n", ctx));
}
-
-/* Here we want the physical address of the memory.
- * This is used when initializing the contents of the
- * area and marking the pages as reserved.
- */
-static inline unsigned long
-pfm_kvirt_to_pa(unsigned long adr)
-{
- __u64 pa = ia64_tpa(adr);
- return pa;
-}
-
static void *
pfm_rvmalloc(unsigned long size)
{
mntput(pfmfs_mnt);
}
-static loff_t
-pfm_lseek(struct file *file, loff_t offset, int whence)
-{
- DPRINT(("pfm_lseek called\n"));
- return -ESPIPE;
-}
-
static ssize_t
-pfm_read(struct file *filp, char *buf, size_t size, loff_t *ppos)
+pfm_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
pfm_context_t *ctx;
pfm_msg_t *msg;
DPRINT(("message is too small ctx=%p (>=%ld)\n", ctx, sizeof(pfm_msg_t)));
return -EINVAL;
}
- /*
- * seeks are not allowed on message queues
- */
- if (ppos != &filp->f_pos) return -ESPIPE;
PROTECT_CTX(ctx, flags);
}
static ssize_t
-pfm_write(struct file *file, const char *ubuf,
+pfm_write(struct file *file, const char __user *ubuf,
size_t size, loff_t *ppos)
{
DPRINT(("pfm_write called\n"));
}
/*
- * context is locked when coming here and interrupts are disabled
+ * interrupt cannot be masked when coming here
*/
static inline int
pfm_do_fasync(int fd, struct file *filp, pfm_context_t *ctx, int on)
pfm_fasync(int fd, struct file *filp, int on)
{
pfm_context_t *ctx;
- unsigned long flags;
int ret;
if (PFM_IS_FILE(filp) == 0) {
printk(KERN_ERR "perfmon: pfm_fasync NULL ctx [%d]\n", current->pid);
return -EBADF;
}
-
-
- PROTECT_CTX(ctx, flags);
-
+ /*
+ * we cannot mask interrupts during this call because this may
+ * may go to sleep if memory is not readily avalaible.
+ *
+ * We are protected from the conetxt disappearing by the get_fd()/put_fd()
+ * done in caller. Serialization of this function is ensured by caller.
+ */
ret = pfm_do_fasync(fd, filp, ctx, on);
+
DPRINT(("pfm_fasync called on ctx_fd=%d on=%d async_queue=%p ret=%d\n",
fd,
on,
ctx->ctx_async_queue, ret));
- UNPROTECT_CTX(ctx, flags);
-
return ret;
}
/*
* XXX: check for signals :
- * - ok of explicit close
+ * - ok for explicit close
* - not ok when coming from exit_files()
*/
schedule();
static struct file_operations pfm_file_ops = {
- .llseek = pfm_lseek,
+ .llseek = no_llseek,
.read = pfm_read,
.write = pfm_write,
.poll = pfm_poll,
DPRINT(("new inode ino=%ld @%p\n", inode->i_ino, inode));
- inode->i_sb = pfmfs_mnt->mnt_sb;
inode->i_mode = S_IFCHR|S_IRUGO;
- inode->i_sock = 0;
inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid;
static void
pfm_free_fd(int fd, struct file *file)
{
+ struct files_struct *files = current->files;
+
+ /*
+ * there ie no fd_uninstall(), so we do it here
+ */
+ spin_lock(&files->file_lock);
+ files->fd[fd] = NULL;
+ spin_unlock(&files->file_lock);
+
if (file) put_filp(file);
put_unused_fd(fd);
}
static int
pfm_remap_buffer(struct vm_area_struct *vma, unsigned long buf, unsigned long addr, unsigned long size)
{
- unsigned long page;
-
DPRINT(("CPU%d buf=0x%lx addr=0x%lx size=%ld\n", smp_processor_id(), buf, addr, size));
while (size > 0) {
- page = pfm_kvirt_to_pa(buf);
+ unsigned long pfn = ia64_tpa(buf) >> PAGE_SHIFT;
- if (pfm_remap_page_range(vma, addr, page, PAGE_SIZE, PAGE_READONLY)) return -ENOMEM;
+
+ if (remap_pfn_range(vma, addr, pfn, PAGE_SIZE, PAGE_READONLY))
+ return -ENOMEM;
addr += PAGE_SIZE;
buf += PAGE_SIZE;
* if ((mm->total_vm << PAGE_SHIFT) + len> task->rlim[RLIMIT_AS].rlim_cur)
* return -ENOMEM;
*/
- if (size > task->rlim[RLIMIT_MEMLOCK].rlim_cur) return -EAGAIN;
+ if (size > task->signal->rlim[RLIMIT_MEMLOCK].rlim_cur)
+ return -ENOMEM;
/*
* We do the easy to undo allocations first.
/*
* partially initialize the vma for the sampling buffer
- *
- * The VM_DONTCOPY flag is very important as it ensures that the mapping
- * will never be inherited for any child process (via fork()) which is always
- * what we want.
*/
vma->vm_mm = mm;
vma->vm_flags = VM_READ| VM_MAYREAD |VM_RESERVED;
goto error;
}
vma->vm_end = vma->vm_start + size;
+ vma->vm_pgoff = vma->vm_start >> PAGE_SHIFT;
DPRINT(("aligned size=%ld, hdr=%p mapped @0x%lx\n", size, ctx->ctx_smpl_hdr, vma->vm_start));
*/
insert_vm_struct(mm, vma);
- // mm->total_vm += size >> PAGE_SHIFT;
vx_vmpages_add(mm, size >> PAGE_SHIFT);
-
+ vm_stat_account(vma);
up_write(&task->mm->mmap_sem);
/*
return -EINVAL;
}
- if (task->state == TASK_ZOMBIE) {
+ if (task->exit_state == EXIT_ZOMBIE) {
DPRINT(("cannot attach to zombie task [%d]\n", task->pid));
return -EBUSY;
}
*/
if (task == current) return 0;
- if (task->state != TASK_STOPPED) {
+ if ((task->state != TASK_STOPPED) && (task->state != TASK_TRACED)) {
DPRINT(("cannot attach to non-stopped task [%d] state=%ld\n", task->pid, task->state));
return -EBUSY;
}
ctx = pfm_context_alloc();
if (!ctx) goto error;
- req->ctx_fd = ctx->ctx_fd = pfm_alloc_fd(&filp);
- if (req->ctx_fd < 0) goto error_file;
+ ret = pfm_alloc_fd(&filp);
+ if (ret < 0) goto error_file;
+
+ req->ctx_fd = ctx->ctx_fd = ret;
/*
* attach context to file
#endif
}
- DPRINT(("pmc[%u]=0x%lx loaded=%d access_pmu=%d all_pmcs=0x%lx used_pmds=0x%lx eventid=%ld smpl_pmds=0x%lx reset_pmds=0x%lx reloads_pmcs=0x%lx used_monitors=0x%lx ovfl_regs=0x%lx\n",
+ DPRINT(("pmc[%u]=0x%lx ld=%d apmu=%d flags=0x%x all_pmcs=0x%lx used_pmds=0x%lx eventid=%ld smpl_pmds=0x%lx reset_pmds=0x%lx reloads_pmcs=0x%lx used_monitors=0x%lx ovfl_regs=0x%lx\n",
cnum,
value,
is_loaded,
can_access_pmu,
+ flags,
ctx->ctx_all_pmcs[0],
ctx->ctx_used_pmds[0],
ctx->ctx_pmds[cnum].eventid,
}
}
- DPRINT(("pmd[%u]=0x%lx loaded=%d access_pmu=%d, hw_value=0x%lx ctx_pmd=0x%lx short_reset=0x%lx "
- "long_reset=0x%lx notify=%c used_pmds=0x%lx reset_pmds=0x%lx reload_pmds=0x%lx all_pmds=0x%lx ovfl_regs=0x%lx\n",
+ DPRINT(("pmd[%u]=0x%lx ld=%d apmu=%d, hw_value=0x%lx ctx_pmd=0x%lx short_reset=0x%lx "
+ "long_reset=0x%lx notify=%c seed=0x%lx mask=0x%lx used_pmds=0x%lx reset_pmds=0x%lx reload_pmds=0x%lx all_pmds=0x%lx ovfl_regs=0x%lx\n",
cnum,
value,
is_loaded,
ctx->ctx_pmds[cnum].short_reset,
ctx->ctx_pmds[cnum].long_reset,
PMC_OVFL_NOTIFY(ctx, cnum) ? 'Y':'N',
+ ctx->ctx_pmds[cnum].seed,
+ ctx->ctx_pmds[cnum].mask,
ctx->ctx_used_pmds[0],
ctx->ctx_pmds[cnum].reset_pmds[0],
ctx->ctx_reload_pmds[0],
}
expert_mode = pfm_sysctl.expert_mode;
- DPRINT(("loaded=%d access_pmu=%d ctx_state=%d\n",
+ DPRINT(("ld=%d apmu=%d ctx_state=%d\n",
is_loaded,
can_access_pmu,
state));
ctx->ctx_ibrs[rnum] = dbreg.val;
- DPRINT(("write ibr%u=0x%lx used_ibrs=0x%x is_loaded=%d access_pmu=%d\n",
+ DPRINT(("write ibr%u=0x%lx used_ibrs=0x%x ld=%d apmu=%d\n",
rnum, dbreg.val, ctx->ctx_used_ibrs[0], is_loaded, can_access_pmu));
} else {
CTX_USED_DBR(ctx, rnum);
}
ctx->ctx_dbrs[rnum] = dbreg.val;
- DPRINT(("write dbr%u=0x%lx used_dbrs=0x%x is_loaded=%d access_pmu=%d\n",
+ DPRINT(("write dbr%u=0x%lx used_dbrs=0x%x ld=%d apmu=%d\n",
rnum, dbreg.val, ctx->ctx_used_dbrs[0], is_loaded, can_access_pmu));
}
}
state = ctx->ctx_state;
is_system = ctx->ctx_fl_system;
- if (state != PFM_CTX_LOADED && state != PFM_CTX_MASKED) return -EINVAL;
+ /*
+ * context must be attached to issue the stop command (includes LOADED,MASKED,ZOMBIE)
+ */
+ if (state == PFM_CTX_UNLOADED) return -EINVAL;
/*
* In system wide and when the context is loaded, access can only happen
return 0;
}
-static void
-pfm_force_cleanup(pfm_context_t *ctx, struct pt_regs *regs)
-{
- struct task_struct *task = ctx->ctx_task;
-
- ia64_psr(regs)->up = 0;
- ia64_psr(regs)->sp = 1;
-
- if (GET_PMU_OWNER() == task) {
- DPRINT(("cleared ownership for [%d]\n", ctx->ctx_task->pid));
- SET_PMU_OWNER(NULL, NULL);
- }
-
- /*
- * disconnect the task from the context and vice-versa
- */
- PFM_SET_WORK_PENDING(task, 0);
-
- task->thread.pfm_context = NULL;
- task->thread.flags &= ~IA64_THREAD_PM_VALID;
-
- DPRINT(("force cleanupf for [%d]\n", task->pid));
-}
-
-
/*
* called only from exit_thread(): task == current
pfm_check_task_state(pfm_context_t *ctx, int cmd, unsigned long flags)
{
struct task_struct *task;
- int state;
+ int state, old_state;
+recheck:
state = ctx->ctx_state;
+ task = ctx->ctx_task;
- task = PFM_CTX_TASK(ctx);
if (task == NULL) {
DPRINT(("context %d no task, state=%d\n", ctx->ctx_fd, state));
return 0;
}
DPRINT(("context %d state=%d [%d] task_state=%ld must_stop=%d\n",
- ctx->ctx_fd,
- state,
- task->pid,
- task->state, PFM_CMD_STOPPED(cmd)));
+ ctx->ctx_fd,
+ state,
+ task->pid,
+ task->state, PFM_CMD_STOPPED(cmd)));
/*
* self-monitoring always ok.
if (task == current || ctx->ctx_fl_system) return 0;
/*
- * context is UNLOADED, MASKED we are safe to go
+ * if context is UNLOADED we are safe to go
*/
- if (state != PFM_CTX_LOADED) return 0;
+ if (state == PFM_CTX_UNLOADED) return 0;
- if (state == PFM_CTX_ZOMBIE) return -EINVAL;
+ /*
+ * no command can operate on a zombie context
+ */
+ if (state == PFM_CTX_ZOMBIE) {
+ DPRINT(("cmd %d state zombie cannot operate on context\n", cmd));
+ return -EINVAL;
+ }
/*
- * context is loaded, we must make sure the task is stopped
+ * context is LOADED or MASKED. Some commands may need to have
+ * the task stopped.
+ *
* We could lift this restriction for UP but it would mean that
* the user has no guarantee the task would not run between
* two successive calls to perfmonctl(). That's probably OK.
* If this user wants to ensure the task does not run, then
* the task must be stopped.
*/
- if (PFM_CMD_STOPPED(cmd) && task->state != TASK_STOPPED) {
- DPRINT(("[%d] task not in stopped state\n", task->pid));
- return -EBUSY;
- }
+ if (PFM_CMD_STOPPED(cmd)) {
+ if ((task->state != TASK_STOPPED) && (task->state != TASK_TRACED)) {
+ DPRINT(("[%d] task not in stopped state\n", task->pid));
+ return -EBUSY;
+ }
+ /*
+ * task is now stopped, wait for ctxsw out
+ *
+ * This is an interesting point in the code.
+ * We need to unprotect the context because
+ * the pfm_save_regs() routines needs to grab
+ * the same lock. There are danger in doing
+ * this because it leaves a window open for
+ * another task to get access to the context
+ * and possibly change its state. The one thing
+ * that is not possible is for the context to disappear
+ * because we are protected by the VFS layer, i.e.,
+ * get_fd()/put_fd().
+ */
+ old_state = state;
- UNPROTECT_CTX(ctx, flags);
+ UNPROTECT_CTX(ctx, flags);
- wait_task_inactive(task);
+ wait_task_inactive(task);
- PROTECT_CTX(ctx, flags);
+ PROTECT_CTX(ctx, flags);
+ /*
+ * we must recheck to verify if state has changed
+ */
+ if (ctx->ctx_state != old_state) {
+ DPRINT(("old_state=%d new_state=%d\n", old_state, ctx->ctx_state));
+ goto recheck;
+ }
+ }
return 0;
}
* system-call entry point (must return long)
*/
asmlinkage long
-sys_perfmonctl (int fd, int cmd, void *arg, int count, long arg5, long arg6, long arg7,
- long arg8, long stack)
+sys_perfmonctl (int fd, int cmd, void __user *arg, int count)
{
- struct pt_regs *regs = (struct pt_regs *)&stack;
struct file *file = NULL;
pfm_context_t *ctx = NULL;
unsigned long flags = 0UL;
if (unlikely(ret)) goto abort_locked;
skip_fd:
- ret = (*func)(ctx, args_k, count, regs);
+ ret = (*func)(ctx, args_k, count, ia64_task_regs(current));
call_made = 1;
static void
pfm_context_force_terminate(pfm_context_t *ctx, struct pt_regs *regs)
{
- if (ctx->ctx_fl_system) {
- printk(KERN_ERR "perfmon: pfm_context_force_terminate [%d] is system-wide\n", current->pid);
- return;
- }
- /*
- * we stop the whole thing, we do no need to flush
- * we know we WERE masked
- */
- pfm_clear_psr_up();
- ia64_psr(regs)->up = 0;
- ia64_psr(regs)->sp = 1;
+ int ret;
- /*
- * disconnect the task from the context and vice-versa
- */
- current->thread.pfm_context = NULL;
- current->thread.flags &= ~IA64_THREAD_PM_VALID;
- ctx->ctx_task = NULL;
+ DPRINT(("entering for [%d]\n", current->pid));
- DPRINT(("context terminated\n"));
+ ret = pfm_context_unload(ctx, NULL, 0, regs);
+ if (ret) {
+ printk(KERN_ERR "pfm_context_force_terminate: [%d] unloaded failed with %d\n", current->pid, ret);
+ }
/*
* and wakeup controlling task, indicating we are now disconnected
UNPROTECT_CTX(ctx, flags);
+ /*
+ * pfm_handle_work() is currently called with interrupts disabled.
+ * The down_interruptible call may sleep, therefore we
+ * must re-enable interrupts to avoid deadlocks. It is
+ * safe to do so because this function is called ONLY
+ * when returning to user level (PUStk=1), in which case
+ * there is no risk of kernel stack overflow due to deep
+ * interrupt nesting.
+ */
+ BUG_ON(flags & IA64_PSR_I);
+ local_irq_enable();
+
DPRINT(("before block sleeping\n"));
/*
DPRINT(("after block sleeping ret=%d\n", ret));
+ /*
+ * disable interrupts to restore state we had upon entering
+ * this function
+ */
+ local_irq_disable();
+
PROTECT_CTX(ctx, flags);
/*
static void
pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, struct pt_regs *regs)
{
- pfm_ovfl_arg_t ovfl_arg;
+ pfm_ovfl_arg_t *ovfl_arg;
unsigned long mask;
unsigned long old_val, ovfl_val, new_val;
unsigned long ovfl_notify = 0UL, ovfl_pmds = 0UL, smpl_pmds = 0UL, reset_pmds;
int j, k, ret = 0;
int this_cpu = smp_processor_id();
- pmd_mask = ovfl_pmds >> PMU_FIRST_COUNTER;
+ pmd_mask = ovfl_pmds >> PMU_FIRST_COUNTER;
+ ovfl_arg = &ctx->ctx_ovfl_arg;
prefetch(ctx->ctx_smpl_hdr);
if ((pmd_mask & 0x1) == 0) continue;
- ovfl_arg.ovfl_pmd = (unsigned char )i;
- ovfl_arg.ovfl_notify = ovfl_notify & mask ? 1 : 0;
- ovfl_arg.active_set = 0;
- ovfl_arg.ovfl_ctrl.val = 0; /* module must fill in all fields */
- ovfl_arg.smpl_pmds[0] = smpl_pmds = ctx->ctx_pmds[i].smpl_pmds[0];
+ ovfl_arg->ovfl_pmd = (unsigned char )i;
+ ovfl_arg->ovfl_notify = ovfl_notify & mask ? 1 : 0;
+ ovfl_arg->active_set = 0;
+ ovfl_arg->ovfl_ctrl.val = 0; /* module must fill in all fields */
+ ovfl_arg->smpl_pmds[0] = smpl_pmds = ctx->ctx_pmds[i].smpl_pmds[0];
- ovfl_arg.pmd_value = ctx->ctx_pmds[i].val;
- ovfl_arg.pmd_last_reset = ctx->ctx_pmds[i].lval;
- ovfl_arg.pmd_eventid = ctx->ctx_pmds[i].eventid;
+ ovfl_arg->pmd_value = ctx->ctx_pmds[i].val;
+ ovfl_arg->pmd_last_reset = ctx->ctx_pmds[i].lval;
+ ovfl_arg->pmd_eventid = ctx->ctx_pmds[i].eventid;
/*
* copy values of pmds of interest. Sampling format may copy them
if (smpl_pmds) {
for(j=0, k=0; smpl_pmds; j++, smpl_pmds >>=1) {
if ((smpl_pmds & 0x1) == 0) continue;
- ovfl_arg.smpl_pmds_values[k++] = PMD_IS_COUNTING(j) ? pfm_read_soft_counter(ctx, j) : ia64_get_pmd(j);
- DPRINT_ovfl(("smpl_pmd[%d]=pmd%u=0x%lx\n", k-1, j, ovfl_arg.smpl_pmds_values[k-1]));
+ ovfl_arg->smpl_pmds_values[k++] = PMD_IS_COUNTING(j) ? pfm_read_soft_counter(ctx, j) : ia64_get_pmd(j);
+ DPRINT_ovfl(("smpl_pmd[%d]=pmd%u=0x%lx\n", k-1, j, ovfl_arg->smpl_pmds_values[k-1]));
}
}
/*
* call custom buffer format record (handler) routine
*/
- ret = (*ctx->ctx_buf_fmt->fmt_handler)(task, ctx->ctx_smpl_hdr, &ovfl_arg, regs, tstamp);
+ ret = (*ctx->ctx_buf_fmt->fmt_handler)(task, ctx->ctx_smpl_hdr, ovfl_arg, regs, tstamp);
end_cycles = ia64_get_itc();
* For those controls, we take the union because they have
* an all or nothing behavior.
*/
- ovfl_ctrl.bits.notify_user |= ovfl_arg.ovfl_ctrl.bits.notify_user;
- ovfl_ctrl.bits.block_task |= ovfl_arg.ovfl_ctrl.bits.block_task;
- ovfl_ctrl.bits.mask_monitoring |= ovfl_arg.ovfl_ctrl.bits.mask_monitoring;
+ ovfl_ctrl.bits.notify_user |= ovfl_arg->ovfl_ctrl.bits.notify_user;
+ ovfl_ctrl.bits.block_task |= ovfl_arg->ovfl_ctrl.bits.block_task;
+ ovfl_ctrl.bits.mask_monitoring |= ovfl_arg->ovfl_ctrl.bits.mask_monitoring;
/*
* build the bitmask of pmds to reset now
*/
- if (ovfl_arg.ovfl_ctrl.bits.reset_ovfl_pmds) reset_pmds |= mask;
+ if (ovfl_arg->ovfl_ctrl.bits.reset_ovfl_pmds) reset_pmds |= mask;
pfm_stats[this_cpu].pfm_smpl_handler_cycles += end_cycles - start_cycles;
}
if (ovfl_notify == 0) reset_pmds = ovfl_pmds;
}
- DPRINT(("ovfl_pmds=0x%lx reset_pmds=0x%lx\n",
- ovfl_pmds,
- reset_pmds));
+ DPRINT_ovfl(("ovfl_pmds=0x%lx reset_pmds=0x%lx\n", ovfl_pmds, reset_pmds));
+
/*
* reset the requested PMD registers using the short reset values
*/
}
#ifdef CONFIG_SMP
+
+static void
+pfm_force_cleanup(pfm_context_t *ctx, struct pt_regs *regs)
+{
+ struct task_struct *task = ctx->ctx_task;
+
+ ia64_psr(regs)->up = 0;
+ ia64_psr(regs)->sp = 1;
+
+ if (GET_PMU_OWNER() == task) {
+ DPRINT(("cleared ownership for [%d]\n", ctx->ctx_task->pid));
+ SET_PMU_OWNER(NULL, NULL);
+ }
+
+ /*
+ * disconnect the task from the context and vice-versa
+ */
+ PFM_SET_WORK_PENDING(task, 0);
+
+ task->thread.pfm_context = NULL;
+ task->thread.flags &= ~IA64_THREAD_PM_VALID;
+
+ DPRINT(("force cleanup for [%d]\n", task->pid));
+}
+
+
/*
* in 2.6, interrupts are masked when we come here and the runqueue lock is held
*/
return;
}
- /*
- * sanity check
- */
- if (ctx->ctx_last_activation != GET_ACTIVATION()) {
- pfm_unprotect_ctx_ctxsw(ctx, flags);
- return;
- }
-
/*
* save current PSR: needed because we modify it
*/
*/
is_self = ctx->ctx_task == task ? 1 : 0;
-#ifdef CONFIG_SMP
- if (task == current) {
-#else
/*
- * in UP, the state can still be in the registers
+ * can access PMU is task is the owner of the PMU state on the current CPU
+ * or if we are running on the CPU bound to the context in system-wide mode
+ * (that is not necessarily the task the context is attached to in this mode).
+ * In system-wide we always have can_access_pmu true because a task running on an
+ * invalid processor is flagged earlier in the call stack (see pfm_stop).
*/
- if (task == current || GET_PMU_OWNER() == task) {
-#endif
- can_access_pmu = 1;
+ can_access_pmu = (GET_PMU_OWNER() == task) || (ctx->ctx_fl_system && ctx->ctx_cpu == smp_processor_id());
+ if (can_access_pmu) {
/*
* Mark the PMU as not owned
* This will cause the interrupt handler to do nothing in case an overflow
* on.
*/
SET_PMU_OWNER(NULL, NULL);
+ DPRINT(("releasing ownership\n"));
/*
* read current overflow status:
* XXX: sampling situation is not taken into account here
*/
mask2 = ctx->ctx_used_pmds[0];
+
+ DPRINT(("is_self=%d ovfl_val=0x%lx mask2=0x%lx\n", is_self, ovfl_val, mask2));
+
for (i = 0; mask2; i++, mask2>>=1) {
/* skip non used pmds */
}
}
- DPRINT(("[%d] is_self=%d ctx_pmd[%d]=0x%lx pmd_val=0x%lx\n", task->pid, is_self, i, val, pmd_val));
+ DPRINT(("[%d] ctx_pmd[%d]=0x%lx pmd_val=0x%lx\n", task->pid, i, val, pmd_val));
if (is_self) task->thread.pmds[i] = pmd_val;
}
#else /* !CONFIG_PERFMON */
asmlinkage long
-sys_perfmonctl (int fd, int cmd, void *arg, int count, long arg5, long arg6, long arg7,
- long arg8, long stack)
+sys_perfmonctl (int fd, int cmd, void *arg, int count)
{
return -ENOSYS;
}