125b48b4cSDavid Hildenbrand (Red Hat) // SPDX-License-Identifier: GPL-2.0-only 225b48b4cSDavid Hildenbrand (Red Hat) /* 325b48b4cSDavid Hildenbrand (Red Hat) * Common interface for implementing a memory balloon, including support 425b48b4cSDavid Hildenbrand (Red Hat) * for migration of pages inflated in a memory balloon. 525b48b4cSDavid Hildenbrand (Red Hat) * 625b48b4cSDavid Hildenbrand (Red Hat) * Copyright (C) 2012, Red Hat, Inc. Rafael Aquini <aquini@redhat.com> 725b48b4cSDavid Hildenbrand (Red Hat) */ 825b48b4cSDavid Hildenbrand (Red Hat) #include <linux/mm.h> 925b48b4cSDavid Hildenbrand (Red Hat) #include <linux/slab.h> 1025b48b4cSDavid Hildenbrand (Red Hat) #include <linux/export.h> 1125b48b4cSDavid Hildenbrand (Red Hat) #include <linux/balloon.h> 1225b48b4cSDavid Hildenbrand (Red Hat) 1325b48b4cSDavid Hildenbrand (Red Hat) /* 1425b48b4cSDavid Hildenbrand (Red Hat) * Lock protecting the balloon_dev_info of all devices. We don't really 1525b48b4cSDavid Hildenbrand (Red Hat) * expect more than one device. 1625b48b4cSDavid Hildenbrand (Red Hat) */ 1725b48b4cSDavid Hildenbrand (Red Hat) static DEFINE_SPINLOCK(balloon_pages_lock); 1825b48b4cSDavid Hildenbrand (Red Hat) 1925b48b4cSDavid Hildenbrand (Red Hat) /** 2025b48b4cSDavid Hildenbrand (Red Hat) * balloon_page_insert - insert a page into the balloon's page list and make 2125b48b4cSDavid Hildenbrand (Red Hat) * the page->private assignment accordingly. 2225b48b4cSDavid Hildenbrand (Red Hat) * @balloon : pointer to balloon device 2325b48b4cSDavid Hildenbrand (Red Hat) * @page : page to be assigned as a 'balloon page' 2425b48b4cSDavid Hildenbrand (Red Hat) * 2525b48b4cSDavid Hildenbrand (Red Hat) * Caller must ensure the balloon_pages_lock is held. 2625b48b4cSDavid Hildenbrand (Red Hat) */ 2725b48b4cSDavid Hildenbrand (Red Hat) static void balloon_page_insert(struct balloon_dev_info *balloon, 2825b48b4cSDavid Hildenbrand (Red Hat) struct page *page) 2925b48b4cSDavid Hildenbrand (Red Hat) { 3025b48b4cSDavid Hildenbrand (Red Hat) lockdep_assert_held(&balloon_pages_lock); 3125b48b4cSDavid Hildenbrand (Red Hat) __SetPageOffline(page); 32*cd8e95d8SDavid Hildenbrand (Red Hat) if (IS_ENABLED(CONFIG_BALLOON_MIGRATION)) { 3325b48b4cSDavid Hildenbrand (Red Hat) SetPageMovableOps(page); 3425b48b4cSDavid Hildenbrand (Red Hat) set_page_private(page, (unsigned long)balloon); 3525b48b4cSDavid Hildenbrand (Red Hat) } 3625b48b4cSDavid Hildenbrand (Red Hat) list_add(&page->lru, &balloon->pages); 3725b48b4cSDavid Hildenbrand (Red Hat) } 3825b48b4cSDavid Hildenbrand (Red Hat) 3925b48b4cSDavid Hildenbrand (Red Hat) /** 4025b48b4cSDavid Hildenbrand (Red Hat) * balloon_page_finalize - prepare a balloon page that was removed from the 4125b48b4cSDavid Hildenbrand (Red Hat) * balloon list for release to the page allocator 4225b48b4cSDavid Hildenbrand (Red Hat) * @page: page to be released to the page allocator 4325b48b4cSDavid Hildenbrand (Red Hat) * 4425b48b4cSDavid Hildenbrand (Red Hat) * Caller must ensure the balloon_pages_lock is held. 4525b48b4cSDavid Hildenbrand (Red Hat) */ 4625b48b4cSDavid Hildenbrand (Red Hat) static void balloon_page_finalize(struct page *page) 4725b48b4cSDavid Hildenbrand (Red Hat) { 4825b48b4cSDavid Hildenbrand (Red Hat) lockdep_assert_held(&balloon_pages_lock); 49*cd8e95d8SDavid Hildenbrand (Red Hat) if (IS_ENABLED(CONFIG_BALLOON_MIGRATION)) 5025b48b4cSDavid Hildenbrand (Red Hat) set_page_private(page, 0); 5125b48b4cSDavid Hildenbrand (Red Hat) /* PageOffline is sticky until the page is freed to the buddy. */ 5225b48b4cSDavid Hildenbrand (Red Hat) } 5325b48b4cSDavid Hildenbrand (Red Hat) 5425b48b4cSDavid Hildenbrand (Red Hat) static void balloon_page_enqueue_one(struct balloon_dev_info *b_dev_info, 5525b48b4cSDavid Hildenbrand (Red Hat) struct page *page) 5625b48b4cSDavid Hildenbrand (Red Hat) { 5725b48b4cSDavid Hildenbrand (Red Hat) balloon_page_insert(b_dev_info, page); 5825b48b4cSDavid Hildenbrand (Red Hat) if (b_dev_info->adjust_managed_page_count) 5925b48b4cSDavid Hildenbrand (Red Hat) adjust_managed_page_count(page, -1); 6025b48b4cSDavid Hildenbrand (Red Hat) __count_vm_event(BALLOON_INFLATE); 6125b48b4cSDavid Hildenbrand (Red Hat) inc_node_page_state(page, NR_BALLOON_PAGES); 6225b48b4cSDavid Hildenbrand (Red Hat) } 6325b48b4cSDavid Hildenbrand (Red Hat) 6425b48b4cSDavid Hildenbrand (Red Hat) /** 6525b48b4cSDavid Hildenbrand (Red Hat) * balloon_page_list_enqueue() - inserts a list of pages into the balloon page 6625b48b4cSDavid Hildenbrand (Red Hat) * list. 6725b48b4cSDavid Hildenbrand (Red Hat) * @b_dev_info: balloon device descriptor where we will insert a new page to 6825b48b4cSDavid Hildenbrand (Red Hat) * @pages: pages to enqueue - allocated using balloon_page_alloc. 6925b48b4cSDavid Hildenbrand (Red Hat) * 7025b48b4cSDavid Hildenbrand (Red Hat) * Driver must call this function to properly enqueue balloon pages before 7125b48b4cSDavid Hildenbrand (Red Hat) * definitively removing them from the guest system. 7225b48b4cSDavid Hildenbrand (Red Hat) * 7325b48b4cSDavid Hildenbrand (Red Hat) * Return: number of pages that were enqueued. 7425b48b4cSDavid Hildenbrand (Red Hat) */ 7525b48b4cSDavid Hildenbrand (Red Hat) size_t balloon_page_list_enqueue(struct balloon_dev_info *b_dev_info, 7625b48b4cSDavid Hildenbrand (Red Hat) struct list_head *pages) 7725b48b4cSDavid Hildenbrand (Red Hat) { 7825b48b4cSDavid Hildenbrand (Red Hat) struct page *page, *tmp; 7925b48b4cSDavid Hildenbrand (Red Hat) unsigned long flags; 8025b48b4cSDavid Hildenbrand (Red Hat) size_t n_pages = 0; 8125b48b4cSDavid Hildenbrand (Red Hat) 8225b48b4cSDavid Hildenbrand (Red Hat) spin_lock_irqsave(&balloon_pages_lock, flags); 8325b48b4cSDavid Hildenbrand (Red Hat) list_for_each_entry_safe(page, tmp, pages, lru) { 8425b48b4cSDavid Hildenbrand (Red Hat) list_del(&page->lru); 8525b48b4cSDavid Hildenbrand (Red Hat) balloon_page_enqueue_one(b_dev_info, page); 8625b48b4cSDavid Hildenbrand (Red Hat) n_pages++; 8725b48b4cSDavid Hildenbrand (Red Hat) } 8825b48b4cSDavid Hildenbrand (Red Hat) spin_unlock_irqrestore(&balloon_pages_lock, flags); 8925b48b4cSDavid Hildenbrand (Red Hat) return n_pages; 9025b48b4cSDavid Hildenbrand (Red Hat) } 9125b48b4cSDavid Hildenbrand (Red Hat) EXPORT_SYMBOL_GPL(balloon_page_list_enqueue); 9225b48b4cSDavid Hildenbrand (Red Hat) 9325b48b4cSDavid Hildenbrand (Red Hat) /** 9425b48b4cSDavid Hildenbrand (Red Hat) * balloon_page_list_dequeue() - removes pages from balloon's page list and 9525b48b4cSDavid Hildenbrand (Red Hat) * returns a list of the pages. 9625b48b4cSDavid Hildenbrand (Red Hat) * @b_dev_info: balloon device descriptor where we will grab a page from. 9725b48b4cSDavid Hildenbrand (Red Hat) * @pages: pointer to the list of pages that would be returned to the caller. 9825b48b4cSDavid Hildenbrand (Red Hat) * @n_req_pages: number of requested pages. 9925b48b4cSDavid Hildenbrand (Red Hat) * 10025b48b4cSDavid Hildenbrand (Red Hat) * Driver must call this function to properly de-allocate a previous enlisted 10125b48b4cSDavid Hildenbrand (Red Hat) * balloon pages before definitively releasing it back to the guest system. 10225b48b4cSDavid Hildenbrand (Red Hat) * This function tries to remove @n_req_pages from the ballooned pages and 10325b48b4cSDavid Hildenbrand (Red Hat) * return them to the caller in the @pages list. 10425b48b4cSDavid Hildenbrand (Red Hat) * 10525b48b4cSDavid Hildenbrand (Red Hat) * Note that this function may fail to dequeue some pages even if the balloon 10625b48b4cSDavid Hildenbrand (Red Hat) * isn't empty - since the page list can be temporarily empty due to compaction 10725b48b4cSDavid Hildenbrand (Red Hat) * of isolated pages. 10825b48b4cSDavid Hildenbrand (Red Hat) * 10925b48b4cSDavid Hildenbrand (Red Hat) * Return: number of pages that were added to the @pages list. 11025b48b4cSDavid Hildenbrand (Red Hat) */ 11125b48b4cSDavid Hildenbrand (Red Hat) size_t balloon_page_list_dequeue(struct balloon_dev_info *b_dev_info, 11225b48b4cSDavid Hildenbrand (Red Hat) struct list_head *pages, size_t n_req_pages) 11325b48b4cSDavid Hildenbrand (Red Hat) { 11425b48b4cSDavid Hildenbrand (Red Hat) struct page *page, *tmp; 11525b48b4cSDavid Hildenbrand (Red Hat) unsigned long flags; 11625b48b4cSDavid Hildenbrand (Red Hat) size_t n_pages = 0; 11725b48b4cSDavid Hildenbrand (Red Hat) 11825b48b4cSDavid Hildenbrand (Red Hat) spin_lock_irqsave(&balloon_pages_lock, flags); 11925b48b4cSDavid Hildenbrand (Red Hat) list_for_each_entry_safe(page, tmp, &b_dev_info->pages, lru) { 12025b48b4cSDavid Hildenbrand (Red Hat) if (n_pages == n_req_pages) 12125b48b4cSDavid Hildenbrand (Red Hat) break; 12225b48b4cSDavid Hildenbrand (Red Hat) list_del(&page->lru); 12325b48b4cSDavid Hildenbrand (Red Hat) if (b_dev_info->adjust_managed_page_count) 12425b48b4cSDavid Hildenbrand (Red Hat) adjust_managed_page_count(page, 1); 12525b48b4cSDavid Hildenbrand (Red Hat) balloon_page_finalize(page); 12625b48b4cSDavid Hildenbrand (Red Hat) __count_vm_event(BALLOON_DEFLATE); 12725b48b4cSDavid Hildenbrand (Red Hat) list_add(&page->lru, pages); 12825b48b4cSDavid Hildenbrand (Red Hat) dec_node_page_state(page, NR_BALLOON_PAGES); 12925b48b4cSDavid Hildenbrand (Red Hat) n_pages++; 13025b48b4cSDavid Hildenbrand (Red Hat) } 13125b48b4cSDavid Hildenbrand (Red Hat) spin_unlock_irqrestore(&balloon_pages_lock, flags); 13225b48b4cSDavid Hildenbrand (Red Hat) 13325b48b4cSDavid Hildenbrand (Red Hat) return n_pages; 13425b48b4cSDavid Hildenbrand (Red Hat) } 13525b48b4cSDavid Hildenbrand (Red Hat) EXPORT_SYMBOL_GPL(balloon_page_list_dequeue); 13625b48b4cSDavid Hildenbrand (Red Hat) 13725b48b4cSDavid Hildenbrand (Red Hat) /** 13825b48b4cSDavid Hildenbrand (Red Hat) * balloon_page_alloc - allocates a new page for insertion into the balloon 13925b48b4cSDavid Hildenbrand (Red Hat) * page list. 14025b48b4cSDavid Hildenbrand (Red Hat) * 14125b48b4cSDavid Hildenbrand (Red Hat) * Driver must call this function to properly allocate a new balloon page. 14225b48b4cSDavid Hildenbrand (Red Hat) * Driver must call balloon_page_enqueue before definitively removing the page 14325b48b4cSDavid Hildenbrand (Red Hat) * from the guest system. 14425b48b4cSDavid Hildenbrand (Red Hat) * 14525b48b4cSDavid Hildenbrand (Red Hat) * Return: struct page for the allocated page or NULL on allocation failure. 14625b48b4cSDavid Hildenbrand (Red Hat) */ 14725b48b4cSDavid Hildenbrand (Red Hat) struct page *balloon_page_alloc(void) 14825b48b4cSDavid Hildenbrand (Red Hat) { 14925b48b4cSDavid Hildenbrand (Red Hat) gfp_t gfp_flags = __GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_NOWARN; 15025b48b4cSDavid Hildenbrand (Red Hat) 151*cd8e95d8SDavid Hildenbrand (Red Hat) if (IS_ENABLED(CONFIG_BALLOON_MIGRATION)) 15225b48b4cSDavid Hildenbrand (Red Hat) gfp_flags |= GFP_HIGHUSER_MOVABLE; 15325b48b4cSDavid Hildenbrand (Red Hat) else 15425b48b4cSDavid Hildenbrand (Red Hat) gfp_flags |= GFP_HIGHUSER; 15525b48b4cSDavid Hildenbrand (Red Hat) 15625b48b4cSDavid Hildenbrand (Red Hat) return alloc_page(gfp_flags); 15725b48b4cSDavid Hildenbrand (Red Hat) } 15825b48b4cSDavid Hildenbrand (Red Hat) EXPORT_SYMBOL_GPL(balloon_page_alloc); 15925b48b4cSDavid Hildenbrand (Red Hat) 16025b48b4cSDavid Hildenbrand (Red Hat) /** 16125b48b4cSDavid Hildenbrand (Red Hat) * balloon_page_enqueue - inserts a new page into the balloon page list. 16225b48b4cSDavid Hildenbrand (Red Hat) * 16325b48b4cSDavid Hildenbrand (Red Hat) * @b_dev_info: balloon device descriptor where we will insert a new page 16425b48b4cSDavid Hildenbrand (Red Hat) * @page: new page to enqueue - allocated using balloon_page_alloc. 16525b48b4cSDavid Hildenbrand (Red Hat) * 16625b48b4cSDavid Hildenbrand (Red Hat) * Drivers must call this function to properly enqueue a new allocated balloon 16725b48b4cSDavid Hildenbrand (Red Hat) * page before definitively removing the page from the guest system. 16825b48b4cSDavid Hildenbrand (Red Hat) * 16925b48b4cSDavid Hildenbrand (Red Hat) * Drivers must not enqueue pages while page->lru is still in 17025b48b4cSDavid Hildenbrand (Red Hat) * use, and must not use page->lru until a page was unqueued again. 17125b48b4cSDavid Hildenbrand (Red Hat) */ 17225b48b4cSDavid Hildenbrand (Red Hat) void balloon_page_enqueue(struct balloon_dev_info *b_dev_info, 17325b48b4cSDavid Hildenbrand (Red Hat) struct page *page) 17425b48b4cSDavid Hildenbrand (Red Hat) { 17525b48b4cSDavid Hildenbrand (Red Hat) unsigned long flags; 17625b48b4cSDavid Hildenbrand (Red Hat) 17725b48b4cSDavid Hildenbrand (Red Hat) spin_lock_irqsave(&balloon_pages_lock, flags); 17825b48b4cSDavid Hildenbrand (Red Hat) balloon_page_enqueue_one(b_dev_info, page); 17925b48b4cSDavid Hildenbrand (Red Hat) spin_unlock_irqrestore(&balloon_pages_lock, flags); 18025b48b4cSDavid Hildenbrand (Red Hat) } 18125b48b4cSDavid Hildenbrand (Red Hat) EXPORT_SYMBOL_GPL(balloon_page_enqueue); 18225b48b4cSDavid Hildenbrand (Red Hat) 18325b48b4cSDavid Hildenbrand (Red Hat) /** 18425b48b4cSDavid Hildenbrand (Red Hat) * balloon_page_dequeue - removes a page from balloon's page list and returns 18525b48b4cSDavid Hildenbrand (Red Hat) * its address to allow the driver to release the page. 18625b48b4cSDavid Hildenbrand (Red Hat) * @b_dev_info: balloon device descriptor where we will grab a page from. 18725b48b4cSDavid Hildenbrand (Red Hat) * 18825b48b4cSDavid Hildenbrand (Red Hat) * Driver must call this function to properly dequeue a previously enqueued page 18925b48b4cSDavid Hildenbrand (Red Hat) * before definitively releasing it back to the guest system. 19025b48b4cSDavid Hildenbrand (Red Hat) * 19125b48b4cSDavid Hildenbrand (Red Hat) * Caller must perform its own accounting to ensure that this 19225b48b4cSDavid Hildenbrand (Red Hat) * function is called only if some pages are actually enqueued. 19325b48b4cSDavid Hildenbrand (Red Hat) * 19425b48b4cSDavid Hildenbrand (Red Hat) * Note that this function may fail to dequeue some pages even if there are 19525b48b4cSDavid Hildenbrand (Red Hat) * some enqueued pages - since the page list can be temporarily empty due to 19625b48b4cSDavid Hildenbrand (Red Hat) * the compaction of isolated pages. 19725b48b4cSDavid Hildenbrand (Red Hat) * 19825b48b4cSDavid Hildenbrand (Red Hat) * TODO: remove the caller accounting requirements, and allow caller to wait 19925b48b4cSDavid Hildenbrand (Red Hat) * until all pages can be dequeued. 20025b48b4cSDavid Hildenbrand (Red Hat) * 20125b48b4cSDavid Hildenbrand (Red Hat) * Return: struct page for the dequeued page, or NULL if no page was dequeued. 20225b48b4cSDavid Hildenbrand (Red Hat) */ 20325b48b4cSDavid Hildenbrand (Red Hat) struct page *balloon_page_dequeue(struct balloon_dev_info *b_dev_info) 20425b48b4cSDavid Hildenbrand (Red Hat) { 20525b48b4cSDavid Hildenbrand (Red Hat) unsigned long flags; 20625b48b4cSDavid Hildenbrand (Red Hat) LIST_HEAD(pages); 20725b48b4cSDavid Hildenbrand (Red Hat) int n_pages; 20825b48b4cSDavid Hildenbrand (Red Hat) 20925b48b4cSDavid Hildenbrand (Red Hat) n_pages = balloon_page_list_dequeue(b_dev_info, &pages, 1); 21025b48b4cSDavid Hildenbrand (Red Hat) 21125b48b4cSDavid Hildenbrand (Red Hat) if (n_pages != 1) { 21225b48b4cSDavid Hildenbrand (Red Hat) /* 21325b48b4cSDavid Hildenbrand (Red Hat) * If we are unable to dequeue a balloon page because the page 21425b48b4cSDavid Hildenbrand (Red Hat) * list is empty and there are no isolated pages, then something 21525b48b4cSDavid Hildenbrand (Red Hat) * went out of track and some balloon pages are lost. 21625b48b4cSDavid Hildenbrand (Red Hat) * BUG() here, otherwise the balloon driver may get stuck in 21725b48b4cSDavid Hildenbrand (Red Hat) * an infinite loop while attempting to release all its pages. 21825b48b4cSDavid Hildenbrand (Red Hat) */ 21925b48b4cSDavid Hildenbrand (Red Hat) spin_lock_irqsave(&balloon_pages_lock, flags); 22025b48b4cSDavid Hildenbrand (Red Hat) if (unlikely(list_empty(&b_dev_info->pages) && 22125b48b4cSDavid Hildenbrand (Red Hat) !b_dev_info->isolated_pages)) 22225b48b4cSDavid Hildenbrand (Red Hat) BUG(); 22325b48b4cSDavid Hildenbrand (Red Hat) spin_unlock_irqrestore(&balloon_pages_lock, flags); 22425b48b4cSDavid Hildenbrand (Red Hat) return NULL; 22525b48b4cSDavid Hildenbrand (Red Hat) } 22625b48b4cSDavid Hildenbrand (Red Hat) return list_first_entry(&pages, struct page, lru); 22725b48b4cSDavid Hildenbrand (Red Hat) } 22825b48b4cSDavid Hildenbrand (Red Hat) EXPORT_SYMBOL_GPL(balloon_page_dequeue); 22925b48b4cSDavid Hildenbrand (Red Hat) 230*cd8e95d8SDavid Hildenbrand (Red Hat) #ifdef CONFIG_BALLOON_MIGRATION 23125b48b4cSDavid Hildenbrand (Red Hat) static struct balloon_dev_info *balloon_page_device(struct page *page) 23225b48b4cSDavid Hildenbrand (Red Hat) { 23325b48b4cSDavid Hildenbrand (Red Hat) return (struct balloon_dev_info *)page_private(page); 23425b48b4cSDavid Hildenbrand (Red Hat) } 23525b48b4cSDavid Hildenbrand (Red Hat) 23625b48b4cSDavid Hildenbrand (Red Hat) static bool balloon_page_isolate(struct page *page, isolate_mode_t mode) 23725b48b4cSDavid Hildenbrand (Red Hat) 23825b48b4cSDavid Hildenbrand (Red Hat) { 23925b48b4cSDavid Hildenbrand (Red Hat) struct balloon_dev_info *b_dev_info; 24025b48b4cSDavid Hildenbrand (Red Hat) unsigned long flags; 24125b48b4cSDavid Hildenbrand (Red Hat) 24225b48b4cSDavid Hildenbrand (Red Hat) spin_lock_irqsave(&balloon_pages_lock, flags); 24325b48b4cSDavid Hildenbrand (Red Hat) b_dev_info = balloon_page_device(page); 24425b48b4cSDavid Hildenbrand (Red Hat) if (!b_dev_info) { 24525b48b4cSDavid Hildenbrand (Red Hat) /* 24625b48b4cSDavid Hildenbrand (Red Hat) * The page already got deflated and removed from the 24725b48b4cSDavid Hildenbrand (Red Hat) * balloon list. 24825b48b4cSDavid Hildenbrand (Red Hat) */ 24925b48b4cSDavid Hildenbrand (Red Hat) spin_unlock_irqrestore(&balloon_pages_lock, flags); 25025b48b4cSDavid Hildenbrand (Red Hat) return false; 25125b48b4cSDavid Hildenbrand (Red Hat) } 25225b48b4cSDavid Hildenbrand (Red Hat) list_del(&page->lru); 25325b48b4cSDavid Hildenbrand (Red Hat) b_dev_info->isolated_pages++; 25425b48b4cSDavid Hildenbrand (Red Hat) spin_unlock_irqrestore(&balloon_pages_lock, flags); 25525b48b4cSDavid Hildenbrand (Red Hat) 25625b48b4cSDavid Hildenbrand (Red Hat) return true; 25725b48b4cSDavid Hildenbrand (Red Hat) } 25825b48b4cSDavid Hildenbrand (Red Hat) 25925b48b4cSDavid Hildenbrand (Red Hat) static void balloon_page_putback(struct page *page) 26025b48b4cSDavid Hildenbrand (Red Hat) { 26125b48b4cSDavid Hildenbrand (Red Hat) struct balloon_dev_info *b_dev_info = balloon_page_device(page); 26225b48b4cSDavid Hildenbrand (Red Hat) unsigned long flags; 26325b48b4cSDavid Hildenbrand (Red Hat) 26425b48b4cSDavid Hildenbrand (Red Hat) /* 26525b48b4cSDavid Hildenbrand (Red Hat) * When we isolated the page, the page was still inflated in a balloon 26625b48b4cSDavid Hildenbrand (Red Hat) * device. As isolated balloon pages cannot get deflated, we still have 26725b48b4cSDavid Hildenbrand (Red Hat) * a balloon device here. 26825b48b4cSDavid Hildenbrand (Red Hat) */ 26925b48b4cSDavid Hildenbrand (Red Hat) if (WARN_ON_ONCE(!b_dev_info)) 27025b48b4cSDavid Hildenbrand (Red Hat) return; 27125b48b4cSDavid Hildenbrand (Red Hat) 27225b48b4cSDavid Hildenbrand (Red Hat) spin_lock_irqsave(&balloon_pages_lock, flags); 27325b48b4cSDavid Hildenbrand (Red Hat) list_add(&page->lru, &b_dev_info->pages); 27425b48b4cSDavid Hildenbrand (Red Hat) b_dev_info->isolated_pages--; 27525b48b4cSDavid Hildenbrand (Red Hat) spin_unlock_irqrestore(&balloon_pages_lock, flags); 27625b48b4cSDavid Hildenbrand (Red Hat) } 27725b48b4cSDavid Hildenbrand (Red Hat) 27825b48b4cSDavid Hildenbrand (Red Hat) static int balloon_page_migrate(struct page *newpage, struct page *page, 27925b48b4cSDavid Hildenbrand (Red Hat) enum migrate_mode mode) 28025b48b4cSDavid Hildenbrand (Red Hat) { 28125b48b4cSDavid Hildenbrand (Red Hat) struct balloon_dev_info *b_dev_info = balloon_page_device(page); 28225b48b4cSDavid Hildenbrand (Red Hat) unsigned long flags; 28325b48b4cSDavid Hildenbrand (Red Hat) int rc; 28425b48b4cSDavid Hildenbrand (Red Hat) 28525b48b4cSDavid Hildenbrand (Red Hat) /* 28625b48b4cSDavid Hildenbrand (Red Hat) * When we isolated the page, the page was still inflated in a balloon 28725b48b4cSDavid Hildenbrand (Red Hat) * device. As isolated balloon pages cannot get deflated, we still have 28825b48b4cSDavid Hildenbrand (Red Hat) * a balloon device here. 28925b48b4cSDavid Hildenbrand (Red Hat) */ 29025b48b4cSDavid Hildenbrand (Red Hat) if (WARN_ON_ONCE(!b_dev_info)) 29125b48b4cSDavid Hildenbrand (Red Hat) return -EAGAIN; 29225b48b4cSDavid Hildenbrand (Red Hat) 29325b48b4cSDavid Hildenbrand (Red Hat) rc = b_dev_info->migratepage(b_dev_info, newpage, page, mode); 29425b48b4cSDavid Hildenbrand (Red Hat) if (rc < 0 && rc != -ENOENT) 29525b48b4cSDavid Hildenbrand (Red Hat) return rc; 29625b48b4cSDavid Hildenbrand (Red Hat) 29725b48b4cSDavid Hildenbrand (Red Hat) spin_lock_irqsave(&balloon_pages_lock, flags); 29825b48b4cSDavid Hildenbrand (Red Hat) if (!rc) { 29925b48b4cSDavid Hildenbrand (Red Hat) /* Insert the new page into the balloon list. */ 30025b48b4cSDavid Hildenbrand (Red Hat) get_page(newpage); 30125b48b4cSDavid Hildenbrand (Red Hat) balloon_page_insert(b_dev_info, newpage); 30225b48b4cSDavid Hildenbrand (Red Hat) __count_vm_event(BALLOON_MIGRATE); 30325b48b4cSDavid Hildenbrand (Red Hat) 30425b48b4cSDavid Hildenbrand (Red Hat) if (b_dev_info->adjust_managed_page_count && 30525b48b4cSDavid Hildenbrand (Red Hat) page_zone(page) != page_zone(newpage)) { 30625b48b4cSDavid Hildenbrand (Red Hat) /* 30725b48b4cSDavid Hildenbrand (Red Hat) * When we migrate a page to a different zone we 30825b48b4cSDavid Hildenbrand (Red Hat) * have to fixup the count of both involved zones. 30925b48b4cSDavid Hildenbrand (Red Hat) */ 31025b48b4cSDavid Hildenbrand (Red Hat) adjust_managed_page_count(page, 1); 31125b48b4cSDavid Hildenbrand (Red Hat) adjust_managed_page_count(newpage, -1); 31225b48b4cSDavid Hildenbrand (Red Hat) } 31325b48b4cSDavid Hildenbrand (Red Hat) } else { 31425b48b4cSDavid Hildenbrand (Red Hat) /* Old page was deflated but new page not inflated. */ 31525b48b4cSDavid Hildenbrand (Red Hat) __count_vm_event(BALLOON_DEFLATE); 31625b48b4cSDavid Hildenbrand (Red Hat) 31725b48b4cSDavid Hildenbrand (Red Hat) if (b_dev_info->adjust_managed_page_count) 31825b48b4cSDavid Hildenbrand (Red Hat) adjust_managed_page_count(page, 1); 31925b48b4cSDavid Hildenbrand (Red Hat) } 32025b48b4cSDavid Hildenbrand (Red Hat) 32125b48b4cSDavid Hildenbrand (Red Hat) b_dev_info->isolated_pages--; 32225b48b4cSDavid Hildenbrand (Red Hat) 32325b48b4cSDavid Hildenbrand (Red Hat) /* Free the now-deflated page we isolated in balloon_page_isolate(). */ 32425b48b4cSDavid Hildenbrand (Red Hat) balloon_page_finalize(page); 32525b48b4cSDavid Hildenbrand (Red Hat) spin_unlock_irqrestore(&balloon_pages_lock, flags); 32625b48b4cSDavid Hildenbrand (Red Hat) 32725b48b4cSDavid Hildenbrand (Red Hat) put_page(page); 32825b48b4cSDavid Hildenbrand (Red Hat) 32925b48b4cSDavid Hildenbrand (Red Hat) return 0; 33025b48b4cSDavid Hildenbrand (Red Hat) } 33125b48b4cSDavid Hildenbrand (Red Hat) 33225b48b4cSDavid Hildenbrand (Red Hat) static const struct movable_operations balloon_mops = { 33325b48b4cSDavid Hildenbrand (Red Hat) .migrate_page = balloon_page_migrate, 33425b48b4cSDavid Hildenbrand (Red Hat) .isolate_page = balloon_page_isolate, 33525b48b4cSDavid Hildenbrand (Red Hat) .putback_page = balloon_page_putback, 33625b48b4cSDavid Hildenbrand (Red Hat) }; 33725b48b4cSDavid Hildenbrand (Red Hat) 33825b48b4cSDavid Hildenbrand (Red Hat) static int __init balloon_init(void) 33925b48b4cSDavid Hildenbrand (Red Hat) { 34025b48b4cSDavid Hildenbrand (Red Hat) return set_movable_ops(&balloon_mops, PGTY_offline); 34125b48b4cSDavid Hildenbrand (Red Hat) } 34225b48b4cSDavid Hildenbrand (Red Hat) core_initcall(balloon_init); 34325b48b4cSDavid Hildenbrand (Red Hat) 344*cd8e95d8SDavid Hildenbrand (Red Hat) #endif /* CONFIG_BALLOON_MIGRATION */ 345