Subject: spufs: extension of spu_create to support affinity definition From: Andre Detsch This patch adds support for additional flags at spu_create, which relate to the establishment of affinity between contexts and contexts to memory. A fourth, optional, parameter is supported. This parameter represent a affinity neighbor of the context being created, and is used when defining SPU-SPU affinity. Affinity is represented as a doubly linked list of spu_contexts. Signed-off-by: Andre Detsch Signed-off-by: Arnd Bergmann --- Index: linux-2.6/arch/powerpc/platforms/cell/spufs/inode.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/inode.c +++ linux-2.6/arch/powerpc/platforms/cell/spufs/inode.c @@ -307,11 +307,102 @@ out: return ret; } -static int spufs_create_context(struct inode *inode, - struct dentry *dentry, - struct vfsmount *mnt, int flags, int mode) +static struct spu_context * +spufs_assert_affinity(unsigned int flags, struct spu_gang *gang, + struct file *filp) +{ + struct spu_context *tmp, *neighbor = NULL; + int count, node; + int aff_supp; + + aff_supp = !list_empty(&(list_entry(be_spu_info[0].available_spus.next, + struct spu, available_list))->aff_list); + + if (!aff_supp) + return ERR_PTR(-ENOTSUPP); + + if (flags & SPU_CREATE_GANG) + return ERR_PTR(-EINVAL); + + if (flags & SPU_CREATE_AFFINITY_MEM && + gang->aff_ref_point && + gang->aff_ref_point->aff_flags & AFF_HAS_MEM_AFFINITY) + return ERR_PTR(-EEXIST); + + if (flags & SPU_CREATE_AFFINITY_SPU) { + if (!filp || filp->f_op != &spufs_context_fops) + return ERR_PTR(-EINVAL); + + neighbor = SPUFS_I(filp->f_dentry->d_inode)->i_ctx; + + if (!list_empty(&neighbor->aff_list) && + !(neighbor->aff_flags & AFF_SUBLIST_HEAD) && + !list_entry(neighbor->aff_list.next, struct spu_context, + aff_list)->aff_flags & AFF_SUBLIST_HEAD) + return ERR_PTR(-EEXIST); + + if (gang != neighbor->gang) + return ERR_PTR(-EINVAL); + + count = 1; + list_for_each_entry(tmp, &gang->aff_list_head, aff_list) + count++; + if (list_empty(&neighbor->aff_list)) + count++; + + for (node = 0; node < MAX_NUMNODES; node++) { + if ((be_spu_info[node].n_spus - atomic_read( + &be_spu_info[node].isolated_spus)) > count) + break; + } + + if (node == MAX_NUMNODES) + return ERR_PTR(-EEXIST); + } + + return neighbor; +} + +static void +spufs_set_affinity(unsigned int flags, struct spu_context *ctx, + struct spu_context *neighbor) +{ + if (flags & SPU_CREATE_AFFINITY_MEM) { + ctx->gang->aff_ref_point = ctx; + ctx->aff_flags |= AFF_HAS_MEM_AFFINITY; + } + if (flags & SPU_CREATE_AFFINITY_SPU) { + if (list_empty(&neighbor->aff_list)) { + list_add_tail(&neighbor->aff_list, + &ctx->gang->aff_list_head); + neighbor->aff_flags |= AFF_SUBLIST_HEAD; + } + + if (list_is_last(&neighbor->aff_list,&ctx->gang->aff_list_head) + || list_entry(neighbor->aff_list.next, struct + spu_context, aff_list)->aff_flags & AFF_SUBLIST_HEAD) { + list_add(&ctx->aff_list, &neighbor->aff_list); + } else { + list_add_tail(&ctx->aff_list, &neighbor->aff_list); + if (neighbor->aff_flags & AFF_SUBLIST_HEAD) { + neighbor->aff_flags &= ~AFF_SUBLIST_HEAD; + ctx->aff_flags |= AFF_SUBLIST_HEAD; + } + } + + if (!ctx->gang->aff_ref_point) + ctx->gang->aff_ref_point = ctx; + } +} + +static int +spufs_create_context(struct inode *inode, struct dentry *dentry, + struct vfsmount *mnt, int flags, int mode, + struct file *aff_filp) { int ret; + struct spu_gang *gang = NULL; + struct spu_context *neighbor = NULL; ret = -EPERM; if ((flags & SPU_CREATE_NOSCHED) && @@ -326,10 +417,29 @@ static int spufs_create_context(struct i ret = -ENODEV; if ((flags & SPU_CREATE_ISOLATE) && !isolated_loader) goto out_unlock; + if (flags & (SPU_CREATE_AFFINITY_MEM | SPU_CREATE_AFFINITY_SPU)) { + gang = SPUFS_I(inode)->i_gang; + mutex_lock(&gang->aff_mutex); + neighbor = spufs_assert_affinity(flags, gang, aff_filp); + if (IS_ERR(neighbor)) { + ret = PTR_ERR(neighbor); + mutex_unlock(&gang->aff_mutex); + goto out_unlock; + } + } ret = spufs_mkdir(inode, dentry, flags, mode & S_IRWXUGO); - if (ret) + if (ret) { + if (flags & (SPU_CREATE_AFFINITY_MEM | SPU_CREATE_AFFINITY_SPU)) + mutex_unlock(&gang->aff_mutex); goto out_unlock; + } + + if (flags & (SPU_CREATE_AFFINITY_MEM | SPU_CREATE_AFFINITY_SPU)) { + spufs_set_affinity(flags, SPUFS_I(dentry->d_inode)->i_ctx, + neighbor); + mutex_unlock(&gang->aff_mutex); + } /* * get references for dget and mntget, will be released @@ -471,7 +581,8 @@ out: static struct file_system_type spufs_type; -long spufs_create(struct nameidata *nd, unsigned int flags, mode_t mode) +long spufs_create(struct nameidata *nd, unsigned int flags, mode_t mode, + struct file *filp) { struct dentry *dentry; int ret; @@ -508,7 +619,7 @@ long spufs_create(struct nameidata *nd, dentry, nd->mnt, mode); else return spufs_create_context(nd->dentry->d_inode, - dentry, nd->mnt, flags, mode); + dentry, nd->mnt, flags, mode, filp); out_dput: dput(dentry); 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 @@ -87,15 +87,32 @@ struct spu_context { unsigned long rt_priority; int policy; int prio; + + struct list_head aff_list; + int aff_flags; }; +/* Flag bits for spu_context aff_flags */ +#define AFF_HAS_MEM_AFFINITY 1 +#define AFF_HAS_SPU_AFFINITY 2 +#define AFF_SUBLIST_HEAD 4 + struct spu_gang { struct list_head list; struct mutex mutex; struct kref kref; int contexts; + + struct spu_context *aff_ref_point; + struct list_head aff_list_head; + struct mutex aff_mutex; + int aff_flags; }; +/* Flag bits for spu_gang aff_flags */ +#define AFF_REL_DISPL_SET 1 +#define AFF_MERGED 2 + struct mfc_dma_command { int32_t pad; /* reserved */ uint32_t lsa; /* local storage address */ @@ -160,7 +177,7 @@ extern struct tree_descr spufs_dir_nosch long spufs_run_spu(struct file *file, struct spu_context *ctx, u32 *npc, u32 *status); long spufs_create(struct nameidata *nd, - unsigned int flags, mode_t mode); + unsigned int flags, mode_t mode, struct file *filp); extern struct file_operations spufs_context_fops; /* gang management */ Index: linux-2.6/include/asm-powerpc/spu.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/spu.h +++ linux-2.6/include/asm-powerpc/spu.h @@ -161,6 +161,7 @@ extern struct be_spu_info be_spu_info[]; struct spu *spu_alloc(void); struct spu *spu_alloc_node(int node); +struct spu *spu_alloc_spu(struct spu *spu); void spu_free(struct spu *spu); int spu_irq_class_0_bottom(struct spu *spu); int spu_irq_class_1_bottom(struct spu *spu); @@ -177,7 +178,8 @@ extern long spu_sys_callback(struct spu_ struct file; extern struct spufs_calls { asmlinkage long (*create_thread)(const char __user *name, - unsigned int flags, mode_t mode); + unsigned int flags, mode_t mode, + struct file *filp); asmlinkage long (*spu_run)(struct file *filp, __u32 __user *unpc, __u32 __user *ustatus); struct module *owner; @@ -204,8 +206,10 @@ struct spu_coredump_calls { #define SPU_CREATE_GANG 0x0002 #define SPU_CREATE_NOSCHED 0x0004 #define SPU_CREATE_ISOLATE 0x0008 +#define SPU_CREATE_AFFINITY_SPU 0x0010 +#define SPU_CREATE_AFFINITY_MEM 0x0020 -#define SPU_CREATE_FLAG_ALL 0x000f /* mask of all valid flags */ +#define SPU_CREATE_FLAG_ALL 0x003f /* mask of all valid flags */ #ifdef CONFIG_SPU_FS_MODULE 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 @@ -35,7 +35,9 @@ struct spu_gang *alloc_spu_gang(void) kref_init(&gang->kref); mutex_init(&gang->mutex); + mutex_init(&gang->aff_mutex); INIT_LIST_HEAD(&gang->list); + INIT_LIST_HEAD(&gang->aff_list_head); out: return gang; @@ -75,6 +77,8 @@ 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)) + list_del(&ctx->aff_list); mutex_unlock(&gang->mutex); put_spu_gang(gang); 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 @@ -51,6 +51,7 @@ struct spu_context *alloc_spu_context(st ctx->state = SPU_STATE_SAVED; ctx->ops = &spu_backing_ops; ctx->owner = get_task_mm(current); + INIT_LIST_HEAD(&ctx->aff_list); if (gang) spu_gang_add_ctx(gang, ctx); ctx->rt_priority = current->rt_priority; Index: linux-2.6/include/linux/syscalls.h =================================================================== --- linux-2.6.orig/include/linux/syscalls.h +++ linux-2.6/include/linux/syscalls.h @@ -549,7 +549,7 @@ asmlinkage long sys_inotify_rm_watch(int asmlinkage long sys_spu_run(int fd, __u32 __user *unpc, __u32 __user *ustatus); asmlinkage long sys_spu_create(const char __user *name, - unsigned int flags, mode_t mode); + unsigned int flags, mode_t mode, int fd); asmlinkage long sys_mknodat(int dfd, const char __user * filename, int mode, unsigned dev); Index: linux-2.6/arch/powerpc/platforms/cell/spu_syscalls.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spu_syscalls.c +++ linux-2.6/arch/powerpc/platforms/cell/spu_syscalls.c @@ -34,14 +34,27 @@ struct spufs_calls spufs_calls = { * this file is not used and the syscalls directly enter the fs code */ asmlinkage long sys_spu_create(const char __user *name, - unsigned int flags, mode_t mode) + unsigned int flags, mode_t mode, int fd) { long ret; struct module *owner = spufs_calls.owner; + struct file *filp; + int fput_needed; ret = -ENOSYS; if (owner && try_module_get(owner)) { - ret = spufs_calls.create_thread(name, flags, mode); + if (flags & SPU_CREATE_AFFINITY_SPU) { + filp = fget_light(fd, &fput_needed); + if (filp) { + ret = spufs_calls.create_thread(name, flags, + mode, filp); + fput_light(filp, fput_needed); + } + } + else { + ret = spufs_calls.create_thread(name, flags, + mode, NULL); + } module_put(owner); } return ret; Index: linux-2.6/arch/powerpc/platforms/cell/spufs/syscalls.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/syscalls.c +++ linux-2.6/arch/powerpc/platforms/cell/spufs/syscalls.c @@ -76,8 +76,8 @@ asmlinkage long sys_spu_run(int fd, __u3 } #endif -asmlinkage long sys_spu_create(const char __user *pathname, - unsigned int flags, mode_t mode) +asmlinkage long do_spu_create(const char __user *pathname, unsigned int flags, + mode_t mode, struct file *filp) { char *tmp; int ret; @@ -90,7 +90,10 @@ asmlinkage long sys_spu_create(const cha ret = path_lookup(tmp, LOOKUP_PARENT| LOOKUP_OPEN|LOOKUP_CREATE, &nd); if (!ret) { - ret = spufs_create(&nd, flags, mode); + if (flags & SPU_CREATE_AFFINITY_SPU) + ret = spufs_create(&nd, flags, mode, filp); + else + ret = spufs_create(&nd, flags, mode, NULL); path_release(&nd); } putname(tmp); @@ -99,8 +102,32 @@ asmlinkage long sys_spu_create(const cha return ret; } +#ifndef MODULE +asmlinkage long sys_spu_create(const char __user *pathname, unsigned int flags, + mode_t mode, int fd) +{ + int fput_needed; + struct file *filp; + long ret; + + if (flags & SPU_CREATE_AFFINITY_SPU) { + ret = -EBADF; + filp = fget_light(fd, &fput_needed); + if (filp) { + ret = do_spu_create(pathname, flags, mode, filp); + fput_light(filp, fput_needed); + } + } + else { + ret = do_spu_create(pathname, flags, mode, NULL); + } + + return ret; +} +#endif + struct spufs_calls spufs_calls = { - .create_thread = sys_spu_create, + .create_thread = do_spu_create, .spu_run = do_spu_run, .owner = THIS_MODULE, };