diff --git a/module/zfs/arc.c b/module/zfs/arc.c index aa193fe59..5b0335e67 100644 --- a/module/zfs/arc.c +++ b/module/zfs/arc.c @@ -6667,16 +6667,22 @@ arc_release(arc_buf_t *buf, const void *tag) ASSERT3S(zfs_refcount_count(&hdr->b_l1hdr.b_refcnt), >, 0); /* - * Do we have more than one buf? + * Do we have more than one buf? Or L2_WRITING with unshared data? + * Single-buf L2_WRITING with shared data can reuse the header since + * L2ARC uses its own transformed copy. */ - if (hdr->b_l1hdr.b_buf != buf || !ARC_BUF_LAST(buf)) { + if (hdr->b_l1hdr.b_buf != buf || !ARC_BUF_LAST(buf) || + (HDR_L2_WRITING(hdr) && !ARC_BUF_SHARED(buf))) { arc_buf_hdr_t *nhdr; uint64_t spa = hdr->b_spa; uint64_t psize = HDR_GET_PSIZE(hdr); uint64_t lsize = HDR_GET_LSIZE(hdr); boolean_t protected = HDR_PROTECTED(hdr); enum zio_compress compress = arc_hdr_get_compress(hdr); + uint8_t complevel = hdr->b_complevel; arc_buf_contents_t type = arc_buf_type(hdr); + boolean_t single_buf_l2writing = (hdr->b_l1hdr.b_buf == buf && + ARC_BUF_LAST(buf) && HDR_L2_WRITING(hdr)); if (ARC_BUF_SHARED(buf) && !ARC_BUF_COMPRESSED(buf)) { ASSERT3P(hdr->b_l1hdr.b_buf, !=, buf); @@ -6687,48 +6693,51 @@ arc_release(arc_buf_t *buf, const void *tag) * Pull the buffer off of this hdr and find the last buffer * in the hdr's buffer list. */ - VERIFY3S(remove_reference(hdr, tag), >, 0); arc_buf_t *lastbuf = arc_buf_remove(hdr, buf); - ASSERT3P(lastbuf, !=, NULL); + EQUIV(single_buf_l2writing, lastbuf == NULL); /* * If the current arc_buf_t and the hdr are sharing their data * buffer, then we must stop sharing that block. */ - if (ARC_BUF_SHARED(buf)) { - ASSERT(!arc_buf_is_shared(lastbuf)); + if (!single_buf_l2writing) { + if (ARC_BUF_SHARED(buf)) { + ASSERT(!arc_buf_is_shared(lastbuf)); - /* - * First, sever the block sharing relationship between - * buf and the arc_buf_hdr_t. - */ - arc_unshare_buf(hdr, buf); + /* + * First, sever the block sharing relationship + * between buf and the arc_buf_hdr_t. + */ + arc_unshare_buf(hdr, buf); - /* - * Now we need to recreate the hdr's b_pabd. Since we - * have lastbuf handy, we try to share with it, but if - * we can't then we allocate a new b_pabd and copy the - * data from buf into it. - */ - if (arc_can_share(hdr, lastbuf)) { - arc_share_buf(hdr, lastbuf); - } else { - arc_hdr_alloc_abd(hdr, 0); - abd_copy_from_buf(hdr->b_l1hdr.b_pabd, - buf->b_data, psize); + /* + * Now we need to recreate the hdr's b_pabd. + * Since we have lastbuf handy, we try to share + * with it, but if we can't then we allocate a + * new b_pabd and copy the data from buf into it + */ + if (arc_can_share(hdr, lastbuf)) { + arc_share_buf(hdr, lastbuf); + } else { + arc_hdr_alloc_abd(hdr, 0); + abd_copy_from_buf(hdr->b_l1hdr.b_pabd, + buf->b_data, psize); + } + } else if (HDR_SHARED_DATA(hdr)) { + /* + * Uncompressed shared buffers are always at the + * end of the list. Compressed buffers don't + * have the same requirements. This makes it + * hard to simply assert that the lastbuf is + * shared so we rely on the hdr's compression + * flags to determine if we have a compressed, + * shared buffer. + */ + ASSERT(arc_buf_is_shared(lastbuf) || + arc_hdr_get_compress(hdr) != + ZIO_COMPRESS_OFF); + ASSERT(!arc_buf_is_shared(buf)); } - } else if (HDR_SHARED_DATA(hdr)) { - /* - * Uncompressed shared buffers are always at the end - * of the list. Compressed buffers don't have the - * same requirements. This makes it hard to - * simply assert that the lastbuf is shared so - * we rely on the hdr's compression flags to determine - * if we have a compressed, shared buffer. - */ - ASSERT(arc_buf_is_shared(lastbuf) || - arc_hdr_get_compress(hdr) != ZIO_COMPRESS_OFF); - ASSERT(!arc_buf_is_shared(buf)); } ASSERT(hdr->b_l1hdr.b_pabd != NULL || HDR_HAS_RABD(hdr)); @@ -6743,10 +6752,15 @@ arc_release(arc_buf_t *buf, const void *tag) if (!arc_hdr_has_uncompressed_buf(hdr)) arc_cksum_free(hdr); + if (single_buf_l2writing) + VERIFY3S(remove_reference(hdr, tag), ==, 0); + else + VERIFY3S(remove_reference(hdr, tag), >, 0); + mutex_exit(hash_lock); - nhdr = arc_hdr_alloc(spa, psize, lsize, protected, - compress, hdr->b_complevel, type); + nhdr = arc_hdr_alloc(spa, psize, lsize, protected, compress, + complevel, type); ASSERT0P(nhdr->b_l1hdr.b_buf); ASSERT0(zfs_refcount_count(&nhdr->b_l1hdr.b_refcnt)); VERIFY3U(nhdr->b_type, ==, type);