inode.c 17.3 KB
Newer Older
1

2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
 * SPU file system
 *
 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
 *
 * Author: Arnd Bergmann <arndb@de.ibm.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/file.h>
#include <linux/fs.h>
26
#include <linux/fsnotify.h>
27 28 29 30
#include <linux/backing-dev.h>
#include <linux/init.h>
#include <linux/ioctl.h>
#include <linux/module.h>
31
#include <linux/mount.h>
32 33 34 35 36 37
#include <linux/namei.h>
#include <linux/pagemap.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/parser.h>

38
#include <asm/prom.h>
39
#include <asm/spu.h>
40
#include <asm/spu_priv1.h>
41 42 43 44
#include <asm/uaccess.h>

#include "spufs.h"

45 46 47 48
struct spufs_sb_info {
	int debug;
};

49
static struct kmem_cache *spufs_inode_cache;
50
char *isolated_loader;
51
static int isolated_loader_size;
52

53 54 55 56 57
static struct spufs_sb_info *spufs_get_sb_info(struct super_block *sb)
{
	return sb->s_fs_info;
}

58 59 60 61 62
static struct inode *
spufs_alloc_inode(struct super_block *sb)
{
	struct spufs_inode_info *ei;

63
	ei = kmem_cache_alloc(spufs_inode_cache, GFP_KERNEL);
64 65
	if (!ei)
		return NULL;
66 67 68

	ei->i_gang = NULL;
	ei->i_ctx = NULL;
69
	ei->i_openers = 0;
70

71 72 73
	return &ei->vfs_inode;
}

Nick Piggin's avatar
Nick Piggin committed
74
static void spufs_i_callback(struct rcu_head *head)
75
{
Nick Piggin's avatar
Nick Piggin committed
76
	struct inode *inode = container_of(head, struct inode, i_rcu);
77 78 79
	kmem_cache_free(spufs_inode_cache, SPUFS_I(inode));
}

Nick Piggin's avatar
Nick Piggin committed
80 81 82 83 84
static void spufs_destroy_inode(struct inode *inode)
{
	call_rcu(&inode->i_rcu, spufs_i_callback);
}

85
static void
86
spufs_init_once(void *p)
87 88 89
{
	struct spufs_inode_info *ei = p;

90
	inode_init_once(&ei->vfs_inode);
91 92 93
}

static struct inode *
Al Viro's avatar
Al Viro committed
94
spufs_new_inode(struct super_block *sb, umode_t mode)
95 96 97 98 99 100 101
{
	struct inode *inode;

	inode = new_inode(sb);
	if (!inode)
		goto out;

102
	inode->i_ino = get_next_ino();
103
	inode->i_mode = mode;
104 105
	inode->i_uid = current_fsuid();
	inode->i_gid = current_fsgid();
106 107 108 109 110 111 112 113
	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
out:
	return inode;
}

static int
spufs_setattr(struct dentry *dentry, struct iattr *attr)
{
114
	struct inode *inode = d_inode(dentry);
115 116 117 118

	if ((attr->ia_valid & ATTR_SIZE) &&
	    (attr->ia_size != inode->i_size))
		return -EINVAL;
Christoph Hellwig's avatar
Christoph Hellwig committed
119 120 121
	setattr_copy(inode, attr);
	mark_inode_dirty(inode);
	return 0;
122 123 124 125 126
}


static int
spufs_new_file(struct super_block *sb, struct dentry *dentry,
Al Viro's avatar
Al Viro committed
127
		const struct file_operations *fops, umode_t mode,
128
		size_t size, struct spu_context *ctx)
129
{
130
	static const struct inode_operations spufs_file_iops = {
131 132 133 134 135 136 137 138 139 140 141 142 143
		.setattr = spufs_setattr,
	};
	struct inode *inode;
	int ret;

	ret = -ENOSPC;
	inode = spufs_new_inode(sb, S_IFREG | mode);
	if (!inode)
		goto out;

	ret = 0;
	inode->i_op = &spufs_file_iops;
	inode->i_fop = fops;
144
	inode->i_size = size;
145
	inode->i_private = SPUFS_I(inode)->i_ctx = get_spu_context(ctx);
146 147 148 149 150 151
	d_add(dentry, inode);
out:
	return ret;
}

