NFS: Treat ENETUNREACH errors as fatal in containers
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Thu, 20 Mar 2025 14:50:26 +0000 (10:50 -0400)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Fri, 21 Mar 2025 16:44:19 +0000 (12:44 -0400)
Propagate the NFS_MOUNT_NETUNREACH_FATAL flag to work with the generic
NFS client. If the flag is set, the client will receive ENETDOWN and
ENETUNREACH errors from the RPC layer, and is expected to treat them as
being fatal.

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/client.c
fs/nfs/nfs4client.c
fs/nfs/nfs4proc.c
include/linux/nfs_fs_sb.h
include/linux/sunrpc/clnt.h
include/linux/sunrpc/sched.h
include/trace/events/sunrpc.h
net/sunrpc/clnt.c

index 3b0918ade53cd331d76baaa86fd2adec5d945b78..02c916a550205f3ccbe9551686e92700900dc176 100644 (file)
@@ -546,6 +546,8 @@ int nfs_create_rpc_client(struct nfs_client *clp,
                args.flags |= RPC_CLNT_CREATE_NOPING;
        if (test_bit(NFS_CS_REUSEPORT, &clp->cl_flags))
                args.flags |= RPC_CLNT_CREATE_REUSEPORT;
+       if (test_bit(NFS_CS_NETUNREACH_FATAL, &clp->cl_flags))
+               args.flags |= RPC_CLNT_CREATE_NETUNREACH_FATAL;
 
        if (!IS_ERR(clp->cl_rpcclient))
                return 0;
@@ -709,6 +711,9 @@ static int nfs_init_server(struct nfs_server *server,
        if (ctx->flags & NFS_MOUNT_NORESVPORT)
                set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
 
+       if (ctx->flags & NFS_MOUNT_NETUNREACH_FATAL)
+               __set_bit(NFS_CS_NETUNREACH_FATAL, &cl_init.init_flags);
+
        /* Allocate or find a client reference we can use */
        clp = nfs_get_client(&cl_init);
        if (IS_ERR(clp))
index 83378f69b35ea59fd6956adf14a3c9967cf08f10..8f7d40844cdc5fe416cb500e534612257f3a32de 100644 (file)
@@ -233,6 +233,8 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
        __set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags);
        if (test_bit(NFS_CS_PNFS, &cl_init->init_flags))
                __set_bit(NFS_CS_PNFS, &clp->cl_flags);
+       if (test_bit(NFS_CS_NETUNREACH_FATAL, &cl_init->init_flags))
+               __set_bit(NFS_CS_NETUNREACH_FATAL, &clp->cl_flags);
        /*
         * Set up the connection to the server before we add add to the
         * global list.
index 24406fc1352d23fcca69c67ab0483cc8aa3b3386..889511650ceb6095e3387c822889cb621e7c572c 100644 (file)
@@ -195,6 +195,9 @@ static int nfs4_map_errors(int err)
                return -EBUSY;
        case -NFS4ERR_NOT_SAME:
                return -ENOTSYNC;
+       case -ENETDOWN:
+       case -ENETUNREACH:
+               break;
        default:
                dprintk("%s could not handle NFSv4 error %d\n",
                                __func__, -err);
index a6ce8590eaaffa68742b1991f10b8c0446723134..71319637a84e61c848a296910109828c2c245d67 100644 (file)
@@ -50,6 +50,7 @@ struct nfs_client {
 #define NFS_CS_DS              7               /* - Server is a DS */
 #define NFS_CS_REUSEPORT       8               /* - reuse src port on reconnect */
 #define NFS_CS_PNFS            9               /* - Server used for pnfs */
+#define NFS_CS_NETUNREACH_FATAL        10              /* - ENETUNREACH errors are fatal */
        struct sockaddr_storage cl_addr;        /* server identifier */
        size_t                  cl_addrlen;
        char *                  cl_hostname;    /* hostname of server */
index fec976e58174230ae24c638a27e9e28438eb68fd..f8b406b0a1af5d8bf7bcdfd0182c794ac09c3ab2 100644 (file)
@@ -64,7 +64,9 @@ struct rpc_clnt {
                                cl_noretranstimeo: 1,/* No retransmit timeouts */
                                cl_autobind : 1,/* use getport() */
                                cl_chatty   : 1,/* be verbose */
-                               cl_shutdown : 1;/* rpc immediate -EIO */
+                               cl_shutdown : 1,/* rpc immediate -EIO */
+                               cl_netunreach_fatal : 1;
+                                               /* Treat ENETUNREACH errors as fatal */
        struct xprtsec_parms    cl_xprtsec;     /* transport security policy */
 
        struct rpc_rtt *        cl_rtt;         /* RTO estimator data */
@@ -175,6 +177,7 @@ struct rpc_add_xprt_test {
 #define RPC_CLNT_CREATE_SOFTERR                (1UL << 10)
 #define RPC_CLNT_CREATE_REUSEPORT      (1UL << 11)
 #define RPC_CLNT_CREATE_CONNECTED      (1UL << 12)
+#define RPC_CLNT_CREATE_NETUNREACH_FATAL       (1UL << 13)
 
 struct rpc_clnt *rpc_create(struct rpc_create_args *args);
 struct rpc_clnt        *rpc_bind_new_program(struct rpc_clnt *,
index eac57914dcf3200c1a6ed39ab030e3fe8b4da3e1..ccba79ebf8932be0ec0b32d2e4ee00b7cddc6349 100644 (file)
@@ -134,6 +134,7 @@ struct rpc_task_setup {
 #define RPC_TASK_MOVEABLE      0x0004          /* nfs4.1+ rpc tasks */
 #define RPC_TASK_NULLCREDS     0x0010          /* Use AUTH_NULL credential */
 #define RPC_CALL_MAJORSEEN     0x0020          /* major timeout seen */
+#define RPC_TASK_NETUNREACH_FATAL 0x0040       /* ENETUNREACH is fatal */
 #define RPC_TASK_DYNAMIC       0x0080          /* task was kmalloc'ed */
 #define        RPC_TASK_NO_ROUND_ROBIN 0x0100          /* send requests on "main" xprt */
 #define RPC_TASK_SOFT          0x0200          /* Use soft timeouts */
index 851841336ee65c1d03fc3565a96f5dbe409c1d64..5d331383047b79b9f6dcd699c87287453c1a5f49 100644 (file)
@@ -343,6 +343,7 @@ TRACE_EVENT(rpc_request,
                { RPC_TASK_MOVEABLE, "MOVEABLE" },                      \
                { RPC_TASK_NULLCREDS, "NULLCREDS" },                    \
                { RPC_CALL_MAJORSEEN, "MAJORSEEN" },                    \
+               { RPC_TASK_NETUNREACH_FATAL, "NETUNREACH_FATAL"},       \
                { RPC_TASK_DYNAMIC, "DYNAMIC" },                        \
                { RPC_TASK_NO_ROUND_ROBIN, "NO_ROUND_ROBIN" },          \
                { RPC_TASK_SOFT, "SOFT" },                              \
index 2fe88ea79a70c134e58abfb03fca230883eddf1f..1cfaf93ceec12eead64152d9bf8b538b1cd666ad 100644 (file)
@@ -512,6 +512,8 @@ static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
                clnt->cl_discrtry = 1;
        if (!(args->flags & RPC_CLNT_CREATE_QUIET))
                clnt->cl_chatty = 1;
+       if (args->flags & RPC_CLNT_CREATE_NETUNREACH_FATAL)
+               clnt->cl_netunreach_fatal = 1;
 
        return clnt;
 }
@@ -662,6 +664,7 @@ static struct rpc_clnt *__rpc_clone_client(struct rpc_create_args *args,
        new->cl_noretranstimeo = clnt->cl_noretranstimeo;
        new->cl_discrtry = clnt->cl_discrtry;
        new->cl_chatty = clnt->cl_chatty;
+       new->cl_netunreach_fatal = clnt->cl_netunreach_fatal;
        new->cl_principal = clnt->cl_principal;
        new->cl_max_connect = clnt->cl_max_connect;
        return new;
@@ -1195,6 +1198,8 @@ void rpc_task_set_client(struct rpc_task *task, struct rpc_clnt *clnt)
                task->tk_flags |= RPC_TASK_TIMEOUT;
        if (clnt->cl_noretranstimeo)
                task->tk_flags |= RPC_TASK_NO_RETRANS_TIMEOUT;
+       if (clnt->cl_netunreach_fatal)
+               task->tk_flags |= RPC_TASK_NETUNREACH_FATAL;
        atomic_inc(&clnt->cl_task_count);
 }
 
@@ -2102,14 +2107,17 @@ call_bind_status(struct rpc_task *task)
        case -EPROTONOSUPPORT:
                trace_rpcb_bind_version_err(task);
                goto retry_timeout;
+       case -ENETDOWN:
+       case -ENETUNREACH:
+               if (task->tk_flags & RPC_TASK_NETUNREACH_FATAL)
+                       break;
+               fallthrough;
        case -ECONNREFUSED:             /* connection problems */
        case -ECONNRESET:
        case -ECONNABORTED:
        case -ENOTCONN:
        case -EHOSTDOWN:
-       case -ENETDOWN:
        case -EHOSTUNREACH:
-       case -ENETUNREACH:
        case -EPIPE:
                trace_rpcb_unreachable_err(task);
                if (!RPC_IS_SOFTCONN(task)) {
@@ -2191,19 +2199,22 @@ call_connect_status(struct rpc_task *task)
 
        task->tk_status = 0;
        switch (status) {
+       case -ENETDOWN:
+       case -ENETUNREACH:
+               if (task->tk_flags & RPC_TASK_NETUNREACH_FATAL)
+                       break;
+               fallthrough;
        case -ECONNREFUSED:
        case -ECONNRESET:
                /* A positive refusal suggests a rebind is needed. */
-               if (RPC_IS_SOFTCONN(task))
-                       break;
                if (clnt->cl_autobind) {
                        rpc_force_rebind(clnt);
+                       if (RPC_IS_SOFTCONN(task))
+                               break;
                        goto out_retry;
                }
                fallthrough;
        case -ECONNABORTED:
-       case -ENETDOWN:
-       case -ENETUNREACH:
        case -EHOSTUNREACH:
        case -EPIPE:
        case -EPROTO:
@@ -2455,10 +2466,13 @@ call_status(struct rpc_task *task)
        trace_rpc_call_status(task);
        task->tk_status = 0;
        switch(status) {
-       case -EHOSTDOWN:
        case -ENETDOWN:
-       case -EHOSTUNREACH:
        case -ENETUNREACH:
+               if (task->tk_flags & RPC_TASK_NETUNREACH_FATAL)
+                       goto out_exit;
+               fallthrough;
+       case -EHOSTDOWN:
+       case -EHOSTUNREACH:
        case -EPERM:
                if (RPC_IS_SOFTCONN(task))
                        goto out_exit;