|
@@ -564,7 +564,7 @@ static int fallback_migrate_page(struct address_space *mapping,
|
|
|
* == 0 - success
|
|
|
*/
|
|
|
static int move_to_new_page(struct page *newpage, struct page *page,
|
|
|
- int remap_swapcache)
|
|
|
+ int remap_swapcache, bool sync)
|
|
|
{
|
|
|
struct address_space *mapping;
|
|
|
int rc;
|
|
@@ -586,18 +586,28 @@ static int move_to_new_page(struct page *newpage, struct page *page,
|
|
|
mapping = page_mapping(page);
|
|
|
if (!mapping)
|
|
|
rc = migrate_page(mapping, newpage, page);
|
|
|
- else if (mapping->a_ops->migratepage)
|
|
|
+ else {
|
|
|
/*
|
|
|
- * Most pages have a mapping and most filesystems
|
|
|
- * should provide a migration function. Anonymous
|
|
|
- * pages are part of swap space which also has its
|
|
|
- * own migration function. This is the most common
|
|
|
- * path for page migration.
|
|
|
+ * Do not writeback pages if !sync and migratepage is
|
|
|
+ * not pointing to migrate_page() which is nonblocking
|
|
|
+ * (swapcache/tmpfs uses migratepage = migrate_page).
|
|
|
*/
|
|
|
- rc = mapping->a_ops->migratepage(mapping,
|
|
|
- newpage, page);
|
|
|
- else
|
|
|
- rc = fallback_migrate_page(mapping, newpage, page);
|
|
|
+ if (PageDirty(page) && !sync &&
|
|
|
+ mapping->a_ops->migratepage != migrate_page)
|
|
|
+ rc = -EBUSY;
|
|
|
+ else if (mapping->a_ops->migratepage)
|
|
|
+ /*
|
|
|
+ * Most pages have a mapping and most filesystems
|
|
|
+ * should provide a migration function. Anonymous
|
|
|
+ * pages are part of swap space which also has its
|
|
|
+ * own migration function. This is the most common
|
|
|
+ * path for page migration.
|
|
|
+ */
|
|
|
+ rc = mapping->a_ops->migratepage(mapping,
|
|
|
+ newpage, page);
|
|
|
+ else
|
|
|
+ rc = fallback_migrate_page(mapping, newpage, page);
|
|
|
+ }
|
|
|
|
|
|
if (rc) {
|
|
|
newpage->mapping = NULL;
|
|
@@ -641,7 +651,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
|
|
|
rc = -EAGAIN;
|
|
|
|
|
|
if (!trylock_page(page)) {
|
|
|
- if (!force)
|
|
|
+ if (!force || !sync)
|
|
|
goto move_newpage;
|
|
|
|
|
|
/*
|
|
@@ -686,7 +696,15 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
|
|
|
BUG_ON(charge);
|
|
|
|
|
|
if (PageWriteback(page)) {
|
|
|
- if (!force || !sync)
|
|
|
+ /*
|
|
|
+ * For !sync, there is no point retrying as the retry loop
|
|
|
+ * is expected to be too short for PageWriteback to be cleared
|
|
|
+ */
|
|
|
+ if (!sync) {
|
|
|
+ rc = -EBUSY;
|
|
|
+ goto uncharge;
|
|
|
+ }
|
|
|
+ if (!force)
|
|
|
goto uncharge;
|
|
|
wait_on_page_writeback(page);
|
|
|
}
|
|
@@ -757,7 +775,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
|
|
|
|
|
|
skip_unmap:
|
|
|
if (!page_mapped(page))
|
|
|
- rc = move_to_new_page(newpage, page, remap_swapcache);
|
|
|
+ rc = move_to_new_page(newpage, page, remap_swapcache, sync);
|
|
|
|
|
|
if (rc && remap_swapcache)
|
|
|
remove_migration_ptes(page, page);
|
|
@@ -850,7 +868,7 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
|
|
|
try_to_unmap(hpage, TTU_MIGRATION|TTU_IGNORE_MLOCK|TTU_IGNORE_ACCESS);
|
|
|
|
|
|
if (!page_mapped(hpage))
|
|
|
- rc = move_to_new_page(new_hpage, hpage, 1);
|
|
|
+ rc = move_to_new_page(new_hpage, hpage, 1, sync);
|
|
|
|
|
|
if (rc)
|
|
|
remove_migration_ptes(hpage, hpage);
|