linux/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c
<<
>>
Prefs
   1/*
   2 * Copyright 2014 Advanced Micro Devices, Inc.
   3 * All Rights Reserved.
   4 *
   5 * Permission is hereby granted, free of charge, to any person obtaining a
   6 * copy of this software and associated documentation files (the
   7 * "Software"), to deal in the Software without restriction, including
   8 * without limitation the rights to use, copy, modify, merge, publish,
   9 * distribute, sub license, and/or sell copies of the Software, and to
  10 * permit persons to whom the Software is furnished to do so, subject to
  11 * the following conditions:
  12 *
  13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
  16 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
  17 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  18 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  19 * USE OR OTHER DEALINGS IN THE SOFTWARE.
  20 *
  21 * The above copyright notice and this permission notice (including the
  22 * next paragraph) shall be included in all copies or substantial portions
  23 * of the Software.
  24 *
  25 */
  26/*
  27 * Authors:
  28 *    Christian König <christian.koenig@amd.com>
  29 */
  30
  31/**
  32 * DOC: MMU Notifier
  33 *
  34 * For coherent userptr handling registers an MMU notifier to inform the driver
  35 * about updates on the page tables of a process.
  36 *
  37 * When somebody tries to invalidate the page tables we block the update until
  38 * all operations on the pages in question are completed, then those pages are
  39 * marked as accessed and also dirty if it wasn't a read only access.
  40 *
  41 * New command submissions using the userptrs in question are delayed until all
  42 * page table invalidation are completed and we once more see a coherent process
  43 * address space.
  44 */
  45
  46#include <linux/firmware.h>
  47#include <linux/module.h>
  48#include <drm/drm.h>
  49
  50#include "amdgpu.h"
  51#include "amdgpu_amdkfd.h"
  52
  53/**
  54 * amdgpu_mn_invalidate_gfx - callback to notify about mm change
  55 *
  56 * @mni: the range (mm) is about to update
  57 * @range: details on the invalidation
  58 * @cur_seq: Value to pass to mmu_interval_set_seq()
  59 *
  60 * Block for operations on BOs to finish and mark pages as accessed and
  61 * potentially dirty.
  62 */
  63static bool amdgpu_mn_invalidate_gfx(struct mmu_interval_notifier *mni,
  64                                     const struct mmu_notifier_range *range,
  65                                     unsigned long cur_seq)
  66{
  67        struct amdgpu_bo *bo = container_of(mni, struct amdgpu_bo, notifier);
  68        struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
  69        long r;
  70
  71        if (!mmu_notifier_range_blockable(range))
  72                return false;
  73
  74        mutex_lock(&adev->notifier_lock);
  75
  76        mmu_interval_set_seq(mni, cur_seq);
  77
  78        r = dma_resv_wait_timeout_rcu(bo->tbo.base.resv, true, false,
  79                                      MAX_SCHEDULE_TIMEOUT);
  80        mutex_unlock(&adev->notifier_lock);
  81        if (r <= 0)
  82                DRM_ERROR("(%ld) failed to wait for user bo\n", r);
  83        return true;
  84}
  85
  86static const struct mmu_interval_notifier_ops amdgpu_mn_gfx_ops = {
  87        .invalidate = amdgpu_mn_invalidate_gfx,
  88};
  89
  90/**
  91 * amdgpu_mn_invalidate_hsa - callback to notify about mm change
  92 *
  93 * @mni: the range (mm) is about to update
  94 * @range: details on the invalidation
  95 * @cur_seq: Value to pass to mmu_interval_set_seq()
  96 *
  97 * We temporarily evict the BO attached to this range. This necessitates
  98 * evicting all user-mode queues of the process.
  99 */
 100static bool amdgpu_mn_invalidate_hsa(struct mmu_interval_notifier *mni,
 101                                     const struct mmu_notifier_range *range,
 102                                     unsigned long cur_seq)
 103{
 104        struct amdgpu_bo *bo = container_of(mni, struct amdgpu_bo, notifier);
 105        struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
 106
 107        if (!mmu_notifier_range_blockable(range))
 108                return false;
 109
 110        mutex_lock(&adev->notifier_lock);
 111
 112        mmu_interval_set_seq(mni, cur_seq);
 113
 114        amdgpu_amdkfd_evict_userptr(bo->kfd_bo, bo->notifier.mm);
 115        mutex_unlock(&adev->notifier_lock);
 116
 117        return true;
 118}
 119
 120static const struct mmu_interval_notifier_ops amdgpu_mn_hsa_ops = {
 121        .invalidate = amdgpu_mn_invalidate_hsa,
 122};
 123
 124/**
 125 * amdgpu_mn_register - register a BO for notifier updates
 126 *
 127 * @bo: amdgpu buffer object
 128 * @addr: userptr addr we should monitor
 129 *
 130 * Registers a mmu_notifier for the given BO at the specified address.
 131 * Returns 0 on success, -ERRNO if anything goes wrong.
 132 */
 133int amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr)
 134{
 135        if (bo->kfd_bo)
 136                return mmu_interval_notifier_insert(&bo->notifier, current->mm,
 137                                                    addr, amdgpu_bo_size(bo),
 138                                                    &amdgpu_mn_hsa_ops);
 139        return mmu_interval_notifier_insert(&bo->notifier, current->mm, addr,
 140                                            amdgpu_bo_size(bo),
 141                                            &amdgpu_mn_gfx_ops);
 142}
 143
 144/**
 145 * amdgpu_mn_unregister - unregister a BO for notifier updates
 146 *
 147 * @bo: amdgpu buffer object
 148 *
 149 * Remove any registration of mmu notifier updates from the buffer object.
 150 */
 151void amdgpu_mn_unregister(struct amdgpu_bo *bo)
 152{
 153        if (!bo->notifier.mm)
 154                return;
 155        mmu_interval_notifier_remove(&bo->notifier);
 156        bo->notifier.mm = NULL;
 157}
 158