| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2025, Advanced Micro Devices, Inc.
- */
- #include <drm/amdxdna_accel.h>
- #include <drm/drm_device.h>
- #include <drm/drm_print.h>
- #include <linux/dma-buf.h>
- #include <linux/overflow.h>
- #include <linux/pagemap.h>
- #include <linux/vmalloc.h>
- #include "amdxdna_pci_drv.h"
- #include "amdxdna_ubuf.h"
- struct amdxdna_ubuf_priv {
- struct page **pages;
- u64 nr_pages;
- enum amdxdna_ubuf_flag flags;
- struct mm_struct *mm;
- };
- static struct sg_table *amdxdna_ubuf_map(struct dma_buf_attachment *attach,
- enum dma_data_direction direction)
- {
- struct amdxdna_ubuf_priv *ubuf = attach->dmabuf->priv;
- struct sg_table *sg;
- int ret;
- sg = kzalloc_obj(*sg);
- if (!sg)
- return ERR_PTR(-ENOMEM);
- ret = sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->nr_pages, 0,
- ubuf->nr_pages << PAGE_SHIFT, GFP_KERNEL);
- if (ret)
- goto err_free_sg;
- if (ubuf->flags & AMDXDNA_UBUF_FLAG_MAP_DMA) {
- ret = dma_map_sgtable(attach->dev, sg, direction, 0);
- if (ret)
- goto err_free_table;
- }
- return sg;
- err_free_table:
- sg_free_table(sg);
- err_free_sg:
- kfree(sg);
- return ERR_PTR(ret);
- }
- static void amdxdna_ubuf_unmap(struct dma_buf_attachment *attach,
- struct sg_table *sg,
- enum dma_data_direction direction)
- {
- struct amdxdna_ubuf_priv *ubuf = attach->dmabuf->priv;
- if (ubuf->flags & AMDXDNA_UBUF_FLAG_MAP_DMA)
- dma_unmap_sgtable(attach->dev, sg, direction, 0);
- sg_free_table(sg);
- kfree(sg);
- }
- static void amdxdna_ubuf_release(struct dma_buf *dbuf)
- {
- struct amdxdna_ubuf_priv *ubuf = dbuf->priv;
- unpin_user_pages(ubuf->pages, ubuf->nr_pages);
- kvfree(ubuf->pages);
- atomic64_sub(ubuf->nr_pages, &ubuf->mm->pinned_vm);
- mmdrop(ubuf->mm);
- kfree(ubuf);
- }
- static vm_fault_t amdxdna_ubuf_vm_fault(struct vm_fault *vmf)
- {
- struct vm_area_struct *vma = vmf->vma;
- struct amdxdna_ubuf_priv *ubuf;
- unsigned long pfn;
- pgoff_t pgoff;
- ubuf = vma->vm_private_data;
- pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
- pfn = page_to_pfn(ubuf->pages[pgoff]);
- return vmf_insert_pfn(vma, vmf->address, pfn);
- }
- static const struct vm_operations_struct amdxdna_ubuf_vm_ops = {
- .fault = amdxdna_ubuf_vm_fault,
- };
- static int amdxdna_ubuf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma)
- {
- struct amdxdna_ubuf_priv *ubuf = dbuf->priv;
- vma->vm_ops = &amdxdna_ubuf_vm_ops;
- vma->vm_private_data = ubuf;
- vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP);
- return 0;
- }
- static int amdxdna_ubuf_vmap(struct dma_buf *dbuf, struct iosys_map *map)
- {
- struct amdxdna_ubuf_priv *ubuf = dbuf->priv;
- void *kva;
- kva = vmap(ubuf->pages, ubuf->nr_pages, VM_MAP, PAGE_KERNEL);
- if (!kva)
- return -EINVAL;
- iosys_map_set_vaddr(map, kva);
- return 0;
- }
- static void amdxdna_ubuf_vunmap(struct dma_buf *dbuf, struct iosys_map *map)
- {
- vunmap(map->vaddr);
- }
- static const struct dma_buf_ops amdxdna_ubuf_dmabuf_ops = {
- .map_dma_buf = amdxdna_ubuf_map,
- .unmap_dma_buf = amdxdna_ubuf_unmap,
- .release = amdxdna_ubuf_release,
- .mmap = amdxdna_ubuf_mmap,
- .vmap = amdxdna_ubuf_vmap,
- .vunmap = amdxdna_ubuf_vunmap,
- };
- struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
- enum amdxdna_ubuf_flag flags,
- u32 num_entries, void __user *va_entries)
- {
- struct amdxdna_dev *xdna = to_xdna_dev(dev);
- unsigned long lock_limit, new_pinned;
- struct amdxdna_drm_va_entry *va_ent;
- struct amdxdna_ubuf_priv *ubuf;
- u32 npages, start = 0;
- struct dma_buf *dbuf;
- int i, ret;
- DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
- if (!can_do_mlock())
- return ERR_PTR(-EPERM);
- ubuf = kzalloc_obj(*ubuf);
- if (!ubuf)
- return ERR_PTR(-ENOMEM);
- ubuf->flags = flags;
- ubuf->mm = current->mm;
- mmgrab(ubuf->mm);
- va_ent = kvzalloc_objs(*va_ent, num_entries);
- if (!va_ent) {
- ret = -ENOMEM;
- goto free_ubuf;
- }
- if (copy_from_user(va_ent, va_entries, sizeof(*va_ent) * num_entries)) {
- XDNA_DBG(xdna, "Access va entries failed");
- ret = -EINVAL;
- goto free_ent;
- }
- for (i = 0, exp_info.size = 0; i < num_entries; i++) {
- if (!IS_ALIGNED(va_ent[i].vaddr, PAGE_SIZE) ||
- !IS_ALIGNED(va_ent[i].len, PAGE_SIZE)) {
- XDNA_ERR(xdna, "Invalid address or len %llx, %llx",
- va_ent[i].vaddr, va_ent[i].len);
- ret = -EINVAL;
- goto free_ent;
- }
- if (check_add_overflow(exp_info.size, va_ent[i].len, &exp_info.size)) {
- ret = -EINVAL;
- goto free_ent;
- }
- }
- ubuf->nr_pages = exp_info.size >> PAGE_SHIFT;
- lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
- new_pinned = atomic64_add_return(ubuf->nr_pages, &ubuf->mm->pinned_vm);
- if (new_pinned > lock_limit && !capable(CAP_IPC_LOCK)) {
- XDNA_DBG(xdna, "New pin %ld, limit %ld, cap %d",
- new_pinned, lock_limit, capable(CAP_IPC_LOCK));
- ret = -ENOMEM;
- goto sub_pin_cnt;
- }
- ubuf->pages = kvmalloc_objs(*ubuf->pages, ubuf->nr_pages);
- if (!ubuf->pages) {
- ret = -ENOMEM;
- goto sub_pin_cnt;
- }
- for (i = 0; i < num_entries; i++) {
- npages = va_ent[i].len >> PAGE_SHIFT;
- ret = pin_user_pages_fast(va_ent[i].vaddr, npages,
- FOLL_WRITE | FOLL_LONGTERM,
- &ubuf->pages[start]);
- if (ret < 0 || ret != npages) {
- ret = -ENOMEM;
- XDNA_ERR(xdna, "Failed to pin pages ret %d", ret);
- goto destroy_pages;
- }
- start += ret;
- }
- exp_info.ops = &amdxdna_ubuf_dmabuf_ops;
- exp_info.priv = ubuf;
- exp_info.flags = O_RDWR | O_CLOEXEC;
- dbuf = dma_buf_export(&exp_info);
- if (IS_ERR(dbuf)) {
- ret = PTR_ERR(dbuf);
- goto destroy_pages;
- }
- kvfree(va_ent);
- return dbuf;
- destroy_pages:
- if (start)
- unpin_user_pages(ubuf->pages, start);
- kvfree(ubuf->pages);
- sub_pin_cnt:
- atomic64_sub(ubuf->nr_pages, &ubuf->mm->pinned_vm);
- free_ent:
- kvfree(va_ent);
- free_ubuf:
- mmdrop(ubuf->mm);
- kfree(ubuf);
- return ERR_PTR(ret);
- }
|