-
Bug
-
Resolution: Unresolved
-
Undefined
-
None
-
rhel-9.2.0.z
-
None
-
Low
-
rhel-sst-filesystems
-
ssg_filesystems_storage_and_HA
-
2
-
QE ack
-
False
-
-
None
-
Red Hat Enterprise Linux
-
None
-
None
What were you trying to do that didn't work?
NFS now uses a heuristic to determine whether to get directory listings using READDIR or READDIRPLUS, depending on whether the userspace program has called stat() a specific number of times. The getdents() syscall returns the directory entry type (d_type) for each entry returned, but the entry's type is only known if READDIRPLUS is used, otherwise DT_UNKNOWN is returned.
This behavior means that programs which only need the entry name and entry type (but not the additional information provided by stat()) will no longer get the d_type, which will require them to start making stat() calls for each entry which did not return a known d_type. As a result, the program will experience a performance hit, now having to make stat() calls to obtain the type, when not required previously.
As shown below, this is not academic
Please provide the package NVR for which bug is seen:
RHEL 9 kernels
RHEL 8 kernels, beginning mid-RHEL8
upstream kernel
How reproducible:
easy
Steps to reproduce
- mount server:/export /mnt/tmp
- for i in {1..1000} ; do touch /mnt/tmp/file$i ; done
- sysctl vm.drop_caches=3
- strace -vo /tmp/trace.out -etrace=file,desc -s 10240 find /mnt/tmp -type f >/dev/null
- examine /tmp/trace.out for getdents() calls with DT_UNKNOWN d_type
Expected results
programs which require d_type information but do not call stat() on the entries themselves continue to get known d_type
Actual results
If stat() is not called at least 8 times on entries returned in the first getdents() call, the second and subsequent getdents() calls will begin returning entries with d_type of DT_UNKNOWN
from the strace:
getdents64(4, [{d_ino=137256816, d_off=10, d_reclen=24, d_type=DT_DIR, d_name="."}, {d_ino=71089655, d_off=12, d_reclen=24, d_type=DT_DIR, d_name=".."}, {d_ino=137256803, d_off=15, d_reclen=32, d_type=DT_REG, d_name="file1"}, {d_ino=137256822, d_off=18, d_reclen=32, d_type=DT_REG, d_name="file2"}, {d_ino=137256825, d_off=21, d_reclen=32, d_type=DT_REG, d_name="file3"}, {d_ino=137256827, d_off=24, d_reclen=32, d_type=DT_REG, d_name="file4"}, ... {d_ino=136763055, d_off=63, d_reclen=32, d_type=DT_REG, d_name="file17"}], 32768) = 592 getdents64(4, [{d_ino=136763056, d_off=66, d_reclen=32, d_type=DT_REG, d_name="file18"}, ... , {d_ino=136763062, d_off=84, d_reclen=32, d_type=DT_REG, d_name="file24"}, {d_ino=136763063, d_off=87, d_reclen=32, d_type=DT_REG, d_name="file25"}, {d_ino=136763064, d_off=90, d_reclen=32, d_type=DT_UNKNOWN, d_name="file26"}, {d_ino=136763065, d_off=93, d_reclen=32, d_type=DT_UNKNOWN, d_name="file27"}, ... ... newfstatat(5, "file26", {st_dev=makedev(0, 0x2a), st_ino=136763064, st_mode=S_IFREG|0644, st_nlink=1, st_uid=0, st_gid=0, st_blksize=262144, st_blocks=0, st_size=0, st_atime=1699392520 /* 2023-11-07T15:28:40.741965897-0600 */, st_atime_nsec=741965897, st_mtime=1699392520 /* 2023-11-07T15:28:40.741965897-0600 */, st_mtime_nsec=741965897, st_ctime=1699392520 /* 2023-11-07T15:28:40.741965897-0600 */, st_ctime_nsec=741965897}, AT_SYMLINK_NOFOLLOW) = 0 newfstatat(5, "file27", {st_dev=makedev(0, 0x2a), st_ino=136763065, st_mode=S_IFREG|0644, st_nlink=1, st_uid=0, st_gid=0, st_blksize=262144, st_blocks=0, st_size=0, st_atime=1699392520 /* 2023-11-07T15:28:40.798966528-0600 */, st_atime_nsec=798966528, st_mtime=1699392520 /* 2023-11-07T15:28:40.798966528-0600 */, st_mtime_nsec=798966528, st_ctime=1699392520 /* 2023-11-07T15:28:40.798966528-0600 */, st_ctime_nsec=798966528}, AT_SYMLINK_NOFOLLOW) = 0
(in other words, 'find -type' will only make stat() calls if d_type is DT_UNKNOWN)