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 <behlendorf1@llnl.gov>
Reviewed-by: Tom Caputi <tcaputi@datto.com>
Signed-off-by: Matthew Ahrens <mahrens@delphix.com>
Closes #7424 
Closes #7429
This commit is contained in:
Matthew Ahrens
2018-04-17 11:06:54 -07:00
committed by Brian Behlendorf
parent b40d45bc6c
commit 0c03d21ac9
7 changed files with 105 additions and 218 deletions
+3 -116
View File
@@ -765,7 +765,7 @@ dmu_objset_zfs_unmounting(objset_t *os)
static int
dmu_free_long_range_impl(objset_t *os, dnode_t *dn, uint64_t offset,
uint64_t length, boolean_t raw)
uint64_t length)
{
uint64_t object_size;
int err;
@@ -848,19 +848,6 @@ dmu_free_long_range_impl(objset_t *os, dnode_t *dn, uint64_t offset,
uint64_t, dmu_tx_get_txg(tx));
dnode_free_range(dn, chunk_begin, chunk_len, tx);
/* if this is a raw free, mark the dirty record as such */
if (raw) {
dbuf_dirty_record_t *dr = dn->dn_dbuf->db_last_dirty;
while (dr != NULL && dr->dr_txg > tx->tx_txg)
dr = dr->dr_next;
if (dr != NULL && dr->dr_txg == tx->tx_txg) {
dr->dt.dl.dr_raw = B_TRUE;
dn->dn_objset->os_next_write_raw
[tx->tx_txg & TXG_MASK] = B_TRUE;
}
}
dmu_tx_commit(tx);
length -= chunk_len;
@@ -878,7 +865,7 @@ dmu_free_long_range(objset_t *os, uint64_t object,
err = dnode_hold(os, object, FTAG, &dn);
if (err != 0)
return (err);
err = dmu_free_long_range_impl(os, dn, offset, length, B_FALSE);
err = dmu_free_long_range_impl(os, dn, offset, length);
/*
* It is important to zero out the maxblkid when freeing the entire
@@ -893,37 +880,8 @@ dmu_free_long_range(objset_t *os, uint64_t object,
return (err);
}
/*
* This function is equivalent to dmu_free_long_range(), but also
* marks the new dirty record as a raw write.
*/
int
dmu_free_long_range_raw(objset_t *os, uint64_t object,
uint64_t offset, uint64_t length)
{
dnode_t *dn;
int err;
err = dnode_hold(os, object, FTAG, &dn);
if (err != 0)
return (err);
err = dmu_free_long_range_impl(os, dn, offset, length, B_TRUE);
/*
* It is important to zero out the maxblkid when freeing the entire
* file, so that (a) subsequent calls to dmu_free_long_range_impl()
* will take the fast path, and (b) dnode_reallocate() can verify
* that the entire file has been freed.
*/
if (err == 0 && offset == 0 && length == DMU_OBJECT_END)
dn->dn_maxblkid = 0;
dnode_rele(dn, FTAG);
return (err);
}
static int
dmu_free_long_object_impl(objset_t *os, uint64_t object, boolean_t raw)
dmu_free_long_object(objset_t *os, uint64_t object)
{
dmu_tx_t *tx;
int err;
@@ -938,8 +896,6 @@ dmu_free_long_object_impl(objset_t *os, uint64_t object, boolean_t raw)
dmu_tx_mark_netfree(tx);
err = dmu_tx_assign(tx, TXG_WAIT);
if (err == 0) {
if (raw)
err = dmu_object_dirty_raw(os, object, tx);
if (err == 0)
err = dmu_object_free(os, object, tx);
@@ -951,19 +907,6 @@ dmu_free_long_object_impl(objset_t *os, uint64_t object, boolean_t raw)
return (err);
}
int
dmu_free_long_object(objset_t *os, uint64_t object)
{
return (dmu_free_long_object_impl(os, object, B_FALSE));
}
int
dmu_free_long_object_raw(objset_t *os, uint64_t object)
{
return (dmu_free_long_object_impl(os, object, B_TRUE));
}
int
dmu_free_range(objset_t *os, uint64_t object, uint64_t offset,
uint64_t size, dmu_tx_t *tx)
@@ -1666,41 +1609,6 @@ dmu_return_arcbuf(arc_buf_t *buf)
arc_buf_destroy(buf, FTAG);
}
int
dmu_convert_mdn_block_to_raw(objset_t *os, uint64_t firstobj,
boolean_t byteorder, const uint8_t *salt, const uint8_t *iv,
const uint8_t *mac, dmu_tx_t *tx)
{
int ret;
dmu_buf_t *handle = NULL;
dmu_buf_impl_t *db = NULL;
uint64_t offset = firstobj * DNODE_MIN_SIZE;
uint64_t dsobj = dmu_objset_id(os);
ret = dmu_buf_hold_by_dnode(DMU_META_DNODE(os), offset, FTAG, &handle,
DMU_READ_PREFETCH | DMU_READ_NO_DECRYPT);
if (ret != 0)
return (ret);
dmu_buf_will_change_crypt_params(handle, tx);
db = (dmu_buf_impl_t *)handle;
ASSERT3P(db->db_buf, !=, NULL);
ASSERT3U(dsobj, !=, 0);
/*
* This technically violates the assumption the dmu code makes
* that dnode blocks are only released in syncing context.
*/
(void) arc_release(db->db_buf, db);
arc_convert_to_raw(db->db_buf, dsobj, byteorder, DMU_OT_DNODE,
salt, iv, mac);
dmu_buf_rele(handle, FTAG);
return (0);
}
void
dmu_copy_from_buf(objset_t *os, uint64_t object, uint64_t offset,
dmu_buf_t *handle, dmu_tx_t *tx)
@@ -2222,25 +2130,6 @@ dmu_object_set_compress(objset_t *os, uint64_t object, uint8_t compress,
dnode_rele(dn, FTAG);
}
/*
* Dirty an object and set the dirty record's raw flag. This is used
* when writing raw data to an object that will not effect the
* encryption parameters, specifically during raw receives.
*/
int
dmu_object_dirty_raw(objset_t *os, uint64_t object, dmu_tx_t *tx)
{
dnode_t *dn;
int err;
err = dnode_hold(os, object, FTAG, &dn);
if (err)
return (err);
dmu_buf_will_change_crypt_params((dmu_buf_t *)dn->dn_dbuf, tx);
dnode_rele(dn, FTAG);
return (err);
}
/*
* When the "redundant_metadata" property is set to "most", only indirect
* blocks of this level and higher will have an additional ditto block.
@@ -2623,9 +2512,7 @@ EXPORT_SYMBOL(dmu_buf_rele_array);
EXPORT_SYMBOL(dmu_prefetch);
EXPORT_SYMBOL(dmu_free_range);
EXPORT_SYMBOL(dmu_free_long_range);
EXPORT_SYMBOL(dmu_free_long_range_raw);
EXPORT_SYMBOL(dmu_free_long_object);
EXPORT_SYMBOL(dmu_free_long_object_raw);
EXPORT_SYMBOL(dmu_read);
EXPORT_SYMBOL(dmu_read_by_dnode);
EXPORT_SYMBOL(dmu_write);