]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'nfsd/nfsd-next'
authorStephen Rothwell <sfr@canb.auug.org.au>
Mon, 8 Oct 2012 23:52:02 +0000 (10:52 +1100)
committerStephen Rothwell <sfr@canb.auug.org.au>
Mon, 8 Oct 2012 23:52:02 +0000 (10:52 +1100)
1  2 
fs/lockd/svc.c
fs/locks.c
fs/nfs/callback.c
fs/nfsd/nfs4state.c
fs/nfsd/vfs.c

diff --combined fs/lockd/svc.c
index 7e355870d5190bec530ed1a751672dea5e71e7f8,e515569f0f8ba7e09103171ac59b1ec9fba5c897..a2aa97d45670635c4d84ef422ea9a1d794cdce69
@@@ -126,7 -126,7 +126,7 @@@ static void restart_grace(void
  static int
  lockd(void *vrqstp)
  {
-       int             err = 0, preverr = 0;
+       int             err = 0;
        struct svc_rqst *rqstp = vrqstp;
  
        /* try_to_freeze() is called from svc_recv() */
                 * recvfrom routine.
                 */
                err = svc_recv(rqstp, timeout);
-               if (err == -EAGAIN || err == -EINTR) {
-                       preverr = err;
+               if (err == -EAGAIN || err == -EINTR)
                        continue;
-               }
-               if (err < 0) {
-                       if (err != preverr) {
-                               printk(KERN_WARNING "%s: unexpected error "
-                                       "from svc_recv (%d)\n", __func__, err);
-                               preverr = err;
-                       }
-                       schedule_timeout_interruptible(HZ);
-                       continue;
-               }
-               preverr = err;
                dprintk("lockd: request from %s\n",
                                svc_print_addr(rqstp, buf, sizeof(buf)));
  
@@@ -596,7 -583,6 +583,7 @@@ static int lockd_init_net(struct net *n
  
        INIT_DELAYED_WORK(&ln->grace_period_end, grace_ender);
        INIT_LIST_HEAD(&ln->grace_list);
 +      spin_lock_init(&ln->nsm_clnt_lock);
        return 0;
  }
  
diff --combined fs/locks.c
index abc7dc6c490b6dc7ca6e9119c585dc10b1a2d3d9,669911e4af9ddec89a6b8141e87bdd97618d12cb..a94e331a52a2febbf46a4df702156c1a013b2990
@@@ -1289,7 -1289,7 +1289,7 @@@ EXPORT_SYMBOL(__break_lease)
  void lease_get_mtime(struct inode *inode, struct timespec *time)
  {
        struct file_lock *flock = inode->i_flock;
-       if (flock && IS_LEASE(flock) && (flock->fl_type & F_WRLCK))
+       if (flock && IS_LEASE(flock) && (flock->fl_type == F_WRLCK))
                *time = current_fs_time(inode->i_sb);
        else
                *time = inode->i_mtime;
@@@ -1625,13 -1625,15 +1625,13 @@@ EXPORT_SYMBOL(flock_lock_file_wait)
   */
  SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd)
  {
 -      struct file *filp;
 -      int fput_needed;
 +      struct fd f = fdget(fd);
        struct file_lock *lock;
        int can_sleep, unlock;
        int error;
  
        error = -EBADF;
 -      filp = fget_light(fd, &fput_needed);
 -      if (!filp)
 +      if (!f.file)
                goto out;
  
        can_sleep = !(cmd & LOCK_NB);
        unlock = (cmd == LOCK_UN);
  
        if (!unlock && !(cmd & LOCK_MAND) &&
 -          !(filp->f_mode & (FMODE_READ|FMODE_WRITE)))
 +          !(f.file->f_mode & (FMODE_READ|FMODE_WRITE)))
                goto out_putf;
  
 -      error = flock_make_lock(filp, &lock, cmd);
 +      error = flock_make_lock(f.file, &lock, cmd);
        if (error)
                goto out_putf;
        if (can_sleep)
                lock->fl_flags |= FL_SLEEP;
  
 -      error = security_file_lock(filp, lock->fl_type);
 +      error = security_file_lock(f.file, lock->fl_type);
        if (error)
                goto out_free;
  
 -      if (filp->f_op && filp->f_op->flock)
 -              error = filp->f_op->flock(filp,
 +      if (f.file->f_op && f.file->f_op->flock)
 +              error = f.file->f_op->flock(f.file,
                                          (can_sleep) ? F_SETLKW : F_SETLK,
                                          lock);
        else
 -              error = flock_lock_file_wait(filp, lock);
 +              error = flock_lock_file_wait(f.file, lock);
  
   out_free:
        locks_free_lock(lock);
  
   out_putf:
 -      fput_light(filp, fput_needed);
 +      fdput(f);
   out:
        return error;
  }
@@@ -2185,8 -2187,8 +2185,8 @@@ static void lock_get_status(struct seq_
        } else {
                seq_printf(f, "%s ",
                               (lease_breaking(fl))
-                              ? (fl->fl_type & F_UNLCK) ? "UNLCK" : "READ "
-                              : (fl->fl_type & F_WRLCK) ? "WRITE" : "READ ");
+                              ? (fl->fl_type == F_UNLCK) ? "UNLCK" : "READ "
+                              : (fl->fl_type == F_WRLCK) ? "WRITE" : "READ ");
        }
        if (inode) {
  #ifdef WE_CAN_BREAK_LSLK_NOW
diff --combined fs/nfs/callback.c
index 2245bef50f37a847b366387a6dc6d0e2d30e1f2f,d9e2a18807839f18902a023756e494cb9676d6b3..9a521fb3986955aab3f32e755bdef94e9c313aa2
@@@ -12,7 -12,6 +12,7 @@@
  #include <linux/sunrpc/svc.h>
  #include <linux/sunrpc/svcsock.h>
  #include <linux/nfs_fs.h>
 +#include <linux/errno.h>
  #include <linux/mutex.h>
  #include <linux/freezer.h>
  #include <linux/kthread.h>
@@@ -24,7 -23,6 +24,7 @@@
  #include "nfs4_fs.h"
  #include "callback.h"
  #include "internal.h"
 +#include "netns.h"
  
  #define NFSDBG_FACILITY NFSDBG_CALLBACK
  
@@@ -39,32 -37,7 +39,32 @@@ static struct nfs_callback_data nfs_cal
  static DEFINE_MUTEX(nfs_callback_mutex);
  static struct svc_program nfs4_callback_program;
  
 -unsigned short nfs_callback_tcpport6;
 +static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
 +{
 +      int ret;
 +      struct nfs_net *nn = net_generic(net, nfs_net_id);
 +
 +      ret = svc_create_xprt(serv, "tcp", net, PF_INET,
 +                              nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
 +      if (ret <= 0)
 +              goto out_err;
 +      nn->nfs_callback_tcpport = ret;
 +      dprintk("NFS: Callback listener port = %u (af %u, net %p)\n",
 +                      nn->nfs_callback_tcpport, PF_INET, net);
 +
 +      ret = svc_create_xprt(serv, "tcp", net, PF_INET6,
 +                              nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
 +      if (ret > 0) {
 +              nn->nfs_callback_tcpport6 = ret;
 +              dprintk("NFS: Callback listener port = %u (af %u, net %p)\n",
 +                              nn->nfs_callback_tcpport6, PF_INET6, net);
 +      } else if (ret != -EAFNOSUPPORT)
 +              goto out_err;
 +      return 0;
 +
 +out_err:
 +      return (ret) ? ret : -ENOMEM;
 +}
  
  /*
   * This is the NFSv4 callback kernel thread.
@@@ -72,7 -45,7 +72,7 @@@
  static int
  nfs4_callback_svc(void *vrqstp)
  {
-       int err, preverr = 0;
+       int err;
        struct svc_rqst *rqstp = vrqstp;
  
        set_freezable();
                 * Listen for a request on the socket
                 */
                err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT);
-               if (err == -EAGAIN || err == -EINTR) {
-                       preverr = err;
+               if (err == -EAGAIN || err == -EINTR)
                        continue;
-               }
-               if (err < 0) {
-                       if (err != preverr) {
-                               printk(KERN_WARNING "NFS: %s: unexpected error "
-                                       "from svc_recv (%d)\n", __func__, err);
-                               preverr = err;
-                       }
-                       schedule_timeout_uninterruptible(HZ);
-                       continue;
-               }
-               preverr = err;
                svc_process(rqstp);
        }
        return 0;
   * Prepare to bring up the NFSv4 callback service
   */
  static struct svc_rqst *
 -nfs4_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
 +nfs4_callback_up(struct svc_serv *serv)
  {
 -      int ret;
 -
 -      ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET,
 -                              nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
 -      if (ret <= 0)
 -              goto out_err;
 -      nfs_callback_tcpport = ret;
 -      dprintk("NFS: Callback listener port = %u (af %u)\n",
 -                      nfs_callback_tcpport, PF_INET);
 -
 -      ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET6,
 -                              nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
 -      if (ret > 0) {
 -              nfs_callback_tcpport6 = ret;
 -              dprintk("NFS: Callback listener port = %u (af %u)\n",
 -                              nfs_callback_tcpport6, PF_INET6);
 -      } else if (ret == -EAFNOSUPPORT)
 -              ret = 0;
 -      else
 -              goto out_err;
 -
        return svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
 -
 -out_err:
 -      if (ret == 0)
 -              ret = -ENOMEM;
 -      return ERR_PTR(ret);
  }
  
  #if defined(CONFIG_NFS_V4_1)
 +static int nfs41_callback_up_net(struct svc_serv *serv, struct net *net)
 +{
 +      /*
 +       * Create an svc_sock for the back channel service that shares the
 +       * fore channel connection.
 +       * Returns the input port (0) and sets the svc_serv bc_xprt on success
 +       */
 +      return svc_create_xprt(serv, "tcp-bc", net, PF_INET, 0,
 +                            SVC_SOCK_ANONYMOUS);
 +}
 +
  /*
   * The callback service for NFSv4.1 callbacks
   */
@@@ -161,9 -137,28 +149,9 @@@ nfs41_callback_svc(void *vrqstp
   * Bring up the NFSv4.1 callback service
   */
  static struct svc_rqst *
 -nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
 +nfs41_callback_up(struct svc_serv *serv)
  {
        struct svc_rqst *rqstp;
 -      int ret;
 -
 -      /*
 -       * Create an svc_sock for the back channel service that shares the
 -       * fore channel connection.
 -       * Returns the input port (0) and sets the svc_serv bc_xprt on success
 -       */
 -      ret = svc_create_xprt(serv, "tcp-bc", &init_net, PF_INET, 0,
 -                            SVC_SOCK_ANONYMOUS);
 -      if (ret < 0) {
 -              rqstp = ERR_PTR(ret);
 -              goto out;
 -      }
 -
 -      /*
 -       * Save the svc_serv in the transport so that it can
 -       * be referenced when the session backchannel is initialized
 -       */
 -      xprt->bc_serv = serv;
  
        INIT_LIST_HEAD(&serv->sv_cb_list);
        spin_lock_init(&serv->sv_cb_lock);
                svc_xprt_put(serv->sv_bc_xprt);
                serv->sv_bc_xprt = NULL;
        }
 -out:
        dprintk("--> %s return %ld\n", __func__,
                IS_ERR(rqstp) ? PTR_ERR(rqstp) : 0);
        return rqstp;
  }
  
 -static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
 -              struct svc_serv *serv, struct rpc_xprt *xprt,
 +static void nfs_minorversion_callback_svc_setup(struct svc_serv *serv,
                struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
  {
 -      if (minorversion) {
 -              *rqstpp = nfs41_callback_up(serv, xprt);
 -              *callback_svc = nfs41_callback_svc;
 -      }
 -      return minorversion;
 +      *rqstpp = nfs41_callback_up(serv);
 +      *callback_svc = nfs41_callback_svc;
  }
  
  static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
 -              struct nfs_callback_data *cb_info)
 +              struct svc_serv *serv)
  {
        if (minorversion)
 -              xprt->bc_serv = cb_info->serv;
 +              /*
 +               * Save the svc_serv in the transport so that it can
 +               * be referenced when the session backchannel is initialized
 +               */
 +              xprt->bc_serv = serv;
  }
  #else
 -static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
 -              struct svc_serv *serv, struct rpc_xprt *xprt,
 -              struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
 +static int nfs41_callback_up_net(struct svc_serv *serv, struct net *net)
  {
        return 0;
  }
  
 +static void nfs_minorversion_callback_svc_setup(struct svc_serv *serv,
 +              struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
 +{
 +      *rqstpp = ERR_PTR(-ENOTSUPP);
 +      *callback_svc = ERR_PTR(-ENOTSUPP);
 +}
 +
  static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
 -              struct nfs_callback_data *cb_info)
 +              struct svc_serv *serv)
  {
  }
  #endif /* CONFIG_NFS_V4_1 */
  
 -/*
 - * Bring up the callback thread if it is not already up.
 - */
 -int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
 +static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt,
 +                                struct svc_serv *serv)
  {
 -      struct svc_serv *serv = NULL;
        struct svc_rqst *rqstp;
        int (*callback_svc)(void *vrqstp);
        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
        char svc_name[12];
 -      int ret = 0;
 -      int minorversion_setup;
 -      struct net *net = &init_net;
 +      int ret;
  
 -      mutex_lock(&nfs_callback_mutex);
 -      if (cb_info->users++ || cb_info->task != NULL) {
 -              nfs_callback_bc_serv(minorversion, xprt, cb_info);
 -              goto out;
 -      }
 -      serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
 -      if (!serv) {
 -              ret = -ENOMEM;
 -              goto out_err;
 -      }
 -      /* As there is only one thread we need to over-ride the
 -       * default maximum of 80 connections
 -       */
 -      serv->sv_maxconn = 1024;
 +      nfs_callback_bc_serv(minorversion, xprt, serv);
  
 -      ret = svc_bind(serv, net);
 -      if (ret < 0) {
 -              printk(KERN_WARNING "NFS: bind callback service failed\n");
 -              goto out_err;
 -      }
 +      if (cb_info->task)
 +              return 0;
  
 -      minorversion_setup =  nfs_minorversion_callback_svc_setup(minorversion,
 -                                      serv, xprt, &rqstp, &callback_svc);
 -      if (!minorversion_setup) {
 +      switch (minorversion) {
 +      case 0:
                /* v4.0 callback setup */
 -              rqstp = nfs4_callback_up(serv, xprt);
 +              rqstp = nfs4_callback_up(serv);
                callback_svc = nfs4_callback_svc;
 +              break;
 +      default:
 +              nfs_minorversion_callback_svc_setup(serv,
 +                              &rqstp, &callback_svc);
        }
  
 -      if (IS_ERR(rqstp)) {
 -              ret = PTR_ERR(rqstp);
 -              goto out_err;
 -      }
 +      if (IS_ERR(rqstp))
 +              return PTR_ERR(rqstp);
  
        svc_sock_update_bufs(serv);
  
                svc_exit_thread(cb_info->rqst);
                cb_info->rqst = NULL;
                cb_info->task = NULL;
 -              goto out_err;
 +              return PTR_ERR(cb_info->task);
 +      }
 +      dprintk("nfs_callback_up: service started\n");
 +      return 0;
 +}
 +
 +static void nfs_callback_down_net(u32 minorversion, struct svc_serv *serv, struct net *net)
 +{
 +      struct nfs_net *nn = net_generic(net, nfs_net_id);
 +
 +      if (--nn->cb_users[minorversion])
 +              return;
 +
 +      dprintk("NFS: destroy per-net callback data; net=%p\n", net);
 +      svc_shutdown_net(serv, net);
 +}
 +
 +static int nfs_callback_up_net(int minorversion, struct svc_serv *serv, struct net *net)
 +{
 +      struct nfs_net *nn = net_generic(net, nfs_net_id);
 +      int ret;
 +
 +      if (nn->cb_users[minorversion]++)
 +              return 0;
 +
 +      dprintk("NFS: create per-net callback data; net=%p\n", net);
 +
 +      ret = svc_bind(serv, net);
 +      if (ret < 0) {
 +              printk(KERN_WARNING "NFS: bind callback service failed\n");
 +              goto err_bind;
 +      }
 +
 +      switch (minorversion) {
 +              case 0:
 +                      ret = nfs4_callback_up_net(serv, net);
 +                      break;
 +              case 1:
 +                      ret = nfs41_callback_up_net(serv, net);
 +                      break;
 +              default:
 +                      printk(KERN_ERR "NFS: unknown callback version: %d\n",
 +                                      minorversion);
 +                      ret = -EINVAL;
 +                      break;
 +      }
 +
 +      if (ret < 0) {
 +              printk(KERN_ERR "NFS: callback service start failed\n");
 +              goto err_socks;
 +      }
 +      return 0;
 +
 +err_socks:
 +      svc_rpcb_cleanup(serv, net);
 +err_bind:
 +      dprintk("NFS: Couldn't create callback socket: err = %d; "
 +                      "net = %p\n", ret, net);
 +      return ret;
 +}
 +
 +static struct svc_serv *nfs_callback_create_svc(int minorversion)
 +{
 +      struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
 +      struct svc_serv *serv;
 +
 +      /*
 +       * Check whether we're already up and running.
 +       */
 +      if (cb_info->task) {
 +              /*
 +               * Note: increase service usage, because later in case of error
 +               * svc_destroy() will be called.
 +               */
 +              svc_get(cb_info->serv);
 +              return cb_info->serv;
        }
 -out:
 +
 +      /*
 +       * Sanity check: if there's no task,
 +       * we should be the first user ...
 +       */
 +      if (cb_info->users)
 +              printk(KERN_WARNING "nfs_callback_create_svc: no kthread, %d users??\n",
 +                      cb_info->users);
 +
 +      serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
 +      if (!serv) {
 +              printk(KERN_ERR "nfs_callback_create_svc: create service failed\n");
 +              return ERR_PTR(-ENOMEM);
 +      }
 +      /* As there is only one thread we need to over-ride the
 +       * default maximum of 80 connections
 +       */
 +      serv->sv_maxconn = 1024;
 +      dprintk("nfs_callback_create_svc: service created\n");
 +      return serv;
 +}
 +
 +/*
 + * Bring up the callback thread if it is not already up.
 + */
 +int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
 +{
 +      struct svc_serv *serv;
 +      struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
 +      int ret;
 +      struct net *net = xprt->xprt_net;
 +
 +      mutex_lock(&nfs_callback_mutex);
 +
 +      serv = nfs_callback_create_svc(minorversion);
 +      if (IS_ERR(serv)) {
 +              ret = PTR_ERR(serv);
 +              goto err_create;
 +      }
 +
 +      ret = nfs_callback_up_net(minorversion, serv, net);
 +      if (ret < 0)
 +              goto err_net;
 +
 +      ret = nfs_callback_start_svc(minorversion, xprt, serv);
 +      if (ret < 0)
 +              goto err_start;
 +
 +      cb_info->users++;
        /*
         * svc_create creates the svc_serv with sv_nrthreads == 1, and then
         * svc_prepare_thread increments that. So we need to call svc_destroy
         * on both success and failure so that the refcount is 1 when the
         * thread exits.
         */
 -      if (serv)
 -              svc_destroy(serv);
 +err_net:
 +      svc_destroy(serv);
 +err_create:
        mutex_unlock(&nfs_callback_mutex);
        return ret;
 -out_err:
 -      dprintk("NFS: Couldn't create callback socket or server thread; "
 -              "err = %d\n", ret);
 -      cb_info->users--;
 -      if (serv)
 -              svc_shutdown_net(serv, net);
 -      goto out;
 +
 +err_start:
 +      nfs_callback_down_net(minorversion, serv, net);
 +      dprintk("NFS: Couldn't create server thread; err = %d\n", ret);
 +      goto err_net;
  }
  
  /*
   * Kill the callback thread if it's no longer being used.
   */
 -void nfs_callback_down(int minorversion)
 +void nfs_callback_down(int minorversion, struct net *net)
  {
        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
  
        mutex_lock(&nfs_callback_mutex);
 +      nfs_callback_down_net(minorversion, cb_info->serv, net);
        cb_info->users--;
        if (cb_info->users == 0 && cb_info->task != NULL) {
                kthread_stop(cb_info->task);
 -              svc_shutdown_net(cb_info->serv, &init_net);
 +              dprintk("nfs_callback_down: service stopped\n");
                svc_exit_thread(cb_info->rqst);
 +              dprintk("nfs_callback_down: service destroyed\n");
                cb_info->serv = NULL;
                cb_info->rqst = NULL;
                cb_info->task = NULL;
diff --combined fs/nfsd/nfs4state.c
index 48a1bad373342b10b689927faf002cf444043b22,412b888faecbac52265b79c52ee91a352a3f1a5f..d0237f872cc4e1fe49060120d725f729783dc245
@@@ -758,7 -758,7 +758,7 @@@ static void nfsd4_put_drc_mem(int slots
        spin_unlock(&nfsd_drc_lock);
  }
  
- static struct nfsd4_session *alloc_session(int slotsize, int numslots)
+ static struct nfsd4_session *__alloc_session(int slotsize, int numslots)
  {
        struct nfsd4_session *new;
        int mem, i;
@@@ -852,35 -852,28 +852,28 @@@ static int nfsd4_register_conn(struct n
        return register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user);
  }
  
- static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses, u32 dir)
+ static void nfsd4_init_conn(struct svc_rqst *rqstp, struct nfsd4_conn *conn, struct nfsd4_session *ses)
  {
-       struct nfsd4_conn *conn;
        int ret;
  
-       conn = alloc_conn(rqstp, dir);
-       if (!conn)
-               return nfserr_jukebox;
        nfsd4_hash_conn(conn, ses);
        ret = nfsd4_register_conn(conn);
        if (ret)
                /* oops; xprt is already down: */
                nfsd4_conn_lost(&conn->cn_xpt_user);
-       if (ses->se_client->cl_cb_state == NFSD4_CB_DOWN &&
-               dir & NFS4_CDFC4_BACK) {
+       if (conn->cn_flags & NFS4_CDFC4_BACK) {
                /* callback channel may be back up */
                nfsd4_probe_callback(ses->se_client);
        }
-       return nfs_ok;
  }
  
- static __be32 nfsd4_new_conn_from_crses(struct svc_rqst *rqstp, struct nfsd4_session *ses)
+ static struct nfsd4_conn *alloc_conn_from_crses(struct svc_rqst *rqstp, struct nfsd4_create_session *cses)
  {
        u32 dir = NFS4_CDFC4_FORE;
  
-       if (ses->se_flags & SESSION4_BACK_CHAN)
+       if (cses->flags & SESSION4_BACK_CHAN)
                dir |= NFS4_CDFC4_BACK;
-       return nfsd4_new_conn(rqstp, ses, dir);
+       return alloc_conn(rqstp, dir);
  }
  
  /* must be called under client_lock */
@@@ -903,20 -896,21 +896,21 @@@ static void nfsd4_del_conns(struct nfsd
        spin_unlock(&clp->cl_lock);
  }
  
+ static void __free_session(struct nfsd4_session *ses)
+ {
+       nfsd4_put_drc_mem(slot_bytes(&ses->se_fchannel), ses->se_fchannel.maxreqs);
+       free_session_slots(ses);
+       kfree(ses);
+ }
  static void free_session(struct kref *kref)
  {
        struct nfsd4_session *ses;
-       int mem;
  
        lockdep_assert_held(&client_lock);
        ses = container_of(kref, struct nfsd4_session, se_ref);
        nfsd4_del_conns(ses);
-       spin_lock(&nfsd_drc_lock);
-       mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel);
-       nfsd_drc_mem_used -= mem;
-       spin_unlock(&nfsd_drc_lock);
-       free_session_slots(ses);
-       kfree(ses);
+       __free_session(ses);
  }
  
  void nfsd4_put_session(struct nfsd4_session *ses)
        spin_unlock(&client_lock);
  }
  
