NFSv4: Fix problems with close in the presence of a delegation
[pandora-kernel.git] / fs / nfs / nfs4proc.c
index a51e1dd..c4a2a68 100644 (file)
@@ -96,6 +96,8 @@ static int nfs4_map_errors(int err)
        case -NFS4ERR_BADOWNER:
        case -NFS4ERR_BADNAME:
                return -EINVAL;
+       case -NFS4ERR_SHARE_DENIED:
+               return -EACCES;
        default:
                dprintk("%s could not handle NFSv4 error %d\n",
                                __func__, -err);
@@ -257,18 +259,28 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
 {
        struct nfs_client *clp = server->nfs_client;
        struct nfs4_state *state = exception->state;
+       struct inode *inode = exception->inode;
        int ret = errorcode;
 
        exception->retry = 0;
        switch(errorcode) {
                case 0:
                        return 0;
+               case -NFS4ERR_OPENMODE:
+                       if (nfs_have_delegation(inode, FMODE_READ)) {
+                               nfs_inode_return_delegation(inode);
+                               exception->retry = 1;
+                               return 0;
+                       }
+                       if (state == NULL)
+                               break;
+                       nfs4_schedule_stateid_recovery(server, state);
+                       goto wait_on_recovery;
                case -NFS4ERR_DELEG_REVOKED:
                case -NFS4ERR_ADMIN_REVOKED:
                case -NFS4ERR_BAD_STATEID:
                        if (state != NULL)
                                nfs_remove_bad_delegation(state->inode);
-               case -NFS4ERR_OPENMODE:
                        if (state == NULL)
                                break;
                        nfs4_schedule_stateid_recovery(server, state);
@@ -291,8 +303,7 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
                        dprintk("%s ERROR: %d Reset session\n", __func__,
                                errorcode);
                        nfs4_schedule_session_recovery(clp->cl_session);
-                       exception->retry = 1;
-                       break;
+                       goto wait_on_recovery;
 #endif /* defined(CONFIG_NFS_V4_1) */
                case -NFS4ERR_FILE_OPEN:
                        if (exception->timeout > HZ) {
@@ -1452,9 +1463,11 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
        data->timestamp = jiffies;
        if (nfs4_setup_sequence(data->o_arg.server,
                                &data->o_arg.seq_args,
-                               &data->o_res.seq_res, 1, task))
-               return;
-       rpc_call_start(task);
+                               &data->o_res.seq_res,
+                               1, task) != 0)
+               nfs_release_seqid(data->o_arg.seqid);
+       else
+               rpc_call_start(task);
        return;
 unlock_no_action:
        rcu_read_unlock();
@@ -1792,6 +1805,7 @@ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, fmode_t fmode
                        nfs_setattr_update_inode(state->inode, sattr);
                nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr);
        }
+       nfs_revalidate_inode(server, state->inode);
        nfs4_opendata_put(opendata);
        nfs4_put_state_owner(sp);
        *res = state;
@@ -1812,6 +1826,7 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry,
        struct nfs4_state *res;
        int status;
 
