There were two problems:
1) au_add_till_max returns ULLONG_MAX if b==0
Reason: in this case, a is not bigger then old but equal. This is the reason for the very big values which confuse df.
2) If aufs is mounted in sum mode, but the source file systems have different block sizes, the result of the statfs might not be useable.
In my case, I have a squashfs system as root with data but no free space (read only file system) and a tmpfs. Both have very different block sizes. This could even result in f_bfree>f_blocks.
=============
PLEASE REVIEW MY PATCH CAREFULLY.
It is my first kernel-code work. I think the changes in au_statfs_sum are quite
good, but au_add_muldiv_till_max is just a hack. I did not know how to handle an
overflow in the multiplication, and there an overflow is much more likely than
in the addition. Maybe bit-shifting is the way-to-go, but then you need to count
bits in bsize (mul and div), which is much more complicated, and you have to be
ready to shift in both ways.
Patch is against aufs2.2-38 branch in aufs2-standalone, but I also checked aufs3
tree and found the same code and problems problems there.
Daniel Alder, daniel-alder@gmx.net
=============
Example:
/dev/loop0 17152 17152 0 100% /root/aufstest/_L1_root (squashfs; block size 131072)
tmp 253488 16 253472 1% /root/aufstest/_L2_tmp (tmpfs, block size 4096)
mount -t aufs -o dirs=/root/aufstest/_L2_tmp=rw:/root/aufstest/_L1_root=ro,sum none _aufs
Result (without patch, something like):
none - - - ?% /root/aufstest/_aufs
Result (with patch):
none 270640 17168 253472 7% /root/aufstest/_aufs
output of "strace df -h _L1_root/ _L2_tmp/ _aufs 2>&1 | grep statfs":
before:
statfs64("_L1_root/", 84, {f_type=0x73717368, f_bsize=131072, f_blocks=134, f_bfree=0, f_bavail=0, f_files=13, f_ffree=0, f_fsid={1792, 0}, f_namelen=256, f_frsize=131072}) = 0
statfs64("_L2_tmp/", 84, {f_type=0x1021994, f_bsize=4096, f_blocks=63372, f_bfree=63368, f_bavail=63368, f_files=63372, f_ffree=63364, f_fsid={0, 0}, f_namelen=255, f_frsize=4096}) = 0
statfs64("_aufs", 84, {f_type=0x61756673, f_bsize=4096, f_blocks=63506, f_bfree=18446744073709551615, f_bavail=18446744073709551615, f_files=63385, f_ffree=18446744073709551615, f_fsid={0, 0}, f_namelen=242, f_frsize=4096}) = 0
after:
statfs64("_L1_root/", 84, {f_type=0x73717368, f_bsize=131072, f_blocks=134, f_bfree=0, f_bavail=0, f_files=13, f_ffree=0, f_fsid={1792, 0}, f_namelen=256, f_frsize=131072}) = 0
statfs64("_L2_tmp/", 84, {f_type=0x1021994, f_bsize=4096, f_blocks=63372, f_bfree=63368, f_bavail=63368, f_files=63372, f_ffree=63364, f_fsid={0, 0}, f_namelen=255, f_frsize=4096}) = 0
statfs64("_aufs", 84, {f_type=0x61756673, f_bsize=4096, f_blocks=67660, f_bfree=63368, f_bavail=63368, f_files=63385, f_ffree=63364, f_fsid={0, 0}, f_namelen=242, f_frsize=4096}) = 0
My testcase (Makefile, to use in a new empty directory):
--------
all: 5stattest.stamp
fullclean: clean
$(RM) 1data/
$(RM) 2data.squashfs *.stamp
clean:
$(RM) 3mount.stamp 4dftest.stamp 5stattest.stamp
-umount ./_aufs
-umount ./_L1_root
-umount ./_L2_tmp
-rmdir _L1_root _L2_tmp _aufs
test ! -d _L1_root
test ! -d _L2_tmp
test ! -d _aufs
@echo "successful."
1data.stamp:
mkdir 1data/
dd if=/dev/urandom of=1data/a bs=512 count=1334
dd if=/dev/urandom of=1data/b bs=512 count=112
dd if=/dev/urandom of=1data/c bs=512 count=11322
dd if=/dev/urandom of=1data/d bs=512 count=4322
touch 1data.stamp
2data.squashfs: 1data.stamp
mksquashfs 1data/ 2data.squashfs
3mount.stamp: 2data.squashfs
mkdir _L1_root
mount -oloop 2data.squashfs _L1_root
mkdir _L2_tmp
mount -t tmpfs tmp _L2_tmp
mkdir _aufs
mount -t aufs -o dirs=$(PWD)/_L2_tmp=rw:$(PWD)/_L1_root=ro,sum none _aufs
touch 3mount.stamp
4dftest.stamp: 3mount.stamp
df -h _aufs
echo test >_aufs/abc
df -h _aufs
touch 4dftest.stamp
5stattest.stamp: 4dftest.stamp
stat -c"%-14n %o %b %B" _L1_root _L2_tmp _aufs
strace df -h _L1_root/ _L2_tmp/ _aufs 2>&1 | grep statfs
touch 5stattest.stamp
-------
Corrects handling of different block sizes when using statfs in sum mode