13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 23d0407baSopenharmony_ci/* 33d0407baSopenharmony_ci * Framework for buffer objects that can be shared across devices/subsystems. 43d0407baSopenharmony_ci * 53d0407baSopenharmony_ci * Copyright(C) 2011 Linaro Limited. All rights reserved. 63d0407baSopenharmony_ci * Author: Sumit Semwal <sumit.semwal@ti.com> 73d0407baSopenharmony_ci * 83d0407baSopenharmony_ci * Many thanks to linaro-mm-sig list, and specially 93d0407baSopenharmony_ci * Arnd Bergmann <arnd@arndb.de>, Rob Clark <rob@ti.com> and 103d0407baSopenharmony_ci * Daniel Vetter <daniel@ffwll.ch> for their support in creation and 113d0407baSopenharmony_ci * refining of this idea. 123d0407baSopenharmony_ci */ 133d0407baSopenharmony_ci 143d0407baSopenharmony_ci#include <linux/fs.h> 153d0407baSopenharmony_ci#include <linux/slab.h> 163d0407baSopenharmony_ci#include <linux/dma-buf.h> 173d0407baSopenharmony_ci#include <linux/dma-fence.h> 183d0407baSopenharmony_ci#include <linux/anon_inodes.h> 193d0407baSopenharmony_ci#include <linux/export.h> 203d0407baSopenharmony_ci#include <linux/debugfs.h> 213d0407baSopenharmony_ci#include <linux/module.h> 223d0407baSopenharmony_ci#include <linux/seq_file.h> 233d0407baSopenharmony_ci#include <linux/poll.h> 243d0407baSopenharmony_ci#include <linux/dma-resv.h> 253d0407baSopenharmony_ci#include <linux/mm.h> 263d0407baSopenharmony_ci#include <linux/mount.h> 273d0407baSopenharmony_ci#include <linux/pseudo_fs.h> 283d0407baSopenharmony_ci 293d0407baSopenharmony_ci#include <uapi/linux/dma-buf.h> 303d0407baSopenharmony_ci#include <uapi/linux/magic.h> 313d0407baSopenharmony_ci 323d0407baSopenharmony_ci#include "dma-buf-sysfs-stats.h" 333d0407baSopenharmony_ci#include "dma-buf-process-info.h" 343d0407baSopenharmony_ci 353d0407baSopenharmony_cistruct dma_buf_list { 363d0407baSopenharmony_ci struct list_head head; 373d0407baSopenharmony_ci struct mutex lock; 383d0407baSopenharmony_ci}; 393d0407baSopenharmony_ci 403d0407baSopenharmony_cistatic struct dma_buf_list db_list; 413d0407baSopenharmony_ci 423d0407baSopenharmony_ci/* 433d0407baSopenharmony_ci * This function helps in traversing the db_list and calls the 443d0407baSopenharmony_ci * callback function which can extract required info out of each 453d0407baSopenharmony_ci * dmabuf. 463d0407baSopenharmony_ci */ 473d0407baSopenharmony_ciint get_each_dmabuf(int (*callback)(const struct dma_buf *dmabuf, void *private), void *private) 483d0407baSopenharmony_ci{ 493d0407baSopenharmony_ci struct dma_buf *buf; 503d0407baSopenharmony_ci int ret = mutex_lock_interruptible(&db_list.lock); 513d0407baSopenharmony_ci if (ret) { 523d0407baSopenharmony_ci return ret; 533d0407baSopenharmony_ci } 543d0407baSopenharmony_ci 553d0407baSopenharmony_ci list_for_each_entry(buf, &db_list.head, list_node) 563d0407baSopenharmony_ci { 573d0407baSopenharmony_ci ret = callback(buf, private); 583d0407baSopenharmony_ci if (ret) { 593d0407baSopenharmony_ci break; 603d0407baSopenharmony_ci } 613d0407baSopenharmony_ci } 623d0407baSopenharmony_ci mutex_unlock(&db_list.lock); 633d0407baSopenharmony_ci return ret; 643d0407baSopenharmony_ci} 653d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(get_each_dmabuf); 663d0407baSopenharmony_ci 673d0407baSopenharmony_cistatic char *dmabuffs_dname(struct dentry *dentry, char *buffer, int buflen) 683d0407baSopenharmony_ci{ 693d0407baSopenharmony_ci struct dma_buf *dmabuf; 703d0407baSopenharmony_ci char name[DMA_BUF_NAME_LEN]; 713d0407baSopenharmony_ci size_t ret = 0; 723d0407baSopenharmony_ci 733d0407baSopenharmony_ci dmabuf = dentry->d_fsdata; 743d0407baSopenharmony_ci spin_lock(&dmabuf->name_lock); 753d0407baSopenharmony_ci if (dmabuf->name) { 763d0407baSopenharmony_ci ret = strlcpy(name, dmabuf->name, DMA_BUF_NAME_LEN); 773d0407baSopenharmony_ci } 783d0407baSopenharmony_ci spin_unlock(&dmabuf->name_lock); 793d0407baSopenharmony_ci 803d0407baSopenharmony_ci return dynamic_dname(dentry, buffer, buflen, "/%s:%s", dentry->d_name.name, ret > 0 ? name : ""); 813d0407baSopenharmony_ci} 823d0407baSopenharmony_ci 833d0407baSopenharmony_cistatic void dma_buf_release(struct dentry *dentry) 843d0407baSopenharmony_ci{ 853d0407baSopenharmony_ci struct dma_buf *dmabuf; 863d0407baSopenharmony_ci 873d0407baSopenharmony_ci dmabuf = dentry->d_fsdata; 883d0407baSopenharmony_ci if (unlikely(!dmabuf)) { 893d0407baSopenharmony_ci return; 903d0407baSopenharmony_ci } 913d0407baSopenharmony_ci 923d0407baSopenharmony_ci BUG_ON(dmabuf->vmapping_counter); 933d0407baSopenharmony_ci 943d0407baSopenharmony_ci /* 953d0407baSopenharmony_ci * Any fences that a dma-buf poll can wait on should be signaled 963d0407baSopenharmony_ci * before releasing dma-buf. This is the responsibility of each 973d0407baSopenharmony_ci * driver that uses the reservation objects. 983d0407baSopenharmony_ci * 993d0407baSopenharmony_ci * If you hit this BUG() it means someone dropped their ref to the 1003d0407baSopenharmony_ci * dma-buf while still having pending operation to the buffer. 1013d0407baSopenharmony_ci */ 1023d0407baSopenharmony_ci BUG_ON(dmabuf->cb_shared.active || dmabuf->cb_excl.active); 1033d0407baSopenharmony_ci 1043d0407baSopenharmony_ci dmabuf->ops->release(dmabuf); 1053d0407baSopenharmony_ci 1063d0407baSopenharmony_ci if (dmabuf->resv == (struct dma_resv *)&dmabuf[1]) { 1073d0407baSopenharmony_ci dma_resv_fini(dmabuf->resv); 1083d0407baSopenharmony_ci } 1093d0407baSopenharmony_ci 1103d0407baSopenharmony_ci WARN_ON(!list_empty(&dmabuf->attachments)); 1113d0407baSopenharmony_ci dma_buf_stats_teardown(dmabuf); 1123d0407baSopenharmony_ci module_put(dmabuf->owner); 1133d0407baSopenharmony_ci kfree(dmabuf->name); 1143d0407baSopenharmony_ci kfree(dmabuf); 1153d0407baSopenharmony_ci} 1163d0407baSopenharmony_ci 1173d0407baSopenharmony_cistatic int dma_buf_file_release(struct inode *inode, struct file *file) 1183d0407baSopenharmony_ci{ 1193d0407baSopenharmony_ci struct dma_buf *dmabuf; 1203d0407baSopenharmony_ci 1213d0407baSopenharmony_ci if (!is_dma_buf_file(file)) { 1223d0407baSopenharmony_ci return -EINVAL; 1233d0407baSopenharmony_ci } 1243d0407baSopenharmony_ci 1253d0407baSopenharmony_ci dmabuf = file->private_data; 1263d0407baSopenharmony_ci 1273d0407baSopenharmony_ci mutex_lock(&db_list.lock); 1283d0407baSopenharmony_ci list_del(&dmabuf->list_node); 1293d0407baSopenharmony_ci mutex_unlock(&db_list.lock); 1303d0407baSopenharmony_ci 1313d0407baSopenharmony_ci return 0; 1323d0407baSopenharmony_ci} 1333d0407baSopenharmony_ci 1343d0407baSopenharmony_cistatic const struct dentry_operations dma_buf_dentry_ops = { 1353d0407baSopenharmony_ci .d_dname = dmabuffs_dname, 1363d0407baSopenharmony_ci .d_release = dma_buf_release, 1373d0407baSopenharmony_ci}; 1383d0407baSopenharmony_ci 1393d0407baSopenharmony_cistatic struct vfsmount *dma_buf_mnt; 1403d0407baSopenharmony_ci 1413d0407baSopenharmony_cistatic int dma_buf_fs_init_context(struct fs_context *fc) 1423d0407baSopenharmony_ci{ 1433d0407baSopenharmony_ci struct pseudo_fs_context *ctx; 1443d0407baSopenharmony_ci 1453d0407baSopenharmony_ci ctx = init_pseudo(fc, DMA_BUF_MAGIC); 1463d0407baSopenharmony_ci if (!ctx) { 1473d0407baSopenharmony_ci return -ENOMEM; 1483d0407baSopenharmony_ci } 1493d0407baSopenharmony_ci ctx->dops = &dma_buf_dentry_ops; 1503d0407baSopenharmony_ci return 0; 1513d0407baSopenharmony_ci} 1523d0407baSopenharmony_ci 1533d0407baSopenharmony_cistatic struct file_system_type dma_buf_fs_type = { 1543d0407baSopenharmony_ci .name = "dmabuf", 1553d0407baSopenharmony_ci .init_fs_context = dma_buf_fs_init_context, 1563d0407baSopenharmony_ci .kill_sb = kill_anon_super, 1573d0407baSopenharmony_ci}; 1583d0407baSopenharmony_ci 1593d0407baSopenharmony_ci#ifdef CONFIG_DMABUF_SYSFS_STATS 1603d0407baSopenharmony_cistatic void dma_buf_vma_open(struct vm_area_struct *vma) 1613d0407baSopenharmony_ci{ 1623d0407baSopenharmony_ci struct dma_buf *dmabuf = vma->vm_file->private_data; 1633d0407baSopenharmony_ci 1643d0407baSopenharmony_ci dmabuf->mmap_count++; 1653d0407baSopenharmony_ci /* call the heap provided vma open() op */ 1663d0407baSopenharmony_ci if (dmabuf->exp_vm_ops->open) { 1673d0407baSopenharmony_ci dmabuf->exp_vm_ops->open(vma); 1683d0407baSopenharmony_ci } 1693d0407baSopenharmony_ci} 1703d0407baSopenharmony_ci 1713d0407baSopenharmony_cistatic void dma_buf_vma_close(struct vm_area_struct *vma) 1723d0407baSopenharmony_ci{ 1733d0407baSopenharmony_ci struct dma_buf *dmabuf = vma->vm_file->private_data; 1743d0407baSopenharmony_ci 1753d0407baSopenharmony_ci if (dmabuf->mmap_count) { 1763d0407baSopenharmony_ci dmabuf->mmap_count--; 1773d0407baSopenharmony_ci } 1783d0407baSopenharmony_ci /* call the heap provided vma close() op */ 1793d0407baSopenharmony_ci if (dmabuf->exp_vm_ops->close) { 1803d0407baSopenharmony_ci dmabuf->exp_vm_ops->close(vma); 1813d0407baSopenharmony_ci } 1823d0407baSopenharmony_ci} 1833d0407baSopenharmony_ci 1843d0407baSopenharmony_cistatic int dma_buf_do_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) 1853d0407baSopenharmony_ci{ 1863d0407baSopenharmony_ci /* call this first because the exporter might override vma->vm_ops */ 1873d0407baSopenharmony_ci int ret = dmabuf->ops->mmap(dmabuf, vma); 1883d0407baSopenharmony_ci if (ret) { 1893d0407baSopenharmony_ci return ret; 1903d0407baSopenharmony_ci } 1913d0407baSopenharmony_ci 1923d0407baSopenharmony_ci /* save the exporter provided vm_ops */ 1933d0407baSopenharmony_ci dmabuf->exp_vm_ops = vma->vm_ops; 1943d0407baSopenharmony_ci dmabuf->vm_ops = *(dmabuf->exp_vm_ops); 1953d0407baSopenharmony_ci /* override open() and close() to provide buffer mmap count */ 1963d0407baSopenharmony_ci dmabuf->vm_ops.open = dma_buf_vma_open; 1973d0407baSopenharmony_ci dmabuf->vm_ops.close = dma_buf_vma_close; 1983d0407baSopenharmony_ci vma->vm_ops = &dmabuf->vm_ops; 1993d0407baSopenharmony_ci dmabuf->mmap_count++; 2003d0407baSopenharmony_ci 2013d0407baSopenharmony_ci return ret; 2023d0407baSopenharmony_ci} 2033d0407baSopenharmony_ci#else /* CONFIG_DMABUF_SYSFS_STATS */ 2043d0407baSopenharmony_cistatic int dma_buf_do_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) 2053d0407baSopenharmony_ci{ 2063d0407baSopenharmony_ci return dmabuf->ops->mmap(dmabuf, vma); 2073d0407baSopenharmony_ci} 2083d0407baSopenharmony_ci#endif /* CONFIG_DMABUF_SYSFS_STATS */ 2093d0407baSopenharmony_ci 2103d0407baSopenharmony_cistatic int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma) 2113d0407baSopenharmony_ci{ 2123d0407baSopenharmony_ci struct dma_buf *dmabuf; 2133d0407baSopenharmony_ci 2143d0407baSopenharmony_ci if (!is_dma_buf_file(file)) { 2153d0407baSopenharmony_ci return -EINVAL; 2163d0407baSopenharmony_ci } 2173d0407baSopenharmony_ci 2183d0407baSopenharmony_ci dmabuf = file->private_data; 2193d0407baSopenharmony_ci 2203d0407baSopenharmony_ci /* check if buffer supports mmap */ 2213d0407baSopenharmony_ci if (!dmabuf->ops->mmap) { 2223d0407baSopenharmony_ci return -EINVAL; 2233d0407baSopenharmony_ci } 2243d0407baSopenharmony_ci 2253d0407baSopenharmony_ci /* check for overflowing the buffer's size */ 2263d0407baSopenharmony_ci if ((vma->vm_pgoff + vma_pages(vma)) > (dmabuf->size >> PAGE_SHIFT)) { 2273d0407baSopenharmony_ci return -EINVAL; 2283d0407baSopenharmony_ci } 2293d0407baSopenharmony_ci 2303d0407baSopenharmony_ci return dma_buf_do_mmap(dmabuf, vma); 2313d0407baSopenharmony_ci} 2323d0407baSopenharmony_ci 2333d0407baSopenharmony_cistatic loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence) 2343d0407baSopenharmony_ci{ 2353d0407baSopenharmony_ci struct dma_buf *dmabuf; 2363d0407baSopenharmony_ci loff_t base; 2373d0407baSopenharmony_ci 2383d0407baSopenharmony_ci if (!is_dma_buf_file(file)) { 2393d0407baSopenharmony_ci return -EBADF; 2403d0407baSopenharmony_ci } 2413d0407baSopenharmony_ci 2423d0407baSopenharmony_ci dmabuf = file->private_data; 2433d0407baSopenharmony_ci 2443d0407baSopenharmony_ci /* only support discovering the end of the buffer, 2453d0407baSopenharmony_ci but also allow SEEK_SET to maintain the idiomatic 2463d0407baSopenharmony_ci SEEK_END(0), SEEK_CUR(0) pattern */ 2473d0407baSopenharmony_ci if (whence == SEEK_END) { 2483d0407baSopenharmony_ci base = dmabuf->size; 2493d0407baSopenharmony_ci } else if (whence == SEEK_SET) { 2503d0407baSopenharmony_ci base = 0; 2513d0407baSopenharmony_ci } else { 2523d0407baSopenharmony_ci return -EINVAL; 2533d0407baSopenharmony_ci } 2543d0407baSopenharmony_ci 2553d0407baSopenharmony_ci if (offset != 0) { 2563d0407baSopenharmony_ci return -EINVAL; 2573d0407baSopenharmony_ci } 2583d0407baSopenharmony_ci 2593d0407baSopenharmony_ci return base + offset; 2603d0407baSopenharmony_ci} 2613d0407baSopenharmony_ci 2623d0407baSopenharmony_ci/** 2633d0407baSopenharmony_ci * DOC: implicit fence polling 2643d0407baSopenharmony_ci * 2653d0407baSopenharmony_ci * To support cross-device and cross-driver synchronization of buffer access 2663d0407baSopenharmony_ci * implicit fences (represented internally in the kernel with &struct dma_fence) 2673d0407baSopenharmony_ci * can be attached to a &dma_buf. The glue for that and a few related things are 2683d0407baSopenharmony_ci * provided in the &dma_resv structure. 2693d0407baSopenharmony_ci * 2703d0407baSopenharmony_ci * Userspace can query the state of these implicitly tracked fences using poll() 2713d0407baSopenharmony_ci * and related system calls 2723d0407baSopenharmony_ci * 2733d0407baSopenharmony_ci * - Checking for EPOLLIN, i.e. read access, can be use to query the state of the 2743d0407baSopenharmony_ci * most recent write or exclusive fence. 2753d0407baSopenharmony_ci * 2763d0407baSopenharmony_ci * - Checking for EPOLLOUT, i.e. write access, can be used to query the state of 2773d0407baSopenharmony_ci * all attached fences, shared and exclusive ones. 2783d0407baSopenharmony_ci * 2793d0407baSopenharmony_ci * Note that this only signals the completion of the respective fences, i.e. the 2803d0407baSopenharmony_ci * DMA transfers are complete. Cache flushing and any other necessary 2813d0407baSopenharmony_ci * preparations before CPU access can begin still need to happen. 2823d0407baSopenharmony_ci */ 2833d0407baSopenharmony_ci 2843d0407baSopenharmony_cistatic void dma_buf_poll_cb(struct dma_fence *fence, struct dma_fence_cb *cb) 2853d0407baSopenharmony_ci{ 2863d0407baSopenharmony_ci struct dma_buf_poll_cb_t *dcb = (struct dma_buf_poll_cb_t *)cb; 2873d0407baSopenharmony_ci unsigned long flags; 2883d0407baSopenharmony_ci 2893d0407baSopenharmony_ci spin_lock_irqsave(&dcb->poll->lock, flags); 2903d0407baSopenharmony_ci wake_up_locked_poll(dcb->poll, dcb->active); 2913d0407baSopenharmony_ci dcb->active = 0; 2923d0407baSopenharmony_ci spin_unlock_irqrestore(&dcb->poll->lock, flags); 2933d0407baSopenharmony_ci} 2943d0407baSopenharmony_ci 2953d0407baSopenharmony_cistatic __poll_t dma_buf_poll(struct file *file, poll_table *poll) 2963d0407baSopenharmony_ci{ 2973d0407baSopenharmony_ci struct dma_buf *dmabuf; 2983d0407baSopenharmony_ci struct dma_resv *resv; 2993d0407baSopenharmony_ci struct dma_resv_list *fobj; 3003d0407baSopenharmony_ci struct dma_fence *fence_excl; 3013d0407baSopenharmony_ci __poll_t events; 3023d0407baSopenharmony_ci unsigned shared_count, seq; 3033d0407baSopenharmony_ci 3043d0407baSopenharmony_ci dmabuf = file->private_data; 3053d0407baSopenharmony_ci if (!dmabuf || !dmabuf->resv) { 3063d0407baSopenharmony_ci return EPOLLERR; 3073d0407baSopenharmony_ci } 3083d0407baSopenharmony_ci 3093d0407baSopenharmony_ci resv = dmabuf->resv; 3103d0407baSopenharmony_ci 3113d0407baSopenharmony_ci poll_wait(file, &dmabuf->poll, poll); 3123d0407baSopenharmony_ci 3133d0407baSopenharmony_ci events = poll_requested_events(poll) & (EPOLLIN | EPOLLOUT); 3143d0407baSopenharmony_ci if (!events) { 3153d0407baSopenharmony_ci return 0; 3163d0407baSopenharmony_ci } 3173d0407baSopenharmony_ci 3183d0407baSopenharmony_ci while (1) { 3193d0407baSopenharmony_ci seq = read_seqcount_begin(&resv->seq); 3203d0407baSopenharmony_ci rcu_read_lock(); 3213d0407baSopenharmony_ci 3223d0407baSopenharmony_ci fobj = rcu_dereference(resv->fence); 3233d0407baSopenharmony_ci if (fobj) { 3243d0407baSopenharmony_ci shared_count = fobj->shared_count; 3253d0407baSopenharmony_ci } else { 3263d0407baSopenharmony_ci shared_count = 0; 3273d0407baSopenharmony_ci } 3283d0407baSopenharmony_ci fence_excl = rcu_dereference(resv->fence_excl); 3293d0407baSopenharmony_ci if (read_seqcount_retry(&resv->seq, seq)) { 3303d0407baSopenharmony_ci rcu_read_unlock(); 3313d0407baSopenharmony_ci continue; 3323d0407baSopenharmony_ci } 3333d0407baSopenharmony_ci break; 3343d0407baSopenharmony_ci } 3353d0407baSopenharmony_ci if (fence_excl && (!(events & EPOLLOUT) || shared_count == 0)) { 3363d0407baSopenharmony_ci struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_excl; 3373d0407baSopenharmony_ci __poll_t pevents = EPOLLIN; 3383d0407baSopenharmony_ci 3393d0407baSopenharmony_ci if (shared_count == 0) { 3403d0407baSopenharmony_ci pevents |= EPOLLOUT; 3413d0407baSopenharmony_ci } 3423d0407baSopenharmony_ci 3433d0407baSopenharmony_ci spin_lock_irq(&dmabuf->poll.lock); 3443d0407baSopenharmony_ci if (dcb->active) { 3453d0407baSopenharmony_ci dcb->active |= pevents; 3463d0407baSopenharmony_ci events &= ~pevents; 3473d0407baSopenharmony_ci } else { 3483d0407baSopenharmony_ci dcb->active = pevents; 3493d0407baSopenharmony_ci } 3503d0407baSopenharmony_ci spin_unlock_irq(&dmabuf->poll.lock); 3513d0407baSopenharmony_ci 3523d0407baSopenharmony_ci if (events & pevents) { 3533d0407baSopenharmony_ci if (!dma_fence_get_rcu(fence_excl)) { 3543d0407baSopenharmony_ci /* force a recheck */ 3553d0407baSopenharmony_ci events &= ~pevents; 3563d0407baSopenharmony_ci dma_buf_poll_cb(NULL, &dcb->cb); 3573d0407baSopenharmony_ci } else if (!dma_fence_add_callback(fence_excl, &dcb->cb, dma_buf_poll_cb)) { 3583d0407baSopenharmony_ci events &= ~pevents; 3593d0407baSopenharmony_ci dma_fence_put(fence_excl); 3603d0407baSopenharmony_ci } else { 3613d0407baSopenharmony_ci /* 3623d0407baSopenharmony_ci * No callback queued, wake up any additional 3633d0407baSopenharmony_ci * waiters. 3643d0407baSopenharmony_ci */ 3653d0407baSopenharmony_ci dma_fence_put(fence_excl); 3663d0407baSopenharmony_ci dma_buf_poll_cb(NULL, &dcb->cb); 3673d0407baSopenharmony_ci } 3683d0407baSopenharmony_ci } 3693d0407baSopenharmony_ci } 3703d0407baSopenharmony_ci 3713d0407baSopenharmony_ci if ((events & EPOLLOUT) && shared_count > 0) { 3723d0407baSopenharmony_ci struct dma_buf_poll_cb_t *dcb = &dmabuf->cb_shared; 3733d0407baSopenharmony_ci int i; 3743d0407baSopenharmony_ci 3753d0407baSopenharmony_ci /* Only queue a new callback if no event has fired yet */ 3763d0407baSopenharmony_ci spin_lock_irq(&dmabuf->poll.lock); 3773d0407baSopenharmony_ci if (dcb->active) { 3783d0407baSopenharmony_ci events &= ~EPOLLOUT; 3793d0407baSopenharmony_ci } else { 3803d0407baSopenharmony_ci dcb->active = EPOLLOUT; 3813d0407baSopenharmony_ci } 3823d0407baSopenharmony_ci spin_unlock_irq(&dmabuf->poll.lock); 3833d0407baSopenharmony_ci 3843d0407baSopenharmony_ci if (!(events & EPOLLOUT)) { 3853d0407baSopenharmony_ci goto out; 3863d0407baSopenharmony_ci } 3873d0407baSopenharmony_ci 3883d0407baSopenharmony_ci for (i = 0; i < shared_count; ++i) { 3893d0407baSopenharmony_ci struct dma_fence *fence = rcu_dereference(fobj->shared[i]); 3903d0407baSopenharmony_ci 3913d0407baSopenharmony_ci if (!dma_fence_get_rcu(fence)) { 3923d0407baSopenharmony_ci /* 3933d0407baSopenharmony_ci * fence refcount dropped to zero, this means 3943d0407baSopenharmony_ci * that fobj has been freed 3953d0407baSopenharmony_ci * 3963d0407baSopenharmony_ci * call dma_buf_poll_cb and force a recheck! 3973d0407baSopenharmony_ci */ 3983d0407baSopenharmony_ci events &= ~EPOLLOUT; 3993d0407baSopenharmony_ci dma_buf_poll_cb(NULL, &dcb->cb); 4003d0407baSopenharmony_ci break; 4013d0407baSopenharmony_ci } 4023d0407baSopenharmony_ci if (!dma_fence_add_callback(fence, &dcb->cb, dma_buf_poll_cb)) { 4033d0407baSopenharmony_ci dma_fence_put(fence); 4043d0407baSopenharmony_ci events &= ~EPOLLOUT; 4053d0407baSopenharmony_ci break; 4063d0407baSopenharmony_ci } 4073d0407baSopenharmony_ci dma_fence_put(fence); 4083d0407baSopenharmony_ci } 4093d0407baSopenharmony_ci 4103d0407baSopenharmony_ci /* No callback queued, wake up any additional waiters. */ 4113d0407baSopenharmony_ci if (i == shared_count) { 4123d0407baSopenharmony_ci dma_buf_poll_cb(NULL, &dcb->cb); 4133d0407baSopenharmony_ci } 4143d0407baSopenharmony_ci } 4153d0407baSopenharmony_ci 4163d0407baSopenharmony_ciout: 4173d0407baSopenharmony_ci rcu_read_unlock(); 4183d0407baSopenharmony_ci return events; 4193d0407baSopenharmony_ci} 4203d0407baSopenharmony_ci 4213d0407baSopenharmony_ci/** 4223d0407baSopenharmony_ci * dma_buf_set_name - Set a name to a specific dma_buf to track the usage. 4233d0407baSopenharmony_ci * The name of the dma-buf buffer can only be set when the dma-buf is not 4243d0407baSopenharmony_ci * attached to any devices. It could theoritically support changing the 4253d0407baSopenharmony_ci * name of the dma-buf if the same piece of memory is used for multiple 4263d0407baSopenharmony_ci * purpose between different devices. 4273d0407baSopenharmony_ci * 4283d0407baSopenharmony_ci * @dmabuf: [in] dmabuf buffer that will be renamed. 4293d0407baSopenharmony_ci * @buf: [in] A piece of userspace memory that contains the name of 4303d0407baSopenharmony_ci * the dma-buf. 4313d0407baSopenharmony_ci * 4323d0407baSopenharmony_ci * Returns 0 on success. If the dma-buf buffer is already attached to 4333d0407baSopenharmony_ci * devices, return -EBUSY. 4343d0407baSopenharmony_ci * 4353d0407baSopenharmony_ci */ 4363d0407baSopenharmony_cistatic long dma_buf_set_name(struct dma_buf *dmabuf, const char __user *buf) 4373d0407baSopenharmony_ci{ 4383d0407baSopenharmony_ci char *name = strndup_user(buf, DMA_BUF_NAME_LEN); 4393d0407baSopenharmony_ci long ret = 0; 4403d0407baSopenharmony_ci 4413d0407baSopenharmony_ci if (IS_ERR(name)) { 4423d0407baSopenharmony_ci return PTR_ERR(name); 4433d0407baSopenharmony_ci } 4443d0407baSopenharmony_ci 4453d0407baSopenharmony_ci dma_resv_lock(dmabuf->resv, NULL); 4463d0407baSopenharmony_ci if (!list_empty(&dmabuf->attachments)) { 4473d0407baSopenharmony_ci ret = -EBUSY; 4483d0407baSopenharmony_ci kfree(name); 4493d0407baSopenharmony_ci goto out_unlock; 4503d0407baSopenharmony_ci } 4513d0407baSopenharmony_ci spin_lock(&dmabuf->name_lock); 4523d0407baSopenharmony_ci kfree(dmabuf->name); 4533d0407baSopenharmony_ci dmabuf->name = name; 4543d0407baSopenharmony_ci spin_unlock(&dmabuf->name_lock); 4553d0407baSopenharmony_ci 4563d0407baSopenharmony_ciout_unlock: 4573d0407baSopenharmony_ci dma_resv_unlock(dmabuf->resv); 4583d0407baSopenharmony_ci return ret; 4593d0407baSopenharmony_ci} 4603d0407baSopenharmony_ci 4613d0407baSopenharmony_cistatic long dma_buf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 4623d0407baSopenharmony_ci{ 4633d0407baSopenharmony_ci struct dma_buf *dmabuf; 4643d0407baSopenharmony_ci struct dma_buf_sync sync; 4653d0407baSopenharmony_ci enum dma_data_direction direction; 4663d0407baSopenharmony_ci int ret; 4673d0407baSopenharmony_ci 4683d0407baSopenharmony_ci dmabuf = file->private_data; 4693d0407baSopenharmony_ci 4703d0407baSopenharmony_ci switch (cmd) { 4713d0407baSopenharmony_ci case DMA_BUF_IOCTL_SYNC: 4723d0407baSopenharmony_ci if (copy_from_user(&sync, (void __user *)arg, sizeof(sync))) { 4733d0407baSopenharmony_ci return -EFAULT; 4743d0407baSopenharmony_ci } 4753d0407baSopenharmony_ci 4763d0407baSopenharmony_ci if (sync.flags & ~DMA_BUF_SYNC_VALID_FLAGS_MASK) { 4773d0407baSopenharmony_ci return -EINVAL; 4783d0407baSopenharmony_ci } 4793d0407baSopenharmony_ci 4803d0407baSopenharmony_ci switch (sync.flags & DMA_BUF_SYNC_RW) { 4813d0407baSopenharmony_ci case DMA_BUF_SYNC_READ: 4823d0407baSopenharmony_ci direction = DMA_FROM_DEVICE; 4833d0407baSopenharmony_ci break; 4843d0407baSopenharmony_ci case DMA_BUF_SYNC_WRITE: 4853d0407baSopenharmony_ci direction = DMA_TO_DEVICE; 4863d0407baSopenharmony_ci break; 4873d0407baSopenharmony_ci case DMA_BUF_SYNC_RW: 4883d0407baSopenharmony_ci direction = DMA_BIDIRECTIONAL; 4893d0407baSopenharmony_ci break; 4903d0407baSopenharmony_ci default: 4913d0407baSopenharmony_ci return -EINVAL; 4923d0407baSopenharmony_ci } 4933d0407baSopenharmony_ci 4943d0407baSopenharmony_ci if (sync.flags & DMA_BUF_SYNC_END) { 4953d0407baSopenharmony_ci ret = dma_buf_end_cpu_access(dmabuf, direction); 4963d0407baSopenharmony_ci } else { 4973d0407baSopenharmony_ci ret = dma_buf_begin_cpu_access(dmabuf, direction); 4983d0407baSopenharmony_ci } 4993d0407baSopenharmony_ci 5003d0407baSopenharmony_ci return ret; 5013d0407baSopenharmony_ci 5023d0407baSopenharmony_ci case DMA_BUF_SET_NAME_A: 5033d0407baSopenharmony_ci case DMA_BUF_SET_NAME_B: 5043d0407baSopenharmony_ci return dma_buf_set_name(dmabuf, (const char __user *)arg); 5053d0407baSopenharmony_ci 5063d0407baSopenharmony_ci default: 5073d0407baSopenharmony_ci return -ENOTTY; 5083d0407baSopenharmony_ci } 5093d0407baSopenharmony_ci} 5103d0407baSopenharmony_ci 5113d0407baSopenharmony_cistatic void dma_buf_show_fdinfo(struct seq_file *m, struct file *file) 5123d0407baSopenharmony_ci{ 5133d0407baSopenharmony_ci struct dma_buf *dmabuf = file->private_data; 5143d0407baSopenharmony_ci 5153d0407baSopenharmony_ci seq_printf(m, "size:\t%zu\n", dmabuf->size); 5163d0407baSopenharmony_ci /* Don't count the temporary reference taken inside procfs seq_show */ 5173d0407baSopenharmony_ci seq_printf(m, "count:\t%ld\n", file_count(dmabuf->file) - 1); 5183d0407baSopenharmony_ci seq_printf(m, "exp_name:\t%s\n", dmabuf->exp_name); 5193d0407baSopenharmony_ci spin_lock(&dmabuf->name_lock); 5203d0407baSopenharmony_ci if (dmabuf->name) { 5213d0407baSopenharmony_ci seq_printf(m, "name:\t%s\n", dmabuf->name); 5223d0407baSopenharmony_ci } 5233d0407baSopenharmony_ci spin_unlock(&dmabuf->name_lock); 5243d0407baSopenharmony_ci} 5253d0407baSopenharmony_ci 5263d0407baSopenharmony_cistatic const struct file_operations dma_buf_fops = { 5273d0407baSopenharmony_ci .release = dma_buf_file_release, 5283d0407baSopenharmony_ci .mmap = dma_buf_mmap_internal, 5293d0407baSopenharmony_ci .llseek = dma_buf_llseek, 5303d0407baSopenharmony_ci .poll = dma_buf_poll, 5313d0407baSopenharmony_ci .unlocked_ioctl = dma_buf_ioctl, 5323d0407baSopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 5333d0407baSopenharmony_ci .show_fdinfo = dma_buf_show_fdinfo, 5343d0407baSopenharmony_ci}; 5353d0407baSopenharmony_ci 5363d0407baSopenharmony_ci/* 5373d0407baSopenharmony_ci * is_dma_buf_file - Check if struct file* is associated with dma_buf 5383d0407baSopenharmony_ci */ 5393d0407baSopenharmony_ciint is_dma_buf_file(struct file *file) 5403d0407baSopenharmony_ci{ 5413d0407baSopenharmony_ci return file->f_op == &dma_buf_fops; 5423d0407baSopenharmony_ci} 5433d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(is_dma_buf_file); 5443d0407baSopenharmony_ci 5453d0407baSopenharmony_cistatic struct file *dma_buf_getfile(struct dma_buf *dmabuf, int flags) 5463d0407baSopenharmony_ci{ 5473d0407baSopenharmony_ci struct file *file; 5483d0407baSopenharmony_ci struct inode *inode = alloc_anon_inode(dma_buf_mnt->mnt_sb); 5493d0407baSopenharmony_ci 5503d0407baSopenharmony_ci if (IS_ERR(inode)) { 5513d0407baSopenharmony_ci return ERR_CAST(inode); 5523d0407baSopenharmony_ci } 5533d0407baSopenharmony_ci 5543d0407baSopenharmony_ci inode->i_size = dmabuf->size; 5553d0407baSopenharmony_ci inode_set_bytes(inode, dmabuf->size); 5563d0407baSopenharmony_ci 5573d0407baSopenharmony_ci file = alloc_file_pseudo(inode, dma_buf_mnt, "dmabuf", flags, &dma_buf_fops); 5583d0407baSopenharmony_ci if (IS_ERR(file)) { 5593d0407baSopenharmony_ci goto err_alloc_file; 5603d0407baSopenharmony_ci } 5613d0407baSopenharmony_ci file->f_flags = flags & (O_ACCMODE | O_NONBLOCK); 5623d0407baSopenharmony_ci file->private_data = dmabuf; 5633d0407baSopenharmony_ci file->f_path.dentry->d_fsdata = dmabuf; 5643d0407baSopenharmony_ci 5653d0407baSopenharmony_ci return file; 5663d0407baSopenharmony_ci 5673d0407baSopenharmony_cierr_alloc_file: 5683d0407baSopenharmony_ci iput(inode); 5693d0407baSopenharmony_ci return file; 5703d0407baSopenharmony_ci} 5713d0407baSopenharmony_ci 5723d0407baSopenharmony_ci/** 5733d0407baSopenharmony_ci * DOC: dma buf device access 5743d0407baSopenharmony_ci * 5753d0407baSopenharmony_ci * For device DMA access to a shared DMA buffer the usual sequence of operations 5763d0407baSopenharmony_ci * is fairly simple 5773d0407baSopenharmony_ci * 5783d0407baSopenharmony_ci * 1. The exporter defines his exporter instance using 5793d0407baSopenharmony_ci * DEFINE_DMA_BUF_EXPORT_INFO() and calls dma_buf_export() to wrap a private 5803d0407baSopenharmony_ci * buffer object into a &dma_buf. It then exports that &dma_buf to userspace 5813d0407baSopenharmony_ci * as a file descriptor by calling dma_buf_fd(). 5823d0407baSopenharmony_ci * 5833d0407baSopenharmony_ci * 2. Userspace passes this file-descriptors to all drivers it wants this buffer 5843d0407baSopenharmony_ci * to share with: First the filedescriptor is converted to a &dma_buf using 5853d0407baSopenharmony_ci * dma_buf_get(). Then the buffer is attached to the device using 5863d0407baSopenharmony_ci * dma_buf_attach(). 5873d0407baSopenharmony_ci * 5883d0407baSopenharmony_ci * Up to this stage the exporter is still free to migrate or reallocate the 5893d0407baSopenharmony_ci * backing storage. 5903d0407baSopenharmony_ci * 5913d0407baSopenharmony_ci * 3. Once the buffer is attached to all devices userspace can initiate DMA 5923d0407baSopenharmony_ci * access to the shared buffer. In the kernel this is done by calling 5933d0407baSopenharmony_ci * dma_buf_map_attachment() and dma_buf_unmap_attachment(). 5943d0407baSopenharmony_ci * 5953d0407baSopenharmony_ci * 4. Once a driver is done with a shared buffer it needs to call 5963d0407baSopenharmony_ci * dma_buf_detach() (after cleaning up any mappings) and then release the 5973d0407baSopenharmony_ci * reference acquired with dma_buf_get by calling dma_buf_put(). 5983d0407baSopenharmony_ci * 5993d0407baSopenharmony_ci * For the detailed semantics exporters are expected to implement see 6003d0407baSopenharmony_ci * &dma_buf_ops. 6013d0407baSopenharmony_ci */ 6023d0407baSopenharmony_ci 6033d0407baSopenharmony_ci/** 6043d0407baSopenharmony_ci * dma_buf_export - Creates a new dma_buf, and associates an anon file 6053d0407baSopenharmony_ci * with this buffer, so it can be exported. 6063d0407baSopenharmony_ci * Also connect the allocator specific data and ops to the buffer. 6073d0407baSopenharmony_ci * Additionally, provide a name string for exporter; useful in debugging. 6083d0407baSopenharmony_ci * 6093d0407baSopenharmony_ci * @exp_info: [in] holds all the export related information provided 6103d0407baSopenharmony_ci * by the exporter. see &struct dma_buf_export_info 6113d0407baSopenharmony_ci * for further details. 6123d0407baSopenharmony_ci * 6133d0407baSopenharmony_ci * Returns, on success, a newly created dma_buf object, which wraps the 6143d0407baSopenharmony_ci * supplied private data and operations for dma_buf_ops. On either missing 6153d0407baSopenharmony_ci * ops, or error in allocating struct dma_buf, will return negative error. 6163d0407baSopenharmony_ci * 6173d0407baSopenharmony_ci * For most cases the easiest way to create @exp_info is through the 6183d0407baSopenharmony_ci * %DEFINE_DMA_BUF_EXPORT_INFO macro. 6193d0407baSopenharmony_ci */ 6203d0407baSopenharmony_cistruct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info) 6213d0407baSopenharmony_ci{ 6223d0407baSopenharmony_ci struct dma_buf *dmabuf; 6233d0407baSopenharmony_ci struct dma_resv *resv = exp_info->resv; 6243d0407baSopenharmony_ci struct file *file; 6253d0407baSopenharmony_ci size_t alloc_size = sizeof(struct dma_buf); 6263d0407baSopenharmony_ci int ret; 6273d0407baSopenharmony_ci 6283d0407baSopenharmony_ci if (!exp_info->resv) { 6293d0407baSopenharmony_ci alloc_size += sizeof(struct dma_resv); 6303d0407baSopenharmony_ci } else { 6313d0407baSopenharmony_ci /* prevent &dma_buf[1] == dma_buf->resv */ 6323d0407baSopenharmony_ci alloc_size += 1; 6333d0407baSopenharmony_ci } 6343d0407baSopenharmony_ci 6353d0407baSopenharmony_ci if (WARN_ON(!exp_info->priv || !exp_info->ops || !exp_info->ops->map_dma_buf || !exp_info->ops->unmap_dma_buf || 6363d0407baSopenharmony_ci !exp_info->ops->release)) { 6373d0407baSopenharmony_ci return ERR_PTR(-EINVAL); 6383d0407baSopenharmony_ci } 6393d0407baSopenharmony_ci 6403d0407baSopenharmony_ci if (WARN_ON(exp_info->ops->cache_sgt_mapping && (exp_info->ops->pin || exp_info->ops->unpin))) { 6413d0407baSopenharmony_ci return ERR_PTR(-EINVAL); 6423d0407baSopenharmony_ci } 6433d0407baSopenharmony_ci 6443d0407baSopenharmony_ci if (WARN_ON(!exp_info->ops->pin != !exp_info->ops->unpin)) { 6453d0407baSopenharmony_ci return ERR_PTR(-EINVAL); 6463d0407baSopenharmony_ci } 6473d0407baSopenharmony_ci 6483d0407baSopenharmony_ci if (!try_module_get(exp_info->owner)) { 6493d0407baSopenharmony_ci return ERR_PTR(-ENOENT); 6503d0407baSopenharmony_ci } 6513d0407baSopenharmony_ci 6523d0407baSopenharmony_ci dmabuf = kzalloc(alloc_size, GFP_KERNEL); 6533d0407baSopenharmony_ci if (!dmabuf) { 6543d0407baSopenharmony_ci ret = -ENOMEM; 6553d0407baSopenharmony_ci goto err_module; 6563d0407baSopenharmony_ci } 6573d0407baSopenharmony_ci 6583d0407baSopenharmony_ci dmabuf->priv = exp_info->priv; 6593d0407baSopenharmony_ci dmabuf->ops = exp_info->ops; 6603d0407baSopenharmony_ci dmabuf->size = exp_info->size; 6613d0407baSopenharmony_ci dmabuf->exp_name = exp_info->exp_name; 6623d0407baSopenharmony_ci dmabuf->owner = exp_info->owner; 6633d0407baSopenharmony_ci spin_lock_init(&dmabuf->name_lock); 6643d0407baSopenharmony_ci init_waitqueue_head(&dmabuf->poll); 6653d0407baSopenharmony_ci dmabuf->cb_excl.poll = dmabuf->cb_shared.poll = &dmabuf->poll; 6663d0407baSopenharmony_ci dmabuf->cb_excl.active = dmabuf->cb_shared.active = 0; 6673d0407baSopenharmony_ci 6683d0407baSopenharmony_ci if (!resv) { 6693d0407baSopenharmony_ci resv = (struct dma_resv *)&dmabuf[1]; 6703d0407baSopenharmony_ci dma_resv_init(resv); 6713d0407baSopenharmony_ci } 6723d0407baSopenharmony_ci dmabuf->resv = resv; 6733d0407baSopenharmony_ci 6743d0407baSopenharmony_ci file = dma_buf_getfile(dmabuf, exp_info->flags); 6753d0407baSopenharmony_ci if (IS_ERR(file)) { 6763d0407baSopenharmony_ci ret = PTR_ERR(file); 6773d0407baSopenharmony_ci goto err_dmabuf; 6783d0407baSopenharmony_ci } 6793d0407baSopenharmony_ci 6803d0407baSopenharmony_ci file->f_mode |= FMODE_LSEEK; 6813d0407baSopenharmony_ci dmabuf->file = file; 6823d0407baSopenharmony_ci 6833d0407baSopenharmony_ci ret = dma_buf_stats_setup(dmabuf); 6843d0407baSopenharmony_ci if (ret) { 6853d0407baSopenharmony_ci goto err_sysfs; 6863d0407baSopenharmony_ci } 6873d0407baSopenharmony_ci 6883d0407baSopenharmony_ci mutex_init(&dmabuf->lock); 6893d0407baSopenharmony_ci INIT_LIST_HEAD(&dmabuf->attachments); 6903d0407baSopenharmony_ci 6913d0407baSopenharmony_ci mutex_lock(&db_list.lock); 6923d0407baSopenharmony_ci list_add(&dmabuf->list_node, &db_list.head); 6933d0407baSopenharmony_ci mutex_unlock(&db_list.lock); 6943d0407baSopenharmony_ci 6953d0407baSopenharmony_ci init_dma_buf_task_info(dmabuf); 6963d0407baSopenharmony_ci return dmabuf; 6973d0407baSopenharmony_ci 6983d0407baSopenharmony_cierr_sysfs: 6993d0407baSopenharmony_ci /* 7003d0407baSopenharmony_ci * Set file->f_path.dentry->d_fsdata to NULL so that when 7013d0407baSopenharmony_ci * dma_buf_release() gets invoked by dentry_ops, it exits 7023d0407baSopenharmony_ci * early before calling the release() dma_buf op. 7033d0407baSopenharmony_ci */ 7043d0407baSopenharmony_ci file->f_path.dentry->d_fsdata = NULL; 7053d0407baSopenharmony_ci fput(file); 7063d0407baSopenharmony_cierr_dmabuf: 7073d0407baSopenharmony_ci kfree(dmabuf); 7083d0407baSopenharmony_cierr_module: 7093d0407baSopenharmony_ci module_put(exp_info->owner); 7103d0407baSopenharmony_ci return ERR_PTR(ret); 7113d0407baSopenharmony_ci} 7123d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_export); 7133d0407baSopenharmony_ci 7143d0407baSopenharmony_ci/** 7153d0407baSopenharmony_ci * dma_buf_fd - returns a file descriptor for the given dma_buf 7163d0407baSopenharmony_ci * @dmabuf: [in] pointer to dma_buf for which fd is required. 7173d0407baSopenharmony_ci * @flags: [in] flags to give to fd 7183d0407baSopenharmony_ci * 7193d0407baSopenharmony_ci * On success, returns an associated 'fd'. Else, returns error. 7203d0407baSopenharmony_ci */ 7213d0407baSopenharmony_ciint dma_buf_fd(struct dma_buf *dmabuf, int flags) 7223d0407baSopenharmony_ci{ 7233d0407baSopenharmony_ci int fd; 7243d0407baSopenharmony_ci 7253d0407baSopenharmony_ci if (!dmabuf || !dmabuf->file) { 7263d0407baSopenharmony_ci return -EINVAL; 7273d0407baSopenharmony_ci } 7283d0407baSopenharmony_ci 7293d0407baSopenharmony_ci fd = get_unused_fd_flags(flags); 7303d0407baSopenharmony_ci if (fd < 0) { 7313d0407baSopenharmony_ci return fd; 7323d0407baSopenharmony_ci } 7333d0407baSopenharmony_ci 7343d0407baSopenharmony_ci fd_install(fd, dmabuf->file); 7353d0407baSopenharmony_ci 7363d0407baSopenharmony_ci return fd; 7373d0407baSopenharmony_ci} 7383d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_fd); 7393d0407baSopenharmony_ci 7403d0407baSopenharmony_ci/** 7413d0407baSopenharmony_ci * dma_buf_get - returns the dma_buf structure related to an fd 7423d0407baSopenharmony_ci * @fd: [in] fd associated with the dma_buf to be returned 7433d0407baSopenharmony_ci * 7443d0407baSopenharmony_ci * On success, returns the dma_buf structure associated with an fd; uses 7453d0407baSopenharmony_ci * file's refcounting done by fget to increase refcount. returns ERR_PTR 7463d0407baSopenharmony_ci * otherwise. 7473d0407baSopenharmony_ci */ 7483d0407baSopenharmony_cistruct dma_buf *dma_buf_get(int fd) 7493d0407baSopenharmony_ci{ 7503d0407baSopenharmony_ci struct file *file; 7513d0407baSopenharmony_ci 7523d0407baSopenharmony_ci file = fget(fd); 7533d0407baSopenharmony_ci if (!file) { 7543d0407baSopenharmony_ci return ERR_PTR(-EBADF); 7553d0407baSopenharmony_ci } 7563d0407baSopenharmony_ci 7573d0407baSopenharmony_ci if (!is_dma_buf_file(file)) { 7583d0407baSopenharmony_ci fput(file); 7593d0407baSopenharmony_ci return ERR_PTR(-EINVAL); 7603d0407baSopenharmony_ci } 7613d0407baSopenharmony_ci 7623d0407baSopenharmony_ci return file->private_data; 7633d0407baSopenharmony_ci} 7643d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_get); 7653d0407baSopenharmony_ci 7663d0407baSopenharmony_ci/** 7673d0407baSopenharmony_ci * dma_buf_put - decreases refcount of the buffer 7683d0407baSopenharmony_ci * @dmabuf: [in] buffer to reduce refcount of 7693d0407baSopenharmony_ci * 7703d0407baSopenharmony_ci * Uses file's refcounting done implicitly by fput(). 7713d0407baSopenharmony_ci * 7723d0407baSopenharmony_ci * If, as a result of this call, the refcount becomes 0, the 'release' file 7733d0407baSopenharmony_ci * operation related to this fd is called. It calls &dma_buf_ops.release vfunc 7743d0407baSopenharmony_ci * in turn, and frees the memory allocated for dmabuf when exported. 7753d0407baSopenharmony_ci */ 7763d0407baSopenharmony_civoid dma_buf_put(struct dma_buf *dmabuf) 7773d0407baSopenharmony_ci{ 7783d0407baSopenharmony_ci if (WARN_ON(!dmabuf || !dmabuf->file)) { 7793d0407baSopenharmony_ci return; 7803d0407baSopenharmony_ci } 7813d0407baSopenharmony_ci 7823d0407baSopenharmony_ci fput(dmabuf->file); 7833d0407baSopenharmony_ci} 7843d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_put); 7853d0407baSopenharmony_ci 7863d0407baSopenharmony_ci/** 7873d0407baSopenharmony_ci * dma_buf_pin - Lock down the DMA-buf 7883d0407baSopenharmony_ci * 7893d0407baSopenharmony_ci * @attach: [in] attachment which should be pinned 7903d0407baSopenharmony_ci * 7913d0407baSopenharmony_ci * Returns: 7923d0407baSopenharmony_ci * 0 on success, negative error code on failure. 7933d0407baSopenharmony_ci */ 7943d0407baSopenharmony_ciint dma_buf_pin(struct dma_buf_attachment *attach) 7953d0407baSopenharmony_ci{ 7963d0407baSopenharmony_ci struct dma_buf *dmabuf = attach->dmabuf; 7973d0407baSopenharmony_ci int ret = 0; 7983d0407baSopenharmony_ci 7993d0407baSopenharmony_ci dma_resv_assert_held(dmabuf->resv); 8003d0407baSopenharmony_ci 8013d0407baSopenharmony_ci if (dmabuf->ops->pin) { 8023d0407baSopenharmony_ci ret = dmabuf->ops->pin(attach); 8033d0407baSopenharmony_ci } 8043d0407baSopenharmony_ci 8053d0407baSopenharmony_ci return ret; 8063d0407baSopenharmony_ci} 8073d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_pin); 8083d0407baSopenharmony_ci 8093d0407baSopenharmony_ci/** 8103d0407baSopenharmony_ci * dma_buf_dynamic_attach - Add the device to dma_buf's attachments list; optionally, 8113d0407baSopenharmony_ci * calls attach() of dma_buf_ops to allow device-specific attach functionality 8123d0407baSopenharmony_ci * @dmabuf: [in] buffer to attach device to. 8133d0407baSopenharmony_ci * @dev: [in] device to be attached. 8143d0407baSopenharmony_ci * @importer_ops: [in] importer operations for the attachment 8153d0407baSopenharmony_ci * @importer_priv: [in] importer private pointer for the attachment 8163d0407baSopenharmony_ci * 8173d0407baSopenharmony_ci * Returns struct dma_buf_attachment pointer for this attachment. Attachments 8183d0407baSopenharmony_ci * must be cleaned up by calling dma_buf_detach(). 8193d0407baSopenharmony_ci * 8203d0407baSopenharmony_ci * Returns 8213d0407baSopenharmony_ci * 8223d0407baSopenharmony_ci * A pointer to newly created &dma_buf_attachment on success, or a negative 8233d0407baSopenharmony_ci * error code wrapped into a pointer on failure. 8243d0407baSopenharmony_ci * 8253d0407baSopenharmony_ci * Note that this can fail if the backing storage of @dmabuf is in a place not 8263d0407baSopenharmony_ci * accessible to @dev, and cannot be moved to a more suitable place. This is 8273d0407baSopenharmony_ci * indicated with the error code -EBUSY. 8283d0407baSopenharmony_ci */ 8293d0407baSopenharmony_cistruct dma_buf_attachment *dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev, 8303d0407baSopenharmony_ci const struct dma_buf_attach_ops *importer_ops, void *importer_priv) 8313d0407baSopenharmony_ci{ 8323d0407baSopenharmony_ci struct dma_buf_attachment *attach; 8333d0407baSopenharmony_ci int ret; 8343d0407baSopenharmony_ci 8353d0407baSopenharmony_ci if (WARN_ON(!dmabuf || !dev)) { 8363d0407baSopenharmony_ci return ERR_PTR(-EINVAL); 8373d0407baSopenharmony_ci } 8383d0407baSopenharmony_ci 8393d0407baSopenharmony_ci if (WARN_ON(importer_ops && !importer_ops->move_notify)) { 8403d0407baSopenharmony_ci return ERR_PTR(-EINVAL); 8413d0407baSopenharmony_ci } 8423d0407baSopenharmony_ci 8433d0407baSopenharmony_ci attach = kzalloc(sizeof(*attach), GFP_KERNEL); 8443d0407baSopenharmony_ci if (!attach) { 8453d0407baSopenharmony_ci return ERR_PTR(-ENOMEM); 8463d0407baSopenharmony_ci } 8473d0407baSopenharmony_ci 8483d0407baSopenharmony_ci attach->dev = dev; 8493d0407baSopenharmony_ci attach->dmabuf = dmabuf; 8503d0407baSopenharmony_ci if (importer_ops) { 8513d0407baSopenharmony_ci attach->peer2peer = importer_ops->allow_peer2peer; 8523d0407baSopenharmony_ci } 8533d0407baSopenharmony_ci attach->importer_ops = importer_ops; 8543d0407baSopenharmony_ci attach->importer_priv = importer_priv; 8553d0407baSopenharmony_ci 8563d0407baSopenharmony_ci if (dmabuf->ops->attach) { 8573d0407baSopenharmony_ci ret = dmabuf->ops->attach(dmabuf, attach); 8583d0407baSopenharmony_ci if (ret) { 8593d0407baSopenharmony_ci goto err_attach; 8603d0407baSopenharmony_ci } 8613d0407baSopenharmony_ci } 8623d0407baSopenharmony_ci dma_resv_lock(dmabuf->resv, NULL); 8633d0407baSopenharmony_ci list_add(&attach->node, &dmabuf->attachments); 8643d0407baSopenharmony_ci dma_resv_unlock(dmabuf->resv); 8653d0407baSopenharmony_ci 8663d0407baSopenharmony_ci /* When either the importer or the exporter can't handle dynamic 8673d0407baSopenharmony_ci * mappings we cache the mapping here to avoid issues with the 8683d0407baSopenharmony_ci * reservation object lock. 8693d0407baSopenharmony_ci */ 8703d0407baSopenharmony_ci if (dma_buf_attachment_is_dynamic(attach) != dma_buf_is_dynamic(dmabuf)) { 8713d0407baSopenharmony_ci struct sg_table *sgt; 8723d0407baSopenharmony_ci 8733d0407baSopenharmony_ci if (dma_buf_is_dynamic(attach->dmabuf)) { 8743d0407baSopenharmony_ci dma_resv_lock(attach->dmabuf->resv, NULL); 8753d0407baSopenharmony_ci ret = dma_buf_pin(attach); 8763d0407baSopenharmony_ci if (ret) { 8773d0407baSopenharmony_ci goto err_unlock; 8783d0407baSopenharmony_ci } 8793d0407baSopenharmony_ci } 8803d0407baSopenharmony_ci 8813d0407baSopenharmony_ci sgt = dmabuf->ops->map_dma_buf(attach, DMA_BIDIRECTIONAL); 8823d0407baSopenharmony_ci if (!sgt) { 8833d0407baSopenharmony_ci sgt = ERR_PTR(-ENOMEM); 8843d0407baSopenharmony_ci } 8853d0407baSopenharmony_ci if (IS_ERR(sgt)) { 8863d0407baSopenharmony_ci ret = PTR_ERR(sgt); 8873d0407baSopenharmony_ci goto err_unpin; 8883d0407baSopenharmony_ci } 8893d0407baSopenharmony_ci if (dma_buf_is_dynamic(attach->dmabuf)) { 8903d0407baSopenharmony_ci dma_resv_unlock(attach->dmabuf->resv); 8913d0407baSopenharmony_ci } 8923d0407baSopenharmony_ci attach->sgt = sgt; 8933d0407baSopenharmony_ci attach->dir = DMA_BIDIRECTIONAL; 8943d0407baSopenharmony_ci } 8953d0407baSopenharmony_ci 8963d0407baSopenharmony_ci return attach; 8973d0407baSopenharmony_ci 8983d0407baSopenharmony_cierr_attach: 8993d0407baSopenharmony_ci kfree(attach); 9003d0407baSopenharmony_ci return ERR_PTR(ret); 9013d0407baSopenharmony_ci 9023d0407baSopenharmony_cierr_unpin: 9033d0407baSopenharmony_ci if (dma_buf_is_dynamic(attach->dmabuf)) { 9043d0407baSopenharmony_ci dma_buf_unpin(attach); 9053d0407baSopenharmony_ci } 9063d0407baSopenharmony_ci 9073d0407baSopenharmony_cierr_unlock: 9083d0407baSopenharmony_ci if (dma_buf_is_dynamic(attach->dmabuf)) { 9093d0407baSopenharmony_ci dma_resv_unlock(attach->dmabuf->resv); 9103d0407baSopenharmony_ci } 9113d0407baSopenharmony_ci 9123d0407baSopenharmony_ci dma_buf_detach(dmabuf, attach); 9133d0407baSopenharmony_ci return ERR_PTR(ret); 9143d0407baSopenharmony_ci} 9153d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_dynamic_attach); 9163d0407baSopenharmony_ci 9173d0407baSopenharmony_ci/** 9183d0407baSopenharmony_ci * dma_buf_attach - Wrapper for dma_buf_dynamic_attach 9193d0407baSopenharmony_ci * @dmabuf: [in] buffer to attach device to. 9203d0407baSopenharmony_ci * @dev: [in] device to be attached. 9213d0407baSopenharmony_ci * 9223d0407baSopenharmony_ci * Wrapper to call dma_buf_dynamic_attach() for drivers which still use a static 9233d0407baSopenharmony_ci * mapping. 9243d0407baSopenharmony_ci */ 9253d0407baSopenharmony_cistruct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf, struct device *dev) 9263d0407baSopenharmony_ci{ 9273d0407baSopenharmony_ci return dma_buf_dynamic_attach(dmabuf, dev, NULL, NULL); 9283d0407baSopenharmony_ci} 9293d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_attach); 9303d0407baSopenharmony_ci 9313d0407baSopenharmony_ci/** 9323d0407baSopenharmony_ci * dma_buf_detach - Remove the given attachment from dmabuf's attachments list; 9333d0407baSopenharmony_ci * optionally calls detach() of dma_buf_ops for device-specific detach 9343d0407baSopenharmony_ci * @dmabuf: [in] buffer to detach from. 9353d0407baSopenharmony_ci * @attach: [in] attachment to be detached; is free'd after this call. 9363d0407baSopenharmony_ci * 9373d0407baSopenharmony_ci * Clean up a device attachment obtained by calling dma_buf_attach(). 9383d0407baSopenharmony_ci */ 9393d0407baSopenharmony_civoid dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) 9403d0407baSopenharmony_ci{ 9413d0407baSopenharmony_ci if (WARN_ON(!dmabuf || !attach)) { 9423d0407baSopenharmony_ci return; 9433d0407baSopenharmony_ci } 9443d0407baSopenharmony_ci 9453d0407baSopenharmony_ci if (attach->sgt) { 9463d0407baSopenharmony_ci if (dma_buf_is_dynamic(attach->dmabuf)) { 9473d0407baSopenharmony_ci dma_resv_lock(attach->dmabuf->resv, NULL); 9483d0407baSopenharmony_ci } 9493d0407baSopenharmony_ci 9503d0407baSopenharmony_ci dmabuf->ops->unmap_dma_buf(attach, attach->sgt, attach->dir); 9513d0407baSopenharmony_ci 9523d0407baSopenharmony_ci if (dma_buf_is_dynamic(attach->dmabuf)) { 9533d0407baSopenharmony_ci dma_buf_unpin(attach); 9543d0407baSopenharmony_ci dma_resv_unlock(attach->dmabuf->resv); 9553d0407baSopenharmony_ci } 9563d0407baSopenharmony_ci } 9573d0407baSopenharmony_ci 9583d0407baSopenharmony_ci dma_resv_lock(dmabuf->resv, NULL); 9593d0407baSopenharmony_ci list_del(&attach->node); 9603d0407baSopenharmony_ci dma_resv_unlock(dmabuf->resv); 9613d0407baSopenharmony_ci if (dmabuf->ops->detach) { 9623d0407baSopenharmony_ci dmabuf->ops->detach(dmabuf, attach); 9633d0407baSopenharmony_ci } 9643d0407baSopenharmony_ci 9653d0407baSopenharmony_ci kfree(attach); 9663d0407baSopenharmony_ci} 9673d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_detach); 9683d0407baSopenharmony_ci 9693d0407baSopenharmony_ci/** 9703d0407baSopenharmony_ci * dma_buf_unpin - Remove lock from DMA-buf 9713d0407baSopenharmony_ci * 9723d0407baSopenharmony_ci * @attach: [in] attachment which should be unpinned 9733d0407baSopenharmony_ci */ 9743d0407baSopenharmony_civoid dma_buf_unpin(struct dma_buf_attachment *attach) 9753d0407baSopenharmony_ci{ 9763d0407baSopenharmony_ci struct dma_buf *dmabuf = attach->dmabuf; 9773d0407baSopenharmony_ci 9783d0407baSopenharmony_ci dma_resv_assert_held(dmabuf->resv); 9793d0407baSopenharmony_ci 9803d0407baSopenharmony_ci if (dmabuf->ops->unpin) { 9813d0407baSopenharmony_ci dmabuf->ops->unpin(attach); 9823d0407baSopenharmony_ci } 9833d0407baSopenharmony_ci} 9843d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_unpin); 9853d0407baSopenharmony_ci 9863d0407baSopenharmony_ci/** 9873d0407baSopenharmony_ci * dma_buf_map_attachment - Returns the scatterlist table of the attachment; 9883d0407baSopenharmony_ci * mapped into _device_ address space. Is a wrapper for map_dma_buf() of the 9893d0407baSopenharmony_ci * dma_buf_ops. 9903d0407baSopenharmony_ci * @attach: [in] attachment whose scatterlist is to be returned 9913d0407baSopenharmony_ci * @direction: [in] direction of DMA transfer 9923d0407baSopenharmony_ci * 9933d0407baSopenharmony_ci * Returns sg_table containing the scatterlist to be returned; returns ERR_PTR 9943d0407baSopenharmony_ci * on error. May return -EINTR if it is interrupted by a signal. 9953d0407baSopenharmony_ci * 9963d0407baSopenharmony_ci * A mapping must be unmapped by using dma_buf_unmap_attachment(). Note that 9973d0407baSopenharmony_ci * the underlying backing storage is pinned for as long as a mapping exists, 9983d0407baSopenharmony_ci * therefore users/importers should not hold onto a mapping for undue amounts of 9993d0407baSopenharmony_ci * time. 10003d0407baSopenharmony_ci */ 10013d0407baSopenharmony_cistruct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, enum dma_data_direction direction) 10023d0407baSopenharmony_ci{ 10033d0407baSopenharmony_ci struct sg_table *sg_table; 10043d0407baSopenharmony_ci int r; 10053d0407baSopenharmony_ci 10063d0407baSopenharmony_ci might_sleep(); 10073d0407baSopenharmony_ci 10083d0407baSopenharmony_ci if (WARN_ON(!attach || !attach->dmabuf)) { 10093d0407baSopenharmony_ci return ERR_PTR(-EINVAL); 10103d0407baSopenharmony_ci } 10113d0407baSopenharmony_ci 10123d0407baSopenharmony_ci if (dma_buf_attachment_is_dynamic(attach)) { 10133d0407baSopenharmony_ci dma_resv_assert_held(attach->dmabuf->resv); 10143d0407baSopenharmony_ci } 10153d0407baSopenharmony_ci 10163d0407baSopenharmony_ci if (attach->sgt) { 10173d0407baSopenharmony_ci /* 10183d0407baSopenharmony_ci * Two mappings with different directions for the same 10193d0407baSopenharmony_ci * attachment are not allowed. 10203d0407baSopenharmony_ci */ 10213d0407baSopenharmony_ci if (attach->dir != direction && attach->dir != DMA_BIDIRECTIONAL) { 10223d0407baSopenharmony_ci return ERR_PTR(-EBUSY); 10233d0407baSopenharmony_ci } 10243d0407baSopenharmony_ci 10253d0407baSopenharmony_ci return attach->sgt; 10263d0407baSopenharmony_ci } 10273d0407baSopenharmony_ci 10283d0407baSopenharmony_ci if (dma_buf_is_dynamic(attach->dmabuf)) { 10293d0407baSopenharmony_ci dma_resv_assert_held(attach->dmabuf->resv); 10303d0407baSopenharmony_ci if (!IS_ENABLED(CONFIG_DMABUF_MOVE_NOTIFY)) { 10313d0407baSopenharmony_ci r = dma_buf_pin(attach); 10323d0407baSopenharmony_ci if (r) { 10333d0407baSopenharmony_ci return ERR_PTR(r); 10343d0407baSopenharmony_ci } 10353d0407baSopenharmony_ci } 10363d0407baSopenharmony_ci } 10373d0407baSopenharmony_ci 10383d0407baSopenharmony_ci sg_table = attach->dmabuf->ops->map_dma_buf(attach, direction); 10393d0407baSopenharmony_ci if (!sg_table) { 10403d0407baSopenharmony_ci sg_table = ERR_PTR(-ENOMEM); 10413d0407baSopenharmony_ci } 10423d0407baSopenharmony_ci 10433d0407baSopenharmony_ci if (IS_ERR(sg_table) && dma_buf_is_dynamic(attach->dmabuf) && !IS_ENABLED(CONFIG_DMABUF_MOVE_NOTIFY)) { 10443d0407baSopenharmony_ci dma_buf_unpin(attach); 10453d0407baSopenharmony_ci } 10463d0407baSopenharmony_ci 10473d0407baSopenharmony_ci if (!IS_ERR(sg_table) && attach->dmabuf->ops->cache_sgt_mapping) { 10483d0407baSopenharmony_ci attach->sgt = sg_table; 10493d0407baSopenharmony_ci attach->dir = direction; 10503d0407baSopenharmony_ci } 10513d0407baSopenharmony_ci 10523d0407baSopenharmony_ci return sg_table; 10533d0407baSopenharmony_ci} 10543d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_map_attachment); 10553d0407baSopenharmony_ci 10563d0407baSopenharmony_ci/** 10573d0407baSopenharmony_ci * dma_buf_unmap_attachment - unmaps and decreases usecount of the buffer;might 10583d0407baSopenharmony_ci * deallocate the scatterlist associated. Is a wrapper for unmap_dma_buf() of 10593d0407baSopenharmony_ci * dma_buf_ops. 10603d0407baSopenharmony_ci * @attach: [in] attachment to unmap buffer from 10613d0407baSopenharmony_ci * @sg_table: [in] scatterlist info of the buffer to unmap 10623d0407baSopenharmony_ci * @direction: [in] direction of DMA transfer 10633d0407baSopenharmony_ci * 10643d0407baSopenharmony_ci * This unmaps a DMA mapping for @attached obtained by dma_buf_map_attachment(). 10653d0407baSopenharmony_ci */ 10663d0407baSopenharmony_civoid dma_buf_unmap_attachment(struct dma_buf_attachment *attach, struct sg_table *sg_table, 10673d0407baSopenharmony_ci enum dma_data_direction direction) 10683d0407baSopenharmony_ci{ 10693d0407baSopenharmony_ci might_sleep(); 10703d0407baSopenharmony_ci 10713d0407baSopenharmony_ci if (WARN_ON(!attach || !attach->dmabuf || !sg_table)) { 10723d0407baSopenharmony_ci return; 10733d0407baSopenharmony_ci } 10743d0407baSopenharmony_ci 10753d0407baSopenharmony_ci if (dma_buf_attachment_is_dynamic(attach)) { 10763d0407baSopenharmony_ci dma_resv_assert_held(attach->dmabuf->resv); 10773d0407baSopenharmony_ci } 10783d0407baSopenharmony_ci 10793d0407baSopenharmony_ci if (attach->sgt == sg_table) { 10803d0407baSopenharmony_ci return; 10813d0407baSopenharmony_ci } 10823d0407baSopenharmony_ci 10833d0407baSopenharmony_ci if (dma_buf_is_dynamic(attach->dmabuf)) { 10843d0407baSopenharmony_ci dma_resv_assert_held(attach->dmabuf->resv); 10853d0407baSopenharmony_ci } 10863d0407baSopenharmony_ci 10873d0407baSopenharmony_ci attach->dmabuf->ops->unmap_dma_buf(attach, sg_table, direction); 10883d0407baSopenharmony_ci 10893d0407baSopenharmony_ci if (dma_buf_is_dynamic(attach->dmabuf) && !IS_ENABLED(CONFIG_DMABUF_MOVE_NOTIFY)) { 10903d0407baSopenharmony_ci dma_buf_unpin(attach); 10913d0407baSopenharmony_ci } 10923d0407baSopenharmony_ci} 10933d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_unmap_attachment); 10943d0407baSopenharmony_ci 10953d0407baSopenharmony_ci/** 10963d0407baSopenharmony_ci * dma_buf_move_notify - notify attachments that DMA-buf is moving 10973d0407baSopenharmony_ci * 10983d0407baSopenharmony_ci * @dmabuf: [in] buffer which is moving 10993d0407baSopenharmony_ci * 11003d0407baSopenharmony_ci * Informs all attachmenst that they need to destroy and recreated all their 11013d0407baSopenharmony_ci * mappings. 11023d0407baSopenharmony_ci */ 11033d0407baSopenharmony_civoid dma_buf_move_notify(struct dma_buf *dmabuf) 11043d0407baSopenharmony_ci{ 11053d0407baSopenharmony_ci struct dma_buf_attachment *attach; 11063d0407baSopenharmony_ci 11073d0407baSopenharmony_ci dma_resv_assert_held(dmabuf->resv); 11083d0407baSopenharmony_ci 11093d0407baSopenharmony_ci list_for_each_entry(attach, &dmabuf->attachments, node) if (attach->importer_ops) 11103d0407baSopenharmony_ci attach->importer_ops->move_notify(attach); 11113d0407baSopenharmony_ci} 11123d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_move_notify); 11133d0407baSopenharmony_ci 11143d0407baSopenharmony_ci/** 11153d0407baSopenharmony_ci * DOC: cpu access 11163d0407baSopenharmony_ci * 11173d0407baSopenharmony_ci * There are mutliple reasons for supporting CPU access to a dma buffer object: 11183d0407baSopenharmony_ci * 11193d0407baSopenharmony_ci * - Fallback operations in the kernel, for example when a device is connected 11203d0407baSopenharmony_ci * over USB and the kernel needs to shuffle the data around first before 11213d0407baSopenharmony_ci * sending it away. Cache coherency is handled by braketing any transactions 11223d0407baSopenharmony_ci * with calls to dma_buf_begin_cpu_access() and dma_buf_end_cpu_access() 11233d0407baSopenharmony_ci * access. 11243d0407baSopenharmony_ci * 11253d0407baSopenharmony_ci * Since for most kernel internal dma-buf accesses need the entire buffer, a 11263d0407baSopenharmony_ci * vmap interface is introduced. Note that on very old 32-bit architectures 11273d0407baSopenharmony_ci * vmalloc space might be limited and result in vmap calls failing. 11283d0407baSopenharmony_ci * 11293d0407baSopenharmony_ci * Interfaces:: 11303d0407baSopenharmony_ci * void \*dma_buf_vmap(struct dma_buf \*dmabuf) 11313d0407baSopenharmony_ci * void dma_buf_vunmap(struct dma_buf \*dmabuf, void \*vaddr) 11323d0407baSopenharmony_ci * 11333d0407baSopenharmony_ci * The vmap call can fail if there is no vmap support in the exporter, or if 11343d0407baSopenharmony_ci * it runs out of vmalloc space. Fallback to kmap should be implemented. Note 11353d0407baSopenharmony_ci * that the dma-buf layer keeps a reference count for all vmap access and 11363d0407baSopenharmony_ci * calls down into the exporter's vmap function only when no vmapping exists, 11373d0407baSopenharmony_ci * and only unmaps it once. Protection against concurrent vmap/vunmap calls is 11383d0407baSopenharmony_ci * provided by taking the dma_buf->lock mutex. 11393d0407baSopenharmony_ci * 11403d0407baSopenharmony_ci * - For full compatibility on the importer side with existing userspace 11413d0407baSopenharmony_ci * interfaces, which might already support mmap'ing buffers. This is needed in 11423d0407baSopenharmony_ci * many processing pipelines (e.g. feeding a software rendered image into a 11433d0407baSopenharmony_ci * hardware pipeline, thumbnail creation, snapshots, ...). Also, Android's ION 11443d0407baSopenharmony_ci * framework already supported this and for DMA buffer file descriptors to 11453d0407baSopenharmony_ci * replace ION buffers mmap support was needed. 11463d0407baSopenharmony_ci * 11473d0407baSopenharmony_ci * There is no special interfaces, userspace simply calls mmap on the dma-buf 11483d0407baSopenharmony_ci * fd. But like for CPU access there's a need to braket the actual access, 11493d0407baSopenharmony_ci * which is handled by the ioctl (DMA_BUF_IOCTL_SYNC). Note that 11503d0407baSopenharmony_ci * DMA_BUF_IOCTL_SYNC can fail with -EAGAIN or -EINTR, in which case it must 11513d0407baSopenharmony_ci * be restarted. 11523d0407baSopenharmony_ci * 11533d0407baSopenharmony_ci * Some systems might need some sort of cache coherency management e.g. when 11543d0407baSopenharmony_ci * CPU and GPU domains are being accessed through dma-buf at the same time. 11553d0407baSopenharmony_ci * To circumvent this problem there are begin/end coherency markers, that 11563d0407baSopenharmony_ci * forward directly to existing dma-buf device drivers vfunc hooks. Userspace 11573d0407baSopenharmony_ci * can make use of those markers through the DMA_BUF_IOCTL_SYNC ioctl. The 11583d0407baSopenharmony_ci * sequence would be used like following: 11593d0407baSopenharmony_ci * 11603d0407baSopenharmony_ci * - mmap dma-buf fd 11613d0407baSopenharmony_ci * - for each drawing/upload cycle in CPU 1. SYNC_START ioctl, 2. read/write 11623d0407baSopenharmony_ci * to mmap area 3. SYNC_END ioctl. This can be repeated as often as you 11633d0407baSopenharmony_ci * want (with the new data being consumed by say the GPU or the scanout 11643d0407baSopenharmony_ci * device) 11653d0407baSopenharmony_ci * - munmap once you don't need the buffer any more 11663d0407baSopenharmony_ci * 11673d0407baSopenharmony_ci * For correctness and optimal performance, it is always required to use 11683d0407baSopenharmony_ci * SYNC_START and SYNC_END before and after, respectively, when accessing the 11693d0407baSopenharmony_ci * mapped address. Userspace cannot rely on coherent access, even when there 11703d0407baSopenharmony_ci * are systems where it just works without calling these ioctls. 11713d0407baSopenharmony_ci * 11723d0407baSopenharmony_ci * - And as a CPU fallback in userspace processing pipelines. 11733d0407baSopenharmony_ci * 11743d0407baSopenharmony_ci * Similar to the motivation for kernel cpu access it is again important that 11753d0407baSopenharmony_ci * the userspace code of a given importing subsystem can use the same 11763d0407baSopenharmony_ci * interfaces with a imported dma-buf buffer object as with a native buffer 11773d0407baSopenharmony_ci * object. This is especially important for drm where the userspace part of 11783d0407baSopenharmony_ci * contemporary OpenGL, X, and other drivers is huge, and reworking them to 11793d0407baSopenharmony_ci * use a different way to mmap a buffer rather invasive. 11803d0407baSopenharmony_ci * 11813d0407baSopenharmony_ci * The assumption in the current dma-buf interfaces is that redirecting the 11823d0407baSopenharmony_ci * initial mmap is all that's needed. A survey of some of the existing 11833d0407baSopenharmony_ci * subsystems shows that no driver seems to do any nefarious thing like 11843d0407baSopenharmony_ci * syncing up with outstanding asynchronous processing on the device or 11853d0407baSopenharmony_ci * allocating special resources at fault time. So hopefully this is good 11863d0407baSopenharmony_ci * enough, since adding interfaces to intercept pagefaults and allow pte 11873d0407baSopenharmony_ci * shootdowns would increase the complexity quite a bit. 11883d0407baSopenharmony_ci * 11893d0407baSopenharmony_ci * Interface:: 11903d0407baSopenharmony_ci * int dma_buf_mmap(struct dma_buf \*, struct vm_area_struct \*, 11913d0407baSopenharmony_ci * unsigned long); 11923d0407baSopenharmony_ci * 11933d0407baSopenharmony_ci * If the importing subsystem simply provides a special-purpose mmap call to 11943d0407baSopenharmony_ci * set up a mapping in userspace, calling do_mmap with dma_buf->file will 11953d0407baSopenharmony_ci * equally achieve that for a dma-buf object. 11963d0407baSopenharmony_ci */ 11973d0407baSopenharmony_ci 11983d0407baSopenharmony_cistatic int _dma_buf_begin_cpu_access(struct dma_buf *dmabuf, enum dma_data_direction direction) 11993d0407baSopenharmony_ci{ 12003d0407baSopenharmony_ci bool write = (direction == DMA_BIDIRECTIONAL || direction == DMA_TO_DEVICE); 12013d0407baSopenharmony_ci struct dma_resv *resv = dmabuf->resv; 12023d0407baSopenharmony_ci long ret; 12033d0407baSopenharmony_ci 12043d0407baSopenharmony_ci /* Wait on any implicit rendering fences */ 12053d0407baSopenharmony_ci ret = dma_resv_wait_timeout_rcu(resv, write, true, MAX_SCHEDULE_TIMEOUT); 12063d0407baSopenharmony_ci if (ret < 0) { 12073d0407baSopenharmony_ci return ret; 12083d0407baSopenharmony_ci } 12093d0407baSopenharmony_ci 12103d0407baSopenharmony_ci return 0; 12113d0407baSopenharmony_ci} 12123d0407baSopenharmony_ci 12133d0407baSopenharmony_ci/** 12143d0407baSopenharmony_ci * dma_buf_begin_cpu_access - Must be called before accessing a dma_buf from the 12153d0407baSopenharmony_ci * cpu in the kernel context. Calls begin_cpu_access to allow exporter-specific 12163d0407baSopenharmony_ci * preparations. Coherency is only guaranteed in the specified range for the 12173d0407baSopenharmony_ci * specified access direction. 12183d0407baSopenharmony_ci * @dmabuf: [in] buffer to prepare cpu access for. 12193d0407baSopenharmony_ci * @direction: [in] length of range for cpu access. 12203d0407baSopenharmony_ci * 12213d0407baSopenharmony_ci * After the cpu access is complete the caller should call 12223d0407baSopenharmony_ci * dma_buf_end_cpu_access(). Only when cpu access is braketed by both calls is 12233d0407baSopenharmony_ci * it guaranteed to be coherent with other DMA access. 12243d0407baSopenharmony_ci * 12253d0407baSopenharmony_ci * Can return negative error values, returns 0 on success. 12263d0407baSopenharmony_ci */ 12273d0407baSopenharmony_ciint dma_buf_begin_cpu_access(struct dma_buf *dmabuf, enum dma_data_direction direction) 12283d0407baSopenharmony_ci{ 12293d0407baSopenharmony_ci int ret = 0; 12303d0407baSopenharmony_ci 12313d0407baSopenharmony_ci if (WARN_ON(!dmabuf)) { 12323d0407baSopenharmony_ci return -EINVAL; 12333d0407baSopenharmony_ci } 12343d0407baSopenharmony_ci 12353d0407baSopenharmony_ci if (dmabuf->ops->begin_cpu_access) { 12363d0407baSopenharmony_ci ret = dmabuf->ops->begin_cpu_access(dmabuf, direction); 12373d0407baSopenharmony_ci } 12383d0407baSopenharmony_ci 12393d0407baSopenharmony_ci /* Ensure that all fences are waited upon - but we first allow 12403d0407baSopenharmony_ci * the native handler the chance to do so more efficiently if it 12413d0407baSopenharmony_ci * chooses. A double invocation here will be reasonably cheap no-op. 12423d0407baSopenharmony_ci */ 12433d0407baSopenharmony_ci if (ret == 0) { 12443d0407baSopenharmony_ci ret = _dma_buf_begin_cpu_access(dmabuf, direction); 12453d0407baSopenharmony_ci } 12463d0407baSopenharmony_ci 12473d0407baSopenharmony_ci return ret; 12483d0407baSopenharmony_ci} 12493d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_begin_cpu_access); 12503d0407baSopenharmony_ci 12513d0407baSopenharmony_ciint dma_buf_begin_cpu_access_partial(struct dma_buf *dmabuf, enum dma_data_direction direction, unsigned int offset, 12523d0407baSopenharmony_ci unsigned int len) 12533d0407baSopenharmony_ci{ 12543d0407baSopenharmony_ci int ret = 0; 12553d0407baSopenharmony_ci 12563d0407baSopenharmony_ci if (WARN_ON(!dmabuf)) { 12573d0407baSopenharmony_ci return -EINVAL; 12583d0407baSopenharmony_ci } 12593d0407baSopenharmony_ci 12603d0407baSopenharmony_ci if (dmabuf->ops->begin_cpu_access_partial) { 12613d0407baSopenharmony_ci ret = dmabuf->ops->begin_cpu_access_partial(dmabuf, direction, offset, len); 12623d0407baSopenharmony_ci } 12633d0407baSopenharmony_ci 12643d0407baSopenharmony_ci /* Ensure that all fences are waited upon - but we first allow 12653d0407baSopenharmony_ci * the native handler the chance to do so more efficiently if it 12663d0407baSopenharmony_ci * chooses. A double invocation here will be reasonably cheap no-op. 12673d0407baSopenharmony_ci */ 12683d0407baSopenharmony_ci if (ret == 0) { 12693d0407baSopenharmony_ci ret = _dma_buf_begin_cpu_access(dmabuf, direction); 12703d0407baSopenharmony_ci } 12713d0407baSopenharmony_ci 12723d0407baSopenharmony_ci return ret; 12733d0407baSopenharmony_ci} 12743d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_begin_cpu_access_partial); 12753d0407baSopenharmony_ci 12763d0407baSopenharmony_ci/** 12773d0407baSopenharmony_ci * dma_buf_end_cpu_access - Must be called after accessing a dma_buf from the 12783d0407baSopenharmony_ci * cpu in the kernel context. Calls end_cpu_access to allow exporter-specific 12793d0407baSopenharmony_ci * actions. Coherency is only guaranteed in the specified range for the 12803d0407baSopenharmony_ci * specified access direction. 12813d0407baSopenharmony_ci * @dmabuf: [in] buffer to complete cpu access for. 12823d0407baSopenharmony_ci * @direction: [in] length of range for cpu access. 12833d0407baSopenharmony_ci * 12843d0407baSopenharmony_ci * This terminates CPU access started with dma_buf_begin_cpu_access(). 12853d0407baSopenharmony_ci * 12863d0407baSopenharmony_ci * Can return negative error values, returns 0 on success. 12873d0407baSopenharmony_ci */ 12883d0407baSopenharmony_ciint dma_buf_end_cpu_access(struct dma_buf *dmabuf, enum dma_data_direction direction) 12893d0407baSopenharmony_ci{ 12903d0407baSopenharmony_ci int ret = 0; 12913d0407baSopenharmony_ci 12923d0407baSopenharmony_ci WARN_ON(!dmabuf); 12933d0407baSopenharmony_ci 12943d0407baSopenharmony_ci if (dmabuf->ops->end_cpu_access) { 12953d0407baSopenharmony_ci ret = dmabuf->ops->end_cpu_access(dmabuf, direction); 12963d0407baSopenharmony_ci } 12973d0407baSopenharmony_ci 12983d0407baSopenharmony_ci return ret; 12993d0407baSopenharmony_ci} 13003d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_end_cpu_access); 13013d0407baSopenharmony_ci 13023d0407baSopenharmony_ciint dma_buf_end_cpu_access_partial(struct dma_buf *dmabuf, enum dma_data_direction direction, unsigned int offset, 13033d0407baSopenharmony_ci unsigned int len) 13043d0407baSopenharmony_ci{ 13053d0407baSopenharmony_ci int ret = 0; 13063d0407baSopenharmony_ci 13073d0407baSopenharmony_ci WARN_ON(!dmabuf); 13083d0407baSopenharmony_ci 13093d0407baSopenharmony_ci if (dmabuf->ops->end_cpu_access_partial) { 13103d0407baSopenharmony_ci ret = dmabuf->ops->end_cpu_access_partial(dmabuf, direction, offset, len); 13113d0407baSopenharmony_ci } 13123d0407baSopenharmony_ci 13133d0407baSopenharmony_ci return ret; 13143d0407baSopenharmony_ci} 13153d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_end_cpu_access_partial); 13163d0407baSopenharmony_ci 13173d0407baSopenharmony_ci/** 13183d0407baSopenharmony_ci * dma_buf_mmap - Setup up a userspace mmap with the given vma 13193d0407baSopenharmony_ci * @dmabuf: [in] buffer that should back the vma 13203d0407baSopenharmony_ci * @vma: [in] vma for the mmap 13213d0407baSopenharmony_ci * @pgoff: [in] offset in pages where this mmap should start within the 13223d0407baSopenharmony_ci * dma-buf buffer. 13233d0407baSopenharmony_ci * 13243d0407baSopenharmony_ci * This function adjusts the passed in vma so that it points at the file of the 13253d0407baSopenharmony_ci * dma_buf operation. It also adjusts the starting pgoff and does bounds 13263d0407baSopenharmony_ci * checking on the size of the vma. Then it calls the exporters mmap function to 13273d0407baSopenharmony_ci * set up the mapping. 13283d0407baSopenharmony_ci * 13293d0407baSopenharmony_ci * Can return negative error values, returns 0 on success. 13303d0407baSopenharmony_ci */ 13313d0407baSopenharmony_ciint dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma, unsigned long pgoff) 13323d0407baSopenharmony_ci{ 13333d0407baSopenharmony_ci struct file *oldfile; 13343d0407baSopenharmony_ci int ret; 13353d0407baSopenharmony_ci 13363d0407baSopenharmony_ci if (WARN_ON(!dmabuf || !vma)) { 13373d0407baSopenharmony_ci return -EINVAL; 13383d0407baSopenharmony_ci } 13393d0407baSopenharmony_ci 13403d0407baSopenharmony_ci /* check if buffer supports mmap */ 13413d0407baSopenharmony_ci if (!dmabuf->ops->mmap) { 13423d0407baSopenharmony_ci return -EINVAL; 13433d0407baSopenharmony_ci } 13443d0407baSopenharmony_ci 13453d0407baSopenharmony_ci /* check for offset overflow */ 13463d0407baSopenharmony_ci if (pgoff + vma_pages(vma) < pgoff) { 13473d0407baSopenharmony_ci return -EOVERFLOW; 13483d0407baSopenharmony_ci } 13493d0407baSopenharmony_ci 13503d0407baSopenharmony_ci /* check for overflowing the buffer's size */ 13513d0407baSopenharmony_ci if ((pgoff + vma_pages(vma)) > (dmabuf->size >> PAGE_SHIFT)) { 13523d0407baSopenharmony_ci return -EINVAL; 13533d0407baSopenharmony_ci } 13543d0407baSopenharmony_ci 13553d0407baSopenharmony_ci /* readjust the vma */ 13563d0407baSopenharmony_ci get_file(dmabuf->file); 13573d0407baSopenharmony_ci oldfile = vma->vm_file; 13583d0407baSopenharmony_ci vma->vm_file = dmabuf->file; 13593d0407baSopenharmony_ci vma->vm_pgoff = pgoff; 13603d0407baSopenharmony_ci 13613d0407baSopenharmony_ci ret = dmabuf->ops->mmap(dmabuf, vma); 13623d0407baSopenharmony_ci if (ret) { 13633d0407baSopenharmony_ci /* restore old parameters on failure */ 13643d0407baSopenharmony_ci vma->vm_file = oldfile; 13653d0407baSopenharmony_ci fput(dmabuf->file); 13663d0407baSopenharmony_ci } else { 13673d0407baSopenharmony_ci if (oldfile) { 13683d0407baSopenharmony_ci fput(oldfile); 13693d0407baSopenharmony_ci } 13703d0407baSopenharmony_ci } 13713d0407baSopenharmony_ci return ret; 13723d0407baSopenharmony_ci} 13733d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_mmap); 13743d0407baSopenharmony_ci 13753d0407baSopenharmony_ci/** 13763d0407baSopenharmony_ci * dma_buf_vmap - Create virtual mapping for the buffer object into kernel 13773d0407baSopenharmony_ci * address space. Same restrictions as for vmap and friends apply. 13783d0407baSopenharmony_ci * @dmabuf: [in] buffer to vmap 13793d0407baSopenharmony_ci * 13803d0407baSopenharmony_ci * This call may fail due to lack of virtual mapping address space. 13813d0407baSopenharmony_ci * These calls are optional in drivers. The intended use for them 13823d0407baSopenharmony_ci * is for mapping objects linear in kernel space for high use objects. 13833d0407baSopenharmony_ci * Please attempt to use kmap/kunmap before thinking about these interfaces. 13843d0407baSopenharmony_ci * 13853d0407baSopenharmony_ci * Returns NULL on error. 13863d0407baSopenharmony_ci */ 13873d0407baSopenharmony_civoid *dma_buf_vmap(struct dma_buf *dmabuf) 13883d0407baSopenharmony_ci{ 13893d0407baSopenharmony_ci void *ptr; 13903d0407baSopenharmony_ci 13913d0407baSopenharmony_ci if (WARN_ON(!dmabuf)) { 13923d0407baSopenharmony_ci return NULL; 13933d0407baSopenharmony_ci } 13943d0407baSopenharmony_ci 13953d0407baSopenharmony_ci if (!dmabuf->ops->vmap) { 13963d0407baSopenharmony_ci return NULL; 13973d0407baSopenharmony_ci } 13983d0407baSopenharmony_ci 13993d0407baSopenharmony_ci mutex_lock(&dmabuf->lock); 14003d0407baSopenharmony_ci if (dmabuf->vmapping_counter) { 14013d0407baSopenharmony_ci dmabuf->vmapping_counter++; 14023d0407baSopenharmony_ci BUG_ON(!dmabuf->vmap_ptr); 14033d0407baSopenharmony_ci ptr = dmabuf->vmap_ptr; 14043d0407baSopenharmony_ci goto out_unlock; 14053d0407baSopenharmony_ci } 14063d0407baSopenharmony_ci 14073d0407baSopenharmony_ci BUG_ON(dmabuf->vmap_ptr); 14083d0407baSopenharmony_ci 14093d0407baSopenharmony_ci ptr = dmabuf->ops->vmap(dmabuf); 14103d0407baSopenharmony_ci if (WARN_ON_ONCE(IS_ERR(ptr))) { 14113d0407baSopenharmony_ci ptr = NULL; 14123d0407baSopenharmony_ci } 14133d0407baSopenharmony_ci if (!ptr) { 14143d0407baSopenharmony_ci goto out_unlock; 14153d0407baSopenharmony_ci } 14163d0407baSopenharmony_ci 14173d0407baSopenharmony_ci dmabuf->vmap_ptr = ptr; 14183d0407baSopenharmony_ci dmabuf->vmapping_counter = 1; 14193d0407baSopenharmony_ci 14203d0407baSopenharmony_ciout_unlock: 14213d0407baSopenharmony_ci mutex_unlock(&dmabuf->lock); 14223d0407baSopenharmony_ci return ptr; 14233d0407baSopenharmony_ci} 14243d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_vmap); 14253d0407baSopenharmony_ci 14263d0407baSopenharmony_ci/** 14273d0407baSopenharmony_ci * dma_buf_vunmap - Unmap a vmap obtained by dma_buf_vmap. 14283d0407baSopenharmony_ci * @dmabuf: [in] buffer to vunmap 14293d0407baSopenharmony_ci * @vaddr: [in] vmap to vunmap 14303d0407baSopenharmony_ci */ 14313d0407baSopenharmony_civoid dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr) 14323d0407baSopenharmony_ci{ 14333d0407baSopenharmony_ci if (WARN_ON(!dmabuf)) { 14343d0407baSopenharmony_ci return; 14353d0407baSopenharmony_ci } 14363d0407baSopenharmony_ci 14373d0407baSopenharmony_ci BUG_ON(!dmabuf->vmap_ptr); 14383d0407baSopenharmony_ci BUG_ON(dmabuf->vmapping_counter == 0); 14393d0407baSopenharmony_ci BUG_ON(dmabuf->vmap_ptr != vaddr); 14403d0407baSopenharmony_ci 14413d0407baSopenharmony_ci mutex_lock(&dmabuf->lock); 14423d0407baSopenharmony_ci if (--dmabuf->vmapping_counter == 0) { 14433d0407baSopenharmony_ci if (dmabuf->ops->vunmap) { 14443d0407baSopenharmony_ci dmabuf->ops->vunmap(dmabuf, vaddr); 14453d0407baSopenharmony_ci } 14463d0407baSopenharmony_ci dmabuf->vmap_ptr = NULL; 14473d0407baSopenharmony_ci } 14483d0407baSopenharmony_ci mutex_unlock(&dmabuf->lock); 14493d0407baSopenharmony_ci} 14503d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_vunmap); 14513d0407baSopenharmony_ci 14523d0407baSopenharmony_ciint dma_buf_get_flags(struct dma_buf *dmabuf, unsigned long *flags) 14533d0407baSopenharmony_ci{ 14543d0407baSopenharmony_ci int ret = 0; 14553d0407baSopenharmony_ci 14563d0407baSopenharmony_ci if (WARN_ON(!dmabuf) || !flags) { 14573d0407baSopenharmony_ci return -EINVAL; 14583d0407baSopenharmony_ci } 14593d0407baSopenharmony_ci 14603d0407baSopenharmony_ci if (dmabuf->ops->get_flags) { 14613d0407baSopenharmony_ci ret = dmabuf->ops->get_flags(dmabuf, flags); 14623d0407baSopenharmony_ci } 14633d0407baSopenharmony_ci 14643d0407baSopenharmony_ci return ret; 14653d0407baSopenharmony_ci} 14663d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_get_flags); 14673d0407baSopenharmony_ci 14683d0407baSopenharmony_ciint dma_buf_get_uuid(struct dma_buf *dmabuf, uuid_t *uuid) 14693d0407baSopenharmony_ci{ 14703d0407baSopenharmony_ci if (WARN_ON(!dmabuf) || !uuid) { 14713d0407baSopenharmony_ci return -EINVAL; 14723d0407baSopenharmony_ci } 14733d0407baSopenharmony_ci 14743d0407baSopenharmony_ci if (!dmabuf->ops->get_uuid) { 14753d0407baSopenharmony_ci return -ENODEV; 14763d0407baSopenharmony_ci } 14773d0407baSopenharmony_ci 14783d0407baSopenharmony_ci return dmabuf->ops->get_uuid(dmabuf, uuid); 14793d0407baSopenharmony_ci} 14803d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(dma_buf_get_uuid); 14813d0407baSopenharmony_ci 14823d0407baSopenharmony_ci#ifdef CONFIG_DEBUG_FS 14833d0407baSopenharmony_cistatic int dma_buf_debug_show(struct seq_file *s, void *unused) 14843d0407baSopenharmony_ci{ 14853d0407baSopenharmony_ci int ret; 14863d0407baSopenharmony_ci struct dma_buf *buf_obj; 14873d0407baSopenharmony_ci struct dma_buf_attachment *attach_obj; 14883d0407baSopenharmony_ci struct dma_resv *robj; 14893d0407baSopenharmony_ci struct dma_resv_list *fobj; 14903d0407baSopenharmony_ci struct dma_fence *fence; 14913d0407baSopenharmony_ci unsigned seq; 14923d0407baSopenharmony_ci int count = 0, attach_count, shared_count, i; 14933d0407baSopenharmony_ci size_t size = 0; 14943d0407baSopenharmony_ci 14953d0407baSopenharmony_ci ret = mutex_lock_interruptible(&db_list.lock); 14963d0407baSopenharmony_ci if (ret) { 14973d0407baSopenharmony_ci return ret; 14983d0407baSopenharmony_ci } 14993d0407baSopenharmony_ci 15003d0407baSopenharmony_ci seq_puts(s, "\nDma-buf Objects:\n"); 15013d0407baSopenharmony_ci seq_printf(s, 15023d0407baSopenharmony_ci "%-8s\t%-8s\t%-8s\t%-8s\texp_name\t%-8s\t" 15033d0407baSopenharmony_ci "%-16s\t%-16s\t%-16s\n", 15043d0407baSopenharmony_ci "size", "flags", "mode", "count", "ino", "buf_name", "exp_pid", "exp_task_comm"); 15053d0407baSopenharmony_ci 15063d0407baSopenharmony_ci list_for_each_entry(buf_obj, &db_list.head, list_node) 15073d0407baSopenharmony_ci { 15083d0407baSopenharmony_ci ret = dma_resv_lock_interruptible(buf_obj->resv, NULL); 15093d0407baSopenharmony_ci if (ret) { 15103d0407baSopenharmony_ci goto error_unlock; 15113d0407baSopenharmony_ci } 15123d0407baSopenharmony_ci 15133d0407baSopenharmony_ci seq_printf(s, 15143d0407baSopenharmony_ci "%08zu\t%08x\t%08x\t%08ld\t%s\t%08lu\t%s\t" 15153d0407baSopenharmony_ci "%-16d\t%-16s\n", 15163d0407baSopenharmony_ci buf_obj->size, buf_obj->file->f_flags, buf_obj->file->f_mode, file_count(buf_obj->file), 15173d0407baSopenharmony_ci buf_obj->exp_name, file_inode(buf_obj->file)->i_ino, buf_obj->name ?: "NULL", 15183d0407baSopenharmony_ci dma_buf_exp_pid(buf_obj), dma_buf_exp_task_comm(buf_obj) ?: "NULL"); 15193d0407baSopenharmony_ci 15203d0407baSopenharmony_ci robj = buf_obj->resv; 15213d0407baSopenharmony_ci while (true) { 15223d0407baSopenharmony_ci seq = read_seqcount_begin(&robj->seq); 15233d0407baSopenharmony_ci rcu_read_lock(); 15243d0407baSopenharmony_ci fobj = rcu_dereference(robj->fence); 15253d0407baSopenharmony_ci shared_count = fobj ? fobj->shared_count : 0; 15263d0407baSopenharmony_ci fence = rcu_dereference(robj->fence_excl); 15273d0407baSopenharmony_ci if (!read_seqcount_retry(&robj->seq, seq)) { 15283d0407baSopenharmony_ci break; 15293d0407baSopenharmony_ci } 15303d0407baSopenharmony_ci rcu_read_unlock(); 15313d0407baSopenharmony_ci } 15323d0407baSopenharmony_ci 15333d0407baSopenharmony_ci if (fence) { 15343d0407baSopenharmony_ci seq_printf(s, "\tExclusive fence: %s %s %ssignalled\n", fence->ops->get_driver_name(fence), 15353d0407baSopenharmony_ci fence->ops->get_timeline_name(fence), dma_fence_is_signaled(fence) ? "" : "un"); 15363d0407baSopenharmony_ci } 15373d0407baSopenharmony_ci for (i = 0; i < shared_count; i++) { 15383d0407baSopenharmony_ci fence = rcu_dereference(fobj->shared[i]); 15393d0407baSopenharmony_ci if (!dma_fence_get_rcu(fence)) { 15403d0407baSopenharmony_ci continue; 15413d0407baSopenharmony_ci } 15423d0407baSopenharmony_ci seq_printf(s, "\tShared fence: %s %s %ssignalled\n", fence->ops->get_driver_name(fence), 15433d0407baSopenharmony_ci fence->ops->get_timeline_name(fence), dma_fence_is_signaled(fence) ? "" : "un"); 15443d0407baSopenharmony_ci dma_fence_put(fence); 15453d0407baSopenharmony_ci } 15463d0407baSopenharmony_ci rcu_read_unlock(); 15473d0407baSopenharmony_ci 15483d0407baSopenharmony_ci seq_puts(s, "\tAttached Devices:\n"); 15493d0407baSopenharmony_ci attach_count = 0; 15503d0407baSopenharmony_ci 15513d0407baSopenharmony_ci list_for_each_entry(attach_obj, &buf_obj->attachments, node) 15523d0407baSopenharmony_ci { 15533d0407baSopenharmony_ci seq_printf(s, "\t%s\n", dev_name(attach_obj->dev)); 15543d0407baSopenharmony_ci attach_count++; 15553d0407baSopenharmony_ci } 15563d0407baSopenharmony_ci dma_resv_unlock(buf_obj->resv); 15573d0407baSopenharmony_ci 15583d0407baSopenharmony_ci seq_printf(s, "Total %d devices attached\n\n", attach_count); 15593d0407baSopenharmony_ci 15603d0407baSopenharmony_ci count++; 15613d0407baSopenharmony_ci size += buf_obj->size; 15623d0407baSopenharmony_ci } 15633d0407baSopenharmony_ci 15643d0407baSopenharmony_ci seq_printf(s, "\nTotal %d objects, %zu bytes\n", count, size); 15653d0407baSopenharmony_ci 15663d0407baSopenharmony_ci mutex_unlock(&db_list.lock); 15673d0407baSopenharmony_ci return 0; 15683d0407baSopenharmony_ci 15693d0407baSopenharmony_cierror_unlock: 15703d0407baSopenharmony_ci mutex_unlock(&db_list.lock); 15713d0407baSopenharmony_ci return ret; 15723d0407baSopenharmony_ci} 15733d0407baSopenharmony_ci 15743d0407baSopenharmony_ciDEFINE_SHOW_ATTRIBUTE(dma_buf_debug); 15753d0407baSopenharmony_ci 15763d0407baSopenharmony_cistatic struct dentry *dma_buf_debugfs_dir; 15773d0407baSopenharmony_ci 15783d0407baSopenharmony_cistatic int dma_buf_init_debugfs(void) 15793d0407baSopenharmony_ci{ 15803d0407baSopenharmony_ci struct dentry *d; 15813d0407baSopenharmony_ci int err = 0; 15823d0407baSopenharmony_ci 15833d0407baSopenharmony_ci d = debugfs_create_dir("dma_buf", NULL); 15843d0407baSopenharmony_ci if (IS_ERR(d)) { 15853d0407baSopenharmony_ci return PTR_ERR(d); 15863d0407baSopenharmony_ci } 15873d0407baSopenharmony_ci 15883d0407baSopenharmony_ci dma_buf_debugfs_dir = d; 15893d0407baSopenharmony_ci 15903d0407baSopenharmony_ci d = debugfs_create_file("bufinfo", S_IRUGO, dma_buf_debugfs_dir, NULL, &dma_buf_debug_fops); 15913d0407baSopenharmony_ci if (IS_ERR(d)) { 15923d0407baSopenharmony_ci pr_debug("dma_buf: debugfs: failed to create node bufinfo\n"); 15933d0407baSopenharmony_ci debugfs_remove_recursive(dma_buf_debugfs_dir); 15943d0407baSopenharmony_ci dma_buf_debugfs_dir = NULL; 15953d0407baSopenharmony_ci err = PTR_ERR(d); 15963d0407baSopenharmony_ci } 15973d0407baSopenharmony_ci 15983d0407baSopenharmony_ci dma_buf_process_info_init_debugfs(dma_buf_debugfs_dir); 15993d0407baSopenharmony_ci return err; 16003d0407baSopenharmony_ci} 16013d0407baSopenharmony_ci 16023d0407baSopenharmony_cistatic void dma_buf_uninit_debugfs(void) 16033d0407baSopenharmony_ci{ 16043d0407baSopenharmony_ci debugfs_remove_recursive(dma_buf_debugfs_dir); 16053d0407baSopenharmony_ci} 16063d0407baSopenharmony_ci#else 16073d0407baSopenharmony_cistatic inline int dma_buf_init_debugfs(void) 16083d0407baSopenharmony_ci{ 16093d0407baSopenharmony_ci return 0; 16103d0407baSopenharmony_ci} 16113d0407baSopenharmony_cistatic inline void dma_buf_uninit_debugfs(void) 16123d0407baSopenharmony_ci{ 16133d0407baSopenharmony_ci} 16143d0407baSopenharmony_ci#endif 16153d0407baSopenharmony_ci 16163d0407baSopenharmony_ci#ifdef CONFIG_DMABUF_PROCESS_INFO 16173d0407baSopenharmony_cistruct dma_buf *get_dma_buf_from_file(struct file *f) 16183d0407baSopenharmony_ci{ 16193d0407baSopenharmony_ci if (IS_ERR_OR_NULL(f)) { 16203d0407baSopenharmony_ci return NULL; 16213d0407baSopenharmony_ci } 16223d0407baSopenharmony_ci 16233d0407baSopenharmony_ci if (!is_dma_buf_file(f)) { 16243d0407baSopenharmony_ci return NULL; 16253d0407baSopenharmony_ci } 16263d0407baSopenharmony_ci 16273d0407baSopenharmony_ci return f->private_data; 16283d0407baSopenharmony_ci} 16293d0407baSopenharmony_ci#endif /* CONFIG_DMABUF_PROCESS_INFO */ 16303d0407baSopenharmony_ci 16313d0407baSopenharmony_cistatic int __init dma_buf_init(void) 16323d0407baSopenharmony_ci{ 16333d0407baSopenharmony_ci int ret; 16343d0407baSopenharmony_ci 16353d0407baSopenharmony_ci ret = dma_buf_init_sysfs_statistics(); 16363d0407baSopenharmony_ci if (ret) { 16373d0407baSopenharmony_ci return ret; 16383d0407baSopenharmony_ci } 16393d0407baSopenharmony_ci 16403d0407baSopenharmony_ci dma_buf_mnt = kern_mount(&dma_buf_fs_type); 16413d0407baSopenharmony_ci if (IS_ERR(dma_buf_mnt)) { 16423d0407baSopenharmony_ci return PTR_ERR(dma_buf_mnt); 16433d0407baSopenharmony_ci } 16443d0407baSopenharmony_ci 16453d0407baSopenharmony_ci mutex_init(&db_list.lock); 16463d0407baSopenharmony_ci INIT_LIST_HEAD(&db_list.head); 16473d0407baSopenharmony_ci dma_buf_init_debugfs(); 16483d0407baSopenharmony_ci dma_buf_process_info_init_procfs(); 16493d0407baSopenharmony_ci return 0; 16503d0407baSopenharmony_ci} 16513d0407baSopenharmony_cisubsys_initcall(dma_buf_init); 16523d0407baSopenharmony_ci 16533d0407baSopenharmony_cistatic void __exit dma_buf_deinit(void) 16543d0407baSopenharmony_ci{ 16553d0407baSopenharmony_ci dma_buf_uninit_debugfs(); 16563d0407baSopenharmony_ci kern_unmount(dma_buf_mnt); 16573d0407baSopenharmony_ci dma_buf_uninit_sysfs_statistics(); 16583d0407baSopenharmony_ci dma_buf_process_info_uninit_procfs(); 16593d0407baSopenharmony_ci} 16603d0407baSopenharmony_ci__exitcall(dma_buf_deinit); 1661