Subject: spufs: add infrastructure to support spu profiling In order for oprofile to find the location in an spu-elf binary where an event counter triggered, we need a way to identify the binary in the first place. Unfortunately, that binary itself can be embedded in a powerpc ELF binary. Since we can assume it is mapped into the effective address space of the running process, have that one write the pointer value into a new spufs file. When a context switch occurs, pass the user value to the profiler so that can look at the mapped file (with some care). Signed-off-by: Arnd Bergmann Index: linus-2.6/arch/powerpc/platforms/cell/spufs/file.c =================================================================== --- linus-2.6.orig/arch/powerpc/platforms/cell/spufs/file.c +++ linus-2.6/arch/powerpc/platforms/cell/spufs/file.c @@ -1344,6 +1344,21 @@ static u64 spufs_id_get(void *data) } DEFINE_SIMPLE_ATTRIBUTE(spufs_id_ops, spufs_id_get, 0, "0x%llx\n") +static u64 spufs_object_id_get(void *data) +{ + struct spu_context *ctx = data; + return ctx->object_id; +} + +static void spufs_object_id_set(void *data, u64 id) +{ + struct spu_context *ctx = data; + ctx->object_id = id; +} + +DEFINE_SIMPLE_ATTRIBUTE(spufs_object_id_ops, spufs_object_id_get, + spufs_object_id_set, "0x%llx\n"); + struct tree_descr spufs_dir_contents[] = { { "mem", &spufs_mem_fops, 0666, }, { "regs", &spufs_regs_fops, 0666, }, @@ -1368,5 +1383,6 @@ struct tree_descr spufs_dir_contents[] = { "event_mask", &spufs_event_mask_ops, 0666, }, { "srr0", &spufs_srr0_ops, 0666, }, { "phys-id", &spufs_id_ops, 0666, }, + { "object-id", &spufs_object_id_ops, 0666, }, {}, }; Index: linus-2.6/arch/powerpc/platforms/cell/spufs/spufs.h =================================================================== --- linus-2.6.orig/arch/powerpc/platforms/cell/spufs/spufs.h +++ linus-2.6/arch/powerpc/platforms/cell/spufs/spufs.h @@ -48,6 +48,7 @@ struct spu_context { struct address_space *cntl; /* 'control' area mappings. */ struct address_space *signal1; /* 'signal1' area mappings. */ struct address_space *signal2; /* 'signal2' area mappings. */ + u64 object_id; /* user space pointer for oprofile */ enum { SPU_STATE_RUNNABLE, SPU_STATE_SAVED } state; struct rw_semaphore state_sema; Index: linus-2.6/arch/powerpc/platforms/cell/spufs/sched.c =================================================================== --- linus-2.6.orig/arch/powerpc/platforms/cell/spufs/sched.c +++ linus-2.6/arch/powerpc/platforms/cell/spufs/sched.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -75,6 +76,25 @@ static inline void mm_needs_global_tlbie __cpus_setall(&mm->cpu_vm_mask, nr); } +static BLOCKING_NOTIFIER_HEAD(spu_switch_notifier); + +static void spu_switch_notify(struct spu *spu, struct spu_context *ctx) +{ + blocking_notifier_call_chain(&spu_switch_notifier, + ctx ? ctx->object_id : 0, spu); +} + +int spu_switch_event_register(struct notifier_block * n) +{ + return blocking_notifier_chain_register(&spu_switch_notifier, n); +} + +int spu_switch_event_unregister(struct notifier_block * n) +{ + return blocking_notifier_chain_unregister(&spu_switch_notifier, n); +} + + static inline void bind_context(struct spu *spu, struct spu_context *ctx) { pr_debug("%s: pid=%d SPU=%d NODE=%d\n", __FUNCTION__, current->pid, @@ -97,12 +117,14 @@ static inline void bind_context(struct s spu_restore(&ctx->csa, spu); spu->timestamp = jiffies; spu_irq_setaffinity(spu, raw_smp_processor_id()); + spu_switch_notify(spu, ctx); } static inline void unbind_context(struct spu *spu, struct spu_context *ctx) { pr_debug("%s: unbind pid=%d SPU=%d NODE=%d\n", __FUNCTION__, spu->pid, spu->number, spu->node); + spu_switch_notify(spu, 0); spu_unmap_mappings(ctx); spu_save(&ctx->csa, spu); spu->timestamp = jiffies; Index: linus-2.6/include/asm-powerpc/spu.h =================================================================== --- linus-2.6.orig/include/asm-powerpc/spu.h +++ linus-2.6/include/asm-powerpc/spu.h @@ -184,6 +184,24 @@ static inline void unregister_spu_syscal #endif /* MODULE */ +/* + * Notifier blocks: + * + * oprofile can get notified when a context switch is performed + * on an spe. The notifer function that gets called is passed + * a pointer to the SPU structure as well as the object-id that + * identifies the binary running on that SPU now. + * + * For a context save, the object-id that is passed is zero, + * identifying that the kernel will run from that moment on. + * + * For a context restore, the object-id is the value written + * to object-id spufs file from user space and the notifer + * function can assume that spu->ctx is valid. + */ +int spu_switch_event_register(struct notifier_block * n); +int spu_switch_event_unregister(struct notifier_block * n); + /* access to priv1 registers */ void spu_int_mask_and(struct spu *spu, int class, u64 mask); void spu_int_mask_or(struct spu *spu, int class, u64 mask);