mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-23 10:54:35 +03:00
arc: avoid possible deadlock in arc_read
In l2arc_evict(), the config lock may be acquired in reverse order (e.g., first the config lock (writer), then a hash lock) unlike in arc_read() during scenarios like L2ARC device removal. To avoid deadlocks, if the attempt to acquire the config lock (reader) fails in arc_read(), release the hash lock, wait for the config lock, and retry from the beginning. Reviewed-by: Alexander Motin <mav@FreeBSD.org> Signed-off-by: Ameer Hamza <ahamza@ixsystems.com> Closes #17071
This commit is contained in:
+22
-4
@@ -5683,6 +5683,7 @@ arc_read(zio_t *pio, spa_t *spa, const blkptr_t *bp,
|
||||
boolean_t no_buf = *arc_flags & ARC_FLAG_NO_BUF;
|
||||
arc_buf_t *buf = NULL;
|
||||
int rc = 0;
|
||||
boolean_t bp_validation = B_FALSE;
|
||||
|
||||
ASSERT(!embedded_bp ||
|
||||
BPE_GET_ETYPE(bp) == BP_EMBEDDED_TYPE_DATA);
|
||||
@@ -5725,7 +5726,7 @@ top:
|
||||
* should always be the case since the blkptr is protected by
|
||||
* a checksum.
|
||||
*/
|
||||
if (!zfs_blkptr_verify(spa, bp, BLK_CONFIG_SKIP,
|
||||
if (zfs_blkptr_verify(spa, bp, BLK_CONFIG_SKIP,
|
||||
BLK_VERIFY_LOG)) {
|
||||
mutex_exit(hash_lock);
|
||||
rc = SET_ERROR(ECKSUM);
|
||||
@@ -5877,6 +5878,8 @@ top:
|
||||
abd_t *hdr_abd;
|
||||
int alloc_flags = encrypted_read ? ARC_HDR_ALLOC_RDATA : 0;
|
||||
arc_buf_contents_t type = BP_GET_BUFC_TYPE(bp);
|
||||
int config_lock;
|
||||
int error;
|
||||
|
||||
if (*arc_flags & ARC_FLAG_CACHED_ONLY) {
|
||||
if (hash_lock != NULL)
|
||||
@@ -5885,16 +5888,31 @@ top:
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (zio_flags & ZIO_FLAG_CONFIG_WRITER) {
|
||||
config_lock = BLK_CONFIG_HELD;
|
||||
} else if (hash_lock != NULL) {
|
||||
/*
|
||||
* Prevent lock order reversal
|
||||
*/
|
||||
config_lock = BLK_CONFIG_NEEDED_TRY;
|
||||
} else {
|
||||
config_lock = BLK_CONFIG_NEEDED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the block pointer contents are reasonable. This
|
||||
* should always be the case since the blkptr is protected by
|
||||
* a checksum.
|
||||
*/
|
||||
if (!zfs_blkptr_verify(spa, bp,
|
||||
(zio_flags & ZIO_FLAG_CONFIG_WRITER) ?
|
||||
BLK_CONFIG_HELD : BLK_CONFIG_NEEDED, BLK_VERIFY_LOG)) {
|
||||
if (!bp_validation && (error = zfs_blkptr_verify(spa, bp,
|
||||
config_lock, BLK_VERIFY_LOG))) {
|
||||
if (hash_lock != NULL)
|
||||
mutex_exit(hash_lock);
|
||||
if (error == EBUSY && !zfs_blkptr_verify(spa, bp,
|
||||
BLK_CONFIG_NEEDED, BLK_VERIFY_LOG)) {
|
||||
bp_validation = B_TRUE;
|
||||
goto top;
|
||||
}
|
||||
rc = SET_ERROR(ECKSUM);
|
||||
goto done;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user