]> rtime.felk.cvut.cz Git - linux-imx.git/commitdiff
Merge commit 'v2.6.34-rc6'
authorJ. Bruce Fields <bfields@citi.umich.edu>
Tue, 4 May 2010 15:27:05 +0000 (11:27 -0400)
committerJ. Bruce Fields <bfields@citi.umich.edu>
Tue, 4 May 2010 15:29:05 +0000 (11:29 -0400)
Conflicts:
fs/nfsd/nfs4callback.c

1  2 
fs/nfsd/export.c
fs/nfsd/nfs4callback.c
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c
fs/nfsd/nfsctl.c
fs/nfsd/vfs.c
net/sunrpc/svc_xprt.c

diff --combined fs/nfsd/export.c
index 55da4d339293f048d1e74c04749bef743a1bd83e,872a5ef550c7733c79650180ed453aaec25e7752..c2a4f71d87dd507a99d20564cff85ff15bd91646
@@@ -12,6 -12,7 +12,7 @@@
   * Copyright (C) 1995, 1996 Olaf Kirch, <okir@monad.swb.de>
   */
  
+ #include <linux/slab.h>
  #include <linux/namei.h>
  #include <linux/module.h>
  #include <linux/exportfs.h>
@@@ -258,9 -259,10 +259,9 @@@ static struct cache_detail svc_expkey_c
        .alloc          = expkey_alloc,
  };
  
 -static struct svc_expkey *
 -svc_expkey_lookup(struct svc_expkey *item)
 +static int
 +svc_expkey_hash(struct svc_expkey *item)
  {
 -      struct cache_head *ch;
        int hash = item->ek_fsidtype;
        char * cp = (char*)item->ek_fsid;
        int len = key_len(item->ek_fsidtype);
        hash ^= hash_mem(cp, len, EXPKEY_HASHBITS);
        hash ^= hash_ptr(item->ek_client, EXPKEY_HASHBITS);
        hash &= EXPKEY_HASHMASK;
 +      return hash;
 +}
 +
 +static struct svc_expkey *
 +svc_expkey_lookup(struct svc_expkey *item)
 +{
 +      struct cache_head *ch;
 +      int hash = svc_expkey_hash(item);
  
        ch = sunrpc_cache_lookup(&svc_expkey_cache, &item->h,
                                 hash);
@@@ -289,7 -283,13 +290,7 @@@ static struct svc_expkey 
  svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old)
  {
        struct cache_head *ch;
 -      int hash = new->ek_fsidtype;
 -      char * cp = (char*)new->ek_fsid;
 -      int len = key_len(new->ek_fsidtype);
 -
 -      hash ^= hash_mem(cp, len, EXPKEY_HASHBITS);
 -      hash ^= hash_ptr(new->ek_client, EXPKEY_HASHBITS);
 -      hash &= EXPKEY_HASHMASK;
 +      int hash = svc_expkey_hash(new);
  
        ch = sunrpc_cache_update(&svc_expkey_cache, &new->h,
                                 &old->h, hash);
@@@ -738,22 -738,14 +739,22 @@@ struct cache_detail svc_export_cache = 
        .alloc          = svc_export_alloc,
  };
  
 -static struct svc_export *
 -svc_export_lookup(struct svc_export *exp)
 +static int
 +svc_export_hash(struct svc_export *exp)
  {
 -      struct cache_head *ch;
        int hash;
 +
        hash = hash_ptr(exp->ex_client, EXPORT_HASHBITS);
        hash ^= hash_ptr(exp->ex_path.dentry, EXPORT_HASHBITS);
        hash ^= hash_ptr(exp->ex_path.mnt, EXPORT_HASHBITS);
 +      return hash;
 +}
 +
 +static struct svc_export *
 +svc_export_lookup(struct svc_export *exp)
 +{
 +      struct cache_head *ch;
 +      int hash = svc_export_hash(exp);
  
        ch = sunrpc_cache_lookup(&svc_export_cache, &exp->h,
                                 hash);
@@@ -767,7 -759,10 +768,7 @@@ static struct svc_export 
  svc_export_update(struct svc_export *new, struct svc_export *old)
  {
        struct cache_head *ch;
 -      int hash;
 -      hash = hash_ptr(old->ex_client, EXPORT_HASHBITS);
 -      hash ^= hash_ptr(old->ex_path.dentry, EXPORT_HASHBITS);
 -      hash ^= hash_ptr(old->ex_path.mnt, EXPORT_HASHBITS);
 +      int hash = svc_export_hash(old);
  
        ch = sunrpc_cache_update(&svc_export_cache, &new->h,
                                 &old->h,
@@@ -1076,9 -1071,9 +1077,9 @@@ exp_export(struct nfsctl_export *nxp
                err = 0;
  finish:
        kfree(new.ex_pathname);
 -      if (exp)
 +      if (!IS_ERR_OR_NULL(exp))
                exp_put(exp);
 -      if (fsid_key && !IS_ERR(fsid_key))
 +      if (!IS_ERR_OR_NULL(fsid_key))
                cache_put(&fsid_key->h, &svc_expkey_cache);
        path_put(&path);
  out_put_clp:
diff --combined fs/nfsd/nfs4callback.c
index ea77aa63754a5f3c5909c6a9811234f3d2540060,7e32bd394e8696afac48c56bc27c768155c9db7e..1d5051d46b4685a6993f3d53ac1e43a98367a84b
@@@ -32,7 -32,7 +32,8 @@@
   */
  
  #include <linux/sunrpc/clnt.h>
 +#include <linux/sunrpc/svc_xprt.h>
+ #include <linux/slab.h>
  #include "nfsd.h"
  #include "state.h"
  
@@@ -79,6 -79,11 +80,6 @@@ enum nfs_cb_opnum4 
                                        cb_sequence_dec_sz +            \
                                        op_dec_sz)
  
 -struct nfs4_rpc_args {
 -      void                            *args_op;
 -      struct nfsd4_cb_sequence        args_seq;
 -};
 -
  /*
  * Generic encode routines from fs/nfs/nfs4xdr.c
  */
@@@ -451,14 -456,15 +452,14 @@@ static struct rpc_program cb_program = 
  
  static int max_cb_time(void)
  {
 -      return max(NFSD_LEASE_TIME/10, (time_t)1) * HZ;
 +      return max(nfsd4_lease/10, (time_t)1) * HZ;
  }
  
  /* Reference counting, callback cleanup, etc., all look racy as heck.
 - * And why is cb_set an atomic? */
 + * And why is cl_cb_set an atomic? */
  
 -int setup_callback_client(struct nfs4_client *clp)
 +int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
  {
 -      struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
        struct rpc_timeout      timeparms = {
                .to_initval     = max_cb_time(),
                .to_retries     = 0,
        if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
                return -EINVAL;
        if (cb->cb_minorversion) {
 -              args.bc_xprt = clp->cl_cb_xprt;
 +              args.bc_xprt = cb->cb_xprt;
                args.protocol = XPRT_TRANSPORT_BC_TCP;
        }
        /* Create RPC client */
                        PTR_ERR(client));
                return PTR_ERR(client);
        }
 -      cb->cb_client = client;
 +      nfsd4_set_callback_client(clp, client);
        return 0;
  
  }
@@@ -508,7 -514,8 +509,7 @@@ static void nfsd4_cb_probe_done(struct 
        if (task->tk_status)
                warn_no_callback_path(clp, task->tk_status);
        else
 -              atomic_set(&clp->cl_cb_conn.cb_set, 1);
 -      put_nfs4_client(clp);
 +              atomic_set(&clp->cl_cb_set, 1);
  }
  
  static const struct rpc_call_ops nfsd4_cb_probe_ops = {
@@@ -530,6 -537,7 +531,6 @@@ int set_callback_cred(void
  
  void do_probe_callback(struct nfs4_client *clp)
  {
 -      struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
        struct rpc_message msg = {
                .rpc_proc       = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
                .rpc_argp       = clp,
        };
        int status;
  
 -      status = rpc_call_async(cb->cb_client, &msg,
 +      status = rpc_call_async(clp->cl_cb_client, &msg,
                                RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
                                &nfsd4_cb_probe_ops, (void *)clp);
 -      if (status) {
 +      if (status)
                warn_no_callback_path(clp, status);
 -              put_nfs4_client(clp);
 -      }
  }
  
  /*
   * Set up the callback client and put a NFSPROC4_CB_NULL on the wire...
   */
 -void
 -nfsd4_probe_callback(struct nfs4_client *clp)
 +void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
  {
        int status;
  
 -      BUG_ON(atomic_read(&clp->cl_cb_conn.cb_set));
 +      BUG_ON(atomic_read(&clp->cl_cb_set));
  
 -      status = setup_callback_client(clp);
 +      status = setup_callback_client(clp, cb);
        if (status) {
                warn_no_callback_path(clp, status);
                return;
        }
 -
 -      /* the task holds a reference to the nfs4_client struct */
 -      atomic_inc(&clp->cl_count);
 -
        do_probe_callback(clp);
  }
  
@@@ -643,32 -658,18 +644,32 @@@ static void nfsd4_cb_done(struct rpc_ta
        }
  }
  
 +
  static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
  {
        struct nfs4_delegation *dp = calldata;
        struct nfs4_client *clp = dp->dl_client;
 +      struct rpc_clnt *current_rpc_client = clp->cl_cb_client;
  
        nfsd4_cb_done(task, calldata);
  
 +      if (current_rpc_client == NULL) {
 +              /* We're shutting down; give up. */
 +              /* XXX: err, or is it ok just to fall through
 +               * and rpc_restart_call? */
 +              return;
 +      }
 +
        switch (task->tk_status) {
        case -EIO:
                /* Network partition? */
 -              atomic_set(&clp->cl_cb_conn.cb_set, 0);
 +              atomic_set(&clp->cl_cb_set, 0);
                warn_no_callback_path(clp, task->tk_status);
 +              if (current_rpc_client != task->tk_client) {
 +                      /* queue a callback on the new connection: */
 +                      nfsd4_cb_recall(dp);
 +                      return;
 +              }
        case -EBADHANDLE:
        case -NFS4ERR_BAD_STATEID:
                /* Race: client probably got cb_recall
                break;
        default:
                /* success, or error we can't handle */
 -              goto done;
 +              return;
        }
        if (dp->dl_retries--) {
                rpc_delay(task, 2*HZ);
                rpc_restart_call(task);
                return;
        } else {
 -              atomic_set(&clp->cl_cb_conn.cb_set, 0);
 +              atomic_set(&clp->cl_cb_set, 0);
                warn_no_callback_path(clp, task->tk_status);
        }
 -done:
 -      kfree(task->tk_msg.rpc_argp);
  }
  
  static void nfsd4_cb_recall_release(void *calldata)
  {
        struct nfs4_delegation *dp = calldata;
 -      struct nfs4_client *clp = dp->dl_client;
  
        nfs4_put_delegation(dp);
 -      put_nfs4_client(clp);
  }
  
  static const struct rpc_call_ops nfsd4_cb_recall_ops = {
        .rpc_release = nfsd4_cb_recall_release,
  };
  
 +static struct workqueue_struct *callback_wq;
 +
 +int nfsd4_create_callback_queue(void)
 +{
 +      callback_wq = create_singlethread_workqueue("nfsd4_callbacks");
 +      if (!callback_wq)
 +              return -ENOMEM;
 +      return 0;
 +}
 +
 +void nfsd4_destroy_callback_queue(void)
 +{
 +      destroy_workqueue(callback_wq);
 +}
 +
 +void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt *new)
 +{
 +      struct rpc_clnt *old = clp->cl_cb_client;
 +
 +      clp->cl_cb_client = new;
 +      /*
 +       * After this, any work that saw the old value of cl_cb_client will
 +       * be gone:
 +       */
 +      flush_workqueue(callback_wq);
 +      /* So we can safely shut it down: */
 +      if (old)
 +              rpc_shutdown_client(old);
 +}
 +
  /*
   * called with dp->dl_count inc'ed.
   */
 -void
 -nfsd4_cb_recall(struct nfs4_delegation *dp)
 +static void _nfsd4_cb_recall(struct nfs4_delegation *dp)
  {
        struct nfs4_client *clp = dp->dl_client;
 -      struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client;
 -      struct nfs4_rpc_args *args;
 +      struct rpc_clnt *clnt = clp->cl_cb_client;
 +      struct nfs4_rpc_args *args = &dp->dl_recall.cb_args;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL],
                .rpc_cred = callback_cred
        };
 -      int status = -ENOMEM;
 +      int status;
 +
 +      if (clnt == NULL)
 +              return; /* Client is shutting down; give up. */
  
 -      args = kzalloc(sizeof(*args), GFP_KERNEL);
 -      if (!args)
 -              goto out;
        args->args_op = dp;
        msg.rpc_argp = args;
        dp->dl_retries = 1;
        status = rpc_call_async(clnt, &msg, RPC_TASK_SOFT,
                                &nfsd4_cb_recall_ops, dp);
 -out:
 -      if (status) {
 -              kfree(args);
 -              put_nfs4_client(clp);
 +      if (status)
                nfs4_put_delegation(dp);
 -      }
 +}
 +
 +void nfsd4_do_callback_rpc(struct work_struct *w)
 +{
 +      /* XXX: for now, just send off delegation recall. */
 +      /* In future, generalize to handle any sort of callback. */
 +      struct nfsd4_callback *c = container_of(w, struct nfsd4_callback, cb_work);
 +      struct nfs4_delegation *dp = container_of(c, struct nfs4_delegation, dl_recall);
 +
 +      _nfsd4_cb_recall(dp);
 +}
 +
 +
 +void nfsd4_cb_recall(struct nfs4_delegation *dp)
 +{
 +      queue_work(callback_wq, &dp->dl_recall.cb_work);
  }
diff --combined fs/nfsd/nfs4proc.c
index 61282f8405b5da8a8216636fdc6e5a0bf47de00c,2ab9e8501bfead1a451fb75cd7441407b412019b..e2dc9608281b1d6b80977765835fc326f340753d
@@@ -33,6 -33,7 +33,7 @@@
   *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  #include <linux/file.h>
+ #include <linux/slab.h>
  
  #include "cache.h"
  #include "xdr4.h"
@@@ -968,36 -969,20 +969,36 @@@ static struct nfsd4_operation nfsd4_ops
  static const char *nfsd4_op_name(unsigned opnum);
  
  /*
 - * Enforce NFSv4.1 COMPOUND ordering rules.
 + * Enforce NFSv4.1 COMPOUND ordering rules:
   *
 - * TODO:
 - * - enforce NFS4ERR_NOT_ONLY_OP,
 - * - DESTROY_SESSION MUST be the final operation in the COMPOUND request.
 + * Also note, enforced elsewhere:
 + *    - SEQUENCE other than as first op results in
 + *      NFS4ERR_SEQUENCE_POS. (Enforced in nfsd4_sequence().)
 + *    - BIND_CONN_TO_SESSION must be the only op in its compound
 + *      (Will be enforced in nfsd4_bind_conn_to_session().)
 + *    - DESTROY_SESSION must be the final operation in a compound, if
 + *      sessionid's in SEQUENCE and DESTROY_SESSION are the same.
 + *      (Enforced in nfsd4_destroy_session().)
   */
 -static bool nfs41_op_ordering_ok(struct nfsd4_compoundargs *args)
 +static __be32 nfs41_check_op_ordering(struct nfsd4_compoundargs *args)
  {
 -      if (args->minorversion && args->opcnt > 0) {
 -              struct nfsd4_op *op = &args->ops[0];
 -              return (op->status == nfserr_op_illegal) ||
 -                     (nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP);
 -      }
 -      return true;
 +      struct nfsd4_op *op = &args->ops[0];
 +
 +      /* These ordering requirements don't apply to NFSv4.0: */
 +      if (args->minorversion == 0)
 +              return nfs_ok;
 +      /* This is weird, but OK, not our problem: */
 +      if (args->opcnt == 0)
 +              return nfs_ok;
 +      if (op->status == nfserr_op_illegal)
 +              return nfs_ok;
 +      if (!(nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP))
 +              return nfserr_op_not_in_session;
 +      if (op->opnum == OP_SEQUENCE)
 +              return nfs_ok;
 +      if (args->opcnt != 1)
 +              return nfserr_not_only_op;
 +      return nfs_ok;
  }
  
  /*
@@@ -1027,7 -1012,6 +1028,7 @@@ nfsd4_proc_compound(struct svc_rqst *rq
        resp->rqstp = rqstp;
        resp->cstate.minorversion = args->minorversion;
        resp->cstate.replay_owner = NULL;
 +      resp->cstate.session = NULL;
        fh_init(&resp->cstate.current_fh, NFS4_FHSIZE);
        fh_init(&resp->cstate.save_fh, NFS4_FHSIZE);
        /* Use the deferral mechanism only for NFSv4.0 compounds */
        if (args->minorversion > nfsd_supported_minorversion)
                goto out;
  
 -      if (!nfs41_op_ordering_ok(args)) {
 +      status = nfs41_check_op_ordering(args);
 +      if (status) {
                op = &args->ops[0];
 -              op->status = nfserr_sequence_pos;
 +              op->status = status;
                goto encode_op;
        }
  
 -      status = nfs_ok;
        while (!status && resp->opcnt < args->opcnt) {
                op = &args->ops[resp->opcnt++];
  
diff --combined fs/nfsd/nfs4state.c
index 737315c61e7e0b77a6d18e605e0e0b006d869a18,6a8fedaa4f55a8ad0f6236296063e385d8eb1a17..f05a3276ba6b74a563dffb102f3b49f5b354de33
@@@ -34,6 -34,7 +34,7 @@@
  
  #include <linux/file.h>
  #include <linux/smp_lock.h>
+ #include <linux/slab.h>
  #include <linux/namei.h>
  #include <linux/swap.h>
  #include <linux/sunrpc/svcauth_gss.h>
@@@ -44,8 -45,8 +45,8 @@@
  #define NFSDDBG_FACILITY                NFSDDBG_PROC
  
  /* Globals */
 -static time_t lease_time = 90;     /* default lease time */
 -static time_t user_lease_time = 90;
 +time_t nfsd4_lease = 90;     /* default lease time */
 +time_t nfsd4_grace = 90;
  static time_t boot_time;
  static u32 current_ownerid = 1;
  static u32 current_fileid = 1;
@@@ -198,7 -199,6 +199,7 @@@ alloc_init_deleg(struct nfs4_client *cl
        atomic_set(&dp->dl_count, 1);
        list_add(&dp->dl_perfile, &fp->fi_delegations);
        list_add(&dp->dl_perclnt, &clp->cl_delegations);
 +      INIT_WORK(&dp->dl_recall.cb_work, nfsd4_do_callback_rpc);
        return dp;
  }
  
@@@ -680,9 -680,27 +681,9 @@@ static struct nfs4_client *alloc_client
        return clp;
  }
  
 -static void
 -shutdown_callback_client(struct nfs4_client *clp)
 -{
 -      struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client;
 -
 -      if (clnt) {
 -              /*
 -               * Callback threads take a reference on the client, so there
 -               * should be no outstanding callbacks at this point.
 -               */
 -              clp->cl_cb_conn.cb_client = NULL;
 -              rpc_shutdown_client(clnt);
 -      }
 -}
 -
  static inline void
  free_client(struct nfs4_client *clp)
  {
 -      shutdown_callback_client(clp);
 -      if (clp->cl_cb_xprt)
 -              svc_xprt_put(clp->cl_cb_xprt);
        if (clp->cl_cred.cr_group_info)
                put_group_info(clp->cl_cred.cr_group_info);
        kfree(clp->cl_principal);
        kfree(clp);
  }
  
 -void
 -put_nfs4_client(struct nfs4_client *clp)
 -{
 -      if (atomic_dec_and_test(&clp->cl_count))
 -              free_client(clp);
 -}
 -
  static void
  expire_client(struct nfs4_client *clp)
  {
        struct nfs4_delegation *dp;
        struct list_head reaplist;
  
 -      dprintk("NFSD: expire_client cl_count %d\n",
 -                          atomic_read(&clp->cl_count));
 -
        INIT_LIST_HEAD(&reaplist);
        spin_lock(&recall_lock);
        while (!list_empty(&clp->cl_delegations)) {
                                 se_perclnt);
                release_session(ses);
        }
 -      put_nfs4_client(clp);
 +      nfsd4_set_callback_client(clp, NULL);
 +      if (clp->cl_cb_conn.cb_xprt)
 +              svc_xprt_put(clp->cl_cb_conn.cb_xprt);
 +      free_client(clp);
  }
  
  static void copy_verf(struct nfs4_client *target, nfs4_verifier *source)
@@@ -814,7 -839,8 +815,7 @@@ static struct nfs4_client *create_clien
        }
  
        memcpy(clp->cl_recdir, recdir, HEXDIR_LEN);
 -      atomic_set(&clp->cl_count, 1);
 -      atomic_set(&clp->cl_cb_conn.cb_set, 0);
 +      atomic_set(&clp->cl_cb_set, 0);
        INIT_LIST_HEAD(&clp->cl_idhash);
        INIT_LIST_HEAD(&clp->cl_strhash);
        INIT_LIST_HEAD(&clp->cl_openowners);
