Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mshv: Add refcount and locking to mem regions

Introduce kref-based reference counting and spinlock protection for
memory regions in Hyper-V partition management. This change improves
memory region lifecycle management and ensures thread-safe access to the
region list.

Previously, the regions list was protected by the partition mutex.
However, this approach is too heavy for frequent fault and invalidation
operations. Finer grained locking is now used to improve efficiency and
concurrency.

This is a precursor to supporting movable memory regions. Fault and
invalidation handling for movable regions will require safe traversal of
the region list and holding a region reference while performing
invalidation or fault operations.

Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
Signed-off-by: Wei Liu <wei.liu@kernel.org>

authored by

Stanislav Kinsburskii and committed by
Wei Liu
c39dda08 abceb429

+45 -12
+16 -3
drivers/hv/mshv_regions.c
··· 7 7 * Authors: Microsoft Linux virtualization team 8 8 */ 9 9 10 + #include <linux/kref.h> 10 11 #include <linux/mm.h> 11 12 #include <linux/vmalloc.h> 12 13 ··· 154 153 155 154 if (!is_mmio) 156 155 region->flags.range_pinned = true; 156 + 157 + kref_init(&region->refcount); 157 158 158 159 return region; 159 160 } ··· 314 311 mshv_region_chunk_unmap); 315 312 } 316 313 317 - void mshv_region_destroy(struct mshv_mem_region *region) 314 + static void mshv_region_destroy(struct kref *ref) 318 315 { 316 + struct mshv_mem_region *region = 317 + container_of(ref, struct mshv_mem_region, refcount); 319 318 struct mshv_partition *partition = region->partition; 320 319 int ret; 321 - 322 - hlist_del(&region->hnode); 323 320 324 321 if (mshv_partition_encrypted(partition)) { 325 322 ret = mshv_region_share(region); ··· 336 333 mshv_region_invalidate(region); 337 334 338 335 vfree(region); 336 + } 337 + 338 + void mshv_region_put(struct mshv_mem_region *region) 339 + { 340 + kref_put(&region->refcount, mshv_region_destroy); 341 + } 342 + 343 + int mshv_region_get(struct mshv_mem_region *region) 344 + { 345 + return kref_get_unless_zero(&region->refcount); 339 346 }
+5 -1
drivers/hv/mshv_root.h
··· 72 72 73 73 struct mshv_mem_region { 74 74 struct hlist_node hnode; 75 + struct kref refcount; 75 76 u64 nr_pages; 76 77 u64 start_gfn; 77 78 u64 start_uaddr; ··· 98 97 u64 pt_id; 99 98 refcount_t pt_ref_count; 100 99 struct mutex pt_mutex; 100 + 101 + spinlock_t pt_mem_regions_lock; 101 102 struct hlist_head pt_mem_regions; // not ordered 102 103 103 104 u32 pt_vp_count; ··· 322 319 int mshv_region_map(struct mshv_mem_region *region); 323 320 void mshv_region_invalidate(struct mshv_mem_region *region); 324 321 int mshv_region_pin(struct mshv_mem_region *region); 325 - void mshv_region_destroy(struct mshv_mem_region *region); 322 + void mshv_region_put(struct mshv_mem_region *region); 323 + int mshv_region_get(struct mshv_mem_region *region); 326 324 327 325 #endif /* _MSHV_ROOT_H_ */
+24 -8
drivers/hv/mshv_root_main.c
··· 1086 1086 u64 nr_pages = HVPFN_DOWN(mem->size); 1087 1087 1088 1088 /* Reject overlapping regions */ 1089 + spin_lock(&partition->pt_mem_regions_lock); 1089 1090 hlist_for_each_entry(rg, &partition->pt_mem_regions, hnode) { 1090 1091 if (mem->guest_pfn + nr_pages <= rg->start_gfn || 1091 1092 rg->start_gfn + rg->nr_pages <= mem->guest_pfn) 1092 1093 continue; 1093 - 1094 + spin_unlock(&partition->pt_mem_regions_lock); 1094 1095 return -EEXIST; 1095 1096 } 1097 + spin_unlock(&partition->pt_mem_regions_lock); 1096 1098 1097 1099 rg = mshv_region_create(mem->guest_pfn, nr_pages, 1098 1100 mem->userspace_addr, mem->flags, ··· 1226 1224 if (ret) 1227 1225 goto errout; 1228 1226 1229 - /* Install the new region */ 1227 + spin_lock(&partition->pt_mem_regions_lock); 1230 1228 hlist_add_head(&region->hnode, &partition->pt_mem_regions); 1229 + spin_unlock(&partition->pt_mem_regions_lock); 1231 1230 1232 1231 return 0; 1233 1232 ··· 1247 1244 if (!(mem.flags & BIT(MSHV_SET_MEM_BIT_UNMAP))) 1248 1245 return -EINVAL; 1249 1246 1247 + spin_lock(&partition->pt_mem_regions_lock); 1248 + 1250 1249 region = mshv_partition_region_by_gfn(partition, mem.guest_pfn); 1251 - if (!region) 1252 - return -EINVAL; 1250 + if (!region) { 1251 + spin_unlock(&partition->pt_mem_regions_lock); 1252 + return -ENOENT; 1253 + } 1253 1254 1254 1255 /* Paranoia check */ 1255 1256 if (region->start_uaddr != mem.userspace_addr || 1256 1257 region->start_gfn != mem.guest_pfn || 1257 - region->nr_pages != HVPFN_DOWN(mem.size)) 1258 + region->nr_pages != HVPFN_DOWN(mem.size)) { 1259 + spin_unlock(&partition->pt_mem_regions_lock); 1258 1260 return -EINVAL; 1261 + } 1259 1262 1260 - mshv_region_destroy(region); 1263 + hlist_del(&region->hnode); 1264 + 1265 + spin_unlock(&partition->pt_mem_regions_lock); 1266 + 1267 + mshv_region_put(region); 1261 1268 1262 1269 return 0; 1263 1270 } ··· 1670 1657 remove_partition(partition); 1671 1658 1672 1659 hlist_for_each_entry_safe(region, n, &partition->pt_mem_regions, 1673 - hnode) 1674 - mshv_region_destroy(region); 1660 + hnode) { 1661 + hlist_del(&region->hnode); 1662 + mshv_region_put(region); 1663 + } 1675 1664 1676 1665 /* Withdraw and free all pages we deposited */ 1677 1666 hv_call_withdraw_memory(U64_MAX, NUMA_NO_NODE, partition->pt_id); ··· 1871 1856 1872 1857 INIT_HLIST_HEAD(&partition->pt_devices); 1873 1858 1859 + spin_lock_init(&partition->pt_mem_regions_lock); 1874 1860 INIT_HLIST_HEAD(&partition->pt_mem_regions); 1875 1861 1876 1862 mshv_eventfd_init(partition);