+       fmode &= FMODE_READ|FMODE_WRITE;
        do {
                status = _nfs4_do_open(dir, dentry, fmode, flags, sattr, cred, &res);
                if (status == 0)
@@ -1828,7 +1843,7 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry,
                 * the user though...
                 */
                if (status == -NFS4ERR_BAD_SEQID) {
-                       printk(KERN_WARNING "NFS: v4 server %s "
+                       pr_warn_ratelimited("NFS: v4 server %s "
                                        " returned a bad sequence-id error!\n",
                                        NFS_SERVER(dir)->nfs_client->cl_hostname);
                        exception.retry = 1;
@@ -1901,6 +1916,7 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
        struct nfs_server *server = NFS_SERVER(inode);
        struct nfs4_exception exception = {
                .state = state,
+               .inode = inode,
        };
        int err;
        do {
@@ -1988,24 +2004,31 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
 {
        struct nfs4_closedata *calldata = data;
        struct nfs4_state *state = calldata->state;
+       bool is_rdonly, is_wronly, is_rdwr;
        int call_close = 0;
 
        if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0)
                return;
 
        task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
-       calldata->arg.fmode = FMODE_READ|FMODE_WRITE;
        spin_lock(&state->owner->so_lock);
+       is_rdwr = test_bit(NFS_O_RDWR_STATE, &state->flags);
+       is_rdonly = test_bit(NFS_O_RDONLY_STATE, &state->flags);
+       is_wronly = test_bit(NFS_O_WRONLY_STATE, &state->flags);
+       /* Calculate the current open share mode */
+       calldata->arg.fmode = 0;
+       if (is_rdonly || is_rdwr)
+               calldata->arg.fmode |= FMODE_READ;
+       if (is_wronly || is_rdwr)
+               calldata->arg.fmode |= FMODE_WRITE;
        /* Calculate the change in open mode */
        if (state->n_rdwr == 0) {
                if (state->n_rdonly == 0) {
-                       call_close |= test_bit(NFS_O_RDONLY_STATE, &state->flags);
-                       call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
+                       call_close |= is_rdonly || is_rdwr;
                        calldata->arg.fmode &= ~FMODE_READ;
                }
                if (state->n_wronly == 0) {
-                       call_close |= test_bit(NFS_O_WRONLY_STATE, &state->flags);
-                       call_close |= test_bit(NFS_O_RDWR_STATE, &state->flags);
+                       call_close |= is_wronly || is_rdwr;
                        calldata->arg.fmode &= ~FMODE_WRITE;
                }
        }
@@ -2031,9 +2054,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
        calldata->timestamp = jiffies;
        if (nfs4_setup_sequence(NFS_SERVER(calldata->inode),
                                &calldata->arg.seq_args, &calldata->res.seq_res,
-                               1, task))
-               return;
-       rpc_call_start(task);
+                               1, task) != 0)
+               nfs_release_seqid(calldata->arg.seqid);
+       else
+               rpc_call_start(task);
 }
 
 static const struct rpc_call_ops nfs4_close_ops = {
@@ -2231,11 +2255,12 @@ static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
                switch (err) {
                case 0:
                case -NFS4ERR_WRONGSEC:
-                       break;
+                       goto out;
                default:
                        err = nfs4_handle_exception(server, err, &exception);
                }
        } while (exception.retry);
+out:
        return err;
 }
 
@@ -3009,11 +3034,11 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
                        dentry->d_parent->d_name.name,
                        dentry->d_name.name,
                        (unsigned long long)cookie);
-       nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args);
+       nfs4_setup_readdir(cookie, NFS_I(dir)->cookieverf, dentry, &args);
        res.pgbase = args.pgbase;
        status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0);
        if (status >= 0) {
-               memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE);
+               memcpy(NFS_I(dir)->cookieverf, res.verifier.data, NFS4_VERIFIER_SIZE);
                status += args.pgbase;
        }
 
@@ -3513,16 +3538,16 @@ out:
        return ret;
 }
 
