}
 DEFINE_SIMPLE_ATTRIBUTE(spufs_id_ops, spufs_id_get, NULL, "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, },
        { "spu_tag_mask", &spufs_spu_tag_mask_ops, 0666, },
        { "event_mask", &spufs_event_mask_ops, 0666, },
        { "srr0", &spufs_srr0_ops, 0666, },
-       { "phys-id", &spufs_id_ops, 0666, },
        { "psmap", &spufs_psmap_fops, 0666, },
+       { "phys-id", &spufs_id_ops, 0666, },
+       { "object-id", &spufs_object_id_ops, 0666, },
        {},
 };
 
 #include <linux/unistd.h>
 #include <linux/numa.h>
 #include <linux/mutex.h>
+#include <linux/notifier.h>
 
 #include <asm/io.h>
 #include <asm/mmu_context.h>
        __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,
        spu_restore(&ctx->csa, spu);
        spu->timestamp = jiffies;
        spu_cpu_affinity_set(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, NULL);
        spu_unmap_mappings(ctx);
        spu_save(&ctx->csa, spu);
        spu->timestamp = jiffies;
 
        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;
 
 #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);
+
 /*
  * This defines the Local Store, Problem Area and Privlege Area of an SPU.
  */