Embed a struct path into struct nameidata instead of nd->{dentry,mnt}
[safe/jmp/linux-2.6] / arch / powerpc / platforms / cell / spufs / inode.c
index 01c4805..6d1228c 100644 (file)
@@ -1,3 +1,4 @@
+
 /*
  * SPU file system
  *
@@ -43,6 +44,7 @@
 
 static struct kmem_cache *spufs_inode_cache;
 char *isolated_loader;
+static int isolated_loader_size;
 
 static struct inode *
 spufs_alloc_inode(struct super_block *sb)
@@ -67,7 +69,7 @@ spufs_destroy_inode(struct inode *inode)
 }
 
 static void
-spufs_init_once(void *p, struct kmem_cache * cachep, unsigned long flags)
+spufs_init_once(struct kmem_cache *cachep, void *p)
 {
        struct spufs_inode_info *ei = p;
 
@@ -170,6 +172,7 @@ static int spufs_rmdir(struct inode *parent, struct dentry *dir)
 {
        /* remove all entries */
        spufs_prune_dir(dir);
+       d_drop(dir);
 
        return simple_rmdir(parent, dir);
 }
@@ -177,7 +180,7 @@ static int spufs_rmdir(struct inode *parent, struct dentry *dir)
 static int spufs_fill_dir(struct dentry *dir, struct tree_descr *files,
                          int mode, struct spu_context *ctx)
 {
-       struct dentry *dentry;
+       struct dentry *dentry, *tmp;
        int ret;
 
        while (files->name && files->name[0]) {
@@ -193,7 +196,20 @@ static int spufs_fill_dir(struct dentry *dir, struct tree_descr *files,
        }
        return 0;
 out:
-       spufs_prune_dir(dir);
+       /*
+        * remove all children from dir. dir->inode is not set so don't
+        * just simply use spufs_prune_dir() and panic afterwards :)
+        * dput() looks like it will do the right thing:
+        * - dec parent's ref counter
+        * - remove child from parent's child list
+        * - free child's inode if possible
+        * - free child
+        */
+       list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) {
+               dput(dentry);
+       }
+
+       shrink_dcache_parent(dir);
        return ret;
 }
 
@@ -219,10 +235,6 @@ static int spufs_dir_close(struct inode *inode, struct file *file)
        return dcache_dir_close(inode, file);
 }
 
-const struct inode_operations spufs_dir_inode_operations = {
-       .lookup = simple_lookup,
-};
-
 const struct file_operations spufs_context_fops = {
        .open           = dcache_dir_open,
        .release        = spufs_dir_close,
@@ -256,7 +268,7 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
                goto out_iput;
 
        ctx->flags = flags;
-       inode->i_op = &spufs_dir_inode_operations;
+       inode->i_op = &simple_dir_inode_operations;
        inode->i_fop = &simple_dir_operations;
        if (flags & SPU_CREATE_NOSCHED)
                ret = spufs_fill_dir(dentry, spufs_dir_nosched_contents,
@@ -274,6 +286,7 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
        goto out;
 
 out_free_ctx:
+       spu_forget(ctx);
        put_spu_context(ctx);
 out_iput:
        iput(inode);
@@ -306,11 +319,117 @@ 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, *err;
+       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) {
+                       err = ERR_PTR(-EEXIST);
+                       goto out_put_neighbor;
+               }
+
+               if (gang != neighbor->gang) {
+                       err = ERR_PTR(-EINVAL);
+                       goto out_put_neighbor;
+               }
+
+               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) {
+                       err = ERR_PTR(-EEXIST);
+                       goto out_put_neighbor;
+               }
+       }
+
+       return neighbor;
+
+out_put_neighbor:
+       put_spu_context(neighbor);
+       return err;
+}
+
+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) &&
@@ -326,9 +445,32 @@ 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);
+               if (neighbor)
+                       put_spu_context(neighbor);
+       }
 
        /*
         * get references for dget and mntget, will be released
@@ -342,6 +484,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:
@@ -349,37 +494,6 @@ out:
        return ret;
 }
 
-static int spufs_rmgang(struct inode *root, struct dentry *dir)
-{
-       /* FIXME: this fails if the dir is not empty,
-                 which causes a leak of gangs. */
-       return simple_rmdir(root, dir);
-}
-
-static int spufs_gang_close(struct inode *inode, struct file *file)
-{
-       struct inode *parent;
-       struct dentry *dir;
-       int ret;
-
-       dir = file->f_path.dentry;
-       parent = dir->d_parent->d_inode;
-
-       ret = spufs_rmgang(parent, dir);
-       WARN_ON(ret);
-
-       return dcache_dir_close(inode, file);
-}
-
-const struct file_operations spufs_gang_fops = {
-       .open           = dcache_dir_open,
-       .release        = spufs_gang_close,
-       .llseek         = dcache_dir_lseek,
-       .read           = generic_read_dir,
-       .readdir        = dcache_readdir,
-       .fsync          = simple_sync_file,
-};
-
 static int
 spufs_mkgang(struct inode *dir, struct dentry *dentry, int mode)
 {
@@ -403,11 +517,10 @@ spufs_mkgang(struct inode *dir, struct dentry *dentry, int mode)
        if (!gang)
                goto out_iput;
 
-       inode->i_op = &spufs_dir_inode_operations;
+       inode->i_op = &simple_dir_inode_operations;
        inode->i_fop = &simple_dir_operations;
 
        d_instantiate(dentry, inode);
-       dget(dentry);
        dir->i_nlink++;
        dentry->d_inode->i_nlink++;
        return ret;
@@ -437,7 +550,7 @@ static int spufs_gang_open(struct dentry *dentry, struct vfsmount *mnt)
                goto out;
        }
 