static void
Al Viro's avatar
Al Viro committed
152
spufs_evict_inode(struct inode *inode)
153
{
154
	struct spufs_inode_info *ei = SPUFS_I(inode);
155
	clear_inode(inode);
156 157 158 159
	if (ei->i_ctx)
		put_spu_context(ei->i_ctx);
	if (ei->i_gang)
		put_spu_gang(ei->i_gang);
160 161
}

162
static void spufs_prune_dir(struct dentry *dir)
163
{
164
	struct dentry *dentry, *tmp;
165

166
	mutex_lock(&d_inode(dir)->i_mutex);
167
	list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_child) {
168
		spin_lock(&dentry->d_lock);
Al Viro's avatar
Al Viro committed
169
		if (simple_positive(dentry)) {
170
			dget_dlock(dentry);
171 172
			__d_drop(dentry);
			spin_unlock(&dentry->d_lock);
173
			simple_unlink(d_inode(dir), dentry);
Nick Piggin's avatar
Nick Piggin committed
174
			/* XXX: what was dcache_lock protecting here? Other
Nick Piggin's avatar
Nick Piggin committed
175 176
			 * filesystems (IB, configfs) release dcache_lock
			 * before unlink */
177 178 179 180
			dput(dentry);
		} else {
			spin_unlock(&dentry->d_lock);
		}
181
	}
182
	shrink_dcache_parent(dir);
183
	mutex_unlock(&d_inode(dir)->i_mutex);
184 185
}

186 187
/* Caller must hold parent->i_mutex */
static int spufs_rmdir(struct inode *parent, struct dentry *dir)
188 189
{
	/* remove all entries */
Al Viro's avatar
Al Viro committed
190
	int res;
191
	spufs_prune_dir(dir);
192
	d_drop(dir);
Al Viro's avatar
Al Viro committed
193 194
	res = simple_rmdir(parent, dir);
	/* We have to give up the mm_struct */
195
	spu_forget(SPUFS_I(d_inode(dir))->i_ctx);
Al Viro's avatar
Al Viro committed
196
	return res;
197 198
}

199
static int spufs_fill_dir(struct dentry *dir,
Al Viro's avatar
Al Viro committed
200
		const struct spufs_tree_descr *files, umode_t mode,
201
		struct spu_context *ctx)
202 203
{
	while (files->name && files->name[0]) {
204 205
		int ret;
		struct dentry *dentry = d_alloc_name(dir, files->name);
206
		if (!dentry)
207
			return -ENOMEM;
208
		ret = spufs_new_file(dir->d_sb, dentry, files->ops,
209
					files->mode & mode, files->size, ctx);
210
		if (ret)
211
			return ret;
212 213 214 215 216
		files++;
	}
	return 0;
}

217 218
static int spufs_dir_close(struct inode *inode, struct file *file)
{
219
	struct spu_context *ctx;
220 221
	struct inode *parent;
	struct dentry *dir;
222 223
	int ret;

224
	dir = file->f_path.dentry;
225 226
	parent = d_inode(dir->d_parent);
	ctx = SPUFS_I(d_inode(dir))->i_ctx;
227

228
	mutex_lock_nested(&parent->i_mutex, I_MUTEX_PARENT);
229 230
	ret = spufs_rmdir(parent, dir);
	mutex_unlock(&parent->i_mutex);
231
	WARN_ON(ret);
232

233 234 235
	return dcache_dir_close(inode, file);
}

236
const struct file_operations spufs_context_fops = {
237 238 239 240
	.open		= dcache_dir_open,
	.release	= spufs_dir_close,
	.llseek		= dcache_dir_lseek,
	.read		= generic_read_dir,
241
	.iterate	= dcache_readdir,
242
	.fsync		= noop_fsync,
243
};
244
EXPORT_SYMBOL_GPL(spufs_context_fops);
245 246

