Always wait for txg sync when umounting dataset

Currently, when unmounting a filesystem, ZFS will only wait for
a txg sync if the dataset is dirty and not readonly. However, this
can be problematic in cases where a dataset is remounted readonly
immediately before being unmounted, which often happens when the
system is being shut down. Since encrypted datasets require that
all I/O is completed before the dataset is disowned, this issue
causes problems when write I/Os leak into the txgs after the
dataset is disowned, which can happen when sync=disabled.

While looking into fixes for this issue, it was discovered that
dsl_dataset_is_dirty() does not return B_TRUE when the dataset has
been removed from the txg dirty datasets list, but has not actually
been processed yet. Furthermore, the implementation is comletely
different from dmu_objset_is_dirty(), adding to the confusion.
Rather than relying on this function, this patch forces the umount
code path (and the remount readonly code path) to always perform a
txg sync on read-write datasets and removes the function altogether.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #7753
Closes #7795
This commit is contained in:
Tom Caputi
2018-08-20 16:42:17 -04:00
committed by Brian Behlendorf
parent 8c4fb36a24
commit 47ab01a18f
5 changed files with 26 additions and 33 deletions
-20
View File
@@ -794,15 +794,6 @@ dsl_dataset_rele_flags(dsl_dataset_t *ds, ds_hold_flags_t flags, void *tag)
(flags & DS_HOLD_FLAG_DECRYPT)) {
(void) spa_keystore_remove_mapping(ds->ds_dir->dd_pool->dp_spa,
ds->ds_object, ds);
/*
* Encrypted datasets require that users only release their
* decrypting reference after the dirty data has actually
* been written out. This ensures that the mapping exists
* when it is needed to write out dirty data.
*/
ASSERT(dmu_buf_user_refcount(ds->ds_dbuf) != 0 ||
!dsl_dataset_is_dirty(ds));
}
dmu_buf_rele(ds->ds_dbuf, tag);
@@ -1168,17 +1159,6 @@ dsl_dataset_dirty(dsl_dataset_t *ds, dmu_tx_t *tx)
}
}
boolean_t
dsl_dataset_is_dirty(dsl_dataset_t *ds)
{
for (int t = 0; t < TXG_SIZE; t++) {
if (txg_list_member(&ds->ds_dir->dd_pool->dp_dirty_datasets,
ds, t))
return (B_TRUE);
}
return (B_FALSE);
}
static int
dsl_dataset_snapshot_reserve_space(dsl_dataset_t *ds, dmu_tx_t *tx)
{
+6 -3
View File
@@ -1745,10 +1745,10 @@ zfsvfs_teardown(zfsvfs_t *zfsvfs, boolean_t unmounting)
zfs_unregister_callbacks(zfsvfs);
/*
* Evict cached data
* Evict cached data. We must write out any dirty data before
* disowning the dataset.
*/
if (dsl_dataset_is_dirty(dmu_objset_ds(zfsvfs->z_os)) &&
!zfs_is_readonly(zfsvfs))
if (!zfs_is_readonly(zfsvfs))
txg_wait_synced(dmu_objset_pool(zfsvfs->z_os), 0);
dmu_objset_evict_dbufs(zfsvfs->z_os);
@@ -1970,6 +1970,9 @@ zfs_remount(struct super_block *sb, int *flags, zfs_mnt_t *zm)
if (error)
return (error);
if (!zfs_is_readonly(zfsvfs) && (*flags & MS_RDONLY))
txg_wait_synced(dmu_objset_pool(zfsvfs->z_os), 0);
zfs_unregister_callbacks(zfsvfs);
zfsvfs_vfs_free(zfsvfs->z_vfs);
+4 -5
View File
@@ -1193,10 +1193,10 @@ zvol_shutdown_zv(zvol_state_t *zv)
zv->zv_dn = NULL;
/*
* Evict cached data
* Evict cached data. We must write out any dirty data before
* disowning the dataset.
*/
if (dsl_dataset_is_dirty(dmu_objset_ds(zv->zv_objset)) &&
!(zv->zv_flags & ZVOL_RDONLY))
if (!(zv->zv_flags & ZVOL_RDONLY))
txg_wait_synced(dmu_objset_pool(zv->zv_objset), 0);
(void) dmu_objset_evict_dbufs(zv->zv_objset);
}
@@ -1489,8 +1489,7 @@ zvol_ioctl(struct block_device *bdev, fmode_t mode,
invalidate_bdev(bdev);
rw_enter(&zv->zv_suspend_lock, RW_READER);
if (dsl_dataset_is_dirty(dmu_objset_ds(zv->zv_objset)) &&
!(zv->zv_flags & ZVOL_RDONLY))
if (!(zv->zv_flags & ZVOL_RDONLY))
txg_wait_synced(dmu_objset_pool(zv->zv_objset), 0);
rw_exit(&zv->zv_suspend_lock);