mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-24 19:28:53 +03:00
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:
committed by
Brian Behlendorf
parent
b40d45bc6c
commit
0c03d21ac9
+3
-116
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user