Commit 8b3d6663 authored by Arnd Bergmann's avatar Arnd Bergmann Committed by Paul Mackerras

[PATCH] spufs: cooperative scheduler support

This adds a scheduler for SPUs to make it possible to use
more logical SPUs than physical ones are present in the
system.

Currently, there is no support for preempting a running
SPU thread, they have to leave the SPU by either triggering
an event on the SPU that causes it to return to the
owning thread or by sending a signal to it.

This patch also adds operations that enable accessing an SPU
in either runnable or saved state. We use an RW semaphore
to protect the state of the SPU from changing underneath
us, while we are holding it readable. In order to change
the state, it is acquired writeable and a context save
or restore is executed before downgrading the semaphore
to read-only.

From: Mark Nutter <mnutter@us.ibm.com>,
      Uli Weigand <Ulrich.Weigand@de.ibm.com>
Signed-off-by: default avatarArnd Bergmann <arndb@de.ibm.com>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent 05b84117
...@@ -68,6 +68,77 @@ void cell_show_cpuinfo(struct seq_file *m) ...@@ -68,6 +68,77 @@ void cell_show_cpuinfo(struct seq_file *m)
of_node_put(root); of_node_put(root);
} }
#ifdef CONFIG_SPARSEMEM
static int __init find_spu_node_id(struct device_node *spe)
{
unsigned int *id;
#ifdef CONFIG_NUMA
struct device_node *cpu;
cpu = spe->parent->parent;
id = (unsigned int *)get_property(cpu, "node-id", NULL);
#else
id = NULL;
#endif
return id ? *id : 0;
}
static void __init cell_spuprop_present(struct device_node *spe,
const char *prop, int early)
{
struct address_prop {
unsigned long address;
unsigned int len;
} __attribute__((packed)) *p;
int proplen;
unsigned long start_pfn, end_pfn, pfn;
int node_id;
p = (void*)get_property(spe, prop, &proplen);
WARN_ON(proplen != sizeof (*p));
node_id = find_spu_node_id(spe);
start_pfn = p->address >> PAGE_SHIFT;
end_pfn = (p->address + p->len + PAGE_SIZE - 1) >> PAGE_SHIFT;
/* We need to call memory_present *before* the call to sparse_init,
but we can initialize the page structs only *after* that call.
Thus, we're being called twice. */
if (early)
memory_present(node_id, start_pfn, end_pfn);
else {
/* As the pages backing SPU LS and I/O are outside the range
of regular memory, their page structs were not initialized
by free_area_init. Do it here instead. */
for (pfn = start_pfn; pfn < end_pfn; pfn++) {
struct page *page = pfn_to_page(pfn);
set_page_links(page, ZONE_DMA, node_id, pfn);
set_page_count(page, 0);
reset_page_mapcount(page);
SetPageReserved(page);
INIT_LIST_HEAD(&page->lru);
}
}
}
static void __init cell_spumem_init(int early)
{
struct device_node *node;
for (node = of_find_node_by_type(NULL, "spe");
node; node = of_find_node_by_type(node, "spe")) {
cell_spuprop_present(node, "local-store", early);
cell_spuprop_present(node, "problem", early);
cell_spuprop_present(node, "priv1", early);
cell_spuprop_present(node, "priv2", early);
}
}
#else
static void __init cell_spumem_init(int early)
{
}
#endif
static void cell_progress(char *s, unsigned short hex) static void cell_progress(char *s, unsigned short hex)
{ {
printk("*** %04x : %s\n", hex, s ? s : ""); printk("*** %04x : %s\n", hex, s ? s : "");
...@@ -99,6 +170,8 @@ static void __init cell_setup_arch(void) ...@@ -99,6 +170,8 @@ static void __init cell_setup_arch(void)
#endif #endif
mmio_nvram_init(); mmio_nvram_init();
cell_spumem_init(0);
} }
/* /*
...@@ -114,6 +187,8 @@ static void __init cell_init_early(void) ...@@ -114,6 +187,8 @@ static void __init cell_init_early(void)
ppc64_interrupt_controller = IC_CELL_PIC; ppc64_interrupt_controller = IC_CELL_PIC;
cell_spumem_init(1);
DBG(" <- cell_init_early()\n"); DBG(" <- cell_init_early()\n");
} }
......
...@@ -69,51 +69,49 @@ static void spu_restart_dma(struct spu *spu) ...@@ -69,51 +69,49 @@ static void spu_restart_dma(struct spu *spu)
static int __spu_trap_data_seg(struct spu *spu, unsigned long ea) static int __spu_trap_data_seg(struct spu *spu, unsigned long ea)
{ {
struct spu_priv2 __iomem *priv2; struct spu_priv2 __iomem *priv2 = spu->priv2;
struct mm_struct *mm; struct mm_struct *mm = spu->mm;
u64 esid, vsid;
pr_debug("%s\n", __FUNCTION__); pr_debug("%s\n", __FUNCTION__);
if (test_bit(SPU_CONTEXT_SWITCH_ACTIVE_nr, &spu->flags)) { if (test_bit(SPU_CONTEXT_SWITCH_ACTIVE_nr, &spu->flags)) {
/* SLBs are pre-loaded for context switch, so
* we should never get here!
*/
printk("%s: invalid access during switch!\n", __func__); printk("%s: invalid access during switch!\n", __func__);
return 1; return 1;
} }
if (!mm || (REGION_ID(ea) != USER_REGION_ID)) {
if (REGION_ID(ea) != USER_REGION_ID) { /* Future: support kernel segments so that drivers
* can use SPUs.
*/
pr_debug("invalid region access at %016lx\n", ea); pr_debug("invalid region access at %016lx\n", ea);
return 1; return 1;
} }
priv2 = spu->priv2; esid = (ea & ESID_MASK) | SLB_ESID_V;
mm = spu->mm; vsid = (get_vsid(mm->context.id, ea) << SLB_VSID_SHIFT) | SLB_VSID_USER;
if (in_hugepage_area(mm->context, ea))
vsid |= SLB_VSID_L;
out_be64(&priv2->slb_index_W, spu->slb_replace);
out_be64(&priv2->slb_vsid_RW, vsid);
out_be64(&priv2->slb_esid_RW, esid);
spu->slb_replace++;
if (spu->slb_replace >= 8) if (spu->slb_replace >= 8)
spu->slb_replace = 0; spu->slb_replace = 0;
out_be64(&priv2->slb_index_W, spu->slb_replace);
out_be64(&priv2->slb_vsid_RW,
(get_vsid(mm->context.id, ea) << SLB_VSID_SHIFT)
| SLB_VSID_USER);
out_be64(&priv2->slb_esid_RW, (ea & ESID_MASK) | SLB_ESID_V);
spu_restart_dma(spu); spu_restart_dma(spu);
pr_debug("set slb %d context %lx, ea %016lx, vsid %016lx, esid %016lx\n",
spu->slb_replace, mm->context.id, ea,
(get_vsid(mm->context.id, ea) << SLB_VSID_SHIFT)| SLB_VSID_USER,
(ea & ESID_MASK) | SLB_ESID_V);
return 0; return 0;
} }
extern int hash_page(unsigned long ea, unsigned long access, unsigned long trap); //XXX extern int hash_page(unsigned long ea, unsigned long access, unsigned long trap); //XXX
static int __spu_trap_data_map(struct spu *spu, unsigned long ea) static int __spu_trap_data_map(struct spu *spu, unsigned long ea, u64 dsisr)
{ {
unsigned long dsisr;
struct spu_priv1 __iomem *priv1;
pr_debug("%s\n", __FUNCTION__); pr_debug("%s\n", __FUNCTION__);
priv1 = spu->priv1;
dsisr = in_be64(&priv1->mfc_dsisr_RW);
/* Handle kernel space hash faults immediately. /* Handle kernel space hash faults immediately.
User hash faults need to be deferred to process context. */ User hash faults need to be deferred to process context. */
...@@ -129,14 +127,17 @@ static int __spu_trap_data_map(struct spu *spu, unsigned long ea) ...@@ -129,14 +127,17 @@ static int __spu_trap_data_map(struct spu *spu, unsigned long ea)
return 1; return 1;
} }
spu->dar = ea;
spu->dsisr = dsisr;
mb();
wake_up(&spu->stop_wq); wake_up(&spu->stop_wq);
return 0; return 0;
} }
static int __spu_trap_mailbox(struct spu *spu) static int __spu_trap_mailbox(struct spu *spu)
{ {
wake_up_all(&spu->ibox_wq); if (spu->ibox_callback)
kill_fasync(&spu->ibox_fasync, SIGIO, POLLIN); spu->ibox_callback(spu);
/* atomically disable SPU mailbox interrupts */ /* atomically disable SPU mailbox interrupts */
spin_lock(&spu->register_lock); spin_lock(&spu->register_lock);
...@@ -171,8 +172,8 @@ static int __spu_trap_tag_group(struct spu *spu) ...@@ -171,8 +172,8 @@ static int __spu_trap_tag_group(struct spu *spu)
static int __spu_trap_spubox(struct spu *spu) static int __spu_trap_spubox(struct spu *spu)
{ {
wake_up_all(&spu->wbox_wq); if (spu->wbox_callback)
kill_fasync(&spu->wbox_fasync, SIGIO, POLLOUT); spu->wbox_callback(spu);
/* atomically disable SPU mailbox interrupts */ /* atomically disable SPU mailbox interrupts */
spin_lock(&spu->register_lock); spin_lock(&spu->register_lock);
...@@ -220,17 +221,25 @@ static irqreturn_t ...@@ -220,17 +221,25 @@ static irqreturn_t
spu_irq_class_1(int irq, void *data, struct pt_regs *regs) spu_irq_class_1(int irq, void *data, struct pt_regs *regs)
{ {
struct spu *spu; struct spu *spu;
unsigned long stat, dar; unsigned long stat, mask, dar, dsisr;
spu = data; spu = data;
stat = in_be64(&spu->priv1->int_stat_class1_RW);
/* atomically read & clear class1 status. */
spin_lock(&spu->register_lock);
mask = in_be64(&spu->priv1->int_mask_class1_RW);
stat = in_be64(&spu->priv1->int_stat_class1_RW) & mask;
dar = in_be64(&spu->priv1->mfc_dar_RW); dar = in_be64(&spu->priv1->mfc_dar_RW);
dsisr = in_be64(&spu->priv1->mfc_dsisr_RW);
out_be64(&spu->priv1->mfc_dsisr_RW, 0UL);
out_be64(&spu->priv1->int_stat_class1_RW, stat);
spin_unlock(&spu->register_lock);
if (stat & 1) /* segment fault */ if (stat & 1) /* segment fault */
__spu_trap_data_seg(spu, dar); __spu_trap_data_seg(spu, dar);
if (stat & 2) { /* mapping fault */ if (stat & 2) { /* mapping fault */
__spu_trap_data_map(spu, dar); __spu_trap_data_map(spu, dar, dsisr);
} }
if (stat & 4) /* ls compare & suspend on get */ if (stat & 4) /* ls compare & suspend on get */
...@@ -239,7 +248,6 @@ spu_irq_class_1(int irq, void *data, struct pt_regs *regs) ...@@ -239,7 +248,6 @@ spu_irq_class_1(int irq, void *data, struct pt_regs *regs)
if (stat & 8) /* ls compare & suspend on put */ if (stat & 8) /* ls compare & suspend on put */
; ;
out_be64(&spu->priv1->int_stat_class1_RW, stat);
return stat ? IRQ_HANDLED : IRQ_NONE; return stat ? IRQ_HANDLED : IRQ_NONE;
} }
...@@ -396,8 +404,6 @@ EXPORT_SYMBOL(spu_alloc); ...@@ -396,8 +404,6 @@ EXPORT_SYMBOL(spu_alloc);
void spu_free(struct spu *spu) void spu_free(struct spu *spu)
{ {
down(&spu_mutex); down(&spu_mutex);
spu->ibox_fasync = NULL;
spu->wbox_fasync = NULL;
list_add_tail(&spu->list, &spu_list); list_add_tail(&spu->list, &spu_list);
up(&spu_mutex); up(&spu_mutex);
} }
...@@ -405,15 +411,13 @@ EXPORT_SYMBOL(spu_free); ...@@ -405,15 +411,13 @@ EXPORT_SYMBOL(spu_free);
static int spu_handle_mm_fault(struct spu *spu) static int spu_handle_mm_fault(struct spu *spu)
{ {
struct spu_priv1 __iomem *priv1;
struct mm_struct *mm = spu->mm; struct mm_struct *mm = spu->mm;
struct vm_area_struct *vma; struct vm_area_struct *vma;
u64 ea, dsisr, is_write; u64 ea, dsisr, is_write;
int ret; int ret;
priv1 = spu->priv1; ea = spu->dar;
ea = in_be64(&priv1->mfc_dar_RW); dsisr = spu->dsisr;
dsisr = in_be64(&priv1->mfc_dsisr_RW);
#if 0 #if 0
if (!IS_VALID_EA(ea)) { if (!IS_VALID_EA(ea)) {
return -EFAULT; return -EFAULT;
...@@ -476,15 +480,14 @@ bad_area: ...@@ -476,15 +480,14 @@ bad_area:
static int spu_handle_pte_fault(struct spu *spu) static int spu_handle_pte_fault(struct spu *spu)
{ {
struct spu_priv1 __iomem *priv1;
u64 ea, dsisr, access, error = 0UL; u64 ea, dsisr, access, error = 0UL;
int ret = 0; int ret = 0;
priv1 = spu->priv1; ea = spu->dar;
ea = in_be64(&priv1->mfc_dar_RW); dsisr = spu->dsisr;
dsisr = in_be64(&priv1->mfc_dsisr_RW);
access = (_PAGE_PRESENT | _PAGE_USER);
if (dsisr & MFC_DSISR_PTE_NOT_FOUND) { if (dsisr & MFC_DSISR_PTE_NOT_FOUND) {
access = (_PAGE_PRESENT | _PAGE_USER);
access |= (dsisr & MFC_DSISR_ACCESS_PUT) ? _PAGE_RW : 0UL;
if (hash_page(ea, access, 0x300) != 0) if (hash_page(ea, access, 0x300) != 0)
error |= CLASS1_ENABLE_STORAGE_FAULT_INTR; error |= CLASS1_ENABLE_STORAGE_FAULT_INTR;
} }
...@@ -495,18 +498,33 @@ static int spu_handle_pte_fault(struct spu *spu) ...@@ -495,18 +498,33 @@ static int spu_handle_pte_fault(struct spu *spu)
else else
error &= ~CLASS1_ENABLE_STORAGE_FAULT_INTR; error &= ~CLASS1_ENABLE_STORAGE_FAULT_INTR;
} }
if (!error) spu->dar = 0UL;
spu->dsisr = 0UL;
if (!error) {
spu_restart_dma(spu); spu_restart_dma(spu);
} else {
__spu_trap_invalid_dma(spu);
}
return ret; return ret;
} }
static inline int spu_pending(struct spu *spu, u32 * stat)
{
struct spu_problem __iomem *prob = spu->problem;
u64 pte_fault;
*stat = in_be32(&prob->spu_status_R);
pte_fault = spu->dsisr &
(MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED);
return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0;
}
int spu_run(struct spu *spu) int spu_run(struct spu *spu)
{ {
struct spu_problem __iomem *prob; struct spu_problem __iomem *prob;
struct spu_priv1 __iomem *priv1; struct spu_priv1 __iomem *priv1;
struct spu_priv2 __iomem *priv2; struct spu_priv2 __iomem *priv2;
unsigned long status; u32 status;
int ret; int ret;
prob = spu->problem; prob = spu->problem;
...@@ -514,21 +532,15 @@ int spu_run(struct spu *spu) ...@@ -514,21 +532,15 @@ int spu_run(struct spu *spu)
priv2 = spu->priv2; priv2 = spu->priv2;
/* Let SPU run. */ /* Let SPU run. */
spu->mm = current->mm;
eieio(); eieio();
out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_RUNNABLE); out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_RUNNABLE);
do { do {
ret = wait_event_interruptible(spu->stop_wq, ret = wait_event_interruptible(spu->stop_wq,
(!((status = in_be32(&prob->spu_status_R)) & 0x1)) spu_pending(spu, &status));
|| (in_be64(&priv1->mfc_dsisr_RW) & MFC_DSISR_PTE_NOT_FOUND)
|| spu->class_0_pending); if (spu->dsisr &
(MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED))
if (status & SPU_STATUS_STOPPED_BY_STOP)
ret = -EAGAIN;
else if (status & SPU_STATUS_STOPPED_BY_HALT)
ret = -EIO;
else if (in_be64(&priv1->mfc_dsisr_RW) & MFC_DSISR_PTE_NOT_FOUND)
ret = spu_handle_pte_fault(spu); ret = spu_handle_pte_fault(spu);
if (spu->class_0_pending) if (spu->class_0_pending)
...@@ -537,7 +549,9 @@ int spu_run(struct spu *spu) ...@@ -537,7 +549,9 @@ int spu_run(struct spu *spu)
if (!ret && signal_pending(current)) if (!ret && signal_pending(current))
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
} while (!ret); } while (!ret && !(status &
(SPU_STATUS_STOPPED_BY_STOP |
SPU_STATUS_STOPPED_BY_HALT)));
/* Ensure SPU is stopped. */ /* Ensure SPU is stopped. */
out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_STOP); out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_STOP);
...@@ -549,8 +563,6 @@ int spu_run(struct spu *spu) ...@@ -549,8 +563,6 @@ int spu_run(struct spu *spu)
out_be64(&priv1->tlb_invalidate_entry_W, 0UL); out_be64(&priv1->tlb_invalidate_entry_W, 0UL);
eieio(); eieio();
spu->mm = NULL;
/* Check for SPU breakpoint. */ /* Check for SPU breakpoint. */
if (unlikely(current->ptrace & PT_PTRACED)) { if (unlikely(current->ptrace & PT_PTRACED)) {
status = in_be32(&prob->spu_status_R); status = in_be32(&prob->spu_status_R);
...@@ -669,19 +681,21 @@ static int __init create_spu(struct device_node *spe) ...@@ -669,19 +681,21 @@ static int __init create_spu(struct device_node *spe)
spu->stop_code = 0; spu->stop_code = 0;
spu->slb_replace = 0; spu->slb_replace = 0;
spu->mm = NULL; spu->mm = NULL;
spu->ctx = NULL;
spu->rq = NULL;
spu->pid = 0;
spu->class_0_pending = 0; spu->class_0_pending = 0;
spu->flags = 0UL; spu->flags = 0UL;
spu->dar = 0UL;
spu->dsisr = 0UL;
spin_lock_init(&spu->register_lock); spin_lock_init(&spu->register_lock);
out_be64(&spu->priv1->mfc_sdr_RW, mfspr(SPRN_SDR1)); out_be64(&spu->priv1->mfc_sdr_RW, mfspr(SPRN_SDR1));
out_be64(&spu->priv1->mfc_sr1_RW, 0x33); out_be64(&spu->priv1->mfc_sr1_RW, 0x33);
init_waitqueue_head(&spu->stop_wq); init_waitqueue_head(&spu->stop_wq);
init_waitqueue_head(&spu->wbox_wq); spu->ibox_callback = NULL;
init_waitqueue_head(&spu->ibox_wq); spu->wbox_callback = NULL;
spu->ibox_fasync = NULL;
spu->wbox_fasync = NULL;
down(&spu_mutex); down(&spu_mutex);
spu->number = number++; spu->number = number++;
......
obj-$(CONFIG_SPU_FS) += spufs.o obj-$(CONFIG_SPU_FS) += spufs.o
spufs-y += inode.o file.o context.o switch.o syscalls.o spufs-y += inode.o file.o context.o switch.o syscalls.o
spufs-y += sched.o backing_ops.o hw_ops.o
# Rules to build switch.o with the help of SPU tool chain # Rules to build switch.o with the help of SPU tool chain
SPU_CROSS := spu- SPU_CROSS := spu-
......
/* backing_ops.c - query/set operations on saved SPU context.
*
* Copyright (C) IBM 2005
* Author: Mark Nutter <mnutter@us.ibm.com>
*
* These register operations allow SPUFS to operate on saved
* SPU contexts rather than hardware.
*
* 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/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <asm/io.h>
#include <asm/spu.h>
#include <asm/spu_csa.h>
#include <asm/mmu_context.h>
#include "spufs.h"
/*
* Reads/writes to various problem and priv2 registers require
* state changes, i.e. generate SPU events, modify channel
* counts, etc.
*/
static void gen_spu_event(struct spu_context *ctx, u32 event)
{
u64 ch0_cnt;
u64 ch0_data;
u64 ch1_data;
ch0_cnt = ctx->csa.spu_chnlcnt_RW[0];
ch0_data = ctx->csa.spu_chnldata_RW[0];
ch1_data = ctx->csa.spu_chnldata_RW[1];
ctx->csa.spu_chnldata_RW[0] |= event;
if ((ch0_cnt == 0) && !(ch0_data & event) && (ch1_data & event)) {
ctx->csa.spu_chnlcnt_RW[0] = 1;
}
}
static int spu_backing_mbox_read(struct spu_context *ctx, u32 * data)
{
u32 mbox_stat;
int ret = 0;
spin_lock(&ctx->csa.register_lock);
mbox_stat = ctx->csa.prob.mb_stat_R;
if (mbox_stat & 0x0000ff) {
/* Read the first available word.
* Implementation note: the depth
* of pu_mb_R is currently 1.
*/
*data = ctx->csa.prob.pu_mb_R;
ctx->csa.prob.mb_stat_R &= ~(0x0000ff);
ctx->csa.spu_chnlcnt_RW[28] = 1;
gen_spu_event(ctx, MFC_PU_MAILBOX_AVAILABLE_EVENT);
ret = 4;
}
spin_unlock(&ctx->csa.register_lock);
return ret;
}
static u32 spu_backing_mbox_stat_read(struct spu_context *ctx)
{
return ctx->csa.prob.mb_stat_R;
}
static int spu_backing_ibox_read(struct spu_context *ctx, u32 * data)
{
int ret;
spin_lock(&ctx->csa.register_lock);
if (ctx->csa.prob.mb_stat_R & 0xff0000) {
/* Read the first available word.
* Implementation note: the depth
* of puint_mb_R is currently 1.
*/
*data = ctx->csa.priv2.puint_mb_R;
ctx->csa.prob.mb_stat_R &= ~(0xff0000);
ctx->csa.spu_chnlcnt_RW[30] = 1;
gen_spu_event(ctx, MFC_PU_INT_MAILBOX_AVAILABLE_EVENT);
ret = 4;
} else {
/* make sure we get woken up by the interrupt */
ctx->csa.priv1.int_mask_class2_RW |= 0x1UL;
ret = 0;
}
spin_unlock(&ctx->csa.register_lock);
return ret;
}
static int spu_backing_wbox_write(struct spu_context *ctx, u32 data)
{
int ret;
spin_lock(&ctx->csa.register_lock);
if ((ctx->csa.prob.mb_stat_R) & 0x00ff00) {
int slot = ctx->csa.spu_chnlcnt_RW[29];
int avail = (ctx->csa.prob.mb_stat_R & 0x00ff00) >> 8;
/* We have space to write wbox_data.
* Implementation note: the depth
* of spu_mb_W is currently 4.
*/
BUG_ON(avail != (4 - slot));
ctx->csa.spu_mailbox_data[slot] = data;
ctx->csa.spu_chnlcnt_RW[29] = ++slot;
ctx->csa.prob.mb_stat_R = (((4 - slot) & 0xff) << 8);
gen_spu_event(ctx, MFC_SPU_MAILBOX_WRITTEN_EVENT);
ret = 4;
} else {
/* make sure we get woken up by the interrupt when space
becomes available */
ctx->csa.priv1.int_mask_class2_RW |= 0x10;
ret = 0;
}
spin_unlock(&ctx->csa.register_lock);
return ret;
}
static u32 spu_backing_signal1_read(struct spu_context *ctx)
{
return ctx->csa.spu_chnldata_RW[3];
}
static void spu_backing_signal1_write(struct spu_context *ctx, u32 data)
{
spin_lock(&ctx->csa.register_lock);
if (ctx->csa.priv2.spu_cfg_RW & 0x1)
ctx->csa.spu_chnldata_RW[3] |= data;
else
ctx->csa.spu_chnldata_RW[3] = data;
ctx->csa.spu_chnlcnt_RW[3] = 1;
gen_spu_event(ctx, MFC_SIGNAL_1_EVENT);
spin_unlock(&ctx->csa.register_lock);
}
static u32 spu_backing_signal2_read(struct spu_context *ctx)
{
return ctx->csa.spu_chnldata_RW[4];
}
static void spu_backing_signal2_write(struct spu_context *ctx, u32 data)
{
spin_lock(&ctx->csa.register_lock);
if (ctx->csa.priv2.spu_cfg_RW & 0x2)
ctx->csa.spu_chnldata_RW[4] |= data;
else
ctx->csa.spu_chnldata_RW[4] = data;
ctx->csa.spu_chnlcnt_RW[4] = 1;
gen_spu_event(ctx, MFC_SIGNAL_2_EVENT);
spin_unlock(&ctx->csa.register_lock);
}
static void spu_backing_signal1_type_set(struct spu_context *ctx, u64 val)
{
u64 tmp;
spin_lock(&ctx->csa.register_lock);
tmp = ctx->csa.priv2.spu_cfg_RW;
if (val)
tmp |= 1;
else
tmp &= ~1;
ctx->csa.priv2.spu_cfg_RW = tmp;
spin_unlock(&ctx->csa.register_lock);
}
static u64 spu_backing_signal1_type_get(struct spu_context *ctx)
{
return ((ctx->csa.priv2.spu_cfg_RW & 1) != 0);
}
static void spu_backing_signal2_type_set(struct spu_context *ctx, u64 val)
{
u64 tmp;
spin_lock(&ctx->csa.register_lock);
tmp = ctx->csa.priv2.spu_cfg_RW;
if (val)
tmp |= 2;
else
tmp &= ~2;
ctx->csa.priv2.spu_cfg_RW = tmp;
spin_unlock(&ctx->csa.register_lock);
}
static u64 spu_backing_signal2_type_get(struct spu_context *ctx)
{
return ((ctx->csa.priv2.spu_cfg_RW & 2) != 0);
}
static u32 spu_backing_npc_read(struct spu_context *ctx)
{
return ctx->csa.prob.spu_npc_RW;
}
static void spu_backing_npc_write(struct spu_context *ctx, u32 val)
{
ctx->csa.prob.spu_npc_RW = val;
}
static u32 spu_backing_status_read(struct spu_context *ctx)
{
return ctx->csa.prob.spu_status_R;
}
static char *spu_backing_get_ls(struct spu_context *ctx)
{
return ctx->csa.lscsa->ls;
}
struct spu_context_ops spu_backing_ops = {
.mbox_read = spu_backing_mbox_read,
.mbox_stat_read = spu_backing_mbox_stat_read,
.ibox_read = spu_backing_ibox_read,
.wbox_write = spu_backing_wbox_write,
.signal1_read = spu_backing_signal1_read,
.signal1_write = spu_backing_signal1_write,
.signal2_read = spu_backing_signal2_read,
.signal2_write = spu_backing_signal2_write,
.signal1_type_set = spu_backing_signal1_type_set,
.signal1_type_get = spu_backing_signal1_type_get,
.signal2_type_set = spu_backing_signal2_type_set,
.signal2_type_get = spu_backing_signal2_type_get,
.npc_read = spu_backing_npc_read,
.npc_write = spu_backing_npc_write,
.status_read = spu_backing_status_read,
.get_ls = spu_backing_get_ls,
};
...@@ -20,39 +20,38 @@ ...@@ -20,39 +20,38 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/spu.h> #include <asm/spu.h>
#include <asm/spu_csa.h> #include <asm/spu_csa.h>
#include "spufs.h" #include "spufs.h"
struct spu_context *alloc_spu_context(void) struct spu_context *alloc_spu_context(struct address_space *local_store)
{ {
struct spu_context *ctx; struct spu_context *ctx;
ctx = kmalloc(sizeof *ctx, GFP_KERNEL); ctx = kmalloc(sizeof *ctx, GFP_KERNEL);
if (!ctx) if (!ctx)
goto out; goto out;
/* Future enhancement: do not call spu_alloc() /* Binding to physical processor deferred
* here. This step should be deferred until * until spu_activate().
* spu_run()!!
*
* More work needs to be done to read(),
* write(), mmap(), etc., so that operations
* are performed on CSA when the context is
* not currently being run. In this way we
* can support arbitrarily large number of
* entries in /spu, allow state queries, etc.
*/ */
ctx->spu = spu_alloc();
if (!ctx->spu)
goto out_free;
spu_init_csa(&ctx->csa); spu_init_csa(&ctx->csa);
if (!ctx->csa.lscsa) { if (!ctx->csa.lscsa) {
spu_free(ctx->spu);
goto out_free; goto out_free;
} }
init_rwsem(&ctx->backing_sema);
spin_lock_init(&ctx->mmio_lock); spin_lock_init(&ctx->mmio_lock);
kref_init(&ctx->kref); kref_init(&ctx->kref);
init_rwsem(&ctx->state_sema);
init_waitqueue_head(&ctx->ibox_wq);
init_waitqueue_head(&ctx->wbox_wq);
ctx->ibox_fasync = NULL;
ctx->wbox_fasync = NULL;
ctx->state = SPU_STATE_SAVED;
ctx->local_store = local_store;
ctx->spu = NULL;
ctx->ops = &spu_backing_ops;
ctx->owner = get_task_mm(current);
goto out; goto out;
out_free: out_free:
kfree(ctx); kfree(ctx);
...@@ -65,8 +64,11 @@ void destroy_spu_context(struct kref *kref) ...@@ -65,8 +64,11 @@ void destroy_spu_context(struct kref *kref)
{ {
struct spu_context *ctx; struct spu_context *ctx;
ctx = container_of(kref, struct spu_context, kref); ctx = container_of(kref, struct spu_context, kref);
if (ctx->spu) down_write(&ctx->state_sema);
spu_free(ctx->spu); spu_deactivate(ctx);
ctx->ibox_fasync = NULL;
ctx->wbox_fasync = NULL;
up_write(&ctx->state_sema);
spu_fini_csa(&ctx->csa); spu_fini_csa(&ctx->csa);
kfree(ctx); kfree(ctx);
} }
...@@ -82,4 +84,80 @@ int put_spu_context(struct spu_context *ctx) ...@@ -82,4 +84,80 @@ int put_spu_context(struct spu_context *ctx)
return kref_put(&ctx->kref, &destroy_spu_context); return kref_put(&ctx->kref, &destroy_spu_context);
} }
/* give up the mm reference when the context is about to be destroyed */
void spu_forget(struct spu_context *ctx)
{
struct mm_struct *mm;
spu_acquire_saved(ctx);
mm = ctx->owner;
ctx->owner = NULL;
mmput(mm);
spu_release(ctx);
}
void spu_acquire(struct spu_context *ctx)
{
down_read(&ctx->state_sema);
}
void spu_release(struct spu_context *ctx)
{
up_read(&ctx->state_sema);
}
static void spu_unmap_mappings(struct spu_context *ctx)
{
unmap_mapping_range(ctx->local_store, 0, LS_SIZE, 1);
}
int spu_acquire_runnable(struct spu_context *ctx)
{
int ret = 0;
down_read(&ctx->state_sema);
if (ctx->state == SPU_STATE_RUNNABLE)
return 0;
/* ctx is about to be freed, can't acquire any more */
if (!ctx->owner) {
ret = -EINVAL;
goto out;
}
up_read(&ctx->state_sema);
down_write(&ctx->state_sema);
if (ctx->state == SPU_STATE_SAVED) {
spu_unmap_mappings(ctx);
ret = spu_activate(ctx, 0);
ctx->state = SPU_STATE_RUNNABLE;
}
downgrade_write(&ctx->state_sema);
if (ret)
goto out;
/* On success, we return holding the lock */
return ret;
out:
/* Release here, to simplify calling code. */
up_read(&ctx->state_sema);
return ret;
}
void spu_acquire_saved(struct spu_context *ctx)
{
down_read(&ctx->state_sema);
if (ctx->state == SPU_STATE_SAVED)
return;
up_read(&ctx->state_sema);
down_write(&ctx->state_sema);
if (ctx->state == SPU_STATE_RUNNABLE) {
spu_unmap_mappings(ctx);
spu_deactivate(ctx);
ctx->state = SPU_STATE_SAVED;
}
downgrade_write(&ctx->state_sema);
}
This diff is collapsed.
/* hw_ops.c - query/set operations on active SPU context.
*
* Copyright (C) IBM 2005
* Author: Mark Nutter <mnutter@us.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/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <asm/io.h>
#include <asm/spu.h>
#include <asm/spu_csa.h>
#include <asm/mmu_context.h>
#include "spufs.h"
static int spu_hw_mbox_read(struct spu_context *ctx, u32 * data)
{
struct spu *spu = ctx->spu;
struct spu_problem __iomem *prob = spu->problem;
u32 mbox_stat;
int ret = 0;
spin_lock_irq(&spu->register_lock);
mbox_stat = in_be32(&prob->mb_stat_R);
if (mbox_stat & 0x0000ff) {
*data = in_be32(&prob->pu_mb_R);
ret = 4;
}
spin_unlock_irq(&spu->register_lock);
return ret;
}
static u32 spu_hw_mbox_stat_read(struct spu_context *ctx)
{
return in_be32(&ctx->spu->problem->mb_stat_R);
}
static int spu_hw_ibox_read(struct spu_context *ctx, u32 * data)
{
struct spu *spu = ctx->spu;
struct spu_problem __iomem *prob = spu->problem;
struct spu_priv1 __iomem *priv1 = spu->priv1;
struct spu_priv2 __iomem *priv2 = spu->priv2;
int ret;
spin_lock_irq(&spu->register_lock);
if (in_be32(&prob->mb_stat_R) & 0xff0000) {
/* read the first available word */
*data = in_be64(&priv2->puint_mb_R);
ret = 4;
} else {
/* make sure we get woken up by the interrupt */
out_be64(&priv1->int_mask_class2_RW,
in_be64(&priv1->int_mask_class2_RW) | 0x1);
ret = 0;
}
spin_unlock_irq(&spu->register_lock);
return ret;
}
static int spu_hw_wbox_write(struct spu_context *ctx, u32 data)
{
struct spu *spu = ctx->spu;
struct spu_problem __iomem *prob = spu->problem;
struct spu_priv1 __iomem *priv1 = spu->priv1;
int ret;
spin_lock_irq(&spu->register_lock);
if (in_be32(&prob->mb_stat_R) & 0x00ff00) {
/* we have space to write wbox_data to */
out_be32(&prob->spu_mb_W, data);
ret = 4;
} else {
/* make sure we get woken up by the interrupt when space
becomes available */
out_be64(&priv1->int_mask_class2_RW,
in_be64(&priv1->int_mask_class2_RW) | 0x10);
ret = 0;
}
spin_unlock_irq(&spu->register_lock);
return ret;
}
static u32 spu_hw_signal1_read(struct spu_context *ctx)
{
return in_be32(&ctx->spu->problem->signal_notify1);
}
static void spu_hw_signal1_write(struct spu_context *ctx, u32 data)
{
out_be32(&ctx->spu->problem->signal_notify1, data);
}
static u32 spu_hw_signal2_read(struct spu_context *ctx)
{
return in_be32(&ctx->spu->problem->signal_notify1);
}
static void spu_hw_signal2_write(struct spu_context *ctx, u32 data)
{
out_be32(&ctx->spu->problem->signal_notify2, data);
}
static void spu_hw_signal1_type_set(struct spu_context *ctx, u64 val)
{
struct spu *spu = ctx->spu;
struct spu_priv2 __iomem *priv2 = spu->priv2;
u64 tmp;
spin_lock_irq(&spu->register_lock);
tmp = in_be64(&priv2->spu_cfg_RW);
if (val)
tmp |= 1;
else
tmp &= ~1;
out_be64(&priv2->spu_cfg_RW, tmp);
spin_unlock_irq(&spu->register_lock);
}
static u64 spu_hw_signal1_type_get(struct spu_context *ctx)
{
return ((in_be64(&ctx->spu->priv2->spu_cfg_RW) & 1) != 0);
}
static void spu_hw_signal2_type_set(struct spu_context *ctx, u64 val)
{
struct spu *spu = ctx->spu;
struct spu_priv2 __iomem *priv2 = spu->priv2;
u64 tmp;
spin_lock_irq(&spu->register_lock);
tmp = in_be64(&priv2->spu_cfg_RW);
if (val)
tmp |= 2;
else
tmp &= ~2;
out_be64(&priv2->spu_cfg_RW, tmp);
spin_unlock_irq(&spu->register_lock);
}
static u64 spu_hw_signal2_type_get(struct spu_context *ctx)
{
return ((in_be64(&ctx->spu->priv2->spu_cfg_RW) & 2) != 0);
}
static u32 spu_hw_npc_read(struct spu_context *ctx)
{
return in_be32(&ctx->spu->problem->spu_npc_RW);
}
static void spu_hw_npc_write(struct spu_context *ctx, u32 val)
{
out_be32(&ctx->spu->problem->spu_npc_RW, val);
}
static u32 spu_hw_status_read(struct spu_context *ctx)
{
return in_be32(&ctx->spu->problem->spu_status_R);
}
static char *spu_hw_get_ls(struct spu_context *ctx)
{
return ctx->spu->local_store;
}
struct spu_context_ops spu_hw_ops = {
.mbox_read = spu_hw_mbox_read,
.mbox_stat_read = spu_hw_mbox_stat_read,
.ibox_read = spu_hw_ibox_read,
.wbox_write = spu_hw_wbox_write,
.signal1_read = spu_hw_signal1_read,
.signal1_write = spu_hw_signal1_write,
.signal2_read = spu_hw_signal2_read,
.signal2_write = spu_hw_signal2_write,
.signal1_type_set = spu_hw_signal1_type_set,
.signal1_type_get = spu_hw_signal1_type_get,
.signal2_type_set = spu_hw_signal2_type_set,
.signal2_type_get = spu_hw_signal2_type_get,
.npc_read = spu_hw_npc_read,
.npc_write = spu_hw_npc_write,
.status_read = spu_hw_status_read,
.get_ls = spu_hw_get_ls,
};
...@@ -41,24 +41,6 @@ ...@@ -41,24 +41,6 @@
static kmem_cache_t *spufs_inode_cache; static kmem_cache_t *spufs_inode_cache;
/* Information about the backing dev, same as ramfs */
#if 0
static struct backing_dev_info spufs_backing_dev_info = {
.ra_pages = 0, /* No readahead */
.capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK |
BDI_CAP_MAP_DIRECT | BDI_CAP_MAP_COPY | BDI_CAP_READ_MAP |
BDI_CAP_WRITE_MAP,
};
static struct address_space_operations spufs_aops = {
.readpage = simple_readpage,
.prepare_write = simple_prepare_write,
.commit_write = simple_commit_write,
};
#endif
/* Inode operations */
static struct inode * static struct inode *
spufs_alloc_inode(struct super_block *sb) spufs_alloc_inode(struct super_block *sb)
{ {
...@@ -111,9 +93,6 @@ spufs_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -111,9 +93,6 @@ spufs_setattr(struct dentry *dentry, struct iattr *attr)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
/* dump_stack();
pr_debug("ia_size %lld, i_size:%lld\n", attr->ia_size, inode->i_size);
*/
if ((attr->ia_valid & ATTR_SIZE) && if ((attr->ia_valid & ATTR_SIZE) &&
(attr->ia_size != inode->i_size)) (attr->ia_size != inode->i_size))
return -EINVAL; return -EINVAL;
...@@ -127,9 +106,7 @@ spufs_new_file(struct super_block *sb, struct dentry *dentry, ...@@ -127,9 +106,7 @@ spufs_new_file(struct super_block *sb, struct dentry *dentry,
struct spu_context *ctx) struct spu_context *ctx)
{ {
static struct inode_operations spufs_file_iops = { static struct inode_operations spufs_file_iops = {
.getattr = simple_getattr,
.setattr = spufs_setattr, .setattr = spufs_setattr,
.unlink = simple_unlink,
}; };
struct inode *inode; struct inode *inode;
int ret; int ret;
...@@ -183,21 +160,32 @@ out: ...@@ -183,21 +160,32 @@ out:
static int spufs_rmdir(struct inode *root, struct dentry *dir_dentry) static int spufs_rmdir(struct inode *root, struct dentry *dir_dentry)
{ {
struct dentry *dentry; struct dentry *dentry, *tmp;
struct spu_context *ctx;
int err; int err;
spin_lock(&dcache_lock);
/* remove all entries */ /* remove all entries */
err = 0; err = 0;
list_for_each_entry(dentry, &dir_dentry->d_subdirs, d_child) { list_for_each_entry_safe(dentry, tmp, &dir_dentry->d_subdirs, d_child) {
if (d_unhashed(dentry) || !dentry->d_inode) spin_lock(&dcache_lock);
continue;
atomic_dec(&dentry->d_count);
spin_lock(&dentry->d_lock); spin_lock(&dentry->d_lock);
__d_drop(dentry); if (!(d_unhashed(dentry)) && dentry->d_inode) {
spin_unlock(&dentry->d_lock); dget_locked(dentry);
__d_drop(dentry);
spin_unlock(&dentry->d_lock);
simple_unlink(dir_dentry->d_inode, dentry);
spin_unlock(&dcache_lock);
dput(dentry);
} else {
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);
}
} }
spin_unlock(&dcache_lock);
/* We have to give up the mm_struct */
ctx = SPUFS_I(dir_dentry->d_inode)->i_ctx;
spu_forget(ctx);
if (!err) { if (!err) {
shrink_dcache_parent(dir_dentry); shrink_dcache_parent(dir_dentry);
err = simple_rmdir(root, dir_dentry); err = simple_rmdir(root, dir_dentry);
...@@ -249,7 +237,7 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ...@@ -249,7 +237,7 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
inode->i_gid = dir->i_gid; inode->i_gid = dir->i_gid;
inode->i_mode &= S_ISGID; inode->i_mode &= S_ISGID;
} }
ctx = alloc_spu_context(); ctx = alloc_spu_context(inode->i_mapping);
SPUFS_I(inode)->i_ctx = ctx; SPUFS_I(inode)->i_ctx = ctx;
if (!ctx) if (!ctx)
goto out_iput; goto out_iput;
...@@ -368,7 +356,8 @@ spufs_parse_options(char *options, struct inode *root) ...@@ -368,7 +356,8 @@ spufs_parse_options(char *options, struct inode *root)
} }
static int static int
spufs_create_root(struct super_block *sb, void *data) { spufs_create_root(struct super_block *sb, void *data)
{
struct inode *inode; struct inode *inode;
int ret; int ret;
...@@ -441,6 +430,10 @@ static int spufs_init(void) ...@@ -441,6 +430,10 @@ static int spufs_init(void)
if (!spufs_inode_cache) if (!spufs_inode_cache)
goto out; goto out;
if (spu_sched_init() != 0) {
kmem_cache_destroy(spufs_inode_cache);
goto out;
}
ret = register_filesystem(&spufs_type); ret = register_filesystem(&spufs_type);
if (ret) if (ret)
goto out_cache; goto out_cache;
...@@ -459,6 +452,7 @@ module_init(spufs_init); ...@@ -459,6 +452,7 @@ module_init(spufs_init);
static void spufs_exit(void) static void spufs_exit(void)
{ {
spu_sched_exit();
unregister_spu_syscalls(&spufs_calls); unregister_spu_syscalls(&spufs_calls);
unregister_filesystem(&spufs_type); unregister_filesystem(&spufs_type);
kmem_cache_destroy(spufs_inode_cache); kmem_cache_destroy(spufs_inode_cache);
......
This diff is collapsed.
...@@ -35,15 +35,50 @@ enum { ...@@ -35,15 +35,50 @@ enum {
SPUFS_MAGIC = 0x23c9b64e, SPUFS_MAGIC = 0x23c9b64e,
}; };
struct spu_context_ops;
struct spu_context { struct spu_context {
struct spu *spu; /* pointer to a physical SPU */ struct spu *spu; /* pointer to a physical SPU */
struct spu_state csa; /* SPU context save area. */ struct spu_state csa; /* SPU context save area. */
struct rw_semaphore backing_sema; /* protects the above */
spinlock_t mmio_lock; /* protects mmio access */ spinlock_t mmio_lock; /* protects mmio access */
struct address_space *local_store;/* local store backing store */
enum { SPU_STATE_RUNNABLE, SPU_STATE_SAVED } state;
struct rw_semaphore state_sema;
struct mm_struct *owner;
struct kref kref; struct kref kref;
wait_queue_head_t ibox_wq;
wait_queue_head_t wbox_wq;
struct fasync_struct *ibox_fasync;
struct fasync_struct *wbox_fasync;
struct spu_context_ops *ops;
};
/* SPU context query/set operations. */
struct spu_context_ops {
int (*mbox_read) (struct spu_context * ctx, u32 * data);
u32(*mbox_stat_read) (struct spu_context * ctx);
int (*ibox_read) (struct spu_context * ctx, u32 * data);
int (*wbox_write) (struct spu_context * ctx, u32 data);
u32(*signal1_read) (struct spu_context * ctx);
void (*signal1_write) (struct spu_context * ctx, u32 data);
u32(*signal2_read) (struct spu_context * ctx);
void (*signal2_write) (struct spu_context * ctx, u32 data);
void (*signal1_type_set) (struct spu_context * ctx, u64 val);
u64(*signal1_type_get) (struct spu_context * ctx);
void (*signal2_type_set) (struct spu_context * ctx, u64 val);
u64(*signal2_type_get) (struct spu_context * ctx);
u32(*npc_read) (struct spu_context * ctx);
void (*npc_write) (struct spu_context * ctx, u32 data);
u32(*status_read) (struct spu_context * ctx);
char*(*get_ls) (struct spu_context * ctx);
}; };
extern struct spu_context_ops spu_hw_ops;
extern struct spu_context_ops spu_backing_ops;
struct spufs_inode_info { struct spufs_inode_info {
struct spu_context *i_ctx; struct spu_context *i_ctx;
struct inode vfs_inode; struct inode vfs_inode;
...@@ -60,14 +95,28 @@ long spufs_create_thread(struct nameidata *nd, const char *name, ...@@ -60,14 +95,28 @@ long spufs_create_thread(struct nameidata *nd, const char *name,
unsigned int flags, mode_t mode); unsigned int flags, mode_t mode);
/* context management */ /* context management */
struct spu_context * alloc_spu_context(void); struct spu_context * alloc_spu_context(struct address_space *local_store);
void destroy_spu_context(struct kref *kref); void destroy_spu_context(struct kref *kref);
struct spu_context * get_spu_context(struct spu_context *ctx); struct spu_context * get_spu_context(struct spu_context *ctx);
int put_spu_context(struct spu_context *ctx); int put_spu_context(struct spu_context *ctx);
void spu_forget(struct spu_context *ctx);
void spu_acquire(struct spu_context *ctx); void spu_acquire(struct spu_context *ctx);
void spu_release(struct spu_context *ctx); void spu_release(struct spu_context *ctx);
void spu_acquire_runnable(struct spu_context *ctx); int spu_acquire_runnable(struct spu_context *ctx);
void spu_acquire_saved(struct spu_context *ctx); void spu_acquire_saved(struct spu_context *ctx);
int spu_activate(struct spu_context *ctx, u64 flags);
void spu_deactivate(struct spu_context *ctx);
void spu_yield(struct spu_context *ctx);
int __init spu_sched_init(void);
void __exit spu_sched_exit(void);
size_t spu_wbox_write(struct spu_context *ctx, u32 data);
size_t spu_ibox_read(struct spu_context *ctx, u32 *data);
/* irq callback funcs. */
void spufs_ibox_callback(struct spu *spu);
void spufs_wbox_callback(struct spu *spu);
#endif #endif
...@@ -646,7 +646,7 @@ static inline void save_spu_mb(struct spu_state *csa, struct spu *spu) ...@@ -646,7 +646,7 @@ static inline void save_spu_mb(struct spu_state *csa, struct spu *spu)
eieio(); eieio();
csa->spu_chnlcnt_RW[29] = in_be64(&priv2->spu_chnlcnt_RW); csa->spu_chnlcnt_RW[29] = in_be64(&priv2->spu_chnlcnt_RW);
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
csa->pu_mailbox_data[i] = in_be64(&priv2->spu_chnldata_RW); csa->spu_mailbox_data[i] = in_be64(&priv2->spu_chnldata_RW);
} }
out_be64(&priv2->spu_chnlcnt_RW, 0UL); out_be64(&priv2->spu_chnlcnt_RW, 0UL);
eieio(); eieio();
...@@ -1667,7 +1667,7 @@ static inline void restore_spu_mb(struct spu_state *csa, struct spu *spu) ...@@ -1667,7 +1667,7 @@ static inline void restore_spu_mb(struct spu_state *csa, struct spu *spu)
eieio(); eieio();
out_be64(&priv2->spu_chnlcnt_RW, csa->spu_chnlcnt_RW[29]); out_be64(&priv2->spu_chnlcnt_RW, csa->spu_chnlcnt_RW[29]);
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
out_be64(&priv2->spu_chnldata_RW, csa->pu_mailbox_data[i]); out_be64(&priv2->spu_chnldata_RW, csa->spu_mailbox_data[i]);
} }
eieio(); eieio();
} }
...@@ -2079,7 +2079,10 @@ int spu_save(struct spu_state *prev, struct spu *spu) ...@@ -2079,7 +2079,10 @@ int spu_save(struct spu_state *prev, struct spu *spu)
acquire_spu_lock(spu); /* Step 1. */ acquire_spu_lock(spu); /* Step 1. */
rc = __do_spu_save(prev, spu); /* Steps 2-53. */ rc = __do_spu_save(prev, spu); /* Steps 2-53. */
release_spu_lock(spu); release_spu_lock(spu);
if (rc) {
panic("%s failed on SPU[%d], rc=%d.\n",
__func__, spu->number, rc);
}
return rc; return rc;
} }
...@@ -2098,34 +2101,31 @@ int spu_restore(struct spu_state *new, struct spu *spu) ...@@ -2098,34 +2101,31 @@ int spu_restore(struct spu_state *new, struct spu *spu)
acquire_spu_lock(spu); acquire_spu_lock(spu);
harvest(NULL, spu); harvest(NULL, spu);
spu->stop_code = 0;
spu->dar = 0;
spu->dsisr = 0;
spu->slb_replace = 0;
spu->class_0_pending = 0;
rc = __do_spu_restore(new, spu); rc = __do_spu_restore(new, spu);
release_spu_lock(spu); release_spu_lock(spu);
if (rc) {
panic("%s failed on SPU[%d] rc=%d.\n",
__func__, spu->number, rc);
}
return rc; return rc;
} }
/** /**
* spu_switch - SPU context switch (save + restore). * spu_harvest - SPU harvest (reset) operation
* @prev: pointer to SPU context save area, to be saved.
* @new: pointer to SPU context save area, to be restored.
* @spu: pointer to SPU iomem structure. * @spu: pointer to SPU iomem structure.
* *
* Perform save, then restore. Only harvest if the * Perform SPU harvest (reset) operation.
* save fails, as cleanup is otherwise not needed.
*/ */
int spu_switch(struct spu_state *prev, struct spu_state *new, struct spu *spu) void spu_harvest(struct spu *spu)
{ {
int rc; acquire_spu_lock(spu);
harvest(NULL, spu);
acquire_spu_lock(spu); /* Save, Step 1. */
rc = __do_spu_save(prev, spu); /* Save, Steps 2-53. */
if (rc != 0) {
harvest(prev, spu);
}
rc = __do_spu_restore(new, spu);
release_spu_lock(spu); release_spu_lock(spu);
return rc;
} }
static void init_prob(struct spu_state *csa) static void init_prob(struct spu_state *csa)
...@@ -2181,6 +2181,7 @@ static void init_priv2(struct spu_state *csa) ...@@ -2181,6 +2181,7 @@ static void init_priv2(struct spu_state *csa)
void spu_init_csa(struct spu_state *csa) void spu_init_csa(struct spu_state *csa)
{ {
struct spu_lscsa *lscsa; struct spu_lscsa *lscsa;
unsigned char *p;
if (!csa) if (!csa)
return; return;
...@@ -2192,6 +2193,11 @@ void spu_init_csa(struct spu_state *csa) ...@@ -2192,6 +2193,11 @@ void spu_init_csa(struct spu_state *csa)
memset(lscsa, 0, sizeof(struct spu_lscsa)); memset(lscsa, 0, sizeof(struct spu_lscsa));
csa->lscsa = lscsa; csa->lscsa = lscsa;
csa->register_lock = SPIN_LOCK_UNLOCKED;
/* Set LS pages reserved to allow for user-space mapping. */
for (p = lscsa->ls; p < lscsa->ls + LS_SIZE; p += PAGE_SIZE)
SetPageReserved(vmalloc_to_page(p));
init_prob(csa); init_prob(csa);
init_priv1(csa); init_priv1(csa);
...@@ -2200,5 +2206,10 @@ void spu_init_csa(struct spu_state *csa) ...@@ -2200,5 +2206,10 @@ void spu_init_csa(struct spu_state *csa)
void spu_fini_csa(struct spu_state *csa) void spu_fini_csa(struct spu_state *csa)
{ {
/* Clear reserved bit before vfree. */
unsigned char *p;
for (p = csa->lscsa->ls; p < csa->lscsa->ls + LS_SIZE; p += PAGE_SIZE)
ClearPageReserved(vmalloc_to_page(p));
vfree(csa->lscsa); vfree(csa->lscsa);
} }
...@@ -36,7 +36,7 @@ long do_spu_run(struct file *filp, __u32 __user *unpc, __u32 __user *ustatus) ...@@ -36,7 +36,7 @@ long do_spu_run(struct file *filp, __u32 __user *unpc, __u32 __user *ustatus)
u32 npc, status; u32 npc, status;
ret = -EFAULT; ret = -EFAULT;
if (get_user(npc, unpc)) if (get_user(npc, unpc) || get_user(status, ustatus))
goto out; goto out;
ret = -EINVAL; ret = -EINVAL;
...@@ -46,13 +46,7 @@ long do_spu_run(struct file *filp, __u32 __user *unpc, __u32 __user *ustatus) ...@@ -46,13 +46,7 @@ long do_spu_run(struct file *filp, __u32 __user *unpc, __u32 __user *ustatus)
i = SPUFS_I(filp->f_dentry->d_inode); i = SPUFS_I(filp->f_dentry->d_inode);
ret = spufs_run_spu(filp, i->i_ctx, &npc, &status); ret = spufs_run_spu(filp, i->i_ctx, &npc, &status);
if (ret ==-EAGAIN || ret == -EIO) if (put_user(npc, unpc) || put_user(status, ustatus))
ret = status;
if (put_user(npc, unpc))
ret = -EFAULT;
if (ustatus && put_user(status, ustatus))
ret = -EFAULT; ret = -EFAULT;
out: out:
return ret; return ret;
......
...@@ -105,6 +105,9 @@ ...@@ -105,6 +105,9 @@
#define SPU_CONTEXT_SWITCH_PENDING (1UL << SPU_CONTEXT_SWITCH_PENDING_nr) #define SPU_CONTEXT_SWITCH_PENDING (1UL << SPU_CONTEXT_SWITCH_PENDING_nr)
#define SPU_CONTEXT_SWITCH_ACTIVE (1UL << SPU_CONTEXT_SWITCH_ACTIVE_nr) #define SPU_CONTEXT_SWITCH_ACTIVE (1UL << SPU_CONTEXT_SWITCH_ACTIVE_nr)
struct spu_context;
struct spu_runqueue;
struct spu { struct spu {
char *name; char *name;
unsigned long local_store_phys; unsigned long local_store_phys;
...@@ -113,23 +116,28 @@ struct spu { ...@@ -113,23 +116,28 @@ struct spu {
struct spu_priv1 __iomem *priv1; struct spu_priv1 __iomem *priv1;
struct spu_priv2 __iomem *priv2; struct spu_priv2 __iomem *priv2;
struct list_head list; struct list_head list;
struct list_head sched_list;
int number; int number;
u32 isrc; u32 isrc;
u32 node; u32 node;
u64 flags; u64 flags;
u64 dar;
u64 dsisr;
struct kref kref; struct kref kref;
size_t ls_size; size_t ls_size;
unsigned int slb_replace; unsigned int slb_replace;
struct mm_struct *mm; struct mm_struct *mm;
struct spu_context *ctx;
struct spu_runqueue *rq;
pid_t pid;
int prio;
int class_0_pending; int class_0_pending;
spinlock_t register_lock; spinlock_t register_lock;
u32 stop_code; u32 stop_code;
wait_queue_head_t stop_wq; wait_queue_head_t stop_wq;
wait_queue_head_t ibox_wq; void (* wbox_callback)(struct spu *spu);
wait_queue_head_t wbox_wq; void (* ibox_callback)(struct spu *spu);
struct fasync_struct *ibox_fasync;
struct fasync_struct *wbox_fasync;
char irq_c0[8]; char irq_c0[8];
char irq_c1[8]; char irq_c1[8];
...@@ -140,9 +148,6 @@ struct spu *spu_alloc(void); ...@@ -140,9 +148,6 @@ struct spu *spu_alloc(void);
void spu_free(struct spu *spu); void spu_free(struct spu *spu);
int spu_run(struct spu *spu); int spu_run(struct spu *spu);
size_t spu_wbox_write(struct spu *spu, u32 data);
size_t spu_ibox_read(struct spu *spu, u32 *data);
extern struct spufs_calls { extern struct spufs_calls {
asmlinkage long (*create_thread)(const char __user *name, asmlinkage long (*create_thread)(const char __user *name,
unsigned int flags, mode_t mode); unsigned int flags, mode_t mode);
......
...@@ -241,6 +241,7 @@ struct spu_state { ...@@ -241,6 +241,7 @@ struct spu_state {
unsigned long suspend_time; unsigned long suspend_time;
u64 slb_esid_RW[8]; u64 slb_esid_RW[8];
u64 slb_vsid_RW[8]; u64 slb_vsid_RW[8];
spinlock_t register_lock;
}; };
extern void spu_init_csa(struct spu_state *csa); extern void spu_init_csa(struct spu_state *csa);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment