]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge tag 'nfs-for-3.10-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 30 Apr 2013 18:28:08 +0000 (11:28 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 30 Apr 2013 18:28:08 +0000 (11:28 -0700)
Pull NFS client bugfixes and cleanups from Trond Myklebust:

 - NLM: stable fix for NFSv2/v3 blocking locks

 - NFSv4.x: stable fixes for the delegation recall error handling code

 - NFSv4.x: Security flavour negotiation fixes and cleanups by Chuck
   Lever

 - SUNRPC: A number of RPCSEC_GSS fixes and cleanups also from Chuck

 - NFSv4.x assorted state management and reboot recovery bugfixes

 - NFSv4.1: In cases where we have already looked up a file, and hold a
   valid filehandle, use the new open-by-filehandle operation instead of
   opening by name.

 - Allow the NFSv4.1 callback thread to freeze

 - NFSv4.x: ensure that file unlock waits for readahead to complete

 - NFSv4.1: ensure that the RPC layer doesn't override the NFS session
   table size negotiation by limiting the number of slots.

 - NFSv4.x: Fix SETATTR spec compatibility issues

* tag 'nfs-for-3.10-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (67 commits)
  NFSv4: Warn once about servers that incorrectly apply open mode to setattr
  NFSv4: Servers should only check SETATTR stateid open mode on size change
  NFSv4: Don't recheck permissions on open in case of recovery cached open
  NFSv4.1: Don't do a delegated open for NFS4_OPEN_CLAIM_DELEG_CUR_FH modes
  NFSv4.1: Use the more efficient open_noattr call for open-by-filehandle
  NFS: Retry SETCLIENTID with AUTH_SYS instead of AUTH_NONE
  NFSv4: Ensure that we clear the NFS_OPEN_STATE flag when appropriate
  LOCKD: Ensure that nlmclnt_block resets block->b_status after a server reboot
  NFSv4: Ensure the LOCK call cannot use the delegation stateid
  NFSv4: Use the open stateid if the delegation has the wrong mode
  nfs: Send atime and mtime as a 64bit value
  NFSv4: Record the OPEN create mode used in the nfs4_opendata structure
  NFSv4.1: Set the RPC_CLNT_CREATE_INFINITE_SLOTS flag for NFSv4.1 transports
  SUNRPC: Allow rpc_create() to request that TCP slots be unlimited
  SUNRPC: Fix a livelock problem in the xprt->backlog queue
  NFSv4: Fix handling of revoked delegations by setattr
  NFSv4 release the sequence id in the return on close case
  nfs: remove unnecessary check for NULL inode->i_flock from nfs_delegation_claim_locks
  NFS: Ensure that NFS file unlock waits for readahead to complete
  NFS: Add functionality to allow waiting on all outstanding reads to complete
  ...

41 files changed:
fs/lockd/clntlock.c
fs/lockd/clntproc.c
fs/nfs/callback.c
fs/nfs/callback_proc.c
fs/nfs/client.c
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/dir.c
fs/nfs/file.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/nfs4_fs.h
fs/nfs/nfs4client.c
fs/nfs/nfs4filelayout.c
fs/nfs/nfs4namespace.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c
fs/nfs/nfs4super.c
fs/nfs/nfs4xdr.c
fs/nfs/pagelist.c
fs/nfs/pnfs.c
fs/nfs/read.c
fs/nfs/super.c
fs/nfs/write.c
fs/nfsd/nfs4xdr.c
include/linux/nfs_fs.h
include/linux/nfs_fs_sb.h
include/linux/nfs_xdr.h
include/linux/sunrpc/auth.h
include/linux/sunrpc/clnt.h
include/linux/sunrpc/gss_api.h
include/linux/sunrpc/xprt.h
net/sunrpc/Kconfig
net/sunrpc/auth.c
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/auth_gss/gss_krb5_mech.c
net/sunrpc/auth_gss/gss_mech_switch.c
net/sunrpc/auth_gss/svcauth_gss.c
net/sunrpc/clnt.c
net/sunrpc/xprt.c
net/sunrpc/xprtsock.c

index 0796c45d0d4d0795420743f104dc4e76e0cebd14..01bfe76627516d836241b249bdeac7b0a02b0f97 100644 (file)
@@ -144,6 +144,9 @@ int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout)
                        timeout);
        if (ret < 0)
                return -ERESTARTSYS;
+       /* Reset the lock status after a server reboot so we resend */
+       if (block->b_status == nlm_lck_denied_grace_period)
+               block->b_status = nlm_lck_blocked;
        req->a_res.status = block->b_status;
        return 0;
 }
index 7e529c3c45c0566fe358ba1975a1d2159a0e21a6..9760ecb9b60f6ae39471ea09fd8f286ca0285da7 100644 (file)
@@ -550,9 +550,6 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
                status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT);
                if (status < 0)
                        break;
-               /* Resend the blocking lock request after a server reboot */
-               if (resp->status ==  nlm_lck_denied_grace_period)
-                       continue;
                if (resp->status != nlm_lck_blocked)
                        break;
        }
index 5088b57b078a729d09ced36b3e491c01cba9cf4c..cff089a412c7f4bde3bd2f1d5138a4679535b772 100644 (file)
@@ -125,6 +125,9 @@ nfs41_callback_svc(void *vrqstp)
        set_freezable();
 
        while (!kthread_should_stop()) {
+               if (try_to_freeze())
+                       continue;
+
                prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
                spin_lock_bh(&serv->sv_cb_lock);
                if (!list_empty(&serv->sv_cb_list)) {
index 2960512792c23f9c68a5930ceec151c8bf0bf7eb..a13d26ede254357d46bc1f24c7c404555048987f 100644 (file)
@@ -500,7 +500,7 @@ __be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy,
                     &args->craa_type_mask))
                pnfs_recall_all_layouts(cps->clp);
        if (flags)
-               nfs_expire_all_delegation_types(cps->clp, flags);
+               nfs_expire_unused_delegation_types(cps->clp, flags);
 out:
        dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
        return status;
index 84d8eae203a7985ba7a78d54849e8e87782e44f8..c513b0cc835f9f32db99def2a6868fdf7848e5fe 100644 (file)
@@ -593,6 +593,8 @@ int nfs_create_rpc_client(struct nfs_client *clp,
                args.flags |= RPC_CLNT_CREATE_DISCRTRY;
        if (test_bit(NFS_CS_NORESVPORT, &clp->cl_flags))
                args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
+       if (test_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags))
+               args.flags |= RPC_CLNT_CREATE_INFINITE_SLOTS;
 
        if (!IS_ERR(clp->cl_rpcclient))
                return 0;
index 6390a4b5fee75823512f30de7f7452696a23093c..57db3244f4d967dd5479dbf3ab883c6d28cc2cbd 100644 (file)
@@ -64,17 +64,15 @@ int nfs4_have_delegation(struct inode *inode, fmode_t flags)
        return ret;
 }
 
-static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state)
+static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
 {
        struct inode *inode = state->inode;
        struct file_lock *fl;
        int status = 0;
 
-       if (inode->i_flock == NULL)
-               return 0;
-
        if (inode->i_flock == NULL)
                goto out;
+
        /* Protect inode->i_flock using the file locks lock */
        lock_flocks();
        for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
@@ -83,7 +81,7 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_
                if (nfs_file_open_context(fl->fl_file) != ctx)
                        continue;
                unlock_flocks();
-               status = nfs4_lock_delegation_recall(state, fl);
+               status = nfs4_lock_delegation_recall(fl, state, stateid);
                if (status < 0)
                        goto out;
                lock_flocks();
@@ -120,7 +118,7 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s
                seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
                err = nfs4_open_delegation_recall(ctx, state, stateid);
                if (!err)
-                       err = nfs_delegation_claim_locks(ctx, state);
+                       err = nfs_delegation_claim_locks(ctx, state, stateid);
                if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
                        err = -EAGAIN;
                mutex_unlock(&sp->so_delegreturn_mutex);
@@ -389,6 +387,24 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
        return err;
 }
 
+static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
+{
+       bool ret = false;
+
+       if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
+               ret = true;
+       if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) {
+               struct inode *inode;
+
+               spin_lock(&delegation->lock);
+               inode = delegation->inode;
+               if (inode && list_empty(&NFS_I(inode)->open_files))
+                       ret = true;
+               spin_unlock(&delegation->lock);
+       }
+       return ret;
+}
+
 /**
  * nfs_client_return_marked_delegations - return previously marked delegations
  * @clp: nfs_client to process
@@ -411,8 +427,7 @@ int nfs_client_return_marked_delegations(struct nfs_client *clp)
        list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
                list_for_each_entry_rcu(delegation, &server->delegations,
                                                                super_list) {
-                       if (!test_and_clear_bit(NFS_DELEGATION_RETURN,
-                                                       &delegation->flags))
+                       if (!nfs_delegation_need_return(delegation))
                                continue;
                        inode = nfs_delegation_grab_inode(delegation);
                        if (inode == NULL)
@@ -471,6 +486,13 @@ int nfs4_inode_return_delegation(struct inode *inode)
        return err;
 }
 
+static void nfs_mark_return_if_closed_delegation(struct nfs_server *server,
+               struct nfs_delegation *delegation)
+{
+       set_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
+       set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
+}
+
 static void nfs_mark_return_delegation(struct nfs_server *server,
                struct nfs_delegation *delegation)
 {
@@ -478,6 +500,45 @@ static void nfs_mark_return_delegation(struct nfs_server *server,
        set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state);
 }
 
+static bool nfs_server_mark_return_all_delegations(struct nfs_server *server)
+{
+       struct nfs_delegation *delegation;
+       bool ret = false;
+
+       list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
+               nfs_mark_return_delegation(server, delegation);
+               ret = true;
+       }
+       return ret;
+}
+
+static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
+{
+       struct nfs_server *server;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
+               nfs_server_mark_return_all_delegations(server);
+       rcu_read_unlock();
+}
+
+static void nfs_delegation_run_state_manager(struct nfs_client *clp)
+{
+       if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
+               nfs4_schedule_state_manager(clp);
+}
+
+/**
+ * nfs_expire_all_delegations
+ * @clp: client to process
+ *
+ */
+void nfs_expire_all_delegations(struct nfs_client *clp)
+{
+       nfs_client_mark_return_all_delegations(clp);
+       nfs_delegation_run_state_manager(clp);
+}
+
 /**
  * nfs_super_return_all_delegations - return delegations for one superblock
  * @sb: sb to process
@@ -486,24 +547,22 @@ static void nfs_mark_return_delegation(struct nfs_server *server,
 void nfs_server_return_all_delegations(struct nfs_server *server)
 {
        struct nfs_client *clp = server->nfs_client;
-       struct nfs_delegation *delegation;
+       bool need_wait;
 
        if (clp == NULL)
                return;
 
        rcu_read_lock();
-       list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
-               spin_lock(&delegation->lock);
-               set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
-               spin_unlock(&delegation->lock);
-       }
+       need_wait = nfs_server_mark_return_all_delegations(server);
        rcu_read_unlock();
 
-       if (nfs_client_return_marked_delegations(clp) != 0)
+       if (need_wait) {
                nfs4_schedule_state_manager(clp);
+               nfs4_wait_clnt_recover(clp);
+       }
 }
 
-static void nfs_mark_return_all_delegation_types(struct nfs_server *server,
+static void nfs_mark_return_unused_delegation_types(struct nfs_server *server,
                                                 fmode_t flags)
 {
        struct nfs_delegation *delegation;
@@ -512,27 +571,21 @@ static void nfs_mark_return_all_delegation_types(struct nfs_server *server,
                if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
                        continue;
                if (delegation->type & flags)
-                       nfs_mark_return_delegation(server, delegation);
+                       nfs_mark_return_if_closed_delegation(server, delegation);
        }
 }
 
-static void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp,
+static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *clp,
                                                        fmode_t flags)
 {
        struct nfs_server *server;
 
        rcu_read_lock();
        list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
-               nfs_mark_return_all_delegation_types(server, flags);
+               nfs_mark_return_unused_delegation_types(server, flags);
        rcu_read_unlock();
 }
 
-static void nfs_delegation_run_state_manager(struct nfs_client *clp)
-{
-       if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
-               nfs4_schedule_state_manager(clp);
-}
-
 void nfs_remove_bad_delegation(struct inode *inode)
 {
        struct nfs_delegation *delegation;
@@ -546,27 +599,17 @@ void nfs_remove_bad_delegation(struct inode *inode)
 EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
 
 /**
- * nfs_expire_all_delegation_types
+ * nfs_expire_unused_delegation_types
  * @clp: client to process
  * @flags: delegation types to expire
  *
  */
-void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags)
+void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags)
 {
-       nfs_client_mark_return_all_delegation_types(clp, flags);
+       nfs_client_mark_return_unused_delegation_types(clp, flags);
        nfs_delegation_run_state_manager(clp);
 }
 
-/**
- * nfs_expire_all_delegations
- * @clp: client to process
- *
- */
-void nfs_expire_all_delegations(struct nfs_client *clp)
-{
-       nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE);
-}
-
 static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
 {
        struct nfs_delegation *delegation;
@@ -574,7 +617,7 @@ static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
        list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
                if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
                        continue;
-               nfs_mark_return_delegation(server, delegation);
+               nfs_mark_return_if_closed_delegation(server, delegation);
        }
 }
 
index d54d4fca67934aae49bb430e2dea52d9d3fbb0ef..9a79c7a99d6d6dd64b03f58481dc8d0fb4b32818 100644 (file)
@@ -28,6 +28,7 @@ struct nfs_delegation {
 enum {
        NFS_DELEGATION_NEED_RECLAIM = 0,
        NFS_DELEGATION_RETURN,
+       NFS_DELEGATION_RETURN_IF_CLOSED,
        NFS_DELEGATION_REFERENCED,
        NFS_DELEGATION_RETURNING,
 };
@@ -41,7 +42,7 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode);
 struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle);
 void nfs_server_return_all_delegations(struct nfs_server *);
 void nfs_expire_all_delegations(struct nfs_client *clp);
-void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags);
+void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags);
 void nfs_expire_unreferenced_delegations(struct nfs_client *clp);
 int nfs_client_return_marked_delegations(struct nfs_client *clp);
 int nfs_delegations_present(struct nfs_client *clp);
@@ -53,7 +54,7 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
 /* NFSv4 delegation-related procedures */
 int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync);
 int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid);
-int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl);
+int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state, const nfs4_stateid *stateid);
 bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode, fmode_t flags);
 
 void nfs_mark_delegation_referenced(struct nfs_delegation *delegation);
index f23f455be42b8e97141bbe9beabdfa09fcff3932..e093e73178b71467aa15eafa1d5327714eff284e 100644 (file)
@@ -1486,6 +1486,8 @@ static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
                goto no_open;
        if (d_mountpoint(dentry))
                goto no_open;
+       if (NFS_SB(dentry->d_sb)->caps & NFS_CAP_ATOMIC_OPEN_V1)
+               goto no_open;
 
        inode = dentry->d_inode;
        parent = dget_parent(dentry);
