Removing ABD Parent Child Reference Before Freeing ABD

Moving the call to zfs_refcount_remove_many() in abd_free() to be called
before any of the ABD free variants are called. This is necessary
because abd_free_gang() adjusts the abd_size for the gang ABD. If the
parent's child references are removed after free'ing the gang ABD the
refcount is not adjusted correctly for the parent's children.

I also removed some stray abd_put() in comments and changed
abd_free_gang_abd() -> abd_free_gang().

Reviewed-by: Mark Maybee <mark.maybee@delphix.com>
Reviewed-by: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Brian Atkinson <batkinson@lanl.gov>
Closes #11539
This commit is contained in:
Brian Atkinson 2021-01-28 10:15:17 -07:00 committed by GitHub
parent 393e69241e
commit 416015ef54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -241,7 +241,7 @@ abd_free_linear(abd_t *abd)
} }
static void static void
abd_free_gang_abd(abd_t *abd) abd_free_gang(abd_t *abd)
{ {
ASSERT(abd_is_gang(abd)); ASSERT(abd_is_gang(abd));
abd_t *cabd; abd_t *cabd;
@ -292,8 +292,13 @@ abd_free(abd_t *abd)
abd_verify(abd); abd_verify(abd);
IMPLY(abd->abd_flags & ABD_FLAG_OWNER, abd->abd_parent == NULL); IMPLY(abd->abd_flags & ABD_FLAG_OWNER, abd->abd_parent == NULL);
if (abd->abd_parent != NULL) {
(void) zfs_refcount_remove_many(&abd->abd_parent->abd_children,
abd->abd_size, abd);
}
if (abd_is_gang(abd)) { if (abd_is_gang(abd)) {
abd_free_gang_abd(abd); abd_free_gang(abd);
} else if (abd_is_linear(abd)) { } else if (abd_is_linear(abd)) {
if (abd->abd_flags & ABD_FLAG_OWNER) if (abd->abd_flags & ABD_FLAG_OWNER)
abd_free_linear(abd); abd_free_linear(abd);
@ -302,11 +307,6 @@ abd_free(abd_t *abd)
abd_free_scatter(abd); abd_free_scatter(abd);
} }
if (abd->abd_parent != NULL) {
(void) zfs_refcount_remove_many(&abd->abd_parent->abd_children,
abd->abd_size, abd);
}
abd_fini_struct(abd); abd_fini_struct(abd);
if (abd->abd_flags & ABD_FLAG_ALLOCD) if (abd->abd_flags & ABD_FLAG_ALLOCD)
abd_free_struct_impl(abd); abd_free_struct_impl(abd);
@ -421,7 +421,7 @@ abd_gang_add(abd_t *pabd, abd_t *cabd, boolean_t free_on_free)
* allocated ABD with ABD_FLAG_GANG_FREE, before * allocated ABD with ABD_FLAG_GANG_FREE, before
* adding it to the gang ABD's list, to make the * adding it to the gang ABD's list, to make the
* gang ABD aware that it is responsible to call * gang ABD aware that it is responsible to call
* abd_put(). We use abd_get_offset() in order * abd_free(). We use abd_get_offset() in order
* to just allocate a new ABD but avoid copying the * to just allocate a new ABD but avoid copying the
* data over into the newly allocated ABD. * data over into the newly allocated ABD.
* *
@ -565,8 +565,7 @@ abd_get_offset_size(abd_t *sabd, size_t off, size_t size)
} }
/* /*
* Return a size scatter ABD. In order to free the returned * Return a size scatter ABD containing only zeros.
* ABD abd_put() must be called.
*/ */
abd_t * abd_t *
abd_get_zeros(size_t size) abd_get_zeros(size_t size)
@ -577,8 +576,7 @@ abd_get_zeros(size_t size)
} }
/* /*
* Allocate a linear ABD structure for buf. You must free this with abd_put() * Allocate a linear ABD structure for buf.
* since the resulting ABD doesn't own its own buffer.
*/ */
abd_t * abd_t *
abd_get_from_buf(void *buf, size_t size) abd_get_from_buf(void *buf, size_t size)