Skip to content

Commit 0d23f5e

Browse files
authored
Fix hash_lock / keystore.sk_dk_lock lock inversion
The keystore.sk_dk_lock should not be held while performing I/O. Drop the lock when reading from disk and update the code so they the first successful caller adds the key. Improve error handling in spa_keystore_create_mapping_impl(). Reviewed by: Thomas Caputi <[email protected]> Reviewed-by: RageLtMan <rageltman@sempervictus> Signed-off-by: Brian Behlendorf <[email protected]> Closes #7112 Closes #7115
1 parent fbd4254 commit 0d23f5e

File tree

1 file changed

+39
-48
lines changed

1 file changed

+39
-48
lines changed

module/zfs/dsl_crypt.c

Lines changed: 39 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -655,55 +655,56 @@ spa_keystore_dsl_key_hold_dd(spa_t *spa, dsl_dir_t *dd, void *tag,
655655
{
656656
int ret;
657657
avl_index_t where;
658-
dsl_crypto_key_t *dck = NULL;
658+
dsl_crypto_key_t *dck_io = NULL, *dck_ks = NULL;
659659
dsl_wrapping_key_t *wkey = NULL;
660660
uint64_t dckobj = dd->dd_crypto_obj;
661661

662-
rw_enter(&spa->spa_keystore.sk_dk_lock, RW_WRITER);
663-
664-
/* lookup the key in the tree of currently loaded keys */
665-
ret = spa_keystore_dsl_key_hold_impl(spa, dckobj, tag, &dck);
666-
if (!ret) {
667-
rw_exit(&spa->spa_keystore.sk_dk_lock);
668-
*dck_out = dck;
662+
/* Lookup the key in the tree of currently loaded keys */
663+
rw_enter(&spa->spa_keystore.sk_dk_lock, RW_READER);
664+
ret = spa_keystore_dsl_key_hold_impl(spa, dckobj, tag, &dck_ks);
665+
rw_exit(&spa->spa_keystore.sk_dk_lock);
666+
if (ret == 0) {
667+
*dck_out = dck_ks;
669668
return (0);
670669
}
671670

672-
/* lookup the wrapping key from the keystore */
671+
/* Lookup the wrapping key from the keystore */
673672
ret = spa_keystore_wkey_hold_dd(spa, dd, FTAG, &wkey);
674673
if (ret != 0) {
675-
ret = SET_ERROR(EACCES);
676-
goto error_unlock;
674+
*dck_out = NULL;
675+
return (SET_ERROR(EACCES));
677676
}
678677

679-
/* read the key from disk */
678+
/* Read the key from disk */
680679
ret = dsl_crypto_key_open(spa->spa_meta_objset, wkey, dckobj,
681-
tag, &dck);
682-
if (ret != 0)
683-
goto error_unlock;
680+
tag, &dck_io);
681+
if (ret != 0) {
682+
dsl_wrapping_key_rele(wkey, FTAG);
683+
*dck_out = NULL;
684+
return (ret);
685+
}
684686

685687
/*
686-
* add the key to the keystore (this should always succeed
687-
* since we made sure it didn't exist before)
688+
* Add the key to the keystore. It may already exist if it was
689+
* added while performing the read from disk. In this case discard
690+
* it and return the key from the keystore.
688691
*/
689-
avl_find(&spa->spa_keystore.sk_dsl_keys, dck, &where);
690-
avl_insert(&spa->spa_keystore.sk_dsl_keys, dck, where);
692+
rw_enter(&spa->spa_keystore.sk_dk_lock, RW_WRITER);
693+
ret = spa_keystore_dsl_key_hold_impl(spa, dckobj, tag, &dck_ks);
694+
if (ret != 0) {
695+
avl_find(&spa->spa_keystore.sk_dsl_keys, dck_io, &where);
696+
avl_insert(&spa->spa_keystore.sk_dsl_keys, dck_io, where);
697+
*dck_out = dck_io;
698+
} else {
699+
dsl_crypto_key_free(dck_io);
700+
*dck_out = dck_ks;
701+
}
691702

692-
/* release the wrapping key (the dsl key now has a reference to it) */
703+
/* Release the wrapping key (the dsl key now has a reference to it) */
693704
dsl_wrapping_key_rele(wkey, FTAG);
694-
695705
rw_exit(&spa->spa_keystore.sk_dk_lock);
696706

697-
*dck_out = dck;
698707
return (0);
699-
700-
error_unlock:
701-
rw_exit(&spa->spa_keystore.sk_dk_lock);
702-
if (wkey != NULL)
703-
dsl_wrapping_key_rele(wkey, FTAG);
704-
705-
*dck_out = NULL;
706-
return (ret);
707708
}
708709

709710
void
@@ -934,20 +935,19 @@ spa_keystore_create_mapping_impl(spa_t *spa, uint64_t dsobj,
934935
{
935936
int ret;
936937
avl_index_t where;
937-
dsl_key_mapping_t *km = NULL, *found_km;
938+
dsl_key_mapping_t *km, *found_km;
938939
boolean_t should_free = B_FALSE;
939940

940-
/* allocate the mapping */
941-
km = kmem_alloc(sizeof (dsl_key_mapping_t), KM_SLEEP);
942-
if (!km)
943-
return (SET_ERROR(ENOMEM));
944-
945-
/* initialize the mapping */
941+
/* Allocate and initialize the mapping */
942+
km = kmem_zalloc(sizeof (dsl_key_mapping_t), KM_SLEEP);
946943
refcount_create(&km->km_refcnt);
947944

948945
ret = spa_keystore_dsl_key_hold_dd(spa, dd, km, &km->km_key);
949-
if (ret != 0)
950-
goto error;
946+
if (ret != 0) {
947+
refcount_destroy(&km->km_refcnt);
948+
kmem_free(km, sizeof (dsl_key_mapping_t));
949+
return (ret);
950+
}
951951

952952
km->km_dsobj = dsobj;
953953

@@ -979,15 +979,6 @@ spa_keystore_create_mapping_impl(spa_t *spa, uint64_t dsobj,
979979
}
980980

981981
return (0);
982-
983-
error:
984-
if (km->km_key)
985-
spa_keystore_dsl_key_rele(spa, km->km_key, km);
986-
987-
refcount_destroy(&km->km_refcnt);
988-
kmem_free(km, sizeof (dsl_key_mapping_t));
989-
990-
return (ret);
991982
}
992983

993984
int

0 commit comments

Comments
 (0)