Skip to content

Commit ca8a51b

Browse files
committed
btrfs: statfs: report zero available if metadata are exhausted
There is one ENOSPC case that's very confusing. There's Available greater than zero but no file operation succeds (besides removing files). This happens when the metadata are exhausted and there's no possibility to allocate another chunk. In this scenario it's normal that there's still some space in the data chunk and the calculation in df reflects that in the Avail value. To at least give some clue about the ENOSPC situation, let statfs report zero value in Avail, even if there's still data space available. Current: /dev/sdb1 4.0G 3.3G 719M 83% /mnt/test New: /dev/sdb1 4.0G 3.3G 0 100% /mnt/test We calculate the remaining metadata space minus global reserve. If this is (supposedly) smaller than zero, there's no space. But this does not hold in practice, the exhausted state happens where's still some positive delta. So we apply some guesswork and compare the delta to a 4M threshold. (Practically observed delta was 2M.) We probably cannot calculate the exact threshold value because this depends on the internal reservations requested by various operations, so some operations that consume a few metadata will succeed even if the Avail is zero. But this is better than the other way around. Signed-off-by: David Sterba <[email protected]>
1 parent 8546b57 commit ca8a51b

File tree

1 file changed

+24
-0
lines changed

1 file changed

+24
-0
lines changed

fs/btrfs/super.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1956,6 +1956,8 @@ static int btrfs_calc_avail_data_space(struct btrfs_root *root, u64 *free_bytes)
19561956
* there are other factors that may change the result (like a new metadata
19571957
* chunk).
19581958
*
1959+
* If metadata is exhausted, f_bavail will be 0.
1960+
*
19591961
* FIXME: not accurate for mixed block groups, total and free/used are ok,
19601962
* available appears slightly larger.
19611963
*/
@@ -1967,11 +1969,13 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
19671969
struct btrfs_space_info *found;
19681970
u64 total_used = 0;
19691971
u64 total_free_data = 0;
1972+
u64 total_free_meta = 0;
19701973
int bits = dentry->d_sb->s_blocksize_bits;
19711974
__be32 *fsid = (__be32 *)fs_info->fsid;
19721975
unsigned factor = 1;
19731976
struct btrfs_block_rsv *block_rsv = &fs_info->global_block_rsv;
19741977
int ret;
1978+
u64 thresh = 0;
19751979

19761980
/*
19771981
* holding chunk_muext to avoid allocating new chunks, holding
@@ -1997,6 +2001,8 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
19972001
}
19982002
}
19992003
}
2004+
if (found->flags & BTRFS_BLOCK_GROUP_METADATA)
2005+
total_free_meta += found->disk_total - found->disk_used;
20002006

20012007
total_used += found->disk_used;
20022008
}
@@ -2019,6 +2025,24 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
20192025
buf->f_bavail += div_u64(total_free_data, factor);
20202026
buf->f_bavail = buf->f_bavail >> bits;
20212027

2028+
/*
2029+
* We calculate the remaining metadata space minus global reserve. If
2030+
* this is (supposedly) smaller than zero, there's no space. But this
2031+
* does not hold in practice, the exhausted state happens where's still
2032+
* some positive delta. So we apply some guesswork and compare the
2033+
* delta to a 4M threshold. (Practically observed delta was ~2M.)
2034+
*
2035+
* We probably cannot calculate the exact threshold value because this
2036+
* depends on the internal reservations requested by various
2037+
* operations, so some operations that consume a few metadata will
2038+
* succeed even if the Avail is zero. But this is better than the other
2039+
* way around.
2040+
*/
2041+
thresh = 4 * 1024 * 1024;
2042+
2043+
if (total_free_meta - thresh < block_rsv->size)
2044+
buf->f_bavail = 0;
2045+
20222046
buf->f_type = BTRFS_SUPER_MAGIC;
20232047
buf->f_bsize = dentry->d_sb->s_blocksize;
20242048
buf->f_namelen = BTRFS_NAME_LEN;

0 commit comments

Comments
 (0)