Fix ENOSPC when unlinking multiple files from full pool
When unlinking multiple files from a pool at 100% capacity, it was possible for ENOSPC to be returned after the first unlink. e.g. rm -f /mnt/fs/test1.0.0 /mnt/fs/test1.1.0 /mnt/fs/test1.2.0 rm: cannot remove '/mnt/fs/test1.1.0': No space left on device rm: cannot remove '/mnt/fs/test1.2.0': No space left on device After waiting for the pending deferred frees from the first unlink to be processed the remaining files can then be unlinked. This is caused by the quota limit in dsl_dir_tempreserve_impl() being temporarily decreased to the allocatable pool capacity less any deferred free space. This is resolved using the existing mechanism of returning ERESTART when over quota as long as we know enough space will shortly be available after processing the pending deferred frees. Reviewed-by: Alexander Motin <mav@FreeBSD.org> Reviewed-by: Ryan Moeller <freqlabs@FreeBSD.org> Reviewed-by: Tony Hutter <hutter2@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #13172
This commit is contained in:
parent
39a4daf742
commit
6df43169b3
|
@ -162,6 +162,7 @@ int dsl_pool_sync_context(dsl_pool_t *dp);
|
||||||
uint64_t dsl_pool_adjustedsize(dsl_pool_t *dp, zfs_space_check_t slop_policy);
|
uint64_t dsl_pool_adjustedsize(dsl_pool_t *dp, zfs_space_check_t slop_policy);
|
||||||
uint64_t dsl_pool_unreserved_space(dsl_pool_t *dp,
|
uint64_t dsl_pool_unreserved_space(dsl_pool_t *dp,
|
||||||
zfs_space_check_t slop_policy);
|
zfs_space_check_t slop_policy);
|
||||||
|
uint64_t dsl_pool_deferred_space(dsl_pool_t *dp);
|
||||||
void dsl_pool_wrlog_count(dsl_pool_t *dp, int64_t size, uint64_t txg);
|
void dsl_pool_wrlog_count(dsl_pool_t *dp, int64_t size, uint64_t txg);
|
||||||
boolean_t dsl_pool_wrlog_over_max(dsl_pool_t *dp);
|
boolean_t dsl_pool_wrlog_over_max(dsl_pool_t *dp);
|
||||||
void dsl_pool_dirty_space(dsl_pool_t *dp, int64_t space, dmu_tx_t *tx);
|
void dsl_pool_dirty_space(dsl_pool_t *dp, int64_t space, dmu_tx_t *tx);
|
||||||
|
|
|
@ -1334,13 +1334,21 @@ top_of_function:
|
||||||
/*
|
/*
|
||||||
* If they are requesting more space, and our current estimate
|
* If they are requesting more space, and our current estimate
|
||||||
* is over quota, they get to try again unless the actual
|
* is over quota, they get to try again unless the actual
|
||||||
* on-disk is over quota and there are no pending changes (which
|
* on-disk is over quota and there are no pending changes
|
||||||
* may free up space for us).
|
* or deferred frees (which may free up space for us).
|
||||||
*/
|
*/
|
||||||
if (used_on_disk + est_inflight >= quota) {
|
if (used_on_disk + est_inflight >= quota) {
|
||||||
if (est_inflight > 0 || used_on_disk < quota ||
|
if (est_inflight > 0 || used_on_disk < quota) {
|
||||||
(retval == ENOSPC && used_on_disk < quota))
|
retval = SET_ERROR(ERESTART);
|
||||||
retval = ERESTART;
|
} else {
|
||||||
|
ASSERT3U(used_on_disk, >=, quota);
|
||||||
|
|
||||||
|
if (retval == ENOSPC && (used_on_disk - quota) <
|
||||||
|
dsl_pool_deferred_space(dd->dd_pool)) {
|
||||||
|
retval = SET_ERROR(ERESTART);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dprintf_dd(dd, "failing: used=%lluK inflight = %lluK "
|
dprintf_dd(dd, "failing: used=%lluK inflight = %lluK "
|
||||||
"quota=%lluK tr=%lluK err=%d\n",
|
"quota=%lluK tr=%lluK err=%d\n",
|
||||||
(u_longlong_t)used_on_disk>>10,
|
(u_longlong_t)used_on_disk>>10,
|
||||||
|
@ -1348,7 +1356,7 @@ top_of_function:
|
||||||
(u_longlong_t)quota>>10, (u_longlong_t)asize>>10, retval);
|
(u_longlong_t)quota>>10, (u_longlong_t)asize>>10, retval);
|
||||||
mutex_exit(&dd->dd_lock);
|
mutex_exit(&dd->dd_lock);
|
||||||
DMU_TX_STAT_BUMP(dmu_tx_quota);
|
DMU_TX_STAT_BUMP(dmu_tx_quota);
|
||||||
return (SET_ERROR(retval));
|
return (retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We need to up our estimated delta before dropping dd_lock */
|
/* We need to up our estimated delta before dropping dd_lock */
|
||||||
|
|
|
@ -950,6 +950,12 @@ dsl_pool_unreserved_space(dsl_pool_t *dp, zfs_space_check_t slop_policy)
|
||||||
return (quota);
|
return (quota);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t
|
||||||
|
dsl_pool_deferred_space(dsl_pool_t *dp)
|
||||||
|
{
|
||||||
|
return (metaslab_class_get_deferred(spa_normal_class(dp->dp_spa)));
|
||||||
|
}
|
||||||
|
|
||||||
boolean_t
|
boolean_t
|
||||||
dsl_pool_need_dirty_delay(dsl_pool_t *dp)
|
dsl_pool_need_dirty_delay(dsl_pool_t *dp)
|
||||||
{
|
{
|
||||||
|
|
|
@ -694,7 +694,7 @@ tags = ['functional', 'nestedfs']
|
||||||
|
|
||||||
[tests/functional/no_space]
|
[tests/functional/no_space]
|
||||||
tests = ['enospc_001_pos', 'enospc_002_pos', 'enospc_003_pos',
|
tests = ['enospc_001_pos', 'enospc_002_pos', 'enospc_003_pos',
|
||||||
'enospc_df']
|
'enospc_df', 'enospc_rm']
|
||||||
tags = ['functional', 'no_space']
|
tags = ['functional', 'no_space']
|
||||||
|
|
||||||
[tests/functional/nopwrite]
|
[tests/functional/nopwrite]
|
||||||
|
|
|
@ -5,7 +5,8 @@ dist_pkgdata_SCRIPTS = \
|
||||||
enospc_001_pos.ksh \
|
enospc_001_pos.ksh \
|
||||||
enospc_002_pos.ksh \
|
enospc_002_pos.ksh \
|
||||||
enospc_003_pos.ksh \
|
enospc_003_pos.ksh \
|
||||||
enospc_df.ksh
|
enospc_df.ksh \
|
||||||
|
enospc_rm.ksh
|
||||||
|
|
||||||
dist_pkgdata_DATA = \
|
dist_pkgdata_DATA = \
|
||||||
enospc.cfg
|
enospc.cfg
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/bin/ksh -p
|
||||||
|
#
|
||||||
|
# CDDL HEADER START
|
||||||
|
#
|
||||||
|
# This file and its contents are supplied under the terms of the
|
||||||
|
# Common Development and Distribution License ("CDDL"), version 1.0.
|
||||||
|
# You may only use this file in accordance with the terms of version
|
||||||
|
# 1.0 of the CDDL.
|
||||||
|
#
|
||||||
|
# A full copy of the text of the CDDL should have accompanied this
|
||||||
|
# source. A copy of the CDDL is also available via the Internet at
|
||||||
|
# http://www.illumos.org/license/CDDL.
|
||||||
|
#
|
||||||
|
# CDDL HEADER END
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014, 2016 by Delphix. All rights reserved.
|
||||||
|
# Copyright (c) 2022 by Lawrence Livermore National Security, LLC.
|
||||||
|
#
|
||||||
|
|
||||||
|
. $STF_SUITE/include/libtest.shlib
|
||||||
|
. $STF_SUITE/tests/functional/no_space/enospc.cfg
|
||||||
|
|
||||||
|
#
|
||||||
|
# DESCRIPTION:
|
||||||
|
# After filling a filesystem, verify the contents can be removed
|
||||||
|
# without encountering an ENOSPC error.
|
||||||
|
#
|
||||||
|
|
||||||
|
verify_runnable "both"
|
||||||
|
|
||||||
|
function cleanup
|
||||||
|
{
|
||||||
|
destroy_pool $TESTPOOL
|
||||||
|
log_must rm -f $all_vdevs
|
||||||
|
}
|
||||||
|
|
||||||
|
log_onexit cleanup
|
||||||
|
|
||||||
|
log_assert "Files can be removed from full file system."
|
||||||
|
|
||||||
|
all_vdevs=$(echo $TEST_BASE_DIR/file.{01..12})
|
||||||
|
|
||||||
|
log_must truncate -s $MINVDEVSIZE $all_vdevs
|
||||||
|
|
||||||
|
log_must zpool create -f $TESTPOOL draid2:8d:2s $all_vdevs
|
||||||
|
log_must zfs create $TESTPOOL/$TESTFS
|
||||||
|
log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
|
||||||
|
log_must zfs set compression=off $TESTPOOL/$TESTFS
|
||||||
|
|
||||||
|
log_note "Writing files until ENOSPC."
|
||||||
|
log_mustnot_expect "No space left on device" fio --name=test \
|
||||||
|
--fallocate=none --rw=write --bs=1M --size=1G --numjobs=4 \
|
||||||
|
--sync=1 --directory=$TESTDIR/ --group_reporting
|
||||||
|
|
||||||
|
log_must rm $TESTDIR/test.*
|
||||||
|
log_must test -z "$(ls -A $TESTDIR)"
|
||||||
|
|
||||||
|
log_pass "All files removed without error"
|
Loading…
Reference in New Issue