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 */
intel_vgpu_find_page_track(struct intel_vgpu * vgpu,unsigned long gfn)39 struct 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 */
intel_vgpu_register_page_track(struct intel_vgpu * vgpu,unsigned long gfn,gvt_page_track_handler_t handler,void * priv)55 int 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 */
intel_vgpu_unregister_page_track(struct intel_vgpu * vgpu,unsigned long gfn)87 void 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 */
intel_vgpu_enable_page_track(struct intel_vgpu * vgpu,unsigned long gfn)108 int 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 */
intel_vgpu_disable_page_track(struct intel_vgpu * vgpu,unsigned long gfn)135 int 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 */
intel_vgpu_page_track_handler(struct intel_vgpu * vgpu,u64 gpa,void * data,unsigned int bytes)164 int 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
187 out:
188 mutex_unlock(&vgpu->vgpu_lock);
189 return ret;
190 }
191