]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/nfsd/nfs4xdr.c
nfsd4: drop most stateowner refcounting
[karo-tx-linux.git] / fs / nfsd / nfs4xdr.c
index 990181103214de4f45d305753aea2daa064f44c0..c4dcba3aac1f3070f35278b77e98990fd635326e 100644 (file)
 #include <linux/namei.h>
 #include <linux/statfs.h>
 #include <linux/utsname.h>
+#include <linux/pagemap.h>
 #include <linux/sunrpc/svcauth_gss.h>
 
 #include "idmap.h"
 #include "acl.h"
 #include "xdr4.h"
 #include "vfs.h"
-
+#include "state.h"
+#include "cache.h"
 
 #define NFSDDBG_FACILITY               NFSDDBG_XDR
 
@@ -131,6 +133,22 @@ xdr_error:                                 \
        }                                       \
 } while (0)
 
+static void save_buf(struct nfsd4_compoundargs *argp, struct nfsd4_saved_compoundargs *savep)
+{
+       savep->p        = argp->p;
+       savep->end      = argp->end;
+       savep->pagelen  = argp->pagelen;
+       savep->pagelist = argp->pagelist;
+}
+
+static void restore_buf(struct nfsd4_compoundargs *argp, struct nfsd4_saved_compoundargs *savep)
+{
+       argp->p        = savep->p;
+       argp->end      = savep->end;
+       argp->pagelen  = savep->pagelen;
+       argp->pagelist = savep->pagelist;
+}
+
 static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
 {
        /* We want more bytes than seem to be available.
@@ -438,7 +456,6 @@ nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
 {
        DECODE_HEAD;
 
-       close->cl_stateowner = NULL;
        READ_BUF(4);
        READ32(close->cl_seqid);
        return nfsd4_decode_stateid(argp, &close->cl_stateid);
@@ -533,7 +550,6 @@ nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
 {
        DECODE_HEAD;
 
-       lock->lk_replay_owner = NULL;
        /*
        * type, reclaim(boolean), offset, length, new_lock_owner(boolean)
        */
@@ -593,7 +609,6 @@ nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku)
 {
        DECODE_HEAD;
 
-       locku->lu_stateowner = NULL;
        READ_BUF(8);
        READ32(locku->lu_type);
        if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
@@ -721,7 +736,6 @@ nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_con
 {
        DECODE_HEAD;
                    
-       open_conf->oc_stateowner = NULL;
        status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid);
        if (status)
                return status;
@@ -736,7 +750,6 @@ nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_d
 {
        DECODE_HEAD;
                    
-       open_down->od_stateowner = NULL;
        status = nfsd4_decode_stateid(argp, &open_down->od_stateid);
        if (status)
                return status;
@@ -1245,6 +1258,19 @@ nfsd4_decode_destroy_session(struct nfsd4_compoundargs *argp,
        DECODE_TAIL;
 }
 
+static __be32
+nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
+                         struct nfsd4_free_stateid *free_stateid)
+{
+       DECODE_HEAD;
+
+       READ_BUF(sizeof(stateid_t));
+       READ32(free_stateid->fr_stateid.si_generation);
+       COPYMEM(&free_stateid->fr_stateid.si_opaque, sizeof(stateid_opaque_t));
+
+       DECODE_TAIL;
+}
+
 static __be32
 nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
                      struct nfsd4_sequence *seq)
@@ -1261,6 +1287,40 @@ nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
        DECODE_TAIL;
 }
 
