page migration: Simplify migrate_pages() Currently migrate_pages() is mess with lots of goto. Its not that readable due to recent changes. Extract two functions from migrate_pages() and get rid of all the gotos. This also changes the way locks are obtained to be a bit softer on the VM. The change allows the logic from migrate_pages() to be moved into unmap_and_move(). migrate_pages() will only wait on certain page states to change but leave the hard wait for locks to unmap_and_move(). Signed-off-by: Christoph Lameter Index: linux-2.6.17-rc3-mm1/mm/migrate.c =================================================================== --- linux-2.6.17-rc3-mm1.orig/mm/migrate.c 2006-05-10 17:47:07.492601155 -0700 +++ linux-2.6.17-rc3-mm1/mm/migrate.c 2006-05-10 17:53:28.527542098 -0700 @@ -534,11 +534,99 @@ static int fallback_migrate_page(struct } /* + * Move a page to a newly allocated page + * The page is locked and all ptes have been successfully removed. + * + * The new page will have replaced the old page if this function + * is successful. + */ +static int move_to_new_page(struct page *newpage, struct page *page) +{ + struct address_space *mapping; + int rc; + + lock_page(newpage); + + /* Prepare mapping for the new page.*/ + newpage->index = page->index; + newpage->mapping = page->mapping; + + mapping = page_mapping(page); + if (!mapping) + rc = migrate_page(mapping, newpage, page); + + 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) + remove_migration_ptes(page, newpage); + else + newpage->mapping = NULL; + + unlock_page(newpage); + + if (!rc) + move_to_lru(newpage); + return rc; +} + +/* + * Obtain the lock on page, remove all ptes and migrate the page + * to the newly allocated page in newpage. + */ +static int unmap_and_move(struct page *newpage, struct page *page, int force) +{ + int rc; + + if (!TestSetPageLocked(page)) { + if (!force) + return -EAGAIN; + lock_page(page); + } + + if (PageWriteback(page)) { + if (!force) { + rc = -EAGAIN; + goto out; + } + wait_on_page_writeback(page); + } + + /* + * Establish migration ptes or remove ptes + */ + if (try_to_unmap(page, 1) != SWAP_FAIL) { + if (!page_mapped(page)) + rc = move_to_new_page(newpage, page); + else + rc = -EAGAIN; + } else + /* A vma has VM_LOCKED set -> permanent failure */ + rc = -EPERM; + + if (rc) + remove_migration_ptes(page, page); +out: + unlock_page(page); + return rc; +} + +/* * migrate_pages * * Two lists are passed to this function. The first list * contains the pages isolated from the LRU to be migrated. - * The second list contains new pages that the pages isolated + * The second list contains new pages that the isolated pages * can be moved to. * * The function returns after 10 attempts or if no pages @@ -565,97 +653,19 @@ redo: retry = 0; list_for_each_entry_safe(page, page2, from, lru) { - struct page *newpage = NULL; - struct address_space *mapping; + + if (list_empty(to)) + break; cond_resched(); - rc = 0; if (page_count(page) == 1) /* page was freed from under us. So we are done. */ - goto next; - - if (to && list_empty(to)) - break; - - /* - * Skip locked pages during the first two passes to give the - * functions holding the lock time to release the page. Later we - * use lock_page() to have a higher chance of acquiring the - * lock. - */ - rc = -EAGAIN; - if (pass > 2) - lock_page(page); + rc = 0; else - if (TestSetPageLocked(page)) - goto next; - - /* - * Only wait on writeback if we have already done a pass where - * we we may have triggered writeouts for lots of pages. - */ - if (pass > 0) - wait_on_page_writeback(page); - else - if (PageWriteback(page)) - goto unlock_page; - - /* - * Establish migration ptes or remove ptes - */ - rc = -EPERM; - if (try_to_unmap(page, 1) == SWAP_FAIL) - /* A vma has VM_LOCKED set -> permanent failure */ - goto unlock_page; - - rc = -EAGAIN; - if (page_mapped(page)) - goto unlock_page; - - newpage = lru_to_page(to); - lock_page(newpage); - /* Prepare mapping for the new page.*/ - newpage->index = page->index; - newpage->mapping = page->mapping; + rc = unmap_and_move(lru_to_page(to), page, pass > 2); - /* - * Pages are properly locked and writeback is complete. - * Try to migrate the page. - */ - mapping = page_mapping(page); - if (!mapping) - rc = migrate_page(mapping, newpage, page); - - 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) - remove_migration_ptes(page, newpage); - - unlock_page(newpage); - -unlock_page: - if (rc) - remove_migration_ptes(page, page); - - unlock_page(page); - -next: if (rc) { - if (newpage) - newpage->mapping = NULL; - if (rc == -EAGAIN) retry++; else { @@ -663,13 +673,8 @@ next: list_move(&page->lru, failed); nr_failed++; } - } else { - if (newpage) { - /* Successful migration. Return page to LRU */ - move_to_lru(newpage); - } + } else list_move(&page->lru, moved); - } } if (retry && pass++ < 10) goto redo;