@@@ -1301,9 -1327,15 +1302,9 @@@ nfsd4_create_session(struct svc_rqst *r
                cs_slot->sl_seqid++; /* from 0 to 1 */
                move_to_confirmed(unconf);
  
 -              /*
 -               * We do not support RDMA or persistent sessions
 -               */
 -              cr_ses->flags &= ~SESSION4_PERSIST;
 -              cr_ses->flags &= ~SESSION4_RDMA;
 -
                if (cr_ses->flags & SESSION4_BACK_CHAN) {
 -                      unconf->cl_cb_xprt = rqstp->rq_xprt;
 -                      svc_xprt_get(unconf->cl_cb_xprt);
 +                      unconf->cl_cb_conn.cb_xprt = rqstp->rq_xprt;
 +                      svc_xprt_get(rqstp->rq_xprt);
                        rpc_copy_addr(
                                (struct sockaddr *)&unconf->cl_cb_conn.cb_addr,
                                sa);
                                cstate->minorversion;
                        unconf->cl_cb_conn.cb_prog = cr_ses->callback_prog;
                        unconf->cl_cb_seq_nr = 1;
 -                      nfsd4_probe_callback(unconf);
 +                      nfsd4_probe_callback(unconf, &unconf->cl_cb_conn);
                }
                conf = unconf;
        } else {
                goto out;
        }
  
 +      /*
 +       * We do not support RDMA or persistent sessions
 +       */
 +      cr_ses->flags &= ~SESSION4_PERSIST;
 +      cr_ses->flags &= ~SESSION4_RDMA;
 +
        status = alloc_init_session(rqstp, conf, cr_ses);
        if (status)
                goto out;
        return status;
  }
  
 +static bool nfsd4_last_compound_op(struct svc_rqst *rqstp)
 +{
 +      struct nfsd4_compoundres *resp = rqstp->rq_resp;
 +      struct nfsd4_compoundargs *argp = rqstp->rq_argp;
 +
 +      return argp->opcnt == resp->opcnt;
 +}
 +
  __be32
  nfsd4_destroy_session(struct svc_rqst *r,
                      struct nfsd4_compound_state *cstate,
         * - Do we need to clear any callback info from previous session?
         */
  
 +      if (!memcmp(&sessionid->sessionid, &cstate->session->se_sessionid,
 +                                      sizeof(struct nfs4_sessionid))) {
 +              if (!nfsd4_last_compound_op(r))
 +                      return nfserr_not_only_op;
 +      }
        dump_sessionid(__func__, &sessionid->sessionid);
        spin_lock(&sessionid_lock);
        ses = find_in_sessionid_hashtbl(&sessionid->sessionid);
        spin_unlock(&sessionid_lock);
  
        /* wait for callbacks */
 -      shutdown_callback_client(ses->se_client);
 +      nfsd4_set_callback_client(ses->se_client, NULL);
        nfsd4_put_session(ses);
        status = nfs_ok;
  out:
@@@ -1443,10 -1456,11 +1444,10 @@@ nfsd4_sequence(struct svc_rqst *rqstp
        cstate->slot = slot;
        cstate->session = session;
  
 -      /* Hold a session reference until done processing the compound:
 -       * nfsd4_put_session called only if the cstate slot is set.
 -       */
 -      nfsd4_get_session(session);
  out:
 +      /* Hold a session reference until done processing the compound. */
 +      if (cstate->session)
 +              nfsd4_get_session(cstate->session);
        spin_unlock(&sessionid_lock);
        /* Renew the clientid on success and on replay */
        if (cstate->session) {
@@@ -1617,8 -1631,9 +1618,8 @@@ nfsd4_setclientid_confirm(struct svc_rq
                if (!same_creds(&conf->cl_cred, &unconf->cl_cred))
                        status = nfserr_clid_inuse;
                else {
 -                      /* XXX: We just turn off callbacks until we can handle
 -                        * change request correctly. */
 -                      atomic_set(&conf->cl_cb_conn.cb_set, 0);
 +                      atomic_set(&conf->cl_cb_set, 0);
 +                      nfsd4_probe_callback(conf, &unconf->cl_cb_conn);
                        expire_client(unconf);
                        status = nfs_ok;
  
                        }
                        move_to_confirmed(unconf);
                        conf = unconf;
 -                      nfsd4_probe_callback(conf);
 +                      nfsd4_probe_callback(conf, &conf->cl_cb_conn);
                        status = nfs_ok;
                }
        } else if ((!conf || (conf && !same_verf(&conf->cl_confirm, &confirm)))
@@@ -2013,6 -2028,7 +2014,6 @@@ void nfsd_break_deleg_cb(struct file_lo
         * lock) we know the server hasn't removed the lease yet, we know
         * it's safe to take a reference: */
        atomic_inc(&dp->dl_count);
 -      atomic_inc(&dp->dl_client->cl_count);
  
        spin_lock(&recall_lock);
        list_add_tail(&dp->dl_recall_lru, &del_recall_lru);
@@@ -2331,7 -2347,7 +2332,7 @@@ nfs4_open_delegation(struct svc_fh *fh
  {
        struct nfs4_delegation *dp;
        struct nfs4_stateowner *sop = stp->st_stateowner;
 -      struct nfs4_cb_conn *cb = &sop->so_client->cl_cb_conn;
 +      int cb_up = atomic_read(&sop->so_client->cl_cb_set);
        struct file_lock fl, *flp = &fl;
        int status, flag = 0;
  
        open->op_recall = 0;
        switch (open->op_claim_type) {
                case NFS4_OPEN_CLAIM_PREVIOUS:
 -                      if (!atomic_read(&cb->cb_set))
 +                      if (!cb_up)
                                open->op_recall = 1;
                        flag = open->op_delegate_type;
                        if (flag == NFS4_OPEN_DELEGATE_NONE)
                         * had the chance to reclaim theirs.... */
                        if (locks_in_grace())
                                goto out;
 -                      if (!atomic_read(&cb->cb_set) || !sop->so_confirmed)
 +                      if (!cb_up || !sop->so_confirmed)
                                goto out;
                        if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE)
                                flag = NFS4_OPEN_DELEGATE_WRITE;
@@@ -2521,7 -2537,7 +2522,7 @@@ nfsd4_renew(struct svc_rqst *rqstp, str
        renew_client(clp);
        status = nfserr_cb_path_down;
        if (!list_empty(&clp->cl_delegations)
 -                      && !atomic_read(&clp->cl_cb_conn.cb_set))
 +                      && !atomic_read(&clp->cl_cb_set))
                goto out;
        status = nfs_ok;
  out:
@@@ -2538,12 -2554,6 +2539,12 @@@ nfsd4_end_grace(void
        dprintk("NFSD: end of grace period\n");
        nfsd4_recdir_purge_old();
        locks_end_grace(&nfsd4_manager);
 +      /*
 +       * Now that every NFSv4 client has had the chance to recover and
 +       * to see the (possibly new, possibly shorter) lease time, we
 +       * can safely set the next grace time to the current lease time:
 +       */
 +      nfsd4_grace = nfsd4_lease;
  }
  
  static time_t
@@@ -2553,9 -2563,9 +2554,9 @@@ nfs4_laundromat(void
        struct nfs4_stateowner *sop;
        struct nfs4_delegation *dp;
        struct list_head *pos, *next, reaplist;
 -      time_t cutoff = get_seconds() - NFSD_LEASE_TIME;
 -      time_t t, clientid_val = NFSD_LEASE_TIME;
 -      time_t u, test_val = NFSD_LEASE_TIME;
 +      time_t cutoff = get_seconds() - nfsd4_lease;
 +      time_t t, clientid_val = nfsd4_lease;
 +      time_t u, test_val = nfsd4_lease;
  
        nfs4_lock_state();
  
                list_del_init(&dp->dl_recall_lru);
                unhash_delegation(dp);
        }
 -      test_val = NFSD_LEASE_TIME;
 +      test_val = nfsd4_lease;
        list_for_each_safe(pos, next, &close_lru) {
                sop = list_entry(pos, struct nfs4_stateowner, so_close_lru);
                if (time_after((unsigned long)sop->so_time, (unsigned long)cutoff)) {
@@@ -2665,7 -2675,7 +2666,7 @@@ EXPIRED_STATEID(stateid_t *stateid
  {
        if (time_before((unsigned long)boot_time,
                        ((unsigned long)stateid->si_boot)) &&
 -          time_before((unsigned long)(stateid->si_boot + lease_time), get_seconds())) {
 +          time_before((unsigned long)(stateid->si_boot + nfsd4_lease), get_seconds())) {
                dprintk("NFSD: expired stateid " STATEID_FMT "!\n",
                        STATEID_VAL(stateid));
                return 1;
@@@ -3966,6 -3976,12 +3967,6 @@@ nfsd4_load_reboot_recovery_data(void
                printk("NFSD: Failure reading reboot recovery data\n");
  }
  
 -unsigned long
 -get_nfs4_grace_period(void)
 -{
 -      return max(user_lease_time, lease_time) * HZ;
 -}
 -
  /*
   * Since the lifetime of a delegation isn't limited to that of an open, a
   * client may quite reasonably hang on to a delegation as long as it has
@@@ -3992,27 -4008,20 +3993,27 @@@ set_max_delegations(void
  static int
  __nfs4_state_start(void)
  {
 -      unsigned long grace_time;
 +      int ret;
  
        boot_time = get_seconds();
 -      grace_time = get_nfs4_grace_period();
 -      lease_time = user_lease_time;
        locks_start_grace(&nfsd4_manager);
        printk(KERN_INFO "NFSD: starting %ld-second grace period\n",
 -             grace_time/HZ);
 +             nfsd4_grace);
 +      ret = set_callback_cred();
 +      if (ret)
 +              return -ENOMEM;
        laundry_wq = create_singlethread_workqueue("nfsd4");
        if (laundry_wq == NULL)
                return -ENOMEM;
 -      queue_delayed_work(laundry_wq, &laundromat_work, grace_time);
 +      ret = nfsd4_create_callback_queue();
 +      if (ret)
 +              goto out_free_laundry;
 +      queue_delayed_work(laundry_wq, &laundromat_work, nfsd4_grace * HZ);
        set_max_delegations();
 -      return set_callback_cred();
 +      return 0;
 +out_free_laundry:
 +      destroy_workqueue(laundry_wq);
 +      return ret;
  }
  
  int
@@@ -4030,6 -4039,12 +4031,6 @@@ nfs4_state_start(void
        return 0;
  }
  
 -time_t
 -nfs4_lease_time(void)
 -{
 -      return lease_time;
 -}
 -
  static void
  __nfs4_state_shutdown(void)
  {
@@@ -4074,7 -4089,6 +4075,7 @@@ nfs4_state_shutdown(void
        nfs4_lock_state();
        nfs4_release_reclaim();
        __nfs4_state_shutdown();
 +      nfsd4_destroy_callback_queue();
        nfs4_unlock_state();
  }
  
@@@ -4114,3 -4128,21 +4115,3 @@@ nfs4_recoverydir(void
  {
        return user_recovery_dirname;
  }
 -
 -/*
 - * Called when leasetime is changed.
 - *
 - * The only way the protocol gives us to handle on-the-fly lease changes is to
 - * simulate a reboot.  Instead of doing that, we just wait till the next time
 - * we start to register any changes in lease time.  If the administrator
 - * really wants to change the lease time *now*, they can go ahead and bring
 - * nfsd down and then back up again after changing the lease time.
 - *
 - * user_lease_time is protected by nfsd_mutex since it's only really accessed
 - * when nfsd is starting
 - */
 -void
 -nfs4_reset_lease(time_t leasetime)
 -{
 -      user_lease_time = leasetime;
 -}
diff --combined fs/nfsd/nfs4xdr.c
index b27bcf33107c57fd87e7537e948f25e3c06d2977,34ccf815ea8aa48f854a8ad6f9d15e64c2c59e51..5c2de471329a6cacd3a6d4c13f07136245f45e10
@@@ -40,6 -40,7 +40,7 @@@
   * at the end of nfs4svc_decode_compoundargs.
   */
  
+ #include <linux/slab.h>
  #include <linux/namei.h>
  #include <linux/statfs.h>
  #include <linux/utsname.h>
@@@ -160,10 -161,10 +161,10 @@@ static __be32 *read_buf(struct nfsd4_co
        argp->p = page_address(argp->pagelist[0]);
        argp->pagelist++;
        if (argp->pagelen < PAGE_SIZE) {
-               argp->end = p + (argp->pagelen>>2);
+               argp->end = argp->p + (argp->pagelen>>2);
                argp->pagelen = 0;
        } else {
-               argp->end = p + (PAGE_SIZE>>2);
+               argp->end = argp->p + (PAGE_SIZE>>2);
                argp->pagelen -= PAGE_SIZE;
        }
        memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
@@@ -1425,10 -1426,10 +1426,10 @@@ nfsd4_decode_compound(struct nfsd4_comp
                        argp->p = page_address(argp->pagelist[0]);
                        argp->pagelist++;
                        if (argp->pagelen < PAGE_SIZE) {
-                               argp->end = p + (argp->pagelen>>2);
+                               argp->end = argp->p + (argp->pagelen>>2);
                                argp->pagelen = 0;
                        } else {
-                               argp->end = p + (PAGE_SIZE>>2);
+                               argp->end = argp->p + (PAGE_SIZE>>2);
                                argp->pagelen -= PAGE_SIZE;
                        }
                }
@@@ -1528,7 -1529,7 +1529,7 @@@ static void write_cinfo(__be32 **p, str
        } } while (0);
  
  /* Encode as an array of strings the string given with components
-  * seperated @sep.
+  * separated @sep.
   */
  static __be32 nfsd4_encode_components(char sep, char *components,
                                   __be32 **pp, int *buflen)
@@@ -1899,7 -1900,7 +1900,7 @@@ nfsd4_encode_fattr(struct svc_fh *fhp, 
        if (bmval0 & FATTR4_WORD0_LEASE_TIME) {
                if ((buflen -= 4) < 0)
                        goto out_resource;
 -              WRITE32(NFSD_LEASE_TIME);
 +              WRITE32(nfsd4_lease);
        }
        if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) {
                if ((buflen -= 4) < 0)
@@@ -3306,13 -3307,11 +3307,13 @@@ nfs4svc_encode_compoundres(struct svc_r
                iov = &rqstp->rq_res.head[0];
        iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
        BUG_ON(iov->iov_len > PAGE_SIZE);
 -      if (nfsd4_has_session(cs) && cs->status != nfserr_replay_cache) {
 -              nfsd4_store_cache_entry(resp);
 -              dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
 -              resp->cstate.slot->sl_inuse = false;
 -              nfsd4_put_session(resp->cstate.session);
 +      if (nfsd4_has_session(cs)) {
 +              if (cs->status != nfserr_replay_cache) {
 +                      nfsd4_store_cache_entry(resp);
 +                      dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
 +                      cs->slot->sl_inuse = false;
 +              }
 +              nfsd4_put_session(cs->session);
        }
        return 1;
  }
diff --combined fs/nfsd/nfsctl.c
index 413cb8ef951baae9b3066afd2f65fbd791d19ccb,e3591073098f769e2d3658e1fa17a1abb30661ad..bc3194ea01f531aac10b52d59a363f1f8369ac0b
@@@ -4,6 -4,7 +4,7 @@@
   * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
   */
  
+ #include <linux/slab.h>
  #include <linux/namei.h>
  #include <linux/ctype.h>
  
@@@ -45,7 -46,6 +46,7 @@@ enum 
         */
  #ifdef CONFIG_NFSD_V4
        NFSD_Leasetime,
 +      NFSD_Gracetime,
        NFSD_RecoveryDir,
  #endif
  };
@@@ -70,7 -70,6 +71,7 @@@ static ssize_t write_ports(struct file 
  static ssize_t write_maxblksize(struct file *file, char *buf, size_t size);
  #ifdef CONFIG_NFSD_V4
  static ssize_t write_leasetime(struct file *file, char *buf, size_t size);
 +static ssize_t write_gracetime(struct file *file, char *buf, size_t size);
  static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
  #endif
  
@@@ -92,7 -91,6 +93,7 @@@ static ssize_t (*write_op[])(struct fil
        [NFSD_MaxBlkSize] = write_maxblksize,
  #ifdef CONFIG_NFSD_V4
        [NFSD_Leasetime] = write_leasetime,
 +      [NFSD_Gracetime] = write_gracetime,
        [NFSD_RecoveryDir] = write_recoverydir,
  #endif
  };
@@@ -1206,45 -1204,29 +1207,45 @@@ static ssize_t write_maxblksize(struct 
  }
  
  #ifdef CONFIG_NFSD_V4
 -extern time_t nfs4_leasetime(void);
 -
 -static ssize_t __write_leasetime(struct file *file, char *buf, size_t size)
 +static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, time_t *time)
  {
 -      /* if size > 10 seconds, call
 -       * nfs4_reset_lease() then write out the new lease (seconds) as reply
 -       */
        char *mesg = buf;
 -      int rv, lease;
 +      int rv, i;
  
        if (size > 0) {
                if (nfsd_serv)
                        return -EBUSY;
 -              rv = get_int(&mesg, &lease);
 +              rv = get_int(&mesg, &i);
                if (rv)
                        return rv;
 -              if (lease < 10 || lease > 3600)
 +              /*
 +               * Some sanity checking.  We don't have a reason for
 +               * these particular numbers, but problems with the
 +               * extremes are:
 +               *      - Too short: the briefest network outage may
 +               *        cause clients to lose all their locks.  Also,
 +               *        the frequent polling may be wasteful.
 +               *      - Too long: do you really want reboot recovery
 +               *        to take more than an hour?  Or to make other
 +               *        clients wait an hour before being able to
 +               *        revoke a dead client's locks?
 +               */
 +              if (i < 10 || i > 3600)
                        return -EINVAL;
 -              nfs4_reset_lease(lease);
 +              *time = i;
        }
  
 -      return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n",
 -                                                      nfs4_lease_time());
 +      return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n", *time);
 +}
 +
 +static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, time_t *time)
 +{
 +      ssize_t rv;
 +
 +      mutex_lock(&nfsd_mutex);
 +      rv = __nfsd4_write_time(file, buf, size, time);
 +      mutex_unlock(&nfsd_mutex);
 +      return rv;
  }
  
  /**
   */
  static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
  {
 -      ssize_t rv;
 +      return nfsd4_write_time(file, buf, size, &nfsd4_lease);
 +}
  
 -      mutex_lock(&nfsd_mutex);
 -      rv = __write_leasetime(file, buf, size);
 -      mutex_unlock(&nfsd_mutex);
 -      return rv;
 +/**
 + * write_gracetime - Set or report current NFSv4 grace period time
 + *
 + * As above, but sets the time of the NFSv4 grace period.
 + *
 + * Note this should never be set to less than the *previous*
 + * lease-period time, but we don't try to enforce this.  (In the common
 + * case (a new boot), we don't know what the previous lease time was
 + * anyway.)
 + */
 +static ssize_t write_gracetime(struct file *file, char *buf, size_t size)
 +{
 +      return nfsd4_write_time(file, buf, size, &nfsd4_grace);
  }
  
  extern char *nfs4_recoverydir(void);
@@@ -1379,7 -1351,6 +1380,7 @@@ static int nfsd_fill_super(struct super
                [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO},
  #ifdef CONFIG_NFSD_V4
                [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
 +              [NFSD_Gracetime] = {"nfsv4gracetime", &transaction_ops, S_IWUSR|S_IRUSR},
                [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR},
  #endif
                /* last one */ {""}
diff --combined fs/nfsd/vfs.c
index c2dcb4c2b1fc28cbc23f6ffea6bfeb79dbd7cd5b,6dd5f1970e01dedc3da245d8d26d87236dfadaf4..23c06f77f4cab11b9ccc775698bd6c9cd1111e80
@@@ -25,6 -25,7 +25,7 @@@
  #include <linux/xattr.h>
  #include <linux/jhash.h>
  #include <linux/ima.h>
+ #include <linux/slab.h>
  #include <asm/uaccess.h>
  #include <linux/exportfs.h>
  #include <linux/writeback.h>
@@@ -723,7 -724,7 +724,7 @@@ nfsd_open(struct svc_rqst *rqstp, struc
        struct inode    *inode;
        int             flags = O_RDONLY|O_LARGEFILE;
        __be32          err;
 -      int             host_err;
 +      int             host_err = 0;
  
        validate_process_creds();
  
         * Check to see if there are any leases on this file.
         * This may block while leases are broken.
         */
 -      host_err = break_lease(inode, O_NONBLOCK | ((access & NFSD_MAY_WRITE) ? O_WRONLY : 0));
 +      if (!(access & NFSD_MAY_NOT_BREAK_LEASE))
 +              host_err = break_lease(inode, O_NONBLOCK | ((access & NFSD_MAY_WRITE) ? O_WRONLY : 0));
        if (host_err == -EWOULDBLOCK)
                host_err = -ETIMEDOUT;
        if (host_err) /* NOMEM or WOULDBLOCK */
@@@ -1169,8 -1169,7 +1170,8 @@@ nfsd_commit(struct svc_rqst *rqstp, str
                        goto out;
        }
  
 -      err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_WRITE, &file);
 +      err = nfsd_open(rqstp, fhp, S_IFREG,
 +                      NFSD_MAY_WRITE|NFSD_MAY_NOT_BREAK_LEASE, &file);
        if (err)
                goto out;
        if (EX_ISSYNC(fhp->fh_export)) {
diff --combined net/sunrpc/svc_xprt.c
index 75f9aa2eb08947164998fca41b1042bcf7a5207f,061b2e0f9118833bcc93facef2cb8bc40f655a01..cbc084939dd8af2b2e009098af2875c96fbc363f
@@@ -9,6 -9,7 +9,7 @@@
  #include <linux/errno.h>
  #include <linux/freezer.h>
  #include <linux/kthread.h>
+ #include <linux/slab.h>
  #include <net/sock.h>
  #include <linux/sunrpc/stats.h>
  #include <linux/sunrpc/svc_xprt.h>
@@@ -743,10 -744,8 +744,10 @@@ int svc_recv(struct svc_rqst *rqstp, lo
                if (rqstp->rq_deferred) {
                        svc_xprt_received(xprt);
                        len = svc_deferred_recv(rqstp);
 -              } else
 +              } else {
                        len = xprt->xpt_ops->xpo_recvfrom(rqstp);
 +                      svc_xprt_received(xprt);
 +              }
                dprintk("svc: got len=%d\n", len);
        }
  
@@@ -894,12 -893,12 +895,12 @@@ void svc_delete_xprt(struct svc_xprt *x
         */
        if (test_bit(XPT_TEMP, &xprt->xpt_flags))
                serv->sv_tmpcnt--;
 +      spin_unlock_bh(&serv->sv_lock);
  
        while ((dr = svc_deferred_dequeue(xprt)) != NULL)
                kfree(dr);
  
        svc_xprt_put(xprt);
 -      spin_unlock_bh(&serv->sv_lock);
  }
  
  void svc_close_xprt(struct svc_xprt *xprt)