KVM
page_track.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Support KVM gust page tracking
4  *
5  * This feature allows us to track page access in guest. Currently, only
6  * write access is tracked.
7  *
8  * Copyright(C) 2015 Intel Corporation.
9  *
10  * Author:
11  * Xiao Guangrong <guangrong.xiao@linux.intel.com>
12  */
13 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14 
15 #include <linux/lockdep.h>
16 #include <linux/kvm_host.h>
17 #include <linux/rculist.h>
18 
19 #include "mmu.h"
20 #include "mmu_internal.h"
21 #include "page_track.h"
22 
24 {
25  return IS_ENABLED(CONFIG_KVM_EXTERNAL_WRITE_TRACKING) ||
27 }
28 
29 void kvm_page_track_free_memslot(struct kvm_memory_slot *slot)
30 {
31  kvfree(slot->arch.gfn_write_track);
32  slot->arch.gfn_write_track = NULL;
33 }
34 
35 static int __kvm_page_track_write_tracking_alloc(struct kvm_memory_slot *slot,
36  unsigned long npages)
37 {
38  const size_t size = sizeof(*slot->arch.gfn_write_track);
39 
40  if (!slot->arch.gfn_write_track)
41  slot->arch.gfn_write_track = __vcalloc(npages, size,
42  GFP_KERNEL_ACCOUNT);
43 
44  return slot->arch.gfn_write_track ? 0 : -ENOMEM;
45 }
46 
47 int kvm_page_track_create_memslot(struct kvm *kvm,
48  struct kvm_memory_slot *slot,
49  unsigned long npages)
50 {
52  return 0;
53 
54  return __kvm_page_track_write_tracking_alloc(slot, npages);
55 }
56 
57 int kvm_page_track_write_tracking_alloc(struct kvm_memory_slot *slot)
58 {
59  return __kvm_page_track_write_tracking_alloc(slot, slot->npages);
60 }
61 
62 static void update_gfn_write_track(struct kvm_memory_slot *slot, gfn_t gfn,
63  short count)
64 {
65  int index, val;
66 
67  index = gfn_to_index(gfn, slot->base_gfn, PG_LEVEL_4K);
68 
69  val = slot->arch.gfn_write_track[index];
70 
71  if (WARN_ON_ONCE(val + count < 0 || val + count > USHRT_MAX))
72  return;
73 
74  slot->arch.gfn_write_track[index] += count;
75 }
76 
77 void __kvm_write_track_add_gfn(struct kvm *kvm, struct kvm_memory_slot *slot,
78  gfn_t gfn)
79 {
80  lockdep_assert_held_write(&kvm->mmu_lock);
81 
82  lockdep_assert_once(lockdep_is_held(&kvm->slots_lock) ||
83  srcu_read_lock_held(&kvm->srcu));
84 
85  if (KVM_BUG_ON(!kvm_page_track_write_tracking_enabled(kvm), kvm))
86  return;
87 
88  update_gfn_write_track(slot, gfn, 1);
89 
90  /*
91  * new track stops large page mapping for the
92  * tracked page.
93  */
94  kvm_mmu_gfn_disallow_lpage(slot, gfn);
95 
96  if (kvm_mmu_slot_gfn_write_protect(kvm, slot, gfn, PG_LEVEL_4K))
98 }
99 
100 void __kvm_write_track_remove_gfn(struct kvm *kvm,
101  struct kvm_memory_slot *slot, gfn_t gfn)
102 {
103  lockdep_assert_held_write(&kvm->mmu_lock);
104 
105  lockdep_assert_once(lockdep_is_held(&kvm->slots_lock) ||
106  srcu_read_lock_held(&kvm->srcu));
107 
108  if (KVM_BUG_ON(!kvm_page_track_write_tracking_enabled(kvm), kvm))
109  return;
110 
111  update_gfn_write_track(slot, gfn, -1);
112 
113  /*
114  * allow large page mapping for the tracked page
115  * after the tracker is gone.
116  */
117  kvm_mmu_gfn_allow_lpage(slot, gfn);
118 }
119 
120 /*
121  * check if the corresponding access on the specified guest page is tracked.
122  */
123 bool kvm_gfn_is_write_tracked(struct kvm *kvm,
124  const struct kvm_memory_slot *slot, gfn_t gfn)
125 {
126  int index;
127 
128  if (!slot)
129  return false;
130 
132  return false;
133 
134  index = gfn_to_index(gfn, slot->base_gfn, PG_LEVEL_4K);
135  return !!READ_ONCE(slot->arch.gfn_write_track[index]);
136 }
137 
138 #ifdef CONFIG_KVM_EXTERNAL_WRITE_TRACKING
139 void kvm_page_track_cleanup(struct kvm *kvm)
140 {
141  struct kvm_page_track_notifier_head *head;
142 
143  head = &kvm->arch.track_notifier_head;
144  cleanup_srcu_struct(&head->track_srcu);
145 }
146 
147 int kvm_page_track_init(struct kvm *kvm)
148 {
149  struct kvm_page_track_notifier_head *head;
150 
151  head = &kvm->arch.track_notifier_head;
152  INIT_HLIST_HEAD(&head->track_notifier_list);
153  return init_srcu_struct(&head->track_srcu);
154 }
155 
156 /*
157  * register the notifier so that event interception for the tracked guest
158  * pages can be received.
159  */
160 int kvm_page_track_register_notifier(struct kvm *kvm,
161  struct kvm_page_track_notifier_node *n)
162 {
163  struct kvm_page_track_notifier_head *head;
164 
165  if (!kvm || kvm->mm != current->mm)
166  return -ESRCH;
167 
168  kvm_get_kvm(kvm);
169 
170  head = &kvm->arch.track_notifier_head;
171 
172  write_lock(&kvm->mmu_lock);
173  hlist_add_head_rcu(&n->node, &head->track_notifier_list);
174  write_unlock(&kvm->mmu_lock);
175  return 0;
176 }
177 EXPORT_SYMBOL_GPL(kvm_page_track_register_notifier);
178 
179 /*
180  * stop receiving the event interception. It is the opposed operation of
181  * kvm_page_track_register_notifier().
182  */
183 void kvm_page_track_unregister_notifier(struct kvm *kvm,
184  struct kvm_page_track_notifier_node *n)
185 {
186  struct kvm_page_track_notifier_head *head;
187 
188  head = &kvm->arch.track_notifier_head;
189 
190  write_lock(&kvm->mmu_lock);
191  hlist_del_rcu(&n->node);
192  write_unlock(&kvm->mmu_lock);
193  synchronize_srcu(&head->track_srcu);
194 
195  kvm_put_kvm(kvm);
196 }
197 EXPORT_SYMBOL_GPL(kvm_page_track_unregister_notifier);
198 
199 /*
200  * Notify the node that write access is intercepted and write emulation is
201  * finished at this time.
202  *
203  * The node should figure out if the written page is the one that node is
204  * interested in by itself.
205  */
206 void __kvm_page_track_write(struct kvm *kvm, gpa_t gpa, const u8 *new, int bytes)
207 {
208  struct kvm_page_track_notifier_head *head;
209  struct kvm_page_track_notifier_node *n;
210  int idx;
211 
212  head = &kvm->arch.track_notifier_head;
213 
214  if (hlist_empty(&head->track_notifier_list))
215  return;
216 
217  idx = srcu_read_lock(&head->track_srcu);
218  hlist_for_each_entry_srcu(n, &head->track_notifier_list, node,
219  srcu_read_lock_held(&head->track_srcu))
220  if (n->track_write)
221  n->track_write(gpa, new, bytes, n);
222  srcu_read_unlock(&head->track_srcu, idx);
223 }
224 
225 /*
226  * Notify external page track nodes that a memory region is being removed from
227  * the VM, e.g. so that users can free any associated metadata.
228  */
229 void kvm_page_track_delete_slot(struct kvm *kvm, struct kvm_memory_slot *slot)
230 {
231  struct kvm_page_track_notifier_head *head;
232  struct kvm_page_track_notifier_node *n;
233  int idx;
234 
235  head = &kvm->arch.track_notifier_head;
236 
237  if (hlist_empty(&head->track_notifier_list))
238  return;
239 
240  idx = srcu_read_lock(&head->track_srcu);
241  hlist_for_each_entry_srcu(n, &head->track_notifier_list, node,
242  srcu_read_lock_held(&head->track_srcu))
243  if (n->track_remove_region)
244  n->track_remove_region(slot->base_gfn, slot->npages, n);
245  srcu_read_unlock(&head->track_srcu, idx);
246 }
247 
248 /*
249  * add guest page to the tracking pool so that corresponding access on that
250  * page will be intercepted.
251  *
252  * @kvm: the guest instance we are interested in.
253  * @gfn: the guest page.
254  */
255 int kvm_write_track_add_gfn(struct kvm *kvm, gfn_t gfn)
256 {
257  struct kvm_memory_slot *slot;
258  int idx;
259 
260  idx = srcu_read_lock(&kvm->srcu);
261 
262  slot = gfn_to_memslot(kvm, gfn);
263  if (!slot) {
264  srcu_read_unlock(&kvm->srcu, idx);
265  return -EINVAL;
266  }
267 
268  write_lock(&kvm->mmu_lock);
269  __kvm_write_track_add_gfn(kvm, slot, gfn);
270  write_unlock(&kvm->mmu_lock);
271 
272  srcu_read_unlock(&kvm->srcu, idx);
273 
274  return 0;
275 }
276 EXPORT_SYMBOL_GPL(kvm_write_track_add_gfn);
277 
278 /*
279  * remove the guest page from the tracking pool which stops the interception
280  * of corresponding access on that page.
281  *
282  * @kvm: the guest instance we are interested in.
283  * @gfn: the guest page.
284  */
285 int kvm_write_track_remove_gfn(struct kvm *kvm, gfn_t gfn)
286 {
287  struct kvm_memory_slot *slot;
288  int idx;
289 
290  idx = srcu_read_lock(&kvm->srcu);
291 
292  slot = gfn_to_memslot(kvm, gfn);
293  if (!slot) {
294  srcu_read_unlock(&kvm->srcu, idx);
295  return -EINVAL;
296  }
297 
298  write_lock(&kvm->mmu_lock);
299  __kvm_write_track_remove_gfn(kvm, slot, gfn);
300  write_unlock(&kvm->mmu_lock);
301 
302  srcu_read_unlock(&kvm->srcu, idx);
303 
304  return 0;
305 }
306 EXPORT_SYMBOL_GPL(kvm_write_track_remove_gfn);
307 #endif
EXPORT_SYMBOL_GPL(kvm_cpu_caps)
void kvm_put_kvm(struct kvm *kvm)
Definition: kvm_main.c:1419
void kvm_flush_remote_tlbs(struct kvm *kvm)
Definition: kvm_main.c:346
struct kvm_memory_slot * gfn_to_memslot(struct kvm *kvm, gfn_t gfn)
Definition: kvm_main.c:2630
void kvm_get_kvm(struct kvm *kvm)
Definition: kvm_main.c:1403
bool kvm_mmu_slot_gfn_write_protect(struct kvm *kvm, struct kvm_memory_slot *slot, u64 gfn, int min_level)
Definition: mmu.c:1414
void kvm_mmu_gfn_allow_lpage(const struct kvm_memory_slot *slot, gfn_t gfn)
Definition: mmu.c:822
bool tdp_enabled
Definition: mmu.c:106
void kvm_mmu_gfn_disallow_lpage(const struct kvm_memory_slot *slot, gfn_t gfn)
Definition: mmu.c:817
static gfn_t gfn_to_index(gfn_t gfn, gfn_t base_gfn, int level)
Definition: mmu.h:284
static bool kvm_shadow_root_allocated(struct kvm *kvm)
Definition: mmu.h:262
int kvm_page_track_write_tracking_alloc(struct kvm_memory_slot *slot)
Definition: page_track.c:57
bool kvm_gfn_is_write_tracked(struct kvm *kvm, const struct kvm_memory_slot *slot, gfn_t gfn)
Definition: page_track.c:123
void kvm_page_track_free_memslot(struct kvm_memory_slot *slot)
Definition: page_track.c:29
void __kvm_write_track_add_gfn(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn)
Definition: page_track.c:77
void __kvm_write_track_remove_gfn(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn)
Definition: page_track.c:100
bool kvm_page_track_write_tracking_enabled(struct kvm *kvm)
Definition: page_track.c:23
static void update_gfn_write_track(struct kvm_memory_slot *slot, gfn_t gfn, short count)
Definition: page_track.c:62
static int __kvm_page_track_write_tracking_alloc(struct kvm_memory_slot *slot, unsigned long npages)
Definition: page_track.c:35
int kvm_page_track_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot, unsigned long npages)
Definition: page_track.c:47
static void kvm_page_track_cleanup(struct kvm *kvm)
Definition: page_track.h:39
static void __kvm_page_track_write(struct kvm *kvm, gpa_t gpa, const u8 *new, int bytes)
Definition: page_track.h:41
static int kvm_page_track_init(struct kvm *kvm)
Definition: page_track.h:38
static void kvm_page_track_delete_slot(struct kvm *kvm, struct kvm_memory_slot *slot)
Definition: page_track.h:43