static int
247
spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
Al Viro's avatar
Al Viro committed
248
		umode_t mode)
249 250 251 252 253 254 255
{
	int ret;
	struct inode *inode;
	struct spu_context *ctx;

	inode = spufs_new_inode(dir->i_sb, mode | S_IFDIR);
	if (!inode)
256
		return -ENOSPC;
257 258 259 260 261

	if (dir->i_mode & S_ISGID) {
		inode->i_gid = dir->i_gid;
		inode->i_mode &= S_ISGID;
	}
262
	ctx = alloc_spu_context(SPUFS_I(dir)->i_gang); /* XXX gang */
263
	SPUFS_I(inode)->i_ctx = ctx;
264 265 266 267
	if (!ctx) {
		iput(inode);
		return -ENOSPC;
	}
268

269
	ctx->flags = flags;
270
	inode->i_op = &simple_dir_inode_operations;
271
	inode->i_fop = &simple_dir_operations;
272 273 274 275 276 277 278 279 280

	mutex_lock(&inode->i_mutex);

	dget(dentry);
	inc_nlink(dir);
	inc_nlink(inode);

	d_instantiate(dentry, inode);

281 282 283 284 285 286
	if (flags & SPU_CREATE_NOSCHED)
		ret = spufs_fill_dir(dentry, spufs_dir_nosched_contents,
					 mode, ctx);
	else
		ret = spufs_fill_dir(dentry, spufs_dir_contents, mode, ctx);

287
	if (!ret && spufs_get_sb_info(dir->i_sb)->debug)
288 289 290 291
		ret = spufs_fill_dir(dentry, spufs_dir_debug_contents,
				mode, ctx);

	if (ret)
292 293 294
		spufs_rmdir(dir, dentry);

	mutex_unlock(&inode->i_mutex);
295

296 297 298
	return ret;
}

299
static int spufs_context_open(struct path *path)
300 301 302 303
{
	int ret;
	struct file *filp;

304
	ret = get_unused_fd_flags(0);
305 306
	if (ret < 0)
		return ret;
307

308
	filp = dentry_open(path, O_RDONLY, current_cred());
309 310
	if (IS_ERR(filp)) {
		put_unused_fd(ret);
311
		return PTR_ERR(filp);
312 313 314 315 316 317 318
	}

	filp->f_op = &spufs_context_fops;
	fd_install(ret, filp);
	return ret;
}

319 320 321 322
static struct spu_context *
spufs_assert_affinity(unsigned int flags, struct spu_gang *gang,
						struct file *filp)
{
323
	struct spu_context *tmp, *neighbor, *err;
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
	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(
Al Viro's avatar
Al Viro committed
350
				SPUFS_I(file_inode(filp))->i_ctx);
351 352 353 354

		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,
355 356 357 358
		    aff_list)->aff_head) {
			err = ERR_PTR(-EEXIST);
			goto out_put_neighbor;
		}
359

360 361 362 363
		if (gang != neighbor->gang) {
			err = ERR_PTR(-EINVAL);
			goto out_put_neighbor;
		}
364 365 366 367 368 369 370 371 372 373 374 375 376

		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;
		}

377 378 379 380
		if (node == MAX_NUMNODES) {
			err = ERR_PTR(-EEXIST);
			goto out_put_neighbor;
		}
381 382 383
	}

	return neighbor;
384 385 386 387

out_put_neighbor:
	put_spu_context(neighbor);
	return err;
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
}

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,
Al Viro's avatar
Al Viro committed
423
			struct vfsmount *mnt, int flags, umode_t mode,
424
			struct file *aff_filp)
