2121#include <linux/memory.h>
2222#include <linux/notifier.h>
2323#include <linux/percpu_counter.h>
24+ #include <linux/page_reporting.h>
2425
2526#include <linux/hyperv.h>
2627#include <asm/hyperv-tlfs.h>
@@ -563,6 +564,8 @@ struct hv_dynmem_device {
563564 * The negotiated version agreed by host.
564565 */
565566 __u32 version ;
567+
568+ struct page_reporting_dev_info pr_dev_info ;
566569};
567570
568571static struct hv_dynmem_device dm_device ;
@@ -1565,6 +1568,89 @@ static void balloon_onchannelcallback(void *context)
15651568
15661569}
15671570
1571+ /* Hyper-V only supports reporting 2MB pages or higher */
1572+ #define HV_MIN_PAGE_REPORTING_ORDER 9
1573+ #define HV_MIN_PAGE_REPORTING_LEN (HV_HYP_PAGE_SIZE << HV_MIN_PAGE_REPORTING_ORDER)
1574+ static int hv_free_page_report (struct page_reporting_dev_info * pr_dev_info ,
1575+ struct scatterlist * sgl , unsigned int nents )
1576+ {
1577+ unsigned long flags ;
1578+ struct hv_memory_hint * hint ;
1579+ int i ;
1580+ u64 status ;
1581+ struct scatterlist * sg ;
1582+
1583+ WARN_ON_ONCE (nents > HV_MEMORY_HINT_MAX_GPA_PAGE_RANGES );
1584+ WARN_ON_ONCE (sgl -> length < HV_MIN_PAGE_REPORTING_LEN );
1585+ local_irq_save (flags );
1586+ hint = * (struct hv_memory_hint * * )this_cpu_ptr (hyperv_pcpu_input_arg );
1587+ if (!hint ) {
1588+ local_irq_restore (flags );
1589+ return - ENOSPC ;
1590+ }
1591+
1592+ hint -> type = HV_EXT_MEMORY_HEAT_HINT_TYPE_COLD_DISCARD ;
1593+ hint -> reserved = 0 ;
1594+ for_each_sg (sgl , sg , nents , i ) {
1595+ union hv_gpa_page_range * range ;
1596+
1597+ range = & hint -> ranges [i ];
1598+ range -> address_space = 0 ;
1599+ /* page reporting only reports 2MB pages or higher */
1600+ range -> page .largepage = 1 ;
1601+ range -> page .additional_pages =
1602+ (sg -> length / HV_MIN_PAGE_REPORTING_LEN ) - 1 ;
1603+ range -> page_size = HV_GPA_PAGE_RANGE_PAGE_SIZE_2MB ;
1604+ range -> base_large_pfn =
1605+ page_to_hvpfn (sg_page (sg )) >> HV_MIN_PAGE_REPORTING_ORDER ;
1606+ }
1607+
1608+ status = hv_do_rep_hypercall (HV_EXT_CALL_MEMORY_HEAT_HINT , nents , 0 ,
1609+ hint , NULL );
1610+ local_irq_restore (flags );
1611+ if ((status & HV_HYPERCALL_RESULT_MASK ) != HV_STATUS_SUCCESS ) {
1612+ pr_err ("Cold memory discard hypercall failed with status %llx\n" ,
1613+ status );
1614+ return - EINVAL ;
1615+ }
1616+
1617+ return 0 ;
1618+ }
1619+
1620+ static void enable_page_reporting (void )
1621+ {
1622+ int ret ;
1623+
1624+ /* Essentially, validating 'PAGE_REPORTING_MIN_ORDER' is big enough. */
1625+ if (pageblock_order < HV_MIN_PAGE_REPORTING_ORDER ) {
1626+ pr_debug ("Cold memory discard is only supported on 2MB pages and above\n" );
1627+ return ;
1628+ }
1629+
1630+ if (!hv_query_ext_cap (HV_EXT_CAPABILITY_MEMORY_COLD_DISCARD_HINT )) {
1631+ pr_debug ("Cold memory discard hint not supported by Hyper-V\n" );
1632+ return ;
1633+ }
1634+
1635+ BUILD_BUG_ON (PAGE_REPORTING_CAPACITY > HV_MEMORY_HINT_MAX_GPA_PAGE_RANGES );
1636+ dm_device .pr_dev_info .report = hv_free_page_report ;
1637+ ret = page_reporting_register (& dm_device .pr_dev_info );
1638+ if (ret < 0 ) {
1639+ dm_device .pr_dev_info .report = NULL ;
1640+ pr_err ("Failed to enable cold memory discard: %d\n" , ret );
1641+ } else {
1642+ pr_info ("Cold memory discard hint enabled\n" );
1643+ }
1644+ }
1645+
1646+ static void disable_page_reporting (void )
1647+ {
1648+ if (dm_device .pr_dev_info .report ) {
1649+ page_reporting_unregister (& dm_device .pr_dev_info );
1650+ dm_device .pr_dev_info .report = NULL ;
1651+ }
1652+ }
1653+
15681654static int balloon_connect_vsp (struct hv_device * dev )
15691655{
15701656 struct dm_version_request version_req ;
@@ -1710,6 +1796,7 @@ static int balloon_probe(struct hv_device *dev,
17101796 if (ret != 0 )
17111797 return ret ;
17121798
1799+ enable_page_reporting ();
17131800 dm_device .state = DM_INITIALIZED ;
17141801
17151802 dm_device .thread =
@@ -1724,6 +1811,7 @@ static int balloon_probe(struct hv_device *dev,
17241811probe_error :
17251812 dm_device .state = DM_INIT_ERROR ;
17261813 dm_device .thread = NULL ;
1814+ disable_page_reporting ();
17271815 vmbus_close (dev -> channel );
17281816#ifdef CONFIG_MEMORY_HOTPLUG
17291817 unregister_memory_notifier (& hv_memory_nb );
@@ -1746,6 +1834,7 @@ static int balloon_remove(struct hv_device *dev)
17461834 cancel_work_sync (& dm -> ha_wrk .wrk );
17471835
17481836 kthread_stop (dm -> thread );
1837+ disable_page_reporting ();
17491838 vmbus_close (dev -> channel );
17501839#ifdef CONFIG_MEMORY_HOTPLUG
17511840 unregister_memory_notifier (& hv_memory_nb );
0 commit comments