2121#include <linux/memory.h>
2222#include <linux/notifier.h>
2323#include <linux/percpu_counter.h>
24-
24+ #include <linux/page_reporting.h>
2525#include <linux/hyperv.h>
2626#include <asm/hyperv-tlfs.h>
2727
@@ -563,6 +563,9 @@ struct hv_dynmem_device {
563563 * The negotiated version agreed by host.
564564 */
565565 __u32 version ;
566+ #ifdef CONFIG_PAGE_REPORTING
567+ struct page_reporting_dev_info ph_dev_info ;
568+ #endif
566569};
567570
568571static struct hv_dynmem_device dm_device ;
@@ -1565,6 +1568,88 @@ static void balloon_onchannelcallback(void *context)
15651568
15661569}
15671570
1571+ #ifdef CONFIG_PAGE_REPORTING
1572+ static int hyperv_discard_pages (struct scatterlist * * sgs , int nents )
1573+ {
1574+ unsigned long flags ;
1575+ struct hv_memory_hint * hint ;
1576+ int i ;
1577+ struct scatterlist * sg ;
1578+ u64 status ;
1579+
1580+ WARN_ON (nents > HV_MAX_GPA_PAGE_RANGES );
1581+ local_irq_save (flags );
1582+ hint = * (struct hv_memory_hint * * )this_cpu_ptr (hyperv_pcpu_input_arg );
1583+ if (!hint ) {
1584+ local_irq_restore (flags );
1585+ return - EINVAL ;
1586+ }
1587+
1588+ hint -> type = HV_MEMORY_HINT_TYPE_COLD_DISCARD ;
1589+ hint -> reserved = 0 ;
1590+ for (i = 0 , sg = sgs [0 ]; sg ; sg = sg_next (sg ), i ++ ) {
1591+ int order ;
1592+ union hv_gpa_page_range * range ;
1593+
1594+ order = get_order (sg -> length );
1595+ range = & hint -> ranges [i ];
1596+ range -> address_space = 0 ;
1597+ range -> page .largepage = 1 ;
1598+ range -> page .additional_pages = (1ull << (order - 9 )) - 1 ;
1599+ range -> base_large_pfn = page_to_pfn (sg_page (sg )) >> 9 ;
1600+ }
1601+
1602+ WARN_ON (i != nents );
1603+
1604+ status = hv_do_rep_hypercall (HVCALL_MEMORY_HEAT_HINT , nents , 0 ,
1605+ hint , NULL );
1606+ local_irq_restore (flags );
1607+ status &= HV_HYPERCALL_RESULT_MASK ;
1608+ if (status != HV_STATUS_SUCCESS ) {
1609+ WARN_ONCE (1 ,
1610+ "Cold memory discard hypercall failed with status 0x%llx\n" ,
1611+ status );
1612+ return - EINVAL ;
1613+ } else {
1614+ return 0 ;
1615+ }
1616+ }
1617+
1618+ static int hv_page_hinting (struct page_reporting_dev_info * ph_dev_info ,
1619+ struct scatterlist * sg , unsigned int nents )
1620+ {
1621+ return hyperv_discard_pages (& sg , nents );
1622+ }
1623+
1624+ static void enable_page_reporting (void )
1625+ {
1626+ int ret ;
1627+
1628+ if (!hyperv_query_ext_cap (HV_CAPABILITY_MEMORY_COLD_DISCARD_HINT )) {
1629+ pr_info ("Cold memory discard hint not supported.\n" );
1630+ return ;
1631+ }
1632+
1633+ BUILD_BUG_ON (PAGE_REPORTING_CAPACITY > HV_MAX_GPA_PAGE_RANGES );
1634+ dm_device .ph_dev_info .report = hv_page_hinting ;
1635+ ret = page_reporting_register (& dm_device .ph_dev_info );
1636+ if (ret < 0 ) {
1637+ pr_err ("Failed to enable cold memory discard: %d\n" , ret );
1638+ dm_device .ph_dev_info .report = NULL ;
1639+ } else {
1640+ pr_info ("Cold memory discard hint enabled\n" );
1641+ }
1642+ }
1643+
1644+ static void disable_hinting (void )
1645+ {
1646+ if (dm_device .ph_dev_info .report ) {
1647+ page_reporting_unregister (& dm_device .ph_dev_info );
1648+ dm_device .ph_dev_info .report = NULL ;
1649+ }
1650+ }
1651+ #endif //CONFIG_PAGE_REPORTING
1652+
15681653static int balloon_connect_vsp (struct hv_device * dev )
15691654{
15701655 struct dm_version_request version_req ;
@@ -1710,6 +1795,10 @@ static int balloon_probe(struct hv_device *dev,
17101795 if (ret != 0 )
17111796 return ret ;
17121797
1798+ #ifdef CONFIG_PAGE_REPORTING
1799+ enable_page_reporting ();
1800+ #endif
1801+
17131802 dm_device .state = DM_INITIALIZED ;
17141803
17151804 dm_device .thread =
@@ -1725,6 +1814,9 @@ static int balloon_probe(struct hv_device *dev,
17251814 dm_device .state = DM_INIT_ERROR ;
17261815 dm_device .thread = NULL ;
17271816 vmbus_close (dev -> channel );
1817+ #ifdef CONFIG_PAGE_REPORTING
1818+ disable_hinting ();
1819+ #endif
17281820#ifdef CONFIG_MEMORY_HOTPLUG
17291821 unregister_memory_notifier (& hv_memory_nb );
17301822 restore_online_page_callback (& hv_online_page );
@@ -1747,6 +1839,9 @@ static int balloon_remove(struct hv_device *dev)
17471839
17481840 kthread_stop (dm -> thread );
17491841 vmbus_close (dev -> channel );
1842+ #ifdef CONFIG_PAGE_REPORTING
1843+ disable_hinting ();
1844+ #endif
17501845#ifdef CONFIG_MEMORY_HOTPLUG
17511846 unregister_memory_notifier (& hv_memory_nb );
17521847 restore_online_page_callback (& hv_online_page );
0 commit comments