index 29f4a48a0ee605ab3f19f62181b7f33303bbbeb5..a87a44f8411304218986faa171f40de1f9564cd2 100644 (file)
@@ -744,6 +744,7 @@ static int
 do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
 {
        struct inode *inode = filp->f_mapping->host;
+       struct nfs_lock_context *l_ctx;
        int status;
 
        /*
@@ -752,6 +753,14 @@ do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
         */
        nfs_sync_mapping(filp->f_mapping);
 
+       l_ctx = nfs_get_lock_context(nfs_file_open_context(filp));
+       if (!IS_ERR(l_ctx)) {
+               status = nfs_iocounter_wait(&l_ctx->io_count);
+               nfs_put_lock_context(l_ctx);
+               if (status < 0)
+                       return status;
+       }
+
        /* NOTE: special case
         *      If we're signalled while cleaning up locks on process exit, we
         *      still need to complete the unlock.
index 1f941674b08967e4204d67f92d66cd6650bbc9a1..c1c7a9d78722257867846f39c74780536b28d0c5 100644 (file)
@@ -561,20 +561,22 @@ static void nfs_init_lock_context(struct nfs_lock_context *l_ctx)
        l_ctx->lockowner.l_owner = current->files;
        l_ctx->lockowner.l_pid = current->tgid;
        INIT_LIST_HEAD(&l_ctx->list);
+       nfs_iocounter_init(&l_ctx->io_count);
 }
 
 static struct nfs_lock_context *__nfs_find_lock_context(struct nfs_open_context *ctx)
 {
-       struct nfs_lock_context *pos;
+       struct nfs_lock_context *head = &ctx->lock_context;
+       struct nfs_lock_context *pos = head;
 
-       list_for_each_entry(pos, &ctx->lock_context.list, list) {
+       do {
                if (pos->lockowner.l_owner != current->files)
                        continue;
                if (pos->lockowner.l_pid != current->tgid)
                        continue;
                atomic_inc(&pos->count);
                return pos;
-       }
+       } while ((pos = list_entry(pos->list.next, typeof(*pos), list)) != head);
        return NULL;
 }
 
index 541c9ebdbc5a3e905f07798e2207bf5eec028081..91e59a39fc08dcfd3b6b788cd9a3610e5d4b8984 100644 (file)
@@ -229,6 +229,13 @@ extern void nfs_pgheader_init(struct nfs_pageio_descriptor *desc,
                              struct nfs_pgio_header *hdr,
                              void (*release)(struct nfs_pgio_header *hdr));
 void nfs_set_pgio_error(struct nfs_pgio_header *hdr, int error, loff_t pos);
+int nfs_iocounter_wait(struct nfs_io_counter *c);
+
+static inline void nfs_iocounter_init(struct nfs_io_counter *c)
+{
+       c->flags = 0;
+       atomic_set(&c->io_count, 0);
+}
 
 /* nfs2xdr.c */
 extern struct rpc_procinfo nfs_procedures[];
index 944c9a5c10390cdca6112f3ecf27574ef0a89e81..553a83cc41061fdd18d8b125d5e7b9ffaea8e99e 100644 (file)
@@ -36,6 +36,7 @@ enum nfs4_client_state {
 
 struct nfs4_minor_version_ops {
        u32     minor_version;
+       unsigned init_caps;
 
        int     (*call_sync)(struct rpc_clnt *clnt,
                        struct nfs_server *server,
@@ -143,12 +144,14 @@ struct nfs4_lock_state {
 enum {
        LK_STATE_IN_USE,
        NFS_DELEGATED_STATE,            /* Current stateid is delegation */
+       NFS_OPEN_STATE,                 /* OPEN stateid is set */
        NFS_O_RDONLY_STATE,             /* OPEN stateid has read-only state */
        NFS_O_WRONLY_STATE,             /* OPEN stateid has write-only state */
        NFS_O_RDWR_STATE,               /* OPEN stateid has read/write state */
        NFS_STATE_RECLAIM_REBOOT,       /* OPEN stateid server rebooted */
        NFS_STATE_RECLAIM_NOGRACE,      /* OPEN stateid needs to recover state */
        NFS_STATE_POSIX_LOCKS,          /* Posix locks are supported */
+       NFS_STATE_RECOVERY_FAILED,      /* OPEN stateid state recovery failed */
 };
 
 struct nfs4_state {
@@ -233,6 +236,10 @@ extern struct rpc_clnt *nfs4_proc_lookup_mountpoint(struct inode *, struct qstr
 extern int nfs4_proc_secinfo(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
 extern int nfs4_release_lockowner(struct nfs4_lock_state *);
 extern const struct xattr_handler *nfs4_xattr_handlers[];
+extern int nfs4_set_rw_stateid(nfs4_stateid *stateid,
+               const struct nfs_open_context *ctx,
+               const struct nfs_lock_context *l_ctx,
+               fmode_t fmode);
 
 #if defined(CONFIG_NFS_V4_1)
 static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server)
@@ -347,13 +354,13 @@ extern int nfs4_wait_clnt_recover(struct nfs_client *clp);
 extern int nfs4_client_recover_expired_lease(struct nfs_client *clp);
 extern void nfs4_schedule_state_manager(struct nfs_client *);
 extern void nfs4_schedule_path_down_recovery(struct nfs_client *clp);
-extern void nfs4_schedule_stateid_recovery(const struct nfs_server *, struct nfs4_state *);
+extern int nfs4_schedule_stateid_recovery(const struct nfs_server *, struct nfs4_state *);
 extern void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags);
 extern void nfs41_handle_server_scope(struct nfs_client *,
                                      struct nfs41_server_scope **);
 extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
 extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl);
-extern void nfs4_select_rw_stateid(nfs4_stateid *, struct nfs4_state *,
+extern int nfs4_select_rw_stateid(nfs4_stateid *, struct nfs4_state *,
                fmode_t, const struct nfs_lockowner *);
 
 extern struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask);
@@ -412,6 +419,11 @@ static inline bool nfs4_stateid_match(const nfs4_stateid *dst, const nfs4_statei
        return memcmp(dst, src, sizeof(*dst)) == 0;
 }
 
+static inline bool nfs4_valid_open_stateid(const struct nfs4_state *state)
+{
+       return test_bit(NFS_STATE_RECOVERY_FAILED, &state->flags) == 0;
+}
+
 #else
 
 #define nfs4_close_state(a, b) do { } while (0)
index 66b6664dcd4c4c9cd209d9420eb9de895cd1e344..947b0c908aa908c643140dd7087b0927753c6d49 100644 (file)
@@ -198,8 +198,12 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp,
        /* Check NFS protocol revision and initialize RPC op vector */
        clp->rpc_ops = &nfs_v4_clientops;
 
+       if (clp->cl_minorversion != 0)
+               __set_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags);
        __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
-       error = nfs_create_rpc_client(clp, timeparms, authflavour);
+       error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_GSS_KRB5I);
+       if (error == -EINVAL)
+               error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_NULL);
        if (error < 0)
                goto error;
 
@@ -730,6 +734,19 @@ static int nfs4_server_common_setup(struct nfs_server *server,
        if (error < 0)
                goto out;
 
+       /* Set the basic capabilities */
+       server->caps |= server->nfs_client->cl_mvops->init_caps;
+       if (server->flags & NFS_MOUNT_NORDIRPLUS)
+                       server->caps &= ~NFS_CAP_READDIRPLUS;
+       /*
+        * Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower
+        * authentication.
+        */
+       if (nfs4_disable_idmapping &&
+                       server->client->cl_auth->au_flavor == RPC_AUTH_UNIX)
+               server->caps |= NFS_CAP_UIDGID_NOMAP;
+
+
        /* Probe the root fh to retrieve its FSID and filehandle */
        error = nfs4_get_rootfh(server, mntfh);
        if (error < 0)
@@ -773,9 +790,6 @@ static int nfs4_init_server(struct nfs_server *server,
 
        /* Initialise the client representation from the mount data */
        server->flags = data->flags;
-       server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK;
-       if (!(data->flags & NFS_MOUNT_NORDIRPLUS))
-                       server->caps |= NFS_CAP_READDIRPLUS;
        server->options = data->options;
 
        /* Get a client record */
@@ -792,13 +806,6 @@ static int nfs4_init_server(struct nfs_server *server,
        if (error < 0)
                goto error;
 
-       /*
-        * Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower
-        * authentication.
-        */
-       if (nfs4_disable_idmapping && data->auth_flavors[0] == RPC_AUTH_UNIX)
-               server->caps |= NFS_CAP_UIDGID_NOMAP;
-
        if (data->rsize)
                server->rsize = nfs_block_size(data->rsize, NULL);
        if (data->wsize)
@@ -876,7 +883,6 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
 
        /* Initialise the client representation from the parent server */
        nfs_server_copy_userdata(server, parent_server);
-       server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR;
 
        /* Get a client representation.
         * Note: NFSv4 always uses TCP, */
index 4fb234d3aefb240f3d067523bda6df467336d2d0..22d10623f5ee3b591de10631044cf90cef4d353a 100644 (file)
@@ -158,11 +158,14 @@ static int filelayout_async_handle_error(struct rpc_task *task,
        case -NFS4ERR_OPENMODE:
                if (state == NULL)
                        break;
-               nfs4_schedule_stateid_recovery(mds_server, state);
+               if (nfs4_schedule_stateid_recovery(mds_server, state) < 0)
+                       goto out_bad_stateid;
                goto wait_on_recovery;
        case -NFS4ERR_EXPIRED:
-               if (state != NULL)
-                       nfs4_schedule_stateid_recovery(mds_server, state);
+               if (state != NULL) {
+                       if (nfs4_schedule_stateid_recovery(mds_server, state) < 0)
+                               goto out_bad_stateid;
+               }
                nfs4_schedule_lease_recovery(mds_client);
                goto wait_on_recovery;
        /* DS session errors */
@@ -226,6 +229,9 @@ static int filelayout_async_handle_error(struct rpc_task *task,
 out:
        task->tk_status = 0;
        return -EAGAIN;
+out_bad_stateid:
+       task->tk_status = -EIO;
+       return 0;
 wait_on_recovery:
        rpc_sleep_on(&mds_client->cl_rpcwaitq, task, NULL);
        if (test_bit(NFS4CLNT_MANAGER_RUNNING, &mds_client->cl_state) == 0)
@@ -299,6 +305,10 @@ static void filelayout_read_prepare(struct rpc_task *task, void *data)
 {
        struct nfs_read_data *rdata = data;
 
+       if (unlikely(test_bit(NFS_CONTEXT_BAD, &rdata->args.context->flags))) {
+               rpc_exit(task, -EIO);
+               return;
+       }
        if (filelayout_reset_to_mds(rdata->header->lseg)) {
                dprintk("%s task %u reset io to MDS\n", __func__, task->tk_pid);
                filelayout_reset_read(rdata);
@@ -307,10 +317,13 @@ static void filelayout_read_prepare(struct rpc_task *task, void *data)
        }
        rdata->read_done_cb = filelayout_read_done_cb;
 
-       nfs41_setup_sequence(rdata->ds_clp->cl_session,
+       if (nfs41_setup_sequence(rdata->ds_clp->cl_session,
                        &rdata->args.seq_args,
                        &rdata->res.seq_res,
-                       task);
+                       task))
+               return;
+       nfs4_set_rw_stateid(&rdata->args.stateid, rdata->args.context,
+                       rdata->args.lock_context, FMODE_READ);
 }
 
 static void filelayout_read_call_done(struct rpc_task *task, void *data)
@@ -401,16 +414,23 @@ static void filelayout_write_prepare(struct rpc_task *task, void *data)
 {
        struct nfs_write_data *wdata = data;
 
+       if (unlikely(test_bit(NFS_CONTEXT_BAD, &wdata->args.context->flags))) {
+               rpc_exit(task, -EIO);
+               return;
+       }
        if (filelayout_reset_to_mds(wdata->header->lseg)) {
                dprintk("%s task %u reset io to MDS\n", __func__, task->tk_pid);
                filelayout_reset_write(wdata);
                rpc_exit(task, 0);
                return;
        }
-       nfs41_setup_sequence(wdata->ds_clp->cl_session,
+       if (nfs41_setup_sequence(wdata->ds_clp->cl_session,
                        &wdata->args.seq_args,
                        &wdata->res.seq_res,
-                       task);
+                       task))
+               return;
+       nfs4_set_rw_stateid(&wdata->args.stateid, wdata->args.context,
+                       wdata->args.lock_context, FMODE_WRITE);
 }
 
 static void filelayout_write_call_done(struct rpc_task *task, void *data)
index 0dd766079e1ca34feb26ba2da2dafafeb1e24060..cdb0b41a48109e274364e4e69150c5b222114953 100644 (file)
@@ -134,33 +134,38 @@ static size_t nfs_parse_server_name(char *string, size_t len,
        return ret;
 }
 
+/**
+ * nfs_find_best_sec - Find a security mechanism supported locally
+ * @flavors: List of security tuples returned by SECINFO procedure
+ *
+ * Return the pseudoflavor of the first security mechanism in
+ * "flavors" that is locally supported.  Return RPC_AUTH_UNIX if
+ * no matching flavor is found in the array.  The "flavors" array
+ * is searched in the order returned from the server, per RFC 3530
+ * recommendation.
+ */
 rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
 {
-       struct gss_api_mech *mech;
-       struct xdr_netobj oid;
-       int i;
-       rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX;
+       rpc_authflavor_t pseudoflavor;
+       struct nfs4_secinfo4 *secinfo;
+       unsigned int i;
 
        for (i = 0; i < flavors->num_flavors; i++) {
-               struct nfs4_secinfo_flavor *flavor;
-               flavor = &flavors->flavors[i];
-
-               if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) {
-                       pseudoflavor = flavor->flavor;
-                       break;
-               } else if (flavor->flavor == RPC_AUTH_GSS) {
-                       oid.len  = flavor->gss.sec_oid4.len;
-                       oid.data = flavor->gss.sec_oid4.data;
-                       mech = gss_mech_get_by_OID(&oid);
-                       if (!mech)
-                               continue;
-                       pseudoflavor = gss_svc_to_pseudoflavor(mech, flavor->gss.service);
-                       gss_mech_put(mech);
+               secinfo = &flavors->flavors[i];
+
+               switch (secinfo->flavor) {
+               case RPC_AUTH_NULL:
+               case RPC_AUTH_UNIX:
+               case RPC_AUTH_GSS:
+                       pseudoflavor = rpcauth_get_pseudoflavor(secinfo->flavor,
+                                                       &secinfo->flavor_info);
+                       if (pseudoflavor != RPC_AUTH_MAXFLAVOR)
+                               return pseudoflavor;
                        break;
                }
        }
 
-       return pseudoflavor;
+       return RPC_AUTH_UNIX;
 }
 
 static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name)
index 0ad025eb523bc6b651d82497c29a0d27ebdf3dac..9da4bd55eb3019a964f8a7a7b0f89e2bab4c8b42 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);
@@ -295,19 +297,30 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
                        }
                        if (state == NULL)
                                break;
-                       nfs4_schedule_stateid_recovery(server, state);
+                       ret = nfs4_schedule_stateid_recovery(server, state);
+                       if (ret < 0)
+                               break;
                        goto wait_on_recovery;
                case -NFS4ERR_DELEG_REVOKED:
                case -NFS4ERR_ADMIN_REVOKED:
                case -NFS4ERR_BAD_STATEID:
+                       if (inode != NULL && nfs4_have_delegation(inode, FMODE_READ)) {
+                               nfs_remove_bad_delegation(inode);
+                               exception->retry = 1;
+                               break;
+                       }
                        if (state == NULL)
                                break;
-                       nfs_remove_bad_delegation(state->inode);
-                       nfs4_schedule_stateid_recovery(server, state);
+                       ret = nfs4_schedule_stateid_recovery(server, state);
+                       if (ret < 0)
+                               break;
                        goto wait_on_recovery;
                case -NFS4ERR_EXPIRED:
-                       if (state != NULL)
-                               nfs4_schedule_stateid_recovery(server, state);
+                       if (state != NULL) {
+                               ret = nfs4_schedule_stateid_recovery(server, state);
+                               if (ret < 0)
+                                       break;
+                       }
                case -NFS4ERR_STALE_STATEID:
                case -NFS4ERR_STALE_CLIENTID:
                        nfs4_schedule_lease_recovery(clp);
@@ -756,10 +769,40 @@ struct nfs4_opendata {
        struct iattr attrs;
        unsigned long timestamp;
        unsigned int rpc_done : 1;
+       unsigned int is_recover : 1;
        int rpc_status;
        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)
 {
@@ -775,6 +818,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);
@@ -793,7 +837,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
@@ -811,7 +854,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];
 
@@ -924,6 +979,7 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *
        if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
                nfs4_stateid_copy(&state->stateid, stateid);
        nfs4_stateid_copy(&state->open_stateid, stateid);
+       set_bit(NFS_OPEN_STATE, &state->flags);
        switch (fmode) {
                case FMODE_READ:
                        set_bit(NFS_O_RDONLY_STATE, &state->flags);
@@ -1047,9 +1103,11 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
                nfs4_stateid_copy(&stateid, &delegation->stateid);
                rcu_read_unlock();
                nfs_release_seqid(opendata->o_arg.seqid);
-               ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode);
-               if (ret != 0)
-                       goto out;
+               if (!opendata->is_recover) {
+                       ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode);
+                       if (ret != 0)
+                               goto out;
+               }
                ret = -EAGAIN;
 
                /* Try to update the stateid using the delegation */
@@ -1194,11 +1252,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;
@@ -1234,6 +1294,7 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *
 
        /* memory barrier prior to reading state->n_* */
        clear_bit(NFS_DELEGATED_STATE, &state->flags);
+       clear_bit(NFS_OPEN_STATE, &state->flags);
        smp_rmb();
        if (state->n_rdwr != 0) {
                clear_bit(NFS_O_RDWR_STATE, &state->flags);
@@ -1284,11 +1345,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)
@@ -1307,6 +1367,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);
@@ -1321,71 +1383,72 @@ 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;
 }
 
