| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | /* | 
|---|
| 3 | * drivers/dma-buf/sync_file.c | 
|---|
| 4 | * | 
|---|
| 5 | * Copyright (C) 2012 Google, Inc. | 
|---|
| 6 | */ | 
|---|
| 7 |  | 
|---|
| 8 | #include <linux/dma-fence-unwrap.h> | 
|---|
| 9 | #include <linux/export.h> | 
|---|
| 10 | #include <linux/file.h> | 
|---|
| 11 | #include <linux/fs.h> | 
|---|
| 12 | #include <linux/kernel.h> | 
|---|
| 13 | #include <linux/poll.h> | 
|---|
| 14 | #include <linux/sched.h> | 
|---|
| 15 | #include <linux/slab.h> | 
|---|
| 16 | #include <linux/uaccess.h> | 
|---|
| 17 | #include <linux/anon_inodes.h> | 
|---|
| 18 | #include <linux/sync_file.h> | 
|---|
| 19 | #include <uapi/linux/sync_file.h> | 
|---|
| 20 |  | 
|---|
| 21 | static const struct file_operations sync_file_fops; | 
|---|
| 22 |  | 
|---|
| 23 | static struct sync_file *sync_file_alloc(void) | 
|---|
| 24 | { | 
|---|
| 25 | struct sync_file *sync_file; | 
|---|
| 26 |  | 
|---|
| 27 | sync_file = kzalloc(sizeof(*sync_file), GFP_KERNEL); | 
|---|
| 28 | if (!sync_file) | 
|---|
| 29 | return NULL; | 
|---|
| 30 |  | 
|---|
| 31 | sync_file->file = anon_inode_getfile(name: "sync_file", fops: &sync_file_fops, | 
|---|
| 32 | priv: sync_file, flags: 0); | 
|---|
| 33 | if (IS_ERR(ptr: sync_file->file)) | 
|---|
| 34 | goto err; | 
|---|
| 35 |  | 
|---|
| 36 | init_waitqueue_head(&sync_file->wq); | 
|---|
| 37 |  | 
|---|
| 38 | INIT_LIST_HEAD(list: &sync_file->cb.node); | 
|---|
| 39 |  | 
|---|
| 40 | return sync_file; | 
|---|
| 41 |  | 
|---|
| 42 | err: | 
|---|
| 43 | kfree(objp: sync_file); | 
|---|
| 44 | return NULL; | 
|---|
| 45 | } | 
|---|
| 46 |  | 
|---|
| 47 | static void fence_check_cb_func(struct dma_fence *f, struct dma_fence_cb *cb) | 
|---|
| 48 | { | 
|---|
| 49 | struct sync_file *sync_file; | 
|---|
| 50 |  | 
|---|
| 51 | sync_file = container_of(cb, struct sync_file, cb); | 
|---|
| 52 |  | 
|---|
| 53 | wake_up_all(&sync_file->wq); | 
|---|
| 54 | } | 
|---|
| 55 |  | 
|---|
| 56 | /** | 
|---|
| 57 | * sync_file_create() - creates a sync file | 
|---|
| 58 | * @fence:	fence to add to the sync_fence | 
|---|
| 59 | * | 
|---|
| 60 | * Creates a sync_file containg @fence. This function acquires and additional | 
|---|
| 61 | * reference of @fence for the newly-created &sync_file, if it succeeds. The | 
|---|
| 62 | * sync_file can be released with fput(sync_file->file). Returns the | 
|---|
| 63 | * sync_file or NULL in case of error. | 
|---|
| 64 | */ | 
|---|
| 65 | struct sync_file *sync_file_create(struct dma_fence *fence) | 
|---|
| 66 | { | 
|---|
| 67 | struct sync_file *sync_file; | 
|---|
| 68 |  | 
|---|
| 69 | sync_file = sync_file_alloc(); | 
|---|
| 70 | if (!sync_file) | 
|---|
| 71 | return NULL; | 
|---|
| 72 |  | 
|---|
| 73 | sync_file->fence = dma_fence_get(fence); | 
|---|
| 74 |  | 
|---|
| 75 | return sync_file; | 
|---|
| 76 | } | 
|---|
| 77 | EXPORT_SYMBOL(sync_file_create); | 
|---|
| 78 |  | 
|---|
| 79 | static struct sync_file *sync_file_fdget(int fd) | 
|---|
| 80 | { | 
|---|
| 81 | struct file *file = fget(fd); | 
|---|
| 82 |  | 
|---|
| 83 | if (!file) | 
|---|
| 84 | return NULL; | 
|---|
| 85 |  | 
|---|
| 86 | if (file->f_op != &sync_file_fops) | 
|---|
| 87 | goto err; | 
|---|
| 88 |  | 
|---|
| 89 | return file->private_data; | 
|---|
| 90 |  | 
|---|
| 91 | err: | 
|---|
| 92 | fput(file); | 
|---|
| 93 | return NULL; | 
|---|
| 94 | } | 
|---|
| 95 |  | 
|---|
| 96 | /** | 
|---|
| 97 | * sync_file_get_fence - get the fence related to the sync_file fd | 
|---|
| 98 | * @fd:		sync_file fd to get the fence from | 
|---|
| 99 | * | 
|---|
| 100 | * Ensures @fd references a valid sync_file and returns a fence that | 
|---|
| 101 | * represents all fence in the sync_file. On error NULL is returned. | 
|---|
| 102 | */ | 
|---|
| 103 | struct dma_fence *sync_file_get_fence(int fd) | 
|---|
| 104 | { | 
|---|
| 105 | struct sync_file *sync_file; | 
|---|
| 106 | struct dma_fence *fence; | 
|---|
| 107 |  | 
|---|
| 108 | sync_file = sync_file_fdget(fd); | 
|---|
| 109 | if (!sync_file) | 
|---|
| 110 | return NULL; | 
|---|
| 111 |  | 
|---|
| 112 | fence = dma_fence_get(fence: sync_file->fence); | 
|---|
| 113 | fput(sync_file->file); | 
|---|
| 114 |  | 
|---|
| 115 | return fence; | 
|---|
| 116 | } | 
|---|
| 117 | EXPORT_SYMBOL(sync_file_get_fence); | 
|---|
| 118 |  | 
|---|
| 119 | /** | 
|---|
| 120 | * sync_file_get_name - get the name of the sync_file | 
|---|
| 121 | * @sync_file:		sync_file to get the fence from | 
|---|
| 122 | * @buf:		destination buffer to copy sync_file name into | 
|---|
| 123 | * @len:		available size of destination buffer. | 
|---|
| 124 | * | 
|---|
| 125 | * Each sync_file may have a name assigned either by the user (when merging | 
|---|
| 126 | * sync_files together) or created from the fence it contains. In the latter | 
|---|
| 127 | * case construction of the name is deferred until use, and so requires | 
|---|
| 128 | * sync_file_get_name(). | 
|---|
| 129 | * | 
|---|
| 130 | * Returns: a string representing the name. | 
|---|
| 131 | */ | 
|---|
| 132 | char *sync_file_get_name(struct sync_file *sync_file, char *buf, int len) | 
|---|
| 133 | { | 
|---|
| 134 | if (sync_file->user_name[0]) { | 
|---|
| 135 | strscpy(buf, sync_file->user_name, len); | 
|---|
| 136 | } else { | 
|---|
| 137 | struct dma_fence *fence = sync_file->fence; | 
|---|
| 138 | const char __rcu *timeline; | 
|---|
| 139 | const char __rcu *driver; | 
|---|
| 140 |  | 
|---|
| 141 | rcu_read_lock(); | 
|---|
| 142 | driver = dma_fence_driver_name(fence); | 
|---|
| 143 | timeline = dma_fence_timeline_name(fence); | 
|---|
| 144 | snprintf(buf, size: len, fmt: "%s-%s%llu-%lld", | 
|---|
| 145 | rcu_dereference(driver), | 
|---|
| 146 | rcu_dereference(timeline), | 
|---|
| 147 | fence->context, | 
|---|
| 148 | fence->seqno); | 
|---|
| 149 | rcu_read_unlock(); | 
|---|
| 150 | } | 
|---|
| 151 |  | 
|---|
| 152 | return buf; | 
|---|
| 153 | } | 
|---|
| 154 |  | 
|---|
| 155 | /** | 
|---|
| 156 | * sync_file_merge() - merge two sync_files | 
|---|
| 157 | * @name:	name of new fence | 
|---|
| 158 | * @a:		sync_file a | 
|---|
| 159 | * @b:		sync_file b | 
|---|
| 160 | * | 
|---|
| 161 | * Creates a new sync_file which contains copies of all the fences in both | 
|---|
| 162 | * @a and @b.  @a and @b remain valid, independent sync_file. Returns the | 
|---|
| 163 | * new merged sync_file or NULL in case of error. | 
|---|
| 164 | */ | 
|---|
| 165 | static struct sync_file *sync_file_merge(const char *name, struct sync_file *a, | 
|---|
| 166 | struct sync_file *b) | 
|---|
| 167 | { | 
|---|
| 168 | struct sync_file *sync_file; | 
|---|
| 169 | struct dma_fence *fence; | 
|---|
| 170 |  | 
|---|
| 171 | sync_file = sync_file_alloc(); | 
|---|
| 172 | if (!sync_file) | 
|---|
| 173 | return NULL; | 
|---|
| 174 |  | 
|---|
| 175 | fence = dma_fence_unwrap_merge(a->fence, b->fence); | 
|---|
| 176 | if (!fence) { | 
|---|
| 177 | fput(sync_file->file); | 
|---|
| 178 | return NULL; | 
|---|
| 179 | } | 
|---|
| 180 | sync_file->fence = fence; | 
|---|
| 181 | strscpy(sync_file->user_name, name, sizeof(sync_file->user_name)); | 
|---|
| 182 | return sync_file; | 
|---|
| 183 | } | 
|---|
| 184 |  | 
|---|
| 185 | static int sync_file_release(struct inode *inode, struct file *file) | 
|---|
| 186 | { | 
|---|
| 187 | struct sync_file *sync_file = file->private_data; | 
|---|
| 188 |  | 
|---|
| 189 | if (test_bit(POLL_ENABLED, &sync_file->flags)) | 
|---|
| 190 | dma_fence_remove_callback(fence: sync_file->fence, cb: &sync_file->cb); | 
|---|
| 191 | dma_fence_put(fence: sync_file->fence); | 
|---|
| 192 | kfree(objp: sync_file); | 
|---|
| 193 |  | 
|---|
| 194 | return 0; | 
|---|
| 195 | } | 
|---|
| 196 |  | 
|---|
| 197 | static __poll_t sync_file_poll(struct file *file, poll_table *wait) | 
|---|
| 198 | { | 
|---|
| 199 | struct sync_file *sync_file = file->private_data; | 
|---|
| 200 |  | 
|---|
| 201 | poll_wait(filp: file, wait_address: &sync_file->wq, p: wait); | 
|---|
| 202 |  | 
|---|
| 203 | if (list_empty(head: &sync_file->cb.node) && | 
|---|
| 204 | !test_and_set_bit(POLL_ENABLED, addr: &sync_file->flags)) { | 
|---|
| 205 | if (dma_fence_add_callback(fence: sync_file->fence, cb: &sync_file->cb, | 
|---|
| 206 | func: fence_check_cb_func) < 0) | 
|---|
| 207 | wake_up_all(&sync_file->wq); | 
|---|
| 208 | } | 
|---|
| 209 |  | 
|---|
| 210 | return dma_fence_is_signaled(fence: sync_file->fence) ? EPOLLIN : 0; | 
|---|
| 211 | } | 
|---|
| 212 |  | 
|---|
| 213 | static long sync_file_ioctl_merge(struct sync_file *sync_file, | 
|---|
| 214 | unsigned long arg) | 
|---|
| 215 | { | 
|---|
| 216 | int fd = get_unused_fd_flags(O_CLOEXEC); | 
|---|
| 217 | int err; | 
|---|
| 218 | struct sync_file *fence2, *fence3; | 
|---|
| 219 | struct sync_merge_data data; | 
|---|
| 220 |  | 
|---|
| 221 | if (fd < 0) | 
|---|
| 222 | return fd; | 
|---|
| 223 |  | 
|---|
| 224 | if (copy_from_user(to: &data, from: (void __user *)arg, n: sizeof(data))) { | 
|---|
| 225 | err = -EFAULT; | 
|---|
| 226 | goto err_put_fd; | 
|---|
| 227 | } | 
|---|
| 228 |  | 
|---|
| 229 | if (data.flags || data.pad) { | 
|---|
| 230 | err = -EINVAL; | 
|---|
| 231 | goto err_put_fd; | 
|---|
| 232 | } | 
|---|
| 233 |  | 
|---|
| 234 | fence2 = sync_file_fdget(fd: data.fd2); | 
|---|
| 235 | if (!fence2) { | 
|---|
| 236 | err = -ENOENT; | 
|---|
| 237 | goto err_put_fd; | 
|---|
| 238 | } | 
|---|
| 239 |  | 
|---|
| 240 | data.name[sizeof(data.name) - 1] = '\0'; | 
|---|
| 241 | fence3 = sync_file_merge(name: data.name, a: sync_file, b: fence2); | 
|---|
| 242 | if (!fence3) { | 
|---|
| 243 | err = -ENOMEM; | 
|---|
| 244 | goto err_put_fence2; | 
|---|
| 245 | } | 
|---|
| 246 |  | 
|---|
| 247 | data.fence = fd; | 
|---|
| 248 | if (copy_to_user(to: (void __user *)arg, from: &data, n: sizeof(data))) { | 
|---|
| 249 | err = -EFAULT; | 
|---|
| 250 | goto err_put_fence3; | 
|---|
| 251 | } | 
|---|
| 252 |  | 
|---|
| 253 | fd_install(fd, file: fence3->file); | 
|---|
| 254 | fput(fence2->file); | 
|---|
| 255 | return 0; | 
|---|
| 256 |  | 
|---|
| 257 | err_put_fence3: | 
|---|
| 258 | fput(fence3->file); | 
|---|
| 259 |  | 
|---|
| 260 | err_put_fence2: | 
|---|
| 261 | fput(fence2->file); | 
|---|
| 262 |  | 
|---|
| 263 | err_put_fd: | 
|---|
| 264 | put_unused_fd(fd); | 
|---|
| 265 | return err; | 
|---|
| 266 | } | 
|---|
| 267 |  | 
|---|
| 268 | static int sync_fill_fence_info(struct dma_fence *fence, | 
|---|
| 269 | struct sync_fence_info *info) | 
|---|
| 270 | { | 
|---|
| 271 | const char __rcu *timeline; | 
|---|
| 272 | const char __rcu *driver; | 
|---|
| 273 |  | 
|---|
| 274 | rcu_read_lock(); | 
|---|
| 275 |  | 
|---|
| 276 | driver = dma_fence_driver_name(fence); | 
|---|
| 277 | timeline = dma_fence_timeline_name(fence); | 
|---|
| 278 |  | 
|---|
| 279 | strscpy(info->obj_name, rcu_dereference(timeline), | 
|---|
| 280 | sizeof(info->obj_name)); | 
|---|
| 281 | strscpy(info->driver_name, rcu_dereference(driver), | 
|---|
| 282 | sizeof(info->driver_name)); | 
|---|
| 283 |  | 
|---|
| 284 | info->status = dma_fence_get_status(fence); | 
|---|
| 285 | info->timestamp_ns = | 
|---|
| 286 | dma_fence_is_signaled(fence) ? | 
|---|
| 287 | ktime_to_ns(kt: dma_fence_timestamp(fence)) : | 
|---|
| 288 | ktime_set(secs: 0, nsecs: 0); | 
|---|
| 289 |  | 
|---|
| 290 | rcu_read_unlock(); | 
|---|
| 291 |  | 
|---|
| 292 | return info->status; | 
|---|
| 293 | } | 
|---|
| 294 |  | 
|---|
| 295 | static long sync_file_ioctl_fence_info(struct sync_file *sync_file, | 
|---|
| 296 | unsigned long arg) | 
|---|
| 297 | { | 
|---|
| 298 | struct sync_fence_info *fence_info = NULL; | 
|---|
| 299 | struct dma_fence_unwrap iter; | 
|---|
| 300 | struct sync_file_info info; | 
|---|
| 301 | unsigned int num_fences; | 
|---|
| 302 | struct dma_fence *fence; | 
|---|
| 303 | int ret; | 
|---|
| 304 | __u32 size; | 
|---|
| 305 |  | 
|---|
| 306 | if (copy_from_user(to: &info, from: (void __user *)arg, n: sizeof(info))) | 
|---|
| 307 | return -EFAULT; | 
|---|
| 308 |  | 
|---|
| 309 | if (info.flags || info.pad) | 
|---|
| 310 | return -EINVAL; | 
|---|
| 311 |  | 
|---|
| 312 | num_fences = 0; | 
|---|
| 313 | dma_fence_unwrap_for_each(fence, &iter, sync_file->fence) | 
|---|
| 314 | ++num_fences; | 
|---|
| 315 |  | 
|---|
| 316 | /* | 
|---|
| 317 | * Passing num_fences = 0 means that userspace doesn't want to | 
|---|
| 318 | * retrieve any sync_fence_info. If num_fences = 0 we skip filling | 
|---|
| 319 | * sync_fence_info and return the actual number of fences on | 
|---|
| 320 | * info->num_fences. | 
|---|
| 321 | */ | 
|---|
| 322 | if (!info.num_fences) { | 
|---|
| 323 | info.status = dma_fence_get_status(fence: sync_file->fence); | 
|---|
| 324 | goto no_fences; | 
|---|
| 325 | } else { | 
|---|
| 326 | info.status = 1; | 
|---|
| 327 | } | 
|---|
| 328 |  | 
|---|
| 329 | if (info.num_fences < num_fences) | 
|---|
| 330 | return -EINVAL; | 
|---|
| 331 |  | 
|---|
| 332 | size = num_fences * sizeof(*fence_info); | 
|---|
| 333 | fence_info = kzalloc(size, GFP_KERNEL); | 
|---|
| 334 | if (!fence_info) | 
|---|
| 335 | return -ENOMEM; | 
|---|
| 336 |  | 
|---|
| 337 | num_fences = 0; | 
|---|
| 338 | dma_fence_unwrap_for_each(fence, &iter, sync_file->fence) { | 
|---|
| 339 | int status; | 
|---|
| 340 |  | 
|---|
| 341 | status = sync_fill_fence_info(fence, info: &fence_info[num_fences++]); | 
|---|
| 342 | info.status = info.status <= 0 ? info.status : status; | 
|---|
| 343 | } | 
|---|
| 344 |  | 
|---|
| 345 | if (copy_to_user(u64_to_user_ptr(info.sync_fence_info), from: fence_info, | 
|---|
| 346 | n: size)) { | 
|---|
| 347 | ret = -EFAULT; | 
|---|
| 348 | goto out; | 
|---|
| 349 | } | 
|---|
| 350 |  | 
|---|
| 351 | no_fences: | 
|---|
| 352 | sync_file_get_name(sync_file, buf: info.name, len: sizeof(info.name)); | 
|---|
| 353 | info.num_fences = num_fences; | 
|---|
| 354 |  | 
|---|
| 355 | if (copy_to_user(to: (void __user *)arg, from: &info, n: sizeof(info))) | 
|---|
| 356 | ret = -EFAULT; | 
|---|
| 357 | else | 
|---|
| 358 | ret = 0; | 
|---|
| 359 |  | 
|---|
| 360 | out: | 
|---|
| 361 | kfree(objp: fence_info); | 
|---|
| 362 |  | 
|---|
| 363 | return ret; | 
|---|
| 364 | } | 
|---|
| 365 |  | 
|---|
| 366 | static int sync_file_ioctl_set_deadline(struct sync_file *sync_file, | 
|---|
| 367 | unsigned long arg) | 
|---|
| 368 | { | 
|---|
| 369 | struct sync_set_deadline ts; | 
|---|
| 370 |  | 
|---|
| 371 | if (copy_from_user(to: &ts, from: (void __user *)arg, n: sizeof(ts))) | 
|---|
| 372 | return -EFAULT; | 
|---|
| 373 |  | 
|---|
| 374 | if (ts.pad) | 
|---|
| 375 | return -EINVAL; | 
|---|
| 376 |  | 
|---|
| 377 | dma_fence_set_deadline(fence: sync_file->fence, deadline: ns_to_ktime(ns: ts.deadline_ns)); | 
|---|
| 378 |  | 
|---|
| 379 | return 0; | 
|---|
| 380 | } | 
|---|
| 381 |  | 
|---|
| 382 | static long sync_file_ioctl(struct file *file, unsigned int cmd, | 
|---|
| 383 | unsigned long arg) | 
|---|
| 384 | { | 
|---|
| 385 | struct sync_file *sync_file = file->private_data; | 
|---|
| 386 |  | 
|---|
| 387 | switch (cmd) { | 
|---|
| 388 | case SYNC_IOC_MERGE: | 
|---|
| 389 | return sync_file_ioctl_merge(sync_file, arg); | 
|---|
| 390 |  | 
|---|
| 391 | case SYNC_IOC_FILE_INFO: | 
|---|
| 392 | return sync_file_ioctl_fence_info(sync_file, arg); | 
|---|
| 393 |  | 
|---|
| 394 | case SYNC_IOC_SET_DEADLINE: | 
|---|
| 395 | return sync_file_ioctl_set_deadline(sync_file, arg); | 
|---|
| 396 |  | 
|---|
| 397 | default: | 
|---|
| 398 | return -ENOTTY; | 
|---|
| 399 | } | 
|---|
| 400 | } | 
|---|
| 401 |  | 
|---|
| 402 | static const struct file_operations sync_file_fops = { | 
|---|
| 403 | .release = sync_file_release, | 
|---|
| 404 | .poll = sync_file_poll, | 
|---|
| 405 | .unlocked_ioctl = sync_file_ioctl, | 
|---|
| 406 | .compat_ioctl = compat_ptr_ioctl, | 
|---|
| 407 | }; | 
|---|
| 408 |  | 
|---|