-       filp->f_op = &spufs_gang_fops;
+       filp->f_op = &simple_dir_operations;
        fd_install(ret, filp);
 out:
        return ret;
@@ -458,8 +571,10 @@ static int spufs_create_gang(struct inode *inode,
         * in error path of *_open().
         */
        ret = spufs_gang_open(dget(dentry), mntget(mnt));
-       if (ret < 0)
-               WARN_ON(spufs_rmgang(inode, dentry));
+       if (ret < 0) {
+               int err = simple_rmdir(inode, dentry);
+               WARN_ON(err);
+       }
 
 out:
        mutex_unlock(&inode->i_mutex);
@@ -470,14 +585,15 @@ 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;
 
        ret = -EINVAL;
        /* check if we are on spufs */
-       if (nd->dentry->d_sb->s_type != &spufs_type)
+       if (nd->path.dentry->d_sb->s_type != &spufs_type)
                goto out;
 
        /* don't accept undefined flags */
@@ -485,9 +601,9 @@ long spufs_create(struct nameidata *nd, unsigned int flags, mode_t mode)
                goto out;
 
        /* only threads can be underneath a gang */
-       if (nd->dentry != nd->dentry->d_sb->s_root) {
+       if (nd->path.dentry != nd->path.dentry->d_sb->s_root) {
                if ((flags & SPU_CREATE_GANG) ||
-                   !SPUFS_I(nd->dentry->d_inode)->i_gang)
+                   !SPUFS_I(nd->path.dentry->d_inode)->i_gang)
                        goto out;
        }
 
@@ -503,16 +619,17 @@ long spufs_create(struct nameidata *nd, unsigned int flags, mode_t mode)
        mode &= ~current->fs->umask;
 
        if (flags & SPU_CREATE_GANG)
-               return spufs_create_gang(nd->dentry->d_inode,
-                                       dentry, nd->mnt, mode);
+               return spufs_create_gang(nd->path.dentry->d_inode,
+                                        dentry, nd->path.mnt, mode);
        else
-               return spufs_create_context(nd->dentry->d_inode,
-                                       dentry, nd->mnt, flags, mode);
+               return spufs_create_context(nd->path.dentry->d_inode,
+                                           dentry, nd->path.mnt, flags, mode,
+                                           filp);
 
 out_dput:
        dput(dentry);
 out_dir:
-       mutex_unlock(&nd->dentry->d_inode->i_mutex);
+       mutex_unlock(&nd->path.dentry->d_inode->i_mutex);
 out:
        return ret;
 }
@@ -567,7 +684,8 @@ spufs_parse_options(char *options, struct inode *root)
 
 static void spufs_exit_isolated_loader(void)
 {
-       kfree(isolated_loader);
+       free_pages((unsigned long) isolated_loader,
+                       get_order(isolated_loader_size));
 }
 
 static void
@@ -585,11 +703,12 @@ spufs_init_isolated_loader(void)
        if (!loader)
                return;
 
-       /* kmalloc should align on a 16 byte boundary..* */
-       isolated_loader = kmalloc(size, GFP_KERNEL);
+       /* the loader must be align on a 16 byte boundary */
+       isolated_loader = (char *)__get_free_pages(GFP_KERNEL, get_order(size));
        if (!isolated_loader)
                return;
 
+       isolated_loader_size = size;
        memcpy(isolated_loader, loader, size);
        printk(KERN_INFO "spufs: SPU isolation mode enabled\n");
 }
@@ -609,7 +728,7 @@ spufs_create_root(struct super_block *sb, void *data)
        if (!inode)
                goto out;
 
-       inode->i_op = &spufs_dir_inode_operations;
+       inode->i_op = &simple_dir_inode_operations;
        inode->i_fop = &simple_dir_operations;
        SPUFS_I(inode)->i_ctx = NULL;
 
@@ -638,8 +757,11 @@ spufs_fill_super(struct super_block *sb, void *data, int silent)
                .statfs = simple_statfs,
                .delete_inode = spufs_delete_inode,
                .drop_inode = generic_delete_inode,
+               .show_options = generic_show_options,
        };
 
+       save_mount_options(sb, data);
+
        sb->s_maxbytes = MAX_LFS_FILESIZE;
        sb->s_blocksize = PAGE_CACHE_SIZE;
        sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
@@ -674,7 +796,7 @@ static int __init spufs_init(void)
        ret = -ENOMEM;
        spufs_inode_cache = kmem_cache_create("spufs_inode_cache",
                        sizeof(struct spufs_inode_info), 0,
-                       SLAB_HWCACHE_ALIGN, spufs_init_once, NULL);
+                       SLAB_HWCACHE_ALIGN, spufs_init_once);
 
        if (!spufs_inode_cache)
                goto out;
@@ -687,16 +809,11 @@ static int __init spufs_init(void)
        ret = register_spu_syscalls(&spufs_calls);
        if (ret)
                goto out_fs;
-       ret = register_arch_coredump_calls(&spufs_coredump_calls);
-       if (ret)
-               goto out_syscalls;
 
        spufs_init_isolated_loader();
 
        return 0;
 
-out_syscalls:
-       unregister_spu_syscalls(&spufs_calls);
 out_fs:
        unregister_filesystem(&spufs_type);
 out_sched:
@@ -712,7 +829,6 @@ static void __exit spufs_exit(void)
 {
        spu_sched_exit();
        spufs_exit_isolated_loader();
-       unregister_arch_coredump_calls(&spufs_coredump_calls);
        unregister_spu_syscalls(&spufs_calls);
        unregister_filesystem(&spufs_type);
        kmem_cache_destroy(spufs_inode_cache);