]> Git Repo - linux.git/commitdiff
Merge tag 'nfsd-6.14' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux
authorLinus Torvalds <[email protected]>
Tue, 28 Jan 2025 01:27:24 +0000 (17:27 -0800)
committerLinus Torvalds <[email protected]>
Tue, 28 Jan 2025 01:27:24 +0000 (17:27 -0800)
Pull nfsd updates from Chuck Lever:
 "Jeff Layton contributed an implementation of NFSv4.2+ attribute
  delegation, as described here:

    https://www.ietf.org/archive/id/draft-ietf-nfsv4-delstid-08.html

  This interoperates with similar functionality introduced into the
  Linux NFS client in v6.11. An attribute delegation permits an NFS
  client to manage a file's mtime, rather than flushing dirty data to
  the NFS server so that the file's mtime reflects the last write, which
  is considerably slower.

  Neil Brown contributed dynamic NFSv4.1 session slot table resizing.
  This facility enables NFSD to increase or decrease the number of slots
  per NFS session depending on server memory availability. More session
  slots means greater parallelism.

  Chuck Lever fixed a long-standing latent bug where NFSv4 COMPOUND
  encoding screws up when crossing a page boundary in the encoding
  buffer. This is a zero-day bug, but hitting it is rare and depends on
  the NFS client implementation. The Linux NFS client does not happen to
  trigger this issue.

  A variety of bug fixes and other incremental improvements fill out the
  list of commits in this release. Great thanks to all contributors,
  reviewers, testers, and bug reporters who participated during this
  development cycle"

* tag 'nfsd-6.14' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (42 commits)
  sunrpc: Remove gss_{de,en}crypt_xdr_buf deadcode
  sunrpc: Remove gss_generic_token deadcode
  sunrpc: Remove unused xprt_iter_get_xprt
  Revert "SUNRPC: Reduce thread wake-up rate when receiving large RPC messages"
  nfsd: implement OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION
  nfsd: handle delegated timestamps in SETATTR
  nfsd: add support for delegated timestamps
  nfsd: rework NFS4_SHARE_WANT_* flag handling
  nfsd: add support for FATTR4_OPEN_ARGUMENTS
  nfsd: prepare delegation code for handing out *_ATTRS_DELEG delegations
  nfsd: rename NFS4_SHARE_WANT_* constants to OPEN4_SHARE_ACCESS_WANT_*
  nfsd: switch to autogenerated definitions for open_delegation_type4
  nfs_common: make include/linux/nfs4.h include generated nfs4_1.h
  nfsd: fix handling of delegated change attr in CB_GETATTR
  SUNRPC: Document validity guarantees of the pointer returned by reserve_space
  NFSD: Insulate nfsd4_encode_fattr4() from page boundaries in the encode buffer
  NFSD: Insulate nfsd4_encode_secinfo() from page boundaries in the encode buffer
  NFSD: Refactor nfsd4_do_encode_secinfo() again
  NFSD: Insulate nfsd4_encode_readlink() from page boundaries in the encode buffer
  NFSD: Insulate nfsd4_encode_read_plus_data() from page boundaries in the encode buffer
  ...

1  2 
fs/nfsd/nfs4recover.c
fs/nfsd/nfs4xdr.c
fs/nfsd/nfsfh.c
net/sunrpc/svc_xprt.c

diff --combined fs/nfsd/nfs4recover.c
index 7f2ceeb118a423cba5f5764a87da80c929d7dc20,1c8fcb04b3cdebbdf87ac0ae97231aac8331b74d..28f4d5311c40dcb2ee5801e0b21b481f51332780
@@@ -82,13 -82,14 +82,13 @@@ nfs4_save_creds(const struct cred **ori
        new->fsuid = GLOBAL_ROOT_UID;
        new->fsgid = GLOBAL_ROOT_GID;
        *original_creds = override_creds(new);
 -      put_cred(new);
        return 0;
  }
  
  static void
  nfs4_reset_creds(const struct cred *original)
  {
 -      revert_creds(original);
 +      put_cred(revert_creds(original));
  }
  
  static void
@@@ -2051,7 -2052,6 +2051,6 @@@ static inline int check_for_legacy_meth
                path_put(&path);
                if (status)
                        return -ENOTDIR;
-               status = nn->client_tracking_ops->init(net);
        }
        return status;
  }
diff --combined fs/nfsd/nfs4xdr.c
index 8dd2e2ada474d7e64a0a6c62f780711c9eafc85b,5d413e751c744332790c2e2a042548a52995d091..e67420729ecd609c46c3f5d411dea73aa0047ffc
@@@ -55,6 -55,7 +55,7 @@@
  #include "netns.h"
  #include "pnfs.h"
  #include "filecache.h"
+ #include "nfs4xdr_gen.h"
  
  #include "trace.h"
  
@@@ -520,6 -521,26 +521,26 @@@ nfsd4_decode_fattr4(struct nfsd4_compou
                *umask = mask & S_IRWXUGO;
                iattr->ia_valid |= ATTR_MODE;
        }
+       if (bmval[2] & FATTR4_WORD2_TIME_DELEG_ACCESS) {
+               fattr4_time_deleg_access access;
+               if (!xdrgen_decode_fattr4_time_deleg_access(argp->xdr, &access))
+                       return nfserr_bad_xdr;
+               iattr->ia_atime.tv_sec = access.seconds;
+               iattr->ia_atime.tv_nsec = access.nseconds;
+               iattr->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET | ATTR_DELEG;
+       }
+       if (bmval[2] & FATTR4_WORD2_TIME_DELEG_MODIFY) {
+               fattr4_time_deleg_modify modify;
+               if (!xdrgen_decode_fattr4_time_deleg_modify(argp->xdr, &modify))
+                       return nfserr_bad_xdr;
+               iattr->ia_mtime.tv_sec = modify.seconds;
+               iattr->ia_mtime.tv_nsec = modify.nseconds;
+               iattr->ia_ctime.tv_sec = modify.seconds;
+               iattr->ia_ctime.tv_nsec = modify.seconds;
+               iattr->ia_valid |= ATTR_CTIME | ATTR_MTIME | ATTR_MTIME_SET | ATTR_DELEG;
+       }
  
        /* request sanity: did attrlist4 contain the expected number of words? */
        if (attrlist4_count != xdr_stream_pos(argp->xdr) - starting_pos)
@@@ -1066,13 -1087,13 +1087,13 @@@ static __be32 nfsd4_decode_share_access
                return nfs_ok;
        if (!argp->minorversion)
                return nfserr_bad_xdr;
-       switch (w & NFS4_SHARE_WANT_MASK) {
-       case NFS4_SHARE_WANT_NO_PREFERENCE:
-       case NFS4_SHARE_WANT_READ_DELEG:
-       case NFS4_SHARE_WANT_WRITE_DELEG:
-       case NFS4_SHARE_WANT_ANY_DELEG:
-       case NFS4_SHARE_WANT_NO_DELEG:
-       case NFS4_SHARE_WANT_CANCEL:
+       switch (w & NFS4_SHARE_WANT_TYPE_MASK) {
+       case OPEN4_SHARE_ACCESS_WANT_NO_PREFERENCE:
+       case OPEN4_SHARE_ACCESS_WANT_READ_DELEG:
+       case OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG:
+       case OPEN4_SHARE_ACCESS_WANT_ANY_DELEG:
+       case OPEN4_SHARE_ACCESS_WANT_NO_DELEG:
+       case OPEN4_SHARE_ACCESS_WANT_CANCEL:
                break;
        default:
                return nfserr_bad_xdr;
@@@ -1884,7 -1905,8 +1905,8 @@@ nfsd4_decode_sequence(struct nfsd4_comp
                return nfserr_bad_xdr;
        seq->seqid = be32_to_cpup(p++);
        seq->slotid = be32_to_cpup(p++);
-       seq->maxslots = be32_to_cpup(p++);
+       /* sa_highest_slotid counts from 0 but maxslots  counts from 1 ... */
+       seq->maxslots = be32_to_cpup(p++) + 1;
        seq->cachethis = be32_to_cpup(p);
  
        seq->status_flags = 0;
@@@ -2818,11 -2840,11 +2840,11 @@@ static __be32 nfsd4_encode_nfsace4(stru
  #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
  static inline __be32
  nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
 -                          void *context, int len)
 +                          const struct lsm_context *context)
  {
        __be32 *p;
  
 -      p = xdr_reserve_space(xdr, len + 4 + 4 + 4);
 +      p = xdr_reserve_space(xdr, context->len + 4 + 4 + 4);
        if (!p)
                return nfserr_resource;
  
         */
        *p++ = cpu_to_be32(0); /* lfs */
        *p++ = cpu_to_be32(0); /* pi */
 -      p = xdr_encode_opaque(p, contextlen);
 +      p = xdr_encode_opaque(p, context->context, context->len);
        return 0;
  }
  #else
  static inline __be32
  nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
 -                          void *context, int len)
 +                          struct lsm_context *context)
  { return 0; }
  #endif
  
@@@ -2919,8 -2941,10 +2941,9 @@@ struct nfsd4_fattr_args 
        struct kstat            stat;
        struct kstatfs          statfs;
        struct nfs4_acl         *acl;
+       u64                     change_attr;
  #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
 -      void                    *context;
 -      int                     contextlen;
 +      struct lsm_context      context;
  #endif
        u32                     rdattr_err;
        bool                    contextsupport;
