Skip to content

nfsd load

Donald Buczek edited this page Jul 20, 2020 · 8 revisions
    MODULE_ALIAS_FS("nfsd");

will make alias fs-nfsd nfsd appear in /lib/modules/$(uname -r)/modules.aliases, so when mount -t nfsd nfsd /proc/fs/nfsd is executed by proc-fs-nfsd.mount, the module is requested by https://elixir.bootlin.com/linux/v5.7/source/fs/filesystems.c#L273

    struct file_system_type *get_fs_type(const char *name)
    {
        /* ... */
	fs = __get_fs_type(name, len);
	if (!fs && (request_module("fs-%.*s", len, name) == 0)) {
		fs = __get_fs_type(name, len);
        /* ... */

https://elixir.bootlin.com/linux/v5.7/source/fs/nfsd/nfsctl.c#L1577

module_init(init_nfsd)

https://elixir.bootlin.com/linux/v5.7/source/fs/nfsd/nfsctl.c#L1517

unsigned int nfsd_net_id;
/* ... */
static struct pernet_operations nfsd_net_ops = {
	.init = nfsd_init_net,
	.exit = nfsd_exit_net,
	.id   = &nfsd_net_id,
	.size = sizeof(struct nfsd_net),
};

static int __init init_nfsd(void)
{
	int retval;
	printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");

	retval = register_pernet_subsys(&nfsd_net_ops);

A zero filled struct nfsd_net is allocated and nfsd_init_net is called for each existing network namespace and will be done for each future network namespace.

https://elixir.bootlin.com/linux/v5.7/source/fs/nfsd/nfsctl.c#L1451

static __net_init int nfsd_init_net(struct net *net)
{
	int retval;
	struct vfsmount *mnt;
	struct nfsd_net *nn = net_generic(net, nfsd_net_id);

	retval = nfsd_export_init(net);
	if (retval)
		goto out_export_error;

init nfsd.export und nfsd.export caches and their /proc/net/rpc/NAME..

	retval = nfsd_idmap_init(net);
	if (retval)
		goto out_idmap_error;

init "nfs4.nametoid" and "nfs4.idtoname" cachees (nfs4 UID/GID mappings to names)

	nn->nfsd_versions = NULL;
	nn->nfsd4_minorversions = NULL;
	retval = nfsd_reply_cache_init(nn);
	if (retval)
		goto out_drc_error;

init reply cache (SLAB "nfsd_drc" but probably merged with another compatible type, so not visible in /proc/slabinfo).

	nn->nfsd_versions = NULL;
	nn->nfsd4_minorversions = NULL;
	retval = nfsd_reply_cache_init(nn);
	if (retval)
		goto out_drc_error;
	nn->nfsd4_lease = 90;	/* default lease time */
	nn->nfsd4_grace = 90;
	nn->somebody_reclaimed = false;
	nn->track_reclaim_completes = false;
	nn->clverifier_counter = prandom_u32();
	nn->clientid_base = prandom_u32();
	nn->clientid_counter = nn->clientid_base + 1;
	nn->s2s_cp_cl_id = nn->clientid_counter++;

	atomic_set(&nn->ntf_refcnt, 0);
	init_waitqueue_head(&nn->ntf_wq);
	seqlock_init(&nn->boot_lock);

	mnt =  vfs_kern_mount(&nfsd_fs_type, SB_KERNMOUNT, "nfsd", NULL);
	if (IS_ERR(mnt)) {
		retval = PTR_ERR(mnt);
		goto out_mount_err;
	}

the mount is not (yet) attached to anything.

rpc.nfsd from nfsd.service creates the sockes and writes the fd numbers to /proc/fs/nfsd/portlist. Then it writes the number of requested threads to /proc/fs/nfsd/thread

https://elixir.bootlin.com/linux/v5.7/source/fs/nfsd/nfsctl.c#L1419

static struct file_system_type nfsd_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "nfsd",
	.init_fs_context = nfsd_init_fs_context,
	.kill_sb	= nfsd_umount,
};

https://elixir.bootlin.com/linux/v5.7/source/fs/nfsd/nfsctl.c#L1403

static int nfsd_init_fs_context(struct fs_context *fc)
{
	put_user_ns(fc->user_ns);
	fc->user_ns = get_user_ns(fc->net_ns->user_ns);
	fc->ops = &nfsd_fs_context_ops;
	return 0;
}

https://elixir.bootlin.com/linux/v5.7/source/fs/nfsd/nfsctl.c#L1398

static const struct fs_context_operations nfsd_fs_context_ops = {
	.free		= nfsd_fs_free_fc,
	.get_tree	= nfsd_fs_get_tree,
};

https://elixir.bootlin.com/linux/v5.7/source/fs/nfsd/nfsctl.c#L1387

static int nfsd_fs_get_tree(struct fs_context *fc)
{
	return get_tree_keyed(fc, nfsd_fill_super, get_net(fc->net_ns));
}

https://elixir.bootlin.com/linux/v5.7/source/fs/nfsd/nfsctl.c#L1341

static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc)
{
	struct nfsd_net *nn = net_generic(current->nsproxy->net_ns,
							nfsd_net_id);
	struct dentry *dentry;
	int ret;

	static const struct tree_descr nfsd_files[] = {
		[NFSD_List] = {"exports", &exports_nfsd_operations, S_IRUGO},
		[NFSD_Export_features] = {"export_features",
					&export_features_operations, S_IRUGO},
		[NFSD_FO_UnlockIP] = {"unlock_ip",
					&transaction_ops, S_IWUSR|S_IRUSR},
		[NFSD_FO_UnlockFS] = {"unlock_filesystem",
					&transaction_ops, S_IWUSR|S_IRUSR},
		[NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR},
		[NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR},
		[NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR},
		[NFSD_Pool_Stats] = {"pool_stats", &pool_stats_operations, S_IRUGO},
		[NFSD_Reply_Cache_Stats] = {"reply_cache_stats", &reply_cache_stats_operations, S_IRUGO},
		[NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR},
		[NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO},
		[NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO},
		[NFSD_MaxConnections] = {"max_connections", &transaction_ops, S_IWUSR|S_IRUGO},
#if defined(CONFIG_SUNRPC_GSS) || defined(CONFIG_SUNRPC_GSS_MODULE)
		[NFSD_SupportedEnctypes] = {"supported_krb5_enctypes", &supported_enctypes_ops, S_IRUGO},
#endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */
#ifdef CONFIG_NFSD_V4
		[NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR},
		[NFSD_Gracetime] = {"nfsv4gracetime", &transaction_ops, S_IWUSR|S_IRUSR},
		[NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR},
		[NFSD_V4EndGrace] = {"v4_end_grace", &transaction_ops, S_IWUSR|S_IRUGO},
#endif
		/* last one */ {""}
	};

	ret = simple_fill_super(sb, 0x6e667364, nfsd_files);
	if (ret)
		return ret;
	dentry = nfsd_mkdir(sb->s_root, NULL, "clients");
	if (IS_ERR(dentry))
		return PTR_ERR(dentry);
	nn->nfsd_client_dir = dentry;
	return 0;
}