Commit | Line | Data |
---|---|---|
7ad530bf EG |
1 | /* |
2 | * drivers/base/sync.c | |
3 | * | |
4 | * Copyright (C) 2012 Google, Inc. | |
5 | * | |
6 | * This software is licensed under the terms of the GNU General Public | |
7 | * License version 2, as published by the Free Software Foundation, and | |
8 | * may be copied, distributed, and modified under those terms. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | */ | |
16 | ||
af7582f2 | 17 | #include <linux/debugfs.h> |
8edb4ad9 | 18 | #include <linux/export.h> |
7ad530bf EG |
19 | #include <linux/file.h> |
20 | #include <linux/fs.h> | |
21 | #include <linux/kernel.h> | |
57b505bb | 22 | #include <linux/poll.h> |
7ad530bf | 23 | #include <linux/sched.h> |
af7582f2 | 24 | #include <linux/seq_file.h> |
7ad530bf EG |
25 | #include <linux/slab.h> |
26 | #include <linux/uaccess.h> | |
27 | #include <linux/anon_inodes.h> | |
28 | ||
29 | #include "sync.h" | |
30 | ||
b699a644 EG |
31 | #define CREATE_TRACE_POINTS |
32 | #include "trace/sync.h" | |
33 | ||
0f0d8406 | 34 | static const struct fence_ops android_fence_ops; |
d7fdb0ae | 35 | static const struct file_operations sync_file_fops; |
af7582f2 | 36 | |
7ad530bf EG |
37 | struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, |
38 | int size, const char *name) | |
39 | { | |
40 | struct sync_timeline *obj; | |
41 | ||
42 | if (size < sizeof(struct sync_timeline)) | |
43 | return NULL; | |
44 | ||
45 | obj = kzalloc(size, GFP_KERNEL); | |
375fb53e | 46 | if (!obj) |
7ad530bf EG |
47 | return NULL; |
48 | ||
c5b86b74 | 49 | kref_init(&obj->kref); |
7ad530bf | 50 | obj->ops = ops; |
0f0d8406 | 51 | obj->context = fence_context_alloc(1); |
7ad530bf EG |
52 | strlcpy(obj->name, name, sizeof(obj->name)); |
53 | ||
54 | INIT_LIST_HEAD(&obj->child_list_head); | |
7ad530bf | 55 | INIT_LIST_HEAD(&obj->active_list_head); |
0f0d8406 | 56 | spin_lock_init(&obj->child_list_lock); |
7ad530bf | 57 | |
0f0d8406 | 58 | sync_timeline_debug_add(obj); |
af7582f2 | 59 | |
7ad530bf EG |
60 | return obj; |
61 | } | |
8edb4ad9 | 62 | EXPORT_SYMBOL(sync_timeline_create); |
7ad530bf | 63 | |
c5b86b74 | 64 | static void sync_timeline_free(struct kref *kref) |
af7582f2 | 65 | { |
c5b86b74 EG |
66 | struct sync_timeline *obj = |
67 | container_of(kref, struct sync_timeline, kref); | |
af7582f2 | 68 | |
0f0d8406 | 69 | sync_timeline_debug_remove(obj); |
af7582f2 EG |
70 | |
71 | kfree(obj); | |
72 | } | |
73 | ||
0f0d8406 ML |
74 | static void sync_timeline_get(struct sync_timeline *obj) |
75 | { | |
76 | kref_get(&obj->kref); | |
77 | } | |
78 | ||
79 | static void sync_timeline_put(struct sync_timeline *obj) | |
80 | { | |
81 | kref_put(&obj->kref, sync_timeline_free); | |
82 | } | |
83 | ||
7ad530bf EG |
84 | void sync_timeline_destroy(struct sync_timeline *obj) |
85 | { | |
7ad530bf | 86 | obj->destroyed = true; |
29606609 NY |
87 | /* |
88 | * Ensure timeline is marked as destroyed before | |
89 | * changing timeline's fences status. | |
90 | */ | |
ac5b705b | 91 | smp_wmb(); |
7ad530bf | 92 | |
0f0d8406 | 93 | sync_timeline_put(obj); |
7ad530bf | 94 | } |
8edb4ad9 | 95 | EXPORT_SYMBOL(sync_timeline_destroy); |
7ad530bf | 96 | |
7ad530bf EG |
97 | void sync_timeline_signal(struct sync_timeline *obj) |
98 | { | |
99 | unsigned long flags; | |
b55b54b5 | 100 | struct fence *fence, *next; |
7ad530bf | 101 | |
b699a644 EG |
102 | trace_sync_timeline(obj); |
103 | ||
0f0d8406 | 104 | spin_lock_irqsave(&obj->child_list_lock, flags); |
7ad530bf | 105 | |
b55b54b5 | 106 | list_for_each_entry_safe(fence, next, &obj->active_list_head, |
0f0d8406 | 107 | active_list) { |
b55b54b5 GP |
108 | if (fence_is_signaled_locked(fence)) |
109 | list_del_init(&fence->active_list); | |
7ad530bf EG |
110 | } |
111 | ||
0f0d8406 | 112 | spin_unlock_irqrestore(&obj->child_list_lock, flags); |
7ad530bf | 113 | } |
8edb4ad9 | 114 | EXPORT_SYMBOL(sync_timeline_signal); |
7ad530bf | 115 | |
b55b54b5 | 116 | struct fence *sync_pt_create(struct sync_timeline *obj, int size) |
7ad530bf | 117 | { |
0f0d8406 | 118 | unsigned long flags; |
b55b54b5 | 119 | struct fence *fence; |
7ad530bf | 120 | |
b55b54b5 | 121 | if (size < sizeof(*fence)) |
7ad530bf EG |
122 | return NULL; |
123 | ||
b55b54b5 GP |
124 | fence = kzalloc(size, GFP_KERNEL); |
125 | if (!fence) | |
7ad530bf EG |
126 | return NULL; |
127 | ||
0f0d8406 ML |
128 | spin_lock_irqsave(&obj->child_list_lock, flags); |
129 | sync_timeline_get(obj); | |
b55b54b5 | 130 | fence_init(fence, &android_fence_ops, &obj->child_list_lock, |
0f0d8406 | 131 | obj->context, ++obj->value); |
b55b54b5 GP |
132 | list_add_tail(&fence->child_list, &obj->child_list_head); |
133 | INIT_LIST_HEAD(&fence->active_list); | |
0f0d8406 | 134 | spin_unlock_irqrestore(&obj->child_list_lock, flags); |
b55b54b5 | 135 | return fence; |
7ad530bf | 136 | } |
8edb4ad9 | 137 | EXPORT_SYMBOL(sync_pt_create); |
7ad530bf | 138 | |
d7fdb0ae | 139 | static struct sync_file *sync_file_alloc(int size, const char *name) |
7ad530bf | 140 | { |
d7fdb0ae | 141 | struct sync_file *sync_file; |
7ad530bf | 142 | |
d7fdb0ae GP |
143 | sync_file = kzalloc(size, GFP_KERNEL); |
144 | if (!sync_file) | |
7ad530bf EG |
145 | return NULL; |
146 | ||
d7fdb0ae GP |
147 | sync_file->file = anon_inode_getfile("sync_file", &sync_file_fops, |
148 | sync_file, 0); | |
149 | if (IS_ERR(sync_file->file)) | |
7ad530bf EG |
150 | goto err; |
151 | ||
d7fdb0ae GP |
152 | kref_init(&sync_file->kref); |
153 | strlcpy(sync_file->name, name, sizeof(sync_file->name)); | |
7ad530bf | 154 | |
d7fdb0ae | 155 | init_waitqueue_head(&sync_file->wq); |
af7582f2 | 156 | |
d7fdb0ae | 157 | return sync_file; |
7ad530bf EG |
158 | |
159 | err: | |
d7fdb0ae | 160 | kfree(sync_file); |
7ad530bf EG |
161 | return NULL; |
162 | } | |
163 | ||
0f0d8406 | 164 | static void fence_check_cb_func(struct fence *f, struct fence_cb *cb) |
7ad530bf | 165 | { |
d7fdb0ae GP |
166 | struct sync_file_cb *check; |
167 | struct sync_file *sync_file; | |
7ad530bf | 168 | |
d7fdb0ae GP |
169 | check = container_of(cb, struct sync_file_cb, cb); |
170 | sync_file = check->sync_file; | |
7ad530bf | 171 | |
d7fdb0ae GP |
172 | if (atomic_dec_and_test(&sync_file->status)) |
173 | wake_up_all(&sync_file->wq); | |
7ad530bf EG |
174 | } |
175 | ||
b55b54b5 | 176 | /* TODO: implement a create which takes more that one fence */ |
d52ef2ce | 177 | struct sync_file *sync_file_create(const char *name, struct fence *fence) |
01544170 | 178 | { |
d7fdb0ae | 179 | struct sync_file *sync_file; |
01544170 | 180 | |
d7fdb0ae GP |
181 | sync_file = sync_file_alloc(offsetof(struct sync_file, cbs[1]), |
182 | name); | |
183 | if (!sync_file) | |
0f0d8406 | 184 | return NULL; |
10f62861 | 185 | |
d7fdb0ae GP |
186 | sync_file->num_fences = 1; |
187 | atomic_set(&sync_file->status, 1); | |
01544170 | 188 | |
b55b54b5 | 189 | sync_file->cbs[0].fence = fence; |
d7fdb0ae | 190 | sync_file->cbs[0].sync_file = sync_file; |
b55b54b5 GP |
191 | if (fence_add_callback(fence, &sync_file->cbs[0].cb, |
192 | fence_check_cb_func)) | |
d7fdb0ae | 193 | atomic_dec(&sync_file->status); |
7ad530bf | 194 | |
d7fdb0ae | 195 | sync_file_debug_add(sync_file); |
10f62861 | 196 | |
d7fdb0ae | 197 | return sync_file; |
7ad530bf | 198 | } |
d7fdb0ae | 199 | EXPORT_SYMBOL(sync_file_create); |
7ad530bf | 200 | |
d7fdb0ae | 201 | struct sync_file *sync_file_fdget(int fd) |
7ad530bf EG |
202 | { |
203 | struct file *file = fget(fd); | |
204 | ||
375fb53e | 205 | if (!file) |
7ad530bf EG |
206 | return NULL; |
207 | ||
d7fdb0ae | 208 | if (file->f_op != &sync_file_fops) |
7ad530bf EG |
209 | goto err; |
210 | ||
211 | return file->private_data; | |
212 | ||
213 | err: | |
214 | fput(file); | |
215 | return NULL; | |
216 | } | |
d7fdb0ae | 217 | EXPORT_SYMBOL(sync_file_fdget); |
7ad530bf | 218 | |
d7fdb0ae | 219 | void sync_file_put(struct sync_file *sync_file) |
7ad530bf | 220 | { |
d7fdb0ae | 221 | fput(sync_file->file); |
7ad530bf | 222 | } |
d7fdb0ae | 223 | EXPORT_SYMBOL(sync_file_put); |
7ad530bf | 224 | |
d7fdb0ae | 225 | void sync_file_install(struct sync_file *sync_file, int fd) |
7ad530bf | 226 | { |
d7fdb0ae | 227 | fd_install(fd, sync_file->file); |
7ad530bf | 228 | } |
d7fdb0ae | 229 | EXPORT_SYMBOL(sync_file_install); |
7ad530bf | 230 | |
d7fdb0ae | 231 | static void sync_file_add_pt(struct sync_file *sync_file, int *i, |
b55b54b5 | 232 | struct fence *fence) |
7ad530bf | 233 | { |
b55b54b5 | 234 | sync_file->cbs[*i].fence = fence; |
d7fdb0ae | 235 | sync_file->cbs[*i].sync_file = sync_file; |
7ad530bf | 236 | |
b55b54b5 | 237 | if (!fence_add_callback(fence, &sync_file->cbs[*i].cb, |
d7fdb0ae | 238 | fence_check_cb_func)) { |
b55b54b5 | 239 | fence_get(fence); |
0f0d8406 ML |
240 | (*i)++; |
241 | } | |
7ad530bf EG |
242 | } |
243 | ||
d7fdb0ae GP |
244 | struct sync_file *sync_file_merge(const char *name, |
245 | struct sync_file *a, struct sync_file *b) | |
7ad530bf | 246 | { |
0f0d8406 | 247 | int num_fences = a->num_fences + b->num_fences; |
d7fdb0ae | 248 | struct sync_file *sync_file; |
0f0d8406 | 249 | int i, i_a, i_b; |
d7fdb0ae | 250 | unsigned long size = offsetof(struct sync_file, cbs[num_fences]); |
7ad530bf | 251 | |
d7fdb0ae GP |
252 | sync_file = sync_file_alloc(size, name); |
253 | if (!sync_file) | |
7ad530bf EG |
254 | return NULL; |
255 | ||
d7fdb0ae | 256 | atomic_set(&sync_file->status, num_fences); |
7ad530bf | 257 | |
0f0d8406 | 258 | /* |
d7fdb0ae | 259 | * Assume sync_file a and b are both ordered and have no |
0f0d8406 ML |
260 | * duplicates with the same context. |
261 | * | |
d7fdb0ae GP |
262 | * If a sync_file can only be created with sync_file_merge |
263 | * and sync_file_create, this is a reasonable assumption. | |
0f0d8406 ML |
264 | */ |
265 | for (i = i_a = i_b = 0; i_a < a->num_fences && i_b < b->num_fences; ) { | |
c88b26dd GP |
266 | struct fence *pt_a = a->cbs[i_a].fence; |
267 | struct fence *pt_b = b->cbs[i_b].fence; | |
0f0d8406 ML |
268 | |
269 | if (pt_a->context < pt_b->context) { | |
d7fdb0ae | 270 | sync_file_add_pt(sync_file, &i, pt_a); |
0f0d8406 ML |
271 | |
272 | i_a++; | |
273 | } else if (pt_a->context > pt_b->context) { | |
d7fdb0ae | 274 | sync_file_add_pt(sync_file, &i, pt_b); |
7ad530bf | 275 | |
0f0d8406 ML |
276 | i_b++; |
277 | } else { | |
278 | if (pt_a->seqno - pt_b->seqno <= INT_MAX) | |
d7fdb0ae | 279 | sync_file_add_pt(sync_file, &i, pt_a); |
0f0d8406 | 280 | else |
d7fdb0ae | 281 | sync_file_add_pt(sync_file, &i, pt_b); |
0f0d8406 ML |
282 | |
283 | i_a++; | |
284 | i_b++; | |
285 | } | |
713648f0 ØE |
286 | } |
287 | ||
0f0d8406 | 288 | for (; i_a < a->num_fences; i_a++) |
c88b26dd | 289 | sync_file_add_pt(sync_file, &i, a->cbs[i_a].fence); |
7ad530bf | 290 | |
0f0d8406 | 291 | for (; i_b < b->num_fences; i_b++) |
c88b26dd | 292 | sync_file_add_pt(sync_file, &i, b->cbs[i_b].fence); |
0f0d8406 ML |
293 | |
294 | if (num_fences > i) | |
d7fdb0ae GP |
295 | atomic_sub(num_fences - i, &sync_file->status); |
296 | sync_file->num_fences = i; | |
0f0d8406 | 297 | |
d7fdb0ae GP |
298 | sync_file_debug_add(sync_file); |
299 | return sync_file; | |
7ad530bf | 300 | } |
d7fdb0ae | 301 | EXPORT_SYMBOL(sync_file_merge); |
7ad530bf | 302 | |
0f0d8406 ML |
303 | static const char *android_fence_get_driver_name(struct fence *fence) |
304 | { | |
b55b54b5 | 305 | struct sync_timeline *parent = fence_parent(fence); |
0f0d8406 ML |
306 | |
307 | return parent->ops->driver_name; | |
308 | } | |
309 | ||
310 | static const char *android_fence_get_timeline_name(struct fence *fence) | |
311 | { | |
b55b54b5 | 312 | struct sync_timeline *parent = fence_parent(fence); |
0f0d8406 ML |
313 | |
314 | return parent->name; | |
315 | } | |
316 | ||
317 | static void android_fence_release(struct fence *fence) | |
318 | { | |
b55b54b5 | 319 | struct sync_timeline *parent = fence_parent(fence); |
0f0d8406 ML |
320 | unsigned long flags; |
321 | ||
322 | spin_lock_irqsave(fence->lock, flags); | |
b55b54b5 GP |
323 | list_del(&fence->child_list); |
324 | if (WARN_ON_ONCE(!list_empty(&fence->active_list))) | |
325 | list_del(&fence->active_list); | |
0f0d8406 ML |
326 | spin_unlock_irqrestore(fence->lock, flags); |
327 | ||
0f0d8406 | 328 | sync_timeline_put(parent); |
b55b54b5 | 329 | fence_free(fence); |
0f0d8406 ML |
330 | } |
331 | ||
332 | static bool android_fence_signaled(struct fence *fence) | |
333 | { | |
b55b54b5 | 334 | struct sync_timeline *parent = fence_parent(fence); |
0f0d8406 ML |
335 | int ret; |
336 | ||
b55b54b5 | 337 | ret = parent->ops->has_signaled(fence); |
0f0d8406 ML |
338 | if (ret < 0) |
339 | fence->status = ret; | |
340 | return ret; | |
341 | } | |
342 | ||
343 | static bool android_fence_enable_signaling(struct fence *fence) | |
344 | { | |
b55b54b5 | 345 | struct sync_timeline *parent = fence_parent(fence); |
0f0d8406 ML |
346 | |
347 | if (android_fence_signaled(fence)) | |
348 | return false; | |
349 | ||
b55b54b5 | 350 | list_add_tail(&fence->active_list, &parent->active_list_head); |
0f0d8406 ML |
351 | return true; |
352 | } | |
353 | ||
0f0d8406 ML |
354 | static void android_fence_value_str(struct fence *fence, |
355 | char *str, int size) | |
356 | { | |
b55b54b5 | 357 | struct sync_timeline *parent = fence_parent(fence); |
0f0d8406 | 358 | |
b55b54b5 | 359 | if (!parent->ops->fence_value_str) { |
0f0d8406 ML |
360 | if (size) |
361 | *str = 0; | |
362 | return; | |
363 | } | |
b55b54b5 | 364 | parent->ops->fence_value_str(fence, str, size); |
0f0d8406 ML |
365 | } |
366 | ||
367 | static void android_fence_timeline_value_str(struct fence *fence, | |
368 | char *str, int size) | |
369 | { | |
b55b54b5 | 370 | struct sync_timeline *parent = fence_parent(fence); |
0f0d8406 ML |
371 | |
372 | if (!parent->ops->timeline_value_str) { | |
373 | if (size) | |
374 | *str = 0; | |
375 | return; | |
376 | } | |
377 | parent->ops->timeline_value_str(parent, str, size); | |
378 | } | |
379 | ||
380 | static const struct fence_ops android_fence_ops = { | |
381 | .get_driver_name = android_fence_get_driver_name, | |
382 | .get_timeline_name = android_fence_get_timeline_name, | |
383 | .enable_signaling = android_fence_enable_signaling, | |
384 | .signaled = android_fence_signaled, | |
385 | .wait = fence_default_wait, | |
386 | .release = android_fence_release, | |
0f0d8406 ML |
387 | .fence_value_str = android_fence_value_str, |
388 | .timeline_value_str = android_fence_timeline_value_str, | |
389 | }; | |
390 | ||
d7fdb0ae | 391 | static void sync_file_free(struct kref *kref) |
01544170 | 392 | { |
d7fdb0ae GP |
393 | struct sync_file *sync_file = container_of(kref, struct sync_file, |
394 | kref); | |
699f6855 | 395 | int i; |
01544170 | 396 | |
d7fdb0ae | 397 | for (i = 0; i < sync_file->num_fences; ++i) { |
c88b26dd | 398 | fence_remove_callback(sync_file->cbs[i].fence, |
d7fdb0ae | 399 | &sync_file->cbs[i].cb); |
c88b26dd | 400 | fence_put(sync_file->cbs[i].fence); |
0f0d8406 | 401 | } |
01544170 | 402 | |
d7fdb0ae | 403 | kfree(sync_file); |
01544170 EG |
404 | } |
405 | ||
d7fdb0ae | 406 | static int sync_file_release(struct inode *inode, struct file *file) |
7ad530bf | 407 | { |
d7fdb0ae | 408 | struct sync_file *sync_file = file->private_data; |
af7582f2 | 409 | |
d7fdb0ae | 410 | sync_file_debug_remove(sync_file); |
cc3c5cdc | 411 | |
d7fdb0ae | 412 | kref_put(&sync_file->kref, sync_file_free); |
7ad530bf EG |
413 | return 0; |
414 | } | |
415 | ||
d7fdb0ae | 416 | static unsigned int sync_file_poll(struct file *file, poll_table *wait) |
57b505bb | 417 | { |
d7fdb0ae | 418 | struct sync_file *sync_file = file->private_data; |
0f0d8406 | 419 | int status; |
57b505bb | 420 | |
d7fdb0ae | 421 | poll_wait(file, &sync_file->wq, wait); |
57b505bb | 422 | |
d7fdb0ae | 423 | status = atomic_read(&sync_file->status); |
c679212d | 424 | |
0f0d8406 | 425 | if (!status) |
57b505bb | 426 | return POLLIN; |
5f9001dc | 427 | if (status < 0) |
57b505bb | 428 | return POLLERR; |
6a44b50f | 429 | return 0; |
57b505bb EG |
430 | } |
431 | ||
d7fdb0ae GP |
432 | static long sync_file_ioctl_merge(struct sync_file *sync_file, |
433 | unsigned long arg) | |
7ad530bf | 434 | { |
9c6cd3b3 | 435 | int fd = get_unused_fd_flags(O_CLOEXEC); |
7ad530bf | 436 | int err; |
d7fdb0ae | 437 | struct sync_file *fence2, *fence3; |
7ad530bf EG |
438 | struct sync_merge_data data; |
439 | ||
92ea915a RSZ |
440 | if (fd < 0) |
441 | return fd; | |
442 | ||
443 | if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { | |
444 | err = -EFAULT; | |
445 | goto err_put_fd; | |
446 | } | |
7ad530bf | 447 | |
d7fdb0ae | 448 | fence2 = sync_file_fdget(data.fd2); |
375fb53e | 449 | if (!fence2) { |
7ad530bf EG |
450 | err = -ENOENT; |
451 | goto err_put_fd; | |
452 | } | |
453 | ||
454 | data.name[sizeof(data.name) - 1] = '\0'; | |
d7fdb0ae | 455 | fence3 = sync_file_merge(data.name, sync_file, fence2); |
375fb53e | 456 | if (!fence3) { |
7ad530bf EG |
457 | err = -ENOMEM; |
458 | goto err_put_fence2; | |
459 | } | |
460 | ||
461 | data.fence = fd; | |
462 | if (copy_to_user((void __user *)arg, &data, sizeof(data))) { | |
463 | err = -EFAULT; | |
464 | goto err_put_fence3; | |
465 | } | |
466 | ||
d7fdb0ae GP |
467 | sync_file_install(fence3, fd); |
468 | sync_file_put(fence2); | |
7ad530bf EG |
469 | return 0; |
470 | ||
471 | err_put_fence3: | |
d7fdb0ae | 472 | sync_file_put(fence3); |
7ad530bf EG |
473 | |
474 | err_put_fence2: | |
d7fdb0ae | 475 | sync_file_put(fence2); |
7ad530bf EG |
476 | |
477 | err_put_fd: | |
478 | put_unused_fd(fd); | |
479 | return err; | |
480 | } | |
481 | ||
e1786348 | 482 | static int sync_fill_fence_info(struct fence *fence, void *data, int size) |
79ba1525 | 483 | { |
e1786348 | 484 | struct sync_fence_info *info = data; |
79ba1525 | 485 | |
e1786348 | 486 | if (size < sizeof(*info)) |
79ba1525 EG |
487 | return -ENOMEM; |
488 | ||
0f0d8406 ML |
489 | strlcpy(info->obj_name, fence->ops->get_timeline_name(fence), |
490 | sizeof(info->obj_name)); | |
491 | strlcpy(info->driver_name, fence->ops->get_driver_name(fence), | |
79ba1525 | 492 | sizeof(info->driver_name)); |
0f0d8406 ML |
493 | if (fence_is_signaled(fence)) |
494 | info->status = fence->status >= 0 ? 1 : fence->status; | |
495 | else | |
496 | info->status = 0; | |
497 | info->timestamp_ns = ktime_to_ns(fence->timestamp); | |
79ba1525 | 498 | |
323de43f | 499 | return sizeof(*info); |
79ba1525 EG |
500 | } |
501 | ||
d7fdb0ae | 502 | static long sync_file_ioctl_fence_info(struct sync_file *sync_file, |
79ba1525 EG |
503 | unsigned long arg) |
504 | { | |
b5b24ac5 | 505 | struct sync_file_info *info; |
79ba1525 EG |
506 | __u32 size; |
507 | __u32 len = 0; | |
0f0d8406 | 508 | int ret, i; |
79ba1525 EG |
509 | |
510 | if (copy_from_user(&size, (void __user *)arg, sizeof(size))) | |
511 | return -EFAULT; | |
512 | ||
b5b24ac5 | 513 | if (size < sizeof(struct sync_file_info)) |
79ba1525 EG |
514 | return -EINVAL; |
515 | ||
516 | if (size > 4096) | |
517 | size = 4096; | |
518 | ||
b5b24ac5 GP |
519 | info = kzalloc(size, GFP_KERNEL); |
520 | if (!info) | |
79ba1525 EG |
521 | return -ENOMEM; |
522 | ||
b5b24ac5 GP |
523 | strlcpy(info->name, sync_file->name, sizeof(info->name)); |
524 | info->status = atomic_read(&sync_file->status); | |
525 | if (info->status >= 0) | |
526 | info->status = !info->status; | |
0f0d8406 | 527 | |
b5b24ac5 | 528 | len = sizeof(struct sync_file_info); |
79ba1525 | 529 | |
d7fdb0ae | 530 | for (i = 0; i < sync_file->num_fences; ++i) { |
b55b54b5 | 531 | struct fence *fence = sync_file->cbs[i].fence; |
79ba1525 | 532 | |
b5b24ac5 | 533 | ret = sync_fill_fence_info(fence, (u8 *)info + len, size - len); |
79ba1525 EG |
534 | |
535 | if (ret < 0) | |
536 | goto out; | |
537 | ||
538 | len += ret; | |
539 | } | |
540 | ||
b5b24ac5 | 541 | info->len = len; |
79ba1525 | 542 | |
b5b24ac5 | 543 | if (copy_to_user((void __user *)arg, info, len)) |
79ba1525 EG |
544 | ret = -EFAULT; |
545 | else | |
546 | ret = 0; | |
547 | ||
548 | out: | |
b5b24ac5 | 549 | kfree(info); |
79ba1525 EG |
550 | |
551 | return ret; | |
552 | } | |
7ad530bf | 553 | |
d7fdb0ae | 554 | static long sync_file_ioctl(struct file *file, unsigned int cmd, |
7ad530bf EG |
555 | unsigned long arg) |
556 | { | |
d7fdb0ae | 557 | struct sync_file *sync_file = file->private_data; |
7b1046e0 | 558 | |
7ad530bf | 559 | switch (cmd) { |
7ad530bf | 560 | case SYNC_IOC_MERGE: |
d7fdb0ae | 561 | return sync_file_ioctl_merge(sync_file, arg); |
af7582f2 | 562 | |
79ba1525 | 563 | case SYNC_IOC_FENCE_INFO: |
d7fdb0ae | 564 | return sync_file_ioctl_fence_info(sync_file, arg); |
79ba1525 | 565 | |
7ad530bf EG |
566 | default: |
567 | return -ENOTTY; | |
568 | } | |
569 | } | |
570 | ||
d7fdb0ae GP |
571 | static const struct file_operations sync_file_fops = { |
572 | .release = sync_file_release, | |
573 | .poll = sync_file_poll, | |
574 | .unlocked_ioctl = sync_file_ioctl, | |
575 | .compat_ioctl = sync_file_ioctl, | |
af7582f2 EG |
576 | }; |
577 |