@@@ -3017,7 -3041,6 +3040,6 @@@ static __be32 nfsd4_encode_fattr4_chang
                                         const struct nfsd4_fattr_args *args)
  {
        const struct svc_export *exp = args->exp;
-       u64 c;
  
        if (unlikely(exp->ex_flags & NFSEXP_V4ROOT)) {
                u32 flush_time = convert_to_wallclock(exp->cd->flush_time);
                        return nfserr_resource;
                return nfs_ok;
        }
-       c = nfsd4_change_attribute(&args->stat);
-       return nfsd4_encode_changeid4(xdr, c);
+       return nfsd4_encode_changeid4(xdr, args->change_attr);
  }
  
  static __be32 nfsd4_encode_fattr4_size(struct xdr_stream *xdr,
@@@ -3375,7 -3396,8 +3395,7 @@@ static __be32 nfsd4_encode_fattr4_suppa
  static __be32 nfsd4_encode_fattr4_sec_label(struct xdr_stream *xdr,
                                            const struct nfsd4_fattr_args *args)
  {
 -      return nfsd4_encode_security_label(xdr, args->rqstp,
 -                                         args->context, args->contextlen);
 +      return nfsd4_encode_security_label(xdr, args->rqstp, &args->context);
  }
  #endif
  
@@@ -3387,6 -3409,56 +3407,56 @@@ static __be32 nfsd4_encode_fattr4_xattr
        return nfsd4_encode_bool(xdr, err == 0);
  }
  
+ #define NFSD_OA_SHARE_ACCESS  (BIT(OPEN_ARGS_SHARE_ACCESS_READ)       | \
+                                BIT(OPEN_ARGS_SHARE_ACCESS_WRITE)      | \
+                                BIT(OPEN_ARGS_SHARE_ACCESS_BOTH))
+ #define NFSD_OA_SHARE_DENY    (BIT(OPEN_ARGS_SHARE_DENY_NONE)         | \
+                                BIT(OPEN_ARGS_SHARE_DENY_READ)         | \
+                                BIT(OPEN_ARGS_SHARE_DENY_WRITE)        | \
+                                BIT(OPEN_ARGS_SHARE_DENY_BOTH))
+ #define NFSD_OA_SHARE_ACCESS_WANT     (BIT(OPEN_ARGS_SHARE_ACCESS_WANT_ANY_DELEG)             | \
+                                        BIT(OPEN_ARGS_SHARE_ACCESS_WANT_NO_DELEG)              | \
+                                        BIT(OPEN_ARGS_SHARE_ACCESS_WANT_CANCEL)                | \
+                                        BIT(OPEN_ARGS_SHARE_ACCESS_WANT_DELEG_TIMESTAMPS)      | \
+                                        BIT(OPEN_ARGS_SHARE_ACCESS_WANT_OPEN_XOR_DELEGATION))
+ #define NFSD_OA_OPEN_CLAIM    (BIT(OPEN_ARGS_OPEN_CLAIM_NULL)         | \
+                                BIT(OPEN_ARGS_OPEN_CLAIM_PREVIOUS)     | \
+                                BIT(OPEN_ARGS_OPEN_CLAIM_DELEGATE_CUR) | \
+                                BIT(OPEN_ARGS_OPEN_CLAIM_DELEGATE_PREV)| \
+                                BIT(OPEN_ARGS_OPEN_CLAIM_FH)           | \
+                                BIT(OPEN_ARGS_OPEN_CLAIM_DELEG_CUR_FH) | \
+                                BIT(OPEN_ARGS_OPEN_CLAIM_DELEG_PREV_FH))
+ #define NFSD_OA_CREATE_MODE   (BIT(OPEN_ARGS_CREATEMODE_UNCHECKED4)   | \
+                                BIT(OPEN_ARGS_CREATE_MODE_GUARDED)     | \
+                                BIT(OPEN_ARGS_CREATEMODE_EXCLUSIVE4)   | \
+                                BIT(OPEN_ARGS_CREATE_MODE_EXCLUSIVE4_1))
+ static uint32_t oa_share_access = NFSD_OA_SHARE_ACCESS;
+ static uint32_t oa_share_deny = NFSD_OA_SHARE_DENY;
+ static uint32_t oa_share_access_want = NFSD_OA_SHARE_ACCESS_WANT;
+ static uint32_t oa_open_claim = NFSD_OA_OPEN_CLAIM;
+ static uint32_t oa_create_mode = NFSD_OA_CREATE_MODE;
+ static const struct open_arguments4 nfsd_open_arguments = {
+       .oa_share_access = { .count = 1, .element = &oa_share_access },
+       .oa_share_deny = { .count = 1, .element = &oa_share_deny },
+       .oa_share_access_want = { .count = 1, .element = &oa_share_access_want },
+       .oa_open_claim = { .count = 1, .element = &oa_open_claim },
+       .oa_create_mode = { .count = 1, .element = &oa_create_mode },
+ };
+ static __be32 nfsd4_encode_fattr4_open_arguments(struct xdr_stream *xdr,
+                                                const struct nfsd4_fattr_args *args)
+ {
+       if (!xdrgen_encode_fattr4_open_arguments(xdr, &nfsd_open_arguments))
+               return nfserr_resource;
+       return nfs_ok;
+ }
  static const nfsd4_enc_attr nfsd4_enc_fattr4_encode_ops[] = {
        [FATTR4_SUPPORTED_ATTRS]        = nfsd4_encode_fattr4_supported_attrs,
        [FATTR4_TYPE]                   = nfsd4_encode_fattr4_type,
  
        [FATTR4_MODE_UMASK]             = nfsd4_encode_fattr4__noop,
        [FATTR4_XATTR_SUPPORT]          = nfsd4_encode_fattr4_xattr_support,
+       [FATTR4_OPEN_ARGUMENTS]         = nfsd4_encode_fattr4_open_arguments,
  };
  
  /*
@@@ -3504,8 -3577,8 +3575,8 @@@ nfsd4_encode_fattr4(struct svc_rqst *rq
        struct nfsd4_fattr_args args;
        struct svc_fh *tempfh = NULL;
        int starting_len = xdr->buf->len;
-       __be32 *attrlen_p, status;
-       int attrlen_offset;
+       unsigned int attrlen_offset;
+       __be32 attrlen, status;
        u32 attrmask[3];
        int err;
        struct nfsd4_compoundres *resp = rqstp->rq_resp;
        args.ignore_crossmnt = (ignore_crossmnt != 0);
        args.acl = NULL;
  #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
 -      args.context = NULL;
 +      args.context.context = NULL;
  #endif
  
        /*
                if (status)
                        goto out;
        }
-       if (attrmask[0] & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) {
+       if ((attrmask[0] & (FATTR4_WORD0_CHANGE |
+                           FATTR4_WORD0_SIZE)) ||
+           (attrmask[1] & (FATTR4_WORD1_TIME_ACCESS |
+                           FATTR4_WORD1_TIME_MODIFY |
+                           FATTR4_WORD1_TIME_METADATA))) {
                status = nfsd4_deleg_getattr_conflict(rqstp, dentry, &dp);
                if (status)
                        goto out;
        if (dp) {
                struct nfs4_cb_fattr *ncf = &dp->dl_cb_fattr;
  
-               if (ncf->ncf_file_modified)
+               if (ncf->ncf_file_modified) {
+                       ++ncf->ncf_initial_cinfo;
                        args.stat.size = ncf->ncf_cur_fsize;
+                       if (!timespec64_is_epoch(&ncf->ncf_cb_mtime))
+                               args.stat.mtime = ncf->ncf_cb_mtime;
+               }
+               args.change_attr = ncf->ncf_initial_cinfo;
+               if (!timespec64_is_epoch(&ncf->ncf_cb_atime))
+                       args.stat.atime = ncf->ncf_cb_atime;
  
                nfs4_put_stid(&dp->dl_stid);
+       } else {
+               args.change_attr = nfsd4_change_attribute(&args.stat);
        }
        if (err)
                goto out_nfserr;
  
             attrmask[0] & FATTR4_WORD0_SUPPORTED_ATTRS) {
                if (exp->ex_flags & NFSEXP_SECURITY_LABEL)
                        err = security_inode_getsecctx(d_inode(dentry),
 -                                              &args.context, &args.contextlen);
 +                                              &args.context);
                else
                        err = -EOPNOTSUPP;
                args.contextsupport = (err == 0);
  
        /* attr_vals */
        attrlen_offset = xdr->buf->len;
