Bug 1962515
| Summary: | df: duplicates listed for remote filesystems (NFS) with same fsid/device without "-a" | |||
|---|---|---|---|---|
| Product: | Red Hat Enterprise Linux 8 | Reporter: | Roberto Bergantinos <rbergant> | |
| Component: | coreutils | Assignee: | Kamil Dudka <kdudka> | |
| Status: | CLOSED ERRATA | QA Contact: | Vojtech Eichler <veichler> | |
| Severity: | medium | Docs Contact: | ||
| Priority: | unspecified | |||
| Version: | 8.3 | CC: | dostwal, fsumsal, kdudka, veichler | |
| Target Milestone: | beta | Keywords: | Triaged | |
| Target Release: | --- | Flags: | pm-rhel:
mirror+
|
|
| Hardware: | All | |||
| OS: | Linux | |||
| Whiteboard: | ||||
| Fixed In Version: | coreutils-8.30-12.el8 | Doc Type: | If docs needed, set a value | |
| Doc Text: | Story Points: | --- | ||
| Clone Of: | ||||
| : | 1979814 (view as bug list) | Environment: | ||
| Last Closed: | 2021-11-09 19:42:17 UTC | Type: | Bug | |
| Regression: | --- | Mount Type: | --- | |
| Documentation: | --- | CRM: | ||
| Verified Versions: | Category: | --- | ||
| oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | ||
| Cloudforms Team: | --- | Target Upstream Version: | ||
| Embargoed: | ||||
| Bug Depends On: | 1979814 | |||
| Bug Blocks: | ||||
Thank you for the thorough analysis! You are right about the cause of the different output of df on RHEL-8 onwards. My only concern is about the performance penalty of the duplicated insertion into the hash table. It could yield worse performance in some cases, thus invalidate the original motivation for introducing the hash table. Comparing device names in the hash table could also decrease the performance because it runs quite frequently (and comparing strings is more expensive than comparing integers).
I wonder if we could fix it like this to achieve the same behavior with lower performance penalty?
--- a/src/df.c
+++ b/src/df.c
@@ -51,12 +51,13 @@
struct devlist
{
dev_t dev_num;
struct mount_entry *me;
struct devlist *next;
+ struct devlist *seen_last; /* valid for hashed devlist entries only */
};
/* Filled with device numbers of examined file systems to avoid
duplicates in output. */
static Hash_table *devlist_table;
@@ -678,13 +679,19 @@ static struct devlist *
devlist_for_dev (dev_t dev)
{
if (devlist_table == NULL)
return NULL;
struct devlist dev_entry;
dev_entry.dev_num = dev;
- return hash_lookup (devlist_table, &dev_entry);
+
+ struct devlist *found = hash_lookup (devlist_table, &dev_entry);
+ if (found == NULL)
+ return NULL;
+
+ /* Return the last devlist entry we have seen with this dev_num */
+ return found->seen_last;
}
static void
devlist_free (void *p)
{
free (p);
@@ -796,14 +803,18 @@ filter_mount_list (bool devices_only)
/* Add the device number to the device_table. */
struct devlist *devlist = xmalloc (sizeof *devlist);
devlist->me = me;
devlist->dev_num = buf.st_dev;
devlist->next = device_list;
device_list = devlist;
- if (hash_insert (devlist_table, devlist) == NULL)
+
+ struct devlist *inserted = hash_insert (devlist_table, devlist);
+ if (inserted == NULL)
xalloc_die ();
+ /* Remember the last devlist entry we have seen with this dev_num */
+ inserted->seen_last = devlist;
me = me->me_next;
}
}
/* Finally rebuild the mount_list from the devlist. */
hi Kamil ! (In reply to Kamil Dudka from comment #1) > Thank you for the thorough analysis! You are right about the cause of the > different output of df on RHEL-8 onwards. My only concern is about the > performance penalty of the duplicated insertion into the hash table. It > could yield worse performance in some cases, thus invalidate the original > motivation for introducing the hash table. Comparing device names in the > hash table could also decrease the performance because it runs quite > frequently (and comparing strings is more expensive than comparing integers). > > I wonder if we could fix it like this to achieve the same behavior with > lower performance penalty? Yes, my fix indeed incurr on a perfomance penalty for same dev case, i guess more potentially possible for network filesystems, for the rest of cases we still have the benefit of hash table > > --- a/src/df.c > +++ b/src/df.c > @@ -51,12 +51,13 @@ > > struct devlist > { > dev_t dev_num; > struct mount_entry *me; > struct devlist *next; > + struct devlist *seen_last; /* valid for hashed devlist entries only */ > }; > > /* Filled with device numbers of examined file systems to avoid > duplicates in output. */ > static Hash_table *devlist_table; > > @@ -678,13 +679,19 @@ static struct devlist * > devlist_for_dev (dev_t dev) > { > if (devlist_table == NULL) > return NULL; > struct devlist dev_entry; > dev_entry.dev_num = dev; > - return hash_lookup (devlist_table, &dev_entry); > + > + struct devlist *found = hash_lookup (devlist_table, &dev_entry); > + if (found == NULL) > + return NULL; > + > + /* Return the last devlist entry we have seen with this dev_num */ > + return found->seen_last; > } > > static void > devlist_free (void *p) > { > free (p); > @@ -796,14 +803,18 @@ filter_mount_list (bool devices_only) > /* Add the device number to the device_table. */ > struct devlist *devlist = xmalloc (sizeof *devlist); > devlist->me = me; > devlist->dev_num = buf.st_dev; > devlist->next = device_list; > device_list = devlist; > - if (hash_insert (devlist_table, devlist) == NULL) > + > + struct devlist *inserted = hash_insert (devlist_table, devlist); > + if (inserted == NULL) > xalloc_die (); > + /* Remember the last devlist entry we have seen with this dev_num > */ > + inserted->seen_last = devlist; > > me = me->me_next; > } > } > > /* Finally rebuild the mount_list from the devlist. */ Nice! i'll take a look and try it tomorrow. Thanks for taking a look ! rgds roberto (In reply to Roberto Bergantinos from comment #2) > Yes, my fix indeed incurr on a perfomance penalty for same dev case, i guess > more potentially possible > for network filesystems, for the rest of cases we still have the benefit of > hash table I totally agree. The performance penalty could only be observable in some special cases. On the other hand, the hash table is there to cover special cases. For 99% of common use cases, the linked list would be sufficient. > Nice! i'll take a look and try it tomorrow. Thanks for taking a look ! Thanks! And sorry that it took me so long to have a closer look at this. Hi Kamil Sorry for delay this fell off my radar. Your patch works in my testing, i build from upstream version : [root@rhel8 coreutils]# src/df -t nfs4 Filesystem 1K-blocks Used Available Use% Mounted on rhel73:/share 8374272 6010880 2363392 72% /testdir/share1 rhel73:/workvol 8374272 6010880 2363392 72% /testdir/share2 [root@rhel8 coreutils]# df -t nfs4 Filesystem 1K-blocks Used Available Use% Mounted on rhel73:/share 8374272 6010880 2363392 72% /testdir/share1 rhel73:/workvol 8374272 6010880 2363392 72% /testdir/share2 rhel73:/workvol 8374272 6010880 2363392 72% /mnt/testdir/share2 had some warnings building but possibly my build machine is not the best. thanks ! rgds roberto Thank you for testing it! I will propose the change upstream. patch proposed upstream: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=49298 upstream commit: https://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=commitdiff;h=v8.32-169-gd6125af095c Since the problem described in this bug report should be resolved in a recent advisory, it has been closed with a resolution of ERRATA. For information on the advisory (coreutils bug fix and enhancement update), and where to find the updated files, follow the link below. If the solution does not work for you, open a new bug report. https://access.redhat.com/errata/RHBA-2021:4418 |
Description of problem: Without -a, duplicates listed for remote filesystems under same device (same exported fsid), test case for NFS : ## RHEL7 : [root@rhel77 ~]# rpm -q coreutils coreutils-8.22-24.el7.x86_64 [root@rhel77 ~]# mkdir -p /testdir ; mkdir -p /mnt/testdir [root@rhel77 ~]# mkdir -p /mnt/testdir/share1 ; mkdir -p /mnt/testdir/share2 [root@rhel77 ~]# mount -o bind /mnt/testdir /testdir [root@rhel77 ~]# mount -o vers=4.1 rhel73:/share /testdir/share1 [root@rhel77 ~]# mount -o vers=4.1 rhel73:/workvol /testdir/share2 [root@rhel77 ~]# df -h -t nfs4 Filesystem Size Used Avail Use% Mounted on rhel73:/share 8.0G 5.4G 2.7G 67% /testdir/share1 rhel73:/workvol 8.0G 5.4G 2.7G 67% /testdir/share2 [root@rhel77 ~]# cat /proc/fs/nfsfs/volumes NV SERVER PORT DEV FSID FSC v4 ac1701e1 801 0:41 628fdfac389143b8:b4ef233ffd082203 no [root@rhel77 ~]# grep "0:41" /proc/self/mountinfo 88 84 0:41 / /testdir/share1 rw,relatime shared:67 - nfs4 ... 91 39 0:41 / /mnt/testdir/share1 rw,relatime shared:67 - nfs4 ... 89 84 0:41 / /testdir/share2 rw,relatime shared:72 - nfs4 ... 95 39 0:41 / /mnt/testdir/share2 rw,relatime shared:72 - ... ## RHEL8 : [root@rhel8 ~]# rpm -q coreutils coreutils-8.30-8.el8.x86_64 [root@rhel8 ~]# mkdir -p /testdir ; mkdir -p /mnt/testdir [root@rhel8 ~]# mkdir -p /mnt/testdir/share1 ; mkdir -p /mnt/testdir/share2 [root@rhel8 ~]# mount -o bind /mnt/testdir /testdir [root@rhel8 ~]# mount -o vers=4.1 rhel73:/share /testdir/share1 [root@rhel8 ~]# mount -o vers=4.1 rhel73:/workvol /testdir/share2 [root@rhel8 ~]# df -h -t nfs4 Filesystem Size Used Avail Use% Mounted on rhel73:/share 8.0G 5.4G 2.7G 67% /testdir/share1 rhel73:/workvol 8.0G 5.4G 2.7G 67% /testdir/share2 rhel73:/workvol 8.0G 5.4G 2.7G 67% /mnt/testdir/share2 <<<--- dupe [root@rhel8 coreutils]# cat /proc/fs/nfsfs/volumes NV SERVER PORT DEV FSID FSC v4 ac1701e1 801 0:48 628fdfac389143b8:b4ef233ffd082203 no [root@rhel8 coreutils]# grep "0:48" /proc/self/mountinfo 335 326 0:48 / /testdir/share1 rw,relatime shared:177 - nfs4 ... 338 92 0:48 / /mnt/testdir/share1 rw,relatime shared:177 - nfs4 ... 336 326 0:48 / /testdir/share2 rw,relatime shared:186 - nfs4 ... 356 92 0:48 / /mnt/testdir/share2 rw,relatime shared:186 - nfs4 ... This is based on a concrete field setup including the bind mount which sort of replicate mount tree. Difference in behaviour between rhel7 and rhel8 is due to inclusion of this upstream commit : 1c17f61ef df: improve performance with many mount points Which essentially uses a hash table on filter_mount_list() instead of a linked list. As a result, when only introduce one entry on the hash table for first filesystem with same device and we compare "devname" to that, that would be "seen_dev", when using linked list we simply compare with the latest element added to the list "device_list", that's why, for rhel7, and in this example no dupes are listed, since entries are consecutive. I've tried this patch to make the behaviour rhel7-like for rhel8, resinserting on the table latest element found if already present : diff --git a/src/df.c b/src/df.c index 42cfeed2c..d011c6088 100644 --- a/src/df.c +++ b/src/df.c @@ -795,12 +795,28 @@ filter_mount_list (bool devices_only) { /* Add the device number to the device_table. */ struct devlist *devlist = xmalloc (sizeof *devlist); + union { + const void *ptr; + struct devlist *my_dev; + } entry; + devlist->me = me; devlist->dev_num = buf.st_dev; devlist->next = device_list; device_list = devlist; - if (hash_insert (devlist_table, devlist) == NULL) - xalloc_die (); + switch (hash_insert_if_absent (devlist_table, devlist, &entry.ptr)) + { + case -1: + xalloc_die (); + break; + case 0: + if ((hash_remove(devlist_table, entry.my_dev) == NULL) || + (hash_insert (devlist_table, devlist) == NULL)) + xalloc_die (); + break; + default: + break; + } me = me->me_next; } Seems to work : [root@rhel8 coreutils]# /tmp/df -h -t nfs4 <<------- df with above patch Filesystem Size Used Avail Use% Mounted on rhel73:/share 8.0G 5.4G 2.7G 67% /testdir/share1 rhel73:/workvol 8.0G 5.4G 2.7G 67% /testdir/share2 [root@rhel8 coreutils]# df -h -t nfs4 Filesystem Size Used Avail Use% Mounted on rhel73:/share 8.0G 5.4G 2.7G 67% /testdir/share1 rhel73:/workvol 8.0G 5.4G 2.7G 67% /testdir/share2 rhel73:/workvol 8.0G 5.4G 2.7G 67% /mnt/testdir/share2 But maybe it should be considered a more general case and modify devlist_compare and/or devlist_hash to consider devname for hashing comparing purposes Version-Release number of selected component (if applicable): 8.3, coreutils-8.30-8.el8.x86_64 How reproducible: 100% Steps to Reproduce: see above 1. 2. 3. Actual results: Expected results: Additional info: