| From aecaac4f10a7eddd17d244a75290371f57bc5aaf Mon Sep 17 00:00:00 2001 |
| From: Piotr Jaroszynski <pjaroszynski@nvidia.com> |
| Date: Sun, 27 Jan 2019 08:46:45 -0800 |
| Subject: iomap: get/put the page in iomap_page_create/release() |
| |
| [ Upstream commit 8e47a457321ca1a74ad194ab5dcbca764bc70731 ] |
| |
| migrate_page_move_mapping() expects pages with private data set to have |
| a page_count elevated by 1. This is what used to happen for xfs through |
| the buffer_heads code before the switch to iomap in commit 82cb14175e7d |
| ("xfs: add support for sub-pagesize writeback without buffer_heads"). |
| Not having the count elevated causes move_pages() to fail on memory |
| mapped files coming from xfs. |
| |
| Make iomap compatible with the migrate_page_move_mapping() assumption by |
| elevating the page count as part of iomap_page_create() and lowering it |
| in iomap_page_release(). |
| |
| It causes the move_pages() syscall to misbehave on memory mapped files |
| from xfs. It does not not move any pages, which I suppose is "just" a |
| perf issue, but it also ends up returning a positive number which is out |
| of spec for the syscall. Talking to Michal Hocko, it sounds like |
| returning positive numbers might be a necessary update to move_pages() |
| anyway though. |
| |
| Fixes: 82cb14175e7d ("xfs: add support for sub-pagesize writeback without buffer_heads") |
| Signed-off-by: Piotr Jaroszynski <pjaroszynski@nvidia.com> |
| [hch: actually get/put the page iomap_migrate_page() to make it work |
| properly] |
| Signed-off-by: Christoph Hellwig <hch@lst.de> |
| Reviewed-by: Dave Chinner <dchinner@redhat.com> |
| Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/iomap.c | 9 +++++++++ |
| 1 file changed, 9 insertions(+) |
| |
| diff --git a/fs/iomap.c b/fs/iomap.c |
| index e57fb1e534c5..2e3e64012db7 100644 |
| --- a/fs/iomap.c |
| +++ b/fs/iomap.c |
| @@ -117,6 +117,12 @@ iomap_page_create(struct inode *inode, struct page *page) |
| atomic_set(&iop->read_count, 0); |
| atomic_set(&iop->write_count, 0); |
| bitmap_zero(iop->uptodate, PAGE_SIZE / SECTOR_SIZE); |
| + |
| + /* |
| + * migrate_page_move_mapping() assumes that pages with private data have |
| + * their count elevated by 1. |
| + */ |
| + get_page(page); |
| set_page_private(page, (unsigned long)iop); |
| SetPagePrivate(page); |
| return iop; |
| @@ -133,6 +139,7 @@ iomap_page_release(struct page *page) |
| WARN_ON_ONCE(atomic_read(&iop->write_count)); |
| ClearPagePrivate(page); |
| set_page_private(page, 0); |
| + put_page(page); |
| kfree(iop); |
| } |
| |
| @@ -565,8 +572,10 @@ iomap_migrate_page(struct address_space *mapping, struct page *newpage, |
| |
| if (page_has_private(page)) { |
| ClearPagePrivate(page); |
| + get_page(newpage); |
| set_page_private(newpage, page_private(page)); |
| set_page_private(page, 0); |
| + put_page(page); |
| SetPagePrivate(newpage); |
| } |
| |
| -- |
| 2.19.1 |
| |