diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-26 13:18:51 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-26 13:18:51 -0700 |
commit | 099bfbfc7fbbe22356c02f0caf709ac32e1126ea (patch) | |
tree | c2dfe2f9445255d866e9203cff9e9f865ef93513 /drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c | |
parent | 22165fa79814e71e7a5974b3c37a5028ed16c8f9 (diff) | |
parent | c5fd936e992dd2829167d2adc63e151675ca6898 (diff) |
Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
Pull drm updates from Dave Airlie:
"This is the main drm pull request for v4.2.
I've one other new driver from freescale on my radar, it's been posted
and reviewed, I'd just like to get someone to give it a last look, so
maybe I'll send it or maybe I'll leave it.
There is no major nouveau changes in here, Ben was working on
something big, and we agreed it was a bit late, there wasn't anything
else he considered urgent to merge.
There might be another msm pull for some bits that are waiting on
arm-soc, I'll see how we time it.
This touches some "of" stuff, acks are in place except for the fixes
to the build in various configs,t hat I just applied.
Summary:
New drivers:
- virtio-gpu:
KMS only pieces of driver for virtio-gpu in qemu.
This is just the first part of this driver, enough to run
unaccelerated userspace on. As qemu merges more we'll start
adding the 3D features for the virgl 3d work.
- amdgpu:
a new driver from AMD to driver their newer GPUs. (VI+)
It contains a new cleaner userspace API, and is a clean
break from radeon moving forward, that AMD are going to
concentrate on. It also contains a set of register headers
auto generated from AMD internal database.
core:
- atomic modesetting API completed, enabled by default now.
- Add support for mode_id blob to atomic ioctl to complete interface.
- bunch of Displayport MST fixes
- lots of misc fixes.
panel:
- new simple panels
- fix some long-standing build issues with bridge drivers
radeon:
- VCE1 support
- add a GPU reset counter for userspace
- lots of fixes.
amdkfd:
- H/W debugger support module
- static user-mode queues
- support killing all the waves when a process terminates
- use standard DECLARE_BITMAP
i915:
- Add Broxton support
- S3, rotation support for Skylake
- RPS booting tuning
- CPT modeset sequence fixes
- ns2501 dither support
- enable cmd parser on haswell
- cdclk handling fixes
- gen8 dynamic pte allocation
- lots of atomic conversion work
exynos:
- Add atomic modesetting support
- Add iommu support
- Consolidate drm driver initialization
- and MIC, DECON and MIPI-DSI support for exynos5433
omapdrm:
- atomic modesetting support (fixes lots of things in rewrite)
tegra:
- DP aux transaction fixes
- iommu support fix
msm:
- adreno a306 support
- various dsi bits
- various 64-bit fixes
- NV12MT support
rcar-du:
- atomic and misc fixes
sti:
- fix HDMI timing complaince
tilcdc:
- use drm component API to access tda998x driver
- fix module unloading
qxl:
- stability fixes"
* 'drm-next' of git://people.freedesktop.org/~airlied/linux: (872 commits)
drm/nouveau: Pause between setting gpu to D3hot and cutting the power
drm/dp/mst: close deadlock in connector destruction.
drm: Always enable atomic API
drm/vgem: Set unique to "vgem"
of: fix a build error to of_graph_get_endpoint_by_regs function
drm/dp/mst: take lock around looking up the branch device on hpd irq
drm/dp/mst: make sure mst_primary mstb is valid in work function
of: add EXPORT_SYMBOL for of_graph_get_endpoint_by_regs
ARM: dts: rename the clock of MIPI DSI 'pll_clk' to 'sclk_mipi'
drm/atomic: Don't set crtc_state->enable manually
drm/exynos: dsi: do not set TE GPIO direction by input
drm/exynos: dsi: add support for MIC driver as a bridge
drm/exynos: dsi: add support for Exynos5433
drm/exynos: dsi: make use of array for clock access
drm/exynos: dsi: make use of driver data for static values
drm/exynos: dsi: add macros for register access
drm/exynos: dsi: rename pll_clk to sclk_clk
drm/exynos: mic: add MIC driver
of: add helper for getting endpoint node of specific identifiers
drm/exynos: add Exynos5433 decon driver
...
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c new file mode 100644 index 000000000000..b1969f2b2038 --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c @@ -0,0 +1,322 @@ +/* + * 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 "amdgpu.h" + +struct amdgpu_mn { + /* constant after initialisation */ + struct amdgpu_device *adev; + struct mm_struct *mm; + struct mmu_notifier mn; + + /* only used on destruction */ + struct work_struct work; + + /* protected by adev->mn_lock */ + struct hlist_node node; + + /* objects protected by lock */ + struct mutex lock; + struct rb_root objects; +}; + +struct amdgpu_mn_node { + struct interval_tree_node it; + struct list_head bos; +}; + +/** + * amdgpu_mn_destroy - destroy the rmn + * + * @work: previously sheduled work item + * + * Lazy destroys the notifier from a work item + */ +static void amdgpu_mn_destroy(struct work_struct *work) +{ + struct amdgpu_mn *rmn = container_of(work, struct amdgpu_mn, work); + struct amdgpu_device *adev = rmn->adev; + struct amdgpu_mn_node *node, *next_node; + struct amdgpu_bo *bo, *next_bo; + + mutex_lock(&adev->mn_lock); + mutex_lock(&rmn->lock); + hash_del(&rmn->node); + rbtree_postorder_for_each_entry_safe(node, next_node, &rmn->objects, + it.rb) { + + interval_tree_remove(&node->it, &rmn->objects); + list_for_each_entry_safe(bo, next_bo, &node->bos, mn_list) { + bo->mn = NULL; + list_del_init(&bo->mn_list); + } + kfree(node); + } + mutex_unlock(&rmn->lock); + mutex_unlock(&adev->mn_lock); + mmu_notifier_unregister(&rmn->mn, rmn->mm); + kfree(rmn); +} + +/** + * amdgpu_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 amdgpu_mn_release(struct mmu_notifier *mn, + struct mm_struct *mm) +{ + struct amdgpu_mn *rmn = container_of(mn, struct amdgpu_mn, mn); + INIT_WORK(&rmn->work, amdgpu_mn_destroy); + schedule_work(&rmn->work); +} + +/** + * amdgpu_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 amdgpu_mn_invalidate_range_start(struct mmu_notifier *mn, + struct mm_struct *mm, + unsigned long start, + unsigned long end) +{ + struct amdgpu_mn *rmn = container_of(mn, struct amdgpu_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 amdgpu_mn_node *node; + struct amdgpu_bo *bo; + long r; + + node = container_of(it, struct amdgpu_mn_node, it); + it = interval_tree_iter_next(it, start, end); + + list_for_each_entry(bo, &node->bos, mn_list) { + + if (!bo->tbo.ttm || bo->tbo.ttm->state != tt_bound) + continue; + + r = amdgpu_bo_reserve(bo, true); + if (r) { + DRM_ERROR("(%ld) failed to reserve user bo\n", r); + continue; + } + + r = reservation_object_wait_timeout_rcu(bo->tbo.resv, + true, false, MAX_SCHEDULE_TIMEOUT); + if (r <= 0) + DRM_ERROR("(%ld) failed to wait for user bo\n", r); + + amdgpu_ttm_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_CPU); + r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); + if (r) + DRM_ERROR("(%ld) failed to validate user bo\n", r); + + amdgpu_bo_unreserve(bo); + } + } + + mutex_unlock(&rmn->lock); +} + +static const struct mmu_notifier_ops amdgpu_mn_ops = { + .release = amdgpu_mn_release, + .invalidate_range_start = amdgpu_mn_invalidate_range_start, +}; + +/** + * amdgpu_mn_get - create notifier context + * + * @adev: amdgpu device pointer + * + * Creates a notifier context for current->mm. + */ +static struct amdgpu_mn *amdgpu_mn_get(struct amdgpu_device *adev) +{ + struct mm_struct *mm = current->mm; + struct amdgpu_mn *rmn; + int r; + + down_write(&mm->mmap_sem); + mutex_lock(&adev->mn_lock); + + hash_for_each_possible(adev->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->adev = adev; + rmn->mm = mm; + rmn->mn.ops = &amdgpu_mn_ops; + mutex_init(&rmn->lock); + rmn->objects = RB_ROOT; + + r = __mmu_notifier_register(&rmn->mn, mm); + if (r) + goto free_rmn; + + hash_add(adev->mn_hash, &rmn->node, (unsigned long)mm); + +release_locks: + mutex_unlock(&adev->mn_lock); + up_write(&mm->mmap_sem); + + return rmn; + +free_rmn: + mutex_unlock(&adev->mn_lock); + up_write(&mm->mmap_sem); + kfree(rmn); + + return ERR_PTR(r); +} + +/** + * amdgpu_mn_register - register a BO for notifier updates + * + * @bo: amdgpu 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 amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr) +{ + unsigned long end = addr + amdgpu_bo_size(bo) - 1; + struct amdgpu_device *adev = bo->adev; + struct amdgpu_mn *rmn; + struct amdgpu_mn_node *node = NULL; + struct list_head bos; + struct interval_tree_node *it; + + rmn = amdgpu_mn_get(adev); + if (IS_ERR(rmn)) + return PTR_ERR(rmn); + + INIT_LIST_HEAD(&bos); + + mutex_lock(&rmn->lock); + + while ((it = interval_tree_iter_first(&rmn->objects, addr, end))) { + kfree(node); + node = container_of(it, struct amdgpu_mn_node, it); + interval_tree_remove(&node->it, &rmn->objects); + addr = min(it->start, addr); + end = max(it->last, end); + list_splice(&node->bos, &bos); + } + + if (!node) { + node = kmalloc(sizeof(struct amdgpu_mn_node), GFP_KERNEL); + if (!node) { + mutex_unlock(&rmn->lock); + return -ENOMEM; + } + } + + bo->mn = rmn; + + node->it.start = addr; + node->it.last = end; + INIT_LIST_HEAD(&node->bos); + list_splice(&bos, &node->bos); + list_add(&bo->mn_list, &node->bos); + + interval_tree_insert(&node->it, &rmn->objects); + + mutex_unlock(&rmn->lock); + + return 0; +} + +/** + * amdgpu_mn_unregister - unregister a BO for notifier updates + * + * @bo: amdgpu buffer object + * + * Remove any registration of MMU notifier updates from the buffer object. + */ +void amdgpu_mn_unregister(struct amdgpu_bo *bo) +{ + struct amdgpu_device *adev = bo->adev; + struct amdgpu_mn *rmn; + struct list_head *head; + + mutex_lock(&adev->mn_lock); + rmn = bo->mn; + if (rmn == NULL) { + mutex_unlock(&adev->mn_lock); + return; + } + + mutex_lock(&rmn->lock); + /* save the next list entry for later */ + head = bo->mn_list.next; + + bo->mn = NULL; + list_del(&bo->mn_list); + + if (list_empty(head)) { + struct amdgpu_mn_node *node; + node = container_of(head, struct amdgpu_mn_node, bos); + interval_tree_remove(&node->it, &rmn->objects); + kfree(node); + } + + mutex_unlock(&rmn->lock); + mutex_unlock(&adev->mn_lock); +} |