nfsd: Fix overflow causing non-working mounts on 1 TB machines [Linux 4.4.187]

This Linux kernel change "nfsd: Fix overflow causing non-working mounts on 1 TB machines" is included in the Linux 4.4.187 release. This change is authored by Paul Menzel <pmenzel [at] molgen.mpg.de> on Wed Jul 3 13:28:15 2019 +0200. The commit for this change in Linux stable tree is faf9d27 (patch) which is from upstream commit 3b2d4dc. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 3b2d4dc.

nfsd: Fix overflow causing non-working mounts on 1 TB machines

[ Upstream commit 3b2d4dcf71c4a91b420f835e52ddea8192300a3b ]

Since commit 10a68cdf10 (nfsd: fix performance-limiting session
calculation) (Linux 5.1-rc1 and 4.19.31), shares from NFS servers with
1 TB of memory cannot be mounted anymore. The mount just hangs on the
client.

The gist of commit 10a68cdf10 is the change below.

    -avail = clamp_t(int, avail, slotsize, avail/3);
    +avail = clamp_t(int, avail, slotsize, total_avail/3);

Here are the macros.

    #define min_t(type, x, y)       __careful_cmp((type)(x), (type)(y), <)
    #define clamp_t(type, val, lo, hi) min_t(type, max_t(type, val, lo), hi)

`total_avail` is 8,434,659,328 on the 1 TB machine. `clamp_t()` casts
the values to `int`, which for 32-bit integers can only hold values
−2,147,483,648 (−2^31) through 2,147,483,647 (2^31 − 1).

`avail` (in the function signature) is just 65536, so that no overflow
was happening. Before the commit the assignment would result in 21845,
and `num = 4`.

When using `total_avail`, it is causing the assignment to be
18446744072226137429 (printed as %lu), and `num` is then 4164608182.

My next guess is, that `nfsd_drc_mem_used` is then exceeded, and the
server thinks there is no memory available any more for this client.

Updating the arguments of `clamp_t()` and `min_t()` to `unsigned long`
fixes the issue.

Now, `avail = 65536` (before commit 10a68cdf10 `avail = 21845`), but
`num = 4` remains the same.

Fixes: c54f24e338ed (nfsd: fix performance-limiting session calculation)
Cc: stable@vger.kernel.org
Signed-off-by: Paul Menzel <pmenzel@molgen.mpg.de>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>

There are 2 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 fs/nfsd/nfs4state.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 1e1abf1..ea5cb1b 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1400,7 +1400,7 @@ static u32 nfsd4_get_drc_mem(struct nfsd4_channel_attrs *ca)
     * Never use more than a third of the remaining memory,
     * unless it's the only way to give this client a slot:
     */
-   avail = clamp_t(int, avail, slotsize, total_avail/3);
+   avail = clamp_t(unsigned long, avail, slotsize, total_avail/3);
    num = min_t(int, num, avail / slotsize);
    nfsd_drc_mem_used += num * slotsize;
    spin_unlock(&nfsd_drc_lock);

Leave a Reply

Your email address will not be published. Required fields are marked *