| From 4ac1c17b2044a1b4b2fbed74451947e905fc2992 Mon Sep 17 00:00:00 2001 |
| From: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com> |
| Date: Thu, 16 Jun 2016 23:26:15 +0200 |
| Subject: UBIFS: Implement ->migratepage() |
| |
| From: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> |
| |
| commit 4ac1c17b2044a1b4b2fbed74451947e905fc2992 upstream. |
| |
| During page migrations UBIFS might get confused |
| and the following assert triggers: |
| [ 213.480000] UBIFS assert failed in ubifs_set_page_dirty at 1451 (pid 436) |
| [ 213.490000] CPU: 0 PID: 436 Comm: drm-stress-test Not tainted 4.4.4-00176-geaa802524636-dirty #1008 |
| [ 213.490000] Hardware name: Allwinner sun4i/sun5i Families |
| [ 213.490000] [<c0015e70>] (unwind_backtrace) from [<c0012cdc>] (show_stack+0x10/0x14) |
| [ 213.490000] [<c0012cdc>] (show_stack) from [<c02ad834>] (dump_stack+0x8c/0xa0) |
| [ 213.490000] [<c02ad834>] (dump_stack) from [<c0236ee8>] (ubifs_set_page_dirty+0x44/0x50) |
| [ 213.490000] [<c0236ee8>] (ubifs_set_page_dirty) from [<c00fa0bc>] (try_to_unmap_one+0x10c/0x3a8) |
| [ 213.490000] [<c00fa0bc>] (try_to_unmap_one) from [<c00fadb4>] (rmap_walk+0xb4/0x290) |
| [ 213.490000] [<c00fadb4>] (rmap_walk) from [<c00fb1bc>] (try_to_unmap+0x64/0x80) |
| [ 213.490000] [<c00fb1bc>] (try_to_unmap) from [<c010dc28>] (migrate_pages+0x328/0x7a0) |
| [ 213.490000] [<c010dc28>] (migrate_pages) from [<c00d0cb0>] (alloc_contig_range+0x168/0x2f4) |
| [ 213.490000] [<c00d0cb0>] (alloc_contig_range) from [<c010ec00>] (cma_alloc+0x170/0x2c0) |
| [ 213.490000] [<c010ec00>] (cma_alloc) from [<c001a958>] (__alloc_from_contiguous+0x38/0xd8) |
| [ 213.490000] [<c001a958>] (__alloc_from_contiguous) from [<c001ad44>] (__dma_alloc+0x23c/0x274) |
| [ 213.490000] [<c001ad44>] (__dma_alloc) from [<c001ae08>] (arm_dma_alloc+0x54/0x5c) |
| [ 213.490000] [<c001ae08>] (arm_dma_alloc) from [<c035cecc>] (drm_gem_cma_create+0xb8/0xf0) |
| [ 213.490000] [<c035cecc>] (drm_gem_cma_create) from [<c035cf20>] (drm_gem_cma_create_with_handle+0x1c/0xe8) |
| [ 213.490000] [<c035cf20>] (drm_gem_cma_create_with_handle) from [<c035d088>] (drm_gem_cma_dumb_create+0x3c/0x48) |
| [ 213.490000] [<c035d088>] (drm_gem_cma_dumb_create) from [<c0341ed8>] (drm_ioctl+0x12c/0x444) |
| [ 213.490000] [<c0341ed8>] (drm_ioctl) from [<c0121adc>] (do_vfs_ioctl+0x3f4/0x614) |
| [ 213.490000] [<c0121adc>] (do_vfs_ioctl) from [<c0121d30>] (SyS_ioctl+0x34/0x5c) |
| [ 213.490000] [<c0121d30>] (SyS_ioctl) from [<c000f2c0>] (ret_fast_syscall+0x0/0x34) |
| |
| UBIFS is using PagePrivate() which can have different meanings across |
| filesystems. Therefore the generic page migration code cannot handle this |
| case correctly. |
| We have to implement our own migration function which basically does a |
| plain copy but also duplicates the page private flag. |
| UBIFS is not a block device filesystem and cannot use buffer_migrate_page(). |
| |
| Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> |
| [rw: Massaged changelog, build fixes, etc...] |
| Signed-off-by: Richard Weinberger <richard@nod.at> |
| Acked-by: Christoph Hellwig <hch@lst.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/ubifs/file.c | 24 ++++++++++++++++++++++++ |
| 1 file changed, 24 insertions(+) |
| |
| --- a/fs/ubifs/file.c |
| +++ b/fs/ubifs/file.c |
| @@ -52,6 +52,7 @@ |
| #include "ubifs.h" |
| #include <linux/mount.h> |
| #include <linux/slab.h> |
| +#include <linux/migrate.h> |
| |
| static int read_block(struct inode *inode, void *addr, unsigned int block, |
| struct ubifs_data_node *dn) |
| @@ -1452,6 +1453,26 @@ static int ubifs_set_page_dirty(struct p |
| return ret; |
| } |
| |
| +#ifdef CONFIG_MIGRATION |
| +static int ubifs_migrate_page(struct address_space *mapping, |
| + struct page *newpage, struct page *page, enum migrate_mode mode) |
| +{ |
| + int rc; |
| + |
| + rc = migrate_page_move_mapping(mapping, newpage, page, NULL, mode, 0); |
| + if (rc != MIGRATEPAGE_SUCCESS) |
| + return rc; |
| + |
| + if (PagePrivate(page)) { |
| + ClearPagePrivate(page); |
| + SetPagePrivate(newpage); |
| + } |
| + |
| + migrate_page_copy(newpage, page); |
| + return MIGRATEPAGE_SUCCESS; |
| +} |
| +#endif |
| + |
| static int ubifs_releasepage(struct page *page, gfp_t unused_gfp_flags) |
| { |
| /* |
| @@ -1591,6 +1612,9 @@ const struct address_space_operations ub |
| .write_end = ubifs_write_end, |
| .invalidatepage = ubifs_invalidatepage, |
| .set_page_dirty = ubifs_set_page_dirty, |
| +#ifdef CONFIG_MIGRATION |
| + .migratepage = ubifs_migrate_page, |
| +#endif |
| .releasepage = ubifs_releasepage, |
| }; |
| |