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 */
23 static vm_fault_t
udmabuf_vm_fault(struct vm_fault
*vmf
)
25 struct vm_area_struct
*vma
= vmf
->vma
;
26 struct udmabuf
*ubuf
= vma
->vm_private_data
;
28 vmf
->page
= ubuf
->pages
[vmf
->pgoff
];
33 static const struct vm_operations_struct udmabuf_vm_ops
= {
34 .fault
= udmabuf_vm_fault
,
37 static int mmap_udmabuf(struct dma_buf
*buf
, struct vm_area_struct
*vma
)
39 struct udmabuf
*ubuf
= buf
->priv
;
41 if ((vma
->vm_flags
& (VM_SHARED
| VM_MAYSHARE
)) == 0)
44 vma
->vm_ops
= &udmabuf_vm_ops
;
45 vma
->vm_private_data
= ubuf
;
49 static struct sg_table
*map_udmabuf(struct dma_buf_attachment
*at
,
50 enum dma_data_direction direction
)
52 struct udmabuf
*ubuf
= at
->dmabuf
->priv
;
56 sg
= kzalloc(sizeof(*sg
), GFP_KERNEL
);
58 return ERR_PTR(-ENOMEM
);
59 ret
= sg_alloc_table_from_pages(sg
, ubuf
->pages
, ubuf
->pagecount
,
60 0, ubuf
->pagecount
<< PAGE_SHIFT
,
64 if (!dma_map_sg(at
->dev
, sg
->sgl
, sg
->nents
, direction
)) {
76 static void unmap_udmabuf(struct dma_buf_attachment
*at
,
78 enum dma_data_direction direction
)
84 static void release_udmabuf(struct dma_buf
*buf
)
86 struct udmabuf
*ubuf
= buf
->priv
;
89 for (pg
= 0; pg
< ubuf
->pagecount
; pg
++)
90 put_page(ubuf
->pages
[pg
]);
95 static void *kmap_udmabuf(struct dma_buf
*buf
, unsigned long page_num
)
97 struct udmabuf
*ubuf
= buf
->priv
;
98 struct page
*page
= ubuf
->pages
[page_num
];
103 static void kunmap_udmabuf(struct dma_buf
*buf
, unsigned long page_num
,
109 static const struct dma_buf_ops udmabuf_ops
= {
110 .map_dma_buf
= map_udmabuf
,
111 .unmap_dma_buf
= unmap_udmabuf
,
112 .release
= release_udmabuf
,
114 .unmap
= kunmap_udmabuf
,
115 .mmap
= mmap_udmabuf
,
118 #define SEALS_WANTED (F_SEAL_SHRINK)
119 #define SEALS_DENIED (F_SEAL_WRITE)
121 static long udmabuf_create(const struct udmabuf_create_list
*head
,
122 const struct udmabuf_create_item
*list
)
124 DEFINE_DMA_BUF_EXPORT_INFO(exp_info
);
125 struct file
*memfd
= NULL
;
126 struct udmabuf
*ubuf
;
128 pgoff_t pgoff
, pgcnt
, pgidx
, pgbuf
= 0, pglimit
;
130 int seals
, ret
= -EINVAL
;
133 ubuf
= kzalloc(sizeof(*ubuf
), GFP_KERNEL
);
137 pglimit
= (size_limit_mb
* 1024 * 1024) >> PAGE_SHIFT
;
138 for (i
= 0; i
< head
->count
; i
++) {
139 if (!IS_ALIGNED(list
[i
].offset
, PAGE_SIZE
))
141 if (!IS_ALIGNED(list
[i
].size
, PAGE_SIZE
))
143 ubuf
->pagecount
+= list
[i
].size
>> PAGE_SHIFT
;
144 if (ubuf
->pagecount
> pglimit
)
147 ubuf
->pages
= kmalloc_array(ubuf
->pagecount
, sizeof(*ubuf
->pages
),
155 for (i
= 0; i
< head
->count
; i
++) {
157 memfd
= fget(list
[i
].memfd
);
160 if (!shmem_mapping(file_inode(memfd
)->i_mapping
))
162 seals
= memfd_fcntl(memfd
, F_GET_SEALS
, 0);
163 if (seals
== -EINVAL
)
166 if ((seals
& SEALS_WANTED
) != SEALS_WANTED
||
167 (seals
& SEALS_DENIED
) != 0)
169 pgoff
= list
[i
].offset
>> PAGE_SHIFT
;
170 pgcnt
= list
[i
].size
>> PAGE_SHIFT
;
171 for (pgidx
= 0; pgidx
< pgcnt
; pgidx
++) {
172 page
= shmem_read_mapping_page(
173 file_inode(memfd
)->i_mapping
, pgoff
+ pgidx
);
178 ubuf
->pages
[pgbuf
++] = page
;
184 exp_info
.ops
= &udmabuf_ops
;
185 exp_info
.size
= ubuf
->pagecount
<< PAGE_SHIFT
;
186 exp_info
.priv
= ubuf
;
187 exp_info
.flags
= O_RDWR
;
189 buf
= dma_buf_export(&exp_info
);
196 if (head
->flags
& UDMABUF_FLAGS_CLOEXEC
)
198 return dma_buf_fd(buf
, flags
);
202 put_page(ubuf
->pages
[--pgbuf
]);
210 static long udmabuf_ioctl_create(struct file
*filp
, unsigned long arg
)
212 struct udmabuf_create create
;
213 struct udmabuf_create_list head
;
214 struct udmabuf_create_item list
;
216 if (copy_from_user(&create
, (void __user
*)arg
,
220 head
.flags
= create
.flags
;
222 list
.memfd
= create
.memfd
;
223 list
.offset
= create
.offset
;
224 list
.size
= create
.size
;
226 return udmabuf_create(&head
, &list
);
229 static long udmabuf_ioctl_create_list(struct file
*filp
, unsigned long arg
)
231 struct udmabuf_create_list head
;
232 struct udmabuf_create_item
*list
;
236 if (copy_from_user(&head
, (void __user
*)arg
, sizeof(head
)))
238 if (head
.count
> list_limit
)
240 lsize
= sizeof(struct udmabuf_create_item
) * head
.count
;
241 list
= memdup_user((void __user
*)(arg
+ sizeof(head
)), lsize
);
243 return PTR_ERR(list
);
245 ret
= udmabuf_create(&head
, list
);
250 static long udmabuf_ioctl(struct file
*filp
, unsigned int ioctl
,
257 ret
= udmabuf_ioctl_create(filp
, arg
);
259 case UDMABUF_CREATE_LIST
:
260 ret
= udmabuf_ioctl_create_list(filp
, arg
);
269 static const struct file_operations udmabuf_fops
= {
270 .owner
= THIS_MODULE
,
271 .unlocked_ioctl
= udmabuf_ioctl
,
274 static struct miscdevice udmabuf_misc
= {
275 .minor
= MISC_DYNAMIC_MINOR
,
277 .fops
= &udmabuf_fops
,
280 static int __init
udmabuf_dev_init(void)
282 return misc_register(&udmabuf_misc
);
285 static void __exit
udmabuf_dev_exit(void)
287 misc_deregister(&udmabuf_misc
);
290 module_init(udmabuf_dev_init
)
291 module_exit(udmabuf_dev_exit
)
293 MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
294 MODULE_LICENSE("GPL v2");