Subject: cell: add placement computation for scheduling of affinity contexts From: Andre Detsch This patch provides the spu affinity placement logic for the spufs scheduler. Each time a gang is going to be scheduled, the placement of a reference context is defined. The placement of all other contexts with affinity from the gang is defined based on this reference context location and on a precomputed displacement offset. Signed-off-by: Andre Detsch Signed-off-by: Arnd Bergmann --- 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 @@ -162,6 +162,8 @@ static void spu_bind_context(struct spu { pr_debug("%s: pid=%d SPU=%d NODE=%d\n", __FUNCTION__, current->pid, spu->number, spu->node); + if (ctx->aff_flags & (AFF_HAS_SPU_AFFINITY | AFF_HAS_MEM_AFFINITY)) + atomic_inc(&ctx->gang->aff_sched_count); spu->ctx = ctx; spu->flags = 0; ctx->spu = spu; @@ -194,6 +196,10 @@ static void spu_unbind_context(struct sp pr_debug("%s: unbind pid=%d SPU=%d NODE=%d\n", __FUNCTION__, spu->pid, spu->number, spu->node); + if (ctx->aff_flags & (AFF_HAS_SPU_AFFINITY | AFF_HAS_MEM_AFFINITY)) { + if (atomic_dec_and_test(&ctx->gang->aff_sched_count)) + ctx->gang->aff_ref_point_location = NULL; + } spu_remove_from_active_list(spu); spu_switch_notify(spu, NULL); spu_unmap_mappings(ctx); @@ -513,3 +519,126 @@ void __exit spu_sched_exit(void) kfree(spu_prio); destroy_workqueue(spu_sched_wq); } + +static void aff_merge_remaining_ctxs(struct spu_gang *gang) +{ + struct spu_context *ctx; + + list_for_each_entry(ctx, &gang->aff_list_head, aff_list) { + if (list_empty(&ctx->aff_list)) { + list_add(&ctx->aff_list, &gang->aff_list_head); + ctx->aff_flags |= AFF_HAS_SPU_AFFINITY; + } + } + gang->aff_flags |= AFF_MERGED; +} + +static void aff_set_rel_displ(struct spu_gang *gang) +{ + struct spu_context *ctx; + int displacement; + + displacement = -1; + list_for_each_entry_reverse(ctx, &gang->aff_ref_point->aff_list, + aff_list) { + if (&ctx->aff_list == &gang->aff_list_head) + break; + ctx->aff_rel_displ = displacement--; + } + + displacement = 0; + list_for_each_entry(ctx, gang->aff_ref_point->aff_list.prev, aff_list) { + if (&ctx->aff_list == &gang->aff_list_head) + break; + ctx->aff_rel_displ = displacement++; + } + + gang->aff_flags |= AFF_REL_DISPL_SET; +} + +static struct spu * +aff_ref_location(int mem_aff, int group_size, int prio, int lowest_rel_displ) +{ + struct spu *spu; + int node; + + /* TODO: A better algorithm could be used to find a good spu to be + * used as reference location for the ctxs chain. + * Also, a isolated SPU should never be chosen here. + */ + for (node = 0; node < MAX_NUMNODES; node++) { + list_for_each_entry(spu, &be_spu_info[node].available_spus, + available_list) { + if (!mem_aff || spu->has_mem_affinity) + return spu; + } + } + BUG_ON(1); + return NULL; +} + +static void aff_set_ref_point_location(struct spu_gang *gang) +{ + int mem_aff, gs, lowest_rel_displ; + struct spu_context *ctx; + struct spu *tmp; + + mem_aff = gang->aff_ref_point->aff_flags & AFF_HAS_MEM_AFFINITY; + lowest_rel_displ = 0; + gs = 0; + list_for_each_entry(tmp, &gang->aff_list_head, aff_list) + gs++; + + list_for_each_entry_reverse(ctx, &gang->aff_ref_point->aff_list, + aff_list) { + if (&ctx->aff_list == &gang->aff_list_head) + break; + lowest_rel_displ = ctx->aff_rel_displ; + } + + gang->aff_ref_point_location = aff_ref_location(mem_aff, gs, ctx->prio, + lowest_rel_displ); +} + +static struct spu* ctx_location(struct spu *ref, int rel_displ) +{ + struct spu *spu; + + /* TODO: skip isolated SPUs */ + if (rel_displ >= 0) { + list_for_each_entry(spu, ref->aff_list.prev, aff_list) { + if (rel_displ == 0) + return spu; + rel_displ--; + } + } else { + list_for_each_entry_reverse(spu, ref->aff_list.next, aff_list) { + if (rel_displ == 0) + return spu; + rel_displ++; + } + } + return NULL; +} + +/** + * affinity_check is called each time a context is going to be scheduled. + * It returns the spu ptr on which the context must run. + */ +struct spu* affinity_check(struct spu_context *ctx) +{ + struct spu_gang *gang; + + if (list_empty(&ctx->aff_list)) + return NULL; + gang = ctx->gang; + if (!gang->aff_ref_point_location) { + if (!gang->aff_flags & AFF_MERGED) + aff_merge_remaining_ctxs(gang); + if (!gang->aff_flags & AFF_REL_DISPL_SET) + aff_set_rel_displ(gang); + aff_set_ref_point_location(gang); + } + BUG_ON(!gang->aff_ref_point_location); + return ctx_location(gang->aff_ref_point_location, ctx->aff_rel_displ); +} 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 @@ -90,6 +90,7 @@ struct spu_context { struct list_head aff_list; int aff_flags; + int aff_rel_displ; }; /* Flag bits for spu_context aff_flags */ @@ -107,6 +108,8 @@ struct spu_gang { struct list_head aff_list_head; struct mutex aff_mutex; int aff_flags; + struct spu *aff_ref_point_location; + atomic_t aff_sched_count; }; /* Flag bits for spu_gang aff_flags */ @@ -187,6 +190,9 @@ int put_spu_gang(struct spu_gang *gang); void spu_gang_remove_ctx(struct spu_gang *gang, struct spu_context *ctx); void spu_gang_add_ctx(struct spu_gang *gang, struct spu_context *ctx); +/* affinity */ +struct spu *affinity_check(struct spu_context *ctx); + /* context management */ static inline void spu_acquire(struct spu_context *ctx) { Index: linux-2.6/arch/powerpc/platforms/cell/spufs/gang.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/gang.c +++ linux-2.6/arch/powerpc/platforms/cell/spufs/gang.c @@ -39,6 +39,7 @@ struct spu_gang *alloc_spu_gang(void) INIT_LIST_HEAD(&gang->list); INIT_LIST_HEAD(&gang->aff_list_head); + gang->aff_ref_point_location = NULL; out: return gang; } @@ -77,8 +78,10 @@ void spu_gang_remove_ctx(struct spu_gang WARN_ON(ctx->gang != gang); list_del_init(&ctx->gang_list); gang->contexts--; - if (ctx->aff_flags & (AFF_HAS_SPU_AFFINITY | AFF_HAS_MEM_AFFINITY)) + if (ctx->aff_flags & (AFF_HAS_SPU_AFFINITY | AFF_HAS_MEM_AFFINITY)) { list_del(&ctx->aff_list); + gang->aff_flags &= ~AFF_REL_DISPL_SET; + } mutex_unlock(&gang->mutex); put_spu_gang(gang);