nfsd4: simplify server xdr->next_page use
[sfrench/cifs-2.6.git] / fs / nfsd / nfs4proc.c
index d543222babf36beb28aba95afd746ddd9298d8ce..6851b003f2a431455e4c7aa1bf3e78d9df4eaa14 100644 (file)
@@ -430,12 +430,12 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                                goto out;
                        break;
                case NFS4_OPEN_CLAIM_PREVIOUS:
-                       open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
                        status = nfs4_check_open_reclaim(&open->op_clientid,
                                                         cstate->minorversion,
                                                         nn);
                        if (status)
                                goto out;
+                       open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
                case NFS4_OPEN_CLAIM_FH:
                case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
                        status = do_open_fhandle(rqstp, cstate, open);
@@ -445,7 +445,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                        break;
                case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
                case NFS4_OPEN_CLAIM_DELEGATE_PREV:
-                       open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
                        dprintk("NFSD: unsupported OPEN claim type %d\n",
                                open->op_claim_type);
                        status = nfserr_notsupp;
@@ -786,7 +785,6 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (!nfsd4_last_compound_op(rqstp))
                rqstp->rq_splice_ok = false;
 
-       nfs4_lock_state();
        /* check stateid */
        if ((status = nfs4_preprocess_stateid_op(SVC_NET(rqstp),
                                                 cstate, &read->rd_stateid,
@@ -794,11 +792,8 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
                goto out;
        }
-       if (read->rd_filp)
-               get_file(read->rd_filp);
        status = nfs_ok;
 out:
-       nfs4_unlock_state();
        read->rd_rqstp = rqstp;
        read->rd_fhp = &cstate->current_fh;
        return status;
@@ -937,10 +932,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        int err;
 
        if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
-               nfs4_lock_state();
                status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), cstate,
                        &setattr->sa_stateid, WR_STATE, NULL);