-static void nfs4_write_cached_acl(struct inode *inode, const char *buf, size_t acl_len)
+static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size_t pgbase, size_t acl_len)
 {
        struct nfs4_cached_acl *acl;
 
-       if (buf && acl_len <= PAGE_SIZE) {
+       if (pages && acl_len <= PAGE_SIZE) {
                acl = kmalloc(sizeof(*acl) + acl_len, GFP_KERNEL);
                if (acl == NULL)
                        goto out;
                acl->cached = 1;
-               memcpy(acl->data, buf, acl_len);
+               _copy_from_pages(acl->data, pages, pgbase, acl_len);
        } else {
                acl = kmalloc(sizeof(*acl), GFP_KERNEL);
                if (acl == NULL)
@@ -3555,13 +3580,13 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
        struct nfs_getaclres res = {
                .acl_len = buflen,
        };
-       void *resp_buf;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL],
                .rpc_argp = &args,
                .rpc_resp = &res,
        };
-       int ret = -ENOMEM, npages, i, acl_len = 0;
+       int ret = -ENOMEM, npages, i;
+       size_t acl_len = 0;
 
        npages = (buflen + PAGE_SIZE - 1) >> PAGE_SHIFT;
        /* As long as we're doing a round trip to the server anyway,
@@ -3569,26 +3594,29 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
        if (npages == 0)
                npages = 1;
 
+       /* Add an extra page to handle the bitmap returned */
+       npages++;
+
        for (i = 0; i < npages; i++) {
                pages[i] = alloc_page(GFP_KERNEL);
                if (!pages[i])
                        goto out_free;
        }
-       if (npages > 1) {
-               /* for decoding across pages */
-               res.acl_scratch = alloc_page(GFP_KERNEL);
-               if (!res.acl_scratch)
-                       goto out_free;
-       }
+
+       /* for decoding across pages */
+       res.acl_scratch = alloc_page(GFP_KERNEL);
+       if (!res.acl_scratch)
+               goto out_free;
+
        args.acl_len = npages * PAGE_SIZE;
        args.acl_pgbase = 0;
+
        /* Let decode_getfacl know not to fail if the ACL data is larger than
         * the page we send as a guess */
        if (buf == NULL)
                res.acl_flags |= NFS4_ACL_LEN_REQUEST;
-       resp_buf = page_address(pages[0]);
 
-       dprintk("%s  buf %p buflen %ld npages %d args.acl_len %ld\n",
+       dprintk("%s  buf %p buflen %zu npages %d args.acl_len %zu\n",
                __func__, buf, buflen, npages, args.acl_len);
        ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode),
                             &msg, &args.seq_args, &res.seq_res, 0);
@@ -3597,16 +3625,16 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
 
        acl_len = res.acl_len - res.acl_data_offset;
        if (acl_len > args.acl_len)
-               nfs4_write_cached_acl(inode, NULL, acl_len);
+               nfs4_write_cached_acl(inode, NULL, 0, acl_len);
        else
-               nfs4_write_cached_acl(inode, resp_buf + res.acl_data_offset,
+               nfs4_write_cached_acl(inode, pages, res.acl_data_offset,
                                      acl_len);
        if (buf) {
                ret = -ERANGE;
                if (acl_len > buflen)
                        goto out_free;
                _copy_from_pages(buf, pages, res.acl_data_offset,
-                               res.acl_len);
+                               acl_len);
        }
        ret = acl_len;
 out_free:
@@ -3743,8 +3771,7 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
                        dprintk("%s ERROR %d, Reset session\n", __func__,
                                task->tk_status);
                        nfs4_schedule_session_recovery(clp->cl_session);
-                       task->tk_status = 0;
-                       return -EAGAIN;
+                       goto wait_on_recovery;
 #endif /* CONFIG_NFS_V4_1 */
                case -NFS4ERR_DELAY:
                        nfs_inc_server_stats(server, NFSIOS_DELAY);
@@ -3866,11 +3893,17 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
                return;
 
        switch (task->tk_status) {
-       case -NFS4ERR_STALE_STATEID:
-       case -NFS4ERR_EXPIRED:
        case 0:
                renew_lease(data->res.server, data->timestamp);
                break;
+       case -NFS4ERR_ADMIN_REVOKED:
+       case -NFS4ERR_DELEG_REVOKED:
+       case -NFS4ERR_BAD_STATEID:
+       case -NFS4ERR_OLD_STATEID:
+       case -NFS4ERR_STALE_STATEID:
+       case -NFS4ERR_EXPIRED:
+               task->tk_status = 0;
+               break;
        default:
                if (nfs4_async_handle_error(task, data->res.server, NULL) ==
                                -EAGAIN) {
@@ -4031,6 +4064,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
                        status = 0;
        }
        request->fl_ops->fl_release_private(request);
+       request->fl_ops = NULL;
 out:
        return status;
 }
@@ -4130,6 +4164,7 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
                        if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN)
                                rpc_restart_call_prepare(task);
        }
