Bug 253424

Summary: [fusefs]: statfs doesn't set f_iosize ("optimal transfer block size")
Product: Base System Reporter: John Millikin <jmillikin>
Component: kernAssignee: Alan Somers <asomers>
Status: In Progress ---    
Severity: Affects Only Me CC: asomers, chris
Priority: ---    
Version: 12.2-RELEASE   
Hardware: Any   
OS: Any   
URL: https://reviews.freebsd.org/D28607

Description John Millikin 2021-02-11 01:21:11 UTC
The FUSE_STATFS response includes fields `bsize` and `frsize`, matching the Linux statfs fields of the same names:

* f_bsize: optimal transfer block size
* f_frsize: fragment size

The FreeBSD `struct statfs' equivalent fields are:

* f_bsize: filesystem fragment size
* f_iosize: optimal transfer block size

The fusefs driver function `fuse_vfsop_statfs()' copies the fragment size correctly, but doesn't copy the transfer block size. This causes `statfs()' calls on a FUSE filesystem to return an incorrect `(struct statfs*)->f_iosize' value of 65535, instead of whatever the filesystem server returned.

Additionally, during testing, I noticed that the wrapper implementation of `statvfs()' doesn't propagate `(struct statfs*)->f_namemax' -- it calls `pathconf(path, _PC_NAME_MAX)' instead, which doesn't return correct values for FUSE filesystems.
Comment 1 Alan Somers freebsd_committer 2021-02-11 02:07:05 UTC
I think you're right about f_iosize.  But what is this about "the wrapper implementation of statvfs"?  What wrapper are you talking about?
Comment 2 John Millikin 2021-02-11 02:25:26 UTC
In libc, the `lib/libc/gen/statvfs.c' file contains an implementation of `statvfs()' that wraps the `statfs()' syscall. This wrapper copies most of the fields, but does not copy the maximum name length -- for that, it uses:

  pcval = pathconf(path, _PC_NAME_MAX);
  if (pcval == -1)
  	result->f_namemax = ~0UL;
  	result->f_namemax = (unsigned long)pcval;
Comment 3 Alan Somers freebsd_committer 2021-02-11 02:51:11 UTC
(In reply to John Millikin from comment #2)
Oh, I thought you were talking about something that wraps statvfs.  I wouldn't call statvfs a wrapper; it's the standardized interface for getting information about a file system.  statfs is the older, but non-portable version.  And the reason that statvfs uses pathconf is because when it was written (2002) statfs didn't have an f_namemax field.  f_namemax was only added in 2003 when statfs(2) was updated for 64-bit block counts.
Comment 4 Alan Somers freebsd_committer 2021-02-12 03:42:01 UTC
John, could you please test the patch under review?
Comment 5 John Millikin 2021-02-12 11:09:06 UTC
(In reply to Alan Somers from comment #4)

The special-casing of `f_iosize' based on `fsess_opt_datacache()' is confusing to me. Is it important that `(struct statfs*)->f_iosize' always be >= the buffer cache iosize? I would expect `statfs()' to report the IO size exactly as provided by the filesystem, without varying depending on presence of a kernel cache.

Consider the case of a file opened with `FOPEN_DIRECT_IO' on a filesystem with a small write size (like a network fs limited by MTU). Trying to get the IO size with `statfs()' will return a much larger number than the filesystem daemon reported, and since the statfs vnop is handled by the mount it's not possible for a client to skip the cache check by using `fstatfs()' and `IO_DIRECT'.