b/arch/powerpc/platforms/cell/spufs/context.c
@@ -32,6 +32,22 @@
atomic_t nr_spu_contexts = ATOMIC_INIT(0);
+static void inc_active_gangs(struct spu_gang *gang)
+{
+ if (atomic_inc_return(&spu_active_gangs[gang->contexts]) == 1) {
+ atomic_set(&largest_active_gang, gang->contexts);
+ mb(); /* XXX atomic_set doesn't have a sync */
+ }
+}
+
+static void dec_active_gangs(struct spu_gang *gang)
+{
+ if (!atomic_dec_return(&spu_active_gangs[gang->contexts])) {
+ atomic_set(&largest_active_gang, gang->contexts);
+ mb(); /* XXX atomic_set doesn't have a sync */
+ }
+}
+
struct spu_context *alloc_spu_context(struct spu_gang *gang)
{
struct spu_context *ctx;
@@ -57,6 +73,8 @@ struct spu_context *alloc_spu_context(struct spu_gang *gang)
if (spu_init_csa(&ctx->csa))
goto out_free_gang;
+ inc_active_gangs(gang);
+
/* If the gang is running, it needs to be stopped, since we have a
* new context that needs to be gang scheduled. Gangs are allowed
* to grow and shrink over time, but they are unscheduled when it
@@ -89,6 +107,7 @@ struct spu_context *alloc_spu_context(struct spu_gang
*gang)
ctx->stats.util_state = SPU_UTIL_IDLE_LOADED;
atomic_inc(&nr_spu_contexts);
+
goto out;
out_free_gang:
@@ -123,6 +142,7 @@ void destroy_spu_context(struct kref *kref)
if (ctx->prof_priv_kref)
kref_put(ctx->prof_priv_kref, ctx->prof_priv_release);
atomic_dec(&nr_spu_contexts);
+ dec_active_gangs(gang);
kfree(ctx->switch_log);
kfree(ctx);
}
b/arch/powerpc/platforms/cell/spufs/inode.c
@@ -263,6 +263,7 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry,
unsigned int flags,
struct inode *inode;
struct spu_context *ctx, *gang_ctx;
struct spu_gang *gang;
+ int node, avail_spus;
ret = -ENOSPC;
inode = spufs_new_inode(dir->i_sb, mode | S_IFDIR);
@@ -280,6 +281,27 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry,
unsigned int flags,
}
}
+ for (node = 0, avail_spus = 0; node < MAX_NUMNODES; node++) {
+ avail_spus += cbe_spu_info[node].n_spus - atomic_read(
+ &cbe_spu_info[node].reserved_spus);
+ }
+
+ /* Ensure there are enough available spus for scheduling. */
+ if (flags & SPU_CREATE_NOSCHED) {
+ /* Can't reserve an spu if it would starve an active gang */
+ if (avail_spus <= atomic_read(&largest_active_gang) + 1) {
+ ret = -EPERM;
+ goto out_iput;
+ }
+ }
+ else {
+ /* Can't create a gang too big either. */
+ if (!avail_spus || (gang && gang->contexts + 1 > avail_spus)) {
+ ret = -EPERM;
+ goto out_iput;
+ }
+ }
+
if (dir->i_mode & S_ISGID) {
inode->i_gid = dir->i_gid;
inode->i_mode &= S_ISGID;
b/arch/powerpc/platforms/cell/spufs/sched.c
@@ -90,6 +90,9 @@ static struct timer_list spusched_timer;
static struct timer_list spuloadavg_timer;
static void spu_unschedule(struct spu_gang *gang);
+atomic_t *spu_active_gangs;
+atomic_t largest_active_gang;
+
/*
* Priority of a normal, non-rt, non-niced'd process (aka nice level 0).
*/
@@ -1506,12 +1509,27 @@ static const struct file_operations spu_loadavg_fops =
{
int __init spu_sched_init(void)
{
struct proc_dir_entry *entry;
- int err = -ENOMEM, i;
+ int err = -ENOMEM, node, nspus, i;
spu_prio = kzalloc(sizeof(struct spu_prio_array), GFP_KERNEL);
if (!spu_prio)
goto out;
+ /*
+ * A gang cannot be larger than the number of spus in the system
+ * since they have to be scheduled at the same time. Allocate an
+ * array of that length to keep track of the size of active gangs.
+ * We need to limit the number of spus that can be reserved to
+ * the starvation of gangs. A reserved spus can be used by the
+ * scheduler.
+ */
+ for (node = 0, nspus = 0; node < MAX_NUMNODES; node++)
+ nspus += cbe_spu_info[node].n_spus;
+ spu_active_gangs = kzalloc(sizeof(atomic_t) * nspus, GFP_KERNEL);
+ if (!spu_active_gangs)
+ goto out_free_spu_prio;
+ atomic_set(&largest_active_gang, 0);
+
for (i = 0; i < MAX_PRIO; i++) {
INIT_LIST_HEAD(&spu_prio->runq[i]);
__clear_bit(i, spu_prio->bitmap);
@@ -1565,5 +1583,6 @@ void spu_sched_exit(void)
}
mutex_unlock(&cbe_spu_info[node].list_mutex);
}
+ kfree(spu_active_gangs);
kfree(spu_prio);
}
b/arch/powerpc/platforms/cell/spufs/spufs.h
@@ -296,6 +296,17 @@ 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);
+/*
+ * Each element of the spu_active_gang[] identifies the number of active
+ * gangs of at least that size. largest_active_gang identifies the size of
+ * the largest aactive gang in the system. Array elements are incremented
+ * as contexts are created and they are decremented as contexts are
destroyed.
+ * The first context in a gang increments element[1], the second element[2],
+ * and so on. largest_active_gang is set to the highest non-zero array
element.
+ */
+extern atomic_t largest_active_gang;
+extern atomic_t *spu_active_gangs;
+
/* fault handling */