RHEL Engineering is moving the tracking of its product development work on RHEL 6 through RHEL 9 to Red Hat Jira (issues.redhat.com). If you're a Red Hat customer, please continue to file support cases via the Red Hat customer portal. If you're not, please head to the "RHEL project" in Red Hat Jira and file new tickets here. Individual Bugzilla bugs in the statuses "NEW", "ASSIGNED", and "POST" are being migrated throughout September 2023. Bugs of Red Hat partners with an assigned Engineering Partner Manager (EPM) are migrated in late September as per pre-agreed dates. Bugs against components "kernel", "kernel-rt", and "kpatch" are only migrated if still in "NEW" or "ASSIGNED". If you cannot log in to RH Jira, please consult article #7032570. That failing, please send an e-mail to the RH Jira admins at rh-issues@redhat.com to troubleshoot your issue as a user management inquiry. The email creates a ServiceNow ticket with Red Hat. Individual Bugzilla bugs that are migrated will be moved to status "CLOSED", resolution "MIGRATED", and set with "MigratedToJIRA" in "Keywords". The link to the successor Jira issue will be found under "Links", have a little "two-footprint" icon next to it, and direct you to the "RHEL project" in Red Hat Jira (issue links are of type "https://issues.redhat.com/browse/RHEL-XXXX", where "X" is a digit). This same link will be available in a blue banner at the top of the page informing you that that bug has been migrated.
Bug 1962515 - df: duplicates listed for remote filesystems (NFS) with same fsid/device without "-a"
Summary: df: duplicates listed for remote filesystems (NFS) with same fsid/device with...
Keywords:
Status: CLOSED ERRATA
Alias: None
Product: Red Hat Enterprise Linux 8
Classification: Red Hat
Component: coreutils
Version: 8.3
Hardware: All
OS: Linux
unspecified
medium
Target Milestone: beta
: ---
Assignee: Kamil Dudka
QA Contact: Vojtech Eichler
URL:
Whiteboard:
Depends On: 1979814
Blocks:
TreeView+ depends on / blocked
 
Reported: 2021-05-20 08:51 UTC by Roberto Bergantinos
Modified: 2024-12-20 20:05 UTC (History)
4 users (show)

Fixed In Version: coreutils-8.30-12.el8
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
: 1979814 (view as bug list)
Environment:
Last Closed: 2021-11-09 19:42:17 UTC
Type: Bug
Target Upstream Version:
Embargoed:
pm-rhel: mirror+


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Red Hat Knowledge Base (Solution) 6989059 0 None None None 2022-12-05 13:38:34 UTC
Red Hat Product Errata RHBA-2021:4418 0 None None None 2021-11-09 19:42:23 UTC

Description Roberto Bergantinos 2021-05-20 08:51:50 UTC
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:

Comment 1 Kamil Dudka 2021-06-09 15:37:12 UTC
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.  */

Comment 2 Roberto Bergantinos 2021-06-09 15:51:02 UTC
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

Comment 3 Kamil Dudka 2021-06-10 06:34:22 UTC
(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.

Comment 4 Roberto Bergantinos 2021-06-24 10:38:45 UTC
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

Comment 5 Kamil Dudka 2021-06-24 16:15:51 UTC
Thank you for testing it!  I will propose the change upstream.

Comment 6 Kamil Dudka 2021-06-30 15:55:50 UTC
patch proposed upstream: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=49298

Comment 12 errata-xmlrpc 2021-11-09 19:42:17 UTC
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


Note You need to log in before you can comment on or make changes to this bug.