1/* $NetBSD: radeon_mn.c,v 1.3 2021/12/18 23:45:43 riastradh Exp $ */ 2 3/* 4 * Copyright 2014 Advanced Micro Devices, Inc. 5 * All Rights Reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the 9 * "Software"), to deal in the Software without restriction, including 10 * without limitation the rights to use, copy, modify, merge, publish, 11 * distribute, sub license, and/or sell copies of the Software, and to 12 * permit persons to whom the Software is furnished to do so, subject to 13 * the following conditions: 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 18 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 * USE OR OTHER DEALINGS IN THE SOFTWARE. 22 * 23 * The above copyright notice and this permission notice (including the 24 * next paragraph) shall be included in all copies or substantial portions 25 * of the Software. 26 * 27 */ 28/* 29 * Authors: 30 * Christian K��nig <christian.koenig@amd.com> 31 */ 32 33#include <sys/cdefs.h> 34__KERNEL_RCSID(0, "$NetBSD: radeon_mn.c,v 1.3 2021/12/18 23:45:43 riastradh Exp $"); 35 36#include <linux/firmware.h> 37#include <linux/module.h> 38#include <linux/mmu_notifier.h> 39 40#include <drm/drm.h> 41 42#include "radeon.h" 43 44/** 45 * radeon_mn_invalidate - callback to notify about mm change 46 * 47 * @mn: our notifier 48 * @range: the VMA under invalidation 49 * 50 * We block for all BOs between start and end to be idle and 51 * unmap them by move them into system domain again. 52 */ 53static bool radeon_mn_invalidate(struct mmu_interval_notifier *mn, 54 const struct mmu_notifier_range *range, 55 unsigned long cur_seq) 56{ 57 struct radeon_bo *bo = container_of(mn, struct radeon_bo, notifier); 58 struct ttm_operation_ctx ctx = { false, false }; 59 long r; 60 61 if (!bo->tbo.ttm || bo->tbo.ttm->state != tt_bound) 62 return true; 63 64 if (!mmu_notifier_range_blockable(range)) 65 return false; 66 67 r = radeon_bo_reserve(bo, true); 68 if (r) { 69 DRM_ERROR("(%ld) failed to reserve user bo\n", r); 70 return true; 71 } 72 73 r = dma_resv_wait_timeout_rcu(bo->tbo.base.resv, true, false, 74 MAX_SCHEDULE_TIMEOUT); 75 if (r <= 0) 76 DRM_ERROR("(%ld) failed to wait for user bo\n", r); 77 78 radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_CPU); 79 r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); 80 if (r) 81 DRM_ERROR("(%ld) failed to validate user bo\n", r); 82 83 radeon_bo_unreserve(bo); 84 return true; 85} 86 87static const struct mmu_interval_notifier_ops radeon_mn_ops = { 88 .invalidate = radeon_mn_invalidate, 89}; 90 91/** 92 * radeon_mn_register - register a BO for notifier updates 93 * 94 * @bo: radeon buffer object 95 * @addr: userptr addr we should monitor 96 * 97 * Registers an MMU notifier for the given BO at the specified address. 98 * Returns 0 on success, -ERRNO if anything goes wrong. 99 */ 100int radeon_mn_register(struct radeon_bo *bo, unsigned long addr) 101{ 102 int ret; 103 104 ret = mmu_interval_notifier_insert(&bo->notifier, current->mm, addr, 105 radeon_bo_size(bo), &radeon_mn_ops); 106 if (ret) 107 return ret; 108 109 /* 110 * FIXME: radeon appears to allow get_user_pages to run during 111 * invalidate_range_start/end, which is not a safe way to read the 112 * PTEs. It should use the mmu_interval_read_begin() scheme around the 113 * get_user_pages to ensure that the PTEs are read properly 114 */ 115 mmu_interval_read_begin(&bo->notifier); 116 return 0; 117} 118 119/** 120 * radeon_mn_unregister - unregister a BO for notifier updates 121 * 122 * @bo: radeon buffer object 123 * 124 * Remove any registration of MMU notifier updates from the buffer object. 125 */ 126void radeon_mn_unregister(struct radeon_bo *bo) 127{ 128 if (!bo->notifier.mm) 129 return; 130 mmu_interval_notifier_remove(&bo->notifier); 131 bo->notifier.mm = NULL; 132} 133