| From foo@baz Fri Aug 12 05:38:47 PM CEST 2022 |
| From: Amir Goldstein <amir73il@gmail.com> |
| Date: Wed, 10 Aug 2022 16:15:50 +0200 |
| Subject: mm: Add kvrealloc() |
| To: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Cc: Sasha Levin <sashal@kernel.org>, "Darrick J . Wong" <djwong@kernel.org>, Leah Rumancik <leah.rumancik@gmail.com>, Chandan Babu R <chandan.babu@oracle.com>, Luis Chamberlain <mcgrof@kernel.org>, Adam Manzanares <a.manzanares@samsung.com>, linux-xfs@vger.kernel.org, stable@vger.kernel.org, Dave Chinner <dchinner@redhat.com>, Mel Gorman <mgorman@techsingularity.net> |
| Message-ID: <20220810141552.168763-2-amir73il@gmail.com> |
| |
| From: Dave Chinner <dchinner@redhat.com> |
| |
| commit de2860f4636256836450c6543be744a50118fc66 upstream. |
| |
| During log recovery of an XFS filesystem with 64kB directory |
| buffers, rebuilding a buffer split across two log records results |
| in a memory allocation warning from krealloc like this: |
| |
| xfs filesystem being mounted at /mnt/scratch supports timestamps until 2038 (0x7fffffff) |
| XFS (dm-0): Unmounting Filesystem |
| XFS (dm-0): Mounting V5 Filesystem |
| XFS (dm-0): Starting recovery (logdev: internal) |
| ------------[ cut here ]------------ |
| WARNING: CPU: 5 PID: 3435170 at mm/page_alloc.c:3539 get_page_from_freelist+0xdee/0xe40 |
| ..... |
| RIP: 0010:get_page_from_freelist+0xdee/0xe40 |
| Call Trace: |
| ? complete+0x3f/0x50 |
| __alloc_pages+0x16f/0x300 |
| alloc_pages+0x87/0x110 |
| kmalloc_order+0x2c/0x90 |
| kmalloc_order_trace+0x1d/0x90 |
| __kmalloc_track_caller+0x215/0x270 |
| ? xlog_recover_add_to_cont_trans+0x63/0x1f0 |
| krealloc+0x54/0xb0 |
| xlog_recover_add_to_cont_trans+0x63/0x1f0 |
| xlog_recovery_process_trans+0xc1/0xd0 |
| xlog_recover_process_ophdr+0x86/0x130 |
| xlog_recover_process_data+0x9f/0x160 |
| xlog_recover_process+0xa2/0x120 |
| xlog_do_recovery_pass+0x40b/0x7d0 |
| ? __irq_work_queue_local+0x4f/0x60 |
| ? irq_work_queue+0x3a/0x50 |
| xlog_do_log_recovery+0x70/0x150 |
| xlog_do_recover+0x38/0x1d0 |
| xlog_recover+0xd8/0x170 |
| xfs_log_mount+0x181/0x300 |
| xfs_mountfs+0x4a1/0x9b0 |
| xfs_fs_fill_super+0x3c0/0x7b0 |
| get_tree_bdev+0x171/0x270 |
| ? suffix_kstrtoint.constprop.0+0xf0/0xf0 |
| xfs_fs_get_tree+0x15/0x20 |
| vfs_get_tree+0x24/0xc0 |
| path_mount+0x2f5/0xaf0 |
| __x64_sys_mount+0x108/0x140 |
| do_syscall_64+0x3a/0x70 |
| entry_SYSCALL_64_after_hwframe+0x44/0xae |
| |
| Essentially, we are taking a multi-order allocation from kmem_alloc() |
| (which has an open coded no fail, no warn loop) and then |
| reallocating it out to 64kB using krealloc(__GFP_NOFAIL) and that is |
| then triggering the above warning. |
| |
| This is a regression caused by converting this code from an open |
| coded no fail/no warn reallocation loop to using __GFP_NOFAIL. |
| |
| What we actually need here is kvrealloc(), so that if contiguous |
| page allocation fails we fall back to vmalloc() and we don't |
| get nasty warnings happening in XFS. |
| |
| Fixes: 771915c4f688 ("xfs: remove kmem_realloc()") |
| Signed-off-by: Dave Chinner <dchinner@redhat.com> |
| Acked-by: Mel Gorman <mgorman@techsingularity.net> |
| Reviewed-by: Darrick J. Wong <djwong@kernel.org> |
| Signed-off-by: Darrick J. Wong <djwong@kernel.org> |
| Signed-off-by: Amir Goldstein <amir73il@gmail.com> |
| Acked-by: Darrick J. Wong <djwong@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/xfs/xfs_log_recover.c | 4 +++- |
| include/linux/mm.h | 2 ++ |
| mm/util.c | 15 +++++++++++++++ |
| 3 files changed, 20 insertions(+), 1 deletion(-) |
| |
| --- a/fs/xfs/xfs_log_recover.c |
| +++ b/fs/xfs/xfs_log_recover.c |
| @@ -2061,7 +2061,9 @@ xlog_recover_add_to_cont_trans( |
| old_ptr = item->ri_buf[item->ri_cnt-1].i_addr; |
| old_len = item->ri_buf[item->ri_cnt-1].i_len; |
| |
| - ptr = krealloc(old_ptr, len + old_len, GFP_KERNEL | __GFP_NOFAIL); |
| + ptr = kvrealloc(old_ptr, old_len, len + old_len, GFP_KERNEL); |
| + if (!ptr) |
| + return -ENOMEM; |
| memcpy(&ptr[old_len], dp, len); |
| item->ri_buf[item->ri_cnt-1].i_len += len; |
| item->ri_buf[item->ri_cnt-1].i_addr = ptr; |
| --- a/include/linux/mm.h |
| +++ b/include/linux/mm.h |
| @@ -788,6 +788,8 @@ static inline void *kvcalloc(size_t n, s |
| return kvmalloc_array(n, size, flags | __GFP_ZERO); |
| } |
| |
| +extern void *kvrealloc(const void *p, size_t oldsize, size_t newsize, |
| + gfp_t flags); |
| extern void kvfree(const void *addr); |
| extern void kvfree_sensitive(const void *addr, size_t len); |
| |
| --- a/mm/util.c |
| +++ b/mm/util.c |
| @@ -661,6 +661,21 @@ void kvfree_sensitive(const void *addr, |
| } |
| EXPORT_SYMBOL(kvfree_sensitive); |
| |
| +void *kvrealloc(const void *p, size_t oldsize, size_t newsize, gfp_t flags) |
| +{ |
| + void *newp; |
| + |
| + if (oldsize >= newsize) |
| + return (void *)p; |
| + newp = kvmalloc(newsize, flags); |
| + if (!newp) |
| + return NULL; |
| + memcpy(newp, p, oldsize); |
| + kvfree(p); |
| + return newp; |
| +} |
| +EXPORT_SYMBOL(kvrealloc); |
| + |
| static inline void *__page_rmapping(struct page *page) |
| { |
| unsigned long mapping; |