[CELL] spufs: add spu stats in sysfs and ctx stat file in spufs
authorAndre Detsch <adetsch@br.ibm.com>
Fri, 20 Jul 2007 19:39:33 +0000 (21:39 +0200)
committerArnd Bergmann <arnd@klappe.arndb.de>
Fri, 20 Jul 2007 19:41:50 +0000 (21:41 +0200)
This patch exports per-context statistics in spufs as long as spu
statistics in sysfs.

It was formed by merging:
"spufs: add spu stats in sysfs"   From: Christoph Hellwig
"spufs: add stat file to spufs"   From: Christoph Hellwig
"spufs: fix libassist accounting" From: Jeremy Kerr
"spusched: fix spu utilization statistics" From: Luke Browning
And some adjustments by myself, after suggestions on cbe-oss-dev.

Having separate patches was making the review process harder
than it should, as we end up integrating spus and ctx statistics
accounting much more than it was on the first implementation.

Signed-off-by: Andre Detsch <adetsch@br.ibm.com>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com>
arch/powerpc/platforms/cell/spu_base.c
arch/powerpc/platforms/cell/spufs/context.c
arch/powerpc/platforms/cell/spufs/fault.c
arch/powerpc/platforms/cell/spufs/file.c
arch/powerpc/platforms/cell/spufs/run.c
arch/powerpc/platforms/cell/spufs/sched.c
arch/powerpc/platforms/cell/spufs/spufs.h
include/asm-powerpc/spu.h

index c563066e640dc5b865731265401607adb1c74234..caaf2bf78cad908a64d867ffb0dc6e2a58ffd20e 100644 (file)
@@ -553,6 +553,7 @@ static int __init create_spu(void *data)
        int ret;
        static int number;
        unsigned long flags;
+       struct timespec ts;
 
        ret = -ENOMEM;
        spu = kzalloc(sizeof (*spu), GFP_KERNEL);
@@ -586,8 +587,9 @@ static int __init create_spu(void *data)
        spin_unlock_irqrestore(&spu_list_lock, flags);
        mutex_unlock(&spu_mutex);
 
-       spu->stats.utilization_state = SPU_UTIL_IDLE;
-       spu->stats.tstamp = jiffies;
+       spu->stats.util_state = SPU_UTIL_IDLE_LOADED;
+       ktime_get_ts(&ts);
+       spu->stats.tstamp = timespec_to_ns(&ts);
 
        goto out;
 
@@ -608,12 +610,20 @@ static const char *spu_state_names[] = {
 static unsigned long long spu_acct_time(struct spu *spu,
                enum spu_utilization_state state)
 {
+       struct timespec ts;
        unsigned long long time = spu->stats.times[state];
 
-       if (spu->stats.utilization_state == state)
-               time += jiffies - spu->stats.tstamp;
+       /*
+        * If the spu is idle or the context is stopped, utilization
+        * statistics are not updated.  Apply the time delta from the
+        * last recorded state of the spu.
+        */
+       if (spu->stats.util_state == state) {
+               ktime_get_ts(&ts);
+               time += timespec_to_ns(&ts) - spu->stats.tstamp;
+       }
 
-       return jiffies_to_msecs(time);
+       return time / NSEC_PER_MSEC;
 }
 
 
@@ -623,11 +633,11 @@ static ssize_t spu_stat_show(struct sys_device *sysdev, char *buf)
 
        return sprintf(buf, "%s %llu %llu %llu %llu "
                      "%llu %llu %llu %llu %llu %llu %llu %llu\n",
-               spu_state_names[spu->stats.utilization_state],
+               spu_state_names[spu->stats.util_state],
                spu_acct_time(spu, SPU_UTIL_USER),
                spu_acct_time(spu, SPU_UTIL_SYSTEM),
                spu_acct_time(spu, SPU_UTIL_IOWAIT),
-               spu_acct_time(spu, SPU_UTIL_IDLE),
+               spu_acct_time(spu, SPU_UTIL_IDLE_LOADED),
                spu->stats.vol_ctx_switch,
                spu->stats.invol_ctx_switch,
                spu->stats.slb_flt,
index 6d7bd60f5380bf22a477e309b570145a5062df44..0e5e55f53c8b7d7134fa6c663d0ff8dbc812087c 100644 (file)
@@ -59,8 +59,7 @@ struct spu_context *alloc_spu_context(struct spu_gang *gang)
                spu_gang_add_ctx(gang, ctx);
        ctx->cpus_allowed = current->cpus_allowed;
        spu_set_timeslice(ctx);
-       ctx->stats.execution_state = SPUCTX_UTIL_USER;
-       ctx->stats.tstamp = jiffies;
+       ctx->stats.util_state = SPU_UTIL_IDLE_LOADED;
 
        atomic_inc(&nr_spu_contexts);
        goto out;
index f53a07437472bc51fb2aa99a2d44255c510d9fce..917eab4be486d40ce8f4aa105d84c3325cb2e55f 100644 (file)
@@ -179,16 +179,14 @@ int spufs_handle_class1(struct spu_context *ctx)
        if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)))
                return 0;
 
