1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/cred.h>
3 #include <linux/device.h>
4 #include <linux/dma-buf.h>
5 #include <linux/highmem.h>
6 #include <linux/init.h>
7 #include <linux/kernel.h>
8 #include <linux/memfd.h>
9 #include <linux/miscdevice.h>
10 #include <linux/module.h>
11 #include <linux/shmem_fs.h>
12 #include <linux/slab.h>
13 #include <linux/udmabuf.h>
15 static const u32 list_limit
= 1024; /* udmabuf_create_list->count limit */
16 static const size_t size_limit_mb
= 64; /* total dmabuf size, in megabytes */
22 struct miscdevice
*device
;
25 static vm_fault_t
udmabuf_vm_fault(struct vm_fault
*vmf
)
27 struct vm_area_struct
*vma
= vmf
->vma
;
28 struct udmabuf
*ubuf
= vma
->vm_private_data
;
30 vmf
->page
= ubuf
->pages
[vmf
->pgoff
];
35 static const struct vm_operations_struct udmabuf_vm_ops
= {
36 .fault
= udmabuf_vm_fault
,
39 static int mmap_udmabuf(struct dma_buf
*buf
, struct vm_area_struct
*vma
)
41 struct udmabuf
*ubuf
= buf
->priv
;
43 if ((vma
->vm_flags
& (VM_SHARED
| VM_MAYSHARE
)) == 0)
46 vma
->vm_ops
= &udmabuf_vm_ops
;
47 vma
->vm_private_data
= ubuf
;
51 static struct sg_table
*get_sg_table(struct device
*dev
, struct dma_buf
*buf
,
52 enum dma_data_direction direction
)
54 struct udmabuf
*ubuf
= buf
->priv
;
58 sg
= kzalloc(sizeof(*sg
), GFP_KERNEL
);
60 return ERR_PTR(-ENOMEM
);
61 ret
= sg_alloc_table_from_pages(sg
, ubuf
->pages
, ubuf
->pagecount
,
62 0, ubuf
->pagecount
<< PAGE_SHIFT
,
66 ret
= dma_map_sgtable(dev
, sg
, direction
, 0);
77 static void put_sg_table(struct device
*dev
, struct sg_table
*sg
,
78 enum dma_data_direction direction
)
80 dma_unmap_sgtable(dev
, sg
, direction
, 0);
85 static struct sg_table
*map_udmabuf(struct dma_buf_attachment
*at
,
86 enum dma_data_direction direction
)
88 return get_sg_table(at
->dev
, at
->dmabuf
, direction
);
91 static void unmap_udmabuf(struct dma_buf_attachment
*at
,
93 enum dma_data_direction direction
)
95 return put_sg_table(at
->dev
, sg
, direction
);
98 static void release_udmabuf(struct dma_buf
*buf
)
100 struct udmabuf
*ubuf
= buf
->priv
;
101 struct device
*dev
= ubuf
->device
->this_device
;
105 put_sg_table(dev
, ubuf
->sg
, DMA_BIDIRECTIONAL
);
107 for (pg
= 0; pg
< ubuf
->pagecount
; pg
++)
108 put_page(ubuf
->pages
[pg
]);
113 static int begin_cpu_udmabuf(struct dma_buf
*buf
,
114 enum dma_data_direction direction
)
116 struct udmabuf
*ubuf
= buf
->priv
;
117 struct device
*dev
= ubuf
->device
->this_device
;
120 ubuf
->sg
= get_sg_table(dev
, buf
, direction
);
121 if (IS_ERR(ubuf
->sg
))
122 return PTR_ERR(ubuf
->sg
);
124 dma_sync_sg_for_cpu(dev
, ubuf
->sg
->sgl
, ubuf
->sg
->nents
,
131 static int end_cpu_udmabuf(struct dma_buf
*buf
,
132 enum dma_data_direction direction
)
134 struct udmabuf
*ubuf
= buf
->priv
;
135 struct device
*dev
= ubuf
->device
->this_device
;
140 dma_sync_sg_for_device(dev
, ubuf
->sg
->sgl
, ubuf
->sg
->nents
, direction
);
144 static const struct dma_buf_ops udmabuf_ops
= {
145 .cache_sgt_mapping
= true,
146 .map_dma_buf
= map_udmabuf
,
147 .unmap_dma_buf
= unmap_udmabuf
,
148 .release
= release_udmabuf
,
149 .mmap
= mmap_udmabuf
,
150 .begin_cpu_access
= begin_cpu_udmabuf
,
151 .end_cpu_access
= end_cpu_udmabuf
,
154 #define SEALS_WANTED (F_SEAL_SHRINK)
155 #define SEALS_DENIED (F_SEAL_WRITE)
157 static long udmabuf_create(struct miscdevice
*device
,
158 struct udmabuf_create_list
*head
,
159 struct udmabuf_create_item
*list
)
161 DEFINE_DMA_BUF_EXPORT_INFO(exp_info
);
162 struct file
*memfd
= NULL
;
163 struct udmabuf
*ubuf
;
165 pgoff_t pgoff
, pgcnt
, pgidx
, pgbuf
= 0, pglimit
;
167 int seals
, ret
= -EINVAL
;
170 ubuf
= kzalloc(sizeof(*ubuf
), GFP_KERNEL
);
174 pglimit
= (size_limit_mb
* 1024 * 1024) >> PAGE_SHIFT
;
175 for (i
= 0; i
< head
->count
; i
++) {
176 if (!IS_ALIGNED(list
[i
].offset
, PAGE_SIZE
))
178 if (!IS_ALIGNED(list
[i
].size
, PAGE_SIZE
))
180 ubuf
->pagecount
+= list
[i
].size
>> PAGE_SHIFT
;
181 if (ubuf
->pagecount
> pglimit
)
184 ubuf
->pages
= kmalloc_array(ubuf
->pagecount
, sizeof(*ubuf
->pages
),
192 for (i
= 0; i
< head
->count
; i
++) {
194 memfd
= fget(list
[i
].memfd
);
197 if (!shmem_mapping(file_inode(memfd
)->i_mapping
))
199 seals
= memfd_fcntl(memfd
, F_GET_SEALS
, 0);
200 if (seals
== -EINVAL
)
203 if ((seals
& SEALS_WANTED
) != SEALS_WANTED
||
204 (seals
& SEALS_DENIED
) != 0)
206 pgoff
= list
[i
].offset
>> PAGE_SHIFT
;
207 pgcnt
= list
[i
].size
>> PAGE_SHIFT
;
208 for (pgidx
= 0; pgidx
< pgcnt
; pgidx
++) {
209 page
= shmem_read_mapping_page(
210 file_inode(memfd
)->i_mapping
, pgoff
+ pgidx
);
215 ubuf
->pages
[pgbuf
++] = page
;
221 exp_info
.ops
= &udmabuf_ops
;
222 exp_info
.size
= ubuf
->pagecount
<< PAGE_SHIFT
;
223 exp_info
.priv
= ubuf
;
224 exp_info
.flags
= O_RDWR
;
226 ubuf
->device
= device
;
227 buf
= dma_buf_export(&exp_info
);
234 if (head
->flags
& UDMABUF_FLAGS_CLOEXEC
)
236 return dma_buf_fd(buf
, flags
);
240 put_page(ubuf
->pages
[--pgbuf
]);
248 static long udmabuf_ioctl_create(struct file
*filp
, unsigned long arg
)
250 struct udmabuf_create create
;
251 struct udmabuf_create_list head
;
252 struct udmabuf_create_item list
;
254 if (copy_from_user(&create
, (void __user
*)arg
,
258 head
.flags
= create
.flags
;
260 list
.memfd
= create
.memfd
;
261 list
.offset
= create
.offset
;
262 list
.size
= create
.size
;
264 return udmabuf_create(filp
->private_data
, &head
, &list
);
267 static long udmabuf_ioctl_create_list(struct file
*filp
, unsigned long arg
)
269 struct udmabuf_create_list head
;
270 struct udmabuf_create_item
*list
;
274 if (copy_from_user(&head
, (void __user
*)arg
, sizeof(head
)))
276 if (head
.count
> list_limit
)
278 lsize
= sizeof(struct udmabuf_create_item
) * head
.count
;
279 list
= memdup_user((void __user
*)(arg
+ sizeof(head
)), lsize
);
281 return PTR_ERR(list
);
283 ret
= udmabuf_create(filp
->private_data
, &head
, list
);
288 static long udmabuf_ioctl(struct file
*filp
, unsigned int ioctl
,
295 ret
= udmabuf_ioctl_create(filp
, arg
);
297 case UDMABUF_CREATE_LIST
:
298 ret
= udmabuf_ioctl_create_list(filp
, arg
);
307 static const struct file_operations udmabuf_fops
= {
308 .owner
= THIS_MODULE
,
309 .unlocked_ioctl
= udmabuf_ioctl
,
311 .compat_ioctl
= udmabuf_ioctl
,
315 static struct miscdevice udmabuf_misc
= {
316 .minor
= MISC_DYNAMIC_MINOR
,
318 .fops
= &udmabuf_fops
,
321 static int __init
udmabuf_dev_init(void)
323 return misc_register(&udmabuf_misc
);
326 static void __exit
udmabuf_dev_exit(void)
328 misc_deregister(&udmabuf_misc
);
331 module_init(udmabuf_dev_init
)
332 module_exit(udmabuf_dev_exit
)
334 MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
335 MODULE_LICENSE("GPL v2");