NFS: Avoid PUTROOTFH when managing leases
[deliverable/linux.git] / fs / nfs / nfs4proc.c
index 22c80f9ed824639d54401dae711392137e60a627..510a7dd84c464d6248df3f05cb6f3a2db6a6fcbd 100644 (file)
@@ -107,6 +107,8 @@ static int nfs4_map_errors(int err)
                return -EPROTONOSUPPORT;
        case -NFS4ERR_ACCESS:
                return -EACCES;
+       case -NFS4ERR_FILE_OPEN:
+               return -EBUSY;
        default:
                dprintk("%s could not handle NFSv4 error %d\n",
                                __func__, -err);
@@ -767,6 +769,35 @@ struct nfs4_opendata {
        int cancelled;
 };
 
+static bool nfs4_clear_cap_atomic_open_v1(struct nfs_server *server,
+               int err, struct nfs4_exception *exception)
+{
+       if (err != -EINVAL)
+               return false;
+       if (!(server->caps & NFS_CAP_ATOMIC_OPEN_V1))
+               return false;
+       server->caps &= ~NFS_CAP_ATOMIC_OPEN_V1;
+       exception->retry = 1;
+       return true;
+}
+
+static enum open_claim_type4
+nfs4_map_atomic_open_claim(struct nfs_server *server,
+               enum open_claim_type4 claim)
+{
+       if (server->caps & NFS_CAP_ATOMIC_OPEN_V1)
+               return claim;
+       switch (claim) {
+       default:
+               return claim;
+       case NFS4_OPEN_CLAIM_FH:
+               return NFS4_OPEN_CLAIM_NULL;
+       case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
+               return NFS4_OPEN_CLAIM_DELEGATE_CUR;
+       case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
+               return NFS4_OPEN_CLAIM_DELEGATE_PREV;
+       }
+}
 
 static void nfs4_init_opendata_res(struct nfs4_opendata *p)
 {
@@ -782,6 +813,7 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p)
 static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
                struct nfs4_state_owner *sp, fmode_t fmode, int flags,
                const struct iattr *attrs,