- static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses)
+ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan)
  {
        struct nfsd4_session *new;
-       struct nfsd4_channel_attrs *fchan = &cses->fore_channel;
        int numslots, slotsize;
-       __be32 status;
-       int idx;
        /*
         * Note decreasing slot size below client's request may
         * make it difficult for client to function correctly, whereas
        if (numslots < 1)
                return NULL;
  
-       new = alloc_session(slotsize, numslots);
+       new = __alloc_session(slotsize, numslots);
        if (!new) {
                nfsd4_put_drc_mem(slotsize, fchan->maxreqs);
                return NULL;
        }
        init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize);
+       return new;
+ }
+ static struct nfsd4_session *init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses)
+ {
+       int idx;
  
        new->se_client = clp;
        gen_sessionid(new);
        spin_unlock(&clp->cl_lock);
        spin_unlock(&client_lock);
  
-       status = nfsd4_new_conn_from_crses(rqstp, new);
-       /* whoops: benny points out, status is ignored! (err, or bogus) */
-       if (status) {
-               spin_lock(&client_lock);
-               free_session(&new->se_ref);
-               spin_unlock(&client_lock);
-               return NULL;
-       }
        if (cses->flags & SESSION4_BACK_CHAN) {
                struct sockaddr *sa = svc_addr(rqstp);
                /*
                rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa);
                clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa);
        }
-       nfsd4_probe_callback(clp);
        return new;
  }
  
@@@ -1131,7 -1118,7 +1118,7 @@@ unhash_client_locked(struct nfs4_clien
  }
  
  static void
expire_client(struct nfs4_client *clp)
destroy_client(struct nfs4_client *clp)
  {
        struct nfs4_openowner *oo;
        struct nfs4_delegation *dp;
        spin_unlock(&client_lock);
  }
  
+ static void expire_client(struct nfs4_client *clp)
+ {
+       nfsd4_client_record_remove(clp);
+       destroy_client(clp);
+ }
  static void copy_verf(struct nfs4_client *target, nfs4_verifier *source)
  {
        memcpy(target->cl_verifier.data, source->data,
@@@ -1223,10 -1216,26 +1216,26 @@@ static bool groups_equal(struct group_i
        return true;
  }
  
+ /*
+  * RFC 3530 language requires clid_inuse be returned when the
+  * "principal" associated with a requests differs from that previously
+  * used.  We use uid, gid's, and gss principal string as our best
+  * approximation.  We also don't want to allow non-gss use of a client
+  * established using gss: in theory cr_principal should catch that
+  * change, but in practice cr_principal can be null even in the gss case
+  * since gssd doesn't always pass down a principal string.
+  */
+ static bool is_gss_cred(struct svc_cred *cr)
+ {
+       /* Is cr_flavor one of the gss "pseudoflavors"?: */
+       return (cr->cr_flavor > RPC_AUTH_MAXFLAVOR);
+ }
  static bool
  same_creds(struct svc_cred *cr1, struct svc_cred *cr2)
  {
-       if ((cr1->cr_flavor != cr2->cr_flavor)
+       if ((is_gss_cred(cr1) != is_gss_cred(cr2))
                || (cr1->cr_uid != cr2->cr_uid)
                || (cr1->cr_gid != cr2->cr_gid)
                || !groups_equal(cr1->cr_group_info, cr2->cr_group_info))
@@@ -1340,13 -1349,15 +1349,15 @@@ move_to_confirmed(struct nfs4_client *c
  }
  
  static struct nfs4_client *
- find_confirmed_client(clientid_t *clid)
+ find_confirmed_client(clientid_t *clid, bool sessions)
  {
        struct nfs4_client *clp;
        unsigned int idhashval = clientid_hashval(clid->cl_id);
  
        list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) {
                if (same_clid(&clp->cl_clientid, clid)) {
+                       if ((bool)clp->cl_minorversion != sessions)
+                               return NULL;
                        renew_client(clp);
                        return clp;
                }
  }
  
  static struct nfs4_client *
- find_unconfirmed_client(clientid_t *clid)
+ find_unconfirmed_client(clientid_t *clid, bool sessions)
  {
        struct nfs4_client *clp;
        unsigned int idhashval = clientid_hashval(clid->cl_id);
  
        list_for_each_entry(clp, &unconf_id_hashtbl[idhashval], cl_idhash) {
-               if (same_clid(&clp->cl_clientid, clid))
+               if (same_clid(&clp->cl_clientid, clid)) {
+                       if ((bool)clp->cl_minorversion != sessions)
+                               return NULL;
                        return clp;
+               }
        }
        return NULL;
  }
@@@ -1651,6 -1665,7 +1665,7 @@@ out_new
                status = nfserr_jukebox;
                goto out;
        }
+       new->cl_minorversion = 1;
  
        gen_clid(new);
        add_to_unconfirmed(new, strhashval);
@@@ -1743,67 -1758,71 +1758,71 @@@ nfsd4_create_session(struct svc_rqst *r
        struct sockaddr *sa = svc_addr(rqstp);
        struct nfs4_client *conf, *unconf;
        struct nfsd4_session *new;
+       struct nfsd4_conn *conn;
        struct nfsd4_clid_slot *cs_slot = NULL;
-       bool confirm_me = false;
        __be32 status = 0;
  
        if (cr_ses->flags & ~SESSION4_FLAG_MASK_A)
                return nfserr_inval;
+       if (check_forechannel_attrs(cr_ses->fore_channel))
+               return nfserr_toosmall;
+       new = alloc_session(&cr_ses->fore_channel);
+       if (!new)
+               return nfserr_jukebox;
+       status = nfserr_jukebox;
+       conn = alloc_conn_from_crses(rqstp, cr_ses);
+       if (!conn)
+               goto out_free_session;
  
        nfs4_lock_state();
-       unconf = find_unconfirmed_client(&cr_ses->clientid);
-       conf = find_confirmed_client(&cr_ses->clientid);
+       unconf = find_unconfirmed_client(&cr_ses->clientid, true);
+       conf = find_confirmed_client(&cr_ses->clientid, true);
  
        if (conf) {
                cs_slot = &conf->cl_cs_slot;
                status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
                if (status == nfserr_replay_cache) {
                        status = nfsd4_replay_create_session(cr_ses, cs_slot);
-                       goto out;
+                       goto out_free_conn;
                } else if (cr_ses->seqid != cs_slot->sl_seqid + 1) {
                        status = nfserr_seq_misordered;
-                       goto out;
+                       goto out_free_conn;
                }
        } else if (unconf) {
+               unsigned int hash;
+               struct nfs4_client *old;
                if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) ||
                    !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) {
                        status = nfserr_clid_inuse;
-                       goto out;
+                       goto out_free_conn;
                }
                cs_slot = &unconf->cl_cs_slot;
                status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
                if (status) {
                        /* an unconfirmed replay returns misordered */
                        status = nfserr_seq_misordered;
-                       goto out;
+                       goto out_free_conn;
                }
-               confirm_me = true;
+               hash = clientstr_hashval(unconf->cl_recdir);
+               old = find_confirmed_client_by_str(unconf->cl_recdir, hash);
+               if (old)
+                       expire_client(old);
+               move_to_confirmed(unconf);
                conf = unconf;
        } else {
                status = nfserr_stale_clientid;
-               goto out;
+               goto out_free_conn;
        }
-       /*
-        * XXX: we should probably set this at creation time, and check
-        * for consistent minorversion use throughout:
-        */
-       conf->cl_minorversion = 1;
+       status = nfs_ok;
        /*
         * We do not support RDMA or persistent sessions
         */
        cr_ses->flags &= ~SESSION4_PERSIST;
        cr_ses->flags &= ~SESSION4_RDMA;
  
-       status = nfserr_toosmall;
-       if (check_forechannel_attrs(cr_ses->fore_channel))
-               goto out;
+       init_session(rqstp, new, conf, cr_ses);
+       nfsd4_init_conn(rqstp, conn, new);
  
-       status = nfserr_jukebox;
-       new = alloc_init_session(rqstp, conf, cr_ses);
-       if (!new)
-               goto out;
-       status = nfs_ok;
        memcpy(cr_ses->sessionid.data, new->se_sessionid.data,
               NFS4_MAX_SESSIONID_LEN);
        memcpy(&cr_ses->fore_channel, &new->se_fchannel,
  
        /* cache solo and embedded create sessions under the state lock */
        nfsd4_cache_create_session(cr_ses, cs_slot, status);
-       if (confirm_me) {
-               unsigned int hash = clientstr_hashval(unconf->cl_recdir);
-               struct nfs4_client *old =
-                       find_confirmed_client_by_str(conf->cl_recdir, hash);
-               if (old)
-                       expire_client(old);
-               move_to_confirmed(conf);
-       }
  out:
        nfs4_unlock_state();
        dprintk("%s returns %d\n", __func__, ntohl(status));
        return status;
+ out_free_conn:
+       free_conn(conn);
+ out_free_session:
+       __free_session(new);
+       goto out;
  }
  
  static bool nfsd4_last_compound_op(struct svc_rqst *rqstp)
@@@ -1854,6 -1870,7 +1870,7 @@@ __be32 nfsd4_bind_conn_to_session(struc
                     struct nfsd4_bind_conn_to_session *bcts)
  {
        __be32 status;
+       struct nfsd4_conn *conn;
  
        if (!nfsd4_last_compound_op(rqstp))
                return nfserr_not_only_op;
                return nfserr_badsession;
  
        status = nfsd4_map_bcts_dir(&bcts->dir);
-       if (!status)
-               nfsd4_new_conn(rqstp, cstate->session, bcts->dir);
-       return status;
+       if (status)
+               return status;
+       conn = alloc_conn(rqstp, bcts->dir);
+       if (!conn)
+               return nfserr_jukebox;
+       nfsd4_init_conn(rqstp, conn, cstate->session);
+       return nfs_ok;
  }
  
  static bool nfsd4_compound_in_session(struct nfsd4_session *session, struct nfs4_sessionid *sid)
@@@ -2085,8 -2106,8 +2106,8 @@@ nfsd4_destroy_clientid(struct svc_rqst 
        __be32 status = 0;
  
        nfs4_lock_state();
-       unconf = find_unconfirmed_client(&dc->clientid);
-       conf = find_confirmed_client(&dc->clientid);
+       unconf = find_unconfirmed_client(&dc->clientid, true);
+       conf = find_confirmed_client(&dc->clientid, true);
  
        if (conf) {
                clp = conf;
@@@ -2200,10 -2221,6 +2221,6 @@@ nfsd4_setclientid(struct svc_rqst *rqst
                copy_clid(new, conf);
        else /* case 4 (new client) or cases 2, 3 (client reboot): */
                gen_clid(new);
-       /*
-        * XXX: we should probably set this at creation time, and check
-        * for consistent minorversion use throughout:
-        */
        new->cl_minorversion = 0;
        gen_callback(new, setclid, rqstp);
        add_to_unconfirmed(new, strhashval);
@@@ -2232,8 -2249,8 +2249,8 @@@ nfsd4_setclientid_confirm(struct svc_rq
                return nfserr_stale_clientid;
        nfs4_lock_state();
  
-       conf = find_confirmed_client(clid);
-       unconf = find_unconfirmed_client(clid);
+       conf = find_confirmed_client(clid, false);
+       unconf = find_unconfirmed_client(clid, false);
        /*
         * We try hard to give out unique clientid's, so if we get an
         * attempt to confirm the same clientid with a different cred,
                unsigned int hash = clientstr_hashval(unconf->cl_recdir);
  
                conf = find_confirmed_client_by_str(unconf->cl_recdir, hash);
-               if (conf) {
-                       nfsd4_client_record_remove(conf);
+               if (conf)
                        expire_client(conf);
-               }
                move_to_confirmed(unconf);
                nfsd4_probe_callback(unconf);
        }
@@@ -2447,16 -2462,20 +2462,20 @@@ same_owner_str(struct nfs4_stateowner *
  }
  
  static struct nfs4_openowner *
- find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open)
+ find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, bool sessions)
  {
        struct nfs4_stateowner *so;
        struct nfs4_openowner *oo;
+       struct nfs4_client *clp;
  
        list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) {
                if (!so->so_is_open_owner)
                        continue;
                if (same_owner_str(so, &open->op_owner, &open->op_clientid)) {
                        oo = openowner(so);
+                       clp = oo->oo_owner.so_client;
+                       if ((bool)clp->cl_minorversion != sessions)
+                               return NULL;
                        renew_client(oo->oo_owner.so_client);
                        return oo;
                }
@@@ -2600,10 -2619,10 +2619,10 @@@ nfsd4_process_open1(struct nfsd4_compou
                return nfserr_jukebox;
  
        strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner);
-       oo = find_openstateowner_str(strhashval, open);
+       oo = find_openstateowner_str(strhashval, open, cstate->minorversion);
        open->op_openowner = oo;
        if (!oo) {
-               clp = find_confirmed_client(clientid);
+               clp = find_confirmed_client(clientid, cstate->minorversion);
                if (clp == NULL)
                        return nfserr_expired;
                goto new_owner;
@@@ -2705,11 -2724,6 +2724,6 @@@ nfs4_check_open(struct nfs4_file *fp, s
        return nfs_ok;
  }
  
- static void nfs4_free_stateid(struct nfs4_ol_stateid *s)
- {
-       kmem_cache_free(stateid_slab, s);
- }
  static inline int nfs4_access_to_access(u32 nfs4_access)
  {
        int flags = 0;
@@@ -2837,7 -2851,8 +2851,7 @@@ static int nfs4_setlease(struct nfs4_de
                return -ENOMEM;
        }
        fp->fi_lease = fl;
 -      fp->fi_deleg_file = fl->fl_file;
 -      get_file(fp->fi_deleg_file);
 +      fp->fi_deleg_file = get_file(fl->fl_file);
        atomic_set(&fp->fi_delegees, 1);
        list_add(&dp->dl_perfile, &fp->fi_delegations);
        return 0;
@@@ -3087,7 -3102,7 +3101,7 @@@ void nfsd4_cleanup_open_state(struct nf
        if (open->op_file)
                nfsd4_free_file(open->op_file);
        if (open->op_stp)
-               nfs4_free_stateid(open->op_stp);
+               free_generic_stateid(open->op_stp);
  }
  
  __be32
@@@ -3104,7 -3119,7 +3118,7 @@@ nfsd4_renew(struct svc_rqst *rqstp, str
        status = nfserr_stale_clientid;
        if (STALE_CLIENTID(clid, nn))
                goto out;
-       clp = find_confirmed_client(clid);
+       clp = find_confirmed_client(clid, cstate->minorversion);
        status = nfserr_expired;
        if (clp == NULL) {
                /* We assume the client took too long to RENEW. */
@@@ -3180,7 -3195,6 +3194,6 @@@ nfs4_laundromat(void
                clp = list_entry(pos, struct nfs4_client, cl_lru);
                dprintk("NFSD: purging unused client (clientid %08x)\n",
                        clp->cl_clientid.cl_id);
-               nfsd4_client_record_remove(clp);
                expire_client(clp);
        }
        spin_lock(&recall_lock);
@@@ -3372,7 -3386,7 +3385,7 @@@ static __be32 nfsd4_validate_stateid(st
        return nfs_ok;
  }
  
- static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s)
+ static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s, bool sessions)
  {
        struct nfs4_client *cl;
        struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
                return nfserr_bad_stateid;
        if (STALE_STATEID(stateid, nn))
                return nfserr_stale_stateid;
-       cl = find_confirmed_client(&stateid->si_opaque.so_clid);
+       cl = find_confirmed_client(&stateid->si_opaque.so_clid, sessions);
        if (!cl)
                return nfserr_expired;
        *s = find_stateid_by_type(cl, stateid, typemask);
@@@ -3414,7 -3428,7 +3427,7 @@@ nfs4_preprocess_stateid_op(struct net *
        if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
                return check_special_stateids(net, current_fh, stateid, flags);
  
-       status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s);
+       status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s, cstate->minorversion);
        if (status)
                return status;
        status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate));
@@@ -3564,7 -3578,7 +3577,7 @@@ nfs4_preprocess_seqid_op(struct nfsd4_c
                seqid, STATEID_VAL(stateid));
  
        *stpp = NULL;
-       status = nfsd4_lookup_stateid(stateid, typemask, &s);
+       status = nfsd4_lookup_stateid(stateid, typemask, &s, cstate->minorversion);
        if (status)
                return status;
        *stpp = openlockstateid(s);
@@@ -3765,6 -3779,7 +3778,7 @@@ nfsd4_close(struct svc_rqst *rqstp, str
        memcpy(&close->cl_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
  
        nfsd4_close_open_stateid(stp);
+       release_last_closed_stateid(oo);
        oo->oo_last_closed_stid = stp;
  
        if (list_empty(&oo->oo_owner.so_stateids)) {
@@@ -3801,7 -3816,7 +3815,7 @@@ nfsd4_delegreturn(struct svc_rqst *rqst
        inode = cstate->current_fh.fh_dentry->d_inode;
  
        nfs4_lock_state();
-       status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s);
+       status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s, cstate->minorversion);
        if (status)
                goto out;
        dp = delegstateid(s);
@@@ -4045,8 -4060,8 +4059,8 @@@ nfsd4_lock(struct svc_rqst *rqstp, stru
        struct nfs4_lockowner *lock_sop = NULL;
        struct nfs4_ol_stateid *lock_stp;
        struct file *filp = NULL;
-       struct file_lock file_lock;
-       struct file_lock conflock;
+       struct file_lock *file_lock = NULL;
+       struct file_lock *conflock = NULL;
        __be32 status = 0;
        bool new_state = false;
        int lkflg;
        if (!locks_in_grace(SVC_NET(rqstp)) && lock->lk_reclaim)
                goto out;
  
-       locks_init_lock(&file_lock);
+       file_lock = locks_alloc_lock();
+       if (!file_lock) {
+               dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
+               status = nfserr_jukebox;
+               goto out;
+       }
+       locks_init_lock(file_lock);
        switch (lock->lk_type) {
                case NFS4_READ_LT:
                case NFS4_READW_LT:
                        filp = find_readable_file(lock_stp->st_file);
                        if (filp)
                                get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ);
-                       file_lock.fl_type = F_RDLCK;
+                       file_lock->fl_type = F_RDLCK;
                        break;
                case NFS4_WRITE_LT:
                case NFS4_WRITEW_LT:
                        filp = find_writeable_file(lock_stp->st_file);
                        if (filp)
                                get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE);
-                       file_lock.fl_type = F_WRLCK;
+                       file_lock->fl_type = F_WRLCK;
                        break;
                default:
                        status = nfserr_inval;
                status = nfserr_openmode;
                goto out;
        }
-       file_lock.fl_owner = (fl_owner_t)lock_sop;
-       file_lock.fl_pid = current->tgid;
-       file_lock.fl_file = filp;
-       file_lock.fl_flags = FL_POSIX;
-       file_lock.fl_lmops = &nfsd_posix_mng_ops;
-       file_lock.fl_start = lock->lk_offset;
-       file_lock.fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
-       nfs4_transform_lock_offset(&file_lock);
-       /*
-       * Try to lock the file in the VFS.
-       * Note: locks.c uses the BKL to protect the inode's lock list.
-       */
+       file_lock->fl_owner = (fl_owner_t)lock_sop;
+       file_lock->fl_pid = current->tgid;
+       file_lock->fl_file = filp;
+       file_lock->fl_flags = FL_POSIX;
+       file_lock->fl_lmops = &nfsd_posix_mng_ops;
+       file_lock->fl_start = lock->lk_offset;
+       file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
+       nfs4_transform_lock_offset(file_lock);
+       conflock = locks_alloc_lock();
+       if (!conflock) {
+               dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
+               status = nfserr_jukebox;
+               goto out;
+       }
  
-       err = vfs_lock_file(filp, F_SETLK, &file_lock, &conflock);
+       err = vfs_lock_file(filp, F_SETLK, file_lock, conflock);
        switch (-err) {
        case 0: /* success! */
                update_stateid(&lock_stp->st_stid.sc_stateid);
        case (EAGAIN):          /* conflock holds conflicting lock */
                status = nfserr_denied;
                dprintk("NFSD: nfsd4_lock: conflicting lock found!\n");
-               nfs4_set_lock_denied(&conflock, &lock->lk_denied);
+               nfs4_set_lock_denied(conflock, &lock->lk_denied);
                break;
        case (EDEADLK):
                status = nfserr_deadlock;
                release_lockowner(lock_sop);
        if (!cstate->replay_owner)
                nfs4_unlock_state();
+       if (file_lock)
+               locks_free_lock(file_lock);
+       if (conflock)
+               locks_free_lock(conflock);
        return status;
  }
  
@@@ -4209,7 -4236,7 +4235,7 @@@ nfsd4_lockt(struct svc_rqst *rqstp, str
            struct nfsd4_lockt *lockt)
  {
        struct inode *inode;
-       struct file_lock file_lock;
+       struct file_lock *file_lock = NULL;
        struct nfs4_lockowner *lo;
        __be32 status;
        struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
                goto out;
  
        inode = cstate->current_fh.fh_dentry->d_inode;
-       locks_init_lock(&file_lock);
+       file_lock = locks_alloc_lock();
+       if (!file_lock) {
+               dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
+               status = nfserr_jukebox;
+               goto out;
+       }
+       locks_init_lock(file_lock);
        switch (lockt->lt_type) {
                case NFS4_READ_LT:
                case NFS4_READW_LT:
-                       file_lock.fl_type = F_RDLCK;
+                       file_lock->fl_type = F_RDLCK;
                break;
                case NFS4_WRITE_LT:
                case NFS4_WRITEW_LT:
-                       file_lock.fl_type = F_WRLCK;
+                       file_lock->fl_type = F_WRLCK;
                break;
                default:
                        dprintk("NFSD: nfs4_lockt: bad lock type!\n");
  
        lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner);
        if (lo)
-               file_lock.fl_owner = (fl_owner_t)lo;
-       file_lock.fl_pid = current->tgid;
-       file_lock.fl_flags = FL_POSIX;
+               file_lock->fl_owner = (fl_owner_t)lo;
+       file_lock->fl_pid = current->tgid;
+       file_lock->fl_flags = FL_POSIX;
  
-       file_lock.fl_start = lockt->lt_offset;
-       file_lock.fl_end = last_byte_offset(lockt->lt_offset, lockt->lt_length);
+       file_lock->fl_start = lockt->lt_offset;
+       file_lock->fl_end = last_byte_offset(lockt->lt_offset, lockt->lt_length);
  
-       nfs4_transform_lock_offset(&file_lock);
+       nfs4_transform_lock_offset(file_lock);
  
-       status = nfsd_test_lock(rqstp, &cstate->current_fh, &file_lock);
+       status = nfsd_test_lock(rqstp, &cstate->current_fh, file_lock);
        if (status)
                goto out;
  
-       if (file_lock.fl_type != F_UNLCK) {
+       if (file_lock->fl_type != F_UNLCK) {
                status = nfserr_denied;
-               nfs4_set_lock_denied(&file_lock, &lockt->lt_denied);
+               nfs4_set_lock_denied(file_lock, &lockt->lt_denied);
        }
  out:
        nfs4_unlock_state();
+       if (file_lock)
+               locks_free_lock(file_lock);
        return status;
  }
  
@@@ -4276,7 -4311,7 +4310,7 @@@ nfsd4_locku(struct svc_rqst *rqstp, str
  {
        struct nfs4_ol_stateid *stp;
        struct file *filp = NULL;
-       struct file_lock file_lock;
+       struct file_lock *file_lock = NULL;
        __be32 status;
        int err;
                                                        
                status = nfserr_lock_range;
                goto out;
        }
-       BUG_ON(!filp);
-       locks_init_lock(&file_lock);
-       file_lock.fl_type = F_UNLCK;
-       file_lock.fl_owner = (fl_owner_t)lockowner(stp->st_stateowner);
-       file_lock.fl_pid = current->tgid;
-       file_lock.fl_file = filp;
-       file_lock.fl_flags = FL_POSIX; 
-       file_lock.fl_lmops = &nfsd_posix_mng_ops;
-       file_lock.fl_start = locku->lu_offset;
-       file_lock.fl_end = last_byte_offset(locku->lu_offset, locku->lu_length);
-       nfs4_transform_lock_offset(&file_lock);
+       file_lock = locks_alloc_lock();
+       if (!file_lock) {
+               dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
+               status = nfserr_jukebox;
+               goto out;
+       }
+       locks_init_lock(file_lock);
+       file_lock->fl_type = F_UNLCK;
+       file_lock->fl_owner = (fl_owner_t)lockowner(stp->st_stateowner);
+       file_lock->fl_pid = current->tgid;
+       file_lock->fl_file = filp;
+       file_lock->fl_flags = FL_POSIX;
+       file_lock->fl_lmops = &nfsd_posix_mng_ops;
+       file_lock->fl_start = locku->lu_offset;
+       file_lock->fl_end = last_byte_offset(locku->lu_offset,
+                                               locku->lu_length);
+       nfs4_transform_lock_offset(file_lock);
  
        /*
        *  Try to unlock the file in the VFS.
        */
-       err = vfs_lock_file(filp, F_SETLK, &file_lock, NULL);
+       err = vfs_lock_file(filp, F_SETLK, file_lock, NULL);
        if (err) {
                dprintk("NFSD: nfs4_locku: vfs_lock_file failed!\n");
                goto out_nfserr;
  out:
        if (!cstate->replay_owner)
                nfs4_unlock_state();
+       if (file_lock)
+               locks_free_lock(file_lock);
        return status;
  
  out_nfserr:
@@@ -4501,12 -4544,12 +4543,12 @@@ nfsd4_find_reclaim_client(struct nfs4_c
  * Called from OPEN. Look for clientid in reclaim list.
  */
  __be32
- nfs4_check_open_reclaim(clientid_t *clid)
+ nfs4_check_open_reclaim(clientid_t *clid, bool sessions)
  {
        struct nfs4_client *clp;
  
        /* find clientid in conf_id_hashtbl */
-       clp = find_confirmed_client(clid);
+       clp = find_confirmed_client(clid, sessions);
        if (clp == NULL)
                return nfserr_reclaim_bad;
  
@@@ -4522,7 -4565,6 +4564,6 @@@ void nfsd_forget_clients(u64 num
  
        nfs4_lock_state();
        list_for_each_entry_safe(clp, next, &client_lru, cl_lru) {
-               nfsd4_client_record_remove(clp);
                expire_client(clp);
                if (++count == num)
                        break;
@@@ -4582,7 -4624,7 +4623,7 @@@ void nfsd_forget_openowners(u64 num
        printk(KERN_INFO "NFSD: Forgot %d open owners", count);
  }
  
- int nfsd_process_n_delegations(u64 num, struct list_head *list)
static int nfsd_process_n_delegations(u64 num, struct list_head *list)
  {
        int i, count = 0;
        struct nfs4_file *fp, *fnext;
@@@ -4747,11 -4789,11 +4788,11 @@@ __nfs4_state_shutdown(void
        for (i = 0; i < CLIENT_HASH_SIZE; i++) {
                while (!list_empty(&conf_id_hashtbl[i])) {
                        clp = list_entry(conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash);
-                       expire_client(clp);
+                       destroy_client(clp);
                }
                while (!list_empty(&unconf_str_hashtbl[i])) {
                        clp = list_entry(unconf_str_hashtbl[i].next, struct nfs4_client, cl_strhash);
-                       expire_client(clp);
+                       destroy_client(clp);
                }
        }
        INIT_LIST_HEAD(&reaplist);
diff --combined fs/nfsd/vfs.c
index 3f67b8e122515302709e606ef38881ba01aae581,e76a17e003c50549e47c692ae79dcd4dd486f2bb..c120b48ec305bed3b15aec207b1dd2dac57e952b
@@@ -480,7 -480,7 +480,7 @@@ set_nfsv4_acl_one(struct dentry *dentry
        if (buf == NULL)
                goto out;
  
 -      len = posix_acl_to_xattr(pacl, buf, buflen);
 +      len = posix_acl_to_xattr(&init_user_ns, pacl, buf, buflen);
        if (len < 0) {
                error = len;
                goto out;
@@@ -549,7 -549,7 +549,7 @@@ _get_posix_acl(struct dentry *dentry, c
        if (buflen <= 0)
                return ERR_PTR(buflen);
  
 -      pacl = posix_acl_from_xattr(buf, buflen);
 +      pacl = posix_acl_from_xattr(&init_user_ns, buf, buflen);
        kfree(buf);
        return pacl;
  }
@@@ -1581,7 -1581,7 +1581,7 @@@ nfsd_readlink(struct svc_rqst *rqstp, s
         */
  
        oldfs = get_fs(); set_fs(KERNEL_DS);
-       host_err = inode->i_op->readlink(path.dentry, buf, *lenp);
+       host_err = inode->i_op->readlink(path.dentry, (char __user *)buf, *lenp);
        set_fs(oldfs);
  
        if (host_err < 0)
@@@ -2264,7 -2264,7 +2264,7 @@@ nfsd_get_posix_acl(struct svc_fh *fhp, 
        if (size < 0)
                return ERR_PTR(size);
  
 -      acl = posix_acl_from_xattr(value, size);
 +      acl = posix_acl_from_xattr(&init_user_ns, value, size);
        kfree(value);
        return acl;
  }
@@@ -2297,7 -2297,7 +2297,7 @@@ nfsd_set_posix_acl(struct svc_fh *fhp, 
                value = kmalloc(size, GFP_KERNEL);
                if (!value)
                        return -ENOMEM;
 -              error = posix_acl_to_xattr(acl, value, size);
 +              error = posix_acl_to_xattr(&init_user_ns, acl, value, size);
                if (error < 0)
                        goto getout;
                size = error;