-       spuctx_switch_state(ctx, SPUCTX_UTIL_IOWAIT);
+       spuctx_switch_state(ctx, SPU_UTIL_IOWAIT);
 
        pr_debug("ctx %p: ea %016lx, dsisr %016lx state %d\n", ctx, ea,
                dsisr, ctx->state);
 
        ctx->stats.hash_flt++;
-       if (ctx->state == SPU_STATE_RUNNABLE) {
+       if (ctx->state == SPU_STATE_RUNNABLE)
                ctx->spu->stats.hash_flt++;
-               spu_switch_state(ctx->spu, SPU_UTIL_IOWAIT);
-       }
 
        /* we must not hold the lock when entering spu_handle_mm_fault */
        spu_release(ctx);
@@ -226,7 +224,7 @@ int spufs_handle_class1(struct spu_context *ctx)
        } else
                spufs_handle_dma_error(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE);
 
-       spuctx_switch_state(ctx, SPUCTX_UTIL_SYSTEM);
+       spuctx_switch_state(ctx, SPU_UTIL_SYSTEM);
        return ret;
 }
 EXPORT_SYMBOL_GPL(spufs_handle_class1);
index fe164112b3d0e75b9a29e7c56c7f04165fafde6b..9351db9472d905eca51729992ef03b943ae26e7c 100644 (file)
@@ -2079,14 +2079,26 @@ static const char *ctx_state_names[] = {
 };
 
 static unsigned long long spufs_acct_time(struct spu_context *ctx,
-               enum spuctx_execution_state state)
+               enum spu_utilization_state state)
 {
-       unsigned long time = ctx->stats.times[state];
+       struct timespec ts;
+       unsigned long long time = ctx->stats.times[state];
 
-       if (ctx->stats.execution_state == state)
-               time += jiffies - ctx->stats.tstamp;
+       /*
+        * In general, utilization statistics are updated by the controlling
+        * thread as the spu context moves through various well defined
+        * state transitions, but if the context is lazily loaded its
+        * utilization statistics are not updated as the controlling thread
+        * is not tightly coupled with the execution of the spu context.  We
+        * calculate and apply the time delta from the last recorded state
+        * of the spu context.
+        */
+       if (ctx->spu && ctx->stats.util_state == state) {
+               ktime_get_ts(&ts);
+               time += timespec_to_ns(&ts) - ctx->stats.tstamp;
+       }
 
-       return jiffies_to_msecs(time);
+       return time / NSEC_PER_MSEC;
 }
 
 static unsigned long long spufs_slb_flts(struct spu_context *ctx)
@@ -2121,11 +2133,11 @@ static int spufs_show_stat(struct seq_file *s, void *private)
        spu_acquire(ctx);
        seq_printf(s, "%s %llu %llu %llu %llu "
                      "%llu %llu %llu %llu %llu %llu %llu %llu\n",
-               ctx_state_names[ctx->stats.execution_state],
-               spufs_acct_time(ctx, SPUCTX_UTIL_USER),
-               spufs_acct_time(ctx, SPUCTX_UTIL_SYSTEM),
-               spufs_acct_time(ctx, SPUCTX_UTIL_IOWAIT),
-               spufs_acct_time(ctx, SPUCTX_UTIL_LOADED),
+               ctx_state_names[ctx->stats.util_state],
+               spufs_acct_time(ctx, SPU_UTIL_USER),
+               spufs_acct_time(ctx, SPU_UTIL_SYSTEM),
+               spufs_acct_time(ctx, SPU_UTIL_IOWAIT),
+               spufs_acct_time(ctx, SPU_UTIL_IDLE_LOADED),
                ctx->stats.vol_ctx_switch,
                ctx->stats.invol_ctx_switch,
                spufs_slb_flts(ctx),
index 58ae13b7de84cd50677a740f87d2d25dc0cba7ee..8c91b3f93152deb2508dc32bd3c2378a0e45a648 100644 (file)
@@ -126,6 +126,8 @@ out:
 
 static int spu_run_init(struct spu_context *ctx, u32 * npc)
 {
+       spuctx_switch_state(ctx, SPU_UTIL_SYSTEM);
+
        if (ctx->flags & SPU_CREATE_ISOLATE) {
                unsigned long runcntl;
 
@@ -151,6 +153,8 @@ static int spu_run_init(struct spu_context *ctx, u32 * npc)
                ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
        }
 
+       spuctx_switch_state(ctx, SPU_UTIL_USER);
+
        return 0;
 }
 
@@ -161,6 +165,8 @@ static int spu_run_fini(struct spu_context *ctx, u32 * npc,
 
        *status = ctx->ops->status_read(ctx);
        *npc = ctx->ops->npc_read(ctx);
+
+       spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED);
        spu_release(ctx);
 
        if (signal_pending(current))
@@ -328,6 +334,9 @@ long spufs_run_spu(struct file *file, struct spu_context *ctx,
                ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status));
                if (unlikely(ret))
                        break;