-               nfs4_unlock_state();
                if (status) {
                        dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
                        return status;
@@ -1006,17 +999,12 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (write->wr_offset >= OFFSET_MAX)
                return nfserr_inval;
 
-       nfs4_lock_state();
        status = nfs4_preprocess_stateid_op(SVC_NET(rqstp),
                                        cstate, stateid, WR_STATE, &filp);
        if (status) {
-               nfs4_unlock_state();
                dprintk("NFSD: nfsd4_write: couldn't process stateid!\n");
                return status;
        }
-       if (filp)
-               get_file(filp);
-       nfs4_unlock_state();
 
        cnt = write->wr_buflen;
        write->wr_how_written = write->wr_stable_how;
@@ -1072,10 +1060,10 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                return nfserr_jukebox;
 
        p = buf;
-       status = nfsd4_encode_fattr(&cstate->current_fh,
+       status = nfsd4_encode_fattr_to_buf(&p, count, &cstate->current_fh,
                                    cstate->current_fh.fh_export,
-                                   cstate->current_fh.fh_dentry, &p,
-                                   count, verify->ve_bmval,
+                                   cstate->current_fh.fh_dentry,
+                                   verify->ve_bmval,
                                    rqstp, 0);
        /*
         * If nfsd4_encode_fattr() ran out of space, assume that's because
@@ -1182,9 +1170,7 @@ struct nfsd4_operation {
 
 static struct nfsd4_operation nfsd4_ops[];
 
-#ifdef NFSD_DEBUG
 static const char *nfsd4_op_name(unsigned opnum);
-#endif
 
 /*
  * Enforce NFSv4.1 COMPOUND ordering rules:
@@ -1226,6 +1212,8 @@ static inline struct nfsd4_operation *OPDESC(struct nfsd4_op *op)
 
 bool nfsd4_cache_this_op(struct nfsd4_op *op)
 {
+       if (op->opnum == OP_ILLEGAL)
+               return false;
        return OPDESC(op)->op_flags & OP_CACHEME;
 }
 
@@ -1262,6 +1250,25 @@ static bool need_wrongsec_check(struct svc_rqst *rqstp)
        return !(nextd->op_flags & OP_HANDLES_WRONGSEC);
 }
 
+static void svcxdr_init_encode(struct svc_rqst *rqstp,
+                              struct nfsd4_compoundres *resp)
+{
+       struct xdr_stream *xdr = &resp->xdr;
+       struct xdr_buf *buf = &rqstp->rq_res;
+       struct kvec *head = buf->head;
+
+       xdr->buf = buf;
+       xdr->iov = head;
+       xdr->p   = head->iov_base + head->iov_len;
+       xdr->end = head->iov_base + PAGE_SIZE - rqstp->rq_auth_slack;
+       /* Tail and page_len should be zero at this point: */
+       buf->len = buf->head[0].iov_len;
+       xdr->scratch.iov_len = 0;
+       xdr->page_ptr = buf->pages - 1;
+       buf->buflen = PAGE_SIZE * (1 + rqstp->rq_page_end - buf->pages)
+               - rqstp->rq_auth_slack;
+}
+
 /*
  * COMPOUND call.
  */
@@ -1275,24 +1282,16 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
        struct nfsd4_compound_state *cstate = &resp->cstate;
        struct svc_fh *current_fh = &cstate->current_fh;
        struct svc_fh *save_fh = &cstate->save_fh;
-       int             slack_bytes;
-       u32             plen = 0;
        __be32          status;
 
-       resp->xbuf = &rqstp->rq_res;
-       resp->p = rqstp->rq_res.head[0].iov_base +
-                                               rqstp->rq_res.head[0].iov_len;
-       resp->tagp = resp->p;
+       svcxdr_init_encode(rqstp, resp);
+       resp->tagp = resp->xdr.p;
        /* reserve space for: taglen, tag, and opcnt */
-       resp->p += 2 + XDR_QUADLEN(args->taglen);
-       resp->end = rqstp->rq_res.head[0].iov_base + PAGE_SIZE;
+       xdr_reserve_space(&resp->xdr, 8 + args->taglen);
        resp->taglen = args->taglen;
        resp->tag = args->tag;
-       resp->opcnt = 0;
        resp->rqstp = rqstp;
        cstate->minorversion = args->minorversion;
-       cstate->replay_owner = NULL;
-       cstate->session = NULL;
        fh_init(current_fh, NFS4_FHSIZE);
        fh_init(save_fh, NFS4_FHSIZE);
        /*
@@ -1332,19 +1331,6 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
                        goto encode_op;
                }
 
-               /* We must be able to encode a successful response to
-                * this operation, with enough room left over to encode a
-                * failed response to the next operation.  If we don't
-                * have enough room, fail with ERR_RESOURCE.
-                */
-               slack_bytes = (char *)resp->end - (char *)resp->p;
-               if (slack_bytes < COMPOUND_SLACK_SPACE
-                               + COMPOUND_ERR_SLACK_SPACE) {
-                       BUG_ON(slack_bytes < COMPOUND_ERR_SLACK_SPACE);
-                       op->status = nfserr_resource;
-                       goto encode_op;
-               }
-
                opdesc = OPDESC(op);
 
                if (!current_fh->fh_dentry) {
@@ -1362,9 +1348,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
 
                /* If op is non-idempotent */
                if (opdesc->op_flags & OP_MODIFIES_SOMETHING) {
-                       plen = opdesc->op_rsize_bop(rqstp, op);
                        /*
-                        * If there's still another operation, make sure
+                        * Don't execute this op if we couldn't encode a
+                        * succesful reply:
+                        */
+                       u32 plen = opdesc->op_rsize_bop(rqstp, op);
+                       /*
+                        * Plus if there's another operation, make sure
                         * we'll have space to at least encode an error:
                         */
                        if (resp->opcnt < args->opcnt)
@@ -1399,7 +1389,7 @@ encode_op:
                }
                if (op->status == nfserr_replay_me) {
                        op->replay = &cstate->replay_owner->so_replay;
-                       nfsd4_encode_replay(resp, op);
+                       nfsd4_encode_replay(&resp->xdr, op);
                        status = op->status = op->replay->rp_status;
                } else {
                        nfsd4_encode_operation(resp, op);
@@ -1438,7 +1428,8 @@ out:
 #define op_encode_change_info_maxsz    (5)
 #define nfs4_fattr_bitmap_maxsz                (4)
 
-#define op_encode_lockowner_maxsz      (1 + XDR_QUADLEN(IDMAP_NAMESZ))
+/* We'll fall back on returning no lockowner if run out of space: */
+#define op_encode_lockowner_maxsz      (0)
 #define op_encode_lock_denied_maxsz    (8 + op_encode_lockowner_maxsz)
 
 #define nfs4_owner_maxsz               (1 + XDR_QUADLEN(IDMAP_NAMESZ))
@@ -1470,6 +1461,49 @@ static inline u32 nfsd4_create_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op
                + nfs4_fattr_bitmap_maxsz) * sizeof(__be32);
 }
 
+/*
+ * Note since this is an idempotent operation we won't insist on failing
+ * the op prematurely if the estimate is too large.  We may turn off splice
+ * reads unnecessarily.
+ */
+static inline u32 nfsd4_getattr_rsize(struct svc_rqst *rqstp,
+                                     struct nfsd4_op *op)
+{
+       u32 *bmap = op->u.getattr.ga_bmval;
+       u32 bmap0 = bmap[0], bmap1 = bmap[1], bmap2 = bmap[2];
+       u32 ret = 0;
+
+       if (bmap0 & FATTR4_WORD0_ACL)
+               return svc_max_payload(rqstp);
+       if (bmap0 & FATTR4_WORD0_FS_LOCATIONS)
+               return svc_max_payload(rqstp);
+
+       if (bmap1 & FATTR4_WORD1_OWNER) {
+               ret += IDMAP_NAMESZ + 4;
+               bmap1 &= ~FATTR4_WORD1_OWNER;
+       }
+       if (bmap1 & FATTR4_WORD1_OWNER_GROUP) {
+               ret += IDMAP_NAMESZ + 4;
+               bmap1 &= ~FATTR4_WORD1_OWNER_GROUP;
+       }
+       if (bmap0 & FATTR4_WORD0_FILEHANDLE) {
+               ret += NFS4_FHSIZE + 4;
+               bmap0 &= ~FATTR4_WORD0_FILEHANDLE;
+       }
+       if (bmap2 & FATTR4_WORD2_SECURITY_LABEL) {
+               ret += NFSD4_MAX_SEC_LABEL_LEN + 12;
+               bmap2 &= ~FATTR4_WORD2_SECURITY_LABEL;
+       }
+       /*
+        * Largest of remaining attributes are 16 bytes (e.g.,
+        * supported_attributes)
+        */
+       ret += 16 * (hweight32(bmap0) + hweight32(bmap1) + hweight32(bmap2));
+       /* bitmask, length */
+       ret += 20;
+       return ret;
+}
+
 static inline u32 nfsd4_link_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
 {
        return (op_encode_hdr_size + op_encode_change_info_maxsz)
@@ -1500,18 +1534,19 @@ static inline u32 nfsd4_read_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
        if (rlen > maxcount)
                rlen = maxcount;
 
-       return (op_encode_hdr_size + 2) * sizeof(__be32) + rlen;
+       return (op_encode_hdr_size + 2 + XDR_QUADLEN(rlen)) * sizeof(__be32);
 }
 
 static inline u32 nfsd4_readdir_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
 {
+       u32 maxcount = svc_max_payload(rqstp);
        u32 rlen = op->u.readdir.rd_maxcount;
 
-       if (rlen > PAGE_SIZE)
-               rlen = PAGE_SIZE;
+       if (rlen > maxcount)
+               rlen = maxcount;
 
-       return (op_encode_hdr_size + op_encode_verifier_maxsz)
-                * sizeof(__be32) + rlen;
+       return (op_encode_hdr_size + op_encode_verifier_maxsz +
+               XDR_QUADLEN(rlen)) * sizeof(__be32);
 }
 
 static inline u32 nfsd4_remove_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
@@ -1526,6 +1561,12 @@ static inline u32 nfsd4_rename_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op
                + op_encode_change_info_maxsz) * sizeof(__be32);
 }
 
