[Cbe-oss-dev] [PATCH 2/2] spufs: add stat file

Christoph Hellwig hch at lst.de
Tue Jun 5 22:55:27 EST 2007


Export per-context statistics in spufs.


Signed-off-by: Christoph Hellwig <hch at lst.de>

Index: linux-cell/arch/powerpc/platforms/cell/spufs/file.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/cell/spufs/file.c	2007-06-04 09:37:48.000000000 +0200
+++ linux-cell/arch/powerpc/platforms/cell/spufs/file.c	2007-06-05 11:05:02.000000000 +0200
@@ -2065,6 +2065,83 @@ static const struct file_operations spuf
 	.release	= single_release,
 };
 
+static const char *ctx_state_names[] = {
+	"user", "system", "iowait", "loaded"
+};
+
+static unsigned long long spufs_acct_time(struct spu_context *ctx,
+		enum spuctx_execution_state state)
+{
+	unsigned long time = ctx->stats.times[state];
+
+	if (ctx->stats.execution_state == state)
+		time += jiffies - ctx->stats.tstamp;
+
+	return jiffies_to_msecs(time);
+}
+
+static unsigned long long spufs_slb_flts(struct spu_context *ctx)
+{
+	unsigned long long slb_flts = ctx->stats.slb_flt;
+
+	if (ctx->state == SPU_STATE_RUNNABLE) {
+		slb_flts += (ctx->spu->stats.slb_flt -
+			     ctx->stats.slb_flt_base);
+	}
+
+	return slb_flts;
+}
+
+static unsigned long long spufs_class2_intrs(struct spu_context *ctx)
+{
+	unsigned long long class2_intrs = ctx->stats.class2_intr;
+
+	if (ctx->state == SPU_STATE_RUNNABLE) {
+		class2_intrs += (ctx->spu->stats.class2_intr -
+				 ctx->stats.class2_intr_base);
+	}
+
+	return class2_intrs;
+}
+
+
+static int spufs_show_stat(struct seq_file *s, void *private)
+{
+	struct spu_context *ctx = s->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->stats.vol_ctx_switch,
+		ctx->stats.invol_ctx_switch,
+		spufs_slb_flts(ctx),
+		ctx->stats.hash_flt,
+		ctx->stats.min_flt,
+		ctx->stats.maj_flt,
+		spufs_class2_intrs(ctx),
+		ctx->stats.libassist);
+	spu_release(ctx);
+	return 0;
+}
+
+static int spufs_stat_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, spufs_show_stat, SPUFS_I(inode)->i_ctx);
+}
+
+static const struct file_operations spufs_stat_fops = {
+	.open		= spufs_stat_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+
 struct tree_descr spufs_dir_contents[] = {
 	{ "capabilities", &spufs_caps_fops, 0444, },
 	{ "mem",  &spufs_mem_fops,  0666, },
@@ -2099,6 +2176,7 @@ struct tree_descr spufs_dir_contents[] =
 	{ "dma_info", &spufs_dma_info_fops, 0444, },
 	{ "proxydma_info", &spufs_proxydma_info_fops, 0444, },
 	{ "tid", &spufs_tid_fops, 0444, },
+	{ "stat", &spufs_stat_fops, 0444, },
 	{},
 };
 
@@ -2123,6 +2201,7 @@ struct tree_descr spufs_dir_nosched_cont
 	{ "phys-id", &spufs_id_ops, 0666, },
 	{ "object-id", &spufs_object_id_ops, 0666, },
 	{ "tid", &spufs_tid_fops, 0444, },
+	{ "stat", &spufs_stat_fops, 0444, },
 	{},
 };
 
Index: linux-cell/arch/powerpc/platforms/cell/spufs/fault.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/cell/spufs/fault.c	2007-06-04 09:37:48.000000000 +0200
+++ linux-cell/arch/powerpc/platforms/cell/spufs/fault.c	2007-06-05 11:05:02.000000000 +0200
@@ -33,7 +33,8 @@
  * function. Currently, there are a few corner cases that we haven't had
  * to handle fortunately.
  */
-static int spu_handle_mm_fault(struct mm_struct *mm, unsigned long ea, unsigned long dsisr)
+static int spu_handle_mm_fault(struct mm_struct *mm, unsigned long ea,
+		unsigned long dsisr, unsigned *flt)
 {
 	struct vm_area_struct *vma;
 	unsigned long is_write;
@@ -73,7 +74,8 @@ good_area:
 			goto bad_area;
 	}
 	ret = 0;
