xref: /linux/arch/powerpc/platforms/cell/spufs/inode.c (revision c983294872ebccd4aacf1b8dd694ac2170feadc3)
167207b96SArnd Bergmann /*
267207b96SArnd Bergmann  * SPU file system
367207b96SArnd Bergmann  *
467207b96SArnd Bergmann  * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
567207b96SArnd Bergmann  *
667207b96SArnd Bergmann  * Author: Arnd Bergmann <arndb@de.ibm.com>
767207b96SArnd Bergmann  *
867207b96SArnd Bergmann  * This program is free software; you can redistribute it and/or modify
967207b96SArnd Bergmann  * it under the terms of the GNU General Public License as published by
1067207b96SArnd Bergmann  * the Free Software Foundation; either version 2, or (at your option)
1167207b96SArnd Bergmann  * any later version.
1267207b96SArnd Bergmann  *
1367207b96SArnd Bergmann  * This program is distributed in the hope that it will be useful,
1467207b96SArnd Bergmann  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1567207b96SArnd Bergmann  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1667207b96SArnd Bergmann  * GNU General Public License for more details.
1767207b96SArnd Bergmann  *
1867207b96SArnd Bergmann  * You should have received a copy of the GNU General Public License
1967207b96SArnd Bergmann  * along with this program; if not, write to the Free Software
2067207b96SArnd Bergmann  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
2167207b96SArnd Bergmann  */
2267207b96SArnd Bergmann 
2367207b96SArnd Bergmann #include <linux/file.h>
2467207b96SArnd Bergmann #include <linux/fs.h>
2567207b96SArnd Bergmann #include <linux/backing-dev.h>
2667207b96SArnd Bergmann #include <linux/init.h>
2767207b96SArnd Bergmann #include <linux/ioctl.h>
2867207b96SArnd Bergmann #include <linux/module.h>
29346f4d3cSArnd Bergmann #include <linux/mount.h>
3067207b96SArnd Bergmann #include <linux/namei.h>
3167207b96SArnd Bergmann #include <linux/pagemap.h>
3267207b96SArnd Bergmann #include <linux/poll.h>
3367207b96SArnd Bergmann #include <linux/slab.h>
3467207b96SArnd Bergmann #include <linux/parser.h>
3567207b96SArnd Bergmann 
3667207b96SArnd Bergmann #include <asm/io.h>
3767207b96SArnd Bergmann #include <asm/semaphore.h>
3867207b96SArnd Bergmann #include <asm/spu.h>
3967207b96SArnd Bergmann #include <asm/uaccess.h>
4067207b96SArnd Bergmann 
4167207b96SArnd Bergmann #include "spufs.h"
4267207b96SArnd Bergmann 
4367207b96SArnd Bergmann static kmem_cache_t *spufs_inode_cache;
4467207b96SArnd Bergmann 
4567207b96SArnd Bergmann static struct inode *
4667207b96SArnd Bergmann spufs_alloc_inode(struct super_block *sb)
4767207b96SArnd Bergmann {
4867207b96SArnd Bergmann 	struct spufs_inode_info *ei;
4967207b96SArnd Bergmann 
5067207b96SArnd Bergmann 	ei = kmem_cache_alloc(spufs_inode_cache, SLAB_KERNEL);
5167207b96SArnd Bergmann 	if (!ei)
5267207b96SArnd Bergmann 		return NULL;
5367207b96SArnd Bergmann 	return &ei->vfs_inode;
5467207b96SArnd Bergmann }
5567207b96SArnd Bergmann 
5667207b96SArnd Bergmann static void
5767207b96SArnd Bergmann spufs_destroy_inode(struct inode *inode)
5867207b96SArnd Bergmann {
5967207b96SArnd Bergmann 	kmem_cache_free(spufs_inode_cache, SPUFS_I(inode));
6067207b96SArnd Bergmann }
6167207b96SArnd Bergmann 
6267207b96SArnd Bergmann static void
6367207b96SArnd Bergmann spufs_init_once(void *p, kmem_cache_t * cachep, unsigned long flags)
6467207b96SArnd Bergmann {
6567207b96SArnd Bergmann 	struct spufs_inode_info *ei = p;
6667207b96SArnd Bergmann 
6767207b96SArnd Bergmann 	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
6867207b96SArnd Bergmann 	    SLAB_CTOR_CONSTRUCTOR) {
6967207b96SArnd Bergmann 		inode_init_once(&ei->vfs_inode);
7067207b96SArnd Bergmann 	}
7167207b96SArnd Bergmann }
7267207b96SArnd Bergmann 
7367207b96SArnd Bergmann static struct inode *
7467207b96SArnd Bergmann spufs_new_inode(struct super_block *sb, int mode)
7567207b96SArnd Bergmann {
7667207b96SArnd Bergmann 	struct inode *inode;
7767207b96SArnd Bergmann 
7867207b96SArnd Bergmann 	inode = new_inode(sb);
7967207b96SArnd Bergmann 	if (!inode)
8067207b96SArnd Bergmann 		goto out;
8167207b96SArnd Bergmann 
8267207b96SArnd Bergmann 	inode->i_mode = mode;
8367207b96SArnd Bergmann 	inode->i_uid = current->fsuid;
8467207b96SArnd Bergmann 	inode->i_gid = current->fsgid;
8567207b96SArnd Bergmann 	inode->i_blksize = PAGE_CACHE_SIZE;
8667207b96SArnd Bergmann 	inode->i_blocks = 0;
8767207b96SArnd Bergmann 	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
8867207b96SArnd Bergmann out:
8967207b96SArnd Bergmann 	return inode;
9067207b96SArnd Bergmann }
9167207b96SArnd Bergmann 
9267207b96SArnd Bergmann static int
9367207b96SArnd Bergmann spufs_setattr(struct dentry *dentry, struct iattr *attr)
9467207b96SArnd Bergmann {
9567207b96SArnd Bergmann 	struct inode *inode = dentry->d_inode;
9667207b96SArnd Bergmann 
9767207b96SArnd Bergmann 	if ((attr->ia_valid & ATTR_SIZE) &&
9867207b96SArnd Bergmann 	    (attr->ia_size != inode->i_size))
9967207b96SArnd Bergmann 		return -EINVAL;
10067207b96SArnd Bergmann 	return inode_setattr(inode, attr);
10167207b96SArnd Bergmann }
10267207b96SArnd Bergmann 
10367207b96SArnd Bergmann 
10467207b96SArnd Bergmann static int
10567207b96SArnd Bergmann spufs_new_file(struct super_block *sb, struct dentry *dentry,
10699ac48f5SArjan van de Ven 		const struct file_operations *fops, int mode,
10767207b96SArnd Bergmann 		struct spu_context *ctx)
10867207b96SArnd Bergmann {
10967207b96SArnd Bergmann 	static struct inode_operations spufs_file_iops = {
11067207b96SArnd Bergmann 		.setattr = spufs_setattr,
11167207b96SArnd Bergmann 	};
11267207b96SArnd Bergmann 	struct inode *inode;
11367207b96SArnd Bergmann 	int ret;
11467207b96SArnd Bergmann 
11567207b96SArnd Bergmann 	ret = -ENOSPC;
11667207b96SArnd Bergmann 	inode = spufs_new_inode(sb, S_IFREG | mode);
11767207b96SArnd Bergmann 	if (!inode)
11867207b96SArnd Bergmann 		goto out;
11967207b96SArnd Bergmann 
12067207b96SArnd Bergmann 	ret = 0;
12167207b96SArnd Bergmann 	inode->i_op = &spufs_file_iops;
12267207b96SArnd Bergmann 	inode->i_fop = fops;
12367207b96SArnd Bergmann 	inode->u.generic_ip = SPUFS_I(inode)->i_ctx = get_spu_context(ctx);
12467207b96SArnd Bergmann 	d_add(dentry, inode);
12567207b96SArnd Bergmann out:
12667207b96SArnd Bergmann 	return ret;
12767207b96SArnd Bergmann }
12867207b96SArnd Bergmann 
12967207b96SArnd Bergmann static void
13067207b96SArnd Bergmann spufs_delete_inode(struct inode *inode)
13167207b96SArnd Bergmann {
13267207b96SArnd Bergmann 	if (SPUFS_I(inode)->i_ctx)
13367207b96SArnd Bergmann 		put_spu_context(SPUFS_I(inode)->i_ctx);
13467207b96SArnd Bergmann 	clear_inode(inode);
13567207b96SArnd Bergmann }
13667207b96SArnd Bergmann 
1373f51dd91SArnd Bergmann static void spufs_prune_dir(struct dentry *dir)
1383f51dd91SArnd Bergmann {
1393f51dd91SArnd Bergmann 	struct dentry *dentry, *tmp;
1401b1dcc1bSJes Sorensen 	mutex_lock(&dir->d_inode->i_mutex);
141c3a9aea7SAndrew Morton 	list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) {
1423f51dd91SArnd Bergmann 		spin_lock(&dcache_lock);
1433f51dd91SArnd Bergmann 		spin_lock(&dentry->d_lock);
1443f51dd91SArnd Bergmann 		if (!(d_unhashed(dentry)) && dentry->d_inode) {
1453f51dd91SArnd Bergmann 			dget_locked(dentry);
1463f51dd91SArnd Bergmann 			__d_drop(dentry);
1473f51dd91SArnd Bergmann 			spin_unlock(&dentry->d_lock);
1483f51dd91SArnd Bergmann 			simple_unlink(dir->d_inode, dentry);
1493f51dd91SArnd Bergmann 			spin_unlock(&dcache_lock);
1503f51dd91SArnd Bergmann 			dput(dentry);
1513f51dd91SArnd Bergmann 		} else {
1523f51dd91SArnd Bergmann 			spin_unlock(&dentry->d_lock);
1533f51dd91SArnd Bergmann 			spin_unlock(&dcache_lock);
1543f51dd91SArnd Bergmann 		}
1553f51dd91SArnd Bergmann 	}
1563f51dd91SArnd Bergmann 	shrink_dcache_parent(dir);
1571b1dcc1bSJes Sorensen 	mutex_unlock(&dir->d_inode->i_mutex);
1583f51dd91SArnd Bergmann }
1593f51dd91SArnd Bergmann 
1600309f02dSMichael Ellerman /* Caller must hold root->i_mutex */
1613f51dd91SArnd Bergmann static int spufs_rmdir(struct inode *root, struct dentry *dir_dentry)
1623f51dd91SArnd Bergmann {
1633f51dd91SArnd Bergmann 	/* remove all entries */
1643f51dd91SArnd Bergmann 	spufs_prune_dir(dir_dentry);
1653f51dd91SArnd Bergmann 
1663f51dd91SArnd Bergmann 	return simple_rmdir(root, dir_dentry);
1673f51dd91SArnd Bergmann }
1683f51dd91SArnd Bergmann 
1693f51dd91SArnd Bergmann static int spufs_fill_dir(struct dentry *dir, struct tree_descr *files,
17067207b96SArnd Bergmann 			  int mode, struct spu_context *ctx)
17167207b96SArnd Bergmann {
17267207b96SArnd Bergmann 	struct dentry *dentry;
17367207b96SArnd Bergmann 	int ret;
17467207b96SArnd Bergmann 
17567207b96SArnd Bergmann 	while (files->name && files->name[0]) {
17667207b96SArnd Bergmann 		ret = -ENOMEM;
17767207b96SArnd Bergmann 		dentry = d_alloc_name(dir, files->name);
17867207b96SArnd Bergmann 		if (!dentry)
17967207b96SArnd Bergmann 			goto out;
18067207b96SArnd Bergmann 		ret = spufs_new_file(dir->d_sb, dentry, files->ops,
18167207b96SArnd Bergmann 					files->mode & mode, ctx);
18267207b96SArnd Bergmann 		if (ret)
18367207b96SArnd Bergmann 			goto out;
18467207b96SArnd Bergmann 		files++;
18567207b96SArnd Bergmann 	}
18667207b96SArnd Bergmann 	return 0;
18767207b96SArnd Bergmann out:
1883f51dd91SArnd Bergmann 	spufs_prune_dir(dir);
18967207b96SArnd Bergmann 	return ret;
19067207b96SArnd Bergmann }
19167207b96SArnd Bergmann 
19267207b96SArnd Bergmann static int spufs_dir_close(struct inode *inode, struct file *file)
19367207b96SArnd Bergmann {
1940309f02dSMichael Ellerman 	struct spu_context *ctx;
19567207b96SArnd Bergmann 	struct inode *dir;
19667207b96SArnd Bergmann 	struct dentry *dentry;
19767207b96SArnd Bergmann 	int ret;
19867207b96SArnd Bergmann 
19967207b96SArnd Bergmann 	dentry = file->f_dentry;
20067207b96SArnd Bergmann 	dir = dentry->d_parent->d_inode;
2010309f02dSMichael Ellerman 	ctx = SPUFS_I(dentry->d_inode)->i_ctx;
202c8ca0633SArnd Bergmann 
2030309f02dSMichael Ellerman 	mutex_lock(&dir->i_mutex);
204c8ca0633SArnd Bergmann 	ret = spufs_rmdir(dir, dentry);
2050309f02dSMichael Ellerman 	mutex_unlock(&dir->i_mutex);
20667207b96SArnd Bergmann 	WARN_ON(ret);
207c8ca0633SArnd Bergmann 
2080309f02dSMichael Ellerman 	/* We have to give up the mm_struct */
2090309f02dSMichael Ellerman 	spu_forget(ctx);
2100309f02dSMichael Ellerman 
21167207b96SArnd Bergmann 	return dcache_dir_close(inode, file);
21267207b96SArnd Bergmann }
21367207b96SArnd Bergmann 
21467207b96SArnd Bergmann struct inode_operations spufs_dir_inode_operations = {
21567207b96SArnd Bergmann 	.lookup = simple_lookup,
21667207b96SArnd Bergmann };
21767207b96SArnd Bergmann 
218e80358adSArnd Bergmann struct file_operations spufs_context_fops = {
21967207b96SArnd Bergmann 	.open		= dcache_dir_open,
22067207b96SArnd Bergmann 	.release	= spufs_dir_close,
22167207b96SArnd Bergmann 	.llseek		= dcache_dir_lseek,
22267207b96SArnd Bergmann 	.read		= generic_read_dir,
22367207b96SArnd Bergmann 	.readdir	= dcache_readdir,
22467207b96SArnd Bergmann 	.fsync		= simple_sync_file,
22567207b96SArnd Bergmann };
22667207b96SArnd Bergmann 
22767207b96SArnd Bergmann static int
22867207b96SArnd Bergmann spufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
22967207b96SArnd Bergmann {
23067207b96SArnd Bergmann 	int ret;
23167207b96SArnd Bergmann 	struct inode *inode;
23267207b96SArnd Bergmann 	struct spu_context *ctx;
23367207b96SArnd Bergmann 
23467207b96SArnd Bergmann 	ret = -ENOSPC;
23567207b96SArnd Bergmann 	inode = spufs_new_inode(dir->i_sb, mode | S_IFDIR);
23667207b96SArnd Bergmann 	if (!inode)
23767207b96SArnd Bergmann 		goto out;
23867207b96SArnd Bergmann 
23967207b96SArnd Bergmann 	if (dir->i_mode & S_ISGID) {
24067207b96SArnd Bergmann 		inode->i_gid = dir->i_gid;
24167207b96SArnd Bergmann 		inode->i_mode &= S_ISGID;
24267207b96SArnd Bergmann 	}
2436df10a82SMark Nutter 	ctx = alloc_spu_context();
24467207b96SArnd Bergmann 	SPUFS_I(inode)->i_ctx = ctx;
24567207b96SArnd Bergmann 	if (!ctx)
24667207b96SArnd Bergmann 		goto out_iput;
24767207b96SArnd Bergmann 
24867207b96SArnd Bergmann 	inode->i_op = &spufs_dir_inode_operations;
24967207b96SArnd Bergmann 	inode->i_fop = &simple_dir_operations;
25067207b96SArnd Bergmann 	ret = spufs_fill_dir(dentry, spufs_dir_contents, mode, ctx);
25167207b96SArnd Bergmann 	if (ret)
25267207b96SArnd Bergmann 		goto out_free_ctx;
25367207b96SArnd Bergmann 
25467207b96SArnd Bergmann 	d_instantiate(dentry, inode);
25567207b96SArnd Bergmann 	dget(dentry);
25667207b96SArnd Bergmann 	dir->i_nlink++;
257346f4d3cSArnd Bergmann 	dentry->d_inode->i_nlink++;
25867207b96SArnd Bergmann 	goto out;
25967207b96SArnd Bergmann 
26067207b96SArnd Bergmann out_free_ctx:
26167207b96SArnd Bergmann 	put_spu_context(ctx);
26267207b96SArnd Bergmann out_iput:
26367207b96SArnd Bergmann 	iput(inode);
26467207b96SArnd Bergmann out:
26567207b96SArnd Bergmann 	return ret;
26667207b96SArnd Bergmann }
26767207b96SArnd Bergmann 
268346f4d3cSArnd Bergmann static int spufs_context_open(struct dentry *dentry, struct vfsmount *mnt)
269346f4d3cSArnd Bergmann {
270346f4d3cSArnd Bergmann 	int ret;
271346f4d3cSArnd Bergmann 	struct file *filp;
272346f4d3cSArnd Bergmann 
273346f4d3cSArnd Bergmann 	ret = get_unused_fd();
274346f4d3cSArnd Bergmann 	if (ret < 0) {
275346f4d3cSArnd Bergmann 		dput(dentry);
276346f4d3cSArnd Bergmann 		mntput(mnt);
277346f4d3cSArnd Bergmann 		goto out;
278346f4d3cSArnd Bergmann 	}
279346f4d3cSArnd Bergmann 
280346f4d3cSArnd Bergmann 	filp = dentry_open(dentry, mnt, O_RDONLY);
281346f4d3cSArnd Bergmann 	if (IS_ERR(filp)) {
282346f4d3cSArnd Bergmann 		put_unused_fd(ret);
283346f4d3cSArnd Bergmann 		ret = PTR_ERR(filp);
284346f4d3cSArnd Bergmann 		goto out;
285346f4d3cSArnd Bergmann 	}
286346f4d3cSArnd Bergmann 
287346f4d3cSArnd Bergmann 	filp->f_op = &spufs_context_fops;
288346f4d3cSArnd Bergmann 	fd_install(ret, filp);
289346f4d3cSArnd Bergmann out:
290346f4d3cSArnd Bergmann 	return ret;
291346f4d3cSArnd Bergmann }
292346f4d3cSArnd Bergmann 
293346f4d3cSArnd Bergmann static struct file_system_type spufs_type;
294346f4d3cSArnd Bergmann 
2956ff730c3SArnd Bergmann long spufs_create_thread(struct nameidata *nd,
29667207b96SArnd Bergmann 			 unsigned int flags, mode_t mode)
29767207b96SArnd Bergmann {
29867207b96SArnd Bergmann 	struct dentry *dentry;
29967207b96SArnd Bergmann 	int ret;
30067207b96SArnd Bergmann 
30167207b96SArnd Bergmann 	/* need to be at the root of spufs */
30267207b96SArnd Bergmann 	ret = -EINVAL;
303346f4d3cSArnd Bergmann 	if (nd->dentry->d_sb->s_type != &spufs_type ||
30467207b96SArnd Bergmann 	    nd->dentry != nd->dentry->d_sb->s_root)
30567207b96SArnd Bergmann 		goto out;
30667207b96SArnd Bergmann 
307*c9832948Sarnd@arndb.de 	/* all flags are reserved */
308*c9832948Sarnd@arndb.de 	if (flags)
309*c9832948Sarnd@arndb.de 		goto out;
310*c9832948Sarnd@arndb.de 
31167207b96SArnd Bergmann 	dentry = lookup_create(nd, 1);
31267207b96SArnd Bergmann 	ret = PTR_ERR(dentry);
31367207b96SArnd Bergmann 	if (IS_ERR(dentry))
31467207b96SArnd Bergmann 		goto out_dir;
31567207b96SArnd Bergmann 
31667207b96SArnd Bergmann 	ret = -EEXIST;
31767207b96SArnd Bergmann 	if (dentry->d_inode)
31867207b96SArnd Bergmann 		goto out_dput;
31967207b96SArnd Bergmann 
32067207b96SArnd Bergmann 	mode &= ~current->fs->umask;
32167207b96SArnd Bergmann 	ret = spufs_mkdir(nd->dentry->d_inode, dentry, mode & S_IRWXUGO);
32267207b96SArnd Bergmann 	if (ret)
32367207b96SArnd Bergmann 		goto out_dput;
32467207b96SArnd Bergmann 
325346f4d3cSArnd Bergmann 	/*
326346f4d3cSArnd Bergmann 	 * get references for dget and mntget, will be released
327346f4d3cSArnd Bergmann 	 * in error path of *_open().
328346f4d3cSArnd Bergmann 	 */
329346f4d3cSArnd Bergmann 	ret = spufs_context_open(dget(dentry), mntget(nd->mnt));
3300309f02dSMichael Ellerman 	if (ret < 0) {
3310309f02dSMichael Ellerman 		WARN_ON(spufs_rmdir(nd->dentry->d_inode, dentry));
3320309f02dSMichael Ellerman 		mutex_unlock(&nd->dentry->d_inode->i_mutex);
3330309f02dSMichael Ellerman 		spu_forget(SPUFS_I(dentry->d_inode)->i_ctx);
3340309f02dSMichael Ellerman 		dput(dentry);
3350309f02dSMichael Ellerman 		goto out;
3360309f02dSMichael Ellerman 	}
33767207b96SArnd Bergmann 
33867207b96SArnd Bergmann out_dput:
33967207b96SArnd Bergmann 	dput(dentry);
34067207b96SArnd Bergmann out_dir:
3411b1dcc1bSJes Sorensen 	mutex_unlock(&nd->dentry->d_inode->i_mutex);
34267207b96SArnd Bergmann out:
34367207b96SArnd Bergmann 	return ret;
34467207b96SArnd Bergmann }
34567207b96SArnd Bergmann 
34667207b96SArnd Bergmann /* File system initialization */
34767207b96SArnd Bergmann enum {
34867207b96SArnd Bergmann 	Opt_uid, Opt_gid, Opt_err,
34967207b96SArnd Bergmann };
35067207b96SArnd Bergmann 
35167207b96SArnd Bergmann static match_table_t spufs_tokens = {
35267207b96SArnd Bergmann 	{ Opt_uid, "uid=%d" },
35367207b96SArnd Bergmann 	{ Opt_gid, "gid=%d" },
35467207b96SArnd Bergmann 	{ Opt_err, NULL  },
35567207b96SArnd Bergmann };
35667207b96SArnd Bergmann 
35767207b96SArnd Bergmann static int
35867207b96SArnd Bergmann spufs_parse_options(char *options, struct inode *root)
35967207b96SArnd Bergmann {
36067207b96SArnd Bergmann 	char *p;
36167207b96SArnd Bergmann 	substring_t args[MAX_OPT_ARGS];
36267207b96SArnd Bergmann 
36367207b96SArnd Bergmann 	while ((p = strsep(&options, ",")) != NULL) {
36467207b96SArnd Bergmann 		int token, option;
36567207b96SArnd Bergmann 
36667207b96SArnd Bergmann 		if (!*p)
36767207b96SArnd Bergmann 			continue;
36867207b96SArnd Bergmann 
36967207b96SArnd Bergmann 		token = match_token(p, spufs_tokens, args);
37067207b96SArnd Bergmann 		switch (token) {
37167207b96SArnd Bergmann 		case Opt_uid:
37267207b96SArnd Bergmann 			if (match_int(&args[0], &option))
37367207b96SArnd Bergmann 				return 0;
37467207b96SArnd Bergmann 			root->i_uid = option;
37567207b96SArnd Bergmann 			break;
37667207b96SArnd Bergmann 		case Opt_gid:
37767207b96SArnd Bergmann 			if (match_int(&args[0], &option))
37867207b96SArnd Bergmann 				return 0;
37967207b96SArnd Bergmann 			root->i_gid = option;
38067207b96SArnd Bergmann 			break;
38167207b96SArnd Bergmann 		default:
38267207b96SArnd Bergmann 			return 0;
38367207b96SArnd Bergmann 		}
38467207b96SArnd Bergmann 	}
38567207b96SArnd Bergmann 	return 1;
38667207b96SArnd Bergmann }
38767207b96SArnd Bergmann 
38867207b96SArnd Bergmann static int
3898b3d6663SArnd Bergmann spufs_create_root(struct super_block *sb, void *data)
3908b3d6663SArnd Bergmann {
39167207b96SArnd Bergmann 	struct inode *inode;
39267207b96SArnd Bergmann 	int ret;
39367207b96SArnd Bergmann 
39467207b96SArnd Bergmann 	ret = -ENOMEM;
39567207b96SArnd Bergmann 	inode = spufs_new_inode(sb, S_IFDIR | 0775);
39667207b96SArnd Bergmann 	if (!inode)
39767207b96SArnd Bergmann 		goto out;
39867207b96SArnd Bergmann 
39967207b96SArnd Bergmann 	inode->i_op = &spufs_dir_inode_operations;
40067207b96SArnd Bergmann 	inode->i_fop = &simple_dir_operations;
40167207b96SArnd Bergmann 	SPUFS_I(inode)->i_ctx = NULL;
40267207b96SArnd Bergmann 
40367207b96SArnd Bergmann 	ret = -EINVAL;
40467207b96SArnd Bergmann 	if (!spufs_parse_options(data, inode))
40567207b96SArnd Bergmann 		goto out_iput;
40667207b96SArnd Bergmann 
40767207b96SArnd Bergmann 	ret = -ENOMEM;
40867207b96SArnd Bergmann 	sb->s_root = d_alloc_root(inode);
40967207b96SArnd Bergmann 	if (!sb->s_root)
41067207b96SArnd Bergmann 		goto out_iput;
41167207b96SArnd Bergmann 
41267207b96SArnd Bergmann 	return 0;
41367207b96SArnd Bergmann out_iput:
41467207b96SArnd Bergmann 	iput(inode);
41567207b96SArnd Bergmann out:
41667207b96SArnd Bergmann 	return ret;
41767207b96SArnd Bergmann }
41867207b96SArnd Bergmann 
41967207b96SArnd Bergmann static int
42067207b96SArnd Bergmann spufs_fill_super(struct super_block *sb, void *data, int silent)
42167207b96SArnd Bergmann {
42267207b96SArnd Bergmann 	static struct super_operations s_ops = {
42367207b96SArnd Bergmann 		.alloc_inode = spufs_alloc_inode,
42467207b96SArnd Bergmann 		.destroy_inode = spufs_destroy_inode,
42567207b96SArnd Bergmann 		.statfs = simple_statfs,
42667207b96SArnd Bergmann 		.delete_inode = spufs_delete_inode,
42767207b96SArnd Bergmann 		.drop_inode = generic_delete_inode,
42867207b96SArnd Bergmann 	};
42967207b96SArnd Bergmann 
43067207b96SArnd Bergmann 	sb->s_maxbytes = MAX_LFS_FILESIZE;
43167207b96SArnd Bergmann 	sb->s_blocksize = PAGE_CACHE_SIZE;
43267207b96SArnd Bergmann 	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
43367207b96SArnd Bergmann 	sb->s_magic = SPUFS_MAGIC;
43467207b96SArnd Bergmann 	sb->s_op = &s_ops;
43567207b96SArnd Bergmann 
43667207b96SArnd Bergmann 	return spufs_create_root(sb, data);
43767207b96SArnd Bergmann }
43867207b96SArnd Bergmann 
43967207b96SArnd Bergmann static struct super_block *
44067207b96SArnd Bergmann spufs_get_sb(struct file_system_type *fstype, int flags,
44167207b96SArnd Bergmann 		const char *name, void *data)
44267207b96SArnd Bergmann {
44367207b96SArnd Bergmann 	return get_sb_single(fstype, flags, data, spufs_fill_super);
44467207b96SArnd Bergmann }
44567207b96SArnd Bergmann 
44667207b96SArnd Bergmann static struct file_system_type spufs_type = {
44767207b96SArnd Bergmann 	.owner = THIS_MODULE,
44867207b96SArnd Bergmann 	.name = "spufs",
44967207b96SArnd Bergmann 	.get_sb = spufs_get_sb,
45067207b96SArnd Bergmann 	.kill_sb = kill_litter_super,
45167207b96SArnd Bergmann };
45267207b96SArnd Bergmann 
453e78b47a5SArnd Bergmann static int __init spufs_init(void)
45467207b96SArnd Bergmann {
45567207b96SArnd Bergmann 	int ret;
45667207b96SArnd Bergmann 	ret = -ENOMEM;
45767207b96SArnd Bergmann 	spufs_inode_cache = kmem_cache_create("spufs_inode_cache",
45867207b96SArnd Bergmann 			sizeof(struct spufs_inode_info), 0,
45967207b96SArnd Bergmann 			SLAB_HWCACHE_ALIGN, spufs_init_once, NULL);
46067207b96SArnd Bergmann 
46167207b96SArnd Bergmann 	if (!spufs_inode_cache)
46267207b96SArnd Bergmann 		goto out;
4638b3d6663SArnd Bergmann 	if (spu_sched_init() != 0) {
4648b3d6663SArnd Bergmann 		kmem_cache_destroy(spufs_inode_cache);
4658b3d6663SArnd Bergmann 		goto out;
4668b3d6663SArnd Bergmann 	}
46767207b96SArnd Bergmann 	ret = register_filesystem(&spufs_type);
46867207b96SArnd Bergmann 	if (ret)
46967207b96SArnd Bergmann 		goto out_cache;
47067207b96SArnd Bergmann 	ret = register_spu_syscalls(&spufs_calls);
47167207b96SArnd Bergmann 	if (ret)
47267207b96SArnd Bergmann 		goto out_fs;
47367207b96SArnd Bergmann 	return 0;
47467207b96SArnd Bergmann out_fs:
47567207b96SArnd Bergmann 	unregister_filesystem(&spufs_type);
47667207b96SArnd Bergmann out_cache:
47767207b96SArnd Bergmann 	kmem_cache_destroy(spufs_inode_cache);
47867207b96SArnd Bergmann out:
47967207b96SArnd Bergmann 	return ret;
48067207b96SArnd Bergmann }
48167207b96SArnd Bergmann module_init(spufs_init);
48267207b96SArnd Bergmann 
483e78b47a5SArnd Bergmann static void __exit spufs_exit(void)
48467207b96SArnd Bergmann {
4858b3d6663SArnd Bergmann 	spu_sched_exit();
48667207b96SArnd Bergmann 	unregister_spu_syscalls(&spufs_calls);
48767207b96SArnd Bergmann 	unregister_filesystem(&spufs_type);
48867207b96SArnd Bergmann 	kmem_cache_destroy(spufs_inode_cache);
48967207b96SArnd Bergmann }
49067207b96SArnd Bergmann module_exit(spufs_exit);
49167207b96SArnd Bergmann 
49267207b96SArnd Bergmann MODULE_LICENSE("GPL");
49367207b96SArnd Bergmann MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");
49467207b96SArnd Bergmann 
495