-static int _nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
+static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct nfs4_state *state, const nfs4_stateid *stateid, int err)
 {
-       struct nfs4_opendata *opendata;
-       int ret;
-
-       opendata = nfs4_open_recoverdata_alloc(ctx, state);
-       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);
-       return ret;
+       switch (err) {
+               default:
+                       printk(KERN_ERR "NFS: %s: unhandled error "
+                                       "%d.\n", __func__, err);
+               case 0:
+               case -ENOENT:
+               case -ESTALE:
+                       break;
+               case -NFS4ERR_BADSESSION:
+               case -NFS4ERR_BADSLOT:
+               case -NFS4ERR_BAD_HIGH_SLOT:
+               case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
+               case -NFS4ERR_DEADSESSION:
+                       set_bit(NFS_DELEGATED_STATE, &state->flags);
+                       nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
+                       return -EAGAIN;
+               case -NFS4ERR_STALE_CLIENTID:
+               case -NFS4ERR_STALE_STATEID:
+                       set_bit(NFS_DELEGATED_STATE, &state->flags);
+               case -NFS4ERR_EXPIRED:
+                       /* Don't recall a delegation if it was lost */
+                       nfs4_schedule_lease_recovery(server->nfs_client);
+                       return -EAGAIN;
+               case -NFS4ERR_DELEG_REVOKED:
+               case -NFS4ERR_ADMIN_REVOKED:
+               case -NFS4ERR_BAD_STATEID:
+               case -NFS4ERR_OPENMODE:
+                       nfs_inode_find_state_and_recover(state->inode,
+                                       stateid);
+                       nfs4_schedule_stateid_recovery(server, state);
+                       return 0;
+               case -NFS4ERR_DELAY:
+               case -NFS4ERR_GRACE:
+                       set_bit(NFS_DELEGATED_STATE, &state->flags);
+                       ssleep(1);
+                       return -EAGAIN;
+               case -ENOMEM:
+               case -NFS4ERR_DENIED:
+                       /* kill_proc(fl->fl_pid, SIGLOST, 1); */
+                       return 0;
+       }
+       return err;
 }
 
 int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid)
 {
-       struct nfs4_exception exception = { };
        struct nfs_server *server = NFS_SERVER(state->inode);
+       struct nfs4_opendata *opendata;
        int err;
-       do {
-               err = _nfs4_open_delegation_recall(ctx, state, stateid);
-               switch (err) {
-                       case 0:
-                       case -ENOENT:
-                       case -ESTALE:
-                               goto out;
-                       case -NFS4ERR_BADSESSION:
-                       case -NFS4ERR_BADSLOT:
-                       case -NFS4ERR_BAD_HIGH_SLOT:
-                       case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
-                       case -NFS4ERR_DEADSESSION:
-                               set_bit(NFS_DELEGATED_STATE, &state->flags);
-                               nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
-                               err = -EAGAIN;
-                               goto out;
-                       case -NFS4ERR_STALE_CLIENTID:
-                       case -NFS4ERR_STALE_STATEID:
-                               set_bit(NFS_DELEGATED_STATE, &state->flags);
-                       case -NFS4ERR_EXPIRED:
-                               /* Don't recall a delegation if it was lost */
-                               nfs4_schedule_lease_recovery(server->nfs_client);
-                               err = -EAGAIN;
-                               goto out;
-                       case -NFS4ERR_DELEG_REVOKED:
-                       case -NFS4ERR_ADMIN_REVOKED:
-                       case -NFS4ERR_BAD_STATEID:
-                               nfs_inode_find_state_and_recover(state->inode,
-                                               stateid);
-                               nfs4_schedule_stateid_recovery(server, state);
-                       case -ENOMEM:
-                               err = 0;
-                               goto out;
-               }
-               set_bit(NFS_DELEGATED_STATE, &state->flags);
-               err = nfs4_handle_exception(server, err, &exception);
-       } while (exception.retry);
-out:
-       return err;
+
+       opendata = nfs4_open_recoverdata_alloc(ctx, state,
+                       NFS4_OPEN_CLAIM_DELEG_CUR_FH);
+       if (IS_ERR(opendata))
+               return PTR_ERR(opendata);
+       nfs4_stateid_copy(&opendata->o_arg.u.delegation, stateid);
+       err = nfs4_open_recover(opendata, state);
+       nfs4_opendata_put(opendata);
+       return nfs4_handle_delegation_recall_error(server, state, stateid, err);
 }
 
 static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata)
@@ -1468,6 +1531,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
 {
        struct nfs4_opendata *data = calldata;
        struct nfs4_state_owner *sp = data->owner;
+       struct nfs_client *clp = sp->so_server->nfs_client;
 
        if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0)
                goto out_wait;
@@ -1483,15 +1547,20 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
                rcu_read_lock();
                delegation = rcu_dereference(NFS_I(data->state->inode)->delegation);
                if (data->o_arg.claim != NFS4_OPEN_CLAIM_DELEGATE_CUR &&
+                   data->o_arg.claim != NFS4_OPEN_CLAIM_DELEG_CUR_FH &&
                    can_open_delegated(delegation, data->o_arg.fmode))
                        goto unlock_no_action;
                rcu_read_unlock();
        }
        /* Update client id. */
-       data->o_arg.clientid = sp->so_server->nfs_client->cl_clientid;
-       if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS) {
-               task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR];
+       data->o_arg.clientid = clp->cl_clientid;
+       switch (data->o_arg.claim) {
+       case NFS4_OPEN_CLAIM_PREVIOUS:
+       case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
+       case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
                data->o_arg.open_bitmap = &nfs4_open_noattr_bitmap[0];
+       case NFS4_OPEN_CLAIM_FH:
+               task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR];
                nfs_copy_fh(&data->o_res.fh, data->o_arg.fh);
        }
        data->timestamp = jiffies;
@@ -1500,6 +1569,16 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
                                &data->o_res.seq_res,
                                task) != 0)
                nfs_release_seqid(data->o_arg.seqid);
+
+       /* Set the create mode (note dependency on the session type) */
+       data->o_arg.createmode = NFS4_CREATE_UNCHECKED;
+       if (data->o_arg.open_flags & O_EXCL) {
+               data->o_arg.createmode = NFS4_CREATE_EXCLUSIVE;
+               if (nfs4_has_persistent_session(clp))
+                       data->o_arg.createmode = NFS4_CREATE_GUARDED;
+               else if (clp->cl_mvops->minor_version > 0)
+                       data->o_arg.createmode = NFS4_CREATE_EXCLUSIVE4_1;
+       }
        return;
 unlock_no_action:
        rcu_read_unlock();
@@ -1595,8 +1674,11 @@ static int nfs4_run_open_task(struct nfs4_opendata *data, int isrecover)
        data->rpc_done = 0;
        data->rpc_status = 0;
        data->cancelled = 0;
-       if (isrecover)
+       data->is_recover = 0;
+       if (isrecover) {
                nfs4_set_sequence_privileged(&o_arg->seq_args);
+               data->is_recover = 1;
+       }
        task = rpc_run_task(&task_setup_data);
         if (IS_ERR(task))
                 return PTR_ERR(task);
@@ -1721,7 +1803,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);
@@ -1739,6 +1822,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;
@@ -1759,7 +1844,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;
@@ -1821,6 +1906,7 @@ static int nfs41_check_open_stateid(struct nfs4_state *state)
                clear_bit(NFS_O_RDONLY_STATE, &state->flags);
                clear_bit(NFS_O_WRONLY_STATE, &state->flags);
                clear_bit(NFS_O_RDWR_STATE, &state->flags);
+               clear_bit(NFS_OPEN_STATE, &state->flags);
        }
        return status;
 }
@@ -1881,10 +1967,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;
@@ -1906,6 +1990,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 */
@@ -1921,7 +2006,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;
 
@@ -1938,7 +2026,8 @@ static int _nfs4_do_open(struct inode *dir,
        if (status != 0)
                goto err_opendata_put;
 
-       if (opendata->o_arg.open_flags & O_EXCL) {
+       if ((opendata->o_arg.open_flags & O_EXCL) &&
+           (opendata->o_arg.createmode != NFS4_CREATE_GUARDED)) {
                nfs4_exclusive_attrset(opendata, sattr);
 
                nfs_fattr_init(opendata->o_res.f_attr);
@@ -1979,6 +2068,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;
@@ -2022,7 +2112,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;
@@ -2050,20 +2142,25 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                .rpc_cred       = cred,
         };
        unsigned long timestamp = jiffies;
+       fmode_t fmode;
+       bool truncate;
        int status;
 
        nfs_fattr_init(fattr);
 
-       if (state != NULL) {
+       /* Servers should only apply open mode checks for file size changes */
+       truncate = (sattr->ia_valid & ATTR_SIZE) ? true : false;
+       fmode = truncate ? FMODE_WRITE : FMODE_READ;
+
+       if (nfs4_copy_delegation_stateid(&arg.stateid, inode, fmode)) {
+               /* Use that stateid */
+       } else if (truncate && state != NULL && nfs4_valid_open_stateid(state)) {
                struct nfs_lockowner lockowner = {
                        .l_owner = current->files,
                        .l_pid = current->tgid,
                };
                nfs4_select_rw_stateid(&arg.stateid, state, FMODE_WRITE,
                                &lockowner);
-       } else if (nfs4_copy_delegation_stateid(&arg.stateid, inode,
-                               FMODE_WRITE)) {
-               /* Use that stateid */
        } else
                nfs4_stateid_copy(&arg.stateid, &zero_stateid);
 
@@ -2087,6 +2184,13 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                err = _nfs4_do_setattr(inode, cred, fattr, sattr, state);
                switch (err) {
                case -NFS4ERR_OPENMODE:
+                       if (!(sattr->ia_valid & ATTR_SIZE)) {
+                               pr_warn_once("NFSv4: server %s is incorrectly "
+                                               "applying open mode checks to "
+                                               "a SETATTR that is not "
+                                               "changing file size.\n",
+                                               server->nfs_client->cl_hostname);
+                       }
                        if (state && !(state->state & FMODE_WRITE)) {
                                err = -EBADF;
                                if (sattr->ia_valid & ATTR_OPEN)
@@ -2130,11 +2234,19 @@ static void nfs4_close_clear_stateid_flags(struct nfs4_state *state,
                fmode_t fmode)
 {
        spin_lock(&state->owner->so_lock);
-       if (!(fmode & FMODE_READ))
+       clear_bit(NFS_O_RDWR_STATE, &state->flags);
+       switch (fmode & (FMODE_READ|FMODE_WRITE)) {
+       case FMODE_WRITE:
                clear_bit(NFS_O_RDONLY_STATE, &state->flags);
-       if (!(fmode & FMODE_WRITE))
+               break;
+       case FMODE_READ:
                clear_bit(NFS_O_WRONLY_STATE, &state->flags);
-       clear_bit(NFS_O_RDWR_STATE, &state->flags);
+               break;
+       case 0:
+               clear_bit(NFS_O_RDONLY_STATE, &state->flags);
+               clear_bit(NFS_O_WRONLY_STATE, &state->flags);
+               clear_bit(NFS_OPEN_STATE, &state->flags);
+       }
        spin_unlock(&state->owner->so_lock);
 }
 
@@ -2202,6 +2314,8 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
                        calldata->arg.fmode &= ~FMODE_WRITE;
                }
        }
+       if (!nfs4_valid_open_stateid(state))
+               call_close = 0;
        spin_unlock(&state->owner->so_lock);
 
        if (!call_close) {
@@ -2212,8 +2326,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
        if (calldata->arg.fmode == 0) {
                task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE];
                if (calldata->roc &&
-                   pnfs_roc_drain(inode, &calldata->roc_barrier, task))
+                   pnfs_roc_drain(inode, &calldata->roc_barrier, task)) {
+                       nfs_release_seqid(calldata->arg.seqid);
                        goto out_wait;
+                   }
        }
 
        nfs_fattr_init(calldata->res.fattr);
@@ -2444,7 +2560,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);
@@ -2452,27 +2568,36 @@ static int nfs4_lookup_root_sec(struct nfs_server *server, struct nfs_fh *fhandl
        return ret;
 }
 
+/*
+ * Retry pseudoroot lookup with various security flavors.  We do this when:
+ *
+ *   NFSv4.0: the PUTROOTFH operation returns NFS4ERR_WRONGSEC
+ *   NFSv4.1: the server does not support the SECINFO_NO_NAME operation
+ *
+ * Returns zero on success, or a negative NFS4ERR value, or a
+ * negative errno value.
+ */
 static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
                              struct nfs_fsinfo *info)
 {
-       int i, len, status = 0;
-       rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS];
-
-       len = rpcauth_list_flavors(flav_array, ARRAY_SIZE(flav_array));
-       if (len < 0)
-               return len;
-
-       for (i = 0; i < len; i++) {
-               /* AUTH_UNIX is the default flavor if none was specified,
-                * thus has already been tried. */
-               if (flav_array[i] == RPC_AUTH_UNIX)
-                       continue;
+       /* Per 3530bis 15.33.5 */
+       static const rpc_authflavor_t flav_array[] = {
+               RPC_AUTH_GSS_KRB5P,
+               RPC_AUTH_GSS_KRB5I,
+               RPC_AUTH_GSS_KRB5,
+               RPC_AUTH_UNIX,                  /* courtesy */
+               RPC_AUTH_NULL,
+       };
+       int status = -EPERM;
+       size_t i;
 
+       for (i = 0; i < ARRAY_SIZE(flav_array); i++) {
                status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]);
                if (status == -NFS4ERR_WRONGSEC || status == -EACCES)
                        continue;
                break;
        }
