Let's update the balloon page references, the balloon page list, the
BALLOON_MIGRATE counter and the isolated-pages counter in
balloon_page_migrate(), after letting the balloon->migratepage() callback
deal with the actual inflation+deflation.
Note that we now perform the balloon list modifications outside of any
implementation-specific locks: which is fine, there is nothing special
about these page actions that the lock would be protecting.
The old page is already no longer in the list (isolated) and the new page
is not yet in the list.
Let's use -ENOENT to communicate the special "inflation of new page failed
after already deflating the old page" to balloon_page_migrate() so it can
handle it accordingly.
While at it, rename balloon->b_dev_info to make it match the other
functions. Also, drop the comment above balloon_page_migrate(), which
seems unnecessary.
Link: https://lkml.kernel.org/r/20260119230133.3551867-6-david@kernel.org
Signed-off-by: David Hildenbrand (Red Hat) <david@kernel.org>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Christophe Leroy <christophe.leroy@csgroup.eu>
Cc: Eugenio Pérez <eperezma@redhat.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Jerrin Shaji George <jerrin.shaji-george@broadcom.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Madhavan Srinivasan <maddy@linux.ibm.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Nicholas Piggin <npiggin@gmail.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: SeongJae Park <sj@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
Cc: Zi Yan <ziy@nvidia.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
struct page *newpage, struct page *page,
enum migrate_mode mode)
{
- unsigned long flags;
-
/*
* loan/"inflate" the newpage first.
*
return -EBUSY;
}
- /* balloon page list reference */
- get_page(newpage);
-
/*
* When we migrate a page to a different zone, we have to fixup the
* count of both involved zones as we adjusted the managed page count
adjust_managed_page_count(newpage, -1);
}
- spin_lock_irqsave(&b_dev_info->pages_lock, flags);
- balloon_page_insert(b_dev_info, newpage);
- __count_vm_event(BALLOON_MIGRATE);
- b_dev_info->isolated_pages--;
- spin_unlock_irqrestore(&b_dev_info->pages_lock, flags);
-
/*
* activate/"deflate" the old page. We ignore any errors just like the
* other callers.
*/
plpar_page_set_active(page);
-
- balloon_page_finalize(page);
- /* balloon page list reference */
- put_page(page);
-
return 0;
}
#else /* CONFIG_BALLOON_COMPACTION */
* @page: a ballooned page that should be migrated.
* @mode: migration mode, ignored.
*
- * This function is really open-coded, but that is according to the interface
- * that balloon_compaction provides.
- *
* Return: zero on success, -EAGAIN when migration cannot be performed
- * momentarily, and -EBUSY if migration failed and should be retried
- * with that specific page.
+ * momentarily, -EBUSY if migration failed and should be retried
+ * with that specific page, and -ENOENT when deflating @page
+ * succeeded but inflating @newpage failed, effectively deflating
+ * the balloon.
*/
static int vmballoon_migratepage(struct balloon_dev_info *b_dev_info,
struct page *newpage, struct page *page,
enum migrate_mode mode)
{
- unsigned long status, flags;
+ unsigned long status;
struct vmballoon *b;
int ret = 0;
goto out_unlock;
}
- /*
- * The page is isolated, so it is safe to delete it without holding
- * @pages_lock . We keep holding @comm_lock since we will need it in a
- * second.
- */
- balloon_page_finalize(page);
- put_page(page);
-
/* Inflate */
vmballoon_add_page(b, 0, newpage);
status = vmballoon_lock_op(b, 1, VMW_BALLOON_4K_PAGE,
* change.
*/
atomic64_dec(&b->size);
- } else {
/*
- * Success. Take a reference for the page, and we will add it to
- * the list after acquiring the lock.
+ * Tell the core that we're deflating the old page and don't
+ * need the new page.
*/
- get_page(newpage);
- }
-
- /* Update the balloon list under the @pages_lock */
- spin_lock_irqsave(&b->b_dev_info.pages_lock, flags);
-
- /*
- * On inflation success, we already took a reference for the @newpage.
- * If we succeed just insert it to the list and update the statistics
- * under the lock.
- */
- if (status == VMW_BALLOON_SUCCESS) {
- balloon_page_insert(&b->b_dev_info, newpage);
- __count_vm_event(BALLOON_MIGRATE);
- } else {
- __count_vm_event(BALLOON_DEFLATE);
+ ret = -ENOENT;
}
-
- /*
- * We deflated successfully, so regardless to the inflation success, we
- * need to reduce the number of isolated_pages.
- */
- b->b_dev_info.isolated_pages--;
- spin_unlock_irqrestore(&b->b_dev_info.pages_lock, flags);
-
out_unlock:
up_read(&b->conf_sem);
return ret;
{
struct virtio_balloon *vb = container_of(vb_dev_info,
struct virtio_balloon, vb_dev_info);
- unsigned long flags;
/*
* In order to avoid lock contention while migrating pages concurrently
if (!mutex_trylock(&vb->balloon_lock))
return -EAGAIN;
- get_page(newpage); /* balloon reference */
-
/*
* When we migrate a page to a different zone and adjusted the
* managed page count when inflating, we have to fixup the count of
}
/* balloon's page migration 1st step -- inflate "newpage" */
- spin_lock_irqsave(&vb_dev_info->pages_lock, flags);
- balloon_page_insert(vb_dev_info, newpage);
- vb_dev_info->isolated_pages--;
- __count_vm_event(BALLOON_MIGRATE);
- spin_unlock_irqrestore(&vb_dev_info->pages_lock, flags);
vb->num_pfns = VIRTIO_BALLOON_PAGES_PER_PAGE;
set_page_pfns(vb, vb->pfns, newpage);
tell_host(vb, vb->inflate_vq);
tell_host(vb, vb->deflate_vq);
mutex_unlock(&vb->balloon_lock);
-
- balloon_page_finalize(page);
- put_page(page); /* balloon reference */
-
return 0;
}
#endif /* CONFIG_BALLOON_COMPACTION */
spin_unlock_irqrestore(&b_dev_info->pages_lock, flags);
}
-/* move_to_new_page() counterpart for a ballooned page */
static int balloon_page_migrate(struct page *newpage, struct page *page,
enum migrate_mode mode)
{
- struct balloon_dev_info *balloon = balloon_page_device(page);
+ struct balloon_dev_info *b_dev_info = balloon_page_device(page);
+ unsigned long flags;
+ int rc;
VM_BUG_ON_PAGE(!PageLocked(page), page);
VM_BUG_ON_PAGE(!PageLocked(newpage), newpage);
* device. As isolated balloon pages cannot get deflated, we still have
* a balloon device here.
*/
- if (WARN_ON_ONCE(!balloon))
+ if (WARN_ON_ONCE(!b_dev_info))
return -EAGAIN;
- return balloon->migratepage(balloon, newpage, page, mode);
+ rc = b_dev_info->migratepage(b_dev_info, newpage, page, mode);
+ if (rc < 0 && rc != -ENOENT)
+ return rc;
+
+ spin_lock_irqsave(&b_dev_info->pages_lock, flags);
+ if (!rc) {
+ /* Insert the new page into the balloon list. */
+ get_page(newpage);
+ balloon_page_insert(b_dev_info, newpage);
+ __count_vm_event(BALLOON_MIGRATE);
+ } else {
+ /* Old page was deflated but new page not inflated. */
+ __count_vm_event(BALLOON_DEFLATE);
+ }
+
+ b_dev_info->isolated_pages--;
+ spin_unlock_irqrestore(&b_dev_info->pages_lock, flags);
+
+ /* Free the now-deflated page we isolated in balloon_page_isolate(). */
+ balloon_page_finalize(page);
+ put_page(page);
+
+ return 0;
}
const struct movable_operations balloon_mops = {