Path: blob/master/drivers/accel/ivpu/ivpu_gem_userptr.c
38186 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2020-2025 Intel Corporation3*/45#include <linux/dma-buf.h>6#include <linux/err.h>7#include <linux/highmem.h>8#include <linux/mm.h>9#include <linux/mman.h>10#include <linux/scatterlist.h>11#include <linux/slab.h>12#include <linux/capability.h>1314#include <drm/drm_device.h>15#include <drm/drm_file.h>16#include <drm/drm_gem.h>1718#include "ivpu_drv.h"19#include "ivpu_gem.h"2021static struct sg_table *22ivpu_gem_userptr_dmabuf_map(struct dma_buf_attachment *attachment,23enum dma_data_direction direction)24{25struct sg_table *sgt = attachment->dmabuf->priv;26int ret;2728ret = dma_map_sgtable(attachment->dev, sgt, direction, DMA_ATTR_SKIP_CPU_SYNC);29if (ret)30return ERR_PTR(ret);3132return sgt;33}3435static void ivpu_gem_userptr_dmabuf_unmap(struct dma_buf_attachment *attachment,36struct sg_table *sgt,37enum dma_data_direction direction)38{39dma_unmap_sgtable(attachment->dev, sgt, direction, DMA_ATTR_SKIP_CPU_SYNC);40}4142static void ivpu_gem_userptr_dmabuf_release(struct dma_buf *dma_buf)43{44struct sg_table *sgt = dma_buf->priv;45struct sg_page_iter page_iter;46struct page *page;4748for_each_sgtable_page(sgt, &page_iter, 0) {49page = sg_page_iter_page(&page_iter);50unpin_user_page(page);51}5253sg_free_table(sgt);54kfree(sgt);55}5657static const struct dma_buf_ops ivpu_gem_userptr_dmabuf_ops = {58.map_dma_buf = ivpu_gem_userptr_dmabuf_map,59.unmap_dma_buf = ivpu_gem_userptr_dmabuf_unmap,60.release = ivpu_gem_userptr_dmabuf_release,61};6263static struct dma_buf *64ivpu_create_userptr_dmabuf(struct ivpu_device *vdev, void __user *user_ptr,65size_t size, uint32_t flags)66{67struct dma_buf_export_info exp_info = {};68struct dma_buf *dma_buf;69struct sg_table *sgt;70struct page **pages;71unsigned long nr_pages = size >> PAGE_SHIFT;72unsigned int gup_flags = FOLL_LONGTERM;73int ret, i, pinned;7475/* Add FOLL_WRITE only if the BO is not read-only */76if (!(flags & DRM_IVPU_BO_READ_ONLY))77gup_flags |= FOLL_WRITE;7879pages = kvmalloc_array(nr_pages, sizeof(*pages), GFP_KERNEL);80if (!pages)81return ERR_PTR(-ENOMEM);8283pinned = pin_user_pages_fast((unsigned long)user_ptr, nr_pages, gup_flags, pages);84if (pinned < 0) {85ret = pinned;86ivpu_dbg(vdev, IOCTL, "Failed to pin user pages: %d\n", ret);87goto free_pages_array;88}8990if (pinned != nr_pages) {91ivpu_dbg(vdev, IOCTL, "Pinned %d pages, expected %lu\n", pinned, nr_pages);92ret = -EFAULT;93goto unpin_pages;94}9596sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);97if (!sgt) {98ret = -ENOMEM;99goto unpin_pages;100}101102ret = sg_alloc_table_from_pages(sgt, pages, nr_pages, 0, size, GFP_KERNEL);103if (ret) {104ivpu_dbg(vdev, IOCTL, "Failed to create sg table: %d\n", ret);105goto free_sgt;106}107108exp_info.exp_name = "ivpu_userptr_dmabuf";109exp_info.owner = THIS_MODULE;110exp_info.ops = &ivpu_gem_userptr_dmabuf_ops;111exp_info.size = size;112exp_info.flags = O_RDWR | O_CLOEXEC;113exp_info.priv = sgt;114115dma_buf = dma_buf_export(&exp_info);116if (IS_ERR(dma_buf)) {117ret = PTR_ERR(dma_buf);118ivpu_dbg(vdev, IOCTL, "Failed to export userptr dma-buf: %d\n", ret);119goto free_sg_table;120}121122kvfree(pages);123return dma_buf;124125free_sg_table:126sg_free_table(sgt);127free_sgt:128kfree(sgt);129unpin_pages:130for (i = 0; i < pinned; i++)131unpin_user_page(pages[i]);132free_pages_array:133kvfree(pages);134return ERR_PTR(ret);135}136137static struct ivpu_bo *138ivpu_bo_create_from_userptr(struct ivpu_device *vdev, void __user *user_ptr,139size_t size, uint32_t flags)140{141struct dma_buf *dma_buf;142struct drm_gem_object *obj;143struct ivpu_bo *bo;144145dma_buf = ivpu_create_userptr_dmabuf(vdev, user_ptr, size, flags);146if (IS_ERR(dma_buf))147return ERR_CAST(dma_buf);148149obj = ivpu_gem_prime_import(&vdev->drm, dma_buf);150if (IS_ERR(obj)) {151dma_buf_put(dma_buf);152return ERR_CAST(obj);153}154155dma_buf_put(dma_buf);156157bo = to_ivpu_bo(obj);158bo->flags = flags;159160return bo;161}162163int ivpu_bo_create_from_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file)164{165struct drm_ivpu_bo_create_from_userptr *args = data;166struct ivpu_file_priv *file_priv = file->driver_priv;167struct ivpu_device *vdev = to_ivpu_device(dev);168void __user *user_ptr = u64_to_user_ptr(args->user_ptr);169struct ivpu_bo *bo;170int ret;171172if (args->flags & ~(DRM_IVPU_BO_HIGH_MEM | DRM_IVPU_BO_DMA_MEM | DRM_IVPU_BO_READ_ONLY)) {173ivpu_dbg(vdev, IOCTL, "Invalid BO flags: 0x%x\n", args->flags);174return -EINVAL;175}176177if (!args->user_ptr || !args->size) {178ivpu_dbg(vdev, IOCTL, "Userptr or size are zero: ptr %llx size %llu\n",179args->user_ptr, args->size);180return -EINVAL;181}182183if (!PAGE_ALIGNED(args->user_ptr) || !PAGE_ALIGNED(args->size)) {184ivpu_dbg(vdev, IOCTL, "Userptr or size not page aligned: ptr %llx size %llu\n",185args->user_ptr, args->size);186return -EINVAL;187}188189if (!access_ok(user_ptr, args->size)) {190ivpu_dbg(vdev, IOCTL, "Userptr is not accessible: ptr %llx size %llu\n",191args->user_ptr, args->size);192return -EFAULT;193}194195bo = ivpu_bo_create_from_userptr(vdev, user_ptr, args->size, args->flags);196if (IS_ERR(bo))197return PTR_ERR(bo);198199ret = drm_gem_handle_create(file, &bo->base.base, &args->handle);200if (ret) {201ivpu_dbg(vdev, IOCTL, "Failed to create handle for BO: %pe ctx %u size %llu flags 0x%x\n",202bo, file_priv->ctx.id, args->size, args->flags);203} else {204ivpu_dbg(vdev, BO, "Created userptr BO: handle=%u vpu_addr=0x%llx size=%llu flags=0x%x\n",205args->handle, bo->vpu_addr, args->size, bo->flags);206args->vpu_addr = bo->vpu_addr;207}208209drm_gem_object_put(&bo->base.base);210211return ret;212}213214215