+
        /*
         * -EACCESS could mean that the user doesn't have correct permissions
         * to access the mount.  It could also mean that we tried to mount
@@ -2485,24 +2610,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);
 }
 
@@ -3381,12 +3518,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;
 }
@@ -3446,6 +3592,46 @@ static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
        return err;
 }
 
+int nfs4_set_rw_stateid(nfs4_stateid *stateid,
+               const struct nfs_open_context *ctx,
+               const struct nfs_lock_context *l_ctx,
+               fmode_t fmode)
+{
+       const struct nfs_lockowner *lockowner = NULL;
+
+       if (l_ctx != NULL)
+               lockowner = &l_ctx->lockowner;
+       return nfs4_select_rw_stateid(stateid, ctx->state, fmode, lockowner);
+}
+EXPORT_SYMBOL_GPL(nfs4_set_rw_stateid);
+
+static bool nfs4_stateid_is_current(nfs4_stateid *stateid,
+               const struct nfs_open_context *ctx,
+               const struct nfs_lock_context *l_ctx,
+               fmode_t fmode)
+{
+       nfs4_stateid current_stateid;
+
+       if (nfs4_set_rw_stateid(&current_stateid, ctx, l_ctx, fmode))
+               return false;
+       return nfs4_stateid_match(stateid, &current_stateid);
+}
+
+static bool nfs4_error_stateid_expired(int err)
+{
+       switch (err) {
+       case -NFS4ERR_DELEG_REVOKED:
+       case -NFS4ERR_ADMIN_REVOKED:
+       case -NFS4ERR_BAD_STATEID:
+       case -NFS4ERR_STALE_STATEID:
+       case -NFS4ERR_OLD_STATEID:
+       case -NFS4ERR_OPENMODE:
+       case -NFS4ERR_EXPIRED:
+               return true;
+       }
+       return false;
+}
+
 void __nfs4_read_done_cb(struct nfs_read_data *data)
 {
        nfs_invalidate_atime(data->header->inode);
@@ -3466,6 +3652,20 @@ static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_read_data *data)
        return 0;
 }
 
+static bool nfs4_read_stateid_changed(struct rpc_task *task,
+               struct nfs_readargs *args)
+{
+
+       if (!nfs4_error_stateid_expired(task->tk_status) ||
+               nfs4_stateid_is_current(&args->stateid,
+                               args->context,
+                               args->lock_context,
+                               FMODE_READ))
+               return false;
+       rpc_restart_call_prepare(task);
+       return true;
+}
+
 static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
 {
 
@@ -3473,7 +3673,8 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
 
        if (!nfs4_sequence_done(task, &data->res.seq_res))
                return -EAGAIN;
-
+       if (nfs4_read_stateid_changed(task, &data->args))
+               return -EAGAIN;
        return data->read_done_cb ? data->read_done_cb(task, data) :
                                    nfs4_read_done_cb(task, data);
 }
@@ -3488,10 +3689,13 @@ static void nfs4_proc_read_setup(struct nfs_read_data *data, struct rpc_message
 
 static void nfs4_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data)
 {
-       nfs4_setup_sequence(NFS_SERVER(data->header->inode),
+       if (nfs4_setup_sequence(NFS_SERVER(data->header->inode),
                        &data->args.seq_args,
                        &data->res.seq_res,
-                       task);
+                       task))
+               return;
+       nfs4_set_rw_stateid(&data->args.stateid, data->args.context,
+                       data->args.lock_context, FMODE_READ);
 }
 
 static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data)
@@ -3509,10 +3713,26 @@ static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data
        return 0;
 }
 
+static bool nfs4_write_stateid_changed(struct rpc_task *task,
+               struct nfs_writeargs *args)
+{
+
+       if (!nfs4_error_stateid_expired(task->tk_status) ||
+               nfs4_stateid_is_current(&args->stateid,
+                               args->context,
+                               args->lock_context,
+                               FMODE_WRITE))
+               return false;
+       rpc_restart_call_prepare(task);
+       return true;
+}
+
 static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
 {
        if (!nfs4_sequence_done(task, &data->res.seq_res))
                return -EAGAIN;
+       if (nfs4_write_stateid_changed(task, &data->args))
+               return -EAGAIN;
        return data->write_done_cb ? data->write_done_cb(task, data) :
                nfs4_write_done_cb(task, data);
 }
@@ -3552,10 +3772,13 @@ static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_messag
 
 static void nfs4_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data)
 {
-       nfs4_setup_sequence(NFS_SERVER(data->header->inode),
+       if (nfs4_setup_sequence(NFS_SERVER(data->header->inode),
                        &data->args.seq_args,
                        &data->res.seq_res,
-                       task);
+                       task))
+               return;
+       nfs4_set_rw_stateid(&data->args.stateid, data->args.context,
+                       data->args.lock_context, FMODE_WRITE);
 }
 
 static void nfs4_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)
@@ -3657,7 +3880,7 @@ static int nfs4_proc_async_renew(struct nfs_client *clp, struct rpc_cred *cred,
                return -ENOMEM;
        data->client = clp;
        data->timestamp = jiffies;
-       return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT,
+       return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT,
                        &nfs4_renew_ops, data);
 }
 
@@ -3671,7 +3894,7 @@ static int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred)
        unsigned long now = jiffies;
        int status;
 
-       status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
+       status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
        if (status < 0)
                return status;
        do_renew_lease(clp, now);
@@ -3981,11 +4204,14 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
                case -NFS4ERR_OPENMODE:
                        if (state == NULL)
                                break;
-                       nfs4_schedule_stateid_recovery(server, state);
+                       if (nfs4_schedule_stateid_recovery(server, state) < 0)
+                               goto stateid_invalid;
                        goto wait_on_recovery;
                case -NFS4ERR_EXPIRED:
-                       if (state != NULL)
-                               nfs4_schedule_stateid_recovery(server, state);
+                       if (state != NULL) {
+                               if (nfs4_schedule_stateid_recovery(server, state) < 0)
+                                       goto stateid_invalid;
+                       }
                case -NFS4ERR_STALE_STATEID:
                case -NFS4ERR_STALE_CLIENTID:
                        nfs4_schedule_lease_recovery(clp);
@@ -4017,6 +4243,9 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
        }
        task->tk_status = nfs4_map_errors(task->tk_status);
        return 0;
+stateid_invalid:
+       task->tk_status = -EIO;
+       return 0;
 wait_on_recovery:
        rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
        if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
@@ -4144,27 +4373,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;
 }
@@ -4628,17 +4847,23 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
                if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0) {
                        goto out_release_lock_seqid;
                }
-               data->arg.open_stateid = &state->stateid;
+               data->arg.open_stateid = &state->open_stateid;
                data->arg.new_lock_owner = 1;
                data->res.open_seqid = data->arg.open_seqid;
        } else
                data->arg.new_lock_owner = 0;
+       if (!nfs4_valid_open_stateid(state)) {
+               data->rpc_status = -EBADF;
+               task->tk_action = NULL;
+               goto out_release_open_seqid;
+       }
        data->timestamp = jiffies;
        if (nfs4_setup_sequence(data->server,
                                &data->arg.seq_args,
                                &data->res.seq_res,
                                task) == 0)
                return;
+out_release_open_seqid:
        nfs_release_seqid(data->arg.open_seqid);
 out_release_lock_seqid:
        nfs_release_seqid(data->arg.lock_seqid);
@@ -4984,58 +5209,16 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
        return status;
 }
 
-int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
+int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state, const nfs4_stateid *stateid)
 {
        struct nfs_server *server = NFS_SERVER(state->inode);
-       struct nfs4_exception exception = { };
        int err;
 
        err = nfs4_set_lock_state(state, fl);
        if (err != 0)
-               goto out;
-       do {
-               err = _nfs4_do_setlk(state, F_SETLK, fl, NFS_LOCK_NEW);
-               switch (err) {
-                       default:
-                               printk(KERN_ERR "NFS: %s: unhandled error "
-                                       "%d.\n", __func__, err);
-                       case 0:
-                       case -ESTALE:
-                               goto out;
-                       case -NFS4ERR_STALE_CLIENTID:
-                       case -NFS4ERR_STALE_STATEID:
-                               set_bit(NFS_DELEGATED_STATE, &state->flags);
-                       case -NFS4ERR_EXPIRED:
-                               nfs4_schedule_lease_recovery(server->nfs_client);
-                               err = -EAGAIN;
-                               goto out;
-                       case -NFS4ERR_BADSESSION:
-                       case -NFS4ERR_BADSLOT:
-                       case -NFS4ERR_BAD_HIGH_SLOT:
-                       case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
-                       case -NFS4ERR_DEADSESSION:
-                               set_bit(NFS_DELEGATED_STATE, &state->flags);
-                               nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
-                               err = -EAGAIN;
-                               goto out;
-                       case -NFS4ERR_DELEG_REVOKED:
-                       case -NFS4ERR_ADMIN_REVOKED:
-                       case -NFS4ERR_BAD_STATEID:
-                       case -NFS4ERR_OPENMODE:
-                               nfs4_schedule_stateid_recovery(server, state);
-                               err = 0;
-                               goto out;
-                       case -ENOMEM:
-                       case -NFS4ERR_DENIED:
-                               /* kill_proc(fl->fl_pid, SIGLOST, 1); */
-                               err = 0;
-                               goto out;
-               }
-               set_bit(NFS_DELEGATED_STATE, &state->flags);
-               err = nfs4_handle_exception(server, err, &exception);
-       } while (exception.retry);
-out:
-       return err;
+               return err;
+       err = _nfs4_do_setlk(state, F_SETLK, fl, NFS_LOCK_NEW);
+       return nfs4_handle_delegation_recall_error(server, state, stateid, err);
 }
 
 struct nfs_release_lockowner_data {
@@ -5849,7 +6032,7 @@ static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
                .rpc_client = clp->cl_rpcclient,
                .rpc_message = &msg,
                .callback_ops = &nfs41_sequence_ops,
-               .flags = RPC_TASK_ASYNC | RPC_TASK_SOFT,
+               .flags = RPC_TASK_ASYNC | RPC_TASK_TIMEOUT,
        };
 
        if (!atomic_inc_not_zero(&clp->cl_count))
@@ -6726,6 +6909,10 @@ static const struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = {
 
 static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
        .minor_version = 0,
+       .init_caps = NFS_CAP_READDIRPLUS
+               | NFS_CAP_ATOMIC_OPEN
+               | NFS_CAP_CHANGE_ATTR
+               | NFS_CAP_POSIX_LOCK,
        .call_sync = _nfs4_call_sync,
        .match_stateid = nfs4_match_stateid,
        .find_root_sec = nfs4_find_root_sec,
@@ -6737,6 +6924,12 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
 #if defined(CONFIG_NFS_V4_1)
 static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
        .minor_version = 1,
+       .init_caps = NFS_CAP_READDIRPLUS
+               | NFS_CAP_ATOMIC_OPEN
+               | NFS_CAP_CHANGE_ATTR
+               | 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,
index d41a3518509fa5a0cf1dbbdcbed87f39fa3eff45..0b32f9483b7afb07d993fb92568bec662b3dd9a4 100644 (file)
@@ -154,18 +154,6 @@ struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
        return cred;
 }
 
-static void nfs4_clear_machine_cred(struct nfs_client *clp)
-{
-       struct rpc_cred *cred;
-
-       spin_lock(&clp->cl_lock);
-       cred = clp->cl_machine_cred;
-       clp->cl_machine_cred = NULL;
-       spin_unlock(&clp->cl_lock);
-       if (cred != NULL)
-               put_rpccred(cred);
-}
-
 static struct rpc_cred *
 nfs4_get_renew_cred_server_locked(struct nfs_server *server)
 {
@@ -699,6 +687,8 @@ __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner)
        list_for_each_entry(state, &nfsi->open_states, inode_states) {
                if (state->owner != owner)
                        continue;
+               if (!nfs4_valid_open_stateid(state))
+                       continue;
                if (atomic_inc_not_zero(&state->count))
                        return state;
        }
@@ -987,13 +977,14 @@ int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl)
        return 0;
 }
 
-static bool nfs4_copy_lock_stateid(nfs4_stateid *dst, struct nfs4_state *state,
+static int nfs4_copy_lock_stateid(nfs4_stateid *dst,
+               struct nfs4_state *state,
                const struct nfs_lockowner *lockowner)
 {
        struct nfs4_lock_state *lsp;
        fl_owner_t fl_owner;
        pid_t fl_pid;
-       bool ret = false;
+       int ret = -ENOENT;
 
 
        if (lockowner == NULL)
@@ -1008,7 +999,10 @@ static bool nfs4_copy_lock_stateid(nfs4_stateid *dst, struct nfs4_state *state,
        lsp = __nfs4_find_lock_state(state, fl_owner, fl_pid, NFS4_ANY_LOCK_TYPE);
        if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) {
                nfs4_stateid_copy(dst, &lsp->ls_stateid);
-               ret = true;
+               ret = 0;
+               smp_rmb();
+               if (!list_empty(&lsp->ls_seqid.list))
+                       ret = -EWOULDBLOCK;
        }
        spin_unlock(&state->state_lock);
        nfs4_put_lock_state(lsp);
@@ -1016,28 +1010,44 @@ static bool nfs4_copy_lock_stateid(nfs4_stateid *dst, struct nfs4_state *state,
        return ret;
 }
 
-static void nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state)
+static int nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state)
 {
+       const nfs4_stateid *src;
+       int ret;
        int seq;
 
        do {
+               src = &zero_stateid;
                seq = read_seqbegin(&state->seqlock);
-               nfs4_stateid_copy(dst, &state->stateid);
+               if (test_bit(NFS_OPEN_STATE, &state->flags))
+                       src = &state->open_stateid;
+               nfs4_stateid_copy(dst, src);
+               ret = 0;
+               smp_rmb();
+               if (!list_empty(&state->owner->so_seqid.list))
+                       ret = -EWOULDBLOCK;
        } while (read_seqretry(&state->seqlock, seq));
+       return ret;
 }
 
 /*
  * Byte-range lock aware utility to initialize the stateid of read/write
  * requests.
  */
-void nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state,
+int nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state,
                fmode_t fmode, const struct nfs_lockowner *lockowner)
 {
+       int ret = 0;
        if (nfs4_copy_delegation_stateid(dst, state->inode, fmode))
-               return;
-       if (nfs4_copy_lock_stateid(dst, state, lockowner))
-               return;
-       nfs4_copy_open_stateid(dst, state);
+               goto out;
+       ret = nfs4_copy_lock_stateid(dst, state, lockowner);
+       if (ret != -ENOENT)
+               goto out;
+       ret = nfs4_copy_open_stateid(dst, state);
+out:
+       if (nfs_server_capable(state->inode, NFS_CAP_STATEID_NFSV41))
+               dst->seqid = 0;
+       return ret;
 }
 
 struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask)
@@ -1286,14 +1296,17 @@ static int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_s
        return 1;
 }
 
-void nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_state *state)
+int nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_state *state)
 {
        struct nfs_client *clp = server->nfs_client;
 
+       if (!nfs4_valid_open_stateid(state))
+               return -EBADF;
        nfs4_state_mark_reclaim_nograce(clp, state);
        dprintk("%s: scheduling stateid recovery for server %s\n", __func__,
                        clp->cl_hostname);
        nfs4_schedule_state_manager(clp);
+       return 0;
 }
 EXPORT_SYMBOL_GPL(nfs4_schedule_stateid_recovery);
 
@@ -1323,6 +1336,27 @@ void nfs_inode_find_state_and_recover(struct inode *inode,
                nfs4_schedule_state_manager(clp);
 }
 
+static void nfs4_state_mark_open_context_bad(struct nfs4_state *state)
+{
+       struct inode *inode = state->inode;
+       struct nfs_inode *nfsi = NFS_I(inode);
+       struct nfs_open_context *ctx;
+
+       spin_lock(&inode->i_lock);
+       list_for_each_entry(ctx, &nfsi->open_files, list) {
+               if (ctx->state != state)
+                       continue;
+               set_bit(NFS_CONTEXT_BAD, &ctx->flags);
+       }
+       spin_unlock(&inode->i_lock);
+}
+
+static void nfs4_state_mark_recovery_failed(struct nfs4_state *state, int error)
+{
+       set_bit(NFS_STATE_RECOVERY_FAILED, &state->flags);
+       nfs4_state_mark_open_context_bad(state);
+}
+
 
 static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops)
 {
@@ -1398,6 +1432,8 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs
        list_for_each_entry(state, &sp->so_states, open_states) {
                if (!test_and_clear_bit(ops->state_flag_bit, &state->flags))
                        continue;
+               if (!nfs4_valid_open_stateid(state))
+                       continue;
                if (state->state == 0)
                        continue;
                atomic_inc(&state->count);
@@ -1430,11 +1466,10 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs
                                 * Open state on this file cannot be recovered
                                 * All we can do is revert to using the zero stateid.
                                 */
-                               memset(&state->stateid, 0,
-                                       sizeof(state->stateid));
-                               /* Mark the file as being 'closed' */
-                               state->state = 0;
+                               nfs4_state_mark_recovery_failed(state, status);
                                break;
+                       case -EAGAIN:
+                               ssleep(1);
                        case -NFS4ERR_ADMIN_REVOKED:
                        case -NFS4ERR_STALE_STATEID:
                        case -NFS4ERR_BAD_STATEID:
@@ -1696,6 +1731,10 @@ static int nfs4_check_lease(struct nfs_client *clp)
        }
        status = ops->renew_lease(clp, cred);
        put_rpccred(cred);
+       if (status == -ETIMEDOUT) {
+               set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
+               return 0;
+       }
 out:
        return nfs4_recovery_handle_error(clp, status);
 }