425 426
{
	int ret;
427 428 429
	int affinity;
	struct spu_gang *gang;
	struct spu_context *neighbor;
430
	struct path path = {.mnt = mnt, .dentry = dentry};
431

432 433
	if ((flags & SPU_CREATE_NOSCHED) &&
	    !capable(CAP_SYS_NICE))
434
		return -EPERM;
435 436 437

	if ((flags & (SPU_CREATE_NOSCHED | SPU_CREATE_ISOLATE))
	    == SPU_CREATE_ISOLATE)
438
		return -EINVAL;
439

440
	if ((flags & SPU_CREATE_ISOLATE) && !isolated_loader)
441
		return -ENODEV;
442

443 444 445 446 447 448
	gang = NULL;
	neighbor = NULL;
	affinity = flags & (SPU_CREATE_AFFINITY_MEM | SPU_CREATE_AFFINITY_SPU);
	if (affinity) {
		gang = SPUFS_I(inode)->i_gang;
		if (!gang)
449
			return -EINVAL;
450 451 452 453 454 455 456 457
		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;
		}
	}

458 459
	ret = spufs_mkdir(inode, dentry, flags, mode & S_IRWXUGO);
	if (ret)
460 461
		goto out_aff_unlock;

462
	if (affinity) {
463
		spufs_set_affinity(flags, SPUFS_I(d_inode(dentry))->i_ctx,
464
								neighbor);
465 466 467
		if (neighbor)
			put_spu_context(neighbor);
	}
468

469
	ret = spufs_context_open(&path);
470
	if (ret < 0)
471 472
		WARN_ON(spufs_rmdir(inode, dentry));

473 474 475
out_aff_unlock:
	if (affinity)
		mutex_unlock(&gang->aff_mutex);
476 477 478 479
	return ret;
}

static int
Al Viro's avatar
Al Viro committed
480
spufs_mkgang(struct inode *dir, struct dentry *dentry, umode_t mode)
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
{
	int ret;
	struct inode *inode;
	struct spu_gang *gang;

	ret = -ENOSPC;
	inode = spufs_new_inode(dir->i_sb, mode | S_IFDIR);
	if (!inode)
		goto out;

	ret = 0;
	if (dir->i_mode & S_ISGID) {
		inode->i_gid = dir->i_gid;
		inode->i_mode &= S_ISGID;
	}
	gang = alloc_spu_gang();
	SPUFS_I(inode)->i_ctx = NULL;
	SPUFS_I(inode)->i_gang = gang;
	if (!gang)
		goto out_iput;

502
	inode->i_op = &simple_dir_inode_operations;
503 504 505
	inode->i_fop = &simple_dir_operations;

	d_instantiate(dentry, inode);
Jeremy Kerr's avatar
Jeremy Kerr committed
506
	inc_nlink(dir);
507
	inc_nlink(d_inode(dentry));
508 509 510 511 512 513 514 515
	return ret;

out_iput:
	iput(inode);
out:
	return ret;
}

516
static int spufs_gang_open(struct path *path)
517 518 519 520
{
	int ret;
	struct file *filp;

521
	ret = get_unused_fd_flags(0);
522 523
	if (ret < 0)
		return ret;
524

525 526 527 528
	/*
	 * get references for dget and mntget, will be released
	 * in error path of *_open().
	 */
529
	filp = dentry_open(path, O_RDONLY, current_cred());
530 531
	if (IS_ERR(filp)) {
		put_unused_fd(ret);
532
		return PTR_ERR(filp);
533 534
	}

535
	filp->f_op = &simple_dir_operations;
536 537 538 539 540 541
	fd_install(ret, filp);
	return ret;
}

static int spufs_create_gang(struct inode *inode,
			struct dentry *dentry,
Al Viro's avatar
Al Viro committed
542
			struct vfsmount *mnt, umode_t mode)
543
{
544
	struct path path = {.mnt = mnt, .dentry = dentry};
545 546 547
	int ret;

	ret = spufs_mkgang(inode, dentry, mode & S_IRWXUGO);
548 549 550 551 552 553
	if (!ret) {
		ret = spufs_gang_open(&path);
		if (ret < 0) {
			int err = simple_rmdir(inode, dentry);
			WARN_ON(err);
		}
554
	}
555 556 557 558
	return ret;
}


559 560
static struct file_system_type spufs_type;

561
long spufs_create(struct path *path, struct dentry *dentry,
Al Viro's avatar
Al Viro committed
562
		unsigned int flags, umode_t mode, struct file *filp)
