diff options
author | Christoph Lameter <clameter@sgi.com> | 2006-02-01 06:05:41 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-02-01 11:53:17 -0500 |
commit | e965f9630c651fa4249039fd4b80c9392d07a856 (patch) | |
tree | 1353dd536d0ee549c30e462086624c21788ee9d2 | |
parent | 7e2ab150d1b3b286a4c864c60a549b2601777b63 (diff) |
[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: Mike Kravetz <kravetz@us.ibm.com>
Signed-off-by: Christoph Lameter <clameter@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | fs/buffer.c | 60 | ||||
-rw-r--r-- | fs/ext2/inode.c | 2 | ||||
-rw-r--r-- | fs/ext3/inode.c | 2 | ||||
-rw-r--r-- | fs/xfs/linux-2.6/xfs_aops.c | 1 | ||||
-rw-r--r-- | fs/xfs/linux-2.6/xfs_buf.c | 1 | ||||
-rw-r--r-- | include/linux/fs.h | 8 | ||||
-rw-r--r-- | include/linux/swap.h | 5 | ||||
-rw-r--r-- | mm/rmap.c | 1 | ||||
-rw-r--r-- | mm/swap_state.c | 1 | ||||
-rw-r--r-- | mm/vmscan.c | 20 |
10 files changed, 100 insertions, 1 deletions
diff --git a/fs/buffer.c b/fs/buffer.c index 3dc712f29d2d..8bcbac87a28c 100644 --- a/fs/buffer.c +++ b/fs/buffer.c | |||
@@ -3050,6 +3050,66 @@ asmlinkage long sys_bdflush(int func, long data) | |||
3050 | } | 3050 | } |
3051 | 3051 | ||
3052 | /* | 3052 | /* |
3053 | * Migration function for pages with buffers. This function can only be used | ||
3054 | * if the underlying filesystem guarantees that no other references to "page" | ||
3055 | * exist. | ||
3056 | */ | ||
3057 | #ifdef CONFIG_MIGRATION | ||
3058 | int buffer_migrate_page(struct page *newpage, struct page *page) | ||
3059 | { | ||
3060 | struct address_space *mapping = page->mapping; | ||
3061 | struct buffer_head *bh, *head; | ||
3062 | |||
3063 | if (!mapping) | ||
3064 | return -EAGAIN; | ||
3065 | |||
3066 | if (!page_has_buffers(page)) | ||
3067 | return migrate_page(newpage, page); | ||
3068 | |||
3069 | head = page_buffers(page); | ||
3070 | |||
3071 | if (migrate_page_remove_references(newpage, page, 3)) | ||
3072 | return -EAGAIN; | ||
3073 | |||
3074 | bh = head; | ||
3075 | do { | ||
3076 | get_bh(bh); | ||
3077 | lock_buffer(bh); | ||
3078 | bh = bh->b_this_page; | ||
3079 | |||
3080 | } while (bh != head); | ||
3081 | |||
3082 | ClearPagePrivate(page); | ||
3083 | set_page_private(newpage, page_private(page)); | ||
3084 | set_page_private(page, 0); | ||
3085 | put_page(page); | ||
3086 | get_page(newpage); | ||
3087 | |||
3088 | bh = head; | ||
3089 | do { | ||
3090 | set_bh_page(bh, newpage, bh_offset(bh)); | ||
3091 | bh = bh->b_this_page; | ||
3092 | |||
3093 | } while (bh != head); | ||
3094 | |||
3095 | SetPagePrivate(newpage); | ||
3096 | |||
3097 | migrate_page_copy(newpage, page); | ||
3098 | |||
3099 | bh = head; | ||
3100 | do { | ||
3101 | unlock_buffer(bh); | ||
3102 | put_bh(bh); | ||
3103 | bh = bh->b_this_page; | ||
3104 | |||
3105 | } while (bh != head); | ||
3106 | |||
3107 | return 0; | ||
3108 | } | ||
3109 | EXPORT_SYMBOL(buffer_migrate_page); | ||
3110 | #endif | ||
3111 | |||
3112 | /* | ||
3053 | * Buffer-head allocation | 3113 | * Buffer-head allocation |
3054 | */ | 3114 | */ |
3055 | static kmem_cache_t *bh_cachep; | 3115 | static kmem_cache_t *bh_cachep; |
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index e7d3f0522d01..a717837f272e 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c | |||
@@ -706,6 +706,7 @@ struct address_space_operations ext2_aops = { | |||
706 | .bmap = ext2_bmap, | 706 | .bmap = ext2_bmap, |
707 | .direct_IO = ext2_direct_IO, | 707 | .direct_IO = ext2_direct_IO, |
708 | .writepages = ext2_writepages, | 708 | .writepages = ext2_writepages, |
709 | .migratepage = buffer_migrate_page, | ||
709 | }; | 710 | }; |
710 | 711 | ||
711 | struct address_space_operations ext2_aops_xip = { | 712 | struct address_space_operations ext2_aops_xip = { |
@@ -723,6 +724,7 @@ struct address_space_operations ext2_nobh_aops = { | |||
723 | .bmap = ext2_bmap, | 724 | .bmap = ext2_bmap, |
724 | .direct_IO = ext2_direct_IO, | 725 | .direct_IO = ext2_direct_IO, |
725 | .writepages = ext2_writepages, | 726 | .writepages = ext2_writepages, |
727 | .migratepage = buffer_migrate_page, | ||
726 | }; | 728 | }; |
727 | 729 | ||
728 | /* | 730 | /* |
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 8824e84f8a56..3fc4238e9703 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c | |||
@@ -1559,6 +1559,7 @@ static struct address_space_operations ext3_ordered_aops = { | |||
1559 | .invalidatepage = ext3_invalidatepage, | 1559 | .invalidatepage = ext3_invalidatepage, |
1560 | .releasepage = ext3_releasepage, | 1560 | .releasepage = ext3_releasepage, |
1561 | .direct_IO = ext3_direct_IO, | 1561 | .direct_IO = ext3_direct_IO, |
1562 | .migratepage = buffer_migrate_page, | ||
1562 | }; | 1563 | }; |
1563 | 1564 | ||
1564 | static struct address_space_operations ext3_writeback_aops = { | 1565 | static struct address_space_operations ext3_writeback_aops = { |
@@ -1572,6 +1573,7 @@ static struct address_space_operations ext3_writeback_aops = { | |||
1572 | .invalidatepage = ext3_invalidatepage, | 1573 | .invalidatepage = ext3_invalidatepage, |
1573 | .releasepage = ext3_releasepage, | 1574 | .releasepage = ext3_releasepage, |
1574 | .direct_IO = ext3_direct_IO, | 1575 | .direct_IO = ext3_direct_IO, |
1576 | .migratepage = buffer_migrate_page, | ||
1575 | }; | 1577 | }; |
1576 | 1578 | ||
1577 | static struct address_space_operations ext3_journalled_aops = { | 1579 | static struct address_space_operations ext3_journalled_aops = { |
diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 120626789406..9892268e3005 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c | |||
@@ -1462,4 +1462,5 @@ struct address_space_operations linvfs_aops = { | |||
1462 | .commit_write = generic_commit_write, | 1462 | .commit_write = generic_commit_write, |
1463 | .bmap = linvfs_bmap, | 1463 | .bmap = linvfs_bmap, |
1464 | .direct_IO = linvfs_direct_IO, | 1464 | .direct_IO = linvfs_direct_IO, |
1465 | .migratepage = buffer_migrate_page, | ||
1465 | }; | 1466 | }; |
diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index a36a8e3b703f..bfb4f2917bb6 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c | |||
@@ -1521,6 +1521,7 @@ xfs_mapping_buftarg( | |||
1521 | struct address_space *mapping; | 1521 | struct address_space *mapping; |
1522 | static struct address_space_operations mapping_aops = { | 1522 | static struct address_space_operations mapping_aops = { |
1523 | .sync_page = block_sync_page, | 1523 | .sync_page = block_sync_page, |
1524 | .migratepage = fail_migrate_page, | ||
1524 | }; | 1525 | }; |
1525 | 1526 | ||
1526 | inode = new_inode(bdev->bd_inode->i_sb); | 1527 | inode = new_inode(bdev->bd_inode->i_sb); |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 84bb449b9b01..e059da947007 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -363,6 +363,8 @@ struct address_space_operations { | |||
363 | loff_t offset, unsigned long nr_segs); | 363 | loff_t offset, unsigned long nr_segs); |
364 | struct page* (*get_xip_page)(struct address_space *, sector_t, | 364 | struct page* (*get_xip_page)(struct address_space *, sector_t, |
365 | int); | 365 | int); |
366 | /* migrate the contents of a page to the specified target */ | ||
367 | int (*migratepage) (struct page *, struct page *); | ||
366 | }; | 368 | }; |
367 | 369 | ||
368 | struct backing_dev_info; | 370 | struct backing_dev_info; |
@@ -1719,6 +1721,12 @@ extern void simple_release_fs(struct vfsmount **mount, int *count); | |||
1719 | 1721 | ||
1720 | extern ssize_t simple_read_from_buffer(void __user *, size_t, loff_t *, const void *, size_t); | 1722 | extern ssize_t simple_read_from_buffer(void __user *, size_t, loff_t *, const void *, size_t); |
1721 | 1723 | ||
1724 | #ifdef CONFIG_MIGRATION | ||
1725 | extern int buffer_migrate_page(struct page *, struct page *); | ||
1726 | #else | ||
1727 | #define buffer_migrate_page NULL | ||
1728 | #endif | ||
1729 | |||
1722 | extern int inode_change_ok(struct inode *, struct iattr *); | 1730 | extern int inode_change_ok(struct inode *, struct iattr *); |
1723 | extern int __must_check inode_setattr(struct inode *, struct iattr *); | 1731 | extern int __must_check inode_setattr(struct inode *, struct iattr *); |
1724 | 1732 | ||
diff --git a/include/linux/swap.h b/include/linux/swap.h index 229b6d04b4b6..f3e17d5963c3 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h | |||
@@ -193,13 +193,18 @@ extern int isolate_lru_page(struct page *p); | |||
193 | extern int putback_lru_pages(struct list_head *l); | 193 | extern int putback_lru_pages(struct list_head *l); |
194 | extern int migrate_page(struct page *, struct page *); | 194 | extern int migrate_page(struct page *, struct page *); |
195 | extern void migrate_page_copy(struct page *, struct page *); | 195 | extern void migrate_page_copy(struct page *, struct page *); |
196 | extern int migrate_page_remove_references(struct page *, struct page *, int); | ||
196 | extern int migrate_pages(struct list_head *l, struct list_head *t, | 197 | extern int migrate_pages(struct list_head *l, struct list_head *t, |
197 | struct list_head *moved, struct list_head *failed); | 198 | struct list_head *moved, struct list_head *failed); |
199 | extern int fail_migrate_page(struct page *, struct page *); | ||
198 | #else | 200 | #else |
199 | static inline int isolate_lru_page(struct page *p) { return -ENOSYS; } | 201 | static inline int isolate_lru_page(struct page *p) { return -ENOSYS; } |
200 | static inline int putback_lru_pages(struct list_head *l) { return 0; } | 202 | static inline int putback_lru_pages(struct list_head *l) { return 0; } |
201 | static inline int migrate_pages(struct list_head *l, struct list_head *t, | 203 | static inline int migrate_pages(struct list_head *l, struct list_head *t, |
202 | struct list_head *moved, struct list_head *failed) { return -ENOSYS; } | 204 | struct list_head *moved, struct list_head *failed) { return -ENOSYS; } |
205 | /* Possible settings for the migrate_page() method in address_operations */ | ||
206 | #define migrate_page NULL | ||
207 | #define fail_migrate_page NULL | ||
203 | #endif | 208 | #endif |
204 | 209 | ||
205 | #ifdef CONFIG_MMU | 210 | #ifdef CONFIG_MMU |
@@ -233,6 +233,7 @@ void remove_from_swap(struct page *page) | |||
233 | 233 | ||
234 | delete_from_swap_cache(page); | 234 | delete_from_swap_cache(page); |
235 | } | 235 | } |
236 | EXPORT_SYMBOL(remove_from_swap); | ||
236 | #endif | 237 | #endif |
237 | 238 | ||
238 | /* | 239 | /* |
diff --git a/mm/swap_state.c b/mm/swap_state.c index 7b09ac503fec..db8a3d3e1636 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c | |||
@@ -27,6 +27,7 @@ static struct address_space_operations swap_aops = { | |||
27 | .writepage = swap_writepage, | 27 | .writepage = swap_writepage, |
28 | .sync_page = block_sync_page, | 28 | .sync_page = block_sync_page, |
29 | .set_page_dirty = __set_page_dirty_nobuffers, | 29 | .set_page_dirty = __set_page_dirty_nobuffers, |
30 | .migratepage = migrate_page, | ||
30 | }; | 31 | }; |
31 | 32 | ||
32 | static struct backing_dev_info swap_backing_dev_info = { | 33 | static struct backing_dev_info swap_backing_dev_info = { |
diff --git a/mm/vmscan.c b/mm/vmscan.c index 5e98b86feb74..5a610804cd06 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c | |||
@@ -615,6 +615,15 @@ int putback_lru_pages(struct list_head *l) | |||
615 | } | 615 | } |
616 | 616 | ||
617 | /* | 617 | /* |
618 | * Non migratable page | ||
619 | */ | ||
620 | int fail_migrate_page(struct page *newpage, struct page *page) | ||
621 | { | ||
622 | return -EIO; | ||
623 | } | ||
624 | EXPORT_SYMBOL(fail_migrate_page); | ||
625 | |||
626 | /* | ||
618 | * swapout a single page | 627 | * swapout a single page |
619 | * page is locked upon entry, unlocked on exit | 628 | * page is locked upon entry, unlocked on exit |
620 | */ | 629 | */ |
@@ -659,6 +668,7 @@ unlock_retry: | |||
659 | retry: | 668 | retry: |
660 | return -EAGAIN; | 669 | return -EAGAIN; |
661 | } | 670 | } |
671 | EXPORT_SYMBOL(swap_page); | ||
662 | 672 | ||
663 | /* | 673 | /* |
664 | * Page migration was first developed in the context of the memory hotplug | 674 | * Page migration was first developed in the context of the memory hotplug |
@@ -674,7 +684,7 @@ retry: | |||
674 | * Remove references for a page and establish the new page with the correct | 684 | * Remove references for a page and establish the new page with the correct |
675 | * basic settings to be able to stop accesses to the page. | 685 | * basic settings to be able to stop accesses to the page. |
676 | */ | 686 | */ |
677 | static int migrate_page_remove_references(struct page *newpage, | 687 | int migrate_page_remove_references(struct page *newpage, |
678 | struct page *page, int nr_refs) | 688 | struct page *page, int nr_refs) |
679 | { | 689 | { |
680 | struct address_space *mapping = page_mapping(page); | 690 | struct address_space *mapping = page_mapping(page); |
@@ -749,6 +759,7 @@ static int migrate_page_remove_references(struct page *newpage, | |||
749 | 759 | ||
750 | return 0; | 760 | return 0; |
751 | } | 761 | } |
762 | EXPORT_SYMBOL(migrate_page_remove_references); | ||
752 | 763 | ||
753 | /* | 764 | /* |
754 | * Copy the page to its new location | 765 | * Copy the page to its new location |
@@ -788,6 +799,7 @@ void migrate_page_copy(struct page *newpage, struct page *page) | |||
788 | if (PageWriteback(newpage)) | 799 | if (PageWriteback(newpage)) |
789 | end_page_writeback(newpage); | 800 | end_page_writeback(newpage); |
790 | } | 801 | } |
802 | EXPORT_SYMBOL(migrate_page_copy); | ||
791 | 803 | ||
792 | /* | 804 | /* |
793 | * Common logic to directly migrate a single page suitable for | 805 | * Common logic to directly migrate a single page suitable for |
@@ -815,6 +827,7 @@ int migrate_page(struct page *newpage, struct page *page) | |||
815 | remove_from_swap(newpage); | 827 | remove_from_swap(newpage); |
816 | return 0; | 828 | return 0; |
817 | } | 829 | } |
830 | EXPORT_SYMBOL(migrate_page); | ||
818 | 831 | ||
819 | /* | 832 | /* |
820 | * migrate_pages | 833 | * migrate_pages |
@@ -914,6 +927,11 @@ redo: | |||
914 | if (!mapping) | 927 | if (!mapping) |
915 | goto unlock_both; | 928 | goto unlock_both; |
916 | 929 | ||
930 | if (mapping->a_ops->migratepage) { | ||
931 | rc = mapping->a_ops->migratepage(newpage, page); | ||
932 | goto unlock_both; | ||
933 | } | ||
934 | |||
917 | /* | 935 | /* |
918 | * Trigger writeout if page is dirty | 936 | * Trigger writeout if page is dirty |
919 | */ | 937 | */ |