+       nfs_release_seqid(calldata->arg.seqid);
 }
 
 static void nfs4_locku_prepare(struct rpc_task *task, void *data)
@@ -4146,9 +4181,11 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)
        calldata->timestamp = jiffies;
        if (nfs4_setup_sequence(calldata->server,
                                &calldata->arg.seq_args,
-                               &calldata->res.seq_res, 1, task))
-               return;
-       rpc_call_start(task);
+                               &calldata->res.seq_res,
+                               1, task) != 0)
+               nfs_release_seqid(calldata->arg.seqid);
+       else
+               rpc_call_start(task);
 }
 
 static const struct rpc_call_ops nfs4_locku_ops = {
@@ -4292,7 +4329,7 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
        /* Do we need to do an open_to_lock_owner? */
        if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) {
                if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0)
-                       return;
+                       goto out_release_lock_seqid;
                data->arg.open_stateid = &state->stateid;
                data->arg.new_lock_owner = 1;
                data->res.open_seqid = data->arg.open_seqid;
@@ -4301,10 +4338,15 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
        data->timestamp = jiffies;
        if (nfs4_setup_sequence(data->server,
                                &data->arg.seq_args,
-                               &data->res.seq_res, 1, task))
+                               &data->res.seq_res,
+                               1, task) == 0) {
+               rpc_call_start(task);
                return;
-       rpc_call_start(task);
-       dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status);
+       }
+       nfs_release_seqid(data->arg.open_seqid);
+out_release_lock_seqid:
+       nfs_release_seqid(data->arg.lock_seqid);
+       dprintk("%s: done!, ret = %d\n", __func__, task->tk_status);
 }
 
 static void nfs4_recover_lock_prepare(struct rpc_task *task, void *calldata)
