diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index 174bd9f911db..e4d0c9f42abd 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c @@ -585,6 +585,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; + goto out; out_free_irqs: @@ -597,6 +600,45 @@ out: return ret; } +static const char *spu_state_names[] = { + "user", "system", "iowait", "idle" +}; + +static unsigned long long spu_acct_time(struct spu *spu, + enum spu_utilization_state state) +{ + unsigned long long time = spu->stats.times[state]; + + if (spu->stats.utilization_state == state) + time += jiffies - spu->stats.tstamp; + + return jiffies_to_msecs(time); +} + + +static ssize_t spu_stat_show(struct sys_device *sysdev, char *buf) +{ + struct spu *spu = container_of(sysdev, struct spu, sysdev); + + 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_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->stats.vol_ctx_switch, + spu->stats.invol_ctx_switch, + spu->stats.slb_flt, + spu->stats.hash_flt, + spu->stats.min_flt, + spu->stats.maj_flt, + spu->stats.class2_intr, + spu->stats.libassist); +} + +static SYSDEV_ATTR(stat, 0644, spu_stat_show, NULL); + static int __init init_spu_base(void) { int i, ret = 0; @@ -622,6 +664,8 @@ static int __init init_spu_base(void) xmon_register_spus(&spu_full_list); + spu_add_sysdev_attr(&attr_stat); + return 0; out_unregister_sysdev_class: diff --git a/arch/powerpc/platforms/cell/spufs/fault.c b/arch/powerpc/platforms/cell/spufs/fault.c index 3a9e49a24ec0..e064d0c0d80e 100644 --- a/arch/powerpc/platforms/cell/spufs/fault.c +++ b/arch/powerpc/platforms/cell/spufs/fault.c @@ -187,6 +187,10 @@ int spufs_handle_class1(struct spu_context *ctx) dsisr, ctx->state); ctx->stats.hash_flt++; + 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); @@ -212,6 +216,12 @@ int spufs_handle_class1(struct spu_context *ctx) ctx->stats.min_flt++; else ctx->stats.maj_flt++; + if (ctx->state == SPU_STATE_RUNNABLE) { + if (flt == VM_FAULT_MINOR) + ctx->spu->stats.min_flt++; + else + ctx->spu->stats.maj_flt++; + } if (ctx->spu) ctx->ops->restart_dma(ctx); diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index 05cf815dbdad..58ae13b7de84 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c @@ -352,7 +352,8 @@ long spufs_run_spu(struct file *file, struct spu_context *ctx, SPU_STATUS_SINGLE_STEP))); if ((status & SPU_STATUS_STOPPED_BY_STOP) && - (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100)) + (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100) && + (ctx->state == SPU_STATE_RUNNABLE)) ctx->stats.libassist++; ctx->ops->master_stop(ctx); diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 9fb3133268f6..e5b4dd1db286 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -251,6 +251,7 @@ 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); } /** @@ -263,6 +264,8 @@ 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); + spu_switch_notify(spu, NULL); spu_unmap_mappings(ctx); spu_save(&ctx->csa, spu); @@ -426,6 +429,7 @@ static struct spu *find_victim(struct spu_context *ctx) spu_remove_from_active_list(spu); spu_unbind_context(spu, victim); victim->stats.invol_ctx_switch++; + spu->stats.invol_ctx_switch++; mutex_unlock(&victim->state_mutex); /* * We need to break out of the wait loop in spu_run @@ -526,6 +530,7 @@ static int __spu_deactivate(struct spu_context *ctx, int force, int max_prio) spu_remove_from_active_list(spu); spu_unbind_context(spu, ctx); ctx->stats.vol_ctx_switch++; + spu->stats.vol_ctx_switch++; spu_free(spu); if (new) wake_up(&new->stop_wq); @@ -572,8 +577,10 @@ void spu_yield(struct spu_context *ctx) mutex_lock(&ctx->state_mutex); if (__spu_deactivate(ctx, 0, MAX_PRIO)) spuctx_switch_state(ctx, SPUCTX_UTIL_USER); - else + else { spuctx_switch_state(ctx, SPUCTX_UTIL_LOADED); + spu_switch_state(ctx->spu, SPU_UTIL_USER); + } mutex_unlock(&ctx->state_mutex); } } @@ -603,6 +610,7 @@ static void spusched_tick(struct spu_context *ctx) __spu_remove_from_active_list(spu); spu_unbind_context(spu, ctx); ctx->stats.invol_ctx_switch++; + spu->stats.invol_ctx_switch++; spu_free(spu); wake_up(&new->stop_wq); /* diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index cd2b54f6e378..08b3530288ac 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -307,4 +307,17 @@ static inline void spuctx_switch_state(struct spu_context *ctx, } } +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; + spu->stats.tstamp = curtime; + spu->stats.utilization_state = new_state; + } +} + #endif diff --git a/include/asm-powerpc/spu.h b/include/asm-powerpc/spu.h index 5957fcdda04c..eedc828cef2d 100644 --- a/include/asm-powerpc/spu.h +++ b/include/asm-powerpc/spu.h @@ -106,6 +106,14 @@ struct spu_context; struct spu_runqueue; struct device_node; +enum spu_utilization_state { + SPU_UTIL_SYSTEM, + SPU_UTIL_USER, + SPU_UTIL_IOWAIT, + SPU_UTIL_IDLE, + SPU_UTIL_MAX +}; + struct spu { const char *name; unsigned long local_store_phys; @@ -159,8 +167,17 @@ 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]; + unsigned long long vol_ctx_switch; + unsigned long long invol_ctx_switch; + unsigned long long min_flt; + unsigned long long maj_flt; + unsigned long long hash_flt; unsigned long long slb_flt; unsigned long long class2_intr; + unsigned long long libassist; } stats; };