On Fri, Oct 09, 2020 at 02:28:19AM -0400, Dai Ngo wrote: > NFS_FS=y as dependency of CONFIG_NFSD_V4_2_INTER_SSC still have > build errors and some configs with NFSD=m to get NFS4ERR_STALE > error when doing inter server copy. > > Added ops table in nfs_common for knfsd to access NFS client modules. This looks like a good start, but I think it could be a lot simpler: > > Fixes: 3ac3711adb88 ("NFSD: Fix NFS server build errors") > Signed-off-by: Dai Ngo <dai.ngo@xxxxxxxxxx> > --- > changes from v2: fix 0-day build issues. > --- > fs/nfs/nfs4file.c | 40 +++++++++++-- > fs/nfs/nfs4super.c | 6 ++ > fs/nfs/super.c | 20 +++++++ > fs/nfs_common/Makefile | 1 + > fs/nfs_common/nfs_ssc.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++ > fs/nfsd/Kconfig | 2 +- > fs/nfsd/nfs4proc.c | 3 +- > include/linux/nfs_ssc.h | 77 +++++++++++++++++++++++++ > 8 files changed, 289 insertions(+), 8 deletions(-) > create mode 100644 fs/nfs_common/nfs_ssc.c > create mode 100644 include/linux/nfs_ssc.h > > diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c > index fdfc77486ace..7d242fcb134a 100644 > --- a/fs/nfs/nfs4file.c > +++ b/fs/nfs/nfs4file.c > @@ -9,6 +9,7 @@ > #include <linux/falloc.h> > #include <linux/mount.h> > #include <linux/nfs_fs.h> > +#include <linux/nfs_ssc.h> > #include "delegation.h" > #include "internal.h" > #include "iostat.h" > @@ -314,9 +315,8 @@ static loff_t nfs42_remap_file_range(struct file *src_file, loff_t src_off, > static int read_name_gen = 1; > #define SSC_READ_NAME_BODY "ssc_read_%d" > > -struct file * > -nfs42_ssc_open(struct vfsmount *ss_mnt, struct nfs_fh *src_fh, > - nfs4_stateid *stateid) > +static struct file *__nfs42_ssc_open(struct vfsmount *ss_mnt, > + struct nfs_fh *src_fh, nfs4_stateid *stateid) > { > struct nfs_fattr fattr; > struct file *filep, *res; > @@ -398,14 +398,42 @@ struct file * > fput(filep); > goto out_free_name; > } > -EXPORT_SYMBOL_GPL(nfs42_ssc_open); > -void nfs42_ssc_close(struct file *filep) > + > +static void __nfs42_ssc_close(struct file *filep) > { > struct nfs_open_context *ctx = nfs_file_open_context(filep); > > ctx->state->flags = 0; > } > -EXPORT_SYMBOL_GPL(nfs42_ssc_close); > + > +static const struct nfs4_ssc_client_ops nfs4_ssc_clnt_ops_tbl = { > + .sco_owner = THIS_MODULE, > + .sco_open = __nfs42_ssc_open, > + .sco_close = __nfs42_ssc_close, > +}; > + > +/** > + * nfs42_ssc_register_ops - Wrapper to register NFS_V4 ops in nfs_common > + * > + * Return values: > + * On success, returns 0 > + * %-EINVAL if validation check fails > + */ > +int nfs42_ssc_register_ops(void) > +{ > + return nfs42_ssc_register(&nfs4_ssc_clnt_ops_tbl); > +} > + > +/** > + * nfs42_ssc_unregister_ops - wrapper to un-register NFS_V4 ops in nfs_common > + * > + * Return values: > + * None. > + */ > +void nfs42_ssc_unregister_ops(void) > +{ > + nfs42_ssc_unregister(&nfs4_ssc_clnt_ops_tbl); > +} > #endif /* CONFIG_NFS_V4_2 */ > > const struct file_operations nfs4_file_operations = { > diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c > index 0c1ab846b83d..ed0c1f9fc890 100644 > --- a/fs/nfs/nfs4super.c > +++ b/fs/nfs/nfs4super.c > @@ -7,6 +7,7 @@ > #include <linux/mount.h> > #include <linux/nfs4_mount.h> > #include <linux/nfs_fs.h> > +#include <linux/nfs_ssc.h> > #include "delegation.h" > #include "internal.h" > #include "nfs4_fs.h" > @@ -273,6 +274,10 @@ static int __init init_nfs_v4(void) > err = nfs4_xattr_cache_init(); > if (err) > goto out2; > + > + err = nfs42_ssc_register_ops(); > + if (err) > + goto out2; > #endif > > err = nfs4_register_sysctl(); > @@ -297,6 +302,7 @@ static void __exit exit_nfs_v4(void) > unregister_nfs_version(&nfs_v4); > #ifdef CONFIG_NFS_V4_2 > nfs4_xattr_cache_exit(); > + nfs42_ssc_unregister_ops(); > #endif > nfs4_unregister_sysctl(); > nfs_idmap_quit(); > diff --git a/fs/nfs/super.c b/fs/nfs/super.c > index 7a70287f21a2..65636fef6a00 100644 > --- a/fs/nfs/super.c > +++ b/fs/nfs/super.c > @@ -57,6 +57,7 @@ > #include <linux/rcupdate.h> > > #include <linux/uaccess.h> > +#include <linux/nfs_ssc.h> > > #include "nfs4_fs.h" > #include "callback.h" > @@ -85,6 +86,11 @@ > }; > EXPORT_SYMBOL_GPL(nfs_sops); > > +static const struct nfs_ssc_client_ops nfs_ssc_clnt_ops_tbl = { > + .sco_owner = THIS_MODULE, > + .sco_sb_deactive = nfs_sb_deactive, > +}; > + > #if IS_ENABLED(CONFIG_NFS_V4) > static int __init register_nfs4_fs(void) > { > @@ -106,6 +112,16 @@ static void unregister_nfs4_fs(void) > } > #endif > > +static int nfs_ssc_register_ops(void) > +{ > + return nfs_ssc_register(&nfs_ssc_clnt_ops_tbl); > +} > + > +static void nfs_ssc_unregister_ops(void) > +{ > + nfs_ssc_unregister(&nfs_ssc_clnt_ops_tbl); > +} > + > static struct shrinker acl_shrinker = { > .count_objects = nfs_access_cache_count, > .scan_objects = nfs_access_cache_scan, > @@ -133,6 +149,9 @@ int __init register_nfs_fs(void) > ret = register_shrinker(&acl_shrinker); > if (ret < 0) > goto error_3; > + ret = nfs_ssc_register_ops(); > + if (ret < 0) > + goto error_3; > return 0; > error_3: > nfs_unregister_sysctl(); > @@ -152,6 +171,7 @@ void __exit unregister_nfs_fs(void) > unregister_shrinker(&acl_shrinker); > nfs_unregister_sysctl(); > unregister_nfs4_fs(); > + nfs_ssc_unregister_ops(); > unregister_filesystem(&nfs_fs_type); > } > > diff --git a/fs/nfs_common/Makefile b/fs/nfs_common/Makefile > index 4bebe834c009..fa82f5aaa6d9 100644 > --- a/fs/nfs_common/Makefile > +++ b/fs/nfs_common/Makefile > @@ -7,3 +7,4 @@ obj-$(CONFIG_NFS_ACL_SUPPORT) += nfs_acl.o > nfs_acl-objs := nfsacl.o > > obj-$(CONFIG_GRACE_PERIOD) += grace.o > +obj-$(CONFIG_GRACE_PERIOD) += nfs_ssc.o > diff --git a/fs/nfs_common/nfs_ssc.c b/fs/nfs_common/nfs_ssc.c > new file mode 100644 > index 000000000000..6d99a6d2d6b9 > --- /dev/null > +++ b/fs/nfs_common/nfs_ssc.c > @@ -0,0 +1,148 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * fs/nfs_common/nfs_ssc_comm.c > + * > + * Helper for knfsd's SSC to access ops in NFS client modules > + * > + * Author: Dai Ngo <dai.ngo@xxxxxxxxxx> > + * > + * Copyright (c) 2020, Oracle and/or its affiliates. > + */ > + > +#include <linux/module.h> > +#include <linux/fs.h> > +#include <linux/nfs_ssc.h> > +#include "../nfs/nfs4_fs.h" > + > +MODULE_LICENSE("GPL"); > + > +/* > + * NFS_FS > + */ > +static void nfs_sb_deactive_def(struct super_block *sb); > + > +static struct nfs_ssc_client_ops nfs_ssc_clnt_ops_def = { > + .sco_owner = THIS_MODULE, > + .sco_sb_deactive = nfs_sb_deactive_def, > +}; > + > +/* > + * NFS_V4 > + */ > +static struct file *nfs42_ssc_open_def(struct vfsmount *ss_mnt, > + struct nfs_fh *src_fh, nfs4_stateid *stateid); > +static void nfs42_ssc_close_def(struct file *filep); > + > +static struct nfs4_ssc_client_ops nfs4_ssc_clnt_ops_def = { > + .sco_owner = THIS_MODULE, > + .sco_open = nfs42_ssc_open_def, > + .sco_close = nfs42_ssc_close_def, > +}; > + > + > +struct nfs_ssc_client_ops_tbl nfs_ssc_client_tbl = { > + .ssc_nfs4_ops = &nfs4_ssc_clnt_ops_def, > + .ssc_nfs_ops = &nfs_ssc_clnt_ops_def > +}; > +EXPORT_SYMBOL_GPL(nfs_ssc_client_tbl); > + > + > +static struct file *nfs42_ssc_open_def(struct vfsmount *ss_mnt, > + struct nfs_fh *src_fh, nfs4_stateid *stateid) > +{ > + return ERR_PTR(-EIO); > +} > + > +static void nfs42_ssc_close_def(struct file *filep) > +{ > +} I don't think we need any of these "_def" ops. We just shouldn't be calling any of these ops in the case the operations aren't loaded. > + > +#ifdef CONFIG_NFS_V4_2 > +/** > + * nfs42_ssc_register - install the NFS_V4 client ops in the nfs_ssc_client_tbl > + * @ops: NFS_V4 ops to be installed > + * > + * Return values: > + * On success, return 0 > + * %-EINVAL if validation check fails > + */ > +int nfs42_ssc_register(const struct nfs4_ssc_client_ops *ops) > +{ > + if (ops == NULL || ops->sco_open == NULL || ops->sco_close == NULL) > + return -EINVAL; This function only has one caller, and we know it won't do these things. It's not worth the check and the error handling in the caller for something so unlikely. > + nfs_ssc_client_tbl.ssc_nfs4_ops = ops; > + return 0; > +} > +EXPORT_SYMBOL_GPL(nfs42_ssc_register); > + > +/** > + * nfs42_ssc_unregister - uninstall the NFS_V4 client ops from > + * the nfs_ssc_client_tbl > + * @ops: ops to be uninstalled > + * > + * Return values: > + * None > + */ > +void nfs42_ssc_unregister(const struct nfs4_ssc_client_ops *ops) > +{ > + if (nfs_ssc_client_tbl.ssc_nfs4_ops != ops) > + return; > + > + nfs_ssc_client_tbl.ssc_nfs4_ops = &nfs4_ssc_clnt_ops_def; > +} > +EXPORT_SYMBOL_GPL(nfs42_ssc_unregister); > +#endif /* CONFIG_NFS_V4_2 */ > + > +/* > + * NFS_FS > + */ > +static void nfs_sb_deactive_def(struct super_block *sb) > +{ > +} > + > +#ifdef CONFIG_NFS_V4_2 > +/** > + * nfs_ssc_register - install the NFS_FS client ops in the nfs_ssc_client_tbl > + * @ops: NFS_FS ops to be installed > + * > + * Return values: > + * On success, return 0 > + * %-EINVAL if validation check fails > + */ > +int nfs_ssc_register(const struct nfs_ssc_client_ops *ops) > +{ > + if (ops == NULL || ops->sco_sb_deactive == NULL) > + return -EINVAL; Ditto, let's drop these checks. > + nfs_ssc_client_tbl.ssc_nfs_ops = ops; > + return 0; > +} > +EXPORT_SYMBOL_GPL(nfs_ssc_register); > + > +/** > + * nfs_ssc_unregister - uninstall the NFS_FS client ops from > + * the nfs_ssc_client_tbl > + * @ops: ops to be uninstalled > + * > + * Return values: > + * None > + */ > +void nfs_ssc_unregister(const struct nfs_ssc_client_ops *ops) > +{ > + if (nfs_ssc_client_tbl.ssc_nfs_ops != ops) > + return; > + nfs_ssc_client_tbl.ssc_nfs_ops = &nfs_ssc_clnt_ops_def; > +} > +EXPORT_SYMBOL_GPL(nfs_ssc_unregister); > + > +#else > +int nfs_ssc_register(const struct nfs_ssc_client_ops *ops) > +{ > + return 0; > +} > +EXPORT_SYMBOL_GPL(nfs_ssc_register); > + > +void nfs_ssc_unregister(const struct nfs_ssc_client_ops *ops) > +{ > +} > +EXPORT_SYMBOL_GPL(nfs_ssc_unregister); > +#endif /* CONFIG_NFS_V4_2 */ > diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig > index 99d2cae91bd6..f368f3215f88 100644 > --- a/fs/nfsd/Kconfig > +++ b/fs/nfsd/Kconfig > @@ -136,7 +136,7 @@ config NFSD_FLEXFILELAYOUT > > config NFSD_V4_2_INTER_SSC > bool "NFSv4.2 inter server to server COPY" > - depends on NFSD_V4 && NFS_V4_1 && NFS_V4_2 && NFS_FS=y > + depends on NFSD_V4 && NFS_V4_1 && NFS_V4_2 > help > This option enables support for NFSv4.2 inter server to > server copy where the destination server calls the NFSv4.2 > diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c > index eaf50eafa935..84e10aef1417 100644 > --- a/fs/nfsd/nfs4proc.c > +++ b/fs/nfsd/nfs4proc.c > @@ -38,6 +38,7 @@ > #include <linux/slab.h> > #include <linux/kthread.h> > #include <linux/sunrpc/addr.h> > +#include <linux/nfs_ssc.h> > > #include "idmap.h" > #include "cache.h" > @@ -1247,7 +1248,7 @@ extern struct file *nfs42_ssc_open(struct vfsmount *ss_mnt, > static void > nfsd4_interssc_disconnect(struct vfsmount *ss_mnt) > { > - nfs_sb_deactive(ss_mnt->mnt_sb); > + nfs_do_sb_deactive(ss_mnt->mnt_sb); > mntput(ss_mnt); > } > > diff --git a/include/linux/nfs_ssc.h b/include/linux/nfs_ssc.h > new file mode 100644 > index 000000000000..45a763bd6b0b > --- /dev/null > +++ b/include/linux/nfs_ssc.h > @@ -0,0 +1,77 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * include/linux/nfs_ssc.h > + * > + * Author: Dai Ngo <dai.ngo@xxxxxxxxxx> > + * > + * Copyright (c) 2020, Oracle and/or its affiliates. > + */ > + > +#include <linux/nfs_fs.h> > + > +extern struct nfs_ssc_client_ops_tbl nfs_ssc_client_tbl; > + > +/* > + * NFS_V4 > + */ > +struct nfs4_ssc_client_ops { > + struct module *sco_owner; > + struct file *(*sco_open)(struct vfsmount *ss_mnt, > + struct nfs_fh *src_fh, nfs4_stateid *stateid); > + void (*sco_close)(struct file *filep); > +}; > + > +/* > + * NFS_FS > + */ > +struct nfs_ssc_client_ops { > + struct module *sco_owner; > + void (*sco_sb_deactive)(struct super_block *sb); > +}; > + > +struct nfs_ssc_client_ops_tbl { > + const struct nfs4_ssc_client_ops *ssc_nfs4_ops; > + const struct nfs_ssc_client_ops *ssc_nfs_ops; > +}; > + > +extern int nfs42_ssc_register_ops(void); > +extern void nfs42_ssc_unregister_ops(void); > + > +extern int nfs42_ssc_register(const struct nfs4_ssc_client_ops *ops); > +extern void nfs42_ssc_unregister(const struct nfs4_ssc_client_ops *ops); > + > +#ifdef CONFIG_NFSD_V4_2_INTER_SSC > +static inline struct file *nfs42_ssc_open(struct vfsmount *ss_mnt, > + struct nfs_fh *src_fh, nfs4_stateid *stateid) > +{ > + struct file *file; > + > + if (!try_module_get(nfs_ssc_client_tbl.ssc_nfs4_ops->sco_owner)) > + return ERR_PTR(-EIO); The module load already happened back when we did the get_fs_type in nfsd4_interssc_connect(), and it's not going away at this point (or there's something already terribly wrong with ss_mnt). So I don't think we need any try_module_get()s or module_put()s. --b. > + file = (*nfs_ssc_client_tbl.ssc_nfs4_ops->sco_open)(ss_mnt, src_fh, stateid); > + module_put(nfs_ssc_client_tbl.ssc_nfs4_ops->sco_owner); > + return file; > +} > + > +static inline void nfs42_ssc_close(struct file *filep) > +{ > + if (!try_module_get(nfs_ssc_client_tbl.ssc_nfs4_ops->sco_owner)) > + return; > + (*nfs_ssc_client_tbl.ssc_nfs4_ops->sco_close)(filep); > + module_put(nfs_ssc_client_tbl.ssc_nfs4_ops->sco_owner); > +} > +#endif > + > +/* > + * NFS_FS > + */ > +extern int nfs_ssc_register(const struct nfs_ssc_client_ops *ops); > +extern void nfs_ssc_unregister(const struct nfs_ssc_client_ops *ops); > + > +static inline void nfs_do_sb_deactive(struct super_block *sb) > +{ > + if (!try_module_get(nfs_ssc_client_tbl.ssc_nfs_ops->sco_owner)) > + return; > + (*nfs_ssc_client_tbl.ssc_nfs_ops->sco_sb_deactive)(sb); > + module_put(nfs_ssc_client_tbl.ssc_nfs_ops->sco_owner); > +} > -- > 1.8.3.1