mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-29 05:44:11 +00:00
23852bec53
- Various cleanup and small features for rtrs - kmap_local_page() conversions - Driver updates and fixes for: efa, rxe, mlx5, hfi1, qed, hns - Cache the IB subnet prefix - Rework how CRC is calcuated in rxe - Clean reference counting in iwpm's netlink - Pull object allocation and lifecycle for user QPs to the uverbs core code - Several small hns features and continued general code cleanups - Fix the scatterlist confusion of orig_nents/nents introduced in an earlier patch creating the append operation -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEfB7FMLh+8QxL+6i3OG33FX4gmxoFAmEudRgACgkQOG33FX4g mxraJA//c6bMxrrTVrzmrtrkyYD4tYWE8RDfgvoyZtleZnnEOJeunCQWakQrpJSv ukSnOGCA3PtnmRMdV54f/11YJ/7otxOJodSO7jWsIoBrqG/lISAdX8mn2iHhrvJ0 dIaFEFPLy0WqoMLCJVIYIupR0IStVHb/mWx0uYL4XnnoYKyt7f7K5JMZpNWMhDN2 ieJw0jfrvEYm8pipWuxUvB16XARlzAWQrjqLpMRI+jFRpbDVBY21dz2/LJvOJPrA LcQ+XXsV/F659ibOAGm6bU4BMda8fE6Lw90B/gmhSswJ205NrdziF5cNYHP0QxcN oMjrjSWWHc9GEE7MTipC2AH8e36qob16Q7CK+zHEJ+ds7R6/O/8XmED1L8/KFpNA FGqnjxnxsl1y27mUegfj1Hh8PfoDp2oVq0lmpEw0CYo4cfVzHSMRrbTR//XmW628 Ie/mJddpFK4oLk+QkSNjSLrnxOvdTkdA58PU0i84S5eUVMNm41jJDkxg2J7vp0Zn sclZsclhUQ9oJ5Q2so81JMWxu4JDn7IByXL0ULBaa6xwQTiVEnyvSxSuPlflhLRW 0vI2ylATYKyWkQqyX7VyWecZJzwhwZj5gMMWmoGsij8bkZhQ/VaQMaesByzSth+h NV5UAYax4GqyOQ/tg/tqT6e5nrI1zof87H64XdTCBpJ7kFyQ/oA= =ZwOe -----END PGP SIGNATURE----- Merge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rdma/rdma Pull rdma updates from Jason Gunthorpe: "This is quite a small cycle, no major series stands out. The HNS and rxe drivers saw the most activity this cycle, with rxe being broken for a good chunk of time. The significant deleted line count is due to a SPDX cleanup series. Summary: - Various cleanup and small features for rtrs - kmap_local_page() conversions - Driver updates and fixes for: efa, rxe, mlx5, hfi1, qed, hns - Cache the IB subnet prefix - Rework how CRC is calcuated in rxe - Clean reference counting in iwpm's netlink - Pull object allocation and lifecycle for user QPs to the uverbs core code - Several small hns features and continued general code cleanups - Fix the scatterlist confusion of orig_nents/nents introduced in an earlier patch creating the append operation" * tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rdma/rdma: (90 commits) RDMA/mlx5: Relax DCS QP creation checks RDMA/hns: Delete unnecessary blank lines. RDMA/hns: Encapsulate the qp db as a function RDMA/hns: Adjust the order in which irq are requested and enabled RDMA/hns: Remove RST2RST error prints for hw v1 RDMA/hns: Remove dqpn filling when modify qp from Init to Init RDMA/hns: Fix QP's resp incomplete assignment RDMA/hns: Fix query destination qpn RDMA/hfi1: Convert to SPDX identifier IB/rdmavt: Convert to SPDX identifier RDMA/hns: Bugfix for incorrect association between dip_idx and dgid RDMA/hns: Bugfix for the missing assignment for dip_idx RDMA/hns: Bugfix for data type of dip_idx RDMA/hns: Fix incorrect lsn field RDMA/irdma: Remove the repeated declaration RDMA/core/sa_query: Retry SA queries RDMA: Use the sg_table directly and remove the opencoded version from umem lib/scatterlist: Fix wrong update of orig_nents lib/scatterlist: Provide a dedicated function to support table append RDMA/hns: Delete unused hns bitmap interface ...
591 lines
15 KiB
C
591 lines
15 KiB
C
/*
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
* Copyright © 2012-2014 Intel Corporation
|
|
*
|
|
* Based on amdgpu_mn, which bears the following notice:
|
|
*
|
|
* Copyright 2014 Advanced Micro Devices, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sub license, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* The above copyright notice and this permission notice (including the
|
|
* next paragraph) shall be included in all copies or substantial portions
|
|
* of the Software.
|
|
*
|
|
*/
|
|
/*
|
|
* Authors:
|
|
* Christian König <christian.koenig@amd.com>
|
|
*/
|
|
|
|
#include <linux/mmu_context.h>
|
|
#include <linux/mempolicy.h>
|
|
#include <linux/swap.h>
|
|
#include <linux/sched/mm.h>
|
|
|
|
#include "i915_drv.h"
|
|
#include "i915_gem_ioctls.h"
|
|
#include "i915_gem_object.h"
|
|
#include "i915_scatterlist.h"
|
|
|
|
#ifdef CONFIG_MMU_NOTIFIER
|
|
|
|
/**
|
|
* i915_gem_userptr_invalidate - callback to notify about mm change
|
|
*
|
|
* @mni: the range (mm) is about to update
|
|
* @range: details on the invalidation
|
|
* @cur_seq: Value to pass to mmu_interval_set_seq()
|
|
*
|
|
* Block for operations on BOs to finish and mark pages as accessed and
|
|
* potentially dirty.
|
|
*/
|
|
static bool i915_gem_userptr_invalidate(struct mmu_interval_notifier *mni,
|
|
const struct mmu_notifier_range *range,
|
|
unsigned long cur_seq)
|
|
{
|
|
struct drm_i915_gem_object *obj = container_of(mni, struct drm_i915_gem_object, userptr.notifier);
|
|
struct drm_i915_private *i915 = to_i915(obj->base.dev);
|
|
long r;
|
|
|
|
if (!mmu_notifier_range_blockable(range))
|
|
return false;
|
|
|
|
write_lock(&i915->mm.notifier_lock);
|
|
|
|
mmu_interval_set_seq(mni, cur_seq);
|
|
|
|
write_unlock(&i915->mm.notifier_lock);
|
|
|
|
/*
|
|
* We don't wait when the process is exiting. This is valid
|
|
* because the object will be cleaned up anyway.
|
|
*
|
|
* This is also temporarily required as a hack, because we
|
|
* cannot currently force non-consistent batch buffers to preempt
|
|
* and reschedule by waiting on it, hanging processes on exit.
|
|
*/
|
|
if (current->flags & PF_EXITING)
|
|
return true;
|
|
|
|
/* we will unbind on next submission, still have userptr pins */
|
|
r = dma_resv_wait_timeout(obj->base.resv, true, false,
|
|
MAX_SCHEDULE_TIMEOUT);
|
|
if (r <= 0)
|
|
drm_err(&i915->drm, "(%ld) failed to wait for idle\n", r);
|
|
|
|
return true;
|
|
}
|
|
|
|
static const struct mmu_interval_notifier_ops i915_gem_userptr_notifier_ops = {
|
|
.invalidate = i915_gem_userptr_invalidate,
|
|
};
|
|
|
|
static int
|
|
i915_gem_userptr_init__mmu_notifier(struct drm_i915_gem_object *obj)
|
|
{
|
|
return mmu_interval_notifier_insert(&obj->userptr.notifier, current->mm,
|
|
obj->userptr.ptr, obj->base.size,
|
|
&i915_gem_userptr_notifier_ops);
|
|
}
|
|
|
|
static void i915_gem_object_userptr_drop_ref(struct drm_i915_gem_object *obj)
|
|
{
|
|
struct page **pvec = NULL;
|
|
|
|
assert_object_held_shared(obj);
|
|
|
|
if (!--obj->userptr.page_ref) {
|
|
pvec = obj->userptr.pvec;
|
|
obj->userptr.pvec = NULL;
|
|
}
|
|
GEM_BUG_ON(obj->userptr.page_ref < 0);
|
|
|
|
if (pvec) {
|
|
const unsigned long num_pages = obj->base.size >> PAGE_SHIFT;
|
|
|
|
unpin_user_pages(pvec, num_pages);
|
|
kvfree(pvec);
|
|
}
|
|
}
|
|
|
|
static int i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
|
|
{
|
|
const unsigned long num_pages = obj->base.size >> PAGE_SHIFT;
|
|
unsigned int max_segment = i915_sg_segment_size();
|
|
struct sg_table *st;
|
|
unsigned int sg_page_sizes;
|
|
struct page **pvec;
|
|
int ret;
|
|
|
|
st = kmalloc(sizeof(*st), GFP_KERNEL);
|
|
if (!st)
|
|
return -ENOMEM;
|
|
|
|
if (!obj->userptr.page_ref) {
|
|
ret = -EAGAIN;
|
|
goto err_free;
|
|
}
|
|
|
|
obj->userptr.page_ref++;
|
|
pvec = obj->userptr.pvec;
|
|
|
|
alloc_table:
|
|
ret = sg_alloc_table_from_pages_segment(st, pvec, num_pages, 0,
|
|
num_pages << PAGE_SHIFT,
|
|
max_segment, GFP_KERNEL);
|
|
if (ret)
|
|
goto err;
|
|
|
|
ret = i915_gem_gtt_prepare_pages(obj, st);
|
|
if (ret) {
|
|
sg_free_table(st);
|
|
|
|
if (max_segment > PAGE_SIZE) {
|
|
max_segment = PAGE_SIZE;
|
|
goto alloc_table;
|
|
}
|
|
|
|
goto err;
|
|
}
|
|
|
|
sg_page_sizes = i915_sg_dma_sizes(st->sgl);
|
|
|
|
__i915_gem_object_set_pages(obj, st, sg_page_sizes);
|
|
|
|
return 0;
|
|
|
|
err:
|
|
i915_gem_object_userptr_drop_ref(obj);
|
|
err_free:
|
|
kfree(st);
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
i915_gem_userptr_put_pages(struct drm_i915_gem_object *obj,
|
|
struct sg_table *pages)
|
|
{
|
|
struct sgt_iter sgt_iter;
|
|
struct page *page;
|
|
|
|
if (!pages)
|
|
return;
|
|
|
|
__i915_gem_object_release_shmem(obj, pages, true);
|
|
i915_gem_gtt_finish_pages(obj, pages);
|
|
|
|
/*
|
|
* We always mark objects as dirty when they are used by the GPU,
|
|
* just in case. However, if we set the vma as being read-only we know
|
|
* that the object will never have been written to.
|
|
*/
|
|
if (i915_gem_object_is_readonly(obj))
|
|
obj->mm.dirty = false;
|
|
|
|
for_each_sgt_page(page, sgt_iter, pages) {
|
|
if (obj->mm.dirty && trylock_page(page)) {
|
|
/*
|
|
* As this may not be anonymous memory (e.g. shmem)
|
|
* but exist on a real mapping, we have to lock
|
|
* the page in order to dirty it -- holding
|
|
* the page reference is not sufficient to
|
|
* prevent the inode from being truncated.
|
|
* Play safe and take the lock.
|
|
*
|
|
* However...!
|
|
*
|
|
* The mmu-notifier can be invalidated for a
|
|
* migrate_page, that is alreadying holding the lock
|
|
* on the page. Such a try_to_unmap() will result
|
|
* in us calling put_pages() and so recursively try
|
|
* to lock the page. We avoid that deadlock with
|
|
* a trylock_page() and in exchange we risk missing
|
|
* some page dirtying.
|
|
*/
|
|
set_page_dirty(page);
|
|
unlock_page(page);
|
|
}
|
|
|
|
mark_page_accessed(page);
|
|
}
|
|
obj->mm.dirty = false;
|
|
|
|
sg_free_table(pages);
|
|
kfree(pages);
|
|
|
|
i915_gem_object_userptr_drop_ref(obj);
|
|
}
|
|
|
|
static int i915_gem_object_userptr_unbind(struct drm_i915_gem_object *obj)
|
|
{
|
|
struct sg_table *pages;
|
|
int err;
|
|
|
|
err = i915_gem_object_unbind(obj, I915_GEM_OBJECT_UNBIND_ACTIVE);
|
|
if (err)
|
|
return err;
|
|
|
|
if (GEM_WARN_ON(i915_gem_object_has_pinned_pages(obj)))
|
|
return -EBUSY;
|
|
|
|
assert_object_held(obj);
|
|
|
|
pages = __i915_gem_object_unset_pages(obj);
|
|
if (!IS_ERR_OR_NULL(pages))
|
|
i915_gem_userptr_put_pages(obj, pages);
|
|
|
|
return err;
|
|
}
|
|
|
|
int i915_gem_object_userptr_submit_init(struct drm_i915_gem_object *obj)
|
|
{
|
|
const unsigned long num_pages = obj->base.size >> PAGE_SHIFT;
|
|
struct page **pvec;
|
|
unsigned int gup_flags = 0;
|
|
unsigned long notifier_seq;
|
|
int pinned, ret;
|
|
|
|
if (obj->userptr.notifier.mm != current->mm)
|
|
return -EFAULT;
|
|
|
|
notifier_seq = mmu_interval_read_begin(&obj->userptr.notifier);
|
|
|
|
ret = i915_gem_object_lock_interruptible(obj, NULL);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (notifier_seq == obj->userptr.notifier_seq && obj->userptr.pvec) {
|
|
i915_gem_object_unlock(obj);
|
|
return 0;
|
|
}
|
|
|
|
ret = i915_gem_object_userptr_unbind(obj);
|
|
i915_gem_object_unlock(obj);
|
|
if (ret)
|
|
return ret;
|
|
|
|
pvec = kvmalloc_array(num_pages, sizeof(struct page *), GFP_KERNEL);
|
|
if (!pvec)
|
|
return -ENOMEM;
|
|
|
|
if (!i915_gem_object_is_readonly(obj))
|
|
gup_flags |= FOLL_WRITE;
|
|
|
|
pinned = ret = 0;
|
|
while (pinned < num_pages) {
|
|
ret = pin_user_pages_fast(obj->userptr.ptr + pinned * PAGE_SIZE,
|
|
num_pages - pinned, gup_flags,
|
|
&pvec[pinned]);
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
pinned += ret;
|
|
}
|
|
ret = 0;
|
|
|
|
ret = i915_gem_object_lock_interruptible(obj, NULL);
|
|
if (ret)
|
|
goto out;
|
|
|
|
if (mmu_interval_read_retry(&obj->userptr.notifier,
|
|
!obj->userptr.page_ref ? notifier_seq :
|
|
obj->userptr.notifier_seq)) {
|
|
ret = -EAGAIN;
|
|
goto out_unlock;
|
|
}
|
|
|
|
if (!obj->userptr.page_ref++) {
|
|
obj->userptr.pvec = pvec;
|
|
obj->userptr.notifier_seq = notifier_seq;
|
|
pvec = NULL;
|
|
ret = ____i915_gem_object_get_pages(obj);
|
|
}
|
|
|
|
obj->userptr.page_ref--;
|
|
|
|
out_unlock:
|
|
i915_gem_object_unlock(obj);
|
|
|
|
out:
|
|
if (pvec) {
|
|
unpin_user_pages(pvec, pinned);
|
|
kvfree(pvec);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int i915_gem_object_userptr_submit_done(struct drm_i915_gem_object *obj)
|
|
{
|
|
if (mmu_interval_read_retry(&obj->userptr.notifier,
|
|
obj->userptr.notifier_seq)) {
|
|
/* We collided with the mmu notifier, need to retry */
|
|
|
|
return -EAGAIN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int i915_gem_object_userptr_validate(struct drm_i915_gem_object *obj)
|
|
{
|
|
int err;
|
|
|
|
err = i915_gem_object_userptr_submit_init(obj);
|
|
if (err)
|
|
return err;
|
|
|
|
err = i915_gem_object_lock_interruptible(obj, NULL);
|
|
if (!err) {
|
|
/*
|
|
* Since we only check validity, not use the pages,
|
|
* it doesn't matter if we collide with the mmu notifier,
|
|
* and -EAGAIN handling is not required.
|
|
*/
|
|
err = i915_gem_object_pin_pages(obj);
|
|
if (!err)
|
|
i915_gem_object_unpin_pages(obj);
|
|
|
|
i915_gem_object_unlock(obj);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static void
|
|
i915_gem_userptr_release(struct drm_i915_gem_object *obj)
|
|
{
|
|
GEM_WARN_ON(obj->userptr.page_ref);
|
|
|
|
mmu_interval_notifier_remove(&obj->userptr.notifier);
|
|
obj->userptr.notifier.mm = NULL;
|
|
}
|
|
|
|
static int
|
|
i915_gem_userptr_dmabuf_export(struct drm_i915_gem_object *obj)
|
|
{
|
|
drm_dbg(obj->base.dev, "Exporting userptr no longer allowed\n");
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
i915_gem_userptr_pwrite(struct drm_i915_gem_object *obj,
|
|
const struct drm_i915_gem_pwrite *args)
|
|
{
|
|
drm_dbg(obj->base.dev, "pwrite to userptr no longer allowed\n");
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
i915_gem_userptr_pread(struct drm_i915_gem_object *obj,
|
|
const struct drm_i915_gem_pread *args)
|
|
{
|
|
drm_dbg(obj->base.dev, "pread from userptr no longer allowed\n");
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static const struct drm_i915_gem_object_ops i915_gem_userptr_ops = {
|
|
.name = "i915_gem_object_userptr",
|
|
.flags = I915_GEM_OBJECT_IS_SHRINKABLE |
|
|
I915_GEM_OBJECT_NO_MMAP |
|
|
I915_GEM_OBJECT_IS_PROXY,
|
|
.get_pages = i915_gem_userptr_get_pages,
|
|
.put_pages = i915_gem_userptr_put_pages,
|
|
.dmabuf_export = i915_gem_userptr_dmabuf_export,
|
|
.pwrite = i915_gem_userptr_pwrite,
|
|
.pread = i915_gem_userptr_pread,
|
|
.release = i915_gem_userptr_release,
|
|
};
|
|
|
|
#endif
|
|
|
|
static int
|
|
probe_range(struct mm_struct *mm, unsigned long addr, unsigned long len)
|
|
{
|
|
const unsigned long end = addr + len;
|
|
struct vm_area_struct *vma;
|
|
int ret = -EFAULT;
|
|
|
|
mmap_read_lock(mm);
|
|
for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) {
|
|
/* Check for holes, note that we also update the addr below */
|
|
if (vma->vm_start > addr)
|
|
break;
|
|
|
|
if (vma->vm_flags & (VM_PFNMAP | VM_MIXEDMAP))
|
|
break;
|
|
|
|
if (vma->vm_end >= end) {
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
addr = vma->vm_end;
|
|
}
|
|
mmap_read_unlock(mm);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Creates a new mm object that wraps some normal memory from the process
|
|
* context - user memory.
|
|
*
|
|
* We impose several restrictions upon the memory being mapped
|
|
* into the GPU.
|
|
* 1. It must be page aligned (both start/end addresses, i.e ptr and size).
|
|
* 2. It must be normal system memory, not a pointer into another map of IO
|
|
* space (e.g. it must not be a GTT mmapping of another object).
|
|
* 3. We only allow a bo as large as we could in theory map into the GTT,
|
|
* that is we limit the size to the total size of the GTT.
|
|
* 4. The bo is marked as being snoopable. The backing pages are left
|
|
* accessible directly by the CPU, but reads and writes by the GPU may
|
|
* incur the cost of a snoop (unless you have an LLC architecture).
|
|
*
|
|
* Synchronisation between multiple users and the GPU is left to userspace
|
|
* through the normal set-domain-ioctl. The kernel will enforce that the
|
|
* GPU relinquishes the VMA before it is returned back to the system
|
|
* i.e. upon free(), munmap() or process termination. However, the userspace
|
|
* malloc() library may not immediately relinquish the VMA after free() and
|
|
* instead reuse it whilst the GPU is still reading and writing to the VMA.
|
|
* Caveat emptor.
|
|
*
|
|
* Also note, that the object created here is not currently a "first class"
|
|
* object, in that several ioctls are banned. These are the CPU access
|
|
* ioctls: mmap(), pwrite and pread. In practice, you are expected to use
|
|
* direct access via your pointer rather than use those ioctls. Another
|
|
* restriction is that we do not allow userptr surfaces to be pinned to the
|
|
* hardware and so we reject any attempt to create a framebuffer out of a
|
|
* userptr.
|
|
*
|
|
* If you think this is a good interface to use to pass GPU memory between
|
|
* drivers, please use dma-buf instead. In fact, wherever possible use
|
|
* dma-buf instead.
|
|
*/
|
|
int
|
|
i915_gem_userptr_ioctl(struct drm_device *dev,
|
|
void *data,
|
|
struct drm_file *file)
|
|
{
|
|
static struct lock_class_key __maybe_unused lock_class;
|
|
struct drm_i915_private *dev_priv = to_i915(dev);
|
|
struct drm_i915_gem_userptr *args = data;
|
|
struct drm_i915_gem_object __maybe_unused *obj;
|
|
int __maybe_unused ret;
|
|
u32 __maybe_unused handle;
|
|
|
|
if (!HAS_LLC(dev_priv) && !HAS_SNOOP(dev_priv)) {
|
|
/* We cannot support coherent userptr objects on hw without
|
|
* LLC and broken snooping.
|
|
*/
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (args->flags & ~(I915_USERPTR_READ_ONLY |
|
|
I915_USERPTR_UNSYNCHRONIZED |
|
|
I915_USERPTR_PROBE))
|
|
return -EINVAL;
|
|
|
|
if (i915_gem_object_size_2big(args->user_size))
|
|
return -E2BIG;
|
|
|
|
if (!args->user_size)
|
|
return -EINVAL;
|
|
|
|
if (offset_in_page(args->user_ptr | args->user_size))
|
|
return -EINVAL;
|
|
|
|
if (!access_ok((char __user *)(unsigned long)args->user_ptr, args->user_size))
|
|
return -EFAULT;
|
|
|
|
if (args->flags & I915_USERPTR_UNSYNCHRONIZED)
|
|
return -ENODEV;
|
|
|
|
if (args->flags & I915_USERPTR_READ_ONLY) {
|
|
/*
|
|
* On almost all of the older hw, we cannot tell the GPU that
|
|
* a page is readonly.
|
|
*/
|
|
if (!dev_priv->gt.vm->has_read_only)
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (args->flags & I915_USERPTR_PROBE) {
|
|
/*
|
|
* Check that the range pointed to represents real struct
|
|
* pages and not iomappings (at this moment in time!)
|
|
*/
|
|
ret = probe_range(current->mm, args->user_ptr, args->user_size);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_MMU_NOTIFIER
|
|
obj = i915_gem_object_alloc();
|
|
if (obj == NULL)
|
|
return -ENOMEM;
|
|
|
|
drm_gem_private_object_init(dev, &obj->base, args->user_size);
|
|
i915_gem_object_init(obj, &i915_gem_userptr_ops, &lock_class, 0);
|
|
obj->mem_flags = I915_BO_FLAG_STRUCT_PAGE;
|
|
obj->read_domains = I915_GEM_DOMAIN_CPU;
|
|
obj->write_domain = I915_GEM_DOMAIN_CPU;
|
|
i915_gem_object_set_cache_coherency(obj, I915_CACHE_LLC);
|
|
|
|
obj->userptr.ptr = args->user_ptr;
|
|
obj->userptr.notifier_seq = ULONG_MAX;
|
|
if (args->flags & I915_USERPTR_READ_ONLY)
|
|
i915_gem_object_set_readonly(obj);
|
|
|
|
/* And keep a pointer to the current->mm for resolving the user pages
|
|
* at binding. This means that we need to hook into the mmu_notifier
|
|
* in order to detect if the mmu is destroyed.
|
|
*/
|
|
ret = i915_gem_userptr_init__mmu_notifier(obj);
|
|
if (ret == 0)
|
|
ret = drm_gem_handle_create(file, &obj->base, &handle);
|
|
|
|
/* drop reference from allocate - handle holds it now */
|
|
i915_gem_object_put(obj);
|
|
if (ret)
|
|
return ret;
|
|
|
|
args->handle = handle;
|
|
return 0;
|
|
#else
|
|
return -ENODEV;
|
|
#endif
|
|
}
|
|
|
|
int i915_gem_init_userptr(struct drm_i915_private *dev_priv)
|
|
{
|
|
#ifdef CONFIG_MMU_NOTIFIER
|
|
rwlock_init(&dev_priv->mm.notifier_lock);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
void i915_gem_cleanup_userptr(struct drm_i915_private *dev_priv)
|
|
{
|
|
}
|