From 1132b26029918aa8fb5ba24a81b5c234e61f356c Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Fri, 13 Aug 2010 17:31:16 -0400 Subject: nfsd: remove duplicate NFS4_STATEID_SIZE declaration Use NFS4_STATEID_SIZE from include/linux/nfs4 Signed-off-by: Andy Adamson Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 988cbb3..014482c 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -41,7 +41,6 @@ #define NFSPROC4_CB_NULL 0 #define NFSPROC4_CB_COMPOUND 1 -#define NFS4_STATEID_SIZE 16 /* Index of predefined Linux callback client operations */ -- cgit v1.1 From 17cebf658e088935d4bdebfc7ad9800e9fc4a0b2 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 12 Aug 2010 16:55:22 +1000 Subject: sunrpc: extract some common sunrpc_cache code from nfsd Rather can duplicating this idiom twice, put it in an inline function. This reduces the usage of 'expiry_time' out side the sunrpc/cache.c code and thus the impact of a change that is about to be made to that field. Signed-off-by: NeilBrown Signed-off-by: J. Bruce Fields --- fs/nfsd/export.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index c2a4f71..e56827b 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -935,10 +935,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 +972,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 +1095,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); } -- cgit v1.1 From c5b29f885afe890f953f7f23424045cdad31d3e4 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 12 Aug 2010 16:55:22 +1000 Subject: sunrpc: use seconds since boot in expiry cache This protects us from confusion when the wallclock time changes. We convert to and from wallclock when setting or reading expiry times. Also use seconds since boot for last_clost time. Signed-off-by: NeilBrown Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4idmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index c78dbf4..808b33a4 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -550,7 +550,7 @@ do_idmap_lookup_nowait(struct ent *(*lookup_fn)(struct ent *), 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() || detail->flush_time > (*item)->h.last_refresh) goto out_put; ret = -ENOENT; -- cgit v1.1 From 8ff30fa4eff2ff9e207961c654caa093f0c84873 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 12 Aug 2010 17:04:07 +1000 Subject: nfsd: disable deferral for NFSv4 Now that a slight delay in getting a reply to an upcall doesn't require deferring of requests, request deferral for all NFSv4 requests - the concept doesn't really fit with the v4 model. Signed-off-by: NeilBrown Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 59ec449..0cdfd02 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -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. -- cgit v1.1 From 839049a8732d689d02051e0198fb60a22f7ccb4b Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 12 Aug 2010 17:04:06 +1000 Subject: nfsd/idmap: drop special request deferal in favour of improved default. The idmap code manages request deferal by waiting for a reply from userspace rather than putting the NFS request on a queue to be retried from the start. Now that the common deferal code does this there is no need for the special code in idmap. Signed-off-by: NeilBrown Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4idmap.c | 105 ++++++---------------------------------------------- 1 file changed, 11 insertions(+), 94 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 808b33a4..f0695e8 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -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 < seconds_since_boot() - || 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; } -- cgit v1.1 From c67874f942e30039442d925b03793e0a46ddcddd Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 22 Sep 2010 12:55:07 +1000 Subject: nfsd: formally deprecate legacy nfsd syscall interface The syscall interface is has been replaced by a more flexible interface since 2.6.0. It is time to work towards discarding the old interface. So add a entry in feature-removal-schedule.txt and print a warning when the interface is used. Signed-off-by: NeilBrown Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsctl.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index b53b1d0..7f0fc88 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -121,6 +121,16 @@ 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) { + char name[sizeof(current->comm)]; + printk(KERN_INFO + "Warning: \"%s\" uses deprecated NFSD interface: %s." + " This will be removed in 2.6.40\n", + get_task_comm(name, current), + 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 -- cgit v1.1 From 1e1405673e4e40a94ed7620553eb440a21040402 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 22 Sep 2010 12:55:07 +1000 Subject: nfsd: allow deprecated interface to be compiled out. Add CONFIG_NFSD_DEPRECATED, default to y. Only include deprecated interface if this is defined. This allows distros to remove this interface before the official removal, and allows developers to test without it. Signed-off-by: NeilBrown Signed-off-by: J. Bruce Fields --- fs/nfsd/Kconfig | 12 ++++++++++++ fs/nfsd/export.c | 22 +++++++++++++++++++--- fs/nfsd/nfsctl.c | 10 ++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig index 4264377..18b3e89 100644 --- a/fs/nfsd/Kconfig +++ b/fs/nfsd/Kconfig @@ -28,6 +28,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 diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index e56827b..a3c7d0c 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -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) { @@ -1147,6 +1160,7 @@ out_unlock: exp_writeunlock(); return err; } +#endif /* CONFIG_NFSD_DEPRECATED */ /* * Obtain the root fh on behalf of a client. @@ -1529,6 +1543,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 @@ -1618,6 +1633,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. diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 7f0fc88..b278e44 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -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, @@ -196,6 +202,7 @@ static const struct file_operations pool_stats_operations = { * payload - write methods */ +#ifdef CONFIG_NFSD_DEPRECATED /** * write_svc - Start kernel's NFSD server * @@ -491,6 +498,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 @@ -1365,6 +1373,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}, @@ -1372,6 +1381,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}, -- cgit v1.1 From 049ef27b224ecc33958465fef83d5e4e8a056115 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Thu, 23 Sep 2010 18:26:58 +0400 Subject: nfsd: Export get_task_comm for nfsd The git://linux-nfs.org/~bfields/linux.git nfsd-next branch doesn't compile when nfsd is a module with the following error: ERROR: "get_task_comm" [fs/nfsd/nfsd.ko] undefined! Replace the get_task_comm call with direct comm access, which is safe for current. Signed-off-by: Pavel Emelyanov Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsctl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index b278e44..7b2fa1d 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -129,12 +129,10 @@ static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size { static int warned; if (file->f_dentry->d_name.name[0] == '.' && !warned) { - char name[sizeof(current->comm)]; printk(KERN_INFO "Warning: \"%s\" uses deprecated NFSD interface: %s." " This will be removed in 2.6.40\n", - get_task_comm(name, current), - file->f_dentry->d_name.name); + current->comm, file->f_dentry->d_name.name); warned = 1; } if (! file->private_data) { -- cgit v1.1 From 74ec1e1269eba65b5f8e810cf0363ddb7aa64de5 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 24 Sep 2010 17:43:59 -0400 Subject: nfsd: fix /proc/net/rpc/nfsd.export/content display Note with "first" always 0, and "lastflags" initially 0, we always dump a spurious set of 0 flags at the start, among other problems. Fix. And attempt to make the code a little more obvious. Signed-off-by: J. Bruce Fields --- fs/nfsd/export.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index a3c7d0c..067e2e6 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -1470,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, -- cgit v1.1 From 352114f395bd79353faf0bc1506ead94de393f55 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Mon, 27 Sep 2010 13:59:48 +0400 Subject: sunrpc: Add net to pure API calls There are two calls that operate on ip_map_cache and are directly called from the nfsd code. Other places will be handled in a different way. Signed-off-by: Pavel Emelyanov Signed-off-by: J. Bruce Fields --- fs/nfsd/export.c | 2 +- fs/nfsd/nfsctl.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 067e2e6..c0fcb7a 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -1593,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); diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 7b2fa1d..b6e192d 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -416,7 +416,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 { @@ -479,7 +479,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 { -- cgit v1.1 From 2b44f1ba40914777f4b1075254ba97663d4e2574 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Thu, 30 Sep 2010 20:47:46 +0200 Subject: nfsd4: adjust buflen for encoded attrs bitmap based on actual bitmap length The existing code adjusted it based on the worst case scenario for the returned bitmap and the best case scenario for the supported attrs attribute. Signed-off-by: Benny Halevy [bfields@redhat.com: removed likely/unlikely's] Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 1a468bb..f35a94a 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -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); -- cgit v1.1 From fc5d00b04a3a58cac8620403dfe9f43f72578ec1 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Wed, 29 Sep 2010 16:03:50 +0400 Subject: sunrpc: Add net argument to svc_create_xprt Signed-off-by: Pavel Emelyanov Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsctl.c | 4 ++-- fs/nfsd/nfssvc.c | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index b6e192d..b81da24 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -1015,12 +1015,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; diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index e2c4346..2bae1d8 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -16,6 +16,7 @@ #include #include #include +#include #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; -- cgit v1.1 From c653ce3f0aee9bb2b221ebf3579385c06f81efcd Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Wed, 29 Sep 2010 16:04:45 +0400 Subject: sunrpc: Add net to rpc_create_args Signed-off-by: Pavel Emelyanov Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 014482c..1112f45 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -479,6 +479,7 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb) .to_retries = 0, }; struct rpc_create_args args = { + .net = &init_net, .protocol = XPRT_TRANSPORT_TCP, .address = (struct sockaddr *) &cb->cb_addr, .addrsize = cb->cb_addrlen, -- cgit v1.1 From 07263f1efe7d5b96e6713471abfa087f41bb2b7c Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 31 May 2010 19:09:40 -0400 Subject: nfsd4: minor variable renaming (cb -> conn) Now that we have both nfsd4_callback and nfsd4_cb_conn structures, I get confused if variables of both types are always named cb.... Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 16 ++++++++-------- fs/nfsd/nfs4state.c | 28 ++++++++++++++-------------- 2 files changed, 22 insertions(+), 22 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 1112f45..4566b69 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -472,7 +472,7 @@ 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(), @@ -481,11 +481,11 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb) struct rpc_create_args args = { .net = &init_net, .protocol = XPRT_TRANSPORT_TCP, - .address = (struct sockaddr *) &cb->cb_addr, - .addrsize = cb->cb_addrlen, + .address = (struct sockaddr *) &conn->cb_addr, + .addrsize = conn->cb_addrlen, .timeout = &timeparms, .program = &cb_program, - .prognumber = cb->cb_prog, + .prognumber = conn->cb_prog, .version = 0, .authflavor = clp->cl_flavor, .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), @@ -495,8 +495,8 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb) if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5)) return -EINVAL; - if (cb->cb_minorversion) { - args.bc_xprt = cb->cb_xprt; + if (conn->cb_minorversion) { + args.bc_xprt = conn->cb_xprt; args.protocol = XPRT_TRANSPORT_BC_TCP; } /* Create RPC client */ @@ -563,13 +563,13 @@ void do_probe_callback(struct nfs4_client *clp) /* * Set up the callback client and put a NFSPROC4_CB_NULL on the wire... */ -void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *cb) +void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn) { int status; BUG_ON(atomic_read(&clp->cl_cb_set)); - status = setup_callback_client(clp, cb); + status = setup_callback_client(clp, conn); if (status) { warn_no_callback_path(clp, status); return; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index cf0d2ff..d347180 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -207,7 +207,7 @@ 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; + struct nfs4_cb_conn *conn = &stp->st_stateowner->so_client->cl_cb_conn; dprintk("NFSD alloc_init_deleg\n"); /* @@ -234,7 +234,7 @@ 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_ident = conn->cb_ident; dp->dl_stateid.si_boot = boot_time; dp->dl_stateid.si_stateownerid = current_delegid++; dp->dl_stateid.si_fileid = 0; @@ -1098,7 +1098,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 +1111,24 @@ 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_minorversion = 0; + 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); -- cgit v1.1 From 586f36735e1d38c32bbfbb2716461e7178724b15 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 26 May 2010 17:40:53 -0400 Subject: nfsd4: combine nfs4_rpc_args and nfsd4_cb_sequence These two structs don't really need to be distinct as far as I can tell. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 30 +++++++++++++++--------------- fs/nfsd/state.h | 11 +++-------- 2 files changed, 18 insertions(+), 23 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 4566b69..5687fce 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -247,7 +247,7 @@ 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 nfs4_rpc_args *args, struct nfs4_cb_compound_hdr *hdr) { __be32 *p; @@ -258,8 +258,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(args->args_clp->cl_sessionid.data, NFS4_MAX_SESSIONID_LEN); + WRITE32(args->args_clp->cl_cb_seq_nr); WRITE32(0); /* slotid, always 0 */ WRITE32(0); /* highest slotid always 0 */ WRITE32(0); /* cachethis always 0 */ @@ -285,12 +285,12 @@ nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p, struct nfs4_delegation *args = rpc_args->args_op; struct nfs4_cb_compound_hdr hdr = { .ident = args->dl_ident, - .minorversion = rpc_args->args_seq.cbs_minorversion, + .minorversion = rpc_args->args_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, rpc_args, &hdr); encode_cb_recall(&xdr, args, &hdr); encode_cb_nops(&hdr); return 0; @@ -338,7 +338,7 @@ 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 nfs4_rpc_args *res, struct rpc_rqst *rqstp) { struct nfs4_sessionid id; @@ -346,7 +346,7 @@ decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *res, u32 dummy; __be32 *p; - if (res->cbs_minorversion == 0) + if (res->args_minorversion == 0) return 0; status = decode_cb_op_hdr(xdr, OP_CB_SEQUENCE); @@ -362,13 +362,13 @@ 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, + if (memcmp(id.data, res->args_clp->cl_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 != res->args_clp->cl_cb_seq_nr) { dprintk("%s Invalid sequence number\n", __func__); goto out; } @@ -392,7 +392,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 nfs4_rpc_args *args) { struct xdr_stream xdr; struct nfs4_cb_compound_hdr hdr; @@ -402,8 +402,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 (args) { + status = decode_cb_sequence(&xdr, args, rqstp); if (status) goto out; } @@ -603,8 +603,8 @@ static int nfsd41_cb_setup_sequence(struct nfs4_client *clp, * 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; + args->args_clp = clp; + task->tk_msg.rpc_resp = args; out: dprintk("%s status=%d\n", __func__, status); @@ -623,7 +623,7 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) u32 minorversion = clp->cl_cb_conn.cb_minorversion; int status = 0; - args->args_seq.cbs_minorversion = minorversion; + args->args_minorversion = minorversion; if (minorversion) { status = nfsd41_cb_setup_sequence(clp, task); if (status) { diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 322518c..59313f1 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -64,15 +64,10 @@ 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; + void *args_op; + struct nfs4_client *args_clp; + u32 args_minorversion; }; struct nfsd4_callback { -- cgit v1.1 From 1c8556026edac60368ceef446f0febc08014ba78 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 26 May 2010 17:46:00 -0400 Subject: nfsd4: rename nfs4_rpc_args->nfsd4_cb_args With apologies for the gratuitous rename, the new name seems more helpful to me. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 14 +++++++------- fs/nfsd/state.h | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 5687fce..5508e92 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -247,7 +247,7 @@ encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp, } static void -encode_cb_sequence(struct xdr_stream *xdr, struct nfs4_rpc_args *args, +encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_args *args, struct nfs4_cb_compound_hdr *hdr) { __be32 *p; @@ -279,7 +279,7 @@ 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_cb_args *rpc_args) { struct xdr_stream xdr; struct nfs4_delegation *args = rpc_args->args_op; @@ -338,7 +338,7 @@ 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 nfs4_rpc_args *res, +decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_args *res, struct rpc_rqst *rqstp) { struct nfs4_sessionid id; @@ -392,7 +392,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 nfs4_rpc_args *args) + struct nfsd4_cb_args *args) { struct xdr_stream xdr; struct nfs4_cb_compound_hdr hdr; @@ -585,7 +585,7 @@ void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn) static int nfsd41_cb_setup_sequence(struct nfs4_client *clp, struct rpc_task *task) { - struct nfs4_rpc_args *args = task->tk_msg.rpc_argp; + struct nfsd4_cb_args *args = task->tk_msg.rpc_argp; u32 *ptr = (u32 *)clp->cl_sessionid.data; int status = 0; @@ -619,7 +619,7 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) { struct nfs4_delegation *dp = calldata; struct nfs4_client *clp = dp->dl_client; - struct nfs4_rpc_args *args = task->tk_msg.rpc_argp; + struct nfsd4_cb_args *args = task->tk_msg.rpc_argp; u32 minorversion = clp->cl_cb_conn.cb_minorversion; int status = 0; @@ -756,7 +756,7 @@ static void _nfsd4_cb_recall(struct nfs4_delegation *dp) { 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 nfsd4_cb_args *args = &dp->dl_recall.cb_args; struct rpc_message msg = { .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL], .rpc_cred = callback_cred diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 59313f1..f988b90 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -64,14 +64,14 @@ typedef struct { (s)->si_fileid, \ (s)->si_generation -struct nfs4_rpc_args { +struct nfsd4_cb_args { void *args_op; struct nfs4_client *args_clp; u32 args_minorversion; }; struct nfsd4_callback { - struct nfs4_rpc_args cb_args; + struct nfsd4_cb_args cb_args; struct work_struct cb_work; }; -- cgit v1.1 From 5878453dbde627a8e1b5a4693087e36cb88d45b1 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 16 May 2010 16:47:08 -0400 Subject: nfsd4: generic callback code Make the recall callback code more generic, so that other callbacks will be able to use it too. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 70 ++++++++++++++++++++++---------------------------- fs/nfsd/state.h | 2 ++ 2 files changed, 33 insertions(+), 39 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 5508e92..a037f26 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -585,7 +585,6 @@ void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn) static int nfsd41_cb_setup_sequence(struct nfs4_client *clp, struct rpc_task *task) { - struct nfsd4_cb_args *args = task->tk_msg.rpc_argp; u32 *ptr = (u32 *)clp->cl_sessionid.data; int status = 0; @@ -598,14 +597,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_clp = clp; - task->tk_msg.rpc_resp = args; - out: dprintk("%s status=%d\n", __func__, status); return status; @@ -617,7 +608,8 @@ 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 nfsd4_cb_args *args = task->tk_msg.rpc_argp; u32 minorversion = clp->cl_cb_conn.cb_minorversion; @@ -640,7 +632,8 @@ 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__, @@ -662,7 +655,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; @@ -707,7 +701,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); } @@ -749,42 +744,39 @@ void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt *new) 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; + if (cb->cb_ops->rpc_release) + cb->cb_ops->rpc_release(cb); +} + +void nfsd4_do_callback_rpc(struct work_struct *w) +{ + struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work); + struct nfs4_client *clp = cb->cb_args.args_clp; struct rpc_clnt *clnt = clp->cl_cb_client; - struct nfsd4_cb_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 (clnt == NULL) { - nfs4_put_delegation(dp); + nfsd4_release_cb(cb); return; /* Client is shutting down; give up. */ } - - 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); + rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT, cb->cb_ops, cb); } -void nfsd4_do_callback_rpc(struct work_struct *w) +void nfsd4_cb_recall(struct nfs4_delegation *dp) { - /* XXX: for now, just send off delegation recall. */ - /* In future, generalize to handle any sort of callback. */ - struct nfsd4_callback *c = container_of(w, struct nfsd4_callback, cb_work); - struct nfs4_delegation *dp = container_of(c, struct nfs4_delegation, dl_recall); - - _nfsd4_cb_recall(dp); -} + struct nfsd4_callback *cb = &dp->dl_recall; + dp->dl_retries = 1; + cb->cb_args.args_op = dp; + cb->cb_args.args_clp = dp->dl_client; + cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL]; + cb->cb_msg.rpc_argp = &cb->cb_args; + cb->cb_msg.rpc_resp = &cb->cb_args; + cb->cb_msg.rpc_cred = callback_cred; + + cb->cb_ops = &nfsd4_cb_recall_ops; + dp->dl_retries = 1; -void nfsd4_cb_recall(struct nfs4_delegation *dp) -{ queue_work(callback_wq, &dp->dl_recall.cb_work); } diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index f988b90..6e59214 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -72,6 +72,8 @@ struct nfsd4_cb_args { struct nfsd4_callback { struct nfsd4_cb_args cb_args; + struct rpc_message cb_msg; + const struct rpc_call_ops *cb_ops; struct work_struct cb_work; }; -- cgit v1.1 From cee277d92495a9ea49a6137fe7005d7c76b31b5b Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 26 May 2010 17:52:14 -0400 Subject: nfsd4: use generic callback code in null case This will eventually allow us, for example, to kick off null callback from contexts where we can't sleep. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 33 ++++++++++++++++++--------------- fs/nfsd/nfs4state.c | 1 + fs/nfsd/state.h | 1 + 3 files changed, 20 insertions(+), 15 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index a037f26..26fa878 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -519,7 +519,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); @@ -528,6 +528,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, }; @@ -543,21 +545,23 @@ int set_callback_cred(void) return 0; } +static struct workqueue_struct *callback_wq; 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_args.args_op = NULL; + cb->cb_args.args_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); } /* @@ -713,8 +717,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"); @@ -760,7 +762,8 @@ void nfsd4_do_callback_rpc(struct work_struct *w) nfsd4_release_cb(cb); return; /* Client is shutting down; give up. */ } - rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT, cb->cb_ops, cb); + 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) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d347180..2f464fb 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -978,6 +978,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, INIT_LIST_HEAD(&clp->cl_delegations); INIT_LIST_HEAD(&clp->cl_sessions); INIT_LIST_HEAD(&clp->cl_lru); + 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"); diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 6e59214..19732d5 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -223,6 +223,7 @@ struct nfs4_client { struct nfs4_cb_conn cl_cb_conn; struct rpc_clnt *cl_cb_client; atomic_t cl_cb_set; + struct nfsd4_callback cl_cb_null; /* for nfs41 */ struct list_head cl_sessions; -- cgit v1.1 From fb003923263c3f0cb02adbd56a22fe16ef5c0e77 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 31 May 2010 18:21:37 -0400 Subject: nfsd4: remove separate cb_args struct I don't see the point of the separate struct. It seems to just be getting in the way. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 45 ++++++++++++++++++++++----------------------- fs/nfsd/state.h | 10 +++------- 2 files changed, 25 insertions(+), 30 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 26fa878..07c3be6 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -247,7 +247,7 @@ encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp, } static void -encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_args *args, +encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb, struct nfs4_cb_compound_hdr *hdr) { __be32 *p; @@ -258,8 +258,8 @@ encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_args *args, RESERVE_SPACE(1 + NFS4_MAX_SESSIONID_LEN + 20); WRITE32(OP_CB_SEQUENCE); - WRITEMEM(args->args_clp->cl_sessionid.data, NFS4_MAX_SESSIONID_LEN); - WRITE32(args->args_clp->cl_cb_seq_nr); + WRITEMEM(cb->cb_clp->cl_sessionid.data, NFS4_MAX_SESSIONID_LEN); + WRITE32(cb->cb_clp->cl_cb_seq_nr); WRITE32(0); /* slotid, always 0 */ WRITE32(0); /* highest slotid always 0 */ WRITE32(0); /* cachethis always 0 */ @@ -279,18 +279,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 nfsd4_cb_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_minorversion, + .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, &hdr); + encode_cb_sequence(&xdr, cb, &hdr); encode_cb_recall(&xdr, args, &hdr); encode_cb_nops(&hdr); return 0; @@ -338,7 +338,7 @@ 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_args *res, +decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb, struct rpc_rqst *rqstp) { struct nfs4_sessionid id; @@ -346,7 +346,7 @@ decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_args *res, u32 dummy; __be32 *p; - if (res->args_minorversion == 0) + if (cb->cb_minorversion == 0) return 0; status = decode_cb_op_hdr(xdr, OP_CB_SEQUENCE); @@ -362,13 +362,13 @@ decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_args *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->args_clp->cl_sessionid.data, + if (memcmp(id.data, cb->cb_clp->cl_sessionid.data, NFS4_MAX_SESSIONID_LEN)) { dprintk("%s Invalid session id\n", __func__); goto out; } READ32(dummy); - if (dummy != res->args_clp->cl_cb_seq_nr) { + if (dummy != cb->cb_clp->cl_cb_seq_nr) { dprintk("%s Invalid sequence number\n", __func__); goto out; } @@ -392,7 +392,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_args *args) + struct nfsd4_callback *cb) { struct xdr_stream xdr; struct nfs4_cb_compound_hdr hdr; @@ -402,8 +402,8 @@ nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p, status = decode_cb_compound_hdr(&xdr, &hdr); if (status) goto out; - if (args) { - status = decode_cb_sequence(&xdr, args, rqstp); + if (cb) { + status = decode_cb_sequence(&xdr, cb, rqstp); if (status) goto out; } @@ -551,8 +551,8 @@ void do_probe_callback(struct nfs4_client *clp) { struct nfsd4_callback *cb = &clp->cl_cb_null; - cb->cb_args.args_op = NULL; - cb->cb_args.args_clp = clp; + 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; @@ -615,11 +615,10 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *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 nfsd4_cb_args *args = task->tk_msg.rpc_argp; u32 minorversion = clp->cl_cb_conn.cb_minorversion; int status = 0; - args->args_minorversion = minorversion; + cb->cb_minorversion = minorversion; if (minorversion) { status = nfsd41_cb_setup_sequence(clp, task); if (status) { @@ -755,7 +754,7 @@ void nfsd4_release_cb(struct nfsd4_callback *cb) void nfsd4_do_callback_rpc(struct work_struct *w) { struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work); - struct nfs4_client *clp = cb->cb_args.args_clp; + struct nfs4_client *clp = cb->cb_clp; struct rpc_clnt *clnt = clp->cl_cb_client; if (clnt == NULL) { @@ -771,11 +770,11 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp) struct nfsd4_callback *cb = &dp->dl_recall; dp->dl_retries = 1; - cb->cb_args.args_op = dp; - cb->cb_args.args_clp = dp->dl_client; + 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_args; - cb->cb_msg.rpc_resp = &cb->cb_args; + 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; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 19732d5..2ece6be 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -64,14 +64,10 @@ typedef struct { (s)->si_fileid, \ (s)->si_generation -struct nfsd4_cb_args { - void *args_op; - struct nfs4_client *args_clp; - u32 args_minorversion; -}; - struct nfsd4_callback { - struct nfsd4_cb_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; -- cgit v1.1 From 6ff8da088766d70f0441feb982b82978a6cbf7ef Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 4 Jun 2010 20:04:45 -0400 Subject: nfsd4: Move callback setup to callback queue Instead of creating the new rpc client from a regular server thread, set a flag, kick off a null call, and allow the null call to do the work of setting up the client on the callback workqueue. Use a spinlock to ensure the callback work gets a consistent view of the callback parameters. This allows, for example, changing the callback from contexts where sleeping is not allowed. I hope it will also keep the locking simple as we add more session and trunking features, by serializing most of the callback-specific work. This also closes a small race where the the new cb_ident could be used with an old connection (or vice-versa). Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 73 +++++++++++++++++++++++++++++++++++--------------- fs/nfsd/nfs4state.c | 7 +++-- fs/nfsd/state.h | 10 +++++-- 3 files changed, 63 insertions(+), 27 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 07c3be6..a269dbe 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -284,7 +284,7 @@ nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p, struct xdr_stream xdr; struct nfs4_delegation *args = cb->cb_op; struct nfs4_cb_compound_hdr hdr = { - .ident = args->dl_ident, + .ident = cb->cb_clp->cl_cb_ident, .minorversion = cb->cb_minorversion, }; @@ -506,7 +506,8 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn) PTR_ERR(client)); return PTR_ERR(client); } - nfsd4_set_callback_client(clp, client); + clp->cl_cb_ident = conn->cb_ident; + clp->cl_cb_client = client; return 0; } @@ -569,15 +570,12 @@ void do_probe_callback(struct nfs4_client *clp) */ void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn) { - int status; - BUG_ON(atomic_read(&clp->cl_cb_set)); - status = setup_callback_client(clp, conn); - if (status) { - warn_no_callback_path(clp, status); - return; - } + spin_lock(&clp->cl_lock); + memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn)); + set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags); + spin_unlock(&clp->cl_lock); do_probe_callback(clp); } @@ -730,19 +728,16 @@ 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); } void nfsd4_release_cb(struct nfsd4_callback *cb) @@ -751,15 +746,51 @@ void nfsd4_release_cb(struct nfsd4_callback *cb) cb->cb_ops->rpc_release(cb); } +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); + + err = setup_callback_client(clp, &conn); + if (err) + warn_no_callback_path(clp, err); +} + void nfsd4_do_callback_rpc(struct work_struct *w) { struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work); struct nfs4_client *clp = cb->cb_clp; - struct rpc_clnt *clnt = clp->cl_cb_client; + struct rpc_clnt *clnt; - if (clnt == NULL) { + 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; /* Client is shutting down; give up. */ + return; } rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN, cb->cb_ops, cb); diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 2f464fb..d3f12dc 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -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 *conn = &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 = conn->cb_ident; dp->dl_stateid.si_boot = boot_time; dp->dl_stateid.si_stateownerid = current_delegid++; dp->dl_stateid.si_fileid = 0; @@ -875,7 +873,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); @@ -978,6 +976,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, 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); @@ -1547,7 +1546,7 @@ 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_put_session(ses); status = nfs_ok; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 2ece6be..58bc2a6 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -84,7 +84,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; @@ -217,10 +216,17 @@ struct nfs4_client { /* 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; + /* 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 */ @@ -439,7 +445,7 @@ 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); -- cgit v1.1 From c23753dac1d21b39facd2ad3c7340dd275b3022f Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 27 Sep 2010 16:22:30 -0400 Subject: nfsd4: fix alloc_init_session BUILD_BUG_ON() Note we're allocating an array of nfsd4_slot *'s, not nfsd4_slot's. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d3f12dc..e305794 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -657,7 +657,7 @@ alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, if (status) goto out; - BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot) + BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot *) + sizeof(struct nfsd4_session) > PAGE_SIZE); status = nfserr_jukebox; -- cgit v1.1 From dd93842457174b847b023314e5a501e5ed45caeb Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 27 Sep 2010 16:26:25 -0400 Subject: nfsd4: fix alloc_init_session return type This returns an nfs error, not -ERRNO. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index e305794..ebddcc1 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -639,9 +639,7 @@ static inline int slot_bytes(struct nfsd4_channel_attrs *ca) return ca->maxresp_cached - NFSD_MIN_HDR_SEQ_SZ; } -static int -alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, - struct nfsd4_create_session *cses) +static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses) { struct nfsd4_session *new, tmp; struct nfsd4_slot *sp; -- cgit v1.1 From 5b6feee9608dce7afd2646f457c93e612526d1d8 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 27 Sep 2010 17:12:05 -0400 Subject: nfsd4: clean up session allocation Changes: - make sure session memory reservation is released on failure path. - use min_t()/min() for more compact code in several places. - break alloc_init_session into smaller pieces. - miscellaneous other cleanup. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 211 ++++++++++++++++++++++------------------------------ 1 file changed, 89 insertions(+), 122 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index ebddcc1..f86476c 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -533,94 +533,6 @@ gen_sessionid(struct nfsd4_session *ses) */ #define NFSD_MIN_HDR_SEQ_SZ (24 + 12 + 44) -/* - * 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) - * 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) -{ - int mem, size = fchan->maxresp_cached; - - if (fchan->maxreqs < 1) - return nfserr_inval; - - 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); - /* 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; - spin_unlock(&nfsd_drc_lock); - - if (fchan->maxreqs == 0) - return nfserr_jukebox; - - fchan->maxresp_cached = size + NFSD_MIN_HDR_SEQ_SZ; - return 0; -} - -/* - * 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) -{ - int status = 0; - __u32 maxcount = nfsd_serv->sv_max_mesg; - - /* headerpadsz set to zero in encode routine */ - - /* 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; - - if (fchan->maxresp_sz > maxcount) - fchan->maxresp_sz = maxcount; - session_fchan->maxresp_sz = fchan->maxresp_sz; - - /* 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; - - /* 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); - - session_fchan->maxresp_cached = fchan->maxresp_cached; - session_fchan->maxreqs = fchan->maxreqs; - - dprintk("%s status %d\n", __func__, status); - return status; -} - static void free_session_slots(struct nfsd4_session *ses) { @@ -639,63 +551,118 @@ static inline int slot_bytes(struct nfsd4_channel_attrs *ca) return ca->maxresp_cached - NFSD_MIN_HDR_SEQ_SZ; } -static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses) +static int nfsd4_sanitize_slot_size(u32 size) { - struct nfsd4_session *new, tmp; - struct nfsd4_slot *sp; - int idx, slotsize, cachesize, i; - int status; + size -= NFSD_MIN_HDR_SEQ_SZ; /* We don't cache the rpc header */ + size = min_t(u32, size, NFSD_SLOT_CACHE_SIZE); - memset(&tmp, 0, sizeof(tmp)); + return size; +} - /* 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; +/* + * 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 nfsd4_get_drc_mem(int slotsize, u32 num) +{ + int avail; - BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot *) - + sizeof(struct nfsd4_session) > PAGE_SIZE); + num = min_t(u32, num, NFSD_MAX_SLOTS_PER_SESSION); - 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; + 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); + nfsd_drc_mem_used -= slotsize * num; + spin_unlock(&nfsd_drc_lock); +} + +static struct nfsd4_session *alloc_session(int slotsize, int numslots) +{ + struct nfsd4_session *new; + int mem, i; - memcpy(new, &tmp, sizeof(*new)); + 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 */ - cachesize = slot_bytes(&new->se_fchannel); - for (i = 0; i < new->se_fchannel.maxreqs; i++) { - sp = kzalloc(sizeof(*sp) + cachesize, GFP_KERNEL); - if (!sp) + 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; - new->se_slots[i] = sp; } + return new; +out_free: + while (i--) + kfree(new->se_slots[i]); + kfree(new); + return NULL; +} + +static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, struct nfsd4_channel_attrs *req, int numslots, int slotsize) +{ + u32 maxrpc = nfsd_serv->sv_max_mesg; + + 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); +} + +static __be32 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 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 nfserr_jukebox; + } + 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); new->se_flags = cses->flags; 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; + return nfs_ok; } /* caller must hold client_lock */ -- cgit v1.1 From c7662518c781edc8059cd9737d18168154bf7510 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 6 Jun 2010 18:12:14 -0400 Subject: nfsd4: keep per-session list of connections The spec requires us in various places to keep track of the connections associated with each session. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 69 +++++++++++++++++++++++++++++++++++++++++------------ fs/nfsd/state.h | 8 +++++++ 2 files changed, 62 insertions(+), 15 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index f86476c..c7c1a7a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -625,11 +625,58 @@ static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, struct nfsd4 new->maxops = min_t(u32, req->maxops, NFSD_MAX_OPS_PER_COMPOUND); } +static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses) +{ + struct nfs4_client *clp = ses->se_client; + struct nfsd4_conn *conn; + + conn = kmalloc(sizeof(struct nfsd4_conn), GFP_KERNEL); + if (!conn) + return nfserr_jukebox; + conn->cn_flags = NFS4_CDFC4_FORE; + svc_xprt_get(rqstp->rq_xprt); + conn->cn_xprt = rqstp->rq_xprt; + + spin_lock(&clp->cl_lock); + list_add(&conn->cn_persession, &ses->se_conns); + spin_unlock(&clp->cl_lock); + + return nfs_ok; +} + +static void free_conn(struct nfsd4_conn *c) +{ + svc_xprt_put(c->cn_xprt); + kfree(c); +} + +void free_session(struct kref *kref) +{ + struct nfsd4_session *ses; + int mem; + + ses = container_of(kref, struct nfsd4_session, se_ref); + while (!list_empty(&ses->se_conns)) { + struct nfsd4_conn *c; + c = list_first_entry(&ses->se_conns, struct nfsd4_conn, cn_persession); + list_del(&c->cn_persession); + free_conn(c); + } + 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 __be32 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; /* @@ -654,6 +701,8 @@ static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp memcpy(clp->cl_sessionid.data, new->se_sessionid.data, NFS4_MAX_SESSIONID_LEN); + INIT_LIST_HEAD(&new->se_conns); + new->se_flags = cses->flags; kref_init(&new->se_ref); idx = hash_sessionid(&new->se_sessionid); @@ -662,6 +711,11 @@ static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp list_add(&new->se_perclnt, &clp->cl_sessions); spin_unlock(&client_lock); + status = nfsd4_new_conn(rqstp, new); + if (status) { + free_session(&new->se_ref); + return nfserr_jukebox; + } return nfs_ok; } @@ -694,21 +748,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) diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 58bc2a6..29413c2 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -152,6 +152,13 @@ struct nfsd4_clid_slot { struct nfsd4_create_session sl_cr_ses; }; +struct nfsd4_conn { + struct list_head cn_persession; + struct svc_xprt *cn_xprt; +/* CDFC4_FORE, CDFC4_BACK: */ + unsigned char cn_flags; +}; + struct nfsd4_session { struct kref se_ref; struct list_head se_hash; /* hash by sessionid */ @@ -161,6 +168,7 @@ 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; struct nfsd4_slot *se_slots[]; /* forward channel slots */ }; -- cgit v1.1 From 19cf5c026f3ee06027523e59478e3fa54f573e5e Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 6 Jun 2010 18:37:16 -0400 Subject: nfsd4: use callbacks on svc_xprt_deletion Remove connections from the list when they go down. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 51 ++++++++++++++++++++++++++++++++++++++++++--------- fs/nfsd/state.h | 3 +++ 2 files changed, 45 insertions(+), 9 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c7c1a7a..b7e9793 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -625,6 +625,25 @@ static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, struct nfsd4 new->maxops = min_t(u32, req->maxops, NFSD_MAX_OPS_PER_COMPOUND); } +static void free_conn(struct nfsd4_conn *c) +{ + svc_xprt_put(c->cn_xprt); + kfree(c); +} + +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; + + spin_lock(&clp->cl_lock); + if (!list_empty(&c->cn_persession)) { + list_del(&c->cn_persession); + free_conn(c); + } + spin_unlock(&clp->cl_lock); +} + static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses) { struct nfs4_client *clp = ses->se_client; @@ -636,18 +655,34 @@ static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses) conn->cn_flags = NFS4_CDFC4_FORE; svc_xprt_get(rqstp->rq_xprt); conn->cn_xprt = rqstp->rq_xprt; + conn->cn_session = ses; spin_lock(&clp->cl_lock); list_add(&conn->cn_persession, &ses->se_conns); spin_unlock(&clp->cl_lock); + conn->cn_xpt_user.callback = nfsd4_conn_lost; + register_xpt_user(rqstp->rq_xprt, &conn->cn_xpt_user); return nfs_ok; } -static void free_conn(struct nfsd4_conn *c) +static void nfsd4_del_conns(struct nfsd4_session *s) { - svc_xprt_put(c->cn_xprt); - kfree(c); + struct nfs4_client *clp = s->se_client; + struct nfsd4_conn *c; + + 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); + + unregister_xpt_user(c->cn_xprt, &c->cn_xpt_user); + free_conn(c); + + spin_lock(&clp->cl_lock); + } + spin_unlock(&clp->cl_lock); } void free_session(struct kref *kref) @@ -656,12 +691,7 @@ void free_session(struct kref *kref) int mem; ses = container_of(kref, struct nfsd4_session, se_ref); - while (!list_empty(&ses->se_conns)) { - struct nfsd4_conn *c; - c = list_first_entry(&ses->se_conns, struct nfsd4_conn, cn_persession); - list_del(&c->cn_persession); - free_conn(c); - } + nfsd4_del_conns(ses); spin_lock(&nfsd_drc_lock); mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel); nfsd_drc_mem_used -= mem; @@ -1552,6 +1582,9 @@ nfsd4_destroy_session(struct svc_rqst *r, /* wait for callbacks */ nfsd4_shutdown_callback(ses->se_client); nfs4_unlock_state(); + + nfsd4_del_conns(ses); + nfsd4_put_session(ses); status = nfs_ok; out: diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 29413c2..8d5e237 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -35,6 +35,7 @@ #ifndef _NFSD4_STATE_H #define _NFSD4_STATE_H +#include #include #include "nfsfh.h" @@ -155,6 +156,8 @@ struct nfsd4_clid_slot { 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; }; -- cgit v1.1 From db90681d6eff89efc1eee523e1cb77eb632a6cf7 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 29 Sep 2010 15:29:32 -0400 Subject: nfsd4: refactor connection allocation Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index b7e9793..3b4d74c 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -644,25 +644,45 @@ static void nfsd4_conn_lost(struct svc_xpt_user *u) spin_unlock(&clp->cl_lock); } -static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses) +static struct nfsd4_conn *alloc_conn(struct svc_rqst *rqstp) { - struct nfs4_client *clp = ses->se_client; struct nfsd4_conn *conn; conn = kmalloc(sizeof(struct nfsd4_conn), GFP_KERNEL); if (!conn) - return nfserr_jukebox; - conn->cn_flags = NFS4_CDFC4_FORE; + return NULL; svc_xprt_get(rqstp->rq_xprt); conn->cn_xprt = rqstp->rq_xprt; - conn->cn_session = ses; + conn->cn_flags = NFS4_CDFC4_FORE; + INIT_LIST_HEAD(&conn->cn_xpt_user.list); + return conn; +} + +static void nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses) +{ + struct nfs4_client *clp = ses->se_client; spin_lock(&clp->cl_lock); + conn->cn_session = ses; list_add(&conn->cn_persession, &ses->se_conns); spin_unlock(&clp->cl_lock); +} +static void nfsd4_register_conn(struct nfsd4_conn *conn) +{ conn->cn_xpt_user.callback = nfsd4_conn_lost; - register_xpt_user(rqstp->rq_xprt, &conn->cn_xpt_user); + register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user); +} + +static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses) +{ + struct nfsd4_conn *conn; + + conn = alloc_conn(rqstp); + if (!conn) + return nfserr_jukebox; + nfsd4_hash_conn(conn, ses); + nfsd4_register_conn(conn); return nfs_ok; } -- cgit v1.1 From 328ead287220711c3ad4490b1f3f691855df4039 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 29 Sep 2010 16:11:06 -0400 Subject: nfsd4: add new connections to session As long as we're not implementing any session security, we should just automatically add any new connections that come along to the list of sessions associated with the session. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 3b4d74c..596702e 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -658,13 +658,18 @@ static struct nfsd4_conn *alloc_conn(struct svc_rqst *rqstp) return conn; } +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 nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses) { struct nfs4_client *clp = ses->se_client; spin_lock(&clp->cl_lock); - conn->cn_session = ses; - list_add(&conn->cn_persession, &ses->se_conns); + __nfsd4_hash_conn(conn, ses); spin_unlock(&clp->cl_lock); } @@ -1612,6 +1617,44 @@ out: return status; } +static struct nfsd4_conn *__nfsd4_find_conn(struct svc_rqst *r, struct nfsd4_session *s) +{ + struct nfsd4_conn *c; + + list_for_each_entry(c, &s->se_conns, cn_persession) { + if (c->cn_xprt == r->rq_xprt) { + return c; + } + } + return NULL; +} + +static void nfsd4_sequence_check_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses) +{ + struct nfs4_client *clp = ses->se_client; + struct nfsd4_conn *c, *new = NULL; + + spin_lock(&clp->cl_lock); + c = __nfsd4_find_conn(rqstp, ses); + spin_unlock(&clp->cl_lock); + if (c) + return; + + new = alloc_conn(rqstp); + + spin_lock(&clp->cl_lock); + c = __nfsd4_find_conn(rqstp, 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, @@ -1656,6 +1699,8 @@ nfsd4_sequence(struct svc_rqst *rqstp, if (status) goto out; + nfsd4_sequence_check_conn(rqstp, session); + /* Success! bump slot seqid */ slot->sl_inuse = true; slot->sl_seqid = seq->seqid; -- cgit v1.1 From 33515142156efc9ab5dbfe93ff8d4765559dc987 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sat, 2 Oct 2010 18:42:39 -0400 Subject: nfsd4: return expired on unfound stateid's Commit 78155ed75f470710f2aecb3e75e3d97107ba8374 "nfsd4: distinguish expired from stale stateids" attempted to distinguish expired and stale stateid's using time information that may not have been completely reliable, so I reverted it. That was throwing out the baby with the bathwater; we still do want to return expired, but let's do that using the simpler approach of just assuming any stateid is expired if it looks like it was given out by the current server instance, but we can't find it any more. This may help clients that are recovering from network partitions. Reported-by: Bian Naimeng Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 596702e..02c23b7 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3046,7 +3046,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) @@ -3066,6 +3070,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) @@ -3140,8 +3145,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; } @@ -3406,6 +3412,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; -- cgit v1.1 From ecec6e34e18660799444c5a163c7313a20fba701 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 11 Oct 2010 16:49:44 -0400 Subject: nfsd4: expire clients more promptly Expire clients more promptly, at the expense of possibly running the laundromat thread more frequently. Though it's not the default, I'd like it to be feasible to run with a lease time of just a few seconds, at which point a minimum 10 second wait between laundromat runs seems a little much. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index b76ac3a..6b641cf 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -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: -- cgit v1.1 From cd5b814458e5554457c6e62f17aed122145b065e Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sat, 2 Oct 2010 17:03:35 -0400 Subject: nfsd4: don't cache seq_misordered replies Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 02c23b7..7f12828 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1510,7 +1510,7 @@ 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 */ @@ -1549,7 +1549,6 @@ nfsd4_create_session(struct svc_rqst *rqstp, NFS4_MAX_SESSIONID_LEN); 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); out: -- cgit v1.1 From edd76786633a3145661c7a90c9baccae8e3c9e84 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 14 Jun 2010 22:26:31 -0400 Subject: nfsd4: move callback setup into session init code The backchannel should be associated with a session, it isn't really global to the client. We do, however, want a pointer global to the client which tracks which session we're currently using for client-based callbacks. This is a first step in that direction; for now, just reshuffling of code with no significant change in behavior. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 29 ++++++++++++++--------------- fs/nfsd/state.h | 1 + 2 files changed, 15 insertions(+), 15 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 7f12828..db5d8c8 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -771,6 +771,19 @@ static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp free_session(&new->se_ref); return nfserr_jukebox; } + 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); + clp->cl_cb_conn.cb_minorversion = 1; + clp->cl_cb_conn.cb_prog = cses->callback_prog; + clp->cl_cb_seq_nr = 1; + nfsd4_probe_callback(clp, &clp->cl_cb_conn); + } return nfs_ok; } @@ -1045,7 +1058,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; } @@ -1515,20 +1528,6 @@ nfsd4_create_session(struct svc_rqst *rqstp, 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); - } conf = unconf; } else { status = nfserr_stale_clientid; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 8d5e237..6e63c1d 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -234,6 +234,7 @@ struct nfs4_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; -- cgit v1.1 From 90c8145bb6fe1d9e0a808de6a701748967588bbd Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 14 Jun 2010 17:49:37 -0400 Subject: nfsd4: use client pointer to backchannel session Instead of copying the sessionid, use the new cl_cb_session pointer, which indicates which session we're using for the backchannel. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 9 +++++---- fs/nfsd/nfs4state.c | 4 +--- fs/nfsd/state.h | 1 - 3 files changed, 6 insertions(+), 8 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index a269dbe..78ac779 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -251,6 +251,7 @@ 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,7 +259,7 @@ encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb, RESERVE_SPACE(1 + NFS4_MAX_SESSIONID_LEN + 20); WRITE32(OP_CB_SEQUENCE); - WRITEMEM(cb->cb_clp->cl_sessionid.data, NFS4_MAX_SESSIONID_LEN); + WRITEMEM(ses->se_sessionid.data, NFS4_MAX_SESSIONID_LEN); WRITE32(cb->cb_clp->cl_cb_seq_nr); WRITE32(0); /* slotid, always 0 */ WRITE32(0); /* highest slotid always 0 */ @@ -341,6 +342,7 @@ static int 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; @@ -362,8 +364,7 @@ decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb, 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, cb->cb_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; } @@ -587,7 +588,7 @@ void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn) static int nfsd41_cb_setup_sequence(struct nfs4_client *clp, struct rpc_task *task) { - 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__, diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index db5d8c8..c942511 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -753,8 +753,6 @@ static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp new->se_client = clp; gen_sessionid(new); - memcpy(clp->cl_sessionid.data, new->se_sessionid.data, - NFS4_MAX_SESSIONID_LEN); INIT_LIST_HEAD(&new->se_conns); @@ -1544,7 +1542,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, if (status) goto out; - memcpy(cr_ses->sessionid.data, conf->cl_sessionid.data, + memcpy(cr_ses->sessionid.data, conf->cl_cb_session->se_sessionid.data, NFS4_MAX_SESSIONID_LEN); cr_ses->seqid = cs_slot->sl_seqid; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 6e63c1d..cdce26a 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -243,7 +243,6 @@ struct nfs4_client { 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; -- cgit v1.1 From ac7c46f29a44f6d7f6d2e36dc874c0b7056acad2 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 14 Jun 2010 19:01:57 -0400 Subject: nfsd4: make backchannel sequence number per-session Currently we don't deal well with a client that has multiple sessions associated with it (even simultaneously, or serially over the lifetime of the client). In particular, we don't attempt to keep the backchannel running after the original session diseappears. We will fix that soon. Once we do that, we need the slot sequence number to be per-session; otherwise, for example, we cannot correctly handle a case like this: - All session 1 connections are lost. - The client creates session 2. We use it for the backchannel (since it's the only working choice). - The client gives us a new connection to use with session 1. - The client destroys session 2. At this point our only choice is to go back to using session 1. When we do so we must use the sequence number that is next for session 1. We therefore need to maintain multiple sequence number streams. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 8 ++++---- fs/nfsd/nfs4state.c | 22 ++++++++++++---------- fs/nfsd/state.h | 2 +- 3 files changed, 17 insertions(+), 15 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 78ac779..5df9dda 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -260,7 +260,7 @@ encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb, WRITE32(OP_CB_SEQUENCE); WRITEMEM(ses->se_sessionid.data, NFS4_MAX_SESSIONID_LEN); - WRITE32(cb->cb_clp->cl_cb_seq_nr); + WRITE32(ses->se_cb_seq_nr); WRITE32(0); /* slotid, always 0 */ WRITE32(0); /* highest slotid always 0 */ WRITE32(0); /* cachethis always 0 */ @@ -369,7 +369,7 @@ decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb, goto out; } READ32(dummy); - if (dummy != cb->cb_clp->cl_cb_seq_nr) { + if (dummy != ses->se_cb_seq_nr) { dprintk("%s Invalid sequence number\n", __func__); goto out; } @@ -643,11 +643,11 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata) if (clp->cl_cb_conn.cb_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; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c942511..6367c44 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -725,8 +725,7 @@ void free_session(struct kref *kref) kfree(ses); } - -static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses) +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; @@ -747,7 +746,7 @@ static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp new = alloc_session(slotsize, numslots); if (!new) { nfsd4_put_drc_mem(slotsize, fchan->maxreqs); - return nfserr_jukebox; + return NULL; } init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize); @@ -756,6 +755,7 @@ static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp INIT_LIST_HEAD(&new->se_conns); + new->se_cb_seq_nr = 1; new->se_flags = cses->flags; kref_init(&new->se_ref); idx = hash_sessionid(&new->se_sessionid); @@ -765,9 +765,10 @@ static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp spin_unlock(&client_lock); status = nfsd4_new_conn(rqstp, new); + /* whoops: benny points out, status is ignored! (err, or bogus) */ if (status) { free_session(&new->se_ref); - return nfserr_jukebox; + return NULL; } if (!clp->cl_cb_session && (cses->flags & SESSION4_BACK_CHAN)) { struct sockaddr *sa = svc_addr(rqstp); @@ -779,10 +780,9 @@ static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa); clp->cl_cb_conn.cb_minorversion = 1; clp->cl_cb_conn.cb_prog = cses->callback_prog; - clp->cl_cb_seq_nr = 1; nfsd4_probe_callback(clp, &clp->cl_cb_conn); } - return nfs_ok; + return new; } /* caller must hold client_lock */ @@ -1485,6 +1485,7 @@ 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; int status = 0; @@ -1538,11 +1539,12 @@ nfsd4_create_session(struct svc_rqst *rqstp, 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_cb_session->se_sessionid.data, + status = nfs_ok; + memcpy(cr_ses->sessionid.data, new->se_sessionid.data, NFS4_MAX_SESSIONID_LEN); cr_ses->seqid = cs_slot->sl_seqid; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index cdce26a..7f5b267 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -172,6 +172,7 @@ struct nfsd4_session { struct nfsd4_channel_attrs se_fchannel; struct nfsd4_channel_attrs se_bchannel; struct list_head se_conns; + u32 se_cb_seq_nr; struct nfsd4_slot *se_slots[]; /* forward channel slots */ }; @@ -249,7 +250,6 @@ struct nfs4_client { /* 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 */ }; -- cgit v1.1 From 86c3e16cc7aace4d1143952813b6cc2a80c51295 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sat, 2 Oct 2010 17:04:00 -0400 Subject: nfsd4: confirm only on succesful create_session Following rfc 5661, section 18.36.4: "If the session is not successfully created, then no changes are made to any client records on the server." We shouldn't be confirming or incrementing the sequence id in this case. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6367c44..7e817d1 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1487,6 +1487,7 @@ nfsd4_create_session(struct svc_rqst *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(); @@ -1509,7 +1510,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)) { @@ -1525,8 +1525,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, goto out; } - cs_slot->sl_seqid++; /* from 0 to 1 */ - move_to_confirmed(unconf); + confirm_me = true; conf = unconf; } else { status = nfserr_stale_clientid; @@ -1546,10 +1545,13 @@ nfsd4_create_session(struct svc_rqst *rqstp, 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; /* 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)); -- cgit v1.1 From d29c374cd20de620898d2936396048518809ae24 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 15 Jun 2010 17:34:11 -0400 Subject: nfsd4: track backchannel connections We need to keep track of which connections are available for use with the backchannel, which for the forechannel, and which for both. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 7e817d1..c470cb7 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -644,7 +644,7 @@ static void nfsd4_conn_lost(struct svc_xpt_user *u) spin_unlock(&clp->cl_lock); } -static struct nfsd4_conn *alloc_conn(struct svc_rqst *rqstp) +static struct nfsd4_conn *alloc_conn(struct svc_rqst *rqstp, u32 flags) { struct nfsd4_conn *conn; @@ -653,7 +653,7 @@ static struct nfsd4_conn *alloc_conn(struct svc_rqst *rqstp) return NULL; svc_xprt_get(rqstp->rq_xprt); conn->cn_xprt = rqstp->rq_xprt; - conn->cn_flags = NFS4_CDFC4_FORE; + conn->cn_flags = flags; INIT_LIST_HEAD(&conn->cn_xpt_user.list); return conn; } @@ -682,8 +682,11 @@ static void nfsd4_register_conn(struct nfsd4_conn *conn) static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses) { struct nfsd4_conn *conn; + u32 flags = NFS4_CDFC4_FORE; - conn = alloc_conn(rqstp); + 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); @@ -1640,7 +1643,7 @@ static void nfsd4_sequence_check_conn(struct svc_rqst *rqstp, struct nfsd4_sessi if (c) return; - new = alloc_conn(rqstp); + new = alloc_conn(rqstp, NFS4_CDFC4_FORE); spin_lock(&clp->cl_lock); c = __nfsd4_find_conn(rqstp, ses); -- cgit v1.1 From 8b5ce5cd44743af84507721fa2cb4125ae67955c Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 19 Oct 2010 17:31:50 -0400 Subject: nfsd4: callback program number is per-session The callback program is allowed to depend on the session which the callback is going over. No change in behavior yet, while we still only do callbacks over a single session for the lifetime of the client. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 1 + fs/nfsd/nfs4state.c | 2 +- fs/nfsd/state.h | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 5df9dda..140bb36 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -498,6 +498,7 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn) return -EINVAL; if (conn->cb_minorversion) { args.bc_xprt = conn->cb_xprt; + args.prognumber = clp->cl_cb_session->se_cb_prog; args.protocol = XPRT_TRANSPORT_BC_TCP; } /* Create RPC client */ diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c470cb7..59bc001 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -760,6 +760,7 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n 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); @@ -782,7 +783,6 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa); clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa); clp->cl_cb_conn.cb_minorversion = 1; - clp->cl_cb_conn.cb_prog = cses->callback_prog; nfsd4_probe_callback(clp, &clp->cl_cb_conn); } return new; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 7f5b267..b3bed36 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -96,7 +96,8 @@ struct nfs4_cb_conn { /* SETCLIENTID info */ struct sockaddr_storage cb_addr; size_t cb_addrlen; - u32 cb_prog; + u32 cb_prog; /* used only in 4.0 case; + per-session otherwise */ u32 cb_minorversion; u32 cb_ident; /* minorversion 0 only */ struct svc_xprt *cb_xprt; /* minorversion 1 only */ @@ -172,6 +173,7 @@ struct nfsd4_session { 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 */ }; -- cgit v1.1 From 5a3c9d71343cf27b7afef24ed312368d48dada09 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 19 Oct 2010 17:56:52 -0400 Subject: nfsd4: separate callback change and callback probe Only one of the nfsd4_callback_probe callers actually cares about changing the callback information. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 15 ++++++++++----- fs/nfsd/nfs4state.c | 7 ++++--- fs/nfsd/state.h | 3 ++- 3 files changed, 16 insertions(+), 9 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 140bb36..d38ee3c 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -550,7 +550,7 @@ int set_callback_cred(void) static struct workqueue_struct *callback_wq; -void do_probe_callback(struct nfs4_client *clp) +static void do_probe_callback(struct nfs4_client *clp) { struct nfsd4_callback *cb = &clp->cl_cb_null; @@ -568,17 +568,22 @@ void do_probe_callback(struct nfs4_client *clp) } /* - * 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 *conn) +void nfsd4_probe_callback(struct nfs4_client *clp) +{ + 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)); spin_lock(&clp->cl_lock); memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn)); - set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags); spin_unlock(&clp->cl_lock); - do_probe_callback(clp); } /* diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 59bc001..2327a8c 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -783,7 +783,7 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa); clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa); clp->cl_cb_conn.cb_minorversion = 1; - nfsd4_probe_callback(clp, &clp->cl_cb_conn); + nfsd4_probe_callback(clp); } return new; } @@ -1912,7 +1912,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; @@ -1946,7 +1947,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))) diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index b3bed36..bbc4d58 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -453,7 +453,8 @@ 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); -- cgit v1.1 From 792c95dd519c54d6b0fd6401b3da7ea67b0d6b72 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 12 Oct 2010 19:55:25 -0400 Subject: nfsd4: delay session removal till free_client Have unhash_client_locked() remove client and associated sessions from global hashes, but delay further dismantling till free_client(). (After unhash_client_locked(), the only remaining references outside the destroying thread are from any connections which have xpt_user callbacks registered.) This will simplify locking on session destruction. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 2327a8c..0f2643d 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -883,6 +883,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); @@ -909,15 +916,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 @@ -1031,6 +1035,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); @@ -1047,7 +1053,6 @@ 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); -- cgit v1.1 From 8323c3b2a6b6543919d5ebdddc7d52f192126161 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 19 Oct 2010 19:36:51 -0400 Subject: nfsd4: move minorversion to client The minorversion seems more a property of the client than the callback channel. Some time we should probably also enforce consistent minorversion usage from the client; for now, this is just a cosmetic change. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 8 ++++---- fs/nfsd/nfs4state.c | 12 ++++++++++-- fs/nfsd/state.h | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index d38ee3c..67bcd2c 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -496,7 +496,7 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn) if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5)) return -EINVAL; - if (conn->cb_minorversion) { + if (clp->cl_minorversion) { args.bc_xprt = conn->cb_xprt; args.prognumber = clp->cl_cb_session->se_cb_prog; args.protocol = XPRT_TRANSPORT_BC_TCP; @@ -620,7 +620,7 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *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; - u32 minorversion = clp->cl_cb_conn.cb_minorversion; + u32 minorversion = clp->cl_minorversion; int status = 0; cb->cb_minorversion = minorversion; @@ -645,9 +645,9 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata) 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_session->se_cb_seq_nr; clear_bit(0, &clp->cl_cb_slot_busy); diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 0f2643d..ce0412f 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -782,7 +782,6 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n 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); - clp->cl_cb_conn.cb_minorversion = 1; nfsd4_probe_callback(clp); } return new; @@ -1200,7 +1199,6 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, u32 scopeid) if (conn->cb_addr.ss_family == AF_INET6) ((struct sockaddr_in6 *)&conn->cb_addr)->sin6_scope_id = scopeid; - conn->cb_minorversion = 0; conn->cb_prog = se->se_callback_prog; conn->cb_ident = se->se_callback_ident; return; @@ -1541,6 +1539,11 @@ nfsd4_create_session(struct svc_rqst *rqstp, } /* + * 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; @@ -1857,6 +1860,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; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index bbc4d58..39adc27 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -98,7 +98,6 @@ struct nfs4_cb_conn { size_t cb_addrlen; u32 cb_prog; /* used only in 4.0 case; per-session otherwise */ - u32 cb_minorversion; u32 cb_ident; /* minorversion 0 only */ struct svc_xprt *cb_xprt; /* minorversion 1 only */ }; @@ -227,6 +226,7 @@ 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; -- cgit v1.1 From 5d18c1c2a9a74e0f966c257520b8b7f5136c87b3 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 19 Oct 2010 23:00:12 -0400 Subject: nfsd4: only require krb5 principal for NFSv4.0 callbacks In the sessions backchannel case, we don't need a krb5 principal name for the client; we use the already-created forechannel credentials instead. Some cleanup, while we're there: make it clearer which code here is 4.0- or sessions- specific. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4callback.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 67bcd2c..143da2e 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -481,22 +481,24 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn) }; struct rpc_create_args args = { .net = &init_net, - .protocol = XPRT_TRANSPORT_TCP, .address = (struct sockaddr *) &conn->cb_addr, .addrsize = conn->cb_addrlen, .timeout = &timeparms, .program = &cb_program, - .prognumber = conn->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 (clp->cl_minorversion) { + 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; @@ -508,7 +510,6 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn) PTR_ERR(client)); return PTR_ERR(client); } - clp->cl_cb_ident = conn->cb_ident; clp->cl_cb_client = client; return 0; -- cgit v1.1 From a663bdd8c5d18d287f7468470816c9e0e66343c1 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 21 Oct 2010 17:17:31 -0400 Subject: nfsd4: fix connection allocation in sequence() We're doing an allocation under a spinlock, and ignoring the possibility of allocation failure. A better fix wouldn't require an unnecessary allocation in the common case, but we'll leave that for later. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'fs/nfsd') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index ce0412f..d4aa1b5 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1628,33 +1628,25 @@ out: return status; } -static struct nfsd4_conn *__nfsd4_find_conn(struct svc_rqst *r, struct nfsd4_session *s) +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 == r->rq_xprt) { + if (c->cn_xprt == xpt) { return c; } } return NULL; } -static void nfsd4_sequence_check_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses) +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, *new = NULL; - - spin_lock(&clp->cl_lock); - c = __nfsd4_find_conn(rqstp, ses); - spin_unlock(&clp->cl_lock); - if (c) - return; - - new = alloc_conn(rqstp, NFS4_CDFC4_FORE); + struct nfsd4_conn *c; spin_lock(&clp->cl_lock); - c = __nfsd4_find_conn(rqstp, ses); + c = __nfsd4_find_conn(new->cn_xprt, ses); if (c) { spin_unlock(&clp->cl_lock); free_conn(new); @@ -1674,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); @@ -1710,7 +1711,8 @@ nfsd4_sequence(struct svc_rqst *rqstp, if (status) goto out; - nfsd4_sequence_check_conn(rqstp, session); + nfsd4_sequence_check_conn(conn, session); + conn = NULL; /* Success! bump slot seqid */ slot->sl_inuse = true; @@ -1726,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; -- cgit v1.1