Merge nfs containerization work from Trond's tree
authorJ. Bruce Fields <bfields@redhat.com>
Wed, 21 Mar 2012 20:42:14 +0000 (16:42 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Mon, 26 Mar 2012 15:48:54 +0000 (11:48 -0400)
The nfs containerization work is a prerequisite for Jeff Layton's reboot
recovery rework.

1  2 
fs/lockd/svc.c
fs/nfsd/nfs4callback.c
fs/nfsd/nfs4state.c
fs/nfsd/nfssvc.c
include/linux/nfs4.h
net/sunrpc/cache.c
net/sunrpc/svcauth_unix.c
net/sunrpc/svcsock.c

diff --combined fs/lockd/svc.c
index 2444780f5cfa061eaea1c1930646bd6d7ff04ee1,2774e1013b34467acc3c1c6bc55f47fcac8d3ca7..f49b9afc443690a2377db100ed7da33452ef98db
@@@ -35,6 -35,8 +35,8 @@@
  #include <linux/lockd/lockd.h>
  #include <linux/nfs.h>
  
+ #include "netns.h"
  #define NLMDBG_FACILITY               NLMDBG_SVC
  #define LOCKD_BUFSIZE         (1024 + NLMSVC_XDRSIZE)
  #define ALLOWED_SIGS          (sigmask(SIGKILL))
@@@ -50,6 -52,8 +52,8 @@@ static struct task_struct     *nlmsvc_task
  static struct svc_rqst                *nlmsvc_rqst;
  unsigned long                 nlmsvc_timeout;
  
+ int lockd_net_id;
  /*
   * These can be set at insmod time (useful for NFS as root filesystem),
   * and also changed through the sysctl interface.  -- Jamie Lokier, Aug 2003
@@@ -189,27 -193,29 +193,29 @@@ lockd(void *vrqstp
  }
  
  static int create_lockd_listener(struct svc_serv *serv, const char *name,
-                                const int family, const unsigned short port)
+                                struct net *net, const int family,
+                                const unsigned short port)
  {
        struct svc_xprt *xprt;
  
-       xprt = svc_find_xprt(serv, name, family, 0);
+       xprt = svc_find_xprt(serv, name, net, family, 0);
        if (xprt == NULL)
-               return svc_create_xprt(serv, name, &init_net, family, port,
+               return svc_create_xprt(serv, name, net, family, port,
                                                SVC_SOCK_DEFAULTS);
        svc_xprt_put(xprt);
        return 0;
  }
  
- static int create_lockd_family(struct svc_serv *serv, const int family)
+ static int create_lockd_family(struct svc_serv *serv, struct net *net,
+                              const int family)
  {
        int err;
  
-       err = create_lockd_listener(serv, "udp", family, nlm_udpport);
+       err = create_lockd_listener(serv, "udp", net, family, nlm_udpport);
        if (err < 0)
                return err;
  
-       return create_lockd_listener(serv, "tcp", family, nlm_tcpport);
+       return create_lockd_listener(serv, "tcp", net, family, nlm_tcpport);
  }
  
  /*
   * Returns zero if all listeners are available; otherwise a
   * negative errno value is returned.
   */
- static int make_socks(struct svc_serv *serv)
+ static int make_socks(struct svc_serv *serv, struct net *net)
  {
        static int warned;
        int err;
  
-       err = create_lockd_family(serv, PF_INET);
+       err = create_lockd_family(serv, net, PF_INET);
        if (err < 0)
                goto out_err;
  
-       err = create_lockd_family(serv, PF_INET6);
+       err = create_lockd_family(serv, net, PF_INET6);
        if (err < 0 && err != -EAFNOSUPPORT)
                goto out_err;
  
@@@ -245,6 -251,47 +251,47 @@@ out_err
        return err;
  }
  
+ static int lockd_up_net(struct net *net)
+ {
+       struct lockd_net *ln = net_generic(net, lockd_net_id);
+       struct svc_serv *serv = nlmsvc_rqst->rq_server;
+       int error;
+       if (ln->nlmsvc_users)
+               return 0;
+       error = svc_rpcb_setup(serv, net);
+       if (error)
+               goto err_rpcb;
+       error = make_socks(serv, net);
+       if (error < 0)
+               goto err_socks;
+       return 0;
+ err_socks:
+       svc_rpcb_cleanup(serv, net);
+ err_rpcb:
+       return error;
+ }
+ static void lockd_down_net(struct net *net)
+ {
+       struct lockd_net *ln = net_generic(net, lockd_net_id);
+       struct svc_serv *serv = nlmsvc_rqst->rq_server;
+       if (ln->nlmsvc_users) {
+               if (--ln->nlmsvc_users == 0) {
+                       nlm_shutdown_hosts_net(net);
+                       svc_shutdown_net(serv, net);
+               }
+       } else {
+               printk(KERN_ERR "lockd_down_net: no users! task=%p, net=%p\n",
+                               nlmsvc_task, net);
+               BUG();
+       }
+ }
  /*
   * Bring up the lockd process if it's not already up.
   */
@@@ -252,13 -299,16 +299,16 @@@ int lockd_up(void
  {
        struct svc_serv *serv;
        int             error = 0;
+       struct net *net = current->nsproxy->net_ns;
  
        mutex_lock(&nlmsvc_mutex);
        /*
         * Check whether we're already up and running.
         */
-       if (nlmsvc_rqst)
+       if (nlmsvc_rqst) {
+               error = lockd_up_net(net);
                goto out;
+       }
  
        /*
         * Sanity check: if there's no pid,
                goto out;
        }
  
-       error = make_socks(serv);
+       error = make_socks(serv, net);
        if (error < 0)
                goto destroy_and_out;
  
  destroy_and_out:
        svc_destroy(serv);
  out:
-       if (!error)
+       if (!error) {
+               struct lockd_net *ln = net_generic(net, lockd_net_id);
+               ln->nlmsvc_users++;
                nlmsvc_users++;
+       }
        mutex_unlock(&nlmsvc_mutex);
        return error;
  }
@@@ -328,8 -382,10 +382,10 @@@ lockd_down(void
  {
        mutex_lock(&nlmsvc_mutex);
        if (nlmsvc_users) {
-               if (--nlmsvc_users)
+               if (--nlmsvc_users) {
+                       lockd_down_net(current->nsproxy->net_ns);
                        goto out;
+               }
        } else {
                printk(KERN_ERR "lockd_down: no users! task=%p\n",
                        nlmsvc_task);
@@@ -440,7 -496,7 +496,7 @@@ static int param_set_##name(const char 
        __typeof__(type) num = which_strtol(val, &endp, 0);             \
        if (endp == val || *endp || num < (min) || num > (max))         \
                return -EINVAL;                                         \
 -      *((int *) kp->arg) = num;                                       \
 +      *((type *) kp->arg) = num;                                      \
        return 0;                                                       \
  }
  
@@@ -497,24 -553,55 +553,55 @@@ module_param_call(nlm_tcpport, param_se
  module_param(nsm_use_hostnames, bool, 0644);
  module_param(nlm_max_connections, uint, 0644);
  
+ static int lockd_init_net(struct net *net)
+ {
+       return 0;
+ }
+ static void lockd_exit_net(struct net *net)
+ {
+ }
+ static struct pernet_operations lockd_net_ops = {
+       .init = lockd_init_net,
+       .exit = lockd_exit_net,
+       .id = &lockd_net_id,
+       .size = sizeof(struct lockd_net),
+ };
  /*
   * Initialising and terminating the module.
   */
  
  static int __init init_nlm(void)
  {
+       int err;
  #ifdef CONFIG_SYSCTL
+       err = -ENOMEM;
        nlm_sysctl_table = register_sysctl_table(nlm_sysctl_root);
-       return nlm_sysctl_table ? 0 : -ENOMEM;
- #else
+       if (nlm_sysctl_table == NULL)
+               goto err_sysctl;
+ #endif
+       err = register_pernet_subsys(&lockd_net_ops);
+       if (err)
+               goto err_pernet;
        return 0;
+ err_pernet:
+ #ifdef CONFIG_SYSCTL
+       unregister_sysctl_table(nlm_sysctl_table);
  #endif
+ err_sysctl:
+       return err;
  }
  
  static void __exit exit_nlm(void)
  {
        /* FIXME: delete all NLM clients */
        nlm_shutdown_hosts();
+       unregister_pernet_subsys(&lockd_net_ops);
  #ifdef CONFIG_SYSCTL
        unregister_sysctl_table(nlm_sysctl_table);
  #endif
diff --combined fs/nfsd/nfs4callback.c
index 24b6bcf1e945c3a2254123594661e66e53ce3615,0e262f32ac415a577793c74bb8cf6e7cd8d9202f..cf6e4190e41c7963d2f024525b9b3b99ce24728f
@@@ -605,24 -605,24 +605,24 @@@ static struct rpc_version nfs_cb_versio
        .procs                  = nfs4_cb_procedures
  };
  
- static struct rpc_version *nfs_cb_version[] = {
+ static const struct rpc_version *nfs_cb_version[] = {
        &nfs_cb_version4,
  };
  
- static struct rpc_program cb_program;
+ static const struct rpc_program cb_program;
  
  static struct rpc_stat cb_stats = {
        .program                = &cb_program
  };
  
  #define NFS4_CALLBACK 0x40000000
- static struct rpc_program cb_program = {
+ static const struct rpc_program cb_program = {
        .name                   = "nfs4_cb",
        .number                 = NFS4_CALLBACK,
        .nrvers                 = ARRAY_SIZE(nfs_cb_version),
        .version                = nfs_cb_version,
        .stats                  = &cb_stats,
-       .pipe_dir_name          = "/nfsd4_cb",
+       .pipe_dir_name          = "nfsd4_cb",
  };
  
  static int max_cb_time(void)
@@@ -986,7 -986,7 +986,7 @@@ static void nfsd4_process_cb_update(str
  
        err = setup_callback_client(clp, &conn, ses);
        if (err) {
 -              warn_no_callback_path(clp, err);
 +              nfsd4_mark_cb_down(clp, err);
                return;
        }
        /* Yay, the callback channel's back! Restart any callbacks: */
diff --combined fs/nfsd/nfs4state.c
index e318964d8acae27fc37adf40f54009f66eabf65a,c5cddd659429f33b371ea03a3d920808911ce8f1..a0a2b535b0e0f82f5e88d6a204fb584e2eefa563
@@@ -58,15 -58,11 +58,15 @@@ static const stateid_t one_stateid = 
  static const stateid_t zero_stateid = {
        /* all fields zero */
  };
 +static const stateid_t currentstateid = {
 +      .si_generation = 1,
 +};
  
  static u64 current_sessionid = 1;
  
  #define ZERO_STATEID(stateid) (!memcmp((stateid), &zero_stateid, sizeof(stateid_t)))
  #define ONE_STATEID(stateid)  (!memcmp((stateid), &one_stateid, sizeof(stateid_t)))
 +#define CURRENT_STATEID(stateid) (!memcmp((stateid), &currentstateid, sizeof(stateid_t)))
  
  /* forward declarations */
  static int check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner);
@@@ -95,19 -91,6 +95,19 @@@ nfs4_lock_state(void
        mutex_lock(&client_mutex);
  }
  
 +static void free_session(struct kref *);
 +
 +/* Must be called under the client_lock */
 +static void nfsd4_put_session_locked(struct nfsd4_session *ses)
 +{
 +      kref_put(&ses->se_ref, free_session);
 +}
 +
 +static void nfsd4_get_session(struct nfsd4_session *ses)
 +{
 +      kref_get(&ses->se_ref);
 +}
 +
  void
  nfs4_unlock_state(void)
  {
@@@ -622,20 -605,12 +622,20 @@@ hash_sessionid(struct nfs4_sessionid *s
        return sid->sequence % SESSION_HASH_SIZE;
  }
  
 +#ifdef NFSD_DEBUG
  static inline void
  dump_sessionid(const char *fn, struct nfs4_sessionid *sessionid)
  {
        u32 *ptr = (u32 *)(&sessionid->data[0]);
        dprintk("%s: %u:%u:%u:%u\n", fn, ptr[0], ptr[1], ptr[2], ptr[3]);
  }
 +#else
 +static inline void
 +dump_sessionid(const char *fn, struct nfs4_sessionid *sessionid)
 +{
 +}
 +#endif
 +
  
  static void
  gen_sessionid(struct nfsd4_session *ses)
@@@ -857,12 -832,11 +857,12 @@@ static void nfsd4_del_conns(struct nfsd
        spin_unlock(&clp->cl_lock);
  }
  
 -void free_session(struct kref *kref)
 +static void free_session(struct kref *kref)
  {
        struct nfsd4_session *ses;
        int mem;
  
 +      BUG_ON(!spin_is_locked(&client_lock));
        ses = container_of(kref, struct nfsd4_session, se_ref);
        nfsd4_del_conns(ses);
        spin_lock(&nfsd_drc_lock);
        kfree(ses);
  }
  
 +void nfsd4_put_session(struct nfsd4_session *ses)
 +{
 +      spin_lock(&client_lock);
 +      nfsd4_put_session_locked(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)
  {
        struct nfsd4_session *new;
        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) {
@@@ -1041,13 -1006,12 +1041,13 @@@ static struct nfs4_client *alloc_client
  static inline void
  free_client(struct nfs4_client *clp)
  {
 +      BUG_ON(!spin_is_locked(&client_lock));
        while (!list_empty(&clp->cl_sessions)) {
                struct nfsd4_session *ses;
                ses = list_entry(clp->cl_sessions.next, struct nfsd4_session,
                                se_perclnt);
                list_del(&ses->se_perclnt);
 -              nfsd4_put_session(ses);
 +              nfsd4_put_session_locked(ses);
        }
        if (clp->cl_cred.cr_group_info)
                put_group_info(clp->cl_cred.cr_group_info);
@@@ -1174,12 -1138,12 +1174,12 @@@ static void gen_clid(struct nfs4_clien
  
  static void gen_confirm(struct nfs4_client *clp)
  {
 +      __be32 verf[2];
        static u32 i;
 -      u32 *p;
  
 -      p = (u32 *)clp->cl_confirm.data;
 -      *p++ = get_seconds();
 -      *p++ = i++;
 +      verf[0] = (__be32)get_seconds();
 +      verf[1] = (__be32)i++;
 +      memcpy(clp->cl_confirm.data, verf, sizeof(clp->cl_confirm.data));
  }
  
  static struct nfs4_stid *find_stateid(struct nfs4_client *cl, stateid_t *t)
@@@ -1216,9 -1180,7 +1216,9 @@@ static struct nfs4_client *create_clien
        if (princ) {
                clp->cl_principal = kstrdup(princ, GFP_KERNEL);
                if (clp->cl_principal == NULL) {
 +                      spin_lock(&client_lock);
                        free_client(clp);
 +                      spin_unlock(&client_lock);
                        return NULL;
                }
        }
@@@ -1346,7 -1308,7 +1346,7 @@@ gen_callback(struct nfs4_client *clp, s
        else
                goto out_err;
  
-       conn->cb_addrlen = rpc_uaddr2sockaddr(se->se_callback_addr_val,
+       conn->cb_addrlen = rpc_uaddr2sockaddr(&init_net, se->se_callback_addr_val,
                                            se->se_callback_addr_len,
                                            (struct sockaddr *)&conn->cb_addr,
                                            sizeof(conn->cb_addr));
@@@ -1385,7 -1347,6 +1385,7 @@@ nfsd4_store_cache_entry(struct nfsd4_co
        slot->sl_opcnt = resp->opcnt;
        slot->sl_status = resp->cstate.status;
  
 +      slot->sl_flags |= NFSD4_SLOT_INITIALIZED;
        if (nfsd4_not_cached(resp)) {
                slot->sl_datalen = 0;
                return;
@@@ -1413,12 -1374,15 +1413,12 @@@ nfsd4_enc_sequence_replay(struct nfsd4_
        struct nfsd4_op *op;
        struct nfsd4_slot *slot = resp->cstate.slot;
  
 -      dprintk("--> %s resp->opcnt %d cachethis %u \n", __func__,
 -              resp->opcnt, resp->cstate.slot->sl_cachethis);
 -
        /* Encode the replayed sequence operation */
        op = &args->ops[resp->opcnt - 1];
        nfsd4_encode_operation(resp, op);
  
        /* Return nfserr_retry_uncached_rep in next operation. */
 -      if (args->opcnt > 1 && slot->sl_cachethis == 0) {
 +      if (args->opcnt > 1 && !(slot->sl_flags & NFSD4_SLOT_CACHETHIS)) {
                op = &args->ops[resp->opcnt++];
                op->status = nfserr_retry_uncached_rep;
                nfsd4_encode_operation(resp, op);
@@@ -1611,11 -1575,16 +1611,11 @@@ check_slot_seqid(u32 seqid, u32 slot_se
                else
                        return nfserr_seq_misordered;
        }
 -      /* Normal */
 +      /* Note unsigned 32-bit arithmetic handles wraparound: */
        if (likely(seqid == slot_seqid + 1))
                return nfs_ok;
 -      /* Replay */
        if (seqid == slot_seqid)
                return nfserr_replay_cache;
 -      /* Wraparound */
 -      if (seqid == 1 && (slot_seqid + 1) == 0)
 -              return nfs_ok;
 -      /* Misordered replay or misordered new request */
        return nfserr_seq_misordered;
  }
  
@@@ -1846,10 -1815,9 +1846,10 @@@ nfsd4_destroy_session(struct svc_rqst *
        nfsd4_probe_callback_sync(ses->se_client);
        nfs4_unlock_state();
  
 +      spin_lock(&client_lock);
        nfsd4_del_conns(ses);
 -
 -      nfsd4_put_session(ses);
 +      nfsd4_put_session_locked(ses);
 +      spin_unlock(&client_lock);
        status = nfs_ok;
  out:
        dprintk("%s returns %d\n", __func__, ntohl(status));
@@@ -1953,12 -1921,8 +1953,12 @@@ nfsd4_sequence(struct svc_rqst *rqstp
         * sr_highest_slotid and the sr_target_slot id to maxslots */
        seq->maxslots = session->se_fchannel.maxreqs;
  
 -      status = check_slot_seqid(seq->seqid, slot->sl_seqid, slot->sl_inuse);
 +      status = check_slot_seqid(seq->seqid, slot->sl_seqid,
 +                                      slot->sl_flags & NFSD4_SLOT_INUSE);
        if (status == nfserr_replay_cache) {
 +              status = nfserr_seq_misordered;
 +              if (!(slot->sl_flags & NFSD4_SLOT_INITIALIZED))
 +                      goto out;
                cstate->slot = slot;
                cstate->session = session;
                /* Return the cached reply status and set cstate->status
        conn = NULL;
  
        /* Success! bump slot seqid */
 -      slot->sl_inuse = true;
        slot->sl_seqid = seq->seqid;
 -      slot->sl_cachethis = seq->cachethis;
 +      slot->sl_flags |= NFSD4_SLOT_INUSE;
 +      if (seq->cachethis)
 +              slot->sl_flags |= NFSD4_SLOT_CACHETHIS;
 +      else
 +              slot->sl_flags &= ~NFSD4_SLOT_CACHETHIS;
  
        cstate->slot = slot;
        cstate->session = session;
@@@ -2672,6 -2633,8 +2672,6 @@@ nfs4_check_delegmode(struct nfs4_delega
  
  static int share_access_to_flags(u32 share_access)
  {
 -      share_access &= ~NFS4_SHARE_WANT_MASK;
 -
        return share_access == NFS4_SHARE_ACCESS_READ ? RD_STATE : WR_STATE;
  }
  
@@@ -2813,15 -2776,10 +2813,15 @@@ nfs4_upgrade_open(struct svc_rqst *rqst
  
  
  static void
 -nfs4_set_claim_prev(struct nfsd4_open *open)
 +nfs4_set_claim_prev(struct nfsd4_open *open, bool has_session)
  {
        open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
 -      open->op_openowner->oo_owner.so_client->cl_firststate = 1;
 +      /*
 +       * On a 4.1+ client, we don't create a state record for a client
 +       * until it performs RECLAIM_COMPLETE:
 +       */
 +      if (!has_session)
 +              open->op_openowner->oo_owner.so_client->cl_firststate = 1;
  }
  
  /* Should we give out recallable state?: */
@@@ -2897,27 -2855,6 +2897,27 @@@ static int nfs4_set_delegation(struct n
        return 0;
  }
  
 +static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status)
 +{
 +      open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE_EXT;
 +      if (status == -EAGAIN)
 +              open->op_why_no_deleg = WND4_CONTENTION;
 +      else {
 +              open->op_why_no_deleg = WND4_RESOURCE;
 +              switch (open->op_deleg_want) {
 +              case NFS4_SHARE_WANT_READ_DELEG:
 +              case NFS4_SHARE_WANT_WRITE_DELEG:
 +              case NFS4_SHARE_WANT_ANY_DELEG:
 +                      break;
 +              case NFS4_SHARE_WANT_CANCEL:
 +                      open->op_why_no_deleg = WND4_CANCELLED;
 +                      break;
 +              case NFS4_SHARE_WANT_NO_DELEG:
 +                      BUG();  /* not supposed to get here */
 +              }
 +      }
 +}
 +
  /*
   * Attempt to hand out a delegation.
   */
@@@ -2927,7 -2864,7 +2927,7 @@@ nfs4_open_delegation(struct svc_fh *fh
        struct nfs4_delegation *dp;
        struct nfs4_openowner *oo = container_of(stp->st_stateowner, struct nfs4_openowner, oo_owner);
        int cb_up;
 -      int status, flag = 0;
 +      int status = 0, flag = 0;
  
        cb_up = nfsd4_cb_channel_good(oo->oo_owner.so_client);
        flag = NFS4_OPEN_DELEGATE_NONE;
        dprintk("NFSD: delegation stateid=" STATEID_FMT "\n",
                STATEID_VAL(&dp->dl_stid.sc_stateid));
  out:
 -      if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS
 -                      && flag == NFS4_OPEN_DELEGATE_NONE
 -                      && open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE)
 -              dprintk("NFSD: WARNING: refusing delegation reclaim\n");
        open->op_delegate_type = flag;
 +      if (flag == NFS4_OPEN_DELEGATE_NONE) {
 +              if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS &&
 +                  open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE)
 +                      dprintk("NFSD: WARNING: refusing delegation reclaim\n");
 +
 +              /* 4.1 client asking for a delegation? */
 +              if (open->op_deleg_want)
 +                      nfsd4_open_deleg_none_ext(open, status);
 +      }
        return;
  out_free:
        nfs4_put_delegation(dp);
@@@ -2986,24 -2918,6 +2986,24 @@@ out_no_deleg
        goto out;
  }
  
 +static void nfsd4_deleg_xgrade_none_ext(struct nfsd4_open *open,
 +                                      struct nfs4_delegation *dp)
 +{
 +      if (open->op_deleg_want == NFS4_SHARE_WANT_READ_DELEG &&
 +          dp->dl_type == NFS4_OPEN_DELEGATE_WRITE) {
 +              open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE_EXT;
 +              open->op_why_no_deleg = WND4_NOT_SUPP_DOWNGRADE;
 +      } else if (open->op_deleg_want == NFS4_SHARE_WANT_WRITE_DELEG &&
 +                 dp->dl_type == NFS4_OPEN_DELEGATE_WRITE) {
 +              open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE_EXT;
 +              open->op_why_no_deleg = WND4_NOT_SUPP_UPGRADE;
 +      }
 +      /* Otherwise the client must be confused wanting a delegation
 +       * it already has, therefore we don't return
 +       * NFS4_OPEN_DELEGATE_NONE_EXT and reason.
 +       */
 +}
 +
  /*
   * called with nfs4_lock_state() held.
   */
@@@ -3065,36 -2979,24 +3065,36 @@@ nfsd4_process_open2(struct svc_rqst *rq
        update_stateid(&stp->st_stid.sc_stateid);
        memcpy(&open->op_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
  
 -      if (nfsd4_has_session(&resp->cstate))
 +      if (nfsd4_has_session(&resp->cstate)) {
                open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
  
 +              if (open->op_deleg_want & NFS4_SHARE_WANT_NO_DELEG) {
 +                      open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE_EXT;
 +                      open->op_why_no_deleg = WND4_NOT_WANTED;
 +                      goto nodeleg;
 +              }
 +      }
 +
        /*
        * Attempt to hand out a delegation. No error return, because the
        * OPEN succeeds even if we fail.
        */
        nfs4_open_delegation(current_fh, open, stp);
 -
 +nodeleg:
        status = nfs_ok;
  
        dprintk("%s: stateid=" STATEID_FMT "\n", __func__,
                STATEID_VAL(&stp->st_stid.sc_stateid));
  out:
 +      /* 4.1 client trying to upgrade/downgrade delegation? */
 +      if (open->op_delegate_type == NFS4_OPEN_DELEGATE_NONE && dp &&
 +          open->op_deleg_want)
 +              nfsd4_deleg_xgrade_none_ext(open, dp);
 +
        if (fp)
                put_nfs4_file(fp);
        if (status == 0 && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
 -              nfs4_set_claim_prev(open);
 +              nfs4_set_claim_prev(open, nfsd4_has_session(&resp->cstate));
        /*
        * To finish the open response, we just need to set the rflags.
        */
@@@ -3498,14 -3400,7 +3498,14 @@@ __be3
  nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                   struct nfsd4_test_stateid *test_stateid)
  {
 -      /* real work is done during encoding */
 +      struct nfsd4_test_stateid_id *stateid;
 +      struct nfs4_client *cl = cstate->session->se_client;
 +
 +      nfs4_lock_state();
 +      list_for_each_entry(stateid, &test_stateid->ts_stateid_list, ts_id_list)
 +              stateid->ts_id_status = nfs4_validate_stateid(cl, &stateid->ts_id_stateid);
 +      nfs4_unlock_state();
 +
        return nfs_ok;
  }
  
@@@ -3701,9 -3596,7 +3701,9 @@@ nfsd4_open_downgrade(struct svc_rqst *r
                        cstate->current_fh.fh_dentry->d_name.name);
  
        /* We don't yet support WANT bits: */
 -      od->od_share_access &= NFS4_SHARE_ACCESS_MASK;
 +      if (od->od_deleg_want)
 +              dprintk("NFSD: %s: od_deleg_want=0x%x ignored\n", __func__,
 +                      od->od_deleg_want);
  
        nfs4_lock_state();
        status = nfs4_preprocess_confirmed_seqid_op(cstate, od->od_seqid,
@@@ -4460,9 -4353,7 +4460,9 @@@ nfs4_has_reclaimed_state(const char *na
        struct nfs4_client *clp;
  
        clp = find_confirmed_client_by_str(name, strhashval);
 -      return clp ? 1 : 0;
 +      if (!clp)
 +              return 0;
 +      return clp->cl_firststate;
  }
  
  /*
@@@ -4722,26 -4613,21 +4722,26 @@@ set_max_delegations(void
  
  /* initialization to perform when the nfsd service is started: */
  
 -static int
 -__nfs4_state_start(void)
 +int
 +nfs4_state_start(void)
  {
        int ret;
  
 +      nfsd4_load_reboot_recovery_data();
        boot_time = get_seconds();
        locks_start_grace(&nfsd4_manager);
        printk(KERN_INFO "NFSD: starting %ld-second grace period\n",
               nfsd4_grace);
        ret = set_callback_cred();
 -      if (ret)
 -              return -ENOMEM;
 +      if (ret) {
 +              ret = -ENOMEM;
 +              goto out_recovery;
 +      }
        laundry_wq = create_singlethread_workqueue("nfsd4");
 -      if (laundry_wq == NULL)
 -              return -ENOMEM;
 +      if (laundry_wq == NULL) {
 +              ret = -ENOMEM;
 +              goto out_recovery;
 +      }
        ret = nfsd4_create_callback_queue();
        if (ret)
                goto out_free_laundry;
        return 0;
  out_free_laundry:
        destroy_workqueue(laundry_wq);
 +out_recovery:
 +      nfs4_release_reclaim();
 +      nfsd4_shutdown_recdir();
        return ret;
  }
  
 -int
 -nfs4_state_start(void)
 -{
 -      nfsd4_load_reboot_recovery_data();
 -      return __nfs4_state_start();
 -}
 -
  static void
  __nfs4_state_shutdown(void)
  {
@@@ -4801,104 -4691,3 +4801,104 @@@ nfs4_state_shutdown(void
        nfs4_unlock_state();
        nfsd4_destroy_callback_queue();
  }
 +
 +static void
 +get_stateid(struct nfsd4_compound_state *cstate, stateid_t *stateid)
 +{
 +      if (HAS_STATE_ID(cstate, CURRENT_STATE_ID_FLAG) && CURRENT_STATEID(stateid))
 +              memcpy(stateid, &cstate->current_stateid, sizeof(stateid_t));
 +}
 +
 +static void
 +put_stateid(struct nfsd4_compound_state *cstate, stateid_t *stateid)
 +{
 +      if (cstate->minorversion) {
 +              memcpy(&cstate->current_stateid, stateid, sizeof(stateid_t));
 +              SET_STATE_ID(cstate, CURRENT_STATE_ID_FLAG);
 +      }
 +}
 +
 +void
 +clear_current_stateid(struct nfsd4_compound_state *cstate)
 +{
 +      CLEAR_STATE_ID(cstate, CURRENT_STATE_ID_FLAG);
 +}
 +
 +/*
 + * functions to set current state id
 + */
 +void
 +nfsd4_set_opendowngradestateid(struct nfsd4_compound_state *cstate, struct nfsd4_open_downgrade *odp)
 +{
 +      put_stateid(cstate, &odp->od_stateid);
 +}
 +
 +void
 +nfsd4_set_openstateid(struct nfsd4_compound_state *cstate, struct nfsd4_open *open)
 +{
 +      put_stateid(cstate, &open->op_stateid);
 +}
 +
 +void
 +nfsd4_set_closestateid(struct nfsd4_compound_state *cstate, struct nfsd4_close *close)
 +{
 +      put_stateid(cstate, &close->cl_stateid);
 +}
 +
 +void
 +nfsd4_set_lockstateid(struct nfsd4_compound_state *cstate, struct nfsd4_lock *lock)
 +{
 +      put_stateid(cstate, &lock->lk_resp_stateid);
 +}
 +
 +/*
 + * functions to consume current state id
 + */
 +
 +void
 +nfsd4_get_opendowngradestateid(struct nfsd4_compound_state *cstate, struct nfsd4_open_downgrade *odp)
 +{
 +      get_stateid(cstate, &odp->od_stateid);
 +}
 +
 +void
 +nfsd4_get_delegreturnstateid(struct nfsd4_compound_state *cstate, struct nfsd4_delegreturn *drp)
 +{
 +      get_stateid(cstate, &drp->dr_stateid);
 +}
 +
 +void
 +nfsd4_get_freestateid(struct nfsd4_compound_state *cstate, struct nfsd4_free_stateid *fsp)
 +{
 +      get_stateid(cstate, &fsp->fr_stateid);
 +}
 +
 +void
 +nfsd4_get_setattrstateid(struct nfsd4_compound_state *cstate, struct nfsd4_setattr *setattr)
 +{
 +      get_stateid(cstate, &setattr->sa_stateid);
 +}
 +
 +void
 +nfsd4_get_closestateid(struct nfsd4_compound_state *cstate, struct nfsd4_close *close)
 +{
 +      get_stateid(cstate, &close->cl_stateid);
 +}
 +
 +void
 +nfsd4_get_lockustateid(struct nfsd4_compound_state *cstate, struct nfsd4_locku *locku)
 +{
 +      get_stateid(cstate, &locku->lu_stateid);
 +}
 +
 +void
 +nfsd4_get_readstateid(struct nfsd4_compound_state *cstate, struct nfsd4_read *read)
 +{
 +      get_stateid(cstate, &read->rd_stateid);
 +}
 +
 +void
 +nfsd4_get_writestateid(struct nfsd4_compound_state *cstate, struct nfsd4_write *write)
 +{
 +      get_stateid(cstate, &write->wr_stateid);
 +}
diff --combined fs/nfsd/nfssvc.c
index aacf1f4b9fb5331a12638f60a49dc04858cfbc01,fce472f5f39e74f2fb9ab36bdf70019f573a73af..28dfad39f0c50a626384c4363955e2b9d7e3212f
@@@ -251,13 -251,13 +251,13 @@@ static void nfsd_shutdown(void
        nfsd_up = false;
  }
  
- static void nfsd_last_thread(struct svc_serv *serv)
+ static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
  {
        /* When last nfsd thread exits we need to do some clean-up */
        nfsd_serv = NULL;
        nfsd_shutdown();
  
-       svc_rpcb_cleanup(serv);
+       svc_rpcb_cleanup(serv, net);
  
        printk(KERN_WARNING "nfsd: last server has exited, flushing export "
                            "cache\n");
@@@ -307,37 -307,33 +307,37 @@@ static void set_max_drc(void
        dprintk("%s nfsd_drc_max_mem %u \n", __func__, nfsd_drc_max_mem);
  }
  
 -int nfsd_create_serv(void)
 +static int nfsd_get_default_max_blksize(void)
  {
 -      int err = 0;
 +      struct sysinfo i;
 +      unsigned long long target;
 +      unsigned long ret;
  
 +      si_meminfo(&i);
 +      target = (i.totalram - i.totalhigh) << PAGE_SHIFT;
 +      /*
 +       * Aim for 1/4096 of memory per thread This gives 1MB on 4Gig
 +       * machines, but only uses 32K on 128M machines.  Bottom out at
 +       * 8K on 32M and smaller.  Of course, this is only a default.
 +       */
 +      target >>= 12;
 +
 +      ret = NFSSVC_MAXBLKSIZE;
 +      while (ret > target && ret >= 8*1024*2)
 +              ret /= 2;
 +      return ret;
 +}
 +
 +int nfsd_create_serv(void)
 +{
        WARN_ON(!mutex_is_locked(&nfsd_mutex));
        if (nfsd_serv) {
                svc_get(nfsd_serv);
                return 0;
        }
 -      if (nfsd_max_blksize == 0) {
 -              /* choose a suitable default */
 -              struct sysinfo i;
 -              si_meminfo(&i);
 -              /* Aim for 1/4096 of memory per thread
 -               * This gives 1MB on 4Gig machines
 -               * But only uses 32K on 128M machines.
 -               * Bottom out at 8K on 32M and smaller.
 -               * Of course, this is only a default.
 -               */
 -              nfsd_max_blksize = NFSSVC_MAXBLKSIZE;
 -              i.totalram <<= PAGE_SHIFT - 12;
 -              while (nfsd_max_blksize > i.totalram &&
 -                     nfsd_max_blksize >= 8*1024*2)
 -                      nfsd_max_blksize /= 2;
 -      }
 +      if (nfsd_max_blksize == 0)
 +              nfsd_max_blksize = nfsd_get_default_max_blksize();
        nfsd_reset_versions();
 -
        nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize,
                                      nfsd_last_thread, nfsd, THIS_MODULE);
        if (nfsd_serv == NULL)
  
        set_max_drc();
        do_gettimeofday(&nfssvc_boot);          /* record boot time */
 -      return err;
 +      return 0;
  }
  
  int nfsd_nrpools(void)
diff --combined include/linux/nfs4.h
index 8cdde4d1fad86a9a3446bdf73e59550f5f87d251,834df8bf08b6e54951bc6483f0ffecbc47c7bd28..0987146b0637a1fd1f9a4ea03fb7040b1d4314cd
@@@ -183,15 -183,12 +183,12 @@@ struct nfs4_acl 
  
  typedef struct { char data[NFS4_VERIFIER_SIZE]; } nfs4_verifier;
  
- struct nfs41_stateid {
+ struct nfs_stateid4 {
        __be32 seqid;
        char other[NFS4_STATEID_OTHER_SIZE];
  } __attribute__ ((packed));
  
- typedef union {
-       char data[NFS4_STATEID_SIZE];
-       struct nfs41_stateid stateid;
- } nfs4_stateid;
+ typedef struct nfs_stateid4 nfs4_stateid;
  
  enum nfs_opnum4 {
        OP_ACCESS = 3,
@@@ -441,20 -438,7 +438,20 @@@ enum limit_by4 
  enum open_delegation_type4 {
        NFS4_OPEN_DELEGATE_NONE = 0,
        NFS4_OPEN_DELEGATE_READ = 1,
 -      NFS4_OPEN_DELEGATE_WRITE = 2
 +      NFS4_OPEN_DELEGATE_WRITE = 2,
 +      NFS4_OPEN_DELEGATE_NONE_EXT = 3, /* 4.1 */
 +};
 +
 +enum why_no_delegation4 { /* new to v4.1 */
 +      WND4_NOT_WANTED = 0,
 +      WND4_CONTENTION = 1,
 +      WND4_RESOURCE = 2,
 +      WND4_NOT_SUPP_FTYPE = 3,
 +      WND4_WRITE_DELEG_NOT_SUPP_FTYPE = 4,
 +      WND4_NOT_SUPP_UPGRADE = 5,
 +      WND4_NOT_SUPP_DOWNGRADE = 6,
 +      WND4_CANCELLED = 7,
 +      WND4_IS_DIR = 8,
  };
  
  enum lock_type4 {
diff --combined net/sunrpc/cache.c
index 8c6598e0334a0347ba29909b992a72cd9f126c31,f21ece08876440d574dad1ac6a09cf22d40d043e..de0b0f39d9d85430f8c7f3af17884473b133120e
@@@ -344,7 -344,7 +344,7 @@@ static int current_index
  static void do_cache_clean(struct work_struct *work);
  static struct delayed_work cache_cleaner;
  
static void sunrpc_init_cache_detail(struct cache_detail *cd)
+ void sunrpc_init_cache_detail(struct cache_detail *cd)
  {
        rwlock_init(&cd->hash_lock);
        INIT_LIST_HEAD(&cd->queue);
        /* start the cleaning process */
        schedule_delayed_work(&cache_cleaner, 0);
  }
+ EXPORT_SYMBOL_GPL(sunrpc_init_cache_detail);
  
static void sunrpc_destroy_cache_detail(struct cache_detail *cd)
+ void sunrpc_destroy_cache_detail(struct cache_detail *cd)
  {
        cache_purge(cd);
        spin_lock(&cache_list_lock);
  out:
        printk(KERN_ERR "nfsd: failed to unregister %s cache\n", cd->name);
  }
+ EXPORT_SYMBOL_GPL(sunrpc_destroy_cache_detail);
  
  /* clean cache tries to find something to clean
   * and cleans it.
@@@ -828,8 -830,6 +830,8 @@@ static ssize_t cache_do_downcall(char *
  {
        ssize_t ret;
  
 +      if (count == 0)
 +              return -EINVAL;
        if (copy_from_user(kaddr, buf, count))
                return -EFAULT;
        kaddr[count] = '\0';
@@@ -1645,12 -1645,6 +1647,6 @@@ int cache_register_net(struct cache_det
  }
  EXPORT_SYMBOL_GPL(cache_register_net);
  
- int cache_register(struct cache_detail *cd)
- {
-       return cache_register_net(cd, &init_net);
- }
- EXPORT_SYMBOL_GPL(cache_register);
  void cache_unregister_net(struct cache_detail *cd, struct net *net)
  {
        remove_cache_proc_entries(cd, net);
  }
  EXPORT_SYMBOL_GPL(cache_unregister_net);
  
- void cache_unregister(struct cache_detail *cd)
+ struct cache_detail *cache_create_net(struct cache_detail *tmpl, struct net *net)
+ {
+       struct cache_detail *cd;
+       cd = kmemdup(tmpl, sizeof(struct cache_detail), GFP_KERNEL);
+       if (cd == NULL)
+               return ERR_PTR(-ENOMEM);
+       cd->hash_table = kzalloc(cd->hash_size * sizeof(struct cache_head *),
+                                GFP_KERNEL);
+       if (cd->hash_table == NULL) {
+               kfree(cd);
+               return ERR_PTR(-ENOMEM);
+       }
+       cd->net = net;
+       return cd;
+ }
+ EXPORT_SYMBOL_GPL(cache_create_net);
+ void cache_destroy_net(struct cache_detail *cd, struct net *net)
  {
-       cache_unregister_net(cd, &init_net);
+       kfree(cd->hash_table);
+       kfree(cd);
  }
- EXPORT_SYMBOL_GPL(cache_unregister);
+ EXPORT_SYMBOL_GPL(cache_destroy_net);
  
  static ssize_t cache_read_pipefs(struct file *filp, char __user *buf,
                                 size_t count, loff_t *ppos)
@@@ -1789,17 -1803,14 +1805,14 @@@ int sunrpc_cache_register_pipefs(struc
        struct dentry *dir;
        int ret = 0;
  
-       sunrpc_init_cache_detail(cd);
        q.name = name;
        q.len = strlen(name);
        q.hash = full_name_hash(q.name, q.len);
        dir = rpc_create_cache_dir(parent, &q, umode, cd);
        if (!IS_ERR(dir))
                cd->u.pipefs.dir = dir;
-       else {
-               sunrpc_destroy_cache_detail(cd);
+       else
                ret = PTR_ERR(dir);
-       }
        return ret;
  }
  EXPORT_SYMBOL_GPL(sunrpc_cache_register_pipefs);
@@@ -1808,7 -1819,6 +1821,6 @@@ void sunrpc_cache_unregister_pipefs(str
  {
        rpc_remove_cache_dir(cd->u.pipefs.dir);
        cd->u.pipefs.dir = NULL;
-       sunrpc_destroy_cache_detail(cd);
  }
  EXPORT_SYMBOL_GPL(sunrpc_cache_unregister_pipefs);
  
index 6ab35736d88fa1e5179583a18830b8f2c45c96c4,bcd574f2ac566a96c34b041f44ce1e0b1bedbed8..521d8f7dc833ac769afe83daf0b03fe818c5be32
@@@ -211,7 -211,7 +211,7 @@@ static int ip_map_parse(struct cache_de
        len = qword_get(&mesg, buf, mlen);
        if (len <= 0) return -EINVAL;
  
-       if (rpc_pton(buf, len, &address.sa, sizeof(address)) == 0)
+       if (rpc_pton(cd->net, buf, len, &address.sa, sizeof(address)) == 0)
                return -EINVAL;
        switch (address.sa.sa_family) {
        case AF_INET:
@@@ -436,7 -436,6 +436,6 @@@ struct unix_gid 
        uid_t                   uid;
        struct group_info       *gi;
  };
- static struct cache_head      *gid_table[GID_HASHMAX];
  
  static void unix_gid_put(struct kref *kref)
  {
@@@ -494,8 -493,7 +493,7 @@@ static int unix_gid_upcall(struct cache
        return sunrpc_cache_pipe_upcall(cd, h, unix_gid_request);
  }
  
- static struct unix_gid *unix_gid_lookup(uid_t uid);
- extern struct cache_detail unix_gid_cache;
+ static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, uid_t uid);
  
  static int unix_gid_parse(struct cache_detail *cd,
                        char *mesg, int mlen)
        time_t expiry;
        struct unix_gid ug, *ugp;
  
 -      if (mlen <= 0 || mesg[mlen-1] != '\n')
 +      if (mesg[mlen - 1] != '\n')
                return -EINVAL;
        mesg[mlen-1] = 0;
  
                GROUP_AT(ug.gi, i) = gid;
        }
  
-       ugp = unix_gid_lookup(uid);
+       ugp = unix_gid_lookup(cd, uid);
        if (ugp) {
                struct cache_head *ch;
                ug.h.flags = 0;
                ug.h.expiry_time = expiry;
-               ch = sunrpc_cache_update(&unix_gid_cache,
+               ch = sunrpc_cache_update(cd,
                                         &ug.h, &ugp->h,
                                         hash_long(uid, GID_HASHBITS));
                if (!ch)
                        err = -ENOMEM;
                else {
                        err = 0;
-                       cache_put(ch, &unix_gid_cache);
+                       cache_put(ch, cd);
                }
        } else
                err = -ENOMEM;
@@@ -587,10 -585,9 +585,9 @@@ static int unix_gid_show(struct seq_fil
        return 0;
  }
  
- struct cache_detail unix_gid_cache = {
+ static struct cache_detail unix_gid_cache_template = {
        .owner          = THIS_MODULE,
        .hash_size      = GID_HASHMAX,
-       .hash_table     = gid_table,
        .name           = "auth.unix.gid",
        .cache_put      = unix_gid_put,
        .cache_upcall   = unix_gid_upcall,
        .alloc          = unix_gid_alloc,
  };
  
- static struct unix_gid *unix_gid_lookup(uid_t uid)
+ int unix_gid_cache_create(struct net *net)
+ {
+       struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+       struct cache_detail *cd;
+       int err;
+       cd = cache_create_net(&unix_gid_cache_template, net);
+       if (IS_ERR(cd))
+               return PTR_ERR(cd);
+       err = cache_register_net(cd, net);
+       if (err) {
+               cache_destroy_net(cd, net);
+               return err;
+       }
+       sn->unix_gid_cache = cd;
+       return 0;
+ }
+ void unix_gid_cache_destroy(struct net *net)
+ {
+       struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+       struct cache_detail *cd = sn->unix_gid_cache;
+       sn->unix_gid_cache = NULL;
+       cache_purge(cd);
+       cache_unregister_net(cd, net);
+       cache_destroy_net(cd, net);
+ }
+ static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, uid_t uid)
  {
        struct unix_gid ug;
        struct cache_head *ch;
  
        ug.uid = uid;
-       ch = sunrpc_cache_lookup(&unix_gid_cache, &ug.h,
-                                hash_long(uid, GID_HASHBITS));
+       ch = sunrpc_cache_lookup(cd, &ug.h, hash_long(uid, GID_HASHBITS));
        if (ch)
                return container_of(ch, struct unix_gid, h);
        else
@@@ -621,11 -646,13 +646,13 @@@ static struct group_info *unix_gid_find
        struct unix_gid *ug;
        struct group_info *gi;
        int ret;
+       struct sunrpc_net *sn = net_generic(rqstp->rq_xprt->xpt_net,
+                                           sunrpc_net_id);
  
-       ug = unix_gid_lookup(uid);
+       ug = unix_gid_lookup(sn->unix_gid_cache, uid);
        if (!ug)
                return ERR_PTR(-EAGAIN);
-       ret = cache_check(&unix_gid_cache, &ug->h, &rqstp->rq_chandle);
+       ret = cache_check(sn->unix_gid_cache, &ug->h, &rqstp->rq_chandle);
        switch (ret) {
        case -ENOENT:
                return ERR_PTR(-ENOENT);
                return ERR_PTR(-ESHUTDOWN);
        case 0:
                gi = get_group_info(ug->gi);
-               cache_put(&ug->h, &unix_gid_cache);
+               cache_put(&ug->h, sn->unix_gid_cache);
                return gi;
        default:
                return ERR_PTR(-EAGAIN);
@@@ -849,56 -876,45 +876,45 @@@ struct auth_ops svcauth_unix = 
        .set_client     = svcauth_unix_set_client,
  };
  
+ static struct cache_detail ip_map_cache_template = {
+       .owner          = THIS_MODULE,
+       .hash_size      = IP_HASHMAX,
+       .name           = "auth.unix.ip",
+       .cache_put      = ip_map_put,
+       .cache_upcall   = ip_map_upcall,
+       .cache_parse    = ip_map_parse,
+       .cache_show     = ip_map_show,
+       .match          = ip_map_match,
+       .init           = ip_map_init,
+       .update         = update,
+       .alloc          = ip_map_alloc,
+ };
  int ip_map_cache_create(struct net *net)
  {
-       int err = -ENOMEM;
-       struct cache_detail *cd;
-       struct cache_head **tbl;
        struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+       struct cache_detail *cd;
+       int err;
  
-       cd = kzalloc(sizeof(struct cache_detail), GFP_KERNEL);
-       if (cd == NULL)
-               goto err_cd;
-       tbl = kzalloc(IP_HASHMAX * sizeof(struct cache_head *), GFP_KERNEL);
-       if (tbl == NULL)
-               goto err_tbl;
-       cd->owner = THIS_MODULE,
-       cd->hash_size = IP_HASHMAX,
-       cd->hash_table = tbl,
-       cd->name = "auth.unix.ip",
-       cd->cache_put = ip_map_put,
-       cd->cache_upcall = ip_map_upcall,
-       cd->cache_parse = ip_map_parse,
-       cd->cache_show = ip_map_show,
-       cd->match = ip_map_match,
-       cd->init = ip_map_init,
-       cd->update = update,
-       cd->alloc = ip_map_alloc,
+       cd = cache_create_net(&ip_map_cache_template, net);
+       if (IS_ERR(cd))
+               return PTR_ERR(cd);
        err = cache_register_net(cd, net);
-       if (err)
-               goto err_reg;
+       if (err) {
+               cache_destroy_net(cd, net);
+               return err;
+       }
        sn->ip_map_cache = cd;
        return 0;
- err_reg:
-       kfree(tbl);
- err_tbl:
-       kfree(cd);
- err_cd:
-       return err;
  }
  
  void ip_map_cache_destroy(struct net *net)
  {
-       struct sunrpc_net *sn;
+       struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+       struct cache_detail *cd = sn->ip_map_cache;
  
-       sn = net_generic(net, sunrpc_net_id);
-       cache_purge(sn->ip_map_cache);
-       cache_unregister_net(sn->ip_map_cache, net);
-       kfree(sn->ip_map_cache->hash_table);
-       kfree(sn->ip_map_cache);
+       sn->ip_map_cache = NULL;
+       cache_purge(cd);
+       cache_unregister_net(cd, net);
+       cache_destroy_net(cd, net);
  }
diff --combined net/sunrpc/svcsock.c
index b3bb18ba350a925950929307a0f22134f0c97fd9,40ae884db865f975f589a433432652d0fe1936ed..824d32fb31214b5f433f439c439e7161fba28fb5
@@@ -396,7 -396,7 +396,7 @@@ static int svc_partial_recvfrom(struct 
                                int buflen, unsigned int base)
  {
        size_t save_iovlen;
-       void __user *save_iovbase;
+       void *save_iovbase;
        unsigned int i;
        int ret;
  
@@@ -1381,6 -1381,8 +1381,6 @@@ void svc_sock_update_bufs(struct svc_se
        spin_lock_bh(&serv->sv_lock);
        list_for_each_entry(svsk, &serv->sv_permsocks, sk_xprt.xpt_list)
                set_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags);
 -      list_for_each_entry(svsk, &serv->sv_tempsocks, sk_xprt.xpt_list)
 -              set_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags);
        spin_unlock_bh(&serv->sv_lock);
  }
  EXPORT_SYMBOL_GPL(svc_sock_update_bufs);
@@@ -1407,7 -1409,8 +1407,8 @@@ static struct svc_sock *svc_setup_socke
  
        /* Register socket with portmapper */
        if (*errp >= 0 && pmap_register)
-               *errp = svc_register(serv, inet->sk_family, inet->sk_protocol,
+               *errp = svc_register(serv, sock_net(sock->sk), inet->sk_family,
+                                    inet->sk_protocol,
                                     ntohs(inet_sk(inet)->inet_sport));
  
        if (*errp < 0) {