Merge branch 'for-2.6.37' of git://linux-nfs.org/~bfields/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 26 Oct 2010 16:55:25 +0000 (09:55 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 26 Oct 2010 16:55:25 +0000 (09:55 -0700)
* 'for-2.6.37' of git://linux-nfs.org/~bfields/linux: (99 commits)
  svcrpc: svc_tcp_sendto XPT_DEAD check is redundant
  svcrpc: no need for XPT_DEAD check in svc_xprt_enqueue
  svcrpc: assume svc_delete_xprt() called only once
  svcrpc: never clear XPT_BUSY on dead xprt
  nfsd4: fix connection allocation in sequence()
  nfsd4: only require krb5 principal for NFSv4.0 callbacks
  nfsd4: move minorversion to client
  nfsd4: delay session removal till free_client
  nfsd4: separate callback change and callback probe
  nfsd4: callback program number is per-session
  nfsd4: track backchannel connections
  nfsd4: confirm only on succesful create_session
  nfsd4: make backchannel sequence number per-session
  nfsd4: use client pointer to backchannel session
  nfsd4: move callback setup into session init code
  nfsd4: don't cache seq_misordered replies
  SUNRPC: Properly initialize sock_xprt.srcaddr in all cases
  SUNRPC: Use conventional switch statement when reclassifying sockets
  sunrpc/xprtrdma: clean up workqueue usage
  sunrpc: Turn list_for_each-s into the ..._entry-s
  ...

Fix up trivial conflicts (two different deprecation notices added in
separate branches) in Documentation/feature-removal-schedule.txt

61 files changed:
Documentation/feature-removal-schedule.txt
fs/Makefile
fs/compat.c
fs/lockd/host.c
fs/lockd/mon.c
fs/lockd/svc.c
fs/lockd/svc4proc.c
fs/lockd/svclock.c
fs/lockd/svcproc.c
fs/nfs/callback.c
fs/nfs/client.c
fs/nfs/dns_resolve.c
fs/nfs/mount_clnt.c
fs/nfsd/Kconfig
fs/nfsd/export.c
fs/nfsd/nfs4callback.c
fs/nfsd/nfs4idmap.c
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c
fs/nfsd/nfsctl.c
fs/nfsd/nfsd.h
fs/nfsd/nfssvc.c
fs/nfsd/state.h
include/linux/net.h
include/linux/nfs4.h
include/linux/sunrpc/auth.h
include/linux/sunrpc/cache.h
include/linux/sunrpc/clnt.h
include/linux/sunrpc/gss_spkm3.h [deleted file]
include/linux/sunrpc/stats.h
include/linux/sunrpc/svc_xprt.h
include/linux/sunrpc/svcauth.h
include/linux/sunrpc/xprt.h
net/socket.c
net/sunrpc/Kconfig
net/sunrpc/auth.c
net/sunrpc/auth_generic.c
net/sunrpc/auth_gss/Makefile
net/sunrpc/auth_gss/gss_spkm3_mech.c [deleted file]
net/sunrpc/auth_gss/gss_spkm3_seal.c [deleted file]
net/sunrpc/auth_gss/gss_spkm3_token.c [deleted file]
net/sunrpc/auth_gss/gss_spkm3_unseal.c [deleted file]
net/sunrpc/auth_gss/svcauth_gss.c
net/sunrpc/cache.c
net/sunrpc/clnt.c
net/sunrpc/netns.h [new file with mode: 0644]
net/sunrpc/rpcb_clnt.c
net/sunrpc/stats.c
net/sunrpc/sunrpc_syms.c
net/sunrpc/svc.c
net/sunrpc/svc_xprt.c
net/sunrpc/svcauth_unix.c
net/sunrpc/svcsock.c
net/sunrpc/xprt.c
net/sunrpc/xprtrdma/svc_rdma.c
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
net/sunrpc/xprtrdma/svc_rdma_sendto.c
net/sunrpc/xprtrdma/svc_rdma_transport.c
net/sunrpc/xprtrdma/transport.c
net/sunrpc/xprtsock.c

index e833c8c81e691797fd44311c5463714ca1535a94..d2af87ba96e11ed5edc7ac4a81e081cff23ee00c 100644 (file)
@@ -535,3 +535,13 @@ Why:       Hareware scan is the prefer method for iwlwifi devices for
 Who:   Wey-Yi Guy <wey-yi.w.guy@intel.com>
 
 ----------------------------
+
+What:   access to nfsd auth cache through sys_nfsservctl or '.' files
+        in the 'nfsd' filesystem.
+When:   2.6.40
+Why:    This is a legacy interface which have been replaced by a more
+        dynamic cache.  Continuing to maintain this interface is an
+        unnecessary burden.
+Who:    NeilBrown <neilb@suse.de>
+
+----------------------------
index e6ec1d309b1d2ba43dd3e036a9f8dcfba9a757a1..26956fcec917f1792307e31d3b58bec32137d7ff 100644 (file)
@@ -29,10 +29,7 @@ obj-$(CONFIG_EVENTFD)                += eventfd.o
 obj-$(CONFIG_AIO)               += aio.o
 obj-$(CONFIG_FILE_LOCKING)      += locks.o
 obj-$(CONFIG_COMPAT)           += compat.o compat_ioctl.o
-
-nfsd-$(CONFIG_NFSD)            := nfsctl.o
-obj-y                          += $(nfsd-y) $(nfsd-m)
-
+obj-$(CONFIG_NFSD_DEPRECATED)  += nfsctl.o
 obj-$(CONFIG_BINFMT_AOUT)      += binfmt_aout.o
 obj-$(CONFIG_BINFMT_EM86)      += binfmt_em86.o
 obj-$(CONFIG_BINFMT_MISC)      += binfmt_misc.o
index 0644a154672b93012a4d7a7f864ba7f505b43213..f03abdadc401d2266375dc3328cb977cecc41fdc 100644 (file)
@@ -1963,7 +1963,7 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds,
 }
 #endif /* HAVE_SET_RESTORE_SIGMASK */
 
-#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
+#if (defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)) && !defined(CONFIG_NFSD_DEPRECATED)
 /* Stuff for NFS server syscalls... */
 struct compat_nfsctl_svc {
        u16                     svc32_port;
index bb464d12104c44dd67b0eee1496c49a8df138ac7..25e21e4023b22a3f51ceb9d1c1869c2b7518f6d0 100644 (file)
@@ -353,6 +353,7 @@ nlm_bind_host(struct nlm_host *host)
                        .to_retries     = 5U,
                };
                struct rpc_create_args args = {
+                       .net            = &init_net,
                        .protocol       = host->h_proto,
                        .address        = nlm_addr(host),
                        .addrsize       = host->h_addrlen,
index e3015464fbab8ac1a9a48f0ea5465080923731bb..e0c91894964446231135d7ad2317b7135fa6265e 100644 (file)
@@ -69,6 +69,7 @@ static struct rpc_clnt *nsm_create(void)
                .sin_addr.s_addr        = htonl(INADDR_LOOPBACK),
        };
        struct rpc_create_args args = {
+               .net                    = &init_net,
                .protocol               = XPRT_TRANSPORT_UDP,
                .address                = (struct sockaddr *)&sin,
                .addrsize               = sizeof(sin),
index f1bacf1a03912bb52939e5d0595b9c71f70ff8d6..b13aabc1229894507ca05ba53159c6f081d77fcf 100644 (file)
@@ -206,7 +206,7 @@ static int create_lockd_listener(struct svc_serv *serv, const char *name,
 
        xprt = svc_find_xprt(serv, name, family, 0);
        if (xprt == NULL)
-               return svc_create_xprt(serv, name, family, port,
+               return svc_create_xprt(serv, name, &init_net, family, port,
                                                SVC_SOCK_DEFAULTS);
        svc_xprt_put(xprt);
        return 0;
index 031c6569a134aa7efcfc776d1cab5ef03bc742b8..a336e832475db7ffd5516a4cbe7e32daff49fd7c 100644 (file)
@@ -230,9 +230,7 @@ static void nlm4svc_callback_exit(struct rpc_task *task, void *data)
 
 static void nlm4svc_callback_release(void *data)
 {
-       lock_kernel();
        nlm_release_call(data);
-       unlock_kernel();
 }
 
 static const struct rpc_call_ops nlm4svc_callback_ops = {
index 84055d31bfc5e427a87c2412eea17f6ea59e2fc4..6f1ef000975abde05f2fec833b3dfcb6fab563dc 100644 (file)
@@ -52,12 +52,13 @@ static const struct rpc_call_ops nlmsvc_grant_ops;
  * The list of blocked locks to retry
  */
 static LIST_HEAD(nlm_blocked);
+static DEFINE_SPINLOCK(nlm_blocked_lock);
 
 /*
  * Insert a blocked lock into the global list
  */
 static void
-nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
+nlmsvc_insert_block_locked(struct nlm_block *block, unsigned long when)
 {
        struct nlm_block *b;
        struct list_head *pos;
@@ -87,6 +88,13 @@ nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
        block->b_when = when;
 }
 
+static void nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
+{
+       spin_lock(&nlm_blocked_lock);
+       nlmsvc_insert_block_locked(block, when);
+       spin_unlock(&nlm_blocked_lock);
+}
+
 /*
  * Remove a block from the global list
  */
@@ -94,7 +102,9 @@ static inline void
 nlmsvc_remove_block(struct nlm_block *block)
 {
        if (!list_empty(&block->b_list)) {
+               spin_lock(&nlm_blocked_lock);
                list_del_init(&block->b_list);
+               spin_unlock(&nlm_blocked_lock);
                nlmsvc_release_block(block);
        }
 }
@@ -651,7 +661,7 @@ static int nlmsvc_grant_deferred(struct file_lock *fl, struct file_lock *conf,
        struct nlm_block *block;
        int rc = -ENOENT;
 
-       lock_kernel();
+       spin_lock(&nlm_blocked_lock);
        list_for_each_entry(block, &nlm_blocked, b_list) {
                if (nlm_compare_locks(&block->b_call->a_args.lock.fl, fl)) {
                        dprintk("lockd: nlmsvc_notify_blocked block %p flags %d\n",
@@ -665,13 +675,13 @@ static int nlmsvc_grant_deferred(struct file_lock *fl, struct file_lock *conf,
                        } else if (result == 0)
                                block->b_granted = 1;
 
-                       nlmsvc_insert_block(block, 0);
+                       nlmsvc_insert_block_locked(block, 0);
                        svc_wake_up(block->b_daemon);
                        rc = 0;
                        break;
                }
        }
-       unlock_kernel();
+       spin_unlock(&nlm_blocked_lock);
        if (rc == -ENOENT)
                printk(KERN_WARNING "lockd: grant for unknown block\n");
        return rc;
@@ -803,7 +813,7 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
 
        dprintk("lockd: GRANT_MSG RPC callback\n");
 
-       lock_kernel();
+       spin_lock(&nlm_blocked_lock);
        /* if the block is not on a list at this point then it has
         * been invalidated. Don't try to requeue it.
         *
@@ -825,19 +835,20 @@ static void nlmsvc_grant_callback(struct rpc_task *task, void *data)
                /* Call was successful, now wait for client callback */
                timeout = 60 * HZ;
        }
-       nlmsvc_insert_block(block, timeout);
+       nlmsvc_insert_block_locked(block, timeout);
        svc_wake_up(block->b_daemon);
 out:
-       unlock_kernel();
+       spin_unlock(&nlm_blocked_lock);
 }
 
+/*
+ * FIXME: nlmsvc_release_block() grabs a mutex.  This is not allowed for an
+ * .rpc_release rpc_call_op
+ */
 static void nlmsvc_grant_release(void *data)
 {
        struct nlm_rqst         *call = data;
-
-       lock_kernel();
        nlmsvc_release_block(call->a_block);
-       unlock_kernel();
 }
 
 static const struct rpc_call_ops nlmsvc_grant_ops = {
index 0f2ab741ae7c8205edeed842e7bbf6f89d6d482f..c3069f38d60220e82726d5c318d0942e125ddb03 100644 (file)
@@ -260,9 +260,7 @@ static void nlmsvc_callback_exit(struct rpc_task *task, void *data)
 
 static void nlmsvc_callback_release(void *data)
 {
-       lock_kernel();
        nlm_release_call(data);
-       unlock_kernel();
 }
 
 static const struct rpc_call_ops nlmsvc_callback_ops = {
index e17b49e2eabd8dcb887819bbf2d8c714e4e2085f..aeec017fe814acbdf17947a68553765a335359ae 100644 (file)
@@ -109,7 +109,7 @@ nfs4_callback_up(struct svc_serv *serv)
 {
        int ret;
 
-       ret = svc_create_xprt(serv, "tcp", PF_INET,
+       ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET,
                                nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
        if (ret <= 0)
                goto out_err;
@@ -117,7 +117,7 @@ nfs4_callback_up(struct svc_serv *serv)
        dprintk("NFS: Callback listener port = %u (af %u)\n",
                        nfs_callback_tcpport, PF_INET);
 
-       ret = svc_create_xprt(serv, "tcp", PF_INET6,
+       ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET6,
                                nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
        if (ret > 0) {
                nfs_callback_tcpport6 = ret;
index fd6f0a70021b9fa8c1918372f2cd62d22636dfb9..0870d0d4efc0b15d1a72b212e5acc693d6a90725 100644 (file)
@@ -605,6 +605,7 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
 {
        struct rpc_clnt         *clnt = NULL;
        struct rpc_create_args args = {
+               .net            = &init_net,
                .protocol       = clp->cl_proto,
                .address        = (struct sockaddr *)&clp->cl_addr,
                .addrsize       = clp->cl_addrlen,
index dba50a5625db7987d1e4a757063f3a5412ff5928..a6e711ad130f9fdb456ad2a09b056214815af4f5 100644 (file)
@@ -167,7 +167,7 @@ static int nfs_dns_show(struct seq_file *m, struct cache_detail *cd,
                return 0;
        }
        item = container_of(h, struct nfs_dns_ent, h);
-       ttl = (long)item->h.expiry_time - (long)get_seconds();
+       ttl = item->h.expiry_time - seconds_since_boot();
        if (ttl < 0)
                ttl = 0;
 
@@ -239,7 +239,7 @@ static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen)
        ttl = get_expiry(&buf);
        if (ttl == 0)
                goto out;
-       key.h.expiry_time = ttl + get_seconds();
+       key.h.expiry_time = ttl + seconds_since_boot();
 
        ret = -ENOMEM;
        item = nfs_dns_lookup(cd, &key);
@@ -301,7 +301,7 @@ static int do_cache_lookup_nowait(struct cache_detail *cd,
                goto out_err;
        ret = -ETIMEDOUT;
        if (!test_bit(CACHE_VALID, &(*item)->h.flags)
-                       || (*item)->h.expiry_time < get_seconds()
+                       || (*item)->h.expiry_time < seconds_since_boot()
                        || cd->flush_time > (*item)->h.last_refresh)
                goto out_put;
        ret = -ENOENT;
index d610203d95c6f96271e48d92baa43287dc2efe29..eceafe74f4737ea5fbcea90b8ac19749737667b9 100644 (file)
@@ -153,6 +153,7 @@ int nfs_mount(struct nfs_mount_request *info)
                .rpc_resp       = &result,
        };
        struct rpc_create_args args = {
+               .net            = &init_net,
                .protocol       = info->protocol,
                .address        = info->sap,
                .addrsize       = info->salen,
@@ -224,6 +225,7 @@ void nfs_umount(const struct nfs_mount_request *info)
                .to_retries = 2,
        };
        struct rpc_create_args args = {
+               .net            = &init_net,
                .protocol       = IPPROTO_UDP,
                .address        = info->sap,
                .addrsize       = info->salen,
index 7cf4ddafb4abc3b0ae4f511bb0d264eab0f563d4..31a78fce4732a0d473884425a0728073bf96668e 100644 (file)
@@ -29,6 +29,18 @@ config NFSD
 
          If unsure, say N.
 
+config NFSD_DEPRECATED
+       bool "Include support for deprecated syscall interface to NFSD"
+       depends on NFSD
+       default y
+       help
+         The syscall interface to nfsd was obsoleted in 2.6.0 by a new
+         filesystem based interface.  The old interface is due for removal
+         in 2.6.40.  If you wish to remove the interface before then
+         say N.
+
+         In unsure, say Y.
+
 config NFSD_V2_ACL
        bool
        depends on NFSD
index c2a4f71d87dd507a99d20564cff85ff15bd91646..c0fcb7ab7f6db42b9793dd2b59f853f92728db35 100644 (file)
@@ -28,9 +28,6 @@
 typedef struct auth_domain     svc_client;
 typedef struct svc_export      svc_export;
 
-static void            exp_do_unexport(svc_export *unexp);
-static int             exp_verify_string(char *cp, int max);
-
 /*
  * We have two caches.
  * One maps client+vfsmnt+dentry to export options - the export map
@@ -802,6 +799,7 @@ exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp)
        return ek;
 }
 
+#ifdef CONFIG_NFSD_DEPRECATED
 static int exp_set_key(svc_client *clp, int fsid_type, u32 *fsidv,
                       struct svc_export *exp)
 {
@@ -852,6 +850,7 @@ exp_get_fsid_key(svc_client *clp, int fsid)
 
        return exp_find_key(clp, FSID_NUM, fsidv, NULL);
 }
+#endif
 
 static svc_export *exp_get_by_name(svc_client *clp, const struct path *path,
                                     struct cache_req *reqp)
@@ -893,6 +892,7 @@ static struct svc_export *exp_parent(svc_client *clp, struct path *path)
        return exp;
 }
 
+#ifdef CONFIG_NFSD_DEPRECATED
 /*
  * Hashtable locking. Write locks are placed only by user processes
  * wanting to modify export information.
@@ -925,6 +925,19 @@ exp_writeunlock(void)
 {
        up_write(&hash_sem);
 }
+#else
+
+/* hash_sem not needed once deprecated interface is removed */
+void exp_readlock(void) {}
+static inline void exp_writelock(void){}
+void exp_readunlock(void) {}
+static inline void exp_writeunlock(void){}
+
+#endif
+
+#ifdef CONFIG_NFSD_DEPRECATED
+static void            exp_do_unexport(svc_export *unexp);
+static int             exp_verify_string(char *cp, int max);
 
 static void exp_fsid_unhash(struct svc_export *exp)
 {
@@ -935,10 +948,9 @@ static void exp_fsid_unhash(struct svc_export *exp)
 
        ek = exp_get_fsid_key(exp->ex_client, exp->ex_fsid);
        if (!IS_ERR(ek)) {
-               ek->h.expiry_time = get_seconds()-1;
+               sunrpc_invalidate(&ek->h, &svc_expkey_cache);
                cache_put(&ek->h, &svc_expkey_cache);
        }
-       svc_expkey_cache.nextcheck = get_seconds();
 }
 
 static int exp_fsid_hash(svc_client *clp, struct svc_export *exp)
@@ -973,10 +985,9 @@ static void exp_unhash(struct svc_export *exp)
 
        ek = exp_get_key(exp->ex_client, inode->i_sb->s_dev, inode->i_ino);
        if (!IS_ERR(ek)) {
-               ek->h.expiry_time = get_seconds()-1;
+               sunrpc_invalidate(&ek->h, &svc_expkey_cache);
                cache_put(&ek->h, &svc_expkey_cache);
        }
-       svc_expkey_cache.nextcheck = get_seconds();
 }
        
 /*
@@ -1097,8 +1108,7 @@ out:
 static void
 exp_do_unexport(svc_export *unexp)
 {
-       unexp->h.expiry_time = get_seconds()-1;
-       svc_export_cache.nextcheck = get_seconds();
+       sunrpc_invalidate(&unexp->h, &svc_export_cache);
        exp_unhash(unexp);
        exp_fsid_unhash(unexp);
 }
@@ -1150,6 +1160,7 @@ out_unlock:
        exp_writeunlock();
        return err;
 }
+#endif /* CONFIG_NFSD_DEPRECATED */
 
 /*
  * Obtain the root fh on behalf of a client.
@@ -1459,25 +1470,43 @@ static void show_secinfo_flags(struct seq_file *m, int flags)
        show_expflags(m, flags, NFSEXP_SECINFO_FLAGS);
 }
 
+static bool secinfo_flags_equal(int f, int g)
+{
+       f &= NFSEXP_SECINFO_FLAGS;
+       g &= NFSEXP_SECINFO_FLAGS;
+       return f == g;
+}
+
+static int show_secinfo_run(struct seq_file *m, struct exp_flavor_info **fp, struct exp_flavor_info *end)
+{
+       int flags;
+
+       flags = (*fp)->flags;
+       seq_printf(m, ",sec=%d", (*fp)->pseudoflavor);
+       (*fp)++;
+       while (*fp != end && secinfo_flags_equal(flags, (*fp)->flags)) {
+               seq_printf(m, ":%d", (*fp)->pseudoflavor);
+               (*fp)++;
+       }
+       return flags;
+}
+
 static void show_secinfo(struct seq_file *m, struct svc_export *exp)
 {
        struct exp_flavor_info *f;
        struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors;
-       int lastflags = 0, first = 0;
+       int flags;
 
        if (exp->ex_nflavors == 0)
                return;
-       for (f = exp->ex_flavors; f < end; f++) {
-               if (first || f->flags != lastflags) {
-                       if (!first)
-                               show_secinfo_flags(m, lastflags);
-                       seq_printf(m, ",sec=%d", f->pseudoflavor);
-                       lastflags = f->flags;
-               } else {
-                       seq_printf(m, ":%d", f->pseudoflavor);
-               }
+       f = exp->ex_flavors;
+       flags = show_secinfo_run(m, &f, end);
+       if (!secinfo_flags_equal(flags, exp->ex_flags))
+               show_secinfo_flags(m, flags);
+       while (f != end) {
+               flags = show_secinfo_run(m, &f, end);
+               show_secinfo_flags(m, flags);
        }
-       show_secinfo_flags(m, lastflags);
 }
 
 static void exp_flags(struct seq_file *m, int flag, int fsid,
@@ -1532,6 +1561,7 @@ const struct seq_operations nfs_exports_op = {
        .show   = e_show,
 };
 
+#ifdef CONFIG_NFSD_DEPRECATED
 /*
  * Add or modify a client.
  * Change requests may involve the list of host addresses. The list of
@@ -1563,7 +1593,7 @@ exp_addclient(struct nfsctl_client *ncp)
        /* Insert client into hashtable. */
        for (i = 0; i < ncp->cl_naddr; i++) {
                ipv6_addr_set_v4mapped(ncp->cl_addrlist[i].s_addr, &addr6);
-               auth_unix_add_addr(&addr6, dom);
+               auth_unix_add_addr(&init_net, &addr6, dom);
        }
        auth_unix_forget_old(dom);
        auth_domain_put(dom);
@@ -1621,6 +1651,7 @@ exp_verify_string(char *cp, int max)
        printk(KERN_NOTICE "nfsd: couldn't validate string %s\n", cp);
        return 0;
 }
+#endif /* CONFIG_NFSD_DEPRECATED */
 
 /*
  * Initialize the exports module.
index 014482c4e57d56e7f15f3cb11d31ee30dbc4330d..143da2eecd7b73a1d3181627c9b03e4c16f3e0c1 100644 (file)
@@ -247,10 +247,11 @@ encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp,
 }
 
 static void
-encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *args,
+encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb,
                   struct nfs4_cb_compound_hdr *hdr)
 {
        __be32 *p;
+       struct nfsd4_session *ses = cb->cb_clp->cl_cb_session;
 
        if (hdr->minorversion == 0)
                return;
@@ -258,8 +259,8 @@ encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *args,
        RESERVE_SPACE(1 + NFS4_MAX_SESSIONID_LEN + 20);
 
        WRITE32(OP_CB_SEQUENCE);
-       WRITEMEM(args->cbs_clp->cl_sessionid.data, NFS4_MAX_SESSIONID_LEN);
-       WRITE32(args->cbs_clp->cl_cb_seq_nr);
+       WRITEMEM(ses->se_sessionid.data, NFS4_MAX_SESSIONID_LEN);
+       WRITE32(ses->se_cb_seq_nr);
        WRITE32(0);             /* slotid, always 0 */
        WRITE32(0);             /* highest slotid always 0 */
        WRITE32(0);             /* cachethis always 0 */
@@ -279,18 +280,18 @@ nfs4_xdr_enc_cb_null(struct rpc_rqst *req, __be32 *p)
 
 static int
 nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p,
-               struct nfs4_rpc_args *rpc_args)
+               struct nfsd4_callback *cb)
 {
        struct xdr_stream xdr;
-       struct nfs4_delegation *args = rpc_args->args_op;
+       struct nfs4_delegation *args = cb->cb_op;
        struct nfs4_cb_compound_hdr hdr = {
-               .ident = args->dl_ident,
-               .minorversion = rpc_args->args_seq.cbs_minorversion,
+               .ident = cb->cb_clp->cl_cb_ident,
+               .minorversion = cb->cb_minorversion,
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_cb_compound_hdr(&xdr, &hdr);
-       encode_cb_sequence(&xdr, &rpc_args->args_seq, &hdr);
+       encode_cb_sequence(&xdr, cb, &hdr);
        encode_cb_recall(&xdr, args, &hdr);
        encode_cb_nops(&hdr);
        return 0;
@@ -338,15 +339,16 @@ decode_cb_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
  * with a single slot.
  */
 static int
-decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *res,
+decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb,
                   struct rpc_rqst *rqstp)
 {
+       struct nfsd4_session *ses = cb->cb_clp->cl_cb_session;
        struct nfs4_sessionid id;
        int status;
        u32 dummy;
        __be32 *p;
 
-       if (res->cbs_minorversion == 0)
+       if (cb->cb_minorversion == 0)
                return 0;
 
        status = decode_cb_op_hdr(xdr, OP_CB_SEQUENCE);
@@ -362,13 +364,12 @@ decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *res,
        READ_BUF(NFS4_MAX_SESSIONID_LEN + 16);
        memcpy(id.data, p, NFS4_MAX_SESSIONID_LEN);
        p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
-       if (memcmp(id.data, res->cbs_clp->cl_sessionid.data,
-                  NFS4_MAX_SESSIONID_LEN)) {
+       if (memcmp(id.data, ses->se_sessionid.data, NFS4_MAX_SESSIONID_LEN)) {
                dprintk("%s Invalid session id\n", __func__);
                goto out;
        }
        READ32(dummy);
-       if (dummy != res->cbs_clp->cl_cb_seq_nr) {
+       if (dummy != ses->se_cb_seq_nr) {
                dprintk("%s Invalid sequence number\n", __func__);
                goto out;
        }
@@ -392,7 +393,7 @@ nfs4_xdr_dec_cb_null(struct rpc_rqst *req, __be32 *p)
 
 static int
 nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p,
-               struct nfsd4_cb_sequence *seq)
+               struct nfsd4_callback *cb)
 {
        struct xdr_stream xdr;
        struct nfs4_cb_compound_hdr hdr;
@@ -402,8 +403,8 @@ nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p,
        status = decode_cb_compound_hdr(&xdr, &hdr);
        if (status)
                goto out;
-       if (seq) {
-               status = decode_cb_sequence(&xdr, seq, rqstp);
+       if (cb) {
+               status = decode_cb_sequence(&xdr, cb, rqstp);
                if (status)
                        goto out;
        }
@@ -472,30 +473,34 @@ static int max_cb_time(void)
 /* Reference counting, callback cleanup, etc., all look racy as heck.
  * And why is cl_cb_set an atomic? */
 
-int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
+int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
 {
        struct rpc_timeout      timeparms = {
                .to_initval     = max_cb_time(),
                .to_retries     = 0,
        };
        struct rpc_create_args args = {
-               .protocol       = XPRT_TRANSPORT_TCP,
-               .address        = (struct sockaddr *) &cb->cb_addr,
-               .addrsize       = cb->cb_addrlen,
+               .net            = &init_net,
+               .address        = (struct sockaddr *) &conn->cb_addr,
+               .addrsize       = conn->cb_addrlen,
                .timeout        = &timeparms,
                .program        = &cb_program,
-               .prognumber     = cb->cb_prog,
                .version        = 0,
                .authflavor     = clp->cl_flavor,
                .flags          = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
-               .client_name    = clp->cl_principal,
        };
        struct rpc_clnt *client;
 
-       if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
-               return -EINVAL;
-       if (cb->cb_minorversion) {
-               args.bc_xprt = cb->cb_xprt;
+       if (clp->cl_minorversion == 0) {
+               if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
+                       return -EINVAL;
+               args.client_name = clp->cl_principal;
+               args.prognumber = conn->cb_prog,
+               args.protocol = XPRT_TRANSPORT_TCP;
+               clp->cl_cb_ident = conn->cb_ident;
+       } else {
+               args.bc_xprt = conn->cb_xprt;
+               args.prognumber = clp->cl_cb_session->se_cb_prog;
                args.protocol = XPRT_TRANSPORT_BC_TCP;
        }
        /* Create RPC client */
@@ -505,7 +510,7 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
                        PTR_ERR(client));
                return PTR_ERR(client);
        }
-       nfsd4_set_callback_client(clp, client);
+       clp->cl_cb_client = client;
        return 0;
 
 }
@@ -518,7 +523,7 @@ static void warn_no_callback_path(struct nfs4_client *clp, int reason)
 
 static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
 {
-       struct nfs4_client *clp = calldata;
+       struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null);
 
        if (task->tk_status)
                warn_no_callback_path(clp, task->tk_status);
@@ -527,6 +532,8 @@ static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
 }
 
 static const struct rpc_call_ops nfsd4_cb_probe_ops = {
+       /* XXX: release method to ensure we set the cb channel down if
+        * necessary on early failure? */
        .rpc_call_done = nfsd4_cb_probe_done,
 };
 
@@ -542,38 +549,42 @@ int set_callback_cred(void)
        return 0;
 }
 
+static struct workqueue_struct *callback_wq;
 
-void do_probe_callback(struct nfs4_client *clp)
+static void do_probe_callback(struct nfs4_client *clp)
 {
-       struct rpc_message msg = {
-               .rpc_proc       = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
-               .rpc_argp       = clp,
-               .rpc_cred       = callback_cred
-       };
-       int status;
+       struct nfsd4_callback *cb = &clp->cl_cb_null;
 
-       status = rpc_call_async(clp->cl_cb_client, &msg,
-                               RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
-                               &nfsd4_cb_probe_ops, (void *)clp);
-       if (status)
-               warn_no_callback_path(clp, status);
+       cb->cb_op = NULL;
+       cb->cb_clp = clp;
+
+       cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL];
+       cb->cb_msg.rpc_argp = NULL;
+       cb->cb_msg.rpc_resp = NULL;
+       cb->cb_msg.rpc_cred = callback_cred;
+
+       cb->cb_ops = &nfsd4_cb_probe_ops;
+
+       queue_work(callback_wq, &cb->cb_work);
 }
 
 /*
- * Set up the callback client and put a NFSPROC4_CB_NULL on the wire...
+ * Poke the callback thread to process any updates to the callback
+ * parameters, and send a null probe.
  */
-void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
+void nfsd4_probe_callback(struct nfs4_client *clp)
 {
-       int status;
+       set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags);
+       do_probe_callback(clp);
+}
 
+void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
+{
        BUG_ON(atomic_read(&clp->cl_cb_set));
 
-       status = setup_callback_client(clp, cb);
-       if (status) {
-               warn_no_callback_path(clp, status);
-               return;
-       }
-       do_probe_callback(clp);
+       spin_lock(&clp->cl_lock);
+       memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn));
+       spin_unlock(&clp->cl_lock);
 }
 
 /*
@@ -584,8 +595,7 @@ void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
 static int nfsd41_cb_setup_sequence(struct nfs4_client *clp,
                struct rpc_task *task)
 {
-       struct nfs4_rpc_args *args = task->tk_msg.rpc_argp;
-       u32 *ptr = (u32 *)clp->cl_sessionid.data;
+       u32 *ptr = (u32 *)clp->cl_cb_session->se_sessionid.data;
        int status = 0;
 
        dprintk("%s: %u:%u:%u:%u\n", __func__,
@@ -597,14 +607,6 @@ static int nfsd41_cb_setup_sequence(struct nfs4_client *clp,
                status = -EAGAIN;
                goto out;
        }
-
-       /*
-        * We'll need the clp during XDR encoding and decoding,
-        * and the sequence during decoding to verify the reply
-        */
-       args->args_seq.cbs_clp = clp;
-       task->tk_msg.rpc_resp = &args->args_seq;
-
 out:
        dprintk("%s status=%d\n", __func__, status);
        return status;
@@ -616,13 +618,13 @@ out:
  */
 static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
 {
-       struct nfs4_delegation *dp = calldata;
+       struct nfsd4_callback *cb = calldata;
+       struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
        struct nfs4_client *clp = dp->dl_client;
-       struct nfs4_rpc_args *args = task->tk_msg.rpc_argp;
-       u32 minorversion = clp->cl_cb_conn.cb_minorversion;
+       u32 minorversion = clp->cl_minorversion;
        int status = 0;
 
-       args->args_seq.cbs_minorversion = minorversion;
+       cb->cb_minorversion = minorversion;
        if (minorversion) {
                status = nfsd41_cb_setup_sequence(clp, task);
                if (status) {
@@ -639,19 +641,20 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
 
 static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
 {
-       struct nfs4_delegation *dp = calldata;
+       struct nfsd4_callback *cb = calldata;
+       struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
        struct nfs4_client *clp = dp->dl_client;
 
        dprintk("%s: minorversion=%d\n", __func__,
-               clp->cl_cb_conn.cb_minorversion);
+               clp->cl_minorversion);
 
-       if (clp->cl_cb_conn.cb_minorversion) {
+       if (clp->cl_minorversion) {
                /* No need for lock, access serialized in nfsd4_cb_prepare */
-               ++clp->cl_cb_seq_nr;
+               ++clp->cl_cb_session->se_cb_seq_nr;
                clear_bit(0, &clp->cl_cb_slot_busy);
                rpc_wake_up_next(&clp->cl_cb_waitq);
                dprintk("%s: freed slot, new seqid=%d\n", __func__,
-                       clp->cl_cb_seq_nr);
+                       clp->cl_cb_session->se_cb_seq_nr);
 
                /* We're done looking into the sequence information */
                task->tk_msg.rpc_resp = NULL;
@@ -661,7 +664,8 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
 
 static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
 {
-       struct nfs4_delegation *dp = calldata;
+       struct nfsd4_callback *cb = calldata;
+       struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
        struct nfs4_client *clp = dp->dl_client;
        struct rpc_clnt *current_rpc_client = clp->cl_cb_client;
 
@@ -706,7 +710,8 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
 
 static void nfsd4_cb_recall_release(void *calldata)
 {
-       struct nfs4_delegation *dp = calldata;
+       struct nfsd4_callback *cb = calldata;
+       struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
 
        nfs4_put_delegation(dp);
 }
@@ -717,8 +722,6 @@ static const struct rpc_call_ops nfsd4_cb_recall_ops = {
        .rpc_release = nfsd4_cb_recall_release,
 };
 
-static struct workqueue_struct *callback_wq;
-
 int nfsd4_create_callback_queue(void)
 {
        callback_wq = create_singlethread_workqueue("nfsd4_callbacks");
@@ -733,57 +736,88 @@ void nfsd4_destroy_callback_queue(void)
 }
 
 /* must be called under the state lock */
-void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt *new)
+void nfsd4_shutdown_callback(struct nfs4_client *clp)
 {
-       struct rpc_clnt *old = clp->cl_cb_client;
-
-       clp->cl_cb_client = new;
+       set_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags);
        /*
-        * After this, any work that saw the old value of cl_cb_client will
-        * be gone:
+        * Note this won't actually result in a null callback;
+        * instead, nfsd4_do_callback_rpc() will detect the killed
+        * client, destroy the rpc client, and stop:
         */
+       do_probe_callback(clp);
        flush_workqueue(callback_wq);
-       /* So we can safely shut it down: */
-       if (old)
-               rpc_shutdown_client(old);
 }
 
-/*
- * called with dp->dl_count inc'ed.
- */
-static void _nfsd4_cb_recall(struct nfs4_delegation *dp)
+void nfsd4_release_cb(struct nfsd4_callback *cb)
 {
-       struct nfs4_client *clp = dp->dl_client;
-       struct rpc_clnt *clnt = clp->cl_cb_client;
-       struct nfs4_rpc_args *args = &dp->dl_recall.cb_args;
-       struct rpc_message msg = {
-               .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL],
-               .rpc_cred = callback_cred
-       };
+       if (cb->cb_ops->rpc_release)
+               cb->cb_ops->rpc_release(cb);
+}
 
-       if (clnt == NULL) {
-               nfs4_put_delegation(dp);
-               return; /* Client is shutting down; give up. */
+void nfsd4_process_cb_update(struct nfsd4_callback *cb)
+{
+       struct nfs4_cb_conn conn;
+       struct nfs4_client *clp = cb->cb_clp;
+       int err;
+
+       /*
+        * This is either an update, or the client dying; in either case,
+        * kill the old client:
+        */
+       if (clp->cl_cb_client) {
+               rpc_shutdown_client(clp->cl_cb_client);
+               clp->cl_cb_client = NULL;
        }
+       if (test_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags))
+               return;
+       spin_lock(&clp->cl_lock);
+       /*
+        * Only serialized callback code is allowed to clear these
+        * flags; main nfsd code can only set them:
+        */
+       BUG_ON(!clp->cl_cb_flags);
+       clear_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags);
+       memcpy(&conn, &cb->cb_clp->cl_cb_conn, sizeof(struct nfs4_cb_conn));
+       spin_unlock(&clp->cl_lock);
 
-       args->args_op = dp;
-       msg.rpc_argp = args;
-       dp->dl_retries = 1;
-       rpc_call_async(clnt, &msg, RPC_TASK_SOFT, &nfsd4_cb_recall_ops, dp);
+       err = setup_callback_client(clp, &conn);
+       if (err)
+               warn_no_callback_path(clp, err);
 }
 
 void nfsd4_do_callback_rpc(struct work_struct *w)
 {
-       /* XXX: for now, just send off delegation recall. */
-       /* In future, generalize to handle any sort of callback. */
-       struct nfsd4_callback *c = container_of(w, struct nfsd4_callback, cb_work);
-       struct nfs4_delegation *dp = container_of(c, struct nfs4_delegation, dl_recall);
+       struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
+       struct nfs4_client *clp = cb->cb_clp;
+       struct rpc_clnt *clnt;
 
-       _nfsd4_cb_recall(dp);
-}
+       if (clp->cl_cb_flags)
+               nfsd4_process_cb_update(cb);
 
+       clnt = clp->cl_cb_client;
+       if (!clnt) {
+               /* Callback channel broken, or client killed; give up: */
+               nfsd4_release_cb(cb);
+               return;
+       }
+       rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
+                       cb->cb_ops, cb);
+}
 
 void nfsd4_cb_recall(struct nfs4_delegation *dp)
 {
+       struct nfsd4_callback *cb = &dp->dl_recall;
+
+       dp->dl_retries = 1;
+       cb->cb_op = dp;
+       cb->cb_clp = dp->dl_client;
+       cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL];
+       cb->cb_msg.rpc_argp = cb;
+       cb->cb_msg.rpc_resp = cb;
+       cb->cb_msg.rpc_cred = callback_cred;
+
+       cb->cb_ops = &nfsd4_cb_recall_ops;
+       dp->dl_retries = 1;
+
        queue_work(callback_wq, &dp->dl_recall.cb_work);
 }
index c78dbf4934247e101d6fee19dd1e3ee150957e80..f0695e815f0ec9e53a2e9934392ac820d36e442e 100644 (file)
@@ -482,109 +482,26 @@ nfsd_idmap_shutdown(void)
        cache_unregister(&nametoid_cache);
 }
 
-/*
- * Deferred request handling
- */
-
-struct idmap_defer_req {
-       struct cache_req                req;
-       struct cache_deferred_req deferred_req;
-       wait_queue_head_t       waitq;
-       atomic_t                        count;
-};
-
-static inline void
-put_mdr(struct idmap_defer_req *mdr)
-{
-       if (atomic_dec_and_test(&mdr->count))
-               kfree(mdr);
-}
-
-static inline void
-get_mdr(struct idmap_defer_req *mdr)
-{
-       atomic_inc(&mdr->count);
-}
-
-static void
-idmap_revisit(struct cache_deferred_req *dreq, int toomany)
-{
-       struct idmap_defer_req *mdr =
-               container_of(dreq, struct idmap_defer_req, deferred_req);
-
-       wake_up(&mdr->waitq);
-       put_mdr(mdr);
-}
-
-static struct cache_deferred_req *
-idmap_defer(struct cache_req *req)
-{
-       struct idmap_defer_req *mdr =
-               container_of(req, struct idmap_defer_req, req);
-
-       mdr->deferred_req.revisit = idmap_revisit;
-       get_mdr(mdr);
-       return (&mdr->deferred_req);
-}
-
-static inline int
-do_idmap_lookup(struct ent *(*lookup_fn)(struct ent *), struct ent *key,
-               struct cache_detail *detail, struct ent **item,
-               struct idmap_defer_req *mdr)
-{
-       *item = lookup_fn(key);
-       if (!*item)
-               return -ENOMEM;
-       return cache_check(detail, &(*item)->h, &mdr->req);
-}
-
-static inline int
-do_idmap_lookup_nowait(struct ent *(*lookup_fn)(struct ent *),
-                       struct ent *key, struct cache_detail *detail,
-                       struct ent **item)
-{
-       int ret = -ENOMEM;
-
-       *item = lookup_fn(key);
-       if (!*item)
-               goto out_err;
-       ret = -ETIMEDOUT;
-       if (!test_bit(CACHE_VALID, &(*item)->h.flags)
-                       || (*item)->h.expiry_time < get_seconds()
-                       || detail->flush_time > (*item)->h.last_refresh)
-               goto out_put;
-       ret = -ENOENT;
-       if (test_bit(CACHE_NEGATIVE, &(*item)->h.flags))
-               goto out_put;
-       return 0;
-out_put:
-       cache_put(&(*item)->h, detail);
-out_err:
-       *item = NULL;
-       return ret;
-}
-
 static int
 idmap_lookup(struct svc_rqst *rqstp,
                struct ent *(*lookup_fn)(struct ent *), struct ent *key,
                struct cache_detail *detail, struct ent **item)
 {
-       struct idmap_defer_req *mdr;
        int ret;
 
-       mdr = kzalloc(sizeof(*mdr), GFP_KERNEL);
-       if (!mdr)
+       *item = lookup_fn(key);
+       if (!*item)
                return -ENOMEM;
-       atomic_set(&mdr->count, 1);
-       init_waitqueue_head(&mdr->waitq);
-       mdr->req.defer = idmap_defer;
-       ret = do_idmap_lookup(lookup_fn, key, detail, item, mdr);
-       if (ret == -EAGAIN) {
-               wait_event_interruptible_timeout(mdr->waitq,
-                       test_bit(CACHE_VALID, &(*item)->h.flags), 1 * HZ);
-               ret = do_idmap_lookup_nowait(lookup_fn, key, detail, item);
+ retry:
+       ret = cache_check(detail, &(*item)->h, &rqstp->rq_chandle);
+
+       if (ret == -ETIMEDOUT) {
+               struct ent *prev_item = *item;
+               *item = lookup_fn(key);
+               if (*item != prev_item)
+                       goto retry;
+               cache_put(&(*item)->h, detail);
        }
-       put_mdr(mdr);
        return ret;
 }
 
index 59ec449b0c7fd8b61040703ee98fa51e7c40e280..0cdfd022bb7b307d344989f493b9873b903dd22d 100644 (file)
@@ -1031,8 +1031,11 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
        resp->cstate.session = NULL;
        fh_init(&resp->cstate.current_fh, NFS4_FHSIZE);
        fh_init(&resp->cstate.save_fh, NFS4_FHSIZE);
-       /* Use the deferral mechanism only for NFSv4.0 compounds */
-       rqstp->rq_usedeferral = (args->minorversion == 0);
+       /*
+        * Don't use the deferral mechanism for NFSv4; compounds make it
+        * too hard to avoid non-idempotency problems.
+        */
+       rqstp->rq_usedeferral = 0;
 
        /*
         * According to RFC3010, this takes precedence over all other errors.
index a7292fcf7718e0d1471ddcfd2dab4e3957f17e18..9019e8ec9dc861a9e3b0ff27c53bc16ab2d42cb1 100644 (file)
@@ -207,7 +207,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
 {
        struct nfs4_delegation *dp;
        struct nfs4_file *fp = stp->st_file;
-       struct nfs4_cb_conn *cb = &stp->st_stateowner->so_client->cl_cb_conn;
 
        dprintk("NFSD alloc_init_deleg\n");
        /*
@@ -234,7 +233,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
        nfs4_file_get_access(fp, O_RDONLY);
        dp->dl_flock = NULL;
        dp->dl_type = type;
-       dp->dl_ident = cb->cb_ident;
        dp->dl_stateid.si_boot = boot_time;
        dp->dl_stateid.si_stateownerid = current_delegid++;
        dp->dl_stateid.si_fileid = 0;
@@ -535,171 +533,258 @@ gen_sessionid(struct nfsd4_session *ses)
  */
 #define NFSD_MIN_HDR_SEQ_SZ  (24 + 12 + 44)
 
+static void
+free_session_slots(struct nfsd4_session *ses)
+{
+       int i;
+
+       for (i = 0; i < ses->se_fchannel.maxreqs; i++)
+               kfree(ses->se_slots[i]);
+}
+
 /*
- * Give the client the number of ca_maxresponsesize_cached slots it
- * requests, of size bounded by NFSD_SLOT_CACHE_SIZE,
- * NFSD_MAX_MEM_PER_SESSION, and nfsd_drc_max_mem. Do not allow more
- * than NFSD_MAX_SLOTS_PER_SESSION.
- *
- * If we run out of reserved DRC memory we should (up to a point)
+ * We don't actually need to cache the rpc and session headers, so we
+ * can allocate a little less for each slot:
+ */
+static inline int slot_bytes(struct nfsd4_channel_attrs *ca)
+{
+       return ca->maxresp_cached - NFSD_MIN_HDR_SEQ_SZ;
+}
+
+static int nfsd4_sanitize_slot_size(u32 size)
+{
+       size -= NFSD_MIN_HDR_SEQ_SZ; /* We don't cache the rpc header */
+       size = min_t(u32, size, NFSD_SLOT_CACHE_SIZE);
+
+       return size;
+}
+
+/*
+ * XXX: If we run out of reserved DRC memory we could (up to a point)
  * re-negotiate active sessions and reduce their slot usage to make
  * rooom for new connections. For now we just fail the create session.
  */
-static int set_forechannel_drc_size(struct nfsd4_channel_attrs *fchan)
+static int nfsd4_get_drc_mem(int slotsize, u32 num)
 {
-       int mem, size = fchan->maxresp_cached;
+       int avail;
 
-       if (fchan->maxreqs < 1)
-               return nfserr_inval;
+       num = min_t(u32, num, NFSD_MAX_SLOTS_PER_SESSION);
 
-       if (size < NFSD_MIN_HDR_SEQ_SZ)
-               size = NFSD_MIN_HDR_SEQ_SZ;
-       size -= NFSD_MIN_HDR_SEQ_SZ;
-       if (size > NFSD_SLOT_CACHE_SIZE)
-               size = NFSD_SLOT_CACHE_SIZE;
-
-       /* bound the maxreqs by NFSD_MAX_MEM_PER_SESSION */
-       mem = fchan->maxreqs * size;
-       if (mem > NFSD_MAX_MEM_PER_SESSION) {
-               fchan->maxreqs = NFSD_MAX_MEM_PER_SESSION / size;
-               if (fchan->maxreqs > NFSD_MAX_SLOTS_PER_SESSION)
-                       fchan->maxreqs = NFSD_MAX_SLOTS_PER_SESSION;
-               mem = fchan->maxreqs * size;
-       }
+       spin_lock(&nfsd_drc_lock);
+       avail = min_t(int, NFSD_MAX_MEM_PER_SESSION,
+                       nfsd_drc_max_mem - nfsd_drc_mem_used);
+       num = min_t(int, num, avail / slotsize);
+       nfsd_drc_mem_used += num * slotsize;
+       spin_unlock(&nfsd_drc_lock);
 
+       return num;
+}
+
+static void nfsd4_put_drc_mem(int slotsize, int num)
+{
        spin_lock(&nfsd_drc_lock);
-       /* bound the total session drc memory ussage */
-       if (mem + nfsd_drc_mem_used > nfsd_drc_max_mem) {
-               fchan->maxreqs = (nfsd_drc_max_mem - nfsd_drc_mem_used) / size;
-               mem = fchan->maxreqs * size;
-       }
-       nfsd_drc_mem_used += mem;
+       nfsd_drc_mem_used -= slotsize * num;
        spin_unlock(&nfsd_drc_lock);
+}
 
-       if (fchan->maxreqs == 0)
-               return nfserr_jukebox;
+static struct nfsd4_session *alloc_session(int slotsize, int numslots)
+{
+       struct nfsd4_session *new;
+       int mem, i;
 
-       fchan->maxresp_cached = size + NFSD_MIN_HDR_SEQ_SZ;
-       return 0;
+       BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot *)
+                       + sizeof(struct nfsd4_session) > PAGE_SIZE);
+       mem = numslots * sizeof(struct nfsd4_slot *);
+
+       new = kzalloc(sizeof(*new) + mem, GFP_KERNEL);
+       if (!new)
+               return NULL;
+       /* allocate each struct nfsd4_slot and data cache in one piece */
+       for (i = 0; i < numslots; i++) {
+               mem = sizeof(struct nfsd4_slot) + slotsize;
+               new->se_slots[i] = kzalloc(mem, GFP_KERNEL);
+               if (!new->se_slots[i])
+                       goto out_free;
+       }
+       return new;
+out_free:
+       while (i--)
+               kfree(new->se_slots[i]);
+       kfree(new);
+       return NULL;
 }
 
-/*
- * fchan holds the client values on input, and the server values on output
- * sv_max_mesg is the maximum payload plus one page for overhead.
- */
-static int init_forechannel_attrs(struct svc_rqst *rqstp,
-                                 struct nfsd4_channel_attrs *session_fchan,
-                                 struct nfsd4_channel_attrs *fchan)
+static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, struct nfsd4_channel_attrs *req, int numslots, int slotsize)
 {
-       int status = 0;
-       __u32   maxcount = nfsd_serv->sv_max_mesg;
+       u32 maxrpc = nfsd_serv->sv_max_mesg;
 
-       /* headerpadsz set to zero in encode routine */
+       new->maxreqs = numslots;
+       new->maxresp_cached = slotsize + NFSD_MIN_HDR_SEQ_SZ;
+       new->maxreq_sz = min_t(u32, req->maxreq_sz, maxrpc);
+       new->maxresp_sz = min_t(u32, req->maxresp_sz, maxrpc);
+       new->maxops = min_t(u32, req->maxops, NFSD_MAX_OPS_PER_COMPOUND);
+}
 