-       attrlen_p = xdr_reserve_space(xdr, XDR_UNIT);
-       if (!attrlen_p)
+       if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT)))
                goto out_resource;
        bitmap_from_arr32(attr_bitmap, attrmask,
                          ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
                if (status != nfs_ok)
                        goto out;
        }
-       *attrlen_p = cpu_to_be32(xdr->buf->len - attrlen_offset - XDR_UNIT);
+       attrlen = cpu_to_be32(xdr->buf->len - attrlen_offset - XDR_UNIT);
+       write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, XDR_UNIT);
        status = nfs_ok;
  
  out:
  #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
 -      if (args.context)
 -              security_release_secctx(args.context, args.contextlen);
 +      if (args.context.context)
 +              security_release_secctx(&args.context);
  #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
        kfree(args.acl);
        if (tempfh) {
@@@ -4227,18 -4315,20 +4313,20 @@@ nfsd4_encode_open_delegation4(struct xd
        if (xdr_stream_encode_u32(xdr, open->op_delegate_type) != XDR_UNIT)
                return nfserr_resource;
        switch (open->op_delegate_type) {
-       case NFS4_OPEN_DELEGATE_NONE:
+       case OPEN_DELEGATE_NONE:
                status = nfs_ok;
                break;
-       case NFS4_OPEN_DELEGATE_READ:
+       case OPEN_DELEGATE_READ:
+       case OPEN_DELEGATE_READ_ATTRS_DELEG:
                /* read */
                status = nfsd4_encode_open_read_delegation4(xdr, open);
                break;
-       case NFS4_OPEN_DELEGATE_WRITE:
+       case OPEN_DELEGATE_WRITE:
+       case OPEN_DELEGATE_WRITE_ATTRS_DELEG:
                /* write */
                status = nfsd4_encode_open_write_delegation4(xdr, open);
                break;
-       case NFS4_OPEN_DELEGATE_NONE_EXT:
+       case OPEN_DELEGATE_NONE_EXT:
                /* od_whynone */
                status = nfsd4_encode_open_none_delegation4(xdr, open);
                break;
@@@ -4314,6 -4404,15 +4402,15 @@@ static __be32 nfsd4_encode_splice_read
        int status, space_left;
        __be32 nfserr;
  
+       /*
+        * Splice read doesn't work if encoding has already wandered
+        * into the XDR buf's page array.
+        */
+       if (unlikely(xdr->buf->page_len)) {
+               WARN_ON_ONCE(1);
+               return nfserr_serverfault;
+       }
        /*
         * Make sure there is room at the end of buf->head for
         * svcxdr_encode_opaque_pages() to create a tail buffer
@@@ -4396,25 -4495,23 +4493,23 @@@ nfsd4_encode_read(struct nfsd4_compound
        struct nfsd4_compoundargs *argp = resp->rqstp->rq_argp;
        struct nfsd4_read *read = &u->read;
        struct xdr_stream *xdr = resp->xdr;
-       int starting_len = xdr->buf->len;
        bool splice_ok = argp->splice_ok;
+       unsigned int eof_offset;
        unsigned long maxcount;
+       __be32 wire_data[2];
        struct file *file;
-       __be32 *p;
  
        if (nfserr)
                return nfserr;
+       eof_offset = xdr->buf->len;
        file = read->rd_nf->nf_file;
  
-       p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */
-       if (!p) {
+       /* Reserve space for the eof flag and byte count */
+       if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT * 2))) {
                WARN_ON_ONCE(splice_ok);
                return nfserr_resource;
        }
-       if (resp->xdr->buf->page_len && splice_ok) {
-               WARN_ON_ONCE(1);
-               return nfserr_serverfault;
-       }
        xdr_commit_encode(xdr);
  
        maxcount = min_t(unsigned long, read->rd_length,
        else
                nfserr = nfsd4_encode_readv(resp, read, file, maxcount);
        if (nfserr) {
-               xdr_truncate_encode(xdr, starting_len);
+               xdr_truncate_encode(xdr, eof_offset);
                return nfserr;
        }
  
-       p = xdr_encode_bool(p, read->rd_eof);
-       *p = cpu_to_be32(read->rd_length);
+       wire_data[0] = read->rd_eof ? xdr_one : xdr_zero;
+       wire_data[1] = cpu_to_be32(read->rd_length);
+       write_bytes_to_xdr_buf(xdr->buf, eof_offset, &wire_data, XDR_UNIT * 2);
        return nfs_ok;
  }
  
@@@ -4439,25 -4537,21 +4535,21 @@@ nfsd4_encode_readlink(struct nfsd4_comp
                      union nfsd4_op_u *u)
  {
        struct nfsd4_readlink *readlink = &u->readlink;
-       __be32 *p, *maxcount_p, zero = xdr_zero;
+       __be32 *p, wire_count, zero = xdr_zero;
        struct xdr_stream *xdr = resp->xdr;
-       int length_offset = xdr->buf->len;
+       unsigned int length_offset;
        int maxcount, status;
  
-       maxcount_p = xdr_reserve_space(xdr, XDR_UNIT);
-       if (!maxcount_p)
+       /* linktext4.count */
+       length_offset = xdr->buf->len;
+       if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT)))
                return nfserr_resource;