+static __be32
+nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_stateid *test_stateid)
+{
+       unsigned int nbytes;
+       stateid_t si;
+       int i;
+       __be32 *p;
+       __be32 status;
+
+       READ_BUF(4);
+       test_stateid->ts_num_ids = ntohl(*p++);
+
+       nbytes = test_stateid->ts_num_ids * sizeof(stateid_t);
+       if (nbytes > (u32)((char *)argp->end - (char *)argp->p))
+               goto xdr_error;
+
+       test_stateid->ts_saved_args = argp;
+       save_buf(argp, &test_stateid->ts_savedp);
+
+       for (i = 0; i < test_stateid->ts_num_ids; i++) {
+               status = nfsd4_decode_stateid(argp, &si);
+               if (status)
+                       return status;
+       }
+
+       status = 0;
+out:
+       return status;
+xdr_error:
+       dprintk("NFSD: xdr error (%s:%d)\n", __FILE__, __LINE__);
+       status = nfserr_bad_xdr;
+       goto out;
+}
+
 static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc)
 {
        DECODE_HEAD;
@@ -1370,7 +1430,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
        [OP_EXCHANGE_ID]        = (nfsd4_dec)nfsd4_decode_exchange_id,
        [OP_CREATE_SESSION]     = (nfsd4_dec)nfsd4_decode_create_session,
        [OP_DESTROY_SESSION]    = (nfsd4_dec)nfsd4_decode_destroy_session,
-       [OP_FREE_STATEID]       = (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_FREE_STATEID]       = (nfsd4_dec)nfsd4_decode_free_stateid,
        [OP_GET_DIR_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp,
        [OP_GETDEVICEINFO]      = (nfsd4_dec)nfsd4_decode_notsupp,
        [OP_GETDEVICELIST]      = (nfsd4_dec)nfsd4_decode_notsupp,
@@ -1380,7 +1440,7 @@ static nfsd4_dec nfsd41_dec_ops[] = {
        [OP_SECINFO_NO_NAME]    = (nfsd4_dec)nfsd4_decode_secinfo_no_name,
        [OP_SEQUENCE]           = (nfsd4_dec)nfsd4_decode_sequence,
        [OP_SET_SSV]            = (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_TEST_STATEID]       = (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_TEST_STATEID]       = (nfsd4_dec)nfsd4_decode_test_stateid,
        [OP_WANT_DELEGATION]    = (nfsd4_dec)nfsd4_decode_notsupp,
        [OP_DESTROY_CLIENTID]   = (nfsd4_dec)nfsd4_decode_notsupp,
        [OP_RECLAIM_COMPLETE]   = (nfsd4_dec)nfsd4_decode_reclaim_complete,
@@ -1402,6 +1462,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
        DECODE_HEAD;
        struct nfsd4_op *op;
        struct nfsd4_minorversion_ops *ops;
+       bool cachethis = false;
        int i;
 
        /*
@@ -1483,7 +1544,16 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                        argp->opcnt = i+1;
                        break;
                }
+               /*
+                * We'll try to cache the result in the DRC if any one
+                * op in the compound wants to be cached:
+                */
+               cachethis |= nfsd4_cache_this_op(op);
        }
+       /* Sessions make the DRC unnecessary: */
+       if (argp->minorversion)
+               cachethis = false;
+       argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE;
 
        DECODE_TAIL;
 }
@@ -1555,15 +1625,19 @@ static void write_cinfo(__be32 **p, struct nfsd4_change_info *c)
  * we know whether the error to be returned is a sequence id mutating error.
  */
 
-#define ENCODE_SEQID_OP_TAIL(stateowner) do {                  \
-       if (seqid_mutating_err(nfserr) && stateowner) {         \
-               stateowner->so_seqid++;                         \
-               stateowner->so_replay.rp_status = nfserr;       \
-               stateowner->so_replay.rp_buflen =               \
-                         (((char *)(resp)->p - (char *)save)); \
-               memcpy(stateowner->so_replay.rp_buf, save,      \
-                       stateowner->so_replay.rp_buflen);       \
-       } } while (0);
+static void encode_seqid_op_tail(struct nfsd4_compoundres *resp, __be32 *save, __be32 nfserr)
+{
+       struct nfs4_stateowner *stateowner = resp->cstate.replay_owner;
+
+       if (seqid_mutating_err(ntohl(nfserr)) && stateowner) {
+               stateowner->so_seqid++;
+               stateowner->so_replay.rp_status = nfserr;
+               stateowner->so_replay.rp_buflen =
+                         (char *)resp->p - (char *)save;
+               memcpy(stateowner->so_replay.rp_buf, save,
+                       stateowner->so_replay.rp_buflen);
+       }
+}
 
 /* Encode as an array of strings the string given with components
  * separated @sep.
@@ -1685,12 +1759,19 @@ static __be32 nfsd4_encode_fs_locations(struct svc_rqst *rqstp,
        return 0;
 }
 
-static u32 nfs4_ftypes[16] = {
-        NF4BAD,  NF4FIFO, NF4CHR, NF4BAD,
-        NF4DIR,  NF4BAD,  NF4BLK, NF4BAD,
-        NF4REG,  NF4BAD,  NF4LNK, NF4BAD,
-        NF4SOCK, NF4BAD,  NF4LNK, NF4BAD,
-};
+static u32 nfs4_file_type(umode_t mode)
+{
+       switch (mode & S_IFMT) {
+       case S_IFIFO:   return NF4FIFO;
+       case S_IFCHR:   return NF4CHR;
+       case S_IFDIR:   return NF4DIR;
+       case S_IFBLK:   return NF4BLK;
+       case S_IFLNK:   return NF4LNK;
+       case S_IFREG:   return NF4REG;
+       case S_IFSOCK:  return NF4SOCK;
+       default:        return NF4BAD;
+       };
+}
 
 static __be32
 nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
@@ -1879,7 +1960,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        if (bmval0 & FATTR4_WORD0_TYPE) {
                if ((buflen -= 4) < 0)
                        goto out_resource;
-               dummy = nfs4_ftypes[(stat.mode & S_IFMT) >> 12];
+               dummy = nfs4_file_type(stat.mode);
                if (dummy == NF4BAD)
                        goto out_serverfault;
                WRITE32(dummy);
@@ -2413,7 +2494,7 @@ nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_c
        if (!nfserr)
                nfsd4_encode_stateid(resp, &close->cl_stateid);
 
-       ENCODE_SEQID_OP_TAIL(close->cl_stateowner);
+       encode_seqid_op_tail(resp, save, nfserr);
        return nfserr;
 }
 
@@ -2489,17 +2570,18 @@ nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh
 static void
 nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld)
 {
+       struct xdr_netobj *conf = &ld->ld_owner;
        __be32 *p;
 
-       RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop ? ld->ld_sop->so_owner.len : 0));
+       RESERVE_SPACE(32 + XDR_LEN(conf->len));
        WRITE64(ld->ld_start);
        WRITE64(ld->ld_length);
        WRITE32(ld->ld_type);
-       if (ld->ld_sop) {
+       if (conf->len) {
                WRITEMEM(&ld->ld_clientid, 8);
-               WRITE32(ld->ld_sop->so_owner.len);
-               WRITEMEM(ld->ld_sop->so_owner.data, ld->ld_sop->so_owner.len);
-               kref_put(&ld->ld_sop->so_ref, nfs4_free_stateowner);
+               WRITE32(conf->len);
+               WRITEMEM(conf->data, conf->len);
+               kfree(conf->data);
        }  else {  /* non - nfsv4 lock in conflict, no clientid nor owner */
                WRITE64((u64)0); /* clientid */
                WRITE32(0); /* length of owner name */