-       /* Use the client's max request and max response size if possible */
-       if (fchan->maxreq_sz > maxcount)
-               fchan->maxreq_sz = maxcount;
-       session_fchan->maxreq_sz = fchan->maxreq_sz;
+static void free_conn(struct nfsd4_conn *c)
+{
+       svc_xprt_put(c->cn_xprt);
+       kfree(c);
+}
 
-       if (fchan->maxresp_sz > maxcount)
-               fchan->maxresp_sz = maxcount;
-       session_fchan->maxresp_sz = fchan->maxresp_sz;
+static void nfsd4_conn_lost(struct svc_xpt_user *u)
+{
+       struct nfsd4_conn *c = container_of(u, struct nfsd4_conn, cn_xpt_user);
+       struct nfs4_client *clp = c->cn_session->se_client;
 
-       /* Use the client's maxops if possible */
-       if (fchan->maxops > NFSD_MAX_OPS_PER_COMPOUND)
-               fchan->maxops = NFSD_MAX_OPS_PER_COMPOUND;
-       session_fchan->maxops = fchan->maxops;
+       spin_lock(&clp->cl_lock);
+       if (!list_empty(&c->cn_persession)) {
+               list_del(&c->cn_persession);
+               free_conn(c);
+       }
+       spin_unlock(&clp->cl_lock);
+}
 
-       /* FIXME: Error means no more DRC pages so the server should
-        * recover pages from existing sessions. For now fail session
-        * creation.
-        */
-       status = set_forechannel_drc_size(fchan);
+static struct nfsd4_conn *alloc_conn(struct svc_rqst *rqstp, u32 flags)
+{
+       struct nfsd4_conn *conn;
 
-       session_fchan->maxresp_cached = fchan->maxresp_cached;
-       session_fchan->maxreqs = fchan->maxreqs;
+       conn = kmalloc(sizeof(struct nfsd4_conn), GFP_KERNEL);
+       if (!conn)
+               return NULL;
+       svc_xprt_get(rqstp->rq_xprt);
+       conn->cn_xprt = rqstp->rq_xprt;
+       conn->cn_flags = flags;
+       INIT_LIST_HEAD(&conn->cn_xpt_user.list);
+       return conn;
+}
 
-       dprintk("%s status %d\n", __func__, status);
-       return status;
+static void __nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses)
+{
+       conn->cn_session = ses;
+       list_add(&conn->cn_persession, &ses->se_conns);
 }
 
-static void
-free_session_slots(struct nfsd4_session *ses)
+static void nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses)
 {
-       int i;
+       struct nfs4_client *clp = ses->se_client;
 
-       for (i = 0; i < ses->se_fchannel.maxreqs; i++)
-               kfree(ses->se_slots[i]);
+       spin_lock(&clp->cl_lock);
+       __nfsd4_hash_conn(conn, ses);
+       spin_unlock(&clp->cl_lock);
 }
 
-/*
- * We don't actually need to cache the rpc and session headers, so we
- * can allocate a little less for each slot:
- */
-static inline int slot_bytes(struct nfsd4_channel_attrs *ca)
+static void nfsd4_register_conn(struct nfsd4_conn *conn)
 {
-       return ca->maxresp_cached - NFSD_MIN_HDR_SEQ_SZ;
+       conn->cn_xpt_user.callback = nfsd4_conn_lost;
+       register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user);
 }
 
-static int
-alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp,
-                  struct nfsd4_create_session *cses)
+static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
 {
-       struct nfsd4_session *new, tmp;
-       struct nfsd4_slot *sp;
-       int idx, slotsize, cachesize, i;
-       int status;
+       struct nfsd4_conn *conn;
+       u32 flags = NFS4_CDFC4_FORE;
 
-       memset(&tmp, 0, sizeof(tmp));
+       if (ses->se_flags & SESSION4_BACK_CHAN)
+               flags |= NFS4_CDFC4_BACK;
+       conn = alloc_conn(rqstp, flags);
+       if (!conn)
+               return nfserr_jukebox;
+       nfsd4_hash_conn(conn, ses);
+       nfsd4_register_conn(conn);
+       return nfs_ok;
+}
 
-       /* FIXME: For now, we just accept the client back channel attributes. */
-       tmp.se_bchannel = cses->back_channel;
-       status = init_forechannel_attrs(rqstp, &tmp.se_fchannel,
-                                       &cses->fore_channel);
-       if (status)
-               goto out;
+static void nfsd4_del_conns(struct nfsd4_session *s)
+{
+       struct nfs4_client *clp = s->se_client;
+       struct nfsd4_conn *c;
 
-       BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot)
-                    + sizeof(struct nfsd4_session) > PAGE_SIZE);
+       spin_lock(&clp->cl_lock);
+       while (!list_empty(&s->se_conns)) {
+               c = list_first_entry(&s->se_conns, struct nfsd4_conn, cn_persession);
+               list_del_init(&c->cn_persession);
+               spin_unlock(&clp->cl_lock);
 
-       status = nfserr_jukebox;
-       /* allocate struct nfsd4_session and slot table pointers in one piece */
-       slotsize = tmp.se_fchannel.maxreqs * sizeof(struct nfsd4_slot *);
-       new = kzalloc(sizeof(*new) + slotsize, GFP_KERNEL);
-       if (!new)
-               goto out;
+               unregister_xpt_user(c->cn_xprt, &c->cn_xpt_user);
+               free_conn(c);
 
-       memcpy(new, &tmp, sizeof(*new));
+               spin_lock(&clp->cl_lock);
+       }
+       spin_unlock(&clp->cl_lock);
+}
 
-       /* allocate each struct nfsd4_slot and data cache in one piece */
-       cachesize = slot_bytes(&new->se_fchannel);
-       for (i = 0; i < new->se_fchannel.maxreqs; i++) {
-               sp = kzalloc(sizeof(*sp) + cachesize, GFP_KERNEL);
-               if (!sp)
-                       goto out_free;
-               new->se_slots[i] = sp;
+void free_session(struct kref *kref)
+{
+       struct nfsd4_session *ses;
+       int mem;
+
+       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);
+}
+
+static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses)
+{
+       struct nfsd4_session *new;
+       struct nfsd4_channel_attrs *fchan = &cses->fore_channel;
+       int numslots, slotsize;
+       int status;
+       int idx;
+
+       /*
+        * Note decreasing slot size below client's request may
+        * make it difficult for client to function correctly, whereas
+        * decreasing the number of slots will (just?) affect
+        * performance.  When short on memory we therefore prefer to
+        * decrease number of slots instead of their size.
+        */
+       slotsize = nfsd4_sanitize_slot_size(fchan->maxresp_cached);
+       numslots = nfsd4_get_drc_mem(slotsize, fchan->maxreqs);
+
+       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);
 
        new->se_client = clp;
        gen_sessionid(new);
