]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
NFS: Add a mount option to make ENETUNREACH errors fatal
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Thu, 20 Mar 2025 15:45:06 +0000 (11:45 -0400)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Fri, 21 Mar 2025 16:44:09 +0000 (12:44 -0400)
If the NFS client was initially created in a container, and that
container is torn down, there is usually no possibity to go back and
destroy any NFS clients that are hung because their virtual network
devices have been unlinked.

Add a flag that tells the NFS client that in these circumstances, it
should treat ENETDOWN and ENETUNREACH errors as fatal to the NFS client.

The option defaults to being on when the mount happens from inside a net
namespace that is not "init_net".

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Tested-by: Jeff Layton <jlayton@kernel.org>
Acked-by: Chuck Lever <chuck.lever@oracle.com>
fs/nfs/fs_context.c
fs/nfs/super.c
include/linux/nfs_fs_sb.h

index 1cabba1231d678a725249fedf4ed70b3c48bb904..13f71ca8c974d54bb378a13ed3a12d3eafe80f77 100644 (file)
@@ -50,6 +50,7 @@ enum nfs_param {
        Opt_clientaddr,
        Opt_cto,
        Opt_alignwrite,
+       Opt_fatal_neterrors,
        Opt_fg,
        Opt_fscache,
        Opt_fscache_flag,
@@ -97,6 +98,20 @@ enum nfs_param {
        Opt_xprtsec,
 };
 
+enum {
+       Opt_fatal_neterrors_default,
+       Opt_fatal_neterrors_enetunreach,
+       Opt_fatal_neterrors_none,
+};
+
+static const struct constant_table nfs_param_enums_fatal_neterrors[] = {
+       { "default",                    Opt_fatal_neterrors_default },
+       { "ENETDOWN:ENETUNREACH",       Opt_fatal_neterrors_enetunreach },
+       { "ENETUNREACH:ENETDOWN",       Opt_fatal_neterrors_enetunreach },
+       { "none",                       Opt_fatal_neterrors_none },
+       {}
+};
+
 enum {
        Opt_local_lock_all,
        Opt_local_lock_flock,
@@ -153,6 +168,8 @@ static const struct fs_parameter_spec nfs_fs_parameters[] = {
        fsparam_string("clientaddr",    Opt_clientaddr),
        fsparam_flag_no("cto",          Opt_cto),
        fsparam_flag_no("alignwrite",   Opt_alignwrite),
+       fsparam_enum("fatal_neterrors", Opt_fatal_neterrors,
+                    nfs_param_enums_fatal_neterrors),
        fsparam_flag  ("fg",            Opt_fg),
        fsparam_flag_no("fsc",          Opt_fscache_flag),
        fsparam_string("fsc",           Opt_fscache),
@@ -896,6 +913,25 @@ static int nfs_fs_context_parse_param(struct fs_context *fc,
                        goto out_of_bounds;
                ctx->nfs_server.max_connect = result.uint_32;
                break;
+       case Opt_fatal_neterrors:
+               trace_nfs_mount_assign(param->key, param->string);
+               switch (result.uint_32) {
+               case Opt_fatal_neterrors_default:
+                       if (fc->net_ns != &init_net)
+                               ctx->flags |= NFS_MOUNT_NETUNREACH_FATAL;
+                       else
+                               ctx->flags &= ~NFS_MOUNT_NETUNREACH_FATAL;
+                       break;
+               case Opt_fatal_neterrors_enetunreach:
+                       ctx->flags |= NFS_MOUNT_NETUNREACH_FATAL;
+                       break;
+               case Opt_fatal_neterrors_none:
+                       ctx->flags &= ~NFS_MOUNT_NETUNREACH_FATAL;
+                       break;
+               default:
+                       goto out_invalid_value;
+               }
+               break;
        case Opt_lookupcache:
                trace_nfs_mount_assign(param->key, param->string);
                switch (result.uint_32) {
@@ -1675,6 +1711,9 @@ static int nfs_init_fs_context(struct fs_context *fc)
                ctx->xprtsec.cert_serial        = TLS_NO_CERT;
                ctx->xprtsec.privkey_serial     = TLS_NO_PRIVKEY;
 
+               if (fc->net_ns != &init_net)
+                       ctx->flags |= NFS_MOUNT_NETUNREACH_FATAL;
+
                fc->s_iflags            |= SB_I_STABLE_WRITES;
        }
        fc->fs_private = ctx;
index 96de658a78868d76b24b3418054098628337f61f..9eea9e62afc9c3a62b73f9b5a5ad2d4722543058 100644 (file)
@@ -457,6 +457,9 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
                { NFS_MOUNT_FORCE_RDIRPLUS, ",rdirplus=force", "" },
                { NFS_MOUNT_UNSHARED, ",nosharecache", "" },
                { NFS_MOUNT_NORESVPORT, ",noresvport", "" },
+               { NFS_MOUNT_NETUNREACH_FATAL,
+                 ",fatal_neterrors=ENETDOWN:ENETUNREACH",
+                 ",fatal_neterrors=none" },
                { 0, NULL, NULL }
        };
        const struct proc_nfs_info *nfs_infop;
index b83d16a42afc58d973af36875e13fd1b2e150c82..a6ce8590eaaffa68742b1991f10b8c0446723134 100644 (file)
@@ -168,6 +168,7 @@ struct nfs_server {
 #define NFS_MOUNT_SHUTDOWN                     0x08000000
 #define NFS_MOUNT_NO_ALIGNWRITE                0x10000000
 #define NFS_MOUNT_FORCE_RDIRPLUS       0x20000000
+#define NFS_MOUNT_NETUNREACH_FATAL     0x40000000
 
        unsigned int            fattr_valid;    /* Valid attributes */
        unsigned int            caps;           /* server capabilities */