563
{
564
	struct inode *dir = d_inode(path->dentry);
565 566
	int ret;

567
	/* check if we are on spufs */
568
	if (path->dentry->d_sb->s_type != &spufs_type)
569
		return -EINVAL;
570

571
	/* don't accept undefined flags */
572
	if (flags & (~SPU_CREATE_FLAG_ALL))
573
		return -EINVAL;
574

575
	/* only threads can be underneath a gang */
576 577 578
	if (path->dentry != path->dentry->d_sb->s_root)
		if ((flags & SPU_CREATE_GANG) || !SPUFS_I(dir)->i_gang)
			return -EINVAL;
579

Al Viro's avatar
Al Viro committed
580
	mode &= ~current_umask();
581

582
	if (flags & SPU_CREATE_GANG)
583
		ret = spufs_create_gang(dir, dentry, path->mnt, mode);
584
	else
585
		ret = spufs_create_context(dir, dentry, path->mnt, flags, mode,
586
					    filp);
587
	if (ret >= 0)
588
		fsnotify_mkdir(dir, dentry);
589 590 591 592 593 594

	return ret;
}

/* File system initialization */
enum {
595
	Opt_uid, Opt_gid, Opt_mode, Opt_debug, Opt_err,
596 597
};

598
static const match_table_t spufs_tokens = {
599 600 601 602 603
	{ Opt_uid,   "uid=%d" },
	{ Opt_gid,   "gid=%d" },
	{ Opt_mode,  "mode=%o" },
	{ Opt_debug, "debug" },
	{ Opt_err,    NULL  },
604 605 606
};

static int
607
spufs_parse_options(struct super_block *sb, char *options, struct inode *root)
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
{
	char *p;
	substring_t args[MAX_OPT_ARGS];

	while ((p = strsep(&options, ",")) != NULL) {
		int token, option;

		if (!*p)
			continue;

		token = match_token(p, spufs_tokens, args);
		switch (token) {
		case Opt_uid:
			if (match_int(&args[0], &option))
				return 0;
623 624 625
			root->i_uid = make_kuid(current_user_ns(), option);
			if (!uid_valid(root->i_uid))
				return 0;
626 627 628 629
			break;
		case Opt_gid:
			if (match_int(&args[0], &option))
				return 0;
630 631 632
			root->i_gid = make_kgid(current_user_ns(), option);
			if (!gid_valid(root->i_gid))
				return 0;
633
			break;
634 635 636 637 638
		case Opt_mode:
			if (match_octal(&args[0], &option))
				return 0;
			root->i_mode = option | S_IFDIR;
			break;
639 640 641
		case Opt_debug:
			spufs_get_sb_info(sb)->debug = 1;
			break;
642 643 644 645 646 647 648
		default:
			return 0;
		}
	}
	return 1;
}

649 650
static void spufs_exit_isolated_loader(void)
{
651 652
	free_pages((unsigned long) isolated_loader,
			get_order(isolated_loader_size));
653 654
}

655 656 657 658 659 660 661 662 663 664 665
static void
spufs_init_isolated_loader(void)
{
	struct device_node *dn;
	const char *loader;
	int size;

	dn = of_find_node_by_path("/spu-isolation");
	if (!dn)
		return;

666
	loader = of_get_property(dn, "loader", &size);
667 668 669
	if (!loader)
		return;

670 671
	/* the loader must be align on a 16 byte boundary */
	isolated_loader = (char *)__get_free_pages(GFP_KERNEL, get_order(size));
672 673 674
	if (!isolated_loader)
		return;

675
	isolated_loader_size = size;
676 677 678 679
	memcpy(isolated_loader, loader, size);
	printk(KERN_INFO "spufs: SPU isolation mode enabled\n");
}