-       idx = hash_sessionid(&new->se_sessionid);
-       memcpy(clp->cl_sessionid.data, new->se_sessionid.data,
-              NFS4_MAX_SESSIONID_LEN);
 
+       INIT_LIST_HEAD(&new->se_conns);
+
+       new->se_cb_seq_nr = 1;
        new->se_flags = cses->flags;
+       new->se_cb_prog = cses->callback_prog;
        kref_init(&new->se_ref);
+       idx = hash_sessionid(&new->se_sessionid);
        spin_lock(&client_lock);
        list_add(&new->se_hash, &sessionid_hashtbl[idx]);
        list_add(&new->se_perclnt, &clp->cl_sessions);
        spin_unlock(&client_lock);
 
-       status = nfs_ok;
-out:
-       return status;
-out_free:
-       free_session_slots(new);
-       kfree(new);
-       goto out;
+       status = nfsd4_new_conn(rqstp, new);
+       /* whoops: benny points out, status is ignored! (err, or bogus) */
+       if (status) {
+               free_session(&new->se_ref);
+               return NULL;
+       }
+       if (!clp->cl_cb_session && (cses->flags & SESSION4_BACK_CHAN)) {
+               struct sockaddr *sa = svc_addr(rqstp);
+
+               clp->cl_cb_session = new;
+               clp->cl_cb_conn.cb_xprt = rqstp->rq_xprt;
+               svc_xprt_get(rqstp->rq_xprt);
+               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;
 }
 
 /* caller must hold client_lock */
@@ -731,21 +816,6 @@ unhash_session(struct nfsd4_session *ses)
        list_del(&ses->se_perclnt);
 }
 
-void
-free_session(struct kref *kref)
-{
-       struct nfsd4_session *ses;
-       int mem;
-
-       ses = container_of(kref, struct nfsd4_session, se_ref);
-       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);
-}
-
 /* must be called under the client_lock */
 static inline void
 renew_client_locked(struct nfs4_client *clp)
@@ -812,6 +882,13 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
 static inline void
 free_client(struct nfs4_client *clp)
 {
+       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);
+       }
        if (clp->cl_cred.cr_group_info)
                put_group_info(clp->cl_cred.cr_group_info);
        kfree(clp->cl_principal);
@@ -838,15 +915,12 @@ release_session_client(struct nfsd4_session *session)
 static inline void
 unhash_client_locked(struct nfs4_client *clp)
 {
+       struct nfsd4_session *ses;
+
        mark_client_expired(clp);
        list_del(&clp->cl_lru);
-       while (!list_empty(&clp->cl_sessions)) {
-               struct nfsd4_session  *ses;
-               ses = list_entry(clp->cl_sessions.next, struct nfsd4_session,
-                                se_perclnt);
-               unhash_session(ses);
-               nfsd4_put_session(ses);
-       }
+       list_for_each_entry(ses, &clp->cl_sessions, se_perclnt)
+               list_del_init(&ses->se_hash);
 }
 
 static void
@@ -875,7 +949,7 @@ expire_client(struct nfs4_client *clp)
                sop = list_entry(clp->cl_openowners.next, struct nfs4_stateowner, so_perclient);
                release_openowner(sop);
        }
-       nfsd4_set_callback_client(clp, NULL);
+       nfsd4_shutdown_callback(clp);
        if (clp->cl_cb_conn.cb_xprt)
                svc_xprt_put(clp->cl_cb_conn.cb_xprt);
        list_del(&clp->cl_idhash);
@@ -960,6 +1034,8 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
        if (clp == NULL)
                return NULL;
 
+       INIT_LIST_HEAD(&clp->cl_sessions);
+
        princ = svc_gss_principal(rqstp);
        if (princ) {
                clp->cl_principal = kstrdup(princ, GFP_KERNEL);
@@ -976,8 +1052,9 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
        INIT_LIST_HEAD(&clp->cl_strhash);
        INIT_LIST_HEAD(&clp->cl_openowners);
        INIT_LIST_HEAD(&clp->cl_delegations);
-       INIT_LIST_HEAD(&clp->cl_sessions);
        INIT_LIST_HEAD(&clp->cl_lru);
+       spin_lock_init(&clp->cl_lock);
+       INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc);
        clp->cl_time = get_seconds();
        clear_bit(0, &clp->cl_cb_slot_busy);
        rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
@@ -986,7 +1063,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
        clp->cl_flavor = rqstp->rq_flavor;
        copy_cred(&clp->cl_cred, &rqstp->rq_cred);
        gen_confirm(clp);
-
+       clp->cl_cb_session = NULL;
        return clp;
 }
 
@@ -1098,7 +1175,7 @@ find_unconfirmed_client_by_str(const char *dname, unsigned int hashval,
 static void
 gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, u32 scopeid)
 {
-       struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
+       struct nfs4_cb_conn *conn = &clp->cl_cb_conn;
        unsigned short expected_family;
 
        /* Currently, we only support tcp and tcp6 for the callback channel */
@@ -1111,24 +1188,23 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, u32 scopeid)
        else
                goto out_err;
 
-       cb->cb_addrlen = rpc_uaddr2sockaddr(se->se_callback_addr_val,
+       conn->cb_addrlen = rpc_uaddr2sockaddr(se->se_callback_addr_val,
                                            se->se_callback_addr_len,
-                                           (struct sockaddr *) &cb->cb_addr,
-                                           sizeof(cb->cb_addr));
+                                           (struct sockaddr *)&conn->cb_addr,
+                                           sizeof(conn->cb_addr));
 
-       if (!cb->cb_addrlen || cb->cb_addr.ss_family != expected_family)
+       if (!conn->cb_addrlen || conn->cb_addr.ss_family != expected_family)
                goto out_err;
 
-       if (cb->cb_addr.ss_family == AF_INET6)
-               ((struct sockaddr_in6 *) &cb->cb_addr)->sin6_scope_id = scopeid;
+       if (conn->cb_addr.ss_family == AF_INET6)
+               ((struct sockaddr_in6 *)&conn->cb_addr)->sin6_scope_id = scopeid;
 
-       cb->cb_minorversion = 0;
-       cb->cb_prog = se->se_callback_prog;
-       cb->cb_ident = se->se_callback_ident;
+       conn->cb_prog = se->se_callback_prog;
+       conn->cb_ident = se->se_callback_ident;
        return;
 out_err:
-       cb->cb_addr.ss_family = AF_UNSPEC;
-       cb->cb_addrlen = 0;
+       conn->cb_addr.ss_family = AF_UNSPEC;
+       conn->cb_addrlen = 0;
        dprintk(KERN_INFO "NFSD: this client (clientid %08x/%08x) "
                "will not receive delegations\n",
                clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
@@ -1415,7 +1491,9 @@ nfsd4_create_session(struct svc_rqst *rqstp,
 {
        struct sockaddr *sa = svc_addr(rqstp);
        struct nfs4_client *conf, *unconf;
+       struct nfsd4_session *new;
        struct nfsd4_clid_slot *cs_slot = NULL;
+       bool confirm_me = false;
        int status = 0;
 
        nfs4_lock_state();
@@ -1438,7 +1516,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
                                cs_slot->sl_seqid, cr_ses->seqid);
                        goto out;
                }
-               cs_slot->sl_seqid++;
        } else if (unconf) {
                if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) ||
                    !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) {
@@ -1451,48 +1528,41 @@ nfsd4_create_session(struct svc_rqst *rqstp,
                if (status) {
                        /* an unconfirmed replay returns misordered */
                        status = nfserr_seq_misordered;
-                       goto out_cache;
+                       goto out;
                }
 
-               cs_slot->sl_seqid++; /* from 0 to 1 */
-               move_to_confirmed(unconf);
-
-               if (cr_ses->flags & SESSION4_BACK_CHAN) {
-                       unconf->cl_cb_conn.cb_xprt = rqstp->rq_xprt;
-                       svc_xprt_get(rqstp->rq_xprt);
-                       rpc_copy_addr(
-                               (struct sockaddr *)&unconf->cl_cb_conn.cb_addr,
-                               sa);
-                       unconf->cl_cb_conn.cb_addrlen = svc_addr_len(sa);
-                       unconf->cl_cb_conn.cb_minorversion =
-                               cstate->minorversion;
-                       unconf->cl_cb_conn.cb_prog = cr_ses->callback_prog;
-                       unconf->cl_cb_seq_nr = 1;
-                       nfsd4_probe_callback(unconf, &unconf->cl_cb_conn);
-               }
+               confirm_me = true;
                conf = unconf;
        } else {
                status = nfserr_stale_clientid;
                goto out;
        }
 
+       /*
+        * XXX: we should probably set this at creation time, and check
+        * for consistent minorversion use throughout:
+        */
+       conf->cl_minorversion = 1;
        /*
         * We do not support RDMA or persistent sessions
         */
        cr_ses->flags &= ~SESSION4_PERSIST;
        cr_ses->flags &= ~SESSION4_RDMA;
 
-       status = alloc_init_session(rqstp, conf, cr_ses);
-       if (status)
+       status = nfserr_jukebox;
+       new = alloc_init_session(rqstp, conf, cr_ses);
+       if (!new)
                goto out;
-
-       memcpy(cr_ses->sessionid.data, conf->cl_sessionid.data,
+       status = nfs_ok;
+       memcpy(cr_ses->sessionid.data, new->se_sessionid.data,
               NFS4_MAX_SESSIONID_LEN);
+       cs_slot->sl_seqid++;
        cr_ses->seqid = cs_slot->sl_seqid;
 
-out_cache:
        /* cache solo and embedded create sessions under the state lock */
        nfsd4_cache_create_session(cr_ses, cs_slot, status);
+       if (confirm_me)
+               move_to_confirmed(conf);
 out:
        nfs4_unlock_state();
        dprintk("%s returns %d\n", __func__, ntohl(status));
@@ -1546,8 +1616,11 @@ nfsd4_destroy_session(struct svc_rqst *r,
 
        nfs4_lock_state();
        /* wait for callbacks */
-       nfsd4_set_callback_client(ses->se_client, NULL);
+       nfsd4_shutdown_callback(ses->se_client);
        nfs4_unlock_state();
+
+       nfsd4_del_conns(ses);
+
        nfsd4_put_session(ses);
        status = nfs_ok;
 out:
@@ -1555,6 +1628,36 @@ out:
        return status;
 }
 
+static struct nfsd4_conn *__nfsd4_find_conn(struct svc_xprt *xpt, struct nfsd4_session *s)
+{
+       struct nfsd4_conn *c;
+
+       list_for_each_entry(c, &s->se_conns, cn_persession) {
+               if (c->cn_xprt == xpt) {
+                       return c;
+               }
+       }
+       return NULL;
+}
+
+static void nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_session *ses)
+{
+       struct nfs4_client *clp = ses->se_client;
+       struct nfsd4_conn *c;
+
+       spin_lock(&clp->cl_lock);
+       c = __nfsd4_find_conn(new->cn_xprt, ses);
+       if (c) {
+               spin_unlock(&clp->cl_lock);
+               free_conn(new);
+               return;
+       }
+       __nfsd4_hash_conn(new, ses);
+       spin_unlock(&clp->cl_lock);
+       nfsd4_register_conn(new);
+       return;
+}
+
 __be32
 nfsd4_sequence(struct svc_rqst *rqstp,
               struct nfsd4_compound_state *cstate,
@@ -1563,11 +1666,20 @@ nfsd4_sequence(struct svc_rqst *rqstp,
        struct nfsd4_compoundres *resp = rqstp->rq_resp;
        struct nfsd4_session *session;
        struct nfsd4_slot *slot;
+       struct nfsd4_conn *conn;
        int status;
 
        if (resp->opcnt != 1)
                return nfserr_sequence_pos;
 
+       /*
+        * Will be either used or freed by nfsd4_sequence_check_conn
+        * below.
+        */
+       conn = alloc_conn(rqstp, NFS4_CDFC4_FORE);
+       if (!conn)
+               return nfserr_jukebox;
+
        spin_lock(&client_lock);
        status = nfserr_badsession;
        session = find_in_sessionid_hashtbl(&seq->sessionid);
@@ -1599,6 +1711,9 @@ nfsd4_sequence(struct svc_rqst *rqstp,
        if (status)
                goto out;
 
+       nfsd4_sequence_check_conn(conn, session);
+       conn = NULL;
+
        /* Success! bump slot seqid */
        slot->sl_inuse = true;
        slot->sl_seqid = seq->seqid;
@@ -1613,6 +1728,7 @@ out:
                nfsd4_get_session(cstate->session);
                atomic_inc(&session->se_client->cl_refcount);
        }
+       kfree(conn);
        spin_unlock(&client_lock);
        dprintk("%s: return %d\n", __func__, ntohl(status));
        return status;
@@ -1747,6 +1863,11 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                        goto out;
                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, rpc_get_scope_id(sa));
        add_to_unconfirmed(new, strhashval);
        setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot;
@@ -1807,7 +1928,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
                        status = nfserr_clid_inuse;
                else {
                        atomic_set(&conf->cl_cb_set, 0);
-                       nfsd4_probe_callback(conf, &unconf->cl_cb_conn);
+                       nfsd4_change_callback(conf, &unconf->cl_cb_conn);
+                       nfsd4_probe_callback(conf);
                        expire_client(unconf);
                        status = nfs_ok;
 
@@ -1841,7 +1963,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
                        }
                        move_to_confirmed(unconf);
                        conf = unconf;
-                       nfsd4_probe_callback(conf, &conf->cl_cb_conn);
+                       nfsd4_probe_callback(conf);
                        status = nfs_ok;
                }
        } else if ((!conf || (conf && !same_verf(&conf->cl_confirm, &confirm)))
@@ -2944,7 +3066,11 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
        if (STALE_STATEID(stateid)) 
                goto out;
 
-       status = nfserr_bad_stateid;
+       /*
+        * We assume that any stateid that has the current boot time,
+        * but that we can't find, is expired:
+        */
+       status = nfserr_expired;
        if (is_delegation_stateid(stateid)) {
                dp = find_delegation_stateid(ino, stateid);
                if (!dp)
@@ -2964,6 +3090,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
                stp = find_stateid(stateid, flags);
                if (!stp)
                        goto out;
+               status = nfserr_bad_stateid;
                if (nfs4_check_fh(current_fh, stp))
                        goto out;
                if (!stp->st_stateowner->so_confirmed)
@@ -3038,8 +3165,9 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
                 * a replayed close:
                 */
                sop = search_close_lru(stateid->si_stateownerid, flags);
+               /* It's not stale; let's assume it's expired: */
                if (sop == NULL)
-                       return nfserr_bad_stateid;
+                       return nfserr_expired;
                *sopp = sop;
                goto check_replay;
        }
@@ -3304,6 +3432,7 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        status = nfserr_bad_stateid;
        if (!is_delegation_stateid(stateid))
                goto out;
+       status = nfserr_expired;
        dp = find_delegation_stateid(inode, stateid);
        if (!dp)
                goto out;
index 1a468bbd330f48410f62a74272c1f4c431755169..f35a94a0402677d36193c9fcb57992edcc061e9d 100644 (file)
@@ -1805,19 +1805,23 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                                goto out_nfserr;
                }
        }
-       if ((buflen -= 16) < 0)
-               goto out_resource;
 
