Subject: spufs: implement /proc/spu_loadavg From: Christoph Hellwig Provide load averange information for spu context. The format is identical to /proc/loadavg, which is also where a lot of code and concepts is borrowed from. Signed-off-by: Christoph Hellwig Signed-off-by: Arnd Bergmann Index: linux-2.6/arch/powerpc/platforms/cell/spufs/context.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/context.c +++ linux-2.6/arch/powerpc/platforms/cell/spufs/context.c @@ -24,10 +24,14 @@ #include #include #include +#include #include #include #include "spufs.h" + +atomic_t nr_spu_contexts = ATOMIC_INIT(0); + struct spu_context *alloc_spu_context(struct spu_gang *gang) { struct spu_context *ctx; @@ -57,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); + + atomic_inc(&nr_spu_contexts); goto out; out_free: kfree(ctx); @@ -78,6 +84,7 @@ void destroy_spu_context(struct kref *kr if (ctx->prof_priv_kref) kref_put(ctx->prof_priv_kref, ctx->prof_priv_release); BUG_ON(!list_empty(&ctx->rq)); + atomic_dec(&nr_spu_contexts); kfree(ctx); } Index: linux-2.6/arch/powerpc/platforms/cell/spufs/sched.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/sched.c +++ linux-2.6/arch/powerpc/platforms/cell/spufs/sched.c @@ -36,6 +36,9 @@ #include #include #include +#include +#include +#include #include #include @@ -50,8 +53,11 @@ struct spu_prio_array { spinlock_t runq_lock; struct list_head active_list[MAX_NUMNODES]; struct mutex active_mutex[MAX_NUMNODES]; + int nr_active[MAX_NUMNODES]; + int nr_waiting; }; +static unsigned long spu_avenrun[3]; static struct spu_prio_array *spu_prio; static struct task_struct *spusched_task; static struct timer_list spusched_timer; @@ -162,14 +168,18 @@ static int node_allowed(struct spu_conte */ static void spu_add_to_active_list(struct spu *spu) { - mutex_lock(&spu_prio->active_mutex[spu->node]); - list_add_tail(&spu->list, &spu_prio->active_list[spu->node]); - mutex_unlock(&spu_prio->active_mutex[spu->node]); + int node = spu->node; + + mutex_lock(&spu_prio->active_mutex[node]); + spu_prio->nr_active[node]++; + list_add_tail(&spu->list, &spu_prio->active_list[node]); + mutex_unlock(&spu_prio->active_mutex[node]); } static void __spu_remove_from_active_list(struct spu *spu) { list_del_init(&spu->list); + spu_prio->nr_active[spu->node]--; } /** @@ -442,6 +452,7 @@ static void __spu_add_to_rq(struct spu_c { int prio = ctx->prio; + spu_prio->nr_waiting++; list_add_tail(&ctx->rq, &spu_prio->runq[prio]); set_bit(prio, spu_prio->bitmap); } @@ -450,8 +461,10 @@ static void __spu_del_from_rq(struct spu { int prio = ctx->prio; - if (!list_empty(&ctx->rq)) + if (!list_empty(&ctx->rq)) { list_del_init(&ctx->rq); + spu_prio->nr_waiting--; + } if (list_empty(&spu_prio->runq[prio])) clear_bit(prio, spu_prio->bitmap); } @@ -738,10 +751,56 @@ static void spusched_tick(struct spu_con } } +/** + * count_active_contexts - count nr of active tasks + * + * Return the number of tasks currently running or waiting to run. + * + * Note that we don't take runq_lock / active_mutex here. Reading + * a single 32bit value is atomic on powerpc, and we don't care + * about memory ordering issues here. + */ +static unsigned long count_active_contexts(void) +{ + int nr_active = 0, node; + + for (node = 0; node < MAX_NUMNODES; node++) + nr_active += spu_prio->nr_active[node]; + nr_active += spu_prio->nr_waiting; + + return nr_active; +} + +/** + * spu_calc_load - given tick count, update the avenrun load estimates. + * @tick: tick count + * + * No locking against reading these values from userspace, as for + * the CPU loadavg code. + */ +static void spu_calc_load(unsigned long ticks) +{ + unsigned long active_tasks; /* fixed-point */ + static int count = LOAD_FREQ; + + count -= ticks; + + if (unlikely(count < 0)) { + active_tasks = count_active_contexts() * FIXED_1; + do { + CALC_LOAD(spu_avenrun[0], EXP_1, active_tasks); + CALC_LOAD(spu_avenrun[1], EXP_5, active_tasks); + CALC_LOAD(spu_avenrun[2], EXP_15, active_tasks); + count += LOAD_FREQ; + } while (count < 0); + } +} + static void spusched_wake(unsigned long data) { mod_timer(&spusched_timer, jiffies + SPUSCHED_TICK); wake_up_process(spusched_task); + spu_calc_load(SPUSCHED_TICK); } static int spusched_thread(void *unused) @@ -769,13 +828,52 @@ static int spusched_thread(void *unused) return 0; } +#define LOAD_INT(x) ((x) >> FSHIFT) +#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100) + +static int show_spu_loadavg(struct seq_file *s, void *private) +{ + int a, b, c; + + a = spu_avenrun[0] + (FIXED_1/200); + b = spu_avenrun[1] + (FIXED_1/200); + c = spu_avenrun[2] + (FIXED_1/200); + + /* + * Note that last_pid doesn't really make much sense for the + * SPU loadavg (it even seems very odd on the CPU side..), + * but we include it here to have a 100% compatible interface. + */ + seq_printf(s, "%d.%02d %d.%02d %d.%02d %ld/%d %d\n", + LOAD_INT(a), LOAD_FRAC(a), + LOAD_INT(b), LOAD_FRAC(b), + LOAD_INT(c), LOAD_FRAC(c), + count_active_contexts(), + atomic_read(&nr_spu_contexts), + current->nsproxy->pid_ns->last_pid); + return 0; +} + +static int spu_loadavg_open(struct inode *inode, struct file *file) +{ + return single_open(file, show_spu_loadavg, NULL); +} + +static const struct file_operations spu_loadavg_fops = { + .open = spu_loadavg_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + int __init spu_sched_init(void) { - int i; + struct proc_dir_entry *entry; + int err = -ENOMEM, i; spu_prio = kzalloc(sizeof(struct spu_prio_array), GFP_KERNEL); if (!spu_prio) - return -ENOMEM; + goto out; for (i = 0; i < MAX_PRIO; i++) { INIT_LIST_HEAD(&spu_prio->runq[i]); @@ -790,11 +888,22 @@ int __init spu_sched_init(void) spusched_task = kthread_run(spusched_thread, NULL, "spusched"); if (IS_ERR(spusched_task)) { - kfree(spu_prio); - return PTR_ERR(spusched_task); + err = PTR_ERR(spusched_task); + goto out_free_spu_prio; } + + entry = create_proc_entry("spu_loadavg", 0, NULL); + if (!entry) + goto out_stop_kthread; + entry->proc_fops = &spu_loadavg_fops; return 0; + out_stop_kthread: + kthread_stop(spusched_task); + out_free_spu_prio: + kfree(spu_prio); + out: + return err; } void __exit spu_sched_exit(void) @@ -802,6 +911,8 @@ void __exit spu_sched_exit(void) struct spu *spu, *tmp; int node; + remove_proc_entry("spu_loadavg", NULL); + kthread_stop(spusched_task); for (node = 0; node < MAX_NUMNODES; node++) { Index: linux-2.6/arch/powerpc/platforms/cell/spufs/spufs.h =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/spufs.h +++ linux-2.6/arch/powerpc/platforms/cell/spufs/spufs.h @@ -198,6 +198,7 @@ int spufs_handle_class1(struct spu_conte struct spu *affinity_check(struct spu_context *ctx); /* context management */ +extern atomic_t nr_spu_contexts; static inline void spu_acquire(struct spu_context *ctx) { mutex_lock(&ctx->state_mutex);