167207b96SArnd Bergmann /* 267207b96SArnd Bergmann * SPU file system -- file contents 367207b96SArnd Bergmann * 467207b96SArnd Bergmann * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 567207b96SArnd Bergmann * 667207b96SArnd Bergmann * Author: Arnd Bergmann <arndb@de.ibm.com> 767207b96SArnd Bergmann * 867207b96SArnd Bergmann * This program is free software; you can redistribute it and/or modify 967207b96SArnd Bergmann * it under the terms of the GNU General Public License as published by 1067207b96SArnd Bergmann * the Free Software Foundation; either version 2, or (at your option) 1167207b96SArnd Bergmann * any later version. 1267207b96SArnd Bergmann * 1367207b96SArnd Bergmann * This program is distributed in the hope that it will be useful, 1467207b96SArnd Bergmann * but WITHOUT ANY WARRANTY; without even the implied warranty of 1567207b96SArnd Bergmann * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1667207b96SArnd Bergmann * GNU General Public License for more details. 1767207b96SArnd Bergmann * 1867207b96SArnd Bergmann * You should have received a copy of the GNU General Public License 1967207b96SArnd Bergmann * along with this program; if not, write to the Free Software 2067207b96SArnd Bergmann * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 2167207b96SArnd Bergmann */ 2267207b96SArnd Bergmann 23a33a7d73SArnd Bergmann #undef DEBUG 24a33a7d73SArnd Bergmann 2567207b96SArnd Bergmann #include <linux/fs.h> 2667207b96SArnd Bergmann #include <linux/ioctl.h> 2767207b96SArnd Bergmann #include <linux/module.h> 28d88cfffaSArnd Bergmann #include <linux/pagemap.h> 2967207b96SArnd Bergmann #include <linux/poll.h> 305110459fSArnd Bergmann #include <linux/ptrace.h> 31cbe709c1SBenjamin Herrenschmidt #include <linux/seq_file.h> 325a0e3ad6STejun Heo #include <linux/slab.h> 3367207b96SArnd Bergmann 3467207b96SArnd Bergmann #include <asm/io.h> 35dfe1e09fSFUJITA Tomonori #include <asm/time.h> 3667207b96SArnd Bergmann #include <asm/spu.h> 37b9e3bd77SDwayne Grant McConnell #include <asm/spu_info.h> 3867207b96SArnd Bergmann #include <asm/uaccess.h> 3967207b96SArnd Bergmann 4067207b96SArnd Bergmann #include "spufs.h" 41ae142e0cSChristoph Hellwig #include "sputrace.h" 4267207b96SArnd Bergmann 4327d5bf2aSBenjamin Herrenschmidt #define SPUFS_MMAP_4K (PAGE_SIZE == 0x1000) 4427d5bf2aSBenjamin Herrenschmidt 45197b1a82SChristoph Hellwig /* Simple attribute files */ 46197b1a82SChristoph Hellwig struct spufs_attr { 47197b1a82SChristoph Hellwig int (*get)(void *, u64 *); 48197b1a82SChristoph Hellwig int (*set)(void *, u64); 49197b1a82SChristoph Hellwig char get_buf[24]; /* enough to store a u64 and "\n\0" */ 50197b1a82SChristoph Hellwig char set_buf[24]; 51197b1a82SChristoph Hellwig void *data; 52197b1a82SChristoph Hellwig const char *fmt; /* format for read operation */ 53197b1a82SChristoph Hellwig struct mutex mutex; /* protects access to these buffers */ 54197b1a82SChristoph Hellwig }; 55197b1a82SChristoph Hellwig 56197b1a82SChristoph Hellwig static int spufs_attr_open(struct inode *inode, struct file *file, 57197b1a82SChristoph Hellwig int (*get)(void *, u64 *), int (*set)(void *, u64), 58197b1a82SChristoph Hellwig const char *fmt) 59197b1a82SChristoph Hellwig { 60197b1a82SChristoph Hellwig struct spufs_attr *attr; 61197b1a82SChristoph Hellwig 62197b1a82SChristoph Hellwig attr = kmalloc(sizeof(*attr), GFP_KERNEL); 63197b1a82SChristoph Hellwig if (!attr) 64197b1a82SChristoph Hellwig return -ENOMEM; 65197b1a82SChristoph Hellwig 66197b1a82SChristoph Hellwig attr->get = get; 67197b1a82SChristoph Hellwig attr->set = set; 68197b1a82SChristoph Hellwig attr->data = inode->i_private; 69197b1a82SChristoph Hellwig attr->fmt = fmt; 70197b1a82SChristoph Hellwig mutex_init(&attr->mutex); 71197b1a82SChristoph Hellwig file->private_data = attr; 72197b1a82SChristoph Hellwig 73197b1a82SChristoph Hellwig return nonseekable_open(inode, file); 74197b1a82SChristoph Hellwig } 75197b1a82SChristoph Hellwig 76197b1a82SChristoph Hellwig static int spufs_attr_release(struct inode *inode, struct file *file) 77197b1a82SChristoph Hellwig { 78197b1a82SChristoph Hellwig kfree(file->private_data); 79197b1a82SChristoph Hellwig return 0; 80197b1a82SChristoph Hellwig } 81197b1a82SChristoph Hellwig 82197b1a82SChristoph Hellwig static ssize_t spufs_attr_read(struct file *file, char __user *buf, 83197b1a82SChristoph Hellwig size_t len, loff_t *ppos) 84197b1a82SChristoph Hellwig { 85197b1a82SChristoph Hellwig struct spufs_attr *attr; 86197b1a82SChristoph Hellwig size_t size; 87197b1a82SChristoph Hellwig ssize_t ret; 88197b1a82SChristoph Hellwig 89197b1a82SChristoph Hellwig attr = file->private_data; 90197b1a82SChristoph Hellwig if (!attr->get) 91197b1a82SChristoph Hellwig return -EACCES; 92197b1a82SChristoph Hellwig 93197b1a82SChristoph Hellwig ret = mutex_lock_interruptible(&attr->mutex); 94197b1a82SChristoph Hellwig if (ret) 95197b1a82SChristoph Hellwig return ret; 96197b1a82SChristoph Hellwig 97197b1a82SChristoph Hellwig if (*ppos) { /* continued read */ 98197b1a82SChristoph Hellwig size = strlen(attr->get_buf); 99197b1a82SChristoph Hellwig } else { /* first read */ 100197b1a82SChristoph Hellwig u64 val; 101197b1a82SChristoph Hellwig ret = attr->get(attr->data, &val); 102197b1a82SChristoph Hellwig if (ret) 103197b1a82SChristoph Hellwig goto out; 104197b1a82SChristoph Hellwig 105197b1a82SChristoph Hellwig size = scnprintf(attr->get_buf, sizeof(attr->get_buf), 106197b1a82SChristoph Hellwig attr->fmt, (unsigned long long)val); 107197b1a82SChristoph Hellwig } 108197b1a82SChristoph Hellwig 109197b1a82SChristoph Hellwig ret = simple_read_from_buffer(buf, len, ppos, attr->get_buf, size); 110197b1a82SChristoph Hellwig out: 111197b1a82SChristoph Hellwig mutex_unlock(&attr->mutex); 112197b1a82SChristoph Hellwig return ret; 113197b1a82SChristoph Hellwig } 114197b1a82SChristoph Hellwig 115197b1a82SChristoph Hellwig static ssize_t spufs_attr_write(struct file *file, const char __user *buf, 116197b1a82SChristoph Hellwig size_t len, loff_t *ppos) 117197b1a82SChristoph Hellwig { 118197b1a82SChristoph Hellwig struct spufs_attr *attr; 119197b1a82SChristoph Hellwig u64 val; 120197b1a82SChristoph Hellwig size_t size; 121197b1a82SChristoph Hellwig ssize_t ret; 122197b1a82SChristoph Hellwig 123197b1a82SChristoph Hellwig attr = file->private_data; 124197b1a82SChristoph Hellwig if (!attr->set) 125197b1a82SChristoph Hellwig return -EACCES; 126197b1a82SChristoph Hellwig 127197b1a82SChristoph Hellwig ret = mutex_lock_interruptible(&attr->mutex); 128197b1a82SChristoph Hellwig if (ret) 129197b1a82SChristoph Hellwig return ret; 130197b1a82SChristoph Hellwig 131197b1a82SChristoph Hellwig ret = -EFAULT; 132197b1a82SChristoph Hellwig size = min(sizeof(attr->set_buf) - 1, len); 133197b1a82SChristoph Hellwig if (copy_from_user(attr->set_buf, buf, size)) 134197b1a82SChristoph Hellwig goto out; 135197b1a82SChristoph Hellwig 136197b1a82SChristoph Hellwig ret = len; /* claim we got the whole input */ 137197b1a82SChristoph Hellwig attr->set_buf[size] = '\0'; 138197b1a82SChristoph Hellwig val = simple_strtol(attr->set_buf, NULL, 0); 139197b1a82SChristoph Hellwig attr->set(attr->data, val); 140197b1a82SChristoph Hellwig out: 141197b1a82SChristoph Hellwig mutex_unlock(&attr->mutex); 142197b1a82SChristoph Hellwig return ret; 143197b1a82SChristoph Hellwig } 144197b1a82SChristoph Hellwig 145197b1a82SChristoph Hellwig #define DEFINE_SPUFS_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt) \ 146197b1a82SChristoph Hellwig static int __fops ## _open(struct inode *inode, struct file *file) \ 147197b1a82SChristoph Hellwig { \ 148197b1a82SChristoph Hellwig __simple_attr_check_format(__fmt, 0ull); \ 149197b1a82SChristoph Hellwig return spufs_attr_open(inode, file, __get, __set, __fmt); \ 150197b1a82SChristoph Hellwig } \ 151828c0950SAlexey Dobriyan static const struct file_operations __fops = { \ 152197b1a82SChristoph Hellwig .owner = THIS_MODULE, \ 153197b1a82SChristoph Hellwig .open = __fops ## _open, \ 154197b1a82SChristoph Hellwig .release = spufs_attr_release, \ 155197b1a82SChristoph Hellwig .read = spufs_attr_read, \ 156197b1a82SChristoph Hellwig .write = spufs_attr_write, \ 157fc15351dSArnd Bergmann .llseek = generic_file_llseek, \ 158197b1a82SChristoph Hellwig }; 159197b1a82SChristoph Hellwig 160cbe709c1SBenjamin Herrenschmidt 16167207b96SArnd Bergmann static int 16267207b96SArnd Bergmann spufs_mem_open(struct inode *inode, struct file *file) 16367207b96SArnd Bergmann { 16467207b96SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 1656df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 16643c2bbd9SChristoph Hellwig 16747d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 1686df10a82SMark Nutter file->private_data = ctx; 16943c2bbd9SChristoph Hellwig if (!i->i_openers++) 1706df10a82SMark Nutter ctx->local_store = inode->i_mapping; 17147d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 17243c2bbd9SChristoph Hellwig return 0; 17343c2bbd9SChristoph Hellwig } 17443c2bbd9SChristoph Hellwig 17543c2bbd9SChristoph Hellwig static int 17643c2bbd9SChristoph Hellwig spufs_mem_release(struct inode *inode, struct file *file) 17743c2bbd9SChristoph Hellwig { 17843c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 17943c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 18043c2bbd9SChristoph Hellwig 18147d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 18243c2bbd9SChristoph Hellwig if (!--i->i_openers) 18343c2bbd9SChristoph Hellwig ctx->local_store = NULL; 18447d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 18567207b96SArnd Bergmann return 0; 18667207b96SArnd Bergmann } 18767207b96SArnd Bergmann 18867207b96SArnd Bergmann static ssize_t 189bf1ab978SDwayne Grant McConnell __spufs_mem_read(struct spu_context *ctx, char __user *buffer, 190bf1ab978SDwayne Grant McConnell size_t size, loff_t *pos) 191bf1ab978SDwayne Grant McConnell { 192bf1ab978SDwayne Grant McConnell char *local_store = ctx->ops->get_ls(ctx); 193bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, local_store, 194bf1ab978SDwayne Grant McConnell LS_SIZE); 195bf1ab978SDwayne Grant McConnell } 196bf1ab978SDwayne Grant McConnell 197bf1ab978SDwayne Grant McConnell static ssize_t 19867207b96SArnd Bergmann spufs_mem_read(struct file *file, char __user *buffer, 19967207b96SArnd Bergmann size_t size, loff_t *pos) 20067207b96SArnd Bergmann { 201bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 202aa0ed2bdSArnd Bergmann ssize_t ret; 20367207b96SArnd Bergmann 204c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 205c9101bdbSChristoph Hellwig if (ret) 206c9101bdbSChristoph Hellwig return ret; 207bf1ab978SDwayne Grant McConnell ret = __spufs_mem_read(ctx, buffer, size, pos); 2088b3d6663SArnd Bergmann spu_release(ctx); 209c9101bdbSChristoph Hellwig 21067207b96SArnd Bergmann return ret; 21167207b96SArnd Bergmann } 21267207b96SArnd Bergmann 21367207b96SArnd Bergmann static ssize_t 21467207b96SArnd Bergmann spufs_mem_write(struct file *file, const char __user *buffer, 215aa0ed2bdSArnd Bergmann size_t size, loff_t *ppos) 21667207b96SArnd Bergmann { 21767207b96SArnd Bergmann struct spu_context *ctx = file->private_data; 2188b3d6663SArnd Bergmann char *local_store; 219aa0ed2bdSArnd Bergmann loff_t pos = *ppos; 2208b3d6663SArnd Bergmann int ret; 22167207b96SArnd Bergmann 222aa0ed2bdSArnd Bergmann if (pos > LS_SIZE) 22367207b96SArnd Bergmann return -EFBIG; 2248b3d6663SArnd Bergmann 225c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 226c9101bdbSChristoph Hellwig if (ret) 227c9101bdbSChristoph Hellwig return ret; 228c9101bdbSChristoph Hellwig 2298b3d6663SArnd Bergmann local_store = ctx->ops->get_ls(ctx); 23063c3b9d7SAkinobu Mita size = simple_write_to_buffer(local_store, LS_SIZE, ppos, buffer, size); 2318b3d6663SArnd Bergmann spu_release(ctx); 232aa0ed2bdSArnd Bergmann 233aa0ed2bdSArnd Bergmann return size; 23467207b96SArnd Bergmann } 23567207b96SArnd Bergmann 236b1e2270fSNick Piggin static int 237b1e2270fSNick Piggin spufs_mem_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 2388b3d6663SArnd Bergmann { 2398b3d6663SArnd Bergmann struct spu_context *ctx = vma->vm_file->private_data; 240b1e2270fSNick Piggin unsigned long address = (unsigned long)vmf->virtual_address; 241b1e2270fSNick Piggin unsigned long pfn, offset; 242b1e2270fSNick Piggin 243f1fa74f4SBenjamin Herrenschmidt #ifdef CONFIG_SPU_FS_64K_LS 244f1fa74f4SBenjamin Herrenschmidt struct spu_state *csa = &ctx->csa; 245f1fa74f4SBenjamin Herrenschmidt int psize; 24678bde53eSBenjamin Herrenschmidt 247f1fa74f4SBenjamin Herrenschmidt /* Check what page size we are using */ 248f1fa74f4SBenjamin Herrenschmidt psize = get_slice_psize(vma->vm_mm, address); 2498b3d6663SArnd Bergmann 250f1fa74f4SBenjamin Herrenschmidt /* Some sanity checking */ 251f1fa74f4SBenjamin Herrenschmidt BUG_ON(csa->use_big_pages != (psize == MMU_PAGE_64K)); 252f1fa74f4SBenjamin Herrenschmidt 253f1fa74f4SBenjamin Herrenschmidt /* Wow, 64K, cool, we need to align the address though */ 254f1fa74f4SBenjamin Herrenschmidt if (csa->use_big_pages) { 255f1fa74f4SBenjamin Herrenschmidt BUG_ON(vma->vm_start & 0xffff); 256f1fa74f4SBenjamin Herrenschmidt address &= ~0xfffful; 257f1fa74f4SBenjamin Herrenschmidt } 258f1fa74f4SBenjamin Herrenschmidt #endif /* CONFIG_SPU_FS_64K_LS */ 259f1fa74f4SBenjamin Herrenschmidt 260b1e2270fSNick Piggin offset = vmf->pgoff << PAGE_SHIFT; 261128b8546SMasato Noguchi if (offset >= LS_SIZE) 262b1e2270fSNick Piggin return VM_FAULT_SIGBUS; 263128b8546SMasato Noguchi 264b1e2270fSNick Piggin pr_debug("spufs_mem_mmap_fault address=0x%lx, offset=0x%lx\n", 265b1e2270fSNick Piggin address, offset); 266f1fa74f4SBenjamin Herrenschmidt 267c9101bdbSChristoph Hellwig if (spu_acquire(ctx)) 268b1e2270fSNick Piggin return VM_FAULT_NOPAGE; 2698b3d6663SArnd Bergmann 270ac91cb8dSArnd Bergmann if (ctx->state == SPU_STATE_SAVED) { 27164b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_cached(vma->vm_page_prot); 27278bde53eSBenjamin Herrenschmidt pfn = vmalloc_to_pfn(ctx->csa.lscsa->ls + offset); 273ac91cb8dSArnd Bergmann } else { 27464b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot); 27578bde53eSBenjamin Herrenschmidt pfn = (ctx->spu->local_store_phys + offset) >> PAGE_SHIFT; 276ac91cb8dSArnd Bergmann } 27778bde53eSBenjamin Herrenschmidt vm_insert_pfn(vma, address, pfn); 27878bde53eSBenjamin Herrenschmidt 2798b3d6663SArnd Bergmann spu_release(ctx); 2808b3d6663SArnd Bergmann 281b1e2270fSNick Piggin return VM_FAULT_NOPAGE; 2828b3d6663SArnd Bergmann } 2838b3d6663SArnd Bergmann 284a352894dSBenjamin Herrenschmidt static int spufs_mem_mmap_access(struct vm_area_struct *vma, 285a352894dSBenjamin Herrenschmidt unsigned long address, 286a352894dSBenjamin Herrenschmidt void *buf, int len, int write) 287a352894dSBenjamin Herrenschmidt { 288a352894dSBenjamin Herrenschmidt struct spu_context *ctx = vma->vm_file->private_data; 289a352894dSBenjamin Herrenschmidt unsigned long offset = address - vma->vm_start; 290a352894dSBenjamin Herrenschmidt char *local_store; 291a352894dSBenjamin Herrenschmidt 292a352894dSBenjamin Herrenschmidt if (write && !(vma->vm_flags & VM_WRITE)) 293a352894dSBenjamin Herrenschmidt return -EACCES; 294a352894dSBenjamin Herrenschmidt if (spu_acquire(ctx)) 295a352894dSBenjamin Herrenschmidt return -EINTR; 296a352894dSBenjamin Herrenschmidt if ((offset + len) > vma->vm_end) 297a352894dSBenjamin Herrenschmidt len = vma->vm_end - offset; 298a352894dSBenjamin Herrenschmidt local_store = ctx->ops->get_ls(ctx); 299a352894dSBenjamin Herrenschmidt if (write) 300a352894dSBenjamin Herrenschmidt memcpy_toio(local_store + offset, buf, len); 301a352894dSBenjamin Herrenschmidt else 302a352894dSBenjamin Herrenschmidt memcpy_fromio(buf, local_store + offset, len); 303a352894dSBenjamin Herrenschmidt spu_release(ctx); 304a352894dSBenjamin Herrenschmidt return len; 305a352894dSBenjamin Herrenschmidt } 30678bde53eSBenjamin Herrenschmidt 307f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_mem_mmap_vmops = { 308b1e2270fSNick Piggin .fault = spufs_mem_mmap_fault, 309a352894dSBenjamin Herrenschmidt .access = spufs_mem_mmap_access, 3108b3d6663SArnd Bergmann }; 3118b3d6663SArnd Bergmann 312f1fa74f4SBenjamin Herrenschmidt static int spufs_mem_mmap(struct file *file, struct vm_area_struct *vma) 31367207b96SArnd Bergmann { 314f1fa74f4SBenjamin Herrenschmidt #ifdef CONFIG_SPU_FS_64K_LS 315f1fa74f4SBenjamin Herrenschmidt struct spu_context *ctx = file->private_data; 316f1fa74f4SBenjamin Herrenschmidt struct spu_state *csa = &ctx->csa; 317f1fa74f4SBenjamin Herrenschmidt 318f1fa74f4SBenjamin Herrenschmidt /* Sanity check VMA alignment */ 319f1fa74f4SBenjamin Herrenschmidt if (csa->use_big_pages) { 320f1fa74f4SBenjamin Herrenschmidt pr_debug("spufs_mem_mmap 64K, start=0x%lx, end=0x%lx," 321f1fa74f4SBenjamin Herrenschmidt " pgoff=0x%lx\n", vma->vm_start, vma->vm_end, 322f1fa74f4SBenjamin Herrenschmidt vma->vm_pgoff); 323f1fa74f4SBenjamin Herrenschmidt if (vma->vm_start & 0xffff) 324f1fa74f4SBenjamin Herrenschmidt return -EINVAL; 325f1fa74f4SBenjamin Herrenschmidt if (vma->vm_pgoff & 0xf) 326f1fa74f4SBenjamin Herrenschmidt return -EINVAL; 327f1fa74f4SBenjamin Herrenschmidt } 328f1fa74f4SBenjamin Herrenschmidt #endif /* CONFIG_SPU_FS_64K_LS */ 329f1fa74f4SBenjamin Herrenschmidt 3308b3d6663SArnd Bergmann if (!(vma->vm_flags & VM_SHARED)) 3318b3d6663SArnd Bergmann return -EINVAL; 33267207b96SArnd Bergmann 33378bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 33464b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot); 3358b3d6663SArnd Bergmann 3368b3d6663SArnd Bergmann vma->vm_ops = &spufs_mem_mmap_vmops; 33767207b96SArnd Bergmann return 0; 33867207b96SArnd Bergmann } 33967207b96SArnd Bergmann 340f1fa74f4SBenjamin Herrenschmidt #ifdef CONFIG_SPU_FS_64K_LS 3411238819aSSebastian Siewior static unsigned long spufs_get_unmapped_area(struct file *file, 3421238819aSSebastian Siewior unsigned long addr, unsigned long len, unsigned long pgoff, 343f1fa74f4SBenjamin Herrenschmidt unsigned long flags) 344f1fa74f4SBenjamin Herrenschmidt { 345f1fa74f4SBenjamin Herrenschmidt struct spu_context *ctx = file->private_data; 346f1fa74f4SBenjamin Herrenschmidt struct spu_state *csa = &ctx->csa; 347f1fa74f4SBenjamin Herrenschmidt 348f1fa74f4SBenjamin Herrenschmidt /* If not using big pages, fallback to normal MM g_u_a */ 349f1fa74f4SBenjamin Herrenschmidt if (!csa->use_big_pages) 350f1fa74f4SBenjamin Herrenschmidt return current->mm->get_unmapped_area(file, addr, len, 351f1fa74f4SBenjamin Herrenschmidt pgoff, flags); 352f1fa74f4SBenjamin Herrenschmidt 353f1fa74f4SBenjamin Herrenschmidt /* Else, try to obtain a 64K pages slice */ 354f1fa74f4SBenjamin Herrenschmidt return slice_get_unmapped_area(addr, len, flags, 355f1fa74f4SBenjamin Herrenschmidt MMU_PAGE_64K, 1, 0); 356f1fa74f4SBenjamin Herrenschmidt } 357f1fa74f4SBenjamin Herrenschmidt #endif /* CONFIG_SPU_FS_64K_LS */ 358f1fa74f4SBenjamin Herrenschmidt 3595dfe4c96SArjan van de Ven static const struct file_operations spufs_mem_fops = { 36067207b96SArnd Bergmann .open = spufs_mem_open, 361ce92987bSChristoph Hellwig .release = spufs_mem_release, 36267207b96SArnd Bergmann .read = spufs_mem_read, 36367207b96SArnd Bergmann .write = spufs_mem_write, 3648b3d6663SArnd Bergmann .llseek = generic_file_llseek, 36567207b96SArnd Bergmann .mmap = spufs_mem_mmap, 366f1fa74f4SBenjamin Herrenschmidt #ifdef CONFIG_SPU_FS_64K_LS 367f1fa74f4SBenjamin Herrenschmidt .get_unmapped_area = spufs_get_unmapped_area, 368f1fa74f4SBenjamin Herrenschmidt #endif 3698b3d6663SArnd Bergmann }; 3708b3d6663SArnd Bergmann 371b1e2270fSNick Piggin static int spufs_ps_fault(struct vm_area_struct *vma, 372b1e2270fSNick Piggin struct vm_fault *vmf, 37378bde53eSBenjamin Herrenschmidt unsigned long ps_offs, 37427d5bf2aSBenjamin Herrenschmidt unsigned long ps_size) 3756df10a82SMark Nutter { 3766df10a82SMark Nutter struct spu_context *ctx = vma->vm_file->private_data; 377b1e2270fSNick Piggin unsigned long area, offset = vmf->pgoff << PAGE_SHIFT; 378eebead5bSChristoph Hellwig int ret = 0; 3796df10a82SMark Nutter 380b1e2270fSNick Piggin spu_context_nospu_trace(spufs_ps_fault__enter, ctx); 381038200cfSChristoph Hellwig 38227d5bf2aSBenjamin Herrenschmidt if (offset >= ps_size) 383b1e2270fSNick Piggin return VM_FAULT_SIGBUS; 3846df10a82SMark Nutter 38560657263SJeremy Kerr if (fatal_signal_pending(current)) 38660657263SJeremy Kerr return VM_FAULT_SIGBUS; 38760657263SJeremy Kerr 38833bfd7a7SArnd Bergmann /* 389d5883137SJeremy Kerr * Because we release the mmap_sem, the context may be destroyed while 390d5883137SJeremy Kerr * we're in spu_wait. Grab an extra reference so it isn't destroyed 391d5883137SJeremy Kerr * in the meantime. 392d5883137SJeremy Kerr */ 393d5883137SJeremy Kerr get_spu_context(ctx); 394d5883137SJeremy Kerr 395d5883137SJeremy Kerr /* 39633bfd7a7SArnd Bergmann * We have to wait for context to be loaded before we have 39733bfd7a7SArnd Bergmann * pages to hand out to the user, but we don't want to wait 39833bfd7a7SArnd Bergmann * with the mmap_sem held. 39933bfd7a7SArnd Bergmann * It is possible to drop the mmap_sem here, but then we need 400b1e2270fSNick Piggin * to return VM_FAULT_NOPAGE because the mappings may have 40133bfd7a7SArnd Bergmann * hanged. 40278bde53eSBenjamin Herrenschmidt */ 403c9101bdbSChristoph Hellwig if (spu_acquire(ctx)) 404d5883137SJeremy Kerr goto refault; 405c9101bdbSChristoph Hellwig 40633bfd7a7SArnd Bergmann if (ctx->state == SPU_STATE_SAVED) { 40733bfd7a7SArnd Bergmann up_read(¤t->mm->mmap_sem); 408b1e2270fSNick Piggin spu_context_nospu_trace(spufs_ps_fault__sleep, ctx); 409eebead5bSChristoph Hellwig ret = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); 410b1e2270fSNick Piggin spu_context_trace(spufs_ps_fault__wake, ctx, ctx->spu); 41133bfd7a7SArnd Bergmann down_read(¤t->mm->mmap_sem); 412c9101bdbSChristoph Hellwig } else { 4136df10a82SMark Nutter area = ctx->spu->problem_phys + ps_offs; 414b1e2270fSNick Piggin vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, 415b1e2270fSNick Piggin (area + offset) >> PAGE_SHIFT); 416b1e2270fSNick Piggin spu_context_trace(spufs_ps_fault__insert, ctx, ctx->spu); 417c9101bdbSChristoph Hellwig } 41833bfd7a7SArnd Bergmann 419eebead5bSChristoph Hellwig if (!ret) 4206df10a82SMark Nutter spu_release(ctx); 421d5883137SJeremy Kerr 422d5883137SJeremy Kerr refault: 423d5883137SJeremy Kerr put_spu_context(ctx); 424b1e2270fSNick Piggin return VM_FAULT_NOPAGE; 4256df10a82SMark Nutter } 4266df10a82SMark Nutter 42727d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 428b1e2270fSNick Piggin static int spufs_cntl_mmap_fault(struct vm_area_struct *vma, 429b1e2270fSNick Piggin struct vm_fault *vmf) 4306df10a82SMark Nutter { 43187ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x4000, SPUFS_CNTL_MAP_SIZE); 4326df10a82SMark Nutter } 4336df10a82SMark Nutter 434f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_cntl_mmap_vmops = { 435b1e2270fSNick Piggin .fault = spufs_cntl_mmap_fault, 4366df10a82SMark Nutter }; 4376df10a82SMark Nutter 4386df10a82SMark Nutter /* 4396df10a82SMark Nutter * mmap support for problem state control area [0x4000 - 0x4fff]. 4406df10a82SMark Nutter */ 4416df10a82SMark Nutter static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma) 4426df10a82SMark Nutter { 4436df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 4446df10a82SMark Nutter return -EINVAL; 4456df10a82SMark Nutter 44678bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 44764b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 4486df10a82SMark Nutter 4496df10a82SMark Nutter vma->vm_ops = &spufs_cntl_mmap_vmops; 4506df10a82SMark Nutter return 0; 4516df10a82SMark Nutter } 45227d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 45327d5bf2aSBenjamin Herrenschmidt #define spufs_cntl_mmap NULL 45427d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 4556df10a82SMark Nutter 456197b1a82SChristoph Hellwig static int spufs_cntl_get(void *data, u64 *val) 457e1dbff2bSArnd Bergmann { 458e1dbff2bSArnd Bergmann struct spu_context *ctx = data; 459c9101bdbSChristoph Hellwig int ret; 460e1dbff2bSArnd Bergmann 461c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 462c9101bdbSChristoph Hellwig if (ret) 463c9101bdbSChristoph Hellwig return ret; 464197b1a82SChristoph Hellwig *val = ctx->ops->status_read(ctx); 465e1dbff2bSArnd Bergmann spu_release(ctx); 466e1dbff2bSArnd Bergmann 467197b1a82SChristoph Hellwig return 0; 468e1dbff2bSArnd Bergmann } 469e1dbff2bSArnd Bergmann 470197b1a82SChristoph Hellwig static int spufs_cntl_set(void *data, u64 val) 471e1dbff2bSArnd Bergmann { 472e1dbff2bSArnd Bergmann struct spu_context *ctx = data; 473c9101bdbSChristoph Hellwig int ret; 474e1dbff2bSArnd Bergmann 475c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 476c9101bdbSChristoph Hellwig if (ret) 477c9101bdbSChristoph Hellwig return ret; 478e1dbff2bSArnd Bergmann ctx->ops->runcntl_write(ctx, val); 479e1dbff2bSArnd Bergmann spu_release(ctx); 480197b1a82SChristoph Hellwig 481197b1a82SChristoph Hellwig return 0; 482e1dbff2bSArnd Bergmann } 483e1dbff2bSArnd Bergmann 4846df10a82SMark Nutter static int spufs_cntl_open(struct inode *inode, struct file *file) 4856df10a82SMark Nutter { 4866df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 4876df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 4886df10a82SMark Nutter 48947d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 4906df10a82SMark Nutter file->private_data = ctx; 49143c2bbd9SChristoph Hellwig if (!i->i_openers++) 4926df10a82SMark Nutter ctx->cntl = inode->i_mapping; 49347d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 4948b88b099SChristoph Hellwig return simple_attr_open(inode, file, spufs_cntl_get, 495e1dbff2bSArnd Bergmann spufs_cntl_set, "0x%08lx"); 4966df10a82SMark Nutter } 4976df10a82SMark Nutter 49843c2bbd9SChristoph Hellwig static int 49943c2bbd9SChristoph Hellwig spufs_cntl_release(struct inode *inode, struct file *file) 50043c2bbd9SChristoph Hellwig { 50143c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 50243c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 50343c2bbd9SChristoph Hellwig 50474bedc4dSChristoph Hellwig simple_attr_release(inode, file); 50543c2bbd9SChristoph Hellwig 50647d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 50743c2bbd9SChristoph Hellwig if (!--i->i_openers) 50843c2bbd9SChristoph Hellwig ctx->cntl = NULL; 50947d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 51043c2bbd9SChristoph Hellwig return 0; 51143c2bbd9SChristoph Hellwig } 51243c2bbd9SChristoph Hellwig 5135dfe4c96SArjan van de Ven static const struct file_operations spufs_cntl_fops = { 5146df10a82SMark Nutter .open = spufs_cntl_open, 51543c2bbd9SChristoph Hellwig .release = spufs_cntl_release, 5168b88b099SChristoph Hellwig .read = simple_attr_read, 5178b88b099SChristoph Hellwig .write = simple_attr_write, 518fc15351dSArnd Bergmann .llseek = generic_file_llseek, 5196df10a82SMark Nutter .mmap = spufs_cntl_mmap, 5206df10a82SMark Nutter }; 5216df10a82SMark Nutter 5228b3d6663SArnd Bergmann static int 5238b3d6663SArnd Bergmann spufs_regs_open(struct inode *inode, struct file *file) 5248b3d6663SArnd Bergmann { 5258b3d6663SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 5268b3d6663SArnd Bergmann file->private_data = i->i_ctx; 5278b3d6663SArnd Bergmann return 0; 5288b3d6663SArnd Bergmann } 5298b3d6663SArnd Bergmann 5308b3d6663SArnd Bergmann static ssize_t 531bf1ab978SDwayne Grant McConnell __spufs_regs_read(struct spu_context *ctx, char __user *buffer, 532bf1ab978SDwayne Grant McConnell size_t size, loff_t *pos) 533bf1ab978SDwayne Grant McConnell { 534bf1ab978SDwayne Grant McConnell struct spu_lscsa *lscsa = ctx->csa.lscsa; 535bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, 536bf1ab978SDwayne Grant McConnell lscsa->gprs, sizeof lscsa->gprs); 537bf1ab978SDwayne Grant McConnell } 538bf1ab978SDwayne Grant McConnell 539bf1ab978SDwayne Grant McConnell static ssize_t 5408b3d6663SArnd Bergmann spufs_regs_read(struct file *file, char __user *buffer, 5418b3d6663SArnd Bergmann size_t size, loff_t *pos) 5428b3d6663SArnd Bergmann { 5438b3d6663SArnd Bergmann int ret; 544bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 5458b3d6663SArnd Bergmann 546f027faa2SJeremy Kerr /* pre-check for file position: if we'd return EOF, there's no point 547f027faa2SJeremy Kerr * causing a deschedule */ 548f027faa2SJeremy Kerr if (*pos >= sizeof(ctx->csa.lscsa->gprs)) 549f027faa2SJeremy Kerr return 0; 550f027faa2SJeremy Kerr 551c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 552c9101bdbSChristoph Hellwig if (ret) 553c9101bdbSChristoph Hellwig return ret; 554bf1ab978SDwayne Grant McConnell ret = __spufs_regs_read(ctx, buffer, size, pos); 55527b1ea09SChristoph Hellwig spu_release_saved(ctx); 5568b3d6663SArnd Bergmann return ret; 5578b3d6663SArnd Bergmann } 5588b3d6663SArnd Bergmann 5598b3d6663SArnd Bergmann static ssize_t 5608b3d6663SArnd Bergmann spufs_regs_write(struct file *file, const char __user *buffer, 5618b3d6663SArnd Bergmann size_t size, loff_t *pos) 5628b3d6663SArnd Bergmann { 5638b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 5648b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 5658b3d6663SArnd Bergmann int ret; 5668b3d6663SArnd Bergmann 567d219889bSJeremy Kerr if (*pos >= sizeof(lscsa->gprs)) 5688b3d6663SArnd Bergmann return -EFBIG; 569d219889bSJeremy Kerr 570c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 571c9101bdbSChristoph Hellwig if (ret) 572c9101bdbSChristoph Hellwig return ret; 5738b3d6663SArnd Bergmann 57463c3b9d7SAkinobu Mita size = simple_write_to_buffer(lscsa->gprs, sizeof(lscsa->gprs), pos, 57563c3b9d7SAkinobu Mita buffer, size); 5768b3d6663SArnd Bergmann 57727b1ea09SChristoph Hellwig spu_release_saved(ctx); 57863c3b9d7SAkinobu Mita return size; 5798b3d6663SArnd Bergmann } 5808b3d6663SArnd Bergmann 5815dfe4c96SArjan van de Ven static const struct file_operations spufs_regs_fops = { 5828b3d6663SArnd Bergmann .open = spufs_regs_open, 5838b3d6663SArnd Bergmann .read = spufs_regs_read, 5848b3d6663SArnd Bergmann .write = spufs_regs_write, 5858b3d6663SArnd Bergmann .llseek = generic_file_llseek, 5868b3d6663SArnd Bergmann }; 5878b3d6663SArnd Bergmann 5888b3d6663SArnd Bergmann static ssize_t 589bf1ab978SDwayne Grant McConnell __spufs_fpcr_read(struct spu_context *ctx, char __user * buffer, 590bf1ab978SDwayne Grant McConnell size_t size, loff_t * pos) 591bf1ab978SDwayne Grant McConnell { 592bf1ab978SDwayne Grant McConnell struct spu_lscsa *lscsa = ctx->csa.lscsa; 593bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, 594bf1ab978SDwayne Grant McConnell &lscsa->fpcr, sizeof(lscsa->fpcr)); 595bf1ab978SDwayne Grant McConnell } 596bf1ab978SDwayne Grant McConnell 597bf1ab978SDwayne Grant McConnell static ssize_t 5988b3d6663SArnd Bergmann spufs_fpcr_read(struct file *file, char __user * buffer, 5998b3d6663SArnd Bergmann size_t size, loff_t * pos) 6008b3d6663SArnd Bergmann { 6018b3d6663SArnd Bergmann int ret; 602bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 6038b3d6663SArnd Bergmann 604c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 605c9101bdbSChristoph Hellwig if (ret) 606c9101bdbSChristoph Hellwig return ret; 607bf1ab978SDwayne Grant McConnell ret = __spufs_fpcr_read(ctx, buffer, size, pos); 60827b1ea09SChristoph Hellwig spu_release_saved(ctx); 6098b3d6663SArnd Bergmann return ret; 6108b3d6663SArnd Bergmann } 6118b3d6663SArnd Bergmann 6128b3d6663SArnd Bergmann static ssize_t 6138b3d6663SArnd Bergmann spufs_fpcr_write(struct file *file, const char __user * buffer, 6148b3d6663SArnd Bergmann size_t size, loff_t * pos) 6158b3d6663SArnd Bergmann { 6168b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 6178b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 6188b3d6663SArnd Bergmann int ret; 6198b3d6663SArnd Bergmann 620d219889bSJeremy Kerr if (*pos >= sizeof(lscsa->fpcr)) 6218b3d6663SArnd Bergmann return -EFBIG; 622c9101bdbSChristoph Hellwig 623c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 624c9101bdbSChristoph Hellwig if (ret) 625c9101bdbSChristoph Hellwig return ret; 626c9101bdbSChristoph Hellwig 62763c3b9d7SAkinobu Mita size = simple_write_to_buffer(&lscsa->fpcr, sizeof(lscsa->fpcr), pos, 62863c3b9d7SAkinobu Mita buffer, size); 6298b3d6663SArnd Bergmann 63027b1ea09SChristoph Hellwig spu_release_saved(ctx); 63163c3b9d7SAkinobu Mita return size; 6328b3d6663SArnd Bergmann } 6338b3d6663SArnd Bergmann 6345dfe4c96SArjan van de Ven static const struct file_operations spufs_fpcr_fops = { 6358b3d6663SArnd Bergmann .open = spufs_regs_open, 6368b3d6663SArnd Bergmann .read = spufs_fpcr_read, 6378b3d6663SArnd Bergmann .write = spufs_fpcr_write, 63867207b96SArnd Bergmann .llseek = generic_file_llseek, 63967207b96SArnd Bergmann }; 64067207b96SArnd Bergmann 64167207b96SArnd Bergmann /* generic open function for all pipe-like files */ 64267207b96SArnd Bergmann static int spufs_pipe_open(struct inode *inode, struct file *file) 64367207b96SArnd Bergmann { 64467207b96SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 64567207b96SArnd Bergmann file->private_data = i->i_ctx; 64667207b96SArnd Bergmann 64767207b96SArnd Bergmann return nonseekable_open(inode, file); 64867207b96SArnd Bergmann } 64967207b96SArnd Bergmann 650cdcc89bbSArnd Bergmann /* 651cdcc89bbSArnd Bergmann * Read as many bytes from the mailbox as possible, until 652cdcc89bbSArnd Bergmann * one of the conditions becomes true: 653cdcc89bbSArnd Bergmann * 654cdcc89bbSArnd Bergmann * - no more data available in the mailbox 655cdcc89bbSArnd Bergmann * - end of the user provided buffer 656cdcc89bbSArnd Bergmann * - end of the mapped area 657cdcc89bbSArnd Bergmann */ 65867207b96SArnd Bergmann static ssize_t spufs_mbox_read(struct file *file, char __user *buf, 65967207b96SArnd Bergmann size_t len, loff_t *pos) 66067207b96SArnd Bergmann { 6618b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 662cdcc89bbSArnd Bergmann u32 mbox_data, __user *udata; 663cdcc89bbSArnd Bergmann ssize_t count; 66467207b96SArnd Bergmann 66567207b96SArnd Bergmann if (len < 4) 66667207b96SArnd Bergmann return -EINVAL; 66767207b96SArnd Bergmann 668cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_WRITE, buf, len)) 66967207b96SArnd Bergmann return -EFAULT; 67067207b96SArnd Bergmann 671cdcc89bbSArnd Bergmann udata = (void __user *)buf; 672cdcc89bbSArnd Bergmann 673c9101bdbSChristoph Hellwig count = spu_acquire(ctx); 674c9101bdbSChristoph Hellwig if (count) 675c9101bdbSChristoph Hellwig return count; 676c9101bdbSChristoph Hellwig 677274cef5eSArnd Bergmann for (count = 0; (count + 4) <= len; count += 4, udata++) { 678cdcc89bbSArnd Bergmann int ret; 679cdcc89bbSArnd Bergmann ret = ctx->ops->mbox_read(ctx, &mbox_data); 680cdcc89bbSArnd Bergmann if (ret == 0) 681cdcc89bbSArnd Bergmann break; 682cdcc89bbSArnd Bergmann 683cdcc89bbSArnd Bergmann /* 684cdcc89bbSArnd Bergmann * at the end of the mapped area, we can fault 685cdcc89bbSArnd Bergmann * but still need to return the data we have 686cdcc89bbSArnd Bergmann * read successfully so far. 687cdcc89bbSArnd Bergmann */ 688cdcc89bbSArnd Bergmann ret = __put_user(mbox_data, udata); 689cdcc89bbSArnd Bergmann if (ret) { 690cdcc89bbSArnd Bergmann if (!count) 691cdcc89bbSArnd Bergmann count = -EFAULT; 692cdcc89bbSArnd Bergmann break; 693cdcc89bbSArnd Bergmann } 694cdcc89bbSArnd Bergmann } 695cdcc89bbSArnd Bergmann spu_release(ctx); 696cdcc89bbSArnd Bergmann 697cdcc89bbSArnd Bergmann if (!count) 698cdcc89bbSArnd Bergmann count = -EAGAIN; 699cdcc89bbSArnd Bergmann 700cdcc89bbSArnd Bergmann return count; 70167207b96SArnd Bergmann } 70267207b96SArnd Bergmann 7035dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_fops = { 70467207b96SArnd Bergmann .open = spufs_pipe_open, 70567207b96SArnd Bergmann .read = spufs_mbox_read, 706fc15351dSArnd Bergmann .llseek = no_llseek, 70767207b96SArnd Bergmann }; 70867207b96SArnd Bergmann 70967207b96SArnd Bergmann static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf, 71067207b96SArnd Bergmann size_t len, loff_t *pos) 71167207b96SArnd Bergmann { 7128b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 713c9101bdbSChristoph Hellwig ssize_t ret; 71467207b96SArnd Bergmann u32 mbox_stat; 71567207b96SArnd Bergmann 71667207b96SArnd Bergmann if (len < 4) 71767207b96SArnd Bergmann return -EINVAL; 71867207b96SArnd Bergmann 719c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 720c9101bdbSChristoph Hellwig if (ret) 721c9101bdbSChristoph Hellwig return ret; 7228b3d6663SArnd Bergmann 7238b3d6663SArnd Bergmann mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff; 7248b3d6663SArnd Bergmann 7258b3d6663SArnd Bergmann spu_release(ctx); 72667207b96SArnd Bergmann 72767207b96SArnd Bergmann if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat)) 72867207b96SArnd Bergmann return -EFAULT; 72967207b96SArnd Bergmann 73067207b96SArnd Bergmann return 4; 73167207b96SArnd Bergmann } 73267207b96SArnd Bergmann 7335dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_stat_fops = { 73467207b96SArnd Bergmann .open = spufs_pipe_open, 73567207b96SArnd Bergmann .read = spufs_mbox_stat_read, 736fc15351dSArnd Bergmann .llseek = no_llseek, 73767207b96SArnd Bergmann }; 73867207b96SArnd Bergmann 73967207b96SArnd Bergmann /* low-level ibox access function */ 7408b3d6663SArnd Bergmann size_t spu_ibox_read(struct spu_context *ctx, u32 *data) 74167207b96SArnd Bergmann { 7428b3d6663SArnd Bergmann return ctx->ops->ibox_read(ctx, data); 74367207b96SArnd Bergmann } 74467207b96SArnd Bergmann 74567207b96SArnd Bergmann static int spufs_ibox_fasync(int fd, struct file *file, int on) 74667207b96SArnd Bergmann { 7478b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 7488b3d6663SArnd Bergmann 7498b3d6663SArnd Bergmann return fasync_helper(fd, file, on, &ctx->ibox_fasync); 7508b3d6663SArnd Bergmann } 7518b3d6663SArnd Bergmann 7528b3d6663SArnd Bergmann /* interrupt-level ibox callback function. */ 7538b3d6663SArnd Bergmann void spufs_ibox_callback(struct spu *spu) 7548b3d6663SArnd Bergmann { 7558b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 7568b3d6663SArnd Bergmann 757e65c2f6fSLuke Browning if (!ctx) 758e65c2f6fSLuke Browning return; 759e65c2f6fSLuke Browning 7608b3d6663SArnd Bergmann wake_up_all(&ctx->ibox_wq); 7618b3d6663SArnd Bergmann kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN); 76267207b96SArnd Bergmann } 76367207b96SArnd Bergmann 764cdcc89bbSArnd Bergmann /* 765cdcc89bbSArnd Bergmann * Read as many bytes from the interrupt mailbox as possible, until 766cdcc89bbSArnd Bergmann * one of the conditions becomes true: 767cdcc89bbSArnd Bergmann * 768cdcc89bbSArnd Bergmann * - no more data available in the mailbox 769cdcc89bbSArnd Bergmann * - end of the user provided buffer 770cdcc89bbSArnd Bergmann * - end of the mapped area 771cdcc89bbSArnd Bergmann * 772cdcc89bbSArnd Bergmann * If the file is opened without O_NONBLOCK, we wait here until 773cdcc89bbSArnd Bergmann * any data is available, but return when we have been able to 774cdcc89bbSArnd Bergmann * read something. 775cdcc89bbSArnd Bergmann */ 77667207b96SArnd Bergmann static ssize_t spufs_ibox_read(struct file *file, char __user *buf, 77767207b96SArnd Bergmann size_t len, loff_t *pos) 77867207b96SArnd Bergmann { 7798b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 780cdcc89bbSArnd Bergmann u32 ibox_data, __user *udata; 781cdcc89bbSArnd Bergmann ssize_t count; 78267207b96SArnd Bergmann 78367207b96SArnd Bergmann if (len < 4) 78467207b96SArnd Bergmann return -EINVAL; 78567207b96SArnd Bergmann 786cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_WRITE, buf, len)) 787cdcc89bbSArnd Bergmann return -EFAULT; 788cdcc89bbSArnd Bergmann 789cdcc89bbSArnd Bergmann udata = (void __user *)buf; 790cdcc89bbSArnd Bergmann 791c9101bdbSChristoph Hellwig count = spu_acquire(ctx); 792c9101bdbSChristoph Hellwig if (count) 793eebead5bSChristoph Hellwig goto out; 79467207b96SArnd Bergmann 795cdcc89bbSArnd Bergmann /* wait only for the first element */ 796cdcc89bbSArnd Bergmann count = 0; 79767207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 798eebead5bSChristoph Hellwig if (!spu_ibox_read(ctx, &ibox_data)) { 799cdcc89bbSArnd Bergmann count = -EAGAIN; 800eebead5bSChristoph Hellwig goto out_unlock; 801eebead5bSChristoph Hellwig } 80267207b96SArnd Bergmann } else { 803cdcc89bbSArnd Bergmann count = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data)); 804cdcc89bbSArnd Bergmann if (count) 805cdcc89bbSArnd Bergmann goto out; 806eebead5bSChristoph Hellwig } 807cdcc89bbSArnd Bergmann 808cdcc89bbSArnd Bergmann /* if we can't write at all, return -EFAULT */ 809cdcc89bbSArnd Bergmann count = __put_user(ibox_data, udata); 810cdcc89bbSArnd Bergmann if (count) 811eebead5bSChristoph Hellwig goto out_unlock; 812cdcc89bbSArnd Bergmann 813cdcc89bbSArnd Bergmann for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { 814cdcc89bbSArnd Bergmann int ret; 815cdcc89bbSArnd Bergmann ret = ctx->ops->ibox_read(ctx, &ibox_data); 816cdcc89bbSArnd Bergmann if (ret == 0) 817cdcc89bbSArnd Bergmann break; 818cdcc89bbSArnd Bergmann /* 819cdcc89bbSArnd Bergmann * at the end of the mapped area, we can fault 820cdcc89bbSArnd Bergmann * but still need to return the data we have 821cdcc89bbSArnd Bergmann * read successfully so far. 822cdcc89bbSArnd Bergmann */ 823cdcc89bbSArnd Bergmann ret = __put_user(ibox_data, udata); 824cdcc89bbSArnd Bergmann if (ret) 825cdcc89bbSArnd Bergmann break; 82667207b96SArnd Bergmann } 82767207b96SArnd Bergmann 828eebead5bSChristoph Hellwig out_unlock: 8298b3d6663SArnd Bergmann spu_release(ctx); 830eebead5bSChristoph Hellwig out: 831cdcc89bbSArnd Bergmann return count; 83267207b96SArnd Bergmann } 83367207b96SArnd Bergmann 83467207b96SArnd Bergmann static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait) 83567207b96SArnd Bergmann { 8368b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 83767207b96SArnd Bergmann unsigned int mask; 83867207b96SArnd Bergmann 8398b3d6663SArnd Bergmann poll_wait(file, &ctx->ibox_wq, wait); 84067207b96SArnd Bergmann 841c9101bdbSChristoph Hellwig /* 842c9101bdbSChristoph Hellwig * For now keep this uninterruptible and also ignore the rule 843c9101bdbSChristoph Hellwig * that poll should not sleep. Will be fixed later. 844c9101bdbSChristoph Hellwig */ 845c9101bdbSChristoph Hellwig mutex_lock(&ctx->state_mutex); 8463a843d7cSArnd Bergmann mask = ctx->ops->mbox_stat_poll(ctx, POLLIN | POLLRDNORM); 8473a843d7cSArnd Bergmann spu_release(ctx); 84867207b96SArnd Bergmann 84967207b96SArnd Bergmann return mask; 85067207b96SArnd Bergmann } 85167207b96SArnd Bergmann 8525dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_fops = { 85367207b96SArnd Bergmann .open = spufs_pipe_open, 85467207b96SArnd Bergmann .read = spufs_ibox_read, 85567207b96SArnd Bergmann .poll = spufs_ibox_poll, 85667207b96SArnd Bergmann .fasync = spufs_ibox_fasync, 857fc15351dSArnd Bergmann .llseek = no_llseek, 85867207b96SArnd Bergmann }; 85967207b96SArnd Bergmann 86067207b96SArnd Bergmann static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf, 86167207b96SArnd Bergmann size_t len, loff_t *pos) 86267207b96SArnd Bergmann { 8638b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 864c9101bdbSChristoph Hellwig ssize_t ret; 86567207b96SArnd Bergmann u32 ibox_stat; 86667207b96SArnd Bergmann 86767207b96SArnd Bergmann if (len < 4) 86867207b96SArnd Bergmann return -EINVAL; 86967207b96SArnd Bergmann 870c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 871c9101bdbSChristoph Hellwig if (ret) 872c9101bdbSChristoph Hellwig return ret; 8738b3d6663SArnd Bergmann ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff; 8748b3d6663SArnd Bergmann spu_release(ctx); 87567207b96SArnd Bergmann 87667207b96SArnd Bergmann if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat)) 87767207b96SArnd Bergmann return -EFAULT; 87867207b96SArnd Bergmann 87967207b96SArnd Bergmann return 4; 88067207b96SArnd Bergmann } 88167207b96SArnd Bergmann 8825dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_stat_fops = { 88367207b96SArnd Bergmann .open = spufs_pipe_open, 88467207b96SArnd Bergmann .read = spufs_ibox_stat_read, 885fc15351dSArnd Bergmann .llseek = no_llseek, 88667207b96SArnd Bergmann }; 88767207b96SArnd Bergmann 88867207b96SArnd Bergmann /* low-level mailbox write */ 8898b3d6663SArnd Bergmann size_t spu_wbox_write(struct spu_context *ctx, u32 data) 89067207b96SArnd Bergmann { 8918b3d6663SArnd Bergmann return ctx->ops->wbox_write(ctx, data); 89267207b96SArnd Bergmann } 89367207b96SArnd Bergmann 89467207b96SArnd Bergmann static int spufs_wbox_fasync(int fd, struct file *file, int on) 89567207b96SArnd Bergmann { 8968b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 8978b3d6663SArnd Bergmann int ret; 8988b3d6663SArnd Bergmann 8998b3d6663SArnd Bergmann ret = fasync_helper(fd, file, on, &ctx->wbox_fasync); 9008b3d6663SArnd Bergmann 9018b3d6663SArnd Bergmann return ret; 9028b3d6663SArnd Bergmann } 9038b3d6663SArnd Bergmann 9048b3d6663SArnd Bergmann /* interrupt-level wbox callback function. */ 9058b3d6663SArnd Bergmann void spufs_wbox_callback(struct spu *spu) 9068b3d6663SArnd Bergmann { 9078b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 9088b3d6663SArnd Bergmann 909e65c2f6fSLuke Browning if (!ctx) 910e65c2f6fSLuke Browning return; 911e65c2f6fSLuke Browning 9128b3d6663SArnd Bergmann wake_up_all(&ctx->wbox_wq); 9138b3d6663SArnd Bergmann kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT); 91467207b96SArnd Bergmann } 91567207b96SArnd Bergmann 916cdcc89bbSArnd Bergmann /* 917cdcc89bbSArnd Bergmann * Write as many bytes to the interrupt mailbox as possible, until 918cdcc89bbSArnd Bergmann * one of the conditions becomes true: 919cdcc89bbSArnd Bergmann * 920cdcc89bbSArnd Bergmann * - the mailbox is full 921cdcc89bbSArnd Bergmann * - end of the user provided buffer 922cdcc89bbSArnd Bergmann * - end of the mapped area 923cdcc89bbSArnd Bergmann * 924cdcc89bbSArnd Bergmann * If the file is opened without O_NONBLOCK, we wait here until 925cdcc89bbSArnd Bergmann * space is availabyl, but return when we have been able to 926cdcc89bbSArnd Bergmann * write something. 927cdcc89bbSArnd Bergmann */ 92867207b96SArnd Bergmann static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, 92967207b96SArnd Bergmann size_t len, loff_t *pos) 93067207b96SArnd Bergmann { 9318b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 932cdcc89bbSArnd Bergmann u32 wbox_data, __user *udata; 933cdcc89bbSArnd Bergmann ssize_t count; 93467207b96SArnd Bergmann 93567207b96SArnd Bergmann if (len < 4) 93667207b96SArnd Bergmann return -EINVAL; 93767207b96SArnd Bergmann 938cdcc89bbSArnd Bergmann udata = (void __user *)buf; 939cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_READ, buf, len)) 940cdcc89bbSArnd Bergmann return -EFAULT; 941cdcc89bbSArnd Bergmann 942cdcc89bbSArnd Bergmann if (__get_user(wbox_data, udata)) 94367207b96SArnd Bergmann return -EFAULT; 94467207b96SArnd Bergmann 945c9101bdbSChristoph Hellwig count = spu_acquire(ctx); 946c9101bdbSChristoph Hellwig if (count) 947eebead5bSChristoph Hellwig goto out; 9488b3d6663SArnd Bergmann 949cdcc89bbSArnd Bergmann /* 950cdcc89bbSArnd Bergmann * make sure we can at least write one element, by waiting 951cdcc89bbSArnd Bergmann * in case of !O_NONBLOCK 952cdcc89bbSArnd Bergmann */ 953cdcc89bbSArnd Bergmann count = 0; 95467207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 955eebead5bSChristoph Hellwig if (!spu_wbox_write(ctx, wbox_data)) { 956cdcc89bbSArnd Bergmann count = -EAGAIN; 957eebead5bSChristoph Hellwig goto out_unlock; 958eebead5bSChristoph Hellwig } 95967207b96SArnd Bergmann } else { 960cdcc89bbSArnd Bergmann count = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data)); 961cdcc89bbSArnd Bergmann if (count) 962cdcc89bbSArnd Bergmann goto out; 963eebead5bSChristoph Hellwig } 964eebead5bSChristoph Hellwig 9658b3d6663SArnd Bergmann 96696de0e25SJan Engelhardt /* write as much as possible */ 967cdcc89bbSArnd Bergmann for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { 968cdcc89bbSArnd Bergmann int ret; 969cdcc89bbSArnd Bergmann ret = __get_user(wbox_data, udata); 970cdcc89bbSArnd Bergmann if (ret) 971cdcc89bbSArnd Bergmann break; 972cdcc89bbSArnd Bergmann 973cdcc89bbSArnd Bergmann ret = spu_wbox_write(ctx, wbox_data); 974cdcc89bbSArnd Bergmann if (ret == 0) 975cdcc89bbSArnd Bergmann break; 976cdcc89bbSArnd Bergmann } 977cdcc89bbSArnd Bergmann 978eebead5bSChristoph Hellwig out_unlock: 979cdcc89bbSArnd Bergmann spu_release(ctx); 980eebead5bSChristoph Hellwig out: 981cdcc89bbSArnd Bergmann return count; 98267207b96SArnd Bergmann } 98367207b96SArnd Bergmann 98467207b96SArnd Bergmann static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait) 98567207b96SArnd Bergmann { 9868b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 98767207b96SArnd Bergmann unsigned int mask; 98867207b96SArnd Bergmann 9898b3d6663SArnd Bergmann poll_wait(file, &ctx->wbox_wq, wait); 99067207b96SArnd Bergmann 991c9101bdbSChristoph Hellwig /* 992c9101bdbSChristoph Hellwig * For now keep this uninterruptible and also ignore the rule 993c9101bdbSChristoph Hellwig * that poll should not sleep. Will be fixed later. 994c9101bdbSChristoph Hellwig */ 995c9101bdbSChristoph Hellwig mutex_lock(&ctx->state_mutex); 9963a843d7cSArnd Bergmann mask = ctx->ops->mbox_stat_poll(ctx, POLLOUT | POLLWRNORM); 9973a843d7cSArnd Bergmann spu_release(ctx); 99867207b96SArnd Bergmann 99967207b96SArnd Bergmann return mask; 100067207b96SArnd Bergmann } 100167207b96SArnd Bergmann 10025dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_fops = { 100367207b96SArnd Bergmann .open = spufs_pipe_open, 100467207b96SArnd Bergmann .write = spufs_wbox_write, 100567207b96SArnd Bergmann .poll = spufs_wbox_poll, 100667207b96SArnd Bergmann .fasync = spufs_wbox_fasync, 1007fc15351dSArnd Bergmann .llseek = no_llseek, 100867207b96SArnd Bergmann }; 100967207b96SArnd Bergmann 101067207b96SArnd Bergmann static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf, 101167207b96SArnd Bergmann size_t len, loff_t *pos) 101267207b96SArnd Bergmann { 10138b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 1014c9101bdbSChristoph Hellwig ssize_t ret; 101567207b96SArnd Bergmann u32 wbox_stat; 101667207b96SArnd Bergmann 101767207b96SArnd Bergmann if (len < 4) 101867207b96SArnd Bergmann return -EINVAL; 101967207b96SArnd Bergmann 1020c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1021c9101bdbSChristoph Hellwig if (ret) 1022c9101bdbSChristoph Hellwig return ret; 10238b3d6663SArnd Bergmann wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff; 10248b3d6663SArnd Bergmann spu_release(ctx); 102567207b96SArnd Bergmann 102667207b96SArnd Bergmann if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat)) 102767207b96SArnd Bergmann return -EFAULT; 102867207b96SArnd Bergmann 102967207b96SArnd Bergmann return 4; 103067207b96SArnd Bergmann } 103167207b96SArnd Bergmann 10325dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_stat_fops = { 103367207b96SArnd Bergmann .open = spufs_pipe_open, 103467207b96SArnd Bergmann .read = spufs_wbox_stat_read, 1035fc15351dSArnd Bergmann .llseek = no_llseek, 103667207b96SArnd Bergmann }; 103767207b96SArnd Bergmann 10386df10a82SMark Nutter static int spufs_signal1_open(struct inode *inode, struct file *file) 10396df10a82SMark Nutter { 10406df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 10416df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 104243c2bbd9SChristoph Hellwig 104347d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 10446df10a82SMark Nutter file->private_data = ctx; 104543c2bbd9SChristoph Hellwig if (!i->i_openers++) 10466df10a82SMark Nutter ctx->signal1 = inode->i_mapping; 104747d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 10486df10a82SMark Nutter return nonseekable_open(inode, file); 10496df10a82SMark Nutter } 10506df10a82SMark Nutter 105143c2bbd9SChristoph Hellwig static int 105243c2bbd9SChristoph Hellwig spufs_signal1_release(struct inode *inode, struct file *file) 105343c2bbd9SChristoph Hellwig { 105443c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 105543c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 105643c2bbd9SChristoph Hellwig 105747d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 105843c2bbd9SChristoph Hellwig if (!--i->i_openers) 105943c2bbd9SChristoph Hellwig ctx->signal1 = NULL; 106047d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 106143c2bbd9SChristoph Hellwig return 0; 106243c2bbd9SChristoph Hellwig } 106343c2bbd9SChristoph Hellwig 1064bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal1_read(struct spu_context *ctx, char __user *buf, 106567207b96SArnd Bergmann size_t len, loff_t *pos) 106667207b96SArnd Bergmann { 106717f88cebSDwayne Grant McConnell int ret = 0; 106867207b96SArnd Bergmann u32 data; 106967207b96SArnd Bergmann 107067207b96SArnd Bergmann if (len < 4) 107167207b96SArnd Bergmann return -EINVAL; 107267207b96SArnd Bergmann 107317f88cebSDwayne Grant McConnell if (ctx->csa.spu_chnlcnt_RW[3]) { 107417f88cebSDwayne Grant McConnell data = ctx->csa.spu_chnldata_RW[3]; 107517f88cebSDwayne Grant McConnell ret = 4; 107617f88cebSDwayne Grant McConnell } 10778b3d6663SArnd Bergmann 107817f88cebSDwayne Grant McConnell if (!ret) 107917f88cebSDwayne Grant McConnell goto out; 108017f88cebSDwayne Grant McConnell 108167207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 108267207b96SArnd Bergmann return -EFAULT; 108367207b96SArnd Bergmann 108417f88cebSDwayne Grant McConnell out: 108517f88cebSDwayne Grant McConnell return ret; 108667207b96SArnd Bergmann } 108767207b96SArnd Bergmann 1088bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal1_read(struct file *file, char __user *buf, 1089bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 1090bf1ab978SDwayne Grant McConnell { 1091bf1ab978SDwayne Grant McConnell int ret; 1092bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 1093bf1ab978SDwayne Grant McConnell 1094c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1095c9101bdbSChristoph Hellwig if (ret) 1096c9101bdbSChristoph Hellwig return ret; 1097bf1ab978SDwayne Grant McConnell ret = __spufs_signal1_read(ctx, buf, len, pos); 109827b1ea09SChristoph Hellwig spu_release_saved(ctx); 1099bf1ab978SDwayne Grant McConnell 1100bf1ab978SDwayne Grant McConnell return ret; 1101bf1ab978SDwayne Grant McConnell } 1102bf1ab978SDwayne Grant McConnell 110367207b96SArnd Bergmann static ssize_t spufs_signal1_write(struct file *file, const char __user *buf, 110467207b96SArnd Bergmann size_t len, loff_t *pos) 110567207b96SArnd Bergmann { 110667207b96SArnd Bergmann struct spu_context *ctx; 1107c9101bdbSChristoph Hellwig ssize_t ret; 110867207b96SArnd Bergmann u32 data; 110967207b96SArnd Bergmann 111067207b96SArnd Bergmann ctx = file->private_data; 111167207b96SArnd Bergmann 111267207b96SArnd Bergmann if (len < 4) 111367207b96SArnd Bergmann return -EINVAL; 111467207b96SArnd Bergmann 111567207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 111667207b96SArnd Bergmann return -EFAULT; 111767207b96SArnd Bergmann 1118c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1119c9101bdbSChristoph Hellwig if (ret) 1120c9101bdbSChristoph Hellwig return ret; 11218b3d6663SArnd Bergmann ctx->ops->signal1_write(ctx, data); 11228b3d6663SArnd Bergmann spu_release(ctx); 112367207b96SArnd Bergmann 112467207b96SArnd Bergmann return 4; 112567207b96SArnd Bergmann } 112667207b96SArnd Bergmann 1127b1e2270fSNick Piggin static int 1128b1e2270fSNick Piggin spufs_signal1_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 11296df10a82SMark Nutter { 113087ff6090SJeremy Kerr #if SPUFS_SIGNAL_MAP_SIZE == 0x1000 113187ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x14000, SPUFS_SIGNAL_MAP_SIZE); 113287ff6090SJeremy Kerr #elif SPUFS_SIGNAL_MAP_SIZE == 0x10000 113327d5bf2aSBenjamin Herrenschmidt /* For 64k pages, both signal1 and signal2 can be used to mmap the whole 113427d5bf2aSBenjamin Herrenschmidt * signal 1 and 2 area 113527d5bf2aSBenjamin Herrenschmidt */ 113687ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE); 113727d5bf2aSBenjamin Herrenschmidt #else 113827d5bf2aSBenjamin Herrenschmidt #error unsupported page size 113927d5bf2aSBenjamin Herrenschmidt #endif 11406df10a82SMark Nutter } 11416df10a82SMark Nutter 1142f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_signal1_mmap_vmops = { 1143b1e2270fSNick Piggin .fault = spufs_signal1_mmap_fault, 11446df10a82SMark Nutter }; 11456df10a82SMark Nutter 11466df10a82SMark Nutter static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma) 11476df10a82SMark Nutter { 11486df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 11496df10a82SMark Nutter return -EINVAL; 11506df10a82SMark Nutter 115178bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 115264b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 11536df10a82SMark Nutter 11546df10a82SMark Nutter vma->vm_ops = &spufs_signal1_mmap_vmops; 11556df10a82SMark Nutter return 0; 11566df10a82SMark Nutter } 11576df10a82SMark Nutter 11585dfe4c96SArjan van de Ven static const struct file_operations spufs_signal1_fops = { 11596df10a82SMark Nutter .open = spufs_signal1_open, 116043c2bbd9SChristoph Hellwig .release = spufs_signal1_release, 116167207b96SArnd Bergmann .read = spufs_signal1_read, 116267207b96SArnd Bergmann .write = spufs_signal1_write, 11636df10a82SMark Nutter .mmap = spufs_signal1_mmap, 1164fc15351dSArnd Bergmann .llseek = no_llseek, 116567207b96SArnd Bergmann }; 116667207b96SArnd Bergmann 1167d054b36fSJeremy Kerr static const struct file_operations spufs_signal1_nosched_fops = { 1168d054b36fSJeremy Kerr .open = spufs_signal1_open, 1169d054b36fSJeremy Kerr .release = spufs_signal1_release, 1170d054b36fSJeremy Kerr .write = spufs_signal1_write, 1171d054b36fSJeremy Kerr .mmap = spufs_signal1_mmap, 1172fc15351dSArnd Bergmann .llseek = no_llseek, 1173d054b36fSJeremy Kerr }; 1174d054b36fSJeremy Kerr 11756df10a82SMark Nutter static int spufs_signal2_open(struct inode *inode, struct file *file) 11766df10a82SMark Nutter { 11776df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 11786df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 117943c2bbd9SChristoph Hellwig 118047d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 11816df10a82SMark Nutter file->private_data = ctx; 118243c2bbd9SChristoph Hellwig if (!i->i_openers++) 11836df10a82SMark Nutter ctx->signal2 = inode->i_mapping; 118447d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 11856df10a82SMark Nutter return nonseekable_open(inode, file); 11866df10a82SMark Nutter } 11876df10a82SMark Nutter 118843c2bbd9SChristoph Hellwig static int 118943c2bbd9SChristoph Hellwig spufs_signal2_release(struct inode *inode, struct file *file) 119043c2bbd9SChristoph Hellwig { 119143c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 119243c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 119343c2bbd9SChristoph Hellwig 119447d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 119543c2bbd9SChristoph Hellwig if (!--i->i_openers) 119643c2bbd9SChristoph Hellwig ctx->signal2 = NULL; 119747d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 119843c2bbd9SChristoph Hellwig return 0; 119943c2bbd9SChristoph Hellwig } 120043c2bbd9SChristoph Hellwig 1201bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal2_read(struct spu_context *ctx, char __user *buf, 120267207b96SArnd Bergmann size_t len, loff_t *pos) 120367207b96SArnd Bergmann { 120417f88cebSDwayne Grant McConnell int ret = 0; 120567207b96SArnd Bergmann u32 data; 120667207b96SArnd Bergmann 120767207b96SArnd Bergmann if (len < 4) 120867207b96SArnd Bergmann return -EINVAL; 120967207b96SArnd Bergmann 121017f88cebSDwayne Grant McConnell if (ctx->csa.spu_chnlcnt_RW[4]) { 121117f88cebSDwayne Grant McConnell data = ctx->csa.spu_chnldata_RW[4]; 121217f88cebSDwayne Grant McConnell ret = 4; 121317f88cebSDwayne Grant McConnell } 12148b3d6663SArnd Bergmann 121517f88cebSDwayne Grant McConnell if (!ret) 121617f88cebSDwayne Grant McConnell goto out; 121717f88cebSDwayne Grant McConnell 121867207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 121967207b96SArnd Bergmann return -EFAULT; 122067207b96SArnd Bergmann 122117f88cebSDwayne Grant McConnell out: 1222bf1ab978SDwayne Grant McConnell return ret; 1223bf1ab978SDwayne Grant McConnell } 1224bf1ab978SDwayne Grant McConnell 1225bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal2_read(struct file *file, char __user *buf, 1226bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 1227bf1ab978SDwayne Grant McConnell { 1228bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 1229bf1ab978SDwayne Grant McConnell int ret; 1230bf1ab978SDwayne Grant McConnell 1231c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1232c9101bdbSChristoph Hellwig if (ret) 1233c9101bdbSChristoph Hellwig return ret; 1234bf1ab978SDwayne Grant McConnell ret = __spufs_signal2_read(ctx, buf, len, pos); 123527b1ea09SChristoph Hellwig spu_release_saved(ctx); 1236bf1ab978SDwayne Grant McConnell 1237bf1ab978SDwayne Grant McConnell return ret; 123867207b96SArnd Bergmann } 123967207b96SArnd Bergmann 124067207b96SArnd Bergmann static ssize_t spufs_signal2_write(struct file *file, const char __user *buf, 124167207b96SArnd Bergmann size_t len, loff_t *pos) 124267207b96SArnd Bergmann { 124367207b96SArnd Bergmann struct spu_context *ctx; 1244c9101bdbSChristoph Hellwig ssize_t ret; 124567207b96SArnd Bergmann u32 data; 124667207b96SArnd Bergmann 124767207b96SArnd Bergmann ctx = file->private_data; 124867207b96SArnd Bergmann 124967207b96SArnd Bergmann if (len < 4) 125067207b96SArnd Bergmann return -EINVAL; 125167207b96SArnd Bergmann 125267207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 125367207b96SArnd Bergmann return -EFAULT; 125467207b96SArnd Bergmann 1255c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1256c9101bdbSChristoph Hellwig if (ret) 1257c9101bdbSChristoph Hellwig return ret; 12588b3d6663SArnd Bergmann ctx->ops->signal2_write(ctx, data); 12598b3d6663SArnd Bergmann spu_release(ctx); 126067207b96SArnd Bergmann 126167207b96SArnd Bergmann return 4; 126267207b96SArnd Bergmann } 126367207b96SArnd Bergmann 126427d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1265b1e2270fSNick Piggin static int 1266b1e2270fSNick Piggin spufs_signal2_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 12676df10a82SMark Nutter { 126887ff6090SJeremy Kerr #if SPUFS_SIGNAL_MAP_SIZE == 0x1000 126987ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x1c000, SPUFS_SIGNAL_MAP_SIZE); 127087ff6090SJeremy Kerr #elif SPUFS_SIGNAL_MAP_SIZE == 0x10000 127127d5bf2aSBenjamin Herrenschmidt /* For 64k pages, both signal1 and signal2 can be used to mmap the whole 127227d5bf2aSBenjamin Herrenschmidt * signal 1 and 2 area 127327d5bf2aSBenjamin Herrenschmidt */ 127487ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE); 127527d5bf2aSBenjamin Herrenschmidt #else 127627d5bf2aSBenjamin Herrenschmidt #error unsupported page size 127727d5bf2aSBenjamin Herrenschmidt #endif 12786df10a82SMark Nutter } 12796df10a82SMark Nutter 1280f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_signal2_mmap_vmops = { 1281b1e2270fSNick Piggin .fault = spufs_signal2_mmap_fault, 12826df10a82SMark Nutter }; 12836df10a82SMark Nutter 12846df10a82SMark Nutter static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma) 12856df10a82SMark Nutter { 12866df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 12876df10a82SMark Nutter return -EINVAL; 12886df10a82SMark Nutter 128978bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 129064b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 12916df10a82SMark Nutter 12926df10a82SMark Nutter vma->vm_ops = &spufs_signal2_mmap_vmops; 12936df10a82SMark Nutter return 0; 12946df10a82SMark Nutter } 129527d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 129627d5bf2aSBenjamin Herrenschmidt #define spufs_signal2_mmap NULL 129727d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 12986df10a82SMark Nutter 12995dfe4c96SArjan van de Ven static const struct file_operations spufs_signal2_fops = { 13006df10a82SMark Nutter .open = spufs_signal2_open, 130143c2bbd9SChristoph Hellwig .release = spufs_signal2_release, 130267207b96SArnd Bergmann .read = spufs_signal2_read, 130367207b96SArnd Bergmann .write = spufs_signal2_write, 13046df10a82SMark Nutter .mmap = spufs_signal2_mmap, 1305fc15351dSArnd Bergmann .llseek = no_llseek, 130667207b96SArnd Bergmann }; 130767207b96SArnd Bergmann 1308d054b36fSJeremy Kerr static const struct file_operations spufs_signal2_nosched_fops = { 1309d054b36fSJeremy Kerr .open = spufs_signal2_open, 1310d054b36fSJeremy Kerr .release = spufs_signal2_release, 1311d054b36fSJeremy Kerr .write = spufs_signal2_write, 1312d054b36fSJeremy Kerr .mmap = spufs_signal2_mmap, 1313fc15351dSArnd Bergmann .llseek = no_llseek, 1314d054b36fSJeremy Kerr }; 1315d054b36fSJeremy Kerr 1316104f0cc2SMichael Ellerman /* 1317104f0cc2SMichael Ellerman * This is a wrapper around DEFINE_SIMPLE_ATTRIBUTE which does the 1318104f0cc2SMichael Ellerman * work of acquiring (or not) the SPU context before calling through 1319104f0cc2SMichael Ellerman * to the actual get routine. The set routine is called directly. 1320104f0cc2SMichael Ellerman */ 1321104f0cc2SMichael Ellerman #define SPU_ATTR_NOACQUIRE 0 1322104f0cc2SMichael Ellerman #define SPU_ATTR_ACQUIRE 1 1323104f0cc2SMichael Ellerman #define SPU_ATTR_ACQUIRE_SAVED 2 1324104f0cc2SMichael Ellerman 1325104f0cc2SMichael Ellerman #define DEFINE_SPUFS_ATTRIBUTE(__name, __get, __set, __fmt, __acquire) \ 1326197b1a82SChristoph Hellwig static int __##__get(void *data, u64 *val) \ 1327104f0cc2SMichael Ellerman { \ 1328104f0cc2SMichael Ellerman struct spu_context *ctx = data; \ 1329c9101bdbSChristoph Hellwig int ret = 0; \ 1330104f0cc2SMichael Ellerman \ 1331104f0cc2SMichael Ellerman if (__acquire == SPU_ATTR_ACQUIRE) { \ 1332c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); \ 1333c9101bdbSChristoph Hellwig if (ret) \ 1334c9101bdbSChristoph Hellwig return ret; \ 1335197b1a82SChristoph Hellwig *val = __get(ctx); \ 1336104f0cc2SMichael Ellerman spu_release(ctx); \ 1337104f0cc2SMichael Ellerman } else if (__acquire == SPU_ATTR_ACQUIRE_SAVED) { \ 1338c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); \ 1339c9101bdbSChristoph Hellwig if (ret) \ 1340c9101bdbSChristoph Hellwig return ret; \ 1341197b1a82SChristoph Hellwig *val = __get(ctx); \ 1342104f0cc2SMichael Ellerman spu_release_saved(ctx); \ 1343104f0cc2SMichael Ellerman } else \ 1344197b1a82SChristoph Hellwig *val = __get(ctx); \ 1345104f0cc2SMichael Ellerman \ 1346197b1a82SChristoph Hellwig return 0; \ 1347104f0cc2SMichael Ellerman } \ 1348197b1a82SChristoph Hellwig DEFINE_SPUFS_SIMPLE_ATTRIBUTE(__name, __##__get, __set, __fmt); 1349104f0cc2SMichael Ellerman 1350197b1a82SChristoph Hellwig static int spufs_signal1_type_set(void *data, u64 val) 135167207b96SArnd Bergmann { 135267207b96SArnd Bergmann struct spu_context *ctx = data; 1353c9101bdbSChristoph Hellwig int ret; 135467207b96SArnd Bergmann 1355c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1356c9101bdbSChristoph Hellwig if (ret) 1357c9101bdbSChristoph Hellwig return ret; 13588b3d6663SArnd Bergmann ctx->ops->signal1_type_set(ctx, val); 13598b3d6663SArnd Bergmann spu_release(ctx); 1360197b1a82SChristoph Hellwig 1361197b1a82SChristoph Hellwig return 0; 136267207b96SArnd Bergmann } 136367207b96SArnd Bergmann 1364104f0cc2SMichael Ellerman static u64 spufs_signal1_type_get(struct spu_context *ctx) 1365bf1ab978SDwayne Grant McConnell { 1366bf1ab978SDwayne Grant McConnell return ctx->ops->signal1_type_get(ctx); 1367bf1ab978SDwayne Grant McConnell } 1368104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get, 1369af8b44e0SJeremy Kerr spufs_signal1_type_set, "%llu\n", SPU_ATTR_ACQUIRE); 1370bf1ab978SDwayne Grant McConnell 137167207b96SArnd Bergmann 1372197b1a82SChristoph Hellwig static int spufs_signal2_type_set(void *data, u64 val) 137367207b96SArnd Bergmann { 137467207b96SArnd Bergmann struct spu_context *ctx = data; 1375c9101bdbSChristoph Hellwig int ret; 137667207b96SArnd Bergmann 1377c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1378c9101bdbSChristoph Hellwig if (ret) 1379c9101bdbSChristoph Hellwig return ret; 13808b3d6663SArnd Bergmann ctx->ops->signal2_type_set(ctx, val); 13818b3d6663SArnd Bergmann spu_release(ctx); 1382197b1a82SChristoph Hellwig 1383197b1a82SChristoph Hellwig return 0; 138467207b96SArnd Bergmann } 138567207b96SArnd Bergmann 1386104f0cc2SMichael Ellerman static u64 spufs_signal2_type_get(struct spu_context *ctx) 1387bf1ab978SDwayne Grant McConnell { 1388bf1ab978SDwayne Grant McConnell return ctx->ops->signal2_type_get(ctx); 1389bf1ab978SDwayne Grant McConnell } 1390104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get, 1391af8b44e0SJeremy Kerr spufs_signal2_type_set, "%llu\n", SPU_ATTR_ACQUIRE); 139267207b96SArnd Bergmann 139327d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1394b1e2270fSNick Piggin static int 1395b1e2270fSNick Piggin spufs_mss_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 1396d9379c4bSarnd@arndb.de { 139787ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x0000, SPUFS_MSS_MAP_SIZE); 1398d9379c4bSarnd@arndb.de } 1399d9379c4bSarnd@arndb.de 1400f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_mss_mmap_vmops = { 1401b1e2270fSNick Piggin .fault = spufs_mss_mmap_fault, 1402d9379c4bSarnd@arndb.de }; 1403d9379c4bSarnd@arndb.de 1404d9379c4bSarnd@arndb.de /* 1405d9379c4bSarnd@arndb.de * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. 1406d9379c4bSarnd@arndb.de */ 1407d9379c4bSarnd@arndb.de static int spufs_mss_mmap(struct file *file, struct vm_area_struct *vma) 1408d9379c4bSarnd@arndb.de { 1409d9379c4bSarnd@arndb.de if (!(vma->vm_flags & VM_SHARED)) 1410d9379c4bSarnd@arndb.de return -EINVAL; 1411d9379c4bSarnd@arndb.de 141278bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 141364b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 1414d9379c4bSarnd@arndb.de 1415d9379c4bSarnd@arndb.de vma->vm_ops = &spufs_mss_mmap_vmops; 1416d9379c4bSarnd@arndb.de return 0; 1417d9379c4bSarnd@arndb.de } 141827d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 141927d5bf2aSBenjamin Herrenschmidt #define spufs_mss_mmap NULL 142027d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 1421d9379c4bSarnd@arndb.de 1422d9379c4bSarnd@arndb.de static int spufs_mss_open(struct inode *inode, struct file *file) 1423d9379c4bSarnd@arndb.de { 1424d9379c4bSarnd@arndb.de struct spufs_inode_info *i = SPUFS_I(inode); 142517e0e270SBenjamin Herrenschmidt struct spu_context *ctx = i->i_ctx; 1426d9379c4bSarnd@arndb.de 1427d9379c4bSarnd@arndb.de file->private_data = i->i_ctx; 142843c2bbd9SChristoph Hellwig 142947d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 143043c2bbd9SChristoph Hellwig if (!i->i_openers++) 143117e0e270SBenjamin Herrenschmidt ctx->mss = inode->i_mapping; 143247d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 1433d9379c4bSarnd@arndb.de return nonseekable_open(inode, file); 1434d9379c4bSarnd@arndb.de } 1435d9379c4bSarnd@arndb.de 143643c2bbd9SChristoph Hellwig static int 143743c2bbd9SChristoph Hellwig spufs_mss_release(struct inode *inode, struct file *file) 143843c2bbd9SChristoph Hellwig { 143943c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 144043c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 144143c2bbd9SChristoph Hellwig 144247d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 144343c2bbd9SChristoph Hellwig if (!--i->i_openers) 144443c2bbd9SChristoph Hellwig ctx->mss = NULL; 144547d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 144643c2bbd9SChristoph Hellwig return 0; 144743c2bbd9SChristoph Hellwig } 144843c2bbd9SChristoph Hellwig 14495dfe4c96SArjan van de Ven static const struct file_operations spufs_mss_fops = { 1450d9379c4bSarnd@arndb.de .open = spufs_mss_open, 145143c2bbd9SChristoph Hellwig .release = spufs_mss_release, 1452d9379c4bSarnd@arndb.de .mmap = spufs_mss_mmap, 1453fc15351dSArnd Bergmann .llseek = no_llseek, 145427d5bf2aSBenjamin Herrenschmidt }; 145527d5bf2aSBenjamin Herrenschmidt 1456b1e2270fSNick Piggin static int 1457b1e2270fSNick Piggin spufs_psmap_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 145827d5bf2aSBenjamin Herrenschmidt { 145987ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x0000, SPUFS_PS_MAP_SIZE); 146027d5bf2aSBenjamin Herrenschmidt } 146127d5bf2aSBenjamin Herrenschmidt 1462f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_psmap_mmap_vmops = { 1463b1e2270fSNick Piggin .fault = spufs_psmap_mmap_fault, 146427d5bf2aSBenjamin Herrenschmidt }; 146527d5bf2aSBenjamin Herrenschmidt 146627d5bf2aSBenjamin Herrenschmidt /* 146727d5bf2aSBenjamin Herrenschmidt * mmap support for full problem state area [0x00000 - 0x1ffff]. 146827d5bf2aSBenjamin Herrenschmidt */ 146927d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma) 147027d5bf2aSBenjamin Herrenschmidt { 147127d5bf2aSBenjamin Herrenschmidt if (!(vma->vm_flags & VM_SHARED)) 147227d5bf2aSBenjamin Herrenschmidt return -EINVAL; 147327d5bf2aSBenjamin Herrenschmidt 147478bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 147564b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 147627d5bf2aSBenjamin Herrenschmidt 147727d5bf2aSBenjamin Herrenschmidt vma->vm_ops = &spufs_psmap_mmap_vmops; 147827d5bf2aSBenjamin Herrenschmidt return 0; 147927d5bf2aSBenjamin Herrenschmidt } 148027d5bf2aSBenjamin Herrenschmidt 148127d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_open(struct inode *inode, struct file *file) 148227d5bf2aSBenjamin Herrenschmidt { 148327d5bf2aSBenjamin Herrenschmidt struct spufs_inode_info *i = SPUFS_I(inode); 148417e0e270SBenjamin Herrenschmidt struct spu_context *ctx = i->i_ctx; 148527d5bf2aSBenjamin Herrenschmidt 148647d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 148727d5bf2aSBenjamin Herrenschmidt file->private_data = i->i_ctx; 148843c2bbd9SChristoph Hellwig if (!i->i_openers++) 148917e0e270SBenjamin Herrenschmidt ctx->psmap = inode->i_mapping; 149047d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 149127d5bf2aSBenjamin Herrenschmidt return nonseekable_open(inode, file); 149227d5bf2aSBenjamin Herrenschmidt } 149327d5bf2aSBenjamin Herrenschmidt 149443c2bbd9SChristoph Hellwig static int 149543c2bbd9SChristoph Hellwig spufs_psmap_release(struct inode *inode, struct file *file) 149643c2bbd9SChristoph Hellwig { 149743c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 149843c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 149943c2bbd9SChristoph Hellwig 150047d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 150143c2bbd9SChristoph Hellwig if (!--i->i_openers) 150243c2bbd9SChristoph Hellwig ctx->psmap = NULL; 150347d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 150443c2bbd9SChristoph Hellwig return 0; 150543c2bbd9SChristoph Hellwig } 150643c2bbd9SChristoph Hellwig 15075dfe4c96SArjan van de Ven static const struct file_operations spufs_psmap_fops = { 150827d5bf2aSBenjamin Herrenschmidt .open = spufs_psmap_open, 150943c2bbd9SChristoph Hellwig .release = spufs_psmap_release, 151027d5bf2aSBenjamin Herrenschmidt .mmap = spufs_psmap_mmap, 1511fc15351dSArnd Bergmann .llseek = no_llseek, 1512d9379c4bSarnd@arndb.de }; 1513d9379c4bSarnd@arndb.de 1514d9379c4bSarnd@arndb.de 151527d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1516b1e2270fSNick Piggin static int 1517b1e2270fSNick Piggin spufs_mfc_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 15186df10a82SMark Nutter { 151987ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x3000, SPUFS_MFC_MAP_SIZE); 15206df10a82SMark Nutter } 15216df10a82SMark Nutter 1522f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_mfc_mmap_vmops = { 1523b1e2270fSNick Piggin .fault = spufs_mfc_mmap_fault, 15246df10a82SMark Nutter }; 15256df10a82SMark Nutter 15266df10a82SMark Nutter /* 15276df10a82SMark Nutter * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. 15286df10a82SMark Nutter */ 15296df10a82SMark Nutter static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma) 15306df10a82SMark Nutter { 15316df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 15326df10a82SMark Nutter return -EINVAL; 15336df10a82SMark Nutter 153478bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 153564b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 15366df10a82SMark Nutter 15376df10a82SMark Nutter vma->vm_ops = &spufs_mfc_mmap_vmops; 15386df10a82SMark Nutter return 0; 15396df10a82SMark Nutter } 154027d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 154127d5bf2aSBenjamin Herrenschmidt #define spufs_mfc_mmap NULL 154227d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 1543a33a7d73SArnd Bergmann 1544a33a7d73SArnd Bergmann static int spufs_mfc_open(struct inode *inode, struct file *file) 1545a33a7d73SArnd Bergmann { 1546a33a7d73SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 1547a33a7d73SArnd Bergmann struct spu_context *ctx = i->i_ctx; 1548a33a7d73SArnd Bergmann 1549a33a7d73SArnd Bergmann /* we don't want to deal with DMA into other processes */ 1550a33a7d73SArnd Bergmann if (ctx->owner != current->mm) 1551a33a7d73SArnd Bergmann return -EINVAL; 1552a33a7d73SArnd Bergmann 1553a33a7d73SArnd Bergmann if (atomic_read(&inode->i_count) != 1) 1554a33a7d73SArnd Bergmann return -EBUSY; 1555a33a7d73SArnd Bergmann 155647d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 1557a33a7d73SArnd Bergmann file->private_data = ctx; 155843c2bbd9SChristoph Hellwig if (!i->i_openers++) 155917e0e270SBenjamin Herrenschmidt ctx->mfc = inode->i_mapping; 156047d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 1561a33a7d73SArnd Bergmann return nonseekable_open(inode, file); 1562a33a7d73SArnd Bergmann } 1563a33a7d73SArnd Bergmann 156443c2bbd9SChristoph Hellwig static int 156543c2bbd9SChristoph Hellwig spufs_mfc_release(struct inode *inode, struct file *file) 156643c2bbd9SChristoph Hellwig { 156743c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 156843c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 156943c2bbd9SChristoph Hellwig 157047d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 157143c2bbd9SChristoph Hellwig if (!--i->i_openers) 157243c2bbd9SChristoph Hellwig ctx->mfc = NULL; 157347d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 157443c2bbd9SChristoph Hellwig return 0; 157543c2bbd9SChristoph Hellwig } 157643c2bbd9SChristoph Hellwig 1577a33a7d73SArnd Bergmann /* interrupt-level mfc callback function. */ 1578a33a7d73SArnd Bergmann void spufs_mfc_callback(struct spu *spu) 1579a33a7d73SArnd Bergmann { 1580a33a7d73SArnd Bergmann struct spu_context *ctx = spu->ctx; 1581a33a7d73SArnd Bergmann 1582e65c2f6fSLuke Browning if (!ctx) 1583e65c2f6fSLuke Browning return; 1584e65c2f6fSLuke Browning 1585a33a7d73SArnd Bergmann wake_up_all(&ctx->mfc_wq); 1586a33a7d73SArnd Bergmann 1587e48b1b45SHarvey Harrison pr_debug("%s %s\n", __func__, spu->name); 1588a33a7d73SArnd Bergmann if (ctx->mfc_fasync) { 1589a33a7d73SArnd Bergmann u32 free_elements, tagstatus; 1590a33a7d73SArnd Bergmann unsigned int mask; 1591a33a7d73SArnd Bergmann 1592a33a7d73SArnd Bergmann /* no need for spu_acquire in interrupt context */ 1593a33a7d73SArnd Bergmann free_elements = ctx->ops->get_mfc_free_elements(ctx); 1594a33a7d73SArnd Bergmann tagstatus = ctx->ops->read_mfc_tagstatus(ctx); 1595a33a7d73SArnd Bergmann 1596a33a7d73SArnd Bergmann mask = 0; 1597a33a7d73SArnd Bergmann if (free_elements & 0xffff) 1598a33a7d73SArnd Bergmann mask |= POLLOUT; 1599a33a7d73SArnd Bergmann if (tagstatus & ctx->tagwait) 1600a33a7d73SArnd Bergmann mask |= POLLIN; 1601a33a7d73SArnd Bergmann 1602a33a7d73SArnd Bergmann kill_fasync(&ctx->mfc_fasync, SIGIO, mask); 1603a33a7d73SArnd Bergmann } 1604a33a7d73SArnd Bergmann } 1605a33a7d73SArnd Bergmann 1606a33a7d73SArnd Bergmann static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status) 1607a33a7d73SArnd Bergmann { 1608a33a7d73SArnd Bergmann /* See if there is one tag group is complete */ 1609a33a7d73SArnd Bergmann /* FIXME we need locking around tagwait */ 1610a33a7d73SArnd Bergmann *status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait; 1611a33a7d73SArnd Bergmann ctx->tagwait &= ~*status; 1612a33a7d73SArnd Bergmann if (*status) 1613a33a7d73SArnd Bergmann return 1; 1614a33a7d73SArnd Bergmann 1615a33a7d73SArnd Bergmann /* enable interrupt waiting for any tag group, 1616a33a7d73SArnd Bergmann may silently fail if interrupts are already enabled */ 1617a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 1618a33a7d73SArnd Bergmann return 0; 1619a33a7d73SArnd Bergmann } 1620a33a7d73SArnd Bergmann 1621a33a7d73SArnd Bergmann static ssize_t spufs_mfc_read(struct file *file, char __user *buffer, 1622a33a7d73SArnd Bergmann size_t size, loff_t *pos) 1623a33a7d73SArnd Bergmann { 1624a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1625a33a7d73SArnd Bergmann int ret = -EINVAL; 1626a33a7d73SArnd Bergmann u32 status; 1627a33a7d73SArnd Bergmann 1628a33a7d73SArnd Bergmann if (size != 4) 1629a33a7d73SArnd Bergmann goto out; 1630a33a7d73SArnd Bergmann 1631c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1632c9101bdbSChristoph Hellwig if (ret) 1633c9101bdbSChristoph Hellwig return ret; 1634c9101bdbSChristoph Hellwig 1635c9101bdbSChristoph Hellwig ret = -EINVAL; 1636a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 1637a33a7d73SArnd Bergmann status = ctx->ops->read_mfc_tagstatus(ctx); 1638a33a7d73SArnd Bergmann if (!(status & ctx->tagwait)) 1639a33a7d73SArnd Bergmann ret = -EAGAIN; 1640a33a7d73SArnd Bergmann else 1641c9101bdbSChristoph Hellwig /* XXX(hch): shouldn't we clear ret here? */ 1642a33a7d73SArnd Bergmann ctx->tagwait &= ~status; 1643a33a7d73SArnd Bergmann } else { 1644a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1645a33a7d73SArnd Bergmann spufs_read_mfc_tagstatus(ctx, &status)); 1646a33a7d73SArnd Bergmann if (ret) 1647a33a7d73SArnd Bergmann goto out; 1648eebead5bSChristoph Hellwig } 1649eebead5bSChristoph Hellwig spu_release(ctx); 1650a33a7d73SArnd Bergmann 1651a33a7d73SArnd Bergmann ret = 4; 1652a33a7d73SArnd Bergmann if (copy_to_user(buffer, &status, 4)) 1653a33a7d73SArnd Bergmann ret = -EFAULT; 1654a33a7d73SArnd Bergmann 1655a33a7d73SArnd Bergmann out: 1656a33a7d73SArnd Bergmann return ret; 1657a33a7d73SArnd Bergmann } 1658a33a7d73SArnd Bergmann 1659a33a7d73SArnd Bergmann static int spufs_check_valid_dma(struct mfc_dma_command *cmd) 1660a33a7d73SArnd Bergmann { 16619477e455SStephen Rothwell pr_debug("queueing DMA %x %llx %x %x %x\n", cmd->lsa, 1662a33a7d73SArnd Bergmann cmd->ea, cmd->size, cmd->tag, cmd->cmd); 1663a33a7d73SArnd Bergmann 1664a33a7d73SArnd Bergmann switch (cmd->cmd) { 1665a33a7d73SArnd Bergmann case MFC_PUT_CMD: 1666a33a7d73SArnd Bergmann case MFC_PUTF_CMD: 1667a33a7d73SArnd Bergmann case MFC_PUTB_CMD: 1668a33a7d73SArnd Bergmann case MFC_GET_CMD: 1669a33a7d73SArnd Bergmann case MFC_GETF_CMD: 1670a33a7d73SArnd Bergmann case MFC_GETB_CMD: 1671a33a7d73SArnd Bergmann break; 1672a33a7d73SArnd Bergmann default: 1673a33a7d73SArnd Bergmann pr_debug("invalid DMA opcode %x\n", cmd->cmd); 1674a33a7d73SArnd Bergmann return -EIO; 1675a33a7d73SArnd Bergmann } 1676a33a7d73SArnd Bergmann 1677a33a7d73SArnd Bergmann if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) { 16789477e455SStephen Rothwell pr_debug("invalid DMA alignment, ea %llx lsa %x\n", 1679a33a7d73SArnd Bergmann cmd->ea, cmd->lsa); 1680a33a7d73SArnd Bergmann return -EIO; 1681a33a7d73SArnd Bergmann } 1682a33a7d73SArnd Bergmann 1683a33a7d73SArnd Bergmann switch (cmd->size & 0xf) { 1684a33a7d73SArnd Bergmann case 1: 1685a33a7d73SArnd Bergmann break; 1686a33a7d73SArnd Bergmann case 2: 1687a33a7d73SArnd Bergmann if (cmd->lsa & 1) 1688a33a7d73SArnd Bergmann goto error; 1689a33a7d73SArnd Bergmann break; 1690a33a7d73SArnd Bergmann case 4: 1691a33a7d73SArnd Bergmann if (cmd->lsa & 3) 1692a33a7d73SArnd Bergmann goto error; 1693a33a7d73SArnd Bergmann break; 1694a33a7d73SArnd Bergmann case 8: 1695a33a7d73SArnd Bergmann if (cmd->lsa & 7) 1696a33a7d73SArnd Bergmann goto error; 1697a33a7d73SArnd Bergmann break; 1698a33a7d73SArnd Bergmann case 0: 1699a33a7d73SArnd Bergmann if (cmd->lsa & 15) 1700a33a7d73SArnd Bergmann goto error; 1701a33a7d73SArnd Bergmann break; 1702a33a7d73SArnd Bergmann error: 1703a33a7d73SArnd Bergmann default: 1704a33a7d73SArnd Bergmann pr_debug("invalid DMA alignment %x for size %x\n", 1705a33a7d73SArnd Bergmann cmd->lsa & 0xf, cmd->size); 1706a33a7d73SArnd Bergmann return -EIO; 1707a33a7d73SArnd Bergmann } 1708a33a7d73SArnd Bergmann 1709a33a7d73SArnd Bergmann if (cmd->size > 16 * 1024) { 1710a33a7d73SArnd Bergmann pr_debug("invalid DMA size %x\n", cmd->size); 1711a33a7d73SArnd Bergmann return -EIO; 1712a33a7d73SArnd Bergmann } 1713a33a7d73SArnd Bergmann 1714a33a7d73SArnd Bergmann if (cmd->tag & 0xfff0) { 1715a33a7d73SArnd Bergmann /* we reserve the higher tag numbers for kernel use */ 1716a33a7d73SArnd Bergmann pr_debug("invalid DMA tag\n"); 1717a33a7d73SArnd Bergmann return -EIO; 1718a33a7d73SArnd Bergmann } 1719a33a7d73SArnd Bergmann 1720a33a7d73SArnd Bergmann if (cmd->class) { 1721a33a7d73SArnd Bergmann /* not supported in this version */ 1722a33a7d73SArnd Bergmann pr_debug("invalid DMA class\n"); 1723a33a7d73SArnd Bergmann return -EIO; 1724a33a7d73SArnd Bergmann } 1725a33a7d73SArnd Bergmann 1726a33a7d73SArnd Bergmann return 0; 1727a33a7d73SArnd Bergmann } 1728a33a7d73SArnd Bergmann 1729a33a7d73SArnd Bergmann static int spu_send_mfc_command(struct spu_context *ctx, 1730a33a7d73SArnd Bergmann struct mfc_dma_command cmd, 1731a33a7d73SArnd Bergmann int *error) 1732a33a7d73SArnd Bergmann { 1733a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 1734a33a7d73SArnd Bergmann if (*error == -EAGAIN) { 1735a33a7d73SArnd Bergmann /* wait for any tag group to complete 1736a33a7d73SArnd Bergmann so we have space for the new command */ 1737a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 1738a33a7d73SArnd Bergmann /* try again, because the queue might be 1739a33a7d73SArnd Bergmann empty again */ 1740a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 1741a33a7d73SArnd Bergmann if (*error == -EAGAIN) 1742a33a7d73SArnd Bergmann return 0; 1743a33a7d73SArnd Bergmann } 1744a33a7d73SArnd Bergmann return 1; 1745a33a7d73SArnd Bergmann } 1746a33a7d73SArnd Bergmann 1747a33a7d73SArnd Bergmann static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer, 1748a33a7d73SArnd Bergmann size_t size, loff_t *pos) 1749a33a7d73SArnd Bergmann { 1750a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1751a33a7d73SArnd Bergmann struct mfc_dma_command cmd; 1752a33a7d73SArnd Bergmann int ret = -EINVAL; 1753a33a7d73SArnd Bergmann 1754a33a7d73SArnd Bergmann if (size != sizeof cmd) 1755a33a7d73SArnd Bergmann goto out; 1756a33a7d73SArnd Bergmann 1757a33a7d73SArnd Bergmann ret = -EFAULT; 1758a33a7d73SArnd Bergmann if (copy_from_user(&cmd, buffer, sizeof cmd)) 1759a33a7d73SArnd Bergmann goto out; 1760a33a7d73SArnd Bergmann 1761a33a7d73SArnd Bergmann ret = spufs_check_valid_dma(&cmd); 1762a33a7d73SArnd Bergmann if (ret) 1763a33a7d73SArnd Bergmann goto out; 1764a33a7d73SArnd Bergmann 1765c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1766c9101bdbSChristoph Hellwig if (ret) 1767c9101bdbSChristoph Hellwig goto out; 1768c9101bdbSChristoph Hellwig 176933bfd7a7SArnd Bergmann ret = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); 1770577f8f10SAkinobu Mita if (ret) 1771577f8f10SAkinobu Mita goto out; 1772577f8f10SAkinobu Mita 1773a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 1774a33a7d73SArnd Bergmann ret = ctx->ops->send_mfc_command(ctx, &cmd); 1775a33a7d73SArnd Bergmann } else { 1776a33a7d73SArnd Bergmann int status; 1777a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1778a33a7d73SArnd Bergmann spu_send_mfc_command(ctx, cmd, &status)); 1779eebead5bSChristoph Hellwig if (ret) 1780eebead5bSChristoph Hellwig goto out; 1781a33a7d73SArnd Bergmann if (status) 1782a33a7d73SArnd Bergmann ret = status; 1783a33a7d73SArnd Bergmann } 1784a33a7d73SArnd Bergmann 1785a33a7d73SArnd Bergmann if (ret) 1786933b0e35SKazunori Asayama goto out_unlock; 1787a33a7d73SArnd Bergmann 1788a33a7d73SArnd Bergmann ctx->tagwait |= 1 << cmd.tag; 17893692dc66SMasato Noguchi ret = size; 1790a33a7d73SArnd Bergmann 1791933b0e35SKazunori Asayama out_unlock: 1792933b0e35SKazunori Asayama spu_release(ctx); 1793a33a7d73SArnd Bergmann out: 1794a33a7d73SArnd Bergmann return ret; 1795a33a7d73SArnd Bergmann } 1796a33a7d73SArnd Bergmann 1797a33a7d73SArnd Bergmann static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait) 1798a33a7d73SArnd Bergmann { 1799a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1800a33a7d73SArnd Bergmann u32 free_elements, tagstatus; 1801a33a7d73SArnd Bergmann unsigned int mask; 1802a33a7d73SArnd Bergmann 1803933b0e35SKazunori Asayama poll_wait(file, &ctx->mfc_wq, wait); 1804933b0e35SKazunori Asayama 1805c9101bdbSChristoph Hellwig /* 1806c9101bdbSChristoph Hellwig * For now keep this uninterruptible and also ignore the rule 1807c9101bdbSChristoph Hellwig * that poll should not sleep. Will be fixed later. 1808c9101bdbSChristoph Hellwig */ 1809c9101bdbSChristoph Hellwig mutex_lock(&ctx->state_mutex); 1810a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2); 1811a33a7d73SArnd Bergmann free_elements = ctx->ops->get_mfc_free_elements(ctx); 1812a33a7d73SArnd Bergmann tagstatus = ctx->ops->read_mfc_tagstatus(ctx); 1813a33a7d73SArnd Bergmann spu_release(ctx); 1814a33a7d73SArnd Bergmann 1815a33a7d73SArnd Bergmann mask = 0; 1816a33a7d73SArnd Bergmann if (free_elements & 0xffff) 1817a33a7d73SArnd Bergmann mask |= POLLOUT | POLLWRNORM; 1818a33a7d73SArnd Bergmann if (tagstatus & ctx->tagwait) 1819a33a7d73SArnd Bergmann mask |= POLLIN | POLLRDNORM; 1820a33a7d73SArnd Bergmann 1821e48b1b45SHarvey Harrison pr_debug("%s: free %d tagstatus %d tagwait %d\n", __func__, 1822a33a7d73SArnd Bergmann free_elements, tagstatus, ctx->tagwait); 1823a33a7d73SArnd Bergmann 1824a33a7d73SArnd Bergmann return mask; 1825a33a7d73SArnd Bergmann } 1826a33a7d73SArnd Bergmann 182773b6af8aSAl Viro static int spufs_mfc_flush(struct file *file, fl_owner_t id) 1828a33a7d73SArnd Bergmann { 1829a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1830a33a7d73SArnd Bergmann int ret; 1831a33a7d73SArnd Bergmann 1832c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1833c9101bdbSChristoph Hellwig if (ret) 1834eebead5bSChristoph Hellwig goto out; 1835a33a7d73SArnd Bergmann #if 0 1836a33a7d73SArnd Bergmann /* this currently hangs */ 1837a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1838a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2)); 1839a33a7d73SArnd Bergmann if (ret) 1840a33a7d73SArnd Bergmann goto out; 1841a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1842a33a7d73SArnd Bergmann ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait); 1843eebead5bSChristoph Hellwig if (ret) 1844eebead5bSChristoph Hellwig goto out; 1845a33a7d73SArnd Bergmann #else 1846a33a7d73SArnd Bergmann ret = 0; 1847a33a7d73SArnd Bergmann #endif 1848a33a7d73SArnd Bergmann spu_release(ctx); 1849eebead5bSChristoph Hellwig out: 1850a33a7d73SArnd Bergmann return ret; 1851a33a7d73SArnd Bergmann } 1852a33a7d73SArnd Bergmann 185302c24a82SJosef Bacik static int spufs_mfc_fsync(struct file *file, loff_t start, loff_t end, int datasync) 1854a33a7d73SArnd Bergmann { 185502c24a82SJosef Bacik struct inode *inode = file->f_path.dentry->d_inode; 185602c24a82SJosef Bacik int err = filemap_write_and_wait_range(inode->i_mapping, start, end); 185702c24a82SJosef Bacik if (!err) { 185802c24a82SJosef Bacik mutex_lock(&inode->i_mutex); 185902c24a82SJosef Bacik err = spufs_mfc_flush(file, NULL); 186002c24a82SJosef Bacik mutex_unlock(&inode->i_mutex); 186102c24a82SJosef Bacik } 186202c24a82SJosef Bacik return err; 1863a33a7d73SArnd Bergmann } 1864a33a7d73SArnd Bergmann 1865a33a7d73SArnd Bergmann static int spufs_mfc_fasync(int fd, struct file *file, int on) 1866a33a7d73SArnd Bergmann { 1867a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1868a33a7d73SArnd Bergmann 1869a33a7d73SArnd Bergmann return fasync_helper(fd, file, on, &ctx->mfc_fasync); 1870a33a7d73SArnd Bergmann } 1871a33a7d73SArnd Bergmann 18725dfe4c96SArjan van de Ven static const struct file_operations spufs_mfc_fops = { 1873a33a7d73SArnd Bergmann .open = spufs_mfc_open, 187443c2bbd9SChristoph Hellwig .release = spufs_mfc_release, 1875a33a7d73SArnd Bergmann .read = spufs_mfc_read, 1876a33a7d73SArnd Bergmann .write = spufs_mfc_write, 1877a33a7d73SArnd Bergmann .poll = spufs_mfc_poll, 1878a33a7d73SArnd Bergmann .flush = spufs_mfc_flush, 1879a33a7d73SArnd Bergmann .fsync = spufs_mfc_fsync, 1880a33a7d73SArnd Bergmann .fasync = spufs_mfc_fasync, 18816df10a82SMark Nutter .mmap = spufs_mfc_mmap, 1882fc15351dSArnd Bergmann .llseek = no_llseek, 1883a33a7d73SArnd Bergmann }; 1884a33a7d73SArnd Bergmann 1885197b1a82SChristoph Hellwig static int spufs_npc_set(void *data, u64 val) 188667207b96SArnd Bergmann { 188767207b96SArnd Bergmann struct spu_context *ctx = data; 1888c9101bdbSChristoph Hellwig int ret; 1889c9101bdbSChristoph Hellwig 1890c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1891c9101bdbSChristoph Hellwig if (ret) 1892c9101bdbSChristoph Hellwig return ret; 18938b3d6663SArnd Bergmann ctx->ops->npc_write(ctx, val); 18948b3d6663SArnd Bergmann spu_release(ctx); 1895197b1a82SChristoph Hellwig 1896197b1a82SChristoph Hellwig return 0; 189767207b96SArnd Bergmann } 189867207b96SArnd Bergmann 1899104f0cc2SMichael Ellerman static u64 spufs_npc_get(struct spu_context *ctx) 190078810ff6SMichael Ellerman { 190178810ff6SMichael Ellerman return ctx->ops->npc_read(ctx); 190278810ff6SMichael Ellerman } 1903104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set, 1904104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE); 190567207b96SArnd Bergmann 1906197b1a82SChristoph Hellwig static int spufs_decr_set(void *data, u64 val) 19078b3d6663SArnd Bergmann { 19088b3d6663SArnd Bergmann struct spu_context *ctx = data; 19098b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1910c9101bdbSChristoph Hellwig int ret; 1911c9101bdbSChristoph Hellwig 1912c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1913c9101bdbSChristoph Hellwig if (ret) 1914c9101bdbSChristoph Hellwig return ret; 19158b3d6663SArnd Bergmann lscsa->decr.slot[0] = (u32) val; 191627b1ea09SChristoph Hellwig spu_release_saved(ctx); 1917197b1a82SChristoph Hellwig 1918197b1a82SChristoph Hellwig return 0; 19198b3d6663SArnd Bergmann } 19208b3d6663SArnd Bergmann 1921104f0cc2SMichael Ellerman static u64 spufs_decr_get(struct spu_context *ctx) 19228b3d6663SArnd Bergmann { 19238b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1924bf1ab978SDwayne Grant McConnell return lscsa->decr.slot[0]; 1925bf1ab978SDwayne Grant McConnell } 1926104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set, 1927104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED); 19288b3d6663SArnd Bergmann 1929197b1a82SChristoph Hellwig static int spufs_decr_status_set(void *data, u64 val) 19308b3d6663SArnd Bergmann { 19318b3d6663SArnd Bergmann struct spu_context *ctx = data; 1932c9101bdbSChristoph Hellwig int ret; 1933c9101bdbSChristoph Hellwig 1934c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1935c9101bdbSChristoph Hellwig if (ret) 1936c9101bdbSChristoph Hellwig return ret; 1937d40a01d4SMasato Noguchi if (val) 1938d40a01d4SMasato Noguchi ctx->csa.priv2.mfc_control_RW |= MFC_CNTL_DECREMENTER_RUNNING; 1939d40a01d4SMasato Noguchi else 1940d40a01d4SMasato Noguchi ctx->csa.priv2.mfc_control_RW &= ~MFC_CNTL_DECREMENTER_RUNNING; 194127b1ea09SChristoph Hellwig spu_release_saved(ctx); 1942197b1a82SChristoph Hellwig 1943197b1a82SChristoph Hellwig return 0; 19448b3d6663SArnd Bergmann } 19458b3d6663SArnd Bergmann 1946104f0cc2SMichael Ellerman static u64 spufs_decr_status_get(struct spu_context *ctx) 19478b3d6663SArnd Bergmann { 1948d40a01d4SMasato Noguchi if (ctx->csa.priv2.mfc_control_RW & MFC_CNTL_DECREMENTER_RUNNING) 1949d40a01d4SMasato Noguchi return SPU_DECR_STATUS_RUNNING; 1950d40a01d4SMasato Noguchi else 1951d40a01d4SMasato Noguchi return 0; 1952bf1ab978SDwayne Grant McConnell } 1953104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get, 1954104f0cc2SMichael Ellerman spufs_decr_status_set, "0x%llx\n", 1955104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 19568b3d6663SArnd Bergmann 1957197b1a82SChristoph Hellwig static int spufs_event_mask_set(void *data, u64 val) 19588b3d6663SArnd Bergmann { 19598b3d6663SArnd Bergmann struct spu_context *ctx = data; 19608b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1961c9101bdbSChristoph Hellwig int ret; 1962c9101bdbSChristoph Hellwig 1963c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1964c9101bdbSChristoph Hellwig if (ret) 1965c9101bdbSChristoph Hellwig return ret; 19668b3d6663SArnd Bergmann lscsa->event_mask.slot[0] = (u32) val; 196727b1ea09SChristoph Hellwig spu_release_saved(ctx); 1968197b1a82SChristoph Hellwig 1969197b1a82SChristoph Hellwig return 0; 19708b3d6663SArnd Bergmann } 19718b3d6663SArnd Bergmann 1972104f0cc2SMichael Ellerman static u64 spufs_event_mask_get(struct spu_context *ctx) 19738b3d6663SArnd Bergmann { 19748b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1975bf1ab978SDwayne Grant McConnell return lscsa->event_mask.slot[0]; 1976bf1ab978SDwayne Grant McConnell } 1977bf1ab978SDwayne Grant McConnell 1978104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get, 1979104f0cc2SMichael Ellerman spufs_event_mask_set, "0x%llx\n", 1980104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 19818b3d6663SArnd Bergmann 1982104f0cc2SMichael Ellerman static u64 spufs_event_status_get(struct spu_context *ctx) 1983b9e3bd77SDwayne Grant McConnell { 1984b9e3bd77SDwayne Grant McConnell struct spu_state *state = &ctx->csa; 1985b9e3bd77SDwayne Grant McConnell u64 stat; 1986b9e3bd77SDwayne Grant McConnell stat = state->spu_chnlcnt_RW[0]; 1987b9e3bd77SDwayne Grant McConnell if (stat) 1988bf1ab978SDwayne Grant McConnell return state->spu_chnldata_RW[0]; 1989bf1ab978SDwayne Grant McConnell return 0; 1990bf1ab978SDwayne Grant McConnell } 1991104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_event_status_ops, spufs_event_status_get, 1992104f0cc2SMichael Ellerman NULL, "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED) 1993b9e3bd77SDwayne Grant McConnell 1994197b1a82SChristoph Hellwig static int spufs_srr0_set(void *data, u64 val) 19958b3d6663SArnd Bergmann { 19968b3d6663SArnd Bergmann struct spu_context *ctx = data; 19978b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1998c9101bdbSChristoph Hellwig int ret; 1999c9101bdbSChristoph Hellwig 2000c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2001c9101bdbSChristoph Hellwig if (ret) 2002c9101bdbSChristoph Hellwig return ret; 20038b3d6663SArnd Bergmann lscsa->srr0.slot[0] = (u32) val; 200427b1ea09SChristoph Hellwig spu_release_saved(ctx); 2005197b1a82SChristoph Hellwig 2006197b1a82SChristoph Hellwig return 0; 20078b3d6663SArnd Bergmann } 20088b3d6663SArnd Bergmann 2009104f0cc2SMichael Ellerman static u64 spufs_srr0_get(struct spu_context *ctx) 20108b3d6663SArnd Bergmann { 20118b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 2012104f0cc2SMichael Ellerman return lscsa->srr0.slot[0]; 20138b3d6663SArnd Bergmann } 2014104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set, 2015104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED) 20168b3d6663SArnd Bergmann 2017104f0cc2SMichael Ellerman static u64 spufs_id_get(struct spu_context *ctx) 20187b1a7014Sarnd@arndb.de { 20197b1a7014Sarnd@arndb.de u64 num; 20207b1a7014Sarnd@arndb.de 20217b1a7014Sarnd@arndb.de if (ctx->state == SPU_STATE_RUNNABLE) 20227b1a7014Sarnd@arndb.de num = ctx->spu->number; 20237b1a7014Sarnd@arndb.de else 20247b1a7014Sarnd@arndb.de num = (unsigned int)-1; 20257b1a7014Sarnd@arndb.de 20267b1a7014Sarnd@arndb.de return num; 20277b1a7014Sarnd@arndb.de } 2028104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_id_ops, spufs_id_get, NULL, "0x%llx\n", 2029104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE) 20307b1a7014Sarnd@arndb.de 2031104f0cc2SMichael Ellerman static u64 spufs_object_id_get(struct spu_context *ctx) 2032bf1ab978SDwayne Grant McConnell { 2033bf1ab978SDwayne Grant McConnell /* FIXME: Should there really be no locking here? */ 2034104f0cc2SMichael Ellerman return ctx->object_id; 2035bf1ab978SDwayne Grant McConnell } 2036bf1ab978SDwayne Grant McConnell 2037197b1a82SChristoph Hellwig static int spufs_object_id_set(void *data, u64 id) 203886767277SArnd Bergmann { 203986767277SArnd Bergmann struct spu_context *ctx = data; 204086767277SArnd Bergmann ctx->object_id = id; 2041197b1a82SChristoph Hellwig 2042197b1a82SChristoph Hellwig return 0; 204386767277SArnd Bergmann } 204486767277SArnd Bergmann 2045104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_object_id_ops, spufs_object_id_get, 2046104f0cc2SMichael Ellerman spufs_object_id_set, "0x%llx\n", SPU_ATTR_NOACQUIRE); 204786767277SArnd Bergmann 2048104f0cc2SMichael Ellerman static u64 spufs_lslr_get(struct spu_context *ctx) 2049bf1ab978SDwayne Grant McConnell { 2050bf1ab978SDwayne Grant McConnell return ctx->csa.priv2.spu_lslr_RW; 2051bf1ab978SDwayne Grant McConnell } 2052104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_lslr_ops, spufs_lslr_get, NULL, "0x%llx\n", 2053104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 2054b9e3bd77SDwayne Grant McConnell 2055b9e3bd77SDwayne Grant McConnell static int spufs_info_open(struct inode *inode, struct file *file) 2056b9e3bd77SDwayne Grant McConnell { 2057b9e3bd77SDwayne Grant McConnell struct spufs_inode_info *i = SPUFS_I(inode); 2058b9e3bd77SDwayne Grant McConnell struct spu_context *ctx = i->i_ctx; 2059b9e3bd77SDwayne Grant McConnell file->private_data = ctx; 2060b9e3bd77SDwayne Grant McConnell return 0; 2061b9e3bd77SDwayne Grant McConnell } 2062b9e3bd77SDwayne Grant McConnell 2063cbe709c1SBenjamin Herrenschmidt static int spufs_caps_show(struct seq_file *s, void *private) 2064cbe709c1SBenjamin Herrenschmidt { 2065cbe709c1SBenjamin Herrenschmidt struct spu_context *ctx = s->private; 2066cbe709c1SBenjamin Herrenschmidt 2067cbe709c1SBenjamin Herrenschmidt if (!(ctx->flags & SPU_CREATE_NOSCHED)) 2068cbe709c1SBenjamin Herrenschmidt seq_puts(s, "sched\n"); 2069cbe709c1SBenjamin Herrenschmidt if (!(ctx->flags & SPU_CREATE_ISOLATE)) 2070cbe709c1SBenjamin Herrenschmidt seq_puts(s, "step\n"); 2071cbe709c1SBenjamin Herrenschmidt return 0; 2072cbe709c1SBenjamin Herrenschmidt } 2073cbe709c1SBenjamin Herrenschmidt 2074cbe709c1SBenjamin Herrenschmidt static int spufs_caps_open(struct inode *inode, struct file *file) 2075cbe709c1SBenjamin Herrenschmidt { 2076cbe709c1SBenjamin Herrenschmidt return single_open(file, spufs_caps_show, SPUFS_I(inode)->i_ctx); 2077cbe709c1SBenjamin Herrenschmidt } 2078cbe709c1SBenjamin Herrenschmidt 2079cbe709c1SBenjamin Herrenschmidt static const struct file_operations spufs_caps_fops = { 2080cbe709c1SBenjamin Herrenschmidt .open = spufs_caps_open, 2081cbe709c1SBenjamin Herrenschmidt .read = seq_read, 2082cbe709c1SBenjamin Herrenschmidt .llseek = seq_lseek, 2083cbe709c1SBenjamin Herrenschmidt .release = single_release, 2084cbe709c1SBenjamin Herrenschmidt }; 2085cbe709c1SBenjamin Herrenschmidt 2086bf1ab978SDwayne Grant McConnell static ssize_t __spufs_mbox_info_read(struct spu_context *ctx, 2087bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2088bf1ab978SDwayne Grant McConnell { 2089bf1ab978SDwayne Grant McConnell u32 data; 2090bf1ab978SDwayne Grant McConnell 2091cbea9238SJeremy Kerr /* EOF if there's no entry in the mbox */ 2092cbea9238SJeremy Kerr if (!(ctx->csa.prob.mb_stat_R & 0x0000ff)) 2093cbea9238SJeremy Kerr return 0; 2094cbea9238SJeremy Kerr 2095bf1ab978SDwayne Grant McConnell data = ctx->csa.prob.pu_mb_R; 2096bf1ab978SDwayne Grant McConnell 2097bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, sizeof data); 2098bf1ab978SDwayne Grant McConnell } 2099bf1ab978SDwayne Grant McConnell 210069a2f00cSDwayne Grant McConnell static ssize_t spufs_mbox_info_read(struct file *file, char __user *buf, 210169a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 210269a2f00cSDwayne Grant McConnell { 2103bf1ab978SDwayne Grant McConnell int ret; 210469a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 210569a2f00cSDwayne Grant McConnell 210669a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 210769a2f00cSDwayne Grant McConnell return -EFAULT; 210869a2f00cSDwayne Grant McConnell 2109c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2110c9101bdbSChristoph Hellwig if (ret) 2111c9101bdbSChristoph Hellwig return ret; 211269a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2113bf1ab978SDwayne Grant McConnell ret = __spufs_mbox_info_read(ctx, buf, len, pos); 211469a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 211527b1ea09SChristoph Hellwig spu_release_saved(ctx); 211669a2f00cSDwayne Grant McConnell 2117bf1ab978SDwayne Grant McConnell return ret; 211869a2f00cSDwayne Grant McConnell } 211969a2f00cSDwayne Grant McConnell 21205dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_info_fops = { 212169a2f00cSDwayne Grant McConnell .open = spufs_info_open, 212269a2f00cSDwayne Grant McConnell .read = spufs_mbox_info_read, 212369a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 212469a2f00cSDwayne Grant McConnell }; 212569a2f00cSDwayne Grant McConnell 2126bf1ab978SDwayne Grant McConnell static ssize_t __spufs_ibox_info_read(struct spu_context *ctx, 2127bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2128bf1ab978SDwayne Grant McConnell { 2129bf1ab978SDwayne Grant McConnell u32 data; 2130bf1ab978SDwayne Grant McConnell 2131cbea9238SJeremy Kerr /* EOF if there's no entry in the ibox */ 2132cbea9238SJeremy Kerr if (!(ctx->csa.prob.mb_stat_R & 0xff0000)) 2133cbea9238SJeremy Kerr return 0; 2134cbea9238SJeremy Kerr 2135bf1ab978SDwayne Grant McConnell data = ctx->csa.priv2.puint_mb_R; 2136bf1ab978SDwayne Grant McConnell 2137bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, sizeof data); 2138bf1ab978SDwayne Grant McConnell } 2139bf1ab978SDwayne Grant McConnell 214069a2f00cSDwayne Grant McConnell static ssize_t spufs_ibox_info_read(struct file *file, char __user *buf, 214169a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 214269a2f00cSDwayne Grant McConnell { 214369a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2144bf1ab978SDwayne Grant McConnell int ret; 214569a2f00cSDwayne Grant McConnell 214669a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 214769a2f00cSDwayne Grant McConnell return -EFAULT; 214869a2f00cSDwayne Grant McConnell 2149c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2150c9101bdbSChristoph Hellwig if (ret) 2151c9101bdbSChristoph Hellwig return ret; 215269a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2153bf1ab978SDwayne Grant McConnell ret = __spufs_ibox_info_read(ctx, buf, len, pos); 215469a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 215527b1ea09SChristoph Hellwig spu_release_saved(ctx); 215669a2f00cSDwayne Grant McConnell 2157bf1ab978SDwayne Grant McConnell return ret; 215869a2f00cSDwayne Grant McConnell } 215969a2f00cSDwayne Grant McConnell 21605dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_info_fops = { 216169a2f00cSDwayne Grant McConnell .open = spufs_info_open, 216269a2f00cSDwayne Grant McConnell .read = spufs_ibox_info_read, 216369a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 216469a2f00cSDwayne Grant McConnell }; 216569a2f00cSDwayne Grant McConnell 2166bf1ab978SDwayne Grant McConnell static ssize_t __spufs_wbox_info_read(struct spu_context *ctx, 2167bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2168bf1ab978SDwayne Grant McConnell { 2169bf1ab978SDwayne Grant McConnell int i, cnt; 2170bf1ab978SDwayne Grant McConnell u32 data[4]; 2171bf1ab978SDwayne Grant McConnell u32 wbox_stat; 2172bf1ab978SDwayne Grant McConnell 2173bf1ab978SDwayne Grant McConnell wbox_stat = ctx->csa.prob.mb_stat_R; 2174bf1ab978SDwayne Grant McConnell cnt = 4 - ((wbox_stat & 0x00ff00) >> 8); 2175bf1ab978SDwayne Grant McConnell for (i = 0; i < cnt; i++) { 2176bf1ab978SDwayne Grant McConnell data[i] = ctx->csa.spu_mailbox_data[i]; 2177bf1ab978SDwayne Grant McConnell } 2178bf1ab978SDwayne Grant McConnell 2179bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, 2180bf1ab978SDwayne Grant McConnell cnt * sizeof(u32)); 2181bf1ab978SDwayne Grant McConnell } 2182bf1ab978SDwayne Grant McConnell 218369a2f00cSDwayne Grant McConnell static ssize_t spufs_wbox_info_read(struct file *file, char __user *buf, 218469a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 218569a2f00cSDwayne Grant McConnell { 218669a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2187bf1ab978SDwayne Grant McConnell int ret; 218869a2f00cSDwayne Grant McConnell 218969a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 219069a2f00cSDwayne Grant McConnell return -EFAULT; 219169a2f00cSDwayne Grant McConnell 2192c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2193c9101bdbSChristoph Hellwig if (ret) 2194c9101bdbSChristoph Hellwig return ret; 219569a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2196bf1ab978SDwayne Grant McConnell ret = __spufs_wbox_info_read(ctx, buf, len, pos); 219769a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 219827b1ea09SChristoph Hellwig spu_release_saved(ctx); 219969a2f00cSDwayne Grant McConnell 2200bf1ab978SDwayne Grant McConnell return ret; 220169a2f00cSDwayne Grant McConnell } 220269a2f00cSDwayne Grant McConnell 22035dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_info_fops = { 220469a2f00cSDwayne Grant McConnell .open = spufs_info_open, 220569a2f00cSDwayne Grant McConnell .read = spufs_wbox_info_read, 220669a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 220769a2f00cSDwayne Grant McConnell }; 220869a2f00cSDwayne Grant McConnell 2209bf1ab978SDwayne Grant McConnell static ssize_t __spufs_dma_info_read(struct spu_context *ctx, 2210bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2211b9e3bd77SDwayne Grant McConnell { 2212b9e3bd77SDwayne Grant McConnell struct spu_dma_info info; 2213b9e3bd77SDwayne Grant McConnell struct mfc_cq_sr *qp, *spuqp; 2214b9e3bd77SDwayne Grant McConnell int i; 2215b9e3bd77SDwayne Grant McConnell 2216b9e3bd77SDwayne Grant McConnell info.dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW; 2217b9e3bd77SDwayne Grant McConnell info.dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0]; 2218b9e3bd77SDwayne Grant McConnell info.dma_info_status = ctx->csa.spu_chnldata_RW[24]; 2219b9e3bd77SDwayne Grant McConnell info.dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25]; 2220b9e3bd77SDwayne Grant McConnell info.dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27]; 2221b9e3bd77SDwayne Grant McConnell for (i = 0; i < 16; i++) { 2222b9e3bd77SDwayne Grant McConnell qp = &info.dma_info_command_data[i]; 2223b9e3bd77SDwayne Grant McConnell spuqp = &ctx->csa.priv2.spuq[i]; 2224b9e3bd77SDwayne Grant McConnell 2225b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data0_RW = spuqp->mfc_cq_data0_RW; 2226b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data1_RW = spuqp->mfc_cq_data1_RW; 2227b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data2_RW = spuqp->mfc_cq_data2_RW; 2228b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data3_RW = spuqp->mfc_cq_data3_RW; 2229b9e3bd77SDwayne Grant McConnell } 2230b9e3bd77SDwayne Grant McConnell 2231b9e3bd77SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &info, 2232b9e3bd77SDwayne Grant McConnell sizeof info); 2233b9e3bd77SDwayne Grant McConnell } 2234b9e3bd77SDwayne Grant McConnell 2235bf1ab978SDwayne Grant McConnell static ssize_t spufs_dma_info_read(struct file *file, char __user *buf, 2236bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 2237bf1ab978SDwayne Grant McConnell { 2238bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2239bf1ab978SDwayne Grant McConnell int ret; 2240bf1ab978SDwayne Grant McConnell 2241bf1ab978SDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 2242bf1ab978SDwayne Grant McConnell return -EFAULT; 2243bf1ab978SDwayne Grant McConnell 2244c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2245c9101bdbSChristoph Hellwig if (ret) 2246c9101bdbSChristoph Hellwig return ret; 2247bf1ab978SDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2248bf1ab978SDwayne Grant McConnell ret = __spufs_dma_info_read(ctx, buf, len, pos); 2249bf1ab978SDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 225027b1ea09SChristoph Hellwig spu_release_saved(ctx); 2251bf1ab978SDwayne Grant McConnell 2252bf1ab978SDwayne Grant McConnell return ret; 2253bf1ab978SDwayne Grant McConnell } 2254bf1ab978SDwayne Grant McConnell 22555dfe4c96SArjan van de Ven static const struct file_operations spufs_dma_info_fops = { 2256b9e3bd77SDwayne Grant McConnell .open = spufs_info_open, 2257b9e3bd77SDwayne Grant McConnell .read = spufs_dma_info_read, 2258fc15351dSArnd Bergmann .llseek = no_llseek, 2259b9e3bd77SDwayne Grant McConnell }; 2260b9e3bd77SDwayne Grant McConnell 2261bf1ab978SDwayne Grant McConnell static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx, 2262bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2263b9e3bd77SDwayne Grant McConnell { 2264b9e3bd77SDwayne Grant McConnell struct spu_proxydma_info info; 2265b9e3bd77SDwayne Grant McConnell struct mfc_cq_sr *qp, *puqp; 2266bf1ab978SDwayne Grant McConnell int ret = sizeof info; 2267b9e3bd77SDwayne Grant McConnell int i; 2268b9e3bd77SDwayne Grant McConnell 2269b9e3bd77SDwayne Grant McConnell if (len < ret) 2270b9e3bd77SDwayne Grant McConnell return -EINVAL; 2271b9e3bd77SDwayne Grant McConnell 2272b9e3bd77SDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 2273b9e3bd77SDwayne Grant McConnell return -EFAULT; 2274b9e3bd77SDwayne Grant McConnell 2275b9e3bd77SDwayne Grant McConnell info.proxydma_info_type = ctx->csa.prob.dma_querytype_RW; 2276b9e3bd77SDwayne Grant McConnell info.proxydma_info_mask = ctx->csa.prob.dma_querymask_RW; 2277b9e3bd77SDwayne Grant McConnell info.proxydma_info_status = ctx->csa.prob.dma_tagstatus_R; 2278b9e3bd77SDwayne Grant McConnell for (i = 0; i < 8; i++) { 2279b9e3bd77SDwayne Grant McConnell qp = &info.proxydma_info_command_data[i]; 2280b9e3bd77SDwayne Grant McConnell puqp = &ctx->csa.priv2.puq[i]; 2281b9e3bd77SDwayne Grant McConnell 2282b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data0_RW = puqp->mfc_cq_data0_RW; 2283b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data1_RW = puqp->mfc_cq_data1_RW; 2284b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data2_RW = puqp->mfc_cq_data2_RW; 2285b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data3_RW = puqp->mfc_cq_data3_RW; 2286b9e3bd77SDwayne Grant McConnell } 2287bf1ab978SDwayne Grant McConnell 2288bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &info, 2289bf1ab978SDwayne Grant McConnell sizeof info); 2290bf1ab978SDwayne Grant McConnell } 2291bf1ab978SDwayne Grant McConnell 2292bf1ab978SDwayne Grant McConnell static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf, 2293bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 2294bf1ab978SDwayne Grant McConnell { 2295bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2296bf1ab978SDwayne Grant McConnell int ret; 2297bf1ab978SDwayne Grant McConnell 2298c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2299c9101bdbSChristoph Hellwig if (ret) 2300c9101bdbSChristoph Hellwig return ret; 2301bf1ab978SDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2302bf1ab978SDwayne Grant McConnell ret = __spufs_proxydma_info_read(ctx, buf, len, pos); 2303b9e3bd77SDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 230427b1ea09SChristoph Hellwig spu_release_saved(ctx); 2305b9e3bd77SDwayne Grant McConnell 2306b9e3bd77SDwayne Grant McConnell return ret; 2307b9e3bd77SDwayne Grant McConnell } 2308b9e3bd77SDwayne Grant McConnell 23095dfe4c96SArjan van de Ven static const struct file_operations spufs_proxydma_info_fops = { 2310b9e3bd77SDwayne Grant McConnell .open = spufs_info_open, 2311b9e3bd77SDwayne Grant McConnell .read = spufs_proxydma_info_read, 2312fc15351dSArnd Bergmann .llseek = no_llseek, 2313b9e3bd77SDwayne Grant McConnell }; 2314b9e3bd77SDwayne Grant McConnell 2315476273adSChristoph Hellwig static int spufs_show_tid(struct seq_file *s, void *private) 2316476273adSChristoph Hellwig { 2317476273adSChristoph Hellwig struct spu_context *ctx = s->private; 2318476273adSChristoph Hellwig 2319476273adSChristoph Hellwig seq_printf(s, "%d\n", ctx->tid); 2320476273adSChristoph Hellwig return 0; 2321476273adSChristoph Hellwig } 2322476273adSChristoph Hellwig 2323476273adSChristoph Hellwig static int spufs_tid_open(struct inode *inode, struct file *file) 2324476273adSChristoph Hellwig { 2325476273adSChristoph Hellwig return single_open(file, spufs_show_tid, SPUFS_I(inode)->i_ctx); 2326476273adSChristoph Hellwig } 2327476273adSChristoph Hellwig 2328476273adSChristoph Hellwig static const struct file_operations spufs_tid_fops = { 2329476273adSChristoph Hellwig .open = spufs_tid_open, 2330476273adSChristoph Hellwig .read = seq_read, 2331476273adSChristoph Hellwig .llseek = seq_lseek, 2332476273adSChristoph Hellwig .release = single_release, 2333476273adSChristoph Hellwig }; 2334476273adSChristoph Hellwig 2335e9f8a0b6SChristoph Hellwig static const char *ctx_state_names[] = { 2336e9f8a0b6SChristoph Hellwig "user", "system", "iowait", "loaded" 2337e9f8a0b6SChristoph Hellwig }; 2338e9f8a0b6SChristoph Hellwig 2339e9f8a0b6SChristoph Hellwig static unsigned long long spufs_acct_time(struct spu_context *ctx, 234027ec41d3SAndre Detsch enum spu_utilization_state state) 2341e9f8a0b6SChristoph Hellwig { 234227ec41d3SAndre Detsch struct timespec ts; 234327ec41d3SAndre Detsch unsigned long long time = ctx->stats.times[state]; 2344e9f8a0b6SChristoph Hellwig 234527ec41d3SAndre Detsch /* 234627ec41d3SAndre Detsch * In general, utilization statistics are updated by the controlling 234727ec41d3SAndre Detsch * thread as the spu context moves through various well defined 234827ec41d3SAndre Detsch * state transitions, but if the context is lazily loaded its 234927ec41d3SAndre Detsch * utilization statistics are not updated as the controlling thread 235027ec41d3SAndre Detsch * is not tightly coupled with the execution of the spu context. We 235127ec41d3SAndre Detsch * calculate and apply the time delta from the last recorded state 235227ec41d3SAndre Detsch * of the spu context. 235327ec41d3SAndre Detsch */ 235427ec41d3SAndre Detsch if (ctx->spu && ctx->stats.util_state == state) { 235527ec41d3SAndre Detsch ktime_get_ts(&ts); 235627ec41d3SAndre Detsch time += timespec_to_ns(&ts) - ctx->stats.tstamp; 235727ec41d3SAndre Detsch } 2358e9f8a0b6SChristoph Hellwig 235927ec41d3SAndre Detsch return time / NSEC_PER_MSEC; 2360e9f8a0b6SChristoph Hellwig } 2361e9f8a0b6SChristoph Hellwig 2362e9f8a0b6SChristoph Hellwig static unsigned long long spufs_slb_flts(struct spu_context *ctx) 2363e9f8a0b6SChristoph Hellwig { 2364e9f8a0b6SChristoph Hellwig unsigned long long slb_flts = ctx->stats.slb_flt; 2365e9f8a0b6SChristoph Hellwig 2366e9f8a0b6SChristoph Hellwig if (ctx->state == SPU_STATE_RUNNABLE) { 2367e9f8a0b6SChristoph Hellwig slb_flts += (ctx->spu->stats.slb_flt - 2368e9f8a0b6SChristoph Hellwig ctx->stats.slb_flt_base); 2369e9f8a0b6SChristoph Hellwig } 2370e9f8a0b6SChristoph Hellwig 2371e9f8a0b6SChristoph Hellwig return slb_flts; 2372e9f8a0b6SChristoph Hellwig } 2373e9f8a0b6SChristoph Hellwig 2374e9f8a0b6SChristoph Hellwig static unsigned long long spufs_class2_intrs(struct spu_context *ctx) 2375e9f8a0b6SChristoph Hellwig { 2376e9f8a0b6SChristoph Hellwig unsigned long long class2_intrs = ctx->stats.class2_intr; 2377e9f8a0b6SChristoph Hellwig 2378e9f8a0b6SChristoph Hellwig if (ctx->state == SPU_STATE_RUNNABLE) { 2379e9f8a0b6SChristoph Hellwig class2_intrs += (ctx->spu->stats.class2_intr - 2380e9f8a0b6SChristoph Hellwig ctx->stats.class2_intr_base); 2381e9f8a0b6SChristoph Hellwig } 2382e9f8a0b6SChristoph Hellwig 2383e9f8a0b6SChristoph Hellwig return class2_intrs; 2384e9f8a0b6SChristoph Hellwig } 2385e9f8a0b6SChristoph Hellwig 2386e9f8a0b6SChristoph Hellwig 2387e9f8a0b6SChristoph Hellwig static int spufs_show_stat(struct seq_file *s, void *private) 2388e9f8a0b6SChristoph Hellwig { 2389e9f8a0b6SChristoph Hellwig struct spu_context *ctx = s->private; 2390c9101bdbSChristoph Hellwig int ret; 2391e9f8a0b6SChristoph Hellwig 2392c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 2393c9101bdbSChristoph Hellwig if (ret) 2394c9101bdbSChristoph Hellwig return ret; 2395c9101bdbSChristoph Hellwig 2396e9f8a0b6SChristoph Hellwig seq_printf(s, "%s %llu %llu %llu %llu " 2397e9f8a0b6SChristoph Hellwig "%llu %llu %llu %llu %llu %llu %llu %llu\n", 239827ec41d3SAndre Detsch ctx_state_names[ctx->stats.util_state], 239927ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_USER), 240027ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_SYSTEM), 240127ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_IOWAIT), 240227ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_IDLE_LOADED), 2403e9f8a0b6SChristoph Hellwig ctx->stats.vol_ctx_switch, 2404e9f8a0b6SChristoph Hellwig ctx->stats.invol_ctx_switch, 2405e9f8a0b6SChristoph Hellwig spufs_slb_flts(ctx), 2406e9f8a0b6SChristoph Hellwig ctx->stats.hash_flt, 2407e9f8a0b6SChristoph Hellwig ctx->stats.min_flt, 2408e9f8a0b6SChristoph Hellwig ctx->stats.maj_flt, 2409e9f8a0b6SChristoph Hellwig spufs_class2_intrs(ctx), 2410e9f8a0b6SChristoph Hellwig ctx->stats.libassist); 2411e9f8a0b6SChristoph Hellwig spu_release(ctx); 2412e9f8a0b6SChristoph Hellwig return 0; 2413e9f8a0b6SChristoph Hellwig } 2414e9f8a0b6SChristoph Hellwig 2415e9f8a0b6SChristoph Hellwig static int spufs_stat_open(struct inode *inode, struct file *file) 2416e9f8a0b6SChristoph Hellwig { 2417e9f8a0b6SChristoph Hellwig return single_open(file, spufs_show_stat, SPUFS_I(inode)->i_ctx); 2418e9f8a0b6SChristoph Hellwig } 2419e9f8a0b6SChristoph Hellwig 2420e9f8a0b6SChristoph Hellwig static const struct file_operations spufs_stat_fops = { 2421e9f8a0b6SChristoph Hellwig .open = spufs_stat_open, 2422e9f8a0b6SChristoph Hellwig .read = seq_read, 2423e9f8a0b6SChristoph Hellwig .llseek = seq_lseek, 2424e9f8a0b6SChristoph Hellwig .release = single_release, 2425e9f8a0b6SChristoph Hellwig }; 2426e9f8a0b6SChristoph Hellwig 24275158e9b5SChristoph Hellwig static inline int spufs_switch_log_used(struct spu_context *ctx) 24285158e9b5SChristoph Hellwig { 24295158e9b5SChristoph Hellwig return (ctx->switch_log->head - ctx->switch_log->tail) % 24305158e9b5SChristoph Hellwig SWITCH_LOG_BUFSIZE; 24315158e9b5SChristoph Hellwig } 24325158e9b5SChristoph Hellwig 24335158e9b5SChristoph Hellwig static inline int spufs_switch_log_avail(struct spu_context *ctx) 24345158e9b5SChristoph Hellwig { 24355158e9b5SChristoph Hellwig return SWITCH_LOG_BUFSIZE - spufs_switch_log_used(ctx); 24365158e9b5SChristoph Hellwig } 24375158e9b5SChristoph Hellwig 24385158e9b5SChristoph Hellwig static int spufs_switch_log_open(struct inode *inode, struct file *file) 24395158e9b5SChristoph Hellwig { 24405158e9b5SChristoph Hellwig struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 2441f5ed0eb6SJeremy Kerr int rc; 24425158e9b5SChristoph Hellwig 2443f5ed0eb6SJeremy Kerr rc = spu_acquire(ctx); 2444f5ed0eb6SJeremy Kerr if (rc) 2445f5ed0eb6SJeremy Kerr return rc; 2446f5ed0eb6SJeremy Kerr 24475158e9b5SChristoph Hellwig if (ctx->switch_log) { 2448f5ed0eb6SJeremy Kerr rc = -EBUSY; 2449f5ed0eb6SJeremy Kerr goto out; 2450f5ed0eb6SJeremy Kerr } 2451f5ed0eb6SJeremy Kerr 2452837ef884SJeremy Kerr ctx->switch_log = kmalloc(sizeof(struct switch_log) + 24535158e9b5SChristoph Hellwig SWITCH_LOG_BUFSIZE * sizeof(struct switch_log_entry), 24545158e9b5SChristoph Hellwig GFP_KERNEL); 2455f5ed0eb6SJeremy Kerr 2456f5ed0eb6SJeremy Kerr if (!ctx->switch_log) { 2457f5ed0eb6SJeremy Kerr rc = -ENOMEM; 24585158e9b5SChristoph Hellwig goto out; 24595158e9b5SChristoph Hellwig } 2460f5ed0eb6SJeremy Kerr 2461837ef884SJeremy Kerr ctx->switch_log->head = ctx->switch_log->tail = 0; 2462f5ed0eb6SJeremy Kerr init_waitqueue_head(&ctx->switch_log->wait); 2463f5ed0eb6SJeremy Kerr rc = 0; 2464f5ed0eb6SJeremy Kerr 2465f5ed0eb6SJeremy Kerr out: 2466f5ed0eb6SJeremy Kerr spu_release(ctx); 2467f5ed0eb6SJeremy Kerr return rc; 2468f5ed0eb6SJeremy Kerr } 2469f5ed0eb6SJeremy Kerr 2470f5ed0eb6SJeremy Kerr static int spufs_switch_log_release(struct inode *inode, struct file *file) 2471f5ed0eb6SJeremy Kerr { 2472f5ed0eb6SJeremy Kerr struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 2473f5ed0eb6SJeremy Kerr int rc; 2474f5ed0eb6SJeremy Kerr 2475f5ed0eb6SJeremy Kerr rc = spu_acquire(ctx); 2476f5ed0eb6SJeremy Kerr if (rc) 2477f5ed0eb6SJeremy Kerr return rc; 2478f5ed0eb6SJeremy Kerr 2479f5ed0eb6SJeremy Kerr kfree(ctx->switch_log); 2480f5ed0eb6SJeremy Kerr ctx->switch_log = NULL; 2481f5ed0eb6SJeremy Kerr spu_release(ctx); 24825158e9b5SChristoph Hellwig 24835158e9b5SChristoph Hellwig return 0; 24845158e9b5SChristoph Hellwig } 24855158e9b5SChristoph Hellwig 24865158e9b5SChristoph Hellwig static int switch_log_sprint(struct spu_context *ctx, char *tbuf, int n) 24875158e9b5SChristoph Hellwig { 24885158e9b5SChristoph Hellwig struct switch_log_entry *p; 24895158e9b5SChristoph Hellwig 24905158e9b5SChristoph Hellwig p = ctx->switch_log->log + ctx->switch_log->tail % SWITCH_LOG_BUFSIZE; 24915158e9b5SChristoph Hellwig 24925158e9b5SChristoph Hellwig return snprintf(tbuf, n, "%u.%09u %d %u %u %llu\n", 24935158e9b5SChristoph Hellwig (unsigned int) p->tstamp.tv_sec, 24945158e9b5SChristoph Hellwig (unsigned int) p->tstamp.tv_nsec, 24955158e9b5SChristoph Hellwig p->spu_id, 24965158e9b5SChristoph Hellwig (unsigned int) p->type, 24975158e9b5SChristoph Hellwig (unsigned int) p->val, 24985158e9b5SChristoph Hellwig (unsigned long long) p->timebase); 24995158e9b5SChristoph Hellwig } 25005158e9b5SChristoph Hellwig 25015158e9b5SChristoph Hellwig static ssize_t spufs_switch_log_read(struct file *file, char __user *buf, 25025158e9b5SChristoph Hellwig size_t len, loff_t *ppos) 25035158e9b5SChristoph Hellwig { 25045158e9b5SChristoph Hellwig struct inode *inode = file->f_path.dentry->d_inode; 25055158e9b5SChristoph Hellwig struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 25065158e9b5SChristoph Hellwig int error = 0, cnt = 0; 25075158e9b5SChristoph Hellwig 250817e37675Sroel kluin if (!buf) 25095158e9b5SChristoph Hellwig return -EINVAL; 25105158e9b5SChristoph Hellwig 2511f5ed0eb6SJeremy Kerr error = spu_acquire(ctx); 2512f5ed0eb6SJeremy Kerr if (error) 2513f5ed0eb6SJeremy Kerr return error; 2514f5ed0eb6SJeremy Kerr 25155158e9b5SChristoph Hellwig while (cnt < len) { 25165158e9b5SChristoph Hellwig char tbuf[128]; 25175158e9b5SChristoph Hellwig int width; 25185158e9b5SChristoph Hellwig 2519f5ed0eb6SJeremy Kerr if (spufs_switch_log_used(ctx) == 0) { 252014f693eeSJeremy Kerr if (cnt > 0) { 252114f693eeSJeremy Kerr /* If there's data ready to go, we can 252214f693eeSJeremy Kerr * just return straight away */ 252314f693eeSJeremy Kerr break; 252414f693eeSJeremy Kerr 252514f693eeSJeremy Kerr } else if (file->f_flags & O_NONBLOCK) { 2526f5ed0eb6SJeremy Kerr error = -EAGAIN; 25275158e9b5SChristoph Hellwig break; 252814f693eeSJeremy Kerr 2529f5ed0eb6SJeremy Kerr } else { 253014f693eeSJeremy Kerr /* spufs_wait will drop the mutex and 253114f693eeSJeremy Kerr * re-acquire, but since we're in read(), the 253214f693eeSJeremy Kerr * file cannot be _released (and so 253314f693eeSJeremy Kerr * ctx->switch_log is stable). 2534f5ed0eb6SJeremy Kerr */ 2535f5ed0eb6SJeremy Kerr error = spufs_wait(ctx->switch_log->wait, 2536f5ed0eb6SJeremy Kerr spufs_switch_log_used(ctx) > 0); 25375158e9b5SChristoph Hellwig 2538f5ed0eb6SJeremy Kerr /* On error, spufs_wait returns without the 2539f5ed0eb6SJeremy Kerr * state mutex held */ 2540f5ed0eb6SJeremy Kerr if (error) 2541f5ed0eb6SJeremy Kerr return error; 25425158e9b5SChristoph Hellwig 254314f693eeSJeremy Kerr /* We may have had entries read from underneath 254414f693eeSJeremy Kerr * us while we dropped the mutex in spufs_wait, 254514f693eeSJeremy Kerr * so re-check */ 254614f693eeSJeremy Kerr if (spufs_switch_log_used(ctx) == 0) 2547f5ed0eb6SJeremy Kerr continue; 254814f693eeSJeremy Kerr } 254914f693eeSJeremy Kerr } 2550f5ed0eb6SJeremy Kerr 25515158e9b5SChristoph Hellwig width = switch_log_sprint(ctx, tbuf, sizeof(tbuf)); 2552f5ed0eb6SJeremy Kerr if (width < len) 25535158e9b5SChristoph Hellwig ctx->switch_log->tail = 25545158e9b5SChristoph Hellwig (ctx->switch_log->tail + 1) % 25555158e9b5SChristoph Hellwig SWITCH_LOG_BUFSIZE; 2556f5ed0eb6SJeremy Kerr else 2557f5ed0eb6SJeremy Kerr /* If the record is greater than space available return 2558f5ed0eb6SJeremy Kerr * partial buffer (so far) */ 25595158e9b5SChristoph Hellwig break; 25605158e9b5SChristoph Hellwig 25615158e9b5SChristoph Hellwig error = copy_to_user(buf + cnt, tbuf, width); 25625158e9b5SChristoph Hellwig if (error) 25635158e9b5SChristoph Hellwig break; 25645158e9b5SChristoph Hellwig cnt += width; 25655158e9b5SChristoph Hellwig } 25665158e9b5SChristoph Hellwig 2567f5ed0eb6SJeremy Kerr spu_release(ctx); 2568f5ed0eb6SJeremy Kerr 25695158e9b5SChristoph Hellwig return cnt == 0 ? error : cnt; 25705158e9b5SChristoph Hellwig } 25715158e9b5SChristoph Hellwig 25725158e9b5SChristoph Hellwig static unsigned int spufs_switch_log_poll(struct file *file, poll_table *wait) 25735158e9b5SChristoph Hellwig { 25745158e9b5SChristoph Hellwig struct inode *inode = file->f_path.dentry->d_inode; 25755158e9b5SChristoph Hellwig struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 25765158e9b5SChristoph Hellwig unsigned int mask = 0; 2577f5ed0eb6SJeremy Kerr int rc; 25785158e9b5SChristoph Hellwig 25795158e9b5SChristoph Hellwig poll_wait(file, &ctx->switch_log->wait, wait); 25805158e9b5SChristoph Hellwig 2581f5ed0eb6SJeremy Kerr rc = spu_acquire(ctx); 2582f5ed0eb6SJeremy Kerr if (rc) 2583f5ed0eb6SJeremy Kerr return rc; 2584f5ed0eb6SJeremy Kerr 25855158e9b5SChristoph Hellwig if (spufs_switch_log_used(ctx) > 0) 25865158e9b5SChristoph Hellwig mask |= POLLIN; 25875158e9b5SChristoph Hellwig 2588f5ed0eb6SJeremy Kerr spu_release(ctx); 2589f5ed0eb6SJeremy Kerr 25905158e9b5SChristoph Hellwig return mask; 25915158e9b5SChristoph Hellwig } 25925158e9b5SChristoph Hellwig 25935158e9b5SChristoph Hellwig static const struct file_operations spufs_switch_log_fops = { 25945158e9b5SChristoph Hellwig .owner = THIS_MODULE, 25955158e9b5SChristoph Hellwig .open = spufs_switch_log_open, 25965158e9b5SChristoph Hellwig .read = spufs_switch_log_read, 25975158e9b5SChristoph Hellwig .poll = spufs_switch_log_poll, 2598f5ed0eb6SJeremy Kerr .release = spufs_switch_log_release, 2599fc15351dSArnd Bergmann .llseek = no_llseek, 26005158e9b5SChristoph Hellwig }; 26015158e9b5SChristoph Hellwig 2602f5ed0eb6SJeremy Kerr /** 2603f5ed0eb6SJeremy Kerr * Log a context switch event to a switch log reader. 2604f5ed0eb6SJeremy Kerr * 2605f5ed0eb6SJeremy Kerr * Must be called with ctx->state_mutex held. 2606f5ed0eb6SJeremy Kerr */ 26075158e9b5SChristoph Hellwig void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx, 26085158e9b5SChristoph Hellwig u32 type, u32 val) 26095158e9b5SChristoph Hellwig { 26105158e9b5SChristoph Hellwig if (!ctx->switch_log) 26115158e9b5SChristoph Hellwig return; 26125158e9b5SChristoph Hellwig 26135158e9b5SChristoph Hellwig if (spufs_switch_log_avail(ctx) > 1) { 26145158e9b5SChristoph Hellwig struct switch_log_entry *p; 26155158e9b5SChristoph Hellwig 26165158e9b5SChristoph Hellwig p = ctx->switch_log->log + ctx->switch_log->head; 26175158e9b5SChristoph Hellwig ktime_get_ts(&p->tstamp); 26185158e9b5SChristoph Hellwig p->timebase = get_tb(); 26195158e9b5SChristoph Hellwig p->spu_id = spu ? spu->number : -1; 26205158e9b5SChristoph Hellwig p->type = type; 26215158e9b5SChristoph Hellwig p->val = val; 26225158e9b5SChristoph Hellwig 26235158e9b5SChristoph Hellwig ctx->switch_log->head = 26245158e9b5SChristoph Hellwig (ctx->switch_log->head + 1) % SWITCH_LOG_BUFSIZE; 26255158e9b5SChristoph Hellwig } 26265158e9b5SChristoph Hellwig 26275158e9b5SChristoph Hellwig wake_up(&ctx->switch_log->wait); 26285158e9b5SChristoph Hellwig } 2629e9f8a0b6SChristoph Hellwig 263046deed69SLuke Browning static int spufs_show_ctx(struct seq_file *s, void *private) 263146deed69SLuke Browning { 263246deed69SLuke Browning struct spu_context *ctx = s->private; 263346deed69SLuke Browning u64 mfc_control_RW; 263446deed69SLuke Browning 263546deed69SLuke Browning mutex_lock(&ctx->state_mutex); 263646deed69SLuke Browning if (ctx->spu) { 263746deed69SLuke Browning struct spu *spu = ctx->spu; 263846deed69SLuke Browning struct spu_priv2 __iomem *priv2 = spu->priv2; 263946deed69SLuke Browning 264046deed69SLuke Browning spin_lock_irq(&spu->register_lock); 264146deed69SLuke Browning mfc_control_RW = in_be64(&priv2->mfc_control_RW); 264246deed69SLuke Browning spin_unlock_irq(&spu->register_lock); 264346deed69SLuke Browning } else { 264446deed69SLuke Browning struct spu_state *csa = &ctx->csa; 264546deed69SLuke Browning 264646deed69SLuke Browning mfc_control_RW = csa->priv2.mfc_control_RW; 264746deed69SLuke Browning } 264846deed69SLuke Browning 264946deed69SLuke Browning seq_printf(s, "%c flgs(%lx) sflgs(%lx) pri(%d) ts(%d) spu(%02d)" 26509477e455SStephen Rothwell " %c %llx %llx %llx %llx %x %x\n", 265146deed69SLuke Browning ctx->state == SPU_STATE_SAVED ? 'S' : 'R', 265246deed69SLuke Browning ctx->flags, 265346deed69SLuke Browning ctx->sched_flags, 265446deed69SLuke Browning ctx->prio, 265546deed69SLuke Browning ctx->time_slice, 265646deed69SLuke Browning ctx->spu ? ctx->spu->number : -1, 265746deed69SLuke Browning !list_empty(&ctx->rq) ? 'q' : ' ', 265846deed69SLuke Browning ctx->csa.class_0_pending, 265946deed69SLuke Browning ctx->csa.class_0_dar, 266046deed69SLuke Browning ctx->csa.class_1_dsisr, 266146deed69SLuke Browning mfc_control_RW, 266246deed69SLuke Browning ctx->ops->runcntl_read(ctx), 266346deed69SLuke Browning ctx->ops->status_read(ctx)); 266446deed69SLuke Browning 266546deed69SLuke Browning mutex_unlock(&ctx->state_mutex); 266646deed69SLuke Browning 266746deed69SLuke Browning return 0; 266846deed69SLuke Browning } 266946deed69SLuke Browning 267046deed69SLuke Browning static int spufs_ctx_open(struct inode *inode, struct file *file) 267146deed69SLuke Browning { 267246deed69SLuke Browning return single_open(file, spufs_show_ctx, SPUFS_I(inode)->i_ctx); 267346deed69SLuke Browning } 267446deed69SLuke Browning 267546deed69SLuke Browning static const struct file_operations spufs_ctx_fops = { 267646deed69SLuke Browning .open = spufs_ctx_open, 267746deed69SLuke Browning .read = seq_read, 267846deed69SLuke Browning .llseek = seq_lseek, 267946deed69SLuke Browning .release = single_release, 268046deed69SLuke Browning }; 268146deed69SLuke Browning 268274254647SJeremy Kerr const struct spufs_tree_descr spufs_dir_contents[] = { 2683cbe709c1SBenjamin Herrenschmidt { "capabilities", &spufs_caps_fops, 0444, }, 26846f7dde81SJeremy Kerr { "mem", &spufs_mem_fops, 0666, LS_SIZE, }, 26856f7dde81SJeremy Kerr { "regs", &spufs_regs_fops, 0666, sizeof(struct spu_reg128[128]), }, 268667207b96SArnd Bergmann { "mbox", &spufs_mbox_fops, 0444, }, 268767207b96SArnd Bergmann { "ibox", &spufs_ibox_fops, 0444, }, 268867207b96SArnd Bergmann { "wbox", &spufs_wbox_fops, 0222, }, 26896f7dde81SJeremy Kerr { "mbox_stat", &spufs_mbox_stat_fops, 0444, sizeof(u32), }, 26906f7dde81SJeremy Kerr { "ibox_stat", &spufs_ibox_stat_fops, 0444, sizeof(u32), }, 26916f7dde81SJeremy Kerr { "wbox_stat", &spufs_wbox_stat_fops, 0444, sizeof(u32), }, 2692603c4612SJeremy Kerr { "signal1", &spufs_signal1_fops, 0666, }, 2693603c4612SJeremy Kerr { "signal2", &spufs_signal2_fops, 0666, }, 269467207b96SArnd Bergmann { "signal1_type", &spufs_signal1_type, 0666, }, 269567207b96SArnd Bergmann { "signal2_type", &spufs_signal2_type, 0666, }, 26966df10a82SMark Nutter { "cntl", &spufs_cntl_fops, 0666, }, 26976f7dde81SJeremy Kerr { "fpcr", &spufs_fpcr_fops, 0666, sizeof(struct spu_reg128), }, 2698b9e3bd77SDwayne Grant McConnell { "lslr", &spufs_lslr_ops, 0444, }, 2699b9e3bd77SDwayne Grant McConnell { "mfc", &spufs_mfc_fops, 0666, }, 2700b9e3bd77SDwayne Grant McConnell { "mss", &spufs_mss_fops, 0666, }, 2701b9e3bd77SDwayne Grant McConnell { "npc", &spufs_npc_ops, 0666, }, 2702b9e3bd77SDwayne Grant McConnell { "srr0", &spufs_srr0_ops, 0666, }, 27038b3d6663SArnd Bergmann { "decr", &spufs_decr_ops, 0666, }, 27048b3d6663SArnd Bergmann { "decr_status", &spufs_decr_status_ops, 0666, }, 27058b3d6663SArnd Bergmann { "event_mask", &spufs_event_mask_ops, 0666, }, 2706b9e3bd77SDwayne Grant McConnell { "event_status", &spufs_event_status_ops, 0444, }, 27076f7dde81SJeremy Kerr { "psmap", &spufs_psmap_fops, 0666, SPUFS_PS_MAP_SIZE, }, 270886767277SArnd Bergmann { "phys-id", &spufs_id_ops, 0666, }, 270986767277SArnd Bergmann { "object-id", &spufs_object_id_ops, 0666, }, 27106f7dde81SJeremy Kerr { "mbox_info", &spufs_mbox_info_fops, 0444, sizeof(u32), }, 27116f7dde81SJeremy Kerr { "ibox_info", &spufs_ibox_info_fops, 0444, sizeof(u32), }, 27126f7dde81SJeremy Kerr { "wbox_info", &spufs_wbox_info_fops, 0444, sizeof(u32), }, 27136f7dde81SJeremy Kerr { "dma_info", &spufs_dma_info_fops, 0444, 27146f7dde81SJeremy Kerr sizeof(struct spu_dma_info), }, 27156f7dde81SJeremy Kerr { "proxydma_info", &spufs_proxydma_info_fops, 0444, 27166f7dde81SJeremy Kerr sizeof(struct spu_proxydma_info)}, 2717476273adSChristoph Hellwig { "tid", &spufs_tid_fops, 0444, }, 2718e9f8a0b6SChristoph Hellwig { "stat", &spufs_stat_fops, 0444, }, 27195158e9b5SChristoph Hellwig { "switch_log", &spufs_switch_log_fops, 0444 }, 272067207b96SArnd Bergmann {}, 272167207b96SArnd Bergmann }; 27225737edd1SMark Nutter 272374254647SJeremy Kerr const struct spufs_tree_descr spufs_dir_nosched_contents[] = { 2724cbe709c1SBenjamin Herrenschmidt { "capabilities", &spufs_caps_fops, 0444, }, 27256f7dde81SJeremy Kerr { "mem", &spufs_mem_fops, 0666, LS_SIZE, }, 27265737edd1SMark Nutter { "mbox", &spufs_mbox_fops, 0444, }, 27275737edd1SMark Nutter { "ibox", &spufs_ibox_fops, 0444, }, 27285737edd1SMark Nutter { "wbox", &spufs_wbox_fops, 0222, }, 27296f7dde81SJeremy Kerr { "mbox_stat", &spufs_mbox_stat_fops, 0444, sizeof(u32), }, 27306f7dde81SJeremy Kerr { "ibox_stat", &spufs_ibox_stat_fops, 0444, sizeof(u32), }, 27316f7dde81SJeremy Kerr { "wbox_stat", &spufs_wbox_stat_fops, 0444, sizeof(u32), }, 2732d054b36fSJeremy Kerr { "signal1", &spufs_signal1_nosched_fops, 0222, }, 2733d054b36fSJeremy Kerr { "signal2", &spufs_signal2_nosched_fops, 0222, }, 27345737edd1SMark Nutter { "signal1_type", &spufs_signal1_type, 0666, }, 27355737edd1SMark Nutter { "signal2_type", &spufs_signal2_type, 0666, }, 27365737edd1SMark Nutter { "mss", &spufs_mss_fops, 0666, }, 27375737edd1SMark Nutter { "mfc", &spufs_mfc_fops, 0666, }, 27385737edd1SMark Nutter { "cntl", &spufs_cntl_fops, 0666, }, 27395737edd1SMark Nutter { "npc", &spufs_npc_ops, 0666, }, 27406f7dde81SJeremy Kerr { "psmap", &spufs_psmap_fops, 0666, SPUFS_PS_MAP_SIZE, }, 27415737edd1SMark Nutter { "phys-id", &spufs_id_ops, 0666, }, 27425737edd1SMark Nutter { "object-id", &spufs_object_id_ops, 0666, }, 2743476273adSChristoph Hellwig { "tid", &spufs_tid_fops, 0444, }, 2744e9f8a0b6SChristoph Hellwig { "stat", &spufs_stat_fops, 0444, }, 27452c3e4787SJeremy Kerr {}, 27462c3e4787SJeremy Kerr }; 27472c3e4787SJeremy Kerr 274874254647SJeremy Kerr const struct spufs_tree_descr spufs_dir_debug_contents[] = { 274946deed69SLuke Browning { ".ctx", &spufs_ctx_fops, 0444, }, 27505737edd1SMark Nutter {}, 27515737edd1SMark Nutter }; 2752bf1ab978SDwayne Grant McConnell 275374254647SJeremy Kerr const struct spufs_coredump_reader spufs_coredump_read[] = { 27544fca9c42SMichael Ellerman { "regs", __spufs_regs_read, NULL, sizeof(struct spu_reg128[128])}, 27554fca9c42SMichael Ellerman { "fpcr", __spufs_fpcr_read, NULL, sizeof(struct spu_reg128) }, 2756104f0cc2SMichael Ellerman { "lslr", NULL, spufs_lslr_get, 19 }, 2757104f0cc2SMichael Ellerman { "decr", NULL, spufs_decr_get, 19 }, 2758104f0cc2SMichael Ellerman { "decr_status", NULL, spufs_decr_status_get, 19 }, 27594fca9c42SMichael Ellerman { "mem", __spufs_mem_read, NULL, LS_SIZE, }, 27604fca9c42SMichael Ellerman { "signal1", __spufs_signal1_read, NULL, sizeof(u32) }, 2761104f0cc2SMichael Ellerman { "signal1_type", NULL, spufs_signal1_type_get, 19 }, 27624fca9c42SMichael Ellerman { "signal2", __spufs_signal2_read, NULL, sizeof(u32) }, 2763104f0cc2SMichael Ellerman { "signal2_type", NULL, spufs_signal2_type_get, 19 }, 2764104f0cc2SMichael Ellerman { "event_mask", NULL, spufs_event_mask_get, 19 }, 2765104f0cc2SMichael Ellerman { "event_status", NULL, spufs_event_status_get, 19 }, 27664fca9c42SMichael Ellerman { "mbox_info", __spufs_mbox_info_read, NULL, sizeof(u32) }, 27674fca9c42SMichael Ellerman { "ibox_info", __spufs_ibox_info_read, NULL, sizeof(u32) }, 27684fca9c42SMichael Ellerman { "wbox_info", __spufs_wbox_info_read, NULL, 4 * sizeof(u32)}, 27694fca9c42SMichael Ellerman { "dma_info", __spufs_dma_info_read, NULL, sizeof(struct spu_dma_info)}, 27704fca9c42SMichael Ellerman { "proxydma_info", __spufs_proxydma_info_read, 27714fca9c42SMichael Ellerman NULL, sizeof(struct spu_proxydma_info)}, 2772104f0cc2SMichael Ellerman { "object-id", NULL, spufs_object_id_get, 19 }, 2773104f0cc2SMichael Ellerman { "npc", NULL, spufs_npc_get, 19 }, 2774936d5bf1SMichael Ellerman { NULL }, 2775bf1ab978SDwayne Grant McConnell }; 2776