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 if (!dma_map_sg(dev
, sg
->sgl
, sg
->nents
, direction
)) {
78 static void put_sg_table(struct device
*dev
, struct sg_table
*sg
,
79 enum dma_data_direction direction
)
81 dma_unmap_sg(dev
, sg
->sgl
, sg
->nents
, direction
);
86 static struct sg_table
*map_udmabuf(struct dma_buf_attachment
*at
,
87 enum dma_data_direction direction
)
89 return get_sg_table(at
->dev
, at
->dmabuf
, direction
);
92 static void unmap_udmabuf(struct dma_buf_attachment
*at
,
94 enum dma_data_direction direction
)
96 return put_sg_table(at
->dev
, sg
, direction
);
99 static void release_udmabuf(struct dma_buf
*buf
)
101 struct udmabuf
*ubuf
= buf
->priv
;
102 struct device
*dev
= ubuf
->device
->this_device
;
106 put_sg_table(dev
, ubuf
->sg
, DMA_BIDIRECTIONAL
);
108 for (pg
= 0; pg
< ubuf
->pagecount
; pg
++)
109 put_page(ubuf
->pages
[pg
]);
114 static int begin_cpu_udmabuf(struct dma_buf
*buf
,
115 enum dma_data_direction direction
)
117 struct udmabuf
*ubuf
= buf
->priv
;
118 struct device
*dev
= ubuf
->device
->this_device
;
121 ubuf
->sg
= get_sg_table(dev
, buf
, direction
);
122 if (IS_ERR(ubuf
->sg
))
123 return PTR_ERR(ubuf
->sg
);
125 dma_sync_sg_for_cpu(dev
, ubuf
->sg
->sgl
, ubuf
->sg
->nents
,
132 static int end_cpu_udmabuf(struct dma_buf
*buf
,
133 enum dma_data_direction direction
)
135 struct udmabuf
*ubuf
= buf
->priv
;
136 struct device
*dev
= ubuf
->device
->this_device
;
141 dma_sync_sg_for_device(dev
, ubuf
->sg
->sgl
, ubuf
->sg
->nents
, direction
);
145 static const struct dma_buf_ops udmabuf_ops
= {
146 .cache_sgt_mapping
= true,
147 .map_dma_buf
= map_udmabuf
,
148 .unmap_dma_buf
= unmap_udmabuf
,
149 .release
= release_udmabuf
,
150 .mmap
= mmap_udmabuf
,
151 .begin_cpu_access
= begin_cpu_udmabuf
,
152 .end_cpu_access
= end_cpu_udmabuf
,
155 #define SEALS_WANTED (F_SEAL_SHRINK)
156 #define SEALS_DENIED (F_SEAL_WRITE)
158 static long udmabuf_create(struct miscdevice
*device
,
159 struct udmabuf_create_list
*head
,
160 struct udmabuf_create_item
*list
)
162 DEFINE_DMA_BUF_EXPORT_INFO(exp_info
);
163 struct file
*memfd
= NULL
;
164 struct udmabuf
*ubuf
;
166 pgoff_t pgoff
, pgcnt
, pgidx
, pgbuf
= 0, pglimit
;
168 int seals
, ret
= -EINVAL
;
171 ubuf
= kzalloc(sizeof(*ubuf
), GFP_KERNEL
);
175 pglimit
= (size_limit_mb
* 1024 * 1024) >> PAGE_SHIFT
;
176 for (i
= 0; i
< head
->count
; i
++) {
177 if (!IS_ALIGNED(list
[i
].offset
, PAGE_SIZE
))
179 if (!IS_ALIGNED(list
[i
].size
, PAGE_SIZE
))
181 ubuf
->pagecount
+= list
[i
].size
>> PAGE_SHIFT
;
182 if (ubuf
->pagecount
> pglimit
)
185 ubuf
->pages
= kmalloc_array(ubuf
->pagecount
, sizeof(*ubuf
->pages
),
193 for (i
= 0; i
< head
->count
; i
++) {
195 memfd
= fget(list
[i
].memfd
);
198 if (!shmem_mapping(file_inode(memfd
)->i_mapping
))
200 seals
= memfd_fcntl(memfd
, F_GET_SEALS
, 0);
201 if (seals
== -EINVAL
)
204 if ((seals
& SEALS_WANTED
) != SEALS_WANTED
||
205 (seals
& SEALS_DENIED
) != 0)
207 pgoff
= list
[i
].offset
>> PAGE_SHIFT
;
208 pgcnt
= list
[i
].size
>> PAGE_SHIFT
;
209 for (pgidx
= 0; pgidx
< pgcnt
; pgidx
++) {
210 page
= shmem_read_mapping_page(
211 file_inode(memfd
)->i_mapping
, pgoff
+ pgidx
);
216 ubuf
->pages
[pgbuf
++] = page
;
222 exp_info
.ops
= &udmabuf_ops
;
223 exp_info
.size
= ubuf
->pagecount
<< PAGE_SHIFT
;
224 exp_info
.priv
= ubuf
;
225 exp_info
.flags
= O_RDWR
;
227 ubuf
->device
= device
;
228 buf
= dma_buf_export(&exp_info
);
235 if (head
->flags
& UDMABUF_FLAGS_CLOEXEC
)
237 return dma_buf_fd(buf
, flags
);
241 put_page(ubuf
->pages
[--pgbuf
]);
249 static long udmabuf_ioctl_create(struct file
*filp
, unsigned long arg
)
251 struct udmabuf_create create
;
252 struct udmabuf_create_list head
;
253 struct udmabuf_create_item list
;
255 if (copy_from_user(&create
, (void __user
*)arg
,
259 head
.flags
= create
.flags
;
261 list
.memfd
= create
.memfd
;
262 list
.offset
= create
.offset
;
263 list
.size
= create
.size
;
265 return udmabuf_create(filp
->private_data
, &head
, &list
);
268 static long udmabuf_ioctl_create_list(struct file
*filp
, unsigned long arg
)
270 struct udmabuf_create_list head
;
271 struct udmabuf_create_item
*list
;
275 if (copy_from_user(&head
, (void __user
*)arg
, sizeof(head
)))
277 if (head
.count
> list_limit
)
279 lsize
= sizeof(struct udmabuf_create_item
) * head
.count
;
280 list
= memdup_user((void __user
*)(arg
+ sizeof(head
)), lsize
);
282 return PTR_ERR(list
);
284 ret
= udmabuf_create(filp
->private_data
, &head
, list
);
289 static long udmabuf_ioctl(struct file
*filp
, unsigned int ioctl
,
296 ret
= udmabuf_ioctl_create(filp
, arg
);
298 case UDMABUF_CREATE_LIST
:
299 ret
= udmabuf_ioctl_create_list(filp
, arg
);
308 static const struct file_operations udmabuf_fops
= {
309 .owner
= THIS_MODULE
,
310 .unlocked_ioctl
= udmabuf_ioctl
,
313 static struct miscdevice udmabuf_misc
= {
314 .minor
= MISC_DYNAMIC_MINOR
,
316 .fops
= &udmabuf_fops
,
319 static int __init
udmabuf_dev_init(void)
321 return misc_register(&udmabuf_misc
);
324 static void __exit
udmabuf_dev_exit(void)
326 misc_deregister(&udmabuf_misc
);
329 module_init(udmabuf_dev_init
)
330 module_exit(udmabuf_dev_exit
)
332 MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
333 MODULE_LICENSE("GPL v2");