680
static int
681 682
spufs_create_root(struct super_block *sb, void *data)
{
683 684 685
	struct inode *inode;
	int ret;

686 687 688 689
	ret = -ENODEV;
	if (!spu_management_ops)
		goto out;

690 691 692 693 694
	ret = -ENOMEM;
	inode = spufs_new_inode(sb, S_IFDIR | 0775);
	if (!inode)
		goto out;

695
	inode->i_op = &simple_dir_inode_operations;
696 697
	inode->i_fop = &simple_dir_operations;
	SPUFS_I(inode)->i_ctx = NULL;
698
	inc_nlink(inode);
699 700

	ret = -EINVAL;
701
	if (!spufs_parse_options(sb, data, inode))
702 703 704
		goto out_iput;

	ret = -ENOMEM;
705
	sb->s_root = d_make_root(inode);
706
	if (!sb->s_root)
707
		goto out;
708 709 710 711 712 713 714 715 716 717 718

	return 0;
out_iput:
	iput(inode);
out:
	return ret;
}

static int
spufs_fill_super(struct super_block *sb, void *data, int silent)
{
719
	struct spufs_sb_info *info;
720
	static const struct super_operations s_ops = {
721 722 723
		.alloc_inode = spufs_alloc_inode,
		.destroy_inode = spufs_destroy_inode,
		.statfs = simple_statfs,
Al Viro's avatar
Al Viro committed
724
		.evict_inode = spufs_evict_inode,
Miklos Szeredi's avatar
Miklos Szeredi committed
725
		.show_options = generic_show_options,
726 727
	};

Miklos Szeredi's avatar
Miklos Szeredi committed
728 729
	save_mount_options(sb, data);

730 731 732 733
	info = kzalloc(sizeof(*info), GFP_KERNEL);
	if (!info)
		return -ENOMEM;

734 735 736 737 738
	sb->s_maxbytes = MAX_LFS_FILESIZE;
	sb->s_blocksize = PAGE_CACHE_SIZE;
	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
	sb->s_magic = SPUFS_MAGIC;
	sb->s_op = &s_ops;
739
	sb->s_fs_info = info;
740 741 742 743

	return spufs_create_root(sb, data);
}

Al Viro's avatar
Al Viro committed
744 745 746
static struct dentry *
spufs_mount(struct file_system_type *fstype, int flags,
		const char *name, void *data)
747
{
Al Viro's avatar
Al Viro committed
748
	return mount_single(fstype, flags, data, spufs_fill_super);
749 750 751 752 753
}

static struct file_system_type spufs_type = {
	.owner = THIS_MODULE,
	.name = "spufs",
Al Viro's avatar
Al Viro committed
754
	.mount = spufs_mount,
755 756
	.kill_sb = kill_litter_super,
};
757
MODULE_ALIAS_FS("spufs");
758

759
static int __init spufs_init(void)
760 761
{
	int ret;
762

763 764 765 766
	ret = -ENODEV;
	if (!spu_management_ops)
		goto out;

767 768 769
	ret = -ENOMEM;
	spufs_inode_cache = kmem_cache_create("spufs_inode_cache",
			sizeof(struct spufs_inode_info), 0,
770
			SLAB_HWCACHE_ALIGN|SLAB_ACCOUNT, spufs_init_once);
771 772 773

	if (!spufs_inode_cache)
		goto out;
774
	ret = spu_sched_init();
775 776
	if (ret)
		goto out_cache;
777
	ret = register_spu_syscalls(&spufs_calls);
778 779
	if (ret)
		goto out_sched;
780
	ret = register_filesystem(&spufs_type);
781
	if (ret)
782
		goto out_syscalls;
783 784

	spufs_init_isolated_loader();
785

786
	return 0;
787

788 789
out_syscalls:
	unregister_spu_syscalls(&spufs_calls);
790 791
out_sched:
	spu_sched_exit();
792 793 794 795 796 797 798
out_cache:
	kmem_cache_destroy(spufs_inode_cache);
out:
	return ret;
}
module_init(spufs_init);

799
static void __exit spufs_exit(void)
800
{
801
	spu_sched_exit();
802
	spufs_exit_isolated_loader();
803 804 805 806 807 808 809 810 811
	unregister_spu_syscalls(&spufs_calls);
	unregister_filesystem(&spufs_type);
	kmem_cache_destroy(spufs_inode_cache);
}
module_exit(spufs_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");