@@ -1725,10 +1764,6 @@ static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
                clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
                return -EPERM;
        case -EACCES:
-               if (clp->cl_machine_cred == NULL)
-                       return -EACCES;
-               /* Handle case where the user hasn't set up machine creds */
-               nfs4_clear_machine_cred(clp);
        case -NFS4ERR_DELAY:
        case -ETIMEDOUT:
        case -EAGAIN:
@@ -1823,31 +1858,18 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
 {
        const struct nfs4_state_recovery_ops *ops =
                                clp->cl_mvops->reboot_recovery_ops;
-       rpc_authflavor_t *flavors, flav, save;
        struct rpc_clnt *clnt;
        struct rpc_cred *cred;
-       int i, len, status;
+       int i, status;
 
        dprintk("NFS: %s: testing '%s'\n", __func__, clp->cl_hostname);
 
-       len = NFS_MAX_SECFLAVORS;
-       flavors = kcalloc(len, sizeof(*flavors), GFP_KERNEL);
-       if (flavors == NULL) {
-               status = -ENOMEM;
-               goto out;
-       }
-       len = rpcauth_list_flavors(flavors, len);
-       if (len < 0) {
-               status = len;
-               goto out_free;
-       }
        clnt = clp->cl_rpcclient;
-       save = clnt->cl_auth->au_flavor;
        i = 0;
 
        mutex_lock(&nfs_clid_init_mutex);
-       status  = -ENOENT;
 again:
+       status  = -ENOENT;
        cred = ops->get_clid_cred(clp);
        if (cred == NULL)
                goto out_unlock;
@@ -1857,12 +1879,6 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
        switch (status) {
        case 0:
                break;
-
-       case -EACCES:
-               if (clp->cl_machine_cred == NULL)
-                       break;
-               /* Handle case where the user hasn't set up machine creds */
-               nfs4_clear_machine_cred(clp);
        case -NFS4ERR_DELAY:
        case -ETIMEDOUT:
        case -EAGAIN:
@@ -1871,17 +1887,12 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
                dprintk("NFS: %s after status %d, retrying\n",
                        __func__, status);
                goto again;
-
+       case -EACCES:
+               if (i++)
+                       break;
        case -NFS4ERR_CLID_INUSE:
        case -NFS4ERR_WRONGSEC:
-               status = -EPERM;
-               if (i >= len)
-                       break;
-
-               flav = flavors[i++];
-               if (flav == save)
-                       flav = flavors[i++];
-               clnt = rpc_clone_client_set_auth(clnt, flav);
+               clnt = rpc_clone_client_set_auth(clnt, RPC_AUTH_UNIX);
                if (IS_ERR(clnt)) {
                        status = PTR_ERR(clnt);
                        break;
@@ -1903,13 +1914,15 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
        case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
                                 * in nfs4_exchange_id */
                status = -EKEYEXPIRED;
+               break;
+       default:
+               pr_warn("NFS: %s unhandled error %d. Exiting with error EIO\n",
+                               __func__, status);
+               status = -EIO;
        }
 
 out_unlock:
        mutex_unlock(&nfs_clid_init_mutex);
-out_free:
-       kfree(flavors);
-out:
        dprintk("NFS: %s: status = %d\n", __func__, status);
        return status;
 }
index 569b166cc050143c8d2cfa7ec506b479e8517240..a5e1a3026d489240cb7a44ae87eec07df019ce3a 100644 (file)
@@ -252,6 +252,8 @@ struct dentry *nfs4_try_mount(int flags, const char *dev_name,
 
        dfprintk(MOUNT, "--> nfs4_try_mount()\n");
 
+       if (data->auth_flavors[0] == RPC_AUTH_MAXFLAVOR)
+               data->auth_flavors[0] = RPC_AUTH_UNIX;
        export_path = data->nfs_server.export_path;
        data->nfs_server.export_path = "/";
        root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,
index e3edda554ac7f4e17da3a6e115df1b1797d63efd..3c79c5878c6da6689df58f4b0990a777798fdf9e 100644 (file)
@@ -530,14 +530,10 @@ static int nfs4_stat_to_errno(int);
                                decode_setclientid_maxsz)
 #define NFS4_enc_setclientid_confirm_sz \
                                (compound_encode_hdr_maxsz + \
-                               encode_setclientid_confirm_maxsz + \
-                               encode_putrootfh_maxsz + \
-                               encode_fsinfo_maxsz)
+                               encode_setclientid_confirm_maxsz)
 #define NFS4_dec_setclientid_confirm_sz \
                                (compound_decode_hdr_maxsz + \
-                               decode_setclientid_confirm_maxsz + \
-                               decode_putrootfh_maxsz + \
-                               decode_fsinfo_maxsz)
+                               decode_setclientid_confirm_maxsz)
 #define NFS4_enc_lock_sz        (compound_encode_hdr_maxsz + \
                                encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
@@ -1058,8 +1054,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
        if (iap->ia_valid & ATTR_ATIME_SET) {
                bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET;
                *p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
-               *p++ = cpu_to_be32(0);
-               *p++ = cpu_to_be32(iap->ia_atime.tv_sec);
+               p = xdr_encode_hyper(p, (s64)iap->ia_atime.tv_sec);
                *p++ = cpu_to_be32(iap->ia_atime.tv_nsec);
        }
        else if (iap->ia_valid & ATTR_ATIME) {
@@ -1069,8 +1064,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const
        if (iap->ia_valid & ATTR_MTIME_SET) {
                bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
                *p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
-               *p++ = cpu_to_be32(0);
-               *p++ = cpu_to_be32(iap->ia_mtime.tv_sec);
+               p = xdr_encode_hyper(p, (s64)iap->ia_mtime.tv_sec);
                *p++ = cpu_to_be32(iap->ia_mtime.tv_nsec);
        }
        else if (iap->ia_valid & ATTR_MTIME) {
@@ -1366,33 +1360,28 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena
 
 static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_openargs *arg)
 {
+       struct iattr dummy;
        __be32 *p;
-       struct nfs_client *clp;
 
        p = reserve_space(xdr, 4);
-       switch(arg->open_flags & O_EXCL) {
-       case 0:
+       switch(arg->createmode) {
+       case NFS4_CREATE_UNCHECKED:
                *p = cpu_to_be32(NFS4_CREATE_UNCHECKED);
                encode_attrs(xdr, arg->u.attrs, arg->server);
                break;
-       default:
-               clp = arg->server->nfs_client;
-               if (clp->cl_mvops->minor_version > 0) {
-                       if (nfs4_has_persistent_session(clp)) {
-                               *p = cpu_to_be32(NFS4_CREATE_GUARDED);
-                               encode_attrs(xdr, arg->u.attrs, arg->server);
-                       } else {
-                               struct iattr dummy;
-
-                               *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE4_1);
-                               encode_nfs4_verifier(xdr, &arg->u.verifier);
-                               dummy.ia_valid = 0;
-                               encode_attrs(xdr, &dummy, arg->server);
-                       }
-               } else {
-                       *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE);
-                       encode_nfs4_verifier(xdr, &arg->u.verifier);
-               }
+       case NFS4_CREATE_GUARDED:
+               *p = cpu_to_be32(NFS4_CREATE_GUARDED);
+               encode_attrs(xdr, arg->u.attrs, arg->server);
+               break;
+       case NFS4_CREATE_EXCLUSIVE:
+               *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE);
+               encode_nfs4_verifier(xdr, &arg->u.verifier);
+               break;
+       case NFS4_CREATE_EXCLUSIVE4_1:
+               *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE4_1);
+               encode_nfs4_verifier(xdr, &arg->u.verifier);
+               dummy.ia_valid = 0;
+               encode_attrs(xdr, &dummy, arg->server);
        }
 }
 
@@ -1459,6 +1448,23 @@ static inline void encode_claim_delegate_cur(struct xdr_stream *xdr, const struc
        encode_string(xdr, name->len, name->name);
 }
 
+static inline void encode_claim_fh(struct xdr_stream *xdr)
+{
+       __be32 *p;
+
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(NFS4_OPEN_CLAIM_FH);
+}
+
+static inline void encode_claim_delegate_cur_fh(struct xdr_stream *xdr, const nfs4_stateid *stateid)
+{
+       __be32 *p;
+
+       p = reserve_space(xdr, 4);
+       *p = cpu_to_be32(NFS4_OPEN_CLAIM_DELEG_CUR_FH);
+       encode_nfs4_stateid(xdr, stateid);
+}
+
 static void encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg, struct compound_hdr *hdr)
 {
        encode_op_hdr(xdr, OP_OPEN, decode_open_maxsz, hdr);
@@ -1474,6 +1480,12 @@ static void encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg,
        case NFS4_OPEN_CLAIM_DELEGATE_CUR:
                encode_claim_delegate_cur(xdr, arg->name, &arg->u.delegation);
                break;
+       case NFS4_OPEN_CLAIM_FH:
+               encode_claim_fh(xdr);
+               break;
+       case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
+               encode_claim_delegate_cur_fh(xdr, &arg->u.delegation);
+               break;
        default:
                BUG();
        }
@@ -1506,35 +1518,12 @@ static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
        encode_op_hdr(xdr, OP_PUTROOTFH, decode_putrootfh_maxsz, hdr);
 }
 
-static void encode_open_stateid(struct xdr_stream *xdr,
-               const struct nfs_open_context *ctx,
-               const struct nfs_lock_context *l_ctx,
-               fmode_t fmode,
-               int zero_seqid)
-{
-       nfs4_stateid stateid;
-
-       if (ctx->state != NULL) {
-               const struct nfs_lockowner *lockowner = NULL;
-
-               if (l_ctx != NULL)
-                       lockowner = &l_ctx->lockowner;
-               nfs4_select_rw_stateid(&stateid, ctx->state,
-                               fmode, lockowner);
-               if (zero_seqid)
-                       stateid.seqid = 0;
-               encode_nfs4_stateid(xdr, &stateid);
-       } else
-               encode_nfs4_stateid(xdr, &zero_stateid);
-}
-
 static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args, struct compound_hdr *hdr)
 {
        __be32 *p;
 
        encode_op_hdr(xdr, OP_READ, decode_read_maxsz, hdr);
-       encode_open_stateid(xdr, args->context, args->lock_context,
-                       FMODE_READ, hdr->minorversion);
+       encode_nfs4_stateid(xdr, &args->stateid);
 
        p = reserve_space(xdr, 12);
        p = xdr_encode_hyper(p, args->offset);
@@ -1670,8 +1659,7 @@ static void encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *arg
        __be32 *p;
 
        encode_op_hdr(xdr, OP_WRITE, decode_write_maxsz, hdr);
-       encode_open_stateid(xdr, args->context, args->lock_context,
-                       FMODE_WRITE, hdr->minorversion);
+       encode_nfs4_stateid(xdr, &args->stateid);
 
        p = reserve_space(xdr, 16);
        p = xdr_encode_hyper(p, args->offset);
@@ -2609,12 +2597,9 @@ static void nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req,
        struct compound_hdr hdr = {
                .nops   = 0,
        };
-       const u32 lease_bitmap[3] = { FATTR4_WORD0_LEASE_TIME };
 
        encode_compound_hdr(xdr, req, &hdr);
        encode_setclientid_confirm(xdr, arg, &hdr);
-       encode_putrootfh(xdr, &hdr);
-       encode_fsinfo(xdr, lease_bitmap, &hdr);
        encode_nops(&hdr);
 }
 
@@ -3497,8 +3482,11 @@ static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path)
        if (n == 0)
                goto root_path;
        dprintk("pathname4: ");
-       path->ncomponents = 0;
-       while (path->ncomponents < n) {
+       if (n > NFS4_PATHNAME_MAXCOMPONENTS) {
+               dprintk("cannot parse %d components in path\n", n);
+               goto out_eio;
+       }
+       for (path->ncomponents = 0; path->ncomponents < n; path->ncomponents++) {
                struct nfs4_string *component = &path->components[path->ncomponents];
                status = decode_opaque_inline(xdr, &component->len, &component->data);
                if (unlikely(status != 0))
@@ -3507,12 +3495,6 @@ static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path)
                        pr_cont("%s%.*s ",
                                (path->ncomponents != n ? "/ " : ""),
                                component->len, component->data);
-               if (path->ncomponents < NFS4_PATHNAME_MAXCOMPONENTS)
-                       path->ncomponents++;
-               else {
-                       dprintk("cannot parse %d components in path\n", n);
-                       goto out_eio;
-               }
        }
 out:
        return status;
@@ -3557,27 +3539,23 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st
        n = be32_to_cpup(p);
        if (n <= 0)
                goto out_eio;