-       if (unlikely(bmval2)) {
+       if (bmval2) {
+               if ((buflen -= 16) < 0)
+                       goto out_resource;
                WRITE32(3);
                WRITE32(bmval0);
                WRITE32(bmval1);
                WRITE32(bmval2);
-       } else if (likely(bmval1)) {
+       } else if (bmval1) {
+               if ((buflen -= 12) < 0)
+                       goto out_resource;
                WRITE32(2);
                WRITE32(bmval0);
                WRITE32(bmval1);
        } else {
+               if ((buflen -= 8) < 0)
+                       goto out_resource;
                WRITE32(1);
                WRITE32(bmval0);
        }
@@ -1828,15 +1832,17 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                u32 word1 = nfsd_suppattrs1(minorversion);
                u32 word2 = nfsd_suppattrs2(minorversion);
 
-               if ((buflen -= 12) < 0)
-                       goto out_resource;
                if (!aclsupport)
                        word0 &= ~FATTR4_WORD0_ACL;
                if (!word2) {
+                       if ((buflen -= 12) < 0)
+                               goto out_resource;
                        WRITE32(2);
                        WRITE32(word0);
                        WRITE32(word1);
                } else {
+                       if ((buflen -= 16) < 0)
+                               goto out_resource;
                        WRITE32(3);
                        WRITE32(word0);
                        WRITE32(word1);
index 06fa87e52e8243cc46a41d5f861aa91388581d7b..d6dc3f61f8bafdd77e3a592b26a543e42ae7d1a9 100644 (file)
@@ -22,6 +22,7 @@
  */
 enum {
        NFSD_Root = 1,
+#ifdef CONFIG_NFSD_DEPRECATED
        NFSD_Svc,
        NFSD_Add,
        NFSD_Del,
@@ -29,6 +30,7 @@ enum {
        NFSD_Unexport,
        NFSD_Getfd,
        NFSD_Getfs,
+#endif
        NFSD_List,
        NFSD_Export_features,
        NFSD_Fh,
@@ -54,6 +56,7 @@ enum {
 /*
  * write() for these nodes.
  */
+#ifdef CONFIG_NFSD_DEPRECATED
 static ssize_t write_svc(struct file *file, char *buf, size_t size);
 static ssize_t write_add(struct file *file, char *buf, size_t size);
 static ssize_t write_del(struct file *file, char *buf, size_t size);
@@ -61,6 +64,7 @@ static ssize_t write_export(struct file *file, char *buf, size_t size);
 static ssize_t write_unexport(struct file *file, char *buf, size_t size);
 static ssize_t write_getfd(struct file *file, char *buf, size_t size);
 static ssize_t write_getfs(struct file *file, char *buf, size_t size);
+#endif
 static ssize_t write_filehandle(struct file *file, char *buf, size_t size);
 static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size);
 static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size);
@@ -76,6 +80,7 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size);
 #endif
 
 static ssize_t (*write_op[])(struct file *, char *, size_t) = {
+#ifdef CONFIG_NFSD_DEPRECATED
        [NFSD_Svc] = write_svc,
        [NFSD_Add] = write_add,
        [NFSD_Del] = write_del,
@@ -83,6 +88,7 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = {
        [NFSD_Unexport] = write_unexport,
        [NFSD_Getfd] = write_getfd,
        [NFSD_Getfs] = write_getfs,
+#endif
        [NFSD_Fh] = write_filehandle,
        [NFSD_FO_UnlockIP] = write_unlock_ip,
        [NFSD_FO_UnlockFS] = write_unlock_fs,
@@ -121,6 +127,14 @@ static ssize_t nfsctl_transaction_write(struct file *file, const char __user *bu
 
 static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
 {
+       static int warned;
+       if (file->f_dentry->d_name.name[0] == '.' && !warned) {
+               printk(KERN_INFO
+                      "Warning: \"%s\" uses deprecated NFSD interface: %s."
+                      "  This will be removed in 2.6.40\n",
+                      current->comm, file->f_dentry->d_name.name);
+               warned = 1;
+       }
        if (! file->private_data) {
                /* An attempt to read a transaction file without writing
                 * causes a 0-byte write so that the file can return
@@ -187,6 +201,7 @@ static const struct file_operations pool_stats_operations = {
  * payload - write methods
  */
 
+#ifdef CONFIG_NFSD_DEPRECATED
 /**
  * write_svc - Start kernel's NFSD server
  *
@@ -402,7 +417,7 @@ static ssize_t write_getfs(struct file *file, char *buf, size_t size)
 
        ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
 
-       clp = auth_unix_lookup(&in6);
+       clp = auth_unix_lookup(&init_net, &in6);
        if (!clp)
                err = -EPERM;
        else {
@@ -465,7 +480,7 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size)
 
        ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6);
 
-       clp = auth_unix_lookup(&in6);
+       clp = auth_unix_lookup(&init_net, &in6);
        if (!clp)
                err = -EPERM;
        else {
@@ -482,6 +497,7 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size)
  out:
        return err;
 }
+#endif /* CONFIG_NFSD_DEPRECATED */
 
 /**
  * write_unlock_ip - Release all locks used by a client
@@ -1000,12 +1016,12 @@ static ssize_t __write_ports_addxprt(char *buf)
        if (err != 0)
                return err;
 
-       err = svc_create_xprt(nfsd_serv, transport,
+       err = svc_create_xprt(nfsd_serv, transport, &init_net,
                                PF_INET, port, SVC_SOCK_ANONYMOUS);
        if (err < 0)
                goto out_err;
 
-       err = svc_create_xprt(nfsd_serv, transport,
+       err = svc_create_xprt(nfsd_serv, transport, &init_net,
                                PF_INET6, port, SVC_SOCK_ANONYMOUS);
        if (err < 0 && err != -EAFNOSUPPORT)
                goto out_close;
@@ -1356,6 +1372,7 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
 static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
 {
        static struct tree_descr nfsd_files[] = {
+#ifdef CONFIG_NFSD_DEPRECATED
                [NFSD_Svc] = {".svc", &transaction_ops, S_IWUSR},
                [NFSD_Add] = {".add", &transaction_ops, S_IWUSR},
                [NFSD_Del] = {".del", &transaction_ops, S_IWUSR},
@@ -1363,6 +1380,7 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent)
                [NFSD_Unexport] = {".unexport", &transaction_ops, S_IWUSR},
                [NFSD_Getfd] = {".getfd", &transaction_ops, S_IWUSR|S_IRUSR},
                [NFSD_Getfs] = {".getfs", &transaction_ops, S_IWUSR|S_IRUSR},
+#endif
                [NFSD_List] = {"exports", &exports_operations, S_IRUGO},
                [NFSD_Export_features] = {"export_features",
                                        &export_features_operations, S_IRUGO},
index b76ac3a82e39f14438f2b3c5c886fe466b84a45d..6b641cf2c19ac59a6f65326c40038e2549c6df83 100644 (file)
@@ -249,7 +249,7 @@ extern time_t nfsd4_grace;
 #define        COMPOUND_SLACK_SPACE            140    /* OP_GETFH */
 #define COMPOUND_ERR_SLACK_SPACE       12     /* OP_SETATTR */
 
-#define NFSD_LAUNDROMAT_MINTIMEOUT      10   /* seconds */
+#define NFSD_LAUNDROMAT_MINTIMEOUT      1   /* seconds */
 
 /*
  * The following attributes are currently not supported by the NFSv4 server:
index e2c43464f23764405fef5e525e3829777be1a1a7..2bae1d86f5f241b8d62a61bf9ed8b5a0bcfc9c8e 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/lockd/bind.h>
 #include <linux/nfsacl.h>
 #include <linux/seq_file.h>
+#include <net/net_namespace.h>
 #include "nfsd.h"
 #include "cache.h"
 #include "vfs.h"
@@ -186,12 +187,12 @@ static int nfsd_init_socks(int port)
        if (!list_empty(&nfsd_serv->sv_permsocks))
                return 0;
 
-       error = svc_create_xprt(nfsd_serv, "udp", PF_INET, port,
+       error = svc_create_xprt(nfsd_serv, "udp", &init_net, PF_INET, port,
                                        SVC_SOCK_DEFAULTS);
        if (error < 0)
                return error;
 
-       error = svc_create_xprt(nfsd_serv, "tcp", PF_INET, port,
+       error = svc_create_xprt(nfsd_serv, "tcp", &init_net, PF_INET, port,
                                        SVC_SOCK_DEFAULTS);
        if (error < 0)
                return error;
index 322518c88e4b09eee40473ed8efaece99e02410b..39adc27b06853c37a40a1e951ff5027625a15b52 100644 (file)
@@ -35,6 +35,7 @@
 #ifndef _NFSD4_STATE_H
 #define _NFSD4_STATE_H
 
+#include <linux/sunrpc/svc_xprt.h>
 #include <linux/nfsd/nfsfh.h>
 #include "nfsfh.h"
 
@@ -64,19 +65,12 @@ typedef struct {
        (s)->si_fileid, \
        (s)->si_generation
 
-struct nfsd4_cb_sequence {
-       /* args/res */
-       u32                     cbs_minorversion;
-       struct nfs4_client      *cbs_clp;
-};
-
-struct nfs4_rpc_args {
-       void                            *args_op;
-       struct nfsd4_cb_sequence        args_seq;
-};
-
 struct nfsd4_callback {
-       struct nfs4_rpc_args cb_args;
+       void *cb_op;
+       struct nfs4_client *cb_clp;
+       u32 cb_minorversion;
+       struct rpc_message cb_msg;
+       const struct rpc_call_ops *cb_ops;
        struct work_struct cb_work;
 };
 
@@ -91,7 +85,6 @@ struct nfs4_delegation {
        u32                     dl_type;
        time_t                  dl_time;
 /* For recall: */
-       u32                     dl_ident;
        stateid_t               dl_stateid;
        struct knfsd_fh         dl_fh;
        int                     dl_retries;
@@ -103,8 +96,8 @@ struct nfs4_cb_conn {
        /* SETCLIENTID info */
        struct sockaddr_storage cb_addr;
        size_t                  cb_addrlen;
-       u32                     cb_prog;
-       u32                     cb_minorversion;
+       u32                     cb_prog; /* used only in 4.0 case;
+                                           per-session otherwise */
        u32                     cb_ident;       /* minorversion 0 only */
        struct svc_xprt         *cb_xprt;       /* minorversion 1 only */
 };
@@ -160,6 +153,15 @@ struct nfsd4_clid_slot {
        struct nfsd4_create_session     sl_cr_ses;
 };
 
+struct nfsd4_conn {
+       struct list_head cn_persession;
+       struct svc_xprt *cn_xprt;
+       struct svc_xpt_user cn_xpt_user;
+       struct nfsd4_session *cn_session;
+/* CDFC4_FORE, CDFC4_BACK: */
+       unsigned char cn_flags;
+};
+
 struct nfsd4_session {
        struct kref             se_ref;
        struct list_head        se_hash;        /* hash by sessionid */
@@ -169,6 +171,9 @@ struct nfsd4_session {
        struct nfs4_sessionid   se_sessionid;
        struct nfsd4_channel_attrs se_fchannel;
        struct nfsd4_channel_attrs se_bchannel;
+       struct list_head        se_conns;
+       u32                     se_cb_prog;
+       u32                     se_cb_seq_nr;
        struct nfsd4_slot       *se_slots[];    /* forward channel slots */
 };
 
@@ -221,24 +226,32 @@ struct nfs4_client {
        clientid_t              cl_clientid;    /* generated by server */
        nfs4_verifier           cl_confirm;     /* generated by server */
        u32                     cl_firststate;  /* recovery dir creation */
+       u32                     cl_minorversion;
 
        /* for v4.0 and v4.1 callbacks: */
        struct nfs4_cb_conn     cl_cb_conn;
+#define NFSD4_CLIENT_CB_UPDATE 1
+#define NFSD4_CLIENT_KILL      2
+       unsigned long           cl_cb_flags;
        struct rpc_clnt         *cl_cb_client;
+       u32                     cl_cb_ident;
        atomic_t                cl_cb_set;
+       struct nfsd4_callback   cl_cb_null;
+       struct nfsd4_session    *cl_cb_session;
+
+       /* for all client information that callback code might need: */
+       spinlock_t              cl_lock;
 
        /* for nfs41 */
        struct list_head        cl_sessions;
        struct nfsd4_clid_slot  cl_cs_slot;     /* create_session slot */
        u32                     cl_exchange_flags;
-       struct nfs4_sessionid   cl_sessionid;
        /* number of rpc's in progress over an associated session: */
        atomic_t                cl_refcount;
 
        /* for nfs41 callbacks */
        /* We currently support a single back channel with a single slot */
        unsigned long           cl_cb_slot_busy;
-       u32                     cl_cb_seq_nr;
        struct rpc_wait_queue   cl_cb_waitq;    /* backchannel callers may */
                                                /* wait here for slots */
 };
@@ -440,12 +453,13 @@ extern int nfs4_in_grace(void);
 extern __be32 nfs4_check_open_reclaim(clientid_t *clid);
 extern void nfs4_free_stateowner(struct kref *kref);
 extern int set_callback_cred(void);
-extern void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *);
+extern void nfsd4_probe_callback(struct nfs4_client *clp);
+extern void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *);
 extern void nfsd4_do_callback_rpc(struct work_struct *);
 extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
 extern int nfsd4_create_callback_queue(void);
 extern void nfsd4_destroy_callback_queue(void);
-extern void nfsd4_set_callback_client(struct nfs4_client *, struct rpc_clnt *);
+extern void nfsd4_shutdown_callback(struct nfs4_client *);
 extern void nfs4_put_delegation(struct nfs4_delegation *dp);
 extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname);
 extern void nfsd4_init_recdir(char *recdir_name);
index dee0b11a87590b31750706fec2bfe93bfec7d2bf..16faa130088c298fe8540b7fa29d365d0ea579b7 100644 (file)
@@ -229,6 +229,8 @@ enum {
 extern int          sock_wake_async(struct socket *sk, int how, int band);
 extern int          sock_register(const struct net_proto_family *fam);
 extern void         sock_unregister(int family);
+extern int          __sock_create(struct net *net, int family, int type, int proto,
+                                struct socket **res, int kern);
 extern int          sock_create(int family, int type, int proto,
                                 struct socket **res);
 extern int          sock_create_kern(int family, int type, int proto,
index a9683d6acaa49f9fb16201b16302e69498cad521..4925b22219d25cfa654aa9d515950acc36d977f4 100644 (file)
@@ -63,6 +63,9 @@
 #define NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL       0x10000
 #define NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED         0x20000
 
+#define NFS4_CDFC4_FORE        0x1
+#define NFS4_CDFC4_BACK 0x2
+
 #define NFS4_SET_TO_SERVER_TIME        0
 #define NFS4_SET_TO_CLIENT_TIME        1
 
index 5bbc447175dce88239097002e403a7957d4e42a2..b2024757edd5a7ca2d15d5bb4e149ebe8205f7eb 100644 (file)
@@ -122,8 +122,8 @@ extern const struct rpc_authops     authnull_ops;
 int __init             rpc_init_authunix(void);
 int __init             rpc_init_generic_auth(void);
 int __init             rpcauth_init_module(void);
-void __exit            rpcauth_remove_module(void);
-void __exit            rpc_destroy_generic_auth(void);
+void                   rpcauth_remove_module(void);
+void                   rpc_destroy_generic_auth(void);
 void                   rpc_destroy_authunix(void);
 
 struct rpc_cred *      rpc_lookup_cred(void);
index 7bf3e84b92f43c1e331040b735bf3faff67fd697..6950c981882dfa4c30247e75fa93758145d0d2a9 100644 (file)
@@ -125,12 +125,15 @@ struct cache_detail {
  */
 struct cache_req {
        struct cache_deferred_req *(*defer)(struct cache_req *req);
+       int thread_wait;  /* How long (jiffies) we can block the
+                          * current thread to wait for updates.
+                          */
 };
 /* this must be embedded in a deferred_request that is being
  * delayed awaiting cache-fill
  */
 struct cache_deferred_req {
-       struct list_head        hash;   /* on hash chain */
+       struct hlist_node       hash;   /* on hash chain */
        struct list_head        recent; /* on fifo */
        struct cache_head       *item;  /* cache item we wait on */
        void                    *owner; /* we might need to discard all defered requests
@@ -194,7 +197,9 @@ extern void cache_purge(struct cache_detail *detail);
 #define NEVER (0x7FFFFFFF)
 extern void __init cache_initialize(void);
 extern int cache_register(struct cache_detail *cd);
+extern int cache_register_net(struct cache_detail *cd, struct net *net);
 extern void cache_unregister(struct cache_detail *cd);
+extern void cache_unregister_net(struct cache_detail *cd, struct net *net);
 
 extern int sunrpc_cache_register_pipefs(struct dentry *parent, const char *,
                                        mode_t, struct cache_detail *);
@@ -218,14 +223,42 @@ static inline int get_int(char **bpp, int *anint)
        return 0;
 }
 
+/*
+ * timestamps kept in the cache are expressed in seconds
+ * since boot.  This is the best for measuring differences in
+ * real time.
+ */
+static inline time_t seconds_since_boot(void)
+{
+       struct timespec boot;
+       getboottime(&boot);
+       return get_seconds() - boot.tv_sec;
+}
+
+static inline time_t convert_to_wallclock(time_t sinceboot)
+{
+       struct timespec boot;
+       getboottime(&boot);
+       return boot.tv_sec + sinceboot;
+}
+
 static inline time_t get_expiry(char **bpp)
 {
        int rv;
+       struct timespec boot;
+
        if (get_int(bpp, &rv))
                return 0;
        if (rv < 0)
                return 0;
-       return rv;
+       getboottime(&boot);
+       return rv - boot.tv_sec;
 }
 
+static inline void sunrpc_invalidate(struct cache_head *h,
+                                    struct cache_detail *detail)
+{
+       h->expiry_time = seconds_since_boot() - 1;
+       detail->nextcheck = seconds_since_boot();
+}
 #endif /*  _LINUX_SUNRPC_CACHE_H_ */
index c83df09a8e2baa274f0822e8fb4d14feb8834c69..a5a55f284b7da5de0fdc38fe7b9dac53feb53672 100644 (file)
@@ -102,6 +102,7 @@ struct rpc_procinfo {
 #ifdef __KERNEL__
 
 struct rpc_create_args {
+       struct net              *net;
        int                     protocol;
        struct sockaddr         *address;
        size_t                  addrsize;
diff --git a/include/linux/sunrpc/gss_spkm3.h b/include/linux/sunrpc/gss_spkm3.h
deleted file mode 100644 (file)
index e3e6a34..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- *  linux/include/linux/sunrpc/gss_spkm3.h
- *
- *  Copyright (c) 2000 The Regents of the University of Michigan.
- *  All rights reserved.
- *
- *  Andy Adamson   <andros@umich.edu>
- */
-
-#include <linux/sunrpc/auth_gss.h>
-#include <linux/sunrpc/gss_err.h>
-#include <linux/sunrpc/gss_asn1.h>
-
-struct spkm3_ctx {
-       struct xdr_netobj       ctx_id;  /* per message context id */
-       int                     endtime; /* endtime of the context */
-       struct xdr_netobj       mech_used;
-       unsigned int            ret_flags ;
-       struct xdr_netobj       conf_alg;
-       struct xdr_netobj       derived_conf_key;
-       struct xdr_netobj       intg_alg;
-       struct xdr_netobj       derived_integ_key;
-};
-
-/* OIDs declarations for K-ALG, I-ALG, C-ALG, and OWF-ALG */
-extern const struct xdr_netobj hmac_md5_oid;
-extern const struct xdr_netobj cast5_cbc_oid;
-
-/* SPKM InnerContext Token types */
-
-#define SPKM_ERROR_TOK 3
-#define SPKM_MIC_TOK   4
-#define SPKM_WRAP_TOK  5
-#define SPKM_DEL_TOK   6
-
-u32 spkm3_make_token(struct spkm3_ctx *ctx, struct xdr_buf * text, struct xdr_netobj * token, int toktype);
-
-u32 spkm3_read_token(struct spkm3_ctx *ctx, struct xdr_netobj *read_token, struct xdr_buf *message_buffer, int toktype);
-
-#define CKSUMTYPE_RSA_MD5            0x0007
-#define CKSUMTYPE_HMAC_MD5           0x0008
-
-s32 make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header,
-               unsigned int hdrlen, struct xdr_buf *body,
-               unsigned int body_offset, struct xdr_netobj *cksum);
-void asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits);
-int decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen,
-                   int explen);
-void spkm3_mic_header(unsigned char **hdrbuf, unsigned int *hdrlen, 
-                   unsigned char *ctxhdr, int elen, int zbit);
-void spkm3_make_mic_token(unsigned  char **tokp, int toklen, 
-                   struct xdr_netobj *mic_hdr,
-                   struct xdr_netobj *md5cksum, int md5elen, int md5zbit);
-u32 spkm3_verify_mic_token(unsigned char **tokp, int *mic_hdrlen, 
-                   unsigned char **cksum);
index 5fa0f20843078a94c8822ef1a1ace6cc5d0ae2c0..680471d1f28a4300700883489c3b8b78335c07da 100644 (file)
@@ -38,8 +38,21 @@ struct svc_stat {
                                rpcbadclnt;
 };
 
-void                   rpc_proc_init(void);
-void                   rpc_proc_exit(void);
+struct net;
+#ifdef CONFIG_PROC_FS
+int                    rpc_proc_init(struct net *);
+void                   rpc_proc_exit(struct net *);
+#else
+static inline int rpc_proc_init(struct net *net)
+{
+       return 0;
+}
+
+static inline void rpc_proc_exit(struct net *net)
+{
+}
+#endif
+
 #ifdef MODULE
 void                   rpc_modcount(struct inode *, int);
 #endif
@@ -54,9 +67,6 @@ void                  svc_proc_unregister(const char *);
 
 void                   svc_seq_show(struct seq_file *,
                                     const struct svc_stat *);
-
-extern struct proc_dir_entry   *proc_net_rpc;
-
 #else
 
 static inline struct proc_dir_entry *rpc_proc_register(struct rpc_stat *s) { return NULL; }
@@ -69,9 +79,6 @@ static inline void svc_proc_unregister(const char *p) {}
 
 static inline void svc_seq_show(struct seq_file *seq,
                                const struct svc_stat *st) {}
-
-#define proc_net_rpc NULL
-
 #endif
 
 #endif /* _LINUX_SUNRPC_STATS_H */
index 5f4e18b3ce73d3b5000b46d633d2eaa7af38c13d..bbdb680ffbe9d46f31de3f88d82d48869543920f 100644 (file)
@@ -12,6 +12,7 @@
 
 struct svc_xprt_ops {
        struct svc_xprt *(*xpo_create)(struct svc_serv *,
+                                      struct net *net,
                                       struct sockaddr *, int,
                                       int);
        struct svc_xprt *(*xpo_accept)(struct svc_xprt *);
@@ -32,6 +33,16 @@ struct svc_xprt_class {
        u32                     xcl_max_payload;
 };
 
+/*
+ * This is embedded in an object that wants a callback before deleting
+ * an xprt; intended for use by NFSv4.1, which needs to know when a
+ * client's tcp connection (and hence possibly a backchannel) goes away.
+ */
+struct svc_xpt_user {
+       struct list_head list;
+       void (*callback)(struct svc_xpt_user *);
+};
+
 struct svc_xprt {
        struct svc_xprt_class   *xpt_class;
        struct svc_xprt_ops     *xpt_ops;
@@ -66,14 +77,31 @@ struct svc_xprt {
        struct sockaddr_storage xpt_remote;     /* remote peer's address */
        size_t                  xpt_remotelen;  /* length of address */
        struct rpc_wait_queue   xpt_bc_pending; /* backchannel wait queue */
+       struct list_head        xpt_users;      /* callbacks on free */
+
+       struct net              *xpt_net;
 };
 
+static inline void register_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
+{
+       spin_lock(&xpt->xpt_lock);
+       list_add(&u->list, &xpt->xpt_users);
+       spin_unlock(&xpt->xpt_lock);
+}
+
+static inline void unregister_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
+{
+       spin_lock(&xpt->xpt_lock);
+       list_del_init(&u->list);
+       spin_unlock(&xpt->xpt_lock);
+}
+
 int    svc_reg_xprt_class(struct svc_xprt_class *);
 void   svc_unreg_xprt_class(struct svc_xprt_class *);
 void   svc_xprt_init(struct svc_xprt_class *, struct svc_xprt *,
                      struct svc_serv *);
-int    svc_create_xprt(struct svc_serv *, const char *, const int,
-                       const unsigned short, int);
+int    svc_create_xprt(struct svc_serv *, const char *, struct net *,
+                       const int, const unsigned short, int);
 void   svc_xprt_enqueue(struct svc_xprt *xprt);
 void   svc_xprt_received(struct svc_xprt *);
 void   svc_xprt_put(struct svc_xprt *xprt);
index d39dbdc7b10f383332caf249c1872458c35562cd..25d333c1b5717f3ee9f659168d805865ab420bd5 100644 (file)
@@ -108,10 +108,15 @@ struct auth_ops {
 #define        SVC_NEGATIVE    4
 #define        SVC_OK          5
 #define        SVC_DROP        6
-#define        SVC_DENIED      7
-#define        SVC_PENDING     8
-#define        SVC_COMPLETE    9
+#define        SVC_CLOSE       7       /* Like SVC_DROP, but request is definitely
+                                * lost so if there is a tcp connection, it
+                                * should be closed
+                                */
+#define        SVC_DENIED      8
+#define        SVC_PENDING     9
+#define        SVC_COMPLETE    10
 
+struct svc_xprt;
 
 extern int     svc_authenticate(struct svc_rqst *rqstp, __be32 *authp);
 extern int     svc_authorise(struct svc_rqst *rqstp);
@@ -121,13 +126,13 @@ extern void       svc_auth_unregister(rpc_authflavor_t flavor);
 
 extern struct auth_domain *unix_domain_find(char *name);
 extern void auth_domain_put(struct auth_domain *item);
-extern int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom);
+extern int auth_unix_add_addr(struct net *net, struct in6_addr *addr, struct auth_domain *dom);
 extern struct auth_domain *auth_domain_lookup(char *name, struct auth_domain *new);
 extern struct auth_domain *auth_domain_find(char *name);
-extern struct auth_domain *auth_unix_lookup(struct in6_addr *addr);
+extern struct auth_domain *auth_unix_lookup(struct net *net, struct in6_addr *addr);
 extern int auth_unix_forget_old(struct auth_domain *dom);
 extern void svcauth_unix_purge(void);
-extern void svcauth_unix_info_release(void *);
+extern void svcauth_unix_info_release(struct svc_xprt *xpt);
 extern int svcauth_unix_set_client(struct svc_rqst *rqstp);
 
 static inline unsigned long hash_str(char *name, int bits)
index ff5a77b28c50c61044e150591d2148c76414cdf3..89d10d279a203d611bbc3f40760ab490c8eea098 100644 (file)
@@ -224,6 +224,7 @@ struct rpc_xprt {
                                        bklog_u;        /* backlog queue utilization */
        } stat;
 
+       struct net              *xprt_net;
        const char              *address_strings[RPC_DISPLAY_MAX];
 };
 
@@ -249,6 +250,7 @@ static inline int bc_prealloc(struct rpc_rqst *req)
 
 struct xprt_create {
        int                     ident;          /* XPRT_TRANSPORT identifier */
+       struct net *            net;
        struct sockaddr *       srcaddr;        /* optional local address */
        struct sockaddr *       dstaddr;        /* remote peer address */
        size_t                  addrlen;
@@ -280,6 +282,8 @@ void                        xprt_release_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task);
 void                   xprt_release(struct rpc_task *task);
 struct rpc_xprt *      xprt_get(struct rpc_xprt *xprt);
 void                   xprt_put(struct rpc_xprt *xprt);
+struct rpc_xprt *      xprt_alloc(struct net *net, int size, int max_req);
+void                   xprt_free(struct rpc_xprt *);
 
 static inline __be32 *xprt_skip_transport_header(struct rpc_xprt *xprt, __be32 *p)
 {
index abf3e2561521acc1ce82167b9edaf71eaf263ee9..7f67c072d4969670a0355c7aaad033dbdc651a5f 100644 (file)
@@ -1145,7 +1145,7 @@ call_kill:
 }
 EXPORT_SYMBOL(sock_wake_async);
 
-static int __sock_create(struct net *net, int family, int type, int protocol,
+int __sock_create(struct net *net, int family, int type, int protocol,
                         struct socket **res, int kern)
 {
        int err;
@@ -1257,6 +1257,7 @@ out_release:
        rcu_read_unlock();
        goto out_sock_release;
 }
+EXPORT_SYMBOL(__sock_create);
 
 int sock_create(int family, int type, int protocol, struct socket **res)
 {
index 3376d7657185324adf2afef79984369bd16984ff..8873fd8ddacd261ac03bc1bb7d5b7a236579baee 100644 (file)
@@ -36,22 +36,3 @@ config RPCSEC_GSS_KRB5
          Kerberos support should be installed.
 
          If unsure, say Y.
-
-config RPCSEC_GSS_SPKM3
-       tristate "Secure RPC: SPKM3 mechanism (EXPERIMENTAL)"
-       depends on SUNRPC && EXPERIMENTAL
-       select SUNRPC_GSS
-       select CRYPTO
-       select CRYPTO_MD5
-       select CRYPTO_DES
-       select CRYPTO_CAST5
-       select CRYPTO_CBC
-       help
-         Choose Y here to enable Secure RPC using the SPKM3 public key
-         GSS-API mechanism (RFC 2025).
-
-         Secure RPC calls with SPKM3 require an auxiliary userspace
-         daemon which may be found in the Linux nfs-utils package
-         available from http://linux-nfs.org/.
-
-         If unsure, say N.
index 68192e562749c44225bd8dd55febe374242f8b5c..afe67849269f52faaad4ee242d47d5645ad4005f 100644 (file)
@@ -658,7 +658,7 @@ out1:
        return err;
 }
 
-void __exit rpcauth_remove_module(void)
+void rpcauth_remove_module(void)
 {
        rpc_destroy_authunix();
        rpc_destroy_generic_auth();
index 43162bb3b78f47cb30c6054907839f92281f4c92..e010a015d99671f27a22573a31eae75ae6cda955 100644 (file)
@@ -158,7 +158,7 @@ int __init rpc_init_generic_auth(void)
        return rpcauth_init_credcache(&generic_auth);
 }
 
-void __exit rpc_destroy_generic_auth(void)
+void rpc_destroy_generic_auth(void)
 {
        rpcauth_destroy_credcache(&generic_auth);
 }
index 74a231735f6773cd5531c89cddbdef8573ebf65e..7350d86a32ee00ab254978e7237dbfe176c05638 100644 (file)
@@ -11,8 +11,3 @@ obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o
 
 rpcsec_gss_krb5-objs := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \
        gss_krb5_seqnum.o gss_krb5_wrap.o gss_krb5_crypto.o gss_krb5_keys.o
-
-obj-$(CONFIG_RPCSEC_GSS_SPKM3) += rpcsec_gss_spkm3.o
-
-rpcsec_gss_spkm3-objs := gss_spkm3_mech.o gss_spkm3_seal.o gss_spkm3_unseal.o \
-       gss_spkm3_token.o
diff --git a/net/sunrpc/auth_gss/gss_spkm3_mech.c b/net/sunrpc/auth_gss/gss_spkm3_mech.c
deleted file mode 100644 (file)
index adade3d..0000000
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- *  linux/net/sunrpc/gss_spkm3_mech.c
- *
- *  Copyright (c) 2003 The Regents of the University of Michigan.
- *  All rights reserved.
- *
- *  Andy Adamson <andros@umich.edu>
- *  J. Bruce Fields <bfields@umich.edu>
- *
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions
- *  are met:
- *
- *  1. Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- *  3. Neither the name of the University nor the names of its
- *     contributors may be used to endorse or promote products derived
- *     from this software without specific prior written permission.
- *
- *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/sunrpc/auth.h>
-#include <linux/in.h>
-#include <linux/sunrpc/svcauth_gss.h>
-#include <linux/sunrpc/gss_spkm3.h>
-#include <linux/sunrpc/xdr.h>
-#include <linux/crypto.h>
-
-#ifdef RPC_DEBUG
-# define RPCDBG_FACILITY       RPCDBG_AUTH
-#endif
-
-static const void *
-simple_get_bytes(const void *p, const void *end, void *res, int len)
-{
-       const void *q = (const void *)((const char *)p + len);
-       if (unlikely(q > end || q < p))
-               return ERR_PTR(-EFAULT);
-       memcpy(res, p, len);
-       return q;
-}
-
-static const void *
-simple_get_netobj(const void *p, const void *end, struct xdr_netobj *res)
-{
-       const void *q;
-       unsigned int len;
-       p = simple_get_bytes(p, end, &len, sizeof(len));
-       if (IS_ERR(p))
-               return p;
-       res->len = len;
-       if (len == 0) {
-               res->data = NULL;
-               return p;
-       }
-       q = (const void *)((const char *)p + len);
-       if (unlikely(q > end || q < p))
-               return ERR_PTR(-EFAULT);
-       res->data = kmemdup(p, len, GFP_NOFS);
-       if (unlikely(res->data == NULL))
-               return ERR_PTR(-ENOMEM);
-       return q;
-}
-
-static int
-gss_import_sec_context_spkm3(const void *p, size_t len,
-                               struct gss_ctx *ctx_id,
-                               gfp_t gfp_mask)
-{
-       const void *end = (const void *)((const char *)p + len);
-       struct  spkm3_ctx *ctx;
-       int     version;
-
-       if (!(ctx = kzalloc(sizeof(*ctx), gfp_mask)))
-               goto out_err;
-
-       p = simple_get_bytes(p, end, &version, sizeof(version));
-       if (IS_ERR(p))
-               goto out_err_free_ctx;
-       if (version != 1) {
-               dprintk("RPC:       unknown spkm3 token format: "
-                               "obsolete nfs-utils?\n");
-               p = ERR_PTR(-EINVAL);
-               goto out_err_free_ctx;
-       }
-
-       p = simple_get_netobj(p, end, &ctx->ctx_id);
-       if (IS_ERR(p))
-               goto out_err_free_ctx;
-
-       p = simple_get_bytes(p, end, &ctx->endtime, sizeof(ctx->endtime));
-       if (IS_ERR(p))
-               goto out_err_free_ctx_id;
-
-       p = simple_get_netobj(p, end, &ctx->mech_used);
-       if (IS_ERR(p))
-               goto out_err_free_ctx_id;
-
-       p = simple_get_bytes(p, end, &ctx->ret_flags, sizeof(ctx->ret_flags));
-       if (IS_ERR(p))
-               goto out_err_free_mech;
-
-       p = simple_get_netobj(p, end, &ctx->conf_alg);
-       if (IS_ERR(p))
-               goto out_err_free_mech;
-
-       p = simple_get_netobj(p, end, &ctx->derived_conf_key);
-       if (IS_ERR(p))
-               goto out_err_free_conf_alg;
-
-       p = simple_get_netobj(p, end, &ctx->intg_alg);
-       if (IS_ERR(p))
-               goto out_err_free_conf_key;
-
-       p = simple_get_netobj(p, end, &ctx->derived_integ_key);
-       if (IS_ERR(p))
-               goto out_err_free_intg_alg;
-
-       if (p != end) {
-               p = ERR_PTR(-EFAULT);
-               goto out_err_free_intg_key;
-       }
-
-       ctx_id->internal_ctx_id = ctx;
-
-       dprintk("RPC:       Successfully imported new spkm context.\n");
-       return 0;
-
-out_err_free_intg_key:
-       kfree(ctx->derived_integ_key.data);
-out_err_free_intg_alg:
-       kfree(ctx->intg_alg.data);
-out_err_free_conf_key:
-       kfree(ctx->derived_conf_key.data);
-out_err_free_conf_alg:
-       kfree(ctx->conf_alg.data);
-out_err_free_mech:
-       kfree(ctx->mech_used.data);
-out_err_free_ctx_id:
-       kfree(ctx->ctx_id.data);
-out_err_free_ctx:
-       kfree(ctx);
-out_err:
-       return PTR_ERR(p);
-}
-
-static void
-gss_delete_sec_context_spkm3(void *internal_ctx)
-{
-       struct spkm3_ctx *sctx = internal_ctx;
-
-       kfree(sctx->derived_integ_key.data);
-       kfree(sctx->intg_alg.data);
-       kfree(sctx->derived_conf_key.data);
-       kfree(sctx->conf_alg.data);
-       kfree(sctx->mech_used.data);
-       kfree(sctx->ctx_id.data);
-       kfree(sctx);
-}
-
-static u32
-gss_verify_mic_spkm3(struct gss_ctx            *ctx,
-                       struct xdr_buf          *signbuf,
-                       struct xdr_netobj       *checksum)
-{
-       u32 maj_stat = 0;
-       struct spkm3_ctx *sctx = ctx->internal_ctx_id;
-
-       maj_stat = spkm3_read_token(sctx, checksum, signbuf, SPKM_MIC_TOK);
-
-       dprintk("RPC:       gss_verify_mic_spkm3 returning %d\n", maj_stat);
-       return maj_stat;
-}
-
-static u32
-gss_get_mic_spkm3(struct gss_ctx       *ctx,
-                    struct xdr_buf     *message_buffer,
-                    struct xdr_netobj  *message_token)
-{
-       u32 err = 0;
-       struct spkm3_ctx *sctx = ctx->internal_ctx_id;
-
-       err = spkm3_make_token(sctx, message_buffer,
-                               message_token, SPKM_MIC_TOK);
-       dprintk("RPC:       gss_get_mic_spkm3 returning %d\n", err);
-       return err;
-}
-
-static const struct gss_api_ops gss_spkm3_ops = {
-       .gss_import_sec_context = gss_import_sec_context_spkm3,
-       .gss_get_mic            = gss_get_mic_spkm3,
-       .gss_verify_mic         = gss_verify_mic_spkm3,
-       .gss_delete_sec_context = gss_delete_sec_context_spkm3,
-};
-
-static struct pf_desc gss_spkm3_pfs[] = {
-       {RPC_AUTH_GSS_SPKM, RPC_GSS_SVC_NONE, "spkm3"},
-       {RPC_AUTH_GSS_SPKMI, RPC_GSS_SVC_INTEGRITY, "spkm3i"},
-};
-
-static struct gss_api_mech gss_spkm3_mech = {
-       .gm_name        = "spkm3",
-       .gm_owner       = THIS_MODULE,
-       .gm_oid         = {7, "\053\006\001\005\005\001\003"},
-       .gm_ops         = &gss_spkm3_ops,
-       .gm_pf_num      = ARRAY_SIZE(gss_spkm3_pfs),
-       .gm_pfs         = gss_spkm3_pfs,
-};
-
-static int __init init_spkm3_module(void)
-{
-       int status;
-
-       status = gss_mech_register(&gss_spkm3_mech);
-       if (status)
-               printk("Failed to register spkm3 gss mechanism!\n");
-       return status;
-}
-
-static void __exit cleanup_spkm3_module(void)
-{
-       gss_mech_unregister(&gss_spkm3_mech);
-}
-
-MODULE_LICENSE("GPL");
-module_init(init_spkm3_module);
-module_exit(cleanup_spkm3_module);
diff --git a/net/sunrpc/auth_gss/gss_spkm3_seal.c b/net/sunrpc/auth_gss/gss_spkm3_seal.c
deleted file mode 100644 (file)
index 5a3a65a..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- *  linux/net/sunrpc/gss_spkm3_seal.c
- *
- *  Copyright (c) 2003 The Regents of the University of Michigan.
- *  All rights reserved.
- *
- *  Andy Adamson <andros@umich.edu>
- *
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions
- *  are met:
- *
- *  1. Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- *  3. Neither the name of the University nor the names of its
- *     contributors may be used to endorse or promote products derived
- *     from this software without specific prior written permission.
- *
- *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <linux/types.h>
-#include <linux/jiffies.h>
-#include <linux/sunrpc/gss_spkm3.h>
-#include <linux/random.h>
-#include <linux/crypto.h>
-#include <linux/pagemap.h>
-#include <linux/scatterlist.h>
-#include <linux/sunrpc/xdr.h>
-
-#ifdef RPC_DEBUG
-# define RPCDBG_FACILITY        RPCDBG_AUTH
-#endif
-
-const struct xdr_netobj hmac_md5_oid = { 8, "\x2B\x06\x01\x05\x05\x08\x01\x01"};
-const struct xdr_netobj cast5_cbc_oid = {9, "\x2A\x86\x48\x86\xF6\x7D\x07\x42\x0A"};
-
-/*
- * spkm3_make_token()
- *
- * Only SPKM_MIC_TOK with md5 intg-alg is supported
- */
-
-u32
-spkm3_make_token(struct spkm3_ctx *ctx,
-                  struct xdr_buf * text, struct xdr_netobj * token,
-                  int toktype)
-{
-       s32                     checksum_type;
-       char                    tokhdrbuf[25];
-       char                    cksumdata[16];
-       struct xdr_netobj       md5cksum = {.len = 0, .data = cksumdata};
-       struct xdr_netobj       mic_hdr = {.len = 0, .data = tokhdrbuf};
-       int                     tokenlen = 0;
-       unsigned char           *ptr;
-       s32                     now;
-       int                     ctxelen = 0, ctxzbit = 0;
-       int                     md5elen = 0, md5zbit = 0;
-
-       now = jiffies;
-
-       if (ctx->ctx_id.len != 16) {
-               dprintk("RPC:       spkm3_make_token BAD ctx_id.len %d\n",
-                               ctx->ctx_id.len);
-               goto out_err;
-       }
-
-       if (!g_OID_equal(&ctx->intg_alg, &hmac_md5_oid)) {
-               dprintk("RPC:       gss_spkm3_seal: unsupported I-ALG "
-                               "algorithm.  only support hmac-md5 I-ALG.\n");
-               goto out_err;
-       } else
-               checksum_type = CKSUMTYPE_HMAC_MD5;
-
-       if (!g_OID_equal(&ctx->conf_alg, &cast5_cbc_oid)) {
-               dprintk("RPC:       gss_spkm3_seal: unsupported C-ALG "
-                               "algorithm\n");
-               goto out_err;
-       }
-
-       if (toktype == SPKM_MIC_TOK) {
-               /* Calculate checksum over the mic-header */
-               asn1_bitstring_len(&ctx->ctx_id, &ctxelen, &ctxzbit);
-               spkm3_mic_header(&mic_hdr.data, &mic_hdr.len, ctx->ctx_id.data,
-                               ctxelen, ctxzbit);
-               if (make_spkm3_checksum(checksum_type, &ctx->derived_integ_key,
-                                       (char *)mic_hdr.data, mic_hdr.len,
-                                       text, 0, &md5cksum))
-                       goto out_err;
-
-               asn1_bitstring_len(&md5cksum, &md5elen, &md5zbit);
-               tokenlen = 10 + ctxelen + 1 + md5elen + 1;
-
-               /* Create token header using generic routines */
-               token->len = g_token_size(&ctx->mech_used, tokenlen + 2);
-
-               ptr = token->data;
-               g_make_token_header(&ctx->mech_used, tokenlen + 2, &ptr);
-
-               spkm3_make_mic_token(&ptr, tokenlen, &mic_hdr, &md5cksum, md5elen, md5zbit);
-       } else if (toktype == SPKM_WRAP_TOK) { /* Not Supported */
-               dprintk("RPC:       gss_spkm3_seal: SPKM_WRAP_TOK "
-                               "not supported\n");
-               goto out_err;
-       }
-
-       /* XXX need to implement sequence numbers, and ctx->expired */
-
-       return  GSS_S_COMPLETE;
-out_err:
-       token->data = NULL;
-       token->len = 0;
-       return GSS_S_FAILURE;
-}
-
-static int
-spkm3_checksummer(struct scatterlist *sg, void *data)
-{
-       struct hash_desc *desc = data;
-
-       return crypto_hash_update(desc, sg, sg->length);
-}
-
-/* checksum the plaintext data and hdrlen bytes of the token header */
-s32
-make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header,
-                   unsigned int hdrlen, struct xdr_buf *body,
-                   unsigned int body_offset, struct xdr_netobj *cksum)
-{
-       char                            *cksumname;
-       struct hash_desc                desc; /* XXX add to ctx? */
-       struct scatterlist              sg[1];
-       int err;
-
-       switch (cksumtype) {
-               case CKSUMTYPE_HMAC_MD5:
-                       cksumname = "hmac(md5)";
-                       break;
-               default:
-                       dprintk("RPC:       spkm3_make_checksum:"
-                                       " unsupported checksum %d", cksumtype);
-                       return GSS_S_FAILURE;
-       }
-
-       if (key->data == NULL || key->len <= 0) return GSS_S_FAILURE;
-
-       desc.tfm = crypto_alloc_hash(cksumname, 0, CRYPTO_ALG_ASYNC);
-       if (IS_ERR(desc.tfm))
-               return GSS_S_FAILURE;
-       cksum->len = crypto_hash_digestsize(desc.tfm);
-       desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
-
-       err = crypto_hash_setkey(desc.tfm, key->data, key->len);
-       if (err)
-               goto out;
-
-       err = crypto_hash_init(&desc);
-       if (err)
-               goto out;
-
-       sg_init_one(sg, header, hdrlen);
-       crypto_hash_update(&desc, sg, sg->length);
-
-       xdr_process_buf(body, body_offset, body->len - body_offset,
-                       spkm3_checksummer, &desc);
-       crypto_hash_final(&desc, cksum->data);
-
-out:
-       crypto_free_hash(desc.tfm);
-
-       return err ? GSS_S_FAILURE : 0;
-}
diff --git a/net/sunrpc/auth_gss/gss_spkm3_token.c b/net/sunrpc/auth_gss/gss_spkm3_token.c
deleted file mode 100644 (file)
index a99825d..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- *  linux/net/sunrpc/gss_spkm3_token.c
- *
- *  Copyright (c) 2003 The Regents of the University of Michigan.
- *  All rights reserved.
- *
- *  Andy Adamson <andros@umich.edu>
- *
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions
- *  are met:
- *
- *  1. Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- *  3. Neither the name of the University nor the names of its
- *     contributors may be used to endorse or promote products derived
- *     from this software without specific prior written permission.
- *
- *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/jiffies.h>
-#include <linux/sunrpc/gss_spkm3.h>
-#include <linux/random.h>
-#include <linux/crypto.h>
-
-#ifdef RPC_DEBUG
-# define RPCDBG_FACILITY        RPCDBG_AUTH
-#endif
-
-/*
- * asn1_bitstring_len()
- *
- * calculate the asn1 bitstring length of the xdr_netobject
- */
-void
-asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits)
-{
-       int i, zbit = 0,elen = in->len;
-       char *ptr;
-
-       ptr = &in->data[in->len -1];
-
-       /* count trailing 0's */
-       for(i = in->len; i > 0; i--) {
-               if (*ptr == 0) {
-                       ptr--;
-                       elen--;
-               } else
-                       break;
-       }
-
-       /* count number of 0 bits in final octet */
-       ptr = &in->data[elen - 1];
-       for(i = 0; i < 8; i++) {
-               short mask = 0x01;
-
-               if (!((mask << i) & *ptr))
-                       zbit++;
-               else
-                       break;
-       }
-       *enclen = elen;
-       *zerobits = zbit;
-}
-
-/*
- * decode_asn1_bitstring()
- *
- * decode a bitstring into a buffer of the expected length.
- * enclen = bit string length
- * explen = expected length (define in rfc)
- */
-int
-decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen, int explen)
-{
-       if (!(out->data = kzalloc(explen,GFP_NOFS)))
-               return 0;
-       out->len = explen;
-       memcpy(out->data, in, enclen);
-       return 1;
-}
-
-/*
- * SPKMInnerContextToken choice SPKM_MIC asn1 token layout
- *
- * contextid is always 16 bytes plain data. max asn1 bitstring len = 17.
- *
- * tokenlen = pos[0] to end of token (max pos[45] with MD5 cksum)
- *
- * pos  value
- * ----------
- * [0] a4  SPKM-MIC tag
- * [1] ??  innertoken length  (max 44)
- *
- *
- * tok_hdr piece of checksum data starts here
- *
- * the maximum mic-header len = 9 + 17 = 26
- *     mic-header
- *     ----------
- * [2] 30      SEQUENCE tag
- * [3] ??      mic-header length: (max 23) = TokenID + ContextID
- *
- *             TokenID  - all fields constant and can be hardcoded
- *             -------
- * [4]   02    Type 2
- * [5]   02    Length 2
- * [6][7] 01 01        TokenID (SPKM_MIC_TOK)
- *
- *             ContextID  - encoded length not constant, calculated
- *             ---------
- * [8] 03      Type 3
- * [9] ??      encoded length
- * [10]        ??      ctxzbit
- * [11]                contextid
- *
- * mic_header piece of checksum data ends here.
- *
- *     int-cksum - encoded length not constant, calculated
- *     ---------
- * [??]        03      Type 3
- * [??]        ??      encoded length
- * [??]        ??      md5zbit
- * [??]                int-cksum (NID_md5 = 16)
- *
- * maximum SPKM-MIC innercontext token length =
- *      10 + encoded contextid_size(17 max) + 2 + encoded
- *       cksum_size (17 maxfor NID_md5) = 46
- */
-
-/*
- * spkm3_mic_header()
- *
- * Prepare the SPKM_MIC_TOK mic-header for check-sum calculation
- * elen: 16 byte context id asn1 bitstring encoded length
- */
-void
-spkm3_mic_header(unsigned char **hdrbuf, unsigned int *hdrlen, unsigned char *ctxdata, int elen, int zbit)
-{
-       char *hptr = *hdrbuf;
-       char *top = *hdrbuf;
-
-       *(u8 *)hptr++ = 0x30;
-       *(u8 *)hptr++ = elen + 7;  /* on the wire header length */
-
-       /* tokenid */
-       *(u8 *)hptr++ = 0x02;
-       *(u8 *)hptr++ = 0x02;
-       *(u8 *)hptr++ = 0x01;
-       *(u8 *)hptr++ = 0x01;
-
-       /* coniextid */
-       *(u8 *)hptr++ = 0x03;
-       *(u8 *)hptr++ = elen + 1; /* add 1 to include zbit */
-       *(u8 *)hptr++ = zbit;
-       memcpy(hptr, ctxdata, elen);
-       hptr += elen;
-       *hdrlen = hptr - top;
-}
-
-/*
- * spkm3_mic_innercontext_token()
- *
- * *tokp points to the beginning of the SPKM_MIC token  described
- * in rfc 2025, section 3.2.1:
- *
- * toklen is the inner token length
- */
-void
-spkm3_make_mic_token(unsigned char **tokp, int toklen, struct xdr_netobj *mic_hdr, struct xdr_netobj *md5cksum, int md5elen, int md5zbit)
-{
-       unsigned char *ict = *tokp;
-
-       *(u8 *)ict++ = 0xa4;
-       *(u8 *)ict++ = toklen;
-       memcpy(ict, mic_hdr->data, mic_hdr->len);
-       ict += mic_hdr->len;
-
-       *(u8 *)ict++ = 0x03;
-       *(u8 *)ict++ = md5elen + 1; /* add 1 to include zbit */
-       *(u8 *)ict++ = md5zbit;
-       memcpy(ict, md5cksum->data, md5elen);
-}
-
-u32
-spkm3_verify_mic_token(unsigned char **tokp, int *mic_hdrlen, unsigned char **cksum)
-{
-       struct xdr_netobj       spkm3_ctx_id = {.len =0, .data = NULL};
-       unsigned char           *ptr = *tokp;
-       int                     ctxelen;
-       u32                     ret = GSS_S_DEFECTIVE_TOKEN;
-
-       /* spkm3 innercontext token preamble */
-       if ((ptr[0] != 0xa4) || (ptr[2] != 0x30)) {
-               dprintk("RPC:       BAD SPKM ictoken preamble\n");
-               goto out;
-       }
-
-       *mic_hdrlen = ptr[3];
-
-       /* token type */
-       if ((ptr[4] != 0x02) || (ptr[5] != 0x02)) {
-               dprintk("RPC:       BAD asn1 SPKM3 token type\n");
-               goto out;
-       }
-
-       /* only support SPKM_MIC_TOK */
-       if((ptr[6] != 0x01) || (ptr[7] != 0x01)) {
-               dprintk("RPC:       ERROR unsupported SPKM3 token\n");
-               goto out;
-       }
-
-       /* contextid */
-       if (ptr[8] != 0x03) {
-               dprintk("RPC:       BAD SPKM3 asn1 context-id type\n");
-               goto out;
-       }
-
-       ctxelen = ptr[9];
-       if (ctxelen > 17) {  /* length includes asn1 zbit octet */
-               dprintk("RPC:       BAD SPKM3 contextid len %d\n", ctxelen);
-               goto out;
-       }
-
-       /* ignore ptr[10] */
-
-       if(!decode_asn1_bitstring(&spkm3_ctx_id, &ptr[11], ctxelen - 1, 16))
-               goto out;
-
-       /*
-       * in the current implementation: the optional int-alg is not present
-       * so the default int-alg (md5) is used the optional snd-seq field is
-       * also not present
-       */
-
-       if (*mic_hdrlen != 6 + ctxelen) {
-               dprintk("RPC:       BAD SPKM_ MIC_TOK header len %d: we only "
-                               "support default int-alg (should be absent) "
-                               "and do not support snd-seq\n", *mic_hdrlen);
-               goto out;
-       }
-       /* checksum */
-       *cksum = (&ptr[10] + ctxelen); /* ctxelen includes ptr[10] */
-
-       ret = GSS_S_COMPLETE;
-out:
-       kfree(spkm3_ctx_id.data);
-       return ret;
-}
-
diff --git a/net/sunrpc/auth_gss/gss_spkm3_unseal.c b/net/sunrpc/auth_gss/gss_spkm3_unseal.c
deleted file mode 100644 (file)
index cc21ee8..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- *  linux/net/sunrpc/gss_spkm3_unseal.c
- *
- *  Copyright (c) 2003 The Regents of the University of Michigan.
- *  All rights reserved.
- *
- *  Andy Adamson <andros@umich.edu>
- *
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions
- *  are met:
- *
- *  1. Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- *  3. Neither the name of the University nor the names of its
- *     contributors may be used to endorse or promote products derived
- *     from this software without specific prior written permission.
- *
- *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/jiffies.h>
-#include <linux/sunrpc/gss_spkm3.h>
-#include <linux/crypto.h>
-
-#ifdef RPC_DEBUG
-# define RPCDBG_FACILITY        RPCDBG_AUTH
-#endif
-
-/*
- * spkm3_read_token()
- *
- * only SPKM_MIC_TOK with md5 intg-alg is supported
- */
-u32
-spkm3_read_token(struct spkm3_ctx *ctx,
-               struct xdr_netobj *read_token,    /* checksum */
-               struct xdr_buf *message_buffer, /* signbuf */
-               int toktype)
-{
-       s32                     checksum_type;
-       s32                     code;
-       struct xdr_netobj       wire_cksum = {.len =0, .data = NULL};
-       char                    cksumdata[16];
-       struct xdr_netobj       md5cksum = {.len = 0, .data = cksumdata};
-       unsigned char           *ptr = (unsigned char *)read_token->data;
-       unsigned char           *cksum;
-       int                     bodysize, md5elen;
-       int                     mic_hdrlen;
-       u32                     ret = GSS_S_DEFECTIVE_TOKEN;
-
-       if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used,
-                                       &bodysize, &ptr, read_token->len))
-               goto out;
-
-       /* decode the token */
-
-       if (toktype != SPKM_MIC_TOK) {
-               dprintk("RPC:       BAD SPKM3 token type: %d\n", toktype);
-               goto out;
-       }
-
-       if ((ret = spkm3_verify_mic_token(&ptr, &mic_hdrlen, &cksum)))
-               goto out;
-
-       if (*cksum++ != 0x03) {
-               dprintk("RPC:       spkm3_read_token BAD checksum type\n");
-               goto out;
-       }
-       md5elen = *cksum++;
-       cksum++;        /* move past the zbit */
-
-       if (!decode_asn1_bitstring(&wire_cksum, cksum, md5elen - 1, 16))
-               goto out;
-
-       /* HARD CODED FOR MD5 */
-
-       /* compute the checksum of the message.
-        * ptr + 2 = start of header piece of checksum
-        * mic_hdrlen + 2 = length of header piece of checksum
-        */
-       ret = GSS_S_DEFECTIVE_TOKEN;
-       if (!g_OID_equal(&ctx->intg_alg, &hmac_md5_oid)) {
-               dprintk("RPC:       gss_spkm3_seal: unsupported I-ALG "
-                               "algorithm\n");
-               goto out;
-       }
-
-       checksum_type = CKSUMTYPE_HMAC_MD5;
-
-       code = make_spkm3_checksum(checksum_type,
-               &ctx->derived_integ_key, ptr + 2, mic_hdrlen + 2,
-               message_buffer, 0, &md5cksum);
-
-       if (code)
-               goto out;
-
-       ret = GSS_S_BAD_SIG;
-       code = memcmp(md5cksum.data, wire_cksum.data, wire_cksum.len);
-       if (code) {
-               dprintk("RPC:       bad MIC checksum\n");
-               goto out;
-       }
-
-
-       /* XXX: need to add expiration and sequencing */
-       ret = GSS_S_COMPLETE;
-out:
-       kfree(wire_cksum.data);
-       return ret;
-}
index cc385b3a59c219741e463cd78842f8e01f7cf9c4..dec2a6fc7c1277f15485011e52a7671eb3052574 100644 (file)
@@ -964,7 +964,7 @@ svcauth_gss_set_client(struct svc_rqst *rqstp)
        if (rqstp->rq_gssclient == NULL)
                return SVC_DENIED;
        stat = svcauth_unix_set_client(rqstp);
-       if (stat == SVC_DROP)
+       if (stat == SVC_DROP || stat == SVC_CLOSE)
                return stat;
        return SVC_OK;
 }
@@ -1018,7 +1018,7 @@ static int svcauth_gss_handle_init(struct svc_rqst *rqstp,
                return SVC_DENIED;
        memset(&rsikey, 0, sizeof(rsikey));
        if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx))
-               return SVC_DROP;
+               return SVC_CLOSE;
        *authp = rpc_autherr_badverf;
        if (svc_safe_getnetobj(argv, &tmpobj)) {
                kfree(rsikey.in_handle.data);
@@ -1026,38 +1026,35 @@ static int svcauth_gss_handle_init(struct svc_rqst *rqstp,
        }
        if (dup_netobj(&rsikey.in_token, &tmpobj)) {
                kfree(rsikey.in_handle.data);
-               return SVC_DROP;
+               return SVC_CLOSE;
        }
 
        /* Perform upcall, or find upcall result: */
        rsip = rsi_lookup(&rsikey);
        rsi_free(&rsikey);
        if (!rsip)
-               return SVC_DROP;
-       switch (cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle)) {
-       case -EAGAIN:
-       case -ETIMEDOUT:
-       case -ENOENT:
+               return SVC_CLOSE;
+       if (cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle) < 0)
                /* No upcall result: */
-               return SVC_DROP;
-       case 0:
-               ret = SVC_DROP;
-               /* Got an answer to the upcall; use it: */
-               if (gss_write_init_verf(rqstp, rsip))
-                       goto out;
-               if (resv->iov_len + 4 > PAGE_SIZE)
-                       goto out;
-               svc_putnl(resv, RPC_SUCCESS);
-               if (svc_safe_putnetobj(resv, &rsip->out_handle))
-                       goto out;
-               if (resv->iov_len + 3 * 4 > PAGE_SIZE)
-                       goto out;
-               svc_putnl(resv, rsip->major_status);
-               svc_putnl(resv, rsip->minor_status);
-               svc_putnl(resv, GSS_SEQ_WIN);
-               if (svc_safe_putnetobj(resv, &rsip->out_token))
-                       goto out;
-       }
+               return SVC_CLOSE;
+
+       ret = SVC_CLOSE;
+       /* Got an answer to the upcall; use it: */
+       if (gss_write_init_verf(rqstp, rsip))
+               goto out;
+       if (resv->iov_len + 4 > PAGE_SIZE)
+               goto out;
+       svc_putnl(resv, RPC_SUCCESS);
+       if (svc_safe_putnetobj(resv, &rsip->out_handle))
+               goto out;
+       if (resv->iov_len + 3 * 4 > PAGE_SIZE)
+               goto out;
+       svc_putnl(resv, rsip->major_status);
+       svc_putnl(resv, rsip->minor_status);
+       svc_putnl(resv, GSS_SEQ_WIN);
+       if (svc_safe_putnetobj(resv, &rsip->out_token))
+               goto out;
+
        ret = SVC_COMPLETE;
 out:
        cache_put(&rsip->h, &rsi_cache);
index 7dce81a926c5e000b1444d2909fad940b93d0cf4..e433e7580e27b221eff94d8b95ceaea703440618 100644 (file)
 #include <linux/sunrpc/cache.h>
 #include <linux/sunrpc/stats.h>
 #include <linux/sunrpc/rpc_pipe_fs.h>
+#include "netns.h"
 
 #define         RPCDBG_FACILITY RPCDBG_CACHE
 
-static int cache_defer_req(struct cache_req *req, struct cache_head *item);
+static void cache_defer_req(struct cache_req *req, struct cache_head *item);
 static void cache_revisit_request(struct cache_head *item);
 
 static void cache_init(struct cache_head *h)
 {
-       time_t now = get_seconds();
+       time_t now = seconds_since_boot();
        h->next = NULL;
        h->flags = 0;
        kref_init(&h->ref);
@@ -51,7 +52,7 @@ static void cache_init(struct cache_head *h)
 
 static inline int cache_is_expired(struct cache_detail *detail, struct cache_head *h)
 {
-       return  (h->expiry_time < get_seconds()) ||
+       return  (h->expiry_time < seconds_since_boot()) ||
                (detail->flush_time > h->last_refresh);
 }
 
@@ -126,7 +127,7 @@ static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch);
 static void cache_fresh_locked(struct cache_head *head, time_t expiry)
 {
        head->expiry_time = expiry;
-       head->last_refresh = get_seconds();
+       head->last_refresh = seconds_since_boot();
        set_bit(CACHE_VALID, &head->flags);
 }
 
@@ -237,7 +238,7 @@ int cache_check(struct cache_detail *detail,
 
        /* now see if we want to start an upcall */
        refresh_age = (h->expiry_time - h->last_refresh);
-       age = get_seconds() - h->last_refresh;
+       age = seconds_since_boot() - h->last_refresh;
 
        if (rqstp == NULL) {
                if (rv == -EAGAIN)
@@ -252,7 +253,7 @@ int cache_check(struct cache_detail *detail,
                                cache_revisit_request(h);
                                if (rv == -EAGAIN) {
                                        set_bit(CACHE_NEGATIVE, &h->flags);
-                                       cache_fresh_locked(h, get_seconds()+CACHE_NEW_EXPIRY);
+                                       cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY);
                                        cache_fresh_unlocked(h, detail);
                                        rv = -ENOENT;
                                }
@@ -267,7 +268,8 @@ int cache_check(struct cache_detail *detail,
        }
 
        if (rv == -EAGAIN) {
-               if (cache_defer_req(rqstp, h) < 0) {
+               cache_defer_req(rqstp, h);
+               if (!test_bit(CACHE_PENDING, &h->flags)) {
                        /* Request is not deferred */
                        rv = cache_is_valid(detail, h);
                        if (rv == -EAGAIN)
@@ -387,11 +389,11 @@ static int cache_clean(void)
                        return -1;
                }
                current_detail = list_entry(next, struct cache_detail, others);
-               if (current_detail->nextcheck > get_seconds())
+               if (current_detail->nextcheck > seconds_since_boot())
                        current_index = current_detail->hash_size;
                else {
                        current_index = 0;
-                       current_detail->nextcheck = get_seconds()+30*60;
+                       current_detail->nextcheck = seconds_since_boot()+30*60;
                }
        }
 
@@ -476,7 +478,7 @@ EXPORT_SYMBOL_GPL(cache_flush);
 void cache_purge(struct cache_detail *detail)
 {
        detail->flush_time = LONG_MAX;
-       detail->nextcheck = get_seconds();
+       detail->nextcheck = seconds_since_boot();
        cache_flush();
        detail->flush_time = 1;
 }
@@ -505,81 +507,155 @@ EXPORT_SYMBOL_GPL(cache_purge);
 
 static DEFINE_SPINLOCK(cache_defer_lock);
 static LIST_HEAD(cache_defer_list);
-static struct list_head cache_defer_hash[DFR_HASHSIZE];
+static struct hlist_head cache_defer_hash[DFR_HASHSIZE];
 static int cache_defer_cnt;
 
-static int cache_defer_req(struct cache_req *req, struct cache_head *item)
+static void __unhash_deferred_req(struct cache_deferred_req *dreq)
+{
+       hlist_del_init(&dreq->hash);
+       if (!list_empty(&dreq->recent)) {
+               list_del_init(&dreq->recent);
+               cache_defer_cnt--;
+       }
+}
+
+static void __hash_deferred_req(struct cache_deferred_req *dreq, struct cache_head *item)
 {
-       struct cache_deferred_req *dreq, *discard;
        int hash = DFR_HASH(item);
 
-       if (cache_defer_cnt >= DFR_MAX) {
-               /* too much in the cache, randomly drop this one,
-                * or continue and drop the oldest below
-                */
-               if (net_random()&1)
-                       return -ENOMEM;
-       }
-       dreq = req->defer(req);
-       if (dreq == NULL)
-               return -ENOMEM;
+       INIT_LIST_HEAD(&dreq->recent);
+       hlist_add_head(&dreq->hash, &cache_defer_hash[hash]);
+}
+
+static void setup_deferral(struct cache_deferred_req *dreq,
+                          struct cache_head *item,
+                          int count_me)
+{
 
        dreq->item = item;
 
        spin_lock(&cache_defer_lock);
 
-       list_add(&dreq->recent, &cache_defer_list);
-
-       if (cache_defer_hash[hash].next == NULL)
-               INIT_LIST_HEAD(&cache_defer_hash[hash]);
-       list_add(&dreq->hash, &cache_defer_hash[hash]);
+       __hash_deferred_req(dreq, item);
 
-       /* it is in, now maybe clean up */
-       discard = NULL;
-       if (++cache_defer_cnt > DFR_MAX) {
-               discard = list_entry(cache_defer_list.prev,
-                                    struct cache_deferred_req, recent);
-               list_del_init(&discard->recent);
-               list_del_init(&discard->hash);
-               cache_defer_cnt--;
+       if (count_me) {
+               cache_defer_cnt++;
+               list_add(&dreq->recent, &cache_defer_list);
        }
+
        spin_unlock(&cache_defer_lock);
 
+}
+
+struct thread_deferred_req {
+       struct cache_deferred_req handle;
+       struct completion completion;
+};
+
+static void cache_restart_thread(struct cache_deferred_req *dreq, int too_many)
+{
+       struct thread_deferred_req *dr =
+               container_of(dreq, struct thread_deferred_req, handle);
+       complete(&dr->completion);
+}
+
+static void cache_wait_req(struct cache_req *req, struct cache_head *item)
+{
+       struct thread_deferred_req sleeper;
+       struct cache_deferred_req *dreq = &sleeper.handle;
+
+       sleeper.completion = COMPLETION_INITIALIZER_ONSTACK(sleeper.completion);
+       dreq->revisit = cache_restart_thread;
+
+       setup_deferral(dreq, item, 0);
+
+       if (!test_bit(CACHE_PENDING, &item->flags) ||
+           wait_for_completion_interruptible_timeout(
+                   &sleeper.completion, req->thread_wait) <= 0) {
+               /* The completion wasn't completed, so we need
+                * to clean up
+                */
+               spin_lock(&cache_defer_lock);
+               if (!hlist_unhashed(&sleeper.handle.hash)) {
+                       __unhash_deferred_req(&sleeper.handle);
+                       spin_unlock(&cache_defer_lock);
+               } else {
+                       /* cache_revisit_request already removed
+                        * this from the hash table, but hasn't
+                        * called ->revisit yet.  It will very soon
+                        * and we need to wait for it.
+                        */
+                       spin_unlock(&cache_defer_lock);
+                       wait_for_completion(&sleeper.completion);
+               }
+       }
+}
+
+static void cache_limit_defers(void)
+{
+       /* Make sure we haven't exceed the limit of allowed deferred
+        * requests.
+        */
+       struct cache_deferred_req *discard = NULL;
+
+       if (cache_defer_cnt <= DFR_MAX)
+               return;
+
+       spin_lock(&cache_defer_lock);
+
+       /* Consider removing either the first or the last */
+       if (cache_defer_cnt > DFR_MAX) {
+               if (net_random() & 1)
+                       discard = list_entry(cache_defer_list.next,
+                                            struct cache_deferred_req, recent);
+               else
+                       discard = list_entry(cache_defer_list.prev,
+                                            struct cache_deferred_req, recent);
+               __unhash_deferred_req(discard);
+       }
+       spin_unlock(&cache_defer_lock);
        if (discard)
-               /* there was one too many */
                discard->revisit(discard, 1);
+}
 
-       if (!test_bit(CACHE_PENDING, &item->flags)) {
-               /* must have just been validated... */
-               cache_revisit_request(item);
-               return -EAGAIN;
+static void cache_defer_req(struct cache_req *req, struct cache_head *item)
+{
+       struct cache_deferred_req *dreq;
+
+       if (req->thread_wait) {
+               cache_wait_req(req, item);
+               if (!test_bit(CACHE_PENDING, &item->flags))
+                       return;
        }
-       return 0;
+       dreq = req->defer(req);
+       if (dreq == NULL)
+               return;
+       setup_deferral(dreq, item, 1);
+       if (!test_bit(CACHE_PENDING, &item->flags))
+               /* Bit could have been cleared before we managed to
+                * set up the deferral, so need to revisit just in case
+                */
+               cache_revisit_request(item);
+
+       cache_limit_defers();
 }
 
 static void cache_revisit_request(struct cache_head *item)
 {
        struct cache_deferred_req *dreq;
        struct list_head pending;
-
-       struct list_head *lp;
+       struct hlist_node *lp, *tmp;
        int hash = DFR_HASH(item);
 
        INIT_LIST_HEAD(&pending);
        spin_lock(&cache_defer_lock);
 
-       lp = cache_defer_hash[hash].next;
-       if (lp) {
-               while (lp != &cache_defer_hash[hash]) {
-                       dreq = list_entry(lp, struct cache_deferred_req, hash);
-                       lp = lp->next;
-                       if (dreq->item == item) {
-                               list_del_init(&dreq->hash);
-                               list_move(&dreq->recent, &pending);
-                               cache_defer_cnt--;
-                       }
+       hlist_for_each_entry_safe(dreq, lp, tmp, &cache_defer_hash[hash], hash)
+               if (dreq->item == item) {
+                       __unhash_deferred_req(dreq);
+                       list_add(&dreq->recent, &pending);
                }
-       }
+
        spin_unlock(&cache_defer_lock);
 
        while (!list_empty(&pending)) {
@@ -600,9 +676,8 @@ void cache_clean_deferred(void *owner)
 
        list_for_each_entry_safe(dreq, tmp, &cache_defer_list, recent) {
                if (dreq->owner == owner) {
-                       list_del_init(&dreq->hash);
-                       list_move(&dreq->recent, &pending);
-                       cache_defer_cnt--;
+                       __unhash_deferred_req(dreq);
+                       list_add(&dreq->recent, &pending);
                }
        }
        spin_unlock(&cache_defer_lock);
@@ -901,7 +976,7 @@ static int cache_release(struct inode *inode, struct file *filp,
                filp->private_data = NULL;
                kfree(rp);
 
-               cd->last_close = get_seconds();
+               cd->last_close = seconds_since_boot();
                atomic_dec(&cd->readers);
        }
        module_put(cd->owner);
@@ -1014,6 +1089,23 @@ static void warn_no_listener(struct cache_detail *detail)
        }
 }
 
+static bool cache_listeners_exist(struct cache_detail *detail)
+{
+       if (atomic_read(&detail->readers))
+               return true;
+       if (detail->last_close == 0)
+               /* This cache was never opened */
+               return false;
+       if (detail->last_close < seconds_since_boot() - 30)
+               /*
+                * We allow for the possibility that someone might
+                * restart a userspace daemon without restarting the
+                * server; but after 30 seconds, we give up.
+                */
+                return false;
+       return true;
+}
+
 /*
  * register an upcall request to user-space and queue it up for read() by the
  * upcall daemon.
@@ -1032,10 +1124,9 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h,
        char *bp;
        int len;
 
-       if (atomic_read(&detail->readers) == 0 &&
-           detail->last_close < get_seconds() - 30) {
-                       warn_no_listener(detail);
-                       return -EINVAL;
+       if (!cache_listeners_exist(detail)) {
+               warn_no_listener(detail);
+               return -EINVAL;
        }
 
        buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
@@ -1094,13 +1185,19 @@ int qword_get(char **bpp, char *dest, int bufsize)
        if (bp[0] == '\\' && bp[1] == 'x') {
                /* HEX STRING */
                bp += 2;
-               while (isxdigit(bp[0]) && isxdigit(bp[1]) && len < bufsize) {
-                       int byte = isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
-                       bp++;
-                       byte <<= 4;
-                       byte |= isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
-                       *dest++ = byte;
-                       bp++;
+               while (len < bufsize) {
+                       int h, l;
+
+                       h = hex_to_bin(bp[0]);
+                       if (h < 0)
+                               break;
+
+                       l = hex_to_bin(bp[1]);
+                       if (l < 0)
+                               break;
+
+                       *dest++ = (h << 4) | l;
+                       bp += 2;
                        len++;
                }
        } else {
@@ -1218,7 +1315,8 @@ static int c_show(struct seq_file *m, void *p)
 
        ifdebug(CACHE)
                seq_printf(m, "# expiry=%ld refcnt=%d flags=%lx\n",
-                          cp->expiry_time, atomic_read(&cp->ref.refcount), cp->flags);
+                          convert_to_wallclock(cp->expiry_time),
+                          atomic_read(&cp->ref.refcount), cp->flags);
        cache_get(cp);
        if (cache_check(cd, cp, NULL))
                /* cache_check does a cache_put on failure */
@@ -1284,7 +1382,7 @@ static ssize_t read_flush(struct file *file, char __user *buf,
        unsigned long p = *ppos;
        size_t len;
 
-       sprintf(tbuf, "%lu\n", cd->flush_time);
+       sprintf(tbuf, "%lu\n", convert_to_wallclock(cd->flush_time));
        len = strlen(tbuf);
        if (p >= len)
                return 0;
@@ -1302,19 +1400,20 @@ static ssize_t write_flush(struct file *file, const char __user *buf,
                           struct cache_detail *cd)
 {
        char tbuf[20];
-       char *ep;
-       long flushtime;
+       char *bp, *ep;
+
        if (*ppos || count > sizeof(tbuf)-1)
                return -EINVAL;
        if (copy_from_user(tbuf, buf, count))
                return -EFAULT;
        tbuf[count] = 0;
-       flushtime = simple_strtoul(tbuf, &ep, 0);
+       simple_strtoul(tbuf, &ep, 0);
        if (*ep && *ep != '\n')
                return -EINVAL;
 
-       cd->flush_time = flushtime;
-       cd->nextcheck = get_seconds();
+       bp = tbuf;
+       cd->flush_time = get_expiry(&bp);
+       cd->nextcheck = seconds_since_boot();
        cache_flush();
 
        *ppos += count;
@@ -1438,8 +1537,10 @@ static const struct file_operations cache_flush_operations_procfs = {
        .llseek         = no_llseek,
 };
 
-static void remove_cache_proc_entries(struct cache_detail *cd)
+static void remove_cache_proc_entries(struct cache_detail *cd, struct net *net)
 {
+       struct sunrpc_net *sn;
+
        if (cd->u.procfs.proc_ent == NULL)
                return;
        if (cd->u.procfs.flush_ent)
@@ -1449,15 +1550,18 @@ static void remove_cache_proc_entries(struct cache_detail *cd)
        if (cd->u.procfs.content_ent)
                remove_proc_entry("content", cd->u.procfs.proc_ent);
        cd->u.procfs.proc_ent = NULL;
-       remove_proc_entry(cd->name, proc_net_rpc);
+       sn = net_generic(net, sunrpc_net_id);
+       remove_proc_entry(cd->name, sn->proc_net_rpc);
 }
 
 #ifdef CONFIG_PROC_FS
-static int create_cache_proc_entries(struct cache_detail *cd)
+static int create_cache_proc_entries(struct cache_detail *cd, struct net *net)
 {
        struct proc_dir_entry *p;
+       struct sunrpc_net *sn;
 
-       cd->u.procfs.proc_ent = proc_mkdir(cd->name, proc_net_rpc);
+       sn = net_generic(net, sunrpc_net_id);
+       cd->u.procfs.proc_ent = proc_mkdir(cd->name, sn->proc_net_rpc);
        if (cd->u.procfs.proc_ent == NULL)
                goto out_nomem;
        cd->u.procfs.channel_ent = NULL;
@@ -1488,11 +1592,11 @@ static int create_cache_proc_entries(struct cache_detail *cd)
        }
        return 0;
 out_nomem:
-       remove_cache_proc_entries(cd);
+       remove_cache_proc_entries(cd, net);
        return -ENOMEM;
 }
 #else /* CONFIG_PROC_FS */
-static int create_cache_proc_entries(struct cache_detail *cd)
+static int create_cache_proc_entries(struct cache_detail *cd, struct net *net)
 {
        return 0;
 }
@@ -1503,23 +1607,33 @@ void __init cache_initialize(void)
        INIT_DELAYED_WORK_DEFERRABLE(&cache_cleaner, do_cache_clean);
 }
 
-int cache_register(struct cache_detail *cd)
+int cache_register_net(struct cache_detail *cd, struct net *net)
 {
        int ret;
 
        sunrpc_init_cache_detail(cd);
-       ret = create_cache_proc_entries(cd);
+       ret = create_cache_proc_entries(cd, net);
        if (ret)
                sunrpc_destroy_cache_detail(cd);
        return ret;
 }
+
+int cache_register(struct cache_detail *cd)
+{
+       return cache_register_net(cd, &init_net);
+}
 EXPORT_SYMBOL_GPL(cache_register);
 
-void cache_unregister(struct cache_detail *cd)
+void cache_unregister_net(struct cache_detail *cd, struct net *net)
 {
-       remove_cache_proc_entries(cd);
+       remove_cache_proc_entries(cd, net);
        sunrpc_destroy_cache_detail(cd);
 }
+
+void cache_unregister(struct cache_detail *cd)
+{
+       cache_unregister_net(cd, &init_net);
+}
 EXPORT_SYMBOL_GPL(cache_unregister);
 
 static ssize_t cache_read_pipefs(struct file *filp, char __user *buf,
index cbc5b8ccc8be060369ef4106ca1651abc5fcb35f..9dab9573be4146feafd1fb2db584faa3b61b0b0b 100644 (file)
@@ -284,6 +284,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
        struct rpc_xprt *xprt;
        struct rpc_clnt *clnt;
        struct xprt_create xprtargs = {
+               .net = args->net,
                .ident = args->protocol,
                .srcaddr = args->saddress,
                .dstaddr = args->address,
diff --git a/net/sunrpc/netns.h b/net/sunrpc/netns.h
new file mode 100644 (file)
index 0000000..d013bf2
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __SUNRPC_NETNS_H__
+#define __SUNRPC_NETNS_H__
+
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+
+struct cache_detail;
+
+struct sunrpc_net {
+       struct proc_dir_entry *proc_net_rpc;
+       struct cache_detail *ip_map_cache;
+};
+
+extern int sunrpc_net_id;
+
+int ip_map_cache_create(struct net *);
+void ip_map_cache_destroy(struct net *);
+
+#endif
index 63ec116b4dd421b32b636e71580d1375693953a1..fa6d7ca2c851fc6cc6614c26f1e8bb5b43362c66 100644 (file)
@@ -177,6 +177,7 @@ static DEFINE_MUTEX(rpcb_create_local_mutex);
 static int rpcb_create_local(void)
 {
        struct rpc_create_args args = {
+               .net            = &init_net,
                .protocol       = XPRT_TRANSPORT_TCP,
                .address        = (struct sockaddr *)&rpcb_inaddr_loopback,
                .addrsize       = sizeof(rpcb_inaddr_loopback),
@@ -229,6 +230,7 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
                                    size_t salen, int proto, u32 version)
 {
        struct rpc_create_args args = {
+               .net            = &init_net,
                .protocol       = proto,
                .address        = srvaddr,
                .addrsize       = salen,
@@ -248,7 +250,7 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
                ((struct sockaddr_in6 *)srvaddr)->sin6_port = htons(RPCBIND_PORT);
                break;
        default:
-               return NULL;
+               return ERR_PTR(-EAFNOSUPPORT);
        }
 
        return rpc_create(&args);
index ea1046f3f9a3cac31616499559b82aa2930effba..f71a73107ae94adeae35303f95f0b3409aee9e07 100644 (file)
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svcsock.h>
 #include <linux/sunrpc/metrics.h>
-#include <net/net_namespace.h>
 
-#define RPCDBG_FACILITY        RPCDBG_MISC
+#include "netns.h"
 
-struct proc_dir_entry  *proc_net_rpc = NULL;
+#define RPCDBG_FACILITY        RPCDBG_MISC
 
 /*
  * Get RPC client stats
@@ -218,10 +217,11 @@ EXPORT_SYMBOL_GPL(rpc_print_iostats);
 static inline struct proc_dir_entry *
 do_register(const char *name, void *data, const struct file_operations *fops)
 {
-       rpc_proc_init();
-       dprintk("RPC:       registering /proc/net/rpc/%s\n", name);
+       struct sunrpc_net *sn;
 
-       return proc_create_data(name, 0, proc_net_rpc, fops, data);
+       dprintk("RPC:       registering /proc/net/rpc/%s\n", name);
+       sn = net_generic(&init_net, sunrpc_net_id);
+       return proc_create_data(name, 0, sn->proc_net_rpc, fops, data);
 }
 
 struct proc_dir_entry *
@@ -234,7 +234,10 @@ EXPORT_SYMBOL_GPL(rpc_proc_register);
 void
 rpc_proc_unregister(const char *name)
 {
-       remove_proc_entry(name, proc_net_rpc);
+       struct sunrpc_net *sn;
+
+       sn = net_generic(&init_net, sunrpc_net_id);
+       remove_proc_entry(name, sn->proc_net_rpc);
 }
 EXPORT_SYMBOL_GPL(rpc_proc_unregister);
 
@@ -248,25 +251,29 @@ EXPORT_SYMBOL_GPL(svc_proc_register);
 void
 svc_proc_unregister(const char *name)
 {
-       remove_proc_entry(name, proc_net_rpc);
+       struct sunrpc_net *sn;
+
+       sn = net_generic(&init_net, sunrpc_net_id);
+       remove_proc_entry(name, sn->proc_net_rpc);
 }
 EXPORT_SYMBOL_GPL(svc_proc_unregister);
 
-void
-rpc_proc_init(void)
+int rpc_proc_init(struct net *net)
 {
+       struct sunrpc_net *sn;
+
        dprintk("RPC:       registering /proc/net/rpc\n");
-       if (!proc_net_rpc)
-               proc_net_rpc = proc_mkdir("rpc", init_net.proc_net);
+       sn = net_generic(net, sunrpc_net_id);
+       sn->proc_net_rpc = proc_mkdir("rpc", net->proc_net);
+       if (sn->proc_net_rpc == NULL)
+               return -ENOMEM;
+
+       return 0;
 }
 
-void
-rpc_proc_exit(void)
+void rpc_proc_exit(struct net *net)
 {
        dprintk("RPC:       unregistering /proc/net/rpc\n");
-       if (proc_net_rpc) {
-               proc_net_rpc = NULL;
-               remove_proc_entry("rpc", init_net.proc_net);
-       }
+       remove_proc_entry("rpc", net->proc_net);
 }
 
index c0d085013a2be26f7bf3f34e195cebfc0a716039..9d0809160994a7839c283bb4902fe537a5ee3999 100644 (file)
 #include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/sunrpc/xprtsock.h>
 
-extern struct cache_detail ip_map_cache, unix_gid_cache;
+#include "netns.h"
+
+int sunrpc_net_id;
+
+static __net_init int sunrpc_init_net(struct net *net)
+{
+       int err;
+
+       err = rpc_proc_init(net);
+       if (err)
+               goto err_proc;
+
+       err = ip_map_cache_create(net);
+       if (err)
+               goto err_ipmap;
+
+       return 0;
+
+err_ipmap:
+       rpc_proc_exit(net);
+err_proc:
+       return err;
+}
+
+static __net_exit void sunrpc_exit_net(struct net *net)
+{
+       ip_map_cache_destroy(net);
+       rpc_proc_exit(net);
+}
+
+static struct pernet_operations sunrpc_net_ops = {
+       .init = sunrpc_init_net,
+       .exit = sunrpc_exit_net,
+       .id = &sunrpc_net_id,
+       .size = sizeof(struct sunrpc_net),
+};
+
+extern struct cache_detail unix_gid_cache;
 
 extern void cleanup_rpcb_clnt(void);
 
@@ -38,18 +75,22 @@ init_sunrpc(void)
        err = rpcauth_init_module();
        if (err)
                goto out3;
+
+       cache_initialize();
+
+       err = register_pernet_subsys(&sunrpc_net_ops);
+       if (err)
+               goto out4;
 #ifdef RPC_DEBUG
        rpc_register_sysctl();
 #endif
-#ifdef CONFIG_PROC_FS
-       rpc_proc_init();
-#endif
-       cache_initialize();
-       cache_register(&ip_map_cache);
        cache_register(&unix_gid_cache);
        svc_init_xprt_sock();   /* svc sock transport */
        init_socket_xprt();     /* clnt sock transport */
        return 0;
+
+out4:
+       rpcauth_remove_module();
 out3:
        rpc_destroy_mempool();
 out2:
@@ -67,13 +108,10 @@ cleanup_sunrpc(void)
        svc_cleanup_xprt_sock();
        unregister_rpc_pipefs();
        rpc_destroy_mempool();
-       cache_unregister(&ip_map_cache);
        cache_unregister(&unix_gid_cache);
+       unregister_pernet_subsys(&sunrpc_net_ops);
 #ifdef RPC_DEBUG
        rpc_unregister_sysctl();
-#endif
-#ifdef CONFIG_PROC_FS
-       rpc_proc_exit();
 #endif
        rcu_barrier(); /* Wait for completion of call_rcu()'s */
 }
index d9017d64597e2beabb7f2edb9147018613d7c412..6359c42c49410f73fbf9e8c7c570964f1ca739b1 100644 (file)
@@ -1055,6 +1055,9 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
                goto err_bad;
        case SVC_DENIED:
                goto err_bad_auth;
+       case SVC_CLOSE:
+               if (test_bit(XPT_TEMP, &rqstp->rq_xprt->xpt_flags))
+                       svc_close_xprt(rqstp->rq_xprt);
        case SVC_DROP:
                goto dropit;
        case SVC_COMPLETE:
index cbc084939dd8af2b2e009098af2875c96fbc363f..c82fe739fbdcc685fae1e59f1fdb7c9e5afd30b0 100644 (file)
@@ -100,16 +100,14 @@ EXPORT_SYMBOL_GPL(svc_unreg_xprt_class);
  */
 int svc_print_xprts(char *buf, int maxlen)
 {
-       struct list_head *le;
+       struct svc_xprt_class *xcl;
        char tmpstr[80];
        int len = 0;
        buf[0] = '\0';
 
        spin_lock(&svc_xprt_class_lock);
-       list_for_each(le, &svc_xprt_class_list) {
+       list_for_each_entry(xcl, &svc_xprt_class_list, xcl_list) {
                int slen;
-               struct svc_xprt_class *xcl =
-                       list_entry(le, struct svc_xprt_class, xcl_list);
 
                sprintf(tmpstr, "%s %d\n", xcl->xcl_name, xcl->xcl_max_payload);
                slen = strlen(tmpstr);
@@ -128,9 +126,9 @@ static void svc_xprt_free(struct kref *kref)
        struct svc_xprt *xprt =
                container_of(kref, struct svc_xprt, xpt_ref);
        struct module *owner = xprt->xpt_class->xcl_owner;
-       if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags) &&
-           xprt->xpt_auth_cache != NULL)
-               svcauth_unix_info_release(xprt->xpt_auth_cache);
+       if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags))
+               svcauth_unix_info_release(xprt);
+       put_net(xprt->xpt_net);
        xprt->xpt_ops->xpo_free(xprt);
        module_put(owner);
 }
@@ -156,15 +154,18 @@ void svc_xprt_init(struct svc_xprt_class *xcl, struct svc_xprt *xprt,
        INIT_LIST_HEAD(&xprt->xpt_list);
        INIT_LIST_HEAD(&xprt->xpt_ready);
        INIT_LIST_HEAD(&xprt->xpt_deferred);
+       INIT_LIST_HEAD(&xprt->xpt_users);
        mutex_init(&xprt->xpt_mutex);
        spin_lock_init(&xprt->xpt_lock);
        set_bit(XPT_BUSY, &xprt->xpt_flags);
        rpc_init_wait_queue(&xprt->xpt_bc_pending, "xpt_bc_pending");
+       xprt->xpt_net = get_net(&init_net);
 }
 EXPORT_SYMBOL_GPL(svc_xprt_init);
 
 static struct svc_xprt *__svc_xpo_create(struct svc_xprt_class *xcl,
                                         struct svc_serv *serv,
+                                        struct net *net,
                                         const int family,
                                         const unsigned short port,
                                         int flags)
@@ -199,12 +200,12 @@ static struct svc_xprt *__svc_xpo_create(struct svc_xprt_class *xcl,
                return ERR_PTR(-EAFNOSUPPORT);
        }
 
-       return xcl->xcl_ops->xpo_create(serv, sap, len, flags);
+       return xcl->xcl_ops->xpo_create(serv, net, sap, len, flags);
 }
 
 int svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
-                   const int family, const unsigned short port,
-                   int flags)
+                   struct net *net, const int family,
+                   const unsigned short port, int flags)
 {
        struct svc_xprt_class *xcl;
 
@@ -220,7 +221,7 @@ int svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
                        goto err;
 
                spin_unlock(&svc_xprt_class_lock);
-               newxprt = __svc_xpo_create(xcl, serv, family, port, flags);
+               newxprt = __svc_xpo_create(xcl, serv, net, family, port, flags);
                if (IS_ERR(newxprt)) {
                        module_put(xcl->xcl_owner);
                        return PTR_ERR(newxprt);
@@ -329,12 +330,6 @@ void svc_xprt_enqueue(struct svc_xprt *xprt)
                       "svc_xprt_enqueue: "
                       "threads and transports both waiting??\n");
 
-       if (test_bit(XPT_DEAD, &xprt->xpt_flags)) {
-               /* Don't enqueue dead transports */
-               dprintk("svc: transport %p is dead, not enqueued\n", xprt);
-               goto out_unlock;
-       }
-
        pool->sp_stats.packets++;
 
        /* Mark transport as busy. It will remain in this state until
@@ -651,6 +646,11 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
        if (signalled() || kthread_should_stop())
                return -EINTR;
 
+       /* Normally we will wait up to 5 seconds for any required
+        * cache information to be provided.
+        */
+       rqstp->rq_chandle.thread_wait = 5*HZ;
+
        spin_lock_bh(&pool->sp_lock);
        xprt = svc_xprt_dequeue(pool);
        if (xprt) {
@@ -658,6 +658,12 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
                svc_xprt_get(xprt);
                rqstp->rq_reserved = serv->sv_max_mesg;
                atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved);
+
+               /* As there is a shortage of threads and this request
+                * had to be queued, don't allow the thread to wait so
+                * long for cache updates.
+                */
+               rqstp->rq_chandle.thread_wait = 1*HZ;
        } else {
                /* No data pending. Go to sleep */
                svc_thread_enqueue(pool, rqstp);
@@ -868,6 +874,19 @@ static void svc_age_temp_xprts(unsigned long closure)
        mod_timer(&serv->sv_temptimer, jiffies + svc_conn_age_period * HZ);
 }
 
+static void call_xpt_users(struct svc_xprt *xprt)
+{
+       struct svc_xpt_user *u;
+
+       spin_lock(&xprt->xpt_lock);
+       while (!list_empty(&xprt->xpt_users)) {
+               u = list_first_entry(&xprt->xpt_users, struct svc_xpt_user, list);
+               list_del(&u->list);
+               u->callback(u);
+       }
+       spin_unlock(&xprt->xpt_lock);
+}
+
 /*
  * Remove a dead transport
  */
@@ -878,7 +897,7 @@ void svc_delete_xprt(struct svc_xprt *xprt)
 
        /* Only do this once */
        if (test_and_set_bit(XPT_DEAD, &xprt->xpt_flags))
-               return;
+               BUG();
 
        dprintk("svc: svc_delete_xprt(%p)\n", xprt);
        xprt->xpt_ops->xpo_detach(xprt);
@@ -900,6 +919,7 @@ void svc_delete_xprt(struct svc_xprt *xprt)
        while ((dr = svc_deferred_dequeue(xprt)) != NULL)
                kfree(dr);
 
+       call_xpt_users(xprt);
        svc_xprt_put(xprt);
 }
 
@@ -910,10 +930,7 @@ void svc_close_xprt(struct svc_xprt *xprt)
                /* someone else will have to effect the close */
                return;
 
-       svc_xprt_get(xprt);
        svc_delete_xprt(xprt);
-       clear_bit(XPT_BUSY, &xprt->xpt_flags);
-       svc_xprt_put(xprt);
 }
 EXPORT_SYMBOL_GPL(svc_close_xprt);
 
index 207311610988a3c3f29a2bf3ce2ea37320d237f1..560677d187f1b0fc7eb3d72c119aa848eb306145 100644 (file)
@@ -18,6 +18,8 @@
 
 #include <linux/sunrpc/clnt.h>
 
+#include "netns.h"
+
 /*
  * AUTHUNIX and AUTHNULL credentials are both handled here.
  * AUTHNULL is treated just like AUTHUNIX except that the uid/gid
@@ -92,7 +94,6 @@ struct ip_map {
        struct unix_domain      *m_client;
        int                     m_add_change;
 };
-static struct cache_head       *ip_table[IP_HASHMAX];
 
 static void ip_map_put(struct kref *kref)
 {
@@ -178,8 +179,8 @@ static int ip_map_upcall(struct cache_detail *cd, struct cache_head *h)
        return sunrpc_cache_pipe_upcall(cd, h, ip_map_request);
 }
 
-static struct ip_map *ip_map_lookup(char *class, struct in6_addr *addr);
-static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t expiry);
+static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class, struct in6_addr *addr);
+static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm, struct unix_domain *udom, time_t expiry);
 
 static int ip_map_parse(struct cache_detail *cd,
                          char *mesg, int mlen)
@@ -219,10 +220,9 @@ static int ip_map_parse(struct cache_detail *cd,
        switch (address.sa.sa_family) {
        case AF_INET:
                /* Form a mapped IPv4 address in sin6 */
-               memset(&sin6, 0, sizeof(sin6));
                sin6.sin6_family = AF_INET6;
-               sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
-               sin6.sin6_addr.s6_addr32[3] = address.s4.sin_addr.s_addr;
+               ipv6_addr_set_v4mapped(address.s4.sin_addr.s_addr,
+                               &sin6.sin6_addr);
                break;
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        case AF_INET6:
@@ -249,9 +249,9 @@ static int ip_map_parse(struct cache_detail *cd,
                dom = NULL;
 
        /* IPv6 scope IDs are ignored for now */
-       ipmp = ip_map_lookup(class, &sin6.sin6_addr);
+       ipmp = __ip_map_lookup(cd, class, &sin6.sin6_addr);
        if (ipmp) {
-               err = ip_map_update(ipmp,
+               err = __ip_map_update(cd, ipmp,
                             container_of(dom, struct unix_domain, h),
                             expiry);
        } else
@@ -294,29 +294,15 @@ static int ip_map_show(struct seq_file *m,
 }
 
 
-struct cache_detail ip_map_cache = {
-       .owner          = THIS_MODULE,
-       .hash_size      = IP_HASHMAX,
-       .hash_table     = ip_table,
-       .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,
-};
-
-static struct ip_map *ip_map_lookup(char *class, struct in6_addr *addr)
+static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class,
+               struct in6_addr *addr)
 {
        struct ip_map ip;
        struct cache_head *ch;
 
        strcpy(ip.m_class, class);
        ipv6_addr_copy(&ip.m_addr, addr);
-       ch = sunrpc_cache_lookup(&ip_map_cache, &ip.h,
+       ch = sunrpc_cache_lookup(cd, &ip.h,
                                 hash_str(class, IP_HASHBITS) ^
                                 hash_ip6(*addr));
 
@@ -326,7 +312,17 @@ static struct ip_map *ip_map_lookup(char *class, struct in6_addr *addr)
                return NULL;
 }
 
-static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t expiry)
+static inline struct ip_map *ip_map_lookup(struct net *net, char *class,
+               struct in6_addr *addr)
+{
+       struct sunrpc_net *sn;
+
+       sn = net_generic(net, sunrpc_net_id);
+       return __ip_map_lookup(sn->ip_map_cache, class, addr);
+}
+
+static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm,
+               struct unix_domain *udom, time_t expiry)
 {
        struct ip_map ip;
        struct cache_head *ch;
@@ -344,17 +340,25 @@ static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t ex
                        ip.m_add_change++;
        }
        ip.h.expiry_time = expiry;
-       ch = sunrpc_cache_update(&ip_map_cache,
-                                &ip.h, &ipm->h,
+       ch = sunrpc_cache_update(cd, &ip.h, &ipm->h,
                                 hash_str(ipm->m_class, IP_HASHBITS) ^
                                 hash_ip6(ipm->m_addr));
        if (!ch)
                return -ENOMEM;
-       cache_put(ch, &ip_map_cache);
+       cache_put(ch, cd);
        return 0;
 }
 
-int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom)
+static inline int ip_map_update(struct net *net, struct ip_map *ipm,
+               struct unix_domain *udom, time_t expiry)
+{
+       struct sunrpc_net *sn;
+
+       sn = net_generic(net, sunrpc_net_id);
+       return __ip_map_update(sn->ip_map_cache, ipm, udom, expiry);
+}
+
+int auth_unix_add_addr(struct net *net, struct in6_addr *addr, struct auth_domain *dom)
 {
        struct unix_domain *udom;
        struct ip_map *ipmp;
@@ -362,10 +366,10 @@ int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom)
        if (dom->flavour != &svcauth_unix)
                return -EINVAL;
        udom = container_of(dom, struct unix_domain, h);
-       ipmp = ip_map_lookup("nfsd", addr);
+       ipmp = ip_map_lookup(net, "nfsd", addr);
 
        if (ipmp)
-               return ip_map_update(ipmp, udom, NEVER);
+               return ip_map_update(net, ipmp, udom, NEVER);
        else
                return -ENOMEM;
 }
@@ -383,16 +387,18 @@ int auth_unix_forget_old(struct auth_domain *dom)
 }
 EXPORT_SYMBOL_GPL(auth_unix_forget_old);
 
-struct auth_domain *auth_unix_lookup(struct in6_addr *addr)
+struct auth_domain *auth_unix_lookup(struct net *net, struct in6_addr *addr)
 {
        struct ip_map *ipm;
        struct auth_domain *rv;
+       struct sunrpc_net *sn;
 
-       ipm = ip_map_lookup("nfsd", addr);
+       sn = net_generic(net, sunrpc_net_id);
+       ipm = ip_map_lookup(net, "nfsd", addr);
 
        if (!ipm)
                return NULL;
-       if (cache_check(&ip_map_cache, &ipm->h, NULL))
+       if (cache_check(sn->ip_map_cache, &ipm->h, NULL))
                return NULL;
 
        if ((ipm->m_client->addr_changes - ipm->m_add_change) >0) {
@@ -403,22 +409,29 @@ struct auth_domain *auth_unix_lookup(struct in6_addr *addr)
                rv = &ipm->m_client->h;
                kref_get(&rv->ref);
        }
-       cache_put(&ipm->h, &ip_map_cache);
+       cache_put(&ipm->h, sn->ip_map_cache);
        return rv;
 }
 EXPORT_SYMBOL_GPL(auth_unix_lookup);
 
 void svcauth_unix_purge(void)
 {
-       cache_purge(&ip_map_cache);
+       struct net *net;
+
+       for_each_net(net) {
+               struct sunrpc_net *sn;
+
+               sn = net_generic(net, sunrpc_net_id);
+               cache_purge(sn->ip_map_cache);
+       }
 }
 EXPORT_SYMBOL_GPL(svcauth_unix_purge);
 
 static inline struct ip_map *
-ip_map_cached_get(struct svc_rqst *rqstp)
+ip_map_cached_get(struct svc_xprt *xprt)
 {
        struct ip_map *ipm = NULL;
-       struct svc_xprt *xprt = rqstp->rq_xprt;
+       struct sunrpc_net *sn;
 
        if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) {
                spin_lock(&xprt->xpt_lock);
@@ -430,9 +443,10 @@ ip_map_cached_get(struct svc_rqst *rqstp)
                                 * remembered, e.g. by a second mount from the
                                 * same IP address.
                                 */
+                               sn = net_generic(xprt->xpt_net, sunrpc_net_id);
                                xprt->xpt_auth_cache = NULL;
                                spin_unlock(&xprt->xpt_lock);
-                               cache_put(&ipm->h, &ip_map_cache);
+                               cache_put(&ipm->h, sn->ip_map_cache);
                                return NULL;
                        }
                        cache_get(&ipm->h);
@@ -443,10 +457,8 @@ ip_map_cached_get(struct svc_rqst *rqstp)
 }
 
 static inline void
