Merge tag 'nfsd-5.15-1' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 8 Sep 2021 22:55:42 +0000 (15:55 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 8 Sep 2021 22:55:42 +0000 (15:55 -0700)
Pull nfsd fixes from Chuck Lever:

 - Restore performance on memory-starved servers

* tag 'nfsd-5.15-1' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux:
  SUNRPC: improve error response to over-size gss credential
  SUNRPC: don't pause on incomplete allocation

1  2 
net/sunrpc/auth_gss/svcauth_gss.c
net/sunrpc/svc_xprt.c

@@@ -194,6 -194,8 +194,8 @@@ static void rsi_request(struct cache_de
        qword_addhex(bpp, blen, rsii->in_handle.data, rsii->in_handle.len);
        qword_addhex(bpp, blen, rsii->in_token.data, rsii->in_token.len);
        (*bpp)[-1] = '\n';
+       WARN_ONCE(*blen < 0,
+                 "RPCSEC/GSS credential too large - please use gssproxy\n");
  }
  
  static int rsi_parse(struct cache_detail *cd,
@@@ -707,11 -709,11 +709,11 @@@ svc_safe_putnetobj(struct kvec *resv, s
  /*
   * Verify the checksum on the header and return SVC_OK on success.
   * Otherwise, return SVC_DROP (in the case of a bad sequence number)
 - * or return SVC_DENIED and indicate error in authp.
 + * or return SVC_DENIED and indicate error in rqstp->rq_auth_stat.
   */
  static int
  gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,
 -                __be32 *rpcstart, struct rpc_gss_wire_cred *gc, __be32 *authp)
 +                __be32 *rpcstart, struct rpc_gss_wire_cred *gc)
  {
        struct gss_ctx          *ctx_id = rsci->mechctx;
        struct xdr_buf          rpchdr;
        iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart;
        xdr_buf_from_iov(&iov, &rpchdr);
  
 -      *authp = rpc_autherr_badverf;
 +      rqstp->rq_auth_stat = rpc_autherr_badverf;
        if (argv->iov_len < 4)
                return SVC_DENIED;
        flavor = svc_getnl(argv);
        if (rqstp->rq_deferred) /* skip verification of revisited request */
                return SVC_OK;
        if (gss_verify_mic(ctx_id, &rpchdr, &checksum) != GSS_S_COMPLETE) {
 -              *authp = rpcsec_gsserr_credproblem;
 +              rqstp->rq_auth_stat = rpcsec_gsserr_credproblem;
                return SVC_DENIED;
        }
  
        if (gc->gc_seq > MAXSEQ) {
                trace_rpcgss_svc_seqno_large(rqstp, gc->gc_seq);
 -              *authp = rpcsec_gsserr_ctxproblem;
 +              rqstp->rq_auth_stat = rpcsec_gsserr_ctxproblem;
                return SVC_DENIED;
        }
        if (!gss_check_seq_num(rqstp, rsci, gc->gc_seq))
@@@ -1038,8 -1040,6 +1040,8 @@@ svcauth_gss_set_client(struct svc_rqst 
        struct rpc_gss_wire_cred *gc = &svcdata->clcred;
        int stat;
  
 +      rqstp->rq_auth_stat = rpc_autherr_badcred;
 +
        /*
         * A gss export can be specified either by:
         *      export  *(sec=krb5,rw)
        stat = svcauth_unix_set_client(rqstp);
        if (stat == SVC_DROP || stat == SVC_CLOSE)
                return stat;
 +
 +      rqstp->rq_auth_stat = rpc_auth_ok;
        return SVC_OK;
  }
  
@@@ -1146,7 -1144,7 +1148,7 @@@ static void gss_free_in_token_pages(str
  }
  
  static int gss_read_proxy_verf(struct svc_rqst *rqstp,
 -                             struct rpc_gss_wire_cred *gc, __be32 *authp,
 +                             struct rpc_gss_wire_cred *gc,
                               struct xdr_netobj *in_handle,
                               struct gssp_in_token *in_token)
  {
        int pages, i, res, pgto, pgfrom;
        size_t inlen, to_offs, from_offs;
  
 -      res = gss_read_common_verf(gc, argv, authp, in_handle);
 +      res = gss_read_common_verf(gc, argv, &rqstp->rq_auth_stat, in_handle);
        if (res)
                return res;
  
@@@ -1231,7 -1229,7 +1233,7 @@@ gss_write_resv(struct kvec *resv, size_
   * Otherwise, drop the request pending an answer to the upcall.
   */
  static int svcauth_gss_legacy_init(struct svc_rqst *rqstp,
 -                      struct rpc_gss_wire_cred *gc, __be32 *authp)
 +                                 struct rpc_gss_wire_cred *gc)
  {
        struct kvec *argv = &rqstp->rq_arg.head[0];
        struct kvec *resv = &rqstp->rq_res.head[0];
        struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
  
        memset(&rsikey, 0, sizeof(rsikey));
 -      ret = gss_read_verf(gc, argv, authp,
 +      ret = gss_read_verf(gc, argv, &rqstp->rq_auth_stat,
                            &rsikey.in_handle, &rsikey.in_token);
        if (ret)
                return ret;
@@@ -1343,7 -1341,7 +1345,7 @@@ out
  }
  
  static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,
 -                      struct rpc_gss_wire_cred *gc, __be32 *authp)
 +                                struct rpc_gss_wire_cred *gc)
  {
        struct kvec *resv = &rqstp->rq_res.head[0];
        struct xdr_netobj cli_handle;
        struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
  
        memset(&ud, 0, sizeof(ud));
 -      ret = gss_read_proxy_verf(rqstp, gc, authp,
 -                                &ud.in_handle, &ud.in_token);
 +      ret = gss_read_proxy_verf(rqstp, gc, &ud.in_handle, &ud.in_token);
        if (ret)
                return ret;
  
@@@ -1528,7 -1527,7 +1530,7 @@@ static void destroy_use_gss_proxy_proc_
   * response here and return SVC_COMPLETE.
   */
  static int
 -svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
 +svcauth_gss_accept(struct svc_rqst *rqstp)
  {
        struct kvec     *argv = &rqstp->rq_arg.head[0];
        struct kvec     *resv = &rqstp->rq_res.head[0];
        int             ret;
        struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);
  
 -      *authp = rpc_autherr_badcred;
 +      rqstp->rq_auth_stat = rpc_autherr_badcred;
        if (!svcdata)
                svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL);
        if (!svcdata)
        if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0))
                goto auth_err;
  
 -      *authp = rpc_autherr_badverf;
 +      rqstp->rq_auth_stat = rpc_autherr_badverf;
        switch (gc->gc_proc) {
        case RPC_GSS_PROC_INIT:
        case RPC_GSS_PROC_CONTINUE_INIT:
                if (use_gss_proxy(SVC_NET(rqstp)))
 -                      return svcauth_gss_proxy_init(rqstp, gc, authp);
 +                      return svcauth_gss_proxy_init(rqstp, gc);
                else
 -                      return svcauth_gss_legacy_init(rqstp, gc, authp);
 +                      return svcauth_gss_legacy_init(rqstp, gc);
        case RPC_GSS_PROC_DATA:
        case RPC_GSS_PROC_DESTROY:
                /* Look up the context, and check the verifier: */
 -              *authp = rpcsec_gsserr_credproblem;
 +              rqstp->rq_auth_stat = rpcsec_gsserr_credproblem;
                rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx);
                if (!rsci)
                        goto auth_err;
 -              switch (gss_verify_header(rqstp, rsci, rpcstart, gc, authp)) {
 +              switch (gss_verify_header(rqstp, rsci, rpcstart, gc)) {
                case SVC_OK:
                        break;
                case SVC_DENIED:
                }
                break;
        default:
 -              *authp = rpc_autherr_rejectedcred;
 +              rqstp->rq_auth_stat = rpc_autherr_rejectedcred;
                goto auth_err;
        }
  
                svc_putnl(resv, RPC_SUCCESS);
                goto complete;
        case RPC_GSS_PROC_DATA:
 -              *authp = rpcsec_gsserr_ctxproblem;
 +              rqstp->rq_auth_stat = rpcsec_gsserr_ctxproblem;
                svcdata->verf_start = resv->iov_base + resv->iov_len;
                if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
                        goto auth_err;
                rqstp->rq_cred = rsci->cred;
                get_group_info(rsci->cred.cr_group_info);
 -              *authp = rpc_autherr_badcred;
 +              rqstp->rq_auth_stat = rpc_autherr_badcred;
                switch (gc->gc_svc) {
                case RPC_GSS_SVC_NONE:
                        break;
diff --combined net/sunrpc/svc_xprt.c
@@@ -663,7 -663,7 +663,7 @@@ static int svc_alloc_arg(struct svc_rqs
  {
        struct svc_serv *serv = rqstp->rq_server;
        struct xdr_buf *arg = &rqstp->rq_arg;
-       unsigned long pages, filled;
+       unsigned long pages, filled, ret;
  
        pagevec_init(&rqstp->rq_pvec);
  
                pages = RPCSVC_MAXPAGES;
        }
  
-       for (;;) {
-               filled = alloc_pages_bulk_array(GFP_KERNEL, pages,
-                                               rqstp->rq_pages);
-               if (filled == pages)
-                       break;
+       for (filled = 0; filled < pages; filled = ret) {
+               ret = alloc_pages_bulk_array(GFP_KERNEL, pages,
+                                            rqstp->rq_pages);
+               if (ret > filled)
+                       /* Made progress, don't sleep yet */
+                       continue;
  
                set_current_state(TASK_INTERRUPTIBLE);
                if (signalled() || kthread_should_stop()) {
@@@ -838,8 -839,7 +839,8 @@@ static int svc_handle_xprt(struct svc_r
                rqstp->rq_stime = ktime_get();
                rqstp->rq_reserved = serv->sv_max_mesg;
                atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved);
 -      }
 +      } else
 +              svc_xprt_received(xprt);
  out:
        trace_svc_handle_xprt(xprt, len);
        return len;