@@ -2517,7 +2599,7 @@ nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lo
        else if (nfserr == nfserr_denied)
                nfsd4_encode_lock_denied(resp, &lock->lk_denied);
 
-       ENCODE_SEQID_OP_TAIL(lock->lk_replay_owner);
+       encode_seqid_op_tail(resp, save, nfserr);
        return nfserr;
 }
 
@@ -2537,7 +2619,7 @@ nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_l
        if (!nfserr)
                nfsd4_encode_stateid(resp, &locku->lu_stateid);
 
-       ENCODE_SEQID_OP_TAIL(locku->lu_stateowner);
+       encode_seqid_op_tail(resp, save, nfserr);
        return nfserr;
 }
 
@@ -2618,7 +2700,7 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op
        }
        /* XXX save filehandle here */
 out:
-       ENCODE_SEQID_OP_TAIL(open->op_stateowner);
+       encode_seqid_op_tail(resp, save, nfserr);
        return nfserr;
 }
 
@@ -2630,7 +2712,7 @@ nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct
        if (!nfserr)
                nfsd4_encode_stateid(resp, &oc->oc_resp_stateid);
 
-       ENCODE_SEQID_OP_TAIL(oc->oc_stateowner);
+       encode_seqid_op_tail(resp, save, nfserr);
        return nfserr;
 }
 
@@ -2642,7 +2724,7 @@ nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struc
        if (!nfserr)
                nfsd4_encode_stateid(resp, &od->od_stateid);
 
-       ENCODE_SEQID_OP_TAIL(od->od_stateowner);
+       encode_seqid_op_tail(resp, save, nfserr);
        return nfserr;
 }
 
@@ -2684,8 +2766,6 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
                        read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen,
                        &maxcount);
 
-       if (nfserr == nfserr_symlink)
-               nfserr = nfserr_inval;
        if (nfserr)
                return nfserr;
        eof = (read->rd_offset + maxcount >=
@@ -2811,8 +2891,6 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
            readdir->common.err == nfserr_toosmall &&
            readdir->buffer == page) 
                nfserr = nfserr_toosmall;
-       if (nfserr == nfserr_symlink)
-               nfserr = nfserr_notdir;
        if (nfserr)
                goto err_no_verf;
 
@@ -3115,6 +3193,21 @@ nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, int nfserr,
        return nfserr;
 }
 
+static __be32
+nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, int nfserr,
+                         struct nfsd4_free_stateid *free_stateid)
+{
+       __be32 *p;
+
+       if (nfserr)
+               return nfserr;
+
+       RESERVE_SPACE(4);
+       WRITE32(nfserr);
+       ADJUST_ARGS();
+       return nfserr;
+}
+
 static __be32
 nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
                      struct nfsd4_sequence *seq)
@@ -3128,9 +3221,9 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
        WRITEMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN);
        WRITE32(seq->seqid);
        WRITE32(seq->slotid);
-       WRITE32(seq->maxslots);
-       /* For now: target_maxslots = maxslots */
-       WRITE32(seq->maxslots);
+       /* Note slotid's are numbered from zero: */
+       WRITE32(seq->maxslots - 1); /* sr_highest_slotid */
+       WRITE32(seq->maxslots - 1); /* sr_target_highest_slotid */
        WRITE32(seq->status_flags);
 
        ADJUST_ARGS();
@@ -3138,6 +3231,36 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
        return 0;
 }
 
+__be32
+nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr,
+                         struct nfsd4_test_stateid *test_stateid)
+{
+       struct nfsd4_compoundargs *argp;
+       stateid_t si;
+       __be32 *p;
+       int i;
+       int valid;
+
+       restore_buf(test_stateid->ts_saved_args, &test_stateid->ts_savedp);
+       argp = test_stateid->ts_saved_args;
+
+       RESERVE_SPACE(4);
+       *p++ = htonl(test_stateid->ts_num_ids);
+       resp->p = p;
+
+       nfs4_lock_state();
+       for (i = 0; i < test_stateid->ts_num_ids; i++) {
+               nfsd4_decode_stateid(argp, &si);
+               valid = nfs4_validate_stateid(&si, test_stateid->ts_has_session);
+               RESERVE_SPACE(4);
+               *p++ = htonl(valid);
+               resp->p = p;
+       }
+       nfs4_unlock_state();
+
+       return nfserr;
+}
+
 static __be32
 nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p)
 {
@@ -3196,7 +3319,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
        [OP_EXCHANGE_ID]        = (nfsd4_enc)nfsd4_encode_exchange_id,
        [OP_CREATE_SESSION]     = (nfsd4_enc)nfsd4_encode_create_session,
        [OP_DESTROY_SESSION]    = (nfsd4_enc)nfsd4_encode_destroy_session,
-       [OP_FREE_STATEID]       = (nfsd4_enc)nfsd4_encode_noop,
+       [OP_FREE_STATEID]       = (nfsd4_enc)nfsd4_encode_free_stateid,
        [OP_GET_DIR_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop,
        [OP_GETDEVICEINFO]      = (nfsd4_enc)nfsd4_encode_noop,
        [OP_GETDEVICELIST]      = (nfsd4_enc)nfsd4_encode_noop,
@@ -3206,7 +3329,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
        [OP_SECINFO_NO_NAME]    = (nfsd4_enc)nfsd4_encode_secinfo_no_name,
        [OP_SEQUENCE]           = (nfsd4_enc)nfsd4_encode_sequence,
        [OP_SET_SSV]            = (nfsd4_enc)nfsd4_encode_noop,
-       [OP_TEST_STATEID]       = (nfsd4_enc)nfsd4_encode_noop,
+       [OP_TEST_STATEID]       = (nfsd4_enc)nfsd4_encode_test_stateid,
        [OP_WANT_DELEGATION]    = (nfsd4_enc)nfsd4_encode_noop,
        [OP_DESTROY_CLIENTID]   = (nfsd4_enc)nfsd4_encode_noop,
        [OP_RECLAIM_COMPLETE]   = (nfsd4_enc)nfsd4_encode_noop,
@@ -3319,8 +3442,11 @@ nfs4svc_encode_voidres(struct svc_rqst *rqstp, __be32 *p, void *dummy)
         return xdr_ressize_check(rqstp, p);
 }
 
-void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args)
+int nfsd4_release_compoundargs(void *rq, __be32 *p, void *resp)
 {
+       struct svc_rqst *rqstp = rq;
+       struct nfsd4_compoundargs *args = rqstp->rq_argp;
+
        if (args->ops != args->iops) {
                kfree(args->ops);
                args->ops = args->iops;
@@ -3333,13 +3459,12 @@ void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args)
                tb->release(tb->buf);
                kfree(tb);
        }
+       return 1;
 }
 
 int
 nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundargs *args)
 {
-       __be32 status;
-
        args->p = p;
        args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len;
        args->pagelist = rqstp->rq_arg.pages;
@@ -3349,11 +3474,7 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_comp
        args->ops = args->iops;
        args->rqstp = rqstp;
 
-       status = nfsd4_decode_compound(args);
-       if (status) {
-               nfsd4_release_compoundargs(args);
-       }
-       return !status;
+       return !nfsd4_decode_compound(args);
 }
 
 int