Commit 484048db authored by Dave Airlie's avatar Dave Airlie

Merge branch 'drm-next-3.18' of git://people.freedesktop.org/~agd5f/linux into drm-next

radeon userptr support.

* 'drm-next-3.18' of git://people.freedesktop.org/~agd5f/linux:
  drm/radeon: allow userptr write access under certain conditions
  drm/radeon: add userptr flag to register MMU notifier v3
  drm/radeon: add userptr flag to directly validate the BO to GTT
  drm/radeon: add userptr flag to limit it to anonymous memory v2
  drm/radeon: add userptr support v8

Conflicts:
	drivers/gpu/drm/radeon/radeon_prime.c
parents d5a0f2e7 bd645e43
...@@ -116,6 +116,7 @@ config DRM_RADEON ...@@ -116,6 +116,7 @@ config DRM_RADEON
select HWMON select HWMON
select BACKLIGHT_CLASS_DEVICE select BACKLIGHT_CLASS_DEVICE
select INTERVAL_TREE select INTERVAL_TREE
select MMU_NOTIFIER
help help
Choose this option if you have an ATI Radeon graphics card. There Choose this option if you have an ATI Radeon graphics card. There
are both PCI and AGP versions. You don't need to choose this to are both PCI and AGP versions. You don't need to choose this to
......
...@@ -80,7 +80,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ ...@@ -80,7 +80,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \ r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \
rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o trinity_dpm.o \ rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o trinity_dpm.o \
trinity_smc.o ni_dpm.o si_smc.o si_dpm.o kv_smc.o kv_dpm.o ci_smc.o \ trinity_smc.o ni_dpm.o si_smc.o si_dpm.o kv_smc.o kv_dpm.o ci_smc.o \
ci_dpm.o dce6_afmt.o radeon_vm.o radeon_ucode.o radeon_ib.o ci_dpm.o dce6_afmt.o radeon_vm.o radeon_ucode.o radeon_ib.o radeon_mn.o
# add async DMA block # add async DMA block
radeon-y += \ radeon-y += \
......
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/interval_tree.h> #include <linux/interval_tree.h>
#include <linux/hashtable.h>
#include <ttm/ttm_bo_api.h> #include <ttm/ttm_bo_api.h>
#include <ttm/ttm_bo_driver.h> #include <ttm/ttm_bo_driver.h>
...@@ -488,6 +489,9 @@ struct radeon_bo { ...@@ -488,6 +489,9 @@ struct radeon_bo {
struct ttm_bo_kmap_obj dma_buf_vmap; struct ttm_bo_kmap_obj dma_buf_vmap;
pid_t pid; pid_t pid;
struct radeon_mn *mn;
struct interval_tree_node mn_it;
}; };
#define gem_to_radeon_bo(gobj) container_of((gobj), struct radeon_bo, gem_base) #define gem_to_radeon_bo(gobj) container_of((gobj), struct radeon_bo, gem_base)
...@@ -1728,6 +1732,11 @@ void radeon_test_ring_sync(struct radeon_device *rdev, ...@@ -1728,6 +1732,11 @@ void radeon_test_ring_sync(struct radeon_device *rdev,
struct radeon_ring *cpB); struct radeon_ring *cpB);
void radeon_test_syncing(struct radeon_device *rdev); void radeon_test_syncing(struct radeon_device *rdev);
/*
* MMU Notifier
*/
int radeon_mn_register(struct radeon_bo *bo, unsigned long addr);
void radeon_mn_unregister(struct radeon_bo *bo);
/* /*
* Debugfs * Debugfs
...@@ -2141,6 +2150,8 @@ int radeon_gem_info_ioctl(struct drm_device *dev, void *data, ...@@ -2141,6 +2150,8 @@ int radeon_gem_info_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp); struct drm_file *filp);
int radeon_gem_create_ioctl(struct drm_device *dev, void *data, int radeon_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp); struct drm_file *filp);
int radeon_gem_userptr_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp);
int radeon_gem_pin_ioctl(struct drm_device *dev, void *data, int radeon_gem_pin_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv); struct drm_file *file_priv);
int radeon_gem_unpin_ioctl(struct drm_device *dev, void *data, int radeon_gem_unpin_ioctl(struct drm_device *dev, void *data,
...@@ -2373,6 +2384,9 @@ struct radeon_device { ...@@ -2373,6 +2384,9 @@ struct radeon_device {
/* tracking pinned memory */ /* tracking pinned memory */
u64 vram_pin_size; u64 vram_pin_size;
u64 gart_pin_size; u64 gart_pin_size;
struct mutex mn_lock;
DECLARE_HASHTABLE(mn_hash, 7);
}; };
bool radeon_is_px(struct drm_device *dev); bool radeon_is_px(struct drm_device *dev);
...@@ -2874,6 +2888,10 @@ extern void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enabl ...@@ -2874,6 +2888,10 @@ extern void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enabl
extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable); extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable);
extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain); extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain);
extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo); extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo);
extern int radeon_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr,
uint32_t flags);
extern bool radeon_ttm_tt_has_userptr(struct ttm_tt *ttm);
extern bool radeon_ttm_tt_is_readonly(struct ttm_tt *ttm);
extern void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 base); extern void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 base);
extern void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc); extern void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
extern int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon); extern int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
......
...@@ -78,7 +78,8 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p) ...@@ -78,7 +78,8 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
struct radeon_cs_chunk *chunk; struct radeon_cs_chunk *chunk;
struct radeon_cs_buckets buckets; struct radeon_cs_buckets buckets;
unsigned i, j; unsigned i, j;
bool duplicate; bool duplicate, need_mmap_lock = false;
int r;
if (p->chunk_relocs_idx == -1) { if (p->chunk_relocs_idx == -1) {
return 0; return 0;
...@@ -165,6 +166,19 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p) ...@@ -165,6 +166,19 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
p->relocs[i].allowed_domains = domain; p->relocs[i].allowed_domains = domain;
} }
if (radeon_ttm_tt_has_userptr(p->relocs[i].robj->tbo.ttm)) {
uint32_t domain = p->relocs[i].prefered_domains;
if (!(domain & RADEON_GEM_DOMAIN_GTT)) {
DRM_ERROR("Only RADEON_GEM_DOMAIN_GTT is "
"allowed for userptr BOs\n");
return -EINVAL;
}
need_mmap_lock = true;
domain = RADEON_GEM_DOMAIN_GTT;
p->relocs[i].prefered_domains = domain;
p->relocs[i].allowed_domains = domain;
}
p->relocs[i].tv.bo = &p->relocs[i].robj->tbo; p->relocs[i].tv.bo = &p->relocs[i].robj->tbo;
p->relocs[i].handle = r->handle; p->relocs[i].handle = r->handle;
...@@ -177,8 +191,15 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p) ...@@ -177,8 +191,15 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
if (p->cs_flags & RADEON_CS_USE_VM) if (p->cs_flags & RADEON_CS_USE_VM)
p->vm_bos = radeon_vm_get_bos(p->rdev, p->ib.vm, p->vm_bos = radeon_vm_get_bos(p->rdev, p->ib.vm,
&p->validated); &p->validated);
if (need_mmap_lock)
down_read(&current->mm->mmap_sem);
r = radeon_bo_list_validate(p->rdev, &p->ticket, &p->validated, p->ring);
return radeon_bo_list_validate(p->rdev, &p->ticket, &p->validated, p->ring); if (need_mmap_lock)
up_read(&current->mm->mmap_sem);
return r;
} }
static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority) static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority)
......
...@@ -1270,6 +1270,8 @@ int radeon_device_init(struct radeon_device *rdev, ...@@ -1270,6 +1270,8 @@ int radeon_device_init(struct radeon_device *rdev,
init_rwsem(&rdev->pm.mclk_lock); init_rwsem(&rdev->pm.mclk_lock);
init_rwsem(&rdev->exclusive_lock); init_rwsem(&rdev->exclusive_lock);
init_waitqueue_head(&rdev->irq.vblank_queue); init_waitqueue_head(&rdev->irq.vblank_queue);
mutex_init(&rdev->mn_lock);
hash_init(rdev->mn_hash);
r = radeon_gem_init(rdev); r = radeon_gem_init(rdev);
if (r) if (r)
return r; return r;
......
...@@ -114,6 +114,9 @@ int radeon_gem_object_open(struct drm_gem_object *obj, ...@@ -114,6 +114,9 @@ int radeon_gem_object_open(struct drm_gem_object *obj,
struct drm_file *file_priv); struct drm_file *file_priv);
void radeon_gem_object_close(struct drm_gem_object *obj, void radeon_gem_object_close(struct drm_gem_object *obj,
struct drm_file *file_priv); struct drm_file *file_priv);
struct dma_buf *radeon_gem_prime_export(struct drm_device *dev,
struct drm_gem_object *gobj,
int flags);
extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc,
unsigned int flags, unsigned int flags,
int *vpos, int *hpos, ktime_t *stime, int *vpos, int *hpos, ktime_t *stime,
...@@ -573,7 +576,7 @@ static struct drm_driver kms_driver = { ...@@ -573,7 +576,7 @@ static struct drm_driver kms_driver = {
.prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle, .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_export = drm_gem_prime_export, .gem_prime_export = radeon_gem_prime_export,
.gem_prime_import = drm_gem_prime_import, .gem_prime_import = drm_gem_prime_import,
.gem_prime_pin = radeon_gem_prime_pin, .gem_prime_pin = radeon_gem_prime_pin,
.gem_prime_unpin = radeon_gem_prime_unpin, .gem_prime_unpin = radeon_gem_prime_unpin,
......
...@@ -272,6 +272,94 @@ int radeon_gem_create_ioctl(struct drm_device *dev, void *data, ...@@ -272,6 +272,94 @@ int radeon_gem_create_ioctl(struct drm_device *dev, void *data,
return 0; return 0;
} }
int radeon_gem_userptr_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp)
{
struct radeon_device *rdev = dev->dev_private;
struct drm_radeon_gem_userptr *args = data;
struct drm_gem_object *gobj;
struct radeon_bo *bo;
uint32_t handle;
int r;
if (offset_in_page(args->addr | args->size))
return -EINVAL;
/* reject unknown flag values */
if (args->flags & ~(RADEON_GEM_USERPTR_READONLY |
RADEON_GEM_USERPTR_ANONONLY | RADEON_GEM_USERPTR_VALIDATE |
RADEON_GEM_USERPTR_REGISTER))
return -EINVAL;
if (args->flags & RADEON_GEM_USERPTR_READONLY) {
/* readonly pages not tested on older hardware */
if (rdev->family < CHIP_R600)
return -EINVAL;
} else if (!(args->flags & RADEON_GEM_USERPTR_ANONONLY) ||
!(args->flags & RADEON_GEM_USERPTR_REGISTER)) {
/* if we want to write to it we must require anonymous
memory and install a MMU notifier */
return -EACCES;
}
down_read(&rdev->exclusive_lock);
/* create a gem object to contain this object in */
r = radeon_gem_object_create(rdev, args->size, 0,
RADEON_GEM_DOMAIN_CPU, 0,
false, &gobj);
if (r)
goto handle_lockup;
bo = gem_to_radeon_bo(gobj);
r = radeon_ttm_tt_set_userptr(bo->tbo.ttm, args->addr, args->flags);
if (r)
goto release_object;
if (args->flags & RADEON_GEM_USERPTR_REGISTER) {
r = radeon_mn_register(bo, args->addr);
if (r)
goto release_object;
}
if (args->flags & RADEON_GEM_USERPTR_VALIDATE) {
down_read(&current->mm->mmap_sem);
r = radeon_bo_reserve(bo, true);
if (r) {
up_read(&current->mm->mmap_sem);
goto release_object;
}
radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_GTT);
r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false);
radeon_bo_unreserve(bo);
up_read(&current->mm->mmap_sem);
if (r)
goto release_object;
}
r = drm_gem_handle_create(filp, gobj, &handle);
/* drop reference from allocate - handle holds it now */
drm_gem_object_unreference_unlocked(gobj);
if (r)
goto handle_lockup;
args->handle = handle;
up_read(&rdev->exclusive_lock);
return 0;
release_object:
drm_gem_object_unreference_unlocked(gobj);
handle_lockup:
up_read(&rdev->exclusive_lock);
r = radeon_gem_handle_lockup(rdev, r);
return r;
}
int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data, int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp) struct drm_file *filp)
{ {
...@@ -315,6 +403,10 @@ int radeon_mode_dumb_mmap(struct drm_file *filp, ...@@ -315,6 +403,10 @@ int radeon_mode_dumb_mmap(struct drm_file *filp,
return -ENOENT; return -ENOENT;
} }
robj = gem_to_radeon_bo(gobj); robj = gem_to_radeon_bo(gobj);
if (radeon_ttm_tt_has_userptr(robj->tbo.ttm)) {
drm_gem_object_unreference_unlocked(gobj);
return -EPERM;
}
*offset_p = radeon_bo_mmap_offset(robj); *offset_p = radeon_bo_mmap_offset(robj);
drm_gem_object_unreference_unlocked(gobj); drm_gem_object_unreference_unlocked(gobj);
return 0; return 0;
...@@ -532,6 +624,11 @@ int radeon_gem_op_ioctl(struct drm_device *dev, void *data, ...@@ -532,6 +624,11 @@ int radeon_gem_op_ioctl(struct drm_device *dev, void *data,
return -ENOENT; return -ENOENT;
} }
robj = gem_to_radeon_bo(gobj); robj = gem_to_radeon_bo(gobj);
r = -EPERM;
if (radeon_ttm_tt_has_userptr(robj->tbo.ttm))
goto out;
r = radeon_bo_reserve(robj, false); r = radeon_bo_reserve(robj, false);
if (unlikely(r)) if (unlikely(r))
goto out; goto out;
......
...@@ -885,5 +885,6 @@ const struct drm_ioctl_desc radeon_ioctls_kms[] = { ...@@ -885,5 +885,6 @@ const struct drm_ioctl_desc radeon_ioctls_kms[] = {
DRM_IOCTL_DEF_DRV(RADEON_GEM_BUSY, radeon_gem_busy_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(RADEON_GEM_BUSY, radeon_gem_busy_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(RADEON_GEM_VA, radeon_gem_va_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(RADEON_GEM_VA, radeon_gem_va_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(RADEON_GEM_OP, radeon_gem_op_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(RADEON_GEM_OP, radeon_gem_op_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(RADEON_GEM_USERPTR, radeon_gem_userptr_ioctl, DRM_AUTH|DRM_UNLOCKED|DRM_RENDER_ALLOW),
}; };
int radeon_max_kms_ioctl = ARRAY_SIZE(radeon_ioctls_kms); int radeon_max_kms_ioctl = ARRAY_SIZE(radeon_ioctls_kms);
/*
* 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/firmware.h>
#include <linux/module.h>
#include <linux/mmu_notifier.h>
#include <drm/drmP.h>
#include <drm/drm.h>
#include "radeon.h"
struct radeon_mn {
/* constant after initialisation */
struct radeon_device *rdev;
struct mm_struct *mm;
struct mmu_notifier mn;
/* only used on destruction */
struct work_struct work;
/* protected by rdev->mn_lock */
struct hlist_node node;
/* objects protected by lock */
struct mutex lock;
struct rb_root objects;
};
/**
* radeon_mn_destroy - destroy the rmn
*
* @work: previously sheduled work item
*
* Lazy destroys the notifier from a work item
*/
static void radeon_mn_destroy(struct work_struct *work)
{
struct radeon_mn *rmn = container_of(work, struct radeon_mn, work);
struct radeon_device *rdev = rmn->rdev;
struct radeon_bo *bo, *next;
mutex_lock(&rdev->mn_lock);
mutex_lock(&rmn->lock);
hash_del(&rmn->node);
rbtree_postorder_for_each_entry_safe(bo, next, &rmn->objects, mn_it.rb) {
interval_tree_remove(&bo->mn_it, &rmn->objects);
bo->mn = NULL;
}
mutex_unlock(&rmn->lock);
mutex_unlock(&rdev->mn_lock);
mmu_notifier_unregister(&rmn->mn, rmn->mm);
kfree(rmn);
}
/**
* radeon_mn_release - callback to notify about mm destruction
*
* @mn: our notifier
* @mn: the mm this callback is about
*
* Shedule a work item to lazy destroy our notifier.
*/
static void radeon_mn_release(struct mmu_notifier *mn,
struct mm_struct *mm)
{
struct radeon_mn *rmn = container_of(mn, struct radeon_mn, mn);
INIT_WORK(&rmn->work, radeon_mn_destroy);
schedule_work(&rmn->work);
}
/**
* radeon_mn_invalidate_range_start - callback to notify about mm change
*
* @mn: our notifier
* @mn: the mm this callback is about
* @start: start of updated range
* @end: end of updated range
*
* We block for all BOs between start and end to be idle and
* unmap them by move them into system domain again.
*/
static void radeon_mn_invalidate_range_start(struct mmu_notifier *mn,
struct mm_struct *mm,
unsigned long start,
unsigned long end)
{
struct radeon_mn *rmn = container_of(mn, struct radeon_mn, mn);
struct interval_tree_node *it;
/* notification is exclusive, but interval is inclusive */
end -= 1;
mutex_lock(&rmn->lock);
it = interval_tree_iter_first(&rmn->objects, start, end);
while (it) {
struct radeon_bo *bo;
int r;
bo = container_of(it, struct radeon_bo, mn_it);
it = interval_tree_iter_next(it, start, end);
r = radeon_bo_reserve(bo, true);
if (r) {
DRM_ERROR("(%d) failed to reserve user bo\n", r);
continue;
}
if (bo->tbo.sync_obj) {
r = radeon_fence_wait(bo->tbo.sync_obj, false);
if (r)
DRM_ERROR("(%d) failed to wait for user bo\n", r);
}
radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_CPU);
r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
if (r)
DRM_ERROR("(%d) failed to validate user bo\n", r);
radeon_bo_unreserve(bo);
}
mutex_unlock(&rmn->lock);
}
static const struct mmu_notifier_ops radeon_mn_ops = {
.release = radeon_mn_release,
.invalidate_range_start = radeon_mn_invalidate_range_start,
};
/**
* radeon_mn_get - create notifier context
*
* @rdev: radeon device pointer
*
* Creates a notifier context for current->mm.
*/
static struct radeon_mn *radeon_mn_get(struct radeon_device *rdev)
{
struct mm_struct *mm = current->mm;
struct radeon_mn *rmn;
int r;
down_write(&mm->mmap_sem);
mutex_lock(&rdev->mn_lock);
hash_for_each_possible(rdev->mn_hash, rmn, node, (unsigned long)mm)
if (rmn->mm == mm)
goto release_locks;
rmn = kzalloc(sizeof(*rmn), GFP_KERNEL);
if (!rmn) {
rmn = ERR_PTR(-ENOMEM);
goto release_locks;
}
rmn->rdev = rdev;
rmn->mm = mm;
rmn->mn.ops = &radeon_mn_ops;
mutex_init(&rmn->lock);
rmn->objects = RB_ROOT;
r = __mmu_notifier_register(&rmn->mn, mm);
if (r)
goto free_rmn;
hash_add(rdev->mn_hash, &rmn->node, (unsigned long)mm);
release_locks:
mutex_unlock(&rdev->mn_lock);
up_write(&mm->mmap_sem);
return rmn;
free_rmn:
mutex_unlock(&rdev->mn_lock);
up_write(&mm->mmap_sem);
kfree(rmn);
return ERR_PTR(r);
}
/**
* radeon_mn_register - register a BO for notifier updates
*
* @bo: radeon buffer object
* @addr: userptr addr we should monitor
*
* Registers an MMU notifier for the given BO at the specified address.
* Returns 0 on success, -ERRNO if anything goes wrong.
*/
int radeon_mn_register(struct radeon_bo *bo, unsigned long addr)
{
unsigned long end = addr + radeon_bo_size(bo) - 1;
struct radeon_device *rdev = bo->rdev;
struct radeon_mn *rmn;
struct interval_tree_node *it;
rmn = radeon_mn_get(rdev);
if (IS_ERR(rmn))
return PTR_ERR(rmn);
mutex_lock(&rmn->lock);
it = interval_tree_iter_first(&rmn->objects, addr, end);
if (it) {
mutex_unlock(&rmn->lock);
return -EEXIST;
}
bo->mn = rmn;
bo->mn_it.start = addr;
bo->mn_it.last = end;
interval_tree_insert(&bo->mn_it, &rmn->objects);
mutex_unlock(&rmn->lock);
return 0;
}
/**
* radeon_mn_unregister - unregister a BO for notifier updates
*
* @bo: radeon buffer object
*
* Remove any registration of MMU notifier updates from the buffer object.
*/
void radeon_mn_unregister(struct radeon_bo *bo)
{
struct radeon_device *rdev = bo->rdev;
struct radeon_mn *rmn;
mutex_lock(&rdev->mn_lock);
rmn = bo->mn;
if (rmn == NULL) {
mutex_unlock(&rdev->mn_lock);
return;
}
mutex_lock(&rmn->lock);
interval_tree_remove(&bo->mn_it, &rmn->objects);
bo->mn = NULL;
mutex_unlock(&rmn->lock);
mutex_unlock(&rdev->mn_lock);
}
...@@ -75,6 +75,7 @@ static void radeon_ttm_bo_destroy(struct ttm_buffer_object *tbo) ...@@ -75,6 +75,7 @@ static void radeon_ttm_bo_destroy(struct ttm_buffer_object *tbo)
bo = container_of(tbo, struct radeon_bo, tbo); bo = container_of(tbo, struct radeon_bo, tbo);
radeon_update_memory_usage(bo, bo->tbo.mem.mem_type, -1); radeon_update_memory_usage(bo, bo->tbo.mem.mem_type, -1);
radeon_mn_unregister(bo);
mutex_lock(&bo->rdev->gem.mutex); mutex_lock(&bo->rdev->gem.mutex);
list_del_init(&bo->list); list_del_init(&bo->list);
...@@ -264,6 +265,9 @@ int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain, u64 max_offset, ...@@ -264,6 +265,9 @@ int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain, u64 max_offset,
{ {
int r, i; int r, i;
if (radeon_ttm_tt_has_userptr(bo->tbo.ttm))
return -EPERM;
if (bo->pin_count) { if (bo->pin_count) {
bo->pin_count++; bo->pin_count++;
if (gpu_addr) if (gpu_addr)
......
...@@ -111,3 +111,13 @@ struct reservation_object *radeon_gem_prime_res_obj(struct drm_gem_object *obj) ...@@ -111,3 +111,13 @@ struct reservation_object *radeon_gem_prime_res_obj(struct drm_gem_object *obj)
return bo->tbo.resv; return bo->tbo.resv;
} }
struct dma_buf *radeon_gem_prime_export(struct drm_device *dev,
struct drm_gem_object *gobj,
int flags)
{
struct radeon_bo *bo = gem_to_radeon_bo(gobj);
if (radeon_ttm_tt_has_userptr(bo->tbo.ttm))
return ERR_PTR(-EPERM);
return drm_gem_prime_export(dev, gobj, flags);
}
...@@ -39,6 +39,8 @@ ...@@ -39,6 +39,8 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/swiotlb.h> #include <linux/swiotlb.h>
#include <linux/swap.h>
#include <linux/pagemap.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include "radeon_reg.h" #include "radeon_reg.h"
#include "radeon.h" #include "radeon.h"
...@@ -515,8 +517,102 @@ struct radeon_ttm_tt { ...@@ -515,8 +517,102 @@ struct radeon_ttm_tt {
struct ttm_dma_tt ttm; struct ttm_dma_tt ttm;
struct radeon_device *rdev; struct radeon_device *rdev;
u64 offset; u64 offset;
uint64_t userptr;
struct mm_struct *usermm;
uint32_t userflags;
}; };
/* prepare the sg table with the user pages */
static int radeon_ttm_tt_pin_userptr(struct ttm_tt *ttm)
{
struct radeon_device *rdev = radeon_get_rdev(ttm->bdev);
struct radeon_ttm_tt *gtt = (void *)ttm;
unsigned pinned = 0, nents;
int r;
int write = !(gtt->userflags & RADEON_GEM_USERPTR_READONLY);
enum dma_data_direction direction = write ?
DMA_BIDIRECTIONAL : DMA_TO_DEVICE;
if (current->mm != gtt->usermm)
return -EPERM;
if (gtt->userflags & RADEON_GEM_USERPTR_ANONONLY) {
/* check that we only pin down anonymous memory
to prevent problems with writeback */
unsigned long end = gtt->userptr + ttm->num_pages * PAGE_SIZE;
struct vm_area_struct *vma;
vma = find_vma(gtt->usermm, gtt->userptr);
if (!vma || vma->vm_file || vma->vm_end < end)
return -EPERM;
}
do {
unsigned num_pages = ttm->num_pages - pinned;
uint64_t userptr = gtt->userptr + pinned * PAGE_SIZE;
struct page **pages = ttm->pages + pinned;
r = get_user_pages(current, current->mm, userptr, num_pages,
write, 0, pages, NULL);
if (r < 0)
goto release_pages;
pinned += r;
} while (pinned < ttm->num_pages);
r = sg_alloc_table_from_pages(ttm->sg, ttm->pages, ttm->num_pages, 0,
ttm->num_pages << PAGE_SHIFT,
GFP_KERNEL);
if (r)
goto release_sg;
r = -ENOMEM;
nents = dma_map_sg(rdev->dev, ttm->sg->sgl, ttm->sg->nents, direction);
if (nents != ttm->sg->nents)
goto release_sg;
drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages,
gtt->ttm.dma_address, ttm->num_pages);
return 0;
release_sg:
kfree(ttm->sg);
release_pages:
release_pages(ttm->pages, pinned, 0);
return r;
}
static void radeon_ttm_tt_unpin_userptr(struct ttm_tt *ttm)
{
struct radeon_device *rdev = radeon_get_rdev(ttm->bdev);
struct radeon_ttm_tt *gtt = (void *)ttm;
struct scatterlist *sg;
int i;
int write = !(gtt->userflags & RADEON_GEM_USERPTR_READONLY);
enum dma_data_direction direction = write ?
DMA_BIDIRECTIONAL : DMA_TO_DEVICE;
/* free the sg table and pages again */
dma_unmap_sg(rdev->dev, ttm->sg->sgl, ttm->sg->nents, direction);
for_each_sg(ttm->sg->sgl, sg, ttm->sg->nents, i) {
struct page *page = sg_page(sg);
if (!(gtt->userflags & RADEON_GEM_USERPTR_READONLY))
set_page_dirty(page);
mark_page_accessed(page);
page_cache_release(page);
}
sg_free_table(ttm->sg);
}
static int radeon_ttm_backend_bind(struct ttm_tt *ttm, static int radeon_ttm_backend_bind(struct ttm_tt *ttm,
struct ttm_mem_reg *bo_mem) struct ttm_mem_reg *bo_mem)
{ {
...@@ -525,6 +621,11 @@ static int radeon_ttm_backend_bind(struct ttm_tt *ttm, ...@@ -525,6 +621,11 @@ static int radeon_ttm_backend_bind(struct ttm_tt *ttm,
RADEON_GART_PAGE_WRITE; RADEON_GART_PAGE_WRITE;
int r; int r;
if (gtt->userptr) {
radeon_ttm_tt_pin_userptr(ttm);
flags &= ~RADEON_GART_PAGE_WRITE;
}
gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT); gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT);
if (!ttm->num_pages) { if (!ttm->num_pages) {
WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n", WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n",
...@@ -547,6 +648,10 @@ static int radeon_ttm_backend_unbind(struct ttm_tt *ttm) ...@@ -547,6 +648,10 @@ static int radeon_ttm_backend_unbind(struct ttm_tt *ttm)
struct radeon_ttm_tt *gtt = (void *)ttm; struct radeon_ttm_tt *gtt = (void *)ttm;
radeon_gart_unbind(gtt->rdev, gtt->offset, ttm->num_pages); radeon_gart_unbind(gtt->rdev, gtt->offset, ttm->num_pages);
if (gtt->userptr)
radeon_ttm_tt_unpin_userptr(ttm);
return 0; return 0;
} }
...@@ -603,6 +708,16 @@ static int radeon_ttm_tt_populate(struct ttm_tt *ttm) ...@@ -603,6 +708,16 @@ static int radeon_ttm_tt_populate(struct ttm_tt *ttm)
if (ttm->state != tt_unpopulated) if (ttm->state != tt_unpopulated)
return 0; return 0;
if (gtt->userptr) {
ttm->sg = kcalloc(1, sizeof(struct sg_table), GFP_KERNEL);
if (!ttm->sg)
return -ENOMEM;
ttm->page_flags |= TTM_PAGE_FLAG_SG;
ttm->state = tt_unbound;
return 0;
}
if (slave && ttm->sg) { if (slave && ttm->sg) {
drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages, drm_prime_sg_to_page_addr_arrays(ttm->sg, ttm->pages,
gtt->ttm.dma_address, ttm->num_pages); gtt->ttm.dma_address, ttm->num_pages);
...@@ -652,6 +767,12 @@ static void radeon_ttm_tt_unpopulate(struct ttm_tt *ttm) ...@@ -652,6 +767,12 @@ static void radeon_ttm_tt_unpopulate(struct ttm_tt *ttm)
unsigned i; unsigned i;
bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG);
if (gtt->userptr) {
kfree(ttm->sg);
ttm->page_flags &= ~TTM_PAGE_FLAG_SG;
return;
}
if (slave) if (slave)
return; return;
...@@ -680,6 +801,40 @@ static void radeon_ttm_tt_unpopulate(struct ttm_tt *ttm) ...@@ -680,6 +801,40 @@ static void radeon_ttm_tt_unpopulate(struct ttm_tt *ttm)
ttm_pool_unpopulate(ttm); ttm_pool_unpopulate(ttm);
} }
int radeon_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr,
uint32_t flags)
{
struct radeon_ttm_tt *gtt = (void *)ttm;
if (gtt == NULL)
return -EINVAL;
gtt->userptr = addr;
gtt->usermm = current->mm;
gtt->userflags = flags;
return 0;
}
bool radeon_ttm_tt_has_userptr(struct ttm_tt *ttm)
{
struct radeon_ttm_tt *gtt = (void *)ttm;
if (gtt == NULL)
return false;
return !!gtt->userptr;
}
bool radeon_ttm_tt_is_readonly(struct ttm_tt *ttm)
{
struct radeon_ttm_tt *gtt = (void *)ttm;
if (gtt == NULL)
return false;
return !!(gtt->userflags & RADEON_GEM_USERPTR_READONLY);
}
static struct ttm_bo_driver radeon_bo_driver = { static struct ttm_bo_driver radeon_bo_driver = {
.ttm_tt_create = &radeon_ttm_tt_create, .ttm_tt_create = &radeon_ttm_tt_create,
.ttm_tt_populate = &radeon_ttm_tt_populate, .ttm_tt_populate = &radeon_ttm_tt_populate,
......
...@@ -892,6 +892,9 @@ int radeon_vm_bo_update(struct radeon_device *rdev, ...@@ -892,6 +892,9 @@ int radeon_vm_bo_update(struct radeon_device *rdev,
bo_va->flags &= ~RADEON_VM_PAGE_VALID; bo_va->flags &= ~RADEON_VM_PAGE_VALID;
bo_va->flags &= ~RADEON_VM_PAGE_SYSTEM; bo_va->flags &= ~RADEON_VM_PAGE_SYSTEM;
bo_va->flags &= ~RADEON_VM_PAGE_SNOOPED; bo_va->flags &= ~RADEON_VM_PAGE_SNOOPED;
if (bo_va->bo && radeon_ttm_tt_is_readonly(bo_va->bo->tbo.ttm))
bo_va->flags &= ~RADEON_VM_PAGE_WRITEABLE;
if (mem) { if (mem) {
addr = mem->start << PAGE_SHIFT; addr = mem->start << PAGE_SHIFT;
if (mem->mem_type != TTM_PL_SYSTEM) { if (mem->mem_type != TTM_PL_SYSTEM) {
......
...@@ -511,6 +511,7 @@ typedef struct { ...@@ -511,6 +511,7 @@ typedef struct {
#define DRM_RADEON_GEM_BUSY 0x2a #define DRM_RADEON_GEM_BUSY 0x2a
#define DRM_RADEON_GEM_VA 0x2b #define DRM_RADEON_GEM_VA 0x2b
#define DRM_RADEON_GEM_OP 0x2c #define DRM_RADEON_GEM_OP 0x2c
#define DRM_RADEON_GEM_USERPTR 0x2d
#define DRM_IOCTL_RADEON_CP_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_INIT, drm_radeon_init_t) #define DRM_IOCTL_RADEON_CP_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_INIT, drm_radeon_init_t)
#define DRM_IOCTL_RADEON_CP_START DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_START) #define DRM_IOCTL_RADEON_CP_START DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_START)
...@@ -554,6 +555,7 @@ typedef struct { ...@@ -554,6 +555,7 @@ typedef struct {
#define DRM_IOCTL_RADEON_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_BUSY, struct drm_radeon_gem_busy) #define DRM_IOCTL_RADEON_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_BUSY, struct drm_radeon_gem_busy)
#define DRM_IOCTL_RADEON_GEM_VA DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_VA, struct drm_radeon_gem_va) #define DRM_IOCTL_RADEON_GEM_VA DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_VA, struct drm_radeon_gem_va)
#define DRM_IOCTL_RADEON_GEM_OP DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_OP, struct drm_radeon_gem_op) #define DRM_IOCTL_RADEON_GEM_OP DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_OP, struct drm_radeon_gem_op)
#define DRM_IOCTL_RADEON_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_USERPTR, struct drm_radeon_gem_userptr)
typedef struct drm_radeon_init { typedef struct drm_radeon_init {
enum { enum {
...@@ -808,6 +810,23 @@ struct drm_radeon_gem_create { ...@@ -808,6 +810,23 @@ struct drm_radeon_gem_create {
uint32_t flags; uint32_t flags;
}; };
/*
* This is not a reliable API and you should expect it to fail for any
* number of reasons and have fallback path that do not use userptr to
* perform any operation.
*/
#define RADEON_GEM_USERPTR_READONLY (1 << 0)
#define RADEON_GEM_USERPTR_ANONONLY (1 << 1)
#define RADEON_GEM_USERPTR_VALIDATE (1 << 2)
#define RADEON_GEM_USERPTR_REGISTER (1 << 3)
struct drm_radeon_gem_userptr {
uint64_t addr;
uint64_t size;
uint32_t flags;
uint32_t handle;
};
#define RADEON_TILING_MACRO 0x1 #define RADEON_TILING_MACRO 0x1
#define RADEON_TILING_MICRO 0x2 #define RADEON_TILING_MICRO 0x2
#define RADEON_TILING_SWAP_16BIT 0x4 #define RADEON_TILING_SWAP_16BIT 0x4
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment