NFSv4.1: Ask for no delegation on OPEN if using O_DIRECT
authorTrond Myklebust <trond.myklebust@primarydata.com>
Fri, 30 Jan 2015 19:21:14 +0000 (14:21 -0500)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Wed, 4 Feb 2015 15:35:32 +0000 (10:35 -0500)
If we're using NFSv4.1, then we have the ability to let the server know
whether or not we believe that returning a delegation as part of our OPEN
request would be useful.
The feature needs to be used with care, since the client sending the request
doesn't necessarily know how other clients are using that file, and how
they may be affected by the delegation.
For this reason, our initial use of the feature will be to let the server
know when the client believes that handing out a delegation would not be
useful.
The first application for this function is when opening the file using
O_DIRECT.

Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
fs/nfs/nfs4proc.c
fs/nfs/nfs4xdr.c
include/linux/nfs_xdr.h

index 6e1c9b2d92c59ada0de0bc6e191c889277b0bbc5..cd4295d84d54b6a4094cfccc2607144a6a942343 100644 (file)
@@ -940,6 +940,31 @@ static bool nfs4_clear_cap_atomic_open_v1(struct nfs_server *server,
        return true;
 }
 
+static u32
+nfs4_map_atomic_open_share(struct nfs_server *server,
+               fmode_t fmode, int openflags)
+{
+       u32 res = 0;
+
+       switch (fmode & (FMODE_READ | FMODE_WRITE)) {
+       case FMODE_READ:
+               res = NFS4_SHARE_ACCESS_READ;
+               break;
+       case FMODE_WRITE:
+               res = NFS4_SHARE_ACCESS_WRITE;
+               break;
+       case FMODE_READ|FMODE_WRITE:
+               res = NFS4_SHARE_ACCESS_BOTH;
+       }
+       if (!(server->caps & NFS_CAP_ATOMIC_OPEN_V1))
+               goto out;
+       /* Want no delegation if we're using O_DIRECT */
+       if (openflags & O_DIRECT)
+               res |= NFS4_SHARE_WANT_NO_DELEG;
+out:
+       return res;
+}
+
 static enum open_claim_type4
 nfs4_map_atomic_open_claim(struct nfs_server *server,
                enum open_claim_type4 claim)
@@ -1002,6 +1027,8 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        atomic_inc(&sp->so_count);
        p->o_arg.open_flags = flags;
        p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE);
+       p->o_arg.share_access = nfs4_map_atomic_open_share(server,
+                       fmode, flags);
        /* don't put an ACCESS op in OPEN compound if O_EXCL, because ACCESS
         * will return permission denied for all bits until close */
        if (!(flags & O_EXCL)) {
@@ -2695,6 +2722,9 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
                        goto out_wait;
                    }
        }
+       calldata->arg.share_access =
+               nfs4_map_atomic_open_share(NFS_SERVER(inode),
+                               calldata->arg.fmode, 0);
 
        nfs_fattr_init(calldata->res.fattr);
        calldata->timestamp = jiffies;
index a2329d69502ba7d51a62103f90c7f67f435f4347..e23a0a664e12d5130162bc320e80a57a5264bc9b 100644 (file)
@@ -1351,24 +1351,12 @@ static void encode_lookup(struct xdr_stream *xdr, const struct qstr *name, struc
        encode_string(xdr, name->len, name->name);
 }
 
-static void encode_share_access(struct xdr_stream *xdr, fmode_t fmode)
+static void encode_share_access(struct xdr_stream *xdr, u32 share_access)
 {
        __be32 *p;
 
        p = reserve_space(xdr, 8);
-       switch (fmode & (FMODE_READ|FMODE_WRITE)) {
-       case FMODE_READ:
-               *p++ = cpu_to_be32(NFS4_SHARE_ACCESS_READ);
-               break;
-       case FMODE_WRITE:
-               *p++ = cpu_to_be32(NFS4_SHARE_ACCESS_WRITE);
-               break;
-       case FMODE_READ|FMODE_WRITE:
-               *p++ = cpu_to_be32(NFS4_SHARE_ACCESS_BOTH);
-               break;
-       default:
-               *p++ = cpu_to_be32(0);
-       }
+       *p++ = cpu_to_be32(share_access);
        *p = cpu_to_be32(0);            /* for linux, share_deny = 0 always */
 }
 