+
+               spuctx_switch_state(ctx, SPU_UTIL_SYSTEM);
+
                if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
                    (status >> SPU_STOP_STATUS_SHIFT == 0x2104)) {
                        ret = spu_process_callback(ctx);
@@ -356,6 +365,7 @@ long spufs_run_spu(struct file *file, struct spu_context *ctx,
            (ctx->state == SPU_STATE_RUNNABLE))
                ctx->stats.libassist++;
 
+
        ctx->ops->master_stop(ctx);
        ret = spu_run_fini(ctx, npc, &status);
        spu_yield(ctx);
index fe789308dd1eecfc4248064aad83b6ab3e71ee18..ecd9e95116add79063772d860a417dac535a6db1 100644 (file)
@@ -229,6 +229,7 @@ static void spu_bind_context(struct spu *spu, struct spu_context *ctx)
 {
        pr_debug("%s: pid=%d SPU=%d NODE=%d\n", __FUNCTION__, current->pid,
                 spu->number, spu->node);
+       spuctx_switch_state(ctx, SPU_UTIL_SYSTEM);
 
        ctx->stats.slb_flt_base = spu->stats.slb_flt;
        ctx->stats.class2_intr_base = spu->stats.class2_intr;
@@ -251,7 +252,8 @@ static void spu_bind_context(struct spu *spu, struct spu_context *ctx)
        spu_cpu_affinity_set(spu, raw_smp_processor_id());
        spu_switch_notify(spu, ctx);
        ctx->state = SPU_STATE_RUNNABLE;
-       spu_switch_state(spu, SPU_UTIL_SYSTEM);
+
+       spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED);
 }
 
 /**
@@ -263,8 +265,7 @@ static void spu_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_state(spu, SPU_UTIL_IDLE);
+       spuctx_switch_state(ctx, SPU_UTIL_SYSTEM);
 
        spu_switch_notify(spu, NULL);
        spu_unmap_mappings(ctx);
@@ -279,7 +280,6 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx)
        spu_associate_mm(spu, NULL);
        spu->pid = 0;
        ctx->ops = &spu_backing_ops;
-       ctx->spu = NULL;
        spu->flags = 0;
        spu->ctx = NULL;
 
@@ -287,6 +287,10 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx)
                (spu->stats.slb_flt - ctx->stats.slb_flt_base);
        ctx->stats.class2_intr +=
                (spu->stats.class2_intr - ctx->stats.class2_intr_base);
+
+       /* This maps the underlying spu state to idle */
+       spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED);
+       ctx->spu = NULL;
 }
 
 /**
@@ -455,8 +459,6 @@ static struct spu *find_victim(struct spu_context *ctx)
  */
 int spu_activate(struct spu_context *ctx, unsigned long flags)
 {
-       spuctx_switch_state(ctx, SPUCTX_UTIL_SYSTEM);
-
        do {
                struct spu *spu;
 
@@ -551,7 +553,6 @@ static int __spu_deactivate(struct spu_context *ctx, int force, int max_prio)
 void spu_deactivate(struct spu_context *ctx)
 {
        __spu_deactivate(ctx, 1, MAX_PRIO);
-       spuctx_switch_state(ctx, SPUCTX_UTIL_USER);
 }
 
 /**
@@ -566,12 +567,7 @@ void spu_yield(struct spu_context *ctx)
 {
        if (!(ctx->flags & SPU_CREATE_NOSCHED)) {
                mutex_lock(&ctx->state_mutex);
-               if (__spu_deactivate(ctx, 0, MAX_PRIO))
-                       spuctx_switch_state(ctx, SPUCTX_UTIL_USER);
-               else {
-                       spuctx_switch_state(ctx, SPUCTX_UTIL_LOADED);
-                       spu_switch_state(ctx->spu, SPU_UTIL_USER);
-               }
+               __spu_deactivate(ctx, 0, MAX_PRIO);
                mutex_unlock(&ctx->state_mutex);
        }
 }
index 34d5f9f8b4ae1fc97a6aef185a77198936366f8a..fdace92843785ababd4fdafc870b6fa6e49b06d4 100644 (file)
@@ -40,19 +40,6 @@ enum {
 struct spu_context_ops;
 struct spu_gang;
 
-/*
- * This is the state for spu utilization reporting to userspace.
- * Because this state is visible to userspace it must never change and needs
- * to be kept strictly separate from any internal state kept by the kernel.
- */
-enum spuctx_execution_state {
-       SPUCTX_UTIL_USER = 0,
-       SPUCTX_UTIL_SYSTEM,
-       SPUCTX_UTIL_IOWAIT,
-       SPUCTX_UTIL_LOADED,
-       SPUCTX_UTIL_MAX
-};
-
 struct spu_context {
        struct spu *spu;                  /* pointer to a physical SPU */
        struct spu_state csa;             /* SPU context save area. */
@@ -104,9 +91,9 @@ struct spu_context {
        /* statistics */
        struct {
                /* updates protected by ctx->state_mutex */
-               enum spuctx_execution_state execution_state;
-               unsigned long tstamp;           /* time of last ctx switch */
-               unsigned long times[SPUCTX_UTIL_MAX];
+               enum spu_utilization_state util_state;
+               unsigned long long tstamp;      /* time of last state switch */
+               unsigned long long times[SPU_UTIL_MAX];
                unsigned long long vol_ctx_switch;
                unsigned long long invol_ctx_switch;
                unsigned long long min_flt;
@@ -293,30 +280,34 @@ extern int spufs_coredump_num_notes;
  * line.
  */
 static inline void spuctx_switch_state(struct spu_context *ctx,
-               enum spuctx_execution_state new_state)
+               enum spu_utilization_state new_state)
 {
-       WARN_ON(!mutex_is_locked(&ctx->state_mutex));
+       unsigned long long curtime;
+       signed long long delta;
+       struct timespec ts;
+       struct spu *spu;
+       enum spu_utilization_state old_state;
 
-       if (ctx->stats.execution_state != new_state) {
-               unsigned long curtime = jiffies;
+       ktime_get_ts(&ts);
+       curtime = timespec_to_ns(&ts);
+       delta = curtime - ctx->stats.tstamp;
 
-               ctx->stats.times[ctx->stats.execution_state] +=
-                                curtime - ctx->stats.tstamp;
-               ctx->stats.tstamp = curtime;
-               ctx->stats.execution_state = new_state;
-       }
-}
-
-static inline void spu_switch_state(struct spu *spu,
-               enum spuctx_execution_state new_state)
-{
-       if (spu->stats.utilization_state != new_state) {
-               unsigned long curtime = jiffies;
-
-               spu->stats.times[spu->stats.utilization_state] +=
-                                curtime - spu->stats.tstamp;
+       WARN_ON(!mutex_is_locked(&ctx->state_mutex));
+       WARN_ON(delta < 0);
+
+       spu = ctx->spu;
+       old_state = ctx->stats.util_state;
+       ctx->stats.util_state = new_state;
+       ctx->stats.tstamp = curtime;
+
+       /*
+        * Update the physical SPU utilization statistics.
+        */
+       if (spu) {
+               ctx->stats.times[old_state] += delta;
+               spu->stats.times[old_state] += delta;
+               spu->stats.util_state = new_state;
                spu->stats.tstamp = curtime;
-               spu->stats.utilization_state = new_state;
        }
 }
 
index a034f03b810706bdf699ab6f50a60165d979088f..12442acdc76f234c1114c7d33bc0ec1e66c56c22 100644 (file)
@@ -107,10 +107,10 @@ struct spu_runqueue;
 struct device_node;
 
 enum spu_utilization_state {
-       SPU_UTIL_SYSTEM,
        SPU_UTIL_USER,
+       SPU_UTIL_SYSTEM,
        SPU_UTIL_IOWAIT,
-       SPU_UTIL_IDLE,
+       SPU_UTIL_IDLE_LOADED,
        SPU_UTIL_MAX
 };
 
@@ -167,9 +167,9 @@ struct spu {
 
        struct {
                /* protected by interrupt reentrancy */
-               enum spu_utilization_state utilization_state;
-               unsigned long tstamp;           /* time of last ctx switch */
-               unsigned long times[SPU_UTIL_MAX];
+               enum spu_utilization_state util_state;
+               unsigned long long tstamp;
+               unsigned long long times[SPU_UTIL_MAX];
                unsigned long long vol_ctx_switch;
                unsigned long long invol_ctx_switch;
                unsigned long long min_flt;