1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * SPU file system -- system call stubs |
4 | * |
5 | * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 |
6 | * (C) Copyright 2006-2007, IBM Corporation |
7 | * |
8 | * Author: Arnd Bergmann <arndb@de.ibm.com> |
9 | */ |
10 | #include <linux/file.h> |
11 | #include <linux/fs.h> |
12 | #include <linux/module.h> |
13 | #include <linux/syscalls.h> |
14 | #include <linux/rcupdate.h> |
15 | #include <linux/binfmts.h> |
16 | |
17 | #include <asm/spu.h> |
18 | |
19 | /* protected by rcu */ |
20 | static struct spufs_calls *spufs_calls; |
21 | |
22 | #ifdef CONFIG_SPU_FS_MODULE |
23 | |
24 | static inline struct spufs_calls *spufs_calls_get(void) |
25 | { |
26 | struct spufs_calls *calls = NULL; |
27 | |
28 | rcu_read_lock(); |
29 | calls = rcu_dereference(spufs_calls); |
30 | if (calls && !try_module_get(calls->owner)) |
31 | calls = NULL; |
32 | rcu_read_unlock(); |
33 | |
34 | return calls; |
35 | } |
36 | |
37 | static inline void spufs_calls_put(struct spufs_calls *calls) |
38 | { |
39 | if (!calls) |
40 | return; |
41 | |
42 | BUG_ON(calls != spufs_calls); |
43 | |
44 | /* we don't need to rcu this, as we hold a reference to the module */ |
45 | module_put(spufs_calls->owner); |
46 | } |
47 | |
48 | #else /* !defined CONFIG_SPU_FS_MODULE */ |
49 | |
50 | static inline struct spufs_calls *spufs_calls_get(void) |
51 | { |
52 | return spufs_calls; |
53 | } |
54 | |
55 | static inline void spufs_calls_put(struct spufs_calls *calls) { } |
56 | |
57 | #endif /* CONFIG_SPU_FS_MODULE */ |
58 | |
59 | DEFINE_CLASS(spufs_calls, struct spufs_calls *, spufs_calls_put(_T), spufs_calls_get(), void) |
60 | |
61 | SYSCALL_DEFINE4(spu_create, const char __user *, name, unsigned int, flags, |
62 | umode_t, mode, int, neighbor_fd) |
63 | { |
64 | CLASS(spufs_calls, calls)(); |
65 | if (!calls) |
66 | return -ENOSYS; |
67 | |
68 | if (flags & SPU_CREATE_AFFINITY_SPU) { |
69 | CLASS(fd, neighbor)(fd: neighbor_fd); |
70 | if (fd_empty(f: neighbor)) |
71 | return -EBADF; |
72 | return calls->create_thread(name, flags, mode, fd_file(neighbor)); |
73 | } else { |
74 | return calls->create_thread(name, flags, mode, NULL); |
75 | } |
76 | } |
77 | |
78 | SYSCALL_DEFINE3(spu_run,int, fd, __u32 __user *, unpc, __u32 __user *, ustatus) |
79 | { |
80 | CLASS(spufs_calls, calls)(); |
81 | if (!calls) |
82 | return -ENOSYS; |
83 | |
84 | CLASS(fd, arg)(fd); |
85 | if (fd_empty(f: arg)) |
86 | return -EBADF; |
87 | |
88 | return calls->spu_run(fd_file(arg), unpc, ustatus); |
89 | } |
90 | |
91 | #ifdef CONFIG_COREDUMP |
92 | int (void) |
93 | { |
94 | CLASS(spufs_calls, calls)(); |
95 | if (!calls) |
96 | return 0; |
97 | |
98 | return calls->coredump_extra_notes_size(); |
99 | } |
100 | |
101 | int (struct coredump_params *cprm) |
102 | { |
103 | CLASS(spufs_calls, calls)(); |
104 | if (!calls) |
105 | return 0; |
106 | |
107 | return calls->coredump_extra_notes_write(cprm); |
108 | } |
109 | #endif |
110 | |
111 | void notify_spus_active(void) |
112 | { |
113 | struct spufs_calls *calls; |
114 | |
115 | calls = spufs_calls_get(); |
116 | if (!calls) |
117 | return; |
118 | |
119 | calls->notify_spus_active(); |
120 | spufs_calls_put(calls); |
121 | |
122 | return; |
123 | } |
124 | |
125 | int register_spu_syscalls(struct spufs_calls *calls) |
126 | { |
127 | if (spufs_calls) |
128 | return -EBUSY; |
129 | |
130 | rcu_assign_pointer(spufs_calls, calls); |
131 | return 0; |
132 | } |
133 | EXPORT_SYMBOL_GPL(register_spu_syscalls); |
134 | |
135 | void unregister_spu_syscalls(struct spufs_calls *calls) |
136 | { |
137 | BUG_ON(spufs_calls->owner != calls->owner); |
138 | RCU_INIT_POINTER(spufs_calls, NULL); |
139 | synchronize_rcu(); |
140 | } |
141 | EXPORT_SYMBOL_GPL(unregister_spu_syscalls); |
142 | |