

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 <taka@valinux.co.jp>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
---

 memhotplug-dave/mm/mmigrate.c |   65 ++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 62 insertions(+), 3 deletions(-)

diff -puN mm/mmigrate.c~P10-migrate-rollback mm/mmigrate.c
--- memhotplug/mm/mmigrate.c~P10-migrate-rollback	2004-12-10 13:53:07.000000000 -0800
+++ memhotplug-dave/mm/mmigrate.c	2004-12-10 13:53:07.000000000 -0800
@@ -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.
+	 */
+	write_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 */
+	write_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;
 }
 
 /*
_
