Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

accel/amdxdna: Support user space allocated buffer

Enhance DRM_IOCTL_AMDXDNA_CREATE_BO to accept user space allocated
buffer pointer. The buffer pages will be pinned in memory. Unless
the CAP_IPC_LOCK is enabled for the application process, the total
pinned memory can not beyond rlimit_memlock.

Reviewed-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
Signed-off-by: Lizhi Hou <lizhi.hou@amd.com>
Link: https://lore.kernel.org/r/20250716164414.112091-1-lizhi.hou@amd.com

Lizhi Hou bd72d4ac 956f82e5

+391 -25
+1
drivers/accel/amdxdna/Makefile
··· 15 15 amdxdna_mailbox_helper.o \ 16 16 amdxdna_pci_drv.o \ 17 17 amdxdna_sysfs.o \ 18 + amdxdna_ubuf.o \ 18 19 npu1_regs.o \ 19 20 npu2_regs.o \ 20 21 npu4_regs.o \
+114 -25
drivers/accel/amdxdna/amdxdna_gem.c
··· 18 18 #include "amdxdna_ctx.h" 19 19 #include "amdxdna_gem.h" 20 20 #include "amdxdna_pci_drv.h" 21 + #include "amdxdna_ubuf.h" 21 22 22 23 #define XDNA_MAX_CMD_BO_SIZE SZ_32K 23 24 ··· 297 296 298 297 vma->vm_private_data = NULL; 299 298 vma->vm_ops = NULL; 300 - ret = dma_buf_mmap(to_gobj(abo)->dma_buf, vma, 0); 299 + ret = dma_buf_mmap(abo->dma_buf, vma, 0); 301 300 if (ret) { 302 301 XDNA_ERR(xdna, "Failed to mmap dma buf %d", ret); 303 302 return ret; ··· 392 391 .vunmap = drm_gem_dmabuf_vunmap, 393 392 }; 394 393 394 + static int amdxdna_gem_obj_vmap(struct drm_gem_object *obj, struct iosys_map *map) 395 + { 396 + struct amdxdna_gem_obj *abo = to_xdna_obj(obj); 397 + 398 + iosys_map_clear(map); 399 + 400 + dma_resv_assert_held(obj->resv); 401 + 402 + if (is_import_bo(abo)) 403 + dma_buf_vmap(abo->dma_buf, map); 404 + else 405 + drm_gem_shmem_object_vmap(obj, map); 406 + 407 + if (!map->vaddr) 408 + return -ENOMEM; 409 + 410 + return 0; 411 + } 412 + 413 + static void amdxdna_gem_obj_vunmap(struct drm_gem_object *obj, struct iosys_map *map) 414 + { 415 + struct amdxdna_gem_obj *abo = to_xdna_obj(obj); 416 + 417 + dma_resv_assert_held(obj->resv); 418 + 419 + if (is_import_bo(abo)) 420 + dma_buf_vunmap(abo->dma_buf, map); 421 + else 422 + drm_gem_shmem_object_vunmap(obj, map); 423 + } 424 + 395 425 static struct dma_buf *amdxdna_gem_prime_export(struct drm_gem_object *gobj, int flags) 396 426 { 427 + struct amdxdna_gem_obj *abo = to_xdna_obj(gobj); 397 428 DEFINE_DMA_BUF_EXPORT_INFO(exp_info); 429 + 430 + if (abo->dma_buf) { 431 + get_dma_buf(abo->dma_buf); 432 + return abo->dma_buf; 433 + } 398 434 399 435 exp_info.ops = &amdxdna_dmabuf_ops; 400 436 exp_info.size = gobj->size; ··· 489 451 .pin = drm_gem_shmem_object_pin, 490 452 .unpin = drm_gem_shmem_object_unpin, 491 453 .get_sg_table = drm_gem_shmem_object_get_sg_table, 492 - .vmap = drm_gem_shmem_object_vmap, 493 - .vunmap = drm_gem_shmem_object_vunmap, 454 + .vmap = amdxdna_gem_obj_vmap, 455 + .vunmap = amdxdna_gem_obj_vunmap, 494 456 .mmap = amdxdna_gem_obj_mmap, 495 457 .vm_ops = &drm_gem_shmem_vm_ops, 496 458 .export = amdxdna_gem_prime_export, ··· 530 492 to_gobj(abo)->funcs = &amdxdna_gem_shmem_funcs; 531 493 532 494 return to_gobj(abo); 495 + } 496 + 497 + static struct amdxdna_gem_obj * 498 + amdxdna_gem_create_shmem_object(struct drm_device *dev, size_t size) 499 + { 500 + struct drm_gem_shmem_object *shmem = drm_gem_shmem_create(dev, size); 501 + 502 + if (IS_ERR(shmem)) 503 + return ERR_CAST(shmem); 504 + 505 + shmem->map_wc = false; 506 + return to_xdna_obj(&shmem->base); 507 + } 508 + 509 + static struct amdxdna_gem_obj * 510 + amdxdna_gem_create_ubuf_object(struct drm_device *dev, struct amdxdna_drm_create_bo *args) 511 + { 512 + struct amdxdna_dev *xdna = to_xdna_dev(dev); 513 + enum amdxdna_ubuf_flag flags = 0; 514 + struct amdxdna_drm_va_tbl va_tbl; 515 + struct drm_gem_object *gobj; 516 + struct dma_buf *dma_buf; 517 + 518 + if (copy_from_user(&va_tbl, u64_to_user_ptr(args->vaddr), sizeof(va_tbl))) { 519 + XDNA_DBG(xdna, "Access va table failed"); 520 + return ERR_PTR(-EINVAL); 521 + } 522 + 523 + if (va_tbl.num_entries) { 524 + if (args->type == AMDXDNA_BO_CMD) 525 + flags |= AMDXDNA_UBUF_FLAG_MAP_DMA; 526 + 527 + dma_buf = amdxdna_get_ubuf(dev, flags, va_tbl.num_entries, 528 + u64_to_user_ptr(args->vaddr + sizeof(va_tbl))); 529 + } else { 530 + dma_buf = dma_buf_get(va_tbl.dmabuf_fd); 531 + } 532 + 533 + if (IS_ERR(dma_buf)) 534 + return ERR_CAST(dma_buf); 535 + 536 + gobj = amdxdna_gem_prime_import(dev, dma_buf); 537 + if (IS_ERR(gobj)) { 538 + dma_buf_put(dma_buf); 539 + return ERR_CAST(gobj); 540 + } 541 + 542 + dma_buf_put(dma_buf); 543 + 544 + return to_xdna_obj(gobj); 545 + } 546 + 547 + static struct amdxdna_gem_obj * 548 + amdxdna_gem_create_object(struct drm_device *dev, 549 + struct amdxdna_drm_create_bo *args) 550 + { 551 + size_t aligned_sz = PAGE_ALIGN(args->size); 552 + 553 + if (args->vaddr) 554 + return amdxdna_gem_create_ubuf_object(dev, args); 555 + 556 + return amdxdna_gem_create_shmem_object(dev, aligned_sz); 533 557 } 534 558 535 559 struct drm_gem_object * ··· 645 545 struct drm_file *filp) 646 546 { 647 547 struct amdxdna_client *client = filp->driver_priv; 648 - struct drm_gem_shmem_object *shmem; 649 548 struct amdxdna_gem_obj *abo; 650 549 651 - shmem = drm_gem_shmem_create(dev, args->size); 652 - if (IS_ERR(shmem)) 653 - return ERR_CAST(shmem); 550 + abo = amdxdna_gem_create_object(dev, args); 551 + if (IS_ERR(abo)) 552 + return ERR_CAST(abo); 654 553 655 - shmem->map_wc = false; 656 - 657 - abo = to_xdna_obj(&shmem->base); 658 554 abo->client = client; 659 555 abo->type = AMDXDNA_BO_SHMEM; 660 556 ··· 665 569 struct amdxdna_client *client = filp->driver_priv; 666 570 struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL); 667 571 struct amdxdna_dev *xdna = to_xdna_dev(dev); 668 - struct drm_gem_shmem_object *shmem; 669 572 struct amdxdna_gem_obj *abo; 670 573 int ret; 671 574 ··· 681 586 goto mm_unlock; 682 587 } 683 588 684 - shmem = drm_gem_shmem_create(dev, args->size); 685 - if (IS_ERR(shmem)) { 686 - ret = PTR_ERR(shmem); 589 + abo = amdxdna_gem_create_object(dev, args); 590 + if (IS_ERR(abo)) { 591 + ret = PTR_ERR(abo); 687 592 goto mm_unlock; 688 593 } 689 594 690 - shmem->map_wc = false; 691 - abo = to_xdna_obj(&shmem->base); 692 595 abo->type = AMDXDNA_BO_DEV_HEAP; 693 596 abo->client = client; 694 597 abo->mem.dev_addr = client->xdna->dev_info->dev_mem_base; ··· 750 657 { 751 658 struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL); 752 659 struct amdxdna_dev *xdna = to_xdna_dev(dev); 753 - struct drm_gem_shmem_object *shmem; 754 660 struct amdxdna_gem_obj *abo; 755 661 int ret; 756 662 ··· 763 671 return ERR_PTR(-EINVAL); 764 672 } 765 673 766 - shmem = drm_gem_shmem_create(dev, args->size); 767 - if (IS_ERR(shmem)) 768 - return ERR_CAST(shmem); 769 - 770 - shmem->map_wc = false; 771 - abo = to_xdna_obj(&shmem->base); 674 + abo = amdxdna_gem_create_object(dev, args); 675 + if (IS_ERR(abo)) 676 + return ERR_CAST(abo); 772 677 773 678 abo->type = AMDXDNA_BO_CMD; 774 679 abo->client = filp->driver_priv; ··· 780 691 return abo; 781 692 782 693 release_obj: 783 - drm_gem_shmem_free(shmem); 694 + drm_gem_object_put(to_gobj(abo)); 784 695 return ERR_PTR(ret); 785 696 } 786 697 ··· 791 702 struct amdxdna_gem_obj *abo; 792 703 int ret; 793 704 794 - if (args->flags || args->vaddr || !args->size) 705 + if (args->flags) 795 706 return -EINVAL; 796 707 797 708 XDNA_DBG(xdna, "BO arg type %d vaddr 0x%llx size 0x%llx flags 0x%llx",
+232
drivers/accel/amdxdna/amdxdna_ubuf.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2025, Advanced Micro Devices, Inc. 4 + */ 5 + 6 + #include <drm/amdxdna_accel.h> 7 + #include <drm/drm_device.h> 8 + #include <drm/drm_print.h> 9 + #include <linux/dma-buf.h> 10 + #include <linux/pagemap.h> 11 + #include <linux/vmalloc.h> 12 + 13 + #include "amdxdna_pci_drv.h" 14 + #include "amdxdna_ubuf.h" 15 + 16 + struct amdxdna_ubuf_priv { 17 + struct page **pages; 18 + u64 nr_pages; 19 + enum amdxdna_ubuf_flag flags; 20 + struct mm_struct *mm; 21 + }; 22 + 23 + static struct sg_table *amdxdna_ubuf_map(struct dma_buf_attachment *attach, 24 + enum dma_data_direction direction) 25 + { 26 + struct amdxdna_ubuf_priv *ubuf = attach->dmabuf->priv; 27 + struct sg_table *sg; 28 + int ret; 29 + 30 + sg = kzalloc(sizeof(*sg), GFP_KERNEL); 31 + if (!sg) 32 + return ERR_PTR(-ENOMEM); 33 + 34 + ret = sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->nr_pages, 0, 35 + ubuf->nr_pages << PAGE_SHIFT, GFP_KERNEL); 36 + if (ret) 37 + return ERR_PTR(ret); 38 + 39 + if (ubuf->flags & AMDXDNA_UBUF_FLAG_MAP_DMA) { 40 + ret = dma_map_sgtable(attach->dev, sg, direction, 0); 41 + if (ret) 42 + return ERR_PTR(ret); 43 + } 44 + 45 + return sg; 46 + } 47 + 48 + static void amdxdna_ubuf_unmap(struct dma_buf_attachment *attach, 49 + struct sg_table *sg, 50 + enum dma_data_direction direction) 51 + { 52 + struct amdxdna_ubuf_priv *ubuf = attach->dmabuf->priv; 53 + 54 + if (ubuf->flags & AMDXDNA_UBUF_FLAG_MAP_DMA) 55 + dma_unmap_sgtable(attach->dev, sg, direction, 0); 56 + 57 + sg_free_table(sg); 58 + kfree(sg); 59 + } 60 + 61 + static void amdxdna_ubuf_release(struct dma_buf *dbuf) 62 + { 63 + struct amdxdna_ubuf_priv *ubuf = dbuf->priv; 64 + 65 + unpin_user_pages(ubuf->pages, ubuf->nr_pages); 66 + kvfree(ubuf->pages); 67 + atomic64_sub(ubuf->nr_pages, &ubuf->mm->pinned_vm); 68 + mmdrop(ubuf->mm); 69 + kfree(ubuf); 70 + } 71 + 72 + static vm_fault_t amdxdna_ubuf_vm_fault(struct vm_fault *vmf) 73 + { 74 + struct vm_area_struct *vma = vmf->vma; 75 + struct amdxdna_ubuf_priv *ubuf; 76 + unsigned long pfn; 77 + pgoff_t pgoff; 78 + 79 + ubuf = vma->vm_private_data; 80 + pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT; 81 + 82 + pfn = page_to_pfn(ubuf->pages[pgoff]); 83 + return vmf_insert_pfn(vma, vmf->address, pfn); 84 + } 85 + 86 + static const struct vm_operations_struct amdxdna_ubuf_vm_ops = { 87 + .fault = amdxdna_ubuf_vm_fault, 88 + }; 89 + 90 + static int amdxdna_ubuf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma) 91 + { 92 + struct amdxdna_ubuf_priv *ubuf = dbuf->priv; 93 + 94 + vma->vm_ops = &amdxdna_ubuf_vm_ops; 95 + vma->vm_private_data = ubuf; 96 + vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP); 97 + 98 + return 0; 99 + } 100 + 101 + static int amdxdna_ubuf_vmap(struct dma_buf *dbuf, struct iosys_map *map) 102 + { 103 + struct amdxdna_ubuf_priv *ubuf = dbuf->priv; 104 + void *kva; 105 + 106 + kva = vmap(ubuf->pages, ubuf->nr_pages, VM_MAP, PAGE_KERNEL); 107 + if (!kva) 108 + return -EINVAL; 109 + 110 + iosys_map_set_vaddr(map, kva); 111 + return 0; 112 + } 113 + 114 + static void amdxdna_ubuf_vunmap(struct dma_buf *dbuf, struct iosys_map *map) 115 + { 116 + vunmap(map->vaddr); 117 + } 118 + 119 + static const struct dma_buf_ops amdxdna_ubuf_dmabuf_ops = { 120 + .map_dma_buf = amdxdna_ubuf_map, 121 + .unmap_dma_buf = amdxdna_ubuf_unmap, 122 + .release = amdxdna_ubuf_release, 123 + .mmap = amdxdna_ubuf_mmap, 124 + .vmap = amdxdna_ubuf_vmap, 125 + .vunmap = amdxdna_ubuf_vunmap, 126 + }; 127 + 128 + struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev, 129 + enum amdxdna_ubuf_flag flags, 130 + u32 num_entries, void __user *va_entries) 131 + { 132 + struct amdxdna_dev *xdna = to_xdna_dev(dev); 133 + unsigned long lock_limit, new_pinned; 134 + struct amdxdna_drm_va_entry *va_ent; 135 + struct amdxdna_ubuf_priv *ubuf; 136 + u32 npages, start = 0; 137 + struct dma_buf *dbuf; 138 + int i, ret; 139 + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); 140 + 141 + if (!can_do_mlock()) 142 + return ERR_PTR(-EPERM); 143 + 144 + ubuf = kzalloc(sizeof(*ubuf), GFP_KERNEL); 145 + if (!ubuf) 146 + return ERR_PTR(-ENOMEM); 147 + 148 + ubuf->flags = flags; 149 + ubuf->mm = current->mm; 150 + mmgrab(ubuf->mm); 151 + 152 + va_ent = kvcalloc(num_entries, sizeof(*va_ent), GFP_KERNEL); 153 + if (!va_ent) { 154 + ret = -ENOMEM; 155 + goto free_ubuf; 156 + } 157 + 158 + if (copy_from_user(va_ent, va_entries, sizeof(*va_ent) * num_entries)) { 159 + XDNA_DBG(xdna, "Access va entries failed"); 160 + ret = -EINVAL; 161 + goto free_ent; 162 + } 163 + 164 + for (i = 0, exp_info.size = 0; i < num_entries; i++) { 165 + if (!IS_ALIGNED(va_ent[i].vaddr, PAGE_SIZE) || 166 + !IS_ALIGNED(va_ent[i].len, PAGE_SIZE)) { 167 + XDNA_ERR(xdna, "Invalid address or len %llx, %llx", 168 + va_ent[i].vaddr, va_ent[i].len); 169 + ret = -EINVAL; 170 + goto free_ent; 171 + } 172 + 173 + exp_info.size += va_ent[i].len; 174 + } 175 + 176 + ubuf->nr_pages = exp_info.size >> PAGE_SHIFT; 177 + lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; 178 + new_pinned = atomic64_add_return(ubuf->nr_pages, &ubuf->mm->pinned_vm); 179 + if (new_pinned > lock_limit && !capable(CAP_IPC_LOCK)) { 180 + XDNA_DBG(xdna, "New pin %ld, limit %ld, cap %d", 181 + new_pinned, lock_limit, capable(CAP_IPC_LOCK)); 182 + ret = -ENOMEM; 183 + goto sub_pin_cnt; 184 + } 185 + 186 + ubuf->pages = kvmalloc_array(ubuf->nr_pages, sizeof(*ubuf->pages), GFP_KERNEL); 187 + if (!ubuf->pages) { 188 + ret = -ENOMEM; 189 + goto sub_pin_cnt; 190 + } 191 + 192 + for (i = 0; i < num_entries; i++) { 193 + npages = va_ent[i].len >> PAGE_SHIFT; 194 + 195 + ret = pin_user_pages_fast(va_ent[i].vaddr, npages, 196 + FOLL_WRITE | FOLL_LONGTERM, 197 + &ubuf->pages[start]); 198 + if (ret < 0 || ret != npages) { 199 + ret = -ENOMEM; 200 + XDNA_ERR(xdna, "Failed to pin pages ret %d", ret); 201 + goto destroy_pages; 202 + } 203 + 204 + start += ret; 205 + } 206 + 207 + exp_info.ops = &amdxdna_ubuf_dmabuf_ops; 208 + exp_info.priv = ubuf; 209 + exp_info.flags = O_RDWR | O_CLOEXEC; 210 + 211 + dbuf = dma_buf_export(&exp_info); 212 + if (IS_ERR(dbuf)) { 213 + ret = PTR_ERR(dbuf); 214 + goto destroy_pages; 215 + } 216 + kvfree(va_ent); 217 + 218 + return dbuf; 219 + 220 + destroy_pages: 221 + if (start) 222 + unpin_user_pages(ubuf->pages, start); 223 + kvfree(ubuf->pages); 224 + sub_pin_cnt: 225 + atomic64_sub(ubuf->nr_pages, &ubuf->mm->pinned_vm); 226 + free_ent: 227 + kvfree(va_ent); 228 + free_ubuf: 229 + mmdrop(ubuf->mm); 230 + kfree(ubuf); 231 + return ERR_PTR(ret); 232 + }
+19
drivers/accel/amdxdna/amdxdna_ubuf.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2025, Advanced Micro Devices, Inc. 4 + */ 5 + #ifndef _AMDXDNA_UBUF_H_ 6 + #define _AMDXDNA_UBUF_H_ 7 + 8 + #include <drm/drm_device.h> 9 + #include <linux/dma-buf.h> 10 + 11 + enum amdxdna_ubuf_flag { 12 + AMDXDNA_UBUF_FLAG_MAP_DMA = 1, 13 + }; 14 + 15 + struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev, 16 + enum amdxdna_ubuf_flag flags, 17 + u32 num_entries, void __user *va_entries); 18 + 19 + #endif /* _AMDXDNA_UBUF_H_ */
+25
include/uapi/drm/amdxdna_accel.h
··· 154 154 }; 155 155 156 156 /** 157 + * struct amdxdna_drm_va_entry 158 + * @vaddr: Virtual address. 159 + * @len: Size of entry. 160 + */ 161 + struct amdxdna_drm_va_entry { 162 + __u64 vaddr; 163 + __u64 len; 164 + }; 165 + 166 + /** 167 + * struct amdxdna_drm_va_tbl 168 + * @dmabuf_fd: The fd of dmabuf. 169 + * @num_entries: Number of va entries. 170 + * @va_entries: Array of va entries. 171 + * 172 + * The input can be either a dmabuf fd or a virtual address entry table. 173 + * When dmabuf_fd is used, num_entries must be zero. 174 + */ 175 + struct amdxdna_drm_va_tbl { 176 + __s32 dmabuf_fd; 177 + __u32 num_entries; 178 + struct amdxdna_drm_va_entry va_entries[]; 179 + }; 180 + 181 + /** 157 182 * struct amdxdna_drm_create_bo - Create a buffer object. 158 183 * @flags: Buffer flags. MBZ. 159 184 * @vaddr: User VA of buffer if applied. MBZ.