Commit a66191c5 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'hyperv-fixes-signed-20260121' of...

Merge tag 'hyperv-fixes-signed-20260121' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux

Pull hyperv fixes from Wei Liu:

 - Fix ARM64 port of the MSHV driver (Anirudh Rayabharam)

 - Fix huge page handling in the MSHV driver (Stanislav Kinsburskii)

 - Minor fixes to driver code (Julia Lawall, Michael Kelley)

* tag 'hyperv-fixes-signed-20260121' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux:
  mshv: handle gpa intercepts for arm64
  mshv: add definitions for arm64 gpa intercepts
  mshv: Add __user attribute to argument passed to access_ok()
  mshv: Store the result of vfs_poll in a variable of type __poll_t
  mshv: Align huge page stride with guest mapping
  Drivers: hv: Always do Hyper-V panic notification in hv_kmsg_dump()
  Drivers: hv: vmbus: fix typo in function name reference
parents 79f255b2 12ffd561
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -195,13 +195,15 @@ static void hv_kmsg_dump(struct kmsg_dumper *dumper,

	/*
	 * Write dump contents to the page. No need to synchronize; panic should
	 * be single-threaded.
	 * be single-threaded. Ignore failures from kmsg_dump_get_buffer() since
	 * panic notification should be done even if there is no message data.
	 * Don't assume bytes_written is set in case of failure, so initialize it.
	 */
	kmsg_dump_rewind(&iter);
	kmsg_dump_get_buffer(&iter, false, hv_panic_page, HV_HYP_PAGE_SIZE,
	bytes_written = 0;
	(void)kmsg_dump_get_buffer(&iter, false, hv_panic_page, HV_HYP_PAGE_SIZE,
			     &bytes_written);
	if (!bytes_written)
		return;

	/*
	 * P3 to contain the physical address of the panic page & P4 to
	 * contain the size of the panic data in that page. Rest of the
@@ -210,7 +212,7 @@ static void hv_kmsg_dump(struct kmsg_dumper *dumper,
	hv_set_msr(HV_MSR_CRASH_P0, 0);
	hv_set_msr(HV_MSR_CRASH_P1, 0);
	hv_set_msr(HV_MSR_CRASH_P2, 0);
	hv_set_msr(HV_MSR_CRASH_P3, virt_to_phys(hv_panic_page));
	hv_set_msr(HV_MSR_CRASH_P3, bytes_written ? virt_to_phys(hv_panic_page) : 0);
	hv_set_msr(HV_MSR_CRASH_P4, bytes_written);

	/*
+1 −1
Original line number Diff line number Diff line
@@ -375,7 +375,7 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)
		return;

	/*
	 * The cmxchg() above does an implicit memory barrier to
	 * The cmpxchg() above does an implicit memory barrier to
	 * ensure the write to MessageType (ie set to
	 * HVMSG_NONE) happens before we read the
	 * MessagePending and EOMing. Otherwise, the EOMing
+1 −1
Original line number Diff line number Diff line
@@ -388,7 +388,7 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
{
	struct eventfd_ctx *eventfd = NULL, *resamplefd = NULL;
	struct mshv_irqfd *irqfd, *tmp;
	unsigned int events;
	__poll_t events;
	int ret;
	int idx;

+62 −31
Original line number Diff line number Diff line
@@ -19,6 +19,41 @@

#define MSHV_MAP_FAULT_IN_PAGES				PTRS_PER_PMD

/**
 * mshv_chunk_stride - Compute stride for mapping guest memory
 * @page      : The page to check for huge page backing
 * @gfn       : Guest frame number for the mapping
 * @page_count: Total number of pages in the mapping
 *
 * Determines the appropriate stride (in pages) for mapping guest memory.
 * Uses huge page stride if the backing page is huge and the guest mapping
 * is properly aligned; otherwise falls back to single page stride.
 *
 * Return: Stride in pages, or -EINVAL if page order is unsupported.
 */
static int mshv_chunk_stride(struct page *page,
			     u64 gfn, u64 page_count)
{
	unsigned int page_order;

	/*
	 * Use single page stride by default. For huge page stride, the
	 * page must be compound and point to the head of the compound
	 * page, and both gfn and page_count must be huge-page aligned.
	 */
	if (!PageCompound(page) || !PageHead(page) ||
	    !IS_ALIGNED(gfn, PTRS_PER_PMD) ||
	    !IS_ALIGNED(page_count, PTRS_PER_PMD))
		return 1;

	page_order = folio_order(page_folio(page));
	/* The hypervisor only supports 2M huge page */
	if (page_order != PMD_ORDER)
		return -EINVAL;

	return 1 << page_order;
}

/**
 * mshv_region_process_chunk - Processes a contiguous chunk of memory pages
 *                             in a region.
@@ -45,25 +80,23 @@ static long mshv_region_process_chunk(struct mshv_mem_region *region,
				      int (*handler)(struct mshv_mem_region *region,
						     u32 flags,
						     u64 page_offset,
						     u64 page_count))
						     u64 page_count,
						     bool huge_page))
{
	u64 count, stride;
	unsigned int page_order;
	u64 gfn = region->start_gfn + page_offset;
	u64 count;
	struct page *page;
	int ret;
	int stride, ret;

	page = region->pages[page_offset];
	if (!page)
		return -EINVAL;

	page_order = folio_order(page_folio(page));
	/* The hypervisor only supports 4K and 2M page sizes */
	if (page_order && page_order != PMD_ORDER)
		return -EINVAL;

	stride = 1 << page_order;
	stride = mshv_chunk_stride(page, gfn, page_count);
	if (stride < 0)
		return stride;

	/* Start at stride since the first page is validated */
	/* Start at stride since the first stride is validated */
	for (count = stride; count < page_count; count += stride) {
		page = region->pages[page_offset + count];

@@ -71,12 +104,13 @@ static long mshv_region_process_chunk(struct mshv_mem_region *region,
		if (!page)
			break;

		/* Break if page size changes */
		if (page_order != folio_order(page_folio(page)))
		/* Break if stride size changes */
		if (stride != mshv_chunk_stride(page, gfn + count,
						page_count - count))
			break;
	}

	ret = handler(region, flags, page_offset, count);
	ret = handler(region, flags, page_offset, count, stride > 1);
	if (ret)
		return ret;

@@ -108,7 +142,8 @@ static int mshv_region_process_range(struct mshv_mem_region *region,
				     int (*handler)(struct mshv_mem_region *region,
						    u32 flags,
						    u64 page_offset,
						    u64 page_count))
						    u64 page_count,
						    bool huge_page))
{
	long ret;

@@ -162,11 +197,10 @@ struct mshv_mem_region *mshv_region_create(u64 guest_pfn, u64 nr_pages,

static int mshv_region_chunk_share(struct mshv_mem_region *region,
				   u32 flags,
				   u64 page_offset, u64 page_count)
				   u64 page_offset, u64 page_count,
				   bool huge_page)
{
	struct page *page = region->pages[page_offset];

	if (PageHuge(page) || PageTransCompound(page))
	if (huge_page)
		flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;

	return hv_call_modify_spa_host_access(region->partition->pt_id,
@@ -188,11 +222,10 @@ int mshv_region_share(struct mshv_mem_region *region)

static int mshv_region_chunk_unshare(struct mshv_mem_region *region,
				     u32 flags,
				     u64 page_offset, u64 page_count)
				     u64 page_offset, u64 page_count,
				     bool huge_page)
{
	struct page *page = region->pages[page_offset];

	if (PageHuge(page) || PageTransCompound(page))
	if (huge_page)
		flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;

	return hv_call_modify_spa_host_access(region->partition->pt_id,
@@ -212,11 +245,10 @@ int mshv_region_unshare(struct mshv_mem_region *region)

static int mshv_region_chunk_remap(struct mshv_mem_region *region,
				   u32 flags,
				   u64 page_offset, u64 page_count)
				   u64 page_offset, u64 page_count,
				   bool huge_page)
{
	struct page *page = region->pages[page_offset];

	if (PageHuge(page) || PageTransCompound(page))
	if (huge_page)
		flags |= HV_MAP_GPA_LARGE_PAGE;

	return hv_call_map_gpa_pages(region->partition->pt_id,
@@ -295,11 +327,10 @@ int mshv_region_pin(struct mshv_mem_region *region)

static int mshv_region_chunk_unmap(struct mshv_mem_region *region,
				   u32 flags,
				   u64 page_offset, u64 page_count)
				   u64 page_offset, u64 page_count,
				   bool huge_page)
{
	struct page *page = region->pages[page_offset];

	if (PageHuge(page) || PageTransCompound(page))
	if (huge_page)
		flags |= HV_UNMAP_GPA_LARGE_PAGE;

	return hv_call_unmap_gpa_pages(region->partition->pt_id,
+9 −8
Original line number Diff line number Diff line
@@ -611,7 +611,6 @@ mshv_partition_region_by_gfn(struct mshv_partition *partition, u64 gfn)
	return NULL;
}

#ifdef CONFIG_X86_64
static struct mshv_mem_region *
mshv_partition_region_by_gfn_get(struct mshv_partition *p, u64 gfn)
{
@@ -643,12 +642,17 @@ static bool mshv_handle_gpa_intercept(struct mshv_vp *vp)
{
	struct mshv_partition *p = vp->vp_partition;
	struct mshv_mem_region *region;
	struct hv_x64_memory_intercept_message *msg;
	bool ret;
	u64 gfn;

	msg = (struct hv_x64_memory_intercept_message *)
#if defined(CONFIG_X86_64)
	struct hv_x64_memory_intercept_message *msg =
		(struct hv_x64_memory_intercept_message *)
		vp->vp_intercept_msg_page->u.payload;
#elif defined(CONFIG_ARM64)
	struct hv_arm64_memory_intercept_message *msg =
		(struct hv_arm64_memory_intercept_message *)
		vp->vp_intercept_msg_page->u.payload;
#endif

	gfn = HVPFN_DOWN(msg->guest_physical_address);

@@ -666,9 +670,6 @@ static bool mshv_handle_gpa_intercept(struct mshv_vp *vp)

	return ret;
}
#else  /* CONFIG_X86_64 */
static bool mshv_handle_gpa_intercept(struct mshv_vp *vp) { return false; }
#endif /* CONFIG_X86_64 */

static bool mshv_vp_handle_intercept(struct mshv_vp *vp)
{
@@ -1280,7 +1281,7 @@ mshv_map_user_memory(struct mshv_partition *partition,
	long ret;

	if (mem.flags & BIT(MSHV_SET_MEM_BIT_UNMAP) ||
	    !access_ok((const void *)mem.userspace_addr, mem.size))
	    !access_ok((const void __user *)mem.userspace_addr, mem.size))
		return -EINVAL;

	mmap_read_lock(current->mm);
Loading