-ip_map_cached_put(struct svc_rqst *rqstp, struct ip_map *ipm)
+ip_map_cached_put(struct svc_xprt *xprt, struct ip_map *ipm)
 {
-       struct svc_xprt *xprt = rqstp->rq_xprt;
-
        if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) {
                spin_lock(&xprt->xpt_lock);
                if (xprt->xpt_auth_cache == NULL) {
@@ -456,15 +468,26 @@ ip_map_cached_put(struct svc_rqst *rqstp, struct ip_map *ipm)
                }
                spin_unlock(&xprt->xpt_lock);
        }
-       if (ipm)
-               cache_put(&ipm->h, &ip_map_cache);
+       if (ipm) {
+               struct sunrpc_net *sn;
+
+               sn = net_generic(xprt->xpt_net, sunrpc_net_id);
+               cache_put(&ipm->h, sn->ip_map_cache);
+       }
 }
 
 void
-svcauth_unix_info_release(void *info)
+svcauth_unix_info_release(struct svc_xprt *xpt)
 {
-       struct ip_map *ipm = info;
-       cache_put(&ipm->h, &ip_map_cache);
+       struct ip_map *ipm;
+
+       ipm = xpt->xpt_auth_cache;
+       if (ipm != NULL) {
+               struct sunrpc_net *sn;
+
+               sn = net_generic(xpt->xpt_net, sunrpc_net_id);
+               cache_put(&ipm->h, sn->ip_map_cache);
+       }
 }
 
 /****************************************************************************
@@ -674,6 +697,8 @@ static struct group_info *unix_gid_find(uid_t uid, struct svc_rqst *rqstp)
        switch (ret) {
        case -ENOENT:
                return ERR_PTR(-ENOENT);
+       case -ETIMEDOUT:
+               return ERR_PTR(-ESHUTDOWN);
        case 0:
                gi = get_group_info(ug->gi);
                cache_put(&ug->h, &unix_gid_cache);
@@ -691,6 +716,9 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)
        struct ip_map *ipm;
        struct group_info *gi;
        struct svc_cred *cred = &rqstp->rq_cred;
+       struct svc_xprt *xprt = rqstp->rq_xprt;
+       struct net *net = xprt->xpt_net;
+       struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
 
        switch (rqstp->rq_addr.ss_family) {
        case AF_INET:
@@ -709,26 +737,27 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)
        if (rqstp->rq_proc == 0)
                return SVC_OK;
 
-       ipm = ip_map_cached_get(rqstp);
+       ipm = ip_map_cached_get(xprt);
        if (ipm == NULL)
-               ipm = ip_map_lookup(rqstp->rq_server->sv_program->pg_class,
+               ipm = __ip_map_lookup(sn->ip_map_cache, rqstp->rq_server->sv_program->pg_class,
                                    &sin6->sin6_addr);
 
        if (ipm == NULL)
                return SVC_DENIED;
 
-       switch (cache_check(&ip_map_cache, &ipm->h, &rqstp->rq_chandle)) {
+       switch (cache_check(sn->ip_map_cache, &ipm->h, &rqstp->rq_chandle)) {
                default:
                        BUG();
-               case -EAGAIN:
                case -ETIMEDOUT:
+                       return SVC_CLOSE;
+               case -EAGAIN:
                        return SVC_DROP;
                case -ENOENT:
                        return SVC_DENIED;
                case 0:
                        rqstp->rq_client = &ipm->m_client->h;
                        kref_get(&rqstp->rq_client->ref);
-                       ip_map_cached_put(rqstp, ipm);
+                       ip_map_cached_put(xprt, ipm);
                        break;
        }
 
@@ -736,6 +765,8 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)
        switch (PTR_ERR(gi)) {
        case -EAGAIN:
                return SVC_DROP;
+       case -ESHUTDOWN:
+               return SVC_CLOSE;
        case -ENOENT:
                break;
        default:
@@ -776,7 +807,7 @@ svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp)
        cred->cr_gid = (gid_t) -1;
        cred->cr_group_info = groups_alloc(0);
        if (cred->cr_group_info == NULL)
-               return SVC_DROP; /* kmalloc failure - client must retry */
+               return SVC_CLOSE; /* kmalloc failure - client must retry */
 
        /* Put NULL verifier */
        svc_putnl(resv, RPC_AUTH_NULL);
@@ -840,7 +871,7 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)
                goto badcred;
        cred->cr_group_info = groups_alloc(slen);
        if (cred->cr_group_info == NULL)
-               return SVC_DROP;
+               return SVC_CLOSE;
        for (i = 0; i < slen; i++)
                GROUP_AT(cred->cr_group_info, i) = svc_getnl(argv);
        if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
@@ -886,3 +917,56 @@ struct auth_ops svcauth_unix = {
        .set_client     = svcauth_unix_set_client,
 };
 
+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);
+
+       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,
+
+       err = cache_register_net(cd, net);
+       if (err)
+               goto err_reg;
+
+       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;
+
+       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);
+}
index 7e534dd0907720073171365cc218b25e012b4c0c..07919e16be3ef3a274d7c099c2e58d2884067edf 100644 (file)
@@ -64,7 +64,8 @@ static void           svc_tcp_sock_detach(struct svc_xprt *);
 static void            svc_sock_free(struct svc_xprt *);
 
 static struct svc_xprt *svc_create_socket(struct svc_serv *, int,
-                                         struct sockaddr *, int, int);
+                                         struct net *, struct sockaddr *,
+                                         int, int);
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 static struct lock_class_key svc_key[2];
 static struct lock_class_key svc_slock_key[2];
@@ -657,10 +658,11 @@ static struct svc_xprt *svc_udp_accept(struct svc_xprt *xprt)
 }
 
 static struct svc_xprt *svc_udp_create(struct svc_serv *serv,
+                                      struct net *net,
                                       struct sockaddr *sa, int salen,
                                       int flags)
 {
-       return svc_create_socket(serv, IPPROTO_UDP, sa, salen, flags);
+       return svc_create_socket(serv, IPPROTO_UDP, net, sa, salen, flags);
 }
 
 static struct svc_xprt_ops svc_udp_ops = {
@@ -1133,9 +1135,6 @@ static int svc_tcp_sendto(struct svc_rqst *rqstp)
        reclen = htonl(0x80000000|((xbufp->len ) - 4));
        memcpy(xbufp->head[0].iov_base, &reclen, 4);
 
-       if (test_bit(XPT_DEAD, &rqstp->rq_xprt->xpt_flags))
-               return -ENOTCONN;
-
        sent = svc_sendto(rqstp, &rqstp->rq_res);
        if (sent != xbufp->len) {
                printk(KERN_NOTICE
@@ -1178,10 +1177,11 @@ static int svc_tcp_has_wspace(struct svc_xprt *xprt)
 }
 
 static struct svc_xprt *svc_tcp_create(struct svc_serv *serv,
+                                      struct net *net,
                                       struct sockaddr *sa, int salen,
                                       int flags)
 {
-       return svc_create_socket(serv, IPPROTO_TCP, sa, salen, flags);
+       return svc_create_socket(serv, IPPROTO_TCP, net, sa, salen, flags);
 }
 
 static struct svc_xprt_ops svc_tcp_ops = {
@@ -1258,19 +1258,13 @@ void svc_sock_update_bufs(struct svc_serv *serv)
         * The number of server threads has changed. Update
         * rcvbuf and sndbuf accordingly on all sockets
         */
-       struct list_head *le;
+       struct svc_sock *svsk;
 
        spin_lock_bh(&serv->sv_lock);
-       list_for_each(le, &serv->sv_permsocks) {
-               struct svc_sock *svsk =
-                       list_entry(le, struct svc_sock, sk_xprt.xpt_list);
+       list_for_each_entry(svsk, &serv->sv_permsocks, sk_xprt.xpt_list)
                set_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags);
-       }
-       list_for_each(le, &serv->sv_tempsocks) {
-               struct svc_sock *svsk =
-                       list_entry(le, struct svc_sock, sk_xprt.xpt_list);
+       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);
@@ -1385,6 +1379,7 @@ EXPORT_SYMBOL_GPL(svc_addsock);
  */
 static struct svc_xprt *svc_create_socket(struct svc_serv *serv,
                                          int protocol,
+                                         struct net *net,
                                          struct sockaddr *sin, int len,
                                          int flags)
 {
@@ -1421,7 +1416,7 @@ static struct svc_xprt *svc_create_socket(struct svc_serv *serv,
                return ERR_PTR(-EINVAL);
        }
 
-       error = sock_create_kern(family, type, protocol, &sock);
+       error = __sock_create(net, family, type, protocol, &sock, 1);
        if (error < 0)
                return ERR_PTR(error);
 
index 970fb00f388ce532d7f0a4e3c7285f98e57af2f5..4c8f18aff7c341885764c6f1525a292688328c5d 100644 (file)
@@ -199,8 +199,6 @@ int xprt_reserve_xprt(struct rpc_task *task)
        if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) {
                if (task == xprt->snd_task)
                        return 1;
-               if (task == NULL)
-                       return 0;
                goto out_sleep;
        }
        xprt->snd_task = task;
@@ -757,13 +755,11 @@ static void xprt_connect_status(struct rpc_task *task)
  */
 struct rpc_rqst *xprt_lookup_rqst(struct rpc_xprt *xprt, __be32 xid)
 {
-       struct list_head *pos;
+       struct rpc_rqst *entry;
 
-       list_for_each(pos, &xprt->recv) {
-               struct rpc_rqst *entry = list_entry(pos, struct rpc_rqst, rq_list);
+       list_for_each_entry(entry, &xprt->recv, rq_list)
                if (entry->rq_xid == xid)
                        return entry;
-       }
 
        dprintk("RPC:       xprt_lookup_rqst did not find xid %08x\n",
                        ntohl(xid));
@@ -962,6 +958,37 @@ static void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
        spin_unlock(&xprt->reserve_lock);
 }
 
+struct rpc_xprt *xprt_alloc(struct net *net, int size, int max_req)
+{
+       struct rpc_xprt *xprt;
+
+       xprt = kzalloc(size, GFP_KERNEL);
+       if (xprt == NULL)
+               goto out;
+
+       xprt->max_reqs = max_req;
+       xprt->slot = kcalloc(max_req, sizeof(struct rpc_rqst), GFP_KERNEL);
+       if (xprt->slot == NULL)
+               goto out_free;
+
+       xprt->xprt_net = get_net(net);
+       return xprt;
+
+out_free:
+       kfree(xprt);
+out:
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(xprt_alloc);
+
+void xprt_free(struct rpc_xprt *xprt)
+{
+       put_net(xprt->xprt_net);
+       kfree(xprt->slot);
+       kfree(xprt);
+}
+EXPORT_SYMBOL_GPL(xprt_free);
+
 /**
  * xprt_reserve - allocate an RPC request slot
  * @task: RPC task requesting a slot allocation
index d718b8fa95253c61d0e0adedd0bccf5cd84d89ed..09af4fab1a456c32c5c7b414ab8923cb29a8ef02 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/slab.h>
 #include <linux/fs.h>
 #include <linux/sysctl.h>
+#include <linux/workqueue.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/sched.h>
 #include <linux/sunrpc/svc_rdma.h>
@@ -74,6 +75,8 @@ atomic_t rdma_stat_sq_prod;
 struct kmem_cache *svc_rdma_map_cachep;
 struct kmem_cache *svc_rdma_ctxt_cachep;
 
+struct workqueue_struct *svc_rdma_wq;
+
 /*
  * This function implements reading and resetting an atomic_t stat
  * variable through read/write to a proc file. Any write to the file
@@ -231,7 +234,7 @@ static ctl_table svcrdma_root_table[] = {
 void svc_rdma_cleanup(void)
 {
        dprintk("SVCRDMA Module Removed, deregister RPC RDMA transport\n");
-       flush_scheduled_work();
+       destroy_workqueue(svc_rdma_wq);
        if (svcrdma_table_header) {
                unregister_sysctl_table(svcrdma_table_header);
                svcrdma_table_header = NULL;
@@ -249,6 +252,11 @@ int svc_rdma_init(void)
        dprintk("\tsq_depth         : %d\n",
                svcrdma_max_requests * RPCRDMA_SQ_DEPTH_MULT);
        dprintk("\tmax_inline       : %d\n", svcrdma_max_req_size);
+
+       svc_rdma_wq = alloc_workqueue("svc_rdma", 0, 0);
+       if (!svc_rdma_wq)
+               return -ENOMEM;
+
        if (!svcrdma_table_header)
                svcrdma_table_header =
                        register_sysctl_table(svcrdma_root_table);
@@ -283,6 +291,7 @@ int svc_rdma_init(void)
        kmem_cache_destroy(svc_rdma_map_cachep);
  err0:
        unregister_sysctl_table(svcrdma_table_header);
+       destroy_workqueue(svc_rdma_wq);
        return -ENOMEM;
 }
 MODULE_AUTHOR("Tom Tucker <tom@opengridcomputing.com>");
index 0194de814933aea3c36d41a6c014eca68c244bd2..df67211c4bafa34449d7449cf221e354884bd39d 100644 (file)
@@ -263,9 +263,9 @@ static int fast_reg_read_chunks(struct svcxprt_rdma *xprt,
        frmr->page_list_len = PAGE_ALIGN(byte_count) >> PAGE_SHIFT;
        for (page_no = 0; page_no < frmr->page_list_len; page_no++) {
                frmr->page_list->page_list[page_no] =
-                       ib_dma_map_single(xprt->sc_cm_id->device,
-                                         page_address(rqstp->rq_arg.pages[page_no]),
-                                         PAGE_SIZE, DMA_FROM_DEVICE);
+                       ib_dma_map_page(xprt->sc_cm_id->device,
+                                       rqstp->rq_arg.pages[page_no], 0,
+                                       PAGE_SIZE, DMA_FROM_DEVICE);
                if (ib_dma_mapping_error(xprt->sc_cm_id->device,
                                         frmr->page_list->page_list[page_no]))
                        goto fatal_err;
@@ -309,17 +309,21 @@ static int rdma_set_ctxt_sge(struct svcxprt_rdma *xprt,
                             int count)
 {
        int i;
+       unsigned long off;
 
        ctxt->count = count;
        ctxt->direction = DMA_FROM_DEVICE;
        for (i = 0; i < count; i++) {
                ctxt->sge[i].length = 0; /* in case map fails */
                if (!frmr) {
+                       BUG_ON(0 == virt_to_page(vec[i].iov_base));
+                       off = (unsigned long)vec[i].iov_base & ~PAGE_MASK;
                        ctxt->sge[i].addr =
-                               ib_dma_map_single(xprt->sc_cm_id->device,
-                                                 vec[i].iov_base,
-                                                 vec[i].iov_len,
-                                                 DMA_FROM_DEVICE);
+                               ib_dma_map_page(xprt->sc_cm_id->device,
+                                               virt_to_page(vec[i].iov_base),
+                                               off,
+                                               vec[i].iov_len,
+                                               DMA_FROM_DEVICE);
                        if (ib_dma_mapping_error(xprt->sc_cm_id->device,
                                                 ctxt->sge[i].addr))
                                return -EINVAL;
@@ -491,6 +495,7 @@ next_sge:
                        printk(KERN_ERR "svcrdma: Error %d posting RDMA_READ\n",
                               err);
                        set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
+                       svc_rdma_unmap_dma(ctxt);
                        svc_rdma_put_context(ctxt, 0);
                        goto out;
                }
index b15e1ebb2bfaca8ea39753a05d3fdd0fc630b92c..249a835b703f1f0ec46c6051ff98249227876509 100644 (file)
@@ -70,8 +70,8 @@
  * on extra page for the RPCRMDA header.
  */
 static int fast_reg_xdr(struct svcxprt_rdma *xprt,
-                struct xdr_buf *xdr,
-                struct svc_rdma_req_map *vec)
+                       struct xdr_buf *xdr,
+                       struct svc_rdma_req_map *vec)
 {
        int sge_no;
        u32 sge_bytes;
@@ -96,21 +96,25 @@ static int fast_reg_xdr(struct svcxprt_rdma *xprt,
        vec->count = 2;
        sge_no++;
 
-       /* Build the FRMR */
+       /* Map the XDR head */
        frmr->kva = frva;
        frmr->direction = DMA_TO_DEVICE;
        frmr->access_flags = 0;
        frmr->map_len = PAGE_SIZE;
        frmr->page_list_len = 1;
+       page_off = (unsigned long)xdr->head[0].iov_base & ~PAGE_MASK;
        frmr->page_list->page_list[page_no] =
-               ib_dma_map_single(xprt->sc_cm_id->device,
-                                 (void *)xdr->head[0].iov_base,
-                                 PAGE_SIZE, DMA_TO_DEVICE);
+               ib_dma_map_page(xprt->sc_cm_id->device,
+                               virt_to_page(xdr->head[0].iov_base),
+                               page_off,
+                               PAGE_SIZE - page_off,
+                               DMA_TO_DEVICE);
        if (ib_dma_mapping_error(xprt->sc_cm_id->device,
                                 frmr->page_list->page_list[page_no]))
                goto fatal_err;
        atomic_inc(&xprt->sc_dma_used);
 
+       /* Map the XDR page list */
        page_off = xdr->page_base;
        page_bytes = xdr->page_len + page_off;
        if (!page_bytes)
@@ -128,9 +132,9 @@ static int fast_reg_xdr(struct svcxprt_rdma *xprt,
                page_bytes -= sge_bytes;
 
                frmr->page_list->page_list[page_no] =
-                       ib_dma_map_single(xprt->sc_cm_id->device,
-                                         page_address(page),
-                                         PAGE_SIZE, DMA_TO_DEVICE);
+                       ib_dma_map_page(xprt->sc_cm_id->device,
+                                       page, page_off,
+                                       sge_bytes, DMA_TO_DEVICE);
                if (ib_dma_mapping_error(xprt->sc_cm_id->device,
                                         frmr->page_list->page_list[page_no]))
                        goto fatal_err;
@@ -166,8 +170,10 @@ static int fast_reg_xdr(struct svcxprt_rdma *xprt,
                vec->sge[sge_no].iov_base = frva + frmr->map_len + page_off;
 
                frmr->page_list->page_list[page_no] =
-                       ib_dma_map_single(xprt->sc_cm_id->device, va, PAGE_SIZE,
-                                         DMA_TO_DEVICE);
+                   ib_dma_map_page(xprt->sc_cm_id->device, virt_to_page(va),
+                                   page_off,
+                                   PAGE_SIZE,
+                                   DMA_TO_DEVICE);
                if (ib_dma_mapping_error(xprt->sc_cm_id->device,
                                         frmr->page_list->page_list[page_no]))
                        goto fatal_err;
@@ -245,6 +251,35 @@ static int map_xdr(struct svcxprt_rdma *xprt,
        return 0;
 }
 
+static dma_addr_t dma_map_xdr(struct svcxprt_rdma *xprt,
+                             struct xdr_buf *xdr,
+                             u32 xdr_off, size_t len, int dir)
+{
+       struct page *page;
+       dma_addr_t dma_addr;
+       if (xdr_off < xdr->head[0].iov_len) {
+               /* This offset is in the head */
+               xdr_off += (unsigned long)xdr->head[0].iov_base & ~PAGE_MASK;
+               page = virt_to_page(xdr->head[0].iov_base);
+       } else {
+               xdr_off -= xdr->head[0].iov_len;
+               if (xdr_off < xdr->page_len) {
+                       /* This offset is in the page list */
+                       page = xdr->pages[xdr_off >> PAGE_SHIFT];
+                       xdr_off &= ~PAGE_MASK;
+               } else {
+                       /* This offset is in the tail */
+                       xdr_off -= xdr->page_len;
+                       xdr_off += (unsigned long)
+                               xdr->tail[0].iov_base & ~PAGE_MASK;
+                       page = virt_to_page(xdr->tail[0].iov_base);
+               }
+       }
+       dma_addr = ib_dma_map_page(xprt->sc_cm_id->device, page, xdr_off,
+                                  min_t(size_t, PAGE_SIZE, len), dir);
+       return dma_addr;
+}
+
 /* Assumptions:
  * - We are using FRMR
  *     - or -
@@ -293,10 +328,9 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
                sge[sge_no].length = sge_bytes;
                if (!vec->frmr) {
                        sge[sge_no].addr =
-                               ib_dma_map_single(xprt->sc_cm_id->device,
-                                                 (void *)
-                                                 vec->sge[xdr_sge_no].iov_base + sge_off,
-                                                 sge_bytes, DMA_TO_DEVICE);
+                               dma_map_xdr(xprt, &rqstp->rq_res, xdr_off,
+                                           sge_bytes, DMA_TO_DEVICE);
+                       xdr_off += sge_bytes;
                        if (ib_dma_mapping_error(xprt->sc_cm_id->device,
                                                 sge[sge_no].addr))
                                goto err;
@@ -333,6 +367,8 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
                goto err;
        return 0;
  err:
+       svc_rdma_unmap_dma(ctxt);
+       svc_rdma_put_frmr(xprt, vec->frmr);
        svc_rdma_put_context(ctxt, 0);
        /* Fatal error, close transport */
        return -EIO;
@@ -494,7 +530,8 @@ static int send_reply_chunks(struct svcxprt_rdma *xprt,
  * In all three cases, this function prepares the RPCRDMA header in
  * sge[0], the 'type' parameter indicates the type to place in the
  * RPCRDMA header, and the 'byte_count' field indicates how much of
- * the XDR to include in this RDMA_SEND.
+ * the XDR to include in this RDMA_SEND. NB: The offset of the payload
+ * to send is zero in the XDR.
  */
 static int send_reply(struct svcxprt_rdma *rdma,
                      struct svc_rqst *rqstp,
@@ -536,23 +573,24 @@ static int send_reply(struct svcxprt_rdma *rdma,
        ctxt->sge[0].lkey = rdma->sc_dma_lkey;
        ctxt->sge[0].length = svc_rdma_xdr_get_reply_hdr_len(rdma_resp);
        ctxt->sge[0].addr =
-               ib_dma_map_single(rdma->sc_cm_id->device, page_address(page),
-                                 ctxt->sge[0].length, DMA_TO_DEVICE);
+           ib_dma_map_page(rdma->sc_cm_id->device, page, 0,
+                           ctxt->sge[0].length, DMA_TO_DEVICE);
        if (ib_dma_mapping_error(rdma->sc_cm_id->device, ctxt->sge[0].addr))
                goto err;
        atomic_inc(&rdma->sc_dma_used);
 
        ctxt->direction = DMA_TO_DEVICE;
 
-       /* Determine how many of our SGE are to be transmitted */
+       /* Map the payload indicated by 'byte_count' */
        for (sge_no = 1; byte_count && sge_no < vec->count; sge_no++) {
+               int xdr_off = 0;
                sge_bytes = min_t(size_t, vec->sge[sge_no].iov_len, byte_count);
                byte_count -= sge_bytes;
                if (!vec->frmr) {
                        ctxt->sge[sge_no].addr =
-                               ib_dma_map_single(rdma->sc_cm_id->device,
-                                                 vec->sge[sge_no].iov_base,
-                                                 sge_bytes, DMA_TO_DEVICE);
+                               dma_map_xdr(rdma, &rqstp->rq_res, xdr_off,
+                                           sge_bytes, DMA_TO_DEVICE);
+                       xdr_off += sge_bytes;
                        if (ib_dma_mapping_error(rdma->sc_cm_id->device,
                                                 ctxt->sge[sge_no].addr))
                                goto err;
index edea15a54e51d90d03b0149e6602f38017743758..9df1eadc912a837c8863bd4a69733a23dbbab134 100644 (file)
@@ -45,6 +45,7 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
+#include <linux/workqueue.h>
 #include <rdma/ib_verbs.h>
 #include <rdma/rdma_cm.h>
 #include <linux/sunrpc/svc_rdma.h>
@@ -52,6 +53,7 @@
 #define RPCDBG_FACILITY        RPCDBG_SVCXPRT
 
 static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
+                                       struct net *net,
                                        struct sockaddr *sa, int salen,
                                        int flags);
 static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt);
@@ -89,6 +91,9 @@ struct svc_xprt_class svc_rdma_class = {
 /* WR context cache. Created in svc_rdma.c  */
 extern struct kmem_cache *svc_rdma_ctxt_cachep;
 
+/* Workqueue created in svc_rdma.c */
+extern struct workqueue_struct *svc_rdma_wq;
+
 struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *xprt)
 {
        struct svc_rdma_op_ctxt *ctxt;
@@ -120,7 +125,7 @@ void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt)
                 */
                if (ctxt->sge[i].lkey == xprt->sc_dma_lkey) {
                        atomic_dec(&xprt->sc_dma_used);
-                       ib_dma_unmap_single(xprt->sc_cm_id->device,
+                       ib_dma_unmap_page(xprt->sc_cm_id->device,
                                            ctxt->sge[i].addr,
                                            ctxt->sge[i].length,
                                            ctxt->direction);
@@ -502,8 +507,8 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt)
                BUG_ON(sge_no >= xprt->sc_max_sge);
                page = svc_rdma_get_page();
                ctxt->pages[sge_no] = page;
-               pa = ib_dma_map_single(xprt->sc_cm_id->device,
-                                    page_address(page), PAGE_SIZE,
+               pa = ib_dma_map_page(xprt->sc_cm_id->device,
+                                    page, 0, PAGE_SIZE,
                                     DMA_FROM_DEVICE);
                if (ib_dma_mapping_error(xprt->sc_cm_id->device, pa))
                        goto err_put_ctxt;
@@ -511,9 +516,9 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt)
                ctxt->sge[sge_no].addr = pa;
                ctxt->sge[sge_no].length = PAGE_SIZE;
                ctxt->sge[sge_no].lkey = xprt->sc_dma_lkey;
+               ctxt->count = sge_no + 1;
                buflen += PAGE_SIZE;
        }
-       ctxt->count = sge_no;
        recv_wr.next = NULL;
        recv_wr.sg_list = &ctxt->sge[0];
        recv_wr.num_sge = ctxt->count;
@@ -529,6 +534,7 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt)
        return ret;
 
  err_put_ctxt:
+       svc_rdma_unmap_dma(ctxt);
        svc_rdma_put_context(ctxt, 1);
        return -ENOMEM;
 }
@@ -670,6 +676,7 @@ static int rdma_cma_handler(struct rdma_cm_id *cma_id,
  * Create a listening RDMA service endpoint.
  */
 static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
+                                       struct net *net,
                                        struct sockaddr *sa, int salen,
                                        int flags)
 {
@@ -798,8 +805,8 @@ static void frmr_unmap_dma(struct svcxprt_rdma *xprt,
                if (ib_dma_mapping_error(frmr->mr->device, addr))
                        continue;
                atomic_dec(&xprt->sc_dma_used);
-               ib_dma_unmap_single(frmr->mr->device, addr, PAGE_SIZE,
-                                   frmr->direction);
+               ib_dma_unmap_page(frmr->mr->device, addr, PAGE_SIZE,
+                                 frmr->direction);
        }
 }
 
@@ -1184,7 +1191,7 @@ static void svc_rdma_free(struct svc_xprt *xprt)
        struct svcxprt_rdma *rdma =
                container_of(xprt, struct svcxprt_rdma, sc_xprt);
        INIT_WORK(&rdma->sc_work, __svc_rdma_free);
-       schedule_work(&rdma->sc_work);
+       queue_work(svc_rdma_wq, &rdma->sc_work);
 }
 
 static int svc_rdma_has_wspace(struct svc_xprt *xprt)
@@ -1274,7 +1281,7 @@ int svc_rdma_send(struct svcxprt_rdma *xprt, struct ib_send_wr *wr)
                                   atomic_read(&xprt->sc_sq_count) <
                                   xprt->sc_sq_depth);
                        if (test_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags))
-                               return 0;
+                               return -ENOTCONN;
                        continue;
                }
                /* Take a transport ref for each WR posted */
@@ -1306,7 +1313,6 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp,
                         enum rpcrdma_errcode err)
 {
        struct ib_send_wr err_wr;
-       struct ib_sge sge;
        struct page *p;
        struct svc_rdma_op_ctxt *ctxt;
        u32 *va;
@@ -1319,26 +1325,27 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp,
        /* XDR encode error */
        length = svc_rdma_xdr_encode_error(xprt, rmsgp, err, va);
 
+       ctxt = svc_rdma_get_context(xprt);
+       ctxt->direction = DMA_FROM_DEVICE;
+       ctxt->count = 1;
+       ctxt->pages[0] = p;
+
        /* Prepare SGE for local address */
-       sge.addr = ib_dma_map_single(xprt->sc_cm_id->device,
-                                  page_address(p), PAGE_SIZE, DMA_FROM_DEVICE);
-       if (ib_dma_mapping_error(xprt->sc_cm_id->device, sge.addr)) {
+       ctxt->sge[0].addr = ib_dma_map_page(xprt->sc_cm_id->device,
+                                           p, 0, length, DMA_FROM_DEVICE);
+       if (ib_dma_mapping_error(xprt->sc_cm_id->device, ctxt->sge[0].addr)) {
                put_page(p);
                return;
        }
        atomic_inc(&xprt->sc_dma_used);
-       sge.lkey = xprt->sc_dma_lkey;
-       sge.length = length;
-
-       ctxt = svc_rdma_get_context(xprt);
-       ctxt->count = 1;
-       ctxt->pages[0] = p;
+       ctxt->sge[0].lkey = xprt->sc_dma_lkey;
+       ctxt->sge[0].length = length;
 
        /* Prepare SEND WR */
        memset(&err_wr, 0, sizeof err_wr);
        ctxt->wr_op = IB_WR_SEND;
        err_wr.wr_id = (unsigned long)ctxt;
-       err_wr.sg_list = &sge;
+       err_wr.sg_list = ctxt->sge;
        err_wr.num_sge = 1;
        err_wr.opcode = IB_WR_SEND;
        err_wr.send_flags = IB_SEND_SIGNALED;
@@ -1348,9 +1355,7 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp,
        if (ret) {
                dprintk("svcrdma: Error %d posting send for protocol error\n",
                        ret);
-               ib_dma_unmap_single(xprt->sc_cm_id->device,
-                                 sge.addr, PAGE_SIZE,
-                                 DMA_FROM_DEVICE);
+               svc_rdma_unmap_dma(ctxt);
                svc_rdma_put_context(ctxt, 1);
        }
 }
index a85e866a77f73d2abde623b3d89a264331f63999..0867070bb5ca2a9fc1e9749c6c06412cdc9c40ff 100644 (file)
@@ -237,8 +237,7 @@ xprt_rdma_destroy(struct rpc_xprt *xprt)
 
        dprintk("RPC:       %s: called\n", __func__);
 
-       cancel_delayed_work(&r_xprt->rdma_connect);
-       flush_scheduled_work();
+       cancel_delayed_work_sync(&r_xprt->rdma_connect);
 
        xprt_clear_connected(xprt);
 
@@ -251,9 +250,7 @@ xprt_rdma_destroy(struct rpc_xprt *xprt)
 
        xprt_rdma_free_addresses(xprt);
 
-       kfree(xprt->slot);
-       xprt->slot = NULL;
-       kfree(xprt);
+       xprt_free(xprt);
 
        dprintk("RPC:       %s: returning\n", __func__);
 
@@ -285,23 +282,14 @@ xprt_setup_rdma(struct xprt_create *args)
                return ERR_PTR(-EBADF);
        }
 
-       xprt = kzalloc(sizeof(struct rpcrdma_xprt), GFP_KERNEL);
+       xprt = xprt_alloc(args->net, sizeof(struct rpcrdma_xprt),
+                       xprt_rdma_slot_table_entries);
        if (xprt == NULL) {
                dprintk("RPC:       %s: couldn't allocate rpcrdma_xprt\n",
                        __func__);
                return ERR_PTR(-ENOMEM);
        }
 
-       xprt->max_reqs = xprt_rdma_slot_table_entries;
-       xprt->slot = kcalloc(xprt->max_reqs,
-                               sizeof(struct rpc_rqst), GFP_KERNEL);
-       if (xprt->slot == NULL) {
-               dprintk("RPC:       %s: couldn't allocate %d slots\n",
-                       __func__, xprt->max_reqs);
-               kfree(xprt);
-               return ERR_PTR(-ENOMEM);
-       }
-
        /* 60 second timeout, no retries */
        xprt->timeout = &xprt_rdma_default_timeout;
        xprt->bind_timeout = (60U * HZ);
@@ -410,8 +398,7 @@ out3:
 out2:
        rpcrdma_ia_close(&new_xprt->rx_ia);
 out1:
-       kfree(xprt->slot);
-       kfree(xprt);
+       xprt_free(xprt);
        return ERR_PTR(rc);
 }
 
@@ -460,7 +447,7 @@ xprt_rdma_connect(struct rpc_task *task)
        } else {
                schedule_delayed_work(&r_xprt->rdma_connect, 0);
                if (!RPC_IS_ASYNC(task))
-                       flush_scheduled_work();
+                       flush_delayed_work(&r_xprt->rdma_connect);
        }
 }
 
