diff --git a/auto-rules-io/usr/bin/big-btrfs-balance b/auto-rules-io/usr/bin/big-btrfs-balance index 08b8280..a3ce899 100755 --- a/auto-rules-io/usr/bin/big-btrfs-balance +++ b/auto-rules-io/usr/bin/big-btrfs-balance @@ -7,12 +7,45 @@ FolderRoot=$(btrfs device stats / | tail -n1 | cut -f1 -d] | cut -f2 -d[) FolderHome=$(btrfs device stats /home | tail -n1 | cut -f1 -d] | cut -f2 -d[) +# A usage filter also picks the last, partly filled chunk that the previous +# balance just packed, so a filesystem that changed little since then has the +# same data rewritten on every boot and frees nothing. Freeing a chunk needs +# at least a chunk of allocated-but-unused space. With single, DUP or RAID1 +# profiles, data chunks are at most 1 GiB, and the kernel reports the +# metadata chunk size, which is at least 256 MiB (its data chunk size there +# is only the upper limit). On smaller filesystems both are 10% of the size, +# rounded up to 16 MiB. +balance() { + local type slack size tenth data_chunk=1073741824 meta_chunk filters=() + meta_chunk=$(cat "/sys/fs/btrfs/$(findmnt -no UUID "$1")/allocation/metadata/chunk_size" 2>/dev/null) + meta_chunk=${meta_chunk:-268435456} + size=$(btrfs filesystem usage -b "$1" 2>/dev/null | awk '/Device size:/ {print $3; exit}') + if (( size )); then + tenth=$(( (size / 10 + 16777215) / 16777216 * 16777216 )) + (( tenth < data_chunk )) && data_chunk=$tenth + (( tenth < meta_chunk )) && meta_chunk=$tenth + fi + while read -r type slack; do + case $type in + Data) (( slack >= data_chunk )) && filters+=(-dusage=50) ;; + Metadata) (( slack >= meta_chunk )) && filters+=(-musage=50) ;; + Data+Metadata) filters+=(-dusage=50 -musage=50) ;; + esac + done < <(btrfs filesystem df -b "$1" | awk -F'[ ,=]+' '$1 == "Data" || $1 == "Metadata" || $1 == "Data+Metadata" { + for (i = 2; i < NF; i++) { if ($i == "total") t[$1] += $(i+1); if ($i == "used") u[$1] += $(i+1) } + } END { for (k in t) print k, t[k] - u[k] }') + + if (( ${#filters[@]} )); then + nice -n+20 ionice -c 3 btrfs balance start "${filters[@]}" "$1" + fi +} + if [ "$FolderRoot" != "" ]; then - nice -n+20 ionice -c 3 btrfs balance start -dusage=50 -musage=50 / + balance / fi if [ "$FolderHome" != "" ] && [ "$FolderHome" != "$FolderRoot" ]; then - nice -n+20 ionice -c 3 btrfs balance start -dusage=50 -musage=50 /home + balance /home fi