+               enum open_claim_type4 claim,
                gfp_t gfp_mask)
 {
        struct dentry *parent = dget_parent(dentry);
@@ -800,7 +832,6 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        p->dir = parent;
        p->owner = sp;
        atomic_inc(&sp->so_count);
-       p->o_arg.fh = NFS_FH(dir);
        p->o_arg.open_flags = flags;
        p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE);
        /* don't put an ACCESS op in OPEN compound if O_EXCL, because ACCESS
@@ -818,7 +849,19 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        p->o_arg.server = server;
        p->o_arg.bitmask = server->attr_bitmask;
        p->o_arg.open_bitmap = &nfs4_fattr_bitmap[0];
-       p->o_arg.claim = NFS4_OPEN_CLAIM_NULL;
+       p->o_arg.claim = nfs4_map_atomic_open_claim(server, claim);
+       switch (p->o_arg.claim) {
+       case NFS4_OPEN_CLAIM_NULL:
+       case NFS4_OPEN_CLAIM_DELEGATE_CUR:
+       case NFS4_OPEN_CLAIM_DELEGATE_PREV:
+               p->o_arg.fh = NFS_FH(dir);
+               break;
+       case NFS4_OPEN_CLAIM_PREVIOUS:
+       case NFS4_OPEN_CLAIM_FH:
+       case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
+       case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
+               p->o_arg.fh = NFS_FH(dentry->d_inode);
+       }
        if (attrs != NULL && attrs->ia_valid != 0) {
                __be32 verf[2];
 
@@ -1200,11 +1243,13 @@ static struct nfs_open_context *nfs4_state_find_open_context(struct nfs4_state *
        return ERR_PTR(-ENOENT);
 }
 
-static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context *ctx, struct nfs4_state *state)
+static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context *ctx,
+               struct nfs4_state *state, enum open_claim_type4 claim)
 {
        struct nfs4_opendata *opendata;
 
-       opendata = nfs4_opendata_alloc(ctx->dentry, state->owner, 0, 0, NULL, GFP_NOFS);
+       opendata = nfs4_opendata_alloc(ctx->dentry, state->owner, 0, 0,
+                       NULL, claim, GFP_NOFS);
        if (opendata == NULL)
                return ERR_PTR(-ENOMEM);
        opendata->state = state;
@@ -1290,11 +1335,10 @@ static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
        fmode_t delegation_type = 0;
        int status;
 
-       opendata = nfs4_open_recoverdata_alloc(ctx, state);
+       opendata = nfs4_open_recoverdata_alloc(ctx, state,
+                       NFS4_OPEN_CLAIM_PREVIOUS);
        if (IS_ERR(opendata))
                return PTR_ERR(opendata);
-       opendata->o_arg.claim = NFS4_OPEN_CLAIM_PREVIOUS;
-       opendata->o_arg.fh = NFS_FH(state->inode);
        rcu_read_lock();
        delegation = rcu_dereference(NFS_I(state->inode)->delegation);
        if (delegation != NULL && test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) != 0)
@@ -1313,6 +1357,8 @@ static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
        int err;
        do {
                err = _nfs4_do_open_reclaim(ctx, state);
+               if (nfs4_clear_cap_atomic_open_v1(server, err, &exception))
+                       continue;
                if (err != -NFS4ERR_DELAY)
                        break;
                nfs4_handle_exception(server, err, &exception);
@@ -1327,7 +1373,7 @@ static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *sta
 
        ctx = nfs4_state_find_open_context(state);
        if (IS_ERR(ctx))
-               return PTR_ERR(ctx);
+               return -EAGAIN;
        ret = nfs4_do_open_reclaim(ctx, state);
        put_nfs_open_context(ctx);
        return ret;
@@ -1338,10 +1384,10 @@ static int _nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs
        struct nfs4_opendata *opendata;
        int ret;
 
-       opendata = nfs4_open_recoverdata_alloc(ctx, state);
+       opendata = nfs4_open_recoverdata_alloc(ctx, state,
+                       NFS4_OPEN_CLAIM_DELEG_CUR_FH);
        if (IS_ERR(opendata))
                return PTR_ERR(opendata);
-       opendata->o_arg.claim = NFS4_OPEN_CLAIM_DELEGATE_CUR;
        nfs4_stateid_copy(&opendata->o_arg.u.delegation, stateid);
        ret = nfs4_open_recover(opendata, state);
        nfs4_opendata_put(opendata);
@@ -1727,7 +1773,8 @@ static int _nfs4_open_expired(struct nfs_open_context *ctx, struct nfs4_state *s
        struct nfs4_opendata *opendata;
        int ret;
 
-       opendata = nfs4_open_recoverdata_alloc(ctx, state);
+       opendata = nfs4_open_recoverdata_alloc(ctx, state,
+                       NFS4_OPEN_CLAIM_FH);
        if (IS_ERR(opendata))
                return PTR_ERR(opendata);
        ret = nfs4_open_recover(opendata, state);
@@ -1745,6 +1792,8 @@ static int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4_state
 
        do {
                err = _nfs4_open_expired(ctx, state);
+               if (nfs4_clear_cap_atomic_open_v1(server, err, &exception))
+                       continue;
                switch (err) {
                default:
                        goto out;
@@ -1765,7 +1814,7 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta
 
        ctx = nfs4_state_find_open_context(state);
        if (IS_ERR(ctx))
-               return PTR_ERR(ctx);
+               return -EAGAIN;
        ret = nfs4_do_open_expired(ctx, state);
        put_nfs_open_context(ctx);
        return ret;
@@ -1887,10 +1936,8 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
        if (ret != 0)
                goto out;
 
-       if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) {
+       if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
                nfs4_schedule_stateid_recovery(server, state);
-               nfs4_wait_clnt_recover(server->nfs_client);
-       }
        *res = state;
 out:
        return ret;
@@ -1912,6 +1959,7 @@ static int _nfs4_do_open(struct inode *dir,
        struct nfs4_state     *state = NULL;
        struct nfs_server       *server = NFS_SERVER(dir);
        struct nfs4_opendata *opendata;
+       enum open_claim_type4 claim = NFS4_OPEN_CLAIM_NULL;
        int status;
 
        /* Protect against reboot recovery conflicts */
@@ -1927,7 +1975,10 @@ static int _nfs4_do_open(struct inode *dir,
        if (dentry->d_inode != NULL)
                nfs4_return_incompatible_delegation(dentry->d_inode, fmode);
        status = -ENOMEM;
-       opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags, sattr, GFP_KERNEL);
+       if (dentry->d_inode)
+               claim = NFS4_OPEN_CLAIM_FH;
+       opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags, sattr,
+                       claim, GFP_KERNEL);
        if (opendata == NULL)
                goto err_put_state_owner;
 
@@ -1985,6 +2036,7 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,
                                        struct rpc_cred *cred,
                                        struct nfs4_threshold **ctx_th)
 {
+       struct nfs_server *server = NFS_SERVER(dir);
        struct nfs4_exception exception = { };
        struct nfs4_state *res;
        int status;
@@ -2028,7 +2080,9 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,
                        exception.retry = 1;
                        continue;
                }