-       res->nlocations = 0;
-       while (res->nlocations < n) {
+       for (res->nlocations = 0; res->nlocations < n; res->nlocations++) {
                u32 m;
-               struct nfs4_fs_location *loc = &res->locations[res->nlocations];
+               struct nfs4_fs_location *loc;
 
+               if (res->nlocations == NFS4_FS_LOCATIONS_MAXENTRIES)
+                       break;
+               loc = &res->locations[res->nlocations];
                p = xdr_inline_decode(xdr, 4);
                if (unlikely(!p))
                        goto out_overflow;
                m = be32_to_cpup(p);
 
-               loc->nservers = 0;
                dprintk("%s: servers:\n", __func__);
-               while (loc->nservers < m) {
-                       struct nfs4_string *server = &loc->servers[loc->nservers];
-                       status = decode_opaque_inline(xdr, &server->len, &server->data);
-                       if (unlikely(status != 0))
-                               goto out_eio;
-                       dprintk("%s ", server->data);
-                       if (loc->nservers < NFS4_FS_LOCATION_MAXSERVERS)
-                               loc->nservers++;
-                       else {
+               for (loc->nservers = 0; loc->nservers < m; loc->nservers++) {
+                       struct nfs4_string *server;
+
+                       if (loc->nservers == NFS4_FS_LOCATION_MAXSERVERS) {
                                unsigned int i;
                                dprintk("%s: using first %u of %u servers "
                                        "returned for location %u\n",
@@ -3591,13 +3569,17 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st
                                        if (unlikely(status != 0))
                                                goto out_eio;
                                }
+                               break;
                        }
+                       server = &loc->servers[loc->nservers];
+                       status = decode_opaque_inline(xdr, &server->len, &server->data);
+                       if (unlikely(status != 0))
+                               goto out_eio;
+                       dprintk("%s ", server->data);
                }
                status = decode_pathname(xdr, &loc->rootpath);
                if (unlikely(status != 0))
                        goto out_eio;
-               if (res->nlocations < NFS4_FS_LOCATIONS_MAXENTRIES)
-                       res->nlocations++;
        }
        if (res->nlocations != 0)
                status = NFS_ATTR_FATTR_V4_LOCATIONS;
@@ -5209,27 +5191,30 @@ static int decode_delegreturn(struct xdr_stream *xdr)
        return decode_op_hdr(xdr, OP_DELEGRETURN);
 }
 
-static int decode_secinfo_gss(struct xdr_stream *xdr, struct nfs4_secinfo_flavor *flavor)
+static int decode_secinfo_gss(struct xdr_stream *xdr,
+                             struct nfs4_secinfo4 *flavor)
 {
+       u32 oid_len;
        __be32 *p;
 
        p = xdr_inline_decode(xdr, 4);
        if (unlikely(!p))
                goto out_overflow;
-       flavor->gss.sec_oid4.len = be32_to_cpup(p);
-       if (flavor->gss.sec_oid4.len > GSS_OID_MAX_LEN)
+       oid_len = be32_to_cpup(p);
+       if (oid_len > GSS_OID_MAX_LEN)
                goto out_err;
 
-       p = xdr_inline_decode(xdr, flavor->gss.sec_oid4.len);
+       p = xdr_inline_decode(xdr, oid_len);
        if (unlikely(!p))
                goto out_overflow;
-       memcpy(flavor->gss.sec_oid4.data, p, flavor->gss.sec_oid4.len);
+       memcpy(flavor->flavor_info.oid.data, p, oid_len);
+       flavor->flavor_info.oid.len = oid_len;
 
        p = xdr_inline_decode(xdr, 8);
        if (unlikely(!p))
                goto out_overflow;
-       flavor->gss.qop4 = be32_to_cpup(p++);
-       flavor->gss.service = be32_to_cpup(p);
+       flavor->flavor_info.qop = be32_to_cpup(p++);
+       flavor->flavor_info.service = be32_to_cpup(p);
 
        return 0;
 
@@ -5242,10 +5227,10 @@ static int decode_secinfo_gss(struct xdr_stream *xdr, struct nfs4_secinfo_flavor
 
 static int decode_secinfo_common(struct xdr_stream *xdr, struct nfs4_secinfo_res *res)
 {
-       struct nfs4_secinfo_flavor *sec_flavor;
+       struct nfs4_secinfo4 *sec_flavor;
+       unsigned int i, num_flavors;
        int status;
        __be32 *p;
-       int i, num_flavors;
 
        p = xdr_inline_decode(xdr, 4);
        if (unlikely(!p))
@@ -6648,8 +6633,7 @@ static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req,
  * Decode SETCLIENTID_CONFIRM response
  */
 static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req,
-                                           struct xdr_stream *xdr,
-                                           struct nfs_fsinfo *fsinfo)
+                                           struct xdr_stream *xdr)
 {
        struct compound_hdr hdr;
        int status;
@@ -6657,10 +6641,6 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req,
        status = decode_compound_hdr(xdr, &hdr);
        if (!status)
                status = decode_setclientid_confirm(xdr);
-       if (!status)
-               status = decode_putrootfh(xdr);
-       if (!status)
-               status = decode_fsinfo(xdr, fsinfo);
        return status;
 }
 
index e56e846e9d2d61ebef728826a9125bcab61c0597..29cfb7ade121276e2d5ed7372d01949c5e0c2819 100644 (file)
@@ -84,6 +84,55 @@ nfs_page_free(struct nfs_page *p)
        kmem_cache_free(nfs_page_cachep, p);
 }
 
+static void
+nfs_iocounter_inc(struct nfs_io_counter *c)
+{
+       atomic_inc(&c->io_count);
+}
+
+static void
+nfs_iocounter_dec(struct nfs_io_counter *c)
+{
+       if (atomic_dec_and_test(&c->io_count)) {
+               clear_bit(NFS_IO_INPROGRESS, &c->flags);
+               smp_mb__after_clear_bit();
+               wake_up_bit(&c->flags, NFS_IO_INPROGRESS);
+       }
+}
+
+static int
+__nfs_iocounter_wait(struct nfs_io_counter *c)
+{
+       wait_queue_head_t *wq = bit_waitqueue(&c->flags, NFS_IO_INPROGRESS);
+       DEFINE_WAIT_BIT(q, &c->flags, NFS_IO_INPROGRESS);
+       int ret = 0;
+
+       do {
+               prepare_to_wait(wq, &q.wait, TASK_KILLABLE);
+               set_bit(NFS_IO_INPROGRESS, &c->flags);
+               if (atomic_read(&c->io_count) == 0)
+                       break;
+               ret = nfs_wait_bit_killable(&c->flags);
+       } while (atomic_read(&c->io_count) != 0);
+       finish_wait(wq, &q.wait);
+       return ret;
+}
+
+/**
+ * nfs_iocounter_wait - wait for i/o to complete
+ * @c: nfs_io_counter to use
+ *
+ * returns -ERESTARTSYS if interrupted by a fatal signal.
+ * Otherwise returns 0 once the io_count hits 0.
+ */
+int
+nfs_iocounter_wait(struct nfs_io_counter *c)
+{
+       if (atomic_read(&c->io_count) == 0)
+               return 0;
+       return __nfs_iocounter_wait(c);
+}
+
 /**
  * nfs_create_request - Create an NFS read/write request.
  * @ctx: open context to use
@@ -104,6 +153,8 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
        struct nfs_page         *req;
        struct nfs_lock_context *l_ctx;
 
+       if (test_bit(NFS_CONTEXT_BAD, &ctx->flags))
+               return ERR_PTR(-EBADF);
        /* try to allocate the request struct */
        req = nfs_page_alloc();
        if (req == NULL)
@@ -116,6 +167,7 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
                return ERR_CAST(l_ctx);
        }
        req->wb_lock_context = l_ctx;
+       nfs_iocounter_inc(&l_ctx->io_count);
 
        /* Initialize the request struct. Initially, we assume a
         * long write-back delay. This will be adjusted in
@@ -175,6 +227,7 @@ static void nfs_clear_request(struct nfs_page *req)
                req->wb_page = NULL;
        }
        if (l_ctx != NULL) {
+               nfs_iocounter_dec(&l_ctx->io_count);
                nfs_put_lock_context(l_ctx);
                req->wb_lock_context = NULL;
        }
index 4bdffe0ba025228803b65d4fe54ab5cb0adda0ed..c5bd758e563768d76b2a43c830dcba8b53e175cf 100644 (file)
@@ -718,6 +718,8 @@ pnfs_choose_layoutget_stateid(nfs4_stateid *dst, struct pnfs_layout_hdr *lo,
        spin_lock(&lo->plh_inode->i_lock);
        if (pnfs_layoutgets_blocked(lo, 1)) {
                status = -EAGAIN;
+       } else if (!nfs4_valid_open_stateid(open_state)) {
+               status = -EBADF;
        } else if (list_empty(&lo->plh_segs)) {
                int seq;
 
index a5e5d9899d56fa622c1156609b406ee953527d6c..70a26c651f0952e596cebd8191f53c2ac937d939 100644 (file)
@@ -514,6 +514,8 @@ void nfs_read_prepare(struct rpc_task *task, void *calldata)
 {
        struct nfs_read_data *data = calldata;
        NFS_PROTO(data->header->inode)->read_rpc_prepare(task, data);
+       if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags)))
+               rpc_exit(task, -EIO);
 }
 
 static const struct rpc_call_ops nfs_read_common_ops = {
index 2f8a29db0f1bed59a8300d64038ebe4404d611a6..eb494f6a4c6bcf3671cbd87df52c68776a94b2ab 100644 (file)
@@ -920,7 +920,7 @@ static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void)
                data->mount_server.port = NFS_UNSPEC_PORT;
                data->nfs_server.port   = NFS_UNSPEC_PORT;
                data->nfs_server.protocol = XPRT_TRANSPORT_TCP;
-               data->auth_flavors[0]   = RPC_AUTH_UNIX;
+               data->auth_flavors[0]   = RPC_AUTH_MAXFLAVOR;
                data->auth_flavor_len   = 1;
                data->minorversion      = 0;
                data->need_mount        = true;
@@ -1608,49 +1608,57 @@ static int nfs_parse_mount_options(char *raw,
 }
 
 /*
- * Match the requested auth flavors with the list returned by
- * the server.  Returns zero and sets the mount's authentication
- * flavor on success; returns -EACCES if server does not support
- * the requested flavor.
+ * Select a security flavor for this mount.  The selected flavor
+ * is planted in args->auth_flavors[0].
  */
-static int nfs_walk_authlist(struct nfs_parsed_mount_data *args,
-                            struct nfs_mount_request *request)
+static void nfs_select_flavor(struct nfs_parsed_mount_data *args,
+                             struct nfs_mount_request *request)
 {
-       unsigned int i, j, server_authlist_len = *(request->auth_flav_len);
+       unsigned int i, count = *(request->auth_flav_len);
+       rpc_authflavor_t flavor;
+
+       if (args->auth_flavors[0] != RPC_AUTH_MAXFLAVOR)
+               goto out;
+
+       /*
+        * The NFSv2 MNT operation does not return a flavor list.
+        */
+       if (args->mount_server.version != NFS_MNT3_VERSION)
+               goto out_default;
 
        /*
         * Certain releases of Linux's mountd return an empty
-        * flavor list.  To prevent behavioral regression with
-        * these servers (ie. rejecting mounts that used to
-        * succeed), revert to pre-2.6.32 behavior (no checking)
-        * if the returned flavor list is empty.
+        * flavor list in some cases.
         */
-       if (server_authlist_len == 0)
-               return 0;
+       if (count == 0)
+               goto out_default;
 
        /*
-        * We avoid sophisticated negotiating here, as there are
-        * plenty of cases where we can get it wrong, providing
-        * either too little or too much security.
-        *
         * RFC 2623, section 2.7 suggests we SHOULD prefer the
         * flavor listed first.  However, some servers list
-        * AUTH_NULL first.  Our caller plants AUTH_SYS, the
-        * preferred default, in args->auth_flavors[0] if user
-        * didn't specify sec= mount option.
+        * AUTH_NULL first.  Avoid ever choosing AUTH_NULL.
         */
-       for (i = 0; i < args->auth_flavor_len; i++)
-               for (j = 0; j < server_authlist_len; j++)
-                       if (args->auth_flavors[i] == request->auth_flavs[j]) {
-                               dfprintk(MOUNT, "NFS: using auth flavor %d\n",
-                                       request->auth_flavs[j]);
-                               args->auth_flavors[0] = request->auth_flavs[j];
-                               return 0;
-                       }
+       for (i = 0; i < count; i++) {
+               struct rpcsec_gss_info info;
+
+               flavor = request->auth_flavs[i];
+               switch (flavor) {
+               case RPC_AUTH_UNIX:
+                       goto out_set;
+               case RPC_AUTH_NULL:
+                       continue;
+               default:
+                       if (rpcauth_get_gssinfo(flavor, &info) == 0)
+                               goto out_set;
+               }
+       }
 
-       dfprintk(MOUNT, "NFS: server does not support requested auth flavor\n");
-       nfs_umount(request);
-       return -EACCES;
+out_default:
+       flavor = RPC_AUTH_UNIX;
+out_set:
+       args->auth_flavors[0] = flavor;
+out:
+       dfprintk(MOUNT, "NFS: using auth flavor %d\n", args->auth_flavors[0]);
 }
 
 /*
@@ -1713,12 +1721,8 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args,
                return status;
        }
 
-       /*
-        * MNTv1 (NFSv2) does not support auth flavor negotiation.
-        */
-       if (args->mount_server.version != NFS_MNT3_VERSION)
-               return 0;
-       return nfs_walk_authlist(args, &request);
+       nfs_select_flavor(args, &request);
+       return 0;
 }
 
 struct dentry *nfs_try_mount(int flags, const char *dev_name,
index c483cc50b82ee5aa77f80f0d057550526469f466..a2c7c28049d5096e484bf18453b2be64f17d884f 100644 (file)
@@ -1251,6 +1251,8 @@ void nfs_write_prepare(struct rpc_task *task, void *calldata)
 {
        struct nfs_write_data *data = calldata;
        NFS_PROTO(data->header->inode)->write_rpc_prepare(task, data);
+       if (unlikely(test_bit(NFS_CONTEXT_BAD, &data->args.context->flags)))
+               rpc_exit(task, -EIO);
 }
 
 void nfs_commit_prepare(struct rpc_task *task, void *calldata)
index a2720071f282f7607f13d63af8d6e9d6cf18bce6..2502951714b1d5826727b37f7566462d4bda6840 100644 (file)
@@ -3138,10 +3138,9 @@ nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
 
 static __be32
 nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
-                        __be32 nfserr,struct svc_export *exp)
+                        __be32 nfserr, struct svc_export *exp)
 {
-       int i = 0;
-       u32 nflavs;
+       u32 i, nflavs;
        struct exp_flavor_info *flavs;
        struct exp_flavor_info def_flavs[2];
        __be32 *p;
@@ -3172,30 +3171,29 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
        WRITE32(nflavs);
        ADJUST_ARGS();
        for (i = 0; i < nflavs; i++) {
-               u32 flav = flavs[i].pseudoflavor;
-               struct gss_api_mech *gm = gss_mech_get_by_pseudoflavor(flav);
+               struct rpcsec_gss_info info;
 
-               if (gm) {
+               if (rpcauth_get_gssinfo(flavs[i].pseudoflavor, &info) == 0) {
                        RESERVE_SPACE(4);
                        WRITE32(RPC_AUTH_GSS);
                        ADJUST_ARGS();
-                       RESERVE_SPACE(4 + gm->gm_oid.len);
-                       WRITE32(gm->gm_oid.len);
-                       WRITEMEM(gm->gm_oid.data, gm->gm_oid.len);
+                       RESERVE_SPACE(4 + info.oid.len);
+                       WRITE32(info.oid.len);
+                       WRITEMEM(info.oid.data, info.oid.len);
                        ADJUST_ARGS();
                        RESERVE_SPACE(4);
-                       WRITE32(0); /* qop */
+                       WRITE32(info.qop);
                        ADJUST_ARGS();
                        RESERVE_SPACE(4);
-                       WRITE32(gss_pseudoflavor_to_service(gm, flav));
+                       WRITE32(info.service);
                        ADJUST_ARGS();
-                       gss_mech_put(gm);
                } else {
                        RESERVE_SPACE(4);
-                       WRITE32(flav);
+                       WRITE32(flavs[i].pseudoflavor);
                        ADJUST_ARGS();
                }
        }
+
 out:
        if (exp)
                exp_put(exp);
index 1cc25682b20bdba4e0cac8cc6925910445104481..fc01d5cb4cf1e013afc41f42f9f9f9b6da6908b7 100644 (file)
@@ -59,11 +59,18 @@ struct nfs_lockowner {
        pid_t l_pid;
 };
 
+#define NFS_IO_INPROGRESS 0
+struct nfs_io_counter {
+       unsigned long flags;
+       atomic_t io_count;
+};
+
 struct nfs_lock_context {
        atomic_t count;
        struct list_head list;
        struct nfs_open_context *open_context;
        struct nfs_lockowner lockowner;
+       struct nfs_io_counter io_count;
 };
 
 struct nfs4_state;
@@ -77,6 +84,7 @@ struct nfs_open_context {
        unsigned long flags;
 #define NFS_CONTEXT_ERROR_WRITE                (0)
 #define NFS_CONTEXT_RESEND_WRITES      (1)
+#define NFS_CONTEXT_BAD                        (2)
        int error;
 
        struct list_head list;
index 6c6ed153a9b4833f055ac7899b066b9c87782ece..3b7fa2abecca690e0d007a4aa97fded3e1a1bad3 100644 (file)
@@ -40,6 +40,7 @@ struct nfs_client {
 #define NFS_CS_NORESVPORT      0               /* - use ephemeral src port */
 #define NFS_CS_DISCRTRY                1               /* - disconnect on RPC retry */
 #define NFS_CS_MIGRATION       2               /* - transparent state migr */
+#define NFS_CS_INFINITE_SLOTS  3               /* - don't limit TCP slots */
        struct sockaddr_storage cl_addr;        /* server identifier */
        size_t                  cl_addrlen;
        char *                  cl_hostname;    /* hostname of server */
@@ -197,5 +198,7 @@ struct nfs_server {
 #define NFS_CAP_MTIME          (1U << 13)
 #define NFS_CAP_POSIX_LOCK     (1U << 14)
 #define NFS_CAP_UIDGID_NOMAP   (1U << 15)
+#define NFS_CAP_STATEID_NFSV41 (1U << 16)
+#define NFS_CAP_ATOMIC_OPEN_V1 (1U << 17)
 
 #endif
index 4b993d358dad5d3a02939f61f96d799c523fa8a5..766c5bc9d441ccf440341dab53e06616e3262363 100644 (file)
@@ -14,9 +14,6 @@
 #define NFS_DEF_FILE_IO_SIZE   (4096U)
 #define NFS_MIN_FILE_IO_SIZE   (1024U)
 
-/* Forward declaration for NFS v3 */
-struct nfs4_secinfo_flavors;
-
 struct nfs4_string {
        unsigned int len;
        char *data;
@@ -349,6 +346,7 @@ struct nfs_openargs {
        const u32 *             bitmask;
        const u32 *             open_bitmap;
        __u32                   claim;
+       enum createmode4        createmode;
 };
 
 struct nfs_openres {
@@ -486,6 +484,7 @@ struct nfs_readargs {
        struct nfs_fh *         fh;
        struct nfs_open_context *context;
        struct nfs_lock_context *lock_context;
+       nfs4_stateid            stateid;
        __u64                   offset;
        __u32                   count;
        unsigned int            pgbase;
@@ -507,6 +506,7 @@ struct nfs_writeargs {
        struct nfs_fh *         fh;
        struct nfs_open_context *context;
        struct nfs_lock_context *lock_context;
+       nfs4_stateid            stateid;
        __u64                   offset;
        __u32                   count;
        enum nfs3_stable_how    stable;
@@ -1050,25 +1050,14 @@ struct nfs4_fs_locations_res {
        struct nfs4_fs_locations       *fs_locations;
 };
 
-struct nfs4_secinfo_oid {
-       unsigned int len;
-       char data[GSS_OID_MAX_LEN];
-};
-
-struct nfs4_secinfo_gss {
-       struct nfs4_secinfo_oid sec_oid4;
-       unsigned int qop4;
-       unsigned int service;
-};
-
-struct nfs4_secinfo_flavor {
-       unsigned int            flavor;
-       struct nfs4_secinfo_gss gss;
+struct nfs4_secinfo4 {
+       u32                     flavor;
+       struct rpcsec_gss_info  flavor_info;
 };
 
 struct nfs4_secinfo_flavors {
-       unsigned int num_flavors;
-       struct nfs4_secinfo_flavor flavors[0];
+       unsigned int            num_flavors;
+       struct nfs4_secinfo4    flavors[0];
 };
 
 struct nfs4_secinfo_arg {
index 58fda1c3c783948f4627361eba9bc93a51bde796..0dd00f4f681046018f25292cc288632dc2593caa 100644 (file)
@@ -22,6 +22,8 @@
 /* size of the nodename buffer */
 #define UNX_MAXNODENAME        32
 
+struct rpcsec_gss_info;
+
 /* Work around the lack of a VFS credential */
 struct auth_cred {
        kuid_t  uid;
@@ -103,6 +105,9 @@ struct rpc_authops {
        int                     (*pipes_create)(struct rpc_auth *);
        void                    (*pipes_destroy)(struct rpc_auth *);
        int                     (*list_pseudoflavors)(rpc_authflavor_t *, int);
+       rpc_authflavor_t        (*info2flavor)(struct rpcsec_gss_info *);
+       int                     (*flavor2info)(rpc_authflavor_t,
+                                               struct rpcsec_gss_info *);
 };
 
 struct rpc_credops {
@@ -137,6 +142,10 @@ int                        rpcauth_register(const struct rpc_authops *);
 int                    rpcauth_unregister(const struct rpc_authops *);
 struct rpc_auth *      rpcauth_create(rpc_authflavor_t, struct rpc_clnt *);
 void                   rpcauth_release(struct rpc_auth *);
+rpc_authflavor_t       rpcauth_get_pseudoflavor(rpc_authflavor_t,
+                               struct rpcsec_gss_info *);
+int                    rpcauth_get_gssinfo(rpc_authflavor_t,
+                               struct rpcsec_gss_info *);
 int                    rpcauth_list_flavors(rpc_authflavor_t *, int);
 struct rpc_cred *      rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int);
 void                   rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *);
index 2cf4ffaa3cd46c25fb77f089a503732fe37d842f..e7d492ce7c18f6ccaa9da544f07ef6af68ba7fe2 100644 (file)
@@ -124,6 +124,7 @@ struct rpc_create_args {
 #define RPC_CLNT_CREATE_NOPING         (1UL << 4)
 #define RPC_CLNT_CREATE_DISCRTRY       (1UL << 5)
 #define RPC_CLNT_CREATE_QUIET          (1UL << 6)
+#define RPC_CLNT_CREATE_INFINITE_SLOTS (1UL << 7)
 
 struct rpc_clnt *rpc_create(struct rpc_create_args *args);
 struct rpc_clnt        *rpc_bind_new_program(struct rpc_clnt *,
index a19e2547ae6aba4481ba4cd01f5bdbbf7545de22..f32b7a47e13f611c65486ca2c70650f8bc3a0646 100644 (file)
@@ -25,10 +25,21 @@ struct gss_ctx {
 
 #define GSS_C_NO_BUFFER                ((struct xdr_netobj) 0)
 #define GSS_C_NO_CONTEXT       ((struct gss_ctx *) 0)
-#define GSS_C_NULL_OID         ((struct xdr_netobj) 0)
+#define GSS_C_QOP_DEFAULT      (0)
 
 /*XXX  arbitrary length - is this set somewhere? */
 #define GSS_OID_MAX_LEN 32
+struct rpcsec_gss_oid {
+       unsigned int    len;
+       u8              data[GSS_OID_MAX_LEN];
+};
+
+/* From RFC 3530 */
+struct rpcsec_gss_info {
+       struct rpcsec_gss_oid   oid;
+       u32                     qop;
+       u32                     service;
+};
 
 /* gss-api prototypes; note that these are somewhat simplified versions of
  * the prototypes specified in RFC 2744. */
@@ -58,12 +69,14 @@ u32 gss_unwrap(
 u32 gss_delete_sec_context(
                struct gss_ctx          **ctx_id);
 
-u32 gss_svc_to_pseudoflavor(struct gss_api_mech *, u32 service);
+rpc_authflavor_t gss_svc_to_pseudoflavor(struct gss_api_mech *, u32 qop,
+                                       u32 service);
 u32 gss_pseudoflavor_to_service(struct gss_api_mech *, u32 pseudoflavor);
 char *gss_service_to_auth_domain_name(struct gss_api_mech *, u32 service);
 
 struct pf_desc {
        u32     pseudoflavor;
+       u32     qop;
        u32     service;
        char    *name;
        char    *auth_domain_name;
@@ -76,7 +89,7 @@ struct pf_desc {
 struct gss_api_mech {
        struct list_head        gm_list;
        struct module           *gm_owner;
-       struct xdr_netobj       gm_oid;
+       struct rpcsec_gss_oid   gm_oid;
        char                    *gm_name;
        const struct gss_api_ops *gm_ops;
        /* pseudoflavors supported by this mechanism: */
@@ -117,9 +130,11 @@ struct gss_api_ops {
 int gss_mech_register(struct gss_api_mech *);
 void gss_mech_unregister(struct gss_api_mech *);
 
-/* returns a mechanism descriptor given an OID, and increments the mechanism's
- * reference count. */
-struct gss_api_mech * gss_mech_get_by_OID(struct xdr_netobj *);
+/* Given a GSS security tuple, look up a pseudoflavor */
+rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *);
+
+/* Given a pseudoflavor, look up a GSS security tuple */
+int gss_mech_flavor2info(rpc_authflavor_t, struct rpcsec_gss_info *);
 
 /* Returns a reference to a mechanism, given a name like "krb5" etc. */
 struct gss_api_mech *gss_mech_get_by_name(const char *);
@@ -130,9 +145,6 @@ struct gss_api_mech *gss_mech_get_by_pseudoflavor(u32);
 /* Fill in an array with a list of supported pseudoflavors */
 int gss_mech_list_pseudoflavors(rpc_authflavor_t *, int);
 
-/* Just increments the mechanism's reference count and returns its input: */
-struct gss_api_mech * gss_mech_get(struct gss_api_mech *);
-
 /* For every successful gss_mech_get or gss_mech_get_by_* call there must be a
  * corresponding call to gss_mech_put. */
 void gss_mech_put(struct gss_api_mech *);
index 30834be03011fc09e8442b62c8222125d4de8a90..ff5392421cb241f3e5f67f85391882d5372193a1 100644 (file)
@@ -255,6 +255,8 @@ static inline int bc_prealloc(struct rpc_rqst *req)
 }
 #endif /* CONFIG_SUNRPC_BACKCHANNEL */
 
+#define XPRT_CREATE_INFINITE_SLOTS     (1U)
+
 struct xprt_create {
        int                     ident;          /* XPRT_TRANSPORT identifier */
        struct net *            net;
@@ -263,6 +265,7 @@ struct xprt_create {
        size_t                  addrlen;
        const char              *servername;
        struct svc_xprt         *bc_xprt;       /* NFSv4.1 backchannel */
+       unsigned int            flags;
 };
 
 struct xprt_class {
@@ -279,6 +282,7 @@ struct xprt_class {
 struct rpc_xprt                *xprt_create_transport(struct xprt_create *args);
 void                   xprt_connect(struct rpc_task *task);
 void                   xprt_reserve(struct rpc_task *task);
+void                   xprt_retry_reserve(struct rpc_task *task);
 int                    xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task);
 int                    xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task);
 void                   xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task);
@@ -334,6 +338,7 @@ int                 xs_swapper(struct rpc_xprt *xprt, int enable);
 #define XPRT_CLOSING           (6)
 #define XPRT_CONNECTION_ABORT  (7)
 #define XPRT_CONNECTION_CLOSE  (8)
+#define XPRT_CONGESTED         (9)
 
 static inline void xprt_set_connected(struct rpc_xprt *xprt)
 {
index 516fe2caac2c98749f73f01512ca09b745166e1b..241b54f30204a63ec4a63372910a8c1fe714dbad 100644 (file)
@@ -3,6 +3,7 @@ config SUNRPC
 
 config SUNRPC_GSS
        tristate
+       select OID_REGISTRY
 
 config SUNRPC_BACKCHANNEL
        bool
@@ -24,7 +25,6 @@ config SUNRPC_XPRT_RDMA
 config SUNRPC_SWAP
        bool
        depends on SUNRPC
-       select NETVM
 
 config RPCSEC_GSS_KRB5
        tristate "Secure RPC: Kerberos V mechanism"
index f5294047df77b03b0feae953405bfd5b41252b76..ed2fdd210c0bac4f5569acbe5da9daa025012cdd 100644 (file)
@@ -82,7 +82,7 @@ MODULE_PARM_DESC(auth_hashtable_size, "RPC credential cache hashtable size");
 
 static u32
 pseudoflavor_to_flavor(u32 flavor) {
-       if (flavor >= RPC_AUTH_MAXFLAVOR)
+       if (flavor > RPC_AUTH_MAXFLAVOR)
                return RPC_AUTH_GSS;
        return flavor;
 }
@@ -123,6 +123,79 @@ rpcauth_unregister(const struct rpc_authops *ops)
 }
 EXPORT_SYMBOL_GPL(rpcauth_unregister);
 
+/**
+ * rpcauth_get_pseudoflavor - check if security flavor is supported
+ * @flavor: a security flavor
+ * @info: a GSS mech OID, quality of protection, and service value
+ *
+ * Verifies that an appropriate kernel module is available or already loaded.
+ * Returns an equivalent pseudoflavor, or RPC_AUTH_MAXFLAVOR if "flavor" is
+ * not supported locally.
+ */
+rpc_authflavor_t
+rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info)
+{
+       const struct rpc_authops *ops;
+       rpc_authflavor_t pseudoflavor;
+
+       ops = auth_flavors[flavor];
+       if (ops == NULL)
+               request_module("rpc-auth-%u", flavor);
+       spin_lock(&rpc_authflavor_lock);
+       ops = auth_flavors[flavor];
+       if (ops == NULL || !try_module_get(ops->owner)) {
+               spin_unlock(&rpc_authflavor_lock);
+               return RPC_AUTH_MAXFLAVOR;
+       }
+       spin_unlock(&rpc_authflavor_lock);
+
+       pseudoflavor = flavor;
+       if (ops->info2flavor != NULL)
+               pseudoflavor = ops->info2flavor(info);
+
+       module_put(ops->owner);
+       return pseudoflavor;
+}
+EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor);
+
+/**
+ * rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor
+ * @pseudoflavor: GSS pseudoflavor to match
+ * @info: rpcsec_gss_info structure to fill in
+ *
+ * Returns zero and fills in "info" if pseudoflavor matches a
+ * supported mechanism.
+ */
+int
+rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
+{
+       rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor);
+       const struct rpc_authops *ops;
+       int result;
+
+       if (flavor >= RPC_AUTH_MAXFLAVOR)
+               return -EINVAL;
+
+       ops = auth_flavors[flavor];
+       if (ops == NULL)
+               request_module("rpc-auth-%u", flavor);
+       spin_lock(&rpc_authflavor_lock);
+       ops = auth_flavors[flavor];
+       if (ops == NULL || !try_module_get(ops->owner)) {
+               spin_unlock(&rpc_authflavor_lock);
+               return -ENOENT;
+       }
+       spin_unlock(&rpc_authflavor_lock);
+
+       result = -ENOENT;
+       if (ops->flavor2info != NULL)
+               result = ops->flavor2info(pseudoflavor, info);
+
+       module_put(ops->owner);
+       return result;
+}
+EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo);
+
 /**
  * rpcauth_list_flavors - discover registered flavors and pseudoflavors
  * @array: array to fill in
index 5257d2982ba53665024e028dc4a5c0cb15dabd90..51415b07174eefd305623173ea40ddaad9b52ba2 100644 (file)
@@ -1641,6 +1641,8 @@ static const struct rpc_authops authgss_ops = {
        .pipes_create   = gss_pipes_dentries_create,
        .pipes_destroy  = gss_pipes_dentries_destroy,
        .list_pseudoflavors = gss_mech_list_pseudoflavors,
+       .info2flavor    = gss_mech_info2flavor,
+       .flavor2info    = gss_mech_flavor2info,
 };
 
 static const struct rpc_credops gss_credops = {
@@ -1733,6 +1735,7 @@ static void __exit exit_rpcsec_gss(void)
        rcu_barrier(); /* Wait for completion of call_rcu()'s */
 }
 
+MODULE_ALIAS("rpc-auth-6");
 MODULE_LICENSE("GPL");
 module_param_named(expired_cred_retry_delay,
                   gss_expired_cred_retry_delay,
index d3611f11a8dfc0767ab5736a87740a68f7645f8d..33255ff889c09aea06e5d8333256c59a2e23d9b6 100644 (file)
@@ -729,16 +729,19 @@ static const struct gss_api_ops gss_kerberos_ops = {
 static struct pf_desc gss_kerberos_pfs[] = {
        [0] = {
                .pseudoflavor = RPC_AUTH_GSS_KRB5,
+               .qop = GSS_C_QOP_DEFAULT,
                .service = RPC_GSS_SVC_NONE,
                .name = "krb5",
        },
        [1] = {
                .pseudoflavor = RPC_AUTH_GSS_KRB5I,
+               .qop = GSS_C_QOP_DEFAULT,
                .service = RPC_GSS_SVC_INTEGRITY,
                .name = "krb5i",
        },
        [2] = {
                .pseudoflavor = RPC_AUTH_GSS_KRB5P,
+               .qop = GSS_C_QOP_DEFAULT,
                .service = RPC_GSS_SVC_PRIVACY,
                .name = "krb5p",
        },
@@ -750,11 +753,12 @@ MODULE_ALIAS("rpc-auth-gss-krb5p");
 MODULE_ALIAS("rpc-auth-gss-390003");
 MODULE_ALIAS("rpc-auth-gss-390004");
 MODULE_ALIAS("rpc-auth-gss-390005");
+MODULE_ALIAS("rpc-auth-gss-1.2.840.113554.1.2.2");
 
 static struct gss_api_mech gss_kerberos_mech = {
        .gm_name        = "krb5",
        .gm_owner       = THIS_MODULE,
-       .gm_oid         = {9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"},
+       .gm_oid         = { 9, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" },
        .gm_ops         = &gss_kerberos_ops,
        .gm_pf_num      = ARRAY_SIZE(gss_kerberos_pfs),
        .gm_pfs         = gss_kerberos_pfs,
index f0f4eee63a3579e5783e9a290a4bfb808f0d593a..79881d6e68a1c53e502312f8a815f556a06c6eab 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/oid_registry.h>
 #include <linux/sunrpc/msg_prot.h>
 #include <linux/sunrpc/gss_asn1.h>
 #include <linux/sunrpc/auth_gss.h>
@@ -102,8 +103,13 @@ gss_mech_svc_setup(struct gss_api_mech *gm)
        return status;
 }
 
-int
-gss_mech_register(struct gss_api_mech *gm)
+/**
+ * gss_mech_register - register a GSS mechanism
+ * @gm: GSS mechanism handle
+ *
+ * Returns zero if successful, or a negative errno.
+ */
+int gss_mech_register(struct gss_api_mech *gm)
 {
        int status;
 
@@ -116,11 +122,14 @@ gss_mech_register(struct gss_api_mech *gm)
        dprintk("RPC:       registered gss mechanism %s\n", gm->gm_name);
        return 0;
 }
-
 EXPORT_SYMBOL_GPL(gss_mech_register);
 
-void
-gss_mech_unregister(struct gss_api_mech *gm)
+/**
+ * gss_mech_unregister - release a GSS mechanism
+ * @gm: GSS mechanism handle
+ *
+ */
+void gss_mech_unregister(struct gss_api_mech *gm)
 {
        spin_lock(&registered_mechs_lock);
        list_del(&gm->gm_list);
@@ -128,18 +137,14 @@ gss_mech_unregister(struct gss_api_mech *gm)
        dprintk("RPC:       unregistered gss mechanism %s\n", gm->gm_name);
        gss_mech_free(gm);
 }
-
 EXPORT_SYMBOL_GPL(gss_mech_unregister);
 
-struct gss_api_mech *
-gss_mech_get(struct gss_api_mech *gm)
+static struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm)
 {
        __module_get(gm->gm_owner);
        return gm;
 }
 
-EXPORT_SYMBOL_GPL(gss_mech_get);
-
 static struct gss_api_mech *
 _gss_mech_get_by_name(const char *name)
 {
@@ -169,12 +174,16 @@ struct gss_api_mech * gss_mech_get_by_name(const char *name)
        }
        return gm;
 }
-EXPORT_SYMBOL_GPL(gss_mech_get_by_name);
 
-struct gss_api_mech *
-gss_mech_get_by_OID(struct xdr_netobj *obj)
+static struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj)
 {
        struct gss_api_mech     *pos, *gm = NULL;
+       char buf[32];
+
+       if (sprint_oid(obj->data, obj->len, buf, sizeof(buf)) < 0)
+               return NULL;
+       dprintk("RPC:       %s(%s)\n", __func__, buf);
+       request_module("rpc-auth-gss-%s", buf);
 
        spin_lock(&registered_mechs_lock);
        list_for_each_entry(pos, &registered_mechs, gm_list) {
@@ -188,11 +197,8 @@ gss_mech_get_by_OID(struct xdr_netobj *obj)
        }
        spin_unlock(&registered_mechs_lock);
        return gm;
-
 }
 
-EXPORT_SYMBOL_GPL(gss_mech_get_by_OID);
-
 static inline int
 mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor)
 {
@@ -237,8 +243,6 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
        return gm;
 }
 
-EXPORT_SYMBOL_GPL(gss_mech_get_by_pseudoflavor);
-
 /**
  * gss_mech_list_pseudoflavors - Discover registered GSS pseudoflavors
  * @array: array to fill in
@@ -268,19 +272,82 @@ int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr, int size)
        return i;
 }
 
-u32
-gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service)
+/**
+ * gss_svc_to_pseudoflavor - map a GSS service number to a pseudoflavor
+ * @gm: GSS mechanism handle
+ * @qop: GSS quality-of-protection value
+ * @service: GSS service value
+ *
+ * Returns a matching security flavor, or RPC_AUTH_MAXFLAVOR if none is found.
+ */
+rpc_authflavor_t gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 qop,
+                                        u32 service)
 {
        int i;
 
        for (i = 0; i < gm->gm_pf_num; i++) {
-               if (gm->gm_pfs[i].service == service) {
+               if (gm->gm_pfs[i].qop == qop &&
+                   gm->gm_pfs[i].service == service) {
                        return gm->gm_pfs[i].pseudoflavor;
                }
        }
-       return RPC_AUTH_MAXFLAVOR; /* illegal value */
+       return RPC_AUTH_MAXFLAVOR;
+}
+
+/**
+ * gss_mech_info2flavor - look up a pseudoflavor given a GSS tuple
+ * @info: a GSS mech OID, quality of protection, and service value
+ *
+ * Returns a matching pseudoflavor, or RPC_AUTH_MAXFLAVOR if the tuple is
+ * not supported.
+ */
+rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *info)
+{
+       rpc_authflavor_t pseudoflavor;
+       struct gss_api_mech *gm;
+
+       gm = gss_mech_get_by_OID(&info->oid);
+       if (gm == NULL)
+               return RPC_AUTH_MAXFLAVOR;
+
+       pseudoflavor = gss_svc_to_pseudoflavor(gm, info->qop, info->service);
+
+       gss_mech_put(gm);
+       return pseudoflavor;
+}
+
+/**
+ * gss_mech_flavor2info - look up a GSS tuple for a given pseudoflavor
+ * @pseudoflavor: GSS pseudoflavor to match
+ * @info: rpcsec_gss_info structure to fill in
+ *
+ * Returns zero and fills in "info" if pseudoflavor matches a
+ * supported mechanism.  Otherwise a negative errno is returned.
+ */
+int gss_mech_flavor2info(rpc_authflavor_t pseudoflavor,
+                        struct rpcsec_gss_info *info)
+{
+       struct gss_api_mech *gm;
+       int i;
+
+       gm = gss_mech_get_by_pseudoflavor(pseudoflavor);
+       if (gm == NULL)
+               return -ENOENT;
+
+       for (i = 0; i < gm->gm_pf_num; i++) {
+               if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) {
+                       memcpy(info->oid.data, gm->gm_oid.data, gm->gm_oid.len);
+                       info->oid.len = gm->gm_oid.len;
+                       info->qop = gm->gm_pfs[i].qop;
+                       info->service = gm->gm_pfs[i].service;
+                       gss_mech_put(gm);
+                       return 0;
+               }
+       }
+
+       gss_mech_put(gm);
+       return -ENOENT;
 }
-EXPORT_SYMBOL_GPL(gss_svc_to_pseudoflavor);
 
 u32
 gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
@@ -294,8 +361,6 @@ gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
        return 0;
 }
 
-EXPORT_SYMBOL_GPL(gss_pseudoflavor_to_service);
-
 char *
 gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
 {
@@ -308,8 +373,6 @@ gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
        return NULL;
 }
 
-EXPORT_SYMBOL_GPL(gss_service_to_auth_domain_name);
-
 void
 gss_mech_put(struct gss_api_mech * gm)
 {
@@ -317,8 +380,6 @@ gss_mech_put(struct gss_api_mech * gm)
                module_put(gm->gm_owner);
 }
 
-EXPORT_SYMBOL_GPL(gss_mech_put);
-
 /* The mech could probably be determined from the token instead, but it's just
  * as easy for now to pass it in. */
 int
index 5ead60550895f848ba2348bba3bbbfc50ddea7ea..c3ba570222dcbdeff872dc099ac3e6c38ea17ab5 100644 (file)
@@ -1220,7 +1220,9 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
                svcdata->rsci = rsci;
                cache_get(&rsci->h);
                rqstp->rq_cred.cr_flavor = gss_svc_to_pseudoflavor(
-                                       rsci->mechctx->mech_type, gc->gc_svc);
+                                       rsci->mechctx->mech_type,
+                                       GSS_C_QOP_DEFAULT,
+                                       gc->gc_svc);
                ret = SVC_OK;
                goto out;
        }
index d5f35f15af983e7a6955c576460a092ec70c0bcb..d259fa9669270506c32b941542baccec55b3d061 100644 (file)
@@ -411,6 +411,8 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
        };
        char servername[48];
 
+       if (args->flags & RPC_CLNT_CREATE_INFINITE_SLOTS)
+               xprtargs.flags |= XPRT_CREATE_INFINITE_SLOTS;
        /*
         * If the caller chooses not to specify a hostname, whip
         * up a string representation of the passed-in address.
@@ -1301,6 +1303,8 @@ call_reserve(struct rpc_task *task)
        xprt_reserve(task);
 }
 
+static void call_retry_reserve(struct rpc_task *task);
+
 /*
  * 1b. Grok the result of xprt_reserve()
  */
@@ -1342,7 +1346,7 @@ call_reserveresult(struct rpc_task *task)
        case -ENOMEM:
                rpc_delay(task, HZ >> 2);
        case -EAGAIN:   /* woken up; retry */
-               task->tk_action = call_reserve;
+               task->tk_action = call_retry_reserve;
                return;
        case -EIO:      /* probably a shutdown */
                break;
@@ -1354,6 +1358,19 @@ call_reserveresult(struct rpc_task *task)
        rpc_exit(task, status);
 }
 
+/*
+ * 1c. Retry reserving an RPC call slot
+ */
+static void
+call_retry_reserve(struct rpc_task *task)
+{
+       dprint_status(task);
+
+       task->tk_status  = 0;
+       task->tk_action  = call_reserveresult;
+       xprt_retry_reserve(task);
+}
+
 /*
  * 2.  Bind and/or refresh the credentials
  */
@@ -1639,22 +1656,26 @@ call_connect_status(struct rpc_task *task)
 
        dprint_status(task);
 
-       task->tk_status = 0;
-       if (status >= 0 || status == -EAGAIN) {
-               clnt->cl_stats->netreconn++;
-               task->tk_action = call_transmit;
-               return;
-       }
-
        trace_rpc_connect_status(task, status);
        switch (status) {
                /* if soft mounted, test if we've timed out */
        case -ETIMEDOUT:
                task->tk_action = call_timeout;
-               break;
-       default:
-               rpc_exit(task, -EIO);
+               return;
+       case -ECONNREFUSED:
+       case -ECONNRESET:
+       case -ENETUNREACH:
+               if (RPC_IS_SOFTCONN(task))
+                       break;
+               /* retry with existing socket, after a delay */
+       case 0:
+       case -EAGAIN:
+               task->tk_status = 0;
+               clnt->cl_stats->netreconn++;
+               task->tk_action = call_transmit;
+               return;
        }
+       rpc_exit(task, status);
 }
 
 /*
index b7478d5e7ffde8ae06c3a02bb682aec69f339adc..745fca3cfd360531f9f18c4dadf2aa186b3c9cf2 100644 (file)
@@ -948,6 +948,34 @@ void xprt_transmit(struct rpc_task *task)
        spin_unlock_bh(&xprt->transport_lock);
 }
 
+static void xprt_add_backlog(struct rpc_xprt *xprt, struct rpc_task *task)
+{
+       set_bit(XPRT_CONGESTED, &xprt->state);
+       rpc_sleep_on(&xprt->backlog, task, NULL);
+}
+
+static void xprt_wake_up_backlog(struct rpc_xprt *xprt)
+{
+       if (rpc_wake_up_next(&xprt->backlog) == NULL)
+               clear_bit(XPRT_CONGESTED, &xprt->state);
+}
+
+static bool xprt_throttle_congested(struct rpc_xprt *xprt, struct rpc_task *task)
+{
+       bool ret = false;
+
+       if (!test_bit(XPRT_CONGESTED, &xprt->state))
+               goto out;
+       spin_lock(&xprt->reserve_lock);
+       if (test_bit(XPRT_CONGESTED, &xprt->state)) {
+               rpc_sleep_on(&xprt->backlog, task, NULL);
+               ret = true;
+       }
+       spin_unlock(&xprt->reserve_lock);
+out:
+       return ret;
+}
+
 static struct rpc_rqst *xprt_dynamic_alloc_slot(struct rpc_xprt *xprt, gfp_t gfp_flags)
 {
        struct rpc_rqst *req = ERR_PTR(-EAGAIN);
@@ -992,7 +1020,7 @@ void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task)
                task->tk_status = -ENOMEM;
                break;
        case -EAGAIN:
-               rpc_sleep_on(&xprt->backlog, task, NULL);
+               xprt_add_backlog(xprt, task);
                dprintk("RPC:       waiting for request slot\n");
        default:
                task->tk_status = -EAGAIN;
@@ -1028,7 +1056,7 @@ static void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
                memset(req, 0, sizeof(*req));   /* mark unused */
                list_add(&req->rq_list, &xprt->free);
        }
-       rpc_wake_up_next(&xprt->backlog);
+       xprt_wake_up_backlog(xprt);
        spin_unlock(&xprt->reserve_lock);
 }
 
@@ -1092,13 +1120,40 @@ EXPORT_SYMBOL_GPL(xprt_free);
  * xprt_reserve - allocate an RPC request slot
  * @task: RPC task requesting a slot allocation
  *
- * If no more slots are available, place the task on the transport's
+ * If the transport is marked as being congested, or if no more
+ * slots are available, place the task on the transport's
  * backlog queue.
  */
 void xprt_reserve(struct rpc_task *task)
 {
        struct rpc_xprt *xprt;
 
+       task->tk_status = 0;
+       if (task->tk_rqstp != NULL)
+               return;
+
+       task->tk_timeout = 0;
+       task->tk_status = -EAGAIN;
+       rcu_read_lock();
+       xprt = rcu_dereference(task->tk_client->cl_xprt);
+       if (!xprt_throttle_congested(xprt, task))
+               xprt->ops->alloc_slot(xprt, task);
+       rcu_read_unlock();
+}
+
+/**
+ * xprt_retry_reserve - allocate an RPC request slot
+ * @task: RPC task requesting a slot allocation
+ *
+ * If no more slots are available, place the task on the transport's
+ * backlog queue.
+ * Note that the only difference with xprt_reserve is that we now
+ * ignore the value of the XPRT_CONGESTED flag.
+ */
+void xprt_retry_reserve(struct rpc_task *task)
+{
+       struct rpc_xprt *xprt;
+
        task->tk_status = 0;
        if (task->tk_rqstp != NULL)
                return;
index 3d02130828da671066b40aa7dd769cf6a7bd4a74..9c2825827decfb1a0b6fd2f4a80c0cd66fe36eb7 100644 (file)
@@ -2207,10 +2207,6 @@ static void xs_tcp_setup_socket(struct work_struct *work)
                 */
                xs_tcp_force_close(xprt);
                break;
-       case -ECONNREFUSED:
-       case -ECONNRESET:
-       case -ENETUNREACH:
-               /* retry with existing socket, after a delay */
        case 0:
        case -EINPROGRESS:
        case -EALREADY:
@@ -2221,6 +2217,10 @@ static void xs_tcp_setup_socket(struct work_struct *work)
                /* Happens, for instance, if the user specified a link
                 * local IPv6 address without a scope-id.
                 */
+       case -ECONNREFUSED:
+       case -ECONNRESET:
+       case -ENETUNREACH:
+               /* retry with existing socket, after a delay */
                goto out;
        }
 out_eagain:
@@ -2767,9 +2767,13 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
        struct rpc_xprt *xprt;
        struct sock_xprt *transport;
        struct rpc_xprt *ret;
+       unsigned int max_slot_table_size = xprt_max_tcp_slot_table_entries;
+
+       if (args->flags & XPRT_CREATE_INFINITE_SLOTS)
+               max_slot_table_size = RPC_MAX_SLOT_TABLE_LIMIT;
 
        xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries,
-                       xprt_max_tcp_slot_table_entries);
+                       max_slot_table_size);
        if (IS_ERR(xprt))
                return xprt;
        transport = container_of(xprt, struct sock_xprt, xprt);