1 /* 2 * SPU file system -- system call stubs 3 * 4 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 5 * (C) Copyright 2006-2007, IBM Corporation 6 * 7 * Author: Arnd Bergmann <arndb@de.ibm.com> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2, or (at your option) 12 * any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 */ 23 #include <linux/file.h> 24 #include <linux/fs.h> 25 #include <linux/module.h> 26 #include <linux/syscalls.h> 27 #include <linux/rcupdate.h> 28 #include <linux/binfmts.h> 29 30 #include <asm/spu.h> 31 32 /* protected by rcu */ 33 static struct spufs_calls *spufs_calls; 34 35 #ifdef CONFIG_SPU_FS_MODULE 36 37 static inline struct spufs_calls *spufs_calls_get(void) 38 { 39 struct spufs_calls *calls = NULL; 40 41 rcu_read_lock(); 42 calls = rcu_dereference(spufs_calls); 43 if (calls && !try_module_get(calls->owner)) 44 calls = NULL; 45 rcu_read_unlock(); 46 47 return calls; 48 } 49 50 static inline void spufs_calls_put(struct spufs_calls *calls) 51 { 52 BUG_ON(calls != spufs_calls); 53 54 /* we don't need to rcu this, as we hold a reference to the module */ 55 module_put(spufs_calls->owner); 56 } 57 58 #else /* !defined CONFIG_SPU_FS_MODULE */ 59 60 static inline struct spufs_calls *spufs_calls_get(void) 61 { 62 return spufs_calls; 63 } 64 65 static inline void spufs_calls_put(struct spufs_calls *calls) { } 66 67 #endif /* CONFIG_SPU_FS_MODULE */ 68 69 SYSCALL_DEFINE4(spu_create, const char __user *, name, unsigned int, flags, 70 umode_t, mode, int, neighbor_fd) 71 { 72 long ret; 73 struct spufs_calls *calls; 74 75 calls = spufs_calls_get(); 76 if (!calls) 77 return -ENOSYS; 78 79 if (flags & SPU_CREATE_AFFINITY_SPU) { 80 struct fd neighbor = fdget(neighbor_fd); 81 ret = -EBADF; 82 if (neighbor.file) { 83 ret = calls->create_thread(name, flags, mode, neighbor.file); 84 fdput(neighbor); 85 } 86 } else 87 ret = calls->create_thread(name, flags, mode, NULL); 88 89 spufs_calls_put(calls); 90 return ret; 91 } 92 93 asmlinkage long sys_spu_run(int fd, __u32 __user *unpc, __u32 __user *ustatus) 94 { 95 long ret; 96 struct fd arg; 97 struct spufs_calls *calls; 98 99 calls = spufs_calls_get(); 100 if (!calls) 101 return -ENOSYS; 102 103 ret = -EBADF; 104 arg = fdget(fd); 105 if (arg.file) { 106 ret = calls->spu_run(arg.file, unpc, ustatus); 107 fdput(arg); 108 } 109 110 spufs_calls_put(calls); 111 return ret; 112 } 113 114 #ifdef CONFIG_COREDUMP 115 int elf_coredump_extra_notes_size(void) 116 { 117 struct spufs_calls *calls; 118 int ret; 119 120 calls = spufs_calls_get(); 121 if (!calls) 122 return 0; 123 124 ret = calls->coredump_extra_notes_size(); 125 126 spufs_calls_put(calls); 127 128 return ret; 129 } 130 131 int elf_coredump_extra_notes_write(struct coredump_params *cprm) 132 { 133 struct spufs_calls *calls; 134 int ret; 135 136 calls = spufs_calls_get(); 137 if (!calls) 138 return 0; 139 140 ret = calls->coredump_extra_notes_write(cprm); 141 142 spufs_calls_put(calls); 143 144 return ret; 145 } 146 #endif 147 148 void notify_spus_active(void) 149 { 150 struct spufs_calls *calls; 151 152 calls = spufs_calls_get(); 153 if (!calls) 154 return; 155 156 calls->notify_spus_active(); 157 spufs_calls_put(calls); 158 159 return; 160 } 161 162 int register_spu_syscalls(struct spufs_calls *calls) 163 { 164 if (spufs_calls) 165 return -EBUSY; 166 167 rcu_assign_pointer(spufs_calls, calls); 168 return 0; 169 } 170 EXPORT_SYMBOL_GPL(register_spu_syscalls); 171 172 void unregister_spu_syscalls(struct spufs_calls *calls) 173 { 174 BUG_ON(spufs_calls->owner != calls->owner); 175 RCU_INIT_POINTER(spufs_calls, NULL); 176 synchronize_rcu(); 177 } 178 EXPORT_SYMBOL_GPL(unregister_spu_syscalls); 179