-	switch (handle_mm_fault(mm, vma, ea, is_write)) {
+	*flt = handle_mm_fault(mm, vma, ea, is_write);
+	switch (*flt) {
 	case VM_FAULT_MINOR:
 		current->min_flt++;
 		break;
@@ -153,6 +155,7 @@ int spufs_handle_class1(struct spu_conte
 {
 	u64 ea, dsisr, access;
 	unsigned long flags;
+	unsigned flt = 0;
 	int ret;
 
 	/*
@@ -178,9 +181,13 @@ int spufs_handle_class1(struct spu_conte
 	if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)))
 		return 0;
 
+	spuctx_switch_state(ctx, SPUCTX_UTIL_IOWAIT);
+
 	pr_debug("ctx %p: ea %016lx, dsisr %016lx state %d\n", ctx, ea,
 		dsisr, ctx->state);
 
+	ctx->stats.hash_flt++;
+
 	/* we must not hold the lock when entering spu_handle_mm_fault */
 	spu_release(ctx);
 
@@ -192,7 +199,7 @@ int spufs_handle_class1(struct spu_conte
 
 	/* hashing failed, so try the actual fault handler */
 	if (ret)
-		ret = spu_handle_mm_fault(current->mm, ea, dsisr);
+		ret = spu_handle_mm_fault(current->mm, ea, dsisr, &flt);
 
 	spu_acquire(ctx);
 	/*
@@ -201,11 +208,17 @@ int spufs_handle_class1(struct spu_conte
 	 * In case of unhandled error report the problem to user space.
 	 */
 	if (!ret) {
+		if (flt == VM_FAULT_MINOR)
+			ctx->stats.min_flt++;
+		else
+			ctx->stats.maj_flt++;
+
 		if (ctx->spu)
 			ctx->ops->restart_dma(ctx);
 	} else
 		spufs_handle_dma_error(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE);
 
+	spuctx_switch_state(ctx, SPUCTX_UTIL_SYSTEM);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(spufs_handle_class1);
Index: linux-cell/arch/powerpc/platforms/cell/spufs/sched.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/cell/spufs/sched.c	2007-06-04 19:40:35.000000000 +0200
+++ linux-cell/arch/powerpc/platforms/cell/spufs/sched.c	2007-06-05 11:08:00.000000000 +0200
@@ -396,6 +396,10 @@ static void spu_bind_context(struct spu 
 		atomic_inc(&cbe_spu_info[spu->node].reserved_spus);
 	if (!list_empty(&ctx->aff_list))
 		atomic_inc(&ctx->gang->aff_sched_count);
+
+	ctx->stats.slb_flt_base = spu->stats.slb_flt;
+	ctx->stats.class2_intr_base = spu->stats.class2_intr;
+
 	spu->ctx = ctx;
 	spu->flags = 0;
 	ctx->spu = spu;
@@ -449,6 +453,11 @@ static void spu_unbind_context(struct sp
 	ctx->spu = NULL;
 	spu->flags = 0;
 	spu->ctx = NULL;
+
+	ctx->stats.slb_flt +=
+		(spu->stats.slb_flt - ctx->stats.slb_flt_base);
+	ctx->stats.class2_intr +=
+		(spu->stats.class2_intr - ctx->stats.class2_intr_base);
 }
 
 /**
@@ -578,6 +587,7 @@ static struct spu *find_victim(struct sp
 			}
 			spu_remove_from_active_list(spu);
 			spu_unbind_context(spu, victim);
+			victim->stats.invol_ctx_switch++;
 			mutex_unlock(&victim->state_mutex);
 			/*
 			 * We need to break out of the wait loop in spu_run
@@ -603,6 +613,7 @@ static struct spu *find_victim(struct sp
  */
 int spu_activate(struct spu_context *ctx, unsigned long flags)
 {
+	spuctx_switch_state(ctx, SPUCTX_UTIL_SYSTEM);
 
 	if (ctx->spu)
 		return 0;
@@ -670,6 +681,7 @@ static int __spu_deactivate(struct spu_c
 		if (new || force) {
 			spu_remove_from_active_list(spu);
 			spu_unbind_context(spu, ctx);
+			ctx->stats.vol_ctx_switch++;
 			spu_free(spu);
 			if (new)
 				wake_up(&new->stop_wq);
@@ -699,6 +711,7 @@ void spu_deactivate(struct spu_context *
 	}
 
 	__spu_deactivate(ctx, 1, MAX_PRIO);
+	spuctx_switch_state(ctx, SPUCTX_UTIL_USER);
 }
 
 /**
@@ -713,8 +726,11 @@ void spu_yield(struct spu_context *ctx)
 {
 	if (!(ctx->flags & SPU_CREATE_NOSCHED)) {
 		mutex_lock(&ctx->state_mutex);
-		__spu_deactivate(ctx, 0, MAX_PRIO);
-		mutex_unlock(&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);
+	  	mutex_unlock(&ctx->state_mutex);
 	}
 }
 
@@ -742,6 +758,7 @@ static void spusched_tick(struct spu_con
 
 			__spu_remove_from_active_list(spu);
 			spu_unbind_context(spu, ctx);
+			ctx->stats.invol_ctx_switch++;
 			spu_free(spu);
 			wake_up(&new->stop_wq);
 			/*
Index: linux-cell/arch/powerpc/platforms/cell/spufs/spufs.h
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/cell/spufs/spufs.h	2007-06-04 09:37:48.000000000 +0200
+++ linux-cell/arch/powerpc/platforms/cell/spufs/spufs.h	2007-06-05 11:05:02.000000000 +0200
@@ -45,6 +45,19 @@ enum {
 	SPU_SCHED_NOTIFY_ACTIVE = 0,
 };
 
+/*
+ * 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. */
@@ -98,6 +111,24 @@ struct spu_context {
 	struct list_head aff_list;
 	int aff_head;
 	int aff_offset;
+
+	/* 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];
+		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 slb_flt_base; /* # at last ctx switch */
+		unsigned long long class2_intr;
+		unsigned long long class2_intr_base; /* # at last ctx switch */
+		unsigned long long libassist;
+	} stats;
 };
 
 struct spu_gang {
@@ -283,4 +314,24 @@ struct spufs_coredump_reader {
 extern struct spufs_coredump_reader spufs_coredump_read[];
 extern int spufs_coredump_num_notes;
 
+/*
+ * This function is a little bit too large for an inline, but
+ * as fault.c is built into the kernel we can't move it out of
+ * line.
+ */
+static inline void spuctx_switch_state(struct spu_context *ctx,
+		enum spuctx_execution_state new_state)
+{
+	WARN_ON(!mutex_is_locked(&ctx->state_mutex));
+
+	if (ctx->stats.execution_state != new_state) {
+		unsigned long curtime = jiffies;
+
+		ctx->stats.times[ctx->stats.execution_state] +=
+				 curtime - ctx->stats.tstamp;
+		ctx->stats.tstamp = curtime;
+		ctx->stats.execution_state = new_state;
+	}
+}
+
 #endif
Index: linux-cell/arch/powerpc/platforms/cell/spufs/context.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/cell/spufs/context.c	2007-06-04 09:37:48.000000000 +0200
+++ linux-cell/arch/powerpc/platforms/cell/spufs/context.c	2007-06-05 11:05:02.000000000 +0200
@@ -61,6 +61,8 @@ struct spu_context *alloc_spu_context(st
 		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;
 
 	atomic_inc(&nr_spu_contexts);
 	goto out;
Index: linux-cell/arch/powerpc/platforms/cell/spufs/run.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/cell/spufs/run.c	2007-06-04 09:37:48.000000000 +0200
+++ linux-cell/arch/powerpc/platforms/cell/spufs/run.c	2007-06-05 11:05:02.000000000 +0200
@@ -362,6 +362,10 @@ long spufs_run_spu(struct file *file, st
 				      SPU_STATUS_STOPPED_BY_HALT |
 				       SPU_STATUS_SINGLE_STEP)));
 
+	if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
+	    ((status >> SPU_STOP_STATUS_SHIFT) & 0x2100))
+		ctx->stats.libassist++;
+
 	spu_disable_spu(ctx);
 	ret = spu_run_fini(ctx, npc, &status);
 	spu_yield(ctx);
Index: linux-cell/arch/powerpc/platforms/cell/spu_base.c
===================================================================
--- linux-cell.orig/arch/powerpc/platforms/cell/spu_base.c	2007-06-04 09:37:48.000000000 +0200
+++ linux-cell/arch/powerpc/platforms/cell/spu_base.c	2007-06-05 11:05:02.000000000 +0200
@@ -203,7 +203,7 @@ static int __spu_trap_data_seg(struct sp
 		spu->slb_replace = 0;
 
 	spu_restart_dma(spu);
-
+	spu->stats.slb_flt++;
 	return 0;
 }
 
@@ -359,6 +359,7 @@ spu_irq_class_2(int irq, void *data)
 	if (stat & 0x10) /* SPU mailbox threshold */
 		spu->wbox_callback(spu);
 
+	spu->stats.class2_intr++;
 	return stat ? IRQ_HANDLED : IRQ_NONE;
 }
 
Index: linux-cell/include/asm-powerpc/spu.h
===================================================================
--- linux-cell.orig/include/asm-powerpc/spu.h	2007-06-04 09:37:48.000000000 +0200
+++ linux-cell/include/asm-powerpc/spu.h	2007-06-05 11:05:02.000000000 +0200
@@ -161,6 +161,12 @@ struct spu {
 
 	int has_mem_affinity;
 	struct list_head aff_list;
+
+	struct {
+		/* protected by interrupt reentrancy */
+		unsigned long long slb_flt;
+		unsigned long long class2_intr;
+	} stats;
 };
 
 struct cbe_spu_info {



More information about the cbe-oss-dev mailing list