Blame view
drivers/dma-buf/udmabuf.c
7.59 KB
fbb0de795 Add udmabuf misc ... |
1 |
// SPDX-License-Identifier: GPL-2.0 |
913965c42 udmabuf: sort hea... |
2 |
#include <linux/cred.h> |
fbb0de795 Add udmabuf misc ... |
3 |
#include <linux/device.h> |
fbb0de795 Add udmabuf misc ... |
4 5 |
#include <linux/dma-buf.h> #include <linux/highmem.h> |
913965c42 udmabuf: sort hea... |
6 7 |
#include <linux/init.h> #include <linux/kernel.h> |
fbb0de795 Add udmabuf misc ... |
8 |
#include <linux/memfd.h> |
913965c42 udmabuf: sort hea... |
9 10 11 12 13 |
#include <linux/miscdevice.h> #include <linux/module.h> #include <linux/shmem_fs.h> #include <linux/slab.h> #include <linux/udmabuf.h> |
fbb0de795 Add udmabuf misc ... |
14 |
|
dc4716d75 udmabuf: rework l... |
15 16 |
static const u32 list_limit = 1024; /* udmabuf_create_list->count limit */ static const size_t size_limit_mb = 64; /* total dmabuf size, in megabytes */ |
fbb0de795 Add udmabuf misc ... |
17 |
struct udmabuf { |
b35f57c43 udmabuf: use pgof... |
18 |
pgoff_t pagecount; |
fbb0de795 Add udmabuf misc ... |
19 |
struct page **pages; |
284562e1f udmabuf: implemen... |
20 |
struct sg_table *sg; |
c1bbed668 udmabuf: add a po... |
21 |
struct miscdevice *device; |
fbb0de795 Add udmabuf misc ... |
22 |
}; |
300133d37 drivers/dma-buf/u... |
23 |
static vm_fault_t udmabuf_vm_fault(struct vm_fault *vmf) |
fbb0de795 Add udmabuf misc ... |
24 25 26 |
{ struct vm_area_struct *vma = vmf->vma; struct udmabuf *ubuf = vma->vm_private_data; |
fbb0de795 Add udmabuf misc ... |
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
vmf->page = ubuf->pages[vmf->pgoff]; get_page(vmf->page); return 0; } static const struct vm_operations_struct udmabuf_vm_ops = { .fault = udmabuf_vm_fault, }; static int mmap_udmabuf(struct dma_buf *buf, struct vm_area_struct *vma) { struct udmabuf *ubuf = buf->priv; if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0) return -EINVAL; vma->vm_ops = &udmabuf_vm_ops; vma->vm_private_data = ubuf; return 0; } |
17a7ce203 udmabuf: separate... |
47 48 |
static struct sg_table *get_sg_table(struct device *dev, struct dma_buf *buf, enum dma_data_direction direction) |
fbb0de795 Add udmabuf misc ... |
49 |
{ |
17a7ce203 udmabuf: separate... |
50 |
struct udmabuf *ubuf = buf->priv; |
fbb0de795 Add udmabuf misc ... |
51 |
struct sg_table *sg; |
a3e722dad udmabuf: improve ... |
52 |
int ret; |
fbb0de795 Add udmabuf misc ... |
53 54 55 |
sg = kzalloc(sizeof(*sg), GFP_KERNEL); if (!sg) |
a3e722dad udmabuf: improve ... |
56 57 58 59 60 61 |
return ERR_PTR(-ENOMEM); ret = sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->pagecount, 0, ubuf->pagecount << PAGE_SHIFT, GFP_KERNEL); if (ret < 0) goto err; |
62296b395 dmabuf: fix commo... |
62 63 |
ret = dma_map_sgtable(dev, sg, direction, 0); if (ret < 0) |
a3e722dad udmabuf: improve ... |
64 |
goto err; |
fbb0de795 Add udmabuf misc ... |
65 |
return sg; |
a3e722dad udmabuf: improve ... |
66 |
err: |
fbb0de795 Add udmabuf misc ... |
67 |
sg_free_table(sg); |
fbb0de795 Add udmabuf misc ... |
68 |
kfree(sg); |
a3e722dad udmabuf: improve ... |
69 |
return ERR_PTR(ret); |
fbb0de795 Add udmabuf misc ... |
70 |
} |
17a7ce203 udmabuf: separate... |
71 72 73 |
static void put_sg_table(struct device *dev, struct sg_table *sg, enum dma_data_direction direction) { |
62296b395 dmabuf: fix commo... |
74 |
dma_unmap_sgtable(dev, sg, direction, 0); |
17a7ce203 udmabuf: separate... |
75 76 77 78 79 80 81 82 83 |
sg_free_table(sg); kfree(sg); } static struct sg_table *map_udmabuf(struct dma_buf_attachment *at, enum dma_data_direction direction) { return get_sg_table(at->dev, at->dmabuf, direction); } |
fbb0de795 Add udmabuf misc ... |
84 85 86 87 |
static void unmap_udmabuf(struct dma_buf_attachment *at, struct sg_table *sg, enum dma_data_direction direction) { |
17a7ce203 udmabuf: separate... |
88 |
return put_sg_table(at->dev, sg, direction); |
fbb0de795 Add udmabuf misc ... |
89 90 91 92 93 |
} static void release_udmabuf(struct dma_buf *buf) { struct udmabuf *ubuf = buf->priv; |
284562e1f udmabuf: implemen... |
94 |
struct device *dev = ubuf->device->this_device; |
fbb0de795 Add udmabuf misc ... |
95 |
pgoff_t pg; |
284562e1f udmabuf: implemen... |
96 97 |
if (ubuf->sg) put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL); |
fbb0de795 Add udmabuf misc ... |
98 99 100 101 102 |
for (pg = 0; pg < ubuf->pagecount; pg++) put_page(ubuf->pages[pg]); kfree(ubuf->pages); kfree(ubuf); } |
284562e1f udmabuf: implemen... |
103 104 105 106 107 108 109 110 111 112 113 |
static int begin_cpu_udmabuf(struct dma_buf *buf, enum dma_data_direction direction) { struct udmabuf *ubuf = buf->priv; struct device *dev = ubuf->device->this_device; if (!ubuf->sg) { ubuf->sg = get_sg_table(dev, buf, direction); if (IS_ERR(ubuf->sg)) return PTR_ERR(ubuf->sg); } else { |
1ffe09590 udmabuf: fix dma-... |
114 115 |
dma_sync_sg_for_cpu(dev, ubuf->sg->sgl, ubuf->sg->nents, direction); |
284562e1f udmabuf: implemen... |
116 117 118 119 120 121 122 123 124 125 126 127 128 |
} return 0; } static int end_cpu_udmabuf(struct dma_buf *buf, enum dma_data_direction direction) { struct udmabuf *ubuf = buf->priv; struct device *dev = ubuf->device->this_device; if (!ubuf->sg) return -EINVAL; |
1ffe09590 udmabuf: fix dma-... |
129 |
dma_sync_sg_for_device(dev, ubuf->sg->sgl, ubuf->sg->nents, direction); |
284562e1f udmabuf: implemen... |
130 131 |
return 0; } |
a34852891 udmabuf: constify... |
132 |
static const struct dma_buf_ops udmabuf_ops = { |
bc7a71da4 udmabuf: use cach... |
133 134 135 136 137 |
.cache_sgt_mapping = true, .map_dma_buf = map_udmabuf, .unmap_dma_buf = unmap_udmabuf, .release = release_udmabuf, .mmap = mmap_udmabuf, |
284562e1f udmabuf: implemen... |
138 139 |
.begin_cpu_access = begin_cpu_udmabuf, .end_cpu_access = end_cpu_udmabuf, |
fbb0de795 Add udmabuf misc ... |
140 141 142 143 |
}; #define SEALS_WANTED (F_SEAL_SHRINK) #define SEALS_DENIED (F_SEAL_WRITE) |
c1bbed668 udmabuf: add a po... |
144 145 146 |
static long udmabuf_create(struct miscdevice *device, struct udmabuf_create_list *head, struct udmabuf_create_item *list) |
fbb0de795 Add udmabuf misc ... |
147 148 149 150 151 |
{ DEFINE_DMA_BUF_EXPORT_INFO(exp_info); struct file *memfd = NULL; struct udmabuf *ubuf; struct dma_buf *buf; |
0d17455ca udmabuf: improve ... |
152 |
pgoff_t pgoff, pgcnt, pgidx, pgbuf = 0, pglimit; |
fbb0de795 Add udmabuf misc ... |
153 154 155 |
struct page *page; int seals, ret = -EINVAL; u32 i, flags; |
33f35429f udmabuf: use size... |
156 |
ubuf = kzalloc(sizeof(*ubuf), GFP_KERNEL); |
fbb0de795 Add udmabuf misc ... |
157 158 |
if (!ubuf) return -ENOMEM; |
dc4716d75 udmabuf: rework l... |
159 |
pglimit = (size_limit_mb * 1024 * 1024) >> PAGE_SHIFT; |
fbb0de795 Add udmabuf misc ... |
160 161 |
for (i = 0; i < head->count; i++) { if (!IS_ALIGNED(list[i].offset, PAGE_SIZE)) |
0d17455ca udmabuf: improve ... |
162 |
goto err; |
fbb0de795 Add udmabuf misc ... |
163 |
if (!IS_ALIGNED(list[i].size, PAGE_SIZE)) |
0d17455ca udmabuf: improve ... |
164 |
goto err; |
fbb0de795 Add udmabuf misc ... |
165 |
ubuf->pagecount += list[i].size >> PAGE_SHIFT; |
dc4716d75 udmabuf: rework l... |
166 |
if (ubuf->pagecount > pglimit) |
0d17455ca udmabuf: improve ... |
167 |
goto err; |
fbb0de795 Add udmabuf misc ... |
168 |
} |
33f35429f udmabuf: use size... |
169 |
ubuf->pages = kmalloc_array(ubuf->pagecount, sizeof(*ubuf->pages), |
fbb0de795 Add udmabuf misc ... |
170 171 172 |
GFP_KERNEL); if (!ubuf->pages) { ret = -ENOMEM; |
0d17455ca udmabuf: improve ... |
173 |
goto err; |
fbb0de795 Add udmabuf misc ... |
174 175 176 177 |
} pgbuf = 0; for (i = 0; i < head->count; i++) { |
7a1c67d78 udmabuf: use EBAD... |
178 |
ret = -EBADFD; |
fbb0de795 Add udmabuf misc ... |
179 180 |
memfd = fget(list[i].memfd); if (!memfd) |
0d17455ca udmabuf: improve ... |
181 |
goto err; |
fbb0de795 Add udmabuf misc ... |
182 |
if (!shmem_mapping(file_inode(memfd)->i_mapping)) |
0d17455ca udmabuf: improve ... |
183 |
goto err; |
fbb0de795 Add udmabuf misc ... |
184 |
seals = memfd_fcntl(memfd, F_GET_SEALS, 0); |
7a1c67d78 udmabuf: use EBAD... |
185 186 187 188 |
if (seals == -EINVAL) goto err; ret = -EINVAL; if ((seals & SEALS_WANTED) != SEALS_WANTED || |
fbb0de795 Add udmabuf misc ... |
189 |
(seals & SEALS_DENIED) != 0) |
0d17455ca udmabuf: improve ... |
190 |
goto err; |
fbb0de795 Add udmabuf misc ... |
191 192 193 194 195 196 197 |
pgoff = list[i].offset >> PAGE_SHIFT; pgcnt = list[i].size >> PAGE_SHIFT; for (pgidx = 0; pgidx < pgcnt; pgidx++) { page = shmem_read_mapping_page( file_inode(memfd)->i_mapping, pgoff + pgidx); if (IS_ERR(page)) { ret = PTR_ERR(page); |
0d17455ca udmabuf: improve ... |
198 |
goto err; |
fbb0de795 Add udmabuf misc ... |
199 200 201 202 |
} ubuf->pages[pgbuf++] = page; } fput(memfd); |
0d17455ca udmabuf: improve ... |
203 |
memfd = NULL; |
fbb0de795 Add udmabuf misc ... |
204 |
} |
fbb0de795 Add udmabuf misc ... |
205 206 207 208 |
exp_info.ops = &udmabuf_ops; exp_info.size = ubuf->pagecount << PAGE_SHIFT; exp_info.priv = ubuf; |
5c074eeab udmabuf: set read... |
209 |
exp_info.flags = O_RDWR; |
fbb0de795 Add udmabuf misc ... |
210 |
|
c1bbed668 udmabuf: add a po... |
211 |
ubuf->device = device; |
fbb0de795 Add udmabuf misc ... |
212 213 214 |
buf = dma_buf_export(&exp_info); if (IS_ERR(buf)) { ret = PTR_ERR(buf); |
0d17455ca udmabuf: improve ... |
215 |
goto err; |
fbb0de795 Add udmabuf misc ... |
216 217 218 219 220 221 |
} flags = 0; if (head->flags & UDMABUF_FLAGS_CLOEXEC) flags |= O_CLOEXEC; return dma_buf_fd(buf, flags); |
0d17455ca udmabuf: improve ... |
222 |
err: |
fbb0de795 Add udmabuf misc ... |
223 224 |
while (pgbuf > 0) put_page(ubuf->pages[--pgbuf]); |
683a0e630 dma-buf/udmabuf: ... |
225 226 |
if (memfd) fput(memfd); |
fbb0de795 Add udmabuf misc ... |
227 228 229 230 231 232 233 234 235 236 237 238 |
kfree(ubuf->pages); kfree(ubuf); return ret; } static long udmabuf_ioctl_create(struct file *filp, unsigned long arg) { struct udmabuf_create create; struct udmabuf_create_list head; struct udmabuf_create_item list; if (copy_from_user(&create, (void __user *)arg, |
33f35429f udmabuf: use size... |
239 |
sizeof(create))) |
fbb0de795 Add udmabuf misc ... |
240 241 242 243 244 245 246 |
return -EFAULT; head.flags = create.flags; head.count = 1; list.memfd = create.memfd; list.offset = create.offset; list.size = create.size; |
c1bbed668 udmabuf: add a po... |
247 |
return udmabuf_create(filp->private_data, &head, &list); |
fbb0de795 Add udmabuf misc ... |
248 249 250 251 252 253 254 255 256 257 258 |
} static long udmabuf_ioctl_create_list(struct file *filp, unsigned long arg) { struct udmabuf_create_list head; struct udmabuf_create_item *list; int ret = -EINVAL; u32 lsize; if (copy_from_user(&head, (void __user *)arg, sizeof(head))) return -EFAULT; |
dc4716d75 udmabuf: rework l... |
259 |
if (head.count > list_limit) |
fbb0de795 Add udmabuf misc ... |
260 261 262 263 264 |
return -EINVAL; lsize = sizeof(struct udmabuf_create_item) * head.count; list = memdup_user((void __user *)(arg + sizeof(head)), lsize); if (IS_ERR(list)) return PTR_ERR(list); |
c1bbed668 udmabuf: add a po... |
265 |
ret = udmabuf_create(filp->private_data, &head, list); |
fbb0de795 Add udmabuf misc ... |
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
kfree(list); return ret; } static long udmabuf_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { long ret; switch (ioctl) { case UDMABUF_CREATE: ret = udmabuf_ioctl_create(filp, arg); break; case UDMABUF_CREATE_LIST: ret = udmabuf_ioctl_create_list(filp, arg); break; default: |
52499d9cd udmabuf: use ENOT... |
283 |
ret = -ENOTTY; |
fbb0de795 Add udmabuf misc ... |
284 285 286 287 288 289 290 291 |
break; } return ret; } static const struct file_operations udmabuf_fops = { .owner = THIS_MODULE, .unlocked_ioctl = udmabuf_ioctl, |
d4a197f40 udmabuf: Add miss... |
292 293 294 |
#ifdef CONFIG_COMPAT .compat_ioctl = udmabuf_ioctl, #endif |
fbb0de795 Add udmabuf misc ... |
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
}; static struct miscdevice udmabuf_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "udmabuf", .fops = &udmabuf_fops, }; static int __init udmabuf_dev_init(void) { return misc_register(&udmabuf_misc); } static void __exit udmabuf_dev_exit(void) { misc_deregister(&udmabuf_misc); } module_init(udmabuf_dev_init) module_exit(udmabuf_dev_exit) MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>"); MODULE_LICENSE("GPL v2"); |