@@ -4441,7 +4483,9 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
 static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request)
 {
        struct nfs_server *server = NFS_SERVER(state->inode);
-       struct nfs4_exception exception = { };
+       struct nfs4_exception exception = {
+               .inode = state->inode,
+       };
        int err;
 
        do {
@@ -4459,7 +4503,9 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request
 static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request)
 {
        struct nfs_server *server = NFS_SERVER(state->inode);
-       struct nfs4_exception exception = { };
+       struct nfs4_exception exception = {
+               .inode = state->inode,
+       };
        int err;
 
        err = nfs4_set_lock_state(state, request);
@@ -4539,6 +4585,7 @@ static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *
 {
        struct nfs4_exception exception = {
                .state = state,
+               .inode = state->inode,
        };
        int err;
 
@@ -4584,6 +4631,20 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
 
        if (state == NULL)
                return -ENOLCK;
+       /*
+        * Don't rely on the VFS having checked the file open mode,
+        * since it won't do this for flock() locks.
+        */
+       switch (request->fl_type & (F_RDLCK|F_WRLCK|F_UNLCK)) {
+       case F_RDLCK:
+               if (!(filp->f_mode & FMODE_READ))
+                       return -EBADF;
+               break;
+       case F_WRLCK:
+               if (!(filp->f_mode & FMODE_WRITE))
+                       return -EBADF;
+       }
+
        do {
                status = nfs4_proc_setlk(state, cmd, request);
                if ((status != -EAGAIN) || IS_SETLK(cmd))
@@ -5497,13 +5558,26 @@ static void nfs41_sequence_prepare(struct rpc_task *task, void *data)
        rpc_call_start(task);
 }
 
+static void nfs41_sequence_prepare_privileged(struct rpc_task *task, void *data)
+{
+       rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED);
+       nfs41_sequence_prepare(task, data);
+}
+
 static const struct rpc_call_ops nfs41_sequence_ops = {
        .rpc_call_done = nfs41_sequence_call_done,
        .rpc_call_prepare = nfs41_sequence_prepare,
        .rpc_release = nfs41_sequence_release,
 };
 
-static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
+static const struct rpc_call_ops nfs41_sequence_privileged_ops = {
+       .rpc_call_done = nfs41_sequence_call_done,
+       .rpc_call_prepare = nfs41_sequence_prepare_privileged,
+       .rpc_release = nfs41_sequence_release,
+};
+
+static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred,
+                                            const struct rpc_call_ops *seq_ops)
 {
        struct nfs4_sequence_data *calldata;
        struct rpc_message msg = {
@@ -5513,7 +5587,7 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_
        struct rpc_task_setup task_setup_data = {
                .rpc_client = clp->cl_rpcclient,
                .rpc_message = &msg,
-               .callback_ops = &nfs41_sequence_ops,
+               .callback_ops = seq_ops,
                .flags = RPC_TASK_ASYNC | RPC_TASK_SOFT,
        };
 
@@ -5539,7 +5613,7 @@ static int nfs41_proc_async_sequence(struct nfs_client *clp, struct rpc_cred *cr
 
        if ((renew_flags & NFS4_RENEW_TIMEOUT) == 0)
                return 0;
-       task = _nfs41_proc_sequence(clp, cred);
+       task = _nfs41_proc_sequence(clp, cred, &nfs41_sequence_ops);
        if (IS_ERR(task))
                ret = PTR_ERR(task);
        else
@@ -5553,7 +5627,7 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
        struct rpc_task *task;
        int ret;
 
-       task = _nfs41_proc_sequence(clp, cred);
+       task = _nfs41_proc_sequence(clp, cred, &nfs41_sequence_privileged_ops);
        if (IS_ERR(task)) {
                ret = PTR_ERR(task);
                goto out;
@@ -5732,11 +5806,58 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
        dprintk("<-- %s\n", __func__);
 }
 
+static size_t max_response_pages(struct nfs_server *server)
+{
+       u32 max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz;
+       return nfs_page_array_len(0, max_resp_sz);
+}
+
+static void nfs4_free_pages(struct page **pages, size_t size)
+{
+       int i;
+
+       if (!pages)
+               return;
+
+       for (i = 0; i < size; i++) {
+               if (!pages[i])
+                       break;
+               __free_page(pages[i]);
+       }
+       kfree(pages);
+}
+
+static struct page **nfs4_alloc_pages(size_t size, gfp_t gfp_flags)
+{
+       struct page **pages;
+       int i;
+
+       pages = kcalloc(size, sizeof(struct page *), gfp_flags);
+       if (!pages) {
+               dprintk("%s: can't alloc array of %zu pages\n", __func__, size);
+               return NULL;
+       }
+
+       for (i = 0; i < size; i++) {
+               pages[i] = alloc_page(gfp_flags);
+               if (!pages[i]) {
+                       dprintk("%s: failed to allocate page\n", __func__);
+                       nfs4_free_pages(pages, size);
+                       return NULL;
+               }
+       }
+
+       return pages;
+}
+
 static void nfs4_layoutget_release(void *calldata)
 {
        struct nfs4_layoutget *lgp = calldata;
+       struct nfs_server *server = NFS_SERVER(lgp->args.inode);
+       size_t max_pages = max_response_pages(server);
 
        dprintk("--> %s\n", __func__);
+       nfs4_free_pages(lgp->args.layout.pages, max_pages);
        put_nfs_open_context(lgp->args.ctx);
        kfree(calldata);
        dprintk("<-- %s\n", __func__);
@@ -5748,9 +5869,10 @@ static const struct rpc_call_ops nfs4_layoutget_call_ops = {
        .rpc_release = nfs4_layoutget_release,
 };
 
-int nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
+int nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags)
 {
        struct nfs_server *server = NFS_SERVER(lgp->args.inode);
+       size_t max_pages = max_response_pages(server);
        struct rpc_task *task;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET],
@@ -5768,6 +5890,13 @@ int nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
 
        dprintk("--> %s\n", __func__);
 
+       lgp->args.layout.pages = nfs4_alloc_pages(max_pages, gfp_flags);
+       if (!lgp->args.layout.pages) {
+               nfs4_layoutget_release(lgp);
+               return -ENOMEM;
+       }
+       lgp->args.layout.pglen = max_pages * PAGE_SIZE;
+
        lgp->res.layoutp = &lgp->args.layout;
        lgp->res.seq_res.sr_slot = NULL;
        task = rpc_run_task(&task_setup_data);
@@ -5776,7 +5905,8 @@ int nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
        status = nfs4_wait_for_completion_rpc_task(task);
        if (status == 0)
                status = task->tk_status;
-       if (status == 0)
+       /* if layoutp->len is 0, nfs4_layoutget_prepare called rpc_exit */
+       if (status == 0 && lgp->res.layoutp->len)
                status = pnfs_layout_process(lgp);
        rpc_put_task(task);
        dprintk("<-- %s status=%d\n", __func__, status);
@@ -5971,42 +6101,29 @@ nfs4_layoutcommit_done(struct rpc_task *task, void *calldata)
                return;
 
        switch (task->tk_status) { /* Just ignore these failures */
-       case NFS4ERR_DELEG_REVOKED: /* layout was recalled */
-       case NFS4ERR_BADIOMODE:     /* no IOMODE_RW layout for range */
-       case NFS4ERR_BADLAYOUT:     /* no layout */
-       case NFS4ERR_GRACE:         /* loca_recalim always false */
+       case -NFS4ERR_DELEG_REVOKED: /* layout was recalled */
+       case -NFS4ERR_BADIOMODE:     /* no IOMODE_RW layout for range */
+       case -NFS4ERR_BADLAYOUT:     /* no layout */
+       case -NFS4ERR_GRACE:        /* loca_recalim always false */
                task->tk_status = 0;
-       }
-
-       if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) {
-               rpc_restart_call_prepare(task);
-               return;
-       }
-
-       if (task->tk_status == 0)
+               break;
+       case 0:
                nfs_post_op_update_inode_force_wcc(data->args.inode,
                                                   data->res.fattr);
+               break;
+       default:
+               if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) {
+                       rpc_restart_call_prepare(task);
+                       return;
+               }
+       }
 }
 
 static void nfs4_layoutcommit_release(void *calldata)
 {
        struct nfs4_layoutcommit_data *data = calldata;
-       struct pnfs_layout_segment *lseg, *tmp;
-       unsigned long *bitlock = &NFS_I(data->args.inode)->flags;
 
        pnfs_cleanup_layoutcommit(data);
-       /* Matched by references in pnfs_set_layoutcommit */
-       list_for_each_entry_safe(lseg, tmp, &data->lseg_list, pls_lc_list) {
-               list_del_init(&lseg->pls_lc_list);
-               if (test_and_clear_bit(NFS_LSEG_LAYOUTCOMMIT,
-                                      &lseg->pls_flags))
-                       put_lseg(lseg);
-       }
-
-       clear_bit_unlock(NFS_INO_LAYOUTCOMMITTING, bitlock);
-       smp_mb__after_clear_bit();
-       wake_up_bit(bitlock, NFS_INO_LAYOUTCOMMITTING);
-
        put_rpccred(data->cred);
        kfree(data);
 }
@@ -6087,12 +6204,13 @@ nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
                switch (err) {
                case 0:
                case -NFS4ERR_WRONGSEC:
-               case -NFS4ERR_NOTSUPP:
-                       break;
+               case -ENOTSUPP:
+                       goto out;
                default:
                        err = nfs4_handle_exception(server, err, &exception);
                }
        } while (exception.retry);
+out:
        return err;
 }
 
@@ -6118,7 +6236,7 @@ nfs41_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
         * Fall back on "guess and check" method if
         * the server doesn't support SECINFO_NO_NAME
         */
-       if (err == -NFS4ERR_WRONGSEC || err == -NFS4ERR_NOTSUPP) {
+       if (err == -NFS4ERR_WRONGSEC || err == -ENOTSUPP) {
                err = nfs4_find_root_sec(server, fhandle, info);
                goto out_freepage;
        }