-       maxcount = PAGE_SIZE;
  
+       /* linktext4.data */
+       maxcount = PAGE_SIZE;
        p = xdr_reserve_space(xdr, maxcount);
        if (!p)
                return nfserr_resource;
-       /*
-        * XXX: By default, vfs_readlink() will truncate symlinks if they
-        * would overflow the buffer.  Is this kosher in NFSv4?  If not, one
-        * easy fix is: if vfs_readlink() precisely fills the buffer, assume
-        * that truncation occurred, and return NFS4ERR_RESOURCE.
-        */
        nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp,
                                                (char *)p, &maxcount);
        if (nfserr == nfserr_isdir)
                nfserr = nfserrno(status);
                goto out_err;
        }
-       *maxcount_p = cpu_to_be32(maxcount);
+       wire_count = cpu_to_be32(maxcount);
+       write_bytes_to_xdr_buf(xdr->buf, length_offset, &wire_count, XDR_UNIT);
        xdr_truncate_encode(xdr, length_offset + 4 + xdr_align_size(maxcount));
        write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount, &zero,
                               xdr_pad_size(maxcount));
@@@ -4605,14 -4701,42 +4699,42 @@@ nfsd4_encode_rpcsec_gss_info(struct xdr
  }
  
  static __be32
- nfsd4_do_encode_secinfo(struct xdr_stream *xdr, struct svc_export *exp)
+ nfsd4_encode_secinfo4(struct xdr_stream *xdr, rpc_authflavor_t pf,
+                     u32 *supported)
+ {
+       struct rpcsec_gss_info info;
+       __be32 status;
+       if (rpcauth_get_gssinfo(pf, &info) == 0) {
+               (*supported)++;
+               /* flavor */
+               status = nfsd4_encode_uint32_t(xdr, RPC_AUTH_GSS);
+               if (status != nfs_ok)
+                       return status;
+               /* flavor_info */
+               status = nfsd4_encode_rpcsec_gss_info(xdr, &info);
+               if (status != nfs_ok)
+                       return status;
+       } else if (pf < RPC_AUTH_MAXFLAVOR) {
+               (*supported)++;
+               /* flavor */
+               status = nfsd4_encode_uint32_t(xdr, pf);
+               if (status != nfs_ok)
+                       return status;
+       }
+       return nfs_ok;
+ }
+ static __be32
+ nfsd4_encode_SECINFO4resok(struct xdr_stream *xdr, struct svc_export *exp)
  {
        u32 i, nflavs, supported;
        struct exp_flavor_info *flavs;
        struct exp_flavor_info def_flavs[2];
-       static bool report = true;
-       __be32 *flavorsp;
-       __be32 status;
+       unsigned int count_offset;
+       __be32 status, wire_count;
  
        if (exp->ex_nflavors) {
                flavs = exp->ex_flavors;
                }
        }
  
