1/* $NetBSD: page_track.c,v 1.2 2021/12/18 23:45:31 riastradh Exp $ */ 2 3/* 4 * Copyright(c) 2011-2017 Intel Corporation. All rights reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the next 14 * paragraph) shall be included in all copies or substantial portions of the 15 * Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. 24 */ 25#include <sys/cdefs.h> 26__KERNEL_RCSID(0, "$NetBSD: page_track.c,v 1.2 2021/12/18 23:45:31 riastradh Exp $"); 27 28#include "i915_drv.h" 29#include "gvt.h" 30 31/** 32 * intel_vgpu_find_page_track - find page track rcord of guest page 33 * @vgpu: a vGPU 34 * @gfn: the gfn of guest page 35 * 36 * Returns: 37 * A pointer to struct intel_vgpu_page_track if found, else NULL returned. 38 */ 39struct intel_vgpu_page_track *intel_vgpu_find_page_track( 40 struct intel_vgpu *vgpu, unsigned long gfn) 41{ 42 return radix_tree_lookup(&vgpu->page_track_tree, gfn); 43} 44 45/** 46 * intel_vgpu_register_page_track - register a guest page to be tacked 47 * @vgpu: a vGPU 48 * @gfn: the gfn of guest page 49 * @handler: page track handler 50 * @priv: tracker private 51 * 52 * Returns: 53 * zero on success, negative error code if failed. 54 */ 55int intel_vgpu_register_page_track(struct intel_vgpu *vgpu, unsigned long gfn, 56 gvt_page_track_handler_t handler, void *priv) 57{ 58 struct intel_vgpu_page_track *track; 59 int ret; 60 61 track = intel_vgpu_find_page_track(vgpu, gfn); 62 if (track) 63 return -EEXIST; 64 65 track = kzalloc(sizeof(*track), GFP_KERNEL); 66 if (!track) 67 return -ENOMEM; 68 69 track->handler = handler; 70 track->priv_data = priv; 71 72 ret = radix_tree_insert(&vgpu->page_track_tree, gfn, track); 73 if (ret) { 74 kfree(track); 75 return ret; 76 } 77 78 return 0; 79} 80 81/** 82 * intel_vgpu_unregister_page_track - unregister the tracked guest page 83 * @vgpu: a vGPU 84 * @gfn: the gfn of guest page 85 * 86 */ 87void intel_vgpu_unregister_page_track(struct intel_vgpu *vgpu, 88 unsigned long gfn) 89{ 90 struct intel_vgpu_page_track *track; 91 92 track = radix_tree_delete(&vgpu->page_track_tree, gfn); 93 if (track) { 94 if (track->tracked) 95 intel_gvt_hypervisor_disable_page_track(vgpu, gfn); 96 kfree(track); 97 } 98} 99 100/** 101 * intel_vgpu_enable_page_track - set write-protection on guest page 102 * @vgpu: a vGPU 103 * @gfn: the gfn of guest page 104 * 105 * Returns: 106 * zero on success, negative error code if failed. 107 */ 108int intel_vgpu_enable_page_track(struct intel_vgpu *vgpu, unsigned long gfn) 109{ 110 struct intel_vgpu_page_track *track; 111 int ret; 112 113 track = intel_vgpu_find_page_track(vgpu, gfn); 114 if (!track) 115 return -ENXIO; 116 117 if (track->tracked) 118 return 0; 119 120 ret = intel_gvt_hypervisor_enable_page_track(vgpu, gfn); 121 if (ret) 122 return ret; 123 track->tracked = true; 124 return 0; 125} 126 127/** 128 * intel_vgpu_enable_page_track - cancel write-protection on guest page 129 * @vgpu: a vGPU 130 * @gfn: the gfn of guest page 131 * 132 * Returns: 133 * zero on success, negative error code if failed. 134 */ 135int intel_vgpu_disable_page_track(struct intel_vgpu *vgpu, unsigned long gfn) 136{ 137 struct intel_vgpu_page_track *track; 138 int ret; 139 140 track = intel_vgpu_find_page_track(vgpu, gfn); 141 if (!track) 142 return -ENXIO; 143 144 if (!track->tracked) 145 return 0; 146 147 ret = intel_gvt_hypervisor_disable_page_track(vgpu, gfn); 148 if (ret) 149 return ret; 150 track->tracked = false; 151 return 0; 152} 153 154/** 155 * intel_vgpu_page_track_handler - called when write to write-protected page 156 * @vgpu: a vGPU 157 * @gpa: the gpa of this write 158 * @data: the writed data 159 * @bytes: the length of this write 160 * 161 * Returns: 162 * zero on success, negative error code if failed. 163 */ 164int intel_vgpu_page_track_handler(struct intel_vgpu *vgpu, u64 gpa, 165 void *data, unsigned int bytes) 166{ 167 struct intel_vgpu_page_track *page_track; 168 int ret = 0; 169 170 mutex_lock(&vgpu->vgpu_lock); 171 172 page_track = intel_vgpu_find_page_track(vgpu, gpa >> PAGE_SHIFT); 173 if (!page_track) { 174 ret = -ENXIO; 175 goto out; 176 } 177 178 if (unlikely(vgpu->failsafe)) { 179 /* Remove write protection to prevent furture traps. */ 180 intel_vgpu_disable_page_track(vgpu, gpa >> PAGE_SHIFT); 181 } else { 182 ret = page_track->handler(page_track, gpa, data, bytes); 183 if (ret) 184 gvt_err("guest page write error, gpa %llx\n", gpa); 185 } 186 187out: 188 mutex_unlock(&vgpu->vgpu_lock); 189 return ret; 190} 191