Drivers: hv: balloon: Support memory hot add for larger page sizes

Still WIP.

Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c
index 35dcda2..e2bb077 100644
--- a/drivers/hv/hv_balloon.c
+++ b/drivers/hv/hv_balloon.c
@@ -500,7 +500,7 @@ enum hv_dm_state {
 static __u8 recv_buffer[HV_HYP_PAGE_SIZE];
 static __u8 balloon_up_send_buffer[HV_HYP_PAGE_SIZE];
 #define PAGES_IN_2M (2 * 1024 * 1024 / PAGE_SIZE)
-#define HA_CHUNK (128 * 1024 * 1024 / PAGE_SIZE)
+#define HA_CHUNK (memory_block_size_bytes() / PAGE_SIZE)
 
 struct hv_dynmem_device {
 	struct hv_device *dev;
@@ -986,6 +986,7 @@ static void hot_add_req(struct work_struct *dummy)
 #ifdef CONFIG_MEMORY_HOTPLUG
 	unsigned long pg_start, pfn_cnt;
 	unsigned long rg_start, rg_sz;
+	unsigned long page_count = 0;
 #endif
 	struct hv_dynmem_device *dm = &dm_device;
 
@@ -994,11 +995,11 @@ static void hot_add_req(struct work_struct *dummy)
 	resp.hdr.size = sizeof(struct dm_hot_add_response);
 
 #ifdef CONFIG_MEMORY_HOTPLUG
-	pg_start = dm->ha_wrk.ha_page_range.finfo.start_page;
-	pfn_cnt = dm->ha_wrk.ha_page_range.finfo.page_cnt;
+	pg_start = hvpfn_to_pfn(dm->ha_wrk.ha_page_range.finfo.start_page);
+	pfn_cnt = hvpfn_to_pfn(dm->ha_wrk.ha_page_range.finfo.page_cnt);
 
-	rg_start = dm->ha_wrk.ha_region_range.finfo.start_page;
-	rg_sz = dm->ha_wrk.ha_region_range.finfo.page_cnt;
+	rg_start = hvpfn_to_pfn(dm->ha_wrk.ha_region_range.finfo.start_page);
+	rg_sz = hvpfn_to_pfn(dm->ha_wrk.ha_region_range.finfo.page_cnt);
 
 	if ((rg_start == 0) && (!dm->host_specified_ha_region)) {
 		unsigned long region_size;
@@ -1022,10 +1023,11 @@ static void hot_add_req(struct work_struct *dummy)
 	}
 
 	if (do_hot_add)
-		resp.page_count = process_hot_add(pg_start, pfn_cnt,
+		page_count = process_hot_add(pg_start, pfn_cnt,
 						rg_start, rg_sz);
 
-	dm->num_pages_added += resp.page_count;
+	dm->num_pages_added += page_count;
+	resp.page_count = (__u32)(page_count * NR_HV_HYP_PAGES_IN_PAGE);
 #endif
 	/*
 	 * The result field of the response structure has the
@@ -1746,9 +1748,16 @@ static int balloon_connect_vsp(struct hv_device *dev)
 
 	/*
 	 * Specify our alignment requirements as it relates
-	 * memory hot-add. Specify 128MB alignment.
+	 * memory hot-add. The alignment should be the same as the memory block
+	 * size, and Hyper-V expects 2 ^ hot_add_alignment * 1MB is the
+	 * alignment in bytes.
 	 */
-	cap_msg.caps.cap_bits.hot_add_alignment = 7;
+	/*
+	 * XXX: memory_block_size_bytes() for ARM64 only is only implemented
+	 * when CONFIG_MEMORY_HOTPLUG=y, and for x86, it doesn't get exported
+	 */
+	cap_msg.caps.cap_bits.hot_add_alignment =
+		count_trailing_zeros(memory_block_size_bytes() / 0x100000);
 
 	/*
 	 * Currently the host does not use these
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index f565a89..f1acd61 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -1763,5 +1763,7 @@ static inline unsigned long virt_to_hvpfn(void *addr)
 #define HVPFN_UP(x)	(((x) + HV_HYP_PAGE_SIZE-1) >> HV_HYP_PAGE_SHIFT)
 #define HVPFN_DOWN(x)	((x) >> HV_HYP_PAGE_SHIFT)
 #define page_to_hvpfn(page)	(page_to_pfn(page) * NR_HV_HYP_PAGES_IN_PAGE)
+/* Caller need to ensure that 'hvpfn` is aligned to PAGE_SIZE */
+#define hvpfn_to_pfn(hvpfn)	((hvpfn) / NR_HV_HYP_PAGES_IN_PAGE)
 
 #endif /* _HYPERV_H */