xfs: fall back to vmalloc when allocation log vector buffers

When using large directory blocks, we regularly see memory
allocations of >64k being made for the shadow log vector buffer.
When we are under memory pressure, kmalloc() may not be able to find
contiguous memory chunks large enough to satisfy these allocations
easily, and if memory is fragmented we can potentially stall here.

TO avoid this problem, switch the log vector buffer allocation to
use kmem_alloc_large(). This will allow failed allocations to fall
back to vmalloc and so remove the dependency on large contiguous
regions of memory being available. This should prevent slowdowns
and potential stalls when memory is low and/or fragmented.

Signed-Off-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>
This commit is contained in:
Dave Chinner 2018-03-06 17:03:28 -08:00 committed by Darrick J. Wong
parent 0c8efd610b
commit cb0a8d2302
3 changed files with 11 additions and 5 deletions

View File

@ -46,13 +46,13 @@ kmem_alloc(size_t size, xfs_km_flags_t flags)
} }
void * void *
kmem_zalloc_large(size_t size, xfs_km_flags_t flags) kmem_alloc_large(size_t size, xfs_km_flags_t flags)
{ {
unsigned nofs_flag = 0; unsigned nofs_flag = 0;
void *ptr; void *ptr;
gfp_t lflags; gfp_t lflags;
ptr = kmem_zalloc(size, flags | KM_MAYFAIL); ptr = kmem_alloc(size, flags | KM_MAYFAIL);
if (ptr) if (ptr)
return ptr; return ptr;
@ -67,7 +67,7 @@ kmem_zalloc_large(size_t size, xfs_km_flags_t flags)
nofs_flag = memalloc_nofs_save(); nofs_flag = memalloc_nofs_save();
lflags = kmem_flags_convert(flags); lflags = kmem_flags_convert(flags);
ptr = __vmalloc(size, lflags | __GFP_ZERO, PAGE_KERNEL); ptr = __vmalloc(size, lflags, PAGE_KERNEL);
if (flags & KM_NOFS) if (flags & KM_NOFS)
memalloc_nofs_restore(nofs_flag); memalloc_nofs_restore(nofs_flag);

View File

@ -71,7 +71,7 @@ kmem_flags_convert(xfs_km_flags_t flags)
} }
extern void *kmem_alloc(size_t, xfs_km_flags_t); extern void *kmem_alloc(size_t, xfs_km_flags_t);
extern void *kmem_zalloc_large(size_t size, xfs_km_flags_t); extern void *kmem_alloc_large(size_t size, xfs_km_flags_t);
extern void *kmem_realloc(const void *, size_t, xfs_km_flags_t); extern void *kmem_realloc(const void *, size_t, xfs_km_flags_t);
static inline void kmem_free(const void *ptr) static inline void kmem_free(const void *ptr)
{ {
@ -85,6 +85,12 @@ kmem_zalloc(size_t size, xfs_km_flags_t flags)
return kmem_alloc(size, flags | KM_ZERO); return kmem_alloc(size, flags | KM_ZERO);
} }
static inline void *
kmem_zalloc_large(size_t size, xfs_km_flags_t flags)
{
return kmem_alloc_large(size, flags | KM_ZERO);
}
/* /*
* Zone interfaces * Zone interfaces
*/ */

View File

@ -202,7 +202,7 @@ xlog_cil_alloc_shadow_bufs(
*/ */
kmem_free(lip->li_lv_shadow); kmem_free(lip->li_lv_shadow);
lv = kmem_alloc(buf_size, KM_SLEEP|KM_NOFS); lv = kmem_alloc_large(buf_size, KM_SLEEP | KM_NOFS);
memset(lv, 0, xlog_cil_iovec_space(niovecs)); memset(lv, 0, xlog_cil_iovec_space(niovecs));
lv->lv_item = lip; lv->lv_item = lip;