When page-migration code fails, it must be rolled back. An old page have to be put back to the radix-tree and wake up all waiters on a new page. Signed-off-by: Hirokazu Takahashi Signed-off-by: Dave Hansen --- memhotplug-dave/mm/mmigrate.c | 65 ++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 62 insertions(+), 3 deletions(-) diff -puN mm/mmigrate.c~AA-PM-10.0-migrate-rollback mm/mmigrate.c --- memhotplug/mm/mmigrate.c~AA-PM-10.0-migrate-rollback 2005-07-28 13:50:38.000000000 -0700 +++ memhotplug-dave/mm/mmigrate.c 2005-07-28 13:50:38.000000000 -0700 @@ -165,9 +165,68 @@ migrate_page_common(struct page *page, s static int rewind_page(struct page *page, struct page *newpage) { - printk("Roll back migration is not implemented yet.\n"); - BUG(); - return 1; + struct address_space *mapping = page_mapping(newpage); + int truncated = !PageSwapCache(newpage) && page_mapping(page) == NULL; + long retry = 1000; + + BUG_ON(mapping == NULL); + + /* + * Rewinding is not needed if the newpage has been already truncated. + */ + if (truncated) + goto out; + + /* + * Try to unwind by notifying waiters. If someone misbehaves, + * we die. + */ + read_lock_irq(&mapping->tree_lock); + page->index = newpage->index; + page->mapping = newpage->mapping; + if (PageSwapCache(newpage)) { + SetPageSwapCache(page); + page->private = newpage->private; + } + if (radix_tree_replace(&mapping->page_tree, page_index(newpage), page) == NULL) { + printk(KERN_ERR "%s(): newpage:%p has gone. We can't roll back page:%p.\n", __FUNCTION__, newpage, page); + BUG(); + } + /* no page_cache_get(page); needed */ + read_unlock_irq(&mapping->tree_lock); +out: + if (PageWriteback(newpage)) + end_page_writeback(newpage); /* XXX */ + newpage->mapping = NULL; + newpage->private = 0; + ClearPageSwapCache(newpage); + /* XXX unmap needed? No, it shouldn't. Handled by fault handlers. */ + unlock_page(newpage); + + /* + * Some requests may be blocked on the newpage. Wait until the + * requests have gone. + */ + while (page_count(newpage) > 2) { + msleep(10); + if (retry-- <= 0) { + retry = 1000; + printk(KERN_ERR "%s(): page:%p can't be rolled back, as there remain some references to newpage:%p yet.\n", __FUNCTION__, page, newpage); + printk(KERN_ERR "newpage %p flags %lx %d %d, page %p flags %lx %d\n", + newpage, newpage->flags, page_count(newpage), + page_mapcount(newpage), + page, page->flags, page_count(page)); + } + } + + BUG_ON(PageUptodate(newpage)); + BUG_ON(PageDirty(newpage)); + BUG_ON(PageActive(newpage)); + BUG_ON(PagePrivate(newpage)); + BUG_ON(page_count(newpage) != 2); + unlock_page(page); + page_cache_release(newpage); + return 0; } /* _