[CELL] spufs: extension of spu_create to support affinity definition
authorArnd Bergmann <arnd@arndb.de>
Fri, 20 Jul 2007 19:39:47 +0000 (21:39 +0200)
committerArnd Bergmann <arnd@klappe.arndb.de>
Fri, 20 Jul 2007 19:42:15 +0000 (21:42 +0200)
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 <adetsch@br.ibm.com>
Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com>
arch/powerpc/platforms/cell/spu_syscalls.c
arch/powerpc/platforms/cell/spufs/context.c
arch/powerpc/platforms/cell/spufs/gang.c
arch/powerpc/platforms/cell/spufs/inode.c
arch/powerpc/platforms/cell/spufs/spufs.h
arch/powerpc/platforms/cell/spufs/syscalls.c
include/asm-powerpc/spu.h
include/linux/syscalls.h

index 261b507..dd2c668 100644 (file)
@@ -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 neighbor_fd)
 {
        long ret;
        struct module *owner = spufs_calls.owner;
+       struct file *neighbor;
+       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) {
+                       neighbor = fget_light(neighbor_fd, &fput_needed);
+                       if (neighbor) {
+                               ret = spufs_calls.create_thread(name, flags,
+                                                               mode, neighbor);
+                               fput_light(neighbor, fput_needed);
+                       }
+               }
+               else {
+                       ret = spufs_calls.create_thread(name, flags,
+                                                       mode, NULL);
+               }
                module_put(owner);
        }
        return ret;
index 6b091ea..a7efb99 100644 (file)
@@ -55,6 +55,7 @@ struct spu_context *alloc_spu_context(struct spu_gang *gang)
        ctx->ops = &spu_backing_ops;
        ctx->owner = get_task_mm(current);
        INIT_LIST_HEAD(&ctx->rq);
+       INIT_LIST_HEAD(&ctx->aff_list);
        if (gang)
                spu_gang_add_ctx(gang, ctx);
        ctx->cpus_allowed = current->cpus_allowed;