-       supported = 0;
-       flavorsp = xdr_reserve_space(xdr, XDR_UNIT);
-       if (!flavorsp)
+       count_offset = xdr->buf->len;
+       if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT)))
                return nfserr_resource;
  
-       for (i = 0; i < nflavs; i++) {
-               rpc_authflavor_t pf = flavs[i].pseudoflavor;
-               struct rpcsec_gss_info info;
-               if (rpcauth_get_gssinfo(pf, &info) == 0) {
-                       supported++;
-                       /* flavor */
-                       status = nfsd4_encode_uint32_t(xdr, RPC_AUTH_GSS);
-                       if (status != nfs_ok)
-                               return status;
-                       /* flavor_info */
-                       status = nfsd4_encode_rpcsec_gss_info(xdr, &info);
-                       if (status != nfs_ok)
-                               return status;
-               } else if (pf < RPC_AUTH_MAXFLAVOR) {
-                       supported++;
-                       /* flavor */
-                       status = nfsd4_encode_uint32_t(xdr, pf);
-                       if (status != nfs_ok)
-                               return status;
-               } else {
-                       if (report)
-                               pr_warn("NFS: SECINFO: security flavor %u "
-                                       "is not supported\n", pf);
-               }
+       for (i = 0, supported = 0; i < nflavs; i++) {
+               status = nfsd4_encode_secinfo4(xdr, flavs[i].pseudoflavor,
+                                              &supported);
+               if (status != nfs_ok)
+                       return status;
        }
  
-       if (nflavs != supported)
-               report = false;
-       *flavorsp = cpu_to_be32(supported);
+       wire_count = cpu_to_be32(supported);
+       write_bytes_to_xdr_buf(xdr->buf, count_offset, &wire_count,
+                              XDR_UNIT);
        return 0;
  }
  
@@@ -4681,7 -4782,7 +4780,7 @@@ nfsd4_encode_secinfo(struct nfsd4_compo
        struct nfsd4_secinfo *secinfo = &u->secinfo;
        struct xdr_stream *xdr = resp->xdr;
  
-       return nfsd4_do_encode_secinfo(xdr, secinfo->si_exp);
+       return nfsd4_encode_SECINFO4resok(xdr, secinfo->si_exp);
  }
  
  static __be32
@@@ -4691,7 -4792,7 +4790,7 @@@ nfsd4_encode_secinfo_no_name(struct nfs
        struct nfsd4_secinfo_no_name *secinfo = &u->secinfo_no_name;
        struct xdr_stream *xdr = resp->xdr;
  
-       return nfsd4_do_encode_secinfo(xdr, secinfo->sin_exp);
+       return nfsd4_encode_SECINFO4resok(xdr, secinfo->sin_exp);
  }
  
  static __be32
@@@ -4966,7 -5067,7 +5065,7 @@@ nfsd4_encode_sequence(struct nfsd4_comp
        if (nfserr != nfs_ok)
                return nfserr;
        /* sr_target_highest_slotid */
-       nfserr = nfsd4_encode_slotid4(xdr, seq->maxslots - 1);
+       nfserr = nfsd4_encode_slotid4(xdr, seq->target_maxslots - 1);
        if (nfserr != nfs_ok)
                return nfserr;
        /* sr_status_flags */
@@@ -5294,17 -5395,20 +5393,20 @@@ nfsd4_encode_read_plus_data(struct nfsd
        struct file *file = read->rd_nf->nf_file;
        struct xdr_stream *xdr = resp->xdr;
        bool splice_ok = argp->splice_ok;
+       unsigned int offset_offset;
+       __be32 nfserr, wire_count;
        unsigned long maxcount;
-       __be32 nfserr, *p;
+       __be64 wire_offset;
  
-       /* Content type, offset, byte count */
-       p = xdr_reserve_space(xdr, 4 + 8 + 4);
-       if (!p)
+       if (xdr_stream_encode_u32(xdr, NFS4_CONTENT_DATA) != XDR_UNIT)
                return nfserr_io;
-       if (resp->xdr->buf->page_len && splice_ok) {
-               WARN_ON_ONCE(splice_ok);
-               return nfserr_serverfault;
-       }
+       offset_offset = xdr->buf->len;
+       /* Reserve space for the byte offset and count */
+       if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT * 3)))
+               return nfserr_io;
+       xdr_commit_encode(xdr);
  
        maxcount = min_t(unsigned long, read->rd_length,
                         (xdr->buf->buflen - xdr->buf->len));
        if (nfserr)
                return nfserr;
  
