Index: linux-2.6.17-rc3-mm1/mm/migrate.c =================================================================== --- linux-2.6.17-rc3-mm1.orig/mm/migrate.c 2006-05-10 17:53:28.527542098 -0700 +++ linux-2.6.17-rc3-mm1/mm/migrate.c 2006-05-10 18:45:15.271441876 -0700 @@ -575,8 +575,11 @@ static int move_to_new_page(struct page unlock_page(newpage); - if (!rc) - move_to_lru(newpage); + /* + * Move the page to the LRU. If migration was not successful + * the the refcount will cause the page to be freed. + */ + move_to_lru(newpage); return rc; } @@ -588,6 +591,10 @@ static int unmap_and_move(struct page *n { int rc; + if (page_count(page) == 1) + /* page was freed from under us. So we are done. */ + return 0; + if (!TestSetPageLocked(page)) { if (!force) return -EAGAIN; @@ -618,6 +625,14 @@ static int unmap_and_move(struct page *n remove_migration_ptes(page, page); out: unlock_page(page); + if (rc != EAGAIN) + /* + * A page that has been migrated has all references + * removed and will be freed. A page that has not been + * migrated will have kepts its references and be + * restored. + */ + move_to_lru(page); return rc; } @@ -635,8 +650,7 @@ out: * * Return: Number of pages not migrated when "to" ran empty. */ -int migrate_pages(struct list_head *from, struct list_head *to, - struct list_head *moved, struct list_head *failed) +int migrate_pages(struct list_head *from, struct list_head *to) { int retry; int nr_failed = 0; @@ -659,22 +673,15 @@ redo: cond_resched(); - if (page_count(page) == 1) - /* page was freed from under us. So we are done. */ - rc = 0; - else - rc = unmap_and_move(lru_to_page(to), page, pass > 2); + rc = unmap_and_move(lru_to_page(to), page, pass > 2); if (rc) { if (rc == -EAGAIN) retry++; - else { + else /* Permanent failure */ - list_move(&page->lru, failed); nr_failed++; - } - } else - list_move(&page->lru, moved); + } } if (retry && pass++ < 10) goto redo; @@ -695,11 +702,10 @@ int migrate_pages_to(struct list_head *p struct vm_area_struct *vma, int dest) { LIST_HEAD(newlist); - LIST_HEAD(moved); - LIST_HEAD(failed); int err = 0; unsigned long offset = 0; int nr_pages; + int nr_failed = 0; struct page *page; struct list_head *p; @@ -733,26 +739,17 @@ redo: if (nr_pages > MIGRATE_CHUNK_SIZE) break; } - err = migrate_pages(pagelist, &newlist, &moved, &failed); + err = migrate_pages(pagelist, &newlist); - putback_lru_pages(&moved); /* Call release pages instead ?? */ - - if (err >= 0 && list_empty(&newlist) && !list_empty(pagelist)) - goto redo; -out: - /* Return leftover allocated pages */ - while (!list_empty(&newlist)) { - page = list_entry(newlist.next, struct page, lru); - list_del(&page->lru); - __free_page(page); + if (err >= 0) { + nr_failed += err; + if (list_empty(&newlist) && !list_empty(pagelist)) + goto redo; } - list_splice(&failed, pagelist); - if (err < 0) - return err; +out: /* Calculate number of leftover pages */ - nr_pages = 0; list_for_each(p, pagelist) - nr_pages++; - return nr_pages; + nr_failed++; + return nr_failed; } Index: linux-2.6.17-rc3-mm1/include/linux/migrate.h =================================================================== --- linux-2.6.17-rc3-mm1.orig/include/linux/migrate.h 2006-05-08 00:48:19.020830239 -0700 +++ linux-2.6.17-rc3-mm1/include/linux/migrate.h 2006-05-10 18:11:39.714737247 -0700 @@ -9,8 +9,7 @@ extern int isolate_lru_page(struct page extern int putback_lru_pages(struct list_head *l); extern int migrate_page(struct address_space *, struct page *, struct page *); -extern int migrate_pages(struct list_head *l, struct list_head *t, - struct list_head *moved, struct list_head *failed); +extern int migrate_pages(struct list_head *l, struct list_head *t); extern int migrate_pages_to(struct list_head *pagelist, struct vm_area_struct *vma, int dest); extern int fail_migrate_page(struct address_space *, @@ -23,8 +22,8 @@ extern int migrate_prep(void); static inline int isolate_lru_page(struct page *p, struct list_head *list) { return -ENOSYS; } static inline int putback_lru_pages(struct list_head *l) { return 0; } -static inline int migrate_pages(struct list_head *l, struct list_head *t, - struct list_head *moved, struct list_head *failed) { return -ENOSYS; } +static inline int migrate_pages(struct list_head *l, struct list_head *t) + { return -ENOSYS; } static inline int migrate_pages_to(struct list_head *pagelist, struct vm_area_struct *vma, int dest) { return 0; }