Merge branch 'work.const-qstr' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[cascardo/linux.git] / fs / nfs / nfs4proc.c
index e75815c..a036e93 100644 (file)
@@ -363,6 +363,7 @@ static int nfs4_do_handle_exception(struct nfs_server *server,
 {
        struct nfs_client *clp = server->nfs_client;
        struct nfs4_state *state = exception->state;
+       const nfs4_stateid *stateid = exception->stateid;
        struct inode *inode = exception->inode;
        int ret = errorcode;
 
@@ -376,9 +377,18 @@ static int nfs4_do_handle_exception(struct nfs_server *server,
                case -NFS4ERR_DELEG_REVOKED:
                case -NFS4ERR_ADMIN_REVOKED:
                case -NFS4ERR_BAD_STATEID:
-                       if (inode && nfs_async_inode_return_delegation(inode,
-                                               NULL) == 0)
-                               goto wait_on_recovery;
+                       if (inode) {
+                               int err;
+
+                               err = nfs_async_inode_return_delegation(inode,
+                                               stateid);
+                               if (err == 0)
+                                       goto wait_on_recovery;
+                               if (stateid != NULL && stateid->type == NFS4_DELEGATION_STATEID_TYPE) {
+                                       exception->retry = 1;
+                                       break;
+                               }
+                       }
                        if (state == NULL)
                                break;
                        ret = nfs4_schedule_stateid_recovery(server, state);
@@ -427,6 +437,7 @@ static int nfs4_do_handle_exception(struct nfs_server *server,
                case -NFS4ERR_DELAY:
                        nfs_inc_server_stats(server, NFSIOS_DELAY);
                case -NFS4ERR_GRACE:
+               case -NFS4ERR_LAYOUTTRYLATER:
                case -NFS4ERR_RECALLCONFLICT:
                        exception->delay = 1;
                        return 0;
@@ -2669,28 +2680,17 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,
        return res;
 }
 
-static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
-                           struct nfs_fattr *fattr, struct iattr *sattr,
-                           struct nfs4_state *state, struct nfs4_label *ilabel,
-                           struct nfs4_label *olabel)
+static int _nfs4_do_setattr(struct inode *inode,
+                           struct nfs_setattrargs *arg,
+                           struct nfs_setattrres *res,
+                           struct rpc_cred *cred,
+                           struct nfs4_state *state)
 {
        struct nfs_server *server = NFS_SERVER(inode);
-        struct nfs_setattrargs  arg = {
-                .fh             = NFS_FH(inode),
-                .iap            = sattr,
-               .server         = server,
-               .bitmask = server->attr_bitmask,
-               .label          = ilabel,
-        };
-        struct nfs_setattrres  res = {
-               .fattr          = fattr,
-               .label          = olabel,
-               .server         = server,
-        };
         struct rpc_message msg = {
                .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
-               .rpc_argp       = &arg,
-               .rpc_resp       = &res,
+               .rpc_argp       = arg,
+               .rpc_resp       = res,
                .rpc_cred       = cred,
         };
        struct rpc_cred *delegation_cred = NULL;
@@ -2699,17 +2699,13 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
        bool truncate;
        int status;
 
-       arg.bitmask = nfs4_bitmask(server, ilabel);
-       if (ilabel)
-               arg.bitmask = nfs4_bitmask(server, olabel);
-
-       nfs_fattr_init(fattr);
+       nfs_fattr_init(res->fattr);
 
        /* Servers should only apply open mode checks for file size changes */
-       truncate = (sattr->ia_valid & ATTR_SIZE) ? true : false;
+       truncate = (arg->iap->ia_valid & ATTR_SIZE) ? true : false;
        fmode = truncate ? FMODE_WRITE : FMODE_READ;
 
-       if (nfs4_copy_delegation_stateid(inode, fmode, &arg.stateid, &delegation_cred)) {
+       if (nfs4_copy_delegation_stateid(inode, fmode, &arg->stateid, &delegation_cred)) {
                /* Use that stateid */
        } else if (truncate && state != NULL) {
                struct nfs_lockowner lockowner = {
@@ -2719,19 +2715,19 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                if (!nfs4_valid_open_stateid(state))
                        return -EBADF;
                if (nfs4_select_rw_stateid(state, FMODE_WRITE, &lockowner,
-                               &arg.stateid, &delegation_cred) == -EIO)
+                               &arg->stateid, &delegation_cred) == -EIO)
                        return -EBADF;
        } else
-               nfs4_stateid_copy(&arg.stateid, &zero_stateid);
+               nfs4_stateid_copy(&arg->stateid, &zero_stateid);
        if (delegation_cred)
                msg.rpc_cred = delegation_cred;
 
-       status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
+       status = nfs4_call_sync(server->client, server, &msg, &arg->seq_args, &res->seq_res, 1);
 
        put_rpccred(delegation_cred);
        if (status == 0 && state != NULL)
                renew_lease(server, timestamp);
-       trace_nfs4_setattr(inode, &arg.stateid, status);
+       trace_nfs4_setattr(inode, &arg->stateid, status);
        return status;
 }
 
@@ -2741,13 +2737,31 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                           struct nfs4_label *olabel)
 {
        struct nfs_server *server = NFS_SERVER(inode);
+        struct nfs_setattrargs  arg = {
+                .fh             = NFS_FH(inode),
+                .iap            = sattr,
+               .server         = server,
+               .bitmask = server->attr_bitmask,
+               .label          = ilabel,
+        };
+        struct nfs_setattrres  res = {
+               .fattr          = fattr,
+               .label          = olabel,
+               .server         = server,
+        };
        struct nfs4_exception exception = {
                .state = state,
                .inode = inode,
+               .stateid = &arg.stateid,
        };
        int err;
+
+       arg.bitmask = nfs4_bitmask(server, ilabel);
+       if (ilabel)
+               arg.bitmask = nfs4_bitmask(server, olabel);
+
        do {
-               err = _nfs4_do_setattr(inode, cred, fattr, sattr, state, ilabel, olabel);
+               err = _nfs4_do_setattr(inode, &arg, &res, cred, state);
                switch (err) {
                case -NFS4ERR_OPENMODE:
                        if (!(sattr->ia_valid & ATTR_SIZE)) {
@@ -3267,13 +3281,6 @@ static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
        return status;
 }
 
-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
@@ -3293,7 +3300,8 @@ int nfs4_proc_get_rootfh(struct nfs_server *server, struct nfs_fh *fhandle,
                status = nfs4_lookup_root(server, fhandle, info);
 
        if (auth_probe || status == NFS4ERR_WRONGSEC)
-               status = nfs4_do_find_root_sec(server, fhandle, info);
+               status = server->nfs_client->cl_mvops->find_root_sec(server,
+                               fhandle, info);
 
        if (status == 0)
                status = nfs4_server_capabilities(server, fhandle);
@@ -4392,7 +4400,8 @@ static void nfs4_proc_read_setup(struct nfs_pgio_header *hdr,
                                 struct rpc_message *msg)
 {
        hdr->timestamp   = jiffies;
-       hdr->pgio_done_cb = nfs4_read_done_cb;
+       if (!hdr->pgio_done_cb)
+               hdr->pgio_done_cb = nfs4_read_done_cb;
        msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
        nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 0);
 }
@@ -7869,11 +7878,13 @@ nfs4_layoutget_handle_exception(struct rpc_task *task,
        struct inode *inode = lgp->args.inode;
        struct nfs_server *server = NFS_SERVER(inode);
        struct pnfs_layout_hdr *lo;
-       int status = task->tk_status;
+       int nfs4err = task->tk_status;
+       int err, status = 0;
+       LIST_HEAD(head);
 
        dprintk("--> %s tk_status => %d\n", __func__, -task->tk_status);
 
-       switch (status) {
+       switch (nfs4err) {
        case 0:
                goto out;
 
@@ -7905,45 +7916,42 @@ nfs4_layoutget_handle_exception(struct rpc_task *task,
                        status = -EOVERFLOW;
                        goto out;
                }
-               /* Fallthrough */
+               status = -EBUSY;
+               break;
        case -NFS4ERR_RECALLCONFLICT:
-               nfs4_handle_exception(server, -NFS4ERR_RECALLCONFLICT,
-                                       exception);
                status = -ERECALLCONFLICT;
-               goto out;
+               break;
        case -NFS4ERR_EXPIRED:
        case -NFS4ERR_BAD_STATEID:
                exception->timeout = 0;
                spin_lock(&inode->i_lock);
-               if (nfs4_stateid_match(&lgp->args.stateid,
+               lo = NFS_I(inode)->layout;
+               /* If the open stateid was bad, then recover it. */
+               if (!lo || test_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags) ||
+                   nfs4_stateid_match_other(&lgp->args.stateid,
                                        &lgp->args.ctx->state->stateid)) {
                        spin_unlock(&inode->i_lock);
-                       /* If the open stateid was bad, then recover it. */
                        exception->state = lgp->args.ctx->state;
                        break;
                }
-               lo = NFS_I(inode)->layout;
-               if (lo && !test_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags) &&
-                   nfs4_stateid_match_other(&lgp->args.stateid, &lo->plh_stateid)) {
-                       LIST_HEAD(head);
-
-                       /*
-                        * Mark the bad layout state as invalid, then retry
-                        * with the current stateid.
-                        */
-                       set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
-                       pnfs_mark_matching_lsegs_invalid(lo, &head, NULL, 0);
-                       spin_unlock(&inode->i_lock);
-                       pnfs_free_lseg_list(&head);
-                       status = -EAGAIN;
-                       goto out;
-               } else
-                       spin_unlock(&inode->i_lock);
-       }
 
-       status = nfs4_handle_exception(server, status, exception);
-       if (exception->retry)
+               /*
+                * Mark the bad layout state as invalid, then retry
+                */
+               pnfs_mark_layout_stateid_invalid(lo, &head);
+               spin_unlock(&inode->i_lock);
+               pnfs_free_lseg_list(&head);
                status = -EAGAIN;
+               goto out;
+       }
+
+       err = nfs4_handle_exception(server, nfs4err, exception);
+       if (!status) {
+               if (exception->retry)
+                       status = -EAGAIN;
+               else
+                       status = err;
+       }
 out:
        dprintk("<-- %s\n", __func__);
        return status;
@@ -8129,8 +8137,7 @@ static void nfs4_layoutreturn_release(void *calldata)
        spin_lock(&lo->plh_inode->i_lock);
        pnfs_mark_matching_lsegs_invalid(lo, &freeme, &lrp->args.range,
                        be32_to_cpu(lrp->args.stateid.seqid));
-       pnfs_mark_layout_returned_if_empty(lo);
-       if (lrp->res.lrs_present)
+       if (lrp->res.lrs_present && pnfs_layout_is_valid(lo))
                pnfs_set_layout_stateid(lo, &lrp->res.stateid, true);
        pnfs_clear_layoutreturn_waitbit(lo);
        spin_unlock(&lo->plh_inode->i_lock);
@@ -8835,7 +8842,7 @@ const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = {
 #endif
 };
 
-ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size)
+static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size)
 {
        ssize_t error, error2;