Commit e965f963 authored by Christoph Lameter's avatar Christoph Lameter Committed by Linus Torvalds

[PATCH] Direct Migration V9: Avoid writeback / page_migrate() method

Migrate a page with buffers without requiring writeback

This introduces a new address space operation migratepage() that may be used
by a filesystem to implement its own version of page migration.

A version is provided that migrates buffers attached to pages.  Some
filesystems (ext2, ext3, xfs) are modified to utilize this feature.

The swapper address space operation are modified so that a regular
migrate_page() will occur for anonymous pages without writeback (migrate_pages
forces every anonymous page to have a swap entry).
Signed-off-by: default avatarMike Kravetz <kravetz@us.ibm.com>
Signed-off-by: default avatarChristoph Lameter <clameter@sgi.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 7e2ab150
...@@ -3049,6 +3049,66 @@ asmlinkage long sys_bdflush(int func, long data) ...@@ -3049,6 +3049,66 @@ asmlinkage long sys_bdflush(int func, long data)
return 0; return 0;
} }
/*
* Migration function for pages with buffers. This function can only be used
* if the underlying filesystem guarantees that no other references to "page"
* exist.
*/
#ifdef CONFIG_MIGRATION
int buffer_migrate_page(struct page *newpage, struct page *page)
{
struct address_space *mapping = page->mapping;
struct buffer_head *bh, *head;
if (!mapping)
return -EAGAIN;
if (!page_has_buffers(page))
return migrate_page(newpage, page);
head = page_buffers(page);
if (migrate_page_remove_references(newpage, page, 3))
return -EAGAIN;
bh = head;
do {
get_bh(bh);
lock_buffer(bh);
bh = bh->b_this_page;
} while (bh != head);
ClearPagePrivate(page);
set_page_private(newpage, page_private(page));
set_page_private(page, 0);
put_page(page);
get_page(newpage);
bh = head;
do {
set_bh_page(bh, newpage, bh_offset(bh));
bh = bh->b_this_page;
} while (bh != head);
SetPagePrivate(newpage);
migrate_page_copy(newpage, page);
bh = head;
do {
unlock_buffer(bh);
put_bh(bh);
bh = bh->b_this_page;
} while (bh != head);
return 0;
}
EXPORT_SYMBOL(buffer_migrate_page);
#endif
/* /*
* Buffer-head allocation * Buffer-head allocation
*/ */
......
...@@ -706,6 +706,7 @@ struct address_space_operations ext2_aops = { ...@@ -706,6 +706,7 @@ struct address_space_operations ext2_aops = {
.bmap = ext2_bmap, .bmap = ext2_bmap,
.direct_IO = ext2_direct_IO, .direct_IO = ext2_direct_IO,
.writepages = ext2_writepages, .writepages = ext2_writepages,
.migratepage = buffer_migrate_page,
}; };
struct address_space_operations ext2_aops_xip = { struct address_space_operations ext2_aops_xip = {
...@@ -723,6 +724,7 @@ struct address_space_operations ext2_nobh_aops = { ...@@ -723,6 +724,7 @@ struct address_space_operations ext2_nobh_aops = {
.bmap = ext2_bmap, .bmap = ext2_bmap,
.direct_IO = ext2_direct_IO, .direct_IO = ext2_direct_IO,
.writepages = ext2_writepages, .writepages = ext2_writepages,
.migratepage = buffer_migrate_page,
}; };
/* /*
......
...@@ -1559,6 +1559,7 @@ static struct address_space_operations ext3_ordered_aops = { ...@@ -1559,6 +1559,7 @@ static struct address_space_operations ext3_ordered_aops = {
.invalidatepage = ext3_invalidatepage, .invalidatepage = ext3_invalidatepage,
.releasepage = ext3_releasepage, .releasepage = ext3_releasepage,
.direct_IO = ext3_direct_IO, .direct_IO = ext3_direct_IO,
.migratepage = buffer_migrate_page,
}; };
static struct address_space_operations ext3_writeback_aops = { static struct address_space_operations ext3_writeback_aops = {
...@@ -1572,6 +1573,7 @@ static struct address_space_operations ext3_writeback_aops = { ...@@ -1572,6 +1573,7 @@ static struct address_space_operations ext3_writeback_aops = {
.invalidatepage = ext3_invalidatepage, .invalidatepage = ext3_invalidatepage,
.releasepage = ext3_releasepage, .releasepage = ext3_releasepage,
.direct_IO = ext3_direct_IO, .direct_IO = ext3_direct_IO,
.migratepage = buffer_migrate_page,
}; };
static struct address_space_operations ext3_journalled_aops = { static struct address_space_operations ext3_journalled_aops = {
......
...@@ -1462,4 +1462,5 @@ struct address_space_operations linvfs_aops = { ...@@ -1462,4 +1462,5 @@ struct address_space_operations linvfs_aops = {
.commit_write = generic_commit_write, .commit_write = generic_commit_write,
.bmap = linvfs_bmap, .bmap = linvfs_bmap,
.direct_IO = linvfs_direct_IO, .direct_IO = linvfs_direct_IO,
.migratepage = buffer_migrate_page,
}; };
...@@ -1521,6 +1521,7 @@ xfs_mapping_buftarg( ...@@ -1521,6 +1521,7 @@ xfs_mapping_buftarg(
struct address_space *mapping; struct address_space *mapping;
static struct address_space_operations mapping_aops = { static struct address_space_operations mapping_aops = {
.sync_page = block_sync_page, .sync_page = block_sync_page,
.migratepage = fail_migrate_page,
}; };
inode = new_inode(bdev->bd_inode->i_sb); inode = new_inode(bdev->bd_inode->i_sb);
......
...@@ -363,6 +363,8 @@ struct address_space_operations { ...@@ -363,6 +363,8 @@ struct address_space_operations {
loff_t offset, unsigned long nr_segs); loff_t offset, unsigned long nr_segs);
struct page* (*get_xip_page)(struct address_space *, sector_t, struct page* (*get_xip_page)(struct address_space *, sector_t,
int); int);
/* migrate the contents of a page to the specified target */
int (*migratepage) (struct page *, struct page *);
}; };
struct backing_dev_info; struct backing_dev_info;
...@@ -1719,6 +1721,12 @@ extern void simple_release_fs(struct vfsmount **mount, int *count); ...@@ -1719,6 +1721,12 @@ extern void simple_release_fs(struct vfsmount **mount, int *count);
extern ssize_t simple_read_from_buffer(void __user *, size_t, loff_t *, const void *, size_t); extern ssize_t simple_read_from_buffer(void __user *, size_t, loff_t *, const void *, size_t);
#ifdef CONFIG_MIGRATION
extern int buffer_migrate_page(struct page *, struct page *);
#else
#define buffer_migrate_page NULL
#endif
extern int inode_change_ok(struct inode *, struct iattr *); extern int inode_change_ok(struct inode *, struct iattr *);
extern int __must_check inode_setattr(struct inode *, struct iattr *); extern int __must_check inode_setattr(struct inode *, struct iattr *);
......
...@@ -193,13 +193,18 @@ extern int isolate_lru_page(struct page *p); ...@@ -193,13 +193,18 @@ extern int isolate_lru_page(struct page *p);
extern int putback_lru_pages(struct list_head *l); extern int putback_lru_pages(struct list_head *l);
extern int migrate_page(struct page *, struct page *); extern int migrate_page(struct page *, struct page *);
extern void migrate_page_copy(struct page *, struct page *); extern void migrate_page_copy(struct page *, struct page *);
extern int migrate_page_remove_references(struct page *, struct page *, int);
extern int migrate_pages(struct list_head *l, struct list_head *t, extern int migrate_pages(struct list_head *l, struct list_head *t,
struct list_head *moved, struct list_head *failed); struct list_head *moved, struct list_head *failed);
extern int fail_migrate_page(struct page *, struct page *);
#else #else
static inline int isolate_lru_page(struct page *p) { return -ENOSYS; } static inline int isolate_lru_page(struct page *p) { return -ENOSYS; }
static inline int putback_lru_pages(struct list_head *l) { return 0; } 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, static inline int migrate_pages(struct list_head *l, struct list_head *t,
struct list_head *moved, struct list_head *failed) { return -ENOSYS; } struct list_head *moved, struct list_head *failed) { return -ENOSYS; }
/* Possible settings for the migrate_page() method in address_operations */
#define migrate_page NULL
#define fail_migrate_page NULL
#endif #endif
#ifdef CONFIG_MMU #ifdef CONFIG_MMU
......
...@@ -233,6 +233,7 @@ void remove_from_swap(struct page *page) ...@@ -233,6 +233,7 @@ void remove_from_swap(struct page *page)
delete_from_swap_cache(page); delete_from_swap_cache(page);
} }
EXPORT_SYMBOL(remove_from_swap);
#endif #endif
/* /*
......
...@@ -27,6 +27,7 @@ static struct address_space_operations swap_aops = { ...@@ -27,6 +27,7 @@ static struct address_space_operations swap_aops = {
.writepage = swap_writepage, .writepage = swap_writepage,
.sync_page = block_sync_page, .sync_page = block_sync_page,
.set_page_dirty = __set_page_dirty_nobuffers, .set_page_dirty = __set_page_dirty_nobuffers,
.migratepage = migrate_page,
}; };
static struct backing_dev_info swap_backing_dev_info = { static struct backing_dev_info swap_backing_dev_info = {
......
...@@ -614,6 +614,15 @@ int putback_lru_pages(struct list_head *l) ...@@ -614,6 +614,15 @@ int putback_lru_pages(struct list_head *l)
return count; return count;
} }
/*
* Non migratable page
*/
int fail_migrate_page(struct page *newpage, struct page *page)
{
return -EIO;
}
EXPORT_SYMBOL(fail_migrate_page);
/* /*
* swapout a single page * swapout a single page
* page is locked upon entry, unlocked on exit * page is locked upon entry, unlocked on exit
...@@ -659,6 +668,7 @@ unlock_retry: ...@@ -659,6 +668,7 @@ unlock_retry:
retry: retry:
return -EAGAIN; return -EAGAIN;
} }
EXPORT_SYMBOL(swap_page);
/* /*
* Page migration was first developed in the context of the memory hotplug * Page migration was first developed in the context of the memory hotplug
...@@ -674,7 +684,7 @@ retry: ...@@ -674,7 +684,7 @@ retry:
* Remove references for a page and establish the new page with the correct * Remove references for a page and establish the new page with the correct
* basic settings to be able to stop accesses to the page. * basic settings to be able to stop accesses to the page.
*/ */
static int migrate_page_remove_references(struct page *newpage, int migrate_page_remove_references(struct page *newpage,
struct page *page, int nr_refs) struct page *page, int nr_refs)
{ {
struct address_space *mapping = page_mapping(page); struct address_space *mapping = page_mapping(page);
...@@ -749,6 +759,7 @@ static int migrate_page_remove_references(struct page *newpage, ...@@ -749,6 +759,7 @@ static int migrate_page_remove_references(struct page *newpage,
return 0; return 0;
} }
EXPORT_SYMBOL(migrate_page_remove_references);
/* /*
* Copy the page to its new location * Copy the page to its new location
...@@ -788,6 +799,7 @@ void migrate_page_copy(struct page *newpage, struct page *page) ...@@ -788,6 +799,7 @@ void migrate_page_copy(struct page *newpage, struct page *page)
if (PageWriteback(newpage)) if (PageWriteback(newpage))
end_page_writeback(newpage); end_page_writeback(newpage);
} }
EXPORT_SYMBOL(migrate_page_copy);
/* /*
* Common logic to directly migrate a single page suitable for * Common logic to directly migrate a single page suitable for
...@@ -815,6 +827,7 @@ int migrate_page(struct page *newpage, struct page *page) ...@@ -815,6 +827,7 @@ int migrate_page(struct page *newpage, struct page *page)
remove_from_swap(newpage); remove_from_swap(newpage);
return 0; return 0;
} }
EXPORT_SYMBOL(migrate_page);
/* /*
* migrate_pages * migrate_pages
...@@ -914,6 +927,11 @@ redo: ...@@ -914,6 +927,11 @@ redo:
if (!mapping) if (!mapping)
goto unlock_both; goto unlock_both;
if (mapping->a_ops->migratepage) {
rc = mapping->a_ops->migratepage(newpage, page);
goto unlock_both;
}
/* /*
* Trigger writeout if page is dirty * Trigger writeout if page is dirty
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment