aboutsummaryrefslogtreecommitdiffstats
path: root/module/zfs/dbuf.c
diff options
context:
space:
mode:
authorMatthew Ahrens <[email protected]>2018-04-17 11:06:54 -0700
committerBrian Behlendorf <[email protected]>2018-04-17 11:06:54 -0700
commit0c03d21ac99ebdbefe65c319fc3712928c40af78 (patch)
tree5ec1e603a374a089fcd5b1c5b673974a5e687c1b /module/zfs/dbuf.c
parentb40d45bc6c0e908c4d1086ac8ac3dcd1f7178d2a (diff)
assertion in arc_release() during encrypted receive
In the existing code, when doing a raw (encrypted) zfs receive, we call arc_convert_to_raw() from open context. This creates a race condition between arc_release()/arc_change_state() and writing out the block from syncing context (arc_write_ready/done()). This change makes it so that when we are doing a raw (encrypted) zfs receive, we save the crypt parameters (salt, iv, mac) of dnode blocks in the dbuf_dirty_record_t, and call arc_convert_to_raw() from syncing context when writing out the block of dnodes. Additionally, we can eliminate dr_raw and associated setters, and instead know that dnode blocks are always raw when doing a zfs receive (see the new field os_raw_receive). Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Tom Caputi <[email protected]> Signed-off-by: Matthew Ahrens <[email protected]> Closes #7424 Closes #7429
Diffstat (limited to 'module/zfs/dbuf.c')
-rw-r--r--module/zfs/dbuf.c59
1 files changed, 38 insertions, 21 deletions
diff --git a/module/zfs/dbuf.c b/module/zfs/dbuf.c
index b0ae6cc72..bc03317a2 100644
--- a/module/zfs/dbuf.c
+++ b/module/zfs/dbuf.c
@@ -1538,7 +1538,7 @@ dbuf_unoverride(dbuf_dirty_record_t *dr)
dr->dt.dl.dr_override_state = DR_NOT_OVERRIDDEN;
dr->dt.dl.dr_nopwrite = B_FALSE;
- dr->dt.dl.dr_raw = B_FALSE;
+ dr->dt.dl.dr_has_raw_params = B_FALSE;
/*
* Release the already-written buffer, so we leave it in
@@ -2211,15 +2211,26 @@ dmu_buf_will_fill(dmu_buf_t *db_fake, dmu_tx_t *tx)
/*
* This function is effectively the same as dmu_buf_will_dirty(), but
- * indicates the caller expects raw encrypted data in the db. It will
- * also set the raw flag on the created dirty record.
+ * indicates the caller expects raw encrypted data in the db, and provides
+ * the crypt params (byteorder, salt, iv, mac) which should be stored in the
+ * blkptr_t when this dbuf is written. This is only used for blocks of
+ * dnodes, during raw receive.
*/
void
-dmu_buf_will_change_crypt_params(dmu_buf_t *db_fake, dmu_tx_t *tx)
+dmu_buf_set_crypt_params(dmu_buf_t *db_fake, boolean_t byteorder,
+ const uint8_t *salt, const uint8_t *iv, const uint8_t *mac, dmu_tx_t *tx)
{
dmu_buf_impl_t *db = (dmu_buf_impl_t *)db_fake;
dbuf_dirty_record_t *dr;
+ /*
+ * dr_has_raw_params is only processed for blocks of dnodes
+ * (see dbuf_sync_dnode_leaf_crypt()).
+ */
+ ASSERT3U(db->db.db_object, ==, DMU_META_DNODE_OBJECT);
+ ASSERT3U(db->db_level, ==, 0);
+ ASSERT(db->db_objset->os_raw_receive);
+
dmu_buf_will_dirty_impl(db_fake,
DB_RF_MUST_SUCCEED | DB_RF_NOPREFETCH | DB_RF_NO_DECRYPT, tx);
@@ -2229,8 +2240,12 @@ dmu_buf_will_change_crypt_params(dmu_buf_t *db_fake, dmu_tx_t *tx)
ASSERT3P(dr, !=, NULL);
ASSERT3U(dr->dr_txg, ==, tx->tx_txg);
- dr->dt.dl.dr_raw = B_TRUE;
- db->db_objset->os_next_write_raw[tx->tx_txg & TXG_MASK] = B_TRUE;
+
+ dr->dt.dl.dr_has_raw_params = B_TRUE;
+ dr->dt.dl.dr_byteorder = byteorder;
+ bcopy(salt, dr->dt.dl.dr_salt, ZIO_DATA_SALT_LEN);
+ bcopy(iv, dr->dt.dl.dr_iv, ZIO_DATA_IV_LEN);
+ bcopy(mac, dr->dt.dl.dr_mac, ZIO_DATA_MAC_LEN);
}
#pragma weak dmu_buf_fill_done = dbuf_fill_done
@@ -2341,7 +2356,6 @@ dbuf_assign_arcbuf(dmu_buf_impl_t *db, arc_buf_t *buf, dmu_tx_t *tx)
ASSERT(db->db_buf != NULL);
if (dr != NULL && dr->dr_txg == tx->tx_txg) {
ASSERT(dr->dt.dl.dr_data == db->db_buf);
- IMPLY(arc_is_encrypted(buf), dr->dt.dl.dr_raw);
if (!arc_released(db->db_buf)) {
ASSERT(dr->dt.dl.dr_override_state ==
@@ -3452,20 +3466,23 @@ dbuf_check_blkptr(dnode_t *dn, dmu_buf_impl_t *db)
}
/*
- * Ensure the dbuf's data is untransformed if the associated dirty
- * record requires it. This is used by dbuf_sync_leaf() to ensure
- * that a dnode block is decrypted before we write new data to it.
- * For raw writes we assert that the buffer is already encrypted.
+ * When syncing out a blocks of dnodes, adjust the block to deal with
+ * encryption. Normally, we make sure the block is decrypted before writing
+ * it. If we have crypt params, then we are writing a raw (encrypted) block,
+ * from a raw receive. In this case, set the ARC buf's crypt params so
+ * that the BP will be filled with the correct byteorder, salt, iv, and mac.
*/
static void
-dbuf_check_crypt(dbuf_dirty_record_t *dr)
+dbuf_prepare_encrypted_dnode_leaf(dbuf_dirty_record_t *dr)
{
int err;
dmu_buf_impl_t *db = dr->dr_dbuf;
ASSERT(MUTEX_HELD(&db->db_mtx));
+ ASSERT3U(db->db.db_object, ==, DMU_META_DNODE_OBJECT);
+ ASSERT3U(db->db_level, ==, 0);
- if (!dr->dt.dl.dr_raw && arc_is_encrypted(db->db_buf)) {
+ if (!db->db_objset->os_raw_receive && arc_is_encrypted(db->db_buf)) {
zbookmark_phys_t zb;
/*
@@ -3481,12 +3498,12 @@ dbuf_check_crypt(dbuf_dirty_record_t *dr)
&zb, B_TRUE);
if (err)
panic("Invalid dnode block MAC");
- } else if (dr->dt.dl.dr_raw) {
- /*
- * Writing raw encrypted data requires the db's arc buffer
- * to be converted to raw by the caller.
- */
- ASSERT(arc_is_encrypted(db->db_buf));
+ } else if (dr->dt.dl.dr_has_raw_params) {
+ (void) arc_release(dr->dt.dl.dr_data, db);
+ arc_convert_to_raw(dr->dt.dl.dr_data,
+ dmu_objset_id(db->db_objset),
+ dr->dt.dl.dr_byteorder, DMU_OT_DNODE,
+ dr->dt.dl.dr_salt, dr->dt.dl.dr_iv, dr->dt.dl.dr_mac);
}
}
@@ -3667,7 +3684,7 @@ dbuf_sync_leaf(dbuf_dirty_record_t *dr, dmu_tx_t *tx)
* or decrypted, depending on what we are writing to it this txg.
*/
if (os->os_encrypted && dn->dn_object == DMU_META_DNODE_OBJECT)
- dbuf_check_crypt(dr);
+ dbuf_prepare_encrypted_dnode_leaf(dr);
if (db->db_state != DB_NOFILL &&
dn->dn_object != DMU_META_DNODE_OBJECT &&
@@ -4336,7 +4353,7 @@ EXPORT_SYMBOL(dbuf_free_range);
EXPORT_SYMBOL(dbuf_new_size);
EXPORT_SYMBOL(dbuf_release_bp);
EXPORT_SYMBOL(dbuf_dirty);
-EXPORT_SYMBOL(dmu_buf_will_change_crypt_params);
+EXPORT_SYMBOL(dmu_buf_set_crypt_params);
EXPORT_SYMBOL(dmu_buf_will_dirty);
EXPORT_SYMBOL(dmu_buf_will_not_fill);
EXPORT_SYMBOL(dmu_buf_will_fill);