diff options
Diffstat (limited to 'arch/powerpc/platforms/cell/spufs')
18 files changed, 1001 insertions, 802 deletions
diff --git a/arch/powerpc/platforms/cell/spufs/.gitignore b/arch/powerpc/platforms/cell/spufs/.gitignore new file mode 100644 index 00000000000..a09ee8d84d6 --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/.gitignore @@ -0,0 +1,2 @@ +spu_save_dump.h +spu_restore_dump.h diff --git a/arch/powerpc/platforms/cell/spufs/Makefile b/arch/powerpc/platforms/cell/spufs/Makefile index 99610a6361f..52a7d2596d3 100644 --- a/arch/powerpc/platforms/cell/spufs/Makefile +++ b/arch/powerpc/platforms/cell/spufs/Makefile @@ -1,10 +1,12 @@ obj-$(CONFIG_SPU_FS) += spufs.o -spufs-y += inode.o file.o context.o syscalls.o coredump.o +spufs-y += inode.o file.o context.o syscalls.o spufs-y += sched.o backing_ops.o hw_ops.o run.o gang.o spufs-y += switch.o fault.o lscsa_alloc.o +spufs-$(CONFIG_COREDUMP) += coredump.o -obj-$(CONFIG_SPU_TRACE) += sputrace.o +# magic for the trace events +CFLAGS_sched.o := -I$(src) # Rules to build switch.o with the help of SPU tool chain SPU_CROSS := spu- @@ -12,10 +14,8 @@ SPU_CC := $(SPU_CROSS)gcc SPU_AS := $(SPU_CROSS)gcc SPU_LD := $(SPU_CROSS)ld SPU_OBJCOPY := $(SPU_CROSS)objcopy -SPU_CFLAGS := -O2 -Wall -I$(srctree)/include \ - -I$(objtree)/include2 -D__KERNEL__ -SPU_AFLAGS := -c -D__ASSEMBLY__ -I$(srctree)/include \ - -I$(objtree)/include2 -D__KERNEL__ +SPU_CFLAGS := -O2 -Wall -I$(srctree)/include -D__KERNEL__ +SPU_AFLAGS := -c -D__ASSEMBLY__ -I$(srctree)/include -D__KERNEL__ SPU_LDFLAGS := -N -Ttext=0x0 $(obj)/switch.o: $(obj)/spu_save_dump.h $(obj)/spu_restore_dump.h diff --git a/arch/powerpc/platforms/cell/spufs/backing_ops.c b/arch/powerpc/platforms/cell/spufs/backing_ops.c index 64eb15b2204..6e8a9ef8590 100644 --- a/arch/powerpc/platforms/cell/spufs/backing_ops.c +++ b/arch/powerpc/platforms/cell/spufs/backing_ops.c @@ -21,7 +21,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/module.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c index 0ad83aeb70b..9c6790d17ed 100644 --- a/arch/powerpc/platforms/cell/spufs/context.c +++ b/arch/powerpc/platforms/cell/spufs/context.c @@ -22,12 +22,13 @@ #include <linux/fs.h> #include <linux/mm.h> -#include <linux/module.h> #include <linux/slab.h> -#include <asm/atomic.h> +#include <linux/atomic.h> +#include <linux/sched.h> #include <asm/spu.h> #include <asm/spu_csa.h> #include "spufs.h" +#include "sputrace.h" atomic_t nr_spu_contexts = ATOMIC_INIT(0); @@ -35,6 +36,8 @@ atomic_t nr_spu_contexts = ATOMIC_INIT(0); struct spu_context *alloc_spu_context(struct spu_gang *gang) { struct spu_context *ctx; + struct timespec ts; + ctx = kzalloc(sizeof *ctx, GFP_KERNEL); if (!ctx) goto out; @@ -64,6 +67,8 @@ struct spu_context *alloc_spu_context(struct spu_gang *gang) __spu_update_sched_info(ctx); spu_set_timeslice(ctx); ctx->stats.util_state = SPU_UTIL_IDLE_LOADED; + ktime_get_ts(&ts); + ctx->stats.tstamp = timespec_to_ns(&ts); atomic_inc(&nr_spu_contexts); goto out; @@ -78,6 +83,7 @@ void destroy_spu_context(struct kref *kref) { struct spu_context *ctx; ctx = container_of(kref, struct spu_context, kref); + spu_context_nospu_trace(destroy_spu_context__enter, ctx); mutex_lock(&ctx->state_mutex); spu_deactivate(ctx); mutex_unlock(&ctx->state_mutex); @@ -88,6 +94,7 @@ void destroy_spu_context(struct kref *kref) kref_put(ctx->prof_priv_kref, ctx->prof_priv_release); BUG_ON(!list_empty(&ctx->rq)); atomic_dec(&nr_spu_contexts); + kfree(ctx->switch_log); kfree(ctx); } @@ -128,17 +135,17 @@ void spu_unmap_mappings(struct spu_context *ctx) if (ctx->local_store) unmap_mapping_range(ctx->local_store, 0, LS_SIZE, 1); if (ctx->mfc) - unmap_mapping_range(ctx->mfc, 0, 0x1000, 1); + unmap_mapping_range(ctx->mfc, 0, SPUFS_MFC_MAP_SIZE, 1); if (ctx->cntl) - unmap_mapping_range(ctx->cntl, 0, 0x1000, 1); + unmap_mapping_range(ctx->cntl, 0, SPUFS_CNTL_MAP_SIZE, 1); if (ctx->signal1) - unmap_mapping_range(ctx->signal1, 0, PAGE_SIZE, 1); + unmap_mapping_range(ctx->signal1, 0, SPUFS_SIGNAL_MAP_SIZE, 1); if (ctx->signal2) - unmap_mapping_range(ctx->signal2, 0, PAGE_SIZE, 1); + unmap_mapping_range(ctx->signal2, 0, SPUFS_SIGNAL_MAP_SIZE, 1); if (ctx->mss) - unmap_mapping_range(ctx->mss, 0, 0x1000, 1); + unmap_mapping_range(ctx->mss, 0, SPUFS_MSS_MAP_SIZE, 1); if (ctx->psmap) - unmap_mapping_range(ctx->psmap, 0, 0x20000, 1); + unmap_mapping_range(ctx->psmap, 0, SPUFS_PS_MAP_SIZE, 1); mutex_unlock(&ctx->mapping_lock); } @@ -150,6 +157,8 @@ int spu_acquire_saved(struct spu_context *ctx) { int ret; + spu_context_nospu_trace(spu_acquire_saved__enter, ctx); + ret = spu_acquire(ctx); if (ret) return ret; diff --git a/arch/powerpc/platforms/cell/spufs/coredump.c b/arch/powerpc/platforms/cell/spufs/coredump.c index 0c6a96b82b2..be6212ddbf0 100644 --- a/arch/powerpc/platforms/cell/spufs/coredump.c +++ b/arch/powerpc/platforms/cell/spufs/coredump.c @@ -22,10 +22,13 @@ #include <linux/elf.h> #include <linux/file.h> +#include <linux/fdtable.h> #include <linux/fs.h> +#include <linux/gfp.h> #include <linux/list.h> -#include <linux/module.h> #include <linux/syscalls.h> +#include <linux/coredump.h> +#include <linux/binfmts.h> #include <asm/uaccess.h> @@ -41,50 +44,12 @@ static ssize_t do_coredump_read(int num, struct spu_context *ctx, void *buffer, return spufs_coredump_read[num].read(ctx, buffer, size, off); data = spufs_coredump_read[num].get(ctx); - ret = snprintf(buffer, size, "0x%.16lx", data); + ret = snprintf(buffer, size, "0x%.16llx", data); if (ret >= size) return size; return ++ret; /* count trailing NULL */ } -/* - * These are the only things you should do on a core-file: use only these - * functions to write out all the necessary info. - */ -static int spufs_dump_write(struct file *file, const void *addr, int nr, loff_t *foffset) -{ - unsigned long limit = current->signal->rlim[RLIMIT_CORE].rlim_cur; - ssize_t written; - - if (*foffset + nr > limit) - return -EIO; - - written = file->f_op->write(file, addr, nr, &file->f_pos); - *foffset += written; - - if (written != nr) - return -EIO; - - return 0; -} - -static int spufs_dump_align(struct file *file, char *buf, loff_t new_off, - loff_t *foffset) -{ - int rc, size; - - size = min((loff_t)PAGE_SIZE, new_off - *foffset); - memset(buf, 0, size); - - rc = 0; - while (rc == 0 && new_off > *foffset) { - size = min((loff_t)PAGE_SIZE, new_off - *foffset); - rc = spufs_dump_write(file, buf, size, foffset); - } - - return rc; -} - static int spufs_ctx_note_size(struct spu_context *ctx, int dfd) { int i, sz, total = 0; @@ -105,6 +70,17 @@ static int spufs_ctx_note_size(struct spu_context *ctx, int dfd) return total; } +static int match_context(const void *v, struct file *file, unsigned fd) +{ + struct spu_context *ctx; + if (file->f_op != &spufs_context_fops) + return 0; + ctx = SPUFS_I(file_inode(file))->i_ctx; + if (ctx->flags & SPU_CREATE_NOSCHED) + return 0; + return fd + 1; +} + /* * The additional architecture-specific notes for Cell are various * context files in the spu context. @@ -114,31 +90,18 @@ static int spufs_ctx_note_size(struct spu_context *ctx, int dfd) * internal functionality to dump them without needing to actually * open the files. */ +/* + * descriptor table is not shared, so files can't change or go away. + */ static struct spu_context *coredump_next_context(int *fd) { - struct fdtable *fdt = files_fdtable(current->files); struct file *file; - struct spu_context *ctx = NULL; - - for (; *fd < fdt->max_fds; (*fd)++) { - if (!FD_ISSET(*fd, fdt->open_fds)) - continue; - - file = fcheck(*fd); - - if (!file || file->f_op != &spufs_context_fops) - continue; - - ctx = SPUFS_I(file->f_dentry->d_inode)->i_ctx; - if (ctx->flags & SPU_CREATE_NOSCHED) - continue; - - /* start searching the next fd next time we're called */ - (*fd)++; - break; - } - - return ctx; + int n = iterate_fd(current->files, *fd, match_context, NULL); + if (!n) + return NULL; + *fd = n - 1; + file = fcheck(*fd); + return SPUFS_I(file_inode(file))->i_ctx; } int spufs_coredump_extra_notes_size(void) @@ -157,16 +120,19 @@ int spufs_coredump_extra_notes_size(void) break; size += rc; + + /* start searching the next fd next time */ + fd++; } return size; } static int spufs_arch_write_note(struct spu_context *ctx, int i, - struct file *file, int dfd, loff_t *foffset) + struct coredump_params *cprm, int dfd) { loff_t pos = 0; - int sz, rc, nread, total = 0; + int sz, rc, total = 0; const int bufsz = PAGE_SIZE; char *name; char fullname[80], *buf; @@ -184,42 +150,39 @@ static int spufs_arch_write_note(struct spu_context *ctx, int i, en.n_descsz = sz; en.n_type = NT_SPU; - rc = spufs_dump_write(file, &en, sizeof(en), foffset); - if (rc) - goto out; + if (!dump_emit(cprm, &en, sizeof(en))) + goto Eio; - rc = spufs_dump_write(file, fullname, en.n_namesz, foffset); - if (rc) - goto out; + if (!dump_emit(cprm, fullname, en.n_namesz)) + goto Eio; - rc = spufs_dump_align(file, buf, roundup(*foffset, 4), foffset); - if (rc) - goto out; + if (!dump_align(cprm, 4)) + goto Eio; do { - nread = do_coredump_read(i, ctx, buf, bufsz, &pos); - if (nread > 0) { - rc = spufs_dump_write(file, buf, nread, foffset); - if (rc) - goto out; - total += nread; + rc = do_coredump_read(i, ctx, buf, bufsz, &pos); + if (rc > 0) { + if (!dump_emit(cprm, buf, rc)) + goto Eio; + total += rc; } - } while (nread == bufsz && total < sz); + } while (rc == bufsz && total < sz); - if (nread < 0) { - rc = nread; + if (rc < 0) goto out; - } - - rc = spufs_dump_align(file, buf, roundup(*foffset - total + sz, 4), - foffset); + if (!dump_skip(cprm, + roundup(cprm->written - total + sz, 4) - cprm->written)) + goto Eio; out: free_page((unsigned long)buf); return rc; +Eio: + free_page((unsigned long)buf); + return -EIO; } -int spufs_coredump_extra_notes_write(struct file *file, loff_t *foffset) +int spufs_coredump_extra_notes_write(struct coredump_params *cprm) { struct spu_context *ctx; int fd, j, rc; @@ -231,7 +194,7 @@ int spufs_coredump_extra_notes_write(struct file *file, loff_t *foffset) return rc; for (j = 0; spufs_coredump_read[j].name != NULL; j++) { - rc = spufs_arch_write_note(ctx, j, file, fd, foffset); + rc = spufs_arch_write_note(ctx, j, cprm, fd); if (rc) { spu_release_saved(ctx); return rc; @@ -239,6 +202,9 @@ int spufs_coredump_extra_notes_write(struct file *file, loff_t *foffset) } spu_release_saved(ctx); + + /* start searching the next fd next time */ + fd++; } return 0; diff --git a/arch/powerpc/platforms/cell/spufs/fault.c b/arch/powerpc/platforms/cell/spufs/fault.c index e46d300e21a..8cb6260cc80 100644 --- a/arch/powerpc/platforms/cell/spufs/fault.c +++ b/arch/powerpc/platforms/cell/spufs/fault.c @@ -21,7 +21,6 @@ */ #include <linux/sched.h> #include <linux/mm.h> -#include <linux/module.h> #include <asm/spu.h> #include <asm/spu_csa.h> @@ -83,13 +82,18 @@ int spufs_handle_class0(struct spu_context *ctx) return 0; if (stat & CLASS0_DMA_ALIGNMENT_INTR) - spufs_handle_event(ctx, ctx->csa.dar, SPE_EVENT_DMA_ALIGNMENT); + spufs_handle_event(ctx, ctx->csa.class_0_dar, + SPE_EVENT_DMA_ALIGNMENT); if (stat & CLASS0_INVALID_DMA_COMMAND_INTR) - spufs_handle_event(ctx, ctx->csa.dar, SPE_EVENT_INVALID_DMA); + spufs_handle_event(ctx, ctx->csa.class_0_dar, + SPE_EVENT_INVALID_DMA); if (stat & CLASS0_SPU_ERROR_INTR) - spufs_handle_event(ctx, ctx->csa.dar, SPE_EVENT_SPE_ERROR); + spufs_handle_event(ctx, ctx->csa.class_0_dar, + SPE_EVENT_SPE_ERROR); + + ctx->csa.class_0_pending = 0; return -EIO; } @@ -119,15 +123,15 @@ int spufs_handle_class1(struct spu_context *ctx) * in time, we can still expect to get the same fault * the immediately after the context restore. */ - ea = ctx->csa.dar; - dsisr = ctx->csa.dsisr; + ea = ctx->csa.class_1_dar; + dsisr = ctx->csa.class_1_dsisr; if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED))) return 0; spuctx_switch_state(ctx, SPU_UTIL_IOWAIT); - pr_debug("ctx %p: ea %016lx, dsisr %016lx state %d\n", ctx, ea, + pr_debug("ctx %p: ea %016llx, dsisr %016llx state %d\n", ctx, ea, dsisr, ctx->state); ctx->stats.hash_flt++; @@ -158,7 +162,7 @@ int spufs_handle_class1(struct spu_context *ctx) * time slicing will not preempt the context while the page fault * handler is running. Context switch code removes mappings. */ - ctx->csa.dar = ctx->csa.dsisr = 0; + ctx->csa.class_1_dar = ctx->csa.class_1_dsisr = 0; /* * If we handled the fault successfully and are in runnable diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index f7a7e8635fb..90986923a53 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -24,20 +24,21 @@ #include <linux/fs.h> #include <linux/ioctl.h> -#include <linux/module.h> +#include <linux/export.h> #include <linux/pagemap.h> #include <linux/poll.h> #include <linux/ptrace.h> #include <linux/seq_file.h> -#include <linux/marker.h> +#include <linux/slab.h> #include <asm/io.h> -#include <asm/semaphore.h> +#include <asm/time.h> #include <asm/spu.h> #include <asm/spu_info.h> #include <asm/uaccess.h> #include "spufs.h" +#include "sputrace.h" #define SPUFS_MMAP_4K (PAGE_SIZE == 0x1000) @@ -147,12 +148,12 @@ static int __fops ## _open(struct inode *inode, struct file *file) \ __simple_attr_check_format(__fmt, 0ull); \ return spufs_attr_open(inode, file, __get, __set, __fmt); \ } \ -static struct file_operations __fops = { \ - .owner = THIS_MODULE, \ +static const struct file_operations __fops = { \ .open = __fops ## _open, \ .release = spufs_attr_release, \ .read = spufs_attr_read, \ .write = spufs_attr_write, \ + .llseek = generic_file_llseek, \ }; @@ -217,32 +218,27 @@ spufs_mem_write(struct file *file, const char __user *buffer, loff_t pos = *ppos; int ret; - if (pos < 0) - return -EINVAL; if (pos > LS_SIZE) return -EFBIG; - if (size > LS_SIZE - pos) - size = LS_SIZE - pos; ret = spu_acquire(ctx); if (ret) return ret; local_store = ctx->ops->get_ls(ctx); - ret = copy_from_user(local_store + pos, buffer, size); + size = simple_write_to_buffer(local_store, LS_SIZE, ppos, buffer, size); spu_release(ctx); - if (ret) - return -EFAULT; - *ppos = pos + size; return size; } -static unsigned long spufs_mem_mmap_nopfn(struct vm_area_struct *vma, - unsigned long address) +static int +spufs_mem_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct spu_context *ctx = vma->vm_file->private_data; - unsigned long pfn, offset, addr0 = address; + unsigned long address = (unsigned long)vmf->virtual_address; + unsigned long pfn, offset; + #ifdef CONFIG_SPU_FS_64K_LS struct spu_state *csa = &ctx->csa; int psize; @@ -260,35 +256,56 @@ static unsigned long spufs_mem_mmap_nopfn(struct vm_area_struct *vma, } #endif /* CONFIG_SPU_FS_64K_LS */ - offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); + offset = vmf->pgoff << PAGE_SHIFT; if (offset >= LS_SIZE) - return NOPFN_SIGBUS; + return VM_FAULT_SIGBUS; - pr_debug("spufs_mem_mmap_nopfn address=0x%lx -> 0x%lx, offset=0x%lx\n", - addr0, address, offset); + pr_debug("spufs_mem_mmap_fault address=0x%lx, offset=0x%lx\n", + address, offset); if (spu_acquire(ctx)) - return NOPFN_REFAULT; + return VM_FAULT_NOPAGE; if (ctx->state == SPU_STATE_SAVED) { - vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) - & ~_PAGE_NO_CACHE); + vma->vm_page_prot = pgprot_cached(vma->vm_page_prot); pfn = vmalloc_to_pfn(ctx->csa.lscsa->ls + offset); } else { - vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) - | _PAGE_NO_CACHE); + vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot); pfn = (ctx->spu->local_store_phys + offset) >> PAGE_SHIFT; } vm_insert_pfn(vma, address, pfn); spu_release(ctx); - return NOPFN_REFAULT; + return VM_FAULT_NOPAGE; } +static int spufs_mem_mmap_access(struct vm_area_struct *vma, + unsigned long address, + void *buf, int len, int write) +{ + struct spu_context *ctx = vma->vm_file->private_data; + unsigned long offset = address - vma->vm_start; + char *local_store; + + if (write && !(vma->vm_flags & VM_WRITE)) + return -EACCES; + if (spu_acquire(ctx)) + return -EINTR; + if ((offset + len) > vma->vm_end) + len = vma->vm_end - offset; + local_store = ctx->ops->get_ls(ctx); + if (write) + memcpy_toio(local_store + offset, buf, len); + else + memcpy_fromio(buf, local_store + offset, len); + spu_release(ctx); + return len; +} -static struct vm_operations_struct spufs_mem_mmap_vmops = { - .nopfn = spufs_mem_mmap_nopfn, +static const struct vm_operations_struct spufs_mem_mmap_vmops = { + .fault = spufs_mem_mmap_fault, + .access = spufs_mem_mmap_access, }; static int spufs_mem_mmap(struct file *file, struct vm_area_struct *vma) @@ -313,8 +330,7 @@ static int spufs_mem_mmap(struct file *file, struct vm_area_struct *vma) return -EINVAL; vma->vm_flags |= VM_IO | VM_PFNMAP; - vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) - | _PAGE_NO_CACHE); + vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot); vma->vm_ops = &spufs_mem_mmap_vmops; return 0; @@ -335,7 +351,7 @@ static unsigned long spufs_get_unmapped_area(struct file *file, /* Else, try to obtain a 64K pages slice */ return slice_get_unmapped_area(addr, len, flags, - MMU_PAGE_64K, 1, 0); + MMU_PAGE_64K, 1); } #endif /* CONFIG_SPU_FS_64K_LS */ @@ -351,20 +367,22 @@ static const struct file_operations spufs_mem_fops = { #endif }; -static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma, - unsigned long address, +static int spufs_ps_fault(struct vm_area_struct *vma, + struct vm_fault *vmf, unsigned long ps_offs, unsigned long ps_size) { struct spu_context *ctx = vma->vm_file->private_data; - unsigned long area, offset = address - vma->vm_start; + unsigned long area, offset = vmf->pgoff << PAGE_SHIFT; int ret = 0; - spu_context_nospu_trace(spufs_ps_nopfn__enter, ctx); + spu_context_nospu_trace(spufs_ps_fault__enter, ctx); - offset += vma->vm_pgoff << PAGE_SHIFT; if (offset >= ps_size) - return NOPFN_SIGBUS; + return VM_FAULT_SIGBUS; + + if (fatal_signal_pending(current)) + return VM_FAULT_SIGBUS; /* * Because we release the mmap_sem, the context may be destroyed while @@ -378,7 +396,7 @@ static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma, * pages to hand out to the user, but we don't want to wait * with the mmap_sem held. * It is possible to drop the mmap_sem here, but then we need - * to return NOPFN_REFAULT because the mappings may have + * to return VM_FAULT_NOPAGE because the mappings may have * hanged. */ if (spu_acquire(ctx)) @@ -386,14 +404,15 @@ static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma, if (ctx->state == SPU_STATE_SAVED) { up_read(¤t->mm->mmap_sem); - spu_context_nospu_trace(spufs_ps_nopfn__sleep, ctx); + spu_context_nospu_trace(spufs_ps_fault__sleep, ctx); ret = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); - spu_context_trace(spufs_ps_nopfn__wake, ctx, ctx->spu); + spu_context_trace(spufs_ps_fault__wake, ctx, ctx->spu); down_read(¤t->mm->mmap_sem); } else { area = ctx->spu->problem_phys + ps_offs; - vm_insert_pfn(vma, address, (area + offset) >> PAGE_SHIFT); - spu_context_trace(spufs_ps_nopfn__insert, ctx, ctx->spu); + vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, + (area + offset) >> PAGE_SHIFT); + spu_context_trace(spufs_ps_fault__insert, ctx, ctx->spu); } if (!ret) @@ -401,18 +420,18 @@ static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma, refault: put_spu_context(ctx); - return NOPFN_REFAULT; + return VM_FAULT_NOPAGE; } #if SPUFS_MMAP_4K -static unsigned long spufs_cntl_mmap_nopfn(struct vm_area_struct *vma, - unsigned long address) +static int spufs_cntl_mmap_fault(struct vm_area_struct *vma, + struct vm_fault *vmf) { - return spufs_ps_nopfn(vma, address, 0x4000, 0x1000); + return spufs_ps_fault(vma, vmf, 0x4000, SPUFS_CNTL_MAP_SIZE); } -static struct vm_operations_struct spufs_cntl_mmap_vmops = { - .nopfn = spufs_cntl_mmap_nopfn, +static const struct vm_operations_struct spufs_cntl_mmap_vmops = { + .fault = spufs_cntl_mmap_fault, }; /* @@ -424,8 +443,7 @@ static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma) return -EINVAL; vma->vm_flags |= VM_IO | VM_PFNMAP; - vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) - | _PAGE_NO_CACHE | _PAGE_GUARDED); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); vma->vm_ops = &spufs_cntl_mmap_vmops; return 0; @@ -496,6 +514,7 @@ static const struct file_operations spufs_cntl_fops = { .release = spufs_cntl_release, .read = simple_attr_read, .write = simple_attr_write, + .llseek = generic_file_llseek, .mmap = spufs_cntl_mmap, }; @@ -523,6 +542,11 @@ spufs_regs_read(struct file *file, char __user *buffer, int ret; struct spu_context *ctx = file->private_data; + /* pre-check for file position: if we'd return EOF, there's no point + * causing a deschedule */ + if (*pos >= sizeof(ctx->csa.lscsa->gprs)) + return 0; + ret = spu_acquire_saved(ctx); if (ret) return ret; @@ -539,20 +563,18 @@ spufs_regs_write(struct file *file, const char __user *buffer, struct spu_lscsa *lscsa = ctx->csa.lscsa; int ret; - size = min_t(ssize_t, sizeof lscsa->gprs - *pos, size); - if (size <= 0) + if (*pos >= sizeof(lscsa->gprs)) return -EFBIG; - *pos += size; ret = spu_acquire_saved(ctx); if (ret) return ret; - ret = copy_from_user(lscsa->gprs + *pos - size, - buffer, size) ? -EFAULT : size; + size = simple_write_to_buffer(lscsa->gprs, sizeof(lscsa->gprs), pos, + buffer, size); spu_release_saved(ctx); - return ret; + return size; } static const struct file_operations spufs_regs_fops = { @@ -594,20 +616,18 @@ spufs_fpcr_write(struct file *file, const char __user * buffer, struct spu_lscsa *lscsa = ctx->csa.lscsa; int ret; - size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size); - if (size <= 0) + if (*pos >= sizeof(lscsa->fpcr)) return -EFBIG; ret = spu_acquire_saved(ctx); if (ret) return ret; - *pos += size; - ret = copy_from_user((char *)&lscsa->fpcr + *pos - size, - buffer, size) ? -EFAULT : size; + size = simple_write_to_buffer(&lscsa->fpcr, sizeof(lscsa->fpcr), pos, + buffer, size); spu_release_saved(ctx); - return ret; + return size; } static const struct file_operations spufs_fpcr_fops = { @@ -682,6 +702,7 @@ static ssize_t spufs_mbox_read(struct file *file, char __user *buf, static const struct file_operations spufs_mbox_fops = { .open = spufs_pipe_open, .read = spufs_mbox_read, + .llseek = no_llseek, }; static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf, @@ -711,6 +732,7 @@ static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf, static const struct file_operations spufs_mbox_stat_fops = { .open = spufs_pipe_open, .read = spufs_mbox_stat_read, + .llseek = no_llseek, }; /* low-level ibox access function */ @@ -831,6 +853,7 @@ static const struct file_operations spufs_ibox_fops = { .read = spufs_ibox_read, .poll = spufs_ibox_poll, .fasync = spufs_ibox_fasync, + .llseek = no_llseek, }; static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf, @@ -858,6 +881,7 @@ static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf, static const struct file_operations spufs_ibox_stat_fops = { .open = spufs_pipe_open, .read = spufs_ibox_stat_read, + .llseek = no_llseek, }; /* low-level mailbox write */ @@ -979,6 +1003,7 @@ static const struct file_operations spufs_wbox_fops = { .write = spufs_wbox_write, .poll = spufs_wbox_poll, .fasync = spufs_wbox_fasync, + .llseek = no_llseek, }; static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf, @@ -1006,6 +1031,7 @@ static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf, static const struct file_operations spufs_wbox_stat_fops = { .open = spufs_pipe_open, .read = spufs_wbox_stat_read, + .llseek = no_llseek, }; static int spufs_signal1_open(struct inode *inode, struct file *file) @@ -1097,23 +1123,23 @@ static ssize_t spufs_signal1_write(struct file *file, const char __user *buf, return 4; } -static unsigned long spufs_signal1_mmap_nopfn(struct vm_area_struct *vma, - unsigned long address) +static int +spufs_signal1_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { -#if PAGE_SIZE == 0x1000 - return spufs_ps_nopfn(vma, address, 0x14000, 0x1000); -#elif PAGE_SIZE == 0x10000 +#if SPUFS_SIGNAL_MAP_SIZE == 0x1000 + return spufs_ps_fault(vma, vmf, 0x14000, SPUFS_SIGNAL_MAP_SIZE); +#elif SPUFS_SIGNAL_MAP_SIZE == 0x10000 /* For 64k pages, both signal1 and signal2 can be used to mmap the whole * signal 1 and 2 area */ - return spufs_ps_nopfn(vma, address, 0x10000, 0x10000); + return spufs_ps_fault(vma, vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE); #else #error unsupported page size #endif } -static struct vm_operations_struct spufs_signal1_mmap_vmops = { - .nopfn = spufs_signal1_mmap_nopfn, +static const struct vm_operations_struct spufs_signal1_mmap_vmops = { + .fault = spufs_signal1_mmap_fault, }; static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma) @@ -1122,8 +1148,7 @@ static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma) return -EINVAL; vma->vm_flags |= VM_IO | VM_PFNMAP; - vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) - | _PAGE_NO_CACHE | _PAGE_GUARDED); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); vma->vm_ops = &spufs_signal1_mmap_vmops; return 0; @@ -1135,6 +1160,7 @@ static const struct file_operations spufs_signal1_fops = { .read = spufs_signal1_read, .write = spufs_signal1_write, .mmap = spufs_signal1_mmap, + .llseek = no_llseek, }; static const struct file_operations spufs_signal1_nosched_fops = { @@ -1142,6 +1168,7 @@ static const struct file_operations spufs_signal1_nosched_fops = { .release = spufs_signal1_release, .write = spufs_signal1_write, .mmap = spufs_signal1_mmap, + .llseek = no_llseek, }; static int spufs_signal2_open(struct inode *inode, struct file *file) @@ -1234,23 +1261,23 @@ static ssize_t spufs_signal2_write(struct file *file, const char __user *buf, } #if SPUFS_MMAP_4K -static unsigned long spufs_signal2_mmap_nopfn(struct vm_area_struct *vma, - unsigned long address) +static int +spufs_signal2_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { -#if PAGE_SIZE == 0x1000 - return spufs_ps_nopfn(vma, address, 0x1c000, 0x1000); -#elif PAGE_SIZE == 0x10000 +#if SPUFS_SIGNAL_MAP_SIZE == 0x1000 + return spufs_ps_fault(vma, vmf, 0x1c000, SPUFS_SIGNAL_MAP_SIZE); +#elif SPUFS_SIGNAL_MAP_SIZE == 0x10000 /* For 64k pages, both signal1 and signal2 can be used to mmap the whole * signal 1 and 2 area */ - return spufs_ps_nopfn(vma, address, 0x10000, 0x10000); + return spufs_ps_fault(vma, vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE); #else #error unsupported page size #endif } -static struct vm_operations_struct spufs_signal2_mmap_vmops = { - .nopfn = spufs_signal2_mmap_nopfn, +static const struct vm_operations_struct spufs_signal2_mmap_vmops = { + .fault = spufs_signal2_mmap_fault, }; static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma) @@ -1259,8 +1286,7 @@ static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma) return -EINVAL; vma->vm_flags |= VM_IO | VM_PFNMAP; - vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) - | _PAGE_NO_CACHE | _PAGE_GUARDED); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); vma->vm_ops = &spufs_signal2_mmap_vmops; return 0; @@ -1275,6 +1301,7 @@ static const struct file_operations spufs_signal2_fops = { .read = spufs_signal2_read, .write = spufs_signal2_write, .mmap = spufs_signal2_mmap, + .llseek = no_llseek, }; static const struct file_operations spufs_signal2_nosched_fops = { @@ -1282,6 +1309,7 @@ static const struct file_operations spufs_signal2_nosched_fops = { .release = spufs_signal2_release, .write = spufs_signal2_write, .mmap = spufs_signal2_mmap, + .llseek = no_llseek, }; /* @@ -1337,7 +1365,7 @@ static u64 spufs_signal1_type_get(struct spu_context *ctx) return ctx->ops->signal1_type_get(ctx); } DEFINE_SPUFS_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get, - spufs_signal1_type_set, "%llu", SPU_ATTR_ACQUIRE); + spufs_signal1_type_set, "%llu\n", SPU_ATTR_ACQUIRE); static int spufs_signal2_type_set(void *data, u64 val) @@ -1359,17 +1387,17 @@ static u64 spufs_signal2_type_get(struct spu_context *ctx) return ctx->ops->signal2_type_get(ctx); } DEFINE_SPUFS_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get, - spufs_signal2_type_set, "%llu", SPU_ATTR_ACQUIRE); + spufs_signal2_type_set, "%llu\n", SPU_ATTR_ACQUIRE); #if SPUFS_MMAP_4K -static unsigned long spufs_mss_mmap_nopfn(struct vm_area_struct *vma, - unsigned long address) +static int +spufs_mss_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { - return spufs_ps_nopfn(vma, address, 0x0000, 0x1000); + return spufs_ps_fault(vma, vmf, 0x0000, SPUFS_MSS_MAP_SIZE); } -static struct vm_operations_struct spufs_mss_mmap_vmops = { - .nopfn = spufs_mss_mmap_nopfn, +static const struct vm_operations_struct spufs_mss_mmap_vmops = { + .fault = spufs_mss_mmap_fault, }; /* @@ -1381,8 +1409,7 @@ static int spufs_mss_mmap(struct file *file, struct vm_area_struct *vma) return -EINVAL; vma->vm_flags |= VM_IO | VM_PFNMAP; - vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) - | _PAGE_NO_CACHE | _PAGE_GUARDED); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); vma->vm_ops = &spufs_mss_mmap_vmops; return 0; @@ -1422,16 +1449,17 @@ static const struct file_operations spufs_mss_fops = { .open = spufs_mss_open, .release = spufs_mss_release, .mmap = spufs_mss_mmap, + .llseek = no_llseek, }; -static unsigned long spufs_psmap_mmap_nopfn(struct vm_area_struct *vma, - unsigned long address) +static int +spufs_psmap_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { - return spufs_ps_nopfn(vma, address, 0x0000, 0x20000); + return spufs_ps_fault(vma, vmf, 0x0000, SPUFS_PS_MAP_SIZE); } -static struct vm_operations_struct spufs_psmap_mmap_vmops = { - .nopfn = spufs_psmap_mmap_nopfn, +static const struct vm_operations_struct spufs_psmap_mmap_vmops = { + .fault = spufs_psmap_mmap_fault, }; /* @@ -1443,8 +1471,7 @@ static int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma) return -EINVAL; vma->vm_flags |= VM_IO | VM_PFNMAP; - vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) - | _PAGE_NO_CACHE | _PAGE_GUARDED); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); vma->vm_ops = &spufs_psmap_mmap_vmops; return 0; @@ -1480,18 +1507,19 @@ static const struct file_operations spufs_psmap_fops = { .open = spufs_psmap_open, .release = spufs_psmap_release, .mmap = spufs_psmap_mmap, + .llseek = no_llseek, }; #if SPUFS_MMAP_4K -static unsigned long spufs_mfc_mmap_nopfn(struct vm_area_struct *vma, - unsigned long address) +static int +spufs_mfc_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { - return spufs_ps_nopfn(vma, address, 0x3000, 0x1000); + return spufs_ps_fault(vma, vmf, 0x3000, SPUFS_MFC_MAP_SIZE); } -static struct vm_operations_struct spufs_mfc_mmap_vmops = { - .nopfn = spufs_mfc_mmap_nopfn, +static const struct vm_operations_struct spufs_mfc_mmap_vmops = { + .fault = spufs_mfc_mmap_fault, }; /* @@ -1503,8 +1531,7 @@ static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma) return -EINVAL; vma->vm_flags |= VM_IO | VM_PFNMAP; - vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) - | _PAGE_NO_CACHE | _PAGE_GUARDED); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); vma->vm_ops = &spufs_mfc_mmap_vmops; return 0; @@ -1556,7 +1583,7 @@ void spufs_mfc_callback(struct spu *spu) wake_up_all(&ctx->mfc_wq); - pr_debug("%s %s\n", __FUNCTION__, spu->name); + pr_debug("%s %s\n", __func__, spu->name); if (ctx->mfc_fasync) { u32 free_elements, tagstatus; unsigned int mask; @@ -1630,7 +1657,7 @@ out: static int spufs_check_valid_dma(struct mfc_dma_command *cmd) { - pr_debug("queueing DMA %x %lx %x %x %x\n", cmd->lsa, + pr_debug("queueing DMA %x %llx %x %x %x\n", cmd->lsa, cmd->ea, cmd->size, cmd->tag, cmd->cmd); switch (cmd->cmd) { @@ -1647,7 +1674,7 @@ static int spufs_check_valid_dma(struct mfc_dma_command *cmd) } if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) { - pr_debug("invalid DMA alignment, ea %lx lsa %x\n", + pr_debug("invalid DMA alignment, ea %llx lsa %x\n", cmd->ea, cmd->lsa); return -EIO; } @@ -1790,7 +1817,7 @@ static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait) if (tagstatus & ctx->tagwait) mask |= POLLIN | POLLRDNORM; - pr_debug("%s: free %d tagstatus %d tagwait %d\n", __FUNCTION__, + pr_debug("%s: free %d tagstatus %d tagwait %d\n", __func__, free_elements, tagstatus, ctx->tagwait); return mask; @@ -1822,10 +1849,16 @@ out: return ret; } -static int spufs_mfc_fsync(struct file *file, struct dentry *dentry, - int datasync) +static int spufs_mfc_fsync(struct file *file, loff_t start, loff_t end, int datasync) { - return spufs_mfc_flush(file, NULL); + struct inode *inode = file_inode(file); + int err = filemap_write_and_wait_range(inode->i_mapping, start, end); + if (!err) { + mutex_lock(&inode->i_mutex); + err = spufs_mfc_flush(file, NULL); + mutex_unlock(&inode->i_mutex); + } + return err; } static int spufs_mfc_fasync(int fd, struct file *file, int on) @@ -1845,6 +1878,7 @@ static const struct file_operations spufs_mfc_fops = { .fsync = spufs_mfc_fsync, .fasync = spufs_mfc_fasync, .mmap = spufs_mfc_mmap, + .llseek = no_llseek, }; static int spufs_npc_set(void *data, u64 val) @@ -2220,6 +2254,7 @@ static ssize_t spufs_dma_info_read(struct file *file, char __user *buf, static const struct file_operations spufs_dma_info_fops = { .open = spufs_info_open, .read = spufs_dma_info_read, + .llseek = no_llseek, }; static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx, @@ -2273,6 +2308,7 @@ static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf, static const struct file_operations spufs_proxydma_info_fops = { .open = spufs_info_open, .read = spufs_proxydma_info_read, + .llseek = no_llseek, }; static int spufs_show_tid(struct seq_file *s, void *private) @@ -2387,23 +2423,276 @@ static const struct file_operations spufs_stat_fops = { .release = single_release, }; +static inline int spufs_switch_log_used(struct spu_context *ctx) +{ + return (ctx->switch_log->head - ctx->switch_log->tail) % + SWITCH_LOG_BUFSIZE; +} + +static inline int spufs_switch_log_avail(struct spu_context *ctx) +{ + return SWITCH_LOG_BUFSIZE - spufs_switch_log_used(ctx); +} + +static int spufs_switch_log_open(struct inode *inode, struct file *file) +{ + struct spu_context *ctx = SPUFS_I(inode)->i_ctx; + int rc; + + rc = spu_acquire(ctx); + if (rc) + return rc; + + if (ctx->switch_log) { + rc = -EBUSY; + goto out; + } + + ctx->switch_log = kmalloc(sizeof(struct switch_log) + + SWITCH_LOG_BUFSIZE * sizeof(struct switch_log_entry), + GFP_KERNEL); + + if (!ctx->switch_log) { + rc = -ENOMEM; + goto out; + } + + ctx->switch_log->head = ctx->switch_log->tail = 0; + init_waitqueue_head(&ctx->switch_log->wait); + rc = 0; + +out: + spu_release(ctx); + return rc; +} + +static int spufs_switch_log_release(struct inode *inode, struct file *file) +{ + struct spu_context *ctx = SPUFS_I(inode)->i_ctx; + int rc; + + rc = spu_acquire(ctx); + if (rc) + return rc; + + kfree(ctx->switch_log); + ctx->switch_log = NULL; + spu_release(ctx); + + return 0; +} + +static int switch_log_sprint(struct spu_context *ctx, char *tbuf, int n) +{ + struct switch_log_entry *p; + + p = ctx->switch_log->log + ctx->switch_log->tail % SWITCH_LOG_BUFSIZE; + + return snprintf(tbuf, n, "%u.%09u %d %u %u %llu\n", + (unsigned int) p->tstamp.tv_sec, + (unsigned int) p->tstamp.tv_nsec, + p->spu_id, + (unsigned int) p->type, + (unsigned int) p->val, + (unsigned long long) p->timebase); +} -struct tree_descr spufs_dir_contents[] = { +static ssize_t spufs_switch_log_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + struct inode *inode = file_inode(file); + struct spu_context *ctx = SPUFS_I(inode)->i_ctx; + int error = 0, cnt = 0; + + if (!buf) + return -EINVAL; + + error = spu_acquire(ctx); + if (error) + return error; + + while (cnt < len) { + char tbuf[128]; + int width; + + if (spufs_switch_log_used(ctx) == 0) { + if (cnt > 0) { + /* If there's data ready to go, we can + * just return straight away */ + break; + + } else if (file->f_flags & O_NONBLOCK) { + error = -EAGAIN; + break; + + } else { + /* spufs_wait will drop the mutex and + * re-acquire, but since we're in read(), the + * file cannot be _released (and so + * ctx->switch_log is stable). + */ + error = spufs_wait(ctx->switch_log->wait, + spufs_switch_log_used(ctx) > 0); + + /* On error, spufs_wait returns without the + * state mutex held */ + if (error) + return error; + + /* We may have had entries read from underneath + * us while we dropped the mutex in spufs_wait, + * so re-check */ + if (spufs_switch_log_used(ctx) == 0) + continue; + } + } + + width = switch_log_sprint(ctx, tbuf, sizeof(tbuf)); + if (width < len) + ctx->switch_log->tail = + (ctx->switch_log->tail + 1) % + SWITCH_LOG_BUFSIZE; + else + /* If the record is greater than space available return + * partial buffer (so far) */ + break; + + error = copy_to_user(buf + cnt, tbuf, width); + if (error) + break; + cnt += width; + } + + spu_release(ctx); + + return cnt == 0 ? error : cnt; +} + +static unsigned int spufs_switch_log_poll(struct file *file, poll_table *wait) +{ + struct inode *inode = file_inode(file); + struct spu_context *ctx = SPUFS_I(inode)->i_ctx; + unsigned int mask = 0; + int rc; + + poll_wait(file, &ctx->switch_log->wait, wait); + + rc = spu_acquire(ctx); + if (rc) + return rc; + + if (spufs_switch_log_used(ctx) > 0) + mask |= POLLIN; + + spu_release(ctx); + + return mask; +} + +static const struct file_operations spufs_switch_log_fops = { + .open = spufs_switch_log_open, + .read = spufs_switch_log_read, + .poll = spufs_switch_log_poll, + .release = spufs_switch_log_release, + .llseek = no_llseek, +}; + +/** + * Log a context switch event to a switch log reader. + * + * Must be called with ctx->state_mutex held. + */ +void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx, + u32 type, u32 val) +{ + if (!ctx->switch_log) + return; + + if (spufs_switch_log_avail(ctx) > 1) { + struct switch_log_entry *p; + + p = ctx->switch_log->log + ctx->switch_log->head; + ktime_get_ts(&p->tstamp); + p->timebase = get_tb(); + p->spu_id = spu ? spu->number : -1; + p->type = type; + p->val = val; + + ctx->switch_log->head = + (ctx->switch_log->head + 1) % SWITCH_LOG_BUFSIZE; + } + + wake_up(&ctx->switch_log->wait); +} + +static int spufs_show_ctx(struct seq_file *s, void *private) +{ + struct spu_context *ctx = s->private; + u64 mfc_control_RW; + + mutex_lock(&ctx->state_mutex); + if (ctx->spu) { + struct spu *spu = ctx->spu; + struct spu_priv2 __iomem *priv2 = spu->priv2; + + spin_lock_irq(&spu->register_lock); + mfc_control_RW = in_be64(&priv2->mfc_control_RW); + spin_unlock_irq(&spu->register_lock); + } else { + struct spu_state *csa = &ctx->csa; + + mfc_control_RW = csa->priv2.mfc_control_RW; + } + + seq_printf(s, "%c flgs(%lx) sflgs(%lx) pri(%d) ts(%d) spu(%02d)" + " %c %llx %llx %llx %llx %x %x\n", + ctx->state == SPU_STATE_SAVED ? 'S' : 'R', + ctx->flags, + ctx->sched_flags, + ctx->prio, + ctx->time_slice, + ctx->spu ? ctx->spu->number : -1, + !list_empty(&ctx->rq) ? 'q' : ' ', + ctx->csa.class_0_pending, + ctx->csa.class_0_dar, + ctx->csa.class_1_dsisr, + mfc_control_RW, + ctx->ops->runcntl_read(ctx), + ctx->ops->status_read(ctx)); + + mutex_unlock(&ctx->state_mutex); + + return 0; +} + +static int spufs_ctx_open(struct inode *inode, struct file *file) +{ + return single_open(file, spufs_show_ctx, SPUFS_I(inode)->i_ctx); +} + +static const struct file_operations spufs_ctx_fops = { + .open = spufs_ctx_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +const struct spufs_tree_descr spufs_dir_contents[] = { { "capabilities", &spufs_caps_fops, 0444, }, - { "mem", &spufs_mem_fops, 0666, }, - { "regs", &spufs_regs_fops, 0666, }, + { "mem", &spufs_mem_fops, 0666, LS_SIZE, }, + { "regs", &spufs_regs_fops, 0666, sizeof(struct spu_reg128[128]), }, { "mbox", &spufs_mbox_fops, 0444, }, { "ibox", &spufs_ibox_fops, 0444, }, { "wbox", &spufs_wbox_fops, 0222, }, - { "mbox_stat", &spufs_mbox_stat_fops, 0444, }, - { "ibox_stat", &spufs_ibox_stat_fops, 0444, }, - { "wbox_stat", &spufs_wbox_stat_fops, 0444, }, + { "mbox_stat", &spufs_mbox_stat_fops, 0444, sizeof(u32), }, + { "ibox_stat", &spufs_ibox_stat_fops, 0444, sizeof(u32), }, + { "wbox_stat", &spufs_wbox_stat_fops, 0444, sizeof(u32), }, { "signal1", &spufs_signal1_fops, 0666, }, { "signal2", &spufs_signal2_fops, 0666, }, { "signal1_type", &spufs_signal1_type, 0666, }, { "signal2_type", &spufs_signal2_type, 0666, }, { "cntl", &spufs_cntl_fops, 0666, }, - { "fpcr", &spufs_fpcr_fops, 0666, }, + { "fpcr", &spufs_fpcr_fops, 0666, sizeof(struct spu_reg128), }, { "lslr", &spufs_lslr_ops, 0444, }, { "mfc", &spufs_mfc_fops, 0666, }, { "mss", &spufs_mss_fops, 0666, }, @@ -2413,28 +2702,31 @@ struct tree_descr spufs_dir_contents[] = { { "decr_status", &spufs_decr_status_ops, 0666, }, { "event_mask", &spufs_event_mask_ops, 0666, }, { "event_status", &spufs_event_status_ops, 0444, }, - { "psmap", &spufs_psmap_fops, 0666, }, + { "psmap", &spufs_psmap_fops, 0666, SPUFS_PS_MAP_SIZE, }, { "phys-id", &spufs_id_ops, 0666, }, { "object-id", &spufs_object_id_ops, 0666, }, - { "mbox_info", &spufs_mbox_info_fops, 0444, }, - { "ibox_info", &spufs_ibox_info_fops, 0444, }, - { "wbox_info", &spufs_wbox_info_fops, 0444, }, - { "dma_info", &spufs_dma_info_fops, 0444, }, - { "proxydma_info", &spufs_proxydma_info_fops, 0444, }, + { "mbox_info", &spufs_mbox_info_fops, 0444, sizeof(u32), }, + { "ibox_info", &spufs_ibox_info_fops, 0444, sizeof(u32), }, + { "wbox_info", &spufs_wbox_info_fops, 0444, sizeof(u32), }, + { "dma_info", &spufs_dma_info_fops, 0444, + sizeof(struct spu_dma_info), }, + { "proxydma_info", &spufs_proxydma_info_fops, 0444, + sizeof(struct spu_proxydma_info)}, { "tid", &spufs_tid_fops, 0444, }, { "stat", &spufs_stat_fops, 0444, }, + { "switch_log", &spufs_switch_log_fops, 0444 }, {}, }; -struct tree_descr spufs_dir_nosched_contents[] = { +const struct spufs_tree_descr spufs_dir_nosched_contents[] = { { "capabilities", &spufs_caps_fops, 0444, }, - { "mem", &spufs_mem_fops, 0666, }, + { "mem", &spufs_mem_fops, 0666, LS_SIZE, }, { "mbox", &spufs_mbox_fops, 0444, }, { "ibox", &spufs_ibox_fops, 0444, }, { "wbox", &spufs_wbox_fops, 0222, }, - { "mbox_stat", &spufs_mbox_stat_fops, 0444, }, - { "ibox_stat", &spufs_ibox_stat_fops, 0444, }, - { "wbox_stat", &spufs_wbox_stat_fops, 0444, }, + { "mbox_stat", &spufs_mbox_stat_fops, 0444, sizeof(u32), }, + { "ibox_stat", &spufs_ibox_stat_fops, 0444, sizeof(u32), }, + { "wbox_stat", &spufs_wbox_stat_fops, 0444, sizeof(u32), }, { "signal1", &spufs_signal1_nosched_fops, 0222, }, { "signal2", &spufs_signal2_nosched_fops, 0222, }, { "signal1_type", &spufs_signal1_type, 0666, }, @@ -2443,7 +2735,7 @@ struct tree_descr spufs_dir_nosched_contents[] = { { "mfc", &spufs_mfc_fops, 0666, }, { "cntl", &spufs_cntl_fops, 0666, }, { "npc", &spufs_npc_ops, 0666, }, - { "psmap", &spufs_psmap_fops, 0666, }, + { "psmap", &spufs_psmap_fops, 0666, SPUFS_PS_MAP_SIZE, }, { "phys-id", &spufs_id_ops, 0666, }, { "object-id", &spufs_object_id_ops, 0666, }, { "tid", &spufs_tid_fops, 0444, }, @@ -2451,7 +2743,12 @@ struct tree_descr spufs_dir_nosched_contents[] = { {}, }; -struct spufs_coredump_reader spufs_coredump_read[] = { +const struct spufs_tree_descr spufs_dir_debug_contents[] = { + { ".ctx", &spufs_ctx_fops, 0444, }, + {}, +}; + +const struct spufs_coredump_reader spufs_coredump_read[] = { { "regs", __spufs_regs_read, NULL, sizeof(struct spu_reg128[128])}, { "fpcr", __spufs_fpcr_read, NULL, sizeof(struct spu_reg128) }, { "lslr", NULL, spufs_lslr_get, 19 }, diff --git a/arch/powerpc/platforms/cell/spufs/hw_ops.c b/arch/powerpc/platforms/cell/spufs/hw_ops.c index 64f8540b832..8655c4cbefc 100644 --- a/arch/powerpc/platforms/cell/spufs/hw_ops.c +++ b/arch/powerpc/platforms/cell/spufs/hw_ops.c @@ -18,7 +18,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/module.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 6d1228c66c5..87ba7cf99cd 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -23,6 +23,7 @@ #include <linux/file.h> #include <linux/fs.h> +#include <linux/fsnotify.h> #include <linux/backing-dev.h> #include <linux/init.h> #include <linux/ioctl.h> @@ -35,17 +36,25 @@ #include <linux/parser.h> #include <asm/prom.h> -#include <asm/semaphore.h> #include <asm/spu.h> #include <asm/spu_priv1.h> #include <asm/uaccess.h> #include "spufs.h" +struct spufs_sb_info { + int debug; +}; + static struct kmem_cache *spufs_inode_cache; char *isolated_loader; static int isolated_loader_size; +static struct spufs_sb_info *spufs_get_sb_info(struct super_block *sb) +{ + return sb->s_fs_info; +} + static struct inode * spufs_alloc_inode(struct super_block *sb) { @@ -62,14 +71,19 @@ spufs_alloc_inode(struct super_block *sb) return &ei->vfs_inode; } -static void -spufs_destroy_inode(struct inode *inode) +static void spufs_i_callback(struct rcu_head *head) { + struct inode *inode = container_of(head, struct inode, i_rcu); kmem_cache_free(spufs_inode_cache, SPUFS_I(inode)); } +static void spufs_destroy_inode(struct inode *inode) +{ + call_rcu(&inode->i_rcu, spufs_i_callback); +} + static void -spufs_init_once(struct kmem_cache *cachep, void *p) +spufs_init_once(void *p) { struct spufs_inode_info *ei = p; @@ -77,7 +91,7 @@ spufs_init_once(struct kmem_cache *cachep, void *p) } static struct inode * -spufs_new_inode(struct super_block *sb, int mode) +spufs_new_inode(struct super_block *sb, umode_t mode) { struct inode *inode; @@ -85,10 +99,10 @@ spufs_new_inode(struct super_block *sb, int mode) if (!inode) goto out; + inode->i_ino = get_next_ino(); inode->i_mode = mode; - inode->i_uid = current->fsuid; - inode->i_gid = current->fsgid; - inode->i_blocks = 0; + inode->i_uid = current_fsuid(); + inode->i_gid = current_fsgid(); inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; out: return inode; @@ -102,16 +116,18 @@ spufs_setattr(struct dentry *dentry, struct iattr *attr) if ((attr->ia_valid & ATTR_SIZE) && (attr->ia_size != inode->i_size)) return -EINVAL; - return inode_setattr(inode, attr); + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; } static int spufs_new_file(struct super_block *sb, struct dentry *dentry, - const struct file_operations *fops, int mode, - struct spu_context *ctx) + const struct file_operations *fops, umode_t mode, + size_t size, struct spu_context *ctx) { - static struct inode_operations spufs_file_iops = { + static const struct inode_operations spufs_file_iops = { .setattr = spufs_setattr, }; struct inode *inode; @@ -125,6 +141,7 @@ spufs_new_file(struct super_block *sb, struct dentry *dentry, ret = 0; inode->i_op = &spufs_file_iops; inode->i_fop = fops; + inode->i_size = size; inode->i_private = SPUFS_I(inode)->i_ctx = get_spu_context(ctx); d_add(dentry, inode); out: @@ -132,15 +149,14 @@ out: } static void -spufs_delete_inode(struct inode *inode) +spufs_evict_inode(struct inode *inode) { struct spufs_inode_info *ei = SPUFS_I(inode); - + clear_inode(inode); if (ei->i_ctx) put_spu_context(ei->i_ctx); if (ei->i_gang) put_spu_gang(ei->i_gang); - clear_inode(inode); } static void spufs_prune_dir(struct dentry *dir) @@ -149,18 +165,18 @@ static void spufs_prune_dir(struct dentry *dir) mutex_lock(&dir->d_inode->i_mutex); list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) { - spin_lock(&dcache_lock); spin_lock(&dentry->d_lock); if (!(d_unhashed(dentry)) && dentry->d_inode) { - dget_locked(dentry); + dget_dlock(dentry); __d_drop(dentry); spin_unlock(&dentry->d_lock); simple_unlink(dir->d_inode, dentry); - spin_unlock(&dcache_lock); + /* XXX: what was dcache_lock protecting here? Other + * filesystems (IB, configfs) release dcache_lock + * before unlink */ dput(dentry); } else { spin_unlock(&dentry->d_lock); - spin_unlock(&dcache_lock); } } shrink_dcache_parent(dir); @@ -171,46 +187,31 @@ static void spufs_prune_dir(struct dentry *dir) static int spufs_rmdir(struct inode *parent, struct dentry *dir) { /* remove all entries */ + int res; spufs_prune_dir(dir); d_drop(dir); - - return simple_rmdir(parent, dir); + res = simple_rmdir(parent, dir); + /* We have to give up the mm_struct */ + spu_forget(SPUFS_I(dir->d_inode)->i_ctx); + return res; } -static int spufs_fill_dir(struct dentry *dir, struct tree_descr *files, - int mode, struct spu_context *ctx) +static int spufs_fill_dir(struct dentry *dir, + const struct spufs_tree_descr *files, umode_t mode, + struct spu_context *ctx) { - struct dentry *dentry, *tmp; - int ret; - while (files->name && files->name[0]) { - ret = -ENOMEM; - dentry = d_alloc_name(dir, files->name); + int ret; + struct dentry *dentry = d_alloc_name(dir, files->name); if (!dentry) - goto out; + return -ENOMEM; ret = spufs_new_file(dir->d_sb, dentry, files->ops, - files->mode & mode, ctx); + files->mode & mode, files->size, ctx); if (ret) - goto out; + return ret; files++; } return 0; -out: - /* - * remove all children from dir. dir->inode is not set so don't - * just simply use spufs_prune_dir() and panic afterwards :) - * dput() looks like it will do the right thing: - * - dec parent's ref counter - * - remove child from parent's child list - * - free child's inode if possible - * - free child - */ - list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) { - dput(dentry); - } - - shrink_dcache_parent(dir); - return ret; } static int spufs_dir_close(struct inode *inode, struct file *file) @@ -224,14 +225,11 @@ static int spufs_dir_close(struct inode *inode, struct file *file) parent = dir->d_parent->d_inode; ctx = SPUFS_I(dir->d_inode)->i_ctx; - mutex_lock(&parent->i_mutex); + mutex_lock_nested(&parent->i_mutex, I_MUTEX_PARENT); ret = spufs_rmdir(parent, dir); mutex_unlock(&parent->i_mutex); WARN_ON(ret); - /* We have to give up the mm_struct */ - spu_forget(ctx); - return dcache_dir_close(inode, file); } @@ -240,23 +238,22 @@ const struct file_operations spufs_context_fops = { .release = spufs_dir_close, .llseek = dcache_dir_lseek, .read = generic_read_dir, - .readdir = dcache_readdir, - .fsync = simple_sync_file, + .iterate = dcache_readdir, + .fsync = noop_fsync, }; EXPORT_SYMBOL_GPL(spufs_context_fops); static int spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags, - int mode) + umode_t mode) { int ret; struct inode *inode; struct spu_context *ctx; - ret = -ENOSPC; inode = spufs_new_inode(dir->i_sb, mode | S_IFDIR); if (!inode) - goto out; + return -ENOSPC; if (dir->i_mode & S_ISGID) { inode->i_gid = dir->i_gid; @@ -264,58 +261,58 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags, } ctx = alloc_spu_context(SPUFS_I(dir)->i_gang); /* XXX gang */ SPUFS_I(inode)->i_ctx = ctx; - if (!ctx) - goto out_iput; + if (!ctx) { + iput(inode); + return -ENOSPC; + } ctx->flags = flags; inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; + + mutex_lock(&inode->i_mutex); + + dget(dentry); + inc_nlink(dir); + inc_nlink(inode); + + d_instantiate(dentry, inode); + 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); + if (!ret && spufs_get_sb_info(dir->i_sb)->debug) + ret = spufs_fill_dir(dentry, spufs_dir_debug_contents, + mode, ctx); + if (ret) - goto out_free_ctx; + spufs_rmdir(dir, dentry); - d_instantiate(dentry, inode); - dget(dentry); - dir->i_nlink++; - dentry->d_inode->i_nlink++; - goto out; + mutex_unlock(&inode->i_mutex); -out_free_ctx: - spu_forget(ctx); - put_spu_context(ctx); -out_iput: - iput(inode); -out: return ret; } -static int spufs_context_open(struct dentry *dentry, struct vfsmount *mnt) +static int spufs_context_open(struct path *path) { int ret; struct file *filp; ret = get_unused_fd(); - if (ret < 0) { - dput(dentry); - mntput(mnt); - goto out; - } + if (ret < 0) + return ret; - filp = dentry_open(dentry, mnt, O_RDONLY); + filp = dentry_open(path, O_RDONLY, current_cred()); if (IS_ERR(filp)) { put_unused_fd(ret); - ret = PTR_ERR(filp); - goto out; + return PTR_ERR(filp); } filp->f_op = &spufs_context_fops; fd_install(ret, filp); -out: return ret; } @@ -350,7 +347,7 @@ spufs_assert_affinity(unsigned int flags, struct spu_gang *gang, return ERR_PTR(-EINVAL); neighbor = get_spu_context( - SPUFS_I(filp->f_dentry->d_inode)->i_ctx); + SPUFS_I(file_inode(filp))->i_ctx); if (!list_empty(&neighbor->aff_list) && !(neighbor->aff_head) && !list_is_last(&neighbor->aff_list, &gang->aff_list_head) && @@ -423,36 +420,33 @@ spufs_set_affinity(unsigned int flags, struct spu_context *ctx, static int spufs_create_context(struct inode *inode, struct dentry *dentry, - struct vfsmount *mnt, int flags, int mode, + struct vfsmount *mnt, int flags, umode_t mode, struct file *aff_filp) { int ret; int affinity; struct spu_gang *gang; struct spu_context *neighbor; + struct path path = {.mnt = mnt, .dentry = dentry}; - ret = -EPERM; if ((flags & SPU_CREATE_NOSCHED) && !capable(CAP_SYS_NICE)) - goto out_unlock; + return -EPERM; - ret = -EINVAL; if ((flags & (SPU_CREATE_NOSCHED | SPU_CREATE_ISOLATE)) == SPU_CREATE_ISOLATE) - goto out_unlock; + return -EINVAL; - ret = -ENODEV; if ((flags & SPU_CREATE_ISOLATE) && !isolated_loader) - goto out_unlock; + return -ENODEV; gang = NULL; neighbor = NULL; affinity = flags & (SPU_CREATE_AFFINITY_MEM | SPU_CREATE_AFFINITY_SPU); if (affinity) { gang = SPUFS_I(inode)->i_gang; - ret = -EINVAL; if (!gang) - goto out_unlock; + return -EINVAL; mutex_lock(&gang->aff_mutex); neighbor = spufs_assert_affinity(flags, gang, aff_filp); if (IS_ERR(neighbor)) { @@ -472,30 +466,18 @@ spufs_create_context(struct inode *inode, struct dentry *dentry, put_spu_context(neighbor); } - /* - * get references for dget and mntget, will be released - * in error path of *_open(). - */ - ret = spufs_context_open(dget(dentry), mntget(mnt)); - if (ret < 0) { + ret = spufs_context_open(&path); + if (ret < 0) WARN_ON(spufs_rmdir(inode, dentry)); - mutex_unlock(&inode->i_mutex); - spu_forget(SPUFS_I(dentry->d_inode)->i_ctx); - goto out; - } out_aff_unlock: if (affinity) mutex_unlock(&gang->aff_mutex); -out_unlock: - mutex_unlock(&inode->i_mutex); -out: - dput(dentry); return ret; } static int -spufs_mkgang(struct inode *dir, struct dentry *dentry, int mode) +spufs_mkgang(struct inode *dir, struct dentry *dentry, umode_t mode) { int ret; struct inode *inode; @@ -521,8 +503,8 @@ spufs_mkgang(struct inode *dir, struct dentry *dentry, int mode) inode->i_fop = &simple_dir_operations; d_instantiate(dentry, inode); - dir->i_nlink++; - dentry->d_inode->i_nlink++; + inc_nlink(dir); + inc_nlink(dentry->d_inode); return ret; out_iput: @@ -531,123 +513,98 @@ out: return ret; } -static int spufs_gang_open(struct dentry *dentry, struct vfsmount *mnt) +static int spufs_gang_open(struct path *path) { int ret; struct file *filp; ret = get_unused_fd(); - if (ret < 0) { - dput(dentry); - mntput(mnt); - goto out; - } + if (ret < 0) + return ret; - filp = dentry_open(dentry, mnt, O_RDONLY); + /* + * get references for dget and mntget, will be released + * in error path of *_open(). + */ + filp = dentry_open(path, O_RDONLY, current_cred()); if (IS_ERR(filp)) { put_unused_fd(ret); - ret = PTR_ERR(filp); - goto out; + return PTR_ERR(filp); } filp->f_op = &simple_dir_operations; fd_install(ret, filp); -out: return ret; } static int spufs_create_gang(struct inode *inode, struct dentry *dentry, - struct vfsmount *mnt, int mode) + struct vfsmount *mnt, umode_t mode) { + struct path path = {.mnt = mnt, .dentry = dentry}; int ret; ret = spufs_mkgang(inode, dentry, mode & S_IRWXUGO); - if (ret) - goto out; - - /* - * get references for dget and mntget, will be released - * in error path of *_open(). - */ - ret = spufs_gang_open(dget(dentry), mntget(mnt)); - if (ret < 0) { - int err = simple_rmdir(inode, dentry); - WARN_ON(err); + if (!ret) { + ret = spufs_gang_open(&path); + if (ret < 0) { + int err = simple_rmdir(inode, dentry); + WARN_ON(err); + } } - -out: - mutex_unlock(&inode->i_mutex); - dput(dentry); return ret; } static struct file_system_type spufs_type; -long spufs_create(struct nameidata *nd, unsigned int flags, mode_t mode, - struct file *filp) +long spufs_create(struct path *path, struct dentry *dentry, + unsigned int flags, umode_t mode, struct file *filp) { - struct dentry *dentry; + struct inode *dir = path->dentry->d_inode; int ret; - ret = -EINVAL; /* check if we are on spufs */ - if (nd->path.dentry->d_sb->s_type != &spufs_type) - goto out; + if (path->dentry->d_sb->s_type != &spufs_type) + return -EINVAL; /* don't accept undefined flags */ if (flags & (~SPU_CREATE_FLAG_ALL)) - goto out; + return -EINVAL; /* only threads can be underneath a gang */ - if (nd->path.dentry != nd->path.dentry->d_sb->s_root) { - if ((flags & SPU_CREATE_GANG) || - !SPUFS_I(nd->path.dentry->d_inode)->i_gang) - goto out; - } - - dentry = lookup_create(nd, 1); - ret = PTR_ERR(dentry); - if (IS_ERR(dentry)) - goto out_dir; + if (path->dentry != path->dentry->d_sb->s_root) + if ((flags & SPU_CREATE_GANG) || !SPUFS_I(dir)->i_gang) + return -EINVAL; - ret = -EEXIST; - if (dentry->d_inode) - goto out_dput; - - mode &= ~current->fs->umask; + mode &= ~current_umask(); if (flags & SPU_CREATE_GANG) - return spufs_create_gang(nd->path.dentry->d_inode, - dentry, nd->path.mnt, mode); + ret = spufs_create_gang(dir, dentry, path->mnt, mode); else - return spufs_create_context(nd->path.dentry->d_inode, - dentry, nd->path.mnt, flags, mode, + ret = spufs_create_context(dir, dentry, path->mnt, flags, mode, filp); + if (ret >= 0) + fsnotify_mkdir(dir, dentry); -out_dput: - dput(dentry); -out_dir: - mutex_unlock(&nd->path.dentry->d_inode->i_mutex); -out: return ret; } /* File system initialization */ enum { - Opt_uid, Opt_gid, Opt_mode, Opt_err, + Opt_uid, Opt_gid, Opt_mode, Opt_debug, Opt_err, }; -static match_table_t spufs_tokens = { - { Opt_uid, "uid=%d" }, - { Opt_gid, "gid=%d" }, - { Opt_mode, "mode=%o" }, - { Opt_err, NULL }, +static const match_table_t spufs_tokens = { + { Opt_uid, "uid=%d" }, + { Opt_gid, "gid=%d" }, + { Opt_mode, "mode=%o" }, + { Opt_debug, "debug" }, + { Opt_err, NULL }, }; static int -spufs_parse_options(char *options, struct inode *root) +spufs_parse_options(struct super_block *sb, char *options, struct inode *root) { char *p; substring_t args[MAX_OPT_ARGS]; @@ -663,18 +620,25 @@ spufs_parse_options(char *options, struct inode *root) case Opt_uid: if (match_int(&args[0], &option)) return 0; - root->i_uid = option; + root->i_uid = make_kuid(current_user_ns(), option); + if (!uid_valid(root->i_uid)) + return 0; break; case Opt_gid: if (match_int(&args[0], &option)) return 0; - root->i_gid = option; + root->i_gid = make_kgid(current_user_ns(), option); + if (!gid_valid(root->i_gid)) + return 0; break; case Opt_mode: if (match_octal(&args[0], &option)) return 0; root->i_mode = option | S_IFDIR; break; + case Opt_debug: + spufs_get_sb_info(sb)->debug = 1; + break; default: return 0; } @@ -731,15 +695,16 @@ spufs_create_root(struct super_block *sb, void *data) inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; SPUFS_I(inode)->i_ctx = NULL; + inc_nlink(inode); ret = -EINVAL; - if (!spufs_parse_options(data, inode)) + if (!spufs_parse_options(sb, data, inode)) goto out_iput; ret = -ENOMEM; - sb->s_root = d_alloc_root(inode); + sb->s_root = d_make_root(inode); if (!sb->s_root) - goto out_iput; + goto out; return 0; out_iput: @@ -751,39 +716,45 @@ out: static int spufs_fill_super(struct super_block *sb, void *data, int silent) { - static struct super_operations s_ops = { + struct spufs_sb_info *info; + static const struct super_operations s_ops = { .alloc_inode = spufs_alloc_inode, .destroy_inode = spufs_destroy_inode, .statfs = simple_statfs, - .delete_inode = spufs_delete_inode, - .drop_inode = generic_delete_inode, + .evict_inode = spufs_evict_inode, .show_options = generic_show_options, }; save_mount_options(sb, data); + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + 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; + sb->s_fs_info = info; return spufs_create_root(sb, data); } -static int -spufs_get_sb(struct file_system_type *fstype, int flags, - const char *name, void *data, struct vfsmount *mnt) +static struct dentry * +spufs_mount(struct file_system_type *fstype, int flags, + const char *name, void *data) { - return get_sb_single(fstype, flags, data, spufs_fill_super, mnt); + return mount_single(fstype, flags, data, spufs_fill_super); } static struct file_system_type spufs_type = { .owner = THIS_MODULE, .name = "spufs", - .get_sb = spufs_get_sb, + .mount = spufs_mount, .kill_sb = kill_litter_super, }; +MODULE_ALIAS_FS("spufs"); static int __init spufs_init(void) { @@ -803,19 +774,19 @@ static int __init spufs_init(void) ret = spu_sched_init(); if (ret) goto out_cache; - ret = register_filesystem(&spufs_type); + ret = register_spu_syscalls(&spufs_calls); if (ret) goto out_sched; - ret = register_spu_syscalls(&spufs_calls); + ret = register_filesystem(&spufs_type); if (ret) - goto out_fs; + goto out_syscalls; spufs_init_isolated_loader(); return 0; -out_fs: - unregister_filesystem(&spufs_type); +out_syscalls: + unregister_spu_syscalls(&spufs_calls); out_sched: spu_sched_exit(); out_cache: diff --git a/arch/powerpc/platforms/cell/spufs/lscsa_alloc.c b/arch/powerpc/platforms/cell/spufs/lscsa_alloc.c index 0e9f325c9ff..147069938cf 100644 --- a/arch/powerpc/platforms/cell/spufs/lscsa_alloc.c +++ b/arch/powerpc/platforms/cell/spufs/lscsa_alloc.c @@ -22,6 +22,7 @@ #include <linux/kernel.h> #include <linux/mm.h> +#include <linux/slab.h> #include <linux/vmalloc.h> #include <asm/spu.h> @@ -35,10 +36,9 @@ static int spu_alloc_lscsa_std(struct spu_state *csa) struct spu_lscsa *lscsa; unsigned char *p; - lscsa = vmalloc(sizeof(struct spu_lscsa)); + lscsa = vzalloc(sizeof(struct spu_lscsa)); if (!lscsa) return -ENOMEM; - memset(lscsa, 0, sizeof(struct spu_lscsa)); csa->lscsa = lscsa; /* Set LS pages reserved to allow for user-space mapping. */ @@ -90,7 +90,7 @@ int spu_alloc_lscsa(struct spu_state *csa) */ for (i = 0; i < SPU_LSCSA_NUM_BIG_PAGES; i++) { /* XXX This is likely to fail, we should use a special pool - * similiar to what hugetlbfs does. + * similar to what hugetlbfs does. */ csa->lscsa_pages[i] = alloc_pages(GFP_KERNEL, SPU_64K_PAGE_ORDER); diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index cac69e11677..4ddf769a64e 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c @@ -11,7 +11,7 @@ #include "spufs.h" /* interrupt-level stop callback function. */ -void spufs_stop_callback(struct spu *spu) +void spufs_stop_callback(struct spu *spu, int irq) { struct spu_context *ctx = spu->ctx; @@ -24,9 +24,18 @@ void spufs_stop_callback(struct spu *spu) */ if (ctx) { /* Copy exception arguments into module specific structure */ - ctx->csa.class_0_pending = spu->class_0_pending; - ctx->csa.dsisr = spu->dsisr; - ctx->csa.dar = spu->dar; + switch(irq) { + case 0 : + ctx->csa.class_0_pending = spu->class_0_pending; + ctx->csa.class_0_dar = spu->class_0_dar; + break; + case 1 : + ctx->csa.class_1_dsisr = spu->class_1_dsisr; + ctx->csa.class_1_dar = spu->class_1_dar; + break; + case 2 : + break; + } /* ensure that the exception status has hit memory before a * thread waiting on the context's stop queue is woken */ @@ -34,11 +43,6 @@ void spufs_stop_callback(struct spu *spu) wake_up_all(&ctx->stop_wq); } - - /* Clear callback arguments from spu structure */ - spu->class_0_pending = 0; - spu->dsisr = 0; - spu->dar = 0; } int spu_stopped(struct spu_context *ctx, u32 *stat) @@ -46,17 +50,25 @@ int spu_stopped(struct spu_context *ctx, u32 *stat) u64 dsisr; u32 stopped; - *stat = ctx->ops->status_read(ctx); + stopped = SPU_STATUS_INVALID_INSTR | SPU_STATUS_SINGLE_STEP | + SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP; - if (test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags)) +top: + *stat = ctx->ops->status_read(ctx); + if (*stat & stopped) { + /* + * If the spu hasn't finished stopping, we need to + * re-read the register to get the stopped value. + */ + if (*stat & SPU_STATUS_RUNNING) + goto top; return 1; + } - stopped = SPU_STATUS_INVALID_INSTR | SPU_STATUS_SINGLE_STEP | - SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP; - if (!(*stat & SPU_STATUS_RUNNING) && (*stat & stopped)) + if (test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags)) return 1; - dsisr = ctx->csa.dsisr; + dsisr = ctx->csa.class_1_dsisr; if (dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)) return 1; @@ -98,13 +110,16 @@ static int spu_setup_isolated(struct spu_context *ctx) != MFC_CNTL_PURGE_DMA_COMPLETE) { if (time_after(jiffies, timeout)) { printk(KERN_ERR "%s: timeout flushing MFC DMA queue\n", - __FUNCTION__); + __func__); ret = -EIO; goto out; } cond_resched(); } + /* clear purge status */ + out_be64(mfc_cntl, 0); + /* put the SPE in kernel mode to allow access to the loader */ sr1 = spu_mfc_sr1_get(ctx->spu); sr1 &= ~MFC_STATE1_PROBLEM_STATE_MASK; @@ -124,7 +139,7 @@ static int spu_setup_isolated(struct spu_context *ctx) status_loading) { if (time_after(jiffies, timeout)) { printk(KERN_ERR "%s: timeout waiting for loader\n", - __FUNCTION__); + __func__); ret = -EIO; goto out_drop_priv; } @@ -134,7 +149,7 @@ static int spu_setup_isolated(struct spu_context *ctx) if (!(status & SPU_STATUS_RUNNING)) { /* If isolated LOAD has failed: run SPU, we will get a stop-and * signal later. */ - pr_debug("%s: isolated LOAD failed\n", __FUNCTION__); + pr_debug("%s: isolated LOAD failed\n", __func__); ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); ret = -EACCES; goto out_drop_priv; @@ -142,7 +157,7 @@ static int spu_setup_isolated(struct spu_context *ctx) if (!(status & SPU_STATUS_ISOLATED_STATE)) { /* This isn't allowed by the CBEA, but check anyway */ - pr_debug("%s: SPU fell out of isolated mode?\n", __FUNCTION__); + pr_debug("%s: SPU fell out of isolated mode?\n", __func__); ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_STOP); ret = -EINVAL; goto out_drop_priv; @@ -194,11 +209,6 @@ static int spu_run_init(struct spu_context *ctx, u32 *npc) (SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE); if (runcntl == 0) runcntl = SPU_RUNCNTL_RUNNABLE; - } - - if (ctx->flags & SPU_CREATE_NOSCHED) { - spuctx_switch_state(ctx, SPU_UTIL_USER); - ctx->ops->runcntl_write(ctx, runcntl); } else { unsigned long privcntl; @@ -207,9 +217,15 @@ static int spu_run_init(struct spu_context *ctx, u32 *npc) else privcntl = SPU_PRIVCNTL_MODE_NORMAL; - ctx->ops->npc_write(ctx, *npc); ctx->ops->privcntl_write(ctx, privcntl); - ctx->ops->runcntl_write(ctx, runcntl); + ctx->ops->npc_write(ctx, *npc); + } + + ctx->ops->runcntl_write(ctx, runcntl); + + if (ctx->flags & SPU_CREATE_NOSCHED) { + spuctx_switch_state(ctx, SPU_UTIL_USER); + } else { if (ctx->state == SPU_STATE_SAVED) { ret = spu_activate(ctx, 0); @@ -236,6 +252,7 @@ static int spu_run_fini(struct spu_context *ctx, u32 *npc, spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED); clear_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags); + spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, *status); spu_release(ctx); if (signal_pending(current)) @@ -282,7 +299,7 @@ static int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret, break; default: printk(KERN_WARNING "%s: unexpected return code %ld\n", - __FUNCTION__, *spu_ret); + __func__, *spu_ret); ret = 0; } return ret; @@ -294,7 +311,7 @@ static int spu_process_callback(struct spu_context *ctx) u32 ls_pointer, npc; void __iomem *ls; long spu_ret; - int ret, ret2; + int ret; /* get syscall block from local store */ npc = ctx->ops->npc_read(ctx) & ~3; @@ -316,13 +333,15 @@ static int spu_process_callback(struct spu_context *ctx) if (spu_ret <= -ERESTARTSYS) { ret = spu_handle_restartsys(ctx, &spu_ret, &npc); } - ret2 = spu_acquire(ctx); + mutex_lock(&ctx->state_mutex); if (ret == -ERESTARTSYS) return ret; - if (ret2) - return -EINTR; } + /* need to re-get the ls, as it may have changed when we released the + * spu */ + ls = (void __iomem *)ctx->ops->get_ls(ctx); + /* write result, jump over indirect pointer */ memcpy_toio(ls + ls_pointer, &spu_ret, sizeof(spu_ret)); ctx->ops->npc_write(ctx, npc); @@ -339,13 +358,14 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) if (mutex_lock_interruptible(&ctx->run_mutex)) return -ERESTARTSYS; - spu_enable_spu(ctx); ctx->event_return = 0; ret = spu_acquire(ctx); if (ret) goto out_unlock; + spu_enable_spu(ctx); + spu_update_sched_info(ctx); ret = spu_run_init(ctx, npc); diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 00528ef84ad..4a0a64fe25d 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -22,11 +22,12 @@ #undef DEBUG -#include <linux/module.h> #include <linux/errno.h> #include <linux/sched.h> +#include <linux/sched/rt.h> #include <linux/kernel.h> #include <linux/mm.h> +#include <linux/slab.h> #include <linux/completion.h> #include <linux/vmalloc.h> #include <linux/smp.h> @@ -39,7 +40,6 @@ #include <linux/pid_namespace.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> -#include <linux/marker.h> #include <asm/io.h> #include <asm/mmu_context.h> @@ -47,6 +47,8 @@ #include <asm/spu_csa.h> #include <asm/spu_priv1.h> #include "spufs.h" +#define CREATE_TRACE_POINTS +#include "sputrace.h" struct spu_prio_array { DECLARE_BITMAP(bitmap, MAX_PRIO); @@ -81,7 +83,6 @@ static struct timer_list spuloadavg_timer; #define MIN_SPU_TIMESLICE max(5 * HZ / (1000 * SPUSCHED_TICK), 1) #define DEF_SPU_TIMESLICE (100 * HZ / (1000 * SPUSCHED_TICK)) -#define MAX_USER_PRIO (MAX_PRIO - MAX_RT_PRIO) #define SCALE_PRIO(x, prio) \ max(x * (MAX_PRIO - prio) / (MAX_USER_PRIO / 2), MIN_SPU_TIMESLICE) @@ -139,7 +140,10 @@ void __spu_update_sched_info(struct spu_context *ctx) * runqueue. The context will be rescheduled on the proper node * if it is timesliced or preempted. */ - ctx->cpus_allowed = current->cpus_allowed; + cpumask_copy(&ctx->cpus_allowed, tsk_cpus_allowed(current)); + + /* Save the current cpu id for spu interrupt routing. */ + ctx->last_ran = raw_smp_processor_id(); } void spu_update_sched_info(struct spu_context *ctx) @@ -163,9 +167,9 @@ void spu_update_sched_info(struct spu_context *ctx) static int __node_allowed(struct spu_context *ctx, int node) { if (nr_cpus_node(node)) { - cpumask_t mask = node_to_cpumask(node); + const struct cpumask *mask = cpumask_of_node(node); - if (cpus_intersects(mask, ctx->cpus_allowed)) + if (cpumask_intersects(mask, &ctx->cpus_allowed)) return 1; } @@ -227,22 +231,26 @@ static void spu_bind_context(struct spu *spu, struct spu_context *ctx) ctx->stats.slb_flt_base = spu->stats.slb_flt; ctx->stats.class2_intr_base = spu->stats.class2_intr; + spu_associate_mm(spu, ctx->owner); + + spin_lock_irq(&spu->register_lock); spu->ctx = ctx; spu->flags = 0; ctx->spu = spu; ctx->ops = &spu_hw_ops; spu->pid = current->pid; spu->tgid = current->tgid; - spu_associate_mm(spu, ctx->owner); spu->ibox_callback = spufs_ibox_callback; spu->wbox_callback = spufs_wbox_callback; spu->stop_callback = spufs_stop_callback; spu->mfc_callback = spufs_mfc_callback; - mb(); + spin_unlock_irq(&spu->register_lock); + spu_unmap_mappings(ctx); + + spu_switch_log_notify(spu, ctx, SWITCH_LOG_START, 0); spu_restore(&ctx->csa, spu); spu->timestamp = jiffies; - spu_cpu_affinity_set(spu, raw_smp_processor_id()); spu_switch_notify(spu, ctx); ctx->state = SPU_STATE_RUNNABLE; @@ -305,11 +313,35 @@ static struct spu *aff_ref_location(struct spu_context *ctx, int mem_aff, */ node = cpu_to_node(raw_smp_processor_id()); for (n = 0; n < MAX_NUMNODES; n++, node++) { + /* + * "available_spus" counts how many spus are not potentially + * going to be used by other affinity gangs whose reference + * context is already in place. Although this code seeks to + * avoid having affinity gangs with a summed amount of + * contexts bigger than the amount of spus in the node, + * this may happen sporadically. In this case, available_spus + * becomes negative, which is harmless. + */ + int available_spus; + node = (node < MAX_NUMNODES) ? node : 0; if (!node_allowed(ctx, node)) continue; + + available_spus = 0; mutex_lock(&cbe_spu_info[node].list_mutex); list_for_each_entry(spu, &cbe_spu_info[node].spus, cbe_list) { + if (spu->ctx && spu->ctx->gang && !spu->ctx->aff_offset + && spu->ctx->gang->aff_ref_spu) + available_spus -= spu->ctx->gang->contexts; + available_spus++; + } + if (available_spus < ctx->gang->contexts) { + mutex_unlock(&cbe_spu_info[node].list_mutex); + continue; + } + + list_for_each_entry(spu, &cbe_spu_info[node].spus, cbe_list) { if ((!mem_aff || spu->has_mem_affinity) && sched_spu(spu)) { mutex_unlock(&cbe_spu_info[node].list_mutex); @@ -382,6 +414,9 @@ static int has_affinity(struct spu_context *ctx) if (list_empty(&ctx->aff_list)) return 0; + if (atomic_read(&ctx->gang->aff_sched_count) == 0) + ctx->gang->aff_ref_spu = NULL; + if (!gang->aff_ref_spu) { if (!(gang->aff_flags & AFF_MERGED)) aff_merge_remaining_ctxs(gang); @@ -400,6 +435,8 @@ static int has_affinity(struct spu_context *ctx) */ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx) { + u32 status; + spu_context_trace(spu_unbind_context__enter, ctx, spu); spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); @@ -407,30 +444,34 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx) if (spu->ctx->flags & SPU_CREATE_NOSCHED) atomic_dec(&cbe_spu_info[spu->node].reserved_spus); - if (ctx->gang){ - mutex_lock(&ctx->gang->aff_mutex); - if (has_affinity(ctx)) { - if (atomic_dec_and_test(&ctx->gang->aff_sched_count)) - ctx->gang->aff_ref_spu = NULL; - } - mutex_unlock(&ctx->gang->aff_mutex); - } + if (ctx->gang) + /* + * If ctx->gang->aff_sched_count is positive, SPU affinity is + * being considered in this gang. Using atomic_dec_if_positive + * allow us to skip an explicit check for affinity in this gang + */ + atomic_dec_if_positive(&ctx->gang->aff_sched_count); spu_switch_notify(spu, NULL); spu_unmap_mappings(ctx); spu_save(&ctx->csa, spu); + spu_switch_log_notify(spu, ctx, SWITCH_LOG_STOP, 0); + + spin_lock_irq(&spu->register_lock); spu->timestamp = jiffies; ctx->state = SPU_STATE_SAVED; spu->ibox_callback = NULL; spu->wbox_callback = NULL; spu->stop_callback = NULL; spu->mfc_callback = NULL; - spu_associate_mm(spu, NULL); spu->pid = 0; spu->tgid = 0; ctx->ops = &spu_backing_ops; spu->flags = 0; spu->ctx = NULL; + spin_unlock_irq(&spu->register_lock); + + spu_associate_mm(spu, NULL); ctx->stats.slb_flt += (spu->stats.slb_flt - ctx->stats.slb_flt_base); @@ -440,6 +481,9 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx) /* This maps the underlying spu state to idle */ spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED); ctx->spu = NULL; + + if (spu_stopped(ctx, &status)) + wake_up_all(&ctx->stop_wq); } /** @@ -465,7 +509,7 @@ static void __spu_add_to_rq(struct spu_context *ctx) list_add_tail(&ctx->rq, &spu_prio->runq[ctx->prio]); set_bit(ctx->prio, spu_prio->bitmap); if (!spu_prio->nr_waiting++) - __mod_timer(&spusched_timer, jiffies + SPUSCHED_TICK); + mod_timer(&spusched_timer, jiffies + SPUSCHED_TICK); } } @@ -545,10 +589,7 @@ static struct spu *spu_get_idle(struct spu_context *ctx) goto found; mutex_unlock(&cbe_spu_info[node].list_mutex); - mutex_lock(&ctx->gang->aff_mutex); - if (atomic_dec_and_test(&ctx->gang->aff_sched_count)) - ctx->gang->aff_ref_spu = NULL; - mutex_unlock(&ctx->gang->aff_mutex); + atomic_dec(&ctx->gang->aff_sched_count); goto not_found; } mutex_unlock(&ctx->gang->aff_mutex); @@ -591,7 +632,7 @@ static struct spu *find_victim(struct spu_context *ctx) struct spu *spu; int node, n; - spu_context_nospu_trace(spu_find_vitim__enter, ctx); + spu_context_nospu_trace(spu_find_victim__enter, ctx); /* * Look for a possible preemption candidate on the local node first. @@ -613,9 +654,12 @@ static struct spu *find_victim(struct spu_context *ctx) if (tmp && tmp->prio > ctx->prio && !(tmp->flags & SPU_CREATE_NOSCHED) && - (!victim || tmp->prio > victim->prio)) + (!victim || tmp->prio > victim->prio)) { victim = spu->ctx; + } } + if (victim) + get_spu_context(victim); mutex_unlock(&cbe_spu_info[node].list_mutex); if (victim) { @@ -630,6 +674,7 @@ static struct spu *find_victim(struct spu_context *ctx) * look at another context or give up after X retries. */ if (!mutex_trylock(&victim->state_mutex)) { + put_spu_context(victim); victim = NULL; goto restart; } @@ -642,6 +687,7 @@ static struct spu *find_victim(struct spu_context *ctx) * restart the search. */ mutex_unlock(&victim->state_mutex); + put_spu_context(victim); victim = NULL; goto restart; } @@ -655,9 +701,11 @@ static struct spu *find_victim(struct spu_context *ctx) victim->stats.invol_ctx_switch++; spu->stats.invol_ctx_switch++; - spu_add_to_rq(victim); + if (test_bit(SPU_SCHED_SPU_RUN, &victim->sched_flags)) + spu_add_to_rq(victim); mutex_unlock(&victim->state_mutex); + put_spu_context(victim); return spu; } @@ -693,17 +741,33 @@ static void spu_schedule(struct spu *spu, struct spu_context *ctx) /* not a candidate for interruptible because it's called either from the scheduler thread or from spu_deactivate */ mutex_lock(&ctx->state_mutex); - __spu_schedule(spu, ctx); + if (ctx->state == SPU_STATE_SAVED) + __spu_schedule(spu, ctx); spu_release(ctx); } -static void spu_unschedule(struct spu *spu, struct spu_context *ctx) +/** + * spu_unschedule - remove a context from a spu, and possibly release it. + * @spu: The SPU to unschedule from + * @ctx: The context currently scheduled on the SPU + * @free_spu Whether to free the SPU for other contexts + * + * Unbinds the context @ctx from the SPU @spu. If @free_spu is non-zero, the + * SPU is made available for other contexts (ie, may be returned by + * spu_get_idle). If this is zero, the caller is expected to schedule another + * context to this spu. + * + * Should be called with ctx->state_mutex held. + */ +static void spu_unschedule(struct spu *spu, struct spu_context *ctx, + int free_spu) { int node = spu->node; mutex_lock(&cbe_spu_info[node].list_mutex); cbe_spu_info[node].nr_active--; - spu->alloc_state = SPU_FREE; + if (free_spu) + spu->alloc_state = SPU_FREE; spu_unbind_context(spu, ctx); ctx->stats.invol_ctx_switch++; spu->stats.invol_ctx_switch++; @@ -781,7 +845,7 @@ static struct spu_context *grab_runnable_context(int prio, int node) struct list_head *rq = &spu_prio->runq[best]; list_for_each_entry(ctx, rq, rq) { - /* XXX(hch): check for affinity here aswell */ + /* XXX(hch): check for affinity here as well */ if (__node_allowed(ctx, node)) { __spu_del_from_rq(ctx); goto found; @@ -803,7 +867,7 @@ static int __spu_deactivate(struct spu_context *ctx, int force, int max_prio) if (spu) { new = grab_runnable_context(max_prio, spu->node); if (new || force) { - spu_unschedule(spu, ctx); + spu_unschedule(spu, ctx, new == NULL); if (new) { if (new->flags & SPU_CREATE_NOSCHED) wake_up(&new->stop_wq); @@ -876,12 +940,13 @@ static noinline void spusched_tick(struct spu_context *ctx) new = grab_runnable_context(ctx->prio + 1, spu->node); if (new) { - spu_unschedule(spu, ctx); + spu_unschedule(spu, ctx, 0); if (test_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags)) spu_add_to_rq(ctx); } else { spu_context_nospu_trace(spusched_tick__newslice, ctx); - ctx->time_slice++; + if (!ctx->time_slice) + ctx->time_slice++; } out: spu_release(ctx); @@ -955,9 +1020,11 @@ static int spusched_thread(void *unused) struct spu_context *ctx = spu->ctx; if (ctx) { + get_spu_context(ctx); mutex_unlock(mtx); spusched_tick(ctx); mutex_lock(mtx); + put_spu_context(ctx); } } mutex_unlock(mtx); @@ -975,6 +1042,7 @@ void spuctx_switch_state(struct spu_context *ctx, struct timespec ts; struct spu *spu; enum spu_utilization_state old_state; + int node; ktime_get_ts(&ts); curtime = timespec_to_ns(&ts); @@ -996,6 +1064,11 @@ void spuctx_switch_state(struct spu_context *ctx, spu->stats.times[old_state] += delta; spu->stats.util_state = new_state; spu->stats.tstamp = curtime; + node = spu->node; + if (old_state == SPU_UTIL_USER) + atomic_dec(&cbe_spu_info[node].busy_spus); + if (new_state == SPU_UTIL_USER) + atomic_inc(&cbe_spu_info[node].busy_spus); } } @@ -1021,7 +1094,7 @@ static int show_spu_loadavg(struct seq_file *s, void *private) LOAD_INT(c), LOAD_FRAC(c), count_active_contexts(), atomic_read(&nr_spu_contexts), - current->nsproxy->pid_ns->last_pid); + task_active_pid_ns(current)->last_pid); return 0; } @@ -1063,10 +1136,9 @@ int __init spu_sched_init(void) mod_timer(&spuloadavg_timer, 0); - entry = create_proc_entry("spu_loadavg", 0, NULL); + entry = proc_create("spu_loadavg", 0, NULL, &spu_loadavg_fops); if (!entry) goto out_stop_kthread; - entry->proc_fops = &spu_loadavg_fops; pr_debug("spusched: tick: %d, min ticks: %d, default ticks: %d\n", SPUSCHED_TICK, MIN_SPU_TIMESLICE, DEF_SPU_TIMESLICE); diff --git a/arch/powerpc/platforms/cell/spufs/spu_restore.c b/arch/powerpc/platforms/cell/spufs/spu_restore.c index 21a9c952d88..72c905f1ee7 100644 --- a/arch/powerpc/platforms/cell/spufs/spu_restore.c +++ b/arch/powerpc/platforms/cell/spufs/spu_restore.c @@ -284,7 +284,7 @@ static inline void restore_complete(void) exit_instrs[3] = BR_INSTR; break; default: - /* SPU_Status[R]=1. No additonal instructions. */ + /* SPU_Status[R]=1. No additional instructions. */ break; } spu_sync(); diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index cdc515182f8..bcfd6f063ef 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -32,6 +32,12 @@ #include <asm/spu_csa.h> #include <asm/spu_info.h> +#define SPUFS_PS_MAP_SIZE 0x20000 +#define SPUFS_MFC_MAP_SIZE 0x1000 +#define SPUFS_CNTL_MAP_SIZE 0x1000 +#define SPUFS_SIGNAL_MAP_SIZE PAGE_SIZE +#define SPUFS_MSS_MAP_SIZE 0x1000 + /* The magic number for our file system */ enum { SPUFS_MAGIC = 0x23c9b64e, @@ -47,6 +53,29 @@ enum { SPU_SCHED_SPU_RUN, /* context is within spu_run */ }; +enum { + SWITCH_LOG_BUFSIZE = 4096, +}; + +enum { + SWITCH_LOG_START, + SWITCH_LOG_STOP, + SWITCH_LOG_EXIT, +}; + +struct switch_log { + wait_queue_head_t wait; + unsigned long head; + unsigned long tail; + struct switch_log_entry { + struct timespec tstamp; + s32 spu_id; + u32 type; + u32 val; + u64 timebase; + } log[]; +}; + struct spu_context { struct spu *spu; /* pointer to a physical SPU */ struct spu_state csa; /* SPU context save area. */ @@ -97,6 +126,7 @@ struct spu_context { cpumask_t cpus_allowed; int policy; int prio; + int last_ran; /* statistics */ struct { @@ -116,6 +146,9 @@ struct spu_context { unsigned long long libassist; } stats; + /* context switch log */ + struct switch_log *switch_log; + struct list_head aff_list; int aff_head; int aff_offset; @@ -200,17 +233,26 @@ struct spufs_inode_info { #define SPUFS_I(inode) \ container_of(inode, struct spufs_inode_info, vfs_inode) -extern struct tree_descr spufs_dir_contents[]; -extern struct tree_descr spufs_dir_nosched_contents[]; +struct spufs_tree_descr { + const char *name; + const struct file_operations *ops; + umode_t mode; + size_t size; +}; + +extern const struct spufs_tree_descr spufs_dir_contents[]; +extern const struct spufs_tree_descr spufs_dir_nosched_contents[]; +extern const struct spufs_tree_descr spufs_dir_debug_contents[]; /* system call implementation */ extern struct spufs_calls spufs_calls; +struct coredump_params; long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *status); -long spufs_create(struct nameidata *nd, unsigned int flags, - mode_t mode, struct file *filp); +long spufs_create(struct path *nd, struct dentry *dentry, unsigned int flags, + umode_t mode, struct file *filp); /* ELF coredump callbacks for writing SPU ELF notes */ extern int spufs_coredump_extra_notes_size(void); -extern int spufs_coredump_extra_notes_write(struct file *file, loff_t *foffset); +extern int spufs_coredump_extra_notes_write(struct coredump_params *cprm); extern const struct file_operations spufs_context_fops; @@ -256,6 +298,8 @@ int spu_activate(struct spu_context *ctx, unsigned long flags); void spu_deactivate(struct spu_context *ctx); void spu_yield(struct spu_context *ctx); void spu_switch_notify(struct spu *spu, struct spu_context *ctx); +void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx, + u32 type, u32 val); void spu_set_timeslice(struct spu_context *ctx); void spu_update_sched_info(struct spu_context *ctx); void __spu_update_sched_info(struct spu_context *ctx); @@ -270,7 +314,7 @@ extern char *isolated_loader; * we need to call spu_release(ctx) before sleeping, and * then spu_acquire(ctx) when awoken. * - * Returns with state_mutex re-acquired when successfull or + * Returns with state_mutex re-acquired when successful or * with -ERESTARTSYS and the state_mutex dropped when interrupted. */ @@ -302,7 +346,7 @@ 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); -void spufs_stop_callback(struct spu *spu); +void spufs_stop_callback(struct spu *spu, int irq); void spufs_mfc_callback(struct spu *spu); void spufs_dma_callback(struct spu *spu, int type); @@ -314,7 +358,7 @@ struct spufs_coredump_reader { u64 (*get)(struct spu_context *ctx); size_t size; }; -extern struct spufs_coredump_reader spufs_coredump_read[]; +extern const struct spufs_coredump_reader spufs_coredump_read[]; extern int spufs_coredump_num_notes; extern int spu_init_csa(struct spu_state *csa); @@ -329,9 +373,4 @@ extern void spu_free_lscsa(struct spu_state *csa); extern void spuctx_switch_state(struct spu_context *ctx, enum spu_utilization_state new_state); -#define spu_context_trace(name, ctx, spu) \ - trace_mark(name, "%p %p", ctx, spu); -#define spu_context_nospu_trace(name, ctx) \ - trace_mark(name, "%p", ctx); - #endif diff --git a/arch/powerpc/platforms/cell/spufs/sputrace.c b/arch/powerpc/platforms/cell/spufs/sputrace.c deleted file mode 100644 index 79aa773f3c9..00000000000 --- a/arch/powerpc/platforms/cell/spufs/sputrace.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 2007 IBM Deutschland Entwicklung GmbH - * Released under GPL v2. - * - * Partially based on net/ipv4/tcp_probe.c. - * - * Simple tracing facility for spu contexts. - */ -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/marker.h> -#include <linux/proc_fs.h> -#include <linux/wait.h> -#include <asm/atomic.h> -#include <asm/uaccess.h> -#include "spufs.h" - -struct spu_probe { - const char *name; - const char *format; - marker_probe_func *probe_func; -}; - -struct sputrace { - ktime_t tstamp; - int owner_tid; /* owner */ - int curr_tid; - const char *name; - int number; -}; - -static int bufsize __read_mostly = 16384; -MODULE_PARM_DESC(bufsize, "Log buffer size (number of records)"); -module_param(bufsize, int, 0); - - -static DEFINE_SPINLOCK(sputrace_lock); -static DECLARE_WAIT_QUEUE_HEAD(sputrace_wait); -static ktime_t sputrace_start; -static unsigned long sputrace_head, sputrace_tail; -static struct sputrace *sputrace_log; - -static int sputrace_used(void) -{ - return (sputrace_head - sputrace_tail) % bufsize; -} - -static inline int sputrace_avail(void) -{ - return bufsize - sputrace_used(); -} - -static int sputrace_sprint(char *tbuf, int n) -{ - const struct sputrace *t = sputrace_log + sputrace_tail % bufsize; - struct timespec tv = - ktime_to_timespec(ktime_sub(t->tstamp, sputrace_start)); - - return snprintf(tbuf, n, - "[%lu.%09lu] %d: %s (ctxthread = %d, spu = %d)\n", - (unsigned long) tv.tv_sec, - (unsigned long) tv.tv_nsec, - t->curr_tid, - t->name, - t->owner_tid, - t->number); -} - -static ssize_t sputrace_read(struct file *file, char __user *buf, - size_t len, loff_t *ppos) -{ - int error = 0, cnt = 0; - - if (!buf || len < 0) - return -EINVAL; - - while (cnt < len) { - char tbuf[128]; - int width; - - error = wait_event_interruptible(sputrace_wait, - sputrace_used() > 0); - if (error) - break; - - spin_lock(&sputrace_lock); - if (sputrace_head == sputrace_tail) { - spin_unlock(&sputrace_lock); - continue; - } - - width = sputrace_sprint(tbuf, sizeof(tbuf)); - if (width < len) - sputrace_tail = (sputrace_tail + 1) % bufsize; - spin_unlock(&sputrace_lock); - - if (width >= len) - break; - - error = copy_to_user(buf + cnt, tbuf, width); - if (error) - break; - cnt += width; - } - - return cnt == 0 ? error : cnt; -} - -static int sputrace_open(struct inode *inode, struct file *file) -{ - spin_lock(&sputrace_lock); - sputrace_head = sputrace_tail = 0; - sputrace_start = ktime_get(); - spin_unlock(&sputrace_lock); - - return 0; -} - -static const struct file_operations sputrace_fops = { - .owner = THIS_MODULE, - .open = sputrace_open, - .read = sputrace_read, -}; - -static void sputrace_log_item(const char *name, struct spu_context *ctx, - struct spu *spu) -{ - spin_lock(&sputrace_lock); - if (sputrace_avail() > 1) { - struct sputrace *t = sputrace_log + sputrace_head; - - t->tstamp = ktime_get(); - t->owner_tid = ctx->tid; - t->name = name; - t->curr_tid = current->pid; - t->number = spu ? spu->number : -1; - - sputrace_head = (sputrace_head + 1) % bufsize; - } else { - printk(KERN_WARNING - "sputrace: lost samples due to full buffer.\n"); - } - spin_unlock(&sputrace_lock); - - wake_up(&sputrace_wait); -} - -static void spu_context_event(void *probe_private, void *call_data, - const char *format, va_list *args) -{ - struct spu_probe *p = probe_private; - struct spu_context *ctx; - struct spu *spu; - - ctx = va_arg(*args, struct spu_context *); - spu = va_arg(*args, struct spu *); - - sputrace_log_item(p->name, ctx, spu); -} - -static void spu_context_nospu_event(void *probe_private, void *call_data, - const char *format, va_list *args) -{ - struct spu_probe *p = probe_private; - struct spu_context *ctx; - - ctx = va_arg(*args, struct spu_context *); - - sputrace_log_item(p->name, ctx, NULL); -} - -struct spu_probe spu_probes[] = { - { "spu_bind_context__enter", "%p %p", spu_context_event }, - { "spu_unbind_context__enter", "%p %p", spu_context_event }, - { "spu_get_idle__enter", "%p", spu_context_nospu_event }, - { "spu_get_idle__found", "%p %p", spu_context_event }, - { "spu_get_idle__not_found", "%p", spu_context_nospu_event }, - { "spu_find_victim__enter", "%p", spu_context_nospu_event }, - { "spusched_tick__preempt", "%p %p", spu_context_event }, - { "spusched_tick__newslice", "%p", spu_context_nospu_event }, - { "spu_yield__enter", "%p", spu_context_nospu_event }, - { "spu_deactivate__enter", "%p", spu_context_nospu_event }, - { "__spu_deactivate__unload", "%p %p", spu_context_event }, - { "spufs_ps_nopfn__enter", "%p", spu_context_nospu_event }, - { "spufs_ps_nopfn__sleep", "%p", spu_context_nospu_event }, - { "spufs_ps_nopfn__wake", "%p %p", spu_context_event }, - { "spufs_ps_nopfn__insert", "%p %p", spu_context_event }, - { "spu_acquire_saved__enter", "%p", spu_context_nospu_event }, - { "destroy_spu_context__enter", "%p", spu_context_nospu_event }, - { "spufs_stop_callback__enter", "%p %p", spu_context_event }, -}; - -static int __init sputrace_init(void) -{ - struct proc_dir_entry *entry; - int i, error = -ENOMEM; - - sputrace_log = kcalloc(sizeof(struct sputrace), - bufsize, GFP_KERNEL); - if (!sputrace_log) - goto out; - - entry = create_proc_entry("sputrace", S_IRUSR, NULL); - if (!entry) - goto out_free_log; - entry->proc_fops = &sputrace_fops; - - for (i = 0; i < ARRAY_SIZE(spu_probes); i++) { - struct spu_probe *p = &spu_probes[i]; - - error = marker_probe_register(p->name, p->format, - p->probe_func, p); - if (error) - printk(KERN_INFO "Unable to register probe %s\n", - p->name); - } - - return 0; - -out_free_log: - kfree(sputrace_log); -out: - return -ENOMEM; -} - -static void __exit sputrace_exit(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(spu_probes); i++) - marker_probe_unregister(spu_probes[i].name, - spu_probes[i].probe_func, &spu_probes[i]); - - remove_proc_entry("sputrace", NULL); - kfree(sputrace_log); -} - -module_init(sputrace_init); -module_exit(sputrace_exit); - -MODULE_LICENSE("GPL"); diff --git a/arch/powerpc/platforms/cell/spufs/sputrace.h b/arch/powerpc/platforms/cell/spufs/sputrace.h new file mode 100644 index 00000000000..db2656aa410 --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/sputrace.h @@ -0,0 +1,39 @@ +#if !defined(_TRACE_SPUFS_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_SPUFS_H + +#include <linux/tracepoint.h> + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM spufs + +TRACE_EVENT(spufs_context, + TP_PROTO(struct spu_context *ctx, struct spu *spu, const char *name), + TP_ARGS(ctx, spu, name), + + TP_STRUCT__entry( + __field(const char *, name) + __field(int, owner_tid) + __field(int, number) + ), + + TP_fast_assign( + __entry->name = name; + __entry->owner_tid = ctx->tid; + __entry->number = spu ? spu->number : -1; + ), + + TP_printk("%s (ctxthread = %d, spu = %d)", + __entry->name, __entry->owner_tid, __entry->number) +); + +#define spu_context_trace(name, ctx, spu) \ + trace_spufs_context(ctx, spu, __stringify(name)) +#define spu_context_nospu_trace(name, ctx) \ + trace_spufs_context(ctx, NULL, __stringify(name)) + +#endif /* _TRACE_SPUFS_H */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE sputrace +#include <trace/define_trace.h> diff --git a/arch/powerpc/platforms/cell/spufs/switch.c b/arch/powerpc/platforms/cell/spufs/switch.c index e9dc7a55d1b..dde35551e74 100644 --- a/arch/powerpc/platforms/cell/spufs/switch.c +++ b/arch/powerpc/platforms/cell/spufs/switch.c @@ -32,7 +32,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/module.h> +#include <linux/export.h> #include <linux/errno.h> #include <linux/hardirq.h> #include <linux/sched.h> @@ -132,6 +132,14 @@ static inline void disable_interrupts(struct spu_state *csa, struct spu *spu) spu_int_mask_set(spu, 2, 0ul); eieio(); spin_unlock_irq(&spu->register_lock); + + /* + * This flag needs to be set before calling synchronize_irq so + * that the update will be visible to the relevant handlers + * via a simple load. + */ + set_bit(SPU_CONTEXT_SWITCH_PENDING, &spu->flags); + clear_bit(SPU_CONTEXT_FAULT_PENDING, &spu->flags); synchronize_irq(spu->irqs[0]); synchronize_irq(spu->irqs[1]); synchronize_irq(spu->irqs[2]); @@ -166,9 +174,8 @@ static inline void set_switch_pending(struct spu_state *csa, struct spu *spu) /* Save, Step 7: * Restore, Step 5: * Set a software context switch pending flag. + * Done above in Step 3 - disable_interrupts(). */ - set_bit(SPU_CONTEXT_SWITCH_PENDING, &spu->flags); - mb(); } static inline void save_mfc_cntl(struct spu_state *csa, struct spu *spu) @@ -186,20 +193,21 @@ static inline void save_mfc_cntl(struct spu_state *csa, struct spu *spu) MFC_CNTL_SUSPEND_COMPLETE); /* fall through */ case MFC_CNTL_SUSPEND_COMPLETE: - if (csa) { + if (csa) csa->priv2.mfc_control_RW = - MFC_CNTL_SUSPEND_MASK | + in_be64(&priv2->mfc_control_RW) | MFC_CNTL_SUSPEND_DMA_QUEUE; - } break; case MFC_CNTL_NORMAL_DMA_QUEUE_OPERATION: out_be64(&priv2->mfc_control_RW, MFC_CNTL_SUSPEND_DMA_QUEUE); POLL_WHILE_FALSE((in_be64(&priv2->mfc_control_RW) & MFC_CNTL_SUSPEND_DMA_STATUS_MASK) == MFC_CNTL_SUSPEND_COMPLETE); - if (csa) { - csa->priv2.mfc_control_RW = 0; - } + if (csa) + csa->priv2.mfc_control_RW = + in_be64(&priv2->mfc_control_RW) & + ~MFC_CNTL_SUSPEND_DMA_QUEUE & + ~MFC_CNTL_SUSPEND_MASK; break; } } @@ -249,16 +257,21 @@ static inline void save_spu_status(struct spu_state *csa, struct spu *spu) } } -static inline void save_mfc_decr(struct spu_state *csa, struct spu *spu) +static inline void save_mfc_stopped_status(struct spu_state *csa, + struct spu *spu) { struct spu_priv2 __iomem *priv2 = spu->priv2; + const u64 mask = MFC_CNTL_DECREMENTER_RUNNING | + MFC_CNTL_DMA_QUEUES_EMPTY; /* Save, Step 12: * Read MFC_CNTL[Ds]. Update saved copy of * CSA.MFC_CNTL[Ds]. + * + * update: do the same with MFC_CNTL[Q]. */ - csa->priv2.mfc_control_RW |= - in_be64(&priv2->mfc_control_RW) & MFC_CNTL_DECREMENTER_RUNNING; + csa->priv2.mfc_control_RW &= ~mask; + csa->priv2.mfc_control_RW |= in_be64(&priv2->mfc_control_RW) & mask; } static inline void halt_mfc_decr(struct spu_state *csa, struct spu *spu) @@ -462,7 +475,9 @@ static inline void purge_mfc_queue(struct spu_state *csa, struct spu *spu) * Restore, Step 14. * Write MFC_CNTL[Pc]=1 (purge queue). */ - out_be64(&priv2->mfc_control_RW, MFC_CNTL_PURGE_DMA_REQUEST); + out_be64(&priv2->mfc_control_RW, + MFC_CNTL_PURGE_DMA_REQUEST | + MFC_CNTL_SUSPEND_MASK); eieio(); } @@ -725,10 +740,14 @@ static inline void set_switch_active(struct spu_state *csa, struct spu *spu) /* Save, Step 48: * Restore, Step 23. * Change the software context switch pending flag - * to context switch active. + * to context switch active. This implementation does + * not uses a switch active flag. * - * This implementation does not uses a switch active flag. + * Now that we have saved the mfc in the csa, we can add in the + * restart command if an exception occurred. */ + if (test_bit(SPU_CONTEXT_FAULT_PENDING, &spu->flags)) + csa->priv2.mfc_control_RW |= MFC_CNTL_RESTART_DMA_COMMAND; clear_bit(SPU_CONTEXT_SWITCH_PENDING, &spu->flags); mb(); } @@ -1690,6 +1709,13 @@ static inline void restore_mfc_sr1(struct spu_state *csa, struct spu *spu) eieio(); } +static inline void set_int_route(struct spu_state *csa, struct spu *spu) +{ + struct spu_context *ctx = spu->ctx; + + spu_cpu_affinity_set(spu, ctx->last_ran); +} + static inline void restore_other_spu_access(struct spu_state *csa, struct spu *spu) { @@ -1721,15 +1747,15 @@ static inline void restore_mfc_cntl(struct spu_state *csa, struct spu *spu) */ out_be64(&priv2->mfc_control_RW, csa->priv2.mfc_control_RW); eieio(); + /* - * FIXME: this is to restart a DMA that we were processing - * before the save. better remember the fault information - * in the csa instead. + * The queue is put back into the same state that was evident prior to + * the context switch. The suspend flag is added to the saved state in + * the csa, if the operational state was suspending or suspended. In + * this case, the code that suspended the mfc is responsible for + * continuing it. Note that SPE faults do not change the operational + * state of the spu. */ - if ((csa->priv2.mfc_control_RW & MFC_CNTL_SUSPEND_DMA_QUEUE_MASK)) { - out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND); - eieio(); - } } static inline void enable_user_access(struct spu_state *csa, struct spu *spu) @@ -1788,7 +1814,7 @@ static int quiece_spu(struct spu_state *prev, struct spu *spu) save_spu_runcntl(prev, spu); /* Step 9. */ save_mfc_sr1(prev, spu); /* Step 10. */ save_spu_status(prev, spu); /* Step 11. */ - save_mfc_decr(prev, spu); /* Step 12. */ + save_mfc_stopped_status(prev, spu); /* Step 12. */ halt_mfc_decr(prev, spu); /* Step 13. */ save_timebase(prev, spu); /* Step 14. */ remove_other_spu_access(prev, spu); /* Step 15. */ @@ -1815,6 +1841,7 @@ static void save_csa(struct spu_state *prev, struct spu *spu) save_mfc_csr_ato(prev, spu); /* Step 24. */ save_mfc_tclass_id(prev, spu); /* Step 25. */ set_mfc_tclass_id(prev, spu); /* Step 26. */ + save_mfc_cmd(prev, spu); /* Step 26a - moved from 44. */ purge_mfc_queue(prev, spu); /* Step 27. */ wait_purge_complete(prev, spu); /* Step 28. */ setup_mfc_sr1(prev, spu); /* Step 30. */ @@ -1831,7 +1858,6 @@ static void save_csa(struct spu_state *prev, struct spu *spu) save_ppuint_mb(prev, spu); /* Step 41. */ save_ch_part1(prev, spu); /* Step 42. */ save_spu_mb(prev, spu); /* Step 43. */ - save_mfc_cmd(prev, spu); /* Step 44. */ reset_ch(prev, spu); /* Step 45. */ } @@ -2000,6 +2026,7 @@ static void restore_csa(struct spu_state *next, struct spu *spu) check_ppuint_mb_stat(next, spu); /* Step 67. */ spu_invalidate_slbs(spu); /* Modified Step 68. */ restore_mfc_sr1(next, spu); /* Step 69. */ + set_int_route(next, spu); /* NEW */ restore_other_spu_access(next, spu); /* Step 70. */ restore_spu_runcntl(next, spu); /* Step 71. */ restore_mfc_cntl(next, spu); /* Step 72. */ diff --git a/arch/powerpc/platforms/cell/spufs/syscalls.c b/arch/powerpc/platforms/cell/spufs/syscalls.c index 49c87769b1f..a87200a535f 100644 --- a/arch/powerpc/platforms/cell/spufs/syscalls.c +++ b/arch/powerpc/platforms/cell/spufs/syscalls.c @@ -1,8 +1,9 @@ #include <linux/file.h> #include <linux/fs.h> -#include <linux/module.h> +#include <linux/export.h> #include <linux/mount.h> #include <linux/namei.h> +#include <linux/slab.h> #include <asm/uaccess.h> @@ -46,7 +47,7 @@ static long do_spu_run(struct file *filp, if (filp->f_op != &spufs_context_fops) goto out; - i = SPUFS_I(filp->f_path.dentry->d_inode); + i = SPUFS_I(file_inode(filp)); ret = spufs_run_spu(i->i_ctx, &npc, &status); if (put_user(npc, unpc)) @@ -59,23 +60,17 @@ out: } static long do_spu_create(const char __user *pathname, unsigned int flags, - mode_t mode, struct file *neighbor) + umode_t mode, struct file *neighbor) { - char *tmp; + struct path path; + struct dentry *dentry; int ret; - tmp = getname(pathname); - ret = PTR_ERR(tmp); - if (!IS_ERR(tmp)) { - struct nameidata nd; - - ret = path_lookup(tmp, LOOKUP_PARENT| - LOOKUP_OPEN|LOOKUP_CREATE, &nd); - if (!ret) { - ret = spufs_create(&nd, flags, mode, neighbor); - path_put(&nd.path); - } - putname(tmp); + dentry = user_path_create(AT_FDCWD, pathname, &path, LOOKUP_DIRECTORY); + ret = PTR_ERR(dentry); + if (!IS_ERR(dentry)) { + ret = spufs_create(&path, dentry, flags, mode, neighbor); + done_path_create(&path, dentry); } return ret; @@ -84,8 +79,10 @@ static long do_spu_create(const char __user *pathname, unsigned int flags, struct spufs_calls spufs_calls = { .create_thread = do_spu_create, .spu_run = do_spu_run, - .coredump_extra_notes_size = spufs_coredump_extra_notes_size, - .coredump_extra_notes_write = spufs_coredump_extra_notes_write, .notify_spus_active = do_notify_spus_active, .owner = THIS_MODULE, +#ifdef CONFIG_COREDUMP + .coredump_extra_notes_size = spufs_coredump_extra_notes_size, + .coredump_extra_notes_write = spufs_coredump_extra_notes_write, +#endif }; |