-       *p++ = cpu_to_be32(NFS4_CONTENT_DATA);
-       p = xdr_encode_hyper(p, read->rd_offset);
-       *p = cpu_to_be32(read->rd_length);
+       wire_offset = cpu_to_be64(read->rd_offset);
+       write_bytes_to_xdr_buf(xdr->buf, offset_offset, &wire_offset,
+                              XDR_UNIT * 2);
+       wire_count = cpu_to_be32(read->rd_length);
+       write_bytes_to_xdr_buf(xdr->buf, offset_offset + XDR_UNIT * 2,
+                              &wire_count, XDR_UNIT);
        return nfs_ok;
  }
  
@@@ -5330,16 -5436,17 +5434,17 @@@ nfsd4_encode_read_plus(struct nfsd4_com
        struct nfsd4_read *read = &u->read;
        struct file *file = read->rd_nf->nf_file;
        struct xdr_stream *xdr = resp->xdr;
-       int starting_len = xdr->buf->len;
+       unsigned int eof_offset;
+       __be32 wire_data[2];
        u32 segments = 0;
-       __be32 *p;
  
        if (nfserr)
                return nfserr;
  
-       /* eof flag, segment count */
-       p = xdr_reserve_space(xdr, 4 + 4);
-       if (!p)
+       eof_offset = xdr->buf->len;
+       /* Reserve space for the eof flag and segment count */
+       if (unlikely(!xdr_reserve_space(xdr, XDR_UNIT * 2)))
                return nfserr_io;
        xdr_commit_encode(xdr);
  
  
        nfserr = nfsd4_encode_read_plus_data(resp, read);
        if (nfserr) {
-               xdr_truncate_encode(xdr, starting_len);
+               xdr_truncate_encode(xdr, eof_offset);
                return nfserr;
        }
  
        segments++;
  
  out:
-       p = xdr_encode_bool(p, read->rd_eof);
-       *p = cpu_to_be32(segments);
+       wire_data[0] = read->rd_eof ? xdr_one : xdr_zero;
+       wire_data[1] = cpu_to_be32(segments);
+       write_bytes_to_xdr_buf(xdr->buf, eof_offset, &wire_data, XDR_UNIT * 2);
        return nfserr;
  }
  
