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/kvm_host.h> 16 #include <linux/rculist.h> 17 18 #include <asm/kvm_page_track.h> 19 20 #include "mmu.h" 21 #include "mmu_internal.h" 22 23 bool kvm_page_track_write_tracking_enabled(struct kvm *kvm) 24 { 25 return IS_ENABLED(CONFIG_KVM_EXTERNAL_WRITE_TRACKING) || 26 !tdp_enabled || kvm_shadow_root_allocated(kvm); 27 } 28 29 void kvm_page_track_free_memslot(struct kvm_memory_slot *slot) 30 { 31 int i; 32 33 for (i = 0; i < KVM_PAGE_TRACK_MAX; i++) { 34 kvfree(slot->arch.gfn_track[i]); 35 slot->arch.gfn_track[i] = NULL; 36 } 37 } 38 39 int kvm_page_track_create_memslot(struct kvm *kvm, 40 struct kvm_memory_slot *slot, 41 unsigned long npages) 42 { 43 int i; 44 45 for (i = 0; i < KVM_PAGE_TRACK_MAX; i++) { 46 if (i == KVM_PAGE_TRACK_WRITE && 47 !kvm_page_track_write_tracking_enabled(kvm)) 48 continue; 49 50 slot->arch.gfn_track[i] = 51 __vcalloc(npages, sizeof(*slot->arch.gfn_track[i]), 52 GFP_KERNEL_ACCOUNT); 53 if (!slot->arch.gfn_track[i]) 54 goto track_free; 55 } 56 57 return 0; 58 59 track_free: 60 kvm_page_track_free_memslot(slot); 61 return -ENOMEM; 62 } 63 64 static inline bool page_track_mode_is_valid(enum kvm_page_track_mode mode) 65 { 66 if (mode < 0 || mode >= KVM_PAGE_TRACK_MAX) 67 return false; 68 69 return true; 70 } 71 72 int kvm_page_track_write_tracking_alloc(struct kvm_memory_slot *slot) 73 { 74 unsigned short *gfn_track; 75 76 if (slot->arch.gfn_track[KVM_PAGE_TRACK_WRITE]) 77 return 0; 78 79 gfn_track = __vcalloc(slot->npages, sizeof(*gfn_track), 80 GFP_KERNEL_ACCOUNT); 81 if (gfn_track == NULL) 82 return -ENOMEM; 83 84 slot->arch.gfn_track[KVM_PAGE_TRACK_WRITE] = gfn_track; 85 return 0; 86 } 87 88 static void update_gfn_track(struct kvm_memory_slot *slot, gfn_t gfn, 89 enum kvm_page_track_mode mode, short count) 90 { 91 int index, val; 92 93 index = gfn_to_index(gfn, slot->base_gfn, PG_LEVEL_4K); 94 95 val = slot->arch.gfn_track[mode][index]; 96 97 if (WARN_ON(val + count < 0 || val + count > USHRT_MAX)) 98 return; 99 100 slot->arch.gfn_track[mode][index] += count; 101 } 102 103 /* 104 * add guest page to the tracking pool so that corresponding access on that 105 * page will be intercepted. 106 * 107 * It should be called under the protection both of mmu-lock and kvm->srcu 108 * or kvm->slots_lock. 109 * 110 * @kvm: the guest instance we are interested in. 111 * @slot: the @gfn belongs to. 112 * @gfn: the guest page. 113 * @mode: tracking mode, currently only write track is supported. 114 */ 115 void kvm_slot_page_track_add_page(struct kvm *kvm, 116 struct kvm_memory_slot *slot, gfn_t gfn, 117 enum kvm_page_track_mode mode) 118 { 119 120 if (WARN_ON(!page_track_mode_is_valid(mode))) 121 return; 122 123 if (WARN_ON(mode == KVM_PAGE_TRACK_WRITE && 124 !kvm_page_track_write_tracking_enabled(kvm))) 125 return; 126 127 update_gfn_track(slot, gfn, mode, 1); 128 129 /* 130 * new track stops large page mapping for the 131 * tracked page. 132 */ 133 kvm_mmu_gfn_disallow_lpage(slot, gfn); 134 135 if (mode == KVM_PAGE_TRACK_WRITE) 136 if (kvm_mmu_slot_gfn_write_protect(kvm, slot, gfn, PG_LEVEL_4K)) 137 kvm_flush_remote_tlbs(kvm); 138 } 139 EXPORT_SYMBOL_GPL(kvm_slot_page_track_add_page); 140 141 /* 142 * remove the guest page from the tracking pool which stops the interception 143 * of corresponding access on that page. It is the opposed operation of 144 * kvm_slot_page_track_add_page(). 145 * 146 * It should be called under the protection both of mmu-lock and kvm->srcu 147 * or kvm->slots_lock. 148 * 149 * @kvm: the guest instance we are interested in. 150 * @slot: the @gfn belongs to. 151 * @gfn: the guest page. 152 * @mode: tracking mode, currently only write track is supported. 153 */ 154 void kvm_slot_page_track_remove_page(struct kvm *kvm, 155 struct kvm_memory_slot *slot, gfn_t gfn, 156 enum kvm_page_track_mode mode) 157 { 158 if (WARN_ON(!page_track_mode_is_valid(mode))) 159 return; 160 161 if (WARN_ON(mode == KVM_PAGE_TRACK_WRITE && 162 !kvm_page_track_write_tracking_enabled(kvm))) 163 return; 164 165 update_gfn_track(slot, gfn, mode, -1); 166 167 /* 168 * allow large page mapping for the tracked page 169 * after the tracker is gone. 170 */ 171 kvm_mmu_gfn_allow_lpage(slot, gfn); 172 } 173 EXPORT_SYMBOL_GPL(kvm_slot_page_track_remove_page); 174 175 /* 176 * check if the corresponding access on the specified guest page is tracked. 177 */ 178 bool kvm_slot_page_track_is_active(struct kvm *kvm, 179 const struct kvm_memory_slot *slot, 180 gfn_t gfn, enum kvm_page_track_mode mode) 181 { 182 int index; 183 184 if (WARN_ON(!page_track_mode_is_valid(mode))) 185 return false; 186 187 if (!slot) 188 return false; 189 190 if (mode == KVM_PAGE_TRACK_WRITE && 191 !kvm_page_track_write_tracking_enabled(kvm)) 192 return false; 193 194 index = gfn_to_index(gfn, slot->base_gfn, PG_LEVEL_4K); 195 return !!READ_ONCE(slot->arch.gfn_track[mode][index]); 196 } 197 198 void kvm_page_track_cleanup(struct kvm *kvm) 199 { 200 struct kvm_page_track_notifier_head *head; 201 202 head = &kvm->arch.track_notifier_head; 203 cleanup_srcu_struct(&head->track_srcu); 204 } 205 206 int kvm_page_track_init(struct kvm *kvm) 207 { 208 struct kvm_page_track_notifier_head *head; 209 210 head = &kvm->arch.track_notifier_head; 211 INIT_HLIST_HEAD(&head->track_notifier_list); 212 return init_srcu_struct(&head->track_srcu); 213 } 214 215 /* 216 * register the notifier so that event interception for the tracked guest 217 * pages can be received. 218 */ 219 void 220 kvm_page_track_register_notifier(struct kvm *kvm, 221 struct kvm_page_track_notifier_node *n) 222 { 223 struct kvm_page_track_notifier_head *head; 224 225 head = &kvm->arch.track_notifier_head; 226 227 write_lock(&kvm->mmu_lock); 228 hlist_add_head_rcu(&n->node, &head->track_notifier_list); 229 write_unlock(&kvm->mmu_lock); 230 } 231 EXPORT_SYMBOL_GPL(kvm_page_track_register_notifier); 232 233 /* 234 * stop receiving the event interception. It is the opposed operation of 235 * kvm_page_track_register_notifier(). 236 */ 237 void 238 kvm_page_track_unregister_notifier(struct kvm *kvm, 239 struct kvm_page_track_notifier_node *n) 240 { 241 struct kvm_page_track_notifier_head *head; 242 243 head = &kvm->arch.track_notifier_head; 244 245 write_lock(&kvm->mmu_lock); 246 hlist_del_rcu(&n->node); 247 write_unlock(&kvm->mmu_lock); 248 synchronize_srcu(&head->track_srcu); 249 } 250 EXPORT_SYMBOL_GPL(kvm_page_track_unregister_notifier); 251 252 /* 253 * Notify the node that write access is intercepted and write emulation is 254 * finished at this time. 255 * 256 * The node should figure out if the written page is the one that node is 257 * interested in by itself. 258 */ 259 void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new, 260 int bytes) 261 { 262 struct kvm_page_track_notifier_head *head; 263 struct kvm_page_track_notifier_node *n; 264 int idx; 265 266 head = &vcpu->kvm->arch.track_notifier_head; 267 268 if (hlist_empty(&head->track_notifier_list)) 269 return; 270 271 idx = srcu_read_lock(&head->track_srcu); 272 hlist_for_each_entry_srcu(n, &head->track_notifier_list, node, 273 srcu_read_lock_held(&head->track_srcu)) 274 if (n->track_write) 275 n->track_write(vcpu, gpa, new, bytes, n); 276 srcu_read_unlock(&head->track_srcu, idx); 277 } 278 279 /* 280 * Notify the node that memory slot is being removed or moved so that it can 281 * drop write-protection for the pages in the memory slot. 282 * 283 * The node should figure out it has any write-protected pages in this slot 284 * by itself. 285 */ 286 void kvm_page_track_flush_slot(struct kvm *kvm, struct kvm_memory_slot *slot) 287 { 288 struct kvm_page_track_notifier_head *head; 289 struct kvm_page_track_notifier_node *n; 290 int idx; 291 292 head = &kvm->arch.track_notifier_head; 293 294 if (hlist_empty(&head->track_notifier_list)) 295 return; 296 297 idx = srcu_read_lock(&head->track_srcu); 298 hlist_for_each_entry_srcu(n, &head->track_notifier_list, node, 299 srcu_read_lock_held(&head->track_srcu)) 300 if (n->track_flush_slot) 301 n->track_flush_slot(kvm, slot, n); 302 srcu_read_unlock(&head->track_srcu, idx); 303 } 304