167207b96SArnd Bergmann /*
267207b96SArnd Bergmann  * SPU file system -- SPU context management
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 
238b3d6663SArnd Bergmann #include <linux/fs.h>
248b3d6663SArnd Bergmann #include <linux/mm.h>
2567207b96SArnd Bergmann #include <linux/slab.h>
2667207b96SArnd Bergmann #include <asm/spu.h>
275473af04SMark Nutter #include <asm/spu_csa.h>
2867207b96SArnd Bergmann #include "spufs.h"
2967207b96SArnd Bergmann 
306263203eSArnd Bergmann struct spu_context *alloc_spu_context(struct spu_gang *gang)
3167207b96SArnd Bergmann {
3267207b96SArnd Bergmann 	struct spu_context *ctx;
33c5c45913SJeremy Kerr 	ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
3467207b96SArnd Bergmann 	if (!ctx)
3567207b96SArnd Bergmann 		goto out;
368b3d6663SArnd Bergmann 	/* Binding to physical processor deferred
378b3d6663SArnd Bergmann 	 * until spu_activate().
385473af04SMark Nutter 	 */
395473af04SMark Nutter 	spu_init_csa(&ctx->csa);
405473af04SMark Nutter 	if (!ctx->csa.lscsa) {
415473af04SMark Nutter 		goto out_free;
425473af04SMark Nutter 	}
4367207b96SArnd Bergmann 	spin_lock_init(&ctx->mmio_lock);
4443c2bbd9SChristoph Hellwig 	spin_lock_init(&ctx->mapping_lock);
4567207b96SArnd Bergmann 	kref_init(&ctx->kref);
46650f8b02SChristoph Hellwig 	mutex_init(&ctx->state_mutex);
475ef8224aSArnd Bergmann 	init_MUTEX(&ctx->run_sema);
488b3d6663SArnd Bergmann 	init_waitqueue_head(&ctx->ibox_wq);
498b3d6663SArnd Bergmann 	init_waitqueue_head(&ctx->wbox_wq);
505110459fSArnd Bergmann 	init_waitqueue_head(&ctx->stop_wq);
51a33a7d73SArnd Bergmann 	init_waitqueue_head(&ctx->mfc_wq);
528b3d6663SArnd Bergmann 	ctx->state = SPU_STATE_SAVED;
538b3d6663SArnd Bergmann 	ctx->ops = &spu_backing_ops;
548b3d6663SArnd Bergmann 	ctx->owner = get_task_mm(current);
55a475c2f4SChristoph Hellwig 	INIT_LIST_HEAD(&ctx->rq);
566263203eSArnd Bergmann 	if (gang)
576263203eSArnd Bergmann 		spu_gang_add_ctx(gang, ctx);
5852f04fcfSChristoph Hellwig 	ctx->rt_priority = current->rt_priority;
592eb1b120SChristoph Hellwig 	ctx->policy = current->policy;
608389998aSChristoph Hellwig 	ctx->prio = current->prio;
612eb1b120SChristoph Hellwig 	INIT_DELAYED_WORK(&ctx->sched_work, spu_sched_tick);
6267207b96SArnd Bergmann 	goto out;
6367207b96SArnd Bergmann out_free:
6467207b96SArnd Bergmann 	kfree(ctx);
6567207b96SArnd Bergmann 	ctx = NULL;
6667207b96SArnd Bergmann out:
6767207b96SArnd Bergmann 	return ctx;
6867207b96SArnd Bergmann }
6967207b96SArnd Bergmann 
7067207b96SArnd Bergmann void destroy_spu_context(struct kref *kref)
7167207b96SArnd Bergmann {
7267207b96SArnd Bergmann 	struct spu_context *ctx;
7367207b96SArnd Bergmann 	ctx = container_of(kref, struct spu_context, kref);
74650f8b02SChristoph Hellwig 	mutex_lock(&ctx->state_mutex);
758b3d6663SArnd Bergmann 	spu_deactivate(ctx);
76650f8b02SChristoph Hellwig 	mutex_unlock(&ctx->state_mutex);
775473af04SMark Nutter 	spu_fini_csa(&ctx->csa);
786263203eSArnd Bergmann 	if (ctx->gang)
796263203eSArnd Bergmann 		spu_gang_remove_ctx(ctx->gang, ctx);
80a475c2f4SChristoph Hellwig 	BUG_ON(!list_empty(&ctx->rq));
8167207b96SArnd Bergmann 	kfree(ctx);
8267207b96SArnd Bergmann }
8367207b96SArnd Bergmann 
8467207b96SArnd Bergmann struct spu_context * get_spu_context(struct spu_context *ctx)
8567207b96SArnd Bergmann {
8667207b96SArnd Bergmann 	kref_get(&ctx->kref);
8767207b96SArnd Bergmann 	return ctx;
8867207b96SArnd Bergmann }
8967207b96SArnd Bergmann 
9067207b96SArnd Bergmann int put_spu_context(struct spu_context *ctx)
9167207b96SArnd Bergmann {
9267207b96SArnd Bergmann 	return kref_put(&ctx->kref, &destroy_spu_context);
9367207b96SArnd Bergmann }
9467207b96SArnd Bergmann 
958b3d6663SArnd Bergmann /* give up the mm reference when the context is about to be destroyed */
968b3d6663SArnd Bergmann void spu_forget(struct spu_context *ctx)
978b3d6663SArnd Bergmann {
988b3d6663SArnd Bergmann 	struct mm_struct *mm;
998b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
1008b3d6663SArnd Bergmann 	mm = ctx->owner;
1018b3d6663SArnd Bergmann 	ctx->owner = NULL;
1028b3d6663SArnd Bergmann 	mmput(mm);
1038b3d6663SArnd Bergmann 	spu_release(ctx);
1048b3d6663SArnd Bergmann }
10567207b96SArnd Bergmann 
1065110459fSArnd Bergmann void spu_unmap_mappings(struct spu_context *ctx)
1078b3d6663SArnd Bergmann {
1086df10a82SMark Nutter 	if (ctx->local_store)
1098b3d6663SArnd Bergmann 		unmap_mapping_range(ctx->local_store, 0, LS_SIZE, 1);
1106df10a82SMark Nutter 	if (ctx->mfc)
11117e0e270SBenjamin Herrenschmidt 		unmap_mapping_range(ctx->mfc, 0, 0x1000, 1);
1126df10a82SMark Nutter 	if (ctx->cntl)
11317e0e270SBenjamin Herrenschmidt 		unmap_mapping_range(ctx->cntl, 0, 0x1000, 1);
1146df10a82SMark Nutter 	if (ctx->signal1)
11517e0e270SBenjamin Herrenschmidt 		unmap_mapping_range(ctx->signal1, 0, PAGE_SIZE, 1);
1166df10a82SMark Nutter 	if (ctx->signal2)
11717e0e270SBenjamin Herrenschmidt 		unmap_mapping_range(ctx->signal2, 0, PAGE_SIZE, 1);
11817e0e270SBenjamin Herrenschmidt 	if (ctx->mss)
11917e0e270SBenjamin Herrenschmidt 		unmap_mapping_range(ctx->mss, 0, 0x1000, 1);
12017e0e270SBenjamin Herrenschmidt 	if (ctx->psmap)
12117e0e270SBenjamin Herrenschmidt 		unmap_mapping_range(ctx->psmap, 0, 0x20000, 1);
1228b3d6663SArnd Bergmann }
1238b3d6663SArnd Bergmann 
1246a0641e5SChristoph Hellwig /**
1256a0641e5SChristoph Hellwig  * spu_acquire_exclusive - lock spu contex and protect against userspace access
1266a0641e5SChristoph Hellwig  * @ctx:	spu contex to lock
1276a0641e5SChristoph Hellwig  *
1286a0641e5SChristoph Hellwig  * Note:
1296a0641e5SChristoph Hellwig  *	Returns 0 and with the context locked on success
1306a0641e5SChristoph Hellwig  *	Returns negative error and with the context _unlocked_ on failure.
1316a0641e5SChristoph Hellwig  */
132099814bbSJeremy Kerr int spu_acquire_exclusive(struct spu_context *ctx)
133099814bbSJeremy Kerr {
1346a0641e5SChristoph Hellwig 	int ret = -EINVAL;
135099814bbSJeremy Kerr 
1366a0641e5SChristoph Hellwig 	spu_acquire(ctx);
1376a0641e5SChristoph Hellwig 	/*
1386a0641e5SChristoph Hellwig 	 * Context is about to be freed, so we can't acquire it anymore.
1396a0641e5SChristoph Hellwig 	 */
1406a0641e5SChristoph Hellwig 	if (!ctx->owner)
1416a0641e5SChristoph Hellwig 		goto out_unlock;
142099814bbSJeremy Kerr 
143099814bbSJeremy Kerr 	if (ctx->state == SPU_STATE_SAVED) {
144099814bbSJeremy Kerr 		ret = spu_activate(ctx, 0);
145099814bbSJeremy Kerr 		if (ret)
1466a0641e5SChristoph Hellwig 			goto out_unlock;
147099814bbSJeremy Kerr 	} else {
1486a0641e5SChristoph Hellwig 		/*
1496a0641e5SChristoph Hellwig 		 * We need to exclude userspace access to the context.
1506a0641e5SChristoph Hellwig 		 *
1516a0641e5SChristoph Hellwig 		 * To protect against memory access we invalidate all ptes
1526a0641e5SChristoph Hellwig 		 * and make sure the pagefault handlers block on the mutex.
1536a0641e5SChristoph Hellwig 		 */
154099814bbSJeremy Kerr 		spu_unmap_mappings(ctx);
155099814bbSJeremy Kerr 	}
156099814bbSJeremy Kerr 
1576a0641e5SChristoph Hellwig 	return 0;
1586a0641e5SChristoph Hellwig 
1596a0641e5SChristoph Hellwig  out_unlock:
1606a0641e5SChristoph Hellwig 	spu_release(ctx);
161099814bbSJeremy Kerr 	return ret;
162099814bbSJeremy Kerr }
163099814bbSJeremy Kerr 
1646a0641e5SChristoph Hellwig /**
1656a0641e5SChristoph Hellwig  * spu_acquire_runnable - lock spu contex and make sure it is in runnable state
1666a0641e5SChristoph Hellwig  * @ctx:	spu contex to lock
1676a0641e5SChristoph Hellwig  *
1686a0641e5SChristoph Hellwig  * Note:
1696a0641e5SChristoph Hellwig  *	Returns 0 and with the context locked on success
1706a0641e5SChristoph Hellwig  *	Returns negative error and with the context _unlocked_ on failure.
1716a0641e5SChristoph Hellwig  */
17226bec673SChristoph Hellwig int spu_acquire_runnable(struct spu_context *ctx, unsigned long flags)
1738b3d6663SArnd Bergmann {
1746a0641e5SChristoph Hellwig 	int ret = -EINVAL;
1758b3d6663SArnd Bergmann 
1766a0641e5SChristoph Hellwig 	spu_acquire(ctx);
1778b3d6663SArnd Bergmann 	if (ctx->state == SPU_STATE_SAVED) {
1786a0641e5SChristoph Hellwig 		/*
1796a0641e5SChristoph Hellwig 		 * Context is about to be freed, so we can't acquire it anymore.
1806a0641e5SChristoph Hellwig 		 */
1816a0641e5SChristoph Hellwig 		if (!ctx->owner)
1826a0641e5SChristoph Hellwig 			goto out_unlock;
18326bec673SChristoph Hellwig 		ret = spu_activate(ctx, flags);
1848b3d6663SArnd Bergmann 		if (ret)
1856a0641e5SChristoph Hellwig 			goto out_unlock;
1868389998aSChristoph Hellwig 	}
1878b3d6663SArnd Bergmann 
1886a0641e5SChristoph Hellwig 	return 0;
1898b3d6663SArnd Bergmann 
1906a0641e5SChristoph Hellwig  out_unlock:
1916a0641e5SChristoph Hellwig 	spu_release(ctx);
1928b3d6663SArnd Bergmann 	return ret;
1938b3d6663SArnd Bergmann }
1948b3d6663SArnd Bergmann 
1956a0641e5SChristoph Hellwig /**
1966a0641e5SChristoph Hellwig  * spu_acquire_saved - lock spu contex and make sure it is in saved state
1976a0641e5SChristoph Hellwig  * @ctx:	spu contex to lock
1986a0641e5SChristoph Hellwig  */
1998b3d6663SArnd Bergmann void spu_acquire_saved(struct spu_context *ctx)
2008b3d6663SArnd Bergmann {
2016a0641e5SChristoph Hellwig 	spu_acquire(ctx);
2026a0641e5SChristoph Hellwig 	if (ctx->state != SPU_STATE_SAVED)
2038b3d6663SArnd Bergmann 		spu_deactivate(ctx);
2048b3d6663SArnd Bergmann }
205