xref: /linux/arch/powerpc/platforms/cell/spufs/inode.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
1de6cc651SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
24ac91378SJan Blunck 
367207b96SArnd Bergmann /*
467207b96SArnd Bergmann  * SPU file system
567207b96SArnd Bergmann  *
667207b96SArnd Bergmann  * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
767207b96SArnd Bergmann  *
867207b96SArnd Bergmann  * Author: Arnd Bergmann <arndb@de.ibm.com>
967207b96SArnd Bergmann  */
1067207b96SArnd Bergmann 
1167207b96SArnd Bergmann #include <linux/file.h>
1267207b96SArnd Bergmann #include <linux/fs.h>
13d2e0981cSDavid Howells #include <linux/fs_context.h>
14d2e0981cSDavid Howells #include <linux/fs_parser.h>
15826be063SChristoph Hellwig #include <linux/fsnotify.h>
1667207b96SArnd Bergmann #include <linux/backing-dev.h>
1767207b96SArnd Bergmann #include <linux/init.h>
1867207b96SArnd Bergmann #include <linux/ioctl.h>
1967207b96SArnd Bergmann #include <linux/module.h>
20346f4d3cSArnd Bergmann #include <linux/mount.h>
2167207b96SArnd Bergmann #include <linux/namei.h>
2267207b96SArnd Bergmann #include <linux/pagemap.h>
2367207b96SArnd Bergmann #include <linux/poll.h>
24e6f6390aSChristophe Leroy #include <linux/of.h>
25e41d12f5SChristoph Hellwig #include <linux/seq_file.h>
2667207b96SArnd Bergmann #include <linux/slab.h>
2767207b96SArnd Bergmann 
2867207b96SArnd Bergmann #include <asm/spu.h>
29ccf17e9dSJeremy Kerr #include <asm/spu_priv1.h>
307c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
3167207b96SArnd Bergmann 
3267207b96SArnd Bergmann #include "spufs.h"
3367207b96SArnd Bergmann 
342c3e4787SJeremy Kerr struct spufs_sb_info {
35d2e0981cSDavid Howells 	bool debug;
362c3e4787SJeremy Kerr };
372c3e4787SJeremy Kerr 
38e18b890bSChristoph Lameter static struct kmem_cache *spufs_inode_cache;
39c6730ed4SJeremy Kerr char *isolated_loader;
408b0d3121SSebastian Siewior static int isolated_loader_size;
4167207b96SArnd Bergmann 
spufs_get_sb_info(struct super_block * sb)422c3e4787SJeremy Kerr static struct spufs_sb_info *spufs_get_sb_info(struct super_block *sb)
432c3e4787SJeremy Kerr {
442c3e4787SJeremy Kerr 	return sb->s_fs_info;
452c3e4787SJeremy Kerr }
462c3e4787SJeremy Kerr 
4767207b96SArnd Bergmann static struct inode *
spufs_alloc_inode(struct super_block * sb)4867207b96SArnd Bergmann spufs_alloc_inode(struct super_block *sb)
4967207b96SArnd Bergmann {
5067207b96SArnd Bergmann 	struct spufs_inode_info *ei;
5167207b96SArnd Bergmann 
52e94b1766SChristoph Lameter 	ei = kmem_cache_alloc(spufs_inode_cache, GFP_KERNEL);
5367207b96SArnd Bergmann 	if (!ei)
5467207b96SArnd Bergmann 		return NULL;
556263203eSArnd Bergmann 
566263203eSArnd Bergmann 	ei->i_gang = NULL;
576263203eSArnd Bergmann 	ei->i_ctx = NULL;
5843c2bbd9SChristoph Hellwig 	ei->i_openers = 0;
596263203eSArnd Bergmann 
6067207b96SArnd Bergmann 	return &ei->vfs_inode;
6167207b96SArnd Bergmann }
6267207b96SArnd Bergmann 
spufs_free_inode(struct inode * inode)636d0e0d0bSAl Viro static void spufs_free_inode(struct inode *inode)
6467207b96SArnd Bergmann {
6567207b96SArnd Bergmann 	kmem_cache_free(spufs_inode_cache, SPUFS_I(inode));
6667207b96SArnd Bergmann }
6767207b96SArnd Bergmann 
6867207b96SArnd Bergmann static void
spufs_init_once(void * p)6951cc5068SAlexey Dobriyan spufs_init_once(void *p)
7067207b96SArnd Bergmann {
7167207b96SArnd Bergmann 	struct spufs_inode_info *ei = p;
7267207b96SArnd Bergmann 
7367207b96SArnd Bergmann 	inode_init_once(&ei->vfs_inode);
7467207b96SArnd Bergmann }
7567207b96SArnd Bergmann 
7667207b96SArnd Bergmann static struct inode *
spufs_new_inode(struct super_block * sb,umode_t mode)77c6684b26SAl Viro spufs_new_inode(struct super_block *sb, umode_t mode)
7867207b96SArnd Bergmann {
7967207b96SArnd Bergmann 	struct inode *inode;
8067207b96SArnd Bergmann 
8167207b96SArnd Bergmann 	inode = new_inode(sb);
8267207b96SArnd Bergmann 	if (!inode)
8367207b96SArnd Bergmann 		goto out;
8467207b96SArnd Bergmann 
856747e832SMichael Ellerman 	inode->i_ino = get_next_ino();
8667207b96SArnd Bergmann 	inode->i_mode = mode;
871330deb0SDavid Howells 	inode->i_uid = current_fsuid();
881330deb0SDavid Howells 	inode->i_gid = current_fsgid();
894c46a0a1SJeff Layton 	simple_inode_init_ts(inode);
9067207b96SArnd Bergmann out:
9167207b96SArnd Bergmann 	return inode;
9267207b96SArnd Bergmann }
9367207b96SArnd Bergmann 
9467207b96SArnd Bergmann static int
spufs_setattr(struct mnt_idmap * idmap,struct dentry * dentry,struct iattr * attr)95c1632a0fSChristian Brauner spufs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
96549c7297SChristian Brauner 	      struct iattr *attr)
9767207b96SArnd Bergmann {
9875c3cfa8SDavid Howells 	struct inode *inode = d_inode(dentry);
9967207b96SArnd Bergmann 
10067207b96SArnd Bergmann 	if ((attr->ia_valid & ATTR_SIZE) &&
10167207b96SArnd Bergmann 	    (attr->ia_size != inode->i_size))
10267207b96SArnd Bergmann 		return -EINVAL;
103c1632a0fSChristian Brauner 	setattr_copy(&nop_mnt_idmap, inode, attr);
1041025774cSChristoph Hellwig 	mark_inode_dirty(inode);
1051025774cSChristoph Hellwig 	return 0;
10667207b96SArnd Bergmann }
10767207b96SArnd Bergmann 
10867207b96SArnd Bergmann 
10967207b96SArnd Bergmann static int
spufs_new_file(struct super_block * sb,struct dentry * dentry,const struct file_operations * fops,umode_t mode,size_t size,struct spu_context * ctx)11067207b96SArnd Bergmann spufs_new_file(struct super_block *sb, struct dentry *dentry,
111c6684b26SAl Viro 		const struct file_operations *fops, umode_t mode,
11223d893f5SJeremy Kerr 		size_t size, struct spu_context *ctx)
11367207b96SArnd Bergmann {
1146e1d5dccSAlexey Dobriyan 	static const struct inode_operations spufs_file_iops = {
11567207b96SArnd Bergmann 		.setattr = spufs_setattr,
11667207b96SArnd Bergmann 	};
11767207b96SArnd Bergmann 	struct inode *inode;
11867207b96SArnd Bergmann 	int ret;
11967207b96SArnd Bergmann 
12067207b96SArnd Bergmann 	ret = -ENOSPC;
12167207b96SArnd Bergmann 	inode = spufs_new_inode(sb, S_IFREG | mode);
12267207b96SArnd Bergmann 	if (!inode)
12367207b96SArnd Bergmann 		goto out;
12467207b96SArnd Bergmann 
12567207b96SArnd Bergmann 	ret = 0;
12667207b96SArnd Bergmann 	inode->i_op = &spufs_file_iops;
12767207b96SArnd Bergmann 	inode->i_fop = fops;
12823d893f5SJeremy Kerr 	inode->i_size = size;
1298e18e294STheodore Ts'o 	inode->i_private = SPUFS_I(inode)->i_ctx = get_spu_context(ctx);
13067207b96SArnd Bergmann 	d_add(dentry, inode);
13167207b96SArnd Bergmann out:
13267207b96SArnd Bergmann 	return ret;
13367207b96SArnd Bergmann }
13467207b96SArnd Bergmann 
13567207b96SArnd Bergmann static void
spufs_evict_inode(struct inode * inode)1360f3f63a4SAl Viro spufs_evict_inode(struct inode *inode)
13767207b96SArnd Bergmann {
1386263203eSArnd Bergmann 	struct spufs_inode_info *ei = SPUFS_I(inode);
139dbd5768fSJan Kara 	clear_inode(inode);
1406263203eSArnd Bergmann 	if (ei->i_ctx)
1416263203eSArnd Bergmann 		put_spu_context(ei->i_ctx);
1426263203eSArnd Bergmann 	if (ei->i_gang)
1436263203eSArnd Bergmann 		put_spu_gang(ei->i_gang);
14467207b96SArnd Bergmann }
14567207b96SArnd Bergmann 
spufs_prune_dir(struct dentry * dir)1463f51dd91SArnd Bergmann static void spufs_prune_dir(struct dentry *dir)
1473f51dd91SArnd Bergmann {
148*da549bddSAl Viro 	struct dentry *dentry;
149*da549bddSAl Viro 	struct hlist_node *n;
1506263203eSArnd Bergmann 
1515955102cSAl Viro 	inode_lock(d_inode(dir));
152*da549bddSAl Viro 	hlist_for_each_entry_safe(dentry, n, &dir->d_children, d_sib) {
1533f51dd91SArnd Bergmann 		spin_lock(&dentry->d_lock);
154dc3f4198SAl Viro 		if (simple_positive(dentry)) {
155dc0474beSNick Piggin 			dget_dlock(dentry);
1563f51dd91SArnd Bergmann 			__d_drop(dentry);
1573f51dd91SArnd Bergmann 			spin_unlock(&dentry->d_lock);
15875c3cfa8SDavid Howells 			simple_unlink(d_inode(dir), dentry);
159b5c84bf6SNick Piggin 			/* XXX: what was dcache_lock protecting here? Other
160da502956SNick Piggin 			 * filesystems (IB, configfs) release dcache_lock
161da502956SNick Piggin 			 * before unlink */
1623f51dd91SArnd Bergmann 			dput(dentry);
1633f51dd91SArnd Bergmann 		} else {
1643f51dd91SArnd Bergmann 			spin_unlock(&dentry->d_lock);
1653f51dd91SArnd Bergmann 		}
1663f51dd91SArnd Bergmann 	}
1673f51dd91SArnd Bergmann 	shrink_dcache_parent(dir);
1685955102cSAl Viro 	inode_unlock(d_inode(dir));
1693f51dd91SArnd Bergmann }
1703f51dd91SArnd Bergmann 
1716263203eSArnd Bergmann /* Caller must hold parent->i_mutex */
spufs_rmdir(struct inode * parent,struct dentry * dir)1726263203eSArnd Bergmann static int spufs_rmdir(struct inode *parent, struct dentry *dir)
1733f51dd91SArnd Bergmann {
1743f51dd91SArnd Bergmann 	/* remove all entries */
17567cba9fdSAl Viro 	int res;
1766263203eSArnd Bergmann 	spufs_prune_dir(dir);
177c443acabSJeremy Kerr 	d_drop(dir);
17867cba9fdSAl Viro 	res = simple_rmdir(parent, dir);
17967cba9fdSAl Viro 	/* We have to give up the mm_struct */
18075c3cfa8SDavid Howells 	spu_forget(SPUFS_I(d_inode(dir))->i_ctx);
18167cba9fdSAl Viro 	return res;
1823f51dd91SArnd Bergmann }
1833f51dd91SArnd Bergmann 
spufs_fill_dir(struct dentry * dir,const struct spufs_tree_descr * files,umode_t mode,struct spu_context * ctx)18474254647SJeremy Kerr static int spufs_fill_dir(struct dentry *dir,
185c6684b26SAl Viro 		const struct spufs_tree_descr *files, umode_t mode,
18674254647SJeremy Kerr 		struct spu_context *ctx)
18767207b96SArnd Bergmann {
18867207b96SArnd Bergmann 	while (files->name && files->name[0]) {
1892248b87eSAl Viro 		int ret;
1902248b87eSAl Viro 		struct dentry *dentry = d_alloc_name(dir, files->name);
19167207b96SArnd Bergmann 		if (!dentry)
1922248b87eSAl Viro 			return -ENOMEM;
19367207b96SArnd Bergmann 		ret = spufs_new_file(dir->d_sb, dentry, files->ops,
19423d893f5SJeremy Kerr 					files->mode & mode, files->size, ctx);
19567207b96SArnd Bergmann 		if (ret)
1962248b87eSAl Viro 			return ret;
19767207b96SArnd Bergmann 		files++;
19867207b96SArnd Bergmann 	}
19967207b96SArnd Bergmann 	return 0;
20067207b96SArnd Bergmann }
20167207b96SArnd Bergmann 
spufs_dir_close(struct inode * inode,struct file * file)20267207b96SArnd Bergmann static int spufs_dir_close(struct inode *inode, struct file *file)
20367207b96SArnd Bergmann {
2046263203eSArnd Bergmann 	struct inode *parent;
2056263203eSArnd Bergmann 	struct dentry *dir;
20667207b96SArnd Bergmann 	int ret;
20767207b96SArnd Bergmann 
208b4d1ab58SJosef Sipek 	dir = file->f_path.dentry;
20975c3cfa8SDavid Howells 	parent = d_inode(dir->d_parent);
210c8ca0633SArnd Bergmann 
2115955102cSAl Viro 	inode_lock_nested(parent, I_MUTEX_PARENT);
2126263203eSArnd Bergmann 	ret = spufs_rmdir(parent, dir);
2135955102cSAl Viro 	inode_unlock(parent);
21467207b96SArnd Bergmann 	WARN_ON(ret);
215c8ca0633SArnd Bergmann 
21667207b96SArnd Bergmann 	return dcache_dir_close(inode, file);
21767207b96SArnd Bergmann }
21867207b96SArnd Bergmann 
2195dfe4c96SArjan van de Ven const struct file_operations spufs_context_fops = {
22067207b96SArnd Bergmann 	.open		= dcache_dir_open,
22167207b96SArnd Bergmann 	.release	= spufs_dir_close,
22267207b96SArnd Bergmann 	.llseek		= dcache_dir_lseek,
22367207b96SArnd Bergmann 	.read		= generic_read_dir,
2244e82901cSAl Viro 	.iterate_shared	= dcache_readdir,
2251b061d92SChristoph Hellwig 	.fsync		= noop_fsync,
22667207b96SArnd Bergmann };
227bf1ab978SDwayne Grant McConnell EXPORT_SYMBOL_GPL(spufs_context_fops);
22867207b96SArnd Bergmann 
22967207b96SArnd Bergmann static int
spufs_mkdir(struct inode * dir,struct dentry * dentry,unsigned int flags,umode_t mode)2309add11daSArnd Bergmann spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
231c6684b26SAl Viro 		umode_t mode)
23267207b96SArnd Bergmann {
23367207b96SArnd Bergmann 	int ret;
23467207b96SArnd Bergmann 	struct inode *inode;
23567207b96SArnd Bergmann 	struct spu_context *ctx;
23667207b96SArnd Bergmann 
23767207b96SArnd Bergmann 	inode = spufs_new_inode(dir->i_sb, mode | S_IFDIR);
23867207b96SArnd Bergmann 	if (!inode)
2392248b87eSAl Viro 		return -ENOSPC;
24067207b96SArnd Bergmann 
241f2d40141SChristian Brauner 	inode_init_owner(&nop_mnt_idmap, inode, dir, mode | S_IFDIR);
2426263203eSArnd Bergmann 	ctx = alloc_spu_context(SPUFS_I(dir)->i_gang); /* XXX gang */
24367207b96SArnd Bergmann 	SPUFS_I(inode)->i_ctx = ctx;
2442248b87eSAl Viro 	if (!ctx) {
2452248b87eSAl Viro 		iput(inode);
2462248b87eSAl Viro 		return -ENOSPC;
2472248b87eSAl Viro 	}
24867207b96SArnd Bergmann 
2499add11daSArnd Bergmann 	ctx->flags = flags;
250b8c295f9SJeremy Kerr 	inode->i_op = &simple_dir_inode_operations;
25167207b96SArnd Bergmann 	inode->i_fop = &simple_dir_operations;
2522248b87eSAl Viro 
2535955102cSAl Viro 	inode_lock(inode);
2542248b87eSAl Viro 
2552248b87eSAl Viro 	dget(dentry);
2562248b87eSAl Viro 	inc_nlink(dir);
2572248b87eSAl Viro 	inc_nlink(inode);
2582248b87eSAl Viro 
2592248b87eSAl Viro 	d_instantiate(dentry, inode);
2602248b87eSAl Viro 
2615737edd1SMark Nutter 	if (flags & SPU_CREATE_NOSCHED)
2625737edd1SMark Nutter 		ret = spufs_fill_dir(dentry, spufs_dir_nosched_contents,
2635737edd1SMark Nutter 					 mode, ctx);
2645737edd1SMark Nutter 	else
26567207b96SArnd Bergmann 		ret = spufs_fill_dir(dentry, spufs_dir_contents, mode, ctx);
2665737edd1SMark Nutter 
2672248b87eSAl Viro 	if (!ret && spufs_get_sb_info(dir->i_sb)->debug)
2682c3e4787SJeremy Kerr 		ret = spufs_fill_dir(dentry, spufs_dir_debug_contents,
2692c3e4787SJeremy Kerr 				mode, ctx);
2702c3e4787SJeremy Kerr 
2712c3e4787SJeremy Kerr 	if (ret)
2722248b87eSAl Viro 		spufs_rmdir(dir, dentry);
2732c3e4787SJeremy Kerr 
2745955102cSAl Viro 	inode_unlock(inode);
27567207b96SArnd Bergmann 
27667207b96SArnd Bergmann 	return ret;
27767207b96SArnd Bergmann }
27867207b96SArnd Bergmann 
spufs_context_open(const struct path * path)27920f45ad5SAl Viro static int spufs_context_open(const struct path *path)
280346f4d3cSArnd Bergmann {
281346f4d3cSArnd Bergmann 	int ret;
282346f4d3cSArnd Bergmann 	struct file *filp;
283346f4d3cSArnd Bergmann 
2846b9cdf39SYann Droneaud 	ret = get_unused_fd_flags(0);
285bf349a44SAl Viro 	if (ret < 0)
286bf349a44SAl Viro 		return ret;
287346f4d3cSArnd Bergmann 
288765927b2SAl Viro 	filp = dentry_open(path, O_RDONLY, current_cred());
289346f4d3cSArnd Bergmann 	if (IS_ERR(filp)) {
290346f4d3cSArnd Bergmann 		put_unused_fd(ret);
291bf349a44SAl Viro 		return PTR_ERR(filp);
292346f4d3cSArnd Bergmann 	}
293346f4d3cSArnd Bergmann 
294346f4d3cSArnd Bergmann 	filp->f_op = &spufs_context_fops;
295346f4d3cSArnd Bergmann 	fd_install(ret, filp);
296346f4d3cSArnd Bergmann 	return ret;
297346f4d3cSArnd Bergmann }
298346f4d3cSArnd Bergmann 
2998e68e2f2SArnd Bergmann static struct spu_context *
spufs_assert_affinity(unsigned int flags,struct spu_gang * gang,struct file * filp)3008e68e2f2SArnd Bergmann spufs_assert_affinity(unsigned int flags, struct spu_gang *gang,
3018e68e2f2SArnd Bergmann 						struct file *filp)
3028e68e2f2SArnd Bergmann {
30358119068SAndre Detsch 	struct spu_context *tmp, *neighbor, *err;
3048e68e2f2SArnd Bergmann 	int count, node;
3058e68e2f2SArnd Bergmann 	int aff_supp;
3068e68e2f2SArnd Bergmann 
3078e68e2f2SArnd Bergmann 	aff_supp = !list_empty(&(list_entry(cbe_spu_info[0].spus.next,
3088e68e2f2SArnd Bergmann 					struct spu, cbe_list))->aff_list);
3098e68e2f2SArnd Bergmann 
3108e68e2f2SArnd Bergmann 	if (!aff_supp)
3118e68e2f2SArnd Bergmann 		return ERR_PTR(-EINVAL);
3128e68e2f2SArnd Bergmann 
3138e68e2f2SArnd Bergmann 	if (flags & SPU_CREATE_GANG)
3148e68e2f2SArnd Bergmann 		return ERR_PTR(-EINVAL);
3158e68e2f2SArnd Bergmann 
3168e68e2f2SArnd Bergmann 	if (flags & SPU_CREATE_AFFINITY_MEM &&
3178e68e2f2SArnd Bergmann 	    gang->aff_ref_ctx &&
3188e68e2f2SArnd Bergmann 	    gang->aff_ref_ctx->flags & SPU_CREATE_AFFINITY_MEM)
3198e68e2f2SArnd Bergmann 		return ERR_PTR(-EEXIST);
3208e68e2f2SArnd Bergmann 
3218e68e2f2SArnd Bergmann 	if (gang->aff_flags & AFF_MERGED)
3228e68e2f2SArnd Bergmann 		return ERR_PTR(-EBUSY);
3238e68e2f2SArnd Bergmann 
3248e68e2f2SArnd Bergmann 	neighbor = NULL;
3258e68e2f2SArnd Bergmann 	if (flags & SPU_CREATE_AFFINITY_SPU) {
3268e68e2f2SArnd Bergmann 		if (!filp || filp->f_op != &spufs_context_fops)
3278e68e2f2SArnd Bergmann 			return ERR_PTR(-EINVAL);
3288e68e2f2SArnd Bergmann 
3298e68e2f2SArnd Bergmann 		neighbor = get_spu_context(
330496ad9aaSAl Viro 				SPUFS_I(file_inode(filp))->i_ctx);
3318e68e2f2SArnd Bergmann 
3328e68e2f2SArnd Bergmann 		if (!list_empty(&neighbor->aff_list) && !(neighbor->aff_head) &&
3338e68e2f2SArnd Bergmann 		    !list_is_last(&neighbor->aff_list, &gang->aff_list_head) &&
3348e68e2f2SArnd Bergmann 		    !list_entry(neighbor->aff_list.next, struct spu_context,
33558119068SAndre Detsch 		    aff_list)->aff_head) {
33658119068SAndre Detsch 			err = ERR_PTR(-EEXIST);
33758119068SAndre Detsch 			goto out_put_neighbor;
33858119068SAndre Detsch 		}
3398e68e2f2SArnd Bergmann 
34058119068SAndre Detsch 		if (gang != neighbor->gang) {
34158119068SAndre Detsch 			err = ERR_PTR(-EINVAL);
34258119068SAndre Detsch 			goto out_put_neighbor;
34358119068SAndre Detsch 		}
3448e68e2f2SArnd Bergmann 
3458e68e2f2SArnd Bergmann 		count = 1;
3468e68e2f2SArnd Bergmann 		list_for_each_entry(tmp, &gang->aff_list_head, aff_list)
3478e68e2f2SArnd Bergmann 			count++;
3488e68e2f2SArnd Bergmann 		if (list_empty(&neighbor->aff_list))
3498e68e2f2SArnd Bergmann 			count++;
3508e68e2f2SArnd Bergmann 
3518e68e2f2SArnd Bergmann 		for (node = 0; node < MAX_NUMNODES; node++) {
3528e68e2f2SArnd Bergmann 			if ((cbe_spu_info[node].n_spus - atomic_read(
3538e68e2f2SArnd Bergmann 				&cbe_spu_info[node].reserved_spus)) >= count)
3548e68e2f2SArnd Bergmann 				break;
3558e68e2f2SArnd Bergmann 		}
3568e68e2f2SArnd Bergmann 
35758119068SAndre Detsch 		if (node == MAX_NUMNODES) {
35858119068SAndre Detsch 			err = ERR_PTR(-EEXIST);
35958119068SAndre Detsch 			goto out_put_neighbor;
36058119068SAndre Detsch 		}
3618e68e2f2SArnd Bergmann 	}
3628e68e2f2SArnd Bergmann 
3638e68e2f2SArnd Bergmann 	return neighbor;
36458119068SAndre Detsch 
36558119068SAndre Detsch out_put_neighbor:
36658119068SAndre Detsch 	put_spu_context(neighbor);
36758119068SAndre Detsch 	return err;
3688e68e2f2SArnd Bergmann }
3698e68e2f2SArnd Bergmann 
3708e68e2f2SArnd Bergmann static void
spufs_set_affinity(unsigned int flags,struct spu_context * ctx,struct spu_context * neighbor)3718e68e2f2SArnd Bergmann spufs_set_affinity(unsigned int flags, struct spu_context *ctx,
3728e68e2f2SArnd Bergmann 					struct spu_context *neighbor)
3738e68e2f2SArnd Bergmann {
3748e68e2f2SArnd Bergmann 	if (flags & SPU_CREATE_AFFINITY_MEM)
3758e68e2f2SArnd Bergmann 		ctx->gang->aff_ref_ctx = ctx;
3768e68e2f2SArnd Bergmann 
3778e68e2f2SArnd Bergmann 	if (flags & SPU_CREATE_AFFINITY_SPU) {
3788e68e2f2SArnd Bergmann 		if (list_empty(&neighbor->aff_list)) {
3798e68e2f2SArnd Bergmann 			list_add_tail(&neighbor->aff_list,
3808e68e2f2SArnd Bergmann 				&ctx->gang->aff_list_head);
3818e68e2f2SArnd Bergmann 			neighbor->aff_head = 1;
3828e68e2f2SArnd Bergmann 		}
3838e68e2f2SArnd Bergmann 
3848e68e2f2SArnd Bergmann 		if (list_is_last(&neighbor->aff_list, &ctx->gang->aff_list_head)
3858e68e2f2SArnd Bergmann 		    || list_entry(neighbor->aff_list.next, struct spu_context,
3868e68e2f2SArnd Bergmann 							aff_list)->aff_head) {
3878e68e2f2SArnd Bergmann 			list_add(&ctx->aff_list, &neighbor->aff_list);
3888e68e2f2SArnd Bergmann 		} else  {
3898e68e2f2SArnd Bergmann 			list_add_tail(&ctx->aff_list, &neighbor->aff_list);
3908e68e2f2SArnd Bergmann 			if (neighbor->aff_head) {
3918e68e2f2SArnd Bergmann 				neighbor->aff_head = 0;
3928e68e2f2SArnd Bergmann 				ctx->aff_head = 1;
3938e68e2f2SArnd Bergmann 			}
3948e68e2f2SArnd Bergmann 		}
3958e68e2f2SArnd Bergmann 
3968e68e2f2SArnd Bergmann 		if (!ctx->gang->aff_ref_ctx)
3978e68e2f2SArnd Bergmann 			ctx->gang->aff_ref_ctx = ctx;
3988e68e2f2SArnd Bergmann 	}
3998e68e2f2SArnd Bergmann }
4008e68e2f2SArnd Bergmann 
4018e68e2f2SArnd Bergmann static int
spufs_create_context(struct inode * inode,struct dentry * dentry,struct vfsmount * mnt,int flags,umode_t mode,struct file * aff_filp)4028e68e2f2SArnd Bergmann spufs_create_context(struct inode *inode, struct dentry *dentry,
403c6684b26SAl Viro 			struct vfsmount *mnt, int flags, umode_t mode,
4048e68e2f2SArnd Bergmann 			struct file *aff_filp)
4056263203eSArnd Bergmann {
4066263203eSArnd Bergmann 	int ret;
4078e68e2f2SArnd Bergmann 	int affinity;
4088e68e2f2SArnd Bergmann 	struct spu_gang *gang;
4098e68e2f2SArnd Bergmann 	struct spu_context *neighbor;
410765927b2SAl Viro 	struct path path = {.mnt = mnt, .dentry = dentry};
4116263203eSArnd Bergmann 
4125737edd1SMark Nutter 	if ((flags & SPU_CREATE_NOSCHED) &&
4135737edd1SMark Nutter 	    !capable(CAP_SYS_NICE))
4141ba44cc9SAl Viro 		return -EPERM;
4155737edd1SMark Nutter 
4165737edd1SMark Nutter 	if ((flags & (SPU_CREATE_NOSCHED | SPU_CREATE_ISOLATE))
4175737edd1SMark Nutter 	    == SPU_CREATE_ISOLATE)
4181ba44cc9SAl Viro 		return -EINVAL;
4195737edd1SMark Nutter 
420bd2e5f82SJeremy Kerr 	if ((flags & SPU_CREATE_ISOLATE) && !isolated_loader)
4211ba44cc9SAl Viro 		return -ENODEV;
422bd2e5f82SJeremy Kerr 
4238e68e2f2SArnd Bergmann 	gang = NULL;
4248e68e2f2SArnd Bergmann 	neighbor = NULL;
4258e68e2f2SArnd Bergmann 	affinity = flags & (SPU_CREATE_AFFINITY_MEM | SPU_CREATE_AFFINITY_SPU);
4268e68e2f2SArnd Bergmann 	if (affinity) {
4278e68e2f2SArnd Bergmann 		gang = SPUFS_I(inode)->i_gang;
4288e68e2f2SArnd Bergmann 		if (!gang)
4291ba44cc9SAl Viro 			return -EINVAL;
4308e68e2f2SArnd Bergmann 		mutex_lock(&gang->aff_mutex);
4318e68e2f2SArnd Bergmann 		neighbor = spufs_assert_affinity(flags, gang, aff_filp);
4328e68e2f2SArnd Bergmann 		if (IS_ERR(neighbor)) {
4338e68e2f2SArnd Bergmann 			ret = PTR_ERR(neighbor);
4348e68e2f2SArnd Bergmann 			goto out_aff_unlock;
4358e68e2f2SArnd Bergmann 		}
4368e68e2f2SArnd Bergmann 	}
4378e68e2f2SArnd Bergmann 
43857ad583fSRussell Currey 	ret = spufs_mkdir(inode, dentry, flags, mode & 0777);
4396263203eSArnd Bergmann 	if (ret)
4408e68e2f2SArnd Bergmann 		goto out_aff_unlock;
4418e68e2f2SArnd Bergmann 
44258119068SAndre Detsch 	if (affinity) {
44375c3cfa8SDavid Howells 		spufs_set_affinity(flags, SPUFS_I(d_inode(dentry))->i_ctx,
4448e68e2f2SArnd Bergmann 								neighbor);
44558119068SAndre Detsch 		if (neighbor)
44658119068SAndre Detsch 			put_spu_context(neighbor);
44758119068SAndre Detsch 	}
4486263203eSArnd Bergmann 
449765927b2SAl Viro 	ret = spufs_context_open(&path);
45066ec7b2cSAl Viro 	if (ret < 0)
4516263203eSArnd Bergmann 		WARN_ON(spufs_rmdir(inode, dentry));
4526263203eSArnd Bergmann 
4538e68e2f2SArnd Bergmann out_aff_unlock:
4548e68e2f2SArnd Bergmann 	if (affinity)
4558e68e2f2SArnd Bergmann 		mutex_unlock(&gang->aff_mutex);
4566263203eSArnd Bergmann 	return ret;
4576263203eSArnd Bergmann }
4586263203eSArnd Bergmann 
4596263203eSArnd Bergmann static int
spufs_mkgang(struct inode * dir,struct dentry * dentry,umode_t mode)460c6684b26SAl Viro spufs_mkgang(struct inode *dir, struct dentry *dentry, umode_t mode)
4616263203eSArnd Bergmann {
4626263203eSArnd Bergmann 	int ret;
4636263203eSArnd Bergmann 	struct inode *inode;
4646263203eSArnd Bergmann 	struct spu_gang *gang;
4656263203eSArnd Bergmann 
4666263203eSArnd Bergmann 	ret = -ENOSPC;
4676263203eSArnd Bergmann 	inode = spufs_new_inode(dir->i_sb, mode | S_IFDIR);
4686263203eSArnd Bergmann 	if (!inode)
4696263203eSArnd Bergmann 		goto out;
4706263203eSArnd Bergmann 
4716263203eSArnd Bergmann 	ret = 0;
472f2d40141SChristian Brauner 	inode_init_owner(&nop_mnt_idmap, inode, dir, mode | S_IFDIR);
4736263203eSArnd Bergmann 	gang = alloc_spu_gang();
4746263203eSArnd Bergmann 	SPUFS_I(inode)->i_ctx = NULL;
4756263203eSArnd Bergmann 	SPUFS_I(inode)->i_gang = gang;
47654a94fcfSDan Carpenter 	if (!gang) {
47754a94fcfSDan Carpenter 		ret = -ENOMEM;
4786263203eSArnd Bergmann 		goto out_iput;
47954a94fcfSDan Carpenter 	}
4806263203eSArnd Bergmann 
481b8c295f9SJeremy Kerr 	inode->i_op = &simple_dir_inode_operations;
4826263203eSArnd Bergmann 	inode->i_fop = &simple_dir_operations;
4836263203eSArnd Bergmann 
4846263203eSArnd Bergmann 	d_instantiate(dentry, inode);
485ba0b996dSJeremy Kerr 	inc_nlink(dir);
48675c3cfa8SDavid Howells 	inc_nlink(d_inode(dentry));
4876263203eSArnd Bergmann 	return ret;
4886263203eSArnd Bergmann 
4896263203eSArnd Bergmann out_iput:
4906263203eSArnd Bergmann 	iput(inode);
4916263203eSArnd Bergmann out:
4926263203eSArnd Bergmann 	return ret;
4936263203eSArnd Bergmann }
4946263203eSArnd Bergmann 
spufs_gang_open(const struct path * path)49520f45ad5SAl Viro static int spufs_gang_open(const struct path *path)
4966263203eSArnd Bergmann {
4976263203eSArnd Bergmann 	int ret;
4986263203eSArnd Bergmann 	struct file *filp;
4996263203eSArnd Bergmann 
5006b9cdf39SYann Droneaud 	ret = get_unused_fd_flags(0);
501bf349a44SAl Viro 	if (ret < 0)
502bf349a44SAl Viro 		return ret;
5036263203eSArnd Bergmann 
504bf349a44SAl Viro 	/*
505bf349a44SAl Viro 	 * get references for dget and mntget, will be released
506bf349a44SAl Viro 	 * in error path of *_open().
507bf349a44SAl Viro 	 */
508765927b2SAl Viro 	filp = dentry_open(path, O_RDONLY, current_cred());
5096263203eSArnd Bergmann 	if (IS_ERR(filp)) {
5106263203eSArnd Bergmann 		put_unused_fd(ret);
511bf349a44SAl Viro 		return PTR_ERR(filp);
5126263203eSArnd Bergmann 	}
5136263203eSArnd Bergmann 
514877907d3SJeremy Kerr 	filp->f_op = &simple_dir_operations;
5156263203eSArnd Bergmann 	fd_install(ret, filp);
5166263203eSArnd Bergmann 	return ret;
5176263203eSArnd Bergmann }
5186263203eSArnd Bergmann 
spufs_create_gang(struct inode * inode,struct dentry * dentry,struct vfsmount * mnt,umode_t mode)5196263203eSArnd Bergmann static int spufs_create_gang(struct inode *inode,
5206263203eSArnd Bergmann 			struct dentry *dentry,
521c6684b26SAl Viro 			struct vfsmount *mnt, umode_t mode)
5226263203eSArnd Bergmann {
523765927b2SAl Viro 	struct path path = {.mnt = mnt, .dentry = dentry};
5246263203eSArnd Bergmann 	int ret;
5256263203eSArnd Bergmann 
52657ad583fSRussell Currey 	ret = spufs_mkgang(inode, dentry, mode & 0777);
5271ba44cc9SAl Viro 	if (!ret) {
528765927b2SAl Viro 		ret = spufs_gang_open(&path);
529877907d3SJeremy Kerr 		if (ret < 0) {
530877907d3SJeremy Kerr 			int err = simple_rmdir(inode, dentry);
531877907d3SJeremy Kerr 			WARN_ON(err);
532877907d3SJeremy Kerr 		}
5331ba44cc9SAl Viro 	}
5346263203eSArnd Bergmann 	return ret;
5356263203eSArnd Bergmann }
5366263203eSArnd Bergmann 
5376263203eSArnd Bergmann 
538346f4d3cSArnd Bergmann static struct file_system_type spufs_type;
539346f4d3cSArnd Bergmann 
spufs_create(const struct path * path,struct dentry * dentry,unsigned int flags,umode_t mode,struct file * filp)54020f45ad5SAl Viro long spufs_create(const struct path *path, struct dentry *dentry,
541c6684b26SAl Viro 		unsigned int flags, umode_t mode, struct file *filp)
54267207b96SArnd Bergmann {
54375c3cfa8SDavid Howells 	struct inode *dir = d_inode(path->dentry);
54467207b96SArnd Bergmann 	int ret;
54567207b96SArnd Bergmann 
5466263203eSArnd Bergmann 	/* check if we are on spufs */
5471ba10681SAl Viro 	if (path->dentry->d_sb->s_type != &spufs_type)
54825b2692aSAl Viro 		return -EINVAL;
54967207b96SArnd Bergmann 
5506263203eSArnd Bergmann 	/* don't accept undefined flags */
5519add11daSArnd Bergmann 	if (flags & (~SPU_CREATE_FLAG_ALL))
55225b2692aSAl Viro 		return -EINVAL;
553c9832948Sarnd@arndb.de 
5546263203eSArnd Bergmann 	/* only threads can be underneath a gang */
55525b2692aSAl Viro 	if (path->dentry != path->dentry->d_sb->s_root)
55625b2692aSAl Viro 		if ((flags & SPU_CREATE_GANG) || !SPUFS_I(dir)->i_gang)
55725b2692aSAl Viro 			return -EINVAL;
5586263203eSArnd Bergmann 
559ce3b0f8dSAl Viro 	mode &= ~current_umask();
56067207b96SArnd Bergmann 
5616263203eSArnd Bergmann 	if (flags & SPU_CREATE_GANG)
56225b2692aSAl Viro 		ret = spufs_create_gang(dir, dentry, path->mnt, mode);
5636263203eSArnd Bergmann 	else
56425b2692aSAl Viro 		ret = spufs_create_context(dir, dentry, path->mnt, flags, mode,
5654ac91378SJan Blunck 					    filp);
566826be063SChristoph Hellwig 	if (ret >= 0)
56725b2692aSAl Viro 		fsnotify_mkdir(dir, dentry);
56867207b96SArnd Bergmann 
56967207b96SArnd Bergmann 	return ret;
57067207b96SArnd Bergmann }
57167207b96SArnd Bergmann 
57267207b96SArnd Bergmann /* File system initialization */
573d2e0981cSDavid Howells struct spufs_fs_context {
574d2e0981cSDavid Howells 	kuid_t	uid;
575d2e0981cSDavid Howells 	kgid_t	gid;
576d2e0981cSDavid Howells 	umode_t	mode;
57767207b96SArnd Bergmann };
57867207b96SArnd Bergmann 
579d2e0981cSDavid Howells enum {
580d2e0981cSDavid Howells 	Opt_uid, Opt_gid, Opt_mode, Opt_debug,
581d2e0981cSDavid Howells };
582d2e0981cSDavid Howells 
583d7167b14SAl Viro static const struct fs_parameter_spec spufs_fs_parameters[] = {
584d2e0981cSDavid Howells 	fsparam_u32	("gid",				Opt_gid),
585d2e0981cSDavid Howells 	fsparam_u32oct	("mode",			Opt_mode),
586d2e0981cSDavid Howells 	fsparam_u32	("uid",				Opt_uid),
587d2e0981cSDavid Howells 	fsparam_flag	("debug",			Opt_debug),
588d2e0981cSDavid Howells 	{}
589d2e0981cSDavid Howells };
590d2e0981cSDavid Howells 
spufs_show_options(struct seq_file * m,struct dentry * root)591a66ca414SDavid Howells static int spufs_show_options(struct seq_file *m, struct dentry *root)
592a66ca414SDavid Howells {
593a66ca414SDavid Howells 	struct spufs_sb_info *sbi = spufs_get_sb_info(root->d_sb);
594a66ca414SDavid Howells 	struct inode *inode = root->d_inode;
595a66ca414SDavid Howells 
596a66ca414SDavid Howells 	if (!uid_eq(inode->i_uid, GLOBAL_ROOT_UID))
597a66ca414SDavid Howells 		seq_printf(m, ",uid=%u",
598a66ca414SDavid Howells 			   from_kuid_munged(&init_user_ns, inode->i_uid));
599a66ca414SDavid Howells 	if (!gid_eq(inode->i_gid, GLOBAL_ROOT_GID))
600a66ca414SDavid Howells 		seq_printf(m, ",gid=%u",
601a66ca414SDavid Howells 			   from_kgid_munged(&init_user_ns, inode->i_gid));
602a66ca414SDavid Howells 	if ((inode->i_mode & S_IALLUGO) != 0775)
603a66ca414SDavid Howells 		seq_printf(m, ",mode=%o", inode->i_mode);
604a66ca414SDavid Howells 	if (sbi->debug)
605a66ca414SDavid Howells 		seq_puts(m, ",debug");
606a66ca414SDavid Howells 	return 0;
607a66ca414SDavid Howells }
608a66ca414SDavid Howells 
spufs_parse_param(struct fs_context * fc,struct fs_parameter * param)609d2e0981cSDavid Howells static int spufs_parse_param(struct fs_context *fc, struct fs_parameter *param)
61067207b96SArnd Bergmann {
611d2e0981cSDavid Howells 	struct spufs_fs_context *ctx = fc->fs_private;
612d2e0981cSDavid Howells 	struct spufs_sb_info *sbi = fc->s_fs_info;
613d2e0981cSDavid Howells 	struct fs_parse_result result;
614d2e0981cSDavid Howells 	kuid_t uid;
615d2e0981cSDavid Howells 	kgid_t gid;
616d2e0981cSDavid Howells 	int opt;
61767207b96SArnd Bergmann 
618d7167b14SAl Viro 	opt = fs_parse(fc, spufs_fs_parameters, param, &result);
619d2e0981cSDavid Howells 	if (opt < 0)
620d2e0981cSDavid Howells 		return opt;
62167207b96SArnd Bergmann 
622d2e0981cSDavid Howells 	switch (opt) {
62367207b96SArnd Bergmann 	case Opt_uid:
624d2e0981cSDavid Howells 		uid = make_kuid(current_user_ns(), result.uint_32);
625d2e0981cSDavid Howells 		if (!uid_valid(uid))
626d2e0981cSDavid Howells 			return invalf(fc, "Unknown uid");
627d2e0981cSDavid Howells 		ctx->uid = uid;
62867207b96SArnd Bergmann 		break;
62967207b96SArnd Bergmann 	case Opt_gid:
630d2e0981cSDavid Howells 		gid = make_kgid(current_user_ns(), result.uint_32);
631d2e0981cSDavid Howells 		if (!gid_valid(gid))
632d2e0981cSDavid Howells 			return invalf(fc, "Unknown gid");
633d2e0981cSDavid Howells 		ctx->gid = gid;
63467207b96SArnd Bergmann 		break;
635f11f5ee7SJeremy Kerr 	case Opt_mode:
636d2e0981cSDavid Howells 		ctx->mode = result.uint_32 & S_IALLUGO;
637f11f5ee7SJeremy Kerr 		break;
6382c3e4787SJeremy Kerr 	case Opt_debug:
639d2e0981cSDavid Howells 		sbi->debug = true;
6402c3e4787SJeremy Kerr 		break;
641d2e0981cSDavid Howells 	}
642d2e0981cSDavid Howells 
64367207b96SArnd Bergmann 	return 0;
64467207b96SArnd Bergmann }
64567207b96SArnd Bergmann 
spufs_exit_isolated_loader(void)646db1384b4SAkinobu Mita static void spufs_exit_isolated_loader(void)
647db1384b4SAkinobu Mita {
6488b0d3121SSebastian Siewior 	free_pages((unsigned long) isolated_loader,
6498b0d3121SSebastian Siewior 			get_order(isolated_loader_size));
650db1384b4SAkinobu Mita }
651db1384b4SAkinobu Mita 
6527c1ab16bSNick Child static void __init
spufs_init_isolated_loader(void)6530afacde3Sarnd@arndb.de spufs_init_isolated_loader(void)
6540afacde3Sarnd@arndb.de {
6550afacde3Sarnd@arndb.de 	struct device_node *dn;
6560afacde3Sarnd@arndb.de 	const char *loader;
6570afacde3Sarnd@arndb.de 	int size;
6580afacde3Sarnd@arndb.de 
6590afacde3Sarnd@arndb.de 	dn = of_find_node_by_path("/spu-isolation");
6600afacde3Sarnd@arndb.de 	if (!dn)
6610afacde3Sarnd@arndb.de 		return;
6620afacde3Sarnd@arndb.de 
663e2eb6392SStephen Rothwell 	loader = of_get_property(dn, "loader", &size);
6646ac059daSMiaoqian Lin 	of_node_put(dn);
6650afacde3Sarnd@arndb.de 	if (!loader)
6660afacde3Sarnd@arndb.de 		return;
6670afacde3Sarnd@arndb.de 
6688b0d3121SSebastian Siewior 	/* the loader must be align on a 16 byte boundary */
6698b0d3121SSebastian Siewior 	isolated_loader = (char *)__get_free_pages(GFP_KERNEL, get_order(size));
6700afacde3Sarnd@arndb.de 	if (!isolated_loader)
6710afacde3Sarnd@arndb.de 		return;
6720afacde3Sarnd@arndb.de 
6738b0d3121SSebastian Siewior 	isolated_loader_size = size;
6740afacde3Sarnd@arndb.de 	memcpy(isolated_loader, loader, size);
6750afacde3Sarnd@arndb.de 	printk(KERN_INFO "spufs: SPU isolation mode enabled\n");
6760afacde3Sarnd@arndb.de }
6770afacde3Sarnd@arndb.de 
spufs_create_root(struct super_block * sb,struct fs_context * fc)678d2e0981cSDavid Howells static int spufs_create_root(struct super_block *sb, struct fs_context *fc)
6798b3d6663SArnd Bergmann {
680d2e0981cSDavid Howells 	struct spufs_fs_context *ctx = fc->fs_private;
68167207b96SArnd Bergmann 	struct inode *inode;
68267207b96SArnd Bergmann 
6838f18a158SArnd Bergmann 	if (!spu_management_ops)
684d2e0981cSDavid Howells 		return -ENODEV;
6858f18a158SArnd Bergmann 
686d2e0981cSDavid Howells 	inode = spufs_new_inode(sb, S_IFDIR | ctx->mode);
68767207b96SArnd Bergmann 	if (!inode)
688d2e0981cSDavid Howells 		return -ENOMEM;
68967207b96SArnd Bergmann 
690d2e0981cSDavid Howells 	inode->i_uid = ctx->uid;
691d2e0981cSDavid Howells 	inode->i_gid = ctx->gid;
692b8c295f9SJeremy Kerr 	inode->i_op = &simple_dir_inode_operations;
69367207b96SArnd Bergmann 	inode->i_fop = &simple_dir_operations;
69467207b96SArnd Bergmann 	SPUFS_I(inode)->i_ctx = NULL;
695e2ed6e4dSJeremy Kerr 	inc_nlink(inode);
69667207b96SArnd Bergmann 
69748fde701SAl Viro 	sb->s_root = d_make_root(inode);
69867207b96SArnd Bergmann 	if (!sb->s_root)
699d2e0981cSDavid Howells 		return -ENOMEM;
70067207b96SArnd Bergmann 	return 0;
70167207b96SArnd Bergmann }
70267207b96SArnd Bergmann 
703d2e0981cSDavid Howells static const struct super_operations spufs_ops = {
70467207b96SArnd Bergmann 	.alloc_inode	= spufs_alloc_inode,
7056d0e0d0bSAl Viro 	.free_inode	= spufs_free_inode,
70667207b96SArnd Bergmann 	.statfs		= simple_statfs,
7070f3f63a4SAl Viro 	.evict_inode	= spufs_evict_inode,
708a66ca414SDavid Howells 	.show_options	= spufs_show_options,
70967207b96SArnd Bergmann };
71067207b96SArnd Bergmann 
spufs_fill_super(struct super_block * sb,struct fs_context * fc)711d2e0981cSDavid Howells static int spufs_fill_super(struct super_block *sb, struct fs_context *fc)
712d2e0981cSDavid Howells {
71367207b96SArnd Bergmann 	sb->s_maxbytes = MAX_LFS_FILESIZE;
71409cbfeafSKirill A. Shutemov 	sb->s_blocksize = PAGE_SIZE;
71509cbfeafSKirill A. Shutemov 	sb->s_blocksize_bits = PAGE_SHIFT;
71667207b96SArnd Bergmann 	sb->s_magic = SPUFS_MAGIC;
717d2e0981cSDavid Howells 	sb->s_op = &spufs_ops;
71867207b96SArnd Bergmann 
719d2e0981cSDavid Howells 	return spufs_create_root(sb, fc);
72067207b96SArnd Bergmann }
72167207b96SArnd Bergmann 
spufs_get_tree(struct fs_context * fc)722d2e0981cSDavid Howells static int spufs_get_tree(struct fs_context *fc)
72367207b96SArnd Bergmann {
724d2e0981cSDavid Howells 	return get_tree_single(fc, spufs_fill_super);
725d2e0981cSDavid Howells }
726d2e0981cSDavid Howells 
spufs_free_fc(struct fs_context * fc)727d2e0981cSDavid Howells static void spufs_free_fc(struct fs_context *fc)
728d2e0981cSDavid Howells {
729d2e0981cSDavid Howells 	kfree(fc->s_fs_info);
730d2e0981cSDavid Howells }
731d2e0981cSDavid Howells 
732d2e0981cSDavid Howells static const struct fs_context_operations spufs_context_ops = {
733d2e0981cSDavid Howells 	.free		= spufs_free_fc,
734d2e0981cSDavid Howells 	.parse_param	= spufs_parse_param,
735d2e0981cSDavid Howells 	.get_tree	= spufs_get_tree,
736d2e0981cSDavid Howells };
737d2e0981cSDavid Howells 
spufs_init_fs_context(struct fs_context * fc)738d2e0981cSDavid Howells static int spufs_init_fs_context(struct fs_context *fc)
739d2e0981cSDavid Howells {
740d2e0981cSDavid Howells 	struct spufs_fs_context *ctx;
741d2e0981cSDavid Howells 	struct spufs_sb_info *sbi;
742d2e0981cSDavid Howells 
743d2e0981cSDavid Howells 	ctx = kzalloc(sizeof(struct spufs_fs_context), GFP_KERNEL);
744d2e0981cSDavid Howells 	if (!ctx)
745d2e0981cSDavid Howells 		goto nomem;
746d2e0981cSDavid Howells 
747d2e0981cSDavid Howells 	sbi = kzalloc(sizeof(struct spufs_sb_info), GFP_KERNEL);
748d2e0981cSDavid Howells 	if (!sbi)
749d2e0981cSDavid Howells 		goto nomem_ctx;
750d2e0981cSDavid Howells 
751d2e0981cSDavid Howells 	ctx->uid = current_uid();
752d2e0981cSDavid Howells 	ctx->gid = current_gid();
753d2e0981cSDavid Howells 	ctx->mode = 0755;
754d2e0981cSDavid Howells 
7552272905aSEmmanuel Nicolet 	fc->fs_private = ctx;
756d2e0981cSDavid Howells 	fc->s_fs_info = sbi;
757d2e0981cSDavid Howells 	fc->ops = &spufs_context_ops;
758d2e0981cSDavid Howells 	return 0;
759d2e0981cSDavid Howells 
760d2e0981cSDavid Howells nomem_ctx:
761d2e0981cSDavid Howells 	kfree(ctx);
762d2e0981cSDavid Howells nomem:
763d2e0981cSDavid Howells 	return -ENOMEM;
76467207b96SArnd Bergmann }
76567207b96SArnd Bergmann 
76667207b96SArnd Bergmann static struct file_system_type spufs_type = {
76767207b96SArnd Bergmann 	.owner = THIS_MODULE,
76867207b96SArnd Bergmann 	.name = "spufs",
769d2e0981cSDavid Howells 	.init_fs_context = spufs_init_fs_context,
770d7167b14SAl Viro 	.parameters	= spufs_fs_parameters,
77167207b96SArnd Bergmann 	.kill_sb = kill_litter_super,
77267207b96SArnd Bergmann };
7737f78e035SEric W. Biederman MODULE_ALIAS_FS("spufs");
77467207b96SArnd Bergmann 
spufs_init(void)775e78b47a5SArnd Bergmann static int __init spufs_init(void)
77667207b96SArnd Bergmann {
77767207b96SArnd Bergmann 	int ret;
778bf1ab978SDwayne Grant McConnell 
779ccf17e9dSJeremy Kerr 	ret = -ENODEV;
780ccf17e9dSJeremy Kerr 	if (!spu_management_ops)
781ccf17e9dSJeremy Kerr 		goto out;
782ccf17e9dSJeremy Kerr 
78367207b96SArnd Bergmann 	ret = -ENOMEM;
78467207b96SArnd Bergmann 	spufs_inode_cache = kmem_cache_create("spufs_inode_cache",
78567207b96SArnd Bergmann 			sizeof(struct spufs_inode_info), 0,
7865d097056SVladimir Davydov 			SLAB_HWCACHE_ALIGN|SLAB_ACCOUNT, spufs_init_once);
78767207b96SArnd Bergmann 
78867207b96SArnd Bergmann 	if (!spufs_inode_cache)
78967207b96SArnd Bergmann 		goto out;
790c99c1994SAkinobu Mita 	ret = spu_sched_init();
79167207b96SArnd Bergmann 	if (ret)
79267207b96SArnd Bergmann 		goto out_cache;
79367207b96SArnd Bergmann 	ret = register_spu_syscalls(&spufs_calls);
79467207b96SArnd Bergmann 	if (ret)
795640045a1SAl Viro 		goto out_sched;
796640045a1SAl Viro 	ret = register_filesystem(&spufs_type);
797640045a1SAl Viro 	if (ret)
798640045a1SAl Viro 		goto out_syscalls;
7990afacde3Sarnd@arndb.de 
8000afacde3Sarnd@arndb.de 	spufs_init_isolated_loader();
801bf1ab978SDwayne Grant McConnell 
80267207b96SArnd Bergmann 	return 0;
803c99c1994SAkinobu Mita 
804640045a1SAl Viro out_syscalls:
805640045a1SAl Viro 	unregister_spu_syscalls(&spufs_calls);
806c99c1994SAkinobu Mita out_sched:
807c99c1994SAkinobu Mita 	spu_sched_exit();
80867207b96SArnd Bergmann out_cache:
80967207b96SArnd Bergmann 	kmem_cache_destroy(spufs_inode_cache);
81067207b96SArnd Bergmann out:
81167207b96SArnd Bergmann 	return ret;
81267207b96SArnd Bergmann }
81367207b96SArnd Bergmann module_init(spufs_init);
81467207b96SArnd Bergmann 
spufs_exit(void)815e78b47a5SArnd Bergmann static void __exit spufs_exit(void)
81667207b96SArnd Bergmann {
8178b3d6663SArnd Bergmann 	spu_sched_exit();
818db1384b4SAkinobu Mita 	spufs_exit_isolated_loader();
81967207b96SArnd Bergmann 	unregister_spu_syscalls(&spufs_calls);
82067207b96SArnd Bergmann 	unregister_filesystem(&spufs_type);
82167207b96SArnd Bergmann 	kmem_cache_destroy(spufs_inode_cache);
82267207b96SArnd Bergmann }
82367207b96SArnd Bergmann module_exit(spufs_exit);
82467207b96SArnd Bergmann 
82567207b96SArnd Bergmann MODULE_LICENSE("GPL");
82667207b96SArnd Bergmann MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");
82767207b96SArnd Bergmann 
828