@@@ -5758,15 -5866,14 +5864,14 @@@ nfsd4_encode_operation(struct nfsd4_com
        struct nfs4_stateowner *so = resp->cstate.replay_owner;
        struct svc_rqst *rqstp = resp->rqstp;
        const struct nfsd4_operation *opdesc = op->opdesc;
-       int post_err_offset;
+       unsigned int op_status_offset;
        nfsd4_enc encoder;
-       __be32 *p;
  
-       p = xdr_reserve_space(xdr, 8);
-       if (!p)
+       if (xdr_stream_encode_u32(xdr, op->opnum) != XDR_UNIT)
+               goto release;
+       op_status_offset = xdr->buf->len;
+       if (!xdr_reserve_space(xdr, XDR_UNIT))
                goto release;
-       *p++ = cpu_to_be32(op->opnum);
-       post_err_offset = xdr->buf->len;
  
        if (op->opnum == OP_ILLEGAL)
                goto status;
                 * bug if we had to do this on a non-idempotent op:
                 */
                warn_on_nonidempotent_op(op);
-               xdr_truncate_encode(xdr, post_err_offset);
+               xdr_truncate_encode(xdr, op_status_offset + XDR_UNIT);
        }
        if (so) {
-               int len = xdr->buf->len - post_err_offset;
+               int len = xdr->buf->len - (op_status_offset + XDR_UNIT);
  
                so->so_replay.rp_status = op->status;
                so->so_replay.rp_buflen = len;
-               read_bytes_from_xdr_buf(xdr->buf, post_err_offset,
+               read_bytes_from_xdr_buf(xdr->buf, op_status_offset + XDR_UNIT,
                                                so->so_replay.rp_buf, len);
        }
  status:
        op->status = nfsd4_map_status(op->status,
                                      resp->cstate.minorversion);
-       *p = op->status;
+       write_bytes_to_xdr_buf(xdr->buf, op_status_offset,
+                              &op->status, XDR_UNIT);
  release:
        if (opdesc && opdesc->op_release)
                opdesc->op_release(&op->u);
diff --combined fs/nfsd/nfsfh.c
index 98d6459724a74cf166ccf1d83db08c65239adfba,bf59f83c6224e61026e7d46d40b7661b91968893..32019751a41e836182ab5b8800e2f39027fe7573
@@@ -222,6 -222,7 +222,6 @@@ static __be32 nfsd_set_fh_dentry(struc
                        cap_raise_nfsd_set(new->cap_effective,
                                           new->cap_permitted);
                put_cred(override_creds(new));
 -              put_cred(new);
        } else {
                error = nfsd_setuser_and_check_port(rqstp, cred, exp);
                if (error)
@@@ -381,6 -382,8 +381,8 @@@ __fh_verify(struct svc_rqst *rqstp
        if (error)
                goto out;
  
+       svc_xprt_set_valid(rqstp->rq_xprt);
        /* Finally, check access permissions. */
        error = nfsd_permission(cred, exp, dentry, access);
  out:
diff --combined net/sunrpc/svc_xprt.c
index aebc0d8ddff5c7083c23f862340dd9b565bed22b,06779b4cdd0a2f78759e05c8e60c5923f410fa00..ae25405d8bd22672a361d1fd3adfdcebb403f90f
@@@ -606,7 -606,8 +606,8 @@@ int svc_port_is_privileged(struct socka
  }
  
  /*
-  * Make sure that we don't have too many active connections. If we have,
+  * Make sure that we don't have too many connections that have not yet
+  * demonstrated that they have access to the NFS server. If we have,
   * something must be dropped. It's not clear what will happen if we allow
   * "too many" connections, but when dealing with network-facing software,
   * we have to code defensively. Here we do that by imposing hard limits.
   * The only somewhat efficient mechanism would be if drop old
   * connections from the same IP first. But right now we don't even
   * record the client IP in svc_sock.
-  *
-  * single-threaded services that expect a lot of clients will probably
-  * need to set sv_maxconn to override the default value which is based
-  * on the number of threads
   */
  static void svc_check_conn_limits(struct svc_serv *serv)
  {
-       unsigned int limit = serv->sv_maxconn ? serv->sv_maxconn :
-                               (serv->sv_nrthreads+3) * 20;
-       if (serv->sv_tmpcnt > limit) {
-               struct svc_xprt *xprt = NULL;
+       if (serv->sv_tmpcnt > XPT_MAX_TMP_CONN) {
+               struct svc_xprt *xprt = NULL, *xprti;
                spin_lock_bh(&serv->sv_lock);
                if (!list_empty(&serv->sv_tempsocks)) {
-                       /* Try to help the admin */
-                       net_notice_ratelimited("%s: too many open connections, consider increasing the %s\n",
-                                              serv->sv_name, serv->sv_maxconn ?
-                                              "max number of connections" :
-                                              "number of threads");
                        /*
                         * Always select the oldest connection. It's not fair,
-                        * but so is life
+                        * but nor is life.
                         */
-                       xprt = list_entry(serv->sv_tempsocks.prev,
-                                         struct svc_xprt,
-                                         xpt_list);
-                       set_bit(XPT_CLOSE, &xprt->xpt_flags);
-                       svc_xprt_get(xprt);
+                       list_for_each_entry_reverse(xprti, &serv->sv_tempsocks,
+                                                   xpt_list) {
+                               if (!test_bit(XPT_PEER_VALID, &xprti->xpt_flags)) {
+                                       xprt = xprti;
+                                       set_bit(XPT_CLOSE, &xprt->xpt_flags);
+                                       svc_xprt_get(xprt);
+                                       break;
+                               }
+                       }
                }
                spin_unlock_bh(&serv->sv_lock);
  
@@@ -671,7 -664,8 +664,7 @@@ static bool svc_alloc_arg(struct svc_rq
        }
  
        for (filled = 0; filled < pages; filled = ret) {
 -              ret = alloc_pages_bulk_array(GFP_KERNEL, pages,
 -                                           rqstp->rq_pages);
 +              ret = alloc_pages_bulk(GFP_KERNEL, pages, rqstp->rq_pages);
                if (ret > filled)
                        /* Made progress, don't sleep yet */
                        continue;
@@@ -1038,7 -1032,8 +1031,8 @@@ static void svc_delete_xprt(struct svc_
  
        spin_lock_bh(&serv->sv_lock);
        list_del_init(&xprt->xpt_list);
-       if (test_bit(XPT_TEMP, &xprt->xpt_flags))
+       if (test_bit(XPT_TEMP, &xprt->xpt_flags) &&
+           !test_bit(XPT_PEER_VALID, &xprt->xpt_flags))
                serv->sv_tmpcnt--;
        spin_unlock_bh(&serv->sv_lock);
  
This page took 0.16893 seconds and 4 git commands to generate.