@@ -1380,7 +1368,7 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena
  * owner 4 = 32
  */
        encode_nfs4_seqid(xdr, arg->seqid);
-       encode_share_access(xdr, arg->fmode);
+       encode_share_access(xdr, arg->share_access);
        p = reserve_space(xdr, 36);
        p = xdr_encode_hyper(p, arg->clientid);
        *p++ = cpu_to_be32(24);
@@ -1535,7 +1523,7 @@ static void encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_close
        encode_op_hdr(xdr, OP_OPEN_DOWNGRADE, decode_open_downgrade_maxsz, hdr);
        encode_nfs4_stateid(xdr, &arg->stateid);
        encode_nfs4_seqid(xdr, arg->seqid);
-       encode_share_access(xdr, arg->fmode);
+       encode_share_access(xdr, arg->share_access);
 }
 
 static void
@@ -4935,20 +4923,13 @@ out_overflow:
        return -EIO;
 }
 
-static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
+static int decode_rw_delegation(struct xdr_stream *xdr,
+               uint32_t delegation_type,
+               struct nfs_openres *res)
 {
        __be32 *p;
-       uint32_t delegation_type;
        int status;
 
-       p = xdr_inline_decode(xdr, 4);
-       if (unlikely(!p))
-               goto out_overflow;
-       delegation_type = be32_to_cpup(p);
-       if (delegation_type == NFS4_OPEN_DELEGATE_NONE) {
-               res->delegation_type = 0;
-               return 0;
-       }
        status = decode_stateid(xdr, &res->delegation);
        if (unlikely(status))
                return status;
@@ -4972,6 +4953,52 @@ out_overflow:
        return -EIO;
 }
 
+static int decode_no_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
+{
+       __be32 *p;
+       uint32_t why_no_delegation;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               goto out_overflow;
+       why_no_delegation = be32_to_cpup(p);
+       switch (why_no_delegation) {
+               case WND4_CONTENTION:
+               case WND4_RESOURCE:
+                       xdr_inline_decode(xdr, 4);
+                       /* Ignore for now */
+       }
+       return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
+{
+       __be32 *p;
+       uint32_t delegation_type;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               goto out_overflow;
+       delegation_type = be32_to_cpup(p);
+       res->delegation_type = 0;
+       switch (delegation_type) {
+       case NFS4_OPEN_DELEGATE_NONE:
+               return 0;
+       case NFS4_OPEN_DELEGATE_READ:
+       case NFS4_OPEN_DELEGATE_WRITE:
+               return decode_rw_delegation(xdr, delegation_type, res);
+       case NFS4_OPEN_DELEGATE_NONE_EXT:
+               return decode_no_delegation(xdr, res);
+       }
+       return -EIO;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
 static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
 {
        __be32 *p;
index 81401125ab2ddecf3912ff6d22629900df0fed8a..2c35e2affa6f0b5705acd729c3fb27f618568307 100644 (file)
@@ -326,6 +326,7 @@ struct nfs_openargs {
        struct nfs_seqid *      seqid;
        int                     open_flags;
        fmode_t                 fmode;
+       u32                     share_access;
        u32                     access;
        __u64                   clientid;
        struct stateowner_id    id;
@@ -393,6 +394,7 @@ struct nfs_closeargs {
        nfs4_stateid            stateid;
        struct nfs_seqid *      seqid;
        fmode_t                 fmode;
+       u32                     share_access;
        const u32 *             bitmask;
 };