index 212ea78..0a752ce 100644 (file)
@@ -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;
@@ -73,6 +75,8 @@ void spu_gang_remove_ctx(struct spu_gang *gang, struct spu_context *ctx)
 {
        mutex_lock(&gang->mutex);
        WARN_ON(ctx->gang != gang);
+       if (!list_empty(&ctx->aff_list))
+               list_del_init(&ctx->aff_list);
        list_del_init(&ctx->gang_list);
        gang->contexts--;
        mutex_unlock(&gang->mutex);
index 7eb4d6c..b3d0dd1 100644 (file)
@@ -316,11 +316,107 @@ 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;
+       int count, node;
+       int aff_supp;
+
+       aff_supp = !list_empty(&(list_entry(cbe_spu_info[0].spus.next,
+                                       struct spu, cbe_list))->aff_list);
+
+       if (!aff_supp)
+               return ERR_PTR(-EINVAL);
+
+       if (flags & SPU_CREATE_GANG)
+               return ERR_PTR(-EINVAL);
+
+       if (flags & SPU_CREATE_AFFINITY_MEM &&
+           gang->aff_ref_ctx &&
+           gang->aff_ref_ctx->flags & SPU_CREATE_AFFINITY_MEM)
+               return ERR_PTR(-EEXIST);
+
+       if (gang->aff_flags & AFF_MERGED)
+               return ERR_PTR(-EBUSY);
+
+       neighbor = NULL;
+       if (flags & SPU_CREATE_AFFINITY_SPU) {
+               if (!filp || filp->f_op != &spufs_context_fops)
+                       return ERR_PTR(-EINVAL);
+
+               neighbor = get_spu_context(
+                               SPUFS_I(filp->f_dentry->d_inode)->i_ctx);
+
+               if (!list_empty(&neighbor->aff_list) && !(neighbor->aff_head) &&
+                   !list_is_last(&neighbor->aff_list, &gang->aff_list_head) &&
+                   !list_entry(neighbor->aff_list.next, struct spu_context,
+                   aff_list)->aff_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 ((cbe_spu_info[node].n_spus - atomic_read(
+                               &cbe_spu_info[node].reserved_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_ctx = ctx;
+
+       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_head = 1;
+               }
+
+               if (list_is_last(&neighbor->aff_list, &ctx->gang->aff_list_head)
+                   || list_entry(neighbor->aff_list.next, struct spu_context,
+                                                       aff_list)->aff_head) {
+                       list_add(&ctx->aff_list, &neighbor->aff_list);
+               } else  {
+                       list_add_tail(&ctx->aff_list, &neighbor->aff_list);
+                       if (neighbor->aff_head) {
+                               neighbor->aff_head = 0;
+                               ctx->aff_head = 1;
+                       }
+               }
+
+               if (!ctx->gang->aff_ref_ctx)
+                       ctx->gang->aff_ref_ctx = 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;
+       int affinity;
+       struct spu_gang *gang;
+       struct spu_context *neighbor;
 
        ret = -EPERM;
        if ((flags & SPU_CREATE_NOSCHED) &&
@@ -336,9 +432,29 @@ static int spufs_create_context(struct inode *inode,
        if ((flags & SPU_CREATE_ISOLATE) && !isolated_loader)
                goto out_unlock;
 
+       gang = NULL;
+       neighbor = NULL;
+       affinity = flags & (SPU_CREATE_AFFINITY_MEM | SPU_CREATE_AFFINITY_SPU);
+       if (affinity) {
+               gang = SPUFS_I(inode)->i_gang;
+               ret = -EINVAL;
+               if (!gang)
+                       goto out_unlock;
+               mutex_lock(&gang->aff_mutex);
+               neighbor = spufs_assert_affinity(flags, gang, aff_filp);
+               if (IS_ERR(neighbor)) {
+                       ret = PTR_ERR(neighbor);
+                       goto out_aff_unlock;
+               }
+       }
+
        ret = spufs_mkdir(inode, dentry, flags, mode & S_IRWXUGO);
        if (ret)
-               goto out_unlock;
+               goto out_aff_unlock;
+
+       if (affinity)
+               spufs_set_affinity(flags, SPUFS_I(dentry->d_inode)->i_ctx,
+                                                               neighbor);
 
        /*
         * get references for dget and mntget, will be released
@@ -352,6 +468,9 @@ static int spufs_create_context(struct inode *inode,
                goto out;
        }
 
+out_aff_unlock:
+       if (affinity)
+               mutex_unlock(&gang->aff_mutex);
 out_unlock:
        mutex_unlock(&inode->i_mutex);
 out:
@@ -450,7 +569,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;
@@ -487,7 +607,7 @@ long spufs_create(struct nameidata *nd, unsigned int flags, mode_t mode)
                                        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 03e8315..36da179 100644 (file)
@@ -109,6 +109,9 @@ struct spu_context {
                unsigned long long class2_intr_base; /* # at last ctx switch */
                unsigned long long libassist;
        } stats;
+
+       struct list_head aff_list;
+       int aff_head;
 };
 
 struct spu_gang {
@@ -116,8 +119,17 @@ struct spu_gang {
        struct mutex mutex;
        struct kref kref;
        int contexts;
+
+       struct spu_context *aff_ref_ctx;
+       struct list_head aff_list_head;
+       struct mutex aff_mutex;
+       int aff_flags;
 };
 
+/* Flag bits for spu_gang aff_flags */
+#define AFF_OFFSETS_SET                1
+#define AFF_MERGED             2
+
 struct mfc_dma_command {
        int32_t pad;    /* reserved */
        uint32_t lsa;   /* local storage address */
@@ -182,8 +194,8 @@ extern struct tree_descr spufs_dir_nosched_contents[];
 
 /* system call implementation */
 long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *status);
-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);
 extern const struct file_operations spufs_context_fops;
 
 /* gang management */
index 13a383c..43f0fb8 100644 (file)
@@ -76,8 +76,8 @@ asmlinkage long sys_spu_run(int fd, __u32 __user *unpc, __u32 __user *ustatus)
 }
 #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 *neighbor)
 {
        char *tmp;
        int ret;
@@ -90,7 +90,7 @@ asmlinkage long sys_spu_create(const char __user *pathname,
                ret = path_lookup(tmp, LOOKUP_PARENT|
                                LOOKUP_OPEN|LOOKUP_CREATE, &nd);
                if (!ret) {
-                       ret = spufs_create(&nd, flags, mode);
+                       ret = spufs_create(&nd, flags, mode, neighbor);
                        path_release(&nd);
                }
                putname(tmp);
@@ -99,8 +99,32 @@ asmlinkage long sys_spu_create(const char __user *pathname,
        return ret;
 }
 
+#ifndef MODULE
+asmlinkage long sys_spu_create(const char __user *pathname, unsigned int flags,
+                               mode_t mode, int neighbor_fd)
+{
+       int fput_needed;
+       struct file *neighbor;
+       long ret;
+
+       if (flags & SPU_CREATE_AFFINITY_SPU) {
+               ret = -EBADF;
+               neighbor = fget_light(neighbor_fd, &fput_needed);
+               if (neighbor) {
+                       ret = do_spu_create(pathname, flags, mode, neighbor);
+                       fput_light(neighbor, 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,
 };
index 18e558b..24f352d 100644 (file)
@@ -196,6 +196,7 @@ extern struct cbe_spu_info cbe_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);
@@ -227,7 +228,8 @@ extern long spu_sys_callback(struct spu_syscall_block *s);
 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 *neighbor);
        asmlinkage long (*spu_run)(struct file *filp, __u32 __user *unpc,
                                                __u32 __user *ustatus);
        struct module *owner;
@@ -254,8 +256,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 7a8b1e3..61def7c 100644 (file)
@@ -549,7 +549,7 @@ asmlinkage long sys_inotify_rm_watch(int fd, u32 wd);
 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);