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