+static inline u32 nfsd4_sequence_rsize(struct svc_rqst *rqstp,
+                                      struct nfsd4_op *op)
+{
+       return NFS4_MAX_SESSIONID_LEN + 20;
+}
+
 static inline u32 nfsd4_setattr_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
 {
        return (op_encode_hdr_size + nfs4_fattr_bitmap_maxsz) * sizeof(__be32);
@@ -1539,7 +1580,7 @@ static inline u32 nfsd4_setclientid_rsize(struct svc_rqst *rqstp, struct nfsd4_o
 
 static inline u32 nfsd4_write_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
 {
-       return (op_encode_hdr_size + op_encode_verifier_maxsz) * sizeof(__be32);
+       return (op_encode_hdr_size + 2 + op_encode_verifier_maxsz) * sizeof(__be32);
 }
 
 static inline u32 nfsd4_exchange_id_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
@@ -1607,6 +1648,7 @@ static struct nfsd4_operation nfsd4_ops[] = {
        [OP_GETATTR] = {
                .op_func = (nfsd4op_func)nfsd4_getattr,
                .op_flags = ALLOWED_ON_ABSENT_FS,
+               .op_rsize_bop = nfsd4_getattr_rsize,
                .op_name = "OP_GETATTR",
        },
        [OP_GETFH] = {
@@ -1676,37 +1718,32 @@ static struct nfsd4_operation nfsd4_ops[] = {
        [OP_PUTFH] = {
                .op_func = (nfsd4op_func)nfsd4_putfh,
                .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
-                               | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING
-                               | OP_CLEAR_STATEID,
+                               | OP_IS_PUTFH_LIKE | OP_CLEAR_STATEID,
                .op_name = "OP_PUTFH",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
        },
        [OP_PUTPUBFH] = {
                .op_func = (nfsd4op_func)nfsd4_putrootfh,
                .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
-                               | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING
-                               | OP_CLEAR_STATEID,
+                               | OP_IS_PUTFH_LIKE | OP_CLEAR_STATEID,
                .op_name = "OP_PUTPUBFH",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
        },
        [OP_PUTROOTFH] = {
                .op_func = (nfsd4op_func)nfsd4_putrootfh,
                .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
-                               | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING
-                               | OP_CLEAR_STATEID,
+                               | OP_IS_PUTFH_LIKE | OP_CLEAR_STATEID,
                .op_name = "OP_PUTROOTFH",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
        },
        [OP_READ] = {
                .op_func = (nfsd4op_func)nfsd4_read,
-               .op_flags = OP_MODIFIES_SOMETHING,
                .op_name = "OP_READ",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_read_rsize,
                .op_get_currentstateid = (stateid_getter)nfsd4_get_readstateid,
        },
        [OP_READDIR] = {
                .op_func = (nfsd4op_func)nfsd4_readdir,
-               .op_flags = OP_MODIFIES_SOMETHING,
                .op_name = "OP_READDIR",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_readdir_rsize,
        },
@@ -1864,14 +1901,33 @@ static struct nfsd4_operation nfsd4_ops[] = {
        },
 };
 
-#ifdef NFSD_DEBUG
+int nfsd4_max_reply(struct svc_rqst *rqstp, struct nfsd4_op *op)
+{
+       struct nfsd4_operation *opdesc;
+       nfsd4op_rsize estimator;
+
+       if (op->opnum == OP_ILLEGAL)
+               return op_encode_hdr_size * sizeof(__be32);
+       opdesc = OPDESC(op);
+       estimator = opdesc->op_rsize_bop;
+       return estimator ? estimator(rqstp, op) : PAGE_SIZE;
+}
+
+void warn_on_nonidempotent_op(struct nfsd4_op *op)
+{
+       if (OPDESC(op)->op_flags & OP_MODIFIES_SOMETHING) {
+               pr_err("unable to encode reply to nonidempotent op %d (%s)\n",
+                       op->opnum, nfsd4_op_name(op->opnum));
+               WARN_ON_ONCE(1);
+       }
+}
+
 static const char *nfsd4_op_name(unsigned opnum)
 {
        if (opnum < ARRAY_SIZE(nfsd4_ops))
                return nfsd4_ops[opnum].op_name;
        return "unknown_operation";
 }
-#endif
 
 #define nfsd4_voidres                  nfsd4_voidargs
 struct nfsd4_voidargs { int dummy; };