aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJorgen Lundman <lundman@lundman.net>2021-07-01 15:28:15 +0000
committerGitHub <noreply@github.com>2021-07-01 15:28:15 +0000
commitc6d1112bf4125e5a22eb47ceb7b8cee01f0df9a1 (patch)
tree5ed4fbdfee28b0a46163a346d32a1984773eb31f
parenteca174527e0b8416550e6ce87c405702fd379ada (diff)
downloadsrc-c6d1112bf4125e5a22eb47ceb7b8cee01f0df9a1.tar.gz
src-c6d1112bf4125e5a22eb47ceb7b8cee01f0df9a1.zip
Fix abd leak, kmem_free correct size of abd_t
Fix a leak of abd_t that manifested mostly when using raidzN with at least as many columns as N (e.g. a four-disk raidz2 but not a three-disk raidz2). Sufficiently heavy raidz use would eventually run a system out of memory. Additionally: * Switch abd_cache arena to FIRSTFIT, which empirically improves perofrmance. * Make abd_chunk_cache more performant and debuggable. * Allocate the abd_zero_buf from abd_chunk_cache rather than the heap. * Don't try to reap non-existent qcaches in abd_cache arena. * KM_PUSHPAGE->KM_SLEEP when allocating chunks from their own arena Reviewed-by: Matthew Ahrens <mahrens@delphix.com> Reviewed-by: Alexander Motin <mav@FreeBSD.org> Signed-off-by: Jorgen Lundman <lundman@lundman.net> Co-authored-by: Sean Doran <smd@use.net> Closes #12295
-rw-r--r--include/sys/abd_impl.h2
-rw-r--r--module/os/freebsd/zfs/abd_os.c9
-rw-r--r--module/os/linux/zfs/abd_os.c3
-rw-r--r--module/zfs/abd.c2
4 files changed, 10 insertions, 6 deletions
diff --git a/include/sys/abd_impl.h b/include/sys/abd_impl.h
index 6bce08cfa343..113700cd72b1 100644
--- a/include/sys/abd_impl.h
+++ b/include/sys/abd_impl.h
@@ -64,7 +64,7 @@ void abd_free_struct(abd_t *);
*/
abd_t *abd_alloc_struct_impl(size_t);
-abd_t *abd_get_offset_scatter(abd_t *, abd_t *, size_t);
+abd_t *abd_get_offset_scatter(abd_t *, abd_t *, size_t, size_t);
void abd_free_struct_impl(abd_t *);
void abd_alloc_chunks(abd_t *, size_t);
void abd_free_chunks(abd_t *);
diff --git a/module/os/freebsd/zfs/abd_os.c b/module/os/freebsd/zfs/abd_os.c
index 47adc2278df2..4f5b33d94510 100644
--- a/module/os/freebsd/zfs/abd_os.c
+++ b/module/os/freebsd/zfs/abd_os.c
@@ -374,14 +374,17 @@ abd_alloc_for_io(size_t size, boolean_t is_metadata)
}
abd_t *
-abd_get_offset_scatter(abd_t *abd, abd_t *sabd, size_t off)
+abd_get_offset_scatter(abd_t *abd, abd_t *sabd, size_t off,
+ size_t size)
{
abd_verify(sabd);
ASSERT3U(off, <=, sabd->abd_size);
size_t new_offset = ABD_SCATTER(sabd).abd_offset + off;
- uint_t chunkcnt = abd_scatter_chunkcnt(sabd) -
- (new_offset / zfs_abd_chunk_size);
+ size_t chunkcnt = abd_chunkcnt_for_bytes(
+ (new_offset % zfs_abd_chunk_size) + size);
+
+ ASSERT3U(chunkcnt, <=, abd_scatter_chunkcnt(sabd));
/*
* If an abd struct is provided, it is only the minimum size. If we
diff --git a/module/os/linux/zfs/abd_os.c b/module/os/linux/zfs/abd_os.c
index af543d6e3f7e..d1d238a4e303 100644
--- a/module/os/linux/zfs/abd_os.c
+++ b/module/os/linux/zfs/abd_os.c
@@ -835,7 +835,8 @@ abd_alloc_for_io(size_t size, boolean_t is_metadata)
}
abd_t *
-abd_get_offset_scatter(abd_t *abd, abd_t *sabd, size_t off)
+abd_get_offset_scatter(abd_t *abd, abd_t *sabd, size_t off,
+ size_t size)
{
int i = 0;
struct scatterlist *sg = NULL;
diff --git a/module/zfs/abd.c b/module/zfs/abd.c
index d5fafccd08af..cc2d3575db63 100644
--- a/module/zfs/abd.c
+++ b/module/zfs/abd.c
@@ -531,7 +531,7 @@ abd_get_offset_impl(abd_t *abd, abd_t *sabd, size_t off, size_t size)
}
ASSERT3U(left, ==, 0);
} else {
- abd = abd_get_offset_scatter(abd, sabd, off);
+ abd = abd_get_offset_scatter(abd, sabd, off, size);
}
ASSERT3P(abd, !=, NULL);