index fe9306bf10cc7f3bba4590ddf853c2ac39eadae9..dfcab5ac65af329149c637b3365751ce66999540 100644 (file)
@@ -774,8 +774,7 @@ static void xs_destroy(struct rpc_xprt *xprt)
 
        xs_close(xprt);
        xs_free_peer_addresses(xprt);
-       kfree(xprt->slot);
-       kfree(xprt);
+       xprt_free(xprt);
        module_put(THIS_MODULE);
 }
 
@@ -1516,7 +1515,7 @@ static void xs_set_port(struct rpc_xprt *xprt, unsigned short port)
        xs_update_peer_port(xprt);
 }
 
-static unsigned short xs_get_srcport(struct sock_xprt *transport, struct socket *sock)
+static unsigned short xs_get_srcport(struct sock_xprt *transport)
 {
        unsigned short port = transport->srcport;
 
@@ -1525,7 +1524,7 @@ static unsigned short xs_get_srcport(struct sock_xprt *transport, struct socket
        return port;
 }
 
-static unsigned short xs_next_srcport(struct sock_xprt *transport, struct socket *sock, unsigned short port)
+static unsigned short xs_next_srcport(struct sock_xprt *transport, unsigned short port)
 {
        if (transport->srcport != 0)
                transport->srcport = 0;
@@ -1535,23 +1534,18 @@ static unsigned short xs_next_srcport(struct sock_xprt *transport, struct socket
                return xprt_max_resvport;
        return --port;
 }
-
-static int xs_bind4(struct sock_xprt *transport, struct socket *sock)
+static int xs_bind(struct sock_xprt *transport, struct socket *sock)
 {
-       struct sockaddr_in myaddr = {
-               .sin_family = AF_INET,
-       };
-       struct sockaddr_in *sa;
+       struct sockaddr_storage myaddr;
        int err, nloop = 0;
-       unsigned short port = xs_get_srcport(transport, sock);
+       unsigned short port = xs_get_srcport(transport);
        unsigned short last;
 
-       sa = (struct sockaddr_in *)&transport->srcaddr;
-       myaddr.sin_addr = sa->sin_addr;
+       memcpy(&myaddr, &transport->srcaddr, transport->xprt.addrlen);
        do {
-               myaddr.sin_port = htons(port);
-               err = kernel_bind(sock, (struct sockaddr *) &myaddr,
-                                               sizeof(myaddr));
+               rpc_set_port((struct sockaddr *)&myaddr, port);
+               err = kernel_bind(sock, (struct sockaddr *)&myaddr,
+                               transport->xprt.addrlen);
                if (port == 0)
                        break;
                if (err == 0) {
@@ -1559,48 +1553,23 @@ static int xs_bind4(struct sock_xprt *transport, struct socket *sock)
                        break;
                }
                last = port;
-               port = xs_next_srcport(transport, sock, port);
+               port = xs_next_srcport(transport, port);
                if (port > last)
                        nloop++;
        } while (err == -EADDRINUSE && nloop != 2);
-       dprintk("RPC:       %s %pI4:%u: %s (%d)\n",
-                       __func__, &myaddr.sin_addr,
-                       port, err ? "failed" : "ok", err);
-       return err;
-}
-
-static int xs_bind6(struct sock_xprt *transport, struct socket *sock)
-{
-       struct sockaddr_in6 myaddr = {
-               .sin6_family = AF_INET6,
-       };
-       struct sockaddr_in6 *sa;
-       int err, nloop = 0;
-       unsigned short port = xs_get_srcport(transport, sock);
-       unsigned short last;
 
-       sa = (struct sockaddr_in6 *)&transport->srcaddr;
-       myaddr.sin6_addr = sa->sin6_addr;
-       do {
-               myaddr.sin6_port = htons(port);
-               err = kernel_bind(sock, (struct sockaddr *) &myaddr,
-                                               sizeof(myaddr));
-               if (port == 0)
-                       break;
-               if (err == 0) {
-                       transport->srcport = port;
-                       break;
-               }
-               last = port;
-               port = xs_next_srcport(transport, sock, port);
-               if (port > last)
-                       nloop++;
-       } while (err == -EADDRINUSE && nloop != 2);
-       dprintk("RPC:       xs_bind6 %pI6:%u: %s (%d)\n",
-               &myaddr.sin6_addr, port, err ? "failed" : "ok", err);
+       if (myaddr.ss_family == AF_INET)
+               dprintk("RPC:       %s %pI4:%u: %s (%d)\n", __func__,
+                               &((struct sockaddr_in *)&myaddr)->sin_addr,
+                               port, err ? "failed" : "ok", err);
+       else
+               dprintk("RPC:       %s %pI6:%u: %s (%d)\n", __func__,
+                               &((struct sockaddr_in6 *)&myaddr)->sin6_addr,
+                               port, err ? "failed" : "ok", err);
        return err;
 }
 
+
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 static struct lock_class_key xs_key[2];
 static struct lock_class_key xs_slock_key[2];
@@ -1622,6 +1591,18 @@ static inline void xs_reclassify_socket6(struct socket *sock)
        sock_lock_init_class_and_name(sk, "slock-AF_INET6-RPC",
                &xs_slock_key[1], "sk_lock-AF_INET6-RPC", &xs_key[1]);
 }
+
+static inline void xs_reclassify_socket(int family, struct socket *sock)
+{
+       switch (family) {
+       case AF_INET:
+               xs_reclassify_socket4(sock);
+               break;
+       case AF_INET6:
+               xs_reclassify_socket6(sock);
+               break;
+       }
+}
 #else
 static inline void xs_reclassify_socket4(struct socket *sock)
 {
@@ -1630,8 +1611,36 @@ static inline void xs_reclassify_socket4(struct socket *sock)
 static inline void xs_reclassify_socket6(struct socket *sock)
 {
 }
+
+static inline void xs_reclassify_socket(int family, struct socket *sock)
+{
+}
 #endif
 
+static struct socket *xs_create_sock(struct rpc_xprt *xprt,
+               struct sock_xprt *transport, int family, int type, int protocol)
+{
+       struct socket *sock;
+       int err;
+
+       err = __sock_create(xprt->xprt_net, family, type, protocol, &sock, 1);
+       if (err < 0) {
+               dprintk("RPC:       can't create %d transport socket (%d).\n",
+                               protocol, -err);
+               goto out;
+       }
+       xs_reclassify_socket(family, sock);
+
+       if (xs_bind(transport, sock)) {
+               sock_release(sock);
+               goto out;
+       }
+
+       return sock;
+out:
+       return ERR_PTR(err);
+}
+
 static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
 {
        struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
@@ -1661,82 +1670,23 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
        xs_udp_do_set_buffer_size(xprt);
 }
 
-/**
- * xs_udp_connect_worker4 - set up a UDP socket
- * @work: RPC transport to connect
- *
- * Invoked by a work queue tasklet.
- */
-static void xs_udp_connect_worker4(struct work_struct *work)
+static void xs_udp_setup_socket(struct work_struct *work)
 {
        struct sock_xprt *transport =
                container_of(work, struct sock_xprt, connect_worker.work);
        struct rpc_xprt *xprt = &transport->xprt;
        struct socket *sock = transport->sock;
-       int err, status = -EIO;
+       int status = -EIO;
 
        if (xprt->shutdown)
                goto out;
 
        /* Start by resetting any existing state */
        xs_reset_transport(transport);
-
-       err = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock);
-       if (err < 0) {
-               dprintk("RPC:       can't create UDP transport socket (%d).\n", -err);
+       sock = xs_create_sock(xprt, transport,
+                       xs_addr(xprt)->sa_family, SOCK_DGRAM, IPPROTO_UDP);
+       if (IS_ERR(sock))
                goto out;
-       }
-       xs_reclassify_socket4(sock);
-
-       if (xs_bind4(transport, sock)) {
-               sock_release(sock);
-               goto out;
-       }
-
-       dprintk("RPC:       worker connecting xprt %p via %s to "
-                               "%s (port %s)\n", xprt,
-                       xprt->address_strings[RPC_DISPLAY_PROTO],
-                       xprt->address_strings[RPC_DISPLAY_ADDR],
-                       xprt->address_strings[RPC_DISPLAY_PORT]);
-
-       xs_udp_finish_connecting(xprt, sock);
-       status = 0;
-out:
-       xprt_clear_connecting(xprt);
-       xprt_wake_pending_tasks(xprt, status);
-}
-
-/**
- * xs_udp_connect_worker6 - set up a UDP socket
- * @work: RPC transport to connect
- *
- * Invoked by a work queue tasklet.
- */
-static void xs_udp_connect_worker6(struct work_struct *work)
-{
-       struct sock_xprt *transport =
-               container_of(work, struct sock_xprt, connect_worker.work);
-       struct rpc_xprt *xprt = &transport->xprt;
-       struct socket *sock = transport->sock;
-       int err, status = -EIO;
-
-       if (xprt->shutdown)
-               goto out;
-
-       /* Start by resetting any existing state */
-       xs_reset_transport(transport);
-
-       err = sock_create_kern(PF_INET6, SOCK_DGRAM, IPPROTO_UDP, &sock);
-       if (err < 0) {
-               dprintk("RPC:       can't create UDP transport socket (%d).\n", -err);
-               goto out;
-       }
-       xs_reclassify_socket6(sock);
-
-       if (xs_bind6(transport, sock) < 0) {
-               sock_release(sock);
-               goto out;
-       }
 
        dprintk("RPC:       worker connecting xprt %p via %s to "
                                "%s (port %s)\n", xprt,
@@ -1755,12 +1705,12 @@ out:
  * We need to preserve the port number so the reply cache on the server can
  * find our cached RPC replies when we get around to reconnecting.
  */
-static void xs_abort_connection(struct rpc_xprt *xprt, struct sock_xprt *transport)
+static void xs_abort_connection(struct sock_xprt *transport)
 {
        int result;
        struct sockaddr any;
 
-       dprintk("RPC:       disconnecting xprt %p to reuse port\n", xprt);
+       dprintk("RPC:       disconnecting xprt %p to reuse port\n", transport);
 
        /*
         * Disconnect the transport socket by doing a connect operation
@@ -1770,13 +1720,13 @@ static void xs_abort_connection(struct rpc_xprt *xprt, struct sock_xprt *transpo
        any.sa_family = AF_UNSPEC;
        result = kernel_connect(transport->sock, &any, sizeof(any), 0);
        if (!result)
-               xs_sock_mark_closed(xprt);
+               xs_sock_mark_closed(&transport->xprt);
        else
                dprintk("RPC:       AF_UNSPEC connect return code %d\n",
                                result);
 }
 
-static void xs_tcp_reuse_connection(struct rpc_xprt *xprt, struct sock_xprt *transport)
+static void xs_tcp_reuse_connection(struct sock_xprt *transport)
 {
        unsigned int state = transport->inet->sk_state;
 
@@ -1799,7 +1749,7 @@ static void xs_tcp_reuse_connection(struct rpc_xprt *xprt, struct sock_xprt *tra
                                "sk_shutdown set to %d\n",
                                __func__, transport->inet->sk_shutdown);
        }
-       xs_abort_connection(xprt, transport);
+       xs_abort_connection(transport);
 }
 
 static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
@@ -1852,12 +1802,12 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
  *
  * Invoked by a work queue tasklet.
  */
-static void xs_tcp_setup_socket(struct rpc_xprt *xprt,
-               struct sock_xprt *transport,
-               struct socket *(*create_sock)(struct rpc_xprt *,
-                       struct sock_xprt *))
+static void xs_tcp_setup_socket(struct work_struct *work)
 {
+       struct sock_xprt *transport =
+               container_of(work, struct sock_xprt, connect_worker.work);
        struct socket *sock = transport->sock;
+       struct rpc_xprt *xprt = &transport->xprt;
        int status = -EIO;
 
        if (xprt->shutdown)
@@ -1865,7 +1815,8 @@ static void xs_tcp_setup_socket(struct rpc_xprt *xprt,
 
        if (!sock) {
                clear_bit(XPRT_CONNECTION_ABORT, &xprt->state);
-               sock = create_sock(xprt, transport);
+               sock = xs_create_sock(xprt, transport,
+                               xs_addr(xprt)->sa_family, SOCK_STREAM, IPPROTO_TCP);
                if (IS_ERR(sock)) {
                        status = PTR_ERR(sock);
                        goto out;
@@ -1876,7 +1827,7 @@ static void xs_tcp_setup_socket(struct rpc_xprt *xprt,
                abort_and_exit = test_and_clear_bit(XPRT_CONNECTION_ABORT,
                                &xprt->state);
                /* "close" the socket, preserving the local port */
-               xs_tcp_reuse_connection(xprt, transport);
+               xs_tcp_reuse_connection(transport);
 
                if (abort_and_exit)
                        goto out_eagain;
@@ -1925,84 +1876,6 @@ out:
        xprt_wake_pending_tasks(xprt, status);
 }
 
-static struct socket *xs_create_tcp_sock4(struct rpc_xprt *xprt,
-               struct sock_xprt *transport)
-{
-       struct socket *sock;
-       int err;
-
-       /* start from scratch */
-       err = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
-       if (err < 0) {
-               dprintk("RPC:       can't create TCP transport socket (%d).\n",
-                               -err);
-               goto out_err;
-       }
-       xs_reclassify_socket4(sock);
-
-       if (xs_bind4(transport, sock) < 0) {
-               sock_release(sock);
-               goto out_err;
-       }
-       return sock;
-out_err:
-       return ERR_PTR(-EIO);
-}
-
-/**
- * xs_tcp_connect_worker4 - connect a TCP socket to a remote endpoint
- * @work: RPC transport to connect
- *
- * Invoked by a work queue tasklet.
- */
-static void xs_tcp_connect_worker4(struct work_struct *work)
-{
-       struct sock_xprt *transport =
-               container_of(work, struct sock_xprt, connect_worker.work);
-       struct rpc_xprt *xprt = &transport->xprt;
-
-       xs_tcp_setup_socket(xprt, transport, xs_create_tcp_sock4);
-}
-
-static struct socket *xs_create_tcp_sock6(struct rpc_xprt *xprt,
-               struct sock_xprt *transport)
-{
-       struct socket *sock;
-       int err;
-
-       /* start from scratch */
-       err = sock_create_kern(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &sock);
-       if (err < 0) {
-               dprintk("RPC:       can't create TCP transport socket (%d).\n",
-                               -err);
-               goto out_err;
-       }
-       xs_reclassify_socket6(sock);
-
-       if (xs_bind6(transport, sock) < 0) {
-               sock_release(sock);
-               goto out_err;
-       }
-       return sock;
-out_err:
-       return ERR_PTR(-EIO);
-}
-
-/**
- * xs_tcp_connect_worker6 - connect a TCP socket to a remote endpoint
- * @work: RPC transport to connect
- *
- * Invoked by a work queue tasklet.
- */
-static void xs_tcp_connect_worker6(struct work_struct *work)
-{
-       struct sock_xprt *transport =
-               container_of(work, struct sock_xprt, connect_worker.work);
-       struct rpc_xprt *xprt = &transport->xprt;
-
-       xs_tcp_setup_socket(xprt, transport, xs_create_tcp_sock6);
-}
-
 /**
  * xs_connect - connect a socket to a remote endpoint
  * @task: address of RPC task that manages state of connect request
@@ -2262,6 +2135,31 @@ static struct rpc_xprt_ops bc_tcp_ops = {
        .print_stats            = xs_tcp_print_stats,
 };
 
+static int xs_init_anyaddr(const int family, struct sockaddr *sap)
+{
+       static const struct sockaddr_in sin = {
+               .sin_family             = AF_INET,
+               .sin_addr.s_addr        = htonl(INADDR_ANY),
+       };
+       static const struct sockaddr_in6 sin6 = {
+               .sin6_family            = AF_INET6,
+               .sin6_addr              = IN6ADDR_ANY_INIT,
+       };
+
+       switch (family) {
+       case AF_INET:
+               memcpy(sap, &sin, sizeof(sin));
+               break;
+       case AF_INET6:
+               memcpy(sap, &sin6, sizeof(sin6));
+               break;
+       default:
+               dprintk("RPC:       %s: Bad address family\n", __func__);
+               return -EAFNOSUPPORT;
+       }
+       return 0;
+}
+
 static struct rpc_xprt *xs_setup_xprt(struct xprt_create *args,
                                      unsigned int slot_table_size)
 {
@@ -2273,27 +2171,25 @@ static struct rpc_xprt *xs_setup_xprt(struct xprt_create *args,
                return ERR_PTR(-EBADF);
        }
 
-       new = kzalloc(sizeof(*new), GFP_KERNEL);
-       if (new == NULL) {
+       xprt = xprt_alloc(args->net, sizeof(*new), slot_table_size);
+       if (xprt == NULL) {
                dprintk("RPC:       xs_setup_xprt: couldn't allocate "
                                "rpc_xprt\n");
                return ERR_PTR(-ENOMEM);
        }
-       xprt = &new->xprt;
-
-       xprt->max_reqs = slot_table_size;
-       xprt->slot = kcalloc(xprt->max_reqs, sizeof(struct rpc_rqst), GFP_KERNEL);
-       if (xprt->slot == NULL) {
-               kfree(xprt);
-               dprintk("RPC:       xs_setup_xprt: couldn't allocate slot "
-                               "table\n");
-               return ERR_PTR(-ENOMEM);
-       }
 
+       new = container_of(xprt, struct sock_xprt, xprt);
        memcpy(&xprt->addr, args->dstaddr, args->addrlen);
        xprt->addrlen = args->addrlen;
        if (args->srcaddr)
                memcpy(&new->srcaddr, args->srcaddr, args->addrlen);
+       else {
+               int err;
+               err = xs_init_anyaddr(args->dstaddr->sa_family,
+                                       (struct sockaddr *)&new->srcaddr);
+               if (err != 0)
+                       return ERR_PTR(err);
+       }
 
        return xprt;
 }
@@ -2341,7 +2237,7 @@ static struct rpc_xprt *xs_setup_udp(struct xprt_create *args)
                        xprt_set_bound(xprt);
 
                INIT_DELAYED_WORK(&transport->connect_worker,
-                                       xs_udp_connect_worker4);
+                                       xs_udp_setup_socket);
                xs_format_peer_addresses(xprt, "udp", RPCBIND_NETID_UDP);
                break;
        case AF_INET6:
@@ -2349,7 +2245,7 @@ static struct rpc_xprt *xs_setup_udp(struct xprt_create *args)
                        xprt_set_bound(xprt);
 
                INIT_DELAYED_WORK(&transport->connect_worker,
-                                       xs_udp_connect_worker6);
+                                       xs_udp_setup_socket);
                xs_format_peer_addresses(xprt, "udp", RPCBIND_NETID_UDP6);
                break;
        default:
@@ -2371,8 +2267,7 @@ static struct rpc_xprt *xs_setup_udp(struct xprt_create *args)
                return xprt;
        ret = ERR_PTR(-EINVAL);
 out_err:
-       kfree(xprt->slot);
-       kfree(xprt);
+       xprt_free(xprt);
        return ret;
 }
 
@@ -2416,7 +2311,7 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
                        xprt_set_bound(xprt);
 
                INIT_DELAYED_WORK(&transport->connect_worker,
-                                       xs_tcp_connect_worker4);
+                                       xs_tcp_setup_socket);
                xs_format_peer_addresses(xprt, "tcp", RPCBIND_NETID_TCP);
                break;
        case AF_INET6:
@@ -2424,7 +2319,7 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
                        xprt_set_bound(xprt);
 
                INIT_DELAYED_WORK(&transport->connect_worker,
-                                       xs_tcp_connect_worker6);
+                                       xs_tcp_setup_socket);
                xs_format_peer_addresses(xprt, "tcp", RPCBIND_NETID_TCP6);
                break;
        default:
@@ -2447,8 +2342,7 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
                return xprt;
        ret = ERR_PTR(-EINVAL);
 out_err:
-       kfree(xprt->slot);
-       kfree(xprt);
+       xprt_free(xprt);
        return ret;
 }
 
@@ -2507,15 +2401,10 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)
                goto out_err;
        }
 
-       if (xprt_bound(xprt))
-               dprintk("RPC:       set up xprt to %s (port %s) via %s\n",
-                               xprt->address_strings[RPC_DISPLAY_ADDR],
-                               xprt->address_strings[RPC_DISPLAY_PORT],
-                               xprt->address_strings[RPC_DISPLAY_PROTO]);
-       else
-               dprintk("RPC:       set up xprt to %s (autobind) via %s\n",
-                               xprt->address_strings[RPC_DISPLAY_ADDR],
-                               xprt->address_strings[RPC_DISPLAY_PROTO]);
+       dprintk("RPC:       set up xprt to %s (port %s) via %s\n",
+                       xprt->address_strings[RPC_DISPLAY_ADDR],
+                       xprt->address_strings[RPC_DISPLAY_PORT],
+                       xprt->address_strings[RPC_DISPLAY_PROTO]);
 
        /*
         * Since we don't want connections for the backchannel, we set
@@ -2528,8 +2417,7 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)
                return xprt;
        ret = ERR_PTR(-EINVAL);
 out_err:
-       kfree(xprt->slot);
-       kfree(xprt);
+       xprt_free(xprt);
        return ret;
 }