-               res = ERR_PTR(nfs4_handle_exception(NFS_SERVER(dir),
+               if (nfs4_clear_cap_atomic_open_v1(server, status, &exception))
+                       continue;
+               res = ERR_PTR(nfs4_handle_exception(server,
                                        status, &exception));
        } while (exception.retry);
        return res;
@@ -2452,7 +2506,7 @@ static int nfs4_lookup_root_sec(struct nfs_server *server, struct nfs_fh *fhandl
 
        auth = rpcauth_create(flavor, server->client);
        if (IS_ERR(auth)) {
-               ret = -EIO;
+               ret = -EACCES;
                goto out;
        }
        ret = nfs4_lookup_root(server, fhandle, info);
@@ -2493,24 +2547,36 @@ static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
        return status;
 }
 
-/*
- * get the file handle for the "/" directory on the server
+static int nfs4_do_find_root_sec(struct nfs_server *server,
+               struct nfs_fh *fhandle, struct nfs_fsinfo *info)
+{
+       int mv = server->nfs_client->cl_minorversion;
+       return nfs_v4_minor_ops[mv]->find_root_sec(server, fhandle, info);
+}
+
+/**
+ * nfs4_proc_get_rootfh - get file handle for server's pseudoroot
+ * @server: initialized nfs_server handle
+ * @fhandle: we fill in the pseudo-fs root file handle
+ * @info: we fill in an FSINFO struct
+ *
+ * Returns zero on success, or a negative errno.
  */
 int nfs4_proc_get_rootfh(struct nfs_server *server, struct nfs_fh *fhandle,
                         struct nfs_fsinfo *info)
 {
-       int minor_version = server->nfs_client->cl_minorversion;
-       int status = nfs4_lookup_root(server, fhandle, info);
-       if ((status == -NFS4ERR_WRONGSEC) && !(server->flags & NFS_MOUNT_SECFLAVOUR))
-               /*
-                * A status of -NFS4ERR_WRONGSEC will be mapped to -EPERM
-                * by nfs4_map_errors() as this function exits.
-                */
-               status = nfs_v4_minor_ops[minor_version]->find_root_sec(server, fhandle, info);
+       int status;
+
+       status = nfs4_lookup_root(server, fhandle, info);
+       if ((status == -NFS4ERR_WRONGSEC) &&
+           !(server->flags & NFS_MOUNT_SECFLAVOUR))
+               status = nfs4_do_find_root_sec(server, fhandle, info);
+
        if (status == 0)
                status = nfs4_server_capabilities(server, fhandle);
        if (status == 0)
                status = nfs4_do_fsinfo(server, fhandle, info);
+
        return nfs4_map_errors(status);
 }
 
@@ -3389,12 +3455,21 @@ static int _nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
 static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
 {
        struct nfs4_exception exception = { };
+       unsigned long now = jiffies;
        int err;
 
        do {
-               err = nfs4_handle_exception(server,
-                               _nfs4_do_fsinfo(server, fhandle, fsinfo),
-                               &exception);
+               err = _nfs4_do_fsinfo(server, fhandle, fsinfo);
+               if (err == 0) {
+                       struct nfs_client *clp = server->nfs_client;
+
+                       spin_lock(&clp->cl_lock);
+                       clp->cl_lease_time = fsinfo->lease_time * HZ;
+                       clp->cl_last_renewal = now;
+                       spin_unlock(&clp->cl_lock);
+                       break;
+               }
+               err = nfs4_handle_exception(server, err, &exception);
        } while (exception.retry);
        return err;
 }
@@ -4235,27 +4310,17 @@ int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
                struct nfs4_setclientid_res *arg,
                struct rpc_cred *cred)
 {
-       struct nfs_fsinfo fsinfo;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID_CONFIRM],
                .rpc_argp = arg,
-               .rpc_resp = &fsinfo,
                .rpc_cred = cred,
        };
-       unsigned long now;
        int status;
 
        dprintk("NFS call  setclientid_confirm auth=%s, (client ID %llx)\n",
                clp->cl_rpcclient->cl_auth->au_ops->au_name,
                clp->cl_clientid);
-       now = jiffies;
        status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
-       if (status == 0) {
-               spin_lock(&clp->cl_lock);
-               clp->cl_lease_time = fsinfo.lease_time * HZ;
-               clp->cl_last_renewal = now;
-               spin_unlock(&clp->cl_lock);
-       }
        dprintk("NFS reply setclientid_confirm: %d\n", status);
        return status;
 }
@@ -6841,7 +6906,9 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
        .init_caps = NFS_CAP_READDIRPLUS
                | NFS_CAP_ATOMIC_OPEN
                | NFS_CAP_CHANGE_ATTR
-               | NFS_CAP_POSIX_LOCK,
+               | NFS_CAP_POSIX_LOCK
+               | NFS_CAP_STATEID_NFSV41
+               | NFS_CAP_ATOMIC_OPEN_V1,
        .call_sync = nfs4_call_sync_sequence,
        .match_stateid = nfs41_match_stateid,
        .find_root_sec = nfs41_find_root_sec,
This page took 0.099388 seconds and 5 git commands to generate.