From 0a03495e3ee75b3457181910081e809dda80cec0 Mon Sep 17 00:00:00 2001 From: Brian Atkinson Date: Tue, 16 Jun 2020 11:05:22 -0600 Subject: [PATCH] Fixing ABD struct allocation for FreeBSD In the event we are allocating a gang ABD in FreeBSD we are passing 0 to abd_alloc_struct(); however, this led to an allocation of ABD scatter with 0 chunks. This left the gang ABD allocation 24 bytes smaller than it should have been. Reviewed-by: Matt Macy Reviewed-by: Brian Behlendorf Co-authored-by: Matt Macy Signed-off-by: Brian Atkinson Closes #10431 --- module/os/freebsd/zfs/abd_os.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/module/os/freebsd/zfs/abd_os.c b/module/os/freebsd/zfs/abd_os.c index e87981815532..50017477572f 100644 --- a/module/os/freebsd/zfs/abd_os.c +++ b/module/os/freebsd/zfs/abd_os.c @@ -198,8 +198,15 @@ abd_t * abd_alloc_struct(size_t size) { size_t chunkcnt = abd_chunkcnt_for_bytes(size); - size_t abd_size = offsetof(abd_t, - abd_u.abd_scatter.abd_chunks[chunkcnt]); + /* + * In the event we are allocating a gang ABD, the size passed in + * will be 0. We must make sure to set abd_size to the size of an + * ABD struct as opposed to an ABD scatter with 0 chunks. The gang + * ABD struct allocation accounts for an additional 24 bytes over + * a scatter ABD with 0 chunks. + */ + size_t abd_size = MAX(sizeof (abd_t), + offsetof(abd_t, abd_u.abd_scatter.abd_chunks[chunkcnt])); abd_t *abd = kmem_alloc(abd_size, KM_PUSHPAGE); ASSERT3P(abd, !=, NULL); list_link_init(&abd->abd_gang_link); @@ -212,8 +219,10 @@ abd_alloc_struct(size_t size) void abd_free_struct(abd_t *abd) { - size_t chunkcnt = abd_is_linear(abd) ? 0 : abd_scatter_chunkcnt(abd); - int size = offsetof(abd_t, abd_u.abd_scatter.abd_chunks[chunkcnt]); + size_t chunkcnt = abd_is_linear(abd) || abd_is_gang(abd) ? 0 : + abd_scatter_chunkcnt(abd); + int size = MAX(sizeof (abd_t), + offsetof(abd_t, abd_u.abd_scatter.abd_chunks[chunkcnt])); mutex_destroy(&abd->abd_mtx); ASSERT(!list_link_active(&abd->abd_gang_link)); kmem_free(abd, size);